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