2 * Copyright (c) 2006 Apple Computer, Inc. All Rights Reserved.
4 * @APPLE_LICENSE_OSREFERENCE_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. The rights granted to you under the
10 * License may not be used to create, or enable the creation or
11 * redistribution of, unlawful or unlicensed copies of an Apple operating
12 * system, or to circumvent, violate, or enable the circumvention or
13 * violation of, any terms of an Apple operating system software license
16 * Please obtain a copy of the License at
17 * http://www.opensource.apple.com/apsl/ and read it before using this
20 * The Original Code and all software distributed under the License are
21 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
22 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
23 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
24 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
25 * Please see the License for the specific language governing rights and
26 * limitations under the License.
28 * @APPLE_LICENSE_OSREFERENCE_HEADER_END@
30 /* @(#)hfs_vfsutils.c 4.0
32 * (c) 1997-2002 Apple Computer, Inc. All Rights Reserved
34 * hfs_vfsutils.c -- Routines that go between the HFS layer and the VFS.
37 #include <sys/param.h>
38 #include <sys/systm.h>
39 #include <sys/kernel.h>
40 #include <sys/malloc.h>
42 #include <sys/mount.h>
45 #include <sys/unistd.h>
46 #include <sys/utfconv.h>
47 #include <sys/kauth.h>
50 #include "hfs_catalog.h"
52 #include "hfs_mount.h"
53 #include "hfs_endian.h"
54 #include "hfs_cnode.h"
56 #include "hfscommon/headers/FileMgrInternal.h"
57 #include "hfscommon/headers/BTreesInternal.h"
58 #include "hfscommon/headers/HFSUnicodeWrappers.h"
61 extern int count_lock_queue(void);
64 static void ReleaseMetaFileVNode(struct vnode
*vp
);
65 static int hfs_late_journal_init(struct hfsmount
*hfsmp
, HFSPlusVolumeHeader
*vhp
, void *_args
);
67 static void hfs_metadatazone_init(struct hfsmount
*);
68 static u_int32_t
hfs_hotfile_freeblocks(struct hfsmount
*);
72 u_int32_t
GetLogicalBlockSize(struct vnode
*vp
);
74 extern int hfs_attrkeycompare(HFSPlusAttrKey
*searchKey
, HFSPlusAttrKey
*trialKey
);
77 //*******************************************************************************
78 // Note: Finder information in the HFS/HFS+ metadata are considered opaque and
79 // hence are not in the right byte order on little endian machines. It is
80 // the responsibility of the finder and other clients to swap the data.
81 //*******************************************************************************
83 //*******************************************************************************
84 // Routine: hfs_MountHFSVolume
87 //*******************************************************************************
88 char hfs_catname
[] = "Catalog B-tree";
89 char hfs_extname
[] = "Extents B-tree";
90 char hfs_vbmname
[] = "Volume Bitmap";
91 char hfs_attrname
[] = "Attribute B-tree";
93 char hfs_privdirname
[] =
94 "\xE2\x90\x80\xE2\x90\x80\xE2\x90\x80\xE2\x90\x80HFS+ Private Data";
97 OSErr
hfs_MountHFSVolume(struct hfsmount
*hfsmp
, HFSMasterDirectoryBlock
*mdb
,
100 ExtendedVCB
*vcb
= HFSTOVCB(hfsmp
);
103 struct cat_desc cndesc
;
104 struct cat_attr cnattr
;
105 struct cat_fork fork
;
107 /* Block size must be a multiple of 512 */
108 if (SWAP_BE32(mdb
->drAlBlkSiz
) == 0 ||
109 (SWAP_BE32(mdb
->drAlBlkSiz
) & 0x01FF) != 0)
112 /* don't mount a writeable volume if its dirty, it must be cleaned by fsck_hfs */
113 if (((hfsmp
->hfs_flags
& HFS_READ_ONLY
) == 0) &&
114 ((SWAP_BE16(mdb
->drAtrb
) & kHFSVolumeUnmountedMask
) == 0)) {
117 hfsmp
->hfs_flags
|= HFS_STANDARD
;
119 * The MDB seems OK: transfer info from it into VCB
120 * Note - the VCB starts out clear (all zeros)
123 vcb
->vcbSigWord
= SWAP_BE16 (mdb
->drSigWord
);
124 vcb
->vcbCrDate
= to_bsd_time(LocalToUTC(SWAP_BE32(mdb
->drCrDate
)));
125 vcb
->localCreateDate
= SWAP_BE32 (mdb
->drCrDate
);
126 vcb
->vcbLsMod
= to_bsd_time(LocalToUTC(SWAP_BE32(mdb
->drLsMod
)));
127 vcb
->vcbAtrb
= SWAP_BE16 (mdb
->drAtrb
);
128 vcb
->vcbNmFls
= SWAP_BE16 (mdb
->drNmFls
);
129 vcb
->vcbVBMSt
= SWAP_BE16 (mdb
->drVBMSt
);
130 vcb
->nextAllocation
= SWAP_BE16 (mdb
->drAllocPtr
);
131 vcb
->totalBlocks
= SWAP_BE16 (mdb
->drNmAlBlks
);
132 vcb
->blockSize
= SWAP_BE32 (mdb
->drAlBlkSiz
);
133 vcb
->vcbClpSiz
= SWAP_BE32 (mdb
->drClpSiz
);
134 vcb
->vcbAlBlSt
= SWAP_BE16 (mdb
->drAlBlSt
);
135 vcb
->vcbNxtCNID
= SWAP_BE32 (mdb
->drNxtCNID
);
136 vcb
->freeBlocks
= SWAP_BE16 (mdb
->drFreeBks
);
137 vcb
->vcbVolBkUp
= to_bsd_time(LocalToUTC(SWAP_BE32(mdb
->drVolBkUp
)));
138 vcb
->vcbWrCnt
= SWAP_BE32 (mdb
->drWrCnt
);
139 vcb
->vcbNmRtDirs
= SWAP_BE16 (mdb
->drNmRtDirs
);
140 vcb
->vcbFilCnt
= SWAP_BE32 (mdb
->drFilCnt
);
141 vcb
->vcbDirCnt
= SWAP_BE32 (mdb
->drDirCnt
);
142 bcopy(mdb
->drFndrInfo
, vcb
->vcbFndrInfo
, sizeof(vcb
->vcbFndrInfo
));
143 if ((hfsmp
->hfs_flags
& HFS_READ_ONLY
) == 0)
144 vcb
->vcbWrCnt
++; /* Compensate for write of MDB on last flush */
146 /* convert hfs encoded name into UTF-8 string */
147 error
= hfs_to_utf8(vcb
, mdb
->drVN
, NAME_MAX
, &utf8chars
, vcb
->vcbVN
);
149 * When an HFS name cannot be encoded with the current
150 * volume encoding we use MacRoman as a fallback.
152 if (error
|| (utf8chars
== 0))
153 (void) mac_roman_to_utf8(mdb
->drVN
, NAME_MAX
, &utf8chars
, vcb
->vcbVN
);
155 hfsmp
->hfs_logBlockSize
= BestBlockSizeFit(vcb
->blockSize
, MAXBSIZE
, hfsmp
->hfs_phys_block_size
);
156 vcb
->vcbVBMIOSize
= kHFSBlockSize
;
158 hfsmp
->hfs_alt_id_sector
= HFS_ALT_SECTOR(hfsmp
->hfs_phys_block_size
,
159 hfsmp
->hfs_phys_block_count
);
161 bzero(&cndesc
, sizeof(cndesc
));
162 cndesc
.cd_parentcnid
= kHFSRootParentID
;
163 cndesc
.cd_flags
|= CD_ISMETA
;
164 bzero(&cnattr
, sizeof(cnattr
));
166 cnattr
.ca_mode
= S_IFREG
;
167 bzero(&fork
, sizeof(fork
));
170 * Set up Extents B-tree vnode
172 cndesc
.cd_nameptr
= hfs_extname
;
173 cndesc
.cd_namelen
= strlen(hfs_extname
);
174 cndesc
.cd_cnid
= cnattr
.ca_fileid
= kHFSExtentsFileID
;
175 fork
.cf_size
= SWAP_BE32(mdb
->drXTFlSize
);
176 fork
.cf_blocks
= fork
.cf_size
/ vcb
->blockSize
;
177 fork
.cf_clump
= SWAP_BE32(mdb
->drXTClpSiz
);
179 fork
.cf_extents
[0].startBlock
= SWAP_BE16(mdb
->drXTExtRec
[0].startBlock
);
180 fork
.cf_extents
[0].blockCount
= SWAP_BE16(mdb
->drXTExtRec
[0].blockCount
);
181 fork
.cf_extents
[1].startBlock
= SWAP_BE16(mdb
->drXTExtRec
[1].startBlock
);
182 fork
.cf_extents
[1].blockCount
= SWAP_BE16(mdb
->drXTExtRec
[1].blockCount
);
183 fork
.cf_extents
[2].startBlock
= SWAP_BE16(mdb
->drXTExtRec
[2].startBlock
);
184 fork
.cf_extents
[2].blockCount
= SWAP_BE16(mdb
->drXTExtRec
[2].blockCount
);
185 cnattr
.ca_blocks
= fork
.cf_blocks
;
187 error
= hfs_getnewvnode(hfsmp
, NULL
, NULL
, &cndesc
, 0, &cnattr
, &fork
,
188 &hfsmp
->hfs_extents_vp
);
189 if (error
) goto MtVolErr
;
190 error
= MacToVFSError(BTOpenPath(VTOF(hfsmp
->hfs_extents_vp
),
191 (KeyCompareProcPtr
)CompareExtentKeys
));
193 hfs_unlock(VTOC(hfsmp
->hfs_extents_vp
));
198 * Set up Catalog B-tree vnode...
200 cndesc
.cd_nameptr
= hfs_catname
;
201 cndesc
.cd_namelen
= strlen(hfs_catname
);
202 cndesc
.cd_cnid
= cnattr
.ca_fileid
= kHFSCatalogFileID
;
203 fork
.cf_size
= SWAP_BE32(mdb
->drCTFlSize
);
204 fork
.cf_blocks
= fork
.cf_size
/ vcb
->blockSize
;
205 fork
.cf_clump
= SWAP_BE32(mdb
->drCTClpSiz
);
207 fork
.cf_extents
[0].startBlock
= SWAP_BE16(mdb
->drCTExtRec
[0].startBlock
);
208 fork
.cf_extents
[0].blockCount
= SWAP_BE16(mdb
->drCTExtRec
[0].blockCount
);
209 fork
.cf_extents
[1].startBlock
= SWAP_BE16(mdb
->drCTExtRec
[1].startBlock
);
210 fork
.cf_extents
[1].blockCount
= SWAP_BE16(mdb
->drCTExtRec
[1].blockCount
);
211 fork
.cf_extents
[2].startBlock
= SWAP_BE16(mdb
->drCTExtRec
[2].startBlock
);
212 fork
.cf_extents
[2].blockCount
= SWAP_BE16(mdb
->drCTExtRec
[2].blockCount
);
213 cnattr
.ca_blocks
= fork
.cf_blocks
;
215 error
= hfs_getnewvnode(hfsmp
, NULL
, NULL
, &cndesc
, 0, &cnattr
, &fork
,
216 &hfsmp
->hfs_catalog_vp
);
218 hfs_unlock(VTOC(hfsmp
->hfs_extents_vp
));
221 error
= MacToVFSError(BTOpenPath(VTOF(hfsmp
->hfs_catalog_vp
),
222 (KeyCompareProcPtr
)CompareCatalogKeys
));
224 hfs_unlock(VTOC(hfsmp
->hfs_catalog_vp
));
225 hfs_unlock(VTOC(hfsmp
->hfs_extents_vp
));
230 * Set up dummy Allocation file vnode (used only for locking bitmap)
232 cndesc
.cd_nameptr
= hfs_vbmname
;
233 cndesc
.cd_namelen
= strlen(hfs_vbmname
);
234 cndesc
.cd_cnid
= cnattr
.ca_fileid
= kHFSAllocationFileID
;
235 bzero(&fork
, sizeof(fork
));
236 cnattr
.ca_blocks
= 0;
238 error
= hfs_getnewvnode(hfsmp
, NULL
, NULL
, &cndesc
, 0, &cnattr
, &fork
,
239 &hfsmp
->hfs_allocation_vp
);
241 hfs_unlock(VTOC(hfsmp
->hfs_catalog_vp
));
242 hfs_unlock(VTOC(hfsmp
->hfs_extents_vp
));
246 /* mark the volume dirty (clear clean unmount bit) */
247 vcb
->vcbAtrb
&= ~kHFSVolumeUnmountedMask
;
250 * all done with system files so we can unlock now...
252 hfs_unlock(VTOC(hfsmp
->hfs_allocation_vp
));
253 hfs_unlock(VTOC(hfsmp
->hfs_catalog_vp
));
254 hfs_unlock(VTOC(hfsmp
->hfs_extents_vp
));
256 if ( error
== noErr
)
258 if ( !(vcb
->vcbAtrb
& kHFSVolumeHardwareLockMask
) ) // if the disk is not write protected
260 MarkVCBDirty( vcb
); // mark VCB dirty so it will be written
265 //-- Release any resources allocated so far before exiting with an error:
267 ReleaseMetaFileVNode(hfsmp
->hfs_catalog_vp
);
268 ReleaseMetaFileVNode(hfsmp
->hfs_extents_vp
);
274 //*******************************************************************************
275 // Routine: hfs_MountHFSPlusVolume
278 //*******************************************************************************
281 OSErr
hfs_MountHFSPlusVolume(struct hfsmount
*hfsmp
, HFSPlusVolumeHeader
*vhp
,
282 off_t embeddedOffset
, u_int64_t disksize
, struct proc
*p
, void *args
, kauth_cred_t cred
)
284 register ExtendedVCB
*vcb
;
285 struct cat_desc cndesc
;
286 struct cat_attr cnattr
;
287 struct cat_fork cfork
;
289 daddr64_t spare_sectors
;
290 struct BTreeInfoRec btinfo
;
296 signature
= SWAP_BE16(vhp
->signature
);
297 version
= SWAP_BE16(vhp
->version
);
299 if (signature
== kHFSPlusSigWord
) {
300 if (version
!= kHFSPlusVersion
) {
301 printf("hfs_mount: invalid HFS+ version: %d\n", version
);
304 } else if (signature
== kHFSXSigWord
) {
305 if (version
!= kHFSXVersion
) {
306 printf("hfs_mount: invalid HFSX version: %d\n", version
);
309 /* The in-memory signature is always 'H+'. */
310 signature
= kHFSPlusSigWord
;
311 hfsmp
->hfs_flags
|= HFS_X
;
313 /* Removed printf for invalid HFS+ signature because it gives
314 * false error for UFS root volume
319 /* Block size must be at least 512 and a power of 2 */
320 blockSize
= SWAP_BE32(vhp
->blockSize
);
321 if (blockSize
< 512 || !powerof2(blockSize
))
324 /* don't mount a writable volume if its dirty, it must be cleaned by fsck_hfs */
325 if ((hfsmp
->hfs_flags
& HFS_READ_ONLY
) == 0 && hfsmp
->jnl
== NULL
&&
326 (SWAP_BE32(vhp
->attributes
) & kHFSVolumeUnmountedMask
) == 0)
329 /* Make sure we can live with the physical block size. */
330 if ((disksize
& (hfsmp
->hfs_phys_block_size
- 1)) ||
331 (embeddedOffset
& (hfsmp
->hfs_phys_block_size
- 1)) ||
332 (blockSize
< hfsmp
->hfs_phys_block_size
)) {
336 * The VolumeHeader seems OK: transfer info from it into VCB
337 * Note - the VCB starts out clear (all zeros)
339 vcb
= HFSTOVCB(hfsmp
);
341 vcb
->vcbSigWord
= signature
;
342 vcb
->vcbJinfoBlock
= SWAP_BE32(vhp
->journalInfoBlock
);
343 vcb
->vcbLsMod
= to_bsd_time(SWAP_BE32(vhp
->modifyDate
));
344 vcb
->vcbAtrb
= SWAP_BE32(vhp
->attributes
);
345 vcb
->vcbClpSiz
= SWAP_BE32(vhp
->rsrcClumpSize
);
346 vcb
->vcbNxtCNID
= SWAP_BE32(vhp
->nextCatalogID
);
347 vcb
->vcbVolBkUp
= to_bsd_time(SWAP_BE32(vhp
->backupDate
));
348 vcb
->vcbWrCnt
= SWAP_BE32(vhp
->writeCount
);
349 vcb
->vcbFilCnt
= SWAP_BE32(vhp
->fileCount
);
350 vcb
->vcbDirCnt
= SWAP_BE32(vhp
->folderCount
);
352 /* copy 32 bytes of Finder info */
353 bcopy(vhp
->finderInfo
, vcb
->vcbFndrInfo
, sizeof(vhp
->finderInfo
));
355 vcb
->vcbAlBlSt
= 0; /* hfs+ allocation blocks start at first block of volume */
356 if ((hfsmp
->hfs_flags
& HFS_READ_ONLY
) == 0)
357 vcb
->vcbWrCnt
++; /* compensate for write of Volume Header on last flush */
359 /* Now fill in the Extended VCB info */
360 vcb
->nextAllocation
= SWAP_BE32(vhp
->nextAllocation
);
361 vcb
->totalBlocks
= SWAP_BE32(vhp
->totalBlocks
);
362 vcb
->freeBlocks
= SWAP_BE32(vhp
->freeBlocks
);
363 vcb
->blockSize
= blockSize
;
364 vcb
->encodingsBitmap
= SWAP_BE64(vhp
->encodingsBitmap
);
365 vcb
->localCreateDate
= SWAP_BE32(vhp
->createDate
);
367 vcb
->hfsPlusIOPosOffset
= embeddedOffset
;
369 /* Default to no free block reserve */
370 vcb
->reserveBlocks
= 0;
373 * Update the logical block size in the mount struct
374 * (currently set up from the wrapper MDB) using the
375 * new blocksize value:
377 hfsmp
->hfs_logBlockSize
= BestBlockSizeFit(vcb
->blockSize
, MAXBSIZE
, hfsmp
->hfs_phys_block_size
);
378 vcb
->vcbVBMIOSize
= min(vcb
->blockSize
, MAXPHYSIO
);
381 * Validate and initialize the location of the alternate volume header.
383 spare_sectors
= hfsmp
->hfs_phys_block_count
-
384 (((daddr64_t
)vcb
->totalBlocks
* blockSize
) /
385 hfsmp
->hfs_phys_block_size
);
387 if (spare_sectors
> (blockSize
/ hfsmp
->hfs_phys_block_size
)) {
388 hfsmp
->hfs_alt_id_sector
= 0; /* partition has grown! */
390 hfsmp
->hfs_alt_id_sector
= (hfsmp
->hfsPlusIOPosOffset
/ hfsmp
->hfs_phys_block_size
) +
391 HFS_ALT_SECTOR(hfsmp
->hfs_phys_block_size
,
392 hfsmp
->hfs_phys_block_count
);
395 bzero(&cndesc
, sizeof(cndesc
));
396 cndesc
.cd_parentcnid
= kHFSRootParentID
;
397 cndesc
.cd_flags
|= CD_ISMETA
;
398 bzero(&cnattr
, sizeof(cnattr
));
400 cnattr
.ca_mode
= S_IFREG
;
403 * Set up Extents B-tree vnode
405 cndesc
.cd_nameptr
= hfs_extname
;
406 cndesc
.cd_namelen
= strlen(hfs_extname
);
407 cndesc
.cd_cnid
= cnattr
.ca_fileid
= kHFSExtentsFileID
;
409 cfork
.cf_size
= SWAP_BE64 (vhp
->extentsFile
.logicalSize
);
410 cfork
.cf_clump
= SWAP_BE32 (vhp
->extentsFile
.clumpSize
);
411 cfork
.cf_blocks
= SWAP_BE32 (vhp
->extentsFile
.totalBlocks
);
412 cfork
.cf_vblocks
= 0;
413 cnattr
.ca_blocks
= cfork
.cf_blocks
;
414 for (i
= 0; i
< kHFSPlusExtentDensity
; i
++) {
415 cfork
.cf_extents
[i
].startBlock
=
416 SWAP_BE32 (vhp
->extentsFile
.extents
[i
].startBlock
);
417 cfork
.cf_extents
[i
].blockCount
=
418 SWAP_BE32 (vhp
->extentsFile
.extents
[i
].blockCount
);
420 retval
= hfs_getnewvnode(hfsmp
, NULL
, NULL
, &cndesc
, 0, &cnattr
, &cfork
,
421 &hfsmp
->hfs_extents_vp
);
423 if (retval
) goto ErrorExit
;
424 retval
= MacToVFSError(BTOpenPath(VTOF(hfsmp
->hfs_extents_vp
),
425 (KeyCompareProcPtr
) CompareExtentKeysPlus
));
427 hfs_unlock(VTOC(hfsmp
->hfs_extents_vp
));
432 * Set up Catalog B-tree vnode
434 cndesc
.cd_nameptr
= hfs_catname
;
435 cndesc
.cd_namelen
= strlen(hfs_catname
);
436 cndesc
.cd_cnid
= cnattr
.ca_fileid
= kHFSCatalogFileID
;
438 cfork
.cf_size
= SWAP_BE64 (vhp
->catalogFile
.logicalSize
);
439 cfork
.cf_clump
= SWAP_BE32 (vhp
->catalogFile
.clumpSize
);
440 cfork
.cf_blocks
= SWAP_BE32 (vhp
->catalogFile
.totalBlocks
);
441 cfork
.cf_vblocks
= 0;
442 cnattr
.ca_blocks
= cfork
.cf_blocks
;
443 for (i
= 0; i
< kHFSPlusExtentDensity
; i
++) {
444 cfork
.cf_extents
[i
].startBlock
=
445 SWAP_BE32 (vhp
->catalogFile
.extents
[i
].startBlock
);
446 cfork
.cf_extents
[i
].blockCount
=
447 SWAP_BE32 (vhp
->catalogFile
.extents
[i
].blockCount
);
449 retval
= hfs_getnewvnode(hfsmp
, NULL
, NULL
, &cndesc
, 0, &cnattr
, &cfork
,
450 &hfsmp
->hfs_catalog_vp
);
452 hfs_unlock(VTOC(hfsmp
->hfs_extents_vp
));
455 retval
= MacToVFSError(BTOpenPath(VTOF(hfsmp
->hfs_catalog_vp
),
456 (KeyCompareProcPtr
) CompareExtendedCatalogKeys
));
458 hfs_unlock(VTOC(hfsmp
->hfs_catalog_vp
));
459 hfs_unlock(VTOC(hfsmp
->hfs_extents_vp
));
462 if ((hfsmp
->hfs_flags
& HFS_X
) &&
463 BTGetInformation(VTOF(hfsmp
->hfs_catalog_vp
), 0, &btinfo
) == 0) {
464 if (btinfo
.keyCompareType
== kHFSBinaryCompare
) {
465 hfsmp
->hfs_flags
|= HFS_CASE_SENSITIVE
;
466 /* Install a case-sensitive key compare */
467 (void) BTOpenPath(VTOF(hfsmp
->hfs_catalog_vp
),
468 (KeyCompareProcPtr
)cat_binarykeycompare
);
473 * Set up Allocation file vnode
475 cndesc
.cd_nameptr
= hfs_vbmname
;
476 cndesc
.cd_namelen
= strlen(hfs_vbmname
);
477 cndesc
.cd_cnid
= cnattr
.ca_fileid
= kHFSAllocationFileID
;
479 cfork
.cf_size
= SWAP_BE64 (vhp
->allocationFile
.logicalSize
);
480 cfork
.cf_clump
= SWAP_BE32 (vhp
->allocationFile
.clumpSize
);
481 cfork
.cf_blocks
= SWAP_BE32 (vhp
->allocationFile
.totalBlocks
);
482 cfork
.cf_vblocks
= 0;
483 cnattr
.ca_blocks
= cfork
.cf_blocks
;
484 for (i
= 0; i
< kHFSPlusExtentDensity
; i
++) {
485 cfork
.cf_extents
[i
].startBlock
=
486 SWAP_BE32 (vhp
->allocationFile
.extents
[i
].startBlock
);
487 cfork
.cf_extents
[i
].blockCount
=
488 SWAP_BE32 (vhp
->allocationFile
.extents
[i
].blockCount
);
490 retval
= hfs_getnewvnode(hfsmp
, NULL
, NULL
, &cndesc
, 0, &cnattr
, &cfork
,
491 &hfsmp
->hfs_allocation_vp
);
493 hfs_unlock(VTOC(hfsmp
->hfs_catalog_vp
));
494 hfs_unlock(VTOC(hfsmp
->hfs_extents_vp
));
499 * Set up Attribute B-tree vnode
501 if (vhp
->attributesFile
.totalBlocks
!= 0) {
502 cndesc
.cd_nameptr
= hfs_attrname
;
503 cndesc
.cd_namelen
= strlen(hfs_attrname
);
504 cndesc
.cd_cnid
= cnattr
.ca_fileid
= kHFSAttributesFileID
;
506 cfork
.cf_size
= SWAP_BE64 (vhp
->attributesFile
.logicalSize
);
507 cfork
.cf_clump
= SWAP_BE32 (vhp
->attributesFile
.clumpSize
);
508 cfork
.cf_blocks
= SWAP_BE32 (vhp
->attributesFile
.totalBlocks
);
509 cfork
.cf_vblocks
= 0;
510 cnattr
.ca_blocks
= cfork
.cf_blocks
;
511 for (i
= 0; i
< kHFSPlusExtentDensity
; i
++) {
512 cfork
.cf_extents
[i
].startBlock
=
513 SWAP_BE32 (vhp
->attributesFile
.extents
[i
].startBlock
);
514 cfork
.cf_extents
[i
].blockCount
=
515 SWAP_BE32 (vhp
->attributesFile
.extents
[i
].blockCount
);
517 retval
= hfs_getnewvnode(hfsmp
, NULL
, NULL
, &cndesc
, 0, &cnattr
, &cfork
,
518 &hfsmp
->hfs_attribute_vp
);
520 hfs_unlock(VTOC(hfsmp
->hfs_allocation_vp
));
521 hfs_unlock(VTOC(hfsmp
->hfs_catalog_vp
));
522 hfs_unlock(VTOC(hfsmp
->hfs_extents_vp
));
525 retval
= MacToVFSError(BTOpenPath(VTOF(hfsmp
->hfs_attribute_vp
),
526 (KeyCompareProcPtr
) hfs_attrkeycompare
));
528 hfs_unlock(VTOC(hfsmp
->hfs_attribute_vp
));
529 hfs_unlock(VTOC(hfsmp
->hfs_allocation_vp
));
530 hfs_unlock(VTOC(hfsmp
->hfs_catalog_vp
));
531 hfs_unlock(VTOC(hfsmp
->hfs_extents_vp
));
537 /* Pick up volume name and create date */
538 retval
= cat_idlookup(hfsmp
, kHFSRootFolderID
, &cndesc
, &cnattr
, NULL
);
540 if (hfsmp
->hfs_attribute_vp
)
541 hfs_unlock(VTOC(hfsmp
->hfs_attribute_vp
));
542 hfs_unlock(VTOC(hfsmp
->hfs_allocation_vp
));
543 hfs_unlock(VTOC(hfsmp
->hfs_catalog_vp
));
544 hfs_unlock(VTOC(hfsmp
->hfs_extents_vp
));
547 vcb
->vcbCrDate
= cnattr
.ca_itime
;
548 vcb
->volumeNameEncodingHint
= cndesc
.cd_encoding
;
549 bcopy(cndesc
.cd_nameptr
, vcb
->vcbVN
, min(255, cndesc
.cd_namelen
));
550 cat_releasedesc(&cndesc
);
552 /* mark the volume dirty (clear clean unmount bit) */
553 vcb
->vcbAtrb
&= ~kHFSVolumeUnmountedMask
;
554 if (hfsmp
->jnl
&& (hfsmp
->hfs_flags
& HFS_READ_ONLY
) == 0) {
555 hfs_flushvolumeheader(hfsmp
, TRUE
, 0);
559 * all done with metadata files so we can unlock now...
561 if (hfsmp
->hfs_attribute_vp
)
562 hfs_unlock(VTOC(hfsmp
->hfs_attribute_vp
));
563 hfs_unlock(VTOC(hfsmp
->hfs_allocation_vp
));
564 hfs_unlock(VTOC(hfsmp
->hfs_catalog_vp
));
565 hfs_unlock(VTOC(hfsmp
->hfs_extents_vp
));
568 // Check if we need to do late journal initialization. This only
569 // happens if a previous version of MacOS X (or 9) touched the disk.
570 // In that case hfs_late_journal_init() will go re-locate the journal
571 // and journal_info_block files and validate that they're still kosher.
573 if ( (vcb
->vcbAtrb
& kHFSVolumeJournaledMask
)
574 && (SWAP_BE32(vhp
->lastMountedVersion
) != kHFSJMountVersion
)
575 && (hfsmp
->jnl
== NULL
)) {
577 retval
= hfs_late_journal_init(hfsmp
, vhp
, args
);
581 // if the journal failed to open, then set the lastMountedVersion
582 // to be "FSK!" which fsck_hfs will see and force the fsck instead
583 // of just bailing out because the volume is journaled.
584 if (!(hfsmp
->hfs_flags
& HFS_READ_ONLY
)) {
585 HFSPlusVolumeHeader
*jvhp
;
586 daddr64_t mdb_offset
;
587 struct buf
*bp
= NULL
;
589 hfsmp
->hfs_flags
|= HFS_NEED_JNL_RESET
;
591 mdb_offset
= (daddr64_t
)((embeddedOffset
/ blockSize
) + HFS_PRI_SECTOR(blockSize
));
593 retval
= (int)buf_meta_bread(hfsmp
->hfs_devvp
, mdb_offset
, blockSize
, cred
, &bp
);
595 jvhp
= (HFSPlusVolumeHeader
*)(buf_dataptr(bp
) + HFS_PRI_OFFSET(blockSize
));
597 if (SWAP_BE16(jvhp
->signature
) == kHFSPlusSigWord
|| SWAP_BE16(jvhp
->signature
) == kHFSXSigWord
) {
598 printf ("hfs(3): Journal replay fail. Writing lastMountVersion as FSK!\n");
599 jvhp
->lastMountedVersion
= SWAP_BE32(kFSKMountVersion
);
607 // clear this so the error exit path won't try to use it
614 } else if (hfsmp
->jnl
) {
615 vfs_setflags(hfsmp
->hfs_mp
, (uint64_t)((unsigned int)MNT_JOURNALED
));
617 } else if (hfsmp
->jnl
|| ((vcb
->vcbAtrb
& kHFSVolumeJournaledMask
) && (hfsmp
->hfs_flags
& HFS_READ_ONLY
))) {
618 struct cat_attr jinfo_attr
, jnl_attr
;
620 if (hfsmp
->hfs_flags
& HFS_READ_ONLY
) {
621 vcb
->vcbAtrb
&= ~kHFSVolumeJournaledMask
;
624 // if we're here we need to fill in the fileid's for the
625 // journal and journal_info_block.
626 hfsmp
->hfs_jnlinfoblkid
= GetFileInfo(vcb
, kRootDirID
, ".journal_info_block", &jinfo_attr
, NULL
);
627 hfsmp
->hfs_jnlfileid
= GetFileInfo(vcb
, kRootDirID
, ".journal", &jnl_attr
, NULL
);
628 if (hfsmp
->hfs_jnlinfoblkid
== 0 || hfsmp
->hfs_jnlfileid
== 0) {
629 printf("hfs: danger! couldn't find the file-id's for the journal or journal_info_block\n");
630 printf("hfs: jnlfileid %d, jnlinfoblkid %d\n", hfsmp
->hfs_jnlfileid
, hfsmp
->hfs_jnlinfoblkid
);
633 if (hfsmp
->hfs_flags
& HFS_READ_ONLY
) {
634 vcb
->vcbAtrb
|= kHFSVolumeJournaledMask
;
639 * Establish a metadata allocation zone.
641 hfs_metadatazone_init(hfsmp
);
644 * Make any metadata zone adjustments.
646 if (hfsmp
->hfs_flags
& HFS_METADATA_ZONE
) {
647 /* Keep the roving allocator out of the metadata zone. */
648 if (vcb
->nextAllocation
>= hfsmp
->hfs_metazone_start
&&
649 vcb
->nextAllocation
<= hfsmp
->hfs_metazone_end
) {
650 vcb
->nextAllocation
= hfsmp
->hfs_metazone_end
+ 1;
654 /* setup private/hidden directory for unlinked files */
655 FindMetaDataDirectory(vcb
);
656 if ((hfsmp
->hfs_flags
& HFS_READ_ONLY
) == 0)
657 hfs_remove_orphans(hfsmp
);
659 if ( !(vcb
->vcbAtrb
& kHFSVolumeHardwareLockMask
) ) // if the disk is not write protected
661 MarkVCBDirty( vcb
); // mark VCB dirty so it will be written
665 * Allow hot file clustering if conditions allow.
667 if ((hfsmp
->hfs_flags
& HFS_METADATA_ZONE
) &&
668 ((hfsmp
->hfs_flags
& HFS_READ_ONLY
) == 0)) {
669 (void) hfs_recording_init(hfsmp
);
672 hfs_checkextendedsecurity(hfsmp
);
678 * A fatal error occurred and the volume cannot be mounted
679 * release any resources that we aquired...
681 if (hfsmp
->hfs_attribute_vp
)
682 ReleaseMetaFileVNode(hfsmp
->hfs_attribute_vp
);
683 ReleaseMetaFileVNode(hfsmp
->hfs_allocation_vp
);
684 ReleaseMetaFileVNode(hfsmp
->hfs_catalog_vp
);
685 ReleaseMetaFileVNode(hfsmp
->hfs_extents_vp
);
692 * ReleaseMetaFileVNode
696 static void ReleaseMetaFileVNode(struct vnode
*vp
)
700 if (vp
&& (fp
= VTOF(vp
))) {
701 if (fp
->fcbBTCBPtr
!= NULL
) {
702 (void)hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
);
703 (void) BTClosePath(fp
);
704 hfs_unlock(VTOC(vp
));
707 /* release the node even if BTClosePath fails */
714 /*************************************************************
716 * Unmounts a hfs volume.
717 * At this point vflush() has been called (to dump all non-metadata files)
719 *************************************************************/
723 hfsUnmount( register struct hfsmount
*hfsmp
, struct proc
*p
)
725 if (hfsmp
->hfs_allocation_vp
)
726 ReleaseMetaFileVNode(hfsmp
->hfs_allocation_vp
);
728 if (hfsmp
->hfs_attribute_vp
)
729 ReleaseMetaFileVNode(hfsmp
->hfs_attribute_vp
);
731 ReleaseMetaFileVNode(hfsmp
->hfs_catalog_vp
);
732 ReleaseMetaFileVNode(hfsmp
->hfs_extents_vp
);
739 * Test if fork has overflow extents.
743 overflow_extents(struct filefork
*fp
)
747 if (VTOVCB(FTOV(fp
))->vcbSigWord
== kHFSPlusSigWord
) {
748 if (fp
->ff_extents
[7].blockCount
== 0)
751 blocks
= fp
->ff_extents
[0].blockCount
+
752 fp
->ff_extents
[1].blockCount
+
753 fp
->ff_extents
[2].blockCount
+
754 fp
->ff_extents
[3].blockCount
+
755 fp
->ff_extents
[4].blockCount
+
756 fp
->ff_extents
[5].blockCount
+
757 fp
->ff_extents
[6].blockCount
+
758 fp
->ff_extents
[7].blockCount
;
760 if (fp
->ff_extents
[2].blockCount
== 0)
763 blocks
= fp
->ff_extents
[0].blockCount
+
764 fp
->ff_extents
[1].blockCount
+
765 fp
->ff_extents
[2].blockCount
;
768 return (fp
->ff_blocks
> blocks
);
773 * Lock HFS system file(s).
777 hfs_systemfile_lock(struct hfsmount
*hfsmp
, int flags
, enum hfslocktype locktype
)
779 if (flags
& ~SFL_VALIDMASK
)
780 panic("hfs_systemfile_lock: invalid lock request (0x%x)", (unsigned long) flags
);
782 * Locking order is Catalog file, Attributes file, Bitmap file, Extents file
784 if (flags
& SFL_CATALOG
) {
785 (void) hfs_lock(VTOC(hfsmp
->hfs_catalog_vp
), locktype
);
787 * When the catalog file has overflow extents then
788 * also acquire the extents b-tree lock if its not
791 if ((flags
& SFL_EXTENTS
) == 0 &&
792 overflow_extents(VTOF(hfsmp
->hfs_catalog_vp
))) {
793 flags
|= SFL_EXTENTS
;
796 if (flags
& SFL_ATTRIBUTE
) {
797 if (hfsmp
->hfs_attribute_vp
) {
798 (void) hfs_lock(VTOC(hfsmp
->hfs_attribute_vp
), locktype
);
800 * When the attribute file has overflow extents then
801 * also acquire the extents b-tree lock if its not
804 if ((flags
& SFL_EXTENTS
) == 0 &&
805 overflow_extents(VTOF(hfsmp
->hfs_attribute_vp
))) {
806 flags
|= SFL_EXTENTS
;
809 flags
&= ~SFL_ATTRIBUTE
;
812 if (flags
& SFL_BITMAP
) {
814 * Since the only bitmap operations are clearing and
815 * setting bits we always need exclusive access. And
816 * when we have a journal, we can "hide" behind that
817 * lock since we can only change the bitmap from
818 * within a transaction.
821 flags
&= ~SFL_BITMAP
;
823 (void) hfs_lock(VTOC(hfsmp
->hfs_allocation_vp
), HFS_EXCLUSIVE_LOCK
);
826 if (flags
& SFL_EXTENTS
) {
828 * Since the extents btree lock is recursive we always
829 * need exclusive access.
831 (void) hfs_lock(VTOC(hfsmp
->hfs_extents_vp
), HFS_EXCLUSIVE_LOCK
);
837 * unlock HFS system file(s).
841 hfs_systemfile_unlock(struct hfsmount
*hfsmp
, int flags
)
845 int numOfLockedBuffs
;
848 lastfsync
= tv
.tv_sec
;
850 if (flags
& ~SFL_VALIDMASK
)
851 panic("hfs_systemfile_unlock: invalid lock request (0x%x)", (unsigned long) flags
);
853 if (flags
& SFL_ATTRIBUTE
&& hfsmp
->hfs_attribute_vp
) {
854 if (hfsmp
->jnl
== NULL
) {
855 BTGetLastSync((FCB
*)VTOF(hfsmp
->hfs_attribute_vp
), &lastfsync
);
856 numOfLockedBuffs
= count_lock_queue();
857 if ((numOfLockedBuffs
> kMaxLockedMetaBuffers
) ||
858 ((numOfLockedBuffs
> 1) && ((tv
.tv_sec
- lastfsync
) >
859 kMaxSecsForFsync
))) {
860 hfs_btsync(hfsmp
->hfs_attribute_vp
, HFS_SYNCTRANS
);
863 hfs_unlock(VTOC(hfsmp
->hfs_attribute_vp
));
865 if (flags
& SFL_CATALOG
) {
866 if (hfsmp
->jnl
== NULL
) {
867 BTGetLastSync((FCB
*)VTOF(hfsmp
->hfs_catalog_vp
), &lastfsync
);
868 numOfLockedBuffs
= count_lock_queue();
869 if ((numOfLockedBuffs
> kMaxLockedMetaBuffers
) ||
870 ((numOfLockedBuffs
> 1) && ((tv
.tv_sec
- lastfsync
) >
871 kMaxSecsForFsync
))) {
872 hfs_btsync(hfsmp
->hfs_catalog_vp
, HFS_SYNCTRANS
);
875 hfs_unlock(VTOC(hfsmp
->hfs_catalog_vp
));
877 if (flags
& SFL_BITMAP
) {
878 hfs_unlock(VTOC(hfsmp
->hfs_allocation_vp
));
880 if (flags
& SFL_EXTENTS
) {
881 if (hfsmp
->jnl
== NULL
) {
882 BTGetLastSync((FCB
*)VTOF(hfsmp
->hfs_extents_vp
), &lastfsync
);
883 numOfLockedBuffs
= count_lock_queue();
884 if ((numOfLockedBuffs
> kMaxLockedMetaBuffers
) ||
885 ((numOfLockedBuffs
> 1) && ((tv
.tv_sec
- lastfsync
) >
886 kMaxSecsForFsync
))) {
887 hfs_btsync(hfsmp
->hfs_extents_vp
, HFS_SYNCTRANS
);
890 hfs_unlock(VTOC(hfsmp
->hfs_extents_vp
));
898 * Check to see if a vnode is locked in the current context
899 * This is to be used for debugging purposes only!!
902 void RequireFileLock(FileReference vp
, int shareable
)
906 /* The extents btree and allocation bitmap are always exclusive. */
907 if (VTOC(vp
)->c_fileid
== kHFSExtentsFileID
||
908 VTOC(vp
)->c_fileid
== kHFSAllocationFileID
) {
912 locked
= VTOC(vp
)->c_lockowner
== (void *)current_thread();
914 if (!locked
&& !shareable
) {
915 switch (VTOC(vp
)->c_fileid
) {
916 case kHFSExtentsFileID
:
917 panic("extents btree not locked! v: 0x%08X\n #\n", (u_int
)vp
);
919 case kHFSCatalogFileID
:
920 panic("catalog btree not locked! v: 0x%08X\n #\n", (u_int
)vp
);
922 case kHFSAllocationFileID
:
923 /* The allocation file can hide behind the jornal lock. */
924 if (VTOHFS(vp
)->jnl
== NULL
)
925 panic("allocation file not locked! v: 0x%08X\n #\n", (u_int
)vp
);
927 case kHFSAttributesFileID
:
928 panic("attributes btree not locked! v: 0x%08X\n #\n", (u_int
)vp
);
937 * There are three ways to qualify for ownership rights on an object:
939 * 1. (a) Your UID matches the cnode's UID.
940 * (b) The object in question is owned by "unknown"
941 * 2. (a) Permissions on the filesystem are being ignored and
942 * your UID matches the replacement UID.
943 * (b) Permissions on the filesystem are being ignored and
944 * the replacement UID is "unknown".
949 hfs_owner_rights(struct hfsmount
*hfsmp
, uid_t cnode_uid
, kauth_cred_t cred
,
950 struct proc
*p
, int invokesuperuserstatus
)
952 if ((kauth_cred_getuid(cred
) == cnode_uid
) || /* [1a] */
953 (cnode_uid
== UNKNOWNUID
) || /* [1b] */
954 ((((unsigned int)vfs_flags(HFSTOVFS(hfsmp
))) & MNT_UNKNOWNPERMISSIONS
) && /* [2] */
955 ((kauth_cred_getuid(cred
) == hfsmp
->hfs_uid
) || /* [2a] */
956 (hfsmp
->hfs_uid
== UNKNOWNUID
))) || /* [2b] */
957 (invokesuperuserstatus
&& (suser(cred
, 0) == 0))) { /* [3] */
965 unsigned long BestBlockSizeFit(unsigned long allocationBlockSize
,
966 unsigned long blockSizeLimit
,
967 unsigned long baseMultiple
) {
969 Compute the optimal (largest) block size (no larger than allocationBlockSize) that is less than the
970 specified limit but still an even multiple of the baseMultiple.
972 int baseBlockCount
, blockCount
;
973 unsigned long trialBlockSize
;
975 if (allocationBlockSize
% baseMultiple
!= 0) {
977 Whoops: the allocation blocks aren't even multiples of the specified base:
978 no amount of dividing them into even parts will be a multiple, either then!
980 return 512; /* Hope for the best */
983 /* Try the obvious winner first, to prevent 12K allocation blocks, for instance,
984 from being handled as two 6K logical blocks instead of 3 4K logical blocks.
985 Even though the former (the result of the loop below) is the larger allocation
986 block size, the latter is more efficient: */
987 if (allocationBlockSize
% PAGE_SIZE
== 0) return PAGE_SIZE
;
989 /* No clear winner exists: pick the largest even fraction <= MAXBSIZE: */
990 baseBlockCount
= allocationBlockSize
/ baseMultiple
; /* Now guaranteed to be an even multiple */
992 for (blockCount
= baseBlockCount
; blockCount
> 0; --blockCount
) {
993 trialBlockSize
= blockCount
* baseMultiple
;
994 if (allocationBlockSize
% trialBlockSize
== 0) { /* An even multiple? */
995 if ((trialBlockSize
<= blockSizeLimit
) &&
996 (trialBlockSize
% baseMultiple
== 0)) {
997 return trialBlockSize
;
1002 /* Note: we should never get here, since blockCount = 1 should always work,
1003 but this is nice and safe and makes the compiler happy, too ... */
1009 * To make the HFS Plus filesystem follow UFS unlink semantics, a remove
1010 * of an active vnode is translated to a move/rename so the file appears
1011 * deleted. The destination folder for these move/renames is setup here
1012 * and a reference to it is place in hfsmp->hfs_privdir_desc.
1016 FindMetaDataDirectory(ExtendedVCB
*vcb
)
1018 struct hfsmount
* hfsmp
;
1019 struct vnode
* dvp
= NULL
;
1020 struct cnode
* dcp
= NULL
;
1021 struct FndrDirInfo
* fndrinfo
;
1022 struct cat_desc out_desc
= {0};
1023 struct proc
*p
= current_proc();
1025 cat_cookie_t cookie
;
1029 if (vcb
->vcbSigWord
!= kHFSPlusSigWord
)
1032 hfsmp
= VCBTOHFS(vcb
);
1034 if (hfsmp
->hfs_privdir_desc
.cd_parentcnid
== 0) {
1035 hfsmp
->hfs_privdir_desc
.cd_parentcnid
= kRootDirID
;
1036 hfsmp
->hfs_privdir_desc
.cd_nameptr
= hfs_privdirname
;
1037 hfsmp
->hfs_privdir_desc
.cd_namelen
= strlen(hfs_privdirname
);
1038 hfsmp
->hfs_privdir_desc
.cd_flags
= CD_ISDIR
;
1041 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_SHARED_LOCK
);
1043 error
= cat_lookup(hfsmp
, &hfsmp
->hfs_privdir_desc
, 0, NULL
,
1044 &hfsmp
->hfs_privdir_attr
, NULL
, NULL
);
1046 hfs_systemfile_unlock(hfsmp
, lockflags
);
1049 hfsmp
->hfs_metadata_createdate
= hfsmp
->hfs_privdir_attr
.ca_itime
;
1050 hfsmp
->hfs_privdir_desc
.cd_cnid
= hfsmp
->hfs_privdir_attr
.ca_fileid
;
1052 * Clear the system immutable flag if set...
1054 if ((hfsmp
->hfs_privdir_attr
.ca_flags
& SF_IMMUTABLE
) &&
1055 (hfsmp
->hfs_flags
& HFS_READ_ONLY
) == 0) {
1056 hfsmp
->hfs_privdir_attr
.ca_flags
&= ~SF_IMMUTABLE
;
1058 if ((error
= hfs_start_transaction(hfsmp
)) != 0) {
1059 return (hfsmp
->hfs_privdir_attr
.ca_fileid
);
1062 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_SHARED_LOCK
);
1063 (void) cat_update(hfsmp
, &hfsmp
->hfs_privdir_desc
,
1064 &hfsmp
->hfs_privdir_attr
, NULL
, NULL
);
1065 hfs_systemfile_unlock(hfsmp
, lockflags
);
1067 hfs_end_transaction(hfsmp
);
1069 return (hfsmp
->hfs_privdir_attr
.ca_fileid
);
1071 } else if (hfsmp
->hfs_flags
& HFS_READ_ONLY
) {
1076 /* Setup the default attributes */
1077 bzero(&hfsmp
->hfs_privdir_attr
, sizeof(struct cat_attr
));
1078 hfsmp
->hfs_privdir_attr
.ca_mode
= S_IFDIR
;
1079 hfsmp
->hfs_privdir_attr
.ca_nlink
= 2;
1080 hfsmp
->hfs_privdir_attr
.ca_itime
= vcb
->vcbCrDate
;
1082 hfsmp
->hfs_privdir_attr
.ca_mtime
= tv
.tv_sec
;
1084 /* hidden and off the desktop view */
1085 fndrinfo
= (struct FndrDirInfo
*)&hfsmp
->hfs_privdir_attr
.ca_finderinfo
;
1086 fndrinfo
->frLocation
.v
= SWAP_BE16 (22460);
1087 fndrinfo
->frLocation
.h
= SWAP_BE16 (22460);
1088 fndrinfo
->frFlags
|= SWAP_BE16 (kIsInvisible
+ kNameLocked
);
1090 if ((error
= hfs_start_transaction(hfsmp
)) != 0) {
1093 /* Reserve some space in the Catalog file. */
1094 if (cat_preflight(hfsmp
, CAT_CREATE
, &cookie
, p
) != 0) {
1095 hfs_end_transaction(hfsmp
);
1100 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_EXCLUSIVE_LOCK
);
1102 error
= cat_create(hfsmp
, &hfsmp
->hfs_privdir_desc
,
1103 &hfsmp
->hfs_privdir_attr
, &out_desc
);
1105 hfs_systemfile_unlock(hfsmp
, lockflags
);
1107 cat_postflight(hfsmp
, &cookie
, p
);
1110 hfs_volupdate(hfsmp
, VOL_UPDATE
, 0);
1112 hfs_end_transaction(hfsmp
);
1117 hfsmp
->hfs_privdir_desc
.cd_hint
= out_desc
.cd_hint
;
1118 hfsmp
->hfs_privdir_desc
.cd_cnid
= out_desc
.cd_cnid
;
1119 hfsmp
->hfs_privdir_attr
.ca_fileid
= out_desc
.cd_cnid
;
1120 hfsmp
->hfs_metadata_createdate
= vcb
->vcbCrDate
;
1122 if (hfs_vget(hfsmp
, kRootDirID
, &dvp
, 0) == 0) {
1124 dcp
->c_childhint
= out_desc
.cd_hint
;
1127 dcp
->c_touch_chgtime
= TRUE
;
1128 dcp
->c_touch_modtime
= TRUE
;
1129 (void) hfs_update(dvp
, 0);
1133 hfs_volupdate(hfsmp
, VOL_MKDIR
, 1);
1134 hfs_end_transaction(hfsmp
);
1136 cat_releasedesc(&out_desc
);
1138 return (out_desc
.cd_cnid
);
1143 GetFileInfo(ExtendedVCB
*vcb
, u_int32_t dirid
, const char *name
,
1144 struct cat_attr
*fattr
, struct cat_fork
*forkinfo
)
1146 struct hfsmount
* hfsmp
;
1147 struct vnode
* dvp
= NULL
;
1148 struct cnode
* dcp
= NULL
;
1149 struct FndrDirInfo
* fndrinfo
;
1150 struct cat_desc jdesc
;
1154 if (vcb
->vcbSigWord
!= kHFSPlusSigWord
)
1157 hfsmp
= VCBTOHFS(vcb
);
1159 memset(&jdesc
, 0, sizeof(struct cat_desc
));
1160 jdesc
.cd_parentcnid
= kRootDirID
;
1161 jdesc
.cd_nameptr
= name
;
1162 jdesc
.cd_namelen
= strlen(name
);
1164 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_SHARED_LOCK
);
1165 error
= cat_lookup(hfsmp
, &jdesc
, 0, NULL
, fattr
, forkinfo
, NULL
);
1166 hfs_systemfile_unlock(hfsmp
, lockflags
);
1169 return (fattr
->ca_fileid
);
1170 } else if (hfsmp
->hfs_flags
& HFS_READ_ONLY
) {
1174 return (0); /* XXX what callers expect on an error */
1179 * On HFS Plus Volume, there can be orphaned files. These
1180 * are files that were unlinked while busy. If the volume
1181 * was not cleanly unmounted then some of these files may
1182 * have persisted and need to be removed.
1186 hfs_remove_orphans(struct hfsmount
* hfsmp
)
1188 struct BTreeIterator
* iterator
= NULL
;
1189 struct FSBufferDescriptor btdata
;
1190 struct HFSPlusCatalogFile filerec
;
1191 struct HFSPlusCatalogKey
* keyp
;
1192 struct proc
*p
= current_proc();
1198 cat_cookie_t cookie
;
1204 int orphanedlinks
= 0;
1206 bzero(&cookie
, sizeof(cookie
));
1208 if (hfsmp
->hfs_flags
& HFS_CLEANED_ORPHANS
)
1211 vcb
= HFSTOVCB(hfsmp
);
1212 fcb
= VTOF(hfsmp
->hfs_catalog_vp
);
1214 btdata
.bufferAddress
= &filerec
;
1215 btdata
.itemSize
= sizeof(filerec
);
1216 btdata
.itemCount
= 1;
1218 MALLOC(iterator
, struct BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
1219 bzero(iterator
, sizeof(*iterator
));
1221 /* Build a key to "temp" */
1222 keyp
= (HFSPlusCatalogKey
*)&iterator
->key
;
1223 keyp
->parentID
= hfsmp
->hfs_privdir_desc
.cd_cnid
;
1224 keyp
->nodeName
.length
= 4; /* "temp" */
1225 keyp
->keyLength
= kHFSPlusCatalogKeyMinimumLength
+ keyp
->nodeName
.length
* 2;
1226 keyp
->nodeName
.unicode
[0] = 't';
1227 keyp
->nodeName
.unicode
[1] = 'e';
1228 keyp
->nodeName
.unicode
[2] = 'm';
1229 keyp
->nodeName
.unicode
[3] = 'p';
1232 * Position the iterator just before the first real temp file.
1234 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_EXCLUSIVE_LOCK
);
1235 (void) BTSearchRecord(fcb
, iterator
, NULL
, NULL
, iterator
);
1236 hfs_systemfile_unlock(hfsmp
, lockflags
);
1238 /* Visit all the temp files in the HFS+ private directory. */
1240 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_EXCLUSIVE_LOCK
);
1241 result
= BTIterateRecord(fcb
, kBTreeNextRecord
, iterator
, &btdata
, NULL
);
1242 hfs_systemfile_unlock(hfsmp
, lockflags
);
1245 if (keyp
->parentID
!= hfsmp
->hfs_privdir_desc
.cd_cnid
)
1247 if (filerec
.recordType
!= kHFSPlusFileRecord
)
1250 (void) utf8_encodestr(keyp
->nodeName
.unicode
, keyp
->nodeName
.length
* 2,
1251 filename
, &namelen
, sizeof(filename
), 0, 0);
1253 (void) sprintf(tempname
, "%s%d", HFS_DELETE_PREFIX
, filerec
.fileID
);
1256 * Delete all files named "tempxxx", where
1257 * xxx is the file's cnid in decimal.
1260 if (bcmp(tempname
, filename
, namelen
) == 0) {
1261 struct filefork dfork
;
1262 struct filefork rfork
;
1265 bzero(&dfork
, sizeof(dfork
));
1266 bzero(&rfork
, sizeof(rfork
));
1267 bzero(&cnode
, sizeof(cnode
));
1269 if (hfs_start_transaction(hfsmp
) != 0) {
1270 printf("hfs_remove_orphans: failed to start transaction\n");
1276 * Reserve some space in the Catalog file.
1278 if (cat_preflight(hfsmp
, CAT_DELETE
, &cookie
, p
) != 0) {
1279 printf("hfs_remove_orphans: cat_preflight failed\n");
1284 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
| SFL_ATTRIBUTE
| SFL_EXTENTS
| SFL_BITMAP
, HFS_EXCLUSIVE_LOCK
);
1287 /* Build a fake cnode */
1288 cat_convertattr(hfsmp
, (CatalogRecord
*)&filerec
, &cnode
.c_attr
,
1289 &dfork
.ff_data
, &rfork
.ff_data
);
1290 cnode
.c_desc
.cd_parentcnid
= hfsmp
->hfs_privdir_desc
.cd_cnid
;
1291 cnode
.c_desc
.cd_nameptr
= filename
;
1292 cnode
.c_desc
.cd_namelen
= namelen
;
1293 cnode
.c_desc
.cd_cnid
= cnode
.c_attr
.ca_fileid
;
1294 cnode
.c_blocks
= dfork
.ff_blocks
+ rfork
.ff_blocks
;
1296 /* Position iterator at previous entry */
1297 if (BTIterateRecord(fcb
, kBTreePrevRecord
, iterator
,
1302 /* Truncate the file to zero (both forks) */
1303 if (dfork
.ff_blocks
> 0) {
1306 dfork
.ff_cp
= &cnode
;
1307 cnode
.c_datafork
= &dfork
;
1308 cnode
.c_rsrcfork
= NULL
;
1309 fsize
= (u_int64_t
)dfork
.ff_blocks
* (u_int64_t
)HFSTOVCB(hfsmp
)->blockSize
;
1311 if (fsize
> HFS_BIGFILE_SIZE
) {
1312 fsize
-= HFS_BIGFILE_SIZE
;
1317 if (TruncateFileC(vcb
, (FCB
*)&dfork
, fsize
, false) != 0) {
1318 printf("error truncting data fork!\n");
1323 // if we're iteratively truncating this file down,
1324 // then end the transaction and start a new one so
1325 // that no one transaction gets too big.
1327 if (fsize
> 0 && started_tr
) {
1328 hfs_end_transaction(hfsmp
);
1329 if (hfs_start_transaction(hfsmp
) != 0) {
1337 if (rfork
.ff_blocks
> 0) {
1338 rfork
.ff_cp
= &cnode
;
1339 cnode
.c_datafork
= NULL
;
1340 cnode
.c_rsrcfork
= &rfork
;
1341 if (TruncateFileC(vcb
, (FCB
*)&rfork
, 0, false) != 0) {
1342 printf("error truncting rsrc fork!\n");
1347 /* Remove the file record from the Catalog */
1348 if (cat_delete(hfsmp
, &cnode
.c_desc
, &cnode
.c_attr
) != 0) {
1349 printf("hfs_remove_oprhans: error deleting cat rec for id %d!\n", cnode
.c_desc
.cd_cnid
);
1350 hfs_volupdate(hfsmp
, VOL_UPDATE
, 0);
1355 /* Delete any attributes, ignore errors */
1356 (void) hfs_removeallattr(hfsmp
, cnode
.c_fileid
);
1358 /* Update parent and volume counts */
1359 hfsmp
->hfs_privdir_attr
.ca_entries
--;
1360 (void)cat_update(hfsmp
, &hfsmp
->hfs_privdir_desc
,
1361 &hfsmp
->hfs_privdir_attr
, NULL
, NULL
);
1362 hfs_volupdate(hfsmp
, VOL_RMFILE
, 0);
1364 /* Drop locks and end the transaction */
1365 hfs_systemfile_unlock(hfsmp
, lockflags
);
1366 cat_postflight(hfsmp
, &cookie
, p
);
1367 catlock
= catreserve
= 0;
1369 hfs_end_transaction(hfsmp
);
1375 if (orphanedlinks
> 0)
1376 printf("HFS: Removed %d orphaned unlinked files\n", orphanedlinks
);
1379 hfs_systemfile_unlock(hfsmp
, lockflags
);
1382 cat_postflight(hfsmp
, &cookie
, p
);
1385 hfs_end_transaction(hfsmp
);
1388 FREE(iterator
, M_TEMP
);
1389 hfsmp
->hfs_flags
|= HFS_CLEANED_ORPHANS
;
1394 * This will return the correct logical block size for a given vnode.
1395 * For most files, it is the allocation block size, for meta data like
1396 * BTrees, this is kept as part of the BTree private nodeSize
1399 GetLogicalBlockSize(struct vnode
*vp
)
1401 u_int32_t logBlockSize
;
1403 DBG_ASSERT(vp
!= NULL
);
1405 /* start with default */
1406 logBlockSize
= VTOHFS(vp
)->hfs_logBlockSize
;
1408 if (vnode_issystem(vp
)) {
1409 if (VTOF(vp
)->fcbBTCBPtr
!= NULL
) {
1410 BTreeInfoRec bTreeInfo
;
1413 * We do not lock the BTrees, because if we are getting block..then the tree
1414 * should be locked in the first place.
1415 * We just want the nodeSize wich will NEVER change..so even if the world
1416 * is changing..the nodeSize should remain the same. Which argues why lock
1417 * it in the first place??
1420 (void) BTGetInformation (VTOF(vp
), kBTreeInfoVersion
, &bTreeInfo
);
1422 logBlockSize
= bTreeInfo
.nodeSize
;
1424 } else if (VTOC(vp
)->c_fileid
== kHFSAllocationFileID
) {
1425 logBlockSize
= VTOVCB(vp
)->vcbVBMIOSize
;
1429 DBG_ASSERT(logBlockSize
> 0);
1431 return logBlockSize
;
1436 hfs_freeblks(struct hfsmount
* hfsmp
, int wantreserve
)
1438 ExtendedVCB
*vcb
= HFSTOVCB(hfsmp
);
1441 HFS_MOUNT_LOCK(hfsmp
, TRUE
);
1442 freeblks
= vcb
->freeBlocks
;
1444 if (freeblks
> vcb
->reserveBlocks
)
1445 freeblks
-= vcb
->reserveBlocks
;
1449 if (freeblks
> vcb
->loanedBlocks
)
1450 freeblks
-= vcb
->loanedBlocks
;
1453 HFS_MOUNT_UNLOCK(hfsmp
, TRUE
);
1455 #ifdef HFS_SPARSE_DEV
1457 * When the underlying device is sparse, check the
1458 * available space on the backing store volume.
1460 if ((hfsmp
->hfs_flags
& HFS_HAS_SPARSE_DEVICE
) && hfsmp
->hfs_backingfs_rootvp
) {
1461 struct vfsstatfs
*vfsp
; /* 272 bytes */
1462 u_int32_t vfreeblks
;
1463 u_int32_t loanedblks
;
1464 struct mount
* backingfs_mp
;
1466 backingfs_mp
= vnode_mount(hfsmp
->hfs_backingfs_rootvp
);
1468 if (vfsp
= vfs_statfs(backingfs_mp
)) {
1469 HFS_MOUNT_LOCK(hfsmp
, TRUE
);
1470 vfreeblks
= (u_int32_t
)vfsp
->f_bavail
;
1471 /* Normalize block count if needed. */
1472 if (vfsp
->f_bsize
!= vcb
->blockSize
) {
1473 vfreeblks
= ((u_int64_t
)vfreeblks
* (u_int64_t
)(vfsp
->f_bsize
)) / vcb
->blockSize
;
1475 if (vfreeblks
> hfsmp
->hfs_sparsebandblks
)
1476 vfreeblks
-= hfsmp
->hfs_sparsebandblks
;
1480 /* Take into account any delayed allocations. */
1481 loanedblks
= 2 * vcb
->loanedBlocks
;
1482 if (vfreeblks
> loanedblks
)
1483 vfreeblks
-= loanedblks
;
1487 freeblks
= MIN(vfreeblks
, freeblks
);
1488 HFS_MOUNT_UNLOCK(hfsmp
, TRUE
);
1491 #endif /* HFS_SPARSE_DEV */
1497 * Map HFS Common errors (negative) to BSD error codes (positive).
1498 * Positive errors (ie BSD errors) are passed through unchanged.
1500 short MacToVFSError(OSErr err
)
1506 case dskFulErr
: /* -34 */
1507 case btNoSpaceAvail
: /* -32733 */
1509 case fxOvFlErr
: /* -32750 */
1512 case btBadNode
: /* -32731 */
1515 case memFullErr
: /* -108 */
1516 return ENOMEM
; /* +12 */
1518 case cmExists
: /* -32718 */
1519 case btExists
: /* -32734 */
1520 return EEXIST
; /* +17 */
1522 case cmNotFound
: /* -32719 */
1523 case btNotFound
: /* -32735 */
1524 return ENOENT
; /* 28 */
1526 case cmNotEmpty
: /* -32717 */
1527 return ENOTEMPTY
; /* 66 */
1529 case cmFThdDirErr
: /* -32714 */
1530 return EISDIR
; /* 21 */
1532 case fxRangeErr
: /* -32751 */
1535 case bdNamErr
: /* -37 */
1536 return ENAMETOOLONG
; /* 63 */
1538 case paramErr
: /* -50 */
1539 case fileBoundsErr
: /* -1309 */
1540 return EINVAL
; /* +22 */
1542 case fsBTBadNodeSize
:
1546 return EIO
; /* +5 */
1552 * Find the current thread's directory hint for a given index.
1554 * Requires an exclusive lock on directory cnode.
1558 hfs_getdirhint(struct cnode
*dcp
, int index
)
1561 directoryhint_t
*hint
;
1562 boolean_t need_remove
, need_init
;
1568 * Look for an existing hint first. If not found, create a new one (when
1569 * the list is not full) or recycle the oldest hint. Since new hints are
1570 * always added to the head of the list, the last hint is always the
1573 TAILQ_FOREACH(hint
, &dcp
->c_hintlist
, dh_link
) {
1574 if (hint
->dh_index
== index
)
1577 if (hint
!= NULL
) { /* found an existing hint */
1580 } else { /* cannot find an existing hint */
1582 if (dcp
->c_dirhintcnt
< HFS_MAXDIRHINTS
) { /* we don't need recycling */
1583 /* Create a default directory hint */
1584 MALLOC_ZONE(hint
, directoryhint_t
*, sizeof(directoryhint_t
), M_HFSDIRHINT
, M_WAITOK
);
1585 ++dcp
->c_dirhintcnt
;
1586 need_remove
= false;
1587 } else { /* recycle the last (i.e., the oldest) hint */
1588 hint
= TAILQ_LAST(&dcp
->c_hintlist
, hfs_hinthead
);
1589 if ((name
= hint
->dh_desc
.cd_nameptr
)) {
1590 hint
->dh_desc
.cd_nameptr
= NULL
;
1591 vfs_removename(name
);
1598 TAILQ_REMOVE(&dcp
->c_hintlist
, hint
, dh_link
);
1600 TAILQ_INSERT_HEAD(&dcp
->c_hintlist
, hint
, dh_link
);
1603 hint
->dh_index
= index
;
1604 hint
->dh_desc
.cd_flags
= 0;
1605 hint
->dh_desc
.cd_encoding
= 0;
1606 hint
->dh_desc
.cd_namelen
= 0;
1607 hint
->dh_desc
.cd_nameptr
= NULL
;
1608 hint
->dh_desc
.cd_parentcnid
= dcp
->c_cnid
;
1609 hint
->dh_desc
.cd_hint
= dcp
->c_childhint
;
1610 hint
->dh_desc
.cd_cnid
= 0;
1612 hint
->dh_time
= tv
.tv_sec
;
1617 * Release a single directory hint.
1619 * Requires an exclusive lock on directory cnode.
1623 hfs_reldirhint(struct cnode
*dcp
, directoryhint_t
* relhint
)
1627 TAILQ_REMOVE(&dcp
->c_hintlist
, relhint
, dh_link
);
1628 name
= relhint
->dh_desc
.cd_nameptr
;
1630 relhint
->dh_desc
.cd_nameptr
= NULL
;
1631 vfs_removename(name
);
1633 FREE_ZONE(relhint
, sizeof(directoryhint_t
), M_HFSDIRHINT
);
1634 --dcp
->c_dirhintcnt
;
1638 * Release directory hints for given directory
1640 * Requires an exclusive lock on directory cnode.
1644 hfs_reldirhints(struct cnode
*dcp
, int stale_hints_only
)
1647 directoryhint_t
*hint
, *prev
;
1650 if (stale_hints_only
)
1653 /* searching from the oldest to the newest, so we can stop early when releasing stale hints only */
1654 for (hint
= TAILQ_LAST(&dcp
->c_hintlist
, hfs_hinthead
); hint
!= NULL
; hint
= prev
) {
1655 if (stale_hints_only
&& (tv
.tv_sec
- hint
->dh_time
) < HFS_DIRHINT_TTL
)
1656 break; /* stop here if this entry is too new */
1657 name
= hint
->dh_desc
.cd_nameptr
;
1659 hint
->dh_desc
.cd_nameptr
= NULL
;
1660 vfs_removename(name
);
1662 prev
= TAILQ_PREV(hint
, hfs_hinthead
, dh_link
); /* must save this pointer before calling FREE_ZONE on this node */
1663 TAILQ_REMOVE(&dcp
->c_hintlist
, hint
, dh_link
);
1664 FREE_ZONE(hint
, sizeof(directoryhint_t
), M_HFSDIRHINT
);
1665 --dcp
->c_dirhintcnt
;
1671 * Perform a case-insensitive compare of two UTF-8 filenames.
1673 * Returns 0 if the strings match.
1677 hfs_namecmp(const char *str1
, size_t len1
, const char *str2
, size_t len2
)
1679 u_int16_t
*ustr1
, *ustr2
;
1680 size_t ulen1
, ulen2
;
1687 maxbytes
= kHFSPlusMaxFileNameChars
<< 1;
1688 MALLOC(ustr1
, u_int16_t
*, maxbytes
<< 1, M_TEMP
, M_WAITOK
);
1689 ustr2
= ustr1
+ (maxbytes
>> 1);
1691 if (utf8_decodestr(str1
, len1
, ustr1
, &ulen1
, maxbytes
, ':', 0) != 0)
1693 if (utf8_decodestr(str2
, len2
, ustr2
, &ulen2
, maxbytes
, ':', 0) != 0)
1696 cmp
= FastUnicodeCompare(ustr1
, ulen1
>>1, ustr2
, ulen2
>>1);
1698 FREE(ustr1
, M_TEMP
);
1705 hfs_early_journal_init(struct hfsmount
*hfsmp
, HFSPlusVolumeHeader
*vhp
,
1706 void *_args
, off_t embeddedOffset
, daddr64_t mdb_offset
,
1707 HFSMasterDirectoryBlock
*mdbp
, kauth_cred_t cred
)
1709 JournalInfoBlock
*jibp
;
1710 struct buf
*jinfo_bp
, *bp
;
1711 int sectors_per_fsblock
, arg_flags
=0, arg_tbufsz
=0;
1712 int retval
, blksize
= hfsmp
->hfs_phys_block_size
;
1713 struct vnode
*devvp
;
1714 struct hfs_mount_args
*args
= _args
;
1716 devvp
= hfsmp
->hfs_devvp
;
1718 if (args
!= NULL
&& (args
->flags
& HFSFSMNT_EXTENDED_ARGS
)) {
1719 arg_flags
= args
->journal_flags
;
1720 arg_tbufsz
= args
->journal_tbuffer_size
;
1723 sectors_per_fsblock
= SWAP_BE32(vhp
->blockSize
) / blksize
;
1725 retval
= (int)buf_meta_bread(devvp
,
1726 (daddr64_t
)((embeddedOffset
/blksize
) +
1727 (SWAP_BE32(vhp
->journalInfoBlock
)*sectors_per_fsblock
)),
1728 SWAP_BE32(vhp
->blockSize
), cred
, &jinfo_bp
);
1732 jibp
= (JournalInfoBlock
*)buf_dataptr(jinfo_bp
);
1733 jibp
->flags
= SWAP_BE32(jibp
->flags
);
1734 jibp
->offset
= SWAP_BE64(jibp
->offset
);
1735 jibp
->size
= SWAP_BE64(jibp
->size
);
1737 if (jibp
->flags
& kJIJournalInFSMask
) {
1738 hfsmp
->jvp
= hfsmp
->hfs_devvp
;
1740 printf("hfs: journal not stored in fs! don't know what to do.\n");
1741 buf_brelse(jinfo_bp
);
1745 // save this off for the hack-y check in hfs_remove()
1746 hfsmp
->jnl_start
= jibp
->offset
/ SWAP_BE32(vhp
->blockSize
);
1747 hfsmp
->jnl_size
= jibp
->size
;
1749 if ((hfsmp
->hfs_flags
& HFS_READ_ONLY
) && (vfs_flags(hfsmp
->hfs_mp
) & MNT_ROOTFS
) == 0) {
1750 // if the file system is read-only, check if the journal is empty.
1751 // if it is, then we can allow the mount. otherwise we have to
1753 retval
= journal_is_clean(hfsmp
->jvp
,
1754 jibp
->offset
+ embeddedOffset
,
1757 hfsmp
->hfs_phys_block_size
);
1761 buf_brelse(jinfo_bp
);
1764 printf("hfs: early journal init: volume on %s is read-only and journal is dirty. Can not mount volume.\n",
1771 if (jibp
->flags
& kJIJournalNeedInitMask
) {
1772 printf("hfs: Initializing the journal (joffset 0x%llx sz 0x%llx)...\n",
1773 jibp
->offset
+ embeddedOffset
, jibp
->size
);
1774 hfsmp
->jnl
= journal_create(hfsmp
->jvp
,
1775 jibp
->offset
+ embeddedOffset
,
1781 hfs_sync_metadata
, hfsmp
->hfs_mp
);
1783 // no need to start a transaction here... if this were to fail
1784 // we'd just re-init it on the next mount.
1785 jibp
->flags
&= ~kJIJournalNeedInitMask
;
1786 jibp
->flags
= SWAP_BE32(jibp
->flags
);
1787 jibp
->offset
= SWAP_BE64(jibp
->offset
);
1788 jibp
->size
= SWAP_BE64(jibp
->size
);
1789 buf_bwrite(jinfo_bp
);
1793 //printf("hfs: Opening the journal (joffset 0x%llx sz 0x%llx vhp_blksize %d)...\n",
1794 // jibp->offset + embeddedOffset,
1795 // jibp->size, SWAP_BE32(vhp->blockSize));
1797 hfsmp
->jnl
= journal_open(hfsmp
->jvp
,
1798 jibp
->offset
+ embeddedOffset
,
1804 hfs_sync_metadata
, hfsmp
->hfs_mp
);
1806 buf_brelse(jinfo_bp
);
1810 if (hfsmp
->jnl
&& mdbp
) {
1811 // reload the mdb because it could have changed
1812 // if the journal had to be replayed.
1813 if (mdb_offset
== 0) {
1814 mdb_offset
= (daddr64_t
)((embeddedOffset
/ blksize
) + HFS_PRI_SECTOR(blksize
));
1816 retval
= (int)buf_meta_bread(devvp
, mdb_offset
, blksize
, cred
, &bp
);
1819 printf("hfs: failed to reload the mdb after opening the journal (retval %d)!\n",
1823 bcopy((char *)buf_dataptr(bp
) + HFS_PRI_OFFSET(blksize
), mdbp
, 512);
1830 //printf("journal @ 0x%x\n", hfsmp->jnl);
1832 // if we expected the journal to be there and we couldn't
1833 // create it or open it then we have to bail out.
1834 if (hfsmp
->jnl
== NULL
) {
1835 printf("hfs: early jnl init: failed to open/create the journal (retval %d).\n", retval
);
1844 // This function will go and re-locate the .journal_info_block and
1845 // the .journal files in case they moved (which can happen if you
1846 // run Norton SpeedDisk). If we fail to find either file we just
1847 // disable journaling for this volume and return. We turn off the
1848 // journaling bit in the vcb and assume it will get written to disk
1849 // later (if it doesn't on the next mount we'd do the same thing
1850 // again which is harmless). If we disable journaling we don't
1851 // return an error so that the volume is still mountable.
1853 // If the info we find for the .journal_info_block and .journal files
1854 // isn't what we had stored, we re-set our cached info and proceed
1855 // with opening the journal normally.
1858 hfs_late_journal_init(struct hfsmount
*hfsmp
, HFSPlusVolumeHeader
*vhp
, void *_args
)
1860 JournalInfoBlock
*jibp
;
1861 struct buf
*jinfo_bp
, *bp
;
1862 int sectors_per_fsblock
, arg_flags
=0, arg_tbufsz
=0;
1863 int retval
, need_flush
= 0, write_jibp
= 0;
1864 struct vnode
*devvp
;
1865 struct cat_attr jib_attr
, jattr
;
1866 struct cat_fork jib_fork
, jfork
;
1869 struct hfs_mount_args
*args
= _args
;
1871 devvp
= hfsmp
->hfs_devvp
;
1872 vcb
= HFSTOVCB(hfsmp
);
1874 if (args
!= NULL
&& (args
->flags
& HFSFSMNT_EXTENDED_ARGS
)) {
1875 if (args
->journal_disable
) {
1879 arg_flags
= args
->journal_flags
;
1880 arg_tbufsz
= args
->journal_tbuffer_size
;
1883 fid
= GetFileInfo(vcb
, kRootDirID
, ".journal_info_block", &jib_attr
, &jib_fork
);
1884 if (fid
== 0 || jib_fork
.cf_extents
[0].startBlock
== 0 || jib_fork
.cf_size
== 0) {
1885 printf("hfs: can't find the .journal_info_block! disabling journaling (start: %d).\n",
1886 jib_fork
.cf_extents
[0].startBlock
);
1887 vcb
->vcbAtrb
&= ~kHFSVolumeJournaledMask
;
1890 hfsmp
->hfs_jnlinfoblkid
= fid
;
1892 // make sure the journal_info_block begins where we think it should.
1893 if (SWAP_BE32(vhp
->journalInfoBlock
) != jib_fork
.cf_extents
[0].startBlock
) {
1894 printf("hfs: The journal_info_block moved (was: %d; is: %d). Fixing up\n",
1895 SWAP_BE32(vhp
->journalInfoBlock
), jib_fork
.cf_extents
[0].startBlock
);
1897 vcb
->vcbJinfoBlock
= jib_fork
.cf_extents
[0].startBlock
;
1898 vhp
->journalInfoBlock
= SWAP_BE32(jib_fork
.cf_extents
[0].startBlock
);
1902 sectors_per_fsblock
= SWAP_BE32(vhp
->blockSize
) / hfsmp
->hfs_phys_block_size
;
1903 retval
= (int)buf_meta_bread(devvp
,
1904 (daddr64_t
)(vcb
->hfsPlusIOPosOffset
/ hfsmp
->hfs_phys_block_size
+
1905 (SWAP_BE32(vhp
->journalInfoBlock
)*sectors_per_fsblock
)),
1906 SWAP_BE32(vhp
->blockSize
), NOCRED
, &jinfo_bp
);
1908 printf("hfs: can't read journal info block. disabling journaling.\n");
1909 vcb
->vcbAtrb
&= ~kHFSVolumeJournaledMask
;
1913 jibp
= (JournalInfoBlock
*)buf_dataptr(jinfo_bp
);
1914 jibp
->flags
= SWAP_BE32(jibp
->flags
);
1915 jibp
->offset
= SWAP_BE64(jibp
->offset
);
1916 jibp
->size
= SWAP_BE64(jibp
->size
);
1918 fid
= GetFileInfo(vcb
, kRootDirID
, ".journal", &jattr
, &jfork
);
1919 if (fid
== 0 || jfork
.cf_extents
[0].startBlock
== 0 || jfork
.cf_size
== 0) {
1920 printf("hfs: can't find the journal file! disabling journaling (start: %d)\n",
1921 jfork
.cf_extents
[0].startBlock
);
1922 buf_brelse(jinfo_bp
);
1923 vcb
->vcbAtrb
&= ~kHFSVolumeJournaledMask
;
1926 hfsmp
->hfs_jnlfileid
= fid
;
1928 // make sure the journal file begins where we think it should.
1929 if ((jibp
->offset
/ (u_int64_t
)vcb
->blockSize
) != jfork
.cf_extents
[0].startBlock
) {
1930 printf("hfs: The journal file moved (was: %lld; is: %d). Fixing up\n",
1931 (jibp
->offset
/ (u_int64_t
)vcb
->blockSize
), jfork
.cf_extents
[0].startBlock
);
1933 jibp
->offset
= (u_int64_t
)jfork
.cf_extents
[0].startBlock
* (u_int64_t
)vcb
->blockSize
;
1937 // check the size of the journal file.
1938 if (jibp
->size
!= (u_int64_t
)jfork
.cf_extents
[0].blockCount
*vcb
->blockSize
) {
1939 printf("hfs: The journal file changed size! (was %lld; is %lld). Fixing up.\n",
1940 jibp
->size
, (u_int64_t
)jfork
.cf_extents
[0].blockCount
*vcb
->blockSize
);
1942 jibp
->size
= (u_int64_t
)jfork
.cf_extents
[0].blockCount
* vcb
->blockSize
;
1946 if (jibp
->flags
& kJIJournalInFSMask
) {
1947 hfsmp
->jvp
= hfsmp
->hfs_devvp
;
1949 printf("hfs: journal not stored in fs! don't know what to do.\n");
1950 buf_brelse(jinfo_bp
);
1954 // save this off for the hack-y check in hfs_remove()
1955 hfsmp
->jnl_start
= jibp
->offset
/ SWAP_BE32(vhp
->blockSize
);
1956 hfsmp
->jnl_size
= jibp
->size
;
1958 if ((hfsmp
->hfs_flags
& HFS_READ_ONLY
) && (vfs_flags(hfsmp
->hfs_mp
) & MNT_ROOTFS
) == 0) {
1959 // if the file system is read-only, check if the journal is empty.
1960 // if it is, then we can allow the mount. otherwise we have to
1962 retval
= journal_is_clean(hfsmp
->jvp
,
1963 jibp
->offset
+ (off_t
)vcb
->hfsPlusIOPosOffset
,
1966 hfsmp
->hfs_phys_block_size
);
1970 buf_brelse(jinfo_bp
);
1973 printf("hfs: late journal init: volume on %s is read-only and journal is dirty. Can not mount volume.\n",
1980 if (jibp
->flags
& kJIJournalNeedInitMask
) {
1981 printf("hfs: Initializing the journal (joffset 0x%llx sz 0x%llx)...\n",
1982 jibp
->offset
+ (off_t
)vcb
->hfsPlusIOPosOffset
, jibp
->size
);
1983 hfsmp
->jnl
= journal_create(hfsmp
->jvp
,
1984 jibp
->offset
+ (off_t
)vcb
->hfsPlusIOPosOffset
,
1987 hfsmp
->hfs_phys_block_size
,
1990 hfs_sync_metadata
, hfsmp
->hfs_mp
);
1992 // no need to start a transaction here... if this were to fail
1993 // we'd just re-init it on the next mount.
1994 jibp
->flags
&= ~kJIJournalNeedInitMask
;
1999 // if we weren't the last person to mount this volume
2000 // then we need to throw away the journal because it
2001 // is likely that someone else mucked with the disk.
2002 // if the journal is empty this is no big deal. if the
2003 // disk is dirty this prevents us from replaying the
2004 // journal over top of changes that someone else made.
2006 arg_flags
|= JOURNAL_RESET
;
2008 //printf("hfs: Opening the journal (joffset 0x%llx sz 0x%llx vhp_blksize %d)...\n",
2009 // jibp->offset + (off_t)vcb->hfsPlusIOPosOffset,
2010 // jibp->size, SWAP_BE32(vhp->blockSize));
2012 hfsmp
->jnl
= journal_open(hfsmp
->jvp
,
2013 jibp
->offset
+ (off_t
)vcb
->hfsPlusIOPosOffset
,
2016 hfsmp
->hfs_phys_block_size
,
2019 hfs_sync_metadata
, hfsmp
->hfs_mp
);
2024 jibp
->flags
= SWAP_BE32(jibp
->flags
);
2025 jibp
->offset
= SWAP_BE64(jibp
->offset
);
2026 jibp
->size
= SWAP_BE64(jibp
->size
);
2028 buf_bwrite(jinfo_bp
);
2030 buf_brelse(jinfo_bp
);
2035 //printf("journal @ 0x%x\n", hfsmp->jnl);
2037 // if we expected the journal to be there and we couldn't
2038 // create it or open it then we have to bail out.
2039 if (hfsmp
->jnl
== NULL
) {
2040 printf("hfs: late jnl init: failed to open/create the journal (retval %d).\n", retval
);
2048 * Calculate the allocation zone for metadata.
2050 * This zone includes the following:
2051 * Allocation Bitmap file
2052 * Overflow Extents file
2055 * Clustered Hot files
2058 * METADATA ALLOCATION ZONE
2059 * ____________________________________________________________________________
2061 * | BM | JF | OEF | CATALOG |---> | HOT FILES |
2062 * |____|____|_____|_______________|______________________________|___________|
2064 * <------------------------------- N * 128 MB ------------------------------->
2067 #define GIGABYTE (u_int64_t)(1024*1024*1024)
2069 #define OVERFLOW_DEFAULT_SIZE (4*1024*1024)
2070 #define OVERFLOW_MAXIMUM_SIZE (128*1024*1024)
2071 #define JOURNAL_DEFAULT_SIZE (8*1024*1024)
2072 #define JOURNAL_MAXIMUM_SIZE (512*1024*1024)
2073 #define HOTBAND_MINIMUM_SIZE (10*1024*1024)
2074 #define HOTBAND_MAXIMUM_SIZE (512*1024*1024)
2077 hfs_metadatazone_init(struct hfsmount
*hfsmp
)
2087 vcb
= HFSTOVCB(hfsmp
);
2088 fs_size
= (u_int64_t
)vcb
->blockSize
* (u_int64_t
)vcb
->totalBlocks
;
2091 * For volumes less than 10 GB, don't bother.
2093 if (fs_size
< ((u_int64_t
)10 * GIGABYTE
))
2096 * Skip non-journaled volumes as well.
2098 if (hfsmp
->jnl
== NULL
)
2102 * Start with allocation bitmap (a fixed size).
2104 zonesize
= roundup(vcb
->totalBlocks
/ 8, vcb
->vcbVBMIOSize
);
2107 * Overflow Extents file gets 4 MB per 100 GB.
2109 items
= fs_size
/ ((u_int64_t
)100 * GIGABYTE
);
2110 filesize
= (u_int64_t
)(items
+ 1) * OVERFLOW_DEFAULT_SIZE
;
2111 if (filesize
> OVERFLOW_MAXIMUM_SIZE
)
2112 filesize
= OVERFLOW_MAXIMUM_SIZE
;
2113 zonesize
+= filesize
;
2114 hfsmp
->hfs_overflow_maxblks
= filesize
/ vcb
->blockSize
;
2117 * Plan for at least 8 MB of journal for each
2118 * 100 GB of disk space (up to a 512 MB).
2120 items
= fs_size
/ ((u_int64_t
)100 * GIGABYTE
);
2121 filesize
= (u_int64_t
)(items
+ 1) * JOURNAL_DEFAULT_SIZE
;
2122 if (filesize
> JOURNAL_MAXIMUM_SIZE
)
2123 filesize
= JOURNAL_MAXIMUM_SIZE
;
2124 zonesize
+= filesize
;
2127 * Catalog file gets 10 MB per 1 GB.
2129 * How about considering the current catalog size (used nodes * node size)
2130 * and the current file data size to help estimate the required
2133 filesize
= MIN((fs_size
/ 1024) * 10, GIGABYTE
);
2134 hfsmp
->hfs_catalog_maxblks
= filesize
/ vcb
->blockSize
;
2135 zonesize
+= filesize
;
2138 * Add space for hot file region.
2140 * ...for now, use 5 MB per 1 GB (0.5 %)
2142 filesize
= (fs_size
/ 1024) * 5;
2143 if (filesize
> HOTBAND_MAXIMUM_SIZE
)
2144 filesize
= HOTBAND_MAXIMUM_SIZE
;
2145 else if (filesize
< HOTBAND_MINIMUM_SIZE
)
2146 filesize
= HOTBAND_MINIMUM_SIZE
;
2148 * Calculate user quota file requirements.
2150 items
= QF_USERS_PER_GB
* (fs_size
/ GIGABYTE
);
2151 if (items
< QF_MIN_USERS
)
2152 items
= QF_MIN_USERS
;
2153 else if (items
> QF_MAX_USERS
)
2154 items
= QF_MAX_USERS
;
2155 if (!powerof2(items
)) {
2163 filesize
+= (items
+ 1) * sizeof(struct dqblk
);
2165 * Calculate group quota file requirements.
2168 items
= QF_GROUPS_PER_GB
* (fs_size
/ GIGABYTE
);
2169 if (items
< QF_MIN_GROUPS
)
2170 items
= QF_MIN_GROUPS
;
2171 else if (items
> QF_MAX_GROUPS
)
2172 items
= QF_MAX_GROUPS
;
2173 if (!powerof2(items
)) {
2181 filesize
+= (items
+ 1) * sizeof(struct dqblk
);
2182 zonesize
+= filesize
;
2185 * Round up entire zone to a bitmap block's worth.
2186 * The extra space goes to the catalog file and hot file area.
2189 zonesize
= roundup(zonesize
, vcb
->vcbVBMIOSize
* 8 * vcb
->blockSize
);
2190 temp
= zonesize
- temp
; /* temp has extra space */
2191 filesize
+= temp
/ 3;
2192 hfsmp
->hfs_catalog_maxblks
+= (temp
- (temp
/ 3)) / vcb
->blockSize
;
2194 hfsmp
->hfs_hotfile_maxblks
= filesize
/ vcb
->blockSize
;
2196 /* Convert to allocation blocks. */
2197 blk
= zonesize
/ vcb
->blockSize
;
2199 /* The default metadata zone location is at the start of volume. */
2200 hfsmp
->hfs_metazone_start
= 1;
2201 hfsmp
->hfs_metazone_end
= blk
- 1;
2203 /* The default hotfile area is at the end of the zone. */
2204 hfsmp
->hfs_hotfile_start
= blk
- (filesize
/ vcb
->blockSize
);
2205 hfsmp
->hfs_hotfile_end
= hfsmp
->hfs_metazone_end
;
2206 hfsmp
->hfs_hotfile_freeblks
= hfs_hotfile_freeblocks(hfsmp
);
2208 printf("HFS: metadata zone is %d to %d\n", hfsmp
->hfs_metazone_start
, hfsmp
->hfs_metazone_end
);
2209 printf("HFS: hot file band is %d to %d\n", hfsmp
->hfs_hotfile_start
, hfsmp
->hfs_hotfile_end
);
2210 printf("HFS: hot file band free blocks = %d\n", hfsmp
->hfs_hotfile_freeblks
);
2212 hfsmp
->hfs_flags
|= HFS_METADATA_ZONE
;
2217 hfs_hotfile_freeblocks(struct hfsmount
*hfsmp
)
2219 ExtendedVCB
*vcb
= HFSTOVCB(hfsmp
);
2223 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_BITMAP
, HFS_EXCLUSIVE_LOCK
);
2224 freeblocks
= MetaZoneFreeBlocks(vcb
);
2225 hfs_systemfile_unlock(hfsmp
, lockflags
);
2227 /* Minus Extents overflow file reserve. */
2229 hfsmp
->hfs_overflow_maxblks
- VTOF(hfsmp
->hfs_extents_vp
)->ff_blocks
;
2230 /* Minus catalog file reserve. */
2232 hfsmp
->hfs_catalog_maxblks
- VTOF(hfsmp
->hfs_catalog_vp
)->ff_blocks
;
2236 return MIN(freeblocks
, hfsmp
->hfs_hotfile_maxblks
);
2240 * Determine if a file is a "virtual" metadata file.
2241 * This includes journal and quota files.
2245 hfs_virtualmetafile(struct cnode
*cp
)
2250 if (cp
->c_parentcnid
!= kHFSRootFolderID
)
2253 filename
= cp
->c_desc
.cd_nameptr
;
2254 if (filename
== NULL
)
2257 if ((strcmp(filename
, ".journal") == 0) ||
2258 (strcmp(filename
, ".journal_info_block") == 0) ||
2259 (strcmp(filename
, ".quota.user") == 0) ||
2260 (strcmp(filename
, ".quota.group") == 0) ||
2261 (strcmp(filename
, ".hotfiles.btree") == 0))
2270 hfs_start_transaction(struct hfsmount
*hfsmp
)
2274 if (hfsmp
->jnl
== NULL
|| journal_owner(hfsmp
->jnl
) != current_thread()) {
2275 lck_rw_lock_shared(&hfsmp
->hfs_global_lock
);
2279 ret
= journal_start_transaction(hfsmp
->jnl
);
2281 OSAddAtomic(1, &hfsmp
->hfs_global_lock_nesting
);
2288 lck_rw_done(&hfsmp
->hfs_global_lock
);
2296 hfs_end_transaction(struct hfsmount
*hfsmp
)
2298 int need_unlock
=0, ret
;
2300 if ( hfsmp
->jnl
== NULL
2301 || ( journal_owner(hfsmp
->jnl
) == current_thread()
2302 && (OSAddAtomic(-1, &hfsmp
->hfs_global_lock_nesting
) == 1)) ) {
2308 ret
= journal_end_transaction(hfsmp
->jnl
);
2314 lck_rw_done(&hfsmp
->hfs_global_lock
);