Whamcloud - gitweb
fuse2fs: fix removing ea inodes when freeing a file
authorDarrick J. Wong <djwong@kernel.org>
Wed, 21 May 2025 22:41:26 +0000 (15:41 -0700)
committerTheodore Ts'o <tytso@mit.edu>
Fri, 23 May 2025 13:41:20 +0000 (09:41 -0400)
If the filesystem has ea_inode set, then each file that has xattrs might
have stored an xattr value in a separate inode.  These inodes also need
to be freed, so create a library function to do that, and call it from
the fuse2fs unlink method.  Seen by ext4/026.

Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Link: https://lore.kernel.org/r/174786677996.1383760.7408606469648643965.stgit@frogsfrogsfrogs
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
debian/libext2fs2t64.symbols
lib/ext2fs/ext2fs.h
lib/ext2fs/ext_attr.c
misc/fuse2fs.c

index 35af888..fc1e16f 100644 (file)
@@ -670,6 +670,7 @@ libext2fs.so.2 libext2fs2t64 #MINVER#
  ext2fs_xattr_get@Base 1.43
  ext2fs_xattr_inode_max_size@Base 1.43
  ext2fs_xattr_remove@Base 1.43
+ ext2fs_xattr_remove_all@Base 1.47.3
  ext2fs_xattr_set@Base 1.43
  ext2fs_xattrs_close@Base 1.43
  ext2fs_xattrs_count@Base 1.43
index 1527719..2661e10 100644 (file)
@@ -1406,6 +1406,7 @@ errcode_t ext2fs_xattr_set(struct ext2_xattr_handle *handle,
                           size_t value_len);
 errcode_t ext2fs_xattr_remove(struct ext2_xattr_handle *handle,
                              const char *key);
+errcode_t ext2fs_xattr_remove_all(struct ext2_xattr_handle *handle);
 errcode_t ext2fs_xattrs_open(ext2_filsys fs, ext2_ino_t ino,
                             struct ext2_xattr_handle **handle);
 errcode_t ext2fs_xattrs_close(struct ext2_xattr_handle **handle);
index 1b5f90d..7723d0f 100644 (file)
@@ -1355,6 +1355,7 @@ static errcode_t xattr_inode_dec_ref(ext2_filsys fs, ext2_ino_t ino)
                        goto out;
        }
 
+       inode.i_flags &= ~EXT4_EA_INODE_FL;
        ext2fs_inode_alloc_stats2(fs, ino, -1 /* inuse */, 0 /* is_dir */);
 
 write_out:
@@ -1725,6 +1726,24 @@ errcode_t ext2fs_xattr_remove(struct ext2_xattr_handle *handle,
        return 0;
 }
 
+errcode_t ext2fs_xattr_remove_all(struct ext2_xattr_handle *handle)
+{
+       struct ext2_xattr *x;
+       struct ext2_xattr *end = handle->attrs + handle->count;
+
+       EXT2_CHECK_MAGIC(handle, EXT2_ET_MAGIC_EA_HANDLE);
+       for (x = handle->attrs; x < end; x++) {
+               ext2fs_free_mem(&x->name);
+               ext2fs_free_mem(&x->value);
+               if (x->ea_ino)
+                       xattr_inode_dec_ref(handle->fs, x->ea_ino);
+       }
+
+       handle->ibody_count = 0;
+       handle->count = 0;
+       return ext2fs_xattrs_write(handle);
+}
+
 errcode_t ext2fs_xattrs_open(ext2_filsys fs, ext2_ino_t ino,
                             struct ext2_xattr_handle **handle)
 {
index f2d613d..2d4a108 100644 (file)
@@ -1224,6 +1224,40 @@ static int unlink_file_by_name(struct fuse2fs *ff, const char *path)
        return update_mtime(fs, dir, NULL);
 }
 
+static errcode_t remove_ea_inodes(struct fuse2fs *ff, ext2_ino_t ino,
+                                 struct ext2_inode_large *inode)
+{
+       ext2_filsys fs = ff->fs;
+       struct ext2_xattr_handle *h;
+       errcode_t err;
+
+       /*
+        * The xattr handle maintains its own private copy of the inode, so
+        * write ours to disk so that we can read it.
+        */
+       err = fuse2fs_write_inode(fs, ino, inode);
+       if (err)
+               return err;
+
+       err = ext2fs_xattrs_open(fs, ino, &h);
+       if (err)
+               return err;
+
+       err = ext2fs_xattrs_read(h);
+       if (err)
+               goto out_close;
+
+       err = ext2fs_xattr_remove_all(h);
+       if (err)
+               goto out_close;
+
+out_close:
+       ext2fs_xattrs_close(&h);
+
+       /* Now read the inode back in. */
+       return fuse2fs_read_inode(fs, ino, inode);
+}
+
 static int remove_inode(struct fuse2fs *ff, ext2_ino_t ino)
 {
        ext2_filsys fs = ff->fs;
@@ -1259,6 +1293,12 @@ static int remove_inode(struct fuse2fs *ff, ext2_ino_t ino)
        if (inode.i_links_count)
                goto write_out;
 
+       if (ext2fs_has_feature_ea_inode(fs->super)) {
+               err = remove_ea_inodes(ff, ino, &inode);
+               if (err)
+                       goto write_out;
+       }
+
        /* Nobody holds this file; free its blocks! */
        err = ext2fs_free_ext_attr(fs, ino, &inode);
        if (err)