X-Git-Url: http://pilppa.org/gitweb/gitweb.cgi?a=blobdiff_plain;f=fs%2Fcompat.c;h=5333c7d7427f6feeee2d72678893cde41294a3f4;hb=8a14342683b1e3adcf5f78660a42fcbd95b44a35;hp=271b75d1597f507bacbd78b7be31227d5492dd2e;hpb=0a3a98f6dd4e8f4d928a09302c0d1c56f2192ac3;p=linux-2.6-omap-h63xx.git diff --git a/fs/compat.c b/fs/compat.c index 271b75d1597..5333c7d7427 100644 --- a/fs/compat.c +++ b/fs/compat.c @@ -53,6 +53,8 @@ #include #include +extern void sigset_from_compat(sigset_t *set, compat_sigset_t *compat); + /* * Not all architectures have sys_utime, so implement this in terms * of sys_utimes. @@ -68,28 +70,33 @@ asmlinkage long compat_sys_utime(char __user *filename, struct compat_utimbuf __ tv[0].tv_usec = 0; tv[1].tv_usec = 0; } - return do_utimes(filename, t ? tv : NULL); + return do_utimes(AT_FDCWD, filename, t ? tv : NULL); } -asmlinkage long compat_sys_utimes(char __user *filename, struct compat_timeval __user *t) +asmlinkage long compat_sys_futimesat(unsigned int dfd, char __user *filename, struct compat_timeval __user *t) { struct timeval tv[2]; - if (t) { + if (t) { if (get_user(tv[0].tv_sec, &t[0].tv_sec) || get_user(tv[0].tv_usec, &t[0].tv_usec) || get_user(tv[1].tv_sec, &t[1].tv_sec) || get_user(tv[1].tv_usec, &t[1].tv_usec)) - return -EFAULT; - } - return do_utimes(filename, t ? tv : NULL); + return -EFAULT; + } + return do_utimes(dfd, filename, t ? tv : NULL); +} + +asmlinkage long compat_sys_utimes(char __user *filename, struct compat_timeval __user *t) +{ + return compat_sys_futimesat(AT_FDCWD, filename, t); } asmlinkage long compat_sys_newstat(char __user * filename, struct compat_stat __user *statbuf) { struct kstat stat; - int error = vfs_stat(filename, &stat); + int error = vfs_stat_fd(AT_FDCWD, filename, &stat); if (!error) error = cp_compat_stat(&stat, statbuf); @@ -100,13 +107,34 @@ asmlinkage long compat_sys_newlstat(char __user * filename, struct compat_stat __user *statbuf) { struct kstat stat; - int error = vfs_lstat(filename, &stat); + int error = vfs_lstat_fd(AT_FDCWD, filename, &stat); if (!error) error = cp_compat_stat(&stat, statbuf); return error; } +asmlinkage long compat_sys_newfstatat(unsigned int dfd, char __user *filename, + struct compat_stat __user *statbuf, int flag) +{ + struct kstat stat; + int error = -EINVAL; + + if ((flag & ~AT_SYMLINK_NOFOLLOW) != 0) + goto out; + + if (flag & AT_SYMLINK_NOFOLLOW) + error = vfs_lstat_fd(dfd, filename, &stat); + else + error = vfs_stat_fd(dfd, filename, &stat); + + if (!error) + error = cp_compat_stat(&stat, statbuf); + +out: + return error; +} + asmlinkage long compat_sys_newfstat(unsigned int fd, struct compat_stat __user * statbuf) { @@ -1290,7 +1318,17 @@ out: asmlinkage long compat_sys_open(const char __user *filename, int flags, int mode) { - return do_sys_open(filename, flags, mode); + return do_sys_open(AT_FDCWD, filename, flags, mode); +} + +/* + * Exactly like fs/open.c:sys_openat(), except that it doesn't set the + * O_LARGEFILE flag. + */ +asmlinkage long +compat_sys_openat(unsigned int dfd, const char __user *filename, int flags, int mode) +{ + return do_sys_open(dfd, filename, flags, mode); } /* @@ -1537,7 +1575,7 @@ out_ret: * Ooo, nasty. We need here to frob 32-bit unsigned longs to * 64-bit unsigned longs. */ -static inline +static int compat_get_fd_set(unsigned long nr, compat_ulong_t __user *ufdset, unsigned long *fdset) { @@ -1570,7 +1608,7 @@ int compat_get_fd_set(unsigned long nr, compat_ulong_t __user *ufdset, return 0; } -static inline +static void compat_set_fd_set(unsigned long nr, compat_ulong_t __user *ufdset, unsigned long *fdset) { @@ -1621,36 +1659,14 @@ static void select_bits_free(void *bits, int size) #define MAX_SELECT_SECONDS \ ((unsigned long) (MAX_SCHEDULE_TIMEOUT / HZ)-1) -asmlinkage long -compat_sys_select(int n, compat_ulong_t __user *inp, compat_ulong_t __user *outp, - compat_ulong_t __user *exp, struct compat_timeval __user *tvp) +int compat_core_sys_select(int n, compat_ulong_t __user *inp, + compat_ulong_t __user *outp, compat_ulong_t __user *exp, s64 *timeout) { fd_set_bits fds; char *bits; - long timeout; int size, max_fdset, ret = -EINVAL; struct fdtable *fdt; - timeout = MAX_SCHEDULE_TIMEOUT; - if (tvp) { - time_t sec, usec; - - if (!access_ok(VERIFY_READ, tvp, sizeof(*tvp)) - || __get_user(sec, &tvp->tv_sec) - || __get_user(usec, &tvp->tv_usec)) { - ret = -EFAULT; - goto out_nofds; - } - - if (sec < 0 || usec < 0) - goto out_nofds; - - if ((unsigned long) sec < MAX_SELECT_SECONDS) { - timeout = ROUND_UP(usec, 1000000/HZ); - timeout += sec * (unsigned long) HZ; - } - } - if (n < 0) goto out_nofds; @@ -1687,19 +1703,7 @@ compat_sys_select(int n, compat_ulong_t __user *inp, compat_ulong_t __user *outp zero_fd_set(n, fds.res_out); zero_fd_set(n, fds.res_ex); - ret = do_select(n, &fds, &timeout); - - if (tvp && !(current->personality & STICKY_TIMEOUTS)) { - time_t sec = 0, usec = 0; - if (timeout) { - sec = timeout / HZ; - usec = timeout % HZ; - usec *= (1000000/HZ); - } - if (put_user(sec, &tvp->tv_sec) || - put_user(usec, &tvp->tv_usec)) - ret = -EFAULT; - } + ret = do_select(n, &fds, timeout); if (ret < 0) goto out; @@ -1720,6 +1724,237 @@ out_nofds: return ret; } +asmlinkage long compat_sys_select(int n, compat_ulong_t __user *inp, + compat_ulong_t __user *outp, compat_ulong_t __user *exp, + struct compat_timeval __user *tvp) +{ + s64 timeout = -1; + struct compat_timeval tv; + int ret; + + if (tvp) { + if (copy_from_user(&tv, tvp, sizeof(tv))) + return -EFAULT; + + if (tv.tv_sec < 0 || tv.tv_usec < 0) + return -EINVAL; + + /* Cast to u64 to make GCC stop complaining */ + if ((u64)tv.tv_sec >= (u64)MAX_INT64_SECONDS) + timeout = -1; /* infinite */ + else { + timeout = ROUND_UP(tv.tv_usec, 1000000/HZ); + timeout += tv.tv_sec * HZ; + } + } + + ret = compat_core_sys_select(n, inp, outp, exp, &timeout); + + if (tvp) { + struct compat_timeval rtv; + + if (current->personality & STICKY_TIMEOUTS) + goto sticky; + rtv.tv_usec = jiffies_to_usecs(do_div((*(u64*)&timeout), HZ)); + rtv.tv_sec = timeout; + if (compat_timeval_compare(&rtv, &tv) >= 0) + rtv = tv; + if (copy_to_user(tvp, &rtv, sizeof(rtv))) { +sticky: + /* + * If an application puts its timeval in read-only + * memory, we don't want the Linux-specific update to + * the timeval to cause a fault after the select has + * completed successfully. However, because we're not + * updating the timeval, we can't restart the system + * call. + */ + if (ret == -ERESTARTNOHAND) + ret = -EINTR; + } + } + + return ret; +} + +#ifdef TIF_RESTORE_SIGMASK +asmlinkage long compat_sys_pselect7(int n, compat_ulong_t __user *inp, + compat_ulong_t __user *outp, compat_ulong_t __user *exp, + struct compat_timespec __user *tsp, compat_sigset_t __user *sigmask, + compat_size_t sigsetsize) +{ + compat_sigset_t ss32; + sigset_t ksigmask, sigsaved; + s64 timeout = MAX_SCHEDULE_TIMEOUT; + struct compat_timespec ts; + int ret; + + if (tsp) { + if (copy_from_user(&ts, tsp, sizeof(ts))) + return -EFAULT; + + if (ts.tv_sec < 0 || ts.tv_nsec < 0) + return -EINVAL; + } + + if (sigmask) { + if (sigsetsize != sizeof(compat_sigset_t)) + return -EINVAL; + if (copy_from_user(&ss32, sigmask, sizeof(ss32))) + return -EFAULT; + sigset_from_compat(&ksigmask, &ss32); + + sigdelsetmask(&ksigmask, sigmask(SIGKILL)|sigmask(SIGSTOP)); + sigprocmask(SIG_SETMASK, &ksigmask, &sigsaved); + } + + do { + if (tsp) { + if ((unsigned long)ts.tv_sec < MAX_SELECT_SECONDS) { + timeout = ROUND_UP(ts.tv_nsec, 1000000000/HZ); + timeout += ts.tv_sec * (unsigned long)HZ; + ts.tv_sec = 0; + ts.tv_nsec = 0; + } else { + ts.tv_sec -= MAX_SELECT_SECONDS; + timeout = MAX_SELECT_SECONDS * HZ; + } + } + + ret = compat_core_sys_select(n, inp, outp, exp, &timeout); + + } while (!ret && !timeout && tsp && (ts.tv_sec || ts.tv_nsec)); + + if (tsp && !(current->personality & STICKY_TIMEOUTS)) { + struct compat_timespec rts; + + rts.tv_sec = timeout / HZ; + rts.tv_nsec = (timeout % HZ) * (NSEC_PER_SEC/HZ); + if (rts.tv_nsec >= NSEC_PER_SEC) { + rts.tv_sec++; + rts.tv_nsec -= NSEC_PER_SEC; + } + if (compat_timespec_compare(&rts, &ts) >= 0) + rts = ts; + copy_to_user(tsp, &rts, sizeof(rts)); + } + + if (ret == -ERESTARTNOHAND) { + /* + * Don't restore the signal mask yet. Let do_signal() deliver + * the signal on the way back to userspace, before the signal + * mask is restored. + */ + if (sigmask) { + memcpy(¤t->saved_sigmask, &sigsaved, + sizeof(sigsaved)); + set_thread_flag(TIF_RESTORE_SIGMASK); + } + } else if (sigmask) + sigprocmask(SIG_SETMASK, &sigsaved, NULL); + + return ret; +} + +asmlinkage long compat_sys_pselect6(int n, compat_ulong_t __user *inp, + compat_ulong_t __user *outp, compat_ulong_t __user *exp, + struct compat_timespec __user *tsp, void __user *sig) +{ + compat_size_t sigsetsize = 0; + compat_uptr_t up = 0; + + if (sig) { + if (!access_ok(VERIFY_READ, sig, + sizeof(compat_uptr_t)+sizeof(compat_size_t)) || + __get_user(up, (compat_uptr_t __user *)sig) || + __get_user(sigsetsize, + (compat_size_t __user *)(sig+sizeof(up)))) + return -EFAULT; + } + return compat_sys_pselect7(n, inp, outp, exp, tsp, compat_ptr(up), + sigsetsize); +} + +asmlinkage long compat_sys_ppoll(struct pollfd __user *ufds, + unsigned int nfds, struct compat_timespec __user *tsp, + const compat_sigset_t __user *sigmask, compat_size_t sigsetsize) +{ + compat_sigset_t ss32; + sigset_t ksigmask, sigsaved; + struct compat_timespec ts; + s64 timeout = -1; + int ret; + + if (tsp) { + if (copy_from_user(&ts, tsp, sizeof(ts))) + return -EFAULT; + + /* We assume that ts.tv_sec is always lower than + the number of seconds that can be expressed in + an s64. Otherwise the compiler bitches at us */ + timeout = ROUND_UP(ts.tv_nsec, 1000000000/HZ); + timeout += ts.tv_sec * HZ; + } + + if (sigmask) { + if (sigsetsize |= sizeof(compat_sigset_t)) + return -EINVAL; + if (copy_from_user(&ss32, sigmask, sizeof(ss32))) + return -EFAULT; + sigset_from_compat(&ksigmask, &ss32); + + sigdelsetmask(&ksigmask, sigmask(SIGKILL)|sigmask(SIGSTOP)); + sigprocmask(SIG_SETMASK, &ksigmask, &sigsaved); + } + + ret = do_sys_poll(ufds, nfds, &timeout); + + /* We can restart this syscall, usually */ + if (ret == -EINTR) { + /* + * Don't restore the signal mask yet. Let do_signal() deliver + * the signal on the way back to userspace, before the signal + * mask is restored. + */ + if (sigmask) { + memcpy(¤t->saved_sigmask, &sigsaved, + sizeof(sigsaved)); + set_thread_flag(TIF_RESTORE_SIGMASK); + } + ret = -ERESTARTNOHAND; + } else if (sigmask) + sigprocmask(SIG_SETMASK, &sigsaved, NULL); + + if (tsp && timeout >= 0) { + struct compat_timespec rts; + + if (current->personality & STICKY_TIMEOUTS) + goto sticky; + /* Yes, we know it's actually an s64, but it's also positive. */ + rts.tv_nsec = jiffies_to_usecs(do_div((*(u64*)&timeout), HZ)) * + 1000; + rts.tv_sec = timeout; + if (compat_timespec_compare(&rts, &ts) >= 0) + rts = ts; + if (copy_to_user(tsp, &rts, sizeof(rts))) { +sticky: + /* + * If an application puts its timeval in read-only + * memory, we don't want the Linux-specific update to + * the timeval to cause a fault after the select has + * completed successfully. However, because we're not + * updating the timeval, we can't restart the system + * call. + */ + if (ret == -ERESTARTNOHAND && timeout >= 0) + ret = -EINTR; + } + } + + return ret; +} +#endif /* TIF_RESTORE_SIGMASK */ + #if defined(CONFIG_NFSD) || defined(CONFIG_NFSD_MODULE) /* Stuff for NFS server syscalls... */ struct compat_nfsctl_svc {