+asmlinkage long sys_select(int n, fd_set __user *inp, fd_set __user *outp,
+ fd_set __user *exp, struct timeval __user *tvp)
+{
+ s64 timeout = -1;
+ struct 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, USEC_PER_SEC/HZ);
+ timeout += tv.tv_sec * HZ;
+ }
+ }
+
+ ret = core_sys_select(n, inp, outp, exp, &timeout);
+
+ if (tvp) {
+ struct 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 (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 sys_pselect7(int n, fd_set __user *inp, fd_set __user *outp,
+ fd_set __user *exp, struct timespec __user *tsp,
+ const sigset_t __user *sigmask, size_t sigsetsize)
+{
+ s64 timeout = MAX_SCHEDULE_TIMEOUT;
+ sigset_t ksigmask, sigsaved;
+ struct 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;
+
+ /* Cast to u64 to make GCC stop complaining */
+ if ((u64)ts.tv_sec >= (u64)MAX_INT64_SECONDS)
+ timeout = -1; /* infinite */
+ else {
+ timeout = ROUND_UP(ts.tv_nsec, NSEC_PER_SEC/HZ);
+ timeout += ts.tv_sec * HZ;
+ }
+ }
+
+ if (sigmask) {
+ /* XXX: Don't preclude handling different sized sigset_t's. */
+ if (sigsetsize != sizeof(sigset_t))
+ return -EINVAL;
+ if (copy_from_user(&ksigmask, sigmask, sizeof(ksigmask)))
+ return -EFAULT;
+
+ sigdelsetmask(&ksigmask, sigmask(SIGKILL)|sigmask(SIGSTOP));
+ sigprocmask(SIG_SETMASK, &ksigmask, &sigsaved);
+ }
+
+ ret = core_sys_select(n, inp, outp, exp, &timeout);
+
+ if (tsp) {
+ struct timespec rts;
+
+ if (current->personality & STICKY_TIMEOUTS)
+ goto sticky;
+ rts.tv_nsec = jiffies_to_usecs(do_div((*(u64*)&timeout), HZ)) *
+ 1000;
+ rts.tv_sec = timeout;
+ if (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)
+ ret = -EINTR;
+ }
+ }
+
+ 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;
+}
+
+/*
+ * Most architectures can't handle 7-argument syscalls. So we provide a
+ * 6-argument version where the sixth argument is a pointer to a structure
+ * which has a pointer to the sigset_t itself followed by a size_t containing
+ * the sigset size.
+ */
+asmlinkage long sys_pselect6(int n, fd_set __user *inp, fd_set __user *outp,
+ fd_set __user *exp, struct timespec __user *tsp, void __user *sig)
+{
+ size_t sigsetsize = 0;
+ sigset_t __user *up = NULL;
+
+ if (sig) {
+ if (!access_ok(VERIFY_READ, sig, sizeof(void *)+sizeof(size_t))
+ || __get_user(up, (sigset_t __user * __user *)sig)
+ || __get_user(sigsetsize,
+ (size_t __user *)(sig+sizeof(void *))))
+ return -EFAULT;
+ }
+
+ return sys_pselect7(n, inp, outp, exp, tsp, up, sigsetsize);
+}
+#endif /* TIF_RESTORE_SIGMASK */
+