UTIMENSAT(2)

名前
utimensat, futimens - ファイルのタイムスタンプをナノ秒精度で変更する

書式
#include <fcntl.h> /* AT_* 定数の定義 */
#include <sys/stat.h>

int utimensat(int dirfd, const char *pathname,
              const struct timespec times[2], int flags);
int futimens(int fd, const struct timespec times[2]);

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

utimensat():
    Since glibc 2.10:
        _XOPEN_SOURCE >= 700 || _POSIX_C_SOURCE >= 200809L
    Before glibc 2.10:
        _ATFILE_SOURCE
futimens():
    Since glibc 2.10:
        _XOPEN_SOURCE >= 700 || _POSIX_C_SOURCE >= 200809L
    Before glibc 2.10:
        _GNU_SOURCE

説明
utimensat() と futimens() はファイルのタイムスタンプをナノ秒精度で更新する。 ファイルのタイムスタンプをセットする際に、 昔からある utime(2) と utimes(2) で指定できるのはそれぞれ秒精度とマイクロ秒精度であり、 この点が異なる点である。 utimensat() では、 ファイルは pathname で渡されるパス名で指定される。 futimens() では、 タイムスタンプを更新するファイルはオープンしたファイルディスクリプター fd で指定される。 どちらのシステムコールでも、 ファイルの新しいタイムスタンプは配列 times で指定される。 times[0] は新しい「最終アクセス時刻」 (atime) を指定し、 times[1] は新しい「最終修正時刻」 (mtime) を指定する。 times の各要素では、 時刻を、 紀元 (Epoch; 1970-01-01 00:00:00 +0000 (UTC)) からの秒数とナノ秒として指定する。 この情報は以下の形式の構造体で渡す。
struct timespec {
    time_t tv_sec;        /* 秒 */
    long   tv_nsec;       /* ナノ秒 */
};

ファイルの更新後のタイムスタンプは、 指定された時刻を超えないファイルシステムがサポートする最大の値に設定される。 それぞれの timespec 構造体の tv_nsec フィールドには UTIME_NOW を指定することができ、 その場合はファイルの対応するタイムスタンプは現在時刻に設定される。 timespec 構造体の tv_nsec フィールドには UTIME_OMIT を指定することができ、 その場合はファイルの対応するタイムスタンプは変更されないままとなる。 このどちらの場合も、 対応する tv_sec フィールドの値は無視される。 times が NULL の場合、 両方のタイムスタンプが現在時刻に設定される。

  アクセス許可の要件
ファイルの両方のタイムスタンプを現在時刻に設定するためには (すなわち times が NULL か、 両方の tv_nsec フィールドに UTIME_NOW が指定するためには)、 以下のいずれかが必要である。

1.  呼び出し元がファイルに対する書き込み許可を持っている。

2.  呼び出し元の実効ユーザー ID がファイルの所有者と一致している。

3.  呼び出し元が適切な特権を持っている。

両方のタイムスタンプを現在時刻に設定する以外の変更するには (times が NULL 以外、 または どちらの tv_nsec フィールドも UTIME_NOW でなくどちらの tv_nsec フィールドも UTIME_OMIT でもない場合)、 上記の条件 2 か条件 3 が必要である。 両方の tv_nsec フィールドに UTIME_OMIT が指定された場合、 ファイルの所有権やアクセス許可のチェックは行われず、 ファイルのタイムスタンプは変更されないが、 それ以外のエラー条件はこの場合も検出される。

  utimensat() 固有の内容
pathname が相対パスの場合、 デフォルトでは、 オープンしたファイルディスクリプター dirfd が参照するディレクトリに対する相対パスと解釈される (utimes(2) のようにカレントワーキングディレクトリに対する相対パスと解釈されるわけではない)。 なぜこのシステムコールが役に立つのかの説明は openat(2) を参照。 pathname が相対パスで dirfd が特別な値 AT_FDCWD の場合、 pathname は (utimes(2) 同様) 呼び出したプロセスのカレントワーキングディレクトリに対する相対パスと解釈される。 pathname が絶対パスの場合、 dirfd は無視される。 flags フィールドはビットマスクで、 0 か <fcntl.h> で定義されている以下の定数を指定できる。

AT_SYMLINK_NOFOLLOW
    pathname がシンボリックリンクの場合に、 リンクが参照するファイルではなくリンク自身のタイムスタンプを更新する。

返り値
成功すると、 utimensat() と futimens() は 0 を返す。 エラーの場合、 -1 を返し、 errno にエラーを示す値を設定する。

エラー

EACCES
    times が NULL か両方の tv_nsec の値が UTIME_NOW で、 以下のいずれかにあてはまる。
    *   呼び出し元の実効ユーザー ID がファイルの所有者と一致せず、 呼び出し元がそのファイルに対する書き込みアクセス許可を持っておらず、 呼び出し元が特権を持っていない (Linux では、ケーパビリティー CAP_FOWNER か CAP_DAC_OVERRIDE を持っていない)。
    *   ファイルが immutable (変更不可) の属性が付いている (chattr(1) 参照)。

EBADF
    (futimens()) fd が有効なファイルディスクリプターでない。

EBADF
    (utimensat()) pathname が相対パスだが、 dirfd が AT_FDCWD でも有効なファイルディスクリプターでもない。

EFAULT
    times が無効なアドレスを指している。 dirfd が AT_FDCWD で pathname が NULL か無効なアドレスである。

EINVAL
    flags に無効な値が指定された。

EINVAL
    tv_nsec フィールドの一つが無効な値である (0 から 999,999,999 までの値の範囲外の値で、 UTIME_NOW でも UTIME_NOW でもない)。 tv_sec フィールドの一つが無効な値である。

EINVAL
    pathname が NULL で、 dirfd が AT_FDCWD ではなく、 flags に AT_SYMLINK_NOFOLLOW が指定されている。

ELOOP
    (utimensat()) pathname を解決する際に遭遇したシンボリックリンクが多すぎた。

ENAMETOOLONG
    (utimensat()) pathname が長すぎる。

ENOENT
    (utimensat()) pathname の構成要素が存在するディレクトリかファイルを参照していない。 pathname が空文字列である。

ENOTDIR
    (utimensat()) pathname が相対パスだが、 dirfd が AT_FDCWD でもディレクトリを参照するファイルディスクリプターでもない。 pathname の構成要素のディレクトリ部分がディレクトリではない。

EPERM
    呼び出し元がタイムスタンプの一方もしくは両方を現在時刻以外の値に更新しようとしたか、 もしくはタイムスタンプの一方を現在時刻に変更し、もう一方は変更しないままにしようとした (すなわち times が NULL 以外で、 どちらの tv_nsec フィールドも UTIME_NOW でもなく、 どちらの tv_nsec フィールドも UTIME_OMIT でもない) 場合で、 以下のいずれかにあてはまる。
    *   呼び出し元の実効ユーザー ID がファイルの所有者と一致せず、 呼び出し元が特権を持っていない (Linux では、ケーパビリティー CAP_FOWNER を持っていない)。
    *   ファイルに追記のみか変更不可 (immutable) の属性が付いている (chattr(1) 参照)。

EROFS
    ファイルが読み込み専用のファイルシステム上にある。

ESRCH
    (utimensat()) pathname の構成要素のディレクトリ部分のいずれかで検索許可がなかった。

バージョン
utimensat() はカーネル 2.6.22 で Linux に追加された。 glibc のサポートはバージョン 2.6 で追加された。 futimens() のサポートは glibc 2.6 で初めて登場した。

準拠
futimens() と utimensat() は POSIX.1-2008 で規定されている。

注意
utimensat() が登場した結果、 futimesat(2) は非推奨となった。 Linux では、 変更不可 (immutable) の属性が付いたファイルのタイムスタンプを変更することはできず、 また、 追記のみ (append-only) の属性が付いたファイルで可能な変更は、 タイムスタンプを現在時刻に設定することだけである。 (これは Linux の utime(2) や utimes() の昔からの動作と一貫性がある動作である)。 Linux では、 futimens() は utimensat() システムコールを使って実装されているライブラリ関数である。 これを可能にするため、 Linux の utimensat() システムコールは非標準の機能を実装している。 pathname が NULL の場合、 呼び出しはファイルディスクリプター dirfd が参照するファイルのタイムスタンプを変更する (ファイルディスクリプターはどのタイプのファイルを参照していてもよい)。 この機能を利用して、 futimens(fd, times) は以下のように実装されている。
    utimensat(fd, NULL, times, 0);
両方の tv_nsec フィールドに UTIME_OMIT が指定された場合、 utimensat() の Linux 実装は、 dirfd と pathname が参照するファイルが存在しない場合でも成功する。

バグ
カーネル 2.6.26 より前では utimensat() と futimens() にはいくつかの悩ましいバグがあった。 これらのバグは、 ドラフト版の POSIX.1 規格との不整合や、 以前からの Linux での動作との違いである。

*   POSIX.1 では、 tv_nsec フィールドの一つが UTIME_NOW か UTIME_OMIT の場合、 対応する tv_sec フィールドは無視されると規定されている。 しかし、 tv_sec フィールドの値を 0 にする必要があった (さもなければエラー EINVAL となった)。

*   いくつかのバグのため、 アクセス許可のチェックにおいて、両方の tv_nsec フィールドが UTIME_NOW に設定された場合が、 常に times に NULL が設定された場合と同じに扱われるわけではなく、 tv_nsec の一つが UTIME_NOW でもう一方が UTIME_OMIT の場合が、 times に任意の値が入った構造体の配列へのポインターが指定された場合と 同じように扱われるわけではない。 その結果、いくつかの場合では、 a) ファイルのタイムスタンプが、 更新を実行する許可を持たないプロセスによって更新されることがある、 b) ファイルのタンプスタンプが、 更新を実行する許可を持つプロセスによって更新できないことがある、 c) エラーの場合に間違った errno 値が返る。

*   POSIX.1 では、 ファイルの書き込み許可を持つプロセス (a process that has write access to the file) は、そのファイルに対して times に NULL や 両方の tv_nsec フィールドが UTIME_NOW の構造体の配列を指定して呼び出しを行い、 両方のタイムスタンプを現在時刻に更新することができると規定されている。 しかし、 futimens() では、 ファイルディスクリプターのアクセス許可が書き込みを許可しているか (access mode of the file descriptor allows writing)のチェックが行われる。

関連項目
chattr(1), futimesat(2), openat(2), stat(2), utimes(2), futimes(3), path_resolution(7), symlink(7)

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