fs/notify/fanotify/fanotify_user.c

Source file repositories/reference/linux-study-clean/fs/notify/fanotify/fanotify_user.c

File Facts

System
Linux kernel
Corpus path
fs/notify/fanotify/fanotify_user.c
Extension
.c
Size
59839 bytes
Lines
2198
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_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags)
{
	struct user_namespace *user_ns = current_user_ns();
	int f_flags, fd;
	unsigned int fid_mode = flags & FANOTIFY_FID_BITS;
	unsigned int class = flags & FANOTIFY_CLASS_BITS;
	unsigned int internal_flags = 0;

	pr_debug("%s: flags=%x event_f_flags=%x\n",
		 __func__, flags, event_f_flags);

	/*
	 * An unprivileged user can setup an fanotify group with limited
	 * functionality - an unprivileged group is limited to notification
	 * events with file handles or mount ids and it cannot use unlimited
	 * queue/marks.
	 */
	if (((flags & FANOTIFY_ADMIN_INIT_FLAGS) ||
	     !(flags & (FANOTIFY_FID_BITS | FAN_REPORT_MNT))) &&
	    !capable(CAP_SYS_ADMIN))
		return -EPERM;

	if (!ns_capable_noaudit(&init_user_ns, CAP_SYS_ADMIN)) {
		/*
		 * Setting the internal flag FANOTIFY_UNPRIV on the group
		 * prevents setting mount/filesystem marks on this group and
		 * prevents reporting pid and open fd in events.
		 */
		internal_flags |= FANOTIFY_UNPRIV;
	}

#ifdef CONFIG_AUDITSYSCALL
	if (flags & ~(FANOTIFY_INIT_FLAGS | FAN_ENABLE_AUDIT))
#else
	if (flags & ~FANOTIFY_INIT_FLAGS)
#endif
		return -EINVAL;

	/* Don't allow mixing mnt events with inode events for now */
	if (flags & FAN_REPORT_MNT) {
		if (class != FAN_CLASS_NOTIF)
			return -EINVAL;
		if (flags & (FANOTIFY_FID_BITS | FAN_REPORT_FD_ERROR))
			return -EINVAL;
	}

	if (event_f_flags & ~FANOTIFY_INIT_ALL_EVENT_F_BITS)
		return -EINVAL;

	switch (event_f_flags & O_ACCMODE) {
	case O_RDONLY:
	case O_RDWR:
	case O_WRONLY:
		break;
	default:
		return -EINVAL;
	}

	if (fid_mode && class != FAN_CLASS_NOTIF)
		return -EINVAL;

	/*
	 * Child name is reported with parent fid so requires dir fid.
	 * We can report both child fid and dir fid with or without name.
	 */
	if ((fid_mode & FAN_REPORT_NAME) && !(fid_mode & FAN_REPORT_DIR_FID))
		return -EINVAL;

	/*
	 * FAN_REPORT_TARGET_FID requires FAN_REPORT_NAME and FAN_REPORT_FID
	 * and is used as an indication to report both dir and child fid on all
	 * dirent events.
	 */
	if ((fid_mode & FAN_REPORT_TARGET_FID) &&
	    (!(fid_mode & FAN_REPORT_NAME) || !(fid_mode & FAN_REPORT_FID)))
		return -EINVAL;

	f_flags = O_RDWR;
	if (flags & FAN_CLOEXEC)
		f_flags |= O_CLOEXEC;
	if (flags & FAN_NONBLOCK)
		f_flags |= O_NONBLOCK;

	CLASS(fsnotify_group, group)(&fanotify_fsnotify_ops,
				     FSNOTIFY_GROUP_USER);
	/* fsnotify_alloc_group takes a ref.  Dropped in fanotify_release */
	if (IS_ERR(group))
		return PTR_ERR(group);

	/* Enforce groups limits per user in all containing user ns */

Annotation

Implementation Notes