Blame view

fs/fhandle.c 6.48 KB
990d6c2d7   Aneesh Kumar K.V   vfs: Add name to ...
1
2
3
4
5
6
7
  #include <linux/syscalls.h>
  #include <linux/slab.h>
  #include <linux/fs.h>
  #include <linux/file.h>
  #include <linux/mount.h>
  #include <linux/namei.h>
  #include <linux/exportfs.h>
becfd1f37   Aneesh Kumar K.V   vfs: Add open by ...
8
9
  #include <linux/fs_struct.h>
  #include <linux/fsnotify.h>
ed5afeaf4   Jeff Mahoney   fs/fhandle.c: add...
10
  #include <linux/personality.h>
990d6c2d7   Aneesh Kumar K.V   vfs: Add name to ...
11
12
  #include <asm/uaccess.h>
  #include "internal.h"
15169fe78   Al Viro   vfs: mnt_id/mnt_g...
13
  #include "mount.h"
990d6c2d7   Aneesh Kumar K.V   vfs: Add name to ...
14
15
16
17
18
19
20
21
22
23
24
  
  static long do_sys_name_to_handle(struct path *path,
  				  struct file_handle __user *ufh,
  				  int __user *mnt_id)
  {
  	long retval;
  	struct file_handle f_handle;
  	int handle_dwords, handle_bytes;
  	struct file_handle *handle = NULL;
  
  	/*
48fc7f7e7   Adam Buchbinder   Fix misspellings ...
25
  	 * We need to make sure whether the file system
990d6c2d7   Aneesh Kumar K.V   vfs: Add name to ...
26
27
  	 * support decoding of the file handle
  	 */
d8c9584ea   Al Viro   vfs: prefer ->den...
28
29
  	if (!path->dentry->d_sb->s_export_op ||
  	    !path->dentry->d_sb->s_export_op->fh_to_dentry)
990d6c2d7   Aneesh Kumar K.V   vfs: Add name to ...
30
31
32
33
34
35
36
37
38
39
40
41
  		return -EOPNOTSUPP;
  
  	if (copy_from_user(&f_handle, ufh, sizeof(struct file_handle)))
  		return -EFAULT;
  
  	if (f_handle.handle_bytes > MAX_HANDLE_SZ)
  		return -EINVAL;
  
  	handle = kmalloc(sizeof(struct file_handle) + f_handle.handle_bytes,
  			 GFP_KERNEL);
  	if (!handle)
  		return -ENOMEM;
48fc7f7e7   Adam Buchbinder   Fix misspellings ...
42
  	/* convert handle size to multiple of sizeof(u32) */
990d6c2d7   Aneesh Kumar K.V   vfs: Add name to ...
43
44
45
46
47
48
49
50
51
52
53
  	handle_dwords = f_handle.handle_bytes >> 2;
  
  	/* we ask for a non connected handle */
  	retval = exportfs_encode_fh(path->dentry,
  				    (struct fid *)handle->f_handle,
  				    &handle_dwords,  0);
  	handle->handle_type = retval;
  	/* convert handle size to bytes */
  	handle_bytes = handle_dwords * sizeof(u32);
  	handle->handle_bytes = handle_bytes;
  	if ((handle->handle_bytes > f_handle.handle_bytes) ||
216b6cbdc   Namjae Jeon   exportfs: add FIL...
54
  	    (retval == FILEID_INVALID) || (retval == -ENOSPC)) {
990d6c2d7   Aneesh Kumar K.V   vfs: Add name to ...
55
56
57
58
59
60
61
62
63
64
65
66
67
68
  		/* As per old exportfs_encode_fh documentation
  		 * we could return ENOSPC to indicate overflow
  		 * But file system returned 255 always. So handle
  		 * both the values
  		 */
  		/*
  		 * set the handle size to zero so we copy only
  		 * non variable part of the file_handle
  		 */
  		handle_bytes = 0;
  		retval = -EOVERFLOW;
  	} else
  		retval = 0;
  	/* copy the mount id */
15169fe78   Al Viro   vfs: mnt_id/mnt_g...
69
70
  	if (copy_to_user(mnt_id, &real_mount(path->mnt)->mnt_id,
  			 sizeof(*mnt_id)) ||
990d6c2d7   Aneesh Kumar K.V   vfs: Add name to ...
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
  	    copy_to_user(ufh, handle,
  			 sizeof(struct file_handle) + handle_bytes))
  		retval = -EFAULT;
  	kfree(handle);
  	return retval;
  }
  
  /**
   * sys_name_to_handle_at: convert name to handle
   * @dfd: directory relative to which name is interpreted if not absolute
   * @name: name that should be converted to handle.
   * @handle: resulting file handle
   * @mnt_id: mount id of the file system containing the file
   * @flag: flag value to indicate whether to follow symlink or not
   *
   * @handle->handle_size indicate the space available to store the
   * variable part of the file handle in bytes. If there is not
   * enough space, the field is updated to return the minimum
   * value required.
   */
  SYSCALL_DEFINE5(name_to_handle_at, int, dfd, const char __user *, name,
  		struct file_handle __user *, handle, int __user *, mnt_id,
  		int, flag)
  {
  	struct path path;
  	int lookup_flags;
  	int err;
  
  	if ((flag & ~(AT_SYMLINK_FOLLOW | AT_EMPTY_PATH)) != 0)
  		return -EINVAL;
  
  	lookup_flags = (flag & AT_SYMLINK_FOLLOW) ? LOOKUP_FOLLOW : 0;
  	if (flag & AT_EMPTY_PATH)
  		lookup_flags |= LOOKUP_EMPTY;
  	err = user_path_at(dfd, name, lookup_flags, &path);
  	if (!err) {
  		err = do_sys_name_to_handle(&path, handle, mnt_id);
  		path_put(&path);
  	}
  	return err;
  }
becfd1f37   Aneesh Kumar K.V   vfs: Add open by ...
112
113
114
  
  static struct vfsmount *get_vfsmount_from_fd(int fd)
  {
2903ff019   Al Viro   switch simple cas...
115
  	struct vfsmount *mnt;
becfd1f37   Aneesh Kumar K.V   vfs: Add open by ...
116
117
118
119
  
  	if (fd == AT_FDCWD) {
  		struct fs_struct *fs = current->fs;
  		spin_lock(&fs->lock);
2903ff019   Al Viro   switch simple cas...
120
  		mnt = mntget(fs->pwd.mnt);
becfd1f37   Aneesh Kumar K.V   vfs: Add open by ...
121
122
  		spin_unlock(&fs->lock);
  	} else {
2903ff019   Al Viro   switch simple cas...
123
124
  		struct fd f = fdget(fd);
  		if (!f.file)
becfd1f37   Aneesh Kumar K.V   vfs: Add open by ...
125
  			return ERR_PTR(-EBADF);
2903ff019   Al Viro   switch simple cas...
126
127
  		mnt = mntget(f.file->f_path.mnt);
  		fdput(f);
becfd1f37   Aneesh Kumar K.V   vfs: Add open by ...
128
  	}
2903ff019   Al Viro   switch simple cas...
129
  	return mnt;
becfd1f37   Aneesh Kumar K.V   vfs: Add open by ...
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
  }
  
  static int vfs_dentry_acceptable(void *context, struct dentry *dentry)
  {
  	return 1;
  }
  
  static int do_handle_to_path(int mountdirfd, struct file_handle *handle,
  			     struct path *path)
  {
  	int retval = 0;
  	int handle_dwords;
  
  	path->mnt = get_vfsmount_from_fd(mountdirfd);
  	if (IS_ERR(path->mnt)) {
  		retval = PTR_ERR(path->mnt);
  		goto out_err;
  	}
  	/* change the handle size to multiple of sizeof(u32) */
  	handle_dwords = handle->handle_bytes >> 2;
  	path->dentry = exportfs_decode_fh(path->mnt,
  					  (struct fid *)handle->f_handle,
  					  handle_dwords, handle->handle_type,
  					  vfs_dentry_acceptable, NULL);
  	if (IS_ERR(path->dentry)) {
  		retval = PTR_ERR(path->dentry);
  		goto out_mnt;
  	}
  	return 0;
  out_mnt:
  	mntput(path->mnt);
  out_err:
  	return retval;
  }
  
  static int handle_to_path(int mountdirfd, struct file_handle __user *ufh,
  		   struct path *path)
  {
  	int retval = 0;
  	struct file_handle f_handle;
  	struct file_handle *handle = NULL;
  
  	/*
  	 * With handle we don't look at the execute bit on the
  	 * the directory. Ideally we would like CAP_DAC_SEARCH.
  	 * But we don't have that
  	 */
  	if (!capable(CAP_DAC_READ_SEARCH)) {
  		retval = -EPERM;
  		goto out_err;
  	}
  	if (copy_from_user(&f_handle, ufh, sizeof(struct file_handle))) {
  		retval = -EFAULT;
  		goto out_err;
  	}
  	if ((f_handle.handle_bytes > MAX_HANDLE_SZ) ||
  	    (f_handle.handle_bytes == 0)) {
  		retval = -EINVAL;
  		goto out_err;
  	}
  	handle = kmalloc(sizeof(struct file_handle) + f_handle.handle_bytes,
  			 GFP_KERNEL);
  	if (!handle) {
  		retval = -ENOMEM;
  		goto out_err;
  	}
  	/* copy the full handle */
161f873b8   Sasha Levin   vfs: read file_ha...
197
198
199
  	*handle = f_handle;
  	if (copy_from_user(&handle->f_handle,
  			   &ufh->f_handle,
becfd1f37   Aneesh Kumar K.V   vfs: Add open by ...
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
  			   f_handle.handle_bytes)) {
  		retval = -EFAULT;
  		goto out_handle;
  	}
  
  	retval = do_handle_to_path(mountdirfd, handle, path);
  
  out_handle:
  	kfree(handle);
  out_err:
  	return retval;
  }
  
  long do_handle_open(int mountdirfd,
  		    struct file_handle __user *ufh, int open_flag)
  {
  	long retval = 0;
  	struct path path;
  	struct file *file;
  	int fd;
  
  	retval = handle_to_path(mountdirfd, ufh, &path);
  	if (retval)
  		return retval;
  
  	fd = get_unused_fd_flags(open_flag);
  	if (fd < 0) {
  		path_put(&path);
  		return fd;
  	}
378c6520e   Jann Horn   fs/coredump: prev...
230
  	file = file_open_root(path.dentry, path.mnt, "", open_flag, 0);
becfd1f37   Aneesh Kumar K.V   vfs: Add open by ...
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
  	if (IS_ERR(file)) {
  		put_unused_fd(fd);
  		retval =  PTR_ERR(file);
  	} else {
  		retval = fd;
  		fsnotify_open(file);
  		fd_install(fd, file);
  	}
  	path_put(&path);
  	return retval;
  }
  
  /**
   * sys_open_by_handle_at: Open the file handle
   * @mountdirfd: directory file descriptor
   * @handle: file handle to be opened
   * @flag: open flags.
   *
   * @mountdirfd indicate the directory file descriptor
   * of the mount point. file handle is decoded relative
   * to the vfsmount pointed by the @mountdirfd. @flags
   * value is same as the open(2) flags.
   */
  SYSCALL_DEFINE3(open_by_handle_at, int, mountdirfd,
  		struct file_handle __user *, handle,
  		int, flags)
  {
  	long ret;
  
  	if (force_o_largefile())
  		flags |= O_LARGEFILE;
  
  	ret = do_handle_open(mountdirfd, handle, flags);
  	return ret;
  }