2 * Copyright (c) 2000-2002 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
20 * @APPLE_LICENSE_HEADER_END@
22 /* @(#)hfs_vfsutils.c 4.0
24 * (c) 1997-2002 Apple Computer, Inc. All Rights Reserved
26 * hfs_vfsutils.c -- Routines that go between the HFS layer and the VFS.
29 #include <sys/param.h>
30 #include <sys/systm.h>
31 #include <sys/kernel.h>
32 #include <sys/malloc.h>
34 #include <sys/mount.h>
35 #include <sys/namei.h>
39 #include <sys/unistd.h>
42 #include "hfs_catalog.h"
44 #include "hfs_mount.h"
45 #include "hfs_endian.h"
46 #include "hfs_cnode.h"
48 #include "hfscommon/headers/FileMgrInternal.h"
49 #include "hfscommon/headers/BTreesInternal.h"
50 #include "hfscommon/headers/HFSUnicodeWrappers.h"
53 extern int count_lock_queue
__P((void));
54 extern uid_t console_user
;
57 static void ReleaseMetaFileVNode(struct vnode
*vp
);
58 static int hfs_late_journal_init(struct hfsmount
*hfsmp
, HFSPlusVolumeHeader
*vhp
, void *_args
);
60 u_int32_t
GetLogicalBlockSize(struct vnode
*vp
);
62 /* BTree accessor routines */
63 extern OSStatus
GetBTreeBlock(FileReference vp
, UInt32 blockNum
, GetBlockOptions options
, BlockDescriptor
*block
);
64 extern OSStatus
SetBTreeBlockSize(FileReference vp
, ByteCount blockSize
, ItemCount minBlockCount
);
65 extern OSStatus
ExtendBTreeFile(FileReference vp
, FSSize minEOF
, FSSize maxEOF
);
66 extern OSStatus
ReleaseBTreeBlock(FileReference vp
, BlockDescPtr blockPtr
, ReleaseBlockOptions options
);
68 //*******************************************************************************
69 // Note: Finder information in the HFS/HFS+ metadata are considered opaque and
70 // hence are not in the right byte order on little endian machines. It is
71 // the responsibility of the finder and other clients to swap the data.
72 //*******************************************************************************
74 //*******************************************************************************
75 // Routine: hfs_MountHFSVolume
78 //*******************************************************************************
79 char hfs_catname
[] = "Catalog B-tree";
80 char hfs_extname
[] = "Extents B-tree";
81 char hfs_vbmname
[] = "Volume Bitmap";
83 char hfs_privdirname
[] =
84 "\xE2\x90\x80\xE2\x90\x80\xE2\x90\x80\xE2\x90\x80HFS+ Private Data";
86 OSErr
hfs_MountHFSVolume(struct hfsmount
*hfsmp
, HFSMasterDirectoryBlock
*mdb
,
89 ExtendedVCB
*vcb
= HFSTOVCB(hfsmp
);
92 struct cat_desc cndesc
;
93 struct cat_attr cnattr
;
96 /* Block size must be a multiple of 512 */
97 if (SWAP_BE32(mdb
->drAlBlkSiz
) == 0 ||
98 (SWAP_BE32(mdb
->drAlBlkSiz
) & 0x01FF) != 0)
101 /* don't mount a writeable volume if its dirty, it must be cleaned by fsck_hfs */
102 if ((hfsmp
->hfs_fs_ronly
== 0) && ((SWAP_BE16(mdb
->drAtrb
) & kHFSVolumeUnmountedMask
) == 0))
106 * The MDB seems OK: transfer info from it into VCB
107 * Note - the VCB starts out clear (all zeros)
110 vcb
->vcbSigWord
= SWAP_BE16 (mdb
->drSigWord
);
111 vcb
->vcbCrDate
= to_bsd_time(LocalToUTC(SWAP_BE32(mdb
->drCrDate
)));
112 vcb
->localCreateDate
= SWAP_BE32 (mdb
->drCrDate
);
113 vcb
->vcbLsMod
= to_bsd_time(LocalToUTC(SWAP_BE32(mdb
->drLsMod
)));
114 vcb
->vcbAtrb
= SWAP_BE16 (mdb
->drAtrb
);
115 vcb
->vcbNmFls
= SWAP_BE16 (mdb
->drNmFls
);
116 vcb
->vcbVBMSt
= SWAP_BE16 (mdb
->drVBMSt
);
117 vcb
->nextAllocation
= SWAP_BE16 (mdb
->drAllocPtr
);
118 vcb
->totalBlocks
= SWAP_BE16 (mdb
->drNmAlBlks
);
119 vcb
->blockSize
= SWAP_BE32 (mdb
->drAlBlkSiz
);
120 vcb
->vcbClpSiz
= SWAP_BE32 (mdb
->drClpSiz
);
121 vcb
->vcbAlBlSt
= SWAP_BE16 (mdb
->drAlBlSt
);
122 vcb
->vcbNxtCNID
= SWAP_BE32 (mdb
->drNxtCNID
);
123 vcb
->freeBlocks
= SWAP_BE16 (mdb
->drFreeBks
);
124 vcb
->vcbVolBkUp
= to_bsd_time(LocalToUTC(SWAP_BE32(mdb
->drVolBkUp
)));
125 vcb
->vcbWrCnt
= SWAP_BE32 (mdb
->drWrCnt
);
126 vcb
->vcbNmRtDirs
= SWAP_BE16 (mdb
->drNmRtDirs
);
127 vcb
->vcbFilCnt
= SWAP_BE32 (mdb
->drFilCnt
);
128 vcb
->vcbDirCnt
= SWAP_BE32 (mdb
->drDirCnt
);
129 bcopy(mdb
->drFndrInfo
, vcb
->vcbFndrInfo
, sizeof(vcb
->vcbFndrInfo
));
130 if (!hfsmp
->hfs_fs_ronly
)
131 vcb
->vcbWrCnt
++; /* Compensate for write of MDB on last flush */
133 /* convert hfs encoded name into UTF-8 string */
134 error
= hfs_to_utf8(vcb
, mdb
->drVN
, NAME_MAX
, &utf8chars
, vcb
->vcbVN
);
136 * When an HFS name cannot be encoded with the current
137 * volume encoding we use MacRoman as a fallback.
139 if (error
|| (utf8chars
== 0))
140 (void) mac_roman_to_utf8(mdb
->drVN
, NAME_MAX
, &utf8chars
, vcb
->vcbVN
);
142 hfsmp
->hfs_logBlockSize
= BestBlockSizeFit(vcb
->blockSize
, MAXBSIZE
, hfsmp
->hfs_phys_block_size
);
143 vcb
->vcbVBMIOSize
= kHFSBlockSize
;
147 bzero(&cndesc
, sizeof(cndesc
));
148 cndesc
.cd_parentcnid
= kRootParID
;
149 bzero(&cnattr
, sizeof(cnattr
));
151 cnattr
.ca_mode
= S_IFREG
;
152 bzero(&fork
, sizeof(fork
));
155 * Set up Extents B-tree vnode
157 cndesc
.cd_nameptr
= hfs_extname
;
158 cndesc
.cd_namelen
= strlen(hfs_extname
);
159 cndesc
.cd_cnid
= cnattr
.ca_fileid
= kHFSExtentsFileID
;
160 fork
.cf_size
= SWAP_BE32(mdb
->drXTFlSize
);
161 fork
.cf_blocks
= fork
.cf_size
/ vcb
->blockSize
;
162 fork
.cf_clump
= SWAP_BE32(mdb
->drXTClpSiz
);
163 fork
.cf_extents
[0].startBlock
= SWAP_BE16(mdb
->drXTExtRec
[0].startBlock
);
164 fork
.cf_extents
[0].blockCount
= SWAP_BE16(mdb
->drXTExtRec
[0].blockCount
);
165 fork
.cf_extents
[1].startBlock
= SWAP_BE16(mdb
->drXTExtRec
[1].startBlock
);
166 fork
.cf_extents
[1].blockCount
= SWAP_BE16(mdb
->drXTExtRec
[1].blockCount
);
167 fork
.cf_extents
[2].startBlock
= SWAP_BE16(mdb
->drXTExtRec
[2].startBlock
);
168 fork
.cf_extents
[2].blockCount
= SWAP_BE16(mdb
->drXTExtRec
[2].blockCount
);
169 cnattr
.ca_blocks
= fork
.cf_blocks
;
171 error
= hfs_getnewvnode(hfsmp
, NULL
, &cndesc
, 0, &cnattr
, &fork
,
172 &vcb
->extentsRefNum
);
173 if (error
) goto MtVolErr
;
174 error
= MacToVFSError(BTOpenPath(VTOF(vcb
->extentsRefNum
),
175 (KeyCompareProcPtr
)CompareExtentKeys
,
176 GetBTreeBlock
, ReleaseBTreeBlock
,
177 ExtendBTreeFile
, SetBTreeBlockSize
));
179 VOP_UNLOCK(vcb
->extentsRefNum
, 0, p
);
184 * Set up Catalog B-tree vnode...
186 cndesc
.cd_nameptr
= hfs_catname
;
187 cndesc
.cd_namelen
= strlen(hfs_catname
);
188 cndesc
.cd_cnid
= cnattr
.ca_fileid
= kHFSCatalogFileID
;
189 fork
.cf_size
= SWAP_BE32(mdb
->drCTFlSize
);
190 fork
.cf_blocks
= fork
.cf_size
/ vcb
->blockSize
;
191 fork
.cf_clump
= SWAP_BE32(mdb
->drCTClpSiz
);
192 fork
.cf_extents
[0].startBlock
= SWAP_BE16(mdb
->drCTExtRec
[0].startBlock
);
193 fork
.cf_extents
[0].blockCount
= SWAP_BE16(mdb
->drCTExtRec
[0].blockCount
);
194 fork
.cf_extents
[1].startBlock
= SWAP_BE16(mdb
->drCTExtRec
[1].startBlock
);
195 fork
.cf_extents
[1].blockCount
= SWAP_BE16(mdb
->drCTExtRec
[1].blockCount
);
196 fork
.cf_extents
[2].startBlock
= SWAP_BE16(mdb
->drCTExtRec
[2].startBlock
);
197 fork
.cf_extents
[2].blockCount
= SWAP_BE16(mdb
->drCTExtRec
[2].blockCount
);
198 cnattr
.ca_blocks
= fork
.cf_blocks
;
200 error
= hfs_getnewvnode(hfsmp
, NULL
, &cndesc
, 0, &cnattr
, &fork
,
201 &vcb
->catalogRefNum
);
203 VOP_UNLOCK(vcb
->extentsRefNum
, 0, p
);
206 error
= MacToVFSError(BTOpenPath(VTOF(vcb
->catalogRefNum
),
207 (KeyCompareProcPtr
)CompareCatalogKeys
,
208 GetBTreeBlock
, ReleaseBTreeBlock
,
209 ExtendBTreeFile
, SetBTreeBlockSize
));
211 VOP_UNLOCK(vcb
->catalogRefNum
, 0, p
);
212 VOP_UNLOCK(vcb
->extentsRefNum
, 0, p
);
216 /* mark the volume dirty (clear clean unmount bit) */
217 vcb
->vcbAtrb
&= ~kHFSVolumeUnmountedMask
;
220 * all done with b-trees so we can unlock now...
222 VOP_UNLOCK(vcb
->catalogRefNum
, 0, p
);
223 VOP_UNLOCK(vcb
->extentsRefNum
, 0, p
);
225 if ( error
== noErr
)
227 if ( !(vcb
->vcbAtrb
& kHFSVolumeHardwareLockMask
) ) // if the disk is not write protected
229 MarkVCBDirty( vcb
); // mark VCB dirty so it will be written
234 //-- Release any resources allocated so far before exiting with an error:
236 ReleaseMetaFileVNode(vcb
->catalogRefNum
);
237 ReleaseMetaFileVNode(vcb
->extentsRefNum
);
243 //*******************************************************************************
244 // Routine: hfs_MountHFSPlusVolume
247 //*******************************************************************************
249 OSErr
hfs_MountHFSPlusVolume(struct hfsmount
*hfsmp
, HFSPlusVolumeHeader
*vhp
,
250 off_t embeddedOffset
, u_int64_t disksize
, struct proc
*p
, void *args
)
252 register ExtendedVCB
*vcb
;
253 struct cat_desc cndesc
;
254 struct cat_attr cnattr
;
258 // XXXdbg - added the kHFSJSigWord case
259 if ((SWAP_BE16(vhp
->signature
) != kHFSPlusSigWord
&&
260 SWAP_BE16(vhp
->signature
) != kHFSJSigWord
) ||
261 SWAP_BE16(vhp
->version
) != kHFSPlusVersion
) {
263 printf("hfs: mount: sig 0x%x and version 0x%x are not HFS or HFS+.\n",
264 vhp
->signature
, vhp
->version
);
268 /* Block size must be at least 512 and a power of 2 */
269 blockSize
= SWAP_BE32(vhp
->blockSize
);
270 if (blockSize
< 512 || (blockSize
& (blockSize
-1)) != 0)
273 /* don't mount a writable volume if its dirty, it must be cleaned by fsck_hfs */
274 if (hfsmp
->hfs_fs_ronly
== 0 && hfsmp
->jnl
== NULL
&& (SWAP_BE32(vhp
->attributes
) & kHFSVolumeUnmountedMask
) == 0)
277 /* Make sure we can live with the physical block size. */
278 if ((disksize
& (hfsmp
->hfs_phys_block_size
- 1)) ||
279 (embeddedOffset
& (hfsmp
->hfs_phys_block_size
- 1)) ||
280 (SWAP_BE32(vhp
->blockSize
) < hfsmp
->hfs_phys_block_size
)) {
284 * The VolumeHeader seems OK: transfer info from it into VCB
285 * Note - the VCB starts out clear (all zeros)
287 vcb
= HFSTOVCB(hfsmp
);
289 vcb
->vcbSigWord
= SWAP_BE16(vhp
->signature
);
291 // XXXdbg - remap this in case we've mounted a dirty journaled volume
292 if (vcb
->vcbSigWord
== kHFSJSigWord
) {
293 vcb
->vcbSigWord
= kHFSPlusSigWord
;
296 vcb
->vcbJinfoBlock
= SWAP_BE32(vhp
->journalInfoBlock
);
297 vcb
->vcbLsMod
= to_bsd_time(SWAP_BE32(vhp
->modifyDate
));
298 vcb
->vcbAtrb
= (UInt16
)SWAP_BE32(vhp
->attributes
);
299 vcb
->vcbClpSiz
= SWAP_BE32(vhp
->rsrcClumpSize
);
300 vcb
->vcbNxtCNID
= SWAP_BE32(vhp
->nextCatalogID
);
301 vcb
->vcbVolBkUp
= to_bsd_time(SWAP_BE32(vhp
->backupDate
));
302 vcb
->vcbWrCnt
= SWAP_BE32(vhp
->writeCount
);
303 vcb
->vcbFilCnt
= SWAP_BE32(vhp
->fileCount
);
304 vcb
->vcbDirCnt
= SWAP_BE32(vhp
->folderCount
);
306 /* copy 32 bytes of Finder info */
307 bcopy(vhp
->finderInfo
, vcb
->vcbFndrInfo
, sizeof(vhp
->finderInfo
));
309 vcb
->vcbAlBlSt
= 0; /* hfs+ allocation blocks start at first block of volume */
310 if (!hfsmp
->hfs_fs_ronly
)
311 vcb
->vcbWrCnt
++; /* compensate for write of Volume Header on last flush */
315 /* Now fill in the Extended VCB info */
316 vcb
->nextAllocation
= SWAP_BE32(vhp
->nextAllocation
);
317 vcb
->totalBlocks
= SWAP_BE32(vhp
->totalBlocks
);
318 vcb
->freeBlocks
= SWAP_BE32(vhp
->freeBlocks
);
319 vcb
->blockSize
= SWAP_BE32(vhp
->blockSize
);
320 vcb
->encodingsBitmap
= SWAP_BE64(vhp
->encodingsBitmap
);
321 vcb
->localCreateDate
= SWAP_BE32(vhp
->createDate
);
323 vcb
->hfsPlusIOPosOffset
= embeddedOffset
;
325 /* Default to no free block reserve */
326 vcb
->reserveBlocks
= 0;
329 * Update the logical block size in the mount struct
330 * (currently set up from the wrapper MDB) using the
331 * new blocksize value:
333 hfsmp
->hfs_logBlockSize
= BestBlockSizeFit(vcb
->blockSize
, MAXBSIZE
, hfsmp
->hfs_phys_block_size
);
334 vcb
->vcbVBMIOSize
= min(vcb
->blockSize
, MAXPHYSIO
);
336 bzero(&cndesc
, sizeof(cndesc
));
337 cndesc
.cd_parentcnid
= kRootParID
;
338 bzero(&cnattr
, sizeof(cnattr
));
340 cnattr
.ca_mode
= S_IFREG
;
343 * Set up Extents B-tree vnode
345 cndesc
.cd_nameptr
= hfs_extname
;
346 cndesc
.cd_namelen
= strlen(hfs_extname
);
347 cndesc
.cd_cnid
= cnattr
.ca_fileid
= kHFSExtentsFileID
;
349 SWAP_HFS_PLUS_FORK_DATA (&vhp
->extentsFile
);
350 cnattr
.ca_blocks
= vhp
->extentsFile
.totalBlocks
;
352 retval
= hfs_getnewvnode(hfsmp
, NULL
, &cndesc
, 0, &cnattr
,
353 (struct cat_fork
*)&vhp
->extentsFile
,
354 &vcb
->extentsRefNum
);
355 SWAP_HFS_PLUS_FORK_DATA (&vhp
->extentsFile
);
357 if (retval
) goto ErrorExit
;
358 retval
= MacToVFSError(BTOpenPath(VTOF(vcb
->extentsRefNum
),
359 (KeyCompareProcPtr
) CompareExtentKeysPlus
,
360 GetBTreeBlock
, ReleaseBTreeBlock
,
361 ExtendBTreeFile
, SetBTreeBlockSize
));
363 VOP_UNLOCK(vcb
->extentsRefNum
, 0, p
);
368 * Set up Catalog B-tree vnode
370 cndesc
.cd_nameptr
= hfs_catname
;
371 cndesc
.cd_namelen
= strlen(hfs_catname
);
372 cndesc
.cd_cnid
= cnattr
.ca_fileid
= kHFSCatalogFileID
;
374 SWAP_HFS_PLUS_FORK_DATA(&vhp
->catalogFile
);
375 cnattr
.ca_blocks
= vhp
->catalogFile
.totalBlocks
;
377 retval
= hfs_getnewvnode(hfsmp
, NULL
, &cndesc
, 0, &cnattr
,
378 (struct cat_fork
*)&vhp
->catalogFile
,
379 &vcb
->catalogRefNum
);
380 SWAP_HFS_PLUS_FORK_DATA(&vhp
->catalogFile
);
382 VOP_UNLOCK(vcb
->extentsRefNum
, 0, p
);
385 retval
= MacToVFSError(BTOpenPath(VTOF(vcb
->catalogRefNum
),
386 (KeyCompareProcPtr
) CompareExtendedCatalogKeys
,
387 GetBTreeBlock
, ReleaseBTreeBlock
,
388 ExtendBTreeFile
, SetBTreeBlockSize
));
390 VOP_UNLOCK(vcb
->catalogRefNum
, 0, p
);
391 VOP_UNLOCK(vcb
->extentsRefNum
, 0, p
);
396 * Set up Allocation file vnode
398 cndesc
.cd_nameptr
= hfs_vbmname
;
399 cndesc
.cd_namelen
= strlen(hfs_vbmname
);
400 cndesc
.cd_cnid
= cnattr
.ca_fileid
= kHFSAllocationFileID
;
402 SWAP_HFS_PLUS_FORK_DATA(&vhp
->allocationFile
);
403 cnattr
.ca_blocks
= vhp
->allocationFile
.totalBlocks
;
405 retval
= hfs_getnewvnode(hfsmp
, NULL
, &cndesc
, 0, &cnattr
,
406 (struct cat_fork
*)&vhp
->allocationFile
,
407 &vcb
->allocationsRefNum
);
408 SWAP_HFS_PLUS_FORK_DATA(&vhp
->allocationFile
);
410 VOP_UNLOCK(vcb
->catalogRefNum
, 0, p
);
411 VOP_UNLOCK(vcb
->extentsRefNum
, 0, p
);
415 /* Pick up volume name and create date */
416 retval
= cat_idlookup(hfsmp
, kHFSRootFolderID
, &cndesc
, &cnattr
, NULL
);
418 VOP_UNLOCK(vcb
->allocationsRefNum
, 0, p
);
419 VOP_UNLOCK(vcb
->catalogRefNum
, 0, p
);
420 VOP_UNLOCK(vcb
->extentsRefNum
, 0, p
);
423 vcb
->vcbCrDate
= cnattr
.ca_itime
;
424 vcb
->volumeNameEncodingHint
= cndesc
.cd_encoding
;
425 bcopy(cndesc
.cd_nameptr
, vcb
->vcbVN
, min(255, cndesc
.cd_namelen
));
426 cat_releasedesc(&cndesc
);
428 /* mark the volume dirty (clear clean unmount bit) */
429 vcb
->vcbAtrb
&= ~kHFSVolumeUnmountedMask
;
430 if (hfsmp
->jnl
&& hfsmp
->hfs_fs_ronly
== 0) {
431 hfs_flushvolumeheader(hfsmp
, TRUE
, TRUE
);
435 * all done with metadata files so we can unlock now...
437 VOP_UNLOCK(vcb
->allocationsRefNum
, 0, p
);
438 VOP_UNLOCK(vcb
->catalogRefNum
, 0, p
);
439 VOP_UNLOCK(vcb
->extentsRefNum
, 0, p
);
441 /* setup private/hidden directory for unlinked files */
442 hfsmp
->hfs_private_metadata_dir
= FindMetaDataDirectory(vcb
);
443 if (hfsmp
->jnl
&& (hfsmp
->hfs_fs_ronly
== 0))
444 hfs_remove_orphans(hfsmp
);
446 if ( !(vcb
->vcbAtrb
& kHFSVolumeHardwareLockMask
) ) // if the disk is not write protected
448 MarkVCBDirty( vcb
); // mark VCB dirty so it will be written
453 // Check if we need to do late journal initialization. This only
454 // happens if a previous version of MacOS X (or 9) touched the disk.
455 // In that case hfs_late_journal_init() will go re-locate the journal
456 // and journal_info_block files and validate that they're still kosher.
458 if ( (vcb
->vcbAtrb
& kHFSVolumeJournaledMask
)
459 && (SWAP_BE32(vhp
->lastMountedVersion
) != kHFSJMountVersion
)
460 && (hfsmp
->jnl
== NULL
)) {
462 retval
= hfs_late_journal_init(hfsmp
, vhp
, args
);
466 } else if (hfsmp
->jnl
) {
467 hfsmp
->hfs_mp
->mnt_flag
|= MNT_JOURNALED
;
469 } else if (hfsmp
->jnl
) {
470 struct cat_attr jinfo_attr
, jnl_attr
;
472 // if we're here we need to fill in the fileid's for the
473 // journal and journal_info_block.
474 hfsmp
->hfs_jnlinfoblkid
= GetFileInfo(vcb
, kRootDirID
, ".journal_info_block", &jinfo_attr
, NULL
);
475 hfsmp
->hfs_jnlfileid
= GetFileInfo(vcb
, kRootDirID
, ".journal", &jnl_attr
, NULL
);
476 if (hfsmp
->hfs_jnlinfoblkid
== 0 || hfsmp
->hfs_jnlfileid
== 0) {
477 printf("hfs: danger! couldn't find the file-id's for the journal or journal_info_block\n");
478 printf("hfs: jnlfileid %d, jnlinfoblkid %d\n", hfsmp
->hfs_jnlfileid
, hfsmp
->hfs_jnlinfoblkid
);
487 * A fatal error occured and the volume cannot be mounted
488 * release any resources that we aquired...
491 InvalidateCatalogCache(vcb
);
492 ReleaseMetaFileVNode(vcb
->allocationsRefNum
);
493 ReleaseMetaFileVNode(vcb
->catalogRefNum
);
494 ReleaseMetaFileVNode(vcb
->extentsRefNum
);
501 * ReleaseMetaFileVNode
505 static void ReleaseMetaFileVNode(struct vnode
*vp
)
509 if (vp
&& (fp
= VTOF(vp
))) {
510 if (fp
->fcbBTCBPtr
!= NULL
)
511 (void) BTClosePath(fp
);
513 /* release the node even if BTClosePath fails */
520 /*************************************************************
522 * Unmounts a hfs volume.
523 * At this point vflush() has been called (to dump all non-metadata files)
525 *************************************************************/
527 short hfsUnmount( register struct hfsmount
*hfsmp
, struct proc
*p
)
529 ExtendedVCB
*vcb
= HFSTOVCB(hfsmp
);
532 InvalidateCatalogCache( vcb
);
534 if (vcb
->vcbSigWord
== kHFSPlusSigWord
)
535 ReleaseMetaFileVNode(vcb
->allocationsRefNum
);
537 ReleaseMetaFileVNode(vcb
->catalogRefNum
);
538 ReleaseMetaFileVNode(vcb
->extentsRefNum
);
545 * Some 3rd party kexts link against hfs_getcatalog so keep a stub for now.
548 hfs_getcatalog(void *p1
, u_long p2
, void *p3
, short p4
, void *p5
)
554 int overflow_extents(struct filefork
*fp
)
558 if (VTOVCB(FTOV(fp
))->vcbSigWord
== kHFSPlusSigWord
) {
559 if (fp
->ff_extents
[7].blockCount
== 0)
562 blocks
= fp
->ff_extents
[0].blockCount
+
563 fp
->ff_extents
[1].blockCount
+
564 fp
->ff_extents
[2].blockCount
+
565 fp
->ff_extents
[3].blockCount
+
566 fp
->ff_extents
[4].blockCount
+
567 fp
->ff_extents
[5].blockCount
+
568 fp
->ff_extents
[6].blockCount
+
569 fp
->ff_extents
[7].blockCount
;
571 if (fp
->ff_extents
[2].blockCount
== 0)
574 blocks
= fp
->ff_extents
[0].blockCount
+
575 fp
->ff_extents
[1].blockCount
+
576 fp
->ff_extents
[2].blockCount
;
579 return (fp
->ff_blocks
> blocks
);
583 /* __private_extern__ */
585 hfs_metafilelocking(struct hfsmount
*hfsmp
, u_long fileID
, u_int flags
, struct proc
*p
)
588 struct vnode
*vp
= NULL
;
589 int numOfLockedBuffs
;
592 vcb
= HFSTOVCB(hfsmp
);
595 case kHFSExtentsFileID
:
596 vp
= vcb
->extentsRefNum
;
599 case kHFSCatalogFileID
:
600 vp
= vcb
->catalogRefNum
;
603 case kHFSAllocationFileID
:
604 /* bitmap is covered by Extents B-tree locking */
607 panic("hfs_lockmetafile: invalid fileID");
610 /* Release, if necesary any locked buffer caches */
611 if ((flags
& LK_TYPE_MASK
) == LK_RELEASE
) {
612 struct timeval tv
= time
;
613 u_int32_t lastfsync
= tv
.tv_sec
;
615 (void) BTGetLastSync((FCB
*)VTOF(vp
), &lastfsync
);
617 numOfLockedBuffs
= count_lock_queue();
618 if ((numOfLockedBuffs
> kMaxLockedMetaBuffers
) || ((numOfLockedBuffs
>1) && ((tv
.tv_sec
- lastfsync
) > kMaxSecsForFsync
))) {
619 hfs_btsync(vp
, HFS_SYNCTRANS
);
625 retval
= lockmgr(&VTOC(vp
)->c_lock
, flags
, &vp
->v_interlock
, p
);
633 * Check to see if a vnode is locked in the current context
634 * This is to be used for debugging purposes only!!
637 void RequireFileLock(FileReference vp
, int shareable
)
639 struct lock__bsd__
*lkp
;
644 pid
= current_proc()->p_pid
;
645 self
= (void *) current_thread();
646 lkp
= &VTOC(vp
)->c_lock
;
648 simple_lock(&lkp
->lk_interlock
);
650 if (shareable
&& (lkp
->lk_sharecount
> 0) && (lkp
->lk_lockholder
== LK_NOPROC
))
652 else if ((lkp
->lk_exclusivecount
> 0) && (lkp
->lk_lockholder
== pid
) && (lkp
->lk_lockthread
== self
))
655 simple_unlock(&lkp
->lk_interlock
);
658 switch (VTOC(vp
)->c_fileid
) {
660 DEBUG_BREAK_MSG((" #\n # RequireFileLock: extent btree vnode not locked! v: 0x%08X\n #\n", (u_int
)vp
));
664 DEBUG_BREAK_MSG((" #\n # RequireFileLock: catalog btree vnode not locked! v: 0x%08X\n #\n", (u_int
)vp
));
668 DEBUG_BREAK_MSG((" #\n # RequireFileLock: file (%d) not locked! v: 0x%08X\n #\n", VTOC(vp
)->c_fileid
, (u_int
)vp
));
677 * There are three ways to qualify for ownership rights on an object:
679 * 1. (a) Your UID matches the cnode's UID.
680 * (b) The object in question is owned by "unknown" and
681 * your UID matches the console user's UID.
682 * 2. (a) Permissions on the filesystem are being ignored and
683 * your UID matches the replacement UID.
684 * (b) Permissions on the filesystem are being ignored and
685 * the replacement UID is "unknown" and
686 * your UID matches the console user UID.
691 hfs_owner_rights(struct hfsmount
*hfsmp
, uid_t cnode_uid
, struct ucred
*cred
,
692 struct proc
*p
, int invokesuperuserstatus
)
694 if ((cred
->cr_uid
== cnode_uid
) || /* [1a] */
695 ((cnode_uid
== UNKNOWNUID
) && (cred
->cr_uid
== console_user
)) || /* [1b] */
696 ((HFSTOVFS(hfsmp
)->mnt_flag
& MNT_UNKNOWNPERMISSIONS
) && /* [2] */
697 ((cred
->cr_uid
== hfsmp
->hfs_uid
) || /* [2a] */
698 ((hfsmp
->hfs_uid
== UNKNOWNUID
) && /* [2b] */
699 (cred
->cr_uid
== console_user
)))) ||
700 (invokesuperuserstatus
&& (suser(cred
, &p
->p_acflag
) == 0))) { /* [3] */
708 unsigned long BestBlockSizeFit(unsigned long allocationBlockSize
,
709 unsigned long blockSizeLimit
,
710 unsigned long baseMultiple
) {
712 Compute the optimal (largest) block size (no larger than allocationBlockSize) that is less than the
713 specified limit but still an even multiple of the baseMultiple.
715 int baseBlockCount
, blockCount
;
716 unsigned long trialBlockSize
;
718 if (allocationBlockSize
% baseMultiple
!= 0) {
720 Whoops: the allocation blocks aren't even multiples of the specified base:
721 no amount of dividing them into even parts will be a multiple, either then!
723 return 512; /* Hope for the best */
726 /* Try the obvious winner first, to prevent 12K allocation blocks, for instance,
727 from being handled as two 6K logical blocks instead of 3 4K logical blocks.
728 Even though the former (the result of the loop below) is the larger allocation
729 block size, the latter is more efficient: */
730 if (allocationBlockSize
% PAGE_SIZE
== 0) return PAGE_SIZE
;
732 /* No clear winner exists: pick the largest even fraction <= MAXBSIZE: */
733 baseBlockCount
= allocationBlockSize
/ baseMultiple
; /* Now guaranteed to be an even multiple */
735 for (blockCount
= baseBlockCount
; blockCount
> 0; --blockCount
) {
736 trialBlockSize
= blockCount
* baseMultiple
;
737 if (allocationBlockSize
% trialBlockSize
== 0) { /* An even multiple? */
738 if ((trialBlockSize
<= blockSizeLimit
) &&
739 (trialBlockSize
% baseMultiple
== 0)) {
740 return trialBlockSize
;
745 /* Note: we should never get here, since blockCount = 1 should always work,
746 but this is nice and safe and makes the compiler happy, too ... */
752 * To make the HFS Plus filesystem follow UFS unlink semantics, a remove
753 * of an active vnode is translated to a move/rename so the file appears
754 * deleted. The destination folder for these move/renames is setup here
755 * and a reference to it is place in hfsmp->hfs_private_metadata_dir.
758 FindMetaDataDirectory(ExtendedVCB
*vcb
)
760 struct hfsmount
* hfsmp
;
761 struct vnode
* dvp
= NULL
;
762 struct cnode
* dcp
= NULL
;
763 struct FndrDirInfo
* fndrinfo
;
764 struct cat_desc out_desc
= {0};
768 if (vcb
->vcbSigWord
!= kHFSPlusSigWord
)
771 hfsmp
= VCBTOHFS(vcb
);
773 if (hfsmp
->hfs_privdir_desc
.cd_parentcnid
== 0) {
774 hfsmp
->hfs_privdir_desc
.cd_parentcnid
= kRootDirID
;
775 hfsmp
->hfs_privdir_desc
.cd_nameptr
= hfs_privdirname
;
776 hfsmp
->hfs_privdir_desc
.cd_namelen
= strlen(hfs_privdirname
);
777 hfsmp
->hfs_privdir_desc
.cd_flags
= CD_ISDIR
;
780 /* Lock catalog b-tree */
781 error
= hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_EXCLUSIVE
, current_proc());
785 error
= cat_lookup(hfsmp
, &hfsmp
->hfs_privdir_desc
, 0, NULL
,
786 &hfsmp
->hfs_privdir_attr
, NULL
);
789 /* Unlock catalog b-tree */
790 (void) hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_RELEASE
, current_proc());
791 hfsmp
->hfs_metadata_createdate
= hfsmp
->hfs_privdir_attr
.ca_itime
;
792 return (hfsmp
->hfs_privdir_attr
.ca_fileid
);
793 } else if (hfsmp
->hfs_fs_ronly
) {
794 /* Unlock catalog b-tree */
795 (void) hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_RELEASE
, current_proc());
799 /* Setup the default attributes */
800 bzero(&hfsmp
->hfs_privdir_attr
, sizeof(struct cat_attr
));
801 hfsmp
->hfs_privdir_attr
.ca_mode
= S_IFDIR
;
802 hfsmp
->hfs_privdir_attr
.ca_flags
= SF_IMMUTABLE
;
803 hfsmp
->hfs_privdir_attr
.ca_nlink
= 2;
804 hfsmp
->hfs_privdir_attr
.ca_itime
= vcb
->vcbCrDate
;
805 hfsmp
->hfs_privdir_attr
.ca_mtime
= time
.tv_sec
;
807 /* hidden and off the desktop view */
808 fndrinfo
= (struct FndrDirInfo
*)&hfsmp
->hfs_privdir_attr
.ca_finderinfo
;
809 fndrinfo
->frLocation
.v
= SWAP_BE16 (22460);
810 fndrinfo
->frLocation
.h
= SWAP_BE16 (22460);
811 fndrinfo
->frFlags
|= SWAP_BE16 (kIsInvisible
+ kNameLocked
);
814 hfs_global_shared_lock_acquire(hfsmp
);
816 if ((error
= journal_start_transaction(hfsmp
->jnl
)) != 0) {
817 hfs_global_shared_lock_release(hfsmp
);
822 error
= cat_create(hfsmp
, &hfsmp
->hfs_privdir_desc
,
823 &hfsmp
->hfs_privdir_attr
, &out_desc
);
825 /* Unlock catalog b-tree */
826 (void) hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_RELEASE
, current_proc());
829 journal_end_transaction(hfsmp
->jnl
);
831 hfs_global_shared_lock_release(hfsmp
);
836 hfsmp
->hfs_privdir_desc
.cd_hint
= out_desc
.cd_hint
;
837 hfsmp
->hfs_privdir_desc
.cd_cnid
= out_desc
.cd_cnid
;
838 hfsmp
->hfs_privdir_attr
.ca_fileid
= out_desc
.cd_cnid
;
839 hfsmp
->hfs_metadata_createdate
= vcb
->vcbCrDate
;
841 if (VFS_ROOT(HFSTOVFS(hfsmp
), &dvp
) == 0) {
843 dcp
->c_childhint
= out_desc
.cd_hint
;
846 dcp
->c_flag
|= C_CHANGE
| C_UPDATE
;
848 (void) VOP_UPDATE(dvp
, &tv
, &tv
, 0);
851 hfs_volupdate(hfsmp
, VOL_MKDIR
, 1);
853 journal_end_transaction(hfsmp
->jnl
);
855 hfs_global_shared_lock_release(hfsmp
);
857 cat_releasedesc(&out_desc
);
859 return (out_desc
.cd_cnid
);
864 GetFileInfo(ExtendedVCB
*vcb
, u_int32_t dirid
, char *name
,
865 struct cat_attr
*fattr
, struct cat_fork
*forkinfo
)
867 struct hfsmount
* hfsmp
;
868 struct vnode
* dvp
= NULL
;
869 struct cnode
* dcp
= NULL
;
870 struct FndrDirInfo
* fndrinfo
;
871 struct cat_desc jdesc
;
875 if (vcb
->vcbSigWord
!= kHFSPlusSigWord
)
878 hfsmp
= VCBTOHFS(vcb
);
880 memset(&jdesc
, 0, sizeof(struct cat_desc
));
881 jdesc
.cd_parentcnid
= kRootDirID
;
882 jdesc
.cd_nameptr
= name
;
883 jdesc
.cd_namelen
= strlen(name
);
885 /* Lock catalog b-tree */
886 error
= hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_EXCLUSIVE
, current_proc());
890 error
= cat_lookup(hfsmp
, &jdesc
, 0, NULL
, fattr
, forkinfo
);
892 (void) hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_RELEASE
, current_proc());
895 return (fattr
->ca_fileid
);
896 } else if (hfsmp
->hfs_fs_ronly
) {
903 * On Journaled HFS, there can be orphaned files. These
904 * are files that were unlinked while busy. If the volume
905 * was not cleanly unmounted then some of these files may
906 * have persisted and need to be removed.
910 hfs_remove_orphans(struct hfsmount
* hfsmp
)
912 struct BTreeIterator
* iterator
= NULL
;
913 struct FSBufferDescriptor btdata
;
914 struct HFSPlusCatalogFile filerec
;
915 struct HFSPlusCatalogKey
* keyp
;
922 int result
, started_tr
= 0;
924 if (hfsmp
->hfs_orphans_cleaned
)
927 vcb
= HFSTOVCB(hfsmp
);
928 fcb
= VTOF(vcb
->catalogRefNum
);
930 btdata
.bufferAddress
= &filerec
;
931 btdata
.itemSize
= sizeof(filerec
);
932 btdata
.itemCount
= 1;
934 MALLOC(iterator
, struct BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
935 bzero(iterator
, sizeof(*iterator
));
936 keyp
= (HFSPlusCatalogKey
*)&iterator
->key
;
937 keyp
->parentID
= hfsmp
->hfs_private_metadata_dir
;
940 hfs_global_shared_lock_acquire(hfsmp
);
942 if (journal_start_transaction(hfsmp
->jnl
) != 0) {
943 hfs_global_shared_lock_release(hfsmp
);
949 /* Lock catalog b-tree */
950 result
= hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_EXCLUSIVE
, current_proc());
956 * Position the iterator at the folder thread record.
957 * (i.e. one record before first child)
959 result
= BTSearchRecord(fcb
, iterator
, NULL
, NULL
, iterator
);
963 /* Visit all the children in the HFS+ private directory. */
965 result
= BTIterateRecord(fcb
, kBTreeNextRecord
, iterator
, &btdata
, NULL
);
968 if (keyp
->parentID
!= hfsmp
->hfs_private_metadata_dir
)
970 if (filerec
.recordType
!= kHFSPlusFileRecord
)
973 (void) utf8_encodestr(keyp
->nodeName
.unicode
, keyp
->nodeName
.length
* 2,
974 filename
, &namelen
, sizeof(filename
), 0, 0);
976 (void) sprintf(tempname
, "%s%d", HFS_DELETE_PREFIX
, filerec
.fileID
);
979 * Delete all files named "tempxxx", where
980 * xxx is the file's cnid in decimal.
982 * Delete all files named "iNodexxx", that
983 * have a link count of zero.
985 if (bcmp(tempname
, filename
, namelen
) == 0) {
986 struct filefork fork
= {0};
987 struct cnode cnode
= {0};
990 //printf("hfs_remove_orphans: removing %s\n", filename);
992 /* Build a fake cnode */
993 cnode
.c_desc
.cd_parentcnid
= hfsmp
->hfs_private_metadata_dir
;
994 cnode
.c_desc
.cd_nameptr
= filename
;
995 cnode
.c_desc
.cd_namelen
= namelen
;
996 cnode
.c_desc
.cd_cnid
= filerec
.fileID
;
997 cnode
.c_attr
.ca_fileid
= filerec
.fileID
;
998 cnode
.c_blocks
= filerec
.dataFork
.totalBlocks
+
999 filerec
.resourceFork
.totalBlocks
;
1001 /* Position iterator at previous entry */
1002 if (BTIterateRecord(fcb
, kBTreePrevRecord
, iterator
,
1006 /* Truncate the file to zero (both forks) */
1007 if (filerec
.dataFork
.totalBlocks
> 0) {
1008 fork
.ff_cp
= &cnode
;
1009 cnode
.c_datafork
= &fork
;
1010 bcopy(&filerec
.dataFork
, &fork
.ff_data
, sizeof(struct cat_fork
));
1011 if (TruncateFileC(vcb
, (FCB
*)&fork
, 0, false) != 0) {
1012 printf("error truncting data fork!\n");
1016 if (filerec
.resourceFork
.totalBlocks
> 0) {
1017 fork
.ff_cp
= &cnode
;
1018 cnode
.c_datafork
= NULL
;
1019 cnode
.c_rsrcfork
= &fork
;
1020 bcopy(&filerec
.resourceFork
, &fork
.ff_data
, sizeof(struct cat_fork
));
1021 if (TruncateFileC(vcb
, (FCB
*)&fork
, 0, false) != 0) {
1022 printf("error truncting rsrc fork!\n");
1027 /* Remove the file record from the Catalog */
1028 if (cat_delete(hfsmp
, &cnode
.c_desc
, &cnode
.c_attr
) != 0) {
1029 printf("error deleting cat rec!\n");
1033 /* Update parent and volume counts */
1034 hfsmp
->hfs_privdir_attr
.ca_entries
--;
1035 (void)cat_update(hfsmp
, &hfsmp
->hfs_privdir_desc
,
1036 &hfsmp
->hfs_privdir_attr
, NULL
, NULL
);
1037 hfs_volupdate(hfsmp
, VOL_RMFILE
, 0);
1042 /* Unlock catalog b-tree */
1044 (void) hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_RELEASE
, current_proc());
1047 journal_end_transaction(hfsmp
->jnl
);
1049 hfs_global_shared_lock_release(hfsmp
);
1051 FREE(iterator
, M_TEMP
);
1052 hfsmp
->hfs_orphans_cleaned
= 1;
1057 * This will return the correct logical block size for a given vnode.
1058 * For most files, it is the allocation block size, for meta data like
1059 * BTrees, this is kept as part of the BTree private nodeSize
1062 GetLogicalBlockSize(struct vnode
*vp
)
1064 u_int32_t logBlockSize
;
1066 DBG_ASSERT(vp
!= NULL
);
1068 /* start with default */
1069 logBlockSize
= VTOHFS(vp
)->hfs_logBlockSize
;
1071 if (vp
->v_flag
& VSYSTEM
) {
1072 if (VTOF(vp
)->fcbBTCBPtr
!= NULL
) {
1073 BTreeInfoRec bTreeInfo
;
1076 * We do not lock the BTrees, because if we are getting block..then the tree
1077 * should be locked in the first place.
1078 * We just want the nodeSize wich will NEVER change..so even if the world
1079 * is changing..the nodeSize should remain the same. Which argues why lock
1080 * it in the first place??
1083 (void) BTGetInformation (VTOF(vp
), kBTreeInfoVersion
, &bTreeInfo
);
1085 logBlockSize
= bTreeInfo
.nodeSize
;
1087 } else if (VTOC(vp
)->c_fileid
== kHFSAllocationFileID
) {
1088 logBlockSize
= VTOVCB(vp
)->vcbVBMIOSize
;
1092 DBG_ASSERT(logBlockSize
> 0);
1094 return logBlockSize
;
1099 hfs_freeblks(struct hfsmount
* hfsmp
, int wantreserve
)
1101 struct vcb_t
*vcb
= HFSTOVCB(hfsmp
);
1104 freeblks
= vcb
->freeBlocks
;
1106 if (freeblks
> vcb
->reserveBlocks
)
1107 freeblks
-= vcb
->reserveBlocks
;
1112 freeblks
-= vcb
->loanedBlocks
;
1117 * Map HFS Common errors (negative) to BSD error codes (positive).
1118 * Positive errors (ie BSD errors) are passed through unchanged.
1120 short MacToVFSError(OSErr err
)
1126 case dskFulErr
: /* -34 */
1128 case btNoSpaceAvail
: /* -32733 */
1130 case fxOvFlErr
: /* -32750 */
1133 case btBadNode
: /* -32731 */
1136 case memFullErr
: /* -108 */
1137 return ENOMEM
; /* +12 */
1139 case cmExists
: /* -32718 */
1140 case btExists
: /* -32734 */
1141 return EEXIST
; /* +17 */
1143 case cmNotFound
: /* -32719 */
1144 case btNotFound
: /* -32735 */
1145 return ENOENT
; /* 28 */
1147 case cmNotEmpty
: /* -32717 */
1148 return ENOTEMPTY
; /* 66 */
1150 case cmFThdDirErr
: /* -32714 */
1151 return EISDIR
; /* 21 */
1153 case fxRangeErr
: /* -32751 */
1156 case bdNamErr
: /* -37 */
1157 return ENAMETOOLONG
; /* 63 */
1159 case paramErr
: /* -50 */
1160 case fileBoundsErr
: /* -1309 */
1161 return EINVAL
; /* +22 */
1163 case fsBTBadNodeSize
:
1167 return EIO
; /* +5 */
1173 * Get the directory entry name hint for a given index.
1174 * The directory cnode (dcp) must be locked.
1178 hfs_getnamehint(struct cnode
*dcp
, int index
)
1180 struct hfs_index
*entry
;
1184 self
= current_thread();
1185 SLIST_FOREACH(entry
, &dcp
->c_indexlist
, hi_link
) {
1186 if ((entry
->hi_index
== index
)
1187 && (entry
->hi_thread
== self
))
1188 return (entry
->hi_name
);
1196 * Save a directory entry name hint for a given index.
1197 * The directory cnode (dcp) must be locked.
1201 hfs_savenamehint(struct cnode
*dcp
, int index
, const char * namehint
)
1203 struct hfs_index
*entry
;
1207 len
= strlen(namehint
);
1208 MALLOC(entry
, struct hfs_index
*, len
+ sizeof(struct hfs_index
),
1210 entry
->hi_index
= index
;
1211 entry
->hi_thread
= current_thread();
1212 bcopy(namehint
, entry
->hi_name
, len
+ 1);
1213 SLIST_INSERT_HEAD(&dcp
->c_indexlist
, entry
, hi_link
);
1218 * Release the directory entry name hint for a given index.
1219 * The directory cnode (dcp) must be locked.
1223 hfs_relnamehint(struct cnode
*dcp
, int index
)
1225 struct hfs_index
*entry
;
1229 self
= current_thread();
1230 SLIST_FOREACH(entry
, &dcp
->c_indexlist
, hi_link
) {
1231 if ((entry
->hi_index
== index
)
1232 && (entry
->hi_thread
== self
)) {
1233 SLIST_REMOVE(&dcp
->c_indexlist
, entry
, hfs_index
,
1235 FREE(entry
, M_TEMP
);
1243 * Release all directory entry name hints.
1247 hfs_relnamehints(struct cnode
*dcp
)
1249 struct hfs_index
*entry
;
1250 struct hfs_index
*next
;
1252 if (!SLIST_EMPTY(&dcp
->c_indexlist
)) {
1253 for(entry
= SLIST_FIRST(&dcp
->c_indexlist
);
1256 next
= SLIST_NEXT(entry
, hi_link
);
1257 SLIST_REMOVE(&dcp
->c_indexlist
, entry
, hfs_index
, hi_link
);
1258 FREE(entry
, M_TEMP
);
1265 * Perform a case-insensitive compare of two UTF-8 filenames.
1267 * Returns 0 if the strings match.
1271 hfs_namecmp(const char *str1
, size_t len1
, const char *str2
, size_t len2
)
1273 u_int16_t
*ustr1
, *ustr2
;
1274 size_t ulen1
, ulen2
;
1281 maxbytes
= kHFSPlusMaxFileNameChars
<< 1;
1282 MALLOC(ustr1
, u_int16_t
*, maxbytes
<< 1, M_TEMP
, M_WAITOK
);
1283 ustr2
= ustr1
+ (maxbytes
>> 1);
1285 if (utf8_decodestr(str1
, len1
, ustr1
, &ulen1
, maxbytes
, ':', 0) != 0)
1287 if (utf8_decodestr(str2
, len2
, ustr2
, &ulen2
, maxbytes
, ':', 0) != 0)
1290 cmp
= FastUnicodeCompare(ustr1
, ulen1
>>1, ustr2
, ulen2
>>1);
1292 FREE(ustr1
, M_TEMP
);
1299 hfs_early_journal_init(struct hfsmount
*hfsmp
, HFSPlusVolumeHeader
*vhp
,
1300 void *_args
, int embeddedOffset
, int mdb_offset
,
1301 HFSMasterDirectoryBlock
*mdbp
, struct ucred
*cred
)
1303 JournalInfoBlock
*jibp
;
1304 struct buf
*jinfo_bp
, *bp
;
1305 int sectors_per_fsblock
, arg_flags
=0, arg_tbufsz
=0;
1306 int retval
, blksize
= hfsmp
->hfs_phys_block_size
;
1307 struct vnode
*devvp
;
1308 struct hfs_mount_args
*args
= _args
;
1310 devvp
= hfsmp
->hfs_devvp
;
1312 if (args
!= NULL
&& (args
->flags
& HFSFSMNT_EXTENDED_ARGS
)) {
1313 arg_flags
= args
->journal_flags
;
1314 arg_tbufsz
= args
->journal_tbuffer_size
;
1317 sectors_per_fsblock
= SWAP_BE32(vhp
->blockSize
) / blksize
;
1319 retval
= meta_bread(devvp
,
1320 embeddedOffset
/blksize
+
1321 (SWAP_BE32(vhp
->journalInfoBlock
)*sectors_per_fsblock
),
1322 SWAP_BE32(vhp
->blockSize
), cred
, &jinfo_bp
);
1326 jibp
= (JournalInfoBlock
*)jinfo_bp
->b_data
;
1327 jibp
->flags
= SWAP_BE32(jibp
->flags
);
1328 jibp
->offset
= SWAP_BE64(jibp
->offset
);
1329 jibp
->size
= SWAP_BE64(jibp
->size
);
1331 if (jibp
->flags
& kJIJournalInFSMask
) {
1332 hfsmp
->jvp
= hfsmp
->hfs_devvp
;
1334 printf("hfs: journal not stored in fs! don't know what to do.\n");
1339 // save this off for the hack-y check in hfs_remove()
1340 hfsmp
->jnl_start
= jibp
->offset
/ SWAP_BE32(vhp
->blockSize
);
1342 if (jibp
->flags
& kJIJournalNeedInitMask
) {
1343 printf("hfs: Initializing the journal (joffset 0x%llx sz 0x%llx)...\n",
1344 jibp
->offset
+ (off_t
)embeddedOffset
, jibp
->size
);
1345 hfsmp
->jnl
= journal_create(hfsmp
->jvp
,
1346 jibp
->offset
+ (off_t
)embeddedOffset
,
1352 hfs_sync_metadata
, hfsmp
->hfs_mp
);
1354 // no need to start a transaction here... if this were to fail
1355 // we'd just re-init it on the next mount.
1356 jibp
->flags
&= ~kJIJournalNeedInitMask
;
1357 jibp
->flags
= SWAP_BE32(jibp
->flags
);
1362 //printf("hfs: Opening the journal (joffset 0x%llx sz 0x%llx vhp_blksize %d)...\n",
1363 // jibp->offset + (off_t)embeddedOffset,
1364 // jibp->size, SWAP_BE32(vhp->blockSize));
1366 hfsmp
->jnl
= journal_open(hfsmp
->jvp
,
1367 jibp
->offset
+ (off_t
)embeddedOffset
,
1373 hfs_sync_metadata
, hfsmp
->hfs_mp
);
1379 if (hfsmp
->jnl
&& mdbp
) {
1380 // reload the mdb because it could have changed
1381 // if the journal had to be replayed.
1382 retval
= meta_bread(devvp
, mdb_offset
, blksize
, cred
, &bp
);
1385 printf("hfs: failed to reload the mdb after opening the journal (retval %d)!\n",
1389 bcopy(bp
->b_data
+ HFS_PRI_OFFSET(blksize
), mdbp
, 512);
1396 //printf("journal @ 0x%x\n", hfsmp->jnl);
1398 // if we expected the journal to be there and we couldn't
1399 // create it or open it then we have to bail out.
1400 if (hfsmp
->jnl
== NULL
) {
1401 hfsmp
->jnl_start
= 0;
1403 printf("hfs: failed to open/create the journal (retval %d).\n", retval
);
1412 // This function will go and re-locate the .journal_info_block and
1413 // the .journal files in case they moved (which can happen if you
1414 // run Norton SpeedDisk). If we fail to find either file we just
1415 // disable journaling for this volume and return. We turn off the
1416 // journaling bit in the vcb and assume it will get written to disk
1417 // later (if it doesn't on the next mount we'd do the same thing
1418 // again which is harmless). If we disable journaling we don't
1419 // return an error so that the volume is still mountable.
1421 // If the info we find for the .journal_info_block and .journal files
1422 // isn't what we had stored, we re-set our cached info and proceed
1423 // with opening the journal normally.
1426 hfs_late_journal_init(struct hfsmount
*hfsmp
, HFSPlusVolumeHeader
*vhp
, void *_args
)
1428 JournalInfoBlock
*jibp
;
1429 struct buf
*jinfo_bp
, *bp
;
1430 int sectors_per_fsblock
, arg_flags
=0, arg_tbufsz
=0;
1431 int retval
, need_flush
= 0, write_jibp
= 0;
1432 struct vnode
*devvp
;
1433 struct cat_attr jib_attr
, jattr
;
1434 struct cat_fork jib_fork
, jfork
;
1437 struct hfs_mount_args
*args
= _args
;
1439 devvp
= hfsmp
->hfs_devvp
;
1440 vcb
= HFSTOVCB(hfsmp
);
1442 if (args
!= NULL
&& (args
->flags
& HFSFSMNT_EXTENDED_ARGS
)) {
1443 if (args
->journal_disable
) {
1447 arg_flags
= args
->journal_flags
;
1448 arg_tbufsz
= args
->journal_tbuffer_size
;
1451 fid
= GetFileInfo(vcb
, kRootDirID
, ".journal_info_block", &jib_attr
, &jib_fork
);
1452 if (fid
== 0 || jib_fork
.cf_extents
[0].startBlock
== 0 || jib_fork
.cf_size
== 0) {
1453 printf("hfs: can't find the .journal_info_block! disabling journaling (start: %d).\n",
1454 jib_fork
.cf_extents
[0].startBlock
);
1455 vcb
->vcbAtrb
&= ~kHFSVolumeJournaledMask
;
1458 hfsmp
->hfs_jnlinfoblkid
= fid
;
1460 // make sure the journal_info_block begins where we think it should.
1461 if (SWAP_BE32(vhp
->journalInfoBlock
) != jib_fork
.cf_extents
[0].startBlock
) {
1462 printf("hfs: The journal_info_block moved (was: %d; is: %d). Fixing up\n",
1463 SWAP_BE32(vhp
->journalInfoBlock
), jib_fork
.cf_extents
[0].startBlock
);
1465 vcb
->vcbJinfoBlock
= jib_fork
.cf_extents
[0].startBlock
;
1466 vhp
->journalInfoBlock
= SWAP_BE32(jib_fork
.cf_extents
[0].startBlock
);
1470 sectors_per_fsblock
= SWAP_BE32(vhp
->blockSize
) / hfsmp
->hfs_phys_block_size
;
1471 retval
= meta_bread(devvp
,
1472 vcb
->hfsPlusIOPosOffset
/ hfsmp
->hfs_phys_block_size
+
1473 (SWAP_BE32(vhp
->journalInfoBlock
)*sectors_per_fsblock
),
1474 SWAP_BE32(vhp
->blockSize
), NOCRED
, &jinfo_bp
);
1476 printf("hfs: can't read journal info block. disabling journaling.\n");
1477 vcb
->vcbAtrb
&= ~kHFSVolumeJournaledMask
;
1481 jibp
= (JournalInfoBlock
*)jinfo_bp
->b_data
;
1482 jibp
->flags
= SWAP_BE32(jibp
->flags
);
1483 jibp
->offset
= SWAP_BE64(jibp
->offset
);
1484 jibp
->size
= SWAP_BE64(jibp
->size
);
1486 fid
= GetFileInfo(vcb
, kRootDirID
, ".journal", &jattr
, &jfork
);
1487 if (fid
== 0 || jfork
.cf_extents
[0].startBlock
== 0 || jfork
.cf_size
== 0) {
1488 printf("hfs: can't find the journal file! disabling journaling (start: %d)\n",
1489 jfork
.cf_extents
[0].startBlock
);
1491 vcb
->vcbAtrb
&= ~kHFSVolumeJournaledMask
;
1494 hfsmp
->hfs_jnlfileid
= fid
;
1496 // make sure the journal file begins where we think it should.
1497 if ((jibp
->offset
/ (u_int64_t
)vcb
->blockSize
) != jfork
.cf_extents
[0].startBlock
) {
1498 printf("hfs: The journal file moved (was: %lld; is: %d). Fixing up\n",
1499 (jibp
->offset
/ (u_int64_t
)vcb
->blockSize
), jfork
.cf_extents
[0].startBlock
);
1501 jibp
->offset
= (u_int64_t
)jfork
.cf_extents
[0].startBlock
* (u_int64_t
)vcb
->blockSize
;
1505 // check the size of the journal file.
1506 if (jibp
->size
!= (u_int64_t
)jfork
.cf_extents
[0].blockCount
*vcb
->blockSize
) {
1507 printf("hfs: The journal file changed size! (was %lld; is %lld). Fixing up.\n",
1508 jibp
->size
, (u_int64_t
)jfork
.cf_extents
[0].blockCount
*vcb
->blockSize
);
1510 jibp
->size
= (u_int64_t
)jfork
.cf_extents
[0].blockCount
* vcb
->blockSize
;
1514 if (jibp
->flags
& kJIJournalInFSMask
) {
1515 hfsmp
->jvp
= hfsmp
->hfs_devvp
;
1517 printf("hfs: journal not stored in fs! don't know what to do.\n");
1522 // save this off for the hack-y check in hfs_remove()
1523 hfsmp
->jnl_start
= jibp
->offset
/ SWAP_BE32(vhp
->blockSize
);
1525 if (jibp
->flags
& kJIJournalNeedInitMask
) {
1526 printf("hfs: Initializing the journal (joffset 0x%llx sz 0x%llx)...\n",
1527 jibp
->offset
+ (off_t
)vcb
->hfsPlusIOPosOffset
, jibp
->size
);
1528 hfsmp
->jnl
= journal_create(hfsmp
->jvp
,
1529 jibp
->offset
+ (off_t
)vcb
->hfsPlusIOPosOffset
,
1532 hfsmp
->hfs_phys_block_size
,
1535 hfs_sync_metadata
, hfsmp
->hfs_mp
);
1537 // no need to start a transaction here... if this were to fail
1538 // we'd just re-init it on the next mount.
1539 jibp
->flags
&= ~kJIJournalNeedInitMask
;
1544 // if we weren't the last person to mount this volume
1545 // then we need to throw away the journal because it
1546 // is likely that someone else mucked with the disk.
1547 // if the journal is empty this is no big deal. if the
1548 // disk is dirty this prevents us from replaying the
1549 // journal over top of changes that someone else made.
1551 arg_flags
|= JOURNAL_RESET
;
1553 //printf("hfs: Opening the journal (joffset 0x%llx sz 0x%llx vhp_blksize %d)...\n",
1554 // jibp->offset + (off_t)vcb->hfsPlusIOPosOffset,
1555 // jibp->size, SWAP_BE32(vhp->blockSize));
1557 hfsmp
->jnl
= journal_open(hfsmp
->jvp
,
1558 jibp
->offset
+ (off_t
)vcb
->hfsPlusIOPosOffset
,
1561 hfsmp
->hfs_phys_block_size
,
1564 hfs_sync_metadata
, hfsmp
->hfs_mp
);
1569 jibp
->flags
= SWAP_BE32(jibp
->flags
);
1570 jibp
->offset
= SWAP_BE64(jibp
->offset
);
1571 jibp
->size
= SWAP_BE64(jibp
->size
);
1580 //printf("journal @ 0x%x\n", hfsmp->jnl);
1582 // if we expected the journal to be there and we couldn't
1583 // create it or open it then we have to bail out.
1584 if (hfsmp
->jnl
== NULL
) {
1585 hfsmp
->jnl_start
= 0;
1587 printf("hfs: failed to open/create the journal (retval %d).\n", retval
);