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>
51 #include <hfs/hfs_mount.h>
52 #include <hfs/hfs_format.h>
54 #include <TargetConditionals.h>
56 /* Sensible wrappers over the byte-swapping routines */
57 #include "hfs_endian.h"
59 #include "optical.h" //only include optical headers on Macs
66 * Replay the journal. We don't care if there are problems.
69 replay_journal(const char *device
)
75 fd
= open(device
, O_RDWR
);
77 warn("Could not open block device %s for writing", device
);
80 if (getvfsbyname("hfs", &vfc
) != 0) {
81 warn("Could not get hfs vfs information");
85 mib
[1] = vfc
.vfc_typenum
;
86 mib
[2] = HFS_REPLAY_JOURNAL
;
88 (void)sysctl(mib
, 4, NULL
, NULL
, NULL
, 0);
96 struct mntopt mopts
[] = {
98 MOPT_IGNORE_OWNERSHIP
,
104 #define HFS_MOUNT_TYPE "hfs"
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);
114 void usage
__P((void));
118 int wrapper_requested
= 0;
120 typedef struct CreateDateAttrBuf
{
122 struct timespec creationTime
;
125 #define HFS_BLOCK_SIZE 512
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)))
132 #define MAC_GMT_FACTOR 2082844800UL
134 #define KEXT_LOAD_COMMAND "/sbin/kextload"
136 #define ENCODING_MODULE_PATH "/System/Library/Filesystems/hfs.fs/Contents/Resources/Encodings/"
138 #define MXENCDNAMELEN 16 /* Maximun length of encoding name string */
140 struct hfs_mnt_encoding
{
141 char encoding_name
[MXENCDNAMELEN
]; /* encoding type name */
142 u_int32_t encoding_id
; /* encoding type number */
147 * Lookup table for hfs encoding names
148 * Note: Names must be in alphabetical order
150 struct hfs_mnt_encoding hfs_mnt_encodinglist
[] = {
156 { "CentralEurRoman", 29 },
157 { "ChineseSimp", 25 },
158 { "ChineseTrad", 2 },
179 { "Roman", 0 }, /* default */
187 { "Ukrainian", 152 },
188 { "Vietnamese", 30 },
193 If path is a path to a block device, then return a path to the
194 corresponding raw device. Else return path unchanged.
196 const char *rawdevice(const char *path
)
198 const char *devdisk
= "/dev/disk";
199 static char raw
[MAXPATHLEN
];
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);
205 /* error in building string. return original. */
209 if ((unsigned long) sn_len
< sizeof(raw
)) {
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
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.
231 device Path name to disk device (eg., "/dev/disk0s2")
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.
239 On error, this routine returns NULL.
241 void *GetMasterBlock(const char *device
)
243 static char *masterBlock
= NULL
;
252 * If we already read the master block, then just return it.
254 if (masterBlock
!= NULL
) {
258 device
= rawdevice(device
);
260 fd
= open(device
, O_RDONLY
| O_NDELAY
, 0);
262 fprintf(stderr
, "GetMasterBlock: Error %d opening %s\n", errno
, device
);
267 * Get the block size so we can read an entire block.
269 err
= ioctl(fd
, DKIOCGETBLOCKSIZE
, &blockSize
);
271 fprintf(stderr
, "GetMasterBlock: Error %d getting block size\n", errno
);
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).
282 offset
= blockSize
> 1024 ? 0 : 1024;
285 * Allocate a buffer and read the block.
287 buf
= malloc(blockSize
);
289 fprintf(stderr
, "GetMasterBlock: Could not malloc %u bytes\n", blockSize
);
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
);
299 * Point at the part of the buffer containing the master block.
300 * Then return that pointer.
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
307 masterBlock
= buf
+ 1024 - offset
;
308 buf
= NULL
; /* Don't free memory that masterBlock points into. */
319 u_int32_t
getVolumeCreateDate(const char *device
)
321 HFSMasterDirectoryBlock
* mdbPtr
;
322 u_int32_t volume_create_time
= 0;
324 mdbPtr
= GetMasterBlock(device
);
325 if (mdbPtr
== NULL
) goto exit
;
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
))) {
331 volume_create_time
= SWAP_BE32 (mdbPtr
->drCrDate
);
333 } else if (mdbPtr
->drSigWord
== kHFSPlusSigWord
) {
334 HFSPlusVolumeHeader
* volHdrPtr
= (HFSPlusVolumeHeader
*) mdbPtr
;
336 volume_create_time
= SWAP_BE32 (volHdrPtr
->createDate
);
338 goto exit
; /* cound not match signature */
341 if (volume_create_time
> MAC_GMT_FACTOR
)
342 volume_create_time
-= MAC_GMT_FACTOR
;
344 volume_create_time
= 0; /* don't let date go negative! */
347 return volume_create_time
;
350 void syncCreateDate(const char *mntpt
, u_int32_t localCreateTime
)
354 struct attrlist attributes
;
355 CreateDateAttrBuf attrReturnBuffer
;
356 int64_t gmtCreateTime
;
358 int32_t newCreateTime
;
360 snprintf(path
, sizeof(path
), "%s/", mntpt
);
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;
370 result
= getattrlist(path
, &attributes
, &attrReturnBuffer
, sizeof(attrReturnBuffer
), 0 );
373 gmtCreateTime
= attrReturnBuffer
.creationTime
.tv_sec
;
374 gmtOffset
= (int32_t)(gmtCreateTime
- (int64_t) localCreateTime
+ 900);
376 gmtOffset
= 1800 * (gmtOffset
/ 1800);
378 gmtOffset
= -1800 * ((-gmtOffset
+ 1799) / 1800);
381 newCreateTime
= localCreateTime
+ gmtOffset
;
384 * if the root directory's create date doesn't match
385 * and its within +/- 15 seconds, then update it
387 if ((newCreateTime
!= attrReturnBuffer
.creationTime
.tv_sec
) &&
388 (( newCreateTime
- attrReturnBuffer
.creationTime
.tv_sec
) > -15) &&
389 ((newCreateTime
- attrReturnBuffer
.creationTime
.tv_sec
) < 15)) {
391 attrReturnBuffer
.creationTime
.tv_sec
= (time_t) newCreateTime
;
392 (void) setattrlist (path
,
394 &attrReturnBuffer
.creationTime
,
395 sizeof(attrReturnBuffer
.creationTime
),
402 * loads an hfs encoding converter module into the kernel
404 * Note: unloading of encoding converter modules is done in the kernel
407 load_encoding(struct hfs_mnt_encoding
*encp
)
413 char kmodfile
[MAXPATHLEN
];
415 /* MacRoman encoding (0) is built into the kernel */
416 if (encp
->encoding_id
== 0)
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
);
428 (void) execl(KEXT_LOAD_COMMAND
, KEXT_LOAD_COMMAND
, kmodfile
, NULL
);
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 */
439 fprintf(stderr
, "unable to load: %s\n", kmodfile
);
450 struct hfs_mount_args args
;
452 char *dev
, dir
[MAXPATHLEN
];
454 struct timeval dummy_timeval
; /* gettimeofday() crashes if the first argument is NULL */
455 u_int32_t localCreateTime
;
456 struct hfs_mnt_encoding
*encp
;
459 int tmp_mntflags
= 0;
462 mntflags
= MNT_NOATIME
;
463 #else // !TARGET_OS_IPHONE
465 #endif // TARGET_OS_IPHONE
468 (void)memset(&args
, '\0', sizeof(struct hfs_mount_args
));
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.
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
;
481 optind
= optreset
= 1; /* Reset for parse of new argv. */
482 while ((ch
= getopt(argc
, argv
, "xu:g:m:e:o:wt:jc")) != EOF
) {
486 unsigned long tbufsize
= strtoul(optarg
, &ptr
, 0);
487 if (tbufsize
>= UINT_MAX
) {
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
);
497 args
.journal_tbuffer_size
*= 1024;
498 else if (*ptr
== 'm')
499 args
.journal_tbuffer_size
*= 1024*1024;
501 if (args
.flags
== VNOVAL
){
504 args
.flags
|= HFSFSMNT_EXTENDED_ARGS
;
508 /* disable the journal */
509 if(args
.flags
== VNOVAL
){
512 args
.flags
|= HFSFSMNT_EXTENDED_ARGS
;
513 args
.journal_disable
= 1;
516 // XXXdbg JOURNAL_NO_GROUP_COMMIT == 0x0001
517 args
.journal_flags
= 0x0001;
520 if (args
.flags
== VNOVAL
)
522 args
.flags
|= HFSFSMNT_NOXONFILES
;
525 args
.hfs_uid
= a_uid(optarg
);
528 args
.hfs_gid
= a_gid(optarg
);
531 args
.hfs_mask
= a_mask(optarg
);
534 encp
= a_encoding(optarg
);
539 getmntopts(optarg
, mopts
, &mntflags
, &dummy
);
543 if (args
.flags
== VNOVAL
)
545 args
.flags
|= HFSFSMNT_WRAPPER
;
546 wrapper_requested
= 1;
553 printf("mount_hfs: ERROR: unrecognized ch = '%c'\n", ch
);
559 if ((mntflags
& MNT_IGNORE_OWNERSHIP
) && !(mntflags
& MNT_UPDATE
)) {
561 * The defaults to be supplied in lieu of the on-disk permissions
562 * (could be overridden by explicit -u, -g, or -m options):
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 */
575 printf("mount_hfs: ERROR: argc == %d != 2\n", argc
);
582 if (realpath(argv
[1], dir
) == NULL
)
583 err(1, "realpath %s", dir
);
587 /* HFS volumes need timezone info to convert local to GMT */
588 (void) gettimeofday( &dummy_timeval
, &args
.hfs_timezone
);
590 /* load requested encoding (if any) for hfs volume */
592 if (load_encoding(encp
) != 0)
593 exit(1); /* load failure */
594 args
.hfs_encoding
= encp
->encoding_id
;
598 * For a new mount (non-update case) fill in default values for all args
600 if ((mntflags
& MNT_UPDATE
) == 0) {
604 if (args
.flags
== VNOVAL
)
607 if ((args
.hfs_encoding
== (u_int32_t
)VNOVAL
) && (encp
== NULL
)) {
610 /* Find a suitable encoding preference. */
611 if ((encoding
= get_encoding_pref(dev
)) != -1) {
613 * Note: the encoding kext was loaded by
614 * hfs.util during the file system probe.
616 args
.hfs_encoding
= encoding
;
618 args
.hfs_encoding
= 0;
621 /* when the mountpoint is root, use default values */
622 if (strcmp(dir
, "/") == 0) {
627 /* otherwise inherit from the mountpoint */
628 } else if (stat(dir
, &sb
) == -1)
629 err(1, "stat %s", dir
);
631 if (args
.hfs_uid
== (uid_t
)VNOVAL
)
632 args
.hfs_uid
= sb
.st_uid
;
634 if (args
.hfs_gid
== (gid_t
)VNOVAL
)
635 args
.hfs_gid
= sb
.st_gid
;
637 if (args
.hfs_mask
== (mode_t
)VNOVAL
)
638 args
.hfs_mask
= sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
);
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
);
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.
661 if ((_optical_is_writable(dev
) & _OPTICAL_WRITABLE_PACKET
)) {
662 mntflags
|= MNT_RDONLY
;
667 mntflags
|= MNT_RDONLY
;
669 if ((mntflags
& MNT_RDONLY
) == 0) {
671 * get the volume's create date so we can synchronize
672 * it with the root directory create date
674 localCreateTime
= getVolumeCreateDate(dev
);
680 if ((mountStatus
= mount(HFS_MOUNT_TYPE
, dir
, mntflags
, &args
)) < 0) {
681 printf("mount_hfs: error on mount(): error = %d.\n", mountStatus
);
686 * synchronize the root directory's create date
687 * with the volume's create date
690 syncCreateDate(dir
, localCreateTime
);
702 char *gname
, *orig
= s
;
707 for (gname
= s
; *s
&& isdigit(*s
); ++s
);
713 errx(1, "unknown group id: %s", orig
);
724 char *uname
, *orig
= s
;
729 for (uname
= s
; *s
&& isdigit(*s
); ++s
);
735 errx(1, "unknown user id: %s", orig
);
750 if (*s
>= '0' && *s
<= '7') {
754 mask
= strtol(optarg
, &ep
, 8);
755 if (mask
>= 0 && mask
<= INT_MAX
)
758 if (!done
|| rv
< 0 || *ep
)
759 errx(1, "invalid file mode: %s", s
);
763 struct hfs_mnt_encoding
*
770 struct hfs_mnt_encoding
*p
, *q
, *enclist
;
771 int elements
= sizeof(hfs_mnt_encodinglist
) / sizeof(struct hfs_mnt_encoding
);
774 /* Use a binary search to find an encoding match */
775 p
= hfs_mnt_encodinglist
;
776 q
= p
+ (elements
- 1);
778 enclist
= p
+ ((q
- p
) >> 1); /* divide by 2 */
779 compare
= strcmp(s
, enclist
->encoding_name
);
782 else if (compare
> 0)
788 for (uname
= s
; *s
&& isdigit(*s
); ++s
);
790 if (*s
) goto unknown
;
792 encoding
= atoi(uname
);
793 for (i
=0, enclist
= hfs_mnt_encodinglist
; i
< elements
; i
++, enclist
++) {
794 if (enclist
->encoding_id
== encoding
)
799 errx(1, "unknown encoding: %s", uname
);
805 * Get file system's encoding preference.
808 get_encoding_pref(const char *device
)
810 struct hfs_mnt_encoding
*enclist
;
811 HFSMasterDirectoryBlock
* mdbp
;
816 mdbp
= GetMasterBlock(device
);
820 if (SWAP_BE16(mdbp
->drSigWord
) != kHFSSigWord
||
821 (SWAP_BE16(mdbp
->drEmbedSigWord
) == kHFSPlusSigWord
&& (!wrapper_requested
))) {
827 encoding
= GET_HFS_TEXT_ENCODING(SWAP_BE32(mdbp
->drFndrInfo
[4]));
829 if (encoding
== -1) {
830 encoding
= get_encoding_bias();
831 if (encoding
== 0 || encoding
== -1)
832 encoding
= get_default_encoding();
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
)
846 * Get kernel's encoding bias.
852 size_t buflen
= sizeof(int);
856 if (getvfsbyname("hfs", &vfc
) < 0)
860 mib
[1] = vfc
.vfc_typenum
;
861 mib
[2] = HFS_ENCODINGBIAS
;
863 if (sysctl(mib
, 3, &hint
, &buflen
, NULL
, 0) < 0)
870 #define __kCFUserEncodingFileName ("/.CFUserTextEncoding")
873 get_default_encoding()
875 struct passwd
*passwdp
;
877 if ((passwdp
= getpwuid(0))) { /* root account */
878 char buffer
[MAXPATHLEN
+ 1];
881 strlcpy(buffer
, passwdp
->pw_dir
, sizeof(buffer
));
882 strlcat(buffer
, __kCFUserEncodingFileName
, sizeof(buffer
));
884 if ((fd
= open(buffer
, O_RDONLY
, 0)) > 0) {
888 readSize
= read(fd
, buffer
, MAXPATHLEN
);
889 buffer
[(readSize
< 0 ? 0 : readSize
)] = '\0';
891 encoding
= strtol(buffer
, NULL
, 0);
892 assert(encoding
> -1 && encoding
<= UINT_MAX
);
893 return (unsigned int)encoding
;
896 return (0); /* Fallback to smRoman */
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");