]> git.saurik.com Git - apple/hfs.git/blob - mount_hfs/mount_hfs.c
hfs-305.10.1.tar.gz
[apple/hfs.git] / mount_hfs / mount_hfs.c
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 */
67 static void
68 replay_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
89 done:
90 if (fd != -1)
91 close(fd);
92 return;
93 }
94
95 struct 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
105 gid_t a_gid __P((char *));
106 uid_t a_uid __P((char *));
107 mode_t a_mask __P((char *));
108 struct hfs_mnt_encoding * a_encoding __P((char *));
109 int get_encoding_pref __P((const char *));
110 int get_encoding_bias __P((void));
111 unsigned int get_default_encoding(void);
112
113 void usage __P((void));
114
115
116 int is_hfs_std = 0;
117 int wrapper_requested = 0;
118
119 typedef 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/Contents/Resources/Encodings/"
136
137 #define MXENCDNAMELEN 16 /* Maximun length of encoding name string */
138
139 struct 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 */
149 struct 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 */
195 const 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 */
240 void *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
309 done:
310 if (fd >= 0)
311 close(fd);
312 if (buf != NULL)
313 free(buf);
314 return masterBlock;
315 }
316
317
318 u_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
345 exit:
346 return volume_create_time;
347 }
348
349 void 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 */
405 static int
406 load_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
444 int
445 main(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
638 #if DEBUG
639 printf("mount_hfs: calling mount: \n" );
640 printf("\tdevice = %s\n", dev);
641 printf("\tmount point = %s\n", dir);
642 printf("\tmount flags = 0x%08x\n", mntflags);
643 printf("\targ flags = 0x%x\n", args.flags);
644 printf("\tuid = %d\n", args.hfs_uid);
645 printf("\tgid = %d\n", args.hfs_gid);
646 printf("\tmode = %o\n", args.hfs_mask);
647 printf("\tencoding = %ld\n", args.hfs_encoding);
648
649 #endif
650
651 #if !TARGET_OS_EMBEDDED
652 /*
653 * We shouldn't really be calling up to other layers, but
654 * an exception was made in this case to fix the situation
655 * where HFS was writable on optical media.
656 */
657
658 if ((_optical_is_writable(dev) & _OPTICAL_WRITABLE_PACKET)) {
659 mntflags |= MNT_RDONLY;
660 }
661 #endif
662
663 if (is_hfs_std)
664 mntflags |= MNT_RDONLY;
665
666 if ((mntflags & MNT_RDONLY) == 0) {
667 /*
668 * get the volume's create date so we can synchronize
669 * it with the root directory create date
670 */
671 localCreateTime = getVolumeCreateDate(dev);
672 }
673 else {
674 localCreateTime = 0;
675 }
676
677 if ((mountStatus = mount(HFS_MOUNT_TYPE, dir, mntflags, &args)) < 0) {
678 printf("mount_hfs: error on mount(): error = %d.\n", mountStatus);
679 err(1, NULL);
680 };
681
682 /*
683 * synchronize the root directory's create date
684 * with the volume's create date
685 */
686 if (localCreateTime)
687 syncCreateDate(dir, localCreateTime);
688
689
690 exit(0);
691 }
692
693
694 gid_t
695 a_gid(s)
696 char *s;
697 {
698 struct group *gr;
699 char *gname, *orig = s;
700 gid_t gid = 0;
701
702 if (*s == '-')
703 s++;
704 for (gname = s; *s && isdigit(*s); ++s);
705 if (!*s) {
706 gid = atoi(gname);
707 } else {
708 gr = getgrnam(orig);
709 if (gr == NULL)
710 errx(1, "unknown group id: %s", orig);
711 gid = gr->gr_gid;
712 }
713 return (gid);
714 }
715
716 uid_t
717 a_uid(s)
718 char *s;
719 {
720 struct passwd *pw;
721 char *uname, *orig = s;
722 uid_t uid = 0;
723
724 if (*s == '-')
725 s++;
726 for (uname = s; *s && isdigit(*s); ++s);
727 if (!*s) {
728 uid = atoi(uname);
729 } else {
730 pw = getpwnam(orig);
731 if (pw == NULL)
732 errx(1, "unknown user id: %s", orig);
733 uid = pw->pw_uid;
734 }
735 return (uid);
736 }
737
738 mode_t
739 a_mask(s)
740 char *s;
741 {
742 int done, rv;
743 char *ep;
744
745 done = 0;
746 rv = -1;
747 if (*s >= '0' && *s <= '7') {
748 done = 1;
749 rv = strtol(optarg, &ep, 8);
750 }
751 if (!done || rv < 0 || *ep)
752 errx(1, "invalid file mode: %s", s);
753 return (rv);
754 }
755
756 struct hfs_mnt_encoding *
757 a_encoding(s)
758 char *s;
759 {
760 char *uname;
761 int i;
762 u_int32_t encoding;
763 struct hfs_mnt_encoding *p, *q, *enclist;
764 int elements = sizeof(hfs_mnt_encodinglist) / sizeof(struct hfs_mnt_encoding);
765 int compare;
766
767 /* Use a binary search to find an encoding match */
768 p = hfs_mnt_encodinglist;
769 q = p + (elements - 1);
770 while (p <= q) {
771 enclist = p + ((q - p) >> 1); /* divide by 2 */
772 compare = strcmp(s, enclist->encoding_name);
773 if (compare < 0)
774 q = enclist - 1;
775 else if (compare > 0)
776 p = enclist + 1;
777 else
778 return (enclist);
779 }
780
781 for (uname = s; *s && isdigit(*s); ++s);
782
783 if (*s) goto unknown;
784
785 encoding = atoi(uname);
786 for (i=0, enclist = hfs_mnt_encodinglist; i < elements; i++, enclist++) {
787 if (enclist->encoding_id == encoding)
788 return (enclist);
789 }
790
791 unknown:
792 errx(1, "unknown encoding: %s", uname);
793 return (NULL);
794 }
795
796
797 /*
798 * Get file system's encoding preference.
799 */
800 int
801 get_encoding_pref(const char *device)
802 {
803 struct hfs_mnt_encoding *enclist;
804 HFSMasterDirectoryBlock * mdbp;
805 int encoding = -1;
806 int elements;
807 int i;
808
809 mdbp = GetMasterBlock(device);
810 if (mdbp == NULL)
811 return 0;
812
813 if (SWAP_BE16(mdbp->drSigWord) != kHFSSigWord ||
814 (SWAP_BE16(mdbp->drEmbedSigWord) == kHFSPlusSigWord && (!wrapper_requested))) {
815 return (-1);
816 }
817 else {
818 is_hfs_std = 1;
819 }
820 encoding = GET_HFS_TEXT_ENCODING(SWAP_BE32(mdbp->drFndrInfo[4]));
821
822 if (encoding == -1) {
823 encoding = get_encoding_bias();
824 if (encoding == 0 || encoding == -1)
825 encoding = get_default_encoding();
826 }
827
828 /* Check if this is a supported encoding. */
829 elements = sizeof(hfs_mnt_encodinglist) / sizeof(struct hfs_mnt_encoding);
830 for (i=0, enclist = hfs_mnt_encodinglist; i < elements; i++, enclist++) {
831 if (enclist->encoding_id == encoding)
832 return (encoding);
833 }
834
835 return (0);
836 }
837
838 /*
839 * Get kernel's encoding bias.
840 */
841 int
842 get_encoding_bias()
843 {
844 int mib[3];
845 size_t buflen = sizeof(int);
846 struct vfsconf vfc;
847 int hint = 0;
848
849 if (getvfsbyname("hfs", &vfc) < 0)
850 goto error;
851
852 mib[0] = CTL_VFS;
853 mib[1] = vfc.vfc_typenum;
854 mib[2] = HFS_ENCODINGBIAS;
855
856 if (sysctl(mib, 3, &hint, &buflen, NULL, 0) < 0)
857 goto error;
858 return (hint);
859 error:
860 return (-1);
861 }
862
863 #define __kCFUserEncodingFileName ("/.CFUserTextEncoding")
864
865 unsigned int
866 get_default_encoding()
867 {
868 struct passwd *passwdp;
869
870 if ((passwdp = getpwuid(0))) { /* root account */
871 char buffer[MAXPATHLEN + 1];
872 int fd;
873
874 strlcpy(buffer, passwdp->pw_dir, sizeof(buffer));
875 strlcat(buffer, __kCFUserEncodingFileName, sizeof(buffer));
876
877 if ((fd = open(buffer, O_RDONLY, 0)) > 0) {
878 ssize_t readSize;
879
880 readSize = read(fd, buffer, MAXPATHLEN);
881 buffer[(readSize < 0 ? 0 : readSize)] = '\0';
882 close(fd);
883 return strtol(buffer, NULL, 0);
884 }
885 }
886 return (0); /* Fallback to smRoman */
887 }
888
889
890 void
891 usage()
892 {
893 (void)fprintf(stderr,
894 "usage: mount_hfs [-xw] [-u user] [-g group] [-m mask] [-e encoding] [-t tbuffer-size] [-j] [-c] [-o options] special-device filesystem-node\n");
895
896 exit(1);
897 }