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