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