/* * 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. Load more
/* * 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 == 32 asmlinkage 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;