1/*
2 * Copyright (c) 1999-2013 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24#include <sys/types.h>
25
26#include <sys/attr.h>
27#include <sys/mount.h>
28#include <sys/stat.h>
29#include <sys/sysctl.h>
30#include <sys/time.h>
31#include <sys/uio.h>
32#include <sys/vnode.h>
33#include <sys/wait.h>
34#include <sys/ioctl.h>
35#include <sys/disk.h>
36
37#include <ctype.h>
38#include <err.h>
39#include <errno.h>
40#include <fcntl.h>
41#include <grp.h>
42#include <limits.h>
43#include <pwd.h>
44#include <stdio.h>
45#include <stdlib.h>
46#include <string.h>
47#include <unistd.h>
48#include <signal.h>
49
50#include <hfs/hfs_mount.h>
51#include <hfs/hfs_format.h>
52
53#include <TargetConditionals.h>
54
55/* Sensible wrappers over the byte-swapping routines */
56#include "hfs_endian.h"
57#if !TARGET_OS_EMBEDDED
58#include "optical.h"
59#endif
60
61#include <mntopts.h>
62
63
64/*
65 * Replay the journal.  We don't care if there are problems.
66 */
67static void
68replay_journal(const char *device)
69{
70	struct vfsconf vfc;
71	int mib[4];
72	int fd = -1;
73
74	fd = open(device, O_RDWR);
75	if (fd == -1) {
76		warn("Could not open block device %s for writing", device);
77		goto done;
78	}
79	if (getvfsbyname("hfs", &vfc) != 0) {
80		warn("Could not get hfs vfs information");
81		goto done;
82	}
83	mib[0] = CTL_VFS;
84	mib[1] = vfc.vfc_typenum;
85	mib[2] = HFS_REPLAY_JOURNAL;
86	mib[3] = fd;
87	(void)sysctl(mib, 4, NULL, NULL, NULL, 0);
88
89done:
90	if (fd != -1)
91		close(fd);
92	return;
93}
94
95struct mntopt mopts[] = {
96	MOPT_STDOPTS,
97	MOPT_IGNORE_OWNERSHIP,
98	MOPT_PERMISSIONS,
99	MOPT_UPDATE,
100	{ NULL }
101};
102
103#define HFS_MOUNT_TYPE				"hfs"
104
105gid_t	a_gid __P((char *));
106uid_t	a_uid __P((char *));
107mode_t	a_mask __P((char *));
108struct hfs_mnt_encoding * a_encoding __P((char *));
109int	get_encoding_pref __P((const char *));
110int	get_encoding_bias __P((void));
111unsigned int  get_default_encoding(void);
112
113void	usage __P((void));
114
115
116int is_hfs_std = 0;
117int wrapper_requested = 0;
118
119typedef struct CreateDateAttrBuf {
120    u_int32_t size;
121    struct timespec creationTime;
122} CreateDateAttrBuf;
123
124#define	HFS_BLOCK_SIZE	512
125
126/*
127 *	This is the straight GMT conversion constant:
128 *	00:00:00 January 1, 1970 - 00:00:00 January 1, 1904
129 *	(3600 * 24 * ((365 * (1970 - 1904)) + (((1970 - 1904) / 4) + 1)))
130 */
131#define MAC_GMT_FACTOR		2082844800UL
132
133#define KEXT_LOAD_COMMAND	"/sbin/kextload"
134
135#define ENCODING_MODULE_PATH	"/System/Library/Filesystems/hfs.fs/Encodings/"
136
137#define MXENCDNAMELEN	16	/* Maximun length of encoding name string */
138
139struct hfs_mnt_encoding {
140	char	encoding_name[MXENCDNAMELEN];	/* encoding type name */
141	u_int32_t encoding_id;			/* encoding type number */
142};
143
144
145/*
146 * Lookup table for hfs encoding names
147 * Note: Names must be in alphabetical order
148 */
149struct hfs_mnt_encoding hfs_mnt_encodinglist[] = {
150	{ "Arabic",	          4 },
151	{ "Armenian",        24 },
152	{ "Bengali",         13 },
153	{ "Burmese",         19 },
154	{ "Celtic",          39 },
155	{ "CentralEurRoman", 29 },
156	{ "ChineseSimp",     25 },
157	{ "ChineseTrad",      2 },
158	{ "Croatian",	     36 },
159	{ "Cyrillic",	      7 },
160	{ "Devanagari",       9 },
161	{ "Ethiopic",        28 },
162	{ "Farsi",          140 },
163	{ "Gaelic",          40 },
164	{ "Georgian",        23 },
165	{ "Greek",	          6 },
166	{ "Gujarati",        11 },
167	{ "Gurmukhi",        10 },
168	{ "Hebrew",	          5 },
169	{ "Icelandic",	     37 },
170	{ "Japanese",	      1 },
171	{ "Kannada",         16 },
172	{ "Khmer",           20 },
173	{ "Korean",	          3 },
174	{ "Laotian",         22 },
175	{ "Malayalam",       17 },
176	{ "Mongolian",       27 },
177	{ "Oriya",           12 },
178	{ "Roman",	          0 },	/* default */
179	{ "Romanian",	     38 },
180	{ "Sinhalese",       18 },
181	{ "Tamil",           14 },
182	{ "Telugu",          15 },
183	{ "Thai",	         21 },
184	{ "Tibetan",         26 },
185	{ "Turkish",	     35 },
186	{ "Ukrainian",      152 },
187	{ "Vietnamese",      30 },
188};
189
190
191/*
192    If path is a path to a block device, then return a path to the
193    corresponding raw device.  Else return path unchanged.
194*/
195const char *rawdevice(const char *path)
196{
197	const char *devdisk = "/dev/disk";
198	static char raw[MAXPATHLEN];
199
200	if (!strncmp(path, devdisk, strlen(devdisk))) {
201		/* The +5 below is strlen("/dev/"), so path+5 points to "disk..." */
202		int sn_len = snprintf(raw, sizeof(raw), "/dev/r%s", path+5);
203		if (sn_len < 0) {
204			/* error in building string. return original. */
205			return path;
206		}
207
208		if ((unsigned long) sn_len < sizeof(raw)) {
209			return raw;
210		}
211	}
212	return path;
213}
214
215
216/*
217	GetMasterBlock
218
219	Return a pointer to the Master Directory Block or Volume Header Block
220	for the volume.  In the case of an HFS volume with embedded HFS Plus
221	volume, this returns the HFS (wrapper) volume's Master Directory Block.
222	That is, the 512 bytes at offset 1024 bytes from the start of the given
223	device/partition.
224
225	The master block is cached globally.  If it has previously been read in,
226	the cached copy will be returned.  If this routine is called multiple times,
227	it must be called with the same device string.
228
229	Arguments:
230		device		Path name to disk device (eg., "/dev/disk0s2")
231
232	Returns:
233	A pointer to the MDB or VHB.  This pointer may be in the middle of a
234	malloc'ed block.  There may be more than 512 bytes of malloc'ed memory
235	at the returned address.
236
237	Errors:
238	On error, this routine returns NULL.
239*/
240void *GetMasterBlock(const char *device)
241{
242	static char *masterBlock = NULL;
243	char *buf = NULL;
244	int err;
245	int fd = -1;
246	uint32_t blockSize;
247	ssize_t amount;
248	off_t offset;
249
250	/*
251	 * If we already read the master block, then just return it.
252	 */
253	if (masterBlock != NULL) {
254		return masterBlock;
255	}
256
257	device = rawdevice(device);
258
259	fd = open(device, O_RDONLY | O_NDELAY, 0);
260	if (fd < 0) {
261		fprintf(stderr, "GetMasterBlock: Error %d opening %s\n", errno, device);
262		goto done;
263	}
264
265	/*
266	 * Get the block size so we can read an entire block.
267	 */
268	err = ioctl(fd, DKIOCGETBLOCKSIZE, &blockSize);
269	if (err == -1) {
270		fprintf(stderr, "GetMasterBlock: Error %d getting block size\n", errno);
271		goto done;
272	}
273
274	/*
275	 * Figure out the offset of the start of the block which contains
276	 * byte offset 1024 (the start of the master block).  This is 1024
277	 * rounded down to a multiple of blockSize.  But since blockSize is
278	 * always a power of two, this will be either 0 (if blockSize > 1024)
279	 * or 1024 (if blockSize <= 1024).
280	 */
281	offset = blockSize > 1024 ? 0 : 1024;
282
283	/*
284	 * Allocate a buffer and read the block.
285	 */
286	buf = malloc(blockSize);
287	if (buf == NULL) {
288		fprintf(stderr, "GetMasterBlock: Could not malloc %u bytes\n", blockSize);
289		goto done;
290	}
291	amount = pread(fd, buf, blockSize, offset);
292	if (amount != blockSize) {
293		fprintf(stderr, "GetMasterBlock: Error %d from read; amount=%ld, wanted=%u\n", errno, amount, blockSize);
294		goto done;
295	}
296
297	/*
298	 * Point at the part of the buffer containing the master block.
299	 * Then return that pointer.
300	 *
301	 * Note: if blockSize <= 1024, then offset = 1024, and the master
302	 * block is at the start of the buffer.  If blockSize > 1024, then
303	 * offset = 0, and the master block is at offset 1024 from the start
304	 * of the buffer.
305	 */
306	masterBlock = buf + 1024 - offset;
307	buf = NULL;	/* Don't free memory that masterBlock points into. */
308
309done:
310	if (fd >= 0)
311		close(fd);
312	if (buf != NULL)
313		free(buf);
314	return masterBlock;
315}
316
317
318u_int32_t getVolumeCreateDate(const char *device)
319{
320	HFSMasterDirectoryBlock * mdbPtr;
321	u_int32_t volume_create_time = 0;
322
323	mdbPtr = GetMasterBlock(device);
324	if (mdbPtr == NULL) goto exit;
325
326	/* get the create date from the MDB (embedded case) or Volume Header */
327	if ((mdbPtr->drSigWord == SWAP_BE16 (kHFSSigWord)) &&
328	    (mdbPtr->drEmbedSigWord == SWAP_BE16 (kHFSPlusSigWord))) {
329		/* Embedded volume*/
330		volume_create_time = SWAP_BE32 (mdbPtr->drCrDate);
331
332	} else if (mdbPtr->drSigWord == kHFSPlusSigWord ) {
333		HFSPlusVolumeHeader * volHdrPtr = (HFSPlusVolumeHeader *) mdbPtr;
334
335		volume_create_time = SWAP_BE32 (volHdrPtr->createDate);
336	} else {
337		goto exit;	/* cound not match signature */
338	}
339
340	if (volume_create_time > MAC_GMT_FACTOR)
341		volume_create_time -= MAC_GMT_FACTOR;
342	else
343		volume_create_time = 0;	/* don't let date go negative! */
344
345exit:
346	return volume_create_time;
347}
348
349void syncCreateDate(const char *mntpt, u_int32_t localCreateTime)
350{
351	int result;
352	char path[256];
353	struct attrlist	attributes;
354	CreateDateAttrBuf attrReturnBuffer;
355	int64_t gmtCreateTime;
356	int32_t gmtOffset;
357	int32_t newCreateTime;
358
359	snprintf(path, sizeof(path), "%s/", mntpt);
360
361	attributes.bitmapcount	= ATTR_BIT_MAP_COUNT;
362	attributes.reserved		= 0;
363	attributes.commonattr	= ATTR_CMN_CRTIME;
364	attributes.volattr 		= 0;
365	attributes.dirattr 		= 0;
366	attributes.fileattr 	= 0;
367	attributes.forkattr 	= 0;
368
369	result = getattrlist(path, &attributes, &attrReturnBuffer, sizeof(attrReturnBuffer), 0 );
370	if (result) return;
371
372	gmtCreateTime = attrReturnBuffer.creationTime.tv_sec;
373	gmtOffset = gmtCreateTime - (int64_t) localCreateTime + 900;
374	if (gmtOffset > 0) {
375		gmtOffset = 1800 * (gmtOffset / 1800);
376	} else {
377		gmtOffset = -1800 * ((-gmtOffset + 1799) / 1800);
378	}
379
380	newCreateTime = localCreateTime + gmtOffset;
381
382	/*
383	 * if the root directory's create date doesn't match
384	 * and its within +/- 15 seconds, then update it
385	 */
386	if ((newCreateTime != attrReturnBuffer.creationTime.tv_sec) &&
387		(( newCreateTime - attrReturnBuffer.creationTime.tv_sec) > -15) &&
388		((newCreateTime - attrReturnBuffer.creationTime.tv_sec) < 15)) {
389
390		attrReturnBuffer.creationTime.tv_sec = (time_t) newCreateTime;
391		(void) setattrlist (path,
392				    &attributes,
393				    &attrReturnBuffer.creationTime,
394				    sizeof(attrReturnBuffer.creationTime),
395				    0);
396	}
397}
398
399/*
400 * load_encoding
401 * loads an hfs encoding converter module into the kernel
402 *
403 * Note: unloading of encoding converter modules is done in the kernel
404 */
405static int
406load_encoding(struct hfs_mnt_encoding *encp)
407{
408	int pid;
409	int loaded;
410	union wait status;
411	struct stat sb;
412	char kmodfile[MAXPATHLEN];
413
414	/* MacRoman encoding (0) is built into the kernel */
415	if (encp->encoding_id == 0)
416		return (0);
417
418	snprintf(kmodfile, sizeof(kmodfile), "%sHFS_Mac%s.kext", ENCODING_MODULE_PATH, encp->encoding_name);
419	if (stat(kmodfile, &sb) == -1) {
420		fprintf(stdout, "unable to find: %s\n", kmodfile);
421		return (-1);
422	}
423
424	loaded = 0;
425	pid = fork();
426	if (pid == 0) {
427		(void) execl(KEXT_LOAD_COMMAND, KEXT_LOAD_COMMAND, kmodfile, NULL);
428
429		exit(1);	/* We can only get here if the exec failed */
430	} else if (pid != -1) {
431		if ((waitpid(pid, (int *)&status, 0) == pid) && WIFEXITED(status)) {
432			/* we attempted a load */
433			loaded = 1;
434		}
435	}
436
437	if (!loaded) {
438		fprintf(stderr, "unable to load: %s\n", kmodfile);
439		return (-1);
440	}
441	return (0);
442}
443
444int
445main(argc, argv)
446	int argc;
447	char **argv;
448{
449	struct hfs_mount_args args;
450	int ch, mntflags;
451	char *dev, dir[MAXPATHLEN];
452	int mountStatus;
453	struct timeval dummy_timeval; /* gettimeofday() crashes if the first argument is NULL */
454	u_int32_t localCreateTime;
455	struct hfs_mnt_encoding *encp;
456
457	int do_rekey = 0;
458	int tmp_mntflags = 0;
459#if TARGET_OS_EMBEDDED
460	mntflags = MNT_NOATIME;
461#else
462	mntflags = 0;
463#endif
464	encp = NULL;
465	(void)memset(&args, '\0', sizeof(struct hfs_mount_args));
466
467   	/*
468   	 * For a mount update, the following args must be explictly
469   	 * passed in as options to change their value.  On a new
470   	 * mount, default values will be computed for all args.
471   	 */
472	args.flags = VNOVAL;
473	args.hfs_uid = (uid_t)VNOVAL;
474	args.hfs_gid = (gid_t)VNOVAL;
475	args.hfs_mask = (mode_t)VNOVAL;
476	args.hfs_encoding = (u_int32_t)VNOVAL;
477
478	optind = optreset = 1;		/* Reset for parse of new argv. */
479	while ((ch = getopt(argc, argv, "xu:g:m:e:o:wt:jc")) != EOF) {
480		switch (ch) {
481		case 't': {
482			char *ptr;
483			unsigned long tbufsize = strtoul(optarg, &ptr, 0);
484			if (tbufsize >= UINT_MAX) {
485				tbufsize = UINT_MAX;
486			}
487			args.journal_tbuffer_size = (unsigned int) strtoul(optarg, &ptr, 0);
488			if ((args.journal_tbuffer_size == 0 ||
489						((uint32_t) args.journal_tbuffer_size) == UINT_MAX) && errno != 0) {
490				fprintf(stderr, "%s: Invalid tbuffer size %s\n", argv[0], optarg);
491				exit(5);
492			} else {
493				if (*ptr == 'k')
494					args.journal_tbuffer_size *= 1024;
495				else if (*ptr == 'm')
496					args.journal_tbuffer_size *= 1024*1024;
497			}
498			if (args.flags == VNOVAL){
499				args.flags = 0;
500			}
501			args.flags |= HFSFSMNT_EXTENDED_ARGS;
502			break;
503		}
504		case 'j':
505			/* disable the journal */
506			if(args.flags == VNOVAL){
507				args.flags = 0;
508			}
509			args.flags |= HFSFSMNT_EXTENDED_ARGS;
510			args.journal_disable = 1;
511			break;
512		case 'c':
513			// XXXdbg JOURNAL_NO_GROUP_COMMIT == 0x0001
514			args.journal_flags = 0x0001;
515			break;
516		case 'x':
517			if (args.flags == VNOVAL)
518				args.flags = 0;
519			args.flags |= HFSFSMNT_NOXONFILES;
520			break;
521		case 'u':
522			args.hfs_uid = a_uid(optarg);
523			break;
524		case 'g':
525			args.hfs_gid = a_gid(optarg);
526			break;
527		case 'm':
528			args.hfs_mask = a_mask(optarg);
529			break;
530		case 'e':
531			encp = a_encoding(optarg);
532			break;
533		case 'o':
534			{
535				int dummy;
536				getmntopts(optarg, mopts, &mntflags, &dummy);
537			}
538			break;
539		case 'w':
540			if (args.flags == VNOVAL)
541				args.flags = 0;
542			args.flags |= HFSFSMNT_WRAPPER;
543			wrapper_requested = 1;
544			break;
545		case '?':
546			usage();
547			break;
548		default:
549#if DEBUG
550			printf("mount_hfs: ERROR: unrecognized ch = '%c'\n", ch);
551#endif
552			usage();
553		}; /* switch */
554	}
555
556	if ((mntflags & MNT_IGNORE_OWNERSHIP) && !(mntflags & MNT_UPDATE)) {
557		/*
558		 * The defaults to be supplied in lieu of the on-disk permissions
559		 * (could be overridden by explicit -u, -g, or -m options):
560		 */
561		if (args.hfs_uid == (uid_t)VNOVAL) args.hfs_uid = UNKNOWNUID;
562		if (args.hfs_gid == (gid_t)VNOVAL) args.hfs_gid = UNKNOWNGID;
563#if OVERRIDE_UNKNOWN_PERMISSIONS
564		if (args.hfs_mask == (mode_t)VNOVAL) args.hfs_mask = ACCESSPERMS;  /* 0777 */
565#endif
566	}
567	argc -= optind;
568	argv += optind;
569
570	if (argc != 2) {
571#if DEBUG
572		printf("mount_hfs: ERROR: argc == %d != 2\n", argc);
573#endif
574		usage();
575	}
576
577	dev = argv[0];
578
579	if (realpath(argv[1], dir) == NULL)
580		err(1, "realpath %s", dir);
581
582	args.fspec = dev;
583
584	/* HFS volumes need timezone info to convert local to GMT */
585	(void) gettimeofday( &dummy_timeval, &args.hfs_timezone );
586
587	/* load requested encoding (if any) for hfs volume */
588	if (encp != NULL) {
589		if (load_encoding(encp) != 0)
590			exit(1);  /* load failure */
591		args.hfs_encoding = encp->encoding_id;
592	}
593
594	/*
595	 * For a new mount (non-update case) fill in default values for all args
596	 */
597	if ((mntflags & MNT_UPDATE) == 0) {
598
599		struct stat sb;
600
601		if (args.flags == VNOVAL)
602			args.flags = 0;
603
604		if ((args.hfs_encoding == (u_int32_t)VNOVAL) && (encp == NULL)) {
605			int encoding;
606
607			/* Find a suitable encoding preference. */
608			if ((encoding = get_encoding_pref(dev)) != -1) {
609				/*
610				 * Note: the encoding kext was loaded by
611				 * hfs.util during the file system probe.
612				 */
613				args.hfs_encoding = encoding;
614			} else {
615				args.hfs_encoding = 0;
616			}
617		}
618		/* when the mountpoint is root, use default values */
619		if (strcmp(dir, "/") == 0) {
620			sb.st_mode = 0777;
621			sb.st_uid = 0;
622			sb.st_gid = 0;
623
624		/* otherwise inherit from the mountpoint */
625		} else if (stat(dir, &sb) == -1)
626			err(1, "stat %s", dir);
627
628		if (args.hfs_uid == (uid_t)VNOVAL)
629			args.hfs_uid = sb.st_uid;
630
631		if (args.hfs_gid == (gid_t)VNOVAL)
632			args.hfs_gid = sb.st_gid;
633
634		if (args.hfs_mask == (mode_t)VNOVAL)
635			args.hfs_mask = sb.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO);
636	}
637#if DEBUG
638    printf("mount_hfs: calling mount: \n" );
639    printf("\tdevice = %s\n", dev);
640    printf("\tmount point = %s\n", dir);
641    printf("\tmount flags = 0x%08x\n", mntflags);
642    printf("\targ flags = 0x%x\n", args.flags);
643    printf("\tuid = %d\n", args.hfs_uid);
644    printf("\tgid = %d\n", args.hfs_gid);
645    printf("\tmode = %o\n", args.hfs_mask);
646    printf("\tencoding = %ld\n", args.hfs_encoding);
647
648#endif
649
650#if !TARGET_OS_EMBEDDED
651    /*
652     * We shouldn't really be calling up to other layers, but
653     * an exception was made in this case to fix the situation
654     * where HFS was writable on optical media.
655     */
656
657    if ((_optical_is_writable(dev) & _OPTICAL_WRITABLE_PACKET)) {
658	    mntflags |= MNT_RDONLY;
659    }
660#endif
661
662    if (is_hfs_std)
663	    mntflags |= MNT_RDONLY;
664
665    if ((mntflags & MNT_RDONLY) == 0) {
666	    /*
667	     * get the volume's create date so we can synchronize
668	     * it with the root directory create date
669	     */
670	    localCreateTime = getVolumeCreateDate(dev);
671    }
672    else {
673	    localCreateTime = 0;
674    }
675
676    if ((mountStatus = mount(HFS_MOUNT_TYPE, dir, mntflags, &args)) < 0) {
677#if DEBUG
678	    printf("mount_hfs: error on mount(): error = %d.\n", mountStatus);
679#endif
680	    err(1, NULL);
681    };
682
683    /*
684     * synchronize the root directory's create date
685     * with the volume's create date
686     */
687    if (localCreateTime)
688	    syncCreateDate(dir, localCreateTime);
689
690
691    exit(0);
692}
693
694
695gid_t
696a_gid(s)
697    char *s;
698{
699    struct group *gr;
700    char *gname, *orig = s;
701    gid_t gid = 0;
702
703    if (*s == '-')
704	s++;
705    for (gname = s; *s && isdigit(*s); ++s);
706    if (!*s) {
707	gid = atoi(gname);
708    } else {
709	gr = getgrnam(orig);
710	if (gr == NULL)
711	    errx(1, "unknown group id: %s", orig);
712	gid = gr->gr_gid;
713    }
714    return (gid);
715}
716
717uid_t
718a_uid(s)
719    char *s;
720{
721    struct passwd *pw;
722    char *uname, *orig = s;
723    uid_t uid = 0;
724
725    if (*s == '-')
726	s++;
727    for (uname = s; *s && isdigit(*s); ++s);
728    if (!*s) {
729	uid = atoi(uname);
730    } else {
731	pw = getpwnam(orig);
732	if (pw == NULL)
733	    errx(1, "unknown user id: %s", orig);
734	uid = pw->pw_uid;
735    }
736    return (uid);
737}
738
739mode_t
740a_mask(s)
741    char *s;
742{
743    int done, rv;
744    char *ep;
745
746    done = 0;
747    rv = -1;
748    if (*s >= '0' && *s <= '7') {
749        done = 1;
750        rv = strtol(optarg, &ep, 8);
751    }
752    if (!done || rv < 0 || *ep)
753        errx(1, "invalid file mode: %s", s);
754    return (rv);
755}
756
757struct hfs_mnt_encoding *
758a_encoding(s)
759	char *s;
760{
761	char *uname;
762	int i;
763	u_int32_t encoding;
764	struct hfs_mnt_encoding *p, *q, *enclist;
765	int elements = sizeof(hfs_mnt_encodinglist) / sizeof(struct hfs_mnt_encoding);
766	int compare;
767
768	/* Use a binary search to find an encoding match */
769	p = hfs_mnt_encodinglist;
770	q = p + (elements - 1);
771	while (p <= q) {
772		enclist = p + ((q - p) >> 1);	/* divide by 2 */
773		compare = strcmp(s, enclist->encoding_name);
774		if (compare < 0)
775			q = enclist - 1;
776		else if (compare > 0)
777			p = enclist + 1;
778		else
779			return (enclist);
780	}
781
782	for (uname = s; *s && isdigit(*s); ++s);
783
784	if (*s) goto unknown;
785
786	encoding = atoi(uname);
787	for (i=0, enclist = hfs_mnt_encodinglist; i < elements; i++, enclist++) {
788		if (enclist->encoding_id == encoding)
789			return (enclist);
790	}
791
792unknown:
793	errx(1, "unknown encoding: %s", uname);
794	return (NULL);
795}
796
797
798/*
799 * Get file system's encoding preference.
800 */
801int
802get_encoding_pref(const char *device)
803{
804	struct hfs_mnt_encoding *enclist;
805	HFSMasterDirectoryBlock * mdbp;
806	int encoding = -1;
807	int elements;
808	int i;
809
810	mdbp = GetMasterBlock(device);
811	if (mdbp == NULL)
812		return 0;
813
814	if (SWAP_BE16(mdbp->drSigWord) != kHFSSigWord ||
815	    (SWAP_BE16(mdbp->drEmbedSigWord) == kHFSPlusSigWord && (!wrapper_requested))) {
816		return (-1);
817	}
818	else {
819		is_hfs_std = 1;
820	}
821	encoding = GET_HFS_TEXT_ENCODING(SWAP_BE32(mdbp->drFndrInfo[4]));
822
823	if (encoding == -1) {
824		encoding = get_encoding_bias();
825		if (encoding == 0 || encoding == -1)
826			encoding = get_default_encoding();
827	}
828
829	/* Check if this is a supported encoding. */
830	elements = sizeof(hfs_mnt_encodinglist) / sizeof(struct hfs_mnt_encoding);
831	for (i=0, enclist = hfs_mnt_encodinglist; i < elements; i++, enclist++) {
832		if (enclist->encoding_id == encoding)
833			return (encoding);
834	}
835
836	return (0);
837}
838
839/*
840 * Get kernel's encoding bias.
841 */
842int
843get_encoding_bias()
844{
845        int mib[3];
846        size_t buflen = sizeof(int);
847        struct vfsconf vfc;
848        int hint = 0;
849
850        if (getvfsbyname("hfs", &vfc) < 0)
851		goto error;
852
853        mib[0] = CTL_VFS;
854        mib[1] = vfc.vfc_typenum;
855        mib[2] = HFS_ENCODINGBIAS;
856
857	if (sysctl(mib, 3, &hint, &buflen, NULL, 0) < 0)
858 		goto error;
859	return (hint);
860error:
861	return (-1);
862}
863
864#define __kCFUserEncodingFileName ("/.CFUserTextEncoding")
865
866unsigned int
867get_default_encoding()
868{
869	struct passwd *passwdp;
870
871	if ((passwdp = getpwuid(0))) {	/* root account */
872		char buffer[MAXPATHLEN + 1];
873		int fd;
874
875		strlcpy(buffer, passwdp->pw_dir, sizeof(buffer));
876		strlcat(buffer, __kCFUserEncodingFileName, sizeof(buffer));
877
878		if ((fd = open(buffer, O_RDONLY, 0)) > 0) {
879			ssize_t readSize;
880
881			readSize = read(fd, buffer, MAXPATHLEN);
882			buffer[(readSize < 0 ? 0 : readSize)] = '\0';
883			close(fd);
884			return strtol(buffer, NULL, 0);
885		}
886	}
887	return (0);	/* Fallback to smRoman */
888}
889
890
891void
892usage()
893{
894	(void)fprintf(stderr,
895               "usage: mount_hfs [-xw] [-u user] [-g group] [-m mask] [-e encoding] [-t tbuffer-size] [-j] [-c] [-o options] special-device filesystem-node\n");
896	(void)fprintf(stderr, "   -j disables journaling; -c disables group-commit for journaling\n");
897
898	exit(1);
899}
900