1121 lines · 24.5 KB · C
RawDownload
/* *  linux/fs/open.c * *  Copyright (C) 1991, 1992  Linus Torvalds */ #include <linux/string.h>#include <linux/mm.h>#include <linux/file.h>#include <linux/smp_lock.h>#include <linux/quotaops.h>#include <linux/fsnotify.h>#include <linux/module.h>#include <linux/slab.h>#include <linux/tty.h>#include <linux/namei.h>#include <linux/backing-dev.h>#include <linux/capability.h>#include <linux/security.h>#include <linux/mount.h>#include <linux/vfs.h>#include <linux/fcntl.h>#include <asm/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> int vfs_statfs(struct dentry *dentry, struct kstatfs *buf){	int retval = -ENODEV; 	if (dentry) {		retval = -ENOSYS;		if (dentry->d_sb->s_op->statfs) {			memset(buf, 0, sizeof(*buf));			retval = security_sb_statfs(dentry);			if (retval)				return retval;			retval = dentry->d_sb->s_op->statfs(dentry, buf);			if (retval == 0 && buf->f_frsize == 0)				buf->f_frsize = buf->f_bsize;		}	}	return retval;} EXPORT_SYMBOL(vfs_statfs); static int vfs_statfs_native(struct dentry *dentry, struct statfs *buf){	struct kstatfs st;	int retval; 	retval = vfs_statfs(dentry, &st);	if (retval)		return retval; 	if (sizeof(*buf) == sizeof(st))		memcpy(buf, &st, sizeof(st));	else {		if (sizeof buf->f_blocks == 4) {			if ((st.f_blocks | st.f_bfree | st.f_bavail) &			    0xffffffff00000000ULL)				return -EOVERFLOW;			/*			 * f_files and f_ffree may be -1; it's okay to stuff			 * that into 32 bits			 */			if (st.f_files != -1 &&			    (st.f_files & 0xffffffff00000000ULL))				return -EOVERFLOW;			if (st.f_ffree != -1 &&			    (st.f_ffree & 0xffffffff00000000ULL))				return -EOVERFLOW;		} 		buf->f_type = st.f_type;		buf->f_bsize = st.f_bsize;		buf->f_blocks = st.f_blocks;		buf->f_bfree = st.f_bfree;		buf->f_bavail = st.f_bavail;		buf->f_files = st.f_files;		buf->f_ffree = st.f_ffree;		buf->f_fsid = st.f_fsid;		buf->f_namelen = st.f_namelen;		buf->f_frsize = st.f_frsize;		memset(buf->f_spare, 0, sizeof(buf->f_spare));	}	return 0;} static int vfs_statfs64(struct dentry *dentry, struct statfs64 *buf){	struct kstatfs st;	int retval; 	retval = vfs_statfs(dentry, &st);	if (retval)		return retval; 	if (sizeof(*buf) == sizeof(st))		memcpy(buf, &st, sizeof(st));	else {		buf->f_type = st.f_type;		buf->f_bsize = st.f_bsize;		buf->f_blocks = st.f_blocks;		buf->f_bfree = st.f_bfree;		buf->f_bavail = st.f_bavail;		buf->f_files = st.f_files;		buf->f_ffree = st.f_ffree;		buf->f_fsid = st.f_fsid;		buf->f_namelen = st.f_namelen;		buf->f_frsize = st.f_frsize;		memset(buf->f_spare, 0, sizeof(buf->f_spare));	}	return 0;} asmlinkage long sys_statfs(const char __user * path, struct statfs __user * buf){	struct nameidata nd;	int error; 	error = user_path_walk(path, &nd);	if (!error) {		struct statfs tmp;		error = vfs_statfs_native(nd.dentry, &tmp);		if (!error && copy_to_user(buf, &tmp, sizeof(tmp)))			error = -EFAULT;		path_release(&nd);	}	return error;}  asmlinkage long sys_statfs64(const char __user *path, size_t sz, struct statfs64 __user *buf){	struct nameidata nd;	long error; 	if (sz != sizeof(*buf))		return -EINVAL;	error = user_path_walk(path, &nd);	if (!error) {		struct statfs64 tmp;		error = vfs_statfs64(nd.dentry, &tmp);		if (!error && copy_to_user(buf, &tmp, sizeof(tmp)))			error = -EFAULT;		path_release(&nd);	}	return error;}  asmlinkage long sys_fstatfs(unsigned int fd, struct statfs __user * buf){	struct file * file;	struct statfs tmp;	int error; 	error = -EBADF;	file = fget(fd);	if (!file)		goto out;	error = vfs_statfs_native(file->f_path.dentry, &tmp);	if (!error && copy_to_user(buf, &tmp, sizeof(tmp)))		error = -EFAULT;	fput(file);out:	return error;} asmlinkage long sys_fstatfs64(unsigned int fd, size_t sz, struct statfs64 __user *buf){	struct file * file;	struct statfs64 tmp;	int error; 	if (sz != sizeof(*buf))		return -EINVAL; 	error = -EBADF;	file = fget(fd);	if (!file)		goto out;	error = vfs_statfs64(file->f_path.dentry, &tmp);	if (!error && copy_to_user(buf, &tmp, sizeof(tmp)))		error = -EFAULT;	fput(file);out:	return error;} int do_truncate(struct dentry *dentry, loff_t length, unsigned int time_attrs,	struct file *filp){	int err;	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;	} 	mutex_lock(&dentry->d_inode->i_mutex);	err = notify_change(dentry, &newattrs);	mutex_unlock(&dentry->d_inode->i_mutex);	return err;} static long do_sys_truncate(const char __user * path, loff_t length){	struct nameidata nd;	struct inode * inode;	int error; 	error = -EINVAL;	if (length < 0)	/* sorry, but loff_t says... */		goto out; 	error = user_path_walk(path, &nd);	if (error)		goto out;	inode = nd.dentry->d_inode; 	/* For directories it's -EISDIR, for other non-regulars - -EINVAL */	error = -EISDIR;	if (S_ISDIR(inode->i_mode))		goto dput_and_out; 	error = -EINVAL;	if (!S_ISREG(inode->i_mode))		goto dput_and_out; 	error = vfs_permission(&nd, MAY_WRITE);	if (error)		goto dput_and_out; 	error = -EROFS;	if (IS_RDONLY(inode))		goto dput_and_out; 	error = -EPERM;	if (IS_IMMUTABLE(inode) || IS_APPEND(inode))		goto dput_and_out; 	/*	 * Make sure that there are no leases.	 */	error = break_lease(inode, FMODE_WRITE);	if (error)		goto dput_and_out; 	error = get_write_access(inode);	if (error)		goto dput_and_out; 	error = locks_verify_truncate(inode, NULL, length);	if (!error) {		DQUOT_INIT(inode);		error = do_truncate(nd.dentry, length, 0, NULL);	}	put_write_access(inode); dput_and_out:	path_release(&nd);out:	return error;} asmlinkage long sys_truncate(const char __user * path, unsigned long length){	/* on 32-bit boxen it will cut the range 2^31--2^32-1 off */	return do_sys_truncate(path, (long)length);} static long do_sys_ftruncate(unsigned int fd, loff_t length, int small){	struct inode * inode;	struct dentry *dentry;	struct file * file;	int error; 	error = -EINVAL;	if (length < 0)		goto out;	error = -EBADF;	file = fget(fd);	if (!file)		goto out; 	/* explicitly opened as large or we are on 64-bit box */	if (file->f_flags & O_LARGEFILE)		small = 0; 	dentry = file->f_path.dentry;	inode = dentry->d_inode;	error = -EINVAL;	if (!S_ISREG(inode->i_mode) || !(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;	if (IS_APPEND(inode))		goto out_putf; 	error = locks_verify_truncate(inode, file, length);	if (!error)		error = do_truncate(dentry, length, ATTR_MTIME|ATTR_CTIME, file);out_putf:	fput(file);out:	return error;} asmlinkage long sys_ftruncate(unsigned int fd, unsigned long length){	long ret = do_sys_ftruncate(fd, length, 1);	/* avoid REGPARM breakage on x86: */	prevent_tail_call(ret);	return ret;} /* LFS versions of truncate are only needed on 32 bit machines */#if BITS_PER_LONG == 32asmlinkage long sys_truncate64(const char __user * path, loff_t length){	return do_sys_truncate(path, length);} asmlinkage long sys_ftruncate64(unsigned int fd, loff_t length){	long ret = do_sys_ftruncate(fd, length, 0);	/* avoid REGPARM breakage on x86: */	prevent_tail_call(ret);	return ret;}#endif /* * 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. */asmlinkage long sys_faccessat(int dfd, const char __user *filename, int mode){	struct nameidata nd;	int old_fsuid, old_fsgid;	kernel_cap_t old_cap;	int res; 	if (mode & ~S_IRWXO)	/* where's F_OK, X_OK, W_OK, R_OK? */		return -EINVAL; 	old_fsuid = current->fsuid;	old_fsgid = current->fsgid;	old_cap = current->cap_effective; 	current->fsuid = current->uid;	current->fsgid = current->gid; 	/*	 * Clear the capabilities if we switch to a non-root user	 *	 * FIXME: There is a race here against sys_capset.  The	 * capabilities can change yet we will restore the old	 * value below.  We should hold task_capabilities_lock,	 * but we cannot because user_path_walk can sleep.	 */	if (current->uid)		cap_clear(current->cap_effective);	else		current->cap_effective = current->cap_permitted; 	res = __user_walk_fd(dfd, filename, LOOKUP_FOLLOW|LOOKUP_ACCESS, &nd);	if (res)		goto out; 	res = vfs_permission(&nd, mode);	/* SuS v2 requires we report a read only fs too */	if(res || !(mode & S_IWOTH) ||	   special_file(nd.dentry->d_inode->i_mode))		goto out_path_release; 	if(IS_RDONLY(nd.dentry->d_inode))		res = -EROFS; out_path_release:	path_release(&nd);out:	current->fsuid = old_fsuid;	current->fsgid = old_fsgid;	current->cap_effective = old_cap; 	return res;} asmlinkage long sys_access(const char __user *filename, int mode){	return sys_faccessat(AT_FDCWD, filename, mode);} asmlinkage long sys_chdir(const char __user * filename){	struct nameidata nd;	int error; 	error = __user_walk(filename,			    LOOKUP_FOLLOW|LOOKUP_DIRECTORY|LOOKUP_CHDIR, &nd);	if (error)		goto out; 	error = vfs_permission(&nd, MAY_EXEC);	if (error)		goto dput_and_out; 	set_fs_pwd(current->fs, nd.mnt, nd.dentry); dput_and_out:	path_release(&nd);out:	return error;} asmlinkage long sys_fchdir(unsigned int fd){	struct file *file;	struct dentry *dentry;	struct inode *inode;	struct vfsmount *mnt;	int error; 	error = -EBADF;	file = fget(fd);	if (!file)		goto out; 	dentry = file->f_path.dentry;	mnt = file->f_path.mnt;	inode = dentry->d_inode; 	error = -ENOTDIR;	if (!S_ISDIR(inode->i_mode))		goto out_putf; 	error = file_permission(file, MAY_EXEC);	if (!error)		set_fs_pwd(current->fs, mnt, dentry);out_putf:	fput(file);out:	return error;} asmlinkage long sys_chroot(const char __user * filename){	struct nameidata nd;	int error; 	error = __user_walk(filename, LOOKUP_FOLLOW | LOOKUP_DIRECTORY | LOOKUP_NOALT, &nd);	if (error)		goto out; 	error = vfs_permission(&nd, MAY_EXEC);	if (error)		goto dput_and_out; 	error = -EPERM;	if (!capable(CAP_SYS_CHROOT))		goto dput_and_out; 	set_fs_root(current->fs, nd.mnt, nd.dentry);	set_fs_altroot();	error = 0;dput_and_out:	path_release(&nd);out:	return error;} asmlinkage long sys_fchmod(unsigned int fd, mode_t mode){	struct inode * inode;	struct dentry * dentry;	struct file * file;	int err = -EBADF;	struct iattr newattrs;

Lines 1–500 of 1121.