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