+// SPDX-License-Identifier: GPL-2.0
/*
* 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>
-#ifdef ENABLE_BLKID
-# include <blkid/blkid.h>
-#endif /* ENABLE_BLKID */
#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)))
/*
- * Prototypes for internal functions.
+ * amount (in bytes) we zero at the beginning and end of the device to
+ * remove traces of other filesystems, raid superblocks, etc.
*/
-static void conflict(char opt, 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 void unknown(char opt, char *s);
-static int ispow2(unsigned int i);
+#define WHACK_SIZE (128 * 1024)
/*
- * The configured block and sector sizes are defined as global variables so
- * that they don't need to be passed to functions that require them.
+ * 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;
-#define MAX_SUBOPTS 16
+/*
+ * 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)
bool str_seen;
bool convert;
bool is_power_2;
- int conflicts[MAX_CONFLICTS];
+ 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 = {
-#define B_LOG 0
- "log",
-#define B_SIZE 1
- "size",
- NULL
+ [B_SIZE] = "size",
},
.subopt_params = {
- { .index = B_LOG,
- .conflicts = { B_SIZE,
- LAST_CONFLICT },
- .minval = XFS_MIN_BLOCKSIZE_LOG,
- .maxval = XFS_MAX_BLOCKSIZE_LOG,
- .defaultval = SUBOPT_NEEDS_VAL,
- },
{ .index = B_SIZE,
.convert = true,
.is_power_2 = true,
- .conflicts = { B_LOG,
- LAST_CONFLICT },
+ .conflicts = { { NULL, LAST_CONFLICT } },
.minval = XFS_MIN_BLOCKSIZE,
.maxval = XFS_MAX_BLOCKSIZE,
.defaultval = SUBOPT_NEEDS_VAL,
struct opt_params dopts = {
.name = 'd',
.subopts = {
-#define D_AGCOUNT 0
- "agcount",
-#define D_FILE 1
- "file",
-#define D_NAME 2
- "name",
-#define D_SIZE 3
- "size",
-#define D_SUNIT 4
- "sunit",
-#define D_SWIDTH 5
- "swidth",
-#define D_AGSIZE 6
- "agsize",
-#define D_SU 7
- "su",
-#define D_SW 8
- "sw",
-#define D_SECTLOG 9
- "sectlog",
-#define D_SECTSIZE 10
- "sectsize",
-#define D_NOALIGN 11
- "noalign",
-#define D_RTINHERIT 12
- "rtinherit",
-#define D_PROJINHERIT 13
- "projinherit",
-#define D_EXTSZINHERIT 14
- "extszinherit",
- NULL
+ [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 = { D_AGSIZE,
- LAST_CONFLICT },
+ .conflicts = { { &dopts, D_AGSIZE },
+ { NULL, LAST_CONFLICT } },
.minval = 1,
.maxval = XFS_MAX_AGNUMBER,
.defaultval = SUBOPT_NEEDS_VAL,
},
{ .index = D_FILE,
- .conflicts = { LAST_CONFLICT },
+ .conflicts = { { NULL, LAST_CONFLICT } },
.minval = 0,
.maxval = 1,
.defaultval = 1,
},
{ .index = D_NAME,
- .conflicts = { LAST_CONFLICT },
+ .conflicts = { { NULL, LAST_CONFLICT } },
.defaultval = SUBOPT_NEEDS_VAL,
},
{ .index = D_SIZE,
- .conflicts = { LAST_CONFLICT },
+ .conflicts = { { NULL, LAST_CONFLICT } },
.convert = true,
.minval = XFS_AG_MIN_BYTES,
.maxval = LLONG_MAX,
.defaultval = SUBOPT_NEEDS_VAL,
},
{ .index = D_SUNIT,
- .conflicts = { D_NOALIGN,
- D_SU,
- D_SW,
- LAST_CONFLICT },
+ .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 = { D_NOALIGN,
- D_SU,
- D_SW,
- LAST_CONFLICT },
+ .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 = { D_AGCOUNT,
- LAST_CONFLICT },
+ .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 = { D_NOALIGN,
- D_SUNIT,
- D_SWIDTH,
- LAST_CONFLICT },
+ .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 = { D_NOALIGN,
- D_SUNIT,
- D_SWIDTH,
- LAST_CONFLICT },
+ .conflicts = { { &dopts, D_NOALIGN },
+ { &dopts, D_SUNIT },
+ { &dopts, D_SWIDTH },
+ { NULL, LAST_CONFLICT } },
.minval = 0,
.maxval = UINT_MAX,
.defaultval = SUBOPT_NEEDS_VAL,
},
- { .index = D_SECTLOG,
- .conflicts = { D_SECTSIZE,
- LAST_CONFLICT },
- .minval = XFS_MIN_SECTORSIZE_LOG,
- .maxval = XFS_MAX_SECTORSIZE_LOG,
- .defaultval = SUBOPT_NEEDS_VAL,
- },
{ .index = D_SECTSIZE,
- .conflicts = { D_SECTLOG,
- LAST_CONFLICT },
+ .conflicts = { { &sopts, S_SIZE },
+ { &sopts, S_SECTSIZE },
+ { NULL, LAST_CONFLICT } },
.convert = true,
.is_power_2 = true,
.minval = XFS_MIN_SECTORSIZE,
.defaultval = SUBOPT_NEEDS_VAL,
},
{ .index = D_NOALIGN,
- .conflicts = { D_SU,
- D_SW,
- D_SUNIT,
- D_SWIDTH,
- LAST_CONFLICT },
+ .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 = { LAST_CONFLICT },
+ .conflicts = { { NULL, LAST_CONFLICT } },
.minval = 1,
.maxval = 1,
.defaultval = 1,
},
{ .index = D_PROJINHERIT,
- .conflicts = { LAST_CONFLICT },
+ .conflicts = { { NULL, LAST_CONFLICT } },
.minval = 0,
.maxval = UINT_MAX,
.defaultval = SUBOPT_NEEDS_VAL,
},
{ .index = D_EXTSZINHERIT,
- .conflicts = { LAST_CONFLICT },
+ .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 = {
-#define I_ALIGN 0
- "align",
-#define I_LOG 1
- "log",
-#define I_MAXPCT 2
- "maxpct",
-#define I_PERBLOCK 3
- "perblock",
-#define I_SIZE 4
- "size",
-#define I_ATTR 5
- "attr",
-#define I_PROJID32BIT 6
- "projid32bit",
-#define I_SPINODES 7
- "sparse",
- NULL
+ [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 = { LAST_CONFLICT },
+ .conflicts = { { NULL, LAST_CONFLICT } },
.minval = 0,
.maxval = 1,
.defaultval = 1,
},
- { .index = I_LOG,
- .conflicts = { I_PERBLOCK,
- I_SIZE,
- LAST_CONFLICT },
- .minval = XFS_DINODE_MIN_LOG,
- .maxval = XFS_DINODE_MAX_LOG,
- .defaultval = SUBOPT_NEEDS_VAL,
- },
{ .index = I_MAXPCT,
- .conflicts = { LAST_CONFLICT },
+ .conflicts = { { NULL, LAST_CONFLICT } },
.minval = 0,
.maxval = 100,
.defaultval = SUBOPT_NEEDS_VAL,
},
{ .index = I_PERBLOCK,
- .conflicts = { I_LOG,
- I_SIZE,
- LAST_CONFLICT },
+ .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 = { I_PERBLOCK,
- I_LOG,
- LAST_CONFLICT },
+ .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 = { LAST_CONFLICT },
+ .conflicts = { { NULL, LAST_CONFLICT } },
.minval = 0,
.maxval = 2,
.defaultval = SUBOPT_NEEDS_VAL,
},
{ .index = I_PROJID32BIT,
- .conflicts = { LAST_CONFLICT },
+ .conflicts = { { NULL, LAST_CONFLICT } },
.minval = 0,
.maxval = 1,
.defaultval = 1,
},
{ .index = I_SPINODES,
- .conflicts = { LAST_CONFLICT },
+ .conflicts = { { NULL, LAST_CONFLICT } },
.minval = 0,
.maxval = 1,
.defaultval = 1,
struct opt_params lopts = {
.name = 'l',
.subopts = {
-#define L_AGNUM 0
- "agnum",
-#define L_INTERNAL 1
- "internal",
-#define L_SIZE 2
- "size",
-#define L_VERSION 3
- "version",
-#define L_SUNIT 4
- "sunit",
-#define L_SU 5
- "su",
-#define L_DEV 6
- "logdev",
-#define L_SECTLOG 7
- "sectlog",
-#define L_SECTSIZE 8
- "sectsize",
-#define L_FILE 9
- "file",
-#define L_NAME 10
- "name",
-#define L_LAZYSBCNTR 11
- "lazy-count",
- NULL
+ [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 = { L_DEV,
- LAST_CONFLICT },
+ .conflicts = { { &lopts, L_DEV },
+ { NULL, LAST_CONFLICT } },
.minval = 0,
.maxval = UINT_MAX,
.defaultval = SUBOPT_NEEDS_VAL,
},
{ .index = L_INTERNAL,
- .conflicts = { L_FILE,
- L_DEV,
- LAST_CONFLICT },
+ .conflicts = { { &lopts, L_FILE },
+ { &lopts, L_DEV },
+ { &lopts, L_SECTSIZE },
+ { NULL, LAST_CONFLICT } },
.minval = 0,
.maxval = 1,
.defaultval = 1,
},
{ .index = L_SIZE,
- .conflicts = { LAST_CONFLICT },
+ .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 = { LAST_CONFLICT },
+ .conflicts = { { NULL, LAST_CONFLICT } },
.minval = 1,
.maxval = 2,
.defaultval = SUBOPT_NEEDS_VAL,
},
{ .index = L_SUNIT,
- .conflicts = { L_SU,
- LAST_CONFLICT },
+ .conflicts = { { &lopts, L_SU },
+ { NULL, LAST_CONFLICT } },
.minval = 1,
.maxval = BTOBB(XLOG_MAX_RECORD_BSIZE),
.defaultval = SUBOPT_NEEDS_VAL,
},
{ .index = L_SU,
- .conflicts = { L_SUNIT,
- LAST_CONFLICT },
+ .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 = { L_AGNUM,
- L_INTERNAL,
- LAST_CONFLICT },
- .defaultval = SUBOPT_NEEDS_VAL,
- },
- { .index = L_SECTLOG,
- .conflicts = { L_SECTSIZE,
- LAST_CONFLICT },
- .minval = XFS_MIN_SECTORSIZE_LOG,
- .maxval = XFS_MAX_SECTORSIZE_LOG,
+ .conflicts = { { &lopts, L_AGNUM },
+ { &lopts, L_NAME },
+ { &lopts, L_INTERNAL },
+ { NULL, LAST_CONFLICT } },
.defaultval = SUBOPT_NEEDS_VAL,
},
{ .index = L_SECTSIZE,
- .conflicts = { L_SECTLOG,
- LAST_CONFLICT },
+ .conflicts = { { &lopts, L_INTERNAL },
+ { NULL, LAST_CONFLICT } },
.convert = true,
.is_power_2 = true,
.minval = XFS_MIN_SECTORSIZE,
.defaultval = SUBOPT_NEEDS_VAL,
},
{ .index = L_FILE,
- .conflicts = { L_INTERNAL,
- LAST_CONFLICT },
+ .conflicts = { { &lopts, L_INTERNAL },
+ { NULL, LAST_CONFLICT } },
.minval = 0,
.maxval = 1,
.defaultval = 1,
},
{ .index = L_NAME,
- .conflicts = { L_AGNUM,
- L_INTERNAL,
- LAST_CONFLICT },
+ .conflicts = { { &lopts, L_AGNUM },
+ { &lopts, L_DEV },
+ { &lopts, L_INTERNAL },
+ { NULL, LAST_CONFLICT } },
.defaultval = SUBOPT_NEEDS_VAL,
},
{ .index = L_LAZYSBCNTR,
- .conflicts = { LAST_CONFLICT },
+ .conflicts = { { NULL, LAST_CONFLICT } },
.minval = 0,
.maxval = 1,
.defaultval = 1,
struct opt_params nopts = {
.name = 'n',
.subopts = {
-#define N_LOG 0
- "log",
-#define N_SIZE 1
- "size",
-#define N_VERSION 2
- "version",
-#define N_FTYPE 3
- "ftype",
- NULL,
+ [N_SIZE] = "size",
+ [N_VERSION] = "version",
+ [N_FTYPE] = "ftype",
},
.subopt_params = {
- { .index = N_LOG,
- .conflicts = { N_SIZE,
- LAST_CONFLICT },
- .minval = XFS_MIN_REC_DIRSIZE,
- .maxval = XFS_MAX_BLOCKSIZE_LOG,
- .defaultval = SUBOPT_NEEDS_VAL,
- },
{ .index = N_SIZE,
- .conflicts = { N_LOG,
- LAST_CONFLICT },
+ .conflicts = { { NULL, LAST_CONFLICT } },
.convert = true,
.is_power_2 = true,
.minval = 1 << XFS_MIN_REC_DIRSIZE,
.defaultval = SUBOPT_NEEDS_VAL,
},
{ .index = N_VERSION,
- .conflicts = { LAST_CONFLICT },
+ .conflicts = { { NULL, LAST_CONFLICT } },
.minval = 2,
.maxval = 2,
.defaultval = SUBOPT_NEEDS_VAL,
},
{ .index = N_FTYPE,
- .conflicts = { LAST_CONFLICT },
+ .conflicts = { { NULL, LAST_CONFLICT } },
.minval = 0,
.maxval = 1,
.defaultval = 1,
struct opt_params ropts = {
.name = 'r',
.subopts = {
-#define R_EXTSIZE 0
- "extsize",
-#define R_SIZE 1
- "size",
-#define R_DEV 2
- "rtdev",
-#define R_FILE 3
- "file",
-#define R_NAME 4
- "name",
-#define R_NOALIGN 5
- "noalign",
- NULL
+ [R_EXTSIZE] = "extsize",
+ [R_SIZE] = "size",
+ [R_DEV] = "rtdev",
+ [R_FILE] = "file",
+ [R_NAME] = "name",
+ [R_NOALIGN] = "noalign",
},
.subopt_params = {
{ .index = R_EXTSIZE,
- .conflicts = { LAST_CONFLICT },
+ .conflicts = { { NULL, LAST_CONFLICT } },
.convert = true,
.minval = XFS_MIN_RTEXTSIZE,
.maxval = XFS_MAX_RTEXTSIZE,
.defaultval = SUBOPT_NEEDS_VAL,
},
{ .index = R_SIZE,
- .conflicts = { LAST_CONFLICT },
+ .conflicts = { { NULL, LAST_CONFLICT } },
.convert = true,
.minval = 0,
.maxval = LLONG_MAX,
.defaultval = SUBOPT_NEEDS_VAL,
},
{ .index = R_DEV,
- .conflicts = { LAST_CONFLICT },
+ .conflicts = { { &ropts, R_NAME },
+ { NULL, LAST_CONFLICT } },
.defaultval = SUBOPT_NEEDS_VAL,
},
{ .index = R_FILE,
.minval = 0,
.maxval = 1,
.defaultval = 1,
- .conflicts = { LAST_CONFLICT },
+ .conflicts = { { NULL, LAST_CONFLICT } },
},
{ .index = R_NAME,
- .conflicts = { LAST_CONFLICT },
+ .conflicts = { { &ropts, R_DEV },
+ { NULL, LAST_CONFLICT } },
.defaultval = SUBOPT_NEEDS_VAL,
},
{ .index = R_NOALIGN,
.minval = 0,
.maxval = 1,
.defaultval = 1,
- .conflicts = { LAST_CONFLICT },
+ .conflicts = { { NULL, LAST_CONFLICT } },
},
},
};
struct opt_params sopts = {
.name = 's',
.subopts = {
-#define S_LOG 0
- "log",
-#define S_SECTLOG 1
- "sectlog",
-#define S_SIZE 2
- "size",
-#define S_SECTSIZE 3
- "sectsize",
- NULL
+ [S_SIZE] = "size",
+ [S_SECTSIZE] = "sectsize",
},
.subopt_params = {
- { .index = S_LOG,
- .conflicts = { S_SIZE,
- S_SECTSIZE,
- LAST_CONFLICT },
- .minval = XFS_MIN_SECTORSIZE_LOG,
- .maxval = XFS_MAX_SECTORSIZE_LOG,
- .defaultval = SUBOPT_NEEDS_VAL,
- },
- { .index = S_SECTLOG,
- .conflicts = { S_SIZE,
- S_SECTSIZE,
- LAST_CONFLICT },
- .minval = XFS_MIN_SECTORSIZE_LOG,
- .maxval = XFS_MAX_SECTORSIZE_LOG,
- .defaultval = SUBOPT_NEEDS_VAL,
- },
{ .index = S_SIZE,
- .conflicts = { S_LOG,
- S_SECTLOG,
- LAST_CONFLICT },
+ .conflicts = { { &sopts, S_SECTSIZE },
+ { &dopts, D_SECTSIZE },
+ { NULL, LAST_CONFLICT } },
.convert = true,
.is_power_2 = true,
.minval = XFS_MIN_SECTORSIZE,
.defaultval = SUBOPT_NEEDS_VAL,
},
{ .index = S_SECTSIZE,
- .conflicts = { S_LOG,
- S_SECTLOG,
- LAST_CONFLICT },
+ .conflicts = { { &sopts, S_SIZE },
+ { &dopts, D_SECTSIZE },
+ { NULL, LAST_CONFLICT } },
.convert = true,
.is_power_2 = true,
.minval = XFS_MIN_SECTORSIZE,
struct opt_params mopts = {
.name = 'm',
.subopts = {
-#define M_CRC 0
- "crc",
-#define M_FINOBT 1
- "finobt",
-#define M_UUID 2
- "uuid",
- NULL
+ [M_CRC] = "crc",
+ [M_FINOBT] = "finobt",
+ [M_UUID] = "uuid",
+ [M_RMAPBT] = "rmapbt",
+ [M_REFLINK] = "reflink",
},
.subopt_params = {
{ .index = M_CRC,
- .conflicts = { LAST_CONFLICT },
+ .conflicts = { { NULL, LAST_CONFLICT } },
.minval = 0,
.maxval = 1,
.defaultval = 1,
},
{ .index = M_FINOBT,
- .conflicts = { LAST_CONFLICT },
+ .conflicts = { { NULL, LAST_CONFLICT } },
.minval = 0,
.maxval = 1,
.defaultval = 1,
},
{ .index = M_UUID,
- .conflicts = { LAST_CONFLICT },
+ .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,
+ },
},
};
-#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)))
+/* 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;
+}
/*
- * Use this macro before we have superblock and mount structure
+ * 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.
*/
-#define DTOBT(d) ((xfs_rfsblock_t)((d) >> (blocklog - BBSHIFT)))
+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;
+};
-/*
- * Use this for block reservations needed for mkfs's conditions
- * (basically no fragmentation).
- */
-#define MKFS_BLOCKRES_INODE \
- ((uint)(mp->m_ialloc_blks + (mp->m_in_maxlevels - 1)))
-#define MKFS_BLOCKRES(rb) \
- ((uint)(MKFS_BLOCKRES_INODE + XFS_DA_NODE_MAXDEPTH + \
- (XFS_BM_MAXLEVELS(mp, XFS_DATA_FORK) - 1) + (rb)))
+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;
-/* amount (in bytes) we zero at the beginning and end of the device to
- * remove traces of other filesystems, raid superblocks, etc.
+ /* 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.
*/
-#define WHACK_SIZE (128 * 1024)
+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;
+};
/*
- * Convert lsu to lsunit for 512 bytes blocks and check validity of the values.
+ * 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
-calc_stripe_factors(
- int dsu,
- int dsw,
- int dsectsz,
- int lsu,
- int lsectsz,
- int *dsunit,
- int *dswidth,
- int *lsunit)
+conflict(
+ struct opt_params *opts,
+ int option,
+ struct opt_params *con_opts,
+ int conflict)
{
- /* Handle data sunit/swidth options */
- if ((*dsunit && !*dswidth) || (!*dsunit && *dswidth)) {
- fprintf(stderr,
- _("both data sunit and data swidth options "
- "must be specified\n"));
- usage();
- }
+ fprintf(stderr, _("Cannot specify both -%c %s and -%c %s\n"),
+ con_opts->name, con_opts->subopts[conflict],
+ opts->name, opts->subopts[option]);
+ usage();
+}
- if (dsu || dsw) {
- if ((dsu && !dsw) || (!dsu && dsw)) {
+
+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,
- _("both data su and data sw options "
- "must be specified\n"));
+_("Blocksize must be provided prior to using 'b' suffix.\n"));
usage();
+ } else {
+ return i * blksize;
}
-
- if (dsu % dsectsz) {
+ }
+ if (*sp == 's') {
+ if (!sectsize) {
fprintf(stderr,
- _("data su must be a multiple of the "
- "sector size (%d)\n"), dsectsz);
+_("Sectorsize must be specified prior to using 's' suffix.\n"));
usage();
+ } else {
+ return i * sectsize;
}
-
- *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);
+ 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
bool force_overwrite,
const char *optname)
{
- struct stat64 statbuf;
+ struct stat statbuf;
if (*isfile && (no_size || no_name)) {
fprintf(stderr,
usage();
}
- if (stat64(name, &statbuf)) {
+ if (stat(name, &statbuf)) {
if (errno == ENOENT && *isfile) {
if (create)
*create = 1;
}
static void
-fixup_log_stripe_unit(
- int lsflag,
- int sunit,
- xfs_rfsblock_t *logblocks,
- int blocklog)
-{
- __uint64_t tmp_logblocks;
-
- /*
- * Make sure that the log size is a multiple of the stripe unit
- */
- if ((*logblocks % sunit) != 0) {
- if (!lsflag) {
- tmp_logblocks = ((*logblocks + (sunit - 1))
- / sunit) * sunit;
- /*
- * If the log is too large, round down
- * instead of round up
- */
- if ((tmp_logblocks > XFS_MAX_LOG_BLOCKS) ||
- ((tmp_logblocks << blocklog) > XFS_MAX_LOG_BYTES)) {
- tmp_logblocks = (*logblocks / sunit) * sunit;
- }
- *logblocks = tmp_logblocks;
- } else {
- fprintf(stderr, _("log size %lld is not a multiple "
- "of the log stripe unit %d\n"),
- (long long) *logblocks, sunit);
- usage();
- }
- }
-}
-
-static xfs_fsblock_t
-fixup_internal_log_stripe(
- xfs_mount_t *mp,
- int lsflag,
- xfs_fsblock_t logstart,
- __uint64_t agsize,
- int sunit,
- xfs_rfsblock_t *logblocks,
+validate_ag_geometry(
int blocklog,
- int *lalign)
+ uint64_t dblocks,
+ uint64_t agsize,
+ uint64_t agcount)
{
- if ((logstart % sunit) != 0) {
- logstart = ((logstart + (sunit - 1))/sunit) * sunit;
- *lalign = 1;
- }
-
- fixup_log_stripe_unit(lsflag, sunit, logblocks, blocklog);
-
- if (*logblocks > agsize - XFS_FSB_TO_AGBNO(mp, logstart)) {
- fprintf(stderr,
- _("Due to stripe alignment, the internal log size "
- "(%lld) is too large.\n"), (long long) *logblocks);
- fprintf(stderr, _("Must fit within an allocation group.\n"));
- usage();
- }
- return logstart;
-}
-
-void
-validate_log_size(__uint64_t logblocks, int blocklog, int min_logblocks)
-{
- if (logblocks < min_logblocks) {
- fprintf(stderr,
- _("log size %lld blocks too small, minimum size is %d blocks\n"),
- (long long)logblocks, min_logblocks);
- usage();
- }
- if (logblocks > XFS_MAX_LOG_BLOCKS) {
- fprintf(stderr,
- _("log size %lld blocks too large, maximum size is %lld blocks\n"),
- (long long)logblocks, XFS_MAX_LOG_BLOCKS);
- usage();
- }
- if ((logblocks << blocklog) > XFS_MAX_LOG_BYTES) {
- fprintf(stderr,
- _("log size %lld bytes too large, maximum size is %lld bytes\n"),
- (long long)(logblocks << blocklog), XFS_MAX_LOG_BYTES);
- usage();
- }
-}
-
-static int
-calc_default_imaxpct(
- int blocklog,
- __uint64_t dblocks)
-{
- /*
- * This returns the % of the disk space that is used for
- * inodes, it changes relatively to the FS size:
- * - over 50 TB, use 1%,
- * - 1TB - 50 TB, use 5%,
- * - under 1 TB, use XFS_DFL_IMAXIMUM_PCT (25%).
- */
-
- if (dblocks < TERABYTES(1, blocklog)) {
- return XFS_DFL_IMAXIMUM_PCT;
- } else if (dblocks < TERABYTES(50, blocklog)) {
- return 5;
- }
-
- return 1;
-}
-
-static void
-validate_ag_geometry(
- int blocklog,
- __uint64_t dblocks,
- __uint64_t agsize,
- __uint64_t agcount)
-{
- if (agsize < XFS_AG_MIN_BLOCKS(blocklog)) {
+ if (agsize < XFS_AG_MIN_BLOCKS(blocklog)) {
fprintf(stderr,
_("agsize (%lld blocks) too small, need at least %lld blocks\n"),
(long long)agsize,
{
void *buf;
xfs_sb_t sb;
- __uint32_t bsize;
+ uint32_t bsize;
int i;
xfs_off_t off;
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) *
+ 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;
off = 0;
for (i = 1; i < sb.sb_agcount; i++) {
off += sb.sb_agblocks;
- if (pwrite64(xi->dfd, buf, new_sb->sb_sectsize,
+ if (pwrite(xi->dfd, buf, new_sb->sb_sectsize,
off << sb.sb_blocklog) == -1)
break;
}
}
static void
-discard_blocks(dev_t dev, __uint64_t nsectors)
+discard_blocks(dev_t dev, uint64_t nsectors)
{
int fd;
platform_discard_blocks(fd, 0, nsectors << 9);
}
-struct sb_feat_args {
- int log_version;
- int attr_version;
- int dir_version;
- int spinodes;
- int finobt;
- bool inode_align;
- bool nci;
- bool lazy_sb_counters;
- bool projid16bit;
- bool crcs_enabled;
- bool dirftype;
- bool parent_pointers;
-};
-
-static void
-sb_set_features(
- struct xfs_sb *sbp,
- struct sb_feat_args *fp,
- int sectsize,
- int lsectsize,
- int dsunit)
-{
-
- sbp->sb_versionnum = XFS_DFL_SB_VERSION_BITS;
- if (fp->crcs_enabled)
- sbp->sb_versionnum |= XFS_SB_VERSION_5;
- else
- sbp->sb_versionnum |= XFS_SB_VERSION_4;
-
- if (fp->inode_align)
- sbp->sb_versionnum |= XFS_SB_VERSION_ALIGNBIT;
- if (dsunit)
- sbp->sb_versionnum |= XFS_SB_VERSION_DALIGNBIT;
- if (fp->log_version == 2)
- sbp->sb_versionnum |= XFS_SB_VERSION_LOGV2BIT;
- if (fp->attr_version == 1)
- sbp->sb_versionnum |= XFS_SB_VERSION_ATTRBIT;
- if (sectsize > BBSIZE || lsectsize > BBSIZE)
- sbp->sb_versionnum |= XFS_SB_VERSION_SECTORBIT;
- if (fp->nci)
- sbp->sb_versionnum |= XFS_SB_VERSION_BORGBIT;
-
-
- sbp->sb_features2 = 0;
- if (fp->lazy_sb_counters)
- sbp->sb_features2 |= XFS_SB_VERSION2_LAZYSBCOUNTBIT;
- if (!fp->projid16bit)
- sbp->sb_features2 |= XFS_SB_VERSION2_PROJID32BIT;
- if (fp->parent_pointers)
- sbp->sb_features2 |= XFS_SB_VERSION2_PARENTBIT;
- if (fp->crcs_enabled)
- sbp->sb_features2 |= XFS_SB_VERSION2_CRCBIT;
- if (fp->attr_version == 2)
- sbp->sb_features2 |= XFS_SB_VERSION2_ATTR2BIT;
-
- /* v5 superblocks have their own feature bit for dirftype */
- if (fp->dirftype && !fp->crcs_enabled)
- sbp->sb_features2 |= XFS_SB_VERSION2_FTYPE;
-
- /* update whether extended features are in use */
- if (sbp->sb_features2 != 0)
- sbp->sb_versionnum |= XFS_SB_VERSION_MOREBITSBIT;
-
- /*
- * Due to a structure alignment issue, sb_features2 ended up in one
- * of two locations, the second "incorrect" location represented by
- * the sb_bad_features2 field. To avoid older kernels mounting
- * filesystems they shouldn't, set both field to the same value.
- */
- sbp->sb_bad_features2 = sbp->sb_features2;
-
- if (!fp->crcs_enabled)
- return;
-
- /* default features for v5 filesystems */
- sbp->sb_features_compat = 0;
- sbp->sb_features_ro_compat = 0;
- sbp->sb_features_incompat = XFS_SB_FEAT_INCOMPAT_FTYPE;
- sbp->sb_features_log_incompat = 0;
-
- if (fp->finobt)
- sbp->sb_features_ro_compat = XFS_SB_FEAT_RO_COMPAT_FINOBT;
-
- /*
- * Sparse inode chunk support has two main inode alignment requirements.
- * First, sparse chunk alignment must match the cluster size. Second,
- * full chunk alignment must match the inode chunk size.
- *
- * Copy the already calculated/scaled inoalignmt to spino_align and
- * update the former to the full inode chunk size.
- */
- if (fp->spinodes) {
- sbp->sb_spino_align = sbp->sb_inoalignmt;
- sbp->sb_inoalignmt = XFS_INODES_PER_CHUNK *
- sbp->sb_inodesize >> sbp->sb_blocklog;
- sbp->sb_features_incompat |= XFS_SB_FEAT_INCOMPAT_SPINODES;
- }
-
-}
-
static __attribute__((noreturn)) void
illegal_option(
const char *value,
const char *reason)
{
fprintf(stderr,
- _("Illegal value %s for -%c %s option. %s\n"),
+ _("Invalid value %s for -%c %s option. %s\n"),
value, opts->name, opts->subopts[index],
- reason ? reason : "");
+ reason);
usage();
}
if (sp->index != index) {
fprintf(stderr,
- ("Developer screwed up option parsing (%d/%d)! Please report!\n"),
+ _("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);
}
/*
*/
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;
}
/* check for conflicts with the option */
for (i = 0; i < MAX_CONFLICTS; i++) {
- int conflict_opt = sp->conflicts[i];
+ struct _conflict *con = &sp->conflicts[i];
- if (conflict_opt == LAST_CONFLICT)
+ if (con->subopt == LAST_CONFLICT)
break;
- if (opts->subopt_params[conflict_opt].seen ||
- opts->subopt_params[conflict_opt].str_seen)
- conflict(opts->name, (char **)opts->subopts,
- conflict_opt, index);
+ if (con->opts->subopt_params[con->subopt].seen ||
+ con->opts->subopt_params[con->subopt].str_seen)
+ conflict(opts, index, con->opts, con->subopt);
}
}
/* 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;
}
c = strtoll(str, &str_end, 0);
if (c == 0 && str_end == str)
- illegal_option(str, opts, index, NULL);
+ illegal_option(str, opts, index,
+ _("Value not recognized as number."));
if (*str_end != '\0')
- illegal_option(str, opts, index, NULL);
+ 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"));
+ illegal_option(str, opts, index, _("Value is too small."));
else if (c > sp->maxval)
- illegal_option(str, opts, index, _("value is too large"));
+ 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"));
+ illegal_option(str, opts, index, _("Value must be a power of 2."));
return c;
}
/* 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)
+static int
+block_opts_parser(
+ struct opt_params *opts,
+ int subopt,
+ char *value,
+ struct cli_params *cli)
{
- __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 force_overwrite;
- struct fsxattr fsx;
- int ilflag;
- 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 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,
- };
-
- platform_uuid_generate(&uuid);
- progname = basename(argv[0]);
- setlocale(LC_ALL, "");
- bindtextdomain(PACKAGE, LOCALEDIR);
- textdomain(PACKAGE);
-
- 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;
- nodsflag = norsflag = 0;
- force_overwrite = 0;
- worst_freelist = 0;
- memset(&fsx, 0, sizeof(fsx));
-
- memset(&xi, 0, sizeof(xi));
- 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':
- 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);
- break;
- case D_SWIDTH:
- dswidth = getnum(value, &dopts,
- D_SWIDTH);
- break;
- case D_SU:
- dsu = getnum(value, &dopts, D_SU);
- break;
- case D_SW:
- dsw = getnum(value, &dopts, D_SW);
- 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;
- 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, "");
- }
- }
- 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;
-
- /*
- * 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 (blocksize < XFS_MIN_BLOCKSIZE || blocksize > XFS_MAX_BLOCKSIZE) {
- fprintf(stderr, _("illegal block size %d\n"), blocksize);
- usage();
- }
- if (sb_feat.crcs_enabled && blocksize < 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();
+ switch (subopt) {
+ case B_SIZE:
+ cli->blocksize = getnum(value, opts, subopt);
+ break;
+ default:
+ return -EINVAL;
}
+ return 0;
+}
- if (!slflag && !ssflag) {
- sectorlog = XFS_MIN_SECTORSIZE_LOG;
- sectorsize = XFS_MIN_SECTORSIZE;
- }
- if (!lslflag && !lssflag) {
- lsectorlog = sectorlog;
- lsectorsize = sectorsize;
+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;
+}
- /*
- * Before anything else, verify that we are correctly operating on
- * files or block devices and set the control parameters correctly.
+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.
*/
- check_device_type(dfile, &xi.disfile, !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,
- force_overwrite, "l");
- if (xi.rtname)
- check_device_type(xi.rtname, &xi.risfile, !rtsize, !xi.rtname,
- Nflag ? NULL : &xi.rcreat,
- force_overwrite, "r");
- if (xi.disfile || xi.lisfile || xi.risfile)
- xi.isdirect = 0;
+ if (cli->xi->disfile || cli->xi->lisfile || cli->xi->risfile)
+ cli->xi->isdirect = 0;
- memset(&ft, 0, sizeof(ft));
- get_topology(&xi, &ft, force_overwrite);
+ memset(ft, 0, sizeof(*ft));
+ get_topology(cli->xi, ft, force_overwrite);
- if (!ssflag) {
+ 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.
*/
- /* Older kernels may not have physical/logical distinction */
- if (!ft.psectorsize)
- ft.psectorsize = ft.lsectorsize;
+ if (!ft->lsectorsize)
+ ft->lsectorsize = XFS_MIN_SECTORSIZE;
- sectorsize = ft.psectorsize ? ft.psectorsize :
- XFS_MIN_SECTORSIZE;
+ /* Older kernels may not have physical/logical distinction */
+ if (!ft->psectorsize)
+ ft->psectorsize = ft->lsectorsize;
- if ((blocksize < sectorsize) && (blocksize >= 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"),
- blocksize, ft.psectorsize);
- fprintf(stderr,
-_("switching to logical sector size %d\n"),
- ft.lsectorsize);
- sectorsize = ft.lsectorsize ? ft.lsectorsize :
- XFS_MIN_SECTORSIZE;
+_("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);
}
- if (!ssflag) {
- sectorlog = libxfs_highbit32(sectorsize);
- if (loginternal) {
- lsectorsize = sectorsize;
- lsectorlog = sectorlog;
- }
+ /* 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 (sectorsize < XFS_MIN_SECTORSIZE ||
- sectorsize > XFS_MAX_SECTORSIZE || sectorsize > blocksize) {
- if (ssflag)
- fprintf(stderr, _("illegal sector size %d\n"), sectorsize);
- else
- fprintf(stderr,
-_("block size %d cannot be smaller than logical sector size %d\n"),
- blocksize, ft.lsectorsize);
+ if (cfg->blocksize < cfg->sectorsize) {
+ fprintf(stderr,
+_("block size %d cannot be smaller than sector size %d\n"),
+ cfg->blocksize, cfg->sectorsize);
usage();
}
- if (sectorsize < ft.lsectorsize) {
+
+ if (cfg->sectorsize < ft->lsectorsize) {
fprintf(stderr, _("illegal sector size %d; hw sector is %d\n"),
- sectorsize, ft.lsectorsize);
+ 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 (lsectorsize < XFS_MIN_SECTORSIZE ||
- lsectorsize > XFS_MAX_SECTORSIZE || lsectorsize > blocksize) {
- fprintf(stderr, _("illegal log sector size %d\n"), lsectorsize);
+
+ 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();
- } else if (lsectorsize > XFS_MIN_SECTORSIZE && !lsu && !lsunit) {
- lsu = blocksize;
- sb_feat.log_version = 2;
}
+ 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 (sb_feat.crcs_enabled) {
- /* minimum inode size is 512 bytes, ipflag checked later */
- if ((isflag || ilflag) && inodelog < XFS_DINODE_DFL_CRC_LOG) {
+ 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);
}
/* inodes always aligned */
- if (!sb_feat.inode_align) {
+ if (!cli->sb_feat.inode_align) {
fprintf(stderr,
-_("Inodes always aligned for CRC enabled filesytems\n"));
+_("Inodes always aligned for CRC enabled filesystems\n"));
usage();
}
/* lazy sb counters always on */
- if (!sb_feat.lazy_sb_counters) {
+ if (!cli->sb_feat.lazy_sb_counters) {
fprintf(stderr,
-_("Lazy superblock counted always enabled for CRC enabled filesytems\n"));
+_("Lazy superblock counters always enabled for CRC enabled filesystems\n"));
usage();
}
/* version 2 logs always on */
- if (sb_feat.log_version != 2) {
+ if (cli->sb_feat.log_version != 2) {
fprintf(stderr,
-_("V2 logs always enabled for CRC enabled filesytems\n"));
+_("V2 logs always enabled for CRC enabled filesystems\n"));
usage();
}
/* attr2 always on */
- if (sb_feat.attr_version != 2) {
+ if (cli->sb_feat.attr_version != 2) {
fprintf(stderr,
-_("V2 attribute format always enabled on CRC enabled filesytems\n"));
+_("V2 attribute format always enabled on CRC enabled filesystems\n"));
usage();
}
/* 32 bit project quota always on */
/* attr2 always on */
- if (sb_feat.projid16bit) {
+ 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,
-_("32 bit Project IDs always enabled on CRC enabled filesytems\n"));
+_("Directory ftype field always enabled on CRC enabled filesystems\n"));
usage();
}
+
} else {
/*
* The kernel doesn't currently support crc=0,finobt=1
* then issue an error.
* The same is also for sparse inodes.
*/
- if (sb_feat.finobt && mopts.subopt_params[M_FINOBT].seen) {
+ if (cli->sb_feat.finobt && cli_opt_set(&mopts, M_FINOBT)) {
fprintf(stderr,
_("finobt not supported without CRC support\n"));
usage();
}
- sb_feat.finobt = 0;
+ cli->sb_feat.finobt = false;
- if (sb_feat.spinodes) {
+ if (cli->sb_feat.spinodes && cli_opt_set(&iopts, I_SPINODES)) {
fprintf(stderr,
_("sparse inodes not supported without CRC support\n"));
usage();
}
- sb_feat.spinodes = 0;
+ 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 (nsflag || nlflag) {
- if (dirblocksize < blocksize ||
- dirblocksize > XFS_MAX_BLOCKSIZE) {
- fprintf(stderr, _("illegal directory block size %d\n"),
- dirblocksize);
+ if (cli->sb_feat.reflink) {
+ fprintf(stderr,
+_("reflink not supported without CRC support\n"));
usage();
}
- } else {
- if (blocksize < (1 << XFS_MIN_REC_DIRSIZE))
- dirblocklog = XFS_MIN_REC_DIRSIZE;
- else
- dirblocklog = blocklog;
- dirblocksize = 1 << dirblocklog;
+ 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 (dsize) {
- __uint64_t dbytes;
+ if (cli->sb_feat.reflink && cli->xi->rtname) {
+ fprintf(stderr,
+_("reflink not supported with realtime devices\n"));
+ usage();
+ cli->sb_feat.reflink = false;
+ }
- 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);
+ 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();
}
- 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
- : XFS_DINODE_DFL_LOG;
- isize = 1 << inodelog;
- }
- if (sb_feat.crcs_enabled && inodelog < XFS_DINODE_DFL_CRC_LOG) {
+ 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 (logsize) {
- __uint64_t logbytes;
+ 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;
- logbytes = getnum(logsize, &lopts, L_SIZE);
- if (logbytes % XFS_MIN_BLOCKSIZE) {
+ 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,
- _("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"),
+ cfg->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"),
+ cfg->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 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 (rtextsize) {
- __uint64_t rtextbytes;
+ if (cli->rtextsize) {
- rtextbytes = getnum(rtextsize, &ropts, R_EXTSIZE);
- if (rtextbytes % blocksize) {
+ 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, blocksize);
+ (long long)rtextbytes, cfg->blocksize);
usage();
}
- rtextblocks = (xfs_extlen_t)(rtextbytes >> blocklog);
+ 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;
- __uint64_t rtextbytes;
+ uint64_t rswidth;
- if (!norsflag && !xi.risfile && !(!rtsize && xi.disfile))
- rswidth = ft.rtswidth;
+ 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 (!norsflag && rswidth && !(BBTOB(rswidth) % blocksize)) {
- rswidth = DTOBT(rswidth);
- rtextbytes = rswidth << blocklog;
- if (XFS_MIN_RTEXTSIZE <= rtextbytes &&
- (rtextbytes <= XFS_MAX_RTEXTSIZE)) {
- rtextblocks = rswidth;
+ 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 (!rtextblocks) {
- rtextblocks = (blocksize < XFS_MIN_RTEXTSIZE) ?
- XFS_MIN_RTEXTSIZE >> blocklog : 1;
+ if (!cfg->rtextblocks) {
+ cfg->rtextblocks = (cfg->blocksize < XFS_MIN_RTEXTSIZE)
+ ? XFS_MIN_RTEXTSIZE >> cfg->blocklog
+ : 1;
}
}
- ASSERT(rtextblocks);
+ ASSERT(cfg->rtextblocks);
+}
- /*
- * 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;
+/*
+ * 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();
+ }
- 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)
+ /* 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,
- _("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 % cfg->sectorsize) {
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"), 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 lsu or lsunit was specified, automatically use v2 logs */
- if ((lsu || lsunit) && sb_feat.log_version == 1) {
+ 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) {
+ /* Ignore nonsense from device. XXX add more validation */
+ if (ft->dsunit && ft->dswidth == 0) {
+ fprintf(stderr,
+_("%s: Volume reports stripe unit of %d bytes and stripe width of 0, ignoring.\n"),
+ progname, BBTOB(ft->dsunit));
+ ft->dsunit = 0;
+ ft->dswidth = 0;
+ } else {
+ dsunit = ft->dsunit;
+ dswidth = ft->dswidth;
+ use_dev = true;
+ }
+ } else {
+ /* check and warn if user-specified 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 specified, using v2 logs\n"));
- sb_feat.log_version = 2;
+_("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;
}
- calc_stripe_factors(dsu, dsw, sectorsize, lsu, lsectorsize,
- &dsunit, &dswidth, &lsunit);
+}
- xi.setblksize = sectorsize;
+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.
*/
- if (!libxfs_init(&xi))
+ xi->setblksize = cfg->sectorsize;
+ if (!libxfs_init(xi))
usage();
- if (!xi.ddev) {
+ if (!xi->ddev) {
fprintf(stderr, _("no device name given in argument list\n"));
usage();
}
* 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);
- sector_mask = (__uint64_t)-1 << (MAX(sectorlog, 10) - BBSHIFT);
- xi.dsize &= sector_mask;
- xi.rtsize &= sector_mask;
- xi.logBBsize &= (__uint64_t)-1 << (MAX(lsectorlog, 10) - BBSHIFT);
-
-
- /* don't do discards on print-only runs or on files */
- if (discard && !Nflag) {
- 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);
- }
-
- if (!liflag && !ldflag)
- loginternal = xi.logdev == 0;
- if (xi.logname)
- logfile = xi.logname;
- else if (loginternal)
- logfile = _("internal log");
- else if (xi.volname && xi.logdev)
- logfile = _("volume log");
- else if (!ldflag) {
- fprintf(stderr, _("no log subvolume or internal log\n"));
- usage();
+
+ 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 (xi.rtname)
- rtfile = xi.rtname;
- else
- if (xi.volname && xi.rtdev)
- rtfile = _("volume rt");
- else if (!xi.rtdev)
- rtfile = _("none");
- if (dsize && xi.dsize > 0 && dblocks > DTOBT(xi.dsize)) {
+
+ if (cfg->dblocks < XFS_MIN_DATA_BLOCKS) {
fprintf(stderr,
- _("size %s specified for data subvolume is too large, "
- "maximum is %lld blocks\n"),
- dsize, (long long)DTOBT(xi.dsize));
- usage();
- } else if (!dsize && xi.dsize > 0)
- dblocks = DTOBT(xi.dsize);
- else if (!dsize) {
- fprintf(stderr, _("can't get size of data subvolume\n"));
+_("size %lld of data subvolume is too small, minimum %d blocks\n"),
+ (long long)cfg->dblocks, XFS_MIN_DATA_BLOCKS);
usage();
}
- if (dblocks < XFS_MIN_DATA_BLOCKS) {
- fprintf(stderr,
- _("size %lld of data subvolume is too small, minimum %d blocks\n"),
- (long long)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);
}
+}
- if (loginternal && xi.logdev) {
- fprintf(stderr,
- _("can't have both external and internal logs\n"));
+/*
+ * 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();
- } else if (loginternal && sectorsize != lsectorsize) {
+ }
+
+ 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,
- _("data and log sector sizes must be equal for internal logs\n"));
+_("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.dbsize > sectorsize) {
- fprintf(stderr, _(
-"Warning: the data subvolume sector size %u is less than the sector size \n\
-reported by the device (%u).\n"),
- sectorsize, xi.dbsize);
- }
- if (!loginternal && xi.lbsize > lsectorsize) {
+ 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"),
- lsectorsize, xi.lbsize);
- }
- if (rtsize && xi.rtsize > 0 && xi.rtbsize > sectorsize) {
- fprintf(stderr, _(
-"Warning: the realtime subvolume sector size %u is less than the sector size\n\
-reported by the device (%u).\n"),
- sectorsize, xi.rtbsize);
+ cfg->lsectorsize, xi->lbsize);
}
+}
- if (rtsize && xi.rtsize > 0 && rtblocks > DTOBT(xi.rtsize)) {
- fprintf(stderr,
- _("size %s specified for rt subvolume is too large, "
- "maximum is %lld blocks\n"),
- rtsize, (long long)DTOBT(xi.rtsize));
- usage();
- } else if (!rtsize && xi.rtsize > 0)
- rtblocks = DTOBT(xi.rtsize);
- else if (rtsize && !xi.rtdev) {
- fprintf(stderr,
- _("size specified for non-existent rt subvolume\n"));
+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();
}
- if (xi.rtdev) {
- rtextents = rtblocks / rtextblocks;
- nbmblocks = (xfs_extlen_t)howmany(rtextents, NBBY * blocksize);
- } else {
- rtextents = rtblocks = 0;
- nbmblocks = 0;
- }
-
- if (!nodsflag) {
- if (dsunit) {
- 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);
- }
- } else {
- dsunit = ft.dsunit;
- dswidth = ft.dswidth;
- nodsflag = 1;
+
+ /* 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 dsunit & dswidth can't be set if nodsflag is set */
+ } 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);
- if (dasize) { /* User-specified AG size */
/*
* Check specified agsize is a multiple of blocksize.
*/
- if (agsize % blocksize) {
+ if (cfg->agsize % cfg->blocksize) {
fprintf(stderr,
- _("agsize (%lld) not a multiple of fs blk size (%d)\n"),
- (long long)agsize, blocksize);
+_("agsize (%s) not a multiple of fs blk size (%d)\n"),
+ cli->agsize, cfg->blocksize);
usage();
}
- agsize /= blocksize;
- agcount = dblocks / agsize + (dblocks % agsize != 0);
-
- } else if (daflag) { /* User-specified AG count */
- agsize = dblocks / agcount + (dblocks % agcount != 0);
+ 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(blocklog, dblocks,
- dsunit | dswidth, &agsize, &agcount);
+ calc_default_ag_geometry(cfg->blocklog, cfg->dblocks,
+ cfg->dsunit, &cfg->agsize,
+ &cfg->agcount);
}
+}
- /*
- * If dsunit is a multiple of fs blocksize, then check that is a
- * multiple of the agsize too
- */
- if (dsunit && !(BBTOB(dsunit) % blocksize) &&
- dswidth && !(BBTOB(dswidth) % blocksize)) {
+/*
+ * 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;
- /* convert from 512 byte blocks to fs blocksize */
- dsunit = DTOBT(dsunit);
- dswidth = DTOBT(dswidth);
+ if (!dsunit)
+ goto validate;
+ /*
+ * agsize is not a multiple of dsunit
+ */
+ if ((cfg->agsize % dsunit) != 0) {
/*
- * agsize is not a multiple of dsunit
+ * Round up to stripe unit boundary. Also make sure
+ * that agsize is still larger than
+ * XFS_AG_MIN_BLOCKS(blocklog)
*/
- if ((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 = ((agsize + (dsunit - 1))/ dsunit) * dsunit;
+ 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)) {
+
/*
- * Round down to stripe unit boundary if rounding up
- * created an AG size that is larger than the AG max.
+ * If the AG size is invalid and we are using device
+ * probed stripe alignment, just clear the alignment
+ * and continue on.
*/
- if (tmp_agsize > XFS_AG_MAX_BLOCKS(blocklog))
- tmp_agsize = ((agsize) / dsunit) * dsunit;
-
- if ((tmp_agsize >= XFS_AG_MIN_BLOCKS(blocklog)) &&
- (tmp_agsize <= XFS_AG_MAX_BLOCKS(blocklog))) {
- agsize = tmp_agsize;
- if (!daflag)
- agcount = dblocks/agsize +
- (dblocks % agsize != 0);
- if (dasize)
- fprintf(stderr,
- _("agsize rounded to %lld, swidth = %d\n"),
- (long long)agsize, dswidth);
- } else {
- if (nodsflag) {
- dsunit = dswidth = 0;
- } else {
- /*
- * agsize is out of bounds, this will
- * print nice details & exit.
- */
- validate_ag_geometry(blocklog, dblocks,
- agsize, agcount);
- exit(1);
- }
+ if (!cli_opt_set(&dopts, D_SUNIT) &&
+ !cli_opt_set(&dopts, D_SU)) {
+ cfg->dsunit = 0;
+ cfg->dswidth = 0;
+ goto validate;
}
- }
- if (dswidth && ((agsize % dswidth) == 0) && (agcount > 1)) {
- /* 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.
+ /*
+ * set the agsize to the invalid value so the following
+ * validation of the ag will fail and print a nice error
+ * and exit.
*/
- tmp_agsize = agsize - dsunit;
- if (tmp_agsize < XFS_AG_MIN_BLOCKS(blocklog)) {
- tmp_agsize = agsize + dsunit;
- if (dblocks < agsize) {
- /* oh well, nothing to do */
- tmp_agsize = agsize;
- }
- }
- if (daflag || dasize) {
- fprintf(stderr, _(
+ 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, for example %llu.\n"),
- (unsigned long long)tmp_agsize);
- } else {
- agsize = tmp_agsize;
- agcount = dblocks/agsize + (dblocks % agsize != 0);
- /*
- * 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))) {
- dblocks = (xfs_rfsblock_t)((agcount - 1) * agsize);
- agcount--;
- ASSERT(agcount != 0);
- }
- }
+an AG size that is one stripe unit smaller or larger, for example %llu.\n"),
+ (unsigned long long)cfg->agsize - dsunit);
+ goto validate;
}
- } else {
- if (nodsflag)
- dsunit = dswidth = 0;
- else {
- 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),
- blocksize);
- exit(1);
+
+ /*
+ * 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->agcount = cfg->dblocks / cfg->agsize +
+ (cfg->dblocks % cfg->agsize != 0);
}
+validate:
/*
* 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))) {
- ASSERT(!daflag);
- dblocks = (xfs_rfsblock_t)((agcount - 1) * agsize);
- agcount--;
- ASSERT(agcount != 0);
+ if (cfg->dblocks % cfg->agsize != 0 &&
+ (cfg->dblocks % cfg->agsize < XFS_AG_MIN_BLOCKS(cfg->blocklog))) {
+ ASSERT(!cli_opt_set(&dopts, D_AGCOUNT));
+ cfg->dblocks = (xfs_rfsblock_t)((cfg->agcount - 1) * cfg->agsize);
+ cfg->agcount--;
+ ASSERT(cfg->agcount != 0);
}
- validate_ag_geometry(blocklog, dblocks, agsize, agcount);
+ validate_ag_geometry(cfg->blocklog, cfg->dblocks,
+ cfg->agsize, cfg->agcount);
+}
- if (!imflag)
- imaxpct = calc_default_imaxpct(blocklog, dblocks);
+static void
+calculate_imaxpct(
+ struct mkfs_params *cfg,
+ struct cli_params *cli)
+{
+ cfg->imaxpct = cli->imaxpct;
+ if (cfg->imaxpct)
+ return;
/*
- * check that log sunit is modulo fsblksize or default it to dsunit.
+ * This returns the % of the disk space that is used for
+ * inodes, it changes relatively to the FS size:
+ * - over 50 TB, use 1%,
+ * - 1TB - 50 TB, use 5%,
+ * - under 1 TB, use XFS_DFL_IMAXIMUM_PCT (25%).
*/
- if (lsunit) {
- /* convert from 512 byte blocks to fs blocks */
- lsunit = DTOBT(lsunit);
- } else if (sb_feat.log_version == 2 && loginternal && dsunit) {
- /* lsunit and dsunit now in fs blocks */
- lsunit = dsunit;
- }
+ if (cfg->dblocks < TERABYTES(1, cfg->blocklog))
+ cfg->imaxpct = XFS_DFL_IMAXIMUM_PCT;
+ else if (cfg->dblocks < TERABYTES(50, cfg->blocklog))
+ cfg->imaxpct = 5;
+ else
+ cfg->imaxpct = 1;
+}
- if (sb_feat.log_version == 2 && (lsunit * blocksize) > 256 * 1024) {
- /* Warn only if specified on commandline */
- if (lsuflag || lsunitflag) {
- fprintf(stderr,
- _("log stripe unit (%d bytes) is too large (maximum is 256KiB)\n"),
- (lsunit * blocksize));
- fprintf(stderr,
- _("log stripe unit adjusted to 32KiB\n"));
- }
- lsunit = (32 * 1024) >> blocklog;
- }
+/*
+ * Set up the initial state of the superblock so we can start using the
+ * libxfs geometry macros.
+ */
+static void
+sb_set_features(
+ struct mkfs_params *cfg,
+ struct xfs_sb *sbp)
+{
+ struct sb_feat_args *fp = &cfg->sb_feat;
- min_logblocks = max_trans_res(sb_feat.crcs_enabled, sb_feat.dir_version,
- sectorlog, blocklog, inodelog, dirblocklog,
- sb_feat.log_version, lsunit, sb_feat.finobt);
- ASSERT(min_logblocks);
- min_logblocks = MAX(XFS_MIN_LOG_BLOCKS, min_logblocks);
- if (!logsize && dblocks >= (1024*1024*1024) >> blocklog)
- min_logblocks = MAX(min_logblocks, XFS_MIN_LOG_BYTES>>blocklog);
- if (logsize && xi.logBBsize > 0 && logblocks > DTOBT(xi.logBBsize)) {
- fprintf(stderr,
-_("size %s specified for log subvolume is too large, maximum is %lld blocks\n"),
- logsize, (long long)DTOBT(xi.logBBsize));
- usage();
- } else if (!logsize && xi.logBBsize > 0) {
- logblocks = DTOBT(xi.logBBsize);
- } else if (logsize && !xi.logdev && !loginternal) {
- fprintf(stderr,
- _("size specified for non-existent log subvolume\n"));
- usage();
- } else if (loginternal && logsize && logblocks >= dblocks) {
- fprintf(stderr, _("size %lld too large for internal log\n"),
- (long long)logblocks);
- usage();
- } else if (!loginternal && !xi.logdev) {
- logblocks = 0;
- } else if (loginternal && !logsize) {
+ sbp->sb_versionnum = XFS_DFL_SB_VERSION_BITS;
+ if (fp->crcs_enabled)
+ sbp->sb_versionnum |= XFS_SB_VERSION_5;
+ else
+ sbp->sb_versionnum |= XFS_SB_VERSION_4;
- if (dblocks < GIGABYTES(1, blocklog)) {
- /* tiny filesystems get minimum sized logs. */
- logblocks = min_logblocks;
- } else if (dblocks < GIGABYTES(16, blocklog)) {
+ if (fp->inode_align) {
+ int cluster_size = XFS_INODE_BIG_CLUSTER_SIZE;
- /*
- * For small filesystems, we want to use the
- * XFS_MIN_LOG_BYTES for filesystems smaller than 16G if
- * at all possible, ramping up to 128MB at 256GB.
- */
- logblocks = MIN(XFS_MIN_LOG_BYTES >> blocklog,
- min_logblocks * XFS_DFL_LOG_FACTOR);
- } else {
- /*
- * With a 2GB max log size, default to maximum size
- * at 4TB. This keeps the same ratio from the older
- * max log size of 128M at 256GB fs size. IOWs,
- * the ratio of fs size to log size is 2048:1.
- */
- logblocks = (dblocks << blocklog) / 2048;
- logblocks = logblocks >> blocklog;
- }
+ sbp->sb_versionnum |= XFS_SB_VERSION_ALIGNBIT;
+ if (cfg->sb_feat.crcs_enabled)
+ cluster_size *= cfg->inodesize / XFS_DINODE_MIN_SIZE;
+ sbp->sb_inoalignmt = cluster_size >> cfg->blocklog;
+ } else
+ sbp->sb_inoalignmt = 0;
- /* Ensure the chosen size meets minimum log size requirements */
- logblocks = MAX(min_logblocks, logblocks);
+ if (cfg->dsunit)
+ sbp->sb_versionnum |= XFS_SB_VERSION_DALIGNBIT;
+ if (fp->log_version == 2)
+ sbp->sb_versionnum |= XFS_SB_VERSION_LOGV2BIT;
+ if (fp->attr_version == 1)
+ sbp->sb_versionnum |= XFS_SB_VERSION_ATTRBIT;
+ if (fp->nci)
+ sbp->sb_versionnum |= XFS_SB_VERSION_BORGBIT;
- /* make sure the log fits wholly within an AG */
- if (logblocks >= agsize)
- logblocks = min_logblocks;
+ if (cfg->sectorsize > BBSIZE || cfg->lsectorsize > BBSIZE) {
+ sbp->sb_versionnum |= XFS_SB_VERSION_SECTORBIT;
+ sbp->sb_logsectlog = (uint8_t)cfg->lsectorlog;
+ sbp->sb_logsectsize = (uint16_t)cfg->lsectorsize;
+ } else {
+ sbp->sb_logsectlog = 0;
+ sbp->sb_logsectsize = 0;
+ }
- /* and now clamp the size to the maximum supported size */
- logblocks = MIN(logblocks, XFS_MAX_LOG_BLOCKS);
- if ((logblocks << blocklog) > XFS_MAX_LOG_BYTES)
- logblocks = XFS_MAX_LOG_BYTES >> blocklog;
+ sbp->sb_features2 = 0;
+ if (fp->lazy_sb_counters)
+ sbp->sb_features2 |= XFS_SB_VERSION2_LAZYSBCOUNTBIT;
+ if (fp->projid32bit)
+ sbp->sb_features2 |= XFS_SB_VERSION2_PROJID32BIT;
+ if (fp->parent_pointers)
+ sbp->sb_features2 |= XFS_SB_VERSION2_PARENTBIT;
+ if (fp->crcs_enabled)
+ sbp->sb_features2 |= XFS_SB_VERSION2_CRCBIT;
+ if (fp->attr_version == 2)
+ sbp->sb_features2 |= XFS_SB_VERSION2_ATTR2BIT;
- }
- validate_log_size(logblocks, blocklog, min_logblocks);
+ /* v5 superblocks have their own feature bit for dirftype */
+ if (fp->dirftype && !fp->crcs_enabled)
+ sbp->sb_features2 |= XFS_SB_VERSION2_FTYPE;
- protostring = setup_proto(protofile);
- bsize = 1 << (blocklog - BBSHIFT);
- mp = &mbuf;
- sbp = &mp->m_sb;
- memset(mp, 0, sizeof(xfs_mount_t));
- sbp->sb_blocklog = (__uint8_t)blocklog;
- sbp->sb_sectlog = (__uint8_t)sectorlog;
- sbp->sb_agblklog = (__uint8_t)libxfs_log2_roundup((unsigned int)agsize);
- sbp->sb_agblocks = (xfs_agblock_t)agsize;
- mp->m_blkbb_log = sbp->sb_blocklog - BBSHIFT;
- mp->m_sectbb_log = sbp->sb_sectlog - BBSHIFT;
+ /* update whether extended features are in use */
+ if (sbp->sb_features2 != 0)
+ sbp->sb_versionnum |= XFS_SB_VERSION_MOREBITSBIT;
/*
- * sb_versionnum and finobt flags must be set before we use
- * xfs_prealloc_blocks().
+ * Due to a structure alignment issue, sb_features2 ended up in one
+ * of two locations, the second "incorrect" location represented by
+ * the sb_bad_features2 field. To avoid older kernels mounting
+ * filesystems they shouldn't, set both field to the same value.
*/
- sb_set_features(&mp->m_sb, &sb_feat, sectorsize, lsectorsize, dsunit);
+ sbp->sb_bad_features2 = sbp->sb_features2;
+
+ if (!fp->crcs_enabled)
+ return;
+ /* default features for v5 filesystems */
+ sbp->sb_features_compat = 0;
+ sbp->sb_features_ro_compat = 0;
+ sbp->sb_features_incompat = XFS_SB_FEAT_INCOMPAT_FTYPE;
+ sbp->sb_features_log_incompat = 0;
- if (loginternal) {
- /*
- * Readjust the log size to fit within an AG if it was sized
- * automatically.
- */
- if (!logsize) {
- logblocks = MIN(logblocks,
- XFS_ALLOC_AG_MAX_USABLE(mp));
+ if (fp->finobt)
+ sbp->sb_features_ro_compat = XFS_SB_FEAT_RO_COMPAT_FINOBT;
+ if (fp->rmapbt)
+ sbp->sb_features_ro_compat |= XFS_SB_FEAT_RO_COMPAT_RMAPBT;
+ if (fp->reflink)
+ sbp->sb_features_ro_compat |= XFS_SB_FEAT_RO_COMPAT_REFLINK;
- /* revalidate the log size is valid if we changed it */
- validate_log_size(logblocks, blocklog, min_logblocks);
- }
- if (logblocks > agsize - xfs_prealloc_blocks(mp)) {
+ /*
+ * Sparse inode chunk support has two main inode alignment requirements.
+ * First, sparse chunk alignment must match the cluster size. Second,
+ * full chunk alignment must match the inode chunk size.
+ *
+ * Copy the already calculated/scaled inoalignmt to spino_align and
+ * update the former to the full inode chunk size.
+ */
+ if (fp->spinodes) {
+ sbp->sb_spino_align = sbp->sb_inoalignmt;
+ sbp->sb_inoalignmt = XFS_INODES_PER_CHUNK *
+ cfg->inodesize >> cfg->blocklog;
+ sbp->sb_features_incompat |= XFS_SB_FEAT_INCOMPAT_SPINODES;
+ }
+
+}
+
+/*
+ * Make sure that the log size is a multiple of the stripe unit
+ */
+static void
+align_log_size(
+ struct mkfs_params *cfg,
+ int sunit)
+{
+ uint64_t tmp_logblocks;
+
+ /* nothing to do if it's already aligned. */
+ if ((cfg->logblocks % sunit) == 0)
+ return;
+
+ if (cli_opt_set(&lopts, L_SIZE)) {
+ fprintf(stderr,
+_("log size %lld is not a multiple of the log stripe unit %d\n"),
+ (long long) cfg->logblocks, sunit);
+ usage();
+ }
+
+ tmp_logblocks = ((cfg->logblocks + (sunit - 1)) / sunit) * sunit;
+
+ /* If the log is too large, round down instead of round up */
+ if ((tmp_logblocks > XFS_MAX_LOG_BLOCKS) ||
+ ((tmp_logblocks << cfg->blocklog) > XFS_MAX_LOG_BYTES)) {
+ tmp_logblocks = (cfg->logblocks / sunit) * sunit;
+ }
+ cfg->logblocks = tmp_logblocks;
+}
+
+/*
+ * Make sure that the internal log is correctly aligned to the specified
+ * stripe unit.
+ */
+static void
+align_internal_log(
+ struct mkfs_params *cfg,
+ struct xfs_mount *mp,
+ int sunit)
+{
+ /* round up log start if necessary */
+ if ((cfg->logstart % sunit) != 0)
+ cfg->logstart = ((cfg->logstart + (sunit - 1)) / sunit) * sunit;
+
+ /* round up/down the log size now */
+ align_log_size(cfg, sunit);
+
+ /* check the aligned log still fits in an AG. */
+ if (cfg->logblocks > cfg->agsize - XFS_FSB_TO_AGBNO(mp, cfg->logstart)) {
+ fprintf(stderr,
+_("Due to stripe alignment, the internal log size (%lld) is too large.\n"
+ "Must fit within an allocation group.\n"),
+ (long long) cfg->logblocks);
+ usage();
+ }
+}
+
+void
+validate_log_size(uint64_t logblocks, int blocklog, int min_logblocks)
+{
+ if (logblocks < min_logblocks) {
+ fprintf(stderr,
+ _("log size %lld blocks too small, minimum size is %d blocks\n"),
+ (long long)logblocks, min_logblocks);
+ usage();
+ }
+ if (logblocks > XFS_MAX_LOG_BLOCKS) {
+ fprintf(stderr,
+ _("log size %lld blocks too large, maximum size is %lld blocks\n"),
+ (long long)logblocks, XFS_MAX_LOG_BLOCKS);
+ usage();
+ }
+ if ((logblocks << blocklog) > XFS_MAX_LOG_BYTES) {
+ fprintf(stderr,
+ _("log size %lld bytes too large, maximum size is %lld bytes\n"),
+ (long long)(logblocks << blocklog), XFS_MAX_LOG_BYTES);
+ usage();
+ }
+}
+
+static void
+calculate_log_size(
+ struct mkfs_params *cfg,
+ struct cli_params *cli,
+ struct xfs_mount *mp)
+{
+ struct xfs_sb *sbp = &mp->m_sb;
+ int min_logblocks;
+ struct xfs_mount mount;
+
+ /* we need a temporary mount to calculate the minimum log size. */
+ memset(&mount, 0, sizeof(mount));
+ mount.m_sb = *sbp;
+ libxfs_mount(&mount, &mp->m_sb, 0, 0, 0, 0);
+ min_logblocks = libxfs_log_calc_minimum_size(&mount);
+ libxfs_umount(&mount);
+
+ ASSERT(min_logblocks);
+ min_logblocks = max(XFS_MIN_LOG_BLOCKS, min_logblocks);
+
+ /* if we have lots of blocks, check against XFS_MIN_LOG_BYTES, too */
+ if (!cli->logsize &&
+ cfg->dblocks >= (1024*1024*1024) >> cfg->blocklog)
+ min_logblocks = max(min_logblocks,
+ XFS_MIN_LOG_BYTES >> cfg->blocklog);
+
+ /*
+ * external logs will have a device and size by now, so all we have
+ * to do is validate it against minimum size and align it.
+ */
+ if (!cfg->loginternal) {
+ if (min_logblocks > cfg->logblocks) {
fprintf(stderr,
- _("internal log size %lld too large, must fit in allocation group\n"),
- (long long)logblocks);
+_("external log device %lld too small, must be at least %lld blocks\n"),
+ (long long)cfg->logblocks,
+ (long long)min_logblocks);
usage();
}
+ cfg->logstart = 0;
+ cfg->logagno = 0;
+ if (cfg->lsunit)
+ align_log_size(cfg, cfg->lsunit);
- if (laflag) {
- if (logagno >= agcount) {
- fprintf(stderr,
- _("log ag number %d too large, must be less than %lld\n"),
- logagno, (long long)agcount);
- usage();
- }
- } else
- logagno = (xfs_agnumber_t)(agcount / 2);
+ validate_log_size(cfg->logblocks, cfg->blocklog, min_logblocks);
+ return;
+ }
+
+ /* internal log - if no size specified, calculate automatically */
+ if (!cfg->logblocks) {
+ if (cfg->dblocks < GIGABYTES(1, cfg->blocklog)) {
+ /* tiny filesystems get minimum sized logs. */
+ cfg->logblocks = min_logblocks;
+ } else if (cfg->dblocks < GIGABYTES(16, cfg->blocklog)) {
+
+ /*
+ * For small filesystems, we want to use the
+ * XFS_MIN_LOG_BYTES for filesystems smaller than 16G if
+ * at all possible, ramping up to 128MB at 256GB.
+ */
+ cfg->logblocks = min(XFS_MIN_LOG_BYTES >> cfg->blocklog,
+ min_logblocks * XFS_DFL_LOG_FACTOR);
+ } else {
+ /*
+ * With a 2GB max log size, default to maximum size
+ * at 4TB. This keeps the same ratio from the older
+ * max log size of 128M at 256GB fs size. IOWs,
+ * the ratio of fs size to log size is 2048:1.
+ */
+ cfg->logblocks = (cfg->dblocks << cfg->blocklog) / 2048;
+ cfg->logblocks = cfg->logblocks >> cfg->blocklog;
+ }
+
+ /* Ensure the chosen size meets minimum log size requirements */
+ cfg->logblocks = max(min_logblocks, cfg->logblocks);
- logstart = XFS_AGB_TO_FSB(mp, logagno, xfs_prealloc_blocks(mp));
/*
- * Align the logstart at stripe unit boundary.
+ * Make sure the log fits wholly within an AG
+ *
+ * XXX: If agf->freeblks ends up as 0 because the log uses all
+ * the free space, it causes the kernel all sorts of problems
+ * with per-ag reservations. Right now just back it off one
+ * block, but there's a whole can of worms here that needs to be
+ * opened to decide what is the valid maximum size of a log in
+ * an AG.
*/
- if (lsunit) {
- logstart = fixup_internal_log_stripe(mp,
- lsflag, logstart, agsize, lsunit,
- &logblocks, blocklog, &lalign);
- } else if (dsunit) {
- logstart = fixup_internal_log_stripe(mp,
- lsflag, logstart, agsize, dsunit,
- &logblocks, blocklog, &lalign);
+ cfg->logblocks = min(cfg->logblocks,
+ libxfs_alloc_ag_max_usable(mp) - 1);
+
+ /* and now clamp the size to the maximum supported size */
+ cfg->logblocks = min(cfg->logblocks, XFS_MAX_LOG_BLOCKS);
+ if ((cfg->logblocks << cfg->blocklog) > XFS_MAX_LOG_BYTES)
+ cfg->logblocks = XFS_MAX_LOG_BYTES >> cfg->blocklog;
+
+ validate_log_size(cfg->logblocks, cfg->blocklog, min_logblocks);
+ }
+
+ if (cfg->logblocks > sbp->sb_agblocks - libxfs_prealloc_blocks(mp)) {
+ fprintf(stderr,
+_("internal log size %lld too large, must fit in allocation group\n"),
+ (long long)cfg->logblocks);
+ usage();
+ }
+
+ if (cli_opt_set(&lopts, L_AGNUM)) {
+ if (cli->logagno >= sbp->sb_agcount) {
+ fprintf(stderr,
+_("log ag number %lld too large, must be less than %lld\n"),
+ (long long)cli->logagno,
+ (long long)sbp->sb_agcount);
+ usage();
}
- } else {
- logstart = 0;
- if (lsunit)
- fixup_log_stripe_unit(lsflag, lsunit,
- &logblocks, blocklog);
- }
- validate_log_size(logblocks, blocklog, min_logblocks);
-
- if (!qflag || Nflag) {
- printf(_(
- "meta-data=%-22s isize=%-6d agcount=%lld, agsize=%lld blks\n"
- " =%-22s sectsz=%-5u attr=%u, projid32bit=%u\n"
- " =%-22s crc=%-8u finobt=%u, sparse=%u\n"
- "data =%-22s bsize=%-6u blocks=%llu, imaxpct=%u\n"
- " =%-22s sunit=%-6u swidth=%u blks\n"
- "naming =version %-14u bsize=%-6u ascii-ci=%d ftype=%d\n"
- "log =%-22s bsize=%-6d blocks=%lld, version=%d\n"
- " =%-22s sectsz=%-5u sunit=%d blks, lazy-count=%d\n"
- "realtime =%-22s extsz=%-6d blocks=%lld, rtextents=%lld\n"),
- dfile, isize, (long long)agcount, (long long)agsize,
- "", sectorsize, sb_feat.attr_version,
- !sb_feat.projid16bit,
- "", sb_feat.crcs_enabled, sb_feat.finobt, sb_feat.spinodes,
- "", blocksize, (long long)dblocks, imaxpct,
- "", dsunit, dswidth,
- sb_feat.dir_version, dirblocksize, sb_feat.nci,
- sb_feat.dirftype,
- logfile, 1 << blocklog, (long long)logblocks,
- sb_feat.log_version, "", lsectorsize, lsunit,
- sb_feat.lazy_sb_counters,
- rtfile, rtextblocks << blocklog,
- (long long)rtblocks, (long long)rtextents);
- if (Nflag)
- exit(0);
+ cfg->logagno = cli->logagno;
+ } else
+ cfg->logagno = (xfs_agnumber_t)(sbp->sb_agcount / 2);
+
+ cfg->logstart = XFS_AGB_TO_FSB(mp, cfg->logagno,
+ libxfs_prealloc_blocks(mp));
+
+ /*
+ * Align the logstart at stripe unit boundary.
+ */
+ if (cfg->lsunit) {
+ align_internal_log(cfg, mp, cfg->lsunit);
+ } else if (cfg->dsunit) {
+ align_internal_log(cfg, mp, cfg->dsunit);
}
+ validate_log_size(cfg->logblocks, cfg->blocklog, min_logblocks);
+}
- if (label)
- strncpy(sbp->sb_fname, label, sizeof(sbp->sb_fname));
+/*
+ * Set up superblock with the minimum parameters required for
+ * the libxfs macros needed by the log sizing code to run successfully.
+ * This includes a minimum log size calculation, so we need everything
+ * that goes into that calculation to be setup here including feature
+ * flags.
+ */
+static void
+start_superblock_setup(
+ struct mkfs_params *cfg,
+ struct xfs_mount *mp,
+ struct xfs_sb *sbp)
+{
sbp->sb_magicnum = XFS_SB_MAGIC;
- sbp->sb_blocksize = blocksize;
- sbp->sb_dblocks = dblocks;
- sbp->sb_rblocks = rtblocks;
- sbp->sb_rextents = rtextents;
- platform_uuid_copy(&sbp->sb_uuid, &uuid);
+ sbp->sb_sectsize = (uint16_t)cfg->sectorsize;
+ sbp->sb_sectlog = (uint8_t)cfg->sectorlog;
+ sbp->sb_blocksize = cfg->blocksize;
+ sbp->sb_blocklog = (uint8_t)cfg->blocklog;
+
+ sbp->sb_agblocks = (xfs_agblock_t)cfg->agsize;
+ sbp->sb_agblklog = (uint8_t)log2_roundup(cfg->agsize);
+ sbp->sb_agcount = (xfs_agnumber_t)cfg->agcount;
+
+ sbp->sb_inodesize = (uint16_t)cfg->inodesize;
+ sbp->sb_inodelog = (uint8_t)cfg->inodelog;
+ sbp->sb_inopblock = (uint16_t)(cfg->blocksize / cfg->inodesize);
+ sbp->sb_inopblog = (uint8_t)(cfg->blocklog - cfg->inodelog);
+
+ sbp->sb_dirblklog = cfg->dirblocklog - cfg->blocklog;
+
+ sb_set_features(cfg, sbp);
+
+ /*
+ * log stripe unit is stored in bytes on disk and cannot be zero
+ * for v2 logs.
+ */
+ if (cfg->sb_feat.log_version == 2) {
+ if (cfg->lsunit)
+ sbp->sb_logsunit = XFS_FSB_TO_B(mp, cfg->lsunit);
+ else
+ sbp->sb_logsunit = 1;
+ } else
+ sbp->sb_logsunit = 0;
+
+}
+
+static void
+initialise_mount(
+ struct mkfs_params *cfg,
+ struct xfs_mount *mp,
+ struct xfs_sb *sbp)
+{
+ /* Minimum needed for libxfs_prealloc_blocks() */
+ mp->m_blkbb_log = sbp->sb_blocklog - BBSHIFT;
+ mp->m_sectbb_log = sbp->sb_sectlog - BBSHIFT;
+}
+
+/*
+ * Format everything from the generated config into the superblock that
+ * will be used to initialise the on-disk superblock. This is the in-memory
+ * copy, so no need to care about endian swapping here.
+ */
+static void
+finish_superblock_setup(
+ struct mkfs_params *cfg,
+ struct xfs_mount *mp,
+ struct xfs_sb *sbp)
+{
+ if (cfg->label)
+ strncpy(sbp->sb_fname, cfg->label, sizeof(sbp->sb_fname));
+
+ sbp->sb_dblocks = cfg->dblocks;
+ sbp->sb_rblocks = cfg->rtblocks;
+ sbp->sb_rextents = cfg->rtextents;
+ platform_uuid_copy(&sbp->sb_uuid, &cfg->uuid);
/* Only in memory; libxfs expects this as if read from disk */
- platform_uuid_copy(&sbp->sb_meta_uuid, &uuid);
- sbp->sb_logstart = logstart;
+ platform_uuid_copy(&sbp->sb_meta_uuid, &cfg->uuid);
+ sbp->sb_logstart = cfg->logstart;
sbp->sb_rootino = sbp->sb_rbmino = sbp->sb_rsumino = NULLFSINO;
- sbp->sb_rextsize = rtextblocks;
- sbp->sb_agcount = (xfs_agnumber_t)agcount;
- sbp->sb_rbmblocks = nbmblocks;
- sbp->sb_logblocks = (xfs_extlen_t)logblocks;
- sbp->sb_sectsize = (__uint16_t)sectorsize;
- sbp->sb_inodesize = (__uint16_t)isize;
- sbp->sb_inopblock = (__uint16_t)(blocksize / isize);
- sbp->sb_sectlog = (__uint8_t)sectorlog;
- sbp->sb_inodelog = (__uint8_t)inodelog;
- sbp->sb_inopblog = (__uint8_t)(blocklog - inodelog);
- sbp->sb_rextslog =
- (__uint8_t)(rtextents ?
- libxfs_highbit32((unsigned int)rtextents) : 0);
+ sbp->sb_rextsize = cfg->rtextblocks;
+ sbp->sb_agcount = (xfs_agnumber_t)cfg->agcount;
+ sbp->sb_rbmblocks = cfg->rtbmblocks;
+ sbp->sb_logblocks = (xfs_extlen_t)cfg->logblocks;
+ sbp->sb_rextslog = (uint8_t)(cfg->rtextents ?
+ libxfs_highbit32((unsigned int)cfg->rtextents) : 0);
sbp->sb_inprogress = 1; /* mkfs is in progress */
- sbp->sb_imax_pct = imaxpct;
+ sbp->sb_imax_pct = cfg->imaxpct;
sbp->sb_icount = 0;
sbp->sb_ifree = 0;
- sbp->sb_fdblocks = dblocks - agcount * xfs_prealloc_blocks(mp) -
- (loginternal ? logblocks : 0);
+ sbp->sb_fdblocks = cfg->dblocks -
+ cfg->agcount * libxfs_prealloc_blocks(mp) -
+ (cfg->loginternal ? cfg->logblocks : 0);
sbp->sb_frextents = 0; /* will do a free later */
sbp->sb_uquotino = sbp->sb_gquotino = sbp->sb_pquotino = 0;
sbp->sb_qflags = 0;
- sbp->sb_unit = dsunit;
- sbp->sb_width = dswidth;
- sbp->sb_dirblklog = dirblocklog - blocklog;
- if (sb_feat.log_version == 2) { /* This is stored in bytes */
- lsunit = (lsunit == 0) ? 1 : XFS_FSB_TO_B(mp, lsunit);
- sbp->sb_logsunit = lsunit;
- } else
- sbp->sb_logsunit = 0;
- if (sb_feat.inode_align) {
- int cluster_size = XFS_INODE_BIG_CLUSTER_SIZE;
- if (sb_feat.crcs_enabled)
- cluster_size *= isize / XFS_DINODE_MIN_SIZE;
- sbp->sb_inoalignmt = cluster_size >> blocklog;
- sb_feat.inode_align = sbp->sb_inoalignmt != 0;
- } else
- sbp->sb_inoalignmt = 0;
- if (lsectorsize != BBSIZE || sectorsize != BBSIZE) {
- sbp->sb_logsectlog = (__uint8_t)lsectorlog;
- sbp->sb_logsectsize = (__uint16_t)lsectorsize;
- } else {
- sbp->sb_logsectlog = 0;
- sbp->sb_logsectsize = 0;
- }
+ sbp->sb_unit = cfg->dsunit;
+ sbp->sb_width = cfg->dswidth;
+
+}
+
+/*
+ * Sanitise the data and log devices and prepare them so libxfs can mount the
+ * device successfully. Also check we can access the rt device if configured.
+ */
+static void
+prepare_devices(
+ struct mkfs_params *cfg,
+ struct libxfs_xinit *xi,
+ struct xfs_mount *mp,
+ struct xfs_sb *sbp,
+ bool clear_stale)
+{
+ struct xfs_buf *buf;
+ int whack_blks = BTOBB(WHACK_SIZE);
+ int lsunit;
+
+ /*
+ * If there's an old XFS filesystem on the device with enough intact
+ * information that we can parse the superblock, there's enough
+ * information on disk to confuse a future xfs_repair call. To avoid
+ * this, whack all the old secondary superblocks that we can find.
+ */
+ if (clear_stale)
+ zero_old_xfs_structures(xi, sbp);
- sb_set_features(&mp->m_sb, &sb_feat, sectorsize, lsectorsize, dsunit);
+ /*
+ * If the data device is a file, grow out the file to its final size if
+ * needed so that the reads for the end of the device in the mount code
+ * will succeed.
+ */
+ if (xi->disfile &&
+ xi->dsize * xi->dbsize < cfg->dblocks * cfg->blocksize) {
+ if (ftruncate(xi->dfd, cfg->dblocks * cfg->blocksize) < 0) {
+ fprintf(stderr,
+ _("%s: Growing the data section failed\n"),
+ progname);
+ exit(1);
+ }
- if (force_overwrite)
- zero_old_xfs_structures(&xi, sbp);
+ /* update size to be able to whack blocks correctly */
+ xi->dsize = BTOBB(cfg->dblocks * cfg->blocksize);
+ }
/*
- * Zero out the beginning of the device, to obliterate any old
+ * Zero out the end to obliterate any old MD RAID (or other) metadata at
+ * the end of the device. (MD sb is ~64k from the end, take out a wider
+ * swath to be sure)
+ */
+ buf = libxfs_getbuf(mp->m_ddev_targp, (xi->dsize - whack_blks),
+ whack_blks);
+ memset(buf->b_addr, 0, WHACK_SIZE);
+ libxfs_writebuf(buf, LIBXFS_EXIT_ON_FAILURE);
+ libxfs_purgebuf(buf);
+
+ /*
+ * Now zero out the beginning of the device, to obliterate any old
* filesystem signatures out there. This should take care of
* swap (somewhere around the page size), jfs (32k),
* ext[2,3] and reiserfs (64k) - and hopefully all else.
*/
- libxfs_buftarg_init(mp, xi.ddev, xi.logdev, xi.rtdev);
- buf = libxfs_getbuf(mp->m_ddev_targp, 0, BTOBB(WHACK_SIZE));
- memset(XFS_BUF_PTR(buf), 0, WHACK_SIZE);
+ buf = libxfs_getbuf(mp->m_ddev_targp, 0, whack_blks);
+ memset(buf->b_addr, 0, WHACK_SIZE);
libxfs_writebuf(buf, LIBXFS_EXIT_ON_FAILURE);
libxfs_purgebuf(buf);
- /* OK, now write the superblock */
+ /* OK, now write the superblock... */
buf = libxfs_getbuf(mp->m_ddev_targp, XFS_SB_DADDR, XFS_FSS_TO_BB(mp, 1));
buf->b_ops = &xfs_sb_buf_ops;
- memset(XFS_BUF_PTR(buf), 0, sectorsize);
- libxfs_sb_to_disk((void *)XFS_BUF_PTR(buf), sbp);
+ memset(buf->b_addr, 0, cfg->sectorsize);
+ libxfs_sb_to_disk(buf->b_addr, sbp);
libxfs_writebuf(buf, LIBXFS_EXIT_ON_FAILURE);
libxfs_purgebuf(buf);
+ /* ...and zero the log.... */
+ lsunit = sbp->sb_logsunit;
+ if (lsunit == 1)
+ lsunit = sbp->sb_logsectsize;
+
+ libxfs_log_clear(mp->m_logdev_targp, NULL,
+ XFS_FSB_TO_DADDR(mp, cfg->logstart),
+ (xfs_extlen_t)XFS_FSB_TO_BB(mp, cfg->logblocks),
+ &sbp->sb_uuid, cfg->sb_feat.log_version,
+ lsunit, XLOG_FMT, XLOG_INIT_CYCLE, false);
+
+ /* finally, check we can write the last block in the realtime area */
+ if (mp->m_rtdev_targp->dev && cfg->rtblocks > 0) {
+ buf = libxfs_getbuf(mp->m_rtdev_targp,
+ XFS_FSB_TO_BB(mp, cfg->rtblocks - 1LL),
+ BTOBB(cfg->blocksize));
+ memset(buf->b_addr, 0, cfg->blocksize);
+ libxfs_writebuf(buf, LIBXFS_EXIT_ON_FAILURE);
+ libxfs_purgebuf(buf);
+ }
+
+}
+
+/*
+ * XXX: this code is mostly common with the kernel growfs code.
+ * These initialisations should be pulled into libxfs to keep the
+ * kernel/userspace header initialisation code the same.
+ */
+static void
+initialise_ag_headers(
+ struct mkfs_params *cfg,
+ struct xfs_mount *mp,
+ struct xfs_sb *sbp,
+ xfs_agnumber_t agno,
+ int *worst_freelist)
+{
+ struct xfs_perag *pag = libxfs_perag_get(mp, agno);
+ struct xfs_agfl *agfl;
+ struct xfs_agf *agf;
+ struct xfs_agi *agi;
+ struct xfs_buf *buf;
+ struct xfs_btree_block *block;
+ struct xfs_alloc_rec *arec;
+ struct xfs_alloc_rec *nrec;
+ int bucket;
+ uint64_t agsize = cfg->agsize;
+ xfs_agblock_t agblocks;
+ bool is_log_ag = false;
+ int c;
+
+ if (cfg->loginternal && agno == cfg->logagno)
+ is_log_ag = true;
+
/*
- * If the data area is a file, then grow it out to its final size
- * if needed so that the reads for the end of the device in the mount
- * code will succeed.
+ * Superblock.
*/
- if (xi.disfile && xi.dsize * xi.dbsize < dblocks * blocksize) {
- if (ftruncate64(xi.dfd, dblocks * blocksize) < 0) {
- fprintf(stderr,
- _("%s: Growing the data section failed\n"),
- progname);
- exit(1);
+ buf = libxfs_getbuf(mp->m_ddev_targp,
+ XFS_AG_DADDR(mp, agno, XFS_SB_DADDR),
+ XFS_FSS_TO_BB(mp, 1));
+ buf->b_ops = &xfs_sb_buf_ops;
+ memset(buf->b_addr, 0, cfg->sectorsize);
+ libxfs_sb_to_disk(buf->b_addr, sbp);
+ libxfs_writebuf(buf, LIBXFS_EXIT_ON_FAILURE);
+
+ /*
+ * AG header block: freespace
+ */
+ buf = libxfs_getbuf(mp->m_ddev_targp,
+ XFS_AG_DADDR(mp, agno, XFS_AGF_DADDR(mp)),
+ XFS_FSS_TO_BB(mp, 1));
+ buf->b_ops = &xfs_agf_buf_ops;
+ agf = XFS_BUF_TO_AGF(buf);
+ memset(agf, 0, cfg->sectorsize);
+ if (agno == cfg->agcount - 1)
+ agsize = cfg->dblocks - (xfs_rfsblock_t)(agno * agsize);
+ agf->agf_magicnum = cpu_to_be32(XFS_AGF_MAGIC);
+ agf->agf_versionnum = cpu_to_be32(XFS_AGF_VERSION);
+ agf->agf_seqno = cpu_to_be32(agno);
+ agf->agf_length = cpu_to_be32(agsize);
+ agf->agf_roots[XFS_BTNUM_BNOi] = cpu_to_be32(XFS_BNO_BLOCK(mp));
+ agf->agf_roots[XFS_BTNUM_CNTi] = cpu_to_be32(XFS_CNT_BLOCK(mp));
+ agf->agf_levels[XFS_BTNUM_BNOi] = cpu_to_be32(1);
+ agf->agf_levels[XFS_BTNUM_CNTi] = cpu_to_be32(1);
+ pag->pagf_levels[XFS_BTNUM_BNOi] = 1;
+ pag->pagf_levels[XFS_BTNUM_CNTi] = 1;
+
+ if (xfs_sb_version_hasrmapbt(sbp)) {
+ agf->agf_roots[XFS_BTNUM_RMAPi] = cpu_to_be32(XFS_RMAP_BLOCK(mp));
+ agf->agf_levels[XFS_BTNUM_RMAPi] = cpu_to_be32(1);
+ agf->agf_rmap_blocks = cpu_to_be32(1);
+ }
+
+ if (xfs_sb_version_hasreflink(sbp)) {
+ agf->agf_refcount_root = cpu_to_be32(libxfs_refc_block(mp));
+ agf->agf_refcount_level = cpu_to_be32(1);
+ agf->agf_refcount_blocks = cpu_to_be32(1);
+ }
+
+ agf->agf_flfirst = 0;
+ agf->agf_fllast = cpu_to_be32(libxfs_agfl_size(mp) - 1);
+ agf->agf_flcount = 0;
+ agblocks = (xfs_agblock_t)(agsize - libxfs_prealloc_blocks(mp));
+ agf->agf_freeblks = cpu_to_be32(agblocks);
+ agf->agf_longest = cpu_to_be32(agblocks);
+
+ if (xfs_sb_version_hascrc(sbp))
+ platform_uuid_copy(&agf->agf_uuid, &sbp->sb_uuid);
+
+ if (is_log_ag) {
+ be32_add_cpu(&agf->agf_freeblks, -(int64_t)cfg->logblocks);
+ agf->agf_longest = cpu_to_be32(agsize -
+ XFS_FSB_TO_AGBNO(mp, cfg->logstart) - cfg->logblocks);
+ }
+ if (libxfs_alloc_min_freelist(mp, pag) > *worst_freelist)
+ *worst_freelist = libxfs_alloc_min_freelist(mp, pag);
+ libxfs_writebuf(buf, LIBXFS_EXIT_ON_FAILURE);
+
+ /*
+ * AG freelist header block
+ */
+ buf = libxfs_getbuf(mp->m_ddev_targp,
+ XFS_AG_DADDR(mp, agno, XFS_AGFL_DADDR(mp)),
+ XFS_FSS_TO_BB(mp, 1));
+ buf->b_ops = &xfs_agfl_buf_ops;
+ agfl = XFS_BUF_TO_AGFL(buf);
+ /* setting to 0xff results in initialisation to NULLAGBLOCK */
+ memset(agfl, 0xff, cfg->sectorsize);
+ if (xfs_sb_version_hascrc(sbp)) {
+ agfl->agfl_magicnum = cpu_to_be32(XFS_AGFL_MAGIC);
+ agfl->agfl_seqno = cpu_to_be32(agno);
+ platform_uuid_copy(&agfl->agfl_uuid, &sbp->sb_uuid);
+ for (bucket = 0; bucket < libxfs_agfl_size(mp); bucket++)
+ agfl->agfl_bno[bucket] = cpu_to_be32(NULLAGBLOCK);
+ }
+
+ libxfs_writebuf(buf, LIBXFS_EXIT_ON_FAILURE);
+
+ /*
+ * AG header block: inodes
+ */
+ buf = libxfs_getbuf(mp->m_ddev_targp,
+ XFS_AG_DADDR(mp, agno, XFS_AGI_DADDR(mp)),
+ XFS_FSS_TO_BB(mp, 1));
+ agi = XFS_BUF_TO_AGI(buf);
+ buf->b_ops = &xfs_agi_buf_ops;
+ memset(agi, 0, cfg->sectorsize);
+ agi->agi_magicnum = cpu_to_be32(XFS_AGI_MAGIC);
+ agi->agi_versionnum = cpu_to_be32(XFS_AGI_VERSION);
+ agi->agi_seqno = cpu_to_be32(agno);
+ agi->agi_length = cpu_to_be32(agsize);
+ agi->agi_count = 0;
+ agi->agi_root = cpu_to_be32(XFS_IBT_BLOCK(mp));
+ agi->agi_level = cpu_to_be32(1);
+ if (xfs_sb_version_hasfinobt(sbp)) {
+ agi->agi_free_root = cpu_to_be32(XFS_FIBT_BLOCK(mp));
+ agi->agi_free_level = cpu_to_be32(1);
+ }
+ agi->agi_freecount = 0;
+ agi->agi_newino = cpu_to_be32(NULLAGINO);
+ agi->agi_dirino = cpu_to_be32(NULLAGINO);
+ if (xfs_sb_version_hascrc(sbp))
+ platform_uuid_copy(&agi->agi_uuid, &sbp->sb_uuid);
+ for (c = 0; c < XFS_AGI_UNLINKED_BUCKETS; c++)
+ agi->agi_unlinked[c] = cpu_to_be32(NULLAGINO);
+ libxfs_writebuf(buf, LIBXFS_EXIT_ON_FAILURE);
+
+ /*
+ * BNO btree root block
+ */
+ buf = libxfs_getbuf(mp->m_ddev_targp,
+ XFS_AGB_TO_DADDR(mp, agno, XFS_BNO_BLOCK(mp)),
+ BTOBB(cfg->blocksize));
+ buf->b_ops = &xfs_allocbt_buf_ops;
+ block = XFS_BUF_TO_BLOCK(buf);
+ memset(block, 0, cfg->blocksize);
+ libxfs_btree_init_block(mp, buf, XFS_BTNUM_BNO, 0, 1, agno, 0);
+
+ arec = XFS_ALLOC_REC_ADDR(mp, block, 1);
+ arec->ar_startblock = cpu_to_be32(libxfs_prealloc_blocks(mp));
+ if (is_log_ag) {
+ xfs_agblock_t start = XFS_FSB_TO_AGBNO(mp, cfg->logstart);
+
+ ASSERT(start >= libxfs_prealloc_blocks(mp));
+ if (start != libxfs_prealloc_blocks(mp)) {
+ /*
+ * Modify first record to pad stripe align of log
+ */
+ arec->ar_blockcount = cpu_to_be32(start -
+ libxfs_prealloc_blocks(mp));
+ nrec = arec + 1;
+ /*
+ * Insert second record at start of internal log
+ * which then gets trimmed.
+ */
+ nrec->ar_startblock = cpu_to_be32(
+ be32_to_cpu(arec->ar_startblock) +
+ be32_to_cpu(arec->ar_blockcount));
+ arec = nrec;
+ be16_add_cpu(&block->bb_numrecs, 1);
+ }
+ /*
+ * Change record start to after the internal log
+ */
+ be32_add_cpu(&arec->ar_startblock, cfg->logblocks);
+ }
+ /*
+ * Calculate the record block count and check for the case where
+ * the log might have consumed all available space in the AG. If
+ * so, reset the record count to 0 to avoid exposure of an invalid
+ * record start block.
+ */
+ arec->ar_blockcount = cpu_to_be32(agsize -
+ be32_to_cpu(arec->ar_startblock));
+ if (!arec->ar_blockcount)
+ block->bb_numrecs = 0;
+
+ libxfs_writebuf(buf, LIBXFS_EXIT_ON_FAILURE);
+
+ /*
+ * CNT btree root block
+ */
+ buf = libxfs_getbuf(mp->m_ddev_targp,
+ XFS_AGB_TO_DADDR(mp, agno, XFS_CNT_BLOCK(mp)),
+ BTOBB(cfg->blocksize));
+ buf->b_ops = &xfs_allocbt_buf_ops;
+ block = XFS_BUF_TO_BLOCK(buf);
+ memset(block, 0, cfg->blocksize);
+ libxfs_btree_init_block(mp, buf, XFS_BTNUM_CNT, 0, 1, agno, 0);
+
+ arec = XFS_ALLOC_REC_ADDR(mp, block, 1);
+ arec->ar_startblock = cpu_to_be32(libxfs_prealloc_blocks(mp));
+ if (is_log_ag) {
+ xfs_agblock_t start = XFS_FSB_TO_AGBNO(mp, cfg->logstart);
+
+ ASSERT(start >= libxfs_prealloc_blocks(mp));
+ if (start != libxfs_prealloc_blocks(mp)) {
+ arec->ar_blockcount = cpu_to_be32(start -
+ libxfs_prealloc_blocks(mp));
+ nrec = arec + 1;
+ nrec->ar_startblock = cpu_to_be32(
+ be32_to_cpu(arec->ar_startblock) +
+ be32_to_cpu(arec->ar_blockcount));
+ arec = nrec;
+ be16_add_cpu(&block->bb_numrecs, 1);
+ }
+ be32_add_cpu(&arec->ar_startblock, cfg->logblocks);
+ }
+ /*
+ * Calculate the record block count and check for the case where
+ * the log might have consumed all available space in the AG. If
+ * so, reset the record count to 0 to avoid exposure of an invalid
+ * record start block.
+ */
+ arec->ar_blockcount = cpu_to_be32(agsize -
+ be32_to_cpu(arec->ar_startblock));
+ if (!arec->ar_blockcount)
+ block->bb_numrecs = 0;
+
+ libxfs_writebuf(buf, LIBXFS_EXIT_ON_FAILURE);
+
+ /*
+ * refcount btree root block
+ */
+ if (xfs_sb_version_hasreflink(sbp)) {
+ buf = libxfs_getbuf(mp->m_ddev_targp,
+ XFS_AGB_TO_DADDR(mp, agno, libxfs_refc_block(mp)),
+ BTOBB(cfg->blocksize));
+ buf->b_ops = &xfs_refcountbt_buf_ops;
+
+ block = XFS_BUF_TO_BLOCK(buf);
+ memset(block, 0, cfg->blocksize);
+ libxfs_btree_init_block(mp, buf, XFS_BTNUM_REFC, 0, 0, agno, 0);
+ libxfs_writebuf(buf, LIBXFS_EXIT_ON_FAILURE);
+ }
+
+ /*
+ * INO btree root block
+ */
+ buf = libxfs_getbuf(mp->m_ddev_targp,
+ XFS_AGB_TO_DADDR(mp, agno, XFS_IBT_BLOCK(mp)),
+ BTOBB(cfg->blocksize));
+ buf->b_ops = &xfs_inobt_buf_ops;
+ block = XFS_BUF_TO_BLOCK(buf);
+ memset(block, 0, cfg->blocksize);
+ libxfs_btree_init_block(mp, buf, XFS_BTNUM_INO, 0, 0, agno, 0);
+ libxfs_writebuf(buf, LIBXFS_EXIT_ON_FAILURE);
+
+ /*
+ * Free INO btree root block
+ */
+ if (xfs_sb_version_hasfinobt(sbp)) {
+ buf = libxfs_getbuf(mp->m_ddev_targp,
+ XFS_AGB_TO_DADDR(mp, agno, XFS_FIBT_BLOCK(mp)),
+ BTOBB(cfg->blocksize));
+ buf->b_ops = &xfs_inobt_buf_ops;
+ block = XFS_BUF_TO_BLOCK(buf);
+ memset(block, 0, cfg->blocksize);
+ libxfs_btree_init_block(mp, buf, XFS_BTNUM_FINO, 0, 0, agno, 0);
+ libxfs_writebuf(buf, LIBXFS_EXIT_ON_FAILURE);
+ }
+
+ /* RMAP btree root block */
+ if (xfs_sb_version_hasrmapbt(sbp)) {
+ struct xfs_rmap_rec *rrec;
+
+ buf = libxfs_getbuf(mp->m_ddev_targp,
+ XFS_AGB_TO_DADDR(mp, agno, XFS_RMAP_BLOCK(mp)),
+ BTOBB(cfg->blocksize));
+ buf->b_ops = &xfs_rmapbt_buf_ops;
+ block = XFS_BUF_TO_BLOCK(buf);
+ memset(block, 0, cfg->blocksize);
+
+ libxfs_btree_init_block(mp, buf, XFS_BTNUM_RMAP, 0, 0, agno, 0);
+
+ /*
+ * mark the AG header regions as static metadata
+ * The BNO btree block is the first block after the
+ * headers, so it's location defines the size of region
+ * the static metadata consumes.
+ */
+ rrec = XFS_RMAP_REC_ADDR(block, 1);
+ rrec->rm_startblock = 0;
+ rrec->rm_blockcount = cpu_to_be32(XFS_BNO_BLOCK(mp));
+ rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_FS);
+ rrec->rm_offset = 0;
+ be16_add_cpu(&block->bb_numrecs, 1);
+
+ /* account freespace btree root blocks */
+ rrec = XFS_RMAP_REC_ADDR(block, 2);
+ rrec->rm_startblock = cpu_to_be32(XFS_BNO_BLOCK(mp));
+ rrec->rm_blockcount = cpu_to_be32(2);
+ rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_AG);
+ rrec->rm_offset = 0;
+ be16_add_cpu(&block->bb_numrecs, 1);
+
+ /* account inode btree root blocks */
+ rrec = XFS_RMAP_REC_ADDR(block, 3);
+ rrec->rm_startblock = cpu_to_be32(XFS_IBT_BLOCK(mp));
+ rrec->rm_blockcount = cpu_to_be32(XFS_RMAP_BLOCK(mp) -
+ XFS_IBT_BLOCK(mp));
+ rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_INOBT);
+ rrec->rm_offset = 0;
+ be16_add_cpu(&block->bb_numrecs, 1);
+
+ /* account for rmap btree root */
+ rrec = XFS_RMAP_REC_ADDR(block, 4);
+ rrec->rm_startblock = cpu_to_be32(XFS_RMAP_BLOCK(mp));
+ rrec->rm_blockcount = cpu_to_be32(1);
+ rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_AG);
+ rrec->rm_offset = 0;
+ be16_add_cpu(&block->bb_numrecs, 1);
+
+ /* account for refcount btree root */
+ if (xfs_sb_version_hasreflink(sbp)) {
+ rrec = XFS_RMAP_REC_ADDR(block, 5);
+ rrec->rm_startblock = cpu_to_be32(libxfs_refc_block(mp));
+ rrec->rm_blockcount = cpu_to_be32(1);
+ rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_REFC);
+ rrec->rm_offset = 0;
+ be16_add_cpu(&block->bb_numrecs, 1);
+ }
+
+ /* account for the log space */
+ if (is_log_ag) {
+ rrec = XFS_RMAP_REC_ADDR(block,
+ be16_to_cpu(block->bb_numrecs) + 1);
+ rrec->rm_startblock = cpu_to_be32(
+ XFS_FSB_TO_AGBNO(mp, cfg->logstart));
+ rrec->rm_blockcount = cpu_to_be32(cfg->logblocks);
+ rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_LOG);
+ rrec->rm_offset = 0;
+ be16_add_cpu(&block->bb_numrecs, 1);
+ }
+
+ libxfs_writebuf(buf, LIBXFS_EXIT_ON_FAILURE);
+ }
+
+ libxfs_perag_put(pag);
+}
+
+static void
+initialise_ag_freespace(
+ struct xfs_mount *mp,
+ xfs_agnumber_t agno,
+ int worst_freelist)
+{
+ struct xfs_alloc_arg args;
+ struct xfs_trans *tp;
+ struct xfs_trans_res tres = {0};
+ int c;
+
+ c = -libxfs_trans_alloc(mp, &tres, worst_freelist, 0, 0, &tp);
+ if (c)
+ res_failed(c);
+
+ memset(&args, 0, sizeof(args));
+ args.tp = tp;
+ args.mp = mp;
+ args.agno = agno;
+ args.alignment = 1;
+ args.pag = libxfs_perag_get(mp, agno);
+
+ libxfs_alloc_fix_freelist(&args, 0);
+ libxfs_perag_put(args.pag);
+ libxfs_trans_commit(tp);
+}
+
+/*
+ * rewrite several secondary superblocks with the root inode number filled out.
+ * This can help repair recovery from a trashed primary superblock without
+ * losing the root inode.
+ */
+static void
+rewrite_secondary_superblocks(
+ struct xfs_mount *mp)
+{
+ struct xfs_buf *buf;
+
+ /* rewrite the last superblock */
+ buf = libxfs_readbuf(mp->m_dev,
+ XFS_AGB_TO_DADDR(mp, mp->m_sb.sb_agcount - 1,
+ XFS_SB_DADDR),
+ XFS_FSS_TO_BB(mp, 1),
+ LIBXFS_EXIT_ON_FAILURE, &xfs_sb_buf_ops);
+ XFS_BUF_TO_SBP(buf)->sb_rootino = cpu_to_be64(mp->m_sb.sb_rootino);
+ libxfs_writebuf(buf, LIBXFS_EXIT_ON_FAILURE);
+
+ /* and one in the middle for luck if there's enough AGs for that */
+ if (mp->m_sb.sb_agcount <= 2)
+ return;
+
+ buf = libxfs_readbuf(mp->m_dev,
+ XFS_AGB_TO_DADDR(mp, (mp->m_sb.sb_agcount - 1) / 2,
+ XFS_SB_DADDR),
+ XFS_FSS_TO_BB(mp, 1),
+ LIBXFS_EXIT_ON_FAILURE, &xfs_sb_buf_ops);
+ XFS_BUF_TO_SBP(buf)->sb_rootino = cpu_to_be64(mp->m_sb.sb_rootino);
+ libxfs_writebuf(buf, LIBXFS_EXIT_ON_FAILURE);
+}
+
+int
+main(
+ int argc,
+ char **argv)
+{
+ xfs_agnumber_t agno;
+ xfs_buf_t *buf;
+ int c;
+ char *dfile = NULL;
+ char *logfile = NULL;
+ char *rtfile = NULL;
+ int dry_run = 0;
+ int discard = 1;
+ int force_overwrite = 0;
+ int quiet = 0;
+ char *protofile = NULL;
+ char *protostring = NULL;
+ int worst_freelist = 0;
+
+ struct libxfs_xinit xi = {
+ .isdirect = LIBXFS_DIRECT,
+ .isreadonly = LIBXFS_EXCLUSIVELY,
+ };
+ struct xfs_mount mbuf = {};
+ struct xfs_mount *mp = &mbuf;
+ struct xfs_sb *sbp = &mp->m_sb;
+ struct fs_topology ft = {};
+ struct cli_params cli = {
+ .xi = &xi,
+ .loginternal = 1,
+ };
+ struct mkfs_params cfg = {};
+
+ /* build time defaults */
+ struct mkfs_default_params dft = {
+ .source = _("package build definitions"),
+ .sectorsize = XFS_MIN_SECTORSIZE,
+ .blocksize = 1 << XFS_DFL_BLOCKSIZE_LOG,
+ .sb_feat = {
+ .log_version = 2,
+ .attr_version = 2,
+ .dir_version = 2,
+ .inode_align = true,
+ .nci = false,
+ .lazy_sb_counters = true,
+ .projid32bit = true,
+ .crcs_enabled = true,
+ .dirftype = true,
+ .finobt = true,
+ .spinodes = true,
+ .rmapbt = false,
+ .reflink = false,
+ .parent_pointers = false,
+ .nodalign = false,
+ .nortalign = false,
+ },
+ };
+
+ platform_uuid_generate(&cli.uuid);
+ progname = basename(argv[0]);
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+
+ /*
+ * TODO: Sourcing defaults from a config file
+ *
+ * Before anything else, see if there's a config file with different
+ * defaults. If a file exists in <package location>, read in the new
+ * default values and overwrite them in the &dft structure. This way the
+ * new defaults will apply before we parse the CLI, and the CLI will
+ * still be able to override them. When more than one source is
+ * implemented, emit a message to indicate where the defaults being
+ * used came from.
+ *
+ * printf(_("Default configuration sourced from %s\n"), dft.source);
+ */
+
+ /* copy new defaults into CLI parsing structure */
+ memcpy(&cli.sb_feat, &dft.sb_feat, sizeof(cli.sb_feat));
+ memcpy(&cli.fsx, &dft.fsx, sizeof(cli.fsx));
+
+ 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, &cli);
+ break;
+ case 'L':
+ if (strlen(optarg) > sizeof(sbp->sb_fname))
+ illegal(optarg, "L");
+ cfg.label = optarg;
+ break;
+ case 'N':
+ dry_run = 1;
+ break;
+ case 'K':
+ discard = 0;
+ break;
+ case 'p':
+ if (protofile)
+ respec('p', NULL, 0);
+ protofile = optarg;
+ break;
+ case 'q':
+ quiet = 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;
+
+ protostring = setup_proto(protofile);
/*
- * Zero out the end of the device, to obliterate any
- * old MD RAID (or other) metadata at the end of the device.
- * (MD sb is ~64k from the end, take out a wider swath to be sure)
+ * Extract as much of the valid config as we can from the CLI input
+ * before opening the libxfs devices.
*/
- if (!xi.disfile) {
- buf = libxfs_getbuf(mp->m_ddev_targp,
- (xi.dsize - BTOBB(WHACK_SIZE)),
- BTOBB(WHACK_SIZE));
- memset(XFS_BUF_PTR(buf), 0, WHACK_SIZE);
- libxfs_writebuf(buf, LIBXFS_EXIT_ON_FAILURE);
- libxfs_purgebuf(buf);
- }
+ validate_blocksize(&cfg, &cli, &dft);
+ validate_sectorsize(&cfg, &cli, &dft, &ft, dfile, dry_run,
+ force_overwrite);
/*
- * Zero the log....
+ * XXX: we still need to set block size and sector size global variables
+ * so that getnum/cvtnum works correctly
*/
- libxfs_log_clear(mp->m_logdev_targp, NULL,
- XFS_FSB_TO_DADDR(mp, logstart),
- (xfs_extlen_t)XFS_FSB_TO_BB(mp, logblocks),
- &sbp->sb_uuid, sb_feat.log_version, lsunit, XLOG_FMT, XLOG_INIT_CYCLE, false);
+ blocksize = cfg.blocksize;
+ sectorsize = cfg.sectorsize;
- mp = libxfs_mount(mp, sbp, xi.ddev, xi.logdev, xi.rtdev, 0);
- if (mp == NULL) {
- fprintf(stderr, _("%s: filesystem failed to initialize\n"),
- progname);
- exit(1);
- }
+ validate_log_sectorsize(&cfg, &cli, &dft);
+ validate_sb_features(&cfg, &cli);
/*
- * XXX: this code is effectively shared with the kernel growfs code.
- * These initialisations should be pulled into libxfs to keep the
- * kernel/userspace header initialisation code the same.
+ * we've now completed basic validation of the features, sector and
+ * block sizes, so from this point onwards we use the values found in
+ * the cfg structure for them, not the command line structure.
*/
- for (agno = 0; agno < agcount; agno++) {
- struct xfs_agfl *agfl;
- int bucket;
- struct xfs_perag *pag = xfs_perag_get(mp, agno);
+ validate_dirblocksize(&cfg, &cli);
+ validate_inodesize(&cfg, &cli);
- /*
- * Superblock.
- */
- buf = libxfs_getbuf(mp->m_ddev_targp,
- XFS_AG_DADDR(mp, agno, XFS_SB_DADDR),
- XFS_FSS_TO_BB(mp, 1));
- buf->b_ops = &xfs_sb_buf_ops;
- memset(XFS_BUF_PTR(buf), 0, sectorsize);
- libxfs_sb_to_disk((void *)XFS_BUF_PTR(buf), sbp);
- libxfs_writebuf(buf, LIBXFS_EXIT_ON_FAILURE);
-
- /*
- * AG header block: freespace
- */
- buf = libxfs_getbuf(mp->m_ddev_targp,
- XFS_AG_DADDR(mp, agno, XFS_AGF_DADDR(mp)),
- XFS_FSS_TO_BB(mp, 1));
- buf->b_ops = &xfs_agf_buf_ops;
- agf = XFS_BUF_TO_AGF(buf);
- memset(agf, 0, sectorsize);
- if (agno == agcount - 1)
- agsize = dblocks - (xfs_rfsblock_t)(agno * agsize);
- agf->agf_magicnum = cpu_to_be32(XFS_AGF_MAGIC);
- agf->agf_versionnum = cpu_to_be32(XFS_AGF_VERSION);
- agf->agf_seqno = cpu_to_be32(agno);
- agf->agf_length = cpu_to_be32(agsize);
- agf->agf_roots[XFS_BTNUM_BNOi] = cpu_to_be32(XFS_BNO_BLOCK(mp));
- agf->agf_roots[XFS_BTNUM_CNTi] = cpu_to_be32(XFS_CNT_BLOCK(mp));
- agf->agf_levels[XFS_BTNUM_BNOi] = cpu_to_be32(1);
- agf->agf_levels[XFS_BTNUM_CNTi] = cpu_to_be32(1);
- pag->pagf_levels[XFS_BTNUM_BNOi] = 1;
- pag->pagf_levels[XFS_BTNUM_CNTi] = 1;
- agf->agf_flfirst = 0;
- agf->agf_fllast = cpu_to_be32(XFS_AGFL_SIZE(mp) - 1);
- agf->agf_flcount = 0;
- nbmblocks = (xfs_extlen_t)(agsize - xfs_prealloc_blocks(mp));
- agf->agf_freeblks = cpu_to_be32(nbmblocks);
- agf->agf_longest = cpu_to_be32(nbmblocks);
- if (xfs_sb_version_hascrc(&mp->m_sb))
- platform_uuid_copy(&agf->agf_uuid, &mp->m_sb.sb_uuid);
-
- if (loginternal && agno == logagno) {
- be32_add_cpu(&agf->agf_freeblks, -logblocks);
- agf->agf_longest = cpu_to_be32(agsize -
- XFS_FSB_TO_AGBNO(mp, logstart) - logblocks);
- }
- if (xfs_alloc_min_freelist(mp, pag) > worst_freelist)
- worst_freelist = xfs_alloc_min_freelist(mp, pag);
- libxfs_writebuf(buf, LIBXFS_EXIT_ON_FAILURE);
+ /*
+ * 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.
+ */
+ cfg.dblocks = calc_dev_size(cli.dsize, &cfg, &dopts, D_SIZE, "data");
+ cfg.logblocks = calc_dev_size(cli.logsize, &cfg, &lopts, L_SIZE, "log");
+ cfg.rtblocks = calc_dev_size(cli.rtsize, &cfg, &ropts, R_SIZE, "rt");
- /*
- * AG freelist header block
- */
- buf = libxfs_getbuf(mp->m_ddev_targp,
- XFS_AG_DADDR(mp, agno, XFS_AGFL_DADDR(mp)),
- XFS_FSS_TO_BB(mp, 1));
- buf->b_ops = &xfs_agfl_buf_ops;
- agfl = XFS_BUF_TO_AGFL(buf);
- /* setting to 0xff results in initialisation to NULLAGBLOCK */
- memset(agfl, 0xff, sectorsize);
- if (xfs_sb_version_hascrc(&mp->m_sb)) {
- agfl->agfl_magicnum = cpu_to_be32(XFS_AGFL_MAGIC);
- agfl->agfl_seqno = cpu_to_be32(agno);
- platform_uuid_copy(&agfl->agfl_uuid, &mp->m_sb.sb_uuid);
- for (bucket = 0; bucket < XFS_AGFL_SIZE(mp); bucket++)
- agfl->agfl_bno[bucket] = cpu_to_be32(NULLAGBLOCK);
- }
+ validate_rtextsize(&cfg, &cli, &ft);
+ calc_stripe_factors(&cfg, &cli, &ft);
- libxfs_writebuf(buf, LIBXFS_EXIT_ON_FAILURE);
+ /*
+ * Open and validate the device configurations
+ */
+ open_devices(&cfg, &xi, (discard && !dry_run));
+ validate_datadev(&cfg, &cli);
+ validate_logdev(&cfg, &cli, &logfile);
+ validate_rtdev(&cfg, &cli, &rtfile);
- /*
- * AG header block: inodes
- */
- buf = libxfs_getbuf(mp->m_ddev_targp,
- XFS_AG_DADDR(mp, agno, XFS_AGI_DADDR(mp)),
- XFS_FSS_TO_BB(mp, 1));
- agi = XFS_BUF_TO_AGI(buf);
- buf->b_ops = &xfs_agi_buf_ops;
- memset(agi, 0, sectorsize);
- agi->agi_magicnum = cpu_to_be32(XFS_AGI_MAGIC);
- agi->agi_versionnum = cpu_to_be32(XFS_AGI_VERSION);
- agi->agi_seqno = cpu_to_be32(agno);
- agi->agi_length = cpu_to_be32((xfs_agblock_t)agsize);
- agi->agi_count = 0;
- agi->agi_root = cpu_to_be32(XFS_IBT_BLOCK(mp));
- agi->agi_level = cpu_to_be32(1);
- if (sb_feat.finobt) {
- agi->agi_free_root = cpu_to_be32(XFS_FIBT_BLOCK(mp));
- agi->agi_free_level = cpu_to_be32(1);
- }
- agi->agi_freecount = 0;
- agi->agi_newino = cpu_to_be32(NULLAGINO);
- agi->agi_dirino = cpu_to_be32(NULLAGINO);
- if (xfs_sb_version_hascrc(&mp->m_sb))
- platform_uuid_copy(&agi->agi_uuid, &mp->m_sb.sb_uuid);
- for (c = 0; c < XFS_AGI_UNLINKED_BUCKETS; c++)
- agi->agi_unlinked[c] = cpu_to_be32(NULLAGINO);
- libxfs_writebuf(buf, LIBXFS_EXIT_ON_FAILURE);
+ /*
+ * At this point when know exactly what size all the devices are,
+ * so we can start validating and calculating layout options that are
+ * dependent on device sizes. Once calculated, make sure everything
+ * aligns to device geometry correctly.
+ */
+ calculate_initial_ag_geometry(&cfg, &cli);
+ align_ag_geometry(&cfg);
- /*
- * BNO btree root block
- */
- buf = libxfs_getbuf(mp->m_ddev_targp,
- XFS_AGB_TO_DADDR(mp, agno, XFS_BNO_BLOCK(mp)),
- bsize);
- buf->b_ops = &xfs_allocbt_buf_ops;
- block = XFS_BUF_TO_BLOCK(buf);
- memset(block, 0, blocksize);
- if (xfs_sb_version_hascrc(&mp->m_sb))
- xfs_btree_init_block(mp, buf, XFS_ABTB_CRC_MAGIC, 0, 1,
- agno, XFS_BTREE_CRC_BLOCKS);
- else
- xfs_btree_init_block(mp, buf, XFS_ABTB_MAGIC, 0, 1,
- agno, 0);
-
- arec = XFS_ALLOC_REC_ADDR(mp, block, 1);
- arec->ar_startblock = cpu_to_be32(xfs_prealloc_blocks(mp));
- if (loginternal && agno == logagno) {
- if (lalign) {
- /*
- * Have to insert two records
- * Insert pad record for stripe align of log
- */
- arec->ar_blockcount = cpu_to_be32(
- XFS_FSB_TO_AGBNO(mp, logstart) -
- be32_to_cpu(arec->ar_startblock));
- nrec = arec + 1;
- /*
- * Insert record at start of internal log
- */
- nrec->ar_startblock = cpu_to_be32(
- be32_to_cpu(arec->ar_startblock) +
- be32_to_cpu(arec->ar_blockcount));
- arec = nrec;
- be16_add_cpu(&block->bb_numrecs, 1);
- }
- /*
- * Change record start to after the internal log
- */
- be32_add_cpu(&arec->ar_startblock, logblocks);
- }
- /*
- * Calculate the record block count and check for the case where
- * the log might have consumed all available space in the AG. If
- * so, reset the record count to 0 to avoid exposure of an invalid
- * record start block.
- */
- arec->ar_blockcount = cpu_to_be32(agsize -
- be32_to_cpu(arec->ar_startblock));
- if (!arec->ar_blockcount)
- block->bb_numrecs = 0;
+ calculate_imaxpct(&cfg, &cli);
- libxfs_writebuf(buf, LIBXFS_EXIT_ON_FAILURE);
+ /*
+ * Set up the basic superblock parameters now so that we can use
+ * the geometry information we've already validated in libxfs
+ * provided functions to determine on-disk format information.
+ */
+ start_superblock_setup(&cfg, mp, sbp);
+ initialise_mount(&cfg, mp, sbp);
- /*
- * CNT btree root block
- */
- buf = libxfs_getbuf(mp->m_ddev_targp,
- XFS_AGB_TO_DADDR(mp, agno, XFS_CNT_BLOCK(mp)),
- bsize);
- buf->b_ops = &xfs_allocbt_buf_ops;
- block = XFS_BUF_TO_BLOCK(buf);
- memset(block, 0, blocksize);
- if (xfs_sb_version_hascrc(&mp->m_sb))
- xfs_btree_init_block(mp, buf, XFS_ABTC_CRC_MAGIC, 0, 1,
- agno, XFS_BTREE_CRC_BLOCKS);
- else
- xfs_btree_init_block(mp, buf, XFS_ABTC_MAGIC, 0, 1,
- agno, 0);
-
- arec = XFS_ALLOC_REC_ADDR(mp, block, 1);
- arec->ar_startblock = cpu_to_be32(xfs_prealloc_blocks(mp));
- if (loginternal && agno == logagno) {
- if (lalign) {
- arec->ar_blockcount = cpu_to_be32(
- XFS_FSB_TO_AGBNO(mp, logstart) -
- be32_to_cpu(arec->ar_startblock));
- nrec = arec + 1;
- nrec->ar_startblock = cpu_to_be32(
- be32_to_cpu(arec->ar_startblock) +
- be32_to_cpu(arec->ar_blockcount));
- arec = nrec;
- be16_add_cpu(&block->bb_numrecs, 1);
- }
- be32_add_cpu(&arec->ar_startblock, logblocks);
- }
- /*
- * Calculate the record block count and check for the case where
- * the log might have consumed all available space in the AG. If
- * so, reset the record count to 0 to avoid exposure of an invalid
- * record start block.
- */
- arec->ar_blockcount = cpu_to_be32(agsize -
- be32_to_cpu(arec->ar_startblock));
- if (!arec->ar_blockcount)
- block->bb_numrecs = 0;
+ /*
+ * With the mount set up, we can finally calculate the log size
+ * constraints and do default size calculations and final validation
+ */
+ calculate_log_size(&cfg, &cli, mp);
- libxfs_writebuf(buf, LIBXFS_EXIT_ON_FAILURE);
+ finish_superblock_setup(&cfg, mp, sbp);
- /*
- * INO btree root block
- */
- buf = libxfs_getbuf(mp->m_ddev_targp,
- XFS_AGB_TO_DADDR(mp, agno, XFS_IBT_BLOCK(mp)),
- bsize);
- buf->b_ops = &xfs_inobt_buf_ops;
- block = XFS_BUF_TO_BLOCK(buf);
- memset(block, 0, blocksize);
- if (xfs_sb_version_hascrc(&mp->m_sb))
- xfs_btree_init_block(mp, buf, XFS_IBT_CRC_MAGIC, 0, 0,
- agno, XFS_BTREE_CRC_BLOCKS);
- else
- xfs_btree_init_block(mp, buf, XFS_IBT_MAGIC, 0, 0,
- agno, 0);
- libxfs_writebuf(buf, LIBXFS_EXIT_ON_FAILURE);
+ /* Print the intended geometry of the fs. */
+ if (!quiet || dry_run) {
+ struct xfs_fsop_geom geo;
+ int error;
- /*
- * Free INO btree root block
- */
- if (!sb_feat.finobt) {
- xfs_perag_put(pag);
- continue;
+ error = -libxfs_fs_geometry(sbp, &geo,
+ XFS_FS_GEOM_MAX_STRUCT_VER);
+ if (error) {
+ fprintf(stderr,
+ _("%s: failed to generate filesystem geometry\n"),
+ progname);
+ exit(1);
}
- buf = libxfs_getbuf(mp->m_ddev_targp,
- XFS_AGB_TO_DADDR(mp, agno, XFS_FIBT_BLOCK(mp)),
- bsize);
- buf->b_ops = &xfs_inobt_buf_ops;
- block = XFS_BUF_TO_BLOCK(buf);
- memset(block, 0, blocksize);
- if (xfs_sb_version_hascrc(&mp->m_sb))
- xfs_btree_init_block(mp, buf, XFS_FIBT_CRC_MAGIC, 0, 0,
- agno, XFS_BTREE_CRC_BLOCKS);
- else
- xfs_btree_init_block(mp, buf, XFS_FIBT_MAGIC, 0, 0,
- agno, 0);
- libxfs_writebuf(buf, LIBXFS_EXIT_ON_FAILURE);
- xfs_perag_put(pag);
+ xfs_report_geom(&geo, dfile, logfile, rtfile);
+ if (dry_run)
+ exit(0);
}
/*
- * Touch last block, make fs the right size if it's a file.
+ * we need the libxfs buffer cache from here on in.
*/
- buf = libxfs_getbuf(mp->m_ddev_targp,
- (xfs_daddr_t)XFS_FSB_TO_BB(mp, dblocks - 1LL), bsize);
- memset(XFS_BUF_PTR(buf), 0, blocksize);
- libxfs_writebuf(buf, LIBXFS_EXIT_ON_FAILURE);
+ libxfs_buftarg_init(mp, xi.ddev, xi.logdev, xi.rtdev);
/*
- * Make sure we can write the last block in the realtime area.
+ * Before we mount the filesystem we need to make sure the devices have
+ * enough of the filesystem structure on them that allows libxfs to
+ * mount.
*/
- if (mp->m_rtdev_targp->dev && rtblocks > 0) {
- buf = libxfs_getbuf(mp->m_rtdev_targp,
- XFS_FSB_TO_BB(mp, rtblocks - 1LL), bsize);
- memset(XFS_BUF_PTR(buf), 0, blocksize);
- libxfs_writebuf(buf, LIBXFS_EXIT_ON_FAILURE);
+ prepare_devices(&cfg, &xi, mp, sbp, force_overwrite);
+ mp = libxfs_mount(mp, sbp, xi.ddev, xi.logdev, xi.rtdev, 0);
+ if (mp == NULL) {
+ fprintf(stderr, _("%s: filesystem failed to initialize\n"),
+ progname);
+ exit(1);
}
/*
- * BNO, CNT free block list
+ * Initialise all the static on disk metadata.
*/
- for (agno = 0; agno < agcount; agno++) {
- xfs_alloc_arg_t args;
- xfs_trans_t *tp;
- struct xfs_trans_res tres = {0};
-
- c = libxfs_trans_alloc(mp, &tres, worst_freelist, 0, 0, &tp);
- if (c)
- res_failed(c);
+ for (agno = 0; agno < cfg.agcount; agno++)
+ initialise_ag_headers(&cfg, mp, sbp, agno, &worst_freelist);
- memset(&args, 0, sizeof(args));
- args.tp = tp;
- args.mp = mp;
- args.agno = agno;
- args.alignment = 1;
- args.pag = xfs_perag_get(mp,agno);
-
- libxfs_alloc_fix_freelist(&args, 0);
- xfs_perag_put(args.pag);
- libxfs_trans_commit(tp);
- }
+ /*
+ * Initialise the freespace freelists (i.e. AGFLs) in each AG.
+ */
+ for (agno = 0; agno < cfg.agcount; agno++)
+ initialise_ag_freespace(mp, agno, worst_freelist);
/*
* Allocate the root inode and anything else in the proto file.
*/
- parse_proto(mp, &fsx, &protostring);
+ parse_proto(mp, &cli.fsx, &protostring);
/*
* Protect ourselves against possible stupidity
}
/*
- * Write out multiple secondary superblocks with rootinode field set
+ * Re-write multiple secondary superblocks with rootinode field set
*/
- if (mp->m_sb.sb_agcount > 1) {
- /*
- * the last superblock
- */
- buf = libxfs_readbuf(mp->m_dev,
- XFS_AGB_TO_DADDR(mp, mp->m_sb.sb_agcount-1,
- XFS_SB_DADDR),
- XFS_FSS_TO_BB(mp, 1),
- LIBXFS_EXIT_ON_FAILURE, &xfs_sb_buf_ops);
- XFS_BUF_TO_SBP(buf)->sb_rootino = cpu_to_be64(
- mp->m_sb.sb_rootino);
- libxfs_writebuf(buf, LIBXFS_EXIT_ON_FAILURE);
- /*
- * and one in the middle for luck
- */
- if (mp->m_sb.sb_agcount > 2) {
- buf = libxfs_readbuf(mp->m_dev,
- XFS_AGB_TO_DADDR(mp, (mp->m_sb.sb_agcount-1)/2,
- XFS_SB_DADDR),
- XFS_FSS_TO_BB(mp, 1),
- LIBXFS_EXIT_ON_FAILURE, &xfs_sb_buf_ops);
- XFS_BUF_TO_SBP(buf)->sb_rootino = cpu_to_be64(
- mp->m_sb.sb_rootino);
- libxfs_writebuf(buf, LIBXFS_EXIT_ON_FAILURE);
- }
- }
+ if (mp->m_sb.sb_agcount > 1)
+ rewrite_secondary_superblocks(mp);
/*
* Dump all inodes and buffers before marking us all done.
if (xi.logdev && xi.logdev != xi.ddev)
libxfs_device_close(xi.logdev);
libxfs_device_close(xi.ddev);
+ libxfs_destroy();
return 0;
}
-
-static void
-conflict(
- char opt,
- char *tab[],
- int oldidx,
- int newidx)
-{
- fprintf(stderr, _("Cannot specify both -%c %s and -%c %s\n"),
- opt, tab[oldidx], opt, tab[newidx]);
- usage();
-}
-
-
-static void
-illegal(
- const char *value,
- const char *opt)
-{
- fprintf(stderr, _("Illegal 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,
- char *tab[],
- int idx)
-{
- fprintf(stderr, _("-%c %s option requires a value\n"), opt, tab[idx]);
- usage();
-}
-
-static void
-respec(
- char opt,
- 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 __attribute__((noreturn))
-usage( void )
-{
- fprintf(stderr, _("Usage: %s\n\
-/* blocksize */ [-b log=n|size=num]\n\
-/* metadata */ [-m crc=0|1,finobt=0|1,uuid=xxx]\n\
-/* data subvol */ [-d agcount=n,agsize=n,file,name=xxx,size=num,\n\
- (sunit=value,swidth=value|su=num,sw=num|noalign),\n\
- sectlog=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,sectlog=n|sectsize=num,\n\
- lazy-count=0|1]\n\
-/* label */ [-L label (maximum 12 characters)]\n\
-/* naming */ [-n log=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 log=n|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);
-}