X-Git-Url: http://ftp.safe.ca/?a=blobdiff_plain;f=fs%2Fopen.c;h=a3a78ceb2a2bf8c52a3b16cb7e5ca5648f710b15;hb=1cf6e7d83bf334cc5916137862c920a97aabc018;hp=ca9981c4a658247bb6e29aaaaad8fa88f4d99dd8;hpb=e63340ae6b6205fef26b40a75673d1c9c0c8bb90;p=safe%2Fjmp%2Flinux-2.6 diff --git a/fs/open.c b/fs/open.c index ca9981c..a3a78ce 100644 --- a/fs/open.c +++ b/fs/open.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -15,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -26,6 +28,7 @@ #include #include #include +#include int vfs_statfs(struct dentry *dentry, struct kstatfs *buf) { @@ -61,7 +64,8 @@ static int vfs_statfs_native(struct dentry *dentry, struct statfs *buf) memcpy(buf, &st, sizeof(st)); else { if (sizeof buf->f_blocks == 4) { - if ((st.f_blocks | st.f_bfree | st.f_bavail) & + if ((st.f_blocks | st.f_bfree | st.f_bavail | + st.f_bsize | st.f_frsize) & 0xffffffff00000000ULL) return -EOVERFLOW; /* @@ -118,43 +122,41 @@ static int vfs_statfs64(struct dentry *dentry, struct statfs64 *buf) return 0; } -asmlinkage long sys_statfs(const char __user * path, struct statfs __user * buf) +SYSCALL_DEFINE2(statfs, const char __user *, pathname, struct statfs __user *, buf) { - struct nameidata nd; + struct path path; int error; - error = user_path_walk(path, &nd); + error = user_path(pathname, &path); if (!error) { struct statfs tmp; - error = vfs_statfs_native(nd.dentry, &tmp); + error = vfs_statfs_native(path.dentry, &tmp); if (!error && copy_to_user(buf, &tmp, sizeof(tmp))) error = -EFAULT; - path_release(&nd); + path_put(&path); } return error; } - -asmlinkage long sys_statfs64(const char __user *path, size_t sz, struct statfs64 __user *buf) +SYSCALL_DEFINE3(statfs64, const char __user *, pathname, size_t, sz, struct statfs64 __user *, buf) { - struct nameidata nd; + struct path path; long error; if (sz != sizeof(*buf)) return -EINVAL; - error = user_path_walk(path, &nd); + error = user_path(pathname, &path); if (!error) { struct statfs64 tmp; - error = vfs_statfs64(nd.dentry, &tmp); + error = vfs_statfs64(path.dentry, &tmp); if (!error && copy_to_user(buf, &tmp, sizeof(tmp))) error = -EFAULT; - path_release(&nd); + path_put(&path); } return error; } - -asmlinkage long sys_fstatfs(unsigned int fd, struct statfs __user * buf) +SYSCALL_DEFINE2(fstatfs, unsigned int, fd, struct statfs __user *, buf) { struct file * file; struct statfs tmp; @@ -172,7 +174,7 @@ out: return error; } -asmlinkage long sys_fstatfs64(unsigned int fd, size_t sz, struct statfs64 __user *buf) +SYSCALL_DEFINE3(fstatfs64, unsigned int, fd, size_t, sz, struct statfs64 __user *, buf) { struct file * file; struct statfs64 tmp; @@ -210,26 +212,29 @@ int do_truncate(struct dentry *dentry, loff_t length, unsigned int time_attrs, newattrs.ia_valid |= ATTR_FILE; } + /* Remove suid/sgid on truncate too */ + newattrs.ia_valid |= should_remove_suid(dentry); + 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) +static long do_sys_truncate(const char __user *pathname, loff_t length) { - struct nameidata nd; - struct inode * inode; + struct path path; + struct inode *inode; int error; error = -EINVAL; if (length < 0) /* sorry, but loff_t says... */ goto out; - error = user_path_walk(path, &nd); + error = user_path(pathname, &path); if (error) goto out; - inode = nd.dentry->d_inode; + inode = path.dentry->d_inode; /* For directories it's -EISDIR, for other non-regulars - -EINVAL */ error = -EISDIR; @@ -240,43 +245,49 @@ static long do_sys_truncate(const char __user * path, loff_t length) if (!S_ISREG(inode->i_mode)) goto dput_and_out; - error = vfs_permission(&nd, MAY_WRITE); + error = mnt_want_write(path.mnt); if (error) goto dput_and_out; - error = -EROFS; - if (IS_RDONLY(inode)) - goto dput_and_out; + error = inode_permission(inode, MAY_WRITE); + if (error) + goto mnt_drop_write_and_out; error = -EPERM; - if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) - goto dput_and_out; + 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. + * Make sure that there are no leases. get_write_access() protects + * against the truncate racing with a lease-granting setlease(). */ error = break_lease(inode, FMODE_WRITE); if (error) - goto dput_and_out; - - error = get_write_access(inode); - if (error) - goto dput_and_out; + goto put_write_and_out; error = locks_verify_truncate(inode, NULL, length); + if (!error) + error = security_path_truncate(&path, length, 0); if (!error) { DQUOT_INIT(inode); - error = do_truncate(nd.dentry, length, 0, NULL); + error = do_truncate(path.dentry, length, 0, NULL); } - put_write_access(inode); +put_write_and_out: + put_write_access(inode); +mnt_drop_write_and_out: + mnt_drop_write(path.mnt); dput_and_out: - path_release(&nd); + path_put(&path); out: return error; } -asmlinkage long sys_truncate(const char __user * path, unsigned long length) +SYSCALL_DEFINE2(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); @@ -318,6 +329,9 @@ static long do_sys_ftruncate(unsigned int fd, loff_t length, int small) error = locks_verify_truncate(inode, file, length); if (!error) + error = security_path_truncate(&file->f_path, length, + ATTR_MTIME|ATTR_CTIME); + if (!error) error = do_truncate(dentry, length, ATTR_MTIME|ATTR_CTIME, file); out_putf: fput(file); @@ -325,28 +339,107 @@ out: return error; } -asmlinkage long sys_ftruncate(unsigned int fd, unsigned long length) +SYSCALL_DEFINE2(ftruncate, unsigned int, fd, unsigned long, length) { long ret = do_sys_ftruncate(fd, length, 1); /* avoid REGPARM breakage on x86: */ - prevent_tail_call(ret); + asmlinkage_protect(2, ret, fd, length); 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) +SYSCALL_DEFINE(truncate64)(const char __user * path, loff_t length) { return do_sys_truncate(path, length); } +#ifdef CONFIG_HAVE_SYSCALL_WRAPPERS +asmlinkage long SyS_truncate64(long path, loff_t length) +{ + return SYSC_truncate64((const char __user *) path, length); +} +SYSCALL_ALIAS(sys_truncate64, SyS_truncate64); +#endif -asmlinkage long sys_ftruncate64(unsigned int fd, loff_t length) +SYSCALL_DEFINE(ftruncate64)(unsigned int fd, loff_t length) { long ret = do_sys_ftruncate(fd, length, 0); /* avoid REGPARM breakage on x86: */ - prevent_tail_call(ret); + asmlinkage_protect(2, ret, fd, length); return ret; } +#ifdef CONFIG_HAVE_SYSCALL_WRAPPERS +asmlinkage long SyS_ftruncate64(long fd, loff_t length) +{ + return SYSC_ftruncate64((unsigned int) fd, length); +} +SYSCALL_ALIAS(sys_ftruncate64, SyS_ftruncate64); +#endif +#endif /* BITS_PER_LONG == 32 */ + +SYSCALL_DEFINE(fallocate)(int fd, int mode, loff_t offset, loff_t len) +{ + struct file *file; + struct inode *inode; + long ret = -EINVAL; + + if (offset < 0 || len <= 0) + goto out; + + /* Return error if mode is not supported */ + ret = -EOPNOTSUPP; + if (mode && !(mode & FALLOC_FL_KEEP_SIZE)) + goto out; + + ret = -EBADF; + file = fget(fd); + if (!file) + goto out; + if (!(file->f_mode & FMODE_WRITE)) + goto out_fput; + /* + * Revalidate the write permissions, in case security policy has + * changed since the files were opened. + */ + ret = security_file_permission(file, MAY_WRITE); + if (ret) + goto out_fput; + + inode = file->f_path.dentry->d_inode; + + ret = -ESPIPE; + if (S_ISFIFO(inode->i_mode)) + goto out_fput; + + ret = -ENODEV; + /* + * Let individual file system decide if it supports preallocation + * for directories or not. + */ + if (!S_ISREG(inode->i_mode) && !S_ISDIR(inode->i_mode)) + goto out_fput; + + ret = -EFBIG; + /* Check for wrap through zero too */ + if (((offset + len) > inode->i_sb->s_maxbytes) || ((offset + len) < 0)) + goto out_fput; + + if (inode->i_op->fallocate) + ret = inode->i_op->fallocate(inode, mode, offset, len); + else + ret = -EOPNOTSUPP; + +out_fput: + fput(file); +out: + return ret; +} +#ifdef CONFIG_HAVE_SYSCALL_WRAPPERS +asmlinkage long SyS_fallocate(long fd, long mode, loff_t offset, loff_t len) +{ + return SYSC_fallocate((int)fd, (int)mode, offset, len); +} +SYSCALL_ALIAS(sys_fallocate, SyS_fallocate); #endif /* @@ -354,92 +447,106 @@ asmlinkage long sys_ftruncate64(unsigned int fd, loff_t length) * 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) +SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode) { - struct nameidata nd; - int old_fsuid, old_fsgid; - kernel_cap_t old_cap; + const struct cred *old_cred; + struct cred *override_cred; + struct path path; + struct inode *inode; 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; + override_cred = prepare_creds(); + if (!override_cred) + return -ENOMEM; - current->fsuid = current->uid; - current->fsgid = current->gid; + override_cred->fsuid = override_cred->uid; + override_cred->fsgid = override_cred->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; + if (!issecure(SECURE_NO_SETUID_FIXUP)) { + /* Clear the capabilities if we switch to a non-root user */ + if (override_cred->uid) + cap_clear(override_cred->cap_effective); + else + override_cred->cap_effective = + override_cred->cap_permitted; + } - res = __user_walk_fd(dfd, filename, LOOKUP_FOLLOW|LOOKUP_ACCESS, &nd); + old_cred = override_creds(override_cred); + + res = user_path_at(dfd, filename, LOOKUP_FOLLOW, &path); if (res) goto out; - res = vfs_permission(&nd, mode); + inode = path.dentry->d_inode; + + 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.mnt->mnt_flags & MNT_NOEXEC) + 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(nd.dentry->d_inode->i_mode)) + if (res || !(mode & S_IWOTH) || special_file(inode->i_mode)) goto out_path_release; - - if(IS_RDONLY(nd.dentry->d_inode)) + /* + * 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_release(&nd); + path_put(&path); out: - current->fsuid = old_fsuid; - current->fsgid = old_fsgid; - current->cap_effective = old_cap; - + revert_creds(old_cred); + put_cred(override_cred); return res; } -asmlinkage long sys_access(const char __user *filename, int mode) +SYSCALL_DEFINE2(access, const char __user *, filename, int, mode) { return sys_faccessat(AT_FDCWD, filename, mode); } -asmlinkage long sys_chdir(const char __user * filename) +SYSCALL_DEFINE1(chdir, const char __user *, filename) { - struct nameidata nd; + struct path path; int error; - error = __user_walk(filename, - LOOKUP_FOLLOW|LOOKUP_DIRECTORY|LOOKUP_CHDIR, &nd); + error = user_path_dir(filename, &path); if (error) goto out; - error = vfs_permission(&nd, MAY_EXEC); + error = inode_permission(path.dentry->d_inode, MAY_EXEC | MAY_ACCESS); if (error) goto dput_and_out; - set_fs_pwd(current->fs, nd.mnt, nd.dentry); + set_fs_pwd(current->fs, &path); dput_and_out: - path_release(&nd); + path_put(&path); out: return error; } -asmlinkage long sys_fchdir(unsigned int fd) +SYSCALL_DEFINE1(fchdir, unsigned int, fd) { struct file *file; - struct dentry *dentry; struct inode *inode; - struct vfsmount *mnt; int error; error = -EBADF; @@ -447,33 +554,31 @@ asmlinkage long sys_fchdir(unsigned int fd) if (!file) goto out; - dentry = file->f_path.dentry; - mnt = file->f_path.mnt; - inode = dentry->d_inode; + inode = file->f_path.dentry->d_inode; error = -ENOTDIR; if (!S_ISDIR(inode->i_mode)) goto out_putf; - error = file_permission(file, MAY_EXEC); + error = inode_permission(inode, MAY_EXEC | MAY_ACCESS); if (!error) - set_fs_pwd(current->fs, mnt, dentry); + set_fs_pwd(current->fs, &file->f_path); out_putf: fput(file); out: return error; } -asmlinkage long sys_chroot(const char __user * filename) +SYSCALL_DEFINE1(chroot, const char __user *, filename) { - struct nameidata nd; + struct path path; int error; - error = __user_walk(filename, LOOKUP_FOLLOW | LOOKUP_DIRECTORY | LOOKUP_NOALT, &nd); + error = user_path_dir(filename, &path); if (error) goto out; - error = vfs_permission(&nd, MAY_EXEC); + error = inode_permission(path.dentry->d_inode, MAY_EXEC | MAY_ACCESS); if (error) goto dput_and_out; @@ -481,16 +586,15 @@ asmlinkage long sys_chroot(const char __user * filename) if (!capable(CAP_SYS_CHROOT)) goto dput_and_out; - set_fs_root(current->fs, nd.mnt, nd.dentry); - set_fs_altroot(); + set_fs_root(current->fs, &path); error = 0; dput_and_out: - path_release(&nd); + path_put(&path); out: return error; } -asmlinkage long sys_fchmod(unsigned int fd, mode_t mode) +SYSCALL_DEFINE2(fchmod, unsigned int, fd, mode_t, mode) { struct inode * inode; struct dentry * dentry; @@ -505,13 +609,10 @@ asmlinkage long sys_fchmod(unsigned int fd, mode_t mode) dentry = file->f_path.dentry; inode = dentry->d_inode; - audit_inode(NULL, inode); + audit_inode(NULL, dentry); - err = -EROFS; - if (IS_RDONLY(inode)) - goto out_putf; - err = -EPERM; - if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) + err = mnt_want_write(file->f_path.mnt); + if (err) goto out_putf; mutex_lock(&inode->i_mutex); if (mode == (mode_t) -1) @@ -520,70 +621,53 @@ asmlinkage long sys_fchmod(unsigned int fd, mode_t mode) newattrs.ia_valid = ATTR_MODE | ATTR_CTIME; err = notify_change(dentry, &newattrs); mutex_unlock(&inode->i_mutex); - + mnt_drop_write(file->f_path.mnt); out_putf: fput(file); out: return err; } -asmlinkage long sys_fchmodat(int dfd, const char __user *filename, - mode_t mode) +SYSCALL_DEFINE3(fchmodat, int, dfd, const char __user *, filename, mode_t, mode) { - struct nameidata nd; - struct inode * inode; + struct path path; + struct inode *inode; int error; struct iattr newattrs; - error = __user_walk_fd(dfd, filename, LOOKUP_FOLLOW, &nd); + error = user_path_at(dfd, filename, LOOKUP_FOLLOW, &path); if (error) goto out; - inode = nd.dentry->d_inode; + inode = path.dentry->d_inode; - error = -EROFS; - if (IS_RDONLY(inode)) - goto dput_and_out; - - error = -EPERM; - if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) + error = mnt_want_write(path.mnt); + if (error) goto dput_and_out; - mutex_lock(&inode->i_mutex); if (mode == (mode_t) -1) mode = inode->i_mode; newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO); newattrs.ia_valid = ATTR_MODE | ATTR_CTIME; - error = notify_change(nd.dentry, &newattrs); + error = notify_change(path.dentry, &newattrs); mutex_unlock(&inode->i_mutex); - + mnt_drop_write(path.mnt); dput_and_out: - path_release(&nd); + path_put(&path); out: return error; } -asmlinkage long sys_chmod(const char __user *filename, mode_t mode) +SYSCALL_DEFINE2(chmod, const char __user *, filename, mode_t, mode) { return sys_fchmodat(AT_FDCWD, filename, mode); } static int chown_common(struct dentry * dentry, uid_t user, gid_t group) { - struct inode * inode; + struct inode *inode = dentry->d_inode; int error; struct iattr newattrs; - error = -ENOENT; - if (!(inode = dentry->d_inode)) { - printk(KERN_ERR "chown_common: NULL inode\n"); - goto out; - } - error = -EROFS; - if (IS_RDONLY(inode)) - goto out; - error = -EPERM; - if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) - goto out; newattrs.ia_valid = ATTR_CTIME; if (user != (uid_t) -1) { newattrs.ia_valid |= ATTR_UID; @@ -594,32 +678,38 @@ static int chown_common(struct dentry * dentry, uid_t user, gid_t group) newattrs.ia_gid = group; } if (!S_ISDIR(inode->i_mode)) - newattrs.ia_valid |= ATTR_KILL_SUID|ATTR_KILL_SGID; + newattrs.ia_valid |= + ATTR_KILL_SUID | ATTR_KILL_SGID | ATTR_KILL_PRIV; mutex_lock(&inode->i_mutex); error = notify_change(dentry, &newattrs); mutex_unlock(&inode->i_mutex); -out: + return error; } -asmlinkage long sys_chown(const char __user * filename, uid_t user, gid_t group) +SYSCALL_DEFINE3(chown, const char __user *, filename, uid_t, user, gid_t, group) { - struct nameidata nd; + struct path path; int error; - error = user_path_walk(filename, &nd); + error = user_path(filename, &path); if (error) goto out; - error = chown_common(nd.dentry, user, group); - path_release(&nd); + error = mnt_want_write(path.mnt); + if (error) + goto out_release; + error = chown_common(path.dentry, user, group); + mnt_drop_write(path.mnt); +out_release: + path_put(&path); out: return error; } -asmlinkage long sys_fchownat(int dfd, const char __user *filename, uid_t user, - gid_t group, int flag) +SYSCALL_DEFINE5(fchownat, int, dfd, const char __user *, filename, uid_t, user, + gid_t, group, int, flag) { - struct nameidata nd; + struct path path; int error = -EINVAL; int follow; @@ -627,31 +717,40 @@ asmlinkage long sys_fchownat(int dfd, const char __user *filename, uid_t user, goto out; follow = (flag & AT_SYMLINK_NOFOLLOW) ? 0 : LOOKUP_FOLLOW; - error = __user_walk_fd(dfd, filename, follow, &nd); + error = user_path_at(dfd, filename, follow, &path); if (error) goto out; - error = chown_common(nd.dentry, user, group); - path_release(&nd); + error = mnt_want_write(path.mnt); + if (error) + goto out_release; + error = chown_common(path.dentry, user, group); + mnt_drop_write(path.mnt); +out_release: + path_put(&path); out: return error; } -asmlinkage long sys_lchown(const char __user * filename, uid_t user, gid_t group) +SYSCALL_DEFINE3(lchown, const char __user *, filename, uid_t, user, gid_t, group) { - struct nameidata nd; + struct path path; int error; - error = user_path_walk_link(filename, &nd); + error = user_lpath(filename, &path); if (error) goto out; - error = chown_common(nd.dentry, user, group); - path_release(&nd); + error = mnt_want_write(path.mnt); + if (error) + goto out_release; + error = chown_common(path.dentry, user, group); + mnt_drop_write(path.mnt); +out_release: + path_put(&path); out: return error; } - -asmlinkage long sys_fchown(unsigned int fd, uid_t user, gid_t group) +SYSCALL_DEFINE3(fchown, unsigned int, fd, uid_t, user, gid_t, group) { struct file * file; int error = -EBADF; @@ -661,29 +760,66 @@ asmlinkage long sys_fchown(unsigned int fd, uid_t user, gid_t group) if (!file) goto out; + error = mnt_want_write(file->f_path.mnt); + if (error) + goto out_fput; dentry = file->f_path.dentry; - audit_inode(NULL, dentry->d_inode); + audit_inode(NULL, dentry); error = chown_common(dentry, user, group); + mnt_drop_write(file->f_path.mnt); +out_fput: fput(file); out: return error; } +/* + * You have to be very careful that these write + * counts get cleaned up in error cases and + * upon __fput(). This should probably never + * be called outside of __dentry_open(). + */ +static inline int __get_file_write_access(struct inode *inode, + struct vfsmount *mnt) +{ + int error; + error = get_write_access(inode); + if (error) + return error; + /* + * Do not take mount writer counts on + * special files since no writes to + * the mount itself will occur. + */ + if (!special_file(inode->i_mode)) { + /* + * Balanced in __fput() + */ + error = mnt_want_write(mnt); + if (error) + put_write_access(inode); + } + return error; +} + static struct file *__dentry_open(struct dentry *dentry, struct vfsmount *mnt, int flags, struct file *f, - int (*open)(struct inode *, struct file *)) + int (*open)(struct inode *, struct file *), + const struct cred *cred) { struct inode *inode; int error; f->f_flags = flags; - f->f_mode = ((flags+1) & O_ACCMODE) | FMODE_LSEEK | + f->f_mode = (__force fmode_t)((flags+1) & O_ACCMODE) | FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE; inode = dentry->d_inode; if (f->f_mode & FMODE_WRITE) { - error = get_write_access(inode); + error = __get_file_write_access(inode, mnt); if (error) goto cleanup_file; + if (!special_file(inode->i_mode)) + file_take_write(f); } f->f_mapping = inode->i_mapping; @@ -693,6 +829,10 @@ static struct file *__dentry_open(struct dentry *dentry, struct vfsmount *mnt, f->f_op = fops_get(inode->i_fop); file_move(f, &inode->i_sb->s_files); + error = security_dentry_open(f, cred); + if (error) + goto cleanup_all; + if (!open && f->f_op) open = f->f_op->open; if (open) { @@ -709,7 +849,7 @@ static struct file *__dentry_open(struct dentry *dentry, struct vfsmount *mnt, if (f->f_flags & O_DIRECT) { if (!f->f_mapping->a_ops || ((!f->f_mapping->a_ops->direct_IO) && - (!f->f_mapping->a_ops->get_xip_page))) { + (!f->f_mapping->a_ops->get_xip_mem))) { fput(f); f = ERR_PTR(-EINVAL); } @@ -719,8 +859,19 @@ static struct file *__dentry_open(struct dentry *dentry, struct vfsmount *mnt, cleanup_all: fops_put(f->f_op); - if (f->f_mode & FMODE_WRITE) + if (f->f_mode & FMODE_WRITE) { put_write_access(inode); + if (!special_file(inode->i_mode)) { + /* + * We don't consider this a real + * mnt_want/drop_write() pair + * because it all happenend right + * here, so just reset the state. + */ + file_reset_write(f); + mnt_drop_write(mnt); + } + } file_kill(f); f->f_path.dentry = NULL; f->f_path.mnt = NULL; @@ -731,43 +882,6 @@ cleanup_file: return ERR_PTR(error); } -/* - * Note that while the flag value (low two bits) for sys_open means: - * 00 - read-only - * 01 - write-only - * 10 - read-write - * 11 - special - * it is changed into - * 00 - no permissions needed - * 01 - read-permission - * 10 - write-permission - * 11 - read-write - * for the internal routines (ie open_namei()/follow_link() etc). 00 is - * used by symlinks. - */ -static struct file *do_filp_open(int dfd, const char *filename, int flags, - int mode) -{ - int namei_flags, error; - struct nameidata nd; - - namei_flags = flags; - if ((namei_flags+1) & O_ACCMODE) - namei_flags++; - - error = open_namei(dfd, filename, namei_flags, mode, &nd); - if (!error) - return nameidata_to_filp(&nd, flags); - - return ERR_PTR(error); -} - -struct file *filp_open(const char *filename, int flags, int mode) -{ - return do_filp_open(AT_FDCWD, filename, flags, mode); -} -EXPORT_SYMBOL(filp_open); - /** * lookup_instantiate_filp - instantiates the open intent filp * @nd: pointer to nameidata @@ -790,14 +904,16 @@ EXPORT_SYMBOL(filp_open); struct file *lookup_instantiate_filp(struct nameidata *nd, struct dentry *dentry, int (*open)(struct inode *, struct file *)) { + const struct cred *cred = current_cred(); + if (IS_ERR(nd->intent.open.file)) goto out; if (IS_ERR(dentry)) goto out_err; - nd->intent.open.file = __dentry_open(dget(dentry), mntget(nd->mnt), + nd->intent.open.file = __dentry_open(dget(dentry), mntget(nd->path.mnt), nd->intent.open.flags - 1, nd->intent.open.file, - open); + open, cred); out: return nd->intent.open.file; out_err: @@ -816,15 +932,17 @@ EXPORT_SYMBOL_GPL(lookup_instantiate_filp); */ struct file *nameidata_to_filp(struct nameidata *nd, int flags) { + const struct cred *cred = current_cred(); struct file *filp; /* Pick up the filp from the open intent */ filp = nd->intent.open.file; /* Has the filesystem initialised the file for us? */ if (filp->f_path.dentry == NULL) - filp = __dentry_open(nd->dentry, nd->mnt, flags, filp, NULL); + filp = __dentry_open(nd->path.dentry, nd->path.mnt, flags, filp, + NULL, cred); else - path_release(nd); + path_put(&nd->path); return filp; } @@ -832,11 +950,24 @@ struct file *nameidata_to_filp(struct nameidata *nd, int flags) * dentry_open() will have done dput(dentry) and mntput(mnt) if it returns an * error. */ -struct file *dentry_open(struct dentry *dentry, struct vfsmount *mnt, int flags) +struct file *dentry_open(struct dentry *dentry, struct vfsmount *mnt, int flags, + const struct cred *cred) { int error; struct file *f; + /* + * We must always pass in a valid mount pointer. Historically + * callers got away with not passing it, but we must enforce this at + * the earliest possible point now to avoid strange problems deep in the + * filesystem stack. + */ + if (!mnt) { + printk(KERN_WARNING "%s called with NULL vfsmount\n", __func__); + dump_stack(); + return ERR_PTR(-EINVAL); + } + error = -ENFILE; f = get_empty_filp(); if (f == NULL) { @@ -845,67 +976,10 @@ struct file *dentry_open(struct dentry *dentry, struct vfsmount *mnt, int flags) return ERR_PTR(error); } - return __dentry_open(dentry, mnt, flags, f, NULL); + return __dentry_open(dentry, mnt, flags, f, NULL, cred); } EXPORT_SYMBOL(dentry_open); -/* - * Find an empty file descriptor entry, and mark it busy. - */ -int get_unused_fd(void) -{ - struct files_struct * files = current->files; - int fd, error; - struct fdtable *fdt; - - error = -EMFILE; - spin_lock(&files->file_lock); - -repeat: - fdt = files_fdtable(files); - fd = find_next_zero_bit(fdt->open_fds->fds_bits, fdt->max_fds, - files->next_fd); - - /* - * N.B. For clone tasks sharing a files structure, this test - * will limit the total number of files that can be opened. - */ - if (fd >= current->signal->rlim[RLIMIT_NOFILE].rlim_cur) - goto out; - - /* Do we need to expand the fd array or fd set? */ - error = expand_files(files, fd); - if (error < 0) - goto out; - - if (error) { - /* - * If we needed to expand the fs array we - * might have blocked - try again. - */ - error = -EMFILE; - goto repeat; - } - - FD_SET(fd, fdt->open_fds); - FD_CLR(fd, fdt->close_on_exec); - files->next_fd = fd + 1; -#if 1 - /* Sanity check */ - if (fdt->fd[fd] != NULL) { - printk(KERN_WARNING "get_unused_fd: slot %d not NULL!\n", fd); - fdt->fd[fd] = NULL; - } -#endif - error = fd; - -out: - spin_unlock(&files->file_lock); - return error; -} - -EXPORT_SYMBOL(get_unused_fd); - static void __put_unused_fd(struct files_struct *files, unsigned int fd) { struct fdtable *fdt = files_fdtable(files); @@ -914,7 +988,7 @@ static void __put_unused_fd(struct files_struct *files, unsigned int fd) files->next_fd = fd; } -void fastcall put_unused_fd(unsigned int fd) +void put_unused_fd(unsigned int fd) { struct files_struct *files = current->files; spin_lock(&files->file_lock); @@ -937,7 +1011,7 @@ EXPORT_SYMBOL(put_unused_fd); * will follow. */ -void fastcall fd_install(unsigned int fd, struct file * file) +void fd_install(unsigned int fd, struct file *file) { struct files_struct *files = current->files; struct fdtable *fdt; @@ -956,7 +1030,7 @@ long do_sys_open(int dfd, const char __user *filename, int flags, int mode) int fd = PTR_ERR(tmp); if (!IS_ERR(tmp)) { - fd = get_unused_fd(); + fd = get_unused_fd_flags(flags); if (fd >= 0) { struct file *f = do_filp_open(dfd, tmp, flags, mode); if (IS_ERR(f)) { @@ -972,7 +1046,7 @@ long do_sys_open(int dfd, const char __user *filename, int flags, int mode) return fd; } -asmlinkage long sys_open(const char __user *filename, int flags, int mode) +SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, int, mode) { long ret; @@ -981,13 +1055,12 @@ asmlinkage long sys_open(const char __user *filename, int flags, int mode) ret = do_sys_open(AT_FDCWD, filename, flags, mode); /* avoid REGPARM breakage on x86: */ - prevent_tail_call(ret); + asmlinkage_protect(3, ret, filename, flags, mode); return ret; } -EXPORT_SYMBOL_GPL(sys_open); -asmlinkage long sys_openat(int dfd, const char __user *filename, int flags, - int mode) +SYSCALL_DEFINE4(openat, int, dfd, const char __user *, filename, int, flags, + int, mode) { long ret; @@ -996,7 +1069,7 @@ asmlinkage long sys_openat(int dfd, const char __user *filename, int flags, ret = do_sys_open(dfd, filename, flags, mode); /* avoid REGPARM breakage on x86: */ - prevent_tail_call(ret); + asmlinkage_protect(4, ret, dfd, filename, flags, mode); return ret; } @@ -1006,7 +1079,7 @@ asmlinkage long sys_openat(int dfd, const char __user *filename, int flags, * For backward compatibility? Maybe this should be moved * into arch/i386 instead? */ -asmlinkage long sys_creat(const char __user * pathname, int mode) +SYSCALL_DEFINE2(creat, const char __user *, pathname, int, mode) { return sys_open(pathname, O_CREAT | O_WRONLY | O_TRUNC, mode); } @@ -1042,7 +1115,7 @@ EXPORT_SYMBOL(filp_close); * releasing the fd. This ensures that one clone task can't release * an fd while another clone is opening it. */ -asmlinkage long sys_close(unsigned int fd) +SYSCALL_DEFINE1(close, unsigned int, fd) { struct file * filp; struct files_struct *files = current->files; @@ -1075,18 +1148,16 @@ out_unlock: spin_unlock(&files->file_lock); return -EBADF; } - EXPORT_SYMBOL(sys_close); /* * This routine simulates a hangup on the tty, to arrange that users * are given clean terminals at login time. */ -asmlinkage long sys_vhangup(void) +SYSCALL_DEFINE0(vhangup) { if (capable(CAP_SYS_TTY_CONFIG)) { - /* XXX: this needs locking */ - tty_vhangup(current->signal->tty); + tty_vhangup_self(); return 0; } return -EPERM; @@ -1101,7 +1172,7 @@ asmlinkage long sys_vhangup(void) int generic_file_open(struct inode * inode, struct file * filp) { if (!(filp->f_flags & O_LARGEFILE) && i_size_read(inode) > MAX_NON_LFS) - return -EFBIG; + return -EOVERFLOW; return 0; }