1234 lines · 27.5 KB · C
RawDownload
/* *  linux/fs/open.c * *  Copyright (C) 1991, 1992  Linus Torvalds */ #include <linux/string.h>#include <linux/mm.h>#include <linux/utime.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> #include <asm/unistd.h> int vfs_statfs(struct super_block *sb, struct kstatfs *buf){	int retval = -ENODEV; 	if (sb) {		retval = -ENOSYS;		if (sb->s_op->statfs) {			memset(buf, 0, sizeof(*buf));			retval = security_sb_statfs(sb);			if (retval)				return retval;			retval = sb->s_op->statfs(sb, 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 super_block *sb, struct statfs *buf){	struct kstatfs st;	int retval; 	retval = vfs_statfs(sb, &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 super_block *sb, struct statfs64 *buf){	struct kstatfs st;	int retval; 	retval = vfs_statfs(sb, &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->d_inode->i_sb, &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->d_inode->i_sb, &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_dentry->d_inode->i_sb, &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_dentry->d_inode->i_sb, &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_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, 0, 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 #ifdef __ARCH_WANT_SYS_UTIME /* * sys_utime() can be implemented in user-level using sys_utimes(). * Is this for backwards compatibility?  If so, why not move it * into the appropriate arch directory (for those architectures that * need it). */ /* If times==NULL, set access and modification to current time, * must be owner or have write permission. * Else, update from *times, must be owner or super user. */asmlinkage long sys_utime(char __user * filename, struct utimbuf __user * times){	int error;	struct nameidata nd;	struct inode * inode;	struct iattr newattrs; 	error = user_path_walk(filename, &nd);	if (error)		goto out;	inode = nd.dentry->d_inode; 	error = -EROFS;	if (IS_RDONLY(inode))		goto dput_and_out; 	/* Don't worry, the checks are done in inode_change_ok() */	newattrs.ia_valid = ATTR_CTIME | ATTR_MTIME | ATTR_ATIME;	if (times) {		error = -EPERM;		if (IS_APPEND(inode) || IS_IMMUTABLE(inode))			goto dput_and_out; 		error = get_user(newattrs.ia_atime.tv_sec, &times->actime);		newattrs.ia_atime.tv_nsec = 0;		if (!error)			error = get_user(newattrs.ia_mtime.tv_sec, &times->modtime);		newattrs.ia_mtime.tv_nsec = 0;		if (error)			goto dput_and_out; 		newattrs.ia_valid |= ATTR_ATIME_SET | ATTR_MTIME_SET;	} else {                error = -EACCES;                if (IS_IMMUTABLE(inode))                        goto dput_and_out; 		if (current->fsuid != inode->i_uid &&		    (error = vfs_permission(&nd, MAY_WRITE)) != 0)			goto dput_and_out;	}	mutex_lock(&inode->i_mutex);	error = notify_change(nd.dentry, &newattrs);	mutex_unlock(&inode->i_mutex);dput_and_out:	path_release(&nd);out:	return error;} #endif /* If times==NULL, set access and modification to current time, * must be owner or have write permission. * Else, update from *times, must be owner or super user. */long do_utimes(int dfd, char __user *filename, struct timeval *times){	int error;	struct nameidata nd;	struct inode * inode;	struct iattr newattrs; 	error = __user_walk_fd(dfd, filename, LOOKUP_FOLLOW, &nd); 	if (error)		goto out;	inode = nd.dentry->d_inode; 	error = -EROFS;	if (IS_RDONLY(inode))		goto dput_and_out; 	/* Don't worry, the checks are done in inode_change_ok() */	newattrs.ia_valid = ATTR_CTIME | ATTR_MTIME | ATTR_ATIME;	if (times) {		error = -EPERM;                if (IS_APPEND(inode) || IS_IMMUTABLE(inode))                        goto dput_and_out; 		newattrs.ia_atime.tv_sec = times[0].tv_sec;		newattrs.ia_atime.tv_nsec = times[0].tv_usec * 1000;		newattrs.ia_mtime.tv_sec = times[1].tv_sec;		newattrs.ia_mtime.tv_nsec = times[1].tv_usec * 1000;		newattrs.ia_valid |= ATTR_ATIME_SET | ATTR_MTIME_SET;	} else {		error = -EACCES;                if (IS_IMMUTABLE(inode))                        goto dput_and_out; 		if (current->fsuid != inode->i_uid &&		    (error = vfs_permission(&nd, MAY_WRITE)) != 0)			goto dput_and_out;	}	mutex_lock(&inode->i_mutex);	error = notify_change(nd.dentry, &newattrs);	mutex_unlock(&inode->i_mutex);dput_and_out:	path_release(&nd);out:	return error;} asmlinkage long sys_futimesat(int dfd, char __user *filename, struct timeval __user *utimes){	struct timeval times[2]; 	if (utimes && copy_from_user(&times, utimes, sizeof(times)))		return -EFAULT;	return do_utimes(dfd, filename, utimes ? times : NULL);} asmlinkage long sys_utimes(char __user *filename, struct timeval __user *utimes){	return sys_futimesat(AT_FDCWD, filename, utimes);}  /* * 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;

Lines 1–500 of 1234.