1/*
2 * Copyright (c) 2006-2008 Apple Inc. All rights reserved.
3 *
4 * This file contains Original Code and/or Modifications of Original Code as
5 * defined in and that are subject to the Apple Public Source License Version
6 * 2.0 (the 'License'). You may not use this file except in compliance with the
7 * License.
8 *
9 * Please obtain a copy of the License at http://www.opensource.apple.com/apsl/
10 * and read it before using this file.
11 *
12 * The Original Code and all software distributed under the License are
13 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR
16 * A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations under the
18 * License.
19 */
20
21#include <sys/loadable_fs.h>
22#ifndef FSUC_GETUUID
23#define FSUC_GETUUID 'k'
24#endif
25
26#include <sys/disk.h>
27#include <sys/ioctl.h>
28#include <sys/mount.h>
29#include <sys/param.h>
30#include <sys/stat.h>
31#include <sys/types.h>
32#include <sys/wait.h>
33
34#include <errno.h>
35#include <fcntl.h>
36#include <stdlib.h>
37#include <stdio.h>
38#include <string.h>
39#include <strings.h>
40#include <unistd.h>
41
42#include <CoreFoundation/CFString.h>
43
44/* Define this if you want debug output to go to syslog. */
45//#define NTFS_UTIL_DEBUG
46
47#ifdef NTFS_UTIL_DEBUG
48#include <syslog.h>
49#endif /* NTFS_UTIL_DEBUG */
50
51#include "ntfs.h"
52#include "ntfs_types.h"
53#include "ntfs_endian.h"
54#include "ntfs_layout.h"
55
56/*
57 * The NTFS in-memory mount point structure.
58 *
59 * Adapted from ../kext/ntfs_volume.h.
60 */
61typedef struct {
62	/* NTFS bootsector provided information. */
63	u32 sector_size;		/* in bytes */
64	u32 cluster_size;		/* in bytes */
65	u32 mft_record_size;		/* in bytes */
66	u32 index_block_size;		/* in bytes */
67	LCN mft_lcn;			/* Cluster location of mft data. */
68	LCN mftmirr_lcn;		/* Cluster location of copy of mft. */
69	int mftmirr_size;		/* Size of mft mirror in mft records. */
70	LCN nr_clusters;		/* Volume size in clusters == number of
71					   bits in lcn bitmap. */
72} ntfs_volume;
73
74typedef struct {
75	MFT_RECORD *m;
76	ATTR_RECORD *a;
77} ntfs_attr_search_ctx;
78
79typedef struct { /* In memory vcn to lcn mapping structure element. */
80	VCN vcn;	/* vcn = Starting virtual cluster number. */
81	LCN lcn;	/* lcn = Starting logical cluster number. */
82	s64 length;	/* Run length in clusters. */
83} ntfs_rl_element;
84
85/* Runlist allocations happen in multiples of this value in bytes. */
86#define NTFS_RL_ALLOC_BLOCK 1024
87
88typedef enum {
89	LCN_HOLE		= -1,	/* Keep this as highest value or die! */
90	LCN_RL_NOT_MAPPED	= -2,
91	LCN_ENOENT		= -3,
92	LCN_ENOMEM		= -4,
93	LCN_EIO			= -5,
94} LCN_SPECIAL_VALUES;
95
96static void usage(const char *progname) __attribute__((noreturn));
97static void usage(const char *progname)
98{
99	fprintf(stderr, "usage: %s action_arg device_arg [mount_point_arg] [Flags]\n", progname);
100	fprintf(stderr, "action_arg:\n");
101	fprintf(stderr, "       -%c (Get UUID Key)\n", FSUC_GETUUID);
102	fprintf(stderr, "       -%c (Mount)\n", FSUC_MOUNT);
103	fprintf(stderr, "       -%c (Probe)\n", FSUC_PROBE);
104	fprintf(stderr, "       -%c (Unmount)\n", FSUC_UNMOUNT);
105	fprintf(stderr, "device_arg:\n");
106	fprintf(stderr, "       device we are acting upon (for example, 'disk0s2')\n");
107	fprintf(stderr, "mount_point_arg:\n");
108	fprintf(stderr, "       required for Mount and Unmount\n");
109	fprintf(stderr, "Flags:\n");
110	fprintf(stderr, "       required for Mount and Probe\n");
111	fprintf(stderr, "       indicates removable or fixed (for example 'fixed')\n");
112	fprintf(stderr, "       indicates readonly or writable (for example 'readonly')\n");
113	fprintf(stderr, "Flags (Mount only):\n");
114	fprintf(stderr, "       indicates suid or nosuid (for example 'nosuid')\n");
115	fprintf(stderr, "       indicates dev or nodev (for example 'nodev')\n");
116	fprintf(stderr, "Examples:\n");
117	fprintf(stderr, "       %s -p disk0s2 fixed writable\n", progname);
118	fprintf(stderr, "       %s -m disk0s2 /my/hfs removable readonly nosuid nodev\n", progname);
119	exit(FSUR_INVAL);
120}
121
122/**
123 * ntfs_boot_sector_is_valid - check if @b contains a valid ntfs boot sector
124 * @b:		Boot sector of device @mp to check.
125 *
126 * Check whether the boot sector @b is a valid ntfs boot sector.
127 *
128 * Return TRUE if it is valid and FALSE if not.
129 *
130 * Copied from ../kext/ntfs_vfsops.c.
131 */
132static BOOL ntfs_boot_sector_is_valid(const NTFS_BOOT_SECTOR *b)
133{
134	/* Check OEMidentifier is "NTFS    " */
135	if (b->oem_id != magicNTFS)
136		goto not_ntfs;
137	/*
138	 * Check bytes per sector value is between 256 and
139	 * NTFS_MAX_SECTOR_SIZE.
140	 */
141	if (le16_to_cpu(b->bpb.bytes_per_sector) < 0x100 ||
142			le16_to_cpu(b->bpb.bytes_per_sector) >
143			NTFS_MAX_SECTOR_SIZE)
144		goto not_ntfs;
145	/* Check sectors per cluster value is valid. */
146	switch (b->bpb.sectors_per_cluster) {
147	case 1: case 2: case 4: case 8: case 16: case 32: case 64: case 128:
148		break;
149	default:
150		goto not_ntfs;
151	}
152	/* Check the cluster size is not above the maximum (64kiB). */
153	if ((u32)le16_to_cpu(b->bpb.bytes_per_sector) *
154			b->bpb.sectors_per_cluster > NTFS_MAX_CLUSTER_SIZE)
155		goto not_ntfs;
156	/* Check reserved/unused fields are really zero. */
157	if (le16_to_cpu(b->bpb.reserved_sectors) ||
158			le16_to_cpu(b->bpb.root_entries) ||
159			le16_to_cpu(b->bpb.sectors) ||
160			le16_to_cpu(b->bpb.sectors_per_fat) ||
161			le32_to_cpu(b->bpb.large_sectors) || b->bpb.fats)
162		goto not_ntfs;
163	/*
164	 * Check clusters per file mft record value is valid.  It can be either
165	 * between -31 and -9 (in which case the actual mft record size is
166	 * -log2() of the absolute value) or a positive power of two.
167	 */
168	if ((u8)b->clusters_per_mft_record < 0xe1 ||
169			(u8)b->clusters_per_mft_record > 0xf7)
170		switch (b->clusters_per_mft_record) {
171		case 1: case 2: case 4: case 8: case 16: case 32: case 64:
172			break;
173		default:
174			goto not_ntfs;
175		}
176	/* Check clusters per index block value is valid. */
177	if ((u8)b->clusters_per_index_block < 0xe1 ||
178			(u8)b->clusters_per_index_block > 0xf7)
179		switch (b->clusters_per_index_block) {
180		case 1: case 2: case 4: case 8: case 16: case 32: case 64:
181			break;
182		default:
183			goto not_ntfs;
184		}
185	/* All checks passed, this boot sector is an NTFS boot sector. */
186	return TRUE;
187not_ntfs:
188	return FALSE;
189}
190
191/**
192 * ntfs_boot_sector_parse - parse the boot sector and store the data in @vol
193 * @vol:	volume structure to initialise with data from boot sector
194 * @b:		boot sector to parse
195 *
196 * Parse the ntfs boot sector @b and store all imporant information therein in
197 * the ntfs_volume @vol.
198 *
199 * Return 0 on success and error code on error.  The following error codes are
200 * defined:
201 *	FSUR_UNRECOGNIZED - Boot sector is invalid/volume is not supported.
202 *
203 * Adapted from ../kext/ntfs_vfsops.c.
204 */
205static int ntfs_boot_sector_parse(ntfs_volume *vol, const NTFS_BOOT_SECTOR *b)
206{
207	s64 ll;
208	int clusters_per_mft_record;
209
210	vol->sector_size = le16_to_cpu(b->bpb.bytes_per_sector);
211	vol->cluster_size = vol->sector_size * b->bpb.sectors_per_cluster;
212	if (vol->cluster_size < vol->sector_size)
213		return FSUR_UNRECOGNIZED;
214	clusters_per_mft_record = b->clusters_per_mft_record;
215	if (clusters_per_mft_record > 0)
216		vol->mft_record_size = vol->cluster_size *
217				clusters_per_mft_record;
218	else
219		/*
220		 * When mft_record_size < cluster_size, clusters_per_mft_record
221		 * = -log2(mft_record_size) bytes.  mft_record_size normaly is
222		 * 1024 bytes, which is encoded as 0xF6 (-10 in decimal).
223		 */
224		vol->mft_record_size = 1 << -clusters_per_mft_record;
225	/*
226	 * Get the size of the volume in clusters and check for 64-bit-ness.
227	 * Windows currently only uses 32 bits to save the clusters so we do
228	 * the same as we do not really want to break compatibility.  We could
229	 * perhaps add a mount option to allow this one day but it would render
230	 * such volumes incompatible with Windows.
231	 */
232	ll = sle64_to_cpu(b->number_of_sectors) / b->bpb.sectors_per_cluster;
233	if ((u64)ll >= (u64)1 << 32)
234		return FSUR_UNRECOGNIZED;
235	vol->nr_clusters = ll;
236	ll = sle64_to_cpu(b->mft_lcn);
237	if (ll >= vol->nr_clusters)
238		return FSUR_UNRECOGNIZED;
239	vol->mft_lcn = ll;
240	ll = sle64_to_cpu(b->mftmirr_lcn);
241	if (ll >= vol->nr_clusters)
242		return FSUR_UNRECOGNIZED;
243	vol->mftmirr_lcn = ll;
244	return 0;
245}
246
247/**
248 * ntfs_mst_fixup_post_read - deprotect multi sector transfer protected data
249 * @b:		pointer to the data to deprotect
250 * @size:	size in bytes of @b
251 *
252 * Perform the necessary post read multi sector transfer fixup and detect the
253 * presence of incomplete multi sector transfers.
254 *
255 * In the case of an incomplete transfer being detected, overwrite the magic of
256 * the ntfs record header being processed with "BAAD" and abort processing.
257 *
258 * Return 0 on success and FSUR_IO_FAIL on error ("BAAD" magic will be
259 * present).
260 *
261 * NOTE: We consider the absence / invalidity of an update sequence array to
262 * mean that the structure is not protected at all and hence does not need to
263 * be fixed up.  Thus, we return success and not failure in this case.  This is
264 * in contrast to ntfs_mst_fixup_pre_write(), see below.
265 *
266 * Adapted from ../kext/ntfs_mst.c.
267 */
268static int ntfs_mst_fixup_post_read(NTFS_RECORD *b, const u32 size)
269{
270	u16 *usa_pos, *data_pos;
271	u16 usa_ofs, usa_count, usn;
272
273	/* Setup the variables. */
274	usa_ofs = le16_to_cpu(b->usa_ofs);
275	/* Decrement usa_count to get number of fixups. */
276	usa_count = le16_to_cpu(b->usa_count) - 1;
277	/* Size and alignment checks. */
278	if (size & (NTFS_BLOCK_SIZE - 1) || usa_ofs & 1 ||
279			(u32)usa_ofs + ((u32)usa_count * 2) > size ||
280			(size >> NTFS_BLOCK_SIZE_SHIFT) != usa_count)
281		return 0;
282	/* Position of usn in update sequence array. */
283	usa_pos = (u16*)b + usa_ofs/sizeof(u16);
284	/*
285	 * The update sequence number which has to be equal to each of the u16
286	 * values before they are fixed up.  Note no need to care for
287	 * endianness since we are comparing and moving data for on disk
288	 * structures which means the data is consistent.  If it is consistenty
289	 * the wrong endianness it does not make any difference.
290	 */
291	usn = *usa_pos;
292	/* Position in protected data of first u16 that needs fixing up. */
293	data_pos = (u16*)b + NTFS_BLOCK_SIZE/sizeof(u16) - 1;
294	/* Check for incomplete multi sector transfer(s). */
295	while (usa_count--) {
296		if (*data_pos != usn) {
297			/*
298			 * Incomplete multi sector transfer detected!  )-:
299			 * Set the magic to "BAAD" and return failure.
300			 * Note that magic_BAAD is already little endian.
301			 */
302			b->magic = magic_BAAD;
303			return FSUR_IO_FAIL;
304		}
305		data_pos += NTFS_BLOCK_SIZE/sizeof(u16);
306	}
307	/* Re-setup the variables. */
308	usa_count = le16_to_cpu(b->usa_count) - 1;
309	data_pos = (u16*)b + NTFS_BLOCK_SIZE/sizeof(u16) - 1;
310	/* Fixup all sectors. */
311	while (usa_count--) {
312		/*
313		 * Increment position in usa and restore original data from
314		 * the usa into the data buffer.
315		 */
316		*data_pos = *(++usa_pos);
317		/* Increment position in data as well. */
318		data_pos += NTFS_BLOCK_SIZE/sizeof(u16);
319	}
320	return 0;
321}
322
323/**
324 * ntfs_attr_search_ctx_init - initialize an attribute search context
325 * @ctx:	attribute search context to initialize
326 * @m:		mft record with which to initialize the search context
327 *
328 * Initialize the attribute search context @ctx with @m.
329 *
330 * Adapted from ../kext/ntfs_attr.c.
331 */
332static void ntfs_attr_search_ctx_init(ntfs_attr_search_ctx *ctx, MFT_RECORD *m)
333{
334	*ctx = (ntfs_attr_search_ctx) {
335		.m = m,
336		.a = (ATTR_RECORD*)((u8*)m + le16_to_cpu(m->attrs_offset)),
337	};
338}
339
340/**
341 * ntfs_attr_find_in_mft_record - find attribute in mft record
342 * @type:	attribute type to find
343 * @ctx:	search context with mft record and attribute to search from
344 *
345 * ntfs_attr_find_in_mft_record() takes a search context @ctx as parameter and
346 * searches the mft record specified by @ctx->m, beginning at @ctx->a, for an
347 * attribute of @type, the attribute must be unnamed.
348 *
349 * If the attribute is found, ntfs_attr_find_in_mft_record() returns 0 and
350 * @ctx->a is set to point to the found attribute.
351 *
352 * If the attribute is not found, FSUR_UNRECOGNIZED is returned and @ctx->a is
353 * set to point to the attribute before which the attribute being searched for
354 * would need to be inserted if such an action were to be desired.
355 *
356 * On actual error, ntfs_attr_find_in_mft_record() returns FSUR_IO_FAIL.  In
357 * this case @ctx->a is undefined and in particular do not rely on it not
358 * having changed.
359 *
360 * Adapted from ../kext/ntfs_attr.c.
361 */
362static int ntfs_attr_find_in_mft_record(const ATTR_TYPE type,
363		ntfs_attr_search_ctx *ctx)
364{
365	ATTR_RECORD *a;
366
367	a = ctx->a;
368	for (;;	a = (ATTR_RECORD*)((u8*)a + le32_to_cpu(a->length))) {
369		if ((u8*)a < (u8*)ctx->m || (u8*)a > (u8*)ctx->m +
370				le32_to_cpu(ctx->m->bytes_allocated))
371			break;
372		ctx->a = a;
373		if (le32_to_cpu(a->type) > le32_to_cpu(type) ||
374				a->type == AT_END)
375			return FSUR_UNRECOGNIZED;
376		if (!a->length)
377			break;
378		if (a->type != type)
379			continue;
380		/* The search failed if the found attribute is named. */
381		if (a->name_length)
382			return FSUR_UNRECOGNIZED;
383		return 0;
384	}
385	return FSUR_IO_FAIL;
386}
387
388/**
389 * ntfs_mapping_pairs_decompress - convert mapping pairs array to runlist
390 * @vol:	ntfs volume on which the attribute resides
391 * @a:		attribute record whose mapping pairs array to decompress
392 * @runlist:	runlist in which to insert @a's runlist
393 *
394 * It is up to the caller to serialize access to the runlist @runlist.
395 *
396 * Decompress the attribute @a's mapping pairs array into a runlist.  On
397 * success, return the decompressed runlist in @runlist.
398 *
399 * If @runlist already contains a runlist, the decompressed runlist is inserted
400 * into the appropriate place in @runlist and the resultant, combined runlist
401 * is returned in @runlist.
402 *
403 * On error, return error code (FSUR_UNRECOGNIZED, FSUR_IO_FAIL, or FSUR_INVAL).
404 *
405 * Adapted from ../kext/ntfs_runlist.c.
406 */
407static int ntfs_mapping_pairs_decompress(ntfs_volume *vol,
408		const ATTR_RECORD *a, ntfs_rl_element **runlist)
409{
410	VCN vcn;		/* Current vcn. */
411	LCN lcn;		/* Current lcn. */
412	s64 deltaxcn;		/* Change in [vl]cn. */
413	u8 *buf;		/* Current position in mapping pairs array. */
414	u8 *a_end;		/* End of attribute. */
415	ntfs_rl_element *rl;
416	unsigned rlsize;	/* Size of runlist buffer. */
417	int err;
418	u16 rlpos;		/* Current runlist position in units of
419				   runlist_elements. */
420	u8 b;			/* Current byte offset in buf. */
421
422	/* Make sure @a exists and is non-resident. */
423	if (!a || !a->non_resident || sle64_to_cpu(a->lowest_vcn) < (VCN)0)
424		return FSUR_INVAL;
425	/* Start at vcn = lowest_vcn and lcn 0. */
426	vcn = sle64_to_cpu(a->lowest_vcn);
427	lcn = 0;
428	/* Get start of the mapping pairs array. */
429	buf = (u8*)a + le16_to_cpu(a->mapping_pairs_offset);
430	a_end = (u8*)a + le32_to_cpu(a->length);
431	if (buf < (u8*)a || buf > a_end)
432		return FSUR_IO_FAIL;
433	/* If the mapping pairs array is valid but empty, nothing to do. */
434	if (!vcn && !*buf)
435		return FSUR_UNRECOGNIZED;
436	/* Current position in runlist array. */
437	rlpos = 0;
438	rlsize = NTFS_RL_ALLOC_BLOCK;
439	/* Allocate NTFS_RL_ALLOC_BLOCK bytes for the runlist. */
440	rl = malloc(rlsize);
441	if (!rl)
442		return FSUR_INVAL;
443	/* Insert unmapped starting element if necessary. */
444	if (vcn) {
445		rl->vcn = 0;
446		rl->lcn = LCN_RL_NOT_MAPPED;
447		rl->length = vcn;
448		rlpos++;
449	}
450	while (buf < a_end && *buf) {
451		/*
452		 * Allocate more memory if needed, including space for the
453		 * not-mapped and terminator elements.
454		 */
455		if (((rlpos + 3) * sizeof(*rl)) > rlsize) {
456			ntfs_rl_element *rl2;
457
458			rl2 = malloc(rlsize + NTFS_RL_ALLOC_BLOCK);
459			if (!rl2) {
460				err = FSUR_INVAL;
461				goto err;
462			}
463			memcpy(rl2, rl, rlsize);
464			free(rl);
465			rl = rl2;
466			rlsize += NTFS_RL_ALLOC_BLOCK;
467		}
468		/* Enter the current vcn into the current runlist element. */
469		rl[rlpos].vcn = vcn;
470		/*
471		 * Get the change in vcn, i.e. the run length in clusters.
472		 * Doing it this way ensures that we sign-extend negative
473		 * values.  A negative run length does not make any sense, but
474		 * hey, I did not design NTFS...
475		 */
476		b = *buf & 0xf;
477		if (b) {
478			if (buf + b >= a_end) {
479				err = FSUR_IO_FAIL;
480				goto err;
481			}
482			for (deltaxcn = (s8)buf[b--]; b; b--)
483				deltaxcn = (deltaxcn << 8) + buf[b];
484		} else /* The length entry is compulsory. */
485			deltaxcn = (s64)-1;
486		/*
487		 * Assume a negative length to indicate data corruption and
488		 * hence clean-up and return NULL.
489		 */
490		if (deltaxcn < 0) {
491			err = FSUR_IO_FAIL;
492			goto err;
493		}
494		/*
495		 * Enter the current run length into the current runlist
496		 * element.
497		 */
498		rl[rlpos].length = deltaxcn;
499		/* Increment the current vcn by the current run length. */
500		vcn += deltaxcn;
501		/*
502		 * There might be no lcn change at all, as is the case for
503		 * sparse clusters on NTFS 3.0+, in which case we set the lcn
504		 * to LCN_HOLE.
505		 */
506		if (!(*buf & 0xf0))
507			rl[rlpos].lcn = LCN_HOLE;
508		else {
509			/* Get the lcn change which really can be negative. */
510			u8 b2 = *buf & 0xf;
511			b = b2 + ((*buf >> 4) & 0xf);
512			if (buf + b >= a_end) {
513				err = FSUR_IO_FAIL;
514				goto err;
515			}
516			for (deltaxcn = (s8)buf[b--]; b > b2; b--)
517				deltaxcn = (deltaxcn << 8) + buf[b];
518			/* Change the current lcn to its new value. */
519			lcn += deltaxcn;
520			/* Check lcn is not below -1. */
521			if (lcn < (LCN)-1) {
522				err = FSUR_IO_FAIL;
523				goto err;
524			}
525			/* Enter the current lcn into the runlist element. */
526			rl[rlpos].lcn = lcn;
527		}
528		/* Get to the next runlist element. */
529		rlpos++;
530		/* Increment the buffer position to the next mapping pair. */
531		buf += (*buf & 0xf) + ((*buf >> 4) & 0xf) + 1;
532	}
533	if (buf >= a_end) {
534		err = FSUR_IO_FAIL;
535		goto err;
536	}
537	/*
538	 * If there is a highest_vcn specified, it must be equal to the final
539	 * vcn in the runlist - 1, or something has gone badly wrong.
540	 */
541	deltaxcn = sle64_to_cpu(a->highest_vcn);
542	if (deltaxcn && vcn - 1 != deltaxcn) {
543		err = FSUR_IO_FAIL;
544		goto err;
545	}
546	/* Setup not mapped runlist element if this is the base extent. */
547	if (!a->lowest_vcn) {
548		VCN max_cluster;
549
550		max_cluster = ((sle64_to_cpu(a->allocated_size) +
551				vol->cluster_size - 1) / vol->cluster_size) - 1;
552		/*
553		 * A highest_vcn of zero means this is a single extent
554		 * attribute so simply terminate the runlist with LCN_ENOENT).
555		 */
556		if (deltaxcn) {
557			/*
558			 * If there is a difference between the highest_vcn and
559			 * the highest cluster, the runlist is either corrupt
560			 * or, more likely, there are more extents following
561			 * this one.
562			 */
563			if (deltaxcn < max_cluster) {
564				rl[rlpos].vcn = vcn;
565				vcn += rl[rlpos].length = max_cluster -
566						deltaxcn;
567				rl[rlpos].lcn = LCN_RL_NOT_MAPPED;
568				rlpos++;
569			} else if (deltaxcn > max_cluster) {
570				err = FSUR_IO_FAIL;
571				goto err;
572			}
573		}
574		rl[rlpos].lcn = LCN_ENOENT;
575	} else /* Not the base extent.  There may be more extents to follow. */
576		rl[rlpos].lcn = LCN_RL_NOT_MAPPED;
577	/* Setup terminating runlist element. */
578	rl[rlpos].vcn = vcn;
579	rl[rlpos].length = (s64)0;
580	*runlist = rl;
581	return 0;
582err:
583	free(rl);
584	return err;
585}
586
587/**
588 * ntfs_rl_vcn_to_lcn - convert a vcn into a lcn given a runlist
589 * @rl:		runlist to use for conversion
590 * @vcn:	vcn to convert
591 * @clusters:	optional return pointer for the number of contiguous clusters
592 *
593 * Convert the virtual cluster number @vcn of an attribute into a logical
594 * cluster number (lcn) of a device using the runlist @rl to map vcns to their
595 * corresponding lcns.
596 *
597 * If @clusters is not NULL, on success (i.e. we return >= LCN_HOLE) we return
598 * the number of contiguous clusters after the returned lcn in *@clusters.
599 *
600 * Since lcns must be >= 0, we use negative return codes with special meaning:
601 *
602 * Return code		Meaning / Description
603 * ==================================================
604 *  LCN_HOLE		Hole / not allocated on disk.
605 *  LCN_RL_NOT_MAPPED	This is part of the runlist which has not been
606 *			inserted into the runlist yet.
607 *  LCN_ENOENT		There is no such vcn in the attribute.
608 *
609 * Locking: - The caller must have locked the runlist (for reading or writing).
610 *	    - This function does not touch the lock.
611 *	    - The runlist is not modified.
612 *
613 * Adapted from ../kext/ntfs_runlist.c.
614 */
615static LCN ntfs_rl_vcn_to_lcn(const ntfs_rl_element *rl, const VCN vcn,
616		s64 *clusters)
617{
618	unsigned i;
619
620	/*
621	 * If @rl is NULL, assume that we have found an unmapped runlist.  The
622	 * caller can then attempt to map it and fail appropriately if
623	 * necessary.
624	 */
625	if (!rl)
626		return LCN_RL_NOT_MAPPED;
627	/* Catch out of lower bounds vcn. */
628	if (vcn < rl[0].vcn)
629		return LCN_ENOENT;
630	for (i = 0; rl[i].length; i++) {
631		if (vcn < rl[i + 1].vcn) {
632			const s64 ofs = vcn - rl[i].vcn;
633			if (clusters)
634				*clusters = rl[i].length - ofs;
635			if (rl[i].lcn >= (LCN)0)
636				return rl[i].lcn + ofs;
637			return rl[i].lcn;
638		}
639	}
640	/*
641	 * Set *@clusters just in case rl[i].lcn is LCN_HOLE.  That should
642	 * never happen since the terminator element should never be of type
643	 * LCN_HOLE but better be safe than sorry.
644	 */
645	if (clusters)
646		*clusters = 0;
647	/*
648	 * The terminator element is setup to the correct value, i.e. one of
649	 * LCN_HOLE, LCN_RL_NOT_MAPPED, or LCN_ENOENT.
650	 */
651	if (rl[i].lcn < (LCN)0)
652		return rl[i].lcn;
653	/* Just in case...  We could replace this with panic() some day. */
654	return LCN_ENOENT;
655}
656
657/**
658 * ntfs_pread - Read from a raw device file descriptor.
659 * @f:			file descriptor to read from
660 * @buf:		temporary buffer of size sector_size bytes
661 * @sector_size:	sector size in bytes
662 * @dst:		destination buffer to read into
663 * @to_read:		how many bytes to read into the destination buffer
664 * @ofs:		offset into the raw device at which to read
665 *
666 * We are working with the raw device thus we need to do sector aligned i/o.
667 */
668static ssize_t ntfs_pread(int f, u8 *buf, long sector_size, void *dst,
669		ssize_t to_read, off_t ofs)
670{
671	off_t io_pos;
672	ssize_t buf_ofs, to_copy, copied;
673
674	copied = 0;
675	while (to_read > 0) {
676		io_pos = ofs & ~((off_t)sector_size - 1);
677		buf_ofs = ofs & ((off_t)sector_size - 1);
678		to_copy = sector_size - buf_ofs;
679		if (to_copy > to_read)
680			to_copy = to_read;
681		/*
682		 * Accept partial reads as long as they contain the data we
683		 * want.
684		 */
685		if (pread(f, buf, sector_size, io_pos) < buf_ofs + to_copy) {
686			if (!copied)
687				copied = -1;
688			break;
689		}
690		memcpy((u8*)dst + copied, buf + buf_ofs, to_copy);
691		to_read -= to_copy;
692		copied += to_copy;
693	}
694	return copied;
695}
696
697/**
698 * get_volume_mft_record - Get the mft record for $Volume system file.
699 * @rdev:	raw device containing NTFS volume
700 * @vol:	ntfs_volume structure to set up describing the NTFS volume
701 * @mrec:	pointer in which to return the mft record of $Volume
702 *
703 * Check whether the volume is an NTFS volume and if so load and return the
704 * initialized ntfs_volume structure in @vol as well as the mft record for the
705 * system file $Volume in @mrec and return FSUR_RECOGNIZED.  The caller is
706 * responsible for freeing the returned mft record @mrec using free().
707 *
708 * If not an NTFS volume return FSUR_UNRECOGNIZED.
709 *
710 * On error return FSUR_INVAL or FSUR_IO_FAIL.
711 */
712static int get_volume_mft_record(char *rdev, ntfs_volume *vol,
713		MFT_RECORD **mrec)
714{
715	VCN vcn;
716	LCN lcn;
717	s64 clusters, io_size;
718	void *buf;
719	NTFS_BOOT_SECTOR *bs;
720	MFT_RECORD *m;
721	ntfs_rl_element *rl;
722	long sector_size;
723	unsigned vcn_ofs;
724	int f, err, to_read;
725	u32 dev_block_size;
726	ntfs_attr_search_ctx ctx;
727
728	/*
729	 * Read the boot sector.  We are working with the raw device thus we
730	 * need to do sector aligned i/o.
731	 *
732	 * The maximum supported sector size for the NTFS driver is the system
733	 * page size thus query the system for the page size and use that for
734	 * the sector size.  If the querying fails then use 32768 which is the
735	 * maximum value that can be set in the NTFS boot sector (it is stored
736	 * in a 16-bit unsigned variable).
737	 *
738	 * The minumum sector size is the ntfs block size (512 bytes).
739	 */
740	sector_size = sysconf(_SC_PAGE_SIZE);
741	if (sector_size < 0)
742		sector_size = 32768;
743	if (sector_size < NTFS_BLOCK_SIZE)
744		sector_size = NTFS_BLOCK_SIZE;
745	f = open(rdev, O_RDONLY);
746	if (f == -1) {
747		return FSUR_IO_FAIL;
748	}
749	/*
750	 * Get the native block size of the device.  If it is bigger than
751	 * @sector_size we need to do i/o in multiples of the native block
752	 * size.
753	 */
754	if (ioctl(f, DKIOCGETBLOCKSIZE, &dev_block_size) < 0) {
755		err = FSUR_IO_FAIL;
756		buf = NULL;
757		goto err;
758	}
759	if (dev_block_size > (u32)sector_size)
760		sector_size = dev_block_size;
761	buf = malloc(sector_size);
762	if (!buf) {
763		err = FSUR_IO_FAIL;
764		goto err;
765	}
766	/*
767	 * We can cope with partial reads as long as we have at least the boot
768	 * sector which fits inside the first NTFS_BLOCK_SIZE bytes.
769	 */
770	if (read(f, buf, sector_size) < NTFS_BLOCK_SIZE) {
771		err = FSUR_IO_FAIL;
772		goto err;
773	}
774	/* Check if the boot sector is a valid NTFS boot sector. */
775	bs = buf;
776	if (!ntfs_boot_sector_is_valid(bs)) {
777		err = FSUR_UNRECOGNIZED;
778		goto err;
779	}
780	/* Parse the boot sector and initialize @vol with its information. */
781	err = ntfs_boot_sector_parse(vol, bs);
782	if (err)
783		goto err;
784	m = malloc(vol->mft_record_size);
785	if (!m) {
786		err = FSUR_INVAL;
787		goto err;
788	}
789	/* Load the mft record for $MFT. */
790	if (ntfs_pread(f, buf, sector_size, m, vol->mft_record_size,
791			vol->mft_lcn * vol->cluster_size) !=
792			(ssize_t)vol->mft_record_size) {
793		err = FSUR_IO_FAIL;
794		goto free_err;
795	}
796	/* Apply the multi sector transfer protection fixups. */
797	err = ntfs_mst_fixup_post_read((NTFS_RECORD*)m, vol->mft_record_size);
798	if (err) {
799		err = FSUR_IO_FAIL;
800		goto free_err;
801	}
802	/* Lookup $DATA attribute. */
803	ntfs_attr_search_ctx_init(&ctx, m);
804	err = ntfs_attr_find_in_mft_record(AT_DATA, &ctx);
805	if (err)
806		goto free_err;
807	/* Decompress the mapping pairs array of the attribute. */
808	rl = NULL;
809	err = ntfs_mapping_pairs_decompress(vol, ctx.a, &rl);
810	if (err)
811		goto free_err;
812	vcn = FILE_Volume * vol->mft_record_size;
813	vcn_ofs = vcn & (vol->cluster_size - 1);
814	vcn /= vol->cluster_size;
815	to_read = vol->mft_record_size;
816	/*
817	 * Determine location of $Volume mft record from mft data runlist and
818	 * read it into memory.  We can safely assume that the first fragment
819	 * of $DATA must specify $Volume's location or the volume would not be
820	 * boot strappable.
821	 */
822	do {
823		lcn = ntfs_rl_vcn_to_lcn(rl, vcn, &clusters);
824		if (lcn < 0) {
825			err = FSUR_IO_FAIL;
826			goto rl_free_err;
827		}
828		io_size = (clusters * vol->cluster_size) - vcn_ofs;
829		if (io_size > to_read)
830			io_size = to_read;
831		if (ntfs_pread(f, buf, sector_size, m, io_size, (lcn *
832				vol->cluster_size) + vcn_ofs) != io_size) {
833			err = FSUR_IO_FAIL;
834			goto rl_free_err;
835		}
836		to_read -= io_size;
837		vcn += clusters;
838		vcn_ofs = 0;
839	} while (to_read > 0);
840	free(rl);
841	/* Apply the multi sector transfer protection fixups. */
842	err = ntfs_mst_fixup_post_read((NTFS_RECORD*)m, vol->mft_record_size);
843	if (err) {
844		err = FSUR_IO_FAIL;
845		goto free_err;
846	}
847	(void)close(f);
848	free(buf);
849	/* Finally got the mft record for $Volume. */
850	*mrec = m;
851	return FSUR_RECOGNIZED;
852rl_free_err:
853	free(rl);
854free_err:
855	free(m);
856err:
857	if (buf)
858		free(buf);
859	(void)close(f);
860	return err;
861}
862
863/**
864 * do_getuuid - Get the UUID key of an NTFS volume.
865 *
866 * If the volume is recognized as an NTFS volume look up its UUID key if it
867 * exists and output it to stdout then return FSUR_RECOGNIZED.
868 *
869 * If there is no UUID then return FSUR_INVAL and do not output anything to
870 * stdout.
871 *
872 * If the volume is not an NTFS volume return FSUR_UNRECOGNIZED.
873 *
874 * On error return FSUR_INVAL or FSUR_IO_FAIL.
875 */
876static int do_getuuid(char *rdev)
877{
878	MFT_RECORD *m;
879	ATTR_RECORD *a;
880	OBJECT_ID_ATTR *obj_id;
881	GUID *guid;
882	unsigned obj_id_len;
883	int err;
884	ntfs_volume vol;
885	ntfs_attr_search_ctx ctx;
886	char uuid[37];
887
888	/* Obtain the mft record for $Volume. */
889	err = get_volume_mft_record(rdev, &vol, &m);
890	if (err != FSUR_RECOGNIZED)
891		goto err;
892	/* Lookup $OBJECT_ID attribute. */
893	ntfs_attr_search_ctx_init(&ctx, m);
894	err = ntfs_attr_find_in_mft_record(AT_OBJECT_ID, &ctx);
895	if (err) {
896		if (err != FSUR_UNRECOGNIZED)
897			goto free_err;
898		/* There is no volume UUID key which is fine. */
899#ifdef NTFS_UTIL_DEBUG
900		/* Log to syslog. */
901		openlog("ntfs.util", LOG_PID, LOG_DAEMON);
902		syslog(LOG_NOTICE, "Volume does not have a UUID key.\n");
903		closelog();
904#endif /* NTFS_UTIL_DEBUG */
905		err = FSUR_INVAL;
906		goto free_err;
907	}
908	a = ctx.a;
909	if (a->non_resident) {
910		err = FSUR_IO_FAIL;
911		goto free_err;
912	}
913	obj_id = (OBJECT_ID_ATTR*)((u8*)a + le16_to_cpu(a->value_offset));
914	obj_id_len = le32_to_cpu(a->value_length);
915	if ((u8*)obj_id + obj_id_len > (u8*)m + vol.mft_record_size ||
916			(u8*)obj_id + obj_id_len > (u8*)a +
917			le32_to_cpu(a->length)) {
918		err = FSUR_IO_FAIL;
919		goto free_err;
920	}
921	guid = &obj_id->object_id;
922	/* Convert guid to utf8 uuid. */
923	if (snprintf(uuid, 37, "%08x-%04x-%04x-%02x%02x-"
924			"%02x%02x%02x%02x%02x%02x", le32_to_cpu(guid->data1),
925			le16_to_cpu(guid->data2), le16_to_cpu(guid->data3),
926			guid->data4[0], guid->data4[1], guid->data4[2],
927			guid->data4[3], guid->data4[4], guid->data4[5],
928			guid->data4[6], guid->data4[7]) != 36) {
929		err = FSUR_IO_FAIL;
930		goto free_err;
931	}
932	/* Output utf8 uuid to stdout. */
933#ifdef NTFS_UTIL_DEBUG
934	/* Log to syslog. */
935	openlog("ntfs.util", LOG_PID, LOG_DAEMON);
936	syslog(LOG_NOTICE, "Volume UUID Key: %s\n", uuid);
937	closelog();
938#endif /* NTFS_UTIL_DEBUG */
939	/*
940	 * If this fails it is not a problem and there is nothing wrong with
941	 * the volume so ignore the return value.
942	 */
943	(void)write(STDOUT_FILENO, uuid, 36);
944	/* Finally done! */
945	err = FSUR_IO_SUCCESS;
946free_err:
947	free(m);
948err:
949	return err;
950}
951
952/*
953 * Invalid NTFS filename characters are encodeded using the
954 * SFM (Services for Macintosh) private use Unicode characters.
955 *
956 * These should only be used for SMB, MSDOS or NTFS.
957 *
958 *    Illegal NTFS Char   SFM Unicode Char
959 *  ----------------------------------------
960 *    0x01-0x1f           0xf001-0xf01f
961 *    '"'                 0xf020
962 *    '*'                 0xf021
963 *    '/'                 0xf022
964 *    '<'                 0xf023
965 *    '>'                 0xf024
966 *    '?'                 0xf025
967 *    '\'                 0xf026
968 *    '|'                 0xf027
969 *    ' '                 0xf028  (Only if last char of the name)
970 *    '.'                 0xf029  (Only if last char of the name)
971 *  ----------------------------------------
972 *
973 *  Reference: http://support.microsoft.com/kb/q117258/
974 */
975
976/*
977 * In the Mac OS 9 days the colon was illegal in a file name. For that reason
978 * SFM had no conversion for the colon. There is a conversion for the
979 * slash. In Mac OS X the slash is illegal in a file name. So for us the colon
980 * is a slash and a slash is a colon. So we can just replace the slash with the
981 * colon in our tables and everything will just work.
982 *
983 * SFM conversion code adapted from xnu/bsd/vfs/vfs_utfconf.c.
984 */
985static u8 sfm2mac[0x30] = {
986	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,	/* 00 - 07 */
987	0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,	/* 08 - 0F */
988	0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,	/* 10 - 17 */
989	0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,	/* 18 - 1F */
990	0x22, 0x2a, 0x3a, 0x3c, 0x3e, 0x3f, 0x5c, 0x7c,	/* 20 - 27 */
991	0x20, 0x2e					/* 28 - 29 */
992};
993
994/**
995 * do_probe - Examine a volume to see if we recognize it as an NTFS volume.
996 *
997 * If the volume is recognized as an NTFS volume look up its volume label and
998 * output it to stdout then return FSUR_RECOGNIZED.
999 *
1000 * If the volume is not an NTFS volume return FSUR_UNRECOGNIZED.
1001 *
1002 * On error return FSUR_INVAL or FSUR_IO_FAIL.
1003 */
1004static int do_probe(char *rdev, const BOOL removable __attribute__((unused)),
1005		const BOOL writable __attribute__((unused)))
1006{
1007	MFT_RECORD *m;
1008	ATTR_RECORD *a;
1009	ntfschar *uname;
1010	CFStringRef s;
1011	char *label;
1012	size_t label_len;
1013	unsigned uname_len, i;
1014	int err;
1015	CFIndex label_size;
1016	ntfs_volume vol;
1017	ntfs_attr_search_ctx ctx;
1018
1019	/* Obtain the mft record for $Volume. */
1020	err = get_volume_mft_record(rdev, &vol, &m);
1021	if (err != FSUR_RECOGNIZED)
1022		goto err;
1023	/* Lookup $VOLUME_NAME attribute. */
1024	ntfs_attr_search_ctx_init(&ctx, m);
1025	err = ntfs_attr_find_in_mft_record(AT_VOLUME_NAME, &ctx);
1026	if (err) {
1027		if (err != FSUR_UNRECOGNIZED)
1028			goto free_err;
1029		/* There is no volume label which is fine. */
1030#ifdef NTFS_UTIL_DEBUG
1031		/* Log to syslog. */
1032		openlog("ntfs.util", LOG_PID, LOG_DAEMON);
1033		syslog(LOG_NOTICE, "Volume is not labelled.\n");
1034		closelog();
1035#endif /* NTFS_UTIL_DEBUG */
1036		err = FSUR_RECOGNIZED;
1037		goto free_err;
1038	}
1039	a = ctx.a;
1040	if (a->non_resident) {
1041		err = FSUR_IO_FAIL;
1042		goto free_err;
1043	}
1044	uname = (ntfschar*)((u8*)a + le16_to_cpu(a->value_offset));
1045	uname_len = le32_to_cpu(a->value_length);
1046	if ((u8*)uname + uname_len > (u8*)m + vol.mft_record_size ||
1047			(u8*)uname + uname_len > (u8*)a +
1048			le32_to_cpu(a->length)) {
1049		err = FSUR_IO_FAIL;
1050		goto free_err;
1051	}
1052	/* Convert length to number of Unicode characters. */
1053	uname_len /= sizeof(ntfschar);
1054	/*
1055	 * Scan through the name looking for any Services For Macintosh encoded
1056	 * characters and if any are found replace them with the decoded ones.
1057	 * We need to do this as the Core Foundation string handling routines
1058	 * are not aware of the SFM encodings.
1059	 */
1060	for (i = 0; i < uname_len; i++) {
1061		ntfschar c;
1062
1063		c = le16_to_cpu(uname[i]);
1064		if ((c & 0xffc0) == 0xf000) {
1065			c &= 0x003f;
1066			if (c <= 0x29)
1067				uname[i] = cpu_to_le16(sfm2mac[c]);
1068		}
1069	}
1070	/* Convert volume name to utf8. */
1071	s = CFStringCreateWithBytes(kCFAllocatorDefault, (UInt8*)uname,
1072			uname_len * sizeof(ntfschar), kCFStringEncodingUTF16LE,
1073			true);
1074	if (!s) {
1075		err = FSUR_IO_FAIL;
1076		goto free_err;
1077	}
1078	label_size = CFStringGetMaximumSizeOfFileSystemRepresentation(s);
1079	label = malloc(label_size);
1080	if (!label) {
1081		err = FSUR_IO_FAIL;
1082		goto s_free_err;
1083	}
1084	if (!CFStringGetFileSystemRepresentation(s, label, label_size)) {
1085		err = FSUR_IO_FAIL;
1086		goto label_free_err;
1087	}
1088	/* Output volume label (now in utf8) to stdout. */
1089	label_len = strlen(label);
1090	if (label_len > (size_t)label_size) {
1091		err = FSUR_IO_FAIL;
1092		goto label_free_err;
1093	}
1094#ifdef NTFS_UTIL_DEBUG
1095	/* Log to syslog. */
1096	openlog("ntfs.util", LOG_PID, LOG_DAEMON);
1097	syslog(LOG_NOTICE, "Volume label: %s\n", label);
1098	closelog();
1099#endif /* NTFS_UTIL_DEBUG */
1100	/*
1101	 * If this fails it is not a problem and there is nothing wrong with
1102	 * the volume so ignore the return value.
1103	 */
1104	(void)write(STDOUT_FILENO, label, label_len);
1105	/* Finally done! */
1106	err = FSUR_RECOGNIZED;
1107label_free_err:
1108	free(label);
1109s_free_err:
1110	CFRelease(s);
1111free_err:
1112	free(m);
1113err:
1114	return err;
1115}
1116
1117/**
1118 * do_exec - Execute an external command.
1119 */
1120static int do_exec(const char *progname, char *const args[])
1121{
1122	pid_t pid;
1123	union wait status;
1124	int err;
1125
1126	pid = fork();
1127	if (pid == -1) {
1128		fprintf(stderr, "%s: fork failed: %s\n", progname,
1129				strerror(errno));
1130		return FSUR_INVAL;
1131	}
1132	if (!pid) {
1133		/* In child process, execute external command. */
1134		(void)execv(args[0], args);
1135		/* We only get here if the execv() failed. */
1136		err = errno;
1137		fprintf(stderr, "%s: execv %s failed: %s\n", progname, args[0],
1138				strerror(err));
1139		exit(err);
1140	}
1141	/* In parent process, wait for exernal command to finish. */
1142	if (wait4(pid, (int*)&status, 0, NULL) != pid) {
1143		fprintf(stderr, "%s: BUG executing %s command.\n", progname,
1144				args[0]);
1145		return FSUR_INVAL;
1146	}
1147	if (!WIFEXITED(status)) {
1148		fprintf(stderr, "%s: %s command aborted by signal %d.\n",
1149				progname, args[0], WTERMSIG(status));
1150		return FSUR_INVAL;
1151	}
1152	err = WEXITSTATUS(status);
1153	if (err) {
1154		fprintf(stderr, "%s: %s command failed: %s\n", progname,
1155				args[0], strerror(err));
1156		return FSUR_IO_FAIL;
1157	}
1158	return FSUR_IO_SUCCESS;
1159}
1160
1161/**
1162 * do_mount - Mount a file system.
1163 */
1164static int do_mount(const char *progname, char *dev, char *mp,
1165		const BOOL removable __attribute__((unused)),
1166		const BOOL readonly, const BOOL nosuid, const BOOL nodev)
1167{
1168	char *const kextargs[] = { "/sbin/kextload",
1169			"/System/Library/Extensions/ntfs.kext", NULL };
1170	char *mountargs[] = { "/sbin/mount", "-w", "-o",
1171			"suid", "-o", "dev", "-t", "ntfs", dev, mp, NULL };
1172	struct vfsconf vfc;
1173
1174	if (!mp || !strlen(mp))
1175		return FSUR_INVAL;
1176	if (readonly)
1177		mountargs[1] = "-r";
1178	if (nosuid)
1179		mountargs[3] = "nosuid";
1180	if (nodev)
1181		mountargs[5] = "nodev";
1182	/*
1183	 * If the kext is not loaded, load it now.  Ignore any errors as the
1184	 * mount will fail appropriately if the kext is not loaded.
1185	 */
1186	if (getvfsbyname("ntfs", &vfc))
1187		(void)do_exec(progname, kextargs);
1188	return do_exec(progname, mountargs);
1189}
1190
1191/**
1192 * do_unmount - Unmount a volume.
1193 */
1194static int do_unmount(const char *progname, char *mp)
1195{
1196	char *const umountargs[] = { "/sbin/umount", mp, NULL };
1197
1198	if (!mp || !strlen(mp))
1199		return FSUR_INVAL;
1200	return do_exec(progname, umountargs);
1201}
1202
1203/**
1204 * main - Main function, parse arguments and cause required action to be taken.
1205 */
1206int main(int argc, char **argv)
1207{
1208	char *progname, *dev, *mp = NULL;
1209	int err;
1210	char opt;
1211	BOOL removable, readonly, nosuid, nodev;
1212	char rawdev[MAXPATHLEN];
1213	char blockdev[MAXPATHLEN];
1214	struct stat sb;
1215
1216	nodev = nosuid = readonly = removable = FALSE;
1217	/* Save & strip off program name. */
1218	progname = argv[0];
1219	argc--;
1220	argv++;
1221#ifdef NTFS_UTIL_DEBUG
1222	/* Log to syslog. */
1223	openlog("ntfs.util", LOG_PID, LOG_DAEMON);
1224	if (argc == 0)
1225		syslog(LOG_NOTICE, "Called without arguments!");
1226	else if (argc == 1)
1227		syslog(LOG_NOTICE, "Called with %d arguments: %s", argc,
1228				argv[0]);
1229	else if (argc == 2)
1230		syslog(LOG_NOTICE, "Called with %d arguments: %s %s", argc,
1231				argv[0], argv[1]);
1232	else if (argc == 3)
1233		syslog(LOG_NOTICE, "Called with %d arguments: %s %s %s", argc,
1234				argv[0], argv[1], argv[2]);
1235	else if (argc == 4)
1236		syslog(LOG_NOTICE, "Called with %d arguments: %s %s %s %s",
1237				argc, argv[0], argv[1], argv[2], argv[3]);
1238	else if (argc == 5)
1239		syslog(LOG_NOTICE, "Called with %d arguments: %s %s %s %s %s",
1240				argc, argv[0], argv[1], argv[2], argv[3],
1241				argv[4]);
1242	else if (argc == 6)
1243		syslog(LOG_NOTICE, "Called with %d arguments: %s %s %s %s %s "
1244				"%s", argc, argv[0], argv[1], argv[2], argv[3],
1245				argv[4], argv[5]);
1246	else
1247		syslog(LOG_NOTICE, "Called with %d arguments: %s %s %s %s %s "
1248				"%s %s", argc, argv[0], argv[1], argv[2],
1249				argv[3], argv[4], argv[5], argv[6]);
1250	closelog();
1251#endif /* NTFS_UTIL_DEBUG */
1252	/*
1253	 * We support probe, mount, and unmount all of which need the command
1254	 * option and the device.
1255	 */
1256	if (argc < 2 || argv[0][0] != '-')
1257		usage(progname);
1258	opt = argv[0][1];
1259	dev = argv[1];
1260	argc -= 2;
1261	argv += 2;
1262	/* Check we have the right number of arguments. */
1263	switch (opt) {
1264	case FSUC_GETUUID:
1265		/* For get UUID key do not need any more arguments. */
1266		if (argc)
1267			usage(progname);
1268		break;
1269	case FSUC_PROBE:
1270		/* For probe need the two mountflags also. */
1271		if (argc != 2)
1272			usage(progname);
1273		break;
1274	case FSUC_MOUNT:
1275		/* For mount need the mount point and four mountflags also. */
1276		if (argc != 5)
1277			usage(progname);
1278		break;
1279	case FSUC_UNMOUNT:
1280		/* For unmount need the mount point also. */
1281		if (argc != 1)
1282			usage(progname);
1283		break;
1284	default:
1285		/* Unsupported command. */
1286		usage(progname);
1287		break;
1288	}
1289	/* Check the raw and block device special files exist. */
1290	err = snprintf(rawdev, sizeof(rawdev), "/dev/r%s", dev);
1291	if (err >= (int)sizeof(rawdev)) {
1292		fprintf(stderr, "%s: Specified device name is too long.\n",
1293				progname);
1294		exit(FSUR_INVAL);
1295	}
1296	if (stat(rawdev, &sb)) {
1297		fprintf(stderr, "%s: stat %s failed, %s\n", progname, rawdev,
1298				strerror(errno));
1299		exit(FSUR_INVAL);
1300	}
1301	err = snprintf(blockdev, sizeof(blockdev), "/dev/%s", dev);
1302	if (err >= (int)sizeof(blockdev)) {
1303		fprintf(stderr, "%s: Specified device name is too long.\n",
1304				progname);
1305		exit(FSUR_INVAL);
1306	}
1307	if (stat(blockdev, &sb)) {
1308		fprintf(stderr, "%s: stat %s failed, %s\n", progname, blockdev,
1309				strerror(errno));
1310		exit(FSUR_INVAL);
1311	}
1312	/* Get the mount point for the mount and unmount cases. */
1313	if (opt == FSUC_MOUNT || opt == FSUC_UNMOUNT) {
1314		mp = argv[0];
1315		argc--;
1316		argv++;
1317	}
1318	/* Get the mount flags for the probe and mount cases. */
1319	if (opt == FSUC_PROBE || opt == FSUC_MOUNT) {
1320		/* mountflag1: Removable or fixed. */
1321		if (!strcmp(argv[0], DEVICE_REMOVABLE))
1322			removable = TRUE;
1323		else if (strcmp(argv[0], DEVICE_FIXED))
1324			usage(progname);
1325		/* mountflag2: Readonly or writable. */
1326		if (!strcmp(argv[1], DEVICE_READONLY))
1327			readonly = TRUE;
1328		else if (strcmp(argv[1], DEVICE_WRITABLE))
1329			usage(progname);
1330		/* Only the mount command supports the third and fourth flag. */
1331		if (opt == FSUC_MOUNT) {
1332			/* mountflag3: Nosuid or suid. */
1333			if (!strcmp(argv[2], "nosuid"))
1334				nosuid = TRUE;
1335			else if (strcmp(argv[2], "suid"))
1336				usage(progname);
1337			/* mountflag4: Nodev or dev. */
1338			if (!strcmp(argv[3], "nodev"))
1339				nodev = TRUE;
1340			else if (strcmp(argv[3], "dev"))
1341				usage(progname);
1342		}
1343	}
1344	/* Finally execute the required command. */
1345	switch (opt) {
1346	case FSUC_GETUUID:
1347		err = do_getuuid(rawdev);
1348		break;
1349	case FSUC_PROBE:
1350		err = do_probe(rawdev, removable, readonly);
1351		break;
1352	case FSUC_MOUNT:
1353		err = do_mount(progname, blockdev, mp, removable, readonly,
1354				nosuid, nodev);
1355		break;
1356	case FSUC_UNMOUNT:
1357		err = do_unmount(progname, mp);
1358		break;
1359	default:
1360		/* Cannot happen... */
1361		usage(progname);
1362		break;
1363	}
1364	return err;
1365}
1366