Provided by: manpages-ja-dev_0.5.0.0.20221215+dfsg-1_all bug

名前

       name_to_handle_at,  open_by_handle_at - パス名に対するハンドルの取得とハンドルによるファイ
       ルのオープン

書式

       #define _GNU_SOURCE         /* feature_test_macros(7) 参照 */
       #include <sys/types.h>
       #include <sys/stat.h>
       #include <fcntl.h>

       int name_to_handle_at(int dirfd, const char *pathname,
                             struct file_handle *handle,
                             int *mount_id, int flags);

       int open_by_handle_at(int mount_fd, struct file_handle *handle,
                             int flags);

説明

       システムコール name_to_handle_at() と open_by_handle_at() は openat(2) の機能を 2 つに分割
       したものである。     name_to_handle_at()    は指定されたファイルに対応するハンドルを返す。
       open_by_handle_at()  は  name_to_handle_at()  が返したハンドルに対応するファイルをオープン
       し、 オープンされたファイルディスクリプターを返す。

   name_to_handle_at()
       name_to_handle_at() システムコールは、 引数 dirfdpathname で指定されるファイルに対応す
       るファイルハンドルとマウント ID を返す。 ファイルハンドルは引数 handle で返される。 handle
       は以下の形式の構造体へのポインターである。

           struct file_handle {
               unsigned int  handle_bytes;   /* Size of f_handle [in, out] */
               int           handle_type;    /* Handle type [out] */
               unsigned char f_handle[0];    /* File identifier (sized by
                                                caller) [out] */
           };

       It  is  the  caller's responsibility to allocate the structure with a size large enough to
       hold the handle returned in f_handle.  Before the call, the handle_bytes field  should  be
       initialized  to  contain  the  allocated  size for f_handle.  (The constant MAX_HANDLE_SZ,
       defined in <fcntl.h>, specifies the maximum expected size for a file handle.  It is not  a
       guaranteed  upper  limit  as  future filesystems may require more space.)  Upon successful
       return, the handle_bytes field is updated to contain the number of bytes actually  written
       to f_handle.

       The  caller  can discover the required size for the file_handle structure by making a call
       in which handle->handle_bytes is zero; in  this  case,  the  call  fails  with  the  error
       EOVERFLOW  and  handle->handle_bytes  is set to indicate the required size; the caller can
       then use this information to allocate a  structure  of  the  correct  size  (see  EXAMPLES
       below).   Some  care  is needed here as EOVERFLOW can also indicate that no file handle is
       available for this particular name in a filesystem which does normally support file-handle
       lookup.   This  case  can  be  detected  when  the  EOVERFLOW  error  is  returned without
       handle_bytes being increased.

       handle_bytes フィールドを使用する以外は、 呼び出し元は file_handle  構造体の内容を意識せず
       に扱うべきである。  フィールド handle_typef_handle は後で open_by_handle_at() を呼び出
       す場合にだけ必要である。

       flags 引数は、 下記の AT_EMPTY_PATHAT_SYMLINK_FOLLOW のうち 0 個以上の論理和を取って構
       成されるビットマスクである。

       引数  pathnamedirfd はその組み合わせでハンドルを取得するファイルを指定する。 以下の 4
       つのパターンがある。

       *  pathname が空でない文字列で絶対パス名を含む場合、  このパス名が参照するファイルに対する
          ハンドルが返される。

       *  pathname   が相対パスが入った空でない文字列で、   dirfd  が特別な値  AT_FDCWD  の場合、
          pathname は呼び出し元のカレントワーキングディレクトリに対する相対パスと解釈され、  その
          ファイルに対するハンドルが返される。

       *  pathname  が相対パスが入った空でない文字列で、  dirfd  がディレクトリを参照するファイル
          ディスクリプターの場合、 pathnamedirfd が参照するディレクトリに対する相対パスと解釈
          され、  そのファイルを参照するハンドルが返される。(なぜ「ディレクトリファイルディスクリ
          プター」が役に立つのかについては openat(2) を参照。)

       *  pathname が空の文字列で flagsAT_EMPTY_PATH が指定されている場合、 dirfd  には任意の
          種別のファイルを参照するオープンされたファイルディスクリプターか AT_FDCWD (カレントワー
          キングディレクトリを意味する) を指定でき、 dirfd が参照するファイルに対するハンドルが返
          される。

       The  mount_id  argument returns an identifier for the filesystem mount that corresponds to
       pathname.   This  corresponds  to  the  first   field   in   one   of   the   records   in
       /proc/self/mountinfo.   Opening  the  pathname  in the fifth field of that record yields a
       file descriptor for the mount point; that file descriptor can be used in a subsequent call
       to  open_by_handle_at().   mount_id  is returned both for a successful call and for a call
       that results in the error EOVERFLOW.

       デフォルトでは、  name_to_handle_at()  は  pathname   がシンボリックリンクの場合にその展開
       (dereference)  を行わず、 リンク自身に対するハンドルを返す。 AT_SYMLINK_FOLLOWflags に
       指定されると、 pathname がシンボリックリンクの場合にリンクの展開が行われる (リンクが参照す
       るファイルに対するハンドルが返される)。

       name_to_handle_at()   does not trigger a mount when the final component of the pathname is
       an automount point.  When a filesystem supports both file handles and automount points,  a
       name_to_handle_at()   call  on an automount point will return with error EOVERFLOW without
       having increased handle_bytes.  This can happen since Linux 4.13 with NFS when accessing a
       directory  which  is  on a separate filesystem on the server.  In this case, the automount
       can be triggered by adding a "/" to the end of the pathname.

   open_by_handle_at()
       open_by_handle_at() システムコールは handle が参照するファイルをオープンする。  handle  は
       前に呼び出した name_to_handle_at() が返したファイルハンドルである。

       mount_fd  引数は、 handle がそのファイルシステムに関連すると解釈されるマウントされたファイ
       ルシステム内の任意のオブジェクト (ファイル、 ディレクトリなど)  のファイルディスクリプター
       である。 特別な値 AT_FDCWD も指定できる。 この値は呼び出し元のカレントワーキングディレクト
       リを意味する。

       引数 flagsopen(2) と同じである。 handle がシンボリックリンクを参照している場合、  呼び
       出し元は   O_PATH   フラグを指定しなければならず、  そのシンボリックリンクは展開されない。
       O_NOFOLLOW が指定された場合は、 O_NOFOLLOW は無視される。

       open_by_handle_at() を呼び出すには、 呼び出し元が  CAP_DAC_READ_SEARCH  ケーパビリティーを
       持っていなければならない。

返り値

       成功すると、  name_to_handle_at() は 0 を返し、 open_by_handle_at() はファイルディスクリプ
       ター (非負の整数) を返す。

       エラーの場合、 どちらのシステムコールも -1 を返し、  errno  にエラーの原因を示す値を設定す
       る。

エラー

       name_to_handle_at() と open_by_handle_at() は openat(2) と同じエラーで失敗する。 また、 こ
       れらのシステムコールは以下のエラーで失敗することもある。

       name_to_handle_at() は以下のエラーで失敗することがある。

       EFAULT pathname, mount_id, handle のどれかがアクセス可能なアドレス空間の外を指している。

       EINVAL flags に無効なビット値が含まれている。

       EINVAL handle->handle_bytesMAX_HANDLE_SZ よりも大きい。

       ENOENT pathname が空文字列だが、 flagsAT_EMPTY_PATH がされていなかった。

       ENOTDIR
              dirfd  で指定されたファイルディスクリプターがディレクトリを参照しておらず、  両方の
              flagsAT_EMPTY_PATH が指定され、 かつ pathname が空文字列である場合でもない。

       EOPNOTSUPP
              ファイルシステムがパス名をファイルハンドルへの変換をサポートしていない。

       EOVERFLOW
              呼び出しに渡された handle->handle_bytes の値が小さすぎた。 このエラーが発生した際、
              handle->handle_bytes はハンドルに必要なサイズに更新される。

       open_by_handle_at() は以下のエラーで失敗することがある。

       EBADF  mount_fd がオープンされたファイルディスクリプターでない。

       EFAULT handle がアクセス可能なアドレス空間の外を指している。

       EINVAL handle->handle_bytesMAX_HANDLE_SZ より大きいか 0 に等しい。

       ELOOP  handle がシンボリックリンクを参照しているが、 flagsO_PATH がされていなかった。

       EPERM  呼び出し元が CAP_DAC_READ_SEARCH ケーパビリティを持っていない。

       ESTALE 指定された handle が有効ではない。 このエラーは、  例えばファイルが削除された場合な
              どに発生する。

バージョン

       これらのシステムコールは Linux 2.6.39 で初めて登場した。ライブラリによるサポートはバージョ
       ン 2.14 以降の glibc で提供されている。

準拠

       これらのシステムコールは非標準の Linux の拡張である。

       FreeBSD には getfh() と openfh() というほとんど同じ機能のシステムコールのペアが存在する。

注意

       あるプロセスで name_to_handle_at() を使ってファイルハンドルを生成して、 そのハンドルを別の
       プロセスの open_by_handle_at() で使用することができる。

       いくつかのファイルシステムでは、    パス名からファイルハンドルへの変換がサポートされていな
       い。 例えば、 /proc, /sys や種々のネットワークファイルシステムなどである。

       ファイルハンドルは、 ファイルが削除されたり、 その他のファイルシステム固有の理由で、  無効
       ("stale")  になる場合がある。  無効なハンドルであることは、 open_by_handle_at() からエラー
       ESTALE が返ることで通知される。

       これらのシステムコールは、    ユーザー空間のファイルサーバーでの使用を意図して設計されてい
       る。  例えば、  ユーザー空間 NFS サーバーがファイルハンドルを生成して、 そのハンドルを NFS
       クライアントに渡すことができる。  その後、  クライアントがファイルをオープンしようとした際
       に、  このハンドルをサーバーに送り返すことができる。  このような機能により、  ユーザー空間
       ファイルサーバーは、 そのサーバーが提供するファイルに関してステートレスで  (状態を保持せず
       に) 動作することができる。

       pathname  がシンボリックリンクを参照していて、 flagsAT_SYMLINK_FOLLOW が指定されていな
       い場合、 name_to_handle_at() は (シンボリックが参照するファイルではなく)  リンクに対するハ
       ンドルを返す。 ハンドルを受け取ったプロセスは、 open_by_handle_at() の O_PATH フラグを使っ
       てハンドルをファイルディスクリプターに変換し、              そのファイルディスクリプターを
       readlinkat(2)  や fchownat(2) などのシステムコールの dirfd 引数として渡すことで、 そのシン
       ボリックリンクに対して操作を行うことができる。

   永続的なファイルシステム ID の取得
       /proc/self/mountinfo のマウント ID は、 ファイルシステムのアンマウント、マウントが行われる
       に連れて再利用されることがある。  したがって、 name_to_handle_at() (の *mount_id) で返され
       たマウント ID は対応するマウントされたファイルシステムを表す永続的な ID と考えるべきではな
       い。  ただし、 アプリケーションは、 マウント ID に対応する  mountinfo レコードの情報を使う
       ことで、 永続的な ID を得ることができる。

       例えば、 mountinfo レコードの 5 番目のフィールドのデバイス名を使って、  /dev/disks/by-uuid
       のシンボリックリンク経由で対応するデバイス  UUID を検索できる。 (UUID を取得するもっと便利
       な方法は libblkid(3) ライブラリを使用することである。) そのプロセスは、逆に、 この UUID を
       使ってデバイス名を検索し、  対応するマウントポイントを取得することで、 open_by_handle_at()
       で使用する mount_fd 引数を生成することができる。

       以下の 2 つのプログラムは name_to_handle_at() と open_by_handle_at()  の使用例を示したもの
       である。 最初のプログラム (t_name_to_handle_at.c) は name_to_handle_at() を使用して、 コマ
       ンドライン引数で指定されたファイルに対応するファイルハンドルとマウント ID を取得する。  ハ
       ンドルとマウント ID は標準出力に出力される。

       2 つ目のプログラム (t_open_by_handle_at.c) は、 標準入力からマウント ID とファイルハンドル
       を読み込む。 それから、  open_by_handle_at()  を利用して、  そのハンドルを使ってファイルを
       オープンする。    追加のコマンドライン引数が指定された場合は、    open_by_handle_at()   の
       mount_fd 引数は、 この引数で渡された名前のディレクトリをオープンして取得する。  それ以外の
       場合、 /proc/self/mountinfo からスキャンして標準入力から読み込んだマウント ID に一致するマ
       ウント  ID   を検索し、   そのレコードで指定されているマウントディレクトリをオープンして、
       mount_fd  を入手する。 (これらのプログラムではマウント ID が永続的ではない点についての対処
       は行わない。)

       以下のシェルセッションは、これら 2 つのプログラムの使用例である。

           $ echo 'Can you please think about it?' > cecilia.txt
           $ ./t_name_to_handle_at cecilia.txt > fh
           $ ./t_open_by_handle_at < fh
           open_by_handle_at: Operation not permitted
           $ sudo ./t_open_by_handle_at < fh      # Need CAP_SYS_ADMIN
           Read 31 bytes
           $ rm cecilia.txt

       ここで、 ファイルを削除し (すぐに) 再作成する。 同じ内容で (運がよければ) 同じ inode  にな
       る。 この場合でも、 open_by_handle_at() はこのファイルハンドルが参照する元のファイルがすで
       に存在しないことを認識する。

           $ stat --printf="%i\n" cecilia.txt     # Display inode number
           4072121
           $ rm cecilia.txt
           $ echo 'Can you please think about it?' > cecilia.txt
           $ stat --printf="%i\n" cecilia.txt     # Check inode number
           4072121
           $ sudo ./t_open_by_handle_at < fh
           open_by_handle_at: Stale NFS file handle

   プログラムのソース: t_name_to_handle_at.c

       #define _GNU_SOURCE
       #include <sys/types.h>
       #include <sys/stat.h>
       #include <fcntl.h>
       #include <stdio.h>
       #include <stdlib.h>
       #include <unistd.h>
       #include <errno.h>
       #include <string.h>

       #define errExit(msg)    do { perror(msg); exit(EXIT_FAILURE); \
                               } while (0)

       int
       main(int argc, char *argv[])
       {
           struct file_handle *fhp;
           int mount_id, fhsize, flags, dirfd;
           char *pathname;

           if (argc != 2) {
               fprintf(stderr, "Usage: %s pathname\n", argv[0]);
               exit(EXIT_FAILURE);
           }

           pathname = argv[1];

           /* file_handle 構造体を確保する */

           fhsize = sizeof(*fhp);
           fhp = malloc(fhsize);
           if (fhp == NULL)
               errExit("malloc");

           /* name_to_handle_at() を最初に呼び出して
              ファイルハンドルに必要なサイズを入手する */

           dirfd = AT_FDCWD;           /* For name_to_handle_at() calls */
           flags = 0;                  /* For name_to_handle_at() calls */
           fhp->handle_bytes = 0;
           if (name_to_handle_at(dirfd, pathname, fhp,
                       &mount_id, flags) != -1 || errno != EOVERFLOW) {
               fprintf(stderr, "Unexpected result from name_to_handle_at()\n");
               exit(EXIT_FAILURE);
           }

           /* file_handle 構造体を正しいサイズに確保し直す */

           fhsize = sizeof(*fhp) + fhp->handle_bytes;
           fhp = realloc(fhp, fhsize);         /* Copies fhp->handle_bytes */
           if (fhp == NULL)
               errExit("realloc");

           /* コマンドラインで指定されたパス名からファイルハンドルを取得 */

           if (name_to_handle_at(dirfd, pathname, fhp, &mount_id, flags) == -1)
               errExit("name_to_handle_at");

           /* t_open_by_handle_at.c で後で再利用できるように、マウント ID、
              ファイルハンドルのサイズ、ファイルハンドルを標準出力に書き出す */

           printf("%d\n", mount_id);
           printf("%u %d   ", fhp->handle_bytes, fhp->handle_type);
           for (int j = 0; j < fhp->handle_bytes; j++)
               printf(" %02x", fhp->f_handle[j]);
           printf("\n");

           exit(EXIT_SUCCESS);
       }

   プログラムのソース: t_open_by_handle_at.c

       #define _GNU_SOURCE
       #include <sys/types.h>
       #include <sys/stat.h>
       #include <fcntl.h>
       #include <limits.h>
       #include <stdio.h>
       #include <stdlib.h>
       #include <unistd.h>
       #include <string.h>

       #define errExit(msg)    do { perror(msg); exit(EXIT_FAILURE); \
                               } while (0)

       /* /proc/self/mountinfo をスキャンして、マウント ID が 'mount_id' に
          一致する行を探す。 (もっと簡単な方法は 'util-linux' プロジェクト
          が提供する 'libmount' ライブラリをインストールして使うことである)
          対応するマウントパスをオープンし、得られたファイルディスクリプターを返す。 */

       static int
       open_mount_path_by_id(int mount_id)
       {
           char *linep;
           size_t lsize;
           char mount_path[PATH_MAX];
           int mi_mount_id, found;
           ssize_t nread;
           FILE *fp;

           fp = fopen("/proc/self/mountinfo", "r");
           if (fp == NULL)
               errExit("fopen");

           found = 0;
           linep = NULL;
           while (!found) {
               nread = getline(&linep, &lsize, fp);
               if (nread == -1)
                   break;

               nread = sscanf(linep, "%d %*d %*s %*s %s",
                              &mi_mount_id, mount_path);
               if (nread != 2) {
                   fprintf(stderr, "Bad sscanf()\n");
                   exit(EXIT_FAILURE);
               }

               if (mi_mount_id == mount_id)
                   found = 1;
           }
           free(linep);

           fclose(fp);

           if (!found) {
               fprintf(stderr, "Could not find mount point\n");
               exit(EXIT_FAILURE);
           }

           return open(mount_path, O_RDONLY);
       }

       int
       main(int argc, char *argv[])
       {
           struct file_handle *fhp;
           int mount_id, fd, mount_fd, handle_bytes;
           ssize_t nread;
           char buf[1000];
       #define LINE_SIZE 100
           char line1[LINE_SIZE], line2[LINE_SIZE];
           char *nextp;

           if ((argc > 1 && strcmp(argv[1], "--help") == 0) || argc > 2) {
               fprintf(stderr, "Usage: %s [mount-path]\n", argv[0]);
               exit(EXIT_FAILURE);
           }

           /* マウント ID とファイルハンドル情報が入った標準入力:

                Line 1: <mount_id>
                Line 2: <handle_bytes> <handle_type>   <bytes of handle in hex>
           */

           if ((fgets(line1, sizeof(line1), stdin) == NULL) ||
                  (fgets(line2, sizeof(line2), stdin) == NULL)) {
               fprintf(stderr, "Missing mount_id / file handle\n");
               exit(EXIT_FAILURE);
           }

           mount_id = atoi(line1);

           handle_bytes = strtoul(line2, &nextp, 0);

           /* handle_bytes があれば、
              file_handle 構造体をここで割り当てできる */

           fhp = malloc(sizeof(*fhp) + handle_bytes);
           if (fhp == NULL)
               errExit("malloc");

           fhp->handle_bytes = handle_bytes;

           fhp->handle_type = strtoul(nextp, &nextp, 0);

           for (int j = 0; j < fhp->handle_bytes; j++)
               fhp->f_handle[j] = strtoul(nextp, &nextp, 16);

           /* マウントポイントのファイルディスクリプターを取得する。
              取得は、コマンドラインで指定されたパス名をオープンするか、
              /proc/self/mounts をスキャンして標準入力から受け取った
              'mount_id' に一致するマウントを探すことで行う。 */

           if (argc > 1)
               mount_fd = open(argv[1], O_RDONLY);
           else
               mount_fd = open_mount_path_by_id(mount_id);

           if (mount_fd == -1)
               errExit("opening mount fd");

           /* ハンドルとマウントポイントを使ってファイルをオープンする */

           fd = open_by_handle_at(mount_fd, fhp, O_RDONLY);
           if (fd == -1)
               errExit("open_by_handle_at");

           /* そのファイルからバイトを読み出す */

           nread = read(fd, buf, sizeof(buf));
           if (nread == -1)
               errExit("read");

           printf("Read %zd bytes\n", nread);

           exit(EXIT_SUCCESS);
       }

関連項目

       open(2), libblkid(3), blkid(8), findfs(8), mount(8)

       ⟨https://www.kernel.org/pub/linux/utils/util-linux/⟩ で入手できる最新の util-linux  リリー
       スの libblkidlibmount のドキュメント。

この文書について

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