]> git.saurik.com Git - apple/xnu.git/blame - bsd/hfs/hfs_vfsutils.c
xnu-2422.115.4.tar.gz
[apple/xnu.git] / bsd / hfs / hfs_vfsutils.c
CommitLineData
1c79356b 1/*
db609669 2 * Copyright (c) 2000-2013 Apple Inc. All rights reserved.
5d5c5d0d 3 *
2d21ac55 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
1c79356b 5 *
2d21ac55
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 License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
8f6c56a5 14 *
2d21ac55
A
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
8f6c56a5
A
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
2d21ac55
A
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
8f6c56a5 25 *
2d21ac55 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
1c79356b
A
27 */
28/* @(#)hfs_vfsutils.c 4.0
29*
9bccf70c 30* (c) 1997-2002 Apple Computer, Inc. All Rights Reserved
1c79356b
A
31*
32* hfs_vfsutils.c -- Routines that go between the HFS layer and the VFS.
33*
1c79356b
A
34*/
35#include <sys/param.h>
36#include <sys/systm.h>
37#include <sys/kernel.h>
38#include <sys/malloc.h>
39#include <sys/stat.h>
1c79356b 40#include <sys/mount.h>
b0d623f7 41#include <sys/mount_internal.h>
1c79356b 42#include <sys/buf.h>
2d21ac55 43#include <sys/buf_internal.h>
1c79356b
A
44#include <sys/ubc.h>
45#include <sys/unistd.h>
91447636
A
46#include <sys/utfconv.h>
47#include <sys/kauth.h>
2d21ac55 48#include <sys/fcntl.h>
6d2010ae 49#include <sys/fsctl.h>
2d21ac55 50#include <sys/vnode_internal.h>
b0d623f7 51#include <kern/clock.h>
2d21ac55
A
52
53#include <libkern/OSAtomic.h>
1c79356b 54
39236c6e
A
55/* for parsing boot-args */
56#include <pexpert/pexpert.h>
57
58#if CONFIG_PROTECT
59#include <sys/cprotect.h>
60#endif
61
1c79356b 62#include "hfs.h"
9bccf70c 63#include "hfs_catalog.h"
1c79356b
A
64#include "hfs_dbg.h"
65#include "hfs_mount.h"
66#include "hfs_endian.h"
9bccf70c 67#include "hfs_cnode.h"
2d21ac55 68#include "hfs_fsctl.h"
1c79356b
A
69
70#include "hfscommon/headers/FileMgrInternal.h"
71#include "hfscommon/headers/BTreesInternal.h"
72#include "hfscommon/headers/HFSUnicodeWrappers.h"
73
1c79356b 74static void ReleaseMetaFileVNode(struct vnode *vp);
b4c24cb9 75static int hfs_late_journal_init(struct hfsmount *hfsmp, HFSPlusVolumeHeader *vhp, void *_args);
1c79356b 76
55e303ae
A
77static u_int32_t hfs_hotfile_freeblocks(struct hfsmount *);
78
6d2010ae
A
79#define HFS_MOUNT_DEBUG 1
80
55e303ae 81
1c79356b
A
82//*******************************************************************************
83// Note: Finder information in the HFS/HFS+ metadata are considered opaque and
84// hence are not in the right byte order on little endian machines. It is
85// the responsibility of the finder and other clients to swap the data.
86//*******************************************************************************
87
88//*******************************************************************************
89// Routine: hfs_MountHFSVolume
90//
91//
92//*******************************************************************************
2d21ac55
A
93unsigned char hfs_catname[] = "Catalog B-tree";
94unsigned char hfs_extname[] = "Extents B-tree";
95unsigned char hfs_vbmname[] = "Volume Bitmap";
96unsigned char hfs_attrname[] = "Attribute B-tree";
97unsigned char hfs_startupname[] = "Startup File";
9bccf70c 98
39236c6e 99#if CONFIG_HFS_STD
1c79356b 100OSErr hfs_MountHFSVolume(struct hfsmount *hfsmp, HFSMasterDirectoryBlock *mdb,
2d21ac55 101 __unused struct proc *p)
1c79356b 102{
9bccf70c
A
103 ExtendedVCB *vcb = HFSTOVCB(hfsmp);
104 int error;
1c79356b 105 ByteCount utf8chars;
9bccf70c
A
106 struct cat_desc cndesc;
107 struct cat_attr cnattr;
108 struct cat_fork fork;
6d2010ae 109 int newvnode_flags = 0;
1c79356b 110
9bccf70c
A
111 /* Block size must be a multiple of 512 */
112 if (SWAP_BE32(mdb->drAlBlkSiz) == 0 ||
113 (SWAP_BE32(mdb->drAlBlkSiz) & 0x01FF) != 0)
1c79356b
A
114 return (EINVAL);
115
1c79356b 116 /* don't mount a writeable volume if its dirty, it must be cleaned by fsck_hfs */
55e303ae
A
117 if (((hfsmp->hfs_flags & HFS_READ_ONLY) == 0) &&
118 ((SWAP_BE16(mdb->drAtrb) & kHFSVolumeUnmountedMask) == 0)) {
1c79356b 119 return (EINVAL);
55e303ae
A
120 }
121 hfsmp->hfs_flags |= HFS_STANDARD;
1c79356b
A
122 /*
123 * The MDB seems OK: transfer info from it into VCB
124 * Note - the VCB starts out clear (all zeros)
125 *
126 */
9bccf70c 127 vcb->vcbSigWord = SWAP_BE16 (mdb->drSigWord);
6d2010ae 128 vcb->hfs_itime = to_bsd_time(LocalToUTC(SWAP_BE32(mdb->drCrDate)));
0b4e3aa0 129 vcb->localCreateDate = SWAP_BE32 (mdb->drCrDate);
9bccf70c
A
130 vcb->vcbLsMod = to_bsd_time(LocalToUTC(SWAP_BE32(mdb->drLsMod)));
131 vcb->vcbAtrb = SWAP_BE16 (mdb->drAtrb);
132 vcb->vcbNmFls = SWAP_BE16 (mdb->drNmFls);
133 vcb->vcbVBMSt = SWAP_BE16 (mdb->drVBMSt);
134 vcb->nextAllocation = SWAP_BE16 (mdb->drAllocPtr);
135 vcb->totalBlocks = SWAP_BE16 (mdb->drNmAlBlks);
2d21ac55 136 vcb->allocLimit = vcb->totalBlocks;
9bccf70c
A
137 vcb->blockSize = SWAP_BE32 (mdb->drAlBlkSiz);
138 vcb->vcbClpSiz = SWAP_BE32 (mdb->drClpSiz);
139 vcb->vcbAlBlSt = SWAP_BE16 (mdb->drAlBlSt);
140 vcb->vcbNxtCNID = SWAP_BE32 (mdb->drNxtCNID);
141 vcb->freeBlocks = SWAP_BE16 (mdb->drFreeBks);
142 vcb->vcbVolBkUp = to_bsd_time(LocalToUTC(SWAP_BE32(mdb->drVolBkUp)));
143 vcb->vcbWrCnt = SWAP_BE32 (mdb->drWrCnt);
144 vcb->vcbNmRtDirs = SWAP_BE16 (mdb->drNmRtDirs);
145 vcb->vcbFilCnt = SWAP_BE32 (mdb->drFilCnt);
146 vcb->vcbDirCnt = SWAP_BE32 (mdb->drDirCnt);
1c79356b 147 bcopy(mdb->drFndrInfo, vcb->vcbFndrInfo, sizeof(vcb->vcbFndrInfo));
55e303ae 148 if ((hfsmp->hfs_flags & HFS_READ_ONLY) == 0)
9bccf70c 149 vcb->vcbWrCnt++; /* Compensate for write of MDB on last flush */
1c79356b
A
150
151 /* convert hfs encoded name into UTF-8 string */
9bccf70c 152 error = hfs_to_utf8(vcb, mdb->drVN, NAME_MAX, &utf8chars, vcb->vcbVN);
1c79356b
A
153 /*
154 * When an HFS name cannot be encoded with the current
155 * volume encoding we use MacRoman as a fallback.
156 */
d41d1dae 157 if (error || (utf8chars == 0)) {
6d2010ae
A
158 error = mac_roman_to_utf8(mdb->drVN, NAME_MAX, &utf8chars, vcb->vcbVN);
159 /* If we fail to encode to UTF8 from Mac Roman, the name is bad. Deny the mount */
d41d1dae
A
160 if (error) {
161 goto MtVolErr;
162 }
163 }
6d2010ae 164
593a1d5f 165 hfsmp->hfs_logBlockSize = BestBlockSizeFit(vcb->blockSize, MAXBSIZE, hfsmp->hfs_logical_block_size);
0b4e3aa0 166 vcb->vcbVBMIOSize = kHFSBlockSize;
1c79356b 167
593a1d5f
A
168 hfsmp->hfs_alt_id_sector = HFS_ALT_SECTOR(hfsmp->hfs_logical_block_size,
169 hfsmp->hfs_logical_block_count);
1c79356b 170
9bccf70c 171 bzero(&cndesc, sizeof(cndesc));
91447636 172 cndesc.cd_parentcnid = kHFSRootParentID;
55e303ae 173 cndesc.cd_flags |= CD_ISMETA;
9bccf70c 174 bzero(&cnattr, sizeof(cnattr));
2d21ac55 175 cnattr.ca_linkcount = 1;
9bccf70c
A
176 cnattr.ca_mode = S_IFREG;
177 bzero(&fork, sizeof(fork));
178
1c79356b 179 /*
9bccf70c
A
180 * Set up Extents B-tree vnode
181 */
182 cndesc.cd_nameptr = hfs_extname;
2d21ac55 183 cndesc.cd_namelen = strlen((char *)hfs_extname);
9bccf70c
A
184 cndesc.cd_cnid = cnattr.ca_fileid = kHFSExtentsFileID;
185 fork.cf_size = SWAP_BE32(mdb->drXTFlSize);
186 fork.cf_blocks = fork.cf_size / vcb->blockSize;
187 fork.cf_clump = SWAP_BE32(mdb->drXTClpSiz);
55e303ae 188 fork.cf_vblocks = 0;
9bccf70c
A
189 fork.cf_extents[0].startBlock = SWAP_BE16(mdb->drXTExtRec[0].startBlock);
190 fork.cf_extents[0].blockCount = SWAP_BE16(mdb->drXTExtRec[0].blockCount);
191 fork.cf_extents[1].startBlock = SWAP_BE16(mdb->drXTExtRec[1].startBlock);
192 fork.cf_extents[1].blockCount = SWAP_BE16(mdb->drXTExtRec[1].blockCount);
193 fork.cf_extents[2].startBlock = SWAP_BE16(mdb->drXTExtRec[2].startBlock);
194 fork.cf_extents[2].blockCount = SWAP_BE16(mdb->drXTExtRec[2].blockCount);
195 cnattr.ca_blocks = fork.cf_blocks;
196
91447636 197 error = hfs_getnewvnode(hfsmp, NULL, NULL, &cndesc, 0, &cnattr, &fork,
6d2010ae
A
198 &hfsmp->hfs_extents_vp, &newvnode_flags);
199 if (error) {
200 if (HFS_MOUNT_DEBUG) {
201 printf("hfs_mounthfs (std): error creating Ext Vnode (%d) \n", error);
202 }
203 goto MtVolErr;
204 }
91447636 205 error = MacToVFSError(BTOpenPath(VTOF(hfsmp->hfs_extents_vp),
55e303ae 206 (KeyCompareProcPtr)CompareExtentKeys));
9bccf70c 207 if (error) {
6d2010ae
A
208 if (HFS_MOUNT_DEBUG) {
209 printf("hfs_mounthfs (std): error opening Ext Vnode (%d) \n", error);
210 }
91447636 211 hfs_unlock(VTOC(hfsmp->hfs_extents_vp));
9bccf70c
A
212 goto MtVolErr;
213 }
2d21ac55 214 hfsmp->hfs_extents_cp = VTOC(hfsmp->hfs_extents_vp);
1c79356b
A
215
216 /*
217 * Set up Catalog B-tree vnode...
218 */
9bccf70c 219 cndesc.cd_nameptr = hfs_catname;
2d21ac55 220 cndesc.cd_namelen = strlen((char *)hfs_catname);
9bccf70c
A
221 cndesc.cd_cnid = cnattr.ca_fileid = kHFSCatalogFileID;
222 fork.cf_size = SWAP_BE32(mdb->drCTFlSize);
223 fork.cf_blocks = fork.cf_size / vcb->blockSize;
224 fork.cf_clump = SWAP_BE32(mdb->drCTClpSiz);
55e303ae 225 fork.cf_vblocks = 0;
9bccf70c
A
226 fork.cf_extents[0].startBlock = SWAP_BE16(mdb->drCTExtRec[0].startBlock);
227 fork.cf_extents[0].blockCount = SWAP_BE16(mdb->drCTExtRec[0].blockCount);
228 fork.cf_extents[1].startBlock = SWAP_BE16(mdb->drCTExtRec[1].startBlock);
229 fork.cf_extents[1].blockCount = SWAP_BE16(mdb->drCTExtRec[1].blockCount);
230 fork.cf_extents[2].startBlock = SWAP_BE16(mdb->drCTExtRec[2].startBlock);
231 fork.cf_extents[2].blockCount = SWAP_BE16(mdb->drCTExtRec[2].blockCount);
232 cnattr.ca_blocks = fork.cf_blocks;
233
91447636 234 error = hfs_getnewvnode(hfsmp, NULL, NULL, &cndesc, 0, &cnattr, &fork,
6d2010ae 235 &hfsmp->hfs_catalog_vp, &newvnode_flags);
9bccf70c 236 if (error) {
6d2010ae
A
237 if (HFS_MOUNT_DEBUG) {
238 printf("hfs_mounthfs (std): error creating catalog Vnode (%d) \n", error);
239 }
91447636 240 hfs_unlock(VTOC(hfsmp->hfs_extents_vp));
9bccf70c
A
241 goto MtVolErr;
242 }
91447636 243 error = MacToVFSError(BTOpenPath(VTOF(hfsmp->hfs_catalog_vp),
55e303ae 244 (KeyCompareProcPtr)CompareCatalogKeys));
9bccf70c 245 if (error) {
6d2010ae
A
246 if (HFS_MOUNT_DEBUG) {
247 printf("hfs_mounthfs (std): error opening catalog Vnode (%d) \n", error);
248 }
91447636
A
249 hfs_unlock(VTOC(hfsmp->hfs_catalog_vp));
250 hfs_unlock(VTOC(hfsmp->hfs_extents_vp));
251 goto MtVolErr;
252 }
2d21ac55 253 hfsmp->hfs_catalog_cp = VTOC(hfsmp->hfs_catalog_vp);
91447636
A
254
255 /*
256 * Set up dummy Allocation file vnode (used only for locking bitmap)
257 */
258 cndesc.cd_nameptr = hfs_vbmname;
2d21ac55 259 cndesc.cd_namelen = strlen((char *)hfs_vbmname);
91447636
A
260 cndesc.cd_cnid = cnattr.ca_fileid = kHFSAllocationFileID;
261 bzero(&fork, sizeof(fork));
262 cnattr.ca_blocks = 0;
263
264 error = hfs_getnewvnode(hfsmp, NULL, NULL, &cndesc, 0, &cnattr, &fork,
6d2010ae 265 &hfsmp->hfs_allocation_vp, &newvnode_flags);
91447636 266 if (error) {
6d2010ae
A
267 if (HFS_MOUNT_DEBUG) {
268 printf("hfs_mounthfs (std): error creating bitmap Vnode (%d) \n", error);
269 }
91447636
A
270 hfs_unlock(VTOC(hfsmp->hfs_catalog_vp));
271 hfs_unlock(VTOC(hfsmp->hfs_extents_vp));
9bccf70c
A
272 goto MtVolErr;
273 }
2d21ac55 274 hfsmp->hfs_allocation_cp = VTOC(hfsmp->hfs_allocation_vp);
1c79356b 275
6d2010ae 276 /* mark the volume dirty (clear clean unmount bit) */
1c79356b
A
277 vcb->vcbAtrb &= ~kHFSVolumeUnmountedMask;
278
6d2010ae 279 if (error == noErr) {
db609669 280 error = cat_idlookup(hfsmp, kHFSRootFolderID, 0, 0, NULL, NULL, NULL);
6d2010ae
A
281 if (HFS_MOUNT_DEBUG) {
282 printf("hfs_mounthfs (std): error looking up root folder (%d) \n", error);
283 }
284 }
285
286 if (error == noErr) {
287 /* If the disk isn't write protected.. */
288 if ( !(vcb->vcbAtrb & kHFSVolumeHardwareLockMask)) {
289 MarkVCBDirty (vcb); // mark VCB dirty so it will be written
290 }
291 }
292
935ed37a
A
293 /*
294 * all done with system files so we can unlock now...
295 */
296 hfs_unlock(VTOC(hfsmp->hfs_allocation_vp));
297 hfs_unlock(VTOC(hfsmp->hfs_catalog_vp));
298 hfs_unlock(VTOC(hfsmp->hfs_extents_vp));
6d2010ae 299
d41d1dae
A
300 if (error == noErr) {
301 /* If successful, then we can just return once we've unlocked the cnodes */
302 return error;
303 }
1c79356b
A
304
305 //-- Release any resources allocated so far before exiting with an error:
9bccf70c 306MtVolErr:
6d2010ae 307 hfsUnmount(hfsmp, NULL);
1c79356b 308
9bccf70c 309 return (error);
1c79356b
A
310}
311
39236c6e
A
312#endif
313
1c79356b
A
314//*******************************************************************************
315// Routine: hfs_MountHFSPlusVolume
316//
317//
318//*******************************************************************************
319
320OSErr hfs_MountHFSPlusVolume(struct hfsmount *hfsmp, HFSPlusVolumeHeader *vhp,
2d21ac55 321 off_t embeddedOffset, u_int64_t disksize, __unused struct proc *p, void *args, kauth_cred_t cred)
1c79356b 322{
9bccf70c
A
323 register ExtendedVCB *vcb;
324 struct cat_desc cndesc;
325 struct cat_attr cnattr;
55e303ae 326 struct cat_fork cfork;
2d21ac55 327 u_int32_t blockSize;
91447636 328 daddr64_t spare_sectors;
55e303ae
A
329 struct BTreeInfoRec btinfo;
330 u_int16_t signature;
2d21ac55 331 u_int16_t hfs_version;
6d2010ae 332 int newvnode_flags = 0;
55e303ae 333 int i;
9bccf70c 334 OSErr retval;
6d2010ae
A
335 char converted_volname[256];
336 size_t volname_length = 0;
337 size_t conv_volname_length = 0;
1c79356b 338
55e303ae 339 signature = SWAP_BE16(vhp->signature);
2d21ac55 340 hfs_version = SWAP_BE16(vhp->version);
55e303ae
A
341
342 if (signature == kHFSPlusSigWord) {
2d21ac55 343 if (hfs_version != kHFSPlusVersion) {
39236c6e 344 printf("hfs_mount: invalid HFS+ version: %x\n", hfs_version);
55e303ae
A
345 return (EINVAL);
346 }
347 } else if (signature == kHFSXSigWord) {
2d21ac55 348 if (hfs_version != kHFSXVersion) {
39236c6e 349 printf("hfs_mount: invalid HFSX version: %x\n", hfs_version);
55e303ae
A
350 return (EINVAL);
351 }
352 /* The in-memory signature is always 'H+'. */
353 signature = kHFSPlusSigWord;
354 hfsmp->hfs_flags |= HFS_X;
355 } else {
91447636
A
356 /* Removed printf for invalid HFS+ signature because it gives
357 * false error for UFS root volume
358 */
6d2010ae 359 if (HFS_MOUNT_DEBUG) {
39236c6e 360 printf("hfs_mounthfsplus: unknown Volume Signature : %x\n", signature);
6d2010ae 361 }
b4c24cb9
A
362 return (EINVAL);
363 }
1c79356b 364
9bccf70c
A
365 /* Block size must be at least 512 and a power of 2 */
366 blockSize = SWAP_BE32(vhp->blockSize);
6d2010ae
A
367 if (blockSize < 512 || !powerof2(blockSize)) {
368 if (HFS_MOUNT_DEBUG) {
369 printf("hfs_mounthfsplus: invalid blocksize (%d) \n", blockSize);
370 }
9bccf70c 371 return (EINVAL);
6d2010ae 372 }
1c79356b
A
373
374 /* don't mount a writable volume if its dirty, it must be cleaned by fsck_hfs */
55e303ae 375 if ((hfsmp->hfs_flags & HFS_READ_ONLY) == 0 && hfsmp->jnl == NULL &&
6d2010ae
A
376 (SWAP_BE32(vhp->attributes) & kHFSVolumeUnmountedMask) == 0) {
377 if (HFS_MOUNT_DEBUG) {
378 printf("hfs_mounthfsplus: cannot mount dirty non-journaled volumes\n");
379 }
1c79356b 380 return (EINVAL);
6d2010ae 381 }
d52fe63f
A
382
383 /* Make sure we can live with the physical block size. */
593a1d5f
A
384 if ((disksize & (hfsmp->hfs_logical_block_size - 1)) ||
385 (embeddedOffset & (hfsmp->hfs_logical_block_size - 1)) ||
386 (blockSize < hfsmp->hfs_logical_block_size)) {
6d2010ae
A
387 if (HFS_MOUNT_DEBUG) {
388 printf("hfs_mounthfsplus: invalid physical blocksize (%d), hfs_logical_blocksize (%d) \n",
389 blockSize, hfsmp->hfs_logical_block_size);
390 }
d52fe63f
A
391 return (ENXIO);
392 }
593a1d5f
A
393
394 /* If allocation block size is less than the physical
395 * block size, we assume that the physical block size
396 * is same as logical block size. The physical block
397 * size value is used to round down the offsets for
398 * reading and writing the primary and alternate volume
399 * headers at physical block boundary and will cause
400 * problems if it is less than the block size.
401 */
402 if (blockSize < hfsmp->hfs_physical_block_size) {
403 hfsmp->hfs_physical_block_size = hfsmp->hfs_logical_block_size;
c910b4d9 404 hfsmp->hfs_log_per_phys = 1;
593a1d5f
A
405 }
406
1c79356b
A
407 /*
408 * The VolumeHeader seems OK: transfer info from it into VCB
409 * Note - the VCB starts out clear (all zeros)
410 */
411 vcb = HFSTOVCB(hfsmp);
412
55e303ae 413 vcb->vcbSigWord = signature;
b4c24cb9 414 vcb->vcbJinfoBlock = SWAP_BE32(vhp->journalInfoBlock);
9bccf70c 415 vcb->vcbLsMod = to_bsd_time(SWAP_BE32(vhp->modifyDate));
91447636 416 vcb->vcbAtrb = SWAP_BE32(vhp->attributes);
9bccf70c
A
417 vcb->vcbClpSiz = SWAP_BE32(vhp->rsrcClumpSize);
418 vcb->vcbNxtCNID = SWAP_BE32(vhp->nextCatalogID);
419 vcb->vcbVolBkUp = to_bsd_time(SWAP_BE32(vhp->backupDate));
420 vcb->vcbWrCnt = SWAP_BE32(vhp->writeCount);
421 vcb->vcbFilCnt = SWAP_BE32(vhp->fileCount);
422 vcb->vcbDirCnt = SWAP_BE32(vhp->folderCount);
1c79356b
A
423
424 /* copy 32 bytes of Finder info */
425 bcopy(vhp->finderInfo, vcb->vcbFndrInfo, sizeof(vhp->finderInfo));
426
427 vcb->vcbAlBlSt = 0; /* hfs+ allocation blocks start at first block of volume */
55e303ae 428 if ((hfsmp->hfs_flags & HFS_READ_ONLY) == 0)
9bccf70c 429 vcb->vcbWrCnt++; /* compensate for write of Volume Header on last flush */
1c79356b 430
9bccf70c
A
431 /* Now fill in the Extended VCB info */
432 vcb->nextAllocation = SWAP_BE32(vhp->nextAllocation);
433 vcb->totalBlocks = SWAP_BE32(vhp->totalBlocks);
2d21ac55 434 vcb->allocLimit = vcb->totalBlocks;
9bccf70c 435 vcb->freeBlocks = SWAP_BE32(vhp->freeBlocks);
55e303ae 436 vcb->blockSize = blockSize;
9bccf70c
A
437 vcb->encodingsBitmap = SWAP_BE64(vhp->encodingsBitmap);
438 vcb->localCreateDate = SWAP_BE32(vhp->createDate);
1c79356b 439
9bccf70c 440 vcb->hfsPlusIOPosOffset = embeddedOffset;
1c79356b 441
9bccf70c
A
442 /* Default to no free block reserve */
443 vcb->reserveBlocks = 0;
1c79356b 444
9bccf70c
A
445 /*
446 * Update the logical block size in the mount struct
447 * (currently set up from the wrapper MDB) using the
448 * new blocksize value:
449 */
593a1d5f 450 hfsmp->hfs_logBlockSize = BestBlockSizeFit(vcb->blockSize, MAXBSIZE, hfsmp->hfs_logical_block_size);
0b4e3aa0 451 vcb->vcbVBMIOSize = min(vcb->blockSize, MAXPHYSIO);
1c79356b 452
91447636
A
453 /*
454 * Validate and initialize the location of the alternate volume header.
455 */
593a1d5f 456 spare_sectors = hfsmp->hfs_logical_block_count -
91447636 457 (((daddr64_t)vcb->totalBlocks * blockSize) /
593a1d5f 458 hfsmp->hfs_logical_block_size);
91447636 459
593a1d5f 460 if (spare_sectors > (daddr64_t)(blockSize / hfsmp->hfs_logical_block_size)) {
91447636
A
461 hfsmp->hfs_alt_id_sector = 0; /* partition has grown! */
462 } else {
593a1d5f
A
463 hfsmp->hfs_alt_id_sector = (hfsmp->hfsPlusIOPosOffset / hfsmp->hfs_logical_block_size) +
464 HFS_ALT_SECTOR(hfsmp->hfs_logical_block_size,
465 hfsmp->hfs_logical_block_count);
91447636
A
466 }
467
9bccf70c 468 bzero(&cndesc, sizeof(cndesc));
91447636 469 cndesc.cd_parentcnid = kHFSRootParentID;
55e303ae 470 cndesc.cd_flags |= CD_ISMETA;
9bccf70c 471 bzero(&cnattr, sizeof(cnattr));
2d21ac55 472 cnattr.ca_linkcount = 1;
9bccf70c 473 cnattr.ca_mode = S_IFREG;
1c79356b
A
474
475 /*
9bccf70c
A
476 * Set up Extents B-tree vnode
477 */
478 cndesc.cd_nameptr = hfs_extname;
2d21ac55 479 cndesc.cd_namelen = strlen((char *)hfs_extname);
9bccf70c
A
480 cndesc.cd_cnid = cnattr.ca_fileid = kHFSExtentsFileID;
481
55e303ae 482 cfork.cf_size = SWAP_BE64 (vhp->extentsFile.logicalSize);
593a1d5f 483 cfork.cf_new_size= 0;
55e303ae
A
484 cfork.cf_clump = SWAP_BE32 (vhp->extentsFile.clumpSize);
485 cfork.cf_blocks = SWAP_BE32 (vhp->extentsFile.totalBlocks);
486 cfork.cf_vblocks = 0;
487 cnattr.ca_blocks = cfork.cf_blocks;
488 for (i = 0; i < kHFSPlusExtentDensity; i++) {
489 cfork.cf_extents[i].startBlock =
490 SWAP_BE32 (vhp->extentsFile.extents[i].startBlock);
491 cfork.cf_extents[i].blockCount =
492 SWAP_BE32 (vhp->extentsFile.extents[i].blockCount);
493 }
91447636 494 retval = hfs_getnewvnode(hfsmp, NULL, NULL, &cndesc, 0, &cnattr, &cfork,
6d2010ae 495 &hfsmp->hfs_extents_vp, &newvnode_flags);
2d21ac55 496 if (retval)
c910b4d9 497 {
6d2010ae
A
498 if (HFS_MOUNT_DEBUG) {
499 printf("hfs_mounthfsplus: hfs_getnewvnode returned (%d) getting extentoverflow BT\n", retval);
500 }
2d21ac55 501 goto ErrorExit;
c910b4d9 502 }
2d21ac55
A
503 hfsmp->hfs_extents_cp = VTOC(hfsmp->hfs_extents_vp);
504 hfs_unlock(hfsmp->hfs_extents_cp);
9bccf70c 505
91447636 506 retval = MacToVFSError(BTOpenPath(VTOF(hfsmp->hfs_extents_vp),
55e303ae 507 (KeyCompareProcPtr) CompareExtentKeysPlus));
2d21ac55 508 if (retval)
c910b4d9 509 {
6d2010ae
A
510 if (HFS_MOUNT_DEBUG) {
511 printf("hfs_mounthfsplus: BTOpenPath returned (%d) getting extentoverflow BT\n", retval);
512 }
9bccf70c 513 goto ErrorExit;
c910b4d9 514 }
1c79356b 515 /*
9bccf70c 516 * Set up Catalog B-tree vnode
1c79356b 517 */
9bccf70c 518 cndesc.cd_nameptr = hfs_catname;
2d21ac55 519 cndesc.cd_namelen = strlen((char *)hfs_catname);
9bccf70c
A
520 cndesc.cd_cnid = cnattr.ca_fileid = kHFSCatalogFileID;
521
55e303ae
A
522 cfork.cf_size = SWAP_BE64 (vhp->catalogFile.logicalSize);
523 cfork.cf_clump = SWAP_BE32 (vhp->catalogFile.clumpSize);
524 cfork.cf_blocks = SWAP_BE32 (vhp->catalogFile.totalBlocks);
525 cfork.cf_vblocks = 0;
526 cnattr.ca_blocks = cfork.cf_blocks;
527 for (i = 0; i < kHFSPlusExtentDensity; i++) {
528 cfork.cf_extents[i].startBlock =
529 SWAP_BE32 (vhp->catalogFile.extents[i].startBlock);
530 cfork.cf_extents[i].blockCount =
531 SWAP_BE32 (vhp->catalogFile.extents[i].blockCount);
532 }
91447636 533 retval = hfs_getnewvnode(hfsmp, NULL, NULL, &cndesc, 0, &cnattr, &cfork,
6d2010ae 534 &hfsmp->hfs_catalog_vp, &newvnode_flags);
9bccf70c 535 if (retval) {
6d2010ae
A
536 if (HFS_MOUNT_DEBUG) {
537 printf("hfs_mounthfsplus: hfs_getnewvnode returned (%d) getting catalog BT\n", retval);
538 }
9bccf70c
A
539 goto ErrorExit;
540 }
2d21ac55
A
541 hfsmp->hfs_catalog_cp = VTOC(hfsmp->hfs_catalog_vp);
542 hfs_unlock(hfsmp->hfs_catalog_cp);
543
91447636 544 retval = MacToVFSError(BTOpenPath(VTOF(hfsmp->hfs_catalog_vp),
55e303ae 545 (KeyCompareProcPtr) CompareExtendedCatalogKeys));
9bccf70c 546 if (retval) {
6d2010ae
A
547 if (HFS_MOUNT_DEBUG) {
548 printf("hfs_mounthfsplus: BTOpenPath returned (%d) getting catalog BT\n", retval);
549 }
9bccf70c
A
550 goto ErrorExit;
551 }
55e303ae 552 if ((hfsmp->hfs_flags & HFS_X) &&
91447636 553 BTGetInformation(VTOF(hfsmp->hfs_catalog_vp), 0, &btinfo) == 0) {
55e303ae
A
554 if (btinfo.keyCompareType == kHFSBinaryCompare) {
555 hfsmp->hfs_flags |= HFS_CASE_SENSITIVE;
556 /* Install a case-sensitive key compare */
91447636 557 (void) BTOpenPath(VTOF(hfsmp->hfs_catalog_vp),
55e303ae
A
558 (KeyCompareProcPtr)cat_binarykeycompare);
559 }
560 }
1c79356b
A
561
562 /*
9bccf70c 563 * Set up Allocation file vnode
1c79356b 564 */
9bccf70c 565 cndesc.cd_nameptr = hfs_vbmname;
2d21ac55 566 cndesc.cd_namelen = strlen((char *)hfs_vbmname);
9bccf70c
A
567 cndesc.cd_cnid = cnattr.ca_fileid = kHFSAllocationFileID;
568
55e303ae
A
569 cfork.cf_size = SWAP_BE64 (vhp->allocationFile.logicalSize);
570 cfork.cf_clump = SWAP_BE32 (vhp->allocationFile.clumpSize);
571 cfork.cf_blocks = SWAP_BE32 (vhp->allocationFile.totalBlocks);
572 cfork.cf_vblocks = 0;
573 cnattr.ca_blocks = cfork.cf_blocks;
574 for (i = 0; i < kHFSPlusExtentDensity; i++) {
575 cfork.cf_extents[i].startBlock =
576 SWAP_BE32 (vhp->allocationFile.extents[i].startBlock);
577 cfork.cf_extents[i].blockCount =
578 SWAP_BE32 (vhp->allocationFile.extents[i].blockCount);
579 }
91447636 580 retval = hfs_getnewvnode(hfsmp, NULL, NULL, &cndesc, 0, &cnattr, &cfork,
6d2010ae 581 &hfsmp->hfs_allocation_vp, &newvnode_flags);
9bccf70c 582 if (retval) {
6d2010ae
A
583 if (HFS_MOUNT_DEBUG) {
584 printf("hfs_mounthfsplus: hfs_getnewvnode returned (%d) getting bitmap\n", retval);
585 }
9bccf70c
A
586 goto ErrorExit;
587 }
2d21ac55
A
588 hfsmp->hfs_allocation_cp = VTOC(hfsmp->hfs_allocation_vp);
589 hfs_unlock(hfsmp->hfs_allocation_cp);
9bccf70c 590
91447636
A
591 /*
592 * Set up Attribute B-tree vnode
593 */
594 if (vhp->attributesFile.totalBlocks != 0) {
595 cndesc.cd_nameptr = hfs_attrname;
2d21ac55 596 cndesc.cd_namelen = strlen((char *)hfs_attrname);
91447636
A
597 cndesc.cd_cnid = cnattr.ca_fileid = kHFSAttributesFileID;
598
599 cfork.cf_size = SWAP_BE64 (vhp->attributesFile.logicalSize);
600 cfork.cf_clump = SWAP_BE32 (vhp->attributesFile.clumpSize);
601 cfork.cf_blocks = SWAP_BE32 (vhp->attributesFile.totalBlocks);
602 cfork.cf_vblocks = 0;
603 cnattr.ca_blocks = cfork.cf_blocks;
604 for (i = 0; i < kHFSPlusExtentDensity; i++) {
605 cfork.cf_extents[i].startBlock =
606 SWAP_BE32 (vhp->attributesFile.extents[i].startBlock);
607 cfork.cf_extents[i].blockCount =
608 SWAP_BE32 (vhp->attributesFile.extents[i].blockCount);
609 }
610 retval = hfs_getnewvnode(hfsmp, NULL, NULL, &cndesc, 0, &cnattr, &cfork,
6d2010ae 611 &hfsmp->hfs_attribute_vp, &newvnode_flags);
91447636 612 if (retval) {
6d2010ae
A
613 if (HFS_MOUNT_DEBUG) {
614 printf("hfs_mounthfsplus: hfs_getnewvnode returned (%d) getting EA BT\n", retval);
615 }
91447636
A
616 goto ErrorExit;
617 }
2d21ac55
A
618 hfsmp->hfs_attribute_cp = VTOC(hfsmp->hfs_attribute_vp);
619 hfs_unlock(hfsmp->hfs_attribute_cp);
91447636
A
620 retval = MacToVFSError(BTOpenPath(VTOF(hfsmp->hfs_attribute_vp),
621 (KeyCompareProcPtr) hfs_attrkeycompare));
622 if (retval) {
6d2010ae
A
623 if (HFS_MOUNT_DEBUG) {
624 printf("hfs_mounthfsplus: BTOpenPath returned (%d) getting EA BT\n", retval);
625 }
626 goto ErrorExit;
627 }
628
629 /* Initialize vnode for virtual attribute data file that spans the
630 * entire file system space for performing I/O to attribute btree
631 * We hold iocount on the attrdata vnode for the entire duration
632 * of mount (similar to btree vnodes)
633 */
634 retval = init_attrdata_vnode(hfsmp);
635 if (retval) {
636 if (HFS_MOUNT_DEBUG) {
637 printf("hfs_mounthfsplus: init_attrdata_vnode returned (%d) for virtual EA file\n", retval);
638 }
91447636
A
639 goto ErrorExit;
640 }
641 }
642
2d21ac55
A
643 /*
644 * Set up Startup file vnode
645 */
646 if (vhp->startupFile.totalBlocks != 0) {
647 cndesc.cd_nameptr = hfs_startupname;
648 cndesc.cd_namelen = strlen((char *)hfs_startupname);
649 cndesc.cd_cnid = cnattr.ca_fileid = kHFSStartupFileID;
650
651 cfork.cf_size = SWAP_BE64 (vhp->startupFile.logicalSize);
652 cfork.cf_clump = SWAP_BE32 (vhp->startupFile.clumpSize);
653 cfork.cf_blocks = SWAP_BE32 (vhp->startupFile.totalBlocks);
654 cfork.cf_vblocks = 0;
655 cnattr.ca_blocks = cfork.cf_blocks;
656 for (i = 0; i < kHFSPlusExtentDensity; i++) {
657 cfork.cf_extents[i].startBlock =
658 SWAP_BE32 (vhp->startupFile.extents[i].startBlock);
659 cfork.cf_extents[i].blockCount =
660 SWAP_BE32 (vhp->startupFile.extents[i].blockCount);
661 }
662 retval = hfs_getnewvnode(hfsmp, NULL, NULL, &cndesc, 0, &cnattr, &cfork,
6d2010ae 663 &hfsmp->hfs_startup_vp, &newvnode_flags);
2d21ac55 664 if (retval) {
6d2010ae
A
665 if (HFS_MOUNT_DEBUG) {
666 printf("hfs_mounthfsplus: hfs_getnewvnode returned (%d) getting startup file\n", retval);
667 }
2d21ac55
A
668 goto ErrorExit;
669 }
670 hfsmp->hfs_startup_cp = VTOC(hfsmp->hfs_startup_vp);
671 hfs_unlock(hfsmp->hfs_startup_cp);
672 }
673
39236c6e
A
674 /*
675 * Pick up volume name and create date
676 *
677 * Acquiring the volume name should not manipulate the bitmap, only the catalog
678 * btree and possibly the extents overflow b-tree.
679 */
db609669 680 retval = cat_idlookup(hfsmp, kHFSRootFolderID, 0, 0, &cndesc, &cnattr, NULL);
9bccf70c 681 if (retval) {
6d2010ae
A
682 if (HFS_MOUNT_DEBUG) {
683 printf("hfs_mounthfsplus: cat_idlookup returned (%d) getting rootfolder \n", retval);
684 }
9bccf70c
A
685 goto ErrorExit;
686 }
6d2010ae 687 vcb->hfs_itime = cnattr.ca_itime;
9bccf70c
A
688 vcb->volumeNameEncodingHint = cndesc.cd_encoding;
689 bcopy(cndesc.cd_nameptr, vcb->vcbVN, min(255, cndesc.cd_namelen));
6d2010ae 690 volname_length = strlen ((const char*)vcb->vcbVN);
9bccf70c 691 cat_releasedesc(&cndesc);
6d2010ae 692
316670eb 693#define DKIOCCSSETLVNAME _IOW('d', 198, char[256])
6d2010ae 694
1c79356b 695
6d2010ae
A
696 /* Send the volume name down to CoreStorage if necessary */
697 retval = utf8_normalizestr(vcb->vcbVN, volname_length, (u_int8_t*)converted_volname, &conv_volname_length, 256, UTF_PRECOMPOSED);
698 if (retval == 0) {
699 (void) VNOP_IOCTL (hfsmp->hfs_devvp, DKIOCCSSETLVNAME, converted_volname, 0, vfs_context_current());
700 }
701
702 /* reset retval == 0. we don't care about errors in volname conversion */
703 retval = 0;
39236c6e 704
6d2010ae 705
39236c6e
A
706 /*
707 * We now always initiate a full bitmap scan even if the volume is read-only because this is
708 * our only shot to do I/Os of dramaticallly different sizes than what the buffer cache ordinarily
709 * expects. TRIMs will not be delivered to the underlying media if the volume is not
710 * read-write though.
711 */
712 thread_t allocator_scanner;
713 hfsmp->scan_var = 0;
714
715 /* Take the HFS mount mutex and wait on scan_var */
716 hfs_lock_mount (hfsmp);
717
718 kernel_thread_start ((thread_continue_t) hfs_scan_blocks, hfsmp, &allocator_scanner);
719 /* Wait until it registers that it's got the appropriate locks */
720 while ((hfsmp->scan_var & HFS_ALLOCATOR_SCAN_INFLIGHT) == 0) {
721 (void) msleep (&hfsmp->scan_var, &hfsmp->hfs_mutex, (PDROP | PINOD), "hfs_scan_blocks", 0);
722 if (hfsmp->scan_var & HFS_ALLOCATOR_SCAN_INFLIGHT) {
723 break;
724 }
725 else {
726 hfs_lock_mount (hfsmp);
727 }
728 }
729
730 thread_deallocate (allocator_scanner);
731
1c79356b
A
732 /* mark the volume dirty (clear clean unmount bit) */
733 vcb->vcbAtrb &= ~kHFSVolumeUnmountedMask;
55e303ae 734 if (hfsmp->jnl && (hfsmp->hfs_flags & HFS_READ_ONLY) == 0) {
91447636 735 hfs_flushvolumeheader(hfsmp, TRUE, 0);
b4c24cb9 736 }
1c79356b 737
2d21ac55
A
738 /* kHFSHasFolderCount is only supported/updated on HFSX volumes */
739 if ((hfsmp->hfs_flags & HFS_X) != 0) {
740 hfsmp->hfs_flags |= HFS_FOLDERCOUNT;
741 }
1c79356b 742
b4c24cb9
A
743 //
744 // Check if we need to do late journal initialization. This only
745 // happens if a previous version of MacOS X (or 9) touched the disk.
746 // In that case hfs_late_journal_init() will go re-locate the journal
747 // and journal_info_block files and validate that they're still kosher.
748 //
749 if ( (vcb->vcbAtrb & kHFSVolumeJournaledMask)
750 && (SWAP_BE32(vhp->lastMountedVersion) != kHFSJMountVersion)
2d21ac55 751 && (hfsmp->jnl == NULL)) {
b4c24cb9
A
752
753 retval = hfs_late_journal_init(hfsmp, vhp, args);
754 if (retval != 0) {
b0d623f7
A
755 if (retval == EROFS) {
756 // EROFS is a special error code that means the volume has an external
757 // journal which we couldn't find. in that case we do not want to
758 // rewrite the volume header - we'll just refuse to mount the volume.
6d2010ae
A
759 if (HFS_MOUNT_DEBUG) {
760 printf("hfs_mounthfsplus: hfs_late_journal_init returned (%d), maybe an external jnl?\n", retval);
761 }
b0d623f7
A
762 retval = EINVAL;
763 goto ErrorExit;
764 }
765
b4c24cb9 766 hfsmp->jnl = NULL;
91447636
A
767
768 // if the journal failed to open, then set the lastMountedVersion
769 // to be "FSK!" which fsck_hfs will see and force the fsck instead
770 // of just bailing out because the volume is journaled.
771 if (!(hfsmp->hfs_flags & HFS_READ_ONLY)) {
772 HFSPlusVolumeHeader *jvhp;
773 daddr64_t mdb_offset;
774 struct buf *bp = NULL;
775
776 hfsmp->hfs_flags |= HFS_NEED_JNL_RESET;
777
778 mdb_offset = (daddr64_t)((embeddedOffset / blockSize) + HFS_PRI_SECTOR(blockSize));
779
b0d623f7 780 bp = NULL;
593a1d5f
A
781 retval = (int)buf_meta_bread(hfsmp->hfs_devvp,
782 HFS_PHYSBLK_ROUNDDOWN(mdb_offset, hfsmp->hfs_log_per_phys),
783 hfsmp->hfs_physical_block_size, cred, &bp);
91447636 784 if (retval == 0) {
593a1d5f 785 jvhp = (HFSPlusVolumeHeader *)(buf_dataptr(bp) + HFS_PRI_OFFSET(hfsmp->hfs_physical_block_size));
91447636
A
786
787 if (SWAP_BE16(jvhp->signature) == kHFSPlusSigWord || SWAP_BE16(jvhp->signature) == kHFSXSigWord) {
788 printf ("hfs(3): Journal replay fail. Writing lastMountVersion as FSK!\n");
789 jvhp->lastMountedVersion = SWAP_BE32(kFSKMountVersion);
790 buf_bwrite(bp);
791 } else {
792 buf_brelse(bp);
793 }
794 bp = NULL;
795 } else if (bp) {
796 buf_brelse(bp);
797 // clear this so the error exit path won't try to use it
798 bp = NULL;
799 }
800 }
6d2010ae
A
801
802 if (HFS_MOUNT_DEBUG) {
803 printf("hfs_mounthfsplus: hfs_late_journal_init returned (%d)\n", retval);
804 }
91447636 805 retval = EINVAL;
b4c24cb9
A
806 goto ErrorExit;
807 } else if (hfsmp->jnl) {
2d21ac55 808 vfs_setflags(hfsmp->hfs_mp, (u_int64_t)((unsigned int)MNT_JOURNALED));
b4c24cb9 809 }
743b1565 810 } else if (hfsmp->jnl || ((vcb->vcbAtrb & kHFSVolumeJournaledMask) && (hfsmp->hfs_flags & HFS_READ_ONLY))) {
b4c24cb9
A
811 struct cat_attr jinfo_attr, jnl_attr;
812
743b1565
A
813 if (hfsmp->hfs_flags & HFS_READ_ONLY) {
814 vcb->vcbAtrb &= ~kHFSVolumeJournaledMask;
815 }
816
b4c24cb9
A
817 // if we're here we need to fill in the fileid's for the
818 // journal and journal_info_block.
819 hfsmp->hfs_jnlinfoblkid = GetFileInfo(vcb, kRootDirID, ".journal_info_block", &jinfo_attr, NULL);
820 hfsmp->hfs_jnlfileid = GetFileInfo(vcb, kRootDirID, ".journal", &jnl_attr, NULL);
821 if (hfsmp->hfs_jnlinfoblkid == 0 || hfsmp->hfs_jnlfileid == 0) {
822 printf("hfs: danger! couldn't find the file-id's for the journal or journal_info_block\n");
823 printf("hfs: jnlfileid %d, jnlinfoblkid %d\n", hfsmp->hfs_jnlfileid, hfsmp->hfs_jnlinfoblkid);
824 }
743b1565
A
825
826 if (hfsmp->hfs_flags & HFS_READ_ONLY) {
827 vcb->vcbAtrb |= kHFSVolumeJournaledMask;
828 }
2d21ac55
A
829
830 if (hfsmp->jnl == NULL) {
831 vfs_clearflags(hfsmp->hfs_mp, (u_int64_t)((unsigned int)MNT_JOURNALED));
832 }
b4c24cb9
A
833 }
834
39236c6e
A
835 if ( !(vcb->vcbAtrb & kHFSVolumeHardwareLockMask) ) // if the disk is not write protected
836 {
837 MarkVCBDirty( vcb ); // mark VCB dirty so it will be written
838 }
839
840 /*
841 * Distinguish 3 potential cases involving content protection:
842 * 1. mount point bit set; vcbAtrb does not support it. Fail.
843 * 2. mount point bit set; vcbattrb supports it. we're good.
844 * 3. mount point bit not set; vcbatrb supports it, turn bit on, then good.
845 */
846 if (vfs_flags(hfsmp->hfs_mp) & MNT_CPROTECT) {
847 /* Does the mount point support it ? */
848 if ((vcb->vcbAtrb & kHFSContentProtectionMask) == 0) {
849 /* Case 1 above */
850 retval = EINVAL;
851 goto ErrorExit;
852 }
853 }
854 else {
855 /* not requested in the mount point. Is it in FS? */
856 if (vcb->vcbAtrb & kHFSContentProtectionMask) {
857 /* Case 3 above */
858 vfs_setflags (hfsmp->hfs_mp, MNT_CPROTECT);
859 }
860 }
861
862 /* At this point, if the mount point flag is set, we can enable it. */
863 if (vfs_flags(hfsmp->hfs_mp) & MNT_CPROTECT) {
864 /* Cases 2+3 above */
865#if CONFIG_PROTECT
866 /* Get the EAs as needed. */
867 int cperr = 0;
868 uint16_t majorversion;
869 uint16_t minorversion;
870
871 struct cp_root_xattr *xattr = NULL;
872 MALLOC (xattr, struct cp_root_xattr*, sizeof(struct cp_root_xattr), M_TEMP, M_WAITOK);
873 if (xattr == NULL) {
874 retval = ENOMEM;
875 goto ErrorExit;
876 }
877 bzero (xattr, sizeof(struct cp_root_xattr));
878
879 /* go get the EA to get the version information */
880 cperr = cp_getrootxattr (hfsmp, xattr);
881 /*
882 * If there was no EA there, then write one out.
883 * Assuming EA is not present on the root means
884 * this is an erase install or a very old FS
885 */
886
887 if (cperr == 0) {
888 /* Have to run a valid CP version. */
889 if ((xattr->major_version < CP_PREV_MAJOR_VERS) || (xattr->major_version > CP_NEW_MAJOR_VERS)) {
890 cperr = EINVAL;
891 }
892 }
893 else if (cperr == ENOATTR) {
894 printf("No root EA set, creating new EA with new version: %d\n", CP_NEW_MAJOR_VERS);
895 bzero(xattr, sizeof(struct cp_root_xattr));
896 xattr->major_version = CP_NEW_MAJOR_VERS;
897 xattr->minor_version = CP_MINOR_VERS;
898 xattr->flags = 0;
899 cperr = cp_setrootxattr (hfsmp, xattr);
900 }
901 majorversion = xattr->major_version;
902 minorversion = xattr->minor_version;
903 if (xattr) {
904 FREE(xattr, M_TEMP);
905 }
906
907 /* Recheck for good status */
908 if (cperr == 0) {
909 /* If we got here, then the CP version is valid. Set it in the mount point */
910 hfsmp->hfs_running_cp_major_vers = majorversion;
911 printf("Running with CP root xattr: %d.%d\n", majorversion, minorversion);
912
913 /*
914 * Acquire the boot-arg for the AKS default key.
915 * Ensure that the boot-arg's value is valid for FILES (not directories),
916 * since only files are actually protected for now.
917 */
918 PE_parse_boot_argn("aks_default_class", &hfsmp->default_cp_class, sizeof(hfsmp->default_cp_class));
919 if (cp_is_valid_class(0, hfsmp->default_cp_class) == 0) {
920 hfsmp->default_cp_class = PROTECTION_CLASS_D;
921 }
922 }
923 else {
924 retval = EPERM;
925 goto ErrorExit;
926 }
927#else
928 /* If CONFIG_PROTECT not built, ignore CP */
929 vfs_clearflags(hfsmp->hfs_mp, MNT_CPROTECT);
930#endif
931 }
932
55e303ae
A
933 /*
934 * Establish a metadata allocation zone.
935 */
6d2010ae 936 hfs_metadatazone_init(hfsmp, false);
55e303ae
A
937
938 /*
939 * Make any metadata zone adjustments.
940 */
941 if (hfsmp->hfs_flags & HFS_METADATA_ZONE) {
942 /* Keep the roving allocator out of the metadata zone. */
943 if (vcb->nextAllocation >= hfsmp->hfs_metazone_start &&
944 vcb->nextAllocation <= hfsmp->hfs_metazone_end) {
2d21ac55 945 HFS_UPDATE_NEXT_ALLOCATION(hfsmp, hfsmp->hfs_metazone_end + 1);
55e303ae 946 }
b0d623f7
A
947 } else {
948 if (vcb->nextAllocation <= 1) {
949 vcb->nextAllocation = hfsmp->hfs_min_alloc_start;
950 }
55e303ae 951 }
b0d623f7 952 vcb->sparseAllocation = hfsmp->hfs_min_alloc_start;
55e303ae 953
2d21ac55
A
954 /* Setup private/hidden directories for hardlinks. */
955 hfs_privatedir_init(hfsmp, FILE_HARDLINKS);
956 hfs_privatedir_init(hfsmp, DIR_HARDLINKS);
957
91447636 958 if ((hfsmp->hfs_flags & HFS_READ_ONLY) == 0)
55e303ae
A
959 hfs_remove_orphans(hfsmp);
960
b0d623f7
A
961 /* See if we need to erase unused Catalog nodes due to <rdar://problem/6947811>. */
962 if ((hfsmp->hfs_flags & HFS_READ_ONLY) == 0)
963 {
964 retval = hfs_erase_unused_nodes(hfsmp);
6d2010ae
A
965 if (retval) {
966 if (HFS_MOUNT_DEBUG) {
967 printf("hfs_mounthfsplus: hfs_erase_unused_nodes returned (%d) for %s \n", retval, hfsmp->vcbVN);
968 }
969
b0d623f7 970 goto ErrorExit;
6d2010ae 971 }
b0d623f7 972 }
39236c6e 973
55e303ae
A
974 /*
975 * Allow hot file clustering if conditions allow.
976 */
977 if ((hfsmp->hfs_flags & HFS_METADATA_ZONE) &&
6d2010ae 978 ((hfsmp->hfs_flags & (HFS_READ_ONLY | HFS_SSD)) == 0)) {
91447636 979 (void) hfs_recording_init(hfsmp);
55e303ae 980 }
b4c24cb9 981
2d21ac55
A
982 /* Force ACLs on HFS+ file systems. */
983 vfs_setextendedsecurity(HFSTOVFS(hfsmp));
984
6d2010ae
A
985 /* Enable extent-based extended attributes by default */
986 hfsmp->hfs_flags |= HFS_XATTR_EXTENTS;
987
1c79356b
A
988 return (0);
989
1c79356b
A
990ErrorExit:
991 /*
6d2010ae
A
992 * A fatal error occurred and the volume cannot be mounted, so
993 * release any resources that we acquired...
1c79356b 994 */
6d2010ae
A
995 hfsUnmount(hfsmp, NULL);
996
997 if (HFS_MOUNT_DEBUG) {
39236c6e 998 printf("hfs_mounthfsplus: encountered error (%d)\n", retval);
6d2010ae 999 }
1c79356b
A
1000 return (retval);
1001}
1002
1003
1004/*
1005 * ReleaseMetaFileVNode
1006 *
1007 * vp L - -
1008 */
1009static void ReleaseMetaFileVNode(struct vnode *vp)
1010{
9bccf70c 1011 struct filefork *fp;
1c79356b 1012
9bccf70c 1013 if (vp && (fp = VTOF(vp))) {
91447636 1014 if (fp->fcbBTCBPtr != NULL) {
39236c6e 1015 (void)hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT);
9bccf70c 1016 (void) BTClosePath(fp);
91447636
A
1017 hfs_unlock(VTOC(vp));
1018 }
1c79356b
A
1019
1020 /* release the node even if BTClosePath fails */
91447636
A
1021 vnode_recycle(vp);
1022 vnode_put(vp);
1c79356b 1023 }
1c79356b
A
1024}
1025
1026
1027/*************************************************************
1028*
1029* Unmounts a hfs volume.
1030* At this point vflush() has been called (to dump all non-metadata files)
1031*
1032*************************************************************/
1033
55e303ae 1034int
2d21ac55 1035hfsUnmount( register struct hfsmount *hfsmp, __unused struct proc *p)
1c79356b 1036{
6d2010ae
A
1037 /* Get rid of our attribute data vnode (if any). This is done
1038 * after the vflush() during mount, so we don't need to worry
1039 * about any locks.
1040 */
2d21ac55 1041 if (hfsmp->hfs_attrdata_vp) {
6d2010ae 1042 ReleaseMetaFileVNode(hfsmp->hfs_attrdata_vp);
2d21ac55
A
1043 hfsmp->hfs_attrdata_vp = NULLVP;
1044 }
1045
6d2010ae 1046 if (hfsmp->hfs_startup_vp) {
2d21ac55 1047 ReleaseMetaFileVNode(hfsmp->hfs_startup_vp);
6d2010ae
A
1048 hfsmp->hfs_startup_cp = NULL;
1049 hfsmp->hfs_startup_vp = NULL;
1050 }
1051
1052 if (hfsmp->hfs_attribute_vp) {
91447636 1053 ReleaseMetaFileVNode(hfsmp->hfs_attribute_vp);
6d2010ae
A
1054 hfsmp->hfs_attribute_cp = NULL;
1055 hfsmp->hfs_attribute_vp = NULL;
1056 }
1c79356b 1057
6d2010ae
A
1058 if (hfsmp->hfs_catalog_vp) {
1059 ReleaseMetaFileVNode(hfsmp->hfs_catalog_vp);
1060 hfsmp->hfs_catalog_cp = NULL;
1061 hfsmp->hfs_catalog_vp = NULL;
1062 }
1c79356b 1063
6d2010ae
A
1064 if (hfsmp->hfs_extents_vp) {
1065 ReleaseMetaFileVNode(hfsmp->hfs_extents_vp);
1066 hfsmp->hfs_extents_cp = NULL;
1067 hfsmp->hfs_extents_vp = NULL;
1068 }
1069
1070 if (hfsmp->hfs_allocation_vp) {
1071 ReleaseMetaFileVNode(hfsmp->hfs_allocation_vp);
1072 hfsmp->hfs_allocation_cp = NULL;
1073 hfsmp->hfs_allocation_vp = NULL;
1074 }
2d21ac55 1075
91447636 1076 return (0);
1c79356b
A
1077}
1078
1079
0b4e3aa0 1080/*
91447636 1081 * Test if fork has overflow extents.
39236c6e
A
1082 *
1083 * Returns:
1084 * non-zero - overflow extents exist
1085 * zero - overflow extents do not exist
0b4e3aa0 1086 */
55e303ae
A
1087__private_extern__
1088int
1089overflow_extents(struct filefork *fp)
1c79356b 1090{
b0d623f7 1091 u_int32_t blocks;
1c79356b 1092
2d21ac55
A
1093 //
1094 // If the vnode pointer is NULL then we're being called
1095 // from hfs_remove_orphans() with a faked-up filefork
1096 // and therefore it has to be an HFS+ volume. Otherwise
1097 // we check through the volume header to see what type
1098 // of volume we're on.
1099 //
1100 if (FTOV(fp) == NULL || VTOVCB(FTOV(fp))->vcbSigWord == kHFSPlusSigWord) {
9bccf70c
A
1101 if (fp->ff_extents[7].blockCount == 0)
1102 return (0);
1c79356b 1103
9bccf70c
A
1104 blocks = fp->ff_extents[0].blockCount +
1105 fp->ff_extents[1].blockCount +
1106 fp->ff_extents[2].blockCount +
1107 fp->ff_extents[3].blockCount +
1108 fp->ff_extents[4].blockCount +
1109 fp->ff_extents[5].blockCount +
1110 fp->ff_extents[6].blockCount +
1111 fp->ff_extents[7].blockCount;
1112 } else {
1113 if (fp->ff_extents[2].blockCount == 0)
1114 return false;
1115
1116 blocks = fp->ff_extents[0].blockCount +
1117 fp->ff_extents[1].blockCount +
1118 fp->ff_extents[2].blockCount;
1119 }
1c79356b 1120
9bccf70c
A
1121 return (fp->ff_blocks > blocks);
1122}
1c79356b 1123
6d2010ae
A
1124/*
1125 * Lock the HFS global journal lock
1126 */
1127int
39236c6e
A
1128hfs_lock_global (struct hfsmount *hfsmp, enum hfs_locktype locktype)
1129{
6d2010ae
A
1130 void *thread = current_thread();
1131
1132 if (hfsmp->hfs_global_lockowner == thread) {
1133 panic ("hfs_lock_global: locking against myself!");
1134 }
1135
1136 /* HFS_SHARED_LOCK */
1137 if (locktype == HFS_SHARED_LOCK) {
1138 lck_rw_lock_shared (&hfsmp->hfs_global_lock);
1139 hfsmp->hfs_global_lockowner = HFS_SHARED_OWNER;
1140 }
1141 /* HFS_EXCLUSIVE_LOCK */
1142 else {
1143 lck_rw_lock_exclusive (&hfsmp->hfs_global_lock);
1144 hfsmp->hfs_global_lockowner = thread;
1145 }
1146
1147 return 0;
1148}
1149
1150
1151/*
1152 * Unlock the HFS global journal lock
1153 */
1154void
39236c6e
A
1155hfs_unlock_global (struct hfsmount *hfsmp)
1156{
6d2010ae
A
1157
1158 void *thread = current_thread();
1159
1160 /* HFS_LOCK_EXCLUSIVE */
1161 if (hfsmp->hfs_global_lockowner == thread) {
1162 hfsmp->hfs_global_lockowner = NULL;
1163 lck_rw_unlock_exclusive (&hfsmp->hfs_global_lock);
1164 }
1165 /* HFS_LOCK_SHARED */
1166 else {
1167 lck_rw_unlock_shared (&hfsmp->hfs_global_lock);
1168 }
1169}
1170
39236c6e
A
1171/*
1172 * Lock the HFS mount lock
1173 *
1174 * Note: this is a mutex, not a rw lock!
1175 */
1176inline
1177void hfs_lock_mount (struct hfsmount *hfsmp) {
1178 lck_mtx_lock (&(hfsmp->hfs_mutex));
1179}
1180
1181/*
1182 * Unlock the HFS mount lock
1183 *
1184 * Note: this is a mutex, not a rw lock!
1185 */
1186inline
1187void hfs_unlock_mount (struct hfsmount *hfsmp) {
1188 lck_mtx_unlock (&(hfsmp->hfs_mutex));
1189}
1c79356b 1190
55e303ae 1191/*
91447636 1192 * Lock HFS system file(s).
55e303ae 1193 */
9bccf70c 1194int
39236c6e 1195hfs_systemfile_lock(struct hfsmount *hfsmp, int flags, enum hfs_locktype locktype)
1c79356b 1196{
91447636 1197 /*
2d21ac55 1198 * Locking order is Catalog file, Attributes file, Startup file, Bitmap file, Extents file
91447636
A
1199 */
1200 if (flags & SFL_CATALOG) {
2d21ac55
A
1201#ifdef HFS_CHECK_LOCK_ORDER
1202 if (hfsmp->hfs_attribute_cp && hfsmp->hfs_attribute_cp->c_lockowner == current_thread()) {
1203 panic("hfs_systemfile_lock: bad lock order (Attributes before Catalog)");
1204 }
1205 if (hfsmp->hfs_startup_cp && hfsmp->hfs_startup_cp->c_lockowner == current_thread()) {
1206 panic("hfs_systemfile_lock: bad lock order (Startup before Catalog)");
1207 }
1208 if (hfsmp-> hfs_extents_cp && hfsmp->hfs_extents_cp->c_lockowner == current_thread()) {
1209 panic("hfs_systemfile_lock: bad lock order (Extents before Catalog)");
1210 }
1211#endif /* HFS_CHECK_LOCK_ORDER */
1212
6d2010ae 1213 if (hfsmp->hfs_catalog_cp) {
39236c6e
A
1214 (void) hfs_lock(hfsmp->hfs_catalog_cp, locktype, HFS_LOCK_DEFAULT);
1215 /*
1216 * When the catalog file has overflow extents then
1217 * also acquire the extents b-tree lock if its not
1218 * already requested.
1219 */
1220 if (((flags & SFL_EXTENTS) == 0) &&
1221 (hfsmp->hfs_catalog_vp != NULL) &&
1222 (overflow_extents(VTOF(hfsmp->hfs_catalog_vp)))) {
1223 flags |= SFL_EXTENTS;
1224 }
6d2010ae
A
1225 } else {
1226 flags &= ~SFL_CATALOG;
1227 }
91447636 1228 }
2d21ac55 1229
39236c6e 1230 if (flags & SFL_ATTRIBUTE) {
2d21ac55
A
1231#ifdef HFS_CHECK_LOCK_ORDER
1232 if (hfsmp->hfs_startup_cp && hfsmp->hfs_startup_cp->c_lockowner == current_thread()) {
1233 panic("hfs_systemfile_lock: bad lock order (Startup before Attributes)");
1234 }
1235 if (hfsmp->hfs_extents_cp && hfsmp->hfs_extents_cp->c_lockowner == current_thread()) {
1236 panic("hfs_systemfile_lock: bad lock order (Extents before Attributes)");
1237 }
1238#endif /* HFS_CHECK_LOCK_ORDER */
1239
1240 if (hfsmp->hfs_attribute_cp) {
39236c6e 1241 (void) hfs_lock(hfsmp->hfs_attribute_cp, locktype, HFS_LOCK_DEFAULT);
91447636
A
1242 /*
1243 * When the attribute file has overflow extents then
1244 * also acquire the extents b-tree lock if its not
1245 * already requested.
1246 */
39236c6e
A
1247 if (((flags & SFL_EXTENTS) == 0) &&
1248 (hfsmp->hfs_attribute_vp != NULL) &&
1249 (overflow_extents(VTOF(hfsmp->hfs_attribute_vp)))) {
91447636
A
1250 flags |= SFL_EXTENTS;
1251 }
1252 } else {
1253 flags &= ~SFL_ATTRIBUTE;
1254 }
1255 }
39236c6e 1256
2d21ac55
A
1257 if (flags & SFL_STARTUP) {
1258#ifdef HFS_CHECK_LOCK_ORDER
1259 if (hfsmp-> hfs_extents_cp && hfsmp->hfs_extents_cp->c_lockowner == current_thread()) {
1260 panic("hfs_systemfile_lock: bad lock order (Extents before Startup)");
1261 }
1262#endif /* HFS_CHECK_LOCK_ORDER */
1263
6d2010ae 1264 if (hfsmp->hfs_startup_cp) {
39236c6e
A
1265 (void) hfs_lock(hfsmp->hfs_startup_cp, locktype, HFS_LOCK_DEFAULT);
1266 /*
1267 * When the startup file has overflow extents then
1268 * also acquire the extents b-tree lock if its not
1269 * already requested.
1270 */
1271 if (((flags & SFL_EXTENTS) == 0) &&
1272 (hfsmp->hfs_startup_vp != NULL) &&
1273 (overflow_extents(VTOF(hfsmp->hfs_startup_vp)))) {
1274 flags |= SFL_EXTENTS;
1275 }
6d2010ae
A
1276 } else {
1277 flags &= ~SFL_STARTUP;
1278 }
2d21ac55 1279 }
39236c6e 1280
2d21ac55
A
1281 /*
1282 * To prevent locks being taken in the wrong order, the extent lock
1283 * gets a bitmap lock as well.
1284 */
1285 if (flags & (SFL_BITMAP | SFL_EXTENTS)) {
39236c6e
A
1286 if (hfsmp->hfs_allocation_cp) {
1287 (void) hfs_lock(hfsmp->hfs_allocation_cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT);
6d2010ae
A
1288 /*
1289 * The bitmap lock is also grabbed when only extent lock
2d21ac55
A
1290 * was requested. Set the bitmap lock bit in the lock
1291 * flags which callers will use during unlock.
1292 */
1293 flags |= SFL_BITMAP;
39236c6e
A
1294 } else {
1295 flags &= ~SFL_BITMAP;
91447636
A
1296 }
1297 }
39236c6e 1298
91447636
A
1299 if (flags & SFL_EXTENTS) {
1300 /*
1301 * Since the extents btree lock is recursive we always
1302 * need exclusive access.
1303 */
6d2010ae 1304 if (hfsmp->hfs_extents_cp) {
39236c6e 1305 (void) hfs_lock(hfsmp->hfs_extents_cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT);
6d2010ae
A
1306 } else {
1307 flags &= ~SFL_EXTENTS;
1308 }
91447636 1309 }
39236c6e 1310
91447636
A
1311 return (flags);
1312}
9bccf70c 1313
91447636
A
1314/*
1315 * unlock HFS system file(s).
1316 */
91447636
A
1317void
1318hfs_systemfile_unlock(struct hfsmount *hfsmp, int flags)
1319{
1320 struct timeval tv;
1321 u_int32_t lastfsync;
1322 int numOfLockedBuffs;
9bccf70c 1323
2d21ac55
A
1324 if (hfsmp->jnl == NULL) {
1325 microuptime(&tv);
1326 lastfsync = tv.tv_sec;
1327 }
1328 if (flags & SFL_STARTUP && hfsmp->hfs_startup_cp) {
1329 hfs_unlock(hfsmp->hfs_startup_cp);
1330 }
1331 if (flags & SFL_ATTRIBUTE && hfsmp->hfs_attribute_cp) {
91447636
A
1332 if (hfsmp->jnl == NULL) {
1333 BTGetLastSync((FCB*)VTOF(hfsmp->hfs_attribute_vp), &lastfsync);
1334 numOfLockedBuffs = count_lock_queue();
1335 if ((numOfLockedBuffs > kMaxLockedMetaBuffers) ||
1336 ((numOfLockedBuffs > 1) && ((tv.tv_sec - lastfsync) >
1337 kMaxSecsForFsync))) {
1338 hfs_btsync(hfsmp->hfs_attribute_vp, HFS_SYNCTRANS);
1339 }
1340 }
2d21ac55 1341 hfs_unlock(hfsmp->hfs_attribute_cp);
9bccf70c 1342 }
6d2010ae 1343 if (flags & SFL_CATALOG && hfsmp->hfs_catalog_cp) {
91447636
A
1344 if (hfsmp->jnl == NULL) {
1345 BTGetLastSync((FCB*)VTOF(hfsmp->hfs_catalog_vp), &lastfsync);
1346 numOfLockedBuffs = count_lock_queue();
1347 if ((numOfLockedBuffs > kMaxLockedMetaBuffers) ||
1348 ((numOfLockedBuffs > 1) && ((tv.tv_sec - lastfsync) >
1349 kMaxSecsForFsync))) {
1350 hfs_btsync(hfsmp->hfs_catalog_vp, HFS_SYNCTRANS);
1351 }
9bccf70c 1352 }
2d21ac55 1353 hfs_unlock(hfsmp->hfs_catalog_cp);
91447636 1354 }
6d2010ae 1355 if (flags & SFL_BITMAP && hfsmp->hfs_allocation_cp) {
2d21ac55 1356 hfs_unlock(hfsmp->hfs_allocation_cp);
91447636 1357 }
6d2010ae 1358 if (flags & SFL_EXTENTS && hfsmp->hfs_extents_cp) {
91447636
A
1359 if (hfsmp->jnl == NULL) {
1360 BTGetLastSync((FCB*)VTOF(hfsmp->hfs_extents_vp), &lastfsync);
1361 numOfLockedBuffs = count_lock_queue();
1362 if ((numOfLockedBuffs > kMaxLockedMetaBuffers) ||
1363 ((numOfLockedBuffs > 1) && ((tv.tv_sec - lastfsync) >
1364 kMaxSecsForFsync))) {
1365 hfs_btsync(hfsmp->hfs_extents_vp, HFS_SYNCTRANS);
1366 }
1367 }
2d21ac55 1368 hfs_unlock(hfsmp->hfs_extents_cp);
9bccf70c 1369 }
9bccf70c 1370}
1c79356b 1371
91447636 1372
9bccf70c
A
1373/*
1374 * RequireFileLock
1375 *
1376 * Check to see if a vnode is locked in the current context
1377 * This is to be used for debugging purposes only!!
1378 */
1379#if HFS_DIAGNOSTIC
1380void RequireFileLock(FileReference vp, int shareable)
1c79356b 1381{
91447636 1382 int locked;
1c79356b 1383
91447636
A
1384 /* The extents btree and allocation bitmap are always exclusive. */
1385 if (VTOC(vp)->c_fileid == kHFSExtentsFileID ||
1386 VTOC(vp)->c_fileid == kHFSAllocationFileID) {
1387 shareable = 0;
1388 }
9bccf70c 1389
91447636 1390 locked = VTOC(vp)->c_lockowner == (void *)current_thread();
9bccf70c 1391
91447636 1392 if (!locked && !shareable) {
9bccf70c 1393 switch (VTOC(vp)->c_fileid) {
91447636 1394 case kHFSExtentsFileID:
b0d623f7 1395 panic("hfs: extents btree not locked! v: 0x%08X\n #\n", (u_int)vp);
91447636
A
1396 break;
1397 case kHFSCatalogFileID:
b0d623f7 1398 panic("hfs: catalog btree not locked! v: 0x%08X\n #\n", (u_int)vp);
91447636
A
1399 break;
1400 case kHFSAllocationFileID:
1401 /* The allocation file can hide behind the jornal lock. */
1402 if (VTOHFS(vp)->jnl == NULL)
b0d623f7 1403 panic("hfs: allocation file not locked! v: 0x%08X\n #\n", (u_int)vp);
91447636 1404 break;
2d21ac55 1405 case kHFSStartupFileID:
b0d623f7 1406 panic("hfs: startup file not locked! v: 0x%08X\n #\n", (u_int)vp);
91447636 1407 case kHFSAttributesFileID:
b0d623f7 1408 panic("hfs: attributes btree not locked! v: 0x%08X\n #\n", (u_int)vp);
91447636 1409 break;
9bccf70c
A
1410 }
1411 }
1c79356b 1412}
9bccf70c 1413#endif
1c79356b
A
1414
1415
9bccf70c
A
1416/*
1417 * There are three ways to qualify for ownership rights on an object:
1418 *
1419 * 1. (a) Your UID matches the cnode's UID.
55e303ae 1420 * (b) The object in question is owned by "unknown"
9bccf70c
A
1421 * 2. (a) Permissions on the filesystem are being ignored and
1422 * your UID matches the replacement UID.
1423 * (b) Permissions on the filesystem are being ignored and
55e303ae 1424 * the replacement UID is "unknown".
9bccf70c
A
1425 * 3. You are root.
1426 *
1427 */
1428int
91447636 1429hfs_owner_rights(struct hfsmount *hfsmp, uid_t cnode_uid, kauth_cred_t cred,
2d21ac55 1430 __unused struct proc *p, int invokesuperuserstatus)
9bccf70c 1431{
91447636 1432 if ((kauth_cred_getuid(cred) == cnode_uid) || /* [1a] */
55e303ae 1433 (cnode_uid == UNKNOWNUID) || /* [1b] */
91447636
A
1434 ((((unsigned int)vfs_flags(HFSTOVFS(hfsmp))) & MNT_UNKNOWNPERMISSIONS) && /* [2] */
1435 ((kauth_cred_getuid(cred) == hfsmp->hfs_uid) || /* [2a] */
55e303ae 1436 (hfsmp->hfs_uid == UNKNOWNUID))) || /* [2b] */
91447636 1437 (invokesuperuserstatus && (suser(cred, 0) == 0))) { /* [3] */
9bccf70c
A
1438 return (0);
1439 } else {
1440 return (EPERM);
1441 }
1c79356b
A
1442}
1443
1444
b0d623f7
A
1445u_int32_t BestBlockSizeFit(u_int32_t allocationBlockSize,
1446 u_int32_t blockSizeLimit,
1447 u_int32_t baseMultiple) {
9bccf70c
A
1448 /*
1449 Compute the optimal (largest) block size (no larger than allocationBlockSize) that is less than the
1450 specified limit but still an even multiple of the baseMultiple.
1451 */
1452 int baseBlockCount, blockCount;
b0d623f7 1453 u_int32_t trialBlockSize;
1c79356b 1454
9bccf70c
A
1455 if (allocationBlockSize % baseMultiple != 0) {
1456 /*
1457 Whoops: the allocation blocks aren't even multiples of the specified base:
1458 no amount of dividing them into even parts will be a multiple, either then!
1459 */
1460 return 512; /* Hope for the best */
1461 };
1c79356b 1462
9bccf70c
A
1463 /* Try the obvious winner first, to prevent 12K allocation blocks, for instance,
1464 from being handled as two 6K logical blocks instead of 3 4K logical blocks.
1465 Even though the former (the result of the loop below) is the larger allocation
1466 block size, the latter is more efficient: */
1467 if (allocationBlockSize % PAGE_SIZE == 0) return PAGE_SIZE;
1c79356b 1468
9bccf70c
A
1469 /* No clear winner exists: pick the largest even fraction <= MAXBSIZE: */
1470 baseBlockCount = allocationBlockSize / baseMultiple; /* Now guaranteed to be an even multiple */
1c79356b 1471
9bccf70c
A
1472 for (blockCount = baseBlockCount; blockCount > 0; --blockCount) {
1473 trialBlockSize = blockCount * baseMultiple;
1474 if (allocationBlockSize % trialBlockSize == 0) { /* An even multiple? */
1475 if ((trialBlockSize <= blockSizeLimit) &&
1476 (trialBlockSize % baseMultiple == 0)) {
1477 return trialBlockSize;
1478 };
1479 };
1480 };
1c79356b 1481
9bccf70c
A
1482 /* Note: we should never get here, since blockCount = 1 should always work,
1483 but this is nice and safe and makes the compiler happy, too ... */
1484 return 512;
1485}
1c79356b 1486
1c79356b 1487
b0d623f7 1488u_int32_t
2d21ac55 1489GetFileInfo(ExtendedVCB *vcb, __unused u_int32_t dirid, const char *name,
b4c24cb9
A
1490 struct cat_attr *fattr, struct cat_fork *forkinfo)
1491{
1492 struct hfsmount * hfsmp;
b4c24cb9 1493 struct cat_desc jdesc;
91447636 1494 int lockflags;
b4c24cb9
A
1495 int error;
1496
1497 if (vcb->vcbSigWord != kHFSPlusSigWord)
1498 return (0);
1499
1500 hfsmp = VCBTOHFS(vcb);
1501
1502 memset(&jdesc, 0, sizeof(struct cat_desc));
1503 jdesc.cd_parentcnid = kRootDirID;
2d21ac55 1504 jdesc.cd_nameptr = (const u_int8_t *)name;
b4c24cb9
A
1505 jdesc.cd_namelen = strlen(name);
1506
91447636 1507 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
39236c6e 1508 error = cat_lookup(hfsmp, &jdesc, 0, 0, NULL, fattr, forkinfo, NULL);
91447636 1509 hfs_systemfile_unlock(hfsmp, lockflags);
b4c24cb9
A
1510
1511 if (error == 0) {
1512 return (fattr->ca_fileid);
55e303ae 1513 } else if (hfsmp->hfs_flags & HFS_READ_ONLY) {
b4c24cb9
A
1514 return (0);
1515 }
91447636
A
1516
1517 return (0); /* XXX what callers expect on an error */
b4c24cb9
A
1518}
1519
1520
1521/*
2d21ac55
A
1522 * On HFS Plus Volumes, there can be orphaned files or directories
1523 * These are files or directories that were unlinked while busy.
1524 * If the volume was not cleanly unmounted then some of these may
b4c24cb9
A
1525 * have persisted and need to be removed.
1526 */
b4c24cb9
A
1527void
1528hfs_remove_orphans(struct hfsmount * hfsmp)
1529{
1530 struct BTreeIterator * iterator = NULL;
1531 struct FSBufferDescriptor btdata;
1532 struct HFSPlusCatalogFile filerec;
1533 struct HFSPlusCatalogKey * keyp;
55e303ae 1534 struct proc *p = current_proc();
b4c24cb9
A
1535 FCB *fcb;
1536 ExtendedVCB *vcb;
1537 char filename[32];
1538 char tempname[32];
1539 size_t namelen;
91447636 1540 cat_cookie_t cookie;
b4c24cb9 1541 int catlock = 0;
55e303ae
A
1542 int catreserve = 0;
1543 int started_tr = 0;
91447636 1544 int lockflags;
55e303ae 1545 int result;
b0d623f7
A
1546 int orphaned_files = 0;
1547 int orphaned_dirs = 0;
91447636
A
1548
1549 bzero(&cookie, sizeof(cookie));
1550
55e303ae 1551 if (hfsmp->hfs_flags & HFS_CLEANED_ORPHANS)
b4c24cb9
A
1552 return;
1553
1554 vcb = HFSTOVCB(hfsmp);
91447636 1555 fcb = VTOF(hfsmp->hfs_catalog_vp);
b4c24cb9
A
1556
1557 btdata.bufferAddress = &filerec;
1558 btdata.itemSize = sizeof(filerec);
1559 btdata.itemCount = 1;
1560
1561 MALLOC(iterator, struct BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
1562 bzero(iterator, sizeof(*iterator));
91447636
A
1563
1564 /* Build a key to "temp" */
b4c24cb9 1565 keyp = (HFSPlusCatalogKey*)&iterator->key;
2d21ac55 1566 keyp->parentID = hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid;
91447636
A
1567 keyp->nodeName.length = 4; /* "temp" */
1568 keyp->keyLength = kHFSPlusCatalogKeyMinimumLength + keyp->nodeName.length * 2;
1569 keyp->nodeName.unicode[0] = 't';
1570 keyp->nodeName.unicode[1] = 'e';
1571 keyp->nodeName.unicode[2] = 'm';
1572 keyp->nodeName.unicode[3] = 'p';
b4c24cb9 1573
b4c24cb9 1574 /*
2d21ac55 1575 * Position the iterator just before the first real temp file/dir.
b4c24cb9 1576 */
91447636
A
1577 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK);
1578 (void) BTSearchRecord(fcb, iterator, NULL, NULL, iterator);
1579 hfs_systemfile_unlock(hfsmp, lockflags);
b4c24cb9 1580
2d21ac55 1581 /* Visit all the temp files/dirs in the HFS+ private directory. */
b4c24cb9 1582 for (;;) {
91447636 1583 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK);
b4c24cb9 1584 result = BTIterateRecord(fcb, kBTreeNextRecord, iterator, &btdata, NULL);
91447636 1585 hfs_systemfile_unlock(hfsmp, lockflags);
b4c24cb9
A
1586 if (result)
1587 break;
2d21ac55 1588 if (keyp->parentID != hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid)
b4c24cb9 1589 break;
b4c24cb9
A
1590
1591 (void) utf8_encodestr(keyp->nodeName.unicode, keyp->nodeName.length * 2,
2d21ac55 1592 (u_int8_t *)filename, &namelen, sizeof(filename), 0, 0);
b4c24cb9 1593
2d21ac55
A
1594 (void) snprintf(tempname, sizeof(tempname), "%s%d",
1595 HFS_DELETE_PREFIX, filerec.fileID);
b4c24cb9
A
1596
1597 /*
2d21ac55
A
1598 * Delete all files (and directories) named "tempxxx",
1599 * where xxx is the file's cnid in decimal.
b4c24cb9 1600 *
b4c24cb9
A
1601 */
1602 if (bcmp(tempname, filename, namelen) == 0) {
91447636 1603 struct filefork dfork;
6d2010ae 1604 struct filefork rfork;
91447636 1605 struct cnode cnode;
6d2010ae 1606 int mode = 0;
91447636
A
1607
1608 bzero(&dfork, sizeof(dfork));
1609 bzero(&rfork, sizeof(rfork));
1610 bzero(&cnode, sizeof(cnode));
1611
2d21ac55
A
1612 /* Delete any attributes, ignore errors */
1613 (void) hfs_removeallattr(hfsmp, filerec.fileID);
1614
91447636
A
1615 if (hfs_start_transaction(hfsmp) != 0) {
1616 printf("hfs_remove_orphans: failed to start transaction\n");
1617 goto exit;
55e303ae 1618 }
91447636 1619 started_tr = 1;
55e303ae
A
1620
1621 /*
1622 * Reserve some space in the Catalog file.
1623 */
1624 if (cat_preflight(hfsmp, CAT_DELETE, &cookie, p) != 0) {
91447636 1625 printf("hfs_remove_orphans: cat_preflight failed\n");
55e303ae
A
1626 goto exit;
1627 }
1628 catreserve = 1;
b4c24cb9 1629
91447636 1630 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG | SFL_ATTRIBUTE | SFL_EXTENTS | SFL_BITMAP, HFS_EXCLUSIVE_LOCK);
55e303ae 1631 catlock = 1;
b4c24cb9
A
1632
1633 /* Build a fake cnode */
55e303ae
A
1634 cat_convertattr(hfsmp, (CatalogRecord *)&filerec, &cnode.c_attr,
1635 &dfork.ff_data, &rfork.ff_data);
2d21ac55
A
1636 cnode.c_desc.cd_parentcnid = hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid;
1637 cnode.c_desc.cd_nameptr = (const u_int8_t *)filename;
b4c24cb9 1638 cnode.c_desc.cd_namelen = namelen;
55e303ae
A
1639 cnode.c_desc.cd_cnid = cnode.c_attr.ca_fileid;
1640 cnode.c_blocks = dfork.ff_blocks + rfork.ff_blocks;
b4c24cb9
A
1641
1642 /* Position iterator at previous entry */
1643 if (BTIterateRecord(fcb, kBTreePrevRecord, iterator,
55e303ae 1644 NULL, NULL) != 0) {
b4c24cb9 1645 break;
55e303ae
A
1646 }
1647
b4c24cb9 1648 /* Truncate the file to zero (both forks) */
55e303ae
A
1649 if (dfork.ff_blocks > 0) {
1650 u_int64_t fsize;
1651
1652 dfork.ff_cp = &cnode;
1653 cnode.c_datafork = &dfork;
1654 cnode.c_rsrcfork = NULL;
1655 fsize = (u_int64_t)dfork.ff_blocks * (u_int64_t)HFSTOVCB(hfsmp)->blockSize;
1656 while (fsize > 0) {
2d21ac55 1657 if (fsize > HFS_BIGFILE_SIZE && overflow_extents(&dfork)) {
55e303ae
A
1658 fsize -= HFS_BIGFILE_SIZE;
1659 } else {
1660 fsize = 0;
1661 }
1662
6d2010ae
A
1663 if (TruncateFileC(vcb, (FCB*)&dfork, fsize, 1, 0,
1664 cnode.c_attr.ca_fileid, false) != 0) {
1665 printf("hfs: error truncating data fork!\n");
55e303ae
A
1666 break;
1667 }
1668
1669 //
1670 // if we're iteratively truncating this file down,
1671 // then end the transaction and start a new one so
1672 // that no one transaction gets too big.
1673 //
1674 if (fsize > 0 && started_tr) {
2d21ac55
A
1675 /* Drop system file locks before starting
1676 * another transaction to preserve lock order.
1677 */
1678 hfs_systemfile_unlock(hfsmp, lockflags);
1679 catlock = 0;
91447636 1680 hfs_end_transaction(hfsmp);
2d21ac55 1681
91447636 1682 if (hfs_start_transaction(hfsmp) != 0) {
55e303ae
A
1683 started_tr = 0;
1684 break;
1685 }
2d21ac55
A
1686 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG | SFL_ATTRIBUTE | SFL_EXTENTS | SFL_BITMAP, HFS_EXCLUSIVE_LOCK);
1687 catlock = 1;
55e303ae 1688 }
b4c24cb9
A
1689 }
1690 }
55e303ae
A
1691
1692 if (rfork.ff_blocks > 0) {
1693 rfork.ff_cp = &cnode;
b4c24cb9 1694 cnode.c_datafork = NULL;
55e303ae 1695 cnode.c_rsrcfork = &rfork;
6d2010ae
A
1696 if (TruncateFileC(vcb, (FCB*)&rfork, 0, 1, 1, cnode.c_attr.ca_fileid, false) != 0) {
1697 printf("hfs: error truncating rsrc fork!\n");
b4c24cb9
A
1698 break;
1699 }
1700 }
1701
2d21ac55 1702 /* Remove the file or folder record from the Catalog */
b4c24cb9 1703 if (cat_delete(hfsmp, &cnode.c_desc, &cnode.c_attr) != 0) {
2d21ac55
A
1704 printf("hfs_remove_orphans: error deleting cat rec for id %d!\n", cnode.c_desc.cd_cnid);
1705 hfs_systemfile_unlock(hfsmp, lockflags);
1706 catlock = 0;
91447636 1707 hfs_volupdate(hfsmp, VOL_UPDATE, 0);
b4c24cb9
A
1708 break;
1709 }
b0d623f7 1710
6d2010ae
A
1711 mode = cnode.c_attr.ca_mode & S_IFMT;
1712
1713 if (mode == S_IFDIR) {
b0d623f7
A
1714 orphaned_dirs++;
1715 }
1716 else {
1717 orphaned_files++;
1718 }
91447636 1719
b4c24cb9 1720 /* Update parent and volume counts */
2d21ac55 1721 hfsmp->hfs_private_attr[FILE_HARDLINKS].ca_entries--;
6d2010ae 1722 if (mode == S_IFDIR) {
2d21ac55
A
1723 DEC_FOLDERCOUNT(hfsmp, hfsmp->hfs_private_attr[FILE_HARDLINKS]);
1724 }
1725
1726 (void)cat_update(hfsmp, &hfsmp->hfs_private_desc[FILE_HARDLINKS],
1727 &hfsmp->hfs_private_attr[FILE_HARDLINKS], NULL, NULL);
55e303ae
A
1728
1729 /* Drop locks and end the transaction */
91447636 1730 hfs_systemfile_unlock(hfsmp, lockflags);
55e303ae
A
1731 cat_postflight(hfsmp, &cookie, p);
1732 catlock = catreserve = 0;
2d21ac55
A
1733
1734 /*
1735 Now that Catalog is unlocked, update the volume info, making
1736 sure to differentiate between files and directories
1737 */
6d2010ae 1738 if (mode == S_IFDIR) {
2d21ac55
A
1739 hfs_volupdate(hfsmp, VOL_RMDIR, 0);
1740 }
1741 else{
1742 hfs_volupdate(hfsmp, VOL_RMFILE, 0);
1743 }
1744
55e303ae 1745 if (started_tr) {
91447636 1746 hfs_end_transaction(hfsmp);
55e303ae
A
1747 started_tr = 0;
1748 }
55e303ae
A
1749
1750 } /* end if */
1751 } /* end for */
b0d623f7
A
1752 if (orphaned_files > 0 || orphaned_dirs > 0)
1753 printf("hfs: Removed %d orphaned / unlinked files and %d directories \n", orphaned_files, orphaned_dirs);
b4c24cb9 1754exit:
55e303ae 1755 if (catlock) {
91447636 1756 hfs_systemfile_unlock(hfsmp, lockflags);
55e303ae
A
1757 }
1758 if (catreserve) {
1759 cat_postflight(hfsmp, &cookie, p);
1760 }
b4c24cb9 1761 if (started_tr) {
91447636 1762 hfs_end_transaction(hfsmp);
55e303ae 1763 }
b4c24cb9
A
1764
1765 FREE(iterator, M_TEMP);
55e303ae 1766 hfsmp->hfs_flags |= HFS_CLEANED_ORPHANS;
b4c24cb9
A
1767}
1768
9bccf70c
A
1769
1770/*
1771 * This will return the correct logical block size for a given vnode.
1772 * For most files, it is the allocation block size, for meta data like
1773 * BTrees, this is kept as part of the BTree private nodeSize
1774 */
1775u_int32_t
1776GetLogicalBlockSize(struct vnode *vp)
1c79356b 1777{
9bccf70c 1778u_int32_t logBlockSize;
1c79356b 1779
9bccf70c 1780 DBG_ASSERT(vp != NULL);
1c79356b 1781
9bccf70c
A
1782 /* start with default */
1783 logBlockSize = VTOHFS(vp)->hfs_logBlockSize;
1c79356b 1784
91447636 1785 if (vnode_issystem(vp)) {
9bccf70c 1786 if (VTOF(vp)->fcbBTCBPtr != NULL) {
0b4e3aa0
A
1787 BTreeInfoRec bTreeInfo;
1788
1789 /*
1790 * We do not lock the BTrees, because if we are getting block..then the tree
1791 * should be locked in the first place.
1792 * We just want the nodeSize wich will NEVER change..so even if the world
1793 * is changing..the nodeSize should remain the same. Which argues why lock
1794 * it in the first place??
1795 */
1796
9bccf70c 1797 (void) BTGetInformation (VTOF(vp), kBTreeInfoVersion, &bTreeInfo);
0b4e3aa0
A
1798
1799 logBlockSize = bTreeInfo.nodeSize;
1800
9bccf70c 1801 } else if (VTOC(vp)->c_fileid == kHFSAllocationFileID) {
0b4e3aa0 1802 logBlockSize = VTOVCB(vp)->vcbVBMIOSize;
1c79356b 1803 }
0b4e3aa0
A
1804 }
1805
1c79356b
A
1806 DBG_ASSERT(logBlockSize > 0);
1807
1808 return logBlockSize;
1809}
1810
9bccf70c
A
1811u_int32_t
1812hfs_freeblks(struct hfsmount * hfsmp, int wantreserve)
1813{
9bccf70c 1814 u_int32_t freeblks;
2d21ac55
A
1815 u_int32_t rsrvblks;
1816 u_int32_t loanblks;
9bccf70c 1817
2d21ac55
A
1818 /*
1819 * We don't bother taking the mount lock
1820 * to look at these values since the values
b7266188 1821 * themselves are each updated atomically
2d21ac55
A
1822 * on aligned addresses.
1823 */
1824 freeblks = hfsmp->freeBlocks;
1825 rsrvblks = hfsmp->reserveBlocks;
1826 loanblks = hfsmp->loanedBlocks;
9bccf70c 1827 if (wantreserve) {
2d21ac55
A
1828 if (freeblks > rsrvblks)
1829 freeblks -= rsrvblks;
9bccf70c
A
1830 else
1831 freeblks = 0;
1832 }
2d21ac55
A
1833 if (freeblks > loanblks)
1834 freeblks -= loanblks;
55e303ae
A
1835 else
1836 freeblks = 0;
1837
6d2010ae 1838#if HFS_SPARSE_DEV
55e303ae
A
1839 /*
1840 * When the underlying device is sparse, check the
1841 * available space on the backing store volume.
1842 */
1843 if ((hfsmp->hfs_flags & HFS_HAS_SPARSE_DEVICE) && hfsmp->hfs_backingfs_rootvp) {
91447636 1844 struct vfsstatfs *vfsp; /* 272 bytes */
2d21ac55 1845 u_int64_t vfreeblks;
55e303ae
A
1846 u_int32_t loanedblks;
1847 struct mount * backingfs_mp;
2d21ac55 1848 struct timeval now;
55e303ae 1849
91447636 1850 backingfs_mp = vnode_mount(hfsmp->hfs_backingfs_rootvp);
55e303ae 1851
2d21ac55
A
1852 microtime(&now);
1853 if ((now.tv_sec - hfsmp->hfs_last_backingstatfs) >= 1) {
1854 vfs_update_vfsstat(backingfs_mp, vfs_context_kernel(), VFS_KERNEL_EVENT);
1855 hfsmp->hfs_last_backingstatfs = now.tv_sec;
1856 }
1857
1858 if ((vfsp = vfs_statfs(backingfs_mp))) {
39236c6e 1859 hfs_lock_mount (hfsmp);
2d21ac55 1860 vfreeblks = vfsp->f_bavail;
55e303ae 1861 /* Normalize block count if needed. */
2d21ac55
A
1862 if (vfsp->f_bsize != hfsmp->blockSize) {
1863 vfreeblks = ((u_int64_t)vfreeblks * (u_int64_t)(vfsp->f_bsize)) / hfsmp->blockSize;
55e303ae 1864 }
2d21ac55 1865 if (vfreeblks > (unsigned int)hfsmp->hfs_sparsebandblks)
55e303ae
A
1866 vfreeblks -= hfsmp->hfs_sparsebandblks;
1867 else
1868 vfreeblks = 0;
1869
1870 /* Take into account any delayed allocations. */
2d21ac55 1871 loanedblks = 2 * hfsmp->loanedBlocks;
55e303ae
A
1872 if (vfreeblks > loanedblks)
1873 vfreeblks -= loanedblks;
1874 else
1875 vfreeblks = 0;
1876
b0d623f7
A
1877 if (hfsmp->hfs_backingfs_maxblocks) {
1878 vfreeblks = MIN(vfreeblks, hfsmp->hfs_backingfs_maxblocks);
1879 }
55e303ae 1880 freeblks = MIN(vfreeblks, freeblks);
39236c6e 1881 hfs_unlock_mount (hfsmp);
55e303ae
A
1882 }
1883 }
1884#endif /* HFS_SPARSE_DEV */
39236c6e
A
1885 if (hfsmp->hfs_flags & HFS_CS) {
1886 uint64_t cs_free_bytes;
1887 uint64_t cs_free_blks;
1888 if (VNOP_IOCTL(hfsmp->hfs_devvp, _DKIOCCSGETFREEBYTES,
1889 (caddr_t)&cs_free_bytes, 0, vfs_context_kernel()) == 0) {
1890 cs_free_blks = cs_free_bytes / hfsmp->blockSize;
1891 if (cs_free_blks > loanblks)
1892 cs_free_blks -= loanblks;
1893 else
1894 cs_free_blks = 0;
1895 freeblks = MIN(cs_free_blks, freeblks);
1896 }
1897 }
9bccf70c 1898
9bccf70c
A
1899 return (freeblks);
1900}
1901
1c79356b
A
1902/*
1903 * Map HFS Common errors (negative) to BSD error codes (positive).
1904 * Positive errors (ie BSD errors) are passed through unchanged.
1905 */
1906short MacToVFSError(OSErr err)
1907{
9bccf70c
A
1908 if (err >= 0)
1909 return err;
1c79356b 1910
39236c6e
A
1911 /* BSD/VFS internal errnos */
1912 switch (err) {
1913 case ERESERVEDNAME: /* -8 */
1914 return err;
1915 }
1916
1c79356b 1917 switch (err) {
9bccf70c 1918 case dskFulErr: /* -34 */
55e303ae 1919 case btNoSpaceAvail: /* -32733 */
b4c24cb9 1920 return ENOSPC;
9bccf70c 1921 case fxOvFlErr: /* -32750 */
b4c24cb9 1922 return EOVERFLOW;
9bccf70c
A
1923
1924 case btBadNode: /* -32731 */
3a60a9f5 1925 return EIO;
9bccf70c
A
1926
1927 case memFullErr: /* -108 */
1928 return ENOMEM; /* +12 */
1929
1930 case cmExists: /* -32718 */
1931 case btExists: /* -32734 */
1932 return EEXIST; /* +17 */
1933
1934 case cmNotFound: /* -32719 */
1935 case btNotFound: /* -32735 */
1936 return ENOENT; /* 28 */
1937
1938 case cmNotEmpty: /* -32717 */
1939 return ENOTEMPTY; /* 66 */
1940
1941 case cmFThdDirErr: /* -32714 */
1942 return EISDIR; /* 21 */
1943
1944 case fxRangeErr: /* -32751 */
b4c24cb9 1945 return ERANGE;
9bccf70c
A
1946
1947 case bdNamErr: /* -37 */
1948 return ENAMETOOLONG; /* 63 */
1949
1950 case paramErr: /* -50 */
1951 case fileBoundsErr: /* -1309 */
1952 return EINVAL; /* +22 */
1953
1954 case fsBTBadNodeSize:
d52fe63f 1955 return ENXIO;
9bccf70c
A
1956
1957 default:
1958 return EIO; /* +5 */
1c79356b
A
1959 }
1960}
1961
1962
1963/*
91447636
A
1964 * Find the current thread's directory hint for a given index.
1965 *
1966 * Requires an exclusive lock on directory cnode.
2d21ac55
A
1967 *
1968 * Use detach if the cnode lock must be dropped while the hint is still active.
1c79356b 1969 */
9bccf70c 1970__private_extern__
91447636 1971directoryhint_t *
2d21ac55 1972hfs_getdirhint(struct cnode *dcp, int index, int detach)
1c79356b 1973{
91447636 1974 struct timeval tv;
b36670ce
A
1975 directoryhint_t *hint;
1976 boolean_t need_remove, need_init;
2d21ac55 1977 const u_int8_t * name;
91447636 1978
91447636
A
1979 microuptime(&tv);
1980
b36670ce
A
1981 /*
1982 * Look for an existing hint first. If not found, create a new one (when
1983 * the list is not full) or recycle the oldest hint. Since new hints are
1984 * always added to the head of the list, the last hint is always the
1985 * oldest.
1986 */
1987 TAILQ_FOREACH(hint, &dcp->c_hintlist, dh_link) {
1988 if (hint->dh_index == index)
1989 break;
9bccf70c 1990 }
b36670ce
A
1991 if (hint != NULL) { /* found an existing hint */
1992 need_init = false;
1993 need_remove = true;
1994 } else { /* cannot find an existing hint */
1995 need_init = true;
1996 if (dcp->c_dirhintcnt < HFS_MAXDIRHINTS) { /* we don't need recycling */
1997 /* Create a default directory hint */
1998 MALLOC_ZONE(hint, directoryhint_t *, sizeof(directoryhint_t), M_HFSDIRHINT, M_WAITOK);
1999 ++dcp->c_dirhintcnt;
2000 need_remove = false;
2001 } else { /* recycle the last (i.e., the oldest) hint */
2002 hint = TAILQ_LAST(&dcp->c_hintlist, hfs_hinthead);
2d21ac55
A
2003 if ((hint->dh_desc.cd_flags & CD_HASBUF) &&
2004 (name = hint->dh_desc.cd_nameptr)) {
b36670ce 2005 hint->dh_desc.cd_nameptr = NULL;
2d21ac55
A
2006 hint->dh_desc.cd_namelen = 0;
2007 hint->dh_desc.cd_flags &= ~CD_HASBUF;
2008 vfs_removename((const char *)name);
b36670ce
A
2009 }
2010 need_remove = true;
91447636 2011 }
9bccf70c 2012 }
91447636 2013
b36670ce
A
2014 if (need_remove)
2015 TAILQ_REMOVE(&dcp->c_hintlist, hint, dh_link);
2016
2d21ac55
A
2017 if (detach)
2018 --dcp->c_dirhintcnt;
2019 else
2020 TAILQ_INSERT_HEAD(&dcp->c_hintlist, hint, dh_link);
b36670ce
A
2021
2022 if (need_init) {
2023 hint->dh_index = index;
2024 hint->dh_desc.cd_flags = 0;
2025 hint->dh_desc.cd_encoding = 0;
2026 hint->dh_desc.cd_namelen = 0;
2027 hint->dh_desc.cd_nameptr = NULL;
2d21ac55 2028 hint->dh_desc.cd_parentcnid = dcp->c_fileid;
b36670ce
A
2029 hint->dh_desc.cd_hint = dcp->c_childhint;
2030 hint->dh_desc.cd_cnid = 0;
2031 }
91447636
A
2032 hint->dh_time = tv.tv_sec;
2033 return (hint);
1c79356b
A
2034}
2035
9bccf70c 2036/*
91447636
A
2037 * Release a single directory hint.
2038 *
2039 * Requires an exclusive lock on directory cnode.
9bccf70c
A
2040 */
2041__private_extern__
2042void
91447636 2043hfs_reldirhint(struct cnode *dcp, directoryhint_t * relhint)
9bccf70c 2044{
2d21ac55
A
2045 const u_int8_t * name;
2046 directoryhint_t *hint;
91447636 2047
2d21ac55
A
2048 /* Check if item is on list (could be detached) */
2049 TAILQ_FOREACH(hint, &dcp->c_hintlist, dh_link) {
2050 if (hint == relhint) {
2051 TAILQ_REMOVE(&dcp->c_hintlist, relhint, dh_link);
2052 --dcp->c_dirhintcnt;
2053 break;
2054 }
2055 }
b36670ce 2056 name = relhint->dh_desc.cd_nameptr;
2d21ac55 2057 if ((relhint->dh_desc.cd_flags & CD_HASBUF) && (name != NULL)) {
b36670ce 2058 relhint->dh_desc.cd_nameptr = NULL;
2d21ac55
A
2059 relhint->dh_desc.cd_namelen = 0;
2060 relhint->dh_desc.cd_flags &= ~CD_HASBUF;
2061 vfs_removename((const char *)name);
1c79356b 2062 }
b36670ce 2063 FREE_ZONE(relhint, sizeof(directoryhint_t), M_HFSDIRHINT);
1c79356b
A
2064}
2065
9bccf70c 2066/*
91447636
A
2067 * Release directory hints for given directory
2068 *
2069 * Requires an exclusive lock on directory cnode.
9bccf70c
A
2070 */
2071__private_extern__
2072void
91447636 2073hfs_reldirhints(struct cnode *dcp, int stale_hints_only)
9bccf70c 2074{
91447636 2075 struct timeval tv;
b36670ce 2076 directoryhint_t *hint, *prev;
2d21ac55 2077 const u_int8_t * name;
91447636
A
2078
2079 if (stale_hints_only)
2080 microuptime(&tv);
b36670ce
A
2081
2082 /* searching from the oldest to the newest, so we can stop early when releasing stale hints only */
2083 for (hint = TAILQ_LAST(&dcp->c_hintlist, hfs_hinthead); hint != NULL; hint = prev) {
2084 if (stale_hints_only && (tv.tv_sec - hint->dh_time) < HFS_DIRHINT_TTL)
2085 break; /* stop here if this entry is too new */
91447636 2086 name = hint->dh_desc.cd_nameptr;
2d21ac55 2087 if ((hint->dh_desc.cd_flags & CD_HASBUF) && (name != NULL)) {
91447636 2088 hint->dh_desc.cd_nameptr = NULL;
2d21ac55
A
2089 hint->dh_desc.cd_namelen = 0;
2090 hint->dh_desc.cd_flags &= ~CD_HASBUF;
2091 vfs_removename((const char *)name);
1c79356b 2092 }
b36670ce
A
2093 prev = TAILQ_PREV(hint, hfs_hinthead, dh_link); /* must save this pointer before calling FREE_ZONE on this node */
2094 TAILQ_REMOVE(&dcp->c_hintlist, hint, dh_link);
91447636
A
2095 FREE_ZONE(hint, sizeof(directoryhint_t), M_HFSDIRHINT);
2096 --dcp->c_dirhintcnt;
1c79356b 2097 }
9bccf70c 2098}
1c79356b 2099
2d21ac55
A
2100/*
2101 * Insert a detached directory hint back into the list of dirhints.
2102 *
2103 * Requires an exclusive lock on directory cnode.
2104 */
2105__private_extern__
2106void
2107hfs_insertdirhint(struct cnode *dcp, directoryhint_t * hint)
2108{
2109 directoryhint_t *test;
2110
2111 TAILQ_FOREACH(test, &dcp->c_hintlist, dh_link) {
2112 if (test == hint)
2113 panic("hfs_insertdirhint: hint %p already on list!", hint);
2114 }
2115
2116 TAILQ_INSERT_HEAD(&dcp->c_hintlist, hint, dh_link);
2117 ++dcp->c_dirhintcnt;
2118}
1c79356b 2119
d7e50217
A
2120/*
2121 * Perform a case-insensitive compare of two UTF-8 filenames.
2122 *
2123 * Returns 0 if the strings match.
2124 */
2125__private_extern__
2126int
2d21ac55 2127hfs_namecmp(const u_int8_t *str1, size_t len1, const u_int8_t *str2, size_t len2)
d7e50217
A
2128{
2129 u_int16_t *ustr1, *ustr2;
2130 size_t ulen1, ulen2;
2131 size_t maxbytes;
2132 int cmp = -1;
2133
2134 if (len1 != len2)
2135 return (cmp);
2136
2137 maxbytes = kHFSPlusMaxFileNameChars << 1;
2138 MALLOC(ustr1, u_int16_t *, maxbytes << 1, M_TEMP, M_WAITOK);
2139 ustr2 = ustr1 + (maxbytes >> 1);
2140
2141 if (utf8_decodestr(str1, len1, ustr1, &ulen1, maxbytes, ':', 0) != 0)
2142 goto out;
2143 if (utf8_decodestr(str2, len2, ustr2, &ulen2, maxbytes, ':', 0) != 0)
2144 goto out;
2145
2146 cmp = FastUnicodeCompare(ustr1, ulen1>>1, ustr2, ulen2>>1);
2147out:
2148 FREE(ustr1, M_TEMP);
2149 return (cmp);
2150}
2151
2152
b0d623f7
A
2153typedef struct jopen_cb_info {
2154 off_t jsize;
2155 char *desired_uuid;
2156 struct vnode *jvp;
2157 size_t blksize;
2158 int need_clean;
2159 int need_init;
2160} jopen_cb_info;
2161
2162static int
2163journal_open_cb(const char *bsd_dev_name, const char *uuid_str, void *arg)
2164{
2165 struct nameidata nd;
2166 jopen_cb_info *ji = (jopen_cb_info *)arg;
2167 char bsd_name[256];
2168 int error;
2169
2170 strlcpy(&bsd_name[0], "/dev/", sizeof(bsd_name));
2171 strlcpy(&bsd_name[5], bsd_dev_name, sizeof(bsd_name)-5);
2172
2173 if (ji->desired_uuid && ji->desired_uuid[0] && strcmp(uuid_str, ji->desired_uuid) != 0) {
2174 return 1; // keep iterating
2175 }
2176
2177 // if we're here, either the desired uuid matched or there was no
2178 // desired uuid so let's try to open the device for writing and
2179 // see if it works. if it does, we'll use it.
2180
6d2010ae 2181 NDINIT(&nd, LOOKUP, OP_LOOKUP, LOCKLEAF, UIO_SYSSPACE32, CAST_USER_ADDR_T(bsd_name), vfs_context_kernel());
b0d623f7
A
2182 if ((error = namei(&nd))) {
2183 printf("hfs: journal open cb: error %d looking up device %s (dev uuid %s)\n", error, bsd_name, uuid_str);
2184 return 1; // keep iterating
2185 }
2186
2187 ji->jvp = nd.ni_vp;
2188 nameidone(&nd);
2189
2190 if (ji->jvp == NULL) {
2191 printf("hfs: journal open cb: did not find %s (error %d)\n", bsd_name, error);
2192 } else {
2193 error = VNOP_OPEN(ji->jvp, FREAD|FWRITE, vfs_context_kernel());
2194 if (error == 0) {
2195 // if the journal is dirty and we didn't specify a desired
2196 // journal device uuid, then do not use the journal. but
2197 // if the journal is just invalid (e.g. it hasn't been
2198 // initialized) then just set the need_init flag.
2199 if (ji->need_clean && ji->desired_uuid && ji->desired_uuid[0] == '\0') {
2200 error = journal_is_clean(ji->jvp, 0, ji->jsize, (void *)1, ji->blksize);
2201 if (error == EBUSY) {
2202 VNOP_CLOSE(ji->jvp, FREAD|FWRITE, vfs_context_kernel());
2203 vnode_put(ji->jvp);
2204 ji->jvp = NULL;
2205 return 1; // keep iterating
2206 } else if (error == EINVAL) {
2207 ji->need_init = 1;
2208 }
2209 }
2210
2211 if (ji->desired_uuid && ji->desired_uuid[0] == '\0') {
2212 strlcpy(ji->desired_uuid, uuid_str, 128);
2213 }
2214 vnode_setmountedon(ji->jvp);
b0d623f7
A
2215 return 0; // stop iterating
2216 } else {
2217 vnode_put(ji->jvp);
2218 ji->jvp = NULL;
2219 }
2220 }
2221
2222 return 1; // keep iterating
2223}
2224
b0d623f7 2225extern void IOBSDIterateMediaWithContent(const char *uuid_cstring, int (*func)(const char *bsd_dev_name, const char *uuid_str, void *arg), void *arg);
b0d623f7
A
2226kern_return_t IOBSDGetPlatformSerialNumber(char *serial_number_str, u_int32_t len);
2227
2228
2229static vnode_t
2230open_journal_dev(const char *vol_device,
2231 int need_clean,
2232 char *uuid_str,
2233 char *machine_serial_num,
2234 off_t jsize,
2235 size_t blksize,
2236 int *need_init)
2237{
2238 int retry_counter=0;
2239 jopen_cb_info ji;
2240
2241 ji.jsize = jsize;
2242 ji.desired_uuid = uuid_str;
2243 ji.jvp = NULL;
2244 ji.blksize = blksize;
2245 ji.need_clean = need_clean;
2246 ji.need_init = 0;
2247
2248// if (uuid_str[0] == '\0') {
2249// printf("hfs: open journal dev: %s: locating any available non-dirty external journal partition\n", vol_device);
2250// } else {
2251// printf("hfs: open journal dev: %s: trying to find the external journal partition w/uuid %s\n", vol_device, uuid_str);
2252// }
2253 while (ji.jvp == NULL && retry_counter++ < 4) {
2254 if (retry_counter > 1) {
2255 if (uuid_str[0]) {
2256 printf("hfs: open_journal_dev: uuid %s not found. waiting 10sec.\n", uuid_str);
2257 } else {
2258 printf("hfs: open_journal_dev: no available external journal partition found. waiting 10sec.\n");
2259 }
2260 delay_for_interval(10* 1000000, NSEC_PER_USEC); // wait for ten seconds and then try again
2261 }
2262
2263 IOBSDIterateMediaWithContent(EXTJNL_CONTENT_TYPE_UUID, journal_open_cb, &ji);
2264 }
2265
2266 if (ji.jvp == NULL) {
2267 printf("hfs: volume: %s: did not find jnl device uuid: %s from machine serial number: %s\n",
2268 vol_device, uuid_str, machine_serial_num);
2269 }
2270
2271 *need_init = ji.need_init;
2272
2273 return ji.jvp;
2274}
2275
2276
b4c24cb9
A
2277int
2278hfs_early_journal_init(struct hfsmount *hfsmp, HFSPlusVolumeHeader *vhp,
91447636
A
2279 void *_args, off_t embeddedOffset, daddr64_t mdb_offset,
2280 HFSMasterDirectoryBlock *mdbp, kauth_cred_t cred)
b4c24cb9
A
2281{
2282 JournalInfoBlock *jibp;
2283 struct buf *jinfo_bp, *bp;
2284 int sectors_per_fsblock, arg_flags=0, arg_tbufsz=0;
b0d623f7 2285 int retval, write_jibp = 0;
593a1d5f 2286 uint32_t blksize = hfsmp->hfs_logical_block_size;
b4c24cb9
A
2287 struct vnode *devvp;
2288 struct hfs_mount_args *args = _args;
2d21ac55
A
2289 u_int32_t jib_flags;
2290 u_int64_t jib_offset;
2291 u_int64_t jib_size;
b0d623f7 2292 const char *dev_name;
2d21ac55 2293
b4c24cb9 2294 devvp = hfsmp->hfs_devvp;
39236c6e 2295 dev_name = vnode_getname_printable(devvp);
b4c24cb9
A
2296
2297 if (args != NULL && (args->flags & HFSFSMNT_EXTENDED_ARGS)) {
2298 arg_flags = args->journal_flags;
2299 arg_tbufsz = args->journal_tbuffer_size;
2300 }
2301
2302 sectors_per_fsblock = SWAP_BE32(vhp->blockSize) / blksize;
2303
b0d623f7 2304 jinfo_bp = NULL;
91447636
A
2305 retval = (int)buf_meta_bread(devvp,
2306 (daddr64_t)((embeddedOffset/blksize) +
b0d623f7
A
2307 ((u_int64_t)SWAP_BE32(vhp->journalInfoBlock)*sectors_per_fsblock)),
2308 hfsmp->hfs_physical_block_size, cred, &jinfo_bp);
2309 if (retval) {
2310 if (jinfo_bp) {
2311 buf_brelse(jinfo_bp);
2312 }
39236c6e 2313 goto cleanup_dev_name;
b0d623f7
A
2314 }
2315
91447636 2316 jibp = (JournalInfoBlock *)buf_dataptr(jinfo_bp);
2d21ac55 2317 jib_flags = SWAP_BE32(jibp->flags);
2d21ac55 2318 jib_size = SWAP_BE64(jibp->size);
b4c24cb9 2319
2d21ac55 2320 if (jib_flags & kJIJournalInFSMask) {
b4c24cb9 2321 hfsmp->jvp = hfsmp->hfs_devvp;
b0d623f7 2322 jib_offset = SWAP_BE64(jibp->offset);
b4c24cb9 2323 } else {
b0d623f7
A
2324 int need_init=0;
2325
2326 // if the volume was unmounted cleanly then we'll pick any
2327 // available external journal partition
2328 //
2329 if (SWAP_BE32(vhp->attributes) & kHFSVolumeUnmountedMask) {
2330 *((char *)&jibp->ext_jnl_uuid[0]) = '\0';
2331 }
2332
2333 hfsmp->jvp = open_journal_dev(dev_name,
2334 !(jib_flags & kJIJournalNeedInitMask),
2335 (char *)&jibp->ext_jnl_uuid[0],
2336 (char *)&jibp->machine_serial_num[0],
2337 jib_size,
2338 hfsmp->hfs_logical_block_size,
2339 &need_init);
2340 if (hfsmp->jvp == NULL) {
39236c6e
A
2341 buf_brelse(jinfo_bp);
2342 retval = EROFS;
2343 goto cleanup_dev_name;
b0d623f7
A
2344 } else {
2345 if (IOBSDGetPlatformSerialNumber(&jibp->machine_serial_num[0], sizeof(jibp->machine_serial_num)) != KERN_SUCCESS) {
2346 strlcpy(&jibp->machine_serial_num[0], "unknown-machine-uuid", sizeof(jibp->machine_serial_num));
2347 }
2348 }
2349
2350 jib_offset = 0;
2351 write_jibp = 1;
2352 if (need_init) {
2353 jib_flags |= kJIJournalNeedInitMask;
2354 }
b4c24cb9
A
2355 }
2356
2357 // save this off for the hack-y check in hfs_remove()
2d21ac55
A
2358 hfsmp->jnl_start = jib_offset / SWAP_BE32(vhp->blockSize);
2359 hfsmp->jnl_size = jib_size;
b4c24cb9 2360
743b1565
A
2361 if ((hfsmp->hfs_flags & HFS_READ_ONLY) && (vfs_flags(hfsmp->hfs_mp) & MNT_ROOTFS) == 0) {
2362 // if the file system is read-only, check if the journal is empty.
2363 // if it is, then we can allow the mount. otherwise we have to
2364 // return failure.
2365 retval = journal_is_clean(hfsmp->jvp,
b0d623f7 2366 jib_offset + embeddedOffset,
2d21ac55 2367 jib_size,
743b1565 2368 devvp,
b0d623f7 2369 hfsmp->hfs_logical_block_size);
743b1565
A
2370
2371 hfsmp->jnl = NULL;
2372
2373 buf_brelse(jinfo_bp);
2374
2375 if (retval) {
39236c6e
A
2376 const char *name = vnode_getname_printable(devvp);
2377 printf("hfs: early journal init: volume on %s is read-only and journal is dirty. Can not mount volume.\n",
2378 name);
2379 vnode_putname_printable(name);
743b1565
A
2380 }
2381
39236c6e 2382 goto cleanup_dev_name;
743b1565
A
2383 }
2384
2d21ac55 2385 if (jib_flags & kJIJournalNeedInitMask) {
b4c24cb9 2386 printf("hfs: Initializing the journal (joffset 0x%llx sz 0x%llx)...\n",
2d21ac55 2387 jib_offset + embeddedOffset, jib_size);
b4c24cb9 2388 hfsmp->jnl = journal_create(hfsmp->jvp,
2d21ac55
A
2389 jib_offset + embeddedOffset,
2390 jib_size,
b4c24cb9
A
2391 devvp,
2392 blksize,
2393 arg_flags,
2394 arg_tbufsz,
39236c6e
A
2395 hfs_sync_metadata, hfsmp->hfs_mp,
2396 hfsmp->hfs_mp);
6d2010ae
A
2397 if (hfsmp->jnl)
2398 journal_trim_set_callback(hfsmp->jnl, hfs_trim_callback, hfsmp);
b4c24cb9
A
2399
2400 // no need to start a transaction here... if this were to fail
2401 // we'd just re-init it on the next mount.
2d21ac55
A
2402 jib_flags &= ~kJIJournalNeedInitMask;
2403 jibp->flags = SWAP_BE32(jib_flags);
91447636 2404 buf_bwrite(jinfo_bp);
b4c24cb9
A
2405 jinfo_bp = NULL;
2406 jibp = NULL;
2407 } else {
2408 //printf("hfs: Opening the journal (joffset 0x%llx sz 0x%llx vhp_blksize %d)...\n",
2d21ac55
A
2409 // jib_offset + embeddedOffset,
2410 // jib_size, SWAP_BE32(vhp->blockSize));
b4c24cb9
A
2411
2412 hfsmp->jnl = journal_open(hfsmp->jvp,
2d21ac55
A
2413 jib_offset + embeddedOffset,
2414 jib_size,
b4c24cb9
A
2415 devvp,
2416 blksize,
2417 arg_flags,
2418 arg_tbufsz,
39236c6e
A
2419 hfs_sync_metadata, hfsmp->hfs_mp,
2420 hfsmp->hfs_mp);
6d2010ae
A
2421 if (hfsmp->jnl)
2422 journal_trim_set_callback(hfsmp->jnl, hfs_trim_callback, hfsmp);
b4c24cb9 2423
b0d623f7
A
2424 if (write_jibp) {
2425 buf_bwrite(jinfo_bp);
2426 } else {
2427 buf_brelse(jinfo_bp);
2428 }
b4c24cb9
A
2429 jinfo_bp = NULL;
2430 jibp = NULL;
2431
2432 if (hfsmp->jnl && mdbp) {
2433 // reload the mdb because it could have changed
2434 // if the journal had to be replayed.
55e303ae 2435 if (mdb_offset == 0) {
91447636 2436 mdb_offset = (daddr64_t)((embeddedOffset / blksize) + HFS_PRI_SECTOR(blksize));
55e303ae 2437 }
b0d623f7 2438 bp = NULL;
593a1d5f
A
2439 retval = (int)buf_meta_bread(devvp,
2440 HFS_PHYSBLK_ROUNDDOWN(mdb_offset, hfsmp->hfs_log_per_phys),
2441 hfsmp->hfs_physical_block_size, cred, &bp);
b4c24cb9 2442 if (retval) {
b0d623f7
A
2443 if (bp) {
2444 buf_brelse(bp);
2445 }
b4c24cb9
A
2446 printf("hfs: failed to reload the mdb after opening the journal (retval %d)!\n",
2447 retval);
39236c6e 2448 goto cleanup_dev_name;
b4c24cb9 2449 }
593a1d5f 2450 bcopy((char *)buf_dataptr(bp) + HFS_PRI_OFFSET(hfsmp->hfs_physical_block_size), mdbp, 512);
91447636 2451 buf_brelse(bp);
b4c24cb9
A
2452 bp = NULL;
2453 }
2454 }
2455
b4c24cb9
A
2456 // if we expected the journal to be there and we couldn't
2457 // create it or open it then we have to bail out.
2458 if (hfsmp->jnl == NULL) {
55e303ae 2459 printf("hfs: early jnl init: failed to open/create the journal (retval %d).\n", retval);
39236c6e
A
2460 retval = EINVAL;
2461 goto cleanup_dev_name;
b4c24cb9 2462 }
1c79356b 2463
39236c6e
A
2464 retval = 0;
2465
2466cleanup_dev_name:
2467 vnode_putname_printable(dev_name);
2468 return retval;
b4c24cb9
A
2469}
2470
2471
2472//
2473// This function will go and re-locate the .journal_info_block and
2474// the .journal files in case they moved (which can happen if you
2475// run Norton SpeedDisk). If we fail to find either file we just
2476// disable journaling for this volume and return. We turn off the
2477// journaling bit in the vcb and assume it will get written to disk
2478// later (if it doesn't on the next mount we'd do the same thing
2479// again which is harmless). If we disable journaling we don't
2480// return an error so that the volume is still mountable.
2481//
2482// If the info we find for the .journal_info_block and .journal files
2483// isn't what we had stored, we re-set our cached info and proceed
2484// with opening the journal normally.
2485//
2486static int
2487hfs_late_journal_init(struct hfsmount *hfsmp, HFSPlusVolumeHeader *vhp, void *_args)
2488{
2489 JournalInfoBlock *jibp;
2d21ac55 2490 struct buf *jinfo_bp;
b4c24cb9 2491 int sectors_per_fsblock, arg_flags=0, arg_tbufsz=0;
2d21ac55 2492 int retval, write_jibp = 0, recreate_journal = 0;
b4c24cb9
A
2493 struct vnode *devvp;
2494 struct cat_attr jib_attr, jattr;
2495 struct cat_fork jib_fork, jfork;
2496 ExtendedVCB *vcb;
b0d623f7 2497 u_int32_t fid;
b4c24cb9 2498 struct hfs_mount_args *args = _args;
2d21ac55
A
2499 u_int32_t jib_flags;
2500 u_int64_t jib_offset;
2501 u_int64_t jib_size;
b4c24cb9
A
2502
2503 devvp = hfsmp->hfs_devvp;
2504 vcb = HFSTOVCB(hfsmp);
2505
2506 if (args != NULL && (args->flags & HFSFSMNT_EXTENDED_ARGS)) {
2507 if (args->journal_disable) {
2508 return 0;
2509 }
2510
2511 arg_flags = args->journal_flags;
2512 arg_tbufsz = args->journal_tbuffer_size;
2513 }
2514
2515 fid = GetFileInfo(vcb, kRootDirID, ".journal_info_block", &jib_attr, &jib_fork);
2516 if (fid == 0 || jib_fork.cf_extents[0].startBlock == 0 || jib_fork.cf_size == 0) {
2517 printf("hfs: can't find the .journal_info_block! disabling journaling (start: %d).\n",
2518 jib_fork.cf_extents[0].startBlock);
2519 vcb->vcbAtrb &= ~kHFSVolumeJournaledMask;
2520 return 0;
2521 }
2522 hfsmp->hfs_jnlinfoblkid = fid;
2523
2524 // make sure the journal_info_block begins where we think it should.
2525 if (SWAP_BE32(vhp->journalInfoBlock) != jib_fork.cf_extents[0].startBlock) {
2526 printf("hfs: The journal_info_block moved (was: %d; is: %d). Fixing up\n",
2527 SWAP_BE32(vhp->journalInfoBlock), jib_fork.cf_extents[0].startBlock);
2528
2529 vcb->vcbJinfoBlock = jib_fork.cf_extents[0].startBlock;
2530 vhp->journalInfoBlock = SWAP_BE32(jib_fork.cf_extents[0].startBlock);
2d21ac55 2531 recreate_journal = 1;
b4c24cb9
A
2532 }
2533
2534
593a1d5f 2535 sectors_per_fsblock = SWAP_BE32(vhp->blockSize) / hfsmp->hfs_logical_block_size;
b0d623f7 2536 jinfo_bp = NULL;
91447636 2537 retval = (int)buf_meta_bread(devvp,
b0d623f7
A
2538 (vcb->hfsPlusIOPosOffset / hfsmp->hfs_logical_block_size +
2539 ((u_int64_t)SWAP_BE32(vhp->journalInfoBlock)*sectors_per_fsblock)),
2540 hfsmp->hfs_physical_block_size, NOCRED, &jinfo_bp);
b4c24cb9 2541 if (retval) {
b0d623f7
A
2542 if (jinfo_bp) {
2543 buf_brelse(jinfo_bp);
2544 }
b4c24cb9
A
2545 printf("hfs: can't read journal info block. disabling journaling.\n");
2546 vcb->vcbAtrb &= ~kHFSVolumeJournaledMask;
2547 return 0;
2548 }
2549
91447636 2550 jibp = (JournalInfoBlock *)buf_dataptr(jinfo_bp);
2d21ac55
A
2551 jib_flags = SWAP_BE32(jibp->flags);
2552 jib_offset = SWAP_BE64(jibp->offset);
2553 jib_size = SWAP_BE64(jibp->size);
b4c24cb9
A
2554
2555 fid = GetFileInfo(vcb, kRootDirID, ".journal", &jattr, &jfork);
2556 if (fid == 0 || jfork.cf_extents[0].startBlock == 0 || jfork.cf_size == 0) {
2557 printf("hfs: can't find the journal file! disabling journaling (start: %d)\n",
2558 jfork.cf_extents[0].startBlock);
91447636 2559 buf_brelse(jinfo_bp);
b4c24cb9
A
2560 vcb->vcbAtrb &= ~kHFSVolumeJournaledMask;
2561 return 0;
2562 }
2563 hfsmp->hfs_jnlfileid = fid;
2564
2565 // make sure the journal file begins where we think it should.
b0d623f7 2566 if ((jib_flags & kJIJournalInFSMask) && (jib_offset / (u_int64_t)vcb->blockSize) != jfork.cf_extents[0].startBlock) {
b4c24cb9 2567 printf("hfs: The journal file moved (was: %lld; is: %d). Fixing up\n",
2d21ac55 2568 (jib_offset / (u_int64_t)vcb->blockSize), jfork.cf_extents[0].startBlock);
b4c24cb9 2569
2d21ac55 2570 jib_offset = (u_int64_t)jfork.cf_extents[0].startBlock * (u_int64_t)vcb->blockSize;
b4c24cb9 2571 write_jibp = 1;
2d21ac55 2572 recreate_journal = 1;
b4c24cb9
A
2573 }
2574
2575 // check the size of the journal file.
2d21ac55 2576 if (jib_size != (u_int64_t)jfork.cf_extents[0].blockCount*vcb->blockSize) {
b4c24cb9 2577 printf("hfs: The journal file changed size! (was %lld; is %lld). Fixing up.\n",
2d21ac55 2578 jib_size, (u_int64_t)jfork.cf_extents[0].blockCount*vcb->blockSize);
b4c24cb9 2579
2d21ac55 2580 jib_size = (u_int64_t)jfork.cf_extents[0].blockCount * vcb->blockSize;
b4c24cb9 2581 write_jibp = 1;
2d21ac55 2582 recreate_journal = 1;
b4c24cb9
A
2583 }
2584
2d21ac55 2585 if (jib_flags & kJIJournalInFSMask) {
b4c24cb9 2586 hfsmp->jvp = hfsmp->hfs_devvp;
b0d623f7 2587 jib_offset += (off_t)vcb->hfsPlusIOPosOffset;
b4c24cb9 2588 } else {
b0d623f7
A
2589 const char *dev_name;
2590 int need_init = 0;
39236c6e
A
2591
2592 dev_name = vnode_getname_printable(devvp);
b0d623f7
A
2593
2594 // since the journal is empty, just use any available external journal
2595 *((char *)&jibp->ext_jnl_uuid[0]) = '\0';
2596
2597 // this fills in the uuid of the device we actually get
2598 hfsmp->jvp = open_journal_dev(dev_name,
2599 !(jib_flags & kJIJournalNeedInitMask),
2600 (char *)&jibp->ext_jnl_uuid[0],
2601 (char *)&jibp->machine_serial_num[0],
2602 jib_size,
2603 hfsmp->hfs_logical_block_size,
2604 &need_init);
2605 if (hfsmp->jvp == NULL) {
39236c6e
A
2606 buf_brelse(jinfo_bp);
2607 vnode_putname_printable(dev_name);
2608 return EROFS;
b0d623f7
A
2609 } else {
2610 if (IOBSDGetPlatformSerialNumber(&jibp->machine_serial_num[0], sizeof(jibp->machine_serial_num)) != KERN_SUCCESS) {
2611 strlcpy(&jibp->machine_serial_num[0], "unknown-machine-serial-num", sizeof(jibp->machine_serial_num));
2612 }
39236c6e 2613 }
b0d623f7
A
2614 jib_offset = 0;
2615 recreate_journal = 1;
2616 write_jibp = 1;
2617 if (need_init) {
2618 jib_flags |= kJIJournalNeedInitMask;
2619 }
39236c6e 2620 vnode_putname_printable(dev_name);
b4c24cb9
A
2621 }
2622
2623 // save this off for the hack-y check in hfs_remove()
2d21ac55
A
2624 hfsmp->jnl_start = jib_offset / SWAP_BE32(vhp->blockSize);
2625 hfsmp->jnl_size = jib_size;
b4c24cb9 2626
743b1565
A
2627 if ((hfsmp->hfs_flags & HFS_READ_ONLY) && (vfs_flags(hfsmp->hfs_mp) & MNT_ROOTFS) == 0) {
2628 // if the file system is read-only, check if the journal is empty.
2629 // if it is, then we can allow the mount. otherwise we have to
2630 // return failure.
2631 retval = journal_is_clean(hfsmp->jvp,
b0d623f7 2632 jib_offset,
2d21ac55 2633 jib_size,
743b1565 2634 devvp,
593a1d5f 2635 hfsmp->hfs_logical_block_size);
743b1565
A
2636
2637 hfsmp->jnl = NULL;
2638
2639 buf_brelse(jinfo_bp);
2640
2641 if (retval) {
39236c6e
A
2642 const char *name = vnode_getname_printable(devvp);
2643 printf("hfs: late journal init: volume on %s is read-only and journal is dirty. Can not mount volume.\n",
2644 name);
2645 vnode_putname_printable(name);
743b1565
A
2646 }
2647
2648 return retval;
2649 }
2650
2d21ac55 2651 if ((jib_flags & kJIJournalNeedInitMask) || recreate_journal) {
b4c24cb9 2652 printf("hfs: Initializing the journal (joffset 0x%llx sz 0x%llx)...\n",
b0d623f7 2653 jib_offset, jib_size);
b4c24cb9 2654 hfsmp->jnl = journal_create(hfsmp->jvp,
b0d623f7 2655 jib_offset,
2d21ac55 2656 jib_size,
b4c24cb9 2657 devvp,
593a1d5f 2658 hfsmp->hfs_logical_block_size,
b4c24cb9
A
2659 arg_flags,
2660 arg_tbufsz,
39236c6e
A
2661 hfs_sync_metadata, hfsmp->hfs_mp,
2662 hfsmp->hfs_mp);
6d2010ae
A
2663 if (hfsmp->jnl)
2664 journal_trim_set_callback(hfsmp->jnl, hfs_trim_callback, hfsmp);
b4c24cb9
A
2665
2666 // no need to start a transaction here... if this were to fail
2667 // we'd just re-init it on the next mount.
2d21ac55 2668 jib_flags &= ~kJIJournalNeedInitMask;
b4c24cb9
A
2669 write_jibp = 1;
2670
2671 } else {
2672 //
2673 // if we weren't the last person to mount this volume
2674 // then we need to throw away the journal because it
2675 // is likely that someone else mucked with the disk.
2676 // if the journal is empty this is no big deal. if the
2677 // disk is dirty this prevents us from replaying the
2678 // journal over top of changes that someone else made.
2679 //
2680 arg_flags |= JOURNAL_RESET;
2681
2682 //printf("hfs: Opening the journal (joffset 0x%llx sz 0x%llx vhp_blksize %d)...\n",
b0d623f7 2683 // jib_offset,
2d21ac55 2684 // jib_size, SWAP_BE32(vhp->blockSize));
b4c24cb9
A
2685
2686 hfsmp->jnl = journal_open(hfsmp->jvp,
b0d623f7 2687 jib_offset,
2d21ac55 2688 jib_size,
b4c24cb9 2689 devvp,
593a1d5f 2690 hfsmp->hfs_logical_block_size,
b4c24cb9
A
2691 arg_flags,
2692 arg_tbufsz,
39236c6e
A
2693 hfs_sync_metadata, hfsmp->hfs_mp,
2694 hfsmp->hfs_mp);
6d2010ae
A
2695 if (hfsmp->jnl)
2696 journal_trim_set_callback(hfsmp->jnl, hfs_trim_callback, hfsmp);
b4c24cb9
A
2697 }
2698
2699
2700 if (write_jibp) {
2d21ac55
A
2701 jibp->flags = SWAP_BE32(jib_flags);
2702 jibp->offset = SWAP_BE64(jib_offset);
2703 jibp->size = SWAP_BE64(jib_size);
b4c24cb9 2704
91447636 2705 buf_bwrite(jinfo_bp);
b4c24cb9 2706 } else {
91447636 2707 buf_brelse(jinfo_bp);
b4c24cb9
A
2708 }
2709 jinfo_bp = NULL;
2710 jibp = NULL;
2711
b4c24cb9
A
2712 // if we expected the journal to be there and we couldn't
2713 // create it or open it then we have to bail out.
2714 if (hfsmp->jnl == NULL) {
55e303ae 2715 printf("hfs: late jnl init: failed to open/create the journal (retval %d).\n", retval);
b4c24cb9
A
2716 return EINVAL;
2717 }
2718
2719 return 0;
2720}
55e303ae
A
2721
2722/*
2723 * Calculate the allocation zone for metadata.
2724 *
2725 * This zone includes the following:
2726 * Allocation Bitmap file
2727 * Overflow Extents file
2728 * Journal file
2729 * Quota files
2730 * Clustered Hot files
2731 * Catalog file
2732 *
2733 * METADATA ALLOCATION ZONE
2734 * ____________________________________________________________________________
2735 * | | | | | | |
2736 * | BM | JF | OEF | CATALOG |---> | HOT FILES |
2737 * |____|____|_____|_______________|______________________________|___________|
2738 *
2739 * <------------------------------- N * 128 MB ------------------------------->
2740 *
2741 */
2742#define GIGABYTE (u_int64_t)(1024*1024*1024)
2743
2744#define OVERFLOW_DEFAULT_SIZE (4*1024*1024)
2745#define OVERFLOW_MAXIMUM_SIZE (128*1024*1024)
2746#define JOURNAL_DEFAULT_SIZE (8*1024*1024)
2747#define JOURNAL_MAXIMUM_SIZE (512*1024*1024)
2748#define HOTBAND_MINIMUM_SIZE (10*1024*1024)
2749#define HOTBAND_MAXIMUM_SIZE (512*1024*1024)
2750
6d2010ae
A
2751/* Initialize the metadata zone.
2752 *
2753 * If the size of the volume is less than the minimum size for
2754 * metadata zone, metadata zone is disabled.
2755 *
2756 * If disable is true, disable metadata zone unconditionally.
2757 */
0b4c1975 2758void
6d2010ae 2759hfs_metadatazone_init(struct hfsmount *hfsmp, int disable)
55e303ae
A
2760{
2761 ExtendedVCB *vcb;
55e303ae
A
2762 u_int64_t fs_size;
2763 u_int64_t zonesize;
2764 u_int64_t temp;
2765 u_int64_t filesize;
2766 u_int32_t blk;
b0d623f7 2767 int items, really_do_it=1;
55e303ae
A
2768
2769 vcb = HFSTOVCB(hfsmp);
0b4c1975 2770 fs_size = (u_int64_t)vcb->blockSize * (u_int64_t)vcb->allocLimit;
55e303ae
A
2771
2772 /*
2773 * For volumes less than 10 GB, don't bother.
2774 */
b0d623f7
A
2775 if (fs_size < ((u_int64_t)10 * GIGABYTE)) {
2776 really_do_it = 0;
2777 }
2778
55e303ae
A
2779 /*
2780 * Skip non-journaled volumes as well.
2781 */
b0d623f7
A
2782 if (hfsmp->jnl == NULL) {
2783 really_do_it = 0;
2784 }
55e303ae 2785
6d2010ae
A
2786 /* If caller wants to disable metadata zone, do it */
2787 if (disable == true) {
2788 really_do_it = 0;
2789 }
2790
55e303ae 2791 /*
b0d623f7
A
2792 * Start with space for the boot blocks and Volume Header.
2793 * 1536 = byte offset from start of volume to end of volume header:
2794 * 1024 bytes is the offset from the start of the volume to the
2795 * start of the volume header (defined by the volume format)
2796 * + 512 bytes (the size of the volume header).
55e303ae 2797 */
b0d623f7
A
2798 zonesize = roundup(1536, hfsmp->blockSize);
2799
55e303ae 2800 /*
b0d623f7 2801 * Add the on-disk size of allocation bitmap.
55e303ae 2802 */
b0d623f7
A
2803 zonesize += hfsmp->hfs_allocation_cp->c_datafork->ff_blocks * hfsmp->blockSize;
2804
2805 /*
2806 * Add space for the Journal Info Block and Journal (if they're in
2807 * this file system).
2808 */
2809 if (hfsmp->jnl && hfsmp->jvp == hfsmp->hfs_devvp) {
2810 zonesize += hfsmp->blockSize + hfsmp->jnl_size;
2811 }
2812
55e303ae 2813 /*
b0d623f7
A
2814 * Add the existing size of the Extents Overflow B-tree.
2815 * (It rarely grows, so don't bother reserving additional room for it.)
55e303ae 2816 */
b0d623f7
A
2817 zonesize += hfsmp->hfs_extents_cp->c_datafork->ff_blocks * hfsmp->blockSize;
2818
55e303ae 2819 /*
b0d623f7
A
2820 * If there is an Attributes B-tree, leave room for 11 clumps worth.
2821 * newfs_hfs allocates one clump, and leaves a gap of 10 clumps.
2822 * When installing a full OS install onto a 20GB volume, we use
2823 * 7 to 8 clumps worth of space (depending on packages), so that leaves
2824 * us with another 3 or 4 clumps worth before we need another extent.
55e303ae 2825 */
b0d623f7
A
2826 if (hfsmp->hfs_attribute_cp) {
2827 zonesize += 11 * hfsmp->hfs_attribute_cp->c_datafork->ff_clumpsize;
2828 }
2829
2830 /*
2831 * Leave room for 11 clumps of the Catalog B-tree.
2832 * Again, newfs_hfs allocates one clump plus a gap of 10 clumps.
2833 * When installing a full OS install onto a 20GB volume, we use
2834 * 7 to 8 clumps worth of space (depending on packages), so that leaves
2835 * us with another 3 or 4 clumps worth before we need another extent.
2836 */
2837 zonesize += 11 * hfsmp->hfs_catalog_cp->c_datafork->ff_clumpsize;
2838
55e303ae
A
2839 /*
2840 * Add space for hot file region.
2841 *
2842 * ...for now, use 5 MB per 1 GB (0.5 %)
2843 */
2844 filesize = (fs_size / 1024) * 5;
2845 if (filesize > HOTBAND_MAXIMUM_SIZE)
2846 filesize = HOTBAND_MAXIMUM_SIZE;
2847 else if (filesize < HOTBAND_MINIMUM_SIZE)
2848 filesize = HOTBAND_MINIMUM_SIZE;
2849 /*
2850 * Calculate user quota file requirements.
2851 */
b0d623f7
A
2852 if (hfsmp->hfs_flags & HFS_QUOTAS) {
2853 items = QF_USERS_PER_GB * (fs_size / GIGABYTE);
2854 if (items < QF_MIN_USERS)
2855 items = QF_MIN_USERS;
2856 else if (items > QF_MAX_USERS)
2857 items = QF_MAX_USERS;
2858 if (!powerof2(items)) {
2859 int x = items;
2860 items = 4;
2861 while (x>>1 != 1) {
2862 x = x >> 1;
2863 items = items << 1;
2864 }
55e303ae 2865 }
b0d623f7
A
2866 filesize += (items + 1) * sizeof(struct dqblk);
2867 /*
2868 * Calculate group quota file requirements.
2869 *
2870 */
2871 items = QF_GROUPS_PER_GB * (fs_size / GIGABYTE);
2872 if (items < QF_MIN_GROUPS)
2873 items = QF_MIN_GROUPS;
2874 else if (items > QF_MAX_GROUPS)
2875 items = QF_MAX_GROUPS;
2876 if (!powerof2(items)) {
2877 int x = items;
2878 items = 4;
2879 while (x>>1 != 1) {
2880 x = x >> 1;
2881 items = items << 1;
2882 }
55e303ae 2883 }
b0d623f7 2884 filesize += (items + 1) * sizeof(struct dqblk);
55e303ae 2885 }
55e303ae
A
2886 zonesize += filesize;
2887
2888 /*
2889 * Round up entire zone to a bitmap block's worth.
2890 * The extra space goes to the catalog file and hot file area.
2891 */
2892 temp = zonesize;
6601e61a 2893 zonesize = roundup(zonesize, (u_int64_t)vcb->vcbVBMIOSize * 8 * vcb->blockSize);
b0d623f7
A
2894 hfsmp->hfs_min_alloc_start = zonesize / vcb->blockSize;
2895 /*
2896 * If doing the round up for hfs_min_alloc_start would push us past
0b4c1975
A
2897 * allocLimit, then just reset it back to 0. Though using a value
2898 * bigger than allocLimit would not cause damage in the block allocator
b0d623f7
A
2899 * code, this value could get stored in the volume header and make it out
2900 * to disk, making the volume header technically corrupt.
2901 */
0b4c1975 2902 if (hfsmp->hfs_min_alloc_start >= hfsmp->allocLimit) {
b0d623f7
A
2903 hfsmp->hfs_min_alloc_start = 0;
2904 }
2905
2906 if (really_do_it == 0) {
0b4c1975
A
2907 /* If metadata zone needs to be disabled because the
2908 * volume was truncated, clear the bit and zero out
2909 * the values that are no longer needed.
2910 */
2911 if (hfsmp->hfs_flags & HFS_METADATA_ZONE) {
2912 /* Disable metadata zone */
2913 hfsmp->hfs_flags &= ~HFS_METADATA_ZONE;
2914
2915 /* Zero out mount point values that are not required */
2916 hfsmp->hfs_catalog_maxblks = 0;
2917 hfsmp->hfs_hotfile_maxblks = 0;
2918 hfsmp->hfs_hotfile_start = 0;
2919 hfsmp->hfs_hotfile_end = 0;
2920 hfsmp->hfs_hotfile_freeblks = 0;
2921 hfsmp->hfs_metazone_start = 0;
2922 hfsmp->hfs_metazone_end = 0;
2923 }
2924
b0d623f7
A
2925 return;
2926 }
2927
55e303ae
A
2928 temp = zonesize - temp; /* temp has extra space */
2929 filesize += temp / 3;
2930 hfsmp->hfs_catalog_maxblks += (temp - (temp / 3)) / vcb->blockSize;
2931
91447636
A
2932 hfsmp->hfs_hotfile_maxblks = filesize / vcb->blockSize;
2933
55e303ae
A
2934 /* Convert to allocation blocks. */
2935 blk = zonesize / vcb->blockSize;
2936
2937 /* The default metadata zone location is at the start of volume. */
2938 hfsmp->hfs_metazone_start = 1;
2939 hfsmp->hfs_metazone_end = blk - 1;
2940
2941 /* The default hotfile area is at the end of the zone. */
39236c6e
A
2942 if (vfs_flags(HFSTOVFS(hfsmp)) & MNT_ROOTFS) {
2943 hfsmp->hfs_hotfile_start = blk - (filesize / vcb->blockSize);
2944 hfsmp->hfs_hotfile_end = hfsmp->hfs_metazone_end;
2945 hfsmp->hfs_hotfile_freeblks = hfs_hotfile_freeblocks(hfsmp);
2946 }
2947 else {
2948 hfsmp->hfs_hotfile_start = 0;
2949 hfsmp->hfs_hotfile_end = 0;
2950 hfsmp->hfs_hotfile_freeblks = 0;
2951 }
55e303ae 2952#if 0
b0d623f7
A
2953 printf("hfs: metadata zone is %d to %d\n", hfsmp->hfs_metazone_start, hfsmp->hfs_metazone_end);
2954 printf("hfs: hot file band is %d to %d\n", hfsmp->hfs_hotfile_start, hfsmp->hfs_hotfile_end);
2955 printf("hfs: hot file band free blocks = %d\n", hfsmp->hfs_hotfile_freeblks);
55e303ae
A
2956#endif
2957 hfsmp->hfs_flags |= HFS_METADATA_ZONE;
2958}
2959
2960
2961static u_int32_t
2962hfs_hotfile_freeblocks(struct hfsmount *hfsmp)
2963{
2964 ExtendedVCB *vcb = HFSTOVCB(hfsmp);
91447636 2965 int lockflags;
55e303ae
A
2966 int freeblocks;
2967
91447636 2968 lockflags = hfs_systemfile_lock(hfsmp, SFL_BITMAP, HFS_EXCLUSIVE_LOCK);
55e303ae 2969 freeblocks = MetaZoneFreeBlocks(vcb);
91447636
A
2970 hfs_systemfile_unlock(hfsmp, lockflags);
2971
55e303ae
A
2972 /* Minus Extents overflow file reserve. */
2973 freeblocks -=
91447636 2974 hfsmp->hfs_overflow_maxblks - VTOF(hfsmp->hfs_extents_vp)->ff_blocks;
55e303ae
A
2975 /* Minus catalog file reserve. */
2976 freeblocks -=
91447636 2977 hfsmp->hfs_catalog_maxblks - VTOF(hfsmp->hfs_catalog_vp)->ff_blocks;
55e303ae
A
2978 if (freeblocks < 0)
2979 freeblocks = 0;
2980
2981 return MIN(freeblocks, hfsmp->hfs_hotfile_maxblks);
2982}
2983
2984/*
2985 * Determine if a file is a "virtual" metadata file.
2986 * This includes journal and quota files.
2987 */
55e303ae
A
2988int
2989hfs_virtualmetafile(struct cnode *cp)
2990{
2d21ac55 2991 const char * filename;
55e303ae
A
2992
2993
2994 if (cp->c_parentcnid != kHFSRootFolderID)
2995 return (0);
2996
2d21ac55 2997 filename = (const char *)cp->c_desc.cd_nameptr;
55e303ae
A
2998 if (filename == NULL)
2999 return (0);
3000
2d21ac55
A
3001 if ((strncmp(filename, ".journal", sizeof(".journal")) == 0) ||
3002 (strncmp(filename, ".journal_info_block", sizeof(".journal_info_block")) == 0) ||
3003 (strncmp(filename, ".quota.user", sizeof(".quota.user")) == 0) ||
3004 (strncmp(filename, ".quota.group", sizeof(".quota.group")) == 0) ||
3005 (strncmp(filename, ".hotfiles.btree", sizeof(".hotfiles.btree")) == 0))
55e303ae
A
3006 return (1);
3007
3008 return (0);
3009}
3010
22ba694c
A
3011__private_extern__
3012void hfs_syncer_lock(struct hfsmount *hfsmp)
3013{
3014 hfs_lock_mount(hfsmp);
3015}
3016
3017__private_extern__
3018void hfs_syncer_unlock(struct hfsmount *hfsmp)
3019{
3020 hfs_unlock_mount(hfsmp);
3021}
3022
3023__private_extern__
3024void hfs_syncer_wait(struct hfsmount *hfsmp)
3025{
3026 msleep(&hfsmp->hfs_sync_incomplete, &hfsmp->hfs_mutex, PWAIT,
3027 "hfs_syncer_wait", NULL);
3028}
3029
3030__private_extern__
3031void hfs_syncer_wakeup(struct hfsmount *hfsmp)
3032{
3033 wakeup(&hfsmp->hfs_sync_incomplete);
3034}
3035
3036__private_extern__
3037uint64_t hfs_usecs_to_deadline(uint64_t usecs)
3038{
3039 uint64_t deadline;
3040 clock_interval_to_deadline(usecs, NSEC_PER_USEC, &deadline);
3041 return deadline;
3042}
3043
3044__private_extern__
3045void hfs_syncer_queue(thread_call_t syncer)
3046{
3047 if (thread_call_enter_delayed_with_leeway(syncer,
3048 NULL,
3049 hfs_usecs_to_deadline(HFS_META_DELAY),
3050 0,
3051 THREAD_CALL_DELAY_SYS_BACKGROUND)) {
3052 printf ("hfs: syncer already scheduled!");
3053 }
3054}
91447636 3055
e2fac8b1
A
3056//
3057// Fire off a timed callback to sync the disk if the
3058// volume is on ejectable media.
3059//
3060 __private_extern__
3061void
3062hfs_sync_ejectable(struct hfsmount *hfsmp)
3063{
22ba694c
A
3064 // If we don't have a syncer or we get called by the syncer, just return
3065 if (!hfsmp->hfs_syncer || current_thread() == hfsmp->hfs_syncer_thread)
3066 return;
e2fac8b1 3067
22ba694c 3068 hfs_syncer_lock(hfsmp);
b0d623f7 3069
22ba694c
A
3070 if (!timerisset(&hfsmp->hfs_sync_req_oldest))
3071 microuptime(&hfsmp->hfs_sync_req_oldest);
e2fac8b1 3072
22ba694c
A
3073 /* If hfs_unmount is running, it will set hfs_syncer to NULL. Also we
3074 don't want to queue again if there is a sync outstanding. */
3075 if (!hfsmp->hfs_syncer || hfsmp->hfs_sync_incomplete) {
3076 hfs_syncer_unlock(hfsmp);
3077 return;
3078 }
e2fac8b1 3079
22ba694c 3080 hfsmp->hfs_sync_incomplete = TRUE;
e2fac8b1 3081
22ba694c 3082 thread_call_t syncer = hfsmp->hfs_syncer;
e2fac8b1 3083
22ba694c 3084 hfs_syncer_unlock(hfsmp);
e2fac8b1 3085
22ba694c
A
3086 hfs_syncer_queue(syncer);
3087}
e2fac8b1 3088
91447636
A
3089int
3090hfs_start_transaction(struct hfsmount *hfsmp)
3091{
2d21ac55
A
3092 int ret, unlock_on_err=0;
3093 void * thread = current_thread();
3094
3095#ifdef HFS_CHECK_LOCK_ORDER
3096 /*
3097 * You cannot start a transaction while holding a system
3098 * file lock. (unless the transaction is nested.)
3099 */
3100 if (hfsmp->jnl && journal_owner(hfsmp->jnl) != thread) {
3101 if (hfsmp->hfs_catalog_cp && hfsmp->hfs_catalog_cp->c_lockowner == thread) {
3102 panic("hfs_start_transaction: bad lock order (cat before jnl)\n");
3103 }
3104 if (hfsmp->hfs_attribute_cp && hfsmp->hfs_attribute_cp->c_lockowner == thread) {
3105 panic("hfs_start_transaction: bad lock order (attr before jnl)\n");
3106 }
3107 if (hfsmp->hfs_extents_cp && hfsmp->hfs_extents_cp->c_lockowner == thread) {
3108 panic("hfs_start_transaction: bad lock order (ext before jnl)\n");
3109 }
3110 }
3111#endif /* HFS_CHECK_LOCK_ORDER */
91447636 3112
6d2010ae
A
3113 if (hfsmp->jnl == NULL || journal_owner(hfsmp->jnl) != thread) {
3114 hfs_lock_global (hfsmp, HFS_SHARED_LOCK);
3115 OSAddAtomic(1, (SInt32 *)&hfsmp->hfs_active_threads);
3116 unlock_on_err = 1;
3117 }
91447636 3118
c910b4d9
A
3119 /* If a downgrade to read-only mount is in progress, no other
3120 * process than the downgrade process is allowed to modify
3121 * the file system.
3122 */
3123 if ((hfsmp->hfs_flags & HFS_RDONLY_DOWNGRADE) &&
3124 (hfsmp->hfs_downgrading_proc != thread)) {
3125 ret = EROFS;
3126 goto out;
3127 }
3128
6d2010ae
A
3129 if (hfsmp->jnl) {
3130 ret = journal_start_transaction(hfsmp->jnl);
3131 if (ret == 0) {
3132 OSAddAtomic(1, &hfsmp->hfs_global_lock_nesting);
3133 }
3134 } else {
3135 ret = 0;
91447636 3136 }
91447636 3137
c910b4d9 3138out:
6d2010ae
A
3139 if (ret != 0 && unlock_on_err) {
3140 hfs_unlock_global (hfsmp);
3141 OSAddAtomic(-1, (SInt32 *)&hfsmp->hfs_active_threads);
3142 }
91447636
A
3143
3144 return ret;
3145}
3146
91447636
A
3147int
3148hfs_end_transaction(struct hfsmount *hfsmp)
3149{
3150 int need_unlock=0, ret;
3151
6d2010ae 3152 if ((hfsmp->jnl == NULL) || ( journal_owner(hfsmp->jnl) == current_thread()
b0d623f7 3153 && (OSAddAtomic(-1, &hfsmp->hfs_global_lock_nesting) == 1)) ) {
91447636
A
3154 need_unlock = 1;
3155 }
3156
6d2010ae
A
3157 if (hfsmp->jnl) {
3158 ret = journal_end_transaction(hfsmp->jnl);
3159 } else {
3160 ret = 0;
3161 }
91447636 3162
6d2010ae
A
3163 if (need_unlock) {
3164 OSAddAtomic(-1, (SInt32 *)&hfsmp->hfs_active_threads);
3165 hfs_unlock_global (hfsmp);
3166 hfs_sync_ejectable(hfsmp);
3167 }
91447636
A
3168
3169 return ret;
3170}
b0d623f7
A
3171
3172
6d2010ae
A
3173/*
3174 * Flush the contents of the journal to the disk.
3175 *
3176 * Input:
3177 * wait_for_IO -
3178 * If TRUE, wait to write in-memory journal to the disk
3179 * consistently, and also wait to write all asynchronous
3180 * metadata blocks to its corresponding locations
3181 * consistently on the disk. This means that the journal
3182 * is empty at this point and does not contain any
3183 * transactions. This is overkill in normal scenarios
3184 * but is useful whenever the metadata blocks are required
3185 * to be consistent on-disk instead of just the journal
3186 * being consistent; like before live verification
3187 * and live volume resizing.
3188 *
3189 * If FALSE, only wait to write in-memory journal to the
3190 * disk consistently. This means that the journal still
3191 * contains uncommitted transactions and the file system
3192 * metadata blocks in the journal transactions might be
3193 * written asynchronously to the disk. But there is no
3194 * guarantee that they are written to the disk before
3195 * returning to the caller. Note that this option is
3196 * sufficient for file system data integrity as it
3197 * guarantees consistent journal content on the disk.
3198 */
b0d623f7 3199int
6d2010ae 3200hfs_journal_flush(struct hfsmount *hfsmp, boolean_t wait_for_IO)
b0d623f7
A
3201{
3202 int ret;
6d2010ae 3203
d1ecb069 3204 /* Only peek at hfsmp->jnl while holding the global lock */
6d2010ae 3205 hfs_lock_global (hfsmp, HFS_SHARED_LOCK);
b0d623f7 3206 if (hfsmp->jnl) {
6d2010ae 3207 ret = journal_flush(hfsmp->jnl, wait_for_IO);
b0d623f7
A
3208 } else {
3209 ret = 0;
3210 }
6d2010ae 3211 hfs_unlock_global (hfsmp);
d1ecb069 3212
b0d623f7
A
3213 return ret;
3214}
3215
3216
3217/*
3218 * hfs_erase_unused_nodes
3219 *
3220 * Check wheter a volume may suffer from unused Catalog B-tree nodes that
3221 * are not zeroed (due to <rdar://problem/6947811>). If so, just write
3222 * zeroes to the unused nodes.
3223 *
3224 * How do we detect when a volume needs this repair? We can't always be
3225 * certain. If a volume was created after a certain date, then it may have
3226 * been created with the faulty newfs_hfs. Since newfs_hfs only created one
3227 * clump, we can assume that if a Catalog B-tree is larger than its clump size,
3228 * that means that the entire first clump must have been written to, which means
3229 * there shouldn't be unused and unwritten nodes in that first clump, and this
3230 * repair is not needed.
3231 *
3232 * We have defined a bit in the Volume Header's attributes to indicate when the
3233 * unused nodes have been repaired. A newer newfs_hfs will set this bit.
3234 * As will fsck_hfs when it repairs the unused nodes.
3235 */
b0d623f7
A
3236int hfs_erase_unused_nodes(struct hfsmount *hfsmp)
3237{
3238 int result;
3239 struct filefork *catalog;
3240 int lockflags;
3241
3242 if (hfsmp->vcbAtrb & kHFSUnusedNodeFixMask)
3243 {
3244 /* This volume has already been checked and repaired. */
3245 return 0;
3246 }
3247
3248 if ((hfsmp->localCreateDate < kHFSUnusedNodesFixDate))
3249 {
3250 /* This volume is too old to have had the problem. */
3251 hfsmp->vcbAtrb |= kHFSUnusedNodeFixMask;
3252 return 0;
3253 }
3254
3255 catalog = hfsmp->hfs_catalog_cp->c_datafork;
3256 if (catalog->ff_size > catalog->ff_clumpsize)
3257 {
3258 /* The entire first clump must have been in use at some point. */
3259 hfsmp->vcbAtrb |= kHFSUnusedNodeFixMask;
3260 return 0;
3261 }
3262
3263 /*
3264 * If we get here, we need to zero out those unused nodes.
3265 *
3266 * We start a transaction and lock the catalog since we're going to be
3267 * making on-disk changes. But note that BTZeroUnusedNodes doens't actually
3268 * do its writing via the journal, because that would be too much I/O
3269 * to fit in a transaction, and it's a pain to break it up into multiple
3270 * transactions. (It behaves more like growing a B-tree would.)
3271 */
3272 printf("hfs_erase_unused_nodes: updating volume %s.\n", hfsmp->vcbVN);
3273 result = hfs_start_transaction(hfsmp);
3274 if (result)
3275 goto done;
3276 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK);
3277 result = BTZeroUnusedNodes(catalog);
3278 vnode_waitforwrites(hfsmp->hfs_catalog_vp, 0, 0, 0, "hfs_erase_unused_nodes");
3279 hfs_systemfile_unlock(hfsmp, lockflags);
3280 hfs_end_transaction(hfsmp);
3281 if (result == 0)
3282 hfsmp->vcbAtrb |= kHFSUnusedNodeFixMask;
3283 printf("hfs_erase_unused_nodes: done updating volume %s.\n", hfsmp->vcbVN);
3284
3285done:
3286 return result;
3287}
6d2010ae
A
3288
3289
3290extern time_t snapshot_timestamp;
3291
3292int
3293check_for_tracked_file(struct vnode *vp, time_t ctime, uint64_t op_type, void *arg)
3294{
3295 int tracked_error = 0, snapshot_error = 0;
3296
3297 if (vp == NULL) {
3298 return 0;
3299 }
3300
39236c6e
A
3301 /* Swap files are special; skip them */
3302 if (vnode_isswap(vp)) {
3303 return 0;
3304 }
3305
316670eb 3306 if (VTOC(vp)->c_bsdflags & UF_TRACKED) {
6d2010ae
A
3307 // the file has the tracked bit set, so send an event to the tracked-file handler
3308 int error;
3309
3310 // printf("hfs: tracked-file: encountered a file with the tracked bit set! (vp %p)\n", vp);
3311 error = resolve_nspace_item(vp, op_type | NAMESPACE_HANDLER_TRACK_EVENT);
3312 if (error) {
3313 if (error == EAGAIN) {
3314 printf("hfs: tracked-file: timed out waiting for namespace handler...\n");
3315
3316 } else if (error == EINTR) {
3317 // printf("hfs: tracked-file: got a signal while waiting for namespace handler...\n");
3318 tracked_error = EINTR;
3319 }
3320 }
3321 }
3322
3323 if (ctime != 0 && snapshot_timestamp != 0 && (ctime <= snapshot_timestamp || vnode_needssnapshots(vp))) {
3324 // the change time is within this epoch
3325 int error;
3326
3327 error = resolve_nspace_item_ext(vp, op_type | NAMESPACE_HANDLER_SNAPSHOT_EVENT, arg);
3328 if (error == EDEADLK) {
3329 snapshot_error = 0;
3330 } else if (error) {
3331 if (error == EAGAIN) {
3332 printf("hfs: cow-snapshot: timed out waiting for namespace handler...\n");
3333 } else if (error == EINTR) {
3334 // printf("hfs: cow-snapshot: got a signal while waiting for namespace handler...\n");
3335 snapshot_error = EINTR;
3336 }
3337 }
3338 }
3339
3340 if (tracked_error) return tracked_error;
3341 if (snapshot_error) return snapshot_error;
3342
3343 return 0;
3344}
3345
3346int
3347check_for_dataless_file(struct vnode *vp, uint64_t op_type)
3348{
3349 int error;
3350
316670eb 3351 if (vp == NULL || (VTOC(vp)->c_bsdflags & UF_COMPRESSED) == 0 || VTOCMP(vp) == NULL || VTOCMP(vp)->cmp_type != DATALESS_CMPFS_TYPE) {
6d2010ae
A
3352 // there's nothing to do, it's not dataless
3353 return 0;
3354 }
39236c6e
A
3355
3356 /* Swap files are special; ignore them */
3357 if (vnode_isswap(vp)) {
3358 return 0;
3359 }
3360
6d2010ae
A
3361 // printf("hfs: dataless: encountered a file with the dataless bit set! (vp %p)\n", vp);
3362 error = resolve_nspace_item(vp, op_type | NAMESPACE_HANDLER_NSPACE_EVENT);
3363 if (error == EDEADLK && op_type == NAMESPACE_HANDLER_WRITE_OP) {
3364 error = 0;
3365 } else if (error) {
3366 if (error == EAGAIN) {
3367 printf("hfs: dataless: timed out waiting for namespace handler...\n");
3368 // XXXdbg - return the fabled ENOTPRESENT (i.e. EJUKEBOX)?
3369 return 0;
3370 } else if (error == EINTR) {
3371 // printf("hfs: dataless: got a signal while waiting for namespace handler...\n");
3372 return EINTR;
3373 }
316670eb 3374 } else if (VTOC(vp)->c_bsdflags & UF_COMPRESSED) {
6d2010ae
A
3375 //
3376 // if we're here, the dataless bit is still set on the file
3377 // which means it didn't get handled. we return an error
3378 // but it's presently ignored by all callers of this function.
3379 //
3380 // XXXdbg - EDATANOTPRESENT is what we really need...
3381 //
3382 return EBADF;
3383 }
3384
3385 return error;
3386}
22ba694c
A
3387
3388
3389//
3390// NOTE: this function takes care of starting a transaction and
3391// acquiring the systemfile lock so that it can call
3392// cat_update().
3393//
3394// NOTE: do NOT hold and cnode locks while calling this function
3395// to avoid deadlocks (because we take a lock on the root
3396// cnode)
3397//
3398int
3399hfs_generate_document_id(struct hfsmount *hfsmp, uint32_t *docid)
3400{
3401 struct vnode *rvp;
3402 struct cnode *cp;
3403 int error;
3404
3405 error = VFS_ROOT(HFSTOVFS(hfsmp), &rvp, vfs_context_kernel());
3406 if (error) {
3407 return error;
3408 }
3409
3410 cp = VTOC(rvp);
3411 if ((error = hfs_lock(cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT)) != 0) {
3412 return error;
3413 }
3414 struct FndrExtendedDirInfo *extinfo = (struct FndrExtendedDirInfo *)((void *)((char *)&cp->c_attr.ca_finderinfo + 16));
3415
3416 int lockflags;
3417 if (hfs_start_transaction(hfsmp) != 0) {
3418 return error;
3419 }
3420 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK);
3421
3422 if (extinfo->document_id == 0) {
3423 // initialize this to start at 3 (one greater than the root-dir id)
3424 extinfo->document_id = 3;
3425 }
3426
3427 *docid = extinfo->document_id++;
3428
3429 // mark the root cnode dirty
3430 cp->c_flag |= C_MODIFIED | C_FORCEUPDATE;
3431 (void) cat_update(hfsmp, &cp->c_desc, &cp->c_attr, NULL, NULL);
3432
3433 hfs_systemfile_unlock (hfsmp, lockflags);
3434 (void) hfs_end_transaction(hfsmp);
3435
3436 (void) hfs_unlock(cp);
3437
3438 vnode_put(rvp);
3439 rvp = NULL;
3440
3441 return 0;
3442}