]> git.saurik.com Git - apple/xnu.git/blob - bsd/hfs/hfs_vfsutils.c
43e5ae8be942e7b46ba2332a411a2083546e953d
[apple/xnu.git] / bsd / hfs / hfs_vfsutils.c
1 /*
2 * Copyright (c) 2000-2007 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28 /* @(#)hfs_vfsutils.c 4.0
29 *
30 * (c) 1997-2002 Apple Computer, Inc. All Rights Reserved
31 *
32 * hfs_vfsutils.c -- Routines that go between the HFS layer and the VFS.
33 *
34 */
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/kernel.h>
38 #include <sys/malloc.h>
39 #include <sys/stat.h>
40 #include <sys/mount.h>
41 #include <sys/buf.h>
42 #include <sys/buf_internal.h>
43 #include <sys/ubc.h>
44 #include <sys/unistd.h>
45 #include <sys/utfconv.h>
46 #include <sys/kauth.h>
47 #include <sys/fcntl.h>
48 #include <sys/vnode_internal.h>
49
50 #include <libkern/OSAtomic.h>
51
52 #include "hfs.h"
53 #include "hfs_catalog.h"
54 #include "hfs_dbg.h"
55 #include "hfs_mount.h"
56 #include "hfs_endian.h"
57 #include "hfs_cnode.h"
58 #include "hfs_fsctl.h"
59
60 #include "hfscommon/headers/FileMgrInternal.h"
61 #include "hfscommon/headers/BTreesInternal.h"
62 #include "hfscommon/headers/HFSUnicodeWrappers.h"
63
64 static void ReleaseMetaFileVNode(struct vnode *vp);
65 static int hfs_late_journal_init(struct hfsmount *hfsmp, HFSPlusVolumeHeader *vhp, void *_args);
66
67 static void hfs_metadatazone_init(struct hfsmount *);
68 static u_int32_t hfs_hotfile_freeblocks(struct hfsmount *);
69
70
71 //*******************************************************************************
72 // Note: Finder information in the HFS/HFS+ metadata are considered opaque and
73 // hence are not in the right byte order on little endian machines. It is
74 // the responsibility of the finder and other clients to swap the data.
75 //*******************************************************************************
76
77 //*******************************************************************************
78 // Routine: hfs_MountHFSVolume
79 //
80 //
81 //*******************************************************************************
82 unsigned char hfs_catname[] = "Catalog B-tree";
83 unsigned char hfs_extname[] = "Extents B-tree";
84 unsigned char hfs_vbmname[] = "Volume Bitmap";
85 unsigned char hfs_attrname[] = "Attribute B-tree";
86 unsigned char hfs_startupname[] = "Startup File";
87
88
89 __private_extern__
90 OSErr hfs_MountHFSVolume(struct hfsmount *hfsmp, HFSMasterDirectoryBlock *mdb,
91 __unused struct proc *p)
92 {
93 ExtendedVCB *vcb = HFSTOVCB(hfsmp);
94 int error;
95 ByteCount utf8chars;
96 struct cat_desc cndesc;
97 struct cat_attr cnattr;
98 struct cat_fork fork;
99
100 /* Block size must be a multiple of 512 */
101 if (SWAP_BE32(mdb->drAlBlkSiz) == 0 ||
102 (SWAP_BE32(mdb->drAlBlkSiz) & 0x01FF) != 0)
103 return (EINVAL);
104
105 /* don't mount a writeable volume if its dirty, it must be cleaned by fsck_hfs */
106 if (((hfsmp->hfs_flags & HFS_READ_ONLY) == 0) &&
107 ((SWAP_BE16(mdb->drAtrb) & kHFSVolumeUnmountedMask) == 0)) {
108 return (EINVAL);
109 }
110 hfsmp->hfs_flags |= HFS_STANDARD;
111 /*
112 * The MDB seems OK: transfer info from it into VCB
113 * Note - the VCB starts out clear (all zeros)
114 *
115 */
116 vcb->vcbSigWord = SWAP_BE16 (mdb->drSigWord);
117 vcb->vcbCrDate = to_bsd_time(LocalToUTC(SWAP_BE32(mdb->drCrDate)));
118 vcb->localCreateDate = SWAP_BE32 (mdb->drCrDate);
119 vcb->vcbLsMod = to_bsd_time(LocalToUTC(SWAP_BE32(mdb->drLsMod)));
120 vcb->vcbAtrb = SWAP_BE16 (mdb->drAtrb);
121 vcb->vcbNmFls = SWAP_BE16 (mdb->drNmFls);
122 vcb->vcbVBMSt = SWAP_BE16 (mdb->drVBMSt);
123 vcb->nextAllocation = SWAP_BE16 (mdb->drAllocPtr);
124 vcb->totalBlocks = SWAP_BE16 (mdb->drNmAlBlks);
125 vcb->allocLimit = vcb->totalBlocks;
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_logical_block_size);
150 vcb->vcbVBMIOSize = kHFSBlockSize;
151
152 hfsmp->hfs_alt_id_sector = HFS_ALT_SECTOR(hfsmp->hfs_logical_block_size,
153 hfsmp->hfs_logical_block_count);
154
155 bzero(&cndesc, sizeof(cndesc));
156 cndesc.cd_parentcnid = kHFSRootParentID;
157 cndesc.cd_flags |= CD_ISMETA;
158 bzero(&cnattr, sizeof(cnattr));
159 cnattr.ca_linkcount = 1;
160 cnattr.ca_mode = S_IFREG;
161 bzero(&fork, sizeof(fork));
162
163 /*
164 * Set up Extents B-tree vnode
165 */
166 cndesc.cd_nameptr = hfs_extname;
167 cndesc.cd_namelen = strlen((char *)hfs_extname);
168 cndesc.cd_cnid = cnattr.ca_fileid = kHFSExtentsFileID;
169 fork.cf_size = SWAP_BE32(mdb->drXTFlSize);
170 fork.cf_blocks = fork.cf_size / vcb->blockSize;
171 fork.cf_clump = SWAP_BE32(mdb->drXTClpSiz);
172 fork.cf_vblocks = 0;
173 fork.cf_extents[0].startBlock = SWAP_BE16(mdb->drXTExtRec[0].startBlock);
174 fork.cf_extents[0].blockCount = SWAP_BE16(mdb->drXTExtRec[0].blockCount);
175 fork.cf_extents[1].startBlock = SWAP_BE16(mdb->drXTExtRec[1].startBlock);
176 fork.cf_extents[1].blockCount = SWAP_BE16(mdb->drXTExtRec[1].blockCount);
177 fork.cf_extents[2].startBlock = SWAP_BE16(mdb->drXTExtRec[2].startBlock);
178 fork.cf_extents[2].blockCount = SWAP_BE16(mdb->drXTExtRec[2].blockCount);
179 cnattr.ca_blocks = fork.cf_blocks;
180
181 error = hfs_getnewvnode(hfsmp, NULL, NULL, &cndesc, 0, &cnattr, &fork,
182 &hfsmp->hfs_extents_vp);
183 if (error) goto MtVolErr;
184 error = MacToVFSError(BTOpenPath(VTOF(hfsmp->hfs_extents_vp),
185 (KeyCompareProcPtr)CompareExtentKeys));
186 if (error) {
187 hfs_unlock(VTOC(hfsmp->hfs_extents_vp));
188 goto MtVolErr;
189 }
190 hfsmp->hfs_extents_cp = VTOC(hfsmp->hfs_extents_vp);
191
192 /*
193 * Set up Catalog B-tree vnode...
194 */
195 cndesc.cd_nameptr = hfs_catname;
196 cndesc.cd_namelen = strlen((char *)hfs_catname);
197 cndesc.cd_cnid = cnattr.ca_fileid = kHFSCatalogFileID;
198 fork.cf_size = SWAP_BE32(mdb->drCTFlSize);
199 fork.cf_blocks = fork.cf_size / vcb->blockSize;
200 fork.cf_clump = SWAP_BE32(mdb->drCTClpSiz);
201 fork.cf_vblocks = 0;
202 fork.cf_extents[0].startBlock = SWAP_BE16(mdb->drCTExtRec[0].startBlock);
203 fork.cf_extents[0].blockCount = SWAP_BE16(mdb->drCTExtRec[0].blockCount);
204 fork.cf_extents[1].startBlock = SWAP_BE16(mdb->drCTExtRec[1].startBlock);
205 fork.cf_extents[1].blockCount = SWAP_BE16(mdb->drCTExtRec[1].blockCount);
206 fork.cf_extents[2].startBlock = SWAP_BE16(mdb->drCTExtRec[2].startBlock);
207 fork.cf_extents[2].blockCount = SWAP_BE16(mdb->drCTExtRec[2].blockCount);
208 cnattr.ca_blocks = fork.cf_blocks;
209
210 error = hfs_getnewvnode(hfsmp, NULL, NULL, &cndesc, 0, &cnattr, &fork,
211 &hfsmp->hfs_catalog_vp);
212 if (error) {
213 hfs_unlock(VTOC(hfsmp->hfs_extents_vp));
214 goto MtVolErr;
215 }
216 error = MacToVFSError(BTOpenPath(VTOF(hfsmp->hfs_catalog_vp),
217 (KeyCompareProcPtr)CompareCatalogKeys));
218 if (error) {
219 hfs_unlock(VTOC(hfsmp->hfs_catalog_vp));
220 hfs_unlock(VTOC(hfsmp->hfs_extents_vp));
221 goto MtVolErr;
222 }
223 hfsmp->hfs_catalog_cp = VTOC(hfsmp->hfs_catalog_vp);
224
225 /*
226 * Set up dummy Allocation file vnode (used only for locking bitmap)
227 */
228 cndesc.cd_nameptr = hfs_vbmname;
229 cndesc.cd_namelen = strlen((char *)hfs_vbmname);
230 cndesc.cd_cnid = cnattr.ca_fileid = kHFSAllocationFileID;
231 bzero(&fork, sizeof(fork));
232 cnattr.ca_blocks = 0;
233
234 error = hfs_getnewvnode(hfsmp, NULL, NULL, &cndesc, 0, &cnattr, &fork,
235 &hfsmp->hfs_allocation_vp);
236 if (error) {
237 hfs_unlock(VTOC(hfsmp->hfs_catalog_vp));
238 hfs_unlock(VTOC(hfsmp->hfs_extents_vp));
239 goto MtVolErr;
240 }
241 hfsmp->hfs_allocation_cp = VTOC(hfsmp->hfs_allocation_vp);
242
243 /* mark the volume dirty (clear clean unmount bit) */
244 vcb->vcbAtrb &= ~kHFSVolumeUnmountedMask;
245
246 if (error == noErr)
247 {
248 error = cat_idlookup(hfsmp, kHFSRootFolderID, 0, NULL, NULL, NULL);
249 }
250
251 if ( error == noErr )
252 {
253 if ( !(vcb->vcbAtrb & kHFSVolumeHardwareLockMask) ) // if the disk is not write protected
254 {
255 MarkVCBDirty( vcb ); // mark VCB dirty so it will be written
256 }
257 }
258
259 /*
260 * all done with system files so we can unlock now...
261 */
262 hfs_unlock(VTOC(hfsmp->hfs_allocation_vp));
263 hfs_unlock(VTOC(hfsmp->hfs_catalog_vp));
264 hfs_unlock(VTOC(hfsmp->hfs_extents_vp));
265
266 goto CmdDone;
267
268 //-- Release any resources allocated so far before exiting with an error:
269 MtVolErr:
270 ReleaseMetaFileVNode(hfsmp->hfs_catalog_vp);
271 ReleaseMetaFileVNode(hfsmp->hfs_extents_vp);
272
273 CmdDone:
274 return (error);
275 }
276
277 //*******************************************************************************
278 // Routine: hfs_MountHFSPlusVolume
279 //
280 //
281 //*******************************************************************************
282
283 __private_extern__
284 OSErr hfs_MountHFSPlusVolume(struct hfsmount *hfsmp, HFSPlusVolumeHeader *vhp,
285 off_t embeddedOffset, u_int64_t disksize, __unused struct proc *p, void *args, kauth_cred_t cred)
286 {
287 register ExtendedVCB *vcb;
288 struct cat_desc cndesc;
289 struct cat_attr cnattr;
290 struct cat_fork cfork;
291 u_int32_t blockSize;
292 daddr64_t spare_sectors;
293 struct BTreeInfoRec btinfo;
294 u_int16_t signature;
295 u_int16_t hfs_version;
296 int i;
297 OSErr retval;
298
299 signature = SWAP_BE16(vhp->signature);
300 hfs_version = SWAP_BE16(vhp->version);
301
302 if (signature == kHFSPlusSigWord) {
303 if (hfs_version != kHFSPlusVersion) {
304 printf("hfs_mount: invalid HFS+ version: %d\n", hfs_version);
305 return (EINVAL);
306 }
307 } else if (signature == kHFSXSigWord) {
308 if (hfs_version != kHFSXVersion) {
309 printf("hfs_mount: invalid HFSX version: %d\n", hfs_version);
310 return (EINVAL);
311 }
312 /* The in-memory signature is always 'H+'. */
313 signature = kHFSPlusSigWord;
314 hfsmp->hfs_flags |= HFS_X;
315 } else {
316 /* Removed printf for invalid HFS+ signature because it gives
317 * false error for UFS root volume
318 */
319 return (EINVAL);
320 }
321
322 /* Block size must be at least 512 and a power of 2 */
323 blockSize = SWAP_BE32(vhp->blockSize);
324 if (blockSize < 512 || !powerof2(blockSize))
325 return (EINVAL);
326
327 /* don't mount a writable volume if its dirty, it must be cleaned by fsck_hfs */
328 if ((hfsmp->hfs_flags & HFS_READ_ONLY) == 0 && hfsmp->jnl == NULL &&
329 (SWAP_BE32(vhp->attributes) & kHFSVolumeUnmountedMask) == 0)
330 return (EINVAL);
331
332 /* Make sure we can live with the physical block size. */
333 if ((disksize & (hfsmp->hfs_logical_block_size - 1)) ||
334 (embeddedOffset & (hfsmp->hfs_logical_block_size - 1)) ||
335 (blockSize < hfsmp->hfs_logical_block_size)) {
336 return (ENXIO);
337 }
338
339 /* If allocation block size is less than the physical
340 * block size, we assume that the physical block size
341 * is same as logical block size. The physical block
342 * size value is used to round down the offsets for
343 * reading and writing the primary and alternate volume
344 * headers at physical block boundary and will cause
345 * problems if it is less than the block size.
346 */
347 if (blockSize < hfsmp->hfs_physical_block_size) {
348 hfsmp->hfs_physical_block_size = hfsmp->hfs_logical_block_size;
349 }
350
351 /*
352 * The VolumeHeader seems OK: transfer info from it into VCB
353 * Note - the VCB starts out clear (all zeros)
354 */
355 vcb = HFSTOVCB(hfsmp);
356
357 vcb->vcbSigWord = signature;
358 vcb->vcbJinfoBlock = SWAP_BE32(vhp->journalInfoBlock);
359 vcb->vcbLsMod = to_bsd_time(SWAP_BE32(vhp->modifyDate));
360 vcb->vcbAtrb = SWAP_BE32(vhp->attributes);
361 vcb->vcbClpSiz = SWAP_BE32(vhp->rsrcClumpSize);
362 vcb->vcbNxtCNID = SWAP_BE32(vhp->nextCatalogID);
363 vcb->vcbVolBkUp = to_bsd_time(SWAP_BE32(vhp->backupDate));
364 vcb->vcbWrCnt = SWAP_BE32(vhp->writeCount);
365 vcb->vcbFilCnt = SWAP_BE32(vhp->fileCount);
366 vcb->vcbDirCnt = SWAP_BE32(vhp->folderCount);
367
368 /* copy 32 bytes of Finder info */
369 bcopy(vhp->finderInfo, vcb->vcbFndrInfo, sizeof(vhp->finderInfo));
370
371 vcb->vcbAlBlSt = 0; /* hfs+ allocation blocks start at first block of volume */
372 if ((hfsmp->hfs_flags & HFS_READ_ONLY) == 0)
373 vcb->vcbWrCnt++; /* compensate for write of Volume Header on last flush */
374
375 /* Now fill in the Extended VCB info */
376 vcb->nextAllocation = SWAP_BE32(vhp->nextAllocation);
377 vcb->totalBlocks = SWAP_BE32(vhp->totalBlocks);
378 vcb->allocLimit = vcb->totalBlocks;
379 vcb->freeBlocks = SWAP_BE32(vhp->freeBlocks);
380 vcb->blockSize = blockSize;
381 vcb->encodingsBitmap = SWAP_BE64(vhp->encodingsBitmap);
382 vcb->localCreateDate = SWAP_BE32(vhp->createDate);
383
384 vcb->hfsPlusIOPosOffset = embeddedOffset;
385
386 /* Default to no free block reserve */
387 vcb->reserveBlocks = 0;
388
389 /*
390 * Update the logical block size in the mount struct
391 * (currently set up from the wrapper MDB) using the
392 * new blocksize value:
393 */
394 hfsmp->hfs_logBlockSize = BestBlockSizeFit(vcb->blockSize, MAXBSIZE, hfsmp->hfs_logical_block_size);
395 vcb->vcbVBMIOSize = min(vcb->blockSize, MAXPHYSIO);
396
397 /*
398 * Validate and initialize the location of the alternate volume header.
399 */
400 spare_sectors = hfsmp->hfs_logical_block_count -
401 (((daddr64_t)vcb->totalBlocks * blockSize) /
402 hfsmp->hfs_logical_block_size);
403
404 if (spare_sectors > (daddr64_t)(blockSize / hfsmp->hfs_logical_block_size)) {
405 hfsmp->hfs_alt_id_sector = 0; /* partition has grown! */
406 } else {
407 hfsmp->hfs_alt_id_sector = (hfsmp->hfsPlusIOPosOffset / hfsmp->hfs_logical_block_size) +
408 HFS_ALT_SECTOR(hfsmp->hfs_logical_block_size,
409 hfsmp->hfs_logical_block_count);
410 }
411
412 bzero(&cndesc, sizeof(cndesc));
413 cndesc.cd_parentcnid = kHFSRootParentID;
414 cndesc.cd_flags |= CD_ISMETA;
415 bzero(&cnattr, sizeof(cnattr));
416 cnattr.ca_linkcount = 1;
417 cnattr.ca_mode = S_IFREG;
418
419 /*
420 * Set up Extents B-tree vnode
421 */
422 cndesc.cd_nameptr = hfs_extname;
423 cndesc.cd_namelen = strlen((char *)hfs_extname);
424 cndesc.cd_cnid = cnattr.ca_fileid = kHFSExtentsFileID;
425
426 cfork.cf_size = SWAP_BE64 (vhp->extentsFile.logicalSize);
427 cfork.cf_new_size= 0;
428 cfork.cf_clump = SWAP_BE32 (vhp->extentsFile.clumpSize);
429 cfork.cf_blocks = SWAP_BE32 (vhp->extentsFile.totalBlocks);
430 cfork.cf_vblocks = 0;
431 cnattr.ca_blocks = cfork.cf_blocks;
432 for (i = 0; i < kHFSPlusExtentDensity; i++) {
433 cfork.cf_extents[i].startBlock =
434 SWAP_BE32 (vhp->extentsFile.extents[i].startBlock);
435 cfork.cf_extents[i].blockCount =
436 SWAP_BE32 (vhp->extentsFile.extents[i].blockCount);
437 }
438 retval = hfs_getnewvnode(hfsmp, NULL, NULL, &cndesc, 0, &cnattr, &cfork,
439 &hfsmp->hfs_extents_vp);
440 if (retval)
441 goto ErrorExit;
442 hfsmp->hfs_extents_cp = VTOC(hfsmp->hfs_extents_vp);
443 hfs_unlock(hfsmp->hfs_extents_cp);
444
445 retval = MacToVFSError(BTOpenPath(VTOF(hfsmp->hfs_extents_vp),
446 (KeyCompareProcPtr) CompareExtentKeysPlus));
447 if (retval)
448 goto ErrorExit;
449 /*
450 * Set up Catalog B-tree vnode
451 */
452 cndesc.cd_nameptr = hfs_catname;
453 cndesc.cd_namelen = strlen((char *)hfs_catname);
454 cndesc.cd_cnid = cnattr.ca_fileid = kHFSCatalogFileID;
455
456 cfork.cf_size = SWAP_BE64 (vhp->catalogFile.logicalSize);
457 cfork.cf_clump = SWAP_BE32 (vhp->catalogFile.clumpSize);
458 cfork.cf_blocks = SWAP_BE32 (vhp->catalogFile.totalBlocks);
459 cfork.cf_vblocks = 0;
460 cnattr.ca_blocks = cfork.cf_blocks;
461 for (i = 0; i < kHFSPlusExtentDensity; i++) {
462 cfork.cf_extents[i].startBlock =
463 SWAP_BE32 (vhp->catalogFile.extents[i].startBlock);
464 cfork.cf_extents[i].blockCount =
465 SWAP_BE32 (vhp->catalogFile.extents[i].blockCount);
466 }
467 retval = hfs_getnewvnode(hfsmp, NULL, NULL, &cndesc, 0, &cnattr, &cfork,
468 &hfsmp->hfs_catalog_vp);
469 if (retval) {
470 goto ErrorExit;
471 }
472 hfsmp->hfs_catalog_cp = VTOC(hfsmp->hfs_catalog_vp);
473 hfs_unlock(hfsmp->hfs_catalog_cp);
474
475 retval = MacToVFSError(BTOpenPath(VTOF(hfsmp->hfs_catalog_vp),
476 (KeyCompareProcPtr) CompareExtendedCatalogKeys));
477 if (retval) {
478 goto ErrorExit;
479 }
480 if ((hfsmp->hfs_flags & HFS_X) &&
481 BTGetInformation(VTOF(hfsmp->hfs_catalog_vp), 0, &btinfo) == 0) {
482 if (btinfo.keyCompareType == kHFSBinaryCompare) {
483 hfsmp->hfs_flags |= HFS_CASE_SENSITIVE;
484 /* Install a case-sensitive key compare */
485 (void) BTOpenPath(VTOF(hfsmp->hfs_catalog_vp),
486 (KeyCompareProcPtr)cat_binarykeycompare);
487 }
488 }
489
490 /*
491 * Set up Allocation file vnode
492 */
493 cndesc.cd_nameptr = hfs_vbmname;
494 cndesc.cd_namelen = strlen((char *)hfs_vbmname);
495 cndesc.cd_cnid = cnattr.ca_fileid = kHFSAllocationFileID;
496
497 cfork.cf_size = SWAP_BE64 (vhp->allocationFile.logicalSize);
498 cfork.cf_clump = SWAP_BE32 (vhp->allocationFile.clumpSize);
499 cfork.cf_blocks = SWAP_BE32 (vhp->allocationFile.totalBlocks);
500 cfork.cf_vblocks = 0;
501 cnattr.ca_blocks = cfork.cf_blocks;
502 for (i = 0; i < kHFSPlusExtentDensity; i++) {
503 cfork.cf_extents[i].startBlock =
504 SWAP_BE32 (vhp->allocationFile.extents[i].startBlock);
505 cfork.cf_extents[i].blockCount =
506 SWAP_BE32 (vhp->allocationFile.extents[i].blockCount);
507 }
508 retval = hfs_getnewvnode(hfsmp, NULL, NULL, &cndesc, 0, &cnattr, &cfork,
509 &hfsmp->hfs_allocation_vp);
510 if (retval) {
511 goto ErrorExit;
512 }
513 hfsmp->hfs_allocation_cp = VTOC(hfsmp->hfs_allocation_vp);
514 hfs_unlock(hfsmp->hfs_allocation_cp);
515
516 /*
517 * Set up Attribute B-tree vnode
518 */
519 if (vhp->attributesFile.totalBlocks != 0) {
520 cndesc.cd_nameptr = hfs_attrname;
521 cndesc.cd_namelen = strlen((char *)hfs_attrname);
522 cndesc.cd_cnid = cnattr.ca_fileid = kHFSAttributesFileID;
523
524 cfork.cf_size = SWAP_BE64 (vhp->attributesFile.logicalSize);
525 cfork.cf_clump = SWAP_BE32 (vhp->attributesFile.clumpSize);
526 cfork.cf_blocks = SWAP_BE32 (vhp->attributesFile.totalBlocks);
527 cfork.cf_vblocks = 0;
528 cnattr.ca_blocks = cfork.cf_blocks;
529 for (i = 0; i < kHFSPlusExtentDensity; i++) {
530 cfork.cf_extents[i].startBlock =
531 SWAP_BE32 (vhp->attributesFile.extents[i].startBlock);
532 cfork.cf_extents[i].blockCount =
533 SWAP_BE32 (vhp->attributesFile.extents[i].blockCount);
534 }
535 retval = hfs_getnewvnode(hfsmp, NULL, NULL, &cndesc, 0, &cnattr, &cfork,
536 &hfsmp->hfs_attribute_vp);
537 if (retval) {
538 goto ErrorExit;
539 }
540 hfsmp->hfs_attribute_cp = VTOC(hfsmp->hfs_attribute_vp);
541 hfs_unlock(hfsmp->hfs_attribute_cp);
542 retval = MacToVFSError(BTOpenPath(VTOF(hfsmp->hfs_attribute_vp),
543 (KeyCompareProcPtr) hfs_attrkeycompare));
544 if (retval) {
545 goto ErrorExit;
546 }
547 }
548
549 /*
550 * Set up Startup file vnode
551 */
552 if (vhp->startupFile.totalBlocks != 0) {
553 cndesc.cd_nameptr = hfs_startupname;
554 cndesc.cd_namelen = strlen((char *)hfs_startupname);
555 cndesc.cd_cnid = cnattr.ca_fileid = kHFSStartupFileID;
556
557 cfork.cf_size = SWAP_BE64 (vhp->startupFile.logicalSize);
558 cfork.cf_clump = SWAP_BE32 (vhp->startupFile.clumpSize);
559 cfork.cf_blocks = SWAP_BE32 (vhp->startupFile.totalBlocks);
560 cfork.cf_vblocks = 0;
561 cnattr.ca_blocks = cfork.cf_blocks;
562 for (i = 0; i < kHFSPlusExtentDensity; i++) {
563 cfork.cf_extents[i].startBlock =
564 SWAP_BE32 (vhp->startupFile.extents[i].startBlock);
565 cfork.cf_extents[i].blockCount =
566 SWAP_BE32 (vhp->startupFile.extents[i].blockCount);
567 }
568 retval = hfs_getnewvnode(hfsmp, NULL, NULL, &cndesc, 0, &cnattr, &cfork,
569 &hfsmp->hfs_startup_vp);
570 if (retval) {
571 goto ErrorExit;
572 }
573 hfsmp->hfs_startup_cp = VTOC(hfsmp->hfs_startup_vp);
574 hfs_unlock(hfsmp->hfs_startup_cp);
575 }
576
577 /* Pick up volume name and create date */
578 retval = cat_idlookup(hfsmp, kHFSRootFolderID, 0, &cndesc, &cnattr, NULL);
579 if (retval) {
580 goto ErrorExit;
581 }
582 vcb->vcbCrDate = cnattr.ca_itime;
583 vcb->volumeNameEncodingHint = cndesc.cd_encoding;
584 bcopy(cndesc.cd_nameptr, vcb->vcbVN, min(255, cndesc.cd_namelen));
585 cat_releasedesc(&cndesc);
586
587 /* mark the volume dirty (clear clean unmount bit) */
588 vcb->vcbAtrb &= ~kHFSVolumeUnmountedMask;
589 if (hfsmp->jnl && (hfsmp->hfs_flags & HFS_READ_ONLY) == 0) {
590 hfs_flushvolumeheader(hfsmp, TRUE, 0);
591 }
592
593 /* kHFSHasFolderCount is only supported/updated on HFSX volumes */
594 if ((hfsmp->hfs_flags & HFS_X) != 0) {
595 hfsmp->hfs_flags |= HFS_FOLDERCOUNT;
596 }
597
598 //
599 // Check if we need to do late journal initialization. This only
600 // happens if a previous version of MacOS X (or 9) touched the disk.
601 // In that case hfs_late_journal_init() will go re-locate the journal
602 // and journal_info_block files and validate that they're still kosher.
603 //
604 if ( (vcb->vcbAtrb & kHFSVolumeJournaledMask)
605 && (SWAP_BE32(vhp->lastMountedVersion) != kHFSJMountVersion)
606 && (hfsmp->jnl == NULL)) {
607
608 retval = hfs_late_journal_init(hfsmp, vhp, args);
609 if (retval != 0) {
610 hfsmp->jnl = NULL;
611
612 // if the journal failed to open, then set the lastMountedVersion
613 // to be "FSK!" which fsck_hfs will see and force the fsck instead
614 // of just bailing out because the volume is journaled.
615 if (!(hfsmp->hfs_flags & HFS_READ_ONLY)) {
616 HFSPlusVolumeHeader *jvhp;
617 daddr64_t mdb_offset;
618 struct buf *bp = NULL;
619
620 hfsmp->hfs_flags |= HFS_NEED_JNL_RESET;
621
622 mdb_offset = (daddr64_t)((embeddedOffset / blockSize) + HFS_PRI_SECTOR(blockSize));
623
624 retval = (int)buf_meta_bread(hfsmp->hfs_devvp,
625 HFS_PHYSBLK_ROUNDDOWN(mdb_offset, hfsmp->hfs_log_per_phys),
626 hfsmp->hfs_physical_block_size, cred, &bp);
627 if (retval == 0) {
628 jvhp = (HFSPlusVolumeHeader *)(buf_dataptr(bp) + HFS_PRI_OFFSET(hfsmp->hfs_physical_block_size));
629
630 if (SWAP_BE16(jvhp->signature) == kHFSPlusSigWord || SWAP_BE16(jvhp->signature) == kHFSXSigWord) {
631 printf ("hfs(3): Journal replay fail. Writing lastMountVersion as FSK!\n");
632 jvhp->lastMountedVersion = SWAP_BE32(kFSKMountVersion);
633 buf_bwrite(bp);
634 } else {
635 buf_brelse(bp);
636 }
637 bp = NULL;
638 } else if (bp) {
639 buf_brelse(bp);
640 // clear this so the error exit path won't try to use it
641 bp = NULL;
642 }
643 }
644
645 retval = EINVAL;
646 goto ErrorExit;
647 } else if (hfsmp->jnl) {
648 vfs_setflags(hfsmp->hfs_mp, (u_int64_t)((unsigned int)MNT_JOURNALED));
649 }
650 } else if (hfsmp->jnl || ((vcb->vcbAtrb & kHFSVolumeJournaledMask) && (hfsmp->hfs_flags & HFS_READ_ONLY))) {
651 struct cat_attr jinfo_attr, jnl_attr;
652
653 if (hfsmp->hfs_flags & HFS_READ_ONLY) {
654 vcb->vcbAtrb &= ~kHFSVolumeJournaledMask;
655 }
656
657 // if we're here we need to fill in the fileid's for the
658 // journal and journal_info_block.
659 hfsmp->hfs_jnlinfoblkid = GetFileInfo(vcb, kRootDirID, ".journal_info_block", &jinfo_attr, NULL);
660 hfsmp->hfs_jnlfileid = GetFileInfo(vcb, kRootDirID, ".journal", &jnl_attr, NULL);
661 if (hfsmp->hfs_jnlinfoblkid == 0 || hfsmp->hfs_jnlfileid == 0) {
662 printf("hfs: danger! couldn't find the file-id's for the journal or journal_info_block\n");
663 printf("hfs: jnlfileid %d, jnlinfoblkid %d\n", hfsmp->hfs_jnlfileid, hfsmp->hfs_jnlinfoblkid);
664 }
665
666 if (hfsmp->hfs_flags & HFS_READ_ONLY) {
667 vcb->vcbAtrb |= kHFSVolumeJournaledMask;
668 }
669
670 if (hfsmp->jnl == NULL) {
671 vfs_clearflags(hfsmp->hfs_mp, (u_int64_t)((unsigned int)MNT_JOURNALED));
672 }
673 }
674
675 /*
676 * Establish a metadata allocation zone.
677 */
678 hfs_metadatazone_init(hfsmp);
679
680 /*
681 * Make any metadata zone adjustments.
682 */
683 if (hfsmp->hfs_flags & HFS_METADATA_ZONE) {
684 /* Keep the roving allocator out of the metadata zone. */
685 if (vcb->nextAllocation >= hfsmp->hfs_metazone_start &&
686 vcb->nextAllocation <= hfsmp->hfs_metazone_end) {
687 HFS_UPDATE_NEXT_ALLOCATION(hfsmp, hfsmp->hfs_metazone_end + 1);
688 }
689 }
690
691 /* Setup private/hidden directories for hardlinks. */
692 hfs_privatedir_init(hfsmp, FILE_HARDLINKS);
693 hfs_privatedir_init(hfsmp, DIR_HARDLINKS);
694
695 if ((hfsmp->hfs_flags & HFS_READ_ONLY) == 0)
696 hfs_remove_orphans(hfsmp);
697
698 if ( !(vcb->vcbAtrb & kHFSVolumeHardwareLockMask) ) // if the disk is not write protected
699 {
700 MarkVCBDirty( vcb ); // mark VCB dirty so it will be written
701 }
702
703 /*
704 * Allow hot file clustering if conditions allow.
705 */
706 if ((hfsmp->hfs_flags & HFS_METADATA_ZONE) &&
707 ((hfsmp->hfs_flags & HFS_READ_ONLY) == 0)) {
708 (void) hfs_recording_init(hfsmp);
709 }
710
711 /* Force ACLs on HFS+ file systems. */
712 vfs_setextendedsecurity(HFSTOVFS(hfsmp));
713
714 /* Check if volume supports writing of extent-based extended attributes */
715 hfs_check_volxattr(hfsmp, HFS_SET_XATTREXTENTS_STATE);
716
717 return (0);
718
719 ErrorExit:
720 /*
721 * A fatal error occurred and the volume cannot be mounted
722 * release any resources that we aquired...
723 */
724 if (hfsmp->hfs_attribute_vp)
725 ReleaseMetaFileVNode(hfsmp->hfs_attribute_vp);
726 ReleaseMetaFileVNode(hfsmp->hfs_allocation_vp);
727 ReleaseMetaFileVNode(hfsmp->hfs_catalog_vp);
728 ReleaseMetaFileVNode(hfsmp->hfs_extents_vp);
729
730 return (retval);
731 }
732
733
734 /*
735 * ReleaseMetaFileVNode
736 *
737 * vp L - -
738 */
739 static void ReleaseMetaFileVNode(struct vnode *vp)
740 {
741 struct filefork *fp;
742
743 if (vp && (fp = VTOF(vp))) {
744 if (fp->fcbBTCBPtr != NULL) {
745 (void)hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK);
746 (void) BTClosePath(fp);
747 hfs_unlock(VTOC(vp));
748 }
749
750 /* release the node even if BTClosePath fails */
751 vnode_recycle(vp);
752 vnode_put(vp);
753 }
754 }
755
756
757 /*************************************************************
758 *
759 * Unmounts a hfs volume.
760 * At this point vflush() has been called (to dump all non-metadata files)
761 *
762 *************************************************************/
763
764 __private_extern__
765 int
766 hfsUnmount( register struct hfsmount *hfsmp, __unused struct proc *p)
767 {
768 /* Get rid of our attribute data vnode (if any). */
769 if (hfsmp->hfs_attrdata_vp) {
770 vnode_t advp = hfsmp->hfs_attrdata_vp;
771
772 if (vnode_get(advp) == 0) {
773 vnode_rele_ext(advp, O_EVTONLY, 0);
774 vnode_put(advp);
775 }
776 hfsmp->hfs_attrdata_vp = NULLVP;
777 }
778
779 if (hfsmp->hfs_startup_vp)
780 ReleaseMetaFileVNode(hfsmp->hfs_startup_vp);
781
782 if (hfsmp->hfs_allocation_vp)
783 ReleaseMetaFileVNode(hfsmp->hfs_allocation_vp);
784
785 if (hfsmp->hfs_attribute_vp)
786 ReleaseMetaFileVNode(hfsmp->hfs_attribute_vp);
787
788 ReleaseMetaFileVNode(hfsmp->hfs_catalog_vp);
789 ReleaseMetaFileVNode(hfsmp->hfs_extents_vp);
790
791 /*
792 * Setting these pointers to NULL so that any references
793 * past this point will fail, and tell us the point of failure.
794 * Also, facilitates a check in hfs_update for a null catalog
795 * vp
796 */
797 hfsmp->hfs_allocation_vp = NULL;
798 hfsmp->hfs_attribute_vp = NULL;
799 hfsmp->hfs_catalog_vp = NULL;
800 hfsmp->hfs_extents_vp = NULL;
801 hfsmp->hfs_startup_vp = NULL;
802
803 return (0);
804 }
805
806
807 /*
808 * Test if fork has overflow extents.
809 */
810 __private_extern__
811 int
812 overflow_extents(struct filefork *fp)
813 {
814 u_long blocks;
815
816 //
817 // If the vnode pointer is NULL then we're being called
818 // from hfs_remove_orphans() with a faked-up filefork
819 // and therefore it has to be an HFS+ volume. Otherwise
820 // we check through the volume header to see what type
821 // of volume we're on.
822 //
823 if (FTOV(fp) == NULL || VTOVCB(FTOV(fp))->vcbSigWord == kHFSPlusSigWord) {
824 if (fp->ff_extents[7].blockCount == 0)
825 return (0);
826
827 blocks = fp->ff_extents[0].blockCount +
828 fp->ff_extents[1].blockCount +
829 fp->ff_extents[2].blockCount +
830 fp->ff_extents[3].blockCount +
831 fp->ff_extents[4].blockCount +
832 fp->ff_extents[5].blockCount +
833 fp->ff_extents[6].blockCount +
834 fp->ff_extents[7].blockCount;
835 } else {
836 if (fp->ff_extents[2].blockCount == 0)
837 return false;
838
839 blocks = fp->ff_extents[0].blockCount +
840 fp->ff_extents[1].blockCount +
841 fp->ff_extents[2].blockCount;
842 }
843
844 return (fp->ff_blocks > blocks);
845 }
846
847
848 /*
849 * Lock HFS system file(s).
850 */
851 __private_extern__
852 int
853 hfs_systemfile_lock(struct hfsmount *hfsmp, int flags, enum hfslocktype locktype)
854 {
855 /*
856 * Locking order is Catalog file, Attributes file, Startup file, Bitmap file, Extents file
857 */
858 if (flags & SFL_CATALOG) {
859
860 #ifdef HFS_CHECK_LOCK_ORDER
861 if (hfsmp->hfs_attribute_cp && hfsmp->hfs_attribute_cp->c_lockowner == current_thread()) {
862 panic("hfs_systemfile_lock: bad lock order (Attributes before Catalog)");
863 }
864 if (hfsmp->hfs_startup_cp && hfsmp->hfs_startup_cp->c_lockowner == current_thread()) {
865 panic("hfs_systemfile_lock: bad lock order (Startup before Catalog)");
866 }
867 if (hfsmp-> hfs_extents_cp && hfsmp->hfs_extents_cp->c_lockowner == current_thread()) {
868 panic("hfs_systemfile_lock: bad lock order (Extents before Catalog)");
869 }
870 #endif /* HFS_CHECK_LOCK_ORDER */
871
872 (void) hfs_lock(hfsmp->hfs_catalog_cp, locktype);
873 /*
874 * When the catalog file has overflow extents then
875 * also acquire the extents b-tree lock if its not
876 * already requested.
877 */
878 if ((flags & SFL_EXTENTS) == 0 &&
879 overflow_extents(VTOF(hfsmp->hfs_catalog_vp))) {
880 flags |= SFL_EXTENTS;
881 }
882 }
883 if (flags & SFL_ATTRIBUTE) {
884
885 #ifdef HFS_CHECK_LOCK_ORDER
886 if (hfsmp->hfs_startup_cp && hfsmp->hfs_startup_cp->c_lockowner == current_thread()) {
887 panic("hfs_systemfile_lock: bad lock order (Startup before Attributes)");
888 }
889 if (hfsmp->hfs_extents_cp && hfsmp->hfs_extents_cp->c_lockowner == current_thread()) {
890 panic("hfs_systemfile_lock: bad lock order (Extents before Attributes)");
891 }
892 #endif /* HFS_CHECK_LOCK_ORDER */
893
894 if (hfsmp->hfs_attribute_cp) {
895 (void) hfs_lock(hfsmp->hfs_attribute_cp, locktype);
896 /*
897 * When the attribute file has overflow extents then
898 * also acquire the extents b-tree lock if its not
899 * already requested.
900 */
901 if ((flags & SFL_EXTENTS) == 0 &&
902 overflow_extents(VTOF(hfsmp->hfs_attribute_vp))) {
903 flags |= SFL_EXTENTS;
904 }
905 } else {
906 flags &= ~SFL_ATTRIBUTE;
907 }
908 }
909 if (flags & SFL_STARTUP) {
910 #ifdef HFS_CHECK_LOCK_ORDER
911 if (hfsmp-> hfs_extents_cp && hfsmp->hfs_extents_cp->c_lockowner == current_thread()) {
912 panic("hfs_systemfile_lock: bad lock order (Extents before Startup)");
913 }
914 #endif /* HFS_CHECK_LOCK_ORDER */
915
916 (void) hfs_lock(hfsmp->hfs_startup_cp, locktype);
917 /*
918 * When the startup file has overflow extents then
919 * also acquire the extents b-tree lock if its not
920 * already requested.
921 */
922 if ((flags & SFL_EXTENTS) == 0 &&
923 overflow_extents(VTOF(hfsmp->hfs_startup_vp))) {
924 flags |= SFL_EXTENTS;
925 }
926 }
927 /*
928 * To prevent locks being taken in the wrong order, the extent lock
929 * gets a bitmap lock as well.
930 */
931 if (flags & (SFL_BITMAP | SFL_EXTENTS)) {
932 /*
933 * Since the only bitmap operations are clearing and
934 * setting bits we always need exclusive access. And
935 * when we have a journal, we can "hide" behind that
936 * lock since we can only change the bitmap from
937 * within a transaction.
938 */
939 if (hfsmp->jnl || (hfsmp->hfs_allocation_cp == NULL)) {
940 flags &= ~SFL_BITMAP;
941 } else {
942 (void) hfs_lock(hfsmp->hfs_allocation_cp, HFS_EXCLUSIVE_LOCK);
943 /* The bitmap lock is also grabbed when only extent lock
944 * was requested. Set the bitmap lock bit in the lock
945 * flags which callers will use during unlock.
946 */
947 flags |= SFL_BITMAP;
948 }
949 }
950 if (flags & SFL_EXTENTS) {
951 /*
952 * Since the extents btree lock is recursive we always
953 * need exclusive access.
954 */
955 (void) hfs_lock(hfsmp->hfs_extents_cp, HFS_EXCLUSIVE_LOCK);
956 }
957 return (flags);
958 }
959
960 /*
961 * unlock HFS system file(s).
962 */
963 __private_extern__
964 void
965 hfs_systemfile_unlock(struct hfsmount *hfsmp, int flags)
966 {
967 struct timeval tv;
968 u_int32_t lastfsync;
969 int numOfLockedBuffs;
970
971 if (hfsmp->jnl == NULL) {
972 microuptime(&tv);
973 lastfsync = tv.tv_sec;
974 }
975 if (flags & SFL_STARTUP && hfsmp->hfs_startup_cp) {
976 hfs_unlock(hfsmp->hfs_startup_cp);
977 }
978 if (flags & SFL_ATTRIBUTE && hfsmp->hfs_attribute_cp) {
979 if (hfsmp->jnl == NULL) {
980 BTGetLastSync((FCB*)VTOF(hfsmp->hfs_attribute_vp), &lastfsync);
981 numOfLockedBuffs = count_lock_queue();
982 if ((numOfLockedBuffs > kMaxLockedMetaBuffers) ||
983 ((numOfLockedBuffs > 1) && ((tv.tv_sec - lastfsync) >
984 kMaxSecsForFsync))) {
985 hfs_btsync(hfsmp->hfs_attribute_vp, HFS_SYNCTRANS);
986 }
987 }
988 hfs_unlock(hfsmp->hfs_attribute_cp);
989 }
990 if (flags & SFL_CATALOG) {
991 if (hfsmp->jnl == NULL) {
992 BTGetLastSync((FCB*)VTOF(hfsmp->hfs_catalog_vp), &lastfsync);
993 numOfLockedBuffs = count_lock_queue();
994 if ((numOfLockedBuffs > kMaxLockedMetaBuffers) ||
995 ((numOfLockedBuffs > 1) && ((tv.tv_sec - lastfsync) >
996 kMaxSecsForFsync))) {
997 hfs_btsync(hfsmp->hfs_catalog_vp, HFS_SYNCTRANS);
998 }
999 }
1000 hfs_unlock(hfsmp->hfs_catalog_cp);
1001 }
1002 if (flags & SFL_BITMAP) {
1003 hfs_unlock(hfsmp->hfs_allocation_cp);
1004 }
1005 if (flags & SFL_EXTENTS) {
1006 if (hfsmp->jnl == NULL) {
1007 BTGetLastSync((FCB*)VTOF(hfsmp->hfs_extents_vp), &lastfsync);
1008 numOfLockedBuffs = count_lock_queue();
1009 if ((numOfLockedBuffs > kMaxLockedMetaBuffers) ||
1010 ((numOfLockedBuffs > 1) && ((tv.tv_sec - lastfsync) >
1011 kMaxSecsForFsync))) {
1012 hfs_btsync(hfsmp->hfs_extents_vp, HFS_SYNCTRANS);
1013 }
1014 }
1015 hfs_unlock(hfsmp->hfs_extents_cp);
1016 }
1017 }
1018
1019
1020 /*
1021 * RequireFileLock
1022 *
1023 * Check to see if a vnode is locked in the current context
1024 * This is to be used for debugging purposes only!!
1025 */
1026 #if HFS_DIAGNOSTIC
1027 void RequireFileLock(FileReference vp, int shareable)
1028 {
1029 int locked;
1030
1031 /* The extents btree and allocation bitmap are always exclusive. */
1032 if (VTOC(vp)->c_fileid == kHFSExtentsFileID ||
1033 VTOC(vp)->c_fileid == kHFSAllocationFileID) {
1034 shareable = 0;
1035 }
1036
1037 locked = VTOC(vp)->c_lockowner == (void *)current_thread();
1038
1039 if (!locked && !shareable) {
1040 switch (VTOC(vp)->c_fileid) {
1041 case kHFSExtentsFileID:
1042 panic("extents btree not locked! v: 0x%08X\n #\n", (u_int)vp);
1043 break;
1044 case kHFSCatalogFileID:
1045 panic("catalog btree not locked! v: 0x%08X\n #\n", (u_int)vp);
1046 break;
1047 case kHFSAllocationFileID:
1048 /* The allocation file can hide behind the jornal lock. */
1049 if (VTOHFS(vp)->jnl == NULL)
1050 panic("allocation file not locked! v: 0x%08X\n #\n", (u_int)vp);
1051 break;
1052 case kHFSStartupFileID:
1053 panic("startup file not locked! v: 0x%08X\n #\n", (u_int)vp);
1054 case kHFSAttributesFileID:
1055 panic("attributes btree not locked! v: 0x%08X\n #\n", (u_int)vp);
1056 break;
1057 }
1058 }
1059 }
1060 #endif
1061
1062
1063 /*
1064 * There are three ways to qualify for ownership rights on an object:
1065 *
1066 * 1. (a) Your UID matches the cnode's UID.
1067 * (b) The object in question is owned by "unknown"
1068 * 2. (a) Permissions on the filesystem are being ignored and
1069 * your UID matches the replacement UID.
1070 * (b) Permissions on the filesystem are being ignored and
1071 * the replacement UID is "unknown".
1072 * 3. You are root.
1073 *
1074 */
1075 int
1076 hfs_owner_rights(struct hfsmount *hfsmp, uid_t cnode_uid, kauth_cred_t cred,
1077 __unused struct proc *p, int invokesuperuserstatus)
1078 {
1079 if ((kauth_cred_getuid(cred) == cnode_uid) || /* [1a] */
1080 (cnode_uid == UNKNOWNUID) || /* [1b] */
1081 ((((unsigned int)vfs_flags(HFSTOVFS(hfsmp))) & MNT_UNKNOWNPERMISSIONS) && /* [2] */
1082 ((kauth_cred_getuid(cred) == hfsmp->hfs_uid) || /* [2a] */
1083 (hfsmp->hfs_uid == UNKNOWNUID))) || /* [2b] */
1084 (invokesuperuserstatus && (suser(cred, 0) == 0))) { /* [3] */
1085 return (0);
1086 } else {
1087 return (EPERM);
1088 }
1089 }
1090
1091
1092 unsigned long BestBlockSizeFit(unsigned long allocationBlockSize,
1093 unsigned long blockSizeLimit,
1094 unsigned long baseMultiple) {
1095 /*
1096 Compute the optimal (largest) block size (no larger than allocationBlockSize) that is less than the
1097 specified limit but still an even multiple of the baseMultiple.
1098 */
1099 int baseBlockCount, blockCount;
1100 unsigned long trialBlockSize;
1101
1102 if (allocationBlockSize % baseMultiple != 0) {
1103 /*
1104 Whoops: the allocation blocks aren't even multiples of the specified base:
1105 no amount of dividing them into even parts will be a multiple, either then!
1106 */
1107 return 512; /* Hope for the best */
1108 };
1109
1110 /* Try the obvious winner first, to prevent 12K allocation blocks, for instance,
1111 from being handled as two 6K logical blocks instead of 3 4K logical blocks.
1112 Even though the former (the result of the loop below) is the larger allocation
1113 block size, the latter is more efficient: */
1114 if (allocationBlockSize % PAGE_SIZE == 0) return PAGE_SIZE;
1115
1116 /* No clear winner exists: pick the largest even fraction <= MAXBSIZE: */
1117 baseBlockCount = allocationBlockSize / baseMultiple; /* Now guaranteed to be an even multiple */
1118
1119 for (blockCount = baseBlockCount; blockCount > 0; --blockCount) {
1120 trialBlockSize = blockCount * baseMultiple;
1121 if (allocationBlockSize % trialBlockSize == 0) { /* An even multiple? */
1122 if ((trialBlockSize <= blockSizeLimit) &&
1123 (trialBlockSize % baseMultiple == 0)) {
1124 return trialBlockSize;
1125 };
1126 };
1127 };
1128
1129 /* Note: we should never get here, since blockCount = 1 should always work,
1130 but this is nice and safe and makes the compiler happy, too ... */
1131 return 512;
1132 }
1133
1134
1135 __private_extern__
1136 u_long
1137 GetFileInfo(ExtendedVCB *vcb, __unused u_int32_t dirid, const char *name,
1138 struct cat_attr *fattr, struct cat_fork *forkinfo)
1139 {
1140 struct hfsmount * hfsmp;
1141 struct cat_desc jdesc;
1142 int lockflags;
1143 int error;
1144
1145 if (vcb->vcbSigWord != kHFSPlusSigWord)
1146 return (0);
1147
1148 hfsmp = VCBTOHFS(vcb);
1149
1150 memset(&jdesc, 0, sizeof(struct cat_desc));
1151 jdesc.cd_parentcnid = kRootDirID;
1152 jdesc.cd_nameptr = (const u_int8_t *)name;
1153 jdesc.cd_namelen = strlen(name);
1154
1155 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
1156 error = cat_lookup(hfsmp, &jdesc, 0, NULL, fattr, forkinfo, NULL);
1157 hfs_systemfile_unlock(hfsmp, lockflags);
1158
1159 if (error == 0) {
1160 return (fattr->ca_fileid);
1161 } else if (hfsmp->hfs_flags & HFS_READ_ONLY) {
1162 return (0);
1163 }
1164
1165 return (0); /* XXX what callers expect on an error */
1166 }
1167
1168
1169 /*
1170 * On HFS Plus Volumes, there can be orphaned files or directories
1171 * These are files or directories that were unlinked while busy.
1172 * If the volume was not cleanly unmounted then some of these may
1173 * have persisted and need to be removed.
1174 */
1175 __private_extern__
1176 void
1177 hfs_remove_orphans(struct hfsmount * hfsmp)
1178 {
1179 struct BTreeIterator * iterator = NULL;
1180 struct FSBufferDescriptor btdata;
1181 struct HFSPlusCatalogFile filerec;
1182 struct HFSPlusCatalogKey * keyp;
1183 struct proc *p = current_proc();
1184 FCB *fcb;
1185 ExtendedVCB *vcb;
1186 char filename[32];
1187 char tempname[32];
1188 size_t namelen;
1189 cat_cookie_t cookie;
1190 int catlock = 0;
1191 int catreserve = 0;
1192 int started_tr = 0;
1193 int lockflags;
1194 int result;
1195 int orphanedlinks = 0;
1196
1197 bzero(&cookie, sizeof(cookie));
1198
1199 if (hfsmp->hfs_flags & HFS_CLEANED_ORPHANS)
1200 return;
1201
1202 vcb = HFSTOVCB(hfsmp);
1203 fcb = VTOF(hfsmp->hfs_catalog_vp);
1204
1205 btdata.bufferAddress = &filerec;
1206 btdata.itemSize = sizeof(filerec);
1207 btdata.itemCount = 1;
1208
1209 MALLOC(iterator, struct BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
1210 bzero(iterator, sizeof(*iterator));
1211
1212 /* Build a key to "temp" */
1213 keyp = (HFSPlusCatalogKey*)&iterator->key;
1214 keyp->parentID = hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid;
1215 keyp->nodeName.length = 4; /* "temp" */
1216 keyp->keyLength = kHFSPlusCatalogKeyMinimumLength + keyp->nodeName.length * 2;
1217 keyp->nodeName.unicode[0] = 't';
1218 keyp->nodeName.unicode[1] = 'e';
1219 keyp->nodeName.unicode[2] = 'm';
1220 keyp->nodeName.unicode[3] = 'p';
1221
1222 /*
1223 * Position the iterator just before the first real temp file/dir.
1224 */
1225 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK);
1226 (void) BTSearchRecord(fcb, iterator, NULL, NULL, iterator);
1227 hfs_systemfile_unlock(hfsmp, lockflags);
1228
1229 /* Visit all the temp files/dirs in the HFS+ private directory. */
1230 for (;;) {
1231 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK);
1232 result = BTIterateRecord(fcb, kBTreeNextRecord, iterator, &btdata, NULL);
1233 hfs_systemfile_unlock(hfsmp, lockflags);
1234 if (result)
1235 break;
1236 if (keyp->parentID != hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid)
1237 break;
1238
1239 (void) utf8_encodestr(keyp->nodeName.unicode, keyp->nodeName.length * 2,
1240 (u_int8_t *)filename, &namelen, sizeof(filename), 0, 0);
1241
1242 (void) snprintf(tempname, sizeof(tempname), "%s%d",
1243 HFS_DELETE_PREFIX, filerec.fileID);
1244
1245 /*
1246 * Delete all files (and directories) named "tempxxx",
1247 * where xxx is the file's cnid in decimal.
1248 *
1249 */
1250 if (bcmp(tempname, filename, namelen) == 0) {
1251 struct filefork dfork;
1252 struct filefork rfork;
1253 struct cnode cnode;
1254
1255 bzero(&dfork, sizeof(dfork));
1256 bzero(&rfork, sizeof(rfork));
1257 bzero(&cnode, sizeof(cnode));
1258
1259 /* Delete any attributes, ignore errors */
1260 (void) hfs_removeallattr(hfsmp, filerec.fileID);
1261
1262 if (hfs_start_transaction(hfsmp) != 0) {
1263 printf("hfs_remove_orphans: failed to start transaction\n");
1264 goto exit;
1265 }
1266 started_tr = 1;
1267
1268 /*
1269 * Reserve some space in the Catalog file.
1270 */
1271 if (cat_preflight(hfsmp, CAT_DELETE, &cookie, p) != 0) {
1272 printf("hfs_remove_orphans: cat_preflight failed\n");
1273 goto exit;
1274 }
1275 catreserve = 1;
1276
1277 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG | SFL_ATTRIBUTE | SFL_EXTENTS | SFL_BITMAP, HFS_EXCLUSIVE_LOCK);
1278 catlock = 1;
1279
1280 /* Build a fake cnode */
1281 cat_convertattr(hfsmp, (CatalogRecord *)&filerec, &cnode.c_attr,
1282 &dfork.ff_data, &rfork.ff_data);
1283 cnode.c_desc.cd_parentcnid = hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid;
1284 cnode.c_desc.cd_nameptr = (const u_int8_t *)filename;
1285 cnode.c_desc.cd_namelen = namelen;
1286 cnode.c_desc.cd_cnid = cnode.c_attr.ca_fileid;
1287 cnode.c_blocks = dfork.ff_blocks + rfork.ff_blocks;
1288
1289 /* Position iterator at previous entry */
1290 if (BTIterateRecord(fcb, kBTreePrevRecord, iterator,
1291 NULL, NULL) != 0) {
1292 break;
1293 }
1294
1295 /* Truncate the file to zero (both forks) */
1296 if (dfork.ff_blocks > 0) {
1297 u_int64_t fsize;
1298
1299 dfork.ff_cp = &cnode;
1300 cnode.c_datafork = &dfork;
1301 cnode.c_rsrcfork = NULL;
1302 fsize = (u_int64_t)dfork.ff_blocks * (u_int64_t)HFSTOVCB(hfsmp)->blockSize;
1303 while (fsize > 0) {
1304 if (fsize > HFS_BIGFILE_SIZE && overflow_extents(&dfork)) {
1305 fsize -= HFS_BIGFILE_SIZE;
1306 } else {
1307 fsize = 0;
1308 }
1309
1310 if (TruncateFileC(vcb, (FCB*)&dfork, fsize, false) != 0) {
1311 printf("error truncting data fork!\n");
1312 break;
1313 }
1314
1315 //
1316 // if we're iteratively truncating this file down,
1317 // then end the transaction and start a new one so
1318 // that no one transaction gets too big.
1319 //
1320 if (fsize > 0 && started_tr) {
1321 /* Drop system file locks before starting
1322 * another transaction to preserve lock order.
1323 */
1324 hfs_systemfile_unlock(hfsmp, lockflags);
1325 catlock = 0;
1326 hfs_end_transaction(hfsmp);
1327
1328 if (hfs_start_transaction(hfsmp) != 0) {
1329 started_tr = 0;
1330 break;
1331 }
1332 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG | SFL_ATTRIBUTE | SFL_EXTENTS | SFL_BITMAP, HFS_EXCLUSIVE_LOCK);
1333 catlock = 1;
1334 }
1335 }
1336 }
1337
1338 if (rfork.ff_blocks > 0) {
1339 rfork.ff_cp = &cnode;
1340 cnode.c_datafork = NULL;
1341 cnode.c_rsrcfork = &rfork;
1342 if (TruncateFileC(vcb, (FCB*)&rfork, 0, false) != 0) {
1343 printf("error truncting rsrc fork!\n");
1344 break;
1345 }
1346 }
1347
1348 /* Remove the file or folder record from the Catalog */
1349 if (cat_delete(hfsmp, &cnode.c_desc, &cnode.c_attr) != 0) {
1350 printf("hfs_remove_orphans: error deleting cat rec for id %d!\n", cnode.c_desc.cd_cnid);
1351 hfs_systemfile_unlock(hfsmp, lockflags);
1352 catlock = 0;
1353 hfs_volupdate(hfsmp, VOL_UPDATE, 0);
1354 break;
1355 }
1356 ++orphanedlinks;
1357
1358 /* Update parent and volume counts */
1359 hfsmp->hfs_private_attr[FILE_HARDLINKS].ca_entries--;
1360 if (cnode.c_attr.ca_mode & S_IFDIR) {
1361 DEC_FOLDERCOUNT(hfsmp, hfsmp->hfs_private_attr[FILE_HARDLINKS]);
1362 }
1363
1364 (void)cat_update(hfsmp, &hfsmp->hfs_private_desc[FILE_HARDLINKS],
1365 &hfsmp->hfs_private_attr[FILE_HARDLINKS], NULL, NULL);
1366
1367 /* Drop locks and end the transaction */
1368 hfs_systemfile_unlock(hfsmp, lockflags);
1369 cat_postflight(hfsmp, &cookie, p);
1370 catlock = catreserve = 0;
1371
1372 /*
1373 Now that Catalog is unlocked, update the volume info, making
1374 sure to differentiate between files and directories
1375 */
1376 if (cnode.c_attr.ca_mode & S_IFDIR) {
1377 hfs_volupdate(hfsmp, VOL_RMDIR, 0);
1378 }
1379 else{
1380 hfs_volupdate(hfsmp, VOL_RMFILE, 0);
1381 }
1382
1383 if (started_tr) {
1384 hfs_end_transaction(hfsmp);
1385 started_tr = 0;
1386 }
1387
1388 } /* end if */
1389 } /* end for */
1390 if (orphanedlinks > 0)
1391 printf("HFS: Removed %d orphaned unlinked files or directories \n", orphanedlinks);
1392 exit:
1393 if (catlock) {
1394 hfs_systemfile_unlock(hfsmp, lockflags);
1395 }
1396 if (catreserve) {
1397 cat_postflight(hfsmp, &cookie, p);
1398 }
1399 if (started_tr) {
1400 hfs_end_transaction(hfsmp);
1401 }
1402
1403 FREE(iterator, M_TEMP);
1404 hfsmp->hfs_flags |= HFS_CLEANED_ORPHANS;
1405 }
1406
1407
1408 /*
1409 * This will return the correct logical block size for a given vnode.
1410 * For most files, it is the allocation block size, for meta data like
1411 * BTrees, this is kept as part of the BTree private nodeSize
1412 */
1413 u_int32_t
1414 GetLogicalBlockSize(struct vnode *vp)
1415 {
1416 u_int32_t logBlockSize;
1417
1418 DBG_ASSERT(vp != NULL);
1419
1420 /* start with default */
1421 logBlockSize = VTOHFS(vp)->hfs_logBlockSize;
1422
1423 if (vnode_issystem(vp)) {
1424 if (VTOF(vp)->fcbBTCBPtr != NULL) {
1425 BTreeInfoRec bTreeInfo;
1426
1427 /*
1428 * We do not lock the BTrees, because if we are getting block..then the tree
1429 * should be locked in the first place.
1430 * We just want the nodeSize wich will NEVER change..so even if the world
1431 * is changing..the nodeSize should remain the same. Which argues why lock
1432 * it in the first place??
1433 */
1434
1435 (void) BTGetInformation (VTOF(vp), kBTreeInfoVersion, &bTreeInfo);
1436
1437 logBlockSize = bTreeInfo.nodeSize;
1438
1439 } else if (VTOC(vp)->c_fileid == kHFSAllocationFileID) {
1440 logBlockSize = VTOVCB(vp)->vcbVBMIOSize;
1441 }
1442 }
1443
1444 DBG_ASSERT(logBlockSize > 0);
1445
1446 return logBlockSize;
1447 }
1448
1449 __private_extern__
1450 u_int32_t
1451 hfs_freeblks(struct hfsmount * hfsmp, int wantreserve)
1452 {
1453 u_int32_t freeblks;
1454 u_int32_t rsrvblks;
1455 u_int32_t loanblks;
1456
1457 /*
1458 * We don't bother taking the mount lock
1459 * to look at these values since the values
1460 * themselves are each updated automically
1461 * on aligned addresses.
1462 */
1463 freeblks = hfsmp->freeBlocks;
1464 rsrvblks = hfsmp->reserveBlocks;
1465 loanblks = hfsmp->loanedBlocks;
1466 if (wantreserve) {
1467 if (freeblks > rsrvblks)
1468 freeblks -= rsrvblks;
1469 else
1470 freeblks = 0;
1471 }
1472 if (freeblks > loanblks)
1473 freeblks -= loanblks;
1474 else
1475 freeblks = 0;
1476
1477 #ifdef HFS_SPARSE_DEV
1478 /*
1479 * When the underlying device is sparse, check the
1480 * available space on the backing store volume.
1481 */
1482 if ((hfsmp->hfs_flags & HFS_HAS_SPARSE_DEVICE) && hfsmp->hfs_backingfs_rootvp) {
1483 struct vfsstatfs *vfsp; /* 272 bytes */
1484 u_int64_t vfreeblks;
1485 u_int32_t loanedblks;
1486 struct mount * backingfs_mp;
1487 struct timeval now;
1488
1489 backingfs_mp = vnode_mount(hfsmp->hfs_backingfs_rootvp);
1490
1491 microtime(&now);
1492 if ((now.tv_sec - hfsmp->hfs_last_backingstatfs) >= 1) {
1493 vfs_update_vfsstat(backingfs_mp, vfs_context_kernel(), VFS_KERNEL_EVENT);
1494 hfsmp->hfs_last_backingstatfs = now.tv_sec;
1495 }
1496
1497 if ((vfsp = vfs_statfs(backingfs_mp))) {
1498 HFS_MOUNT_LOCK(hfsmp, TRUE);
1499 vfreeblks = vfsp->f_bavail;
1500 /* Normalize block count if needed. */
1501 if (vfsp->f_bsize != hfsmp->blockSize) {
1502 vfreeblks = ((u_int64_t)vfreeblks * (u_int64_t)(vfsp->f_bsize)) / hfsmp->blockSize;
1503 }
1504 if (vfreeblks > (unsigned int)hfsmp->hfs_sparsebandblks)
1505 vfreeblks -= hfsmp->hfs_sparsebandblks;
1506 else
1507 vfreeblks = 0;
1508
1509 /* Take into account any delayed allocations. */
1510 loanedblks = 2 * hfsmp->loanedBlocks;
1511 if (vfreeblks > loanedblks)
1512 vfreeblks -= loanedblks;
1513 else
1514 vfreeblks = 0;
1515
1516 freeblks = MIN(vfreeblks, freeblks);
1517 HFS_MOUNT_UNLOCK(hfsmp, TRUE);
1518 }
1519 }
1520 #endif /* HFS_SPARSE_DEV */
1521
1522 return (freeblks);
1523 }
1524
1525 /*
1526 * Map HFS Common errors (negative) to BSD error codes (positive).
1527 * Positive errors (ie BSD errors) are passed through unchanged.
1528 */
1529 short MacToVFSError(OSErr err)
1530 {
1531 if (err >= 0)
1532 return err;
1533
1534 switch (err) {
1535 case dskFulErr: /* -34 */
1536 case btNoSpaceAvail: /* -32733 */
1537 return ENOSPC;
1538 case fxOvFlErr: /* -32750 */
1539 return EOVERFLOW;
1540
1541 case btBadNode: /* -32731 */
1542 return EIO;
1543
1544 case memFullErr: /* -108 */
1545 return ENOMEM; /* +12 */
1546
1547 case cmExists: /* -32718 */
1548 case btExists: /* -32734 */
1549 return EEXIST; /* +17 */
1550
1551 case cmNotFound: /* -32719 */
1552 case btNotFound: /* -32735 */
1553 return ENOENT; /* 28 */
1554
1555 case cmNotEmpty: /* -32717 */
1556 return ENOTEMPTY; /* 66 */
1557
1558 case cmFThdDirErr: /* -32714 */
1559 return EISDIR; /* 21 */
1560
1561 case fxRangeErr: /* -32751 */
1562 return ERANGE;
1563
1564 case bdNamErr: /* -37 */
1565 return ENAMETOOLONG; /* 63 */
1566
1567 case paramErr: /* -50 */
1568 case fileBoundsErr: /* -1309 */
1569 return EINVAL; /* +22 */
1570
1571 case fsBTBadNodeSize:
1572 return ENXIO;
1573
1574 default:
1575 return EIO; /* +5 */
1576 }
1577 }
1578
1579
1580 /*
1581 * Find the current thread's directory hint for a given index.
1582 *
1583 * Requires an exclusive lock on directory cnode.
1584 *
1585 * Use detach if the cnode lock must be dropped while the hint is still active.
1586 */
1587 __private_extern__
1588 directoryhint_t *
1589 hfs_getdirhint(struct cnode *dcp, int index, int detach)
1590 {
1591 struct timeval tv;
1592 directoryhint_t *hint;
1593 boolean_t need_remove, need_init;
1594 const u_int8_t * name;
1595
1596 microuptime(&tv);
1597
1598 /*
1599 * Look for an existing hint first. If not found, create a new one (when
1600 * the list is not full) or recycle the oldest hint. Since new hints are
1601 * always added to the head of the list, the last hint is always the
1602 * oldest.
1603 */
1604 TAILQ_FOREACH(hint, &dcp->c_hintlist, dh_link) {
1605 if (hint->dh_index == index)
1606 break;
1607 }
1608 if (hint != NULL) { /* found an existing hint */
1609 need_init = false;
1610 need_remove = true;
1611 } else { /* cannot find an existing hint */
1612 need_init = true;
1613 if (dcp->c_dirhintcnt < HFS_MAXDIRHINTS) { /* we don't need recycling */
1614 /* Create a default directory hint */
1615 MALLOC_ZONE(hint, directoryhint_t *, sizeof(directoryhint_t), M_HFSDIRHINT, M_WAITOK);
1616 ++dcp->c_dirhintcnt;
1617 need_remove = false;
1618 } else { /* recycle the last (i.e., the oldest) hint */
1619 hint = TAILQ_LAST(&dcp->c_hintlist, hfs_hinthead);
1620 if ((hint->dh_desc.cd_flags & CD_HASBUF) &&
1621 (name = hint->dh_desc.cd_nameptr)) {
1622 hint->dh_desc.cd_nameptr = NULL;
1623 hint->dh_desc.cd_namelen = 0;
1624 hint->dh_desc.cd_flags &= ~CD_HASBUF;
1625 vfs_removename((const char *)name);
1626 }
1627 need_remove = true;
1628 }
1629 }
1630
1631 if (need_remove)
1632 TAILQ_REMOVE(&dcp->c_hintlist, hint, dh_link);
1633
1634 if (detach)
1635 --dcp->c_dirhintcnt;
1636 else
1637 TAILQ_INSERT_HEAD(&dcp->c_hintlist, hint, dh_link);
1638
1639 if (need_init) {
1640 hint->dh_index = index;
1641 hint->dh_desc.cd_flags = 0;
1642 hint->dh_desc.cd_encoding = 0;
1643 hint->dh_desc.cd_namelen = 0;
1644 hint->dh_desc.cd_nameptr = NULL;
1645 hint->dh_desc.cd_parentcnid = dcp->c_fileid;
1646 hint->dh_desc.cd_hint = dcp->c_childhint;
1647 hint->dh_desc.cd_cnid = 0;
1648 }
1649 hint->dh_time = tv.tv_sec;
1650 return (hint);
1651 }
1652
1653 /*
1654 * Release a single directory hint.
1655 *
1656 * Requires an exclusive lock on directory cnode.
1657 */
1658 __private_extern__
1659 void
1660 hfs_reldirhint(struct cnode *dcp, directoryhint_t * relhint)
1661 {
1662 const u_int8_t * name;
1663 directoryhint_t *hint;
1664
1665 /* Check if item is on list (could be detached) */
1666 TAILQ_FOREACH(hint, &dcp->c_hintlist, dh_link) {
1667 if (hint == relhint) {
1668 TAILQ_REMOVE(&dcp->c_hintlist, relhint, dh_link);
1669 --dcp->c_dirhintcnt;
1670 break;
1671 }
1672 }
1673 name = relhint->dh_desc.cd_nameptr;
1674 if ((relhint->dh_desc.cd_flags & CD_HASBUF) && (name != NULL)) {
1675 relhint->dh_desc.cd_nameptr = NULL;
1676 relhint->dh_desc.cd_namelen = 0;
1677 relhint->dh_desc.cd_flags &= ~CD_HASBUF;
1678 vfs_removename((const char *)name);
1679 }
1680 FREE_ZONE(relhint, sizeof(directoryhint_t), M_HFSDIRHINT);
1681 }
1682
1683 /*
1684 * Release directory hints for given directory
1685 *
1686 * Requires an exclusive lock on directory cnode.
1687 */
1688 __private_extern__
1689 void
1690 hfs_reldirhints(struct cnode *dcp, int stale_hints_only)
1691 {
1692 struct timeval tv;
1693 directoryhint_t *hint, *prev;
1694 const u_int8_t * name;
1695
1696 if (stale_hints_only)
1697 microuptime(&tv);
1698
1699 /* searching from the oldest to the newest, so we can stop early when releasing stale hints only */
1700 for (hint = TAILQ_LAST(&dcp->c_hintlist, hfs_hinthead); hint != NULL; hint = prev) {
1701 if (stale_hints_only && (tv.tv_sec - hint->dh_time) < HFS_DIRHINT_TTL)
1702 break; /* stop here if this entry is too new */
1703 name = hint->dh_desc.cd_nameptr;
1704 if ((hint->dh_desc.cd_flags & CD_HASBUF) && (name != NULL)) {
1705 hint->dh_desc.cd_nameptr = NULL;
1706 hint->dh_desc.cd_namelen = 0;
1707 hint->dh_desc.cd_flags &= ~CD_HASBUF;
1708 vfs_removename((const char *)name);
1709 }
1710 prev = TAILQ_PREV(hint, hfs_hinthead, dh_link); /* must save this pointer before calling FREE_ZONE on this node */
1711 TAILQ_REMOVE(&dcp->c_hintlist, hint, dh_link);
1712 FREE_ZONE(hint, sizeof(directoryhint_t), M_HFSDIRHINT);
1713 --dcp->c_dirhintcnt;
1714 }
1715 }
1716
1717 /*
1718 * Insert a detached directory hint back into the list of dirhints.
1719 *
1720 * Requires an exclusive lock on directory cnode.
1721 */
1722 __private_extern__
1723 void
1724 hfs_insertdirhint(struct cnode *dcp, directoryhint_t * hint)
1725 {
1726 directoryhint_t *test;
1727
1728 TAILQ_FOREACH(test, &dcp->c_hintlist, dh_link) {
1729 if (test == hint)
1730 panic("hfs_insertdirhint: hint %p already on list!", hint);
1731 }
1732
1733 TAILQ_INSERT_HEAD(&dcp->c_hintlist, hint, dh_link);
1734 ++dcp->c_dirhintcnt;
1735 }
1736
1737 /*
1738 * Perform a case-insensitive compare of two UTF-8 filenames.
1739 *
1740 * Returns 0 if the strings match.
1741 */
1742 __private_extern__
1743 int
1744 hfs_namecmp(const u_int8_t *str1, size_t len1, const u_int8_t *str2, size_t len2)
1745 {
1746 u_int16_t *ustr1, *ustr2;
1747 size_t ulen1, ulen2;
1748 size_t maxbytes;
1749 int cmp = -1;
1750
1751 if (len1 != len2)
1752 return (cmp);
1753
1754 maxbytes = kHFSPlusMaxFileNameChars << 1;
1755 MALLOC(ustr1, u_int16_t *, maxbytes << 1, M_TEMP, M_WAITOK);
1756 ustr2 = ustr1 + (maxbytes >> 1);
1757
1758 if (utf8_decodestr(str1, len1, ustr1, &ulen1, maxbytes, ':', 0) != 0)
1759 goto out;
1760 if (utf8_decodestr(str2, len2, ustr2, &ulen2, maxbytes, ':', 0) != 0)
1761 goto out;
1762
1763 cmp = FastUnicodeCompare(ustr1, ulen1>>1, ustr2, ulen2>>1);
1764 out:
1765 FREE(ustr1, M_TEMP);
1766 return (cmp);
1767 }
1768
1769
1770 __private_extern__
1771 int
1772 hfs_early_journal_init(struct hfsmount *hfsmp, HFSPlusVolumeHeader *vhp,
1773 void *_args, off_t embeddedOffset, daddr64_t mdb_offset,
1774 HFSMasterDirectoryBlock *mdbp, kauth_cred_t cred)
1775 {
1776 JournalInfoBlock *jibp;
1777 struct buf *jinfo_bp, *bp;
1778 int sectors_per_fsblock, arg_flags=0, arg_tbufsz=0;
1779 int retval;
1780 uint32_t blksize = hfsmp->hfs_logical_block_size;
1781 struct vnode *devvp;
1782 struct hfs_mount_args *args = _args;
1783 u_int32_t jib_flags;
1784 u_int64_t jib_offset;
1785 u_int64_t jib_size;
1786
1787 devvp = hfsmp->hfs_devvp;
1788
1789 if (args != NULL && (args->flags & HFSFSMNT_EXTENDED_ARGS)) {
1790 arg_flags = args->journal_flags;
1791 arg_tbufsz = args->journal_tbuffer_size;
1792 }
1793
1794 sectors_per_fsblock = SWAP_BE32(vhp->blockSize) / blksize;
1795
1796 retval = (int)buf_meta_bread(devvp,
1797 (daddr64_t)((embeddedOffset/blksize) +
1798 (SWAP_BE32(vhp->journalInfoBlock)*sectors_per_fsblock)),
1799 SWAP_BE32(vhp->blockSize), cred, &jinfo_bp);
1800 if (retval)
1801 return retval;
1802
1803 jibp = (JournalInfoBlock *)buf_dataptr(jinfo_bp);
1804 jib_flags = SWAP_BE32(jibp->flags);
1805 jib_offset = SWAP_BE64(jibp->offset);
1806 jib_size = SWAP_BE64(jibp->size);
1807
1808 if (jib_flags & kJIJournalInFSMask) {
1809 hfsmp->jvp = hfsmp->hfs_devvp;
1810 } else {
1811 printf("hfs: journal not stored in fs! don't know what to do.\n");
1812 buf_brelse(jinfo_bp);
1813 return EINVAL;
1814 }
1815
1816 // save this off for the hack-y check in hfs_remove()
1817 hfsmp->jnl_start = jib_offset / SWAP_BE32(vhp->blockSize);
1818 hfsmp->jnl_size = jib_size;
1819
1820 if ((hfsmp->hfs_flags & HFS_READ_ONLY) && (vfs_flags(hfsmp->hfs_mp) & MNT_ROOTFS) == 0) {
1821 // if the file system is read-only, check if the journal is empty.
1822 // if it is, then we can allow the mount. otherwise we have to
1823 // return failure.
1824 retval = journal_is_clean(hfsmp->jvp,
1825 jib_offset + embeddedOffset,
1826 jib_size,
1827 devvp,
1828 hfsmp->hfs_logical_block_size);
1829
1830 hfsmp->jnl = NULL;
1831
1832 buf_brelse(jinfo_bp);
1833
1834 if (retval) {
1835 printf("hfs: early journal init: volume on %s is read-only and journal is dirty. Can not mount volume.\n",
1836 vnode_name(devvp));
1837 }
1838
1839 return retval;
1840 }
1841
1842 if (jib_flags & kJIJournalNeedInitMask) {
1843 printf("hfs: Initializing the journal (joffset 0x%llx sz 0x%llx)...\n",
1844 jib_offset + embeddedOffset, jib_size);
1845 hfsmp->jnl = journal_create(hfsmp->jvp,
1846 jib_offset + embeddedOffset,
1847 jib_size,
1848 devvp,
1849 blksize,
1850 arg_flags,
1851 arg_tbufsz,
1852 hfs_sync_metadata, hfsmp->hfs_mp);
1853
1854 // no need to start a transaction here... if this were to fail
1855 // we'd just re-init it on the next mount.
1856 jib_flags &= ~kJIJournalNeedInitMask;
1857 jibp->flags = SWAP_BE32(jib_flags);
1858 buf_bwrite(jinfo_bp);
1859 jinfo_bp = NULL;
1860 jibp = NULL;
1861 } else {
1862 //printf("hfs: Opening the journal (joffset 0x%llx sz 0x%llx vhp_blksize %d)...\n",
1863 // jib_offset + embeddedOffset,
1864 // jib_size, SWAP_BE32(vhp->blockSize));
1865
1866 hfsmp->jnl = journal_open(hfsmp->jvp,
1867 jib_offset + embeddedOffset,
1868 jib_size,
1869 devvp,
1870 blksize,
1871 arg_flags,
1872 arg_tbufsz,
1873 hfs_sync_metadata, hfsmp->hfs_mp);
1874
1875 buf_brelse(jinfo_bp);
1876 jinfo_bp = NULL;
1877 jibp = NULL;
1878
1879 if (hfsmp->jnl && mdbp) {
1880 // reload the mdb because it could have changed
1881 // if the journal had to be replayed.
1882 if (mdb_offset == 0) {
1883 mdb_offset = (daddr64_t)((embeddedOffset / blksize) + HFS_PRI_SECTOR(blksize));
1884 }
1885 retval = (int)buf_meta_bread(devvp,
1886 HFS_PHYSBLK_ROUNDDOWN(mdb_offset, hfsmp->hfs_log_per_phys),
1887 hfsmp->hfs_physical_block_size, cred, &bp);
1888 if (retval) {
1889 buf_brelse(bp);
1890 printf("hfs: failed to reload the mdb after opening the journal (retval %d)!\n",
1891 retval);
1892 return retval;
1893 }
1894 bcopy((char *)buf_dataptr(bp) + HFS_PRI_OFFSET(hfsmp->hfs_physical_block_size), mdbp, 512);
1895 buf_brelse(bp);
1896 bp = NULL;
1897 }
1898 }
1899
1900
1901 //printf("journal @ 0x%x\n", hfsmp->jnl);
1902
1903 // if we expected the journal to be there and we couldn't
1904 // create it or open it then we have to bail out.
1905 if (hfsmp->jnl == NULL) {
1906 printf("hfs: early jnl init: failed to open/create the journal (retval %d).\n", retval);
1907 return EINVAL;
1908 }
1909
1910 return 0;
1911 }
1912
1913
1914 //
1915 // This function will go and re-locate the .journal_info_block and
1916 // the .journal files in case they moved (which can happen if you
1917 // run Norton SpeedDisk). If we fail to find either file we just
1918 // disable journaling for this volume and return. We turn off the
1919 // journaling bit in the vcb and assume it will get written to disk
1920 // later (if it doesn't on the next mount we'd do the same thing
1921 // again which is harmless). If we disable journaling we don't
1922 // return an error so that the volume is still mountable.
1923 //
1924 // If the info we find for the .journal_info_block and .journal files
1925 // isn't what we had stored, we re-set our cached info and proceed
1926 // with opening the journal normally.
1927 //
1928 static int
1929 hfs_late_journal_init(struct hfsmount *hfsmp, HFSPlusVolumeHeader *vhp, void *_args)
1930 {
1931 JournalInfoBlock *jibp;
1932 struct buf *jinfo_bp;
1933 int sectors_per_fsblock, arg_flags=0, arg_tbufsz=0;
1934 int retval, write_jibp = 0, recreate_journal = 0;
1935 struct vnode *devvp;
1936 struct cat_attr jib_attr, jattr;
1937 struct cat_fork jib_fork, jfork;
1938 ExtendedVCB *vcb;
1939 u_long fid;
1940 struct hfs_mount_args *args = _args;
1941 u_int32_t jib_flags;
1942 u_int64_t jib_offset;
1943 u_int64_t jib_size;
1944
1945 devvp = hfsmp->hfs_devvp;
1946 vcb = HFSTOVCB(hfsmp);
1947
1948 if (args != NULL && (args->flags & HFSFSMNT_EXTENDED_ARGS)) {
1949 if (args->journal_disable) {
1950 return 0;
1951 }
1952
1953 arg_flags = args->journal_flags;
1954 arg_tbufsz = args->journal_tbuffer_size;
1955 }
1956
1957 fid = GetFileInfo(vcb, kRootDirID, ".journal_info_block", &jib_attr, &jib_fork);
1958 if (fid == 0 || jib_fork.cf_extents[0].startBlock == 0 || jib_fork.cf_size == 0) {
1959 printf("hfs: can't find the .journal_info_block! disabling journaling (start: %d).\n",
1960 jib_fork.cf_extents[0].startBlock);
1961 vcb->vcbAtrb &= ~kHFSVolumeJournaledMask;
1962 return 0;
1963 }
1964 hfsmp->hfs_jnlinfoblkid = fid;
1965
1966 // make sure the journal_info_block begins where we think it should.
1967 if (SWAP_BE32(vhp->journalInfoBlock) != jib_fork.cf_extents[0].startBlock) {
1968 printf("hfs: The journal_info_block moved (was: %d; is: %d). Fixing up\n",
1969 SWAP_BE32(vhp->journalInfoBlock), jib_fork.cf_extents[0].startBlock);
1970
1971 vcb->vcbJinfoBlock = jib_fork.cf_extents[0].startBlock;
1972 vhp->journalInfoBlock = SWAP_BE32(jib_fork.cf_extents[0].startBlock);
1973 recreate_journal = 1;
1974 }
1975
1976
1977 sectors_per_fsblock = SWAP_BE32(vhp->blockSize) / hfsmp->hfs_logical_block_size;
1978 retval = (int)buf_meta_bread(devvp,
1979 (daddr64_t)(vcb->hfsPlusIOPosOffset / hfsmp->hfs_logical_block_size +
1980 (SWAP_BE32(vhp->journalInfoBlock)*sectors_per_fsblock)),
1981 SWAP_BE32(vhp->blockSize), NOCRED, &jinfo_bp);
1982 if (retval) {
1983 printf("hfs: can't read journal info block. disabling journaling.\n");
1984 vcb->vcbAtrb &= ~kHFSVolumeJournaledMask;
1985 return 0;
1986 }
1987
1988 jibp = (JournalInfoBlock *)buf_dataptr(jinfo_bp);
1989 jib_flags = SWAP_BE32(jibp->flags);
1990 jib_offset = SWAP_BE64(jibp->offset);
1991 jib_size = SWAP_BE64(jibp->size);
1992
1993 fid = GetFileInfo(vcb, kRootDirID, ".journal", &jattr, &jfork);
1994 if (fid == 0 || jfork.cf_extents[0].startBlock == 0 || jfork.cf_size == 0) {
1995 printf("hfs: can't find the journal file! disabling journaling (start: %d)\n",
1996 jfork.cf_extents[0].startBlock);
1997 buf_brelse(jinfo_bp);
1998 vcb->vcbAtrb &= ~kHFSVolumeJournaledMask;
1999 return 0;
2000 }
2001 hfsmp->hfs_jnlfileid = fid;
2002
2003 // make sure the journal file begins where we think it should.
2004 if ((jib_offset / (u_int64_t)vcb->blockSize) != jfork.cf_extents[0].startBlock) {
2005 printf("hfs: The journal file moved (was: %lld; is: %d). Fixing up\n",
2006 (jib_offset / (u_int64_t)vcb->blockSize), jfork.cf_extents[0].startBlock);
2007
2008 jib_offset = (u_int64_t)jfork.cf_extents[0].startBlock * (u_int64_t)vcb->blockSize;
2009 write_jibp = 1;
2010 recreate_journal = 1;
2011 }
2012
2013 // check the size of the journal file.
2014 if (jib_size != (u_int64_t)jfork.cf_extents[0].blockCount*vcb->blockSize) {
2015 printf("hfs: The journal file changed size! (was %lld; is %lld). Fixing up.\n",
2016 jib_size, (u_int64_t)jfork.cf_extents[0].blockCount*vcb->blockSize);
2017
2018 jib_size = (u_int64_t)jfork.cf_extents[0].blockCount * vcb->blockSize;
2019 write_jibp = 1;
2020 recreate_journal = 1;
2021 }
2022
2023 if (jib_flags & kJIJournalInFSMask) {
2024 hfsmp->jvp = hfsmp->hfs_devvp;
2025 } else {
2026 printf("hfs: journal not stored in fs! don't know what to do.\n");
2027 buf_brelse(jinfo_bp);
2028 return EINVAL;
2029 }
2030
2031 // save this off for the hack-y check in hfs_remove()
2032 hfsmp->jnl_start = jib_offset / SWAP_BE32(vhp->blockSize);
2033 hfsmp->jnl_size = jib_size;
2034
2035 if ((hfsmp->hfs_flags & HFS_READ_ONLY) && (vfs_flags(hfsmp->hfs_mp) & MNT_ROOTFS) == 0) {
2036 // if the file system is read-only, check if the journal is empty.
2037 // if it is, then we can allow the mount. otherwise we have to
2038 // return failure.
2039 retval = journal_is_clean(hfsmp->jvp,
2040 jib_offset + (off_t)vcb->hfsPlusIOPosOffset,
2041 jib_size,
2042 devvp,
2043 hfsmp->hfs_logical_block_size);
2044
2045 hfsmp->jnl = NULL;
2046
2047 buf_brelse(jinfo_bp);
2048
2049 if (retval) {
2050 printf("hfs: late journal init: volume on %s is read-only and journal is dirty. Can not mount volume.\n",
2051 vnode_name(devvp));
2052 }
2053
2054 return retval;
2055 }
2056
2057 if ((jib_flags & kJIJournalNeedInitMask) || recreate_journal) {
2058 printf("hfs: Initializing the journal (joffset 0x%llx sz 0x%llx)...\n",
2059 jib_offset + (off_t)vcb->hfsPlusIOPosOffset, jib_size);
2060 hfsmp->jnl = journal_create(hfsmp->jvp,
2061 jib_offset + (off_t)vcb->hfsPlusIOPosOffset,
2062 jib_size,
2063 devvp,
2064 hfsmp->hfs_logical_block_size,
2065 arg_flags,
2066 arg_tbufsz,
2067 hfs_sync_metadata, hfsmp->hfs_mp);
2068
2069 // no need to start a transaction here... if this were to fail
2070 // we'd just re-init it on the next mount.
2071 jib_flags &= ~kJIJournalNeedInitMask;
2072 write_jibp = 1;
2073
2074 } else {
2075 //
2076 // if we weren't the last person to mount this volume
2077 // then we need to throw away the journal because it
2078 // is likely that someone else mucked with the disk.
2079 // if the journal is empty this is no big deal. if the
2080 // disk is dirty this prevents us from replaying the
2081 // journal over top of changes that someone else made.
2082 //
2083 arg_flags |= JOURNAL_RESET;
2084
2085 //printf("hfs: Opening the journal (joffset 0x%llx sz 0x%llx vhp_blksize %d)...\n",
2086 // jib_offset + (off_t)vcb->hfsPlusIOPosOffset,
2087 // jib_size, SWAP_BE32(vhp->blockSize));
2088
2089 hfsmp->jnl = journal_open(hfsmp->jvp,
2090 jib_offset + (off_t)vcb->hfsPlusIOPosOffset,
2091 jib_size,
2092 devvp,
2093 hfsmp->hfs_logical_block_size,
2094 arg_flags,
2095 arg_tbufsz,
2096 hfs_sync_metadata, hfsmp->hfs_mp);
2097 }
2098
2099
2100 if (write_jibp) {
2101 jibp->flags = SWAP_BE32(jib_flags);
2102 jibp->offset = SWAP_BE64(jib_offset);
2103 jibp->size = SWAP_BE64(jib_size);
2104
2105 buf_bwrite(jinfo_bp);
2106 } else {
2107 buf_brelse(jinfo_bp);
2108 }
2109 jinfo_bp = NULL;
2110 jibp = NULL;
2111
2112 //printf("journal @ 0x%x\n", hfsmp->jnl);
2113
2114 // if we expected the journal to be there and we couldn't
2115 // create it or open it then we have to bail out.
2116 if (hfsmp->jnl == NULL) {
2117 printf("hfs: late jnl init: failed to open/create the journal (retval %d).\n", retval);
2118 return EINVAL;
2119 }
2120
2121 return 0;
2122 }
2123
2124 /*
2125 * Calculate the allocation zone for metadata.
2126 *
2127 * This zone includes the following:
2128 * Allocation Bitmap file
2129 * Overflow Extents file
2130 * Journal file
2131 * Quota files
2132 * Clustered Hot files
2133 * Catalog file
2134 *
2135 * METADATA ALLOCATION ZONE
2136 * ____________________________________________________________________________
2137 * | | | | | | |
2138 * | BM | JF | OEF | CATALOG |---> | HOT FILES |
2139 * |____|____|_____|_______________|______________________________|___________|
2140 *
2141 * <------------------------------- N * 128 MB ------------------------------->
2142 *
2143 */
2144 #define GIGABYTE (u_int64_t)(1024*1024*1024)
2145
2146 #define OVERFLOW_DEFAULT_SIZE (4*1024*1024)
2147 #define OVERFLOW_MAXIMUM_SIZE (128*1024*1024)
2148 #define JOURNAL_DEFAULT_SIZE (8*1024*1024)
2149 #define JOURNAL_MAXIMUM_SIZE (512*1024*1024)
2150 #define HOTBAND_MINIMUM_SIZE (10*1024*1024)
2151 #define HOTBAND_MAXIMUM_SIZE (512*1024*1024)
2152
2153 static void
2154 hfs_metadatazone_init(struct hfsmount *hfsmp)
2155 {
2156 ExtendedVCB *vcb;
2157 u_int64_t fs_size;
2158 u_int64_t zonesize;
2159 u_int64_t temp;
2160 u_int64_t filesize;
2161 u_int32_t blk;
2162 int items;
2163
2164 vcb = HFSTOVCB(hfsmp);
2165 fs_size = (u_int64_t)vcb->blockSize * (u_int64_t)vcb->totalBlocks;
2166
2167 /*
2168 * For volumes less than 10 GB, don't bother.
2169 */
2170 if (fs_size < ((u_int64_t)10 * GIGABYTE))
2171 return;
2172 /*
2173 * Skip non-journaled volumes as well.
2174 */
2175 if (hfsmp->jnl == NULL)
2176 return;
2177
2178 /*
2179 * Start with allocation bitmap (a fixed size).
2180 */
2181 zonesize = roundup(vcb->totalBlocks / 8, vcb->vcbVBMIOSize);
2182
2183 /*
2184 * Overflow Extents file gets 4 MB per 100 GB.
2185 */
2186 items = fs_size / ((u_int64_t)100 * GIGABYTE);
2187 filesize = (u_int64_t)(items + 1) * OVERFLOW_DEFAULT_SIZE;
2188 if (filesize > OVERFLOW_MAXIMUM_SIZE)
2189 filesize = OVERFLOW_MAXIMUM_SIZE;
2190 zonesize += filesize;
2191 hfsmp->hfs_overflow_maxblks = filesize / vcb->blockSize;
2192
2193 /*
2194 * Plan for at least 8 MB of journal for each
2195 * 100 GB of disk space (up to a 512 MB).
2196 */
2197 items = fs_size / ((u_int64_t)100 * GIGABYTE);
2198 filesize = (u_int64_t)(items + 1) * JOURNAL_DEFAULT_SIZE;
2199 if (filesize > JOURNAL_MAXIMUM_SIZE)
2200 filesize = JOURNAL_MAXIMUM_SIZE;
2201 zonesize += filesize;
2202
2203 /*
2204 * Catalog file gets 10 MB per 1 GB.
2205 *
2206 * How about considering the current catalog size (used nodes * node size)
2207 * and the current file data size to help estimate the required
2208 * catalog size.
2209 */
2210 filesize = MIN((fs_size / 1024) * 10, GIGABYTE);
2211 hfsmp->hfs_catalog_maxblks = filesize / vcb->blockSize;
2212 zonesize += filesize;
2213
2214 /*
2215 * Add space for hot file region.
2216 *
2217 * ...for now, use 5 MB per 1 GB (0.5 %)
2218 */
2219 filesize = (fs_size / 1024) * 5;
2220 if (filesize > HOTBAND_MAXIMUM_SIZE)
2221 filesize = HOTBAND_MAXIMUM_SIZE;
2222 else if (filesize < HOTBAND_MINIMUM_SIZE)
2223 filesize = HOTBAND_MINIMUM_SIZE;
2224 /*
2225 * Calculate user quota file requirements.
2226 */
2227 items = QF_USERS_PER_GB * (fs_size / GIGABYTE);
2228 if (items < QF_MIN_USERS)
2229 items = QF_MIN_USERS;
2230 else if (items > QF_MAX_USERS)
2231 items = QF_MAX_USERS;
2232 if (!powerof2(items)) {
2233 int x = items;
2234 items = 4;
2235 while (x>>1 != 1) {
2236 x = x >> 1;
2237 items = items << 1;
2238 }
2239 }
2240 filesize += (items + 1) * sizeof(struct dqblk);
2241 /*
2242 * Calculate group quota file requirements.
2243 *
2244 */
2245 items = QF_GROUPS_PER_GB * (fs_size / GIGABYTE);
2246 if (items < QF_MIN_GROUPS)
2247 items = QF_MIN_GROUPS;
2248 else if (items > QF_MAX_GROUPS)
2249 items = QF_MAX_GROUPS;
2250 if (!powerof2(items)) {
2251 int x = items;
2252 items = 4;
2253 while (x>>1 != 1) {
2254 x = x >> 1;
2255 items = items << 1;
2256 }
2257 }
2258 filesize += (items + 1) * sizeof(struct dqblk);
2259 zonesize += filesize;
2260
2261 /*
2262 * Round up entire zone to a bitmap block's worth.
2263 * The extra space goes to the catalog file and hot file area.
2264 */
2265 temp = zonesize;
2266 zonesize = roundup(zonesize, (u_int64_t)vcb->vcbVBMIOSize * 8 * vcb->blockSize);
2267 temp = zonesize - temp; /* temp has extra space */
2268 filesize += temp / 3;
2269 hfsmp->hfs_catalog_maxblks += (temp - (temp / 3)) / vcb->blockSize;
2270
2271 hfsmp->hfs_hotfile_maxblks = filesize / vcb->blockSize;
2272
2273 /* Convert to allocation blocks. */
2274 blk = zonesize / vcb->blockSize;
2275
2276 /* The default metadata zone location is at the start of volume. */
2277 hfsmp->hfs_metazone_start = 1;
2278 hfsmp->hfs_metazone_end = blk - 1;
2279
2280 /* The default hotfile area is at the end of the zone. */
2281 hfsmp->hfs_hotfile_start = blk - (filesize / vcb->blockSize);
2282 hfsmp->hfs_hotfile_end = hfsmp->hfs_metazone_end;
2283 hfsmp->hfs_hotfile_freeblks = hfs_hotfile_freeblocks(hfsmp);
2284 #if 0
2285 printf("HFS: metadata zone is %d to %d\n", hfsmp->hfs_metazone_start, hfsmp->hfs_metazone_end);
2286 printf("HFS: hot file band is %d to %d\n", hfsmp->hfs_hotfile_start, hfsmp->hfs_hotfile_end);
2287 printf("HFS: hot file band free blocks = %d\n", hfsmp->hfs_hotfile_freeblks);
2288 #endif
2289 hfsmp->hfs_flags |= HFS_METADATA_ZONE;
2290 }
2291
2292
2293 static u_int32_t
2294 hfs_hotfile_freeblocks(struct hfsmount *hfsmp)
2295 {
2296 ExtendedVCB *vcb = HFSTOVCB(hfsmp);
2297 int lockflags;
2298 int freeblocks;
2299
2300 lockflags = hfs_systemfile_lock(hfsmp, SFL_BITMAP, HFS_EXCLUSIVE_LOCK);
2301 freeblocks = MetaZoneFreeBlocks(vcb);
2302 hfs_systemfile_unlock(hfsmp, lockflags);
2303
2304 /* Minus Extents overflow file reserve. */
2305 freeblocks -=
2306 hfsmp->hfs_overflow_maxblks - VTOF(hfsmp->hfs_extents_vp)->ff_blocks;
2307 /* Minus catalog file reserve. */
2308 freeblocks -=
2309 hfsmp->hfs_catalog_maxblks - VTOF(hfsmp->hfs_catalog_vp)->ff_blocks;
2310 if (freeblocks < 0)
2311 freeblocks = 0;
2312
2313 return MIN(freeblocks, hfsmp->hfs_hotfile_maxblks);
2314 }
2315
2316 /*
2317 * Determine if a file is a "virtual" metadata file.
2318 * This includes journal and quota files.
2319 */
2320 __private_extern__
2321 int
2322 hfs_virtualmetafile(struct cnode *cp)
2323 {
2324 const char * filename;
2325
2326
2327 if (cp->c_parentcnid != kHFSRootFolderID)
2328 return (0);
2329
2330 filename = (const char *)cp->c_desc.cd_nameptr;
2331 if (filename == NULL)
2332 return (0);
2333
2334 if ((strncmp(filename, ".journal", sizeof(".journal")) == 0) ||
2335 (strncmp(filename, ".journal_info_block", sizeof(".journal_info_block")) == 0) ||
2336 (strncmp(filename, ".quota.user", sizeof(".quota.user")) == 0) ||
2337 (strncmp(filename, ".quota.group", sizeof(".quota.group")) == 0) ||
2338 (strncmp(filename, ".hotfiles.btree", sizeof(".hotfiles.btree")) == 0))
2339 return (1);
2340
2341 return (0);
2342 }
2343
2344
2345 __private_extern__
2346 int
2347 hfs_start_transaction(struct hfsmount *hfsmp)
2348 {
2349 int ret, unlock_on_err=0;
2350 void * thread = current_thread();
2351
2352 #ifdef HFS_CHECK_LOCK_ORDER
2353 /*
2354 * You cannot start a transaction while holding a system
2355 * file lock. (unless the transaction is nested.)
2356 */
2357 if (hfsmp->jnl && journal_owner(hfsmp->jnl) != thread) {
2358 if (hfsmp->hfs_catalog_cp && hfsmp->hfs_catalog_cp->c_lockowner == thread) {
2359 panic("hfs_start_transaction: bad lock order (cat before jnl)\n");
2360 }
2361 if (hfsmp->hfs_attribute_cp && hfsmp->hfs_attribute_cp->c_lockowner == thread) {
2362 panic("hfs_start_transaction: bad lock order (attr before jnl)\n");
2363 }
2364 if (hfsmp->hfs_extents_cp && hfsmp->hfs_extents_cp->c_lockowner == thread) {
2365 panic("hfs_start_transaction: bad lock order (ext before jnl)\n");
2366 }
2367 }
2368 #endif /* HFS_CHECK_LOCK_ORDER */
2369
2370 if (hfsmp->jnl == NULL || journal_owner(hfsmp->jnl) != thread) {
2371 lck_rw_lock_shared(&hfsmp->hfs_global_lock);
2372 unlock_on_err = 1;
2373 }
2374
2375 if (hfsmp->jnl) {
2376 ret = journal_start_transaction(hfsmp->jnl);
2377 if (ret == 0) {
2378 OSAddAtomic(1, (SInt32 *)&hfsmp->hfs_global_lock_nesting);
2379 }
2380 } else {
2381 ret = 0;
2382 }
2383
2384 if (ret != 0 && unlock_on_err) {
2385 lck_rw_unlock_shared(&hfsmp->hfs_global_lock);
2386 }
2387
2388 return ret;
2389 }
2390
2391 __private_extern__
2392 int
2393 hfs_end_transaction(struct hfsmount *hfsmp)
2394 {
2395 int need_unlock=0, ret;
2396
2397 if ( hfsmp->jnl == NULL
2398 || ( journal_owner(hfsmp->jnl) == current_thread()
2399 && (OSAddAtomic(-1, (SInt32 *)&hfsmp->hfs_global_lock_nesting) == 1)) ) {
2400
2401 need_unlock = 1;
2402 }
2403
2404 if (hfsmp->jnl) {
2405 ret = journal_end_transaction(hfsmp->jnl);
2406 } else {
2407 ret = 0;
2408 }
2409
2410 if (need_unlock) {
2411 lck_rw_unlock_shared(&hfsmp->hfs_global_lock);
2412 }
2413
2414 return ret;
2415 }