name_to_handle_atシステムコール
Rev.1を表示中。最新版はこちら。
dfd/flagsでuser_path_at()でnameのpathを取得し、do_sys_name_to_handle()をコールしてfhandle/mount idを取得します。
SYSCALL_DEFINE5(name_to_handle_at, int, dfd, const char __user *, name,
struct file_handle __user *, handle, int __user *, mnt_id,
int, flag)
{
struct path path;
int lookup_flags;
int err;
if ((flag & ~(AT_SYMLINK_FOLLOW | AT_EMPTY_PATH)) != 0)
return -EINVAL;
lookup_flags = (flag & AT_SYMLINK_FOLLOW) ? LOOKUP_FOLLOW : 0;
if (flag & AT_EMPTY_PATH)
lookup_flags |= LOOKUP_EMPTY;
err = user_path_at(dfd, name, lookup_flags, &path);
if (!err) {
err = do_sys_name_to_handle(&path, handle, mnt_id);
path_put(&path);
}
return err;
}
ファイルのスーパブロックのコールバック関数s_export_op->fh_to_dentryが定義されなければエラーです。この関数はハンドルからdentryを取得するもので、これは実デバイスの読み書きを行うもので、ファイルシステムに直接依存します。(デフォルト処理が実装できない。)この処理がないとhandleを取得しても意味がありません。ufh->handle_bytesに応じたメモリを割り当て、exportfs_encode_fh()でufh->f_handleに(struct fid *)として、ファイル情報を設定します。返り値はhandleのタイプです。
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;
}
ファイルシステムに応じたコールバック関数がコールされます。もし定義されていないとexport_encode_fh()がコールされます。
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;
}
export_encode_fh()は32ビット長のinode番号で管理されるファイルシステムに適応され、ext2/3/4等が該当します。デフォルトでFILEID_INO32_GENでinode_no/inode_generationが設定され、システムコールから呼ばれた場合個のケースに当てはまります。カーネルサポートnfsのhandle取得で、引数のconnectableが0でなく、通常ファイルならその親のディレクトリのinode_no/inode_generationも設定され、FILEID_INO32_GEN_PARENTとなります。ファイル名の変更/削除/移動等の処理を実装するに、親のディレクトリ情報が必要となるからです。
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;
}
ちなみに、handleタイプです。
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,
};
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];
};
};
補足
inode_generationにinodeをアロケートする度に、インクリメントないしget_seconds()でxtimeの秒を設定したりしています。inode_noはファイルシステム下での管理で、ファイルシステムに関係なくinodeをリスト管理するために、inode_generationもキー情報とするために利用されているようです。とにかくこの2つの情報で、確実にファイルが特定できるということです。






