Provided by: manpages-ja-dev_0.5.0.0.20180315+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] */
           };

       f_handle で返されるハンドルを保持するのに十分な大きさの構造体を確保するのは、 呼び出し元が
       責任をもって行う必要がある。 呼び出し前に、 handle_bytes フィールドは f_handle  用に格納さ
       れたサイズで初期化すべきである  (<fcntl.h> で定義されている定数 MAX_HANDLE_SZ でファイルハ
       ンドルの最大サイズが規定されている)。 呼び出しが成功でリターンする際、 handle_bytes フィー
       ルドは f_handle に実際に書き込まれたバイト数に更新される。

       呼び出し元では、 handle->handle_bytes を 0 に設定して呼び出しを行うことで、 file_handle 構
       造体に必要なサイズを知ることができる。 この場合、  この呼び出しはエラー  EOVERFLOW  で失敗
       し、  handle->handle_bytes に必要なサイズが設定される。 呼び出し元はこの情報を使って、正し
       いサイズの構造体を割り当てることができる (下記の「例」を参照)。

       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 が参照するファイルに対するハンドルが返
          される。

       mount_id 引き数は、 pathname に対応するファイルシステムのマウントの識別子を返す。 この識別
       子は  /proc/self/mountinfo  のいずれかのレコードの最初のフィールドに対応する。  対応するレ
       コードの 5 番目のフィールドのパス名をオープンすると、 このマウントポイントのファイルディス
       クリプターが得られる。 このファイルディスクリプターはこの後の open_by_handle_at() の呼び出
       しで使用できる。

       デフォルトでは、   name_to_handle_at()  は  pathname  がシンボリックリンクの場合にその展開
       (dereference) を行わず、 リンク自身に対するハンドルを返す。 AT_SYMLINK_FOLLOWflags  に
       指定されると、 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, j;
           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(struct file_handle) + 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("%d %d   ", fhp->handle_bytes, fhp->handle_type);
           for (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, j;
           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(struct file_handle) + handle_bytes);
           if (fhp == NULL)
               errExit("malloc");

           fhp->handle_bytes = handle_bytes;

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

           for (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 プロジェクトのリリース 3.79 の一部 である。プロジェクト
       の説明とバグ報告に関する情報は http://www.kernel.org/doc/man-pages/ に書かれている。