fs/select.c

Source file repositories/reference/linux-study-clean/fs/select.c

File Facts

System
Linux kernel
Corpus path
fs/select.c
Extension
.c
Size
35573 bytes
Lines
1439
Domain
Core OS
Bucket
VFS And Filesystem Core
Inferred role
Core OS: syscall or user/kernel boundary
Status
core implementation candidate

Why This File Exists

Core operating-system implementation surface: boot, tasks, memory, VFS, syscall-facing interfaces, synchronization, credentials, and isolation.

Dependency Surface

Detected Declarations

Annotated Snippet

SYSCALL_DEFINE5(select, int, n, fd_set __user *, inp, fd_set __user *, outp,
		fd_set __user *, exp, struct __kernel_old_timeval __user *, tvp)
{
	return kern_select(n, inp, outp, exp, tvp);
}

static long do_pselect(int n, fd_set __user *inp, fd_set __user *outp,
		       fd_set __user *exp, void __user *tsp,
		       const sigset_t __user *sigmask, size_t sigsetsize,
		       enum poll_time_type type)
{
	struct timespec64 ts, end_time, *to = NULL;
	int ret;

	if (tsp) {
		switch (type) {
		case PT_TIMESPEC:
			if (get_timespec64(&ts, tsp))
				return -EFAULT;
			break;
		case PT_OLD_TIMESPEC:
			if (get_old_timespec32(&ts, tsp))
				return -EFAULT;
			break;
		default:
			BUG();
		}

		to = &end_time;
		if (poll_select_set_timeout(to, ts.tv_sec, ts.tv_nsec))
			return -EINVAL;
	}

	ret = set_user_sigmask(sigmask, sigsetsize);
	if (ret)
		return ret;

	ret = core_sys_select(n, inp, outp, exp, to);
	return poll_select_finish(&end_time, tsp, type, 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.
 */
struct sigset_argpack {
	sigset_t __user *p;
	size_t size;
};

static inline int get_sigset_argpack(struct sigset_argpack *to,
				     struct sigset_argpack __user *from)
{
	// the path is hot enough for overhead of copy_from_user() to matter
	if (from) {
		scoped_user_read_access(from, Efault) {
			unsafe_get_user(to->p, &from->p, Efault);
			unsafe_get_user(to->size, &from->size, Efault);
		}
	}
	return 0;
Efault:
	return -EFAULT;
}

SYSCALL_DEFINE6(pselect6, int, n, fd_set __user *, inp, fd_set __user *, outp,
		fd_set __user *, exp, struct __kernel_timespec __user *, tsp,
		void __user *, sig)
{
	struct sigset_argpack x = {NULL, 0};

	if (get_sigset_argpack(&x, sig))
		return -EFAULT;

	return do_pselect(n, inp, outp, exp, tsp, x.p, x.size, PT_TIMESPEC);
}

#if defined(CONFIG_COMPAT_32BIT_TIME) && !defined(CONFIG_64BIT)

SYSCALL_DEFINE6(pselect6_time32, int, n, fd_set __user *, inp, fd_set __user *, outp,
		fd_set __user *, exp, struct old_timespec32 __user *, tsp,
		void __user *, sig)
{
	struct sigset_argpack x = {NULL, 0};

	if (get_sigset_argpack(&x, sig))
		return -EFAULT;

Annotation

Implementation Notes