]> git.saurik.com Git - apple/xnu.git/blame - bsd/hfs/hfs_vfsutils.c
xnu-517.9.4.tar.gz
[apple/xnu.git] / bsd / hfs / hfs_vfsutils.c
CommitLineData
1c79356b 1/*
55e303ae 2 * Copyright (c) 2000-2003 Apple Computer, Inc. All rights reserved.
1c79356b
A
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
e5568f75
A
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.
1c79356b 11 *
e5568f75
A
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
1c79356b
A
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
e5568f75
A
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
1c79356b
A
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22/* @(#)hfs_vfsutils.c 4.0
23*
9bccf70c 24* (c) 1997-2002 Apple Computer, Inc. All Rights Reserved
1c79356b
A
25*
26* hfs_vfsutils.c -- Routines that go between the HFS layer and the VFS.
27*
1c79356b
A
28*/
29#include <sys/param.h>
30#include <sys/systm.h>
31#include <sys/kernel.h>
32#include <sys/malloc.h>
33#include <sys/stat.h>
1c79356b 34#include <sys/mount.h>
9bccf70c 35#include <sys/namei.h>
1c79356b
A
36#include <sys/lock.h>
37#include <sys/buf.h>
38#include <sys/ubc.h>
39#include <sys/unistd.h>
40
41#include "hfs.h"
9bccf70c 42#include "hfs_catalog.h"
1c79356b
A
43#include "hfs_dbg.h"
44#include "hfs_mount.h"
45#include "hfs_endian.h"
9bccf70c 46#include "hfs_cnode.h"
1c79356b
A
47
48#include "hfscommon/headers/FileMgrInternal.h"
49#include "hfscommon/headers/BTreesInternal.h"
50#include "hfscommon/headers/HFSUnicodeWrappers.h"
51
1c79356b 52
1c79356b 53extern int count_lock_queue __P((void));
1c79356b 54
1c79356b
A
55
56static void ReleaseMetaFileVNode(struct vnode *vp);
b4c24cb9 57static int hfs_late_journal_init(struct hfsmount *hfsmp, HFSPlusVolumeHeader *vhp, void *_args);
1c79356b 58
55e303ae
A
59static void hfs_metadatazone_init(struct hfsmount *);
60static u_int32_t hfs_hotfile_freeblocks(struct hfsmount *);
61
62
63
1c79356b
A
64u_int32_t GetLogicalBlockSize(struct vnode *vp);
65
66/* BTree accessor routines */
67extern OSStatus GetBTreeBlock(FileReference vp, UInt32 blockNum, GetBlockOptions options, BlockDescriptor *block);
68extern OSStatus SetBTreeBlockSize(FileReference vp, ByteCount blockSize, ItemCount minBlockCount);
69extern OSStatus ExtendBTreeFile(FileReference vp, FSSize minEOF, FSSize maxEOF);
70extern OSStatus ReleaseBTreeBlock(FileReference vp, BlockDescPtr blockPtr, ReleaseBlockOptions options);
71
72//*******************************************************************************
73// Note: Finder information in the HFS/HFS+ metadata are considered opaque and
74// hence are not in the right byte order on little endian machines. It is
75// the responsibility of the finder and other clients to swap the data.
76//*******************************************************************************
77
78//*******************************************************************************
79// Routine: hfs_MountHFSVolume
80//
81//
82//*******************************************************************************
9bccf70c
A
83char hfs_catname[] = "Catalog B-tree";
84char hfs_extname[] = "Extents B-tree";
85char hfs_vbmname[] = "Volume Bitmap";
86
87char hfs_privdirname[] =
88 "\xE2\x90\x80\xE2\x90\x80\xE2\x90\x80\xE2\x90\x80HFS+ Private Data";
1c79356b 89
55e303ae 90__private_extern__
1c79356b 91OSErr hfs_MountHFSVolume(struct hfsmount *hfsmp, HFSMasterDirectoryBlock *mdb,
d52fe63f 92 struct proc *p)
1c79356b 93{
9bccf70c
A
94 ExtendedVCB *vcb = HFSTOVCB(hfsmp);
95 int error;
1c79356b 96 ByteCount utf8chars;
9bccf70c
A
97 struct cat_desc cndesc;
98 struct cat_attr cnattr;
99 struct cat_fork fork;
1c79356b 100
9bccf70c
A
101 /* Block size must be a multiple of 512 */
102 if (SWAP_BE32(mdb->drAlBlkSiz) == 0 ||
103 (SWAP_BE32(mdb->drAlBlkSiz) & 0x01FF) != 0)
1c79356b
A
104 return (EINVAL);
105
1c79356b 106 /* don't mount a writeable volume if its dirty, it must be cleaned by fsck_hfs */
55e303ae
A
107 if (((hfsmp->hfs_flags & HFS_READ_ONLY) == 0) &&
108 ((SWAP_BE16(mdb->drAtrb) & kHFSVolumeUnmountedMask) == 0)) {
1c79356b 109 return (EINVAL);
55e303ae
A
110 }
111 hfsmp->hfs_flags |= HFS_STANDARD;
1c79356b
A
112 /*
113 * The MDB seems OK: transfer info from it into VCB
114 * Note - the VCB starts out clear (all zeros)
115 *
116 */
9bccf70c
A
117 vcb->vcbSigWord = SWAP_BE16 (mdb->drSigWord);
118 vcb->vcbCrDate = to_bsd_time(LocalToUTC(SWAP_BE32(mdb->drCrDate)));
0b4e3aa0 119 vcb->localCreateDate = SWAP_BE32 (mdb->drCrDate);
9bccf70c
A
120 vcb->vcbLsMod = to_bsd_time(LocalToUTC(SWAP_BE32(mdb->drLsMod)));
121 vcb->vcbAtrb = SWAP_BE16 (mdb->drAtrb);
122 vcb->vcbNmFls = SWAP_BE16 (mdb->drNmFls);
123 vcb->vcbVBMSt = SWAP_BE16 (mdb->drVBMSt);
124 vcb->nextAllocation = SWAP_BE16 (mdb->drAllocPtr);
125 vcb->totalBlocks = SWAP_BE16 (mdb->drNmAlBlks);
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);
1c79356b 136 bcopy(mdb->drFndrInfo, vcb->vcbFndrInfo, sizeof(vcb->vcbFndrInfo));
55e303ae 137 if ((hfsmp->hfs_flags & HFS_READ_ONLY) == 0)
9bccf70c 138 vcb->vcbWrCnt++; /* Compensate for write of MDB on last flush */
1c79356b
A
139
140 /* convert hfs encoded name into UTF-8 string */
9bccf70c 141 error = hfs_to_utf8(vcb, mdb->drVN, NAME_MAX, &utf8chars, vcb->vcbVN);
1c79356b
A
142 /*
143 * When an HFS name cannot be encoded with the current
144 * volume encoding we use MacRoman as a fallback.
145 */
9bccf70c 146 if (error || (utf8chars == 0))
1c79356b
A
147 (void) mac_roman_to_utf8(mdb->drVN, NAME_MAX, &utf8chars, vcb->vcbVN);
148
9bccf70c 149 hfsmp->hfs_logBlockSize = BestBlockSizeFit(vcb->blockSize, MAXBSIZE, hfsmp->hfs_phys_block_size);
0b4e3aa0 150 vcb->vcbVBMIOSize = kHFSBlockSize;
1c79356b 151
1c79356b
A
152 VCB_LOCK_INIT(vcb);
153
9bccf70c
A
154 bzero(&cndesc, sizeof(cndesc));
155 cndesc.cd_parentcnid = kRootParID;
55e303ae 156 cndesc.cd_flags |= CD_ISMETA;
9bccf70c
A
157 bzero(&cnattr, sizeof(cnattr));
158 cnattr.ca_nlink = 1;
159 cnattr.ca_mode = S_IFREG;
160 bzero(&fork, sizeof(fork));
161
1c79356b 162 /*
9bccf70c
A
163 * Set up Extents B-tree vnode
164 */
165 cndesc.cd_nameptr = hfs_extname;
166 cndesc.cd_namelen = strlen(hfs_extname);
167 cndesc.cd_cnid = cnattr.ca_fileid = kHFSExtentsFileID;
168 fork.cf_size = SWAP_BE32(mdb->drXTFlSize);
169 fork.cf_blocks = fork.cf_size / vcb->blockSize;
170 fork.cf_clump = SWAP_BE32(mdb->drXTClpSiz);
55e303ae 171 fork.cf_vblocks = 0;
9bccf70c
A
172 fork.cf_extents[0].startBlock = SWAP_BE16(mdb->drXTExtRec[0].startBlock);
173 fork.cf_extents[0].blockCount = SWAP_BE16(mdb->drXTExtRec[0].blockCount);
174 fork.cf_extents[1].startBlock = SWAP_BE16(mdb->drXTExtRec[1].startBlock);
175 fork.cf_extents[1].blockCount = SWAP_BE16(mdb->drXTExtRec[1].blockCount);
176 fork.cf_extents[2].startBlock = SWAP_BE16(mdb->drXTExtRec[2].startBlock);
177 fork.cf_extents[2].blockCount = SWAP_BE16(mdb->drXTExtRec[2].blockCount);
178 cnattr.ca_blocks = fork.cf_blocks;
179
180 error = hfs_getnewvnode(hfsmp, NULL, &cndesc, 0, &cnattr, &fork,
181 &vcb->extentsRefNum);
182 if (error) goto MtVolErr;
183 error = MacToVFSError(BTOpenPath(VTOF(vcb->extentsRefNum),
55e303ae 184 (KeyCompareProcPtr)CompareExtentKeys));
9bccf70c
A
185 if (error) {
186 VOP_UNLOCK(vcb->extentsRefNum, 0, p);
187 goto MtVolErr;
188 }
1c79356b
A
189
190 /*
191 * Set up Catalog B-tree vnode...
192 */
9bccf70c
A
193 cndesc.cd_nameptr = hfs_catname;
194 cndesc.cd_namelen = strlen(hfs_catname);
195 cndesc.cd_cnid = cnattr.ca_fileid = kHFSCatalogFileID;
196 fork.cf_size = SWAP_BE32(mdb->drCTFlSize);
197 fork.cf_blocks = fork.cf_size / vcb->blockSize;
198 fork.cf_clump = SWAP_BE32(mdb->drCTClpSiz);
55e303ae 199 fork.cf_vblocks = 0;
9bccf70c
A
200 fork.cf_extents[0].startBlock = SWAP_BE16(mdb->drCTExtRec[0].startBlock);
201 fork.cf_extents[0].blockCount = SWAP_BE16(mdb->drCTExtRec[0].blockCount);
202 fork.cf_extents[1].startBlock = SWAP_BE16(mdb->drCTExtRec[1].startBlock);
203 fork.cf_extents[1].blockCount = SWAP_BE16(mdb->drCTExtRec[1].blockCount);
204 fork.cf_extents[2].startBlock = SWAP_BE16(mdb->drCTExtRec[2].startBlock);
205 fork.cf_extents[2].blockCount = SWAP_BE16(mdb->drCTExtRec[2].blockCount);
206 cnattr.ca_blocks = fork.cf_blocks;
207
208 error = hfs_getnewvnode(hfsmp, NULL, &cndesc, 0, &cnattr, &fork,
209 &vcb->catalogRefNum);
210 if (error) {
211 VOP_UNLOCK(vcb->extentsRefNum, 0, p);
212 goto MtVolErr;
213 }
214 error = MacToVFSError(BTOpenPath(VTOF(vcb->catalogRefNum),
55e303ae 215 (KeyCompareProcPtr)CompareCatalogKeys));
9bccf70c
A
216 if (error) {
217 VOP_UNLOCK(vcb->catalogRefNum, 0, p);
218 VOP_UNLOCK(vcb->extentsRefNum, 0, p);
219 goto MtVolErr;
220 }
1c79356b
A
221
222 /* mark the volume dirty (clear clean unmount bit) */
223 vcb->vcbAtrb &= ~kHFSVolumeUnmountedMask;
224
1c79356b
A
225 /*
226 * all done with b-trees so we can unlock now...
227 */
9bccf70c
A
228 VOP_UNLOCK(vcb->catalogRefNum, 0, p);
229 VOP_UNLOCK(vcb->extentsRefNum, 0, p);
1c79356b 230
9bccf70c 231 if ( error == noErr )
1c79356b
A
232 {
233 if ( !(vcb->vcbAtrb & kHFSVolumeHardwareLockMask) ) // if the disk is not write protected
234 {
235 MarkVCBDirty( vcb ); // mark VCB dirty so it will be written
236 }
237 }
238 goto CmdDone;
239
240 //-- Release any resources allocated so far before exiting with an error:
9bccf70c 241MtVolErr:
1c79356b
A
242 ReleaseMetaFileVNode(vcb->catalogRefNum);
243 ReleaseMetaFileVNode(vcb->extentsRefNum);
244
9bccf70c
A
245CmdDone:
246 return (error);
1c79356b
A
247}
248
249//*******************************************************************************
250// Routine: hfs_MountHFSPlusVolume
251//
252//
253//*******************************************************************************
254
55e303ae 255__private_extern__
1c79356b 256OSErr hfs_MountHFSPlusVolume(struct hfsmount *hfsmp, HFSPlusVolumeHeader *vhp,
b4c24cb9 257 off_t embeddedOffset, u_int64_t disksize, struct proc *p, void *args)
1c79356b 258{
9bccf70c
A
259 register ExtendedVCB *vcb;
260 struct cat_desc cndesc;
261 struct cat_attr cnattr;
55e303ae 262 struct cat_fork cfork;
9bccf70c 263 UInt32 blockSize;
55e303ae
A
264 u_int64_t volumesize;
265 struct BTreeInfoRec btinfo;
266 u_int16_t signature;
267 u_int16_t version;
268 int i;
9bccf70c 269 OSErr retval;
1c79356b 270
55e303ae
A
271 signature = SWAP_BE16(vhp->signature);
272 version = SWAP_BE16(vhp->version);
273
274 if (signature == kHFSPlusSigWord) {
275 if (version != kHFSPlusVersion) {
276 printf("hfs_mount: invalid HFS+ version: %d\n", version);
277 return (EINVAL);
278 }
279 } else if (signature == kHFSXSigWord) {
280 if (version != kHFSXVersion) {
281 printf("hfs_mount: invalid HFSX version: %d\n", version);
282 return (EINVAL);
283 }
284 /* The in-memory signature is always 'H+'. */
285 signature = kHFSPlusSigWord;
286 hfsmp->hfs_flags |= HFS_X;
287 } else {
288 printf("hfs_mount: invalid HFS+ sig 0x%04x\n", signature);
b4c24cb9
A
289 return (EINVAL);
290 }
1c79356b 291
9bccf70c
A
292 /* Block size must be at least 512 and a power of 2 */
293 blockSize = SWAP_BE32(vhp->blockSize);
55e303ae 294 if (blockSize < 512 || !powerof2(blockSize))
9bccf70c 295 return (EINVAL);
1c79356b
A
296
297 /* don't mount a writable volume if its dirty, it must be cleaned by fsck_hfs */
55e303ae
A
298 if ((hfsmp->hfs_flags & HFS_READ_ONLY) == 0 && hfsmp->jnl == NULL &&
299 (SWAP_BE32(vhp->attributes) & kHFSVolumeUnmountedMask) == 0)
1c79356b 300 return (EINVAL);
d52fe63f
A
301
302 /* Make sure we can live with the physical block size. */
303 if ((disksize & (hfsmp->hfs_phys_block_size - 1)) ||
304 (embeddedOffset & (hfsmp->hfs_phys_block_size - 1)) ||
55e303ae 305 (blockSize < hfsmp->hfs_phys_block_size)) {
d52fe63f
A
306 return (ENXIO);
307 }
1c79356b
A
308 /*
309 * The VolumeHeader seems OK: transfer info from it into VCB
310 * Note - the VCB starts out clear (all zeros)
311 */
312 vcb = HFSTOVCB(hfsmp);
313
55e303ae 314 vcb->vcbSigWord = signature;
b4c24cb9 315 vcb->vcbJinfoBlock = SWAP_BE32(vhp->journalInfoBlock);
9bccf70c
A
316 vcb->vcbLsMod = to_bsd_time(SWAP_BE32(vhp->modifyDate));
317 vcb->vcbAtrb = (UInt16)SWAP_BE32(vhp->attributes);
318 vcb->vcbClpSiz = SWAP_BE32(vhp->rsrcClumpSize);
319 vcb->vcbNxtCNID = SWAP_BE32(vhp->nextCatalogID);
320 vcb->vcbVolBkUp = to_bsd_time(SWAP_BE32(vhp->backupDate));
321 vcb->vcbWrCnt = SWAP_BE32(vhp->writeCount);
322 vcb->vcbFilCnt = SWAP_BE32(vhp->fileCount);
323 vcb->vcbDirCnt = SWAP_BE32(vhp->folderCount);
1c79356b
A
324
325 /* copy 32 bytes of Finder info */
326 bcopy(vhp->finderInfo, vcb->vcbFndrInfo, sizeof(vhp->finderInfo));
327
328 vcb->vcbAlBlSt = 0; /* hfs+ allocation blocks start at first block of volume */
55e303ae 329 if ((hfsmp->hfs_flags & HFS_READ_ONLY) == 0)
9bccf70c 330 vcb->vcbWrCnt++; /* compensate for write of Volume Header on last flush */
1c79356b
A
331
332 VCB_LOCK_INIT(vcb);
333
9bccf70c
A
334 /* Now fill in the Extended VCB info */
335 vcb->nextAllocation = SWAP_BE32(vhp->nextAllocation);
336 vcb->totalBlocks = SWAP_BE32(vhp->totalBlocks);
337 vcb->freeBlocks = SWAP_BE32(vhp->freeBlocks);
55e303ae 338 vcb->blockSize = blockSize;
9bccf70c
A
339 vcb->encodingsBitmap = SWAP_BE64(vhp->encodingsBitmap);
340 vcb->localCreateDate = SWAP_BE32(vhp->createDate);
1c79356b 341
9bccf70c 342 vcb->hfsPlusIOPosOffset = embeddedOffset;
1c79356b 343
9bccf70c
A
344 /* Default to no free block reserve */
345 vcb->reserveBlocks = 0;
1c79356b 346
9bccf70c
A
347 /*
348 * Update the logical block size in the mount struct
349 * (currently set up from the wrapper MDB) using the
350 * new blocksize value:
351 */
352 hfsmp->hfs_logBlockSize = BestBlockSizeFit(vcb->blockSize, MAXBSIZE, hfsmp->hfs_phys_block_size);
0b4e3aa0 353 vcb->vcbVBMIOSize = min(vcb->blockSize, MAXPHYSIO);
1c79356b 354
9bccf70c
A
355 bzero(&cndesc, sizeof(cndesc));
356 cndesc.cd_parentcnid = kRootParID;
55e303ae 357 cndesc.cd_flags |= CD_ISMETA;
9bccf70c
A
358 bzero(&cnattr, sizeof(cnattr));
359 cnattr.ca_nlink = 1;
360 cnattr.ca_mode = S_IFREG;
1c79356b
A
361
362 /*
9bccf70c
A
363 * Set up Extents B-tree vnode
364 */
365 cndesc.cd_nameptr = hfs_extname;
366 cndesc.cd_namelen = strlen(hfs_extname);
367 cndesc.cd_cnid = cnattr.ca_fileid = kHFSExtentsFileID;
368
55e303ae
A
369 cfork.cf_size = SWAP_BE64 (vhp->extentsFile.logicalSize);
370 cfork.cf_clump = SWAP_BE32 (vhp->extentsFile.clumpSize);
371 cfork.cf_blocks = SWAP_BE32 (vhp->extentsFile.totalBlocks);
372 cfork.cf_vblocks = 0;
373 cnattr.ca_blocks = cfork.cf_blocks;
374 for (i = 0; i < kHFSPlusExtentDensity; i++) {
375 cfork.cf_extents[i].startBlock =
376 SWAP_BE32 (vhp->extentsFile.extents[i].startBlock);
377 cfork.cf_extents[i].blockCount =
378 SWAP_BE32 (vhp->extentsFile.extents[i].blockCount);
379 }
380 retval = hfs_getnewvnode(hfsmp, NULL, &cndesc, 0, &cnattr, &cfork,
9bccf70c 381 &vcb->extentsRefNum);
9bccf70c 382
1c79356b 383 if (retval) goto ErrorExit;
9bccf70c 384 retval = MacToVFSError(BTOpenPath(VTOF(vcb->extentsRefNum),
55e303ae 385 (KeyCompareProcPtr) CompareExtentKeysPlus));
9bccf70c
A
386 if (retval) {
387 VOP_UNLOCK(vcb->extentsRefNum, 0, p);
388 goto ErrorExit;
389 }
1c79356b
A
390
391 /*
9bccf70c 392 * Set up Catalog B-tree vnode
1c79356b 393 */
9bccf70c
A
394 cndesc.cd_nameptr = hfs_catname;
395 cndesc.cd_namelen = strlen(hfs_catname);
396 cndesc.cd_cnid = cnattr.ca_fileid = kHFSCatalogFileID;
397
55e303ae
A
398 cfork.cf_size = SWAP_BE64 (vhp->catalogFile.logicalSize);
399 cfork.cf_clump = SWAP_BE32 (vhp->catalogFile.clumpSize);
400 cfork.cf_blocks = SWAP_BE32 (vhp->catalogFile.totalBlocks);
401 cfork.cf_vblocks = 0;
402 cnattr.ca_blocks = cfork.cf_blocks;
403 for (i = 0; i < kHFSPlusExtentDensity; i++) {
404 cfork.cf_extents[i].startBlock =
405 SWAP_BE32 (vhp->catalogFile.extents[i].startBlock);
406 cfork.cf_extents[i].blockCount =
407 SWAP_BE32 (vhp->catalogFile.extents[i].blockCount);
408 }
409 retval = hfs_getnewvnode(hfsmp, NULL, &cndesc, 0, &cnattr, &cfork,
9bccf70c 410 &vcb->catalogRefNum);
9bccf70c
A
411 if (retval) {
412 VOP_UNLOCK(vcb->extentsRefNum, 0, p);
413 goto ErrorExit;
414 }
415 retval = MacToVFSError(BTOpenPath(VTOF(vcb->catalogRefNum),
55e303ae 416 (KeyCompareProcPtr) CompareExtendedCatalogKeys));
9bccf70c
A
417 if (retval) {
418 VOP_UNLOCK(vcb->catalogRefNum, 0, p);
419 VOP_UNLOCK(vcb->extentsRefNum, 0, p);
420 goto ErrorExit;
421 }
55e303ae
A
422 if ((hfsmp->hfs_flags & HFS_X) &&
423 BTGetInformation(VTOF(vcb->catalogRefNum), 0, &btinfo) == 0) {
424 if (btinfo.keyCompareType == kHFSBinaryCompare) {
425 hfsmp->hfs_flags |= HFS_CASE_SENSITIVE;
426 /* Install a case-sensitive key compare */
427 (void) BTOpenPath(VTOF(vcb->catalogRefNum),
428 (KeyCompareProcPtr)cat_binarykeycompare);
429 }
430 }
1c79356b
A
431
432 /*
9bccf70c 433 * Set up Allocation file vnode
1c79356b 434 */
9bccf70c
A
435 cndesc.cd_nameptr = hfs_vbmname;
436 cndesc.cd_namelen = strlen(hfs_vbmname);
437 cndesc.cd_cnid = cnattr.ca_fileid = kHFSAllocationFileID;
438
55e303ae
A
439 cfork.cf_size = SWAP_BE64 (vhp->allocationFile.logicalSize);
440 cfork.cf_clump = SWAP_BE32 (vhp->allocationFile.clumpSize);
441 cfork.cf_blocks = SWAP_BE32 (vhp->allocationFile.totalBlocks);
442 cfork.cf_vblocks = 0;
443 cnattr.ca_blocks = cfork.cf_blocks;
444 for (i = 0; i < kHFSPlusExtentDensity; i++) {
445 cfork.cf_extents[i].startBlock =
446 SWAP_BE32 (vhp->allocationFile.extents[i].startBlock);
447 cfork.cf_extents[i].blockCount =
448 SWAP_BE32 (vhp->allocationFile.extents[i].blockCount);
449 }
450 retval = hfs_getnewvnode(hfsmp, NULL, &cndesc, 0, &cnattr, &cfork,
9bccf70c 451 &vcb->allocationsRefNum);
9bccf70c
A
452 if (retval) {
453 VOP_UNLOCK(vcb->catalogRefNum, 0, p);
454 VOP_UNLOCK(vcb->extentsRefNum, 0, p);
455 goto ErrorExit;
456 }
457
458 /* Pick up volume name and create date */
459 retval = cat_idlookup(hfsmp, kHFSRootFolderID, &cndesc, &cnattr, NULL);
460 if (retval) {
461 VOP_UNLOCK(vcb->allocationsRefNum, 0, p);
462 VOP_UNLOCK(vcb->catalogRefNum, 0, p);
463 VOP_UNLOCK(vcb->extentsRefNum, 0, p);
464 goto ErrorExit;
465 }
466 vcb->vcbCrDate = cnattr.ca_itime;
467 vcb->volumeNameEncodingHint = cndesc.cd_encoding;
468 bcopy(cndesc.cd_nameptr, vcb->vcbVN, min(255, cndesc.cd_namelen));
469 cat_releasedesc(&cndesc);
1c79356b
A
470
471 /* mark the volume dirty (clear clean unmount bit) */
472 vcb->vcbAtrb &= ~kHFSVolumeUnmountedMask;
55e303ae 473 if (hfsmp->jnl && (hfsmp->hfs_flags & HFS_READ_ONLY) == 0) {
b4c24cb9
A
474 hfs_flushvolumeheader(hfsmp, TRUE, TRUE);
475 }
1c79356b 476
1c79356b
A
477 /*
478 * all done with metadata files so we can unlock now...
479 */
480 VOP_UNLOCK(vcb->allocationsRefNum, 0, p);
481 VOP_UNLOCK(vcb->catalogRefNum, 0, p);
482 VOP_UNLOCK(vcb->extentsRefNum, 0, p);
483
b4c24cb9
A
484 //
485 // Check if we need to do late journal initialization. This only
486 // happens if a previous version of MacOS X (or 9) touched the disk.
487 // In that case hfs_late_journal_init() will go re-locate the journal
488 // and journal_info_block files and validate that they're still kosher.
489 //
490 if ( (vcb->vcbAtrb & kHFSVolumeJournaledMask)
491 && (SWAP_BE32(vhp->lastMountedVersion) != kHFSJMountVersion)
492 && (hfsmp->jnl == NULL)) {
493
494 retval = hfs_late_journal_init(hfsmp, vhp, args);
495 if (retval != 0) {
496 hfsmp->jnl = NULL;
497 goto ErrorExit;
498 } else if (hfsmp->jnl) {
499 hfsmp->hfs_mp->mnt_flag |= MNT_JOURNALED;
500 }
501 } else if (hfsmp->jnl) {
502 struct cat_attr jinfo_attr, jnl_attr;
503
504 // if we're here we need to fill in the fileid's for the
505 // journal and journal_info_block.
506 hfsmp->hfs_jnlinfoblkid = GetFileInfo(vcb, kRootDirID, ".journal_info_block", &jinfo_attr, NULL);
507 hfsmp->hfs_jnlfileid = GetFileInfo(vcb, kRootDirID, ".journal", &jnl_attr, NULL);
508 if (hfsmp->hfs_jnlinfoblkid == 0 || hfsmp->hfs_jnlfileid == 0) {
509 printf("hfs: danger! couldn't find the file-id's for the journal or journal_info_block\n");
510 printf("hfs: jnlfileid %d, jnlinfoblkid %d\n", hfsmp->hfs_jnlfileid, hfsmp->hfs_jnlinfoblkid);
511 }
512 }
513
55e303ae
A
514 /*
515 * Establish a metadata allocation zone.
516 */
517 hfs_metadatazone_init(hfsmp);
518
519 /*
520 * Make any metadata zone adjustments.
521 */
522 if (hfsmp->hfs_flags & HFS_METADATA_ZONE) {
523 /* Keep the roving allocator out of the metadata zone. */
524 if (vcb->nextAllocation >= hfsmp->hfs_metazone_start &&
525 vcb->nextAllocation <= hfsmp->hfs_metazone_end) {
526 vcb->nextAllocation = hfsmp->hfs_metazone_end + 1;
527 }
528 }
529
530 /* setup private/hidden directory for unlinked files */
531 FindMetaDataDirectory(vcb);
532 if (hfsmp->jnl && ((hfsmp->hfs_flags & HFS_READ_ONLY) == 0))
533 hfs_remove_orphans(hfsmp);
534
535 if ( !(vcb->vcbAtrb & kHFSVolumeHardwareLockMask) ) // if the disk is not write protected
536 {
537 MarkVCBDirty( vcb ); // mark VCB dirty so it will be written
538 }
539
540
541 /*
542 * Allow hot file clustering if conditions allow.
543 */
544 if ((hfsmp->hfs_flags & HFS_METADATA_ZONE) &&
545 ((hfsmp->hfs_flags & HFS_READ_ONLY) == 0)) {
546 (void) hfs_recording_init(hfsmp, p);
547 }
b4c24cb9 548
1c79356b
A
549 return (0);
550
1c79356b
A
551ErrorExit:
552 /*
553 * A fatal error occured and the volume cannot be mounted
554 * release any resources that we aquired...
555 */
556
9bccf70c 557 InvalidateCatalogCache(vcb);
1c79356b
A
558 ReleaseMetaFileVNode(vcb->allocationsRefNum);
559 ReleaseMetaFileVNode(vcb->catalogRefNum);
560 ReleaseMetaFileVNode(vcb->extentsRefNum);
561
562 return (retval);
563}
564
565
566/*
567 * ReleaseMetaFileVNode
568 *
569 * vp L - -
570 */
571static void ReleaseMetaFileVNode(struct vnode *vp)
572{
9bccf70c 573 struct filefork *fp;
1c79356b 574
9bccf70c
A
575 if (vp && (fp = VTOF(vp))) {
576 if (fp->fcbBTCBPtr != NULL)
577 (void) BTClosePath(fp);
1c79356b
A
578
579 /* release the node even if BTClosePath fails */
9bccf70c
A
580 vrele(vp);
581 vgone(vp);
1c79356b 582 }
1c79356b
A
583}
584
585
586/*************************************************************
587*
588* Unmounts a hfs volume.
589* At this point vflush() has been called (to dump all non-metadata files)
590*
591*************************************************************/
592
55e303ae
A
593__private_extern__
594int
595hfsUnmount( register struct hfsmount *hfsmp, struct proc *p)
1c79356b 596{
9bccf70c
A
597 ExtendedVCB *vcb = HFSTOVCB(hfsmp);
598 int retval = E_NONE;
1c79356b 599
1c79356b 600 InvalidateCatalogCache( vcb );
1c79356b 601
55e303ae
A
602 if (hfsmp->hfc_filevp) {
603 ReleaseMetaFileVNode(hfsmp->hfc_filevp);
604 hfsmp->hfc_filevp = NULL;
605 }
606
1c79356b
A
607 if (vcb->vcbSigWord == kHFSPlusSigWord)
608 ReleaseMetaFileVNode(vcb->allocationsRefNum);
609
610 ReleaseMetaFileVNode(vcb->catalogRefNum);
611 ReleaseMetaFileVNode(vcb->extentsRefNum);
612
613 return (retval);
614}
615
616
0b4e3aa0 617/*
55e303ae 618 * Test is fork has overflow extents.
0b4e3aa0 619 */
55e303ae
A
620__private_extern__
621int
622overflow_extents(struct filefork *fp)
1c79356b 623{
9bccf70c 624 u_long blocks;
1c79356b 625
9bccf70c
A
626 if (VTOVCB(FTOV(fp))->vcbSigWord == kHFSPlusSigWord) {
627 if (fp->ff_extents[7].blockCount == 0)
628 return (0);
1c79356b 629
9bccf70c
A
630 blocks = fp->ff_extents[0].blockCount +
631 fp->ff_extents[1].blockCount +
632 fp->ff_extents[2].blockCount +
633 fp->ff_extents[3].blockCount +
634 fp->ff_extents[4].blockCount +
635 fp->ff_extents[5].blockCount +
636 fp->ff_extents[6].blockCount +
637 fp->ff_extents[7].blockCount;
638 } else {
639 if (fp->ff_extents[2].blockCount == 0)
640 return false;
641
642 blocks = fp->ff_extents[0].blockCount +
643 fp->ff_extents[1].blockCount +
644 fp->ff_extents[2].blockCount;
645 }
1c79356b 646
9bccf70c
A
647 return (fp->ff_blocks > blocks);
648}
1c79356b
A
649
650
55e303ae
A
651/*
652 * Lock/Unlock a metadata file.
653 */
654__private_extern__
9bccf70c
A
655int
656hfs_metafilelocking(struct hfsmount *hfsmp, u_long fileID, u_int flags, struct proc *p)
1c79356b 657{
9bccf70c
A
658 ExtendedVCB *vcb;
659 struct vnode *vp = NULL;
660 int numOfLockedBuffs;
661 int retval = 0;
1c79356b 662
9bccf70c
A
663 vcb = HFSTOVCB(hfsmp);
664
665 switch (fileID) {
666 case kHFSExtentsFileID:
667 vp = vcb->extentsRefNum;
668 break;
669
670 case kHFSCatalogFileID:
671 vp = vcb->catalogRefNum;
672 break;
673
674 case kHFSAllocationFileID:
675 /* bitmap is covered by Extents B-tree locking */
676 /* FALL THROUGH */
677 default:
678 panic("hfs_lockmetafile: invalid fileID");
679 }
1c79356b 680
55e303ae
A
681 if ((flags & LK_TYPE_MASK) != LK_RELEASE) {
682 flags |= LK_RETRY;
683 } else if (hfsmp->jnl == NULL) {
9bccf70c
A
684 struct timeval tv = time;
685 u_int32_t lastfsync = tv.tv_sec;
1c79356b 686
9bccf70c
A
687 (void) BTGetLastSync((FCB*)VTOF(vp), &lastfsync);
688
689 numOfLockedBuffs = count_lock_queue();
55e303ae
A
690 if ((numOfLockedBuffs > kMaxLockedMetaBuffers) ||
691 ((numOfLockedBuffs > 1) && ((tv.tv_sec - lastfsync) > kMaxSecsForFsync))) {
9bccf70c
A
692 hfs_btsync(vp, HFS_SYNCTRANS);
693 }
9bccf70c
A
694 }
695
696 retval = lockmgr(&VTOC(vp)->c_lock, flags, &vp->v_interlock, p);
1c79356b 697
9bccf70c
A
698 return (retval);
699}
1c79356b 700
9bccf70c
A
701/*
702 * RequireFileLock
703 *
704 * Check to see if a vnode is locked in the current context
705 * This is to be used for debugging purposes only!!
706 */
707#if HFS_DIAGNOSTIC
708void RequireFileLock(FileReference vp, int shareable)
1c79356b 709{
9bccf70c
A
710 struct lock__bsd__ *lkp;
711 int locked = false;
712 pid_t pid;
713 void * self;
1c79356b 714
9bccf70c 715 pid = current_proc()->p_pid;
55e303ae 716 self = (void *) current_act();
9bccf70c 717 lkp = &VTOC(vp)->c_lock;
1c79356b 718
9bccf70c
A
719 simple_lock(&lkp->lk_interlock);
720
721 if (shareable && (lkp->lk_sharecount > 0) && (lkp->lk_lockholder == LK_NOPROC))
722 locked = true;
723 else if ((lkp->lk_exclusivecount > 0) && (lkp->lk_lockholder == pid) && (lkp->lk_lockthread == self))
724 locked = true;
1c79356b 725
9bccf70c
A
726 simple_unlock(&lkp->lk_interlock);
727
728 if (!locked) {
729 switch (VTOC(vp)->c_fileid) {
730 case 3:
731 DEBUG_BREAK_MSG((" #\n # RequireFileLock: extent btree vnode not locked! v: 0x%08X\n #\n", (u_int)vp));
732 break;
1c79356b 733
9bccf70c
A
734 case 4:
735 DEBUG_BREAK_MSG((" #\n # RequireFileLock: catalog btree vnode not locked! v: 0x%08X\n #\n", (u_int)vp));
736 break;
1c79356b 737
9bccf70c
A
738 default:
739 DEBUG_BREAK_MSG((" #\n # RequireFileLock: file (%d) not locked! v: 0x%08X\n #\n", VTOC(vp)->c_fileid, (u_int)vp));
740 break;
741 }
742 }
1c79356b 743}
9bccf70c 744#endif
1c79356b
A
745
746
9bccf70c
A
747/*
748 * There are three ways to qualify for ownership rights on an object:
749 *
750 * 1. (a) Your UID matches the cnode's UID.
55e303ae 751 * (b) The object in question is owned by "unknown"
9bccf70c
A
752 * 2. (a) Permissions on the filesystem are being ignored and
753 * your UID matches the replacement UID.
754 * (b) Permissions on the filesystem are being ignored and
55e303ae 755 * the replacement UID is "unknown".
9bccf70c
A
756 * 3. You are root.
757 *
758 */
759int
760hfs_owner_rights(struct hfsmount *hfsmp, uid_t cnode_uid, struct ucred *cred,
761 struct proc *p, int invokesuperuserstatus)
762{
763 if ((cred->cr_uid == cnode_uid) || /* [1a] */
55e303ae 764 (cnode_uid == UNKNOWNUID) || /* [1b] */
9bccf70c
A
765 ((HFSTOVFS(hfsmp)->mnt_flag & MNT_UNKNOWNPERMISSIONS) && /* [2] */
766 ((cred->cr_uid == hfsmp->hfs_uid) || /* [2a] */
55e303ae 767 (hfsmp->hfs_uid == UNKNOWNUID))) || /* [2b] */
9bccf70c
A
768 (invokesuperuserstatus && (suser(cred, &p->p_acflag) == 0))) { /* [3] */
769 return (0);
770 } else {
771 return (EPERM);
772 }
1c79356b
A
773}
774
775
9bccf70c
A
776unsigned long BestBlockSizeFit(unsigned long allocationBlockSize,
777 unsigned long blockSizeLimit,
778 unsigned long baseMultiple) {
779 /*
780 Compute the optimal (largest) block size (no larger than allocationBlockSize) that is less than the
781 specified limit but still an even multiple of the baseMultiple.
782 */
783 int baseBlockCount, blockCount;
784 unsigned long trialBlockSize;
1c79356b 785
9bccf70c
A
786 if (allocationBlockSize % baseMultiple != 0) {
787 /*
788 Whoops: the allocation blocks aren't even multiples of the specified base:
789 no amount of dividing them into even parts will be a multiple, either then!
790 */
791 return 512; /* Hope for the best */
792 };
1c79356b 793
9bccf70c
A
794 /* Try the obvious winner first, to prevent 12K allocation blocks, for instance,
795 from being handled as two 6K logical blocks instead of 3 4K logical blocks.
796 Even though the former (the result of the loop below) is the larger allocation
797 block size, the latter is more efficient: */
798 if (allocationBlockSize % PAGE_SIZE == 0) return PAGE_SIZE;
1c79356b 799
9bccf70c
A
800 /* No clear winner exists: pick the largest even fraction <= MAXBSIZE: */
801 baseBlockCount = allocationBlockSize / baseMultiple; /* Now guaranteed to be an even multiple */
1c79356b 802
9bccf70c
A
803 for (blockCount = baseBlockCount; blockCount > 0; --blockCount) {
804 trialBlockSize = blockCount * baseMultiple;
805 if (allocationBlockSize % trialBlockSize == 0) { /* An even multiple? */
806 if ((trialBlockSize <= blockSizeLimit) &&
807 (trialBlockSize % baseMultiple == 0)) {
808 return trialBlockSize;
809 };
810 };
811 };
1c79356b 812
9bccf70c
A
813 /* Note: we should never get here, since blockCount = 1 should always work,
814 but this is nice and safe and makes the compiler happy, too ... */
815 return 512;
816}
1c79356b 817
1c79356b 818
9bccf70c
A
819/*
820 * To make the HFS Plus filesystem follow UFS unlink semantics, a remove
821 * of an active vnode is translated to a move/rename so the file appears
822 * deleted. The destination folder for these move/renames is setup here
55e303ae 823 * and a reference to it is place in hfsmp->hfs_privdir_desc.
9bccf70c 824 */
55e303ae 825__private_extern__
9bccf70c
A
826u_long
827FindMetaDataDirectory(ExtendedVCB *vcb)
1c79356b 828{
9bccf70c
A
829 struct hfsmount * hfsmp;
830 struct vnode * dvp = NULL;
831 struct cnode * dcp = NULL;
832 struct FndrDirInfo * fndrinfo;
833 struct cat_desc out_desc = {0};
55e303ae 834 struct proc *p = current_proc();
9bccf70c 835 struct timeval tv;
55e303ae 836 cat_cookie_t cookie;
9bccf70c
A
837 int error;
838
839 if (vcb->vcbSigWord != kHFSPlusSigWord)
7b1edb79 840 return (0);
7b1edb79 841
9bccf70c 842 hfsmp = VCBTOHFS(vcb);
7b1edb79 843
9bccf70c
A
844 if (hfsmp->hfs_privdir_desc.cd_parentcnid == 0) {
845 hfsmp->hfs_privdir_desc.cd_parentcnid = kRootDirID;
846 hfsmp->hfs_privdir_desc.cd_nameptr = hfs_privdirname;
847 hfsmp->hfs_privdir_desc.cd_namelen = strlen(hfs_privdirname);
848 hfsmp->hfs_privdir_desc.cd_flags = CD_ISDIR;
7b1edb79
A
849 }
850
9bccf70c 851 /* Lock catalog b-tree */
55e303ae 852 if (hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_SHARED, p) != 0)
9bccf70c 853 return (0);
1c79356b 854
9bccf70c
A
855 error = cat_lookup(hfsmp, &hfsmp->hfs_privdir_desc, 0, NULL,
856 &hfsmp->hfs_privdir_attr, NULL);
857
55e303ae
A
858 /* Unlock catalog b-tree */
859 (void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, p);
860
9bccf70c 861 if (error == 0) {
9bccf70c 862 hfsmp->hfs_metadata_createdate = hfsmp->hfs_privdir_attr.ca_itime;
55e303ae
A
863 hfsmp->hfs_privdir_desc.cd_cnid = hfsmp->hfs_privdir_attr.ca_fileid;
864 /*
865 * Clear the system immutable flag if set...
866 */
867 if ((hfsmp->hfs_privdir_attr.ca_flags & SF_IMMUTABLE) &&
868 (hfsmp->hfs_flags & HFS_READ_ONLY) == 0) {
869 hfsmp->hfs_privdir_attr.ca_flags &= ~SF_IMMUTABLE;
870
871 hfs_global_shared_lock_acquire(hfsmp);
872 if (hfsmp->jnl) {
873 if ((error = journal_start_transaction(hfsmp->jnl)) != 0) {
874 hfs_global_shared_lock_release(hfsmp);
875 return (hfsmp->hfs_privdir_attr.ca_fileid);
876 }
877 }
878 if (hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_SHARED, p) == 0) {
879 (void)cat_update(hfsmp, &hfsmp->hfs_privdir_desc,
880 &hfsmp->hfs_privdir_attr, NULL, NULL);
881 (void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, p);
882 }
883 if (hfsmp->jnl) {
884 journal_end_transaction(hfsmp->jnl);
885 }
886 hfs_global_shared_lock_release(hfsmp);
887 }
9bccf70c 888 return (hfsmp->hfs_privdir_attr.ca_fileid);
55e303ae
A
889
890 } else if (hfsmp->hfs_flags & HFS_READ_ONLY) {
891
9bccf70c 892 return (0);
1c79356b 893 }
9bccf70c
A
894
895 /* Setup the default attributes */
896 bzero(&hfsmp->hfs_privdir_attr, sizeof(struct cat_attr));
897 hfsmp->hfs_privdir_attr.ca_mode = S_IFDIR;
9bccf70c
A
898 hfsmp->hfs_privdir_attr.ca_nlink = 2;
899 hfsmp->hfs_privdir_attr.ca_itime = vcb->vcbCrDate;
900 hfsmp->hfs_privdir_attr.ca_mtime = time.tv_sec;
901
902 /* hidden and off the desktop view */
903 fndrinfo = (struct FndrDirInfo *)&hfsmp->hfs_privdir_attr.ca_finderinfo;
904 fndrinfo->frLocation.v = SWAP_BE16 (22460);
905 fndrinfo->frLocation.h = SWAP_BE16 (22460);
906 fndrinfo->frFlags |= SWAP_BE16 (kIsInvisible + kNameLocked);
907
b4c24cb9
A
908 // XXXdbg
909 hfs_global_shared_lock_acquire(hfsmp);
910 if (hfsmp->jnl) {
911 if ((error = journal_start_transaction(hfsmp->jnl)) != 0) {
912 hfs_global_shared_lock_release(hfsmp);
913 return (0);
914 }
915 }
55e303ae
A
916 /* Reserve some space in the Catalog file. */
917 if (cat_preflight(hfsmp, CAT_CREATE, &cookie, p) != 0) {
918 if (hfsmp->jnl) {
919 journal_end_transaction(hfsmp->jnl);
920 }
921 hfs_global_shared_lock_release(hfsmp);
922 return (0);
923 }
b4c24cb9 924
55e303ae
A
925 if (hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_EXCLUSIVE, p) == 0) {
926 error = cat_create(hfsmp, &hfsmp->hfs_privdir_desc,
927 &hfsmp->hfs_privdir_attr, &out_desc);
9bccf70c 928
55e303ae
A
929 (void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, p);
930 }
931
932 cat_postflight(hfsmp, &cookie, p);
933
b4c24cb9
A
934 if (error) {
935 if (hfsmp->jnl) {
936 journal_end_transaction(hfsmp->jnl);
937 }
938 hfs_global_shared_lock_release(hfsmp);
939
940 return (0);
941 }
1c79356b 942
9bccf70c
A
943 hfsmp->hfs_privdir_desc.cd_hint = out_desc.cd_hint;
944 hfsmp->hfs_privdir_desc.cd_cnid = out_desc.cd_cnid;
945 hfsmp->hfs_privdir_attr.ca_fileid = out_desc.cd_cnid;
946 hfsmp->hfs_metadata_createdate = vcb->vcbCrDate;
947
948 if (VFS_ROOT(HFSTOVFS(hfsmp), &dvp) == 0) {
949 dcp = VTOC(dvp);
950 dcp->c_childhint = out_desc.cd_hint;
951 dcp->c_nlink++;
952 dcp->c_entries++;
953 dcp->c_flag |= C_CHANGE | C_UPDATE;
954 tv = time;
955 (void) VOP_UPDATE(dvp, &tv, &tv, 0);
956 vput(dvp);
1c79356b 957 }
9bccf70c 958 hfs_volupdate(hfsmp, VOL_MKDIR, 1);
b4c24cb9
A
959 if (hfsmp->jnl) {
960 journal_end_transaction(hfsmp->jnl);
961 }
962 hfs_global_shared_lock_release(hfsmp);
963
9bccf70c 964 cat_releasedesc(&out_desc);
1c79356b 965
9bccf70c 966 return (out_desc.cd_cnid);
1c79356b
A
967}
968
b4c24cb9
A
969__private_extern__
970u_long
971GetFileInfo(ExtendedVCB *vcb, u_int32_t dirid, char *name,
972 struct cat_attr *fattr, struct cat_fork *forkinfo)
973{
974 struct hfsmount * hfsmp;
975 struct vnode * dvp = NULL;
976 struct cnode * dcp = NULL;
977 struct FndrDirInfo * fndrinfo;
978 struct cat_desc jdesc;
979 struct timeval tv;
980 int error;
981
982 if (vcb->vcbSigWord != kHFSPlusSigWord)
983 return (0);
984
985 hfsmp = VCBTOHFS(vcb);
986
987 memset(&jdesc, 0, sizeof(struct cat_desc));
988 jdesc.cd_parentcnid = kRootDirID;
989 jdesc.cd_nameptr = name;
990 jdesc.cd_namelen = strlen(name);
991
992 /* Lock catalog b-tree */
993 error = hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_EXCLUSIVE, current_proc());
994 if (error)
995 return (0);
996
997 error = cat_lookup(hfsmp, &jdesc, 0, NULL, fattr, forkinfo);
998
999 (void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, current_proc());
1000
1001 if (error == 0) {
1002 return (fattr->ca_fileid);
55e303ae 1003 } else if (hfsmp->hfs_flags & HFS_READ_ONLY) {
b4c24cb9
A
1004 return (0);
1005 }
1006}
1007
1008
1009/*
1010 * On Journaled HFS, there can be orphaned files. These
1011 * are files that were unlinked while busy. If the volume
1012 * was not cleanly unmounted then some of these files may
1013 * have persisted and need to be removed.
1014 */
1015__private_extern__
1016void
1017hfs_remove_orphans(struct hfsmount * hfsmp)
1018{
1019 struct BTreeIterator * iterator = NULL;
1020 struct FSBufferDescriptor btdata;
1021 struct HFSPlusCatalogFile filerec;
1022 struct HFSPlusCatalogKey * keyp;
55e303ae 1023 struct proc *p = current_proc();
b4c24cb9
A
1024 FCB *fcb;
1025 ExtendedVCB *vcb;
1026 char filename[32];
1027 char tempname[32];
1028 size_t namelen;
55e303ae 1029 cat_cookie_t cookie = {0};
b4c24cb9 1030 int catlock = 0;
55e303ae
A
1031 int catreserve = 0;
1032 int started_tr = 0;
1033 int shared_lock = 0;
1034 int result;
b4c24cb9 1035
55e303ae 1036 if (hfsmp->hfs_flags & HFS_CLEANED_ORPHANS)
b4c24cb9
A
1037 return;
1038
1039 vcb = HFSTOVCB(hfsmp);
1040 fcb = VTOF(vcb->catalogRefNum);
1041
1042 btdata.bufferAddress = &filerec;
1043 btdata.itemSize = sizeof(filerec);
1044 btdata.itemCount = 1;
1045
1046 MALLOC(iterator, struct BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
1047 bzero(iterator, sizeof(*iterator));
1048 keyp = (HFSPlusCatalogKey*)&iterator->key;
55e303ae 1049 keyp->parentID = hfsmp->hfs_privdir_desc.cd_cnid;
b4c24cb9 1050
55e303ae 1051 result = hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_EXCLUSIVE, p);
b4c24cb9
A
1052 if (result)
1053 goto exit;
b4c24cb9
A
1054 /*
1055 * Position the iterator at the folder thread record.
1056 * (i.e. one record before first child)
1057 */
1058 result = BTSearchRecord(fcb, iterator, NULL, NULL, iterator);
55e303ae
A
1059
1060 (void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, p);
b4c24cb9
A
1061 if (result)
1062 goto exit;
1063
1064 /* Visit all the children in the HFS+ private directory. */
1065 for (;;) {
55e303ae
A
1066 result = hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_EXCLUSIVE, p);
1067 if (result)
1068 goto exit;
1069
b4c24cb9 1070 result = BTIterateRecord(fcb, kBTreeNextRecord, iterator, &btdata, NULL);
55e303ae
A
1071
1072 (void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, p);
b4c24cb9
A
1073 if (result)
1074 break;
55e303ae
A
1075
1076 if (keyp->parentID != hfsmp->hfs_privdir_desc.cd_cnid)
b4c24cb9
A
1077 break;
1078 if (filerec.recordType != kHFSPlusFileRecord)
1079 continue;
1080
1081 (void) utf8_encodestr(keyp->nodeName.unicode, keyp->nodeName.length * 2,
1082 filename, &namelen, sizeof(filename), 0, 0);
1083
1084 (void) sprintf(tempname, "%s%d", HFS_DELETE_PREFIX, filerec.fileID);
1085
1086 /*
1087 * Delete all files named "tempxxx", where
1088 * xxx is the file's cnid in decimal.
1089 *
b4c24cb9
A
1090 */
1091 if (bcmp(tempname, filename, namelen) == 0) {
55e303ae
A
1092 struct filefork dfork = {0};
1093 struct filefork rfork = {0};
1094 struct cnode cnode = {0};
1095
1096 // XXXdbg
1097 hfs_global_shared_lock_acquire(hfsmp);
1098 shared_lock = 1;
1099 if (hfsmp->jnl) {
1100 if (journal_start_transaction(hfsmp->jnl) != 0) {
1101 goto exit;
1102 }
1103 started_tr = 1;
1104 }
1105
1106 /*
1107 * Reserve some space in the Catalog file.
1108 */
1109 if (cat_preflight(hfsmp, CAT_DELETE, &cookie, p) != 0) {
1110 goto exit;
1111 }
1112 catreserve = 1;
b4c24cb9 1113
55e303ae
A
1114 /* Lock catalog b-tree */
1115 if (hfs_metafilelocking(hfsmp, kHFSCatalogFileID,
1116 LK_EXCLUSIVE, p) != 0) {
1117 goto exit;
1118 }
1119 catlock = 1;
b4c24cb9
A
1120
1121 /* Build a fake cnode */
55e303ae
A
1122 cat_convertattr(hfsmp, (CatalogRecord *)&filerec, &cnode.c_attr,
1123 &dfork.ff_data, &rfork.ff_data);
1124 cnode.c_desc.cd_parentcnid = hfsmp->hfs_privdir_desc.cd_cnid;
b4c24cb9
A
1125 cnode.c_desc.cd_nameptr = filename;
1126 cnode.c_desc.cd_namelen = namelen;
55e303ae
A
1127 cnode.c_desc.cd_cnid = cnode.c_attr.ca_fileid;
1128 cnode.c_blocks = dfork.ff_blocks + rfork.ff_blocks;
b4c24cb9
A
1129
1130 /* Position iterator at previous entry */
1131 if (BTIterateRecord(fcb, kBTreePrevRecord, iterator,
55e303ae 1132 NULL, NULL) != 0) {
b4c24cb9 1133 break;
55e303ae
A
1134 }
1135
b4c24cb9 1136 /* Truncate the file to zero (both forks) */
55e303ae
A
1137 if (dfork.ff_blocks > 0) {
1138 u_int64_t fsize;
1139
1140 dfork.ff_cp = &cnode;
1141 cnode.c_datafork = &dfork;
1142 cnode.c_rsrcfork = NULL;
1143 fsize = (u_int64_t)dfork.ff_blocks * (u_int64_t)HFSTOVCB(hfsmp)->blockSize;
1144 while (fsize > 0) {
1145 if (fsize > HFS_BIGFILE_SIZE) {
1146 fsize -= HFS_BIGFILE_SIZE;
1147 } else {
1148 fsize = 0;
1149 }
1150
1151 if (TruncateFileC(vcb, (FCB*)&dfork, fsize, false) != 0) {
1152 printf("error truncting data fork!\n");
1153 break;
1154 }
1155
1156 //
1157 // if we're iteratively truncating this file down,
1158 // then end the transaction and start a new one so
1159 // that no one transaction gets too big.
1160 //
1161 if (fsize > 0 && started_tr) {
1162 journal_end_transaction(hfsmp->jnl);
1163 if (journal_start_transaction(hfsmp->jnl) != 0) {
1164 started_tr = 0;
1165 break;
1166 }
1167 }
b4c24cb9
A
1168 }
1169 }
55e303ae
A
1170
1171 if (rfork.ff_blocks > 0) {
1172 rfork.ff_cp = &cnode;
b4c24cb9 1173 cnode.c_datafork = NULL;
55e303ae
A
1174 cnode.c_rsrcfork = &rfork;
1175 if (TruncateFileC(vcb, (FCB*)&rfork, 0, false) != 0) {
b4c24cb9
A
1176 printf("error truncting rsrc fork!\n");
1177 break;
1178 }
1179 }
1180
1181 /* Remove the file record from the Catalog */
1182 if (cat_delete(hfsmp, &cnode.c_desc, &cnode.c_attr) != 0) {
1183 printf("error deleting cat rec!\n");
1184 break;
1185 }
1186
1187 /* Update parent and volume counts */
1188 hfsmp->hfs_privdir_attr.ca_entries--;
1189 (void)cat_update(hfsmp, &hfsmp->hfs_privdir_desc,
1190 &hfsmp->hfs_privdir_attr, NULL, NULL);
1191 hfs_volupdate(hfsmp, VOL_RMFILE, 0);
55e303ae
A
1192
1193 /* Drop locks and end the transaction */
1194 (void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, p);
1195 cat_postflight(hfsmp, &cookie, p);
1196 catlock = catreserve = 0;
1197 if (started_tr) {
1198 journal_end_transaction(hfsmp->jnl);
1199 started_tr = 0;
1200 }
1201 hfs_global_shared_lock_release(hfsmp);
1202 shared_lock = 0;
1203
1204 } /* end if */
1205 } /* end for */
b4c24cb9
A
1206
1207exit:
55e303ae
A
1208 if (catlock) {
1209 (void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, p);
1210 }
1211 if (catreserve) {
1212 cat_postflight(hfsmp, &cookie, p);
1213 }
b4c24cb9
A
1214 if (started_tr) {
1215 journal_end_transaction(hfsmp->jnl);
1216 }
55e303ae
A
1217 if (shared_lock) {
1218 hfs_global_shared_lock_release(hfsmp);
1219 }
b4c24cb9
A
1220
1221 FREE(iterator, M_TEMP);
55e303ae 1222 hfsmp->hfs_flags |= HFS_CLEANED_ORPHANS;
b4c24cb9
A
1223}
1224
9bccf70c
A
1225
1226/*
1227 * This will return the correct logical block size for a given vnode.
1228 * For most files, it is the allocation block size, for meta data like
1229 * BTrees, this is kept as part of the BTree private nodeSize
1230 */
1231u_int32_t
1232GetLogicalBlockSize(struct vnode *vp)
1c79356b 1233{
9bccf70c 1234u_int32_t logBlockSize;
1c79356b 1235
9bccf70c 1236 DBG_ASSERT(vp != NULL);
1c79356b 1237
9bccf70c
A
1238 /* start with default */
1239 logBlockSize = VTOHFS(vp)->hfs_logBlockSize;
1c79356b 1240
0b4e3aa0 1241 if (vp->v_flag & VSYSTEM) {
9bccf70c 1242 if (VTOF(vp)->fcbBTCBPtr != NULL) {
0b4e3aa0
A
1243 BTreeInfoRec bTreeInfo;
1244
1245 /*
1246 * We do not lock the BTrees, because if we are getting block..then the tree
1247 * should be locked in the first place.
1248 * We just want the nodeSize wich will NEVER change..so even if the world
1249 * is changing..the nodeSize should remain the same. Which argues why lock
1250 * it in the first place??
1251 */
1252
9bccf70c 1253 (void) BTGetInformation (VTOF(vp), kBTreeInfoVersion, &bTreeInfo);
0b4e3aa0
A
1254
1255 logBlockSize = bTreeInfo.nodeSize;
1256
9bccf70c 1257 } else if (VTOC(vp)->c_fileid == kHFSAllocationFileID) {
0b4e3aa0 1258 logBlockSize = VTOVCB(vp)->vcbVBMIOSize;
1c79356b 1259 }
0b4e3aa0
A
1260 }
1261
1c79356b
A
1262 DBG_ASSERT(logBlockSize > 0);
1263
1264 return logBlockSize;
1265}
1266
9bccf70c
A
1267__private_extern__
1268u_int32_t
1269hfs_freeblks(struct hfsmount * hfsmp, int wantreserve)
1270{
1271 struct vcb_t *vcb = HFSTOVCB(hfsmp);
1272 u_int32_t freeblks;
1273
1274 freeblks = vcb->freeBlocks;
1275 if (wantreserve) {
1276 if (freeblks > vcb->reserveBlocks)
1277 freeblks -= vcb->reserveBlocks;
1278 else
1279 freeblks = 0;
1280 }
55e303ae
A
1281 if (freeblks > vcb->loanedBlocks)
1282 freeblks -= vcb->loanedBlocks;
1283 else
1284 freeblks = 0;
1285
1286#ifdef HFS_SPARSE_DEV
1287 /*
1288 * When the underlying device is sparse, check the
1289 * available space on the backing store volume.
1290 */
1291 if ((hfsmp->hfs_flags & HFS_HAS_SPARSE_DEVICE) && hfsmp->hfs_backingfs_rootvp) {
1292 struct statfs statbuf; /* 272 bytes */
1293 u_int32_t vfreeblks;
1294 u_int32_t loanedblks;
1295 struct mount * backingfs_mp;
1296
1297 backingfs_mp = hfsmp->hfs_backingfs_rootvp->v_mount;
1298
1299 if (VFS_STATFS(backingfs_mp, &statbuf, current_proc()) == 0) {
1300 vfreeblks = statbuf.f_bavail;
1301 /* Normalize block count if needed. */
1302 if (statbuf.f_bsize != vcb->blockSize) {
1303 vfreeblks = ((u_int64_t)vfreeblks * (u_int64_t)statbuf.f_bsize) / vcb->blockSize;
1304 }
1305 if (vfreeblks > hfsmp->hfs_sparsebandblks)
1306 vfreeblks -= hfsmp->hfs_sparsebandblks;
1307 else
1308 vfreeblks = 0;
1309
1310 /* Take into account any delayed allocations. */
1311 loanedblks = 2 * vcb->loanedBlocks;
1312 if (vfreeblks > loanedblks)
1313 vfreeblks -= loanedblks;
1314 else
1315 vfreeblks = 0;
1316
1317 freeblks = MIN(vfreeblks, freeblks);
1318 }
1319 }
1320#endif /* HFS_SPARSE_DEV */
9bccf70c 1321
9bccf70c
A
1322 return (freeblks);
1323}
1324
1c79356b
A
1325/*
1326 * Map HFS Common errors (negative) to BSD error codes (positive).
1327 * Positive errors (ie BSD errors) are passed through unchanged.
1328 */
1329short MacToVFSError(OSErr err)
1330{
9bccf70c
A
1331 if (err >= 0)
1332 return err;
1c79356b 1333
1c79356b 1334 switch (err) {
9bccf70c 1335 case dskFulErr: /* -34 */
55e303ae 1336 case btNoSpaceAvail: /* -32733 */
b4c24cb9 1337 return ENOSPC;
9bccf70c 1338 case fxOvFlErr: /* -32750 */
b4c24cb9 1339 return EOVERFLOW;
9bccf70c
A
1340
1341 case btBadNode: /* -32731 */
b4c24cb9 1342 return EBADF;
9bccf70c
A
1343
1344 case memFullErr: /* -108 */
1345 return ENOMEM; /* +12 */
1346
1347 case cmExists: /* -32718 */
1348 case btExists: /* -32734 */
1349 return EEXIST; /* +17 */
1350
1351 case cmNotFound: /* -32719 */
1352 case btNotFound: /* -32735 */
1353 return ENOENT; /* 28 */
1354
1355 case cmNotEmpty: /* -32717 */
1356 return ENOTEMPTY; /* 66 */
1357
1358 case cmFThdDirErr: /* -32714 */
1359 return EISDIR; /* 21 */
1360
1361 case fxRangeErr: /* -32751 */
b4c24cb9 1362 return ERANGE;
9bccf70c
A
1363
1364 case bdNamErr: /* -37 */
1365 return ENAMETOOLONG; /* 63 */
1366
1367 case paramErr: /* -50 */
1368 case fileBoundsErr: /* -1309 */
1369 return EINVAL; /* +22 */
1370
1371 case fsBTBadNodeSize:
d52fe63f 1372 return ENXIO;
9bccf70c
A
1373
1374 default:
1375 return EIO; /* +5 */
1c79356b
A
1376 }
1377}
1378
1379
1380/*
9bccf70c
A
1381 * Get the directory entry name hint for a given index.
1382 * The directory cnode (dcp) must be locked.
1c79356b 1383 */
9bccf70c
A
1384__private_extern__
1385char *
1386hfs_getnamehint(struct cnode *dcp, int index)
1c79356b 1387{
9bccf70c 1388 struct hfs_index *entry;
1c79356b 1389
9bccf70c 1390 if (index > 0) {
9bccf70c 1391 SLIST_FOREACH(entry, &dcp->c_indexlist, hi_link) {
ccc36f2f 1392 if (entry->hi_index == index)
9bccf70c
A
1393 return (entry->hi_name);
1394 }
1395 }
1c79356b 1396
9bccf70c 1397 return (NULL);
1c79356b
A
1398}
1399
9bccf70c
A
1400/*
1401 * Save a directory entry name hint for a given index.
1402 * The directory cnode (dcp) must be locked.
1403 */
1404__private_extern__
1405void
1406hfs_savenamehint(struct cnode *dcp, int index, const char * namehint)
1407{
1408 struct hfs_index *entry;
1409 int len;
1410
1411 if (index > 0) {
1412 len = strlen(namehint);
1413 MALLOC(entry, struct hfs_index *, len + sizeof(struct hfs_index),
1414 M_TEMP, M_WAITOK);
1415 entry->hi_index = index;
9bccf70c
A
1416 bcopy(namehint, entry->hi_name, len + 1);
1417 SLIST_INSERT_HEAD(&dcp->c_indexlist, entry, hi_link);
1418 }
1c79356b
A
1419}
1420
9bccf70c
A
1421/*
1422 * Release the directory entry name hint for a given index.
1423 * The directory cnode (dcp) must be locked.
1424 */
1425__private_extern__
1426void
1427hfs_relnamehint(struct cnode *dcp, int index)
1428{
1429 struct hfs_index *entry;
9bccf70c
A
1430
1431 if (index > 0) {
9bccf70c 1432 SLIST_FOREACH(entry, &dcp->c_indexlist, hi_link) {
ccc36f2f 1433 if (entry->hi_index == index) {
9bccf70c
A
1434 SLIST_REMOVE(&dcp->c_indexlist, entry, hfs_index,
1435 hi_link);
1436 FREE(entry, M_TEMP);
1437 break;
1438 }
1439 }
1c79356b 1440 }
1c79356b
A
1441}
1442
9bccf70c
A
1443/*
1444 * Release all directory entry name hints.
1445 */
1446__private_extern__
1447void
1448hfs_relnamehints(struct cnode *dcp)
1449{
1450 struct hfs_index *entry;
1451 struct hfs_index *next;
1c79356b 1452
9bccf70c
A
1453 if (!SLIST_EMPTY(&dcp->c_indexlist)) {
1454 for(entry = SLIST_FIRST(&dcp->c_indexlist);
1455 entry != NULL;
1456 entry = next) {
1457 next = SLIST_NEXT(entry, hi_link);
1458 SLIST_REMOVE(&dcp->c_indexlist, entry, hfs_index, hi_link);
1459 FREE(entry, M_TEMP);
1c79356b 1460 }
1c79356b 1461 }
9bccf70c 1462}
1c79356b 1463
1c79356b 1464
d7e50217
A
1465/*
1466 * Perform a case-insensitive compare of two UTF-8 filenames.
1467 *
1468 * Returns 0 if the strings match.
1469 */
1470__private_extern__
1471int
1472hfs_namecmp(const char *str1, size_t len1, const char *str2, size_t len2)
1473{
1474 u_int16_t *ustr1, *ustr2;
1475 size_t ulen1, ulen2;
1476 size_t maxbytes;
1477 int cmp = -1;
1478
1479 if (len1 != len2)
1480 return (cmp);
1481
1482 maxbytes = kHFSPlusMaxFileNameChars << 1;
1483 MALLOC(ustr1, u_int16_t *, maxbytes << 1, M_TEMP, M_WAITOK);
1484 ustr2 = ustr1 + (maxbytes >> 1);
1485
1486 if (utf8_decodestr(str1, len1, ustr1, &ulen1, maxbytes, ':', 0) != 0)
1487 goto out;
1488 if (utf8_decodestr(str2, len2, ustr2, &ulen2, maxbytes, ':', 0) != 0)
1489 goto out;
1490
1491 cmp = FastUnicodeCompare(ustr1, ulen1>>1, ustr2, ulen2>>1);
1492out:
1493 FREE(ustr1, M_TEMP);
1494 return (cmp);
1495}
1496
1497
b4c24cb9
A
1498__private_extern__
1499int
1500hfs_early_journal_init(struct hfsmount *hfsmp, HFSPlusVolumeHeader *vhp,
1501 void *_args, int embeddedOffset, int mdb_offset,
1502 HFSMasterDirectoryBlock *mdbp, struct ucred *cred)
1503{
1504 JournalInfoBlock *jibp;
1505 struct buf *jinfo_bp, *bp;
1506 int sectors_per_fsblock, arg_flags=0, arg_tbufsz=0;
1507 int retval, blksize = hfsmp->hfs_phys_block_size;
1508 struct vnode *devvp;
1509 struct hfs_mount_args *args = _args;
1510
1511 devvp = hfsmp->hfs_devvp;
1512
1513 if (args != NULL && (args->flags & HFSFSMNT_EXTENDED_ARGS)) {
1514 arg_flags = args->journal_flags;
1515 arg_tbufsz = args->journal_tbuffer_size;
1516 }
1517
1518 sectors_per_fsblock = SWAP_BE32(vhp->blockSize) / blksize;
1519
1520 retval = meta_bread(devvp,
1521 embeddedOffset/blksize +
1522 (SWAP_BE32(vhp->journalInfoBlock)*sectors_per_fsblock),
1523 SWAP_BE32(vhp->blockSize), cred, &jinfo_bp);
1524 if (retval)
1525 return retval;
1526
1527 jibp = (JournalInfoBlock *)jinfo_bp->b_data;
1528 jibp->flags = SWAP_BE32(jibp->flags);
1529 jibp->offset = SWAP_BE64(jibp->offset);
1530 jibp->size = SWAP_BE64(jibp->size);
1531
1532 if (jibp->flags & kJIJournalInFSMask) {
1533 hfsmp->jvp = hfsmp->hfs_devvp;
1534 } else {
1535 printf("hfs: journal not stored in fs! don't know what to do.\n");
1536 brelse(jinfo_bp);
1537 return EINVAL;
1538 }
1539
1540 // save this off for the hack-y check in hfs_remove()
1541 hfsmp->jnl_start = jibp->offset / SWAP_BE32(vhp->blockSize);
55e303ae 1542 hfsmp->jnl_size = jibp->size;
b4c24cb9
A
1543
1544 if (jibp->flags & kJIJournalNeedInitMask) {
1545 printf("hfs: Initializing the journal (joffset 0x%llx sz 0x%llx)...\n",
1546 jibp->offset + (off_t)embeddedOffset, jibp->size);
1547 hfsmp->jnl = journal_create(hfsmp->jvp,
1548 jibp->offset + (off_t)embeddedOffset,
1549 jibp->size,
1550 devvp,
1551 blksize,
1552 arg_flags,
1553 arg_tbufsz,
1554 hfs_sync_metadata, hfsmp->hfs_mp);
1555
1556 // no need to start a transaction here... if this were to fail
1557 // we'd just re-init it on the next mount.
1558 jibp->flags &= ~kJIJournalNeedInitMask;
1559 jibp->flags = SWAP_BE32(jibp->flags);
55e303ae
A
1560 jibp->offset = SWAP_BE64(jibp->offset);
1561 jibp->size = SWAP_BE64(jibp->size);
b4c24cb9
A
1562 bwrite(jinfo_bp);
1563 jinfo_bp = NULL;
1564 jibp = NULL;
1565 } else {
1566 //printf("hfs: Opening the journal (joffset 0x%llx sz 0x%llx vhp_blksize %d)...\n",
1567 // jibp->offset + (off_t)embeddedOffset,
1568 // jibp->size, SWAP_BE32(vhp->blockSize));
1569
1570 hfsmp->jnl = journal_open(hfsmp->jvp,
1571 jibp->offset + (off_t)embeddedOffset,
1572 jibp->size,
1573 devvp,
1574 blksize,
1575 arg_flags,
1576 arg_tbufsz,
1577 hfs_sync_metadata, hfsmp->hfs_mp);
1578
1579 brelse(jinfo_bp);
1580 jinfo_bp = NULL;
1581 jibp = NULL;
1582
1583 if (hfsmp->jnl && mdbp) {
1584 // reload the mdb because it could have changed
1585 // if the journal had to be replayed.
55e303ae
A
1586 if (mdb_offset == 0) {
1587 mdb_offset = (embeddedOffset / blksize) + HFS_PRI_SECTOR(blksize);
1588 }
b4c24cb9
A
1589 retval = meta_bread(devvp, mdb_offset, blksize, cred, &bp);
1590 if (retval) {
1591 brelse(bp);
1592 printf("hfs: failed to reload the mdb after opening the journal (retval %d)!\n",
1593 retval);
1594 return retval;
1595 }
1596 bcopy(bp->b_data + HFS_PRI_OFFSET(blksize), mdbp, 512);
1597 brelse(bp);
1598 bp = NULL;
1599 }
1600 }
1601
1602
1603 //printf("journal @ 0x%x\n", hfsmp->jnl);
1604
1605 // if we expected the journal to be there and we couldn't
1606 // create it or open it then we have to bail out.
1607 if (hfsmp->jnl == NULL) {
55e303ae 1608 printf("hfs: early jnl init: failed to open/create the journal (retval %d).\n", retval);
b4c24cb9
A
1609 return EINVAL;
1610 }
1c79356b 1611
b4c24cb9
A
1612 return 0;
1613}
1614
1615
1616//
1617// This function will go and re-locate the .journal_info_block and
1618// the .journal files in case they moved (which can happen if you
1619// run Norton SpeedDisk). If we fail to find either file we just
1620// disable journaling for this volume and return. We turn off the
1621// journaling bit in the vcb and assume it will get written to disk
1622// later (if it doesn't on the next mount we'd do the same thing
1623// again which is harmless). If we disable journaling we don't
1624// return an error so that the volume is still mountable.
1625//
1626// If the info we find for the .journal_info_block and .journal files
1627// isn't what we had stored, we re-set our cached info and proceed
1628// with opening the journal normally.
1629//
1630static int
1631hfs_late_journal_init(struct hfsmount *hfsmp, HFSPlusVolumeHeader *vhp, void *_args)
1632{
1633 JournalInfoBlock *jibp;
1634 struct buf *jinfo_bp, *bp;
1635 int sectors_per_fsblock, arg_flags=0, arg_tbufsz=0;
1636 int retval, need_flush = 0, write_jibp = 0;
1637 struct vnode *devvp;
1638 struct cat_attr jib_attr, jattr;
1639 struct cat_fork jib_fork, jfork;
1640 ExtendedVCB *vcb;
1641 u_long fid;
1642 struct hfs_mount_args *args = _args;
1643
1644 devvp = hfsmp->hfs_devvp;
1645 vcb = HFSTOVCB(hfsmp);
1646
1647 if (args != NULL && (args->flags & HFSFSMNT_EXTENDED_ARGS)) {
1648 if (args->journal_disable) {
1649 return 0;
1650 }
1651
1652 arg_flags = args->journal_flags;
1653 arg_tbufsz = args->journal_tbuffer_size;
1654 }
1655
1656 fid = GetFileInfo(vcb, kRootDirID, ".journal_info_block", &jib_attr, &jib_fork);
1657 if (fid == 0 || jib_fork.cf_extents[0].startBlock == 0 || jib_fork.cf_size == 0) {
1658 printf("hfs: can't find the .journal_info_block! disabling journaling (start: %d).\n",
1659 jib_fork.cf_extents[0].startBlock);
1660 vcb->vcbAtrb &= ~kHFSVolumeJournaledMask;
1661 return 0;
1662 }
1663 hfsmp->hfs_jnlinfoblkid = fid;
1664
1665 // make sure the journal_info_block begins where we think it should.
1666 if (SWAP_BE32(vhp->journalInfoBlock) != jib_fork.cf_extents[0].startBlock) {
1667 printf("hfs: The journal_info_block moved (was: %d; is: %d). Fixing up\n",
1668 SWAP_BE32(vhp->journalInfoBlock), jib_fork.cf_extents[0].startBlock);
1669
1670 vcb->vcbJinfoBlock = jib_fork.cf_extents[0].startBlock;
1671 vhp->journalInfoBlock = SWAP_BE32(jib_fork.cf_extents[0].startBlock);
1672 }
1673
1674
1675 sectors_per_fsblock = SWAP_BE32(vhp->blockSize) / hfsmp->hfs_phys_block_size;
1676 retval = meta_bread(devvp,
1677 vcb->hfsPlusIOPosOffset / hfsmp->hfs_phys_block_size +
1678 (SWAP_BE32(vhp->journalInfoBlock)*sectors_per_fsblock),
1679 SWAP_BE32(vhp->blockSize), NOCRED, &jinfo_bp);
1680 if (retval) {
1681 printf("hfs: can't read journal info block. disabling journaling.\n");
1682 vcb->vcbAtrb &= ~kHFSVolumeJournaledMask;
1683 return 0;
1684 }
1685
1686 jibp = (JournalInfoBlock *)jinfo_bp->b_data;
1687 jibp->flags = SWAP_BE32(jibp->flags);
1688 jibp->offset = SWAP_BE64(jibp->offset);
1689 jibp->size = SWAP_BE64(jibp->size);
1690
1691 fid = GetFileInfo(vcb, kRootDirID, ".journal", &jattr, &jfork);
1692 if (fid == 0 || jfork.cf_extents[0].startBlock == 0 || jfork.cf_size == 0) {
1693 printf("hfs: can't find the journal file! disabling journaling (start: %d)\n",
1694 jfork.cf_extents[0].startBlock);
1695 brelse(jinfo_bp);
1696 vcb->vcbAtrb &= ~kHFSVolumeJournaledMask;
1697 return 0;
1698 }
1699 hfsmp->hfs_jnlfileid = fid;
1700
1701 // make sure the journal file begins where we think it should.
1702 if ((jibp->offset / (u_int64_t)vcb->blockSize) != jfork.cf_extents[0].startBlock) {
1703 printf("hfs: The journal file moved (was: %lld; is: %d). Fixing up\n",
1704 (jibp->offset / (u_int64_t)vcb->blockSize), jfork.cf_extents[0].startBlock);
1705
1706 jibp->offset = (u_int64_t)jfork.cf_extents[0].startBlock * (u_int64_t)vcb->blockSize;
1707 write_jibp = 1;
1708 }
1709
1710 // check the size of the journal file.
1711 if (jibp->size != (u_int64_t)jfork.cf_extents[0].blockCount*vcb->blockSize) {
1712 printf("hfs: The journal file changed size! (was %lld; is %lld). Fixing up.\n",
1713 jibp->size, (u_int64_t)jfork.cf_extents[0].blockCount*vcb->blockSize);
1714
1715 jibp->size = (u_int64_t)jfork.cf_extents[0].blockCount * vcb->blockSize;
1716 write_jibp = 1;
1717 }
1718
1719 if (jibp->flags & kJIJournalInFSMask) {
1720 hfsmp->jvp = hfsmp->hfs_devvp;
1721 } else {
1722 printf("hfs: journal not stored in fs! don't know what to do.\n");
1723 brelse(jinfo_bp);
1724 return EINVAL;
1725 }
1726
1727 // save this off for the hack-y check in hfs_remove()
1728 hfsmp->jnl_start = jibp->offset / SWAP_BE32(vhp->blockSize);
55e303ae 1729 hfsmp->jnl_size = jibp->size;
b4c24cb9
A
1730
1731 if (jibp->flags & kJIJournalNeedInitMask) {
1732 printf("hfs: Initializing the journal (joffset 0x%llx sz 0x%llx)...\n",
1733 jibp->offset + (off_t)vcb->hfsPlusIOPosOffset, jibp->size);
1734 hfsmp->jnl = journal_create(hfsmp->jvp,
1735 jibp->offset + (off_t)vcb->hfsPlusIOPosOffset,
1736 jibp->size,
1737 devvp,
1738 hfsmp->hfs_phys_block_size,
1739 arg_flags,
1740 arg_tbufsz,
1741 hfs_sync_metadata, hfsmp->hfs_mp);
1742
1743 // no need to start a transaction here... if this were to fail
1744 // we'd just re-init it on the next mount.
1745 jibp->flags &= ~kJIJournalNeedInitMask;
1746 write_jibp = 1;
1747
1748 } else {
1749 //
1750 // if we weren't the last person to mount this volume
1751 // then we need to throw away the journal because it
1752 // is likely that someone else mucked with the disk.
1753 // if the journal is empty this is no big deal. if the
1754 // disk is dirty this prevents us from replaying the
1755 // journal over top of changes that someone else made.
1756 //
1757 arg_flags |= JOURNAL_RESET;
1758
1759 //printf("hfs: Opening the journal (joffset 0x%llx sz 0x%llx vhp_blksize %d)...\n",
1760 // jibp->offset + (off_t)vcb->hfsPlusIOPosOffset,
1761 // jibp->size, SWAP_BE32(vhp->blockSize));
1762
1763 hfsmp->jnl = journal_open(hfsmp->jvp,
1764 jibp->offset + (off_t)vcb->hfsPlusIOPosOffset,
1765 jibp->size,
1766 devvp,
1767 hfsmp->hfs_phys_block_size,
1768 arg_flags,
1769 arg_tbufsz,
1770 hfs_sync_metadata, hfsmp->hfs_mp);
1771 }
1772
1773
1774 if (write_jibp) {
1775 jibp->flags = SWAP_BE32(jibp->flags);
1776 jibp->offset = SWAP_BE64(jibp->offset);
1777 jibp->size = SWAP_BE64(jibp->size);
1778
1779 bwrite(jinfo_bp);
1780 } else {
1781 brelse(jinfo_bp);
1782 }
1783 jinfo_bp = NULL;
1784 jibp = NULL;
1785
1786 //printf("journal @ 0x%x\n", hfsmp->jnl);
1787
1788 // if we expected the journal to be there and we couldn't
1789 // create it or open it then we have to bail out.
1790 if (hfsmp->jnl == NULL) {
55e303ae 1791 printf("hfs: late jnl init: failed to open/create the journal (retval %d).\n", retval);
b4c24cb9
A
1792 return EINVAL;
1793 }
1794
1795 return 0;
1796}
55e303ae
A
1797
1798/*
1799 * Calculate the allocation zone for metadata.
1800 *
1801 * This zone includes the following:
1802 * Allocation Bitmap file
1803 * Overflow Extents file
1804 * Journal file
1805 * Quota files
1806 * Clustered Hot files
1807 * Catalog file
1808 *
1809 * METADATA ALLOCATION ZONE
1810 * ____________________________________________________________________________
1811 * | | | | | | |
1812 * | BM | JF | OEF | CATALOG |---> | HOT FILES |
1813 * |____|____|_____|_______________|______________________________|___________|
1814 *
1815 * <------------------------------- N * 128 MB ------------------------------->
1816 *
1817 */
1818#define GIGABYTE (u_int64_t)(1024*1024*1024)
1819
1820#define OVERFLOW_DEFAULT_SIZE (4*1024*1024)
1821#define OVERFLOW_MAXIMUM_SIZE (128*1024*1024)
1822#define JOURNAL_DEFAULT_SIZE (8*1024*1024)
1823#define JOURNAL_MAXIMUM_SIZE (512*1024*1024)
1824#define HOTBAND_MINIMUM_SIZE (10*1024*1024)
1825#define HOTBAND_MAXIMUM_SIZE (512*1024*1024)
1826
1827static void
1828hfs_metadatazone_init(struct hfsmount *hfsmp)
1829{
1830 ExtendedVCB *vcb;
1831 struct BTreeInfoRec btinfo;
1832 u_int64_t fs_size;
1833 u_int64_t zonesize;
1834 u_int64_t temp;
1835 u_int64_t filesize;
1836 u_int32_t blk;
1837 int items;
1838
1839 vcb = HFSTOVCB(hfsmp);
1840 fs_size = (u_int64_t)vcb->blockSize * (u_int64_t)vcb->totalBlocks;
1841
1842 /*
1843 * For volumes less than 10 GB, don't bother.
1844 */
1845 if (fs_size < ((u_int64_t)10 * GIGABYTE))
1846 return;
1847 /*
1848 * Skip non-journaled volumes as well.
1849 */
1850 if (hfsmp->jnl == NULL)
1851 return;
1852
1853 /*
1854 * Start with allocation bitmap (a fixed size).
1855 */
1856 zonesize = roundup(vcb->totalBlocks / 8, vcb->vcbVBMIOSize);
1857
1858 /*
1859 * Overflow Extents file gets 4 MB per 100 GB.
1860 */
1861 items = fs_size / ((u_int64_t)100 * GIGABYTE);
1862 filesize = (u_int64_t)(items + 1) * OVERFLOW_DEFAULT_SIZE;
1863 if (filesize > OVERFLOW_MAXIMUM_SIZE)
1864 filesize = OVERFLOW_MAXIMUM_SIZE;
1865 zonesize += filesize;
1866 hfsmp->hfs_overflow_maxblks = filesize / vcb->blockSize;
1867
1868 /*
1869 * Plan for at least 8 MB of journal for each
1870 * 100 GB of disk space (up to a 512 MB).
1871 */
1872 items = fs_size / ((u_int64_t)100 * GIGABYTE);
1873 filesize = (u_int64_t)(items + 1) * JOURNAL_DEFAULT_SIZE;
1874 if (filesize > JOURNAL_MAXIMUM_SIZE)
1875 filesize = JOURNAL_MAXIMUM_SIZE;
1876 zonesize += filesize;
1877
1878 /*
1879 * Catalog file gets 10 MB per 1 GB.
1880 *
1881 * How about considering the current catalog size (used nodes * node size)
1882 * and the current file data size to help estimate the required
1883 * catalog size.
1884 */
1885 filesize = MIN((fs_size / 1024) * 10, GIGABYTE);
1886 hfsmp->hfs_catalog_maxblks = filesize / vcb->blockSize;
1887 zonesize += filesize;
1888
1889 /*
1890 * Add space for hot file region.
1891 *
1892 * ...for now, use 5 MB per 1 GB (0.5 %)
1893 */
1894 filesize = (fs_size / 1024) * 5;
1895 if (filesize > HOTBAND_MAXIMUM_SIZE)
1896 filesize = HOTBAND_MAXIMUM_SIZE;
1897 else if (filesize < HOTBAND_MINIMUM_SIZE)
1898 filesize = HOTBAND_MINIMUM_SIZE;
1899 /*
1900 * Calculate user quota file requirements.
1901 */
1902 items = QF_USERS_PER_GB * (fs_size / GIGABYTE);
1903 if (items < QF_MIN_USERS)
1904 items = QF_MIN_USERS;
1905 else if (items > QF_MAX_USERS)
1906 items = QF_MAX_USERS;
1907 if (!powerof2(items)) {
1908 int x = items;
1909 items = 4;
1910 while (x>>1 != 1) {
1911 x = x >> 1;
1912 items = items << 1;
1913 }
1914 }
1915 filesize += (items + 1) * sizeof(struct dqblk);
1916 /*
1917 * Calculate group quota file requirements.
1918 *
1919 */
1920 items = QF_GROUPS_PER_GB * (fs_size / GIGABYTE);
1921 if (items < QF_MIN_GROUPS)
1922 items = QF_MIN_GROUPS;
1923 else if (items > QF_MAX_GROUPS)
1924 items = QF_MAX_GROUPS;
1925 if (!powerof2(items)) {
1926 int x = items;
1927 items = 4;
1928 while (x>>1 != 1) {
1929 x = x >> 1;
1930 items = items << 1;
1931 }
1932 }
1933 filesize += (items + 1) * sizeof(struct dqblk);
1934 hfsmp->hfs_hotfile_maxblks = filesize / vcb->blockSize;
1935 zonesize += filesize;
1936
1937 /*
1938 * Round up entire zone to a bitmap block's worth.
1939 * The extra space goes to the catalog file and hot file area.
1940 */
1941 temp = zonesize;
1942 zonesize = roundup(zonesize, vcb->vcbVBMIOSize * 8 * vcb->blockSize);
1943 temp = zonesize - temp; /* temp has extra space */
1944 filesize += temp / 3;
1945 hfsmp->hfs_catalog_maxblks += (temp - (temp / 3)) / vcb->blockSize;
1946
1947 /* Convert to allocation blocks. */
1948 blk = zonesize / vcb->blockSize;
1949
1950 /* The default metadata zone location is at the start of volume. */
1951 hfsmp->hfs_metazone_start = 1;
1952 hfsmp->hfs_metazone_end = blk - 1;
1953
1954 /* The default hotfile area is at the end of the zone. */
1955 hfsmp->hfs_hotfile_start = blk - (filesize / vcb->blockSize);
1956 hfsmp->hfs_hotfile_end = hfsmp->hfs_metazone_end;
1957 hfsmp->hfs_hotfile_freeblks = hfs_hotfile_freeblocks(hfsmp);
1958#if 0
1959 printf("HFS: metadata zone is %d to %d\n", hfsmp->hfs_metazone_start, hfsmp->hfs_metazone_end);
1960 printf("HFS: hot file band is %d to %d\n", hfsmp->hfs_hotfile_start, hfsmp->hfs_hotfile_end);
1961 printf("HFS: hot file band free blocks = %d\n", hfsmp->hfs_hotfile_freeblks);
1962#endif
1963 hfsmp->hfs_flags |= HFS_METADATA_ZONE;
1964}
1965
1966
1967static u_int32_t
1968hfs_hotfile_freeblocks(struct hfsmount *hfsmp)
1969{
1970 ExtendedVCB *vcb = HFSTOVCB(hfsmp);
1971 int freeblocks;
1972
1973 freeblocks = MetaZoneFreeBlocks(vcb);
1974 /* Minus Extents overflow file reserve. */
1975 freeblocks -=
1976 hfsmp->hfs_overflow_maxblks - VTOF(vcb->extentsRefNum)->ff_blocks;
1977 /* Minus catalog file reserve. */
1978 freeblocks -=
1979 hfsmp->hfs_catalog_maxblks - VTOF(vcb->catalogRefNum)->ff_blocks;
1980 if (freeblocks < 0)
1981 freeblocks = 0;
1982
1983 return MIN(freeblocks, hfsmp->hfs_hotfile_maxblks);
1984}
1985
1986/*
1987 * Determine if a file is a "virtual" metadata file.
1988 * This includes journal and quota files.
1989 */
1990__private_extern__
1991int
1992hfs_virtualmetafile(struct cnode *cp)
1993{
1994 char * filename;
1995
1996
1997 if (cp->c_parentcnid != kHFSRootFolderID)
1998 return (0);
1999
2000 filename = cp->c_desc.cd_nameptr;
2001 if (filename == NULL)
2002 return (0);
2003
2004 if ((strcmp(filename, ".journal") == 0) ||
2005 (strcmp(filename, ".journal_info_block") == 0) ||
2006 (strcmp(filename, ".quota.user") == 0) ||
2007 (strcmp(filename, ".quota.group") == 0) ||
2008 (strcmp(filename, ".hotfiles.btree") == 0))
2009 return (1);
2010
2011 return (0);
2012}
2013