Provided by: manpages-ja-dev_0.5.0.0.20210215+dfsg-1_all
名前
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() システムコールは、 引き数 dirfd と pathname で指定されるファイルに対応 するファイルハンドルとマウント 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_type と f_handle は後で open_by_handle_at() を呼び出 す場合にだけ必要である。 flags 引き数は、 下記の AT_EMPTY_PATH と AT_SYMLINK_FOLLOW のうち 0 個以上の論理和を取って 構成されるビットマスクである。 引き数 pathname と dirfd はその組み合わせでハンドルを取得するファイルを指定する。 以下の 4 つのパターンがある。 * pathname が空でない文字列で絶対パス名を含む場合、 このパス名が参照するファイルに対する ハンドルが返される。 * pathname が相対パスが入った空でない文字列で、 dirfd が特別な値 AT_FDCWD の場合、 pathname は呼び出し元のカレントワーキングディレクトリに対する相対パスと解釈され、 その ファイルに対するハンドルが返される。 * pathname が相対パスが入った空でない文字列で、 dirfd がディレクトリを参照するファイル ディスクリプターの場合、 pathname は dirfd が参照するディレクトリに対する相対パスと解釈 され、 そのファイルを参照するハンドルが返される。(なぜ「ディレクトリファイルディスクリ プター」が役に立つのかについては openat(2) を参照。) * pathname が空の文字列で flags に AT_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_FOLLOW が flags に 指定されると、 pathname がシンボリックリンクの場合にリンクの展開が行われる (リンクが参照す るファイルに対するハンドルが返される)。 open_by_handle_at() open_by_handle_at() システムコールは handle が参照するファイルをオープンする。 handle は 前に呼び出した name_to_handle_at() が返したファイルハンドルである。 mount_fd 引き数は、 handle がそのファイルシステムに関連すると解釈されるマウントされたファ イルシステム内の任意のオブジェクト (ファイル、 ディレクトリなど) のファイルディスクリプ ターである。 特別な値 AT_FDCWD も指定できる。 この値は呼び出し元のカレントワーキングディレ クトリを意味する。 引き数 flags は open(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_bytes が MAX_HANDLE_SZ よりも大きい。 ENOENT pathname が空文字列だが、 flags に AT_EMPTY_PATH がされていなかった。 ENOTDIR dirfd で指定されたファイルディスクリプターがディレクトリを参照しておらず、 両方の flags に AT_EMPTY_PATH が指定され、 かつ pathname が空文字列である場合でもない。 EOPNOTSUPP ファイルシステムがパス名をファイルハンドルへの変換をサポートしていない。 EOVERFLOW 呼び出しに渡された handle->handle_bytes の値が小さすぎた。 このエラーが発生した際、 handle->handle_bytes はハンドルに必要なサイズに更新される。 open_by_handle_at() は以下のエラーで失敗することがある。 EBADF mount_fd がオープンされたファイルディスクリプターでない。 EFAULT handle がアクセス可能なアドレス空間の外を指している。 EINVAL handle->handle_bytes が MAX_HANDLE_SZ より大きいか 0 に等しい。 ELOOP handle がシンボリックリンクを参照しているが、 flags に O_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 がシンボリックリンクを参照していて、 flags に AT_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 リリー スの libblkid と libmount のドキュメント。
この文書について
この man ページは Linux man-pages プロジェクトのリリース 3.79 の一部 である。プロジェクト の説明とバグ報告に関する情報は http://www.kernel.org/doc/man-pages/ に書かれている。