Skip to content

Commit becfd1f

Browse files
kvaneeshAl Viro
authored and
Al Viro
committed
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 <[email protected]> Signed-off-by: Al Viro <[email protected]>
1 parent 990d6c2 commit becfd1f

File tree

6 files changed

+181
-0
lines changed

6 files changed

+181
-0
lines changed

fs/compat.c

+13
Original file line numberDiff line numberDiff line change
@@ -2284,3 +2284,16 @@ asmlinkage long compat_sys_timerfd_gettime(int ufd,
22842284
}
22852285

22862286
#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

+2
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,8 @@ struct dentry *exportfs_decode_fh(struct vfsmount *mnt, struct fid *fid,
374374
/*
375375
* Try to get any dentry for the given file handle from the filesystem.
376376
*/
377+
if (!nop || !nop->fh_to_dentry)
378+
return ERR_PTR(-ESTALE);
377379
result = nop->fh_to_dentry(mnt->mnt_sb, fid, fh_len, fileid_type);
378380
if (!result)
379381
result = ERR_PTR(-ESTALE);

fs/fhandle.c

+158
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
#include <linux/mount.h>
66
#include <linux/namei.h>
77
#include <linux/exportfs.h>
8+
#include <linux/fs_struct.h>
9+
#include <linux/fsnotify.h>
810
#include <asm/uaccess.h>
911
#include "internal.h"
1012

@@ -105,3 +107,159 @@ SYSCALL_DEFINE5(name_to_handle_at, int, dfd, const char __user *, name,
105107
}
106108
return err;
107109
}
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+
}

fs/internal.h

+3
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,9 @@ extern struct file *do_filp_open(int dfd, const char *pathname,
117117
extern struct file *do_file_open_root(struct dentry *, struct vfsmount *,
118118
const char *, const struct open_flags *, int lookup_flags);
119119

120+
extern long do_handle_open(int mountdirfd,
121+
struct file_handle __user *ufh, int open_flag);
122+
120123
/*
121124
* inode.c
122125
*/

include/linux/syscalls.h

+3
Original file line numberDiff line numberDiff line change
@@ -836,4 +836,7 @@ asmlinkage long sys_old_mmap(struct mmap_arg_struct __user *arg);
836836
asmlinkage long sys_name_to_handle_at(int dfd, const char __user *name,
837837
struct file_handle __user *handle,
838838
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);
839842
#endif

kernel/sys_ni.c

+2
Original file line numberDiff line numberDiff line change
@@ -189,3 +189,5 @@ cond_syscall(sys_fanotify_mark);
189189

190190
/* open by handle */
191191
cond_syscall(sys_name_to_handle_at);
192+
cond_syscall(sys_open_by_handle_at);
193+
cond_syscall(compat_sys_open_by_handle_at);

0 commit comments

Comments
 (0)