diff options
author | Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com> | 2011-01-29 08:13:26 -0500 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2011-03-15 02:21:37 -0400 |
commit | 990d6c2d7aee921e3bce22b2d6a750fd552262be (patch) | |
tree | af273c4bfbfd0342d39d05d5a017382eb32a7538 /fs/fhandle.c | |
parent | f52e0c11305aa09ed56cad97ffc8f0cdc3d78b5d (diff) |
vfs: Add name to file handle conversion support
The syscall also return mount id which can be used
to lookup file system specific information such as uuid
in /proc/<pid>/mountinfo
Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'fs/fhandle.c')
-rw-r--r-- | fs/fhandle.c | 107 |
1 files changed, 107 insertions, 0 deletions
diff --git a/fs/fhandle.c b/fs/fhandle.c new file mode 100644 index 000000000000..9f79e743a840 --- /dev/null +++ b/fs/fhandle.c | |||
@@ -0,0 +1,107 @@ | |||
1 | #include <linux/syscalls.h> | ||
2 | #include <linux/slab.h> | ||
3 | #include <linux/fs.h> | ||
4 | #include <linux/file.h> | ||
5 | #include <linux/mount.h> | ||
6 | #include <linux/namei.h> | ||
7 | #include <linux/exportfs.h> | ||
8 | #include <asm/uaccess.h> | ||
9 | #include "internal.h" | ||
10 | |||
11 | static long do_sys_name_to_handle(struct path *path, | ||
12 | struct file_handle __user *ufh, | ||
13 | int __user *mnt_id) | ||
14 | { | ||
15 | long retval; | ||
16 | struct file_handle f_handle; | ||
17 | int handle_dwords, handle_bytes; | ||
18 | struct file_handle *handle = NULL; | ||
19 | |||
20 | /* | ||
21 | * We need t make sure wether the file system | ||
22 | * support decoding of the file handle | ||
23 | */ | ||
24 | if (!path->mnt->mnt_sb->s_export_op || | ||
25 | !path->mnt->mnt_sb->s_export_op->fh_to_dentry) | ||
26 | return -EOPNOTSUPP; | ||
27 | |||
28 | if (copy_from_user(&f_handle, ufh, sizeof(struct file_handle))) | ||
29 | return -EFAULT; | ||
30 | |||
31 | if (f_handle.handle_bytes > MAX_HANDLE_SZ) | ||
32 | return -EINVAL; | ||
33 | |||
34 | handle = kmalloc(sizeof(struct file_handle) + f_handle.handle_bytes, | ||
35 | GFP_KERNEL); | ||
36 | if (!handle) | ||
37 | return -ENOMEM; | ||
38 | |||
39 | /* convert handle size to multiple of sizeof(u32) */ | ||
40 | handle_dwords = f_handle.handle_bytes >> 2; | ||
41 | |||
42 | /* we ask for a non connected handle */ | ||
43 | retval = exportfs_encode_fh(path->dentry, | ||
44 | (struct fid *)handle->f_handle, | ||
45 | &handle_dwords, 0); | ||
46 | handle->handle_type = retval; | ||
47 | /* convert handle size to bytes */ | ||
48 | handle_bytes = handle_dwords * sizeof(u32); | ||
49 | handle->handle_bytes = handle_bytes; | ||
50 | if ((handle->handle_bytes > f_handle.handle_bytes) || | ||
51 | (retval == 255) || (retval == -ENOSPC)) { | ||
52 | /* As per old exportfs_encode_fh documentation | ||
53 | * we could return ENOSPC to indicate overflow | ||
54 | * But file system returned 255 always. So handle | ||
55 | * both the values | ||
56 | */ | ||
57 | /* | ||
58 | * set the handle size to zero so we copy only | ||
59 | * non variable part of the file_handle | ||
60 | */ | ||
61 | handle_bytes = 0; | ||
62 | retval = -EOVERFLOW; | ||
63 | } else | ||
64 | retval = 0; | ||
65 | /* copy the mount id */ | ||
66 | if (copy_to_user(mnt_id, &path->mnt->mnt_id, sizeof(*mnt_id)) || | ||
67 | copy_to_user(ufh, handle, | ||
68 | sizeof(struct file_handle) + handle_bytes)) | ||
69 | retval = -EFAULT; | ||
70 | kfree(handle); | ||
71 | return retval; | ||
72 | } | ||
73 | |||
74 | /** | ||
75 | * sys_name_to_handle_at: convert name to handle | ||
76 | * @dfd: directory relative to which name is interpreted if not absolute | ||
77 | * @name: name that should be converted to handle. | ||
78 | * @handle: resulting file handle | ||
79 | * @mnt_id: mount id of the file system containing the file | ||
80 | * @flag: flag value to indicate whether to follow symlink or not | ||
81 | * | ||
82 | * @handle->handle_size indicate the space available to store the | ||
83 | * variable part of the file handle in bytes. If there is not | ||
84 | * enough space, the field is updated to return the minimum | ||
85 | * value required. | ||
86 | */ | ||
87 | SYSCALL_DEFINE5(name_to_handle_at, int, dfd, const char __user *, name, | ||
88 | struct file_handle __user *, handle, int __user *, mnt_id, | ||
89 | int, flag) | ||
90 | { | ||
91 | struct path path; | ||
92 | int lookup_flags; | ||
93 | int err; | ||
94 | |||
95 | if ((flag & ~(AT_SYMLINK_FOLLOW | AT_EMPTY_PATH)) != 0) | ||
96 | return -EINVAL; | ||
97 | |||
98 | lookup_flags = (flag & AT_SYMLINK_FOLLOW) ? LOOKUP_FOLLOW : 0; | ||
99 | if (flag & AT_EMPTY_PATH) | ||
100 | lookup_flags |= LOOKUP_EMPTY; | ||
101 | err = user_path_at(dfd, name, lookup_flags, &path); | ||
102 | if (!err) { | ||
103 | err = do_sys_name_to_handle(&path, handle, mnt_id); | ||
104 | path_put(&path); | ||
105 | } | ||
106 | return err; | ||
107 | } | ||