#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <grp.h>
#include <paths.h>
#include <pwd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <syslog.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/mount.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <IOKit/storage/IOMediaBSDClient.h>
#include <hfs/hfs_format.h>
#include "newfs_hfs.h"
#if __STDC__
#include <stdarg.h>
#else
#include <varargs.h>
#endif
#define NOVAL (-1)
#define UMASK (0755)
#define ACCESSMASK (0777)
#define MAXHFSVOLSIZE (0x7FFFFFFF80000000ULL)
#define ROUNDUP(x,y) (((x)+(y)-1)/(y)*(y))
static void getnodeopts __P((char* optlist));
static void getclumpopts __P((char* optlist));
static gid_t a_gid __P((char *));
static uid_t a_uid __P((char *));
static mode_t a_mask __P((char *));
static int hfs_newfs __P((char *device));
static void validate_hfsplus_block_size __P((UInt64 sectorCount, UInt32 sectorSize));
static void hfsplus_params __P((const DriveInfo* dip, hfsparams_t *defaults));
static UInt32 clumpsizecalc __P((UInt32 clumpblocks));
static UInt32 CalcHFSPlusBTreeClumpSize __P((UInt32 blockSize, UInt32 nodeSize, UInt64 sectors, int fileID));
static void usage __P((void));
static int get_high_bit (u_int64_t bitstring);
static int bad_disk_size (u_int64_t numsectors, u_int64_t sectorsize);
char *progname;
char gVolumeName[kHFSPlusMaxFileNameChars + 1] = {kDefaultVolumeNameStr};
char rawdevice[MAXPATHLEN];
char blkdevice[MAXPATHLEN];
uint32_t gBlockSize = 0;
UInt32 gNextCNID = kHFSFirstUserCatalogNodeID;
time_t createtime;
int gNoCreate = FALSE;
int gUserCatNodeSize = FALSE;
int gCaseSensitive = FALSE;
int gUserAttrSize = FALSE;
int gContentProtect = FALSE;
#ifdef DEBUG_BUILD
uint16_t gProtectLevel = 0;
#endif
#define JOURNAL_DEFAULT_SIZE (8*1024*1024)
int gJournaled = FALSE;
char *gJournalDevice = NULL;
UInt64 gJournalSize = 0;
uid_t gUserID = (uid_t)NOVAL;
gid_t gGroupID = (gid_t)NOVAL;
mode_t gModeMask = (mode_t)NOVAL;
UInt64 gPartitionSize = 0;
UInt32 catnodesiz = 8192;
UInt32 extnodesiz = 4096;
UInt32 atrnodesiz = 8192;
UInt32 catclumpblks = 0;
UInt32 extclumpblks = 0;
UInt32 atrclumpblks = 0;
UInt32 bmclumpblks = 0;
UInt32 rsrclumpblks = 0;
UInt32 datclumpblks = 0;
uint32_t hfsgrowblks = 0;
UInt64
get_num(char *str)
{
UInt64 num;
char *ptr;
num = strtoull(str, &ptr, 0);
if (*ptr) {
char scale = tolower(*ptr);
switch(scale) {
case 'b':
num *= 512ULL;
break;
case 'p':
num *= 1024ULL;
case 't':
num *= 1024ULL;
case 'g':
num *= 1024ULL;
case 'm':
num *= 1024ULL;
case 'k':
num *= 1024ULL;
break;
default:
num = 0ULL;
break;
}
}
return num;
}
int
main(argc, argv)
int argc;
char **argv;
{
extern char *optarg;
extern int optind;
int ch;
char *cp, *special;
struct statfs *mp;
int n;
if ((progname = strrchr(*argv, '/')))
++progname;
else
progname = *argv;
#ifdef DEBUG_BUILD
while ((ch = getopt(argc, argv, "G:J:D:M:N:PU:hsb:c:i:n:v:p:")) != -1)
#else
while ((ch = getopt(argc, argv, "G:J:D:M:N:PU:hsb:c:i:n:v:")) != -1)
#endif
switch (ch) {
case 'G':
gGroupID = a_gid(optarg);
break;
case 'J':
gJournaled = TRUE;
if (isdigit(optarg[0])) {
gJournalSize = get_num(optarg);
if (gJournalSize < 512*1024) {
printf("%s: journal size %lldk too small. Reset to %dk.\n",
progname, gJournalSize/1024, JOURNAL_DEFAULT_SIZE/1024);
gJournalSize = JOURNAL_DEFAULT_SIZE;
}
} else {
optind--;
}
break;
case 'D':
gJournalDevice = (char *)optarg;
break;
case 'N':
gNoCreate = TRUE;
if (isdigit(optarg[0])) {
gPartitionSize = get_num(optarg);
} else {
optind--;
}
break;
case 'P':
gContentProtect = TRUE;
break;
#ifdef DEBUG_BUILD
case 'p':
if (isdigit (optarg[0])) {
uint64_t level = get_num (optarg);
gProtectLevel = (uint16_t) level;
}
else {
optind--;
}
break;
#endif
case 'M':
gModeMask = a_mask(optarg);
break;
case 'U':
gUserID = a_uid(optarg);
break;
case 'b':
{
UInt64 tempBlockSize;
tempBlockSize = get_num(optarg);
if (tempBlockSize < HFSMINBSIZE)
fatal("%s: bad allocation block size (too small)", optarg);
if (tempBlockSize > HFSMAXBSIZE)
fatal("%s: bad allocation block size (too large)", optarg);
gBlockSize = tempBlockSize;
break;
}
case 'c':
getclumpopts(optarg);
break;
case 'i':
gNextCNID = atoi(optarg);
if (gNextCNID < kHFSFirstUserCatalogNodeID)
fatal("%s: starting catalog node id too small (must be > 15)", optarg);
break;
case 'n':
getnodeopts(optarg);
break;
case 's':
gCaseSensitive = TRUE;
break;
case 'v':
n = strlen(optarg);
if (n > (sizeof(gVolumeName) - 1))
fatal("\"%s\" is too long (%d byte maximum)",
optarg, sizeof(gVolumeName) - 1);
if (n == 0)
fatal("name required with -v option");
strlcpy(gVolumeName, optarg, sizeof(gVolumeName));
break;
case '?':
default:
usage();
}
argc -= optind;
argv += optind;
#ifdef DEBUG_BUILD
if ((gProtectLevel) && !(gContentProtect)) {
fatal ("content protection must be specified to set a protection level");
}
#endif
if (gPartitionSize != 0) {
if (argc != 0)
usage();
rawdevice[0] = blkdevice[0] = 0;
} else {
if (argc != 1)
usage();
special = argv[0];
cp = strrchr(special, '/');
if (cp != 0)
special = cp + 1;
if (*special == 'r')
special++;
(void) snprintf(rawdevice, sizeof(rawdevice), "%sr%s", _PATH_DEV, special);
(void) snprintf(blkdevice, sizeof(blkdevice), "%s%s", _PATH_DEV, special);
}
if (gPartitionSize == 0) {
n = getmntinfo(&mp, MNT_NOWAIT);
if (n == 0)
fatal("%s: getmntinfo: %s", blkdevice, strerror(errno));
while (--n >= 0) {
if (strcmp(blkdevice, mp->f_mntfromname) == 0)
fatal("%s is mounted on %s", blkdevice, mp->f_mntonname);
++mp;
}
}
if (hfs_newfs(rawdevice) < 0) {
err(1, "cannot create filesystem on %s", rawdevice);
}
exit(0);
}
static void getnodeopts(char* optlist)
{
char *strp = optlist;
char *ndarg;
char *p;
UInt32 ndsize;
while((ndarg = strsep(&strp, ",")) != NULL && *ndarg != '\0') {
p = strchr(ndarg, '=');
if (p == NULL)
usage();
ndsize = atoi(p+1);
switch (*ndarg) {
case 'c':
if (ndsize < 4096 || ndsize > 32768 || (ndsize & (ndsize-1)) != 0)
fatal("%s: invalid catalog b-tree node size", ndarg);
catnodesiz = ndsize;
gUserCatNodeSize = TRUE;
break;
case 'e':
if (ndsize < 1024 || ndsize > 32768 || (ndsize & (ndsize-1)) != 0)
fatal("%s: invalid extents b-tree node size", ndarg);
extnodesiz = ndsize;
break;
case 'a':
if (ndsize < 4096 || ndsize > 32768 || (ndsize & (ndsize-1)) != 0)
fatal("%s: invalid atrribute b-tree node size", ndarg);
atrnodesiz = ndsize;
break;
default:
usage();
}
}
}
static void getclumpopts(char* optlist)
{
char *strp = optlist;
char *ndarg;
char *p;
UInt32 clpblocks;
while((ndarg = strsep(&strp, ",")) != NULL && *ndarg != '\0') {
p = strchr(ndarg, '=');
if (p == NULL)
usage();
clpblocks = atoi(p+1);
switch (*ndarg) {
case 'a':
atrclumpblks = clpblocks;
gUserAttrSize = TRUE;
break;
case 'b':
bmclumpblks = clpblocks;
break;
case 'c':
catclumpblks = clpblocks;
break;
case 'd':
datclumpblks = clpblocks;
break;
case 'e':
extclumpblks = clpblocks;
break;
case 'r':
rsrclumpblks = clpblocks;
break;
default:
usage();
}
}
}
gid_t
static a_gid(char *s)
{
struct group *gr;
char *gname;
gid_t gid = 0;
if ((gr = getgrnam(s)) != NULL)
gid = gr->gr_gid;
else {
for (gname = s; *s && isdigit(*s); ++s);
if (!*s)
gid = atoi(gname);
else
errx(1, "unknown group id: %s", gname);
}
return (gid);
}
static uid_t
a_uid(char *s)
{
struct passwd *pw;
char *uname;
uid_t uid = 0;
if ((pw = getpwnam(s)) != NULL)
uid = pw->pw_uid;
else {
for (uname = s; *s && isdigit(*s); ++s);
if (!*s)
uid = atoi(uname);
else
errx(1, "unknown user id: %s", uname);
}
return (uid);
}
static mode_t
a_mask(char *s)
{
int done, rv;
char *ep;
done = 0;
rv = -1;
if (*s >= '0' && *s <= '7') {
done = 1;
rv = strtol(s, &ep, 8);
}
if (!done || rv < 0 || *ep)
errx(1, "invalid access mask: %s", s);
return (rv);
}
static int bad_disk_size (u_int64_t numsectors, u_int64_t sectorsize) {
u_int32_t maxSectorBits = 0;
u_int32_t maxSectorSizeBits = 0;
u_int32_t maxBits = 0;
u_int64_t bytes;
maxSectorBits = get_high_bit (numsectors);
maxSectorSizeBits = get_high_bit (sectorsize);
maxBits = maxSectorBits + maxSectorSizeBits;
if (maxBits > 63) {
return 1;
}
bytes = numsectors * sectorsize;
if (bytes > MAXHFSVOLSIZE) {
return 1;
}
return 0;
}
#define NUM_ALLOC_BLOCKSIZES 21
#define GROWTH_BOUNDARY 41
u_int64_t alloc_blocksize[NUM_ALLOC_BLOCKSIZES] = {
4096,
8192,
8192,
16384,
32768,
65536,
131072,
262144,
524288,
1048576,
2097152,
4194304,
8388608,
16777216,
33554432,
67108864,
134217728,
268435456,
536870912,
1073741824,
2147483648ULL
};
static int get_high_bit (u_int64_t bitstring) {
u_int64_t bits = bitstring;
int counter = 0;
while (bits) {
bits = (bits >> 1);
counter++;
}
return counter;
}
static void validate_hfsplus_block_size(UInt64 sectorCount, UInt32 sectorSize)
{
if (gBlockSize == 0) {
u_int64_t fs_size = sectorCount * sectorSize;
gBlockSize = DFL_BLKSIZE;
int bit_index = get_high_bit (fs_size);
bit_index -= GROWTH_BOUNDARY;
if ((bit_index >= 0) && (bit_index < 22)) {
gBlockSize = alloc_blocksize[bit_index];
}
if (bit_index >= 22) {
fatal("Error: Disk Device is too big (%llu sectors, %d bytes per sector", sectorCount, sectorSize);
}
}
else {
if ((gBlockSize & (gBlockSize-1)) != 0) {
fatal("%s: bad HFS Plus allocation block size (must be a power of two)", optarg);
}
if ((sectorCount / (gBlockSize / sectorSize)) > 0xFFFFFFFF) {
fatal("%s: block size is too small for %lld sectors", optarg, gBlockSize, sectorCount);
}
if (gBlockSize < HFSOPTIMALBLKSIZE) {
warnx("Warning: %u is a non-optimal block size (4096 would be a better choice)", (unsigned int)gBlockSize);
}
}
}
static int
hfs_newfs(char *device)
{
struct stat stbuf;
DriveInfo dip = { 0 };
int fso = -1;
int retval = 0;
hfsparams_t defaults = {0};
UInt64 maxPhysPerIO = 0;
if (gPartitionSize) {
dip.sectorSize = kBytesPerSector;
dip.physTotalSectors = dip.totalSectors = gPartitionSize / kBytesPerSector;
dip.physSectorSize = kBytesPerSector;
dip.fd = 0;
} else {
if (gNoCreate) {
fso = open( device, O_RDONLY | O_NDELAY, 0 );
} else {
fso = open( device, O_RDWR | O_NDELAY, 0 );
}
if (fso == -1) {
return -1;
}
dip.fd = fso;
fcntl(fso, F_NOCACHE, 1);
if (fso < 0)
fatal("%s: %s", device, strerror(errno));
if (fstat( fso, &stbuf) < 0)
fatal("%s: %s", device, strerror(errno));
if (ioctl(fso, DKIOCGETBLOCKSIZE, &dip.physSectorSize) < 0)
fatal("%s: %s", device, strerror(errno));
if ((dip.physSectorSize % kBytesPerSector) != 0)
fatal("%d is an unsupported sector size\n", dip.physSectorSize);
if (ioctl(fso, DKIOCGETBLOCKCOUNT, &dip.physTotalSectors) < 0)
fatal("%s: %s", device, strerror(errno));
}
dip.physSectorsPerIO = (1024 * 1024) / dip.physSectorSize;
if (fso != -1 && ioctl(fso, DKIOCGETMAXBLOCKCOUNTREAD, &maxPhysPerIO) < 0)
fatal("%s: %s", device, strerror(errno));
if (maxPhysPerIO)
dip.physSectorsPerIO = MIN(dip.physSectorsPerIO, maxPhysPerIO);
if (fso != -1 && ioctl(fso, DKIOCGETMAXBLOCKCOUNTWRITE, &maxPhysPerIO) < 0)
fatal("%s: %s", device, strerror(errno));
if (maxPhysPerIO)
dip.physSectorsPerIO = MIN(dip.physSectorsPerIO, maxPhysPerIO);
if (fso != -1 && ioctl(fso, DKIOCGETMAXBYTECOUNTREAD, &maxPhysPerIO) < 0)
fatal("%s: %s", device, strerror(errno));
if (maxPhysPerIO)
dip.physSectorsPerIO = MIN(dip.physSectorsPerIO, maxPhysPerIO / dip.physSectorSize);
if (fso != -1 && ioctl(fso, DKIOCGETMAXBYTECOUNTWRITE, &maxPhysPerIO) < 0)
fatal("%s: %s", device, strerror(errno));
if (maxPhysPerIO)
dip.physSectorsPerIO = MIN(dip.physSectorsPerIO, maxPhysPerIO / dip.physSectorSize);
dip.sectorSize = kBytesPerSector;
dip.totalSectors = dip.physTotalSectors * dip.physSectorSize / dip.sectorSize;
dip.sectorOffset = 0;
time(&createtime);
u_int64_t secsize = (u_int64_t) dip.sectorSize;
if (bad_disk_size(dip.totalSectors, secsize)) {
fatal("%s: partition is too big (maximum is %llu KB)", device, MAXHFSVOLSIZE/1024);
}
validate_hfsplus_block_size(dip.totalSectors, dip.sectorSize);
if ((dip.totalSectors * dip.sectorSize ) < kMinHFSPlusVolumeSize)
fatal("%s: partition is too small (minimum is %d KB)", device, kMinHFSPlusVolumeSize/1024);
hfsplus_params(&dip, &defaults);
if (gNoCreate == 0) {
retval = make_hfsplus(&dip, &defaults);
if (retval == 0) {
printf("Initialized %s as a ", device);
if (dip.totalSectors > 2048ULL*1024*1024)
printf("%ld TB",
(long)((dip.totalSectors + (1024ULL*1024*1024))/(2048ULL*1024*1024)));
else if (dip.totalSectors > 2048*1024)
printf("%ld GB",
(long)((dip.totalSectors + (1024*1024))/(2048*1024)));
else if (dip.totalSectors > 2048)
printf("%ld MB",
(long)((dip.totalSectors + 1024)/2048));
else
printf("%ld KB",
(long)((dip.totalSectors + 1)/2));
if (gJournaled)
printf(" HFS Plus volume with a %uk journal\n",
(u_int32_t)defaults.journalSize/1024);
else
printf(" HFS Plus volume\n");
}
}
if (retval)
fatal("%s: %s", device, strerror(errno));
if ( fso > 0 ) {
close(fso);
}
return retval;
}
#define BLOCK_INFO_SIZE 16
static void hfsplus_params (const DriveInfo* dip, hfsparams_t *defaults)
{
UInt64 sectorCount = dip->totalSectors;
UInt32 sectorSize = dip->sectorSize;
uint32_t totalBlocks;
UInt32 minClumpSize;
UInt32 clumpSize;
UInt32 oddBitmapBytes;
defaults->flags = 0;
defaults->blockSize = gBlockSize;
defaults->nextFreeFileID = gNextCNID;
defaults->createDate = createtime + MAC_GMT_FACTOR;
defaults->hfsAlignment = 0;
defaults->journaledHFS = gJournaled;
defaults->journalDevice = gJournalDevice;
if (gUserID != (uid_t)NOVAL ||
gGroupID != (gid_t)NOVAL ||
gModeMask != (mode_t)NOVAL) {
defaults->owner = (gUserID == (uid_t)NOVAL) ? geteuid() : gUserID;
defaults->group = (gGroupID == (gid_t)NOVAL) ? getegid() : gGroupID;
defaults->mask = (gModeMask == (mode_t)NOVAL) ? UMASK : (gModeMask & ACCESSMASK);
defaults->flags |= kUseAccessPerms;
}
if (gJournaled) {
if (gJournalSize != 0) {
if (dip->physSectorSize != 0) {
u_int32_t min_size = 0;
min_size = dip->physSectorSize * (dip->physSectorSize / BLOCK_INFO_SIZE);
if (gJournalSize < min_size) {
printf("%s: journal size %lldk too small. Reset to %dk.\n",
progname, gJournalSize/1024, JOURNAL_DEFAULT_SIZE/1024);
gJournalSize = 0;
}
}
defaults->journalSize = gJournalSize;
}
if ((gJournalSize == 0) || (defaults->journalSize == 0)) {
UInt32 jscale;
jscale = (sectorCount * sectorSize) / ((UInt64)100 * 1024 * 1024 * 1024);
if (jscale > 64) {
jscale = 64;
}
defaults->journalSize = JOURNAL_DEFAULT_SIZE * (jscale + 1);
}
if (defaults->journalSize > 512 * 1024 * 1024) {
defaults->journalSize = 512 * 1024 * 1024;
}
if (defaults->journalSize < defaults->blockSize) {
defaults->journalSize = defaults->blockSize;
}
}
if (sectorCount * sectorSize < 128*1024*1024) {
defaults->journalSize = 512 * 1024;
}
strncpy((char *)defaults->volumeName, gVolumeName, sizeof(defaults->volumeName) - 1);
defaults->volumeName[sizeof(defaults->volumeName) - 1] = '\0';
if (rsrclumpblks == 0) {
if (gBlockSize > DFL_BLKSIZE)
defaults->rsrcClumpSize = ROUNDUP(kHFSPlusRsrcClumpFactor * DFL_BLKSIZE, gBlockSize);
else
defaults->rsrcClumpSize = kHFSPlusRsrcClumpFactor * gBlockSize;
} else
defaults->rsrcClumpSize = clumpsizecalc(rsrclumpblks);
if (datclumpblks == 0) {
if (gBlockSize > DFL_BLKSIZE)
defaults->dataClumpSize = ROUNDUP(kHFSPlusRsrcClumpFactor * DFL_BLKSIZE, gBlockSize);
else
defaults->dataClumpSize = kHFSPlusRsrcClumpFactor * gBlockSize;
} else
defaults->dataClumpSize = clumpsizecalc(datclumpblks);
if (!gUserCatNodeSize) {
if ((gBlockSize < HFSOPTIMALBLKSIZE) ||
((UInt64)(sectorCount * sectorSize) < (UInt64)0x40000000))
catnodesiz = 4096;
}
if (catclumpblks == 0) {
clumpSize = CalcHFSPlusBTreeClumpSize(gBlockSize, catnodesiz, sectorCount, kHFSCatalogFileID);
}
else {
clumpSize = clumpsizecalc(catclumpblks);
if (clumpSize % catnodesiz != 0)
fatal("c=%ld: clump size is not a multiple of node size\n", clumpSize/gBlockSize);
}
defaults->catalogClumpSize = clumpSize;
defaults->catalogNodeSize = catnodesiz;
if (gBlockSize < 4096 && gBlockSize < catnodesiz)
warnx("Warning: block size %u is less than catalog b-tree node size %u", (unsigned int)gBlockSize, (unsigned int)catnodesiz);
if (extclumpblks == 0) {
clumpSize = CalcHFSPlusBTreeClumpSize(gBlockSize, extnodesiz, sectorCount, kHFSExtentsFileID);
}
else {
clumpSize = clumpsizecalc(extclumpblks);
if (clumpSize % extnodesiz != 0)
fatal("e=%ld: clump size is not a multiple of node size\n", clumpSize/gBlockSize);
}
defaults->extentsClumpSize = clumpSize;
defaults->extentsNodeSize = extnodesiz;
if (gBlockSize < extnodesiz)
warnx("Warning: block size %u is less than extents b-tree node size %u", (unsigned int)gBlockSize, (unsigned int)extnodesiz);
if (atrclumpblks == 0) {
if (gUserAttrSize) {
clumpSize = 0;
}
else {
clumpSize = CalcHFSPlusBTreeClumpSize(gBlockSize, atrnodesiz, sectorCount, kHFSAttributesFileID);
}
}
else {
clumpSize = clumpsizecalc(atrclumpblks);
if (clumpSize % atrnodesiz != 0)
fatal("a=%ld: clump size is not a multiple of node size\n", clumpSize/gBlockSize);
}
defaults->attributesClumpSize = clumpSize;
defaults->attributesNodeSize = atrnodesiz;
totalBlocks = sectorCount / (gBlockSize / sectorSize);
minClumpSize = totalBlocks >> 3;
if (totalBlocks & 7)
++minClumpSize;
if ((oddBitmapBytes = minClumpSize % gBlockSize))
minClumpSize = minClumpSize - oddBitmapBytes + gBlockSize;
if (bmclumpblks == 0) {
clumpSize = minClumpSize;
}
else {
clumpSize = clumpsizecalc(bmclumpblks);
if (clumpSize < minClumpSize)
fatal("b=%ld: bitmap clump size is too small\n", clumpSize/gBlockSize);
}
defaults->allocationClumpSize = clumpSize;
if (gCaseSensitive)
defaults->flags |= kMakeCaseSensitive;
if (gContentProtect)
defaults->flags |= kMakeContentProtect;
#ifdef DEBUG_BUILD
if (gProtectLevel)
defaults->protectlevel = gProtectLevel;
#endif
if (gNoCreate) {
if (gPartitionSize == 0)
printf("%llu sectors (%u bytes per sector)\n", dip->physTotalSectors, dip->physSectorSize);
printf("HFS Plus format parameters:\n");
printf("\tvolume name: \"%s\"\n", gVolumeName);
printf("\tblock-size: %u\n", defaults->blockSize);
printf("\ttotal blocks: %u\n", totalBlocks);
if (gJournaled)
printf("\tjournal-size: %uk\n", defaults->journalSize/1024);
printf("\tfirst free catalog node id: %u\n", defaults->nextFreeFileID);
printf("\tcatalog b-tree node size: %u\n", defaults->catalogNodeSize);
printf("\tinitial catalog file size: %u\n", defaults->catalogClumpSize);
printf("\textents b-tree node size: %u\n", defaults->extentsNodeSize);
printf("\tinitial extents file size: %u\n", defaults->extentsClumpSize);
printf("\tattributes b-tree node size: %u\n", defaults->attributesNodeSize);
printf("\tinitial attributes file size: %u\n", defaults->attributesClumpSize);
printf("\tinitial allocation file size: %u (%u blocks)\n",
defaults->allocationClumpSize, defaults->allocationClumpSize / gBlockSize);
printf("\tdata fork clump size: %u\n", defaults->dataClumpSize);
printf("\tresource fork clump size: %u\n", defaults->rsrcClumpSize);
if (defaults->flags & kUseAccessPerms) {
printf("\tuser ID: %d\n", (int)defaults->owner);
printf("\tgroup ID: %d\n", (int)defaults->group);
printf("\taccess mask: %o\n", (int)defaults->mask);
}
}
}
static UInt32
clumpsizecalc(UInt32 clumpblocks)
{
UInt64 clumpsize;
clumpsize = (UInt64)clumpblocks * (UInt64)gBlockSize;
if (clumpsize & (UInt64)(0xFFFFFFFF00000000ULL))
fatal("=%ld: too many blocks for clump size!", clumpblocks);
return ((UInt32)clumpsize);
}
#define CLUMP_ENTRIES 15
short clumptbl[CLUMP_ENTRIES * 3] = {
4, 4, 4,
6, 6, 4,
8, 8, 4,
11, 11, 5,
64, 32, 5,
84, 49, 6,
111, 74, 7,
147, 111, 8,
194, 169, 9,
256, 256, 11,
294, 294, 14,
338, 338, 16,
388, 388, 20,
446, 446, 25,
512, 512, 32
};
static UInt32
CalcHFSPlusBTreeClumpSize(UInt32 blockSize, UInt32 nodeSize, UInt64 sectors, int fileID)
{
UInt32 mod = MAX(nodeSize, blockSize);
UInt32 clumpSize;
int column;
int i;
switch (fileID) {
case kHFSAttributesFileID:
column = 0;
break;
case kHFSCatalogFileID:
column = 1;
break;
default:
column = 2;
break;
}
if (sectors < 0x200000) {
clumpSize = sectors << 2;
if (clumpSize < (8 * nodeSize))
clumpSize = 8 * nodeSize;
} else {
for (i = 0, sectors = sectors >> 22;
sectors && (i < CLUMP_ENTRIES-1);
++i, sectors = sectors >> 1);
clumpSize = clumptbl[column + (i) * 3] * 1024 * 1024;
}
clumpSize /= mod;
clumpSize *= mod;
if (clumpSize == 0)
clumpSize = mod;
return (clumpSize);
}
void
#if __STDC__
fatal(const char *fmt, ...)
#else
fatal(fmt, va_alist)
char *fmt;
va_dcl
#endif
{
va_list ap;
#if __STDC__
va_start(ap, fmt);
#else
va_start(ap);
#endif
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()
{
fprintf(stderr, "usage: %s [-N [partition-size]] [hfsplus-options] special-device\n", progname);
fprintf(stderr, " options:\n");
fprintf(stderr, "\t-N do not create file system, just print out parameters\n");
fprintf(stderr, "\t-s use case-sensitive filenames (default is case-insensitive)\n");
fprintf(stderr, " where hfsplus-options are:\n");
fprintf(stderr, "\t-J [journal-size] make this HFS+ volume journaled\n");
fprintf(stderr, "\t-D journal-dev use 'journal-dev' for an external journal\n");
fprintf(stderr, "\t-G group-id (for root directory)\n");
fprintf(stderr, "\t-U user-id (for root directory)\n");
fprintf(stderr, "\t-M octal access-mask (for root directory)\n");
fprintf(stderr, "\t-b allocation block size (4096 optimal)\n");
fprintf(stderr, "\t-c clump size list (comma separated)\n");
fprintf(stderr, "\t\ta=blocks (attributes file)\n");
fprintf(stderr, "\t\tb=blocks (bitmap file)\n");
fprintf(stderr, "\t\tc=blocks (catalog file)\n");
fprintf(stderr, "\t\td=blocks (user data fork)\n");
fprintf(stderr, "\t\te=blocks (extents file)\n");
fprintf(stderr, "\t\tr=blocks (user resource fork)\n");
fprintf(stderr, "\t-i starting catalog node id\n");
fprintf(stderr, "\t-n b-tree node size list (comma separated)\n");
fprintf(stderr, "\t\te=size (extents b-tree)\n");
fprintf(stderr, "\t\tc=size (catalog b-tree)\n");
fprintf(stderr, "\t\ta=size (attributes b-tree)\n");
fprintf(stderr, "\t-v volume name (in ascii or UTF-8)\n");
fprintf(stderr, " examples:\n");
fprintf(stderr, "\t%s -v Untitled /dev/rdisk0s7 \n", progname);
fprintf(stderr, "\t%s -v Untitled -n c=4096,e=1024 /dev/rdisk0s7 \n", progname);
fprintf(stderr, "\t%s -v Untitled -c b=64,c=1024 /dev/rdisk0s7 \n\n", progname);
exit(1);
}