0001
0002 #include <linux/syscalls.h>
0003 #include <linux/slab.h>
0004 #include <linux/fs.h>
0005 #include <linux/file.h>
0006 #include <linux/mount.h>
0007 #include <linux/namei.h>
0008 #include <linux/exportfs.h>
0009 #include <linux/fs_struct.h>
0010 #include <linux/fsnotify.h>
0011 #include <linux/personality.h>
0012 #include <linux/uaccess.h>
0013 #include <linux/compat.h>
0014 #include "internal.h"
0015 #include "mount.h"
0016
0017 static long do_sys_name_to_handle(struct path *path,
0018 struct file_handle __user *ufh,
0019 int __user *mnt_id)
0020 {
0021 long retval;
0022 struct file_handle f_handle;
0023 int handle_dwords, handle_bytes;
0024 struct file_handle *handle = NULL;
0025
0026
0027
0028
0029
0030 if (!path->dentry->d_sb->s_export_op ||
0031 !path->dentry->d_sb->s_export_op->fh_to_dentry)
0032 return -EOPNOTSUPP;
0033
0034 if (copy_from_user(&f_handle, ufh, sizeof(struct file_handle)))
0035 return -EFAULT;
0036
0037 if (f_handle.handle_bytes > MAX_HANDLE_SZ)
0038 return -EINVAL;
0039
0040 handle = kmalloc(sizeof(struct file_handle) + f_handle.handle_bytes,
0041 GFP_KERNEL);
0042 if (!handle)
0043 return -ENOMEM;
0044
0045
0046 handle_dwords = f_handle.handle_bytes >> 2;
0047
0048
0049 retval = exportfs_encode_fh(path->dentry,
0050 (struct fid *)handle->f_handle,
0051 &handle_dwords, 0);
0052 handle->handle_type = retval;
0053
0054 handle_bytes = handle_dwords * sizeof(u32);
0055 handle->handle_bytes = handle_bytes;
0056 if ((handle->handle_bytes > f_handle.handle_bytes) ||
0057 (retval == FILEID_INVALID) || (retval == -ENOSPC)) {
0058
0059
0060
0061
0062
0063
0064
0065
0066
0067 handle_bytes = 0;
0068 retval = -EOVERFLOW;
0069 } else
0070 retval = 0;
0071
0072 if (put_user(real_mount(path->mnt)->mnt_id, mnt_id) ||
0073 copy_to_user(ufh, handle,
0074 sizeof(struct file_handle) + handle_bytes))
0075 retval = -EFAULT;
0076 kfree(handle);
0077 return retval;
0078 }
0079
0080
0081
0082
0083
0084
0085
0086
0087
0088
0089
0090
0091
0092
0093 SYSCALL_DEFINE5(name_to_handle_at, int, dfd, const char __user *, name,
0094 struct file_handle __user *, handle, int __user *, mnt_id,
0095 int, flag)
0096 {
0097 struct path path;
0098 int lookup_flags;
0099 int err;
0100
0101 if ((flag & ~(AT_SYMLINK_FOLLOW | AT_EMPTY_PATH)) != 0)
0102 return -EINVAL;
0103
0104 lookup_flags = (flag & AT_SYMLINK_FOLLOW) ? LOOKUP_FOLLOW : 0;
0105 if (flag & AT_EMPTY_PATH)
0106 lookup_flags |= LOOKUP_EMPTY;
0107 err = user_path_at(dfd, name, lookup_flags, &path);
0108 if (!err) {
0109 err = do_sys_name_to_handle(&path, handle, mnt_id);
0110 path_put(&path);
0111 }
0112 return err;
0113 }
0114
0115 static struct vfsmount *get_vfsmount_from_fd(int fd)
0116 {
0117 struct vfsmount *mnt;
0118
0119 if (fd == AT_FDCWD) {
0120 struct fs_struct *fs = current->fs;
0121 spin_lock(&fs->lock);
0122 mnt = mntget(fs->pwd.mnt);
0123 spin_unlock(&fs->lock);
0124 } else {
0125 struct fd f = fdget(fd);
0126 if (!f.file)
0127 return ERR_PTR(-EBADF);
0128 mnt = mntget(f.file->f_path.mnt);
0129 fdput(f);
0130 }
0131 return mnt;
0132 }
0133
0134 static int vfs_dentry_acceptable(void *context, struct dentry *dentry)
0135 {
0136 return 1;
0137 }
0138
0139 static int do_handle_to_path(int mountdirfd, struct file_handle *handle,
0140 struct path *path)
0141 {
0142 int retval = 0;
0143 int handle_dwords;
0144
0145 path->mnt = get_vfsmount_from_fd(mountdirfd);
0146 if (IS_ERR(path->mnt)) {
0147 retval = PTR_ERR(path->mnt);
0148 goto out_err;
0149 }
0150
0151 handle_dwords = handle->handle_bytes >> 2;
0152 path->dentry = exportfs_decode_fh(path->mnt,
0153 (struct fid *)handle->f_handle,
0154 handle_dwords, handle->handle_type,
0155 vfs_dentry_acceptable, NULL);
0156 if (IS_ERR(path->dentry)) {
0157 retval = PTR_ERR(path->dentry);
0158 goto out_mnt;
0159 }
0160 return 0;
0161 out_mnt:
0162 mntput(path->mnt);
0163 out_err:
0164 return retval;
0165 }
0166
0167 static int handle_to_path(int mountdirfd, struct file_handle __user *ufh,
0168 struct path *path)
0169 {
0170 int retval = 0;
0171 struct file_handle f_handle;
0172 struct file_handle *handle = NULL;
0173
0174
0175
0176
0177
0178
0179 if (!capable(CAP_DAC_READ_SEARCH)) {
0180 retval = -EPERM;
0181 goto out_err;
0182 }
0183 if (copy_from_user(&f_handle, ufh, sizeof(struct file_handle))) {
0184 retval = -EFAULT;
0185 goto out_err;
0186 }
0187 if ((f_handle.handle_bytes > MAX_HANDLE_SZ) ||
0188 (f_handle.handle_bytes == 0)) {
0189 retval = -EINVAL;
0190 goto out_err;
0191 }
0192 handle = kmalloc(sizeof(struct file_handle) + f_handle.handle_bytes,
0193 GFP_KERNEL);
0194 if (!handle) {
0195 retval = -ENOMEM;
0196 goto out_err;
0197 }
0198
0199 *handle = f_handle;
0200 if (copy_from_user(&handle->f_handle,
0201 &ufh->f_handle,
0202 f_handle.handle_bytes)) {
0203 retval = -EFAULT;
0204 goto out_handle;
0205 }
0206
0207 retval = do_handle_to_path(mountdirfd, handle, path);
0208
0209 out_handle:
0210 kfree(handle);
0211 out_err:
0212 return retval;
0213 }
0214
0215 static long do_handle_open(int mountdirfd, struct file_handle __user *ufh,
0216 int open_flag)
0217 {
0218 long retval = 0;
0219 struct path path;
0220 struct file *file;
0221 int fd;
0222
0223 retval = handle_to_path(mountdirfd, ufh, &path);
0224 if (retval)
0225 return retval;
0226
0227 fd = get_unused_fd_flags(open_flag);
0228 if (fd < 0) {
0229 path_put(&path);
0230 return fd;
0231 }
0232 file = file_open_root(&path, "", open_flag, 0);
0233 if (IS_ERR(file)) {
0234 put_unused_fd(fd);
0235 retval = PTR_ERR(file);
0236 } else {
0237 retval = fd;
0238 fsnotify_open(file);
0239 fd_install(fd, file);
0240 }
0241 path_put(&path);
0242 return retval;
0243 }
0244
0245
0246
0247
0248
0249
0250
0251
0252
0253
0254
0255
0256 SYSCALL_DEFINE3(open_by_handle_at, int, mountdirfd,
0257 struct file_handle __user *, handle,
0258 int, flags)
0259 {
0260 long ret;
0261
0262 if (force_o_largefile())
0263 flags |= O_LARGEFILE;
0264
0265 ret = do_handle_open(mountdirfd, handle, flags);
0266 return ret;
0267 }
0268
0269 #ifdef CONFIG_COMPAT
0270
0271
0272
0273
0274 COMPAT_SYSCALL_DEFINE3(open_by_handle_at, int, mountdirfd,
0275 struct file_handle __user *, handle, int, flags)
0276 {
0277 return do_handle_open(mountdirfd, handle, flags);
0278 }
0279 #endif