2 * Copyright (c) 2000-2002 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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.
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
20 * @APPLE_LICENSE_HEADER_END@
22 /* @(#)hfs_vfsutils.c 4.0
24 * (c) 1997-2002 Apple Computer, Inc. All Rights Reserved
26 * hfs_vfsutils.c -- Routines that go between the HFS layer and the VFS.
29 #include <sys/param.h>
30 #include <sys/systm.h>
31 #include <sys/kernel.h>
32 #include <sys/malloc.h>
34 #include <sys/mount.h>
35 #include <sys/namei.h>
39 #include <sys/unistd.h>
42 #include "hfs_catalog.h"
44 #include "hfs_mount.h"
45 #include "hfs_endian.h"
46 #include "hfs_cnode.h"
48 #include "hfscommon/headers/FileMgrInternal.h"
49 #include "hfscommon/headers/BTreesInternal.h"
50 #include "hfscommon/headers/HFSUnicodeWrappers.h"
53 extern int count_lock_queue
__P((void));
54 extern uid_t console_user
;
57 static void ReleaseMetaFileVNode(struct vnode
*vp
);
59 u_int32_t
GetLogicalBlockSize(struct vnode
*vp
);
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
);
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 //*******************************************************************************
73 //*******************************************************************************
74 // Routine: hfs_MountHFSVolume
77 //*******************************************************************************
78 char hfs_catname
[] = "Catalog B-tree";
79 char hfs_extname
[] = "Extents B-tree";
80 char hfs_vbmname
[] = "Volume Bitmap";
82 char hfs_privdirname
[] =
83 "\xE2\x90\x80\xE2\x90\x80\xE2\x90\x80\xE2\x90\x80HFS+ Private Data";
85 OSErr
hfs_MountHFSVolume(struct hfsmount
*hfsmp
, HFSMasterDirectoryBlock
*mdb
,
88 ExtendedVCB
*vcb
= HFSTOVCB(hfsmp
);
91 struct cat_desc cndesc
;
92 struct cat_attr cnattr
;
95 /* Block size must be a multiple of 512 */
96 if (SWAP_BE32(mdb
->drAlBlkSiz
) == 0 ||
97 (SWAP_BE32(mdb
->drAlBlkSiz
) & 0x01FF) != 0)
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))
105 * The MDB seems OK: transfer info from it into VCB
106 * Note - the VCB starts out clear (all zeros)
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 */
132 /* convert hfs encoded name into UTF-8 string */
133 error
= hfs_to_utf8(vcb
, mdb
->drVN
, NAME_MAX
, &utf8chars
, vcb
->vcbVN
);
135 * When an HFS name cannot be encoded with the current
136 * volume encoding we use MacRoman as a fallback.
138 if (error
|| (utf8chars
== 0))
139 (void) mac_roman_to_utf8(mdb
->drVN
, NAME_MAX
, &utf8chars
, vcb
->vcbVN
);
141 hfsmp
->hfs_logBlockSize
= BestBlockSizeFit(vcb
->blockSize
, MAXBSIZE
, hfsmp
->hfs_phys_block_size
);
142 vcb
->vcbVBMIOSize
= kHFSBlockSize
;
146 bzero(&cndesc
, sizeof(cndesc
));
147 cndesc
.cd_parentcnid
= kRootParID
;
148 bzero(&cnattr
, sizeof(cnattr
));
150 cnattr
.ca_mode
= S_IFREG
;
151 bzero(&fork
, sizeof(fork
));
154 * Set up Extents B-tree vnode
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
;
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
));
178 VOP_UNLOCK(vcb
->extentsRefNum
, 0, p
);
183 * Set up Catalog B-tree vnode...
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
;
199 error
= hfs_getnewvnode(hfsmp
, NULL
, &cndesc
, 0, &cnattr
, &fork
,
200 &vcb
->catalogRefNum
);
202 VOP_UNLOCK(vcb
->extentsRefNum
, 0, p
);
205 error
= MacToVFSError(BTOpenPath(VTOF(vcb
->catalogRefNum
),
206 (KeyCompareProcPtr
)CompareCatalogKeys
,
207 GetBTreeBlock
, ReleaseBTreeBlock
,
208 ExtendBTreeFile
, SetBTreeBlockSize
));
210 VOP_UNLOCK(vcb
->catalogRefNum
, 0, p
);
211 VOP_UNLOCK(vcb
->extentsRefNum
, 0, p
);
215 /* mark the volume dirty (clear clean unmount bit) */
216 vcb
->vcbAtrb
&= ~kHFSVolumeUnmountedMask
;
219 * all done with b-trees so we can unlock now...
221 VOP_UNLOCK(vcb
->catalogRefNum
, 0, p
);
222 VOP_UNLOCK(vcb
->extentsRefNum
, 0, p
);
224 if ( error
== noErr
)
226 if ( !(vcb
->vcbAtrb
& kHFSVolumeHardwareLockMask
) ) // if the disk is not write protected
228 MarkVCBDirty( vcb
); // mark VCB dirty so it will be written
233 //-- Release any resources allocated so far before exiting with an error:
235 ReleaseMetaFileVNode(vcb
->catalogRefNum
);
236 ReleaseMetaFileVNode(vcb
->extentsRefNum
);
242 //*******************************************************************************
243 // Routine: hfs_MountHFSPlusVolume
246 //*******************************************************************************
248 OSErr
hfs_MountHFSPlusVolume(struct hfsmount
*hfsmp
, HFSPlusVolumeHeader
*vhp
,
249 off_t embeddedOffset
, u_int64_t disksize
, struct proc
*p
)
251 register ExtendedVCB
*vcb
;
252 struct cat_desc cndesc
;
253 struct cat_attr cnattr
;
257 if (SWAP_BE16(vhp
->signature
) != kHFSPlusSigWord
||
258 SWAP_BE16(vhp
->version
) != kHFSPlusVersion
)
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)
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)
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
)) {
277 * The VolumeHeader seems OK: transfer info from it into VCB
278 * Note - the VCB starts out clear (all zeros)
280 vcb
= HFSTOVCB(hfsmp
);
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
);
292 /* copy 32 bytes of Finder info */
293 bcopy(vhp
->finderInfo
, vcb
->vcbFndrInfo
, sizeof(vhp
->finderInfo
));
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 */
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
);
309 vcb
->hfsPlusIOPosOffset
= embeddedOffset
;
311 /* Default to no free block reserve */
312 vcb
->reserveBlocks
= 0;
315 * Update the logical block size in the mount struct
316 * (currently set up from the wrapper MDB) using the
317 * new blocksize value:
319 hfsmp
->hfs_logBlockSize
= BestBlockSizeFit(vcb
->blockSize
, MAXBSIZE
, hfsmp
->hfs_phys_block_size
);
320 vcb
->vcbVBMIOSize
= min(vcb
->blockSize
, MAXPHYSIO
);
322 bzero(&cndesc
, sizeof(cndesc
));
323 cndesc
.cd_parentcnid
= kRootParID
;
324 bzero(&cnattr
, sizeof(cnattr
));
326 cnattr
.ca_mode
= S_IFREG
;
329 * Set up Extents B-tree vnode
331 cndesc
.cd_nameptr
= hfs_extname
;
332 cndesc
.cd_namelen
= strlen(hfs_extname
);
333 cndesc
.cd_cnid
= cnattr
.ca_fileid
= kHFSExtentsFileID
;
335 SWAP_HFS_PLUS_FORK_DATA (&vhp
->extentsFile
);
336 cnattr
.ca_blocks
= vhp
->extentsFile
.totalBlocks
;
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
);
343 if (retval
) goto ErrorExit
;
344 retval
= MacToVFSError(BTOpenPath(VTOF(vcb
->extentsRefNum
),
345 (KeyCompareProcPtr
) CompareExtentKeysPlus
,
346 GetBTreeBlock
, ReleaseBTreeBlock
,
347 ExtendBTreeFile
, SetBTreeBlockSize
));
349 VOP_UNLOCK(vcb
->extentsRefNum
, 0, p
);
354 * Set up Catalog B-tree vnode
356 cndesc
.cd_nameptr
= hfs_catname
;
357 cndesc
.cd_namelen
= strlen(hfs_catname
);
358 cndesc
.cd_cnid
= cnattr
.ca_fileid
= kHFSCatalogFileID
;
360 SWAP_HFS_PLUS_FORK_DATA(&vhp
->catalogFile
);
361 cnattr
.ca_blocks
= vhp
->catalogFile
.totalBlocks
;
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
);
368 VOP_UNLOCK(vcb
->extentsRefNum
, 0, p
);
371 retval
= MacToVFSError(BTOpenPath(VTOF(vcb
->catalogRefNum
),
372 (KeyCompareProcPtr
) CompareExtendedCatalogKeys
,
373 GetBTreeBlock
, ReleaseBTreeBlock
,
374 ExtendBTreeFile
, SetBTreeBlockSize
));
376 VOP_UNLOCK(vcb
->catalogRefNum
, 0, p
);
377 VOP_UNLOCK(vcb
->extentsRefNum
, 0, p
);
382 * Set up Allocation file vnode
384 cndesc
.cd_nameptr
= hfs_vbmname
;
385 cndesc
.cd_namelen
= strlen(hfs_vbmname
);
386 cndesc
.cd_cnid
= cnattr
.ca_fileid
= kHFSAllocationFileID
;
388 SWAP_HFS_PLUS_FORK_DATA(&vhp
->allocationFile
);
389 cnattr
.ca_blocks
= vhp
->allocationFile
.totalBlocks
;
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
);
396 VOP_UNLOCK(vcb
->catalogRefNum
, 0, p
);
397 VOP_UNLOCK(vcb
->extentsRefNum
, 0, p
);
401 /* Pick up volume name and create date */
402 retval
= cat_idlookup(hfsmp
, kHFSRootFolderID
, &cndesc
, &cnattr
, NULL
);
404 VOP_UNLOCK(vcb
->allocationsRefNum
, 0, p
);
405 VOP_UNLOCK(vcb
->catalogRefNum
, 0, p
);
406 VOP_UNLOCK(vcb
->extentsRefNum
, 0, p
);
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
);
414 /* mark the volume dirty (clear clean unmount bit) */
415 vcb
->vcbAtrb
&= ~kHFSVolumeUnmountedMask
;
418 * all done with metadata files so we can unlock now...
420 VOP_UNLOCK(vcb
->allocationsRefNum
, 0, p
);
421 VOP_UNLOCK(vcb
->catalogRefNum
, 0, p
);
422 VOP_UNLOCK(vcb
->extentsRefNum
, 0, p
);
424 /* setup private/hidden directory for unlinked files */
425 hfsmp
->hfs_private_metadata_dir
= FindMetaDataDirectory(vcb
);
427 if ( !(vcb
->vcbAtrb
& kHFSVolumeHardwareLockMask
) ) // if the disk is not write protected
429 MarkVCBDirty( vcb
); // mark VCB dirty so it will be written
436 * A fatal error occured and the volume cannot be mounted
437 * release any resources that we aquired...
440 InvalidateCatalogCache(vcb
);
441 ReleaseMetaFileVNode(vcb
->allocationsRefNum
);
442 ReleaseMetaFileVNode(vcb
->catalogRefNum
);
443 ReleaseMetaFileVNode(vcb
->extentsRefNum
);
450 * ReleaseMetaFileVNode
454 static void ReleaseMetaFileVNode(struct vnode
*vp
)
458 if (vp
&& (fp
= VTOF(vp
))) {
459 if (fp
->fcbBTCBPtr
!= NULL
)
460 (void) BTClosePath(fp
);
462 /* release the node even if BTClosePath fails */
469 /*************************************************************
471 * Unmounts a hfs volume.
472 * At this point vflush() has been called (to dump all non-metadata files)
474 *************************************************************/
476 short hfsUnmount( register struct hfsmount
*hfsmp
, struct proc
*p
)
478 ExtendedVCB
*vcb
= HFSTOVCB(hfsmp
);
481 InvalidateCatalogCache( vcb
);
483 if (vcb
->vcbSigWord
== kHFSPlusSigWord
)
484 ReleaseMetaFileVNode(vcb
->allocationsRefNum
);
486 ReleaseMetaFileVNode(vcb
->catalogRefNum
);
487 ReleaseMetaFileVNode(vcb
->extentsRefNum
);
494 * Some 3rd party kexts link against hfs_getcatalog so keep a stub for now.
497 hfs_getcatalog(void *p1
, u_long p2
, void *p3
, short p4
, void *p5
)
503 int overflow_extents(struct filefork
*fp
)
507 if (VTOVCB(FTOV(fp
))->vcbSigWord
== kHFSPlusSigWord
) {
508 if (fp
->ff_extents
[7].blockCount
== 0)
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
;
520 if (fp
->ff_extents
[2].blockCount
== 0)
523 blocks
= fp
->ff_extents
[0].blockCount
+
524 fp
->ff_extents
[1].blockCount
+
525 fp
->ff_extents
[2].blockCount
;
528 return (fp
->ff_blocks
> blocks
);
532 /* __private_extern__ */
534 hfs_metafilelocking(struct hfsmount
*hfsmp
, u_long fileID
, u_int flags
, struct proc
*p
)
537 struct vnode
*vp
= NULL
;
538 int numOfLockedBuffs
;
541 vcb
= HFSTOVCB(hfsmp
);
544 case kHFSExtentsFileID
:
545 vp
= vcb
->extentsRefNum
;
548 case kHFSCatalogFileID
:
549 vp
= vcb
->catalogRefNum
;
552 case kHFSAllocationFileID
:
553 /* bitmap is covered by Extents B-tree locking */
556 panic("hfs_lockmetafile: invalid fileID");
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
;
564 (void) BTGetLastSync((FCB
*)VTOF(vp
), &lastfsync
);
566 numOfLockedBuffs
= count_lock_queue();
567 if ((numOfLockedBuffs
> kMaxLockedMetaBuffers
) || ((numOfLockedBuffs
>1) && ((tv
.tv_sec
- lastfsync
) > kMaxSecsForFsync
))) {
568 hfs_btsync(vp
, HFS_SYNCTRANS
);
574 retval
= lockmgr(&VTOC(vp
)->c_lock
, flags
, &vp
->v_interlock
, p
);
582 * Check to see if a vnode is locked in the current context
583 * This is to be used for debugging purposes only!!
586 void RequireFileLock(FileReference vp
, int shareable
)
588 struct lock__bsd__
*lkp
;
593 pid
= current_proc()->p_pid
;
594 self
= (void *) current_thread();
595 lkp
= &VTOC(vp
)->c_lock
;
597 simple_lock(&lkp
->lk_interlock
);
599 if (shareable
&& (lkp
->lk_sharecount
> 0) && (lkp
->lk_lockholder
== LK_NOPROC
))
601 else if ((lkp
->lk_exclusivecount
> 0) && (lkp
->lk_lockholder
== pid
) && (lkp
->lk_lockthread
== self
))
604 simple_unlock(&lkp
->lk_interlock
);
607 switch (VTOC(vp
)->c_fileid
) {
609 DEBUG_BREAK_MSG((" #\n # RequireFileLock: extent btree vnode not locked! v: 0x%08X\n #\n", (u_int
)vp
));
613 DEBUG_BREAK_MSG((" #\n # RequireFileLock: catalog btree vnode not locked! v: 0x%08X\n #\n", (u_int
)vp
));
617 DEBUG_BREAK_MSG((" #\n # RequireFileLock: file (%d) not locked! v: 0x%08X\n #\n", VTOC(vp
)->c_fileid
, (u_int
)vp
));
626 * There are three ways to qualify for ownership rights on an object:
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.
640 hfs_owner_rights(struct hfsmount
*hfsmp
, uid_t cnode_uid
, struct ucred
*cred
,
641 struct proc
*p
, int invokesuperuserstatus
)
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] */
657 unsigned long BestBlockSizeFit(unsigned long allocationBlockSize
,
658 unsigned long blockSizeLimit
,
659 unsigned long baseMultiple
) {
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.
664 int baseBlockCount
, blockCount
;
665 unsigned long trialBlockSize
;
667 if (allocationBlockSize
% baseMultiple
!= 0) {
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!
672 return 512; /* Hope for the best */
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
;
681 /* No clear winner exists: pick the largest even fraction <= MAXBSIZE: */
682 baseBlockCount
= allocationBlockSize
/ baseMultiple
; /* Now guaranteed to be an even multiple */
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
;
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 ... */
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.
707 FindMetaDataDirectory(ExtendedVCB
*vcb
)
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};
717 if (vcb
->vcbSigWord
!= kHFSPlusSigWord
)
720 hfsmp
= VCBTOHFS(vcb
);
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
;
729 /* Lock catalog b-tree */
730 error
= hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_EXCLUSIVE
, current_proc());
734 error
= cat_lookup(hfsmp
, &hfsmp
->hfs_privdir_desc
, 0, NULL
,
735 &hfsmp
->hfs_privdir_attr
, NULL
);
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());
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
;
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
);
762 error
= cat_create(hfsmp
, &hfsmp
->hfs_privdir_desc
,
763 &hfsmp
->hfs_privdir_attr
, &out_desc
);
765 /* Unlock catalog b-tree */
766 (void) hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_RELEASE
, current_proc());
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
;
775 if (VFS_ROOT(HFSTOVFS(hfsmp
), &dvp
) == 0) {
777 dcp
->c_childhint
= out_desc
.cd_hint
;
780 dcp
->c_flag
|= C_CHANGE
| C_UPDATE
;
782 (void) VOP_UPDATE(dvp
, &tv
, &tv
, 0);
785 hfs_volupdate(hfsmp
, VOL_MKDIR
, 1);
786 cat_releasedesc(&out_desc
);
788 return (out_desc
.cd_cnid
);
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
798 GetLogicalBlockSize(struct vnode
*vp
)
800 u_int32_t logBlockSize
;
802 DBG_ASSERT(vp
!= NULL
);
804 /* start with default */
805 logBlockSize
= VTOHFS(vp
)->hfs_logBlockSize
;
807 if (vp
->v_flag
& VSYSTEM
) {
808 if (VTOF(vp
)->fcbBTCBPtr
!= NULL
) {
809 BTreeInfoRec bTreeInfo
;
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??
819 (void) BTGetInformation (VTOF(vp
), kBTreeInfoVersion
, &bTreeInfo
);
821 logBlockSize
= bTreeInfo
.nodeSize
;
823 } else if (VTOC(vp
)->c_fileid
== kHFSAllocationFileID
) {
824 logBlockSize
= VTOVCB(vp
)->vcbVBMIOSize
;
828 DBG_ASSERT(logBlockSize
> 0);
835 hfs_freeblks(struct hfsmount
* hfsmp
, int wantreserve
)
837 struct vcb_t
*vcb
= HFSTOVCB(hfsmp
);
840 freeblks
= vcb
->freeBlocks
;
842 if (freeblks
> vcb
->reserveBlocks
)
843 freeblks
-= vcb
->reserveBlocks
;
848 freeblks
-= vcb
->loanedBlocks
;
853 * Map HFS Common errors (negative) to BSD error codes (positive).
854 * Positive errors (ie BSD errors) are passed through unchanged.
856 short MacToVFSError(OSErr err
)
862 case dskFulErr
: /* -34 */
863 case btNoSpaceAvail
: /* -32733 */
864 case fxOvFlErr
: /* -32750 */
865 return ENOSPC
; /* +28 */
867 case btBadNode
: /* -32731 */
870 case memFullErr
: /* -108 */
871 return ENOMEM
; /* +12 */
873 case cmExists
: /* -32718 */
874 case btExists
: /* -32734 */
875 return EEXIST
; /* +17 */
877 case cmNotFound
: /* -32719 */
878 case btNotFound
: /* -32735 */
879 return ENOENT
; /* 28 */
881 case cmNotEmpty
: /* -32717 */
882 return ENOTEMPTY
; /* 66 */
884 case cmFThdDirErr
: /* -32714 */
885 return EISDIR
; /* 21 */
887 case fxRangeErr
: /* -32751 */
890 case bdNamErr
: /* -37 */
891 return ENAMETOOLONG
; /* 63 */
893 case paramErr
: /* -50 */
894 case fileBoundsErr
: /* -1309 */
895 return EINVAL
; /* +22 */
897 case fsBTBadNodeSize
:
907 * Get the directory entry name hint for a given index.
908 * The directory cnode (dcp) must be locked.
912 hfs_getnamehint(struct cnode
*dcp
, int index
)
914 struct hfs_index
*entry
;
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
);
930 * Save a directory entry name hint for a given index.
931 * The directory cnode (dcp) must be locked.
935 hfs_savenamehint(struct cnode
*dcp
, int index
, const char * namehint
)
937 struct hfs_index
*entry
;
941 len
= strlen(namehint
);
942 MALLOC(entry
, struct hfs_index
*, len
+ sizeof(struct hfs_index
),
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
);
952 * Release the directory entry name hint for a given index.
953 * The directory cnode (dcp) must be locked.
957 hfs_relnamehint(struct cnode
*dcp
, int index
)
959 struct hfs_index
*entry
;
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
,
977 * Release all directory entry name hints.
981 hfs_relnamehints(struct cnode
*dcp
)
983 struct hfs_index
*entry
;
984 struct hfs_index
*next
;
986 if (!SLIST_EMPTY(&dcp
->c_indexlist
)) {
987 for(entry
= SLIST_FIRST(&dcp
->c_indexlist
);
990 next
= SLIST_NEXT(entry
, hi_link
);
991 SLIST_REMOVE(&dcp
->c_indexlist
, entry
, hfs_index
, hi_link
);