]> git.saurik.com Git - apple/xnu.git/blob - bsd/hfs/hfs_vfsutils.c
xnu-517.9.4.tar.gz
[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
1390 if (index > 0) {
1391 SLIST_FOREACH(entry, &dcp->c_indexlist, hi_link) {
1392 if (entry->hi_index == index)
1393 return (entry->hi_name);
1394 }
1395 }
1396
1397 return (NULL);
1398 }
1399
1400 /*
1401 * Save a directory entry name hint for a given index.
1402 * The directory cnode (dcp) must be locked.
1403 */
1404 __private_extern__
1405 void
1406 hfs_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;
1416 bcopy(namehint, entry->hi_name, len + 1);
1417 SLIST_INSERT_HEAD(&dcp->c_indexlist, entry, hi_link);
1418 }
1419 }
1420
1421 /*
1422 * Release the directory entry name hint for a given index.
1423 * The directory cnode (dcp) must be locked.
1424 */
1425 __private_extern__
1426 void
1427 hfs_relnamehint(struct cnode *dcp, int index)
1428 {
1429 struct hfs_index *entry;
1430
1431 if (index > 0) {
1432 SLIST_FOREACH(entry, &dcp->c_indexlist, hi_link) {
1433 if (entry->hi_index == index) {
1434 SLIST_REMOVE(&dcp->c_indexlist, entry, hfs_index,
1435 hi_link);
1436 FREE(entry, M_TEMP);
1437 break;
1438 }
1439 }
1440 }
1441 }
1442
1443 /*
1444 * Release all directory entry name hints.
1445 */
1446 __private_extern__
1447 void
1448 hfs_relnamehints(struct cnode *dcp)
1449 {
1450 struct hfs_index *entry;
1451 struct hfs_index *next;
1452
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);
1460 }
1461 }
1462 }
1463
1464
1465 /*
1466 * Perform a case-insensitive compare of two UTF-8 filenames.
1467 *
1468 * Returns 0 if the strings match.
1469 */
1470 __private_extern__
1471 int
1472 hfs_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);
1492 out:
1493 FREE(ustr1, M_TEMP);
1494 return (cmp);
1495 }
1496
1497
1498 __private_extern__
1499 int
1500 hfs_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);
1542 hfsmp->jnl_size = jibp->size;
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);
1560 jibp->offset = SWAP_BE64(jibp->offset);
1561 jibp->size = SWAP_BE64(jibp->size);
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.
1586 if (mdb_offset == 0) {
1587 mdb_offset = (embeddedOffset / blksize) + HFS_PRI_SECTOR(blksize);
1588 }
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) {
1608 printf("hfs: early jnl init: failed to open/create the journal (retval %d).\n", retval);
1609 return EINVAL;
1610 }
1611
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 //
1630 static int
1631 hfs_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);
1729 hfsmp->jnl_size = jibp->size;
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) {
1791 printf("hfs: late jnl init: failed to open/create the journal (retval %d).\n", retval);
1792 return EINVAL;
1793 }
1794
1795 return 0;
1796 }
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
1827 static void
1828 hfs_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
1967 static u_int32_t
1968 hfs_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__
1991 int
1992 hfs_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