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/ に書かれている。