]> git.saurik.com Git - apple/xnu.git/blame - bsd/hfs/hfs_vfsutils.c
xnu-344.tar.gz
[apple/xnu.git] / bsd / hfs / hfs_vfsutils.c
CommitLineData
1c79356b 1/*
9bccf70c 2 * Copyright (c) 2000-2002 Apple Computer, Inc. All rights reserved.
1c79356b
A
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
11 *
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22/* @(#)hfs_vfsutils.c 4.0
23*
9bccf70c 24* (c) 1997-2002 Apple Computer, Inc. All Rights Reserved
1c79356b
A
25*
26* hfs_vfsutils.c -- Routines that go between the HFS layer and the VFS.
27*
1c79356b
A
28*/
29#include <sys/param.h>
30#include <sys/systm.h>
31#include <sys/kernel.h>
32#include <sys/malloc.h>
33#include <sys/stat.h>
1c79356b 34#include <sys/mount.h>
9bccf70c 35#include <sys/namei.h>
1c79356b
A
36#include <sys/lock.h>
37#include <sys/buf.h>
38#include <sys/ubc.h>
39#include <sys/unistd.h>
40
41#include "hfs.h"
9bccf70c 42#include "hfs_catalog.h"
1c79356b
A
43#include "hfs_dbg.h"
44#include "hfs_mount.h"
45#include "hfs_endian.h"
9bccf70c 46#include "hfs_cnode.h"
1c79356b
A
47
48#include "hfscommon/headers/FileMgrInternal.h"
49#include "hfscommon/headers/BTreesInternal.h"
50#include "hfscommon/headers/HFSUnicodeWrappers.h"
51
1c79356b 52
1c79356b
A
53extern int count_lock_queue __P((void));
54extern uid_t console_user;
55
1c79356b
A
56
57static void ReleaseMetaFileVNode(struct vnode *vp);
58
1c79356b
A
59u_int32_t GetLogicalBlockSize(struct vnode *vp);
60
61/* BTree accessor routines */
62extern OSStatus GetBTreeBlock(FileReference vp, UInt32 blockNum, GetBlockOptions options, BlockDescriptor *block);
63extern OSStatus SetBTreeBlockSize(FileReference vp, ByteCount blockSize, ItemCount minBlockCount);
64extern OSStatus ExtendBTreeFile(FileReference vp, FSSize minEOF, FSSize maxEOF);
65extern OSStatus ReleaseBTreeBlock(FileReference vp, BlockDescPtr blockPtr, ReleaseBlockOptions options);
66
67//*******************************************************************************
68// Note: Finder information in the HFS/HFS+ metadata are considered opaque and
69// hence are not in the right byte order on little endian machines. It is
70// the responsibility of the finder and other clients to swap the data.
71//*******************************************************************************
72
73//*******************************************************************************
74// Routine: hfs_MountHFSVolume
75//
76//
77//*******************************************************************************
9bccf70c
A
78char hfs_catname[] = "Catalog B-tree";
79char hfs_extname[] = "Extents B-tree";
80char hfs_vbmname[] = "Volume Bitmap";
81
82char hfs_privdirname[] =
83 "\xE2\x90\x80\xE2\x90\x80\xE2\x90\x80\xE2\x90\x80HFS+ Private Data";
1c79356b
A
84
85OSErr hfs_MountHFSVolume(struct hfsmount *hfsmp, HFSMasterDirectoryBlock *mdb,
d52fe63f 86 struct proc *p)
1c79356b 87{
9bccf70c
A
88 ExtendedVCB *vcb = HFSTOVCB(hfsmp);
89 int error;
1c79356b 90 ByteCount utf8chars;
9bccf70c
A
91 struct cat_desc cndesc;
92 struct cat_attr cnattr;
93 struct cat_fork fork;
1c79356b 94
9bccf70c
A
95 /* Block size must be a multiple of 512 */
96 if (SWAP_BE32(mdb->drAlBlkSiz) == 0 ||
97 (SWAP_BE32(mdb->drAlBlkSiz) & 0x01FF) != 0)
1c79356b
A
98 return (EINVAL);
99
1c79356b 100 /* don't mount a writeable volume if its dirty, it must be cleaned by fsck_hfs */
9bccf70c 101 if ((hfsmp->hfs_fs_ronly == 0) && ((SWAP_BE16(mdb->drAtrb) & kHFSVolumeUnmountedMask) == 0))
1c79356b
A
102 return (EINVAL);
103
104 /*
105 * The MDB seems OK: transfer info from it into VCB
106 * Note - the VCB starts out clear (all zeros)
107 *
108 */
9bccf70c
A
109 vcb->vcbSigWord = SWAP_BE16 (mdb->drSigWord);
110 vcb->vcbCrDate = to_bsd_time(LocalToUTC(SWAP_BE32(mdb->drCrDate)));
0b4e3aa0 111 vcb->localCreateDate = SWAP_BE32 (mdb->drCrDate);
9bccf70c
A
112 vcb->vcbLsMod = to_bsd_time(LocalToUTC(SWAP_BE32(mdb->drLsMod)));
113 vcb->vcbAtrb = SWAP_BE16 (mdb->drAtrb);
114 vcb->vcbNmFls = SWAP_BE16 (mdb->drNmFls);
115 vcb->vcbVBMSt = SWAP_BE16 (mdb->drVBMSt);
116 vcb->nextAllocation = SWAP_BE16 (mdb->drAllocPtr);
117 vcb->totalBlocks = SWAP_BE16 (mdb->drNmAlBlks);
118 vcb->blockSize = SWAP_BE32 (mdb->drAlBlkSiz);
119 vcb->vcbClpSiz = SWAP_BE32 (mdb->drClpSiz);
120 vcb->vcbAlBlSt = SWAP_BE16 (mdb->drAlBlSt);
121 vcb->vcbNxtCNID = SWAP_BE32 (mdb->drNxtCNID);
122 vcb->freeBlocks = SWAP_BE16 (mdb->drFreeBks);
123 vcb->vcbVolBkUp = to_bsd_time(LocalToUTC(SWAP_BE32(mdb->drVolBkUp)));
124 vcb->vcbWrCnt = SWAP_BE32 (mdb->drWrCnt);
125 vcb->vcbNmRtDirs = SWAP_BE16 (mdb->drNmRtDirs);
126 vcb->vcbFilCnt = SWAP_BE32 (mdb->drFilCnt);
127 vcb->vcbDirCnt = SWAP_BE32 (mdb->drDirCnt);
1c79356b 128 bcopy(mdb->drFndrInfo, vcb->vcbFndrInfo, sizeof(vcb->vcbFndrInfo));
9bccf70c
A
129 if (!hfsmp->hfs_fs_ronly)
130 vcb->vcbWrCnt++; /* Compensate for write of MDB on last flush */
1c79356b
A
131
132 /* convert hfs encoded name into UTF-8 string */
9bccf70c 133 error = hfs_to_utf8(vcb, mdb->drVN, NAME_MAX, &utf8chars, vcb->vcbVN);
1c79356b
A
134 /*
135 * When an HFS name cannot be encoded with the current
136 * volume encoding we use MacRoman as a fallback.
137 */
9bccf70c 138 if (error || (utf8chars == 0))
1c79356b
A
139 (void) mac_roman_to_utf8(mdb->drVN, NAME_MAX, &utf8chars, vcb->vcbVN);
140
9bccf70c 141 hfsmp->hfs_logBlockSize = BestBlockSizeFit(vcb->blockSize, MAXBSIZE, hfsmp->hfs_phys_block_size);
0b4e3aa0 142 vcb->vcbVBMIOSize = kHFSBlockSize;
1c79356b 143
1c79356b
A
144 VCB_LOCK_INIT(vcb);
145
9bccf70c
A
146 bzero(&cndesc, sizeof(cndesc));
147 cndesc.cd_parentcnid = kRootParID;
148 bzero(&cnattr, sizeof(cnattr));
149 cnattr.ca_nlink = 1;
150 cnattr.ca_mode = S_IFREG;
151 bzero(&fork, sizeof(fork));
152
1c79356b 153 /*
9bccf70c
A
154 * Set up Extents B-tree vnode
155 */
156 cndesc.cd_nameptr = hfs_extname;
157 cndesc.cd_namelen = strlen(hfs_extname);
158 cndesc.cd_cnid = cnattr.ca_fileid = kHFSExtentsFileID;
159 fork.cf_size = SWAP_BE32(mdb->drXTFlSize);
160 fork.cf_blocks = fork.cf_size / vcb->blockSize;
161 fork.cf_clump = SWAP_BE32(mdb->drXTClpSiz);
162 fork.cf_extents[0].startBlock = SWAP_BE16(mdb->drXTExtRec[0].startBlock);
163 fork.cf_extents[0].blockCount = SWAP_BE16(mdb->drXTExtRec[0].blockCount);
164 fork.cf_extents[1].startBlock = SWAP_BE16(mdb->drXTExtRec[1].startBlock);
165 fork.cf_extents[1].blockCount = SWAP_BE16(mdb->drXTExtRec[1].blockCount);
166 fork.cf_extents[2].startBlock = SWAP_BE16(mdb->drXTExtRec[2].startBlock);
167 fork.cf_extents[2].blockCount = SWAP_BE16(mdb->drXTExtRec[2].blockCount);
168 cnattr.ca_blocks = fork.cf_blocks;
169
170 error = hfs_getnewvnode(hfsmp, NULL, &cndesc, 0, &cnattr, &fork,
171 &vcb->extentsRefNum);
172 if (error) goto MtVolErr;
173 error = MacToVFSError(BTOpenPath(VTOF(vcb->extentsRefNum),
174 (KeyCompareProcPtr)CompareExtentKeys,
175 GetBTreeBlock, ReleaseBTreeBlock,
176 ExtendBTreeFile, SetBTreeBlockSize));
177 if (error) {
178 VOP_UNLOCK(vcb->extentsRefNum, 0, p);
179 goto MtVolErr;
180 }
1c79356b
A
181
182 /*
183 * Set up Catalog B-tree vnode...
184 */
9bccf70c
A
185 cndesc.cd_nameptr = hfs_catname;
186 cndesc.cd_namelen = strlen(hfs_catname);
187 cndesc.cd_cnid = cnattr.ca_fileid = kHFSCatalogFileID;
188 fork.cf_size = SWAP_BE32(mdb->drCTFlSize);
189 fork.cf_blocks = fork.cf_size / vcb->blockSize;
190 fork.cf_clump = SWAP_BE32(mdb->drCTClpSiz);
191 fork.cf_extents[0].startBlock = SWAP_BE16(mdb->drCTExtRec[0].startBlock);
192 fork.cf_extents[0].blockCount = SWAP_BE16(mdb->drCTExtRec[0].blockCount);
193 fork.cf_extents[1].startBlock = SWAP_BE16(mdb->drCTExtRec[1].startBlock);
194 fork.cf_extents[1].blockCount = SWAP_BE16(mdb->drCTExtRec[1].blockCount);
195 fork.cf_extents[2].startBlock = SWAP_BE16(mdb->drCTExtRec[2].startBlock);
196 fork.cf_extents[2].blockCount = SWAP_BE16(mdb->drCTExtRec[2].blockCount);
197 cnattr.ca_blocks = fork.cf_blocks;
198
199 error = hfs_getnewvnode(hfsmp, NULL, &cndesc, 0, &cnattr, &fork,
200 &vcb->catalogRefNum);
201 if (error) {
202 VOP_UNLOCK(vcb->extentsRefNum, 0, p);
203 goto MtVolErr;
204 }
205 error = MacToVFSError(BTOpenPath(VTOF(vcb->catalogRefNum),
206 (KeyCompareProcPtr)CompareCatalogKeys,
207 GetBTreeBlock, ReleaseBTreeBlock,
208 ExtendBTreeFile, SetBTreeBlockSize));
209 if (error) {
210 VOP_UNLOCK(vcb->catalogRefNum, 0, p);
211 VOP_UNLOCK(vcb->extentsRefNum, 0, p);
212 goto MtVolErr;
213 }
1c79356b
A
214
215 /* mark the volume dirty (clear clean unmount bit) */
216 vcb->vcbAtrb &= ~kHFSVolumeUnmountedMask;
217
1c79356b
A
218 /*
219 * all done with b-trees so we can unlock now...
220 */
9bccf70c
A
221 VOP_UNLOCK(vcb->catalogRefNum, 0, p);
222 VOP_UNLOCK(vcb->extentsRefNum, 0, p);
1c79356b 223
9bccf70c 224 if ( error == noErr )
1c79356b
A
225 {
226 if ( !(vcb->vcbAtrb & kHFSVolumeHardwareLockMask) ) // if the disk is not write protected
227 {
228 MarkVCBDirty( vcb ); // mark VCB dirty so it will be written
229 }
230 }
231 goto CmdDone;
232
233 //-- Release any resources allocated so far before exiting with an error:
9bccf70c 234MtVolErr:
1c79356b
A
235 ReleaseMetaFileVNode(vcb->catalogRefNum);
236 ReleaseMetaFileVNode(vcb->extentsRefNum);
237
9bccf70c
A
238CmdDone:
239 return (error);
1c79356b
A
240}
241
242//*******************************************************************************
243// Routine: hfs_MountHFSPlusVolume
244//
245//
246//*******************************************************************************
247
248OSErr hfs_MountHFSPlusVolume(struct hfsmount *hfsmp, HFSPlusVolumeHeader *vhp,
9bccf70c 249 off_t embeddedOffset, u_int64_t disksize, struct proc *p)
1c79356b 250{
9bccf70c
A
251 register ExtendedVCB *vcb;
252 struct cat_desc cndesc;
253 struct cat_attr cnattr;
254 UInt32 blockSize;
255 OSErr retval;
1c79356b 256
9bccf70c
A
257 if (SWAP_BE16(vhp->signature) != kHFSPlusSigWord ||
258 SWAP_BE16(vhp->version) != kHFSPlusVersion)
259 return (EINVAL);
1c79356b 260
9bccf70c
A
261 /* Block size must be at least 512 and a power of 2 */
262 blockSize = SWAP_BE32(vhp->blockSize);
263 if (blockSize < 512 || (blockSize & (blockSize-1)) != 0)
264 return (EINVAL);
1c79356b
A
265
266 /* don't mount a writable volume if its dirty, it must be cleaned by fsck_hfs */
9bccf70c 267 if (hfsmp->hfs_fs_ronly == 0 && (SWAP_BE32(vhp->attributes) & kHFSVolumeUnmountedMask) == 0)
1c79356b 268 return (EINVAL);
d52fe63f
A
269
270 /* Make sure we can live with the physical block size. */
271 if ((disksize & (hfsmp->hfs_phys_block_size - 1)) ||
272 (embeddedOffset & (hfsmp->hfs_phys_block_size - 1)) ||
273 (SWAP_BE32(vhp->blockSize) < hfsmp->hfs_phys_block_size)) {
274 return (ENXIO);
275 }
1c79356b
A
276 /*
277 * The VolumeHeader seems OK: transfer info from it into VCB
278 * Note - the VCB starts out clear (all zeros)
279 */
280 vcb = HFSTOVCB(hfsmp);
281
9bccf70c
A
282 vcb->vcbSigWord = SWAP_BE16(vhp->signature);
283 vcb->vcbLsMod = to_bsd_time(SWAP_BE32(vhp->modifyDate));
284 vcb->vcbAtrb = (UInt16)SWAP_BE32(vhp->attributes);
285 vcb->vcbClpSiz = SWAP_BE32(vhp->rsrcClumpSize);
286 vcb->vcbNxtCNID = SWAP_BE32(vhp->nextCatalogID);
287 vcb->vcbVolBkUp = to_bsd_time(SWAP_BE32(vhp->backupDate));
288 vcb->vcbWrCnt = SWAP_BE32(vhp->writeCount);
289 vcb->vcbFilCnt = SWAP_BE32(vhp->fileCount);
290 vcb->vcbDirCnt = SWAP_BE32(vhp->folderCount);
1c79356b
A
291
292 /* copy 32 bytes of Finder info */
293 bcopy(vhp->finderInfo, vcb->vcbFndrInfo, sizeof(vhp->finderInfo));
294
295 vcb->vcbAlBlSt = 0; /* hfs+ allocation blocks start at first block of volume */
9bccf70c
A
296 if (!hfsmp->hfs_fs_ronly)
297 vcb->vcbWrCnt++; /* compensate for write of Volume Header on last flush */
1c79356b
A
298
299 VCB_LOCK_INIT(vcb);
300
9bccf70c
A
301 /* Now fill in the Extended VCB info */
302 vcb->nextAllocation = SWAP_BE32(vhp->nextAllocation);
303 vcb->totalBlocks = SWAP_BE32(vhp->totalBlocks);
304 vcb->freeBlocks = SWAP_BE32(vhp->freeBlocks);
305 vcb->blockSize = SWAP_BE32(vhp->blockSize);
306 vcb->encodingsBitmap = SWAP_BE64(vhp->encodingsBitmap);
307 vcb->localCreateDate = SWAP_BE32(vhp->createDate);
1c79356b 308
9bccf70c 309 vcb->hfsPlusIOPosOffset = embeddedOffset;
1c79356b 310
9bccf70c
A
311 /* Default to no free block reserve */
312 vcb->reserveBlocks = 0;
1c79356b 313
9bccf70c
A
314 /*
315 * Update the logical block size in the mount struct
316 * (currently set up from the wrapper MDB) using the
317 * new blocksize value:
318 */
319 hfsmp->hfs_logBlockSize = BestBlockSizeFit(vcb->blockSize, MAXBSIZE, hfsmp->hfs_phys_block_size);
0b4e3aa0 320 vcb->vcbVBMIOSize = min(vcb->blockSize, MAXPHYSIO);
1c79356b 321
9bccf70c
A
322 bzero(&cndesc, sizeof(cndesc));
323 cndesc.cd_parentcnid = kRootParID;
324 bzero(&cnattr, sizeof(cnattr));
325 cnattr.ca_nlink = 1;
326 cnattr.ca_mode = S_IFREG;
1c79356b
A
327
328 /*
9bccf70c
A
329 * Set up Extents B-tree vnode
330 */
331 cndesc.cd_nameptr = hfs_extname;
332 cndesc.cd_namelen = strlen(hfs_extname);
333 cndesc.cd_cnid = cnattr.ca_fileid = kHFSExtentsFileID;
334
335 SWAP_HFS_PLUS_FORK_DATA (&vhp->extentsFile);
336 cnattr.ca_blocks = vhp->extentsFile.totalBlocks;
337
338 retval = hfs_getnewvnode(hfsmp, NULL, &cndesc, 0, &cnattr,
339 (struct cat_fork *)&vhp->extentsFile,
340 &vcb->extentsRefNum);
341 SWAP_HFS_PLUS_FORK_DATA (&vhp->extentsFile);
342
1c79356b 343 if (retval) goto ErrorExit;
9bccf70c
A
344 retval = MacToVFSError(BTOpenPath(VTOF(vcb->extentsRefNum),
345 (KeyCompareProcPtr) CompareExtentKeysPlus,
346 GetBTreeBlock, ReleaseBTreeBlock,
347 ExtendBTreeFile, SetBTreeBlockSize));
348 if (retval) {
349 VOP_UNLOCK(vcb->extentsRefNum, 0, p);
350 goto ErrorExit;
351 }
1c79356b
A
352
353 /*
9bccf70c 354 * Set up Catalog B-tree vnode
1c79356b 355 */
9bccf70c
A
356 cndesc.cd_nameptr = hfs_catname;
357 cndesc.cd_namelen = strlen(hfs_catname);
358 cndesc.cd_cnid = cnattr.ca_fileid = kHFSCatalogFileID;
359
360 SWAP_HFS_PLUS_FORK_DATA(&vhp->catalogFile);
361 cnattr.ca_blocks = vhp->catalogFile.totalBlocks;
362
363 retval = hfs_getnewvnode(hfsmp, NULL, &cndesc, 0, &cnattr,
364 (struct cat_fork *)&vhp->catalogFile,
365 &vcb->catalogRefNum);
366 SWAP_HFS_PLUS_FORK_DATA(&vhp->catalogFile);
367 if (retval) {
368 VOP_UNLOCK(vcb->extentsRefNum, 0, p);
369 goto ErrorExit;
370 }
371 retval = MacToVFSError(BTOpenPath(VTOF(vcb->catalogRefNum),
372 (KeyCompareProcPtr) CompareExtendedCatalogKeys,
373 GetBTreeBlock, ReleaseBTreeBlock,
374 ExtendBTreeFile, SetBTreeBlockSize));
375 if (retval) {
376 VOP_UNLOCK(vcb->catalogRefNum, 0, p);
377 VOP_UNLOCK(vcb->extentsRefNum, 0, p);
378 goto ErrorExit;
379 }
1c79356b
A
380
381 /*
9bccf70c 382 * Set up Allocation file vnode
1c79356b 383 */
9bccf70c
A
384 cndesc.cd_nameptr = hfs_vbmname;
385 cndesc.cd_namelen = strlen(hfs_vbmname);
386 cndesc.cd_cnid = cnattr.ca_fileid = kHFSAllocationFileID;
387
388 SWAP_HFS_PLUS_FORK_DATA(&vhp->allocationFile);
389 cnattr.ca_blocks = vhp->allocationFile.totalBlocks;
390
391 retval = hfs_getnewvnode(hfsmp, NULL, &cndesc, 0, &cnattr,
392 (struct cat_fork *)&vhp->allocationFile,
393 &vcb->allocationsRefNum);
394 SWAP_HFS_PLUS_FORK_DATA(&vhp->allocationFile);
395 if (retval) {
396 VOP_UNLOCK(vcb->catalogRefNum, 0, p);
397 VOP_UNLOCK(vcb->extentsRefNum, 0, p);
398 goto ErrorExit;
399 }
400
401 /* Pick up volume name and create date */
402 retval = cat_idlookup(hfsmp, kHFSRootFolderID, &cndesc, &cnattr, NULL);
403 if (retval) {
404 VOP_UNLOCK(vcb->allocationsRefNum, 0, p);
405 VOP_UNLOCK(vcb->catalogRefNum, 0, p);
406 VOP_UNLOCK(vcb->extentsRefNum, 0, p);
407 goto ErrorExit;
408 }
409 vcb->vcbCrDate = cnattr.ca_itime;
410 vcb->volumeNameEncodingHint = cndesc.cd_encoding;
411 bcopy(cndesc.cd_nameptr, vcb->vcbVN, min(255, cndesc.cd_namelen));
412 cat_releasedesc(&cndesc);
1c79356b
A
413
414 /* mark the volume dirty (clear clean unmount bit) */
415 vcb->vcbAtrb &= ~kHFSVolumeUnmountedMask;
416
1c79356b
A
417 /*
418 * all done with metadata files so we can unlock now...
419 */
420 VOP_UNLOCK(vcb->allocationsRefNum, 0, p);
421 VOP_UNLOCK(vcb->catalogRefNum, 0, p);
422 VOP_UNLOCK(vcb->extentsRefNum, 0, p);
423
9bccf70c
A
424 /* setup private/hidden directory for unlinked files */
425 hfsmp->hfs_private_metadata_dir = FindMetaDataDirectory(vcb);
426
427 if ( !(vcb->vcbAtrb & kHFSVolumeHardwareLockMask) ) // if the disk is not write protected
1c79356b 428 {
9bccf70c 429 MarkVCBDirty( vcb ); // mark VCB dirty so it will be written
1c79356b 430 }
1c79356b
A
431
432 return (0);
433
1c79356b
A
434ErrorExit:
435 /*
436 * A fatal error occured and the volume cannot be mounted
437 * release any resources that we aquired...
438 */
439
9bccf70c 440 InvalidateCatalogCache(vcb);
1c79356b
A
441 ReleaseMetaFileVNode(vcb->allocationsRefNum);
442 ReleaseMetaFileVNode(vcb->catalogRefNum);
443 ReleaseMetaFileVNode(vcb->extentsRefNum);
444
445 return (retval);
446}
447
448
449/*
450 * ReleaseMetaFileVNode
451 *
452 * vp L - -
453 */
454static void ReleaseMetaFileVNode(struct vnode *vp)
455{
9bccf70c 456 struct filefork *fp;
1c79356b 457
9bccf70c
A
458 if (vp && (fp = VTOF(vp))) {
459 if (fp->fcbBTCBPtr != NULL)
460 (void) BTClosePath(fp);
1c79356b
A
461
462 /* release the node even if BTClosePath fails */
9bccf70c
A
463 vrele(vp);
464 vgone(vp);
1c79356b 465 }
1c79356b
A
466}
467
468
469/*************************************************************
470*
471* Unmounts a hfs volume.
472* At this point vflush() has been called (to dump all non-metadata files)
473*
474*************************************************************/
475
476short hfsUnmount( register struct hfsmount *hfsmp, struct proc *p)
477{
9bccf70c
A
478 ExtendedVCB *vcb = HFSTOVCB(hfsmp);
479 int retval = E_NONE;
1c79356b 480
1c79356b 481 InvalidateCatalogCache( vcb );
1c79356b
A
482
483 if (vcb->vcbSigWord == kHFSPlusSigWord)
484 ReleaseMetaFileVNode(vcb->allocationsRefNum);
485
486 ReleaseMetaFileVNode(vcb->catalogRefNum);
487 ReleaseMetaFileVNode(vcb->extentsRefNum);
488
489 return (retval);
490}
491
492
0b4e3aa0 493/*
9bccf70c 494 * Some 3rd party kexts link against hfs_getcatalog so keep a stub for now.
0b4e3aa0 495 */
9bccf70c
A
496short
497hfs_getcatalog(void *p1, u_long p2, void *p3, short p4, void *p5)
0b4e3aa0 498{
9bccf70c 499 return ENOENT;
0b4e3aa0
A
500}
501
502
9bccf70c 503int overflow_extents(struct filefork *fp)
1c79356b 504{
9bccf70c 505 u_long blocks;
1c79356b 506
9bccf70c
A
507 if (VTOVCB(FTOV(fp))->vcbSigWord == kHFSPlusSigWord) {
508 if (fp->ff_extents[7].blockCount == 0)
509 return (0);
1c79356b 510
9bccf70c
A
511 blocks = fp->ff_extents[0].blockCount +
512 fp->ff_extents[1].blockCount +
513 fp->ff_extents[2].blockCount +
514 fp->ff_extents[3].blockCount +
515 fp->ff_extents[4].blockCount +
516 fp->ff_extents[5].blockCount +
517 fp->ff_extents[6].blockCount +
518 fp->ff_extents[7].blockCount;
519 } else {
520 if (fp->ff_extents[2].blockCount == 0)
521 return false;
522
523 blocks = fp->ff_extents[0].blockCount +
524 fp->ff_extents[1].blockCount +
525 fp->ff_extents[2].blockCount;
526 }
1c79356b 527
9bccf70c
A
528 return (fp->ff_blocks > blocks);
529}
1c79356b
A
530
531
9bccf70c
A
532/* __private_extern__ */
533int
534hfs_metafilelocking(struct hfsmount *hfsmp, u_long fileID, u_int flags, struct proc *p)
1c79356b 535{
9bccf70c
A
536 ExtendedVCB *vcb;
537 struct vnode *vp = NULL;
538 int numOfLockedBuffs;
539 int retval = 0;
1c79356b 540
9bccf70c
A
541 vcb = HFSTOVCB(hfsmp);
542
543 switch (fileID) {
544 case kHFSExtentsFileID:
545 vp = vcb->extentsRefNum;
546 break;
547
548 case kHFSCatalogFileID:
549 vp = vcb->catalogRefNum;
550 break;
551
552 case kHFSAllocationFileID:
553 /* bitmap is covered by Extents B-tree locking */
554 /* FALL THROUGH */
555 default:
556 panic("hfs_lockmetafile: invalid fileID");
557 }
1c79356b 558
9bccf70c
A
559 /* Release, if necesary any locked buffer caches */
560 if ((flags & LK_TYPE_MASK) == LK_RELEASE) {
561 struct timeval tv = time;
562 u_int32_t lastfsync = tv.tv_sec;
1c79356b 563
9bccf70c
A
564 (void) BTGetLastSync((FCB*)VTOF(vp), &lastfsync);
565
566 numOfLockedBuffs = count_lock_queue();
567 if ((numOfLockedBuffs > kMaxLockedMetaBuffers) || ((numOfLockedBuffs>1) && ((tv.tv_sec - lastfsync) > kMaxSecsForFsync))) {
568 hfs_btsync(vp, HFS_SYNCTRANS);
569 }
570 } else {
571 flags |= LK_RETRY;
572 }
573
574 retval = lockmgr(&VTOC(vp)->c_lock, flags, &vp->v_interlock, p);
1c79356b 575
9bccf70c
A
576 return (retval);
577}
1c79356b 578
9bccf70c
A
579/*
580 * RequireFileLock
581 *
582 * Check to see if a vnode is locked in the current context
583 * This is to be used for debugging purposes only!!
584 */
585#if HFS_DIAGNOSTIC
586void RequireFileLock(FileReference vp, int shareable)
1c79356b 587{
9bccf70c
A
588 struct lock__bsd__ *lkp;
589 int locked = false;
590 pid_t pid;
591 void * self;
1c79356b 592
9bccf70c
A
593 pid = current_proc()->p_pid;
594 self = (void *) current_thread();
595 lkp = &VTOC(vp)->c_lock;
1c79356b 596
9bccf70c
A
597 simple_lock(&lkp->lk_interlock);
598
599 if (shareable && (lkp->lk_sharecount > 0) && (lkp->lk_lockholder == LK_NOPROC))
600 locked = true;
601 else if ((lkp->lk_exclusivecount > 0) && (lkp->lk_lockholder == pid) && (lkp->lk_lockthread == self))
602 locked = true;
1c79356b 603
9bccf70c
A
604 simple_unlock(&lkp->lk_interlock);
605
606 if (!locked) {
607 switch (VTOC(vp)->c_fileid) {
608 case 3:
609 DEBUG_BREAK_MSG((" #\n # RequireFileLock: extent btree vnode not locked! v: 0x%08X\n #\n", (u_int)vp));
610 break;
1c79356b 611
9bccf70c
A
612 case 4:
613 DEBUG_BREAK_MSG((" #\n # RequireFileLock: catalog btree vnode not locked! v: 0x%08X\n #\n", (u_int)vp));
614 break;
1c79356b 615
9bccf70c
A
616 default:
617 DEBUG_BREAK_MSG((" #\n # RequireFileLock: file (%d) not locked! v: 0x%08X\n #\n", VTOC(vp)->c_fileid, (u_int)vp));
618 break;
619 }
620 }
1c79356b 621}
9bccf70c 622#endif
1c79356b
A
623
624
9bccf70c
A
625/*
626 * There are three ways to qualify for ownership rights on an object:
627 *
628 * 1. (a) Your UID matches the cnode's UID.
629 * (b) The object in question is owned by "unknown" and
630 * your UID matches the console user's UID.
631 * 2. (a) Permissions on the filesystem are being ignored and
632 * your UID matches the replacement UID.
633 * (b) Permissions on the filesystem are being ignored and
634 * the replacement UID is "unknown" and
635 * your UID matches the console user UID.
636 * 3. You are root.
637 *
638 */
639int
640hfs_owner_rights(struct hfsmount *hfsmp, uid_t cnode_uid, struct ucred *cred,
641 struct proc *p, int invokesuperuserstatus)
642{
643 if ((cred->cr_uid == cnode_uid) || /* [1a] */
644 ((cnode_uid == UNKNOWNUID) && (cred->cr_uid == console_user)) || /* [1b] */
645 ((HFSTOVFS(hfsmp)->mnt_flag & MNT_UNKNOWNPERMISSIONS) && /* [2] */
646 ((cred->cr_uid == hfsmp->hfs_uid) || /* [2a] */
647 ((hfsmp->hfs_uid == UNKNOWNUID) && /* [2b] */
648 (cred->cr_uid == console_user)))) ||
649 (invokesuperuserstatus && (suser(cred, &p->p_acflag) == 0))) { /* [3] */
650 return (0);
651 } else {
652 return (EPERM);
653 }
1c79356b
A
654}
655
656
9bccf70c
A
657unsigned long BestBlockSizeFit(unsigned long allocationBlockSize,
658 unsigned long blockSizeLimit,
659 unsigned long baseMultiple) {
660 /*
661 Compute the optimal (largest) block size (no larger than allocationBlockSize) that is less than the
662 specified limit but still an even multiple of the baseMultiple.
663 */
664 int baseBlockCount, blockCount;
665 unsigned long trialBlockSize;
1c79356b 666
9bccf70c
A
667 if (allocationBlockSize % baseMultiple != 0) {
668 /*
669 Whoops: the allocation blocks aren't even multiples of the specified base:
670 no amount of dividing them into even parts will be a multiple, either then!
671 */
672 return 512; /* Hope for the best */
673 };
1c79356b 674
9bccf70c
A
675 /* Try the obvious winner first, to prevent 12K allocation blocks, for instance,
676 from being handled as two 6K logical blocks instead of 3 4K logical blocks.
677 Even though the former (the result of the loop below) is the larger allocation
678 block size, the latter is more efficient: */
679 if (allocationBlockSize % PAGE_SIZE == 0) return PAGE_SIZE;
1c79356b 680
9bccf70c
A
681 /* No clear winner exists: pick the largest even fraction <= MAXBSIZE: */
682 baseBlockCount = allocationBlockSize / baseMultiple; /* Now guaranteed to be an even multiple */
1c79356b 683
9bccf70c
A
684 for (blockCount = baseBlockCount; blockCount > 0; --blockCount) {
685 trialBlockSize = blockCount * baseMultiple;
686 if (allocationBlockSize % trialBlockSize == 0) { /* An even multiple? */
687 if ((trialBlockSize <= blockSizeLimit) &&
688 (trialBlockSize % baseMultiple == 0)) {
689 return trialBlockSize;
690 };
691 };
692 };
1c79356b 693
9bccf70c
A
694 /* Note: we should never get here, since blockCount = 1 should always work,
695 but this is nice and safe and makes the compiler happy, too ... */
696 return 512;
697}
1c79356b 698
1c79356b 699
9bccf70c
A
700/*
701 * To make the HFS Plus filesystem follow UFS unlink semantics, a remove
702 * of an active vnode is translated to a move/rename so the file appears
703 * deleted. The destination folder for these move/renames is setup here
704 * and a reference to it is place in hfsmp->hfs_private_metadata_dir.
705 */
706u_long
707FindMetaDataDirectory(ExtendedVCB *vcb)
1c79356b 708{
9bccf70c
A
709 struct hfsmount * hfsmp;
710 struct vnode * dvp = NULL;
711 struct cnode * dcp = NULL;
712 struct FndrDirInfo * fndrinfo;
713 struct cat_desc out_desc = {0};
714 struct timeval tv;
715 int error;
716
717 if (vcb->vcbSigWord != kHFSPlusSigWord)
7b1edb79 718 return (0);
7b1edb79 719
9bccf70c 720 hfsmp = VCBTOHFS(vcb);
7b1edb79 721
9bccf70c
A
722 if (hfsmp->hfs_privdir_desc.cd_parentcnid == 0) {
723 hfsmp->hfs_privdir_desc.cd_parentcnid = kRootDirID;
724 hfsmp->hfs_privdir_desc.cd_nameptr = hfs_privdirname;
725 hfsmp->hfs_privdir_desc.cd_namelen = strlen(hfs_privdirname);
726 hfsmp->hfs_privdir_desc.cd_flags = CD_ISDIR;
7b1edb79
A
727 }
728
9bccf70c
A
729 /* Lock catalog b-tree */
730 error = hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_EXCLUSIVE, current_proc());
731 if (error)
732 return (0);
1c79356b 733
9bccf70c
A
734 error = cat_lookup(hfsmp, &hfsmp->hfs_privdir_desc, 0, NULL,
735 &hfsmp->hfs_privdir_attr, NULL);
736
737 if (error == 0) {
738 /* Unlock catalog b-tree */
739 (void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, current_proc());
740 hfsmp->hfs_metadata_createdate = hfsmp->hfs_privdir_attr.ca_itime;
741 return (hfsmp->hfs_privdir_attr.ca_fileid);
742 } else if (hfsmp->hfs_fs_ronly) {
743 /* Unlock catalog b-tree */
744 (void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, current_proc());
745 return (0);
1c79356b 746 }
9bccf70c
A
747
748 /* Setup the default attributes */
749 bzero(&hfsmp->hfs_privdir_attr, sizeof(struct cat_attr));
750 hfsmp->hfs_privdir_attr.ca_mode = S_IFDIR;
751 hfsmp->hfs_privdir_attr.ca_flags = SF_IMMUTABLE;
752 hfsmp->hfs_privdir_attr.ca_nlink = 2;
753 hfsmp->hfs_privdir_attr.ca_itime = vcb->vcbCrDate;
754 hfsmp->hfs_privdir_attr.ca_mtime = time.tv_sec;
755
756 /* hidden and off the desktop view */
757 fndrinfo = (struct FndrDirInfo *)&hfsmp->hfs_privdir_attr.ca_finderinfo;
758 fndrinfo->frLocation.v = SWAP_BE16 (22460);
759 fndrinfo->frLocation.h = SWAP_BE16 (22460);
760 fndrinfo->frFlags |= SWAP_BE16 (kIsInvisible + kNameLocked);
761
762 error = cat_create(hfsmp, &hfsmp->hfs_privdir_desc,
763 &hfsmp->hfs_privdir_attr, &out_desc);
764
765 /* Unlock catalog b-tree */
766 (void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, current_proc());
767 if (error)
768 return (0);
1c79356b 769
9bccf70c
A
770 hfsmp->hfs_privdir_desc.cd_hint = out_desc.cd_hint;
771 hfsmp->hfs_privdir_desc.cd_cnid = out_desc.cd_cnid;
772 hfsmp->hfs_privdir_attr.ca_fileid = out_desc.cd_cnid;
773 hfsmp->hfs_metadata_createdate = vcb->vcbCrDate;
774
775 if (VFS_ROOT(HFSTOVFS(hfsmp), &dvp) == 0) {
776 dcp = VTOC(dvp);
777 dcp->c_childhint = out_desc.cd_hint;
778 dcp->c_nlink++;
779 dcp->c_entries++;
780 dcp->c_flag |= C_CHANGE | C_UPDATE;
781 tv = time;
782 (void) VOP_UPDATE(dvp, &tv, &tv, 0);
783 vput(dvp);
1c79356b 784 }
9bccf70c
A
785 hfs_volupdate(hfsmp, VOL_MKDIR, 1);
786 cat_releasedesc(&out_desc);
1c79356b 787
9bccf70c 788 return (out_desc.cd_cnid);
1c79356b
A
789}
790
9bccf70c
A
791
792/*
793 * This will return the correct logical block size for a given vnode.
794 * For most files, it is the allocation block size, for meta data like
795 * BTrees, this is kept as part of the BTree private nodeSize
796 */
797u_int32_t
798GetLogicalBlockSize(struct vnode *vp)
1c79356b 799{
9bccf70c 800u_int32_t logBlockSize;
1c79356b 801
9bccf70c 802 DBG_ASSERT(vp != NULL);
1c79356b 803
9bccf70c
A
804 /* start with default */
805 logBlockSize = VTOHFS(vp)->hfs_logBlockSize;
1c79356b 806
0b4e3aa0 807 if (vp->v_flag & VSYSTEM) {
9bccf70c 808 if (VTOF(vp)->fcbBTCBPtr != NULL) {
0b4e3aa0
A
809 BTreeInfoRec bTreeInfo;
810
811 /*
812 * We do not lock the BTrees, because if we are getting block..then the tree
813 * should be locked in the first place.
814 * We just want the nodeSize wich will NEVER change..so even if the world
815 * is changing..the nodeSize should remain the same. Which argues why lock
816 * it in the first place??
817 */
818
9bccf70c 819 (void) BTGetInformation (VTOF(vp), kBTreeInfoVersion, &bTreeInfo);
0b4e3aa0
A
820
821 logBlockSize = bTreeInfo.nodeSize;
822
9bccf70c 823 } else if (VTOC(vp)->c_fileid == kHFSAllocationFileID) {
0b4e3aa0 824 logBlockSize = VTOVCB(vp)->vcbVBMIOSize;
1c79356b 825 }
0b4e3aa0
A
826 }
827
1c79356b
A
828 DBG_ASSERT(logBlockSize > 0);
829
830 return logBlockSize;
831}
832
9bccf70c
A
833__private_extern__
834u_int32_t
835hfs_freeblks(struct hfsmount * hfsmp, int wantreserve)
836{
837 struct vcb_t *vcb = HFSTOVCB(hfsmp);
838 u_int32_t freeblks;
839
840 freeblks = vcb->freeBlocks;
841 if (wantreserve) {
842 if (freeblks > vcb->reserveBlocks)
843 freeblks -= vcb->reserveBlocks;
844 else
845 freeblks = 0;
846 }
847
848 freeblks -= vcb->loanedBlocks;
849 return (freeblks);
850}
851
1c79356b
A
852/*
853 * Map HFS Common errors (negative) to BSD error codes (positive).
854 * Positive errors (ie BSD errors) are passed through unchanged.
855 */
856short MacToVFSError(OSErr err)
857{
9bccf70c
A
858 if (err >= 0)
859 return err;
1c79356b 860
1c79356b 861 switch (err) {
9bccf70c
A
862 case dskFulErr: /* -34 */
863 case btNoSpaceAvail: /* -32733 */
864 case fxOvFlErr: /* -32750 */
865 return ENOSPC; /* +28 */
866
867 case btBadNode: /* -32731 */
868 return EIO; /* +5 */
869
870 case memFullErr: /* -108 */
871 return ENOMEM; /* +12 */
872
873 case cmExists: /* -32718 */
874 case btExists: /* -32734 */
875 return EEXIST; /* +17 */
876
877 case cmNotFound: /* -32719 */
878 case btNotFound: /* -32735 */
879 return ENOENT; /* 28 */
880
881 case cmNotEmpty: /* -32717 */
882 return ENOTEMPTY; /* 66 */
883
884 case cmFThdDirErr: /* -32714 */
885 return EISDIR; /* 21 */
886
887 case fxRangeErr: /* -32751 */
888 return EIO; /* 5 */
889
890 case bdNamErr: /* -37 */
891 return ENAMETOOLONG; /* 63 */
892
893 case paramErr: /* -50 */
894 case fileBoundsErr: /* -1309 */
895 return EINVAL; /* +22 */
896
897 case fsBTBadNodeSize:
d52fe63f 898 return ENXIO;
9bccf70c
A
899
900 default:
901 return EIO; /* +5 */
1c79356b
A
902 }
903}
904
905
906/*
9bccf70c
A
907 * Get the directory entry name hint for a given index.
908 * The directory cnode (dcp) must be locked.
1c79356b 909 */
9bccf70c
A
910__private_extern__
911char *
912hfs_getnamehint(struct cnode *dcp, int index)
1c79356b 913{
9bccf70c
A
914 struct hfs_index *entry;
915 void *self;
1c79356b 916
9bccf70c
A
917 if (index > 0) {
918 self = current_thread();
919 SLIST_FOREACH(entry, &dcp->c_indexlist, hi_link) {
920 if ((entry->hi_index == index)
921 && (entry->hi_thread == self))
922 return (entry->hi_name);
923 }
924 }
1c79356b 925
9bccf70c 926 return (NULL);
1c79356b
A
927}
928
9bccf70c
A
929/*
930 * Save a directory entry name hint for a given index.
931 * The directory cnode (dcp) must be locked.
932 */
933__private_extern__
934void
935hfs_savenamehint(struct cnode *dcp, int index, const char * namehint)
936{
937 struct hfs_index *entry;
938 int len;
939
940 if (index > 0) {
941 len = strlen(namehint);
942 MALLOC(entry, struct hfs_index *, len + sizeof(struct hfs_index),
943 M_TEMP, M_WAITOK);
944 entry->hi_index = index;
945 entry->hi_thread = current_thread();
946 bcopy(namehint, entry->hi_name, len + 1);
947 SLIST_INSERT_HEAD(&dcp->c_indexlist, entry, hi_link);
948 }
1c79356b
A
949}
950
9bccf70c
A
951/*
952 * Release the directory entry name hint for a given index.
953 * The directory cnode (dcp) must be locked.
954 */
955__private_extern__
956void
957hfs_relnamehint(struct cnode *dcp, int index)
958{
959 struct hfs_index *entry;
960 void *self;
961
962 if (index > 0) {
963 self = current_thread();
964 SLIST_FOREACH(entry, &dcp->c_indexlist, hi_link) {
965 if ((entry->hi_index == index)
966 && (entry->hi_thread == self)) {
967 SLIST_REMOVE(&dcp->c_indexlist, entry, hfs_index,
968 hi_link);
969 FREE(entry, M_TEMP);
970 break;
971 }
972 }
1c79356b 973 }
1c79356b
A
974}
975
9bccf70c
A
976/*
977 * Release all directory entry name hints.
978 */
979__private_extern__
980void
981hfs_relnamehints(struct cnode *dcp)
982{
983 struct hfs_index *entry;
984 struct hfs_index *next;
1c79356b 985
9bccf70c
A
986 if (!SLIST_EMPTY(&dcp->c_indexlist)) {
987 for(entry = SLIST_FIRST(&dcp->c_indexlist);
988 entry != NULL;
989 entry = next) {
990 next = SLIST_NEXT(entry, hi_link);
991 SLIST_REMOVE(&dcp->c_indexlist, entry, hfs_index, hi_link);
992 FREE(entry, M_TEMP);
1c79356b 993 }
1c79356b 994 }
9bccf70c 995}
1c79356b 996
1c79356b 997
1c79356b 998