Hi folks, I'm going to put my 2c worth in here in the form of a patch. The tl;dr of it all is that I think we need to reset and reflect on what I was originally trying to acheive with the table based option parsing: factoring and simplifying mkfs into an easy to understand, maintainable code base.... And, while I remember, there's a handful of input validation bugs I found in the code whiel I was doing this (like missing conflict checks). Anyway, have a look, based on the current for-next branch. > >> [1] # mkfs.xfs -dfile,name=fsfile,size=1g,sectsize=4k -lfile,name=logfile,size=512m,sectsize=512 > > > > I see -d sectsize is in the --help screen but not the manpage. Can we > > fix that? > > I made it, but Dave would rather see the -d sectsize option removed. > Which I'm not sure about... > See "[PATCH] xfsprogs: add sectsize/sectlog to the man page" Slash and burn - there is so much useless, redundant crap in the CLI we've been holding onto for 15 years that we should just get rid of it. That's what I was intending to do originally with this rework and I still see no reason why we should be keeping stuff that just causes user confusion and implemention complexity. Cheers, Dave. -- Dave Chinner david@xxxxxxxxxxxxx mkfs.xfs: factor the crap out of input parsing From: Dave Chinner <dchinner@xxxxxxxxxx> Up front: this compiles, but I haven't even smoke tested it. It's a patch for discussion and reflection, not for testing.... After spending most of today talking to Eric about this mkfs factoring that doesn't seem to be going in the right direction, I figured I'd spend the afternoon and pick up where I left the original patchset I wrote all those years ago. The primary goal I had for that work was to make mkfs.xfs maintainable and lay a solid foundation for being able to modify it in future. The goal wasn't to store all the config information the options table - the goal was to provide a structure that we could refactor the code around. We seem to have lost sight of this. So, what this patch does is make a start on factoring the crap out of the input parsing and parameter validation. It's based on the current for-next tree, so I've ignored all the other patches that are out there, even though there are some with changes in them that are useful and good. Instead, I hacked together a quick global "cli geometry" structure that holds the handful of values that can be set from the CLI, and pushed all the other feature flag things into the existing sb_feat global structure. I then factored the sub options parsing loop using a table setup with to call a suboption specific parser function that does the table based option parsing. This gets *all* the suboption parsing out of the main option parsing loop which is soooo much simpler: case 'b': case 'd': case 'i': case 'l': case 'm': case 'n': case 'r': case 's': parse_subopts(c, optarg); break; Having done that, I started on the mess that followed the option parsing loop and started separating that out into functions that validate and set a clear set of parameters that the mkfs code then later uses. Hence we end up with code like this in the main function. /* * Extract as much of the valid config as we can from the CLI input * before opening the libxfs devices. */ validate_blocksize(&blocksize, &blocklog); validate_sectorsize(§orsize, §orlog, &ft, dfile, Nflag, force_overwrite); validate_log_sectorsize(&lsectorsize, &lsectorlog, sectorsize, sectorlog); validate_sb_features(); validate_dirblocksize(&dirblocksize, &dirblocklog, blocklog); validate_inodesize(&isize, &inodelog, &inopblock, blocklog); It's far, far easier to see what mkfs is doing now, and what appears to be random futzing with flags and input parameters has turned into code that has clear and obvious meaning. This is what the table based input option parsing was supposed to lead to. Instead of a forest of similar but not at all the same flags and varaibles, we get clear, obvious code that can then be easily understood. Then we can abstract out all the calculated and validated config variables into a single structure that we then use to build the on-disk filesystem structure. And once the building of the fs structure uses abstracted config structures, we can look at adding new input and output methods that manipulate those config structures rather than the CLI option parsing table..... Luis, some of this factoring isn't that much different in concept from your original patch set for config file support. The difference is in the way it is acheived - by abstracting it into individual option parameter type parser functions. Jan - the combining of all the option parameter structures is similar in concept to your merged tables, but it does it via a separate table designed specifically the generic sub-option parsing code. So some of the concepts should be recognisable, the difference is in the simplicity of implementation. Now, I need to be clear here: do not think that the hacky "cli geometry" structure is a permanent fixture or will even last through tomorrow. It's just a means to an end to make the initial factoring simple and fast. That's the mistake that's been made with the option parsing - we've ended up focussing on the option parsing table rather than where the option table parsing was supposed to take us. Keep in mind that I said to Eric 4 hours ago "I might send a patch" less than 5 hours ago because it's the best way to get people to understand why I think we need to reset and where we need to go first before even thinking about other ways of getting config information into and out of mkfs. So, yeah, there's still a lot of work in this to finish it off to the point where I can think about how to split it for review (maybe another full day of uninterrrupted hacking on my part), but I'm done for the day now and I think everyone needs to have a look and a think about it before I get back to it in the morning... Signed-Off-By: Dave Chinner <dchinner@xxxxxxxxxx> --- mkfs/xfs_mkfs.c | 1690 ++++++++++++++++++++++++++++++------------------------- 1 file changed, 916 insertions(+), 774 deletions(-) diff --git a/mkfs/xfs_mkfs.c b/mkfs/xfs_mkfs.c index 7bb6408f9b77..aeb081add83e 100644 --- a/mkfs/xfs_mkfs.c +++ b/mkfs/xfs_mkfs.c @@ -24,11 +24,11 @@ /* * Prototypes for internal functions. */ -static void conflict(char opt, char *tab[], int oldidx, int newidx); +static void conflict(char opt, const char *tab[], int oldidx, int newidx); static void illegal(const char *value, const char *opt); static __attribute__((noreturn)) void usage (void); -static __attribute__((noreturn)) void reqval(char opt, char *tab[], int idx); -static void respec(char opt, char *tab[], int idx); +static __attribute__((noreturn)) void reqval(char opt, const char *tab[], int idx); +static void respec(char opt, const char *tab[], int idx); static void unknown(char opt, char *s); static int ispow2(unsigned int i); @@ -428,6 +428,8 @@ struct opt_params lopts = { { .index = L_INTERNAL, .conflicts = { L_FILE, L_DEV, + L_SECTLOG, + L_SECTSIZE, LAST_CONFLICT }, .minval = 0, .maxval = 1, @@ -469,6 +471,7 @@ struct opt_params lopts = { }, { .index = L_SECTLOG, .conflicts = { L_SECTSIZE, + L_INTERNAL, LAST_CONFLICT }, .minval = XFS_MIN_SECTORSIZE_LOG, .maxval = XFS_MAX_SECTORSIZE_LOG, @@ -476,6 +479,7 @@ struct opt_params lopts = { }, { .index = L_SECTSIZE, .conflicts = { L_SECTLOG, + L_INTERNAL, LAST_CONFLICT }, .convert = true, .is_power_2 = true, @@ -729,74 +733,6 @@ struct opt_params mopts = { */ #define WHACK_SIZE (128 * 1024) -/* - * Convert lsu to lsunit for 512 bytes blocks and check validity of the values. - */ -static void -calc_stripe_factors( - int dsu, - int dsw, - int dsectsz, - int lsu, - int lsectsz, - int *dsunit, - int *dswidth, - int *lsunit) -{ - /* Handle data sunit/swidth options */ - if ((*dsunit && !*dswidth) || (!*dsunit && *dswidth)) { - fprintf(stderr, - _("both data sunit and data swidth options " - "must be specified\n")); - usage(); - } - - if (dsu || dsw) { - if ((dsu && !dsw) || (!dsu && dsw)) { - fprintf(stderr, - _("both data su and data sw options " - "must be specified\n")); - usage(); - } - - if (dsu % dsectsz) { - fprintf(stderr, - _("data su must be a multiple of the " - "sector size (%d)\n"), dsectsz); - usage(); - } - - *dsunit = (int)BTOBBT(dsu); - *dswidth = *dsunit * dsw; - } - - if (*dsunit && (*dswidth % *dsunit != 0)) { - fprintf(stderr, - _("data stripe width (%d) must be a multiple of the " - "data stripe unit (%d)\n"), *dswidth, *dsunit); - usage(); - } - - /* Handle log sunit options */ - - if (lsu) - *lsunit = (int)BTOBBT(lsu); - - /* verify if lsu/lsunit is a multiple block size */ - if (lsu % blocksize != 0) { - fprintf(stderr, -_("log stripe unit (%d) must be a multiple of the block size (%d)\n"), - lsu, blocksize); - exit(1); - } - if ((BBTOB(*lsunit) % blocksize != 0)) { - fprintf(stderr, -_("log stripe unit (%d) must be a multiple of the block size (%d)\n"), - BBTOB(*lsunit), blocksize); - exit(1); - } -} - static void check_device_type( const char *name, @@ -1161,6 +1097,31 @@ struct sb_feat_args { bool parent_pointers; bool rmapbt; bool reflink; + bool nodalign; + bool nortalign; + uuid_t m_uuid; +}; + +/* + * Default values for superblock features + */ +struct sb_feat_args sb_feat = { + .finobt = 1, + .spinodes = 0, + .log_version = 2, + .attr_version = 2, + .dir_version = XFS_DFL_DIR_VERSION, + .inode_align = XFS_IFLAG_ALIGN, + .nci = false, + .lazy_sb_counters = true, + .projid16bit = false, + .crcs_enabled = true, + .dirftype = true, + .parent_pointers = false, + .rmapbt = false, + .reflink = false, + .nodalign = false, + .nortalign = false, }; static void @@ -1283,7 +1244,7 @@ check_opt( fprintf(stderr, ("Developer screwed up option parsing (%d/%d)! Please report!\n"), sp->index, index); - reqval(opts->name, (char **)opts->subopts, index); + reqval(opts->name, opts->subopts, index); } /* @@ -1296,11 +1257,11 @@ check_opt( */ if (!str_seen) { if (sp->seen) - respec(opts->name, (char **)opts->subopts, index); + respec(opts->name, opts->subopts, index); sp->seen = true; } else { if (sp->str_seen) - respec(opts->name, (char **)opts->subopts, index); + respec(opts->name, opts->subopts, index); sp->str_seen = true; } @@ -1312,7 +1273,7 @@ check_opt( break; if (opts->subopt_params[conflict_opt].seen || opts->subopt_params[conflict_opt].str_seen) - conflict(opts->name, (char **)opts->subopts, + conflict(opts->name, opts->subopts, conflict_opt, index); } } @@ -1330,7 +1291,7 @@ getnum( /* empty strings might just return a default value */ if (!str || *str == '\0') { if (sp->defaultval == SUBOPT_NEEDS_VAL) - reqval(opts->name, (char **)opts->subopts, index); + reqval(opts->name, opts->subopts, index); return sp->defaultval; } @@ -1386,615 +1347,472 @@ getstr( /* empty strings for string options are not valid */ if (!str || *str == '\0') - reqval(opts->name, (char **)opts->subopts, index); + reqval(opts->name, opts->subopts, index); return str; } -int -main( - int argc, - char **argv) +/* geometry specified on input. 0 is not specified */ +struct cli_geometry { + int sectorsize; + int blocksize; + char *dsize; + int64_t agsize; + int agcount; + int dsunit; + int dswidth; + int dsu; + int dsw; + int inodesize; + int inopblock; + int imaxpct; + int dirblocksize; + int logagno; + char *logsize; + int lsectorsize; + int loginternal; + int lsu; + int lsunit; + char *rtextsize; + char *rtsize; + +} cligeo; + +/* root inode characteristics */ +struct fsxattr fsx; + +/* libxfs file/device setup info */ +libxfs_init_t xi; + +static int +block_opts_parser( + struct opt_params *opts, + int subopt, + char *value) { - uint64_t agcount; - xfs_agf_t *agf; - xfs_agi_t *agi; - xfs_agnumber_t agno; - uint64_t agsize; - xfs_alloc_rec_t *arec; - struct xfs_btree_block *block; - int blflag; int blocklog; - int bsflag; - int bsize; - xfs_buf_t *buf; - int c; - int daflag; - int dasize; - xfs_rfsblock_t dblocks; - char *dfile; - int dirblocklog; - int dirblocksize; - char *dsize; - int dsu; - int dsw; - int dsunit; - int dswidth; - int dsflag; - int force_overwrite; - struct fsxattr fsx; - int ilflag; - int imaxpct; - int imflag; + + switch (subopt) { + case B_LOG: + blocklog = getnum(value, &bopts, B_LOG); + cligeo.blocksize = 1 << blocklog; + break; + case B_SIZE: + cligeo.blocksize = getnum(value, &bopts, B_SIZE); + break; + default: + return -EINVAL; + } + return 0; +} + +static int +data_opts_parser( + struct opt_params *opts, + int subopt, + char *value) +{ + int sectorlog; + + switch (subopt) { + case D_AGCOUNT: + cligeo.agcount = getnum(value, opts, D_AGCOUNT); + break; + case D_AGSIZE: + cligeo.agsize = getnum(value, opts, D_AGSIZE); + break; + case D_FILE: + xi.disfile = getnum(value, opts, D_FILE); + break; + case D_NAME: + xi.dname = getstr(value, opts, D_NAME); + break; + case D_SIZE: + cligeo.dsize = getstr(value, opts, D_SIZE); + break; + case D_SUNIT: + cligeo.dsunit = getnum(value, opts, D_SUNIT); + break; + case D_SWIDTH: + cligeo.dswidth = getnum(value, opts, D_SWIDTH); + break; + case D_SU: + cligeo.dsu = getnum(value, opts, D_SU); + break; + case D_SW: + cligeo.dsw = getnum(value, opts, D_SW); + break; + case D_NOALIGN: + sb_feat.nodalign = getnum(value, opts, D_NOALIGN); + break; + case D_SECTLOG: + if (cligeo.sectorsize) + conflict('d', opts->subopts, D_SECTSIZE, D_SECTLOG); + sectorlog = getnum(value, opts, D_SECTLOG); + cligeo.sectorsize = 1 << sectorlog; + break; + case D_SECTSIZE: + cligeo.sectorsize = getnum(value, opts, D_SECTSIZE); + break; + case D_RTINHERIT: + if (getnum(value, opts, D_RTINHERIT)) + fsx.fsx_xflags |= XFS_DIFLAG_RTINHERIT; + break; + case D_PROJINHERIT: + fsx.fsx_projid = getnum(value, opts, D_PROJINHERIT); + fsx.fsx_xflags |= XFS_DIFLAG_PROJINHERIT; + break; + case D_EXTSZINHERIT: + fsx.fsx_extsize = getnum(value, opts, D_EXTSZINHERIT); + fsx.fsx_xflags |= XFS_DIFLAG_EXTSZINHERIT; + break; + default: + return -EINVAL; + } + return 0; +} + +static int +inode_opts_parser( + struct opt_params *opts, + int subopt, + char *value) +{ int inodelog; - int inopblock; - int ipflag; - int isflag; - int isize; - char *label = NULL; - int laflag; - int lalign; - int ldflag; - int liflag; - xfs_agnumber_t logagno; - xfs_rfsblock_t logblocks; - char *logfile; - int loginternal; - char *logsize; - xfs_fsblock_t logstart; - int lvflag; - int lsflag; - int lsuflag; - int lsunitflag; + + switch (subopt) { + case I_ALIGN: + sb_feat.inode_align = getnum(value, &iopts, I_ALIGN); + break; + case I_LOG: + inodelog = getnum(value, &iopts, I_LOG); + cligeo.inodesize = 1 << inodelog; + break; + case I_MAXPCT: + cligeo.imaxpct = getnum(value, &iopts, I_MAXPCT); + break; + case I_PERBLOCK: + cligeo.inopblock = getnum(value, &iopts, I_PERBLOCK); + break; + case I_SIZE: + cligeo.inodesize = getnum(value, &iopts, I_SIZE); + break; + case I_ATTR: + sb_feat.attr_version = getnum(value, &iopts, I_ATTR); + break; + case I_PROJID32BIT: + sb_feat.projid16bit = !getnum(value, &iopts, I_PROJID32BIT); + break; + case I_SPINODES: + sb_feat.spinodes = getnum(value, &iopts, I_SPINODES); + break; + default: + return -EINVAL; + } + return 0; +} + +static int +log_opts_parser( + struct opt_params *opts, + int subopt, + char *value) +{ int lsectorlog; - int lsectorsize; - int lslflag; - int lssflag; - int lsu; - int lsunit; - int min_logblocks; - xfs_mount_t *mp; - xfs_mount_t mbuf; - xfs_extlen_t nbmblocks; - int nlflag; - int nodsflag; - int norsflag; - xfs_alloc_rec_t *nrec; - int nsflag; - int nvflag; - int Nflag; - int discard = 1; - char *p; - char *protofile; - char *protostring; - int qflag; - xfs_rfsblock_t rtblocks; - xfs_extlen_t rtextblocks; - xfs_rtblock_t rtextents; - char *rtextsize; - char *rtfile; - char *rtsize; - xfs_sb_t *sbp; - int sectorlog; - uint64_t sector_mask; - int slflag; - int ssflag; - uint64_t tmp_agsize; - uuid_t uuid; - int worst_freelist; - libxfs_init_t xi; - struct fs_topology ft; - struct sb_feat_args sb_feat = { - .finobt = 1, - .spinodes = 0, - .log_version = 2, - .attr_version = 2, - .dir_version = XFS_DFL_DIR_VERSION, - .inode_align = XFS_IFLAG_ALIGN, - .nci = false, - .lazy_sb_counters = true, - .projid16bit = false, - .crcs_enabled = true, - .dirftype = true, - .parent_pointers = false, - .rmapbt = false, - .reflink = false, - }; - platform_uuid_generate(&uuid); - progname = basename(argv[0]); - setlocale(LC_ALL, ""); - bindtextdomain(PACKAGE, LOCALEDIR); - textdomain(PACKAGE); + switch (subopt) { + case L_AGNUM: + cligeo.logagno = getnum(value, &lopts, L_AGNUM); + break; + case L_FILE: + xi.lisfile = getnum(value, &lopts, L_FILE); + break; + case L_INTERNAL: + cligeo.loginternal = getnum(value, &lopts, L_INTERNAL); + break; + case L_SU: + cligeo.lsu = getnum(value, &lopts, L_SU); + break; + case L_SUNIT: + cligeo.lsunit = getnum(value, &lopts, L_SUNIT); + break; + case L_NAME: + case L_DEV: + xi.logname = getstr(value, &lopts, L_NAME); + cligeo.loginternal = 0; + break; + case L_VERSION: + sb_feat.log_version = getnum(value, &lopts, L_VERSION); + break; + case L_SIZE: + cligeo.logsize = getstr(value, &lopts, L_SIZE); + break; + case L_SECTLOG: + lsectorlog = getnum(value, &lopts, L_SECTLOG); + cligeo.lsectorsize = 1 << lsectorlog; + break; + case L_SECTSIZE: + cligeo.lsectorsize = getnum(value, &lopts, L_SECTSIZE); + break; + case L_LAZYSBCNTR: + sb_feat.lazy_sb_counters = getnum(value, &lopts, L_LAZYSBCNTR); + break; + default: + return -EINVAL; + } + return 0; +} - blflag = bsflag = slflag = ssflag = lslflag = lssflag = 0; - blocklog = blocksize = 0; - sectorlog = lsectorlog = 0; - sectorsize = lsectorsize = 0; - agsize = daflag = dasize = dblocks = 0; - ilflag = imflag = ipflag = isflag = 0; - liflag = laflag = lsflag = lsuflag = lsunitflag = ldflag = lvflag = 0; - loginternal = 1; - logagno = logblocks = rtblocks = rtextblocks = 0; - Nflag = nlflag = nsflag = nvflag = 0; - dirblocklog = dirblocksize = 0; - qflag = 0; - imaxpct = inodelog = inopblock = isize = 0; - dfile = logfile = rtfile = NULL; - dsize = logsize = rtsize = rtextsize = protofile = NULL; - dsu = dsw = dsunit = dswidth = lalign = lsu = lsunit = 0; - dsflag = nodsflag = norsflag = 0; - force_overwrite = 0; - worst_freelist = 0; - memset(&fsx, 0, sizeof(fsx)); +static int +meta_opts_parser( + struct opt_params *opts, + int subopt, + char *value) +{ + switch (subopt) { + case M_CRC: + sb_feat.crcs_enabled = getnum(value, &mopts, M_CRC); + if (sb_feat.crcs_enabled) + sb_feat.dirftype = true; + break; + case M_FINOBT: + sb_feat.finobt = getnum(value, &mopts, M_FINOBT); + break; + case M_UUID: + if (!value || *value == '\0') + reqval('m', opts->subopts, M_UUID); + if (platform_uuid_parse(value, &sb_feat.m_uuid)) + illegal(value, "m uuid"); + break; + case M_RMAPBT: + sb_feat.rmapbt = getnum(value, &mopts, M_RMAPBT); + break; + case M_REFLINK: + sb_feat.reflink = getnum(value, &mopts, M_REFLINK); + break; + default: + return -EINVAL; + } + return 0; +} - memset(&xi, 0, sizeof(xi)); - xi.isdirect = LIBXFS_DIRECT; - xi.isreadonly = LIBXFS_EXCLUSIVELY; +static int +naming_opts_parser( + struct opt_params *opts, + int subopt, + char *value) +{ + int dirblocklog; - while ((c = getopt(argc, argv, "b:d:i:l:L:m:n:KNp:qr:s:CfV")) != EOF) { - switch (c) { - case 'C': - case 'f': - force_overwrite = 1; - break; - case 'b': - p = optarg; - while (*p != '\0') { - char **subopts = (char **)bopts.subopts; - char *value; - - switch (getsubopt(&p, subopts, &value)) { - case B_LOG: - blocklog = getnum(value, &bopts, B_LOG); - blocksize = 1 << blocklog; - blflag = 1; - break; - case B_SIZE: - blocksize = getnum(value, &bopts, - B_SIZE); - blocklog = libxfs_highbit32(blocksize); - bsflag = 1; - break; - default: - unknown('b', value); - } - } - break; - case 'd': - p = optarg; - while (*p != '\0') { - char **subopts = (char **)dopts.subopts; - char *value; - - switch (getsubopt(&p, subopts, &value)) { - case D_AGCOUNT: - agcount = getnum(value, &dopts, - D_AGCOUNT); - daflag = 1; - break; - case D_AGSIZE: - agsize = getnum(value, &dopts, D_AGSIZE); - dasize = 1; - break; - case D_FILE: - xi.disfile = getnum(value, &dopts, - D_FILE); - break; - case D_NAME: - xi.dname = getstr(value, &dopts, D_NAME); - break; - case D_SIZE: - dsize = getstr(value, &dopts, D_SIZE); - break; - case D_SUNIT: - dsunit = getnum(value, &dopts, D_SUNIT); - dsflag = 1; - break; - case D_SWIDTH: - dswidth = getnum(value, &dopts, - D_SWIDTH); - dsflag = 1; - break; - case D_SU: - dsu = getnum(value, &dopts, D_SU); - dsflag = 1; - break; - case D_SW: - dsw = getnum(value, &dopts, D_SW); - dsflag = 1; - break; - case D_NOALIGN: - nodsflag = getnum(value, &dopts, - D_NOALIGN); - break; - case D_SECTLOG: - sectorlog = getnum(value, &dopts, - D_SECTLOG); - sectorsize = 1 << sectorlog; - slflag = 1; - break; - case D_SECTSIZE: - sectorsize = getnum(value, &dopts, - D_SECTSIZE); - sectorlog = - libxfs_highbit32(sectorsize); - ssflag = 1; - break; - case D_RTINHERIT: - c = getnum(value, &dopts, D_RTINHERIT); - if (c) - fsx.fsx_xflags |= - XFS_DIFLAG_RTINHERIT; - break; - case D_PROJINHERIT: - fsx.fsx_projid = getnum(value, &dopts, - D_PROJINHERIT); - fsx.fsx_xflags |= - XFS_DIFLAG_PROJINHERIT; - break; - case D_EXTSZINHERIT: - fsx.fsx_extsize = getnum(value, &dopts, - D_EXTSZINHERIT); - fsx.fsx_xflags |= - XFS_DIFLAG_EXTSZINHERIT; - break; - default: - unknown('d', value); - } - } - break; - case 'i': - p = optarg; - while (*p != '\0') { - char **subopts = (char **)iopts.subopts; - char *value; - - switch (getsubopt(&p, subopts, &value)) { - case I_ALIGN: - sb_feat.inode_align = getnum(value, - &iopts, I_ALIGN); - break; - case I_LOG: - inodelog = getnum(value, &iopts, I_LOG); - isize = 1 << inodelog; - ilflag = 1; - break; - case I_MAXPCT: - imaxpct = getnum(value, &iopts, - I_MAXPCT); - imflag = 1; - break; - case I_PERBLOCK: - inopblock = getnum(value, &iopts, - I_PERBLOCK); - ipflag = 1; - break; - case I_SIZE: - isize = getnum(value, &iopts, I_SIZE); - inodelog = libxfs_highbit32(isize); - isflag = 1; - break; - case I_ATTR: - sb_feat.attr_version = - getnum(value, &iopts, I_ATTR); - break; - case I_PROJID32BIT: - sb_feat.projid16bit = - !getnum(value, &iopts, - I_PROJID32BIT); - break; - case I_SPINODES: - sb_feat.spinodes = getnum(value, - &iopts, I_SPINODES); - break; - default: - unknown('i', value); - } - } - break; - case 'l': - p = optarg; - while (*p != '\0') { - char **subopts = (char **)lopts.subopts; - char *value; - - switch (getsubopt(&p, subopts, &value)) { - case L_AGNUM: - logagno = getnum(value, &lopts, L_AGNUM); - laflag = 1; - break; - case L_FILE: - xi.lisfile = getnum(value, &lopts, - L_FILE); - break; - case L_INTERNAL: - loginternal = getnum(value, &lopts, - L_INTERNAL); - liflag = 1; - break; - case L_SU: - lsu = getnum(value, &lopts, L_SU); - lsuflag = 1; - break; - case L_SUNIT: - lsunit = getnum(value, &lopts, L_SUNIT); - lsunitflag = 1; - break; - case L_NAME: - case L_DEV: - logfile = getstr(value, &lopts, L_NAME); - xi.logname = logfile; - ldflag = 1; - loginternal = 0; - break; - case L_VERSION: - sb_feat.log_version = - getnum(value, &lopts, L_VERSION); - lvflag = 1; - break; - case L_SIZE: - logsize = getstr(value, &lopts, L_SIZE); - break; - case L_SECTLOG: - lsectorlog = getnum(value, &lopts, - L_SECTLOG); - lsectorsize = 1 << lsectorlog; - lslflag = 1; - break; - case L_SECTSIZE: - lsectorsize = getnum(value, &lopts, - L_SECTSIZE); - lsectorlog = - libxfs_highbit32(lsectorsize); - lssflag = 1; - break; - case L_LAZYSBCNTR: - sb_feat.lazy_sb_counters = - getnum(value, &lopts, - L_LAZYSBCNTR); - break; - default: - unknown('l', value); - } - } - break; - case 'L': - if (strlen(optarg) > sizeof(sbp->sb_fname)) - illegal(optarg, "L"); - label = optarg; - break; - case 'm': - p = optarg; - while (*p != '\0') { - char **subopts = (char **)mopts.subopts; - char *value; - - switch (getsubopt(&p, subopts, &value)) { - case M_CRC: - sb_feat.crcs_enabled = - getnum(value, &mopts, M_CRC); - if (sb_feat.crcs_enabled) - sb_feat.dirftype = true; - break; - case M_FINOBT: - sb_feat.finobt = getnum( - value, &mopts, M_FINOBT); - break; - case M_UUID: - if (!value || *value == '\0') - reqval('m', subopts, M_UUID); - if (platform_uuid_parse(value, &uuid)) - illegal(optarg, "m uuid"); - break; - case M_RMAPBT: - sb_feat.rmapbt = getnum( - value, &mopts, M_RMAPBT); - break; - case M_REFLINK: - sb_feat.reflink = getnum( - value, &mopts, M_REFLINK); - break; - default: - unknown('m', value); - } - } - break; - case 'n': - p = optarg; - while (*p != '\0') { - char **subopts = (char **)nopts.subopts; - char *value; - - switch (getsubopt(&p, subopts, &value)) { - case N_LOG: - dirblocklog = getnum(value, &nopts, - N_LOG); - dirblocksize = 1 << dirblocklog; - nlflag = 1; - break; - case N_SIZE: - dirblocksize = getnum(value, &nopts, - N_SIZE); - dirblocklog = - libxfs_highbit32(dirblocksize); - nsflag = 1; - break; - case N_VERSION: - value = getstr(value, &nopts, N_VERSION); - if (!strcasecmp(value, "ci")) { - /* ASCII CI mode */ - sb_feat.nci = true; - } else { - sb_feat.dir_version = - getnum(value, &nopts, - N_VERSION); - } - nvflag = 1; - break; - case N_FTYPE: - sb_feat.dirftype = getnum(value, &nopts, - N_FTYPE); - break; - default: - unknown('n', value); - } - } - break; - case 'N': - Nflag = 1; - break; - case 'K': - discard = 0; - break; - case 'p': - if (protofile) - respec('p', NULL, 0); - protofile = optarg; - break; - case 'q': - qflag = 1; - break; - case 'r': - p = optarg; - while (*p != '\0') { - char **subopts = (char **)ropts.subopts; - char *value; - - switch (getsubopt(&p, subopts, &value)) { - case R_EXTSIZE: - rtextsize = getstr(value, &ropts, - R_EXTSIZE); - break; - case R_FILE: - xi.risfile = getnum(value, &ropts, - R_FILE); - break; - case R_NAME: - case R_DEV: - xi.rtname = getstr(value, &ropts, - R_NAME); - break; - case R_SIZE: - rtsize = getstr(value, &ropts, R_SIZE); - break; - case R_NOALIGN: - norsflag = getnum(value, &ropts, - R_NOALIGN); - break; - default: - unknown('r', value); - } - } - break; - case 's': - p = optarg; - while (*p != '\0') { - char **subopts = (char **)sopts.subopts; - char *value; - - switch (getsubopt(&p, subopts, &value)) { - case S_LOG: - case S_SECTLOG: - if (lssflag) - conflict('s', subopts, - S_SECTSIZE, S_SECTLOG); - sectorlog = getnum(value, &sopts, - S_SECTLOG); - lsectorlog = sectorlog; - sectorsize = 1 << sectorlog; - lsectorsize = sectorsize; - lslflag = slflag = 1; - break; - case S_SIZE: - case S_SECTSIZE: - if (lslflag) - conflict('s', subopts, S_SECTLOG, - S_SECTSIZE); - sectorsize = getnum(value, &sopts, - S_SECTSIZE); - lsectorsize = sectorsize; - sectorlog = - libxfs_highbit32(sectorsize); - lsectorlog = sectorlog; - lssflag = ssflag = 1; - break; - default: - unknown('s', value); - } - } - break; - case 'V': - printf(_("%s version %s\n"), progname, VERSION); - exit(0); - case '?': - unknown(optopt, ""); + switch (subopt) { + case N_LOG: + dirblocklog = getnum(value, opts, N_LOG); + cligeo.dirblocksize = 1 << dirblocklog; + break; + case N_SIZE: + cligeo.dirblocksize = getnum(value, opts, N_SIZE); + break; + case N_VERSION: + value = getstr(value, &nopts, N_VERSION); + if (!strcasecmp(value, "ci")) { + /* ASCII CI mode */ + sb_feat.nci = true; + } else { + sb_feat.dir_version = getnum(value, opts, N_VERSION); } + break; + case N_FTYPE: + sb_feat.dirftype = getnum(value, opts, N_FTYPE); + break; + default: + return -EINVAL; } - if (argc - optind > 1) { - fprintf(stderr, _("extra arguments\n")); - usage(); - } else if (argc - optind == 1) { - dfile = xi.volname = getstr(argv[optind], &dopts, D_NAME); - } else - dfile = xi.dname; + return 0; +} + +static int +rtdev_opts_parser( + struct opt_params *opts, + int subopt, + char *value) +{ + switch (subopt) { + case R_EXTSIZE: + cligeo.rtextsize = getstr(value, &ropts, R_EXTSIZE); + break; + case R_FILE: + xi.risfile = getnum(value, &ropts, R_FILE); + break; + case R_NAME: + case R_DEV: + xi.rtname = getstr(value, &ropts, R_NAME); + break; + case R_SIZE: + cligeo.rtsize = getstr(value, &ropts, R_SIZE); + break; + case R_NOALIGN: + sb_feat.nortalign = getnum(value, &ropts, R_NOALIGN); + break; + default: + return -EINVAL; + } + return 0; +} + +static int +sector_opts_parser( + struct opt_params *opts, + int subopt, + char *value) +{ + int sectorlog; + + switch (subopt) { + case S_LOG: + case S_SECTLOG: + if (cligeo.sectorsize) + conflict('s', opts->subopts, S_SECTSIZE, S_SECTLOG); + sectorlog = getnum(value, &sopts, S_SECTLOG); + cligeo.sectorsize = 1 << sectorlog; + cligeo.lsectorsize = cligeo.sectorsize; + break; + case S_SIZE: + case S_SECTSIZE: + if (cligeo.sectorsize) + conflict('s', opts->subopts, S_SECTLOG, S_SECTSIZE); + cligeo.sectorsize = getnum(value, &sopts, S_SECTSIZE); + cligeo.lsectorsize = cligeo.sectorsize; + break; + default: + return -EINVAL; + } + return 0; +} + +struct subopts { + char opt; + struct opt_params *opts; + int (*parser)(); +} subopt_tab[] = { + { 'b', &bopts, block_opts_parser }, + { 'd', &dopts, data_opts_parser }, + { 'i', &iopts, inode_opts_parser }, + { 'l', &lopts, log_opts_parser }, + { 'm', &mopts, meta_opts_parser }, + { 'n', &nopts, naming_opts_parser }, + { 'r', &ropts, rtdev_opts_parser }, + { 's', &sopts, sector_opts_parser }, + { '\0', NULL, NULL }, +}; + +static void +parse_subopts( + char opt, + char *arg) +{ + struct subopts *sop = &subopt_tab[0]; + char *p; + int ret = 0; + + while (sop->opts) { + if (sop->opt == opt) + break; + } + + /* should never happen */ + if (!sop->opts) + return; + + p = arg; + while (*p != '\0') { + char **subopts = (char **)sop->opts->subopts; + char *value; + int subopt; + + subopt = getsubopt(&p, subopts, &value); + + ret = (sop->parser)(sop->opts, subopt, value); + if (ret) + unknown(opt, value); + } +} + +static void +validate_blocksize( + unsigned int *size, + int *size_log) +{ /* * Blocksize and sectorsize first, other things depend on them * For RAID4/5/6 we want to align sector size and block size, * so we need to start with the device geometry extraction too. */ - if (!blflag && !bsflag) { - blocklog = XFS_DFL_BLOCKSIZE_LOG; - blocksize = 1 << XFS_DFL_BLOCKSIZE_LOG; + if (!cligeo.blocksize) { + *size_log = XFS_DFL_BLOCKSIZE_LOG; + *size = 1 << XFS_DFL_BLOCKSIZE_LOG; + } else { + *size = cligeo.blocksize; + *size_log = libxfs_highbit32(cligeo.blocksize); } - if (blocksize < XFS_MIN_BLOCKSIZE || blocksize > XFS_MAX_BLOCKSIZE) { - fprintf(stderr, _("illegal block size %d\n"), blocksize); + + /* validate block sizes are in range */ + if (*size < XFS_MIN_BLOCKSIZE || *size > XFS_MAX_BLOCKSIZE) { + fprintf(stderr, _("illegal block size %d\n"), *size); usage(); } - if (sb_feat.crcs_enabled && blocksize < XFS_MIN_CRC_BLOCKSIZE) { + if (sb_feat.crcs_enabled && *size < XFS_MIN_CRC_BLOCKSIZE) { fprintf(stderr, _("Minimum block size for CRC enabled filesystems is %d bytes.\n"), XFS_MIN_CRC_BLOCKSIZE); usage(); } - if (sb_feat.crcs_enabled && !sb_feat.dirftype) { - fprintf(stderr, _("cannot disable ftype with crcs enabled\n")); - usage(); - } +} - if (!slflag && !ssflag) { - sectorlog = XFS_MIN_SECTORSIZE_LOG; - sectorsize = XFS_MIN_SECTORSIZE; - } - if (!lslflag && !lssflag) { - lsectorlog = sectorlog; - lsectorsize = sectorsize; +static void +validate_sectorsize( + unsigned int *size, + int *size_log, + struct fs_topology *ft, + char *dfile, + int Nflag, + int force_overwrite) +{ + /* set configured sector sizes in preparation for checks */ + if (!cligeo.sectorsize) { + *size_log = XFS_MIN_SECTORSIZE_LOG; + *size = XFS_MIN_SECTORSIZE; + } else { + *size = cligeo.sectorsize; + *size_log = libxfs_highbit32(cligeo.sectorsize); } /* * Before anything else, verify that we are correctly operating on * files or block devices and set the control parameters correctly. - * Explicitly disable direct IO for image files so we don't error out on - * sector size mismatches between the new filesystem and the underlying - * host filesystem. */ - check_device_type(dfile, &xi.disfile, !dsize, !dfile, + check_device_type(dfile, &xi.disfile, !cligeo.dsize, !dfile, Nflag ? NULL : &xi.dcreat, force_overwrite, "d"); - if (!loginternal) - check_device_type(xi.logname, &xi.lisfile, !logsize, !xi.logname, - Nflag ? NULL : &xi.lcreat, + if (!cligeo.loginternal) + check_device_type(xi.logname, &xi.lisfile, !cligeo.logsize, + !xi.logname, Nflag ? NULL : &xi.lcreat, force_overwrite, "l"); if (xi.rtname) - check_device_type(xi.rtname, &xi.risfile, !rtsize, !xi.rtname, - Nflag ? NULL : &xi.rcreat, + check_device_type(xi.rtname, &xi.risfile, !cligeo.rtsize, + !xi.rtname, Nflag ? NULL : &xi.rcreat, force_overwrite, "r"); + + /* + * Explicitly disable direct IO for image files so we don't error out on + * sector size mismatches between the new filesystem and the underlying + * host filesystem. + */ if (xi.disfile || xi.lisfile || xi.risfile) xi.isdirect = 0; - memset(&ft, 0, sizeof(ft)); - get_topology(&xi, &ft, force_overwrite); + memset(ft, 0, sizeof(*ft)); + get_topology(&xi, ft, force_overwrite); - if (!ssflag) { + if (!cligeo.sectorsize) { /* * Unless specified manually on the command line use the * advertised sector size of the device. We use the physical @@ -2004,56 +1822,93 @@ _("Minimum block size for CRC enabled filesystems is %d bytes.\n"), */ /* Older kernels may not have physical/logical distinction */ - if (!ft.psectorsize) - ft.psectorsize = ft.lsectorsize; + if (!ft->psectorsize) + ft->psectorsize = ft->lsectorsize; - sectorsize = ft.psectorsize ? ft.psectorsize : + *size = ft->psectorsize ? ft->psectorsize : XFS_MIN_SECTORSIZE; - if ((blocksize < sectorsize) && (blocksize >= ft.lsectorsize)) { + if ((blocksize < *size) && (blocksize >= ft->lsectorsize)) { fprintf(stderr, _("specified blocksize %d is less than device physical sector size %d\n"), - blocksize, ft.psectorsize); + blocksize, ft->psectorsize); fprintf(stderr, _("switching to logical sector size %d\n"), - ft.lsectorsize); - sectorsize = ft.lsectorsize ? ft.lsectorsize : + ft->lsectorsize); + *size = ft->lsectorsize ? ft->lsectorsize : XFS_MIN_SECTORSIZE; } - } - if (!ssflag) { - sectorlog = libxfs_highbit32(sectorsize); - if (loginternal) { - lsectorsize = sectorsize; - lsectorlog = sectorlog; - } + *size_log = libxfs_highbit32(*size); } - if (sectorsize < XFS_MIN_SECTORSIZE || - sectorsize > XFS_MAX_SECTORSIZE || sectorsize > blocksize) { - if (ssflag) - fprintf(stderr, _("illegal sector size %d\n"), sectorsize); + /* validate specified/probed sector size */ + if (*size < XFS_MIN_SECTORSIZE || + *size > XFS_MAX_SECTORSIZE || *size > blocksize) { + if (cligeo.sectorsize) + fprintf(stderr, _("illegal sector size %d\n"), *size); else fprintf(stderr, _("block size %d cannot be smaller than logical sector size %d\n"), - blocksize, ft.lsectorsize); + blocksize, ft->lsectorsize); usage(); } - if (sectorsize < ft.lsectorsize) { + if (*size < ft->lsectorsize) { fprintf(stderr, _("illegal sector size %d; hw sector is %d\n"), - sectorsize, ft.lsectorsize); + *size, ft->lsectorsize); + usage(); + } +} + +/* + * Grab log sector size and validate. + * + * XXX: probe sector size on external log device rather than using ssize? + */ +static void +validate_log_sectorsize( + int *lsize, + int *lsize_log, + int sectorsize, + int sectorlog) +{ + + if (cligeo.lsectorsize && cligeo.loginternal) { + fprintf(stderr, _("Can't set sector size on internal log!\n")); usage(); } - if (lsectorsize < XFS_MIN_SECTORSIZE || - lsectorsize > XFS_MAX_SECTORSIZE || lsectorsize > blocksize) { - fprintf(stderr, _("illegal log sector size %d\n"), lsectorsize); + if (!cligeo.lsectorsize) { + *lsize_log = sectorlog; + *lsize = sectorsize; + } else { + *lsize = cligeo.lsectorsize; + *lsize_log = libxfs_highbit32(cligeo.lsectorsize); + } + + if (*lsize < XFS_MIN_SECTORSIZE || + *lsize > XFS_MAX_SECTORSIZE || *lsize > blocksize) { + fprintf(stderr, _("illegal log sector size %d\n"), *lsize); usage(); - } else if (lsectorsize > XFS_MIN_SECTORSIZE && !lsu && !lsunit) { - lsu = blocksize; + } + if (*lsize > XFS_MIN_SECTORSIZE && + cligeo.lsu <= 0 && cligeo.lsunit <= 0) { + cligeo.lsu = blocksize; sb_feat.log_version = 2; } + /* if lsu or lsunit was specified, automatically use v2 logs */ + if ((cligeo.lsu || cligeo.lsunit) && sb_feat.log_version == 1) { + fprintf(stderr, + _("log stripe unit specified, using v2 logs\n")); + sb_feat.log_version = 2; + } + +} + +static void +validate_sb_features(void) +{ + /* * Now we have blocks and sector sizes set up, check parameters that are * no longer optional for CRC enabled filesystems. Catch them up front @@ -2061,7 +1916,8 @@ _("block size %d cannot be smaller than logical sector size %d\n"), */ if (sb_feat.crcs_enabled) { /* minimum inode size is 512 bytes, ipflag checked later */ - if ((isflag || ilflag) && inodelog < XFS_DINODE_DFL_CRC_LOG) { + if (cligeo.inodesize && + cligeo.inodesize < (1 << XFS_DINODE_DFL_CRC_LOG)) { fprintf(stderr, _("Minimum inode size for CRCs is %d bytes\n"), 1 << XFS_DINODE_DFL_CRC_LOG); @@ -2103,6 +1959,14 @@ _("V2 attribute format always enabled on CRC enabled filesytems\n")); _("32 bit Project IDs always enabled on CRC enabled filesytems\n")); usage(); } + + /* ftype always on */ + if (!sb_feat.dirftype) { + fprintf(stderr, +_("Directory ftype field always enabled on CRC enabled filesytems\n")); + usage(); + } + } else { /* * The kernel doesn't currently support crc=0,finobt=1 @@ -2149,103 +2013,137 @@ _("rmapbt not supported with realtime devices\n")); usage(); sb_feat.rmapbt = false; } +} - if (nsflag || nlflag) { - if (dirblocksize < blocksize || - dirblocksize > XFS_MAX_BLOCKSIZE) { - fprintf(stderr, _("illegal directory block size %d\n"), - dirblocksize); - usage(); - } - } else { +static void +validate_dirblocksize( + int *size, + int *size_log, + int blocklog) +{ + + if (cligeo.dirblocksize && + (cligeo.dirblocksize < blocksize || + cligeo.dirblocksize > XFS_MAX_BLOCKSIZE)) { + fprintf(stderr, _("illegal directory block size %d\n"), + cligeo.dirblocksize); + usage(); + } + + if (!cligeo.dirblocksize) { if (blocksize < (1 << XFS_MIN_REC_DIRSIZE)) - dirblocklog = XFS_MIN_REC_DIRSIZE; + *size_log = XFS_MIN_REC_DIRSIZE; else - dirblocklog = blocklog; - dirblocksize = 1 << dirblocklog; + *size_log = blocklog; + *size = 1 << *size_log; + } else { + *size = cligeo.dirblocksize; + *size_log = libxfs_highbit32(cligeo.dirblocksize); } +} +static void +validate_inodesize( + int *size, + int *size_log, + int *inodes_per_block, + int blocklog) +{ - if (dsize) { - uint64_t dbytes; - - dbytes = getnum(dsize, &dopts, D_SIZE); - if (dbytes % XFS_MIN_BLOCKSIZE) { - fprintf(stderr, - _("illegal data length %lld, not a multiple of %d\n"), - (long long)dbytes, XFS_MIN_BLOCKSIZE); - usage(); - } - dblocks = (xfs_rfsblock_t)(dbytes >> blocklog); - if (dbytes % blocksize) - fprintf(stderr, _("warning: " - "data length %lld not a multiple of %d, truncated to %lld\n"), - (long long)dbytes, blocksize, - (long long)(dblocks << blocklog)); - } - if (ipflag) { - inodelog = blocklog - libxfs_highbit32(inopblock); - isize = 1 << inodelog; - } else if (!ilflag && !isflag) { - inodelog = sb_feat.crcs_enabled ? XFS_DINODE_DFL_CRC_LOG + if (cligeo.inopblock) { + *inodes_per_block = cligeo.inopblock; + *size_log = blocklog - libxfs_highbit32(cligeo.inopblock); + *size = 1 << *size_log; + } else if (cligeo.inodesize) { + *size = cligeo.inodesize; + *size_log = libxfs_highbit32(*size); + *inodes_per_block = blocksize / *size; + } else { + *size_log = sb_feat.crcs_enabled ? XFS_DINODE_DFL_CRC_LOG : XFS_DINODE_DFL_LOG; - isize = 1 << inodelog; + *size = 1 << *size_log; + *inodes_per_block = blocksize / *size; } - if (sb_feat.crcs_enabled && inodelog < XFS_DINODE_DFL_CRC_LOG) { + if (sb_feat.crcs_enabled && *size_log < XFS_DINODE_DFL_CRC_LOG) { fprintf(stderr, _("Minimum inode size for CRCs is %d bytes\n"), 1 << XFS_DINODE_DFL_CRC_LOG); usage(); } - if (logsize) { - uint64_t logbytes; + if (*size > blocksize / XFS_MIN_INODE_PERBLOCK || + *inodes_per_block < XFS_MIN_INODE_PERBLOCK || + *size < XFS_DINODE_MIN_SIZE || + *size > XFS_DINODE_MAX_SIZE) { + int maxsz; - logbytes = getnum(logsize, &lopts, L_SIZE); - if (logbytes % XFS_MIN_BLOCKSIZE) { + fprintf(stderr, _("illegal inode size %d\n"), *size); + maxsz = MIN(blocksize / XFS_MIN_INODE_PERBLOCK, + XFS_DINODE_MAX_SIZE); + if (XFS_DINODE_MIN_SIZE == maxsz) fprintf(stderr, - _("illegal log length %lld, not a multiple of %d\n"), - (long long)logbytes, XFS_MIN_BLOCKSIZE); - usage(); - } - logblocks = (xfs_rfsblock_t)(logbytes >> blocklog); - if (logbytes % blocksize) + _("allowable inode size with %d byte blocks is %d\n"), + blocksize, XFS_DINODE_MIN_SIZE); + else fprintf(stderr, - _("warning: log length %lld not a multiple of %d, truncated to %lld\n"), - (long long)logbytes, blocksize, - (long long)(logblocks << blocklog)); + _("allowable inode size with %d byte blocks is between %d and %d\n"), + blocksize, XFS_DINODE_MIN_SIZE, maxsz); + exit(1); } - if (rtsize) { - uint64_t rtbytes; +} - rtbytes = getnum(rtsize, &ropts, R_SIZE); - if (rtbytes % XFS_MIN_BLOCKSIZE) { - fprintf(stderr, - _("illegal rt length %lld, not a multiple of %d\n"), - (long long)rtbytes, XFS_MIN_BLOCKSIZE); - usage(); - } - rtblocks = (xfs_rfsblock_t)(rtbytes >> blocklog); - if (rtbytes % blocksize) - fprintf(stderr, - _("warning: rt length %lld not a multiple of %d, truncated to %lld\n"), - (long long)rtbytes, blocksize, - (long long)(rtblocks << blocklog)); +static xfs_rfsblock_t +calc_dev_size( + char *size, + struct opt_params *opts, + int sizeopt, + int blocklog, + char *type) +{ + uint64_t dbytes; + xfs_rfsblock_t dblocks; + + if (!size) + return 0; + + dbytes = getnum(size, opts, sizeopt); + if (dbytes % XFS_MIN_BLOCKSIZE) { + fprintf(stderr, + _("illegal %s length %lld, not a multiple of %d\n"), + type, (long long)dbytes, XFS_MIN_BLOCKSIZE); + usage(); + } + dblocks = (xfs_rfsblock_t)(dbytes >> blocklog); + if (dbytes % blocksize) { + fprintf(stderr, +_("warning: %s length %lld not a multiple of %d, truncated to %lld\n"), + type, (long long)dbytes, blocksize, + (long long)(dblocks << blocklog)); } + return dblocks; +} + +static void +validate_rtextsize( + xfs_extlen_t *blocks, + struct fs_topology *ft, + int blocklog) +{ + uint64_t rtextbytes; + /* * If specified, check rt extent size against its constraints. */ - if (rtextsize) { - uint64_t rtextbytes; + if (cligeo.rtextsize) { - rtextbytes = getnum(rtextsize, &ropts, R_EXTSIZE); + rtextbytes = getnum(cligeo.rtextsize, &ropts, R_EXTSIZE); if (rtextbytes % blocksize) { fprintf(stderr, _("illegal rt extent size %lld, not a multiple of %d\n"), (long long)rtextbytes, blocksize); usage(); } - rtextblocks = (xfs_extlen_t)(rtextbytes >> blocklog); + *blocks = (xfs_extlen_t)(rtextbytes >> blocklog); } else { /* * If realtime extsize has not been specified by the user, @@ -2253,70 +2151,314 @@ _("rmapbt not supported with realtime devices\n")); * to the stripe width. */ uint64_t rswidth; - uint64_t rtextbytes; - if (!norsflag && !xi.risfile && !(!rtsize && xi.disfile)) - rswidth = ft.rtswidth; + if (!sb_feat.nortalign && !xi.risfile && + !(!cligeo.rtsize && xi.disfile)) + rswidth = ft->rtswidth; else rswidth = 0; /* check that rswidth is a multiple of fs blocksize */ - if (!norsflag && rswidth && !(BBTOB(rswidth) % blocksize)) { + if (!sb_feat.nortalign && rswidth && + !(BBTOB(rswidth) % blocksize)) { rswidth = DTOBT(rswidth); rtextbytes = rswidth << blocklog; if (XFS_MIN_RTEXTSIZE <= rtextbytes && (rtextbytes <= XFS_MAX_RTEXTSIZE)) { - rtextblocks = rswidth; + *blocks = rswidth; } } - if (!rtextblocks) { - rtextblocks = (blocksize < XFS_MIN_RTEXTSIZE) ? + if (!*blocks) { + *blocks = (blocksize < XFS_MIN_RTEXTSIZE) ? XFS_MIN_RTEXTSIZE >> blocklog : 1; } } - ASSERT(rtextblocks); + ASSERT(*blocks); +} - /* - * Check some argument sizes against mins, maxes. - */ - if (isize > blocksize / XFS_MIN_INODE_PERBLOCK || - isize < XFS_DINODE_MIN_SIZE || - isize > XFS_DINODE_MAX_SIZE) { - int maxsz; +/* + * Convert lsu to lsunit for 512 bytes blocks and check validity of the values. + */ +static void +calc_stripe_factors( + int dsectsz, + int lsectsz, + int *dsunit, + int *dswidth, + int *lsunit) +{ + int dsu; + int dsw; + int lsu; - fprintf(stderr, _("illegal inode size %d\n"), isize); - maxsz = MIN(blocksize / XFS_MIN_INODE_PERBLOCK, - XFS_DINODE_MAX_SIZE); - if (XFS_DINODE_MIN_SIZE == maxsz) + *dsunit = cligeo.dsunit == -1 ? 0 : cligeo.dsunit; + *dswidth = cligeo.dswidth == -1 ? 0 : cligeo.dswidth; + *lsunit = cligeo.lsunit == -1 ? 0 : cligeo.lsunit; + + dsu = cligeo.dsu == -1 ? 0 : cligeo.dsu; + dsw = cligeo.dsw == -1 ? 0 : cligeo.dsw; + lsu = cligeo.lsu == -1 ? 0 : cligeo.lsu; + + /* Handle data sunit/swidth options */ + if ((*dsunit && !*dswidth) || (!*dsunit && *dswidth)) { + fprintf(stderr, +_("both data sunit and data swidth options must be specified\n")); + usage(); + } + + if (dsu || dsw) { + if ((dsu && !dsw) || (!dsu && dsw)) { fprintf(stderr, - _("allowable inode size with %d byte blocks is %d\n"), - blocksize, XFS_DINODE_MIN_SIZE); - else +_("both data su and data sw options must be specified\n")); + usage(); + } + + if (dsu % dsectsz) { fprintf(stderr, - _("allowable inode size with %d byte blocks is between %d and %d\n"), - blocksize, XFS_DINODE_MIN_SIZE, maxsz); - exit(1); +_("data su must be a multiple of the sector size (%d)\n"), dsectsz); + usage(); + } + + *dsunit = (int)BTOBBT(dsu); + *dswidth = *dsunit * dsw; } - /* if lsu or lsunit was specified, automatically use v2 logs */ - if ((lsu || lsunit) && sb_feat.log_version == 1) { + if (*dsunit && (*dswidth % *dsunit != 0)) { fprintf(stderr, - _("log stripe unit specified, using v2 logs\n")); - sb_feat.log_version = 2; +_("data stripe width (%d) must be a multiple of the data stripe unit (%d)\n"), + *dswidth, *dsunit); + usage(); } - calc_stripe_factors(dsu, dsw, sectorsize, lsu, lsectorsize, - &dsunit, &dswidth, &lsunit); + /* Handle log sunit options */ + + if (lsu) + *lsunit = (int)BTOBBT(lsu); + + /* verify if lsu/lsunit is a multiple block size */ + if (lsu % blocksize != 0) { + fprintf(stderr, +_("log stripe unit (%d) must be a multiple of the block size (%d)\n"), + lsu, blocksize); + exit(1); + } + if ((BBTOB(*lsunit) % blocksize != 0)) { + fprintf(stderr, +_("log stripe unit (%d) must be a multiple of the block size (%d)\n"), + BBTOB(*lsunit), blocksize); + exit(1); + } /* If sunit & swidth were manually specified as 0, same as noalign */ - if (dsflag && !dsunit && !dswidth) - nodsflag = 1; + if (cligeo.dsunit != -1 && !dsunit && !dswidth) + sb_feat.nodalign = true; +} + +int +main( + int argc, + char **argv) +{ + uint64_t agcount; + xfs_agf_t *agf; + xfs_agi_t *agi; + xfs_agnumber_t agno; + uint64_t agsize; + xfs_alloc_rec_t *arec; + struct xfs_btree_block *block; + int blocklog; + int bsize; + xfs_buf_t *buf; + int c; + int daflag; + int dasize; + xfs_rfsblock_t dblocks; + char *dfile; + int dirblocklog; + int dirblocksize; + char *dsize; + int dsunit; + int dswidth; + int force_overwrite; + int imaxpct; + int imflag; + int inodelog; + int inopblock; + int ipflag; + int isflag; + int isize; + char *label = NULL; + int laflag; + int lalign; + int ldflag; + int liflag; + xfs_agnumber_t logagno; + xfs_rfsblock_t logblocks; + char *logfile; + int loginternal; + char *logsize; + xfs_fsblock_t logstart; + int lvflag; + int lsflag; + int lsuflag; + int lsunitflag; + int lsectorlog; + int lsectorsize; + int lsu; + int lsunit; + int min_logblocks; + xfs_mount_t *mp; + xfs_mount_t mbuf; + xfs_extlen_t nbmblocks; + int nlflag; + int nodsflag; + int norsflag; + xfs_alloc_rec_t *nrec; + int nsflag; + int nvflag; + int Nflag; + int discard = 1; + char *protofile; + char *protostring; + int qflag; + xfs_rfsblock_t rtblocks; + xfs_extlen_t rtextblocks; + xfs_rtblock_t rtextents; + char *rtextsize; + char *rtfile; + char *rtsize; + xfs_sb_t *sbp; + int sectorlog; + uint64_t sector_mask; + uint64_t tmp_agsize; + uuid_t uuid; + int worst_freelist; + struct fs_topology ft; + + platform_uuid_generate(&uuid); + progname = basename(argv[0]); + setlocale(LC_ALL, ""); + bindtextdomain(PACKAGE, LOCALEDIR); + textdomain(PACKAGE); + + blocklog = blocksize = 0; + sectorlog = lsectorlog = 0; + sectorsize = lsectorsize = 0; + agsize = daflag = dasize = dblocks = 0; + imflag = ipflag = isflag = 0; + liflag = laflag = lsflag = lsuflag = lsunitflag = ldflag = lvflag = 0; + loginternal = 1; + logagno = logblocks = rtblocks = rtextblocks = 0; + Nflag = nlflag = nsflag = nvflag = 0; + dirblocklog = dirblocksize = 0; + qflag = 0; + imaxpct = inodelog = inopblock = isize = 0; + dfile = logfile = rtfile = NULL; + dsize = logsize = rtsize = rtextsize = protofile = NULL; + dsunit = dswidth = lalign = lsu = lsunit = 0; + nodsflag = norsflag = 0; + force_overwrite = 0; + worst_freelist = 0; + + /* + * set parameters that can be set to zero to -1 or a default value so we + * can tell if they have been set or not This gets rid of all the "was + * it specified" flags. + */ + cligeo.dsu = -1; + cligeo.dsw = -1; + cligeo.dsunit = -1; + cligeo.dswidth = -1; + cligeo.loginternal = 1; + cligeo.logagno = -1; + cligeo.lsu = -1; + cligeo.lsunit = -1; + + xi.isdirect = LIBXFS_DIRECT; + xi.isreadonly = LIBXFS_EXCLUSIVELY; + + while ((c = getopt(argc, argv, "b:d:i:l:L:m:n:KNp:qr:s:CfV")) != EOF) { + switch (c) { + case 'C': + case 'f': + force_overwrite = 1; + break; + case 'b': + case 'd': + case 'i': + case 'l': + case 'm': + case 'n': + case 'r': + case 's': + parse_subopts(c, optarg); + break; + case 'L': + if (strlen(optarg) > sizeof(sbp->sb_fname)) + illegal(optarg, "L"); + label = optarg; + break; + case 'N': + Nflag = 1; + break; + case 'K': + discard = 0; + break; + case 'p': + if (protofile) + respec('p', NULL, 0); + protofile = optarg; + break; + case 'q': + qflag = 1; + break; + case 'V': + printf(_("%s version %s\n"), progname, VERSION); + exit(0); + case '?': + unknown(optopt, ""); + } + } + if (argc - optind > 1) { + fprintf(stderr, _("extra arguments\n")); + usage(); + } else if (argc - optind == 1) { + dfile = xi.volname = getstr(argv[optind], &dopts, D_NAME); + } else + dfile = xi.dname; + + /* + * Extract as much of the valid config as we can from the CLI input + * before opening the libxfs devices. + */ + validate_blocksize(&blocksize, &blocklog); + validate_sectorsize(§orsize, §orlog, &ft, dfile, Nflag, + force_overwrite); + validate_log_sectorsize(&lsectorsize, &lsectorlog, + sectorsize, sectorlog); + validate_sb_features(); + + validate_dirblocksize(&dirblocksize, &dirblocklog, blocklog); + validate_inodesize(&isize, &inodelog, &inopblock, blocklog); + + /* + * if the device size was specified convert it to a block count + * now we have a valid block size. These will be set to zero if + * nothing was specified, indicating we should use the full device. + */ + dblocks = calc_dev_size(cligeo.dsize, &dopts, D_SIZE, blocklog, "data"); + logblocks = calc_dev_size(cligeo.logsize, &lopts, L_SIZE, blocklog, "log"); + rtblocks = calc_dev_size(cligeo.rtsize, &ropts, R_SIZE, blocklog, "rt"); + + validate_rtextsize(&rtextblocks, &ft, blocklog); + + calc_stripe_factors(sectorsize, lsectorsize, &dsunit, &dswidth, &lsunit); - xi.setblksize = sectorsize; /* * Initialize. This will open the log and rt devices as well. */ + xi.setblksize = sectorsize; if (!libxfs_init(&xi)) usage(); if (!xi.ddev) { @@ -3364,7 +3506,7 @@ _("size %s specified for log subvolume is too large, maximum is %lld blocks\n"), static void conflict( char opt, - char *tab[], + const char *tab[], int oldidx, int newidx) { @@ -3393,7 +3535,7 @@ ispow2( static void __attribute__((noreturn)) reqval( char opt, - char *tab[], + const char *tab[], int idx) { fprintf(stderr, _("-%c %s option requires a value\n"), opt, tab[idx]); @@ -3403,7 +3545,7 @@ reqval( static void respec( char opt, - char *tab[], + const char *tab[], int idx) { fprintf(stderr, "-%c ", opt); -- To unsubscribe from this list: send the line "unsubscribe linux-xfs" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html