]> git.karo-electronics.de Git - karo-tx-linux.git/blob - fs/compat.c
fcntl: move compat syscalls from compat.c
[karo-tx-linux.git] / fs / compat.c
1 /*
2  *  linux/fs/compat.c
3  *
4  *  Kernel compatibililty routines for e.g. 32 bit syscall support
5  *  on 64 bit kernels.
6  *
7  *  Copyright (C) 2002       Stephen Rothwell, IBM Corporation
8  *  Copyright (C) 1997-2000  Jakub Jelinek  (jakub@redhat.com)
9  *  Copyright (C) 1998       Eddie C. Dost  (ecd@skynet.be)
10  *  Copyright (C) 2001,2002  Andi Kleen, SuSE Labs 
11  *  Copyright (C) 2003       Pavel Machek (pavel@ucw.cz)
12  *
13  *  This program is free software; you can redistribute it and/or modify
14  *  it under the terms of the GNU General Public License version 2 as
15  *  published by the Free Software Foundation.
16  */
17
18 #include <linux/stddef.h>
19 #include <linux/kernel.h>
20 #include <linux/linkage.h>
21 #include <linux/compat.h>
22 #include <linux/errno.h>
23 #include <linux/time.h>
24 #include <linux/cred.h>
25 #include <linux/fs.h>
26 #include <linux/fcntl.h>
27 #include <linux/namei.h>
28 #include <linux/file.h>
29 #include <linux/fdtable.h>
30 #include <linux/vfs.h>
31 #include <linux/ioctl.h>
32 #include <linux/init.h>
33 #include <linux/ncp_mount.h>
34 #include <linux/nfs4_mount.h>
35 #include <linux/syscalls.h>
36 #include <linux/ctype.h>
37 #include <linux/dirent.h>
38 #include <linux/fsnotify.h>
39 #include <linux/highuid.h>
40 #include <linux/personality.h>
41 #include <linux/rwsem.h>
42 #include <linux/tsacct_kern.h>
43 #include <linux/security.h>
44 #include <linux/highmem.h>
45 #include <linux/signal.h>
46 #include <linux/mm.h>
47 #include <linux/fs_struct.h>
48 #include <linux/slab.h>
49 #include <linux/pagemap.h>
50 #include <linux/aio.h>
51
52 #include <linux/uaccess.h>
53 #include <asm/mmu_context.h>
54 #include <asm/ioctls.h>
55 #include "internal.h"
56
57 static int cp_compat_stat(struct kstat *stat, struct compat_stat __user *ubuf)
58 {
59         struct compat_stat tmp;
60
61         if (!old_valid_dev(stat->dev) || !old_valid_dev(stat->rdev))
62                 return -EOVERFLOW;
63
64         memset(&tmp, 0, sizeof(tmp));
65         tmp.st_dev = old_encode_dev(stat->dev);
66         tmp.st_ino = stat->ino;
67         if (sizeof(tmp.st_ino) < sizeof(stat->ino) && tmp.st_ino != stat->ino)
68                 return -EOVERFLOW;
69         tmp.st_mode = stat->mode;
70         tmp.st_nlink = stat->nlink;
71         if (tmp.st_nlink != stat->nlink)
72                 return -EOVERFLOW;
73         SET_UID(tmp.st_uid, from_kuid_munged(current_user_ns(), stat->uid));
74         SET_GID(tmp.st_gid, from_kgid_munged(current_user_ns(), stat->gid));
75         tmp.st_rdev = old_encode_dev(stat->rdev);
76         if ((u64) stat->size > MAX_NON_LFS)
77                 return -EOVERFLOW;
78         tmp.st_size = stat->size;
79         tmp.st_atime = stat->atime.tv_sec;
80         tmp.st_atime_nsec = stat->atime.tv_nsec;
81         tmp.st_mtime = stat->mtime.tv_sec;
82         tmp.st_mtime_nsec = stat->mtime.tv_nsec;
83         tmp.st_ctime = stat->ctime.tv_sec;
84         tmp.st_ctime_nsec = stat->ctime.tv_nsec;
85         tmp.st_blocks = stat->blocks;
86         tmp.st_blksize = stat->blksize;
87         return copy_to_user(ubuf, &tmp, sizeof(tmp)) ? -EFAULT : 0;
88 }
89
90 COMPAT_SYSCALL_DEFINE2(newstat, const char __user *, filename,
91                        struct compat_stat __user *, statbuf)
92 {
93         struct kstat stat;
94         int error;
95
96         error = vfs_stat(filename, &stat);
97         if (error)
98                 return error;
99         return cp_compat_stat(&stat, statbuf);
100 }
101
102 COMPAT_SYSCALL_DEFINE2(newlstat, const char __user *, filename,
103                        struct compat_stat __user *, statbuf)
104 {
105         struct kstat stat;
106         int error;
107
108         error = vfs_lstat(filename, &stat);
109         if (error)
110                 return error;
111         return cp_compat_stat(&stat, statbuf);
112 }
113
114 #ifndef __ARCH_WANT_STAT64
115 COMPAT_SYSCALL_DEFINE4(newfstatat, unsigned int, dfd,
116                        const char __user *, filename,
117                        struct compat_stat __user *, statbuf, int, flag)
118 {
119         struct kstat stat;
120         int error;
121
122         error = vfs_fstatat(dfd, filename, &stat, flag);
123         if (error)
124                 return error;
125         return cp_compat_stat(&stat, statbuf);
126 }
127 #endif
128
129 COMPAT_SYSCALL_DEFINE2(newfstat, unsigned int, fd,
130                        struct compat_stat __user *, statbuf)
131 {
132         struct kstat stat;
133         int error = vfs_fstat(fd, &stat);
134
135         if (!error)
136                 error = cp_compat_stat(&stat, statbuf);
137         return error;
138 }
139
140 /* A write operation does a read from user space and vice versa */
141 #define vrfy_dir(type) ((type) == READ ? VERIFY_WRITE : VERIFY_READ)
142
143 ssize_t compat_rw_copy_check_uvector(int type,
144                 const struct compat_iovec __user *uvector, unsigned long nr_segs,
145                 unsigned long fast_segs, struct iovec *fast_pointer,
146                 struct iovec **ret_pointer)
147 {
148         compat_ssize_t tot_len;
149         struct iovec *iov = *ret_pointer = fast_pointer;
150         ssize_t ret = 0;
151         int seg;
152
153         /*
154          * SuS says "The readv() function *may* fail if the iovcnt argument
155          * was less than or equal to 0, or greater than {IOV_MAX}.  Linux has
156          * traditionally returned zero for zero segments, so...
157          */
158         if (nr_segs == 0)
159                 goto out;
160
161         ret = -EINVAL;
162         if (nr_segs > UIO_MAXIOV)
163                 goto out;
164         if (nr_segs > fast_segs) {
165                 ret = -ENOMEM;
166                 iov = kmalloc(nr_segs*sizeof(struct iovec), GFP_KERNEL);
167                 if (iov == NULL)
168                         goto out;
169         }
170         *ret_pointer = iov;
171
172         ret = -EFAULT;
173         if (!access_ok(VERIFY_READ, uvector, nr_segs*sizeof(*uvector)))
174                 goto out;
175
176         /*
177          * Single unix specification:
178          * We should -EINVAL if an element length is not >= 0 and fitting an
179          * ssize_t.
180          *
181          * In Linux, the total length is limited to MAX_RW_COUNT, there is
182          * no overflow possibility.
183          */
184         tot_len = 0;
185         ret = -EINVAL;
186         for (seg = 0; seg < nr_segs; seg++) {
187                 compat_uptr_t buf;
188                 compat_ssize_t len;
189
190                 if (__get_user(len, &uvector->iov_len) ||
191                    __get_user(buf, &uvector->iov_base)) {
192                         ret = -EFAULT;
193                         goto out;
194                 }
195                 if (len < 0)    /* size_t not fitting in compat_ssize_t .. */
196                         goto out;
197                 if (type >= 0 &&
198                     !access_ok(vrfy_dir(type), compat_ptr(buf), len)) {
199                         ret = -EFAULT;
200                         goto out;
201                 }
202                 if (len > MAX_RW_COUNT - tot_len)
203                         len = MAX_RW_COUNT - tot_len;
204                 tot_len += len;
205                 iov->iov_base = compat_ptr(buf);
206                 iov->iov_len = (compat_size_t) len;
207                 uvector++;
208                 iov++;
209         }
210         ret = tot_len;
211
212 out:
213         return ret;
214 }
215
216 struct compat_ncp_mount_data {
217         compat_int_t version;
218         compat_uint_t ncp_fd;
219         __compat_uid_t mounted_uid;
220         compat_pid_t wdog_pid;
221         unsigned char mounted_vol[NCP_VOLNAME_LEN + 1];
222         compat_uint_t time_out;
223         compat_uint_t retry_count;
224         compat_uint_t flags;
225         __compat_uid_t uid;
226         __compat_gid_t gid;
227         compat_mode_t file_mode;
228         compat_mode_t dir_mode;
229 };
230
231 struct compat_ncp_mount_data_v4 {
232         compat_int_t version;
233         compat_ulong_t flags;
234         compat_ulong_t mounted_uid;
235         compat_long_t wdog_pid;
236         compat_uint_t ncp_fd;
237         compat_uint_t time_out;
238         compat_uint_t retry_count;
239         compat_ulong_t uid;
240         compat_ulong_t gid;
241         compat_ulong_t file_mode;
242         compat_ulong_t dir_mode;
243 };
244
245 static void *do_ncp_super_data_conv(void *raw_data)
246 {
247         int version = *(unsigned int *)raw_data;
248
249         if (version == 3) {
250                 struct compat_ncp_mount_data *c_n = raw_data;
251                 struct ncp_mount_data *n = raw_data;
252
253                 n->dir_mode = c_n->dir_mode;
254                 n->file_mode = c_n->file_mode;
255                 n->gid = c_n->gid;
256                 n->uid = c_n->uid;
257                 memmove (n->mounted_vol, c_n->mounted_vol, (sizeof (c_n->mounted_vol) + 3 * sizeof (unsigned int)));
258                 n->wdog_pid = c_n->wdog_pid;
259                 n->mounted_uid = c_n->mounted_uid;
260         } else if (version == 4) {
261                 struct compat_ncp_mount_data_v4 *c_n = raw_data;
262                 struct ncp_mount_data_v4 *n = raw_data;
263
264                 n->dir_mode = c_n->dir_mode;
265                 n->file_mode = c_n->file_mode;
266                 n->gid = c_n->gid;
267                 n->uid = c_n->uid;
268                 n->retry_count = c_n->retry_count;
269                 n->time_out = c_n->time_out;
270                 n->ncp_fd = c_n->ncp_fd;
271                 n->wdog_pid = c_n->wdog_pid;
272                 n->mounted_uid = c_n->mounted_uid;
273                 n->flags = c_n->flags;
274         } else if (version != 5) {
275                 return NULL;
276         }
277
278         return raw_data;
279 }
280
281
282 struct compat_nfs_string {
283         compat_uint_t len;
284         compat_uptr_t data;
285 };
286
287 static inline void compat_nfs_string(struct nfs_string *dst,
288                                      struct compat_nfs_string *src)
289 {
290         dst->data = compat_ptr(src->data);
291         dst->len = src->len;
292 }
293
294 struct compat_nfs4_mount_data_v1 {
295         compat_int_t version;
296         compat_int_t flags;
297         compat_int_t rsize;
298         compat_int_t wsize;
299         compat_int_t timeo;
300         compat_int_t retrans;
301         compat_int_t acregmin;
302         compat_int_t acregmax;
303         compat_int_t acdirmin;
304         compat_int_t acdirmax;
305         struct compat_nfs_string client_addr;
306         struct compat_nfs_string mnt_path;
307         struct compat_nfs_string hostname;
308         compat_uint_t host_addrlen;
309         compat_uptr_t host_addr;
310         compat_int_t proto;
311         compat_int_t auth_flavourlen;
312         compat_uptr_t auth_flavours;
313 };
314
315 static int do_nfs4_super_data_conv(void *raw_data)
316 {
317         int version = *(compat_uint_t *) raw_data;
318
319         if (version == 1) {
320                 struct compat_nfs4_mount_data_v1 *raw = raw_data;
321                 struct nfs4_mount_data *real = raw_data;
322
323                 /* copy the fields backwards */
324                 real->auth_flavours = compat_ptr(raw->auth_flavours);
325                 real->auth_flavourlen = raw->auth_flavourlen;
326                 real->proto = raw->proto;
327                 real->host_addr = compat_ptr(raw->host_addr);
328                 real->host_addrlen = raw->host_addrlen;
329                 compat_nfs_string(&real->hostname, &raw->hostname);
330                 compat_nfs_string(&real->mnt_path, &raw->mnt_path);
331                 compat_nfs_string(&real->client_addr, &raw->client_addr);
332                 real->acdirmax = raw->acdirmax;
333                 real->acdirmin = raw->acdirmin;
334                 real->acregmax = raw->acregmax;
335                 real->acregmin = raw->acregmin;
336                 real->retrans = raw->retrans;
337                 real->timeo = raw->timeo;
338                 real->wsize = raw->wsize;
339                 real->rsize = raw->rsize;
340                 real->flags = raw->flags;
341                 real->version = raw->version;
342         }
343
344         return 0;
345 }
346
347 #define NCPFS_NAME      "ncpfs"
348 #define NFS4_NAME       "nfs4"
349
350 COMPAT_SYSCALL_DEFINE5(mount, const char __user *, dev_name,
351                        const char __user *, dir_name,
352                        const char __user *, type, compat_ulong_t, flags,
353                        const void __user *, data)
354 {
355         char *kernel_type;
356         void *options;
357         char *kernel_dev;
358         int retval;
359
360         kernel_type = copy_mount_string(type);
361         retval = PTR_ERR(kernel_type);
362         if (IS_ERR(kernel_type))
363                 goto out;
364
365         kernel_dev = copy_mount_string(dev_name);
366         retval = PTR_ERR(kernel_dev);
367         if (IS_ERR(kernel_dev))
368                 goto out1;
369
370         options = copy_mount_options(data);
371         retval = PTR_ERR(options);
372         if (IS_ERR(options))
373                 goto out2;
374
375         if (kernel_type && options) {
376                 if (!strcmp(kernel_type, NCPFS_NAME)) {
377                         do_ncp_super_data_conv(options);
378                 } else if (!strcmp(kernel_type, NFS4_NAME)) {
379                         retval = -EINVAL;
380                         if (do_nfs4_super_data_conv(options))
381                                 goto out3;
382                 }
383         }
384
385         retval = do_mount(kernel_dev, dir_name, kernel_type, flags, options);
386
387  out3:
388         kfree(options);
389  out2:
390         kfree(kernel_dev);
391  out1:
392         kfree(kernel_type);
393  out:
394         return retval;
395 }
396
397 /*
398  * Exactly like fs/open.c:sys_open(), except that it doesn't set the
399  * O_LARGEFILE flag.
400  */
401 COMPAT_SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, umode_t, mode)
402 {
403         return do_sys_open(AT_FDCWD, filename, flags, mode);
404 }
405
406 /*
407  * Exactly like fs/open.c:sys_openat(), except that it doesn't set the
408  * O_LARGEFILE flag.
409  */
410 COMPAT_SYSCALL_DEFINE4(openat, int, dfd, const char __user *, filename, int, flags, umode_t, mode)
411 {
412         return do_sys_open(dfd, filename, flags, mode);
413 }
414
415 #ifdef CONFIG_FHANDLE
416 /*
417  * Exactly like fs/open.c:sys_open_by_handle_at(), except that it
418  * doesn't set the O_LARGEFILE flag.
419  */
420 COMPAT_SYSCALL_DEFINE3(open_by_handle_at, int, mountdirfd,
421                              struct file_handle __user *, handle, int, flags)
422 {
423         return do_handle_open(mountdirfd, handle, flags);
424 }
425 #endif