bionic (2) open_by_handle_at.2.gz

Provided by: manpages-ja-dev_0.5.0.0.20161015+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/ に書かれている。