#include <sys/loadable_fs.h>
#ifndef FSUC_GETUUID
#define FSUC_GETUUID 'k'
#endif
#include <sys/disk.h>
#include <sys/ioctl.h>
#include <sys/mount.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <CoreFoundation/CFString.h>
#ifdef NTFS_UTIL_DEBUG
#include <syslog.h>
#endif
#include "ntfs.h"
#include "ntfs_types.h"
#include "ntfs_endian.h"
#include "ntfs_layout.h"
typedef struct {
u32 sector_size;
u32 cluster_size;
u32 mft_record_size;
u32 index_block_size;
LCN mft_lcn;
LCN mftmirr_lcn;
int mftmirr_size;
LCN nr_clusters;
} ntfs_volume;
typedef struct {
MFT_RECORD *m;
ATTR_RECORD *a;
} ntfs_attr_search_ctx;
typedef struct {
VCN vcn;
LCN lcn;
s64 length;
} ntfs_rl_element;
#define NTFS_RL_ALLOC_BLOCK 1024
typedef enum {
LCN_HOLE = -1,
LCN_RL_NOT_MAPPED = -2,
LCN_ENOENT = -3,
LCN_ENOMEM = -4,
LCN_EIO = -5,
} LCN_SPECIAL_VALUES;
static void usage(const char *progname) __attribute__((noreturn));
static void usage(const char *progname)
{
fprintf(stderr, "usage: %s action_arg device_arg [mount_point_arg] [Flags]\n", progname);
fprintf(stderr, "action_arg:\n");
fprintf(stderr, " -%c (Get UUID Key)\n", FSUC_GETUUID);
fprintf(stderr, " -%c (Mount)\n", FSUC_MOUNT);
fprintf(stderr, " -%c (Probe)\n", FSUC_PROBE);
fprintf(stderr, " -%c (Unmount)\n", FSUC_UNMOUNT);
fprintf(stderr, "device_arg:\n");
fprintf(stderr, " device we are acting upon (for example, 'disk0s2')\n");
fprintf(stderr, "mount_point_arg:\n");
fprintf(stderr, " required for Mount and Unmount\n");
fprintf(stderr, "Flags:\n");
fprintf(stderr, " required for Mount and Probe\n");
fprintf(stderr, " indicates removable or fixed (for example 'fixed')\n");
fprintf(stderr, " indicates readonly or writable (for example 'readonly')\n");
fprintf(stderr, "Flags (Mount only):\n");
fprintf(stderr, " indicates suid or nosuid (for example 'nosuid')\n");
fprintf(stderr, " indicates dev or nodev (for example 'nodev')\n");
fprintf(stderr, "Examples:\n");
fprintf(stderr, " %s -p disk0s2 fixed writable\n", progname);
fprintf(stderr, " %s -m disk0s2 /my/hfs removable readonly nosuid nodev\n", progname);
exit(FSUR_INVAL);
}
static BOOL ntfs_boot_sector_is_valid(const NTFS_BOOT_SECTOR *b)
{
if (b->oem_id != magicNTFS)
goto not_ntfs;
if (le16_to_cpu(b->bpb.bytes_per_sector) < 0x100 ||
le16_to_cpu(b->bpb.bytes_per_sector) >
NTFS_MAX_SECTOR_SIZE)
goto not_ntfs;
switch (b->bpb.sectors_per_cluster) {
case 1: case 2: case 4: case 8: case 16: case 32: case 64: case 128:
break;
default:
goto not_ntfs;
}
if ((u32)le16_to_cpu(b->bpb.bytes_per_sector) *
b->bpb.sectors_per_cluster > NTFS_MAX_CLUSTER_SIZE)
goto not_ntfs;
if (le16_to_cpu(b->bpb.reserved_sectors) ||
le16_to_cpu(b->bpb.root_entries) ||
le16_to_cpu(b->bpb.sectors) ||
le16_to_cpu(b->bpb.sectors_per_fat) ||
le32_to_cpu(b->bpb.large_sectors) || b->bpb.fats)
goto not_ntfs;
if ((u8)b->clusters_per_mft_record < 0xe1 ||
(u8)b->clusters_per_mft_record > 0xf7)
switch (b->clusters_per_mft_record) {
case 1: case 2: case 4: case 8: case 16: case 32: case 64:
break;
default:
goto not_ntfs;
}
if ((u8)b->clusters_per_index_block < 0xe1 ||
(u8)b->clusters_per_index_block > 0xf7)
switch (b->clusters_per_index_block) {
case 1: case 2: case 4: case 8: case 16: case 32: case 64:
break;
default:
goto not_ntfs;
}
return TRUE;
not_ntfs:
return FALSE;
}
static int ntfs_boot_sector_parse(ntfs_volume *vol, const NTFS_BOOT_SECTOR *b)
{
s64 ll;
int clusters_per_mft_record;
vol->sector_size = le16_to_cpu(b->bpb.bytes_per_sector);
vol->cluster_size = vol->sector_size * b->bpb.sectors_per_cluster;
if (vol->cluster_size < vol->sector_size)
return FSUR_UNRECOGNIZED;
clusters_per_mft_record = b->clusters_per_mft_record;
if (clusters_per_mft_record > 0)
vol->mft_record_size = vol->cluster_size *
clusters_per_mft_record;
else
vol->mft_record_size = 1 << -clusters_per_mft_record;
ll = sle64_to_cpu(b->number_of_sectors) / b->bpb.sectors_per_cluster;
if ((u64)ll >= (u64)1 << 32)
return FSUR_UNRECOGNIZED;
vol->nr_clusters = ll;
ll = sle64_to_cpu(b->mft_lcn);
if (ll >= vol->nr_clusters)
return FSUR_UNRECOGNIZED;
vol->mft_lcn = ll;
ll = sle64_to_cpu(b->mftmirr_lcn);
if (ll >= vol->nr_clusters)
return FSUR_UNRECOGNIZED;
vol->mftmirr_lcn = ll;
return 0;
}
static int ntfs_mst_fixup_post_read(NTFS_RECORD *b, const u32 size)
{
u16 *usa_pos, *data_pos;
u16 usa_ofs, usa_count, usn;
usa_ofs = le16_to_cpu(b->usa_ofs);
usa_count = le16_to_cpu(b->usa_count) - 1;
if (size & (NTFS_BLOCK_SIZE - 1) || usa_ofs & 1 ||
(u32)usa_ofs + ((u32)usa_count * 2) > size ||
(size >> NTFS_BLOCK_SIZE_SHIFT) != usa_count)
return 0;
usa_pos = (u16*)b + usa_ofs/sizeof(u16);
usn = *usa_pos;
data_pos = (u16*)b + NTFS_BLOCK_SIZE/sizeof(u16) - 1;
while (usa_count--) {
if (*data_pos != usn) {
b->magic = magic_BAAD;
return FSUR_IO_FAIL;
}
data_pos += NTFS_BLOCK_SIZE/sizeof(u16);
}
usa_count = le16_to_cpu(b->usa_count) - 1;
data_pos = (u16*)b + NTFS_BLOCK_SIZE/sizeof(u16) - 1;
while (usa_count--) {
*data_pos = *(++usa_pos);
data_pos += NTFS_BLOCK_SIZE/sizeof(u16);
}
return 0;
}
static void ntfs_attr_search_ctx_init(ntfs_attr_search_ctx *ctx, MFT_RECORD *m)
{
*ctx = (ntfs_attr_search_ctx) {
.m = m,
.a = (ATTR_RECORD*)((u8*)m + le16_to_cpu(m->attrs_offset)),
};
}
static int ntfs_attr_find_in_mft_record(const ATTR_TYPE type,
ntfs_attr_search_ctx *ctx)
{
ATTR_RECORD *a;
a = ctx->a;
for (;; a = (ATTR_RECORD*)((u8*)a + le32_to_cpu(a->length))) {
if ((u8*)a < (u8*)ctx->m || (u8*)a > (u8*)ctx->m +
le32_to_cpu(ctx->m->bytes_allocated))
break;
ctx->a = a;
if (le32_to_cpu(a->type) > le32_to_cpu(type) ||
a->type == AT_END)
return FSUR_UNRECOGNIZED;
if (!a->length)
break;
if (a->type != type)
continue;
if (a->name_length)
return FSUR_UNRECOGNIZED;
return 0;
}
return FSUR_IO_FAIL;
}
static int ntfs_mapping_pairs_decompress(ntfs_volume *vol,
const ATTR_RECORD *a, ntfs_rl_element **runlist)
{
VCN vcn;
LCN lcn;
s64 deltaxcn;
u8 *buf;
u8 *a_end;
ntfs_rl_element *rl;
unsigned rlsize;
int err;
u16 rlpos;
u8 b;
if (!a || !a->non_resident || sle64_to_cpu(a->lowest_vcn) < (VCN)0)
return FSUR_INVAL;
vcn = sle64_to_cpu(a->lowest_vcn);
lcn = 0;
buf = (u8*)a + le16_to_cpu(a->mapping_pairs_offset);
a_end = (u8*)a + le32_to_cpu(a->length);
if (buf < (u8*)a || buf > a_end)
return FSUR_IO_FAIL;
if (!vcn && !*buf)
return FSUR_UNRECOGNIZED;
rlpos = 0;
rlsize = NTFS_RL_ALLOC_BLOCK;
rl = malloc(rlsize);
if (!rl)
return FSUR_INVAL;
if (vcn) {
rl->vcn = 0;
rl->lcn = LCN_RL_NOT_MAPPED;
rl->length = vcn;
rlpos++;
}
while (buf < a_end && *buf) {
if (((rlpos + 3) * sizeof(*rl)) > rlsize) {
ntfs_rl_element *rl2;
rl2 = malloc(rlsize + NTFS_RL_ALLOC_BLOCK);
if (!rl2) {
err = FSUR_INVAL;
goto err;
}
memcpy(rl2, rl, rlsize);
free(rl);
rl = rl2;
rlsize += NTFS_RL_ALLOC_BLOCK;
}
rl[rlpos].vcn = vcn;
b = *buf & 0xf;
if (b) {
if (buf + b >= a_end) {
err = FSUR_IO_FAIL;
goto err;
}
for (deltaxcn = (s8)buf[b--]; b; b--)
deltaxcn = (deltaxcn << 8) + buf[b];
} else
deltaxcn = (s64)-1;
if (deltaxcn < 0) {
err = FSUR_IO_FAIL;
goto err;
}
rl[rlpos].length = deltaxcn;
vcn += deltaxcn;
if (!(*buf & 0xf0))
rl[rlpos].lcn = LCN_HOLE;
else {
u8 b2 = *buf & 0xf;
b = b2 + ((*buf >> 4) & 0xf);
if (buf + b >= a_end) {
err = FSUR_IO_FAIL;
goto err;
}
for (deltaxcn = (s8)buf[b--]; b > b2; b--)
deltaxcn = (deltaxcn << 8) + buf[b];
lcn += deltaxcn;
if (lcn < (LCN)-1) {
err = FSUR_IO_FAIL;
goto err;
}
rl[rlpos].lcn = lcn;
}
rlpos++;
buf += (*buf & 0xf) + ((*buf >> 4) & 0xf) + 1;
}
if (buf >= a_end) {
err = FSUR_IO_FAIL;
goto err;
}
deltaxcn = sle64_to_cpu(a->highest_vcn);
if (deltaxcn && vcn - 1 != deltaxcn) {
err = FSUR_IO_FAIL;
goto err;
}
if (!a->lowest_vcn) {
VCN max_cluster;
max_cluster = ((sle64_to_cpu(a->allocated_size) +
vol->cluster_size - 1) / vol->cluster_size) - 1;
if (deltaxcn) {
if (deltaxcn < max_cluster) {
rl[rlpos].vcn = vcn;
vcn += rl[rlpos].length = max_cluster -
deltaxcn;
rl[rlpos].lcn = LCN_RL_NOT_MAPPED;
rlpos++;
} else if (deltaxcn > max_cluster) {
err = FSUR_IO_FAIL;
goto err;
}
}
rl[rlpos].lcn = LCN_ENOENT;
} else
rl[rlpos].lcn = LCN_RL_NOT_MAPPED;
rl[rlpos].vcn = vcn;
rl[rlpos].length = (s64)0;
*runlist = rl;
return 0;
err:
free(rl);
return err;
}
static LCN ntfs_rl_vcn_to_lcn(const ntfs_rl_element *rl, const VCN vcn,
s64 *clusters)
{
unsigned i;
if (!rl)
return LCN_RL_NOT_MAPPED;
if (vcn < rl[0].vcn)
return LCN_ENOENT;
for (i = 0; rl[i].length; i++) {
if (vcn < rl[i + 1].vcn) {
const s64 ofs = vcn - rl[i].vcn;
if (clusters)
*clusters = rl[i].length - ofs;
if (rl[i].lcn >= (LCN)0)
return rl[i].lcn + ofs;
return rl[i].lcn;
}
}
if (clusters)
*clusters = 0;
if (rl[i].lcn < (LCN)0)
return rl[i].lcn;
return LCN_ENOENT;
}
static ssize_t ntfs_pread(int f, u8 *buf, long sector_size, void *dst,
ssize_t to_read, off_t ofs)
{
off_t io_pos;
ssize_t buf_ofs, to_copy, copied;
copied = 0;
while (to_read > 0) {
io_pos = ofs & ~((off_t)sector_size - 1);
buf_ofs = ofs & ((off_t)sector_size - 1);
to_copy = sector_size - buf_ofs;
if (to_copy > to_read)
to_copy = to_read;
if (pread(f, buf, sector_size, io_pos) < buf_ofs + to_copy) {
if (!copied)
copied = -1;
break;
}
memcpy((u8*)dst + copied, buf + buf_ofs, to_copy);
to_read -= to_copy;
copied += to_copy;
}
return copied;
}
static int get_volume_mft_record(char *rdev, ntfs_volume *vol,
MFT_RECORD **mrec)
{
VCN vcn;
LCN lcn;
s64 clusters, io_size;
void *buf;
NTFS_BOOT_SECTOR *bs;
MFT_RECORD *m;
ntfs_rl_element *rl;
long sector_size;
unsigned vcn_ofs;
int f, err, to_read;
u32 dev_block_size;
ntfs_attr_search_ctx ctx;
sector_size = sysconf(_SC_PAGE_SIZE);
if (sector_size < 0)
sector_size = 32768;
if (sector_size < NTFS_BLOCK_SIZE)
sector_size = NTFS_BLOCK_SIZE;
f = open(rdev, O_RDONLY);
if (f == -1) {
return FSUR_IO_FAIL;
}
if (ioctl(f, DKIOCGETBLOCKSIZE, &dev_block_size) < 0) {
err = FSUR_IO_FAIL;
buf = NULL;
goto err;
}
if (dev_block_size > (u32)sector_size)
sector_size = dev_block_size;
buf = malloc(sector_size);
if (!buf) {
err = FSUR_IO_FAIL;
goto err;
}
if (read(f, buf, sector_size) < NTFS_BLOCK_SIZE) {
err = FSUR_IO_FAIL;
goto err;
}
bs = buf;
if (!ntfs_boot_sector_is_valid(bs)) {
err = FSUR_UNRECOGNIZED;
goto err;
}
err = ntfs_boot_sector_parse(vol, bs);
if (err)
goto err;
m = malloc(vol->mft_record_size);
if (!m) {
err = FSUR_INVAL;
goto err;
}
if (ntfs_pread(f, buf, sector_size, m, vol->mft_record_size,
vol->mft_lcn * vol->cluster_size) !=
(ssize_t)vol->mft_record_size) {
err = FSUR_IO_FAIL;
goto free_err;
}
err = ntfs_mst_fixup_post_read((NTFS_RECORD*)m, vol->mft_record_size);
if (err) {
err = FSUR_IO_FAIL;
goto free_err;
}
ntfs_attr_search_ctx_init(&ctx, m);
err = ntfs_attr_find_in_mft_record(AT_DATA, &ctx);
if (err)
goto free_err;
rl = NULL;
err = ntfs_mapping_pairs_decompress(vol, ctx.a, &rl);
if (err)
goto free_err;
vcn = FILE_Volume * vol->mft_record_size;
vcn_ofs = vcn & (vol->cluster_size - 1);
vcn /= vol->cluster_size;
to_read = vol->mft_record_size;
do {
lcn = ntfs_rl_vcn_to_lcn(rl, vcn, &clusters);
if (lcn < 0) {
err = FSUR_IO_FAIL;
goto rl_free_err;
}
io_size = (clusters * vol->cluster_size) - vcn_ofs;
if (io_size > to_read)
io_size = to_read;
if (ntfs_pread(f, buf, sector_size, m, io_size, (lcn *
vol->cluster_size) + vcn_ofs) != io_size) {
err = FSUR_IO_FAIL;
goto rl_free_err;
}
to_read -= io_size;
vcn += clusters;
vcn_ofs = 0;
} while (to_read > 0);
free(rl);
err = ntfs_mst_fixup_post_read((NTFS_RECORD*)m, vol->mft_record_size);
if (err) {
err = FSUR_IO_FAIL;
goto free_err;
}
(void)close(f);
free(buf);
*mrec = m;
return FSUR_RECOGNIZED;
rl_free_err:
free(rl);
free_err:
free(m);
err:
if (buf)
free(buf);
(void)close(f);
return err;
}
static int do_getuuid(char *rdev)
{
MFT_RECORD *m;
ATTR_RECORD *a;
OBJECT_ID_ATTR *obj_id;
GUID *guid;
unsigned obj_id_len;
int err;
ntfs_volume vol;
ntfs_attr_search_ctx ctx;
char uuid[37];
err = get_volume_mft_record(rdev, &vol, &m);
if (err != FSUR_RECOGNIZED)
goto err;
ntfs_attr_search_ctx_init(&ctx, m);
err = ntfs_attr_find_in_mft_record(AT_OBJECT_ID, &ctx);
if (err) {
if (err != FSUR_UNRECOGNIZED)
goto free_err;
#ifdef NTFS_UTIL_DEBUG
openlog("ntfs.util", LOG_PID, LOG_DAEMON);
syslog(LOG_NOTICE, "Volume does not have a UUID key.\n");
closelog();
#endif
err = FSUR_INVAL;
goto free_err;
}
a = ctx.a;
if (a->non_resident) {
err = FSUR_IO_FAIL;
goto free_err;
}
obj_id = (OBJECT_ID_ATTR*)((u8*)a + le16_to_cpu(a->value_offset));
obj_id_len = le32_to_cpu(a->value_length);
if ((u8*)obj_id + obj_id_len > (u8*)m + vol.mft_record_size ||
(u8*)obj_id + obj_id_len > (u8*)a +
le32_to_cpu(a->length)) {
err = FSUR_IO_FAIL;
goto free_err;
}
guid = &obj_id->object_id;
if (snprintf(uuid, 37, "%08x-%04x-%04x-%02x%02x-"
"%02x%02x%02x%02x%02x%02x", le32_to_cpu(guid->data1),
le16_to_cpu(guid->data2), le16_to_cpu(guid->data3),
guid->data4[0], guid->data4[1], guid->data4[2],
guid->data4[3], guid->data4[4], guid->data4[5],
guid->data4[6], guid->data4[7]) != 36) {
err = FSUR_IO_FAIL;
goto free_err;
}
#ifdef NTFS_UTIL_DEBUG
openlog("ntfs.util", LOG_PID, LOG_DAEMON);
syslog(LOG_NOTICE, "Volume UUID Key: %s\n", uuid);
closelog();
#endif
(void)write(STDOUT_FILENO, uuid, 36);
err = FSUR_IO_SUCCESS;
free_err:
free(m);
err:
return err;
}
static u8 sfm2mac[0x30] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
0x22, 0x2a, 0x3a, 0x3c, 0x3e, 0x3f, 0x5c, 0x7c,
0x20, 0x2e
};
static int do_probe(char *rdev, const BOOL removable __attribute__((unused)),
const BOOL writable __attribute__((unused)))
{
MFT_RECORD *m;
ATTR_RECORD *a;
ntfschar *uname;
CFStringRef s;
char *label;
size_t label_len;
unsigned uname_len, i;
int err;
CFIndex label_size;
ntfs_volume vol;
ntfs_attr_search_ctx ctx;
err = get_volume_mft_record(rdev, &vol, &m);
if (err != FSUR_RECOGNIZED)
goto err;
ntfs_attr_search_ctx_init(&ctx, m);
err = ntfs_attr_find_in_mft_record(AT_VOLUME_NAME, &ctx);
if (err) {
if (err != FSUR_UNRECOGNIZED)
goto free_err;
#ifdef NTFS_UTIL_DEBUG
openlog("ntfs.util", LOG_PID, LOG_DAEMON);
syslog(LOG_NOTICE, "Volume is not labelled.\n");
closelog();
#endif
err = FSUR_RECOGNIZED;
goto free_err;
}
a = ctx.a;
if (a->non_resident) {
err = FSUR_IO_FAIL;
goto free_err;
}
uname = (ntfschar*)((u8*)a + le16_to_cpu(a->value_offset));
uname_len = le32_to_cpu(a->value_length);
if ((u8*)uname + uname_len > (u8*)m + vol.mft_record_size ||
(u8*)uname + uname_len > (u8*)a +
le32_to_cpu(a->length)) {
err = FSUR_IO_FAIL;
goto free_err;
}
uname_len /= sizeof(ntfschar);
for (i = 0; i < uname_len; i++) {
ntfschar c;
c = le16_to_cpu(uname[i]);
if ((c & 0xffc0) == 0xf000) {
c &= 0x003f;
if (c <= 0x29)
uname[i] = cpu_to_le16(sfm2mac[c]);
}
}
s = CFStringCreateWithBytes(kCFAllocatorDefault, (UInt8*)uname,
uname_len * sizeof(ntfschar), kCFStringEncodingUTF16LE,
true);
if (!s) {
err = FSUR_IO_FAIL;
goto free_err;
}
label_size = CFStringGetMaximumSizeOfFileSystemRepresentation(s);
label = malloc(label_size);
if (!label) {
err = FSUR_IO_FAIL;
goto s_free_err;
}
if (!CFStringGetFileSystemRepresentation(s, label, label_size)) {
err = FSUR_IO_FAIL;
goto label_free_err;
}
label_len = strlen(label);
if (label_len > (size_t)label_size) {
err = FSUR_IO_FAIL;
goto label_free_err;
}
#ifdef NTFS_UTIL_DEBUG
openlog("ntfs.util", LOG_PID, LOG_DAEMON);
syslog(LOG_NOTICE, "Volume label: %s\n", label);
closelog();
#endif
(void)write(STDOUT_FILENO, label, label_len);
err = FSUR_RECOGNIZED;
label_free_err:
free(label);
s_free_err:
CFRelease(s);
free_err:
free(m);
err:
return err;
}
static int do_exec(const char *progname, char *const args[])
{
pid_t pid;
union wait status;
int err;
pid = fork();
if (pid == -1) {
fprintf(stderr, "%s: fork failed: %s\n", progname,
strerror(errno));
return FSUR_INVAL;
}
if (!pid) {
(void)execv(args[0], args);
err = errno;
fprintf(stderr, "%s: execv %s failed: %s\n", progname, args[0],
strerror(err));
exit(err);
}
if (wait4(pid, (int*)&status, 0, NULL) != pid) {
fprintf(stderr, "%s: BUG executing %s command.\n", progname,
args[0]);
return FSUR_INVAL;
}
if (!WIFEXITED(status)) {
fprintf(stderr, "%s: %s command aborted by signal %d.\n",
progname, args[0], WTERMSIG(status));
return FSUR_INVAL;
}
err = WEXITSTATUS(status);
if (err) {
fprintf(stderr, "%s: %s command failed: %s\n", progname,
args[0], strerror(err));
return FSUR_IO_FAIL;
}
return FSUR_IO_SUCCESS;
}
static int do_mount(const char *progname, char *dev, char *mp,
const BOOL removable __attribute__((unused)),
const BOOL readonly, const BOOL nosuid, const BOOL nodev)
{
char *const kextargs[] = { "/sbin/kextload",
"/System/Library/Extensions/ntfs.kext", NULL };
char *mountargs[] = { "/sbin/mount", "-w", "-o",
"suid", "-o", "dev", "-t", "ntfs", dev, mp, NULL };
struct vfsconf vfc;
if (!mp || !strlen(mp))
return FSUR_INVAL;
if (readonly)
mountargs[1] = "-r";
if (nosuid)
mountargs[3] = "nosuid";
if (nodev)
mountargs[5] = "nodev";
if (getvfsbyname("ntfs", &vfc))
(void)do_exec(progname, kextargs);
return do_exec(progname, mountargs);
}
static int do_unmount(const char *progname, char *mp)
{
char *const umountargs[] = { "/sbin/umount", mp, NULL };
if (!mp || !strlen(mp))
return FSUR_INVAL;
return do_exec(progname, umountargs);
}
int main(int argc, char **argv)
{
char *progname, *dev, *mp = NULL;
int err;
char opt;
BOOL removable, readonly, nosuid, nodev;
char rawdev[MAXPATHLEN];
char blockdev[MAXPATHLEN];
struct stat sb;
nodev = nosuid = readonly = removable = FALSE;
progname = argv[0];
argc--;
argv++;
#ifdef NTFS_UTIL_DEBUG
openlog("ntfs.util", LOG_PID, LOG_DAEMON);
if (argc == 0)
syslog(LOG_NOTICE, "Called without arguments!");
else if (argc == 1)
syslog(LOG_NOTICE, "Called with %d arguments: %s", argc,
argv[0]);
else if (argc == 2)
syslog(LOG_NOTICE, "Called with %d arguments: %s %s", argc,
argv[0], argv[1]);
else if (argc == 3)
syslog(LOG_NOTICE, "Called with %d arguments: %s %s %s", argc,
argv[0], argv[1], argv[2]);
else if (argc == 4)
syslog(LOG_NOTICE, "Called with %d arguments: %s %s %s %s",
argc, argv[0], argv[1], argv[2], argv[3]);
else if (argc == 5)
syslog(LOG_NOTICE, "Called with %d arguments: %s %s %s %s %s",
argc, argv[0], argv[1], argv[2], argv[3],
argv[4]);
else if (argc == 6)
syslog(LOG_NOTICE, "Called with %d arguments: %s %s %s %s %s "
"%s", argc, argv[0], argv[1], argv[2], argv[3],
argv[4], argv[5]);
else
syslog(LOG_NOTICE, "Called with %d arguments: %s %s %s %s %s "
"%s %s", argc, argv[0], argv[1], argv[2],
argv[3], argv[4], argv[5], argv[6]);
closelog();
#endif
if (argc < 2 || argv[0][0] != '-')
usage(progname);
opt = argv[0][1];
dev = argv[1];
argc -= 2;
argv += 2;
switch (opt) {
case FSUC_GETUUID:
if (argc)
usage(progname);
break;
case FSUC_PROBE:
if (argc != 2)
usage(progname);
break;
case FSUC_MOUNT:
if (argc != 5)
usage(progname);
break;
case FSUC_UNMOUNT:
if (argc != 1)
usage(progname);
break;
default:
usage(progname);
break;
}
err = snprintf(rawdev, sizeof(rawdev), "/dev/r%s", dev);
if (err >= (int)sizeof(rawdev)) {
fprintf(stderr, "%s: Specified device name is too long.\n",
progname);
exit(FSUR_INVAL);
}
if (stat(rawdev, &sb)) {
fprintf(stderr, "%s: stat %s failed, %s\n", progname, rawdev,
strerror(errno));
exit(FSUR_INVAL);
}
err = snprintf(blockdev, sizeof(blockdev), "/dev/%s", dev);
if (err >= (int)sizeof(blockdev)) {
fprintf(stderr, "%s: Specified device name is too long.\n",
progname);
exit(FSUR_INVAL);
}
if (stat(blockdev, &sb)) {
fprintf(stderr, "%s: stat %s failed, %s\n", progname, blockdev,
strerror(errno));
exit(FSUR_INVAL);
}
if (opt == FSUC_MOUNT || opt == FSUC_UNMOUNT) {
mp = argv[0];
argc--;
argv++;
}
if (opt == FSUC_PROBE || opt == FSUC_MOUNT) {
if (!strcmp(argv[0], DEVICE_REMOVABLE))
removable = TRUE;
else if (strcmp(argv[0], DEVICE_FIXED))
usage(progname);
if (!strcmp(argv[1], DEVICE_READONLY))
readonly = TRUE;
else if (strcmp(argv[1], DEVICE_WRITABLE))
usage(progname);
if (opt == FSUC_MOUNT) {
if (!strcmp(argv[2], "nosuid"))
nosuid = TRUE;
else if (strcmp(argv[2], "suid"))
usage(progname);
if (!strcmp(argv[3], "nodev"))
nodev = TRUE;
else if (strcmp(argv[3], "dev"))
usage(progname);
}
}
switch (opt) {
case FSUC_GETUUID:
err = do_getuuid(rawdev);
break;
case FSUC_PROBE:
err = do_probe(rawdev, removable, readonly);
break;
case FSUC_MOUNT:
err = do_mount(progname, blockdev, mp, removable, readonly,
nosuid, nodev);
break;
case FSUC_UNMOUNT:
err = do_unmount(progname, mp);
break;
default:
usage(progname);
break;
}
return err;
}