* [PATCH RESEND 1/4] e2fsck: fix orphaned extent files handling
2026-06-19 15:17 [PATCH RESEND 0/4] e2fsck: Fix orphan inodes processing Etienne AUJAMES
@ 2026-06-19 15:24 ` Etienne AUJAMES
2026-06-19 15:32 ` [PATCH RESEND 2/4] libext2fs: add quota to libext2fs Etienne AUJAMES
` (2 subsequent siblings)
3 siblings, 0 replies; 5+ messages in thread
From: Etienne AUJAMES @ 2026-06-19 15:24 UTC (permalink / raw)
To: linux-ext4, Theodore Ts'o; +Cc: Andreas Dilger, Li Dongyang
release_inode_blocks() does not handle corectly multi-levels extent
files: it does not count the non-leaf blocks directly released by
ext2fs_block_iterate3().
This patch relies on ext2fs_get_stat_i_blocks() count for quota update
and ext2fs_free_blocks_count() to count number of blocks released by
release_inode_blocks().
Add regression test: f_orphan_truncate_extents_inode
Signed-off-by: Etienne AUJAMES <eaujames@ddn.com>
Change-Id: Ib0c3aaaa685e7bcfae896617cda03005d19539ff
Lustre-bug-id: https://jira.whamcloud.com/browse/LU-20049
---
e2fsck/super.c | 244 +++++++++---------
.../f_orphan_truncate_extents_inode/expect.1 | 3 +
.../f_orphan_truncate_extents_inode/expect.2 | 7 +
.../f_orphan_truncate_extents_inode/image.gz | Bin 0 -> 2854 bytes
tests/f_orphan_truncate_extents_inode/name | 1 +
tests/f_orphan_truncate_extents_inode/script | 3 +
6 files changed, 133 insertions(+), 125 deletions(-)
create mode 100644 tests/f_orphan_truncate_extents_inode/expect.1
create mode 100644 tests/f_orphan_truncate_extents_inode/expect.2
create mode 100644 tests/f_orphan_truncate_extents_inode/image.gz
create mode 100644 tests/f_orphan_truncate_extents_inode/name
create mode 100644 tests/f_orphan_truncate_extents_inode/script
diff --git a/e2fsck/super.c b/e2fsck/super.c
index cfc0919a2..c2ccefd54 100644
--- a/e2fsck/super.c
+++ b/e2fsck/super.c
@@ -62,21 +62,14 @@ static int check_super_value64(e2fsck_t ctx, const char *descr,
return 1;
}
-/*
- * helper function to release an inode
- */
struct process_block_struct {
- e2fsck_t ctx;
- char *buf;
+ e2fsck_t ctx;
+ char *buf;
struct problem_context *pctx;
- int truncating;
- int truncate_offset;
e2_blkcnt_t truncate_block;
- int truncated_blocks;
- int abort;
+ e2_blkcnt_t truncated_blocks;
errcode_t errcode;
blk64_t last_cluster;
- struct ext2_inode_large *inode;
};
static int release_inode_block(ext2_filsys fs,
@@ -91,7 +84,6 @@ static int release_inode_block(ext2_filsys fs,
struct problem_context *pctx;
blk64_t blk = *block_nr;
blk64_t cluster = EXT2FS_B2C(fs, *block_nr);
- int retval = 0;
pb = (struct process_block_struct *) priv_data;
ctx = pb->ctx;
@@ -111,155 +103,157 @@ static int release_inode_block(ext2_filsys fs,
if ((blk < fs->super->s_first_data_block) ||
(blk >= ext2fs_blocks_count(fs->super))) {
fix_problem(ctx, PR_0_ORPHAN_ILLEGAL_BLOCK_NUM, pctx);
- return_abort:
- pb->abort = 1;
+ pb->errcode = EXT2_ET_BAD_BLOCK_NUM;
return BLOCK_ABORT;
}
if (!ext2fs_test_block_bitmap2(fs->block_map, blk)) {
fix_problem(ctx, PR_0_ORPHAN_ALREADY_CLEARED_BLOCK, pctx);
- goto return_abort;
+ pb->errcode = EXT2_ET_BAD_BLOCK_NUM;
+ return BLOCK_ABORT;
}
/*
- * If we are deleting an orphan, then we leave the fields alone.
- * If we are truncating an orphan, then update the inode fields
- * and clean up any partial block data.
+ * We don't remove direct blocks until we've reached
+ * the truncation block.
*/
- if (pb->truncating) {
- /*
- * We only remove indirect blocks if they are
- * completely empty.
- */
- if (blockcnt < 0) {
- int i, limit;
- blk_t *bp;
-
- pb->errcode = io_channel_read_blk64(fs->io, blk, 1,
- pb->buf);
- if (pb->errcode)
- goto return_abort;
-
- limit = fs->blocksize >> 2;
- for (i = 0, bp = (blk_t *) pb->buf;
- i < limit; i++, bp++)
- if (*bp)
- return 0;
- }
- /*
- * We don't remove direct blocks until we've reached
- * the truncation block.
- */
- if (blockcnt >= 0 && blockcnt < pb->truncate_block)
- return 0;
- /*
- * If part of the last block needs truncating, we do
- * it here.
- */
- if ((blockcnt == pb->truncate_block) && pb->truncate_offset) {
- pb->errcode = io_channel_read_blk64(fs->io, blk, 1,
- pb->buf);
- if (pb->errcode)
- goto return_abort;
- memset(pb->buf + pb->truncate_offset, 0,
- fs->blocksize - pb->truncate_offset);
- pb->errcode = io_channel_write_blk64(fs->io, blk, 1,
- pb->buf);
- if (pb->errcode)
- goto return_abort;
- }
- pb->truncated_blocks++;
- *block_nr = 0;
- retval |= BLOCK_CHANGED;
+ if (blockcnt >= 0 && blockcnt < pb->truncate_block)
+ return 0;
+
+ /*
+ * We only remove indirect blocks if they are
+ * completely empty.
+ */
+ if (blockcnt < 0) {
+ int i, limit;
+ blk_t *bp;
+
+ pb->errcode = io_channel_read_blk64(fs->io, blk, 1,
+ pb->buf);
+ if (pb->errcode)
+ return BLOCK_ABORT;
+
+ limit = fs->blocksize >> 2;
+ for (i = 0, bp = (blk_t *) pb->buf;
+ i < limit; i++, bp++)
+ if (*bp)
+ return 0;
}
- if (ctx->qctx)
- quota_data_sub(ctx->qctx, pb->inode, 0, ctx->fs->blocksize);
ext2fs_block_alloc_stats2(fs, blk, -1);
- ctx->free_blocks++;
- return retval;
+ pb->truncated_blocks++;
+ *block_nr = 0;
+
+ return BLOCK_CHANGED;
}
-/*
- * This function releases an inode. Returns 1 if an inconsistency was
- * found. If the inode has a link count, then it is being truncated and
- * not deleted.
- */
-static int release_inode_blocks(e2fsck_t ctx, ext2_ino_t ino,
- struct ext2_inode_large *inode, char *block_buf,
- struct problem_context *pctx)
+static errcode_t truncate_inode_blocks(e2fsck_t ctx, ext2_ino_t ino,
+ struct ext2_inode_large *inode,
+ char *block_buf,
+ struct problem_context *pctx)
{
- struct process_block_struct pb;
- ext2_filsys fs = ctx->fs;
- blk64_t blk;
- errcode_t retval;
- __u32 count;
+ ext2_filsys fs = ctx->fs;
+ struct process_block_struct pb = { 0 };
+ e2_blkcnt_t truncate_block = 0;
+ __u32 truncate_offset = 0;
+ blk64_t blk;
+ int ret_flags;
+ errcode_t retval = 0;
if (!ext2fs_inode_has_valid_blocks2(fs, EXT2_INODE(inode)))
- goto release_acl;
+ return 0;
- pb.buf = block_buf + 3 * ctx->fs->blocksize;
- pb.ctx = ctx;
- pb.abort = 0;
- pb.errcode = 0;
- pb.pctx = pctx;
- pb.last_cluster = 0;
- pb.inode = inode;
if (inode->i_links_count) {
- pb.truncating = 1;
- pb.truncate_block = (e2_blkcnt_t)
+ truncate_offset = inode->i_size % fs->blocksize;
+ truncate_block = (e2_blkcnt_t)
((EXT2_I_SIZE(inode) + fs->blocksize - 1) /
fs->blocksize);
- pb.truncate_offset = inode->i_size % fs->blocksize;
- } else {
- pb.truncating = 0;
- pb.truncate_block = 0;
- pb.truncate_offset = 0;
}
- pb.truncated_blocks = 0;
+
+ pb.buf = block_buf;
+ pb.ctx = ctx;
+ pb.pctx = pctx;
+ pb.truncate_block = truncate_block;
retval = ext2fs_block_iterate3(fs, ino, BLOCK_FLAG_DEPTH_TRAVERSE,
block_buf, release_inode_block, &pb);
if (retval) {
com_err("release_inode_blocks", retval,
_("while calling ext2fs_block_iterate for inode %u"),
ino);
- return 1;
+ return retval;
}
- if (pb.abort)
- return 1;
+ if (pb.errcode)
+ return pb.errcode;
/* Refresh the inode since ext2fs_block_iterate may have changed it */
e2fsck_read_inode_full(ctx, ino, EXT2_INODE(inode), sizeof(*inode),
"release_inode_blocks");
- if (pb.truncated_blocks)
- ext2fs_iblk_sub_blocks(fs, EXT2_INODE(inode),
- pb.truncated_blocks);
-release_acl:
- blk = ext2fs_file_acl_block(fs, EXT2_INODE(inode));
- if (blk) {
- retval = ext2fs_adjust_ea_refcount3(fs, blk, block_buf, -1,
- &count, ino);
- if (retval == EXT2_ET_BAD_EA_BLOCK_NUM) {
- retval = 0;
- count = 1;
- }
- if (retval) {
- com_err("release_inode_blocks", retval,
- _("while calling ext2fs_adjust_ea_refcount2 for inode %u"),
- ino);
- return 1;
- }
- if (count == 0) {
- if (ctx->qctx)
- quota_data_sub(ctx->qctx, inode, 0,
- ctx->fs->blocksize);
- ext2fs_block_alloc_stats2(fs, blk, -1);
- ctx->free_blocks++;
- }
- ext2fs_file_acl_block_set(fs, EXT2_INODE(inode), 0);
+ ext2fs_iblk_sub_blocks(fs, EXT2_INODE(inode), pb.truncated_blocks);
+ if (!truncate_offset)
+ return 0;
+
+ /* Is there an initialized block at the end? */
+ retval = ext2fs_bmap2(fs, ino, NULL, NULL, 0,
+ truncate_block, &ret_flags, &blk);
+ if (retval)
+ return retval;
+ if ((blk == 0) || (ret_flags & BMAP_RET_UNINIT))
+ return 0;
+
+ retval = io_channel_read_blk64(fs->io, blk, 1, block_buf);
+ if (retval)
+ return retval;
+
+ memset(block_buf + truncate_offset, 0, fs->blocksize - truncate_offset);
+ retval = io_channel_write_blk64(fs->io, blk, 1, block_buf);
+
+ return retval;
+}
+
+/*
+ * This function releases an inode. Returns 1 if an inconsistency was
+ * found. If the inode has a link count, then it is being truncated and
+ * not deleted.
+ */
+static int release_inode_blocks(e2fsck_t ctx, ext2_ino_t ino,
+ struct ext2_inode_large *inode, char *block_buf,
+ struct problem_context *pctx)
+{
+ ext2_filsys fs = ctx->fs;
+ blk64_t free_blks, ino_blks;
+ char *buf;
+ errcode_t err;
+ int rc = 0;
+
+ free_blks = ext2fs_free_blocks_count(fs->super);
+ ino_blks = ext2fs_get_stat_i_blocks(fs, EXT2_INODE(inode));
+ buf = block_buf + 3 * ctx->fs->blocksize;
+ if (truncate_inode_blocks(ctx, ino, inode, buf, pctx)) {
+ rc = 1;
+ goto update_counts;
}
- return 0;
+ if (inode->i_links_count)
+ goto update_counts;
+
+ err = ext2fs_free_ext_attr(fs, ino, inode);
+ if (err) {
+ com_err(__func__, err,
+ _("while calling ext2fs_free_ext_attr for inode %u"),
+ ino);
+ rc = 1;
+ goto update_counts;
+ }
+
+ rc = 0;
+
+update_counts:
+ ctx->free_blocks += ext2fs_free_blocks_count(fs->super) - free_blks;
+ ino_blks -= ext2fs_get_stat_i_blocks(fs, EXT2_INODE(inode));
+ if (ctx->qctx)
+ quota_data_sub(ctx->qctx, inode, 0, ino_blks << 9);
+
+ return rc;
}
/* Load all quota data in preparation for orphan clearing. */
diff --git a/tests/f_orphan_truncate_extents_inode/expect.1 b/tests/f_orphan_truncate_extents_inode/expect.1
new file mode 100644
index 000000000..b24aae7ad
--- /dev/null
+++ b/tests/f_orphan_truncate_extents_inode/expect.1
@@ -0,0 +1,3 @@
+test_filesys: Truncating orphaned inode 12 (uid=0, gid=0, mode=0100644, size=4096)
+test_filesys: clean, 12/128 files, 75/1024 blocks
+Exit status is 0
diff --git a/tests/f_orphan_truncate_extents_inode/expect.2 b/tests/f_orphan_truncate_extents_inode/expect.2
new file mode 100644
index 000000000..7edff9bce
--- /dev/null
+++ b/tests/f_orphan_truncate_extents_inode/expect.2
@@ -0,0 +1,7 @@
+Pass 1: Checking inodes, blocks, and sizes
+Pass 2: Checking directory structure
+Pass 3: Checking directory connectivity
+Pass 4: Checking reference counts
+Pass 5: Checking group summary information
+test_filesys: 12/128 files (16.7% non-contiguous), 75/1024 blocks
+Exit status is 0
diff --git a/tests/f_orphan_truncate_extents_inode/image.gz b/tests/f_orphan_truncate_extents_inode/image.gz
new file mode 100644
index 0000000000000000000000000000000000000000..30681b879455b936e05d4dcbb4feaed6c4ff1eb9
GIT binary patch
literal 2854
zcmeHI{Z|ub7RHY)J?j?rU>6Hbay;x&EDLO;6|5w)E)secX|xtmD~TnV2mvC7HJUid
zQceX863bCRLDH29M-);L`3R7JUqnJ68v*MeBqBnh8Iq!8LP#>R!|wj*U*PHdF!S7d
zp7*))-g)mcB<cI_uW5H?EnSwC`z_~iz|44l$zvCxXV$_uLK&f*VL@Hf4~zB?`7d(G
z9Z{!g&3Sg&a>DO7t*O4TcjJk~J3vBKc=8_z<Sx_ZjK+W5OQMCD&zoA-ZaHK-k{JHC
zZM3Gff_sHcoAcz(H>U;-V<l(x4%b;(x0@n(<rf>BqHdjt*wL`GuHnw>6mHu1+Nyoz
zW#pC>z>l3<rY6NLyqxh`dRgPmwBTT=F8iEJ6rWe+`*)uuF5F_14mkHWWP@|oHuvqk
zFtsZg@btzm3uh`mVERrPNbQ_SpU6QtArK8AfQ6rCaPSmduP5Cr`@p3+K0lm-sT=o1
z<%}^hZj+oB9rI3o${4Y@Q+VB9n~x6jB~<Rlj7xR8Q=6t^&$)n|E!E7`0sp&trZS&*
zN$6dy6=sCTJUS6JNg!<9@XwVyQqmY<>0I@?)`7e%^~C9E?mTr@Do-uCS66sakURfa
zW69B;b{JjAXwR$EWrD^|QH!GH=*g;l(*nlPsL{fW*GAR_*hxR5OWtxT?7yS4mTGsR
z-Qz{f4kh`)haPQK(8^OkjS!YrD%XD=EexyN<QpQ@MPt2z+0Hrd@>&xA*JqnXp0Db;
z@R>W?h~4>m$^%q(oWgL9Y<7tSE54y~V@noy^{m!D0kZ7tIiJk9Exa3{G2eaS_1|D_
zuJ_aksd10YC-MpwE{|3*BGdA!W{E!kIQd@69%FRuNuW#a1==a%mmR}?CfC>H9(;l|
zG-HiC2x}U<q^`@RR(!qis=;$A|1!j5VKLN|K(&`C=|_#~gBWntYZ0CS7yjY|c7HSJ
zbLQr1hCLQfGzKfSU=xYCw-eBpnqimXdOoNaWMXFDytSd){e0FKs758Nn*1LW`bjPK
z*!pf1ygqYRCz)EndT+j1c=LU1VzVZeXRq3mCRJ`Ag=K{BLC{=-wPDLR^scY!65ji>
zT=#;t?f)P2t&e9#>EbU%9;j(3v#DewfZX;C{}M?EUF5dkN(EFu?JuVcKk`wZO}ZLJ
zt0*z8_n@lhh#%@MBle-mb4L(srYJ(h9TR`*l{Qmq{7>8chmh`TbWV)yBO@c2pZD4s
zB|o5no@S1!NF^@~X2Q@#{}($1NHCcK1+s)Hx%6efn9BYZIa7(k>8w8<mY>^qDMOJq
zqKC_DEs&}<j%_?=E(ck22a)e=1V~ydIIJik>KP<WoQOYn6ah_Mm4GHY1gxQ4@$a4c
z7hHc$#s&yrg7@fLAIvh<tLRBGjx0&uFQ#;Cf7#kjA60Q^0=DstL(d?NU)8Md*dW?n
zz(ZPXS~&1p2-{iaFpB>q+=T?2`jsnb3WGRWn2WqIg{kP<d3`7g)*VDvn6@ghP)(#r
zG`ACZRH=b(W}a5X-G`0l(AaWNVmN{nk&Bh?`yUUOL-+~azZ&{r4(m$}X`@wwNT+;8
zvAP-?x&qc!V^A<2)~t5#N=I(l95A<5q_ifx*_H!U2e7tkCI))6Pba|@Drc#~$RKJW
zM5%L1IQ1~)5HHfc&ReJ?Dg_m;^ZqaPt%T?oT<5``ZxzE<z3`z}i-TaC*S-I7A_Cz&
zd%i`+7F<vy<4u*ZjZsXQvU?S{$=i>&h$kh=6M{@uWd*Hb!{0-#$+%n?u3}zX?8jAr
zy*Q}BRooZxB0u8VoPOa$>Q{JHx>)1@4))@U7PB=G_H~_&IHe5db8tST9uL%0!t)eB
z81Jn+MtR8C*%S!1RoJ&7S53vric2^+Ynz0)de_0%YZs$w+bry)$@@|9$-51W5Kx+D
zM6(JwNX)fPrM%QJh^7|Mkyw)kl9V|5(G<^{t(JD0_VGmi(zhP--;(ce2YNa`^Bc;u
z`+mo3>m6G?M1`W#MlOTkj`ST-(;byJS+B@#_jLtUosXJl>q8euJ;ek<5-Fq7f7OP<
z&ZHPUx(%PI2joaq`u$r243dg0;u|i(-puz@f?oKcID(yyu*iuJ{Q*26{+u1}J!(K<
z7C9WM&!nkznL&rUiTqDHqk<mI!k0}ORMzeC!I}_C4Y+$w4S&NOC>nd>j&GfTBK6=8
z8fr(Rh`$Ae+)3_3_)rgsBRXQd&9?6$dXk$15Hu0EZz*x#io|_OF`x}^4O3P09#26U
zo&>RZB{OAkWApe$P?A%uB$dvXVM;S$&>ZsA4+Um!E%)c-B&%fik(~%`##j8u@G>0z
ztg$9S2Z(5J{|Ve;_|Px3^r!)G%f}cTJ2lVgW|T>eQwB#I@JEYAv~LiDAslF1acg>`
z_sBuk7EHy9#(k?1e<f!Lqe>GmWFfC@Q4ml@G%!BYgnLc449ITRBLDrzzQYIY9o*Wl
Qs3(SfSGqhPU{%0>0O93B*Z=?k
literal 0
HcmV?d00001
diff --git a/tests/f_orphan_truncate_extents_inode/name b/tests/f_orphan_truncate_extents_inode/name
new file mode 100644
index 000000000..6f16502b3
--- /dev/null
+++ b/tests/f_orphan_truncate_extents_inode/name
@@ -0,0 +1 @@
+truncating an orphaned extent-mapped inode in preen mode
diff --git a/tests/f_orphan_truncate_extents_inode/script b/tests/f_orphan_truncate_extents_inode/script
new file mode 100644
index 000000000..fb895e9a4
--- /dev/null
+++ b/tests/f_orphan_truncate_extents_inode/script
@@ -0,0 +1,3 @@
+FSCK_OPT=-p
+SECOND_FSCK_OPT="-yf -E no_optimize_extents"
+. $cmd_dir/run_e2fsck
--
2.43.7
^ permalink raw reply related [flat|nested] 5+ messages in thread* [PATCH RESEND 2/4] libext2fs: add quota to libext2fs
2026-06-19 15:17 [PATCH RESEND 0/4] e2fsck: Fix orphan inodes processing Etienne AUJAMES
2026-06-19 15:24 ` [PATCH RESEND 1/4] e2fsck: fix orphaned extent files handling Etienne AUJAMES
@ 2026-06-19 15:32 ` Etienne AUJAMES
2026-06-19 15:32 ` [PATCH RESEND 3/4] libext2fs: update iblock when using ea_inode feature Etienne AUJAMES
2026-06-19 15:33 ` [PATCH RESEND 4/4] libext2fs: add ext2fs_xattrs_release_all() helper Etienne AUJAMES
3 siblings, 0 replies; 5+ messages in thread
From: Etienne AUJAMES @ 2026-06-19 15:32 UTC (permalink / raw)
To: linux-ext4, Theodore Ts'o; +Cc: Andreas Dilger, Li Dongyang
add quota related interface to libext2fs and install the
relevant headers.
Change-Id: I17e6b5aa74e0f1bb1465168a1cf4e03184e003b0
Lustre-bug-id: https://jira.whamcloud.com/browse/LU-13241
Signed-off-by: Li Dongyang <dongyangli@ddn.com>
Signed-off-by: Etienne AUJAMES <eaujames@ddn.com>
---
lib/ext2fs/Makefile.in | 43 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 43 insertions(+)
diff --git a/lib/ext2fs/Makefile.in b/lib/ext2fs/Makefile.in
index e9a6ced24..0656c4c5c 100644
--- a/lib/ext2fs/Makefile.in
+++ b/lib/ext2fs/Makefile.in
@@ -28,6 +28,8 @@ DEBUG_OBJS= debug_cmds.o extent_cmds.o tst_cmds.o debugfs.o util.o \
create_inode_libarchive.o journal.o revoke.o recovery.o \
do_journal.o do_orphan.o
+QUOTA_LIB_OBJS= mkquota.o quotaio.o quotaio_v2.o quotaio_tree.o dict.o
+
DEBUG_SRCS= debug_cmds.c extent_cmds.c tst_cmds.c \
$(top_srcdir)/debugfs/debugfs.c \
$(top_srcdir)/debugfs/util.c \
@@ -57,6 +59,7 @@ DEBUG_SRCS= debug_cmds.c extent_cmds.c tst_cmds.c \
@TDB_CMT@TDB_OBJ= tdb.o
OBJS= $(DEBUGFS_LIB_OBJS) $(RESIZE_LIB_OBJS) $(E2IMAGE_LIB_OBJS) \
+ $(QUOTA_LIB_OBJS) \
$(TEST_IO_LIB_OBJS) \
ext2_err.o \
alloc.o \
@@ -236,6 +239,7 @@ SRCS= ext2_err.c \
HFILES= bitops.h ext2fs.h ext2_io.h ext2_fs.h ext2_ext_attr.h ext3_extents.h \
tdb.h qcow2.h hashmap.h
+QUOTA_HFILES= quotaio.h dqblk_v2.h quotaio_tree.h dict.h
HFILES_IN= ext2_err.h ext2_types.h
LIBRARY= libext2fs
@@ -459,6 +463,41 @@ do_orphan.o: $(top_srcdir)/debugfs/do_orphan.c
$(E) " CC $<"
$(Q) $(CC) $(DEBUGFS_CFLAGS) -c $< -o $@
+mkquota.o: $(top_srcdir)/lib/support/mkquota.c
+ $(E) " CC $<"
+ $(Q) $(CC) -I$(top_srcdir)/lib/support $(ALL_CFLAGS_STLIB) -c $< -o $@
+@PROFILE_CMT@ $(Q) $(CC) -I$(top_srcdir)/lib/support $(ALL_CFLAGS_STLIB) -g -pg -o profiled/$*.o -c $<
+@ELF_CMT@ $(Q) $(CC) -I$(top_srcdir)/lib/support $(ALL_CFLAGS_SHLIB) -fPIC -shared -o elfshared/$*.o -c $<
+@BSDLIB_CMT@ $(Q) $(CC) -I$(top_srcdir)/lib/support $(ALL_CFLAGS_SHLIB) $(BSDLIB_PIC_FLAG) -o pic/$*.o -c $<
+
+quotaio.o: $(top_srcdir)/lib/support/quotaio.c
+ $(E) " CC $<"
+ $(Q) $(CC) -I$(top_srcdir)/lib/support $(ALL_CFLAGS_STLIB) -c $< -o $@
+@PROFILE_CMT@ $(Q) $(CC) -I$(top_srcdir)/lib/support $(ALL_CFLAGS_STLIB) -g -pg -o profiled/$*.o -c $<
+@ELF_CMT@ $(Q) $(CC) -I$(top_srcdir)/lib/support $(ALL_CFLAGS_SHLIB) -fPIC -shared -o elfshared/$*.o -c $<
+@BSDLIB_CMT@ $(Q) $(CC) -I$(top_srcdir)/lib/support $(ALL_CFLAGS_SHLIB) $(BSDLIB_PIC_FLAG) -o pic/$*.o -c $<
+
+quotaio_v2.o: $(top_srcdir)/lib/support/quotaio_v2.c
+ $(E) " CC $<"
+ $(Q) $(CC) -I$(top_srcdir)/lib/support $(ALL_CFLAGS_STLIB) -c $< -o $@
+@PROFILE_CMT@ $(Q) $(CC) -I$(top_srcdir)/lib/support $(ALL_CFLAGS_STLIB) -g -pg -o profiled/$*.o -c $<
+@ELF_CMT@ $(Q) $(CC) -I$(top_srcdir)/lib/support $(ALL_CFLAGS_SHLIB) -fPIC -shared -o elfshared/$*.o -c $<
+@BSDLIB_CMT@ $(Q) $(CC) -I$(top_srcdir)/lib/support $(ALL_CFLAGS_SHLIB) $(BSDLIB_PIC_FLAG) -o pic/$*.o -c $<
+
+quotaio_tree.o: $(top_srcdir)/lib/support/quotaio_tree.c
+ $(E) " CC $<"
+ $(Q) $(CC) -I$(top_srcdir)/lib/support $(ALL_CFLAGS_STLIB) -c $< -o $@
+@PROFILE_CMT@ $(Q) $(CC) -I$(top_srcdir)/lib/support $(ALL_CFLAGS_STLIB) -g -pg -o profiled/$*.o -c $<
+@ELF_CMT@ $(Q) $(CC) -I$(top_srcdir)/lib/support $(ALL_CFLAGS_SHLIB) -fPIC -shared -o elfshared/$*.o -c $<
+@BSDLIB_CMT@ $(Q) $(CC) -I$(top_srcdir)/lib/support $(ALL_CFLAGS_SHLIB) $(BSDLIB_PIC_FLAG) -o pic/$*.o -c $<
+
+dict.o: $(top_srcdir)/lib/support/dict.c
+ $(E) " CC $<"
+ $(Q) $(CC) -I$(top_srcdir)/lib/support $(ALL_CFLAGS_STLIB) -c $< -o $@
+@PROFILE_CMT@ $(Q) $(CC) -I$(top_srcdir)/lib/support $(ALL_CFLAGS_STLIB) -g -pg -o profiled/$*.o -c $<
+@ELF_CMT@ $(Q) $(CC) -I$(top_srcdir)/lib/support $(ALL_CFLAGS_SHLIB) -fPIC -shared -o elfshared/$*.o -c $<
+@BSDLIB_CMT@ $(Q) $(CC) -I$(top_srcdir)/lib/support $(ALL_CFLAGS_SHLIB) $(BSDLIB_PIC_FLAG) -o pic/$*.o -c $<
+
xattrs.o: $(top_srcdir)/debugfs/xattrs.c
$(E) " CC $<"
$(Q) $(CC) $(DEBUGFS_CFLAGS) -c $< -o $@
@@ -586,6 +625,10 @@ install:: all $(HFILES) $(HFILES_IN) installdirs ext2fs.pc
echo " INSTALL_DATA $(includedir)/ext2fs/$$i"; \
$(INSTALL_DATA) $(srcdir)/$$i $(DESTDIR)$(includedir)/ext2fs/$$i; \
done
+ $(Q) for i in $(QUOTA_HFILES); do \
+ echo " INSTALL_DATA $(includedir)/ext2fs/$$i"; \
+ $(INSTALL_DATA) $(top_srcdir)/lib/support/$$i $(DESTDIR)$(includedir)/ext2fs/$$i; \
+ done
$(Q) for i in $(HFILES_IN); do \
echo " INSTALL_DATA $(includedir)/ext2fs/$$i"; \
$(INSTALL_DATA) $$i $(DESTDIR)$(includedir)/ext2fs/$$i; \
--
2.43.7
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH RESEND 3/4] libext2fs: update iblock when using ea_inode feature
2026-06-19 15:17 [PATCH RESEND 0/4] e2fsck: Fix orphan inodes processing Etienne AUJAMES
2026-06-19 15:24 ` [PATCH RESEND 1/4] e2fsck: fix orphaned extent files handling Etienne AUJAMES
2026-06-19 15:32 ` [PATCH RESEND 2/4] libext2fs: add quota to libext2fs Etienne AUJAMES
@ 2026-06-19 15:32 ` Etienne AUJAMES
2026-06-19 15:33 ` [PATCH RESEND 4/4] libext2fs: add ext2fs_xattrs_release_all() helper Etienne AUJAMES
3 siblings, 0 replies; 5+ messages in thread
From: Etienne AUJAMES @ 2026-06-19 15:32 UTC (permalink / raw)
To: linux-ext4, Theodore Ts'o; +Cc: Andreas Dilger, Li Dongyang
When a xattr is stored in an ea_inode, ext2fs_xattrs_* functions do
not update the inode block count.
This patch uses a cached inode to update the block count and quota
when writing xattrs.
It also fix an xattr remove case: the current ACL block was not
release by ext2fs_xattrs_write().
Add a helper function ext2fs_iblk_get() to get the inode block count
in cluster count unit.
Add ext2fs_xattrs_open_inode() to specify an optional cached inode to
use or update.
The function handle an optional quota context argument to update quota
accounting. It is hard to predict inode quota usage with ea_inode
deduplication.
For testing purposes, modify the debugfs "ea_set" command to handle
input file larger than the FS block size.
Add a regression test: d_xattr_ea_inode
Fixes: 50d0998cfe ("libext2fs: add ea_inode support to set xattr")
Signed-off-by: Etienne AUJAMES <eaujames@ddn.com>
Change-Id: I34733255bb76ffe2386d8cd6c19ce4561be4da3a
Lustre-bug-id: https://jira.whamcloud.com/browse/LU-20049
---
debugfs/xattrs.c | 19 ++-
e2fsck/pass1.c | 12 +-
lib/ext2fs/ext2fs.h | 7 ++
lib/ext2fs/ext_attr.c | 227 ++++++++++++++++++++++------------
lib/ext2fs/i_block.c | 14 +++
lib/support/quotaio.h | 1 -
tests/d_xattr_ea_inode/expect | 137 ++++++++++++++++++++
tests/d_xattr_ea_inode/name | 1 +
tests/d_xattr_ea_inode/script | 85 +++++++++++++
9 files changed, 415 insertions(+), 88 deletions(-)
create mode 100644 tests/d_xattr_ea_inode/expect
create mode 100644 tests/d_xattr_ea_inode/name
create mode 100644 tests/d_xattr_ea_inode/script
diff --git a/debugfs/xattrs.c b/debugfs/xattrs.c
index b518941c9..8364281f4 100644
--- a/debugfs/xattrs.c
+++ b/debugfs/xattrs.c
@@ -14,6 +14,7 @@ extern int optind;
extern char *optarg;
#endif
#include <ctype.h>
+#include <unistd.h>
#include "support/cstring.h"
#include "debugfs.h"
@@ -299,10 +300,24 @@ void do_set_xattr(int argc, ss_argv_t argv, int sci_idx EXT2FS_ATTR((unused)),
goto out;
if (fp) {
- err = ext2fs_get_mem(current_fs->blocksize, &buf);
+ struct stat st;
+
+ if (fstat(fileno(fp), &st)) {
+ err = errno;
+ goto out;
+ }
+ if (st.st_size > sysconf(_SC_ARG_MAX)) {
+ err = EFBIG;
+ goto out;
+ }
+ err = ext2fs_get_mem(st.st_size, &buf);
if (err)
goto out;
- buflen = fread(buf, 1, current_fs->blocksize, fp);
+ buflen = fread(buf, 1, st.st_size, fp);
+ if (ferror(fp)) {
+ err = errno;
+ goto out;
+ }
} else {
buf = argv[optind + 2];
buflen = parse_c_string(buf);
diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index fdde76cc2..364128f4d 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -968,18 +968,18 @@ static void reserve_block_for_lnf_repair(e2fsck_t ctx)
static errcode_t get_inline_data_ea_size(ext2_filsys fs, ext2_ino_t ino,
struct ext2_inode *inode,
- size_t *sz)
+ size_t inode_size, size_t *sz)
{
void *p;
struct ext2_xattr_handle *handle;
errcode_t retval;
- retval = ext2fs_xattrs_open(fs, ino, &handle);
+ retval = ext2fs_xattrs_open_inode(fs, ino, inode, inode_size, NULL,
+ &handle);
if (retval)
return retval;
- retval = ext2fs_xattrs_read_inode(handle,
- (struct ext2_inode_large *)inode);
+ retval = ext2fs_xattrs_read(handle);
if (retval)
goto err;
@@ -1580,6 +1580,7 @@ void e2fsck_pass1(e2fsck_t ctx)
size_t size = 0;
pctx.errcode = get_inline_data_ea_size(fs, ino, inode,
+ inode_size,
&size);
if (!pctx.errcode &&
fix_problem(ctx, PR_1_INLINE_DATA_FEATURE, &pctx)) {
@@ -1603,7 +1604,8 @@ void e2fsck_pass1(e2fsck_t ctx)
flags = fs->flags;
if (failed_csum)
fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
- err = get_inline_data_ea_size(fs, ino, inode, &size);
+ err = get_inline_data_ea_size(fs, ino, inode,
+ inode_size, &size);
fs->flags = (flags & EXT2_FLAG_IGNORE_CSUM_ERRORS) |
(fs->flags & ~EXT2_FLAG_IGNORE_CSUM_ERRORS);
diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
index c4fcb10be..56de5ea50 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -94,6 +94,8 @@ typedef __s64 __bitwise ext2_off64_t;
typedef __s64 __bitwise e2_blkcnt_t;
typedef __u32 __bitwise ext2_dirhash_t;
+typedef struct quota_ctx *quota_ctx_t;
+
#if EXT2_FLAT_INCLUDES
#include "com_err.h"
#include "ext2_io.h"
@@ -1408,6 +1410,10 @@ errcode_t ext2fs_xattr_set(struct ext2_xattr_handle *handle,
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_inode(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_inode *inode, size_t inode_size,
+ quota_ctx_t qctx,
+ 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);
@@ -1607,6 +1613,7 @@ errcode_t ext2fs_iblk_add_blocks(ext2_filsys fs, struct ext2_inode *inode,
errcode_t ext2fs_iblk_sub_blocks(ext2_filsys fs, struct ext2_inode *inode,
blk64_t num_blocks);
errcode_t ext2fs_iblk_set(ext2_filsys fs, struct ext2_inode *inode, blk64_t b);
+blk64_t ext2fs_iblk_get(ext2_filsys fs, struct ext2_inode *inode);
/* imager.c */
extern errcode_t ext2fs_image_inode_write(ext2_filsys fs, int fd, int flags);
diff --git a/lib/ext2fs/ext_attr.c b/lib/ext2fs/ext_attr.c
index 7723d0f91..3b90b70bb 100644
--- a/lib/ext2fs/ext_attr.c
+++ b/lib/ext2fs/ext_attr.c
@@ -22,6 +22,7 @@
#include "ext2_fs.h"
#include "ext2_ext_attr.h"
#include "ext4_acl.h"
+#include "support/quotaio.h"
#include "ext2fsP.h"
@@ -361,8 +362,14 @@ struct ext2_xattr_handle {
int capacity;
int count;
int ibody_count;
+ struct ext2_inode *in_inode;
+ size_t in_inode_size;
+ struct ext2_inode_large *alloc_inode;
+ struct ext2_inode_large *inode;
+ size_t inode_size;
ext2_ino_t ino;
unsigned int flags;
+ quota_ctx_t qctx;
};
static errcode_t ext2fs_xattrs_expand(struct ext2_xattr_handle *h,
@@ -499,9 +506,11 @@ out:
return err;
}
-static errcode_t prep_ea_block_for_write(ext2_filsys fs, ext2_ino_t ino,
- struct ext2_inode_large *inode)
+static errcode_t prep_ea_block_for_write(ext2_filsys fs,
+ struct ext2_xattr_handle *handle)
{
+ struct ext2_inode_large *inode = handle->inode;
+ ext2_ino_t ino = handle->ino;
struct ext2_ext_attr_header *header;
void *block_buf = NULL;
blk64_t blk, goal;
@@ -541,11 +550,15 @@ static errcode_t prep_ea_block_for_write(ext2_filsys fs, ext2_ino_t ino,
if (err)
goto out2;
} else {
- /* No block, we must increment i_blocks */
+ /* No block, we must increment i_blocks and quota */
err = ext2fs_iblk_add_blocks(fs, (struct ext2_inode *)inode,
1);
if (err)
goto out;
+
+ if (handle->qctx)
+ quota_data_add(handle->qctx, handle->inode, handle->ino,
+ EXT2FS_C2B(fs, 1) * fs->blocksize);
}
/* Allocate a block */
@@ -744,8 +757,7 @@ write_xattrs_to_buffer(ext2_filsys fs, struct ext2_xattr *attrs, int count,
errcode_t ext2fs_xattrs_write(struct ext2_xattr_handle *handle)
{
ext2_filsys fs = handle->fs;
- const unsigned int inode_size = EXT2_INODE_SIZE(fs->super);
- struct ext2_inode_large *inode;
+ struct ext2_inode_large *inode = handle->inode;
char *start, *block_buf = NULL;
struct ext2_ext_attr_header *header;
__u32 ea_inode_magic;
@@ -755,21 +767,12 @@ errcode_t ext2fs_xattrs_write(struct ext2_xattr_handle *handle)
errcode_t err;
EXT2_CHECK_MAGIC(handle, EXT2_ET_MAGIC_EA_HANDLE);
- i = inode_size;
- if (i < sizeof(*inode))
- i = sizeof(*inode);
- err = ext2fs_get_memzero(i, &inode);
- if (err)
- return err;
-
- err = ext2fs_read_inode_full(fs, handle->ino, EXT2_INODE(inode),
- inode_size);
- if (err)
- goto out;
+ if (!inode)
+ return EINVAL;
/* If extra_isize isn't set, we need to set it now */
if (inode->i_extra_isize == 0 &&
- inode_size > EXT2_GOOD_OLD_INODE_SIZE) {
+ handle->inode_size > EXT2_GOOD_OLD_INODE_SIZE) {
char *p = (char *)inode;
size_t extra = fs->super->s_want_extra_isize;
@@ -778,22 +781,20 @@ errcode_t ext2fs_xattrs_write(struct ext2_xattr_handle *handle)
memset(p + EXT2_GOOD_OLD_INODE_SIZE, 0, extra);
inode->i_extra_isize = extra;
}
- if (inode->i_extra_isize & 3) {
- err = EXT2_ET_INODE_CORRUPTED;
- goto out;
- }
+ if (inode->i_extra_isize & 3)
+ return EXT2_ET_INODE_CORRUPTED;
/* Does the inode have space for EA? */
if (inode->i_extra_isize < sizeof(inode->i_extra_isize) ||
- inode_size <= EXT2_GOOD_OLD_INODE_SIZE + inode->i_extra_isize +
- sizeof(__u32))
+ handle->inode_size <= EXT2_GOOD_OLD_INODE_SIZE +
+ inode->i_extra_isize + sizeof(__u32))
goto write_ea_block;
/* Write the inode EA */
ea_inode_magic = EXT2_EXT_ATTR_MAGIC;
memcpy(((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE +
inode->i_extra_isize, &ea_inode_magic, sizeof(__u32));
- storage_size = inode_size - EXT2_GOOD_OLD_INODE_SIZE -
+ storage_size = handle->inode_size - EXT2_GOOD_OLD_INODE_SIZE -
inode->i_extra_isize - sizeof(__u32);
start = ((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE +
inode->i_extra_isize + sizeof(__u32);
@@ -801,17 +802,16 @@ errcode_t ext2fs_xattrs_write(struct ext2_xattr_handle *handle)
err = write_xattrs_to_buffer(fs, handle->attrs, handle->ibody_count,
start, storage_size, 0, 0);
if (err)
- goto out;
+ return err;
write_ea_block:
/* Are we done? */
- if (handle->ibody_count == handle->count &&
- !ext2fs_file_acl_block(fs, EXT2_INODE(inode)))
+ if (handle->ibody_count == handle->count)
goto skip_ea_block;
/* Write the EA block */
err = ext2fs_get_memzero(fs->blocksize, &block_buf);
if (err)
- goto out;
+ return err;
storage_size = fs->blocksize - sizeof(struct ext2_ext_attr_header);
start = block_buf + sizeof(struct ext2_ext_attr_header);
@@ -820,7 +820,7 @@ write_ea_block:
handle->count - handle->ibody_count, start,
storage_size, start - block_buf, 1);
if (err)
- goto out2;
+ goto out;
/* Write a header on the EA block */
header = (struct ext2_ext_attr_header *) block_buf;
@@ -829,15 +829,15 @@ write_ea_block:
header->h_blocks = 1;
/* Get a new block for writing */
- err = prep_ea_block_for_write(fs, handle->ino, inode);
+ err = prep_ea_block_for_write(fs, handle);
if (err)
- goto out2;
+ goto out;
/* Finally, write the new EA block */
blk = ext2fs_file_acl_block(fs, EXT2_INODE(inode));
err = ext2fs_write_ext_attr3(fs, blk, block_buf, handle->ino);
if (err)
- goto out2;
+ goto out;
skip_ea_block:
blk = ext2fs_file_acl_block(fs, (struct ext2_inode *)inode);
@@ -845,19 +845,26 @@ skip_ea_block:
/* xattrs shrunk, free the block */
err = ext2fs_free_ext_attr(fs, handle->ino, inode);
if (err)
- goto out;
+ return err;
+ if (handle->qctx)
+ quota_data_sub(handle->qctx, inode, handle->ino,
+ EXT2FS_C2B(fs, 1) * fs->blocksize);
}
/* Write the inode */
err = ext2fs_write_inode_full(fs, handle->ino, EXT2_INODE(inode),
- inode_size);
+ handle->inode_size);
if (err)
- goto out2;
+ goto out;
+
+ /* Update the caller cached inode if provided */
+ if (handle->in_inode && handle->in_inode != EXT2_INODE(handle->inode))
+ memcpy(handle->in_inode, EXT2_INODE(handle->inode),
+ handle->in_inode_size);
-out2:
- ext2fs_free_mem(&block_buf);
out:
- ext2fs_free_mem(&inode);
+ ext2fs_free_mem(&block_buf);
+
return err;
}
@@ -1130,29 +1137,51 @@ out:
return err;
}
-errcode_t ext2fs_xattrs_read(struct ext2_xattr_handle *handle)
+errcode_t ext2fs_xattrs_read(struct ext2_xattr_handle *h)
{
- struct ext2_inode_large *inode;
- size_t inode_size = EXT2_INODE_SIZE(handle->fs->super);
errcode_t err;
- EXT2_CHECK_MAGIC(handle, EXT2_ET_MAGIC_EA_HANDLE);
+ EXT2_CHECK_MAGIC(h, EXT2_ET_MAGIC_EA_HANDLE);
+
+ h->inode_size = EXT2_INODE_SIZE(h->fs->super);
+ if (h->inode_size < sizeof(*h->inode))
+ h->inode_size = sizeof(*h->inode);
+
+ /* Use the caller cached inode if possible */
+ if (h->in_inode && h->in_inode_size >= h->inode_size) {
+ h->inode = (struct ext2_inode_large *) h->in_inode;
+ goto xattrs_read;
+ }
+
+ /* Flush the caller cached inode if provided */
+ if (h->in_inode) {
+ err = ext2fs_write_inode_full(h->fs, h->ino, h->in_inode,
+ h->in_inode_size);
+ if (err)
+ goto err;
+ }
- if (inode_size < sizeof(*inode))
- inode_size = sizeof(*inode);
- err = ext2fs_get_memzero(inode_size, &inode);
+ err = ext2fs_get_memzero(h->inode_size, &h->alloc_inode);
if (err)
return err;
- err = ext2fs_read_inode_full(handle->fs, handle->ino, EXT2_INODE(inode),
- EXT2_INODE_SIZE(handle->fs->super));
+ err = ext2fs_read_inode_full(h->fs, h->ino, EXT2_INODE(h->alloc_inode),
+ h->inode_size);
if (err)
- goto out;
+ goto err;
- err = ext2fs_xattrs_read_inode(handle, inode);
+ h->inode = h->alloc_inode;
-out:
- ext2fs_free_mem(&inode);
+xattrs_read:
+ err = ext2fs_xattrs_read_inode(h, h->inode);
+ if (err)
+ goto err;
+
+ return 0;
+
+err:
+ h->inode_size = 0;
+ ext2fs_free_mem(&h->alloc_inode);
return err;
}
@@ -1272,12 +1301,15 @@ out:
return err;
}
-static errcode_t xattr_create_ea_inode(ext2_filsys fs, const void *value,
- size_t value_len, ext2_ino_t *ea_ino)
+static errcode_t xattr_create_ea_inode(struct ext2_xattr_handle *handle,
+ const void *value, size_t value_len,
+ ext2_ino_t *ea_ino)
{
+ ext2_filsys fs = handle->fs;
struct ext2_inode inode;
ext2_ino_t ino;
ext2_file_t file;
+ blk64_t iblk;
__u32 hash;
errcode_t ret;
@@ -1317,16 +1349,30 @@ static errcode_t xattr_create_ea_inode(ext2_filsys fs, const void *value,
if (ret)
return ret;
+ ret = ext2fs_read_inode(fs, ino, &inode);
+ if (ret)
+ return ret;
+
ext2fs_inode_alloc_stats2(fs, ino, 1 /* inuse */, 0 /* isdir */);
+ iblk = ext2fs_iblk_get(fs, &inode);
+ ext2fs_iblk_add_blocks(fs, EXT2_INODE(handle->inode), iblk);
+ if (handle->qctx) {
+ quota_data_add(handle->qctx, handle->inode, handle->ino,
+ EXT2FS_C2B(fs, iblk) * fs->blocksize);
+ quota_data_inodes(handle->qctx, handle->inode, handle->ino, +1);
+ }
*ea_ino = ino;
return 0;
}
-static errcode_t xattr_inode_dec_ref(ext2_filsys fs, ext2_ino_t ino)
+static errcode_t xattr_inode_dec_ref(struct ext2_xattr_handle *handle,
+ ext2_ino_t ino)
{
+ ext2_filsys fs = handle->fs;
struct ext2_inode_large inode;
__u64 ref_count;
+ blk64_t iblk;
errcode_t ret;
ret = ext2fs_read_inode_full(fs, ino, (struct ext2_inode *)&inode,
@@ -1338,6 +1384,14 @@ static errcode_t xattr_inode_dec_ref(ext2_filsys fs, ext2_ino_t ino)
ref_count--;
ext2fs_set_ea_inode_ref(EXT2_INODE(&inode), ref_count);
+ iblk = ext2fs_iblk_get(fs, EXT2_INODE(&inode));
+ ext2fs_iblk_sub_blocks(fs, EXT2_INODE(handle->inode), iblk);
+ if (handle->qctx) {
+ quota_data_sub(handle->qctx, handle->inode, handle->ino,
+ EXT2FS_C2B(fs, iblk) * fs->blocksize);
+ quota_data_inodes(handle->qctx, handle->inode, handle->ino, -1);
+ }
+
if (ref_count)
goto write_out;
@@ -1365,7 +1419,8 @@ out:
return ret;
}
-static errcode_t xattr_update_entry(ext2_filsys fs, struct ext2_xattr *x,
+static errcode_t xattr_update_entry(struct ext2_xattr_handle *handle,
+ struct ext2_xattr *x,
const char *name, const char *short_name,
int index, const void *value,
size_t value_len, int in_inode)
@@ -1390,13 +1445,13 @@ static errcode_t xattr_update_entry(ext2_filsys fs, struct ext2_xattr *x,
memcpy(new_value, value, value_len);
if (in_inode) {
- ret = xattr_create_ea_inode(fs, value, value_len, &ea_ino);
+ ret = xattr_create_ea_inode(handle, value, value_len, &ea_ino);
if (ret)
goto fail;
}
if (x->ea_ino) {
- ret = xattr_inode_dec_ref(fs, x->ea_ino);
+ ret = xattr_inode_dec_ref(handle, x->ea_ino);
if (ret)
goto fail;
}
@@ -1419,7 +1474,7 @@ fail:
if (new_value)
ext2fs_free_mem(&new_value);
if (ea_ino)
- xattr_inode_dec_ref(fs, ea_ino);
+ xattr_inode_dec_ref(handle, ea_ino);
return ret;
}
@@ -1486,7 +1541,7 @@ static errcode_t xattr_array_update(struct ext2_xattr_handle *h,
}
/* Update the existing entry. */
- ret = xattr_update_entry(h->fs, &h->attrs[old_idx], name,
+ ret = xattr_update_entry(h, &h->attrs[old_idx], name,
shortname, name_idx, value,
value_len, in_inode);
if (ret)
@@ -1515,7 +1570,7 @@ static errcode_t xattr_array_update(struct ext2_xattr_handle *h,
if (old_idx >= 0) {
/* Update the existing entry. */
- ret = xattr_update_entry(h->fs, &h->attrs[old_idx], name,
+ ret = xattr_update_entry(h, &h->attrs[old_idx], name,
shortname, name_idx, value,
value_len, in_inode);
if (ret)
@@ -1551,7 +1606,7 @@ add_new:
return ret;
}
- ret = xattr_update_entry(h->fs, &h->attrs[h->count], name, shortname,
+ ret = xattr_update_entry(h, &h->attrs[h->count], name, shortname,
name_idx, value, value_len, in_inode);
if (ret)
return ret;
@@ -1594,8 +1649,7 @@ errcode_t ext2fs_xattr_set(struct ext2_xattr_handle *h,
size_t value_len)
{
ext2_filsys fs = h->fs;
- const int inode_size = EXT2_INODE_SIZE(fs->super);
- struct ext2_inode_large *inode = NULL;
+ struct ext2_inode_large *inode = h->inode;
struct ext2_xattr *x;
char *new_value;
int ibody_free, block_free;
@@ -1605,6 +1659,8 @@ errcode_t ext2fs_xattr_set(struct ext2_xattr_handle *h,
errcode_t ret;
EXT2_CHECK_MAGIC(h, EXT2_ET_MAGIC_EA_HANDLE);
+ if (!inode || !h->inode_size)
+ return EINVAL;
ret = ext2fs_get_mem(value_len, &new_value);
if (ret)
@@ -1632,23 +1688,14 @@ errcode_t ext2fs_xattr_set(struct ext2_xattr_handle *h,
break;
}
}
-
- ret = ext2fs_get_memzero(inode_size, &inode);
- if (ret)
- goto out;
- ret = ext2fs_read_inode_full(fs, h->ino,
- (struct ext2_inode *)inode,
- inode_size);
- if (ret)
- goto out;
- if (inode_size > EXT2_GOOD_OLD_INODE_SIZE) {
+ if (h->inode_size > EXT2_GOOD_OLD_INODE_SIZE) {
extra_isize = inode->i_extra_isize;
if (extra_isize == 0) {
extra_isize = fs->super->s_want_extra_isize;
if (extra_isize == 0)
extra_isize = sizeof(__u32);
}
- ibody_free = inode_size - EXT2_GOOD_OLD_INODE_SIZE;
+ ibody_free = h->inode_size - EXT2_GOOD_OLD_INODE_SIZE;
ibody_free -= extra_isize;
/* Extended attribute magic and final null entry. */
ibody_free -= sizeof(__u32) * 2;
@@ -1694,8 +1741,6 @@ errcode_t ext2fs_xattr_set(struct ext2_xattr_handle *h,
write_out:
ret = ext2fs_xattrs_write(h);
out:
- if (inode)
- ext2fs_free_mem(&inode);
ext2fs_free_mem(&new_value);
return ret;
}
@@ -1712,7 +1757,7 @@ errcode_t ext2fs_xattr_remove(struct ext2_xattr_handle *handle,
ext2fs_free_mem(&x->name);
ext2fs_free_mem(&x->value);
if (x->ea_ino)
- xattr_inode_dec_ref(handle->fs, x->ea_ino);
+ xattr_inode_dec_ref(handle, x->ea_ino);
memmove(x, x + 1, (end - x - 1)*sizeof(*x));
memset(end - 1, 0, sizeof(*end));
if (x < handle->attrs + handle->ibody_count)
@@ -1736,7 +1781,7 @@ errcode_t ext2fs_xattr_remove_all(struct ext2_xattr_handle *handle)
ext2fs_free_mem(&x->name);
ext2fs_free_mem(&x->value);
if (x->ea_ino)
- xattr_inode_dec_ref(handle->fs, x->ea_ino);
+ xattr_inode_dec_ref(handle, x->ea_ino);
}
handle->ibody_count = 0;
@@ -1744,8 +1789,14 @@ errcode_t ext2fs_xattr_remove_all(struct ext2_xattr_handle *handle)
return ext2fs_xattrs_write(handle);
}
-errcode_t ext2fs_xattrs_open(ext2_filsys fs, ext2_ino_t ino,
- struct ext2_xattr_handle **handle)
+/* If the inode size is set to EXT2_INODE_SIZE(fs), the input inode is used
+ * directly. Otherwise, the ext2fs_xattrs_* functions operate on a separate copy
+ * (with inline xattrs) and update the caller's cached inode on write.
+ */
+errcode_t ext2fs_xattrs_open_inode(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_inode *inode, size_t inode_size,
+ quota_ctx_t qctx,
+ struct ext2_xattr_handle **handle)
{
struct ext2_xattr_handle *h;
errcode_t err;
@@ -1754,6 +1805,9 @@ errcode_t ext2fs_xattrs_open(ext2_filsys fs, ext2_ino_t ino,
!ext2fs_has_feature_inline_data(fs->super))
return EXT2_ET_MISSING_EA_FEATURE;
+ if (inode && inode_size < sizeof(*inode))
+ return EINVAL;
+
err = ext2fs_get_memzero(sizeof(*h), &h);
if (err)
return err;
@@ -1768,11 +1822,23 @@ errcode_t ext2fs_xattrs_open(ext2_filsys fs, ext2_ino_t ino,
}
h->count = 0;
h->ino = ino;
+ h->in_inode = inode;
+ h->in_inode_size = inode_size;
+ h->alloc_inode = NULL;
+ h->inode = NULL;
+ h->inode_size = 0;
h->fs = fs;
+ h->qctx = qctx;
*handle = h;
return 0;
}
+errcode_t ext2fs_xattrs_open(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_xattr_handle **handle)
+{
+ return ext2fs_xattrs_open_inode(fs, ino, NULL, 0, NULL, handle);
+}
+
errcode_t ext2fs_xattrs_close(struct ext2_xattr_handle **handle)
{
struct ext2_xattr_handle *h = *handle;
@@ -1780,6 +1846,7 @@ errcode_t ext2fs_xattrs_close(struct ext2_xattr_handle **handle)
EXT2_CHECK_MAGIC(h, EXT2_ET_MAGIC_EA_HANDLE);
xattrs_free_keys(h);
ext2fs_free_mem(&h->attrs);
+ ext2fs_free_mem(&h->alloc_inode);
ext2fs_free_mem(handle);
return 0;
}
diff --git a/lib/ext2fs/i_block.c b/lib/ext2fs/i_block.c
index 2eecf02fc..064a5c989 100644
--- a/lib/ext2fs/i_block.c
+++ b/lib/ext2fs/i_block.c
@@ -88,3 +88,17 @@ errcode_t ext2fs_iblk_set(ext2_filsys fs, struct ext2_inode *inode, blk64_t b)
return EOVERFLOW;
return 0;
}
+
+blk64_t ext2fs_iblk_get(ext2_filsys fs, struct ext2_inode *inode)
+{
+ blk64_t blk = inode->i_blocks;
+
+ if (ext2fs_has_feature_huge_file(fs->super))
+ blk += ((blk64_t) inode->osd2.linux2.l_i_blocks_hi) << 32;
+
+ if (!ext2fs_has_feature_huge_file(fs->super) ||
+ !(inode->i_flags & EXT4_HUGE_FILE_FL))
+ blk /= fs->blocksize / 512;
+
+ return EXT2FS_B2C(fs, blk);
+}
diff --git a/lib/support/quotaio.h b/lib/support/quotaio.h
index 6152416fb..c76486919 100644
--- a/lib/support/quotaio.h
+++ b/lib/support/quotaio.h
@@ -58,7 +58,6 @@ enum quota_type {
#define QUOTA_PRJ_BIT (1 << PRJQUOTA)
#define QUOTA_ALL_BIT (QUOTA_USR_BIT | QUOTA_GRP_BIT | QUOTA_PRJ_BIT)
-typedef struct quota_ctx *quota_ctx_t;
struct dict_t;
struct quota_ctx {
diff --git a/tests/d_xattr_ea_inode/expect b/tests/d_xattr_ea_inode/expect
new file mode 100644
index 000000000..aaad9c5b3
--- /dev/null
+++ b/tests/d_xattr_ea_inode/expect
@@ -0,0 +1,137 @@
+debugfs edit extended attributes with ea_inode feature
+mke2fs -Fq -b 4096 -O ea_inode test.img 1m
+Exit status is 0
+Generate xattr value (8292 bytes)
+ea_set -f d_xattr_ea_inode.tmp / user.test1
+Exit status is 0
+ea_get -f d_xattr_ea_inode.ver.tmp / user.test1
+Exit status is 0
+Compare xattr values (8292 bytes)
+stat /
+Blockcount: 32
+Exit status is 0
+ea_rm / user.test1
+Exit status is 0
+e2fsck -yf -N test_filesys
+Pass 1: Checking inodes, blocks, and sizes
+Pass 2: Checking directory structure
+Pass 3: Checking directory connectivity
+Pass 4: Checking reference counts
+Pass 5: Checking group summary information
+test_filesys: 11/128 files (0.0% non-contiguous), 18/256 blocks
+Exit status is 0
+
+Generate xattr value (4097 bytes)
+ea_set -f d_xattr_ea_inode.tmp / user.test1
+Exit status is 0
+ea_get -f d_xattr_ea_inode.ver.tmp / user.test1
+Exit status is 0
+Compare xattr values (4097 bytes)
+stat /
+Blockcount: 24
+Exit status is 0
+e2fsck -yf -N test_filesys
+Pass 1: Checking inodes, blocks, and sizes
+Pass 2: Checking directory structure
+Pass 3: Checking directory connectivity
+Pass 4: Checking reference counts
+Pass 5: Checking group summary information
+test_filesys: 12/128 files (0.0% non-contiguous), 20/256 blocks
+Exit status is 0
+
+Generate xattr value (102 bytes)
+ea_set -f d_xattr_ea_inode.tmp / user.test2
+Exit status is 0
+ea_get -f d_xattr_ea_inode.ver.tmp / user.test2
+Exit status is 0
+Compare xattr values (102 bytes)
+stat /
+Blockcount: 32
+Exit status is 0
+e2fsck -yf -N test_filesys
+Pass 1: Checking inodes, blocks, and sizes
+Pass 2: Checking directory structure
+Pass 3: Checking directory connectivity
+Pass 4: Checking reference counts
+Pass 5: Checking group summary information
+test_filesys: 12/128 files (0.0% non-contiguous), 21/256 blocks
+Exit status is 0
+
+Generate xattr value (5005 bytes)
+ea_set -f d_xattr_ea_inode.tmp / user.test2
+Exit status is 0
+ea_get -f d_xattr_ea_inode.ver.tmp / user.test2
+Exit status is 0
+Compare xattr values (5005 bytes)
+stat /
+Blockcount: 40
+Exit status is 0
+ea_rm / user.test2
+Exit status is 0
+e2fsck -yf -N test_filesys
+Pass 1: Checking inodes, blocks, and sizes
+Pass 2: Checking directory structure
+Pass 3: Checking directory connectivity
+Pass 4: Checking reference counts
+Pass 5: Checking group summary information
+test_filesys: 12/128 files (0.0% non-contiguous), 20/256 blocks
+Exit status is 0
+
+Generate xattr value (512 bytes)
+ea_set -f d_xattr_ea_inode.tmp / user.test1
+Exit status is 0
+ea_get -f d_xattr_ea_inode.ver.tmp / user.test1
+Exit status is 0
+Compare xattr values (512 bytes)
+stat /
+Blockcount: 16
+Exit status is 0
+ea_rm / user.test1
+Exit status is 0
+e2fsck -yf -N test_filesys
+Pass 1: Checking inodes, blocks, and sizes
+Pass 2: Checking directory structure
+Pass 3: Checking directory connectivity
+Pass 4: Checking reference counts
+Pass 5: Checking group summary information
+test_filesys: 11/128 files (0.0% non-contiguous), 18/256 blocks
+Exit status is 0
+
+Generate xattr value (1024 bytes)
+ea_set -f d_xattr_ea_inode.tmp / user.test1
+Exit status is 0
+ea_get -f d_xattr_ea_inode.ver.tmp / user.test1
+Exit status is 0
+Compare xattr values (1024 bytes)
+stat /
+Blockcount: 16
+Exit status is 0
+e2fsck -yf -N test_filesys
+Pass 1: Checking inodes, blocks, and sizes
+Pass 2: Checking directory structure
+Pass 3: Checking directory connectivity
+Pass 4: Checking reference counts
+Pass 5: Checking group summary information
+test_filesys: 11/128 files (0.0% non-contiguous), 19/256 blocks
+Exit status is 0
+
+Generate xattr value (5000 bytes)
+ea_set -f d_xattr_ea_inode.tmp / user.test1
+Exit status is 0
+ea_get -f d_xattr_ea_inode.ver.tmp / user.test1
+Exit status is 0
+Compare xattr values (5000 bytes)
+stat /
+Blockcount: 24
+Exit status is 0
+ea_rm / user.test1
+Exit status is 0
+e2fsck -yf -N test_filesys
+Pass 1: Checking inodes, blocks, and sizes
+Pass 2: Checking directory structure
+Pass 3: Checking directory connectivity
+Pass 4: Checking reference counts
+Pass 5: Checking group summary information
+test_filesys: 11/128 files (0.0% non-contiguous), 18/256 blocks
+Exit status is 0
+
diff --git a/tests/d_xattr_ea_inode/name b/tests/d_xattr_ea_inode/name
new file mode 100644
index 000000000..9e36dc986
--- /dev/null
+++ b/tests/d_xattr_ea_inode/name
@@ -0,0 +1 @@
+edit extended attributes in debugfs with ea_inode feature
diff --git a/tests/d_xattr_ea_inode/script b/tests/d_xattr_ea_inode/script
new file mode 100644
index 000000000..84104549c
--- /dev/null
+++ b/tests/d_xattr_ea_inode/script
@@ -0,0 +1,85 @@
+#!/bin/bash
+
+if ! test -x $DEBUGFS_EXE; then
+ echo "$test_name: $test_description: skipped (no debugfs)"
+ return 0
+fi
+
+OUT=$test_name.log
+EXP=$test_dir/expect
+VERIFY_FSCK_OPT=-yf
+
+TEST_DATA=$test_name.tmp
+VERIFY_DATA=$test_name.ver.tmp
+
+echo "debugfs edit extended attributes with ea_inode feature" > $OUT.new
+
+d_xattr_ea_inode_check() {
+ local xattr_size=$1
+ local xattr_name=$2
+ local ea_rm=$3
+
+ echo "Generate xattr value ($xattr_size bytes)" >> $OUT.new
+ echo $xattr_size |
+ awk '{srand();for(i=0;i<$1;i++) printf("%c",97+int(rand()*26));}' > $TEST_DATA
+
+ echo "ea_set -f $TEST_DATA / $xattr_name" >> $OUT.new
+ $DEBUGFS -w -R "ea_set -f $TEST_DATA / $xattr_name" $TMPFILE >> $OUT.new 2>&1
+ echo Exit status is $? >> $OUT.new
+
+ echo "ea_get -f $VERIFY_DATA / $xattr_name" >> $OUT.new
+ $DEBUGFS -w -R "ea_get -f $VERIFY_DATA / $xattr_name" $TMPFILE >> $OUT.new 2>&1
+ echo Exit status is $? >> $OUT.new
+
+ echo "Compare xattr values ($xattr_size bytes)" >> $OUT.new
+ diff -u $TEST_DATA $VERIFY_DATA >> $OUT.new
+
+ echo "stat /" >> $OUT.new
+ ($DEBUGFS -c -R "stat /" $TMPFILE | grep -Eo "Blockcount: [0-9]+") >> $OUT.new 2>&1
+ echo Exit status is $? >> $OUT.new
+
+ if $ea_rm; then
+ echo "ea_rm / $xattr_name" >> $OUT.new
+ $DEBUGFS -w -R "ea_rm / $xattr_name" $TMPFILE >> $OUT.new 2>&1
+ echo Exit status is $? >> $OUT.new
+ fi
+
+ echo e2fsck $VERIFY_FSCK_OPT -N test_filesys >> $OUT.new
+ $FSCK $VERIFY_FSCK_OPT -N test_filesys $TMPFILE >> $OUT.new 2>&1
+ echo Exit status is $? >> $OUT.new
+ echo >> $OUT.new
+}
+
+truncate -s1M $TMPFILE 2>&1
+
+echo "mke2fs -Fq -b 4096 -O ea_inode test.img 1m" >> $OUT.new
+$MKE2FS -Fq -b 4096 -O ea_inode $TMPFILE 1m > /dev/null 2>&1
+echo Exit status is $? >> $OUT.new
+
+d_xattr_ea_inode_check 8292 user.test1 true
+
+d_xattr_ea_inode_check 4097 user.test1 false
+d_xattr_ea_inode_check 102 user.test2 false
+d_xattr_ea_inode_check 5005 user.test2 true
+d_xattr_ea_inode_check 512 user.test1 true
+
+d_xattr_ea_inode_check 1024 user.test1 false
+d_xattr_ea_inode_check 5000 user.test1 true
+
+sed -f $cmd_dir/filter.sed $OUT.new > $OUT
+
+#
+# Do the verification
+#
+
+rm -f $TMPFILE $TEST_DATA $VERIFY_DATA $OUT.new
+
+if cmp -s $OUT $EXP; then
+ echo "$test_name: $test_description: ok"
+ touch $test_name.ok
+else
+ echo "$test_name: $test_description: failed"
+ diff $DIFF_OPTS $EXP $OUT > $test_name.failed
+fi
+
+unset VERIFY_FSCK_OPT VERIFY_DATA TEST_DATA OUT EXP d_xattr_ea_inode_check
--
2.43.7
^ permalink raw reply related [flat|nested] 5+ messages in thread* [PATCH RESEND 4/4] libext2fs: add ext2fs_xattrs_release_all() helper
2026-06-19 15:17 [PATCH RESEND 0/4] e2fsck: Fix orphan inodes processing Etienne AUJAMES
` (2 preceding siblings ...)
2026-06-19 15:32 ` [PATCH RESEND 3/4] libext2fs: update iblock when using ea_inode feature Etienne AUJAMES
@ 2026-06-19 15:33 ` Etienne AUJAMES
3 siblings, 0 replies; 5+ messages in thread
From: Etienne AUJAMES @ 2026-06-19 15:33 UTC (permalink / raw)
To: linux-ext4, Theodore Ts'o; +Cc: Andreas Dilger, Li Dongyang
This patch adds a helper function ext2fs_xattrs_release_all() which
removes all extended attributes and updates the quota accordingly.
The main purpose of this is to handle ea_inode xattrs in e2fsck when
deleting orphan inodes:
# e2fsck -yf /tmp/ext4
e2fsck 1.47.3-wc2 (11-Nov-2025)
Clearing orphaned inode 12 (uid=0, gid=0, mode=0100644, size=0)
Pass 1: Checking inodes, blocks, and sizes
Pass 2: Checking directory structure
Pass 3: Checking directory connectivity
Pass 4: Checking reference counts
Regular filesystem inode 13 has EA_INODE flag set. Clear<y>? yes
Unattached inode 13
Connect to /lost+found<y>? yes
Inode 13 ref count is 2, should be 1. Fix<y>? yes
fuse2fs, debugfs and mke2fs are updated to use this function and
handle ea_inode on inode deletion.
Update d_xattr_ea_inode to check for the inode deletion case.
Add a regression test: f_orphan_ea_inode
Signed-off-by: Etienne AUJAMES <eaujames@ddn.com>
Change-Id: I4a84a50d43b8b9aab2dfc352a92256c710a3659e
Lustre-bug-id: https://jira.whamcloud.com/browse/LU-20049
---
debugfs/debugfs.c | 33 +++++++--
e2fsck/super.c | 67 +++++++++++-------
lib/ext2fs/ext2fs.h | 3 +
lib/ext2fs/ext_attr.c | 41 +++++++++++
misc/create_inode_libarchive.c | 35 ++++-----
misc/fuse2fs.c | 117 +++++++++++--------------------
tests/d_xattr_ea_inode/expect | 51 ++++++++++++++
tests/d_xattr_ea_inode/script | 55 ++++++++++-----
tests/f_orphan_ea_inode/expect.1 | 6 ++
tests/f_orphan_ea_inode/expect.2 | 7 ++
tests/f_orphan_ea_inode/image.gz | Bin 0 -> 2139 bytes
tests/f_orphan_ea_inode/name | 1 +
tests/f_orphan_ea_inode/script | 3 +
13 files changed, 277 insertions(+), 142 deletions(-)
create mode 100644 tests/f_orphan_ea_inode/expect.1
create mode 100644 tests/f_orphan_ea_inode/expect.2
create mode 100644 tests/f_orphan_ea_inode/image.gz
create mode 100644 tests/f_orphan_ea_inode/name
create mode 100644 tests/f_orphan_ea_inode/script
diff --git a/debugfs/debugfs.c b/debugfs/debugfs.c
index b9f248be2..d316293d2 100644
--- a/debugfs/debugfs.c
+++ b/debugfs/debugfs.c
@@ -1861,21 +1861,40 @@ static int release_blocks_proc(ext2_filsys fs, blk64_t *blocknr,
static void kill_file_by_inode(ext2_ino_t inode)
{
- struct ext2_inode inode_buf;
+ struct ext2_inode_large *inode_buf;
+ size_t inode_size = EXT2_INODE_SIZE(current_fs->super);
+ errcode_t err;
- if (debugfs_read_inode(inode, &inode_buf, 0))
- return;
- ext2fs_set_dtime(current_fs, &inode_buf);
- if (debugfs_write_inode(inode, &inode_buf, 0))
+ err = ext2fs_get_memzero(inode_size, &inode_buf);
+ if (err)
return;
- if (ext2fs_inode_has_valid_blocks2(current_fs, &inode_buf)) {
+
+ err = ext2fs_read_inode_full(current_fs, inode, EXT2_INODE(inode_buf),
+ inode_size);
+ if (err) {
+ com_err(__func__, err, "while reading inode %u", inode);
+ goto out;
+ }
+
+ ext2fs_set_dtime(current_fs, EXT2_INODE(inode_buf));
+ ext2fs_xattrs_release_all(current_fs, inode, inode_buf, inode_size,
+ NULL);
+ if (ext2fs_inode_has_valid_blocks2(current_fs, EXT2_INODE(inode_buf))) {
blk64_t last_cluster = 0;
ext2fs_block_iterate3(current_fs, inode, BLOCK_FLAG_READ_ONLY,
NULL, release_blocks_proc, &last_cluster);
}
printf("\n");
ext2fs_inode_alloc_stats2(current_fs, inode, -1,
- LINUX_S_ISDIR(inode_buf.i_mode));
+ LINUX_S_ISDIR(inode_buf->i_mode));
+
+ err = ext2fs_write_inode_full(current_fs, inode, EXT2_INODE(inode_buf),
+ inode_size);
+ if (err)
+ com_err(__func__, err, "while writing inode %u", inode);
+
+out:
+ ext2fs_free_mem(&inode_buf);
}
diff --git a/e2fsck/super.c b/e2fsck/super.c
index c2ccefd54..1a94ba567 100644
--- a/e2fsck/super.c
+++ b/e2fsck/super.c
@@ -156,13 +156,14 @@ static errcode_t truncate_inode_blocks(e2fsck_t ctx, ext2_ino_t ino,
struct process_block_struct pb = { 0 };
e2_blkcnt_t truncate_block = 0;
__u32 truncate_offset = 0;
- blk64_t blk;
+ blk64_t blk, iblks;
int ret_flags;
errcode_t retval = 0;
if (!ext2fs_inode_has_valid_blocks2(fs, EXT2_INODE(inode)))
return 0;
+ iblks = ext2fs_get_stat_i_blocks(fs, EXT2_INODE(inode));
if (inode->i_links_count) {
truncate_offset = inode->i_size % fs->blocksize;
truncate_block = (e2_blkcnt_t)
@@ -190,6 +191,10 @@ static errcode_t truncate_inode_blocks(e2fsck_t ctx, ext2_ino_t ino,
"release_inode_blocks");
ext2fs_iblk_sub_blocks(fs, EXT2_INODE(inode), pb.truncated_blocks);
+ iblks -= ext2fs_get_stat_i_blocks(fs, EXT2_INODE(inode));
+ if (ctx->qctx)
+ quota_data_sub(ctx->qctx, inode, ino, iblks * 512);
+
if (!truncate_offset)
return 0;
@@ -217,17 +222,19 @@ static errcode_t truncate_inode_blocks(e2fsck_t ctx, ext2_ino_t ino,
* not deleted.
*/
static int release_inode_blocks(e2fsck_t ctx, ext2_ino_t ino,
- struct ext2_inode_large *inode, char *block_buf,
+ struct ext2_inode_large *inode,
+ size_t inode_size, char *block_buf,
struct problem_context *pctx)
{
ext2_filsys fs = ctx->fs;
- blk64_t free_blks, ino_blks;
+ blk64_t free_blks;
+ __u32 free_inodes;
char *buf;
errcode_t err;
int rc = 0;
+ free_inodes = fs->super->s_free_inodes_count;
free_blks = ext2fs_free_blocks_count(fs->super);
- ino_blks = ext2fs_get_stat_i_blocks(fs, EXT2_INODE(inode));
buf = block_buf + 3 * ctx->fs->blocksize;
if (truncate_inode_blocks(ctx, ino, inode, buf, pctx)) {
rc = 1;
@@ -236,7 +243,7 @@ static int release_inode_blocks(e2fsck_t ctx, ext2_ino_t ino,
if (inode->i_links_count)
goto update_counts;
- err = ext2fs_free_ext_attr(fs, ino, inode);
+ err = ext2fs_xattrs_release_all(fs, ino, inode, inode_size, ctx->qctx);
if (err) {
com_err(__func__, err,
_("while calling ext2fs_free_ext_attr for inode %u"),
@@ -249,9 +256,8 @@ static int release_inode_blocks(e2fsck_t ctx, ext2_ino_t ino,
update_counts:
ctx->free_blocks += ext2fs_free_blocks_count(fs->super) - free_blks;
- ino_blks -= ext2fs_get_stat_i_blocks(fs, EXT2_INODE(inode));
- if (ctx->qctx)
- quota_data_sub(ctx->qctx, inode, 0, ino_blks << 9);
+ free_inodes = fs->super->s_free_inodes_count - free_inodes;
+ ctx->free_inodes += free_inodes;
return rc;
}
@@ -312,44 +318,55 @@ static int release_orphan_inode(e2fsck_t ctx, ext2_ino_t *ino, char *block_buf)
{
ext2_filsys fs = ctx->fs;
struct problem_context pctx;
- struct ext2_inode_large inode;
+ struct ext2_inode_large *inode;
+ size_t inode_size = EXT2_INODE_SIZE(fs->super);
ext2_ino_t next_ino;
+ int rc = 1;
+
+ if (ext2fs_get_memzero(inode_size, &inode))
+ return 1;
- e2fsck_read_inode_full(ctx, *ino, EXT2_INODE(&inode),
- sizeof(inode), "release_orphan_inode");
+ e2fsck_read_inode_full(ctx, *ino, EXT2_INODE(inode),
+ inode_size, __func__);
clear_problem_context(&pctx);
pctx.ino = *ino;
- pctx.inode = EXT2_INODE(&inode);
- pctx.str = inode.i_links_count ? _("Truncating") : _("Clearing");
+ pctx.inode = EXT2_INODE(inode);
+ pctx.str = inode->i_links_count ? _("Truncating") : _("Clearing");
fix_problem(ctx, PR_0_ORPHAN_CLEAR_INODE, &pctx);
- next_ino = inode.i_dtime;
+ next_ino = inode->i_dtime;
if (next_ino &&
((next_ino < EXT2_FIRST_INODE(fs->super)) ||
(next_ino > fs->super->s_inodes_count))) {
pctx.ino = next_ino;
fix_problem(ctx, PR_0_ORPHAN_ILLEGAL_INODE, &pctx);
- return 1;
+ goto out;
}
- if (release_inode_blocks(ctx, *ino, &inode, block_buf, &pctx))
- return 1;
+ if (release_inode_blocks(ctx, *ino, inode, inode_size, block_buf,
+ &pctx))
+ goto out;
- if (!inode.i_links_count) {
+ if (!inode->i_links_count) {
if (ctx->qctx)
- quota_data_inodes(ctx->qctx, &inode, *ino, -1);
+ quota_data_inodes(ctx->qctx, inode, *ino, -1);
ext2fs_inode_alloc_stats2(fs, *ino, -1,
- LINUX_S_ISDIR(inode.i_mode));
+ LINUX_S_ISDIR(inode->i_mode));
ctx->free_inodes++;
- ext2fs_set_dtime(fs, EXT2_INODE(&inode));
+ ext2fs_set_dtime(fs, EXT2_INODE(inode));
} else {
- inode.i_dtime = 0;
+ inode->i_dtime = 0;
}
- e2fsck_write_inode_full(ctx, *ino, EXT2_INODE(&inode),
- sizeof(inode), "delete_file");
+ e2fsck_write_inode_full(ctx, *ino, EXT2_INODE(inode),
+ inode_size, __func__);
*ino = next_ino;
- return 0;
+ rc = 0;
+
+out:
+ ext2fs_free_mem(&inode);
+
+ return rc;
}
struct process_orphan_block_data {
diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
index 56de5ea50..cb3f1a3a1 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -1425,6 +1425,9 @@ errcode_t ext2fs_xattr_inode_max_size(ext2_filsys fs, ext2_ino_t ino,
#define XATTR_HANDLE_FLAG_RAW 0x0001
errcode_t ext2fs_xattrs_flags(struct ext2_xattr_handle *handle,
unsigned int *new_flags, unsigned int *old_flags);
+errcode_t ext2fs_xattrs_release_all(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_inode_large *inode,
+ size_t inode_size, quota_ctx_t qctx);
extern void ext2fs_ext_attr_block_rehash(struct ext2_ext_attr_header *header,
struct ext2_ext_attr_entry *end);
extern __u32 ext2fs_get_ea_inode_hash(struct ext2_inode *inode);
diff --git a/lib/ext2fs/ext_attr.c b/lib/ext2fs/ext_attr.c
index 3b90b70bb..2a2e79acd 100644
--- a/lib/ext2fs/ext_attr.c
+++ b/lib/ext2fs/ext_attr.c
@@ -1868,3 +1868,44 @@ errcode_t ext2fs_xattrs_flags(struct ext2_xattr_handle *handle,
handle->flags = *new_flags;
return 0;
}
+
+errcode_t ext2fs_xattrs_release_all(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_inode_large *inode,
+ size_t inode_size, quota_ctx_t qctx)
+{
+ struct ext2_xattr_handle *h;
+ errcode_t err = 0;
+
+ if (!ext2fs_has_feature_ea_inode(fs->super)) {
+ blk64_t blk = ext2fs_file_acl_block(fs, EXT2_INODE(inode));
+
+ if (!blk)
+ return 0;
+
+ err = ext2fs_free_ext_attr(fs, ino, inode);
+ if (err || !qctx)
+ return err;
+
+ quota_data_sub(qctx, inode, ino,
+ EXT2FS_C2B(fs, 1) * fs->blocksize);
+ return 0;
+ }
+
+ err = ext2fs_xattrs_open_inode(fs, ino, EXT2_INODE(inode), inode_size,
+ qctx, &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);
+
+ return err;
+}
diff --git a/misc/create_inode_libarchive.c b/misc/create_inode_libarchive.c
index fadf0721f..4736e8c22 100644
--- a/misc/create_inode_libarchive.c
+++ b/misc/create_inode_libarchive.c
@@ -261,46 +261,49 @@ static inline unsigned int __round_up(unsigned int quantity, unsigned int size)
static int remove_inode(ext2_filsys fs, ext2_ino_t ino)
{
errcode_t ret = 0;
- struct ext2_inode_large inode;
+ struct ext2_inode_large *inode;
+ size_t inode_size = EXT2_INODE_SIZE(fs->super);
- memset(&inode, 0, sizeof(inode));
- ret = ext2fs_read_inode_full(fs, ino, (struct ext2_inode *)&inode,
- sizeof(inode));
+ ret = ext2fs_get_memzero(inode_size, &inode);
+ if (ret)
+ return ret;
+
+ ret = ext2fs_read_inode_full(fs, ino, EXT2_INODE(inode), inode_size);
if (ret)
goto out;
- switch (inode.i_links_count) {
+ switch (inode->i_links_count) {
case 0:
return 0; /* XXX: already done? */
case 1:
- inode.i_links_count--;
- ext2fs_set_dtime(fs, EXT2_INODE(&inode));
+ inode->i_links_count--;
+ ext2fs_set_dtime(fs, EXT2_INODE(inode));
break;
default:
- inode.i_links_count--;
+ inode->i_links_count--;
}
- if (inode.i_links_count)
+ if (inode->i_links_count)
goto write_out;
/* Nobody holds this file; free its blocks! */
- ret = ext2fs_free_ext_attr(fs, ino, &inode);
+ ret = ext2fs_xattrs_release_all(fs, ino, inode, inode_size, NULL);
if (ret)
goto write_out;
- if (ext2fs_inode_has_valid_blocks2(fs, (struct ext2_inode *)&inode)) {
- ret = ext2fs_punch(fs, ino, (struct ext2_inode *)&inode, NULL,
- 0, ~0ULL);
+ if (ext2fs_inode_has_valid_blocks2(fs, EXT2_INODE(inode))) {
+ ret = ext2fs_punch(fs, ino, EXT2_INODE(inode), NULL, 0, ~0ULL);
if (ret)
goto write_out;
}
- ext2fs_inode_alloc_stats2(fs, ino, -1, LINUX_S_ISDIR(inode.i_mode));
+ ext2fs_inode_alloc_stats2(fs, ino, -1, LINUX_S_ISDIR(inode->i_mode));
write_out:
- ret = ext2fs_write_inode_full(fs, ino, (struct ext2_inode *)&inode,
- sizeof(inode));
+ ret = ext2fs_write_inode_full(fs, ino, EXT2_INODE(inode), inode_size);
out:
+ ext2fs_free_mem(&inode);
+
return ret;
}
diff --git a/misc/fuse2fs.c b/misc/fuse2fs.c
index 94e289fab..11141f645 100644
--- a/misc/fuse2fs.c
+++ b/misc/fuse2fs.c
@@ -2274,123 +2274,88 @@ static int fuse2fs_unlink(struct fuse2fs *ff, const char *path,
return 0;
}
-static int remove_ea_inodes(struct fuse2fs *ff, ext2_ino_t ino,
- struct ext2_inode_large *inode)
+static int remove_inode(struct fuse2fs *ff, ext2_ino_t ino)
{
ext2_filsys fs = ff->fs;
- struct ext2_xattr_handle *h;
errcode_t err;
+ struct ext2_inode_large *inode;
+ size_t inode_size = EXT2_INODE_SIZE(fs->super);
int ret = 0;
- /*
- * 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);
+ err = ext2fs_get_memzero(inode_size, &inode);
if (err)
return translate_error(fs, ino, err);
- err = ext2fs_xattrs_open(fs, ino, &h);
- if (err)
- return translate_error(fs, ino, err);
-
- err = ext2fs_xattrs_read(h);
+ err = ext2fs_read_inode_full(fs, ino, EXT2_INODE(inode), inode_size);
if (err) {
ret = translate_error(fs, ino, err);
- goto out_close;
- }
-
- err = ext2fs_xattr_remove_all(h);
- if (err) {
- ret = translate_error(fs, ino, err);
- goto out_close;
+ goto out;
}
-
-out_close:
- ext2fs_xattrs_close(&h);
- if (ret)
- return ret;
-
- /* Now read the inode back in. */
- err = fuse2fs_read_inode(fs, ino, inode);
- if (err)
- return translate_error(fs, ino, err);
-
- return 0;
-}
-
-static int remove_inode(struct fuse2fs *ff, ext2_ino_t ino)
-{
- ext2_filsys fs = ff->fs;
- errcode_t err;
- struct ext2_inode_large inode;
- int ret = 0;
-
- err = fuse2fs_read_inode(fs, ino, &inode);
- if (err)
- return translate_error(fs, ino, err);
-
dbg_printf(ff, "%s: put ino=%d links=%d\n", __func__, ino,
- inode.i_links_count);
+ inode->i_links_count);
- if (S_ISDIR(inode.i_mode)) {
+ if (S_ISDIR(inode->i_mode)) {
/*
* Caller should have checked that this is an empty directory
* before starting the unlink process. nlink is usually 2, but
* it could be 1 if this dir ever had more than 65000 subdirs.
* Zero the link count.
*/
- if (!ext2fs_dir_link_empty(EXT2_INODE(&inode)))
- return translate_error(fs, ino, EXT2_ET_INODE_CORRUPTED);
- inode.i_links_count = 0;
- ext2fs_set_dtime(fs, EXT2_INODE(&inode));
+ if (!ext2fs_dir_link_empty(EXT2_INODE(inode))) {
+ ret = translate_error(fs, ino, EXT2_ET_INODE_CORRUPTED);
+ goto out;
+ }
+ inode->i_links_count = 0;
+ ext2fs_set_dtime(fs, EXT2_INODE(inode));
} else {
/*
* Any other file type can be hardlinked, so all we need to do
* is decrement the nlink.
*/
- if (inode.i_links_count == 0)
- return translate_error(fs, ino, EXT2_ET_INODE_CORRUPTED);
- inode.i_links_count--;
- if (!inode.i_links_count)
- ext2fs_set_dtime(fs, EXT2_INODE(&inode));
+ if (inode->i_links_count == 0) {
+ ret = translate_error(fs, ino, EXT2_ET_INODE_CORRUPTED);
+ goto out;
+ }
+ inode->i_links_count--;
+ if (!inode->i_links_count)
+ ext2fs_set_dtime(fs, EXT2_INODE(inode));
}
- ret = update_ctime(fs, ino, &inode);
+ ret = update_ctime(fs, ino, inode);
if (ret)
- return ret;
+ goto out;
/* Still linked? Leave it be. */
- if (inode.i_links_count)
+ if (inode->i_links_count)
goto write_out;
- if (ext2fs_has_feature_ea_inode(fs->super)) {
- ret = remove_ea_inodes(ff, ino, &inode);
- if (ret)
- return ret;
- }
-
/* Nobody holds this file; free its blocks! */
- err = ext2fs_free_ext_attr(fs, ino, &inode);
- if (err)
- return translate_error(fs, ino, err);
+ err = ext2fs_xattrs_release_all(fs, ino, inode, inode_size, NULL);
+ if (err) {
+ ret = translate_error(fs, ino, err);
+ goto out;
+ }
- if (ext2fs_inode_has_valid_blocks2(fs, EXT2_INODE(&inode))) {
- err = ext2fs_punch(fs, ino, EXT2_INODE(&inode), NULL,
+ if (ext2fs_inode_has_valid_blocks2(fs, EXT2_INODE(inode))) {
+ err = ext2fs_punch(fs, ino, EXT2_INODE(inode), NULL,
0, ~0ULL);
- if (err)
- return translate_error(fs, ino, err);
+ if (err) {
+ ret = translate_error(fs, ino, err);
+ goto out;
+ }
}
ext2fs_inode_alloc_stats2(fs, ino, -1,
- LINUX_S_ISDIR(inode.i_mode));
+ LINUX_S_ISDIR(inode->i_mode));
write_out:
- err = fuse2fs_write_inode(fs, ino, &inode);
+ err = ext2fs_write_inode_full(fs, ino, EXT2_INODE(inode), inode_size);
if (err)
- return translate_error(fs, ino, err);
+ ret = translate_error(fs, ino, err);
+out:
+ ext2fs_free_mem(&inode);
- return 0;
+ return ret;
}
static int __op_unlink(struct fuse2fs *ff, const char *path)
diff --git a/tests/d_xattr_ea_inode/expect b/tests/d_xattr_ea_inode/expect
index aaad9c5b3..e1878c3dc 100644
--- a/tests/d_xattr_ea_inode/expect
+++ b/tests/d_xattr_ea_inode/expect
@@ -135,3 +135,54 @@ Pass 5: Checking group summary information
test_filesys: 11/128 files (0.0% non-contiguous), 18/256 blocks
Exit status is 0
+write d_xattr_ea_inode.tmp test_file
+Allocated inode: 12
+Exit status is 0
+
+Generate xattr value (1024 bytes)
+ea_set -f d_xattr_ea_inode.tmp test_file user.test1
+Exit status is 0
+ea_get -f d_xattr_ea_inode.ver.tmp test_file user.test1
+Exit status is 0
+Compare xattr values (1024 bytes)
+stat test_file
+Blockcount: 16
+Exit status is 0
+e2fsck -yf -N test_filesys
+Pass 1: Checking inodes, blocks, and sizes
+Pass 2: Checking directory structure
+Pass 3: Checking directory connectivity
+Pass 4: Checking reference counts
+Pass 5: Checking group summary information
+test_filesys: 12/128 files (0.0% non-contiguous), 20/256 blocks
+Exit status is 0
+
+Generate xattr value (16384 bytes)
+ea_set -f d_xattr_ea_inode.tmp test_file user.test2
+Exit status is 0
+ea_get -f d_xattr_ea_inode.ver.tmp test_file user.test2
+Exit status is 0
+Compare xattr values (16384 bytes)
+stat test_file
+Blockcount: 48
+Exit status is 0
+e2fsck -yf -N test_filesys
+Pass 1: Checking inodes, blocks, and sizes
+Pass 2: Checking directory structure
+Pass 3: Checking directory connectivity
+Pass 4: Checking reference counts
+Pass 5: Checking group summary information
+test_filesys: 13/128 files (0.0% non-contiguous), 24/256 blocks
+Exit status is 0
+
+rm test_file
+
+Exit status is 0
+e2fsck -yf -N test_filesys
+Pass 1: Checking inodes, blocks, and sizes
+Pass 2: Checking directory structure
+Pass 3: Checking directory connectivity
+Pass 4: Checking reference counts
+Pass 5: Checking group summary information
+test_filesys: 11/128 files (0.0% non-contiguous), 18/256 blocks
+Exit status is 0
diff --git a/tests/d_xattr_ea_inode/script b/tests/d_xattr_ea_inode/script
index 84104549c..c24eb6cd5 100644
--- a/tests/d_xattr_ea_inode/script
+++ b/tests/d_xattr_ea_inode/script
@@ -15,32 +15,33 @@ VERIFY_DATA=$test_name.ver.tmp
echo "debugfs edit extended attributes with ea_inode feature" > $OUT.new
d_xattr_ea_inode_check() {
- local xattr_size=$1
- local xattr_name=$2
- local ea_rm=$3
+ local path=$1
+ local xattr_size=$2
+ local xattr_name=$3
+ local ea_rm=$4
echo "Generate xattr value ($xattr_size bytes)" >> $OUT.new
echo $xattr_size |
awk '{srand();for(i=0;i<$1;i++) printf("%c",97+int(rand()*26));}' > $TEST_DATA
- echo "ea_set -f $TEST_DATA / $xattr_name" >> $OUT.new
- $DEBUGFS -w -R "ea_set -f $TEST_DATA / $xattr_name" $TMPFILE >> $OUT.new 2>&1
+ echo "ea_set -f $TEST_DATA $path $xattr_name" >> $OUT.new
+ $DEBUGFS -w -R "ea_set -f $TEST_DATA $path $xattr_name" $TMPFILE >> $OUT.new 2>&1
echo Exit status is $? >> $OUT.new
- echo "ea_get -f $VERIFY_DATA / $xattr_name" >> $OUT.new
- $DEBUGFS -w -R "ea_get -f $VERIFY_DATA / $xattr_name" $TMPFILE >> $OUT.new 2>&1
+ echo "ea_get -f $VERIFY_DATA $path $xattr_name" >> $OUT.new
+ $DEBUGFS -w -R "ea_get -f $VERIFY_DATA $path $xattr_name" $TMPFILE >> $OUT.new 2>&1
echo Exit status is $? >> $OUT.new
echo "Compare xattr values ($xattr_size bytes)" >> $OUT.new
diff -u $TEST_DATA $VERIFY_DATA >> $OUT.new
- echo "stat /" >> $OUT.new
- ($DEBUGFS -c -R "stat /" $TMPFILE | grep -Eo "Blockcount: [0-9]+") >> $OUT.new 2>&1
+ echo "stat $path" >> $OUT.new
+ ($DEBUGFS -c -R "stat $path" $TMPFILE | grep -Eo "Blockcount: [0-9]+") >> $OUT.new 2>&1
echo Exit status is $? >> $OUT.new
if $ea_rm; then
- echo "ea_rm / $xattr_name" >> $OUT.new
- $DEBUGFS -w -R "ea_rm / $xattr_name" $TMPFILE >> $OUT.new 2>&1
+ echo "ea_rm $path $xattr_name" >> $OUT.new
+ $DEBUGFS -w -R "ea_rm $path $xattr_name" $TMPFILE >> $OUT.new 2>&1
echo Exit status is $? >> $OUT.new
fi
@@ -56,15 +57,33 @@ echo "mke2fs -Fq -b 4096 -O ea_inode test.img 1m" >> $OUT.new
$MKE2FS -Fq -b 4096 -O ea_inode $TMPFILE 1m > /dev/null 2>&1
echo Exit status is $? >> $OUT.new
-d_xattr_ea_inode_check 8292 user.test1 true
+d_xattr_ea_inode_check / 8292 user.test1 true
-d_xattr_ea_inode_check 4097 user.test1 false
-d_xattr_ea_inode_check 102 user.test2 false
-d_xattr_ea_inode_check 5005 user.test2 true
-d_xattr_ea_inode_check 512 user.test1 true
+d_xattr_ea_inode_check / 4097 user.test1 false
+d_xattr_ea_inode_check / 102 user.test2 false
+d_xattr_ea_inode_check / 5005 user.test2 true
+d_xattr_ea_inode_check / 512 user.test1 true
-d_xattr_ea_inode_check 1024 user.test1 false
-d_xattr_ea_inode_check 5000 user.test1 true
+d_xattr_ea_inode_check / 1024 user.test1 false
+d_xattr_ea_inode_check / 5000 user.test1 true
+
+# Create and remove a file with ea_inode
+echo "test_file_content" > $TEST_DATA
+echo "write $TEST_DATA test_file" >> $OUT.new
+$DEBUGFS -w -R "write $TEST_DATA test_file" $TMPFILE >> $OUT.new 2>&1
+echo Exit status is $? >> $OUT.new
+echo >> $OUT.new
+
+d_xattr_ea_inode_check test_file 1024 user.test1 false
+d_xattr_ea_inode_check test_file 16384 user.test2 false
+
+echo "rm test_file" >> $OUT.new
+$DEBUGFS -w -R "rm test_file" $TMPFILE >> $OUT.new 2>&1
+echo Exit status is $? >> $OUT.new
+
+echo e2fsck $VERIFY_FSCK_OPT -N test_filesys >> $OUT.new
+$FSCK $VERIFY_FSCK_OPT -N test_filesys $TMPFILE >> $OUT.new 2>&1
+echo Exit status is $? >> $OUT.new
sed -f $cmd_dir/filter.sed $OUT.new > $OUT
diff --git a/tests/f_orphan_ea_inode/expect.1 b/tests/f_orphan_ea_inode/expect.1
new file mode 100644
index 000000000..3eba3d718
--- /dev/null
+++ b/tests/f_orphan_ea_inode/expect.1
@@ -0,0 +1,6 @@
+test_filesys: Clearing orphaned inode 12 (uid=0, gid=0, mode=0100644, size=0)
+test_filesys: Clearing orphaned inode 13 (uid=1000, gid=1000, mode=0100644, size=6)
+test_filesys: Clearing orphaned inode 14 (uid=1001, gid=1001, mode=0100644, size=0)
+test_filesys: Clearing orphaned inode 15 (uid=1002, gid=1002, mode=0100644, size=6)
+test_filesys: clean, 13/128 files, 23/256 blocks
+Exit status is 0
diff --git a/tests/f_orphan_ea_inode/expect.2 b/tests/f_orphan_ea_inode/expect.2
new file mode 100644
index 000000000..bf76a5c25
--- /dev/null
+++ b/tests/f_orphan_ea_inode/expect.2
@@ -0,0 +1,7 @@
+Pass 1: Checking inodes, blocks, and sizes
+Pass 2: Checking directory structure
+Pass 3: Checking directory connectivity
+Pass 4: Checking reference counts
+Pass 5: Checking group summary information
+test_filesys: 13/128 files (0.0% non-contiguous), 23/256 blocks
+Exit status is 0
diff --git a/tests/f_orphan_ea_inode/image.gz b/tests/f_orphan_ea_inode/image.gz
new file mode 100644
index 0000000000000000000000000000000000000000..95f0e53aebd45e041b4b1f53be7c374c8144c1b7
GIT binary patch
literal 2139
zcmeH`ZB){C6vzM8Hrp}7g@%~MtYvweYvyWYCGO-yK4OW}V<wpzXd)%L%m=7B52Z~^
zQ={e6O5&KqaG4HETqaXUP01LVmg2*F0G7z3!f&v>*_*xGyS^{(x%d9g@7#Mo=U!CN
zx^;`kZBo{(O(w?UlOWkxD8Vt(5&DxMNiEw;EPH7y+Ew~8s5^)kG8_|gs>t&FoaZqP
zhX^f4FF$nsWQ%V%)-Su+(l6Bj)v?|nzO6mdz3EPOer?uZ1ll)s&Ai{i%Nu890TO2K
zshf0{b{<Zv_<<poPxa+^D#yc4?<l;OGK`3u+<CLg<*w-W<I&Z&TQxBKQZK^k`xB!I
z(xSjWKVP%@5e;nGe!!u<P+^A{(m#3T?#y#3+&#B^K;w?n8!FVs+wQPuX=y9mv_BLa
z=6Qv2PmV?vhTVTy7c;X9{Xof3m)GR|oFiSpu)UCoih#ockGL374P)C>)fKnfhQuM3
ztE3o$LWqHZD0#-n3TAl<f}q;T^&2Retol@MQ}DTA*g>%tf_w_xidVV}P2DUekE)eD
zT(0*hq{s=2DLZ|=cfjpP$NK%@=c#ATI5bt|@jRbe)6n{i=2q(KOUp^UXH_U#Uun@v
zibGUrxGp$!c<IY}PVE{YCR5k~q_VDufa}qrdE~6*s9_O;x6eT`O1@ed`+(FRn2_dR
zy?yp+&_3XFs02d_6Stp^N{Bi#Yr8ZraBTIL_E9OTb@d@SI&nN#{_FNup8eKF^qsOO
zqmqFvSOVkM=-~sio=0EA9y8h{L<fM1e&apib)L>gIAYUPiAMJD3-(ra9L!`-503X&
zkE^6#8;v0xBSQ8}OeIL;lcoEH!L8Ob?!DL*>IBEr%AXAqYhC(lf<e&c!9rpa1t~yN
zROqvHMj-DAi1se&rBWj}t3|)vFRr%EM^&ByvH|gy4IU$Un2?-Mx~$FA^ip}&LeisK
zMvT5Alb4%Cyn)UP1b6qGr{*f{1-#`qLPf<rz=XvKHf=M{A^}#ihNMxl8`(<!IBOM7
z=?Vu{qUa390%0_(p($2JAJg$)p88HTI~3?`q@4qkMSSqK%?ZGaTsb#=nv};~c|3`S
z;tkGKjZz$r3up$s<cf@zu0*Io+u5{!G4)1V1%K%aHgejF{rVU<@yn7Td!-oe>@~D*
z0Vc{mZQ6tXP$#y`cS52**Q|e%QQ8=WpBK$J4K_}_l~<2P(lgK;5~jzBUMu8J=*SB{
zu6o87DJL#ukb`Hs$o89qW=2h^DO_^T0O|gm1q7WOdI3WMASpos)5bw|%t04(St-?I
zI<+XXZ&F+7Op7CM4HI=7$IonKxlbh#x3CCTLZL{v1h~&|gDaytp6nCp!M9BLOtX(e
z)ppMY*hgR0Ug#Q`KxpXdycinpU<q<si|6gN=q#7j;8mhaAKp)|TL9rd#RA&=v>uJ!
zB?fibNa<QO0K9aI&P3I~31I2szuSflXauk3xuJ7kIrh1K({`9b!eg3<AD9nik=amQ
z4A7F1hyxGu5`+1`8GRLqB}7xt%qp6>bru0;9?UEe*0yT@Se{ymzx_(1Yw1fztv6+{
z&4>Pe{l|1(NBH&nZ1g*N%U^UYwDJ`y93DU>!vnq1xwwn%W;V>4G3=h2i~ss#c00=v
i9Rno8r?2x(P~Yhtfp-M{Zvr7`V4?Up>pcjvf&Kz@_kh^|
literal 0
HcmV?d00001
diff --git a/tests/f_orphan_ea_inode/name b/tests/f_orphan_ea_inode/name
new file mode 100644
index 000000000..b892ff960
--- /dev/null
+++ b/tests/f_orphan_ea_inode/name
@@ -0,0 +1 @@
+clearing orphan inodes with ea_inode and quota features
diff --git a/tests/f_orphan_ea_inode/script b/tests/f_orphan_ea_inode/script
new file mode 100644
index 000000000..9650d07d0
--- /dev/null
+++ b/tests/f_orphan_ea_inode/script
@@ -0,0 +1,3 @@
+FSCK_OPT=-p
+SECOND_FSCK_OPT="-yf"
+. $cmd_dir/run_e2fsck
--
2.43.7
^ permalink raw reply related [flat|nested] 5+ messages in thread