]> git.saurik.com Git - apple/xnu.git/blob - bsd/hfs/hfs_vfsutils.c
19b5c03c5d239383bb884edf9a65ebb280d5fb92
[apple/xnu.git] / bsd / hfs / hfs_vfsutils.c
1 /*
2 * Copyright (c) 2000-2003 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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.
11 *
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
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22 /* @(#)hfs_vfsutils.c 4.0
23 *
24 * (c) 1997-2002 Apple Computer, Inc. All Rights Reserved
25 *
26 * hfs_vfsutils.c -- Routines that go between the HFS layer and the VFS.
27 *
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>
34 #include <sys/mount.h>
35 #include <sys/namei.h>
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"
42 #include "hfs_catalog.h"
43 #include "hfs_dbg.h"
44 #include "hfs_mount.h"
45 #include "hfs_endian.h"
46 #include "hfs_cnode.h"
47
48 #include "hfscommon/headers/FileMgrInternal.h"
49 #include "hfscommon/headers/BTreesInternal.h"
50 #include "hfscommon/headers/HFSUnicodeWrappers.h"
51
52
53 extern int count_lock_queue __P((void));
54
55
56 static void ReleaseMetaFileVNode(struct vnode *vp);
57 static int hfs_late_journal_init(struct hfsmount *hfsmp, HFSPlusVolumeHeader *vhp, void *_args);
58
59 static void hfs_metadatazone_init(struct hfsmount *);
60 static u_int32_t hfs_hotfile_freeblocks(struct hfsmount *);
61
62
63
64 u_int32_t GetLogicalBlockSize(struct vnode *vp);
65
66 /* BTree accessor routines */
67 extern OSStatus GetBTreeBlock(FileReference vp, UInt32 blockNum, GetBlockOptions options, BlockDescriptor *block);
68 extern OSStatus SetBTreeBlockSize(FileReference vp, ByteCount blockSize, ItemCount minBlockCount);
69 extern OSStatus ExtendBTreeFile(FileReference vp, FSSize minEOF, FSSize maxEOF);
70 extern 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 //*******************************************************************************
83 char hfs_catname[] = "Catalog B-tree";
84 char hfs_extname[] = "Extents B-tree";
85 char hfs_vbmname[] = "Volume Bitmap";
86
87 char hfs_privdirname[] =
88 "\xE2\x90\x80\xE2\x90\x80\xE2\x90\x80\xE2\x90\x80HFS+ Private Data";
89
90 __private_extern__
91 OSErr hfs_MountHFSVolume(struct hfsmount *hfsmp, HFSMasterDirectoryBlock *mdb,
92 struct proc *p)
93 {
94 ExtendedVCB *vcb = HFSTOVCB(hfsmp);
95 int error;
96 ByteCount utf8chars;
97 struct cat_desc cndesc;
98 struct cat_attr cnattr;
99 struct cat_fork fork;
100
101 /* Block size must be a multiple of 512 */
102 if (SWAP_BE32(mdb->drAlBlkSiz) == 0 ||
103 (SWAP_BE32(mdb->drAlBlkSiz) & 0x01FF) != 0)
104 return (EINVAL);
105
106 /* don't mount a writeable volume if its dirty, it must be cleaned by fsck_hfs */
107 if (((hfsmp->hfs_flags & HFS_READ_ONLY) == 0) &&
108 ((SWAP_BE16(mdb->drAtrb) & kHFSVolumeUnmountedMask) == 0)) {
109 return (EINVAL);
110 }
111 hfsmp->hfs_flags |= HFS_STANDARD;
112 /*
113 * The MDB seems OK: transfer info from it into VCB
114 * Note - the VCB starts out clear (all zeros)
115 *
116 */
117 vcb->vcbSigWord = SWAP_BE16 (mdb->drSigWord);
118 vcb->vcbCrDate = to_bsd_time(LocalToUTC(SWAP_BE32(mdb->drCrDate)));
119 vcb->localCreateDate = SWAP_BE32 (mdb->drCrDate);
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);
136 bcopy(mdb->drFndrInfo, vcb->vcbFndrInfo, sizeof(vcb->vcbFndrInfo));
137 if ((hfsmp->hfs_flags & HFS_READ_ONLY) == 0)
138 vcb->vcbWrCnt++; /* Compensate for write of MDB on last flush */
139
140 /* convert hfs encoded name into UTF-8 string */
141 error = hfs_to_utf8(vcb, mdb->drVN, NAME_MAX, &utf8chars, vcb->vcbVN);
142 /*
143 * When an HFS name cannot be encoded with the current
144 * volume encoding we use MacRoman as a fallback.
145 */
146 if (error || (utf8chars == 0))
147 (void) mac_roman_to_utf8(mdb->drVN, NAME_MAX, &utf8chars, vcb->vcbVN);
148
149 hfsmp->hfs_logBlockSize = BestBlockSizeFit(vcb->blockSize, MAXBSIZE, hfsmp->hfs_phys_block_size);
150 vcb->vcbVBMIOSize = kHFSBlockSize;
151
152 VCB_LOCK_INIT(vcb);
153
154 bzero(&cndesc, sizeof(cndesc));
155 cndesc.cd_parentcnid = kRootParID;
156 cndesc.cd_flags |= CD_ISMETA;
157 bzero(&cnattr, sizeof(cnattr));
158 cnattr.ca_nlink = 1;
159 cnattr.ca_mode = S_IFREG;
160 bzero(&fork, sizeof(fork));
161
162 /*
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);
171 fork.cf_vblocks = 0;
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),
184 (KeyCompareProcPtr)CompareExtentKeys));
185 if (error) {
186 VOP_UNLOCK(vcb->extentsRefNum, 0, p);
187 goto MtVolErr;
188 }
189
190 /*
191 * Set up Catalog B-tree vnode...
192 */
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);
199 fork.cf_vblocks = 0;
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),
215 (KeyCompareProcPtr)CompareCatalogKeys));
216 if (error) {
217 VOP_UNLOCK(vcb->catalogRefNum, 0, p);
218 VOP_UNLOCK(vcb->extentsRefNum, 0, p);
219 goto MtVolErr;
220 }
221
222 /* mark the volume dirty (clear clean unmount bit) */
223 vcb->vcbAtrb &= ~kHFSVolumeUnmountedMask;
224
225 /*
226 * all done with b-trees so we can unlock now...
227 */
228 VOP_UNLOCK(vcb->catalogRefNum, 0, p);
229 VOP_UNLOCK(vcb->extentsRefNum, 0, p);
230
231 if ( error == noErr )
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:
241 MtVolErr:
242 ReleaseMetaFileVNode(vcb->catalogRefNum);
243 ReleaseMetaFileVNode(vcb->extentsRefNum);
244
245 CmdDone:
246 return (error);
247 }
248
249 //*******************************************************************************
250 // Routine: hfs_MountHFSPlusVolume
251 //
252 //
253 //*******************************************************************************
254
255 __private_extern__
256 OSErr hfs_MountHFSPlusVolume(struct hfsmount *hfsmp, HFSPlusVolumeHeader *vhp,
257 off_t embeddedOffset, u_int64_t disksize, struct proc *p, void *args)
258 {
259 register ExtendedVCB *vcb;
260 struct cat_desc cndesc;
261 struct cat_attr cnattr;
262 struct cat_fork cfork;
263 UInt32 blockSize;
264 u_int64_t volumesize;
265 struct BTreeInfoRec btinfo;
266 u_int16_t signature;
267 u_int16_t version;
268 int i;
269 OSErr retval;
270
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);
289 return (EINVAL);
290 }
291
292 /* Block size must be at least 512 and a power of 2 */
293 blockSize = SWAP_BE32(vhp->blockSize);
294 if (blockSize < 512 || !powerof2(blockSize))
295 return (EINVAL);
296
297 /* don't mount a writable volume if its dirty, it must be cleaned by fsck_hfs */
298 if ((hfsmp->hfs_flags & HFS_READ_ONLY) == 0 && hfsmp->jnl == NULL &&
299 (SWAP_BE32(vhp->attributes) & kHFSVolumeUnmountedMask) == 0)
300 return (EINVAL);
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)) ||
305 (blockSize < hfsmp->hfs_phys_block_size)) {
306 return (ENXIO);
307 }
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
314 vcb->vcbSigWord = signature;
315 vcb->vcbJinfoBlock = SWAP_BE32(vhp->journalInfoBlock);
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);
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 */
329 if ((hfsmp->hfs_flags & HFS_READ_ONLY) == 0)
330 vcb->vcbWrCnt++; /* compensate for write of Volume Header on last flush */
331
332 VCB_LOCK_INIT(vcb);
333
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);
338 vcb->blockSize = blockSize;
339 vcb->encodingsBitmap = SWAP_BE64(vhp->encodingsBitmap);
340 vcb->localCreateDate = SWAP_BE32(vhp->createDate);
341
342 vcb->hfsPlusIOPosOffset = embeddedOffset;
343
344 /* Default to no free block reserve */
345 vcb->reserveBlocks = 0;
346
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);
353 vcb->vcbVBMIOSize = min(vcb->blockSize, MAXPHYSIO);
354
355 bzero(&cndesc, sizeof(cndesc));
356 cndesc.cd_parentcnid = kRootParID;
357 cndesc.cd_flags |= CD_ISMETA;
358 bzero(&cnattr, sizeof(cnattr));
359 cnattr.ca_nlink = 1;
360 cnattr.ca_mode = S_IFREG;
361
362 /*
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
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,
381 &vcb->extentsRefNum);
382
383 if (retval) goto ErrorExit;
384 retval = MacToVFSError(BTOpenPath(VTOF(vcb->extentsRefNum),
385 (KeyCompareProcPtr) CompareExtentKeysPlus));
386 if (retval) {
387 VOP_UNLOCK(vcb->extentsRefNum, 0, p);
388 goto ErrorExit;
389 }
390
391 /*
392 * Set up Catalog B-tree vnode
393 */
394 cndesc.cd_nameptr = hfs_catname;
395 cndesc.cd_namelen = strlen(hfs_catname);
396 cndesc.cd_cnid = cnattr.ca_fileid = kHFSCatalogFileID;
397
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,
410 &vcb->catalogRefNum);
411 if (retval) {
412 VOP_UNLOCK(vcb->extentsRefNum, 0, p);
413 goto ErrorExit;
414 }
415 retval = MacToVFSError(BTOpenPath(VTOF(vcb->catalogRefNum),
416 (KeyCompareProcPtr) CompareExtendedCatalogKeys));
417 if (retval) {
418 VOP_UNLOCK(vcb->catalogRefNum, 0, p);
419 VOP_UNLOCK(vcb->extentsRefNum, 0, p);
420 goto ErrorExit;
421 }
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 }
431
432 /*
433 * Set up Allocation file vnode
434 */
435 cndesc.cd_nameptr = hfs_vbmname;
436 cndesc.cd_namelen = strlen(hfs_vbmname);
437 cndesc.cd_cnid = cnattr.ca_fileid = kHFSAllocationFileID;
438
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,
451 &vcb->allocationsRefNum);
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);
470
471 /* mark the volume dirty (clear clean unmount bit) */
472 vcb->vcbAtrb &= ~kHFSVolumeUnmountedMask;
473 if (hfsmp->jnl && (hfsmp->hfs_flags & HFS_READ_ONLY) == 0) {
474 hfs_flushvolumeheader(hfsmp, TRUE, TRUE);
475 }
476
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
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
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 }
548
549 return (0);
550
551 ErrorExit:
552 /*
553 * A fatal error occured and the volume cannot be mounted
554 * release any resources that we aquired...
555 */
556
557 InvalidateCatalogCache(vcb);
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 */
571 static void ReleaseMetaFileVNode(struct vnode *vp)
572 {
573 struct filefork *fp;
574
575 if (vp && (fp = VTOF(vp))) {
576 if (fp->fcbBTCBPtr != NULL)
577 (void) BTClosePath(fp);
578
579 /* release the node even if BTClosePath fails */
580 vrele(vp);
581 vgone(vp);
582 }
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
593 __private_extern__
594 int
595 hfsUnmount( register struct hfsmount *hfsmp, struct proc *p)
596 {
597 ExtendedVCB *vcb = HFSTOVCB(hfsmp);
598 int retval = E_NONE;
599
600 InvalidateCatalogCache( vcb );
601
602 if (hfsmp->hfc_filevp) {
603 ReleaseMetaFileVNode(hfsmp->hfc_filevp);
604 hfsmp->hfc_filevp = NULL;
605 }
606
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
617 /*
618 * Test is fork has overflow extents.
619 */
620 __private_extern__
621 int
622 overflow_extents(struct filefork *fp)
623 {
624 u_long blocks;
625
626 if (VTOVCB(FTOV(fp))->vcbSigWord == kHFSPlusSigWord) {
627 if (fp->ff_extents[7].blockCount == 0)
628 return (0);
629
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 }
646
647 return (fp->ff_blocks > blocks);
648 }
649
650
651 /*
652 * Lock/Unlock a metadata file.
653 */
654 __private_extern__
655 int
656 hfs_metafilelocking(struct hfsmount *hfsmp, u_long fileID, u_int flags, struct proc *p)
657 {
658 ExtendedVCB *vcb;
659 struct vnode *vp = NULL;
660 int numOfLockedBuffs;
661 int retval = 0;
662
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 }
680
681 if ((flags & LK_TYPE_MASK) != LK_RELEASE) {
682 flags |= LK_RETRY;
683 } else if (hfsmp->jnl == NULL) {
684 struct timeval tv = time;
685 u_int32_t lastfsync = tv.tv_sec;
686
687 (void) BTGetLastSync((FCB*)VTOF(vp), &lastfsync);
688
689 numOfLockedBuffs = count_lock_queue();
690 if ((numOfLockedBuffs > kMaxLockedMetaBuffers) ||
691 ((numOfLockedBuffs > 1) && ((tv.tv_sec - lastfsync) > kMaxSecsForFsync))) {
692 hfs_btsync(vp, HFS_SYNCTRANS);
693 }
694 }
695
696 retval = lockmgr(&VTOC(vp)->c_lock, flags, &vp->v_interlock, p);
697
698 return (retval);
699 }
700
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
708 void RequireFileLock(FileReference vp, int shareable)
709 {
710 struct lock__bsd__ *lkp;
711 int locked = false;
712 pid_t pid;
713 void * self;
714
715 pid = current_proc()->p_pid;
716 self = (void *) current_act();
717 lkp = &VTOC(vp)->c_lock;
718
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;
725
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;
733
734 case 4:
735 DEBUG_BREAK_MSG((" #\n # RequireFileLock: catalog btree vnode not locked! v: 0x%08X\n #\n", (u_int)vp));
736 break;
737
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 }
743 }
744 #endif
745
746
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.
751 * (b) The object in question is owned by "unknown"
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
755 * the replacement UID is "unknown".
756 * 3. You are root.
757 *
758 */
759 int
760 hfs_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] */
764 (cnode_uid == UNKNOWNUID) || /* [1b] */
765 ((HFSTOVFS(hfsmp)->mnt_flag & MNT_UNKNOWNPERMISSIONS) && /* [2] */
766 ((cred->cr_uid == hfsmp->hfs_uid) || /* [2a] */
767 (hfsmp->hfs_uid == UNKNOWNUID))) || /* [2b] */
768 (invokesuperuserstatus && (suser(cred, &p->p_acflag) == 0))) { /* [3] */
769 return (0);
770 } else {
771 return (EPERM);
772 }
773 }
774
775
776 unsigned 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;
785
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 };
793
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;
799
800 /* No clear winner exists: pick the largest even fraction <= MAXBSIZE: */
801 baseBlockCount = allocationBlockSize / baseMultiple; /* Now guaranteed to be an even multiple */
802
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 };
812
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 }
817
818
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
823 * and a reference to it is place in hfsmp->hfs_privdir_desc.
824 */
825 __private_extern__
826 u_long
827 FindMetaDataDirectory(ExtendedVCB *vcb)
828 {
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};
834 struct proc *p = current_proc();
835 struct timeval tv;
836 cat_cookie_t cookie;
837 int error;
838
839 if (vcb->vcbSigWord != kHFSPlusSigWord)
840 return (0);
841
842 hfsmp = VCBTOHFS(vcb);
843
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;
849 }
850
851 /* Lock catalog b-tree */
852 if (hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_SHARED, p) != 0)
853 return (0);
854
855 error = cat_lookup(hfsmp, &hfsmp->hfs_privdir_desc, 0, NULL,
856 &hfsmp->hfs_privdir_attr, NULL);
857
858 /* Unlock catalog b-tree */
859 (void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, p);
860
861 if (error == 0) {
862 hfsmp->hfs_metadata_createdate = hfsmp->hfs_privdir_attr.ca_itime;
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 }
888 return (hfsmp->hfs_privdir_attr.ca_fileid);
889
890 } else if (hfsmp->hfs_flags & HFS_READ_ONLY) {
891
892 return (0);
893 }
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;
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
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 }
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 }
924
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);
928
929 (void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, p);
930 }
931
932 cat_postflight(hfsmp, &cookie, p);
933
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 }
942
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);
957 }
958 hfs_volupdate(hfsmp, VOL_MKDIR, 1);
959 if (hfsmp->jnl) {
960 journal_end_transaction(hfsmp->jnl);
961 }
962 hfs_global_shared_lock_release(hfsmp);
963
964 cat_releasedesc(&out_desc);
965
966 return (out_desc.cd_cnid);
967 }
968
969 __private_extern__
970 u_long
971 GetFileInfo(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);
1003 } else if (hfsmp->hfs_flags & HFS_READ_ONLY) {
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__
1016 void
1017 hfs_remove_orphans(struct hfsmount * hfsmp)
1018 {
1019 struct BTreeIterator * iterator = NULL;
1020 struct FSBufferDescriptor btdata;
1021 struct HFSPlusCatalogFile filerec;
1022 struct HFSPlusCatalogKey * keyp;
1023 struct proc *p = current_proc();
1024 FCB *fcb;
1025 ExtendedVCB *vcb;
1026 char filename[32];
1027 char tempname[32];
1028 size_t namelen;
1029 cat_cookie_t cookie = {0};
1030 int catlock = 0;
1031 int catreserve = 0;
1032 int started_tr = 0;
1033 int shared_lock = 0;
1034 int result;
1035
1036 if (hfsmp->hfs_flags & HFS_CLEANED_ORPHANS)
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;
1049 keyp->parentID = hfsmp->hfs_privdir_desc.cd_cnid;
1050
1051 result = hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_EXCLUSIVE, p);
1052 if (result)
1053 goto exit;
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);
1059
1060 (void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, p);
1061 if (result)
1062 goto exit;
1063
1064 /* Visit all the children in the HFS+ private directory. */
1065 for (;;) {
1066 result = hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_EXCLUSIVE, p);
1067 if (result)
1068 goto exit;
1069
1070 result = BTIterateRecord(fcb, kBTreeNextRecord, iterator, &btdata, NULL);
1071
1072 (void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, p);
1073 if (result)
1074 break;
1075
1076 if (keyp->parentID != hfsmp->hfs_privdir_desc.cd_cnid)
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 *
1090 */
1091 if (bcmp(tempname, filename, namelen) == 0) {
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;
1113
1114 /* Lock catalog b-tree */
1115 if (hfs_metafilelocking(hfsmp, kHFSCatalogFileID,
1116 LK_EXCLUSIVE, p) != 0) {
1117 goto exit;
1118 }
1119 catlock = 1;
1120
1121 /* Build a fake cnode */
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;
1125 cnode.c_desc.cd_nameptr = filename;
1126 cnode.c_desc.cd_namelen = namelen;
1127 cnode.c_desc.cd_cnid = cnode.c_attr.ca_fileid;
1128 cnode.c_blocks = dfork.ff_blocks + rfork.ff_blocks;
1129
1130 /* Position iterator at previous entry */
1131 if (BTIterateRecord(fcb, kBTreePrevRecord, iterator,
1132 NULL, NULL) != 0) {
1133 break;
1134 }
1135
1136 /* Truncate the file to zero (both forks) */
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 }
1168 }
1169 }
1170
1171 if (rfork.ff_blocks > 0) {
1172 rfork.ff_cp = &cnode;
1173 cnode.c_datafork = NULL;
1174 cnode.c_rsrcfork = &rfork;
1175 if (TruncateFileC(vcb, (FCB*)&rfork, 0, false) != 0) {
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);
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 */
1206
1207 exit:
1208 if (catlock) {
1209 (void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, p);
1210 }
1211 if (catreserve) {
1212 cat_postflight(hfsmp, &cookie, p);
1213 }
1214 if (started_tr) {
1215 journal_end_transaction(hfsmp->jnl);
1216 }
1217 if (shared_lock) {
1218 hfs_global_shared_lock_release(hfsmp);
1219 }
1220
1221 FREE(iterator, M_TEMP);
1222 hfsmp->hfs_flags |= HFS_CLEANED_ORPHANS;
1223 }
1224
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 */
1231 u_int32_t
1232 GetLogicalBlockSize(struct vnode *vp)
1233 {
1234 u_int32_t logBlockSize;
1235
1236 DBG_ASSERT(vp != NULL);
1237
1238 /* start with default */
1239 logBlockSize = VTOHFS(vp)->hfs_logBlockSize;
1240
1241 if (vp->v_flag & VSYSTEM) {
1242 if (VTOF(vp)->fcbBTCBPtr != NULL) {
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
1253 (void) BTGetInformation (VTOF(vp), kBTreeInfoVersion, &bTreeInfo);
1254
1255 logBlockSize = bTreeInfo.nodeSize;
1256
1257 } else if (VTOC(vp)->c_fileid == kHFSAllocationFileID) {
1258 logBlockSize = VTOVCB(vp)->vcbVBMIOSize;
1259 }
1260 }
1261
1262 DBG_ASSERT(logBlockSize > 0);
1263
1264 return logBlockSize;
1265 }
1266
1267 __private_extern__
1268 u_int32_t
1269 hfs_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 }
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 */
1321
1322 return (freeblks);
1323 }
1324
1325 /*
1326 * Map HFS Common errors (negative) to BSD error codes (positive).
1327 * Positive errors (ie BSD errors) are passed through unchanged.
1328 */
1329 short MacToVFSError(OSErr err)
1330 {
1331 if (err >= 0)
1332 return err;
1333
1334 switch (err) {
1335 case dskFulErr: /* -34 */
1336 case btNoSpaceAvail: /* -32733 */
1337 return ENOSPC;
1338 case fxOvFlErr: /* -32750 */
1339 return EOVERFLOW;
1340
1341 case btBadNode: /* -32731 */
1342 return EBADF;
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 */
1362 return ERANGE;
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:
1372 return ENXIO;
1373
1374 default:
1375 return EIO; /* +5 */
1376 }
1377 }
1378
1379
1380 /*
1381 * Get the directory entry name hint for a given index.
1382 * The directory cnode (dcp) must be locked.
1383 */
1384 __private_extern__
1385 char *
1386 hfs_getnamehint(struct cnode *dcp, int index)
1387 {
1388 struct hfs_index *entry;
1389 void *self;
1390
1391 if (index > 0) {
1392 self = current_act();
1393 SLIST_FOREACH(entry, &dcp->c_indexlist, hi_link) {
1394 if ((entry->hi_index == index)
1395 && (entry->hi_thread == self))
1396 return (entry->hi_name);
1397 }
1398 }
1399
1400 return (NULL);
1401 }
1402
1403 /*
1404 * Save a directory entry name hint for a given index.
1405 * The directory cnode (dcp) must be locked.
1406 */
1407 __private_extern__
1408 void
1409 hfs_savenamehint(struct cnode *dcp, int index, const char * namehint)
1410 {
1411 struct hfs_index *entry;
1412 int len;
1413
1414 if (index > 0) {
1415 len = strlen(namehint);
1416 MALLOC(entry, struct hfs_index *, len + sizeof(struct hfs_index),
1417 M_TEMP, M_WAITOK);
1418 entry->hi_index = index;
1419 entry->hi_thread = current_act();
1420 bcopy(namehint, entry->hi_name, len + 1);
1421 SLIST_INSERT_HEAD(&dcp->c_indexlist, entry, hi_link);
1422 }
1423 }
1424
1425 /*
1426 * Release the directory entry name hint for a given index.
1427 * The directory cnode (dcp) must be locked.
1428 */
1429 __private_extern__
1430 void
1431 hfs_relnamehint(struct cnode *dcp, int index)
1432 {
1433 struct hfs_index *entry;
1434 void *self;
1435
1436 if (index > 0) {
1437 self = current_act();
1438 SLIST_FOREACH(entry, &dcp->c_indexlist, hi_link) {
1439 if ((entry->hi_index == index)
1440 && (entry->hi_thread == self)) {
1441 SLIST_REMOVE(&dcp->c_indexlist, entry, hfs_index,
1442 hi_link);
1443 FREE(entry, M_TEMP);
1444 break;
1445 }
1446 }
1447 }
1448 }
1449
1450 /*
1451 * Release all directory entry name hints.
1452 */
1453 __private_extern__
1454 void
1455 hfs_relnamehints(struct cnode *dcp)
1456 {
1457 struct hfs_index *entry;
1458 struct hfs_index *next;
1459
1460 if (!SLIST_EMPTY(&dcp->c_indexlist)) {
1461 for(entry = SLIST_FIRST(&dcp->c_indexlist);
1462 entry != NULL;
1463 entry = next) {
1464 next = SLIST_NEXT(entry, hi_link);
1465 SLIST_REMOVE(&dcp->c_indexlist, entry, hfs_index, hi_link);
1466 FREE(entry, M_TEMP);
1467 }
1468 }
1469 }
1470
1471
1472 /*
1473 * Perform a case-insensitive compare of two UTF-8 filenames.
1474 *
1475 * Returns 0 if the strings match.
1476 */
1477 __private_extern__
1478 int
1479 hfs_namecmp(const char *str1, size_t len1, const char *str2, size_t len2)
1480 {
1481 u_int16_t *ustr1, *ustr2;
1482 size_t ulen1, ulen2;
1483 size_t maxbytes;
1484 int cmp = -1;
1485
1486 if (len1 != len2)
1487 return (cmp);
1488
1489 maxbytes = kHFSPlusMaxFileNameChars << 1;
1490 MALLOC(ustr1, u_int16_t *, maxbytes << 1, M_TEMP, M_WAITOK);
1491 ustr2 = ustr1 + (maxbytes >> 1);
1492
1493 if (utf8_decodestr(str1, len1, ustr1, &ulen1, maxbytes, ':', 0) != 0)
1494 goto out;
1495 if (utf8_decodestr(str2, len2, ustr2, &ulen2, maxbytes, ':', 0) != 0)
1496 goto out;
1497
1498 cmp = FastUnicodeCompare(ustr1, ulen1>>1, ustr2, ulen2>>1);
1499 out:
1500 FREE(ustr1, M_TEMP);
1501 return (cmp);
1502 }
1503
1504
1505 __private_extern__
1506 int
1507 hfs_early_journal_init(struct hfsmount *hfsmp, HFSPlusVolumeHeader *vhp,
1508 void *_args, int embeddedOffset, int mdb_offset,
1509 HFSMasterDirectoryBlock *mdbp, struct ucred *cred)
1510 {
1511 JournalInfoBlock *jibp;
1512 struct buf *jinfo_bp, *bp;
1513 int sectors_per_fsblock, arg_flags=0, arg_tbufsz=0;
1514 int retval, blksize = hfsmp->hfs_phys_block_size;
1515 struct vnode *devvp;
1516 struct hfs_mount_args *args = _args;
1517
1518 devvp = hfsmp->hfs_devvp;
1519
1520 if (args != NULL && (args->flags & HFSFSMNT_EXTENDED_ARGS)) {
1521 arg_flags = args->journal_flags;
1522 arg_tbufsz = args->journal_tbuffer_size;
1523 }
1524
1525 sectors_per_fsblock = SWAP_BE32(vhp->blockSize) / blksize;
1526
1527 retval = meta_bread(devvp,
1528 embeddedOffset/blksize +
1529 (SWAP_BE32(vhp->journalInfoBlock)*sectors_per_fsblock),
1530 SWAP_BE32(vhp->blockSize), cred, &jinfo_bp);
1531 if (retval)
1532 return retval;
1533
1534 jibp = (JournalInfoBlock *)jinfo_bp->b_data;
1535 jibp->flags = SWAP_BE32(jibp->flags);
1536 jibp->offset = SWAP_BE64(jibp->offset);
1537 jibp->size = SWAP_BE64(jibp->size);
1538
1539 if (jibp->flags & kJIJournalInFSMask) {
1540 hfsmp->jvp = hfsmp->hfs_devvp;
1541 } else {
1542 printf("hfs: journal not stored in fs! don't know what to do.\n");
1543 brelse(jinfo_bp);
1544 return EINVAL;
1545 }
1546
1547 // save this off for the hack-y check in hfs_remove()
1548 hfsmp->jnl_start = jibp->offset / SWAP_BE32(vhp->blockSize);
1549 hfsmp->jnl_size = jibp->size;
1550
1551 if (jibp->flags & kJIJournalNeedInitMask) {
1552 printf("hfs: Initializing the journal (joffset 0x%llx sz 0x%llx)...\n",
1553 jibp->offset + (off_t)embeddedOffset, jibp->size);
1554 hfsmp->jnl = journal_create(hfsmp->jvp,
1555 jibp->offset + (off_t)embeddedOffset,
1556 jibp->size,
1557 devvp,
1558 blksize,
1559 arg_flags,
1560 arg_tbufsz,
1561 hfs_sync_metadata, hfsmp->hfs_mp);
1562
1563 // no need to start a transaction here... if this were to fail
1564 // we'd just re-init it on the next mount.
1565 jibp->flags &= ~kJIJournalNeedInitMask;
1566 jibp->flags = SWAP_BE32(jibp->flags);
1567 jibp->offset = SWAP_BE64(jibp->offset);
1568 jibp->size = SWAP_BE64(jibp->size);
1569 bwrite(jinfo_bp);
1570 jinfo_bp = NULL;
1571 jibp = NULL;
1572 } else {
1573 //printf("hfs: Opening the journal (joffset 0x%llx sz 0x%llx vhp_blksize %d)...\n",
1574 // jibp->offset + (off_t)embeddedOffset,
1575 // jibp->size, SWAP_BE32(vhp->blockSize));
1576
1577 hfsmp->jnl = journal_open(hfsmp->jvp,
1578 jibp->offset + (off_t)embeddedOffset,
1579 jibp->size,
1580 devvp,
1581 blksize,
1582 arg_flags,
1583 arg_tbufsz,
1584 hfs_sync_metadata, hfsmp->hfs_mp);
1585
1586 brelse(jinfo_bp);
1587 jinfo_bp = NULL;
1588 jibp = NULL;
1589
1590 if (hfsmp->jnl && mdbp) {
1591 // reload the mdb because it could have changed
1592 // if the journal had to be replayed.
1593 if (mdb_offset == 0) {
1594 mdb_offset = (embeddedOffset / blksize) + HFS_PRI_SECTOR(blksize);
1595 }
1596 retval = meta_bread(devvp, mdb_offset, blksize, cred, &bp);
1597 if (retval) {
1598 brelse(bp);
1599 printf("hfs: failed to reload the mdb after opening the journal (retval %d)!\n",
1600 retval);
1601 return retval;
1602 }
1603 bcopy(bp->b_data + HFS_PRI_OFFSET(blksize), mdbp, 512);
1604 brelse(bp);
1605 bp = NULL;
1606 }
1607 }
1608
1609
1610 //printf("journal @ 0x%x\n", hfsmp->jnl);
1611
1612 // if we expected the journal to be there and we couldn't
1613 // create it or open it then we have to bail out.
1614 if (hfsmp->jnl == NULL) {
1615 printf("hfs: early jnl init: failed to open/create the journal (retval %d).\n", retval);
1616 return EINVAL;
1617 }
1618
1619 return 0;
1620 }
1621
1622
1623 //
1624 // This function will go and re-locate the .journal_info_block and
1625 // the .journal files in case they moved (which can happen if you
1626 // run Norton SpeedDisk). If we fail to find either file we just
1627 // disable journaling for this volume and return. We turn off the
1628 // journaling bit in the vcb and assume it will get written to disk
1629 // later (if it doesn't on the next mount we'd do the same thing
1630 // again which is harmless). If we disable journaling we don't
1631 // return an error so that the volume is still mountable.
1632 //
1633 // If the info we find for the .journal_info_block and .journal files
1634 // isn't what we had stored, we re-set our cached info and proceed
1635 // with opening the journal normally.
1636 //
1637 static int
1638 hfs_late_journal_init(struct hfsmount *hfsmp, HFSPlusVolumeHeader *vhp, void *_args)
1639 {
1640 JournalInfoBlock *jibp;
1641 struct buf *jinfo_bp, *bp;
1642 int sectors_per_fsblock, arg_flags=0, arg_tbufsz=0;
1643 int retval, need_flush = 0, write_jibp = 0;
1644 struct vnode *devvp;
1645 struct cat_attr jib_attr, jattr;
1646 struct cat_fork jib_fork, jfork;
1647 ExtendedVCB *vcb;
1648 u_long fid;
1649 struct hfs_mount_args *args = _args;
1650
1651 devvp = hfsmp->hfs_devvp;
1652 vcb = HFSTOVCB(hfsmp);
1653
1654 if (args != NULL && (args->flags & HFSFSMNT_EXTENDED_ARGS)) {
1655 if (args->journal_disable) {
1656 return 0;
1657 }
1658
1659 arg_flags = args->journal_flags;
1660 arg_tbufsz = args->journal_tbuffer_size;
1661 }
1662
1663 fid = GetFileInfo(vcb, kRootDirID, ".journal_info_block", &jib_attr, &jib_fork);
1664 if (fid == 0 || jib_fork.cf_extents[0].startBlock == 0 || jib_fork.cf_size == 0) {
1665 printf("hfs: can't find the .journal_info_block! disabling journaling (start: %d).\n",
1666 jib_fork.cf_extents[0].startBlock);
1667 vcb->vcbAtrb &= ~kHFSVolumeJournaledMask;
1668 return 0;
1669 }
1670 hfsmp->hfs_jnlinfoblkid = fid;
1671
1672 // make sure the journal_info_block begins where we think it should.
1673 if (SWAP_BE32(vhp->journalInfoBlock) != jib_fork.cf_extents[0].startBlock) {
1674 printf("hfs: The journal_info_block moved (was: %d; is: %d). Fixing up\n",
1675 SWAP_BE32(vhp->journalInfoBlock), jib_fork.cf_extents[0].startBlock);
1676
1677 vcb->vcbJinfoBlock = jib_fork.cf_extents[0].startBlock;
1678 vhp->journalInfoBlock = SWAP_BE32(jib_fork.cf_extents[0].startBlock);
1679 }
1680
1681
1682 sectors_per_fsblock = SWAP_BE32(vhp->blockSize) / hfsmp->hfs_phys_block_size;
1683 retval = meta_bread(devvp,
1684 vcb->hfsPlusIOPosOffset / hfsmp->hfs_phys_block_size +
1685 (SWAP_BE32(vhp->journalInfoBlock)*sectors_per_fsblock),
1686 SWAP_BE32(vhp->blockSize), NOCRED, &jinfo_bp);
1687 if (retval) {
1688 printf("hfs: can't read journal info block. disabling journaling.\n");
1689 vcb->vcbAtrb &= ~kHFSVolumeJournaledMask;
1690 return 0;
1691 }
1692
1693 jibp = (JournalInfoBlock *)jinfo_bp->b_data;
1694 jibp->flags = SWAP_BE32(jibp->flags);
1695 jibp->offset = SWAP_BE64(jibp->offset);
1696 jibp->size = SWAP_BE64(jibp->size);
1697
1698 fid = GetFileInfo(vcb, kRootDirID, ".journal", &jattr, &jfork);
1699 if (fid == 0 || jfork.cf_extents[0].startBlock == 0 || jfork.cf_size == 0) {
1700 printf("hfs: can't find the journal file! disabling journaling (start: %d)\n",
1701 jfork.cf_extents[0].startBlock);
1702 brelse(jinfo_bp);
1703 vcb->vcbAtrb &= ~kHFSVolumeJournaledMask;
1704 return 0;
1705 }
1706 hfsmp->hfs_jnlfileid = fid;
1707
1708 // make sure the journal file begins where we think it should.
1709 if ((jibp->offset / (u_int64_t)vcb->blockSize) != jfork.cf_extents[0].startBlock) {
1710 printf("hfs: The journal file moved (was: %lld; is: %d). Fixing up\n",
1711 (jibp->offset / (u_int64_t)vcb->blockSize), jfork.cf_extents[0].startBlock);
1712
1713 jibp->offset = (u_int64_t)jfork.cf_extents[0].startBlock * (u_int64_t)vcb->blockSize;
1714 write_jibp = 1;
1715 }
1716
1717 // check the size of the journal file.
1718 if (jibp->size != (u_int64_t)jfork.cf_extents[0].blockCount*vcb->blockSize) {
1719 printf("hfs: The journal file changed size! (was %lld; is %lld). Fixing up.\n",
1720 jibp->size, (u_int64_t)jfork.cf_extents[0].blockCount*vcb->blockSize);
1721
1722 jibp->size = (u_int64_t)jfork.cf_extents[0].blockCount * vcb->blockSize;
1723 write_jibp = 1;
1724 }
1725
1726 if (jibp->flags & kJIJournalInFSMask) {
1727 hfsmp->jvp = hfsmp->hfs_devvp;
1728 } else {
1729 printf("hfs: journal not stored in fs! don't know what to do.\n");
1730 brelse(jinfo_bp);
1731 return EINVAL;
1732 }
1733
1734 // save this off for the hack-y check in hfs_remove()
1735 hfsmp->jnl_start = jibp->offset / SWAP_BE32(vhp->blockSize);
1736 hfsmp->jnl_size = jibp->size;
1737
1738 if (jibp->flags & kJIJournalNeedInitMask) {
1739 printf("hfs: Initializing the journal (joffset 0x%llx sz 0x%llx)...\n",
1740 jibp->offset + (off_t)vcb->hfsPlusIOPosOffset, jibp->size);
1741 hfsmp->jnl = journal_create(hfsmp->jvp,
1742 jibp->offset + (off_t)vcb->hfsPlusIOPosOffset,
1743 jibp->size,
1744 devvp,
1745 hfsmp->hfs_phys_block_size,
1746 arg_flags,
1747 arg_tbufsz,
1748 hfs_sync_metadata, hfsmp->hfs_mp);
1749
1750 // no need to start a transaction here... if this were to fail
1751 // we'd just re-init it on the next mount.
1752 jibp->flags &= ~kJIJournalNeedInitMask;
1753 write_jibp = 1;
1754
1755 } else {
1756 //
1757 // if we weren't the last person to mount this volume
1758 // then we need to throw away the journal because it
1759 // is likely that someone else mucked with the disk.
1760 // if the journal is empty this is no big deal. if the
1761 // disk is dirty this prevents us from replaying the
1762 // journal over top of changes that someone else made.
1763 //
1764 arg_flags |= JOURNAL_RESET;
1765
1766 //printf("hfs: Opening the journal (joffset 0x%llx sz 0x%llx vhp_blksize %d)...\n",
1767 // jibp->offset + (off_t)vcb->hfsPlusIOPosOffset,
1768 // jibp->size, SWAP_BE32(vhp->blockSize));
1769
1770 hfsmp->jnl = journal_open(hfsmp->jvp,
1771 jibp->offset + (off_t)vcb->hfsPlusIOPosOffset,
1772 jibp->size,
1773 devvp,
1774 hfsmp->hfs_phys_block_size,
1775 arg_flags,
1776 arg_tbufsz,
1777 hfs_sync_metadata, hfsmp->hfs_mp);
1778 }
1779
1780
1781 if (write_jibp) {
1782 jibp->flags = SWAP_BE32(jibp->flags);
1783 jibp->offset = SWAP_BE64(jibp->offset);
1784 jibp->size = SWAP_BE64(jibp->size);
1785
1786 bwrite(jinfo_bp);
1787 } else {
1788 brelse(jinfo_bp);
1789 }
1790 jinfo_bp = NULL;
1791 jibp = NULL;
1792
1793 //printf("journal @ 0x%x\n", hfsmp->jnl);
1794
1795 // if we expected the journal to be there and we couldn't
1796 // create it or open it then we have to bail out.
1797 if (hfsmp->jnl == NULL) {
1798 printf("hfs: late jnl init: failed to open/create the journal (retval %d).\n", retval);
1799 return EINVAL;
1800 }
1801
1802 return 0;
1803 }
1804
1805 /*
1806 * Calculate the allocation zone for metadata.
1807 *
1808 * This zone includes the following:
1809 * Allocation Bitmap file
1810 * Overflow Extents file
1811 * Journal file
1812 * Quota files
1813 * Clustered Hot files
1814 * Catalog file
1815 *
1816 * METADATA ALLOCATION ZONE
1817 * ____________________________________________________________________________
1818 * | | | | | | |
1819 * | BM | JF | OEF | CATALOG |---> | HOT FILES |
1820 * |____|____|_____|_______________|______________________________|___________|
1821 *
1822 * <------------------------------- N * 128 MB ------------------------------->
1823 *
1824 */
1825 #define GIGABYTE (u_int64_t)(1024*1024*1024)
1826
1827 #define OVERFLOW_DEFAULT_SIZE (4*1024*1024)
1828 #define OVERFLOW_MAXIMUM_SIZE (128*1024*1024)
1829 #define JOURNAL_DEFAULT_SIZE (8*1024*1024)
1830 #define JOURNAL_MAXIMUM_SIZE (512*1024*1024)
1831 #define HOTBAND_MINIMUM_SIZE (10*1024*1024)
1832 #define HOTBAND_MAXIMUM_SIZE (512*1024*1024)
1833
1834 static void
1835 hfs_metadatazone_init(struct hfsmount *hfsmp)
1836 {
1837 ExtendedVCB *vcb;
1838 struct BTreeInfoRec btinfo;
1839 u_int64_t fs_size;
1840 u_int64_t zonesize;
1841 u_int64_t temp;
1842 u_int64_t filesize;
1843 u_int32_t blk;
1844 int items;
1845
1846 vcb = HFSTOVCB(hfsmp);
1847 fs_size = (u_int64_t)vcb->blockSize * (u_int64_t)vcb->totalBlocks;
1848
1849 /*
1850 * For volumes less than 10 GB, don't bother.
1851 */
1852 if (fs_size < ((u_int64_t)10 * GIGABYTE))
1853 return;
1854 /*
1855 * Skip non-journaled volumes as well.
1856 */
1857 if (hfsmp->jnl == NULL)
1858 return;
1859
1860 /*
1861 * Start with allocation bitmap (a fixed size).
1862 */
1863 zonesize = roundup(vcb->totalBlocks / 8, vcb->vcbVBMIOSize);
1864
1865 /*
1866 * Overflow Extents file gets 4 MB per 100 GB.
1867 */
1868 items = fs_size / ((u_int64_t)100 * GIGABYTE);
1869 filesize = (u_int64_t)(items + 1) * OVERFLOW_DEFAULT_SIZE;
1870 if (filesize > OVERFLOW_MAXIMUM_SIZE)
1871 filesize = OVERFLOW_MAXIMUM_SIZE;
1872 zonesize += filesize;
1873 hfsmp->hfs_overflow_maxblks = filesize / vcb->blockSize;
1874
1875 /*
1876 * Plan for at least 8 MB of journal for each
1877 * 100 GB of disk space (up to a 512 MB).
1878 */
1879 items = fs_size / ((u_int64_t)100 * GIGABYTE);
1880 filesize = (u_int64_t)(items + 1) * JOURNAL_DEFAULT_SIZE;
1881 if (filesize > JOURNAL_MAXIMUM_SIZE)
1882 filesize = JOURNAL_MAXIMUM_SIZE;
1883 zonesize += filesize;
1884
1885 /*
1886 * Catalog file gets 10 MB per 1 GB.
1887 *
1888 * How about considering the current catalog size (used nodes * node size)
1889 * and the current file data size to help estimate the required
1890 * catalog size.
1891 */
1892 filesize = MIN((fs_size / 1024) * 10, GIGABYTE);
1893 hfsmp->hfs_catalog_maxblks = filesize / vcb->blockSize;
1894 zonesize += filesize;
1895
1896 /*
1897 * Add space for hot file region.
1898 *
1899 * ...for now, use 5 MB per 1 GB (0.5 %)
1900 */
1901 filesize = (fs_size / 1024) * 5;
1902 if (filesize > HOTBAND_MAXIMUM_SIZE)
1903 filesize = HOTBAND_MAXIMUM_SIZE;
1904 else if (filesize < HOTBAND_MINIMUM_SIZE)
1905 filesize = HOTBAND_MINIMUM_SIZE;
1906 /*
1907 * Calculate user quota file requirements.
1908 */
1909 items = QF_USERS_PER_GB * (fs_size / GIGABYTE);
1910 if (items < QF_MIN_USERS)
1911 items = QF_MIN_USERS;
1912 else if (items > QF_MAX_USERS)
1913 items = QF_MAX_USERS;
1914 if (!powerof2(items)) {
1915 int x = items;
1916 items = 4;
1917 while (x>>1 != 1) {
1918 x = x >> 1;
1919 items = items << 1;
1920 }
1921 }
1922 filesize += (items + 1) * sizeof(struct dqblk);
1923 /*
1924 * Calculate group quota file requirements.
1925 *
1926 */
1927 items = QF_GROUPS_PER_GB * (fs_size / GIGABYTE);
1928 if (items < QF_MIN_GROUPS)
1929 items = QF_MIN_GROUPS;
1930 else if (items > QF_MAX_GROUPS)
1931 items = QF_MAX_GROUPS;
1932 if (!powerof2(items)) {
1933 int x = items;
1934 items = 4;
1935 while (x>>1 != 1) {
1936 x = x >> 1;
1937 items = items << 1;
1938 }
1939 }
1940 filesize += (items + 1) * sizeof(struct dqblk);
1941 hfsmp->hfs_hotfile_maxblks = filesize / vcb->blockSize;
1942 zonesize += filesize;
1943
1944 /*
1945 * Round up entire zone to a bitmap block's worth.
1946 * The extra space goes to the catalog file and hot file area.
1947 */
1948 temp = zonesize;
1949 zonesize = roundup(zonesize, vcb->vcbVBMIOSize * 8 * vcb->blockSize);
1950 temp = zonesize - temp; /* temp has extra space */
1951 filesize += temp / 3;
1952 hfsmp->hfs_catalog_maxblks += (temp - (temp / 3)) / vcb->blockSize;
1953
1954 /* Convert to allocation blocks. */
1955 blk = zonesize / vcb->blockSize;
1956
1957 /* The default metadata zone location is at the start of volume. */
1958 hfsmp->hfs_metazone_start = 1;
1959 hfsmp->hfs_metazone_end = blk - 1;
1960
1961 /* The default hotfile area is at the end of the zone. */
1962 hfsmp->hfs_hotfile_start = blk - (filesize / vcb->blockSize);
1963 hfsmp->hfs_hotfile_end = hfsmp->hfs_metazone_end;
1964 hfsmp->hfs_hotfile_freeblks = hfs_hotfile_freeblocks(hfsmp);
1965 #if 0
1966 printf("HFS: metadata zone is %d to %d\n", hfsmp->hfs_metazone_start, hfsmp->hfs_metazone_end);
1967 printf("HFS: hot file band is %d to %d\n", hfsmp->hfs_hotfile_start, hfsmp->hfs_hotfile_end);
1968 printf("HFS: hot file band free blocks = %d\n", hfsmp->hfs_hotfile_freeblks);
1969 #endif
1970 hfsmp->hfs_flags |= HFS_METADATA_ZONE;
1971 }
1972
1973
1974 static u_int32_t
1975 hfs_hotfile_freeblocks(struct hfsmount *hfsmp)
1976 {
1977 ExtendedVCB *vcb = HFSTOVCB(hfsmp);
1978 int freeblocks;
1979
1980 freeblocks = MetaZoneFreeBlocks(vcb);
1981 /* Minus Extents overflow file reserve. */
1982 freeblocks -=
1983 hfsmp->hfs_overflow_maxblks - VTOF(vcb->extentsRefNum)->ff_blocks;
1984 /* Minus catalog file reserve. */
1985 freeblocks -=
1986 hfsmp->hfs_catalog_maxblks - VTOF(vcb->catalogRefNum)->ff_blocks;
1987 if (freeblocks < 0)
1988 freeblocks = 0;
1989
1990 return MIN(freeblocks, hfsmp->hfs_hotfile_maxblks);
1991 }
1992
1993 /*
1994 * Determine if a file is a "virtual" metadata file.
1995 * This includes journal and quota files.
1996 */
1997 __private_extern__
1998 int
1999 hfs_virtualmetafile(struct cnode *cp)
2000 {
2001 char * filename;
2002
2003
2004 if (cp->c_parentcnid != kHFSRootFolderID)
2005 return (0);
2006
2007 filename = cp->c_desc.cd_nameptr;
2008 if (filename == NULL)
2009 return (0);
2010
2011 if ((strcmp(filename, ".journal") == 0) ||
2012 (strcmp(filename, ".journal_info_block") == 0) ||
2013 (strcmp(filename, ".quota.user") == 0) ||
2014 (strcmp(filename, ".quota.group") == 0) ||
2015 (strcmp(filename, ".hotfiles.btree") == 0))
2016 return (1);
2017
2018 return (0);
2019 }
2020