]> git.saurik.com Git - apple/xnu.git/blame - bsd/hfs/hfs_vfsutils.c
xnu-792.6.22.tar.gz
[apple/xnu.git] / bsd / hfs / hfs_vfsutils.c
CommitLineData
1c79356b 1/*
91447636 2 * Copyright (c) 2000-2005 Apple Computer, Inc. All rights reserved.
1c79356b
A
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
e5568f75
A
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.
1c79356b 11 *
e5568f75
A
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
1c79356b
A
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
e5568f75
A
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.
1c79356b
A
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22/* @(#)hfs_vfsutils.c 4.0
23*
9bccf70c 24* (c) 1997-2002 Apple Computer, Inc. All Rights Reserved
1c79356b
A
25*
26* hfs_vfsutils.c -- Routines that go between the HFS layer and the VFS.
27*
1c79356b
A
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>
1c79356b 34#include <sys/mount.h>
1c79356b
A
35#include <sys/buf.h>
36#include <sys/ubc.h>
37#include <sys/unistd.h>
91447636
A
38#include <sys/utfconv.h>
39#include <sys/kauth.h>
1c79356b
A
40
41#include "hfs.h"
9bccf70c 42#include "hfs_catalog.h"
1c79356b
A
43#include "hfs_dbg.h"
44#include "hfs_mount.h"
45#include "hfs_endian.h"
9bccf70c 46#include "hfs_cnode.h"
1c79356b
A
47
48#include "hfscommon/headers/FileMgrInternal.h"
49#include "hfscommon/headers/BTreesInternal.h"
50#include "hfscommon/headers/HFSUnicodeWrappers.h"
51
1c79356b 52
91447636 53extern int count_lock_queue(void);
1c79356b 54
1c79356b
A
55
56static void ReleaseMetaFileVNode(struct vnode *vp);
b4c24cb9 57static int hfs_late_journal_init(struct hfsmount *hfsmp, HFSPlusVolumeHeader *vhp, void *_args);
1c79356b 58
55e303ae
A
59static void hfs_metadatazone_init(struct hfsmount *);
60static u_int32_t hfs_hotfile_freeblocks(struct hfsmount *);
61
62
63
1c79356b
A
64u_int32_t GetLogicalBlockSize(struct vnode *vp);
65
91447636
A
66extern int hfs_attrkeycompare(HFSPlusAttrKey *searchKey, HFSPlusAttrKey *trialKey);
67
1c79356b
A
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//*******************************************************************************
9bccf70c
A
80char hfs_catname[] = "Catalog B-tree";
81char hfs_extname[] = "Extents B-tree";
82char hfs_vbmname[] = "Volume Bitmap";
91447636 83char hfs_attrname[] = "Attribute B-tree";
9bccf70c
A
84
85char hfs_privdirname[] =
86 "\xE2\x90\x80\xE2\x90\x80\xE2\x90\x80\xE2\x90\x80HFS+ Private Data";
1c79356b 87
55e303ae 88__private_extern__
1c79356b 89OSErr hfs_MountHFSVolume(struct hfsmount *hfsmp, HFSMasterDirectoryBlock *mdb,
d52fe63f 90 struct proc *p)
1c79356b 91{
9bccf70c
A
92 ExtendedVCB *vcb = HFSTOVCB(hfsmp);
93 int error;
1c79356b 94 ByteCount utf8chars;
9bccf70c
A
95 struct cat_desc cndesc;
96 struct cat_attr cnattr;
97 struct cat_fork fork;
1c79356b 98
9bccf70c
A
99 /* Block size must be a multiple of 512 */
100 if (SWAP_BE32(mdb->drAlBlkSiz) == 0 ||
101 (SWAP_BE32(mdb->drAlBlkSiz) & 0x01FF) != 0)
1c79356b
A
102 return (EINVAL);
103
1c79356b 104 /* don't mount a writeable volume if its dirty, it must be cleaned by fsck_hfs */
55e303ae
A
105 if (((hfsmp->hfs_flags & HFS_READ_ONLY) == 0) &&
106 ((SWAP_BE16(mdb->drAtrb) & kHFSVolumeUnmountedMask) == 0)) {
1c79356b 107 return (EINVAL);
55e303ae
A
108 }
109 hfsmp->hfs_flags |= HFS_STANDARD;
1c79356b
A
110 /*
111 * The MDB seems OK: transfer info from it into VCB
112 * Note - the VCB starts out clear (all zeros)
113 *
114 */
9bccf70c
A
115 vcb->vcbSigWord = SWAP_BE16 (mdb->drSigWord);
116 vcb->vcbCrDate = to_bsd_time(LocalToUTC(SWAP_BE32(mdb->drCrDate)));
0b4e3aa0 117 vcb->localCreateDate = SWAP_BE32 (mdb->drCrDate);
9bccf70c
A
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);
1c79356b 134 bcopy(mdb->drFndrInfo, vcb->vcbFndrInfo, sizeof(vcb->vcbFndrInfo));
55e303ae 135 if ((hfsmp->hfs_flags & HFS_READ_ONLY) == 0)
9bccf70c 136 vcb->vcbWrCnt++; /* Compensate for write of MDB on last flush */
1c79356b
A
137
138 /* convert hfs encoded name into UTF-8 string */
9bccf70c 139 error = hfs_to_utf8(vcb, mdb->drVN, NAME_MAX, &utf8chars, vcb->vcbVN);
1c79356b
A
140 /*
141 * When an HFS name cannot be encoded with the current
142 * volume encoding we use MacRoman as a fallback.
143 */
9bccf70c 144 if (error || (utf8chars == 0))
1c79356b
A
145 (void) mac_roman_to_utf8(mdb->drVN, NAME_MAX, &utf8chars, vcb->vcbVN);
146
9bccf70c 147 hfsmp->hfs_logBlockSize = BestBlockSizeFit(vcb->blockSize, MAXBSIZE, hfsmp->hfs_phys_block_size);
0b4e3aa0 148 vcb->vcbVBMIOSize = kHFSBlockSize;
1c79356b 149
91447636
A
150 hfsmp->hfs_alt_id_sector = HFS_ALT_SECTOR(hfsmp->hfs_phys_block_size,
151 hfsmp->hfs_phys_block_count);
1c79356b 152
9bccf70c 153 bzero(&cndesc, sizeof(cndesc));
91447636 154 cndesc.cd_parentcnid = kHFSRootParentID;
55e303ae 155 cndesc.cd_flags |= CD_ISMETA;
9bccf70c
A
156 bzero(&cnattr, sizeof(cnattr));
157 cnattr.ca_nlink = 1;
158 cnattr.ca_mode = S_IFREG;
159 bzero(&fork, sizeof(fork));
160
1c79356b 161 /*
9bccf70c
A
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);
55e303ae 170 fork.cf_vblocks = 0;
9bccf70c
A
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
91447636
A
179 error = hfs_getnewvnode(hfsmp, NULL, NULL, &cndesc, 0, &cnattr, &fork,
180 &hfsmp->hfs_extents_vp);
9bccf70c 181 if (error) goto MtVolErr;
91447636 182 error = MacToVFSError(BTOpenPath(VTOF(hfsmp->hfs_extents_vp),
55e303ae 183 (KeyCompareProcPtr)CompareExtentKeys));
9bccf70c 184 if (error) {
91447636 185 hfs_unlock(VTOC(hfsmp->hfs_extents_vp));
9bccf70c
A
186 goto MtVolErr;
187 }
1c79356b
A
188
189 /*
190 * Set up Catalog B-tree vnode...
191 */
9bccf70c
A
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);
55e303ae 198 fork.cf_vblocks = 0;
9bccf70c
A
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
91447636
A
207 error = hfs_getnewvnode(hfsmp, NULL, NULL, &cndesc, 0, &cnattr, &fork,
208 &hfsmp->hfs_catalog_vp);
9bccf70c 209 if (error) {
91447636 210 hfs_unlock(VTOC(hfsmp->hfs_extents_vp));
9bccf70c
A
211 goto MtVolErr;
212 }
91447636 213 error = MacToVFSError(BTOpenPath(VTOF(hfsmp->hfs_catalog_vp),
55e303ae 214 (KeyCompareProcPtr)CompareCatalogKeys));
9bccf70c 215 if (error) {
91447636
A
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));
9bccf70c
A
235 goto MtVolErr;
236 }
1c79356b
A
237
238 /* mark the volume dirty (clear clean unmount bit) */
239 vcb->vcbAtrb &= ~kHFSVolumeUnmountedMask;
240
1c79356b 241 /*
91447636 242 * all done with system files so we can unlock now...
1c79356b 243 */
91447636
A
244 hfs_unlock(VTOC(hfsmp->hfs_allocation_vp));
245 hfs_unlock(VTOC(hfsmp->hfs_catalog_vp));
246 hfs_unlock(VTOC(hfsmp->hfs_extents_vp));
1c79356b 247
9bccf70c 248 if ( error == noErr )
1c79356b
A
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:
9bccf70c 258MtVolErr:
91447636
A
259 ReleaseMetaFileVNode(hfsmp->hfs_catalog_vp);
260 ReleaseMetaFileVNode(hfsmp->hfs_extents_vp);
1c79356b 261
9bccf70c
A
262CmdDone:
263 return (error);
1c79356b
A
264}
265
266//*******************************************************************************
267// Routine: hfs_MountHFSPlusVolume
268//
269//
270//*******************************************************************************
271
55e303ae 272__private_extern__
1c79356b 273OSErr hfs_MountHFSPlusVolume(struct hfsmount *hfsmp, HFSPlusVolumeHeader *vhp,
91447636 274 off_t embeddedOffset, u_int64_t disksize, struct proc *p, void *args, kauth_cred_t cred)
1c79356b 275{
9bccf70c
A
276 register ExtendedVCB *vcb;
277 struct cat_desc cndesc;
278 struct cat_attr cnattr;
55e303ae 279 struct cat_fork cfork;
9bccf70c 280 UInt32 blockSize;
91447636 281 daddr64_t spare_sectors;
55e303ae
A
282 struct BTreeInfoRec btinfo;
283 u_int16_t signature;
284 u_int16_t version;
285 int i;
9bccf70c 286 OSErr retval;
1c79356b 287
55e303ae
A
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 {
91447636
A
305 /* Removed printf for invalid HFS+ signature because it gives
306 * false error for UFS root volume
307 */
b4c24cb9
A
308 return (EINVAL);
309 }
1c79356b 310
9bccf70c
A
311 /* Block size must be at least 512 and a power of 2 */
312 blockSize = SWAP_BE32(vhp->blockSize);
55e303ae 313 if (blockSize < 512 || !powerof2(blockSize))
9bccf70c 314 return (EINVAL);
1c79356b
A
315
316 /* don't mount a writable volume if its dirty, it must be cleaned by fsck_hfs */
55e303ae
A
317 if ((hfsmp->hfs_flags & HFS_READ_ONLY) == 0 && hfsmp->jnl == NULL &&
318 (SWAP_BE32(vhp->attributes) & kHFSVolumeUnmountedMask) == 0)
1c79356b 319 return (EINVAL);
d52fe63f
A
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)) ||
55e303ae 324 (blockSize < hfsmp->hfs_phys_block_size)) {
d52fe63f
A
325 return (ENXIO);
326 }
1c79356b
A
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
55e303ae 333 vcb->vcbSigWord = signature;
b4c24cb9 334 vcb->vcbJinfoBlock = SWAP_BE32(vhp->journalInfoBlock);
9bccf70c 335 vcb->vcbLsMod = to_bsd_time(SWAP_BE32(vhp->modifyDate));
91447636 336 vcb->vcbAtrb = SWAP_BE32(vhp->attributes);
9bccf70c
A
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);
1c79356b
A
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 */
55e303ae 348 if ((hfsmp->hfs_flags & HFS_READ_ONLY) == 0)
9bccf70c 349 vcb->vcbWrCnt++; /* compensate for write of Volume Header on last flush */
1c79356b 350
9bccf70c
A
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);
55e303ae 355 vcb->blockSize = blockSize;
9bccf70c
A
356 vcb->encodingsBitmap = SWAP_BE64(vhp->encodingsBitmap);
357 vcb->localCreateDate = SWAP_BE32(vhp->createDate);
1c79356b 358
9bccf70c 359 vcb->hfsPlusIOPosOffset = embeddedOffset;
1c79356b 360
9bccf70c
A
361 /* Default to no free block reserve */
362 vcb->reserveBlocks = 0;
1c79356b 363
9bccf70c
A
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);
0b4e3aa0 370 vcb->vcbVBMIOSize = min(vcb->blockSize, MAXPHYSIO);
1c79356b 371
91447636
A
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
9bccf70c 387 bzero(&cndesc, sizeof(cndesc));
91447636 388 cndesc.cd_parentcnid = kHFSRootParentID;
55e303ae 389 cndesc.cd_flags |= CD_ISMETA;
9bccf70c
A
390 bzero(&cnattr, sizeof(cnattr));
391 cnattr.ca_nlink = 1;
392 cnattr.ca_mode = S_IFREG;
1c79356b
A
393
394 /*
9bccf70c
A
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
55e303ae
A
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 }
91447636
A
412 retval = hfs_getnewvnode(hfsmp, NULL, NULL, &cndesc, 0, &cnattr, &cfork,
413 &hfsmp->hfs_extents_vp);
9bccf70c 414
1c79356b 415 if (retval) goto ErrorExit;
91447636 416 retval = MacToVFSError(BTOpenPath(VTOF(hfsmp->hfs_extents_vp),
55e303ae 417 (KeyCompareProcPtr) CompareExtentKeysPlus));
9bccf70c 418 if (retval) {
91447636 419 hfs_unlock(VTOC(hfsmp->hfs_extents_vp));
9bccf70c
A
420 goto ErrorExit;
421 }
1c79356b
A
422
423 /*
9bccf70c 424 * Set up Catalog B-tree vnode
1c79356b 425 */
9bccf70c
A
426 cndesc.cd_nameptr = hfs_catname;
427 cndesc.cd_namelen = strlen(hfs_catname);
428 cndesc.cd_cnid = cnattr.ca_fileid = kHFSCatalogFileID;
429
55e303ae
A
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 }
91447636
A
441 retval = hfs_getnewvnode(hfsmp, NULL, NULL, &cndesc, 0, &cnattr, &cfork,
442 &hfsmp->hfs_catalog_vp);
9bccf70c 443 if (retval) {
91447636 444 hfs_unlock(VTOC(hfsmp->hfs_extents_vp));
9bccf70c
A
445 goto ErrorExit;
446 }
91447636 447 retval = MacToVFSError(BTOpenPath(VTOF(hfsmp->hfs_catalog_vp),
55e303ae 448 (KeyCompareProcPtr) CompareExtendedCatalogKeys));
9bccf70c 449 if (retval) {
91447636
A
450 hfs_unlock(VTOC(hfsmp->hfs_catalog_vp));
451 hfs_unlock(VTOC(hfsmp->hfs_extents_vp));
9bccf70c
A
452 goto ErrorExit;
453 }
55e303ae 454 if ((hfsmp->hfs_flags & HFS_X) &&
91447636 455 BTGetInformation(VTOF(hfsmp->hfs_catalog_vp), 0, &btinfo) == 0) {
55e303ae
A
456 if (btinfo.keyCompareType == kHFSBinaryCompare) {
457 hfsmp->hfs_flags |= HFS_CASE_SENSITIVE;
458 /* Install a case-sensitive key compare */
91447636 459 (void) BTOpenPath(VTOF(hfsmp->hfs_catalog_vp),
55e303ae
A
460 (KeyCompareProcPtr)cat_binarykeycompare);
461 }
462 }
1c79356b
A
463
464 /*
9bccf70c 465 * Set up Allocation file vnode
1c79356b 466 */
9bccf70c
A
467 cndesc.cd_nameptr = hfs_vbmname;
468 cndesc.cd_namelen = strlen(hfs_vbmname);
469 cndesc.cd_cnid = cnattr.ca_fileid = kHFSAllocationFileID;
470
55e303ae
A
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 }
91447636
A
482 retval = hfs_getnewvnode(hfsmp, NULL, NULL, &cndesc, 0, &cnattr, &cfork,
483 &hfsmp->hfs_allocation_vp);
9bccf70c 484 if (retval) {
91447636
A
485 hfs_unlock(VTOC(hfsmp->hfs_catalog_vp));
486 hfs_unlock(VTOC(hfsmp->hfs_extents_vp));
9bccf70c
A
487 goto ErrorExit;
488 }
489
91447636
A
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
9bccf70c
A
529 /* Pick up volume name and create date */
530 retval = cat_idlookup(hfsmp, kHFSRootFolderID, &cndesc, &cnattr, NULL);
531 if (retval) {
91447636
A
532 hfs_unlock(VTOC(hfsmp->hfs_allocation_vp));
533 hfs_unlock(VTOC(hfsmp->hfs_catalog_vp));
534 hfs_unlock(VTOC(hfsmp->hfs_extents_vp));
9bccf70c
A
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);
1c79356b
A
541
542 /* mark the volume dirty (clear clean unmount bit) */
543 vcb->vcbAtrb &= ~kHFSVolumeUnmountedMask;
55e303ae 544 if (hfsmp->jnl && (hfsmp->hfs_flags & HFS_READ_ONLY) == 0) {
91447636 545 hfs_flushvolumeheader(hfsmp, TRUE, 0);
b4c24cb9 546 }
1c79356b 547
1c79356b
A
548 /*
549 * all done with metadata files so we can unlock now...
550 */
91447636
A
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));
1c79356b 556
b4c24cb9
A
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;
91447636
A
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;
b4c24cb9
A
603 goto ErrorExit;
604 } else if (hfsmp->jnl) {
91447636 605 vfs_setflags(hfsmp->hfs_mp, (uint64_t)((unsigned int)MNT_JOURNALED));
b4c24cb9
A
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
55e303ae
A
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);
91447636 638 if ((hfsmp->hfs_flags & HFS_READ_ONLY) == 0)
55e303ae
A
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
55e303ae
A
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)) {
91447636 651 (void) hfs_recording_init(hfsmp);
55e303ae 652 }
b4c24cb9 653
91447636
A
654 hfs_checkextendedsecurity(hfsmp);
655
1c79356b
A
656 return (0);
657
1c79356b
A
658ErrorExit:
659 /*
91447636 660 * A fatal error occurred and the volume cannot be mounted
1c79356b
A
661 * release any resources that we aquired...
662 */
91447636
A
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);
1c79356b
A
668
669 return (retval);
670}
671
672
673/*
674 * ReleaseMetaFileVNode
675 *
676 * vp L - -
677 */
678static void ReleaseMetaFileVNode(struct vnode *vp)
679{
9bccf70c 680 struct filefork *fp;
1c79356b 681
9bccf70c 682 if (vp && (fp = VTOF(vp))) {
91447636
A
683 if (fp->fcbBTCBPtr != NULL) {
684 (void)hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK);
9bccf70c 685 (void) BTClosePath(fp);
91447636
A
686 hfs_unlock(VTOC(vp));
687 }
1c79356b
A
688
689 /* release the node even if BTClosePath fails */
91447636
A
690 vnode_recycle(vp);
691 vnode_put(vp);
1c79356b 692 }
1c79356b
A
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
55e303ae
A
703__private_extern__
704int
705hfsUnmount( register struct hfsmount *hfsmp, struct proc *p)
1c79356b 706{
91447636
A
707 if (hfsmp->hfs_allocation_vp)
708 ReleaseMetaFileVNode(hfsmp->hfs_allocation_vp);
1c79356b 709
91447636
A
710 if (hfsmp->hfs_attribute_vp)
711 ReleaseMetaFileVNode(hfsmp->hfs_attribute_vp);
1c79356b 712
91447636
A
713 ReleaseMetaFileVNode(hfsmp->hfs_catalog_vp);
714 ReleaseMetaFileVNode(hfsmp->hfs_extents_vp);
1c79356b 715
91447636 716 return (0);
1c79356b
A
717}
718
719
0b4e3aa0 720/*
91447636 721 * Test if fork has overflow extents.
0b4e3aa0 722 */
55e303ae
A
723__private_extern__
724int
725overflow_extents(struct filefork *fp)
1c79356b 726{
9bccf70c 727 u_long blocks;
1c79356b 728
9bccf70c
A
729 if (VTOVCB(FTOV(fp))->vcbSigWord == kHFSPlusSigWord) {
730 if (fp->ff_extents[7].blockCount == 0)
731 return (0);
1c79356b 732
9bccf70c
A
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 }
1c79356b 749
9bccf70c
A
750 return (fp->ff_blocks > blocks);
751}
1c79356b
A
752
753
55e303ae 754/*
91447636 755 * Lock HFS system file(s).
55e303ae
A
756 */
757__private_extern__
9bccf70c 758int
91447636 759hfs_systemfile_lock(struct hfsmount *hfsmp, int flags, enum hfslocktype locktype)
1c79356b 760{
91447636
A
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}
9bccf70c 817
91447636
A
818/*
819 * unlock HFS system file(s).
820 */
821__private_extern__
822void
823hfs_systemfile_unlock(struct hfsmount *hfsmp, int flags)
824{
825 struct timeval tv;
826 u_int32_t lastfsync;
827 int numOfLockedBuffs;
9bccf70c 828
91447636
A
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));
9bccf70c 846 }
91447636
A
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 }
9bccf70c 856 }
91447636
A
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));
9bccf70c 873 }
9bccf70c 874}
1c79356b 875
91447636 876
9bccf70c
A
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
884void RequireFileLock(FileReference vp, int shareable)
1c79356b 885{
91447636 886 int locked;
1c79356b 887
91447636
A
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 }
9bccf70c 893
91447636 894 locked = VTOC(vp)->c_lockowner == (void *)current_thread();
9bccf70c 895
91447636 896 if (!locked && !shareable) {
9bccf70c 897 switch (VTOC(vp)->c_fileid) {
91447636
A
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;
9bccf70c
A
912 }
913 }
1c79356b 914}
9bccf70c 915#endif
1c79356b
A
916
917
9bccf70c
A
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.
55e303ae 922 * (b) The object in question is owned by "unknown"
9bccf70c
A
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
55e303ae 926 * the replacement UID is "unknown".
9bccf70c
A
927 * 3. You are root.
928 *
929 */
930int
91447636 931hfs_owner_rights(struct hfsmount *hfsmp, uid_t cnode_uid, kauth_cred_t cred,
9bccf70c
A
932 struct proc *p, int invokesuperuserstatus)
933{
91447636 934 if ((kauth_cred_getuid(cred) == cnode_uid) || /* [1a] */
55e303ae 935 (cnode_uid == UNKNOWNUID) || /* [1b] */
91447636
A
936 ((((unsigned int)vfs_flags(HFSTOVFS(hfsmp))) & MNT_UNKNOWNPERMISSIONS) && /* [2] */
937 ((kauth_cred_getuid(cred) == hfsmp->hfs_uid) || /* [2a] */
55e303ae 938 (hfsmp->hfs_uid == UNKNOWNUID))) || /* [2b] */
91447636 939 (invokesuperuserstatus && (suser(cred, 0) == 0))) { /* [3] */
9bccf70c
A
940 return (0);
941 } else {
942 return (EPERM);
943 }
1c79356b
A
944}
945
946
9bccf70c
A
947unsigned 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;
1c79356b 956
9bccf70c
A
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 };
1c79356b 964
9bccf70c
A
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;
1c79356b 970
9bccf70c
A
971 /* No clear winner exists: pick the largest even fraction <= MAXBSIZE: */
972 baseBlockCount = allocationBlockSize / baseMultiple; /* Now guaranteed to be an even multiple */
1c79356b 973
9bccf70c
A
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 };
1c79356b 983
9bccf70c
A
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}
1c79356b 988
1c79356b 989
9bccf70c
A
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
55e303ae 994 * and a reference to it is place in hfsmp->hfs_privdir_desc.
9bccf70c 995 */
55e303ae 996__private_extern__
9bccf70c
A
997u_long
998FindMetaDataDirectory(ExtendedVCB *vcb)
1c79356b 999{
9bccf70c
A
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};
55e303ae 1005 struct proc *p = current_proc();
9bccf70c 1006 struct timeval tv;
55e303ae 1007 cat_cookie_t cookie;
91447636 1008 int lockflags;
9bccf70c
A
1009 int error;
1010
1011 if (vcb->vcbSigWord != kHFSPlusSigWord)
7b1edb79 1012 return (0);
7b1edb79 1013
9bccf70c 1014 hfsmp = VCBTOHFS(vcb);
7b1edb79 1015
9bccf70c
A
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;
7b1edb79
A
1021 }
1022
91447636 1023 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
1c79356b 1024
9bccf70c 1025 error = cat_lookup(hfsmp, &hfsmp->hfs_privdir_desc, 0, NULL,
91447636 1026 &hfsmp->hfs_privdir_attr, NULL, NULL);
9bccf70c 1027
91447636 1028 hfs_systemfile_unlock(hfsmp, lockflags);
55e303ae 1029
9bccf70c 1030 if (error == 0) {
9bccf70c 1031 hfsmp->hfs_metadata_createdate = hfsmp->hfs_privdir_attr.ca_itime;
55e303ae
A
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
91447636
A
1040 if ((error = hfs_start_transaction(hfsmp)) != 0) {
1041 return (hfsmp->hfs_privdir_attr.ca_fileid);
55e303ae 1042 }
91447636
A
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);
55e303ae 1050 }
9bccf70c 1051 return (hfsmp->hfs_privdir_attr.ca_fileid);
55e303ae
A
1052
1053 } else if (hfsmp->hfs_flags & HFS_READ_ONLY) {
1054
9bccf70c 1055 return (0);
1c79356b 1056 }
9bccf70c
A
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;
9bccf70c
A
1061 hfsmp->hfs_privdir_attr.ca_nlink = 2;
1062 hfsmp->hfs_privdir_attr.ca_itime = vcb->vcbCrDate;
91447636
A
1063 microtime(&tv);
1064 hfsmp->hfs_privdir_attr.ca_mtime = tv.tv_sec;
9bccf70c
A
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
91447636
A
1072 if ((error = hfs_start_transaction(hfsmp)) != 0) {
1073 return (0);
b4c24cb9 1074 }
55e303ae
A
1075 /* Reserve some space in the Catalog file. */
1076 if (cat_preflight(hfsmp, CAT_CREATE, &cookie, p) != 0) {
91447636
A
1077 hfs_end_transaction(hfsmp);
1078
1079 return (0);
55e303ae 1080 }
b4c24cb9 1081
91447636
A
1082 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK);
1083
55e303ae
A
1084 error = cat_create(hfsmp, &hfsmp->hfs_privdir_desc,
1085 &hfsmp->hfs_privdir_attr, &out_desc);
9bccf70c 1086
91447636 1087 hfs_systemfile_unlock(hfsmp, lockflags);
55e303ae
A
1088
1089 cat_postflight(hfsmp, &cookie, p);
1090
b4c24cb9 1091 if (error) {
91447636 1092 hfs_volupdate(hfsmp, VOL_UPDATE, 0);
b4c24cb9 1093
91447636
A
1094 hfs_end_transaction(hfsmp);
1095
1096 return (0);
b4c24cb9 1097 }
1c79356b 1098
9bccf70c
A
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;
91447636
A
1103
1104 if (hfs_vget(hfsmp, kRootDirID, &dvp, 0) == 0) {
9bccf70c
A
1105 dcp = VTOC(dvp);
1106 dcp->c_childhint = out_desc.cd_hint;
1107 dcp->c_nlink++;
1108 dcp->c_entries++;
91447636
A
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);
1c79356b 1114 }
9bccf70c 1115 hfs_volupdate(hfsmp, VOL_MKDIR, 1);
91447636 1116 hfs_end_transaction(hfsmp);
b4c24cb9 1117
9bccf70c 1118 cat_releasedesc(&out_desc);
1c79356b 1119
9bccf70c 1120 return (out_desc.cd_cnid);
1c79356b
A
1121}
1122
b4c24cb9
A
1123__private_extern__
1124u_long
91447636 1125GetFileInfo(ExtendedVCB *vcb, u_int32_t dirid, const char *name,
b4c24cb9
A
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;
91447636 1133 int lockflags;
b4c24cb9
A
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
91447636
A
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);
b4c24cb9
A
1149
1150 if (error == 0) {
1151 return (fattr->ca_fileid);
55e303ae 1152 } else if (hfsmp->hfs_flags & HFS_READ_ONLY) {
b4c24cb9
A
1153 return (0);
1154 }
91447636
A
1155
1156 return (0); /* XXX what callers expect on an error */
b4c24cb9
A
1157}
1158
1159
1160/*
91447636 1161 * On HFS Plus Volume, there can be orphaned files. These
b4c24cb9
A
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__
1167void
1168hfs_remove_orphans(struct hfsmount * hfsmp)
1169{
1170 struct BTreeIterator * iterator = NULL;
1171 struct FSBufferDescriptor btdata;
1172 struct HFSPlusCatalogFile filerec;
1173 struct HFSPlusCatalogKey * keyp;
55e303ae 1174 struct proc *p = current_proc();
b4c24cb9
A
1175 FCB *fcb;
1176 ExtendedVCB *vcb;
1177 char filename[32];
1178 char tempname[32];
1179 size_t namelen;
91447636 1180 cat_cookie_t cookie;
b4c24cb9 1181 int catlock = 0;
55e303ae
A
1182 int catreserve = 0;
1183 int started_tr = 0;
91447636 1184 int lockflags;
55e303ae 1185 int result;
91447636
A
1186 int orphanedlinks = 0;
1187
1188 bzero(&cookie, sizeof(cookie));
1189
55e303ae 1190 if (hfsmp->hfs_flags & HFS_CLEANED_ORPHANS)
b4c24cb9
A
1191 return;
1192
1193 vcb = HFSTOVCB(hfsmp);
91447636 1194 fcb = VTOF(hfsmp->hfs_catalog_vp);
b4c24cb9
A
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));
91447636
A
1202
1203 /* Build a key to "temp" */
b4c24cb9 1204 keyp = (HFSPlusCatalogKey*)&iterator->key;
55e303ae 1205 keyp->parentID = hfsmp->hfs_privdir_desc.cd_cnid;
91447636
A
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';
b4c24cb9 1212
b4c24cb9 1213 /*
91447636 1214 * Position the iterator just before the first real temp file.
b4c24cb9 1215 */
91447636
A
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);
b4c24cb9 1219
91447636 1220 /* Visit all the temp files in the HFS+ private directory. */
b4c24cb9 1221 for (;;) {
91447636 1222 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK);
b4c24cb9 1223 result = BTIterateRecord(fcb, kBTreeNextRecord, iterator, &btdata, NULL);
91447636 1224 hfs_systemfile_unlock(hfsmp, lockflags);
b4c24cb9
A
1225 if (result)
1226 break;
55e303ae 1227 if (keyp->parentID != hfsmp->hfs_privdir_desc.cd_cnid)
b4c24cb9
A
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 *
b4c24cb9
A
1241 */
1242 if (bcmp(tempname, filename, namelen) == 0) {
91447636
A
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;
55e303ae 1254 }
91447636 1255 started_tr = 1;
55e303ae
A
1256
1257 /*
1258 * Reserve some space in the Catalog file.
1259 */
1260 if (cat_preflight(hfsmp, CAT_DELETE, &cookie, p) != 0) {
91447636 1261 printf("hfs_remove_orphans: cat_preflight failed\n");
55e303ae
A
1262 goto exit;
1263 }
1264 catreserve = 1;
b4c24cb9 1265
91447636 1266 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG | SFL_ATTRIBUTE | SFL_EXTENTS | SFL_BITMAP, HFS_EXCLUSIVE_LOCK);
55e303ae 1267 catlock = 1;
b4c24cb9
A
1268
1269 /* Build a fake cnode */
55e303ae
A
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;
b4c24cb9
A
1273 cnode.c_desc.cd_nameptr = filename;
1274 cnode.c_desc.cd_namelen = namelen;
55e303ae
A
1275 cnode.c_desc.cd_cnid = cnode.c_attr.ca_fileid;
1276 cnode.c_blocks = dfork.ff_blocks + rfork.ff_blocks;
b4c24cb9
A
1277
1278 /* Position iterator at previous entry */
1279 if (BTIterateRecord(fcb, kBTreePrevRecord, iterator,
55e303ae 1280 NULL, NULL) != 0) {
b4c24cb9 1281 break;
55e303ae
A
1282 }
1283
b4c24cb9 1284 /* Truncate the file to zero (both forks) */
55e303ae
A
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) {
91447636
A
1310 hfs_end_transaction(hfsmp);
1311 if (hfs_start_transaction(hfsmp) != 0) {
55e303ae
A
1312 started_tr = 0;
1313 break;
1314 }
1315 }
b4c24cb9
A
1316 }
1317 }
55e303ae
A
1318
1319 if (rfork.ff_blocks > 0) {
1320 rfork.ff_cp = &cnode;
b4c24cb9 1321 cnode.c_datafork = NULL;
55e303ae
A
1322 cnode.c_rsrcfork = &rfork;
1323 if (TruncateFileC(vcb, (FCB*)&rfork, 0, false) != 0) {
b4c24cb9
A
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) {
91447636
A
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);
b4c24cb9
A
1333 break;
1334 }
91447636
A
1335 ++orphanedlinks;
1336
1337 /* Delete any attributes, ignore errors */
1338 (void) hfs_removeallattr(hfsmp, cnode.c_fileid);
b4c24cb9
A
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);
55e303ae
A
1345
1346 /* Drop locks and end the transaction */
91447636 1347 hfs_systemfile_unlock(hfsmp, lockflags);
55e303ae
A
1348 cat_postflight(hfsmp, &cookie, p);
1349 catlock = catreserve = 0;
1350 if (started_tr) {
91447636 1351 hfs_end_transaction(hfsmp);
55e303ae
A
1352 started_tr = 0;
1353 }
55e303ae
A
1354
1355 } /* end if */
1356 } /* end for */
91447636
A
1357 if (orphanedlinks > 0)
1358 printf("HFS: Removed %d orphaned unlinked files\n", orphanedlinks);
b4c24cb9 1359exit:
55e303ae 1360 if (catlock) {
91447636 1361 hfs_systemfile_unlock(hfsmp, lockflags);
55e303ae
A
1362 }
1363 if (catreserve) {
1364 cat_postflight(hfsmp, &cookie, p);
1365 }
b4c24cb9 1366 if (started_tr) {
91447636 1367 hfs_end_transaction(hfsmp);
55e303ae 1368 }
b4c24cb9
A
1369
1370 FREE(iterator, M_TEMP);
55e303ae 1371 hfsmp->hfs_flags |= HFS_CLEANED_ORPHANS;
b4c24cb9
A
1372}
1373
9bccf70c
A
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 */
1380u_int32_t
1381GetLogicalBlockSize(struct vnode *vp)
1c79356b 1382{
9bccf70c 1383u_int32_t logBlockSize;
1c79356b 1384
9bccf70c 1385 DBG_ASSERT(vp != NULL);
1c79356b 1386
9bccf70c
A
1387 /* start with default */
1388 logBlockSize = VTOHFS(vp)->hfs_logBlockSize;
1c79356b 1389
91447636 1390 if (vnode_issystem(vp)) {
9bccf70c 1391 if (VTOF(vp)->fcbBTCBPtr != NULL) {
0b4e3aa0
A
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
9bccf70c 1402 (void) BTGetInformation (VTOF(vp), kBTreeInfoVersion, &bTreeInfo);
0b4e3aa0
A
1403
1404 logBlockSize = bTreeInfo.nodeSize;
1405
9bccf70c 1406 } else if (VTOC(vp)->c_fileid == kHFSAllocationFileID) {
0b4e3aa0 1407 logBlockSize = VTOVCB(vp)->vcbVBMIOSize;
1c79356b 1408 }
0b4e3aa0
A
1409 }
1410
1c79356b
A
1411 DBG_ASSERT(logBlockSize > 0);
1412
1413 return logBlockSize;
1414}
1415
9bccf70c
A
1416__private_extern__
1417u_int32_t
1418hfs_freeblks(struct hfsmount * hfsmp, int wantreserve)
1419{
91447636 1420 ExtendedVCB *vcb = HFSTOVCB(hfsmp);
9bccf70c
A
1421 u_int32_t freeblks;
1422
91447636 1423 HFS_MOUNT_LOCK(hfsmp, TRUE);
9bccf70c
A
1424 freeblks = vcb->freeBlocks;
1425 if (wantreserve) {
1426 if (freeblks > vcb->reserveBlocks)
1427 freeblks -= vcb->reserveBlocks;
1428 else
1429 freeblks = 0;
1430 }
55e303ae
A
1431 if (freeblks > vcb->loanedBlocks)
1432 freeblks -= vcb->loanedBlocks;
1433 else
1434 freeblks = 0;
91447636 1435 HFS_MOUNT_UNLOCK(hfsmp, TRUE);
55e303ae
A
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) {
91447636 1443 struct vfsstatfs *vfsp; /* 272 bytes */
55e303ae
A
1444 u_int32_t vfreeblks;
1445 u_int32_t loanedblks;
1446 struct mount * backingfs_mp;
1447
91447636 1448 backingfs_mp = vnode_mount(hfsmp->hfs_backingfs_rootvp);
55e303ae 1449
91447636
A
1450 if (vfsp = vfs_statfs(backingfs_mp)) {
1451 HFS_MOUNT_LOCK(hfsmp, TRUE);
1452 vfreeblks = (u_int32_t)vfsp->f_bavail;
55e303ae 1453 /* Normalize block count if needed. */
91447636
A
1454 if (vfsp->f_bsize != vcb->blockSize) {
1455 vfreeblks = ((u_int64_t)vfreeblks * (u_int64_t)(vfsp->f_bsize)) / vcb->blockSize;
55e303ae
A
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);
91447636 1470 HFS_MOUNT_UNLOCK(hfsmp, TRUE);
55e303ae
A
1471 }
1472 }
1473#endif /* HFS_SPARSE_DEV */
9bccf70c 1474
9bccf70c
A
1475 return (freeblks);
1476}
1477
1c79356b
A
1478/*
1479 * Map HFS Common errors (negative) to BSD error codes (positive).
1480 * Positive errors (ie BSD errors) are passed through unchanged.
1481 */
1482short MacToVFSError(OSErr err)
1483{
9bccf70c
A
1484 if (err >= 0)
1485 return err;
1c79356b 1486
1c79356b 1487 switch (err) {
9bccf70c 1488 case dskFulErr: /* -34 */
55e303ae 1489 case btNoSpaceAvail: /* -32733 */
b4c24cb9 1490 return ENOSPC;
9bccf70c 1491 case fxOvFlErr: /* -32750 */
b4c24cb9 1492 return EOVERFLOW;
9bccf70c
A
1493
1494 case btBadNode: /* -32731 */
3a60a9f5 1495 return EIO;
9bccf70c
A
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 */
b4c24cb9 1515 return ERANGE;
9bccf70c
A
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:
d52fe63f 1525 return ENXIO;
9bccf70c
A
1526
1527 default:
1528 return EIO; /* +5 */
1c79356b
A
1529 }
1530}
1531
1532
1533/*
91447636
A
1534 * Find the current thread's directory hint for a given index.
1535 *
1536 * Requires an exclusive lock on directory cnode.
1c79356b 1537 */
9bccf70c 1538__private_extern__
91447636
A
1539directoryhint_t *
1540hfs_getdirhint(struct cnode *dcp, int index)
1c79356b 1541{
91447636 1542 struct timeval tv;
b36670ce
A
1543 directoryhint_t *hint;
1544 boolean_t need_remove, need_init;
91447636
A
1545 char * name;
1546
91447636
A
1547 microuptime(&tv);
1548
b36670ce
A
1549 /*
1550 * Look for an existing hint first. If not found, create a new one (when
1551 * the list is not full) or recycle the oldest hint. Since new hints are
1552 * always added to the head of the list, the last hint is always the
1553 * oldest.
1554 */
1555 TAILQ_FOREACH(hint, &dcp->c_hintlist, dh_link) {
1556 if (hint->dh_index == index)
1557 break;
9bccf70c 1558 }
b36670ce
A
1559 if (hint != NULL) { /* found an existing hint */
1560 need_init = false;
1561 need_remove = true;
1562 } else { /* cannot find an existing hint */
1563 need_init = true;
1564 if (dcp->c_dirhintcnt < HFS_MAXDIRHINTS) { /* we don't need recycling */
1565 /* Create a default directory hint */
1566 MALLOC_ZONE(hint, directoryhint_t *, sizeof(directoryhint_t), M_HFSDIRHINT, M_WAITOK);
1567 ++dcp->c_dirhintcnt;
1568 need_remove = false;
1569 } else { /* recycle the last (i.e., the oldest) hint */
1570 hint = TAILQ_LAST(&dcp->c_hintlist, hfs_hinthead);
1571 if ((name = hint->dh_desc.cd_nameptr)) {
1572 hint->dh_desc.cd_nameptr = NULL;
1573 vfs_removename(name);
1574 }
1575 need_remove = true;
91447636 1576 }
9bccf70c 1577 }
91447636 1578
b36670ce
A
1579 if (need_remove)
1580 TAILQ_REMOVE(&dcp->c_hintlist, hint, dh_link);
1581
1582 TAILQ_INSERT_HEAD(&dcp->c_hintlist, hint, dh_link);
1583
1584 if (need_init) {
1585 hint->dh_index = index;
1586 hint->dh_desc.cd_flags = 0;
1587 hint->dh_desc.cd_encoding = 0;
1588 hint->dh_desc.cd_namelen = 0;
1589 hint->dh_desc.cd_nameptr = NULL;
1590 hint->dh_desc.cd_parentcnid = dcp->c_cnid;
1591 hint->dh_desc.cd_hint = dcp->c_childhint;
1592 hint->dh_desc.cd_cnid = 0;
1593 }
91447636
A
1594 hint->dh_time = tv.tv_sec;
1595 return (hint);
1c79356b
A
1596}
1597
9bccf70c 1598/*
91447636
A
1599 * Release a single directory hint.
1600 *
1601 * Requires an exclusive lock on directory cnode.
9bccf70c
A
1602 */
1603__private_extern__
1604void
91447636 1605hfs_reldirhint(struct cnode *dcp, directoryhint_t * relhint)
9bccf70c 1606{
91447636
A
1607 char * name;
1608
b36670ce
A
1609 TAILQ_REMOVE(&dcp->c_hintlist, relhint, dh_link);
1610 name = relhint->dh_desc.cd_nameptr;
1611 if (name != NULL) {
1612 relhint->dh_desc.cd_nameptr = NULL;
1613 vfs_removename(name);
1c79356b 1614 }
b36670ce
A
1615 FREE_ZONE(relhint, sizeof(directoryhint_t), M_HFSDIRHINT);
1616 --dcp->c_dirhintcnt;
1c79356b
A
1617}
1618
9bccf70c 1619/*
91447636
A
1620 * Release directory hints for given directory
1621 *
1622 * Requires an exclusive lock on directory cnode.
9bccf70c
A
1623 */
1624__private_extern__
1625void
91447636 1626hfs_reldirhints(struct cnode *dcp, int stale_hints_only)
9bccf70c 1627{
91447636 1628 struct timeval tv;
b36670ce 1629 directoryhint_t *hint, *prev;
91447636
A
1630 char * name;
1631
1632 if (stale_hints_only)
1633 microuptime(&tv);
b36670ce
A
1634
1635 /* searching from the oldest to the newest, so we can stop early when releasing stale hints only */
1636 for (hint = TAILQ_LAST(&dcp->c_hintlist, hfs_hinthead); hint != NULL; hint = prev) {
1637 if (stale_hints_only && (tv.tv_sec - hint->dh_time) < HFS_DIRHINT_TTL)
1638 break; /* stop here if this entry is too new */
91447636
A
1639 name = hint->dh_desc.cd_nameptr;
1640 if (name != NULL) {
1641 hint->dh_desc.cd_nameptr = NULL;
1642 vfs_removename(name);
1c79356b 1643 }
b36670ce
A
1644 prev = TAILQ_PREV(hint, hfs_hinthead, dh_link); /* must save this pointer before calling FREE_ZONE on this node */
1645 TAILQ_REMOVE(&dcp->c_hintlist, hint, dh_link);
91447636
A
1646 FREE_ZONE(hint, sizeof(directoryhint_t), M_HFSDIRHINT);
1647 --dcp->c_dirhintcnt;
1c79356b 1648 }
9bccf70c 1649}
1c79356b 1650
1c79356b 1651
d7e50217
A
1652/*
1653 * Perform a case-insensitive compare of two UTF-8 filenames.
1654 *
1655 * Returns 0 if the strings match.
1656 */
1657__private_extern__
1658int
1659hfs_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);
1679out:
1680 FREE(ustr1, M_TEMP);
1681 return (cmp);
1682}
1683
1684
b4c24cb9
A
1685__private_extern__
1686int
1687hfs_early_journal_init(struct hfsmount *hfsmp, HFSPlusVolumeHeader *vhp,
91447636
A
1688 void *_args, off_t embeddedOffset, daddr64_t mdb_offset,
1689 HFSMasterDirectoryBlock *mdbp, kauth_cred_t cred)
b4c24cb9
A
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
91447636
A
1707 retval = (int)buf_meta_bread(devvp,
1708 (daddr64_t)((embeddedOffset/blksize) +
1709 (SWAP_BE32(vhp->journalInfoBlock)*sectors_per_fsblock)),
b4c24cb9
A
1710 SWAP_BE32(vhp->blockSize), cred, &jinfo_bp);
1711 if (retval)
1712 return retval;
1713
91447636 1714 jibp = (JournalInfoBlock *)buf_dataptr(jinfo_bp);
b4c24cb9
A
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");
91447636 1723 buf_brelse(jinfo_bp);
b4c24cb9
A
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);
55e303ae 1729 hfsmp->jnl_size = jibp->size;
b4c24cb9
A
1730
1731 if (jibp->flags & kJIJournalNeedInitMask) {
1732 printf("hfs: Initializing the journal (joffset 0x%llx sz 0x%llx)...\n",
91447636 1733 jibp->offset + embeddedOffset, jibp->size);
b4c24cb9 1734 hfsmp->jnl = journal_create(hfsmp->jvp,
91447636 1735 jibp->offset + embeddedOffset,
b4c24cb9
A
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);
55e303ae
A
1747 jibp->offset = SWAP_BE64(jibp->offset);
1748 jibp->size = SWAP_BE64(jibp->size);
91447636 1749 buf_bwrite(jinfo_bp);
b4c24cb9
A
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",
91447636 1754 // jibp->offset + embeddedOffset,
b4c24cb9
A
1755 // jibp->size, SWAP_BE32(vhp->blockSize));
1756
1757 hfsmp->jnl = journal_open(hfsmp->jvp,
91447636 1758 jibp->offset + embeddedOffset,
b4c24cb9
A
1759 jibp->size,
1760 devvp,
1761 blksize,
1762 arg_flags,
1763 arg_tbufsz,
1764 hfs_sync_metadata, hfsmp->hfs_mp);
1765
91447636 1766 buf_brelse(jinfo_bp);
b4c24cb9
A
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.
55e303ae 1773 if (mdb_offset == 0) {
91447636 1774 mdb_offset = (daddr64_t)((embeddedOffset / blksize) + HFS_PRI_SECTOR(blksize));
55e303ae 1775 }
91447636 1776 retval = (int)buf_meta_bread(devvp, mdb_offset, blksize, cred, &bp);
b4c24cb9 1777 if (retval) {
91447636 1778 buf_brelse(bp);
b4c24cb9
A
1779 printf("hfs: failed to reload the mdb after opening the journal (retval %d)!\n",
1780 retval);
1781 return retval;
1782 }
91447636
A
1783 bcopy((char *)buf_dataptr(bp) + HFS_PRI_OFFSET(blksize), mdbp, 512);
1784 buf_brelse(bp);
b4c24cb9
A
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) {
55e303ae 1795 printf("hfs: early jnl init: failed to open/create the journal (retval %d).\n", retval);
b4c24cb9
A
1796 return EINVAL;
1797 }
1c79356b 1798
b4c24cb9
A
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//
1817static int
1818hfs_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;
91447636
A
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)),
b4c24cb9
A
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
91447636 1873 jibp = (JournalInfoBlock *)buf_dataptr(jinfo_bp);
b4c24cb9
A
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);
91447636 1882 buf_brelse(jinfo_bp);
b4c24cb9
A
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");
91447636 1910 buf_brelse(jinfo_bp);
b4c24cb9
A
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);
55e303ae 1916 hfsmp->jnl_size = jibp->size;
b4c24cb9
A
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
91447636 1966 buf_bwrite(jinfo_bp);
b4c24cb9 1967 } else {
91447636 1968 buf_brelse(jinfo_bp);
b4c24cb9
A
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) {
55e303ae 1978 printf("hfs: late jnl init: failed to open/create the journal (retval %d).\n", retval);
b4c24cb9
A
1979 return EINVAL;
1980 }
1981
1982 return 0;
1983}
55e303ae
A
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
2014static void
2015hfs_metadatazone_init(struct hfsmount *hfsmp)
2016{
2017 ExtendedVCB *vcb;
55e303ae
A
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);
55e303ae
A
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
91447636
A
2132 hfsmp->hfs_hotfile_maxblks = filesize / vcb->blockSize;
2133
55e303ae
A
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
2154static u_int32_t
2155hfs_hotfile_freeblocks(struct hfsmount *hfsmp)
2156{
2157 ExtendedVCB *vcb = HFSTOVCB(hfsmp);
91447636 2158 int lockflags;
55e303ae
A
2159 int freeblocks;
2160
91447636 2161 lockflags = hfs_systemfile_lock(hfsmp, SFL_BITMAP, HFS_EXCLUSIVE_LOCK);
55e303ae 2162 freeblocks = MetaZoneFreeBlocks(vcb);
91447636
A
2163 hfs_systemfile_unlock(hfsmp, lockflags);
2164
55e303ae
A
2165 /* Minus Extents overflow file reserve. */
2166 freeblocks -=
91447636 2167 hfsmp->hfs_overflow_maxblks - VTOF(hfsmp->hfs_extents_vp)->ff_blocks;
55e303ae
A
2168 /* Minus catalog file reserve. */
2169 freeblocks -=
91447636 2170 hfsmp->hfs_catalog_maxblks - VTOF(hfsmp->hfs_catalog_vp)->ff_blocks;
55e303ae
A
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__
2182int
2183hfs_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
91447636
A
2205
2206__private_extern__
2207int
2208hfs_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__
2233int
2234hfs_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}