ls -lで出力されるdrwxr-xr-x+やdrwxr-xr-x@の末尾の+や@はPOSIXで標準化されているという話

## はじめに

Linux や macOS などで `ls -l` を実行した時、その出力のファイルモードに `drwxr-xr-x+` や `drwxr-xr-x@` といった謎の記号 (`+`, `@`, `.` 等) が末尾に付いているのを見たことがないでしょうか？ 「`ls -l` の出力に余計な一文字を勝手に追加するな！」と憤慨してはいけません。ここに文字を追加して良いことは POSIX で標準化されているからです。

## POSIX ではどのように標準化されているのか？

POSIX で [ls]( https://pubs.opengroup.org/onlinepubs/9699919799/utilities/ls.html) コマンドがどのように標準化されているか確認してみましょう。`ls -l` の出力の一つ目のフィールドは file mode です。その部分の定義を引用します。

> The file mode written under the -l, -n, [XSI] [Option Start] -g, and -o [Option End] options shall consist of the following format:
> 
> ```c
> "%c%s%s%s%s", <entry type>,
>      <owner permissions>, <group permissions>, <other permissions>,
>      <optional alternate access method flag>
> ```
>
> The \<optional alternate access method flag\> shall be the empty string if there is no alternate or additional access control method associated with the file; otherwise, it shall be a string containing a single printable character that is not a \<blank\>.

見やすく書き直すと前から順に次のようになっています。

1. \<entry type\> (`%c`)
2. パーミッション
    1. 所有者 \<owner permissions\> (`%s`)
    2. グループ \<group permissions\> (`%s`)
    2. その他 \<other permissions\> (`%s`)
3. \<optional alternate access method flag\> (`%s`)

この 3 番目が末尾の `+` や `@` が書かれているところです。次に書かれているコメントの通り、optional alternate access method flag は空文字か印刷可能な一文字でなければなりません。

## optional alternate access method flag とはなにか？

日本語（？）にするならば「オプショナルな代替アクセス方式フラグ」と言ったところでしょうか？要するに `rwx` で表現されるよく見かけるパーミッションに変わるより優れたアクセス制御方式、いわゆる ACL (Access Control List) のための文字です。ここに文字があるとパーミッションとは別に追加の ACL の情報が記録されているよという意味になるのです。

元々 POSIX では、POSIX ACL (POSIX.1e) と呼ばれる従来のパーミッションに変わる新しいアクセス制御方式の標準化を進めていました。しかし、これはどうやら 1997 年頃に撤回されたようです（参考 [Why was POSIX.1e withdrawn?](https://unix.stackexchange.com/questions/489820/why-was-posix-1e-withdrawn)）。最初に optional alternate access method flag を表示する `ls` コマンドの実装があったのか、POSIX ACL と共に登場したのかは不明ですが、少なくとも optional alternate access method flag は、POSIX ACL の情報があることを示す場所として必要とされたのだと思います。

POSIX ACL は撤回されましたが、ドラフト17 まで進んでおり、標準化はされていないものの多くの OS では POSIX ACL に近い形の ACL が実装されています。そしてそれらの実装は、ファイルに追加の情報がある時、optional alternate access method flag でそのことを表しています。

## optional alternate access method flag の文字の意味とは？

optional alternate access method flag に使える文字は任意の一文字です。[POSIX ls]( https://pubs.opengroup.org/onlinepubs/9699919799/utilities/ls.html) には次のように書かれています。

> An early proposal specified that the \<optional alternate access method flag\> had to be '+' if there was an alternate access method used on the file or \<space\> if there was not. This was changed to be \<space\> if there is not and a single printable character if there is. This was done for three reasons:
>
> 1. There are historical implementations using characters other than '+'.
> 2. There are implementations that vary this character used in that position to distinguish between various alternate access methods in use.
> 3. The standard developers did not want to preclude future specifications that might need a way to specify more than one alternate access method.
>
> Nonetheless, implementations providing a single alternate access method are encouraged to use '+'.

要約すると、初期の提案では `+` という文字を使う予定だったが、それ以外の文字を使っている実装がすでにあり、将来複数の代替アクセス方式を実装できるように、それ以外の文字も使って良い（ただし、一つしかない場合は `+` を推奨する）ことにしたとのことです。

Linux では `getfacl` / `setfacl` を使って 拡張 ACL を操作することが出来ます。`getfacl` / `setfacl` はどうやら POSIX ACL で標準化される予定のコマンドだったようです。拡張 ACL は `+` で表されます。詳細を確認していませんが SELinux の場合はセキュリティコンテキストを設定すると `.` で表されるようです。セキュリティコンテキストはファイルの拡張属性として `getfattr` / `setfattr` で設定や取得を行います。

macOS では `chmod` コマンドが拡張されており `+a` や `=a` などを使って ACL を操作できるようです。`@` の場合は拡張属性が付いていることを意味しているようです。拡張属性は ACL ではないはずなので、optional alternate access method flag を想定された用途とは少し違うものに使っていると考えるべきかもしれません。それもあって `+` ではなく `@` を使っているのでしょう。

これらの話をまとめると以下のようになります。

- Linux
  - `+`: 拡張 ACL ・・・ `getfact` / `setfacl` 
  - `.`: SELinux ・・・ `getfattr` / `setfattr` （拡張属性）
- macOS
  - `+`: ACL ・・・ `chmod`
  - `@`: 拡張属性 ・・・ `xattr`

その他の OS ではまた違うものがあるかもしれません。

## POSIX コマンドの注意点と移植性の限界

POSIX では `ls -l` の出力 `drwxr-xr-x` の次に追加の一文字を表示して、ファイルに対して追加の情報があることを示して良いと標準化されています。しかし、その追加の情報にアクセスするコマンドは POSIX では標準化されておらず、POSIX コマンドだけではファイルの追加の情報にアクセスできません。これは POSIX で規定されている範囲では、OS の能力を引き出すことが出来ないことを意味しています。POSIX は移植性があると認められたものだけを標準規格としてまとめているので当然とも言えますが、どれだけ OS が優れた機能を実装していたとしてもそれが使えないというのはもったいない話です。アプリケーション開発者は移植性を保ちつつ OS の機能を引き出すにはどうすればいいか？をうまく考えて設計・開発する必要があります。

また POSIX 標準コマンドが ACL や拡張属性をどのように扱うかにも注意が必要です。例えば Linux では `cp` コマンドは何もオプションを指定してない時、基本的なパーミッション情報 （`rwx`) は `umask` コマンドでマスクされていない限りコピーされますが、ACL はコピーされません。これをコピーするには `-p` オプションが必要です。しかし `-p` オプションを付けても拡張属性はコピーされません。拡張属性をコピーする場合は `--preserve=xattr` オプションが必要なようです。`-a` オプションは POSIX では標準化されていないことに注意してください。macOS も Linux と同様に ACL をコピーする場合は `-p` オプションが必要でが、拡張属性の場合は何もオプションをつけずにコピーされるようです。

ファイルに ACL や拡張属性といった追加情報があるという事実は、パーミッションだけをコピーしても元のファイルと同じ状態にはならないということを意味しています。例えばファイルを上書きする時に、一旦削除してから再作成するという方法を使う場合、パーミッションだけを復元しても、ACL や拡張属性の情報は消えてしまうため同じ状態にはなりません。OS 毎に異なる情報を取得して反映させるのはかなり難しい作業です。

POSIX で標準化されていない部分なので、各 OS で違いがあるのは当然です。POSIX に準拠した移植性が高いアプリケーションでは、これらの拡張機能に対応する必要があります。拡張機能を搭載することは POSIX では禁じられていません。したがって ACL や拡張属性への対応はアプリケーション側が行わなければいけないことです。

## さいごに

ファイルモードの末尾の `+`、`@`、`.` はあまり見かけないので、ついつい見逃しやすいものですが、POSIX でも標準化されているものです。`ls -l` のファイルモードをパースして処理する時には、ここに追加の一文字が存在する場合があるということに注意しましょう。また移植性があるアプリケーションを開発する際には、これらの情報をどう扱うかを（必ずしも必要ではありませんが）考える必要があります。