2 * Copyright (c) 2000-2007 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
28 /* @(#)hfs_vfsutils.c 4.0
30 * (c) 1997-2002 Apple Computer, Inc. All Rights Reserved
32 * hfs_vfsutils.c -- Routines that go between the HFS layer and the VFS.
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/kernel.h>
38 #include <sys/malloc.h>
40 #include <sys/mount.h>
42 #include <sys/buf_internal.h>
44 #include <sys/unistd.h>
45 #include <sys/utfconv.h>
46 #include <sys/kauth.h>
47 #include <sys/fcntl.h>
48 #include <sys/vnode_internal.h>
50 #include <libkern/OSAtomic.h>
53 #include "hfs_catalog.h"
55 #include "hfs_mount.h"
56 #include "hfs_endian.h"
57 #include "hfs_cnode.h"
58 #include "hfs_fsctl.h"
60 #include "hfscommon/headers/FileMgrInternal.h"
61 #include "hfscommon/headers/BTreesInternal.h"
62 #include "hfscommon/headers/HFSUnicodeWrappers.h"
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
*);
71 //*******************************************************************************
72 // Note: Finder information in the HFS/HFS+ metadata are considered opaque and
73 // hence are not in the right byte order on little endian machines. It is
74 // the responsibility of the finder and other clients to swap the data.
75 //*******************************************************************************
77 //*******************************************************************************
78 // Routine: hfs_MountHFSVolume
81 //*******************************************************************************
82 unsigned char hfs_catname
[] = "Catalog B-tree";
83 unsigned char hfs_extname
[] = "Extents B-tree";
84 unsigned char hfs_vbmname
[] = "Volume Bitmap";
85 unsigned char hfs_attrname
[] = "Attribute B-tree";
86 unsigned char hfs_startupname
[] = "Startup File";
90 OSErr
hfs_MountHFSVolume(struct hfsmount
*hfsmp
, HFSMasterDirectoryBlock
*mdb
,
91 __unused
struct proc
*p
)
93 ExtendedVCB
*vcb
= HFSTOVCB(hfsmp
);
96 struct cat_desc cndesc
;
97 struct cat_attr cnattr
;
100 /* Block size must be a multiple of 512 */
101 if (SWAP_BE32(mdb
->drAlBlkSiz
) == 0 ||
102 (SWAP_BE32(mdb
->drAlBlkSiz
) & 0x01FF) != 0)
105 /* don't mount a writeable volume if its dirty, it must be cleaned by fsck_hfs */
106 if (((hfsmp
->hfs_flags
& HFS_READ_ONLY
) == 0) &&
107 ((SWAP_BE16(mdb
->drAtrb
) & kHFSVolumeUnmountedMask
) == 0)) {
110 hfsmp
->hfs_flags
|= HFS_STANDARD
;
112 * The MDB seems OK: transfer info from it into VCB
113 * Note - the VCB starts out clear (all zeros)
116 vcb
->vcbSigWord
= SWAP_BE16 (mdb
->drSigWord
);
117 vcb
->vcbCrDate
= to_bsd_time(LocalToUTC(SWAP_BE32(mdb
->drCrDate
)));
118 vcb
->localCreateDate
= SWAP_BE32 (mdb
->drCrDate
);
119 vcb
->vcbLsMod
= to_bsd_time(LocalToUTC(SWAP_BE32(mdb
->drLsMod
)));
120 vcb
->vcbAtrb
= SWAP_BE16 (mdb
->drAtrb
);
121 vcb
->vcbNmFls
= SWAP_BE16 (mdb
->drNmFls
);
122 vcb
->vcbVBMSt
= SWAP_BE16 (mdb
->drVBMSt
);
123 vcb
->nextAllocation
= SWAP_BE16 (mdb
->drAllocPtr
);
124 vcb
->totalBlocks
= SWAP_BE16 (mdb
->drNmAlBlks
);
125 vcb
->allocLimit
= vcb
->totalBlocks
;
126 vcb
->blockSize
= SWAP_BE32 (mdb
->drAlBlkSiz
);
127 vcb
->vcbClpSiz
= SWAP_BE32 (mdb
->drClpSiz
);
128 vcb
->vcbAlBlSt
= SWAP_BE16 (mdb
->drAlBlSt
);
129 vcb
->vcbNxtCNID
= SWAP_BE32 (mdb
->drNxtCNID
);
130 vcb
->freeBlocks
= SWAP_BE16 (mdb
->drFreeBks
);
131 vcb
->vcbVolBkUp
= to_bsd_time(LocalToUTC(SWAP_BE32(mdb
->drVolBkUp
)));
132 vcb
->vcbWrCnt
= SWAP_BE32 (mdb
->drWrCnt
);
133 vcb
->vcbNmRtDirs
= SWAP_BE16 (mdb
->drNmRtDirs
);
134 vcb
->vcbFilCnt
= SWAP_BE32 (mdb
->drFilCnt
);
135 vcb
->vcbDirCnt
= SWAP_BE32 (mdb
->drDirCnt
);
136 bcopy(mdb
->drFndrInfo
, vcb
->vcbFndrInfo
, sizeof(vcb
->vcbFndrInfo
));
137 if ((hfsmp
->hfs_flags
& HFS_READ_ONLY
) == 0)
138 vcb
->vcbWrCnt
++; /* Compensate for write of MDB on last flush */
140 /* convert hfs encoded name into UTF-8 string */
141 error
= hfs_to_utf8(vcb
, mdb
->drVN
, NAME_MAX
, &utf8chars
, vcb
->vcbVN
);
143 * When an HFS name cannot be encoded with the current
144 * volume encoding we use MacRoman as a fallback.
146 if (error
|| (utf8chars
== 0))
147 (void) mac_roman_to_utf8(mdb
->drVN
, NAME_MAX
, &utf8chars
, vcb
->vcbVN
);
149 hfsmp
->hfs_logBlockSize
= BestBlockSizeFit(vcb
->blockSize
, MAXBSIZE
, hfsmp
->hfs_logical_block_size
);
150 vcb
->vcbVBMIOSize
= kHFSBlockSize
;
152 hfsmp
->hfs_alt_id_sector
= HFS_ALT_SECTOR(hfsmp
->hfs_logical_block_size
,
153 hfsmp
->hfs_logical_block_count
);
155 bzero(&cndesc
, sizeof(cndesc
));
156 cndesc
.cd_parentcnid
= kHFSRootParentID
;
157 cndesc
.cd_flags
|= CD_ISMETA
;
158 bzero(&cnattr
, sizeof(cnattr
));
159 cnattr
.ca_linkcount
= 1;
160 cnattr
.ca_mode
= S_IFREG
;
161 bzero(&fork
, sizeof(fork
));
164 * Set up Extents B-tree vnode
166 cndesc
.cd_nameptr
= hfs_extname
;
167 cndesc
.cd_namelen
= strlen((char *)hfs_extname
);
168 cndesc
.cd_cnid
= cnattr
.ca_fileid
= kHFSExtentsFileID
;
169 fork
.cf_size
= SWAP_BE32(mdb
->drXTFlSize
);
170 fork
.cf_blocks
= fork
.cf_size
/ vcb
->blockSize
;
171 fork
.cf_clump
= SWAP_BE32(mdb
->drXTClpSiz
);
173 fork
.cf_extents
[0].startBlock
= SWAP_BE16(mdb
->drXTExtRec
[0].startBlock
);
174 fork
.cf_extents
[0].blockCount
= SWAP_BE16(mdb
->drXTExtRec
[0].blockCount
);
175 fork
.cf_extents
[1].startBlock
= SWAP_BE16(mdb
->drXTExtRec
[1].startBlock
);
176 fork
.cf_extents
[1].blockCount
= SWAP_BE16(mdb
->drXTExtRec
[1].blockCount
);
177 fork
.cf_extents
[2].startBlock
= SWAP_BE16(mdb
->drXTExtRec
[2].startBlock
);
178 fork
.cf_extents
[2].blockCount
= SWAP_BE16(mdb
->drXTExtRec
[2].blockCount
);
179 cnattr
.ca_blocks
= fork
.cf_blocks
;
181 error
= hfs_getnewvnode(hfsmp
, NULL
, NULL
, &cndesc
, 0, &cnattr
, &fork
,
182 &hfsmp
->hfs_extents_vp
);
183 if (error
) goto MtVolErr
;
184 error
= MacToVFSError(BTOpenPath(VTOF(hfsmp
->hfs_extents_vp
),
185 (KeyCompareProcPtr
)CompareExtentKeys
));
187 hfs_unlock(VTOC(hfsmp
->hfs_extents_vp
));
190 hfsmp
->hfs_extents_cp
= VTOC(hfsmp
->hfs_extents_vp
);
193 * Set up Catalog B-tree vnode...
195 cndesc
.cd_nameptr
= hfs_catname
;
196 cndesc
.cd_namelen
= strlen((char *)hfs_catname
);
197 cndesc
.cd_cnid
= cnattr
.ca_fileid
= kHFSCatalogFileID
;
198 fork
.cf_size
= SWAP_BE32(mdb
->drCTFlSize
);
199 fork
.cf_blocks
= fork
.cf_size
/ vcb
->blockSize
;
200 fork
.cf_clump
= SWAP_BE32(mdb
->drCTClpSiz
);
202 fork
.cf_extents
[0].startBlock
= SWAP_BE16(mdb
->drCTExtRec
[0].startBlock
);
203 fork
.cf_extents
[0].blockCount
= SWAP_BE16(mdb
->drCTExtRec
[0].blockCount
);
204 fork
.cf_extents
[1].startBlock
= SWAP_BE16(mdb
->drCTExtRec
[1].startBlock
);
205 fork
.cf_extents
[1].blockCount
= SWAP_BE16(mdb
->drCTExtRec
[1].blockCount
);
206 fork
.cf_extents
[2].startBlock
= SWAP_BE16(mdb
->drCTExtRec
[2].startBlock
);
207 fork
.cf_extents
[2].blockCount
= SWAP_BE16(mdb
->drCTExtRec
[2].blockCount
);
208 cnattr
.ca_blocks
= fork
.cf_blocks
;
210 error
= hfs_getnewvnode(hfsmp
, NULL
, NULL
, &cndesc
, 0, &cnattr
, &fork
,
211 &hfsmp
->hfs_catalog_vp
);
213 hfs_unlock(VTOC(hfsmp
->hfs_extents_vp
));
216 error
= MacToVFSError(BTOpenPath(VTOF(hfsmp
->hfs_catalog_vp
),
217 (KeyCompareProcPtr
)CompareCatalogKeys
));
219 hfs_unlock(VTOC(hfsmp
->hfs_catalog_vp
));
220 hfs_unlock(VTOC(hfsmp
->hfs_extents_vp
));
223 hfsmp
->hfs_catalog_cp
= VTOC(hfsmp
->hfs_catalog_vp
);
226 * Set up dummy Allocation file vnode (used only for locking bitmap)
228 cndesc
.cd_nameptr
= hfs_vbmname
;
229 cndesc
.cd_namelen
= strlen((char *)hfs_vbmname
);
230 cndesc
.cd_cnid
= cnattr
.ca_fileid
= kHFSAllocationFileID
;
231 bzero(&fork
, sizeof(fork
));
232 cnattr
.ca_blocks
= 0;
234 error
= hfs_getnewvnode(hfsmp
, NULL
, NULL
, &cndesc
, 0, &cnattr
, &fork
,
235 &hfsmp
->hfs_allocation_vp
);
237 hfs_unlock(VTOC(hfsmp
->hfs_catalog_vp
));
238 hfs_unlock(VTOC(hfsmp
->hfs_extents_vp
));
241 hfsmp
->hfs_allocation_cp
= VTOC(hfsmp
->hfs_allocation_vp
);
243 /* mark the volume dirty (clear clean unmount bit) */
244 vcb
->vcbAtrb
&= ~kHFSVolumeUnmountedMask
;
248 error
= cat_idlookup(hfsmp
, kHFSRootFolderID
, 0, NULL
, NULL
, NULL
);
251 if ( error
== noErr
)
253 if ( !(vcb
->vcbAtrb
& kHFSVolumeHardwareLockMask
) ) // if the disk is not write protected
255 MarkVCBDirty( vcb
); // mark VCB dirty so it will be written
260 * all done with system files so we can unlock now...
262 hfs_unlock(VTOC(hfsmp
->hfs_allocation_vp
));
263 hfs_unlock(VTOC(hfsmp
->hfs_catalog_vp
));
264 hfs_unlock(VTOC(hfsmp
->hfs_extents_vp
));
268 //-- Release any resources allocated so far before exiting with an error:
270 ReleaseMetaFileVNode(hfsmp
->hfs_catalog_vp
);
271 ReleaseMetaFileVNode(hfsmp
->hfs_extents_vp
);
277 //*******************************************************************************
278 // Routine: hfs_MountHFSPlusVolume
281 //*******************************************************************************
284 OSErr
hfs_MountHFSPlusVolume(struct hfsmount
*hfsmp
, HFSPlusVolumeHeader
*vhp
,
285 off_t embeddedOffset
, u_int64_t disksize
, __unused
struct proc
*p
, void *args
, kauth_cred_t cred
)
287 register ExtendedVCB
*vcb
;
288 struct cat_desc cndesc
;
289 struct cat_attr cnattr
;
290 struct cat_fork cfork
;
292 daddr64_t spare_sectors
;
293 struct BTreeInfoRec btinfo
;
295 u_int16_t hfs_version
;
299 signature
= SWAP_BE16(vhp
->signature
);
300 hfs_version
= SWAP_BE16(vhp
->version
);
302 if (signature
== kHFSPlusSigWord
) {
303 if (hfs_version
!= kHFSPlusVersion
) {
304 printf("hfs_mount: invalid HFS+ version: %d\n", hfs_version
);
307 } else if (signature
== kHFSXSigWord
) {
308 if (hfs_version
!= kHFSXVersion
) {
309 printf("hfs_mount: invalid HFSX version: %d\n", hfs_version
);
312 /* The in-memory signature is always 'H+'. */
313 signature
= kHFSPlusSigWord
;
314 hfsmp
->hfs_flags
|= HFS_X
;
316 /* Removed printf for invalid HFS+ signature because it gives
317 * false error for UFS root volume
322 /* Block size must be at least 512 and a power of 2 */
323 blockSize
= SWAP_BE32(vhp
->blockSize
);
324 if (blockSize
< 512 || !powerof2(blockSize
))
327 /* don't mount a writable volume if its dirty, it must be cleaned by fsck_hfs */
328 if ((hfsmp
->hfs_flags
& HFS_READ_ONLY
) == 0 && hfsmp
->jnl
== NULL
&&
329 (SWAP_BE32(vhp
->attributes
) & kHFSVolumeUnmountedMask
) == 0)
332 /* Make sure we can live with the physical block size. */
333 if ((disksize
& (hfsmp
->hfs_logical_block_size
- 1)) ||
334 (embeddedOffset
& (hfsmp
->hfs_logical_block_size
- 1)) ||
335 (blockSize
< hfsmp
->hfs_logical_block_size
)) {
339 /* If allocation block size is less than the physical
340 * block size, we assume that the physical block size
341 * is same as logical block size. The physical block
342 * size value is used to round down the offsets for
343 * reading and writing the primary and alternate volume
344 * headers at physical block boundary and will cause
345 * problems if it is less than the block size.
347 if (blockSize
< hfsmp
->hfs_physical_block_size
) {
348 hfsmp
->hfs_physical_block_size
= hfsmp
->hfs_logical_block_size
;
349 hfsmp
->hfs_log_per_phys
= 1;
353 * The VolumeHeader seems OK: transfer info from it into VCB
354 * Note - the VCB starts out clear (all zeros)
356 vcb
= HFSTOVCB(hfsmp
);
358 vcb
->vcbSigWord
= signature
;
359 vcb
->vcbJinfoBlock
= SWAP_BE32(vhp
->journalInfoBlock
);
360 vcb
->vcbLsMod
= to_bsd_time(SWAP_BE32(vhp
->modifyDate
));
361 vcb
->vcbAtrb
= SWAP_BE32(vhp
->attributes
);
362 vcb
->vcbClpSiz
= SWAP_BE32(vhp
->rsrcClumpSize
);
363 vcb
->vcbNxtCNID
= SWAP_BE32(vhp
->nextCatalogID
);
364 vcb
->vcbVolBkUp
= to_bsd_time(SWAP_BE32(vhp
->backupDate
));
365 vcb
->vcbWrCnt
= SWAP_BE32(vhp
->writeCount
);
366 vcb
->vcbFilCnt
= SWAP_BE32(vhp
->fileCount
);
367 vcb
->vcbDirCnt
= SWAP_BE32(vhp
->folderCount
);
369 /* copy 32 bytes of Finder info */
370 bcopy(vhp
->finderInfo
, vcb
->vcbFndrInfo
, sizeof(vhp
->finderInfo
));
372 vcb
->vcbAlBlSt
= 0; /* hfs+ allocation blocks start at first block of volume */
373 if ((hfsmp
->hfs_flags
& HFS_READ_ONLY
) == 0)
374 vcb
->vcbWrCnt
++; /* compensate for write of Volume Header on last flush */
376 /* Now fill in the Extended VCB info */
377 vcb
->nextAllocation
= SWAP_BE32(vhp
->nextAllocation
);
378 vcb
->totalBlocks
= SWAP_BE32(vhp
->totalBlocks
);
379 vcb
->allocLimit
= vcb
->totalBlocks
;
380 vcb
->freeBlocks
= SWAP_BE32(vhp
->freeBlocks
);
381 vcb
->blockSize
= blockSize
;
382 vcb
->encodingsBitmap
= SWAP_BE64(vhp
->encodingsBitmap
);
383 vcb
->localCreateDate
= SWAP_BE32(vhp
->createDate
);
385 vcb
->hfsPlusIOPosOffset
= embeddedOffset
;
387 /* Default to no free block reserve */
388 vcb
->reserveBlocks
= 0;
391 * Update the logical block size in the mount struct
392 * (currently set up from the wrapper MDB) using the
393 * new blocksize value:
395 hfsmp
->hfs_logBlockSize
= BestBlockSizeFit(vcb
->blockSize
, MAXBSIZE
, hfsmp
->hfs_logical_block_size
);
396 vcb
->vcbVBMIOSize
= min(vcb
->blockSize
, MAXPHYSIO
);
399 * Validate and initialize the location of the alternate volume header.
401 spare_sectors
= hfsmp
->hfs_logical_block_count
-
402 (((daddr64_t
)vcb
->totalBlocks
* blockSize
) /
403 hfsmp
->hfs_logical_block_size
);
405 if (spare_sectors
> (daddr64_t
)(blockSize
/ hfsmp
->hfs_logical_block_size
)) {
406 hfsmp
->hfs_alt_id_sector
= 0; /* partition has grown! */
408 hfsmp
->hfs_alt_id_sector
= (hfsmp
->hfsPlusIOPosOffset
/ hfsmp
->hfs_logical_block_size
) +
409 HFS_ALT_SECTOR(hfsmp
->hfs_logical_block_size
,
410 hfsmp
->hfs_logical_block_count
);
413 bzero(&cndesc
, sizeof(cndesc
));
414 cndesc
.cd_parentcnid
= kHFSRootParentID
;
415 cndesc
.cd_flags
|= CD_ISMETA
;
416 bzero(&cnattr
, sizeof(cnattr
));
417 cnattr
.ca_linkcount
= 1;
418 cnattr
.ca_mode
= S_IFREG
;
421 * Set up Extents B-tree vnode
423 cndesc
.cd_nameptr
= hfs_extname
;
424 cndesc
.cd_namelen
= strlen((char *)hfs_extname
);
425 cndesc
.cd_cnid
= cnattr
.ca_fileid
= kHFSExtentsFileID
;
427 cfork
.cf_size
= SWAP_BE64 (vhp
->extentsFile
.logicalSize
);
428 cfork
.cf_new_size
= 0;
429 cfork
.cf_clump
= SWAP_BE32 (vhp
->extentsFile
.clumpSize
);
430 cfork
.cf_blocks
= SWAP_BE32 (vhp
->extentsFile
.totalBlocks
);
431 cfork
.cf_vblocks
= 0;
432 cnattr
.ca_blocks
= cfork
.cf_blocks
;
433 for (i
= 0; i
< kHFSPlusExtentDensity
; i
++) {
434 cfork
.cf_extents
[i
].startBlock
=
435 SWAP_BE32 (vhp
->extentsFile
.extents
[i
].startBlock
);
436 cfork
.cf_extents
[i
].blockCount
=
437 SWAP_BE32 (vhp
->extentsFile
.extents
[i
].blockCount
);
439 retval
= hfs_getnewvnode(hfsmp
, NULL
, NULL
, &cndesc
, 0, &cnattr
, &cfork
,
440 &hfsmp
->hfs_extents_vp
);
445 hfsmp
->hfs_extents_cp
= VTOC(hfsmp
->hfs_extents_vp
);
446 hfs_unlock(hfsmp
->hfs_extents_cp
);
448 retval
= MacToVFSError(BTOpenPath(VTOF(hfsmp
->hfs_extents_vp
),
449 (KeyCompareProcPtr
) CompareExtentKeysPlus
));
455 * Set up Catalog B-tree vnode
457 cndesc
.cd_nameptr
= hfs_catname
;
458 cndesc
.cd_namelen
= strlen((char *)hfs_catname
);
459 cndesc
.cd_cnid
= cnattr
.ca_fileid
= kHFSCatalogFileID
;
461 cfork
.cf_size
= SWAP_BE64 (vhp
->catalogFile
.logicalSize
);
462 cfork
.cf_clump
= SWAP_BE32 (vhp
->catalogFile
.clumpSize
);
463 cfork
.cf_blocks
= SWAP_BE32 (vhp
->catalogFile
.totalBlocks
);
464 cfork
.cf_vblocks
= 0;
465 cnattr
.ca_blocks
= cfork
.cf_blocks
;
466 for (i
= 0; i
< kHFSPlusExtentDensity
; i
++) {
467 cfork
.cf_extents
[i
].startBlock
=
468 SWAP_BE32 (vhp
->catalogFile
.extents
[i
].startBlock
);
469 cfork
.cf_extents
[i
].blockCount
=
470 SWAP_BE32 (vhp
->catalogFile
.extents
[i
].blockCount
);
472 retval
= hfs_getnewvnode(hfsmp
, NULL
, NULL
, &cndesc
, 0, &cnattr
, &cfork
,
473 &hfsmp
->hfs_catalog_vp
);
477 hfsmp
->hfs_catalog_cp
= VTOC(hfsmp
->hfs_catalog_vp
);
478 hfs_unlock(hfsmp
->hfs_catalog_cp
);
480 retval
= MacToVFSError(BTOpenPath(VTOF(hfsmp
->hfs_catalog_vp
),
481 (KeyCompareProcPtr
) CompareExtendedCatalogKeys
));
485 if ((hfsmp
->hfs_flags
& HFS_X
) &&
486 BTGetInformation(VTOF(hfsmp
->hfs_catalog_vp
), 0, &btinfo
) == 0) {
487 if (btinfo
.keyCompareType
== kHFSBinaryCompare
) {
488 hfsmp
->hfs_flags
|= HFS_CASE_SENSITIVE
;
489 /* Install a case-sensitive key compare */
490 (void) BTOpenPath(VTOF(hfsmp
->hfs_catalog_vp
),
491 (KeyCompareProcPtr
)cat_binarykeycompare
);
496 * Set up Allocation file vnode
498 cndesc
.cd_nameptr
= hfs_vbmname
;
499 cndesc
.cd_namelen
= strlen((char *)hfs_vbmname
);
500 cndesc
.cd_cnid
= cnattr
.ca_fileid
= kHFSAllocationFileID
;
502 cfork
.cf_size
= SWAP_BE64 (vhp
->allocationFile
.logicalSize
);
503 cfork
.cf_clump
= SWAP_BE32 (vhp
->allocationFile
.clumpSize
);
504 cfork
.cf_blocks
= SWAP_BE32 (vhp
->allocationFile
.totalBlocks
);
505 cfork
.cf_vblocks
= 0;
506 cnattr
.ca_blocks
= cfork
.cf_blocks
;
507 for (i
= 0; i
< kHFSPlusExtentDensity
; i
++) {
508 cfork
.cf_extents
[i
].startBlock
=
509 SWAP_BE32 (vhp
->allocationFile
.extents
[i
].startBlock
);
510 cfork
.cf_extents
[i
].blockCount
=
511 SWAP_BE32 (vhp
->allocationFile
.extents
[i
].blockCount
);
513 retval
= hfs_getnewvnode(hfsmp
, NULL
, NULL
, &cndesc
, 0, &cnattr
, &cfork
,
514 &hfsmp
->hfs_allocation_vp
);
518 hfsmp
->hfs_allocation_cp
= VTOC(hfsmp
->hfs_allocation_vp
);
519 hfs_unlock(hfsmp
->hfs_allocation_cp
);
522 * Set up Attribute B-tree vnode
524 if (vhp
->attributesFile
.totalBlocks
!= 0) {
525 cndesc
.cd_nameptr
= hfs_attrname
;
526 cndesc
.cd_namelen
= strlen((char *)hfs_attrname
);
527 cndesc
.cd_cnid
= cnattr
.ca_fileid
= kHFSAttributesFileID
;
529 cfork
.cf_size
= SWAP_BE64 (vhp
->attributesFile
.logicalSize
);
530 cfork
.cf_clump
= SWAP_BE32 (vhp
->attributesFile
.clumpSize
);
531 cfork
.cf_blocks
= SWAP_BE32 (vhp
->attributesFile
.totalBlocks
);
532 cfork
.cf_vblocks
= 0;
533 cnattr
.ca_blocks
= cfork
.cf_blocks
;
534 for (i
= 0; i
< kHFSPlusExtentDensity
; i
++) {
535 cfork
.cf_extents
[i
].startBlock
=
536 SWAP_BE32 (vhp
->attributesFile
.extents
[i
].startBlock
);
537 cfork
.cf_extents
[i
].blockCount
=
538 SWAP_BE32 (vhp
->attributesFile
.extents
[i
].blockCount
);
540 retval
= hfs_getnewvnode(hfsmp
, NULL
, NULL
, &cndesc
, 0, &cnattr
, &cfork
,
541 &hfsmp
->hfs_attribute_vp
);
545 hfsmp
->hfs_attribute_cp
= VTOC(hfsmp
->hfs_attribute_vp
);
546 hfs_unlock(hfsmp
->hfs_attribute_cp
);
547 retval
= MacToVFSError(BTOpenPath(VTOF(hfsmp
->hfs_attribute_vp
),
548 (KeyCompareProcPtr
) hfs_attrkeycompare
));
555 * Set up Startup file vnode
557 if (vhp
->startupFile
.totalBlocks
!= 0) {
558 cndesc
.cd_nameptr
= hfs_startupname
;
559 cndesc
.cd_namelen
= strlen((char *)hfs_startupname
);
560 cndesc
.cd_cnid
= cnattr
.ca_fileid
= kHFSStartupFileID
;
562 cfork
.cf_size
= SWAP_BE64 (vhp
->startupFile
.logicalSize
);
563 cfork
.cf_clump
= SWAP_BE32 (vhp
->startupFile
.clumpSize
);
564 cfork
.cf_blocks
= SWAP_BE32 (vhp
->startupFile
.totalBlocks
);
565 cfork
.cf_vblocks
= 0;
566 cnattr
.ca_blocks
= cfork
.cf_blocks
;
567 for (i
= 0; i
< kHFSPlusExtentDensity
; i
++) {
568 cfork
.cf_extents
[i
].startBlock
=
569 SWAP_BE32 (vhp
->startupFile
.extents
[i
].startBlock
);
570 cfork
.cf_extents
[i
].blockCount
=
571 SWAP_BE32 (vhp
->startupFile
.extents
[i
].blockCount
);
573 retval
= hfs_getnewvnode(hfsmp
, NULL
, NULL
, &cndesc
, 0, &cnattr
, &cfork
,
574 &hfsmp
->hfs_startup_vp
);
578 hfsmp
->hfs_startup_cp
= VTOC(hfsmp
->hfs_startup_vp
);
579 hfs_unlock(hfsmp
->hfs_startup_cp
);
582 /* Pick up volume name and create date */
583 retval
= cat_idlookup(hfsmp
, kHFSRootFolderID
, 0, &cndesc
, &cnattr
, NULL
);
587 vcb
->vcbCrDate
= cnattr
.ca_itime
;
588 vcb
->volumeNameEncodingHint
= cndesc
.cd_encoding
;
589 bcopy(cndesc
.cd_nameptr
, vcb
->vcbVN
, min(255, cndesc
.cd_namelen
));
590 cat_releasedesc(&cndesc
);
592 /* mark the volume dirty (clear clean unmount bit) */
593 vcb
->vcbAtrb
&= ~kHFSVolumeUnmountedMask
;
594 if (hfsmp
->jnl
&& (hfsmp
->hfs_flags
& HFS_READ_ONLY
) == 0) {
595 hfs_flushvolumeheader(hfsmp
, TRUE
, 0);
598 /* kHFSHasFolderCount is only supported/updated on HFSX volumes */
599 if ((hfsmp
->hfs_flags
& HFS_X
) != 0) {
600 hfsmp
->hfs_flags
|= HFS_FOLDERCOUNT
;
604 // Check if we need to do late journal initialization. This only
605 // happens if a previous version of MacOS X (or 9) touched the disk.
606 // In that case hfs_late_journal_init() will go re-locate the journal
607 // and journal_info_block files and validate that they're still kosher.
609 if ( (vcb
->vcbAtrb
& kHFSVolumeJournaledMask
)
610 && (SWAP_BE32(vhp
->lastMountedVersion
) != kHFSJMountVersion
)
611 && (hfsmp
->jnl
== NULL
)) {
613 retval
= hfs_late_journal_init(hfsmp
, vhp
, args
);
617 // if the journal failed to open, then set the lastMountedVersion
618 // to be "FSK!" which fsck_hfs will see and force the fsck instead
619 // of just bailing out because the volume is journaled.
620 if (!(hfsmp
->hfs_flags
& HFS_READ_ONLY
)) {
621 HFSPlusVolumeHeader
*jvhp
;
622 daddr64_t mdb_offset
;
623 struct buf
*bp
= NULL
;
625 hfsmp
->hfs_flags
|= HFS_NEED_JNL_RESET
;
627 mdb_offset
= (daddr64_t
)((embeddedOffset
/ blockSize
) + HFS_PRI_SECTOR(blockSize
));
629 retval
= (int)buf_meta_bread(hfsmp
->hfs_devvp
,
630 HFS_PHYSBLK_ROUNDDOWN(mdb_offset
, hfsmp
->hfs_log_per_phys
),
631 hfsmp
->hfs_physical_block_size
, cred
, &bp
);
633 jvhp
= (HFSPlusVolumeHeader
*)(buf_dataptr(bp
) + HFS_PRI_OFFSET(hfsmp
->hfs_physical_block_size
));
635 if (SWAP_BE16(jvhp
->signature
) == kHFSPlusSigWord
|| SWAP_BE16(jvhp
->signature
) == kHFSXSigWord
) {
636 printf ("hfs(3): Journal replay fail. Writing lastMountVersion as FSK!\n");
637 jvhp
->lastMountedVersion
= SWAP_BE32(kFSKMountVersion
);
645 // clear this so the error exit path won't try to use it
652 } else if (hfsmp
->jnl
) {
653 vfs_setflags(hfsmp
->hfs_mp
, (u_int64_t
)((unsigned int)MNT_JOURNALED
));
655 } else if (hfsmp
->jnl
|| ((vcb
->vcbAtrb
& kHFSVolumeJournaledMask
) && (hfsmp
->hfs_flags
& HFS_READ_ONLY
))) {
656 struct cat_attr jinfo_attr
, jnl_attr
;
658 if (hfsmp
->hfs_flags
& HFS_READ_ONLY
) {
659 vcb
->vcbAtrb
&= ~kHFSVolumeJournaledMask
;
662 // if we're here we need to fill in the fileid's for the
663 // journal and journal_info_block.
664 hfsmp
->hfs_jnlinfoblkid
= GetFileInfo(vcb
, kRootDirID
, ".journal_info_block", &jinfo_attr
, NULL
);
665 hfsmp
->hfs_jnlfileid
= GetFileInfo(vcb
, kRootDirID
, ".journal", &jnl_attr
, NULL
);
666 if (hfsmp
->hfs_jnlinfoblkid
== 0 || hfsmp
->hfs_jnlfileid
== 0) {
667 printf("hfs: danger! couldn't find the file-id's for the journal or journal_info_block\n");
668 printf("hfs: jnlfileid %d, jnlinfoblkid %d\n", hfsmp
->hfs_jnlfileid
, hfsmp
->hfs_jnlinfoblkid
);
671 if (hfsmp
->hfs_flags
& HFS_READ_ONLY
) {
672 vcb
->vcbAtrb
|= kHFSVolumeJournaledMask
;
675 if (hfsmp
->jnl
== NULL
) {
676 vfs_clearflags(hfsmp
->hfs_mp
, (u_int64_t
)((unsigned int)MNT_JOURNALED
));
681 * Establish a metadata allocation zone.
683 hfs_metadatazone_init(hfsmp
);
686 * Make any metadata zone adjustments.
688 if (hfsmp
->hfs_flags
& HFS_METADATA_ZONE
) {
689 /* Keep the roving allocator out of the metadata zone. */
690 if (vcb
->nextAllocation
>= hfsmp
->hfs_metazone_start
&&
691 vcb
->nextAllocation
<= hfsmp
->hfs_metazone_end
) {
692 HFS_UPDATE_NEXT_ALLOCATION(hfsmp
, hfsmp
->hfs_metazone_end
+ 1);
696 /* Setup private/hidden directories for hardlinks. */
697 hfs_privatedir_init(hfsmp
, FILE_HARDLINKS
);
698 hfs_privatedir_init(hfsmp
, DIR_HARDLINKS
);
700 if ((hfsmp
->hfs_flags
& HFS_READ_ONLY
) == 0)
701 hfs_remove_orphans(hfsmp
);
703 if ( !(vcb
->vcbAtrb
& kHFSVolumeHardwareLockMask
) ) // if the disk is not write protected
705 MarkVCBDirty( vcb
); // mark VCB dirty so it will be written
709 * Allow hot file clustering if conditions allow.
711 if ((hfsmp
->hfs_flags
& HFS_METADATA_ZONE
) &&
712 ((hfsmp
->hfs_flags
& HFS_READ_ONLY
) == 0)) {
713 (void) hfs_recording_init(hfsmp
);
716 /* Force ACLs on HFS+ file systems. */
717 vfs_setextendedsecurity(HFSTOVFS(hfsmp
));
719 /* Check if volume supports writing of extent-based extended attributes */
720 hfs_check_volxattr(hfsmp
, HFS_SET_XATTREXTENTS_STATE
);
726 * A fatal error occurred and the volume cannot be mounted
727 * release any resources that we aquired...
729 if (hfsmp
->hfs_attribute_vp
)
730 ReleaseMetaFileVNode(hfsmp
->hfs_attribute_vp
);
731 ReleaseMetaFileVNode(hfsmp
->hfs_allocation_vp
);
732 ReleaseMetaFileVNode(hfsmp
->hfs_catalog_vp
);
733 ReleaseMetaFileVNode(hfsmp
->hfs_extents_vp
);
740 * ReleaseMetaFileVNode
744 static void ReleaseMetaFileVNode(struct vnode
*vp
)
748 if (vp
&& (fp
= VTOF(vp
))) {
749 if (fp
->fcbBTCBPtr
!= NULL
) {
750 (void)hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
);
751 (void) BTClosePath(fp
);
752 hfs_unlock(VTOC(vp
));
755 /* release the node even if BTClosePath fails */
762 /*************************************************************
764 * Unmounts a hfs volume.
765 * At this point vflush() has been called (to dump all non-metadata files)
767 *************************************************************/
771 hfsUnmount( register struct hfsmount
*hfsmp
, __unused
struct proc
*p
)
773 /* Get rid of our attribute data vnode (if any). */
774 if (hfsmp
->hfs_attrdata_vp
) {
775 vnode_t advp
= hfsmp
->hfs_attrdata_vp
;
777 if (vnode_get(advp
) == 0) {
778 vnode_rele_ext(advp
, O_EVTONLY
, 0);
781 hfsmp
->hfs_attrdata_vp
= NULLVP
;
784 if (hfsmp
->hfs_startup_vp
)
785 ReleaseMetaFileVNode(hfsmp
->hfs_startup_vp
);
787 if (hfsmp
->hfs_allocation_vp
)
788 ReleaseMetaFileVNode(hfsmp
->hfs_allocation_vp
);
790 if (hfsmp
->hfs_attribute_vp
)
791 ReleaseMetaFileVNode(hfsmp
->hfs_attribute_vp
);
793 ReleaseMetaFileVNode(hfsmp
->hfs_catalog_vp
);
794 ReleaseMetaFileVNode(hfsmp
->hfs_extents_vp
);
797 * Setting these pointers to NULL so that any references
798 * past this point will fail, and tell us the point of failure.
799 * Also, facilitates a check in hfs_update for a null catalog
802 hfsmp
->hfs_allocation_vp
= NULL
;
803 hfsmp
->hfs_attribute_vp
= NULL
;
804 hfsmp
->hfs_catalog_vp
= NULL
;
805 hfsmp
->hfs_extents_vp
= NULL
;
806 hfsmp
->hfs_startup_vp
= NULL
;
813 * Test if fork has overflow extents.
817 overflow_extents(struct filefork
*fp
)
822 // If the vnode pointer is NULL then we're being called
823 // from hfs_remove_orphans() with a faked-up filefork
824 // and therefore it has to be an HFS+ volume. Otherwise
825 // we check through the volume header to see what type
826 // of volume we're on.
828 if (FTOV(fp
) == NULL
|| VTOVCB(FTOV(fp
))->vcbSigWord
== kHFSPlusSigWord
) {
829 if (fp
->ff_extents
[7].blockCount
== 0)
832 blocks
= fp
->ff_extents
[0].blockCount
+
833 fp
->ff_extents
[1].blockCount
+
834 fp
->ff_extents
[2].blockCount
+
835 fp
->ff_extents
[3].blockCount
+
836 fp
->ff_extents
[4].blockCount
+
837 fp
->ff_extents
[5].blockCount
+
838 fp
->ff_extents
[6].blockCount
+
839 fp
->ff_extents
[7].blockCount
;
841 if (fp
->ff_extents
[2].blockCount
== 0)
844 blocks
= fp
->ff_extents
[0].blockCount
+
845 fp
->ff_extents
[1].blockCount
+
846 fp
->ff_extents
[2].blockCount
;
849 return (fp
->ff_blocks
> blocks
);
854 * Lock HFS system file(s).
858 hfs_systemfile_lock(struct hfsmount
*hfsmp
, int flags
, enum hfslocktype locktype
)
861 * Locking order is Catalog file, Attributes file, Startup file, Bitmap file, Extents file
863 if (flags
& SFL_CATALOG
) {
865 #ifdef HFS_CHECK_LOCK_ORDER
866 if (hfsmp
->hfs_attribute_cp
&& hfsmp
->hfs_attribute_cp
->c_lockowner
== current_thread()) {
867 panic("hfs_systemfile_lock: bad lock order (Attributes before Catalog)");
869 if (hfsmp
->hfs_startup_cp
&& hfsmp
->hfs_startup_cp
->c_lockowner
== current_thread()) {
870 panic("hfs_systemfile_lock: bad lock order (Startup before Catalog)");
872 if (hfsmp
-> hfs_extents_cp
&& hfsmp
->hfs_extents_cp
->c_lockowner
== current_thread()) {
873 panic("hfs_systemfile_lock: bad lock order (Extents before Catalog)");
875 #endif /* HFS_CHECK_LOCK_ORDER */
877 (void) hfs_lock(hfsmp
->hfs_catalog_cp
, locktype
);
879 * When the catalog file has overflow extents then
880 * also acquire the extents b-tree lock if its not
883 if ((flags
& SFL_EXTENTS
) == 0 &&
884 overflow_extents(VTOF(hfsmp
->hfs_catalog_vp
))) {
885 flags
|= SFL_EXTENTS
;
888 if (flags
& SFL_ATTRIBUTE
) {
890 #ifdef HFS_CHECK_LOCK_ORDER
891 if (hfsmp
->hfs_startup_cp
&& hfsmp
->hfs_startup_cp
->c_lockowner
== current_thread()) {
892 panic("hfs_systemfile_lock: bad lock order (Startup before Attributes)");
894 if (hfsmp
->hfs_extents_cp
&& hfsmp
->hfs_extents_cp
->c_lockowner
== current_thread()) {
895 panic("hfs_systemfile_lock: bad lock order (Extents before Attributes)");
897 #endif /* HFS_CHECK_LOCK_ORDER */
899 if (hfsmp
->hfs_attribute_cp
) {
900 (void) hfs_lock(hfsmp
->hfs_attribute_cp
, locktype
);
902 * When the attribute file has overflow extents then
903 * also acquire the extents b-tree lock if its not
906 if ((flags
& SFL_EXTENTS
) == 0 &&
907 overflow_extents(VTOF(hfsmp
->hfs_attribute_vp
))) {
908 flags
|= SFL_EXTENTS
;
911 flags
&= ~SFL_ATTRIBUTE
;
914 if (flags
& SFL_STARTUP
) {
915 #ifdef HFS_CHECK_LOCK_ORDER
916 if (hfsmp
-> hfs_extents_cp
&& hfsmp
->hfs_extents_cp
->c_lockowner
== current_thread()) {
917 panic("hfs_systemfile_lock: bad lock order (Extents before Startup)");
919 #endif /* HFS_CHECK_LOCK_ORDER */
921 (void) hfs_lock(hfsmp
->hfs_startup_cp
, locktype
);
923 * When the startup file has overflow extents then
924 * also acquire the extents b-tree lock if its not
927 if ((flags
& SFL_EXTENTS
) == 0 &&
928 overflow_extents(VTOF(hfsmp
->hfs_startup_vp
))) {
929 flags
|= SFL_EXTENTS
;
933 * To prevent locks being taken in the wrong order, the extent lock
934 * gets a bitmap lock as well.
936 if (flags
& (SFL_BITMAP
| SFL_EXTENTS
)) {
938 * Since the only bitmap operations are clearing and
939 * setting bits we always need exclusive access. And
940 * when we have a journal, we can "hide" behind that
941 * lock since we can only change the bitmap from
942 * within a transaction.
944 if (hfsmp
->jnl
|| (hfsmp
->hfs_allocation_cp
== NULL
)) {
945 flags
&= ~SFL_BITMAP
;
947 (void) hfs_lock(hfsmp
->hfs_allocation_cp
, HFS_EXCLUSIVE_LOCK
);
948 /* The bitmap lock is also grabbed when only extent lock
949 * was requested. Set the bitmap lock bit in the lock
950 * flags which callers will use during unlock.
955 if (flags
& SFL_EXTENTS
) {
957 * Since the extents btree lock is recursive we always
958 * need exclusive access.
960 (void) hfs_lock(hfsmp
->hfs_extents_cp
, HFS_EXCLUSIVE_LOCK
);
966 * unlock HFS system file(s).
970 hfs_systemfile_unlock(struct hfsmount
*hfsmp
, int flags
)
974 int numOfLockedBuffs
;
976 if (hfsmp
->jnl
== NULL
) {
978 lastfsync
= tv
.tv_sec
;
980 if (flags
& SFL_STARTUP
&& hfsmp
->hfs_startup_cp
) {
981 hfs_unlock(hfsmp
->hfs_startup_cp
);
983 if (flags
& SFL_ATTRIBUTE
&& hfsmp
->hfs_attribute_cp
) {
984 if (hfsmp
->jnl
== NULL
) {
985 BTGetLastSync((FCB
*)VTOF(hfsmp
->hfs_attribute_vp
), &lastfsync
);
986 numOfLockedBuffs
= count_lock_queue();
987 if ((numOfLockedBuffs
> kMaxLockedMetaBuffers
) ||
988 ((numOfLockedBuffs
> 1) && ((tv
.tv_sec
- lastfsync
) >
989 kMaxSecsForFsync
))) {
990 hfs_btsync(hfsmp
->hfs_attribute_vp
, HFS_SYNCTRANS
);
993 hfs_unlock(hfsmp
->hfs_attribute_cp
);
995 if (flags
& SFL_CATALOG
) {
996 if (hfsmp
->jnl
== NULL
) {
997 BTGetLastSync((FCB
*)VTOF(hfsmp
->hfs_catalog_vp
), &lastfsync
);
998 numOfLockedBuffs
= count_lock_queue();
999 if ((numOfLockedBuffs
> kMaxLockedMetaBuffers
) ||
1000 ((numOfLockedBuffs
> 1) && ((tv
.tv_sec
- lastfsync
) >
1001 kMaxSecsForFsync
))) {
1002 hfs_btsync(hfsmp
->hfs_catalog_vp
, HFS_SYNCTRANS
);
1005 hfs_unlock(hfsmp
->hfs_catalog_cp
);
1007 if (flags
& SFL_BITMAP
) {
1008 hfs_unlock(hfsmp
->hfs_allocation_cp
);
1010 if (flags
& SFL_EXTENTS
) {
1011 if (hfsmp
->jnl
== NULL
) {
1012 BTGetLastSync((FCB
*)VTOF(hfsmp
->hfs_extents_vp
), &lastfsync
);
1013 numOfLockedBuffs
= count_lock_queue();
1014 if ((numOfLockedBuffs
> kMaxLockedMetaBuffers
) ||
1015 ((numOfLockedBuffs
> 1) && ((tv
.tv_sec
- lastfsync
) >
1016 kMaxSecsForFsync
))) {
1017 hfs_btsync(hfsmp
->hfs_extents_vp
, HFS_SYNCTRANS
);
1020 hfs_unlock(hfsmp
->hfs_extents_cp
);
1028 * Check to see if a vnode is locked in the current context
1029 * This is to be used for debugging purposes only!!
1032 void RequireFileLock(FileReference vp
, int shareable
)
1036 /* The extents btree and allocation bitmap are always exclusive. */
1037 if (VTOC(vp
)->c_fileid
== kHFSExtentsFileID
||
1038 VTOC(vp
)->c_fileid
== kHFSAllocationFileID
) {
1042 locked
= VTOC(vp
)->c_lockowner
== (void *)current_thread();
1044 if (!locked
&& !shareable
) {
1045 switch (VTOC(vp
)->c_fileid
) {
1046 case kHFSExtentsFileID
:
1047 panic("extents btree not locked! v: 0x%08X\n #\n", (u_int
)vp
);
1049 case kHFSCatalogFileID
:
1050 panic("catalog btree not locked! v: 0x%08X\n #\n", (u_int
)vp
);
1052 case kHFSAllocationFileID
:
1053 /* The allocation file can hide behind the jornal lock. */
1054 if (VTOHFS(vp
)->jnl
== NULL
)
1055 panic("allocation file not locked! v: 0x%08X\n #\n", (u_int
)vp
);
1057 case kHFSStartupFileID
:
1058 panic("startup file not locked! v: 0x%08X\n #\n", (u_int
)vp
);
1059 case kHFSAttributesFileID
:
1060 panic("attributes btree not locked! v: 0x%08X\n #\n", (u_int
)vp
);
1069 * There are three ways to qualify for ownership rights on an object:
1071 * 1. (a) Your UID matches the cnode's UID.
1072 * (b) The object in question is owned by "unknown"
1073 * 2. (a) Permissions on the filesystem are being ignored and
1074 * your UID matches the replacement UID.
1075 * (b) Permissions on the filesystem are being ignored and
1076 * the replacement UID is "unknown".
1081 hfs_owner_rights(struct hfsmount
*hfsmp
, uid_t cnode_uid
, kauth_cred_t cred
,
1082 __unused
struct proc
*p
, int invokesuperuserstatus
)
1084 if ((kauth_cred_getuid(cred
) == cnode_uid
) || /* [1a] */
1085 (cnode_uid
== UNKNOWNUID
) || /* [1b] */
1086 ((((unsigned int)vfs_flags(HFSTOVFS(hfsmp
))) & MNT_UNKNOWNPERMISSIONS
) && /* [2] */
1087 ((kauth_cred_getuid(cred
) == hfsmp
->hfs_uid
) || /* [2a] */
1088 (hfsmp
->hfs_uid
== UNKNOWNUID
))) || /* [2b] */
1089 (invokesuperuserstatus
&& (suser(cred
, 0) == 0))) { /* [3] */
1097 unsigned long BestBlockSizeFit(unsigned long allocationBlockSize
,
1098 unsigned long blockSizeLimit
,
1099 unsigned long baseMultiple
) {
1101 Compute the optimal (largest) block size (no larger than allocationBlockSize) that is less than the
1102 specified limit but still an even multiple of the baseMultiple.
1104 int baseBlockCount
, blockCount
;
1105 unsigned long trialBlockSize
;
1107 if (allocationBlockSize
% baseMultiple
!= 0) {
1109 Whoops: the allocation blocks aren't even multiples of the specified base:
1110 no amount of dividing them into even parts will be a multiple, either then!
1112 return 512; /* Hope for the best */
1115 /* Try the obvious winner first, to prevent 12K allocation blocks, for instance,
1116 from being handled as two 6K logical blocks instead of 3 4K logical blocks.
1117 Even though the former (the result of the loop below) is the larger allocation
1118 block size, the latter is more efficient: */
1119 if (allocationBlockSize
% PAGE_SIZE
== 0) return PAGE_SIZE
;
1121 /* No clear winner exists: pick the largest even fraction <= MAXBSIZE: */
1122 baseBlockCount
= allocationBlockSize
/ baseMultiple
; /* Now guaranteed to be an even multiple */
1124 for (blockCount
= baseBlockCount
; blockCount
> 0; --blockCount
) {
1125 trialBlockSize
= blockCount
* baseMultiple
;
1126 if (allocationBlockSize
% trialBlockSize
== 0) { /* An even multiple? */
1127 if ((trialBlockSize
<= blockSizeLimit
) &&
1128 (trialBlockSize
% baseMultiple
== 0)) {
1129 return trialBlockSize
;
1134 /* Note: we should never get here, since blockCount = 1 should always work,
1135 but this is nice and safe and makes the compiler happy, too ... */
1142 GetFileInfo(ExtendedVCB
*vcb
, __unused u_int32_t dirid
, const char *name
,
1143 struct cat_attr
*fattr
, struct cat_fork
*forkinfo
)
1145 struct hfsmount
* hfsmp
;
1146 struct cat_desc jdesc
;
1150 if (vcb
->vcbSigWord
!= kHFSPlusSigWord
)
1153 hfsmp
= VCBTOHFS(vcb
);
1155 memset(&jdesc
, 0, sizeof(struct cat_desc
));
1156 jdesc
.cd_parentcnid
= kRootDirID
;
1157 jdesc
.cd_nameptr
= (const u_int8_t
*)name
;
1158 jdesc
.cd_namelen
= strlen(name
);
1160 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_SHARED_LOCK
);
1161 error
= cat_lookup(hfsmp
, &jdesc
, 0, NULL
, fattr
, forkinfo
, NULL
);
1162 hfs_systemfile_unlock(hfsmp
, lockflags
);
1165 return (fattr
->ca_fileid
);
1166 } else if (hfsmp
->hfs_flags
& HFS_READ_ONLY
) {
1170 return (0); /* XXX what callers expect on an error */
1175 * On HFS Plus Volumes, there can be orphaned files or directories
1176 * These are files or directories that were unlinked while busy.
1177 * If the volume was not cleanly unmounted then some of these may
1178 * have persisted and need to be removed.
1182 hfs_remove_orphans(struct hfsmount
* hfsmp
)
1184 struct BTreeIterator
* iterator
= NULL
;
1185 struct FSBufferDescriptor btdata
;
1186 struct HFSPlusCatalogFile filerec
;
1187 struct HFSPlusCatalogKey
* keyp
;
1188 struct proc
*p
= current_proc();
1194 cat_cookie_t cookie
;
1200 int orphanedlinks
= 0;
1202 bzero(&cookie
, sizeof(cookie
));
1204 if (hfsmp
->hfs_flags
& HFS_CLEANED_ORPHANS
)
1207 vcb
= HFSTOVCB(hfsmp
);
1208 fcb
= VTOF(hfsmp
->hfs_catalog_vp
);
1210 btdata
.bufferAddress
= &filerec
;
1211 btdata
.itemSize
= sizeof(filerec
);
1212 btdata
.itemCount
= 1;
1214 MALLOC(iterator
, struct BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
1215 bzero(iterator
, sizeof(*iterator
));
1217 /* Build a key to "temp" */
1218 keyp
= (HFSPlusCatalogKey
*)&iterator
->key
;
1219 keyp
->parentID
= hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
;
1220 keyp
->nodeName
.length
= 4; /* "temp" */
1221 keyp
->keyLength
= kHFSPlusCatalogKeyMinimumLength
+ keyp
->nodeName
.length
* 2;
1222 keyp
->nodeName
.unicode
[0] = 't';
1223 keyp
->nodeName
.unicode
[1] = 'e';
1224 keyp
->nodeName
.unicode
[2] = 'm';
1225 keyp
->nodeName
.unicode
[3] = 'p';
1228 * Position the iterator just before the first real temp file/dir.
1230 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_EXCLUSIVE_LOCK
);
1231 (void) BTSearchRecord(fcb
, iterator
, NULL
, NULL
, iterator
);
1232 hfs_systemfile_unlock(hfsmp
, lockflags
);
1234 /* Visit all the temp files/dirs in the HFS+ private directory. */
1236 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_EXCLUSIVE_LOCK
);
1237 result
= BTIterateRecord(fcb
, kBTreeNextRecord
, iterator
, &btdata
, NULL
);
1238 hfs_systemfile_unlock(hfsmp
, lockflags
);
1241 if (keyp
->parentID
!= hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
)
1244 (void) utf8_encodestr(keyp
->nodeName
.unicode
, keyp
->nodeName
.length
* 2,
1245 (u_int8_t
*)filename
, &namelen
, sizeof(filename
), 0, 0);
1247 (void) snprintf(tempname
, sizeof(tempname
), "%s%d",
1248 HFS_DELETE_PREFIX
, filerec
.fileID
);
1251 * Delete all files (and directories) named "tempxxx",
1252 * where xxx is the file's cnid in decimal.
1255 if (bcmp(tempname
, filename
, namelen
) == 0) {
1256 struct filefork dfork
;
1257 struct filefork rfork
;
1260 bzero(&dfork
, sizeof(dfork
));
1261 bzero(&rfork
, sizeof(rfork
));
1262 bzero(&cnode
, sizeof(cnode
));
1264 /* Delete any attributes, ignore errors */
1265 (void) hfs_removeallattr(hfsmp
, filerec
.fileID
);
1267 if (hfs_start_transaction(hfsmp
) != 0) {
1268 printf("hfs_remove_orphans: failed to start transaction\n");
1274 * Reserve some space in the Catalog file.
1276 if (cat_preflight(hfsmp
, CAT_DELETE
, &cookie
, p
) != 0) {
1277 printf("hfs_remove_orphans: cat_preflight failed\n");
1282 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
| SFL_ATTRIBUTE
| SFL_EXTENTS
| SFL_BITMAP
, HFS_EXCLUSIVE_LOCK
);
1285 /* Build a fake cnode */
1286 cat_convertattr(hfsmp
, (CatalogRecord
*)&filerec
, &cnode
.c_attr
,
1287 &dfork
.ff_data
, &rfork
.ff_data
);
1288 cnode
.c_desc
.cd_parentcnid
= hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
;
1289 cnode
.c_desc
.cd_nameptr
= (const u_int8_t
*)filename
;
1290 cnode
.c_desc
.cd_namelen
= namelen
;
1291 cnode
.c_desc
.cd_cnid
= cnode
.c_attr
.ca_fileid
;
1292 cnode
.c_blocks
= dfork
.ff_blocks
+ rfork
.ff_blocks
;
1294 /* Position iterator at previous entry */
1295 if (BTIterateRecord(fcb
, kBTreePrevRecord
, iterator
,
1300 /* Truncate the file to zero (both forks) */
1301 if (dfork
.ff_blocks
> 0) {
1304 dfork
.ff_cp
= &cnode
;
1305 cnode
.c_datafork
= &dfork
;
1306 cnode
.c_rsrcfork
= NULL
;
1307 fsize
= (u_int64_t
)dfork
.ff_blocks
* (u_int64_t
)HFSTOVCB(hfsmp
)->blockSize
;
1309 if (fsize
> HFS_BIGFILE_SIZE
&& overflow_extents(&dfork
)) {
1310 fsize
-= HFS_BIGFILE_SIZE
;
1315 if (TruncateFileC(vcb
, (FCB
*)&dfork
, fsize
, false) != 0) {
1316 printf("error truncting data fork!\n");
1321 // if we're iteratively truncating this file down,
1322 // then end the transaction and start a new one so
1323 // that no one transaction gets too big.
1325 if (fsize
> 0 && started_tr
) {
1326 /* Drop system file locks before starting
1327 * another transaction to preserve lock order.
1329 hfs_systemfile_unlock(hfsmp
, lockflags
);
1331 hfs_end_transaction(hfsmp
);
1333 if (hfs_start_transaction(hfsmp
) != 0) {
1337 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
| SFL_ATTRIBUTE
| SFL_EXTENTS
| SFL_BITMAP
, HFS_EXCLUSIVE_LOCK
);
1343 if (rfork
.ff_blocks
> 0) {
1344 rfork
.ff_cp
= &cnode
;
1345 cnode
.c_datafork
= NULL
;
1346 cnode
.c_rsrcfork
= &rfork
;
1347 if (TruncateFileC(vcb
, (FCB
*)&rfork
, 0, false) != 0) {
1348 printf("error truncting rsrc fork!\n");
1353 /* Remove the file or folder record from the Catalog */
1354 if (cat_delete(hfsmp
, &cnode
.c_desc
, &cnode
.c_attr
) != 0) {
1355 printf("hfs_remove_orphans: error deleting cat rec for id %d!\n", cnode
.c_desc
.cd_cnid
);
1356 hfs_systemfile_unlock(hfsmp
, lockflags
);
1358 hfs_volupdate(hfsmp
, VOL_UPDATE
, 0);
1363 /* Update parent and volume counts */
1364 hfsmp
->hfs_private_attr
[FILE_HARDLINKS
].ca_entries
--;
1365 if (cnode
.c_attr
.ca_mode
& S_IFDIR
) {
1366 DEC_FOLDERCOUNT(hfsmp
, hfsmp
->hfs_private_attr
[FILE_HARDLINKS
]);
1369 (void)cat_update(hfsmp
, &hfsmp
->hfs_private_desc
[FILE_HARDLINKS
],
1370 &hfsmp
->hfs_private_attr
[FILE_HARDLINKS
], NULL
, NULL
);
1372 /* Drop locks and end the transaction */
1373 hfs_systemfile_unlock(hfsmp
, lockflags
);
1374 cat_postflight(hfsmp
, &cookie
, p
);
1375 catlock
= catreserve
= 0;
1378 Now that Catalog is unlocked, update the volume info, making
1379 sure to differentiate between files and directories
1381 if (cnode
.c_attr
.ca_mode
& S_IFDIR
) {
1382 hfs_volupdate(hfsmp
, VOL_RMDIR
, 0);
1385 hfs_volupdate(hfsmp
, VOL_RMFILE
, 0);
1389 hfs_end_transaction(hfsmp
);
1395 if (orphanedlinks
> 0)
1396 printf("HFS: Removed %d orphaned unlinked files or directories \n", orphanedlinks
);
1399 hfs_systemfile_unlock(hfsmp
, lockflags
);
1402 cat_postflight(hfsmp
, &cookie
, p
);
1405 hfs_end_transaction(hfsmp
);
1408 FREE(iterator
, M_TEMP
);
1409 hfsmp
->hfs_flags
|= HFS_CLEANED_ORPHANS
;
1414 * This will return the correct logical block size for a given vnode.
1415 * For most files, it is the allocation block size, for meta data like
1416 * BTrees, this is kept as part of the BTree private nodeSize
1419 GetLogicalBlockSize(struct vnode
*vp
)
1421 u_int32_t logBlockSize
;
1423 DBG_ASSERT(vp
!= NULL
);
1425 /* start with default */
1426 logBlockSize
= VTOHFS(vp
)->hfs_logBlockSize
;
1428 if (vnode_issystem(vp
)) {
1429 if (VTOF(vp
)->fcbBTCBPtr
!= NULL
) {
1430 BTreeInfoRec bTreeInfo
;
1433 * We do not lock the BTrees, because if we are getting block..then the tree
1434 * should be locked in the first place.
1435 * We just want the nodeSize wich will NEVER change..so even if the world
1436 * is changing..the nodeSize should remain the same. Which argues why lock
1437 * it in the first place??
1440 (void) BTGetInformation (VTOF(vp
), kBTreeInfoVersion
, &bTreeInfo
);
1442 logBlockSize
= bTreeInfo
.nodeSize
;
1444 } else if (VTOC(vp
)->c_fileid
== kHFSAllocationFileID
) {
1445 logBlockSize
= VTOVCB(vp
)->vcbVBMIOSize
;
1449 DBG_ASSERT(logBlockSize
> 0);
1451 return logBlockSize
;
1456 hfs_freeblks(struct hfsmount
* hfsmp
, int wantreserve
)
1463 * We don't bother taking the mount lock
1464 * to look at these values since the values
1465 * themselves are each updated automically
1466 * on aligned addresses.
1468 freeblks
= hfsmp
->freeBlocks
;
1469 rsrvblks
= hfsmp
->reserveBlocks
;
1470 loanblks
= hfsmp
->loanedBlocks
;
1472 if (freeblks
> rsrvblks
)
1473 freeblks
-= rsrvblks
;
1477 if (freeblks
> loanblks
)
1478 freeblks
-= loanblks
;
1482 #ifdef HFS_SPARSE_DEV
1484 * When the underlying device is sparse, check the
1485 * available space on the backing store volume.
1487 if ((hfsmp
->hfs_flags
& HFS_HAS_SPARSE_DEVICE
) && hfsmp
->hfs_backingfs_rootvp
) {
1488 struct vfsstatfs
*vfsp
; /* 272 bytes */
1489 u_int64_t vfreeblks
;
1490 u_int32_t loanedblks
;
1491 struct mount
* backingfs_mp
;
1494 backingfs_mp
= vnode_mount(hfsmp
->hfs_backingfs_rootvp
);
1497 if ((now
.tv_sec
- hfsmp
->hfs_last_backingstatfs
) >= 1) {
1498 vfs_update_vfsstat(backingfs_mp
, vfs_context_kernel(), VFS_KERNEL_EVENT
);
1499 hfsmp
->hfs_last_backingstatfs
= now
.tv_sec
;
1502 if ((vfsp
= vfs_statfs(backingfs_mp
))) {
1503 HFS_MOUNT_LOCK(hfsmp
, TRUE
);
1504 vfreeblks
= vfsp
->f_bavail
;
1505 /* Normalize block count if needed. */
1506 if (vfsp
->f_bsize
!= hfsmp
->blockSize
) {
1507 vfreeblks
= ((u_int64_t
)vfreeblks
* (u_int64_t
)(vfsp
->f_bsize
)) / hfsmp
->blockSize
;
1509 if (vfreeblks
> (unsigned int)hfsmp
->hfs_sparsebandblks
)
1510 vfreeblks
-= hfsmp
->hfs_sparsebandblks
;
1514 /* Take into account any delayed allocations. */
1515 loanedblks
= 2 * hfsmp
->loanedBlocks
;
1516 if (vfreeblks
> loanedblks
)
1517 vfreeblks
-= loanedblks
;
1521 freeblks
= MIN(vfreeblks
, freeblks
);
1522 HFS_MOUNT_UNLOCK(hfsmp
, TRUE
);
1525 #endif /* HFS_SPARSE_DEV */
1531 * Map HFS Common errors (negative) to BSD error codes (positive).
1532 * Positive errors (ie BSD errors) are passed through unchanged.
1534 short MacToVFSError(OSErr err
)
1540 case dskFulErr
: /* -34 */
1541 case btNoSpaceAvail
: /* -32733 */
1543 case fxOvFlErr
: /* -32750 */
1546 case btBadNode
: /* -32731 */
1549 case memFullErr
: /* -108 */
1550 return ENOMEM
; /* +12 */
1552 case cmExists
: /* -32718 */
1553 case btExists
: /* -32734 */
1554 return EEXIST
; /* +17 */
1556 case cmNotFound
: /* -32719 */
1557 case btNotFound
: /* -32735 */
1558 return ENOENT
; /* 28 */
1560 case cmNotEmpty
: /* -32717 */
1561 return ENOTEMPTY
; /* 66 */
1563 case cmFThdDirErr
: /* -32714 */
1564 return EISDIR
; /* 21 */
1566 case fxRangeErr
: /* -32751 */
1569 case bdNamErr
: /* -37 */
1570 return ENAMETOOLONG
; /* 63 */
1572 case paramErr
: /* -50 */
1573 case fileBoundsErr
: /* -1309 */
1574 return EINVAL
; /* +22 */
1576 case fsBTBadNodeSize
:
1580 return EIO
; /* +5 */
1586 * Find the current thread's directory hint for a given index.
1588 * Requires an exclusive lock on directory cnode.
1590 * Use detach if the cnode lock must be dropped while the hint is still active.
1594 hfs_getdirhint(struct cnode
*dcp
, int index
, int detach
)
1597 directoryhint_t
*hint
;
1598 boolean_t need_remove
, need_init
;
1599 const u_int8_t
* name
;
1604 * Look for an existing hint first. If not found, create a new one (when
1605 * the list is not full) or recycle the oldest hint. Since new hints are
1606 * always added to the head of the list, the last hint is always the
1609 TAILQ_FOREACH(hint
, &dcp
->c_hintlist
, dh_link
) {
1610 if (hint
->dh_index
== index
)
1613 if (hint
!= NULL
) { /* found an existing hint */
1616 } else { /* cannot find an existing hint */
1618 if (dcp
->c_dirhintcnt
< HFS_MAXDIRHINTS
) { /* we don't need recycling */
1619 /* Create a default directory hint */
1620 MALLOC_ZONE(hint
, directoryhint_t
*, sizeof(directoryhint_t
), M_HFSDIRHINT
, M_WAITOK
);
1621 ++dcp
->c_dirhintcnt
;
1622 need_remove
= false;
1623 } else { /* recycle the last (i.e., the oldest) hint */
1624 hint
= TAILQ_LAST(&dcp
->c_hintlist
, hfs_hinthead
);
1625 if ((hint
->dh_desc
.cd_flags
& CD_HASBUF
) &&
1626 (name
= hint
->dh_desc
.cd_nameptr
)) {
1627 hint
->dh_desc
.cd_nameptr
= NULL
;
1628 hint
->dh_desc
.cd_namelen
= 0;
1629 hint
->dh_desc
.cd_flags
&= ~CD_HASBUF
;
1630 vfs_removename((const char *)name
);
1637 TAILQ_REMOVE(&dcp
->c_hintlist
, hint
, dh_link
);
1640 --dcp
->c_dirhintcnt
;
1642 TAILQ_INSERT_HEAD(&dcp
->c_hintlist
, hint
, dh_link
);
1645 hint
->dh_index
= index
;
1646 hint
->dh_desc
.cd_flags
= 0;
1647 hint
->dh_desc
.cd_encoding
= 0;
1648 hint
->dh_desc
.cd_namelen
= 0;
1649 hint
->dh_desc
.cd_nameptr
= NULL
;
1650 hint
->dh_desc
.cd_parentcnid
= dcp
->c_fileid
;
1651 hint
->dh_desc
.cd_hint
= dcp
->c_childhint
;
1652 hint
->dh_desc
.cd_cnid
= 0;
1654 hint
->dh_time
= tv
.tv_sec
;
1659 * Release a single directory hint.
1661 * Requires an exclusive lock on directory cnode.
1665 hfs_reldirhint(struct cnode
*dcp
, directoryhint_t
* relhint
)
1667 const u_int8_t
* name
;
1668 directoryhint_t
*hint
;
1670 /* Check if item is on list (could be detached) */
1671 TAILQ_FOREACH(hint
, &dcp
->c_hintlist
, dh_link
) {
1672 if (hint
== relhint
) {
1673 TAILQ_REMOVE(&dcp
->c_hintlist
, relhint
, dh_link
);
1674 --dcp
->c_dirhintcnt
;
1678 name
= relhint
->dh_desc
.cd_nameptr
;
1679 if ((relhint
->dh_desc
.cd_flags
& CD_HASBUF
) && (name
!= NULL
)) {
1680 relhint
->dh_desc
.cd_nameptr
= NULL
;
1681 relhint
->dh_desc
.cd_namelen
= 0;
1682 relhint
->dh_desc
.cd_flags
&= ~CD_HASBUF
;
1683 vfs_removename((const char *)name
);
1685 FREE_ZONE(relhint
, sizeof(directoryhint_t
), M_HFSDIRHINT
);
1689 * Release directory hints for given directory
1691 * Requires an exclusive lock on directory cnode.
1695 hfs_reldirhints(struct cnode
*dcp
, int stale_hints_only
)
1698 directoryhint_t
*hint
, *prev
;
1699 const u_int8_t
* name
;
1701 if (stale_hints_only
)
1704 /* searching from the oldest to the newest, so we can stop early when releasing stale hints only */
1705 for (hint
= TAILQ_LAST(&dcp
->c_hintlist
, hfs_hinthead
); hint
!= NULL
; hint
= prev
) {
1706 if (stale_hints_only
&& (tv
.tv_sec
- hint
->dh_time
) < HFS_DIRHINT_TTL
)
1707 break; /* stop here if this entry is too new */
1708 name
= hint
->dh_desc
.cd_nameptr
;
1709 if ((hint
->dh_desc
.cd_flags
& CD_HASBUF
) && (name
!= NULL
)) {
1710 hint
->dh_desc
.cd_nameptr
= NULL
;
1711 hint
->dh_desc
.cd_namelen
= 0;
1712 hint
->dh_desc
.cd_flags
&= ~CD_HASBUF
;
1713 vfs_removename((const char *)name
);
1715 prev
= TAILQ_PREV(hint
, hfs_hinthead
, dh_link
); /* must save this pointer before calling FREE_ZONE on this node */
1716 TAILQ_REMOVE(&dcp
->c_hintlist
, hint
, dh_link
);
1717 FREE_ZONE(hint
, sizeof(directoryhint_t
), M_HFSDIRHINT
);
1718 --dcp
->c_dirhintcnt
;
1723 * Insert a detached directory hint back into the list of dirhints.
1725 * Requires an exclusive lock on directory cnode.
1729 hfs_insertdirhint(struct cnode
*dcp
, directoryhint_t
* hint
)
1731 directoryhint_t
*test
;
1733 TAILQ_FOREACH(test
, &dcp
->c_hintlist
, dh_link
) {
1735 panic("hfs_insertdirhint: hint %p already on list!", hint
);
1738 TAILQ_INSERT_HEAD(&dcp
->c_hintlist
, hint
, dh_link
);
1739 ++dcp
->c_dirhintcnt
;
1743 * Perform a case-insensitive compare of two UTF-8 filenames.
1745 * Returns 0 if the strings match.
1749 hfs_namecmp(const u_int8_t
*str1
, size_t len1
, const u_int8_t
*str2
, size_t len2
)
1751 u_int16_t
*ustr1
, *ustr2
;
1752 size_t ulen1
, ulen2
;
1759 maxbytes
= kHFSPlusMaxFileNameChars
<< 1;
1760 MALLOC(ustr1
, u_int16_t
*, maxbytes
<< 1, M_TEMP
, M_WAITOK
);
1761 ustr2
= ustr1
+ (maxbytes
>> 1);
1763 if (utf8_decodestr(str1
, len1
, ustr1
, &ulen1
, maxbytes
, ':', 0) != 0)
1765 if (utf8_decodestr(str2
, len2
, ustr2
, &ulen2
, maxbytes
, ':', 0) != 0)
1768 cmp
= FastUnicodeCompare(ustr1
, ulen1
>>1, ustr2
, ulen2
>>1);
1770 FREE(ustr1
, M_TEMP
);
1777 hfs_early_journal_init(struct hfsmount
*hfsmp
, HFSPlusVolumeHeader
*vhp
,
1778 void *_args
, off_t embeddedOffset
, daddr64_t mdb_offset
,
1779 HFSMasterDirectoryBlock
*mdbp
, kauth_cred_t cred
)
1781 JournalInfoBlock
*jibp
;
1782 struct buf
*jinfo_bp
, *bp
;
1783 int sectors_per_fsblock
, arg_flags
=0, arg_tbufsz
=0;
1785 uint32_t blksize
= hfsmp
->hfs_logical_block_size
;
1786 struct vnode
*devvp
;
1787 struct hfs_mount_args
*args
= _args
;
1788 u_int32_t jib_flags
;
1789 u_int64_t jib_offset
;
1792 devvp
= hfsmp
->hfs_devvp
;
1794 if (args
!= NULL
&& (args
->flags
& HFSFSMNT_EXTENDED_ARGS
)) {
1795 arg_flags
= args
->journal_flags
;
1796 arg_tbufsz
= args
->journal_tbuffer_size
;
1799 sectors_per_fsblock
= SWAP_BE32(vhp
->blockSize
) / blksize
;
1801 retval
= (int)buf_meta_bread(devvp
,
1802 (daddr64_t
)((embeddedOffset
/blksize
) +
1803 (SWAP_BE32(vhp
->journalInfoBlock
)*sectors_per_fsblock
)),
1804 SWAP_BE32(vhp
->blockSize
), cred
, &jinfo_bp
);
1808 jibp
= (JournalInfoBlock
*)buf_dataptr(jinfo_bp
);
1809 jib_flags
= SWAP_BE32(jibp
->flags
);
1810 jib_offset
= SWAP_BE64(jibp
->offset
);
1811 jib_size
= SWAP_BE64(jibp
->size
);
1813 if (jib_flags
& kJIJournalInFSMask
) {
1814 hfsmp
->jvp
= hfsmp
->hfs_devvp
;
1816 printf("hfs: journal not stored in fs! don't know what to do.\n");
1817 buf_brelse(jinfo_bp
);
1821 // save this off for the hack-y check in hfs_remove()
1822 hfsmp
->jnl_start
= jib_offset
/ SWAP_BE32(vhp
->blockSize
);
1823 hfsmp
->jnl_size
= jib_size
;
1825 if ((hfsmp
->hfs_flags
& HFS_READ_ONLY
) && (vfs_flags(hfsmp
->hfs_mp
) & MNT_ROOTFS
) == 0) {
1826 // if the file system is read-only, check if the journal is empty.
1827 // if it is, then we can allow the mount. otherwise we have to
1829 retval
= journal_is_clean(hfsmp
->jvp
,
1830 jib_offset
+ embeddedOffset
,
1833 hfsmp
->hfs_logical_block_size
);
1837 buf_brelse(jinfo_bp
);
1840 printf("hfs: early journal init: volume on %s is read-only and journal is dirty. Can not mount volume.\n",
1847 if (jib_flags
& kJIJournalNeedInitMask
) {
1848 printf("hfs: Initializing the journal (joffset 0x%llx sz 0x%llx)...\n",
1849 jib_offset
+ embeddedOffset
, jib_size
);
1850 hfsmp
->jnl
= journal_create(hfsmp
->jvp
,
1851 jib_offset
+ embeddedOffset
,
1857 hfs_sync_metadata
, hfsmp
->hfs_mp
);
1859 // no need to start a transaction here... if this were to fail
1860 // we'd just re-init it on the next mount.
1861 jib_flags
&= ~kJIJournalNeedInitMask
;
1862 jibp
->flags
= SWAP_BE32(jib_flags
);
1863 buf_bwrite(jinfo_bp
);
1867 //printf("hfs: Opening the journal (joffset 0x%llx sz 0x%llx vhp_blksize %d)...\n",
1868 // jib_offset + embeddedOffset,
1869 // jib_size, SWAP_BE32(vhp->blockSize));
1871 hfsmp
->jnl
= journal_open(hfsmp
->jvp
,
1872 jib_offset
+ embeddedOffset
,
1878 hfs_sync_metadata
, hfsmp
->hfs_mp
);
1880 buf_brelse(jinfo_bp
);
1884 if (hfsmp
->jnl
&& mdbp
) {
1885 // reload the mdb because it could have changed
1886 // if the journal had to be replayed.
1887 if (mdb_offset
== 0) {
1888 mdb_offset
= (daddr64_t
)((embeddedOffset
/ blksize
) + HFS_PRI_SECTOR(blksize
));
1890 retval
= (int)buf_meta_bread(devvp
,
1891 HFS_PHYSBLK_ROUNDDOWN(mdb_offset
, hfsmp
->hfs_log_per_phys
),
1892 hfsmp
->hfs_physical_block_size
, cred
, &bp
);
1895 printf("hfs: failed to reload the mdb after opening the journal (retval %d)!\n",
1899 bcopy((char *)buf_dataptr(bp
) + HFS_PRI_OFFSET(hfsmp
->hfs_physical_block_size
), mdbp
, 512);
1906 //printf("journal @ 0x%x\n", hfsmp->jnl);
1908 // if we expected the journal to be there and we couldn't
1909 // create it or open it then we have to bail out.
1910 if (hfsmp
->jnl
== NULL
) {
1911 printf("hfs: early jnl init: failed to open/create the journal (retval %d).\n", retval
);
1920 // This function will go and re-locate the .journal_info_block and
1921 // the .journal files in case they moved (which can happen if you
1922 // run Norton SpeedDisk). If we fail to find either file we just
1923 // disable journaling for this volume and return. We turn off the
1924 // journaling bit in the vcb and assume it will get written to disk
1925 // later (if it doesn't on the next mount we'd do the same thing
1926 // again which is harmless). If we disable journaling we don't
1927 // return an error so that the volume is still mountable.
1929 // If the info we find for the .journal_info_block and .journal files
1930 // isn't what we had stored, we re-set our cached info and proceed
1931 // with opening the journal normally.
1934 hfs_late_journal_init(struct hfsmount
*hfsmp
, HFSPlusVolumeHeader
*vhp
, void *_args
)
1936 JournalInfoBlock
*jibp
;
1937 struct buf
*jinfo_bp
;
1938 int sectors_per_fsblock
, arg_flags
=0, arg_tbufsz
=0;
1939 int retval
, write_jibp
= 0, recreate_journal
= 0;
1940 struct vnode
*devvp
;
1941 struct cat_attr jib_attr
, jattr
;
1942 struct cat_fork jib_fork
, jfork
;
1945 struct hfs_mount_args
*args
= _args
;
1946 u_int32_t jib_flags
;
1947 u_int64_t jib_offset
;
1950 devvp
= hfsmp
->hfs_devvp
;
1951 vcb
= HFSTOVCB(hfsmp
);
1953 if (args
!= NULL
&& (args
->flags
& HFSFSMNT_EXTENDED_ARGS
)) {
1954 if (args
->journal_disable
) {
1958 arg_flags
= args
->journal_flags
;
1959 arg_tbufsz
= args
->journal_tbuffer_size
;
1962 fid
= GetFileInfo(vcb
, kRootDirID
, ".journal_info_block", &jib_attr
, &jib_fork
);
1963 if (fid
== 0 || jib_fork
.cf_extents
[0].startBlock
== 0 || jib_fork
.cf_size
== 0) {
1964 printf("hfs: can't find the .journal_info_block! disabling journaling (start: %d).\n",
1965 jib_fork
.cf_extents
[0].startBlock
);
1966 vcb
->vcbAtrb
&= ~kHFSVolumeJournaledMask
;
1969 hfsmp
->hfs_jnlinfoblkid
= fid
;
1971 // make sure the journal_info_block begins where we think it should.
1972 if (SWAP_BE32(vhp
->journalInfoBlock
) != jib_fork
.cf_extents
[0].startBlock
) {
1973 printf("hfs: The journal_info_block moved (was: %d; is: %d). Fixing up\n",
1974 SWAP_BE32(vhp
->journalInfoBlock
), jib_fork
.cf_extents
[0].startBlock
);
1976 vcb
->vcbJinfoBlock
= jib_fork
.cf_extents
[0].startBlock
;
1977 vhp
->journalInfoBlock
= SWAP_BE32(jib_fork
.cf_extents
[0].startBlock
);
1978 recreate_journal
= 1;
1982 sectors_per_fsblock
= SWAP_BE32(vhp
->blockSize
) / hfsmp
->hfs_logical_block_size
;
1983 retval
= (int)buf_meta_bread(devvp
,
1984 (daddr64_t
)(vcb
->hfsPlusIOPosOffset
/ hfsmp
->hfs_logical_block_size
+
1985 (SWAP_BE32(vhp
->journalInfoBlock
)*sectors_per_fsblock
)),
1986 SWAP_BE32(vhp
->blockSize
), NOCRED
, &jinfo_bp
);
1988 printf("hfs: can't read journal info block. disabling journaling.\n");
1989 vcb
->vcbAtrb
&= ~kHFSVolumeJournaledMask
;
1993 jibp
= (JournalInfoBlock
*)buf_dataptr(jinfo_bp
);
1994 jib_flags
= SWAP_BE32(jibp
->flags
);
1995 jib_offset
= SWAP_BE64(jibp
->offset
);
1996 jib_size
= SWAP_BE64(jibp
->size
);
1998 fid
= GetFileInfo(vcb
, kRootDirID
, ".journal", &jattr
, &jfork
);
1999 if (fid
== 0 || jfork
.cf_extents
[0].startBlock
== 0 || jfork
.cf_size
== 0) {
2000 printf("hfs: can't find the journal file! disabling journaling (start: %d)\n",
2001 jfork
.cf_extents
[0].startBlock
);
2002 buf_brelse(jinfo_bp
);
2003 vcb
->vcbAtrb
&= ~kHFSVolumeJournaledMask
;
2006 hfsmp
->hfs_jnlfileid
= fid
;
2008 // make sure the journal file begins where we think it should.
2009 if ((jib_offset
/ (u_int64_t
)vcb
->blockSize
) != jfork
.cf_extents
[0].startBlock
) {
2010 printf("hfs: The journal file moved (was: %lld; is: %d). Fixing up\n",
2011 (jib_offset
/ (u_int64_t
)vcb
->blockSize
), jfork
.cf_extents
[0].startBlock
);
2013 jib_offset
= (u_int64_t
)jfork
.cf_extents
[0].startBlock
* (u_int64_t
)vcb
->blockSize
;
2015 recreate_journal
= 1;
2018 // check the size of the journal file.
2019 if (jib_size
!= (u_int64_t
)jfork
.cf_extents
[0].blockCount
*vcb
->blockSize
) {
2020 printf("hfs: The journal file changed size! (was %lld; is %lld). Fixing up.\n",
2021 jib_size
, (u_int64_t
)jfork
.cf_extents
[0].blockCount
*vcb
->blockSize
);
2023 jib_size
= (u_int64_t
)jfork
.cf_extents
[0].blockCount
* vcb
->blockSize
;
2025 recreate_journal
= 1;
2028 if (jib_flags
& kJIJournalInFSMask
) {
2029 hfsmp
->jvp
= hfsmp
->hfs_devvp
;
2031 printf("hfs: journal not stored in fs! don't know what to do.\n");
2032 buf_brelse(jinfo_bp
);
2036 // save this off for the hack-y check in hfs_remove()
2037 hfsmp
->jnl_start
= jib_offset
/ SWAP_BE32(vhp
->blockSize
);
2038 hfsmp
->jnl_size
= jib_size
;
2040 if ((hfsmp
->hfs_flags
& HFS_READ_ONLY
) && (vfs_flags(hfsmp
->hfs_mp
) & MNT_ROOTFS
) == 0) {
2041 // if the file system is read-only, check if the journal is empty.
2042 // if it is, then we can allow the mount. otherwise we have to
2044 retval
= journal_is_clean(hfsmp
->jvp
,
2045 jib_offset
+ (off_t
)vcb
->hfsPlusIOPosOffset
,
2048 hfsmp
->hfs_logical_block_size
);
2052 buf_brelse(jinfo_bp
);
2055 printf("hfs: late journal init: volume on %s is read-only and journal is dirty. Can not mount volume.\n",
2062 if ((jib_flags
& kJIJournalNeedInitMask
) || recreate_journal
) {
2063 printf("hfs: Initializing the journal (joffset 0x%llx sz 0x%llx)...\n",
2064 jib_offset
+ (off_t
)vcb
->hfsPlusIOPosOffset
, jib_size
);
2065 hfsmp
->jnl
= journal_create(hfsmp
->jvp
,
2066 jib_offset
+ (off_t
)vcb
->hfsPlusIOPosOffset
,
2069 hfsmp
->hfs_logical_block_size
,
2072 hfs_sync_metadata
, hfsmp
->hfs_mp
);
2074 // no need to start a transaction here... if this were to fail
2075 // we'd just re-init it on the next mount.
2076 jib_flags
&= ~kJIJournalNeedInitMask
;
2081 // if we weren't the last person to mount this volume
2082 // then we need to throw away the journal because it
2083 // is likely that someone else mucked with the disk.
2084 // if the journal is empty this is no big deal. if the
2085 // disk is dirty this prevents us from replaying the
2086 // journal over top of changes that someone else made.
2088 arg_flags
|= JOURNAL_RESET
;
2090 //printf("hfs: Opening the journal (joffset 0x%llx sz 0x%llx vhp_blksize %d)...\n",
2091 // jib_offset + (off_t)vcb->hfsPlusIOPosOffset,
2092 // jib_size, SWAP_BE32(vhp->blockSize));
2094 hfsmp
->jnl
= journal_open(hfsmp
->jvp
,
2095 jib_offset
+ (off_t
)vcb
->hfsPlusIOPosOffset
,
2098 hfsmp
->hfs_logical_block_size
,
2101 hfs_sync_metadata
, hfsmp
->hfs_mp
);
2106 jibp
->flags
= SWAP_BE32(jib_flags
);
2107 jibp
->offset
= SWAP_BE64(jib_offset
);
2108 jibp
->size
= SWAP_BE64(jib_size
);
2110 buf_bwrite(jinfo_bp
);
2112 buf_brelse(jinfo_bp
);
2117 //printf("journal @ 0x%x\n", hfsmp->jnl);
2119 // if we expected the journal to be there and we couldn't
2120 // create it or open it then we have to bail out.
2121 if (hfsmp
->jnl
== NULL
) {
2122 printf("hfs: late jnl init: failed to open/create the journal (retval %d).\n", retval
);
2130 * Calculate the allocation zone for metadata.
2132 * This zone includes the following:
2133 * Allocation Bitmap file
2134 * Overflow Extents file
2137 * Clustered Hot files
2140 * METADATA ALLOCATION ZONE
2141 * ____________________________________________________________________________
2143 * | BM | JF | OEF | CATALOG |---> | HOT FILES |
2144 * |____|____|_____|_______________|______________________________|___________|
2146 * <------------------------------- N * 128 MB ------------------------------->
2149 #define GIGABYTE (u_int64_t)(1024*1024*1024)
2151 #define OVERFLOW_DEFAULT_SIZE (4*1024*1024)
2152 #define OVERFLOW_MAXIMUM_SIZE (128*1024*1024)
2153 #define JOURNAL_DEFAULT_SIZE (8*1024*1024)
2154 #define JOURNAL_MAXIMUM_SIZE (512*1024*1024)
2155 #define HOTBAND_MINIMUM_SIZE (10*1024*1024)
2156 #define HOTBAND_MAXIMUM_SIZE (512*1024*1024)
2159 hfs_metadatazone_init(struct hfsmount
*hfsmp
)
2169 vcb
= HFSTOVCB(hfsmp
);
2170 fs_size
= (u_int64_t
)vcb
->blockSize
* (u_int64_t
)vcb
->totalBlocks
;
2173 * For volumes less than 10 GB, don't bother.
2175 if (fs_size
< ((u_int64_t
)10 * GIGABYTE
))
2178 * Skip non-journaled volumes as well.
2180 if (hfsmp
->jnl
== NULL
)
2184 * Start with allocation bitmap (a fixed size).
2186 zonesize
= roundup(vcb
->totalBlocks
/ 8, vcb
->vcbVBMIOSize
);
2189 * Overflow Extents file gets 4 MB per 100 GB.
2191 items
= fs_size
/ ((u_int64_t
)100 * GIGABYTE
);
2192 filesize
= (u_int64_t
)(items
+ 1) * OVERFLOW_DEFAULT_SIZE
;
2193 if (filesize
> OVERFLOW_MAXIMUM_SIZE
)
2194 filesize
= OVERFLOW_MAXIMUM_SIZE
;
2195 zonesize
+= filesize
;
2196 hfsmp
->hfs_overflow_maxblks
= filesize
/ vcb
->blockSize
;
2199 * Plan for at least 8 MB of journal for each
2200 * 100 GB of disk space (up to a 512 MB).
2202 items
= fs_size
/ ((u_int64_t
)100 * GIGABYTE
);
2203 filesize
= (u_int64_t
)(items
+ 1) * JOURNAL_DEFAULT_SIZE
;
2204 if (filesize
> JOURNAL_MAXIMUM_SIZE
)
2205 filesize
= JOURNAL_MAXIMUM_SIZE
;
2206 zonesize
+= filesize
;
2209 * Catalog file gets 10 MB per 1 GB.
2211 * How about considering the current catalog size (used nodes * node size)
2212 * and the current file data size to help estimate the required
2215 filesize
= MIN((fs_size
/ 1024) * 10, GIGABYTE
);
2216 hfsmp
->hfs_catalog_maxblks
= filesize
/ vcb
->blockSize
;
2217 zonesize
+= filesize
;
2220 * Add space for hot file region.
2222 * ...for now, use 5 MB per 1 GB (0.5 %)
2224 filesize
= (fs_size
/ 1024) * 5;
2225 if (filesize
> HOTBAND_MAXIMUM_SIZE
)
2226 filesize
= HOTBAND_MAXIMUM_SIZE
;
2227 else if (filesize
< HOTBAND_MINIMUM_SIZE
)
2228 filesize
= HOTBAND_MINIMUM_SIZE
;
2230 * Calculate user quota file requirements.
2232 items
= QF_USERS_PER_GB
* (fs_size
/ GIGABYTE
);
2233 if (items
< QF_MIN_USERS
)
2234 items
= QF_MIN_USERS
;
2235 else if (items
> QF_MAX_USERS
)
2236 items
= QF_MAX_USERS
;
2237 if (!powerof2(items
)) {
2245 filesize
+= (items
+ 1) * sizeof(struct dqblk
);
2247 * Calculate group quota file requirements.
2250 items
= QF_GROUPS_PER_GB
* (fs_size
/ GIGABYTE
);
2251 if (items
< QF_MIN_GROUPS
)
2252 items
= QF_MIN_GROUPS
;
2253 else if (items
> QF_MAX_GROUPS
)
2254 items
= QF_MAX_GROUPS
;
2255 if (!powerof2(items
)) {
2263 filesize
+= (items
+ 1) * sizeof(struct dqblk
);
2264 zonesize
+= filesize
;
2267 * Round up entire zone to a bitmap block's worth.
2268 * The extra space goes to the catalog file and hot file area.
2271 zonesize
= roundup(zonesize
, (u_int64_t
)vcb
->vcbVBMIOSize
* 8 * vcb
->blockSize
);
2272 temp
= zonesize
- temp
; /* temp has extra space */
2273 filesize
+= temp
/ 3;
2274 hfsmp
->hfs_catalog_maxblks
+= (temp
- (temp
/ 3)) / vcb
->blockSize
;
2276 hfsmp
->hfs_hotfile_maxblks
= filesize
/ vcb
->blockSize
;
2278 /* Convert to allocation blocks. */
2279 blk
= zonesize
/ vcb
->blockSize
;
2281 /* The default metadata zone location is at the start of volume. */
2282 hfsmp
->hfs_metazone_start
= 1;
2283 hfsmp
->hfs_metazone_end
= blk
- 1;
2285 /* The default hotfile area is at the end of the zone. */
2286 hfsmp
->hfs_hotfile_start
= blk
- (filesize
/ vcb
->blockSize
);
2287 hfsmp
->hfs_hotfile_end
= hfsmp
->hfs_metazone_end
;
2288 hfsmp
->hfs_hotfile_freeblks
= hfs_hotfile_freeblocks(hfsmp
);
2290 printf("HFS: metadata zone is %d to %d\n", hfsmp
->hfs_metazone_start
, hfsmp
->hfs_metazone_end
);
2291 printf("HFS: hot file band is %d to %d\n", hfsmp
->hfs_hotfile_start
, hfsmp
->hfs_hotfile_end
);
2292 printf("HFS: hot file band free blocks = %d\n", hfsmp
->hfs_hotfile_freeblks
);
2294 hfsmp
->hfs_flags
|= HFS_METADATA_ZONE
;
2299 hfs_hotfile_freeblocks(struct hfsmount
*hfsmp
)
2301 ExtendedVCB
*vcb
= HFSTOVCB(hfsmp
);
2305 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_BITMAP
, HFS_EXCLUSIVE_LOCK
);
2306 freeblocks
= MetaZoneFreeBlocks(vcb
);
2307 hfs_systemfile_unlock(hfsmp
, lockflags
);
2309 /* Minus Extents overflow file reserve. */
2311 hfsmp
->hfs_overflow_maxblks
- VTOF(hfsmp
->hfs_extents_vp
)->ff_blocks
;
2312 /* Minus catalog file reserve. */
2314 hfsmp
->hfs_catalog_maxblks
- VTOF(hfsmp
->hfs_catalog_vp
)->ff_blocks
;
2318 return MIN(freeblocks
, hfsmp
->hfs_hotfile_maxblks
);
2322 * Determine if a file is a "virtual" metadata file.
2323 * This includes journal and quota files.
2327 hfs_virtualmetafile(struct cnode
*cp
)
2329 const char * filename
;
2332 if (cp
->c_parentcnid
!= kHFSRootFolderID
)
2335 filename
= (const char *)cp
->c_desc
.cd_nameptr
;
2336 if (filename
== NULL
)
2339 if ((strncmp(filename
, ".journal", sizeof(".journal")) == 0) ||
2340 (strncmp(filename
, ".journal_info_block", sizeof(".journal_info_block")) == 0) ||
2341 (strncmp(filename
, ".quota.user", sizeof(".quota.user")) == 0) ||
2342 (strncmp(filename
, ".quota.group", sizeof(".quota.group")) == 0) ||
2343 (strncmp(filename
, ".hotfiles.btree", sizeof(".hotfiles.btree")) == 0))
2352 // Fire off a timed callback to sync the disk if the
2353 // volume is on ejectable media.
2357 hfs_sync_ejectable(struct hfsmount
*hfsmp
)
2359 if (hfsmp
->hfs_syncer
) {
2360 uint32_t secs
, usecs
;
2363 clock_get_calendar_microtime(&secs
, &usecs
);
2364 now
= ((uint64_t)secs
* 1000000) + usecs
;
2366 if (hfsmp
->hfs_sync_scheduled
== 0) {
2369 hfsmp
->hfs_last_sync_request_time
= now
;
2371 clock_interval_to_deadline(HFS_META_DELAY
, HFS_MILLISEC_SCALE
, &deadline
);
2374 * Increment hfs_sync_scheduled on the assumption that we're the
2375 * first thread to schedule the timer. If some other thread beat
2376 * us, then we'll decrement it. If we *were* the first to
2377 * schedule the timer, then we need to keep track that the
2378 * callback is waiting to complete.
2380 OSIncrementAtomic((volatile SInt32
*)&hfsmp
->hfs_sync_scheduled
);
2381 if (thread_call_enter_delayed(hfsmp
->hfs_syncer
, deadline
))
2382 OSDecrementAtomic((volatile SInt32
*)&hfsmp
->hfs_sync_scheduled
);
2384 OSIncrementAtomic((volatile SInt32
*)&hfsmp
->hfs_sync_incomplete
);
2392 hfs_start_transaction(struct hfsmount
*hfsmp
)
2394 int ret
, unlock_on_err
=0;
2395 void * thread
= current_thread();
2397 #ifdef HFS_CHECK_LOCK_ORDER
2399 * You cannot start a transaction while holding a system
2400 * file lock. (unless the transaction is nested.)
2402 if (hfsmp
->jnl
&& journal_owner(hfsmp
->jnl
) != thread
) {
2403 if (hfsmp
->hfs_catalog_cp
&& hfsmp
->hfs_catalog_cp
->c_lockowner
== thread
) {
2404 panic("hfs_start_transaction: bad lock order (cat before jnl)\n");
2406 if (hfsmp
->hfs_attribute_cp
&& hfsmp
->hfs_attribute_cp
->c_lockowner
== thread
) {
2407 panic("hfs_start_transaction: bad lock order (attr before jnl)\n");
2409 if (hfsmp
->hfs_extents_cp
&& hfsmp
->hfs_extents_cp
->c_lockowner
== thread
) {
2410 panic("hfs_start_transaction: bad lock order (ext before jnl)\n");
2413 #endif /* HFS_CHECK_LOCK_ORDER */
2415 if (hfsmp
->jnl
== NULL
|| journal_owner(hfsmp
->jnl
) != thread
) {
2416 lck_rw_lock_shared(&hfsmp
->hfs_global_lock
);
2417 OSAddAtomic(1, (SInt32
*)&hfsmp
->hfs_active_threads
);
2421 /* If a downgrade to read-only mount is in progress, no other
2422 * process than the downgrade process is allowed to modify
2425 if ((hfsmp
->hfs_flags
& HFS_RDONLY_DOWNGRADE
) &&
2426 (hfsmp
->hfs_downgrading_proc
!= thread
)) {
2432 ret
= journal_start_transaction(hfsmp
->jnl
);
2434 OSAddAtomic(1, (SInt32
*)&hfsmp
->hfs_global_lock_nesting
);
2441 if (ret
!= 0 && unlock_on_err
) {
2442 lck_rw_unlock_shared(&hfsmp
->hfs_global_lock
);
2443 OSAddAtomic(-1, (SInt32
*)&hfsmp
->hfs_active_threads
);
2451 hfs_end_transaction(struct hfsmount
*hfsmp
)
2453 int need_unlock
=0, ret
;
2455 if ( hfsmp
->jnl
== NULL
2456 || ( journal_owner(hfsmp
->jnl
) == current_thread()
2457 && (OSAddAtomic(-1, (SInt32
*)&hfsmp
->hfs_global_lock_nesting
) == 1)) ) {
2463 ret
= journal_end_transaction(hfsmp
->jnl
);
2469 OSAddAtomic(-1, (SInt32
*)&hfsmp
->hfs_active_threads
);
2470 lck_rw_unlock_shared(&hfsmp
->hfs_global_lock
);
2471 hfs_sync_ejectable(hfsmp
);