openat(2) ファイルのオープン、作成を行う

Other Alias

open, creat

書式

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>


int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
int creat(const char *pathname, mode_t mode);

int openat(int dirfd, const char *pathname, int flags);
int openat(int dirfd, const char *pathname, int flags, mode_t mode);

glibc 向けの機能検査マクロの要件 (feature_test_macros(7) 参照):

openat():

glibc 2.10 以降:
_XOPEN_SOURCE >= 700 || _POSIX_C_SOURCE >= 200809L
glibc 2.10 より前:
_ATFILE_SOURCE

説明

ファイルの pathname を与えると、 open() はファイルディスクリプタを返す。 ファイルディスクリプタは、この後に続くシステムコール (read(2), write(2), lseek(2), fcntl(2) など) で使用される小さな非負の整数である。 このシステムコールが成功した場合に返されるファイルディスクリプタは そのプロセスがその時点でオープンしていないファイルディスクリプタの うち最小の数字のものとなる。

デフォルトでは、新しいファイルディスクリプタは execve(2) を実行した後も オープンされたままとなる (つまり、 fcntl(2) に説明がある FD_CLOEXEC ファイルディスクリプタフラグは最初は無効である; 後述の O_CLOEXEC フラグ を使うとこのデフォルトを変更することができる)。 ファイルオフセット (file offset) はファイルの先頭に設定される (lseek(2) 参照)。

open() を呼び出すと、「オープンファイル記述」 (open file description) が作成される。ファイル記述とは、システム全体の オープン中のファイルのテーブルのエントリである。 このエントリは、ファイルオフセットとファイル状態フラグ (fcntl(2) F_SETFL 操作により変更可能) が保持する。 ファイルディスクリプタはこれらのエントリの一つへの参照である。 この後で pathname が削除されたり、他のファイルを参照するように変更されたりしても、 この参照は影響を受けない。 新しいオープンファイル記述は最初は他のどのプロセスとも 共有されていないが、 fork(2) で共有が起こる場合がある。

引き数 flags には、アクセスモード O_RDONLY, O_WRONLY, O_RDWR のどれかひとつが入っていなければならない。 これらはそれぞれ読み込み専用、書き込み専用、読み書き用に ファイルをオープンすることを要求するものである。

さらに、 flags には、ファイル作成フラグ (file creation flag) とファイル状態フラグ (file status flag) を 0 個以上「ビット単位の OR (bitwise-or)」で 指定することができる。 ファイル作成フラグO_CLOEXEC, O_CREAT, O_DIRECTORY, O_EXCL, O_NOCTTY, O_NOFOLLOW, O_TMPFILE, O_TRUNC, O_TTY_INIT である。 ファイル状態フラグ は以下のリストのうち上記以外の残りのものである。 二種類のフラグの違いは、ファイル状態フラグの方はその内容を取得したり (場合によっては) 変更したりできる点にある。詳細は fcntl(2) を参照。

すべてのファイル作成フラグとファイル状態フラグを以下のリストに示す。

O_APPEND
ファイルを追加 (append) モードでオープンする。 毎回の write(2) の前に lseek(2) を行ったかのように、ファイルポインタをファイルの最後に移動する。 NFS ファイルシステムで、 O_APPEND を使用すると、複数のプロセスがひとつのファイルに同時にデータを追加した場合、 ファイルが壊れてしまうことがある。 これは NFS が追加モードをサポートしていないため、 クライアントのカーネル (kernel) がそれをシミュレートしなければならないのだが、 競合状態を避けることはできないからである。
O_ASYNC
シグナル駆動 I/O (signal-driven I/O) を有効にする: このファイルディスクリプタへの 入力または出力が可能になった場合に、シグナルを生成する (デフォルトは SIGIO であるが、 fcntl(2) によって変更可能である)。 この機能が使用可能なのは端末、疑似端末、ソケットのみであり、 (Linux 2.6 以降では) パイプと FIFO に対しても使用できる。 さらに詳しい説明は fcntl(2) を参照すること。 下記の「バグ」も参照。
O_CLOEXEC (Linux 2.6.23 以降)
新しいファイルディスクリプタに対して close-on-exec フラグを有効にする。 このフラグを指定することで、 プログラムは FD_CLOEXEC フラグをセットするために fcntl(2) F_SETFD 操作を別途呼び出す必要がなくなる。

ある種のマルチスレッドのプログラムはこのフラグの使用は不可欠である点に注意すること。 なぜなら、個別に FD_CLOEXEC フラグを設定する fcntl(2) F_SETFD 操作を呼び出したとしても、あるスレッドがファイルディスクリプタを オープンするのと同時に別のスレッドが fork(2) と execve(2) を実行するという競合条件を避けるのには十分ではないからである。 実行の順序に依存して、この競合条件の結果、 open() が返したファイルディスクリプタが fork(2) で作成された子プロセスにより実行されるプログラムに意図せず見えてしまう可能性がある。 (この種の競合は、 本質的に、 close-on-exec フラグをセットすべきファイルディスクリプタを作成するどのシステムコールでも起こり得るものであり、 他のいろいろな Linux システムコールでこの問題に対処するために O_CLOEXEC と同等の機能が提供されている。)

O_CREAT
ファイルが存在しなかった場合は作成 (create) する。 ファイルの所有者 (ユーザー ID) は、プロセスの実効ユーザー ID に設定される。 グループ所有権 (グループ ID) は、プロセスの実効グループ ID または親ディレクトリのグループ ID に設定される (これは、ファイルシステムタイプ、マウントオプション、 親ディレクトリのモードに依存する。 mount(8) で説明されているマウントオプション bsdgroupssysvgroups を参照)。

mode は新しいファイルを作成する場合に使用するアクセス許可 (permission) を指定する。 flagsO_CREATO_TMPFILE が指定されている場合、 mode を指定しなければならない。 O_CREATO_TMPFILE も指定されていない場合、 mode は無視される。 有効なアクセス許可は、普段と同じようにプロセスの umask によって修正され、作成されたファイルの許可は (mode & ~umask) となる。 このモードは、新しく作成されたファイルに対するそれ以降のアクセス にのみ適用される点に注意すること。 読み取り専用のファイルを作成する open() コールであっても、 読み書き可能なファイルディスクリプタを返すことがありうる。

mode のために以下のシンボル定数が提供されている :

S_IRWXU
00700 ユーザー (ファイルの所有者) に読み込み、書き込み、 実行の許可がある。
S_IRUSR
00400 ユーザーに読み込みの許可がある。
S_IWUSR
00200 ユーザーに書き込みの許可がある。
S_IXUSR
00100 ユーザーに実行の許可がある。
S_IRWXG
00070 グループに読み込み、書き込み、実行の許可がある。
S_IRGRP
00040 グループに読み込みの許可がある。
S_IWGRP
00020 グループに書き込みの許可がある。
S_IXGRP
00010 グループに実行の許可がある。
S_IRWXO
00007 他人 (others) に読み込み、書き込み、実行の許可がある。
S_IROTH
00004 他人に読み込みの許可がある。
S_IWOTH
00002 他人に書き込みの許可がある。
S_IXOTH
00001 他人に実行の許可がある。
O_DIRECT (Linux 2.4.10 以降)
このファイルに対する I/O のキャッシュの効果を最小化しようとする。このフラグを使うと、一般的に性能が低下する。 しかしアプリケーションが独自にキャッシングを行っているような 特別な場合には役に立つ。 ファイルの I/O はユーザー空間バッファに対して直接行われる。 O_DIRECT フラグ自身はデータを同期で転送しようとはするが、 O_SYNC フラグのようにデータと必要なメタデータの転送が保証されるわけではない。同期 I/O を保証するためには、 O_DIRECT に加えて O_SYNC を使用しなければならない。下記の「注意」の節の議論も参照。

ブロックデバイスに対する似通った意味のインターフェースが raw(8) で説明されている (但し、このインタフェースは非推奨である)。

O_DIRECTORY
pathname がディレクトリでなければオープンは失敗する。 このフラグは、 opendir(3) が FIFO やテープデバイスに対してコールされた場合の サービス不能 (denial-of-service) 攻撃を避けるために カーネル 2.1.126 で追加された。
O_DSYNC
ファイルに対する書き込み操作は、同期 I/O のデータ完全性完了の要件に基づいて行われる。

write(2) (や同様のコール) が返るまでに、 書き込まれたデータおよびデータを取得するのに必要なファイルメタデータが裏で利用されているハードウェアに転送される (つまり、write(2) の後に fdatasync(2) を呼び出したのと同じようになる)。 下記の「注意」も参照のこと

O_EXCL
この呼び出しでファイルが作成されることを保証する。このフラグが O_CREAT と 一緒に指定され、 pathname のファイルが既に存在した場合、 open() は失敗 する。

これら二つのフラグが指定された際、シンボリックリンクは辿られない。 pathname がシンボリックリンクの場合、 シンボリックリンクがどこを指しているかに関わらず open() は失敗する。

一般的には、 O_CREAT を指定せずに O_EXCL を使用した場合の O_EXCL の動作は規定されていない。 これには一つ例外があり、Linux 2.6 以降では、 pathname がブロックデバイスを参照している場合、 O_CREAT なしで O_EXCL を使用することができる。 システムがそのブロックデバイスを使用中の場合 (例えば、 マウントされているなど)、 open() はエラー EBUSY で失敗する。

NFS では、 O_EXCL は、Linux 2.6 以降で NFSv3 以降を使っている場合でのみサポートされる。 O_EXCL サポートが提供されていない NFS 環境では、このフラグに頼って ロック処理を実行するプログラムは競合状態 (race condition) に出会う 可能性がある。 ロックファイルを使用して不可分 (atomic) なファイルロックを実現し、 NFS が O_EXCL をサポートしているかに依存しないようにしたい場合、 移植性のある方法は、同じファイルシステム上に他と名前の重ならない ファイル (例えばホスト名と PID を組み合わせた名前) を作成し、 link(2) を使用してそのロックファイルへのリンクを作成することである。 link(2) コールの返り値が 0 ならばロックに成功している。 あるいは、そのファイルに stat(2) を使用してリンク数 (link count) が 2 になっているかをチェックする。 そうなっていれば、同じくロックに成功しているということである。

O_LARGEFILE
(LFS) off_t ではサイズを表せない (だだし off64_t ではサイズを表せる)ファ イルをオープン可能にする。この定義を有効にするためには、(どのヘッダファイ ルをインクルードするよりも前に) _LARGEFILE64_SOURCE マクロを定義しなければ ならない。 32 ビットシステムにおいて大きなファイルにアクセスしたい場合、 (O_LARGEFILE を使うよりも) _FILE_OFFSET_BITS 機能検査マクロを 64 に セットする方が望ましい方法である (feature_test_macros(7) を参照)。
O_NOATIME (Linux 2.6.8 以降)
ファイルに対して read(2) が実行されたときに、最終アクセス時刻 (inode の st_atime) を更新しない。 このフラグはインデックス作成やバックアッププログラムで使うことを意図している。 これを使うとディスクに対する操作を大幅に減らすことができる。 このフラグは全てのファイルシステムに対して有効であるわけではない。 その一例が NFS であり、サーバがアクセス時刻を管理している。
O_NOCTTY
pathname が端末 (terminal) デバイス --- tty(4) 参照 --- を指している 場合に、たとえそのプロセスが制御端末を持っていなくても、オープンしたファイル は制御端末にはならない。
O_NOFOLLOW
pathname がシンボリックリンクだった場合、オープンは失敗する。 これは FreeBSD の拡張で、Linux には バージョン 2.1.126 で追加された。 このフラグが指定された場合でも pathname の前の方の要素 (最後のディレクトリセパレータより前の部分) にあるシンボリックリンクについてはリンクが辿られる。 下記の O_PATH も参照のこと。
O_NONBLOCK または O_NDELAY
可能ならば、ファイルは非停止 (nonblocking) モードでオープンされる。 open() も、返したファイルディスクリプタに対する以後のすべての操作も呼び出 したプロセスを待たせることはない。 FIFO (名前付きパイプ) を扱う場合には fifo(7) も参照すること。 強制ファイルロック (mandatory file lock) やファイ ルリース (file lease) と組み合わせた場合の、 O_NONBLOCK の効果についての 議論は、 fcntl(2) を参照すること。
O_PATH (Linux 2.6.39 以降)
このフラグを指定して取得したファイルディスクリプタは、 ファイルシステムツリー内での場所を示すため、 純粋にファイルディスクリプタレベルでの作用する操作を実行するため、 の二つの目的で使用することができる。 ファイル自身はオープンされず、 他のファイル操作 (例えば read(2), write(2), fchmod(2), fchown(2), fgetxattr(2), mmap(2)) はエラー EBADF で失敗する。

取得したファイルディスクリプタに対して以下の操作を行うことが「できる」。

*
close(2); fchdir(2) (Linux 3.5 以降); fstat(2) (Linux 3.6 以降)
*
ファイルディスクリプタの複製 (dup(2), fcntl(2) F_DUPFD など)
*
ファイルディスクリプタフラグの取得と設定 (fcntl(2) の F_GETFDF_SETFD)
*
fcntl(2) の F_GETFL 操作を使ったオープンされたファイルの状態フラグの取得。 返されるフラグには O_PATH ビットが含まれる。

*
openat(2) や他の "*at()" 系のシステムコールの dirfd 引数としてそのファイルディスクリプタを渡す。
*
そのファイルディスクリプタを別のプロセスに UNIX ドメインソケット経由で渡す。 (unix(7) の SCM_RIGHTS を参照)
flagsO_PATH が指定された場合、 O_DIRECTORYO_NOFOLLOW 以外のフラグビットは無視される。

pathname がシンボリックリンクで O_NOFOLLOW フラグも合わせて指定された場合、 この呼び出しではシンボリックリンクを参照するファイルディスクリプタを返す。 このファイルディスクリプタは、 空のパス名を指定した fchownat(2), fstatat(2), linkat(2), readlinkat(2) の呼び出しで dirfd 引数として使うことで、 そのシンボリックリンクに対して操作を行うことができる。

O_SYNC
ファイルに対する書き込み操作は、同期 I/O のファイル完全性完了の要件に基づいて行われる (これに対し O_DSYNC では同期 I/O のデータ完全性完了が提供される)。

write(2) (や同様のコール) が返るまでに、 書き込まれたデータと関連するファイルメタデータが裏で利用されているハードウェアに転送される (つまり、write(2) の後に fsync(2) を呼び出したのと同じようになる)。 下記の「注意」も参照のこと

O_TMPFILE (Linux 3.11 以降)
名前なしの一時ファイルを作成する。 pathname 引き数はディレクトリを指定する。 名前なしの inode がそのディレクトリが存在するファイルシステムに作成される。 そのファイルに名前を付与しない限り、 作成されたファイルに書き込まれた内容は、 最後のファイルディスクリプタがクローズされる際に失われる。

O_TMPFILE は必ず O_RDWRO_WRONLY のいずれかと一緒に使わなければならない。 O_EXCL も指定することができる。 O_EXCL が指定されなかった場合、 linkat(2) を使って、そのファイルシステムにこの一時ファイルへのリンクを作成し、ファイルを永続化することができる。 以下のコードのようにすればよい。

char path[PATH_MAX];
fd = open("/path/to/dir", O_TMPFILE | O_RDWR,
                        S_IRUSR | S_IWUSR);
/* 'fd' に対するファイル I/O ... */
snprintf(path, PATH_MAX,  "/proc/self/fd/%d", fd);
linkat(AT_FDCWD, path, AT_FDCWD, "/path/for/file",
                        AT_SYMLINK_FOLLOW);

この場合、 open() の mode 引き数は O_CREAT と同様にファイルのアクセス許可モードの決定に使われる。

O_TMPFILE とともに O_EXCL を指定すると、 一時ファイルに対して上記の方法でファイルシステムへのリンクを行うことができなくなる (この場合の O_EXCL の意味は他の場合の O_EXCL の意味とは異なる点に注意)。

O_TMPFILE には主に二つの用途がある。

*
改善された tmpfile(3) の機能: (1) クローズ時に自動的に削除される、 (2) パス名では決して参照できない、 (3) シンボリックリンク攻撃ができない、 (4) 呼び出し元が一意な名前を考える必要がない、 という特長を持つ競合のない一時ファイルの作成。
*
最初は見えないファイルを作成し、 それからデータを書き込んだり、適切なファイルシステム属性を持つように調整したり (chown(2), chmod(2), fsetxattr(2) など) した後、 準備が全て整った状態で (上述の linkat(2) を使って) ファイルシステム内にアトミックにリンクを行う。
O_TMPFILE は、 裏で利用されるファイルシステムによるサポートが必要である。 一部の Linux ファイルシステムだけがこの機能をサポートしている。 最初の実装では、 ext2, ext3, ext4, UDF, Minix, shmem ファイルシステムがサポートしていた。 XFS でのサポートが Linux 3.15 で追加された。
O_TRUNC
ファイルが既に存在し、通常ファイルであり、 アクセスモードで書き込みが許可されている (つまり、 O_RDWR または O_WRONLY の) 場合、長さ 0 に切り詰め (truncate) られる。 ファイルが FIFO または端末デバイスファイルの場合、 O_TRUNC フラグは無視される。 それ以外の場合、 O_TRUNC の効果は未定義である。

creat()

creat() は flagsO_CREAT|O_WRONLY|O_TRUNC を指定して open() を行うのと等価である。

openat()

openat() システムコールは open() と全く同様に動作するが、以下で説明する点が異なる。

pathname で指定されたパス名が相対パスの場合、このパス名はファイルディスクリプター dirfd が参照するディレクトリに対する相対パスと解釈される (open() に相対パス名を渡した場合のように、呼び出したプロセスのカレントワーキングディレクトリに対する相対パスではない)。

pathname で指定されたパス名が相対パスで、 dirfd が特別な値 AT_FDCWD の場合、 (open() と同様に) pathname は呼び出したプロセスのカレントワーキングディレクトリに対する相対パスと解釈される。

pathname で指定されたパス名が絶対パスの場合、 dirfd は無視される。

返り値

open(), openat(), creat() は新しいファイルディスクリプタを返す。 エラーが発生した場合は -1 を返す (その場合は errno が適切に設定される)。

エラー

open(), openat(), creat() は以下のエラーで失敗する。
EACCES
ファイルに対する要求されたアクセスが許されていないか、 pathname のディレクトリ部分の何れかのディレクトリに検索許可がなかった。 またはファイルが存在せず、親ディレクトリへの書き込み許可がなかった。 (path_resolution(7) も参照すること。)
EDQUOT
O_CREAT が指定された場合で、そのファイルが存在せず、ディスクブロックか inode がそのファイルシステムのユーザクォータに達していた。
EEXIST
pathname は既に存在し、 O_CREATO_EXCL が使用された。
EFAULT
pathname がアクセス可能なアドレス空間の外を指している。
EFBIG
EOVERFLOW 参照。
EINTR
遅いデバイス (例えば FIFO、 fifo(7) 参照) のオープンが完了するのを待って停止している間に システムコールがシグナルハンドラにより割り込まれた。 signal(7) 参照。
EINVAL
ファイルシステムが O_DIRECT フラグをサポートしていない。 詳細は注意を参照。
EINVAL
flags に無効な値が入っている。
EINVAL
flagsO_TMPFILE が指定されたが、 O_WRONLYO_RDWR も指定されていなかった。
EISDIR
pathname はディレクトリを参照しており、書き込み要求が含まれていた (つまり O_WRONLY または O_RDWR が設定されている)。
EISDIR
pathname が存在するディレクトリを参照していて、 O_TMPFILE および O_WRONLYO_RDWR の一方が flags に指定されていたが、 このカーネルバージョンでは O_TMPFILE 機能が提供されていない。
ELOOP
pathname を解決する際に遭遇したシンボリックリンクが多過ぎる。
ELOOP
pathname がシンボリックリンクで、 flagsO_NOFOLLOW が指定されたが、 O_PATH が指定されていなかった。
EMFILE
プロセスがオープンしているファイル数がすでに最大数に達している。
ENAMETOOLONG
pathname が長過ぎる。
ENFILE
オープンされているファイルの総数がシステムの制限に達している。
ENODEV
pathname がデバイススペシャルファイルを参照しており、対応するデバイスが存在しない。 (これは Linux カーネルのバグであり、この場合には ENXIO が返されるべきである)
ENOENT
O_CREAT が設定されておらず、かつ指定されたファイルが存在しない。 または、 pathname のディレクトリ部分が存在しないか壊れた (dangling) シンボリックリンクである。
ENOENT
pathname が存在しないディレクトリを参照していて、 O_TMPFILE および O_WRONLYO_RDWR の一方が flags に指定されていたが、 このカーネルバージョンでは O_TMPFILE 機能が提供されていない。
ENOMEM
十分なカーネルメモリーがない。
ENOSPC
pathname を作成する必要があるが、 pathname を含んでいるデバイスに新しいファイルのための空き容量がない。
ENOTDIR
pathname に含まれるディレクトリ部分のどれかが実際にはディレクトリでない。 または O_DIRECTORY が指定されており、 pathname がディレクトリでない。
ENXIO
O_NONBLOCK | O_WRONLY が設定されており、指定したファイルが FIFO で そのファイルを読み込みのためにオープンしているプロセスが存在しない。 または、ファイルがデバイススペシャルファイルで 対応するデバイスが存在しない。
EOPNOTSUPP
pathname を含んでいるファイルシステムが O_TMPFILE をサポートしていない。
EOVERFLOW
pathname が参照しているのが、大き過ぎてオープンできない通常のファイルである。 通常、このエラーが発生するは、32 ビットプラットフォーム上で -D_FILE_OFFSET_BITS=64 を指定せずにコンパイルされたアプリケーションが、ファイルサイズが (2<31)-1 ビットを超えるファイルを開こうとした場合である。 上記の O_LARGEFILE も参照。 これは POSIX.1-2001 で規定されているエラーである。 2.6.24 より前のカーネルでは、Linux はこの場合にエラー EFBIG を返していた。
EPERM
O_NOATIME フラグが指定されたが、呼び出し元の実効ユーザー ID が ファイルの所有者と一致せず、かつ呼び出し元に特権 (CAP_FOWNER) がない。
EROFS
pathname が読み込み専用のファイルシステム上のファイルを参照しており、 書き込みアクセスが要求された。
ETXTBSY
pathname が現在実行中の実行イメージを参照しており、書き込みが要求された。
EWOULDBLOCK
O_NONBLOCK フラグが指定されたが、そのファイルには矛盾するリースが設定されていた (fcntl(2) 参照)。

openat() では以下のエラーも発生する。

EBADF
dirfd が有効なファイルディスクリプタではない。
ENOTDIR
pathname が相対パスで、 dirfd がディレクトリ以外のファイルを参照しているファイルディスクリプタである。

バージョン

openat() はカーネル 2.6.16 で Linux に追加された。 ライブラリによるサポートはバージョン 2.4 で glibc に追加された。

準拠

open(), creat() SVr4, 4.3BSD, POSIX.1-2001, POSIX.1-2008.

openat(): POSIX.1-2008.

フラグ O_DIRECT, O_NOATIME, O_PATH, O_TMPFILE は Linux 特有のものである。 これらのフラグの定義を得るためには _GNU_SOURCE を定義しなければならない。

フラグ O_CLOEXEC, O_DIRECTORY, O_NOFOLLOW は POSIX.1-2001 では規定されていないが、 POSIX.1-2008 では規定されている。 glibc 2.12 以降では、これらの定義を得るには、 _POSIX_C_SOURCE を 200809L 以上の値で定義するか、 _XOPEN_SOURCE を 700 以上の値で定義する。 glibc 2.11 以前では、 これらの定義を得るには _GNU_SOURCE を定義する。

feature_test_macros(7) に注意書きがあるように、 _POSIX_C_SOURCE, _XOPEN_SOURCE, _GNU_SOURCE などの機能検査マクロはどのヘッダーファイルをインクルードするより前に定義しなければならない。

注意

Linux では、 O_NONBLOCK フラグは、 open を実行したいが read または write を実行する意図は 必ずしもないことを意味する。 これは ioctl(2) のためのファイルディスクリプタを取得するために、 デバイスをオープンするときによく用いられる。

O_RDONLY | O_TRUNC の影響は未定義であり、その動作は実装によって異なる。 多くのシステムではファイルは実際に切り詰められる。

open() はスペシャルファイルをオープンすることができるが、 creat() でスペシャルファイルを作成できない点に注意すること。 代わりに mknod(2) を使用する。

ファイルが新しく作成されると、 ファイルの st_atime, st_ctime, st_mtime フィールド (それぞれ最終アクセス時刻、最終状態変更時刻、最終修正時刻である。 stat(2) 参照) が現在時刻に設定される。 さらに親ディレクトリの st_ctimest_mtime も現在時刻に設定される。 それ以外の場合で、O_TRUNC フラグでファイルが修正されたときは、 ファイルの st_ctimest_mtime フィールドが現在時刻に設定される。

同期 I/O

POSIX.1-2008 の「同期 I/O」の選択肢として複数種類が規定されており、 動作を制御するために open() フラグとして O_SYNC, O_DSYNC, O_RSYNC が規定されている。 この選択肢を実装がサポートしているかに関わらず、 各実装では少なくとも通常のファイルに対して O_SYNC が利用できなければならない。

Linux は O_SYNCO_DSYNC を実装しているが、 O_RSYNC は実装していない (少し間違っているのだが、 glibc では O_RSYNCO_SYNC と同じ値で定義されている)。

O_SYNC は、 同期 I/O でのファイル完全性完了を提供する。 つまり、 書き込み操作はデータとすべての関連メタデータを裏で利用されているハードウェアにフラッシュすることを意味する。 O_DSYNC は、 同期 I/O でのデータ完全性完了を提供する。 つまり、 書き込み操作はデータを裏で利用されているハードウェアにフラッシュするが、 それ以降の読み出し操作が正常に完了するのに必要なメタデータの更新のみをフラッシュする。 データ完全性完了は、 ファイル完全性完了を必要としないアプリケーションで、 ディスク操作の数を減らすことができる。

2 種類の完了の違いを理解するために、 ファイルメタデータの 2 つの要素、 ファイルの最終修正時刻 (st_mtime) とファイル長、を考える。 すべての書き込み操作は最終修正時刻を更新するが、 ファイルの末尾にデータを追加する書き込み操作のみがファイル長を変更する。 最終修正時刻は、 読み出しが正常に完了するのに必要ではないが、 ファイル長は必要である。 したがって、 O_DSYNC はファイル長のメタデータの更新がフラッシュされることだけを保証する (これに対して O_SYNC では最終修正時刻のメタデータも常にフラッシュされる)。

Linux 2.6.33 より前では、 Linux は open() では O_SYNC フラグのみを実装していた。 しかしながら、 このフラグが指定された場合、 ほとんどのファイルシステムで提供されていたのは実際には同期 I/O でのデータ完全性完了と等価なものであった (つまり、 O_SYNC は実際には O_DSYNC と等価なものとして実装されていた)。

Linux 2.6.33 行こう では、 正しい O_SYNC のサポートが提供されている。 しかしながら、 バイナリレベルの後方互換性を保証するため、 O_DSYNC は以前の O_SYNC と同じ値で定義されており、 O_SYNCO_DSYNC フラグの値を含む新しい (2 ビットの) フラグ値として定義されている。 これにより、 新しいヘッダを使ってコンパイルされたアプリケーションで、 2.6.33 より前のカーネルで少なくとも O_DSYNC の動作は同じになることが保証される。

NFS

NFS を実現しているプロトコルには多くの不備があり、特に O_SYNCO_NDELAY に影響する。

UID マッピングを使用している NFS ファイルシステムでは、 open() がファイルディスクリプタを返した場合でも read(2) が EACCES で拒否される場合がある。 これはクライアントがアクセス許可のチェックを行って open() を実行するが、読み込みや書き込みの際には サーバーで UID マッピングが行われるためである。

ファイルアクセスモード

「アクセスモード」の値 O_RDONLY, O_WRONLY, O_RDWR は、 flags に指定できる他の値と違い、個々のビットを指定するものではなく、 これらの値は flags の下位 2 ビットを定義する。 O_RDONLY, O_WRONLY, O_RDWR はそれぞれ 0, 1, 2 に定義されている。 言い換えると、 O_RDONLY | O_WRONLY の組み合わせは論理的に間違いであり、確かに O_RDWR と同じ意味ではない。

Linux では、特別な、非標準なアクセスモードとして 3 (バイナリでは 11) が 予約されており flags に指定できる。 このアクセスモードを指定すると、ファイルの読み出し/書き込み許可をチェックし、 読み出しにも書き込みにも使用できないディスクリプタを返す。 この非標準のアクセスモードはいくつかの Linux ドライバで、デバイス固有の ioctl(2) 操作にのみ使用されるディスクリプタを返すために使われている。

openat() や他のディレクトリファイルディスクリプタ API の基本原理

openat() やディレクトリファイルディスクリプタを引き数を取る他のシステムコールやライブラリ関数 (faccessat(2), fanotify_mark(2), fchmodat(2), fchownat(2), fstatat(2), futimesat(2), linkat(2), mkdirat(2), mknodat(2), name_to_handle_at(2), readlinkat(2), renameat(2), symlinkat(2), unlinkat(2), utimensat(2) mkfifoat(3), scandirat(3)) は二つの理由から用意されている。 ここでは、 openat コールに関して説明するが、この基本原理は他のインターフェースでも同じである。

最初の理由として、 openat() を使うと、 アプリケーションは、 カレントワーキングディレクトリ以外のディレクトリで open() を使ってファイルをオープンする際に起こり得る競合条件を避けることができる。 これらの競合条件は、 open() に渡されたディレクトリプレフィックスの構成要素が open() の呼び出しと並行して変化する可能性があるという点に由来している。 このような競合条件は、 対象のディレクトリに対するファイルディスクリプタをオープンし、 それから openat() の dirfd 引き数としてそのファイルディスクリプタを指定することで、 避けることができる。

二つ目として、 openat() を使うと、アプリケーションが管理するファイルディスクリプタにより、 スレッド単位の「カレントワーキングディレクトリ」を実装することができる (この機能は、 /proc/self/fd/dirfd を使った方法でも実現することができるが、 効率の面で落とる)。

O_DIRECT

O_DIRECT フラグを使用する場合、ユーザ空間バッファの長さやアドレス、 I/O のファイルオフセットに関してアラインメントの制限が課されることがある。 Linux では、アラインメントの制限はファイルシステムやカーネルのバージョンに よって異なり、全く制限が存在しない場合もある。 しかしながら、現在のところ、指定されたファイルやファイルシステムに対して こうした制限があるかを見つけるための、アプリケーション向けのインタフェースで ファイルシステム非依存のものは存在しない。 いくつかのファイルシステムでは、制限を確認するための独自のインタフェースが 提供されている。例えば、 xfsctl(3) の XFS_IOC_DIOINFO 命令である。

Linux 2.4 では、転送サイズ、 ユーザーバッファのアラインメント、ファイルオフセットは、 ファイルシステムの論理ブロックサイズの倍数でなければならない。 Linux 2.6 では、512 バイトごとの境界に配置されていれば充分である。

メモリバッファがプライベートマッピング (mmap(2) の MAP_PRIVATE フラグで作成されたマッピング) の場合には、O_DIRECT I/O は fork(2) システムコールと同時に決して実行すべきではない (プライベートマッピングには、ヒープ領域に割り当てられたメモリや静的に 割り当てたバッファも含まれる)。非同期 I/O インターフェース (AIO) 経由 やプロセス内の他のスレッドから発行された、このような I/O は、 fork(2) が呼び出される前に完了されるべきである。 そうしなかった場合、データ破壊や、親プロセスや子プロセスでの予期しない 動作が起こる可能性がある。 O_DIRECT I/O 用のメモリバッファが shmat(2) やMAP_SHARED フラグ 付きの mmap(2) で作成された場合には、この制限はあてはまらない。 madvise(2) でメモリバッファにアドバイス MADV_DONTFORK が設定され ている場合にも、この制限はあてはまらない(MADV_DONTFORK はそのメモリ バッファが fork(2) 後に子プロセスからは利用できないことを保証するも のである)。

O_DIRECT フラグは SGI IRIX で導入された。SGI IRIX にも Linux 2.4 と同様の (ユーザーバッファの) アラインメントの制限がある。 また、IRIX には適切な配置とサイズを取得するための fcntl(2) コールがある。 FreeBSD 4.x も同じ名前のフラグを導入したが、アラインメントの制限はない。

O_DIRECT が Linux でサポートされたのは、カーネルバージョン 2.4.10 である。 古い Linux カーネルは、このフラグを単に無視する。 O_DIRECT フラグをサポートしていないファイルシステムもあり、その場合は、 O_DIRECT を使用すると open() は EINVAL で失敗する。

アプリケーションは、同じファイル、 特に同じファイルの重複するバイト領域に対して、 O_DIRECT と通常の I/O を混ぜて使うのは避けるべきである。 ファイルシステムがこのような状況において一貫性の問題を正しく 扱うことができる場合であっても、全体の I/O スループットは どちらか一方を使用するときと比べて低速になるであろう。 同様に、アプリケーションは、同じファイルに対して mmap(2) と直接 I/O (O_DIRECT) を混ぜて使うのも避けるべきである。

NFS で O_DIRECT を使った場合の動作はローカルのファイルシステムの場合と違う。 古いカーネルや、ある種の設定でコンパイルされたカーネルは、 O_DIRECT と NFS の組み合わせをサポートしていないかもしれない。 NFS プロトコル自体はサーバにフラグを渡す機能は持っていないので、 O_DIRECT I/O はクライアント上のページキャッシュをバイパスするだけになり、 サーバは I/O をキャッシュしているかもしれない。 クライアントは、 O_DIRECT の同期機構を保持するため、サーバに対して I/O を同期して行うように依頼する。 サーバによっては、こうした状況下、特に I/O サイズが小さい場合に 性能が大きく劣化する。 また、サーバによっては、I/O が安定したストレージにまで行われたと、 クライアントに対して嘘をつくものもある。 これは、サーバの電源故障が起こった際にデータの完全性が保たれない 危険は少しあるが、性能面での不利な条件を回避するために行われている。 Linux の NFS クライアントでは O_DIRECT I/O でのアラインメントの制限はない。

まとめると、 O_DIRECT は、注意して使うべきであるが、強力なツールとなる可能性を持っている。 アプリケーションは O_DIRECT をデフォルトでは無効になっている性能向上のためのオプションと 考えておくのがよいであろう。

「O_DIRECT でいつも困るのは、インタフェース全部が本当にお馬鹿な点だ。 たぶん危ないマインドコントロール剤で 頭がおかしくなったサルが設計したんじゃないかな」 --- Linus

バグ

現在のところ、 open() の呼び出し時に O_ASYNC を指定してシグナル駆動 I/O を有効にすることはできない。 このフラグを有効にするには fcntl(2) を使用すること。

カーネルが O_TMPFILE 機能をサポートしているかを判定する際に、 EISDIRENOENT の 2 つのエラーコードをチェックしなければならない。

この文書について

この man ページは Linux man-pages プロジェクトのリリース 3.65 の一部 である。プロジェクトの説明とバグ報告に関する情報は http://www.kernel.org/doc/man-pages/ に書かれている。