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:44 -0400 |
commit | becfd1f37544798cbdfd788f32c827160fab98c1 (patch) | |
tree | 80353bdc7e59c661e841d2d0029bb308c0ed4da0 | |
parent | 990d6c2d7aee921e3bce22b2d6a750fd552262be (diff) |
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>
-rw-r--r-- | fs/compat.c | 13 | ||||
-rw-r--r-- | fs/exportfs/expfs.c | 2 | ||||
-rw-r--r-- | fs/fhandle.c | 158 | ||||
-rw-r--r-- | fs/internal.h | 3 | ||||
-rw-r--r-- | include/linux/syscalls.h | 3 | ||||
-rw-r--r-- | kernel/sys_ni.c | 2 |
6 files changed, 181 insertions, 0 deletions
diff --git a/fs/compat.c b/fs/compat.c index a071775f3bb3..c6d31a3bab88 100644 --- a/fs/compat.c +++ b/fs/compat.c | |||
@@ -2284,3 +2284,16 @@ asmlinkage long compat_sys_timerfd_gettime(int ufd, | |||
2284 | } | 2284 | } |
2285 | 2285 | ||
2286 | #endif /* CONFIG_TIMERFD */ | 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 | ||
diff --git a/fs/exportfs/expfs.c b/fs/exportfs/expfs.c index cfe55731b6dc..b05acb796135 100644 --- a/fs/exportfs/expfs.c +++ b/fs/exportfs/expfs.c | |||
@@ -374,6 +374,8 @@ struct dentry *exportfs_decode_fh(struct vfsmount *mnt, struct fid *fid, | |||
374 | /* | 374 | /* |
375 | * Try to get any dentry for the given file handle from the filesystem. | 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 | result = nop->fh_to_dentry(mnt->mnt_sb, fid, fh_len, fileid_type); | 379 | result = nop->fh_to_dentry(mnt->mnt_sb, fid, fh_len, fileid_type); |
378 | if (!result) | 380 | if (!result) |
379 | result = ERR_PTR(-ESTALE); | 381 | result = ERR_PTR(-ESTALE); |
diff --git a/fs/fhandle.c b/fs/fhandle.c index 9f79e743a840..bf93ad2bee07 100644 --- a/fs/fhandle.c +++ b/fs/fhandle.c | |||
@@ -5,6 +5,8 @@ | |||
5 | #include <linux/mount.h> | 5 | #include <linux/mount.h> |
6 | #include <linux/namei.h> | 6 | #include <linux/namei.h> |
7 | #include <linux/exportfs.h> | 7 | #include <linux/exportfs.h> |
8 | #include <linux/fs_struct.h> | ||
9 | #include <linux/fsnotify.h> | ||
8 | #include <asm/uaccess.h> | 10 | #include <asm/uaccess.h> |
9 | #include "internal.h" | 11 | #include "internal.h" |
10 | 12 | ||
@@ -105,3 +107,159 @@ SYSCALL_DEFINE5(name_to_handle_at, int, dfd, const char __user *, name, | |||
105 | } | 107 | } |
106 | return err; | 108 | return err; |
107 | } | 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; | ||
265 | } | ||
diff --git a/fs/internal.h b/fs/internal.h index 52abc5287f50..f3d15de44b15 100644 --- a/fs/internal.h +++ b/fs/internal.h | |||
@@ -117,6 +117,9 @@ extern struct file *do_filp_open(int dfd, const char *pathname, | |||
117 | extern struct file *do_file_open_root(struct dentry *, struct vfsmount *, | 117 | extern struct file *do_file_open_root(struct dentry *, struct vfsmount *, |
118 | const char *, const struct open_flags *, int lookup_flags); | 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 | * inode.c | 124 | * inode.c |
122 | */ | 125 | */ |
diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 970112613fb4..2d9b79c0f224 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h | |||
@@ -836,4 +836,7 @@ asmlinkage long sys_old_mmap(struct mmap_arg_struct __user *arg); | |||
836 | asmlinkage long sys_name_to_handle_at(int dfd, const char __user *name, | 836 | asmlinkage long sys_name_to_handle_at(int dfd, const char __user *name, |
837 | struct file_handle __user *handle, | 837 | struct file_handle __user *handle, |
838 | int __user *mnt_id, int flag); | 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 | #endif | 842 | #endif |
diff --git a/kernel/sys_ni.c b/kernel/sys_ni.c index 4e013439ac28..25cc41cd8f33 100644 --- a/kernel/sys_ni.c +++ b/kernel/sys_ni.c | |||
@@ -189,3 +189,5 @@ cond_syscall(sys_fanotify_mark); | |||
189 | 189 | ||
190 | /* open by handle */ | 190 | /* open by handle */ |
191 | cond_syscall(sys_name_to_handle_at); | 191 | cond_syscall(sys_name_to_handle_at); |
192 | cond_syscall(sys_open_by_handle_at); | ||
193 | cond_syscall(compat_sys_open_by_handle_at); | ||