aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--e2fsck/super.c121
-rw-r--r--lib/support/mkquota.c12
-rw-r--r--tests/f_orphquot/expect10
-rw-r--r--tests/f_orphquot/image.bz2bin0 -> 1327 bytes
-rw-r--r--tests/f_orphquot/script26
5 files changed, 145 insertions, 24 deletions
diff --git a/e2fsck/super.c b/e2fsck/super.c
index 5e29b64ef..9c0e0e7b3 100644
--- a/e2fsck/super.c
+++ b/e2fsck/super.c
@@ -72,6 +72,7 @@ struct process_block_struct {
int abort;
errcode_t errcode;
blk64_t last_cluster;
+ struct ext2_inode_large *inode;
};
static int release_inode_block(ext2_filsys fs,
@@ -168,6 +169,8 @@ static int release_inode_block(ext2_filsys fs,
retval |= BLOCK_CHANGED;
}
+ 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;
@@ -179,15 +182,16 @@ static int release_inode_block(ext2_filsys fs,
* not deleted.
*/
static int release_inode_blocks(e2fsck_t ctx, ext2_ino_t ino,
- struct ext2_inode *inode, char *block_buf,
+ 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;
- if (!ext2fs_inode_has_valid_blocks2(fs, inode))
+ if (!ext2fs_inode_has_valid_blocks2(fs, EXT2_INODE(inode)))
return 0;
pb.buf = block_buf + 3 * ctx->fs->blocksize;
@@ -196,6 +200,7 @@ static int release_inode_blocks(e2fsck_t ctx, ext2_ino_t ino,
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)
@@ -220,15 +225,17 @@ static int release_inode_blocks(e2fsck_t ctx, ext2_ino_t ino,
return 1;
/* Refresh the inode since ext2fs_block_iterate may have changed it */
- e2fsck_read_inode(ctx, ino, inode, "release_inode_blocks");
+ e2fsck_read_inode_full(ctx, ino, EXT2_INODE(inode), sizeof(*inode),
+ "release_inode_blocks");
if (pb.truncated_blocks)
- ext2fs_iblk_sub_blocks(fs, inode, pb.truncated_blocks);
+ ext2fs_iblk_sub_blocks(fs, EXT2_INODE(inode),
+ pb.truncated_blocks);
- if (ext2fs_file_acl_block(fs, inode)) {
- retval = ext2fs_adjust_ea_refcount3(fs,
- ext2fs_file_acl_block(fs, inode),
- block_buf, -1, &count, ino);
+ 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;
@@ -240,15 +247,68 @@ static int release_inode_blocks(e2fsck_t ctx, ext2_ino_t ino,
return 1;
}
if (count == 0) {
- ext2fs_block_alloc_stats2(fs,
- ext2fs_file_acl_block(fs, inode), -1);
+ 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, inode, 0);
+ ext2fs_file_acl_block_set(fs, EXT2_INODE(inode), 0);
}
return 0;
}
+/* Load all quota data in preparation for orphan clearing. */
+static errcode_t e2fsck_read_all_quotas(e2fsck_t ctx)
+{
+ ext2_ino_t qf_ino;
+ enum quota_type qtype;
+ errcode_t retval = 0;
+
+ if (!ext2fs_has_feature_quota(ctx->fs->super))
+ return retval;
+
+ retval = quota_init_context(&ctx->qctx, ctx->fs, 0);
+ if (retval)
+ return retval;
+
+ for (qtype = 0 ; qtype < MAXQUOTAS; qtype++) {
+ qf_ino = *quota_sb_inump(ctx->fs->super, qtype);
+ if (qf_ino == 0)
+ continue;
+
+ retval = quota_update_limits(ctx->qctx, qf_ino, qtype);
+ if (retval)
+ break;
+ }
+ if (retval)
+ quota_release_context(&ctx->qctx);
+ return retval;
+}
+
+/* Write all the quota info to disk. */
+static errcode_t e2fsck_write_all_quotas(e2fsck_t ctx)
+{
+ struct problem_context pctx;
+ enum quota_type qtype;
+
+ if (!ext2fs_has_feature_quota(ctx->fs->super))
+ return 0;
+
+ clear_problem_context(&pctx);
+ for (qtype = 0 ; qtype < MAXQUOTAS; qtype++) {
+ pctx.num = qtype;
+ pctx.errcode = quota_write_inode(ctx->qctx, 1 << qtype);
+ if (pctx.errcode) {
+ fix_problem(ctx, PR_6_WRITE_QUOTAS, &pctx);
+ break;
+ }
+ }
+
+ quota_release_context(&ctx->qctx);
+ return pctx.errcode;
+}
+
/*
* This function releases all of the orphan inodes. It returns 1 if
* it hit some error, and 0 on success.
@@ -257,13 +317,20 @@ static int release_orphan_inodes(e2fsck_t ctx)
{
ext2_filsys fs = ctx->fs;
ext2_ino_t ino, next_ino;
- struct ext2_inode inode;
+ struct ext2_inode_large inode;
struct problem_context pctx;
char *block_buf;
if ((ino = fs->super->s_last_orphan) == 0)
return 0;
+ clear_problem_context(&pctx);
+ pctx.errcode = e2fsck_read_all_quotas(ctx);
+ if (pctx.errcode) {
+ fix_problem(ctx, PR_0_QUOTA_INIT_CTX, &pctx);
+ return 1;
+ }
+
/*
* Win or lose, we won't be using the head of the orphan inode
* list again.
@@ -276,15 +343,18 @@ static int release_orphan_inodes(e2fsck_t ctx)
* list, since the orphan list can't be trusted; and we're
* going to be running a full e2fsck run anyway...
*/
- if (fs->super->s_state & EXT2_ERROR_FS)
+ if (fs->super->s_state & EXT2_ERROR_FS) {
+ if (ctx->qctx)
+ quota_release_context(&ctx->qctx);
return 0;
+ }
if ((ino < EXT2_FIRST_INODE(fs->super)) ||
(ino > fs->super->s_inodes_count)) {
clear_problem_context(&pctx);
pctx.ino = ino;
fix_problem(ctx, PR_0_ORPHAN_ILLEGAL_HEAD_INODE, &pctx);
- return 1;
+ goto err_qctx;
}
block_buf = (char *) e2fsck_allocate_memory(ctx, fs->blocksize * 4,
@@ -292,10 +362,11 @@ static int release_orphan_inodes(e2fsck_t ctx)
e2fsck_read_bitmaps(ctx);
while (ino) {
- e2fsck_read_inode(ctx, ino, &inode, "release_orphan_inodes");
+ e2fsck_read_inode_full(ctx, ino, EXT2_INODE(&inode),
+ sizeof(inode), "release_orphan_inodes");
clear_problem_context(&pctx);
pctx.ino = ino;
- pctx.inode = &inode;
+ pctx.inode = EXT2_INODE(&inode);
pctx.str = inode.i_links_count ? _("Truncating") :
_("Clearing");
@@ -307,13 +378,15 @@ static int release_orphan_inodes(e2fsck_t ctx)
(next_ino > fs->super->s_inodes_count))) {
pctx.ino = next_ino;
fix_problem(ctx, PR_0_ORPHAN_ILLEGAL_INODE, &pctx);
- goto return_abort;
+ goto err_buf;
}
if (release_inode_blocks(ctx, ino, &inode, block_buf, &pctx))
- goto return_abort;
+ goto err_buf;
if (!inode.i_links_count) {
+ if (ctx->qctx)
+ quota_data_inodes(ctx->qctx, &inode, ino, -1);
ext2fs_inode_alloc_stats2(fs, ino, -1,
LINUX_S_ISDIR(inode.i_mode));
ctx->free_inodes++;
@@ -321,13 +394,21 @@ static int release_orphan_inodes(e2fsck_t ctx)
} else {
inode.i_dtime = 0;
}
- e2fsck_write_inode(ctx, ino, &inode, "delete_file");
+ e2fsck_write_inode_full(ctx, ino, EXT2_INODE(&inode),
+ sizeof(inode), "delete_file");
ino = next_ino;
}
ext2fs_free_mem(&block_buf);
+ pctx.errcode = e2fsck_write_all_quotas(ctx);
+ if (pctx.errcode)
+ goto err;
return 0;
-return_abort:
+err_buf:
ext2fs_free_mem(&block_buf);
+err_qctx:
+ if (ctx->qctx)
+ quota_release_context(&ctx->qctx);
+err:
return 1;
}
diff --git a/lib/support/mkquota.c b/lib/support/mkquota.c
index e65c95b76..efc37cb49 100644
--- a/lib/support/mkquota.c
+++ b/lib/support/mkquota.c
@@ -516,6 +516,7 @@ struct scan_dquots_data {
dict_t *quota_dict;
int update_limits; /* update limits from disk */
int update_usage;
+ int check_consistency;
int usage_is_inconsistent;
};
@@ -533,8 +534,9 @@ static int scan_dquots_callback(struct dquot *dquot, void *cb_data)
print_dquot("dsk", dquot);
/* Check if there is inconsistency */
- if (dq->dq_dqb.dqb_curspace != dquot->dq_dqb.dqb_curspace ||
- dq->dq_dqb.dqb_curinodes != dquot->dq_dqb.dqb_curinodes) {
+ if (scan_data->check_consistency &&
+ (dq->dq_dqb.dqb_curspace != dquot->dq_dqb.dqb_curspace ||
+ dq->dq_dqb.dqb_curinodes != dquot->dq_dqb.dqb_curinodes)) {
scan_data->usage_is_inconsistent = 1;
fprintf(stderr, "[QUOTA WARNING] Usage inconsistent for ID %u:"
"actual (%lld, %lld) != expected (%lld, %lld)\n",
@@ -568,8 +570,9 @@ static errcode_t quota_read_all_dquots(struct quota_handle *qh,
struct scan_dquots_data scan_data;
scan_data.quota_dict = qctx->quota_dict[qh->qh_type];
- scan_data.update_limits = update_limits;
- scan_data.update_usage = 0;
+ scan_data.check_consistency = 0;
+ scan_data.update_limits = 0;
+ scan_data.update_usage = 1;
return qh->qh_ops->scan_dquots(qh, scan_dquots_callback, &scan_data);
}
@@ -659,6 +662,7 @@ errcode_t quota_compare_and_update(quota_ctx_t qctx, enum quota_type qtype,
scan_data.quota_dict = qctx->quota_dict[qtype];
scan_data.update_limits = 1;
scan_data.update_usage = 0;
+ scan_data.check_consistency = 1;
scan_data.usage_is_inconsistent = 0;
err = qh.qh_ops->scan_dquots(&qh, scan_dquots_callback, &scan_data);
if (err) {
diff --git a/tests/f_orphquot/expect b/tests/f_orphquot/expect
new file mode 100644
index 000000000..90a78130b
--- /dev/null
+++ b/tests/f_orphquot/expect
@@ -0,0 +1,10 @@
+Clearing orphaned inode 12 (uid=0, gid=0, mode=0100644, size=3842048)
+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_filesystem: ***** FILE SYSTEM WAS MODIFIED *****
+test_filesystem: 11/512 files (9.1% non-contiguous), 1070/2048 blocks
+Exit status is 0
diff --git a/tests/f_orphquot/image.bz2 b/tests/f_orphquot/image.bz2
new file mode 100644
index 000000000..44c831852
--- /dev/null
+++ b/tests/f_orphquot/image.bz2
Binary files differ
diff --git a/tests/f_orphquot/script b/tests/f_orphquot/script
new file mode 100644
index 000000000..acdf5670c
--- /dev/null
+++ b/tests/f_orphquot/script
@@ -0,0 +1,26 @@
+test_description="e2fsck with quota and orphan inodes"
+OUT=$test_name.log
+EXP=$test_dir/expect
+
+bzip2 -dc < $test_dir/image.bz2 > $TMPFILE
+
+rm -rf $OUT
+$FSCK -f -y -N test_filesystem $TMPFILE > $OUT.new 2>&1
+status=$?
+echo Exit status is $status >> $OUT.new
+sed -f $cmd_dir/filter.sed $OUT.new >> $OUT
+rm -f $OUT.new
+
+cmp -s $OUT $EXP
+status=$?
+
+if [ "$status" = 0 ] ; 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
+ rm -f tmp_expect
+fi
+
+unset IMAGE FSCK_OPT OUT EXP