Merge tag 'upstream-4.9-rc1' of git://git.infradead.org/linux-ubifs
authorLinus Torvalds <torvalds@linux-foundation.org>
Tue, 11 Oct 2016 17:49:44 +0000 (10:49 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Tue, 11 Oct 2016 17:49:44 +0000 (10:49 -0700)
Pull UBI/UBIFS updates from Richard Weinberger:
 "This pull request contains:

   - Fixes for both UBI and UBIFS
   - overlayfs support (O_TMPFILE, RENAME_WHITEOUT/EXCHANGE)
   - Code refactoring for the upcoming MLC support"

[ Ugh, we just got rid of the "rename2()" naming for the extended rename
  functionality. And this re-introduces it in ubifs with the cross-
  renaming and whiteout support.

  But rather than do any re-organizations in the merge itself, the
  naming can be cleaned up later ]

* tag 'upstream-4.9-rc1' of git://git.infradead.org/linux-ubifs: (27 commits)
  UBIFS: improve function-level documentation
  ubifs: fix host xattr_len when changing xattr
  ubifs: Use move variable in ubifs_rename()
  ubifs: Implement RENAME_EXCHANGE
  ubifs: Implement RENAME_WHITEOUT
  ubifs: Implement O_TMPFILE
  ubi: Fix Fastmap's update_vol()
  ubi: Fix races around ubi_refill_pools()
  ubi: Deal with interrupted erasures in WL
  UBI: introduce the VID buffer concept
  UBI: hide EBA internals
  UBI: provide an helper to query LEB information
  UBI: provide an helper to check whether a LEB is mapped or not
  UBI: add an helper to check lnum validity
  UBI: simplify LEB write and atomic LEB change code
  UBI: simplify recover_peb() code
  UBI: move the global ech and vidh variables into struct ubi_attach_info
  UBI: provide helpers to allocate and free aeb elements
  UBI: fastmap: use ubi_io_{read, write}_data() instead of ubi_io_{read, write}()
  UBI: fastmap: use ubi_rb_for_each_entry() in unmap_peb()
  ...

1  2 
fs/ubifs/dir.c
fs/ubifs/file.c

diff --combined fs/ubifs/dir.c
@@@ -301,6 -301,95 +301,95 @@@ out_budg
        return err;
  }
  
+ static int do_tmpfile(struct inode *dir, struct dentry *dentry,
+                     umode_t mode, struct inode **whiteout)
+ {
+       struct inode *inode;
+       struct ubifs_info *c = dir->i_sb->s_fs_info;
+       struct ubifs_budget_req req = { .new_ino = 1, .new_dent = 1};
+       struct ubifs_budget_req ino_req = { .dirtied_ino = 1 };
+       struct ubifs_inode *ui, *dir_ui = ubifs_inode(dir);
+       int err, instantiated = 0;
+       /*
+        * Budget request settings: new dirty inode, new direntry,
+        * budget for dirtied inode will be released via writeback.
+        */
+       dbg_gen("dent '%pd', mode %#hx in dir ino %lu",
+               dentry, mode, dir->i_ino);
+       err = ubifs_budget_space(c, &req);
+       if (err)
+               return err;
+       err = ubifs_budget_space(c, &ino_req);
+       if (err) {
+               ubifs_release_budget(c, &req);
+               return err;
+       }
+       inode = ubifs_new_inode(c, dir, mode);
+       if (IS_ERR(inode)) {
+               err = PTR_ERR(inode);
+               goto out_budg;
+       }
+       ui = ubifs_inode(inode);
+       if (whiteout) {
+               init_special_inode(inode, inode->i_mode, WHITEOUT_DEV);
+               ubifs_assert(inode->i_op == &ubifs_file_inode_operations);
+       }
+       err = ubifs_init_security(dir, inode, &dentry->d_name);
+       if (err)
+               goto out_inode;
+       mutex_lock(&ui->ui_mutex);
+       insert_inode_hash(inode);
+       if (whiteout) {
+               mark_inode_dirty(inode);
+               drop_nlink(inode);
+               *whiteout = inode;
+       } else {
+               d_tmpfile(dentry, inode);
+       }
+       ubifs_assert(ui->dirty);
+       instantiated = 1;
+       mutex_unlock(&ui->ui_mutex);
+       mutex_lock(&dir_ui->ui_mutex);
+       err = ubifs_jnl_update(c, dir, &dentry->d_name, inode, 1, 0);
+       if (err)
+               goto out_cancel;
+       mutex_unlock(&dir_ui->ui_mutex);
+       ubifs_release_budget(c, &req);
+       return 0;
+ out_cancel:
+       mutex_unlock(&dir_ui->ui_mutex);
+ out_inode:
+       make_bad_inode(inode);
+       if (!instantiated)
+               iput(inode);
+ out_budg:
+       ubifs_release_budget(c, &req);
+       if (!instantiated)
+               ubifs_release_budget(c, &ino_req);
+       ubifs_err(c, "cannot create temporary file, error %d", err);
+       return err;
+ }
+ static int ubifs_tmpfile(struct inode *dir, struct dentry *dentry,
+                        umode_t mode)
+ {
+       return do_tmpfile(dir, dentry, mode, NULL);
+ }
  /**
   * vfs_dent_type - get VFS directory entry type.
   * @type: UBIFS directory entry type
@@@ -927,37 -1016,43 +1016,43 @@@ out_budg
  }
  
  /**
-  * lock_3_inodes - a wrapper for locking three UBIFS inodes.
+  * lock_4_inodes - a wrapper for locking three UBIFS inodes.
   * @inode1: first inode
   * @inode2: second inode
   * @inode3: third inode
+  * @inode4: fouth inode
   *
   * This function is used for 'ubifs_rename()' and @inode1 may be the same as
-  * @inode2 whereas @inode3 may be %NULL.
+  * @inode2 whereas @inode3 and @inode4 may be %NULL.
   *
   * We do not implement any tricks to guarantee strict lock ordering, because
   * VFS has already done it for us on the @i_mutex. So this is just a simple
   * wrapper function.
   */
- static void lock_3_inodes(struct inode *inode1, struct inode *inode2,
-                         struct inode *inode3)
+ static void lock_4_inodes(struct inode *inode1, struct inode *inode2,
+                         struct inode *inode3, struct inode *inode4)
  {
        mutex_lock_nested(&ubifs_inode(inode1)->ui_mutex, WB_MUTEX_1);
        if (inode2 != inode1)
                mutex_lock_nested(&ubifs_inode(inode2)->ui_mutex, WB_MUTEX_2);
        if (inode3)
                mutex_lock_nested(&ubifs_inode(inode3)->ui_mutex, WB_MUTEX_3);
+       if (inode4)
+               mutex_lock_nested(&ubifs_inode(inode4)->ui_mutex, WB_MUTEX_4);
  }
  
  /**
-  * unlock_3_inodes - a wrapper for unlocking three UBIFS inodes for rename.
+  * unlock_4_inodes - a wrapper for unlocking three UBIFS inodes for rename.
   * @inode1: first inode
   * @inode2: second inode
   * @inode3: third inode
+  * @inode4: fouth inode
   */
- static void unlock_3_inodes(struct inode *inode1, struct inode *inode2,
-                           struct inode *inode3)
+ static void unlock_4_inodes(struct inode *inode1, struct inode *inode2,
+                           struct inode *inode3, struct inode *inode4)
  {
+       if (inode4)
+               mutex_unlock(&ubifs_inode(inode4)->ui_mutex);
        if (inode3)
                mutex_unlock(&ubifs_inode(inode3)->ui_mutex);
        if (inode1 != inode2)
@@@ -972,7 -1067,9 +1067,9 @@@ static int ubifs_rename(struct inode *o
        struct ubifs_info *c = old_dir->i_sb->s_fs_info;
        struct inode *old_inode = d_inode(old_dentry);
        struct inode *new_inode = d_inode(new_dentry);
+       struct inode *whiteout = NULL;
        struct ubifs_inode *old_inode_ui = ubifs_inode(old_inode);
+       struct ubifs_inode *whiteout_ui = NULL;
        int err, release, sync = 0, move = (new_dir != old_dir);
        int is_dir = S_ISDIR(old_inode->i_mode);
        int unlink = !!new_inode;
        struct timespec time;
        unsigned int uninitialized_var(saved_nlink);
  
 +      if (flags & ~RENAME_NOREPLACE)
 +              return -EINVAL;
 +
        /*
         * Budget request settings: deletion direntry, new direntry, removing
         * the old inode, and changing old and new parent directory inodes.
         * separately.
         */
  
-       dbg_gen("dent '%pd' ino %lu in dir ino %lu to dent '%pd' in dir ino %lu",
+       dbg_gen("dent '%pd' ino %lu in dir ino %lu to dent '%pd' in dir ino %lu flags 0x%x",
                old_dentry, old_inode->i_ino, old_dir->i_ino,
-               new_dentry, new_dir->i_ino);
-       ubifs_assert(inode_is_locked(old_dir));
-       ubifs_assert(inode_is_locked(new_dir));
+               new_dentry, new_dir->i_ino, flags);
        if (unlink)
                ubifs_assert(inode_is_locked(new_inode));
  
        if (unlink && is_dir) {
                err = check_dir_empty(c, new_inode);
                if (err)
                return err;
        }
  
-       lock_3_inodes(old_dir, new_dir, new_inode);
+       if (flags & RENAME_WHITEOUT) {
+               union ubifs_dev_desc *dev = NULL;
+               dev = kmalloc(sizeof(union ubifs_dev_desc), GFP_NOFS);
+               if (!dev) {
+                       ubifs_release_budget(c, &req);
+                       ubifs_release_budget(c, &ino_req);
+                       return -ENOMEM;
+               }
+               err = do_tmpfile(old_dir, old_dentry, S_IFCHR | WHITEOUT_MODE, &whiteout);
+               if (err) {
+                       ubifs_release_budget(c, &req);
+                       ubifs_release_budget(c, &ino_req);
+                       kfree(dev);
+                       return err;
+               }
+               whiteout->i_state |= I_LINKABLE;
+               whiteout_ui = ubifs_inode(whiteout);
+               whiteout_ui->data = dev;
+               whiteout_ui->data_len = ubifs_encode_dev(dev, MKDEV(0, 0));
+               ubifs_assert(!whiteout_ui->dirty);
+       }
+       lock_4_inodes(old_dir, new_dir, new_inode, whiteout);
  
        /*
         * Like most other Unix systems, set the @i_ctime for inodes on a
                if (unlink && IS_SYNC(new_inode))
                        sync = 1;
        }
-       err = ubifs_jnl_rename(c, old_dir, old_dentry, new_dir, new_dentry,
+       if (whiteout) {
+               struct ubifs_budget_req wht_req = { .dirtied_ino = 1,
+                               .dirtied_ino_d = \
+                               ALIGN(ubifs_inode(whiteout)->data_len, 8) };
+               err = ubifs_budget_space(c, &wht_req);
+               if (err) {
+                       ubifs_release_budget(c, &req);
+                       ubifs_release_budget(c, &ino_req);
+                       kfree(whiteout_ui->data);
+                       whiteout_ui->data_len = 0;
+                       iput(whiteout);
+                       return err;
+               }
+               inc_nlink(whiteout);
+               mark_inode_dirty(whiteout);
+               whiteout->i_state &= ~I_LINKABLE;
+               iput(whiteout);
+       }
+       err = ubifs_jnl_rename(c, old_dir, old_dentry, new_dir, new_dentry, whiteout,
                               sync);
        if (err)
                goto out_cancel;
  
-       unlock_3_inodes(old_dir, new_dir, new_inode);
+       unlock_4_inodes(old_dir, new_dir, new_inode, whiteout);
        ubifs_release_budget(c, &req);
  
        mutex_lock(&old_inode_ui->ui_mutex);
@@@ -1129,12 -1268,74 +1271,74 @@@ out_cancel
                                inc_nlink(old_dir);
                }
        }
-       unlock_3_inodes(old_dir, new_dir, new_inode);
+       if (whiteout) {
+               drop_nlink(whiteout);
+               iput(whiteout);
+       }
+       unlock_4_inodes(old_dir, new_dir, new_inode, whiteout);
        ubifs_release_budget(c, &ino_req);
        ubifs_release_budget(c, &req);
        return err;
  }
  
+ static int ubifs_xrename(struct inode *old_dir, struct dentry *old_dentry,
+                       struct inode *new_dir, struct dentry *new_dentry)
+ {
+       struct ubifs_info *c = old_dir->i_sb->s_fs_info;
+       struct ubifs_budget_req req = { .new_dent = 1, .mod_dent = 1,
+                               .dirtied_ino = 2 };
+       int sync = IS_DIRSYNC(old_dir) || IS_DIRSYNC(new_dir);
+       struct inode *fst_inode = d_inode(old_dentry);
+       struct inode *snd_inode = d_inode(new_dentry);
+       struct timespec time;
+       int err;
+       ubifs_assert(fst_inode && snd_inode);
+       lock_4_inodes(old_dir, new_dir, NULL, NULL);
+       time = ubifs_current_time(old_dir);
+       fst_inode->i_ctime = time;
+       snd_inode->i_ctime = time;
+       old_dir->i_mtime = old_dir->i_ctime = time;
+       new_dir->i_mtime = new_dir->i_ctime = time;
+       if (old_dir != new_dir) {
+               if (S_ISDIR(fst_inode->i_mode) && !S_ISDIR(snd_inode->i_mode)) {
+                       inc_nlink(new_dir);
+                       drop_nlink(old_dir);
+               }
+               else if (!S_ISDIR(fst_inode->i_mode) && S_ISDIR(snd_inode->i_mode)) {
+                       drop_nlink(new_dir);
+                       inc_nlink(old_dir);
+               }
+       }
+       err = ubifs_jnl_xrename(c, old_dir, old_dentry, new_dir, new_dentry,
+                               sync);
+       unlock_4_inodes(old_dir, new_dir, NULL, NULL);
+       ubifs_release_budget(c, &req);
+       return err;
+ }
+ static int ubifs_rename2(struct inode *old_dir, struct dentry *old_dentry,
+                       struct inode *new_dir, struct dentry *new_dentry,
+                       unsigned int flags)
+ {
+       if (flags & ~(RENAME_NOREPLACE | RENAME_WHITEOUT | RENAME_EXCHANGE))
+               return -EINVAL;
+       ubifs_assert(inode_is_locked(old_dir));
+       ubifs_assert(inode_is_locked(new_dir));
+       if (flags & RENAME_EXCHANGE)
+               return ubifs_xrename(old_dir, old_dentry, new_dir, new_dentry);
+       return ubifs_rename(old_dir, old_dentry, new_dir, new_dentry, flags);
+ }
  int ubifs_getattr(struct vfsmount *mnt, struct dentry *dentry,
                  struct kstat *stat)
  {
@@@ -1183,13 -1384,17 +1387,14 @@@ const struct inode_operations ubifs_dir
        .mkdir       = ubifs_mkdir,
        .rmdir       = ubifs_rmdir,
        .mknod       = ubifs_mknod,
-       .rename     = ubifs_rename,
 -      .rename2     = ubifs_rename2,
++      .rename      = ubifs_rename2,
        .setattr     = ubifs_setattr,
        .getattr     = ubifs_getattr,
 -      .setxattr    = generic_setxattr,
 -      .getxattr    = generic_getxattr,
        .listxattr   = ubifs_listxattr,
 -      .removexattr = generic_removexattr,
  #ifdef CONFIG_UBIFS_ATIME_SUPPORT
        .update_time = ubifs_update_time,
  #endif
+       .tmpfile     = ubifs_tmpfile,
  };
  
  const struct file_operations ubifs_dir_operations = {
diff --combined fs/ubifs/file.c
@@@ -1262,7 -1262,7 +1262,7 @@@ int ubifs_setattr(struct dentry *dentry
  
        dbg_gen("ino %lu, mode %#x, ia_valid %#x",
                inode->i_ino, inode->i_mode, attr->ia_valid);
 -      err = inode_change_ok(inode, attr);
 +      err = setattr_prepare(dentry, attr);
        if (err)
                return err;
  
@@@ -1397,7 -1397,7 +1397,7 @@@ int ubifs_update_time(struct inode *ino
  #endif
  
  /**
-  * update_ctime - update mtime and ctime of an inode.
+  * update_mctime - update mtime and ctime of an inode.
   * @inode: inode to update
   *
   * This function updates mtime and ctime of the inode if it is not equivalent to
@@@ -1621,7 -1621,10 +1621,7 @@@ const struct address_space_operations u
  const struct inode_operations ubifs_file_inode_operations = {
        .setattr     = ubifs_setattr,
        .getattr     = ubifs_getattr,
 -      .setxattr    = generic_setxattr,
 -      .getxattr    = generic_getxattr,
        .listxattr   = ubifs_listxattr,
 -      .removexattr = generic_removexattr,
  #ifdef CONFIG_UBIFS_ATIME_SUPPORT
        .update_time = ubifs_update_time,
  #endif
@@@ -1632,7 -1635,10 +1632,7 @@@ const struct inode_operations ubifs_sym
        .get_link    = simple_get_link,
        .setattr     = ubifs_setattr,
        .getattr     = ubifs_getattr,
 -      .setxattr    = generic_setxattr,
 -      .getxattr    = generic_getxattr,
        .listxattr   = ubifs_listxattr,
 -      .removexattr = generic_removexattr,
  #ifdef CONFIG_UBIFS_ATIME_SUPPORT
        .update_time = ubifs_update_time,
  #endif