]> Git Repo - linux.git/commitdiff
Merge tag 'for-6.6-rc2-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/kdave...
authorLinus Torvalds <[email protected]>
Wed, 20 Sep 2023 18:03:45 +0000 (11:03 -0700)
committerLinus Torvalds <[email protected]>
Wed, 20 Sep 2023 18:03:45 +0000 (11:03 -0700)
Pull btrfs fixes from David Sterba:
 "A few more followup fixes to the directory listing.

  People have noticed different behaviour compared to other filesystems
  after changes in 6.5. This is now unified to more "logical" and
  expected behaviour while still within POSIX. And a few more fixes for
  stable.

   - change behaviour of readdir()/rewinddir() when new directory
     entries are created after opendir(), properly tracking the last
     entry

   - fix race in readdir when multiple threads can set the last entry
     index for a directory

  Additionally:

   - use exclusive lock when direct io might need to drop privs and call
     notify_change()

   - don't clear uptodate bit on page after an error, this may lead to a
     deadlock in subpage mode

   - fix waiting pattern when multiple readers block on Merkle tree
     data, switch to folios"

* tag 'for-6.6-rc2-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/kdave/linux:
  btrfs: fix race between reading a directory and adding entries to it
  btrfs: refresh dir last index during a rewinddir(3) call
  btrfs: set last dir index to the current last index when opening dir
  btrfs: don't clear uptodate on write errors
  btrfs: file_remove_privs needs an exclusive lock in direct io write
  btrfs: convert btrfs_read_merkle_tree_page() to use a folio

1  2 
fs/btrfs/file.c
fs/btrfs/inode.c

diff --combined fs/btrfs/file.c
index ca46a529d56b1999cff4a785661db40dca049e47,e8726a83b6490851a2e442ecac362161d8fd4eb9..87a5c128f7169854e5502f0ae01eeb7fceae0f46
@@@ -876,9 -876,9 +876,9 @@@ static int prepare_uptodate_page(struc
        return 0;
  }
  
 -static unsigned int get_prepare_fgp_flags(bool nowait)
 +static fgf_t get_prepare_fgp_flags(bool nowait)
  {
 -      unsigned int fgp_flags = FGP_LOCK | FGP_ACCESSED | FGP_CREAT;
 +      fgf_t fgp_flags = FGP_LOCK | FGP_ACCESSED | FGP_CREAT;
  
        if (nowait)
                fgp_flags |= FGP_NOWAIT;
@@@ -910,7 -910,7 +910,7 @@@ static noinline int prepare_pages(struc
        int i;
        unsigned long index = pos >> PAGE_SHIFT;
        gfp_t mask = get_prepare_gfp_flags(inode, nowait);
 -      unsigned int fgp_flags = get_prepare_fgp_flags(nowait);
 +      fgf_t fgp_flags = get_prepare_fgp_flags(nowait);
        int err = 0;
        int faili;
  
@@@ -1106,6 -1106,24 +1106,6 @@@ void btrfs_check_nocow_unlock(struct bt
        btrfs_drew_write_unlock(&inode->root->snapshot_lock);
  }
  
 -static void update_time_for_write(struct inode *inode)
 -{
 -      struct timespec64 now;
 -
 -      if (IS_NOCMTIME(inode))
 -              return;
 -
 -      now = current_time(inode);
 -      if (!timespec64_equal(&inode->i_mtime, &now))
 -              inode->i_mtime = now;
 -
 -      if (!timespec64_equal(&inode->i_ctime, &now))
 -              inode->i_ctime = now;
 -
 -      if (IS_I_VERSION(inode))
 -              inode_inc_iversion(inode);
 -}
 -
  static int btrfs_write_check(struct kiocb *iocb, struct iov_iter *from,
                             size_t count)
  {
         * need to start yet another transaction to update the inode as we will
         * update the inode when we finish writing whatever data we write.
         */
 -      update_time_for_write(inode);
 +      if (!IS_NOCMTIME(inode)) {
 +              inode->i_mtime = inode_set_ctime_current(inode);
 +              inode_inc_iversion(inode);
 +      }
  
        start_pos = round_down(pos, fs_info->sectorsize);
        oldsize = i_size_read(inode);
@@@ -1451,8 -1466,13 +1451,13 @@@ static ssize_t btrfs_direct_write(struc
        if (iocb->ki_flags & IOCB_NOWAIT)
                ilock_flags |= BTRFS_ILOCK_TRY;
  
-       /* If the write DIO is within EOF, use a shared lock */
-       if (iocb->ki_pos + iov_iter_count(from) <= i_size_read(inode))
+       /*
+        * If the write DIO is within EOF, use a shared lock and also only if
+        * security bits will likely not be dropped by file_remove_privs() called
+        * from btrfs_write_check(). Either will need to be rechecked after the
+        * lock was acquired.
+        */
+       if (iocb->ki_pos + iov_iter_count(from) <= i_size_read(inode) && IS_NOSEC(inode))
                ilock_flags |= BTRFS_ILOCK_SHARED;
  
  relock:
        if (err < 0)
                return err;
  
+       /* Shared lock cannot be used with security bits set. */
+       if ((ilock_flags & BTRFS_ILOCK_SHARED) && !IS_NOSEC(inode)) {
+               btrfs_inode_unlock(BTRFS_I(inode), ilock_flags);
+               ilock_flags &= ~BTRFS_ILOCK_SHARED;
+               goto relock;
+       }
        err = generic_write_checks(iocb, from);
        if (err <= 0) {
                btrfs_inode_unlock(BTRFS_I(inode), ilock_flags);
@@@ -2444,8 -2471,10 +2456,8 @@@ int btrfs_replace_file_extents(struct b
                 */
                inode_inc_iversion(&inode->vfs_inode);
  
 -              if (!extent_info || extent_info->update_times) {
 -                      inode->vfs_inode.i_mtime = current_time(&inode->vfs_inode);
 -                      inode->vfs_inode.i_ctime = inode->vfs_inode.i_mtime;
 -              }
 +              if (!extent_info || extent_info->update_times)
 +                      inode->vfs_inode.i_mtime = inode_set_ctime_current(&inode->vfs_inode);
  
                ret = btrfs_update_inode(trans, root, inode);
                if (ret)
@@@ -2686,7 -2715,8 +2698,7 @@@ static int btrfs_punch_hole(struct fil
  
        ASSERT(trans != NULL);
        inode_inc_iversion(inode);
 -      inode->i_mtime = current_time(inode);
 -      inode->i_ctime = inode->i_mtime;
 +      inode->i_mtime = inode_set_ctime_current(inode);
        ret = btrfs_update_inode(trans, root, BTRFS_I(inode));
        updated_inode = true;
        btrfs_end_transaction(trans);
@@@ -2703,10 -2733,11 +2715,10 @@@ out_only_mutex
                 * for detecting, at fsync time, if the inode isn't yet in the
                 * log tree or it's there but not up to date.
                 */
 -              struct timespec64 now = current_time(inode);
 +              struct timespec64 now = inode_set_ctime_current(inode);
  
                inode_inc_iversion(inode);
                inode->i_mtime = now;
 -              inode->i_ctime = now;
                trans = btrfs_start_transaction(root, 1);
                if (IS_ERR(trans)) {
                        ret = PTR_ERR(trans);
@@@ -2777,7 -2808,7 +2789,7 @@@ static int btrfs_fallocate_update_isize
        if (IS_ERR(trans))
                return PTR_ERR(trans);
  
 -      inode->i_ctime = current_time(inode);
 +      inode_set_ctime_current(inode);
        i_size_write(inode, end);
        btrfs_inode_safe_disk_i_size_write(BTRFS_I(inode), 0);
        ret = btrfs_update_inode(trans, root, BTRFS_I(inode));
diff --combined fs/btrfs/inode.c
index f09fbdc43f0f5b15c71d67564cfb264764ff1169,5e1b15e69ed6e9160c6086e55e1066f0b61cba27..7814b9d654ce1234465bf8ad37a7890e06fd88aa
@@@ -1085,9 -1085,6 +1085,6 @@@ static void submit_uncompressed_range(s
                        btrfs_mark_ordered_io_finished(inode, locked_page,
                                                       page_start, PAGE_SIZE,
                                                       !ret);
-                       btrfs_page_clear_uptodate(inode->root->fs_info,
-                                                 locked_page, page_start,
-                                                 PAGE_SIZE);
                        mapping_set_error(locked_page->mapping, ret);
                        unlock_page(locked_page);
                }
@@@ -2791,7 -2788,6 +2788,6 @@@ out_page
                mapping_set_error(page->mapping, ret);
                btrfs_mark_ordered_io_finished(inode, page, page_start,
                                               PAGE_SIZE, !ret);
-               btrfs_page_clear_uptodate(fs_info, page, page_start, PAGE_SIZE);
                clear_page_dirty_for_io(page);
        }
        btrfs_page_clear_checked(fs_info, page, page_start, PAGE_SIZE);
@@@ -3764,8 -3760,8 +3760,8 @@@ static int btrfs_read_locked_inode(stru
        inode->i_mtime.tv_sec = btrfs_timespec_sec(leaf, &inode_item->mtime);
        inode->i_mtime.tv_nsec = btrfs_timespec_nsec(leaf, &inode_item->mtime);
  
 -      inode->i_ctime.tv_sec = btrfs_timespec_sec(leaf, &inode_item->ctime);
 -      inode->i_ctime.tv_nsec = btrfs_timespec_nsec(leaf, &inode_item->ctime);
 +      inode_set_ctime(inode, btrfs_timespec_sec(leaf, &inode_item->ctime),
 +                      btrfs_timespec_nsec(leaf, &inode_item->ctime));
  
        BTRFS_I(inode)->i_otime.tv_sec =
                btrfs_timespec_sec(leaf, &inode_item->otime);
@@@ -3936,9 -3932,9 +3932,9 @@@ static void fill_inode_item(struct btrf
                                      inode->i_mtime.tv_nsec);
  
        btrfs_set_token_timespec_sec(&token, &item->ctime,
 -                                   inode->i_ctime.tv_sec);
 +                                   inode_get_ctime(inode).tv_sec);
        btrfs_set_token_timespec_nsec(&token, &item->ctime,
 -                                    inode->i_ctime.tv_nsec);
 +                                    inode_get_ctime(inode).tv_nsec);
  
        btrfs_set_token_timespec_sec(&token, &item->otime,
                                     BTRFS_I(inode)->i_otime.tv_sec);
@@@ -4136,8 -4132,9 +4132,8 @@@ err
        btrfs_i_size_write(dir, dir->vfs_inode.i_size - name->len * 2);
        inode_inc_iversion(&inode->vfs_inode);
        inode_inc_iversion(&dir->vfs_inode);
 -      inode->vfs_inode.i_ctime = current_time(&inode->vfs_inode);
 -      dir->vfs_inode.i_mtime = inode->vfs_inode.i_ctime;
 -      dir->vfs_inode.i_ctime = inode->vfs_inode.i_ctime;
 +      inode_set_ctime_current(&inode->vfs_inode);
 +      dir->vfs_inode.i_mtime = inode_set_ctime_current(&dir->vfs_inode);
        ret = btrfs_update_inode(trans, root, dir);
  out:
        return ret;
@@@ -4310,7 -4307,8 +4306,7 @@@ static int btrfs_unlink_subvol(struct b
  
        btrfs_i_size_write(dir, dir->vfs_inode.i_size - fname.disk_name.len * 2);
        inode_inc_iversion(&dir->vfs_inode);
 -      dir->vfs_inode.i_mtime = current_time(&dir->vfs_inode);
 -      dir->vfs_inode.i_ctime = dir->vfs_inode.i_mtime;
 +      dir->vfs_inode.i_mtime = inode_set_ctime_current(&dir->vfs_inode);
        ret = btrfs_update_inode_fallback(trans, root, dir);
        if (ret)
                btrfs_abort_transaction(trans, ret);
@@@ -4960,7 -4958,8 +4956,7 @@@ static int btrfs_setsize(struct inode *
        if (newsize != oldsize) {
                inode_inc_iversion(inode);
                if (!(mask & (ATTR_CTIME | ATTR_MTIME))) {
 -                      inode->i_mtime = current_time(inode);
 -                      inode->i_ctime = inode->i_mtime;
 +                      inode->i_mtime = inode_set_ctime_current(inode);
                }
        }
  
@@@ -5604,8 -5603,9 +5600,8 @@@ static struct inode *new_simple_dir(str
        inode->i_opflags &= ~IOP_XATTR;
        inode->i_fop = &simple_dir_operations;
        inode->i_mode = S_IFDIR | S_IRUGO | S_IWUSR | S_IXUGO;
 -      inode->i_mtime = current_time(inode);
 +      inode->i_mtime = inode_set_ctime_current(inode);
        inode->i_atime = dir->i_atime;
 -      inode->i_ctime = dir->i_ctime;
        BTRFS_I(inode)->i_otime = inode->i_mtime;
        inode->i_uid = dir->i_uid;
        inode->i_gid = dir->i_gid;
  
  static int btrfs_get_dir_last_index(struct btrfs_inode *dir, u64 *index)
  {
-       if (dir->index_cnt == (u64)-1) {
-               int ret;
+       int ret = 0;
  
+       btrfs_inode_lock(dir, 0);
+       if (dir->index_cnt == (u64)-1) {
                ret = btrfs_inode_delayed_dir_index_count(dir);
                if (ret) {
                        ret = btrfs_set_inode_index_count(dir);
                        if (ret)
-                               return ret;
+                               goto out;
                }
        }
  
-       *index = dir->index_cnt;
+       /* index_cnt is the index number of next new entry, so decrement it. */
+       *index = dir->index_cnt - 1;
+ out:
+       btrfs_inode_unlock(dir, 0);
  
-       return 0;
+       return ret;
  }
  
  /*
@@@ -5817,6 -5821,19 +5817,19 @@@ static int btrfs_opendir(struct inode *
        return 0;
  }
  
+ static loff_t btrfs_dir_llseek(struct file *file, loff_t offset, int whence)
+ {
+       struct btrfs_file_private *private = file->private_data;
+       int ret;
+       ret = btrfs_get_dir_last_index(BTRFS_I(file_inode(file)),
+                                      &private->last_index);
+       if (ret)
+               return ret;
+       return generic_file_llseek(file, offset, whence);
+ }
  struct dir_entry {
        u64 ino;
        u64 offset;
@@@ -6008,7 -6025,8 +6021,7 @@@ static int btrfs_dirty_inode(struct btr
   * This is a copy of file_update_time.  We need this so we can return error on
   * ENOSPC for updating the inode in the case of file write and mmap writes.
   */
 -static int btrfs_update_time(struct inode *inode, struct timespec64 *now,
 -                           int flags)
 +static int btrfs_update_time(struct inode *inode, int flags)
  {
        struct btrfs_root *root = BTRFS_I(inode)->root;
        bool dirty = flags & ~S_VERSION;
        if (btrfs_root_readonly(root))
                return -EROFS;
  
 -      if (flags & S_VERSION)
 -              dirty |= inode_maybe_inc_iversion(inode, dirty);
 -      if (flags & S_CTIME)
 -              inode->i_ctime = *now;
 -      if (flags & S_MTIME)
 -              inode->i_mtime = *now;
 -      if (flags & S_ATIME)
 -              inode->i_atime = *now;
 +      dirty = inode_update_timestamps(inode, flags);
        return dirty ? btrfs_dirty_inode(BTRFS_I(inode)) : 0;
  }
  
@@@ -6264,8 -6289,9 +6277,8 @@@ int btrfs_create_new_inode(struct btrfs
                goto discard;
        }
  
 -      inode->i_mtime = current_time(inode);
 +      inode->i_mtime = inode_set_ctime_current(inode);
        inode->i_atime = inode->i_mtime;
 -      inode->i_ctime = inode->i_mtime;
        BTRFS_I(inode)->i_otime = inode->i_mtime;
  
        /*
@@@ -6430,10 -6456,12 +6443,10 @@@ int btrfs_add_link(struct btrfs_trans_h
         * log replay procedure is responsible for setting them to their correct
         * values (the ones it had when the fsync was done).
         */
 -      if (!test_bit(BTRFS_FS_LOG_RECOVERING, &root->fs_info->flags)) {
 -              struct timespec64 now = current_time(&parent_inode->vfs_inode);
 +      if (!test_bit(BTRFS_FS_LOG_RECOVERING, &root->fs_info->flags))
 +              parent_inode->vfs_inode.i_mtime =
 +                      inode_set_ctime_current(&parent_inode->vfs_inode);
  
 -              parent_inode->vfs_inode.i_mtime = now;
 -              parent_inode->vfs_inode.i_ctime = now;
 -      }
        ret = btrfs_update_inode(trans, root, parent_inode);
        if (ret)
                btrfs_abort_transaction(trans, ret);
@@@ -6573,7 -6601,7 +6586,7 @@@ static int btrfs_link(struct dentry *ol
        BTRFS_I(inode)->dir_index = 0ULL;
        inc_nlink(inode);
        inode_inc_iversion(inode);
 -      inode->i_ctime = current_time(inode);
 +      inode_set_ctime_current(inode);
        ihold(inode);
        set_bit(BTRFS_INODE_COPY_EVERYTHING, &BTRFS_I(inode)->runtime_flags);
  
@@@ -8639,7 -8667,7 +8652,7 @@@ static int btrfs_getattr(struct mnt_idm
                                  STATX_ATTR_IMMUTABLE |
                                  STATX_ATTR_NODUMP);
  
 -      generic_fillattr(idmap, inode, stat);
 +      generic_fillattr(idmap, request_mask, inode, stat);
        stat->dev = BTRFS_I(inode)->root->anon_dev;
  
        spin_lock(&BTRFS_I(inode)->lock);
@@@ -8663,6 -8691,7 +8676,6 @@@ static int btrfs_rename_exchange(struc
        struct btrfs_root *dest = BTRFS_I(new_dir)->root;
        struct inode *new_inode = new_dentry->d_inode;
        struct inode *old_inode = old_dentry->d_inode;
 -      struct timespec64 ctime = current_time(old_inode);
        struct btrfs_rename_ctx old_rename_ctx;
        struct btrfs_rename_ctx new_rename_ctx;
        u64 old_ino = btrfs_ino(BTRFS_I(old_inode));
        inode_inc_iversion(new_dir);
        inode_inc_iversion(old_inode);
        inode_inc_iversion(new_inode);
 -      old_dir->i_mtime = ctime;
 -      old_dir->i_ctime = ctime;
 -      new_dir->i_mtime = ctime;
 -      new_dir->i_ctime = ctime;
 -      old_inode->i_ctime = ctime;
 -      new_inode->i_ctime = ctime;
 +      simple_rename_timestamp(old_dir, old_dentry, new_dir, new_dentry);
  
        if (old_dentry->d_parent != new_dentry->d_parent) {
                btrfs_record_unlink_dir(trans, BTRFS_I(old_dir),
@@@ -9057,7 -9091,11 +9070,7 @@@ static int btrfs_rename(struct mnt_idma
        inode_inc_iversion(old_dir);
        inode_inc_iversion(new_dir);
        inode_inc_iversion(old_inode);
 -      old_dir->i_mtime = current_time(old_dir);
 -      old_dir->i_ctime = old_dir->i_mtime;
 -      new_dir->i_mtime = old_dir->i_mtime;
 -      new_dir->i_ctime = old_dir->i_mtime;
 -      old_inode->i_ctime = old_dir->i_mtime;
 +      simple_rename_timestamp(old_dir, old_dentry, new_dir, new_dentry);
  
        if (old_dentry->d_parent != new_dentry->d_parent)
                btrfs_record_unlink_dir(trans, BTRFS_I(old_dir),
  
        if (new_inode) {
                inode_inc_iversion(new_inode);
 -              new_inode->i_ctime = current_time(new_inode);
                if (unlikely(btrfs_ino(BTRFS_I(new_inode)) ==
                             BTRFS_EMPTY_SUBVOL_DIR_OBJECTID)) {
                        ret = btrfs_unlink_subvol(trans, BTRFS_I(new_dir), new_dentry);
@@@ -9613,7 -9652,7 +9626,7 @@@ next
                *alloc_hint = ins.objectid + ins.offset;
  
                inode_inc_iversion(inode);
 -              inode->i_ctime = current_time(inode);
 +              inode_set_ctime_current(inode);
                BTRFS_I(inode)->flags |= BTRFS_INODE_PREALLOC;
                if (!(mode & FALLOC_FL_KEEP_SIZE) &&
                    (actual_len > inode->i_size) &&
@@@ -10868,7 -10907,7 +10881,7 @@@ static const struct inode_operations bt
  };
  
  static const struct file_operations btrfs_dir_file_operations = {
-       .llseek         = generic_file_llseek,
+       .llseek         = btrfs_dir_llseek,
        .read           = generic_read_dir,
        .iterate_shared = btrfs_real_readdir,
        .open           = btrfs_opendir,
This page took 0.082955 seconds and 4 git commands to generate.