Commit becfd1f37544798cbdfd788f32c827160fab98c1
Committed by
Al Viro
1 parent
990d6c2d7a
Exists in
master
and in
39 other branches
vfs: Add open by file handle support
[AV: duplicate of open() guts removed; file_open_root() used instead] Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Showing 6 changed files with 181 additions and 0 deletions Side-by-side Diff
fs/compat.c
... | ... | @@ -2284,4 +2284,17 @@ |
2284 | 2284 | } |
2285 | 2285 | |
2286 | 2286 | #endif /* CONFIG_TIMERFD */ |
2287 | + | |
2288 | +#ifdef CONFIG_FHANDLE | |
2289 | +/* | |
2290 | + * Exactly like fs/open.c:sys_open_by_handle_at(), except that it | |
2291 | + * doesn't set the O_LARGEFILE flag. | |
2292 | + */ | |
2293 | +asmlinkage long | |
2294 | +compat_sys_open_by_handle_at(int mountdirfd, | |
2295 | + struct file_handle __user *handle, int flags) | |
2296 | +{ | |
2297 | + return do_handle_open(mountdirfd, handle, flags); | |
2298 | +} | |
2299 | +#endif |
fs/exportfs/expfs.c
... | ... | @@ -374,6 +374,8 @@ |
374 | 374 | /* |
375 | 375 | * Try to get any dentry for the given file handle from the filesystem. |
376 | 376 | */ |
377 | + if (!nop || !nop->fh_to_dentry) | |
378 | + return ERR_PTR(-ESTALE); | |
377 | 379 | result = nop->fh_to_dentry(mnt->mnt_sb, fid, fh_len, fileid_type); |
378 | 380 | if (!result) |
379 | 381 | result = ERR_PTR(-ESTALE); |
fs/fhandle.c
... | ... | @@ -5,6 +5,8 @@ |
5 | 5 | #include <linux/mount.h> |
6 | 6 | #include <linux/namei.h> |
7 | 7 | #include <linux/exportfs.h> |
8 | +#include <linux/fs_struct.h> | |
9 | +#include <linux/fsnotify.h> | |
8 | 10 | #include <asm/uaccess.h> |
9 | 11 | #include "internal.h" |
10 | 12 | |
... | ... | @@ -104,5 +106,161 @@ |
104 | 106 | path_put(&path); |
105 | 107 | } |
106 | 108 | return err; |
109 | +} | |
110 | + | |
111 | +static struct vfsmount *get_vfsmount_from_fd(int fd) | |
112 | +{ | |
113 | + struct path path; | |
114 | + | |
115 | + if (fd == AT_FDCWD) { | |
116 | + struct fs_struct *fs = current->fs; | |
117 | + spin_lock(&fs->lock); | |
118 | + path = fs->pwd; | |
119 | + mntget(path.mnt); | |
120 | + spin_unlock(&fs->lock); | |
121 | + } else { | |
122 | + int fput_needed; | |
123 | + struct file *file = fget_light(fd, &fput_needed); | |
124 | + if (!file) | |
125 | + return ERR_PTR(-EBADF); | |
126 | + path = file->f_path; | |
127 | + mntget(path.mnt); | |
128 | + fput_light(file, fput_needed); | |
129 | + } | |
130 | + return path.mnt; | |
131 | +} | |
132 | + | |
133 | +static int vfs_dentry_acceptable(void *context, struct dentry *dentry) | |
134 | +{ | |
135 | + return 1; | |
136 | +} | |
137 | + | |
138 | +static int do_handle_to_path(int mountdirfd, struct file_handle *handle, | |
139 | + struct path *path) | |
140 | +{ | |
141 | + int retval = 0; | |
142 | + int handle_dwords; | |
143 | + | |
144 | + path->mnt = get_vfsmount_from_fd(mountdirfd); | |
145 | + if (IS_ERR(path->mnt)) { | |
146 | + retval = PTR_ERR(path->mnt); | |
147 | + goto out_err; | |
148 | + } | |
149 | + /* change the handle size to multiple of sizeof(u32) */ | |
150 | + handle_dwords = handle->handle_bytes >> 2; | |
151 | + path->dentry = exportfs_decode_fh(path->mnt, | |
152 | + (struct fid *)handle->f_handle, | |
153 | + handle_dwords, handle->handle_type, | |
154 | + vfs_dentry_acceptable, NULL); | |
155 | + if (IS_ERR(path->dentry)) { | |
156 | + retval = PTR_ERR(path->dentry); | |
157 | + goto out_mnt; | |
158 | + } | |
159 | + return 0; | |
160 | +out_mnt: | |
161 | + mntput(path->mnt); | |
162 | +out_err: | |
163 | + return retval; | |
164 | +} | |
165 | + | |
166 | +static int handle_to_path(int mountdirfd, struct file_handle __user *ufh, | |
167 | + struct path *path) | |
168 | +{ | |
169 | + int retval = 0; | |
170 | + struct file_handle f_handle; | |
171 | + struct file_handle *handle = NULL; | |
172 | + | |
173 | + /* | |
174 | + * With handle we don't look at the execute bit on the | |
175 | + * the directory. Ideally we would like CAP_DAC_SEARCH. | |
176 | + * But we don't have that | |
177 | + */ | |
178 | + if (!capable(CAP_DAC_READ_SEARCH)) { | |
179 | + retval = -EPERM; | |
180 | + goto out_err; | |
181 | + } | |
182 | + if (copy_from_user(&f_handle, ufh, sizeof(struct file_handle))) { | |
183 | + retval = -EFAULT; | |
184 | + goto out_err; | |
185 | + } | |
186 | + if ((f_handle.handle_bytes > MAX_HANDLE_SZ) || | |
187 | + (f_handle.handle_bytes == 0)) { | |
188 | + retval = -EINVAL; | |
189 | + goto out_err; | |
190 | + } | |
191 | + handle = kmalloc(sizeof(struct file_handle) + f_handle.handle_bytes, | |
192 | + GFP_KERNEL); | |
193 | + if (!handle) { | |
194 | + retval = -ENOMEM; | |
195 | + goto out_err; | |
196 | + } | |
197 | + /* copy the full handle */ | |
198 | + if (copy_from_user(handle, ufh, | |
199 | + sizeof(struct file_handle) + | |
200 | + f_handle.handle_bytes)) { | |
201 | + retval = -EFAULT; | |
202 | + goto out_handle; | |
203 | + } | |
204 | + | |
205 | + retval = do_handle_to_path(mountdirfd, handle, path); | |
206 | + | |
207 | +out_handle: | |
208 | + kfree(handle); | |
209 | +out_err: | |
210 | + return retval; | |
211 | +} | |
212 | + | |
213 | +long do_handle_open(int mountdirfd, | |
214 | + struct file_handle __user *ufh, int open_flag) | |
215 | +{ | |
216 | + long retval = 0; | |
217 | + struct path path; | |
218 | + struct file *file; | |
219 | + int fd; | |
220 | + | |
221 | + retval = handle_to_path(mountdirfd, ufh, &path); | |
222 | + if (retval) | |
223 | + return retval; | |
224 | + | |
225 | + fd = get_unused_fd_flags(open_flag); | |
226 | + if (fd < 0) { | |
227 | + path_put(&path); | |
228 | + return fd; | |
229 | + } | |
230 | + file = file_open_root(path.dentry, path.mnt, "", open_flag); | |
231 | + if (IS_ERR(file)) { | |
232 | + put_unused_fd(fd); | |
233 | + retval = PTR_ERR(file); | |
234 | + } else { | |
235 | + retval = fd; | |
236 | + fsnotify_open(file); | |
237 | + fd_install(fd, file); | |
238 | + } | |
239 | + path_put(&path); | |
240 | + return retval; | |
241 | +} | |
242 | + | |
243 | +/** | |
244 | + * sys_open_by_handle_at: Open the file handle | |
245 | + * @mountdirfd: directory file descriptor | |
246 | + * @handle: file handle to be opened | |
247 | + * @flag: open flags. | |
248 | + * | |
249 | + * @mountdirfd indicate the directory file descriptor | |
250 | + * of the mount point. file handle is decoded relative | |
251 | + * to the vfsmount pointed by the @mountdirfd. @flags | |
252 | + * value is same as the open(2) flags. | |
253 | + */ | |
254 | +SYSCALL_DEFINE3(open_by_handle_at, int, mountdirfd, | |
255 | + struct file_handle __user *, handle, | |
256 | + int, flags) | |
257 | +{ | |
258 | + long ret; | |
259 | + | |
260 | + if (force_o_largefile()) | |
261 | + flags |= O_LARGEFILE; | |
262 | + | |
263 | + ret = do_handle_open(mountdirfd, handle, flags); | |
264 | + return ret; | |
107 | 265 | } |
fs/internal.h
... | ... | @@ -117,6 +117,9 @@ |
117 | 117 | extern struct file *do_file_open_root(struct dentry *, struct vfsmount *, |
118 | 118 | const char *, const struct open_flags *, int lookup_flags); |
119 | 119 | |
120 | +extern long do_handle_open(int mountdirfd, | |
121 | + struct file_handle __user *ufh, int open_flag); | |
122 | + | |
120 | 123 | /* |
121 | 124 | * inode.c |
122 | 125 | */ |
include/linux/syscalls.h
... | ... | @@ -836,5 +836,8 @@ |
836 | 836 | asmlinkage long sys_name_to_handle_at(int dfd, const char __user *name, |
837 | 837 | struct file_handle __user *handle, |
838 | 838 | int __user *mnt_id, int flag); |
839 | +asmlinkage long sys_open_by_handle_at(int mountdirfd, | |
840 | + struct file_handle __user *handle, | |
841 | + int flags); | |
839 | 842 | #endif |
kernel/sys_ni.c