nfsd4: kill unnecessary check in preprocess_stateid_op
[safe/jmp/linux-2.6] / fs / open.c
index 0d94319..b70e766 100644 (file)
--- a/fs/open.c
+++ b/fs/open.c
@@ -7,7 +7,6 @@
 #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>
@@ -27,6 +26,7 @@
 #include <linux/syscalls.h>
 #include <linux/rcupdate.h>
 #include <linux/audit.h>
+#include <linux/falloc.h>
 
 int vfs_statfs(struct dentry *dentry, struct kstatfs *buf)
 {
@@ -127,10 +127,10 @@ asmlinkage long sys_statfs(const char __user * path, struct statfs __user * buf)
        error = user_path_walk(path, &nd);
        if (!error) {
                struct statfs tmp;
-               error = vfs_statfs_native(nd.dentry, &tmp);
+               error = vfs_statfs_native(nd.path.dentry, &tmp);
                if (!error && copy_to_user(buf, &tmp, sizeof(tmp)))
                        error = -EFAULT;
-               path_release(&nd);
+               path_put(&nd.path);
        }
        return error;
 }
@@ -146,10 +146,10 @@ asmlinkage long sys_statfs64(const char __user *path, size_t sz, struct statfs64
        error = user_path_walk(path, &nd);
        if (!error) {
                struct statfs64 tmp;
-               error = vfs_statfs64(nd.dentry, &tmp);
+               error = vfs_statfs64(nd.path.dentry, &tmp);
                if (!error && copy_to_user(buf, &tmp, sizeof(tmp)))
                        error = -EFAULT;
-               path_release(&nd);
+               path_put(&nd.path);
        }
        return error;
 }
@@ -211,6 +211,9 @@ 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);
@@ -230,7 +233,7 @@ static long do_sys_truncate(const char __user * path, loff_t length)
        error = user_path_walk(path, &nd);
        if (error)
                goto out;
-       inode = nd.dentry->d_inode;
+       inode = nd.path.dentry->d_inode;
 
        /* For directories it's -EISDIR, for other non-regulars - -EINVAL */
        error = -EISDIR;
@@ -241,38 +244,42 @@ 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(nd.path.mnt);
        if (error)
                goto dput_and_out;
 
-       error = -EROFS;
-       if (IS_RDONLY(inode))
-               goto dput_and_out;
+       error = vfs_permission(&nd, MAY_WRITE);
+       if (error)
+               goto mnt_drop_write_and_out;
 
        error = -EPERM;
        if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
-               goto dput_and_out;
+               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) {
                DQUOT_INIT(inode);
-               error = do_truncate(nd.dentry, length, 0, NULL);
+               error = do_truncate(nd.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(nd.path.mnt);
 dput_and_out:
-       path_release(&nd);
+       path_put(&nd.path);
 out:
        return error;
 }
@@ -330,7 +337,7 @@ 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);
+       asmlinkage_protect(2, ret, fd, length);
        return ret;
 }
 
@@ -345,11 +352,69 @@ 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);
+       asmlinkage_protect(2, ret, fd, length);
        return ret;
 }
 #endif
 
+asmlinkage long sys_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 && inode->i_op->fallocate)
+               ret = inode->i_op->fallocate(inode, mode, offset, len);
+       else
+               ret = -EOPNOTSUPP;
+
+out_fput:
+       fput(file);
+out:
+       return ret;
+}
+
 /*
  * access() needs to use the real uid/gid, not the effective uid/gid.
  * We do this by temporarily clearing all FS-related capabilities and
@@ -392,14 +457,23 @@ asmlinkage long sys_faccessat(int dfd, const char __user *filename, int mode)
        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))
+          special_file(nd.path.dentry->d_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(nd.path.mnt))
                res = -EROFS;
 
 out_path_release:
-       path_release(&nd);
+       path_put(&nd.path);
 out:
        current->fsuid = old_fsuid;
        current->fsgid = old_fsgid;
@@ -427,10 +501,10 @@ asmlinkage long sys_chdir(const char __user * filename)
        if (error)
                goto dput_and_out;
 
-       set_fs_pwd(current->fs, nd.mnt, nd.dentry);
+       set_fs_pwd(current->fs, &nd.path);
 
 dput_and_out:
-       path_release(&nd);
+       path_put(&nd.path);
 out:
        return error;
 }
@@ -438,9 +512,7 @@ out:
 asmlinkage long sys_fchdir(unsigned int fd)
 {
        struct file *file;
-       struct dentry *dentry;
        struct inode *inode;
-       struct vfsmount *mnt;
        int error;
 
        error = -EBADF;
@@ -448,9 +520,7 @@ 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))
@@ -458,7 +528,7 @@ asmlinkage long sys_fchdir(unsigned int fd)
 
        error = file_permission(file, MAY_EXEC);
        if (!error)
-               set_fs_pwd(current->fs, mnt, dentry);
+               set_fs_pwd(current->fs, &file->f_path);
 out_putf:
        fput(file);
 out:
@@ -482,11 +552,11 @@ 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_root(current->fs, &nd.path);
        set_fs_altroot();
        error = 0;
 dput_and_out:
-       path_release(&nd);
+       path_put(&nd.path);
 out:
        return error;
 }
@@ -506,14 +576,14 @@ 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))
+       err = mnt_want_write(file->f_path.mnt);
+       if (err)
                goto out_putf;
        err = -EPERM;
        if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
-               goto out_putf;
+               goto out_drop_write;
        mutex_lock(&inode->i_mutex);
        if (mode == (mode_t) -1)
                mode = inode->i_mode;
@@ -522,6 +592,8 @@ asmlinkage long sys_fchmod(unsigned int fd, mode_t mode)
        err = notify_change(dentry, &newattrs);
        mutex_unlock(&inode->i_mutex);
 
+out_drop_write:
+       mnt_drop_write(file->f_path.mnt);
 out_putf:
        fput(file);
 out:
@@ -539,26 +611,28 @@ asmlinkage long sys_fchmodat(int dfd, const char __user *filename,
        error = __user_walk_fd(dfd, filename, LOOKUP_FOLLOW, &nd);
        if (error)
                goto out;
-       inode = nd.dentry->d_inode;
+       inode = nd.path.dentry->d_inode;
 
-       error = -EROFS;
-       if (IS_RDONLY(inode))
+       error = mnt_want_write(nd.path.mnt);
+       if (error)
                goto dput_and_out;
 
        error = -EPERM;
        if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
-               goto dput_and_out;
+               goto out_drop_write;
 
        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(nd.path.dentry, &newattrs);
        mutex_unlock(&inode->i_mutex);
 
+out_drop_write:
+       mnt_drop_write(nd.path.mnt);
 dput_and_out:
-       path_release(&nd);
+       path_put(&nd.path);
 out:
        return error;
 }
@@ -579,9 +653,6 @@ static int chown_common(struct dentry * dentry, uid_t user, gid_t group)
                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;
@@ -595,7 +666,8 @@ 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);
@@ -611,8 +683,13 @@ asmlinkage long sys_chown(const char __user * filename, uid_t user, gid_t group)
        error = user_path_walk(filename, &nd);
        if (error)
                goto out;
-       error = chown_common(nd.dentry, user, group);
-       path_release(&nd);
+       error = mnt_want_write(nd.path.mnt);
+       if (error)
+               goto out_release;
+       error = chown_common(nd.path.dentry, user, group);
+       mnt_drop_write(nd.path.mnt);
+out_release:
+       path_put(&nd.path);
 out:
        return error;
 }
@@ -631,8 +708,13 @@ asmlinkage long sys_fchownat(int dfd, const char __user *filename, uid_t user,
        error = __user_walk_fd(dfd, filename, follow, &nd);
        if (error)
                goto out;
-       error = chown_common(nd.dentry, user, group);
-       path_release(&nd);
+       error = mnt_want_write(nd.path.mnt);
+       if (error)
+               goto out_release;
+       error = chown_common(nd.path.dentry, user, group);
+       mnt_drop_write(nd.path.mnt);
+out_release:
+       path_put(&nd.path);
 out:
        return error;
 }
@@ -645,8 +727,13 @@ asmlinkage long sys_lchown(const char __user * filename, uid_t user, gid_t group
        error = user_path_walk_link(filename, &nd);
        if (error)
                goto out;
-       error = chown_common(nd.dentry, user, group);
-       path_release(&nd);
+       error = mnt_want_write(nd.path.mnt);
+       if (error)
+               goto out_release;
+       error = chown_common(nd.path.dentry, user, group);
+       mnt_drop_write(nd.path.mnt);
+out_release:
+       path_put(&nd.path);
 out:
        return error;
 }
@@ -662,14 +749,48 @@ 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 *))
@@ -682,9 +803,11 @@ static struct file *__dentry_open(struct dentry *dentry, struct vfsmount *mnt,
                                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;
@@ -694,6 +817,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);
+       if (error)
+               goto cleanup_all;
+
        if (!open && f->f_op)
                open = f->f_op->open;
        if (open) {
@@ -720,8 +847,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;
@@ -732,43 +870,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
@@ -795,7 +896,7 @@ struct file *lookup_instantiate_filp(struct nameidata *nd, struct dentry *dentry
                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);
@@ -823,9 +924,10 @@ struct file *nameidata_to_filp(struct nameidata *nd, int flags)
        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);
        else
-               path_release(nd);
+               path_put(&nd->path);
        return filp;
 }
 
@@ -838,6 +940,18 @@ struct file *dentry_open(struct dentry *dentry, struct vfsmount *mnt, int flags)
        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) {
@@ -853,7 +967,7 @@ EXPORT_SYMBOL(dentry_open);
 /*
  * Find an empty file descriptor entry, and mark it busy.
  */
-int get_unused_fd(void)
+int get_unused_fd_flags(int flags)
 {
        struct files_struct * files = current->files;
        int fd, error;
@@ -864,8 +978,7 @@ int get_unused_fd(void)
 
 repeat:
        fdt = files_fdtable(files);
-       fd = find_next_zero_bit(fdt->open_fds->fds_bits,
-                               fdt->max_fdset,
+       fd = find_next_zero_bit(fdt->open_fds->fds_bits, fdt->max_fds,
                                files->next_fd);
 
        /*
@@ -890,7 +1003,10 @@ repeat:
        }
 
        FD_SET(fd, fdt->open_fds);
-       FD_CLR(fd, fdt->close_on_exec);
+       if (flags & O_CLOEXEC)
+               FD_SET(fd, fdt->close_on_exec);
+       else
+               FD_CLR(fd, fdt->close_on_exec);
        files->next_fd = fd + 1;
 #if 1
        /* Sanity check */
@@ -906,6 +1022,11 @@ out:
        return error;
 }
 
+int get_unused_fd(void)
+{
+       return get_unused_fd_flags(0);
+}
+
 EXPORT_SYMBOL(get_unused_fd);
 
 static void __put_unused_fd(struct files_struct *files, unsigned int fd)
@@ -916,7 +1037,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);
@@ -939,7 +1060,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;
@@ -958,7 +1079,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)) {
@@ -983,10 +1104,9 @@ 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)
@@ -998,7 +1118,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;
 }
 
@@ -1103,7 +1223,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;
 }