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