Aneesh Kumar K.V | 990d6c2 | 2011-01-29 18:43:26 +0530 | [diff] [blame^] | 1 | #include <linux/syscalls.h> |
| 2 | #include <linux/slab.h> |
| 3 | #include <linux/fs.h> |
| 4 | #include <linux/file.h> |
| 5 | #include <linux/mount.h> |
| 6 | #include <linux/namei.h> |
| 7 | #include <linux/exportfs.h> |
| 8 | #include <asm/uaccess.h> |
| 9 | #include "internal.h" |
| 10 | |
| 11 | static long do_sys_name_to_handle(struct path *path, |
| 12 | struct file_handle __user *ufh, |
| 13 | int __user *mnt_id) |
| 14 | { |
| 15 | long retval; |
| 16 | struct file_handle f_handle; |
| 17 | int handle_dwords, handle_bytes; |
| 18 | struct file_handle *handle = NULL; |
| 19 | |
| 20 | /* |
| 21 | * We need t make sure wether the file system |
| 22 | * support decoding of the file handle |
| 23 | */ |
| 24 | if (!path->mnt->mnt_sb->s_export_op || |
| 25 | !path->mnt->mnt_sb->s_export_op->fh_to_dentry) |
| 26 | return -EOPNOTSUPP; |
| 27 | |
| 28 | if (copy_from_user(&f_handle, ufh, sizeof(struct file_handle))) |
| 29 | return -EFAULT; |
| 30 | |
| 31 | if (f_handle.handle_bytes > MAX_HANDLE_SZ) |
| 32 | return -EINVAL; |
| 33 | |
| 34 | handle = kmalloc(sizeof(struct file_handle) + f_handle.handle_bytes, |
| 35 | GFP_KERNEL); |
| 36 | if (!handle) |
| 37 | return -ENOMEM; |
| 38 | |
| 39 | /* convert handle size to multiple of sizeof(u32) */ |
| 40 | handle_dwords = f_handle.handle_bytes >> 2; |
| 41 | |
| 42 | /* we ask for a non connected handle */ |
| 43 | retval = exportfs_encode_fh(path->dentry, |
| 44 | (struct fid *)handle->f_handle, |
| 45 | &handle_dwords, 0); |
| 46 | handle->handle_type = retval; |
| 47 | /* convert handle size to bytes */ |
| 48 | handle_bytes = handle_dwords * sizeof(u32); |
| 49 | handle->handle_bytes = handle_bytes; |
| 50 | if ((handle->handle_bytes > f_handle.handle_bytes) || |
| 51 | (retval == 255) || (retval == -ENOSPC)) { |
| 52 | /* As per old exportfs_encode_fh documentation |
| 53 | * we could return ENOSPC to indicate overflow |
| 54 | * But file system returned 255 always. So handle |
| 55 | * both the values |
| 56 | */ |
| 57 | /* |
| 58 | * set the handle size to zero so we copy only |
| 59 | * non variable part of the file_handle |
| 60 | */ |
| 61 | handle_bytes = 0; |
| 62 | retval = -EOVERFLOW; |
| 63 | } else |
| 64 | retval = 0; |
| 65 | /* copy the mount id */ |
| 66 | if (copy_to_user(mnt_id, &path->mnt->mnt_id, sizeof(*mnt_id)) || |
| 67 | copy_to_user(ufh, handle, |
| 68 | sizeof(struct file_handle) + handle_bytes)) |
| 69 | retval = -EFAULT; |
| 70 | kfree(handle); |
| 71 | return retval; |
| 72 | } |
| 73 | |
| 74 | /** |
| 75 | * sys_name_to_handle_at: convert name to handle |
| 76 | * @dfd: directory relative to which name is interpreted if not absolute |
| 77 | * @name: name that should be converted to handle. |
| 78 | * @handle: resulting file handle |
| 79 | * @mnt_id: mount id of the file system containing the file |
| 80 | * @flag: flag value to indicate whether to follow symlink or not |
| 81 | * |
| 82 | * @handle->handle_size indicate the space available to store the |
| 83 | * variable part of the file handle in bytes. If there is not |
| 84 | * enough space, the field is updated to return the minimum |
| 85 | * value required. |
| 86 | */ |
| 87 | SYSCALL_DEFINE5(name_to_handle_at, int, dfd, const char __user *, name, |
| 88 | struct file_handle __user *, handle, int __user *, mnt_id, |
| 89 | int, flag) |
| 90 | { |
| 91 | struct path path; |
| 92 | int lookup_flags; |
| 93 | int err; |
| 94 | |
| 95 | if ((flag & ~(AT_SYMLINK_FOLLOW | AT_EMPTY_PATH)) != 0) |
| 96 | return -EINVAL; |
| 97 | |
| 98 | lookup_flags = (flag & AT_SYMLINK_FOLLOW) ? LOOKUP_FOLLOW : 0; |
| 99 | if (flag & AT_EMPTY_PATH) |
| 100 | lookup_flags |= LOOKUP_EMPTY; |
| 101 | err = user_path_at(dfd, name, lookup_flags, &path); |
| 102 | if (!err) { |
| 103 | err = do_sys_name_to_handle(&path, handle, mnt_id); |
| 104 | path_put(&path); |
| 105 | } |
| 106 | return err; |
| 107 | } |