+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 */
+