]> Pileus Git - ~andy/linux/blobdiff - fs/open.c
fs: Add freezing handling to mnt_want_write() / mnt_drop_write()
[~andy/linux] / fs / open.c
index d6c79a0dffc7b0827b09562e11fa0f610af5657d..9ddc1856550379e223dcc962ec43ac432807a0d7 100644 (file)
--- a/fs/open.c
+++ b/fs/open.c
@@ -397,10 +397,10 @@ SYSCALL_DEFINE1(fchdir, unsigned int, fd)
 {
        struct file *file;
        struct inode *inode;
-       int error;
+       int error, fput_needed;
 
        error = -EBADF;
-       file = fget(fd);
+       file = fget_raw_light(fd, &fput_needed);
        if (!file)
                goto out;
 
@@ -414,7 +414,7 @@ SYSCALL_DEFINE1(fchdir, unsigned int, fd)
        if (!error)
                set_fs_pwd(current->fs, &file->f_path);
 out_putf:
-       fput(file);
+       fput_light(file, fput_needed);
 out:
        return error;
 }
@@ -537,25 +537,6 @@ static int chown_common(struct path *path, uid_t user, gid_t group)
        return error;
 }
 
-SYSCALL_DEFINE3(chown, const char __user *, filename, uid_t, user, gid_t, group)
-{
-       struct path path;
-       int error;
-
-       error = user_path(filename, &path);
-       if (error)
-               goto out;
-       error = mnt_want_write(path.mnt);
-       if (error)
-               goto out_release;
-       error = chown_common(&path, user, group);
-       mnt_drop_write(path.mnt);
-out_release:
-       path_put(&path);
-out:
-       return error;
-}
-
 SYSCALL_DEFINE5(fchownat, int, dfd, const char __user *, filename, uid_t, user,
                gid_t, group, int, flag)
 {
@@ -583,23 +564,15 @@ out:
        return error;
 }
 
-SYSCALL_DEFINE3(lchown, const char __user *, filename, uid_t, user, gid_t, group)
+SYSCALL_DEFINE3(chown, const char __user *, filename, uid_t, user, gid_t, group)
 {
-       struct path path;
-       int error;
+       return sys_fchownat(AT_FDCWD, filename, user, group, 0);
+}
 
-       error = user_lpath(filename, &path);
-       if (error)
-               goto out;
-       error = mnt_want_write(path.mnt);
-       if (error)
-               goto out_release;
-       error = chown_common(&path, user, group);
-       mnt_drop_write(path.mnt);
-out_release:
-       path_put(&path);
-out:
-       return error;
+SYSCALL_DEFINE3(lchown, const char __user *, filename, uid_t, user, gid_t, group)
+{
+       return sys_fchownat(AT_FDCWD, filename, user, group,
+                           AT_SYMLINK_NOFOLLOW);
 }
 
 SYSCALL_DEFINE3(fchown, unsigned int, fd, uid_t, user, gid_t, group)
@@ -647,7 +620,7 @@ static inline int __get_file_write_access(struct inode *inode,
                /*
                 * Balanced in __fput()
                 */
-               error = mnt_want_write(mnt);
+               error = __mnt_want_write(mnt);
                if (error)
                        put_write_access(inode);
        }
@@ -667,10 +640,9 @@ int open_check_o_direct(struct file *f)
        return 0;
 }
 
-static struct file *do_dentry_open(struct dentry *dentry, struct vfsmount *mnt,
-                                  struct file *f,
-                                  int (*open)(struct inode *, struct file *),
-                                  const struct cred *cred)
+static int do_dentry_open(struct file *f,
+                         int (*open)(struct inode *, struct file *),
+                         const struct cred *cred)
 {
        static const struct file_operations empty_fops = {};
        struct inode *inode;
@@ -682,9 +654,10 @@ static struct file *do_dentry_open(struct dentry *dentry, struct vfsmount *mnt,
        if (unlikely(f->f_flags & O_PATH))
                f->f_mode = FMODE_PATH;
 
-       inode = dentry->d_inode;
+       path_get(&f->f_path);
+       inode = f->f_path.dentry->d_inode;
        if (f->f_mode & FMODE_WRITE) {
-               error = __get_file_write_access(inode, mnt);
+               error = __get_file_write_access(inode, f->f_path.mnt);
                if (error)
                        goto cleanup_file;
                if (!special_file(inode->i_mode))
@@ -692,14 +665,12 @@ static struct file *do_dentry_open(struct dentry *dentry, struct vfsmount *mnt,
        }
 
        f->f_mapping = inode->i_mapping;
-       f->f_path.dentry = dentry;
-       f->f_path.mnt = mnt;
        f->f_pos = 0;
        file_sb_list_add(f, inode->i_sb);
 
        if (unlikely(f->f_mode & FMODE_PATH)) {
                f->f_op = &empty_fops;
-               return f;
+               return 0;
        }
 
        f->f_op = fops_get(inode->i_fop);
@@ -726,10 +697,11 @@ static struct file *do_dentry_open(struct dentry *dentry, struct vfsmount *mnt,
 
        file_ra_state_init(&f->f_ra, f->f_mapping->host->i_mapping);
 
-       return f;
+       return 0;
 
 cleanup_all:
        fops_put(f->f_op);
+       file_sb_list_del(f);
        if (f->f_mode & FMODE_WRITE) {
                put_write_access(inode);
                if (!special_file(inode->i_mode)) {
@@ -740,124 +712,60 @@ cleanup_all:
                         * here, so just reset the state.
                         */
                        file_reset_write(f);
-                       mnt_drop_write(mnt);
+                       mnt_drop_write(f->f_path.mnt);
                }
        }
-       file_sb_list_del(f);
-       f->f_path.dentry = NULL;
-       f->f_path.mnt = NULL;
 cleanup_file:
-       dput(dentry);
-       mntput(mnt);
-       return ERR_PTR(error);
-}
-
-static struct file *__dentry_open(struct dentry *dentry, struct vfsmount *mnt,
-                               struct file *f,
-                               int (*open)(struct inode *, struct file *),
-                               const struct cred *cred)
-{
-       struct file *res = do_dentry_open(dentry, mnt, f, open, cred);
-       if (!IS_ERR(res)) {
-               int error = open_check_o_direct(f);
-               if (error) {
-                       fput(res);
-                       res = ERR_PTR(error);
-               }
-       } else {
-               put_filp(f);
-       }
-       return res;
+       path_put(&f->f_path);
+       f->f_path.mnt = NULL;
+       f->f_path.dentry = NULL;
+       return error;
 }
 
 /**
- * lookup_instantiate_filp - instantiates the open intent filp
- * @nd: pointer to nameidata
+ * finish_open - finish opening a file
+ * @od: opaque open data
  * @dentry: pointer to dentry
  * @open: open callback
  *
- * Helper for filesystems that want to use lookup open intents and pass back
- * a fully instantiated struct file to the caller.
- * This function is meant to be called from within a filesystem's
- * lookup method.
- * Beware of calling it for non-regular files! Those ->open methods might block
- * (e.g. in fifo_open), leaving you with parent locked (and in case of fifo,
- * leading to a deadlock, as nobody can open that fifo anymore, because
- * another process to open fifo will block on locked parent when doing lookup).
- * Note that in case of error, nd->intent.open.file is destroyed, but the
- * path information remains valid.
+ * This can be used to finish opening a file passed to i_op->atomic_open().
+ *
  * If the open callback is set to NULL, then the standard f_op->open()
  * filesystem callback is substituted.
  */
-struct file *lookup_instantiate_filp(struct nameidata *nd, struct dentry *dentry,
-               int (*open)(struct inode *, struct file *))
+int finish_open(struct file *file, struct dentry *dentry,
+               int (*open)(struct inode *, struct file *),
+               int *opened)
 {
-       const struct cred *cred = current_cred();
+       int error;
+       BUG_ON(*opened & FILE_OPENED); /* once it's opened, it's opened */
 
-       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->path.mnt),
-                                            nd->intent.open.file,
-                                            open, cred);
-out:
-       return nd->intent.open.file;
-out_err:
-       release_open_intent(nd);
-       nd->intent.open.file = ERR_CAST(dentry);
-       goto out;
+       file->f_path.dentry = dentry;
+       error = do_dentry_open(file, open, current_cred());
+       if (!error)
+               *opened |= FILE_OPENED;
+
+       return error;
 }
-EXPORT_SYMBOL_GPL(lookup_instantiate_filp);
+EXPORT_SYMBOL(finish_open);
 
 /**
- * nameidata_to_filp - convert a nameidata to an open filp.
- * @nd: pointer to nameidata
- * @flags: open flags
+ * finish_no_open - finish ->atomic_open() without opening the file
+ *
+ * @od: opaque open data
+ * @dentry: dentry or NULL (as returned from ->lookup())
  *
- * Note that this function destroys the original nameidata
+ * This can be used to set the result of a successful lookup in ->atomic_open().
+ * The filesystem's atomic_open() method shall return NULL after calling this.
  */
-struct file *nameidata_to_filp(struct nameidata *nd)
+int finish_no_open(struct file *file, struct dentry *dentry)
 {
-       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) {
-               nd->intent.open.file = NULL;
-       } else {
-               struct file *res;
-
-               path_get(&nd->path);
-               res = do_dentry_open(nd->path.dentry, nd->path.mnt,
-                                    filp, NULL, cred);
-               if (!IS_ERR(res)) {
-                       int error;
-
-                       nd->intent.open.file = NULL;
-                       BUG_ON(res != filp);
-
-                       error = open_check_o_direct(filp);
-                       if (error) {
-                               fput(filp);
-                               filp = ERR_PTR(error);
-                       }
-               } else {
-                       /* Allow nd->intent.open.file to be recycled */
-                       filp = res;
-               }
-       }
-       return filp;
+       file->f_path.dentry = dentry;
+       return 1;
 }
+EXPORT_SYMBOL(finish_no_open);
 
-/*
- * 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(const struct path *path, int flags,
                         const struct cred *cred)
 {
        int error;
@@ -866,18 +774,27 @@ struct file *dentry_open(struct dentry *dentry, struct vfsmount *mnt, int flags,
        validate_creds(cred);
 
        /* We must always pass in a valid mount pointer. */
-       BUG_ON(!mnt);
+       BUG_ON(!path->mnt);
 
        error = -ENFILE;
        f = get_empty_filp();
-       if (f == NULL) {
-               dput(dentry);
-               mntput(mnt);
+       if (f == NULL)
                return ERR_PTR(error);
-       }
 
        f->f_flags = flags;
-       return __dentry_open(dentry, mnt, f, NULL, cred);
+       f->f_path = *path;
+       error = do_dentry_open(f, NULL, cred);
+       if (!error) {
+               error = open_check_o_direct(f);
+               if (error) {
+                       fput(f);
+                       f = ERR_PTR(error);
+               }
+       } else { 
+               put_filp(f);
+               f = ERR_PTR(error);
+       }
+       return f;
 }
 EXPORT_SYMBOL(dentry_open);