#include <sys/mount.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <err.h>
#include <errno.h>
#include <mntopts.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sysexits.h>
#include <unistd.h>
#include "ntfs.h"
#include "ntfs_types.h"
static struct mntopt mopts[] = {
MOPT_STDOPTS,
MOPT_FSTAB_COMPAT,
MOPT_ASYNC,
MOPT_SYNC,
MOPT_FORCE,
MOPT_UPDATE,
MOPT_RELOAD,
{ NULL, 0, 0, 0 }
};
static void usage(const char *progname) __attribute__((noreturn));
static void usage(const char *progname)
{
errx(EX_USAGE, "usage: %s [-s] [-o options] special-device "
"filesystem-node\n", progname);
}
static int do_exec(const char *progname, char *const args[])
{
pid_t pid;
union wait status;
int eo;
pid = fork();
if (pid == -1) {
fprintf(stderr, "%s: fork failed: %s\n", progname,
strerror(errno));
return -1;
}
if (!pid) {
(void)execv(args[0], args);
eo = errno;
fprintf(stderr, "%s: execv %s failed: %s\n", progname, args[0],
strerror(eo));
exit(eo);
}
if (wait4(pid, (int*)&status, 0, NULL) != pid) {
fprintf(stderr, "%s: BUG executing %s command.\n", progname,
args[0]);
return -1;
}
if (!WIFEXITED(status)) {
fprintf(stderr, "%s: %s command aborted by signal %d.\n",
progname, args[0], WTERMSIG(status));
return -1;
}
eo = WEXITSTATUS(status);
if (eo) {
fprintf(stderr, "%s: %s command failed: %s\n", progname,
args[0], strerror(eo));
return -1;
}
return 0;
}
static void rmslashes(char *rrpin, char *rrpout)
{
char *rrpoutstart;
*rrpout = *rrpin;
for (rrpoutstart = rrpout; *rrpin != '\0'; *rrpout++ = *rrpin++) {
while (*rrpin == '/' && *(rrpin + 1) == '/')
rrpin++;
}
if (rrpout - rrpoutstart > 1 && *(rrpout - 1) == '/')
*(rrpout - 1) = '\0';
else
*rrpout = '\0';
}
static void checkpath(const char *path, char *resolved)
{
struct stat sb;
if (!realpath(path, resolved) || stat(resolved, &sb))
err(EX_USAGE, "%s", resolved);
if (!S_ISDIR(sb.st_mode))
errx(EX_USAGE, "%s: not a directory", resolved);
}
int main(int argc, char **argv)
{
char *progname, *dev;
ntfs_mount_options_header *opts_hdr;
ntfs_mount_options_1_0 *opts;
int ch, dummy, flags = 0;
char dir[MAXPATHLEN];
const char ntfs[] = "ntfs";
char *const kextargs[] = { "/sbin/kextload",
"/System/Library/Extensions/ntfs.kext", NULL };
struct vfsconf vfc;
BOOL case_sensitive;
flags = MNT_RDONLY;
progname = argv[0];
case_sensitive = FALSE;
while ((ch = getopt(argc, argv, "so:h?")) != -1) {
switch (ch) {
case 's':
case_sensitive = TRUE;
break;
case 'o': {
mntoptparse_t tmp;
tmp = getmntopts(optarg, mopts, &flags, &dummy);
if (!tmp)
err(EX_OSERR, "getmntopts() failed");
freemntopts(tmp);
break;
}
case 'h':
case '?':
default:
usage(progname);
break;
}
}
argc -= optind;
argv += optind;
if (argc != 2)
usage(progname);
dev = argv[0];
checkpath(argv[1], dir);
rmslashes(dev, dev);
opts_hdr = valloc(((sizeof(*opts_hdr) + 7) & ~7) + sizeof(*opts));
if (!opts_hdr)
err(EX_OSERR, "valloc() failed");
*opts_hdr = (ntfs_mount_options_header) {
.fspec = dev,
.major_ver = 1,
.minor_ver = 0,
};
opts = (ntfs_mount_options_1_0*)((char*)opts_hdr +
((sizeof(*opts_hdr) + 7) & ~7));
*opts = (ntfs_mount_options_1_0) {
.flags = case_sensitive ? NTFS_MNT_OPT_CASE_SENSITIVE : 0,
};
if (getvfsbyname(ntfs, &vfc)) {
(void)do_exec(progname, kextargs);
if (getvfsbyname(ntfs, &vfc))
errx(EX_OSERR, "Failed to load NTFS file system kext.");
}
if (mount(ntfs, dir, flags, opts_hdr) < 0)
err(EX_OSERR, "%s on %s", dev, dir);
free(opts_hdr);
return 0;
}