diff --git a/mkfs/xfs_mkfs.c b/mkfs/xfs_mkfs.c index 36360ba7d7f0d0bad8392b7e3b95d0b6ad01df90..6d0109a88bacd9e61033a3fe4890f719070b3d25 100644 --- a/mkfs/xfs_mkfs.c +++ b/mkfs/xfs_mkfs.c @@ -122,8 +122,11 @@ enum { M_MAX_OPTS, }; -/* Just define the max options array size manually right now */ -#define MAX_SUBOPTS D_MAX_OPTS +/* + * Just define the max options array size manually to the largest + * enum right now, leaving room for a NULL terminator at the end + */ +#define MAX_SUBOPTS (D_MAX_OPTS + 1) #define SUBOPT_NEEDS_VAL (-1LL) #define MAX_CONFLICTS 8 @@ -224,6 +227,7 @@ static struct opt_params bopts = { .name = 'b', .subopts = { [B_SIZE] = "size", + [B_MAX_OPTS] = NULL, }, .subopt_params = { { .index = B_SIZE, @@ -255,6 +259,7 @@ static struct opt_params dopts = { [D_PROJINHERIT] = "projinherit", [D_EXTSZINHERIT] = "extszinherit", [D_COWEXTSIZE] = "cowextsize", + [D_MAX_OPTS] = NULL, }, .subopt_params = { { .index = D_AGCOUNT, @@ -384,6 +389,7 @@ static struct opt_params iopts = { [I_ATTR] = "attr", [I_PROJID32BIT] = "projid32bit", [I_SPINODES] = "sparse", + [I_MAX_OPTS] = NULL, }, .subopt_params = { { .index = I_ALIGN, @@ -449,6 +455,7 @@ static struct opt_params lopts = { [L_FILE] = "file", [L_NAME] = "name", [L_LAZYSBCNTR] = "lazy-count", + [L_MAX_OPTS] = NULL, }, .subopt_params = { { .index = L_AGNUM, @@ -540,6 +547,7 @@ static struct opt_params nopts = { [N_SIZE] = "size", [N_VERSION] = "version", [N_FTYPE] = "ftype", + [N_MAX_OPTS] = NULL, }, .subopt_params = { { .index = N_SIZE, @@ -574,6 +582,7 @@ static struct opt_params ropts = { [R_FILE] = "file", [R_NAME] = "name", [R_NOALIGN] = "noalign", + [R_MAX_OPTS] = NULL, }, .subopt_params = { { .index = R_EXTSIZE, @@ -620,6 +629,7 @@ static struct opt_params sopts = { .subopts = { [S_SIZE] = "size", [S_SECTSIZE] = "sectsize", + [S_MAX_OPTS] = NULL, }, .subopt_params = { { .index = S_SIZE, @@ -655,6 +665,7 @@ static struct opt_params mopts = { [M_REFLINK] = "reflink", [M_INOBTCNT] = "inobtcount", [M_BIGTIME] = "bigtime", + [M_MAX_OPTS] = NULL, }, .subopt_params = { { .index = M_CRC, @@ -3165,6 +3176,49 @@ validate_log_size(uint64_t logblocks, int blocklog, int min_logblocks) } } +static void +adjust_ag0_internal_logblocks( + struct mkfs_params *cfg, + struct xfs_mount *mp, + int min_logblocks, + int *max_logblocks) +{ + int backoff = 0; + int ichunk_blocks; + + /* + * mkfs will trip over the write verifiers if the log is allocated in + * AG 0 and consumes enough space that we cannot allocate a non-sparse + * inode chunk for the root directory. The inode allocator requires + * that the AG have enough free space for the chunk itself plus enough + * to fix up the freelist with aligned blocks if we need to fill the + * allocation from the AGFL. + */ + ichunk_blocks = XFS_INODES_PER_CHUNK * cfg->inodesize >> cfg->blocklog; + backoff = ichunk_blocks * 4; + + /* + * We try to align inode allocations to the data device stripe unit, + * so ensure there's enough space to perform an aligned allocation. + * The inode geometry structure isn't set up yet, so compute this by + * hand. + */ + backoff = max(backoff, cfg->dsunit * 2); + + *max_logblocks -= backoff; + + /* If the specified log size is too big, complain. */ + if (cli_opt_set(&lopts, L_SIZE) && cfg->logblocks > *max_logblocks) { + fprintf(stderr, +_("internal log size %lld too large, must be less than %d\n"), + (long long)cfg->logblocks, + *max_logblocks); + usage(); + } + + cfg->logblocks = min(cfg->logblocks, *max_logblocks); +} + static void calculate_log_size( struct mkfs_params *cfg, @@ -3173,6 +3227,7 @@ calculate_log_size( { struct xfs_sb *sbp = &mp->m_sb; int min_logblocks; + int max_logblocks; /* absolute max for this AG */ struct xfs_mount mount; /* we need a temporary mount to calculate the minimum log size. */ @@ -3212,6 +3267,19 @@ _("external log device %lld too small, must be at least %lld blocks\n"), return; } + /* + * Make sure the log fits wholly within an AG + * + * XXX: If agf->freeblks ends up as 0 because the log uses all + * the free space, it causes the kernel all sorts of problems + * with per-ag reservations. Right now just back it off one + * block, but there's a whole can of worms here that needs to be + * opened to decide what is the valid maximum size of a log in + * an AG. + */ + max_logblocks = libxfs_alloc_ag_max_usable(mp) - 1; + + /* internal log - if no size specified, calculate automatically */ if (!cfg->logblocks) { if (cfg->dblocks < GIGABYTES(1, cfg->blocklog)) { @@ -3237,21 +3305,10 @@ _("external log device %lld too small, must be at least %lld blocks\n"), cfg->logblocks = cfg->logblocks >> cfg->blocklog; } - /* Ensure the chosen size meets minimum log size requirements */ + /* Ensure the chosen size fits within log size requirements */ cfg->logblocks = max(min_logblocks, cfg->logblocks); - /* - * Make sure the log fits wholly within an AG - * - * XXX: If agf->freeblks ends up as 0 because the log uses all - * the free space, it causes the kernel all sorts of problems - * with per-ag reservations. Right now just back it off one - * block, but there's a whole can of worms here that needs to be - * opened to decide what is the valid maximum size of a log in - * an AG. - */ - cfg->logblocks = min(cfg->logblocks, - libxfs_alloc_ag_max_usable(mp) - 1); + cfg->logblocks = min(cfg->logblocks, max_logblocks); /* and now clamp the size to the maximum supported size */ cfg->logblocks = min(cfg->logblocks, XFS_MAX_LOG_BLOCKS); @@ -3259,6 +3316,13 @@ _("external log device %lld too small, must be at least %lld blocks\n"), cfg->logblocks = XFS_MAX_LOG_BYTES >> cfg->blocklog; validate_log_size(cfg->logblocks, cfg->blocklog, min_logblocks); + } else if (cfg->logblocks > max_logblocks) { + /* check specified log size */ + fprintf(stderr, +_("internal log size %lld too large, must be less than %d\n"), + (long long)cfg->logblocks, + max_logblocks); + usage(); } if (cfg->logblocks > sbp->sb_agblocks - libxfs_prealloc_blocks(mp)) { @@ -3280,6 +3344,10 @@ _("log ag number %lld too large, must be less than %lld\n"), } else cfg->logagno = (xfs_agnumber_t)(sbp->sb_agcount / 2); + if (cfg->logagno == 0) + adjust_ag0_internal_logblocks(cfg, mp, min_logblocks, + &max_logblocks); + cfg->logstart = XFS_AGB_TO_FSB(mp, cfg->logagno, libxfs_prealloc_blocks(mp)); diff --git a/repair/attr_repair.c b/repair/attr_repair.c index 5ad81c091ea4d3317d1288ee07166ea6e700a52b..8cd9e2de75deeb33dd0bbf8236f2afb3d604b84b 100644 --- a/repair/attr_repair.c +++ b/repair/attr_repair.c @@ -584,6 +584,26 @@ process_leaf_attr_block( firstb = mp->m_sb.sb_blocksize; stop = xfs_attr3_leaf_hdr_size(leaf); + /* + * Empty leaf blocks at offset zero can occur as a race between + * setxattr and the system going down, so we only take action if we're + * running in modify mode. See xfs_attr3_leaf_verify for details of + * how we've screwed this up many times. + */ + if (!leafhdr.count && da_bno == 0) { + if (no_modify) { + do_log( + _("would clear empty leaf attr block 0, inode %" PRIu64 "\n"), + ino); + return 0; + } + + do_warn( + _("will clear empty leaf attr block 0, inode %" PRIu64 "\n"), + ino); + return 1; + } + /* does the count look sorta valid? */ if (!leafhdr.count || leafhdr.count * sizeof(xfs_attr_leaf_entry_t) + stop >