2 * Copyright (c) 2000-2003 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
23 * @APPLE_LICENSE_HEADER_END@
25 /* @(#)hfs_vfsutils.c 4.0
27 * (c) 1997-2002 Apple Computer, Inc. All Rights Reserved
29 * hfs_vfsutils.c -- Routines that go between the HFS layer and the VFS.
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/kernel.h>
35 #include <sys/malloc.h>
37 #include <sys/mount.h>
38 #include <sys/namei.h>
42 #include <sys/unistd.h>
45 #include "hfs_catalog.h"
47 #include "hfs_mount.h"
48 #include "hfs_endian.h"
49 #include "hfs_cnode.h"
51 #include "hfscommon/headers/FileMgrInternal.h"
52 #include "hfscommon/headers/BTreesInternal.h"
53 #include "hfscommon/headers/HFSUnicodeWrappers.h"
56 extern int count_lock_queue
__P((void));
59 static void ReleaseMetaFileVNode(struct vnode
*vp
);
60 static int hfs_late_journal_init(struct hfsmount
*hfsmp
, HFSPlusVolumeHeader
*vhp
, void *_args
);
62 static void hfs_metadatazone_init(struct hfsmount
*);
63 static u_int32_t
hfs_hotfile_freeblocks(struct hfsmount
*);
67 u_int32_t
GetLogicalBlockSize(struct vnode
*vp
);
69 /* BTree accessor routines */
70 extern OSStatus
GetBTreeBlock(FileReference vp
, UInt32 blockNum
, GetBlockOptions options
, BlockDescriptor
*block
);
71 extern OSStatus
SetBTreeBlockSize(FileReference vp
, ByteCount blockSize
, ItemCount minBlockCount
);
72 extern OSStatus
ExtendBTreeFile(FileReference vp
, FSSize minEOF
, FSSize maxEOF
);
73 extern OSStatus
ReleaseBTreeBlock(FileReference vp
, BlockDescPtr blockPtr
, ReleaseBlockOptions options
);
75 //*******************************************************************************
76 // Note: Finder information in the HFS/HFS+ metadata are considered opaque and
77 // hence are not in the right byte order on little endian machines. It is
78 // the responsibility of the finder and other clients to swap the data.
79 //*******************************************************************************
81 //*******************************************************************************
82 // Routine: hfs_MountHFSVolume
85 //*******************************************************************************
86 char hfs_catname
[] = "Catalog B-tree";
87 char hfs_extname
[] = "Extents B-tree";
88 char hfs_vbmname
[] = "Volume Bitmap";
90 char hfs_privdirname
[] =
91 "\xE2\x90\x80\xE2\x90\x80\xE2\x90\x80\xE2\x90\x80HFS+ Private Data";
94 OSErr
hfs_MountHFSVolume(struct hfsmount
*hfsmp
, HFSMasterDirectoryBlock
*mdb
,
97 ExtendedVCB
*vcb
= HFSTOVCB(hfsmp
);
100 struct cat_desc cndesc
;
101 struct cat_attr cnattr
;
102 struct cat_fork fork
;
104 /* Block size must be a multiple of 512 */
105 if (SWAP_BE32(mdb
->drAlBlkSiz
) == 0 ||
106 (SWAP_BE32(mdb
->drAlBlkSiz
) & 0x01FF) != 0)
109 /* don't mount a writeable volume if its dirty, it must be cleaned by fsck_hfs */
110 if (((hfsmp
->hfs_flags
& HFS_READ_ONLY
) == 0) &&
111 ((SWAP_BE16(mdb
->drAtrb
) & kHFSVolumeUnmountedMask
) == 0)) {
114 hfsmp
->hfs_flags
|= HFS_STANDARD
;
116 * The MDB seems OK: transfer info from it into VCB
117 * Note - the VCB starts out clear (all zeros)
120 vcb
->vcbSigWord
= SWAP_BE16 (mdb
->drSigWord
);
121 vcb
->vcbCrDate
= to_bsd_time(LocalToUTC(SWAP_BE32(mdb
->drCrDate
)));
122 vcb
->localCreateDate
= SWAP_BE32 (mdb
->drCrDate
);
123 vcb
->vcbLsMod
= to_bsd_time(LocalToUTC(SWAP_BE32(mdb
->drLsMod
)));
124 vcb
->vcbAtrb
= SWAP_BE16 (mdb
->drAtrb
);
125 vcb
->vcbNmFls
= SWAP_BE16 (mdb
->drNmFls
);
126 vcb
->vcbVBMSt
= SWAP_BE16 (mdb
->drVBMSt
);
127 vcb
->nextAllocation
= SWAP_BE16 (mdb
->drAllocPtr
);
128 vcb
->totalBlocks
= SWAP_BE16 (mdb
->drNmAlBlks
);
129 vcb
->blockSize
= SWAP_BE32 (mdb
->drAlBlkSiz
);
130 vcb
->vcbClpSiz
= SWAP_BE32 (mdb
->drClpSiz
);
131 vcb
->vcbAlBlSt
= SWAP_BE16 (mdb
->drAlBlSt
);
132 vcb
->vcbNxtCNID
= SWAP_BE32 (mdb
->drNxtCNID
);
133 vcb
->freeBlocks
= SWAP_BE16 (mdb
->drFreeBks
);
134 vcb
->vcbVolBkUp
= to_bsd_time(LocalToUTC(SWAP_BE32(mdb
->drVolBkUp
)));
135 vcb
->vcbWrCnt
= SWAP_BE32 (mdb
->drWrCnt
);
136 vcb
->vcbNmRtDirs
= SWAP_BE16 (mdb
->drNmRtDirs
);
137 vcb
->vcbFilCnt
= SWAP_BE32 (mdb
->drFilCnt
);
138 vcb
->vcbDirCnt
= SWAP_BE32 (mdb
->drDirCnt
);
139 bcopy(mdb
->drFndrInfo
, vcb
->vcbFndrInfo
, sizeof(vcb
->vcbFndrInfo
));
140 if ((hfsmp
->hfs_flags
& HFS_READ_ONLY
) == 0)
141 vcb
->vcbWrCnt
++; /* Compensate for write of MDB on last flush */
143 /* convert hfs encoded name into UTF-8 string */
144 error
= hfs_to_utf8(vcb
, mdb
->drVN
, NAME_MAX
, &utf8chars
, vcb
->vcbVN
);
146 * When an HFS name cannot be encoded with the current
147 * volume encoding we use MacRoman as a fallback.
149 if (error
|| (utf8chars
== 0))
150 (void) mac_roman_to_utf8(mdb
->drVN
, NAME_MAX
, &utf8chars
, vcb
->vcbVN
);
152 hfsmp
->hfs_logBlockSize
= BestBlockSizeFit(vcb
->blockSize
, MAXBSIZE
, hfsmp
->hfs_phys_block_size
);
153 vcb
->vcbVBMIOSize
= kHFSBlockSize
;
157 bzero(&cndesc
, sizeof(cndesc
));
158 cndesc
.cd_parentcnid
= kRootParID
;
159 cndesc
.cd_flags
|= CD_ISMETA
;
160 bzero(&cnattr
, sizeof(cnattr
));
162 cnattr
.ca_mode
= S_IFREG
;
163 bzero(&fork
, sizeof(fork
));
166 * Set up Extents B-tree vnode
168 cndesc
.cd_nameptr
= hfs_extname
;
169 cndesc
.cd_namelen
= strlen(hfs_extname
);
170 cndesc
.cd_cnid
= cnattr
.ca_fileid
= kHFSExtentsFileID
;
171 fork
.cf_size
= SWAP_BE32(mdb
->drXTFlSize
);
172 fork
.cf_blocks
= fork
.cf_size
/ vcb
->blockSize
;
173 fork
.cf_clump
= SWAP_BE32(mdb
->drXTClpSiz
);
175 fork
.cf_extents
[0].startBlock
= SWAP_BE16(mdb
->drXTExtRec
[0].startBlock
);
176 fork
.cf_extents
[0].blockCount
= SWAP_BE16(mdb
->drXTExtRec
[0].blockCount
);
177 fork
.cf_extents
[1].startBlock
= SWAP_BE16(mdb
->drXTExtRec
[1].startBlock
);
178 fork
.cf_extents
[1].blockCount
= SWAP_BE16(mdb
->drXTExtRec
[1].blockCount
);
179 fork
.cf_extents
[2].startBlock
= SWAP_BE16(mdb
->drXTExtRec
[2].startBlock
);
180 fork
.cf_extents
[2].blockCount
= SWAP_BE16(mdb
->drXTExtRec
[2].blockCount
);
181 cnattr
.ca_blocks
= fork
.cf_blocks
;
183 error
= hfs_getnewvnode(hfsmp
, NULL
, &cndesc
, 0, &cnattr
, &fork
,
184 &vcb
->extentsRefNum
);
185 if (error
) goto MtVolErr
;
186 error
= MacToVFSError(BTOpenPath(VTOF(vcb
->extentsRefNum
),
187 (KeyCompareProcPtr
)CompareExtentKeys
));
189 VOP_UNLOCK(vcb
->extentsRefNum
, 0, p
);
194 * Set up Catalog B-tree vnode...
196 cndesc
.cd_nameptr
= hfs_catname
;
197 cndesc
.cd_namelen
= strlen(hfs_catname
);
198 cndesc
.cd_cnid
= cnattr
.ca_fileid
= kHFSCatalogFileID
;
199 fork
.cf_size
= SWAP_BE32(mdb
->drCTFlSize
);
200 fork
.cf_blocks
= fork
.cf_size
/ vcb
->blockSize
;
201 fork
.cf_clump
= SWAP_BE32(mdb
->drCTClpSiz
);
203 fork
.cf_extents
[0].startBlock
= SWAP_BE16(mdb
->drCTExtRec
[0].startBlock
);
204 fork
.cf_extents
[0].blockCount
= SWAP_BE16(mdb
->drCTExtRec
[0].blockCount
);
205 fork
.cf_extents
[1].startBlock
= SWAP_BE16(mdb
->drCTExtRec
[1].startBlock
);
206 fork
.cf_extents
[1].blockCount
= SWAP_BE16(mdb
->drCTExtRec
[1].blockCount
);
207 fork
.cf_extents
[2].startBlock
= SWAP_BE16(mdb
->drCTExtRec
[2].startBlock
);
208 fork
.cf_extents
[2].blockCount
= SWAP_BE16(mdb
->drCTExtRec
[2].blockCount
);
209 cnattr
.ca_blocks
= fork
.cf_blocks
;
211 error
= hfs_getnewvnode(hfsmp
, NULL
, &cndesc
, 0, &cnattr
, &fork
,
212 &vcb
->catalogRefNum
);
214 VOP_UNLOCK(vcb
->extentsRefNum
, 0, p
);
217 error
= MacToVFSError(BTOpenPath(VTOF(vcb
->catalogRefNum
),
218 (KeyCompareProcPtr
)CompareCatalogKeys
));
220 VOP_UNLOCK(vcb
->catalogRefNum
, 0, p
);
221 VOP_UNLOCK(vcb
->extentsRefNum
, 0, p
);
225 /* mark the volume dirty (clear clean unmount bit) */
226 vcb
->vcbAtrb
&= ~kHFSVolumeUnmountedMask
;
229 * all done with b-trees so we can unlock now...
231 VOP_UNLOCK(vcb
->catalogRefNum
, 0, p
);
232 VOP_UNLOCK(vcb
->extentsRefNum
, 0, p
);
234 if ( error
== noErr
)
236 if ( !(vcb
->vcbAtrb
& kHFSVolumeHardwareLockMask
) ) // if the disk is not write protected
238 MarkVCBDirty( vcb
); // mark VCB dirty so it will be written
243 //-- Release any resources allocated so far before exiting with an error:
245 ReleaseMetaFileVNode(vcb
->catalogRefNum
);
246 ReleaseMetaFileVNode(vcb
->extentsRefNum
);
252 //*******************************************************************************
253 // Routine: hfs_MountHFSPlusVolume
256 //*******************************************************************************
259 OSErr
hfs_MountHFSPlusVolume(struct hfsmount
*hfsmp
, HFSPlusVolumeHeader
*vhp
,
260 off_t embeddedOffset
, u_int64_t disksize
, struct proc
*p
, void *args
)
262 register ExtendedVCB
*vcb
;
263 struct cat_desc cndesc
;
264 struct cat_attr cnattr
;
265 struct cat_fork cfork
;
267 u_int64_t volumesize
;
268 struct BTreeInfoRec btinfo
;
274 signature
= SWAP_BE16(vhp
->signature
);
275 version
= SWAP_BE16(vhp
->version
);
277 if (signature
== kHFSPlusSigWord
) {
278 if (version
!= kHFSPlusVersion
) {
279 printf("hfs_mount: invalid HFS+ version: %d\n", version
);
282 } else if (signature
== kHFSXSigWord
) {
283 if (version
!= kHFSXVersion
) {
284 printf("hfs_mount: invalid HFSX version: %d\n", version
);
287 /* The in-memory signature is always 'H+'. */
288 signature
= kHFSPlusSigWord
;
289 hfsmp
->hfs_flags
|= HFS_X
;
291 printf("hfs_mount: invalid HFS+ sig 0x%04x\n", signature
);
295 /* Block size must be at least 512 and a power of 2 */
296 blockSize
= SWAP_BE32(vhp
->blockSize
);
297 if (blockSize
< 512 || !powerof2(blockSize
))
300 /* don't mount a writable volume if its dirty, it must be cleaned by fsck_hfs */
301 if ((hfsmp
->hfs_flags
& HFS_READ_ONLY
) == 0 && hfsmp
->jnl
== NULL
&&
302 (SWAP_BE32(vhp
->attributes
) & kHFSVolumeUnmountedMask
) == 0)
305 /* Make sure we can live with the physical block size. */
306 if ((disksize
& (hfsmp
->hfs_phys_block_size
- 1)) ||
307 (embeddedOffset
& (hfsmp
->hfs_phys_block_size
- 1)) ||
308 (blockSize
< hfsmp
->hfs_phys_block_size
)) {
312 * The VolumeHeader seems OK: transfer info from it into VCB
313 * Note - the VCB starts out clear (all zeros)
315 vcb
= HFSTOVCB(hfsmp
);
317 vcb
->vcbSigWord
= signature
;
318 vcb
->vcbJinfoBlock
= SWAP_BE32(vhp
->journalInfoBlock
);
319 vcb
->vcbLsMod
= to_bsd_time(SWAP_BE32(vhp
->modifyDate
));
320 vcb
->vcbAtrb
= (UInt16
)SWAP_BE32(vhp
->attributes
);
321 vcb
->vcbClpSiz
= SWAP_BE32(vhp
->rsrcClumpSize
);
322 vcb
->vcbNxtCNID
= SWAP_BE32(vhp
->nextCatalogID
);
323 vcb
->vcbVolBkUp
= to_bsd_time(SWAP_BE32(vhp
->backupDate
));
324 vcb
->vcbWrCnt
= SWAP_BE32(vhp
->writeCount
);
325 vcb
->vcbFilCnt
= SWAP_BE32(vhp
->fileCount
);
326 vcb
->vcbDirCnt
= SWAP_BE32(vhp
->folderCount
);
328 /* copy 32 bytes of Finder info */
329 bcopy(vhp
->finderInfo
, vcb
->vcbFndrInfo
, sizeof(vhp
->finderInfo
));
331 vcb
->vcbAlBlSt
= 0; /* hfs+ allocation blocks start at first block of volume */
332 if ((hfsmp
->hfs_flags
& HFS_READ_ONLY
) == 0)
333 vcb
->vcbWrCnt
++; /* compensate for write of Volume Header on last flush */
337 /* Now fill in the Extended VCB info */
338 vcb
->nextAllocation
= SWAP_BE32(vhp
->nextAllocation
);
339 vcb
->totalBlocks
= SWAP_BE32(vhp
->totalBlocks
);
340 vcb
->freeBlocks
= SWAP_BE32(vhp
->freeBlocks
);
341 vcb
->blockSize
= blockSize
;
342 vcb
->encodingsBitmap
= SWAP_BE64(vhp
->encodingsBitmap
);
343 vcb
->localCreateDate
= SWAP_BE32(vhp
->createDate
);
345 vcb
->hfsPlusIOPosOffset
= embeddedOffset
;
347 /* Default to no free block reserve */
348 vcb
->reserveBlocks
= 0;
351 * Update the logical block size in the mount struct
352 * (currently set up from the wrapper MDB) using the
353 * new blocksize value:
355 hfsmp
->hfs_logBlockSize
= BestBlockSizeFit(vcb
->blockSize
, MAXBSIZE
, hfsmp
->hfs_phys_block_size
);
356 vcb
->vcbVBMIOSize
= min(vcb
->blockSize
, MAXPHYSIO
);
358 bzero(&cndesc
, sizeof(cndesc
));
359 cndesc
.cd_parentcnid
= kRootParID
;
360 cndesc
.cd_flags
|= CD_ISMETA
;
361 bzero(&cnattr
, sizeof(cnattr
));
363 cnattr
.ca_mode
= S_IFREG
;
366 * Set up Extents B-tree vnode
368 cndesc
.cd_nameptr
= hfs_extname
;
369 cndesc
.cd_namelen
= strlen(hfs_extname
);
370 cndesc
.cd_cnid
= cnattr
.ca_fileid
= kHFSExtentsFileID
;
372 cfork
.cf_size
= SWAP_BE64 (vhp
->extentsFile
.logicalSize
);
373 cfork
.cf_clump
= SWAP_BE32 (vhp
->extentsFile
.clumpSize
);
374 cfork
.cf_blocks
= SWAP_BE32 (vhp
->extentsFile
.totalBlocks
);
375 cfork
.cf_vblocks
= 0;
376 cnattr
.ca_blocks
= cfork
.cf_blocks
;
377 for (i
= 0; i
< kHFSPlusExtentDensity
; i
++) {
378 cfork
.cf_extents
[i
].startBlock
=
379 SWAP_BE32 (vhp
->extentsFile
.extents
[i
].startBlock
);
380 cfork
.cf_extents
[i
].blockCount
=
381 SWAP_BE32 (vhp
->extentsFile
.extents
[i
].blockCount
);
383 retval
= hfs_getnewvnode(hfsmp
, NULL
, &cndesc
, 0, &cnattr
, &cfork
,
384 &vcb
->extentsRefNum
);
386 if (retval
) goto ErrorExit
;
387 retval
= MacToVFSError(BTOpenPath(VTOF(vcb
->extentsRefNum
),
388 (KeyCompareProcPtr
) CompareExtentKeysPlus
));
390 VOP_UNLOCK(vcb
->extentsRefNum
, 0, p
);
395 * Set up Catalog B-tree vnode
397 cndesc
.cd_nameptr
= hfs_catname
;
398 cndesc
.cd_namelen
= strlen(hfs_catname
);
399 cndesc
.cd_cnid
= cnattr
.ca_fileid
= kHFSCatalogFileID
;
401 cfork
.cf_size
= SWAP_BE64 (vhp
->catalogFile
.logicalSize
);
402 cfork
.cf_clump
= SWAP_BE32 (vhp
->catalogFile
.clumpSize
);
403 cfork
.cf_blocks
= SWAP_BE32 (vhp
->catalogFile
.totalBlocks
);
404 cfork
.cf_vblocks
= 0;
405 cnattr
.ca_blocks
= cfork
.cf_blocks
;
406 for (i
= 0; i
< kHFSPlusExtentDensity
; i
++) {
407 cfork
.cf_extents
[i
].startBlock
=
408 SWAP_BE32 (vhp
->catalogFile
.extents
[i
].startBlock
);
409 cfork
.cf_extents
[i
].blockCount
=
410 SWAP_BE32 (vhp
->catalogFile
.extents
[i
].blockCount
);
412 retval
= hfs_getnewvnode(hfsmp
, NULL
, &cndesc
, 0, &cnattr
, &cfork
,
413 &vcb
->catalogRefNum
);
415 VOP_UNLOCK(vcb
->extentsRefNum
, 0, p
);
418 retval
= MacToVFSError(BTOpenPath(VTOF(vcb
->catalogRefNum
),
419 (KeyCompareProcPtr
) CompareExtendedCatalogKeys
));
421 VOP_UNLOCK(vcb
->catalogRefNum
, 0, p
);
422 VOP_UNLOCK(vcb
->extentsRefNum
, 0, p
);
425 if ((hfsmp
->hfs_flags
& HFS_X
) &&
426 BTGetInformation(VTOF(vcb
->catalogRefNum
), 0, &btinfo
) == 0) {
427 if (btinfo
.keyCompareType
== kHFSBinaryCompare
) {
428 hfsmp
->hfs_flags
|= HFS_CASE_SENSITIVE
;
429 /* Install a case-sensitive key compare */
430 (void) BTOpenPath(VTOF(vcb
->catalogRefNum
),
431 (KeyCompareProcPtr
)cat_binarykeycompare
);
436 * Set up Allocation file vnode
438 cndesc
.cd_nameptr
= hfs_vbmname
;
439 cndesc
.cd_namelen
= strlen(hfs_vbmname
);
440 cndesc
.cd_cnid
= cnattr
.ca_fileid
= kHFSAllocationFileID
;
442 cfork
.cf_size
= SWAP_BE64 (vhp
->allocationFile
.logicalSize
);
443 cfork
.cf_clump
= SWAP_BE32 (vhp
->allocationFile
.clumpSize
);
444 cfork
.cf_blocks
= SWAP_BE32 (vhp
->allocationFile
.totalBlocks
);
445 cfork
.cf_vblocks
= 0;
446 cnattr
.ca_blocks
= cfork
.cf_blocks
;
447 for (i
= 0; i
< kHFSPlusExtentDensity
; i
++) {
448 cfork
.cf_extents
[i
].startBlock
=
449 SWAP_BE32 (vhp
->allocationFile
.extents
[i
].startBlock
);
450 cfork
.cf_extents
[i
].blockCount
=
451 SWAP_BE32 (vhp
->allocationFile
.extents
[i
].blockCount
);
453 retval
= hfs_getnewvnode(hfsmp
, NULL
, &cndesc
, 0, &cnattr
, &cfork
,
454 &vcb
->allocationsRefNum
);
456 VOP_UNLOCK(vcb
->catalogRefNum
, 0, p
);
457 VOP_UNLOCK(vcb
->extentsRefNum
, 0, p
);
461 /* Pick up volume name and create date */
462 retval
= cat_idlookup(hfsmp
, kHFSRootFolderID
, &cndesc
, &cnattr
, NULL
);
464 VOP_UNLOCK(vcb
->allocationsRefNum
, 0, p
);
465 VOP_UNLOCK(vcb
->catalogRefNum
, 0, p
);
466 VOP_UNLOCK(vcb
->extentsRefNum
, 0, p
);
469 vcb
->vcbCrDate
= cnattr
.ca_itime
;
470 vcb
->volumeNameEncodingHint
= cndesc
.cd_encoding
;
471 bcopy(cndesc
.cd_nameptr
, vcb
->vcbVN
, min(255, cndesc
.cd_namelen
));
472 cat_releasedesc(&cndesc
);
474 /* mark the volume dirty (clear clean unmount bit) */
475 vcb
->vcbAtrb
&= ~kHFSVolumeUnmountedMask
;
476 if (hfsmp
->jnl
&& (hfsmp
->hfs_flags
& HFS_READ_ONLY
) == 0) {
477 hfs_flushvolumeheader(hfsmp
, TRUE
, TRUE
);
481 * all done with metadata files so we can unlock now...
483 VOP_UNLOCK(vcb
->allocationsRefNum
, 0, p
);
484 VOP_UNLOCK(vcb
->catalogRefNum
, 0, p
);
485 VOP_UNLOCK(vcb
->extentsRefNum
, 0, p
);
488 // Check if we need to do late journal initialization. This only
489 // happens if a previous version of MacOS X (or 9) touched the disk.
490 // In that case hfs_late_journal_init() will go re-locate the journal
491 // and journal_info_block files and validate that they're still kosher.
493 if ( (vcb
->vcbAtrb
& kHFSVolumeJournaledMask
)
494 && (SWAP_BE32(vhp
->lastMountedVersion
) != kHFSJMountVersion
)
495 && (hfsmp
->jnl
== NULL
)) {
497 retval
= hfs_late_journal_init(hfsmp
, vhp
, args
);
501 } else if (hfsmp
->jnl
) {
502 hfsmp
->hfs_mp
->mnt_flag
|= MNT_JOURNALED
;
504 } else if (hfsmp
->jnl
) {
505 struct cat_attr jinfo_attr
, jnl_attr
;
507 // if we're here we need to fill in the fileid's for the
508 // journal and journal_info_block.
509 hfsmp
->hfs_jnlinfoblkid
= GetFileInfo(vcb
, kRootDirID
, ".journal_info_block", &jinfo_attr
, NULL
);
510 hfsmp
->hfs_jnlfileid
= GetFileInfo(vcb
, kRootDirID
, ".journal", &jnl_attr
, NULL
);
511 if (hfsmp
->hfs_jnlinfoblkid
== 0 || hfsmp
->hfs_jnlfileid
== 0) {
512 printf("hfs: danger! couldn't find the file-id's for the journal or journal_info_block\n");
513 printf("hfs: jnlfileid %d, jnlinfoblkid %d\n", hfsmp
->hfs_jnlfileid
, hfsmp
->hfs_jnlinfoblkid
);
518 * Establish a metadata allocation zone.
520 hfs_metadatazone_init(hfsmp
);
523 * Make any metadata zone adjustments.
525 if (hfsmp
->hfs_flags
& HFS_METADATA_ZONE
) {
526 /* Keep the roving allocator out of the metadata zone. */
527 if (vcb
->nextAllocation
>= hfsmp
->hfs_metazone_start
&&
528 vcb
->nextAllocation
<= hfsmp
->hfs_metazone_end
) {
529 vcb
->nextAllocation
= hfsmp
->hfs_metazone_end
+ 1;
533 /* setup private/hidden directory for unlinked files */
534 FindMetaDataDirectory(vcb
);
535 if (hfsmp
->jnl
&& ((hfsmp
->hfs_flags
& HFS_READ_ONLY
) == 0))
536 hfs_remove_orphans(hfsmp
);
538 if ( !(vcb
->vcbAtrb
& kHFSVolumeHardwareLockMask
) ) // if the disk is not write protected
540 MarkVCBDirty( vcb
); // mark VCB dirty so it will be written
545 * Allow hot file clustering if conditions allow.
547 if ((hfsmp
->hfs_flags
& HFS_METADATA_ZONE
) &&
548 ((hfsmp
->hfs_flags
& HFS_READ_ONLY
) == 0)) {
549 (void) hfs_recording_init(hfsmp
, p
);
556 * A fatal error occured and the volume cannot be mounted
557 * release any resources that we aquired...
560 InvalidateCatalogCache(vcb
);
561 ReleaseMetaFileVNode(vcb
->allocationsRefNum
);
562 ReleaseMetaFileVNode(vcb
->catalogRefNum
);
563 ReleaseMetaFileVNode(vcb
->extentsRefNum
);
570 * ReleaseMetaFileVNode
574 static void ReleaseMetaFileVNode(struct vnode
*vp
)
578 if (vp
&& (fp
= VTOF(vp
))) {
579 if (fp
->fcbBTCBPtr
!= NULL
)
580 (void) BTClosePath(fp
);
582 /* release the node even if BTClosePath fails */
589 /*************************************************************
591 * Unmounts a hfs volume.
592 * At this point vflush() has been called (to dump all non-metadata files)
594 *************************************************************/
598 hfsUnmount( register struct hfsmount
*hfsmp
, struct proc
*p
)
600 ExtendedVCB
*vcb
= HFSTOVCB(hfsmp
);
603 InvalidateCatalogCache( vcb
);
605 if (hfsmp
->hfc_filevp
) {
606 ReleaseMetaFileVNode(hfsmp
->hfc_filevp
);
607 hfsmp
->hfc_filevp
= NULL
;
610 if (vcb
->vcbSigWord
== kHFSPlusSigWord
)
611 ReleaseMetaFileVNode(vcb
->allocationsRefNum
);
613 ReleaseMetaFileVNode(vcb
->catalogRefNum
);
614 ReleaseMetaFileVNode(vcb
->extentsRefNum
);
621 * Test is fork has overflow extents.
625 overflow_extents(struct filefork
*fp
)
629 if (VTOVCB(FTOV(fp
))->vcbSigWord
== kHFSPlusSigWord
) {
630 if (fp
->ff_extents
[7].blockCount
== 0)
633 blocks
= fp
->ff_extents
[0].blockCount
+
634 fp
->ff_extents
[1].blockCount
+
635 fp
->ff_extents
[2].blockCount
+
636 fp
->ff_extents
[3].blockCount
+
637 fp
->ff_extents
[4].blockCount
+
638 fp
->ff_extents
[5].blockCount
+
639 fp
->ff_extents
[6].blockCount
+
640 fp
->ff_extents
[7].blockCount
;
642 if (fp
->ff_extents
[2].blockCount
== 0)
645 blocks
= fp
->ff_extents
[0].blockCount
+
646 fp
->ff_extents
[1].blockCount
+
647 fp
->ff_extents
[2].blockCount
;
650 return (fp
->ff_blocks
> blocks
);
655 * Lock/Unlock a metadata file.
659 hfs_metafilelocking(struct hfsmount
*hfsmp
, u_long fileID
, u_int flags
, struct proc
*p
)
662 struct vnode
*vp
= NULL
;
663 int numOfLockedBuffs
;
666 vcb
= HFSTOVCB(hfsmp
);
669 case kHFSExtentsFileID
:
670 vp
= vcb
->extentsRefNum
;
673 case kHFSCatalogFileID
:
674 vp
= vcb
->catalogRefNum
;
677 case kHFSAllocationFileID
:
678 /* bitmap is covered by Extents B-tree locking */
681 panic("hfs_lockmetafile: invalid fileID");
684 if ((flags
& LK_TYPE_MASK
) != LK_RELEASE
) {
686 } else if (hfsmp
->jnl
== NULL
) {
687 struct timeval tv
= time
;
688 u_int32_t lastfsync
= tv
.tv_sec
;
690 (void) BTGetLastSync((FCB
*)VTOF(vp
), &lastfsync
);
692 numOfLockedBuffs
= count_lock_queue();
693 if ((numOfLockedBuffs
> kMaxLockedMetaBuffers
) ||
694 ((numOfLockedBuffs
> 1) && ((tv
.tv_sec
- lastfsync
) > kMaxSecsForFsync
))) {
695 hfs_btsync(vp
, HFS_SYNCTRANS
);
699 retval
= lockmgr(&VTOC(vp
)->c_lock
, flags
, &vp
->v_interlock
, p
);
707 * Check to see if a vnode is locked in the current context
708 * This is to be used for debugging purposes only!!
711 void RequireFileLock(FileReference vp
, int shareable
)
713 struct lock__bsd__
*lkp
;
718 pid
= current_proc()->p_pid
;
719 self
= (void *) current_act();
720 lkp
= &VTOC(vp
)->c_lock
;
722 simple_lock(&lkp
->lk_interlock
);
724 if (shareable
&& (lkp
->lk_sharecount
> 0) && (lkp
->lk_lockholder
== LK_NOPROC
))
726 else if ((lkp
->lk_exclusivecount
> 0) && (lkp
->lk_lockholder
== pid
) && (lkp
->lk_lockthread
== self
))
729 simple_unlock(&lkp
->lk_interlock
);
732 switch (VTOC(vp
)->c_fileid
) {
734 DEBUG_BREAK_MSG((" #\n # RequireFileLock: extent btree vnode not locked! v: 0x%08X\n #\n", (u_int
)vp
));
738 DEBUG_BREAK_MSG((" #\n # RequireFileLock: catalog btree vnode not locked! v: 0x%08X\n #\n", (u_int
)vp
));
742 DEBUG_BREAK_MSG((" #\n # RequireFileLock: file (%d) not locked! v: 0x%08X\n #\n", VTOC(vp
)->c_fileid
, (u_int
)vp
));
751 * There are three ways to qualify for ownership rights on an object:
753 * 1. (a) Your UID matches the cnode's UID.
754 * (b) The object in question is owned by "unknown"
755 * 2. (a) Permissions on the filesystem are being ignored and
756 * your UID matches the replacement UID.
757 * (b) Permissions on the filesystem are being ignored and
758 * the replacement UID is "unknown".
763 hfs_owner_rights(struct hfsmount
*hfsmp
, uid_t cnode_uid
, struct ucred
*cred
,
764 struct proc
*p
, int invokesuperuserstatus
)
766 if ((cred
->cr_uid
== cnode_uid
) || /* [1a] */
767 (cnode_uid
== UNKNOWNUID
) || /* [1b] */
768 ((HFSTOVFS(hfsmp
)->mnt_flag
& MNT_UNKNOWNPERMISSIONS
) && /* [2] */
769 ((cred
->cr_uid
== hfsmp
->hfs_uid
) || /* [2a] */
770 (hfsmp
->hfs_uid
== UNKNOWNUID
))) || /* [2b] */
771 (invokesuperuserstatus
&& (suser(cred
, &p
->p_acflag
) == 0))) { /* [3] */
779 unsigned long BestBlockSizeFit(unsigned long allocationBlockSize
,
780 unsigned long blockSizeLimit
,
781 unsigned long baseMultiple
) {
783 Compute the optimal (largest) block size (no larger than allocationBlockSize) that is less than the
784 specified limit but still an even multiple of the baseMultiple.
786 int baseBlockCount
, blockCount
;
787 unsigned long trialBlockSize
;
789 if (allocationBlockSize
% baseMultiple
!= 0) {
791 Whoops: the allocation blocks aren't even multiples of the specified base:
792 no amount of dividing them into even parts will be a multiple, either then!
794 return 512; /* Hope for the best */
797 /* Try the obvious winner first, to prevent 12K allocation blocks, for instance,
798 from being handled as two 6K logical blocks instead of 3 4K logical blocks.
799 Even though the former (the result of the loop below) is the larger allocation
800 block size, the latter is more efficient: */
801 if (allocationBlockSize
% PAGE_SIZE
== 0) return PAGE_SIZE
;
803 /* No clear winner exists: pick the largest even fraction <= MAXBSIZE: */
804 baseBlockCount
= allocationBlockSize
/ baseMultiple
; /* Now guaranteed to be an even multiple */
806 for (blockCount
= baseBlockCount
; blockCount
> 0; --blockCount
) {
807 trialBlockSize
= blockCount
* baseMultiple
;
808 if (allocationBlockSize
% trialBlockSize
== 0) { /* An even multiple? */
809 if ((trialBlockSize
<= blockSizeLimit
) &&
810 (trialBlockSize
% baseMultiple
== 0)) {
811 return trialBlockSize
;
816 /* Note: we should never get here, since blockCount = 1 should always work,
817 but this is nice and safe and makes the compiler happy, too ... */
823 * To make the HFS Plus filesystem follow UFS unlink semantics, a remove
824 * of an active vnode is translated to a move/rename so the file appears
825 * deleted. The destination folder for these move/renames is setup here
826 * and a reference to it is place in hfsmp->hfs_privdir_desc.
830 FindMetaDataDirectory(ExtendedVCB
*vcb
)
832 struct hfsmount
* hfsmp
;
833 struct vnode
* dvp
= NULL
;
834 struct cnode
* dcp
= NULL
;
835 struct FndrDirInfo
* fndrinfo
;
836 struct cat_desc out_desc
= {0};
837 struct proc
*p
= current_proc();
842 if (vcb
->vcbSigWord
!= kHFSPlusSigWord
)
845 hfsmp
= VCBTOHFS(vcb
);
847 if (hfsmp
->hfs_privdir_desc
.cd_parentcnid
== 0) {
848 hfsmp
->hfs_privdir_desc
.cd_parentcnid
= kRootDirID
;
849 hfsmp
->hfs_privdir_desc
.cd_nameptr
= hfs_privdirname
;
850 hfsmp
->hfs_privdir_desc
.cd_namelen
= strlen(hfs_privdirname
);
851 hfsmp
->hfs_privdir_desc
.cd_flags
= CD_ISDIR
;
854 /* Lock catalog b-tree */
855 if (hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_SHARED
, p
) != 0)
858 error
= cat_lookup(hfsmp
, &hfsmp
->hfs_privdir_desc
, 0, NULL
,
859 &hfsmp
->hfs_privdir_attr
, NULL
);
861 /* Unlock catalog b-tree */
862 (void) hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_RELEASE
, p
);
865 hfsmp
->hfs_metadata_createdate
= hfsmp
->hfs_privdir_attr
.ca_itime
;
866 hfsmp
->hfs_privdir_desc
.cd_cnid
= hfsmp
->hfs_privdir_attr
.ca_fileid
;
868 * Clear the system immutable flag if set...
870 if ((hfsmp
->hfs_privdir_attr
.ca_flags
& SF_IMMUTABLE
) &&
871 (hfsmp
->hfs_flags
& HFS_READ_ONLY
) == 0) {
872 hfsmp
->hfs_privdir_attr
.ca_flags
&= ~SF_IMMUTABLE
;
874 hfs_global_shared_lock_acquire(hfsmp
);
876 if ((error
= journal_start_transaction(hfsmp
->jnl
)) != 0) {
877 hfs_global_shared_lock_release(hfsmp
);
878 return (hfsmp
->hfs_privdir_attr
.ca_fileid
);
881 if (hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_SHARED
, p
) == 0) {
882 (void)cat_update(hfsmp
, &hfsmp
->hfs_privdir_desc
,
883 &hfsmp
->hfs_privdir_attr
, NULL
, NULL
);
884 (void) hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_RELEASE
, p
);
887 journal_end_transaction(hfsmp
->jnl
);
889 hfs_global_shared_lock_release(hfsmp
);
891 return (hfsmp
->hfs_privdir_attr
.ca_fileid
);
893 } else if (hfsmp
->hfs_flags
& HFS_READ_ONLY
) {
898 /* Setup the default attributes */
899 bzero(&hfsmp
->hfs_privdir_attr
, sizeof(struct cat_attr
));
900 hfsmp
->hfs_privdir_attr
.ca_mode
= S_IFDIR
;
901 hfsmp
->hfs_privdir_attr
.ca_nlink
= 2;
902 hfsmp
->hfs_privdir_attr
.ca_itime
= vcb
->vcbCrDate
;
903 hfsmp
->hfs_privdir_attr
.ca_mtime
= time
.tv_sec
;
905 /* hidden and off the desktop view */
906 fndrinfo
= (struct FndrDirInfo
*)&hfsmp
->hfs_privdir_attr
.ca_finderinfo
;
907 fndrinfo
->frLocation
.v
= SWAP_BE16 (22460);
908 fndrinfo
->frLocation
.h
= SWAP_BE16 (22460);
909 fndrinfo
->frFlags
|= SWAP_BE16 (kIsInvisible
+ kNameLocked
);
912 hfs_global_shared_lock_acquire(hfsmp
);
914 if ((error
= journal_start_transaction(hfsmp
->jnl
)) != 0) {
915 hfs_global_shared_lock_release(hfsmp
);
919 /* Reserve some space in the Catalog file. */
920 if (cat_preflight(hfsmp
, CAT_CREATE
, &cookie
, p
) != 0) {
922 journal_end_transaction(hfsmp
->jnl
);
924 hfs_global_shared_lock_release(hfsmp
);
928 if (hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_EXCLUSIVE
, p
) == 0) {
929 error
= cat_create(hfsmp
, &hfsmp
->hfs_privdir_desc
,
930 &hfsmp
->hfs_privdir_attr
, &out_desc
);
932 (void) hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_RELEASE
, p
);
935 cat_postflight(hfsmp
, &cookie
, p
);
939 journal_end_transaction(hfsmp
->jnl
);
941 hfs_global_shared_lock_release(hfsmp
);
946 hfsmp
->hfs_privdir_desc
.cd_hint
= out_desc
.cd_hint
;
947 hfsmp
->hfs_privdir_desc
.cd_cnid
= out_desc
.cd_cnid
;
948 hfsmp
->hfs_privdir_attr
.ca_fileid
= out_desc
.cd_cnid
;
949 hfsmp
->hfs_metadata_createdate
= vcb
->vcbCrDate
;
951 if (VFS_ROOT(HFSTOVFS(hfsmp
), &dvp
) == 0) {
953 dcp
->c_childhint
= out_desc
.cd_hint
;
956 dcp
->c_flag
|= C_CHANGE
| C_UPDATE
;
958 (void) VOP_UPDATE(dvp
, &tv
, &tv
, 0);
961 hfs_volupdate(hfsmp
, VOL_MKDIR
, 1);
963 journal_end_transaction(hfsmp
->jnl
);
965 hfs_global_shared_lock_release(hfsmp
);
967 cat_releasedesc(&out_desc
);
969 return (out_desc
.cd_cnid
);
974 GetFileInfo(ExtendedVCB
*vcb
, u_int32_t dirid
, char *name
,
975 struct cat_attr
*fattr
, struct cat_fork
*forkinfo
)
977 struct hfsmount
* hfsmp
;
978 struct vnode
* dvp
= NULL
;
979 struct cnode
* dcp
= NULL
;
980 struct FndrDirInfo
* fndrinfo
;
981 struct cat_desc jdesc
;
985 if (vcb
->vcbSigWord
!= kHFSPlusSigWord
)
988 hfsmp
= VCBTOHFS(vcb
);
990 memset(&jdesc
, 0, sizeof(struct cat_desc
));
991 jdesc
.cd_parentcnid
= kRootDirID
;
992 jdesc
.cd_nameptr
= name
;
993 jdesc
.cd_namelen
= strlen(name
);
995 /* Lock catalog b-tree */
996 error
= hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_EXCLUSIVE
, current_proc());
1000 error
= cat_lookup(hfsmp
, &jdesc
, 0, NULL
, fattr
, forkinfo
);
1002 (void) hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_RELEASE
, current_proc());
1005 return (fattr
->ca_fileid
);
1006 } else if (hfsmp
->hfs_flags
& HFS_READ_ONLY
) {
1013 * On Journaled HFS, there can be orphaned files. These
1014 * are files that were unlinked while busy. If the volume
1015 * was not cleanly unmounted then some of these files may
1016 * have persisted and need to be removed.
1020 hfs_remove_orphans(struct hfsmount
* hfsmp
)
1022 struct BTreeIterator
* iterator
= NULL
;
1023 struct FSBufferDescriptor btdata
;
1024 struct HFSPlusCatalogFile filerec
;
1025 struct HFSPlusCatalogKey
* keyp
;
1026 struct proc
*p
= current_proc();
1032 cat_cookie_t cookie
= {0};
1036 int shared_lock
= 0;
1039 if (hfsmp
->hfs_flags
& HFS_CLEANED_ORPHANS
)
1042 vcb
= HFSTOVCB(hfsmp
);
1043 fcb
= VTOF(vcb
->catalogRefNum
);
1045 btdata
.bufferAddress
= &filerec
;
1046 btdata
.itemSize
= sizeof(filerec
);
1047 btdata
.itemCount
= 1;
1049 MALLOC(iterator
, struct BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
1050 bzero(iterator
, sizeof(*iterator
));
1051 keyp
= (HFSPlusCatalogKey
*)&iterator
->key
;
1052 keyp
->parentID
= hfsmp
->hfs_privdir_desc
.cd_cnid
;
1054 result
= hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_EXCLUSIVE
, p
);
1058 * Position the iterator at the folder thread record.
1059 * (i.e. one record before first child)
1061 result
= BTSearchRecord(fcb
, iterator
, NULL
, NULL
, iterator
);
1063 (void) hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_RELEASE
, p
);
1067 /* Visit all the children in the HFS+ private directory. */
1069 result
= hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_EXCLUSIVE
, p
);
1073 result
= BTIterateRecord(fcb
, kBTreeNextRecord
, iterator
, &btdata
, NULL
);
1075 (void) hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_RELEASE
, p
);
1079 if (keyp
->parentID
!= hfsmp
->hfs_privdir_desc
.cd_cnid
)
1081 if (filerec
.recordType
!= kHFSPlusFileRecord
)
1084 (void) utf8_encodestr(keyp
->nodeName
.unicode
, keyp
->nodeName
.length
* 2,
1085 filename
, &namelen
, sizeof(filename
), 0, 0);
1087 (void) sprintf(tempname
, "%s%d", HFS_DELETE_PREFIX
, filerec
.fileID
);
1090 * Delete all files named "tempxxx", where
1091 * xxx is the file's cnid in decimal.
1094 if (bcmp(tempname
, filename
, namelen
) == 0) {
1095 struct filefork dfork
= {0};
1096 struct filefork rfork
= {0};
1097 struct cnode cnode
= {0};
1100 hfs_global_shared_lock_acquire(hfsmp
);
1103 if (journal_start_transaction(hfsmp
->jnl
) != 0) {
1110 * Reserve some space in the Catalog file.
1112 if (cat_preflight(hfsmp
, CAT_DELETE
, &cookie
, p
) != 0) {
1117 /* Lock catalog b-tree */
1118 if (hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
,
1119 LK_EXCLUSIVE
, p
) != 0) {
1124 /* Build a fake cnode */
1125 cat_convertattr(hfsmp
, (CatalogRecord
*)&filerec
, &cnode
.c_attr
,
1126 &dfork
.ff_data
, &rfork
.ff_data
);
1127 cnode
.c_desc
.cd_parentcnid
= hfsmp
->hfs_privdir_desc
.cd_cnid
;
1128 cnode
.c_desc
.cd_nameptr
= filename
;
1129 cnode
.c_desc
.cd_namelen
= namelen
;
1130 cnode
.c_desc
.cd_cnid
= cnode
.c_attr
.ca_fileid
;
1131 cnode
.c_blocks
= dfork
.ff_blocks
+ rfork
.ff_blocks
;
1133 /* Position iterator at previous entry */
1134 if (BTIterateRecord(fcb
, kBTreePrevRecord
, iterator
,
1139 /* Truncate the file to zero (both forks) */
1140 if (dfork
.ff_blocks
> 0) {
1143 dfork
.ff_cp
= &cnode
;
1144 cnode
.c_datafork
= &dfork
;
1145 cnode
.c_rsrcfork
= NULL
;
1146 fsize
= (u_int64_t
)dfork
.ff_blocks
* (u_int64_t
)HFSTOVCB(hfsmp
)->blockSize
;
1148 if (fsize
> HFS_BIGFILE_SIZE
) {
1149 fsize
-= HFS_BIGFILE_SIZE
;
1154 if (TruncateFileC(vcb
, (FCB
*)&dfork
, fsize
, false) != 0) {
1155 printf("error truncting data fork!\n");
1160 // if we're iteratively truncating this file down,
1161 // then end the transaction and start a new one so
1162 // that no one transaction gets too big.
1164 if (fsize
> 0 && started_tr
) {
1165 journal_end_transaction(hfsmp
->jnl
);
1166 if (journal_start_transaction(hfsmp
->jnl
) != 0) {
1174 if (rfork
.ff_blocks
> 0) {
1175 rfork
.ff_cp
= &cnode
;
1176 cnode
.c_datafork
= NULL
;
1177 cnode
.c_rsrcfork
= &rfork
;
1178 if (TruncateFileC(vcb
, (FCB
*)&rfork
, 0, false) != 0) {
1179 printf("error truncting rsrc fork!\n");
1184 /* Remove the file record from the Catalog */
1185 if (cat_delete(hfsmp
, &cnode
.c_desc
, &cnode
.c_attr
) != 0) {
1186 printf("error deleting cat rec!\n");
1190 /* Update parent and volume counts */
1191 hfsmp
->hfs_privdir_attr
.ca_entries
--;
1192 (void)cat_update(hfsmp
, &hfsmp
->hfs_privdir_desc
,
1193 &hfsmp
->hfs_privdir_attr
, NULL
, NULL
);
1194 hfs_volupdate(hfsmp
, VOL_RMFILE
, 0);
1196 /* Drop locks and end the transaction */
1197 (void) hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_RELEASE
, p
);
1198 cat_postflight(hfsmp
, &cookie
, p
);
1199 catlock
= catreserve
= 0;
1201 journal_end_transaction(hfsmp
->jnl
);
1204 hfs_global_shared_lock_release(hfsmp
);
1212 (void) hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_RELEASE
, p
);
1215 cat_postflight(hfsmp
, &cookie
, p
);
1218 journal_end_transaction(hfsmp
->jnl
);
1221 hfs_global_shared_lock_release(hfsmp
);
1224 FREE(iterator
, M_TEMP
);
1225 hfsmp
->hfs_flags
|= HFS_CLEANED_ORPHANS
;
1230 * This will return the correct logical block size for a given vnode.
1231 * For most files, it is the allocation block size, for meta data like
1232 * BTrees, this is kept as part of the BTree private nodeSize
1235 GetLogicalBlockSize(struct vnode
*vp
)
1237 u_int32_t logBlockSize
;
1239 DBG_ASSERT(vp
!= NULL
);
1241 /* start with default */
1242 logBlockSize
= VTOHFS(vp
)->hfs_logBlockSize
;
1244 if (vp
->v_flag
& VSYSTEM
) {
1245 if (VTOF(vp
)->fcbBTCBPtr
!= NULL
) {
1246 BTreeInfoRec bTreeInfo
;
1249 * We do not lock the BTrees, because if we are getting block..then the tree
1250 * should be locked in the first place.
1251 * We just want the nodeSize wich will NEVER change..so even if the world
1252 * is changing..the nodeSize should remain the same. Which argues why lock
1253 * it in the first place??
1256 (void) BTGetInformation (VTOF(vp
), kBTreeInfoVersion
, &bTreeInfo
);
1258 logBlockSize
= bTreeInfo
.nodeSize
;
1260 } else if (VTOC(vp
)->c_fileid
== kHFSAllocationFileID
) {
1261 logBlockSize
= VTOVCB(vp
)->vcbVBMIOSize
;
1265 DBG_ASSERT(logBlockSize
> 0);
1267 return logBlockSize
;
1272 hfs_freeblks(struct hfsmount
* hfsmp
, int wantreserve
)
1274 struct vcb_t
*vcb
= HFSTOVCB(hfsmp
);
1277 freeblks
= vcb
->freeBlocks
;
1279 if (freeblks
> vcb
->reserveBlocks
)
1280 freeblks
-= vcb
->reserveBlocks
;
1284 if (freeblks
> vcb
->loanedBlocks
)
1285 freeblks
-= vcb
->loanedBlocks
;
1289 #ifdef HFS_SPARSE_DEV
1291 * When the underlying device is sparse, check the
1292 * available space on the backing store volume.
1294 if ((hfsmp
->hfs_flags
& HFS_HAS_SPARSE_DEVICE
) && hfsmp
->hfs_backingfs_rootvp
) {
1295 struct statfs statbuf
; /* 272 bytes */
1296 u_int32_t vfreeblks
;
1297 u_int32_t loanedblks
;
1298 struct mount
* backingfs_mp
;
1300 backingfs_mp
= hfsmp
->hfs_backingfs_rootvp
->v_mount
;
1302 if (VFS_STATFS(backingfs_mp
, &statbuf
, current_proc()) == 0) {
1303 vfreeblks
= statbuf
.f_bavail
;
1304 /* Normalize block count if needed. */
1305 if (statbuf
.f_bsize
!= vcb
->blockSize
) {
1306 vfreeblks
= ((u_int64_t
)vfreeblks
* (u_int64_t
)statbuf
.f_bsize
) / vcb
->blockSize
;
1308 if (vfreeblks
> hfsmp
->hfs_sparsebandblks
)
1309 vfreeblks
-= hfsmp
->hfs_sparsebandblks
;
1313 /* Take into account any delayed allocations. */
1314 loanedblks
= 2 * vcb
->loanedBlocks
;
1315 if (vfreeblks
> loanedblks
)
1316 vfreeblks
-= loanedblks
;
1320 freeblks
= MIN(vfreeblks
, freeblks
);
1323 #endif /* HFS_SPARSE_DEV */
1329 * Map HFS Common errors (negative) to BSD error codes (positive).
1330 * Positive errors (ie BSD errors) are passed through unchanged.
1332 short MacToVFSError(OSErr err
)
1338 case dskFulErr
: /* -34 */
1339 case btNoSpaceAvail
: /* -32733 */
1341 case fxOvFlErr
: /* -32750 */
1344 case btBadNode
: /* -32731 */
1347 case memFullErr
: /* -108 */
1348 return ENOMEM
; /* +12 */
1350 case cmExists
: /* -32718 */
1351 case btExists
: /* -32734 */
1352 return EEXIST
; /* +17 */
1354 case cmNotFound
: /* -32719 */
1355 case btNotFound
: /* -32735 */
1356 return ENOENT
; /* 28 */
1358 case cmNotEmpty
: /* -32717 */
1359 return ENOTEMPTY
; /* 66 */
1361 case cmFThdDirErr
: /* -32714 */
1362 return EISDIR
; /* 21 */
1364 case fxRangeErr
: /* -32751 */
1367 case bdNamErr
: /* -37 */
1368 return ENAMETOOLONG
; /* 63 */
1370 case paramErr
: /* -50 */
1371 case fileBoundsErr
: /* -1309 */
1372 return EINVAL
; /* +22 */
1374 case fsBTBadNodeSize
:
1378 return EIO
; /* +5 */
1384 * Get the directory entry name hint for a given index.
1385 * The directory cnode (dcp) must be locked.
1389 hfs_getnamehint(struct cnode
*dcp
, int index
)
1391 struct hfs_index
*entry
;
1395 self
= current_act();
1396 SLIST_FOREACH(entry
, &dcp
->c_indexlist
, hi_link
) {
1397 if ((entry
->hi_index
== index
)
1398 && (entry
->hi_thread
== self
))
1399 return (entry
->hi_name
);
1407 * Save a directory entry name hint for a given index.
1408 * The directory cnode (dcp) must be locked.
1412 hfs_savenamehint(struct cnode
*dcp
, int index
, const char * namehint
)
1414 struct hfs_index
*entry
;
1418 len
= strlen(namehint
);
1419 MALLOC(entry
, struct hfs_index
*, len
+ sizeof(struct hfs_index
),
1421 entry
->hi_index
= index
;
1422 entry
->hi_thread
= current_act();
1423 bcopy(namehint
, entry
->hi_name
, len
+ 1);
1424 SLIST_INSERT_HEAD(&dcp
->c_indexlist
, entry
, hi_link
);
1429 * Release the directory entry name hint for a given index.
1430 * The directory cnode (dcp) must be locked.
1434 hfs_relnamehint(struct cnode
*dcp
, int index
)
1436 struct hfs_index
*entry
;
1440 self
= current_act();
1441 SLIST_FOREACH(entry
, &dcp
->c_indexlist
, hi_link
) {
1442 if ((entry
->hi_index
== index
)
1443 && (entry
->hi_thread
== self
)) {
1444 SLIST_REMOVE(&dcp
->c_indexlist
, entry
, hfs_index
,
1446 FREE(entry
, M_TEMP
);
1454 * Release all directory entry name hints.
1458 hfs_relnamehints(struct cnode
*dcp
)
1460 struct hfs_index
*entry
;
1461 struct hfs_index
*next
;
1463 if (!SLIST_EMPTY(&dcp
->c_indexlist
)) {
1464 for(entry
= SLIST_FIRST(&dcp
->c_indexlist
);
1467 next
= SLIST_NEXT(entry
, hi_link
);
1468 SLIST_REMOVE(&dcp
->c_indexlist
, entry
, hfs_index
, hi_link
);
1469 FREE(entry
, M_TEMP
);
1476 * Perform a case-insensitive compare of two UTF-8 filenames.
1478 * Returns 0 if the strings match.
1482 hfs_namecmp(const char *str1
, size_t len1
, const char *str2
, size_t len2
)
1484 u_int16_t
*ustr1
, *ustr2
;
1485 size_t ulen1
, ulen2
;
1492 maxbytes
= kHFSPlusMaxFileNameChars
<< 1;
1493 MALLOC(ustr1
, u_int16_t
*, maxbytes
<< 1, M_TEMP
, M_WAITOK
);
1494 ustr2
= ustr1
+ (maxbytes
>> 1);
1496 if (utf8_decodestr(str1
, len1
, ustr1
, &ulen1
, maxbytes
, ':', 0) != 0)
1498 if (utf8_decodestr(str2
, len2
, ustr2
, &ulen2
, maxbytes
, ':', 0) != 0)
1501 cmp
= FastUnicodeCompare(ustr1
, ulen1
>>1, ustr2
, ulen2
>>1);
1503 FREE(ustr1
, M_TEMP
);
1510 hfs_early_journal_init(struct hfsmount
*hfsmp
, HFSPlusVolumeHeader
*vhp
,
1511 void *_args
, int embeddedOffset
, int mdb_offset
,
1512 HFSMasterDirectoryBlock
*mdbp
, struct ucred
*cred
)
1514 JournalInfoBlock
*jibp
;
1515 struct buf
*jinfo_bp
, *bp
;
1516 int sectors_per_fsblock
, arg_flags
=0, arg_tbufsz
=0;
1517 int retval
, blksize
= hfsmp
->hfs_phys_block_size
;
1518 struct vnode
*devvp
;
1519 struct hfs_mount_args
*args
= _args
;
1521 devvp
= hfsmp
->hfs_devvp
;
1523 if (args
!= NULL
&& (args
->flags
& HFSFSMNT_EXTENDED_ARGS
)) {
1524 arg_flags
= args
->journal_flags
;
1525 arg_tbufsz
= args
->journal_tbuffer_size
;
1528 sectors_per_fsblock
= SWAP_BE32(vhp
->blockSize
) / blksize
;
1530 retval
= meta_bread(devvp
,
1531 embeddedOffset
/blksize
+
1532 (SWAP_BE32(vhp
->journalInfoBlock
)*sectors_per_fsblock
),
1533 SWAP_BE32(vhp
->blockSize
), cred
, &jinfo_bp
);
1537 jibp
= (JournalInfoBlock
*)jinfo_bp
->b_data
;
1538 jibp
->flags
= SWAP_BE32(jibp
->flags
);
1539 jibp
->offset
= SWAP_BE64(jibp
->offset
);
1540 jibp
->size
= SWAP_BE64(jibp
->size
);
1542 if (jibp
->flags
& kJIJournalInFSMask
) {
1543 hfsmp
->jvp
= hfsmp
->hfs_devvp
;
1545 printf("hfs: journal not stored in fs! don't know what to do.\n");
1550 // save this off for the hack-y check in hfs_remove()
1551 hfsmp
->jnl_start
= jibp
->offset
/ SWAP_BE32(vhp
->blockSize
);
1552 hfsmp
->jnl_size
= jibp
->size
;
1554 if (jibp
->flags
& kJIJournalNeedInitMask
) {
1555 printf("hfs: Initializing the journal (joffset 0x%llx sz 0x%llx)...\n",
1556 jibp
->offset
+ (off_t
)embeddedOffset
, jibp
->size
);
1557 hfsmp
->jnl
= journal_create(hfsmp
->jvp
,
1558 jibp
->offset
+ (off_t
)embeddedOffset
,
1564 hfs_sync_metadata
, hfsmp
->hfs_mp
);
1566 // no need to start a transaction here... if this were to fail
1567 // we'd just re-init it on the next mount.
1568 jibp
->flags
&= ~kJIJournalNeedInitMask
;
1569 jibp
->flags
= SWAP_BE32(jibp
->flags
);
1570 jibp
->offset
= SWAP_BE64(jibp
->offset
);
1571 jibp
->size
= SWAP_BE64(jibp
->size
);
1576 //printf("hfs: Opening the journal (joffset 0x%llx sz 0x%llx vhp_blksize %d)...\n",
1577 // jibp->offset + (off_t)embeddedOffset,
1578 // jibp->size, SWAP_BE32(vhp->blockSize));
1580 hfsmp
->jnl
= journal_open(hfsmp
->jvp
,
1581 jibp
->offset
+ (off_t
)embeddedOffset
,
1587 hfs_sync_metadata
, hfsmp
->hfs_mp
);
1593 if (hfsmp
->jnl
&& mdbp
) {
1594 // reload the mdb because it could have changed
1595 // if the journal had to be replayed.
1596 if (mdb_offset
== 0) {
1597 mdb_offset
= (embeddedOffset
/ blksize
) + HFS_PRI_SECTOR(blksize
);
1599 retval
= meta_bread(devvp
, mdb_offset
, blksize
, cred
, &bp
);
1602 printf("hfs: failed to reload the mdb after opening the journal (retval %d)!\n",
1606 bcopy(bp
->b_data
+ HFS_PRI_OFFSET(blksize
), mdbp
, 512);
1613 //printf("journal @ 0x%x\n", hfsmp->jnl);
1615 // if we expected the journal to be there and we couldn't
1616 // create it or open it then we have to bail out.
1617 if (hfsmp
->jnl
== NULL
) {
1618 printf("hfs: early jnl init: failed to open/create the journal (retval %d).\n", retval
);
1627 // This function will go and re-locate the .journal_info_block and
1628 // the .journal files in case they moved (which can happen if you
1629 // run Norton SpeedDisk). If we fail to find either file we just
1630 // disable journaling for this volume and return. We turn off the
1631 // journaling bit in the vcb and assume it will get written to disk
1632 // later (if it doesn't on the next mount we'd do the same thing
1633 // again which is harmless). If we disable journaling we don't
1634 // return an error so that the volume is still mountable.
1636 // If the info we find for the .journal_info_block and .journal files
1637 // isn't what we had stored, we re-set our cached info and proceed
1638 // with opening the journal normally.
1641 hfs_late_journal_init(struct hfsmount
*hfsmp
, HFSPlusVolumeHeader
*vhp
, void *_args
)
1643 JournalInfoBlock
*jibp
;
1644 struct buf
*jinfo_bp
, *bp
;
1645 int sectors_per_fsblock
, arg_flags
=0, arg_tbufsz
=0;
1646 int retval
, need_flush
= 0, write_jibp
= 0;
1647 struct vnode
*devvp
;
1648 struct cat_attr jib_attr
, jattr
;
1649 struct cat_fork jib_fork
, jfork
;
1652 struct hfs_mount_args
*args
= _args
;
1654 devvp
= hfsmp
->hfs_devvp
;
1655 vcb
= HFSTOVCB(hfsmp
);
1657 if (args
!= NULL
&& (args
->flags
& HFSFSMNT_EXTENDED_ARGS
)) {
1658 if (args
->journal_disable
) {
1662 arg_flags
= args
->journal_flags
;
1663 arg_tbufsz
= args
->journal_tbuffer_size
;
1666 fid
= GetFileInfo(vcb
, kRootDirID
, ".journal_info_block", &jib_attr
, &jib_fork
);
1667 if (fid
== 0 || jib_fork
.cf_extents
[0].startBlock
== 0 || jib_fork
.cf_size
== 0) {
1668 printf("hfs: can't find the .journal_info_block! disabling journaling (start: %d).\n",
1669 jib_fork
.cf_extents
[0].startBlock
);
1670 vcb
->vcbAtrb
&= ~kHFSVolumeJournaledMask
;
1673 hfsmp
->hfs_jnlinfoblkid
= fid
;
1675 // make sure the journal_info_block begins where we think it should.
1676 if (SWAP_BE32(vhp
->journalInfoBlock
) != jib_fork
.cf_extents
[0].startBlock
) {
1677 printf("hfs: The journal_info_block moved (was: %d; is: %d). Fixing up\n",
1678 SWAP_BE32(vhp
->journalInfoBlock
), jib_fork
.cf_extents
[0].startBlock
);
1680 vcb
->vcbJinfoBlock
= jib_fork
.cf_extents
[0].startBlock
;
1681 vhp
->journalInfoBlock
= SWAP_BE32(jib_fork
.cf_extents
[0].startBlock
);
1685 sectors_per_fsblock
= SWAP_BE32(vhp
->blockSize
) / hfsmp
->hfs_phys_block_size
;
1686 retval
= meta_bread(devvp
,
1687 vcb
->hfsPlusIOPosOffset
/ hfsmp
->hfs_phys_block_size
+
1688 (SWAP_BE32(vhp
->journalInfoBlock
)*sectors_per_fsblock
),
1689 SWAP_BE32(vhp
->blockSize
), NOCRED
, &jinfo_bp
);
1691 printf("hfs: can't read journal info block. disabling journaling.\n");
1692 vcb
->vcbAtrb
&= ~kHFSVolumeJournaledMask
;
1696 jibp
= (JournalInfoBlock
*)jinfo_bp
->b_data
;
1697 jibp
->flags
= SWAP_BE32(jibp
->flags
);
1698 jibp
->offset
= SWAP_BE64(jibp
->offset
);
1699 jibp
->size
= SWAP_BE64(jibp
->size
);
1701 fid
= GetFileInfo(vcb
, kRootDirID
, ".journal", &jattr
, &jfork
);
1702 if (fid
== 0 || jfork
.cf_extents
[0].startBlock
== 0 || jfork
.cf_size
== 0) {
1703 printf("hfs: can't find the journal file! disabling journaling (start: %d)\n",
1704 jfork
.cf_extents
[0].startBlock
);
1706 vcb
->vcbAtrb
&= ~kHFSVolumeJournaledMask
;
1709 hfsmp
->hfs_jnlfileid
= fid
;
1711 // make sure the journal file begins where we think it should.
1712 if ((jibp
->offset
/ (u_int64_t
)vcb
->blockSize
) != jfork
.cf_extents
[0].startBlock
) {
1713 printf("hfs: The journal file moved (was: %lld; is: %d). Fixing up\n",
1714 (jibp
->offset
/ (u_int64_t
)vcb
->blockSize
), jfork
.cf_extents
[0].startBlock
);
1716 jibp
->offset
= (u_int64_t
)jfork
.cf_extents
[0].startBlock
* (u_int64_t
)vcb
->blockSize
;
1720 // check the size of the journal file.
1721 if (jibp
->size
!= (u_int64_t
)jfork
.cf_extents
[0].blockCount
*vcb
->blockSize
) {
1722 printf("hfs: The journal file changed size! (was %lld; is %lld). Fixing up.\n",
1723 jibp
->size
, (u_int64_t
)jfork
.cf_extents
[0].blockCount
*vcb
->blockSize
);
1725 jibp
->size
= (u_int64_t
)jfork
.cf_extents
[0].blockCount
* vcb
->blockSize
;
1729 if (jibp
->flags
& kJIJournalInFSMask
) {
1730 hfsmp
->jvp
= hfsmp
->hfs_devvp
;
1732 printf("hfs: journal not stored in fs! don't know what to do.\n");
1737 // save this off for the hack-y check in hfs_remove()
1738 hfsmp
->jnl_start
= jibp
->offset
/ SWAP_BE32(vhp
->blockSize
);
1739 hfsmp
->jnl_size
= jibp
->size
;
1741 if (jibp
->flags
& kJIJournalNeedInitMask
) {
1742 printf("hfs: Initializing the journal (joffset 0x%llx sz 0x%llx)...\n",
1743 jibp
->offset
+ (off_t
)vcb
->hfsPlusIOPosOffset
, jibp
->size
);
1744 hfsmp
->jnl
= journal_create(hfsmp
->jvp
,
1745 jibp
->offset
+ (off_t
)vcb
->hfsPlusIOPosOffset
,
1748 hfsmp
->hfs_phys_block_size
,
1751 hfs_sync_metadata
, hfsmp
->hfs_mp
);
1753 // no need to start a transaction here... if this were to fail
1754 // we'd just re-init it on the next mount.
1755 jibp
->flags
&= ~kJIJournalNeedInitMask
;
1760 // if we weren't the last person to mount this volume
1761 // then we need to throw away the journal because it
1762 // is likely that someone else mucked with the disk.
1763 // if the journal is empty this is no big deal. if the
1764 // disk is dirty this prevents us from replaying the
1765 // journal over top of changes that someone else made.
1767 arg_flags
|= JOURNAL_RESET
;
1769 //printf("hfs: Opening the journal (joffset 0x%llx sz 0x%llx vhp_blksize %d)...\n",
1770 // jibp->offset + (off_t)vcb->hfsPlusIOPosOffset,
1771 // jibp->size, SWAP_BE32(vhp->blockSize));
1773 hfsmp
->jnl
= journal_open(hfsmp
->jvp
,
1774 jibp
->offset
+ (off_t
)vcb
->hfsPlusIOPosOffset
,
1777 hfsmp
->hfs_phys_block_size
,
1780 hfs_sync_metadata
, hfsmp
->hfs_mp
);
1785 jibp
->flags
= SWAP_BE32(jibp
->flags
);
1786 jibp
->offset
= SWAP_BE64(jibp
->offset
);
1787 jibp
->size
= SWAP_BE64(jibp
->size
);
1796 //printf("journal @ 0x%x\n", hfsmp->jnl);
1798 // if we expected the journal to be there and we couldn't
1799 // create it or open it then we have to bail out.
1800 if (hfsmp
->jnl
== NULL
) {
1801 printf("hfs: late jnl init: failed to open/create the journal (retval %d).\n", retval
);
1809 * Calculate the allocation zone for metadata.
1811 * This zone includes the following:
1812 * Allocation Bitmap file
1813 * Overflow Extents file
1816 * Clustered Hot files
1819 * METADATA ALLOCATION ZONE
1820 * ____________________________________________________________________________
1822 * | BM | JF | OEF | CATALOG |---> | HOT FILES |
1823 * |____|____|_____|_______________|______________________________|___________|
1825 * <------------------------------- N * 128 MB ------------------------------->
1828 #define GIGABYTE (u_int64_t)(1024*1024*1024)
1830 #define OVERFLOW_DEFAULT_SIZE (4*1024*1024)
1831 #define OVERFLOW_MAXIMUM_SIZE (128*1024*1024)
1832 #define JOURNAL_DEFAULT_SIZE (8*1024*1024)
1833 #define JOURNAL_MAXIMUM_SIZE (512*1024*1024)
1834 #define HOTBAND_MINIMUM_SIZE (10*1024*1024)
1835 #define HOTBAND_MAXIMUM_SIZE (512*1024*1024)
1838 hfs_metadatazone_init(struct hfsmount
*hfsmp
)
1841 struct BTreeInfoRec btinfo
;
1849 vcb
= HFSTOVCB(hfsmp
);
1850 fs_size
= (u_int64_t
)vcb
->blockSize
* (u_int64_t
)vcb
->totalBlocks
;
1853 * For volumes less than 10 GB, don't bother.
1855 if (fs_size
< ((u_int64_t
)10 * GIGABYTE
))
1858 * Skip non-journaled volumes as well.
1860 if (hfsmp
->jnl
== NULL
)
1864 * Start with allocation bitmap (a fixed size).
1866 zonesize
= roundup(vcb
->totalBlocks
/ 8, vcb
->vcbVBMIOSize
);
1869 * Overflow Extents file gets 4 MB per 100 GB.
1871 items
= fs_size
/ ((u_int64_t
)100 * GIGABYTE
);
1872 filesize
= (u_int64_t
)(items
+ 1) * OVERFLOW_DEFAULT_SIZE
;
1873 if (filesize
> OVERFLOW_MAXIMUM_SIZE
)
1874 filesize
= OVERFLOW_MAXIMUM_SIZE
;
1875 zonesize
+= filesize
;
1876 hfsmp
->hfs_overflow_maxblks
= filesize
/ vcb
->blockSize
;
1879 * Plan for at least 8 MB of journal for each
1880 * 100 GB of disk space (up to a 512 MB).
1882 items
= fs_size
/ ((u_int64_t
)100 * GIGABYTE
);
1883 filesize
= (u_int64_t
)(items
+ 1) * JOURNAL_DEFAULT_SIZE
;
1884 if (filesize
> JOURNAL_MAXIMUM_SIZE
)
1885 filesize
= JOURNAL_MAXIMUM_SIZE
;
1886 zonesize
+= filesize
;
1889 * Catalog file gets 10 MB per 1 GB.
1891 * How about considering the current catalog size (used nodes * node size)
1892 * and the current file data size to help estimate the required
1895 filesize
= MIN((fs_size
/ 1024) * 10, GIGABYTE
);
1896 hfsmp
->hfs_catalog_maxblks
= filesize
/ vcb
->blockSize
;
1897 zonesize
+= filesize
;
1900 * Add space for hot file region.
1902 * ...for now, use 5 MB per 1 GB (0.5 %)
1904 filesize
= (fs_size
/ 1024) * 5;
1905 if (filesize
> HOTBAND_MAXIMUM_SIZE
)
1906 filesize
= HOTBAND_MAXIMUM_SIZE
;
1907 else if (filesize
< HOTBAND_MINIMUM_SIZE
)
1908 filesize
= HOTBAND_MINIMUM_SIZE
;
1910 * Calculate user quota file requirements.
1912 items
= QF_USERS_PER_GB
* (fs_size
/ GIGABYTE
);
1913 if (items
< QF_MIN_USERS
)
1914 items
= QF_MIN_USERS
;
1915 else if (items
> QF_MAX_USERS
)
1916 items
= QF_MAX_USERS
;
1917 if (!powerof2(items
)) {
1925 filesize
+= (items
+ 1) * sizeof(struct dqblk
);
1927 * Calculate group quota file requirements.
1930 items
= QF_GROUPS_PER_GB
* (fs_size
/ GIGABYTE
);
1931 if (items
< QF_MIN_GROUPS
)
1932 items
= QF_MIN_GROUPS
;
1933 else if (items
> QF_MAX_GROUPS
)
1934 items
= QF_MAX_GROUPS
;
1935 if (!powerof2(items
)) {
1943 filesize
+= (items
+ 1) * sizeof(struct dqblk
);
1944 hfsmp
->hfs_hotfile_maxblks
= filesize
/ vcb
->blockSize
;
1945 zonesize
+= filesize
;
1948 * Round up entire zone to a bitmap block's worth.
1949 * The extra space goes to the catalog file and hot file area.
1952 zonesize
= roundup(zonesize
, vcb
->vcbVBMIOSize
* 8 * vcb
->blockSize
);
1953 temp
= zonesize
- temp
; /* temp has extra space */
1954 filesize
+= temp
/ 3;
1955 hfsmp
->hfs_catalog_maxblks
+= (temp
- (temp
/ 3)) / vcb
->blockSize
;
1957 /* Convert to allocation blocks. */
1958 blk
= zonesize
/ vcb
->blockSize
;
1960 /* The default metadata zone location is at the start of volume. */
1961 hfsmp
->hfs_metazone_start
= 1;
1962 hfsmp
->hfs_metazone_end
= blk
- 1;
1964 /* The default hotfile area is at the end of the zone. */
1965 hfsmp
->hfs_hotfile_start
= blk
- (filesize
/ vcb
->blockSize
);
1966 hfsmp
->hfs_hotfile_end
= hfsmp
->hfs_metazone_end
;
1967 hfsmp
->hfs_hotfile_freeblks
= hfs_hotfile_freeblocks(hfsmp
);
1969 printf("HFS: metadata zone is %d to %d\n", hfsmp
->hfs_metazone_start
, hfsmp
->hfs_metazone_end
);
1970 printf("HFS: hot file band is %d to %d\n", hfsmp
->hfs_hotfile_start
, hfsmp
->hfs_hotfile_end
);
1971 printf("HFS: hot file band free blocks = %d\n", hfsmp
->hfs_hotfile_freeblks
);
1973 hfsmp
->hfs_flags
|= HFS_METADATA_ZONE
;
1978 hfs_hotfile_freeblocks(struct hfsmount
*hfsmp
)
1980 ExtendedVCB
*vcb
= HFSTOVCB(hfsmp
);
1983 freeblocks
= MetaZoneFreeBlocks(vcb
);
1984 /* Minus Extents overflow file reserve. */
1986 hfsmp
->hfs_overflow_maxblks
- VTOF(vcb
->extentsRefNum
)->ff_blocks
;
1987 /* Minus catalog file reserve. */
1989 hfsmp
->hfs_catalog_maxblks
- VTOF(vcb
->catalogRefNum
)->ff_blocks
;
1993 return MIN(freeblocks
, hfsmp
->hfs_hotfile_maxblks
);
1997 * Determine if a file is a "virtual" metadata file.
1998 * This includes journal and quota files.
2002 hfs_virtualmetafile(struct cnode
*cp
)
2007 if (cp
->c_parentcnid
!= kHFSRootFolderID
)
2010 filename
= cp
->c_desc
.cd_nameptr
;
2011 if (filename
== NULL
)
2014 if ((strcmp(filename
, ".journal") == 0) ||
2015 (strcmp(filename
, ".journal_info_block") == 0) ||
2016 (strcmp(filename
, ".quota.user") == 0) ||
2017 (strcmp(filename
, ".quota.group") == 0) ||
2018 (strcmp(filename
, ".hotfiles.btree") == 0))