name_to_handle_atシステムコール
Rev.4を表示中。最新版はこちら。
name_to_handle_atシステムコールは、ファイルのinodeの属性を取得し、取得したinodeでopen_by_handle_atシステムコールにてファイルパスに依存することなく、ファイル参照することです。直接inodeによりファイルをオープンする事でファイルパスによる走査が必要なく、nfsのようにサーバのパスに変更があってもクライアントのdentryキャッシュに反映しない環境下でのファイル参照を可能とします。なお、ファイル名でなくinodeでの参照故、ファイルシステム下での移動等、inode番号に影響しない変更があっても、ファイル参照に影響しません。
検証サンプル
#define _GNU_SOURCE #include <fcntl.h> #include <stdio.h> #include <stdlib.h> enum fid_type { /* * The root, or export point, of the filesystem. * (Never actually passed down to the filesystem. */ FILEID_ROOT = 0, /* * 32bit inode number, 32 bit generation number. */ FILEID_INO32_GEN = 1, /* * 32bit inode number, 32 bit generation number, * 32 bit parent directory inode number. */ FILEID_INO32_GEN_PARENT = 2, /* * 64 bit object ID, 64 bit root object ID, * 32 bit generation number. */ FILEID_BTRFS_WITHOUT_PARENT = 0x4d, /* * 64 bit object ID, 64 bit root object ID, * 32 bit generation number, * 64 bit parent object ID, 32 bit parent generation. */ FILEID_BTRFS_WITH_PARENT = 0x4e, /* * 64 bit object ID, 64 bit root object ID, * 32 bit generation number, * 64 bit parent object ID, 32 bit parent generation, * 64 bit parent root object ID. */ FILEID_BTRFS_WITH_PARENT_ROOT = 0x4f, /* * 32 bit block number, 16 bit partition reference, * 16 bit unused, 32 bit generation number. */ FILEID_UDF_WITHOUT_PARENT = 0x51, /* * 32 bit block number, 16 bit partition reference, * 16 bit unused, 32 bit generation number, * 32 bit parent block number, 32 bit parent generation number */ FILEID_UDF_WITH_PARENT = 0x52, /* * 64 bit checkpoint number, 64 bit inode number, * 32 bit generation number. */ FILEID_NILFS_WITHOUT_PARENT = 0x61, /* * 64 bit checkpoint number, 64 bit inode number, * 32 bit generation number, 32 bit parent generation. * 64 bit parent inode number. */ FILEID_NILFS_WITH_PARENT = 0x62, }; void main() { struct file_handle *fhp; int mount_id; long unsigned int *inode_no; int i; fhp = malloc(sizeof(struct file_handle) + MAX_HANDLE_SZ); if (fhp == NULL){ exit(1); } fhp->handle_bytes = MAX_HANDLE_SZ; if (name_to_handle_at(AT_FDCWD, "./babakaka", fhp, &mount_id, 0) == -1){ exit(1); } printf("%s\n", (fhp->handle_type == FILEID_INO32_GEN)? "INO32_GEN": "OTHER"); inode_no= (long unsigned int *)fhp->f_handle; printf("mountid %10d\n", mount_id); printf("inode no %10u\n", *inode_no++); printf("inode gen %10u\n", *inode_no++); printf("parent inode no %10u\n", *inode_no++); printf("parent inode gen %10u\n", *inode_no); }結果
[root@localhost north]# ls -i babakaka 150750 babakaka [root@localhost north]# ./a.out INO32_GEN mountid 22 inode no 150750 inode gen 2379400899 parent inode no 0 parent inode gen 0順に[マウントID] [親マウントID] [デバイスのメジャー番号:マイナ番号] 本件のファイルシステムは/ / rw,relatime - ext4となります。
[root@localhost north]# ps PID TTY TIME CMD 1254 pts/1 00:00:01 bash 2065 pts/1 00:00:00 ps [root@localhost north]# cat /proc/1254/mountinfo | grep 22 16 22 0:3 / /proc rw,nosuid,nodev,noexec,relatime - proc proc rw 17 22 0:15 / /sys rw,nosuid,nodev,noexec,relatime - sysfs sysfs rw 18 22 0:5 / /dev rw,nosuid,relatime - devtmpfs devtmpfs rw,size=248900k,nr_inodes=62225,mode=755 22 1 253:1 / / rw,relatime - ext4 /dev/mapper/VolGroup-lv_root rw,user_xattr,barrier=1,data=ordered 23 22 0:17 / /run rw,nosuid,nodev,relatime - tmpfs tmpfs rw,mode=755 28 14 0:22 / /sys/fs/cgroup/devices rw,nosuid,nodev,noexec,relatime - cgroup cgroup rw,devices 35 22 0:29 / /media rw,nosuid,nodev,noexec,relatime - tmpfs tmpfs rw,mode=755 42 22 0:33 / /var/lib/nfs/rpc_pipefs rw,relatime - rpc_pipefs sunrpc rw 43 22 8:2 / /boot rw,relatime - ext4 /dev/sda2 rw,user_xattr,acl,barrier=1,data=ordered 44 22 8:17 / /mnt rw,relatime - ext3 /dev/sdb1 rw,user_xattr,acl,barrier=1,nodelalloc,data=ordered 21 22 0:34 / /misc rw,relatime - autofs /etc/auto.misc rw,fd=7,pgrp=888,timeout=300,minproto=5,maxproto=5,indirect 48 22 0:36 / /net rw,relatime - autofs -hosts rw,fd=19,pgrp=888,timeout=300,minproto=5,maxproto=5,indirect
struct fidとしてfhp->f_handle[]にファイルのinode->i_ino/inode->i_generationが設定されます。i_generationはスーパブロックからinode取得毎にインクリメントされ、ファイルが削除され新規作成したファイルが削除したファイルのinode番号となっても、i_generationは異なり、そのようなファイルは参照できないようにするためです。なお、i32/udf下で管理されないファイルシステムは、ファイルシステム依存のnop->encode_fh()がコールされ、独自のファイルシステム属性が__u32 raw[]に設定されるようです。
NFS等でのファイルの削除/移動等、親ディレクトリinodeを更新する必要のある操作で、parent_ino/parent_genが設定されるのではと思います。なお、name_to_handle_atシステムコールではparent_ino/parent_genを設定されません。handle->handle_typeはFILEID_INO32_GENです。
udfは、dvdのようにファイルの変更不可のファイルシステムでinodeで管理するのでなく、ブロック単位で管理されるファイルシステムの属性です。
#define MAX_HANDLE_SZ 128 struct fid { union { struct { u32 ino; u32 gen; u32 parent_ino; u32 parent_gen; } i32; struct { u32 block; u16 partref; u16 parent_partref; u32 generation; u32 parent_block; u32 parent_generation; } udf; __u32 raw[0]; }; }; static long do_sys_name_to_handle(struct path *path, struct file_handle __user *ufh, int __user *mnt_id) { long retval; struct file_handle f_handle; int handle_dwords, handle_bytes; struct file_handle *handle = NULL; if (!path->dentry->d_sb->s_export_op || !path->dentry->d_sb->s_export_op->fh_to_dentry) return -EOPNOTSUPP; if (copy_from_user(&f_handle, ufh, sizeof(struct file_handle))) return -EFAULT; if (f_handle.handle_bytes > MAX_HANDLE_SZ) return -EINVAL; handle = kmalloc(sizeof(struct file_handle) + f_handle.handle_bytes, GFP_KERNEL); if (!handle) return -ENOMEM; handle_dwords = f_handle.handle_bytes >> 2; retval = exportfs_encode_fh(path->dentry, (struct fid *)handle->f_handle, &handle_dwords, 0); handle->handle_type = retval; handle_bytes = handle_dwords * sizeof(u32); handle->handle_bytes = handle_bytes; if ((handle->handle_bytes > f_handle.handle_bytes) || (retval == 255) || (retval == -ENOSPC)) { handle_bytes = 0; retval = -EOVERFLOW; } else retval = 0; if (copy_to_user(mnt_id, &real_mount(path->mnt)->mnt_id, sizeof(*mnt_id)) || copy_to_user(ufh, handle, sizeof(struct file_handle) + handle_bytes)) retval = -EFAULT; kfree(handle); return retval; } int exportfs_encode_fh(struct dentry *dentry, struct fid *fid, int *max_len, int connectable) { const struct export_operations *nop = dentry->d_sb->s_export_op; int error; if (nop->encode_fh) error = nop->encode_fh(dentry, fid->raw, max_len, connectable); else error = export_encode_fh(dentry, fid, max_len, connectable); return error; } static int export_encode_fh(struct dentry *dentry, struct fid *fid, int *max_len, int connectable) { struct inode * inode = dentry->d_inode; int len = *max_len; int type = FILEID_INO32_GEN; if (connectable && (len < 4)) { *max_len = 4; return 255; } else if (len < 2) { *max_len = 2; return 255; } len = 2; fid->i32.ino = inode->i_ino; fid->i32.gen = inode->i_generation; if (connectable && !S_ISDIR(inode->i_mode)) { struct inode *parent; spin_lock(&dentry->d_lock); parent = dentry->d_parent->d_inode; fid->i32.parent_ino = parent->i_ino; fid->i32.parent_gen = parent->i_generation; spin_unlock(&dentry->d_lock); len = 4; type = FILEID_INO32_GEN_PARENT; } *max_len = len; return type; }