2 * Copyright (c) 1999-2013 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
24 #include <sys/types.h>
27 #include <sys/mount.h>
29 #include <sys/sysctl.h>
32 #include <sys/vnode.h>
34 #include <sys/ioctl.h>
50 #include <hfs/hfs_mount.h>
51 #include <hfs/hfs_format.h>
53 #include <TargetConditionals.h>
55 /* Sensible wrappers over the byte-swapping routines */
56 #include "hfs_endian.h"
58 #include "optical.h" //only include optical headers on Macs
65 * Replay the journal. We don't care if there are problems.
68 replay_journal(const char *device
)
74 fd
= open(device
, O_RDWR
);
76 warn("Could not open block device %s for writing", device
);
79 if (getvfsbyname("hfs", &vfc
) != 0) {
80 warn("Could not get hfs vfs information");
84 mib
[1] = vfc
.vfc_typenum
;
85 mib
[2] = HFS_REPLAY_JOURNAL
;
87 (void)sysctl(mib
, 4, NULL
, NULL
, NULL
, 0);
95 struct mntopt mopts
[] = {
97 MOPT_IGNORE_OWNERSHIP
,
103 #define HFS_MOUNT_TYPE "hfs"
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);
113 void usage
__P((void));
117 int wrapper_requested
= 0;
119 typedef struct CreateDateAttrBuf
{
121 struct timespec creationTime
;
124 #define HFS_BLOCK_SIZE 512
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)))
131 #define MAC_GMT_FACTOR 2082844800UL
133 #define KEXT_LOAD_COMMAND "/sbin/kextload"
135 #define ENCODING_MODULE_PATH "/System/Library/Filesystems/hfs.fs/Contents/Resources/Encodings/"
137 #define MXENCDNAMELEN 16 /* Maximun length of encoding name string */
139 struct hfs_mnt_encoding
{
140 char encoding_name
[MXENCDNAMELEN
]; /* encoding type name */
141 u_int32_t encoding_id
; /* encoding type number */
146 * Lookup table for hfs encoding names
147 * Note: Names must be in alphabetical order
149 struct hfs_mnt_encoding hfs_mnt_encodinglist
[] = {
155 { "CentralEurRoman", 29 },
156 { "ChineseSimp", 25 },
157 { "ChineseTrad", 2 },
178 { "Roman", 0 }, /* default */
186 { "Ukrainian", 152 },
187 { "Vietnamese", 30 },
192 If path is a path to a block device, then return a path to the
193 corresponding raw device. Else return path unchanged.
195 const char *rawdevice(const char *path
)
197 const char *devdisk
= "/dev/disk";
198 static char raw
[MAXPATHLEN
];
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);
204 /* error in building string. return original. */
208 if ((unsigned long) sn_len
< sizeof(raw
)) {
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
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.
230 device Path name to disk device (eg., "/dev/disk0s2")
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.
238 On error, this routine returns NULL.
240 void *GetMasterBlock(const char *device
)
242 static char *masterBlock
= NULL
;
251 * If we already read the master block, then just return it.
253 if (masterBlock
!= NULL
) {
257 device
= rawdevice(device
);
259 fd
= open(device
, O_RDONLY
| O_NDELAY
, 0);
261 fprintf(stderr
, "GetMasterBlock: Error %d opening %s\n", errno
, device
);
266 * Get the block size so we can read an entire block.
268 err
= ioctl(fd
, DKIOCGETBLOCKSIZE
, &blockSize
);
270 fprintf(stderr
, "GetMasterBlock: Error %d getting block size\n", errno
);
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).
281 offset
= blockSize
> 1024 ? 0 : 1024;
284 * Allocate a buffer and read the block.
286 buf
= malloc(blockSize
);
288 fprintf(stderr
, "GetMasterBlock: Could not malloc %u bytes\n", blockSize
);
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
);
298 * Point at the part of the buffer containing the master block.
299 * Then return that pointer.
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
306 masterBlock
= buf
+ 1024 - offset
;
307 buf
= NULL
; /* Don't free memory that masterBlock points into. */
318 u_int32_t
getVolumeCreateDate(const char *device
)
320 HFSMasterDirectoryBlock
* mdbPtr
;
321 u_int32_t volume_create_time
= 0;
323 mdbPtr
= GetMasterBlock(device
);
324 if (mdbPtr
== NULL
) goto exit
;
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
))) {
330 volume_create_time
= SWAP_BE32 (mdbPtr
->drCrDate
);
332 } else if (mdbPtr
->drSigWord
== kHFSPlusSigWord
) {
333 HFSPlusVolumeHeader
* volHdrPtr
= (HFSPlusVolumeHeader
*) mdbPtr
;
335 volume_create_time
= SWAP_BE32 (volHdrPtr
->createDate
);
337 goto exit
; /* cound not match signature */
340 if (volume_create_time
> MAC_GMT_FACTOR
)
341 volume_create_time
-= MAC_GMT_FACTOR
;
343 volume_create_time
= 0; /* don't let date go negative! */
346 return volume_create_time
;
349 void syncCreateDate(const char *mntpt
, u_int32_t localCreateTime
)
353 struct attrlist attributes
;
354 CreateDateAttrBuf attrReturnBuffer
;
355 int64_t gmtCreateTime
;
357 int32_t newCreateTime
;
359 snprintf(path
, sizeof(path
), "%s/", mntpt
);
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;
369 result
= getattrlist(path
, &attributes
, &attrReturnBuffer
, sizeof(attrReturnBuffer
), 0 );
372 gmtCreateTime
= attrReturnBuffer
.creationTime
.tv_sec
;
373 gmtOffset
= gmtCreateTime
- (int64_t) localCreateTime
+ 900;
375 gmtOffset
= 1800 * (gmtOffset
/ 1800);
377 gmtOffset
= -1800 * ((-gmtOffset
+ 1799) / 1800);
380 newCreateTime
= localCreateTime
+ gmtOffset
;
383 * if the root directory's create date doesn't match
384 * and its within +/- 15 seconds, then update it
386 if ((newCreateTime
!= attrReturnBuffer
.creationTime
.tv_sec
) &&
387 (( newCreateTime
- attrReturnBuffer
.creationTime
.tv_sec
) > -15) &&
388 ((newCreateTime
- attrReturnBuffer
.creationTime
.tv_sec
) < 15)) {
390 attrReturnBuffer
.creationTime
.tv_sec
= (time_t) newCreateTime
;
391 (void) setattrlist (path
,
393 &attrReturnBuffer
.creationTime
,
394 sizeof(attrReturnBuffer
.creationTime
),
401 * loads an hfs encoding converter module into the kernel
403 * Note: unloading of encoding converter modules is done in the kernel
406 load_encoding(struct hfs_mnt_encoding
*encp
)
412 char kmodfile
[MAXPATHLEN
];
414 /* MacRoman encoding (0) is built into the kernel */
415 if (encp
->encoding_id
== 0)
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
);
427 (void) execl(KEXT_LOAD_COMMAND
, KEXT_LOAD_COMMAND
, kmodfile
, NULL
);
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 */
438 fprintf(stderr
, "unable to load: %s\n", kmodfile
);
449 struct hfs_mount_args args
;
451 char *dev
, dir
[MAXPATHLEN
];
453 struct timeval dummy_timeval
; /* gettimeofday() crashes if the first argument is NULL */
454 u_int32_t localCreateTime
;
455 struct hfs_mnt_encoding
*encp
;
458 int tmp_mntflags
= 0;
461 mntflags
= MNT_NOATIME
;
462 #else // !TARGET_OS_IPHONE
464 #endif // TARGET_OS_IPHONE
467 (void)memset(&args
, '\0', sizeof(struct hfs_mount_args
));
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.
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
;
480 optind
= optreset
= 1; /* Reset for parse of new argv. */
481 while ((ch
= getopt(argc
, argv
, "xu:g:m:e:o:wt:jc")) != EOF
) {
485 unsigned long tbufsize
= strtoul(optarg
, &ptr
, 0);
486 if (tbufsize
>= UINT_MAX
) {
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
);
496 args
.journal_tbuffer_size
*= 1024;
497 else if (*ptr
== 'm')
498 args
.journal_tbuffer_size
*= 1024*1024;
500 if (args
.flags
== VNOVAL
){
503 args
.flags
|= HFSFSMNT_EXTENDED_ARGS
;
507 /* disable the journal */
508 if(args
.flags
== VNOVAL
){
511 args
.flags
|= HFSFSMNT_EXTENDED_ARGS
;
512 args
.journal_disable
= 1;
515 // XXXdbg JOURNAL_NO_GROUP_COMMIT == 0x0001
516 args
.journal_flags
= 0x0001;
519 if (args
.flags
== VNOVAL
)
521 args
.flags
|= HFSFSMNT_NOXONFILES
;
524 args
.hfs_uid
= a_uid(optarg
);
527 args
.hfs_gid
= a_gid(optarg
);
530 args
.hfs_mask
= a_mask(optarg
);
533 encp
= a_encoding(optarg
);
538 getmntopts(optarg
, mopts
, &mntflags
, &dummy
);
542 if (args
.flags
== VNOVAL
)
544 args
.flags
|= HFSFSMNT_WRAPPER
;
545 wrapper_requested
= 1;
552 printf("mount_hfs: ERROR: unrecognized ch = '%c'\n", ch
);
558 if ((mntflags
& MNT_IGNORE_OWNERSHIP
) && !(mntflags
& MNT_UPDATE
)) {
560 * The defaults to be supplied in lieu of the on-disk permissions
561 * (could be overridden by explicit -u, -g, or -m options):
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 */
574 printf("mount_hfs: ERROR: argc == %d != 2\n", argc
);
581 if (realpath(argv
[1], dir
) == NULL
)
582 err(1, "realpath %s", dir
);
586 /* HFS volumes need timezone info to convert local to GMT */
587 (void) gettimeofday( &dummy_timeval
, &args
.hfs_timezone
);
589 /* load requested encoding (if any) for hfs volume */
591 if (load_encoding(encp
) != 0)
592 exit(1); /* load failure */
593 args
.hfs_encoding
= encp
->encoding_id
;
597 * For a new mount (non-update case) fill in default values for all args
599 if ((mntflags
& MNT_UPDATE
) == 0) {
603 if (args
.flags
== VNOVAL
)
606 if ((args
.hfs_encoding
== (u_int32_t
)VNOVAL
) && (encp
== NULL
)) {
609 /* Find a suitable encoding preference. */
610 if ((encoding
= get_encoding_pref(dev
)) != -1) {
612 * Note: the encoding kext was loaded by
613 * hfs.util during the file system probe.
615 args
.hfs_encoding
= encoding
;
617 args
.hfs_encoding
= 0;
620 /* when the mountpoint is root, use default values */
621 if (strcmp(dir
, "/") == 0) {
626 /* otherwise inherit from the mountpoint */
627 } else if (stat(dir
, &sb
) == -1)
628 err(1, "stat %s", dir
);
630 if (args
.hfs_uid
== (uid_t
)VNOVAL
)
631 args
.hfs_uid
= sb
.st_uid
;
633 if (args
.hfs_gid
== (gid_t
)VNOVAL
)
634 args
.hfs_gid
= sb
.st_gid
;
636 if (args
.hfs_mask
== (mode_t
)VNOVAL
)
637 args
.hfs_mask
= sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
);
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
);
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.
660 if ((_optical_is_writable(dev
) & _OPTICAL_WRITABLE_PACKET
)) {
661 mntflags
|= MNT_RDONLY
;
666 mntflags
|= MNT_RDONLY
;
668 if ((mntflags
& MNT_RDONLY
) == 0) {
670 * get the volume's create date so we can synchronize
671 * it with the root directory create date
673 localCreateTime
= getVolumeCreateDate(dev
);
679 if ((mountStatus
= mount(HFS_MOUNT_TYPE
, dir
, mntflags
, &args
)) < 0) {
680 printf("mount_hfs: error on mount(): error = %d.\n", mountStatus
);
685 * synchronize the root directory's create date
686 * with the volume's create date
689 syncCreateDate(dir
, localCreateTime
);
701 char *gname
, *orig
= s
;
706 for (gname
= s
; *s
&& isdigit(*s
); ++s
);
712 errx(1, "unknown group id: %s", orig
);
723 char *uname
, *orig
= s
;
728 for (uname
= s
; *s
&& isdigit(*s
); ++s
);
734 errx(1, "unknown user id: %s", orig
);
749 if (*s
>= '0' && *s
<= '7') {
751 rv
= strtol(optarg
, &ep
, 8);
753 if (!done
|| rv
< 0 || *ep
)
754 errx(1, "invalid file mode: %s", s
);
758 struct hfs_mnt_encoding
*
765 struct hfs_mnt_encoding
*p
, *q
, *enclist
;
766 int elements
= sizeof(hfs_mnt_encodinglist
) / sizeof(struct hfs_mnt_encoding
);
769 /* Use a binary search to find an encoding match */
770 p
= hfs_mnt_encodinglist
;
771 q
= p
+ (elements
- 1);
773 enclist
= p
+ ((q
- p
) >> 1); /* divide by 2 */
774 compare
= strcmp(s
, enclist
->encoding_name
);
777 else if (compare
> 0)
783 for (uname
= s
; *s
&& isdigit(*s
); ++s
);
785 if (*s
) goto unknown
;
787 encoding
= atoi(uname
);
788 for (i
=0, enclist
= hfs_mnt_encodinglist
; i
< elements
; i
++, enclist
++) {
789 if (enclist
->encoding_id
== encoding
)
794 errx(1, "unknown encoding: %s", uname
);
800 * Get file system's encoding preference.
803 get_encoding_pref(const char *device
)
805 struct hfs_mnt_encoding
*enclist
;
806 HFSMasterDirectoryBlock
* mdbp
;
811 mdbp
= GetMasterBlock(device
);
815 if (SWAP_BE16(mdbp
->drSigWord
) != kHFSSigWord
||
816 (SWAP_BE16(mdbp
->drEmbedSigWord
) == kHFSPlusSigWord
&& (!wrapper_requested
))) {
822 encoding
= GET_HFS_TEXT_ENCODING(SWAP_BE32(mdbp
->drFndrInfo
[4]));
824 if (encoding
== -1) {
825 encoding
= get_encoding_bias();
826 if (encoding
== 0 || encoding
== -1)
827 encoding
= get_default_encoding();
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
)
841 * Get kernel's encoding bias.
847 size_t buflen
= sizeof(int);
851 if (getvfsbyname("hfs", &vfc
) < 0)
855 mib
[1] = vfc
.vfc_typenum
;
856 mib
[2] = HFS_ENCODINGBIAS
;
858 if (sysctl(mib
, 3, &hint
, &buflen
, NULL
, 0) < 0)
865 #define __kCFUserEncodingFileName ("/.CFUserTextEncoding")
868 get_default_encoding()
870 struct passwd
*passwdp
;
872 if ((passwdp
= getpwuid(0))) { /* root account */
873 char buffer
[MAXPATHLEN
+ 1];
876 strlcpy(buffer
, passwdp
->pw_dir
, sizeof(buffer
));
877 strlcat(buffer
, __kCFUserEncodingFileName
, sizeof(buffer
));
879 if ((fd
= open(buffer
, O_RDONLY
, 0)) > 0) {
882 readSize
= read(fd
, buffer
, MAXPATHLEN
);
883 buffer
[(readSize
< 0 ? 0 : readSize
)] = '\0';
885 return strtol(buffer
, NULL
, 0);
888 return (0); /* Fallback to smRoman */
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");