From patchwork Fri Aug 18 18:19:42 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andreas Dilger X-Patchwork-Id: 803366 Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=linux-ext4-owner@vger.kernel.org; receiver=) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 3xYs5N64vyz9t1t for ; Sat, 19 Aug 2017 04:27:52 +1000 (AEST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751446AbdHRS1w (ORCPT ); Fri, 18 Aug 2017 14:27:52 -0400 Received: from smtp-out-no.shaw.ca ([64.59.134.9]:54550 "EHLO smtp-out-no.shaw.ca" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751807AbdHRS1v (ORCPT ); Fri, 18 Aug 2017 14:27:51 -0400 X-Greylist: delayed 487 seconds by postgrey-1.27 at vger.kernel.org; Fri, 18 Aug 2017 14:27:51 EDT Received: from cabot.adilger.int ([70.77.216.213]) by shaw.ca with SMTP id ilryd3stNI8mCilrzd2OfX; Fri, 18 Aug 2017 12:19:44 -0600 X-Authority-Analysis: v=2.2 cv=HahkdmM8 c=1 sm=1 tr=0 a=BQvS1EmAg2ttxjPVUuc1UQ==:117 a=BQvS1EmAg2ttxjPVUuc1UQ==:17 a=VwQbUJbxAAAA:8 a=RPJ6JBhKAAAA:8 a=IHv0yzR9u9SmF7IoSq4A:9 a=AjGcO6oz07-iQ99wixmX:22 a=fa_un-3J20JGBB2Tu-mn:22 From: Andreas Dilger To: tytso@mit.edu Cc: linux-ext4@vger.kernel.org, Andreas Dilger Subject: [PATCH] e2fsck: set dir_nlink feature if large dir exists Date: Fri, 18 Aug 2017 12:19:42 -0600 Message-Id: <1503080382-37376-1-git-send-email-adilger@dilger.ca> X-Mailer: git-send-email 1.8.0 X-CMAE-Envelope: MS4wfNtXc0RDJLJfkKQlI7G1Q3is/qvEaR7cGISJatodu9fh6n/uPSi3XROOJxB4QtlXj1jt1fIWv205QksEYu+U4SQ6qAFh7PxPZI0rHT3ati2ma6Zk6ztk io5yM0HXiSdtHTOuUX9X4aEPgLpdQuUJ1aZczhIwh+LNtO4GcYQba2Z4gZcPaSxOoeE+kG0h30JkzpXe8NGan31Jby45Fxr6VlRJh33mHQLnm3GsZ9q47NdZ Sender: linux-ext4-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-ext4@vger.kernel.org If there is a directory with more than EXT2_LINK_MAX (65000) subdirectories, but the DIR_NLINK feature is not set in the superblock, the feature should be set before continuing on to change the on-disk directory link count to 1. While most filesystems should have DIR_NLINK set (it was set by default for all ext4 filesystems, and the kernel before 4.12 automatically set it if the directory link count grew too large), it is possible that this flag is lost due to disk corruption or for an upgraded filesystem. We no longer want the kernel to automatically enable this feature. Addresses: https://bugzilla.kernel.org/show_bug.cgi?id=196405 Signed-off-by: Andreas Dilger --- e2fsck/pass4.c | 12 +++++++++- e2fsck/problem.c | 5 ++++ e2fsck/problem.h | 3 +++ tests/f_large_dir/expect | 7 ++++-- tests/f_large_dir/script | 60 ++++++++++++++++++++++++++++++------------------ 5 files changed, 62 insertions(+), 25 deletions(-) diff --git a/e2fsck/pass4.c b/e2fsck/pass4.c index 663f87a..d0ff8e9 100644 --- a/e2fsck/pass4.c +++ b/e2fsck/pass4.c @@ -170,6 +170,7 @@ void e2fsck_pass4(e2fsck_t ctx) #endif struct problem_context pctx; __u16 link_count, link_counted; + int dir_nlink_fs; char *buf = 0; dgrp_t group, maxgroup; @@ -193,6 +194,8 @@ void e2fsck_pass4(e2fsck_t ctx) if (!(ctx->options & E2F_OPT_PREEN)) fix_problem(ctx, PR_4_PASS_HEADER, &pctx); + dir_nlink_fs = ext2fs_has_feature_dir_nlink(fs->super); + group = 0; maxgroup = fs->group_desc_count; if (ctx->progress) @@ -249,8 +252,15 @@ void e2fsck_pass4(e2fsck_t ctx) &link_counted); } isdir = ext2fs_test_inode_bitmap2(ctx->inode_dir_map, i); - if (isdir && (link_counted > EXT2_LINK_MAX)) + if (isdir && (link_counted > EXT2_LINK_MAX)) { + if (!dir_nlink_fs && + fix_problem(ctx, PR_4_DIR_NLINK_FEATURE, &pctx)) { + ext2fs_set_feature_dir_nlink(fs->super); + ext2fs_mark_super_dirty(fs); + dir_nlink_fs = 1; + } link_counted = 1; + } if (link_counted != link_count) { e2fsck_read_inode_full(ctx, i, EXT2_INODE(inode), inode_size, "pass4"); diff --git a/e2fsck/problem.c b/e2fsck/problem.c index 9706933..25c1de9 100644 --- a/e2fsck/problem.c +++ b/e2fsck/problem.c @@ -1873,6 +1873,11 @@ static struct e2fsck_problem problem_table[] = { N_("@a @i %i ref count is %N, @s %n. "), PROMPT_FIX, PR_PREEN_OK }, + /* directory exceeds max links, but no DIR_NLINK feature in superblock*/ + { PR_4_DIR_NLINK_FEATURE, + N_("@d exceeds max links, but no DIR_NLINK feature in @S.\n"), + PROMPT_FIX, 0 }, + /* Pass 5 errors */ /* Pass 5: Checking group summary information */ diff --git a/e2fsck/problem.h b/e2fsck/problem.h index f30f8f0..07ed0a7 100644 --- a/e2fsck/problem.h +++ b/e2fsck/problem.h @@ -1134,6 +1134,9 @@ struct problem_context { /* Extended attribute inode ref count wrong */ #define PR_4_EA_INODE_REF_COUNT 0x040005 +/* directory exceeds max links, but no DIR_NLINK feature in superblock */ +#define PR_4_DIR_NLINK_FEATURE 0x040006 + /* * Pass 5 errors */ diff --git a/tests/f_large_dir/expect b/tests/f_large_dir/expect index b099460..4b9ca6f 100644 --- a/tests/f_large_dir/expect +++ b/tests/f_large_dir/expect @@ -3,10 +3,13 @@ Pass 2: Checking directory structure Pass 3: Checking directory connectivity Pass 3A: Optimizing directories Pass 4: Checking reference counts -Inode 13 ref count is 1, should be 47245. Fix? yes +Directory exceeds max links, but no DIR_NLINK feature in superblock. +Fix? yes + +Inode 12 ref count is 65012, should be 1. Fix? yes Pass 5: Checking group summary information test.img: ***** FILE SYSTEM WAS MODIFIED ***** -test.img: 13/115368 files (0.0% non-contiguous), 32817/460800 blocks +test.img: 65023/65104 files (0.0% non-contiguous), 96668/100937 blocks Exit status is 1 diff --git a/tests/f_large_dir/script b/tests/f_large_dir/script index 0b5fdff..a10fe16 100644 --- a/tests/f_large_dir/script +++ b/tests/f_large_dir/script @@ -5,43 +5,59 @@ E2FSCK=../e2fsck/e2fsck NAMELEN=255 DIRENT_SZ=8 BLOCKSZ=1024 +INODESZ=128 DIRENT_PER_LEAF=$((BLOCKSZ / (NAMELEN + DIRENT_SZ))) HEADER=32 INDEX_SZ=8 INDEX_L1=$(((BLOCKSZ - HEADER) / INDEX_SZ)) INDEX_L2=$(((BLOCKSZ - DIRENT_SZ) / INDEX_SZ)) ENTRIES=$((INDEX_L1 * INDEX_L2 * DIRENT_PER_LEAF)) +DIRBLK=$((2 + INDEX_L1 * INDEX_L2)) +EXT4_LINK_MAX=65000 +[ $ENTRIES -lt $((EXT4_LINK_MAX + 10)) ] && ENTRIES=$((EXT4_LINK_MAX + 10)) +FSIZE=$(((DIRBLK + EXT4_LINK_MAX * ((BLOCKSZ + INODESZ) / BLOCKSZ)) * 5 / 4)) -cp /dev/null $OUT -$MKE2FS -b 1024 -O large_dir,uninit_bg,dir_nlink -F $TMPFILE 460800 \ - > /dev/null 2>&1 +> $OUT +$MKE2FS -b 1024 -O large_dir,uninit_bg -N $((ENTRIES + 50)) \ + -I $INODESZ -F $TMPFILE $FSIZE > $OUT 2>&1 +RC=$? +if [ $RC -eq 0 ]; then { - echo "feature large_dir" + START=$SECONDS echo "mkdir /foo" echo "cd /foo" - touch foofile - echo "write foofile foofile" + touch $TMPFILE.tmp + echo "write $TMPFILE.tmp foofile" i=0 - while test $i -lt $ENTRIES ; do - if test $(( i % DIRENT_PER_LEAF )) -eq 0 ; then - echo "expand ./" + while test $i -lt $ENTRIES ; do + if test $((i % DIRENT_PER_LEAF)) -eq 0; then + echo "expand ./" fi - if test $(( i % 5000 )) -eq 0 -a $i -gt 0 ; then - >&2 echo "$test_name: $i processed" + if test $((i % 5000)) -eq 0 -a $i -gt 0; then + ELAPSED=$((SECONDS - START)) + RATE=$((i / ELAPSED)) + >&2 echo "$test_name: $i processed in ${ELAPSED}s @ $RATE/s" fi - printf "ln foofile %0255X\n" $i - i=$(($i + 1)) + if test $i -lt $((EXT4_LINK_MAX + 10)); then + printf "mkdir d%0254u\n" $i + else + printf "ln foofile f%0254u\n" $i + fi + i=$((i + 1)) done -} | $DEBUGFS -w -f /dev/stdin $TMPFILE > /dev/null 2>&1 - -$E2FSCK -yfD $TMPFILE > $OUT.new 2>&1 -status=$? -echo Exit status is $status >> $OUT.new -sed -f $cmd_dir/filter.sed -e "s;$TMPFILE;test.img;" $OUT.new >> $OUT -rm -f $OUT.new +} | $DEBUGFS -w -f /dev/stdin $TMPFILE > $OUT + RC=$? +fi +if [ $RC -eq 0 ]; then + $E2FSCK -yfD $TMPFILE > $OUT.new 2>&1 + status=$? + echo "Exit status is $status" >> $OUT.new + sed -f $cmd_dir/filter.sed -e "s;$TMPFILE;test.img;" $OUT.new > $OUT + rm -f $OUT.new -cmp -s $OUT $EXP -RC=$? + cmp -s $OUT $EXP + RC=$? +fi if [ $RC -eq 0 ]; then echo "$test_name: $test_description: ok" touch $test_name.ok