LCOV - code coverage report
Current view: top level - fs - fhandle.c (source / functions) Hit Total Coverage
Test: fstests of 6.5.0-rc3-achx @ Mon Jul 31 20:08:12 PDT 2023 Lines: 98 123 79.7 %
Date: 2023-07-31 20:08:12 Functions: 9 13 69.2 %

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

Generated by: LCOV version 1.14