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