]> git.saurik.com Git - apple/xnu.git/blob - bsd/hfs/hfs_vfsutils.c
c45f8a898cb04cbdb99732df5e63635e65ade57f
[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 * 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 *
24 * (c) 1997-2002 Apple Computer, Inc. All Rights Reserved
25 *
26 * hfs_vfsutils.c -- Routines that go between the HFS layer and the VFS.
27 *
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>
34 #include <sys/mount.h>
35 #include <sys/namei.h>
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"
42 #include "hfs_catalog.h"
43 #include "hfs_dbg.h"
44 #include "hfs_mount.h"
45 #include "hfs_endian.h"
46 #include "hfs_cnode.h"
47
48 #include "hfscommon/headers/FileMgrInternal.h"
49 #include "hfscommon/headers/BTreesInternal.h"
50 #include "hfscommon/headers/HFSUnicodeWrappers.h"
51
52
53 extern int count_lock_queue __P((void));
54 extern uid_t console_user;
55
56
57 static void ReleaseMetaFileVNode(struct vnode *vp);
58
59 u_int32_t GetLogicalBlockSize(struct vnode *vp);
60
61 /* BTree accessor routines */
62 extern OSStatus GetBTreeBlock(FileReference vp, UInt32 blockNum, GetBlockOptions options, BlockDescriptor *block);
63 extern OSStatus SetBTreeBlockSize(FileReference vp, ByteCount blockSize, ItemCount minBlockCount);
64 extern OSStatus ExtendBTreeFile(FileReference vp, FSSize minEOF, FSSize maxEOF);
65 extern 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 //*******************************************************************************
78 char hfs_catname[] = "Catalog B-tree";
79 char hfs_extname[] = "Extents B-tree";
80 char hfs_vbmname[] = "Volume Bitmap";
81
82 char hfs_privdirname[] =
83 "\xE2\x90\x80\xE2\x90\x80\xE2\x90\x80\xE2\x90\x80HFS+ Private Data";
84
85 OSErr hfs_MountHFSVolume(struct hfsmount *hfsmp, HFSMasterDirectoryBlock *mdb,
86 struct proc *p)
87 {
88 ExtendedVCB *vcb = HFSTOVCB(hfsmp);
89 int error;
90 ByteCount utf8chars;
91 struct cat_desc cndesc;
92 struct cat_attr cnattr;
93 struct cat_fork fork;
94
95 /* Block size must be a multiple of 512 */
96 if (SWAP_BE32(mdb->drAlBlkSiz) == 0 ||
97 (SWAP_BE32(mdb->drAlBlkSiz) & 0x01FF) != 0)
98 return (EINVAL);
99
100 /* don't mount a writeable volume if its dirty, it must be cleaned by fsck_hfs */
101 if ((hfsmp->hfs_fs_ronly == 0) && ((SWAP_BE16(mdb->drAtrb) & kHFSVolumeUnmountedMask) == 0))
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 */
109 vcb->vcbSigWord = SWAP_BE16 (mdb->drSigWord);
110 vcb->vcbCrDate = to_bsd_time(LocalToUTC(SWAP_BE32(mdb->drCrDate)));
111 vcb->localCreateDate = SWAP_BE32 (mdb->drCrDate);
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);
128 bcopy(mdb->drFndrInfo, vcb->vcbFndrInfo, sizeof(vcb->vcbFndrInfo));
129 if (!hfsmp->hfs_fs_ronly)
130 vcb->vcbWrCnt++; /* Compensate for write of MDB on last flush */
131
132 /* convert hfs encoded name into UTF-8 string */
133 error = hfs_to_utf8(vcb, mdb->drVN, NAME_MAX, &utf8chars, vcb->vcbVN);
134 /*
135 * When an HFS name cannot be encoded with the current
136 * volume encoding we use MacRoman as a fallback.
137 */
138 if (error || (utf8chars == 0))
139 (void) mac_roman_to_utf8(mdb->drVN, NAME_MAX, &utf8chars, vcb->vcbVN);
140
141 hfsmp->hfs_logBlockSize = BestBlockSizeFit(vcb->blockSize, MAXBSIZE, hfsmp->hfs_phys_block_size);
142 vcb->vcbVBMIOSize = kHFSBlockSize;
143
144 VCB_LOCK_INIT(vcb);
145
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
153 /*
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 }
181
182 /*
183 * Set up Catalog B-tree vnode...
184 */
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 }
214
215 /* mark the volume dirty (clear clean unmount bit) */
216 vcb->vcbAtrb &= ~kHFSVolumeUnmountedMask;
217
218 /*
219 * all done with b-trees so we can unlock now...
220 */
221 VOP_UNLOCK(vcb->catalogRefNum, 0, p);
222 VOP_UNLOCK(vcb->extentsRefNum, 0, p);
223
224 if ( error == noErr )
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:
234 MtVolErr:
235 ReleaseMetaFileVNode(vcb->catalogRefNum);
236 ReleaseMetaFileVNode(vcb->extentsRefNum);
237
238 CmdDone:
239 return (error);
240 }
241
242 //*******************************************************************************
243 // Routine: hfs_MountHFSPlusVolume
244 //
245 //
246 //*******************************************************************************
247
248 OSErr hfs_MountHFSPlusVolume(struct hfsmount *hfsmp, HFSPlusVolumeHeader *vhp,
249 off_t embeddedOffset, u_int64_t disksize, struct proc *p)
250 {
251 register ExtendedVCB *vcb;
252 struct cat_desc cndesc;
253 struct cat_attr cnattr;
254 UInt32 blockSize;
255 OSErr retval;
256
257 if (SWAP_BE16(vhp->signature) != kHFSPlusSigWord ||
258 SWAP_BE16(vhp->version) != kHFSPlusVersion)
259 return (EINVAL);
260
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);
265
266 /* don't mount a writable volume if its dirty, it must be cleaned by fsck_hfs */
267 if (hfsmp->hfs_fs_ronly == 0 && (SWAP_BE32(vhp->attributes) & kHFSVolumeUnmountedMask) == 0)
268 return (EINVAL);
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 }
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
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);
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 */
296 if (!hfsmp->hfs_fs_ronly)
297 vcb->vcbWrCnt++; /* compensate for write of Volume Header on last flush */
298
299 VCB_LOCK_INIT(vcb);
300
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);
308
309 vcb->hfsPlusIOPosOffset = embeddedOffset;
310
311 /* Default to no free block reserve */
312 vcb->reserveBlocks = 0;
313
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);
320 vcb->vcbVBMIOSize = min(vcb->blockSize, MAXPHYSIO);
321
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;
327
328 /*
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
343 if (retval) goto ErrorExit;
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 }
352
353 /*
354 * Set up Catalog B-tree vnode
355 */
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 }
380
381 /*
382 * Set up Allocation file vnode
383 */
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);
413
414 /* mark the volume dirty (clear clean unmount bit) */
415 vcb->vcbAtrb &= ~kHFSVolumeUnmountedMask;
416
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
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
428 {
429 MarkVCBDirty( vcb ); // mark VCB dirty so it will be written
430 }
431
432 return (0);
433
434 ErrorExit:
435 /*
436 * A fatal error occured and the volume cannot be mounted
437 * release any resources that we aquired...
438 */
439
440 InvalidateCatalogCache(vcb);
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 */
454 static void ReleaseMetaFileVNode(struct vnode *vp)
455 {
456 struct filefork *fp;
457
458 if (vp && (fp = VTOF(vp))) {
459 if (fp->fcbBTCBPtr != NULL)
460 (void) BTClosePath(fp);
461
462 /* release the node even if BTClosePath fails */
463 vrele(vp);
464 vgone(vp);
465 }
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
476 short hfsUnmount( register struct hfsmount *hfsmp, struct proc *p)
477 {
478 ExtendedVCB *vcb = HFSTOVCB(hfsmp);
479 int retval = E_NONE;
480
481 InvalidateCatalogCache( vcb );
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
493 /*
494 * Some 3rd party kexts link against hfs_getcatalog so keep a stub for now.
495 */
496 short
497 hfs_getcatalog(void *p1, u_long p2, void *p3, short p4, void *p5)
498 {
499 return ENOENT;
500 }
501
502
503 int overflow_extents(struct filefork *fp)
504 {
505 u_long blocks;
506
507 if (VTOVCB(FTOV(fp))->vcbSigWord == kHFSPlusSigWord) {
508 if (fp->ff_extents[7].blockCount == 0)
509 return (0);
510
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 }
527
528 return (fp->ff_blocks > blocks);
529 }
530
531
532 /* __private_extern__ */
533 int
534 hfs_metafilelocking(struct hfsmount *hfsmp, u_long fileID, u_int flags, struct proc *p)
535 {
536 ExtendedVCB *vcb;
537 struct vnode *vp = NULL;
538 int numOfLockedBuffs;
539 int retval = 0;
540
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 }
558
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;
563
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);
575
576 return (retval);
577 }
578
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
586 void RequireFileLock(FileReference vp, int shareable)
587 {
588 struct lock__bsd__ *lkp;
589 int locked = false;
590 pid_t pid;
591 void * self;
592
593 pid = current_proc()->p_pid;
594 self = (void *) current_thread();
595 lkp = &VTOC(vp)->c_lock;
596
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;
603
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;
611
612 case 4:
613 DEBUG_BREAK_MSG((" #\n # RequireFileLock: catalog btree vnode not locked! v: 0x%08X\n #\n", (u_int)vp));
614 break;
615
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 }
621 }
622 #endif
623
624
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 */
639 int
640 hfs_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 }
654 }
655
656
657 unsigned 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;
666
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 };
674
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;
680
681 /* No clear winner exists: pick the largest even fraction <= MAXBSIZE: */
682 baseBlockCount = allocationBlockSize / baseMultiple; /* Now guaranteed to be an even multiple */
683
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 };
693
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 }
698
699
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 */
706 u_long
707 FindMetaDataDirectory(ExtendedVCB *vcb)
708 {
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)
718 return (0);
719
720 hfsmp = VCBTOHFS(vcb);
721
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;
727 }
728
729 /* Lock catalog b-tree */
730 error = hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_EXCLUSIVE, current_proc());
731 if (error)
732 return (0);
733
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);
746 }
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);
769
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);
784 }
785 hfs_volupdate(hfsmp, VOL_MKDIR, 1);
786 cat_releasedesc(&out_desc);
787
788 return (out_desc.cd_cnid);
789 }
790
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 */
797 u_int32_t
798 GetLogicalBlockSize(struct vnode *vp)
799 {
800 u_int32_t logBlockSize;
801
802 DBG_ASSERT(vp != NULL);
803
804 /* start with default */
805 logBlockSize = VTOHFS(vp)->hfs_logBlockSize;
806
807 if (vp->v_flag & VSYSTEM) {
808 if (VTOF(vp)->fcbBTCBPtr != NULL) {
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
819 (void) BTGetInformation (VTOF(vp), kBTreeInfoVersion, &bTreeInfo);
820
821 logBlockSize = bTreeInfo.nodeSize;
822
823 } else if (VTOC(vp)->c_fileid == kHFSAllocationFileID) {
824 logBlockSize = VTOVCB(vp)->vcbVBMIOSize;
825 }
826 }
827
828 DBG_ASSERT(logBlockSize > 0);
829
830 return logBlockSize;
831 }
832
833 __private_extern__
834 u_int32_t
835 hfs_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
852 /*
853 * Map HFS Common errors (negative) to BSD error codes (positive).
854 * Positive errors (ie BSD errors) are passed through unchanged.
855 */
856 short MacToVFSError(OSErr err)
857 {
858 if (err >= 0)
859 return err;
860
861 switch (err) {
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:
898 return ENXIO;
899
900 default:
901 return EIO; /* +5 */
902 }
903 }
904
905
906 /*
907 * Get the directory entry name hint for a given index.
908 * The directory cnode (dcp) must be locked.
909 */
910 __private_extern__
911 char *
912 hfs_getnamehint(struct cnode *dcp, int index)
913 {
914 struct hfs_index *entry;
915 void *self;
916
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 }
925
926 return (NULL);
927 }
928
929 /*
930 * Save a directory entry name hint for a given index.
931 * The directory cnode (dcp) must be locked.
932 */
933 __private_extern__
934 void
935 hfs_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 }
949 }
950
951 /*
952 * Release the directory entry name hint for a given index.
953 * The directory cnode (dcp) must be locked.
954 */
955 __private_extern__
956 void
957 hfs_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 }
973 }
974 }
975
976 /*
977 * Release all directory entry name hints.
978 */
979 __private_extern__
980 void
981 hfs_relnamehints(struct cnode *dcp)
982 {
983 struct hfs_index *entry;
984 struct hfs_index *next;
985
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);
993 }
994 }
995 }
996
997
998