1383 lines · 33.2 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only/* *  linux/fs/open.c * *  Copyright (C) 1991, 1992  Linus Torvalds */ #include <linux/string.h>#include <linux/mm.h>#include <linux/file.h>#include <linux/fdtable.h>#include <linux/fsnotify.h>#include <linux/module.h>#include <linux/tty.h>#include <linux/namei.h>#include <linux/backing-dev.h>#include <linux/capability.h>#include <linux/securebits.h>#include <linux/security.h>#include <linux/mount.h>#include <linux/fcntl.h>#include <linux/slab.h>#include <linux/uaccess.h>#include <linux/fs.h>#include <linux/personality.h>#include <linux/pagemap.h>#include <linux/syscalls.h>#include <linux/rcupdate.h>#include <linux/audit.h>#include <linux/falloc.h>#include <linux/fs_struct.h>#include <linux/ima.h>#include <linux/dnotify.h>#include <linux/compat.h> #include "internal.h" int do_truncate(struct dentry *dentry, loff_t length, unsigned int time_attrs,	struct file *filp){	int ret;	struct iattr newattrs; 	/* Not pretty: "inode->i_size" shouldn't really be signed. But it is. */	if (length < 0)		return -EINVAL; 	newattrs.ia_size = length;	newattrs.ia_valid = ATTR_SIZE | time_attrs;	if (filp) {		newattrs.ia_file = filp;		newattrs.ia_valid |= ATTR_FILE;	} 	/* Remove suid, sgid, and file capabilities on truncate too */	ret = dentry_needs_remove_privs(dentry);	if (ret < 0)		return ret;	if (ret)		newattrs.ia_valid |= ret | ATTR_FORCE; 	inode_lock(dentry->d_inode);	/* Note any delegations or leases have already been broken: */	ret = notify_change(dentry, &newattrs, NULL);	inode_unlock(dentry->d_inode);	return ret;} long vfs_truncate(const struct path *path, loff_t length){	struct inode *inode;	long error; 	inode = path->dentry->d_inode; 	/* For directories it's -EISDIR, for other non-regulars - -EINVAL */	if (S_ISDIR(inode->i_mode))		return -EISDIR;	if (!S_ISREG(inode->i_mode))		return -EINVAL; 	error = mnt_want_write(path->mnt);	if (error)		goto out; 	error = inode_permission(inode, MAY_WRITE);	if (error)		goto mnt_drop_write_and_out; 	error = -EPERM;	if (IS_APPEND(inode))		goto mnt_drop_write_and_out; 	error = get_write_access(inode);	if (error)		goto mnt_drop_write_and_out; 	/*	 * Make sure that there are no leases.  get_write_access() protects	 * against the truncate racing with a lease-granting setlease().	 */	error = break_lease(inode, O_WRONLY);	if (error)		goto put_write_and_out; 	error = locks_verify_truncate(inode, NULL, length);	if (!error)		error = security_path_truncate(path);	if (!error)		error = do_truncate(path->dentry, length, 0, NULL); put_write_and_out:	put_write_access(inode);mnt_drop_write_and_out:	mnt_drop_write(path->mnt);out:	return error;}EXPORT_SYMBOL_GPL(vfs_truncate); long do_sys_truncate(const char __user *pathname, loff_t length){	unsigned int lookup_flags = LOOKUP_FOLLOW;	struct path path;	int error; 	if (length < 0)	/* sorry, but loff_t says... */		return -EINVAL; retry:	error = user_path_at(AT_FDCWD, pathname, lookup_flags, &path);	if (!error) {		error = vfs_truncate(&path, length);		path_put(&path);	}	if (retry_estale(error, lookup_flags)) {		lookup_flags |= LOOKUP_REVAL;		goto retry;	}	return error;} SYSCALL_DEFINE2(truncate, const char __user *, path, long, length){	return do_sys_truncate(path, length);} #ifdef CONFIG_COMPATCOMPAT_SYSCALL_DEFINE2(truncate, const char __user *, path, compat_off_t, length){	return do_sys_truncate(path, length);}#endif long do_sys_ftruncate(unsigned int fd, loff_t length, int small){	struct inode *inode;	struct dentry *dentry;	struct fd f;	int error; 	error = -EINVAL;	if (length < 0)		goto out;	error = -EBADF;	f = fdget(fd);	if (!f.file)		goto out; 	/* explicitly opened as large or we are on 64-bit box */	if (f.file->f_flags & O_LARGEFILE)		small = 0; 	dentry = f.file->f_path.dentry;	inode = dentry->d_inode;	error = -EINVAL;	if (!S_ISREG(inode->i_mode) || !(f.file->f_mode & FMODE_WRITE))		goto out_putf; 	error = -EINVAL;	/* Cannot ftruncate over 2^31 bytes without large file support */	if (small && length > MAX_NON_LFS)		goto out_putf; 	error = -EPERM;	/* Check IS_APPEND on real upper inode */	if (IS_APPEND(file_inode(f.file)))		goto out_putf; 	sb_start_write(inode->i_sb);	error = locks_verify_truncate(inode, f.file, length);	if (!error)		error = security_path_truncate(&f.file->f_path);	if (!error)		error = do_truncate(dentry, length, ATTR_MTIME|ATTR_CTIME, f.file);	sb_end_write(inode->i_sb);out_putf:	fdput(f);out:	return error;} SYSCALL_DEFINE2(ftruncate, unsigned int, fd, unsigned long, length){	return do_sys_ftruncate(fd, length, 1);} #ifdef CONFIG_COMPATCOMPAT_SYSCALL_DEFINE2(ftruncate, unsigned int, fd, compat_ulong_t, length){	return do_sys_ftruncate(fd, length, 1);}#endif /* LFS versions of truncate are only needed on 32 bit machines */#if BITS_PER_LONG == 32SYSCALL_DEFINE2(truncate64, const char __user *, path, loff_t, length){	return do_sys_truncate(path, length);} SYSCALL_DEFINE2(ftruncate64, unsigned int, fd, loff_t, length){	return do_sys_ftruncate(fd, length, 0);}#endif /* BITS_PER_LONG == 32 */  int vfs_fallocate(struct file *file, int mode, loff_t offset, loff_t len){	struct inode *inode = file_inode(file);	long ret; 	if (offset < 0 || len <= 0)		return -EINVAL; 	/* Return error if mode is not supported */	if (mode & ~FALLOC_FL_SUPPORTED_MASK)		return -EOPNOTSUPP; 	/* Punch hole and zero range are mutually exclusive */	if ((mode & (FALLOC_FL_PUNCH_HOLE | FALLOC_FL_ZERO_RANGE)) ==	    (FALLOC_FL_PUNCH_HOLE | FALLOC_FL_ZERO_RANGE))		return -EOPNOTSUPP; 	/* Punch hole must have keep size set */	if ((mode & FALLOC_FL_PUNCH_HOLE) &&	    !(mode & FALLOC_FL_KEEP_SIZE))		return -EOPNOTSUPP; 	/* Collapse range should only be used exclusively. */	if ((mode & FALLOC_FL_COLLAPSE_RANGE) &&	    (mode & ~FALLOC_FL_COLLAPSE_RANGE))		return -EINVAL; 	/* Insert range should only be used exclusively. */	if ((mode & FALLOC_FL_INSERT_RANGE) &&	    (mode & ~FALLOC_FL_INSERT_RANGE))		return -EINVAL; 	/* Unshare range should only be used with allocate mode. */	if ((mode & FALLOC_FL_UNSHARE_RANGE) &&	    (mode & ~(FALLOC_FL_UNSHARE_RANGE | FALLOC_FL_KEEP_SIZE)))		return -EINVAL; 	if (!(file->f_mode & FMODE_WRITE))		return -EBADF; 	/*	 * We can only allow pure fallocate on append only files	 */	if ((mode & ~FALLOC_FL_KEEP_SIZE) && IS_APPEND(inode))		return -EPERM; 	if (IS_IMMUTABLE(inode))		return -EPERM; 	/*	 * We cannot allow any fallocate operation on an active swapfile	 */	if (IS_SWAPFILE(inode))		return -ETXTBSY; 	/*	 * Revalidate the write permissions, in case security policy has	 * changed since the files were opened.	 */	ret = security_file_permission(file, MAY_WRITE);	if (ret)		return ret; 	if (S_ISFIFO(inode->i_mode))		return -ESPIPE; 	if (S_ISDIR(inode->i_mode))		return -EISDIR; 	if (!S_ISREG(inode->i_mode) && !S_ISBLK(inode->i_mode))		return -ENODEV; 	/* Check for wrap through zero too */	if (((offset + len) > inode->i_sb->s_maxbytes) || ((offset + len) < 0))		return -EFBIG; 	if (!file->f_op->fallocate)		return -EOPNOTSUPP; 	file_start_write(file);	ret = file->f_op->fallocate(file, mode, offset, len); 	/*	 * Create inotify and fanotify events.	 *	 * To keep the logic simple always create events if fallocate succeeds.	 * This implies that events are even created if the file size remains	 * unchanged, e.g. when using flag FALLOC_FL_KEEP_SIZE.	 */	if (ret == 0)		fsnotify_modify(file); 	file_end_write(file);	return ret;}EXPORT_SYMBOL_GPL(vfs_fallocate); int ksys_fallocate(int fd, int mode, loff_t offset, loff_t len){	struct fd f = fdget(fd);	int error = -EBADF; 	if (f.file) {		error = vfs_fallocate(f.file, mode, offset, len);		fdput(f);	}	return error;} SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len){	return ksys_fallocate(fd, mode, offset, len);} /* * access() needs to use the real uid/gid, not the effective uid/gid. * We do this by temporarily clearing all FS-related capabilities and * switching the fsuid/fsgid around to the real ones. */static const struct cred *access_override_creds(void){	const struct cred *old_cred;	struct cred *override_cred; 	override_cred = prepare_creds();	if (!override_cred)		return NULL; 	override_cred->fsuid = override_cred->uid;	override_cred->fsgid = override_cred->gid; 	if (!issecure(SECURE_NO_SETUID_FIXUP)) {		/* Clear the capabilities if we switch to a non-root user */		kuid_t root_uid = make_kuid(override_cred->user_ns, 0);		if (!uid_eq(override_cred->uid, root_uid))			cap_clear(override_cred->cap_effective);		else			override_cred->cap_effective =				override_cred->cap_permitted;	} 	/*	 * The new set of credentials can *only* be used in	 * task-synchronous circumstances, and does not need	 * RCU freeing, unless somebody then takes a separate	 * reference to it.	 *	 * NOTE! This is _only_ true because this credential	 * is used purely for override_creds() that installs	 * it as the subjective cred. Other threads will be	 * accessing ->real_cred, not the subjective cred.	 *	 * If somebody _does_ make a copy of this (using the	 * 'get_current_cred()' function), that will clear the	 * non_rcu field, because now that other user may be	 * expecting RCU freeing. But normal thread-synchronous	 * cred accesses will keep things non-RCY.	 */	override_cred->non_rcu = 1; 	old_cred = override_creds(override_cred); 	/* override_cred() gets its own ref */	put_cred(override_cred); 	return old_cred;} static long do_faccessat(int dfd, const char __user *filename, int mode, int flags){	struct path path;	struct inode *inode;	int res;	unsigned int lookup_flags = LOOKUP_FOLLOW;	const struct cred *old_cred = NULL; 	if (mode & ~S_IRWXO)	/* where's F_OK, X_OK, W_OK, R_OK? */		return -EINVAL; 	if (flags & ~(AT_EACCESS | AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH))		return -EINVAL; 	if (flags & AT_SYMLINK_NOFOLLOW)		lookup_flags &= ~LOOKUP_FOLLOW;	if (flags & AT_EMPTY_PATH)		lookup_flags |= LOOKUP_EMPTY; 	if (!(flags & AT_EACCESS)) {		old_cred = access_override_creds();		if (!old_cred)			return -ENOMEM;	} retry:	res = user_path_at(dfd, filename, lookup_flags, &path);	if (res)		goto out; 	inode = d_backing_inode(path.dentry); 	if ((mode & MAY_EXEC) && S_ISREG(inode->i_mode)) {		/*		 * MAY_EXEC on regular files is denied if the fs is mounted		 * with the "noexec" flag.		 */		res = -EACCES;		if (path_noexec(&path))			goto out_path_release;	} 	res = inode_permission(inode, mode | MAY_ACCESS);	/* SuS v2 requires we report a read only fs too */	if (res || !(mode & S_IWOTH) || special_file(inode->i_mode))		goto out_path_release;	/*	 * This is a rare case where using __mnt_is_readonly()	 * is OK without a mnt_want/drop_write() pair.  Since	 * no actual write to the fs is performed here, we do	 * not need to telegraph to that to anyone.	 *	 * By doing this, we accept that this access is	 * inherently racy and know that the fs may change	 * state before we even see this result.	 */	if (__mnt_is_readonly(path.mnt))		res = -EROFS; out_path_release:	path_put(&path);	if (retry_estale(res, lookup_flags)) {		lookup_flags |= LOOKUP_REVAL;		goto retry;	}out:	if (old_cred)		revert_creds(old_cred); 	return res;} SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode){	return do_faccessat(dfd, filename, mode, 0);} SYSCALL_DEFINE4(faccessat2, int, dfd, const char __user *, filename, int, mode,		int, flags){	return do_faccessat(dfd, filename, mode, flags);} SYSCALL_DEFINE2(access, const char __user *, filename, int, mode){	return do_faccessat(AT_FDCWD, filename, mode, 0);} SYSCALL_DEFINE1(chdir, const char __user *, filename){	struct path path;	int error;	unsigned int lookup_flags = LOOKUP_FOLLOW | LOOKUP_DIRECTORY;retry:	error = user_path_at(AT_FDCWD, filename, lookup_flags, &path);	if (error)		goto out; 	error = inode_permission(path.dentry->d_inode, MAY_EXEC | MAY_CHDIR);	if (error)		goto dput_and_out; 	set_fs_pwd(current->fs, &path); 

Lines 1–500 of 1383.