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