]> git.saurik.com Git - apple/xnu.git/blob - bsd/hfs/hfs_vfsutils.c
63aa2a90ea671d19a392e081ca03df519bf74425
[apple/xnu.git] / bsd / hfs / hfs_vfsutils.c
1 /*
2 * Copyright (c) 2000-2002 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
7 *
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * file.
14 *
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
22 *
23 * @APPLE_LICENSE_HEADER_END@
24 */
25 /* @(#)hfs_vfsutils.c 4.0
26 *
27 * (c) 1997-2002 Apple Computer, Inc. All Rights Reserved
28 *
29 * hfs_vfsutils.c -- Routines that go between the HFS layer and the VFS.
30 *
31 */
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/kernel.h>
35 #include <sys/malloc.h>
36 #include <sys/stat.h>
37 #include <sys/mount.h>
38 #include <sys/namei.h>
39 #include <sys/lock.h>
40 #include <sys/buf.h>
41 #include <sys/ubc.h>
42 #include <sys/unistd.h>
43
44 #include "hfs.h"
45 #include "hfs_catalog.h"
46 #include "hfs_dbg.h"
47 #include "hfs_mount.h"
48 #include "hfs_endian.h"
49 #include "hfs_cnode.h"
50
51 #include "hfscommon/headers/FileMgrInternal.h"
52 #include "hfscommon/headers/BTreesInternal.h"
53 #include "hfscommon/headers/HFSUnicodeWrappers.h"
54
55
56 extern int count_lock_queue __P((void));
57 extern uid_t console_user;
58
59
60 static void ReleaseMetaFileVNode(struct vnode *vp);
61 static int hfs_late_journal_init(struct hfsmount *hfsmp, HFSPlusVolumeHeader *vhp, void *_args);
62
63 u_int32_t GetLogicalBlockSize(struct vnode *vp);
64
65 /* BTree accessor routines */
66 extern OSStatus GetBTreeBlock(FileReference vp, UInt32 blockNum, GetBlockOptions options, BlockDescriptor *block);
67 extern OSStatus SetBTreeBlockSize(FileReference vp, ByteCount blockSize, ItemCount minBlockCount);
68 extern OSStatus ExtendBTreeFile(FileReference vp, FSSize minEOF, FSSize maxEOF);
69 extern OSStatus ReleaseBTreeBlock(FileReference vp, BlockDescPtr blockPtr, ReleaseBlockOptions options);
70
71 //*******************************************************************************
72 // Note: Finder information in the HFS/HFS+ metadata are considered opaque and
73 // hence are not in the right byte order on little endian machines. It is
74 // the responsibility of the finder and other clients to swap the data.
75 //*******************************************************************************
76
77 //*******************************************************************************
78 // Routine: hfs_MountHFSVolume
79 //
80 //
81 //*******************************************************************************
82 char hfs_catname[] = "Catalog B-tree";
83 char hfs_extname[] = "Extents B-tree";
84 char hfs_vbmname[] = "Volume Bitmap";
85
86 char hfs_privdirname[] =
87 "\xE2\x90\x80\xE2\x90\x80\xE2\x90\x80\xE2\x90\x80HFS+ Private Data";
88
89 OSErr hfs_MountHFSVolume(struct hfsmount *hfsmp, HFSMasterDirectoryBlock *mdb,
90 struct proc *p)
91 {
92 ExtendedVCB *vcb = HFSTOVCB(hfsmp);
93 int error;
94 ByteCount utf8chars;
95 struct cat_desc cndesc;
96 struct cat_attr cnattr;
97 struct cat_fork fork;
98
99 /* Block size must be a multiple of 512 */
100 if (SWAP_BE32(mdb->drAlBlkSiz) == 0 ||
101 (SWAP_BE32(mdb->drAlBlkSiz) & 0x01FF) != 0)
102 return (EINVAL);
103
104 /* don't mount a writeable volume if its dirty, it must be cleaned by fsck_hfs */
105 if ((hfsmp->hfs_fs_ronly == 0) && ((SWAP_BE16(mdb->drAtrb) & kHFSVolumeUnmountedMask) == 0))
106 return (EINVAL);
107
108 /*
109 * The MDB seems OK: transfer info from it into VCB
110 * Note - the VCB starts out clear (all zeros)
111 *
112 */
113 vcb->vcbSigWord = SWAP_BE16 (mdb->drSigWord);
114 vcb->vcbCrDate = to_bsd_time(LocalToUTC(SWAP_BE32(mdb->drCrDate)));
115 vcb->localCreateDate = SWAP_BE32 (mdb->drCrDate);
116 vcb->vcbLsMod = to_bsd_time(LocalToUTC(SWAP_BE32(mdb->drLsMod)));
117 vcb->vcbAtrb = SWAP_BE16 (mdb->drAtrb);
118 vcb->vcbNmFls = SWAP_BE16 (mdb->drNmFls);
119 vcb->vcbVBMSt = SWAP_BE16 (mdb->drVBMSt);
120 vcb->nextAllocation = SWAP_BE16 (mdb->drAllocPtr);
121 vcb->totalBlocks = SWAP_BE16 (mdb->drNmAlBlks);
122 vcb->blockSize = SWAP_BE32 (mdb->drAlBlkSiz);
123 vcb->vcbClpSiz = SWAP_BE32 (mdb->drClpSiz);
124 vcb->vcbAlBlSt = SWAP_BE16 (mdb->drAlBlSt);
125 vcb->vcbNxtCNID = SWAP_BE32 (mdb->drNxtCNID);
126 vcb->freeBlocks = SWAP_BE16 (mdb->drFreeBks);
127 vcb->vcbVolBkUp = to_bsd_time(LocalToUTC(SWAP_BE32(mdb->drVolBkUp)));
128 vcb->vcbWrCnt = SWAP_BE32 (mdb->drWrCnt);
129 vcb->vcbNmRtDirs = SWAP_BE16 (mdb->drNmRtDirs);
130 vcb->vcbFilCnt = SWAP_BE32 (mdb->drFilCnt);
131 vcb->vcbDirCnt = SWAP_BE32 (mdb->drDirCnt);
132 bcopy(mdb->drFndrInfo, vcb->vcbFndrInfo, sizeof(vcb->vcbFndrInfo));
133 if (!hfsmp->hfs_fs_ronly)
134 vcb->vcbWrCnt++; /* Compensate for write of MDB on last flush */
135
136 /* convert hfs encoded name into UTF-8 string */
137 error = hfs_to_utf8(vcb, mdb->drVN, NAME_MAX, &utf8chars, vcb->vcbVN);
138 /*
139 * When an HFS name cannot be encoded with the current
140 * volume encoding we use MacRoman as a fallback.
141 */
142 if (error || (utf8chars == 0))
143 (void) mac_roman_to_utf8(mdb->drVN, NAME_MAX, &utf8chars, vcb->vcbVN);
144
145 hfsmp->hfs_logBlockSize = BestBlockSizeFit(vcb->blockSize, MAXBSIZE, hfsmp->hfs_phys_block_size);
146 vcb->vcbVBMIOSize = kHFSBlockSize;
147
148 VCB_LOCK_INIT(vcb);
149
150 bzero(&cndesc, sizeof(cndesc));
151 cndesc.cd_parentcnid = kRootParID;
152 bzero(&cnattr, sizeof(cnattr));
153 cnattr.ca_nlink = 1;
154 cnattr.ca_mode = S_IFREG;
155 bzero(&fork, sizeof(fork));
156
157 /*
158 * Set up Extents B-tree vnode
159 */
160 cndesc.cd_nameptr = hfs_extname;
161 cndesc.cd_namelen = strlen(hfs_extname);
162 cndesc.cd_cnid = cnattr.ca_fileid = kHFSExtentsFileID;
163 fork.cf_size = SWAP_BE32(mdb->drXTFlSize);
164 fork.cf_blocks = fork.cf_size / vcb->blockSize;
165 fork.cf_clump = SWAP_BE32(mdb->drXTClpSiz);
166 fork.cf_extents[0].startBlock = SWAP_BE16(mdb->drXTExtRec[0].startBlock);
167 fork.cf_extents[0].blockCount = SWAP_BE16(mdb->drXTExtRec[0].blockCount);
168 fork.cf_extents[1].startBlock = SWAP_BE16(mdb->drXTExtRec[1].startBlock);
169 fork.cf_extents[1].blockCount = SWAP_BE16(mdb->drXTExtRec[1].blockCount);
170 fork.cf_extents[2].startBlock = SWAP_BE16(mdb->drXTExtRec[2].startBlock);
171 fork.cf_extents[2].blockCount = SWAP_BE16(mdb->drXTExtRec[2].blockCount);
172 cnattr.ca_blocks = fork.cf_blocks;
173
174 error = hfs_getnewvnode(hfsmp, NULL, &cndesc, 0, &cnattr, &fork,
175 &vcb->extentsRefNum);
176 if (error) goto MtVolErr;
177 error = MacToVFSError(BTOpenPath(VTOF(vcb->extentsRefNum),
178 (KeyCompareProcPtr)CompareExtentKeys,
179 GetBTreeBlock, ReleaseBTreeBlock,
180 ExtendBTreeFile, SetBTreeBlockSize));
181 if (error) {
182 VOP_UNLOCK(vcb->extentsRefNum, 0, p);
183 goto MtVolErr;
184 }
185
186 /*
187 * Set up Catalog B-tree vnode...
188 */
189 cndesc.cd_nameptr = hfs_catname;
190 cndesc.cd_namelen = strlen(hfs_catname);
191 cndesc.cd_cnid = cnattr.ca_fileid = kHFSCatalogFileID;
192 fork.cf_size = SWAP_BE32(mdb->drCTFlSize);
193 fork.cf_blocks = fork.cf_size / vcb->blockSize;
194 fork.cf_clump = SWAP_BE32(mdb->drCTClpSiz);
195 fork.cf_extents[0].startBlock = SWAP_BE16(mdb->drCTExtRec[0].startBlock);
196 fork.cf_extents[0].blockCount = SWAP_BE16(mdb->drCTExtRec[0].blockCount);
197 fork.cf_extents[1].startBlock = SWAP_BE16(mdb->drCTExtRec[1].startBlock);
198 fork.cf_extents[1].blockCount = SWAP_BE16(mdb->drCTExtRec[1].blockCount);
199 fork.cf_extents[2].startBlock = SWAP_BE16(mdb->drCTExtRec[2].startBlock);
200 fork.cf_extents[2].blockCount = SWAP_BE16(mdb->drCTExtRec[2].blockCount);
201 cnattr.ca_blocks = fork.cf_blocks;
202
203 error = hfs_getnewvnode(hfsmp, NULL, &cndesc, 0, &cnattr, &fork,
204 &vcb->catalogRefNum);
205 if (error) {
206 VOP_UNLOCK(vcb->extentsRefNum, 0, p);
207 goto MtVolErr;
208 }
209 error = MacToVFSError(BTOpenPath(VTOF(vcb->catalogRefNum),
210 (KeyCompareProcPtr)CompareCatalogKeys,
211 GetBTreeBlock, ReleaseBTreeBlock,
212 ExtendBTreeFile, SetBTreeBlockSize));
213 if (error) {
214 VOP_UNLOCK(vcb->catalogRefNum, 0, p);
215 VOP_UNLOCK(vcb->extentsRefNum, 0, p);
216 goto MtVolErr;
217 }
218
219 /* mark the volume dirty (clear clean unmount bit) */
220 vcb->vcbAtrb &= ~kHFSVolumeUnmountedMask;
221
222 /*
223 * all done with b-trees so we can unlock now...
224 */
225 VOP_UNLOCK(vcb->catalogRefNum, 0, p);
226 VOP_UNLOCK(vcb->extentsRefNum, 0, p);
227
228 if ( error == noErr )
229 {
230 if ( !(vcb->vcbAtrb & kHFSVolumeHardwareLockMask) ) // if the disk is not write protected
231 {
232 MarkVCBDirty( vcb ); // mark VCB dirty so it will be written
233 }
234 }
235 goto CmdDone;
236
237 //-- Release any resources allocated so far before exiting with an error:
238 MtVolErr:
239 ReleaseMetaFileVNode(vcb->catalogRefNum);
240 ReleaseMetaFileVNode(vcb->extentsRefNum);
241
242 CmdDone:
243 return (error);
244 }
245
246 //*******************************************************************************
247 // Routine: hfs_MountHFSPlusVolume
248 //
249 //
250 //*******************************************************************************
251
252 OSErr hfs_MountHFSPlusVolume(struct hfsmount *hfsmp, HFSPlusVolumeHeader *vhp,
253 off_t embeddedOffset, u_int64_t disksize, struct proc *p, void *args)
254 {
255 register ExtendedVCB *vcb;
256 struct cat_desc cndesc;
257 struct cat_attr cnattr;
258 UInt32 blockSize;
259 OSErr retval;
260
261 // XXXdbg - added the kHFSJSigWord case
262 if ((SWAP_BE16(vhp->signature) != kHFSPlusSigWord &&
263 SWAP_BE16(vhp->signature) != kHFSJSigWord) ||
264 SWAP_BE16(vhp->version) != kHFSPlusVersion) {
265 // XXXdbg
266 printf("hfs: mount: sig 0x%x and version 0x%x are not HFS or HFS+.\n",
267 vhp->signature, vhp->version);
268 return (EINVAL);
269 }
270
271 /* Block size must be at least 512 and a power of 2 */
272 blockSize = SWAP_BE32(vhp->blockSize);
273 if (blockSize < 512 || (blockSize & (blockSize-1)) != 0)
274 return (EINVAL);
275
276 /* don't mount a writable volume if its dirty, it must be cleaned by fsck_hfs */
277 if (hfsmp->hfs_fs_ronly == 0 && hfsmp->jnl == NULL && (SWAP_BE32(vhp->attributes) & kHFSVolumeUnmountedMask) == 0)
278 return (EINVAL);
279
280 /* Make sure we can live with the physical block size. */
281 if ((disksize & (hfsmp->hfs_phys_block_size - 1)) ||
282 (embeddedOffset & (hfsmp->hfs_phys_block_size - 1)) ||
283 (SWAP_BE32(vhp->blockSize) < hfsmp->hfs_phys_block_size)) {
284 return (ENXIO);
285 }
286 /*
287 * The VolumeHeader seems OK: transfer info from it into VCB
288 * Note - the VCB starts out clear (all zeros)
289 */
290 vcb = HFSTOVCB(hfsmp);
291
292 vcb->vcbSigWord = SWAP_BE16(vhp->signature);
293
294 // XXXdbg - remap this in case we've mounted a dirty journaled volume
295 if (vcb->vcbSigWord == kHFSJSigWord) {
296 vcb->vcbSigWord = kHFSPlusSigWord;
297 }
298
299 vcb->vcbJinfoBlock = SWAP_BE32(vhp->journalInfoBlock);
300 vcb->vcbLsMod = to_bsd_time(SWAP_BE32(vhp->modifyDate));
301 vcb->vcbAtrb = (UInt16)SWAP_BE32(vhp->attributes);
302 vcb->vcbClpSiz = SWAP_BE32(vhp->rsrcClumpSize);
303 vcb->vcbNxtCNID = SWAP_BE32(vhp->nextCatalogID);
304 vcb->vcbVolBkUp = to_bsd_time(SWAP_BE32(vhp->backupDate));
305 vcb->vcbWrCnt = SWAP_BE32(vhp->writeCount);
306 vcb->vcbFilCnt = SWAP_BE32(vhp->fileCount);
307 vcb->vcbDirCnt = SWAP_BE32(vhp->folderCount);
308
309 /* copy 32 bytes of Finder info */
310 bcopy(vhp->finderInfo, vcb->vcbFndrInfo, sizeof(vhp->finderInfo));
311
312 vcb->vcbAlBlSt = 0; /* hfs+ allocation blocks start at first block of volume */
313 if (!hfsmp->hfs_fs_ronly)
314 vcb->vcbWrCnt++; /* compensate for write of Volume Header on last flush */
315
316 VCB_LOCK_INIT(vcb);
317
318 /* Now fill in the Extended VCB info */
319 vcb->nextAllocation = SWAP_BE32(vhp->nextAllocation);
320 vcb->totalBlocks = SWAP_BE32(vhp->totalBlocks);
321 vcb->freeBlocks = SWAP_BE32(vhp->freeBlocks);
322 vcb->blockSize = SWAP_BE32(vhp->blockSize);
323 vcb->encodingsBitmap = SWAP_BE64(vhp->encodingsBitmap);
324 vcb->localCreateDate = SWAP_BE32(vhp->createDate);
325
326 vcb->hfsPlusIOPosOffset = embeddedOffset;
327
328 /* Default to no free block reserve */
329 vcb->reserveBlocks = 0;
330
331 /*
332 * Update the logical block size in the mount struct
333 * (currently set up from the wrapper MDB) using the
334 * new blocksize value:
335 */
336 hfsmp->hfs_logBlockSize = BestBlockSizeFit(vcb->blockSize, MAXBSIZE, hfsmp->hfs_phys_block_size);
337 vcb->vcbVBMIOSize = min(vcb->blockSize, MAXPHYSIO);
338
339 bzero(&cndesc, sizeof(cndesc));
340 cndesc.cd_parentcnid = kRootParID;
341 bzero(&cnattr, sizeof(cnattr));
342 cnattr.ca_nlink = 1;
343 cnattr.ca_mode = S_IFREG;
344
345 /*
346 * Set up Extents B-tree vnode
347 */
348 cndesc.cd_nameptr = hfs_extname;
349 cndesc.cd_namelen = strlen(hfs_extname);
350 cndesc.cd_cnid = cnattr.ca_fileid = kHFSExtentsFileID;
351
352 SWAP_HFS_PLUS_FORK_DATA (&vhp->extentsFile);
353 cnattr.ca_blocks = vhp->extentsFile.totalBlocks;
354
355 retval = hfs_getnewvnode(hfsmp, NULL, &cndesc, 0, &cnattr,
356 (struct cat_fork *)&vhp->extentsFile,
357 &vcb->extentsRefNum);
358 SWAP_HFS_PLUS_FORK_DATA (&vhp->extentsFile);
359
360 if (retval) goto ErrorExit;
361 retval = MacToVFSError(BTOpenPath(VTOF(vcb->extentsRefNum),
362 (KeyCompareProcPtr) CompareExtentKeysPlus,
363 GetBTreeBlock, ReleaseBTreeBlock,
364 ExtendBTreeFile, SetBTreeBlockSize));
365 if (retval) {
366 VOP_UNLOCK(vcb->extentsRefNum, 0, p);
367 goto ErrorExit;
368 }
369
370 /*
371 * Set up Catalog B-tree vnode
372 */
373 cndesc.cd_nameptr = hfs_catname;
374 cndesc.cd_namelen = strlen(hfs_catname);
375 cndesc.cd_cnid = cnattr.ca_fileid = kHFSCatalogFileID;
376
377 SWAP_HFS_PLUS_FORK_DATA(&vhp->catalogFile);
378 cnattr.ca_blocks = vhp->catalogFile.totalBlocks;
379
380 retval = hfs_getnewvnode(hfsmp, NULL, &cndesc, 0, &cnattr,
381 (struct cat_fork *)&vhp->catalogFile,
382 &vcb->catalogRefNum);
383 SWAP_HFS_PLUS_FORK_DATA(&vhp->catalogFile);
384 if (retval) {
385 VOP_UNLOCK(vcb->extentsRefNum, 0, p);
386 goto ErrorExit;
387 }
388 retval = MacToVFSError(BTOpenPath(VTOF(vcb->catalogRefNum),
389 (KeyCompareProcPtr) CompareExtendedCatalogKeys,
390 GetBTreeBlock, ReleaseBTreeBlock,
391 ExtendBTreeFile, SetBTreeBlockSize));
392 if (retval) {
393 VOP_UNLOCK(vcb->catalogRefNum, 0, p);
394 VOP_UNLOCK(vcb->extentsRefNum, 0, p);
395 goto ErrorExit;
396 }
397
398 /*
399 * Set up Allocation file vnode
400 */
401 cndesc.cd_nameptr = hfs_vbmname;
402 cndesc.cd_namelen = strlen(hfs_vbmname);
403 cndesc.cd_cnid = cnattr.ca_fileid = kHFSAllocationFileID;
404
405 SWAP_HFS_PLUS_FORK_DATA(&vhp->allocationFile);
406 cnattr.ca_blocks = vhp->allocationFile.totalBlocks;
407
408 retval = hfs_getnewvnode(hfsmp, NULL, &cndesc, 0, &cnattr,
409 (struct cat_fork *)&vhp->allocationFile,
410 &vcb->allocationsRefNum);
411 SWAP_HFS_PLUS_FORK_DATA(&vhp->allocationFile);
412 if (retval) {
413 VOP_UNLOCK(vcb->catalogRefNum, 0, p);
414 VOP_UNLOCK(vcb->extentsRefNum, 0, p);
415 goto ErrorExit;
416 }
417
418 /* Pick up volume name and create date */
419 retval = cat_idlookup(hfsmp, kHFSRootFolderID, &cndesc, &cnattr, NULL);
420 if (retval) {
421 VOP_UNLOCK(vcb->allocationsRefNum, 0, p);
422 VOP_UNLOCK(vcb->catalogRefNum, 0, p);
423 VOP_UNLOCK(vcb->extentsRefNum, 0, p);
424 goto ErrorExit;
425 }
426 vcb->vcbCrDate = cnattr.ca_itime;
427 vcb->volumeNameEncodingHint = cndesc.cd_encoding;
428 bcopy(cndesc.cd_nameptr, vcb->vcbVN, min(255, cndesc.cd_namelen));
429 cat_releasedesc(&cndesc);
430
431 /* mark the volume dirty (clear clean unmount bit) */
432 vcb->vcbAtrb &= ~kHFSVolumeUnmountedMask;
433 if (hfsmp->jnl && hfsmp->hfs_fs_ronly == 0) {
434 hfs_flushvolumeheader(hfsmp, TRUE, TRUE);
435 }
436
437 /*
438 * all done with metadata files so we can unlock now...
439 */
440 VOP_UNLOCK(vcb->allocationsRefNum, 0, p);
441 VOP_UNLOCK(vcb->catalogRefNum, 0, p);
442 VOP_UNLOCK(vcb->extentsRefNum, 0, p);
443
444 /* setup private/hidden directory for unlinked files */
445 hfsmp->hfs_private_metadata_dir = FindMetaDataDirectory(vcb);
446 if (hfsmp->jnl && (hfsmp->hfs_fs_ronly == 0))
447 hfs_remove_orphans(hfsmp);
448
449 if ( !(vcb->vcbAtrb & kHFSVolumeHardwareLockMask) ) // if the disk is not write protected
450 {
451 MarkVCBDirty( vcb ); // mark VCB dirty so it will be written
452 }
453
454
455 //
456 // Check if we need to do late journal initialization. This only
457 // happens if a previous version of MacOS X (or 9) touched the disk.
458 // In that case hfs_late_journal_init() will go re-locate the journal
459 // and journal_info_block files and validate that they're still kosher.
460 //
461 if ( (vcb->vcbAtrb & kHFSVolumeJournaledMask)
462 && (SWAP_BE32(vhp->lastMountedVersion) != kHFSJMountVersion)
463 && (hfsmp->jnl == NULL)) {
464
465 retval = hfs_late_journal_init(hfsmp, vhp, args);
466 if (retval != 0) {
467 hfsmp->jnl = NULL;
468 goto ErrorExit;
469 } else if (hfsmp->jnl) {
470 hfsmp->hfs_mp->mnt_flag |= MNT_JOURNALED;
471 }
472 } else if (hfsmp->jnl) {
473 struct cat_attr jinfo_attr, jnl_attr;
474
475 // if we're here we need to fill in the fileid's for the
476 // journal and journal_info_block.
477 hfsmp->hfs_jnlinfoblkid = GetFileInfo(vcb, kRootDirID, ".journal_info_block", &jinfo_attr, NULL);
478 hfsmp->hfs_jnlfileid = GetFileInfo(vcb, kRootDirID, ".journal", &jnl_attr, NULL);
479 if (hfsmp->hfs_jnlinfoblkid == 0 || hfsmp->hfs_jnlfileid == 0) {
480 printf("hfs: danger! couldn't find the file-id's for the journal or journal_info_block\n");
481 printf("hfs: jnlfileid %d, jnlinfoblkid %d\n", hfsmp->hfs_jnlfileid, hfsmp->hfs_jnlinfoblkid);
482 }
483 }
484
485
486 return (0);
487
488 ErrorExit:
489 /*
490 * A fatal error occured and the volume cannot be mounted
491 * release any resources that we aquired...
492 */
493
494 InvalidateCatalogCache(vcb);
495 ReleaseMetaFileVNode(vcb->allocationsRefNum);
496 ReleaseMetaFileVNode(vcb->catalogRefNum);
497 ReleaseMetaFileVNode(vcb->extentsRefNum);
498
499 return (retval);
500 }
501
502
503 /*
504 * ReleaseMetaFileVNode
505 *
506 * vp L - -
507 */
508 static void ReleaseMetaFileVNode(struct vnode *vp)
509 {
510 struct filefork *fp;
511
512 if (vp && (fp = VTOF(vp))) {
513 if (fp->fcbBTCBPtr != NULL)
514 (void) BTClosePath(fp);
515
516 /* release the node even if BTClosePath fails */
517 vrele(vp);
518 vgone(vp);
519 }
520 }
521
522
523 /*************************************************************
524 *
525 * Unmounts a hfs volume.
526 * At this point vflush() has been called (to dump all non-metadata files)
527 *
528 *************************************************************/
529
530 short hfsUnmount( register struct hfsmount *hfsmp, struct proc *p)
531 {
532 ExtendedVCB *vcb = HFSTOVCB(hfsmp);
533 int retval = E_NONE;
534
535 InvalidateCatalogCache( vcb );
536
537 if (vcb->vcbSigWord == kHFSPlusSigWord)
538 ReleaseMetaFileVNode(vcb->allocationsRefNum);
539
540 ReleaseMetaFileVNode(vcb->catalogRefNum);
541 ReleaseMetaFileVNode(vcb->extentsRefNum);
542
543 return (retval);
544 }
545
546
547 /*
548 * Some 3rd party kexts link against hfs_getcatalog so keep a stub for now.
549 */
550 short
551 hfs_getcatalog(void *p1, u_long p2, void *p3, short p4, void *p5)
552 {
553 return ENOENT;
554 }
555
556
557 int overflow_extents(struct filefork *fp)
558 {
559 u_long blocks;
560
561 if (VTOVCB(FTOV(fp))->vcbSigWord == kHFSPlusSigWord) {
562 if (fp->ff_extents[7].blockCount == 0)
563 return (0);
564
565 blocks = fp->ff_extents[0].blockCount +
566 fp->ff_extents[1].blockCount +
567 fp->ff_extents[2].blockCount +
568 fp->ff_extents[3].blockCount +
569 fp->ff_extents[4].blockCount +
570 fp->ff_extents[5].blockCount +
571 fp->ff_extents[6].blockCount +
572 fp->ff_extents[7].blockCount;
573 } else {
574 if (fp->ff_extents[2].blockCount == 0)
575 return false;
576
577 blocks = fp->ff_extents[0].blockCount +
578 fp->ff_extents[1].blockCount +
579 fp->ff_extents[2].blockCount;
580 }
581
582 return (fp->ff_blocks > blocks);
583 }
584
585
586 /* __private_extern__ */
587 int
588 hfs_metafilelocking(struct hfsmount *hfsmp, u_long fileID, u_int flags, struct proc *p)
589 {
590 ExtendedVCB *vcb;
591 struct vnode *vp = NULL;
592 int numOfLockedBuffs;
593 int retval = 0;
594
595 vcb = HFSTOVCB(hfsmp);
596
597 switch (fileID) {
598 case kHFSExtentsFileID:
599 vp = vcb->extentsRefNum;
600 break;
601
602 case kHFSCatalogFileID:
603 vp = vcb->catalogRefNum;
604 break;
605
606 case kHFSAllocationFileID:
607 /* bitmap is covered by Extents B-tree locking */
608 /* FALL THROUGH */
609 default:
610 panic("hfs_lockmetafile: invalid fileID");
611 }
612
613 /* Release, if necesary any locked buffer caches */
614 if ((flags & LK_TYPE_MASK) == LK_RELEASE) {
615 struct timeval tv = time;
616 u_int32_t lastfsync = tv.tv_sec;
617
618 (void) BTGetLastSync((FCB*)VTOF(vp), &lastfsync);
619
620 numOfLockedBuffs = count_lock_queue();
621 if ((numOfLockedBuffs > kMaxLockedMetaBuffers) || ((numOfLockedBuffs>1) && ((tv.tv_sec - lastfsync) > kMaxSecsForFsync))) {
622 hfs_btsync(vp, HFS_SYNCTRANS);
623 }
624 } else {
625 flags |= LK_RETRY;
626 }
627
628 retval = lockmgr(&VTOC(vp)->c_lock, flags, &vp->v_interlock, p);
629
630 return (retval);
631 }
632
633 /*
634 * RequireFileLock
635 *
636 * Check to see if a vnode is locked in the current context
637 * This is to be used for debugging purposes only!!
638 */
639 #if HFS_DIAGNOSTIC
640 void RequireFileLock(FileReference vp, int shareable)
641 {
642 struct lock__bsd__ *lkp;
643 int locked = false;
644 pid_t pid;
645 void * self;
646
647 pid = current_proc()->p_pid;
648 self = (void *) current_thread();
649 lkp = &VTOC(vp)->c_lock;
650
651 simple_lock(&lkp->lk_interlock);
652
653 if (shareable && (lkp->lk_sharecount > 0) && (lkp->lk_lockholder == LK_NOPROC))
654 locked = true;
655 else if ((lkp->lk_exclusivecount > 0) && (lkp->lk_lockholder == pid) && (lkp->lk_lockthread == self))
656 locked = true;
657
658 simple_unlock(&lkp->lk_interlock);
659
660 if (!locked) {
661 switch (VTOC(vp)->c_fileid) {
662 case 3:
663 DEBUG_BREAK_MSG((" #\n # RequireFileLock: extent btree vnode not locked! v: 0x%08X\n #\n", (u_int)vp));
664 break;
665
666 case 4:
667 DEBUG_BREAK_MSG((" #\n # RequireFileLock: catalog btree vnode not locked! v: 0x%08X\n #\n", (u_int)vp));
668 break;
669
670 default:
671 DEBUG_BREAK_MSG((" #\n # RequireFileLock: file (%d) not locked! v: 0x%08X\n #\n", VTOC(vp)->c_fileid, (u_int)vp));
672 break;
673 }
674 }
675 }
676 #endif
677
678
679 /*
680 * There are three ways to qualify for ownership rights on an object:
681 *
682 * 1. (a) Your UID matches the cnode's UID.
683 * (b) The object in question is owned by "unknown" and
684 * your UID matches the console user's UID.
685 * 2. (a) Permissions on the filesystem are being ignored and
686 * your UID matches the replacement UID.
687 * (b) Permissions on the filesystem are being ignored and
688 * the replacement UID is "unknown" and
689 * your UID matches the console user UID.
690 * 3. You are root.
691 *
692 */
693 int
694 hfs_owner_rights(struct hfsmount *hfsmp, uid_t cnode_uid, struct ucred *cred,
695 struct proc *p, int invokesuperuserstatus)
696 {
697 if ((cred->cr_uid == cnode_uid) || /* [1a] */
698 ((cnode_uid == UNKNOWNUID) && (cred->cr_uid == console_user)) || /* [1b] */
699 ((HFSTOVFS(hfsmp)->mnt_flag & MNT_UNKNOWNPERMISSIONS) && /* [2] */
700 ((cred->cr_uid == hfsmp->hfs_uid) || /* [2a] */
701 ((hfsmp->hfs_uid == UNKNOWNUID) && /* [2b] */
702 (cred->cr_uid == console_user)))) ||
703 (invokesuperuserstatus && (suser(cred, &p->p_acflag) == 0))) { /* [3] */
704 return (0);
705 } else {
706 return (EPERM);
707 }
708 }
709
710
711 unsigned long BestBlockSizeFit(unsigned long allocationBlockSize,
712 unsigned long blockSizeLimit,
713 unsigned long baseMultiple) {
714 /*
715 Compute the optimal (largest) block size (no larger than allocationBlockSize) that is less than the
716 specified limit but still an even multiple of the baseMultiple.
717 */
718 int baseBlockCount, blockCount;
719 unsigned long trialBlockSize;
720
721 if (allocationBlockSize % baseMultiple != 0) {
722 /*
723 Whoops: the allocation blocks aren't even multiples of the specified base:
724 no amount of dividing them into even parts will be a multiple, either then!
725 */
726 return 512; /* Hope for the best */
727 };
728
729 /* Try the obvious winner first, to prevent 12K allocation blocks, for instance,
730 from being handled as two 6K logical blocks instead of 3 4K logical blocks.
731 Even though the former (the result of the loop below) is the larger allocation
732 block size, the latter is more efficient: */
733 if (allocationBlockSize % PAGE_SIZE == 0) return PAGE_SIZE;
734
735 /* No clear winner exists: pick the largest even fraction <= MAXBSIZE: */
736 baseBlockCount = allocationBlockSize / baseMultiple; /* Now guaranteed to be an even multiple */
737
738 for (blockCount = baseBlockCount; blockCount > 0; --blockCount) {
739 trialBlockSize = blockCount * baseMultiple;
740 if (allocationBlockSize % trialBlockSize == 0) { /* An even multiple? */
741 if ((trialBlockSize <= blockSizeLimit) &&
742 (trialBlockSize % baseMultiple == 0)) {
743 return trialBlockSize;
744 };
745 };
746 };
747
748 /* Note: we should never get here, since blockCount = 1 should always work,
749 but this is nice and safe and makes the compiler happy, too ... */
750 return 512;
751 }
752
753
754 /*
755 * To make the HFS Plus filesystem follow UFS unlink semantics, a remove
756 * of an active vnode is translated to a move/rename so the file appears
757 * deleted. The destination folder for these move/renames is setup here
758 * and a reference to it is place in hfsmp->hfs_private_metadata_dir.
759 */
760 u_long
761 FindMetaDataDirectory(ExtendedVCB *vcb)
762 {
763 struct hfsmount * hfsmp;
764 struct vnode * dvp = NULL;
765 struct cnode * dcp = NULL;
766 struct FndrDirInfo * fndrinfo;
767 struct cat_desc out_desc = {0};
768 struct timeval tv;
769 int error;
770
771 if (vcb->vcbSigWord != kHFSPlusSigWord)
772 return (0);
773
774 hfsmp = VCBTOHFS(vcb);
775
776 if (hfsmp->hfs_privdir_desc.cd_parentcnid == 0) {
777 hfsmp->hfs_privdir_desc.cd_parentcnid = kRootDirID;
778 hfsmp->hfs_privdir_desc.cd_nameptr = hfs_privdirname;
779 hfsmp->hfs_privdir_desc.cd_namelen = strlen(hfs_privdirname);
780 hfsmp->hfs_privdir_desc.cd_flags = CD_ISDIR;
781 }
782
783 /* Lock catalog b-tree */
784 error = hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_EXCLUSIVE, current_proc());
785 if (error)
786 return (0);
787
788 error = cat_lookup(hfsmp, &hfsmp->hfs_privdir_desc, 0, NULL,
789 &hfsmp->hfs_privdir_attr, NULL);
790
791 if (error == 0) {
792 /* Unlock catalog b-tree */
793 (void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, current_proc());
794 hfsmp->hfs_metadata_createdate = hfsmp->hfs_privdir_attr.ca_itime;
795 return (hfsmp->hfs_privdir_attr.ca_fileid);
796 } else if (hfsmp->hfs_fs_ronly) {
797 /* Unlock catalog b-tree */
798 (void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, current_proc());
799 return (0);
800 }
801
802 /* Setup the default attributes */
803 bzero(&hfsmp->hfs_privdir_attr, sizeof(struct cat_attr));
804 hfsmp->hfs_privdir_attr.ca_mode = S_IFDIR;
805 hfsmp->hfs_privdir_attr.ca_flags = SF_IMMUTABLE;
806 hfsmp->hfs_privdir_attr.ca_nlink = 2;
807 hfsmp->hfs_privdir_attr.ca_itime = vcb->vcbCrDate;
808 hfsmp->hfs_privdir_attr.ca_mtime = time.tv_sec;
809
810 /* hidden and off the desktop view */
811 fndrinfo = (struct FndrDirInfo *)&hfsmp->hfs_privdir_attr.ca_finderinfo;
812 fndrinfo->frLocation.v = SWAP_BE16 (22460);
813 fndrinfo->frLocation.h = SWAP_BE16 (22460);
814 fndrinfo->frFlags |= SWAP_BE16 (kIsInvisible + kNameLocked);
815
816 // XXXdbg
817 hfs_global_shared_lock_acquire(hfsmp);
818 if (hfsmp->jnl) {
819 if ((error = journal_start_transaction(hfsmp->jnl)) != 0) {
820 hfs_global_shared_lock_release(hfsmp);
821 return (0);
822 }
823 }
824
825 error = cat_create(hfsmp, &hfsmp->hfs_privdir_desc,
826 &hfsmp->hfs_privdir_attr, &out_desc);
827
828 /* Unlock catalog b-tree */
829 (void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, current_proc());
830 if (error) {
831 if (hfsmp->jnl) {
832 journal_end_transaction(hfsmp->jnl);
833 }
834 hfs_global_shared_lock_release(hfsmp);
835
836 return (0);
837 }
838
839 hfsmp->hfs_privdir_desc.cd_hint = out_desc.cd_hint;
840 hfsmp->hfs_privdir_desc.cd_cnid = out_desc.cd_cnid;
841 hfsmp->hfs_privdir_attr.ca_fileid = out_desc.cd_cnid;
842 hfsmp->hfs_metadata_createdate = vcb->vcbCrDate;
843
844 if (VFS_ROOT(HFSTOVFS(hfsmp), &dvp) == 0) {
845 dcp = VTOC(dvp);
846 dcp->c_childhint = out_desc.cd_hint;
847 dcp->c_nlink++;
848 dcp->c_entries++;
849 dcp->c_flag |= C_CHANGE | C_UPDATE;
850 tv = time;
851 (void) VOP_UPDATE(dvp, &tv, &tv, 0);
852 vput(dvp);
853 }
854 hfs_volupdate(hfsmp, VOL_MKDIR, 1);
855 if (hfsmp->jnl) {
856 journal_end_transaction(hfsmp->jnl);
857 }
858 hfs_global_shared_lock_release(hfsmp);
859
860 cat_releasedesc(&out_desc);
861
862 return (out_desc.cd_cnid);
863 }
864
865 __private_extern__
866 u_long
867 GetFileInfo(ExtendedVCB *vcb, u_int32_t dirid, char *name,
868 struct cat_attr *fattr, struct cat_fork *forkinfo)
869 {
870 struct hfsmount * hfsmp;
871 struct vnode * dvp = NULL;
872 struct cnode * dcp = NULL;
873 struct FndrDirInfo * fndrinfo;
874 struct cat_desc jdesc;
875 struct timeval tv;
876 int error;
877
878 if (vcb->vcbSigWord != kHFSPlusSigWord)
879 return (0);
880
881 hfsmp = VCBTOHFS(vcb);
882
883 memset(&jdesc, 0, sizeof(struct cat_desc));
884 jdesc.cd_parentcnid = kRootDirID;
885 jdesc.cd_nameptr = name;
886 jdesc.cd_namelen = strlen(name);
887
888 /* Lock catalog b-tree */
889 error = hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_EXCLUSIVE, current_proc());
890 if (error)
891 return (0);
892
893 error = cat_lookup(hfsmp, &jdesc, 0, NULL, fattr, forkinfo);
894
895 (void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, current_proc());
896
897 if (error == 0) {
898 return (fattr->ca_fileid);
899 } else if (hfsmp->hfs_fs_ronly) {
900 return (0);
901 }
902 }
903
904
905 /*
906 * On Journaled HFS, there can be orphaned files. These
907 * are files that were unlinked while busy. If the volume
908 * was not cleanly unmounted then some of these files may
909 * have persisted and need to be removed.
910 */
911 __private_extern__
912 void
913 hfs_remove_orphans(struct hfsmount * hfsmp)
914 {
915 struct BTreeIterator * iterator = NULL;
916 struct FSBufferDescriptor btdata;
917 struct HFSPlusCatalogFile filerec;
918 struct HFSPlusCatalogKey * keyp;
919 FCB *fcb;
920 ExtendedVCB *vcb;
921 char filename[32];
922 char tempname[32];
923 size_t namelen;
924 int catlock = 0;
925 int result, started_tr = 0;
926
927 if (hfsmp->hfs_orphans_cleaned)
928 return;
929
930 vcb = HFSTOVCB(hfsmp);
931 fcb = VTOF(vcb->catalogRefNum);
932
933 btdata.bufferAddress = &filerec;
934 btdata.itemSize = sizeof(filerec);
935 btdata.itemCount = 1;
936
937 MALLOC(iterator, struct BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
938 bzero(iterator, sizeof(*iterator));
939 keyp = (HFSPlusCatalogKey*)&iterator->key;
940 keyp->parentID = hfsmp->hfs_private_metadata_dir;
941
942 // XXXdbg
943 hfs_global_shared_lock_acquire(hfsmp);
944 if (hfsmp->jnl) {
945 if (journal_start_transaction(hfsmp->jnl) != 0) {
946 hfs_global_shared_lock_release(hfsmp);
947 return;
948 }
949 started_tr = 1;
950 }
951
952 /* Lock catalog b-tree */
953 result = hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_EXCLUSIVE, current_proc());
954 if (result)
955 goto exit;
956 catlock = 1;
957
958 /*
959 * Position the iterator at the folder thread record.
960 * (i.e. one record before first child)
961 */
962 result = BTSearchRecord(fcb, iterator, NULL, NULL, iterator);
963 if (result)
964 goto exit;
965
966 /* Visit all the children in the HFS+ private directory. */
967 for (;;) {
968 result = BTIterateRecord(fcb, kBTreeNextRecord, iterator, &btdata, NULL);
969 if (result)
970 break;
971 if (keyp->parentID != hfsmp->hfs_private_metadata_dir)
972 break;
973 if (filerec.recordType != kHFSPlusFileRecord)
974 continue;
975
976 (void) utf8_encodestr(keyp->nodeName.unicode, keyp->nodeName.length * 2,
977 filename, &namelen, sizeof(filename), 0, 0);
978
979 (void) sprintf(tempname, "%s%d", HFS_DELETE_PREFIX, filerec.fileID);
980
981 /*
982 * Delete all files named "tempxxx", where
983 * xxx is the file's cnid in decimal.
984 *
985 * Delete all files named "iNodexxx", that
986 * have a link count of zero.
987 */
988 if (bcmp(tempname, filename, namelen) == 0) {
989 struct filefork fork = {0};
990 struct cnode cnode = {0};
991
992 // XXXdebug
993 //printf("hfs_remove_orphans: removing %s\n", filename);
994
995 /* Build a fake cnode */
996 cnode.c_desc.cd_parentcnid = hfsmp->hfs_private_metadata_dir;
997 cnode.c_desc.cd_nameptr = filename;
998 cnode.c_desc.cd_namelen = namelen;
999 cnode.c_desc.cd_cnid = filerec.fileID;
1000 cnode.c_attr.ca_fileid = filerec.fileID;
1001 cnode.c_blocks = filerec.dataFork.totalBlocks +
1002 filerec.resourceFork.totalBlocks;
1003
1004 /* Position iterator at previous entry */
1005 if (BTIterateRecord(fcb, kBTreePrevRecord, iterator,
1006 NULL, NULL) != 0)
1007 break;
1008
1009 /* Truncate the file to zero (both forks) */
1010 if (filerec.dataFork.totalBlocks > 0) {
1011 fork.ff_cp = &cnode;
1012 cnode.c_datafork = &fork;
1013 bcopy(&filerec.dataFork, &fork.ff_data, sizeof(struct cat_fork));
1014 if (TruncateFileC(vcb, (FCB*)&fork, 0, false) != 0) {
1015 printf("error truncting data fork!\n");
1016 break;
1017 }
1018 }
1019 if (filerec.resourceFork.totalBlocks > 0) {
1020 fork.ff_cp = &cnode;
1021 cnode.c_datafork = NULL;
1022 cnode.c_rsrcfork = &fork;
1023 bcopy(&filerec.resourceFork, &fork.ff_data, sizeof(struct cat_fork));
1024 if (TruncateFileC(vcb, (FCB*)&fork, 0, false) != 0) {
1025 printf("error truncting rsrc fork!\n");
1026 break;
1027 }
1028 }
1029
1030 /* Remove the file record from the Catalog */
1031 if (cat_delete(hfsmp, &cnode.c_desc, &cnode.c_attr) != 0) {
1032 printf("error deleting cat rec!\n");
1033 break;
1034 }
1035
1036 /* Update parent and volume counts */
1037 hfsmp->hfs_privdir_attr.ca_entries--;
1038 (void)cat_update(hfsmp, &hfsmp->hfs_privdir_desc,
1039 &hfsmp->hfs_privdir_attr, NULL, NULL);
1040 hfs_volupdate(hfsmp, VOL_RMFILE, 0);
1041 }
1042 }
1043
1044 exit:
1045 /* Unlock catalog b-tree */
1046 if (catlock)
1047 (void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, current_proc());
1048
1049 if (started_tr) {
1050 journal_end_transaction(hfsmp->jnl);
1051 }
1052 hfs_global_shared_lock_release(hfsmp);
1053
1054 FREE(iterator, M_TEMP);
1055 hfsmp->hfs_orphans_cleaned = 1;
1056 }
1057
1058
1059 /*
1060 * This will return the correct logical block size for a given vnode.
1061 * For most files, it is the allocation block size, for meta data like
1062 * BTrees, this is kept as part of the BTree private nodeSize
1063 */
1064 u_int32_t
1065 GetLogicalBlockSize(struct vnode *vp)
1066 {
1067 u_int32_t logBlockSize;
1068
1069 DBG_ASSERT(vp != NULL);
1070
1071 /* start with default */
1072 logBlockSize = VTOHFS(vp)->hfs_logBlockSize;
1073
1074 if (vp->v_flag & VSYSTEM) {
1075 if (VTOF(vp)->fcbBTCBPtr != NULL) {
1076 BTreeInfoRec bTreeInfo;
1077
1078 /*
1079 * We do not lock the BTrees, because if we are getting block..then the tree
1080 * should be locked in the first place.
1081 * We just want the nodeSize wich will NEVER change..so even if the world
1082 * is changing..the nodeSize should remain the same. Which argues why lock
1083 * it in the first place??
1084 */
1085
1086 (void) BTGetInformation (VTOF(vp), kBTreeInfoVersion, &bTreeInfo);
1087
1088 logBlockSize = bTreeInfo.nodeSize;
1089
1090 } else if (VTOC(vp)->c_fileid == kHFSAllocationFileID) {
1091 logBlockSize = VTOVCB(vp)->vcbVBMIOSize;
1092 }
1093 }
1094
1095 DBG_ASSERT(logBlockSize > 0);
1096
1097 return logBlockSize;
1098 }
1099
1100 __private_extern__
1101 u_int32_t
1102 hfs_freeblks(struct hfsmount * hfsmp, int wantreserve)
1103 {
1104 struct vcb_t *vcb = HFSTOVCB(hfsmp);
1105 u_int32_t freeblks;
1106
1107 freeblks = vcb->freeBlocks;
1108 if (wantreserve) {
1109 if (freeblks > vcb->reserveBlocks)
1110 freeblks -= vcb->reserveBlocks;
1111 else
1112 freeblks = 0;
1113 }
1114
1115 freeblks -= vcb->loanedBlocks;
1116 return (freeblks);
1117 }
1118
1119 /*
1120 * Map HFS Common errors (negative) to BSD error codes (positive).
1121 * Positive errors (ie BSD errors) are passed through unchanged.
1122 */
1123 short MacToVFSError(OSErr err)
1124 {
1125 if (err >= 0)
1126 return err;
1127
1128 switch (err) {
1129 case dskFulErr: /* -34 */
1130 return ENOSPC;
1131 case btNoSpaceAvail: /* -32733 */
1132 return EFBIG;
1133 case fxOvFlErr: /* -32750 */
1134 return EOVERFLOW;
1135
1136 case btBadNode: /* -32731 */
1137 return EBADF;
1138
1139 case memFullErr: /* -108 */
1140 return ENOMEM; /* +12 */
1141
1142 case cmExists: /* -32718 */
1143 case btExists: /* -32734 */
1144 return EEXIST; /* +17 */
1145
1146 case cmNotFound: /* -32719 */
1147 case btNotFound: /* -32735 */
1148 return ENOENT; /* 28 */
1149
1150 case cmNotEmpty: /* -32717 */
1151 return ENOTEMPTY; /* 66 */
1152
1153 case cmFThdDirErr: /* -32714 */
1154 return EISDIR; /* 21 */
1155
1156 case fxRangeErr: /* -32751 */
1157 return ERANGE;
1158
1159 case bdNamErr: /* -37 */
1160 return ENAMETOOLONG; /* 63 */
1161
1162 case paramErr: /* -50 */
1163 case fileBoundsErr: /* -1309 */
1164 return EINVAL; /* +22 */
1165
1166 case fsBTBadNodeSize:
1167 return ENXIO;
1168
1169 default:
1170 return EIO; /* +5 */
1171 }
1172 }
1173
1174
1175 /*
1176 * Get the directory entry name hint for a given index.
1177 * The directory cnode (dcp) must be locked.
1178 */
1179 __private_extern__
1180 char *
1181 hfs_getnamehint(struct cnode *dcp, int index)
1182 {
1183 struct hfs_index *entry;
1184 void *self;
1185
1186 if (index > 0) {
1187 self = current_thread();
1188 SLIST_FOREACH(entry, &dcp->c_indexlist, hi_link) {
1189 if ((entry->hi_index == index)
1190 && (entry->hi_thread == self))
1191 return (entry->hi_name);
1192 }
1193 }
1194
1195 return (NULL);
1196 }
1197
1198 /*
1199 * Save a directory entry name hint for a given index.
1200 * The directory cnode (dcp) must be locked.
1201 */
1202 __private_extern__
1203 void
1204 hfs_savenamehint(struct cnode *dcp, int index, const char * namehint)
1205 {
1206 struct hfs_index *entry;
1207 int len;
1208
1209 if (index > 0) {
1210 len = strlen(namehint);
1211 MALLOC(entry, struct hfs_index *, len + sizeof(struct hfs_index),
1212 M_TEMP, M_WAITOK);
1213 entry->hi_index = index;
1214 entry->hi_thread = current_thread();
1215 bcopy(namehint, entry->hi_name, len + 1);
1216 SLIST_INSERT_HEAD(&dcp->c_indexlist, entry, hi_link);
1217 }
1218 }
1219
1220 /*
1221 * Release the directory entry name hint for a given index.
1222 * The directory cnode (dcp) must be locked.
1223 */
1224 __private_extern__
1225 void
1226 hfs_relnamehint(struct cnode *dcp, int index)
1227 {
1228 struct hfs_index *entry;
1229 void *self;
1230
1231 if (index > 0) {
1232 self = current_thread();
1233 SLIST_FOREACH(entry, &dcp->c_indexlist, hi_link) {
1234 if ((entry->hi_index == index)
1235 && (entry->hi_thread == self)) {
1236 SLIST_REMOVE(&dcp->c_indexlist, entry, hfs_index,
1237 hi_link);
1238 FREE(entry, M_TEMP);
1239 break;
1240 }
1241 }
1242 }
1243 }
1244
1245 /*
1246 * Release all directory entry name hints.
1247 */
1248 __private_extern__
1249 void
1250 hfs_relnamehints(struct cnode *dcp)
1251 {
1252 struct hfs_index *entry;
1253 struct hfs_index *next;
1254
1255 if (!SLIST_EMPTY(&dcp->c_indexlist)) {
1256 for(entry = SLIST_FIRST(&dcp->c_indexlist);
1257 entry != NULL;
1258 entry = next) {
1259 next = SLIST_NEXT(entry, hi_link);
1260 SLIST_REMOVE(&dcp->c_indexlist, entry, hfs_index, hi_link);
1261 FREE(entry, M_TEMP);
1262 }
1263 }
1264 }
1265
1266
1267 /*
1268 * Perform a case-insensitive compare of two UTF-8 filenames.
1269 *
1270 * Returns 0 if the strings match.
1271 */
1272 __private_extern__
1273 int
1274 hfs_namecmp(const char *str1, size_t len1, const char *str2, size_t len2)
1275 {
1276 u_int16_t *ustr1, *ustr2;
1277 size_t ulen1, ulen2;
1278 size_t maxbytes;
1279 int cmp = -1;
1280
1281 if (len1 != len2)
1282 return (cmp);
1283
1284 maxbytes = kHFSPlusMaxFileNameChars << 1;
1285 MALLOC(ustr1, u_int16_t *, maxbytes << 1, M_TEMP, M_WAITOK);
1286 ustr2 = ustr1 + (maxbytes >> 1);
1287
1288 if (utf8_decodestr(str1, len1, ustr1, &ulen1, maxbytes, ':', 0) != 0)
1289 goto out;
1290 if (utf8_decodestr(str2, len2, ustr2, &ulen2, maxbytes, ':', 0) != 0)
1291 goto out;
1292
1293 cmp = FastUnicodeCompare(ustr1, ulen1>>1, ustr2, ulen2>>1);
1294 out:
1295 FREE(ustr1, M_TEMP);
1296 return (cmp);
1297 }
1298
1299
1300 __private_extern__
1301 int
1302 hfs_early_journal_init(struct hfsmount *hfsmp, HFSPlusVolumeHeader *vhp,
1303 void *_args, int embeddedOffset, int mdb_offset,
1304 HFSMasterDirectoryBlock *mdbp, struct ucred *cred)
1305 {
1306 JournalInfoBlock *jibp;
1307 struct buf *jinfo_bp, *bp;
1308 int sectors_per_fsblock, arg_flags=0, arg_tbufsz=0;
1309 int retval, blksize = hfsmp->hfs_phys_block_size;
1310 struct vnode *devvp;
1311 struct hfs_mount_args *args = _args;
1312
1313 devvp = hfsmp->hfs_devvp;
1314
1315 if (args != NULL && (args->flags & HFSFSMNT_EXTENDED_ARGS)) {
1316 arg_flags = args->journal_flags;
1317 arg_tbufsz = args->journal_tbuffer_size;
1318 }
1319
1320 sectors_per_fsblock = SWAP_BE32(vhp->blockSize) / blksize;
1321
1322 retval = meta_bread(devvp,
1323 embeddedOffset/blksize +
1324 (SWAP_BE32(vhp->journalInfoBlock)*sectors_per_fsblock),
1325 SWAP_BE32(vhp->blockSize), cred, &jinfo_bp);
1326 if (retval)
1327 return retval;
1328
1329 jibp = (JournalInfoBlock *)jinfo_bp->b_data;
1330 jibp->flags = SWAP_BE32(jibp->flags);
1331 jibp->offset = SWAP_BE64(jibp->offset);
1332 jibp->size = SWAP_BE64(jibp->size);
1333
1334 if (jibp->flags & kJIJournalInFSMask) {
1335 hfsmp->jvp = hfsmp->hfs_devvp;
1336 } else {
1337 printf("hfs: journal not stored in fs! don't know what to do.\n");
1338 brelse(jinfo_bp);
1339 return EINVAL;
1340 }
1341
1342 // save this off for the hack-y check in hfs_remove()
1343 hfsmp->jnl_start = jibp->offset / SWAP_BE32(vhp->blockSize);
1344
1345 if (jibp->flags & kJIJournalNeedInitMask) {
1346 printf("hfs: Initializing the journal (joffset 0x%llx sz 0x%llx)...\n",
1347 jibp->offset + (off_t)embeddedOffset, jibp->size);
1348 hfsmp->jnl = journal_create(hfsmp->jvp,
1349 jibp->offset + (off_t)embeddedOffset,
1350 jibp->size,
1351 devvp,
1352 blksize,
1353 arg_flags,
1354 arg_tbufsz,
1355 hfs_sync_metadata, hfsmp->hfs_mp);
1356
1357 // no need to start a transaction here... if this were to fail
1358 // we'd just re-init it on the next mount.
1359 jibp->flags &= ~kJIJournalNeedInitMask;
1360 jibp->flags = SWAP_BE32(jibp->flags);
1361 bwrite(jinfo_bp);
1362 jinfo_bp = NULL;
1363 jibp = NULL;
1364 } else {
1365 //printf("hfs: Opening the journal (joffset 0x%llx sz 0x%llx vhp_blksize %d)...\n",
1366 // jibp->offset + (off_t)embeddedOffset,
1367 // jibp->size, SWAP_BE32(vhp->blockSize));
1368
1369 hfsmp->jnl = journal_open(hfsmp->jvp,
1370 jibp->offset + (off_t)embeddedOffset,
1371 jibp->size,
1372 devvp,
1373 blksize,
1374 arg_flags,
1375 arg_tbufsz,
1376 hfs_sync_metadata, hfsmp->hfs_mp);
1377
1378 brelse(jinfo_bp);
1379 jinfo_bp = NULL;
1380 jibp = NULL;
1381
1382 if (hfsmp->jnl && mdbp) {
1383 // reload the mdb because it could have changed
1384 // if the journal had to be replayed.
1385 retval = meta_bread(devvp, mdb_offset, blksize, cred, &bp);
1386 if (retval) {
1387 brelse(bp);
1388 printf("hfs: failed to reload the mdb after opening the journal (retval %d)!\n",
1389 retval);
1390 return retval;
1391 }
1392 bcopy(bp->b_data + HFS_PRI_OFFSET(blksize), mdbp, 512);
1393 brelse(bp);
1394 bp = NULL;
1395 }
1396 }
1397
1398
1399 //printf("journal @ 0x%x\n", hfsmp->jnl);
1400
1401 // if we expected the journal to be there and we couldn't
1402 // create it or open it then we have to bail out.
1403 if (hfsmp->jnl == NULL) {
1404 hfsmp->jnl_start = 0;
1405
1406 printf("hfs: failed to open/create the journal (retval %d).\n", retval);
1407 return EINVAL;
1408 }
1409
1410 return 0;
1411 }
1412
1413
1414 //
1415 // This function will go and re-locate the .journal_info_block and
1416 // the .journal files in case they moved (which can happen if you
1417 // run Norton SpeedDisk). If we fail to find either file we just
1418 // disable journaling for this volume and return. We turn off the
1419 // journaling bit in the vcb and assume it will get written to disk
1420 // later (if it doesn't on the next mount we'd do the same thing
1421 // again which is harmless). If we disable journaling we don't
1422 // return an error so that the volume is still mountable.
1423 //
1424 // If the info we find for the .journal_info_block and .journal files
1425 // isn't what we had stored, we re-set our cached info and proceed
1426 // with opening the journal normally.
1427 //
1428 static int
1429 hfs_late_journal_init(struct hfsmount *hfsmp, HFSPlusVolumeHeader *vhp, void *_args)
1430 {
1431 JournalInfoBlock *jibp;
1432 struct buf *jinfo_bp, *bp;
1433 int sectors_per_fsblock, arg_flags=0, arg_tbufsz=0;
1434 int retval, need_flush = 0, write_jibp = 0;
1435 struct vnode *devvp;
1436 struct cat_attr jib_attr, jattr;
1437 struct cat_fork jib_fork, jfork;
1438 ExtendedVCB *vcb;
1439 u_long fid;
1440 struct hfs_mount_args *args = _args;
1441
1442 devvp = hfsmp->hfs_devvp;
1443 vcb = HFSTOVCB(hfsmp);
1444
1445 if (args != NULL && (args->flags & HFSFSMNT_EXTENDED_ARGS)) {
1446 if (args->journal_disable) {
1447 return 0;
1448 }
1449
1450 arg_flags = args->journal_flags;
1451 arg_tbufsz = args->journal_tbuffer_size;
1452 }
1453
1454 fid = GetFileInfo(vcb, kRootDirID, ".journal_info_block", &jib_attr, &jib_fork);
1455 if (fid == 0 || jib_fork.cf_extents[0].startBlock == 0 || jib_fork.cf_size == 0) {
1456 printf("hfs: can't find the .journal_info_block! disabling journaling (start: %d).\n",
1457 jib_fork.cf_extents[0].startBlock);
1458 vcb->vcbAtrb &= ~kHFSVolumeJournaledMask;
1459 return 0;
1460 }
1461 hfsmp->hfs_jnlinfoblkid = fid;
1462
1463 // make sure the journal_info_block begins where we think it should.
1464 if (SWAP_BE32(vhp->journalInfoBlock) != jib_fork.cf_extents[0].startBlock) {
1465 printf("hfs: The journal_info_block moved (was: %d; is: %d). Fixing up\n",
1466 SWAP_BE32(vhp->journalInfoBlock), jib_fork.cf_extents[0].startBlock);
1467
1468 vcb->vcbJinfoBlock = jib_fork.cf_extents[0].startBlock;
1469 vhp->journalInfoBlock = SWAP_BE32(jib_fork.cf_extents[0].startBlock);
1470 }
1471
1472
1473 sectors_per_fsblock = SWAP_BE32(vhp->blockSize) / hfsmp->hfs_phys_block_size;
1474 retval = meta_bread(devvp,
1475 vcb->hfsPlusIOPosOffset / hfsmp->hfs_phys_block_size +
1476 (SWAP_BE32(vhp->journalInfoBlock)*sectors_per_fsblock),
1477 SWAP_BE32(vhp->blockSize), NOCRED, &jinfo_bp);
1478 if (retval) {
1479 printf("hfs: can't read journal info block. disabling journaling.\n");
1480 vcb->vcbAtrb &= ~kHFSVolumeJournaledMask;
1481 return 0;
1482 }
1483
1484 jibp = (JournalInfoBlock *)jinfo_bp->b_data;
1485 jibp->flags = SWAP_BE32(jibp->flags);
1486 jibp->offset = SWAP_BE64(jibp->offset);
1487 jibp->size = SWAP_BE64(jibp->size);
1488
1489 fid = GetFileInfo(vcb, kRootDirID, ".journal", &jattr, &jfork);
1490 if (fid == 0 || jfork.cf_extents[0].startBlock == 0 || jfork.cf_size == 0) {
1491 printf("hfs: can't find the journal file! disabling journaling (start: %d)\n",
1492 jfork.cf_extents[0].startBlock);
1493 brelse(jinfo_bp);
1494 vcb->vcbAtrb &= ~kHFSVolumeJournaledMask;
1495 return 0;
1496 }
1497 hfsmp->hfs_jnlfileid = fid;
1498
1499 // make sure the journal file begins where we think it should.
1500 if ((jibp->offset / (u_int64_t)vcb->blockSize) != jfork.cf_extents[0].startBlock) {
1501 printf("hfs: The journal file moved (was: %lld; is: %d). Fixing up\n",
1502 (jibp->offset / (u_int64_t)vcb->blockSize), jfork.cf_extents[0].startBlock);
1503
1504 jibp->offset = (u_int64_t)jfork.cf_extents[0].startBlock * (u_int64_t)vcb->blockSize;
1505 write_jibp = 1;
1506 }
1507
1508 // check the size of the journal file.
1509 if (jibp->size != (u_int64_t)jfork.cf_extents[0].blockCount*vcb->blockSize) {
1510 printf("hfs: The journal file changed size! (was %lld; is %lld). Fixing up.\n",
1511 jibp->size, (u_int64_t)jfork.cf_extents[0].blockCount*vcb->blockSize);
1512
1513 jibp->size = (u_int64_t)jfork.cf_extents[0].blockCount * vcb->blockSize;
1514 write_jibp = 1;
1515 }
1516
1517 if (jibp->flags & kJIJournalInFSMask) {
1518 hfsmp->jvp = hfsmp->hfs_devvp;
1519 } else {
1520 printf("hfs: journal not stored in fs! don't know what to do.\n");
1521 brelse(jinfo_bp);
1522 return EINVAL;
1523 }
1524
1525 // save this off for the hack-y check in hfs_remove()
1526 hfsmp->jnl_start = jibp->offset / SWAP_BE32(vhp->blockSize);
1527
1528 if (jibp->flags & kJIJournalNeedInitMask) {
1529 printf("hfs: Initializing the journal (joffset 0x%llx sz 0x%llx)...\n",
1530 jibp->offset + (off_t)vcb->hfsPlusIOPosOffset, jibp->size);
1531 hfsmp->jnl = journal_create(hfsmp->jvp,
1532 jibp->offset + (off_t)vcb->hfsPlusIOPosOffset,
1533 jibp->size,
1534 devvp,
1535 hfsmp->hfs_phys_block_size,
1536 arg_flags,
1537 arg_tbufsz,
1538 hfs_sync_metadata, hfsmp->hfs_mp);
1539
1540 // no need to start a transaction here... if this were to fail
1541 // we'd just re-init it on the next mount.
1542 jibp->flags &= ~kJIJournalNeedInitMask;
1543 write_jibp = 1;
1544
1545 } else {
1546 //
1547 // if we weren't the last person to mount this volume
1548 // then we need to throw away the journal because it
1549 // is likely that someone else mucked with the disk.
1550 // if the journal is empty this is no big deal. if the
1551 // disk is dirty this prevents us from replaying the
1552 // journal over top of changes that someone else made.
1553 //
1554 arg_flags |= JOURNAL_RESET;
1555
1556 //printf("hfs: Opening the journal (joffset 0x%llx sz 0x%llx vhp_blksize %d)...\n",
1557 // jibp->offset + (off_t)vcb->hfsPlusIOPosOffset,
1558 // jibp->size, SWAP_BE32(vhp->blockSize));
1559
1560 hfsmp->jnl = journal_open(hfsmp->jvp,
1561 jibp->offset + (off_t)vcb->hfsPlusIOPosOffset,
1562 jibp->size,
1563 devvp,
1564 hfsmp->hfs_phys_block_size,
1565 arg_flags,
1566 arg_tbufsz,
1567 hfs_sync_metadata, hfsmp->hfs_mp);
1568 }
1569
1570
1571 if (write_jibp) {
1572 jibp->flags = SWAP_BE32(jibp->flags);
1573 jibp->offset = SWAP_BE64(jibp->offset);
1574 jibp->size = SWAP_BE64(jibp->size);
1575
1576 bwrite(jinfo_bp);
1577 } else {
1578 brelse(jinfo_bp);
1579 }
1580 jinfo_bp = NULL;
1581 jibp = NULL;
1582
1583 //printf("journal @ 0x%x\n", hfsmp->jnl);
1584
1585 // if we expected the journal to be there and we couldn't
1586 // create it or open it then we have to bail out.
1587 if (hfsmp->jnl == NULL) {
1588 hfsmp->jnl_start = 0;
1589
1590 printf("hfs: failed to open/create the journal (retval %d).\n", retval);
1591 return EINVAL;
1592 }
1593
1594 return 0;
1595 }