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