#include <sys/param.h>
#include <sys/stat.h>
#include <sys/diskslice.h>
#include <sys/mount.h>
#include <sys/sysctl.h>
#include <vfs/ufs/dir.h>
#include <vfs/ufs/dinode.h>
#include <vfs/ufs/fs.h>
#include <vfs/ufs/ufsmount.h>
#include <ctype.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <mntopts.h>
#include <inttypes.h>
#include <paths.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#include <disktab.h>
#ifdef MFS
#include <sys/types.h>
#include <sys/mman.h>
#endif
#include <stdarg.h>
#include "defs.h"
struct mntopt mopts[] = {
MOPT_STDOPTS,
MOPT_ASYNC,
MOPT_NULL,
};
void fatal(const char *fmt, ...) __dead2 __printflike(1, 2);
#define COMPAT
#define DFL_FRAGSIZE 2048
#define DFL_BLKSIZE 16384
#define DESCPG 65536
#define ROTDELAY 0
#define MAXBLKPG(bsize) ((bsize) / sizeof(daddr_t))
#define NFPI 4
#define NRPOS 1
#define NTRACKS 1
#define NSECTORS 4096
int mfs;
char *mfs_mtpt;
struct stat mfs_mtstat;
int Lflag;
int Nflag;
int Oflag;
int Cflag;
int Uflag;
int Eflag;
uint64_t slice_offset;
u_long fssize;
int ntracks = NTRACKS;
int nsectors = NSECTORS;
int nphyssectors;
int secpercyl;
int trackspares = -1;
int cylspares = -1;
int sectorsize;
int realsectorsize;
int rpm;
int interleave;
int trackskew = -1;
int headswitch;
int trackseek;
int fsize = 0;
int bsize = 0;
int cpg = DESCPG;
int cpgflg;
int minfree = MINFREE;
int opt = DEFAULTOPT;
int density;
int maxcontig = 0;
int rotdelay = ROTDELAY;
int maxbpg;
int nrpos = NRPOS;
int avgfilesize = AVFILESIZ;
int avgfilesperdir = AFPDIR;
int bbsize = BBSIZE;
int sbsize = SBSIZE;
int mntflags = MNT_ASYNC;
int t_or_u_flag = 0;
caddr_t membase;
char *filename;
u_char *volumelabel = NULL;
#ifdef COMPAT
char *disktype;
int unlabeled;
#endif
char mfsdevname[256];
char *progname;
static void usage(void);
static void mfsintr(int signo);
int
main(int argc, char **argv)
{
int ch, i;
struct disktab geom;
struct stat st;
struct statfs *mp;
int fsi = -1, fso = -1, len, n, vflag;
char *s1, *s2, *special;
const char *opstring;
#ifdef MFS
struct vfsconf vfc;
int error;
#endif
bzero(&geom, sizeof(geom));
vflag = 0;
if ((progname = strrchr(*argv, '/')))
++progname;
else
progname = *argv;
if (strstr(progname, "mfs")) {
mfs = 1;
Nflag++;
}
opstring = mfs ?
"L:NCF:T:Ua:b:c:d:e:f:g:h:i:m:o:s:v" :
"L:NEOS:T:Ua:b:c:d:e:f:g:h:i:k:l:m:n:o:p:r:s:t:u:vx:";
while ((ch = getopt(argc, argv, opstring)) != -1) {
switch (ch) {
case 'E':
Eflag = 1;
break;
case 'L':
volumelabel = optarg;
i = -1;
while (isalnum(volumelabel[++i]))
;
if (volumelabel[i] != '\0')
errx(1, "bad volume label. Valid characters are alphanumerics.");
if (strlen(volumelabel) >= MAXVOLLEN)
errx(1, "bad volume label. Length is longer than %d.",
MAXVOLLEN);
Lflag = 1;
break;
case 'N':
Nflag = 1;
break;
case 'O':
Oflag = 1;
break;
case 'C':
Cflag = 1;
break;
case 'S':
if ((sectorsize = atoi(optarg)) <= 0)
fatal("%s: bad sector size", optarg);
break;
#ifdef COMPAT
case 'T':
disktype = optarg;
break;
#endif
case 'F':
filename = optarg;
break;
case 'U':
Uflag = 1;
break;
case 'a':
if ((maxcontig = atoi(optarg)) <= 0)
fatal("%s: bad maximum contiguous blocks",
optarg);
break;
case 'b':
if ((bsize = atoi(optarg)) < MINBSIZE)
fatal("%s: bad block size", optarg);
break;
case 'c':
if ((cpg = atoi(optarg)) <= 0)
fatal("%s: bad cylinders/group", optarg);
cpgflg++;
break;
case 'd':
if ((rotdelay = atoi(optarg)) < 0)
fatal("%s: bad rotational delay", optarg);
break;
case 'e':
if ((maxbpg = atoi(optarg)) <= 0)
fatal("%s: bad blocks per file in a cylinder group",
optarg);
break;
case 'f':
if ((fsize = atoi(optarg)) <= 0)
fatal("%s: bad fragment size", optarg);
break;
case 'g':
if ((avgfilesize = atoi(optarg)) <= 0)
fatal("%s: bad average file size", optarg);
break;
case 'h':
if ((avgfilesperdir = atoi(optarg)) <= 0)
fatal("%s: bad average files per dir", optarg);
break;
case 'i':
if ((density = atoi(optarg)) <= 0)
fatal("%s: bad bytes per inode", optarg);
break;
case 'k':
if ((trackskew = atoi(optarg)) < 0)
fatal("%s: bad track skew", optarg);
break;
case 'l':
if ((interleave = atoi(optarg)) <= 0)
fatal("%s: bad interleave", optarg);
break;
case 'm':
if ((minfree = atoi(optarg)) < 0 || minfree > 99)
fatal("%s: bad free space %%", optarg);
break;
case 'n':
if ((nrpos = atoi(optarg)) < 0)
fatal("%s: bad rotational layout count",
optarg);
if (nrpos == 0)
nrpos = 1;
break;
case 'o':
if (mfs)
getmntopts(optarg, mopts, &mntflags, 0);
else {
if (strcmp(optarg, "space") == 0)
opt = FS_OPTSPACE;
else if (strcmp(optarg, "time") == 0)
opt = FS_OPTTIME;
else
fatal("%s: unknown optimization preference: use `space' or `time'", optarg);
}
break;
case 'p':
if ((trackspares = atoi(optarg)) < 0)
fatal("%s: bad spare sectors per track",
optarg);
break;
case 'r':
if ((rpm = atoi(optarg)) <= 0)
fatal("%s: bad revolutions/minute", optarg);
break;
case 's':
fssize = strtoul(optarg, NULL, 10);
if (fssize == 0 || fssize > LONG_MAX)
fatal("%s: bad file system size", optarg);
break;
case 't':
t_or_u_flag++;
if ((ntracks = atoi(optarg)) < 0)
fatal("%s: bad total tracks", optarg);
break;
case 'u':
t_or_u_flag++;
if ((nsectors = atoi(optarg)) < 0)
fatal("%s: bad sectors/track", optarg);
break;
case 'v':
vflag = 1;
break;
case 'x':
if ((cylspares = atoi(optarg)) < 0)
fatal("%s: bad spare sectors per cylinder",
optarg);
break;
case '?':
default:
usage();
}
}
argc -= optind;
argv += optind;
if (argc != 2 && (mfs || argc != 1))
usage();
special = argv[0];
if (mfs && !strcmp(special, "swap")) {
fso = -1;
geom.d_media_blksize = 512;
geom.d_nheads = 16;
geom.d_secpertrack = 64;
geom.d_secpercyl = 1024;
geom.d_media_blocks = 16384;
geom.d_rpm = 3600;
geom.d_interleave = 1;
goto havelabel;
}
if (stat(special, &st) < 0 && special[0] && special[0] != '/')
asprintf(&special, "/dev/%s", special);
if (Eflag) {
char sysctl_name[64];
int trim_enabled = 0;
size_t olen = sizeof(trim_enabled);
char *dev_name = strdup(special);
dev_name = strtok(dev_name + strlen("/dev/da"),"s");
sprintf(sysctl_name, "kern.cam.da.%s.trim_enabled",
dev_name);
if (sysctlbyname(sysctl_name, &trim_enabled, &olen, NULL, 0) < 0) {
printf("Device:%s does not support the TRIM command\n",
special);
usage();
}
if(!trim_enabled) {
printf("Erase device option selected, but sysctl (%s) "
"is not enabled\n",sysctl_name);
usage();
}
}
if (Nflag) {
fso = -1;
} else {
fso = open(special, O_WRONLY);
if (fso < 0)
fatal("%s: %s", special, strerror(errno));
n = getmntinfo(&mp, MNT_NOWAIT);
if (n == 0)
fatal("%s: getmntinfo: %s", special, strerror(errno));
len = sizeof(_PATH_DEV) - 1;
s1 = special;
if (strncmp(_PATH_DEV, s1, len) == 0)
s1 += len;
while (--n >= 0) {
s2 = mp->f_mntfromname;
if (strncmp(_PATH_DEV, s2, len) == 0) {
s2 += len - 1;
*s2 = 'r';
}
if (strcmp(s1, s2) == 0 || strcmp(s1, &s2[1]) == 0)
fatal("%s is mounted on %s",
special, mp->f_mntonname);
++mp;
}
}
if (mfs && disktype != NULL) {
struct disktab *dt;
if ((dt = getdisktabbyname(disktype)) == NULL)
fatal("%s: unknown disk type", disktype);
geom = *dt;
} else {
struct partinfo pinfo;
if (special[0] == 0)
fatal("null special file name");
fsi = open(special, O_RDONLY);
if (fsi < 0)
fatal("%s: %s", special, strerror(errno));
if (fstat(fsi, &st) < 0)
fatal("%s: %s", special, strerror(errno));
if ((st.st_mode & S_IFMT) != S_IFCHR && !mfs && !vflag)
printf("%s: %s: not a character-special device\n",
progname, special);
#ifdef COMPAT
if (!mfs && disktype == NULL)
disktype = argv[1];
#endif
if (ioctl(fsi, DIOCGPART, &pinfo) < 0) {
if (!vflag) {
fatal("%s: unable to retrieve geometry "
"information", argv[0]);
}
geom.d_media_blksize = 512;
geom.d_nheads = 16;
geom.d_secpertrack = 64;
geom.d_secpercyl = 1024;
geom.d_media_blocks = st.st_size /
geom.d_media_blksize;
geom.d_media_size = geom.d_media_blocks *
geom.d_media_blksize;
} else {
geom.d_media_blksize = pinfo.media_blksize;
geom.d_nheads = pinfo.d_nheads;
geom.d_secpertrack = pinfo.d_secpertrack;
geom.d_secpercyl = pinfo.d_secpercyl;
geom.d_media_blocks = pinfo.media_blocks;
geom.d_media_size = pinfo.media_size;
slice_offset = pinfo.media_offset;
}
if (geom.d_media_blocks == 0 || geom.d_media_size == 0) {
fatal("%s: is unavailable", argv[0]);
}
printf("%s: media size %6.2fMB\n",
argv[0], geom.d_media_size / 1024.0 / 1024.0);
if (geom.d_media_size / 512 >= 0x80000000ULL)
fatal("%s: media size is too large for newfs to handle",
argv[0]);
}
havelabel:
if (fssize == 0)
fssize = geom.d_media_blocks;
if ((u_long)fssize > geom.d_media_blocks && !mfs) {
fatal("%s: maximum file system size is %" PRIu64 " blocks",
argv[0], geom.d_media_blocks);
}
if (rpm == 0) {
rpm = geom.d_rpm;
if (rpm <= 0)
rpm = 3600;
}
if (ntracks == 0) {
ntracks = geom.d_nheads;
if (ntracks <= 0)
fatal("%s: no default #tracks", argv[0]);
}
if (nsectors == 0) {
nsectors = geom.d_secpertrack;
if (nsectors <= 0)
fatal("%s: no default #sectors/track", argv[0]);
}
if (sectorsize == 0) {
sectorsize = geom.d_media_blksize;
if (sectorsize <= 0)
fatal("%s: no default sector size", argv[0]);
}
if (trackskew == -1) {
trackskew = geom.d_trackskew;
if (trackskew < 0)
trackskew = 0;
}
if (interleave == 0) {
interleave = geom.d_interleave;
if (interleave <= 0)
interleave = 1;
}
if (fsize == 0)
fsize = MAX(DFL_FRAGSIZE, geom.d_media_blksize);
if (bsize == 0)
bsize = MIN(DFL_BLKSIZE, 8 * fsize);
if (maxcontig == 0)
maxcontig = MAX(1, MAXPHYS / bsize - 1);
if (density == 0)
density = NFPI * fsize;
if (minfree < MINFREE && opt != FS_OPTSPACE) {
fprintf(stderr, "Warning: changing optimization to space ");
fprintf(stderr, "because minfree is less than %d%%\n", MINFREE);
opt = FS_OPTSPACE;
}
if (trackspares == -1)
trackspares = 0;
nphyssectors = nsectors + trackspares;
if (cylspares == -1)
cylspares = 0;
secpercyl = nsectors * ntracks - cylspares;
if (t_or_u_flag && (uint32_t)secpercyl != geom.d_secpercyl)
fprintf(stderr, "%s (%d) %s (%u)\n",
"Warning: calculated sectors per cylinder", secpercyl,
"disagrees with disk label", geom.d_secpercyl);
if (maxbpg == 0)
maxbpg = MAXBLKPG(bsize);
headswitch = geom.d_headswitch;
trackseek = geom.d_trkseek;
realsectorsize = sectorsize;
if (sectorsize != DEV_BSIZE) {
int secperblk = sectorsize / DEV_BSIZE;
sectorsize = DEV_BSIZE;
nsectors *= secperblk;
nphyssectors *= secperblk;
secpercyl *= secperblk;
fssize *= secperblk;
}
if (mfs) {
mfs_mtpt = argv[1];
if (
stat(mfs_mtpt, &mfs_mtstat) < 0 ||
!S_ISDIR(mfs_mtstat.st_mode)
) {
fatal("mount point not dir: %s", mfs_mtpt);
}
}
mkfs(special, fsi, fso, (Cflag && mfs) ? argv[1] : NULL);
if (!Nflag)
close(fso);
close(fsi);
#ifdef MFS
if (mfs) {
struct mfs_args args;
bzero(&args, sizeof(args));
snprintf(mfsdevname, sizeof(mfsdevname), "/dev/mfs%d",
getpid());
args.fspec = mfsdevname;
args.export.ex_root = -2;
if (mntflags & MNT_RDONLY)
args.export.ex_flags = MNT_EXRDONLY;
else
args.export.ex_flags = 0;
args.base = membase;
args.size = fssize * fsize;
error = getvfsbyname("mfs", &vfc);
if (error && vfsisloadable("mfs")) {
if (vfsload("mfs"))
fatal("vfsload(mfs)");
endvfsent();
error = getvfsbyname("mfs", &vfc);
}
if (error)
fatal("mfs filesystem not available");
#if 0
int udev;
udev = (253 << 8) | (getpid() & 255) |
((getpid() & ~0xFF) << 8);
if (mknod(mfsdevname, S_IFCHR | 0700, udev) < 0)
printf("Warning: unable to create %s\n", mfsdevname);
#endif
signal(SIGINT, mfsintr);
if (mount(vfc.vfc_name, argv[1], mntflags, &args) < 0)
fatal("%s: %s", argv[1], strerror(errno));
signal(SIGINT, SIG_DFL);
mfsintr(SIGINT);
}
#endif
exit(0);
}
#ifdef MFS
static void
mfsintr(__unused int signo)
{
if (filename)
munmap(membase, fssize * fsize);
#if 0
remove(mfsdevname);
#endif
}
#endif
void
fatal(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
if (fcntl(STDERR_FILENO, F_GETFL) < 0) {
openlog(progname, LOG_CONS, LOG_DAEMON);
vsyslog(LOG_ERR, fmt, ap);
closelog();
} else {
vwarnx(fmt, ap);
}
va_end(ap);
exit(1);
}
void
usage(void)
{
if (mfs) {
fprintf(stderr,
"usage: %s [ -fsoptions ] special-device mount-point\n",
progname);
} else
fprintf(stderr,
"usage: %s [ -fsoptions ] special-device%s\n",
progname,
#ifdef COMPAT
" [device-type]");
#else
"");
#endif
fprintf(stderr, "where fsoptions are:\n");
fprintf(stderr, "\t-C (mfs) Copy the underlying filesystem to the MFS mount\n");
fprintf(stderr, "\t-E erase file system contents using TRIM\n");
fprintf(stderr, "\t-L volume name\n");
fprintf(stderr,
"\t-N do not create file system, just print out parameters\n");
fprintf(stderr, "\t-O create a 4.3BSD format filesystem\n");
fprintf(stderr, "\t-S sector size\n");
#ifdef COMPAT
fprintf(stderr, "\t-T disktype\n");
#endif
fprintf(stderr, "\t-U enable soft updates\n");
fprintf(stderr, "\t-a maximum contiguous blocks\n");
fprintf(stderr, "\t-b block size\n");
fprintf(stderr, "\t-c cylinders/group\n");
fprintf(stderr, "\t-d rotational delay between contiguous blocks\n");
fprintf(stderr, "\t-e maximum blocks per file in a cylinder group\n");
fprintf(stderr, "\t-f frag size\n");
fprintf(stderr, "\t-g average file size\n");
fprintf(stderr, "\t-h average files per directory\n");
fprintf(stderr, "\t-i number of bytes per inode\n");
fprintf(stderr, "\t-k sector 0 skew, per track\n");
fprintf(stderr, "\t-l hardware sector interleave\n");
fprintf(stderr, "\t-m minimum free space %%\n");
fprintf(stderr, "\t-n number of distinguished rotational positions\n");
fprintf(stderr, "\t-o optimization preference (`space' or `time')\n");
fprintf(stderr, "\t-p spare sectors per track\n");
fprintf(stderr, "\t-s file system size (sectors)\n");
fprintf(stderr, "\t-r revolutions/minute\n");
fprintf(stderr, "\t-t tracks/cylinder\n");
fprintf(stderr, "\t-u sectors/track\n");
fprintf(stderr,
"\t-v do not attempt to determine partition name from device name\n");
fprintf(stderr, "\t-x spare sectors per cylinder\n");
exit(1);
}