Commit | Line | Data |
---|---|---|
990d6c2d AK |
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 | } |