fhandl
Rev.1を表示中。最新版はこちら。
Linux man pages onlineの内容を実装を踏まえて書き改めて形となります。ファイル読み書きは、そのファイルのパスをdentry走査し、そのdentryからのinodeのコールバック関数をfileオペレーションコールバックに設定し、ファイルの読み書きを行っています。fhandleはファイル名からファイル読み書きするのでなく、ファイルシステムのinode情報から直接ファイル読み書きするものです。
nfsではファイルパス等、各クライアント依存でリアルタイムに反映されません。実装によっては、クライアントサイドのキャッシュとも影響してきます。従ってファイルのやり取りは直接inodeで行った方が他のクライアントからの影響が少ないわけです。例えば、ファイルをrenameしてもinodeは同じです。従ってrenameに関係なく、rename前の名前で。というイメージでrenameされたファイルを読み書きできるわけです。ただし、inodeからファイルを取得すると言うことで、この実装はファイルシステム依存となります。なおファイルシステムにstruct export_operationsが設定されていなければ、サポートされません。
システムコールname_to_handle_atとopen_by_handle_atは、ユーザランドでnfsを実装するためのシステムコールらしく、下記サンプルは、Linux man pages onlineサンプルのエッセンスだけを抜き出し、見やすくしたものです。
fhandlesize_get()でstruct file_handle *fhpのサイズを取得しています。そこに設定されるinode情報はファイルシステムによって異なるからです。
fhandlesize_get()で/mntのファイルシステムのfhandleサイズを取得します。この時fh.handle_bytes = 0でname_to_handle_at()をコールすると、fhにはhandle_bytesとhandle_typeしか設定されません。取得したサイズメモリを確保したfphを取得し、fhandle_get()で/mnt/hogeのfhandlを取得します。
このfhandleでfhandle_read()をコールすることで、/mnt/hogeを読み出します。なお、/mnt/hogeのファイルシステムのマウントポイントのFILEIDが必要となります。実サンプルでは/proc/self/mountinfoからname_to_handle_at()で取得した/mnt/hogeのマウントIDからマウント先を取得するようになっていますが、ここでは/mntと決め打ちしています。一般ユーザはマウント先をunmountできません。従ってパス名を変更することもできません。
#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> void fhandle_read(int mount_fd, struct file_handle *fhp); void fhandle_get(char* file, struct file_handle* fhp, int* mount_id); int fhandlesize_get(char* path); #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; int size; size = fhandlesize_get("/mnt/hoge"); fhsize = sizeof(struct file_handle) + size; fhp = malloc(fhsize); if (fhp == NULL) errExit("realloc"); fhp->handle_bytes = size; fhandle_get("/mnt/hoge", fhp, &mount_id); fhandle_read(mount_id, fhp); exit(EXIT_SUCCESS); } int fhandlesize_get(char* path) { struct file_handle fh; int mount_id; fh.handle_bytes = 0; if (name_to_handle_at(AT_FDCWD, path, &fh, &mount_id, 0) != -1 || errno != EOVERFLOW) { fprintf(stderr, "Unexpected result from name_to_handle_at()\n"); exit(EXIT_FAILURE); } return fh.handle_bytes; } void fhandle_get(char* file, struct file_handle* fhp, int* mount_id) { int j; if (name_to_handle_at(AT_FDCWD, "/mnt/hoge", fhp, mount_id, 0) == -1) errExit("name_to_handle_at"); printf("mount :%d\n", *mount_id); printf("size :%d\n", fhp->handle_bytes); printf("type :%d\n", fhp->handle_type); printf("f_handle:"); for (j = 0; j < fhp->handle_bytes; j++) printf("%02x:", fhp->f_handle[j]); printf("\n"); } void fhandle_read(int mount_id, struct file_handle *fhp) { int fd, nread, mnt_fd; char buf[64]; mnt_fd = open("/mnt", O_RDONLY); if (fd == -1) { printf("read pen err\n"); exit(1); } fd = open_by_handle_at(mnt_fd, fhp, O_RDONLY); nread = read(fd, buf, sizeof(buf)); if (nread == -1) errExit("read"); buf[nread] = 0; printf("data :%s\n", buf); } [root@localhost kitamura]# echo abcdefg > /mnt/hoge [root@localhost kitamura]# ./a.out mount :43 size :8 type :1 f_handle:1d:00:00:00:86:75:c0:e3: data :abcdefg [root@localhost kitamura]# cat /proc/self/mountinfo | grep 43 43 22 8:17 / /mnt rw,relatime - ext3 /dev/sdb1 rw,user_xattr,acl,barrier=1,nodelalloc,data=orderedhandle_bytesはf_handleのバイトサイズ(インデックス数)、handle_typeはf_handle情報のタイプで、デフォルトはFILEID_INO32_GEN=1で、32ビット長さで、i_no/i_generationが設定されということです。
struct file_handle { __u32 handle_bytes; int handle_type; unsigned char f_handle[0]; };
補足
fh.handle_bytesが取得するファイルシステムのf_handle領域よりhandle_bytesが小さいとき、EOVERFLOWエラーとなり、そのメッセージがname_to_handle_at: Value too large for defined data typeとなります。fhandlesize_get()でEOVERFLOWはエラーとしていません。