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