/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
/*
* University Copyright- Copyright (c) 1982, 1986, 1988
* The Regents of the University of California
* All Rights Reserved
*
* University Acknowledgment- Portions of this document are derived from
* software developed by the University of California, Berkeley, and its
* contributors.
*/
/*
* The maximum supported file system size (in sectors) is the
* number of frags that can be represented in an int32_t field
* (INT_MAX) times the maximum number of sectors per frag. Since
* the maximum frag size is MAXBSIZE, the maximum number of sectors
* per frag is MAXBSIZE/DEV_BSIZE.
*/
#define FS_MAX (((diskaddr_t)INT_MAX) * (MAXBSIZE/DEV_BSIZE))
/*
* make file system for cylinder-group style file systems
*
* usage:
*
* mkfs [-F FSType] [-V] [-G [-P]] [-M dirname] [-m] [options]
* [-o specific_options] special size
* [nsect ntrack bsize fsize cpg minfree rps nbpi opt apc rotdelay
* 2 3 4 5 6 7 8 9 10 11 12
* nrpos maxcontig mtb]
* 13 14 15
*
* where specific_options are:
* N - no create
* nsect - The number of sectors per track
* ntrack - The number of tracks per cylinder
* bsize - block size
* fragsize - fragment size
* cgsize - The number of disk cylinders per cylinder group.
* free - minimum free space
* rps - rotational speed (rev/sec).
* nbpi - number of data bytes per allocated inode
* opt - optimization (space, time)
* apc - number of alternates
* gap - gap size
* nrpos - number of rotational positions
* maxcontig - maximum number of logical blocks that will be
* allocated contiguously before inserting rotational delay
* mtb - if "y", set up file system for eventual growth to over a
* a terabyte
* -P Do not grow the file system, but print on stdout the maximal
* size in sectors to which the file system can be increased. The calculated
* size is limited by the value provided by the operand size.
*
* Note that -P is a project-private interface and together with -G intended
* to be used only by the growfs script. It is therefore purposely not
* documented in the man page.
* The -P option is covered by PSARC case 2003/422.
*/
/*
* The following constants set the defaults used for the number
* of sectors/track (fs_nsect), and number of tracks/cyl (fs_ntrak).
*
* NSECT NTRAK
* 72MB CDC 18 9
* 30MB CDC 18 5
* 720KB Diskette 9 2
*
* However the defaults will be different for disks larger than CHSLIMIT.
*/
#define DFLNSECT 32
#define DFLNTRAK 16
/*
* The following default sectors and tracks values are used for
* non-efi disks that are larger than the CHS addressing limit. The
* existing default cpg of 16 (DESCPG) holds good for larger disks too.
*/
#define DEF_SECTORS_EFI 128
#define DEF_TRACKS_EFI 48
/*
* The maximum number of cylinders in a group depends upon how much
* information can be stored on a single cylinder. The default is to
* use 16 cylinders per group. This is effectively tradition - it was
* the largest value acceptable under SunOs 4.1
*/
#define DESCPG 16 /* desired fs_cpg */
/*
* The following two constants set the default block and fragment sizes.
* Both constants must be a power of 2 and meet the following constraints:
* MINBSIZE <= DESBLKSIZE <= MAXBSIZE
* DEV_BSIZE <= DESFRAGSIZE <= DESBLKSIZE
* DESBLKSIZE / DESFRAGSIZE <= 8
*/
#define DESBLKSIZE 8192
#define DESFRAGSIZE 1024
/*
* MINFREE gives the minimum acceptable percentage of file system
* blocks which may be free. If the freelist drops below this level
* only the superuser may continue to allocate blocks. This may
* be set to 0 if no reserve of free blocks is deemed necessary,
* however throughput drops by fifty percent if the file system
* is run at between 90% and 100% full; thus the default value of
* fs_minfree is 10%. With 10% free space, fragmentation is not a
* problem, so we choose to optimize for time.
*/
#define MINFREE 10
#define DEFAULTOPT FS_OPTTIME
/*
* ROTDELAY gives the minimum number of milliseconds to initiate
* another disk transfer on the same cylinder. It is no longer used
* and will always default to 0.
*/
#define ROTDELAY 0
/*
* MAXBLKPG determines the maximum number of data blocks which are
* placed in a single cylinder group. The default is one indirect
* block worth of data blocks.
*/
#define MAXBLKPG(bsize) ((bsize) / sizeof (daddr32_t))
/*
* Each file system has a number of inodes statically allocated.
* We allocate one inode slot per NBPI bytes, expecting this
* to be far more than we will ever need.
*/
#define NBPI 2048 /* Number Bytes Per Inode */
#define MTB_NBPI (MB) /* Number Bytes Per Inode for multi-terabyte */
/*
* Disks are assumed to rotate at 60HZ, unless otherwise specified.
*/
#define DEFHZ 60
/*
* Cylinder group related limits.
*
* For each cylinder we keep track of the availability of blocks at different
* rotational positions, so that we can lay out the data to be picked
* up with minimum rotational latency. NRPOS is the number of rotational
* positions which we distinguish. With NRPOS 8 the resolution of our
* summary information is 2ms for a typical 3600 rpm drive.
*/
#define NRPOS 8 /* number distinct rotational positions */
#ifdef DEBUG
#define dprintf(x) printf x
#else
#define dprintf(x)
#endif
/*
* For the -N option, when calculating the backup superblocks, do not print
* them if we are not really sure. We may have to try an alternate method of
* arriving at the superblocks. So defer printing till a handful of superblocks
* look good.
*/
#define tprintf(x) if (Nflag && retry) \
(void) strncat(tmpbuf, x, strlen(x)); \
else \
(void) fprintf(stderr, x);
#define ALTSB 32 /* Location of first backup superblock */
/*
* range_check "user_supplied" flag values.
*/
#define RC_DEFAULT 0
#define RC_KEYWORD 1
#define RC_POSITIONAL 2
/*
* ufs hole
*/
#define UFS_HOLE -1
#ifndef STANDALONE
#include <stdio.h>
#include <sys/mnttab.h>
#endif
#include <stdlib.h>
#include <unistd.h>
#include <malloc.h>
#include <string.h>
#include <strings.h>
#include <ctype.h>
#include <errno.h>
#include <sys/param.h>
#include <time.h>
#include <sys/types.h>
#include <sys/sysmacros.h>
#include <sys/vnode.h>
#include <sys/fs/ufs_fsdir.h>
#include <sys/fs/ufs_inode.h>
#include <sys/fs/ufs_fs.h>
#include <sys/fs/ufs_log.h>
#include <sys/mntent.h>
#include <sys/filio.h>
#include <limits.h>
#include <sys/int_const.h>
#include <signal.h>
#include <sys/efi_partition.h>
#include "roll_log.h"
#define bcopy(f, t, n) (void) memcpy(t, f, n)
#define bzero(s, n) (void) memset(s, 0, n)
#define bcmp(s, d, n) memcmp(s, d, n)
#define index(s, r) strchr(s, r)
#define rindex(s, r) strrchr(s, r)
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <locale.h>
#include <fcntl.h>
#include <sys/isa_defs.h> /* for ENDIAN defines */
#include <sys/vtoc.h>
#include <sys/dkio.h>
#include <sys/asynch.h>
extern offset_t llseek();
extern char *getfullblkname();
extern long lrand48();
extern int optind;
extern char *optarg;
/*
* The size of a cylinder group is calculated by CGSIZE. The maximum size
* is limited by the fact that cylinder groups are at most one block.
* Its size is derived from the size of the maps maintained in the
* cylinder group and the (struct cg) size.
*/
#define CGSIZE(fs) \
/* base cg */ (sizeof (struct cg) + \
/* blktot size */ (fs)->fs_cpg * sizeof (long) + \
/* blks size */ (fs)->fs_cpg * (fs)->fs_nrpos * sizeof (short) + \
/* inode map */ howmany((fs)->fs_ipg, NBBY) + \
/* block map */ howmany((fs)->fs_cpg * (fs)->fs_spc / NSPF(fs), NBBY))
/*
* We limit the size of the inode map to be no more than a
* third of the cylinder group space, since we must leave at
* least an equal amount of space for the block map.
*
* N.B.: MAXIpG must be a multiple of INOPB(fs).
*/
#define MAXIpG(fs) roundup((fs)->fs_bsize * NBBY / 3, INOPB(fs))
/*
* Same as MAXIpG, but parameterized by the block size (b) and the
* cylinder group divisor (d), which is the reciprocal of the fraction of the
* cylinder group overhead block that is used for the inode map. So for
* example, if d = 5, the macro's computation assumes that 1/5 of the
* cylinder group overhead block can be dedicated to the inode map.
*/
#define MAXIpG_B(b, d) roundup((b) * NBBY / (d), (b) / sizeof (struct dinode))
#define UMASK 0755
#define MAXINOPB (MAXBSIZE / sizeof (struct dinode))
#define POWEROF2(num) (((num) & ((num) - 1)) == 0)
#define MB (1024*1024)
#define BETWEEN(x, l, h) ((x) >= (l) && (x) <= (h))
/*
* Used to set the inode generation number. Since both inodes and dinodes
* are dealt with, we really need a pointer to an icommon here.
*/
#define IRANDOMIZE(icp) (icp)->ic_gen = lrand48();
/*
* Flags for number()
*/
#define ALLOW_PERCENT 0x01 /* allow trailing `%' on number */
#define ALLOW_MS1 0x02 /* allow trailing `ms', state 1 */
#define ALLOW_MS2 0x04 /* allow trailing `ms', state 2 */
#define ALLOW_END_ONLY 0x08 /* must be at end of number & suffixes */
#define MAXAIO 1000 /* maximum number of outstanding I/O's we'll manage */
#define BLOCK 1 /* block in aiowait */
#define NOBLOCK 0 /* don't block in aiowait */
#define RELEASE 1 /* free an aio buffer after use */
#define SAVE 0 /* don't free the buffer */
typedef struct aio_trans {
aio_result_t resultbuf;
diskaddr_t bno;
char *buffer;
int size;
int release;
struct aio_trans *next;
} aio_trans;
typedef struct aio_results {
int max;
int outstanding;
int maxpend;
aio_trans *trans;
} aio_results;
int aio_inited = 0;
aio_results results;
/*
* Allow up to MAXBUF aio requests that each have a unique buffer.
* More aio's might be done, but not using memory through the getbuf()
* interface. This can be raised, but you run into the potential of
* using more memory than is physically available on the machine,
* and if you start swapping, you can forget about performance.
* To prevent this, we also limit the total memory used for a given
* type of buffer to MAXBUFMEM.
*
* Tests indicate a cylinder group's worth of inodes takes:
*
* NBPI Size of Inode Buffer
* 2k 1688k
* 8k 424k
*
* initcg() stores all the inodes for a cylinder group in one buffer,
* so allowing 20 buffers could take 32 MB if not limited by MAXBUFMEM.
*/
#define MAXBUF 20
#define MAXBUFMEM (8 * 1024 * 1024)
/*
* header information for buffers managed by getbuf() and freebuf()
*/
typedef struct bufhdr {
struct bufhdr *head;
struct bufhdr *next;
} bufhdr;
int bufhdrsize;
bufhdr inodebuf = { NULL, NULL };
bufhdr cgsumbuf = { NULL, NULL };
#define SECTORS_PER_TERABYTE (1LL << 31)
/*
* The following constant specifies an upper limit for file system size
* that is actually a lot bigger than we expect to support with UFS. (Since
* it's specified in sectors, the file system size would be 2**44 * 512,
* which is 2**53, which is 8192 Terabytes.) However, it's useful
* for checking the basic sanity of a size value that is input on the
* command line.
*/
#define FS_SIZE_UPPER_LIMIT 0x100000000000LL
/*
* Forward declarations
*/
static char *getbuf(bufhdr *bufhead, int size);
static void freebuf(char *buf);
static void freetrans(aio_trans *transp);
static aio_trans *get_aiop();
static aio_trans *wait_for_write(int block);
static void initcg(int cylno);
static void fsinit();
static int makedir(struct direct *protodir, int entries);
static void iput(struct inode *ip);
static void rdfs(diskaddr_t bno, int size, char *bf);
static void wtfs(diskaddr_t bno, int size, char *bf);
static void awtfs(diskaddr_t bno, int size, char *bf, int release);
static void wtfs_breakup(diskaddr_t bno, int size, char *bf);
static int isblock(struct fs *fs, unsigned char *cp, int h);
static void clrblock(struct fs *fs, unsigned char *cp, int h);
static void setblock(struct fs *fs, unsigned char *cp, int h);
static void usage();
static void dump_fscmd(char *fsys, int fsi);
static uint64_t number(uint64_t d_value, char *param, int flags);
static int match(char *s);
static char checkopt(char *optim);
static char checkmtb(char *mtbarg);
static void range_check(long *varp, char *name, long minimum,
long maximum, long def_val, int user_supplied);
static void range_check_64(uint64_t *varp, char *name, uint64_t minimum,
uint64_t maximum, uint64_t def_val, int user_supplied);
static daddr32_t alloc(int size, int mode);
static diskaddr_t get_max_size(int fd);
static long get_max_track_size(int fd);
static void block_sigint(sigset_t *old_mask);
static void unblock_sigint(sigset_t *old_mask);
static void recover_from_sigint(int signum);
static int confirm_abort(void);
static int getaline(FILE *fp, char *loc, int maxlen);
static void flush_writes(void);
static long compute_maxcpg(long, long, long, long, long);
static int in_64bit_mode(void);
static int validate_size(int fd, diskaddr_t size);
static void dump_sblock(void);
/*
* Workaround for mkfs to function properly on disks attached to XMIT 2.X
* controller. If the address is not aligned at 8 byte boundary, mkfs on
* disks attached to XMIT 2.X controller exhibts un-predictable behaviour.
*/
#define XMIT_2_X_ALIGN 8
#pragma align XMIT_2_X_ALIGN(fsun, altfsun, cgun)
union {
struct fs fs;
char pad[SBSIZE];
} fsun, altfsun;
#define sblock fsun.fs
#define altsblock altfsun.fs
struct csum *fscs;
union cgun {
struct cg cg;
char pad[MAXBSIZE];
} cgun;
#define acg cgun.cg
/*
* Size of screen in cols in which to fit output
*/
#define WIDTH 80
struct dinode zino[MAXBSIZE / sizeof (struct dinode)];
/*
* file descriptors used for rdfs(fsi) and wtfs(fso).
* Initialized to an illegal file descriptor number.
*/
int fsi = -1;
int fso = -1;
/*
* The BIG parameter is machine dependent. It should be a longlong integer
* constant that can be used by the number parser to check the validity
* of numeric parameters.
*/
#define BIG 0x7fffffffffffffffLL
/* Used to indicate to number() that a bogus value should cause us to exit */
#define NO_DEFAULT LONG_MIN
/*
* INVALIDSBLIMIT is the number of bad backup superblocks that will be
* tolerated before we decide to try arriving at a different set of them
* using a different logic. This is applicable for non-EFI disks only.
*/
#define INVALIDSBLIMIT 10
/*
* The *_flag variables are used to indicate that the user specified
* the values, rather than that we made them up ourselves. We can
* complain about the user giving us bogus values.
*/
/* semi-constants */
long sectorsize = DEV_BSIZE; /* bytes/sector from param.h */
long bbsize = BBSIZE; /* boot block size */
long sbsize = SBSIZE; /* superblock size */
/* parameters */
diskaddr_t fssize_db; /* file system size in disk blocks */
diskaddr_t fssize_frag; /* file system size in frags */
long cpg; /* cylinders/cylinder group */
int cpg_flag = RC_DEFAULT;
long rotdelay = -1; /* rotational delay between blocks */
int rotdelay_flag = RC_DEFAULT;
long maxcontig; /* max contiguous blocks to allocate */
int maxcontig_flag = RC_DEFAULT;
long nsect = DFLNSECT; /* sectors per track */
int nsect_flag = RC_DEFAULT;
long ntrack = DFLNTRAK; /* tracks per cylinder group */
int ntrack_flag = RC_DEFAULT;
long bsize = DESBLKSIZE; /* filesystem block size */
int bsize_flag = RC_DEFAULT;
long fragsize = DESFRAGSIZE; /* filesystem fragment size */
int fragsize_flag = RC_DEFAULT;
long minfree = MINFREE; /* fs_minfree */
int minfree_flag = RC_DEFAULT;
long rps = DEFHZ; /* revolutions/second of drive */
int rps_flag = RC_DEFAULT;
long nbpi = NBPI; /* number of bytes per inode */
int nbpi_flag = RC_DEFAULT;
long nrpos = NRPOS; /* number of rotational positions */
int nrpos_flag = RC_DEFAULT;
long apc = 0; /* alternate sectors per cylinder */
int apc_flag = RC_DEFAULT;
char opt = 't'; /* optimization style, `t' or `s' */
char mtb = 'n'; /* multi-terabyte format, 'y' or 'n' */
#define DEFAULT_SECT_TRAK_CPG (nsect_flag == RC_DEFAULT && \
ntrack_flag == RC_DEFAULT && \
cpg_flag == RC_DEFAULT)
long debug = 0; /* enable debugging output */
int spc_flag = 0; /* alternate sectors specified or */
/* found */
/* global state */
int Nflag; /* do not write to disk */
int mflag; /* return the command line used to create this FS */
int rflag; /* report the superblock in an easily-parsed form */
int Rflag; /* dump the superblock in binary */
char *fsys;
time_t mkfstime;
char *string;
int label_type;
/*
* logging support
*/
int ismdd; /* true if device is a SVM device */
int islog; /* true if ufs or SVM logging is enabled */
int islogok; /* true if ufs/SVM log state is good */
static int isufslog; /* true if ufs logging is enabled */
static int waslog; /* true when ufs logging disabled during grow */
/*
* growfs defines, globals, and forward references
*/
#define NOTENOUGHSPACE 33
int grow;
#define GROW_WITH_DEFAULT_TRAK (grow && ntrack_flag == RC_DEFAULT)
static int Pflag; /* probe to which size the fs can be grown */
int ismounted;
char *directory;
diskaddr_t grow_fssize;
long grow_fs_size;
long grow_fs_ncg;
diskaddr_t grow_fs_csaddr;
long grow_fs_cssize;
int grow_fs_clean;
struct csum *grow_fscs;
diskaddr_t grow_sifrag;
int test;
int testforce;
diskaddr_t testfrags;
int inlockexit;
int isbad;
void lockexit(int);
void randomgeneration(void);
void checksummarysize(void);
int checksblock(struct fs, int);
void growinit(char *);
void checkdev(char *, char *);
void checkmount(struct mnttab *, char *);
struct dinode *gdinode(ino_t);
int csfraginrange(daddr32_t);
struct csfrag *findcsfrag(daddr32_t, struct csfrag **);
void checkindirect(ino_t, daddr32_t *, daddr32_t, int);
void addcsfrag(ino_t, daddr32_t, struct csfrag **);
void delcsfrag(daddr32_t, struct csfrag **);
void checkdirect(ino_t, daddr32_t *, daddr32_t *, int);
void findcsfragino(void);
void fixindirect(daddr32_t, int);
void fixdirect(caddr_t, daddr32_t, daddr32_t *, int);
void fixcsfragino(void);
void extendsummaryinfo(void);
int notenoughspace(void);
void unalloccsfragino(void);
void unalloccsfragfree(void);
void findcsfragfree(void);
void copycsfragino(void);
void rdcg(long);
void wtcg(void);
void flcg(void);
void allocfrags(long, daddr32_t *, long *);
void alloccsfragino(void);
void alloccsfragfree(void);
void freefrags(daddr32_t, long, long);
int findfreerange(long *, long *);
void resetallocinfo(void);
void extendcg(long);
void ulockfs(void);
void wlockfs(void);
void clockfs(void);
void wtsb(void);
static int64_t checkfragallocated(daddr32_t);
static struct csum *read_summaryinfo(struct fs *);
static diskaddr_t probe_summaryinfo();
int
main(int argc, char *argv[])
{
long i, mincpc, mincpg, ibpcl;
long cylno, rpos, blk, j, warn = 0;
long mincpgcnt, maxcpg;
uint64_t used, bpcg, inospercg;
long mapcramped, inodecramped;
long postblsize, rotblsize, totalsbsize;
FILE *mnttab;
struct mnttab mntp;
char *special;
struct statvfs64 fs;
struct dk_geom dkg;
struct dk_cinfo dkcinfo;
struct dk_minfo dkminfo;
char pbuf[sizeof (uint64_t) * 3 + 1];
char *tmpbuf;
int width, plen;
uint64_t num;
int c, saverr;
diskaddr_t max_fssize;
long tmpmaxcontig = -1;
struct sigaction sigact;
uint64_t nbytes64;
int remaining_cg;
int do_dot = 0;
int use_efi_dflts = 0, retry = 0, isremovable = 0, ishotpluggable = 0;
int invalid_sb_cnt, ret, skip_this_sb, cg_too_small;
int geom_nsect, geom_ntrack, geom_cpg;
(void) setlocale(LC_ALL, "");
#if !defined(TEXT_DOMAIN)
#define TEXT_DOMAIN "SYS_TEST"
#endif
(void) textdomain(TEXT_DOMAIN);
while ((c = getopt(argc, argv, "F:bmo:VPGM:T:t:")) != EOF) {
switch (c) {
case 'F':
string = optarg;
if (strcmp(string, "ufs") != 0)
usage();
break;
case 'm': /* return command line used to create this FS */
mflag++;
break;
case 'o':
/*
* ufs specific options.
*/
string = optarg;
while (*string != '\0') {
if (match("nsect=")) {
nsect = number(DFLNSECT, "nsect", 0);
nsect_flag = RC_KEYWORD;
} else if (match("ntrack=")) {
ntrack = number(DFLNTRAK, "ntrack", 0);
ntrack_flag = RC_KEYWORD;
} else if (match("bsize=")) {
bsize = number(DESBLKSIZE, "bsize", 0);
bsize_flag = RC_KEYWORD;
} else if (match("fragsize=")) {
fragsize = number(DESFRAGSIZE,
"fragsize", 0);
fragsize_flag = RC_KEYWORD;
} else if (match("cgsize=")) {
cpg = number(DESCPG, "cgsize", 0);
cpg_flag = RC_KEYWORD;
} else if (match("free=")) {
minfree = number(MINFREE, "free",
ALLOW_PERCENT);
minfree_flag = RC_KEYWORD;
} else if (match("maxcontig=")) {
tmpmaxcontig =
number(-1, "maxcontig", 0);
maxcontig_flag = RC_KEYWORD;
} else if (match("nrpos=")) {
nrpos = number(NRPOS, "nrpos", 0);
nrpos_flag = RC_KEYWORD;
} else if (match("rps=")) {
rps = number(DEFHZ, "rps", 0);
rps_flag = RC_KEYWORD;
} else if (match("nbpi=")) {
nbpi = number(NBPI, "nbpi", 0);
nbpi_flag = RC_KEYWORD;
} else if (match("opt=")) {
opt = checkopt(string);
} else if (match("mtb=")) {
mtb = checkmtb(string);
} else if (match("apc=")) {
apc = number(0, "apc", 0);
apc_flag = RC_KEYWORD;
} else if (match("gap=")) {
(void) number(0, "gap", ALLOW_MS1);
rotdelay = ROTDELAY;
rotdelay_flag = RC_DEFAULT;
} else if (match("debug=")) {
debug = number(0, "debug", 0);
} else if (match("N")) {
Nflag++;
} else if (match("calcsb")) {
rflag++;
Nflag++;
} else if (match("calcbinsb")) {
rflag++;
Rflag++;
Nflag++;
} else if (*string == '\0') {
break;
} else {
(void) fprintf(stderr, gettext(
"illegal option: %s\n"), string);
usage();
}
if (*string == ',') string++;
if (*string == ' ') string++;
}
break;
case 'V':
{
char *opt_text;
int opt_count;
(void) fprintf(stdout, gettext("mkfs -F ufs "));
for (opt_count = 1; opt_count < argc;
opt_count++) {
opt_text = argv[opt_count];
if (opt_text)
(void) fprintf(stdout, " %s ",
opt_text);
}
(void) fprintf(stdout, "\n");
}
break;
case 'b': /* do nothing for this */
break;
case 'M': /* grow the mounted file system */
directory = optarg;
/* FALLTHROUGH */
case 'G': /* grow the file system */
grow = 1;
break;
case 'P': /* probe the file system growing size */
Pflag = 1;
grow = 1; /* probe mode implies fs growing */
break;
case 'T': /* For testing */
testforce = 1;
/* FALLTHROUGH */
case 't':
test = 1;
string = optarg;
testfrags = number(NO_DEFAULT, "testfrags", 0);
break;
case '?':
usage();
break;
}
}
#ifdef MKFS_DEBUG
/*
* Turning on MKFS_DEBUG causes mkfs to produce a filesystem
* that can be reproduced by setting the time to 0 and seeding
* the random number generator to a constant.
*/
mkfstime = 0; /* reproducible results */
#else
(void) time(&mkfstime);
#endif
if (optind >= (argc - 1)) {
if (optind > (argc - 1)) {
(void) fprintf(stderr,
gettext("special not specified\n"));
usage();
} else if (mflag == 0) {
(void) fprintf(stderr,
gettext("size not specified\n"));
usage();
}
}
argc -= optind;
argv = &argv[optind];
fsys = argv[0];
fsi = open64(fsys, O_RDONLY);
if (fsi < 0) {
(void) fprintf(stderr, gettext("%s: cannot open\n"), fsys);
lockexit(32);
}
if (mflag) {
dump_fscmd(fsys, fsi);
lockexit(0);
}
/*
* The task of setting all of the configuration parameters for a
* UFS file system is basically a matter of solving n equations
* in m variables. Typically, m is greater than n, so there is
* usually more than one valid solution. Since this is usually
* an under-constrained problem, it's not always obvious what the
* "best" configuration is.
*
* In general, the approach is to
* 1. Determine the values for the file system parameters
* that are externally contrained and therefore not adjustable
* by mkfs (such as the device's size and maxtransfer size).
* 2. Acquire the user's requested setting for all configuration
* values that can be set on the command line.
* 3. Determine the final value of all configuration values, by
* the following approach:
* - set the file system block size (fs_bsize). Although
* this could be regarded as an adjustable parameter, in
* fact, it's pretty much a constant. At this time, it's
* generally set to 8k (with older hardware, it can
* sometimes make sense to set it to 4k, but those
* situations are pretty rare now).
* - re-adjust the maximum file system size based on the
* value of the file system block size. Since the
* frag size can't be any larger than a file system
* block, and the number of frags in the file system
* has to fit into 31 bits, the file system block size
* affects the maximum file system size.
* - now that the real maximum file system is known, set the
* actual size of the file system to be created to
* MIN(requested size, maximum file system size).
* - now validate, and if necessary, adjust the following
* values:
* rotdelay
* nsect
* maxcontig
* apc
* frag_size
* rps
* minfree
* nrpos
* nrack
* nbpi
* - calculate maxcpg (the maximum value of the cylinders-per-
* cylinder-group configuration parameters). There are two
* algorithms for calculating maxcpg: an old one, which is
* used for file systems of less than 1 terabyte, and a
* new one, implemented in the function compute_maxcpg(),
* which is used for file systems of greater than 1 TB.
* The difference between them is that compute_maxcpg()
* really tries to maximize the cpg value. The old
* algorithm fails to take advantage of smaller frags and
* lower inode density when determining the maximum cpg,
* and thus comes up with much lower numbers in some
* configurations. At some point, we might use the
* new algorithm for determining maxcpg for all file
* systems, but at this time, the changes implemented for
* multi-terabyte UFS are NOT being automatically applied
* to UFS file systems of less than a terabyte (in the
* interest of not changing existing UFS policy too much
* until the ramifications of the changes are well-understood
* and have been evaluated for their effects on performance.)
* - check the current values of the configuration parameters
* against the various constraints imposed by UFS. These
* include:
* * There must be at least one inode in each
* cylinder group.
* * The cylinder group overhead block, which
* contains the inode and frag bigmaps, must fit
* within one file system block.
* * The space required for inode maps should
* occupy no more than a third of the cylinder
* group overhead block.
* * The rotational position tables have to fit
* within the available space in the super block.
* Adjust the configuration values that can be adjusted
* so that these constraints are satisfied. The
* configuration values that are adjustable are:
* * frag size
* * cylinders per group
* * inode density (can be increased)
* * number of rotational positions (the rotational
* position tables are eliminated altogether if
* there isn't enough room for them.)
* 4. Set the values for all the dependent configuration
* values (those that aren't settable on the command
* line and which are completely dependent on the
* adjustable parameters). This include cpc (cycles
* per cylinder, spc (sectors-per-cylinder), and many others.
*/
/*
* Figure out the partition size and initialize the label_type.
*/
max_fssize = get_max_size(fsi);
/*
* Get and check positional arguments, if any.
*/
switch (argc - 1) {
default:
usage();
/*NOTREACHED*/
case 15:
mtb = checkmtb(argv[15]);
/* FALLTHROUGH */
case 14:
string = argv[14];
tmpmaxcontig = number(-1, "maxcontig", 0);
maxcontig_flag = RC_POSITIONAL;
/* FALLTHROUGH */
case 13:
string = argv[13];
nrpos = number(NRPOS, "nrpos", 0);
nrpos_flag = RC_POSITIONAL;
/* FALLTHROUGH */
case 12:
string = argv[12];
rotdelay = ROTDELAY;
rotdelay_flag = RC_DEFAULT;
/* FALLTHROUGH */
case 11:
string = argv[11];
apc = number(0, "apc", 0);
apc_flag = RC_POSITIONAL;
/* FALLTHROUGH */
case 10:
opt = checkopt(argv[10]);
/* FALLTHROUGH */
case 9:
string = argv[9];
nbpi = number(NBPI, "nbpi", 0);
nbpi_flag = RC_POSITIONAL;
/* FALLTHROUGH */
case 8:
string = argv[8];
rps = number(DEFHZ, "rps", 0);
rps_flag = RC_POSITIONAL;
/* FALLTHROUGH */
case 7:
string = argv[7];
minfree = number(MINFREE, "free", ALLOW_PERCENT);
minfree_flag = RC_POSITIONAL;
/* FALLTHROUGH */
case 6:
string = argv[6];
cpg = number(DESCPG, "cgsize", 0);
cpg_flag = RC_POSITIONAL;
/* FALLTHROUGH */
case 5:
string = argv[5];
fragsize = number(DESFRAGSIZE, "fragsize", 0);
fragsize_flag = RC_POSITIONAL;
/* FALLTHROUGH */
case 4:
string = argv[4];
bsize = number(DESBLKSIZE, "bsize", 0);
bsize_flag = RC_POSITIONAL;
/* FALLTHROUGH */
case 3:
string = argv[3];
ntrack = number(DFLNTRAK, "ntrack", 0);
ntrack_flag = RC_POSITIONAL;
/* FALLTHROUGH */
case 2:
string = argv[2];
nsect = number(DFLNSECT, "nsect", 0);
nsect_flag = RC_POSITIONAL;
/* FALLTHROUGH */
case 1:
string = argv[1];
fssize_db = number(max_fssize, "size", 0);
}
/*
* Initialize the parameters in the same way as newfs so that
* newfs and mkfs would result in the same file system layout
* for EFI labelled disks. Do this only in the absence of user
* specified values for these parameters.
*/
if (label_type == LABEL_TYPE_EFI) {
if (apc_flag == RC_DEFAULT) apc = 0;
if (nrpos_flag == RC_DEFAULT) nrpos = 1;
if (ntrack_flag == RC_DEFAULT) ntrack = DEF_TRACKS_EFI;
if (rps_flag == RC_DEFAULT) rps = DEFHZ;
if (nsect_flag == RC_DEFAULT) nsect = DEF_SECTORS_EFI;
}
if ((maxcontig_flag == RC_DEFAULT) || (tmpmaxcontig == -1) ||
(maxcontig == -1)) {
long maxtrax = get_max_track_size(fsi);
maxcontig = maxtrax / bsize;
} else {
maxcontig = tmpmaxcontig;
}
dprintf(("DeBuG maxcontig : %ld\n", maxcontig));
if (rotdelay == -1) { /* default by newfs and mkfs */
rotdelay = ROTDELAY;
}
if (cpg_flag == RC_DEFAULT) { /* If not explicity set, use default */
cpg = DESCPG;
}
dprintf(("DeBuG cpg : %ld\n", cpg));
/*
* Now that we have the semi-sane args, either positional, via -o,
* or by defaulting, handle inter-dependencies and range checks.
*/
/*
* Settle the file system block size first, since it's a fixed
* parameter once set and so many other parameters, including
* max_fssize, depend on it.
*/
range_check(&bsize, "bsize", MINBSIZE, MAXBSIZE, DESBLKSIZE,
bsize_flag);
if (!POWEROF2(bsize)) {
(void) fprintf(stderr,
gettext("block size must be a power of 2, not %ld\n"),
bsize);
bsize = DESBLKSIZE;
(void) fprintf(stderr,
gettext("mkfs: bsize reset to default %ld\n"),
bsize);
}
if (fssize_db > max_fssize && validate_size(fsi, fssize_db)) {
(void) fprintf(stderr, gettext(
"Warning: the requested size of this file system\n"
"(%lld sectors) is greater than the size of the\n"
"device reported by the driver (%lld sectors).\n"
"However, a read of the device at the requested size\n"
"does succeed, so the requested size will be used.\n"),
fssize_db, max_fssize);
max_fssize = fssize_db;
}
/*
* Since the maximum allocatable unit (the frag) must be less than
* or equal to bsize, and the number of frags must be less than or
* equal to INT_MAX, the total size of the file system (in
* bytes) must be less than or equal to bsize * INT_MAX.
*/
if (max_fssize > ((diskaddr_t)bsize/DEV_BSIZE) * INT_MAX)
max_fssize = ((diskaddr_t)bsize/DEV_BSIZE) * INT_MAX;
range_check_64(&fssize_db, "size", 1024LL, max_fssize, max_fssize, 1);
if (fssize_db >= SECTORS_PER_TERABYTE) {
mtb = 'y';
if (!in_64bit_mode()) {
(void) fprintf(stderr, gettext(
"mkfs: Warning: Creating a file system greater than 1 terabyte on a\n"
" system running a 32-bit kernel. This file system will not be\n"
" accessible until the system is rebooted with a 64-bit kernel.\n"));
}
}
dprintf(("DeBuG mtb : %c\n", mtb));
/*
* With newer and much larger disks, the newfs(1M) and mkfs_ufs(1M)
* commands had problems in correctly handling the "native" geometries
* for various storage devices.
*
* To handle the new age disks, mkfs_ufs(1M) will use the EFI style
* for non-EFI disks that are larger than the CHS addressing limit
* ( > 8GB approx ) and ignore the disk geometry information for
* these drives. This is what is currently done for multi-terrabyte
* filesystems on EFI disks.
*
* However if the user asked for a specific layout by supplying values
* for even one of the three parameters (nsect, ntrack, cpg), honour
* the user supplied parameters.
*
* Choosing EFI style or native geometry style can make a lot of
* difference, because the size of a cylinder group is dependent on
* this choice. This in turn means that the position of alternate
* superblocks varies depending on the style chosen. It is not
* necessary that all disks of size > CHSLIMIT have EFI style layout.
* There can be disks which are > CHSLIMIT size, but have native
* geometry style layout, thereby warranting the need for alternate
* logic in superblock detection.
*/
if (mtb != 'y' && (ntrack == -1 || GROW_WITH_DEFAULT_TRAK ||
DEFAULT_SECT_TRAK_CPG)) {
/*
* "-1" indicates that we were called from newfs and ntracks
* was not specified in newfs command line. Calculate nsect
* and ntrack in the same manner as newfs.
*
* This is required because, the defaults for nsect and ntrack
* is hardcoded in mkfs, whereas to generate the alternate
* superblock locations for the -N option, there is a need for
* the geometry based values that newfs would have arrived at.
* Newfs would have arrived at these values as below.
*/
if (label_type == LABEL_TYPE_EFI ||
label_type == LABEL_TYPE_OTHER) {
use_efi_dflts = 1;
retry = 1;
} else if (ioctl(fsi, DKIOCGGEOM, &dkg)) {
dprintf(("%s: Unable to read Disk geometry", fsys));
perror(gettext("Unable to read Disk geometry"));
lockexit(32);
} else {
nsect = dkg.dkg_nsect;
ntrack = dkg.dkg_nhead;
#ifdef i386 /* Bug 1170182 */
if (ntrack > 32 && (ntrack % 16) != 0) {
ntrack -= (ntrack % 16);
}
#endif
if (ioctl(fsi, DKIOCREMOVABLE, &isremovable)) {
dprintf(("DeBuG Unable to determine if %s is"
" Removable Media. Proceeding with system"
" determined parameters.\n", fsys));
isremovable = 0;
}
if (ioctl(fsi, DKIOCHOTPLUGGABLE, &ishotpluggable)) {
dprintf(("DeBuG Unable to determine if %s is"
" Hotpluggable Media. Proceeding with "
"system determined parameters.\n", fsys));
ishotpluggable = 0;
}
if ((((diskaddr_t)dkg.dkg_ncyl * dkg.dkg_nhead *
dkg.dkg_nsect) > CHSLIMIT) || isremovable ||
ishotpluggable) {
use_efi_dflts = 1;
retry = 1;
}
}
}
dprintf(("DeBuG CHSLIMIT = %d geom = %llu\n", CHSLIMIT,
(diskaddr_t)dkg.dkg_ncyl * dkg.dkg_nhead * dkg.dkg_nsect));
dprintf(("DeBuG label_type = %d isremovable = %d ishotpluggable = %d "
"use_efi_dflts = %d\n", label_type, isremovable, ishotpluggable,
use_efi_dflts));
/*
* For the newfs -N case, even if the disksize is > CHSLIMIT, do not
* blindly follow EFI style. If the fs_version indicates a geometry
* based layout, try that one first. If it fails we can always try the
* other logic.
*
* If we were called from growfs, we will have a problem if we mix
* and match the filesystem creation and growth styles. For example,
* if we create using EFI style, we have to also grow using EFI
* style. So follow the style indicated by the fs_version.
*
* Read and verify the primary superblock. If it looks sane, use the
* fs_version from the superblock. If the primary superblock does
* not look good, read and verify the first alternate superblock at
* ALTSB. Use the fs_version to decide whether to use the
* EFI style logic or the old geometry based logic to calculate
* the alternate superblock locations.
*/
if ((Nflag && use_efi_dflts) || (grow)) {
if (grow && ntrack_flag != RC_DEFAULT)
goto start_fs_creation;
rdfs((diskaddr_t)(SBOFF / sectorsize), (int)sbsize,
(char *)&altsblock);
ret = checksblock(altsblock, 1);
if (!ret) {
if (altsblock.fs_magic == MTB_UFS_MAGIC) {
mtb = 'y';
goto start_fs_creation;
}
use_efi_dflts = (altsblock.fs_version ==
UFS_EFISTYLE4NONEFI_VERSION_2) ? 1 : 0;
} else {
/*
* The primary superblock didn't help in determining
* the fs_version. Try the first alternate superblock.
*/
dprintf(("DeBuG checksblock() failed - error : %d"
" for sb : %d\n", ret, SBOFF/sectorsize));
rdfs((diskaddr_t)ALTSB, (int)sbsize,
(char *)&altsblock);
ret = checksblock(altsblock, 1);
if (!ret) {
if (altsblock.fs_magic == MTB_UFS_MAGIC) {
mtb = 'y';
goto start_fs_creation;
}
use_efi_dflts = (altsblock.fs_version ==
UFS_EFISTYLE4NONEFI_VERSION_2) ? 1 : 0;
}
dprintf(("DeBuG checksblock() returned : %d"
" for sb : %d\n", ret, ALTSB));
}
}
geom_nsect = nsect;
geom_ntrack = ntrack;
geom_cpg = cpg;
dprintf(("DeBuG geom_nsect=%d, geom_ntrack=%d, geom_cpg=%d\n",
geom_nsect, geom_ntrack, geom_cpg));
start_fs_creation:
retry_alternate_logic:
invalid_sb_cnt = 0;
cg_too_small = 0;
if (use_efi_dflts) {
nsect = DEF_SECTORS_EFI;
ntrack = DEF_TRACKS_EFI;
cpg = DESCPG;
dprintf(("\nDeBuG Using EFI defaults\n"));
} else {
nsect = geom_nsect;
ntrack = geom_ntrack;
cpg = geom_cpg;
dprintf(("\nDeBuG Using Geometry\n"));
/*
* 32K based on max block size of 64K, and rotational layout
* test of nsect <= (256 * sectors/block). Current block size
* limit is not 64K, but it's growing soon.
*/
range_check(&nsect, "nsect", 1, 32768, DFLNSECT, nsect_flag);
/*
* ntrack is the number of tracks per cylinder.
* The ntrack value must be between 1 and the total number of
* sectors in the file system.
*/
range_check(&ntrack, "ntrack", 1,
fssize_db > INT_MAX ? INT_MAX : (uint32_t)fssize_db,
DFLNTRAK, ntrack_flag);
}
range_check(&apc, "apc", 0, nsect - 1, 0, apc_flag);
if (mtb == 'y')
fragsize = bsize;
range_check(&fragsize, "fragsize", sectorsize, bsize,
MAX(bsize / MAXFRAG, MIN(DESFRAGSIZE, bsize)), fragsize_flag);
if ((bsize / MAXFRAG) > fragsize) {
(void) fprintf(stderr, gettext(
"fragment size %ld is too small, minimum with block size %ld is %ld\n"),
fragsize, bsize, bsize / MAXFRAG);
(void) fprintf(stderr,
gettext("mkfs: fragsize reset to minimum %ld\n"),
bsize / MAXFRAG);
fragsize = bsize / MAXFRAG;
}
if (!POWEROF2(fragsize)) {
(void) fprintf(stderr,
gettext("fragment size must be a power of 2, not %ld\n"),
fragsize);
fragsize = MAX(bsize / MAXFRAG, MIN(DESFRAGSIZE, bsize));
(void) fprintf(stderr,
gettext("mkfs: fragsize reset to %ld\n"),
fragsize);
}
/* At this point, bsize must be >= fragsize, so no need to check it */
if (bsize < PAGESIZE) {
(void) fprintf(stderr, gettext(
"WARNING: filesystem block size (%ld) is smaller than "
"memory page size (%ld).\nResulting filesystem can not be "
"mounted on this system.\n\n"),
bsize, (long)PAGESIZE);
}
range_check(&rps, "rps", 1, 1000, DEFHZ, rps_flag);
range_check(&minfree, "free", 0, 99, MINFREE, minfree_flag);
range_check(&nrpos, "nrpos", 1, nsect, MIN(nsect, NRPOS), nrpos_flag);
/*
* nbpi is variable, but 2MB seems a reasonable upper limit,
* as 4MB tends to cause problems (using otherwise-default
* parameters). The true limit is where we end up with one
* inode per cylinder group. If this file system is being
* configured for multi-terabyte access, nbpi must be at least 1MB.
*/
if (mtb == 'y' && nbpi < MTB_NBPI) {
if (nbpi_flag != RC_DEFAULT)
(void) fprintf(stderr, gettext("mkfs: bad value for "
"nbpi: must be at least 1048576 for multi-terabyte,"
" nbpi reset to default 1048576\n"));
nbpi = MTB_NBPI;
}
if (mtb == 'y')
range_check(&nbpi, "nbpi", MTB_NBPI, 2 * MB, MTB_NBPI,
nbpi_flag);
else
range_check(&nbpi, "nbpi", DEV_BSIZE, 2 * MB, NBPI, nbpi_flag);
/*
* maxcpg is another variably-limited parameter. Calculate
* the limit based on what we've got for its dependent
* variables. Effectively, it's how much space is left in the
* superblock after all the other bits are accounted for. We
* only fill in sblock fields so we can use MAXIpG.
*
* If the calculation of maxcpg below (for the mtb == 'n'
* case) is changed, update newfs as well.
*
* For old-style, non-MTB format file systems, use the old
* algorithm for calculating the maximum cylinder group size,
* even though it limits the cylinder group more than necessary.
* Since layout can affect performance, we don't want to change
* the default layout for non-MTB file systems at this time.
* However, for MTB file systems, use the new maxcpg calculation,
* which really maxes out the cylinder group size.
*/
sblock.fs_bsize = bsize;
sblock.fs_inopb = sblock.fs_bsize / sizeof (struct dinode);
if (mtb == 'n') {
maxcpg = (bsize - sizeof (struct cg) -
howmany(MAXIpG(&sblock), NBBY)) /
(sizeof (long) + nrpos * sizeof (short) +
nsect / (MAXFRAG * NBBY));
} else {
maxcpg = compute_maxcpg(bsize, fragsize, nbpi, nrpos,
nsect * ntrack);
}
dprintf(("DeBuG cpg : %ld\n", cpg));
/*
* Increase the cpg to maxcpg if either newfs was invoked
* with -T option or if mkfs wants to create a mtb file system
* and if the user has not specified the cpg.
*/
if (cpg == -1 || (mtb == 'y' && cpg_flag == RC_DEFAULT))
cpg = maxcpg;
dprintf(("DeBuG cpg : %ld\n", cpg));
/*
* mincpg is variable in complex ways, so we really can't
* do a sane lower-end limit check at this point.
*/
range_check(&cpg, "cgsize", 1, maxcpg, MIN(maxcpg, DESCPG), cpg_flag);
/*
* get the controller info
*/
ismdd = 0;
islog = 0;
islogok = 0;
waslog = 0;
if (ioctl(fsi, DKIOCINFO, &dkcinfo) == 0)
/*
* if it is an MDD (disksuite) device
*/
if (dkcinfo.dki_ctype == DKC_MD) {
ismdd++;
/*
* check the logging device
*/
if (ioctl(fsi, _FIOISLOG, NULL) == 0) {
islog++;
if (ioctl(fsi, _FIOISLOGOK, NULL) == 0)
islogok++;
}
}
/*
* Do not grow the file system, but print on stdout the maximum
* size in sectors to which the file system can be increased.
* The calculated size is limited by fssize_db.
* Note that we don't lock the filesystem and therefore under rare
* conditions (the filesystem is mounted, the free block count is
* almost zero, and the superuser is still changing it) the calculated
* size can be imprecise.
*/
if (Pflag) {
(void) printf("%llu\n", probe_summaryinfo());
exit(0);
}
/*
* If we're growing an existing filesystem, then we're about
* to start doing things that can require recovery efforts if
* we get interrupted, so make sure we get a chance to do so.
*/
if (grow) {
sigact.sa_handler = recover_from_sigint;
sigemptyset(&sigact.sa_mask);
sigact.sa_flags = SA_RESTART;
if (sigaction(SIGINT, &sigact, (struct sigaction *)NULL) < 0) {
perror(gettext("Could not register SIGINT handler"));
lockexit(3);
}
}
if (!Nflag) {
/*
* Check if MNTTAB is trustable
*/
if (statvfs64(MNTTAB, &fs) < 0) {
(void) fprintf(stderr, gettext("can't statvfs %s\n"),
MNTTAB);
exit(32);
}
if (strcmp(MNTTYPE_MNTFS, fs.f_basetype) != 0) {
(void) fprintf(stderr, gettext(
"%s file system type is not %s, can't mkfs\n"),
MNTTAB, MNTTYPE_MNTFS);
exit(32);
}
special = getfullblkname(fsys);
checkdev(fsys, special);
/*
* If we found the block device name,
* then check the mount table.
* if mounted, and growing write lock the file system
*
*/
if ((special != NULL) && (*special != '\0')) {
if ((mnttab = fopen(MNTTAB, "r")) == NULL) {
(void) fprintf(stderr, gettext(
"can't open %s\n"), MNTTAB);
exit(32);
}
while ((getmntent(mnttab, &mntp)) == NULL) {
if (grow) {
checkmount(&mntp, special);
continue;
}
if (strcmp(special, mntp.mnt_special) == 0) {
(void) fprintf(stderr, gettext(
"%s is mounted, can't mkfs\n"),
special);
exit(32);
}
}
(void) fclose(mnttab);
}
if (directory && (ismounted == 0)) {
(void) fprintf(stderr, gettext("%s is not mounted\n"),
special);
lockexit(32);
}
fso = (grow) ? open64(fsys, O_WRONLY) : creat64(fsys, 0666);
if (fso < 0) {
saverr = errno;
(void) fprintf(stderr,
gettext("%s: cannot create: %s\n"),
fsys, strerror(saverr));
lockexit(32);
}
} else {
/*
* For the -N case, a file descriptor is needed for the llseek()
* in wtfs(). See the comment in wtfs() for more information.
*
* Get a file descriptor that's read-only so that this code
* doesn't accidentally write to the file.
*/
fso = open64(fsys, O_RDONLY);
if (fso < 0) {
saverr = errno;
(void) fprintf(stderr, gettext("%s: cannot open: %s\n"),
fsys, strerror(saverr));
lockexit(32);
}
}
/*
* Check the media sector size
*/
if (ioctl(fso, DKIOCGMEDIAINFO, &dkminfo) != -1) {
if (dkminfo.dki_lbsize != 0 &&
POWEROF2(dkminfo.dki_lbsize / DEV_BSIZE) &&
dkminfo.dki_lbsize != DEV_BSIZE) {
fprintf(stderr,
gettext("The device sector size %u is not "
"supported by ufs!\n"), dkminfo.dki_lbsize);
(void) close(fso);
exit(1);
}
}
/*
* seed random # generator (for ic_generation)
*/
#ifdef MKFS_DEBUG
srand48(12962); /* reproducible results */
#else
srand48((long)(time((time_t *)NULL) + getpid()));
#endif
if (grow) {
growinit(fsys);
goto grow00;
}
/*
* Validate the given file system size.
* Verify that its last block can actually be accessed.
*
* Note: it's ok to use sblock as a buffer because it is immediately
* overwritten by the rdfs() of the superblock in the next line.
*
* ToDo: Because the size checking is done in rdfs()/wtfs(), the
* error message for specifying an illegal size is very unfriendly.
* In the future, one could replace the rdfs()/wtfs() calls
* below with in-line calls to read() or write(). This allows better
* error messages to be put in place.
*/
rdfs(fssize_db - 1, (int)sectorsize, (char *)&sblock);
/*
* make the fs unmountable
*/
rdfs((diskaddr_t)(SBOFF / sectorsize), (int)sbsize, (char *)&sblock);
sblock.fs_magic = -1;
sblock.fs_clean = FSBAD;
sblock.fs_state = FSOKAY - sblock.fs_time;
wtfs((diskaddr_t)(SBOFF / sectorsize), (int)sbsize, (char *)&sblock);
bzero(&sblock, (size_t)sbsize);
sblock.fs_nsect = nsect;
sblock.fs_ntrak = ntrack;
/*
* Validate specified/determined spc
* and calculate minimum cylinders per group.
*/
/*
* sectors/cyl = tracks/cyl * sectors/track
*/
sblock.fs_spc = sblock.fs_ntrak * sblock.fs_nsect;
grow00:
if (apc_flag) {
sblock.fs_spc -= apc;
}
/*
* Have to test for this separately from apc_flag, due to
* the growfs case....
*/
if (sblock.fs_spc != sblock.fs_ntrak * sblock.fs_nsect) {
spc_flag = 1;
}
if (grow)
goto grow10;
sblock.fs_nrpos = nrpos;
sblock.fs_bsize = bsize;
sblock.fs_fsize = fragsize;
sblock.fs_minfree = minfree;
grow10:
if (nbpi < sblock.fs_fsize) {
(void) fprintf(stderr, gettext(
"warning: wasteful data byte allocation / inode (nbpi):\n"));
(void) fprintf(stderr, gettext(
"%ld smaller than allocatable fragment size of %d\n"),
nbpi, sblock.fs_fsize);
}
if (grow)
goto grow20;
if (opt == 's')
sblock.fs_optim = FS_OPTSPACE;
else
sblock.fs_optim = FS_OPTTIME;
sblock.fs_bmask = ~(sblock.fs_bsize - 1);
sblock.fs_fmask = ~(sblock.fs_fsize - 1);
/*
* Planning now for future expansion.
*/
#if defined(_BIG_ENDIAN)
sblock.fs_qbmask.val[0] = 0;
sblock.fs_qbmask.val[1] = ~sblock.fs_bmask;
sblock.fs_qfmask.val[0] = 0;
sblock.fs_qfmask.val[1] = ~sblock.fs_fmask;
#endif
#if defined(_LITTLE_ENDIAN)
sblock.fs_qbmask.val[0] = ~sblock.fs_bmask;
sblock.fs_qbmask.val[1] = 0;
sblock.fs_qfmask.val[0] = ~sblock.fs_fmask;
sblock.fs_qfmask.val[1] = 0;
#endif
for (sblock.fs_bshift = 0, i = sblock.fs_bsize; i > 1; i >>= 1)
sblock.fs_bshift++;
for (sblock.fs_fshift = 0, i = sblock.fs_fsize; i > 1; i >>= 1)
sblock.fs_fshift++;
sblock.fs_frag = numfrags(&sblock, sblock.fs_bsize);
for (sblock.fs_fragshift = 0, i = sblock.fs_frag; i > 1; i >>= 1)
sblock.fs_fragshift++;
if (sblock.fs_frag > MAXFRAG) {
(void) fprintf(stderr, gettext(
"fragment size %d is too small, minimum with block size %d is %d\n"),
sblock.fs_fsize, sblock.fs_bsize,
sblock.fs_bsize / MAXFRAG);
lockexit(32);
}
sblock.fs_nindir = sblock.fs_bsize / sizeof (daddr32_t);
sblock.fs_inopb = sblock.fs_bsize / sizeof (struct dinode);
sblock.fs_nspf = sblock.fs_fsize / sectorsize;
for (sblock.fs_fsbtodb = 0, i = NSPF(&sblock); i > 1; i >>= 1)
sblock.fs_fsbtodb++;
/*
* Compute the super-block, cylinder group, and inode blocks.
* Note that these "blkno" are really fragment addresses.
* For example, on an 8K/1K (block/fragment) system, fs_sblkno is 16,
* fs_cblkno is 24, and fs_iblkno is 32. This is why CGSIZE is so
* important: only 1 FS block is allocated for the cg struct (fragment
* numbers 24 through 31).
*/
sblock.fs_sblkno =
roundup(howmany(bbsize + sbsize, sblock.fs_fsize), sblock.fs_frag);
sblock.fs_cblkno = (daddr32_t)(sblock.fs_sblkno +
roundup(howmany(sbsize, sblock.fs_fsize), sblock.fs_frag));
sblock.fs_iblkno = sblock.fs_cblkno + sblock.fs_frag;
sblock.fs_cgoffset = roundup(
howmany(sblock.fs_nsect, NSPF(&sblock)), sblock.fs_frag);
for (sblock.fs_cgmask = -1, i = sblock.fs_ntrak; i > 1; i >>= 1)
sblock.fs_cgmask <<= 1;
if (!POWEROF2(sblock.fs_ntrak))
sblock.fs_cgmask <<= 1;
/*
* Validate specified/determined spc
* and calculate minimum cylinders per group.
*/
for (sblock.fs_cpc = NSPB(&sblock), i = sblock.fs_spc;
sblock.fs_cpc > 1 && (i & 1) == 0;
sblock.fs_cpc >>= 1, i >>= 1)
/* void */;
mincpc = sblock.fs_cpc;
/* if these calculations are changed, check dump_fscmd also */
bpcg = (uint64_t)sblock.fs_spc * sectorsize;
inospercg = (uint64_t)roundup(bpcg / sizeof (struct dinode),
INOPB(&sblock));
if (inospercg > MAXIpG(&sblock))
inospercg = MAXIpG(&sblock);
used = (uint64_t)(sblock.fs_iblkno + inospercg /
INOPF(&sblock)) * NSPF(&sblock);
mincpgcnt = (long)howmany((uint64_t)sblock.fs_cgoffset *
(~sblock.fs_cgmask) + used, sblock.fs_spc);
mincpg = roundup(mincpgcnt, mincpc);
/*
* Insure that cylinder group with mincpg has enough space
* for block maps
*/
sblock.fs_cpg = mincpg;
sblock.fs_ipg = (int32_t)inospercg;
mapcramped = 0;
/*
* Make sure the cg struct fits within the file system block.
* Use larger block sizes until it fits
*/
while (CGSIZE(&sblock) > sblock.fs_bsize) {
mapcramped = 1;
if (sblock.fs_bsize < MAXBSIZE) {
sblock.fs_bsize <<= 1;
if ((i & 1) == 0) {
i >>= 1;
} else {
sblock.fs_cpc <<= 1;
mincpc <<= 1;
mincpg = roundup(mincpgcnt, mincpc);
sblock.fs_cpg = mincpg;
}
sblock.fs_frag <<= 1;
sblock.fs_fragshift += 1;
if (sblock.fs_frag <= MAXFRAG)
continue;
}
/*
* Looped far enough. The fragment is now as large as the
* filesystem block!
*/
if (sblock.fs_fsize == sblock.fs_bsize) {
(void) fprintf(stderr, gettext(
"There is no block size that can support this disk\n"));
lockexit(32);
}
/*
* Try a larger fragment. Double the fragment size.
*/
sblock.fs_frag >>= 1;
sblock.fs_fragshift -= 1;
sblock.fs_fsize <<= 1;
sblock.fs_nspf <<= 1;
}
/*
* Insure that cylinder group with mincpg has enough space for inodes
*/
inodecramped = 0;
used *= sectorsize;
nbytes64 = (uint64_t)mincpg * bpcg - used;
inospercg = (uint64_t)roundup((nbytes64 / nbpi), INOPB(&sblock));
sblock.fs_ipg = (int32_t)inospercg;
while (inospercg > MAXIpG(&sblock)) {
inodecramped = 1;
if (mincpc == 1 || sblock.fs_frag == 1 ||
sblock.fs_bsize == MINBSIZE)
break;
nbytes64 = (uint64_t)mincpg * bpcg - used;
(void) fprintf(stderr,
gettext("With a block size of %d %s %lu\n"),
sblock.fs_bsize, gettext("minimum bytes per inode is"),
(uint32_t)(nbytes64 / MAXIpG(&sblock) + 1));
sblock.fs_bsize >>= 1;
sblock.fs_frag >>= 1;
sblock.fs_fragshift -= 1;
mincpc >>= 1;
sblock.fs_cpg = roundup(mincpgcnt, mincpc);
if (CGSIZE(&sblock) > sblock.fs_bsize) {
sblock.fs_bsize <<= 1;
break;
}
mincpg = sblock.fs_cpg;
nbytes64 = (uint64_t)mincpg * bpcg - used;
inospercg = (uint64_t)roundup((nbytes64 / nbpi),
INOPB(&sblock));
sblock.fs_ipg = (int32_t)inospercg;
}
if (inodecramped) {
if (inospercg > MAXIpG(&sblock)) {
nbytes64 = (uint64_t)mincpg * bpcg - used;
(void) fprintf(stderr, gettext(
"Minimum bytes per inode is %d\n"),
(uint32_t)(nbytes64 / MAXIpG(&sblock) + 1));
} else if (!mapcramped) {
(void) fprintf(stderr, gettext(
"With %ld bytes per inode, minimum cylinders per group is %ld\n"),
nbpi, mincpg);
}
}
if (mapcramped) {
(void) fprintf(stderr, gettext(
"With %d sectors per cylinder, minimum cylinders "
"per group is %ld\n"),
sblock.fs_spc, mincpg);
}
if (inodecramped || mapcramped) {
/*
* To make this at least somewhat comprehensible in
* the world of i18n, figure out what we're going to
* say and then say it all at one time. The days of
* needing to scrimp on string space are behind us....
*/
if ((sblock.fs_bsize != bsize) &&
(sblock.fs_fsize != fragsize)) {
(void) fprintf(stderr, gettext(
"This requires the block size to be changed from %ld to %d\n"
"and the fragment size to be changed from %ld to %d\n"),
bsize, sblock.fs_bsize,
fragsize, sblock.fs_fsize);
} else if (sblock.fs_bsize != bsize) {
(void) fprintf(stderr, gettext(
"This requires the block size to be changed from %ld to %d\n"),
bsize, sblock.fs_bsize);
} else if (sblock.fs_fsize != fragsize) {
(void) fprintf(stderr, gettext(
"This requires the fragment size to be changed from %ld to %d\n"),
fragsize, sblock.fs_fsize);
} else {
(void) fprintf(stderr, gettext(
"Unable to make filesystem fit with the given constraints\n"));
}
(void) fprintf(stderr, gettext(
"Please re-run mkfs with corrected parameters\n"));
lockexit(32);
}
/*
* Calculate the number of cylinders per group
*/
sblock.fs_cpg = cpg;
if (sblock.fs_cpg % mincpc != 0) {
(void) fprintf(stderr, gettext(
"Warning: cylinder groups must have a multiple "
"of %ld cylinders with the given\n parameters\n"),
mincpc);
sblock.fs_cpg = roundup(sblock.fs_cpg, mincpc);
(void) fprintf(stderr, gettext("Rounded cgsize up to %d\n"),
sblock.fs_cpg);
}
/*
* Must insure there is enough space for inodes
*/
/* if these calculations are changed, check dump_fscmd also */
nbytes64 = (uint64_t)sblock.fs_cpg * bpcg - used;
sblock.fs_ipg = roundup((uint32_t)(nbytes64 / nbpi), INOPB(&sblock));
/*
* Slim down cylinders per group, until the inodes can fit.
*/
while (sblock.fs_ipg > MAXIpG(&sblock)) {
inodecramped = 1;
sblock.fs_cpg -= mincpc;
nbytes64 = (uint64_t)sblock.fs_cpg * bpcg - used;
sblock.fs_ipg = roundup((uint32_t)(nbytes64 / nbpi),
INOPB(&sblock));
}
/*
* Must insure there is enough space to hold block map.
* Cut down on cylinders per group, until the cg struct fits in a
* filesystem block.
*/
while (CGSIZE(&sblock) > sblock.fs_bsize) {
mapcramped = 1;
sblock.fs_cpg -= mincpc;
nbytes64 = (uint64_t)sblock.fs_cpg * bpcg - used;
sblock.fs_ipg = roundup((uint32_t)(nbytes64 / nbpi),
INOPB(&sblock));
}
sblock.fs_fpg = (sblock.fs_cpg * sblock.fs_spc) / NSPF(&sblock);
if ((sblock.fs_cpg * sblock.fs_spc) % NSPB(&sblock) != 0) {
(void) fprintf(stderr,
gettext("newfs: panic (fs_cpg * fs_spc) %% NSPF != 0\n"));
lockexit(32);
}
if (sblock.fs_cpg < mincpg) {
(void) fprintf(stderr, gettext(
"With the given parameters, cgsize must be at least %ld; please re-run mkfs\n"),
mincpg);
lockexit(32);
}
sblock.fs_cgsize = fragroundup(&sblock, CGSIZE(&sblock));
grow20:
/*
* Now have size for file system and nsect and ntrak.
* Determine number of cylinders and blocks in the file system.
*/
fssize_frag = (int64_t)dbtofsb(&sblock, fssize_db);
if (fssize_frag > INT_MAX) {
(void) fprintf(stderr, gettext(
"There are too many fragments in the system, increase fragment size\n"),
mincpg);
lockexit(32);
}
sblock.fs_size = (int32_t)fssize_frag;
sblock.fs_ncyl = (int32_t)(fssize_frag * NSPF(&sblock) / sblock.fs_spc);
if (fssize_frag * NSPF(&sblock) >
(uint64_t)sblock.fs_ncyl * sblock.fs_spc) {
sblock.fs_ncyl++;
warn = 1;
}
if (sblock.fs_ncyl < 1) {
(void) fprintf(stderr, gettext(
"file systems must have at least one cylinder\n"));
lockexit(32);
}
if (grow)
goto grow30;
/*
* Determine feasability/values of rotational layout tables.
*
* The size of the rotational layout tables is limited by the size
* of the file system block, fs_bsize. The amount of space
* available for tables is calculated as (fs_bsize - sizeof (struct
* fs)). The size of these tables is inversely proportional to the
* block size of the file system. The size increases if sectors per
* track are not powers of two, because more cylinders must be
* described by the tables before the rotational pattern repeats
* (fs_cpc).
*/
sblock.fs_postblformat = FS_DYNAMICPOSTBLFMT;
sblock.fs_sbsize = fragroundup(&sblock, sizeof (struct fs));
sblock.fs_npsect = sblock.fs_nsect;
if (sblock.fs_ntrak == 1) {
sblock.fs_cpc = 0;
goto next;
}
postblsize = sblock.fs_nrpos * sblock.fs_cpc * sizeof (short);
rotblsize = sblock.fs_cpc * sblock.fs_spc / NSPB(&sblock);
totalsbsize = sizeof (struct fs) + rotblsize;
/* do static allocation if nrpos == 8 and fs_cpc == 16 */
if (sblock.fs_nrpos == 8 && sblock.fs_cpc <= 16) {
/* use old static table space */
sblock.fs_postbloff = (char *)(&sblock.fs_opostbl[0][0]) -
(char *)(&sblock.fs_link);
sblock.fs_rotbloff = &sblock.fs_space[0] -
(uchar_t *)(&sblock.fs_link);
} else {
/* use 4.3 dynamic table space */
sblock.fs_postbloff = &sblock.fs_space[0] -
(uchar_t *)(&sblock.fs_link);
sblock.fs_rotbloff = sblock.fs_postbloff + postblsize;
totalsbsize += postblsize;
}
if (totalsbsize > sblock.fs_bsize ||
sblock.fs_nsect > (1 << NBBY) * NSPB(&sblock)) {
(void) fprintf(stderr, gettext(
"Warning: insufficient space in super block for\n"
"rotational layout tables with nsect %d, ntrack %d, "
"and nrpos %d.\nOmitting tables - file system "
"performance may be impaired.\n"),
sblock.fs_nsect, sblock.fs_ntrak, sblock.fs_nrpos);
/*
* Setting fs_cpc to 0 tells alloccgblk() in ufs_alloc.c to
* ignore the positional layout table and rotational
* position table.
*/
sblock.fs_cpc = 0;
goto next;
}
sblock.fs_sbsize = fragroundup(&sblock, totalsbsize);
/*
* calculate the available blocks for each rotational position
*/
for (cylno = 0; cylno < sblock.fs_cpc; cylno++)
for (rpos = 0; rpos < sblock.fs_nrpos; rpos++)
fs_postbl(&sblock, cylno)[rpos] = -1;
for (i = (rotblsize - 1) * sblock.fs_frag;
i >= 0; i -= sblock.fs_frag) {
cylno = cbtocylno(&sblock, i);
rpos = cbtorpos(&sblock, i);
blk = fragstoblks(&sblock, i);
if (fs_postbl(&sblock, cylno)[rpos] == -1)
fs_rotbl(&sblock)[blk] = 0;
else
fs_rotbl(&sblock)[blk] =
fs_postbl(&sblock, cylno)[rpos] - blk;
fs_postbl(&sblock, cylno)[rpos] = blk;
}
next:
grow30:
/*
* Compute/validate number of cylinder groups.
* Note that if an excessively large filesystem is specified
* (e.g., more than 16384 cylinders for an 8K filesystem block), it
* does not get detected until checksummarysize()
*/
sblock.fs_ncg = sblock.fs_ncyl / sblock.fs_cpg;
if (sblock.fs_ncyl % sblock.fs_cpg)
sblock.fs_ncg++;
sblock.fs_dblkno = sblock.fs_iblkno + sblock.fs_ipg / INOPF(&sblock);
i = MIN(~sblock.fs_cgmask, sblock.fs_ncg - 1);
ibpcl = cgdmin(&sblock, i) - cgbase(&sblock, i);
if (ibpcl >= sblock.fs_fpg) {
(void) fprintf(stderr, gettext(
"inode blocks/cyl group (%d) >= data blocks (%d)\n"),
cgdmin(&sblock, i) - cgbase(&sblock, i) / sblock.fs_frag,
sblock.fs_fpg / sblock.fs_frag);
if ((ibpcl < 0) || (sblock.fs_fpg < 0)) {
(void) fprintf(stderr, gettext(
"number of cylinders per cylinder group (%d) must be decreased.\n"),
sblock.fs_cpg);
} else {
(void) fprintf(stderr, gettext(
"number of cylinders per cylinder group (%d) must be increased.\n"),
sblock.fs_cpg);
}
(void) fprintf(stderr, gettext(
"Note that cgsize may have been adjusted to allow struct cg to fit.\n"));
lockexit(32);
}
j = sblock.fs_ncg - 1;
if ((i = fssize_frag - j * sblock.fs_fpg) < sblock.fs_fpg &&
cgdmin(&sblock, j) - cgbase(&sblock, j) > i) {
(void) fprintf(stderr, gettext(
"Warning: inode blocks/cyl group (%d) >= data "
"blocks (%ld) in last\n cylinder group. This "
"implies %ld sector(s) cannot be allocated.\n"),
(cgdmin(&sblock, j) - cgbase(&sblock, j)) / sblock.fs_frag,
i / sblock.fs_frag, i * NSPF(&sblock));
/*
* If there is only one cylinder group and that is not even
* big enough to hold the inodes, exit.
*/
if (sblock.fs_ncg == 1)
cg_too_small = 1;
sblock.fs_ncg--;
sblock.fs_ncyl = sblock.fs_ncg * sblock.fs_cpg;
sblock.fs_size = fssize_frag =
(int64_t)sblock.fs_ncyl * (int64_t)sblock.fs_spc /
(int64_t)NSPF(&sblock);
warn = 0;
}
if (warn && !spc_flag) {
(void) fprintf(stderr, gettext(
"Warning: %d sector(s) in last cylinder unallocated\n"),
sblock.fs_spc - (uint32_t)(fssize_frag * NSPF(&sblock) -
(uint64_t)(sblock.fs_ncyl - 1) * sblock.fs_spc));
}
/*
* fill in remaining fields of the super block
*/
/*
* The csum records are stored in cylinder group 0, starting at
* cgdmin, the first data block.
*/
sblock.fs_csaddr = cgdmin(&sblock, 0);
sblock.fs_cssize =
fragroundup(&sblock, sblock.fs_ncg * sizeof (struct csum));
i = sblock.fs_bsize / sizeof (struct csum);
sblock.fs_csmask = ~(i - 1);
for (sblock.fs_csshift = 0; i > 1; i >>= 1)
sblock.fs_csshift++;
fscs = (struct csum *)calloc(1, sblock.fs_cssize);
checksummarysize();
if (mtb == 'y') {
sblock.fs_magic = MTB_UFS_MAGIC;
sblock.fs_version = MTB_UFS_VERSION_1;
} else {
sblock.fs_magic = FS_MAGIC;
if (use_efi_dflts)
sblock.fs_version = UFS_EFISTYLE4NONEFI_VERSION_2;
else
sblock.fs_version = UFS_VERSION_MIN;
}
if (grow) {
bcopy((caddr_t)grow_fscs, (caddr_t)fscs, (int)grow_fs_cssize);
extendsummaryinfo();
goto grow40;
}
sblock.fs_rotdelay = rotdelay;
sblock.fs_maxcontig = maxcontig;
sblock.fs_maxbpg = MAXBLKPG(sblock.fs_bsize);
sblock.fs_rps = rps;
sblock.fs_cgrotor = 0;
sblock.fs_cstotal.cs_ndir = 0;
sblock.fs_cstotal.cs_nbfree = 0;
sblock.fs_cstotal.cs_nifree = 0;
sblock.fs_cstotal.cs_nffree = 0;
sblock.fs_fmod = 0;
sblock.fs_ronly = 0;
sblock.fs_time = mkfstime;
sblock.fs_state = FSOKAY - sblock.fs_time;
sblock.fs_clean = FSCLEAN;
grow40:
/*
* If all that's needed is a dump of the superblock we
* would use by default, we've got it now. So, splat it
* out and leave.
*/
if (rflag) {
dump_sblock();
lockexit(0);
}
/*
* Dump out summary information about file system.
*/
(void) fprintf(stderr, gettext(
"%s:\t%lld sectors in %d cylinders of %d tracks, %d sectors\n"),
fsys, (uint64_t)sblock.fs_size * NSPF(&sblock), sblock.fs_ncyl,
sblock.fs_ntrak, sblock.fs_nsect);
(void) fprintf(stderr, gettext(
"\t%.1fMB in %d cyl groups (%d c/g, %.2fMB/g, %d i/g)\n"),
(float)sblock.fs_size * sblock.fs_fsize / MB, sblock.fs_ncg,
sblock.fs_cpg, (float)sblock.fs_fpg * sblock.fs_fsize / MB,
sblock.fs_ipg);
tmpbuf = calloc(sblock.fs_ncg / 50 + 500, 1);
if (tmpbuf == NULL) {
perror("calloc");
lockexit(32);
}
if (cg_too_small) {
(void) fprintf(stderr, gettext("File system creation failed. "
"There is only one cylinder group and\nthat is "
"not even big enough to hold the inodes.\n"));
lockexit(32);
}
/*
* Now build the cylinders group blocks and
* then print out indices of cylinder groups.
*/
tprintf(gettext(
"super-block backups (for fsck -F ufs -o b=#) at:\n"));
for (width = cylno = 0; cylno < sblock.fs_ncg && cylno < 10; cylno++) {
if ((grow == 0) || (cylno >= grow_fs_ncg))
initcg(cylno);
num = fsbtodb(&sblock, (uint64_t)cgsblock(&sblock, cylno));
/*
* If Nflag and if the disk is larger than the CHSLIMIT,
* then sanity test the superblocks before reporting. If there
* are too many superblocks which look insane, we have
* to retry with alternate logic. If both methods have
* failed, then our efforts to arrive at alternate
* superblocks failed, so complain and exit.
*/
if (Nflag && retry) {
skip_this_sb = 0;
rdfs((diskaddr_t)num, sbsize, (char *)&altsblock);
ret = checksblock(altsblock, 1);
if (ret) {
skip_this_sb = 1;
invalid_sb_cnt++;
dprintf(("DeBuG checksblock() failed - error :"
" %d for sb : %llu invalid_sb_cnt : %d\n",
ret, num, invalid_sb_cnt));
} else {
/*
* Though the superblock looks sane, verify if
* the fs_version in the superblock and the
* logic that we are using to arrive at the
* superblocks match.
*/
if (use_efi_dflts && altsblock.fs_version
!= UFS_EFISTYLE4NONEFI_VERSION_2) {
skip_this_sb = 1;
invalid_sb_cnt++;
}
}
if (invalid_sb_cnt >= INVALIDSBLIMIT) {
if (retry > 1) {
(void) fprintf(stderr, gettext(
"Error determining alternate "
"superblock locations\n"));
free(tmpbuf);
lockexit(32);
}
retry++;
use_efi_dflts = !use_efi_dflts;
free(tmpbuf);
goto retry_alternate_logic;
}
if (skip_this_sb)
continue;
}
(void) sprintf(pbuf, " %llu,", num);
plen = strlen(pbuf);
if ((width + plen) > (WIDTH - 1)) {
width = plen;
tprintf("\n");
} else {
width += plen;
}
if (Nflag && retry)
(void) strncat(tmpbuf, pbuf, strlen(pbuf));
else
(void) fprintf(stderr, "%s", pbuf);
}
tprintf("\n");
remaining_cg = sblock.fs_ncg - cylno;
/*
* If there are more than 300 cylinder groups still to be
* initialized, print a "." for every 50 cylinder groups.
*/
if (remaining_cg > 300) {
tprintf(gettext("Initializing cylinder groups:\n"));
do_dot = 1;
}
/*
* Now initialize all cylinder groups between the first ten
* and the last ten.
*
* If the number of cylinder groups was less than 10, all of the
* cylinder group offsets would have printed in the last loop
* and cylno will already be equal to sblock.fs_ncg and so this
* loop will not be entered. If there are less than 20 cylinder
* groups, cylno is already less than fs_ncg - 10, so this loop
* won't be entered in that case either.
*/
i = 0;
for (; cylno < sblock.fs_ncg - 10; cylno++) {
if ((grow == 0) || (cylno >= grow_fs_ncg))
initcg(cylno);
if (do_dot && cylno % 50 == 0) {
tprintf(".");
i++;
if (i == WIDTH - 1) {
tprintf("\n");
i = 0;
}
}
}
/*
* Now print the cylinder group offsets for the last 10
* cylinder groups, if any are left.
*/
if (do_dot) {
tprintf(gettext(
"\nsuper-block backups for last 10 cylinder groups at:\n"));
}
for (width = 0; cylno < sblock.fs_ncg; cylno++) {
if ((grow == 0) || (cylno >= grow_fs_ncg))
initcg(cylno);
num = fsbtodb(&sblock, (uint64_t)cgsblock(&sblock, cylno));
if (Nflag && retry) {
skip_this_sb = 0;
rdfs((diskaddr_t)num, sbsize, (char *)&altsblock);
ret = checksblock(altsblock, 1);
if (ret) {
skip_this_sb = 1;
invalid_sb_cnt++;
dprintf(("DeBuG checksblock() failed - error :"
" %d for sb : %llu invalid_sb_cnt : %d\n",
ret, num, invalid_sb_cnt));
} else {
/*
* Though the superblock looks sane, verify if
* the fs_version in the superblock and the
* logic that we are using to arrive at the
* superblocks match.
*/
if (use_efi_dflts && altsblock.fs_version
!= UFS_EFISTYLE4NONEFI_VERSION_2) {
skip_this_sb = 1;
invalid_sb_cnt++;
}
}
if (invalid_sb_cnt >= INVALIDSBLIMIT) {
if (retry > 1) {
(void) fprintf(stderr, gettext(
"Error determining alternate "
"superblock locations\n"));
free(tmpbuf);
lockexit(32);
}
retry++;
use_efi_dflts = !use_efi_dflts;
free(tmpbuf);
goto retry_alternate_logic;
}
if (skip_this_sb)
continue;
}
/* Don't print ',' for the last superblock */
if (cylno == sblock.fs_ncg-1)
(void) sprintf(pbuf, " %llu", num);
else
(void) sprintf(pbuf, " %llu,", num);
plen = strlen(pbuf);
if ((width + plen) > (WIDTH - 1)) {
width = plen;
tprintf("\n");
} else {
width += plen;
}
if (Nflag && retry)
(void) strncat(tmpbuf, pbuf, strlen(pbuf));
else
(void) fprintf(stderr, "%s", pbuf);
}
tprintf("\n");
if (Nflag) {
if (retry)
(void) fprintf(stderr, "%s", tmpbuf);
free(tmpbuf);
lockexit(0);
}
free(tmpbuf);
if (grow)
goto grow50;
/*
* Now construct the initial file system,
* then write out the super-block.
*/
fsinit();
grow50:
/*
* write the superblock and csum information
*/
wtsb();
/*
* extend the last cylinder group in the original file system
*/
if (grow) {
extendcg(grow_fs_ncg-1);
wtsb();
}
/*
* Write out the duplicate super blocks to the first 10
* cylinder groups (or fewer, if there are fewer than 10
* cylinder groups).
*/
for (cylno = 0; cylno < sblock.fs_ncg && cylno < 10; cylno++)
awtfs(fsbtodb(&sblock, (uint64_t)cgsblock(&sblock, cylno)),
(int)sbsize, (char *)&sblock, SAVE);
/*
* Now write out duplicate super blocks to the remaining
* cylinder groups. In the case of multi-terabyte file
* systems, just write out the super block to the last ten
* cylinder groups (or however many are left).
*/
if (mtb == 'y') {
if (sblock.fs_ncg <= 10)
cylno = sblock.fs_ncg;
else if (sblock.fs_ncg <= 20)
cylno = 10;
else
cylno = sblock.fs_ncg - 10;
}
for (; cylno < sblock.fs_ncg; cylno++)
awtfs(fsbtodb(&sblock, (uint64_t)cgsblock(&sblock, cylno)),
(int)sbsize, (char *)&sblock, SAVE);
/*
* Flush out all the AIO writes we've done. It's not
* necessary to do this explicitly, but it's the only
* way to report any errors from those writes.
*/
flush_writes();
/*
* set clean flag
*/
if (grow)
sblock.fs_clean = grow_fs_clean;
else
sblock.fs_clean = FSCLEAN;
sblock.fs_time = mkfstime;
sblock.fs_state = FSOKAY - sblock.fs_time;
wtfs((diskaddr_t)(SBOFF / sectorsize), sbsize, (char *)&sblock);
isbad = 0;
if (fsync(fso) == -1) {
saverr = errno;
(void) fprintf(stderr,
gettext("mkfs: fsync failed on write disk: %s\n"),
strerror(saverr));
/* we're just cleaning up, so keep going */
}
if (close(fsi) == -1) {
saverr = errno;
(void) fprintf(stderr,
gettext("mkfs: close failed on read disk: %s\n"),
strerror(saverr));
/* we're just cleaning up, so keep going */
}
if (close(fso) == -1) {
saverr = errno;
(void) fprintf(stderr,
gettext("mkfs: close failed on write disk: %s\n"),
strerror(saverr));
/* we're just cleaning up, so keep going */
}
fsi = fso = -1;
#ifndef STANDALONE
lockexit(0);
#endif
return (0);
}
/*
* Figure out how big the partition we're dealing with is.
* The value returned is in disk blocks (sectors);
*/
static diskaddr_t
get_max_size(int fd)
{
struct extvtoc vtoc;
dk_gpt_t *efi_vtoc;
diskaddr_t slicesize;
int index = read_extvtoc(fd, &vtoc);
if (index >= 0) {
label_type = LABEL_TYPE_VTOC;
} else {
if (index == VT_ENOTSUP || index == VT_ERROR) {
/* it might be an EFI label */
index = efi_alloc_and_read(fd, &efi_vtoc);
label_type = LABEL_TYPE_EFI;
}
}
if (index < 0) {
switch (index) {
case VT_ERROR:
break;
case VT_EIO:
errno = EIO;
break;
case VT_EINVAL:
errno = EINVAL;
}
perror(gettext("Can not determine partition size"));
lockexit(32);
}
if (label_type == LABEL_TYPE_EFI) {
slicesize = efi_vtoc->efi_parts[index].p_size;
efi_free(efi_vtoc);
} else {
/*
* In the vtoc struct, p_size is a 32-bit signed quantity.
* In the dk_gpt struct (efi's version of the vtoc), p_size
* is an unsigned 64-bit quantity. By casting the vtoc's
* psize to an unsigned 32-bit quantity, it will be copied
* to 'slicesize' (an unsigned 64-bit diskaddr_t) without
* sign extension.
*/
slicesize = (uint32_t)vtoc.v_part[index].p_size;
}
dprintf(("DeBuG get_max_size index = %d, p_size = %lld, dolimit = %d\n",
index, slicesize, (slicesize > FS_MAX)));
/*
* The next line limits a UFS file system to the maximum
* supported size.
*/
if (slicesize > FS_MAX)
return (FS_MAX);
return (slicesize);
}
static long
get_max_track_size(int fd)
{
struct dk_cinfo ci;
long track_size = -1;
if (ioctl(fd, DKIOCINFO, &ci) == 0) {
track_size = ci.dki_maxtransfer * DEV_BSIZE;
}
if ((track_size < 0)) {
int error = 0;
int maxphys;
int gotit = 0;
gotit = fsgetmaxphys(&maxphys, &error);
if (gotit) {
track_size = MIN(MB, maxphys);
} else {
(void) fprintf(stderr, gettext(
"Warning: Could not get system value for maxphys. The value for\n"
"maxcontig will default to 1MB.\n"));
track_size = MB;
}
}
return (track_size);
}
/*
* Initialize a cylinder group.
*/
static void
initcg(int cylno)
{
diskaddr_t cbase, d;
diskaddr_t dlower; /* last data block before cg metadata */
diskaddr_t dupper; /* first data block after cg metadata */
diskaddr_t dmax;
int64_t i;
struct csum *cs;
struct dinode *inode_buffer;
int size;
/*
* Variables used to store intermediate results as a part of
* the internal implementation of the cbtocylno() macros.
*/
diskaddr_t bno; /* UFS block number (not sector number) */
int cbcylno; /* current cylinder number */
int cbcylno_sect; /* sector offset within cylinder */
int cbsect_incr; /* amount to increment sector offset */
/*
* Variables used to store intermediate results as a part of
* the internal implementation of the cbtorpos() macros.
*/
short *cgblks; /* pointer to array of free blocks in cg */
int trackrpos; /* tmp variable for rotation position */
int trackoff; /* offset within a track */
int trackoff_incr; /* amount to increment trackoff */
int rpos; /* rotation position of current block */
int rpos_incr; /* amount to increment rpos per block */
union cgun *icgun; /* local pointer to a cg summary block */
#define icg (icgun->cg)
icgun = (union cgun *)getbuf(&cgsumbuf, sizeof (union cgun));
/*
* Determine block bounds for cylinder group.
* Allow space for super block summary information in first
* cylinder group.
*/
cbase = cgbase(&sblock, cylno);
dmax = cbase + sblock.fs_fpg;
if (dmax > sblock.fs_size) /* last cg may be smaller than normal */
dmax = sblock.fs_size;
dlower = cgsblock(&sblock, cylno) - cbase;
dupper = cgdmin(&sblock, cylno) - cbase;
if (cylno == 0)
dupper += howmany(sblock.fs_cssize, sblock.fs_fsize);
cs = fscs + cylno;
icg.cg_time = mkfstime;
icg.cg_magic = CG_MAGIC;
icg.cg_cgx = cylno;
/* last one gets whatever's left */
if (cylno == sblock.fs_ncg - 1)
icg.cg_ncyl = sblock.fs_ncyl - (sblock.fs_cpg * cylno);
else
icg.cg_ncyl = sblock.fs_cpg;
icg.cg_niblk = sblock.fs_ipg;
icg.cg_ndblk = dmax - cbase;
icg.cg_cs.cs_ndir = 0;
icg.cg_cs.cs_nffree = 0;
icg.cg_cs.cs_nbfree = 0;
icg.cg_cs.cs_nifree = 0;
icg.cg_rotor = 0;
icg.cg_frotor = 0;
icg.cg_irotor = 0;
icg.cg_btotoff = &icg.cg_space[0] - (uchar_t *)(&icg.cg_link);
icg.cg_boff = icg.cg_btotoff + sblock.fs_cpg * sizeof (long);
icg.cg_iusedoff = icg.cg_boff +
sblock.fs_cpg * sblock.fs_nrpos * sizeof (short);
icg.cg_freeoff = icg.cg_iusedoff + howmany(sblock.fs_ipg, NBBY);
icg.cg_nextfreeoff = icg.cg_freeoff +
howmany(sblock.fs_cpg * sblock.fs_spc / NSPF(&sblock), NBBY);
for (i = 0; i < sblock.fs_frag; i++) {
icg.cg_frsum[i] = 0;
}
bzero((caddr_t)cg_inosused(&icg), icg.cg_freeoff - icg.cg_iusedoff);
icg.cg_cs.cs_nifree += sblock.fs_ipg;
if (cylno == 0)
for (i = 0; i < UFSROOTINO; i++) {
setbit(cg_inosused(&icg), i);
icg.cg_cs.cs_nifree--;
}
/*
* Initialize all the inodes in the cylinder group using
* random numbers.
*/
size = sblock.fs_ipg * sizeof (struct dinode);
inode_buffer = (struct dinode *)getbuf(&inodebuf, size);
for (i = 0; i < sblock.fs_ipg; i++) {
IRANDOMIZE(&(inode_buffer[i].di_ic));
}
/*
* Write all inodes in a single write for performance.
*/
awtfs(fsbtodb(&sblock, (uint64_t)cgimin(&sblock, cylno)), (int)size,
(char *)inode_buffer, RELEASE);
bzero((caddr_t)cg_blktot(&icg), icg.cg_boff - icg.cg_btotoff);
bzero((caddr_t)cg_blks(&sblock, &icg, 0),
icg.cg_iusedoff - icg.cg_boff);
bzero((caddr_t)cg_blksfree(&icg), icg.cg_nextfreeoff - icg.cg_freeoff);
if (cylno > 0) {
for (d = 0; d < dlower; d += sblock.fs_frag) {
setblock(&sblock, cg_blksfree(&icg), d/sblock.fs_frag);
icg.cg_cs.cs_nbfree++;
cg_blktot(&icg)[cbtocylno(&sblock, d)]++;
cg_blks(&sblock, &icg, cbtocylno(&sblock, d))
[cbtorpos(&sblock, d)]++;
}
sblock.fs_dsize += dlower;
}
sblock.fs_dsize += icg.cg_ndblk - dupper;
if ((i = dupper % sblock.fs_frag) != 0) {
icg.cg_frsum[sblock.fs_frag - i]++;
for (d = dupper + sblock.fs_frag - i; dupper < d; dupper++) {
setbit(cg_blksfree(&icg), dupper);
icg.cg_cs.cs_nffree++;
}
}
/*
* WARNING: The following code is somewhat confusing, but
* results in a substantial performance improvement in mkfs.
*
* Instead of using cbtocylno() and cbtorpos() macros, we
* keep track of all the intermediate state of those macros
* in some variables. This allows simple addition to be
* done to calculate the results as we step through the
* blocks in an orderly fashion instead of the slower
* multiplication and division the macros are forced to
* used so they can support random input. (Multiplication,
* division, and remainder operations typically take about
* 10x as many processor cycles as other operations.)
*
* The basic idea is to take code:
*
* for (x = starting_x; x < max; x++)
* y = (x * c) / z
*
* and rewrite it to take advantage of the fact that
* the variable x is incrementing in an orderly way:
*
* intermediate = starting_x * c
* yval = intermediate / z
* for (x = starting_x; x < max; x++) {
* y = yval;
* intermediate += c
* if (intermediate > z) {
* yval++;
* intermediate -= z
* }
* }
*
* Performance has improved as much as 4X using this code.
*/
/*
* Initialize the starting points for all the cbtocylno()
* macro variables and figure out the increments needed each
* time through the loop.
*/
cbcylno_sect = dupper * NSPF(&sblock);
cbsect_incr = sblock.fs_frag * NSPF(&sblock);
cbcylno = cbcylno_sect / sblock.fs_spc;
cbcylno_sect %= sblock.fs_spc;
cgblks = cg_blks(&sblock, &icg, cbcylno);
bno = dupper / sblock.fs_frag;
/*
* Initialize the starting points for all the cbtorpos()
* macro variables and figure out the increments needed each
* time through the loop.
*
* It's harder to simplify the cbtorpos() macro if there were
* alternate sectors specified (or if they previously existed
* in the growfs case). Since this is rare, we just revert to
* using the macros in this case and skip the variable setup.
*/
if (!spc_flag) {
trackrpos = (cbcylno_sect % sblock.fs_nsect) * sblock.fs_nrpos;
rpos = trackrpos / sblock.fs_nsect;
trackoff = trackrpos % sblock.fs_nsect;
trackoff_incr = cbsect_incr * sblock.fs_nrpos;
rpos_incr = (trackoff_incr / sblock.fs_nsect) % sblock.fs_nrpos;
trackoff_incr = trackoff_incr % sblock.fs_nsect;
}
/*
* Loop through all the blocks, marking them free and
* updating totals kept in the superblock and cg summary.
*/
for (d = dupper; d + sblock.fs_frag <= dmax - cbase; ) {
setblock(&sblock, cg_blksfree(&icg), bno);
icg.cg_cs.cs_nbfree++;
cg_blktot(&icg)[cbcylno]++;
if (!spc_flag)
cgblks[rpos]++;
else
cg_blks(&sblock, &icg, cbtocylno(&sblock, d))
[cbtorpos(&sblock, d)]++;
d += sblock.fs_frag;
bno++;
/*
* Increment the sector offset within the cylinder
* for the cbtocylno() macro reimplementation. If
* we're beyond the end of the cylinder, update the
* cylinder number, calculate the offset in the
* new cylinder, and update the cgblks pointer
* to the next rotational position.
*/
cbcylno_sect += cbsect_incr;
if (cbcylno_sect >= sblock.fs_spc) {
cbcylno++;
cbcylno_sect -= sblock.fs_spc;
cgblks += sblock.fs_nrpos;
}
/*
* If there aren't alternate sectors, increment the
* rotational position variables for the cbtorpos()
* reimplementation. Note that we potentially
* increment rpos twice. Once by rpos_incr, and one
* more time when we wrap to a new track because
* trackoff >= fs_nsect.
*/
if (!spc_flag) {
trackoff += trackoff_incr;
rpos += rpos_incr;
if (trackoff >= sblock.fs_nsect) {
trackoff -= sblock.fs_nsect;
rpos++;
}
if (rpos >= sblock.fs_nrpos)
rpos -= sblock.fs_nrpos;
}
}
if (d < dmax - cbase) {
icg.cg_frsum[dmax - cbase - d]++;
for (; d < dmax - cbase; d++) {
setbit(cg_blksfree(&icg), d);
icg.cg_cs.cs_nffree++;
}
}
sblock.fs_cstotal.cs_ndir += icg.cg_cs.cs_ndir;
sblock.fs_cstotal.cs_nffree += icg.cg_cs.cs_nffree;
sblock.fs_cstotal.cs_nbfree += icg.cg_cs.cs_nbfree;
sblock.fs_cstotal.cs_nifree += icg.cg_cs.cs_nifree;
*cs = icg.cg_cs;
awtfs(fsbtodb(&sblock, (uint64_t)cgtod(&sblock, cylno)),
sblock.fs_bsize, (char *)&icg, RELEASE);
}
/*
* initialize the file system
*/
struct inode node;
#define LOSTDIR
#ifdef LOSTDIR
#define PREDEFDIR 3
#else
#define PREDEFDIR 2
#endif
struct direct root_dir[] = {
{ UFSROOTINO, sizeof (struct direct), 1, "." },
{ UFSROOTINO, sizeof (struct direct), 2, ".." },
#ifdef LOSTDIR
{ LOSTFOUNDINO, sizeof (struct direct), 10, "lost+found" },
#endif
};
#ifdef LOSTDIR
struct direct lost_found_dir[] = {
{ LOSTFOUNDINO, sizeof (struct direct), 1, "." },
{ UFSROOTINO, sizeof (struct direct), 2, ".." },
{ 0, DIRBLKSIZ, 0, 0 },
};
#endif
char buf[MAXBSIZE];
static void
fsinit()
{
int i;
/*
* initialize the node
*/
node.i_atime = mkfstime;
node.i_mtime = mkfstime;
node.i_ctime = mkfstime;
#ifdef LOSTDIR
/*
* create the lost+found directory
*/
(void) makedir(lost_found_dir, 2);
for (i = DIRBLKSIZ; i < sblock.fs_bsize; i += DIRBLKSIZ) {
bcopy(&lost_found_dir[2], &buf[i], DIRSIZ(&lost_found_dir[2]));
}
node.i_number = LOSTFOUNDINO;
node.i_smode = IFDIR | 0700;
node.i_nlink = 2;
node.i_size = sblock.fs_bsize;
node.i_db[0] = alloc((int)node.i_size, node.i_mode);
node.i_blocks = btodb(fragroundup(&sblock, (int)node.i_size));
IRANDOMIZE(&node.i_ic);
wtfs(fsbtodb(&sblock, (uint64_t)node.i_db[0]), (int)node.i_size, buf);
iput(&node);
#endif
/*
* create the root directory
*/
node.i_number = UFSROOTINO;
node.i_mode = IFDIR | UMASK;
node.i_nlink = PREDEFDIR;
node.i_size = makedir(root_dir, PREDEFDIR);
node.i_db[0] = alloc(sblock.fs_fsize, node.i_mode);
/* i_size < 2GB because we are initializing the file system */
node.i_blocks = btodb(fragroundup(&sblock, (int)node.i_size));
IRANDOMIZE(&node.i_ic);
wtfs(fsbtodb(&sblock, (uint64_t)node.i_db[0]), sblock.fs_fsize, buf);
iput(&node);
}
/*
* construct a set of directory entries in "buf".
* return size of directory.
*/
static int
makedir(struct direct *protodir, int entries)
{
char *cp;
int i;
ushort_t spcleft;
spcleft = DIRBLKSIZ;
for (cp = buf, i = 0; i < entries - 1; i++) {
protodir[i].d_reclen = DIRSIZ(&protodir[i]);
bcopy(&protodir[i], cp, protodir[i].d_reclen);
cp += protodir[i].d_reclen;
spcleft -= protodir[i].d_reclen;
}
protodir[i].d_reclen = spcleft;
bcopy(&protodir[i], cp, DIRSIZ(&protodir[i]));
return (DIRBLKSIZ);
}
/*
* allocate a block or frag
*/
static daddr32_t
alloc(int size, int mode)
{
int i, frag;
daddr32_t d;
rdfs(fsbtodb(&sblock, (uint64_t)cgtod(&sblock, 0)), sblock.fs_cgsize,
(char *)&acg);
if (acg.cg_magic != CG_MAGIC) {
(void) fprintf(stderr, gettext("cg 0: bad magic number\n"));
lockexit(32);
}
if (acg.cg_cs.cs_nbfree == 0) {
(void) fprintf(stderr,
gettext("first cylinder group ran out of space\n"));
lockexit(32);
}
for (d = 0; d < acg.cg_ndblk; d += sblock.fs_frag)
if (isblock(&sblock, cg_blksfree(&acg), d / sblock.fs_frag))
goto goth;
(void) fprintf(stderr,
gettext("internal error: can't find block in cyl 0\n"));
lockexit(32);
goth:
clrblock(&sblock, cg_blksfree(&acg), d / sblock.fs_frag);
acg.cg_cs.cs_nbfree--;
sblock.fs_cstotal.cs_nbfree--;
fscs[0].cs_nbfree--;
if (mode & IFDIR) {
acg.cg_cs.cs_ndir++;
sblock.fs_cstotal.cs_ndir++;
fscs[0].cs_ndir++;
}
cg_blktot(&acg)[cbtocylno(&sblock, d)]--;
cg_blks(&sblock, &acg, cbtocylno(&sblock, d))[cbtorpos(&sblock, d)]--;
if (size != sblock.fs_bsize) {
frag = howmany(size, sblock.fs_fsize);
fscs[0].cs_nffree += sblock.fs_frag - frag;
sblock.fs_cstotal.cs_nffree += sblock.fs_frag - frag;
acg.cg_cs.cs_nffree += sblock.fs_frag - frag;
acg.cg_frsum[sblock.fs_frag - frag]++;
for (i = frag; i < sblock.fs_frag; i++)
setbit(cg_blksfree(&acg), d + i);
}
wtfs(fsbtodb(&sblock, (uint64_t)cgtod(&sblock, 0)), sblock.fs_cgsize,
(char *)&acg);
return (d);
}
/*
* Allocate an inode on the disk
*/
static void
iput(struct inode *ip)
{
struct dinode buf[MAXINOPB];
diskaddr_t d;
rdfs(fsbtodb(&sblock, (uint64_t)cgtod(&sblock, 0)), sblock.fs_cgsize,
(char *)&acg);
if (acg.cg_magic != CG_MAGIC) {
(void) fprintf(stderr, gettext("cg 0: bad magic number\n"));
lockexit(32);
}
acg.cg_cs.cs_nifree--;
setbit(cg_inosused(&acg), ip->i_number);
wtfs(fsbtodb(&sblock, (uint64_t)cgtod(&sblock, 0)), sblock.fs_cgsize,
(char *)&acg);
sblock.fs_cstotal.cs_nifree--;
fscs[0].cs_nifree--;
if ((int)ip->i_number >= sblock.fs_ipg * sblock.fs_ncg) {
(void) fprintf(stderr,
gettext("fsinit: inode value out of range (%d).\n"),
ip->i_number);
lockexit(32);
}
d = fsbtodb(&sblock, (uint64_t)itod(&sblock, (int)ip->i_number));
rdfs(d, sblock.fs_bsize, (char *)buf);
buf[itoo(&sblock, (int)ip->i_number)].di_ic = ip->i_ic;
wtfs(d, sblock.fs_bsize, (char *)buf);
}
/*
* getbuf() -- Get a buffer for use in an AIO operation. Buffer
* is zero'd the first time returned, left with whatever
* was in memory after that. This function actually gets
* enough memory the first time it's called to support
* MAXBUF buffers like a slab allocator. When all the
* buffers are in use, it waits for an aio to complete
* and make a buffer available.
*
* Never returns an error. Either succeeds or exits.
*/
static char *
getbuf(bufhdr *bufhead, int size)
{
bufhdr *pbuf;
bufhdr *prev;
int i;
int buf_size, max_bufs;
/*
* Initialize all the buffers
*/
if (bufhead->head == NULL) {
/*
* round up the size of our buffer header to a
* 16 byte boundary so the address we return to
* the caller is "suitably aligned".
*/
bufhdrsize = (sizeof (bufhdr) + 15) & ~15;
/*
* Add in our header to the buffer and round it all up to
* a 16 byte boundry so each member of the slab is aligned.
*/
buf_size = (size + bufhdrsize + 15) & ~15;
/*
* Limit number of buffers to lesser of MAXBUFMEM's worth
* or MAXBUF, whichever is less.
*/
max_bufs = MAXBUFMEM / buf_size;
if (max_bufs > MAXBUF)
max_bufs = MAXBUF;
pbuf = (bufhdr *)calloc(max_bufs, buf_size);
if (pbuf == NULL) {
perror("calloc");
lockexit(32);
}
bufhead->head = bufhead;
prev = bufhead;
for (i = 0; i < max_bufs; i++) {
pbuf->head = bufhead;
prev->next = pbuf;
prev = pbuf;
pbuf = (bufhdr *)((char *)pbuf + buf_size);
}
}
/*
* Get an available buffer, waiting for I/O if necessary
*/
wait_for_write(NOBLOCK);
while (bufhead->next == NULL)
wait_for_write(BLOCK);
/*
* Take the buffer off the list
*/
pbuf = bufhead->next;
bufhead->next = pbuf->next;
pbuf->next = NULL;
/*
* return the empty buffer space just past the header
*/
return ((char *)pbuf + bufhdrsize);
}
/*
* freebuf() -- Free a buffer gotten previously through getbuf.
* Puts the buffer back on the appropriate list for
* later use. Never calls free().
*
* Assumes that SIGINT is blocked.
*/
static void
freebuf(char *buf)
{
bufhdr *pbuf;
bufhdr *bufhead;
/*
* get the header for this buffer
*/
pbuf = (bufhdr *)(buf - bufhdrsize);
/*
* Put it back on the list of available buffers
*/
bufhead = pbuf->head;
pbuf->next = bufhead->next;
bufhead->next = pbuf;
}
/*
* freetrans() -- Free a transaction gotten previously through getaiop.
* Puts the transaction struct back on the appropriate list for
* later use. Never calls free().
*
* Assumes that SIGINT is blocked.
*/
static void
freetrans(aio_trans *transp)
{
/*
* free the buffer associated with this AIO if needed
*/
if (transp->release == RELEASE)
freebuf(transp->buffer);
/*
* Put transaction on the free list
*/
transp->next = results.trans;
results.trans = transp;
}
/*
* wait_for_write() -- Wait for an aio write to complete. Return
* the transaction structure for that write.
*
* Blocks SIGINT if necessary.
*/
aio_trans *
wait_for_write(int block)
{
aio_trans *transp;
aio_result_t *resultp;
static struct timeval zero_wait = { 0, 0 };
sigset_t old_mask;
/*
* If we know there aren't any outstanding transactions, just return
*/
if (results.outstanding == 0)
return ((aio_trans *) 0);
block_sigint(&old_mask);
resultp = aiowait(block ? NULL : &zero_wait);
if (resultp == NULL ||
(resultp == (aio_result_t *)-1 && errno == EINVAL)) {
unblock_sigint(&old_mask);
return ((aio_trans *) 0);
}
results.outstanding--;
transp = (aio_trans *)resultp;
if (resultp->aio_return != transp->size) {
if (resultp->aio_return == -1) {
/*
* The aiowrite() may have failed because the
* kernel didn't have enough memory to do the job.
* Flush all pending writes and try a normal
* write(). wtfs_breakup() will call exit if it
* fails, so we don't worry about errors here.
*/
flush_writes();
wtfs_breakup(transp->bno, transp->size, transp->buffer);
} else {
(void) fprintf(stderr, gettext(
"short write (%d of %d bytes) on sector %lld\n"),
resultp->aio_return, transp->size,
transp->bno);
/*
* Don't unblock SIGINT, to avoid potential
* looping due to queued interrupts and
* error handling.
*/
lockexit(32);
}
}
resultp->aio_return = 0;
freetrans(transp);
unblock_sigint(&old_mask);
return (transp);
}
/*
* flush_writes() -- flush all the outstanding aio writes.
*/
static void
flush_writes(void)
{
while (wait_for_write(BLOCK))
;
}
/*
* get_aiop() -- find and return an aio_trans structure on which a new
* aio can be done. Blocks on aiowait() if needed. Reaps
* all outstanding completed aio's.
*
* Assumes that SIGINT is blocked.
*/
aio_trans *
get_aiop()
{
int i;
aio_trans *transp;
aio_trans *prev;
/*
* initialize aio stuff
*/
if (!aio_inited) {
aio_inited = 1;
results.maxpend = 0;
results.outstanding = 0;
results.max = MAXAIO;
results.trans = (aio_trans *)calloc(results.max,
sizeof (aio_trans));
if (results.trans == NULL) {
perror("calloc");
lockexit(32);
}
/*
* Initialize the linked list of aio transaction
* structures. Note that the final "next" pointer
* will be NULL since we got the buffer from calloc().
*/
prev = results.trans;
for (i = 1; i < results.max; i++) {
prev->next = &(results.trans[i]);
prev = prev->next;
}
}
wait_for_write(NOBLOCK);
while (results.trans == NULL)
wait_for_write(BLOCK);
transp = results.trans;
results.trans = results.trans->next;
transp->next = 0;
transp->resultbuf.aio_return = AIO_INPROGRESS;
return (transp);
}
/*
* read a block from the file system
*/
static void
rdfs(diskaddr_t bno, int size, char *bf)
{
int n, saverr;
/*
* In case we need any data that's pending in an aiowrite(),
* we wait for them all to complete before doing a read.
*/
flush_writes();
/*
* Note: the llseek() can succeed, even if the offset is out of range.
* It's not until the file i/o operation (the read()) that one knows
* for sure if the raw device can handle the offset.
*/
if (llseek(fsi, (offset_t)bno * sectorsize, 0) < 0) {
saverr = errno;
(void) fprintf(stderr,
gettext("seek error on sector %lld: %s\n"),
bno, strerror(saverr));
lockexit(32);
}
n = read(fsi, bf, size);
if (n != size) {
saverr = errno;
if (n == -1)
(void) fprintf(stderr,
gettext("read error on sector %lld: %s\n"),
bno, strerror(saverr));
else
(void) fprintf(stderr, gettext(
"short read (%d of %d bytes) on sector %lld\n"),
n, size, bno);
lockexit(32);
}
}
/*
* write a block to the file system
*/
static void
wtfs(diskaddr_t bno, int size, char *bf)
{
int n, saverr;
if (fso == -1)
return;
/*
* Note: the llseek() can succeed, even if the offset is out of range.
* It's not until the file i/o operation (the write()) that one knows
* for sure if the raw device can handle the offset.
*/
if (llseek(fso, (offset_t)bno * sectorsize, 0) < 0) {
saverr = errno;
(void) fprintf(stderr,
gettext("seek error on sector %lld: %s\n"),
bno, strerror(saverr));
lockexit(32);
}
if (Nflag)
return;
n = write(fso, bf, size);
if (n != size) {
saverr = errno;
if (n == -1)
(void) fprintf(stderr,
gettext("write error on sector %lld: %s\n"),
bno, strerror(saverr));
else
(void) fprintf(stderr, gettext(
"short write (%d of %d bytes) on sector %lld\n"),
n, size, bno);
lockexit(32);
}
}
/*
* write a block to the file system -- buffered with aio
*/
static void
awtfs(diskaddr_t bno, int size, char *bf, int release)
{
int n;
aio_trans *transp;
sigset_t old_mask;
if (fso == -1)
return;
/*
* We need to keep things consistent if we get interrupted,
* so defer any expected interrupts for the time being.
*/
block_sigint(&old_mask);
if (Nflag) {
if (release == RELEASE)
freebuf(bf);
} else {
transp = get_aiop();
transp->bno = bno;
transp->buffer = bf;
transp->size = size;
transp->release = release;
n = aiowrite(fso, bf, size, (off_t)bno * sectorsize,
SEEK_SET, &transp->resultbuf);
if (n < 0) {
/*
* The aiowrite() may have failed because the
* kernel didn't have enough memory to do the job.
* Flush all pending writes and try a normal
* write(). wtfs_breakup() will call exit if it
* fails, so we don't worry about errors here.
*/
flush_writes();
wtfs_breakup(transp->bno, transp->size, transp->buffer);
freetrans(transp);
} else {
/*
* Keep track of our pending writes.
*/
results.outstanding++;
if (results.outstanding > results.maxpend)
results.maxpend = results.outstanding;
}
}
unblock_sigint(&old_mask);
}
/*
* write a block to the file system, but break it up into sbsize
* chunks to avoid forcing a large amount of memory to be locked down.
* Only used as a fallback when an aio write has failed.
*/
static void
wtfs_breakup(diskaddr_t bno, int size, char *bf)
{
int n, saverr;
int wsize;
int block_incr = sbsize / sectorsize;
if (size < sbsize)
wsize = size;
else
wsize = sbsize;
n = 0;
while (size) {
/*
* Note: the llseek() can succeed, even if the offset is
* out of range. It's not until the file i/o operation
* (the write()) that one knows for sure if the raw device
* can handle the offset.
*/
if (llseek(fso, (offset_t)bno * sectorsize, 0) < 0) {
saverr = errno;
(void) fprintf(stderr,
gettext("seek error on sector %lld: %s\n"),
bno, strerror(saverr));
lockexit(32);
}
n = write(fso, bf, wsize);
if (n == -1) {
saverr = errno;
(void) fprintf(stderr,
gettext("write error on sector %lld: %s\n"),
bno, strerror(saverr));
lockexit(32);
}
if (n != wsize) {
saverr = errno;
(void) fprintf(stderr, gettext(
"short write (%d of %d bytes) on sector %lld\n"),
n, size, bno);
lockexit(32);
}
bno += block_incr;
bf += wsize;
size -= wsize;
if (size < wsize)
wsize = size;
}
}
/*
* check if a block is available
*/
static int
isblock(struct fs *fs, unsigned char *cp, int h)
{
unsigned char mask;
switch (fs->fs_frag) {
case 8:
return (cp[h] == 0xff);
case 4:
mask = 0x0f << ((h & 0x1) << 2);
return ((cp[h >> 1] & mask) == mask);
case 2:
mask = 0x03 << ((h & 0x3) << 1);
return ((cp[h >> 2] & mask) == mask);
case 1:
mask = 0x01 << (h & 0x7);
return ((cp[h >> 3] & mask) == mask);
default:
(void) fprintf(stderr, "isblock bad fs_frag %d\n", fs->fs_frag);
return (0);
}
}
/*
* take a block out of the map
*/
static void
clrblock(struct fs *fs, unsigned char *cp, int h)
{
switch ((fs)->fs_frag) {
case 8:
cp[h] = 0;
return;
case 4:
cp[h >> 1] &= ~(0x0f << ((h & 0x1) << 2));
return;
case 2:
cp[h >> 2] &= ~(0x03 << ((h & 0x3) << 1));
return;
case 1:
cp[h >> 3] &= ~(0x01 << (h & 0x7));
return;
default:
(void) fprintf(stderr,
gettext("clrblock: bad fs_frag value %d\n"), fs->fs_frag);
return;
}
}
/*
* put a block into the map
*/
static void
setblock(struct fs *fs, unsigned char *cp, int h)
{
switch (fs->fs_frag) {
case 8:
cp[h] = 0xff;
return;
case 4:
cp[h >> 1] |= (0x0f << ((h & 0x1) << 2));
return;
case 2:
cp[h >> 2] |= (0x03 << ((h & 0x3) << 1));
return;
case 1:
cp[h >> 3] |= (0x01 << (h & 0x7));
return;
default:
(void) fprintf(stderr,
gettext("setblock: bad fs_frag value %d\n"), fs->fs_frag);
return;
}
}
static void
usage()
{
(void) fprintf(stderr,
gettext("ufs usage: mkfs [-F FSType] [-V] [-m] [-o options] "
"special " /* param 0 */
"size(sectors) \\ \n")); /* param 1 */
(void) fprintf(stderr,
"[nsect " /* param 2 */
"ntrack " /* param 3 */
"bsize " /* param 4 */
"fragsize " /* param 5 */
"cpg " /* param 6 */
"free " /* param 7 */
"rps " /* param 8 */
"nbpi " /* param 9 */
"opt " /* param 10 */
"apc " /* param 11 */
"gap " /* param 12 */
"nrpos " /* param 13 */
"maxcontig " /* param 14 */
"mtb]\n"); /* param 15 */
(void) fprintf(stderr,
gettext(" -m : dump fs cmd line used to make this partition\n"
" -V :print this command line and return\n"
" -o :ufs options: :nsect=%d,ntrack=%d,bsize=%d,fragsize=%d\n"
" -o :ufs options: :cgsize=%d,free=%d,rps=%d,nbpi=%d,opt=%c\n"
" -o :ufs options: :apc=%d,gap=%d,nrpos=%d,maxcontig=%d\n"
" -o :ufs options: :mtb=%c,calcsb,calcbinsb\n"
"NOTE that all -o suboptions: must be separated only by commas so as to\n"
"be parsed as a single argument\n"),
nsect, ntrack, bsize, fragsize, cpg, sblock.fs_minfree, rps,
nbpi, opt, apc, (rotdelay == -1) ? 0 : rotdelay,
sblock.fs_nrpos, maxcontig, mtb);
lockexit(32);
}
/*ARGSUSED*/
static void
dump_fscmd(char *fsys, int fsi)
{
int64_t used, bpcg, inospercg;
int64_t nbpi;
uint64_t nbytes64;
bzero((char *)&sblock, sizeof (sblock));
rdfs((diskaddr_t)SBLOCK, SBSIZE, (char *)&sblock);
/*
* ensure a valid file system and if not, exit with error or else
* we will end up computing block numbers etc and dividing by zero
* which will cause floating point errors in this routine.
*/
if ((sblock.fs_magic != FS_MAGIC) &&
(sblock.fs_magic != MTB_UFS_MAGIC)) {
(void) fprintf(stderr, gettext(
"[not currently a valid file system - bad superblock]\n"));
lockexit(32);
}
if (sblock.fs_magic == FS_MAGIC &&
(sblock.fs_version != UFS_EFISTYLE4NONEFI_VERSION_2 &&
sblock.fs_version != UFS_VERSION_MIN)) {
(void) fprintf(stderr, gettext(
"Unknown version of UFS format: %d\n"), sblock.fs_version);
lockexit(32);
}
if (sblock.fs_magic == MTB_UFS_MAGIC &&
(sblock.fs_version > MTB_UFS_VERSION_1 ||
sblock.fs_version < MTB_UFS_VERSION_MIN)) {
(void) fprintf(stderr, gettext(
"Unknown version of UFS format: %d\n"), sblock.fs_version);
lockexit(32);
}
/*
* Compute a reasonable nbpi value.
* The algorithm for "used" is copied from code
* in main() verbatim.
* The nbpi equation is taken from main where the
* fs_ipg value is set for the last time. The INOPB(...) - 1
* is used to account for the roundup.
* The problem is that a range of nbpi values map to
* the same file system layout. So it is not possible
* to calculate the exact value specified when the file
* system was created. So instead we determine the top
* end of the range of values.
*/
bpcg = sblock.fs_spc * sectorsize;
inospercg = (int64_t)roundup(bpcg / sizeof (struct dinode),
INOPB(&sblock));
if (inospercg > MAXIpG(&sblock))
inospercg = MAXIpG(&sblock);
used = (int64_t)
(sblock.fs_iblkno + inospercg / INOPF(&sblock)) * NSPF(&sblock);
used *= sectorsize;
nbytes64 = (uint64_t)sblock.fs_cpg * bpcg - used;
/*
* The top end of the range of values for nbpi may not be
* a valid command line value for mkfs. Report the bottom
* end instead.
*/
nbpi = (int64_t)(nbytes64 / (sblock.fs_ipg));
(void) fprintf(stdout, gettext("mkfs -F ufs -o "), fsys);
(void) fprintf(stdout, "nsect=%d,ntrack=%d,",
sblock.fs_nsect, sblock.fs_ntrak);
(void) fprintf(stdout, "bsize=%d,fragsize=%d,cgsize=%d,free=%d,",
sblock.fs_bsize, sblock.fs_fsize, sblock.fs_cpg, sblock.fs_minfree);
(void) fprintf(stdout, "rps=%d,nbpi=%lld,opt=%c,apc=%d,gap=%d,",
sblock.fs_rps, nbpi, (sblock.fs_optim == FS_OPTSPACE) ? 's' : 't',
(sblock.fs_ntrak * sblock.fs_nsect) - sblock.fs_spc,
sblock.fs_rotdelay);
(void) fprintf(stdout, "nrpos=%d,maxcontig=%d,mtb=%c ",
sblock.fs_nrpos, sblock.fs_maxcontig,
((sblock.fs_magic == MTB_UFS_MAGIC) ? 'y' : 'n'));
(void) fprintf(stdout, "%s %lld\n", fsys,
fsbtodb(&sblock, sblock.fs_size));
bzero((char *)&sblock, sizeof (sblock));
}
/* number ************************************************************* */
/* */
/* Convert a numeric string arg to binary */
/* */
/* Args: d_value - default value, if have parse error */
/* param - the name of the argument, for error messages */
/* flags - parser state and what's allowed in the arg */
/* Global arg: string - pointer to command arg */
/* */
/* Valid forms: 123 | 123k | 123*123 | 123x123 */
/* */
/* Return: converted number */
/* */
/* ******************************************************************** */
static uint64_t
number(uint64_t d_value, char *param, int flags)
{
char *cs;
uint64_t n, t;
uint64_t cut = BIG / 10; /* limit to avoid overflow */
int minus = 0;
cs = string;
if (*cs == '-') {
minus = 1;
cs += 1;
}
if ((*cs < '0') || (*cs > '9')) {
goto bail_out;
}
n = 0;
while ((*cs >= '0') && (*cs <= '9') && (n <= cut)) {
n = n*10 + *cs++ - '0';
}
if (minus)
n = -n;
for (;;) {
switch (*cs++) {
case 'k':
if (flags & ALLOW_END_ONLY)
goto bail_out;
if (n > (BIG / 1024))
goto overflow;
n *= 1024;
continue;
case '*':
case 'x':
if (flags & ALLOW_END_ONLY)
goto bail_out;
string = cs;
t = number(d_value, param, flags);
if (n > (BIG / t))
goto overflow;
n *= t;
cs = string + 1; /* adjust for -- below */
/* recursion has read rest of expression */
/* FALLTHROUGH */
case ',':
case '\0':
cs--;
string = cs;
return (n);
case '%':
if (flags & ALLOW_END_ONLY)
goto bail_out;
if (flags & ALLOW_PERCENT) {
flags &= ~ALLOW_PERCENT;
flags |= ALLOW_END_ONLY;
continue;
}
goto bail_out;
case 'm':
if (flags & ALLOW_END_ONLY)
goto bail_out;
if (flags & ALLOW_MS1) {
flags &= ~ALLOW_MS1;
flags |= ALLOW_MS2;
continue;
}
goto bail_out;
case 's':
if (flags & ALLOW_END_ONLY)
goto bail_out;
if (flags & ALLOW_MS2) {
flags &= ~ALLOW_MS2;
flags |= ALLOW_END_ONLY;
continue;
}
goto bail_out;
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
overflow:
(void) fprintf(stderr,
gettext("mkfs: value for %s overflowed\n"),
param);
while ((*cs != '\0') && (*cs != ','))
cs++;
string = cs;
return (BIG);
default:
bail_out:
(void) fprintf(stderr, gettext(
"mkfs: bad numeric arg for %s: \"%s\"\n"),
param, string);
while ((*cs != '\0') && (*cs != ','))
cs++;
string = cs;
if (d_value != NO_DEFAULT) {
(void) fprintf(stderr,
gettext("mkfs: %s reset to default %lld\n"),
param, d_value);
return (d_value);
}
lockexit(2);
}
} /* never gets here */
}
/* match ************************************************************** */
/* */
/* Compare two text strings for equality */
/* */
/* Arg: s - pointer to string to match with a command arg */
/* Global arg: string - pointer to command arg */
/* */
/* Return: 1 if match, 0 if no match */
/* If match, also reset `string' to point to the text */
/* that follows the matching text. */
/* */
/* ******************************************************************** */
static int
match(char *s)
{
char *cs;
cs = string;
while (*cs++ == *s) {
if (*s++ == '\0') {
goto true;
}
}
if (*s != '\0') {
return (0);
}
true:
cs--;
string = cs;
return (1);
}
/*
* GROWFS ROUTINES
*/
/* ARGSUSED */
void
lockexit(int exitstatus)
{
if (Pflag) {
/* the probe mode neither changes nor locks the filesystem */
exit(exitstatus);
}
/*
* flush the dirty cylinder group
*/
if (inlockexit == 0) {
inlockexit = 1;
flcg();
}
if (aio_inited) {
flush_writes();
}
/*
* make sure the file system is unlocked before exiting
*/
if ((inlockexit == 1) && (!isbad)) {
inlockexit = 2;
ulockfs();
/*
* if logging was enabled, then re-enable it
*/
if (waslog) {
if (rl_log_control(fsys, _FIOLOGENABLE) != RL_SUCCESS) {
(void) fprintf(stderr, gettext(
"failed to re-enable logging\n"));
}
}
} else if (grow) {
if (isbad) {
(void) fprintf(stderr, gettext(
"Filesystem is currently inconsistent. It "
"must be repaired with fsck(1M)\nbefore being "
"used. Use the following command to "
"do this:\n\n\tfsck %s\n\n"), fsys);
if (ismounted) {
(void) fprintf(stderr, gettext(
"You will be told that the filesystem "
"is already mounted, and asked if you\n"
"wish to continue. Answer `yes' to "
"this question.\n\n"));
}
(void) fprintf(stderr, gettext(
"One problem should be reported, that the summary "
"information is bad.\nYou will then be asked if it "
"should be salvaged. Answer `yes' to\nthis "
"question.\n\n"));
}
if (ismounted) {
/*
* In theory, there's no way to get here without
* isbad also being set, but be robust in the
* face of future code changes.
*/
(void) fprintf(stderr, gettext(
"The filesystem is currently mounted "
"read-only and write-locked. "));
if (isbad) {
(void) fprintf(stderr, gettext(
"After\nrunning fsck, unlock the "
"filesystem and "));
} else {
(void) fprintf(stderr, gettext(
"Unlock the filesystem\nand "));
}
(void) fprintf(stderr, gettext(
"re-enable writing with\nthe following "
"command:\n\n\tlockfs -u %s\n\n"), directory);
}
}
exit(exitstatus);
}
void
randomgeneration()
{
int i;
struct dinode *dp;
/*
* always perform fsirand(1) function... newfs will notice that
* the inodes have been randomized and will not call fsirand itself
*/
for (i = 0, dp = zino; i < sblock.fs_inopb; ++i, ++dp)
IRANDOMIZE(&dp->di_ic);
}
/*
* Check the size of the summary information.
* Fields in sblock are not changed in this function.
*
* For an 8K filesystem block, the maximum number of cylinder groups is 16384.
* MAXCSBUFS {32} * 8K {FS block size}
* divided by (sizeof csum) {16}
*
* Note that MAXCSBUFS is not used in the kernel; as of Solaris 2.6 build 32,
* this is the only place where it's referenced.
*/
void
checksummarysize()
{
diskaddr_t dmax;
diskaddr_t dmin;
int64_t cg0frags;
int64_t cg0blocks;
int64_t maxncg;
int64_t maxfrags;
uint64_t fs_size;
uint64_t maxfs_blocks; /* filesystem blocks for max filesystem size */
/*
* compute the maximum summary info size
*/
dmin = cgdmin(&sblock, 0);
dmax = cgbase(&sblock, 0) + sblock.fs_fpg;
fs_size = (grow) ? grow_fs_size : sblock.fs_size;
if (dmax > fs_size)
dmax = fs_size;
cg0frags = dmax - dmin;
cg0blocks = cg0frags / sblock.fs_frag;
cg0frags = cg0blocks * sblock.fs_frag;
maxncg = (longlong_t)cg0blocks *
(longlong_t)(sblock.fs_bsize / sizeof (struct csum));
maxfs_blocks = FS_MAX;
if (maxncg > ((longlong_t)maxfs_blocks / (longlong_t)sblock.fs_fpg) + 1)
maxncg = ((longlong_t)maxfs_blocks /
(longlong_t)sblock.fs_fpg) + 1;
maxfrags = maxncg * (longlong_t)sblock.fs_fpg;
if (maxfrags > maxfs_blocks)
maxfrags = maxfs_blocks;
/*
* remember for later processing in extendsummaryinfo()
*/
if (test)
grow_sifrag = dmin + (cg0blocks * sblock.fs_frag);
if (testfrags == 0)
testfrags = cg0frags;
if (testforce)
if (testfrags > cg0frags) {
(void) fprintf(stderr,
gettext("Too many test frags (%lld); "
"try %lld\n"), testfrags, cg0frags);
lockexit(32);
}
/*
* if summary info is too large (too many cg's) tell the user and exit
*/
if ((longlong_t)sblock.fs_size > maxfrags) {
(void) fprintf(stderr, gettext(
"Too many cylinder groups with %llu sectors;\n try "
"increasing cgsize, or decreasing fssize to %llu\n"),
fsbtodb(&sblock, (uint64_t)sblock.fs_size),
fsbtodb(&sblock, (uint64_t)maxfrags));
lockexit(32);
}
}
/*
* checksblock() has two uses:
* - One is to sanity test the superblock and is used when newfs(1M)
* is invoked with the "-N" option. If any discrepancy was found,
* just return whatever error was found and do not exit.
* - the other use of it is in places where you expect the superblock
* to be sane, and if it isn't, then we exit.
* Which of the above two actions to take is indicated with the second argument.
*/
int
checksblock(struct fs sb, int proceed)
{
int err = 0;
char *errmsg;
if ((sb.fs_magic != FS_MAGIC) && (sb.fs_magic != MTB_UFS_MAGIC)) {
err = 1;
errmsg = gettext("Bad superblock; magic number wrong\n");
} else if ((sb.fs_magic == FS_MAGIC &&
(sb.fs_version != UFS_EFISTYLE4NONEFI_VERSION_2 &&
sb.fs_version != UFS_VERSION_MIN)) ||
(sb.fs_magic == MTB_UFS_MAGIC &&
(sb.fs_version > MTB_UFS_VERSION_1 ||
sb.fs_version < MTB_UFS_VERSION_MIN))) {
err = 2;
errmsg = gettext("Unrecognized version of UFS\n");
} else if (sb.fs_ncg < 1) {
err = 3;
errmsg = gettext("Bad superblock; ncg out of range\n");
} else if (sb.fs_cpg < 1) {
err = 4;
errmsg = gettext("Bad superblock; cpg out of range\n");
} else if (sb.fs_ncg * sb.fs_cpg < sb.fs_ncyl ||
(sb.fs_ncg - 1) * sb.fs_cpg >= sb.fs_ncyl) {
err = 5;
errmsg = gettext("Bad superblock; ncyl out of range\n");
} else if (sb.fs_sbsize <= 0 || sb.fs_sbsize > sb.fs_bsize) {
err = 6;
errmsg = gettext("Bad superblock; superblock size out of "
"range\n");
}
if (proceed) {
if (err) dprintf(("%s", errmsg));
return (err);
}
if (err) {
fprintf(stderr, "%s", errmsg);
lockexit(32);
}
return (32);
}
/*
* Roll the embedded log, if any, and set up the global variables
* islog, islogok and isufslog.
*/
static void
logsetup(char *devstr)
{
void *buf, *ud_buf;
extent_block_t *ebp;
ml_unit_t *ul;
ml_odunit_t *ud;
/*
* Does the superblock indicate that we are supposed to have a log ?
*/
if (sblock.fs_logbno == 0) {
/*
* No log present, nothing to do.
*/
islogok = 0;
islog = 0;
isufslog = 0;
return;
} else {
/*
* There's a log in a yet unknown state, attempt to roll it.
*/
islog = 1;
islogok = 0;
isufslog = 0;
/*
* We failed to roll the log, bail out.
*/
if (rl_roll_log(devstr) != RL_SUCCESS)
return;
isufslog = 1;
/* log is not okay; check the fs */
if ((FSOKAY != (sblock.fs_state + sblock.fs_time)) ||
(sblock.fs_clean != FSLOG))
return;
/* get the log allocation block */
buf = (void *)malloc(DEV_BSIZE);
if (buf == (void *) NULL)
return;
ud_buf = (void *)malloc(DEV_BSIZE);
if (ud_buf == (void *) NULL) {
free(buf);
return;
}
rdfs((diskaddr_t)logbtodb(&sblock, sblock.fs_logbno),
DEV_BSIZE, buf);
ebp = (extent_block_t *)buf;
/* log allocation block is not okay; check the fs */
if (ebp->type != LUFS_EXTENTS) {
free(buf);
free(ud_buf);
return;
}
/* get the log state block(s) */
rdfs((diskaddr_t)logbtodb(&sblock, ebp->extents[0].pbno),
DEV_BSIZE, ud_buf);
ud = (ml_odunit_t *)ud_buf;
ul = (ml_unit_t *)malloc(sizeof (*ul));
ul->un_ondisk = *ud;
/* log state is okay */
if ((ul->un_chksum == ul->un_head_ident + ul->un_tail_ident) &&
(ul->un_version == LUFS_VERSION_LATEST) &&
(ul->un_badlog == 0))
islogok = 1;
free(ud_buf);
free(buf);
free(ul);
}
}
void
growinit(char *devstr)
{
int i;
char buf[DEV_BSIZE];
/*
* Read and verify the superblock
*/
rdfs((diskaddr_t)(SBOFF / sectorsize), (int)sbsize, (char *)&sblock);
(void) checksblock(sblock, 0);
if (sblock.fs_postblformat != FS_DYNAMICPOSTBLFMT) {
(void) fprintf(stderr,
gettext("old file system format; can't growfs\n"));
lockexit(32);
}
/*
* can't shrink a file system
*/
grow_fssize = fsbtodb(&sblock, (uint64_t)sblock.fs_size);
if (fssize_db < grow_fssize) {
(void) fprintf(stderr,
gettext("%lld sectors < current size of %lld sectors\n"),
fssize_db, grow_fssize);
lockexit(32);
}
/*
* can't grow a system to over a terabyte unless it was set up
* as an MTB UFS file system.
*/
if (mtb == 'y' && sblock.fs_magic != MTB_UFS_MAGIC) {
if (fssize_db >= SECTORS_PER_TERABYTE) {
(void) fprintf(stderr, gettext(
"File system was not set up with the multi-terabyte format.\n"));
(void) fprintf(stderr, gettext(
"Its size cannot be increased to a terabyte or more.\n"));
} else {
(void) fprintf(stderr, gettext(
"Cannot convert file system to multi-terabyte format.\n"));
}
lockexit(32);
}
logsetup(devstr);
/*
* can't growfs when logging device has errors
*/
if ((islog && !islogok) ||
((FSOKAY == (sblock.fs_state + sblock.fs_time)) &&
(sblock.fs_clean == FSLOG && !islog))) {
(void) fprintf(stderr,
gettext("logging device has errors; can't growfs\n"));
lockexit(32);
}
/*
* disable ufs logging for growing
*/
if (isufslog) {
if (rl_log_control(devstr, _FIOLOGDISABLE) != RL_SUCCESS) {
(void) fprintf(stderr, gettext(
"failed to disable logging\n"));
lockexit(32);
}
islog = 0;
waslog = 1;
}
/*
* if mounted write lock the file system to be grown
*/
if (ismounted)
wlockfs();
/*
* refresh dynamic superblock state - disabling logging will have
* changed the amount of free space available in the file system
*/
rdfs((diskaddr_t)(SBOFF / sectorsize), sbsize, (char *)&sblock);
/*
* make sure device is big enough
*/
rdfs((diskaddr_t)fssize_db - 1, DEV_BSIZE, buf);
wtfs((diskaddr_t)fssize_db - 1, DEV_BSIZE, buf);
/*
* read current summary information
*/
grow_fscs = read_summaryinfo(&sblock);
/*
* save some current size related fields from the superblock
* These are used in extendsummaryinfo()
*/
grow_fs_size = sblock.fs_size;
grow_fs_ncg = sblock.fs_ncg;
grow_fs_csaddr = (diskaddr_t)sblock.fs_csaddr;
grow_fs_cssize = sblock.fs_cssize;
/*
* save and reset the clean flag
*/
if (FSOKAY == (sblock.fs_state + sblock.fs_time))
grow_fs_clean = sblock.fs_clean;
else
grow_fs_clean = FSBAD;
sblock.fs_clean = FSBAD;
sblock.fs_state = FSOKAY - sblock.fs_time;
isbad = 1;
wtfs((diskaddr_t)(SBOFF / sectorsize), sbsize, (char *)&sblock);
}
void
checkdev(char *rdev, char *bdev)
{
struct stat64 statarea;
if (stat64(bdev, &statarea) < 0) {
(void) fprintf(stderr, gettext("can't check mount point; "));
(void) fprintf(stderr, gettext("can't stat %s\n"), bdev);
lockexit(32);
}
if ((statarea.st_mode & S_IFMT) != S_IFBLK) {
(void) fprintf(stderr, gettext(
"can't check mount point; %s is not a block device\n"),
bdev);
lockexit(32);
}
if (stat64(rdev, &statarea) < 0) {
(void) fprintf(stderr, gettext("can't stat %s\n"), rdev);
lockexit(32);
}
if ((statarea.st_mode & S_IFMT) != S_IFCHR) {
(void) fprintf(stderr,
gettext("%s is not a character device\n"), rdev);
lockexit(32);
}
}
void
checkmount(struct mnttab *mntp, char *bdevname)
{
struct stat64 statdir;
struct stat64 statdev;
if (strcmp(bdevname, mntp->mnt_special) == 0) {
if (stat64(mntp->mnt_mountp, &statdir) == -1) {
(void) fprintf(stderr, gettext("can't stat %s\n"),
mntp->mnt_mountp);
lockexit(32);
}
if (stat64(mntp->mnt_special, &statdev) == -1) {
(void) fprintf(stderr, gettext("can't stat %s\n"),
mntp->mnt_special);
lockexit(32);
}
if (statdir.st_dev != statdev.st_rdev) {
(void) fprintf(stderr, gettext(
"%s is not mounted on %s; mnttab(4) wrong\n"),
mntp->mnt_special, mntp->mnt_mountp);
lockexit(32);
}
ismounted = 1;
if (directory) {
if (strcmp(mntp->mnt_mountp, directory) != 0) {
(void) fprintf(stderr,
gettext("%s is mounted on %s, not %s\n"),
bdevname, mntp->mnt_mountp, directory);
lockexit(32);
}
} else {
if (grow)
(void) fprintf(stderr, gettext(
"%s is mounted on %s; can't growfs\n"),
bdevname, mntp->mnt_mountp);
else
(void) fprintf(stderr,
gettext("%s is mounted, can't mkfs\n"),
bdevname);
lockexit(32);
}
}
}
struct dinode *dibuf = 0;
diskaddr_t difrag = 0;
struct dinode *
gdinode(ino_t ino)
{
/*
* read the block of inodes containing inode number ino
*/
if (dibuf == 0)
dibuf = (struct dinode *)malloc((unsigned)sblock.fs_bsize);
if (itod(&sblock, ino) != difrag) {
difrag = itod(&sblock, ino);
rdfs(fsbtodb(&sblock, (uint64_t)difrag), (int)sblock.fs_bsize,
(char *)dibuf);
}
return (dibuf + (ino % INOPB(&sblock)));
}
/*
* structure that manages the frags we need for extended summary info
* These frags can be:
* free
* data block
* alloc block
*/
struct csfrag {
struct csfrag *next; /* next entry */
daddr32_t ofrag; /* old frag */
daddr32_t nfrag; /* new frag */
long cylno; /* cylno of nfrag */
long frags; /* number of frags */
long size; /* size in bytes */
ino_t ino; /* inode number */
long fixed; /* Boolean - Already fixed? */
};
struct csfrag *csfrag; /* state unknown */
struct csfrag *csfragino; /* frags belonging to an inode */
struct csfrag *csfragfree; /* frags that are free */
daddr32_t maxcsfrag = 0; /* maximum in range */
daddr32_t mincsfrag = 0x7fffffff; /* minimum in range */
int
csfraginrange(daddr32_t frag)
{
return ((frag >= mincsfrag) && (frag <= maxcsfrag));
}
struct csfrag *
findcsfrag(daddr32_t frag, struct csfrag **cfap)
{
struct csfrag *cfp;
if (!csfraginrange(frag))
return (NULL);
for (cfp = *cfap; cfp; cfp = cfp->next)
if (cfp->ofrag == frag)
return (cfp);
return (NULL);
}
void
checkindirect(ino_t ino, daddr32_t *fragsp, daddr32_t frag, int level)
{
int i;
int ne = sblock.fs_bsize / sizeof (daddr32_t);
daddr32_t fsb[MAXBSIZE / sizeof (daddr32_t)];
if (frag == 0)
return;
rdfs(fsbtodb(&sblock, frag), (int)sblock.fs_bsize,
(char *)fsb);
checkdirect(ino, fragsp, fsb, sblock.fs_bsize / sizeof (daddr32_t));
if (level)
for (i = 0; i < ne && *fragsp; ++i)
checkindirect(ino, fragsp, fsb[i], level-1);
}
void
addcsfrag(ino_t ino, daddr32_t frag, struct csfrag **cfap)
{
struct csfrag *cfp, *curr, *prev;
/*
* establish a range for faster checking in csfraginrange()
*/
if (frag > maxcsfrag)
maxcsfrag = frag;
if (frag < mincsfrag)
mincsfrag = frag;
/*
* if this frag belongs to an inode and is not the start of a block
* then see if it is part of a frag range for this inode
*/
if (ino && (frag % sblock.fs_frag))
for (cfp = *cfap; cfp; cfp = cfp->next) {
if (ino != cfp->ino)
continue;
if (frag != cfp->ofrag + cfp->frags)
continue;
cfp->frags++;
cfp->size += sblock.fs_fsize;
return;
}
/*
* allocate a csfrag entry and insert it in an increasing order into the
* specified list
*/
cfp = (struct csfrag *)calloc(1, sizeof (struct csfrag));
cfp->ino = ino;
cfp->ofrag = frag;
cfp->frags = 1;
cfp->size = sblock.fs_fsize;
for (prev = NULL, curr = *cfap; curr != NULL;
prev = curr, curr = curr->next) {
if (frag < curr->ofrag) {
cfp->next = curr;
if (prev)
prev->next = cfp; /* middle element */
else
*cfap = cfp; /* first element */
break;
}
if (curr->next == NULL) {
curr->next = cfp; /* last element */
break;
}
}
if (*cfap == NULL) /* will happen only once */
*cfap = cfp;
}
void
delcsfrag(daddr32_t frag, struct csfrag **cfap)
{
struct csfrag *cfp;
struct csfrag **cfpp;
/*
* free up entry whose beginning frag matches
*/
for (cfpp = cfap; *cfpp; cfpp = &(*cfpp)->next) {
if (frag == (*cfpp)->ofrag) {
cfp = *cfpp;
*cfpp = (*cfpp)->next;
free((char *)cfp);
return;
}
}
}
/*
* See whether any of the direct blocks in the array pointed by "db" and of
* length "ne" are within the range of frags needed to extend the cylinder
* summary. If so, remove those frags from the "as-yet-unclassified" list
* (csfrag) and add them to the "owned-by-inode" list (csfragino).
* For each such frag found, decrement the frag count pointed to by fragsp.
* "ino" is the inode that contains (either directly or indirectly) the frags
* being checked.
*/
void
checkdirect(ino_t ino, daddr32_t *fragsp, daddr32_t *db, int ne)
{
int i;
int j;
int found;
diskaddr_t frag;
/*
* scan for allocation within the new summary info range
*/
for (i = 0; i < ne && *fragsp; ++i) {
if ((frag = *db++) != 0) {
found = 0;
for (j = 0; j < sblock.fs_frag && *fragsp; ++j) {
if (found || (found = csfraginrange(frag))) {
addcsfrag(ino, frag, &csfragino);
delcsfrag(frag, &csfrag);
}
++frag;
--(*fragsp);
}
}
}
}
void
findcsfragino()
{
int i;
int j;
daddr32_t frags;
struct dinode *dp;
/*
* scan all old inodes looking for allocations in the new
* summary info range. Move the affected frag from the
* generic csfrag list onto the `owned-by-inode' list csfragino.
*/
for (i = UFSROOTINO; i < grow_fs_ncg*sblock.fs_ipg && csfrag; ++i) {
dp = gdinode((ino_t)i);
switch (dp->di_mode & IFMT) {
case IFSHAD :
case IFLNK :
case IFDIR :
case IFREG : break;
default : continue;
}
frags = dbtofsb(&sblock, dp->di_blocks);
checkdirect((ino_t)i, &frags, &dp->di_db[0], NDADDR+NIADDR);
for (j = 0; j < NIADDR && frags; ++j) {
/* Negate the block if its an fallocate'd block */
if (dp->di_ib[j] < 0 && dp->di_ib[j] != UFS_HOLE)
checkindirect((ino_t)i, &frags,
-(dp->di_ib[j]), j);
else
checkindirect((ino_t)i, &frags,
dp->di_ib[j], j);
}
}
}
void
fixindirect(daddr32_t frag, int level)
{
int i;
int ne = sblock.fs_bsize / sizeof (daddr32_t);
daddr32_t fsb[MAXBSIZE / sizeof (daddr32_t)];
if (frag == 0)
return;
rdfs(fsbtodb(&sblock, (uint64_t)frag), (int)sblock.fs_bsize,
(char *)fsb);
fixdirect((caddr_t)fsb, frag, fsb, ne);
if (level)
for (i = 0; i < ne; ++i)
fixindirect(fsb[i], level-1);
}
void
fixdirect(caddr_t bp, daddr32_t frag, daddr32_t *db, int ne)
{
int i;
struct csfrag *cfp;
for (i = 0; i < ne; ++i, ++db) {
if (*db == 0)
continue;
if ((cfp = findcsfrag(*db, &csfragino)) == NULL)
continue;
*db = cfp->nfrag;
cfp->fixed = 1;
wtfs(fsbtodb(&sblock, (uint64_t)frag), (int)sblock.fs_bsize,
bp);
}
}
void
fixcsfragino()
{
int i;
struct dinode *dp;
struct csfrag *cfp;
for (cfp = csfragino; cfp; cfp = cfp->next) {
if (cfp->fixed)
continue;
dp = gdinode((ino_t)cfp->ino);
fixdirect((caddr_t)dibuf, difrag, dp->di_db, NDADDR+NIADDR);
for (i = 0; i < NIADDR; ++i)
fixindirect(dp->di_ib[i], i);
}
}
/*
* Read the cylinders summary information specified by settings in the
* passed 'fs' structure into a new allocated array of csum structures.
* The caller is responsible for freeing the returned array.
* Return a pointer to an array of csum structures.
*/
static struct csum *
read_summaryinfo(struct fs *fsp)
{
struct csum *csp;
int i;
if ((csp = malloc((size_t)fsp->fs_cssize)) == NULL) {
(void) fprintf(stderr, gettext("cannot create csum list,"
" not enough memory\n"));
exit(32);
}
for (i = 0; i < fsp->fs_cssize; i += fsp->fs_bsize) {
rdfs(fsbtodb(fsp,
(uint64_t)(fsp->fs_csaddr + numfrags(fsp, i))),
(int)(fsp->fs_cssize - i < fsp->fs_bsize ?
fsp->fs_cssize - i : fsp->fs_bsize), ((caddr_t)csp) + i);
}
return (csp);
}
/*
* Check the allocation of fragments that are to be made part of a csum block.
* A fragment is allocated if it is either in the csfragfree list or, it is
* in the csfragino list and has new frags associated with it.
* Return the number of allocated fragments.
*/
int64_t
checkfragallocated(daddr32_t frag)
{
struct csfrag *cfp;
/*
* Since the lists are sorted we can break the search if the asked
* frag is smaller then the one in the list.
*/
for (cfp = csfragfree; cfp != NULL && frag >= cfp->ofrag;
cfp = cfp->next) {
if (frag == cfp->ofrag)
return (1);
}
for (cfp = csfragino; cfp != NULL && frag >= cfp->ofrag;
cfp = cfp->next) {
if (frag == cfp->ofrag && cfp->nfrag != 0)
return (cfp->frags);
}
return (0);
}
/*
* Figure out how much the filesystem can be grown. The limiting factor is
* the available free space needed to extend the cg summary info block.
* The free space is determined in three steps:
* - Try to extend the cg summary block to the required size.
* - Find free blocks in last cg.
* - Find free space in the last already allocated fragment of the summary info
* block, and use it for additional csum structures.
* Return the maximum size of the new filesystem or 0 if it can't be grown.
* Please note that this function leaves the global list pointers csfrag,
* csfragfree, and csfragino initialized, and the caller is responsible for
* freeing the lists.
*/
diskaddr_t
probe_summaryinfo()
{
/* fragments by which the csum block can be extended. */
int64_t growth_csum_frags = 0;
/* fragments by which the filesystem can be extended. */
int64_t growth_fs_frags = 0;
int64_t new_fs_cssize; /* size of csum blk in the new FS */
int64_t new_fs_ncg; /* number of cg in the new FS */
int64_t spare_csum;
daddr32_t oldfrag_daddr;
daddr32_t newfrag_daddr;
daddr32_t daddr;
int i;
/*
* read and verify the superblock
*/
rdfs((diskaddr_t)(SBOFF / sectorsize), (int)sbsize, (char *)&sblock);
(void) checksblock(sblock, 0);
/*
* check how much we can extend the cg summary info block
*/
/*
* read current summary information
*/
fscs = read_summaryinfo(&sblock);
/*
* build list of frags needed for cg summary info block extension
*/
oldfrag_daddr = howmany(sblock.fs_cssize, sblock.fs_fsize) +
sblock.fs_csaddr;
new_fs_ncg = howmany(dbtofsb(&sblock, fssize_db), sblock.fs_fpg);
new_fs_cssize = fragroundup(&sblock, new_fs_ncg * sizeof (struct csum));
newfrag_daddr = howmany(new_fs_cssize, sblock.fs_fsize) +
sblock.fs_csaddr;
/*
* add all of the frags that are required to grow the cyl summary to the
* csfrag list, which is the generic/unknown list, since at this point
* we don't yet know the state of those frags.
*/
for (daddr = oldfrag_daddr; daddr < newfrag_daddr; daddr++)
addcsfrag((ino_t)0, daddr, &csfrag);
/*
* filter free fragments and allocate them. Note that the free frags
* must be allocated first otherwise they could be grabbed by
* alloccsfragino() for data frags.
*/
findcsfragfree();
alloccsfragfree();
/*
* filter fragments owned by inodes and allocate them
*/
grow_fs_ncg = sblock.fs_ncg; /* findcsfragino() needs this glob. var. */
findcsfragino();
alloccsfragino();
if (notenoughspace()) {
/*
* check how many consecutive fragments could be allocated
* in both lists.
*/
int64_t tmp_frags;
for (daddr = oldfrag_daddr; daddr < newfrag_daddr;
daddr += tmp_frags) {
if ((tmp_frags = checkfragallocated(daddr)) > 0)
growth_csum_frags += tmp_frags;
else
break;
}
} else {
/*
* We have all we need for the new desired size,
* so clean up and report back.
*/
return (fssize_db);
}
/*
* given the number of fragments by which the csum block can be grown
* compute by how many new fragments the FS can be increased.
* It is the number of csum instances per fragment multiplied by
* `growth_csum_frags' and the number of fragments per cylinder group.
*/
growth_fs_frags = howmany(sblock.fs_fsize, sizeof (struct csum)) *
growth_csum_frags * sblock.fs_fpg;
/*
* compute free fragments in the last cylinder group
*/
rdcg(sblock.fs_ncg - 1);
growth_fs_frags += sblock.fs_fpg - acg.cg_ndblk;
/*
* compute how many csum instances are unused in the old csum block.
* For each unused csum instance the FS can be grown by one cylinder
* group without extending the csum block.
*/
spare_csum = howmany(sblock.fs_cssize, sizeof (struct csum)) -
sblock.fs_ncg;
if (spare_csum > 0)
growth_fs_frags += spare_csum * sblock.fs_fpg;
/*
* recalculate the new filesystem size in sectors, shorten it by
* the requested size `fssize_db' if necessary.
*/
if (growth_fs_frags > 0) {
diskaddr_t sect;
sect = (sblock.fs_size + growth_fs_frags) * sblock.fs_nspf;
return ((sect > fssize_db) ? fssize_db : sect);
}
return (0);
}
void
extendsummaryinfo()
{
int64_t i;
int localtest = test;
int64_t frags;
daddr32_t oldfrag;
daddr32_t newfrag;
/*
* if no-write (-N), don't bother
*/
if (Nflag)
return;
again:
flcg();
/*
* summary info did not change size -- do nothing unless in test mode
*/
if (grow_fs_cssize == sblock.fs_cssize)
if (!localtest)
return;
/*
* build list of frags needed for additional summary information
*/
oldfrag = howmany(grow_fs_cssize, sblock.fs_fsize) + grow_fs_csaddr;
newfrag = howmany(sblock.fs_cssize, sblock.fs_fsize) + grow_fs_csaddr;
/*
* add all of the frags that are required to grow the cyl summary to the
* csfrag list, which is the generic/unknown list, since at this point
* we don't yet know the state of those frags.
*/
for (i = oldfrag, frags = 0; i < newfrag; ++i, ++frags)
addcsfrag((ino_t)0, (diskaddr_t)i, &csfrag);
/*
* reduce the number of data blocks in the file system (fs_dsize) by
* the number of frags that need to be added to the cyl summary
*/
sblock.fs_dsize -= (newfrag - oldfrag);
/*
* In test mode, we move more data than necessary from
* cylinder group 0. The lookup/allocate/move code can be
* better stressed without having to create HUGE file systems.
*/
if (localtest)
for (i = newfrag; i < grow_sifrag; ++i) {
if (frags >= testfrags)
break;
frags++;
addcsfrag((ino_t)0, (diskaddr_t)i, &csfrag);
}
/*
* move frags to free or inode lists, depending on owner
*/
findcsfragfree();
findcsfragino();
/*
* if not all frags can be located, file system must be inconsistent
*/
if (csfrag) {
isbad = 1; /* should already be set, but make sure */
lockexit(32);
}
/*
* allocate the free frags. Note that the free frags must be allocated
* first otherwise they could be grabbed by alloccsfragino() for data
* frags.
*/
alloccsfragfree();
/*
* allocate extra space for inode frags
*/
alloccsfragino();
/*
* not enough space
*/
if (notenoughspace()) {
unalloccsfragfree();
unalloccsfragino();
if (localtest && !testforce) {
localtest = 0;
goto again;
}
(void) fprintf(stderr, gettext("Not enough free space\n"));
lockexit(NOTENOUGHSPACE);
}
/*
* copy the data from old frags to new frags
*/
copycsfragino();
/*
* fix the inodes to point to the new frags
*/
fixcsfragino();
/*
* We may have moved more frags than we needed. Free them.
*/
rdcg((long)0);
for (i = newfrag; i <= maxcsfrag; ++i)
setbit(cg_blksfree(&acg), i-cgbase(&sblock, 0));
wtcg();
flcg();
}
/*
* Check if all fragments in the `csfragino' list were reallocated.
*/
int
notenoughspace()
{
struct csfrag *cfp;
/*
* If any element in the csfragino array has a "new frag location"
* of 0, the allocfrags() function was unsuccessful in allocating
* space for moving the frag represented by this array element.
*/
for (cfp = csfragino; cfp; cfp = cfp->next)
if (cfp->nfrag == 0)
return (1);
return (0);
}
void
unalloccsfragino()
{
struct csfrag *cfp;
while ((cfp = csfragino) != NULL) {
if (cfp->nfrag)
freefrags(cfp->nfrag, cfp->frags, cfp->cylno);
delcsfrag(cfp->ofrag, &csfragino);
}
}
void
unalloccsfragfree()
{
struct csfrag *cfp;
while ((cfp = csfragfree) != NULL) {
freefrags(cfp->ofrag, cfp->frags, cfp->cylno);
delcsfrag(cfp->ofrag, &csfragfree);
}
}
/*
* For each frag in the "as-yet-unclassified" list (csfrag), see if
* it's free (i.e., its bit is set in the free frag bit map). If so,
* move it from the "as-yet-unclassified" list to the csfragfree list.
*/
void
findcsfragfree()
{
struct csfrag *cfp;
struct csfrag *cfpnext;
/*
* move free frags onto the free-frag list
*/
rdcg((long)0);
for (cfp = csfrag; cfp; cfp = cfpnext) {
cfpnext = cfp->next;
if (isset(cg_blksfree(&acg), cfp->ofrag - cgbase(&sblock, 0))) {
addcsfrag(cfp->ino, cfp->ofrag, &csfragfree);
delcsfrag(cfp->ofrag, &csfrag);
}
}
}
void
copycsfragino()
{
struct csfrag *cfp;
char buf[MAXBSIZE];
/*
* copy data from old frags to newly allocated frags
*/
for (cfp = csfragino; cfp; cfp = cfp->next) {
rdfs(fsbtodb(&sblock, (uint64_t)cfp->ofrag), (int)cfp->size,
buf);
wtfs(fsbtodb(&sblock, (uint64_t)cfp->nfrag), (int)cfp->size,
buf);
}
}
long curcylno = -1;
int cylnodirty = 0;
void
rdcg(long cylno)
{
if (cylno != curcylno) {
flcg();
curcylno = cylno;
rdfs(fsbtodb(&sblock, (uint64_t)cgtod(&sblock, curcylno)),
(int)sblock.fs_cgsize, (char *)&acg);
}
}
void
flcg()
{
if (cylnodirty) {
if (debug && Pflag) {
(void) fprintf(stderr,
"Assert: cylnodirty set in probe mode\n");
return;
}
resetallocinfo();
wtfs(fsbtodb(&sblock, (uint64_t)cgtod(&sblock, curcylno)),
(int)sblock.fs_cgsize, (char *)&acg);
cylnodirty = 0;
}
curcylno = -1;
}
void
wtcg()
{
if (!Pflag) {
/* probe mode should never write to disk */
cylnodirty = 1;
}
}
void
allocfrags(long frags, daddr32_t *fragp, long *cylnop)
{
int i;
int j;
long bits;
long bit;
/*
* Allocate a free-frag range in an old cylinder group
*/
for (i = 0, *fragp = 0; i < grow_fs_ncg; ++i) {
if (((fscs+i)->cs_nffree < frags) && ((fscs+i)->cs_nbfree == 0))
continue;
rdcg((long)i);
bit = bits = 0;
while (findfreerange(&bit, &bits)) {
if (frags <= bits) {
for (j = 0; j < frags; ++j)
clrbit(cg_blksfree(&acg), bit+j);
wtcg();
*cylnop = i;
*fragp = bit + cgbase(&sblock, i);
return;
}
bit += bits;
}
}
}
/*
* Allocate space for frags that need to be moved in order to free up space for
* expanding the cylinder summary info.
* For each frag that needs to be moved (each frag or range of frags in
* the csfragino list), allocate a new location and store the frag number
* of that new location in the nfrag field of the csfrag struct.
* If a new frag can't be allocated for any element in the csfragino list,
* set the new frag number for that element to 0 and return immediately.
* The notenoughspace() function will detect this condition.
*/
void
alloccsfragino()
{
struct csfrag *cfp;
/*
* allocate space for inode frag ranges
*/
for (cfp = csfragino; cfp; cfp = cfp->next) {
allocfrags(cfp->frags, &cfp->nfrag, &cfp->cylno);
if (cfp->nfrag == 0)
break;
}
}
void
alloccsfragfree()
{
struct csfrag *cfp;
/*
* allocate the free frags needed for extended summary info
*/
rdcg((long)0);
for (cfp = csfragfree; cfp; cfp = cfp->next)
clrbit(cg_blksfree(&acg), cfp->ofrag - cgbase(&sblock, 0));
wtcg();
}
void
freefrags(daddr32_t frag, long frags, long cylno)
{
int i;
/*
* free frags
*/
rdcg(cylno);
for (i = 0; i < frags; ++i) {
setbit(cg_blksfree(&acg), (frag+i) - cgbase(&sblock, cylno));
}
wtcg();
}
int
findfreerange(long *bitp, long *bitsp)
{
long bit;
/*
* find a range of free bits in a cylinder group bit map
*/
for (bit = *bitp, *bitsp = 0; bit < acg.cg_ndblk; ++bit)
if (isset(cg_blksfree(&acg), bit))
break;
if (bit >= acg.cg_ndblk)
return (0);
*bitp = bit;
*bitsp = 1;
for (++bit; bit < acg.cg_ndblk; ++bit, ++(*bitsp)) {
if ((bit % sblock.fs_frag) == 0)
break;
if (isclr(cg_blksfree(&acg), bit))
break;
}
return (1);
}
void
resetallocinfo()
{
long cno;
long bit;
long bits;
/*
* Compute the free blocks/frags info and update the appropriate
* inmemory superblock, summary info, and cylinder group fields
*/
sblock.fs_cstotal.cs_nffree -= acg.cg_cs.cs_nffree;
sblock.fs_cstotal.cs_nbfree -= acg.cg_cs.cs_nbfree;
acg.cg_cs.cs_nffree = 0;
acg.cg_cs.cs_nbfree = 0;
bzero((caddr_t)acg.cg_frsum, sizeof (acg.cg_frsum));
bzero((caddr_t)cg_blktot(&acg), (int)(acg.cg_iusedoff-acg.cg_btotoff));
bit = bits = 0;
while (findfreerange(&bit, &bits)) {
if (bits == sblock.fs_frag) {
acg.cg_cs.cs_nbfree++;
cno = cbtocylno(&sblock, bit);
cg_blktot(&acg)[cno]++;
cg_blks(&sblock, &acg, cno)[cbtorpos(&sblock, bit)]++;
} else {
acg.cg_cs.cs_nffree += bits;
acg.cg_frsum[bits]++;
}
bit += bits;
}
*(fscs + acg.cg_cgx) = acg.cg_cs;
sblock.fs_cstotal.cs_nffree += acg.cg_cs.cs_nffree;
sblock.fs_cstotal.cs_nbfree += acg.cg_cs.cs_nbfree;
}
void
extendcg(long cylno)
{
int i;
diskaddr_t dupper;
diskaddr_t cbase;
diskaddr_t dmax;
/*
* extend the cylinder group at the end of the old file system
* if it was partially allocated becase of lack of space
*/
flcg();
rdcg(cylno);
dupper = acg.cg_ndblk;
if (cylno == sblock.fs_ncg - 1)
acg.cg_ncyl = sblock.fs_ncyl - (sblock.fs_cpg * cylno);
else
acg.cg_ncyl = sblock.fs_cpg;
cbase = cgbase(&sblock, cylno);
dmax = cbase + sblock.fs_fpg;
if (dmax > sblock.fs_size)
dmax = sblock.fs_size;
acg.cg_ndblk = dmax - cbase;
for (i = dupper; i < acg.cg_ndblk; ++i)
setbit(cg_blksfree(&acg), i);
sblock.fs_dsize += (acg.cg_ndblk - dupper);
wtcg();
flcg();
}
struct lockfs lockfs;
int lockfd;
int islocked;
int lockfskey;
char lockfscomment[128];
void
ulockfs()
{
/*
* if the file system was locked, unlock it before exiting
*/
if (islocked == 0)
return;
/*
* first, check if the lock held
*/
lockfs.lf_flags = LOCKFS_MOD;
if (ioctl(lockfd, _FIOLFSS, &lockfs) == -1) {
perror(directory);
lockexit(32);
}
if (LOCKFS_IS_MOD(&lockfs)) {
(void) fprintf(stderr,
gettext("FILE SYSTEM CHANGED DURING GROWFS!\n"));
(void) fprintf(stderr,
gettext(" See lockfs(1), umount(1), and fsck(1)\n"));
lockexit(32);
}
/*
* unlock the file system
*/
lockfs.lf_lock = LOCKFS_ULOCK;
lockfs.lf_flags = 0;
lockfs.lf_key = lockfskey;
clockfs();
if (ioctl(lockfd, _FIOLFS, &lockfs) == -1) {
perror(directory);
lockexit(32);
}
}
void
wlockfs()
{
/*
* if no-write (-N), don't bother
*/
if (Nflag)
return;
/*
* open the mountpoint, and write lock the file system
*/
if ((lockfd = open64(directory, O_RDONLY)) == -1) {
perror(directory);
lockexit(32);
}
/*
* check if it is already locked
*/
if (ioctl(lockfd, _FIOLFSS, &lockfs) == -1) {
perror(directory);
lockexit(32);
}
if (lockfs.lf_lock != LOCKFS_WLOCK) {
lockfs.lf_lock = LOCKFS_WLOCK;
lockfs.lf_flags = 0;
lockfs.lf_key = 0;
clockfs();
if (ioctl(lockfd, _FIOLFS, &lockfs) == -1) {
perror(directory);
lockexit(32);
}
}
islocked = 1;
lockfskey = lockfs.lf_key;
}
void
clockfs()
{
time_t t;
char *ct;
(void) time(&t);
ct = ctime(&t);
ct[strlen(ct)-1] = '\0';
(void) sprintf(lockfscomment, "%s -- mkfs pid %d", ct, getpid());
lockfs.lf_comlen = strlen(lockfscomment)+1;
lockfs.lf_comment = lockfscomment;
}
/*
* Write the csum records and the superblock
*/
void
wtsb()
{
long i;
/*
* write summary information
*/
for (i = 0; i < sblock.fs_cssize; i += sblock.fs_bsize)
wtfs(fsbtodb(&sblock, (uint64_t)(sblock.fs_csaddr +
numfrags(&sblock, i))),
(int)(sblock.fs_cssize - i < sblock.fs_bsize ?
sblock.fs_cssize - i : sblock.fs_bsize),
((char *)fscs) + i);
/*
* write superblock
*/
sblock.fs_time = mkfstime;
wtfs((diskaddr_t)(SBOFF / sectorsize), sbsize, (char *)&sblock);
}
/*
* Verify that the optimization selection is reasonable, and advance
* the global "string" appropriately.
*/
static char
checkopt(char *optim)
{
char opt;
int limit = strcspn(optim, ",");
switch (limit) {
case 0: /* missing indicator (have comma or nul) */
(void) fprintf(stderr, gettext(
"mkfs: missing optimization flag reset to `t' (time)\n"));
opt = 't';
break;
case 1: /* single-character indicator */
opt = *optim;
if ((opt != 's') && (opt != 't')) {
(void) fprintf(stderr, gettext(
"mkfs: bad optimization value `%c' reset to `t' (time)\n"),
opt);
opt = 't';
}
break;
default: /* multi-character indicator */
(void) fprintf(stderr, gettext(
"mkfs: bad optimization value `%*.*s' reset to `t' (time)\n"),
limit, limit, optim);
opt = 't';
break;
}
string += limit;
return (opt);
}
/*
* Verify that the mtb selection is reasonable, and advance
* the global "string" appropriately.
*/
static char
checkmtb(char *mtbarg)
{
char mtbc;
int limit = strcspn(mtbarg, ",");
switch (limit) {
case 0: /* missing indicator (have comma or nul) */
(void) fprintf(stderr, gettext(
"mkfs: missing mtb flag reset to `n' (no mtb support)\n"));
mtbc = 'n';
break;
case 1: /* single-character indicator */
mtbc = tolower(*mtbarg);
if ((mtbc != 'y') && (mtbc != 'n')) {
(void) fprintf(stderr, gettext(
"mkfs: bad mtb value `%c' reset to `n' (no mtb support)\n"),
mtbc);
mtbc = 'n';
}
break;
default: /* multi-character indicator */
(void) fprintf(stderr, gettext(
"mkfs: bad mtb value `%*.*s' reset to `n' (no mtb support)\n"),
limit, limit, mtbarg);
opt = 'n';
break;
}
string += limit;
return (mtbc);
}
/*
* Verify that a value is in a range. If it is not, resets it to
* its default value if one is supplied, exits otherwise.
*
* When testing, can compare user_supplied to RC_KEYWORD or RC_POSITIONAL.
*/
static void
range_check(long *varp, char *name, long minimum, long maximum,
long def_val, int user_supplied)
{
dprintf(("DeBuG %s : %ld (%ld %ld %ld)\n",
name, *varp, minimum, maximum, def_val));
if ((*varp < minimum) || (*varp > maximum)) {
if (user_supplied != RC_DEFAULT) {
(void) fprintf(stderr, gettext(
"mkfs: bad value for %s: %ld must be between %ld and %ld\n"),
name, *varp, minimum, maximum);
}
if (def_val != NO_DEFAULT) {
if (user_supplied) {
(void) fprintf(stderr,
gettext("mkfs: %s reset to default %ld\n"),
name, def_val);
}
*varp = def_val;
dprintf(("DeBuG %s : %ld\n", name, *varp));
return;
}
lockexit(2);
/*NOTREACHED*/
}
}
/*
* Verify that a value is in a range. If it is not, resets it to
* its default value if one is supplied, exits otherwise.
*
* When testing, can compare user_supplied to RC_KEYWORD or RC_POSITIONAL.
*/
static void
range_check_64(uint64_t *varp, char *name, uint64_t minimum, uint64_t maximum,
uint64_t def_val, int user_supplied)
{
if ((*varp < minimum) || (*varp > maximum)) {
if (user_supplied != RC_DEFAULT) {
(void) fprintf(stderr, gettext(
"mkfs: bad value for %s: %lld must be between %lld and %lld\n"),
name, *varp, minimum, maximum);
}
if (def_val != NO_DEFAULT) {
if (user_supplied) {
(void) fprintf(stderr,
gettext("mkfs: %s reset to default %lld\n"),
name, def_val);
}
*varp = def_val;
return;
}
lockexit(2);
/*NOTREACHED*/
}
}
/*
* Blocks SIGINT from delivery. Returns the previous mask in the
* buffer provided, so that mask may be later restored.
*/
static void
block_sigint(sigset_t *old_mask)
{
sigset_t block_mask;
if (sigemptyset(&block_mask) < 0) {
fprintf(stderr, gettext("Could not clear signal mask\n"));
lockexit(3);
}
if (sigaddset(&block_mask, SIGINT) < 0) {
fprintf(stderr, gettext("Could not set signal mask\n"));
lockexit(3);
}
if (sigprocmask(SIG_BLOCK, &block_mask, old_mask) < 0) {
fprintf(stderr, gettext("Could not block SIGINT\n"));
lockexit(3);
}
}
/*
* Restores the signal mask that was in force before a call
* to block_sigint(). This may actually still have SIGINT blocked,
* if we've been recursively invoked.
*/
static void
unblock_sigint(sigset_t *old_mask)
{
if (sigprocmask(SIG_UNBLOCK, old_mask, (sigset_t *)NULL) < 0) {
fprintf(stderr, gettext("Could not restore signal mask\n"));
lockexit(3);
}
}
/*
* Attempt to be somewhat graceful about being interrupted, rather than
* just silently leaving the filesystem in an unusable state.
*
* The kernel has blocked SIGINT upon entry, so we don't have to worry
* about recursion if the user starts pounding on the keyboard.
*/
static void
recover_from_sigint(int signum)
{
if (fso > -1) {
if ((Nflag != 0) || confirm_abort()) {
lockexit(4);
}
}
}
static int
confirm_abort(void)
{
char line[80];
printf(gettext("\n\nAborting at this point will leave the filesystem "
"in an inconsistent\nstate. If you do choose to stop, "
"you will be given instructions on how to\nrecover "
"the filesystem. Do you wish to cancel the filesystem "
"grow\noperation (y/n)?"));
if (getaline(stdin, line, sizeof (line)) == EOF)
line[0] = 'y';
printf("\n");
if (line[0] == 'y' || line[0] == 'Y')
return (1);
else {
return (0);
}
}
static int
getaline(FILE *fp, char *loc, int maxlen)
{
int n;
char *p, *lastloc;
p = loc;
lastloc = &p[maxlen-1];
while ((n = getc(fp)) != '\n') {
if (n == EOF)
return (EOF);
if (!isspace(n) && p < lastloc)
*p++ = n;
}
*p = 0;
return (p - loc);
}
/*
* Calculate the maximum value of cylinders-per-group for a file
* system with the characteristics:
*
* bsize - file system block size
* fragsize - frag size
* nbpi - number of bytes of disk space per inode
* nrpos - number of rotational positions
* spc - sectors per cylinder
*
* These five characteristic are not adjustable (by this function).
* The only attribute of the file system which IS adjusted by this
* function in order to maximize cylinders-per-group is the proportion
* of the cylinder group overhead block used for the inode map. The
* inode map cannot occupy more than one-third of the cylinder group
* overhead block, but it's OK for it to occupy less than one-third
* of the overhead block.
*
* The setting of nbpi determines one possible value for the maximum
* size of a cylinder group. It does so because it determines the total
* number of inodes in the file system (file system size is fixed, and
* nbpi is fixed, so the total number of inodes is fixed too). The
* cylinder group has to be small enough so that the number of inodes
* in the cylinder group is less than or equal to the number of bits
* in one-third (or whatever proportion is assumed) of a file system
* block. The details of the calculation are:
*
* The macro MAXIpG_B(bsize, inode_divisor) determines the maximum
* number of inodes that can be in a cylinder group, given the
* proportion of the cylinder group overhead block used for the
* inode bitmaps (an inode_divisor of 3 means that 1/3 of the
* block is used for inode bitmaps; an inode_divisor of 12 means
* that 1/12 of the block is used for inode bitmaps.)
*
* Once the number of inodes per cylinder group is known, the
* maximum value of cylinders-per-group (determined by nbpi)
* is calculated by the formula
*
* maxcpg_given_nbpi = (size of a cylinder group)/(size of a cylinder)
*
* = (inodes-per-cg * nbpi)/(spc * DEV_BSIZE)
*
* (Interestingly, the size of the file system never enters
* into this calculation.)
*
* Another possible value for the maximum cylinder group size is determined
* by frag_size and nrpos. The frags in the cylinder group must be
* representable in the frag bitmaps in the cylinder overhead block and the
* rotational positions for each cylinder must be represented in the
* rotational position tables. The calculation of the maximum cpg
* value, given the frag and nrpos vales, is:
*
* maxcpg_given_fragsize =
* (available space in the overhead block) / (size of per-cylinder data)
*
* The available space in the overhead block =
* bsize - sizeof (struct cg) - space_used_for_inode_bitmaps
*
* The size of the per-cylinder data is:
* sizeof(long) # for the "blocks avail per cylinder" field
* + nrpos * sizeof(short) # for the rotational position table entry
* + frags-per-cylinder/NBBY # number of bytes to represent this
* # cylinder in the frag bitmap
*
* The two calculated maximum values of cylinder-per-group will typically
* turn out to be different, since they are derived from two different
* constraints. Usually, maxcpg_given_nbpi is much bigger than
* maxcpg_given_fragsize. But they can be brought together by
* adjusting the proportion of the overhead block dedicated to
* the inode bitmaps. Decreasing the proportion of the cylinder
* group overhead block used for inode maps will decrease
* maxcpg_given_nbpi and increase maxcpg_given_fragsize.
*
* This function calculates the initial values of maxcpg_given_nbpi
* and maxcpg_given_fragsize assuming that 1/3 of the cg overhead
* block is used for inode bitmaps. Then it decreases the proportion
* of the cg overhead block used for inode bitmaps (by increasing
* the value of inode_divisor) until maxcpg_given_nbpi and
* maxcpg_given_fragsize are the same, or stop changing, or
* maxcpg_given_nbpi is less than maxcpg_given_fragsize.
*
* The loop terminates when any of the following occur:
* * maxcpg_given_fragsize is greater than or equal to
* maxcpg_given_nbpi
* * neither maxcpg_given_fragsize nor maxcpg_given_nbpi
* change in the expected direction
*
* The loop is guaranteed to terminate because it only continues
* while maxcpg_given_fragsize and maxcpg_given_nbpi are approaching
* each other. As soon they cross each other, or neither one changes
* in the direction of the other, or one of them moves in the wrong
* direction, the loop completes.
*/
static long
compute_maxcpg(long bsize, long fragsize, long nbpi, long nrpos, long spc)
{
int maxcpg_given_nbpi; /* in cylinders */
int maxcpg_given_fragsize; /* in cylinders */
int spf; /* sectors per frag */
int inode_divisor;
int old_max_given_frag = 0;
int old_max_given_nbpi = INT_MAX;
spf = fragsize / DEV_BSIZE;
inode_divisor = 3;
while (1) {
maxcpg_given_nbpi =
(((int64_t)(MAXIpG_B(bsize, inode_divisor))) * nbpi) /
(DEV_BSIZE * ((int64_t)spc));
maxcpg_given_fragsize =
(bsize - (sizeof (struct cg)) - (bsize / inode_divisor)) /
(sizeof (long) + nrpos * sizeof (short) +
(spc / spf) / NBBY);
if (maxcpg_given_fragsize >= maxcpg_given_nbpi)
return (maxcpg_given_nbpi);
/*
* If neither value moves toward the other, return the
* least of the old values (we use the old instead of the
* new because: if the old is the same as the new, it
* doesn't matter which ones we use. If one of the
* values changed, but in the wrong direction, the
* new values are suspect. Better use the old. This
* shouldn't happen, but it's best to check.
*/
if (!(maxcpg_given_nbpi < old_max_given_nbpi) &&
!(maxcpg_given_fragsize > old_max_given_frag))
return (MIN(old_max_given_nbpi, old_max_given_frag));
/*
* This is probably impossible, but if one of the maxcpg
* values moved in the "right" direction and one moved
* in the "wrong" direction (that is, the two values moved
* in the same direction), the previous conditional won't
* recognize that the values aren't converging (since at
* least one value moved in the "right" direction, the
* last conditional says "keep going").
*
* Just to make absolutely certain that the loop terminates,
* check for one of the values moving in the "wrong" direction
* and terminate the loop if it happens.
*/
if (maxcpg_given_nbpi > old_max_given_nbpi ||
maxcpg_given_fragsize < old_max_given_frag)
return (MIN(old_max_given_nbpi, old_max_given_frag));
old_max_given_nbpi = maxcpg_given_nbpi;
old_max_given_frag = maxcpg_given_fragsize;
inode_divisor++;
}
}
static int
in_64bit_mode(void)
{
/* cmd must be an absolute path, for security */
char *cmd = "/usr/bin/isainfo -b";
char buf[BUFSIZ];
FILE *ptr;
int retval = 0;
putenv("IFS= \t");
if ((ptr = popen(cmd, "r")) != NULL) {
if (fgets(buf, BUFSIZ, ptr) != NULL &&
strncmp(buf, "64", 2) == 0)
retval = 1;
(void) pclose(ptr);
}
return (retval);
}
/*
* validate_size
*
* Return 1 if the device appears to be at least "size" sectors long.
* Return 0 if it's shorter or we can't read it.
*/
static int
validate_size(int fd, diskaddr_t size)
{
char buf[DEV_BSIZE];
int rc;
if ((llseek(fd, (offset_t)((size - 1) * DEV_BSIZE), SEEK_SET) == -1) ||
(read(fd, buf, DEV_BSIZE)) != DEV_BSIZE)
rc = 0;
else
rc = 1;
return (rc);
}
/*
* Print every field of the calculated superblock, along with
* its value. To make parsing easier on the caller, the value
* is printed first, then the name. Additionally, there's only
* one name/value pair per line. All values are reported in
* hexadecimal (with the traditional 0x prefix), as that's slightly
* easier for humans to read. Not that they're expected to, but
* debugging happens.
*/
static void
dump_sblock(void)
{
int row, column, pending, written;
caddr_t source;
if (Rflag) {
pending = sizeof (sblock);
source = (caddr_t)&sblock;
do {
written = write(fileno(stdout), source, pending);
pending -= written;
source += written;
} while ((pending > 0) && (written > 0));
if (written < 0) {
perror(gettext("Binary dump of superblock failed"));
lockexit(1);
}
return;
} else {
printf("0x%x sblock.fs_link\n", sblock.fs_link);
printf("0x%x sblock.fs_rolled\n", sblock.fs_rolled);
printf("0x%x sblock.fs_sblkno\n", sblock.fs_sblkno);
printf("0x%x sblock.fs_cblkno\n", sblock.fs_cblkno);
printf("0x%x sblock.fs_iblkno\n", sblock.fs_iblkno);
printf("0x%x sblock.fs_dblkno\n", sblock.fs_dblkno);
printf("0x%x sblock.fs_cgoffset\n", sblock.fs_cgoffset);
printf("0x%x sblock.fs_cgmask\n", sblock.fs_cgmask);
printf("0x%x sblock.fs_time\n", sblock.fs_time);
printf("0x%x sblock.fs_size\n", sblock.fs_size);
printf("0x%x sblock.fs_dsize\n", sblock.fs_dsize);
printf("0x%x sblock.fs_ncg\n", sblock.fs_ncg);
printf("0x%x sblock.fs_bsize\n", sblock.fs_bsize);
printf("0x%x sblock.fs_fsize\n", sblock.fs_fsize);
printf("0x%x sblock.fs_frag\n", sblock.fs_frag);
printf("0x%x sblock.fs_minfree\n", sblock.fs_minfree);
printf("0x%x sblock.fs_rotdelay\n", sblock.fs_rotdelay);
printf("0x%x sblock.fs_rps\n", sblock.fs_rps);
printf("0x%x sblock.fs_bmask\n", sblock.fs_bmask);
printf("0x%x sblock.fs_fmask\n", sblock.fs_fmask);
printf("0x%x sblock.fs_bshift\n", sblock.fs_bshift);
printf("0x%x sblock.fs_fshift\n", sblock.fs_fshift);
printf("0x%x sblock.fs_maxcontig\n", sblock.fs_maxcontig);
printf("0x%x sblock.fs_maxbpg\n", sblock.fs_maxbpg);
printf("0x%x sblock.fs_fragshift\n", sblock.fs_fragshift);
printf("0x%x sblock.fs_fsbtodb\n", sblock.fs_fsbtodb);
printf("0x%x sblock.fs_sbsize\n", sblock.fs_sbsize);
printf("0x%x sblock.fs_csmask\n", sblock.fs_csmask);
printf("0x%x sblock.fs_csshift\n", sblock.fs_csshift);
printf("0x%x sblock.fs_nindir\n", sblock.fs_nindir);
printf("0x%x sblock.fs_inopb\n", sblock.fs_inopb);
printf("0x%x sblock.fs_nspf\n", sblock.fs_nspf);
printf("0x%x sblock.fs_optim\n", sblock.fs_optim);
#ifdef _LITTLE_ENDIAN
printf("0x%x sblock.fs_state\n", sblock.fs_state);
#else
printf("0x%x sblock.fs_npsect\n", sblock.fs_npsect);
#endif
printf("0x%x sblock.fs_si\n", sblock.fs_si);
printf("0x%x sblock.fs_trackskew\n", sblock.fs_trackskew);
printf("0x%x sblock.fs_id[0]\n", sblock.fs_id[0]);
printf("0x%x sblock.fs_id[1]\n", sblock.fs_id[1]);
printf("0x%x sblock.fs_csaddr\n", sblock.fs_csaddr);
printf("0x%x sblock.fs_cssize\n", sblock.fs_cssize);
printf("0x%x sblock.fs_cgsize\n", sblock.fs_cgsize);
printf("0x%x sblock.fs_ntrak\n", sblock.fs_ntrak);
printf("0x%x sblock.fs_nsect\n", sblock.fs_nsect);
printf("0x%x sblock.fs_spc\n", sblock.fs_spc);
printf("0x%x sblock.fs_ncyl\n", sblock.fs_ncyl);
printf("0x%x sblock.fs_cpg\n", sblock.fs_cpg);
printf("0x%x sblock.fs_ipg\n", sblock.fs_ipg);
printf("0x%x sblock.fs_fpg\n", sblock.fs_fpg);
printf("0x%x sblock.fs_cstotal\n", sblock.fs_cstotal);
printf("0x%x sblock.fs_fmod\n", sblock.fs_fmod);
printf("0x%x sblock.fs_clean\n", sblock.fs_clean);
printf("0x%x sblock.fs_ronly\n", sblock.fs_ronly);
printf("0x%x sblock.fs_flags\n", sblock.fs_flags);
printf("0x%x sblock.fs_fsmnt\n", sblock.fs_fsmnt);
printf("0x%x sblock.fs_cgrotor\n", sblock.fs_cgrotor);
printf("0x%x sblock.fs_u.fs_csp\n", sblock.fs_u.fs_csp);
printf("0x%x sblock.fs_cpc\n", sblock.fs_cpc);
/*
* No macros are defined for the dimensions of the
* opostbl array.
*/
for (row = 0; row < 16; row++) {
for (column = 0; column < 8; column++) {
printf("0x%x sblock.fs_opostbl[%d][%d]\n",
sblock.fs_opostbl[row][column],
row, column);
}
}
/*
* Ditto the size of sparecon.
*/
for (row = 0; row < 51; row++) {
printf("0x%x sblock.fs_sparecon[%d]\n",
sblock.fs_sparecon[row], row);
}
printf("0x%x sblock.fs_version\n", sblock.fs_version);
printf("0x%x sblock.fs_logbno\n", sblock.fs_logbno);
printf("0x%x sblock.fs_reclaim\n", sblock.fs_reclaim);
printf("0x%x sblock.fs_sparecon2\n", sblock.fs_sparecon2);
#ifdef _LITTLE_ENDIAN
printf("0x%x sblock.fs_npsect\n", sblock.fs_npsect);
#else
printf("0x%x sblock.fs_state\n", sblock.fs_state);
#endif
printf("0x%llx sblock.fs_qbmask\n", sblock.fs_qbmask);
printf("0x%llx sblock.fs_qfmask\n", sblock.fs_qfmask);
printf("0x%x sblock.fs_postblformat\n", sblock.fs_postblformat);
printf("0x%x sblock.fs_nrpos\n", sblock.fs_nrpos);
printf("0x%x sblock.fs_postbloff\n", sblock.fs_postbloff);
printf("0x%x sblock.fs_rotbloff\n", sblock.fs_rotbloff);
printf("0x%x sblock.fs_magic\n", sblock.fs_magic);
/*
* fs_space isn't of much use in this context, so we'll
* just ignore it for now.
*/
}
}