blob: a135e06e72155dd4428dbf8dc9d9fc50d3f67be7 [file] [log] [blame]
/*
* Copyright (c) 2000-2005 Silicon Graphics, Inc.
* All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it would be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "libfrog.h"
#include "libxfs.h"
#include <ctype.h>
#include "xfs_multidisk.h"
#include "libxcmd.h"
#include "fsgeom.h"
#define TERABYTES(count, blog) ((uint64_t)(count) << (40 - (blog)))
#define GIGABYTES(count, blog) ((uint64_t)(count) << (30 - (blog)))
#define MEGABYTES(count, blog) ((uint64_t)(count) << (20 - (blog)))
/*
* Use this macro before we have superblock and mount structure to
* convert from basic blocks to filesystem blocks.
*/
#define DTOBT(d, bl) ((xfs_rfsblock_t)((d) >> ((bl) - BBSHIFT)))
/*
* amount (in bytes) we zero at the beginning and end of the device to
* remove traces of other filesystems, raid superblocks, etc.
*/
#define WHACK_SIZE (128 * 1024)
/*
* XXX: The configured block and sector sizes are defined as global variables so
* that they don't need to be passed to getnum/cvtnum().
*/
unsigned int blocksize;
unsigned int sectorsize;
/*
* Enums for each CLI parameter type are declared first so we can calculate the
* maximum array size needed to hold them automatically.
*/
enum {
B_SIZE = 0,
B_MAX_OPTS,
};
enum {
D_AGCOUNT = 0,
D_FILE,
D_NAME,
D_SIZE,
D_SUNIT,
D_SWIDTH,
D_AGSIZE,
D_SU,
D_SW,
D_SECTSIZE,
D_NOALIGN,
D_RTINHERIT,
D_PROJINHERIT,
D_EXTSZINHERIT,
D_COWEXTSIZE,
D_MAX_OPTS,
};
enum {
I_ALIGN = 0,
I_MAXPCT,
I_PERBLOCK,
I_SIZE,
I_ATTR,
I_PROJID32BIT,
I_SPINODES,
I_MAX_OPTS,
};
enum {
L_AGNUM = 0,
L_INTERNAL,
L_SIZE,
L_VERSION,
L_SUNIT,
L_SU,
L_DEV,
L_SECTSIZE,
L_FILE,
L_NAME,
L_LAZYSBCNTR,
L_MAX_OPTS,
};
enum {
N_SIZE = 0,
N_VERSION,
N_FTYPE,
N_MAX_OPTS,
};
enum {
R_EXTSIZE = 0,
R_SIZE,
R_DEV,
R_FILE,
R_NAME,
R_NOALIGN,
R_MAX_OPTS,
};
enum {
S_SIZE = 0,
S_SECTSIZE,
S_MAX_OPTS,
};
enum {
M_CRC = 0,
M_FINOBT,
M_UUID,
M_RMAPBT,
M_REFLINK,
M_MAX_OPTS,
};
/* Just define the max options array size manually right now */
#define MAX_SUBOPTS D_MAX_OPTS
#define SUBOPT_NEEDS_VAL (-1LL)
#define MAX_CONFLICTS 8
#define LAST_CONFLICT (-1)
/*
* Table for parsing mkfs parameters.
*
* Description of the structure members follows:
*
* name MANDATORY
* Name is a single char, e.g., for '-d file', name is 'd'.
*
* subopts MANDATORY
* Subopts is a list of strings naming suboptions. In the example above,
* it would contain "file". The last entry of this list has to be NULL.
*
* subopt_params MANDATORY
* This is a list of structs tied with subopts. For each entry in subopts,
* a corresponding entry has to be defined:
*
* subopt_params struct:
* index MANDATORY
* This number, starting from zero, denotes which item in subopt_params
* it is. The index has to be the same as is the order in subopts list,
* so we can access the right item both in subopt_param and subopts.
*
* seen INTERNAL
* Do not set this flag when definning a subopt. It is used to remeber that
* this subopt was already seen, for example for conflicts detection.
*
* str_seen INTERNAL
* Do not set. It is used internally for respecification, when some options
* has to be parsed twice - at first as a string, then later as a number.
*
* convert OPTIONAL
* A flag signalling whether the user-given value can use suffixes.
* If you want to allow the use of user-friendly values like 13k, 42G,
* set it to true.
*
* is_power_2 OPTIONAL
* An optional flag for subopts where the given value has to be a power
* of two.
*
* conflicts MANDATORY
* If your subopt is in a conflict with some other option, specify it.
* Accepts the .index values of the conflicting subopts and the last
* member of this list has to be LAST_CONFLICT.
*
* minval, maxval OPTIONAL
* These options are used for automatic range check and they have to be
* always used together in pair. If you don't want to limit the max value,
* use something like UINT_MAX. If no value is given, then you must either
* supply your own validation, or refuse any value in the 'case
* X_SOMETHING' block. If you forget to define the min and max value, but
* call a standard function for validating user's value, it will cause an
* error message notifying you about this issue.
*
* (Said in another way, you can't have minval and maxval both equal
* to zero. But if one value is different: minval=0 and maxval=1,
* then it is OK.)
*
* defaultval MANDATORY
* The value used if user specifies the subopt, but no value.
* If the subopt accepts some values (-d file=[1|0]), then this
* sets what is used with simple specifying the subopt (-d file).
* A special SUBOPT_NEEDS_VAL can be used to require a user-given
* value in any case.
*/
struct opt_params {
const char name;
const char *subopts[MAX_SUBOPTS];
struct subopt_param {
int index;
bool seen;
bool str_seen;
bool convert;
bool is_power_2;
struct _conflict {
struct opt_params *opts;
int subopt;
} conflicts[MAX_CONFLICTS];
long long minval;
long long maxval;
long long defaultval;
} subopt_params[MAX_SUBOPTS];
};
/*
* The two dimensional conflict array requires some initialisations to know
* about tables that haven't yet been defined. Work around this ordering
* issue with extern definitions here.
*/
extern struct opt_params sopts;
struct opt_params bopts = {
.name = 'b',
.subopts = {
[B_SIZE] = "size",
},
.subopt_params = {
{ .index = B_SIZE,
.convert = true,
.is_power_2 = true,
.conflicts = { { NULL, LAST_CONFLICT } },
.minval = XFS_MIN_BLOCKSIZE,
.maxval = XFS_MAX_BLOCKSIZE,
.defaultval = SUBOPT_NEEDS_VAL,
},
},
};
struct opt_params dopts = {
.name = 'd',
.subopts = {
[D_AGCOUNT] = "agcount",
[D_FILE] = "file",
[D_NAME] = "name",
[D_SIZE] = "size",
[D_SUNIT] = "sunit",
[D_SWIDTH] = "swidth",
[D_AGSIZE] = "agsize",
[D_SU] = "su",
[D_SW] = "sw",
[D_SECTSIZE] = "sectsize",
[D_NOALIGN] = "noalign",
[D_RTINHERIT] = "rtinherit",
[D_PROJINHERIT] = "projinherit",
[D_EXTSZINHERIT] = "extszinherit",
[D_COWEXTSIZE] = "cowextsize",
},
.subopt_params = {
{ .index = D_AGCOUNT,
.conflicts = { { &dopts, D_AGSIZE },
{ NULL, LAST_CONFLICT } },
.minval = 1,
.maxval = XFS_MAX_AGNUMBER,
.defaultval = SUBOPT_NEEDS_VAL,
},
{ .index = D_FILE,
.conflicts = { { NULL, LAST_CONFLICT } },
.minval = 0,
.maxval = 1,
.defaultval = 1,
},
{ .index = D_NAME,
.conflicts = { { NULL, LAST_CONFLICT } },
.defaultval = SUBOPT_NEEDS_VAL,
},
{ .index = D_SIZE,
.conflicts = { { NULL, LAST_CONFLICT } },
.convert = true,
.minval = XFS_AG_MIN_BYTES,
.maxval = LLONG_MAX,
.defaultval = SUBOPT_NEEDS_VAL,
},
{ .index = D_SUNIT,
.conflicts = { { &dopts, D_NOALIGN },
{ &dopts, D_SU },
{ &dopts, D_SW },
{ NULL, LAST_CONFLICT } },
.minval = 0,
.maxval = UINT_MAX,
.defaultval = SUBOPT_NEEDS_VAL,
},
{ .index = D_SWIDTH,
.conflicts = { { &dopts, D_NOALIGN },
{ &dopts, D_SU },
{ &dopts, D_SW },
{ NULL, LAST_CONFLICT } },
.minval = 0,
.maxval = UINT_MAX,
.defaultval = SUBOPT_NEEDS_VAL,
},
{ .index = D_AGSIZE,
.conflicts = { { &dopts, D_AGCOUNT },
{ NULL, LAST_CONFLICT } },
.convert = true,
.minval = XFS_AG_MIN_BYTES,
.maxval = XFS_AG_MAX_BYTES,
.defaultval = SUBOPT_NEEDS_VAL,
},
{ .index = D_SU,
.conflicts = { { &dopts, D_NOALIGN },
{ &dopts, D_SUNIT },
{ &dopts, D_SWIDTH },
{ NULL, LAST_CONFLICT } },
.convert = true,
.minval = 0,
.maxval = UINT_MAX,
.defaultval = SUBOPT_NEEDS_VAL,
},
{ .index = D_SW,
.conflicts = { { &dopts, D_NOALIGN },
{ &dopts, D_SUNIT },
{ &dopts, D_SWIDTH },
{ NULL, LAST_CONFLICT } },
.minval = 0,
.maxval = UINT_MAX,
.defaultval = SUBOPT_NEEDS_VAL,
},
{ .index = D_SECTSIZE,
.conflicts = { { &sopts, S_SIZE },
{ &sopts, S_SECTSIZE },
{ NULL, LAST_CONFLICT } },
.convert = true,
.is_power_2 = true,
.minval = XFS_MIN_SECTORSIZE,
.maxval = XFS_MAX_SECTORSIZE,
.defaultval = SUBOPT_NEEDS_VAL,
},
{ .index = D_NOALIGN,
.conflicts = { { &dopts, D_SU },
{ &dopts, D_SW },
{ &dopts, D_SUNIT },
{ &dopts, D_SWIDTH },
{ NULL, LAST_CONFLICT } },
.minval = 0,
.maxval = 1,
.defaultval = 1,
},
{ .index = D_RTINHERIT,
.conflicts = { { NULL, LAST_CONFLICT } },
.minval = 1,
.maxval = 1,
.defaultval = 1,
},
{ .index = D_PROJINHERIT,
.conflicts = { { NULL, LAST_CONFLICT } },
.minval = 0,
.maxval = UINT_MAX,
.defaultval = SUBOPT_NEEDS_VAL,
},
{ .index = D_EXTSZINHERIT,
.conflicts = { { NULL, LAST_CONFLICT } },
.minval = 0,
.maxval = UINT_MAX,
.defaultval = SUBOPT_NEEDS_VAL,
},
{ .index = D_COWEXTSIZE,
.conflicts = { { NULL, LAST_CONFLICT } },
.minval = 0,
.maxval = UINT_MAX,
.defaultval = SUBOPT_NEEDS_VAL,
},
},
};
struct opt_params iopts = {
.name = 'i',
.subopts = {
[I_ALIGN] = "align",
[I_MAXPCT] = "maxpct",
[I_PERBLOCK] = "perblock",
[I_SIZE] = "size",
[I_ATTR] = "attr",
[I_PROJID32BIT] = "projid32bit",
[I_SPINODES] = "sparse",
},
.subopt_params = {
{ .index = I_ALIGN,
.conflicts = { { NULL, LAST_CONFLICT } },
.minval = 0,
.maxval = 1,
.defaultval = 1,
},
{ .index = I_MAXPCT,
.conflicts = { { NULL, LAST_CONFLICT } },
.minval = 0,
.maxval = 100,
.defaultval = SUBOPT_NEEDS_VAL,
},
{ .index = I_PERBLOCK,
.conflicts = { { &iopts, I_SIZE },
{ NULL, LAST_CONFLICT } },
.is_power_2 = true,
.minval = XFS_MIN_INODE_PERBLOCK,
.maxval = XFS_MAX_BLOCKSIZE / XFS_DINODE_MIN_SIZE,
.defaultval = SUBOPT_NEEDS_VAL,
},
{ .index = I_SIZE,
.conflicts = { { &iopts, I_PERBLOCK },
{ NULL, LAST_CONFLICT } },
.is_power_2 = true,
.minval = XFS_DINODE_MIN_SIZE,
.maxval = XFS_DINODE_MAX_SIZE,
.defaultval = SUBOPT_NEEDS_VAL,
},
{ .index = I_ATTR,
.conflicts = { { NULL, LAST_CONFLICT } },
.minval = 0,
.maxval = 2,
.defaultval = SUBOPT_NEEDS_VAL,
},
{ .index = I_PROJID32BIT,
.conflicts = { { NULL, LAST_CONFLICT } },
.minval = 0,
.maxval = 1,
.defaultval = 1,
},
{ .index = I_SPINODES,
.conflicts = { { NULL, LAST_CONFLICT } },
.minval = 0,
.maxval = 1,
.defaultval = 1,
},
},
};
struct opt_params lopts = {
.name = 'l',
.subopts = {
[L_AGNUM] = "agnum",
[L_INTERNAL] = "internal",
[L_SIZE] = "size",
[L_VERSION] = "version",
[L_SUNIT] = "sunit",
[L_SU] = "su",
[L_DEV] = "logdev",
[L_SECTSIZE] = "sectsize",
[L_FILE] = "file",
[L_NAME] = "name",
[L_LAZYSBCNTR] = "lazy-count",
},
.subopt_params = {
{ .index = L_AGNUM,
.conflicts = { { &lopts, L_DEV },
{ NULL, LAST_CONFLICT } },
.minval = 0,
.maxval = UINT_MAX,
.defaultval = SUBOPT_NEEDS_VAL,
},
{ .index = L_INTERNAL,
.conflicts = { { &lopts, L_FILE },
{ &lopts, L_DEV },
{ &lopts, L_SECTSIZE },
{ NULL, LAST_CONFLICT } },
.minval = 0,
.maxval = 1,
.defaultval = 1,
},
{ .index = L_SIZE,
.conflicts = { { NULL, LAST_CONFLICT } },
.convert = true,
.minval = 2 * 1024 * 1024LL, /* XXX: XFS_MIN_LOG_BYTES */
.maxval = XFS_MAX_LOG_BYTES,
.defaultval = SUBOPT_NEEDS_VAL,
},
{ .index = L_VERSION,
.conflicts = { { NULL, LAST_CONFLICT } },
.minval = 1,
.maxval = 2,
.defaultval = SUBOPT_NEEDS_VAL,
},
{ .index = L_SUNIT,
.conflicts = { { &lopts, L_SU },
{ NULL, LAST_CONFLICT } },
.minval = 1,
.maxval = BTOBB(XLOG_MAX_RECORD_BSIZE),
.defaultval = SUBOPT_NEEDS_VAL,
},
{ .index = L_SU,
.conflicts = { { &lopts, L_SUNIT },
{ NULL, LAST_CONFLICT } },
.convert = true,
.minval = BBTOB(1),
.maxval = XLOG_MAX_RECORD_BSIZE,
.defaultval = SUBOPT_NEEDS_VAL,
},
{ .index = L_DEV,
.conflicts = { { &lopts, L_AGNUM },
{ &lopts, L_NAME },
{ &lopts, L_INTERNAL },
{ NULL, LAST_CONFLICT } },
.defaultval = SUBOPT_NEEDS_VAL,
},
{ .index = L_SECTSIZE,
.conflicts = { { &lopts, L_INTERNAL },
{ NULL, LAST_CONFLICT } },
.convert = true,
.is_power_2 = true,
.minval = XFS_MIN_SECTORSIZE,
.maxval = XFS_MAX_SECTORSIZE,
.defaultval = SUBOPT_NEEDS_VAL,
},
{ .index = L_FILE,
.conflicts = { { &lopts, L_INTERNAL },
{ NULL, LAST_CONFLICT } },
.minval = 0,
.maxval = 1,
.defaultval = 1,
},
{ .index = L_NAME,
.conflicts = { { &lopts, L_AGNUM },
{ &lopts, L_DEV },
{ &lopts, L_INTERNAL },
{ NULL, LAST_CONFLICT } },
.defaultval = SUBOPT_NEEDS_VAL,
},
{ .index = L_LAZYSBCNTR,
.conflicts = { { NULL, LAST_CONFLICT } },
.minval = 0,
.maxval = 1,
.defaultval = 1,
},
},
};
struct opt_params nopts = {
.name = 'n',
.subopts = {
[N_SIZE] = "size",
[N_VERSION] = "version",
[N_FTYPE] = "ftype",
},
.subopt_params = {
{ .index = N_SIZE,
.conflicts = { { NULL, LAST_CONFLICT } },
.convert = true,
.is_power_2 = true,
.minval = 1 << XFS_MIN_REC_DIRSIZE,
.maxval = XFS_MAX_BLOCKSIZE,
.defaultval = SUBOPT_NEEDS_VAL,
},
{ .index = N_VERSION,
.conflicts = { { NULL, LAST_CONFLICT } },
.minval = 2,
.maxval = 2,
.defaultval = SUBOPT_NEEDS_VAL,
},
{ .index = N_FTYPE,
.conflicts = { { NULL, LAST_CONFLICT } },
.minval = 0,
.maxval = 1,
.defaultval = 1,
},
},
};
struct opt_params ropts = {
.name = 'r',
.subopts = {
[R_EXTSIZE] = "extsize",
[R_SIZE] = "size",
[R_DEV] = "rtdev",
[R_FILE] = "file",
[R_NAME] = "name",
[R_NOALIGN] = "noalign",
},
.subopt_params = {
{ .index = R_EXTSIZE,
.conflicts = { { NULL, LAST_CONFLICT } },
.convert = true,
.minval = XFS_MIN_RTEXTSIZE,
.maxval = XFS_MAX_RTEXTSIZE,
.defaultval = SUBOPT_NEEDS_VAL,
},
{ .index = R_SIZE,
.conflicts = { { NULL, LAST_CONFLICT } },
.convert = true,
.minval = 0,
.maxval = LLONG_MAX,
.defaultval = SUBOPT_NEEDS_VAL,
},
{ .index = R_DEV,
.conflicts = { { &ropts, R_NAME },
{ NULL, LAST_CONFLICT } },
.defaultval = SUBOPT_NEEDS_VAL,
},
{ .index = R_FILE,
.minval = 0,
.maxval = 1,
.defaultval = 1,
.conflicts = { { NULL, LAST_CONFLICT } },
},
{ .index = R_NAME,
.conflicts = { { &ropts, R_DEV },
{ NULL, LAST_CONFLICT } },
.defaultval = SUBOPT_NEEDS_VAL,
},
{ .index = R_NOALIGN,
.minval = 0,
.maxval = 1,
.defaultval = 1,
.conflicts = { { NULL, LAST_CONFLICT } },
},
},
};
struct opt_params sopts = {
.name = 's',
.subopts = {
[S_SIZE] = "size",
[S_SECTSIZE] = "sectsize",
},
.subopt_params = {
{ .index = S_SIZE,
.conflicts = { { &sopts, S_SECTSIZE },
{ &dopts, D_SECTSIZE },
{ NULL, LAST_CONFLICT } },
.convert = true,
.is_power_2 = true,
.minval = XFS_MIN_SECTORSIZE,
.maxval = XFS_MAX_SECTORSIZE,
.defaultval = SUBOPT_NEEDS_VAL,
},
{ .index = S_SECTSIZE,
.conflicts = { { &sopts, S_SIZE },
{ &dopts, D_SECTSIZE },
{ NULL, LAST_CONFLICT } },
.convert = true,
.is_power_2 = true,
.minval = XFS_MIN_SECTORSIZE,
.maxval = XFS_MAX_SECTORSIZE,
.defaultval = SUBOPT_NEEDS_VAL,
},
},
};
struct opt_params mopts = {
.name = 'm',
.subopts = {
[M_CRC] = "crc",
[M_FINOBT] = "finobt",
[M_UUID] = "uuid",
[M_RMAPBT] = "rmapbt",
[M_REFLINK] = "reflink",
},
.subopt_params = {
{ .index = M_CRC,
.conflicts = { { NULL, LAST_CONFLICT } },
.minval = 0,
.maxval = 1,
.defaultval = 1,
},
{ .index = M_FINOBT,
.conflicts = { { NULL, LAST_CONFLICT } },
.minval = 0,
.maxval = 1,
.defaultval = 1,
},
{ .index = M_UUID,
.conflicts = { { NULL, LAST_CONFLICT } },
.defaultval = SUBOPT_NEEDS_VAL,
},
{ .index = M_RMAPBT,
.conflicts = { { NULL, LAST_CONFLICT } },
.minval = 0,
.maxval = 1,
.defaultval = 1,
},
{ .index = M_REFLINK,
.conflicts = { { NULL, LAST_CONFLICT } },
.minval = 0,
.maxval = 1,
.defaultval = 1,
},
},
};
/* quick way of checking if a parameter was set on the CLI */
static bool
cli_opt_set(
struct opt_params *opts,
int subopt)
{
return opts->subopt_params[subopt].seen ||
opts->subopt_params[subopt].str_seen;
}
/*
* Options configured on the command line.
*
* This stores all the specific config parameters the user sets on the command
* line. We do not use these values directly - they are inputs to the mkfs
* geometry validation and override any default configuration value we have.
*
* We don't keep flags to indicate what parameters are set - if we need to check
* if an option was set on the command line, we check the relevant entry in the
* option table which records whether it was specified in the .seen and
* .str_seen variables in the table.
*
* Some parameters are stored as strings for post-parsing after their dependent
* options have been resolved (e.g. block size and sector size have been parsed
* and validated).
*
* This allows us to check that values have been set without needing separate
* flags for each value, and hence avoids needing to record and check for each
* specific option that can set the value later on in the code. In the cases
* where we don't have a cli_params structure around, the above cli_opt_set()
* function can be used.
*/
struct sb_feat_args {
int log_version;
int attr_version;
int dir_version;
bool inode_align; /* XFS_SB_VERSION_ALIGNBIT */
bool nci; /* XFS_SB_VERSION_BORGBIT */
bool lazy_sb_counters; /* XFS_SB_VERSION2_LAZYSBCOUNTBIT */
bool parent_pointers; /* XFS_SB_VERSION2_PARENTBIT */
bool projid32bit; /* XFS_SB_VERSION2_PROJID32BIT */
bool crcs_enabled; /* XFS_SB_VERSION2_CRCBIT */
bool dirftype; /* XFS_SB_VERSION2_FTYPE */
bool finobt; /* XFS_SB_FEAT_RO_COMPAT_FINOBT */
bool spinodes; /* XFS_SB_FEAT_INCOMPAT_SPINODES */
bool rmapbt; /* XFS_SB_FEAT_RO_COMPAT_RMAPBT */
bool reflink; /* XFS_SB_FEAT_RO_COMPAT_REFLINK */
bool nodalign;
bool nortalign;
};
struct cli_params {
int sectorsize;
int blocksize;
/* parameters that depend on sector/block size being validated. */
char *dsize;
char *agsize;
char *dsu;
char *dirblocksize;
char *logsize;
char *lsu;
char *rtextsize;
char *rtsize;
/* parameters where 0 is a valid CLI value */
int dsunit;
int dswidth;
int dsw;
int64_t logagno;
int loginternal;
int lsunit;
/* parameters where 0 is not a valid value */
int64_t agcount;
int inodesize;
int inopblock;
int imaxpct;
int lsectorsize;
uuid_t uuid;
/* feature flags that are set */
struct sb_feat_args sb_feat;
/* root inode characteristics */
struct fsxattr fsx;
/* libxfs device setup */
struct libxfs_xinit *xi;
};
/*
* Calculated filesystem feature and geometry information.
*
* This structure contains the information we will use to create the on-disk
* filesystem from. The validation and calculation code uses it to store all the
* temporary and final config state for the filesystem.
*
* The information in this structure will contain a mix of validated CLI input
* variables, default feature state and calculated values that are needed to
* construct the superblock and other on disk features. These are all in one
* place so that we don't have to pass handfuls of seemingly arbitrary variables
* around to different functions to do the work we need to do.
*/
struct mkfs_params {
int blocksize;
int blocklog;
int sectorsize;
int sectorlog;
int lsectorsize;
int lsectorlog;
int dirblocksize;
int dirblocklog;
int inodesize;
int inodelog;
int inopblock;
uint64_t dblocks;
uint64_t logblocks;
uint64_t rtblocks;
uint64_t rtextblocks;
uint64_t rtextents;
uint64_t rtbmblocks; /* rt bitmap blocks */
int dsunit; /* in FSBs */
int dswidth; /* in FSBs */
int lsunit; /* in FSBs */
uint64_t agsize;
uint64_t agcount;
int imaxpct;
bool loginternal;
uint64_t logstart;
uint64_t logagno;
uuid_t uuid;
char *label;
struct sb_feat_args sb_feat;
};
/*
* Default filesystem features and configuration values
*
* This structure contains the default mkfs values that are to be used when
* a user does not specify the option on the command line. We do not use these
* values directly - they are inputs to the mkfs geometry validation and
* calculations.
*/
struct mkfs_default_params {
char *source; /* where the defaults came from */
int sectorsize;
int blocksize;
/* feature flags that are set */
struct sb_feat_args sb_feat;
/* root inode characteristics */
struct fsxattr fsx;
};
static void __attribute__((noreturn))
usage( void )
{
fprintf(stderr, _("Usage: %s\n\
/* blocksize */ [-b size=num]\n\
/* metadata */ [-m crc=0|1,finobt=0|1,uuid=xxx,rmapbt=0|1,reflink=0|1]\n\
/* data subvol */ [-d agcount=n,agsize=n,file,name=xxx,size=num,\n\
(sunit=value,swidth=value|su=num,sw=num|noalign),\n\
sectsize=num\n\
/* force overwrite */ [-f]\n\
/* inode size */ [-i log=n|perblock=n|size=num,maxpct=n,attr=0|1|2,\n\
projid32bit=0|1,sparse=0|1]\n\
/* no discard */ [-K]\n\
/* log subvol */ [-l agnum=n,internal,size=num,logdev=xxx,version=n\n\
sunit=value|su=num,sectsize=num,lazy-count=0|1]\n\
/* label */ [-L label (maximum 12 characters)]\n\
/* naming */ [-n size=num,version=2|ci,ftype=0|1]\n\
/* no-op info only */ [-N]\n\
/* prototype file */ [-p fname]\n\
/* quiet */ [-q]\n\
/* realtime subvol */ [-r extsize=num,size=num,rtdev=xxx]\n\
/* sectorsize */ [-s size=num]\n\
/* version */ [-V]\n\
devicename\n\
<devicename> is required unless -d name=xxx is given.\n\
<num> is xxx (bytes), xxxs (sectors), xxxb (fs blocks), xxxk (xxx KiB),\n\
xxxm (xxx MiB), xxxg (xxx GiB), xxxt (xxx TiB) or xxxp (xxx PiB).\n\
<value> is xxx (512 byte blocks).\n"),
progname);
exit(1);
}
static void
conflict(
struct opt_params *opts,
int option,
struct opt_params *con_opts,
int conflict)
{
fprintf(stderr, _("Cannot specify both -%c %s and -%c %s\n"),
con_opts->name, con_opts->subopts[conflict],
opts->name, opts->subopts[option]);
usage();
}
static void
illegal(
const char *value,
const char *opt)
{
fprintf(stderr, _("Invalid value %s for -%s option\n"), value, opt);
usage();
}
static int
ispow2(
unsigned int i)
{
return (i & (i - 1)) == 0;
}
static void __attribute__((noreturn))
reqval(
char opt,
const char *tab[],
int idx)
{
fprintf(stderr, _("-%c %s option requires a value\n"), opt, tab[idx]);
usage();
}
static void
respec(
char opt,
const char *tab[],
int idx)
{
fprintf(stderr, "-%c ", opt);
if (tab)
fprintf(stderr, "%s ", tab[idx]);
fprintf(stderr, _("option respecified\n"));
usage();
}
static void
unknown(
char opt,
char *s)
{
fprintf(stderr, _("unknown option -%c %s\n"), opt, s);
usage();
}
long long
cvtnum(
unsigned int blksize,
unsigned int sectsize,
const char *s)
{
long long i;
char *sp;
int c;
i = strtoll(s, &sp, 0);
if (i == 0 && sp == s)
return -1LL;
if (*sp == '\0')
return i;
if (sp[1] != '\0')
return -1LL;
if (*sp == 'b') {
if (!blksize) {
fprintf(stderr,
_("Blocksize must be provided prior to using 'b' suffix.\n"));
usage();
} else {
return i * blksize;
}
}
if (*sp == 's') {
if (!sectsize) {
fprintf(stderr,
_("Sectorsize must be specified prior to using 's' suffix.\n"));
usage();
} else {
return i * sectsize;
}
}
c = tolower(*sp);
switch (c) {
case 'e':
i *= 1024LL;
/* fall through */
case 'p':
i *= 1024LL;
/* fall through */
case 't':
i *= 1024LL;
/* fall through */
case 'g':
i *= 1024LL;
/* fall through */
case 'm':
i *= 1024LL;
/* fall through */
case 'k':
return i * 1024LL;
default:
break;
}
return -1LL;
}
static void
check_device_type(
const char *name,
int *isfile,
bool no_size,
bool no_name,
int *create,
bool force_overwrite,
const char *optname)
{
struct stat statbuf;
if (*isfile && (no_size || no_name)) {
fprintf(stderr,
_("if -%s file then -%s name and -%s size are required\n"),
optname, optname, optname);
usage();
}
if (!name) {
fprintf(stderr, _("No device name specified\n"));
usage();
}
if (stat(name, &statbuf)) {
if (errno == ENOENT && *isfile) {
if (create)
*create = 1;
return;
}
fprintf(stderr,
_("Error accessing specified device %s: %s\n"),
name, strerror(errno));
usage();
return;
}
if (!force_overwrite && check_overwrite(name)) {
fprintf(stderr,
_("%s: Use the -f option to force overwrite.\n"),
progname);
exit(1);
}
/*
* We only want to completely truncate and recreate an existing file if
* we were specifically told it was a file. Set the create flag only in
* this case to trigger that behaviour.
*/
if (S_ISREG(statbuf.st_mode)) {
if (!*isfile)
*isfile = 1;
else if (create)
*create = 1;
return;
}
if (S_ISBLK(statbuf.st_mode)) {
if (*isfile) {
fprintf(stderr,
_("specified \"-%s file\" on a block device %s\n"),
optname, name);
usage();
}
return;
}
fprintf(stderr,
_("specified device %s not a file or block device\n"),
name);
usage();
}
static void
validate_ag_geometry(
int blocklog,
uint64_t dblocks,
uint64_t agsize,
uint64_t agcount)
{
if (agsize < XFS_AG_MIN_BLOCKS(blocklog)) {
fprintf(stderr,
_("agsize (%lld blocks) too small, need at least %lld blocks\n"),
(long long)agsize,
(long long)XFS_AG_MIN_BLOCKS(blocklog));
usage();
}
if (agsize > XFS_AG_MAX_BLOCKS(blocklog)) {
fprintf(stderr,
_("agsize (%lld blocks) too big, maximum is %lld blocks\n"),
(long long)agsize,
(long long)XFS_AG_MAX_BLOCKS(blocklog));
usage();
}
if (agsize > dblocks) {
fprintf(stderr,
_("agsize (%lld blocks) too big, data area is %lld blocks\n"),
(long long)agsize, (long long)dblocks);
usage();
}
if (agsize < XFS_AG_MIN_BLOCKS(blocklog)) {
fprintf(stderr,
_("too many allocation groups for size = %lld\n"),
(long long)agsize);
fprintf(stderr, _("need at most %lld allocation groups\n"),
(long long)(dblocks / XFS_AG_MIN_BLOCKS(blocklog) +
(dblocks % XFS_AG_MIN_BLOCKS(blocklog) != 0)));
usage();
}
if (agsize > XFS_AG_MAX_BLOCKS(blocklog)) {
fprintf(stderr,
_("too few allocation groups for size = %lld\n"), (long long)agsize);
fprintf(stderr,
_("need at least %lld allocation groups\n"),
(long long)(dblocks / XFS_AG_MAX_BLOCKS(blocklog) +
(dblocks % XFS_AG_MAX_BLOCKS(blocklog) != 0)));
usage();
}
/*
* If the last AG is too small, reduce the filesystem size
* and drop the blocks.
*/
if ( dblocks % agsize != 0 &&
(dblocks % agsize < XFS_AG_MIN_BLOCKS(blocklog))) {
fprintf(stderr,
_("last AG size %lld blocks too small, minimum size is %lld blocks\n"),
(long long)(dblocks % agsize),
(long long)XFS_AG_MIN_BLOCKS(blocklog));
usage();
}
/*
* If agcount is too large, make it smaller.
*/
if (agcount > XFS_MAX_AGNUMBER + 1) {
fprintf(stderr,
_("%lld allocation groups is too many, maximum is %lld\n"),
(long long)agcount, (long long)XFS_MAX_AGNUMBER + 1);
usage();
}
}
static void
zero_old_xfs_structures(
libxfs_init_t *xi,
xfs_sb_t *new_sb)
{
void *buf;
xfs_sb_t sb;
uint32_t bsize;
int i;
xfs_off_t off;
/*
* We open regular files with O_TRUNC|O_CREAT. Nothing to do here...
*/
if (xi->disfile && xi->dcreat)
return;
/*
* read in existing filesystem superblock, use its geometry
* settings and zero the existing secondary superblocks.
*/
buf = memalign(libxfs_device_alignment(), new_sb->sb_sectsize);
if (!buf) {
fprintf(stderr,
_("error reading existing superblock -- failed to memalign buffer\n"));
return;
}
memset(buf, 0, new_sb->sb_sectsize);
/*
* If we are creating an image file, it might be of zero length at this
* point in time. Hence reading the existing superblock is going to
* return zero bytes. It's not a failure we need to warn about in this
* case.
*/
off = pread(xi->dfd, buf, new_sb->sb_sectsize, 0);
if (off != new_sb->sb_sectsize) {
if (!xi->disfile)
fprintf(stderr,
_("error reading existing superblock: %s\n"),
strerror(errno));
goto done;
}
libxfs_sb_from_disk(&sb, buf);
/*
* perform same basic superblock validation to make sure we
* actually zero secondary blocks
*/
if (sb.sb_magicnum != XFS_SB_MAGIC || sb.sb_blocksize == 0)
goto done;
for (bsize = 1, i = 0; bsize < sb.sb_blocksize &&
i < sizeof(sb.sb_blocksize) * NBBY; i++)
bsize <<= 1;
if (i < XFS_MIN_BLOCKSIZE_LOG || i > XFS_MAX_BLOCKSIZE_LOG ||
i != sb.sb_blocklog)
goto done;
if (sb.sb_dblocks > ((uint64_t)sb.sb_agcount * sb.sb_agblocks) ||
sb.sb_dblocks < ((uint64_t)(sb.sb_agcount - 1) *
sb.sb_agblocks + XFS_MIN_AG_BLOCKS))
goto done;
/*
* block size and basic geometry seems alright, zero the secondaries.
*/
memset(buf, 0, new_sb->sb_sectsize);
off = 0;
for (i = 1; i < sb.sb_agcount; i++) {
off += sb.sb_agblocks;
if (pwrite(xi->dfd, buf, new_sb->sb_sectsize,
off << sb.sb_blocklog) == -1)
break;
}
done:
free(buf);
}
static void
discard_blocks(dev_t dev, uint64_t nsectors)
{
int fd;
/*
* We intentionally ignore errors from the discard ioctl. It is
* not necessary for the mkfs functionality but just an optimization.
*/
fd = libxfs_device_to_fd(dev);
if (fd > 0)
platform_discard_blocks(fd, 0, nsectors << 9);
}
static __attribute__((noreturn)) void
illegal_option(
const char *value,
struct opt_params *opts,
int index,
const char *reason)
{
fprintf(stderr,
_("Invalid value %s for -%c %s option. %s\n"),
value, opts->name, opts->subopts[index],
reason);
usage();
}
/*
* Check for conflicts and option respecification.
*/
static void
check_opt(
struct opt_params *opts,
int index,
bool str_seen)
{
struct subopt_param *sp = &opts->subopt_params[index];
int i;
if (sp->index != index) {
fprintf(stderr,
_("Developer screwed up option parsing (%d/%d)! Please report!\n"),
sp->index, index);
reqval(opts->name, opts->subopts, index);
}
/*
* Check for respecification of the option. This is more complex than it
* seems because some options are parsed twice - once as a string during
* input parsing, then later the string is passed to getnum for
* conversion into a number and bounds checking. Hence the two variables
* used to track the different uses based on the @str parameter passed
* to us.
*/
if (!str_seen) {
if (sp->seen)
respec(opts->name, opts->subopts, index);
sp->seen = true;
} else {
if (sp->str_seen)
respec(opts->name, opts->subopts, index);
sp->str_seen = true;
}
/* check for conflicts with the option */
for (i = 0; i < MAX_CONFLICTS; i++) {
struct _conflict *con = &sp->conflicts[i];
if (con->subopt == LAST_CONFLICT)
break;
if (con->opts->subopt_params[con->subopt].seen ||
con->opts->subopt_params[con->subopt].str_seen)
conflict(opts, index, con->opts, con->subopt);
}
}
static long long
getnum(
const char *str,
struct opt_params *opts,
int index)
{
struct subopt_param *sp = &opts->subopt_params[index];
long long c;
check_opt(opts, index, false);
/* empty strings might just return a default value */
if (!str || *str == '\0') {
if (sp->defaultval == SUBOPT_NEEDS_VAL)
reqval(opts->name, opts->subopts, index);
return sp->defaultval;
}
if (sp->minval == 0 && sp->maxval == 0) {
fprintf(stderr,
_("Option -%c %s has undefined minval/maxval."
"Can't verify value range. This is a bug.\n"),
opts->name, opts->subopts[index]);
exit(1);
}
/*
* Some values are pure numbers, others can have suffixes that define
* the units of the number. Those get passed to cvtnum(), otherwise we
* convert it ourselves to guarantee there is no trailing garbage in the
* number.
*/
if (sp->convert)
c = cvtnum(blocksize, sectorsize, str);
else {
char *str_end;
c = strtoll(str, &str_end, 0);
if (c == 0 && str_end == str)
illegal_option(str, opts, index,
_("Value not recognized as number."));
if (*str_end != '\0')
illegal_option(str, opts, index,
_("Unit suffixes are not allowed."));
}
/* Validity check the result. */
if (c < sp->minval)
illegal_option(str, opts, index, _("Value is too small."));
else if (c > sp->maxval)
illegal_option(str, opts, index, _("Value is too large."));
if (sp->is_power_2 && !ispow2(c))
illegal_option(str, opts, index, _("Value must be a power of 2."));
return c;
}
/*
* Option is a string - do all the option table work, and check there
* is actually an option string. Otherwise we don't do anything with the string
* here - validation will be done later when the string is converted to a value
* or used as a file/device path.
*/
static char *
getstr(
char *str,
struct opt_params *opts,
int index)
{
check_opt(opts, index, true);
/* empty strings for string options are not valid */
if (!str || *str == '\0')
reqval(opts->name, opts->subopts, index);
return str;
}
static int
block_opts_parser(
struct opt_params *opts,
int subopt,
char *value,
struct cli_params *cli)
{
switch (subopt) {
case B_SIZE:
cli->blocksize = getnum(value, opts, subopt);
break;
default:
return -EINVAL;
}
return 0;
}
static int
data_opts_parser(
struct opt_params *opts,
int subopt,
char *value,
struct cli_params *cli)
{
switch (subopt) {
case D_AGCOUNT:
cli->agcount = getnum(value, opts, subopt);
break;
case D_AGSIZE:
cli->agsize = getstr(value, opts, subopt);
break;
case D_FILE:
cli->xi->disfile = getnum(value, opts, subopt);
break;
case D_NAME:
cli->xi->dname = getstr(value, opts, subopt);
break;
case D_SIZE:
cli->dsize = getstr(value, opts, subopt);
break;
case D_SUNIT:
cli->dsunit = getnum(value, opts, subopt);
break;
case D_SWIDTH:
cli->dswidth = getnum(value, opts, subopt);
break;
case D_SU:
cli->dsu = getstr(value, opts, subopt);
break;
case D_SW:
cli->dsw = getnum(value, opts, subopt);
break;
case D_NOALIGN:
cli->sb_feat.nodalign = getnum(value, opts, subopt);
break;
case D_SECTSIZE:
cli->sectorsize = getnum(value, opts, subopt);
break;
case D_RTINHERIT:
if (getnum(value, opts, subopt))
cli->fsx.fsx_xflags |= XFS_DIFLAG_RTINHERIT;
break;
case D_PROJINHERIT:
cli->fsx.fsx_projid = getnum(value, opts, subopt);
cli->fsx.fsx_xflags |= XFS_DIFLAG_PROJINHERIT;
break;
case D_EXTSZINHERIT:
cli->fsx.fsx_extsize = getnum(value, opts, subopt);
cli->fsx.fsx_xflags |= XFS_DIFLAG_EXTSZINHERIT;
break;
case D_COWEXTSIZE:
cli->fsx.fsx_cowextsize = getnum(value, opts, subopt);
cli->fsx.fsx_xflags |= FS_XFLAG_COWEXTSIZE;
break;
default:
return -EINVAL;
}
return 0;
}
static int
inode_opts_parser(
struct opt_params *opts,
int subopt,
char *value,
struct cli_params *cli)
{
switch (subopt) {
case I_ALIGN:
cli->sb_feat.inode_align = getnum(value, opts, subopt);
break;
case I_MAXPCT:
cli->imaxpct = getnum(value, opts, subopt);
break;
case I_PERBLOCK:
cli->inopblock = getnum(value, opts, subopt);
break;
case I_SIZE:
cli->inodesize = getnum(value, opts, subopt);
break;
case I_ATTR:
cli->sb_feat.attr_version = getnum(value, opts, subopt);
break;
case I_PROJID32BIT:
cli->sb_feat.projid32bit = getnum(value, opts, subopt);
break;
case I_SPINODES:
cli->sb_feat.spinodes = getnum(value, opts, subopt);
break;
default:
return -EINVAL;
}
return 0;
}
static int
log_opts_parser(
struct opt_params *opts,
int subopt,
char *value,
struct cli_params *cli)
{
switch (subopt) {
case L_AGNUM:
cli->logagno = getnum(value, opts, subopt);
break;
case L_FILE:
cli->xi->lisfile = getnum(value, opts, subopt);
break;
case L_INTERNAL:
cli->loginternal = getnum(value, opts, subopt);
break;
case L_SU:
cli->lsu = getstr(value, opts, subopt);
break;
case L_SUNIT:
cli->lsunit = getnum(value, opts, subopt);
break;
case L_NAME:
case L_DEV:
cli->xi->logname = getstr(value, opts, subopt);
cli->loginternal = 0;
break;
case L_VERSION:
cli->sb_feat.log_version = getnum(value, opts, subopt);
break;
case L_SIZE:
cli->logsize = getstr(value, opts, subopt);
break;
case L_SECTSIZE:
cli->lsectorsize = getnum(value, opts, subopt);
break;
case L_LAZYSBCNTR:
cli->sb_feat.lazy_sb_counters = getnum(value, opts, subopt);
break;
default:
return -EINVAL;
}
return 0;
}
static int
meta_opts_parser(
struct opt_params *opts,
int subopt,
char *value,
struct cli_params *cli)
{
switch (subopt) {
case M_CRC:
cli->sb_feat.crcs_enabled = getnum(value, opts, subopt);
if (cli->sb_feat.crcs_enabled)
cli->sb_feat.dirftype = true;
break;
case M_FINOBT:
cli->sb_feat.finobt = getnum(value, opts, subopt);
break;
case M_UUID:
if (!value || *value == '\0')
reqval('m', opts->subopts, subopt);
if (platform_uuid_parse(value, &cli->uuid))
illegal(value, "m uuid");
break;
case M_RMAPBT:
cli->sb_feat.rmapbt = getnum(value, opts, subopt);
break;
case M_REFLINK:
cli->sb_feat.reflink = getnum(value, opts, subopt);
break;
default:
return -EINVAL;
}
return 0;
}
static int
naming_opts_parser(
struct opt_params *opts,
int subopt,
char *value,
struct cli_params *cli)
{
switch (subopt) {
case N_SIZE:
cli->dirblocksize = getstr(value, opts, subopt);
break;
case N_VERSION:
value = getstr(value, &nopts, subopt);
if (!strcasecmp(value, "ci")) {
/* ASCII CI mode */
cli->sb_feat.nci = true;
} else {
cli->sb_feat.dir_version = getnum(value, opts, subopt);
}
break;
case N_FTYPE:
cli->sb_feat.dirftype = getnum(value, opts, subopt);
break;
default:
return -EINVAL;
}
return 0;
}
static int
rtdev_opts_parser(
struct opt_params *opts,
int subopt,
char *value,
struct cli_params *cli)
{
switch (subopt) {
case R_EXTSIZE:
cli->rtextsize = getstr(value, opts, subopt);
break;
case R_FILE:
cli->xi->risfile = getnum(value, opts, subopt);
break;
case R_NAME:
case R_DEV:
cli->xi->rtname = getstr(value, opts, subopt);
break;
case R_SIZE:
cli->rtsize = getstr(value, opts, subopt);
break;
case R_NOALIGN:
cli->sb_feat.nortalign = getnum(value, opts, subopt);
break;
default:
return -EINVAL;
}
return 0;
}
static int
sector_opts_parser(
struct opt_params *opts,
int subopt,
char *value,
struct cli_params *cli)
{
switch (subopt) {
case S_SIZE:
case S_SECTSIZE:
cli->sectorsize = getnum(value, opts, subopt);
cli->lsectorsize = cli->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 cli_params *cli)
{
struct subopts *sop = &subopt_tab[0];
char *p;
int ret = 0;
while (sop->opts) {
if (sop->opt == opt)
break;
sop++;
}
/* 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, cli);
if (ret)
unknown(opt, value);
}
}
static void
validate_sectorsize(
struct mkfs_params *cfg,
struct cli_params *cli,
struct mkfs_default_params *dft,
struct fs_topology *ft,
char *dfile,
int dry_run,
int force_overwrite)
{
/* set configured sector sizes in preparation for checks */
if (!cli->sectorsize) {
cfg->sectorsize = dft->sectorsize;
} else {
cfg->sectorsize = cli->sectorsize;
}
cfg->sectorlog = libxfs_highbit32(cfg->sectorsize);
/*
* Before anything else, verify that we are correctly operating on
* files or block devices and set the control parameters correctly.
*/
check_device_type(dfile, &cli->xi->disfile, !cli->dsize, !dfile,
dry_run ? NULL : &cli->xi->dcreat,
force_overwrite, "d");
if (!cli->loginternal)
check_device_type(cli->xi->logname, &cli->xi->lisfile,
!cli->logsize, !cli->xi->logname,
dry_run ? NULL : &cli->xi->lcreat,
force_overwrite, "l");
if (cli->xi->rtname)
check_device_type(cli->xi->rtname, &cli->xi->risfile,
!cli->rtsize, !cli->xi->rtname,
dry_run ? NULL : &cli->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 (cli->xi->disfile || cli->xi->lisfile || cli->xi->risfile)
cli->xi->isdirect = 0;
memset(ft, 0, sizeof(*ft));
get_topology(cli->xi, ft, force_overwrite);
if (!cli->sectorsize) {
/*
* Unless specified manually on the command line use the
* advertised sector size of the device. We use the physical
* sector size unless the requested block size is smaller
* than that, then we can use logical, but warn about the
* inefficiency.
*
* Set the topology sectors if they were not probed to the
* minimum supported sector size.
*/
if (!ft->lsectorsize)
ft->lsectorsize = XFS_MIN_SECTORSIZE;
/* Older kernels may not have physical/logical distinction */
if (!ft->psectorsize)
ft->psectorsize = ft->lsectorsize;
cfg->sectorsize = ft->psectorsize;
if (cfg->blocksize < cfg->sectorsize &&
cfg->blocksize >= ft->lsectorsize) {
fprintf(stderr,
_("specified blocksize %d is less than device physical sector size %d\n"
"switching to logical sector size %d\n"),
cfg->blocksize, ft->psectorsize,
ft->lsectorsize);
cfg->sectorsize = ft->lsectorsize;
}
cfg->sectorlog = libxfs_highbit32(cfg->sectorsize);
}
/* validate specified/probed sector size */
if (cfg->sectorsize < XFS_MIN_SECTORSIZE ||
cfg->sectorsize > XFS_MAX_SECTORSIZE) {
fprintf(stderr, _("illegal sector size %d\n"), cfg->sectorsize);
usage();
}
if (cfg->blocksize < cfg->sectorsize) {
fprintf(stderr,
_("block size %d cannot be smaller than sector size %d\n"),
cfg->blocksize, cfg->sectorsize);
usage();
}
if (cfg->sectorsize < ft->lsectorsize) {
fprintf(stderr, _("illegal sector size %d; hw sector is %d\n"),
cfg->sectorsize, ft->lsectorsize);
usage();
}
}
static void
validate_blocksize(
struct mkfs_params *cfg,
struct cli_params *cli,
struct mkfs_default_params *dft)
{
/*
* 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 (!cli->blocksize)
cfg->blocksize = dft->blocksize;
else
cfg->blocksize = cli->blocksize;
cfg->blocklog = libxfs_highbit32(cfg->blocksize);
/* validate block sizes are in range */
if (cfg->blocksize < XFS_MIN_BLOCKSIZE ||
cfg->blocksize > XFS_MAX_BLOCKSIZE) {
fprintf(stderr, _("illegal block size %d\n"), cfg->blocksize);
usage();
}
if (cli->sb_feat.crcs_enabled &&
cfg->blocksize < XFS_MIN_CRC_BLOCKSIZE) {
fprintf(stderr,
_("Minimum block size for CRC enabled filesystems is %d bytes.\n"),
XFS_MIN_CRC_BLOCKSIZE);
usage();
}
}
/*
* Grab log sector size and validate.
*
* XXX: should we probe sector size on external log device rather than using
* the data device sector size?
*/
static void
validate_log_sectorsize(
struct mkfs_params *cfg,
struct cli_params *cli,
struct mkfs_default_params *dft)
{
if (cli->loginternal && cli->lsectorsize &&
cli->lsectorsize != cfg->sectorsize) {
fprintf(stderr,
_("Can't change sector size on internal log!\n"));
usage();
}
if (cli->lsectorsize)
cfg->lsectorsize = cli->lsectorsize;
else if (cli->loginternal)
cfg->lsectorsize = cfg->sectorsize;
else
cfg->lsectorsize = dft->sectorsize;
cfg->lsectorlog = libxfs_highbit32(cfg->lsectorsize);
if (cfg->lsectorsize < XFS_MIN_SECTORSIZE ||
cfg->lsectorsize > XFS_MAX_SECTORSIZE ||
cfg->lsectorsize > cfg->blocksize) {
fprintf(stderr, _("illegal log sector size %d\n"),
cfg->lsectorsize);
usage();
}
if (cfg->lsectorsize > XFS_MIN_SECTORSIZE) {
if (cli->sb_feat.log_version < 2) {
/* user specified non-default log version */
fprintf(stderr,
_("Version 1 logs do not support sector size %d\n"),
cfg->lsectorsize);
usage();
}
}
/* if lsu or lsunit was specified, automatically use v2 logs */
if ((cli_opt_set(&lopts, L_SU) || cli_opt_set(&lopts, L_SUNIT)) &&
cli->sb_feat.log_version == 1) {
fprintf(stderr,
_("log stripe unit specified, using v2 logs\n"));
cli->sb_feat.log_version = 2;
}
}
/*
* Check that the incoming features make sense. The CLI structure was
* initialised with the default values before parsing, so we can just
* check it and copy it straight across to the cfg structure if it
* checks out.
*/
static void
validate_sb_features(
struct mkfs_params *cfg,
struct cli_params *cli)
{
/*
* Now we have blocks and sector sizes set up, check parameters that are
* no longer optional for CRC enabled filesystems. Catch them up front
* here before doing anything else.
*/
if (cli->sb_feat.crcs_enabled) {
/* minimum inode size is 512 bytes, rest checked later */
if (cli->inodesize &&
cli->inodesize < (1 << XFS_DINODE_DFL_CRC_LOG)) {
fprintf(stderr,
_("Minimum inode size for CRCs is %d bytes\n"),
1 << XFS_DINODE_DFL_CRC_LOG);
usage();
}
/* inodes always aligned */
if (!cli->sb_feat.inode_align) {
fprintf(stderr,
_("Inodes always aligned for CRC enabled filesystems\n"));
usage();
}
/* lazy sb counters always on */
if (!cli->sb_feat.lazy_sb_counters) {
fprintf(stderr,
_("Lazy superblock counters always enabled for CRC enabled filesystems\n"));
usage();
}
/* version 2 logs always on */
if (cli->sb_feat.log_version != 2) {
fprintf(stderr,
_("V2 logs always enabled for CRC enabled filesystems\n"));
usage();
}
/* attr2 always on */
if (cli->sb_feat.attr_version != 2) {
fprintf(stderr,
_("V2 attribute format always enabled on CRC enabled filesystems\n"));
usage();
}
/* 32 bit project quota always on */
/* attr2 always on */
if (!cli->sb_feat.projid32bit) {
fprintf(stderr,
_("32 bit Project IDs always enabled on CRC enabled filesystems\n"));
usage();
}
/* ftype always on */
if (!cli->sb_feat.dirftype) {
fprintf(stderr,
_("Directory ftype field always enabled on CRC enabled filesystems\n"));
usage();
}
} else {
/*
* The kernel doesn't currently support crc=0,finobt=1
* filesystems. If crcs are not enabled and the user has not
* explicitly turned finobt on, then silently turn it off to
* avoid an unnecessary warning.
* If the user explicitly tried to use crc=0,finobt=1,
* then issue an error.
* The same is also for sparse inodes.
*/
if (cli->sb_feat.finobt && cli_opt_set(&mopts, M_FINOBT)) {
fprintf(stderr,
_("finobt not supported without CRC support\n"));
usage();
}
cli->sb_feat.finobt = false;
if (cli->sb_feat.spinodes && cli_opt_set(&iopts, I_SPINODES)) {
fprintf(stderr,
_("sparse inodes not supported without CRC support\n"));
usage();
}
cli->sb_feat.spinodes = false;
if (cli->sb_feat.rmapbt) {
fprintf(stderr,
_("rmapbt not supported without CRC support\n"));
usage();
}
cli->sb_feat.rmapbt = false;
if (cli->sb_feat.reflink) {
fprintf(stderr,
_("reflink not supported without CRC support\n"));
usage();
}
cli->sb_feat.reflink = false;
}
if ((cli->fsx.fsx_xflags & FS_XFLAG_COWEXTSIZE) &&
!cli->sb_feat.reflink) {
fprintf(stderr,
_("cowextsize not supported without reflink support\n"));
usage();
}
if (cli->sb_feat.reflink && cli->xi->rtname) {
fprintf(stderr,
_("reflink not supported with realtime devices\n"));
usage();
cli->sb_feat.reflink = false;
}
if (cli->sb_feat.rmapbt && cli->xi->rtname) {
fprintf(stderr,
_("rmapbt not supported with realtime devices\n"));
usage();
cli->sb_feat.rmapbt = false;
}
/*
* Copy features across to config structure now.
*/
cfg->sb_feat = cli->sb_feat;
if (!platform_uuid_is_null(&cli->uuid))
platform_uuid_copy(&cfg->uuid, &cli->uuid);
}
static void
validate_dirblocksize(
struct mkfs_params *cfg,
struct cli_params *cli)
{
if (cli->dirblocksize)
cfg->dirblocksize = getnum(cli->dirblocksize, &nopts, N_SIZE);
if (cfg->dirblocksize) {
if (cfg->dirblocksize < cfg->blocksize ||
cfg->dirblocksize > XFS_MAX_BLOCKSIZE) {
fprintf(stderr, _("illegal directory block size %d\n"),
cfg->dirblocksize);
usage();
}
cfg->dirblocklog = libxfs_highbit32(cfg->dirblocksize);
return;
}
/* use default size based on current block size */
if (cfg->blocksize < (1 << XFS_MIN_REC_DIRSIZE))
cfg->dirblocklog = XFS_MIN_REC_DIRSIZE;
else
cfg->dirblocklog = cfg->blocklog;
cfg->dirblocksize = 1 << cfg->dirblocklog;
}
static void
validate_inodesize(
struct mkfs_params *cfg,
struct cli_params *cli)
{
if (cli->inopblock)
cfg->inodelog = cfg->blocklog - libxfs_highbit32(cli->inopblock);
else if (cli->inodesize)
cfg->inodelog = libxfs_highbit32(cli->inodesize);
else if (cfg->sb_feat.crcs_enabled)
cfg->inodelog = XFS_DINODE_DFL_CRC_LOG;
else
cfg->inodelog = XFS_DINODE_DFL_LOG;
cfg->inodesize = 1 << cfg->inodelog;
cfg->inopblock = cfg->blocksize / cfg->inodesize;
/* input parsing has already validated non-crc inode size range */
if (cfg->sb_feat.crcs_enabled &&
cfg->inodelog < XFS_DINODE_DFL_CRC_LOG) {
fprintf(stderr,
_("Minimum inode size for CRCs is %d bytes\n"),
1 << XFS_DINODE_DFL_CRC_LOG);
usage();
}
if (cfg->inodesize > cfg->blocksize / XFS_MIN_INODE_PERBLOCK ||
cfg->inopblock < XFS_MIN_INODE_PERBLOCK ||
cfg->inodesize < XFS_DINODE_MIN_SIZE ||
cfg->inodesize > XFS_DINODE_MAX_SIZE) {
int maxsz;
fprintf(stderr, _("illegal inode size %d\n"), cfg->inodesize);
maxsz = MIN(cfg->blocksize / XFS_MIN_INODE_PERBLOCK,
XFS_DINODE_MAX_SIZE);
if (XFS_DINODE_MIN_SIZE == maxsz)
fprintf(stderr,
_("allowable inode size with %d byte blocks is %d\n"),
cfg->blocksize, XFS_DINODE_MIN_SIZE);
else
fprintf(stderr,
_("allowable inode size with %d byte blocks is between %d and %d\n"),
cfg->blocksize, XFS_DINODE_MIN_SIZE, maxsz);
exit(1);
}
}
static xfs_rfsblock_t
calc_dev_size(
char *size,
struct mkfs_params *cfg,
struct opt_params *opts,
int sizeopt,
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 >> cfg->blocklog);
if (dbytes % cfg->blocksize) {
fprintf(stderr,
_("warning: %s length %lld not a multiple of %d, truncated to %lld\n"),
type, (long long)dbytes, cfg->blocksize,
(long long)(dblocks << cfg->blocklog));
}
return dblocks;
}
static void
validate_rtextsize(
struct mkfs_params *cfg,
struct cli_params *cli,
struct fs_topology *ft)
{
uint64_t rtextbytes;
/*
* If specified, check rt extent size against its constraints.
*/
if (cli->rtextsize) {
rtextbytes = getnum(cli->rtextsize, &ropts, R_EXTSIZE);
if (rtextbytes % cfg->blocksize) {
fprintf(stderr,
_("illegal rt extent size %lld, not a multiple of %d\n"),
(long long)rtextbytes, cfg->blocksize);
usage();
}
cfg->rtextblocks = (xfs_extlen_t)(rtextbytes >> cfg->blocklog);
} else {
/*
* If realtime extsize has not been specified by the user,
* and the underlying volume is striped, then set rtextblocks
* to the stripe width.
*/
uint64_t rswidth;
if (!cfg->sb_feat.nortalign && !cli->xi->risfile &&
!(!cli->rtsize && cli->xi->disfile))
rswidth = ft->rtswidth;
else
rswidth = 0;
/* check that rswidth is a multiple of fs blocksize */
if (!cfg->sb_feat.nortalign && rswidth &&
!(BBTOB(rswidth) % cfg->blocksize)) {
rswidth = DTOBT(rswidth, cfg->blocklog);
rtextbytes = rswidth << cfg->blocklog;
if (rtextbytes > XFS_MIN_RTEXTSIZE &&
rtextbytes <= XFS_MAX_RTEXTSIZE) {
cfg->rtextblocks = rswidth;
}
}
if (!cfg->rtextblocks) {
cfg->rtextblocks = (cfg->blocksize < XFS_MIN_RTEXTSIZE)
? XFS_MIN_RTEXTSIZE >> cfg->blocklog
: 1;
}
}
ASSERT(cfg->rtextblocks);
}
/*
* Validate the configured stripe geometry, or is none is specified, pull
* the configuration from the underlying device.
*
* CLI parameters come in as different units, go out as filesystem blocks.
*/
static void
calc_stripe_factors(
struct mkfs_params *cfg,
struct cli_params *cli,
struct fs_topology *ft)
{
long long int big_dswidth;
int dsunit = 0;
int dswidth = 0;
int lsunit = 0;
int dsu = 0;
int dsw = 0;
int lsu = 0;
bool use_dev = false;
if (cli_opt_set(&dopts, D_SUNIT))
dsunit = cli->dsunit;
if (cli_opt_set(&dopts, D_SWIDTH))
dswidth = cli->dswidth;
if (cli_opt_set(&dopts, D_SU))
dsu = getnum(cli->dsu, &dopts, D_SU);
if (cli_opt_set(&dopts, D_SW))
dsw = cli->dsw;
/* data sunit/swidth options */
if (cli_opt_set(&dopts, D_SUNIT) != cli_opt_set(&dopts, D_SWIDTH)) {
fprintf(stderr,
_("both data sunit and data swidth options must be specified\n"));
usage();
}
/* convert dsu/dsw to dsunit/dswidth and use them from now on */
if (dsu || dsw) {
if (cli_opt_set(&dopts, D_SU) != cli_opt_set(&dopts, D_SW)) {
fprintf(stderr,
_("both data su and data sw options must be specified\n"));
usage();
}
if (dsu % cfg->sectorsize) {
fprintf(stderr,
_("data su must be a multiple of the sector size (%d)\n"), cfg->sectorsize);
usage();
}
dsunit = (int)BTOBBT(dsu);
big_dswidth = (long long int)dsunit * dsw;
if (big_dswidth > INT_MAX) {
fprintf(stderr,
_("data stripe width (%lld) is too large of a multiple of the data stripe unit (%d)\n"),
big_dswidth, dsunit);
usage();
}
dswidth = big_dswidth;
}
if ((dsunit && !dswidth) || (!dsunit && dswidth) ||
(dsunit && (dswidth % dsunit != 0))) {
fprintf(stderr,
_("data stripe width (%d) must be a multiple of the data stripe unit (%d)\n"),
dswidth, dsunit);
usage();
}
/* If sunit & swidth were manually specified as 0, same as noalign */
if ((cli_opt_set(&dopts, D_SUNIT) || cli_opt_set(&dopts, D_SU)) &&
!dsunit && !dswidth)
cfg->sb_feat.nodalign = true;
/* if we are not using alignment, don't apply device defaults */
if (cfg->sb_feat.nodalign) {
cfg->dsunit = 0;
cfg->dswidth = 0;
goto check_lsunit;
}
/* if no stripe config set, use the device default */
if (!dsunit) {
dsunit = ft->dsunit;
dswidth = ft->dswidth;
use_dev = true;
} else {
/* check and warn is alignment is sub-optimal */
if (ft->dsunit && ft->dsunit != dsunit) {
fprintf(stderr,
_("%s: Specified data stripe unit %d is not the same as the volume stripe unit %d\n"),
progname, dsunit, ft->dsunit);
}
if (ft->dswidth && ft->dswidth != dswidth) {
fprintf(stderr,
_("%s: Specified data stripe width %d is not the same as the volume stripe width %d\n"),
progname, dswidth, ft->dswidth);
}
}
/*
* now we have our stripe config, check it's a multiple of block
* size.
*/
if ((BBTOB(dsunit) % cfg->blocksize) ||
(BBTOB(dswidth) % cfg->blocksize)) {
/*
* If we are using device defaults, just clear them and we're
* good to go. Otherwise bail out with an error.
*/
if (!use_dev) {
fprintf(stderr,
_("%s: Stripe unit(%d) or stripe width(%d) is not a multiple of the block size(%d)\n"),
progname, BBTOB(dsunit), BBTOB(dswidth),
cfg->blocksize);
exit(1);
}
dsunit = 0;
dswidth = 0;
cfg->sb_feat.nodalign = true;
}
/* convert from 512 byte blocks to fs blocksize */
cfg->dsunit = DTOBT(dsunit, cfg->blocklog);
cfg->dswidth = DTOBT(dswidth, cfg->blocklog);
check_lsunit:
/* log sunit options */
if (cli_opt_set(&lopts, L_SUNIT))
lsunit = cli->lsunit;
else if (cli_opt_set(&lopts, L_SU))
lsu = getnum(cli->lsu, &lopts, L_SU);
else if (cfg->lsectorsize > XLOG_HEADER_SIZE)
lsu = cfg->blocksize; /* lsunit matches filesystem block size */
if (lsu) {
/* verify if lsu is a multiple block size */
if (lsu % cfg->blocksize != 0) {
fprintf(stderr,
_("log stripe unit (%d) must be a multiple of the block size (%d)\n"),
lsu, cfg->blocksize);
usage();
}
lsunit = (int)BTOBBT(lsu);
}
if (BBTOB(lsunit) % cfg->blocksize != 0) {
fprintf(stderr,
_("log stripe unit (%d) must be a multiple of the block size (%d)\n"),
BBTOB(lsunit), cfg->blocksize);
usage();
}
/*
* check that log sunit is modulo fsblksize or default it to dsunit.
*/
if (lsunit) {
/* convert from 512 byte blocks to fs blocks */
cfg->lsunit = DTOBT(lsunit, cfg->blocklog);
} else if (cfg->sb_feat.log_version == 2 &&
cfg->loginternal && cfg->dsunit) {
/* lsunit and dsunit now in fs blocks */
cfg->lsunit = cfg->dsunit;
}
if (cfg->sb_feat.log_version == 2 &&
cfg->lsunit * cfg->blocksize > 256 * 1024) {
/* Warn only if specified on commandline */
if (cli->lsu || cli->lsunit != -1) {
fprintf(stderr,
_("log stripe unit (%d bytes) is too large (maximum is 256KiB)\n"
"log stripe unit adjusted to 32KiB\n"),
(cfg->lsunit * cfg->blocksize));
}
/* XXX: 64k block size? */
cfg->lsunit = (32 * 1024) / cfg->blocksize;
}
}
static void
open_devices(
struct mkfs_params *cfg,
struct libxfs_xinit *xi,
bool discard)
{
uint64_t sector_mask;
/*
* Initialize. This will open the log and rt devices as well.
*/
xi->setblksize = cfg->sectorsize;
if (!libxfs_init(xi))
usage();
if (!xi->ddev) {
fprintf(stderr, _("no device name given in argument list\n"));
usage();
}
/*
* Ok, Linux only has a 1024-byte resolution on device _size_,
* and the sizes below are in basic 512-byte blocks,
* so if we have (size % 2), on any partition, we can't get
* to the last 512 bytes. The same issue exists for larger
* sector sizes - we cannot write past the last sector.
*
* So, we reduce the size (in basic blocks) to a perfect
* multiple of the sector size, or 1024, whichever is larger.
*/
sector_mask = (uint64_t)-1 << (MAX(cfg->sectorlog, 10) - BBSHIFT);
xi->dsize &= sector_mask;
xi->rtsize &= sector_mask;
xi->logBBsize &= (uint64_t)-1 << (MAX(cfg->lsectorlog, 10) - BBSHIFT);
if (!discard)
return;
if (!xi->disfile)
discard_blocks(xi->ddev, xi->dsize);
if (xi->rtdev && !xi->risfile)
discard_blocks(xi->rtdev, xi->rtsize);
if (xi->logdev && xi->logdev != xi->ddev && !xi->lisfile)
discard_blocks(xi->logdev, xi->logBBsize);
}
static void
validate_datadev(
struct mkfs_params *cfg,
struct cli_params *cli)
{
struct libxfs_xinit *xi = cli->xi;
if (!xi->dsize) {
/*
* if the device is a file, we can't validate the size here.
* Instead, the file will be truncated to the correct length
* later on. if it's not a file, we've got a dud device.
*/
if (!xi->disfile) {
fprintf(stderr, _("can't get size of data subvolume\n"));
usage();
}
ASSERT(cfg->dblocks);
} else if (cfg->dblocks) {
/* check the size fits into the underlying device */
if (cfg->dblocks > DTOBT(xi->dsize, cfg->blocklog)) {
fprintf(stderr,
_("size %s specified for data subvolume is too large, maximum is %lld blocks\n"),
cli->dsize,
(long long)DTOBT(xi->dsize, cfg->blocklog));
usage();
}
} else {
/* no user size, so use the full block device */
cfg->dblocks = DTOBT(xi->dsize, cfg->blocklog);
}
if (cfg->dblocks < XFS_MIN_DATA_BLOCKS) {
fprintf(stderr,
_("size %lld of data subvolume is too small, minimum %d blocks\n"),
(long long)cfg->dblocks, XFS_MIN_DATA_BLOCKS);
usage();
}
if (xi->dbsize > cfg->sectorsize) {
fprintf(stderr, _(
"Warning: the data subvolume sector size %u is less than the sector size \n\
reported by the device (%u).\n"),
cfg->sectorsize, xi->dbsize);
}
}
/*
* This is more complex than it needs to be because we still support volume
* based external logs. They are only discovered *after* the devices have been
* opened, hence the crazy "is this really an internal log" checks here.
*/
static void
validate_logdev(
struct mkfs_params *cfg,
struct cli_params *cli,
char **devname)
{
struct libxfs_xinit *xi = cli->xi;
*devname = NULL;
/* check for volume log first */
if (cli->loginternal && xi->volname && xi->logdev) {
*devname = _("volume log");
cfg->loginternal = false;
} else
cfg->loginternal = cli->loginternal;
/* now run device checks */
if (cfg->loginternal) {
if (xi->logdev) {
fprintf(stderr,
_("can't have both external and internal logs\n"));
usage();
}
/*
* if no sector size has been specified on the command line,
* use what has been configured and validated for the data
* device.
*/
if (!cli->lsectorsize) {
cfg->lsectorsize = cfg->sectorsize;
cfg->lsectorlog = cfg->sectorlog;
}
if (cfg->sectorsize != cfg->lsectorsize) {
fprintf(stderr,
_("data and log sector sizes must be equal for internal logs\n"));
usage();
}
if (cli->logsize && cfg->logblocks >= cfg->dblocks) {
fprintf(stderr,
_("log size %lld too large for internal log\n"),
(long long)cfg->logblocks);
usage();
}
*devname = _("internal log");
return;
}
/* External/log subvolume checks */
if (xi->logname)
*devname = xi->logname;
if (!*devname || !xi->logdev) {
fprintf(stderr, _("no log subvolume or external log.\n"));
usage();
}
if (!cfg->logblocks) {
if (xi->logBBsize == 0) {
fprintf(stderr,
_("unable to get size of the log subvolume.\n"));
usage();
}
cfg->logblocks = DTOBT(xi->logBBsize, cfg->blocklog);
} else if (cfg->logblocks > DTOBT(xi->logBBsize, cfg->blocklog)) {
fprintf(stderr,
_("size %s specified for log subvolume is too large, maximum is %lld blocks\n"),
cli->logsize,
(long long)DTOBT(xi->logBBsize, cfg->blocklog));
usage();
}
if (xi->lbsize > cfg->lsectorsize) {
fprintf(stderr, _(
"Warning: the log subvolume sector size %u is less than the sector size\n\
reported by the device (%u).\n"),
cfg->lsectorsize, xi->lbsize);
}
}
static void
validate_rtdev(
struct mkfs_params *cfg,
struct cli_params *cli,
char **devname)
{
struct libxfs_xinit *xi = cli->xi;
*devname = NULL;
if (!xi->rtdev) {
if (cli->rtsize) {
fprintf(stderr,
_("size specified for non-existent rt subvolume\n"));
usage();
}
*devname = _("none");
cfg->rtblocks = 0;
cfg->rtextents = 0;
cfg->rtbmblocks = 0;
return;
}
if (!xi->rtsize) {
fprintf(stderr, _("Invalid zero length rt subvolume found\n"));
usage();
}
/* volume rtdev */
if (xi->volname)
*devname = _("volume rt");
else
*devname = xi->rtname;
if (cli->rtsize) {
if (cfg->rtblocks > DTOBT(xi->rtsize, cfg->blocklog)) {
fprintf(stderr,
_("size %s specified for rt subvolume is too large, maxi->um is %lld blocks\n"),
cli->rtsize,
(long long)DTOBT(xi->rtsize, cfg->blocklog));
usage();
}
if (xi->rtbsize > cfg->sectorsize) {
fprintf(stderr, _(
"Warning: the realtime subvolume sector size %u is less than the sector size\n\
reported by the device (%u).\n"),
cfg->sectorsize, xi->rtbsize);
}
} else {
/* grab volume size */
cfg->rtblocks = DTOBT(xi->rtsize, cfg->blocklog);
}
cfg->rtextents = cfg->rtblocks / cfg->rtextblocks;
cfg->rtbmblocks = (xfs_extlen_t)howmany(cfg->rtextents,
NBBY * cfg->blocksize);
}
static void
calculate_initial_ag_geometry(
struct mkfs_params *cfg,
struct cli_params *cli)
{
if (cli->agsize) { /* User-specified AG size */
cfg->agsize = getnum(cli->agsize, &dopts, D_AGSIZE);
/*
* Check specified agsize is a multiple of blocksize.
*/
if (cfg->agsize % cfg->blocksize) {
fprintf(stderr,
_("agsize (%s) not a multiple of fs blk size (%d)\n"),
cli->agsize, cfg->blocksize);
usage();
}
cfg->agsize /= cfg->blocksize;
cfg->agcount = cfg->dblocks / cfg->agsize +
(cfg->dblocks % cfg->agsize != 0);
} else if (cli->agcount) { /* User-specified AG count */
cfg->agcount = cli->agcount;
cfg->agsize = cfg->dblocks / cfg->agcount +
(cfg->dblocks % cfg->agcount != 0);
} else {
calc_default_ag_geometry(cfg->blocklog, cfg->dblocks,
cfg->dsunit, &cfg->agsize,
&cfg->agcount);
}
}
/*
* Align the AG size to stripe geometry. If this fails and we are using
* discovered stripe geometry, tell the caller to clear the stripe geometry.
* Otherwise, set the aligned geometry (valid or invalid!) so that the
* validation call will fail and exit.
*/
static void
align_ag_geometry(
struct mkfs_params *cfg)
{
uint64_t tmp_agsize;
int dsunit = cfg->dsunit;
if (!dsunit)
goto validate;
/*
* agsize is not a multiple of dsunit
*/
if ((cfg->agsize % dsunit) != 0) {
/*
* Round up to stripe unit boundary. Also make sure
* that agsize is still larger than
* XFS_AG_MIN_BLOCKS(blocklog)
*/
tmp_agsize = ((cfg->agsize + dsunit - 1) / dsunit) * dsunit;
/*
* Round down to stripe unit boundary if rounding up
* created an AG size that is larger than the AG max.
*/
if (tmp_agsize > XFS_AG_MAX_BLOCKS(cfg->blocklog))
tmp_agsize = (cfg->agsize / dsunit) * dsunit;
if (tmp_agsize < XFS_AG_MIN_BLOCKS(cfg->blocklog) &&
tmp_agsize > XFS_AG_MAX_BLOCKS(cfg->blocklog)) {
/*
* If the AG size is invalid and we are using device
* probed stripe alignment, just clear the alignment
* and continue on.
*/
if (!cli_opt_set(&dopts, D_SUNIT) &&
!cli_opt_set(&dopts, D_SU)) {
cfg->dsunit = 0;
cfg->dswidth = 0;
goto validate;
}
/*
* set the agsize to the invalid value so the following
* validation of the ag will fail and print a nice error
* and exit.
*/
cfg->agsize = tmp_agsize;
goto validate;
}
/* update geometry to be stripe unit aligned */
cfg->agsize = tmp_agsize;
if (!cli_opt_set(&dopts, D_AGCOUNT))
cfg->agcount = cfg->dblocks / cfg->agsize +
(cfg->dblocks % cfg->agsize != 0);
if (cli_opt_set(&dopts, D_AGSIZE))
fprintf(stderr,
_("agsize rounded to %lld, sunit = %d\n"),
(long long)cfg->agsize, dsunit);
}
if ((cfg->agsize % cfg->dswidth) == 0 &&
cfg->dswidth != cfg->dsunit &&
cfg->agcount > 1) {
if (cli_opt_set(&dopts, D_AGCOUNT) ||
cli_opt_set(&dopts, D_AGSIZE)) {
fprintf(stderr, _(
"Warning: AG size is a multiple of stripe width. This can cause performance\n\
problems by aligning all AGs on the same disk. To avoid this, run mkfs with\n\
an AG size that is one stripe unit smaller or larger, for example %llu.\n"),
(unsigned long long)cfg->agsize - dsunit);
goto validate;
}
/*
* This is a non-optimal configuration because all AGs start on
* the same disk in the stripe. Changing the AG size by one
* sunit will guarantee that this does not happen.
*/
tmp_agsize = cfg->agsize - dsunit;
if (tmp_agsize < XFS_AG_MIN_BLOCKS(cfg->blocklog)) {
tmp_agsize = cfg->agsize + dsunit;
if (cfg->dblocks < cfg->agsize) {
/* oh well, nothing to do */
tmp_agsize = cfg->agsize;
}
}
cfg->agsize = tmp_agsize;
cfg->