]> git.saurik.com Git - apple/hfs.git/blob - mount_hfs/mount_hfs.c
hfs-522.0.9.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_OSX
58 #include "optical.h" //only include optical headers on Macs
59 #endif //osx
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
460 #if TARGET_OS_IPHONE
461 mntflags = MNT_NOATIME;
462 #else // !TARGET_OS_IPHONE
463 mntflags = 0;
464 #endif // TARGET_OS_IPHONE
465
466 encp = NULL;
467 (void)memset(&args, '\0', sizeof(struct hfs_mount_args));
468
469 /*
470 * For a mount update, the following args must be explictly
471 * passed in as options to change their value. On a new
472 * mount, default values will be computed for all args.
473 */
474 args.flags = VNOVAL;
475 args.hfs_uid = (uid_t)VNOVAL;
476 args.hfs_gid = (gid_t)VNOVAL;
477 args.hfs_mask = (mode_t)VNOVAL;
478 args.hfs_encoding = (u_int32_t)VNOVAL;
479
480 optind = optreset = 1; /* Reset for parse of new argv. */
481 while ((ch = getopt(argc, argv, "xu:g:m:e:o:wt:jc")) != EOF) {
482 switch (ch) {
483 case 't': {
484 char *ptr;
485 unsigned long tbufsize = strtoul(optarg, &ptr, 0);
486 if (tbufsize >= UINT_MAX) {
487 tbufsize = UINT_MAX;
488 }
489 args.journal_tbuffer_size = (unsigned int) strtoul(optarg, &ptr, 0);
490 if ((args.journal_tbuffer_size == 0 ||
491 ((uint32_t) args.journal_tbuffer_size) == UINT_MAX) && errno != 0) {
492 fprintf(stderr, "%s: Invalid tbuffer size %s\n", argv[0], optarg);
493 exit(5);
494 } else {
495 if (*ptr == 'k')
496 args.journal_tbuffer_size *= 1024;
497 else if (*ptr == 'm')
498 args.journal_tbuffer_size *= 1024*1024;
499 }
500 if (args.flags == VNOVAL){
501 args.flags = 0;
502 }
503 args.flags |= HFSFSMNT_EXTENDED_ARGS;
504 break;
505 }
506 case 'j':
507 /* disable the journal */
508 if(args.flags == VNOVAL){
509 args.flags = 0;
510 }
511 args.flags |= HFSFSMNT_EXTENDED_ARGS;
512 args.journal_disable = 1;
513 break;
514 case 'c':
515 // XXXdbg JOURNAL_NO_GROUP_COMMIT == 0x0001
516 args.journal_flags = 0x0001;
517 break;
518 case 'x':
519 if (args.flags == VNOVAL)
520 args.flags = 0;
521 args.flags |= HFSFSMNT_NOXONFILES;
522 break;
523 case 'u':
524 args.hfs_uid = a_uid(optarg);
525 break;
526 case 'g':
527 args.hfs_gid = a_gid(optarg);
528 break;
529 case 'm':
530 args.hfs_mask = a_mask(optarg);
531 break;
532 case 'e':
533 encp = a_encoding(optarg);
534 break;
535 case 'o':
536 {
537 int dummy;
538 getmntopts(optarg, mopts, &mntflags, &dummy);
539 }
540 break;
541 case 'w':
542 if (args.flags == VNOVAL)
543 args.flags = 0;
544 args.flags |= HFSFSMNT_WRAPPER;
545 wrapper_requested = 1;
546 break;
547 case '?':
548 usage();
549 break;
550 default:
551 #if DEBUG
552 printf("mount_hfs: ERROR: unrecognized ch = '%c'\n", ch);
553 #endif
554 usage();
555 }; /* switch */
556 }
557
558 if ((mntflags & MNT_IGNORE_OWNERSHIP) && !(mntflags & MNT_UPDATE)) {
559 /*
560 * The defaults to be supplied in lieu of the on-disk permissions
561 * (could be overridden by explicit -u, -g, or -m options):
562 */
563 if (args.hfs_uid == (uid_t)VNOVAL) args.hfs_uid = UNKNOWNUID;
564 if (args.hfs_gid == (gid_t)VNOVAL) args.hfs_gid = UNKNOWNGID;
565 #if OVERRIDE_UNKNOWN_PERMISSIONS
566 if (args.hfs_mask == (mode_t)VNOVAL) args.hfs_mask = ACCESSPERMS; /* 0777 */
567 #endif
568 }
569 argc -= optind;
570 argv += optind;
571
572 if (argc != 2) {
573 #if DEBUG
574 printf("mount_hfs: ERROR: argc == %d != 2\n", argc);
575 #endif
576 usage();
577 }
578
579 dev = argv[0];
580
581 if (realpath(argv[1], dir) == NULL)
582 err(1, "realpath %s", dir);
583
584 args.fspec = dev;
585
586 /* HFS volumes need timezone info to convert local to GMT */
587 (void) gettimeofday( &dummy_timeval, &args.hfs_timezone );
588
589 /* load requested encoding (if any) for hfs volume */
590 if (encp != NULL) {
591 if (load_encoding(encp) != 0)
592 exit(1); /* load failure */
593 args.hfs_encoding = encp->encoding_id;
594 }
595
596 /*
597 * For a new mount (non-update case) fill in default values for all args
598 */
599 if ((mntflags & MNT_UPDATE) == 0) {
600
601 struct stat sb;
602
603 if (args.flags == VNOVAL)
604 args.flags = 0;
605
606 if ((args.hfs_encoding == (u_int32_t)VNOVAL) && (encp == NULL)) {
607 int encoding;
608
609 /* Find a suitable encoding preference. */
610 if ((encoding = get_encoding_pref(dev)) != -1) {
611 /*
612 * Note: the encoding kext was loaded by
613 * hfs.util during the file system probe.
614 */
615 args.hfs_encoding = encoding;
616 } else {
617 args.hfs_encoding = 0;
618 }
619 }
620 /* when the mountpoint is root, use default values */
621 if (strcmp(dir, "/") == 0) {
622 sb.st_mode = 0777;
623 sb.st_uid = 0;
624 sb.st_gid = 0;
625
626 /* otherwise inherit from the mountpoint */
627 } else if (stat(dir, &sb) == -1)
628 err(1, "stat %s", dir);
629
630 if (args.hfs_uid == (uid_t)VNOVAL)
631 args.hfs_uid = sb.st_uid;
632
633 if (args.hfs_gid == (gid_t)VNOVAL)
634 args.hfs_gid = sb.st_gid;
635
636 if (args.hfs_mask == (mode_t)VNOVAL)
637 args.hfs_mask = sb.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO);
638 }
639
640 #if DEBUG
641 printf("mount_hfs: calling mount: \n" );
642 printf("\tdevice = %s\n", dev);
643 printf("\tmount point = %s\n", dir);
644 printf("\tmount flags = 0x%08x\n", mntflags);
645 printf("\targ flags = 0x%x\n", args.flags);
646 printf("\tuid = %d\n", args.hfs_uid);
647 printf("\tgid = %d\n", args.hfs_gid);
648 printf("\tmode = %o\n", args.hfs_mask);
649 printf("\tencoding = %ld\n", args.hfs_encoding);
650
651 #endif
652
653 #if TARGET_OS_OSX
654 /*
655 * We shouldn't really be calling up to other layers, but
656 * an exception was made in this case to fix the situation
657 * where HFS was writable on optical media.
658 */
659
660 if ((_optical_is_writable(dev) & _OPTICAL_WRITABLE_PACKET)) {
661 mntflags |= MNT_RDONLY;
662 }
663 #endif
664
665 if (is_hfs_std)
666 mntflags |= MNT_RDONLY;
667
668 if ((mntflags & MNT_RDONLY) == 0) {
669 /*
670 * get the volume's create date so we can synchronize
671 * it with the root directory create date
672 */
673 localCreateTime = getVolumeCreateDate(dev);
674 }
675 else {
676 localCreateTime = 0;
677 }
678
679 if ((mountStatus = mount(HFS_MOUNT_TYPE, dir, mntflags, &args)) < 0) {
680 printf("mount_hfs: error on mount(): error = %d.\n", mountStatus);
681 err(1, NULL);
682 };
683
684 /*
685 * synchronize the root directory's create date
686 * with the volume's create date
687 */
688 if (localCreateTime)
689 syncCreateDate(dir, localCreateTime);
690
691
692 exit(0);
693 }
694
695
696 gid_t
697 a_gid(s)
698 char *s;
699 {
700 struct group *gr;
701 char *gname, *orig = s;
702 gid_t gid = 0;
703
704 if (*s == '-')
705 s++;
706 for (gname = s; *s && isdigit(*s); ++s);
707 if (!*s) {
708 gid = atoi(gname);
709 } else {
710 gr = getgrnam(orig);
711 if (gr == NULL)
712 errx(1, "unknown group id: %s", orig);
713 gid = gr->gr_gid;
714 }
715 return (gid);
716 }
717
718 uid_t
719 a_uid(s)
720 char *s;
721 {
722 struct passwd *pw;
723 char *uname, *orig = s;
724 uid_t uid = 0;
725
726 if (*s == '-')
727 s++;
728 for (uname = s; *s && isdigit(*s); ++s);
729 if (!*s) {
730 uid = atoi(uname);
731 } else {
732 pw = getpwnam(orig);
733 if (pw == NULL)
734 errx(1, "unknown user id: %s", orig);
735 uid = pw->pw_uid;
736 }
737 return (uid);
738 }
739
740 mode_t
741 a_mask(s)
742 char *s;
743 {
744 int done, rv;
745 char *ep;
746
747 done = 0;
748 rv = -1;
749 if (*s >= '0' && *s <= '7') {
750 done = 1;
751 rv = strtol(optarg, &ep, 8);
752 }
753 if (!done || rv < 0 || *ep)
754 errx(1, "invalid file mode: %s", s);
755 return (rv);
756 }
757
758 struct hfs_mnt_encoding *
759 a_encoding(s)
760 char *s;
761 {
762 char *uname;
763 int i;
764 u_int32_t encoding;
765 struct hfs_mnt_encoding *p, *q, *enclist;
766 int elements = sizeof(hfs_mnt_encodinglist) / sizeof(struct hfs_mnt_encoding);
767 int compare;
768
769 /* Use a binary search to find an encoding match */
770 p = hfs_mnt_encodinglist;
771 q = p + (elements - 1);
772 while (p <= q) {
773 enclist = p + ((q - p) >> 1); /* divide by 2 */
774 compare = strcmp(s, enclist->encoding_name);
775 if (compare < 0)
776 q = enclist - 1;
777 else if (compare > 0)
778 p = enclist + 1;
779 else
780 return (enclist);
781 }
782
783 for (uname = s; *s && isdigit(*s); ++s);
784
785 if (*s) goto unknown;
786
787 encoding = atoi(uname);
788 for (i=0, enclist = hfs_mnt_encodinglist; i < elements; i++, enclist++) {
789 if (enclist->encoding_id == encoding)
790 return (enclist);
791 }
792
793 unknown:
794 errx(1, "unknown encoding: %s", uname);
795 return (NULL);
796 }
797
798
799 /*
800 * Get file system's encoding preference.
801 */
802 int
803 get_encoding_pref(const char *device)
804 {
805 struct hfs_mnt_encoding *enclist;
806 HFSMasterDirectoryBlock * mdbp;
807 int encoding = -1;
808 int elements;
809 int i;
810
811 mdbp = GetMasterBlock(device);
812 if (mdbp == NULL)
813 return 0;
814
815 if (SWAP_BE16(mdbp->drSigWord) != kHFSSigWord ||
816 (SWAP_BE16(mdbp->drEmbedSigWord) == kHFSPlusSigWord && (!wrapper_requested))) {
817 return (-1);
818 }
819 else {
820 is_hfs_std = 1;
821 }
822 encoding = GET_HFS_TEXT_ENCODING(SWAP_BE32(mdbp->drFndrInfo[4]));
823
824 if (encoding == -1) {
825 encoding = get_encoding_bias();
826 if (encoding == 0 || encoding == -1)
827 encoding = get_default_encoding();
828 }
829
830 /* Check if this is a supported encoding. */
831 elements = sizeof(hfs_mnt_encodinglist) / sizeof(struct hfs_mnt_encoding);
832 for (i=0, enclist = hfs_mnt_encodinglist; i < elements; i++, enclist++) {
833 if (enclist->encoding_id == encoding)
834 return (encoding);
835 }
836
837 return (0);
838 }
839
840 /*
841 * Get kernel's encoding bias.
842 */
843 int
844 get_encoding_bias()
845 {
846 int mib[3];
847 size_t buflen = sizeof(int);
848 struct vfsconf vfc;
849 int hint = 0;
850
851 if (getvfsbyname("hfs", &vfc) < 0)
852 goto error;
853
854 mib[0] = CTL_VFS;
855 mib[1] = vfc.vfc_typenum;
856 mib[2] = HFS_ENCODINGBIAS;
857
858 if (sysctl(mib, 3, &hint, &buflen, NULL, 0) < 0)
859 goto error;
860 return (hint);
861 error:
862 return (-1);
863 }
864
865 #define __kCFUserEncodingFileName ("/.CFUserTextEncoding")
866
867 unsigned int
868 get_default_encoding()
869 {
870 struct passwd *passwdp;
871
872 if ((passwdp = getpwuid(0))) { /* root account */
873 char buffer[MAXPATHLEN + 1];
874 int fd;
875
876 strlcpy(buffer, passwdp->pw_dir, sizeof(buffer));
877 strlcat(buffer, __kCFUserEncodingFileName, sizeof(buffer));
878
879 if ((fd = open(buffer, O_RDONLY, 0)) > 0) {
880 ssize_t readSize;
881
882 readSize = read(fd, buffer, MAXPATHLEN);
883 buffer[(readSize < 0 ? 0 : readSize)] = '\0';
884 close(fd);
885 return strtol(buffer, NULL, 0);
886 }
887 }
888 return (0); /* Fallback to smRoman */
889 }
890
891
892 void
893 usage()
894 {
895 (void)fprintf(stderr,
896 "usage: mount_hfs [-xw] [-u user] [-g group] [-m mask] [-e encoding] [-t tbuffer-size] [-j] [-c] [-o options] special-device filesystem-node\n");
897
898 exit(1);
899 }