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