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