2 * Copyright (c) 2000-2002 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));
57 extern uid_t console_user
;
60 static void ReleaseMetaFileVNode(struct vnode
*vp
);
61 static int hfs_late_journal_init(struct hfsmount
*hfsmp
, HFSPlusVolumeHeader
*vhp
, void *_args
);
63 u_int32_t
GetLogicalBlockSize(struct vnode
*vp
);
65 /* BTree accessor routines */
66 extern OSStatus
GetBTreeBlock(FileReference vp
, UInt32 blockNum
, GetBlockOptions options
, BlockDescriptor
*block
);
67 extern OSStatus
SetBTreeBlockSize(FileReference vp
, ByteCount blockSize
, ItemCount minBlockCount
);
68 extern OSStatus
ExtendBTreeFile(FileReference vp
, FSSize minEOF
, FSSize maxEOF
);
69 extern OSStatus
ReleaseBTreeBlock(FileReference vp
, BlockDescPtr blockPtr
, ReleaseBlockOptions options
);
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 char hfs_catname
[] = "Catalog B-tree";
83 char hfs_extname
[] = "Extents B-tree";
84 char hfs_vbmname
[] = "Volume Bitmap";
86 char hfs_privdirname
[] =
87 "\xE2\x90\x80\xE2\x90\x80\xE2\x90\x80\xE2\x90\x80HFS+ Private Data";
89 OSErr
hfs_MountHFSVolume(struct hfsmount
*hfsmp
, HFSMasterDirectoryBlock
*mdb
,
92 ExtendedVCB
*vcb
= HFSTOVCB(hfsmp
);
95 struct cat_desc cndesc
;
96 struct cat_attr cnattr
;
99 /* Block size must be a multiple of 512 */
100 if (SWAP_BE32(mdb
->drAlBlkSiz
) == 0 ||
101 (SWAP_BE32(mdb
->drAlBlkSiz
) & 0x01FF) != 0)
104 /* don't mount a writeable volume if its dirty, it must be cleaned by fsck_hfs */
105 if ((hfsmp
->hfs_fs_ronly
== 0) && ((SWAP_BE16(mdb
->drAtrb
) & kHFSVolumeUnmountedMask
) == 0))
109 * The MDB seems OK: transfer info from it into VCB
110 * Note - the VCB starts out clear (all zeros)
113 vcb
->vcbSigWord
= SWAP_BE16 (mdb
->drSigWord
);
114 vcb
->vcbCrDate
= to_bsd_time(LocalToUTC(SWAP_BE32(mdb
->drCrDate
)));
115 vcb
->localCreateDate
= SWAP_BE32 (mdb
->drCrDate
);
116 vcb
->vcbLsMod
= to_bsd_time(LocalToUTC(SWAP_BE32(mdb
->drLsMod
)));
117 vcb
->vcbAtrb
= SWAP_BE16 (mdb
->drAtrb
);
118 vcb
->vcbNmFls
= SWAP_BE16 (mdb
->drNmFls
);
119 vcb
->vcbVBMSt
= SWAP_BE16 (mdb
->drVBMSt
);
120 vcb
->nextAllocation
= SWAP_BE16 (mdb
->drAllocPtr
);
121 vcb
->totalBlocks
= SWAP_BE16 (mdb
->drNmAlBlks
);
122 vcb
->blockSize
= SWAP_BE32 (mdb
->drAlBlkSiz
);
123 vcb
->vcbClpSiz
= SWAP_BE32 (mdb
->drClpSiz
);
124 vcb
->vcbAlBlSt
= SWAP_BE16 (mdb
->drAlBlSt
);
125 vcb
->vcbNxtCNID
= SWAP_BE32 (mdb
->drNxtCNID
);
126 vcb
->freeBlocks
= SWAP_BE16 (mdb
->drFreeBks
);
127 vcb
->vcbVolBkUp
= to_bsd_time(LocalToUTC(SWAP_BE32(mdb
->drVolBkUp
)));
128 vcb
->vcbWrCnt
= SWAP_BE32 (mdb
->drWrCnt
);
129 vcb
->vcbNmRtDirs
= SWAP_BE16 (mdb
->drNmRtDirs
);
130 vcb
->vcbFilCnt
= SWAP_BE32 (mdb
->drFilCnt
);
131 vcb
->vcbDirCnt
= SWAP_BE32 (mdb
->drDirCnt
);
132 bcopy(mdb
->drFndrInfo
, vcb
->vcbFndrInfo
, sizeof(vcb
->vcbFndrInfo
));
133 if (!hfsmp
->hfs_fs_ronly
)
134 vcb
->vcbWrCnt
++; /* Compensate for write of MDB on last flush */
136 /* convert hfs encoded name into UTF-8 string */
137 error
= hfs_to_utf8(vcb
, mdb
->drVN
, NAME_MAX
, &utf8chars
, vcb
->vcbVN
);
139 * When an HFS name cannot be encoded with the current
140 * volume encoding we use MacRoman as a fallback.
142 if (error
|| (utf8chars
== 0))
143 (void) mac_roman_to_utf8(mdb
->drVN
, NAME_MAX
, &utf8chars
, vcb
->vcbVN
);
145 hfsmp
->hfs_logBlockSize
= BestBlockSizeFit(vcb
->blockSize
, MAXBSIZE
, hfsmp
->hfs_phys_block_size
);
146 vcb
->vcbVBMIOSize
= kHFSBlockSize
;
150 bzero(&cndesc
, sizeof(cndesc
));
151 cndesc
.cd_parentcnid
= kRootParID
;
152 bzero(&cnattr
, sizeof(cnattr
));
154 cnattr
.ca_mode
= S_IFREG
;
155 bzero(&fork
, sizeof(fork
));
158 * Set up Extents B-tree vnode
160 cndesc
.cd_nameptr
= hfs_extname
;
161 cndesc
.cd_namelen
= strlen(hfs_extname
);
162 cndesc
.cd_cnid
= cnattr
.ca_fileid
= kHFSExtentsFileID
;
163 fork
.cf_size
= SWAP_BE32(mdb
->drXTFlSize
);
164 fork
.cf_blocks
= fork
.cf_size
/ vcb
->blockSize
;
165 fork
.cf_clump
= SWAP_BE32(mdb
->drXTClpSiz
);
166 fork
.cf_extents
[0].startBlock
= SWAP_BE16(mdb
->drXTExtRec
[0].startBlock
);
167 fork
.cf_extents
[0].blockCount
= SWAP_BE16(mdb
->drXTExtRec
[0].blockCount
);
168 fork
.cf_extents
[1].startBlock
= SWAP_BE16(mdb
->drXTExtRec
[1].startBlock
);
169 fork
.cf_extents
[1].blockCount
= SWAP_BE16(mdb
->drXTExtRec
[1].blockCount
);
170 fork
.cf_extents
[2].startBlock
= SWAP_BE16(mdb
->drXTExtRec
[2].startBlock
);
171 fork
.cf_extents
[2].blockCount
= SWAP_BE16(mdb
->drXTExtRec
[2].blockCount
);
172 cnattr
.ca_blocks
= fork
.cf_blocks
;
174 error
= hfs_getnewvnode(hfsmp
, NULL
, &cndesc
, 0, &cnattr
, &fork
,
175 &vcb
->extentsRefNum
);
176 if (error
) goto MtVolErr
;
177 error
= MacToVFSError(BTOpenPath(VTOF(vcb
->extentsRefNum
),
178 (KeyCompareProcPtr
)CompareExtentKeys
,
179 GetBTreeBlock
, ReleaseBTreeBlock
,
180 ExtendBTreeFile
, SetBTreeBlockSize
));
182 VOP_UNLOCK(vcb
->extentsRefNum
, 0, p
);
187 * Set up Catalog B-tree vnode...
189 cndesc
.cd_nameptr
= hfs_catname
;
190 cndesc
.cd_namelen
= strlen(hfs_catname
);
191 cndesc
.cd_cnid
= cnattr
.ca_fileid
= kHFSCatalogFileID
;
192 fork
.cf_size
= SWAP_BE32(mdb
->drCTFlSize
);
193 fork
.cf_blocks
= fork
.cf_size
/ vcb
->blockSize
;
194 fork
.cf_clump
= SWAP_BE32(mdb
->drCTClpSiz
);
195 fork
.cf_extents
[0].startBlock
= SWAP_BE16(mdb
->drCTExtRec
[0].startBlock
);
196 fork
.cf_extents
[0].blockCount
= SWAP_BE16(mdb
->drCTExtRec
[0].blockCount
);
197 fork
.cf_extents
[1].startBlock
= SWAP_BE16(mdb
->drCTExtRec
[1].startBlock
);
198 fork
.cf_extents
[1].blockCount
= SWAP_BE16(mdb
->drCTExtRec
[1].blockCount
);
199 fork
.cf_extents
[2].startBlock
= SWAP_BE16(mdb
->drCTExtRec
[2].startBlock
);
200 fork
.cf_extents
[2].blockCount
= SWAP_BE16(mdb
->drCTExtRec
[2].blockCount
);
201 cnattr
.ca_blocks
= fork
.cf_blocks
;
203 error
= hfs_getnewvnode(hfsmp
, NULL
, &cndesc
, 0, &cnattr
, &fork
,
204 &vcb
->catalogRefNum
);
206 VOP_UNLOCK(vcb
->extentsRefNum
, 0, p
);
209 error
= MacToVFSError(BTOpenPath(VTOF(vcb
->catalogRefNum
),
210 (KeyCompareProcPtr
)CompareCatalogKeys
,
211 GetBTreeBlock
, ReleaseBTreeBlock
,
212 ExtendBTreeFile
, SetBTreeBlockSize
));
214 VOP_UNLOCK(vcb
->catalogRefNum
, 0, p
);
215 VOP_UNLOCK(vcb
->extentsRefNum
, 0, p
);
219 /* mark the volume dirty (clear clean unmount bit) */
220 vcb
->vcbAtrb
&= ~kHFSVolumeUnmountedMask
;
223 * all done with b-trees so we can unlock now...
225 VOP_UNLOCK(vcb
->catalogRefNum
, 0, p
);
226 VOP_UNLOCK(vcb
->extentsRefNum
, 0, p
);
228 if ( error
== noErr
)
230 if ( !(vcb
->vcbAtrb
& kHFSVolumeHardwareLockMask
) ) // if the disk is not write protected
232 MarkVCBDirty( vcb
); // mark VCB dirty so it will be written
237 //-- Release any resources allocated so far before exiting with an error:
239 ReleaseMetaFileVNode(vcb
->catalogRefNum
);
240 ReleaseMetaFileVNode(vcb
->extentsRefNum
);
246 //*******************************************************************************
247 // Routine: hfs_MountHFSPlusVolume
250 //*******************************************************************************
252 OSErr
hfs_MountHFSPlusVolume(struct hfsmount
*hfsmp
, HFSPlusVolumeHeader
*vhp
,
253 off_t embeddedOffset
, u_int64_t disksize
, struct proc
*p
, void *args
)
255 register ExtendedVCB
*vcb
;
256 struct cat_desc cndesc
;
257 struct cat_attr cnattr
;
261 // XXXdbg - added the kHFSJSigWord case
262 if ((SWAP_BE16(vhp
->signature
) != kHFSPlusSigWord
&&
263 SWAP_BE16(vhp
->signature
) != kHFSJSigWord
) ||
264 SWAP_BE16(vhp
->version
) != kHFSPlusVersion
) {
266 printf("hfs: mount: sig 0x%x and version 0x%x are not HFS or HFS+.\n",
267 vhp
->signature
, vhp
->version
);
271 /* Block size must be at least 512 and a power of 2 */
272 blockSize
= SWAP_BE32(vhp
->blockSize
);
273 if (blockSize
< 512 || (blockSize
& (blockSize
-1)) != 0)
276 /* don't mount a writable volume if its dirty, it must be cleaned by fsck_hfs */
277 if (hfsmp
->hfs_fs_ronly
== 0 && hfsmp
->jnl
== NULL
&& (SWAP_BE32(vhp
->attributes
) & kHFSVolumeUnmountedMask
) == 0)
280 /* Make sure we can live with the physical block size. */
281 if ((disksize
& (hfsmp
->hfs_phys_block_size
- 1)) ||
282 (embeddedOffset
& (hfsmp
->hfs_phys_block_size
- 1)) ||
283 (SWAP_BE32(vhp
->blockSize
) < hfsmp
->hfs_phys_block_size
)) {
287 * The VolumeHeader seems OK: transfer info from it into VCB
288 * Note - the VCB starts out clear (all zeros)
290 vcb
= HFSTOVCB(hfsmp
);
292 vcb
->vcbSigWord
= SWAP_BE16(vhp
->signature
);
294 // XXXdbg - remap this in case we've mounted a dirty journaled volume
295 if (vcb
->vcbSigWord
== kHFSJSigWord
) {
296 vcb
->vcbSigWord
= kHFSPlusSigWord
;
299 vcb
->vcbJinfoBlock
= SWAP_BE32(vhp
->journalInfoBlock
);
300 vcb
->vcbLsMod
= to_bsd_time(SWAP_BE32(vhp
->modifyDate
));
301 vcb
->vcbAtrb
= (UInt16
)SWAP_BE32(vhp
->attributes
);
302 vcb
->vcbClpSiz
= SWAP_BE32(vhp
->rsrcClumpSize
);
303 vcb
->vcbNxtCNID
= SWAP_BE32(vhp
->nextCatalogID
);
304 vcb
->vcbVolBkUp
= to_bsd_time(SWAP_BE32(vhp
->backupDate
));
305 vcb
->vcbWrCnt
= SWAP_BE32(vhp
->writeCount
);
306 vcb
->vcbFilCnt
= SWAP_BE32(vhp
->fileCount
);
307 vcb
->vcbDirCnt
= SWAP_BE32(vhp
->folderCount
);
309 /* copy 32 bytes of Finder info */
310 bcopy(vhp
->finderInfo
, vcb
->vcbFndrInfo
, sizeof(vhp
->finderInfo
));
312 vcb
->vcbAlBlSt
= 0; /* hfs+ allocation blocks start at first block of volume */
313 if (!hfsmp
->hfs_fs_ronly
)
314 vcb
->vcbWrCnt
++; /* compensate for write of Volume Header on last flush */
318 /* Now fill in the Extended VCB info */
319 vcb
->nextAllocation
= SWAP_BE32(vhp
->nextAllocation
);
320 vcb
->totalBlocks
= SWAP_BE32(vhp
->totalBlocks
);
321 vcb
->freeBlocks
= SWAP_BE32(vhp
->freeBlocks
);
322 vcb
->blockSize
= SWAP_BE32(vhp
->blockSize
);
323 vcb
->encodingsBitmap
= SWAP_BE64(vhp
->encodingsBitmap
);
324 vcb
->localCreateDate
= SWAP_BE32(vhp
->createDate
);
326 vcb
->hfsPlusIOPosOffset
= embeddedOffset
;
328 /* Default to no free block reserve */
329 vcb
->reserveBlocks
= 0;
332 * Update the logical block size in the mount struct
333 * (currently set up from the wrapper MDB) using the
334 * new blocksize value:
336 hfsmp
->hfs_logBlockSize
= BestBlockSizeFit(vcb
->blockSize
, MAXBSIZE
, hfsmp
->hfs_phys_block_size
);
337 vcb
->vcbVBMIOSize
= min(vcb
->blockSize
, MAXPHYSIO
);
339 bzero(&cndesc
, sizeof(cndesc
));
340 cndesc
.cd_parentcnid
= kRootParID
;
341 bzero(&cnattr
, sizeof(cnattr
));
343 cnattr
.ca_mode
= S_IFREG
;
346 * Set up Extents B-tree vnode
348 cndesc
.cd_nameptr
= hfs_extname
;
349 cndesc
.cd_namelen
= strlen(hfs_extname
);
350 cndesc
.cd_cnid
= cnattr
.ca_fileid
= kHFSExtentsFileID
;
352 SWAP_HFS_PLUS_FORK_DATA (&vhp
->extentsFile
);
353 cnattr
.ca_blocks
= vhp
->extentsFile
.totalBlocks
;
355 retval
= hfs_getnewvnode(hfsmp
, NULL
, &cndesc
, 0, &cnattr
,
356 (struct cat_fork
*)&vhp
->extentsFile
,
357 &vcb
->extentsRefNum
);
358 SWAP_HFS_PLUS_FORK_DATA (&vhp
->extentsFile
);
360 if (retval
) goto ErrorExit
;
361 retval
= MacToVFSError(BTOpenPath(VTOF(vcb
->extentsRefNum
),
362 (KeyCompareProcPtr
) CompareExtentKeysPlus
,
363 GetBTreeBlock
, ReleaseBTreeBlock
,
364 ExtendBTreeFile
, SetBTreeBlockSize
));
366 VOP_UNLOCK(vcb
->extentsRefNum
, 0, p
);
371 * Set up Catalog B-tree vnode
373 cndesc
.cd_nameptr
= hfs_catname
;
374 cndesc
.cd_namelen
= strlen(hfs_catname
);
375 cndesc
.cd_cnid
= cnattr
.ca_fileid
= kHFSCatalogFileID
;
377 SWAP_HFS_PLUS_FORK_DATA(&vhp
->catalogFile
);
378 cnattr
.ca_blocks
= vhp
->catalogFile
.totalBlocks
;
380 retval
= hfs_getnewvnode(hfsmp
, NULL
, &cndesc
, 0, &cnattr
,
381 (struct cat_fork
*)&vhp
->catalogFile
,
382 &vcb
->catalogRefNum
);
383 SWAP_HFS_PLUS_FORK_DATA(&vhp
->catalogFile
);
385 VOP_UNLOCK(vcb
->extentsRefNum
, 0, p
);
388 retval
= MacToVFSError(BTOpenPath(VTOF(vcb
->catalogRefNum
),
389 (KeyCompareProcPtr
) CompareExtendedCatalogKeys
,
390 GetBTreeBlock
, ReleaseBTreeBlock
,
391 ExtendBTreeFile
, SetBTreeBlockSize
));
393 VOP_UNLOCK(vcb
->catalogRefNum
, 0, p
);
394 VOP_UNLOCK(vcb
->extentsRefNum
, 0, p
);
399 * Set up Allocation file vnode
401 cndesc
.cd_nameptr
= hfs_vbmname
;
402 cndesc
.cd_namelen
= strlen(hfs_vbmname
);
403 cndesc
.cd_cnid
= cnattr
.ca_fileid
= kHFSAllocationFileID
;
405 SWAP_HFS_PLUS_FORK_DATA(&vhp
->allocationFile
);
406 cnattr
.ca_blocks
= vhp
->allocationFile
.totalBlocks
;
408 retval
= hfs_getnewvnode(hfsmp
, NULL
, &cndesc
, 0, &cnattr
,
409 (struct cat_fork
*)&vhp
->allocationFile
,
410 &vcb
->allocationsRefNum
);
411 SWAP_HFS_PLUS_FORK_DATA(&vhp
->allocationFile
);
413 VOP_UNLOCK(vcb
->catalogRefNum
, 0, p
);
414 VOP_UNLOCK(vcb
->extentsRefNum
, 0, p
);
418 /* Pick up volume name and create date */
419 retval
= cat_idlookup(hfsmp
, kHFSRootFolderID
, &cndesc
, &cnattr
, NULL
);
421 VOP_UNLOCK(vcb
->allocationsRefNum
, 0, p
);
422 VOP_UNLOCK(vcb
->catalogRefNum
, 0, p
);
423 VOP_UNLOCK(vcb
->extentsRefNum
, 0, p
);
426 vcb
->vcbCrDate
= cnattr
.ca_itime
;
427 vcb
->volumeNameEncodingHint
= cndesc
.cd_encoding
;
428 bcopy(cndesc
.cd_nameptr
, vcb
->vcbVN
, min(255, cndesc
.cd_namelen
));
429 cat_releasedesc(&cndesc
);
431 /* mark the volume dirty (clear clean unmount bit) */
432 vcb
->vcbAtrb
&= ~kHFSVolumeUnmountedMask
;
433 if (hfsmp
->jnl
&& hfsmp
->hfs_fs_ronly
== 0) {
434 hfs_flushvolumeheader(hfsmp
, TRUE
, TRUE
);
438 * all done with metadata files so we can unlock now...
440 VOP_UNLOCK(vcb
->allocationsRefNum
, 0, p
);
441 VOP_UNLOCK(vcb
->catalogRefNum
, 0, p
);
442 VOP_UNLOCK(vcb
->extentsRefNum
, 0, p
);
444 /* setup private/hidden directory for unlinked files */
445 hfsmp
->hfs_private_metadata_dir
= FindMetaDataDirectory(vcb
);
446 if (hfsmp
->jnl
&& (hfsmp
->hfs_fs_ronly
== 0))
447 hfs_remove_orphans(hfsmp
);
449 if ( !(vcb
->vcbAtrb
& kHFSVolumeHardwareLockMask
) ) // if the disk is not write protected
451 MarkVCBDirty( vcb
); // mark VCB dirty so it will be written
456 // Check if we need to do late journal initialization. This only
457 // happens if a previous version of MacOS X (or 9) touched the disk.
458 // In that case hfs_late_journal_init() will go re-locate the journal
459 // and journal_info_block files and validate that they're still kosher.
461 if ( (vcb
->vcbAtrb
& kHFSVolumeJournaledMask
)
462 && (SWAP_BE32(vhp
->lastMountedVersion
) != kHFSJMountVersion
)
463 && (hfsmp
->jnl
== NULL
)) {
465 retval
= hfs_late_journal_init(hfsmp
, vhp
, args
);
469 } else if (hfsmp
->jnl
) {
470 hfsmp
->hfs_mp
->mnt_flag
|= MNT_JOURNALED
;
472 } else if (hfsmp
->jnl
) {
473 struct cat_attr jinfo_attr
, jnl_attr
;
475 // if we're here we need to fill in the fileid's for the
476 // journal and journal_info_block.
477 hfsmp
->hfs_jnlinfoblkid
= GetFileInfo(vcb
, kRootDirID
, ".journal_info_block", &jinfo_attr
, NULL
);
478 hfsmp
->hfs_jnlfileid
= GetFileInfo(vcb
, kRootDirID
, ".journal", &jnl_attr
, NULL
);
479 if (hfsmp
->hfs_jnlinfoblkid
== 0 || hfsmp
->hfs_jnlfileid
== 0) {
480 printf("hfs: danger! couldn't find the file-id's for the journal or journal_info_block\n");
481 printf("hfs: jnlfileid %d, jnlinfoblkid %d\n", hfsmp
->hfs_jnlfileid
, hfsmp
->hfs_jnlinfoblkid
);
490 * A fatal error occured and the volume cannot be mounted
491 * release any resources that we aquired...
494 InvalidateCatalogCache(vcb
);
495 ReleaseMetaFileVNode(vcb
->allocationsRefNum
);
496 ReleaseMetaFileVNode(vcb
->catalogRefNum
);
497 ReleaseMetaFileVNode(vcb
->extentsRefNum
);
504 * ReleaseMetaFileVNode
508 static void ReleaseMetaFileVNode(struct vnode
*vp
)
512 if (vp
&& (fp
= VTOF(vp
))) {
513 if (fp
->fcbBTCBPtr
!= NULL
)
514 (void) BTClosePath(fp
);
516 /* release the node even if BTClosePath fails */
523 /*************************************************************
525 * Unmounts a hfs volume.
526 * At this point vflush() has been called (to dump all non-metadata files)
528 *************************************************************/
530 short hfsUnmount( register struct hfsmount
*hfsmp
, struct proc
*p
)
532 ExtendedVCB
*vcb
= HFSTOVCB(hfsmp
);
535 InvalidateCatalogCache( vcb
);
537 if (vcb
->vcbSigWord
== kHFSPlusSigWord
)
538 ReleaseMetaFileVNode(vcb
->allocationsRefNum
);
540 ReleaseMetaFileVNode(vcb
->catalogRefNum
);
541 ReleaseMetaFileVNode(vcb
->extentsRefNum
);
548 * Some 3rd party kexts link against hfs_getcatalog so keep a stub for now.
551 hfs_getcatalog(void *p1
, u_long p2
, void *p3
, short p4
, void *p5
)
557 int overflow_extents(struct filefork
*fp
)
561 if (VTOVCB(FTOV(fp
))->vcbSigWord
== kHFSPlusSigWord
) {
562 if (fp
->ff_extents
[7].blockCount
== 0)
565 blocks
= fp
->ff_extents
[0].blockCount
+
566 fp
->ff_extents
[1].blockCount
+
567 fp
->ff_extents
[2].blockCount
+
568 fp
->ff_extents
[3].blockCount
+
569 fp
->ff_extents
[4].blockCount
+
570 fp
->ff_extents
[5].blockCount
+
571 fp
->ff_extents
[6].blockCount
+
572 fp
->ff_extents
[7].blockCount
;
574 if (fp
->ff_extents
[2].blockCount
== 0)
577 blocks
= fp
->ff_extents
[0].blockCount
+
578 fp
->ff_extents
[1].blockCount
+
579 fp
->ff_extents
[2].blockCount
;
582 return (fp
->ff_blocks
> blocks
);
586 /* __private_extern__ */
588 hfs_metafilelocking(struct hfsmount
*hfsmp
, u_long fileID
, u_int flags
, struct proc
*p
)
591 struct vnode
*vp
= NULL
;
592 int numOfLockedBuffs
;
595 vcb
= HFSTOVCB(hfsmp
);
598 case kHFSExtentsFileID
:
599 vp
= vcb
->extentsRefNum
;
602 case kHFSCatalogFileID
:
603 vp
= vcb
->catalogRefNum
;
606 case kHFSAllocationFileID
:
607 /* bitmap is covered by Extents B-tree locking */
610 panic("hfs_lockmetafile: invalid fileID");
613 /* Release, if necesary any locked buffer caches */
614 if ((flags
& LK_TYPE_MASK
) == LK_RELEASE
) {
615 struct timeval tv
= time
;
616 u_int32_t lastfsync
= tv
.tv_sec
;
618 (void) BTGetLastSync((FCB
*)VTOF(vp
), &lastfsync
);
620 numOfLockedBuffs
= count_lock_queue();
621 if ((numOfLockedBuffs
> kMaxLockedMetaBuffers
) || ((numOfLockedBuffs
>1) && ((tv
.tv_sec
- lastfsync
) > kMaxSecsForFsync
))) {
622 hfs_btsync(vp
, HFS_SYNCTRANS
);
628 retval
= lockmgr(&VTOC(vp
)->c_lock
, flags
, &vp
->v_interlock
, p
);
636 * Check to see if a vnode is locked in the current context
637 * This is to be used for debugging purposes only!!
640 void RequireFileLock(FileReference vp
, int shareable
)
642 struct lock__bsd__
*lkp
;
647 pid
= current_proc()->p_pid
;
648 self
= (void *) current_thread();
649 lkp
= &VTOC(vp
)->c_lock
;
651 simple_lock(&lkp
->lk_interlock
);
653 if (shareable
&& (lkp
->lk_sharecount
> 0) && (lkp
->lk_lockholder
== LK_NOPROC
))
655 else if ((lkp
->lk_exclusivecount
> 0) && (lkp
->lk_lockholder
== pid
) && (lkp
->lk_lockthread
== self
))
658 simple_unlock(&lkp
->lk_interlock
);
661 switch (VTOC(vp
)->c_fileid
) {
663 DEBUG_BREAK_MSG((" #\n # RequireFileLock: extent btree vnode not locked! v: 0x%08X\n #\n", (u_int
)vp
));
667 DEBUG_BREAK_MSG((" #\n # RequireFileLock: catalog btree vnode not locked! v: 0x%08X\n #\n", (u_int
)vp
));
671 DEBUG_BREAK_MSG((" #\n # RequireFileLock: file (%d) not locked! v: 0x%08X\n #\n", VTOC(vp
)->c_fileid
, (u_int
)vp
));
680 * There are three ways to qualify for ownership rights on an object:
682 * 1. (a) Your UID matches the cnode's UID.
683 * (b) The object in question is owned by "unknown" and
684 * your UID matches the console user's UID.
685 * 2. (a) Permissions on the filesystem are being ignored and
686 * your UID matches the replacement UID.
687 * (b) Permissions on the filesystem are being ignored and
688 * the replacement UID is "unknown" and
689 * your UID matches the console user UID.
694 hfs_owner_rights(struct hfsmount
*hfsmp
, uid_t cnode_uid
, struct ucred
*cred
,
695 struct proc
*p
, int invokesuperuserstatus
)
697 if ((cred
->cr_uid
== cnode_uid
) || /* [1a] */
698 ((cnode_uid
== UNKNOWNUID
) && (cred
->cr_uid
== console_user
)) || /* [1b] */
699 ((HFSTOVFS(hfsmp
)->mnt_flag
& MNT_UNKNOWNPERMISSIONS
) && /* [2] */
700 ((cred
->cr_uid
== hfsmp
->hfs_uid
) || /* [2a] */
701 ((hfsmp
->hfs_uid
== UNKNOWNUID
) && /* [2b] */
702 (cred
->cr_uid
== console_user
)))) ||
703 (invokesuperuserstatus
&& (suser(cred
, &p
->p_acflag
) == 0))) { /* [3] */
711 unsigned long BestBlockSizeFit(unsigned long allocationBlockSize
,
712 unsigned long blockSizeLimit
,
713 unsigned long baseMultiple
) {
715 Compute the optimal (largest) block size (no larger than allocationBlockSize) that is less than the
716 specified limit but still an even multiple of the baseMultiple.
718 int baseBlockCount
, blockCount
;
719 unsigned long trialBlockSize
;
721 if (allocationBlockSize
% baseMultiple
!= 0) {
723 Whoops: the allocation blocks aren't even multiples of the specified base:
724 no amount of dividing them into even parts will be a multiple, either then!
726 return 512; /* Hope for the best */
729 /* Try the obvious winner first, to prevent 12K allocation blocks, for instance,
730 from being handled as two 6K logical blocks instead of 3 4K logical blocks.
731 Even though the former (the result of the loop below) is the larger allocation
732 block size, the latter is more efficient: */
733 if (allocationBlockSize
% PAGE_SIZE
== 0) return PAGE_SIZE
;
735 /* No clear winner exists: pick the largest even fraction <= MAXBSIZE: */
736 baseBlockCount
= allocationBlockSize
/ baseMultiple
; /* Now guaranteed to be an even multiple */
738 for (blockCount
= baseBlockCount
; blockCount
> 0; --blockCount
) {
739 trialBlockSize
= blockCount
* baseMultiple
;
740 if (allocationBlockSize
% trialBlockSize
== 0) { /* An even multiple? */
741 if ((trialBlockSize
<= blockSizeLimit
) &&
742 (trialBlockSize
% baseMultiple
== 0)) {
743 return trialBlockSize
;
748 /* Note: we should never get here, since blockCount = 1 should always work,
749 but this is nice and safe and makes the compiler happy, too ... */
755 * To make the HFS Plus filesystem follow UFS unlink semantics, a remove
756 * of an active vnode is translated to a move/rename so the file appears
757 * deleted. The destination folder for these move/renames is setup here
758 * and a reference to it is place in hfsmp->hfs_private_metadata_dir.
761 FindMetaDataDirectory(ExtendedVCB
*vcb
)
763 struct hfsmount
* hfsmp
;
764 struct vnode
* dvp
= NULL
;
765 struct cnode
* dcp
= NULL
;
766 struct FndrDirInfo
* fndrinfo
;
767 struct cat_desc out_desc
= {0};
771 if (vcb
->vcbSigWord
!= kHFSPlusSigWord
)
774 hfsmp
= VCBTOHFS(vcb
);
776 if (hfsmp
->hfs_privdir_desc
.cd_parentcnid
== 0) {
777 hfsmp
->hfs_privdir_desc
.cd_parentcnid
= kRootDirID
;
778 hfsmp
->hfs_privdir_desc
.cd_nameptr
= hfs_privdirname
;
779 hfsmp
->hfs_privdir_desc
.cd_namelen
= strlen(hfs_privdirname
);
780 hfsmp
->hfs_privdir_desc
.cd_flags
= CD_ISDIR
;
783 /* Lock catalog b-tree */
784 error
= hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_EXCLUSIVE
, current_proc());
788 error
= cat_lookup(hfsmp
, &hfsmp
->hfs_privdir_desc
, 0, NULL
,
789 &hfsmp
->hfs_privdir_attr
, NULL
);
792 /* Unlock catalog b-tree */
793 (void) hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_RELEASE
, current_proc());
794 hfsmp
->hfs_metadata_createdate
= hfsmp
->hfs_privdir_attr
.ca_itime
;
795 return (hfsmp
->hfs_privdir_attr
.ca_fileid
);
796 } else if (hfsmp
->hfs_fs_ronly
) {
797 /* Unlock catalog b-tree */
798 (void) hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_RELEASE
, current_proc());
802 /* Setup the default attributes */
803 bzero(&hfsmp
->hfs_privdir_attr
, sizeof(struct cat_attr
));
804 hfsmp
->hfs_privdir_attr
.ca_mode
= S_IFDIR
;
805 hfsmp
->hfs_privdir_attr
.ca_flags
= SF_IMMUTABLE
;
806 hfsmp
->hfs_privdir_attr
.ca_nlink
= 2;
807 hfsmp
->hfs_privdir_attr
.ca_itime
= vcb
->vcbCrDate
;
808 hfsmp
->hfs_privdir_attr
.ca_mtime
= time
.tv_sec
;
810 /* hidden and off the desktop view */
811 fndrinfo
= (struct FndrDirInfo
*)&hfsmp
->hfs_privdir_attr
.ca_finderinfo
;
812 fndrinfo
->frLocation
.v
= SWAP_BE16 (22460);
813 fndrinfo
->frLocation
.h
= SWAP_BE16 (22460);
814 fndrinfo
->frFlags
|= SWAP_BE16 (kIsInvisible
+ kNameLocked
);
817 hfs_global_shared_lock_acquire(hfsmp
);
819 if ((error
= journal_start_transaction(hfsmp
->jnl
)) != 0) {
820 hfs_global_shared_lock_release(hfsmp
);
825 error
= cat_create(hfsmp
, &hfsmp
->hfs_privdir_desc
,
826 &hfsmp
->hfs_privdir_attr
, &out_desc
);
828 /* Unlock catalog b-tree */
829 (void) hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_RELEASE
, current_proc());
832 journal_end_transaction(hfsmp
->jnl
);
834 hfs_global_shared_lock_release(hfsmp
);
839 hfsmp
->hfs_privdir_desc
.cd_hint
= out_desc
.cd_hint
;
840 hfsmp
->hfs_privdir_desc
.cd_cnid
= out_desc
.cd_cnid
;
841 hfsmp
->hfs_privdir_attr
.ca_fileid
= out_desc
.cd_cnid
;
842 hfsmp
->hfs_metadata_createdate
= vcb
->vcbCrDate
;
844 if (VFS_ROOT(HFSTOVFS(hfsmp
), &dvp
) == 0) {
846 dcp
->c_childhint
= out_desc
.cd_hint
;
849 dcp
->c_flag
|= C_CHANGE
| C_UPDATE
;
851 (void) VOP_UPDATE(dvp
, &tv
, &tv
, 0);
854 hfs_volupdate(hfsmp
, VOL_MKDIR
, 1);
856 journal_end_transaction(hfsmp
->jnl
);
858 hfs_global_shared_lock_release(hfsmp
);
860 cat_releasedesc(&out_desc
);
862 return (out_desc
.cd_cnid
);
867 GetFileInfo(ExtendedVCB
*vcb
, u_int32_t dirid
, char *name
,
868 struct cat_attr
*fattr
, struct cat_fork
*forkinfo
)
870 struct hfsmount
* hfsmp
;
871 struct vnode
* dvp
= NULL
;
872 struct cnode
* dcp
= NULL
;
873 struct FndrDirInfo
* fndrinfo
;
874 struct cat_desc jdesc
;
878 if (vcb
->vcbSigWord
!= kHFSPlusSigWord
)
881 hfsmp
= VCBTOHFS(vcb
);
883 memset(&jdesc
, 0, sizeof(struct cat_desc
));
884 jdesc
.cd_parentcnid
= kRootDirID
;
885 jdesc
.cd_nameptr
= name
;
886 jdesc
.cd_namelen
= strlen(name
);
888 /* Lock catalog b-tree */
889 error
= hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_EXCLUSIVE
, current_proc());
893 error
= cat_lookup(hfsmp
, &jdesc
, 0, NULL
, fattr
, forkinfo
);
895 (void) hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_RELEASE
, current_proc());
898 return (fattr
->ca_fileid
);
899 } else if (hfsmp
->hfs_fs_ronly
) {
906 * On Journaled HFS, there can be orphaned files. These
907 * are files that were unlinked while busy. If the volume
908 * was not cleanly unmounted then some of these files may
909 * have persisted and need to be removed.
913 hfs_remove_orphans(struct hfsmount
* hfsmp
)
915 struct BTreeIterator
* iterator
= NULL
;
916 struct FSBufferDescriptor btdata
;
917 struct HFSPlusCatalogFile filerec
;
918 struct HFSPlusCatalogKey
* keyp
;
925 int result
, started_tr
= 0;
927 if (hfsmp
->hfs_orphans_cleaned
)
930 vcb
= HFSTOVCB(hfsmp
);
931 fcb
= VTOF(vcb
->catalogRefNum
);
933 btdata
.bufferAddress
= &filerec
;
934 btdata
.itemSize
= sizeof(filerec
);
935 btdata
.itemCount
= 1;
937 MALLOC(iterator
, struct BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
938 bzero(iterator
, sizeof(*iterator
));
939 keyp
= (HFSPlusCatalogKey
*)&iterator
->key
;
940 keyp
->parentID
= hfsmp
->hfs_private_metadata_dir
;
943 hfs_global_shared_lock_acquire(hfsmp
);
945 if (journal_start_transaction(hfsmp
->jnl
) != 0) {
946 hfs_global_shared_lock_release(hfsmp
);
952 /* Lock catalog b-tree */
953 result
= hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_EXCLUSIVE
, current_proc());
959 * Position the iterator at the folder thread record.
960 * (i.e. one record before first child)
962 result
= BTSearchRecord(fcb
, iterator
, NULL
, NULL
, iterator
);
966 /* Visit all the children in the HFS+ private directory. */
968 result
= BTIterateRecord(fcb
, kBTreeNextRecord
, iterator
, &btdata
, NULL
);
971 if (keyp
->parentID
!= hfsmp
->hfs_private_metadata_dir
)
973 if (filerec
.recordType
!= kHFSPlusFileRecord
)
976 (void) utf8_encodestr(keyp
->nodeName
.unicode
, keyp
->nodeName
.length
* 2,
977 filename
, &namelen
, sizeof(filename
), 0, 0);
979 (void) sprintf(tempname
, "%s%d", HFS_DELETE_PREFIX
, filerec
.fileID
);
982 * Delete all files named "tempxxx", where
983 * xxx is the file's cnid in decimal.
985 * Delete all files named "iNodexxx", that
986 * have a link count of zero.
988 if (bcmp(tempname
, filename
, namelen
) == 0) {
989 struct filefork fork
= {0};
990 struct cnode cnode
= {0};
993 //printf("hfs_remove_orphans: removing %s\n", filename);
995 /* Build a fake cnode */
996 cnode
.c_desc
.cd_parentcnid
= hfsmp
->hfs_private_metadata_dir
;
997 cnode
.c_desc
.cd_nameptr
= filename
;
998 cnode
.c_desc
.cd_namelen
= namelen
;
999 cnode
.c_desc
.cd_cnid
= filerec
.fileID
;
1000 cnode
.c_attr
.ca_fileid
= filerec
.fileID
;
1001 cnode
.c_blocks
= filerec
.dataFork
.totalBlocks
+
1002 filerec
.resourceFork
.totalBlocks
;
1004 /* Position iterator at previous entry */
1005 if (BTIterateRecord(fcb
, kBTreePrevRecord
, iterator
,
1009 /* Truncate the file to zero (both forks) */
1010 if (filerec
.dataFork
.totalBlocks
> 0) {
1011 fork
.ff_cp
= &cnode
;
1012 cnode
.c_datafork
= &fork
;
1013 bcopy(&filerec
.dataFork
, &fork
.ff_data
, sizeof(struct cat_fork
));
1014 if (TruncateFileC(vcb
, (FCB
*)&fork
, 0, false) != 0) {
1015 printf("error truncting data fork!\n");
1019 if (filerec
.resourceFork
.totalBlocks
> 0) {
1020 fork
.ff_cp
= &cnode
;
1021 cnode
.c_datafork
= NULL
;
1022 cnode
.c_rsrcfork
= &fork
;
1023 bcopy(&filerec
.resourceFork
, &fork
.ff_data
, sizeof(struct cat_fork
));
1024 if (TruncateFileC(vcb
, (FCB
*)&fork
, 0, false) != 0) {
1025 printf("error truncting rsrc fork!\n");
1030 /* Remove the file record from the Catalog */
1031 if (cat_delete(hfsmp
, &cnode
.c_desc
, &cnode
.c_attr
) != 0) {
1032 printf("error deleting cat rec!\n");
1036 /* Update parent and volume counts */
1037 hfsmp
->hfs_privdir_attr
.ca_entries
--;
1038 (void)cat_update(hfsmp
, &hfsmp
->hfs_privdir_desc
,
1039 &hfsmp
->hfs_privdir_attr
, NULL
, NULL
);
1040 hfs_volupdate(hfsmp
, VOL_RMFILE
, 0);
1045 /* Unlock catalog b-tree */
1047 (void) hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_RELEASE
, current_proc());
1050 journal_end_transaction(hfsmp
->jnl
);
1052 hfs_global_shared_lock_release(hfsmp
);
1054 FREE(iterator
, M_TEMP
);
1055 hfsmp
->hfs_orphans_cleaned
= 1;
1060 * This will return the correct logical block size for a given vnode.
1061 * For most files, it is the allocation block size, for meta data like
1062 * BTrees, this is kept as part of the BTree private nodeSize
1065 GetLogicalBlockSize(struct vnode
*vp
)
1067 u_int32_t logBlockSize
;
1069 DBG_ASSERT(vp
!= NULL
);
1071 /* start with default */
1072 logBlockSize
= VTOHFS(vp
)->hfs_logBlockSize
;
1074 if (vp
->v_flag
& VSYSTEM
) {
1075 if (VTOF(vp
)->fcbBTCBPtr
!= NULL
) {
1076 BTreeInfoRec bTreeInfo
;
1079 * We do not lock the BTrees, because if we are getting block..then the tree
1080 * should be locked in the first place.
1081 * We just want the nodeSize wich will NEVER change..so even if the world
1082 * is changing..the nodeSize should remain the same. Which argues why lock
1083 * it in the first place??
1086 (void) BTGetInformation (VTOF(vp
), kBTreeInfoVersion
, &bTreeInfo
);
1088 logBlockSize
= bTreeInfo
.nodeSize
;
1090 } else if (VTOC(vp
)->c_fileid
== kHFSAllocationFileID
) {
1091 logBlockSize
= VTOVCB(vp
)->vcbVBMIOSize
;
1095 DBG_ASSERT(logBlockSize
> 0);
1097 return logBlockSize
;
1102 hfs_freeblks(struct hfsmount
* hfsmp
, int wantreserve
)
1104 struct vcb_t
*vcb
= HFSTOVCB(hfsmp
);
1107 freeblks
= vcb
->freeBlocks
;
1109 if (freeblks
> vcb
->reserveBlocks
)
1110 freeblks
-= vcb
->reserveBlocks
;
1115 freeblks
-= vcb
->loanedBlocks
;
1120 * Map HFS Common errors (negative) to BSD error codes (positive).
1121 * Positive errors (ie BSD errors) are passed through unchanged.
1123 short MacToVFSError(OSErr err
)
1129 case dskFulErr
: /* -34 */
1131 case btNoSpaceAvail
: /* -32733 */
1133 case fxOvFlErr
: /* -32750 */
1136 case btBadNode
: /* -32731 */
1139 case memFullErr
: /* -108 */
1140 return ENOMEM
; /* +12 */
1142 case cmExists
: /* -32718 */
1143 case btExists
: /* -32734 */
1144 return EEXIST
; /* +17 */
1146 case cmNotFound
: /* -32719 */
1147 case btNotFound
: /* -32735 */
1148 return ENOENT
; /* 28 */
1150 case cmNotEmpty
: /* -32717 */
1151 return ENOTEMPTY
; /* 66 */
1153 case cmFThdDirErr
: /* -32714 */
1154 return EISDIR
; /* 21 */
1156 case fxRangeErr
: /* -32751 */
1159 case bdNamErr
: /* -37 */
1160 return ENAMETOOLONG
; /* 63 */
1162 case paramErr
: /* -50 */
1163 case fileBoundsErr
: /* -1309 */
1164 return EINVAL
; /* +22 */
1166 case fsBTBadNodeSize
:
1170 return EIO
; /* +5 */
1176 * Get the directory entry name hint for a given index.
1177 * The directory cnode (dcp) must be locked.
1181 hfs_getnamehint(struct cnode
*dcp
, int index
)
1183 struct hfs_index
*entry
;
1187 self
= current_thread();
1188 SLIST_FOREACH(entry
, &dcp
->c_indexlist
, hi_link
) {
1189 if ((entry
->hi_index
== index
)
1190 && (entry
->hi_thread
== self
))
1191 return (entry
->hi_name
);
1199 * Save a directory entry name hint for a given index.
1200 * The directory cnode (dcp) must be locked.
1204 hfs_savenamehint(struct cnode
*dcp
, int index
, const char * namehint
)
1206 struct hfs_index
*entry
;
1210 len
= strlen(namehint
);
1211 MALLOC(entry
, struct hfs_index
*, len
+ sizeof(struct hfs_index
),
1213 entry
->hi_index
= index
;
1214 entry
->hi_thread
= current_thread();
1215 bcopy(namehint
, entry
->hi_name
, len
+ 1);
1216 SLIST_INSERT_HEAD(&dcp
->c_indexlist
, entry
, hi_link
);
1221 * Release the directory entry name hint for a given index.
1222 * The directory cnode (dcp) must be locked.
1226 hfs_relnamehint(struct cnode
*dcp
, int index
)
1228 struct hfs_index
*entry
;
1232 self
= current_thread();
1233 SLIST_FOREACH(entry
, &dcp
->c_indexlist
, hi_link
) {
1234 if ((entry
->hi_index
== index
)
1235 && (entry
->hi_thread
== self
)) {
1236 SLIST_REMOVE(&dcp
->c_indexlist
, entry
, hfs_index
,
1238 FREE(entry
, M_TEMP
);
1246 * Release all directory entry name hints.
1250 hfs_relnamehints(struct cnode
*dcp
)
1252 struct hfs_index
*entry
;
1253 struct hfs_index
*next
;
1255 if (!SLIST_EMPTY(&dcp
->c_indexlist
)) {
1256 for(entry
= SLIST_FIRST(&dcp
->c_indexlist
);
1259 next
= SLIST_NEXT(entry
, hi_link
);
1260 SLIST_REMOVE(&dcp
->c_indexlist
, entry
, hfs_index
, hi_link
);
1261 FREE(entry
, M_TEMP
);
1268 * Perform a case-insensitive compare of two UTF-8 filenames.
1270 * Returns 0 if the strings match.
1274 hfs_namecmp(const char *str1
, size_t len1
, const char *str2
, size_t len2
)
1276 u_int16_t
*ustr1
, *ustr2
;
1277 size_t ulen1
, ulen2
;
1284 maxbytes
= kHFSPlusMaxFileNameChars
<< 1;
1285 MALLOC(ustr1
, u_int16_t
*, maxbytes
<< 1, M_TEMP
, M_WAITOK
);
1286 ustr2
= ustr1
+ (maxbytes
>> 1);
1288 if (utf8_decodestr(str1
, len1
, ustr1
, &ulen1
, maxbytes
, ':', 0) != 0)
1290 if (utf8_decodestr(str2
, len2
, ustr2
, &ulen2
, maxbytes
, ':', 0) != 0)
1293 cmp
= FastUnicodeCompare(ustr1
, ulen1
>>1, ustr2
, ulen2
>>1);
1295 FREE(ustr1
, M_TEMP
);
1302 hfs_early_journal_init(struct hfsmount
*hfsmp
, HFSPlusVolumeHeader
*vhp
,
1303 void *_args
, int embeddedOffset
, int mdb_offset
,
1304 HFSMasterDirectoryBlock
*mdbp
, struct ucred
*cred
)
1306 JournalInfoBlock
*jibp
;
1307 struct buf
*jinfo_bp
, *bp
;
1308 int sectors_per_fsblock
, arg_flags
=0, arg_tbufsz
=0;
1309 int retval
, blksize
= hfsmp
->hfs_phys_block_size
;
1310 struct vnode
*devvp
;
1311 struct hfs_mount_args
*args
= _args
;
1313 devvp
= hfsmp
->hfs_devvp
;
1315 if (args
!= NULL
&& (args
->flags
& HFSFSMNT_EXTENDED_ARGS
)) {
1316 arg_flags
= args
->journal_flags
;
1317 arg_tbufsz
= args
->journal_tbuffer_size
;
1320 sectors_per_fsblock
= SWAP_BE32(vhp
->blockSize
) / blksize
;
1322 retval
= meta_bread(devvp
,
1323 embeddedOffset
/blksize
+
1324 (SWAP_BE32(vhp
->journalInfoBlock
)*sectors_per_fsblock
),
1325 SWAP_BE32(vhp
->blockSize
), cred
, &jinfo_bp
);
1329 jibp
= (JournalInfoBlock
*)jinfo_bp
->b_data
;
1330 jibp
->flags
= SWAP_BE32(jibp
->flags
);
1331 jibp
->offset
= SWAP_BE64(jibp
->offset
);
1332 jibp
->size
= SWAP_BE64(jibp
->size
);
1334 if (jibp
->flags
& kJIJournalInFSMask
) {
1335 hfsmp
->jvp
= hfsmp
->hfs_devvp
;
1337 printf("hfs: journal not stored in fs! don't know what to do.\n");
1342 // save this off for the hack-y check in hfs_remove()
1343 hfsmp
->jnl_start
= jibp
->offset
/ SWAP_BE32(vhp
->blockSize
);
1345 if (jibp
->flags
& kJIJournalNeedInitMask
) {
1346 printf("hfs: Initializing the journal (joffset 0x%llx sz 0x%llx)...\n",
1347 jibp
->offset
+ (off_t
)embeddedOffset
, jibp
->size
);
1348 hfsmp
->jnl
= journal_create(hfsmp
->jvp
,
1349 jibp
->offset
+ (off_t
)embeddedOffset
,
1355 hfs_sync_metadata
, hfsmp
->hfs_mp
);
1357 // no need to start a transaction here... if this were to fail
1358 // we'd just re-init it on the next mount.
1359 jibp
->flags
&= ~kJIJournalNeedInitMask
;
1360 jibp
->flags
= SWAP_BE32(jibp
->flags
);
1365 //printf("hfs: Opening the journal (joffset 0x%llx sz 0x%llx vhp_blksize %d)...\n",
1366 // jibp->offset + (off_t)embeddedOffset,
1367 // jibp->size, SWAP_BE32(vhp->blockSize));
1369 hfsmp
->jnl
= journal_open(hfsmp
->jvp
,
1370 jibp
->offset
+ (off_t
)embeddedOffset
,
1376 hfs_sync_metadata
, hfsmp
->hfs_mp
);
1382 if (hfsmp
->jnl
&& mdbp
) {
1383 // reload the mdb because it could have changed
1384 // if the journal had to be replayed.
1385 retval
= meta_bread(devvp
, mdb_offset
, blksize
, cred
, &bp
);
1388 printf("hfs: failed to reload the mdb after opening the journal (retval %d)!\n",
1392 bcopy(bp
->b_data
+ HFS_PRI_OFFSET(blksize
), mdbp
, 512);
1399 //printf("journal @ 0x%x\n", hfsmp->jnl);
1401 // if we expected the journal to be there and we couldn't
1402 // create it or open it then we have to bail out.
1403 if (hfsmp
->jnl
== NULL
) {
1404 hfsmp
->jnl_start
= 0;
1406 printf("hfs: failed to open/create the journal (retval %d).\n", retval
);
1415 // This function will go and re-locate the .journal_info_block and
1416 // the .journal files in case they moved (which can happen if you
1417 // run Norton SpeedDisk). If we fail to find either file we just
1418 // disable journaling for this volume and return. We turn off the
1419 // journaling bit in the vcb and assume it will get written to disk
1420 // later (if it doesn't on the next mount we'd do the same thing
1421 // again which is harmless). If we disable journaling we don't
1422 // return an error so that the volume is still mountable.
1424 // If the info we find for the .journal_info_block and .journal files
1425 // isn't what we had stored, we re-set our cached info and proceed
1426 // with opening the journal normally.
1429 hfs_late_journal_init(struct hfsmount
*hfsmp
, HFSPlusVolumeHeader
*vhp
, void *_args
)
1431 JournalInfoBlock
*jibp
;
1432 struct buf
*jinfo_bp
, *bp
;
1433 int sectors_per_fsblock
, arg_flags
=0, arg_tbufsz
=0;
1434 int retval
, need_flush
= 0, write_jibp
= 0;
1435 struct vnode
*devvp
;
1436 struct cat_attr jib_attr
, jattr
;
1437 struct cat_fork jib_fork
, jfork
;
1440 struct hfs_mount_args
*args
= _args
;
1442 devvp
= hfsmp
->hfs_devvp
;
1443 vcb
= HFSTOVCB(hfsmp
);
1445 if (args
!= NULL
&& (args
->flags
& HFSFSMNT_EXTENDED_ARGS
)) {
1446 if (args
->journal_disable
) {
1450 arg_flags
= args
->journal_flags
;
1451 arg_tbufsz
= args
->journal_tbuffer_size
;
1454 fid
= GetFileInfo(vcb
, kRootDirID
, ".journal_info_block", &jib_attr
, &jib_fork
);
1455 if (fid
== 0 || jib_fork
.cf_extents
[0].startBlock
== 0 || jib_fork
.cf_size
== 0) {
1456 printf("hfs: can't find the .journal_info_block! disabling journaling (start: %d).\n",
1457 jib_fork
.cf_extents
[0].startBlock
);
1458 vcb
->vcbAtrb
&= ~kHFSVolumeJournaledMask
;
1461 hfsmp
->hfs_jnlinfoblkid
= fid
;
1463 // make sure the journal_info_block begins where we think it should.
1464 if (SWAP_BE32(vhp
->journalInfoBlock
) != jib_fork
.cf_extents
[0].startBlock
) {
1465 printf("hfs: The journal_info_block moved (was: %d; is: %d). Fixing up\n",
1466 SWAP_BE32(vhp
->journalInfoBlock
), jib_fork
.cf_extents
[0].startBlock
);
1468 vcb
->vcbJinfoBlock
= jib_fork
.cf_extents
[0].startBlock
;
1469 vhp
->journalInfoBlock
= SWAP_BE32(jib_fork
.cf_extents
[0].startBlock
);
1473 sectors_per_fsblock
= SWAP_BE32(vhp
->blockSize
) / hfsmp
->hfs_phys_block_size
;
1474 retval
= meta_bread(devvp
,
1475 vcb
->hfsPlusIOPosOffset
/ hfsmp
->hfs_phys_block_size
+
1476 (SWAP_BE32(vhp
->journalInfoBlock
)*sectors_per_fsblock
),
1477 SWAP_BE32(vhp
->blockSize
), NOCRED
, &jinfo_bp
);
1479 printf("hfs: can't read journal info block. disabling journaling.\n");
1480 vcb
->vcbAtrb
&= ~kHFSVolumeJournaledMask
;
1484 jibp
= (JournalInfoBlock
*)jinfo_bp
->b_data
;
1485 jibp
->flags
= SWAP_BE32(jibp
->flags
);
1486 jibp
->offset
= SWAP_BE64(jibp
->offset
);
1487 jibp
->size
= SWAP_BE64(jibp
->size
);
1489 fid
= GetFileInfo(vcb
, kRootDirID
, ".journal", &jattr
, &jfork
);
1490 if (fid
== 0 || jfork
.cf_extents
[0].startBlock
== 0 || jfork
.cf_size
== 0) {
1491 printf("hfs: can't find the journal file! disabling journaling (start: %d)\n",
1492 jfork
.cf_extents
[0].startBlock
);
1494 vcb
->vcbAtrb
&= ~kHFSVolumeJournaledMask
;
1497 hfsmp
->hfs_jnlfileid
= fid
;
1499 // make sure the journal file begins where we think it should.
1500 if ((jibp
->offset
/ (u_int64_t
)vcb
->blockSize
) != jfork
.cf_extents
[0].startBlock
) {
1501 printf("hfs: The journal file moved (was: %lld; is: %d). Fixing up\n",
1502 (jibp
->offset
/ (u_int64_t
)vcb
->blockSize
), jfork
.cf_extents
[0].startBlock
);
1504 jibp
->offset
= (u_int64_t
)jfork
.cf_extents
[0].startBlock
* (u_int64_t
)vcb
->blockSize
;
1508 // check the size of the journal file.
1509 if (jibp
->size
!= (u_int64_t
)jfork
.cf_extents
[0].blockCount
*vcb
->blockSize
) {
1510 printf("hfs: The journal file changed size! (was %lld; is %lld). Fixing up.\n",
1511 jibp
->size
, (u_int64_t
)jfork
.cf_extents
[0].blockCount
*vcb
->blockSize
);
1513 jibp
->size
= (u_int64_t
)jfork
.cf_extents
[0].blockCount
* vcb
->blockSize
;
1517 if (jibp
->flags
& kJIJournalInFSMask
) {
1518 hfsmp
->jvp
= hfsmp
->hfs_devvp
;
1520 printf("hfs: journal not stored in fs! don't know what to do.\n");
1525 // save this off for the hack-y check in hfs_remove()
1526 hfsmp
->jnl_start
= jibp
->offset
/ SWAP_BE32(vhp
->blockSize
);
1528 if (jibp
->flags
& kJIJournalNeedInitMask
) {
1529 printf("hfs: Initializing the journal (joffset 0x%llx sz 0x%llx)...\n",
1530 jibp
->offset
+ (off_t
)vcb
->hfsPlusIOPosOffset
, jibp
->size
);
1531 hfsmp
->jnl
= journal_create(hfsmp
->jvp
,
1532 jibp
->offset
+ (off_t
)vcb
->hfsPlusIOPosOffset
,
1535 hfsmp
->hfs_phys_block_size
,
1538 hfs_sync_metadata
, hfsmp
->hfs_mp
);
1540 // no need to start a transaction here... if this were to fail
1541 // we'd just re-init it on the next mount.
1542 jibp
->flags
&= ~kJIJournalNeedInitMask
;
1547 // if we weren't the last person to mount this volume
1548 // then we need to throw away the journal because it
1549 // is likely that someone else mucked with the disk.
1550 // if the journal is empty this is no big deal. if the
1551 // disk is dirty this prevents us from replaying the
1552 // journal over top of changes that someone else made.
1554 arg_flags
|= JOURNAL_RESET
;
1556 //printf("hfs: Opening the journal (joffset 0x%llx sz 0x%llx vhp_blksize %d)...\n",
1557 // jibp->offset + (off_t)vcb->hfsPlusIOPosOffset,
1558 // jibp->size, SWAP_BE32(vhp->blockSize));
1560 hfsmp
->jnl
= journal_open(hfsmp
->jvp
,
1561 jibp
->offset
+ (off_t
)vcb
->hfsPlusIOPosOffset
,
1564 hfsmp
->hfs_phys_block_size
,
1567 hfs_sync_metadata
, hfsmp
->hfs_mp
);
1572 jibp
->flags
= SWAP_BE32(jibp
->flags
);
1573 jibp
->offset
= SWAP_BE64(jibp
->offset
);
1574 jibp
->size
= SWAP_BE64(jibp
->size
);
1583 //printf("journal @ 0x%x\n", hfsmp->jnl);
1585 // if we expected the journal to be there and we couldn't
1586 // create it or open it then we have to bail out.
1587 if (hfsmp
->jnl
== NULL
) {
1588 hfsmp
->jnl_start
= 0;
1590 printf("hfs: failed to open/create the journal (retval %d).\n", retval
);