#include <sys/cdefs.h>
#include <sys/param.h>
#include <sys/mount.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/vnode.h>
#include <sys/wait.h>
#include <sys/time.h> // gettimeofday
#include <ctype.h>
#include <err.h>
#include <grp.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <hfs/hfs_mount.h>
#include <hfs/hfs_format.h>
#include <sys/attr.h>
#include <errno.h>
#include "hfs_endian.h"
#define Radar_2238317 1
#if ! Radar_2238317
#include <mntopts.h>
#else // Radar_2238317
struct mntopt {
const char *m_option;
int m_inverse;
int m_flag;
int m_altloc;
};
#define MOPT_ASYNC { "async", 0, MNT_ASYNC, 0 }
#define MOPT_NODEV { "dev", 1, MNT_NODEV, 0 }
#define MOPT_NOEXEC { "exec", 1, MNT_NOEXEC, 0 }
#define MOPT_NOSUID { "suid", 1, MNT_NOSUID, 0 }
#define MOPT_RDONLY { "rdonly", 0, MNT_RDONLY, 0 }
#define MOPT_SYNC { "sync", 0, MNT_SYNCHRONOUS, 0 }
#define MOPT_UNION { "union", 0, MNT_UNION, 0 }
#define MOPT_USERQUOTA { "userquota", 0, 0, 0 }
#define MOPT_GROUPQUOTA { "groupquota", 0, 0, 0 }
#define MOPT_PERMISSIONS { "perm", 1, MNT_UNKNOWNPERMISSIONS, 0 }
#define MOPT_FORCE { "force", 0, MNT_FORCE, 0 }
#define MOPT_UPDATE { "update", 0, MNT_UPDATE, 0 }
#define MOPT_RO { "ro", 0, MNT_RDONLY, 0 }
#define MOPT_RW { "rw", 1, MNT_RDONLY, 0 }
#define MOPT_AUTO { "auto", 0, 0, 0 }
#define MOPT_FSTAB_COMPAT \
MOPT_RO, \
MOPT_RW, \
MOPT_AUTO
#define MOPT_STDOPTS \
MOPT_USERQUOTA, \
MOPT_GROUPQUOTA, \
MOPT_FSTAB_COMPAT, \
MOPT_NODEV, \
MOPT_NOEXEC, \
MOPT_NOSUID, \
MOPT_RDONLY, \
MOPT_UNION, \
MOPT_PERMISSIONS
void getmntopts __P((const char *, const struct mntopt *, int *, int *));
extern int getmnt_silent;
#endif // Radar_2238317
struct mntopt mopts[] = {
MOPT_STDOPTS,
MOPT_UPDATE,
{ NULL }
};
#define HFS_MOUNT_TYPE "hfs"
#define DEFAULT_ROOTUID -2
#define DEFAULT_ANON_UID -2
gid_t a_gid __P((char *));
uid_t a_uid __P((char *));
mode_t a_mask __P((char *));
struct hfs_mnt_encoding * a_encoding __P((char *));
void usage __P((void));
typedef struct CreateDateAttrBuf {
u_long size;
struct timespec creationTime;
} CreateDateAttrBuf;
#define HFS_BLOCK_SIZE 512
#define MAC_GMT_FACTOR 2082844800UL
#define KEXT_LOAD_COMMAND "/sbin/kextload"
#define ENCODING_MODULE_PATH "/System/Library/Filesystems/hfs.fs/Encodings/"
#define MXENCDNAMELEN 16
struct hfs_mnt_encoding {
char encoding_name[MXENCDNAMELEN];
u_long encoding_id;
};
struct hfs_mnt_encoding hfs_mnt_encodinglist[] = {
{ "Arabic", 4 },
{ "Armenian", 24 },
{ "Bengali", 13 },
{ "Burmese", 19 },
{ "Celtic", 39 },
{ "CentralEurRoman", 29 },
{ "ChineseSimp", 25 },
{ "ChineseTrad", 2 },
{ "Croatian", 36 },
{ "Cyrillic", 7 },
{ "Devanagari", 9 },
{ "Ethiopic", 28 },
{ "Farsi", 140 },
{ "Gaelic", 40 },
{ "Georgian", 23 },
{ "Greek", 6 },
{ "Gujarati", 11 },
{ "Gurmukhi", 10 },
{ "Hebrew", 5 },
{ "Icelandic", 37 },
{ "Japanese", 1 },
{ "Kannada", 16 },
{ "Khmer", 20 },
{ "Korean", 3 },
{ "Laotian", 22 },
{ "Malayalam", 17 },
{ "Mongolian", 27 },
{ "Oriya", 12 },
{ "Roman", 0 },
{ "Romanian", 38 },
{ "Sinhalese", 18 },
{ "Tamil", 14 },
{ "Telugu", 15 },
{ "Thai", 21 },
{ "Tibetan", 26 },
{ "Turkish", 35 },
{ "Ukrainian", 152 },
{ "Vietnamese", 30 },
};
u_long getVolumeCreateDate(const char *device)
{
int fd = 0;
off_t offset;
char * bufPtr;
HFSMasterDirectoryBlock * mdbPtr;
u_long volume_create_time = 0;
bufPtr = (char *)malloc(HFS_BLOCK_SIZE);
if ( ! bufPtr ) goto exit;
fd = open( device, O_RDONLY | O_NDELAY, 0 );
if( fd <= 0 ) goto exit;
offset = (off_t)(2 * HFS_BLOCK_SIZE);
if (lseek(fd, offset, SEEK_SET) != offset) goto exit;
if (read(fd, bufPtr, HFS_BLOCK_SIZE) != HFS_BLOCK_SIZE) goto exit;
mdbPtr = (HFSMasterDirectoryBlock *) bufPtr;
if ((mdbPtr->drSigWord == SWAP_BE16 (kHFSSigWord)) &&
(mdbPtr->drEmbedSigWord == SWAP_BE16 (kHFSPlusSigWord))) {
volume_create_time = SWAP_BE32 (mdbPtr->drCrDate);
} else if (mdbPtr->drSigWord == kHFSPlusSigWord ) {
HFSPlusVolumeHeader * volHdrPtr = (HFSPlusVolumeHeader *) bufPtr;
volume_create_time = SWAP_BE32 (volHdrPtr->createDate);
} else {
goto exit;
}
if (volume_create_time > MAC_GMT_FACTOR)
volume_create_time -= MAC_GMT_FACTOR;
else
volume_create_time = 0;
exit:
if ( fd > 0 )
close( fd );
if ( bufPtr )
free( bufPtr );
return volume_create_time;
}
void syncCreateDate(const char *mntpt, u_long localCreateTime)
{
int result;
char path[256];
struct attrlist attributes;
CreateDateAttrBuf attrReturnBuffer;
int64_t gmtCreateTime;
int32_t gmtOffset;
int32_t newCreateTime;
snprintf(path, sizeof(path), "%s/", mntpt);
attributes.bitmapcount = ATTR_BIT_MAP_COUNT;
attributes.reserved = 0;
attributes.commonattr = ATTR_CMN_CRTIME;
attributes.volattr = 0;
attributes.dirattr = 0;
attributes.fileattr = 0;
attributes.forkattr = 0;
result = getattrlist(path, &attributes, &attrReturnBuffer, sizeof(attrReturnBuffer), 0 );
if (result) return;
gmtCreateTime = attrReturnBuffer.creationTime.tv_sec;
gmtOffset = gmtCreateTime - (int64_t) localCreateTime + 900;
if (gmtOffset > 0) {
gmtOffset = 1800 * (gmtOffset / 1800);
} else {
gmtOffset = -1800 * ((-gmtOffset + 1799) / 1800);
}
newCreateTime = localCreateTime + gmtOffset;
if ((newCreateTime != attrReturnBuffer.creationTime.tv_sec) &&
(( newCreateTime - attrReturnBuffer.creationTime.tv_sec) > -15) &&
((newCreateTime - attrReturnBuffer.creationTime.tv_sec) < 15)) {
attrReturnBuffer.creationTime.tv_sec = (u_long) newCreateTime;
(void) setattrlist (path,
&attributes,
&attrReturnBuffer.creationTime,
sizeof(attrReturnBuffer.creationTime),
0);
}
}
static void
load_encoding(struct hfs_mnt_encoding *encp)
{
int pid;
int loaded;
union wait status;
struct stat sb;
char kmodfile[MAXPATHLEN];
if (encp->encoding_id == 0) return;
sprintf(kmodfile, "%sHFS_Mac%s.kext", ENCODING_MODULE_PATH, encp->encoding_name);
if (stat(kmodfile, &sb) == -1)
errx(1, "unable to find: %s", kmodfile);
loaded = 0;
pid = fork();
if (pid == 0) {
(void) execl(KEXT_LOAD_COMMAND, KEXT_LOAD_COMMAND, kmodfile, NULL);
exit(1);
} else if (pid != -1) {
if ((waitpid(pid, (int *)&status, 0) == pid) && WIFEXITED(status)) {
loaded = 1;
}
}
if (!loaded)
errx(1, "unable to load: %s", kmodfile);
}
int
main(argc, argv)
int argc;
char **argv;
{
struct hfs_mount_args args;
int ch, mntflags;
char *dev, *dir;
int mountStatus;
struct timeval dummy_timeval;
u_long localCreateTime;
struct hfs_mnt_encoding *encp;
mntflags = 0;
encp = NULL;
(void)memset(&args, '\0', sizeof(struct hfs_mount_args));
args.flags = VNOVAL;
args.hfs_uid = (uid_t)VNOVAL;
args.hfs_gid = (gid_t)VNOVAL;
args.hfs_mask = (mode_t)VNOVAL;
args.hfs_encoding = (u_long)VNOVAL;
optind = optreset = 1;
while ((ch = getopt(argc, argv, "xu:g:m:e:o:wt:jc")) != EOF)
switch (ch) {
case 't': {
char *ptr;
args.journal_tbuffer_size = strtoul(optarg, &ptr, 0);
if (errno != 0) {
fprintf(stderr, "%s: Invalid tbuffer size %s\n", argv[0], optarg);
exit(5);
} else {
if (*ptr == 'k')
args.journal_tbuffer_size *= 1024;
else if (*ptr == 'm')
args.journal_tbuffer_size *= 1024*1024;
}
if (args.flags == VNOVAL)
args.flags = HFSFSMNT_EXTENDED_ARGS;
break;
}
case 'j':
args.journal_disable = 1;
break;
case 'c':
args.journal_flags = 0x0001;
break;
case 'x':
if (args.flags == VNOVAL)
args.flags = 0;
args.flags |= HFSFSMNT_NOXONFILES;
break;
case 'u':
args.hfs_uid = a_uid(optarg);
break;
case 'g':
args.hfs_gid = a_gid(optarg);
break;
case 'm':
args.hfs_mask = a_mask(optarg);
break;
case 'e':
encp = a_encoding(optarg);
break;
case 'o':
{
int dummy;
getmntopts(optarg, mopts, &mntflags, &dummy);
if (mntflags & MNT_UNKNOWNPERMISSIONS) {
if (args.hfs_uid == (uid_t)VNOVAL) args.hfs_uid = UNKNOWNUID;
if (args.hfs_gid == (gid_t)VNOVAL) args.hfs_gid = UNKNOWNGID;
#if OVERRIDE_UNKNOWN_PERMISSIONS
if (args.hfs_mask == (mode_t)VNOVAL) args.hfs_mask = ACCESSPERMS;
#endif
};
}
break;
case 'w':
if (args.flags == VNOVAL)
args.flags = 0;
args.flags |= HFSFSMNT_WRAPPER;
break;
case '?':
usage();
break;
default:
#if DEBUG
printf("mount_hfs: ERROR: unrecognized ch = '%c'\n", ch);
#endif
usage();
};
argc -= optind;
argv += optind;
if (argc != 2) {
#if DEBUG
printf("mount_hfs: ERROR: argc == %d != 2\n", argc);
#endif
usage();
}
dev = argv[0];
dir = argv[1];
args.fspec = dev;
args.export.ex_root = DEFAULT_ROOTUID;
args.export.ex_anon.cr_uid = DEFAULT_ANON_UID;
if (mntflags & MNT_RDONLY)
args.export.ex_flags = MNT_EXRDONLY;
else
args.export.ex_flags = 0;
(void) gettimeofday( &dummy_timeval, &args.hfs_timezone );
if (encp != NULL) {
load_encoding(encp);
args.hfs_encoding = encp->encoding_id;
}
if ((mntflags & MNT_UPDATE) == 0) {
struct stat sb;
if (args.flags == VNOVAL)
args.flags = 0;
if (args.hfs_encoding == (u_long)VNOVAL)
args.hfs_encoding = 0;
if (strcmp(dir, "/") == 0) {
sb.st_mode = 0777;
sb.st_uid = 0;
sb.st_gid = 0;
} else if (stat(dir, &sb) == -1)
err(1, "stat %s", dir);
if (args.hfs_uid == (uid_t)VNOVAL)
args.hfs_uid = sb.st_uid;
if (args.hfs_gid == (gid_t)VNOVAL)
args.hfs_gid = sb.st_gid;
if (args.hfs_mask == (mode_t)VNOVAL)
args.hfs_mask = sb.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO);
}
#if DEBUG
printf("mount_hfs: calling mount: \n" );
printf("\tdevice = %s\n", dev);
printf("\tmount point = %s\n", dir);
printf("\tmount flags = 0x%08x\n", mntflags);
printf("\targ flags = 0x%x\n", args.flags);
printf("\tuid = %d\n", args.hfs_uid);
printf("\tgid = %d\n", args.hfs_gid);
printf("\tmode = %o\n", args.hfs_mask);
printf("\tencoding = %ld\n", args.hfs_encoding);
#endif
if ((mntflags & MNT_RDONLY) == 0) {
localCreateTime = getVolumeCreateDate(dev);
}
else {
localCreateTime = 0;
}
if ((mountStatus = mount(HFS_MOUNT_TYPE, dir, mntflags, &args)) < 0) {
#if DEBUG
printf("mount_hfs: error on mount(): error = %d.\n", mountStatus);
#endif
err(1, NULL);
};
if (localCreateTime)
syncCreateDate(dir, localCreateTime);
exit(0);
}
gid_t
a_gid(s)
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);
}
uid_t
a_uid(s)
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);
}
mode_t
a_mask(s)
char *s;
{
int done, rv;
char *ep;
done = 0;
rv = -1;
if (*s >= '0' && *s <= '7') {
done = 1;
rv = strtol(optarg, &ep, 8);
}
if (!done || rv < 0 || *ep)
errx(1, "invalid file mode: %s", s);
return (rv);
}
struct hfs_mnt_encoding *
a_encoding(s)
char *s;
{
char *uname;
int i;
u_long encoding;
struct hfs_mnt_encoding *p, *q, *enclist;
int elements = sizeof(hfs_mnt_encodinglist) / sizeof(struct hfs_mnt_encoding);
int compare;
p = hfs_mnt_encodinglist;
q = p + (elements - 1);
while (p <= q) {
enclist = p + ((q - p) >> 1);
compare = strcmp(s, enclist->encoding_name);
if (compare < 0)
q = enclist - 1;
else if (compare > 0)
p = enclist + 1;
else
return (enclist);
}
for (uname = s; *s && isdigit(*s); ++s);
if (*s) goto unknown;
encoding = atoi(uname);
for (i=0, enclist = hfs_mnt_encodinglist; i < elements; i++, enclist++) {
if (enclist->encoding_id == encoding)
return (enclist);
}
unknown:
errx(1, "unknown encoding: %s", uname);
return (NULL);
}
void
usage()
{
(void)fprintf(stderr,
"usage: mount_hfs [-xw] [-u user] [-g group] [-m mask] [-e encoding] [-t tbuffer-size] [-j] [-c] [-o options] special-device filesystem-node\n");
(void)fprintf(stderr, " -j disables journaling; -c disables group-commit for journaling\n");
exit(1);
}