/*
- * Copyright (c) 2000-2001 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2000-2010 Apple Inc. All rights reserved.
*
- * @APPLE_LICENSE_HEADER_START@
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
- * The contents of this file constitute Original Code as defined in and
- * are subject to the Apple Public Source License Version 1.1 (the
- * "License"). You may not use this file except in compliance with the
- * License. Please obtain a copy of the License at
- * http://www.apple.com/publicsource and read it before using this file.
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. The rights granted to you under the License
+ * may not be used to create, or enable the creation or redistribution of,
+ * unlawful or unlicensed copies of an Apple operating system, or to
+ * circumvent, violate, or enable the circumvention or violation of, any
+ * terms of an Apple operating system software license agreement.
*
- * This Original Code and all software distributed under the License are
- * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
- * License for the specific language governing rights and limitations
- * under the License.
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
*
- * @APPLE_LICENSE_HEADER_END@
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
*/
/* @(#)hfs_vfsutils.c 4.0
*
-* (c) 1997-2000 Apple Computer, Inc. All Rights Reserved
+* (c) 1997-2002 Apple Computer, Inc. All Rights Reserved
*
* hfs_vfsutils.c -- Routines that go between the HFS layer and the VFS.
*
-* Change History (most recent first):
-*
-* 22-Jan-2000 Don Brady Remove calls to MountCheck.
-* 7-Sep-1999 Don Brady Add HFS Plus hard-link support.
-* 25-Aug-1999 Don Brady Dont't use vcbAlBlSt for HFS plus volumes (2350009).
-* 9-Aug-1999 Pat Dirks Added support for ATTR_VOL_ENCODINGSUSED [#2357367].
-* 16-Jul-1999 Pat Dirks Fixed PackCommonCatalogInfoAttributeBlock to return full range of possible vnode types [#2317604]
-* 15-Jun-1999 Pat Dirks Added support for return of mounted device in hfs_getattrlist [#2345297].
-* 9-Jun-1999 Don Brady Cleanup vcb accesses in hfs_MountHFSVolume.
-* 3-Jun-1999 Don Brady Remove references to unused/legacy vcb fields (eg vcbXTClpSiz).
-* 21-May-1999 Don Brady Add call to hfs_vinit in hfsGet to support mknod.
-* 6-Apr-1999 Don Brady Fixed de-reference of NULL dvp in hfsGet.
-* 22-Mar-1999 Don Brady Add support for UFS delete semantics.
-* 1-Mar-1999 Scott Roberts Dont double MALLOC on long names.
-* 23-Feb-1999 Pat Dirks Change incrementing of meta refcount to be done BEFORE lock is acquired.
-* 2-Feb-1999 Pat Dirks For volume ATTR_CMN_SCRIPT use vcb->volumeNameEncodingHint instead of 0.
-* 10-Mar-1999 Don Brady Removing obsolete code.
-* 2-Feb-1999 Don Brady For volume ATTR_CMN_SCRIPT use vcb->volumeNameEncodingHint instead of 0.
-* 18-Jan-1999 Pat Dirks Changed CopyCatalogToHFSNode to start with ACCESSPERMS instead of adding
-* write access only for unlocked files (now handled via IMMUTABLE setting)
-* 7-Dec-1998 Pat Dirks Changed PackCatalogInfoFileAttributeBlock to return proper I/O block size.
-* 7-Dec-1998 Don Brady Pack the real text encoding instead of zero.
-* 16-Dec-1998 Don Brady Use the root's crtime intead of vcb create time for getattrlist.
-* 16-Dec-1998 Don Brady Use the root's crtime intead of vcb create time for getattrlist.
-* 2-Dec-1998 Scott Roberts Copy the mdbVN correctly into the vcb.
-* 3-Dec-1998 Pat Dirks Added support for ATTR_VOL_MOUNTFLAGS.
-* 20-Nov-1998 Don Brady Add support for UTF-8 names.
-* 18-Nov-1998 Pat Dirks Changed UnpackCommonAttributeBlock to call wait for hfs_chflags to update catalog entry when changing flags
-* 13-Nov-1998 Pat Dirks Changed BestBlockSizeFit to try PAGE_SIZE only and skip check for MAXBSIZE.
-* 10-Nov-1998 Pat Dirks Changed CopyCatalogToHFSNode to ensure consistency between lock flag and IMMUTABLE bits.
-* 10-Nov-1998 Pat Dirks Added MapFileOffset(), LogicalBlockSize() and UpdateBlockMappingTable() routines.
-* 18-Nov-1998 Pat Dirks Changed PackVolAttributeBlock to return proper logical block size
-* for ATTR_VOL_IOBLOCKSIZE attribute.
-* 3-Nov-1998 Umesh Vaishampayan Changes to deal with "struct timespec"
-* change in the kernel.
-* 23-Sep-1998 Don Brady In UnpackCommonAttributeBlock simplified setting of gid, uid and mode.
-* 10-Nov-1998 Pat Dirks Added MapFileOffset(), LogicalBlockSize() and UpdateBlockMappingTable() routines.
-* 17-Sep-1998 Pat Dirks Changed BestBlockSizeFit to try MAXBSIZE and PAGE_SIZE first.
-* 8-Sep-1998 Don Brady Fix CopyVNodeToCatalogNode to use h_mtime for contentModDate (instead of h_ctime).
-* 4-Sep-1998 Pat Dirks Added BestBlockSizeFit routine.
-* 18-Aug-1998 Don Brady Change DEBUG_BREAK_MSG to a DBG_UTILS in MacToVFSError (radar #2262802).
-* 30-Jun-1998 Don Brady Add calls to MacToVFSError to hfs/hfsplus mount routines (for radar #2249539).
-* 22-Jun-1998 Don Brady Add more error cases to MacToVFSError; all HFS Common errors are negative.
-* Changed hfsDelete to call DeleteFile for files.
-* 4-Jun-1998 Pat Dirks Changed incorrect references to 'vcbAlBlkSize' to 'blockSize';
-* Added hfsCreateFileID.
-* 4-Jun-1998 Don Brady Add hfsMoveRename to replace hfsMove and hfsRename. Use VPUT/VRELE macros
-* instead of vput/vrele to catch bad ref counts.
-* 28-May-1998 Pat Dirks Adjusted for change in definition of ATTR_CMN_NAME and removed ATTR_CMN_RAWDEVICE.
-* 7-May-1998 Don Brady Added check for NULL vp to hfs_metafilelocking (radar #2233832).
-* 24-Apr-1998 Pat Dirks Fixed AttributeBlockSize to return only length of variable attribute block.
-* 4/21/1998 Don Brady Add SUPPORTS_MAC_ALIASES conditional (for radar #2225419).
-* 4/21/1998 Don Brady Map cmNotEmpty errors to ENOTEMPTY (radar #2229259).
-* 4/21/1998 Don Brady Fix up time/date conversions.
-* 4/20/1998 Don Brady Remove course-grained hfs metadata locking.
-* 4/18/1998 Don Brady Add VCB locking.
-* 4/17/1998 Pat Dirks Fixed PackFileAttributeBlock to return more up-to-date EOF/PEOF info from vnode.
-* 4/15/1998 Don Brady Add hasOverflowExtents and hfs_metafilelocking. Use ExtendBTreeFile instead
-* of SetEndOfForkProc. Set forktype for system files.
-* 4/14/1998 Deric Horn PackCatalogInfoAttributeBlock(), and related packing routines to
-* pack attribute data given hfsCatalogInfo, without the objects vnode;
-* 4/14/1998 Scott Roberts Add execute priviledges to all hfs objects.
-* 4/9/1998 Don Brady Add MDB/VolumeHeader flushing to hfsUnmount;
-* 4/8/1998 Don Brady Make sure vcbVRefNum field gets initialized (use MAKE_VREFNUM).
-* 4/6/1998 Don Brady Removed calls to CreateVolumeCatalogCache (obsolete).
-* 4/06/1998 Scott Roberts Added complex file support.
-* 4/02/1998 Don Brady UpdateCatalogNode now takes parID and name as input.
-* 3/31/1998 Don Brady Sync up with final HFSVolumes.h header file.
-* 3/31/1998 Don Brady Check result from UFSToHFSStr to make sure hfs/hfs+ names are not greater
-* than 31 characters.
-* 3/30/1998 Don Brady In InitMetaFileVNode set VSYSTEM bit in vnode's v_flag.
-* 3/26/1998 Don Brady Cleaned up hfs_MountXXX routines. Removed CloseBtreeFile and OpenBTreeFile.
-* Simplified hfsUnmount (removed MacOS specific code).
-* 3/17/1998 Don Brady AttributeBlockSize calculation did not account for the size field (4bytes).
-* PackVolCommonAttributes and PackCommonAttributeBlock for ATTR_CMN_NAME
-* were not setting up the name correctly.
-* 3/17/1998 Don Brady Changed CreateCatalogNode interface to take kCatalogFolderNode and
-* kCatalogFileNode as type input. Also, force MountCheck to always run.
-* 12-nov-1997 Scott Roberts Initially created file.
-* 17-Mar-98 ser Broke out and created CopyCatalogToHFSNode()
-*
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/stat.h>
-#include <sys/attr.h>
#include <sys/mount.h>
-#include <sys/lock.h>
+#include <sys/mount_internal.h>
#include <sys/buf.h>
+#include <sys/buf_internal.h>
#include <sys/ubc.h>
#include <sys/unistd.h>
+#include <sys/utfconv.h>
+#include <sys/kauth.h>
+#include <sys/fcntl.h>
+#include <sys/fsctl.h>
+#include <sys/vnode_internal.h>
+#include <kern/clock.h>
+
+#include <libkern/OSAtomic.h>
#include "hfs.h"
+#include "hfs_catalog.h"
#include "hfs_dbg.h"
#include "hfs_mount.h"
#include "hfs_endian.h"
+#include "hfs_cnode.h"
+#include "hfs_fsctl.h"
#include "hfscommon/headers/FileMgrInternal.h"
#include "hfscommon/headers/BTreesInternal.h"
#include "hfscommon/headers/HFSUnicodeWrappers.h"
-#define SUPPORTS_MAC_ALIASES 0
-#define kMaxSecsForFsync 5
-
-#define BYPASSBLOCKINGOPTIMIZATION 0
-
-extern int (**hfs_vnodeop_p)(void *);
-extern int (**hfs_specop_p)(void *);
-extern int (**hfs_fifoop_p)(void *);
-extern int count_lock_queue __P((void));
-extern uid_t console_user;
-
-OSErr ValidMasterDirectoryBlock( HFSMasterDirectoryBlock *mdb );
-
-/* Externs from vhash */
-extern void hfs_vhashins_sibling(dev_t dev, UInt32 nodeID, struct hfsnode *hp, struct hfsfilemeta **fm);
-extern void hfs_vhashins(dev_t dev, UInt32 nodeID,struct hfsnode *hp);
-extern struct vnode *hfs_vhashget(dev_t dev, UInt32 nodeID, UInt8 forkType);
-
-extern int hfs_vinit( struct mount *mntp, int (**specops)(void *), int (**fifoops)(), struct vnode **vpp);
-
-extern OSErr GetVolumeNameFromCatalog(ExtendedVCB *vcb);
-
-static int InitMetaFileVNode(struct vnode *vp, off_t eof, u_long clumpSize, const HFSPlusExtentRecord extents,
- HFSCatalogNodeID fileID, void * keyCompareProc);
-
static void ReleaseMetaFileVNode(struct vnode *vp);
+static int hfs_late_journal_init(struct hfsmount *hfsmp, HFSPlusVolumeHeader *vhp, void *_args);
-static void RemovedMetaDataDirectory(ExtendedVCB *vcb);
+static u_int32_t hfs_hotfile_freeblocks(struct hfsmount *);
-void CopyCatalogToObjectMeta(struct hfsCatalogInfo *catalogInfo, struct vnode *vp, struct hfsfilemeta *fm);
-void CopyCatalogToFCB(struct hfsCatalogInfo *catalogInfo, struct vnode *vp);
-void hfs_name_CatToMeta(CatalogNodeData *nodeData, struct hfsfilemeta *fm);
-u_int32_t GetLogicalBlockSize(struct vnode *vp);
+#define HFS_MOUNT_DEBUG 1
-/* BTree accessor routines */
-extern OSStatus GetBTreeBlock(FileReference vp, UInt32 blockNum, GetBlockOptions options, BlockDescriptor *block);
-extern OSStatus SetBTreeBlockSize(FileReference vp, ByteCount blockSize, ItemCount minBlockCount);
-extern OSStatus ExtendBTreeFile(FileReference vp, FSSize minEOF, FSSize maxEOF);
-extern OSStatus ReleaseBTreeBlock(FileReference vp, BlockDescPtr blockPtr, ReleaseBlockOptions options);
//*******************************************************************************
// Note: Finder information in the HFS/HFS+ metadata are considered opaque and
//
//
//*******************************************************************************
+unsigned char hfs_catname[] = "Catalog B-tree";
+unsigned char hfs_extname[] = "Extents B-tree";
+unsigned char hfs_vbmname[] = "Volume Bitmap";
+unsigned char hfs_attrname[] = "Attribute B-tree";
+unsigned char hfs_startupname[] = "Startup File";
+
OSErr hfs_MountHFSVolume(struct hfsmount *hfsmp, HFSMasterDirectoryBlock *mdb,
- u_long sectors, struct proc *p)
+ __unused struct proc *p)
{
- ExtendedVCB *vcb = HFSTOVCB(hfsmp);
- struct vnode *tmpvnode;
- OSErr err;
- HFSPlusExtentRecord extents;
+ ExtendedVCB *vcb = HFSTOVCB(hfsmp);
+ int error;
ByteCount utf8chars;
- DBG_FUNC_NAME("hfs_MountHFSVolume");
- DBG_PRINT_FUNC_NAME();
-
- if (hfsmp == nil || mdb == nil) /* exit if bad paramater */
+ struct cat_desc cndesc;
+ struct cat_attr cnattr;
+ struct cat_fork fork;
+ int newvnode_flags = 0;
+
+ /* Block size must be a multiple of 512 */
+ if (SWAP_BE32(mdb->drAlBlkSiz) == 0 ||
+ (SWAP_BE32(mdb->drAlBlkSiz) & 0x01FF) != 0)
return (EINVAL);
- err = ValidMasterDirectoryBlock( mdb ); /* make sure this is an HFS disk */
- if (err)
- return MacToVFSError(err);
-
/* don't mount a writeable volume if its dirty, it must be cleaned by fsck_hfs */
- if ((hfsmp->hfs_fs_ronly == 0) && ((SWAP_BE16 (mdb->drAtrb) & kHFSVolumeUnmountedMask) == 0))
+ if (((hfsmp->hfs_flags & HFS_READ_ONLY) == 0) &&
+ ((SWAP_BE16(mdb->drAtrb) & kHFSVolumeUnmountedMask) == 0)) {
return (EINVAL);
-
+ }
+ hfsmp->hfs_flags |= HFS_STANDARD;
/*
* The MDB seems OK: transfer info from it into VCB
* Note - the VCB starts out clear (all zeros)
*
*/
- vcb->vcbVRefNum = MAKE_VREFNUM(hfsmp->hfs_raw_dev);
-
- vcb->vcbSigWord = SWAP_BE16 (mdb->drSigWord);
- vcb->vcbCrDate = LocalToUTC (SWAP_BE32 (mdb->drCrDate));
+ vcb->vcbSigWord = SWAP_BE16 (mdb->drSigWord);
+ vcb->hfs_itime = to_bsd_time(LocalToUTC(SWAP_BE32(mdb->drCrDate)));
vcb->localCreateDate = SWAP_BE32 (mdb->drCrDate);
- vcb->vcbLsMod = LocalToUTC (SWAP_BE32 (mdb->drLsMod));
- vcb->vcbAtrb = SWAP_BE16 (mdb->drAtrb);
- vcb->vcbNmFls = SWAP_BE16 (mdb->drNmFls);
- vcb->vcbVBMSt = SWAP_BE16 (mdb->drVBMSt);
- vcb->nextAllocation = SWAP_BE16 (mdb->drAllocPtr);
- vcb->totalBlocks = SWAP_BE16 (mdb->drNmAlBlks);
- vcb->blockSize = SWAP_BE32 (mdb->drAlBlkSiz);
- vcb->vcbClpSiz = SWAP_BE32 (mdb->drClpSiz);
- vcb->vcbAlBlSt = SWAP_BE16 (mdb->drAlBlSt);
- vcb->vcbNxtCNID = SWAP_BE32 (mdb->drNxtCNID);
- vcb->freeBlocks = SWAP_BE16 (mdb->drFreeBks);
- vcb->vcbVolBkUp = LocalToUTC (SWAP_BE32 (mdb->drVolBkUp));
- vcb->vcbWrCnt = SWAP_BE32 (mdb->drWrCnt);
- vcb->vcbNmRtDirs = SWAP_BE16 (mdb->drNmRtDirs);
- vcb->vcbFilCnt = SWAP_BE32 (mdb->drFilCnt);
- vcb->vcbDirCnt = SWAP_BE32 (mdb->drDirCnt);
+ vcb->vcbLsMod = to_bsd_time(LocalToUTC(SWAP_BE32(mdb->drLsMod)));
+ vcb->vcbAtrb = SWAP_BE16 (mdb->drAtrb);
+ vcb->vcbNmFls = SWAP_BE16 (mdb->drNmFls);
+ vcb->vcbVBMSt = SWAP_BE16 (mdb->drVBMSt);
+ vcb->nextAllocation = SWAP_BE16 (mdb->drAllocPtr);
+ vcb->totalBlocks = SWAP_BE16 (mdb->drNmAlBlks);
+ vcb->allocLimit = vcb->totalBlocks;
+ vcb->blockSize = SWAP_BE32 (mdb->drAlBlkSiz);
+ vcb->vcbClpSiz = SWAP_BE32 (mdb->drClpSiz);
+ vcb->vcbAlBlSt = SWAP_BE16 (mdb->drAlBlSt);
+ vcb->vcbNxtCNID = SWAP_BE32 (mdb->drNxtCNID);
+ vcb->freeBlocks = SWAP_BE16 (mdb->drFreeBks);
+ vcb->vcbVolBkUp = to_bsd_time(LocalToUTC(SWAP_BE32(mdb->drVolBkUp)));
+ vcb->vcbWrCnt = SWAP_BE32 (mdb->drWrCnt);
+ vcb->vcbNmRtDirs = SWAP_BE16 (mdb->drNmRtDirs);
+ vcb->vcbFilCnt = SWAP_BE32 (mdb->drFilCnt);
+ vcb->vcbDirCnt = SWAP_BE32 (mdb->drDirCnt);
bcopy(mdb->drFndrInfo, vcb->vcbFndrInfo, sizeof(vcb->vcbFndrInfo));
- vcb->nextAllocation = SWAP_BE16 ( mdb->drAllocPtr); /* Duplicate?!?!?! */
- vcb->encodingsBitmap = 0;
- vcb->vcbWrCnt++; /* Compensate for write of MDB on last flush */
- /*
- * Copy the drVN field, which is a Pascal String to the vcb, which is a cstring
- */
+ if ((hfsmp->hfs_flags & HFS_READ_ONLY) == 0)
+ vcb->vcbWrCnt++; /* Compensate for write of MDB on last flush */
/* convert hfs encoded name into UTF-8 string */
- err = hfs_to_utf8(vcb, mdb->drVN, NAME_MAX, &utf8chars, vcb->vcbVN);
+ error = hfs_to_utf8(vcb, mdb->drVN, NAME_MAX, &utf8chars, vcb->vcbVN);
/*
* When an HFS name cannot be encoded with the current
* volume encoding we use MacRoman as a fallback.
*/
- if (err || (utf8chars == 0))
- (void) mac_roman_to_utf8(mdb->drVN, NAME_MAX, &utf8chars, vcb->vcbVN);
-
- vcb->altIDSector = sectors - 2;
-
- // Initialize our dirID/nodePtr cache associated with this volume.
- err = InitMRUCache( sizeof(UInt32), kDefaultNumMRUCacheBlocks, &(vcb->hintCachePtr) );
- ReturnIfError( err );
+ if (error || (utf8chars == 0)) {
+ error = mac_roman_to_utf8(mdb->drVN, NAME_MAX, &utf8chars, vcb->vcbVN);
+ /* If we fail to encode to UTF8 from Mac Roman, the name is bad. Deny the mount */
+ if (error) {
+ goto MtVolErr;
+ }
+ }
- hfsmp->hfs_logBlockSize = BestBlockSizeFit(vcb->blockSize, MAXBSIZE, hfsmp->hfs_phys_block_size);
+ hfsmp->hfs_logBlockSize = BestBlockSizeFit(vcb->blockSize, MAXBSIZE, hfsmp->hfs_logical_block_size);
vcb->vcbVBMIOSize = kHFSBlockSize;
- // XXX PPD: Should check here for hardware lock flag and set flags in VCB/MP appropriately
- VCB_LOCK_INIT(vcb);
+ hfsmp->hfs_alt_id_sector = HFS_ALT_SECTOR(hfsmp->hfs_logical_block_size,
+ hfsmp->hfs_logical_block_count);
+
+ bzero(&cndesc, sizeof(cndesc));
+ cndesc.cd_parentcnid = kHFSRootParentID;
+ cndesc.cd_flags |= CD_ISMETA;
+ bzero(&cnattr, sizeof(cnattr));
+ cnattr.ca_linkcount = 1;
+ cnattr.ca_mode = S_IFREG;
+ bzero(&fork, sizeof(fork));
/*
- * Set up Extents B-tree vnode...
- */
- err = GetInitializedVNode(hfsmp, &tmpvnode);
- if (err) goto MtVolErr;
- /* HFSToHFSPlusExtents(mdb->drXTExtRec, extents); */ /* ASDFADSFSD */
- extents[0].startBlock = SWAP_BE16 (mdb->drXTExtRec[0].startBlock);
- extents[0].blockCount = SWAP_BE16 (mdb->drXTExtRec[0].blockCount);
- extents[1].startBlock = SWAP_BE16 (mdb->drXTExtRec[1].startBlock);
- extents[1].blockCount = SWAP_BE16 (mdb->drXTExtRec[1].blockCount);
- extents[2].startBlock = SWAP_BE16 (mdb->drXTExtRec[2].startBlock);
- extents[2].blockCount = SWAP_BE16 (mdb->drXTExtRec[2].blockCount);
-
- err = InitMetaFileVNode(tmpvnode, SWAP_BE32 (mdb->drXTFlSize), SWAP_BE32 (mdb->drXTClpSiz), extents,
- kHFSExtentsFileID, CompareExtentKeys);
- if (err) goto MtVolErr;
+ * Set up Extents B-tree vnode
+ */
+ cndesc.cd_nameptr = hfs_extname;
+ cndesc.cd_namelen = strlen((char *)hfs_extname);
+ cndesc.cd_cnid = cnattr.ca_fileid = kHFSExtentsFileID;
+ fork.cf_size = SWAP_BE32(mdb->drXTFlSize);
+ fork.cf_blocks = fork.cf_size / vcb->blockSize;
+ fork.cf_clump = SWAP_BE32(mdb->drXTClpSiz);
+ fork.cf_vblocks = 0;
+ fork.cf_extents[0].startBlock = SWAP_BE16(mdb->drXTExtRec[0].startBlock);
+ fork.cf_extents[0].blockCount = SWAP_BE16(mdb->drXTExtRec[0].blockCount);
+ fork.cf_extents[1].startBlock = SWAP_BE16(mdb->drXTExtRec[1].startBlock);
+ fork.cf_extents[1].blockCount = SWAP_BE16(mdb->drXTExtRec[1].blockCount);
+ fork.cf_extents[2].startBlock = SWAP_BE16(mdb->drXTExtRec[2].startBlock);
+ fork.cf_extents[2].blockCount = SWAP_BE16(mdb->drXTExtRec[2].blockCount);
+ cnattr.ca_blocks = fork.cf_blocks;
+
+ error = hfs_getnewvnode(hfsmp, NULL, NULL, &cndesc, 0, &cnattr, &fork,
+ &hfsmp->hfs_extents_vp, &newvnode_flags);
+ if (error) {
+ if (HFS_MOUNT_DEBUG) {
+ printf("hfs_mounthfs (std): error creating Ext Vnode (%d) \n", error);
+ }
+ goto MtVolErr;
+ }
+ error = MacToVFSError(BTOpenPath(VTOF(hfsmp->hfs_extents_vp),
+ (KeyCompareProcPtr)CompareExtentKeys));
+ if (error) {
+ if (HFS_MOUNT_DEBUG) {
+ printf("hfs_mounthfs (std): error opening Ext Vnode (%d) \n", error);
+ }
+ hfs_unlock(VTOC(hfsmp->hfs_extents_vp));
+ goto MtVolErr;
+ }
+ hfsmp->hfs_extents_cp = VTOC(hfsmp->hfs_extents_vp);
/*
* Set up Catalog B-tree vnode...
*/
- err = GetInitializedVNode(hfsmp, &tmpvnode);
- if (err) goto MtVolErr;
- /* HFSToHFSPlusExtents(mdb->drCTExtRec, extents); */
- extents[0].startBlock = SWAP_BE16 (mdb->drCTExtRec[0].startBlock);
- extents[0].blockCount = SWAP_BE16 (mdb->drCTExtRec[0].blockCount);
- extents[1].startBlock = SWAP_BE16 (mdb->drCTExtRec[1].startBlock);
- extents[1].blockCount = SWAP_BE16 (mdb->drCTExtRec[1].blockCount);
- extents[2].startBlock = SWAP_BE16 (mdb->drCTExtRec[2].startBlock);
- extents[2].blockCount = SWAP_BE16 (mdb->drCTExtRec[2].blockCount);
-
- err = InitMetaFileVNode(tmpvnode, SWAP_BE32 (mdb->drCTFlSize), SWAP_BE32 (mdb->drCTClpSiz), extents,
- kHFSCatalogFileID, CompareCatalogKeys);
- if (err) goto MtVolErr;
+ cndesc.cd_nameptr = hfs_catname;
+ cndesc.cd_namelen = strlen((char *)hfs_catname);
+ cndesc.cd_cnid = cnattr.ca_fileid = kHFSCatalogFileID;
+ fork.cf_size = SWAP_BE32(mdb->drCTFlSize);
+ fork.cf_blocks = fork.cf_size / vcb->blockSize;
+ fork.cf_clump = SWAP_BE32(mdb->drCTClpSiz);
+ fork.cf_vblocks = 0;
+ fork.cf_extents[0].startBlock = SWAP_BE16(mdb->drCTExtRec[0].startBlock);
+ fork.cf_extents[0].blockCount = SWAP_BE16(mdb->drCTExtRec[0].blockCount);
+ fork.cf_extents[1].startBlock = SWAP_BE16(mdb->drCTExtRec[1].startBlock);
+ fork.cf_extents[1].blockCount = SWAP_BE16(mdb->drCTExtRec[1].blockCount);
+ fork.cf_extents[2].startBlock = SWAP_BE16(mdb->drCTExtRec[2].startBlock);
+ fork.cf_extents[2].blockCount = SWAP_BE16(mdb->drCTExtRec[2].blockCount);
+ cnattr.ca_blocks = fork.cf_blocks;
+
+ error = hfs_getnewvnode(hfsmp, NULL, NULL, &cndesc, 0, &cnattr, &fork,
+ &hfsmp->hfs_catalog_vp, &newvnode_flags);
+ if (error) {
+ if (HFS_MOUNT_DEBUG) {
+ printf("hfs_mounthfs (std): error creating catalog Vnode (%d) \n", error);
+ }
+ hfs_unlock(VTOC(hfsmp->hfs_extents_vp));
+ goto MtVolErr;
+ }
+ error = MacToVFSError(BTOpenPath(VTOF(hfsmp->hfs_catalog_vp),
+ (KeyCompareProcPtr)CompareCatalogKeys));
+ if (error) {
+ if (HFS_MOUNT_DEBUG) {
+ printf("hfs_mounthfs (std): error opening catalog Vnode (%d) \n", error);
+ }
+ hfs_unlock(VTOC(hfsmp->hfs_catalog_vp));
+ hfs_unlock(VTOC(hfsmp->hfs_extents_vp));
+ goto MtVolErr;
+ }
+ hfsmp->hfs_catalog_cp = VTOC(hfsmp->hfs_catalog_vp);
+
+ /*
+ * Set up dummy Allocation file vnode (used only for locking bitmap)
+ */
+ cndesc.cd_nameptr = hfs_vbmname;
+ cndesc.cd_namelen = strlen((char *)hfs_vbmname);
+ cndesc.cd_cnid = cnattr.ca_fileid = kHFSAllocationFileID;
+ bzero(&fork, sizeof(fork));
+ cnattr.ca_blocks = 0;
+
+ error = hfs_getnewvnode(hfsmp, NULL, NULL, &cndesc, 0, &cnattr, &fork,
+ &hfsmp->hfs_allocation_vp, &newvnode_flags);
+ if (error) {
+ if (HFS_MOUNT_DEBUG) {
+ printf("hfs_mounthfs (std): error creating bitmap Vnode (%d) \n", error);
+ }
+ hfs_unlock(VTOC(hfsmp->hfs_catalog_vp));
+ hfs_unlock(VTOC(hfsmp->hfs_extents_vp));
+ goto MtVolErr;
+ }
+ hfsmp->hfs_allocation_cp = VTOC(hfsmp->hfs_allocation_vp);
/* mark the volume dirty (clear clean unmount bit) */
vcb->vcbAtrb &= ~kHFSVolumeUnmountedMask;
- /* Remove any MetaDataDirectory from hfs disks */
- if (hfsmp->hfs_fs_ronly == 0)
- RemovedMetaDataDirectory(vcb);
-
+ if (error == noErr) {
+ error = cat_idlookup(hfsmp, kHFSRootFolderID, 0, NULL, NULL, NULL);
+ if (HFS_MOUNT_DEBUG) {
+ printf("hfs_mounthfs (std): error looking up root folder (%d) \n", error);
+ }
+ }
+
+ if (error == noErr) {
+ /* If the disk isn't write protected.. */
+ if ( !(vcb->vcbAtrb & kHFSVolumeHardwareLockMask)) {
+ MarkVCBDirty (vcb); // mark VCB dirty so it will be written
+ }
+ }
+
/*
- * all done with b-trees so we can unlock now...
+ * all done with system files so we can unlock now...
*/
- VOP_UNLOCK(vcb->catalogRefNum, 0, p);
- VOP_UNLOCK(vcb->extentsRefNum, 0, p);
-
- err = noErr;
-
- if ( err == noErr )
- {
- if ( !(vcb->vcbAtrb & kHFSVolumeHardwareLockMask) ) // if the disk is not write protected
- {
- MarkVCBDirty( vcb ); // mark VCB dirty so it will be written
- }
- }
- goto CmdDone;
+ hfs_unlock(VTOC(hfsmp->hfs_allocation_vp));
+ hfs_unlock(VTOC(hfsmp->hfs_catalog_vp));
+ hfs_unlock(VTOC(hfsmp->hfs_extents_vp));
+
+ if (error == noErr) {
+ /* If successful, then we can just return once we've unlocked the cnodes */
+ return error;
+ }
//-- Release any resources allocated so far before exiting with an error:
-MtVolErr:;
- ReleaseMetaFileVNode(vcb->catalogRefNum);
- ReleaseMetaFileVNode(vcb->extentsRefNum);
+MtVolErr:
+ hfsUnmount(hfsmp, NULL);
-CmdDone:;
- return( err );
+ return (error);
}
//*******************************************************************************
//*******************************************************************************
OSErr hfs_MountHFSPlusVolume(struct hfsmount *hfsmp, HFSPlusVolumeHeader *vhp,
- u_long embBlkOffset, u_long sectors, struct proc *p)
+ off_t embeddedOffset, u_int64_t disksize, __unused struct proc *p, void *args, kauth_cred_t cred)
{
- register ExtendedVCB *vcb;
- HFSPlusForkData *fdp;
- struct vnode *tmpvnode;
- OSErr retval;
-
- if (hfsmp == nil || vhp == nil) /* exit if bad paramater */
+ register ExtendedVCB *vcb;
+ struct cat_desc cndesc;
+ struct cat_attr cnattr;
+ struct cat_fork cfork;
+ u_int32_t blockSize;
+ daddr64_t spare_sectors;
+ struct BTreeInfoRec btinfo;
+ u_int16_t signature;
+ u_int16_t hfs_version;
+ int newvnode_flags = 0;
+ int i;
+ OSErr retval;
+ char converted_volname[256];
+ size_t volname_length = 0;
+ size_t conv_volname_length = 0;
+
+ signature = SWAP_BE16(vhp->signature);
+ hfs_version = SWAP_BE16(vhp->version);
+
+ if (signature == kHFSPlusSigWord) {
+ if (hfs_version != kHFSPlusVersion) {
+ printf("hfs_mount: invalid HFS+ version: %d\n", hfs_version);
+ return (EINVAL);
+ }
+ } else if (signature == kHFSXSigWord) {
+ if (hfs_version != kHFSXVersion) {
+ printf("hfs_mount: invalid HFSX version: %d\n", hfs_version);
+ return (EINVAL);
+ }
+ /* The in-memory signature is always 'H+'. */
+ signature = kHFSPlusSigWord;
+ hfsmp->hfs_flags |= HFS_X;
+ } else {
+ /* Removed printf for invalid HFS+ signature because it gives
+ * false error for UFS root volume
+ */
+ if (HFS_MOUNT_DEBUG) {
+ printf("hfs_mounthfsplus: unknown Volume Signature\n");
+ }
return (EINVAL);
+ }
- DBG_VFS(("hfs_MountHFSPlusVolume: signature=0x%x, version=%d, blockSize=%ld\n",
- SWAP_BE16 (vhp->signature),
- SWAP_BE16 (vhp->version),
- SWAP_BE32 (vhp->blockSize)));
-
- retval = ValidVolumeHeader(vhp); /* make sure this is an HFS Plus disk */
- if (retval)
- return MacToVFSError(retval);
+ /* Block size must be at least 512 and a power of 2 */
+ blockSize = SWAP_BE32(vhp->blockSize);
+ if (blockSize < 512 || !powerof2(blockSize)) {
+ if (HFS_MOUNT_DEBUG) {
+ printf("hfs_mounthfsplus: invalid blocksize (%d) \n", blockSize);
+ }
+ return (EINVAL);
+ }
/* don't mount a writable volume if its dirty, it must be cleaned by fsck_hfs */
- if (hfsmp->hfs_fs_ronly == 0 && (SWAP_BE32 (vhp->attributes) & kHFSVolumeUnmountedMask) == 0)
+ if ((hfsmp->hfs_flags & HFS_READ_ONLY) == 0 && hfsmp->jnl == NULL &&
+ (SWAP_BE32(vhp->attributes) & kHFSVolumeUnmountedMask) == 0) {
+ if (HFS_MOUNT_DEBUG) {
+ printf("hfs_mounthfsplus: cannot mount dirty non-journaled volumes\n");
+ }
return (EINVAL);
+ }
+
+ /* Make sure we can live with the physical block size. */
+ if ((disksize & (hfsmp->hfs_logical_block_size - 1)) ||
+ (embeddedOffset & (hfsmp->hfs_logical_block_size - 1)) ||
+ (blockSize < hfsmp->hfs_logical_block_size)) {
+ if (HFS_MOUNT_DEBUG) {
+ printf("hfs_mounthfsplus: invalid physical blocksize (%d), hfs_logical_blocksize (%d) \n",
+ blockSize, hfsmp->hfs_logical_block_size);
+ }
+ return (ENXIO);
+ }
+
+ /* If allocation block size is less than the physical
+ * block size, we assume that the physical block size
+ * is same as logical block size. The physical block
+ * size value is used to round down the offsets for
+ * reading and writing the primary and alternate volume
+ * headers at physical block boundary and will cause
+ * problems if it is less than the block size.
+ */
+ if (blockSize < hfsmp->hfs_physical_block_size) {
+ hfsmp->hfs_physical_block_size = hfsmp->hfs_logical_block_size;
+ hfsmp->hfs_log_per_phys = 1;
+ }
+
/*
* The VolumeHeader seems OK: transfer info from it into VCB
* Note - the VCB starts out clear (all zeros)
*/
vcb = HFSTOVCB(hfsmp);
- //DBG_ASSERT((hfsmp->hfs_raw_dev & 0xFFFF0000) == 0);
- vcb->vcbVRefNum = MAKE_VREFNUM(hfsmp->hfs_raw_dev);
- vcb->vcbSigWord = SWAP_BE16 (vhp->signature);
- vcb->vcbLsMod = SWAP_BE32 (vhp->modifyDate);
- vcb->vcbAtrb = (UInt16) SWAP_BE32 (vhp->attributes); // VCB only uses lower 16 bits
- vcb->vcbClpSiz = SWAP_BE32 (vhp->rsrcClumpSize);
- vcb->vcbNxtCNID = SWAP_BE32 (vhp->nextCatalogID);
- vcb->vcbVolBkUp = SWAP_BE32 (vhp->backupDate);
- vcb->vcbWrCnt = SWAP_BE32 (vhp->writeCount);
- vcb->vcbFilCnt = SWAP_BE32 (vhp->fileCount);
- vcb->vcbDirCnt = SWAP_BE32 (vhp->folderCount);
+ vcb->vcbSigWord = signature;
+ vcb->vcbJinfoBlock = SWAP_BE32(vhp->journalInfoBlock);
+ vcb->vcbLsMod = to_bsd_time(SWAP_BE32(vhp->modifyDate));
+ vcb->vcbAtrb = SWAP_BE32(vhp->attributes);
+ vcb->vcbClpSiz = SWAP_BE32(vhp->rsrcClumpSize);
+ vcb->vcbNxtCNID = SWAP_BE32(vhp->nextCatalogID);
+ vcb->vcbVolBkUp = to_bsd_time(SWAP_BE32(vhp->backupDate));
+ vcb->vcbWrCnt = SWAP_BE32(vhp->writeCount);
+ vcb->vcbFilCnt = SWAP_BE32(vhp->fileCount);
+ vcb->vcbDirCnt = SWAP_BE32(vhp->folderCount);
/* copy 32 bytes of Finder info */
bcopy(vhp->finderInfo, vcb->vcbFndrInfo, sizeof(vhp->finderInfo));
vcb->vcbAlBlSt = 0; /* hfs+ allocation blocks start at first block of volume */
- vcb->vcbWrCnt++; /* compensate for write of Volume Header on last flush */
-
- VCB_LOCK_INIT(vcb);
+ if ((hfsmp->hfs_flags & HFS_READ_ONLY) == 0)
+ vcb->vcbWrCnt++; /* compensate for write of Volume Header on last flush */
- /* Now fill in the Extended VCB info */
- vcb->nextAllocation = SWAP_BE32 (vhp->nextAllocation);
- vcb->totalBlocks = SWAP_BE32 (vhp->totalBlocks);
- vcb->freeBlocks = SWAP_BE32 (vhp->freeBlocks);
- vcb->blockSize = SWAP_BE32 (vhp->blockSize);
- vcb->checkedDate = SWAP_BE32 (vhp->checkedDate);
- vcb->encodingsBitmap = SWAP_BE64 (vhp->encodingsBitmap);
+ /* Now fill in the Extended VCB info */
+ vcb->nextAllocation = SWAP_BE32(vhp->nextAllocation);
+ vcb->totalBlocks = SWAP_BE32(vhp->totalBlocks);
+ vcb->allocLimit = vcb->totalBlocks;
+ vcb->freeBlocks = SWAP_BE32(vhp->freeBlocks);
+ vcb->blockSize = blockSize;
+ vcb->encodingsBitmap = SWAP_BE64(vhp->encodingsBitmap);
+ vcb->localCreateDate = SWAP_BE32(vhp->createDate);
- vcb->hfsPlusIOPosOffset = embBlkOffset * 512;
+ vcb->hfsPlusIOPosOffset = embeddedOffset;
- vcb->altIDSector = embBlkOffset + sectors - 2;
+ /* Default to no free block reserve */
+ vcb->reserveBlocks = 0;
- vcb->localCreateDate = SWAP_BE32 (vhp->createDate); /* in local time, not GMT! */
-
- /* Update the logical block size in the mount struct (currently set up from the wrapper MDB)
- using the new blocksize value: */
- hfsmp->hfs_logBlockSize = BestBlockSizeFit(vcb->blockSize, MAXBSIZE, hfsmp->hfs_phys_block_size);
+ /*
+ * Update the logical block size in the mount struct
+ * (currently set up from the wrapper MDB) using the
+ * new blocksize value:
+ */
+ hfsmp->hfs_logBlockSize = BestBlockSizeFit(vcb->blockSize, MAXBSIZE, hfsmp->hfs_logical_block_size);
vcb->vcbVBMIOSize = min(vcb->blockSize, MAXPHYSIO);
- // XXX PPD: Should check here for hardware lock flag and set flags in VCB/MP appropriately
- // vcb->vcbAtrb |= kVolumeHardwareLockMask; // XXX this line for debugging only!!!!
+ /*
+ * Validate and initialize the location of the alternate volume header.
+ */
+ spare_sectors = hfsmp->hfs_logical_block_count -
+ (((daddr64_t)vcb->totalBlocks * blockSize) /
+ hfsmp->hfs_logical_block_size);
+
+ if (spare_sectors > (daddr64_t)(blockSize / hfsmp->hfs_logical_block_size)) {
+ hfsmp->hfs_alt_id_sector = 0; /* partition has grown! */
+ } else {
+ hfsmp->hfs_alt_id_sector = (hfsmp->hfsPlusIOPosOffset / hfsmp->hfs_logical_block_size) +
+ HFS_ALT_SECTOR(hfsmp->hfs_logical_block_size,
+ hfsmp->hfs_logical_block_count);
+ }
- // Initialize our dirID/nodePtr cache associated with this volume.
- retval = InitMRUCache( sizeof(UInt32), kDefaultNumMRUCacheBlocks, &(vcb->hintCachePtr) );
- if (retval != noErr) goto ErrorExit;
+ bzero(&cndesc, sizeof(cndesc));
+ cndesc.cd_parentcnid = kHFSRootParentID;
+ cndesc.cd_flags |= CD_ISMETA;
+ bzero(&cnattr, sizeof(cnattr));
+ cnattr.ca_linkcount = 1;
+ cnattr.ca_mode = S_IFREG;
/*
- * Set up Extents B-tree vnode...
- */
- retval = GetInitializedVNode(hfsmp, &tmpvnode);
- if (retval) goto ErrorExit;
- fdp = &vhp->extentsFile;
- SWAP_HFS_PLUS_FORK_DATA (fdp);
- retval = InitMetaFileVNode(tmpvnode, fdp->logicalSize, fdp->clumpSize, fdp->extents,
- kHFSExtentsFileID, CompareExtentKeysPlus);
- SWAP_HFS_PLUS_FORK_DATA (fdp);
- if (retval) goto ErrorExit;
+ * Set up Extents B-tree vnode
+ */
+ cndesc.cd_nameptr = hfs_extname;
+ cndesc.cd_namelen = strlen((char *)hfs_extname);
+ cndesc.cd_cnid = cnattr.ca_fileid = kHFSExtentsFileID;
+
+ cfork.cf_size = SWAP_BE64 (vhp->extentsFile.logicalSize);
+ cfork.cf_new_size= 0;
+ cfork.cf_clump = SWAP_BE32 (vhp->extentsFile.clumpSize);
+ cfork.cf_blocks = SWAP_BE32 (vhp->extentsFile.totalBlocks);
+ cfork.cf_vblocks = 0;
+ cnattr.ca_blocks = cfork.cf_blocks;
+ for (i = 0; i < kHFSPlusExtentDensity; i++) {
+ cfork.cf_extents[i].startBlock =
+ SWAP_BE32 (vhp->extentsFile.extents[i].startBlock);
+ cfork.cf_extents[i].blockCount =
+ SWAP_BE32 (vhp->extentsFile.extents[i].blockCount);
+ }
+ retval = hfs_getnewvnode(hfsmp, NULL, NULL, &cndesc, 0, &cnattr, &cfork,
+ &hfsmp->hfs_extents_vp, &newvnode_flags);
+ if (retval)
+ {
+ if (HFS_MOUNT_DEBUG) {
+ printf("hfs_mounthfsplus: hfs_getnewvnode returned (%d) getting extentoverflow BT\n", retval);
+ }
+ goto ErrorExit;
+ }
+ hfsmp->hfs_extents_cp = VTOC(hfsmp->hfs_extents_vp);
+ hfs_unlock(hfsmp->hfs_extents_cp);
+ retval = MacToVFSError(BTOpenPath(VTOF(hfsmp->hfs_extents_vp),
+ (KeyCompareProcPtr) CompareExtentKeysPlus));
+ if (retval)
+ {
+ if (HFS_MOUNT_DEBUG) {
+ printf("hfs_mounthfsplus: BTOpenPath returned (%d) getting extentoverflow BT\n", retval);
+ }
+ goto ErrorExit;
+ }
/*
- * Set up Catalog B-tree vnode...
+ * Set up Catalog B-tree vnode
*/
- retval = GetInitializedVNode(hfsmp, &tmpvnode);
- if (retval) goto ErrorExit;
- fdp = &vhp->catalogFile;
- SWAP_HFS_PLUS_FORK_DATA (fdp);
- retval = InitMetaFileVNode(tmpvnode, fdp->logicalSize, fdp->clumpSize, fdp->extents,
- kHFSCatalogFileID, CompareExtendedCatalogKeys);
- SWAP_HFS_PLUS_FORK_DATA (fdp);
- if (retval) goto ErrorExit;
+ cndesc.cd_nameptr = hfs_catname;
+ cndesc.cd_namelen = strlen((char *)hfs_catname);
+ cndesc.cd_cnid = cnattr.ca_fileid = kHFSCatalogFileID;
+
+ cfork.cf_size = SWAP_BE64 (vhp->catalogFile.logicalSize);
+ cfork.cf_clump = SWAP_BE32 (vhp->catalogFile.clumpSize);
+ cfork.cf_blocks = SWAP_BE32 (vhp->catalogFile.totalBlocks);
+ cfork.cf_vblocks = 0;
+ cnattr.ca_blocks = cfork.cf_blocks;
+ for (i = 0; i < kHFSPlusExtentDensity; i++) {
+ cfork.cf_extents[i].startBlock =
+ SWAP_BE32 (vhp->catalogFile.extents[i].startBlock);
+ cfork.cf_extents[i].blockCount =
+ SWAP_BE32 (vhp->catalogFile.extents[i].blockCount);
+ }
+ retval = hfs_getnewvnode(hfsmp, NULL, NULL, &cndesc, 0, &cnattr, &cfork,
+ &hfsmp->hfs_catalog_vp, &newvnode_flags);
+ if (retval) {
+ if (HFS_MOUNT_DEBUG) {
+ printf("hfs_mounthfsplus: hfs_getnewvnode returned (%d) getting catalog BT\n", retval);
+ }
+ goto ErrorExit;
+ }
+ hfsmp->hfs_catalog_cp = VTOC(hfsmp->hfs_catalog_vp);
+ hfs_unlock(hfsmp->hfs_catalog_cp);
+
+ retval = MacToVFSError(BTOpenPath(VTOF(hfsmp->hfs_catalog_vp),
+ (KeyCompareProcPtr) CompareExtendedCatalogKeys));
+ if (retval) {
+ if (HFS_MOUNT_DEBUG) {
+ printf("hfs_mounthfsplus: BTOpenPath returned (%d) getting catalog BT\n", retval);
+ }
+ goto ErrorExit;
+ }
+ if ((hfsmp->hfs_flags & HFS_X) &&
+ BTGetInformation(VTOF(hfsmp->hfs_catalog_vp), 0, &btinfo) == 0) {
+ if (btinfo.keyCompareType == kHFSBinaryCompare) {
+ hfsmp->hfs_flags |= HFS_CASE_SENSITIVE;
+ /* Install a case-sensitive key compare */
+ (void) BTOpenPath(VTOF(hfsmp->hfs_catalog_vp),
+ (KeyCompareProcPtr)cat_binarykeycompare);
+ }
+ }
/*
- * Set up Allocation file vnode...
+ * Set up Allocation file vnode
*/
- retval = GetInitializedVNode(hfsmp, &tmpvnode);
- if (retval) goto ErrorExit;
- fdp = &vhp->allocationFile;
- SWAP_HFS_PLUS_FORK_DATA (fdp);
- retval = InitMetaFileVNode(tmpvnode, fdp->logicalSize, fdp->clumpSize, fdp->extents,
- kHFSAllocationFileID, NULL);
- SWAP_HFS_PLUS_FORK_DATA (fdp);
- if (retval) goto ErrorExit;
-
+ cndesc.cd_nameptr = hfs_vbmname;
+ cndesc.cd_namelen = strlen((char *)hfs_vbmname);
+ cndesc.cd_cnid = cnattr.ca_fileid = kHFSAllocationFileID;
+
+ cfork.cf_size = SWAP_BE64 (vhp->allocationFile.logicalSize);
+ cfork.cf_clump = SWAP_BE32 (vhp->allocationFile.clumpSize);
+ cfork.cf_blocks = SWAP_BE32 (vhp->allocationFile.totalBlocks);
+ cfork.cf_vblocks = 0;
+ cnattr.ca_blocks = cfork.cf_blocks;
+ for (i = 0; i < kHFSPlusExtentDensity; i++) {
+ cfork.cf_extents[i].startBlock =
+ SWAP_BE32 (vhp->allocationFile.extents[i].startBlock);
+ cfork.cf_extents[i].blockCount =
+ SWAP_BE32 (vhp->allocationFile.extents[i].blockCount);
+ }
+ retval = hfs_getnewvnode(hfsmp, NULL, NULL, &cndesc, 0, &cnattr, &cfork,
+ &hfsmp->hfs_allocation_vp, &newvnode_flags);
+ if (retval) {
+ if (HFS_MOUNT_DEBUG) {
+ printf("hfs_mounthfsplus: hfs_getnewvnode returned (%d) getting bitmap\n", retval);
+ }
+ goto ErrorExit;
+ }
+ hfsmp->hfs_allocation_cp = VTOC(hfsmp->hfs_allocation_vp);
+ hfs_unlock(hfsmp->hfs_allocation_cp);
+
+ /*
+ * Set up Attribute B-tree vnode
+ */
+ if (vhp->attributesFile.totalBlocks != 0) {
+ cndesc.cd_nameptr = hfs_attrname;
+ cndesc.cd_namelen = strlen((char *)hfs_attrname);
+ cndesc.cd_cnid = cnattr.ca_fileid = kHFSAttributesFileID;
+
+ cfork.cf_size = SWAP_BE64 (vhp->attributesFile.logicalSize);
+ cfork.cf_clump = SWAP_BE32 (vhp->attributesFile.clumpSize);
+ cfork.cf_blocks = SWAP_BE32 (vhp->attributesFile.totalBlocks);
+ cfork.cf_vblocks = 0;
+ cnattr.ca_blocks = cfork.cf_blocks;
+ for (i = 0; i < kHFSPlusExtentDensity; i++) {
+ cfork.cf_extents[i].startBlock =
+ SWAP_BE32 (vhp->attributesFile.extents[i].startBlock);
+ cfork.cf_extents[i].blockCount =
+ SWAP_BE32 (vhp->attributesFile.extents[i].blockCount);
+ }
+ retval = hfs_getnewvnode(hfsmp, NULL, NULL, &cndesc, 0, &cnattr, &cfork,
+ &hfsmp->hfs_attribute_vp, &newvnode_flags);
+ if (retval) {
+ if (HFS_MOUNT_DEBUG) {
+ printf("hfs_mounthfsplus: hfs_getnewvnode returned (%d) getting EA BT\n", retval);
+ }
+ goto ErrorExit;
+ }
+ hfsmp->hfs_attribute_cp = VTOC(hfsmp->hfs_attribute_vp);
+ hfs_unlock(hfsmp->hfs_attribute_cp);
+ retval = MacToVFSError(BTOpenPath(VTOF(hfsmp->hfs_attribute_vp),
+ (KeyCompareProcPtr) hfs_attrkeycompare));
+ if (retval) {
+ if (HFS_MOUNT_DEBUG) {
+ printf("hfs_mounthfsplus: BTOpenPath returned (%d) getting EA BT\n", retval);
+ }
+ goto ErrorExit;
+ }
+
+ /* Initialize vnode for virtual attribute data file that spans the
+ * entire file system space for performing I/O to attribute btree
+ * We hold iocount on the attrdata vnode for the entire duration
+ * of mount (similar to btree vnodes)
+ */
+ retval = init_attrdata_vnode(hfsmp);
+ if (retval) {
+ if (HFS_MOUNT_DEBUG) {
+ printf("hfs_mounthfsplus: init_attrdata_vnode returned (%d) for virtual EA file\n", retval);
+ }
+ goto ErrorExit;
+ }
+ }
+
/*
- * Now that Catalog file is open get the volume name from the catalog
+ * Set up Startup file vnode
*/
- retval = MacToVFSError( GetVolumeNameFromCatalog(vcb) );
- if (retval != noErr) goto ErrorExit;
+ if (vhp->startupFile.totalBlocks != 0) {
+ cndesc.cd_nameptr = hfs_startupname;
+ cndesc.cd_namelen = strlen((char *)hfs_startupname);
+ cndesc.cd_cnid = cnattr.ca_fileid = kHFSStartupFileID;
+
+ cfork.cf_size = SWAP_BE64 (vhp->startupFile.logicalSize);
+ cfork.cf_clump = SWAP_BE32 (vhp->startupFile.clumpSize);
+ cfork.cf_blocks = SWAP_BE32 (vhp->startupFile.totalBlocks);
+ cfork.cf_vblocks = 0;
+ cnattr.ca_blocks = cfork.cf_blocks;
+ for (i = 0; i < kHFSPlusExtentDensity; i++) {
+ cfork.cf_extents[i].startBlock =
+ SWAP_BE32 (vhp->startupFile.extents[i].startBlock);
+ cfork.cf_extents[i].blockCount =
+ SWAP_BE32 (vhp->startupFile.extents[i].blockCount);
+ }
+ retval = hfs_getnewvnode(hfsmp, NULL, NULL, &cndesc, 0, &cnattr, &cfork,
+ &hfsmp->hfs_startup_vp, &newvnode_flags);
+ if (retval) {
+ if (HFS_MOUNT_DEBUG) {
+ printf("hfs_mounthfsplus: hfs_getnewvnode returned (%d) getting startup file\n", retval);
+ }
+ goto ErrorExit;
+ }
+ hfsmp->hfs_startup_cp = VTOC(hfsmp->hfs_startup_vp);
+ hfs_unlock(hfsmp->hfs_startup_cp);
+ }
+
+ /* Pick up volume name and create date */
+ retval = cat_idlookup(hfsmp, kHFSRootFolderID, 0, &cndesc, &cnattr, NULL);
+ if (retval) {
+ if (HFS_MOUNT_DEBUG) {
+ printf("hfs_mounthfsplus: cat_idlookup returned (%d) getting rootfolder \n", retval);
+ }
+ goto ErrorExit;
+ }
+ vcb->hfs_itime = cnattr.ca_itime;
+ vcb->volumeNameEncodingHint = cndesc.cd_encoding;
+ bcopy(cndesc.cd_nameptr, vcb->vcbVN, min(255, cndesc.cd_namelen));
+ volname_length = strlen ((const char*)vcb->vcbVN);
+ cat_releasedesc(&cndesc);
+
+#define DKIOCCSSETLVNAME _IOW('d', 198, char[1024])
+
+ /* Send the volume name down to CoreStorage if necessary */
+ retval = utf8_normalizestr(vcb->vcbVN, volname_length, (u_int8_t*)converted_volname, &conv_volname_length, 256, UTF_PRECOMPOSED);
+ if (retval == 0) {
+ (void) VNOP_IOCTL (hfsmp->hfs_devvp, DKIOCCSSETLVNAME, converted_volname, 0, vfs_context_current());
+ }
+
+ /* reset retval == 0. we don't care about errors in volname conversion */
+ retval = 0;
+
/* mark the volume dirty (clear clean unmount bit) */
vcb->vcbAtrb &= ~kHFSVolumeUnmountedMask;
+ if (hfsmp->jnl && (hfsmp->hfs_flags & HFS_READ_ONLY) == 0) {
+ hfs_flushvolumeheader(hfsmp, TRUE, 0);
+ }
+
+ /* kHFSHasFolderCount is only supported/updated on HFSX volumes */
+ if ((hfsmp->hfs_flags & HFS_X) != 0) {
+ hfsmp->hfs_flags |= HFS_FOLDERCOUNT;
+ }
- /* setup private/hidden directory for unlinked files */
- hfsmp->hfs_private_metadata_dir = FindMetaDataDirectory(vcb);
+ //
+ // Check if we need to do late journal initialization. This only
+ // happens if a previous version of MacOS X (or 9) touched the disk.
+ // In that case hfs_late_journal_init() will go re-locate the journal
+ // and journal_info_block files and validate that they're still kosher.
+ //
+ if ( (vcb->vcbAtrb & kHFSVolumeJournaledMask)
+ && (SWAP_BE32(vhp->lastMountedVersion) != kHFSJMountVersion)
+ && (hfsmp->jnl == NULL)) {
+
+ retval = hfs_late_journal_init(hfsmp, vhp, args);
+ if (retval != 0) {
+ if (retval == EROFS) {
+ // EROFS is a special error code that means the volume has an external
+ // journal which we couldn't find. in that case we do not want to
+ // rewrite the volume header - we'll just refuse to mount the volume.
+ if (HFS_MOUNT_DEBUG) {
+ printf("hfs_mounthfsplus: hfs_late_journal_init returned (%d), maybe an external jnl?\n", retval);
+ }
+ retval = EINVAL;
+ goto ErrorExit;
+ }
+
+ hfsmp->jnl = NULL;
+
+ // if the journal failed to open, then set the lastMountedVersion
+ // to be "FSK!" which fsck_hfs will see and force the fsck instead
+ // of just bailing out because the volume is journaled.
+ if (!(hfsmp->hfs_flags & HFS_READ_ONLY)) {
+ HFSPlusVolumeHeader *jvhp;
+ daddr64_t mdb_offset;
+ struct buf *bp = NULL;
+
+ hfsmp->hfs_flags |= HFS_NEED_JNL_RESET;
+
+ mdb_offset = (daddr64_t)((embeddedOffset / blockSize) + HFS_PRI_SECTOR(blockSize));
+
+ bp = NULL;
+ retval = (int)buf_meta_bread(hfsmp->hfs_devvp,
+ HFS_PHYSBLK_ROUNDDOWN(mdb_offset, hfsmp->hfs_log_per_phys),
+ hfsmp->hfs_physical_block_size, cred, &bp);
+ if (retval == 0) {
+ jvhp = (HFSPlusVolumeHeader *)(buf_dataptr(bp) + HFS_PRI_OFFSET(hfsmp->hfs_physical_block_size));
+
+ if (SWAP_BE16(jvhp->signature) == kHFSPlusSigWord || SWAP_BE16(jvhp->signature) == kHFSXSigWord) {
+ printf ("hfs(3): Journal replay fail. Writing lastMountVersion as FSK!\n");
+ jvhp->lastMountedVersion = SWAP_BE32(kFSKMountVersion);
+ buf_bwrite(bp);
+ } else {
+ buf_brelse(bp);
+ }
+ bp = NULL;
+ } else if (bp) {
+ buf_brelse(bp);
+ // clear this so the error exit path won't try to use it
+ bp = NULL;
+ }
+ }
+
+ if (HFS_MOUNT_DEBUG) {
+ printf("hfs_mounthfsplus: hfs_late_journal_init returned (%d)\n", retval);
+ }
+ retval = EINVAL;
+ goto ErrorExit;
+ } else if (hfsmp->jnl) {
+ vfs_setflags(hfsmp->hfs_mp, (u_int64_t)((unsigned int)MNT_JOURNALED));
+ }
+ } else if (hfsmp->jnl || ((vcb->vcbAtrb & kHFSVolumeJournaledMask) && (hfsmp->hfs_flags & HFS_READ_ONLY))) {
+ struct cat_attr jinfo_attr, jnl_attr;
+
+ if (hfsmp->hfs_flags & HFS_READ_ONLY) {
+ vcb->vcbAtrb &= ~kHFSVolumeJournaledMask;
+ }
+
+ // if we're here we need to fill in the fileid's for the
+ // journal and journal_info_block.
+ hfsmp->hfs_jnlinfoblkid = GetFileInfo(vcb, kRootDirID, ".journal_info_block", &jinfo_attr, NULL);
+ hfsmp->hfs_jnlfileid = GetFileInfo(vcb, kRootDirID, ".journal", &jnl_attr, NULL);
+ if (hfsmp->hfs_jnlinfoblkid == 0 || hfsmp->hfs_jnlfileid == 0) {
+ printf("hfs: danger! couldn't find the file-id's for the journal or journal_info_block\n");
+ printf("hfs: jnlfileid %d, jnlinfoblkid %d\n", hfsmp->hfs_jnlfileid, hfsmp->hfs_jnlinfoblkid);
+ }
+
+ if (hfsmp->hfs_flags & HFS_READ_ONLY) {
+ vcb->vcbAtrb |= kHFSVolumeJournaledMask;
+ }
+
+ if (hfsmp->jnl == NULL) {
+ vfs_clearflags(hfsmp->hfs_mp, (u_int64_t)((unsigned int)MNT_JOURNALED));
+ }
+ }
/*
- * all done with metadata files so we can unlock now...
+ * Establish a metadata allocation zone.
*/
- VOP_UNLOCK(vcb->allocationsRefNum, 0, p);
- VOP_UNLOCK(vcb->catalogRefNum, 0, p);
- VOP_UNLOCK(vcb->extentsRefNum, 0, p);
+ hfs_metadatazone_init(hfsmp, false);
- if ( !(vcb->vcbAtrb & kHFSVolumeHardwareLockMask) ) // if the disk is not write protected
- {
- MarkVCBDirty( vcb ); // mark VCB dirty so it will be written
+ /*
+ * Make any metadata zone adjustments.
+ */
+ if (hfsmp->hfs_flags & HFS_METADATA_ZONE) {
+ /* Keep the roving allocator out of the metadata zone. */
+ if (vcb->nextAllocation >= hfsmp->hfs_metazone_start &&
+ vcb->nextAllocation <= hfsmp->hfs_metazone_end) {
+ HFS_UPDATE_NEXT_ALLOCATION(hfsmp, hfsmp->hfs_metazone_end + 1);
+ }
+ } else {
+ if (vcb->nextAllocation <= 1) {
+ vcb->nextAllocation = hfsmp->hfs_min_alloc_start;
+ }
}
-
- DBG_VFS(("hfs_MountHFSPlusVolume: returning (%d)\n", retval));
+ vcb->sparseAllocation = hfsmp->hfs_min_alloc_start;
- return (0);
+ /* Setup private/hidden directories for hardlinks. */
+ hfs_privatedir_init(hfsmp, FILE_HARDLINKS);
+ hfs_privatedir_init(hfsmp, DIR_HARDLINKS);
+ if ((hfsmp->hfs_flags & HFS_READ_ONLY) == 0)
+ hfs_remove_orphans(hfsmp);
+
+ /* See if we need to erase unused Catalog nodes due to <rdar://problem/6947811>. */
+ if ((hfsmp->hfs_flags & HFS_READ_ONLY) == 0)
+ {
+ retval = hfs_erase_unused_nodes(hfsmp);
+ if (retval) {
+ if (HFS_MOUNT_DEBUG) {
+ printf("hfs_mounthfsplus: hfs_erase_unused_nodes returned (%d) for %s \n", retval, hfsmp->vcbVN);
+ }
+
+ goto ErrorExit;
+ }
+ }
+
+ if ( !(vcb->vcbAtrb & kHFSVolumeHardwareLockMask) ) // if the disk is not write protected
+ {
+ MarkVCBDirty( vcb ); // mark VCB dirty so it will be written
+ }
-ErrorExit:
/*
- * A fatal error occured and the volume cannot be mounted
- * release any resources that we aquired...
+ * Allow hot file clustering if conditions allow.
*/
+ if ((hfsmp->hfs_flags & HFS_METADATA_ZONE) &&
+ ((hfsmp->hfs_flags & (HFS_READ_ONLY | HFS_SSD)) == 0)) {
+ (void) hfs_recording_init(hfsmp);
+ }
- DBG_VFS(("hfs_MountHFSPlusVolume: fatal error (%d)\n", retval));
+ /* Force ACLs on HFS+ file systems. */
+ vfs_setextendedsecurity(HFSTOVFS(hfsmp));
- InvalidateCatalogCache(vcb);
-
- ReleaseMetaFileVNode(vcb->allocationsRefNum);
- ReleaseMetaFileVNode(vcb->catalogRefNum);
- ReleaseMetaFileVNode(vcb->extentsRefNum);
+ /* Enable extent-based extended attributes by default */
+ hfsmp->hfs_flags |= HFS_XATTR_EXTENTS;
+
+ /* See if this volume should have per-file content protection enabled */
+ if (vcb->vcbAtrb & kHFSContentProtectionMask) {
+ vfs_setflags (hfsmp->hfs_mp, MNT_CPROTECT);
+ }
+
+ return (0);
+ErrorExit:
+ /*
+ * A fatal error occurred and the volume cannot be mounted, so
+ * release any resources that we acquired...
+ */
+ hfsUnmount(hfsmp, NULL);
+
+ if (HFS_MOUNT_DEBUG) {
+ printf("hfs_mounthfsplus: encountered errorr (%d)\n", retval);
+ }
return (retval);
}
*/
static void ReleaseMetaFileVNode(struct vnode *vp)
{
- if (vp)
- {
- FCB *fcb = VTOFCB(vp);
+ struct filefork *fp;
- if (fcb->fcbBTCBPtr != NULL)
- (void) BTClosePath(fcb); /* ignore errors since there is only one path open */
+ if (vp && (fp = VTOF(vp))) {
+ if (fp->fcbBTCBPtr != NULL) {
+ (void)hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK);
+ (void) BTClosePath(fp);
+ hfs_unlock(VTOC(vp));
+ }
/* release the node even if BTClosePath fails */
- if (VOP_ISLOCKED(vp))
- vput(vp);
- else
- vrele(vp);
- }
-}
-
-
-/*
- * InitMetaFileVNode
- *
- * vp U L L
- */
-static int InitMetaFileVNode(struct vnode *vp, off_t eof, u_long clumpSize, const HFSPlusExtentRecord extents,
- HFSCatalogNodeID fileID, void * keyCompareProc)
-{
- FCB *fcb;
- ExtendedVCB *vcb;
- int result = 0;
-
- DBG_ASSERT(vp != NULL);
- DBG_ASSERT(vp->v_data != NULL);
-
- vcb = VTOVCB(vp);
- fcb = VTOFCB(vp);
-
- switch (fileID)
- {
- case kHFSExtentsFileID:
- vcb->extentsRefNum = vp;
- break;
-
- case kHFSCatalogFileID:
- vcb->catalogRefNum = vp;
- break;
-
- case kHFSAllocationFileID:
- vcb->allocationsRefNum = vp;
- break;
-
- default:
- panic("InitMetaFileVNode: invalid fileID!");
+ vnode_recycle(vp);
+ vnode_put(vp);
}
-
- fcb->fcbEOF = eof;
- fcb->fcbPLen = eof;
- fcb->fcbClmpSize = clumpSize;
- H_FILEID(VTOH(vp)) = fileID;
- H_DIRID(VTOH(vp)) = kHFSRootParentID;
- H_FORKTYPE(VTOH(vp)) = kSysFile;
-
- bcopy(extents, fcb->fcbExtents, sizeof(HFSPlusExtentRecord));
-
- /*
- * Lock the hfsnode and insert the hfsnode into the hash queue:
- */
- hfs_vhashins(H_DEV(VTOH(vp)), fileID, VTOH(vp));
- vp->v_flag |= VSYSTEM; /* tag our metadata files (used by vflush call) */
-
- /* As the vnode is a system vnode we don't need UBC */
- if(UBCINFOEXISTS(vp)) {
- /* So something is wrong if the it exists */
- panic("ubc exists for system vnode");
- }
-
- if (keyCompareProc != NULL) {
- result = BTOpenPath(fcb,
- (KeyCompareProcPtr) keyCompareProc,
- GetBTreeBlock,
- ReleaseBTreeBlock,
- ExtendBTreeFile,
- SetBTreeBlockSize);
- result = MacToVFSError(result);
- }
-
- return (result);
}
*
*************************************************************/
-short hfsUnmount( register struct hfsmount *hfsmp, struct proc *p)
+int
+hfsUnmount( register struct hfsmount *hfsmp, __unused struct proc *p)
{
- ExtendedVCB *vcb = HFSTOVCB(hfsmp);
- int retval = E_NONE;
+ /* Get rid of our attribute data vnode (if any). This is done
+ * after the vflush() during mount, so we don't need to worry
+ * about any locks.
+ */
+ if (hfsmp->hfs_attrdata_vp) {
+ ReleaseMetaFileVNode(hfsmp->hfs_attrdata_vp);
+ hfsmp->hfs_attrdata_vp = NULLVP;
+ }
- (void) DisposeMRUCache(vcb->hintCachePtr);
- InvalidateCatalogCache( vcb );
- // XXX PPD: Should dispose of any allocated volume cache here: call DisposeVolumeCacheBlocks( vcb )?
+ if (hfsmp->hfs_startup_vp) {
+ ReleaseMetaFileVNode(hfsmp->hfs_startup_vp);
+ hfsmp->hfs_startup_cp = NULL;
+ hfsmp->hfs_startup_vp = NULL;
+ }
+
+ if (hfsmp->hfs_attribute_vp) {
+ ReleaseMetaFileVNode(hfsmp->hfs_attribute_vp);
+ hfsmp->hfs_attribute_cp = NULL;
+ hfsmp->hfs_attribute_vp = NULL;
+ }
- (void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_EXCLUSIVE, p);
- (void) hfs_metafilelocking(hfsmp, kHFSExtentsFileID, LK_EXCLUSIVE, p);
+ if (hfsmp->hfs_catalog_vp) {
+ ReleaseMetaFileVNode(hfsmp->hfs_catalog_vp);
+ hfsmp->hfs_catalog_cp = NULL;
+ hfsmp->hfs_catalog_vp = NULL;
+ }
- if (vcb->vcbSigWord == kHFSPlusSigWord)
- ReleaseMetaFileVNode(vcb->allocationsRefNum);
+ if (hfsmp->hfs_extents_vp) {
+ ReleaseMetaFileVNode(hfsmp->hfs_extents_vp);
+ hfsmp->hfs_extents_cp = NULL;
+ hfsmp->hfs_extents_vp = NULL;
+ }
- ReleaseMetaFileVNode(vcb->catalogRefNum);
- ReleaseMetaFileVNode(vcb->extentsRefNum);
+ if (hfsmp->hfs_allocation_vp) {
+ ReleaseMetaFileVNode(hfsmp->hfs_allocation_vp);
+ hfsmp->hfs_allocation_cp = NULL;
+ hfsmp->hfs_allocation_vp = NULL;
+ }
- return (retval);
+ return (0);
}
/*
- * hfs_resolvelink - auto resolve HFS+ hardlinks
- *
- * Used after calling GetCatalogNode or GetCatalogOffspring
+ * Test if fork has overflow extents.
*/
-void hfs_resolvelink(ExtendedVCB *vcb, CatalogNodeData *cndp)
+__private_extern__
+int
+overflow_extents(struct filefork *fp)
{
- struct FInfo *fip;
- char iNodeName[32];
- UInt32 hint;
- UInt32 indlinkno;
- UInt32 linkparid, linkcnid;
- OSErr result;
-
- fip = (struct FInfo *) &cndp->cnd_finderInfo;
+ u_int32_t blocks;
+
+ //
+ // If the vnode pointer is NULL then we're being called
+ // from hfs_remove_orphans() with a faked-up filefork
+ // and therefore it has to be an HFS+ volume. Otherwise
+ // we check through the volume header to see what type
+ // of volume we're on.
+ //
+ if (FTOV(fp) == NULL || VTOVCB(FTOV(fp))->vcbSigWord == kHFSPlusSigWord) {
+ if (fp->ff_extents[7].blockCount == 0)
+ return (0);
- /*
- * if this is an indirect link (hardlink) then auto resolve it...
- */
- if ((vcb->vcbSigWord == kHFSPlusSigWord)
- && (cndp->cnd_type == kCatalogFileNode)
- && (fip->fdType == kHardLinkFileType)
- && (fip->fdCreator == kHFSPlusCreator)
- && ((cndp->cnd_createDate == vcb->vcbCrDate) ||
- (cndp->cnd_createDate == VCBTOHFS(vcb)->hfs_metadata_createdate))) {
+ blocks = fp->ff_extents[0].blockCount +
+ fp->ff_extents[1].blockCount +
+ fp->ff_extents[2].blockCount +
+ fp->ff_extents[3].blockCount +
+ fp->ff_extents[4].blockCount +
+ fp->ff_extents[5].blockCount +
+ fp->ff_extents[6].blockCount +
+ fp->ff_extents[7].blockCount;
+ } else {
+ if (fp->ff_extents[2].blockCount == 0)
+ return false;
- indlinkno = cndp->cnd_iNodeNum;
- MAKE_INODE_NAME(iNodeName, indlinkno);
- /*
- * Get nodeData from the data node file.
- * Flag the node data to NOT copy the name (ie preserve the original)
- * Also preserve the parent directory ID.
- */
- linkparid = cndp->cnm_parID;
- linkcnid = cndp->cnd_nodeID;
- cndp->cnm_flags |= kCatNameNoCopyName;
- result = GetCatalogNode(vcb, VCBTOHFS(vcb)->hfs_private_metadata_dir,
- iNodeName, 0, 0, cndp, &hint);
- cndp->cnm_flags &= ~kCatNameNoCopyName;
-
- /* Make sure there's a reference */
- if (result == 0) {
- if (cndp->cnd_linkCount == 0) cndp->cnd_linkCount = 2;
-
- /* Keep a copy of iNodeNum to put into h_indnodeno */
- cndp->cnd_iNodeNumCopy = indlinkno;
- cndp->cnm_parID = linkparid;
- cndp->cnd_linkCNID = linkcnid;
- }
- }
-}
+ blocks = fp->ff_extents[0].blockCount +
+ fp->ff_extents[1].blockCount +
+ fp->ff_extents[2].blockCount;
+ }
+ return (fp->ff_blocks > blocks);
+}
/*
- * Performs a lookup on the given dirID, name. Returns the catalog info
- *
- * If len is -1, then it is a null terminated string, pass it along to MacOS as kUndefinedStrLen
+ * Lock the HFS global journal lock
*/
+int
+hfs_lock_global (struct hfsmount *hfsmp, enum hfslocktype locktype) {
-short hfs_getcatalog (ExtendedVCB *vcb, UInt32 parentDirID, char *name, short len, hfsCatalogInfo *catInfo)
-{
- OSErr result;
- UInt32 length;
-
- if (len == -1 ) { /* Convert it to MacOS terms */
- if (name)
- length = strlen(name);
- else
- length = kUndefinedStrLen;
- }
- else
- length = len;
-
- result = GetCatalogNode(vcb, parentDirID, name, length, catInfo->hint, &catInfo->nodeData, &catInfo->hint);
+ void *thread = current_thread();
-#if HFS_DIAGNOSTICS
- if (catInfo->nodeData.cnm_nameptr) {
- DBG_ASSERT(strlen(catInfo->nodeData.cnm_nameptr) == catInfo->nodeData.cnm_length);
+ if (hfsmp->hfs_global_lockowner == thread) {
+ panic ("hfs_lock_global: locking against myself!");
}
-#endif
- if (result == 0)
- hfs_resolvelink(vcb, &catInfo->nodeData);
+ /* HFS_SHARED_LOCK */
+ if (locktype == HFS_SHARED_LOCK) {
+ lck_rw_lock_shared (&hfsmp->hfs_global_lock);
+ hfsmp->hfs_global_lockowner = HFS_SHARED_OWNER;
+ }
+ /* HFS_EXCLUSIVE_LOCK */
+ else {
+ lck_rw_lock_exclusive (&hfsmp->hfs_global_lock);
+ hfsmp->hfs_global_lockowner = thread;
+ }
- return MacToVFSError(result);
+ return 0;
}
+/*
+ * Unlock the HFS global journal lock
+ */
+void
+hfs_unlock_global (struct hfsmount *hfsmp) {
+
+ void *thread = current_thread();
-short hfsDelete (ExtendedVCB *vcb, UInt32 parentDirID, StringPtr name, short isfile, UInt32 catalogHint)
-{
- OSErr result = noErr;
-
- /* XXX have all the file's blocks been flushed/trashed? */
-
- /*
- * DeleteFile will delete the catalog node and then
- * free up any disk space used by the file.
- */
- if (isfile)
- result = DeleteFile(vcb, parentDirID, name, catalogHint);
- else /* is a directory */
- result = DeleteCatalogNode(vcb, parentDirID, name, catalogHint);
-
- if (result)
- DBG_ERR(("on Delete, DeleteFile returned: %d: dirid: %ld name: %s\n", result, parentDirID, name));
-
- return MacToVFSError(result);
+ /* HFS_LOCK_EXCLUSIVE */
+ if (hfsmp->hfs_global_lockowner == thread) {
+ hfsmp->hfs_global_lockowner = NULL;
+ lck_rw_unlock_exclusive (&hfsmp->hfs_global_lock);
+ }
+ /* HFS_LOCK_SHARED */
+ else {
+ lck_rw_unlock_shared (&hfsmp->hfs_global_lock);
+ }
}
-short hfsMoveRename (ExtendedVCB *vcb, UInt32 oldDirID, char *oldName, UInt32 newDirID, char *newName, UInt32 *hint)
+/*
+ * Lock HFS system file(s).
+ */
+int
+hfs_systemfile_lock(struct hfsmount *hfsmp, int flags, enum hfslocktype locktype)
{
- OSErr result = noErr;
+ /*
+ * Locking order is Catalog file, Attributes file, Startup file, Bitmap file, Extents file
+ */
+ if (flags & SFL_CATALOG) {
- result = MoveRenameCatalogNode(vcb, oldDirID,oldName, *hint, newDirID, newName, hint, 0);
-
- if (result)
- DBG_ERR(("on hfsMoveRename, MoveRenameCatalogNode returned: %d: newdirid: %ld newname: %s\n", result, newDirID, newName));
-
-
- return MacToVFSError(result);
-}
-
-/* XXX SER pass back the hint so other people can use it */
-
-
-short hfsCreate(ExtendedVCB *vcb, UInt32 dirID, char *name, int mode, UInt32 tehint)
-{
- OSErr result = noErr;
- HFSCatalogNodeID catalogNodeID;
- UInt32 catalogHint;
- UInt32 type;
-
- /* just test for directories, the default is to create a file (like symlinks) */
- if ((mode & IFMT) == IFDIR)
- type = kCatalogFolderNode;
- else
- type = kCatalogFileNode;
-
- result = CreateCatalogNode (vcb, dirID, name, type, &catalogNodeID, &catalogHint, tehint);
-
- return MacToVFSError(result);
-}
-
-
-short hfsCreateFileID (ExtendedVCB *vcb, UInt32 parentDirID, StringPtr name, UInt32 catalogHint, UInt32 *fileIDPtr)
-{
- return MacToVFSError(CreateFileIDRef(vcb, parentDirID, name, catalogHint, fileIDPtr));
-}
-
-
-/********************************************************************************/
-/* */
-/* hfs_vget_catinfo - Returns a vnode derived from a hfs catInfo struct */
-/* */
-/********************************************************************************/
-
-int hfs_vget_catinfo(struct vnode *parent_vp, struct hfsCatalogInfo *catInfo, u_int32_t forkType, struct vnode **target_vp)
-{
- int retval = E_NONE;
-
- if (forkType == kDefault) {
- if (catInfo->nodeData.cnd_type == kCatalogFolderNode)
- forkType = kDirectory;
- else
- forkType = kDataFork;
- }
-
- *target_vp = hfs_vhashget(H_DEV(VTOH(parent_vp)), catInfo->nodeData.cnd_nodeID, forkType);
-
- if (*target_vp == NULL)
- retval = hfs_vcreate( VTOVCB(parent_vp), catInfo, forkType, target_vp);
-
- return (retval);
-}
-
-
-
-/************************************************************************/
-/* hfs_vcreate - Returns a vnode derived from hfs */
-/* */
-/* When creating the vnode, care must be made to set the */
-/* correct fields in the correct order. Calls to malloc() */
-/* and other subroutines, can cause a context switch, */
-/* and the fields must be ready for the possibility */
-/* */
-/* */
-/************************************************************************/
+#ifdef HFS_CHECK_LOCK_ORDER
+ if (hfsmp->hfs_attribute_cp && hfsmp->hfs_attribute_cp->c_lockowner == current_thread()) {
+ panic("hfs_systemfile_lock: bad lock order (Attributes before Catalog)");
+ }
+ if (hfsmp->hfs_startup_cp && hfsmp->hfs_startup_cp->c_lockowner == current_thread()) {
+ panic("hfs_systemfile_lock: bad lock order (Startup before Catalog)");
+ }
+ if (hfsmp-> hfs_extents_cp && hfsmp->hfs_extents_cp->c_lockowner == current_thread()) {
+ panic("hfs_systemfile_lock: bad lock order (Extents before Catalog)");
+ }
+#endif /* HFS_CHECK_LOCK_ORDER */
-short hfs_vcreate(ExtendedVCB *vcb, hfsCatalogInfo *catInfo, UInt8 forkType, struct vnode **vpp)
-{
- struct hfsnode *hp;
- struct vnode *vp;
- struct hfsmount *hfsmp;
- struct hfsfilemeta *fm;
- struct mount *mp;
- struct vfsFCB *xfcb;
- dev_t dev;
- short retval;
-
- hfsmp = VCBTOHFS(vcb);
- mp = HFSTOVFS(hfsmp);
- dev = hfsmp->hfs_raw_dev;
-
- /* Check if unmount in progress */
- if (mp->mnt_kern_flag & MNTK_UNMOUNT) {
- *vpp = NULL;
- return (EPERM);
- }
+ if (hfsmp->hfs_catalog_cp) {
+ (void) hfs_lock(hfsmp->hfs_catalog_cp, locktype);
+ } else {
+ flags &= ~SFL_CATALOG;
+ }
- /*
- * If this is a hard link then check if the
- * data node already exists in our hash.
- */
- if ((forkType == kDataFork)
- && (catInfo->nodeData.cnd_type == kCatalogFileNode)
- && ((catInfo->nodeData.cnd_mode & IFMT) == IFREG)
- && (catInfo->nodeData.cnd_linkCount > 0)) {
- vp = hfs_vhashget(dev, catInfo->nodeData.cnd_nodeID, kDataFork);
- if (vp != NULL) {
- /* Use the name of the link and it's parent ID. */
- hp = VTOH(vp);
- H_DIRID(hp) = catInfo->nodeData.cnm_parID;
- hfs_set_metaname(catInfo->nodeData.cnm_nameptr, hp->h_meta, hfsmp);
- *vpp = vp;
- return (0);
+ /*
+ * When the catalog file has overflow extents then
+ * also acquire the extents b-tree lock if its not
+ * already requested.
+ */
+ if ((flags & SFL_EXTENTS) == 0 &&
+ overflow_extents(VTOF(hfsmp->hfs_catalog_vp))) {
+ flags |= SFL_EXTENTS;
}
}
+ if (flags & SFL_ATTRIBUTE) {
- MALLOC_ZONE(hp, struct hfsnode *, sizeof(struct hfsnode), M_HFSNODE, M_WAITOK);
- bzero((caddr_t)hp, sizeof(struct hfsnode));
- hp->h_nodeflags |= IN_ALLOCATING;
- lockinit(&hp->h_lock, PINOD, "hfsnode", 0, 0);
- H_FORKTYPE(hp) = forkType;
- rl_init(&hp->h_invalidranges);
+#ifdef HFS_CHECK_LOCK_ORDER
+ if (hfsmp->hfs_startup_cp && hfsmp->hfs_startup_cp->c_lockowner == current_thread()) {
+ panic("hfs_systemfile_lock: bad lock order (Startup before Attributes)");
+ }
+ if (hfsmp->hfs_extents_cp && hfsmp->hfs_extents_cp->c_lockowner == current_thread()) {
+ panic("hfs_systemfile_lock: bad lock order (Extents before Attributes)");
+ }
+#endif /* HFS_CHECK_LOCK_ORDER */
- /*
- * There were several blocking points since we first
- * checked the hash. Now that we're through blocking,
- * check the hash again in case we're racing for the
- * same hnode.
- */
- vp = hfs_vhashget(dev, catInfo->nodeData.cnd_nodeID, forkType);
- if (vp != NULL) {
- /* We lost the race, use the winner's vnode */
- FREE_ZONE(hp, sizeof(struct hfsnode), M_HFSNODE);
- *vpp = vp;
- UBCINFOCHECK("hfs_vcreate", vp);
- return (0);
+ if (hfsmp->hfs_attribute_cp) {
+ (void) hfs_lock(hfsmp->hfs_attribute_cp, locktype);
+ /*
+ * When the attribute file has overflow extents then
+ * also acquire the extents b-tree lock if its not
+ * already requested.
+ */
+ if ((flags & SFL_EXTENTS) == 0 &&
+ overflow_extents(VTOF(hfsmp->hfs_attribute_vp))) {
+ flags |= SFL_EXTENTS;
+ }
+ } else {
+ flags &= ~SFL_ATTRIBUTE;
+ }
}
+ if (flags & SFL_STARTUP) {
+#ifdef HFS_CHECK_LOCK_ORDER
+ if (hfsmp-> hfs_extents_cp && hfsmp->hfs_extents_cp->c_lockowner == current_thread()) {
+ panic("hfs_systemfile_lock: bad lock order (Extents before Startup)");
+ }
+#endif /* HFS_CHECK_LOCK_ORDER */
- /*
- * Insert the hfsnode into the hash queue, also if meta exists
- * add to sibling list and return the meta address
- */
- fm = NULL;
- if (SIBLING_FORKTYPE(forkType))
- hfs_vhashins_sibling(dev, catInfo->nodeData.cnd_nodeID, hp, &fm);
- else
- hfs_vhashins(dev, catInfo->nodeData.cnd_nodeID, hp);
-
- /* Allocate a new vnode. If unsuccesful, leave after freeing memory */
- if ((retval = getnewvnode(VT_HFS, mp, hfs_vnodeop_p, &vp))) {
- hfs_vhashrem(hp);
- if (hp->h_nodeflags & IN_WANT) {
- hp->h_nodeflags &= ~IN_WANT;
- wakeup(hp);
+ if (hfsmp->hfs_startup_cp) {
+ (void) hfs_lock(hfsmp->hfs_startup_cp, locktype);
+ } else {
+ flags &= ~SFL_STARTUP;
}
- FREE_ZONE(hp, sizeof(struct hfsnode), M_HFSNODE);
- *vpp = NULL;
- return (retval);
- }
- hp->h_vp = vp;
- vp->v_data = hp;
- hp->h_nodeflags &= ~IN_ALLOCATING;
- if (hp->h_nodeflags & IN_WANT) {
- hp->h_nodeflags &= ~IN_WANT;
- wakeup((caddr_t)hp);
+ /*
+ * When the startup file has overflow extents then
+ * also acquire the extents b-tree lock if its not
+ * already requested.
+ */
+ if ((flags & SFL_EXTENTS) == 0 &&
+ overflow_extents(VTOF(hfsmp->hfs_startup_vp))) {
+ flags |= SFL_EXTENTS;
+ }
}
-
- /*
- * If needed allocate and init the object meta data:
+ /*
+ * To prevent locks being taken in the wrong order, the extent lock
+ * gets a bitmap lock as well.
*/
- if (fm == NULL) {
- /* Allocate it....remember we can do a context switch here */
- MALLOC_ZONE(fm, struct hfsfilemeta *, sizeof(struct hfsfilemeta), M_HFSFMETA, M_WAITOK);
- bzero(fm, sizeof(struct hfsfilemeta));
-
- /* Fill it in */
+ if (flags & (SFL_BITMAP | SFL_EXTENTS)) {
/*
- * NOTICE: XXX Even though we have added the vnode to the hash so it is alive on TWO
- * accessable lists, we do not assign it until later,
- * this helps to make sure we do not use a half initiated meta
+ * If there's no bitmap cnode, ignore the bitmap lock.
*/
-
- /* Init the sibling list if needed */
- if (SIBLING_FORKTYPE(forkType)) {
- simple_lock_init(&fm->h_siblinglock);
- CIRCLEQ_INIT(&fm->h_siblinghead);
- CIRCLEQ_INSERT_HEAD(&fm->h_siblinghead, hp, h_sibling);
- };
-
- fm->h_dev = dev;
- CopyCatalogToObjectMeta(catInfo, vp, fm);
-
+ if (hfsmp->hfs_allocation_cp == NULL) {
+ flags &= ~SFL_BITMAP;
+ } else {
+ (void) hfs_lock(hfsmp->hfs_allocation_cp, HFS_EXCLUSIVE_LOCK);
+ /*
+ * The bitmap lock is also grabbed when only extent lock
+ * was requested. Set the bitmap lock bit in the lock
+ * flags which callers will use during unlock.
+ */
+ flags |= SFL_BITMAP;
+ }
+ }
+ if (flags & SFL_EXTENTS) {
/*
- * the vnode is finally alive, with the exception of the FCB below,
- * It is finally locked and ready for its debutante ball
+ * Since the extents btree lock is recursive we always
+ * need exclusive access.
*/
- hp->h_meta = fm;
- };
- fm->h_usecount++;
-
- /*
- * Init the File Control Block.
- */
- CopyCatalogToFCB(catInfo, vp);
-
- /*
- * Finish vnode initialization.
- * Setting the v_type 'stamps' the vnode as 'complete', so should be done almost last.
- *
- * At this point the vnode should be locked and fully allocated. And ready to be used
- * or accessed. (though having it locked prevents most of this, it
- * can still be accessed through lists and hashs).
- */
- vp->v_type = IFTOVT(hp->h_meta->h_mode);
- if ((vp->v_type == VREG)
- && (UBCINFOMISSING(vp) || UBCINFORECLAIMED(vp))) {
- ubc_info_init(vp);
- }
-
- /*
- * Initialize the vnode from the inode, check for aliases, sets the VROOT flag.
- * Note that the underlying vnode may have changed.
- */
- if ((retval = hfs_vinit(mp, hfs_specop_p, hfs_fifoop_p, &vp))) {
- vput(vp);
- *vpp = NULL;
- return (retval);
+ if (hfsmp->hfs_extents_cp) {
+ (void) hfs_lock(hfsmp->hfs_extents_cp, HFS_EXCLUSIVE_LOCK);
+ } else {
+ flags &= ~SFL_EXTENTS;
+ }
}
-
- /*
- * Finish inode initialization now that aliasing has been resolved.
- */
- hp->h_meta->h_devvp = hfsmp->hfs_devvp;
- VREF(hp->h_meta->h_devvp);
-
- *vpp = vp;
- return 0;
+ return (flags);
}
-void CopyCatalogToObjectMeta(struct hfsCatalogInfo *catalogInfo, struct vnode *vp, struct hfsfilemeta *fm)
+/*
+ * unlock HFS system file(s).
+ */
+void
+hfs_systemfile_unlock(struct hfsmount *hfsmp, int flags)
{
- ExtendedVCB *vcb = VTOVCB(vp);
- struct mount *mp = VTOVFS(vp);
- Boolean isHFSPlus, isDirectory;
- ushort finderFlags;
- ushort filetype;
-
- DBG_ASSERT (fm != NULL);
- DBG_ASSERT (fm->h_namelen == 0);
- DBG_ASSERT (fm->h_namePtr == 0);
-
- DBG_UTILS(("\tCopying to file's meta data: name:%s, nodeid:%ld\n", catalogInfo->nodeData.cnm_nameptr, catalogInfo->nodeData.cnd_nodeID));
-
- isHFSPlus = (vcb->vcbSigWord == kHFSPlusSigWord);
- isDirectory = (catalogInfo->nodeData.cnd_type == kCatalogFolderNode);
- finderFlags = SWAP_BE16 (((struct FInfo *)(&catalogInfo->nodeData.cnd_finderInfo))->fdFlags);
-
- /* Copy over the dirid, and hint */
- fm->h_nodeID = catalogInfo->nodeData.cnd_nodeID;
- fm->h_dirID = catalogInfo->nodeData.cnm_parID;
- fm->h_hint = catalogInfo->hint;
-
- /* Copy over the name */
- hfs_name_CatToMeta(&catalogInfo->nodeData, fm);
+ struct timeval tv;
+ u_int32_t lastfsync;
+ int numOfLockedBuffs;
-
- /* get dates in BSD format */
- fm->h_mtime = to_bsd_time(catalogInfo->nodeData.cnd_contentModDate);
- fm->h_crtime = to_bsd_time(catalogInfo->nodeData.cnd_createDate);
- fm->h_butime = to_bsd_time(catalogInfo->nodeData.cnd_backupDate);
- if (isHFSPlus) {
- fm->h_atime = to_bsd_time(catalogInfo->nodeData.cnd_accessDate);
- fm->h_ctime = to_bsd_time(catalogInfo->nodeData.cnd_attributeModDate);
+ if (hfsmp->jnl == NULL) {
+ microuptime(&tv);
+ lastfsync = tv.tv_sec;
}
- else {
- fm->h_atime = to_bsd_time(catalogInfo->nodeData.cnd_contentModDate);
- fm->h_ctime = to_bsd_time(catalogInfo->nodeData.cnd_contentModDate);
- }
-
- /* Now the rest */
- if (isHFSPlus && (catalogInfo->nodeData.cnd_mode & IFMT)) {
- fm->h_uid = catalogInfo->nodeData.cnd_ownerID;
- fm->h_gid = catalogInfo->nodeData.cnd_groupID;
- fm->h_pflags = catalogInfo->nodeData.cnd_ownerFlags |
- (catalogInfo->nodeData.cnd_adminFlags << 16);
- fm->h_mode = (mode_t)catalogInfo->nodeData.cnd_mode;
-#if 1
- if (fm->h_uid == 0xFFFFFFFD) { /* 0xfffffffd = 4294967293, the old "unknown" */
- fm->h_uid = UNKNOWNUID;
- fm->h_metaflags |= IN_CHANGE;
- vcb->vcbFlags |= kHFS_DamagedVolume; /* Trigger fsck on next mount */
- };
- if (fm->h_gid == 0xFFFFFFFD) { /* 0xfffffffd = 4294967293, the old "unknown" */
- fm->h_gid = UNKNOWNGID;
- fm->h_metaflags |= IN_CHANGE;
- vcb->vcbFlags |= kHFS_DamagedVolume; /* Trigger fsck on next mount */
- };
-#endif
- filetype = fm->h_mode & IFMT;
- if (filetype == IFCHR || filetype == IFBLK)
- fm->h_rdev = catalogInfo->nodeData.cnd_rawDevice;
- else {
- fm->h_rdev = 0;
-#if HFS_HARDLINKS
- if (catalogInfo->nodeData.cnd_type == kCatalogFileNode &&
- catalogInfo->nodeData.cnd_linkCount > 0) {
- fm->h_nlink = catalogInfo->nodeData.cnd_linkCount;
- fm->h_indnodeno = catalogInfo->nodeData.cnd_iNodeNumCopy;
- fm->h_metaflags |= IN_DATANODE;
+ if (flags & SFL_STARTUP && hfsmp->hfs_startup_cp) {
+ hfs_unlock(hfsmp->hfs_startup_cp);
+ }
+ if (flags & SFL_ATTRIBUTE && hfsmp->hfs_attribute_cp) {
+ if (hfsmp->jnl == NULL) {
+ BTGetLastSync((FCB*)VTOF(hfsmp->hfs_attribute_vp), &lastfsync);
+ numOfLockedBuffs = count_lock_queue();
+ if ((numOfLockedBuffs > kMaxLockedMetaBuffers) ||
+ ((numOfLockedBuffs > 1) && ((tv.tv_sec - lastfsync) >
+ kMaxSecsForFsync))) {
+ hfs_btsync(hfsmp->hfs_attribute_vp, HFS_SYNCTRANS);
}
-#endif
}
-
- if (mp->mnt_flag & MNT_UNKNOWNPERMISSIONS) {
- /*
- * Override the permissions as determined by the mount auguments
- * in ALMOST the same way unset permissions are treated but keep
- * track of whether or not the file or folder is hfs locked
- * by leaving the h_pflags field unchanged from what was unpacked
- * out of the catalog.
- */
- fm->h_metaflags |= IN_UNSETACCESS;
- fm->h_uid = VTOHFS(vp)->hfs_uid;
- fm->h_gid = VTOHFS(vp)->hfs_gid;
-#if OVERRIDE_UNKNOWN_PERMISSIONS
- /* Default access is full read/write/execute: */
- /* XXX won't this smash IFCHR, IFBLK and IFLNK (for no-follow lookups)? */
- fm->h_mode = ACCESSPERMS; /* 0777: rwxrwxrwx */
- fm->h_rdev = 0;
-
- /* ... but no more than that permitted by the mount point's: */
- if (isDirectory) {
- fm->h_mode &= VTOHFS(vp)->hfs_dir_mask;
- }
- else {
- fm->h_mode &= VTOHFS(vp)->hfs_file_mask;
+ hfs_unlock(hfsmp->hfs_attribute_cp);
+ }
+ if (flags & SFL_CATALOG && hfsmp->hfs_catalog_cp) {
+ if (hfsmp->jnl == NULL) {
+ BTGetLastSync((FCB*)VTOF(hfsmp->hfs_catalog_vp), &lastfsync);
+ numOfLockedBuffs = count_lock_queue();
+ if ((numOfLockedBuffs > kMaxLockedMetaBuffers) ||
+ ((numOfLockedBuffs > 1) && ((tv.tv_sec - lastfsync) >
+ kMaxSecsForFsync))) {
+ hfs_btsync(hfsmp->hfs_catalog_vp, HFS_SYNCTRANS);
}
-
- if(isDirectory)
- fm->h_mode |= IFDIR;
- else if (SUPPORTS_MAC_ALIASES && (finderFlags & kIsAlias)) /* aliases will be symlinks in the future */
- fm->h_mode |= IFLNK;
- else
- fm->h_mode |= IFREG;
-#endif
- };
- } else {
- /*
- * Set the permissions as determined by the mount auguments
- * but keep in account if the file or folder is hfs locked
- */
- fm->h_metaflags |= IN_UNSETACCESS;
- fm->h_uid = VTOHFS(vp)->hfs_uid;
- fm->h_gid = VTOHFS(vp)->hfs_gid;
- fm->h_pflags = 0; /* No valid pflags on disk (IMMUTABLE is synced from lock flag later) */
- fm->h_rdev = 0; /* No valid rdev on disk */
- /* Default access is full read/write/execute: */
- fm->h_mode = ACCESSPERMS; /* 0777: rwxrwxrwx */
-
- /* ... but no more than that permitted by the mount point's: */
- if (isDirectory) {
- fm->h_mode &= VTOHFS(vp)->hfs_dir_mask;
}
- else {
- fm->h_mode &= VTOHFS(vp)->hfs_file_mask;
+ hfs_unlock(hfsmp->hfs_catalog_cp);
+ }
+ if (flags & SFL_BITMAP && hfsmp->hfs_allocation_cp) {
+ hfs_unlock(hfsmp->hfs_allocation_cp);
+ }
+ if (flags & SFL_EXTENTS && hfsmp->hfs_extents_cp) {
+ if (hfsmp->jnl == NULL) {
+ BTGetLastSync((FCB*)VTOF(hfsmp->hfs_extents_vp), &lastfsync);
+ numOfLockedBuffs = count_lock_queue();
+ if ((numOfLockedBuffs > kMaxLockedMetaBuffers) ||
+ ((numOfLockedBuffs > 1) && ((tv.tv_sec - lastfsync) >
+ kMaxSecsForFsync))) {
+ hfs_btsync(hfsmp->hfs_extents_vp, HFS_SYNCTRANS);
+ }
}
-
- if(isDirectory)
- fm->h_mode |= IFDIR;
- else if (SUPPORTS_MAC_ALIASES && (finderFlags & kIsAlias)) /* aliases will be symlinks in the future */
- fm->h_mode |= IFLNK;
- else
- fm->h_mode |= IFREG;
- };
-
- /* Make sure that there is no nodeType/mode mismatch */
- if (isDirectory && ((fm->h_mode & IFMT) != IFDIR)) {
- fm->h_mode &= ~IFMT; /* Clear the bad bits */
- fm->h_mode |= IFDIR; /* Set the proper one */
- };
-
- /* Make sure the IMMUTABLE bits are in sync with the locked flag in the catalog: */
- if (!isDirectory) {
- if (catalogInfo->nodeData.cnd_flags & kHFSFileLockedMask) {
- /* The file's supposed to be locked:
- Make sure at least one of the IMMUTABLE bits is set: */
- if ((fm->h_pflags & (SF_IMMUTABLE | UF_IMMUTABLE)) == 0) {
- fm->h_pflags |= UF_IMMUTABLE; /* Set the user-changable IMMUTABLE bit */
- };
- } else {
- /* The file's supposed to be unlocked: */
- fm->h_pflags &= ~(SF_IMMUTABLE | UF_IMMUTABLE);
- };
- };
-
- if (isDirectory) {
- fm->h_nlink = 2 + catalogInfo->nodeData.cnd_valence;
- fm->h_size = (2 * sizeof(hfsdotentry)) +
- (catalogInfo->nodeData.cnd_valence * AVERAGE_HFSDIRENTRY_SIZE);
- if (fm->h_size < MAX_HFSDIRENTRY_SIZE)
- fm->h_size = MAX_HFSDIRENTRY_SIZE;
- } else {
- fm->h_size = (off_t)vcb->blockSize *
- (off_t)(catalogInfo->nodeData.cnd_rsrcfork.totalBlocks +
- catalogInfo->nodeData.cnd_datafork.totalBlocks);
+ hfs_unlock(hfsmp->hfs_extents_cp);
}
}
-void CopyCatalogToFCB(struct hfsCatalogInfo *catalogInfo, struct vnode *vp)
-{
- FCB *fcb = VTOFCB(vp);
- ExtendedVCB *vcb = VTOVCB(vp);
- Boolean isHFSPlus, isDirectory, isResource;
- HFSPlusExtentDescriptor *extents;
- UInt8 forkType;
-
- DBG_ASSERT (vp != NULL);
- DBG_ASSERT (fcb != NULL);
- DBG_ASSERT (vcb != NULL);
- DBG_ASSERT (VTOH(vp) != NULL);
-
- forkType = H_FORKTYPE(VTOH(vp));
- isResource = (forkType == kRsrcFork);
- isDirectory = (catalogInfo->nodeData.cnd_type == kCatalogFolderNode);
- isHFSPlus = (vcb->vcbSigWord == kHFSPlusSigWord);
-
- /* Init the fcb */
- fcb->fcbFlags = catalogInfo->nodeData.cnd_flags;
-
- if (forkType != kDirectory) {
- fcb->fcbFlags &= kHFSFileLockedMask; /* Clear resource, dirty bits */
- if (fcb->fcbFlags != 0) /* if clear, its not locked, then.. */
- fcb->fcbFlags = fcbFileLockedMask; /* duplicate the bit for later use */
-
- fcb->fcbClmpSize = vcb->vcbClpSiz; /*XXX why not use the one in catalogInfo? */
-
- if (isResource)
- extents = catalogInfo->nodeData.cnd_rsrcfork.extents;
- else
- extents = catalogInfo->nodeData.cnd_datafork.extents;
-
- /* Copy the extents to their correct location: */
- bcopy (extents, fcb->fcbExtents, sizeof(HFSPlusExtentRecord));
-
- if (isResource) {
- fcb->fcbEOF = catalogInfo->nodeData.cnd_rsrcfork.logicalSize;
- fcb->fcbPLen = (off_t)((off_t)catalogInfo->nodeData.cnd_rsrcfork.totalBlocks * (off_t)vcb->blockSize);
- fcb->fcbFlags |= fcbResourceMask;
- } else {
- fcb->fcbEOF = catalogInfo->nodeData.cnd_datafork.logicalSize;
- fcb->fcbPLen = (off_t)((off_t)catalogInfo->nodeData.cnd_datafork.totalBlocks * (off_t)vcb->blockSize);
- };
- };
-
-
-}
-
-int hasOverflowExtents(struct hfsnode *hp)
-{
- ExtendedVCB *vcb = HTOVCB(hp);
- FCB *fcb = HTOFCB(hp);
- u_long blocks;
-
- if (vcb->vcbSigWord == kHFSPlusSigWord)
- {
-
- if (fcb->fcbExtents[7].blockCount == 0)
- return false;
-
- blocks = fcb->fcbExtents[0].blockCount +
- fcb->fcbExtents[1].blockCount +
- fcb->fcbExtents[2].blockCount +
- fcb->fcbExtents[3].blockCount +
- fcb->fcbExtents[4].blockCount +
- fcb->fcbExtents[5].blockCount +
- fcb->fcbExtents[6].blockCount +
- fcb->fcbExtents[7].blockCount;
- }
- else
- {
- if (fcb->fcbExtents[2].blockCount == 0)
- return false;
-
- blocks = fcb->fcbExtents[0].blockCount +
- fcb->fcbExtents[1].blockCount +
- fcb->fcbExtents[2].blockCount;
- }
-
- return ((fcb->fcbPLen / vcb->blockSize) > blocks);
-}
-
-
-int hfs_metafilelocking(struct hfsmount *hfsmp, u_long fileID, u_int flags, struct proc *p)
+/*
+ * RequireFileLock
+ *
+ * Check to see if a vnode is locked in the current context
+ * This is to be used for debugging purposes only!!
+ */
+#if HFS_DIAGNOSTIC
+void RequireFileLock(FileReference vp, int shareable)
{
- ExtendedVCB *vcb;
- struct vnode *vp = NULL;
- int numOfLockedBuffs;
- int retval = 0;
-
- vcb = HFSTOVCB(hfsmp);
-
- DBG_UTILS(("hfs_metafilelocking: vol: %d, file: %d %s%s%s\n", vcb->vcbVRefNum, fileID,
- ((flags & LK_TYPE_MASK) == LK_RELEASE ? "RELEASE" : ""),
- ((flags & LK_TYPE_MASK) == LK_EXCLUSIVE ? "EXCLUSIVE" : ""),
- ((flags & LK_TYPE_MASK) == LK_SHARED ? "SHARED" : "") ));
+ int locked;
-
- switch (fileID)
- {
+ /* The extents btree and allocation bitmap are always exclusive. */
+ if (VTOC(vp)->c_fileid == kHFSExtentsFileID ||
+ VTOC(vp)->c_fileid == kHFSAllocationFileID) {
+ shareable = 0;
+ }
+
+ locked = VTOC(vp)->c_lockowner == (void *)current_thread();
+
+ if (!locked && !shareable) {
+ switch (VTOC(vp)->c_fileid) {
case kHFSExtentsFileID:
- vp = vcb->extentsRefNum;
+ panic("hfs: extents btree not locked! v: 0x%08X\n #\n", (u_int)vp);
break;
-
case kHFSCatalogFileID:
- vp = vcb->catalogRefNum;
+ panic("hfs: catalog btree not locked! v: 0x%08X\n #\n", (u_int)vp);
break;
-
case kHFSAllocationFileID:
- /* bitmap is covered by Extents B-tree locking */
- /* FALL THROUGH */
- default:
- panic("hfs_lockmetafile: invalid fileID");
+ /* The allocation file can hide behind the jornal lock. */
+ if (VTOHFS(vp)->jnl == NULL)
+ panic("hfs: allocation file not locked! v: 0x%08X\n #\n", (u_int)vp);
+ break;
+ case kHFSStartupFileID:
+ panic("hfs: startup file not locked! v: 0x%08X\n #\n", (u_int)vp);
+ case kHFSAttributesFileID:
+ panic("hfs: attributes btree not locked! v: 0x%08X\n #\n", (u_int)vp);
+ break;
+ }
}
-
- if (vp != NULL) {
-
- /* Release, if necesary any locked buffer caches */
- if ((flags & LK_TYPE_MASK) == LK_RELEASE) {
- struct timeval tv = time;
- u_int32_t lastfsync = tv.tv_sec;
-
- (void) BTGetLastSync(VTOFCB(vp), &lastfsync);
-
- numOfLockedBuffs = count_lock_queue();
- if ((numOfLockedBuffs > kMaxLockedMetaBuffers) || ((numOfLockedBuffs>1) && ((tv.tv_sec - lastfsync) > kMaxSecsForFsync))) {
- DBG_UTILS(("Synching meta deta: %d... # locked buffers = %d, fsync gap = %ld\n", H_FILEID(VTOH(vp)),
- numOfLockedBuffs, (tv.tv_sec - lastfsync)));
- hfs_fsync_transaction(vp);
- };
- };
-
- retval = lockmgr(&VTOH(vp)->h_lock, flags, &vp->v_interlock, p);
- };
-
- return retval;
}
+#endif
/*
* There are three ways to qualify for ownership rights on an object:
*
- * 1. (a) Your UID matches the UID of the vnode
- * (b) The object in question is owned by "unknown" and your UID matches the console user's UID
- * 2. (a) Permissions on the filesystem are being ignored and your UID matches the replacement UID
- * (b) Permissions on the filesystem are being ignored and the replacement UID is "unknown" and
- * your UID matches the console user UID
- * 3. You are root
+ * 1. (a) Your UID matches the cnode's UID.
+ * (b) The object in question is owned by "unknown"
+ * 2. (a) Permissions on the filesystem are being ignored and
+ * your UID matches the replacement UID.
+ * (b) Permissions on the filesystem are being ignored and
+ * the replacement UID is "unknown".
+ * 3. You are root.
*
*/
-int hfs_owner_rights(struct vnode *vp, struct ucred *cred, struct proc *p, Boolean invokesuperuserstatus) {
- return ((cred->cr_uid == VTOH(vp)->h_meta->h_uid) || /* [1a] */
- ((VTOH(vp)->h_meta->h_uid == UNKNOWNUID) && (cred->cr_uid == console_user)) || /* [1b] */
- ((VTOVFS(vp)->mnt_flag & MNT_UNKNOWNPERMISSIONS) && /* [2] */
- ((cred->cr_uid == VTOHFS(vp)->hfs_uid) || /* [2a] */
- ((VTOHFS(vp)->hfs_uid == UNKNOWNUID) && (cred->cr_uid == console_user)))) || /* [2b] */
- (invokesuperuserstatus && (suser(cred, &p->p_acflag) == 0))) ? 0 : EPERM;
+int
+hfs_owner_rights(struct hfsmount *hfsmp, uid_t cnode_uid, kauth_cred_t cred,
+ __unused struct proc *p, int invokesuperuserstatus)
+{
+ if ((kauth_cred_getuid(cred) == cnode_uid) || /* [1a] */
+ (cnode_uid == UNKNOWNUID) || /* [1b] */
+ ((((unsigned int)vfs_flags(HFSTOVFS(hfsmp))) & MNT_UNKNOWNPERMISSIONS) && /* [2] */
+ ((kauth_cred_getuid(cred) == hfsmp->hfs_uid) || /* [2a] */
+ (hfsmp->hfs_uid == UNKNOWNUID))) || /* [2b] */
+ (invokesuperuserstatus && (suser(cred, 0) == 0))) { /* [3] */
+ return (0);
+ } else {
+ return (EPERM);
+ }
}
+u_int32_t BestBlockSizeFit(u_int32_t allocationBlockSize,
+ u_int32_t blockSizeLimit,
+ u_int32_t baseMultiple) {
+ /*
+ Compute the optimal (largest) block size (no larger than allocationBlockSize) that is less than the
+ specified limit but still an even multiple of the baseMultiple.
+ */
+ int baseBlockCount, blockCount;
+ u_int32_t trialBlockSize;
-int hfs_catalogentry_owner_rights(uid_t obj_uid, struct mount *mp, struct ucred *cred, struct proc *p, Boolean invokesuperuserstatus) {
- return ((cred->cr_uid == obj_uid) || /* [1a] */
- ((obj_uid == UNKNOWNUID) && (cred->cr_uid == console_user)) || /* [1b] */
- ((mp->mnt_flag & MNT_UNKNOWNPERMISSIONS) && /* [2] */
- ((cred->cr_uid == VFSTOHFS(mp)->hfs_uid) || /* [2a] */
- ((VFSTOHFS(mp)->hfs_uid == UNKNOWNUID) && (cred->cr_uid == console_user)))) || /* [2b] */
- (invokesuperuserstatus && (suser(cred, &p->p_acflag) == 0))) ? 0 : EPERM;
-}
+ if (allocationBlockSize % baseMultiple != 0) {
+ /*
+ Whoops: the allocation blocks aren't even multiples of the specified base:
+ no amount of dividing them into even parts will be a multiple, either then!
+ */
+ return 512; /* Hope for the best */
+ };
+ /* Try the obvious winner first, to prevent 12K allocation blocks, for instance,
+ from being handled as two 6K logical blocks instead of 3 4K logical blocks.
+ Even though the former (the result of the loop below) is the larger allocation
+ block size, the latter is more efficient: */
+ if (allocationBlockSize % PAGE_SIZE == 0) return PAGE_SIZE;
+ /* No clear winner exists: pick the largest even fraction <= MAXBSIZE: */
+ baseBlockCount = allocationBlockSize / baseMultiple; /* Now guaranteed to be an even multiple */
-void CopyVNodeToCatalogNode (struct vnode *vp, struct CatalogNodeData *nodeData)
-{
- ExtendedVCB *vcb;
- FCB *fcb;
- struct hfsnode *hp;
- Boolean isHFSPlus, isResource;
- HFSPlusExtentDescriptor *extents;
- off_t fileReadLimit;
-
- hp = VTOH(vp);
- vcb = HTOVCB(hp);
- fcb = HTOFCB(hp);
- isResource = (H_FORKTYPE(hp) == kRsrcFork);
- isHFSPlus = (vcb->vcbSigWord == kHFSPlusSigWord);
-
- /* date and time of last fork modification */
- if (hp->h_meta->h_mtime != 0)
- nodeData->cnd_contentModDate = to_hfs_time(hp->h_meta->h_mtime);
-
- if (isHFSPlus) {
- /* Make sure that there is no nodeType/mode mismatch */
- if ((nodeData->cnd_type == kCatalogFolderNode)
- && ((hp->h_meta->h_mode & IFMT) != IFDIR)) {
-
- DBG_ASSERT((hp->h_meta->h_mode & IFMT) == IFDIR);
- hp->h_meta->h_mode &= ~IFMT; /* Clear the bad bits */
- hp->h_meta->h_mode |= IFDIR; /* Set the proper one */
- };
- /* date and time of last modification (any kind) */
- if (hp->h_meta->h_ctime != 0)
- nodeData->cnd_attributeModDate = to_hfs_time(hp->h_meta->h_ctime);
- /* date and time of last access (MacOS X only) */
- if (hp->h_meta->h_atime != 0)
- nodeData->cnd_accessDate = to_hfs_time(hp->h_meta->h_atime);
- /* hfs_setattr can change the create date */
- if (hp->h_meta->h_crtime != 0)
- nodeData->cnd_createDate = to_hfs_time(hp->h_meta->h_crtime);
- if (! (hp->h_meta->h_metaflags & IN_UNSETACCESS)) {
- nodeData->cnd_adminFlags = hp->h_meta->h_pflags >> 16;
- nodeData->cnd_ownerFlags = hp->h_meta->h_pflags & 0x000000FF;
- nodeData->cnd_mode = hp->h_meta->h_mode;
- nodeData->cnd_ownerID = hp->h_meta->h_uid;
- nodeData->cnd_groupID = hp->h_meta->h_gid;
- }
- };
-
- /* the rest only applies to files */
- if (nodeData->cnd_type == kCatalogFileNode) {
- if (hp->h_meta->h_pflags & (SF_IMMUTABLE | UF_IMMUTABLE)) {
- /* The file is locked: set the locked bit in the catalog. */
- nodeData->cnd_flags |= kHFSFileLockedMask;
- } else {
- /* The file is unlocked: make sure the locked bit in the catalog is clear. */
- nodeData->cnd_flags &= ~kHFSFileLockedMask;
- };
- if (CIRCLEQ_EMPTY(&hp->h_invalidranges)) {
- fileReadLimit = fcb->fcbEOF;
- } else {
- fileReadLimit = CIRCLEQ_FIRST(&hp->h_invalidranges)->rl_start;
- };
- if (isResource) {
- extents = nodeData->cnd_rsrcfork.extents;
- nodeData->cnd_rsrcfork.logicalSize = fileReadLimit;
- nodeData->cnd_rsrcfork.totalBlocks = fcb->fcbPLen / vcb->blockSize;
- } else {
- extents = nodeData->cnd_datafork.extents;
- nodeData->cnd_datafork.logicalSize = fileReadLimit;
- nodeData->cnd_datafork.totalBlocks = fcb->fcbPLen / vcb->blockSize;
- };
+ for (blockCount = baseBlockCount; blockCount > 0; --blockCount) {
+ trialBlockSize = blockCount * baseMultiple;
+ if (allocationBlockSize % trialBlockSize == 0) { /* An even multiple? */
+ if ((trialBlockSize <= blockSizeLimit) &&
+ (trialBlockSize % baseMultiple == 0)) {
+ return trialBlockSize;
+ };
+ };
+ };
- bcopy ( fcb->fcbExtents, extents, sizeof(HFSPlusExtentRecord));
+ /* Note: we should never get here, since blockCount = 1 should always work,
+ but this is nice and safe and makes the compiler happy, too ... */
+ return 512;
+}
- if ((vp->v_type == VBLK) || (vp->v_type == VCHR))
- nodeData->cnd_rawDevice = hp->h_meta->h_rdev;
- else if (hp->h_meta->h_metaflags & IN_DATANODE)
- nodeData->cnd_linkCount = hp->h_meta->h_nlink;
-
- if (vp->v_type == VLNK) {
- ((struct FInfo *)(&nodeData->cnd_finderInfo))->fdType = SWAP_BE32 (kSymLinkFileType);
- ((struct FInfo *)(&nodeData->cnd_finderInfo))->fdCreator = SWAP_BE32 (kSymLinkCreator);
+
+u_int32_t
+GetFileInfo(ExtendedVCB *vcb, __unused u_int32_t dirid, const char *name,
+ struct cat_attr *fattr, struct cat_fork *forkinfo)
+{
+ struct hfsmount * hfsmp;
+ struct cat_desc jdesc;
+ int lockflags;
+ int error;
- /* Set this up as an alias */
- #if SUPPORTS_MAC_ALIASES
- ((struct FInfo *)(&nodeData->cnd_finderInfo))->fdFlags |= SWAP_BE16 (kIsAlias);
- #endif
- }
- }
- }
+ if (vcb->vcbSigWord != kHFSPlusSigWord)
+ return (0);
+
+ hfsmp = VCBTOHFS(vcb);
+ memset(&jdesc, 0, sizeof(struct cat_desc));
+ jdesc.cd_parentcnid = kRootDirID;
+ jdesc.cd_nameptr = (const u_int8_t *)name;
+ jdesc.cd_namelen = strlen(name);
-/*********************************************************************
+ lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
+ error = cat_lookup(hfsmp, &jdesc, 0, NULL, fattr, forkinfo, NULL);
+ hfs_systemfile_unlock(hfsmp, lockflags);
- Sets the name in the filemeta structure
+ if (error == 0) {
+ return (fattr->ca_fileid);
+ } else if (hfsmp->hfs_flags & HFS_READ_ONLY) {
+ return (0);
+ }
- XXX Does not preflight if changing from one size to another
- XXX Currently not protected from context switching
+ return (0); /* XXX what callers expect on an error */
+}
-*********************************************************************/
-void hfs_set_metaname(char *name, struct hfsfilemeta *fm, struct hfsmount *hfsmp)
+/*
+ * On HFS Plus Volumes, there can be orphaned files or directories
+ * These are files or directories that were unlinked while busy.
+ * If the volume was not cleanly unmounted then some of these may
+ * have persisted and need to be removed.
+ */
+void
+hfs_remove_orphans(struct hfsmount * hfsmp)
{
-int namelen = strlen(name);
-char *tname, *fname;
+ struct BTreeIterator * iterator = NULL;
+ struct FSBufferDescriptor btdata;
+ struct HFSPlusCatalogFile filerec;
+ struct HFSPlusCatalogKey * keyp;
+ struct proc *p = current_proc();
+ FCB *fcb;
+ ExtendedVCB *vcb;
+ char filename[32];
+ char tempname[32];
+ size_t namelen;
+ cat_cookie_t cookie;
+ int catlock = 0;
+ int catreserve = 0;
+ int started_tr = 0;
+ int lockflags;
+ int result;
+ int orphaned_files = 0;
+ int orphaned_dirs = 0;
+
+ bzero(&cookie, sizeof(cookie));
+
+ if (hfsmp->hfs_flags & HFS_CLEANED_ORPHANS)
+ return;
-#if HFS_DIAGNOSTIC
- DBG_ASSERT(name != NULL);
- DBG_ASSERT(fm != NULL);
- if (fm->h_namePtr) {
- DBG_ASSERT(fm->h_namelen == strlen(fm->h_namePtr));
- if (strlen(fm->h_namePtr) > MAXHFSVNODELEN)
- DBG_ASSERT(fm->h_metaflags & IN_LONGNAME);
- };
- if (fm->h_metaflags & IN_LONGNAME) {
- DBG_ASSERT(fm->h_namePtr != (char *)fm->h_fileName);
- DBG_ASSERT(fm->h_namePtr != NULL);
- };
-#endif //HFS_DIAGNOSTIC
+ vcb = HFSTOVCB(hfsmp);
+ fcb = VTOF(hfsmp->hfs_catalog_vp);
+
+ btdata.bufferAddress = &filerec;
+ btdata.itemSize = sizeof(filerec);
+ btdata.itemCount = 1;
+
+ MALLOC(iterator, struct BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
+ bzero(iterator, sizeof(*iterator));
+ /* Build a key to "temp" */
+ keyp = (HFSPlusCatalogKey*)&iterator->key;
+ keyp->parentID = hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid;
+ keyp->nodeName.length = 4; /* "temp" */
+ keyp->keyLength = kHFSPlusCatalogKeyMinimumLength + keyp->nodeName.length * 2;
+ keyp->nodeName.unicode[0] = 't';
+ keyp->nodeName.unicode[1] = 'e';
+ keyp->nodeName.unicode[2] = 'm';
+ keyp->nodeName.unicode[3] = 'p';
+
/*
- * Details that have to be dealt with:
- * 1. No name is allocated. fm->h_namePtr should be NULL
- * 2. A name is being changed and:
- * a. it was in static space and now cannot fit
- * b. It was malloc'd and now will fit in the static
- * c. It did and will fit in the static
- * This could be a little smarter:
- * - Dont re'malloc if the new name is smaller (but then wasting memory)
- * - If its a longname but the same size, we still free and malloc
- * -
+ * Position the iterator just before the first real temp file/dir.
*/
-
-
- /* Allocate the new memory */
- if (namelen > MAXHFSVNODELEN) {
+ lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK);
+ (void) BTSearchRecord(fcb, iterator, NULL, NULL, iterator);
+ hfs_systemfile_unlock(hfsmp, lockflags);
+
+ /* Visit all the temp files/dirs in the HFS+ private directory. */
+ for (;;) {
+ lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK);
+ result = BTIterateRecord(fcb, kBTreeNextRecord, iterator, &btdata, NULL);
+ hfs_systemfile_unlock(hfsmp, lockflags);
+ if (result)
+ break;
+ if (keyp->parentID != hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid)
+ break;
+
+ (void) utf8_encodestr(keyp->nodeName.unicode, keyp->nodeName.length * 2,
+ (u_int8_t *)filename, &namelen, sizeof(filename), 0, 0);
+
+ (void) snprintf(tempname, sizeof(tempname), "%s%d",
+ HFS_DELETE_PREFIX, filerec.fileID);
+
/*
- * Notice the we ALWAYS allocate, even if the new is less then the old,
- * or even if they are the SAME
+ * Delete all files (and directories) named "tempxxx",
+ * where xxx is the file's cnid in decimal.
+ *
*/
- MALLOC(tname, char *, namelen+1, M_TEMP, M_WAITOK);
- }
- else
- tname = fm->h_fileName;
+ if (bcmp(tempname, filename, namelen) == 0) {
+ struct filefork dfork;
+ struct filefork rfork;
+ struct cnode cnode;
+ int mode = 0;
+
+ bzero(&dfork, sizeof(dfork));
+ bzero(&rfork, sizeof(rfork));
+ bzero(&cnode, sizeof(cnode));
+
+ /* Delete any attributes, ignore errors */
+ (void) hfs_removeallattr(hfsmp, filerec.fileID);
+
+ if (hfs_start_transaction(hfsmp) != 0) {
+ printf("hfs_remove_orphans: failed to start transaction\n");
+ goto exit;
+ }
+ started_tr = 1;
+
+ /*
+ * Reserve some space in the Catalog file.
+ */
+ if (cat_preflight(hfsmp, CAT_DELETE, &cookie, p) != 0) {
+ printf("hfs_remove_orphans: cat_preflight failed\n");
+ goto exit;
+ }
+ catreserve = 1;
+
+ lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG | SFL_ATTRIBUTE | SFL_EXTENTS | SFL_BITMAP, HFS_EXCLUSIVE_LOCK);
+ catlock = 1;
+
+ /* Build a fake cnode */
+ cat_convertattr(hfsmp, (CatalogRecord *)&filerec, &cnode.c_attr,
+ &dfork.ff_data, &rfork.ff_data);
+ cnode.c_desc.cd_parentcnid = hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid;
+ cnode.c_desc.cd_nameptr = (const u_int8_t *)filename;
+ cnode.c_desc.cd_namelen = namelen;
+ cnode.c_desc.cd_cnid = cnode.c_attr.ca_fileid;
+ cnode.c_blocks = dfork.ff_blocks + rfork.ff_blocks;
+
+ /* Position iterator at previous entry */
+ if (BTIterateRecord(fcb, kBTreePrevRecord, iterator,
+ NULL, NULL) != 0) {
+ break;
+ }
- simple_lock(&hfsmp->hfs_renamelock);
-
- /* Check to see if there is something to free, if yes, remember it */
- if (fm->h_metaflags & IN_LONGNAME)
- fname = fm->h_namePtr;
- else
- fname = NULL;
-
- /* Set the flag */
- if (namelen > MAXHFSVNODELEN) {
- fm->h_metaflags |= IN_LONGNAME;
- }
- else {
- fm->h_metaflags &= ~IN_LONGNAME;
- };
+ /* Truncate the file to zero (both forks) */
+ if (dfork.ff_blocks > 0) {
+ u_int64_t fsize;
+
+ dfork.ff_cp = &cnode;
+ cnode.c_datafork = &dfork;
+ cnode.c_rsrcfork = NULL;
+ fsize = (u_int64_t)dfork.ff_blocks * (u_int64_t)HFSTOVCB(hfsmp)->blockSize;
+ while (fsize > 0) {
+ if (fsize > HFS_BIGFILE_SIZE && overflow_extents(&dfork)) {
+ fsize -= HFS_BIGFILE_SIZE;
+ } else {
+ fsize = 0;
+ }
+
+ if (TruncateFileC(vcb, (FCB*)&dfork, fsize, 1, 0,
+ cnode.c_attr.ca_fileid, false) != 0) {
+ printf("hfs: error truncating data fork!\n");
+
+ break;
+ }
+
+ //
+ // if we're iteratively truncating this file down,
+ // then end the transaction and start a new one so
+ // that no one transaction gets too big.
+ //
+ if (fsize > 0 && started_tr) {
+ /* Drop system file locks before starting
+ * another transaction to preserve lock order.
+ */
+ hfs_systemfile_unlock(hfsmp, lockflags);
+ catlock = 0;
+ hfs_end_transaction(hfsmp);
+
+ if (hfs_start_transaction(hfsmp) != 0) {
+ started_tr = 0;
+ break;
+ }
+ lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG | SFL_ATTRIBUTE | SFL_EXTENTS | SFL_BITMAP, HFS_EXCLUSIVE_LOCK);
+ catlock = 1;
+ }
+ }
+ }
- /* Now copy it over */
- bcopy(name, tname, namelen+1);
+ if (rfork.ff_blocks > 0) {
+ rfork.ff_cp = &cnode;
+ cnode.c_datafork = NULL;
+ cnode.c_rsrcfork = &rfork;
+ if (TruncateFileC(vcb, (FCB*)&rfork, 0, 1, 1, cnode.c_attr.ca_fileid, false) != 0) {
+ printf("hfs: error truncating rsrc fork!\n");
+ break;
+ }
+ }
- fm->h_namePtr = tname;
- fm->h_namelen = namelen;
+ /* Remove the file or folder record from the Catalog */
+ if (cat_delete(hfsmp, &cnode.c_desc, &cnode.c_attr) != 0) {
+ printf("hfs_remove_orphans: error deleting cat rec for id %d!\n", cnode.c_desc.cd_cnid);
+ hfs_systemfile_unlock(hfsmp, lockflags);
+ catlock = 0;
+ hfs_volupdate(hfsmp, VOL_UPDATE, 0);
+ break;
+ }
+
+ mode = cnode.c_attr.ca_mode & S_IFMT;
- simple_unlock(&hfsmp->hfs_renamelock);
-
- /* Lastly, free the old, if set */
- if (fname != NULL)
- FREE(fname, M_TEMP);
-
-}
+ if (mode == S_IFDIR) {
+ orphaned_dirs++;
+ }
+ else {
+ orphaned_files++;
+ }
-void hfs_name_CatToMeta(CatalogNodeData *nodeData, struct hfsfilemeta *fm)
-{
-char *fname;
+ /* Update parent and volume counts */
+ hfsmp->hfs_private_attr[FILE_HARDLINKS].ca_entries--;
+ if (mode == S_IFDIR) {
+ DEC_FOLDERCOUNT(hfsmp, hfsmp->hfs_private_attr[FILE_HARDLINKS]);
+ }
-#if HFS_DIAGNOSTIC
- DBG_ASSERT(nodeData != NULL);
- DBG_ASSERT(fm != NULL);
- if (fm->h_namePtr) {
- DBG_ASSERT(fm->h_namelen == strlen(fm->h_namePtr));
- if (strlen(fm->h_namePtr) > MAXHFSVNODELEN)
- DBG_ASSERT(fm->h_metaflags & IN_LONGNAME);
- };
- if (fm->h_metaflags & IN_LONGNAME) {
- DBG_ASSERT(fm->h_namePtr != (char *)fm->h_fileName);
- DBG_ASSERT(fm->h_namePtr != NULL);
- };
-
- DBG_ASSERT(nodeData->cnm_nameptr != NULL);
-
- if (nodeData->cnm_length) {
- DBG_ASSERT(strlen(nodeData->cnm_nameptr) == nodeData->cnm_length);
- }
-
- if (nodeData->cnm_length > MAXHFSVNODELEN)
- { DBG_ASSERT(nodeData->cnm_nameptr != nodeData->cnm_namespace); }
- else if (nodeData->cnm_nameptr)
- { DBG_ASSERT(nodeData->cnm_nameptr == nodeData->cnm_namespace); }
+ (void)cat_update(hfsmp, &hfsmp->hfs_private_desc[FILE_HARDLINKS],
+ &hfsmp->hfs_private_attr[FILE_HARDLINKS], NULL, NULL);
-#endif //HFS_DIAGNOSTIC
-
+ /* Drop locks and end the transaction */
+ hfs_systemfile_unlock(hfsmp, lockflags);
+ cat_postflight(hfsmp, &cookie, p);
+ catlock = catreserve = 0;
- /* Check to see if there is something to free, if yes, remember it */
- if (fm->h_metaflags & IN_LONGNAME)
- fname = fm->h_namePtr;
- else
- fname = NULL;
+ /*
+ Now that Catalog is unlocked, update the volume info, making
+ sure to differentiate between files and directories
+ */
+ if (mode == S_IFDIR) {
+ hfs_volupdate(hfsmp, VOL_RMDIR, 0);
+ }
+ else{
+ hfs_volupdate(hfsmp, VOL_RMFILE, 0);
+ }
- /* Set the flag */
- if (nodeData->cnm_length > MAXHFSVNODELEN) {
- fm->h_metaflags |= IN_LONGNAME;
- } else {
- fm->h_metaflags &= ~IN_LONGNAME;
- };
+ if (started_tr) {
+ hfs_end_transaction(hfsmp);
+ started_tr = 0;
+ }
- /* Copy over the name */
- if (nodeData->cnm_nameptr == nodeData->cnm_namespace) {
- bcopy(nodeData->cnm_namespace, fm->h_fileName, nodeData->cnm_length+1);
- fm->h_namePtr = fm->h_fileName;
- }
- else {
- fm->h_namePtr = nodeData->cnm_nameptr;
+ } /* end if */
+ } /* end for */
+ if (orphaned_files > 0 || orphaned_dirs > 0)
+ printf("hfs: Removed %d orphaned / unlinked files and %d directories \n", orphaned_files, orphaned_dirs);
+exit:
+ if (catlock) {
+ hfs_systemfile_unlock(hfsmp, lockflags);
+ }
+ if (catreserve) {
+ cat_postflight(hfsmp, &cookie, p);
+ }
+ if (started_tr) {
+ hfs_end_transaction(hfsmp);
}
- fm->h_namelen = nodeData->cnm_length;
-
- nodeData->cnm_flags |= kCatNameIsConsumed;
- nodeData->cnm_flags &= ~kCatNameIsAllocated;
- nodeData->cnm_length = 0;
- nodeData->cnm_nameptr = (char *)0;
- nodeData->cnm_namespace[0] = 0;
-
- /* Lastly, free the old, if set */
- if (fname != NULL)
- FREE(fname, M_TEMP);
-}
-
-
-
-unsigned long DerivePermissionSummary(uid_t obj_uid, gid_t obj_gid, mode_t obj_mode, struct mount *mp, struct ucred *cred, struct proc *p) {
- register gid_t *gp;
- unsigned long permissions;
- int i;
-
- /* User id 0 (root) always gets access. */
- if (cred->cr_uid == 0) {
- permissions = R_OK | W_OK | X_OK;
- goto Exit;
- };
-
- /* Otherwise, check the owner. */
- if (hfs_catalogentry_owner_rights(obj_uid, mp, cred, p, false) == 0) {
- permissions = ((unsigned long)obj_mode & S_IRWXU) >> 6;
- goto Exit;
- }
-
- /* Otherwise, check the groups. */
- if (! (mp->mnt_flag & MNT_UNKNOWNPERMISSIONS)) {
- for (i = 0, gp = cred->cr_groups; i < cred->cr_ngroups; i++, gp++) {
- if (obj_gid == *gp) {
- permissions = ((unsigned long)obj_mode & S_IRWXG) >> 3;
- goto Exit;
- }
- };
- };
-
- /* Otherwise, settle for 'others' access. */
- permissions = (unsigned long)obj_mode & S_IRWXO;
-
-Exit:
- return permissions;
+ FREE(iterator, M_TEMP);
+ hfsmp->hfs_flags |= HFS_CLEANED_ORPHANS;
}
-
-int AttributeBlockSize(struct attrlist *attrlist) {
- int size;
- attrgroup_t a;
+/*
+ * This will return the correct logical block size for a given vnode.
+ * For most files, it is the allocation block size, for meta data like
+ * BTrees, this is kept as part of the BTree private nodeSize
+ */
+u_int32_t
+GetLogicalBlockSize(struct vnode *vp)
+{
+u_int32_t logBlockSize;
-#if ((ATTR_CMN_NAME | ATTR_CMN_DEVID | ATTR_CMN_FSID | ATTR_CMN_OBJTYPE | \
- ATTR_CMN_OBJTAG | ATTR_CMN_OBJID | ATTR_CMN_OBJPERMANENTID | ATTR_CMN_PAROBJID | \
- ATTR_CMN_SCRIPT | ATTR_CMN_CRTIME | ATTR_CMN_MODTIME | ATTR_CMN_CHGTIME | \
- ATTR_CMN_ACCTIME | ATTR_CMN_BKUPTIME | ATTR_CMN_FNDRINFO | ATTR_CMN_OWNERID | \
- ATTR_CMN_GRPID | ATTR_CMN_ACCESSMASK | ATTR_CMN_NAMEDATTRCOUNT | ATTR_CMN_NAMEDATTRLIST| \
- ATTR_CMN_FLAGS | ATTR_CMN_USERACCESS) != ATTR_CMN_VALIDMASK)
-#error AttributeBlockSize: Missing bits in common mask computation!
-#endif
- DBG_ASSERT((attrlist->commonattr & ~ATTR_CMN_VALIDMASK) == 0);
-
-#if ((ATTR_VOL_FSTYPE | ATTR_VOL_SIGNATURE | ATTR_VOL_SIZE | ATTR_VOL_SPACEFREE | \
- ATTR_VOL_SPACEAVAIL | ATTR_VOL_MINALLOCATION | ATTR_VOL_ALLOCATIONCLUMP | ATTR_VOL_IOBLOCKSIZE | \
- ATTR_VOL_OBJCOUNT | ATTR_VOL_FILECOUNT | ATTR_VOL_DIRCOUNT | ATTR_VOL_MAXOBJCOUNT | \
- ATTR_VOL_MOUNTPOINT | ATTR_VOL_NAME | ATTR_VOL_MOUNTFLAGS | ATTR_VOL_INFO | \
- ATTR_VOL_MOUNTEDDEVICE| ATTR_VOL_ENCODINGSUSED | ATTR_VOL_CAPABILITIES | ATTR_VOL_ATTRIBUTES) != ATTR_VOL_VALIDMASK)
-#error AttributeBlockSize: Missing bits in volume mask computation!
-#endif
- DBG_ASSERT((attrlist->volattr & ~ATTR_VOL_VALIDMASK) == 0);
-
-#if ((ATTR_DIR_LINKCOUNT | ATTR_DIR_ENTRYCOUNT | ATTR_DIR_MOUNTSTATUS) != ATTR_DIR_VALIDMASK)
-#error AttributeBlockSize: Missing bits in directory mask computation!
-#endif
- DBG_ASSERT((attrlist->dirattr & ~ATTR_DIR_VALIDMASK) == 0);
-#if ((ATTR_FILE_LINKCOUNT | ATTR_FILE_TOTALSIZE | ATTR_FILE_ALLOCSIZE | ATTR_FILE_IOBLOCKSIZE | \
- ATTR_FILE_CLUMPSIZE | ATTR_FILE_DEVTYPE | ATTR_FILE_FILETYPE | ATTR_FILE_FORKCOUNT | \
- ATTR_FILE_FORKLIST | ATTR_FILE_DATALENGTH | ATTR_FILE_DATAALLOCSIZE | ATTR_FILE_DATAEXTENTS | \
- ATTR_FILE_RSRCLENGTH | ATTR_FILE_RSRCALLOCSIZE | ATTR_FILE_RSRCEXTENTS) != ATTR_FILE_VALIDMASK)
-#error AttributeBlockSize: Missing bits in file mask computation!
-#endif
- DBG_ASSERT((attrlist->fileattr & ~ATTR_FILE_VALIDMASK) == 0);
+ DBG_ASSERT(vp != NULL);
-#if ((ATTR_FORK_TOTALSIZE | ATTR_FORK_ALLOCSIZE) != ATTR_FORK_VALIDMASK)
-#error AttributeBlockSize: Missing bits in fork mask computation!
-#endif
- DBG_ASSERT((attrlist->forkattr & ~ATTR_FORK_VALIDMASK) == 0);
+ /* start with default */
+ logBlockSize = VTOHFS(vp)->hfs_logBlockSize;
- size = 0;
+ if (vnode_issystem(vp)) {
+ if (VTOF(vp)->fcbBTCBPtr != NULL) {
+ BTreeInfoRec bTreeInfo;
- if ((a = attrlist->commonattr) != 0) {
- if (a & ATTR_CMN_NAME) size += sizeof(struct attrreference);
- if (a & ATTR_CMN_DEVID) size += sizeof(dev_t);
- if (a & ATTR_CMN_FSID) size += sizeof(fsid_t);
- if (a & ATTR_CMN_OBJTYPE) size += sizeof(fsobj_type_t);
- if (a & ATTR_CMN_OBJTAG) size += sizeof(fsobj_tag_t);
- if (a & ATTR_CMN_OBJID) size += sizeof(fsobj_id_t);
- if (a & ATTR_CMN_OBJPERMANENTID) size += sizeof(fsobj_id_t);
- if (a & ATTR_CMN_PAROBJID) size += sizeof(fsobj_id_t);
- if (a & ATTR_CMN_SCRIPT) size += sizeof(text_encoding_t);
- if (a & ATTR_CMN_CRTIME) size += sizeof(struct timespec);
- if (a & ATTR_CMN_MODTIME) size += sizeof(struct timespec);
- if (a & ATTR_CMN_CHGTIME) size += sizeof(struct timespec);
- if (a & ATTR_CMN_ACCTIME) size += sizeof(struct timespec);
- if (a & ATTR_CMN_BKUPTIME) size += sizeof(struct timespec);
- if (a & ATTR_CMN_FNDRINFO) size += 32 * sizeof(UInt8);
- if (a & ATTR_CMN_OWNERID) size += sizeof(uid_t);
- if (a & ATTR_CMN_GRPID) size += sizeof(gid_t);
- if (a & ATTR_CMN_ACCESSMASK) size += sizeof(u_long);
- if (a & ATTR_CMN_NAMEDATTRCOUNT) size += sizeof(u_long);
- if (a & ATTR_CMN_NAMEDATTRLIST) size += sizeof(struct attrreference);
- if (a & ATTR_CMN_FLAGS) size += sizeof(u_long);
- if (a & ATTR_CMN_USERACCESS) size += sizeof(u_long);
- };
- if ((a = attrlist->volattr) != 0) {
- if (a & ATTR_VOL_FSTYPE) size += sizeof(u_long);
- if (a & ATTR_VOL_SIGNATURE) size += sizeof(u_long);
- if (a & ATTR_VOL_SIZE) size += sizeof(off_t);
- if (a & ATTR_VOL_SPACEFREE) size += sizeof(off_t);
- if (a & ATTR_VOL_SPACEAVAIL) size += sizeof(off_t);
- if (a & ATTR_VOL_MINALLOCATION) size += sizeof(off_t);
- if (a & ATTR_VOL_ALLOCATIONCLUMP) size += sizeof(off_t);
- if (a & ATTR_VOL_IOBLOCKSIZE) size += sizeof(u_long);
- if (a & ATTR_VOL_OBJCOUNT) size += sizeof(u_long);
- if (a & ATTR_VOL_FILECOUNT) size += sizeof(u_long);
- if (a & ATTR_VOL_DIRCOUNT) size += sizeof(u_long);
- if (a & ATTR_VOL_MAXOBJCOUNT) size += sizeof(u_long);
- if (a & ATTR_VOL_MOUNTPOINT) size += sizeof(struct attrreference);
- if (a & ATTR_VOL_NAME) size += sizeof(struct attrreference);
- if (a & ATTR_VOL_MOUNTFLAGS) size += sizeof(u_long);
- if (a & ATTR_VOL_MOUNTEDDEVICE) size += sizeof(struct attrreference);
- if (a & ATTR_VOL_ENCODINGSUSED) size += sizeof(unsigned long long);
- if (a & ATTR_VOL_CAPABILITIES) size += sizeof(vol_capabilities_attr_t);
- if (a & ATTR_VOL_ATTRIBUTES) size += sizeof(vol_attributes_attr_t);
- };
- if ((a = attrlist->dirattr) != 0) {
- if (a & ATTR_DIR_LINKCOUNT) size += sizeof(u_long);
- if (a & ATTR_DIR_ENTRYCOUNT) size += sizeof(u_long);
- if (a & ATTR_DIR_MOUNTSTATUS) size += sizeof(u_long);
- };
- if ((a = attrlist->fileattr) != 0) {
- if (a & ATTR_FILE_LINKCOUNT) size += sizeof(u_long);
- if (a & ATTR_FILE_TOTALSIZE) size += sizeof(off_t);
- if (a & ATTR_FILE_ALLOCSIZE) size += sizeof(off_t);
- if (a & ATTR_FILE_IOBLOCKSIZE) size += sizeof(size_t);
- if (a & ATTR_FILE_CLUMPSIZE) size += sizeof(off_t);
- if (a & ATTR_FILE_DEVTYPE) size += sizeof(u_long);
- if (a & ATTR_FILE_FILETYPE) size += sizeof(u_long);
- if (a & ATTR_FILE_FORKCOUNT) size += sizeof(u_long);
- if (a & ATTR_FILE_FORKLIST) size += sizeof(struct attrreference);
- if (a & ATTR_FILE_DATALENGTH) size += sizeof(off_t);
- if (a & ATTR_FILE_DATAALLOCSIZE) size += sizeof(off_t);
- if (a & ATTR_FILE_DATAEXTENTS) size += sizeof(extentrecord);
- if (a & ATTR_FILE_RSRCLENGTH) size += sizeof(off_t);
- if (a & ATTR_FILE_RSRCALLOCSIZE) size += sizeof(off_t);
- if (a & ATTR_FILE_RSRCEXTENTS) size += sizeof(extentrecord);
- };
- if ((a = attrlist->forkattr) != 0) {
- if (a & ATTR_FORK_TOTALSIZE) size += sizeof(off_t);
- if (a & ATTR_FORK_ALLOCSIZE) size += sizeof(off_t);
- };
-
- return size;
-}
-
+ /*
+ * We do not lock the BTrees, because if we are getting block..then the tree
+ * should be locked in the first place.
+ * We just want the nodeSize wich will NEVER change..so even if the world
+ * is changing..the nodeSize should remain the same. Which argues why lock
+ * it in the first place??
+ */
+
+ (void) BTGetInformation (VTOF(vp), kBTreeInfoVersion, &bTreeInfo);
+
+ logBlockSize = bTreeInfo.nodeSize;
+ } else if (VTOC(vp)->c_fileid == kHFSAllocationFileID) {
+ logBlockSize = VTOVCB(vp)->vcbVBMIOSize;
+ }
+ }
-char* FindMountpointName(struct mount *mp) {
- size_t namelength = strlen(mp->mnt_stat.f_mntonname);
- int foundchars = 0;
- char *c;
-
- if (namelength == 0) return NULL;
-
- /* Look backwards through the name string, looking for the first slash
- encountered (which must precede the last part of the pathname)
- */
- for (c = mp->mnt_stat.f_mntonname + namelength - 1; namelength > 0; --c, --namelength) {
- if (*c != '/') {
- foundchars = 1;
- } else if (foundchars) {
- return (c + 1);
- };
- };
+ DBG_ASSERT(logBlockSize > 0);
- return mp->mnt_stat.f_mntonname;
+ return logBlockSize;
}
+u_int32_t
+hfs_freeblks(struct hfsmount * hfsmp, int wantreserve)
+{
+ u_int32_t freeblks;
+ u_int32_t rsrvblks;
+ u_int32_t loanblks;
-
-void PackObjectName(struct vnode *vp,
- char *name,
- size_t namelen,
- void **attrbufptrptr,
- void **varbufptrptr) {
- char *mpname;
- size_t mpnamelen;
- u_long attrlength;
-
- /* The name of an object may be incorrect for the root of a mounted filesystem
- because it may be mounted on a different directory name than the name of the
- volume (such as "blah-1". For the root directory, it's best to return the
- last element of the location where the volume's mounted:
+ /*
+ * We don't bother taking the mount lock
+ * to look at these values since the values
+ * themselves are each updated atomically
+ * on aligned addresses.
*/
- if ((vp->v_flag & VROOT) && (mpname = FindMountpointName(vp->v_mount))) {
- mpnamelen = strlen(mpname);
-
- /* Trim off any trailing slashes: */
- while ((mpnamelen > 0) && (mpname[mpnamelen-1] == '/')) {
- --mpnamelen;
- };
-
- /* If there's anything left, use it instead of the volume's name */
- if (mpnamelen > 0) {
- name = mpname;
- namelen = mpnamelen;
- };
- };
-
- attrlength = namelen + 1;
- ((struct attrreference *)(*attrbufptrptr))->attr_dataoffset = (char *)(*varbufptrptr) - (char *)(*attrbufptrptr);
- ((struct attrreference *)(*attrbufptrptr))->attr_length = attrlength;
- (void) strncpy((unsigned char *)(*varbufptrptr), name, attrlength);
-
- /* Advance beyond the space just allocated and round up to the next 4-byte boundary: */
- (char *)(*varbufptrptr) += attrlength + ((4 - (attrlength & 3)) & 3);
- ++((struct attrreference *)(*attrbufptrptr));
-}
-
-
+ freeblks = hfsmp->freeBlocks;
+ rsrvblks = hfsmp->reserveBlocks;
+ loanblks = hfsmp->loanedBlocks;
+ if (wantreserve) {
+ if (freeblks > rsrvblks)
+ freeblks -= rsrvblks;
+ else
+ freeblks = 0;
+ }
+ if (freeblks > loanblks)
+ freeblks -= loanblks;
+ else
+ freeblks = 0;
-void PackVolCommonAttributes(struct attrlist *alist,
- struct vnode *root_vp,
- struct hfsCatalogInfo *root_catInfo,
- void **attrbufptrptr,
- void **varbufptrptr) {
- void *attrbufptr;
- void *varbufptr;
- attrgroup_t a;
- struct hfsnode *root_hp = VTOH(root_vp);
- struct mount *mp = VTOVFS(root_vp);
- struct hfsmount *hfsmp = VTOHFS(root_vp);
- ExtendedVCB *vcb = HFSTOVCB(hfsmp);
- u_long attrlength;
-
- attrbufptr = *attrbufptrptr;
- varbufptr = *varbufptrptr;
+#if HFS_SPARSE_DEV
+ /*
+ * When the underlying device is sparse, check the
+ * available space on the backing store volume.
+ */
+ if ((hfsmp->hfs_flags & HFS_HAS_SPARSE_DEVICE) && hfsmp->hfs_backingfs_rootvp) {
+ struct vfsstatfs *vfsp; /* 272 bytes */
+ u_int64_t vfreeblks;
+ u_int32_t loanedblks;
+ struct mount * backingfs_mp;
+ struct timeval now;
+
+ backingfs_mp = vnode_mount(hfsmp->hfs_backingfs_rootvp);
+
+ microtime(&now);
+ if ((now.tv_sec - hfsmp->hfs_last_backingstatfs) >= 1) {
+ vfs_update_vfsstat(backingfs_mp, vfs_context_kernel(), VFS_KERNEL_EVENT);
+ hfsmp->hfs_last_backingstatfs = now.tv_sec;
+ }
- if ((a = alist->commonattr) != 0) {
- if (a & ATTR_CMN_NAME) {
- PackObjectName(root_vp, H_NAME(root_hp), root_hp->h_meta->h_namelen, &attrbufptr, &varbufptr);
- };
- if (a & ATTR_CMN_DEVID) *((dev_t *)attrbufptr)++ = hfsmp->hfs_raw_dev;
- if (a & ATTR_CMN_FSID) {
- *((fsid_t *)attrbufptr) = mp->mnt_stat.f_fsid;
- ++((fsid_t *)attrbufptr);
- };
- if (a & ATTR_CMN_OBJTYPE) *((fsobj_type_t *)attrbufptr)++ = 0;
- if (a & ATTR_CMN_OBJTAG) *((fsobj_tag_t *)attrbufptr)++ = VT_HFS;
- if (a & ATTR_CMN_OBJID) {
- ((fsobj_id_t *)attrbufptr)->fid_objno = 0;
- ((fsobj_id_t *)attrbufptr)->fid_generation = 0;
- ++((fsobj_id_t *)attrbufptr);
- };
- if (a & ATTR_CMN_OBJPERMANENTID) {
- ((fsobj_id_t *)attrbufptr)->fid_objno = 0;
- ((fsobj_id_t *)attrbufptr)->fid_generation = 0;
- ++((fsobj_id_t *)attrbufptr);
- };
- if (a & ATTR_CMN_PAROBJID) {
- ((fsobj_id_t *)attrbufptr)->fid_objno = 0;
- ((fsobj_id_t *)attrbufptr)->fid_generation = 0;
- ++((fsobj_id_t *)attrbufptr);
- };
- VCB_LOCK(vcb);
- if (a & ATTR_CMN_SCRIPT) *((text_encoding_t *)attrbufptr)++ = vcb->volumeNameEncodingHint;
- /* NOTE: all VCB dates are in Mac OS time */
- if (a & ATTR_CMN_CRTIME) {
- ((struct timespec *)attrbufptr)->tv_sec = to_bsd_time(vcb->vcbCrDate);
- ((struct timespec *)attrbufptr)->tv_nsec = 0;
- ++((struct timespec *)attrbufptr);
- };
- if (a & ATTR_CMN_MODTIME) {
- ((struct timespec *)attrbufptr)->tv_sec = to_bsd_time(vcb->vcbLsMod);
- ((struct timespec *)attrbufptr)->tv_nsec = 0;
- ++((struct timespec *)attrbufptr);
- };
- if (a & ATTR_CMN_CHGTIME) {
- ((struct timespec *)attrbufptr)->tv_sec = to_bsd_time(vcb->vcbLsMod);
- ((struct timespec *)attrbufptr)->tv_nsec = 0;
- ++((struct timespec *)attrbufptr);
- };
- if (a & ATTR_CMN_ACCTIME) {
- ((struct timespec *)attrbufptr)->tv_sec = to_bsd_time(vcb->vcbLsMod);
- ((struct timespec *)attrbufptr)->tv_nsec = 0;
- ++((struct timespec *)attrbufptr);
- };
- if (a & ATTR_CMN_BKUPTIME) {
- ((struct timespec *)attrbufptr)->tv_sec = to_bsd_time(vcb->vcbVolBkUp);
- ((struct timespec *)attrbufptr)->tv_nsec = 0;
- ++((struct timespec *)attrbufptr);
- };
- if (a & ATTR_CMN_FNDRINFO) {
- bcopy (&vcb->vcbFndrInfo, attrbufptr, sizeof(vcb->vcbFndrInfo));
- (char *)attrbufptr += sizeof(vcb->vcbFndrInfo);
- };
- VCB_UNLOCK(vcb);
- if (a & ATTR_CMN_OWNERID) {
- if (mp->mnt_flag & MNT_UNKNOWNPERMISSIONS) {
- *((uid_t *)attrbufptr)++ =
- (VTOHFS(root_vp)->hfs_uid == UNKNOWNUID) ? console_user : VTOHFS(root_vp)->hfs_uid;
- } else {
- *((uid_t *)attrbufptr)++ =
- (root_hp->h_meta->h_uid == UNKNOWNUID) ? console_user : root_hp->h_meta->h_uid;
- };
- };
- if (a & ATTR_CMN_GRPID) {
- if (mp->mnt_flag & MNT_UNKNOWNPERMISSIONS) {
- *((gid_t *)attrbufptr)++ = VTOHFS(root_vp)->hfs_gid;
- } else {
- *((gid_t *)attrbufptr)++ = root_hp->h_meta->h_gid;
- };
- };
- if (a & ATTR_CMN_ACCESSMASK) *((u_long *)attrbufptr)++ = (u_long)root_hp->h_meta->h_mode;
- if (a & ATTR_CMN_NAMEDATTRCOUNT) *((u_long *)attrbufptr)++ = 0; /* XXX PPD TBC */
- if (a & ATTR_CMN_NAMEDATTRLIST) {
- attrlength = 0;
- ((struct attrreference *)attrbufptr)->attr_dataoffset = 0;
- ((struct attrreference *)attrbufptr)->attr_length = attrlength;
+ if ((vfsp = vfs_statfs(backingfs_mp))) {
+ HFS_MOUNT_LOCK(hfsmp, TRUE);
+ vfreeblks = vfsp->f_bavail;
+ /* Normalize block count if needed. */
+ if (vfsp->f_bsize != hfsmp->blockSize) {
+ vfreeblks = ((u_int64_t)vfreeblks * (u_int64_t)(vfsp->f_bsize)) / hfsmp->blockSize;
+ }
+ if (vfreeblks > (unsigned int)hfsmp->hfs_sparsebandblks)
+ vfreeblks -= hfsmp->hfs_sparsebandblks;
+ else
+ vfreeblks = 0;
- /* Advance beyond the space just allocated and round up to the next 4-byte boundary: */
- (char *)varbufptr += attrlength + ((4 - (attrlength & 3)) & 3);
- ++((struct attrreference *)attrbufptr);
- };
- if (a & ATTR_CMN_FLAGS) *((u_long *)attrbufptr)++ = root_hp->h_meta->h_pflags;
- if (a & ATTR_CMN_USERACCESS) {
- if (mp->mnt_flag & MNT_UNKNOWNPERMISSIONS) {
- *((u_long *)attrbufptr)++ =
- DerivePermissionSummary((VTOHFS(root_vp)->hfs_uid == UNKNOWNUID) ? console_user : VTOHFS(root_vp)->hfs_uid,
- VTOHFS(root_vp)->hfs_gid,
- root_hp->h_meta->h_mode,
- VTOVFS(root_vp),
- current_proc()->p_ucred,
- current_proc());
- } else {
- *((u_long *)attrbufptr)++ =
- DerivePermissionSummary((root_hp->h_meta->h_uid == UNKNOWNUID) ? console_user : root_hp->h_meta->h_uid,
- root_hp->h_meta->h_gid,
- root_hp->h_meta->h_mode,
- VTOVFS(root_vp),
- current_proc()->p_ucred,
- current_proc());
- };
- };
- };
-
- *attrbufptrptr = attrbufptr;
- *varbufptrptr = varbufptr;
-}
-
+ /* Take into account any delayed allocations. */
+ loanedblks = 2 * hfsmp->loanedBlocks;
+ if (vfreeblks > loanedblks)
+ vfreeblks -= loanedblks;
+ else
+ vfreeblks = 0;
+ if (hfsmp->hfs_backingfs_maxblocks) {
+ vfreeblks = MIN(vfreeblks, hfsmp->hfs_backingfs_maxblocks);
+ }
+ freeblks = MIN(vfreeblks, freeblks);
+ HFS_MOUNT_UNLOCK(hfsmp, TRUE);
+ }
+ }
+#endif /* HFS_SPARSE_DEV */
-void PackVolAttributeBlock(struct attrlist *alist,
- struct vnode *root_vp,
- struct hfsCatalogInfo *root_catInfo,
- void **attrbufptrptr,
- void **varbufptrptr) {
- void *attrbufptr;
- void *varbufptr;
- attrgroup_t a;
- struct mount *mp = VTOVFS(root_vp);
- struct hfsmount *hfsmp = VTOHFS(root_vp);
- ExtendedVCB *vcb = HFSTOVCB(hfsmp);
- u_long attrlength;
-
- attrbufptr = *attrbufptrptr;
- varbufptr = *varbufptrptr;
-
- if ((a = alist->volattr) != 0) {
- VCB_LOCK(vcb);
- if (a & ATTR_VOL_FSTYPE) *((u_long *)attrbufptr)++ = (u_long)mp->mnt_vfc->vfc_typenum;
- if (a & ATTR_VOL_SIGNATURE) *((u_long *)attrbufptr)++ = (u_long)vcb->vcbSigWord;
- if (a & ATTR_VOL_SIZE) *((off_t *)attrbufptr)++ = (off_t)vcb->totalBlocks * (off_t)vcb->blockSize;
- if (a & ATTR_VOL_SPACEFREE) *((off_t *)attrbufptr)++ = (off_t)vcb->freeBlocks * (off_t)vcb->blockSize;
- if (a & ATTR_VOL_SPACEAVAIL) *((off_t *)attrbufptr)++ = (off_t)vcb->freeBlocks * (off_t)vcb->blockSize;
- if (a & ATTR_VOL_MINALLOCATION) *((off_t *)attrbufptr)++ = (off_t)vcb->blockSize;
- if (a & ATTR_VOL_ALLOCATIONCLUMP) *((off_t *)attrbufptr)++ = (off_t)(vcb->vcbClpSiz);
- if (a & ATTR_VOL_IOBLOCKSIZE) *((u_long *)attrbufptr)++ = (u_long)hfsmp->hfs_logBlockSize;
- if (a & ATTR_VOL_OBJCOUNT) *((u_long *)attrbufptr)++ = (u_long)vcb->vcbFilCnt + (u_long)vcb->vcbDirCnt;
- if (a & ATTR_VOL_FILECOUNT) *((u_long *)attrbufptr)++ = (u_long)vcb->vcbFilCnt;
- if (a & ATTR_VOL_DIRCOUNT) *((u_long *)attrbufptr)++ = (u_long)vcb->vcbDirCnt;
- if (a & ATTR_VOL_MAXOBJCOUNT) *((u_long *)attrbufptr)++ = 0xFFFFFFFF;
- if (a & ATTR_VOL_MOUNTPOINT) {
- ((struct attrreference *)attrbufptr)->attr_dataoffset = (char *)varbufptr - (char *)attrbufptr;
- ((struct attrreference *)attrbufptr)->attr_length = strlen(mp->mnt_stat.f_mntonname) + 1;
- attrlength = ((struct attrreference *)attrbufptr)->attr_length;
- attrlength = attrlength + ((4 - (attrlength & 3)) & 3); /* round up to the next 4-byte boundary: */
- (void) bcopy(mp->mnt_stat.f_mntonname, varbufptr, attrlength);
-
- /* Advance beyond the space just allocated: */
- (char *)varbufptr += attrlength;
- ++((struct attrreference *)attrbufptr);
- };
- if (a & ATTR_VOL_NAME) {
- ((struct attrreference *)attrbufptr)->attr_dataoffset = (char *)varbufptr - (char *)attrbufptr;
- ((struct attrreference *)attrbufptr)->attr_length = VTOH(root_vp)->h_meta->h_namelen + 1;
- attrlength = ((struct attrreference *)attrbufptr)->attr_length;
- attrlength = attrlength + ((4 - (attrlength & 3)) & 3); /* round up to the next 4-byte boundary: */
- bcopy(H_NAME(VTOH(root_vp)), varbufptr, attrlength);
-
- /* Advance beyond the space just allocated: */
- (char *)varbufptr += attrlength;
- ++((struct attrreference *)attrbufptr);
- };
- if (a & ATTR_VOL_MOUNTFLAGS) *((u_long *)attrbufptr)++ = (u_long)mp->mnt_flag;
- if (a & ATTR_VOL_MOUNTEDDEVICE) {
- ((struct attrreference *)attrbufptr)->attr_dataoffset = (char *)varbufptr - (char *)attrbufptr;
- ((struct attrreference *)attrbufptr)->attr_length = strlen(mp->mnt_stat.f_mntfromname) + 1;
- attrlength = ((struct attrreference *)attrbufptr)->attr_length;
- attrlength = attrlength + ((4 - (attrlength & 3)) & 3); /* round up to the next 4-byte boundary: */
- (void) bcopy(mp->mnt_stat.f_mntfromname, varbufptr, attrlength);
-
- /* Advance beyond the space just allocated: */
- (char *)varbufptr += attrlength;
- ++((struct attrreference *)attrbufptr);
- };
- if (a & ATTR_VOL_ENCODINGSUSED) *((unsigned long long *)attrbufptr)++ = (unsigned long long)vcb->encodingsBitmap;
- if (a & ATTR_VOL_CAPABILITIES) {
- if (vcb->vcbSigWord == kHFSPlusSigWord) {
- ((vol_capabilities_attr_t *)attrbufptr)->capabilities[VOL_CAPABILITIES_FORMAT] =
- VOL_CAP_FMT_PERSISTENTOBJECTIDS | VOL_CAP_FMT_SYMBOLICLINKS | VOL_CAP_FMT_HARDLINKS;
- } else { /* Plain HFS */
- ((vol_capabilities_attr_t *)attrbufptr)->capabilities[VOL_CAPABILITIES_FORMAT] =
- VOL_CAP_FMT_PERSISTENTOBJECTIDS;
- }
- ((vol_capabilities_attr_t *)attrbufptr)->capabilities[VOL_CAPABILITIES_INTERFACES] =
- VOL_CAP_INT_SEARCHFS | VOL_CAP_INT_ATTRLIST | VOL_CAP_INT_NFSEXPORT | VOL_CAP_INT_READDIRATTR;
- ((vol_capabilities_attr_t *)attrbufptr)->capabilities[VOL_CAPABILITIES_RESERVED1] = 0;
- ((vol_capabilities_attr_t *)attrbufptr)->capabilities[VOL_CAPABILITIES_RESERVED2] = 0;
-
- ((vol_capabilities_attr_t *)attrbufptr)->valid[VOL_CAPABILITIES_FORMAT] =
- VOL_CAP_FMT_PERSISTENTOBJECTIDS | VOL_CAP_FMT_SYMBOLICLINKS | VOL_CAP_FMT_HARDLINKS;
- ((vol_capabilities_attr_t *)attrbufptr)->valid[VOL_CAPABILITIES_INTERFACES] =
- VOL_CAP_INT_SEARCHFS | VOL_CAP_INT_ATTRLIST | VOL_CAP_INT_NFSEXPORT | VOL_CAP_INT_READDIRATTR;
- ((vol_capabilities_attr_t *)attrbufptr)->valid[VOL_CAPABILITIES_RESERVED1] = 0;
- ((vol_capabilities_attr_t *)attrbufptr)->valid[VOL_CAPABILITIES_RESERVED2] = 0;
-
- ++((vol_capabilities_attr_t *)attrbufptr);
- };
- if (a & ATTR_VOL_ATTRIBUTES) {
- ((vol_attributes_attr_t *)attrbufptr)->validattr.commonattr = ATTR_CMN_VALIDMASK;
- ((vol_attributes_attr_t *)attrbufptr)->validattr.volattr = ATTR_VOL_VALIDMASK;
- ((vol_attributes_attr_t *)attrbufptr)->validattr.dirattr = ATTR_DIR_VALIDMASK;
- ((vol_attributes_attr_t *)attrbufptr)->validattr.fileattr = ATTR_FILE_VALIDMASK;
- ((vol_attributes_attr_t *)attrbufptr)->validattr.forkattr = ATTR_FORK_VALIDMASK;
-
- ((vol_attributes_attr_t *)attrbufptr)->nativeattr.commonattr = ATTR_CMN_VALIDMASK;
- ((vol_attributes_attr_t *)attrbufptr)->nativeattr.volattr = ATTR_VOL_VALIDMASK;
- ((vol_attributes_attr_t *)attrbufptr)->nativeattr.dirattr = ATTR_DIR_VALIDMASK;
- ((vol_attributes_attr_t *)attrbufptr)->nativeattr.fileattr = ATTR_FILE_VALIDMASK;
- ((vol_attributes_attr_t *)attrbufptr)->nativeattr.forkattr = ATTR_FORK_VALIDMASK;
-
- ++((vol_attributes_attr_t *)attrbufptr);
- };
- VCB_UNLOCK(vcb);
- };
-
- *attrbufptrptr = attrbufptr;
- *varbufptrptr = varbufptr;
+ return (freeblks);
}
-
-
-
-void PackVolumeInfo(struct attrlist *alist,
- struct vnode *root_vp,
- struct hfsCatalogInfo *root_catinfo,
- void **attrbufptrptr,
- void **varbufptrptr) {
-
- PackVolCommonAttributes(alist, root_vp, root_catinfo, attrbufptrptr, varbufptrptr);
- PackVolAttributeBlock(alist, root_vp, root_catinfo, attrbufptrptr, varbufptrptr);
-};
-
-// Pack the common attribute contents of an objects hfsCatalogInfo
-void PackCommonCatalogInfoAttributeBlock(struct attrlist *alist,
- struct vnode *root_vp,
- struct hfsCatalogInfo *catalogInfo,
- void **attrbufptrptr,
- void **varbufptrptr )
+/*
+ * Map HFS Common errors (negative) to BSD error codes (positive).
+ * Positive errors (ie BSD errors) are passed through unchanged.
+ */
+short MacToVFSError(OSErr err)
{
- struct hfsnode *hp;
- void *attrbufptr;
- void *varbufptr;
- attrgroup_t a;
- u_long attrlength;
- Boolean isHFSPlus;
-
- hp = VTOH(root_vp);
- attrbufptr = *attrbufptrptr;
- varbufptr = *varbufptrptr;
- isHFSPlus = (VTOVCB(root_vp)->vcbSigWord == kHFSPlusSigWord);
-
- if ((a = alist->commonattr) != 0)
- {
- if (a & ATTR_CMN_NAME)
- {
- attrlength = strlen(catalogInfo->nodeData.cnm_nameptr) + 1;
- ((struct attrreference *)attrbufptr)->attr_dataoffset = (char *)varbufptr - (char *)attrbufptr;
- ((struct attrreference *)attrbufptr)->attr_length = attrlength;
- (void) strncpy((unsigned char *)varbufptr,
- catalogInfo->nodeData.cnm_nameptr, attrlength);
-
- /* Advance beyond the space just allocated and round up to the next 4-byte boundary: */
- (char *)varbufptr += attrlength + ((4 - (attrlength & 3)) & 3);
- ++((struct attrreference *)attrbufptr);
- };
- if (a & ATTR_CMN_DEVID) *((dev_t *)attrbufptr)++ = H_DEV(hp);
- if (a & ATTR_CMN_FSID) {
- *((fsid_t *)attrbufptr) = VTOVFS(root_vp)->mnt_stat.f_fsid;
- ++((fsid_t *)attrbufptr);
- };
- if (a & ATTR_CMN_OBJTYPE)
- {
- switch (catalogInfo->nodeData.cnd_type) {
- case kCatalogFolderNode:
- *((fsobj_type_t *)attrbufptr)++ = VDIR;
- break;
-
- case kCatalogFileNode:
- /* Files in an HFS+ catalog can represent many things (regular files, symlinks, block/character devices, ...) */
- if ((HTOVCB(hp)->vcbSigWord == kHFSPlusSigWord) &&
- (catalogInfo->nodeData.cnd_mode & IFMT)) {
- *((fsobj_type_t *)attrbufptr)++ =
- IFTOVT((mode_t)catalogInfo->nodeData.cnd_mode);
- } else {
- *((fsobj_type_t *)attrbufptr)++ = VREG;
- };
- break;
-
- default:
- *((fsobj_type_t *)attrbufptr)++ = VNON;
- break;
- };
- }
- if (a & ATTR_CMN_OBJTAG) *((fsobj_tag_t *)attrbufptr)++ = root_vp->v_tag;
- if (a & ATTR_CMN_OBJID) {
- u_int32_t cnid;
-
- /* For hard links use the link's cnid */
- if (catalogInfo->nodeData.cnd_iNodeNumCopy != 0)
- cnid = catalogInfo->nodeData.cnd_linkCNID;
- else
- cnid = catalogInfo->nodeData.cnd_nodeID;
- ((fsobj_id_t *)attrbufptr)->fid_objno = cnid;
- ((fsobj_id_t *)attrbufptr)->fid_generation = 0;
- ++((fsobj_id_t *)attrbufptr);
- };
- if (a & ATTR_CMN_OBJPERMANENTID) {
- u_int32_t cnid;
-
- /* For hard links use the link's cnid */
- if (catalogInfo->nodeData.cnd_iNodeNumCopy != 0)
- cnid = catalogInfo->nodeData.cnd_linkCNID;
- else
- cnid = catalogInfo->nodeData.cnd_nodeID;
- ((fsobj_id_t *)attrbufptr)->fid_objno = cnid;
- ((fsobj_id_t *)attrbufptr)->fid_generation = 0;
- ++((fsobj_id_t *)attrbufptr);
- };
- if (a & ATTR_CMN_PAROBJID)
- {
- ((fsobj_id_t *)attrbufptr)->fid_objno = catalogInfo->nodeData.cnm_parID;
- ((fsobj_id_t *)attrbufptr)->fid_generation = 0;
- ++((fsobj_id_t *)attrbufptr);
- };
- if (a & ATTR_CMN_SCRIPT)
- {
- if (HTOVCB(hp)->vcbSigWord == kHFSPlusSigWord) {
- *((text_encoding_t *)attrbufptr)++ = catalogInfo->nodeData.cnd_textEncoding;
- } else {
- *((text_encoding_t *)attrbufptr)++ = VTOHFS(root_vp)->hfs_encoding;
- }
- };
- if (a & ATTR_CMN_CRTIME)
- {
- ((struct timespec *)attrbufptr)->tv_sec = to_bsd_time(catalogInfo->nodeData.cnd_createDate);
- ((struct timespec *)attrbufptr)->tv_nsec = 0;
- ++((struct timespec *)attrbufptr);
- };
- if (a & ATTR_CMN_MODTIME)
- {
- ((struct timespec *)attrbufptr)->tv_sec = to_bsd_time(catalogInfo->nodeData.cnd_contentModDate);
- ((struct timespec *)attrbufptr)->tv_nsec = 0;
- ++((struct timespec *)attrbufptr);
- };
- if (a & ATTR_CMN_CHGTIME)
- {
- ((struct timespec *)attrbufptr)->tv_sec = to_bsd_time(catalogInfo->nodeData.cnd_attributeModDate);
- ((struct timespec *)attrbufptr)->tv_nsec = 0;
- ++((struct timespec *)attrbufptr);
- };
- if (a & ATTR_CMN_ACCTIME)
- {
- ((struct timespec *)attrbufptr)->tv_sec = to_bsd_time(catalogInfo->nodeData.cnd_accessDate);
- ((struct timespec *)attrbufptr)->tv_nsec = 0;
- ++((struct timespec *)attrbufptr);
- };
- if (a & ATTR_CMN_BKUPTIME)
- {
- ((struct timespec *)attrbufptr)->tv_sec = to_bsd_time(catalogInfo->nodeData.cnd_backupDate);
- ((struct timespec *)attrbufptr)->tv_nsec = 0;
- ++((struct timespec *)attrbufptr);
- };
- if (a & ATTR_CMN_FNDRINFO)
- {
- bcopy (&catalogInfo->nodeData.cnd_finderInfo, attrbufptr, sizeof(catalogInfo->nodeData.cnd_finderInfo));
- (char *)attrbufptr += sizeof(catalogInfo->nodeData.cnd_finderInfo);
- };
- if (a & ATTR_CMN_OWNERID) {
- if ((VTOVFS(root_vp)->mnt_flag & MNT_UNKNOWNPERMISSIONS) ||
- ((catalogInfo->nodeData.cnd_mode & IFMT) == 0)) {
- *((uid_t *)attrbufptr)++ =
- (VTOHFS(root_vp)->hfs_uid == UNKNOWNUID) ? console_user : VTOHFS(root_vp)->hfs_uid;
- } else {
- *((uid_t *)attrbufptr)++ =
- (catalogInfo->nodeData.cnd_ownerID == UNKNOWNUID) ? console_user : catalogInfo->nodeData.cnd_ownerID;
- };
- }
- if (a & ATTR_CMN_GRPID) {
- if ((VTOVFS(root_vp)->mnt_flag & MNT_UNKNOWNPERMISSIONS) ||
- ((catalogInfo->nodeData.cnd_mode & IFMT) == 0)) {
- *((gid_t *)attrbufptr)++ = VTOHFS(root_vp)->hfs_gid;
- } else {
- *((gid_t *)attrbufptr)++ = catalogInfo->nodeData.cnd_groupID;
- };
- }
- if (a & ATTR_CMN_ACCESSMASK) {
- if (((catalogInfo->nodeData.cnd_mode & IFMT) == 0)
-#if OVERRIDE_UNKNOWN_PERMISSIONS
- || (VTOVFS(root_vp)->mnt_flag & MNT_UNKNOWNPERMISSIONS)
-#endif
- ) {
- switch (catalogInfo->nodeData.cnd_type) {
- case kCatalogFileNode:
- /* Files in an HFS+ catalog can represent many things (regular files, symlinks, block/character devices, ...) */
- *((u_long *)attrbufptr)++ = (u_long)(IFREG | (ACCESSPERMS & (u_long)(VTOHFS(root_vp)->hfs_file_mask)));
- break;
-
- case kCatalogFolderNode:
- *((u_long *)attrbufptr)++ = (u_long)(IFDIR | (ACCESSPERMS & (u_long)(VTOHFS(root_vp)->hfs_dir_mask)));
- break;
-
- default:
- *((u_long *)attrbufptr)++ = (u_long)((catalogInfo->nodeData.cnd_mode & IFMT) |
- VTOHFS(root_vp)->hfs_dir_mask);
- };
- } else {
- *((u_long *)attrbufptr)++ =
- (u_long)catalogInfo->nodeData.cnd_mode;
- };
- }
- if (a & ATTR_CMN_NAMEDATTRCOUNT) *((u_long *)attrbufptr)++ = 0; /* XXX PPD TBC */
- if (a & ATTR_CMN_NAMEDATTRLIST)
- {
- attrlength = 0;
- ((struct attrreference *)attrbufptr)->attr_dataoffset = 0;
- ((struct attrreference *)attrbufptr)->attr_length = attrlength;
-
- /* Advance beyond the space just allocated and round up to the next 4-byte boundary: */
- (char *)varbufptr += attrlength + ((4 - (attrlength & 3)) & 3);
- ++((struct attrreference *)attrbufptr);
- };
- if (a & ATTR_CMN_FLAGS) {
- u_long flags;
-
- if (catalogInfo->nodeData.cnd_mode & IFMT)
- flags = catalogInfo->nodeData.cnd_ownerFlags |
- catalogInfo->nodeData.cnd_adminFlags << 16;
- else
- flags = 0;
-
- if (catalogInfo->nodeData.cnd_type == kCatalogFileNode) {
- if (catalogInfo->nodeData.cnd_flags & kHFSFileLockedMask)
- flags |= UF_IMMUTABLE;
- else
- flags &= ~UF_IMMUTABLE;
- };
- *((u_long *)attrbufptr)++ = flags;
- };
- if (a & ATTR_CMN_USERACCESS) {
- if ((VTOVFS(root_vp)->mnt_flag & MNT_UNKNOWNPERMISSIONS) ||
- ((catalogInfo->nodeData.cnd_mode & IFMT) == 0)) {
- *((u_long *)attrbufptr)++ =
- DerivePermissionSummary((VTOHFS(root_vp)->hfs_uid == UNKNOWNUID) ? console_user : VTOHFS(root_vp)->hfs_uid,
- VTOHFS(root_vp)->hfs_gid,
-#if OVERRIDE_UNKNOWN_PERMISSIONS
- (catalogInfo->nodeData.cnd_type == kCatalogFileNode) ? VTOHFS(root_vp)->hfs_file_mask : VTOHFS(root_vp)->hfs_dir_mask,
-#else
- (catalogInfo->nodeData.cnd_mode & IFMT) ?
- (u_long)catalogInfo->nodeData.cnd_mode :
- ((catalogInfo->nodeData.cnd_type == kCatalogFileNode) ?
- VTOHFS(root_vp)->hfs_file_mask :
- VTOHFS(root_vp)->hfs_dir_mask),
-#endif
- VTOVFS(root_vp),
- current_proc()->p_ucred,
- current_proc());
- } else {
- *((u_long *)attrbufptr)++ =
- DerivePermissionSummary((catalogInfo->nodeData.cnd_ownerID == UNKNOWNUID) ? console_user : catalogInfo->nodeData.cnd_ownerID,
- catalogInfo->nodeData.cnd_groupID,
- (mode_t)catalogInfo->nodeData.cnd_mode,
- VTOVFS(root_vp),
- current_proc()->p_ucred,
- current_proc());
- };
- };
- };
-
- *attrbufptrptr = attrbufptr;
- *varbufptrptr = varbufptr;
-}
-
+ if (err >= 0)
+ return err;
-void PackCommonAttributeBlock(struct attrlist *alist,
- struct vnode *vp,
- struct hfsCatalogInfo *catInfo,
- void **attrbufptrptr,
- void **varbufptrptr) {
- struct hfsnode *hp;
- void *attrbufptr;
- void *varbufptr;
- attrgroup_t a;
- u_long attrlength;
+ switch (err) {
+ case dskFulErr: /* -34 */
+ case btNoSpaceAvail: /* -32733 */
+ return ENOSPC;
+ case fxOvFlErr: /* -32750 */
+ return EOVERFLOW;
- hp = VTOH(vp);
+ case btBadNode: /* -32731 */
+ return EIO;
- attrbufptr = *attrbufptrptr;
- varbufptr = *varbufptrptr;
+ case memFullErr: /* -108 */
+ return ENOMEM; /* +12 */
- if ((a = alist->commonattr) != 0) {
- if (a & ATTR_CMN_NAME) {
- PackObjectName(vp, H_NAME(hp), hp->h_meta->h_namelen, &attrbufptr, &varbufptr);
- };
- if (a & ATTR_CMN_DEVID) *((dev_t *)attrbufptr)++ = H_DEV(hp);
- if (a & ATTR_CMN_FSID) {
- *((fsid_t *)attrbufptr) = VTOVFS(vp)->mnt_stat.f_fsid;
- ++((fsid_t *)attrbufptr);
- };
- if (a & ATTR_CMN_OBJTYPE) *((fsobj_type_t *)attrbufptr)++ = vp->v_type;
- if (a & ATTR_CMN_OBJTAG) *((fsobj_tag_t *)attrbufptr)++ = vp->v_tag;
- if (a & ATTR_CMN_OBJID) {
- u_int32_t cnid;
-
- /* For hard links use the link's cnid */
- if (hp->h_meta->h_metaflags & IN_DATANODE)
- cnid = catInfo->nodeData.cnd_linkCNID;
- else
- cnid = H_FILEID(hp);
- ((fsobj_id_t *)attrbufptr)->fid_objno = cnid;
- ((fsobj_id_t *)attrbufptr)->fid_generation = 0;
- ++((fsobj_id_t *)attrbufptr);
- };
- if (a & ATTR_CMN_OBJPERMANENTID) {
- u_int32_t cnid;
-
- /* For hard links use the link's cnid */
- if (hp->h_meta->h_metaflags & IN_DATANODE)
- cnid = catInfo->nodeData.cnd_linkCNID;
- else
- cnid = H_FILEID(hp);
- ((fsobj_id_t *)attrbufptr)->fid_objno = cnid;
- ((fsobj_id_t *)attrbufptr)->fid_generation = 0;
- ++((fsobj_id_t *)attrbufptr);
- };
- if (a & ATTR_CMN_PAROBJID) {
- ((fsobj_id_t *)attrbufptr)->fid_objno = H_DIRID(hp);
- ((fsobj_id_t *)attrbufptr)->fid_generation = 0;
- ++((fsobj_id_t *)attrbufptr);
- };
- if (a & ATTR_CMN_SCRIPT)
- {
- if (HTOVCB(hp)->vcbSigWord == kHFSPlusSigWord) {
- *((text_encoding_t *)attrbufptr)++ = catInfo->nodeData.cnd_textEncoding;
- } else {
- *((text_encoding_t *)attrbufptr)++ = VTOHFS(vp)->hfs_encoding;
- }
- };
- if (a & ATTR_CMN_CRTIME) {
- ((struct timespec *)attrbufptr)->tv_sec = hp->h_meta->h_crtime;
- ((struct timespec *)attrbufptr)->tv_nsec = 0;
- ++((struct timespec *)attrbufptr);
- };
- if (a & ATTR_CMN_MODTIME) {
- ((struct timespec *)attrbufptr)->tv_sec = hp->h_meta->h_mtime;
- ((struct timespec *)attrbufptr)->tv_nsec = 0;
- ++((struct timespec *)attrbufptr);
- };
- if (a & ATTR_CMN_CHGTIME) {
- ((struct timespec *)attrbufptr)->tv_sec = hp->h_meta->h_ctime;
- ((struct timespec *)attrbufptr)->tv_nsec = 0;
- ++((struct timespec *)attrbufptr);
- };
- if (a & ATTR_CMN_ACCTIME) {
- ((struct timespec *)attrbufptr)->tv_sec = hp->h_meta->h_atime;
- ((struct timespec *)attrbufptr)->tv_nsec = 0;
- ++((struct timespec *)attrbufptr);
- };
- if (a & ATTR_CMN_BKUPTIME) {
- ((struct timespec *)attrbufptr)->tv_sec = hp->h_meta->h_butime;
- ((struct timespec *)attrbufptr)->tv_nsec = 0;
- ++((struct timespec *)attrbufptr);
- };
- if (a & ATTR_CMN_FNDRINFO) {
- bcopy (&catInfo->nodeData.cnd_finderInfo, attrbufptr, sizeof(catInfo->nodeData.cnd_finderInfo));
- (char *)attrbufptr += sizeof(catInfo->nodeData.cnd_finderInfo);
- };
- if (a & ATTR_CMN_OWNERID) {
- if (VTOVFS(vp)->mnt_flag & MNT_UNKNOWNPERMISSIONS) {
- *((uid_t *)attrbufptr)++ =
- (VTOHFS(vp)->hfs_uid == UNKNOWNUID) ? console_user : VTOHFS(vp)->hfs_uid;
- } else {
- *((uid_t *)attrbufptr)++ =
- (hp->h_meta->h_uid == UNKNOWNUID) ? console_user : hp->h_meta->h_uid;
- }
- };
- if (a & ATTR_CMN_GRPID) {
- if (VTOVFS(vp)->mnt_flag & MNT_UNKNOWNPERMISSIONS) {
- *((gid_t *)attrbufptr)++ = VTOHFS(vp)->hfs_gid;
- } else {
- *((gid_t *)attrbufptr)++ = hp->h_meta->h_gid;
- };
- };
- if (a & ATTR_CMN_ACCESSMASK) *((u_long *)attrbufptr)++ = (u_long)hp->h_meta->h_mode;
- if (a & ATTR_CMN_NAMEDATTRCOUNT) *((u_long *)attrbufptr)++ = 0; /* XXX PPD TBC */
- if (a & ATTR_CMN_NAMEDATTRLIST) {
- attrlength = 0;
- ((struct attrreference *)attrbufptr)->attr_dataoffset = 0;
- ((struct attrreference *)attrbufptr)->attr_length = attrlength;
-
- /* Advance beyond the space just allocated and round up to the next 4-byte boundary: */
- (char *)varbufptr += attrlength + ((4 - (attrlength & 3)) & 3);
- ++((struct attrreference *)attrbufptr);
- };
- if (a & ATTR_CMN_FLAGS) *((u_long *)attrbufptr)++ = hp->h_meta->h_pflags;
- if (a & ATTR_CMN_USERACCESS) {
- if (VTOVFS(vp)->mnt_flag & MNT_UNKNOWNPERMISSIONS) {
- *((u_long *)attrbufptr)++ =
- DerivePermissionSummary((VTOHFS(vp)->hfs_uid == UNKNOWNUID) ? console_user : VTOHFS(vp)->hfs_uid,
- VTOHFS(vp)->hfs_gid,
- hp->h_meta->h_mode,
- VTOVFS(vp),
- current_proc()->p_ucred,
- current_proc());
- } else {
- *((u_long *)attrbufptr)++ =
- DerivePermissionSummary((hp->h_meta->h_uid == UNKNOWNUID) ? console_user : hp->h_meta->h_uid,
- hp->h_meta->h_gid,
- hp->h_meta->h_mode,
- VTOVFS(vp),
- current_proc()->p_ucred,
- current_proc());
- };
- };
- };
+ case cmExists: /* -32718 */
+ case btExists: /* -32734 */
+ return EEXIST; /* +17 */
- *attrbufptrptr = attrbufptr;
- *varbufptrptr = varbufptr;
-}
-
-
-// Pack the directory attributes given hfsCatalogInfo
-void PackCatalogInfoDirAttributeBlock( struct attrlist *alist, struct vnode *vp,
- struct hfsCatalogInfo *catInfo, void **attrbufptrptr, void **varbufptrptr )
-{
- void *attrbufptr;
- attrgroup_t a;
- u_long valence;
+ case cmNotFound: /* -32719 */
+ case btNotFound: /* -32735 */
+ return ENOENT; /* 28 */
- attrbufptr = *attrbufptrptr;
- a = alist->dirattr;
+ case cmNotEmpty: /* -32717 */
+ return ENOTEMPTY; /* 66 */
- if ( (catInfo->nodeData.cnd_type == kCatalogFolderNode) && (a != 0) ) {
- valence = catInfo->nodeData.cnd_valence;
- if ((catInfo->nodeData.cnm_parID == kRootParID) &&
- (VTOHFS(vp)->hfs_private_metadata_dir != 0)) {
- --valence; /* hide private dir */
- }
- /* The 'link count' is faked */
- if (a & ATTR_DIR_LINKCOUNT)
- *((u_long *)attrbufptr)++ = 2 + valence;
- if (a & ATTR_DIR_ENTRYCOUNT)
- *((u_long *)attrbufptr)++ = valence;
- if (a & ATTR_DIR_MOUNTSTATUS)
- *((u_long *)attrbufptr)++ = 0;
- };
+ case cmFThdDirErr: /* -32714 */
+ return EISDIR; /* 21 */
- *attrbufptrptr = attrbufptr;
-}
-
-
-void PackDirAttributeBlock(struct attrlist *alist,
- struct vnode *vp,
- struct hfsCatalogInfo *catInfo,
- void **attrbufptrptr,
- void **varbufptrptr) {
- void *attrbufptr;
- attrgroup_t a;
- u_long valence;
+ case fxRangeErr: /* -32751 */
+ return ERANGE;
- attrbufptr = *attrbufptrptr;
+ case bdNamErr: /* -37 */
+ return ENAMETOOLONG; /* 63 */
- a = alist->dirattr;
- if ((vp->v_type == VDIR) && (a != 0)) {
- valence = catInfo->nodeData.cnd_valence;
- if ((catInfo->nodeData.cnm_parID == kRootParID) &&
- (VTOHFS(vp)->hfs_private_metadata_dir != 0)) {
- --valence; /* hide private dir */
- }
-
- /* The 'link count' is faked */
- if (a & ATTR_DIR_LINKCOUNT)
- *((u_long *)attrbufptr)++ = 2 + valence;
- if (a & ATTR_DIR_ENTRYCOUNT)
- *((u_long *)attrbufptr)++ = valence;
- if (a & ATTR_DIR_MOUNTSTATUS) {
- if (vp->v_mountedhere) {
- *((u_long *)attrbufptr)++ = DIR_MNTSTATUS_MNTPOINT;
- } else {
- *((u_long *)attrbufptr)++ = 0;
- };
- };
- };
+ case paramErr: /* -50 */
+ case fileBoundsErr: /* -1309 */
+ return EINVAL; /* +22 */
- *attrbufptrptr = attrbufptr;
-}
+ case fsBTBadNodeSize:
+ return ENXIO;
+ default:
+ return EIO; /* +5 */
+ }
+}
-// Pack the file attributes from the hfsCatalogInfo for the file.
-void PackCatalogInfoFileAttributeBlock( struct attrlist *alist, struct vnode *root_vp, struct hfsCatalogInfo *catInfo, void **attrbufptrptr, void **varbufptrptr )
+/*
+ * Find the current thread's directory hint for a given index.
+ *
+ * Requires an exclusive lock on directory cnode.
+ *
+ * Use detach if the cnode lock must be dropped while the hint is still active.
+ */
+__private_extern__
+directoryhint_t *
+hfs_getdirhint(struct cnode *dcp, int index, int detach)
{
- void *attrbufptr;
- void *varbufptr;
- attrgroup_t a;
- u_long attrlength;
- ExtendedVCB *vcb = VTOVCB(root_vp);
-
- attrbufptr = *attrbufptrptr;
- varbufptr = *varbufptrptr;
-
- a = alist->fileattr;
- if ( (catInfo->nodeData.cnd_type == kCatalogFileNode) && (a != 0) )
- {
-#if HFS_HARDLINKS
- if (a & ATTR_FILE_LINKCOUNT) {
- u_long linkcnt = catInfo->nodeData.cnd_linkCount;
+ struct timeval tv;
+ directoryhint_t *hint;
+ boolean_t need_remove, need_init;
+ const u_int8_t * name;
- if (linkcnt < 1)
- linkcnt = 1;
- *((u_long *)attrbufptr)++ = linkcnt;
- }
-#else
- if (a & ATTR_FILE_LINKCOUNT) *((u_long *)attrbufptr)++ = 1;
-#endif
- if (a & ATTR_FILE_TOTALSIZE) {
- *((off_t *)attrbufptr)++ =
- (off_t)catInfo->nodeData.cnd_datafork.logicalSize +
- (off_t)catInfo->nodeData.cnd_rsrcfork.logicalSize;
- }
- if (a & ATTR_FILE_ALLOCSIZE) {
- *((off_t *)attrbufptr)++ =
- (off_t)((off_t)catInfo->nodeData.cnd_datafork.totalBlocks * (off_t)vcb->blockSize) +
- (off_t)((off_t)catInfo->nodeData.cnd_rsrcfork.totalBlocks * (off_t)vcb->blockSize);
- }
- if (a & ATTR_FILE_IOBLOCKSIZE) {
- *((u_long *)attrbufptr)++ = (u_long)(VTOHFS(root_vp)->hfs_logBlockSize);
- }
- if (a & ATTR_FILE_CLUMPSIZE) {
- *((u_long *)attrbufptr)++ = vcb->vcbClpSiz;
- }
- if (a & ATTR_FILE_DEVTYPE) {
- u_long rawdev;
- u_short filetype;
+ microuptime(&tv);
- filetype = (catInfo->nodeData.cnd_mode & IFMT);
- if (filetype == IFCHR || filetype == IFBLK)
- rawdev = (u_long)catInfo->nodeData.cnd_rawDevice;
- else
- rawdev = 0;
-
- *((u_long *)attrbufptr)++ = rawdev;
- }
- if (a & ATTR_FILE_FILETYPE) {
- *((u_long *)attrbufptr)++ = 0; /* XXX PPD */
- }
- if (a & ATTR_FILE_FORKCOUNT) {
- *((u_long *)attrbufptr)++ = 2; /* XXX PPD */
+ /*
+ * Look for an existing hint first. If not found, create a new one (when
+ * the list is not full) or recycle the oldest hint. Since new hints are
+ * always added to the head of the list, the last hint is always the
+ * oldest.
+ */
+ TAILQ_FOREACH(hint, &dcp->c_hintlist, dh_link) {
+ if (hint->dh_index == index)
+ break;
+ }
+ if (hint != NULL) { /* found an existing hint */
+ need_init = false;
+ need_remove = true;
+ } else { /* cannot find an existing hint */
+ need_init = true;
+ if (dcp->c_dirhintcnt < HFS_MAXDIRHINTS) { /* we don't need recycling */
+ /* Create a default directory hint */
+ MALLOC_ZONE(hint, directoryhint_t *, sizeof(directoryhint_t), M_HFSDIRHINT, M_WAITOK);
+ ++dcp->c_dirhintcnt;
+ need_remove = false;
+ } else { /* recycle the last (i.e., the oldest) hint */
+ hint = TAILQ_LAST(&dcp->c_hintlist, hfs_hinthead);
+ if ((hint->dh_desc.cd_flags & CD_HASBUF) &&
+ (name = hint->dh_desc.cd_nameptr)) {
+ hint->dh_desc.cd_nameptr = NULL;
+ hint->dh_desc.cd_namelen = 0;
+ hint->dh_desc.cd_flags &= ~CD_HASBUF;
+ vfs_removename((const char *)name);
+ }
+ need_remove = true;
}
- if (a & ATTR_FILE_FORKLIST) {
- attrlength = 0;
- ((struct attrreference *)attrbufptr)->attr_dataoffset = 0;
- ((struct attrreference *)attrbufptr)->attr_length = attrlength;
-
- /* Advance beyond the space just allocated and round up to the next 4-byte boundary: */
- (char *)varbufptr += attrlength + ((4 - (attrlength & 3)) & 3);
- ++((struct attrreference *)attrbufptr);
- };
- if (a & ATTR_FILE_DATALENGTH) {
- *((off_t *)attrbufptr)++ =
- (off_t)catInfo->nodeData.cnd_datafork.logicalSize;
- }
- if (a & ATTR_FILE_DATAALLOCSIZE) {
- *((off_t *)attrbufptr)++ =
- (off_t)((off_t)catInfo->nodeData.cnd_datafork.totalBlocks * (off_t)vcb->blockSize);
- }
- if (a & ATTR_FILE_DATAEXTENTS) {
- bcopy(&catInfo->nodeData.cnd_datafork.extents, attrbufptr, sizeof(extentrecord));
- (char *)attrbufptr += sizeof(extentrecord) + ((4 - (sizeof(extentrecord) & 3)) & 3);
- };
- if (a & ATTR_FILE_RSRCLENGTH) {
- *((off_t *)attrbufptr)++ =
- (off_t)catInfo->nodeData.cnd_rsrcfork.logicalSize;
- }
- if (a & ATTR_FILE_RSRCALLOCSIZE) {
- *((off_t *)attrbufptr)++ =
- (off_t)((off_t)catInfo->nodeData.cnd_rsrcfork.totalBlocks * (off_t)vcb->blockSize);
- }
- if (a & ATTR_FILE_RSRCEXTENTS) {
- bcopy(&catInfo->nodeData.cnd_rsrcfork.extents, attrbufptr, sizeof(extentrecord));
- (char *)attrbufptr += sizeof(extentrecord) + ((4 - (sizeof(extentrecord) & 3)) & 3);
- };
- };
-
- *attrbufptrptr = attrbufptr;
- *varbufptrptr = varbufptr;
-}
+ }
+ if (need_remove)
+ TAILQ_REMOVE(&dcp->c_hintlist, hint, dh_link);
-void PackFileAttributeBlock(struct attrlist *alist,
- struct vnode *vp,
- struct hfsCatalogInfo *catInfo,
- void **attrbufptrptr,
- void **varbufptrptr) {
- struct hfsnode *hp = VTOH(vp);
- FCB *fcb = HTOFCB(hp);
- ExtendedVCB *vcb = HTOVCB(hp);
- Boolean isHFSPlus = (vcb->vcbSigWord == kHFSPlusSigWord);
- void *attrbufptr = *attrbufptrptr;
- void *varbufptr = *varbufptrptr;
- attrgroup_t a = alist->fileattr;
- u_long attrlength;
-
- if (a != 0) {
-#if HFS_HARDLINKS
- if (a & ATTR_FILE_LINKCOUNT) {
- u_long linkcnt = catInfo->nodeData.cnd_linkCount;
-
- if (linkcnt < 1)
- linkcnt = 1;
- *((u_long *)attrbufptr)++ = linkcnt;
- }
-#else
- if (a & ATTR_FILE_LINKCOUNT) *((u_long *)attrbufptr)++ = 1;
-#endif
- if (a & ATTR_FILE_TOTALSIZE) {
- *((off_t *)attrbufptr)++ =
- (off_t)catInfo->nodeData.cnd_datafork.logicalSize +
- (off_t)catInfo->nodeData.cnd_rsrcfork.logicalSize;
- }
- if (a & ATTR_FILE_ALLOCSIZE) {
- switch (H_FORKTYPE(hp)) {
- case kDataFork:
- *((off_t *)attrbufptr)++ =
- (off_t)fcb->fcbPLen +
- (off_t)((off_t)catInfo->nodeData.cnd_rsrcfork.totalBlocks * (off_t)vcb->blockSize);
- break;
- case kRsrcFork:
- *((off_t *)attrbufptr)++ =
- (off_t)fcb->fcbPLen +
- (off_t)((off_t)catInfo->nodeData.cnd_datafork.totalBlocks * (off_t)vcb->blockSize);
- break;
- default:
- *((off_t *)attrbufptr)++ =
- (off_t)((off_t)catInfo->nodeData.cnd_datafork.totalBlocks * (off_t)vcb->blockSize) +
- (off_t)((off_t)catInfo->nodeData.cnd_rsrcfork.totalBlocks * (off_t)vcb->blockSize);
- };
- };
- if (a & ATTR_FILE_IOBLOCKSIZE) *((u_long *)attrbufptr)++ = GetLogicalBlockSize(vp);
- if (a & ATTR_FILE_CLUMPSIZE) *((u_long *)attrbufptr)++ = fcb->fcbClmpSize;
- if (a & ATTR_FILE_DEVTYPE) {
- u_long rawdev;
-
- if ((vp->v_type == VBLK) || (vp->v_type == VCHR))
- rawdev = (u_long)catInfo->nodeData.cnd_rawDevice;
- else
- rawdev = 0;
- *((u_long *)attrbufptr)++ = rawdev;
- }
- if (a & ATTR_FILE_FILETYPE) *((u_long *)attrbufptr)++ = 0; /* XXX PPD */
- if (a & ATTR_FILE_FORKCOUNT) *((u_long *)attrbufptr)++ = 2; /* XXX PPD */
- if (a & ATTR_FILE_FORKLIST) {
- attrlength = 0;
- ((struct attrreference *)attrbufptr)->attr_dataoffset = 0;
- ((struct attrreference *)attrbufptr)->attr_length = attrlength;
-
- /* Advance beyond the space just allocated and round up to the next 4-byte boundary: */
- (char *)varbufptr += attrlength + ((4 - (attrlength & 3)) & 3);
- ++((struct attrreference *)attrbufptr);
- };
- if (H_FORKTYPE(hp) == kDataFork) {
- if (a & ATTR_FILE_DATALENGTH)
- *((off_t *)attrbufptr)++ = fcb->fcbEOF;
- if (a & ATTR_FILE_DATAALLOCSIZE) *((off_t *)attrbufptr)++ = fcb->fcbPLen;
- if (a & ATTR_FILE_DATAEXTENTS) {
- bcopy ( fcb->fcbExtents, attrbufptr, sizeof(extentrecord));
- (char *)attrbufptr += sizeof(extentrecord) + ((4 - (sizeof(extentrecord) & 3)) & 3);
- };
- } else {
- if (a & ATTR_FILE_DATALENGTH) {
- *((off_t *)attrbufptr)++ =
- (off_t)catInfo->nodeData.cnd_datafork.logicalSize;
- }
- if (a & ATTR_FILE_DATAALLOCSIZE) {
- *((off_t *)attrbufptr)++ =
- (off_t)((off_t)catInfo->nodeData.cnd_datafork.totalBlocks * (off_t)vcb->blockSize);
- }
- if (a & ATTR_FILE_DATAEXTENTS) {
- bcopy(&catInfo->nodeData.cnd_datafork.extents, attrbufptr, sizeof(extentrecord));
- (char *)attrbufptr += sizeof(extentrecord) + ((4 - (sizeof(extentrecord) & 3)) & 3);
- };
- };
- if (H_FORKTYPE(hp) == kRsrcFork) {
- if (a & ATTR_FILE_RSRCLENGTH)
- *((off_t *)attrbufptr)++ = fcb->fcbEOF;
- if (a & ATTR_FILE_RSRCALLOCSIZE) *((off_t *)attrbufptr)++ = fcb->fcbPLen;
- if (a & ATTR_FILE_RSRCEXTENTS) {
- bcopy ( fcb->fcbExtents, attrbufptr, sizeof(extentrecord));
- (char *)attrbufptr += sizeof(extentrecord) + ((4 - (sizeof(extentrecord) & 3)) & 3);
- };
- } else {
- if (a & ATTR_FILE_RSRCLENGTH) {
- *((off_t *)attrbufptr)++ =
- (off_t)catInfo->nodeData.cnd_rsrcfork.logicalSize;
- }
- if (a & ATTR_FILE_RSRCALLOCSIZE) {
- *((off_t *)attrbufptr)++ =
- (off_t)((off_t)catInfo->nodeData.cnd_rsrcfork.totalBlocks * (off_t)vcb->blockSize);
- }
- if (a & ATTR_FILE_RSRCEXTENTS) {
- bcopy(&catInfo->nodeData.cnd_rsrcfork.extents, attrbufptr, sizeof(extentrecord));
- (char *)attrbufptr += sizeof(extentrecord) + ((4 - (sizeof(extentrecord) & 3)) & 3);
- };
- };
- };
-
- *attrbufptrptr = attrbufptr;
- *varbufptrptr = varbufptr;
-}
-
-#if 0
-void PackForkAttributeBlock(struct attrlist *alist,
- struct vnode *vp,
- struct hfsCatalogInfo *catInfo,
- void **attrbufptrptr,
- void **varbufptrptr) {
- /* XXX PPD TBC */
+ if (detach)
+ --dcp->c_dirhintcnt;
+ else
+ TAILQ_INSERT_HEAD(&dcp->c_hintlist, hint, dh_link);
+
+ if (need_init) {
+ hint->dh_index = index;
+ hint->dh_desc.cd_flags = 0;
+ hint->dh_desc.cd_encoding = 0;
+ hint->dh_desc.cd_namelen = 0;
+ hint->dh_desc.cd_nameptr = NULL;
+ hint->dh_desc.cd_parentcnid = dcp->c_fileid;
+ hint->dh_desc.cd_hint = dcp->c_childhint;
+ hint->dh_desc.cd_cnid = 0;
+ }
+ hint->dh_time = tv.tv_sec;
+ return (hint);
}
-#endif
-
-// This routine takes catInfo, and alist, as inputs and packs it into an attribute block.
-void PackCatalogInfoAttributeBlock ( struct attrlist *alist, struct vnode *root_vp, struct hfsCatalogInfo *catInfo, void **attrbufptrptr, void **varbufptrptr)
+/*
+ * Release a single directory hint.
+ *
+ * Requires an exclusive lock on directory cnode.
+ */
+__private_extern__
+void
+hfs_reldirhint(struct cnode *dcp, directoryhint_t * relhint)
{
- //XXX Preflight that alist only contains bits with fields in catInfo
-
- PackCommonCatalogInfoAttributeBlock( alist, root_vp, catInfo, attrbufptrptr, varbufptrptr );
-
- switch ( catInfo->nodeData.cnd_type )
- {
- case kCatalogFolderNode:
- PackCatalogInfoDirAttributeBlock( alist, root_vp, catInfo, attrbufptrptr, varbufptrptr );
- break;
-
- case kCatalogFileNode:
- PackCatalogInfoFileAttributeBlock( alist, root_vp, catInfo, attrbufptrptr, varbufptrptr );
- break;
-
- default: /* Without this the compiler complains about VNON,VBLK,VCHR,VLNK,VSOCK,VFIFO,VBAD and VSTR not being handled... */
- /* XXX PPD - Panic? */
+ const u_int8_t * name;
+ directoryhint_t *hint;
+
+ /* Check if item is on list (could be detached) */
+ TAILQ_FOREACH(hint, &dcp->c_hintlist, dh_link) {
+ if (hint == relhint) {
+ TAILQ_REMOVE(&dcp->c_hintlist, relhint, dh_link);
+ --dcp->c_dirhintcnt;
break;
+ }
+ }
+ name = relhint->dh_desc.cd_nameptr;
+ if ((relhint->dh_desc.cd_flags & CD_HASBUF) && (name != NULL)) {
+ relhint->dh_desc.cd_nameptr = NULL;
+ relhint->dh_desc.cd_namelen = 0;
+ relhint->dh_desc.cd_flags &= ~CD_HASBUF;
+ vfs_removename((const char *)name);
}
+ FREE_ZONE(relhint, sizeof(directoryhint_t), M_HFSDIRHINT);
}
+/*
+ * Release directory hints for given directory
+ *
+ * Requires an exclusive lock on directory cnode.
+ */
+__private_extern__
+void
+hfs_reldirhints(struct cnode *dcp, int stale_hints_only)
+{
+ struct timeval tv;
+ directoryhint_t *hint, *prev;
+ const u_int8_t * name;
+
+ if (stale_hints_only)
+ microuptime(&tv);
+
+ /* searching from the oldest to the newest, so we can stop early when releasing stale hints only */
+ for (hint = TAILQ_LAST(&dcp->c_hintlist, hfs_hinthead); hint != NULL; hint = prev) {
+ if (stale_hints_only && (tv.tv_sec - hint->dh_time) < HFS_DIRHINT_TTL)
+ break; /* stop here if this entry is too new */
+ name = hint->dh_desc.cd_nameptr;
+ if ((hint->dh_desc.cd_flags & CD_HASBUF) && (name != NULL)) {
+ hint->dh_desc.cd_nameptr = NULL;
+ hint->dh_desc.cd_namelen = 0;
+ hint->dh_desc.cd_flags &= ~CD_HASBUF;
+ vfs_removename((const char *)name);
+ }
+ prev = TAILQ_PREV(hint, hfs_hinthead, dh_link); /* must save this pointer before calling FREE_ZONE on this node */
+ TAILQ_REMOVE(&dcp->c_hintlist, hint, dh_link);
+ FREE_ZONE(hint, sizeof(directoryhint_t), M_HFSDIRHINT);
+ --dcp->c_dirhintcnt;
+ }
+}
-
-void PackAttributeBlock(struct attrlist *alist,
- struct vnode *vp,
- struct hfsCatalogInfo *catInfo,
- void **attrbufptrptr,
- void **varbufptrptr)
+/*
+ * Insert a detached directory hint back into the list of dirhints.
+ *
+ * Requires an exclusive lock on directory cnode.
+ */
+__private_extern__
+void
+hfs_insertdirhint(struct cnode *dcp, directoryhint_t * hint)
{
- if (alist->volattr != 0) {
- DBG_ASSERT((vp->v_flag & VROOT) != 0);
- PackVolumeInfo(alist,vp, catInfo, attrbufptrptr, varbufptrptr);
- } else {
- PackCommonAttributeBlock(alist, vp, catInfo, attrbufptrptr, varbufptrptr);
-
- switch (vp->v_type) {
- case VDIR:
- PackDirAttributeBlock(alist, vp, catInfo, attrbufptrptr, varbufptrptr);
- break;
-
- case VREG:
- case VLNK:
- PackFileAttributeBlock(alist, vp, catInfo, attrbufptrptr, varbufptrptr);
- break;
-
- /* Without this the compiler complains about VNON,VBLK,VCHR,VLNK,VSOCK,VFIFO,VBAD and VSTR
- not being handled...
- */
- default:
- /* XXX PPD - Panic? */
- break;
- };
- };
-};
+ directoryhint_t *test;
+ TAILQ_FOREACH(test, &dcp->c_hintlist, dh_link) {
+ if (test == hint)
+ panic("hfs_insertdirhint: hint %p already on list!", hint);
+ }
+ TAILQ_INSERT_HEAD(&dcp->c_hintlist, hint, dh_link);
+ ++dcp->c_dirhintcnt;
+}
-void UnpackVolumeAttributeBlock(struct attrlist *alist,
- struct vnode *root_vp,
- ExtendedVCB *vcb,
- void **attrbufptrptr,
- void **varbufptrptr) {
- void *attrbufptr = *attrbufptrptr;
- attrgroup_t a;
-
- if ((alist->commonattr == 0) && (alist->volattr == 0)) {
- return; /* Get out without dirtying the VCB */
- };
+/*
+ * Perform a case-insensitive compare of two UTF-8 filenames.
+ *
+ * Returns 0 if the strings match.
+ */
+__private_extern__
+int
+hfs_namecmp(const u_int8_t *str1, size_t len1, const u_int8_t *str2, size_t len2)
+{
+ u_int16_t *ustr1, *ustr2;
+ size_t ulen1, ulen2;
+ size_t maxbytes;
+ int cmp = -1;
+
+ if (len1 != len2)
+ return (cmp);
+
+ maxbytes = kHFSPlusMaxFileNameChars << 1;
+ MALLOC(ustr1, u_int16_t *, maxbytes << 1, M_TEMP, M_WAITOK);
+ ustr2 = ustr1 + (maxbytes >> 1);
+
+ if (utf8_decodestr(str1, len1, ustr1, &ulen1, maxbytes, ':', 0) != 0)
+ goto out;
+ if (utf8_decodestr(str2, len2, ustr2, &ulen2, maxbytes, ':', 0) != 0)
+ goto out;
+
+ cmp = FastUnicodeCompare(ustr1, ulen1>>1, ustr2, ulen2>>1);
+out:
+ FREE(ustr1, M_TEMP);
+ return (cmp);
+}
- VCB_LOCK(vcb);
- a = alist->commonattr;
-
- if (a & ATTR_CMN_SCRIPT) {
- vcb->volumeNameEncodingHint = (u_int32_t)*(((text_encoding_t *)attrbufptr)++);
-#if HFS_DIAGNOSTIC
- a &= ~ATTR_CMN_SCRIPT;
-#endif
- };
- if (a & ATTR_CMN_CRTIME) {
- vcb->vcbCrDate = to_hfs_time((UInt32)((struct timespec *)attrbufptr)->tv_sec);
- /* Need to update the local time also */
- vcb->localCreateDate = UTCToLocal(vcb->vcbCrDate);
- ++((struct timespec *)attrbufptr);
-#if HFS_DIAGNOSTIC
- a &= ~ATTR_CMN_CRTIME;
-#endif
- };
- if (a & ATTR_CMN_MODTIME) {
- vcb->vcbLsMod = to_hfs_time((UInt32)((struct timespec *)attrbufptr)->tv_sec);
- ++((struct timespec *)attrbufptr);
-#if HFS_DIAGNOSTIC
- a &= ~ATTR_CMN_MODTIME;
-#endif
- };
- if (a & ATTR_CMN_BKUPTIME) {
- vcb->vcbVolBkUp = to_hfs_time((UInt32)((struct timespec *)attrbufptr)->tv_sec);
- ++((struct timespec *)attrbufptr);
-#if HFS_DIAGNOSTIC
- a &= ~ATTR_CMN_BKUPTIME;
-#endif
- };
- if (a & ATTR_CMN_FNDRINFO) {
- bcopy (attrbufptr, &vcb->vcbFndrInfo, sizeof(vcb->vcbFndrInfo));
- (char *)attrbufptr += sizeof(vcb->vcbFndrInfo);
-#if HFS_DIAGNOSTIC
- a &= ~ATTR_CMN_FNDRINFO;
-#endif
- };
-
- DBG_ASSERT(a == 0); /* All common attributes for volumes must've been handled by now... */
+typedef struct jopen_cb_info {
+ off_t jsize;
+ char *desired_uuid;
+ struct vnode *jvp;
+ size_t blksize;
+ int need_clean;
+ int need_init;
+} jopen_cb_info;
- a = alist->volattr & ~ATTR_VOL_INFO;
- if (a & ATTR_VOL_NAME) {
- copystr(((char *)attrbufptr) + *((u_long *)attrbufptr), vcb->vcbVN, sizeof(vcb->vcbVN), NULL);
- (char *)attrbufptr += sizeof(struct attrreference);
-#if HFS_DIAGNOSTIC
- a &= ~ATTR_VOL_NAME;
-#endif
- };
+static int
+journal_open_cb(const char *bsd_dev_name, const char *uuid_str, void *arg)
+{
+ struct nameidata nd;
+ jopen_cb_info *ji = (jopen_cb_info *)arg;
+ char bsd_name[256];
+ int error;
- DBG_ASSERT(a == 0); /* All common attributes for volumes must've been handled by now... */
+ strlcpy(&bsd_name[0], "/dev/", sizeof(bsd_name));
+ strlcpy(&bsd_name[5], bsd_dev_name, sizeof(bsd_name)-5);
- vcb->vcbFlags |= 0xFF00; // Mark the VCB dirty
+ if (ji->desired_uuid && ji->desired_uuid[0] && strcmp(uuid_str, ji->desired_uuid) != 0) {
+ return 1; // keep iterating
+ }
- VCB_UNLOCK(vcb);
-}
+ // if we're here, either the desired uuid matched or there was no
+ // desired uuid so let's try to open the device for writing and
+ // see if it works. if it does, we'll use it.
+
+ NDINIT(&nd, LOOKUP, OP_LOOKUP, LOCKLEAF, UIO_SYSSPACE32, CAST_USER_ADDR_T(bsd_name), vfs_context_kernel());
+ if ((error = namei(&nd))) {
+ printf("hfs: journal open cb: error %d looking up device %s (dev uuid %s)\n", error, bsd_name, uuid_str);
+ return 1; // keep iterating
+ }
+ ji->jvp = nd.ni_vp;
+ nameidone(&nd);
-void UnpackCommonAttributeBlock(struct attrlist *alist,
- struct vnode *vp,
- struct hfsCatalogInfo *catInfo,
- void **attrbufptrptr,
- void **varbufptrptr) {
- struct hfsnode *hp = VTOH(vp);
- void *attrbufptr;
- attrgroup_t a;
-
- attrbufptr = *attrbufptrptr;
+ if (ji->jvp == NULL) {
+ printf("hfs: journal open cb: did not find %s (error %d)\n", bsd_name, error);
+ } else {
+ error = VNOP_OPEN(ji->jvp, FREAD|FWRITE, vfs_context_kernel());
+ if (error == 0) {
+ // if the journal is dirty and we didn't specify a desired
+ // journal device uuid, then do not use the journal. but
+ // if the journal is just invalid (e.g. it hasn't been
+ // initialized) then just set the need_init flag.
+ if (ji->need_clean && ji->desired_uuid && ji->desired_uuid[0] == '\0') {
+ error = journal_is_clean(ji->jvp, 0, ji->jsize, (void *)1, ji->blksize);
+ if (error == EBUSY) {
+ VNOP_CLOSE(ji->jvp, FREAD|FWRITE, vfs_context_kernel());
+ vnode_put(ji->jvp);
+ ji->jvp = NULL;
+ return 1; // keep iterating
+ } else if (error == EINVAL) {
+ ji->need_init = 1;
+ }
+ }
- DBG_ASSERT(catInfo != NULL);
-
- a = alist->commonattr;
- if (a & ATTR_CMN_SCRIPT) {
- catInfo->nodeData.cnd_textEncoding = (u_int32_t)*((text_encoding_t *)attrbufptr)++;
- UpdateVolumeEncodings(VTOVCB(vp), catInfo->nodeData.cnd_textEncoding); /* Update the volume encoding */
-#if HFS_DIAGNOSTIC
- a &= ~ATTR_CMN_SCRIPT;
-#endif
- };
- if (a & ATTR_CMN_CRTIME) {
- catInfo->nodeData.cnd_createDate = to_hfs_time((UInt32)((struct timespec *)attrbufptr)->tv_sec);
- VTOH(vp)->h_meta->h_crtime = (UInt32)((struct timespec *)attrbufptr)->tv_sec;
- ++((struct timespec *)attrbufptr);
-#if HFS_DIAGNOSTIC
- a &= ~ATTR_CMN_CRTIME;
-#endif
- };
- if (a & ATTR_CMN_MODTIME) {
- catInfo->nodeData.cnd_contentModDate = to_hfs_time((UInt32)((struct timespec *)attrbufptr)->tv_sec);
- VTOH(vp)->h_meta->h_mtime = (UInt32)((struct timespec *)attrbufptr)->tv_sec;
- ++((struct timespec *)attrbufptr);
- hp->h_nodeflags &= ~IN_UPDATE;
-#if HFS_DIAGNOSTIC
- a &= ~ATTR_CMN_MODTIME;
-#endif
- };
- if (a & ATTR_CMN_CHGTIME) {
- catInfo->nodeData.cnd_attributeModDate = to_hfs_time((UInt32)((struct timespec *)attrbufptr)->tv_sec);
- VTOH(vp)->h_meta->h_ctime = (UInt32)((struct timespec *)attrbufptr)->tv_sec;
- ++((struct timespec *)attrbufptr);
- hp->h_nodeflags &= ~IN_CHANGE;
-#if HFS_DIAGNOSTIC
- a &= ~ATTR_CMN_CHGTIME;
-#endif
- };
- if (a & ATTR_CMN_ACCTIME) {
- catInfo->nodeData.cnd_accessDate = to_hfs_time((UInt32)((struct timespec *)attrbufptr)->tv_sec);
- VTOH(vp)->h_meta->h_atime = (UInt32)((struct timespec *)attrbufptr)->tv_sec;
- ++((struct timespec *)attrbufptr);
- hp->h_nodeflags &= ~IN_ACCESS;
-#if HFS_DIAGNOSTIC
- a &= ~ATTR_CMN_ACCTIME;
-#endif
- };
- if (a & ATTR_CMN_BKUPTIME) {
- catInfo->nodeData.cnd_backupDate = to_hfs_time((UInt32)((struct timespec *)attrbufptr)->tv_sec);
- VTOH(vp)->h_meta->h_butime = (UInt32)((struct timespec *)attrbufptr)->tv_sec;
- ++((struct timespec *)attrbufptr);
-#if HFS_DIAGNOSTIC
- a &= ~ATTR_CMN_BKUPTIME;
-#endif
- };
- if (a & ATTR_CMN_FNDRINFO) {
- bcopy (attrbufptr, &catInfo->nodeData.cnd_finderInfo, sizeof(catInfo->nodeData.cnd_finderInfo));
- (char *)attrbufptr += sizeof(catInfo->nodeData.cnd_finderInfo);
-#if HFS_DIAGNOSTIC
- a &= ~ATTR_CMN_FNDRINFO;
-#endif
- };
- if (a & ATTR_CMN_OWNERID) {
- if (VTOVCB(vp)->vcbSigWord == kHFSPlusSigWord) {
- u_int32_t uid = (u_int32_t)*((uid_t *)attrbufptr)++;
- if (uid != (uid_t)VNOVAL)
- hp->h_meta->h_uid = uid; /* catalog will get updated by hfs_chown() */
- }
- else {
- ((uid_t *)attrbufptr)++;
+ if (ji->desired_uuid && ji->desired_uuid[0] == '\0') {
+ strlcpy(ji->desired_uuid, uuid_str, 128);
+ }
+ vnode_setmountedon(ji->jvp);
+ // printf("hfs: journal open cb: got device %s (%s)\n", bsd_name, uuid_str);
+ return 0; // stop iterating
+ } else {
+ vnode_put(ji->jvp);
+ ji->jvp = NULL;
}
-#if HFS_DIAGNOSTIC
- a &= ~ATTR_CMN_OWNERID;
-#endif
- };
- if (a & ATTR_CMN_GRPID) {
- u_int32_t gid = (u_int32_t)*((gid_t *)attrbufptr)++;
- if (VTOVCB(vp)->vcbSigWord == kHFSPlusSigWord) {
- if (gid != (gid_t)VNOVAL)
- hp->h_meta->h_gid = gid; /* catalog will get updated by hfs_chown() */
- };
-#if HFS_DIAGNOSTIC
- a &= ~ATTR_CMN_GRPID;
-#endif
- };
- if (a & ATTR_CMN_ACCESSMASK) {
- u_int16_t mode = (u_int16_t)*((u_long *)attrbufptr)++;
- if (VTOVCB(vp)->vcbSigWord == kHFSPlusSigWord) {
- if (mode != (mode_t)VNOVAL) {
- hp->h_meta->h_mode &= ~ALLPERMS;
- hp->h_meta->h_mode |= (mode & ALLPERMS); /* catalog will get updated by hfs_chmod() */
- }
- };
-#if HFS_DIAGNOSTIC
- a &= ~ATTR_CMN_ACCESSMASK;
-#endif
- };
- if (a & ATTR_CMN_FLAGS) {
- u_long flags = *((u_long *)attrbufptr)++;
- /* Flags are settable only on HFS+ volumes. A special exception is made for the IMMUTABLE
- flags (SF_IMMUTABLE and UF_IMMUTABLE), which can be set on HFS volumes as well: */
- if ((VTOVCB(vp)->vcbSigWord == kHFSPlusSigWord) ||
- ((VTOVCB(vp)->vcbSigWord == kHFSSigWord) && ((flags & ~IMMUTABLE) == 0))) {
- if (flags != (u_long)VNOVAL) {
- hp->h_meta->h_pflags = flags; /* catalog will get updated by hfs_chflags */
- };
- };
-#if HFS_DIAGNOSTIC
- a &= ~ATTR_CMN_FLAGS;
-#endif
- };
-
-#if HFS_DIAGNOSTIC
- if (a != 0) {
- DEBUG_BREAK_MSG(("UnpackCommonAttributes: unhandled bit: 0x%08X\n", a));
- };
-#endif
+ }
- *attrbufptrptr = attrbufptr;
-// *varbufptrptr = varbufptr;
+ return 1; // keep iterating
}
+extern dev_t IOBSDGetMediaWithUUID(const char *uuid_cstring, char *bsd_name, int bsd_name_len, int timeout);
+extern void IOBSDIterateMediaWithContent(const char *uuid_cstring, int (*func)(const char *bsd_dev_name, const char *uuid_str, void *arg), void *arg);
+kern_return_t IOBSDGetPlatformSerialNumber(char *serial_number_str, u_int32_t len);
-#if 0
-void UnpackDirAttributeBlock(struct attrlist *alist,
- struct vnode *vp,
- struct hfsCatalogInfo *catInfo,
- void **attrbufptrptr,
- void **varbufptrptr) {
- void *attrbufptr;
- void *varbufptr;
- attrgroup_t a;
- u_long attrlength;
-
- attrbufptr = *attrbufptrptr;
- varbufptr = *varbufptrptr;
-
- /* XXX PPD TBC */
-
- *attrbufptrptr = attrbufptr;
- *varbufptrptr = varbufptr;
-}
-#endif
-
-
+static vnode_t
+open_journal_dev(const char *vol_device,
+ int need_clean,
+ char *uuid_str,
+ char *machine_serial_num,
+ off_t jsize,
+ size_t blksize,
+ int *need_init)
+{
+ int retry_counter=0;
+ jopen_cb_info ji;
+
+ ji.jsize = jsize;
+ ji.desired_uuid = uuid_str;
+ ji.jvp = NULL;
+ ji.blksize = blksize;
+ ji.need_clean = need_clean;
+ ji.need_init = 0;
+
+// if (uuid_str[0] == '\0') {
+// printf("hfs: open journal dev: %s: locating any available non-dirty external journal partition\n", vol_device);
+// } else {
+// printf("hfs: open journal dev: %s: trying to find the external journal partition w/uuid %s\n", vol_device, uuid_str);
+// }
+ while (ji.jvp == NULL && retry_counter++ < 4) {
+ if (retry_counter > 1) {
+ if (uuid_str[0]) {
+ printf("hfs: open_journal_dev: uuid %s not found. waiting 10sec.\n", uuid_str);
+ } else {
+ printf("hfs: open_journal_dev: no available external journal partition found. waiting 10sec.\n");
+ }
+ delay_for_interval(10* 1000000, NSEC_PER_USEC); // wait for ten seconds and then try again
+ }
-#if 0
-void UnpackFileAttributeBlock(struct attrlist *alist,
- struct vnode *vp,
- struct hfsCatalogInfo *catInfo,
- void **attrbufptrptr,
- void **varbufptrptr) {
- void *attrbufptr;
- void *varbufptr;
- attrgroup_t a;
- u_long attrlength;
-
- attrbufptr = *attrbufptrptr;
- varbufptr = *varbufptrptr;
-
- /* XXX PPD TBC */
-
- *attrbufptrptr = attrbufptr;
- *varbufptrptr = varbufptr;
-}
-#endif
+ IOBSDIterateMediaWithContent(EXTJNL_CONTENT_TYPE_UUID, journal_open_cb, &ji);
+ }
+ if (ji.jvp == NULL) {
+ printf("hfs: volume: %s: did not find jnl device uuid: %s from machine serial number: %s\n",
+ vol_device, uuid_str, machine_serial_num);
+ }
+ *need_init = ji.need_init;
-#if 0
-void UnpackForkAttributeBlock(struct attrlist *alist,
- struct vnode *vp,
- struct hfsCatalogInfo *catInfo,
- void **attrbufptrptr,
- void **varbufptrptr) {
- void *attrbufptr;
- void *varbufptr;
- attrgroup_t a;
- u_long attrlength;
-
- attrbufptr = *attrbufptrptr;
- varbufptr = *varbufptrptr;
-
- /* XXX PPD TBC */
-
- *attrbufptrptr = attrbufptr;
- *varbufptrptr = varbufptr;
+ return ji.jvp;
}
-#endif
+int
+hfs_early_journal_init(struct hfsmount *hfsmp, HFSPlusVolumeHeader *vhp,
+ void *_args, off_t embeddedOffset, daddr64_t mdb_offset,
+ HFSMasterDirectoryBlock *mdbp, kauth_cred_t cred)
+{
+ JournalInfoBlock *jibp;
+ struct buf *jinfo_bp, *bp;
+ int sectors_per_fsblock, arg_flags=0, arg_tbufsz=0;
+ int retval, write_jibp = 0;
+ uint32_t blksize = hfsmp->hfs_logical_block_size;
+ struct vnode *devvp;
+ struct hfs_mount_args *args = _args;
+ u_int32_t jib_flags;
+ u_int64_t jib_offset;
+ u_int64_t jib_size;
+ const char *dev_name;
+
+ devvp = hfsmp->hfs_devvp;
+ dev_name = vnode_name(devvp);
+ if (dev_name == NULL) {
+ dev_name = "unknown-dev";
+ }
-void UnpackAttributeBlock(struct attrlist *alist,
- struct vnode *vp,
- struct hfsCatalogInfo *catInfo,
- void **attrbufptrptr,
- void **varbufptrptr) {
-
+ if (args != NULL && (args->flags & HFSFSMNT_EXTENDED_ARGS)) {
+ arg_flags = args->journal_flags;
+ arg_tbufsz = args->journal_tbuffer_size;
+ }
- if (alist->volattr != 0) {
- UnpackVolumeAttributeBlock(alist, vp, VTOVCB(vp), attrbufptrptr, varbufptrptr);
- return;
- };
-
- /* We're dealing with a vnode object here: */
- UnpackCommonAttributeBlock(alist, vp, catInfo, attrbufptrptr, varbufptrptr);
+ sectors_per_fsblock = SWAP_BE32(vhp->blockSize) / blksize;
+
+ jinfo_bp = NULL;
+ retval = (int)buf_meta_bread(devvp,
+ (daddr64_t)((embeddedOffset/blksize) +
+ ((u_int64_t)SWAP_BE32(vhp->journalInfoBlock)*sectors_per_fsblock)),
+ hfsmp->hfs_physical_block_size, cred, &jinfo_bp);
+ if (retval) {
+ if (jinfo_bp) {
+ buf_brelse(jinfo_bp);
+ }
+ return retval;
+ }
-#if 0
- switch (vp->v_type) {
- case VDIR:
- UnpackDirAttributeBlock(alist, vp, catInfo, attrbufptrptr, varbufptrptr);
- break;
-
- case VREG:
- /* case VCPLX: */ /* XXX PPD TBC */
- UnpackFileAttributeBlock(alist, vp, catInfo, attrbufptrptr, varbufptrptr);
- break;
-
- case VFORK:
- UnpackForkAttributeBlock(alist, vp, catInfo, attrbufptrptr, varbufptrptr);
- break;
-
- /* Without this the compiler complains about VNON,VBLK,VCHR,VLNK,VSOCK,VFIFO,VBAD and VSTR
- not being handled...
- */
- default:
- /* XXX PPD - Panic? */
- break;
- };
-#endif
+ jibp = (JournalInfoBlock *)buf_dataptr(jinfo_bp);
+ jib_flags = SWAP_BE32(jibp->flags);
+ jib_size = SWAP_BE64(jibp->size);
-};
+ if (jib_flags & kJIJournalInFSMask) {
+ hfsmp->jvp = hfsmp->hfs_devvp;
+ jib_offset = SWAP_BE64(jibp->offset);
+ } else {
+ int need_init=0;
+
+ // if the volume was unmounted cleanly then we'll pick any
+ // available external journal partition
+ //
+ if (SWAP_BE32(vhp->attributes) & kHFSVolumeUnmountedMask) {
+ *((char *)&jibp->ext_jnl_uuid[0]) = '\0';
+ }
+ hfsmp->jvp = open_journal_dev(dev_name,
+ !(jib_flags & kJIJournalNeedInitMask),
+ (char *)&jibp->ext_jnl_uuid[0],
+ (char *)&jibp->machine_serial_num[0],
+ jib_size,
+ hfsmp->hfs_logical_block_size,
+ &need_init);
+ if (hfsmp->jvp == NULL) {
+ buf_brelse(jinfo_bp);
+ return EROFS;
+ } else {
+ if (IOBSDGetPlatformSerialNumber(&jibp->machine_serial_num[0], sizeof(jibp->machine_serial_num)) != KERN_SUCCESS) {
+ strlcpy(&jibp->machine_serial_num[0], "unknown-machine-uuid", sizeof(jibp->machine_serial_num));
+ }
+ }
-unsigned long BestBlockSizeFit(unsigned long allocationBlockSize,
- unsigned long blockSizeLimit,
- unsigned long baseMultiple) {
- /*
- Compute the optimal (largest) block size (no larger than allocationBlockSize) that is less than the
- specified limit but still an even multiple of the baseMultiple.
- */
- int baseBlockCount, blockCount;
- unsigned long trialBlockSize;
+ jib_offset = 0;
+ write_jibp = 1;
+ if (need_init) {
+ jib_flags |= kJIJournalNeedInitMask;
+ }
+ }
- if (allocationBlockSize % baseMultiple != 0) {
- /*
- Whoops: the allocation blocks aren't even multiples of the specified base:
- no amount of dividing them into even parts will be a multiple, either then!
- */
- return 512; /* Hope for the best */
- };
+ // save this off for the hack-y check in hfs_remove()
+ hfsmp->jnl_start = jib_offset / SWAP_BE32(vhp->blockSize);
+ hfsmp->jnl_size = jib_size;
+
+ if ((hfsmp->hfs_flags & HFS_READ_ONLY) && (vfs_flags(hfsmp->hfs_mp) & MNT_ROOTFS) == 0) {
+ // if the file system is read-only, check if the journal is empty.
+ // if it is, then we can allow the mount. otherwise we have to
+ // return failure.
+ retval = journal_is_clean(hfsmp->jvp,
+ jib_offset + embeddedOffset,
+ jib_size,
+ devvp,
+ hfsmp->hfs_logical_block_size);
+
+ hfsmp->jnl = NULL;
+
+ buf_brelse(jinfo_bp);
+
+ if (retval) {
+ const char *name = vnode_getname(devvp);
+ printf("hfs: early journal init: volume on %s is read-only and journal is dirty. Can not mount volume.\n",
+ name ? name : "");
+ if (name)
+ vnode_putname(name);
+ }
- /* Try the obvious winner first, to prevent 12K allocation blocks, for instance,
- from being handled as two 6K logical blocks instead of 3 4K logical blocks.
- Even though the former (the result of the loop below) is the larger allocation
- block size, the latter is more efficient: */
- if (allocationBlockSize % PAGE_SIZE == 0) return PAGE_SIZE;
+ return retval;
+ }
- /* No clear winner exists: pick the largest even fraction <= MAXBSIZE: */
- baseBlockCount = allocationBlockSize / baseMultiple; /* Now guaranteed to be an even multiple */
+ if (jib_flags & kJIJournalNeedInitMask) {
+ printf("hfs: Initializing the journal (joffset 0x%llx sz 0x%llx)...\n",
+ jib_offset + embeddedOffset, jib_size);
+ hfsmp->jnl = journal_create(hfsmp->jvp,
+ jib_offset + embeddedOffset,
+ jib_size,
+ devvp,
+ blksize,
+ arg_flags,
+ arg_tbufsz,
+ hfs_sync_metadata, hfsmp->hfs_mp);
+ if (hfsmp->jnl)
+ journal_trim_set_callback(hfsmp->jnl, hfs_trim_callback, hfsmp);
+
+ // no need to start a transaction here... if this were to fail
+ // we'd just re-init it on the next mount.
+ jib_flags &= ~kJIJournalNeedInitMask;
+ jibp->flags = SWAP_BE32(jib_flags);
+ buf_bwrite(jinfo_bp);
+ jinfo_bp = NULL;
+ jibp = NULL;
+ } else {
+ //printf("hfs: Opening the journal (joffset 0x%llx sz 0x%llx vhp_blksize %d)...\n",
+ // jib_offset + embeddedOffset,
+ // jib_size, SWAP_BE32(vhp->blockSize));
+
+ hfsmp->jnl = journal_open(hfsmp->jvp,
+ jib_offset + embeddedOffset,
+ jib_size,
+ devvp,
+ blksize,
+ arg_flags,
+ arg_tbufsz,
+ hfs_sync_metadata, hfsmp->hfs_mp);
+ if (hfsmp->jnl)
+ journal_trim_set_callback(hfsmp->jnl, hfs_trim_callback, hfsmp);
+
+ if (write_jibp) {
+ buf_bwrite(jinfo_bp);
+ } else {
+ buf_brelse(jinfo_bp);
+ }
+ jinfo_bp = NULL;
+ jibp = NULL;
+
+ if (hfsmp->jnl && mdbp) {
+ // reload the mdb because it could have changed
+ // if the journal had to be replayed.
+ if (mdb_offset == 0) {
+ mdb_offset = (daddr64_t)((embeddedOffset / blksize) + HFS_PRI_SECTOR(blksize));
+ }
+ bp = NULL;
+ retval = (int)buf_meta_bread(devvp,
+ HFS_PHYSBLK_ROUNDDOWN(mdb_offset, hfsmp->hfs_log_per_phys),
+ hfsmp->hfs_physical_block_size, cred, &bp);
+ if (retval) {
+ if (bp) {
+ buf_brelse(bp);
+ }
+ printf("hfs: failed to reload the mdb after opening the journal (retval %d)!\n",
+ retval);
+ return retval;
+ }
+ bcopy((char *)buf_dataptr(bp) + HFS_PRI_OFFSET(hfsmp->hfs_physical_block_size), mdbp, 512);
+ buf_brelse(bp);
+ bp = NULL;
+ }
+ }
- for (blockCount = baseBlockCount; blockCount > 0; --blockCount) {
- trialBlockSize = blockCount * baseMultiple;
- if (allocationBlockSize % trialBlockSize == 0) { /* An even multiple? */
- if ((trialBlockSize <= blockSizeLimit) &&
- (trialBlockSize % baseMultiple == 0)) {
- return trialBlockSize;
- };
- };
- };
- /* Note: we should never get here, since blockCount = 1 should always work,
- but this is nice and safe and makes the compiler happy, too ... */
- return 512;
+ //printf("journal @ 0x%x\n", hfsmp->jnl);
+
+ // if we expected the journal to be there and we couldn't
+ // create it or open it then we have to bail out.
+ if (hfsmp->jnl == NULL) {
+ printf("hfs: early jnl init: failed to open/create the journal (retval %d).\n", retval);
+ return EINVAL;
+ }
+
+ return 0;
}
-/*
- * To make the HFS Plus filesystem follow UFS unlink semantics, a remove
- * of an active vnode is translated to a move/rename so the file appears
- * deleted. The destination folder for these move/renames is setup here
- * and a reference to it is place in hfsmp->hfs_private_metadata_dir.
- */
-u_long
-FindMetaDataDirectory(ExtendedVCB *vcb)
+//
+// This function will go and re-locate the .journal_info_block and
+// the .journal files in case they moved (which can happen if you
+// run Norton SpeedDisk). If we fail to find either file we just
+// disable journaling for this volume and return. We turn off the
+// journaling bit in the vcb and assume it will get written to disk
+// later (if it doesn't on the next mount we'd do the same thing
+// again which is harmless). If we disable journaling we don't
+// return an error so that the volume is still mountable.
+//
+// If the info we find for the .journal_info_block and .journal files
+// isn't what we had stored, we re-set our cached info and proceed
+// with opening the journal normally.
+//
+static int
+hfs_late_journal_init(struct hfsmount *hfsmp, HFSPlusVolumeHeader *vhp, void *_args)
{
- char namep[32];
- hfsCatalogInfo catInfo;
- HFSCatalogNodeID dirID;
- u_int32_t metadata_createdate;
- int retval;
+ JournalInfoBlock *jibp;
+ struct buf *jinfo_bp;
+ int sectors_per_fsblock, arg_flags=0, arg_tbufsz=0;
+ int retval, write_jibp = 0, recreate_journal = 0;
+ struct vnode *devvp;
+ struct cat_attr jib_attr, jattr;
+ struct cat_fork jib_fork, jfork;
+ ExtendedVCB *vcb;
+ u_int32_t fid;
+ struct hfs_mount_args *args = _args;
+ u_int32_t jib_flags;
+ u_int64_t jib_offset;
+ u_int64_t jib_size;
+
+ devvp = hfsmp->hfs_devvp;
+ vcb = HFSTOVCB(hfsmp);
- if (vcb->vcbSigWord != kHFSPlusSigWord)
- return (0);
+ if (args != NULL && (args->flags & HFSFSMNT_EXTENDED_ARGS)) {
+ if (args->journal_disable) {
+ return 0;
+ }
- dirID = 0;
- metadata_createdate = 0;
- strncpy(namep, HFSPLUS_PRIVATE_DIR, sizeof(namep));
- INIT_CATALOGDATA(&catInfo.nodeData, kCatNameNoCopyName);
- catInfo.hint = kNoHint;
-
- /* lock catalog b-tree */
- retval = hfs_metafilelocking(VCBTOHFS(vcb), kHFSCatalogFileID, LK_SHARED, current_proc());
- if (retval) goto Err_Exit;
-
- if (hfs_getcatalog(vcb, kRootDirID, namep, -1, &catInfo) == 0) {
- dirID = catInfo.nodeData.cnd_nodeID;
- metadata_createdate = catInfo.nodeData.cnd_createDate;
- } else if (VCBTOHFS(vcb)->hfs_fs_ronly == 0) {
- if (CreateCatalogNode(vcb, kRootDirID, namep, kCatalogFolderNode, &dirID, &catInfo.hint, 0) == 0) {
- catInfo.hint = kNoHint;
- if (hfs_getcatalog(vcb, kRootDirID, namep, -1, &catInfo) == 0) {
-
- /* create date is later used for validation */
- catInfo.nodeData.cnd_createDate = vcb->vcbCrDate;
- metadata_createdate = catInfo.nodeData.cnd_createDate;
-
- /* directory with no permissions owned by root */
- catInfo.nodeData.cnd_mode = IFDIR;
- catInfo.nodeData.cnd_adminFlags = (SF_IMMUTABLE >> 16);
-
- /* hidden and off the desktop view */
- ((struct DInfo *)(&catInfo.nodeData.cnd_finderInfo))->frLocation.v = SWAP_BE16 (22460);
- ((struct DInfo *)(&catInfo.nodeData.cnd_finderInfo))->frLocation.h = SWAP_BE16 (22460);
- ((struct DInfo *)(&catInfo.nodeData.cnd_finderInfo))->frFlags |= SWAP_BE16 (kIsInvisible + kNameLocked);
-
- (void) UpdateCatalogNode(vcb, kRootDirID, namep, catInfo.hint, &catInfo.nodeData);
- }
+ arg_flags = args->journal_flags;
+ arg_tbufsz = args->journal_tbuffer_size;
+ }
+
+ fid = GetFileInfo(vcb, kRootDirID, ".journal_info_block", &jib_attr, &jib_fork);
+ if (fid == 0 || jib_fork.cf_extents[0].startBlock == 0 || jib_fork.cf_size == 0) {
+ printf("hfs: can't find the .journal_info_block! disabling journaling (start: %d).\n",
+ jib_fork.cf_extents[0].startBlock);
+ vcb->vcbAtrb &= ~kHFSVolumeJournaledMask;
+ return 0;
+ }
+ hfsmp->hfs_jnlinfoblkid = fid;
+
+ // make sure the journal_info_block begins where we think it should.
+ if (SWAP_BE32(vhp->journalInfoBlock) != jib_fork.cf_extents[0].startBlock) {
+ printf("hfs: The journal_info_block moved (was: %d; is: %d). Fixing up\n",
+ SWAP_BE32(vhp->journalInfoBlock), jib_fork.cf_extents[0].startBlock);
+
+ vcb->vcbJinfoBlock = jib_fork.cf_extents[0].startBlock;
+ vhp->journalInfoBlock = SWAP_BE32(jib_fork.cf_extents[0].startBlock);
+ recreate_journal = 1;
+ }
+
+
+ sectors_per_fsblock = SWAP_BE32(vhp->blockSize) / hfsmp->hfs_logical_block_size;
+ jinfo_bp = NULL;
+ retval = (int)buf_meta_bread(devvp,
+ (vcb->hfsPlusIOPosOffset / hfsmp->hfs_logical_block_size +
+ ((u_int64_t)SWAP_BE32(vhp->journalInfoBlock)*sectors_per_fsblock)),
+ hfsmp->hfs_physical_block_size, NOCRED, &jinfo_bp);
+ if (retval) {
+ if (jinfo_bp) {
+ buf_brelse(jinfo_bp);
}
+ printf("hfs: can't read journal info block. disabling journaling.\n");
+ vcb->vcbAtrb &= ~kHFSVolumeJournaledMask;
+ return 0;
}
- /* unlock catalog b-tree */
- (void) hfs_metafilelocking(VCBTOHFS(vcb), kHFSCatalogFileID, LK_RELEASE, current_proc());
-
- VCBTOHFS(vcb)->hfs_metadata_createdate = metadata_createdate;
-Err_Exit:
- CLEAN_CATALOGDATA(&catInfo.nodeData);
+ jibp = (JournalInfoBlock *)buf_dataptr(jinfo_bp);
+ jib_flags = SWAP_BE32(jibp->flags);
+ jib_offset = SWAP_BE64(jibp->offset);
+ jib_size = SWAP_BE64(jibp->size);
+
+ fid = GetFileInfo(vcb, kRootDirID, ".journal", &jattr, &jfork);
+ if (fid == 0 || jfork.cf_extents[0].startBlock == 0 || jfork.cf_size == 0) {
+ printf("hfs: can't find the journal file! disabling journaling (start: %d)\n",
+ jfork.cf_extents[0].startBlock);
+ buf_brelse(jinfo_bp);
+ vcb->vcbAtrb &= ~kHFSVolumeJournaledMask;
+ return 0;
+ }
+ hfsmp->hfs_jnlfileid = fid;
- return dirID;
-}
+ // make sure the journal file begins where we think it should.
+ if ((jib_flags & kJIJournalInFSMask) && (jib_offset / (u_int64_t)vcb->blockSize) != jfork.cf_extents[0].startBlock) {
+ printf("hfs: The journal file moved (was: %lld; is: %d). Fixing up\n",
+ (jib_offset / (u_int64_t)vcb->blockSize), jfork.cf_extents[0].startBlock);
+ jib_offset = (u_int64_t)jfork.cf_extents[0].startBlock * (u_int64_t)vcb->blockSize;
+ write_jibp = 1;
+ recreate_journal = 1;
+ }
-static void
-RemovedMetaDataDirectory(ExtendedVCB *vcb)
-{
- char name[32];
- hfsCatalogInfo catInfo;
- int retval;
+ // check the size of the journal file.
+ if (jib_size != (u_int64_t)jfork.cf_extents[0].blockCount*vcb->blockSize) {
+ printf("hfs: The journal file changed size! (was %lld; is %lld). Fixing up.\n",
+ jib_size, (u_int64_t)jfork.cf_extents[0].blockCount*vcb->blockSize);
+
+ jib_size = (u_int64_t)jfork.cf_extents[0].blockCount * vcb->blockSize;
+ write_jibp = 1;
+ recreate_journal = 1;
+ }
- strncpy(name, HFSPLUS_PRIVATE_DIR, sizeof(name));
- INIT_CATALOGDATA(&catInfo.nodeData, kCatNameNoCopyName);
+ if (jib_flags & kJIJournalInFSMask) {
+ hfsmp->jvp = hfsmp->hfs_devvp;
+ jib_offset += (off_t)vcb->hfsPlusIOPosOffset;
+ } else {
+ const char *dev_name;
+ int need_init = 0;
+
+ dev_name = vnode_name(devvp);
+ if (dev_name == NULL) {
+ dev_name = "unknown-dev";
+ }
+
+ // since the journal is empty, just use any available external journal
+ *((char *)&jibp->ext_jnl_uuid[0]) = '\0';
+
+ // this fills in the uuid of the device we actually get
+ hfsmp->jvp = open_journal_dev(dev_name,
+ !(jib_flags & kJIJournalNeedInitMask),
+ (char *)&jibp->ext_jnl_uuid[0],
+ (char *)&jibp->machine_serial_num[0],
+ jib_size,
+ hfsmp->hfs_logical_block_size,
+ &need_init);
+ if (hfsmp->jvp == NULL) {
+ buf_brelse(jinfo_bp);
+ return EROFS;
+ } else {
+ if (IOBSDGetPlatformSerialNumber(&jibp->machine_serial_num[0], sizeof(jibp->machine_serial_num)) != KERN_SUCCESS) {
+ strlcpy(&jibp->machine_serial_num[0], "unknown-machine-serial-num", sizeof(jibp->machine_serial_num));
+ }
+ }
+ jib_offset = 0;
+ recreate_journal = 1;
+ write_jibp = 1;
+ if (need_init) {
+ jib_flags |= kJIJournalNeedInitMask;
+ }
+ }
- /* lock catalog b-tree */
- retval = hfs_metafilelocking(VCBTOHFS(vcb), kHFSCatalogFileID, LK_SHARED, current_proc());
- if (retval) goto Err_Exit;
+ // save this off for the hack-y check in hfs_remove()
+ hfsmp->jnl_start = jib_offset / SWAP_BE32(vhp->blockSize);
+ hfsmp->jnl_size = jib_size;
+
+ if ((hfsmp->hfs_flags & HFS_READ_ONLY) && (vfs_flags(hfsmp->hfs_mp) & MNT_ROOTFS) == 0) {
+ // if the file system is read-only, check if the journal is empty.
+ // if it is, then we can allow the mount. otherwise we have to
+ // return failure.
+ retval = journal_is_clean(hfsmp->jvp,
+ jib_offset,
+ jib_size,
+ devvp,
+ hfsmp->hfs_logical_block_size);
+
+ hfsmp->jnl = NULL;
+
+ buf_brelse(jinfo_bp);
+
+ if (retval) {
+ const char *name = vnode_getname(devvp);
+ printf("hfs: late journal init: volume on %s is read-only and journal is dirty. Can not mount volume.\n",
+ name ? name : "");
+ if (name)
+ vnode_putname(name);
+ }
+
+ return retval;
+ }
- /* If the HFSPLUSMETADATAFOLDER exists then delete it. */
- retval = GetCatalogNode(vcb, kRootDirID, name, strlen(name), kNoHint,
- &catInfo.nodeData, &catInfo.hint);
- if (retval == 0 && (catInfo.nodeData.cnd_type == kCatalogFolderNode)) {
- (void) DeleteCatalogNode(vcb, kRootDirID, name, catInfo.hint);
- printf("hfs_mount: removed \"%s\" from hfs volume \"%s\"\n", name, vcb->vcbVN);
+ if ((jib_flags & kJIJournalNeedInitMask) || recreate_journal) {
+ printf("hfs: Initializing the journal (joffset 0x%llx sz 0x%llx)...\n",
+ jib_offset, jib_size);
+ hfsmp->jnl = journal_create(hfsmp->jvp,
+ jib_offset,
+ jib_size,
+ devvp,
+ hfsmp->hfs_logical_block_size,
+ arg_flags,
+ arg_tbufsz,
+ hfs_sync_metadata, hfsmp->hfs_mp);
+ if (hfsmp->jnl)
+ journal_trim_set_callback(hfsmp->jnl, hfs_trim_callback, hfsmp);
+
+ // no need to start a transaction here... if this were to fail
+ // we'd just re-init it on the next mount.
+ jib_flags &= ~kJIJournalNeedInitMask;
+ write_jibp = 1;
+
+ } else {
+ //
+ // if we weren't the last person to mount this volume
+ // then we need to throw away the journal because it
+ // is likely that someone else mucked with the disk.
+ // if the journal is empty this is no big deal. if the
+ // disk is dirty this prevents us from replaying the
+ // journal over top of changes that someone else made.
+ //
+ arg_flags |= JOURNAL_RESET;
+
+ //printf("hfs: Opening the journal (joffset 0x%llx sz 0x%llx vhp_blksize %d)...\n",
+ // jib_offset,
+ // jib_size, SWAP_BE32(vhp->blockSize));
+
+ hfsmp->jnl = journal_open(hfsmp->jvp,
+ jib_offset,
+ jib_size,
+ devvp,
+ hfsmp->hfs_logical_block_size,
+ arg_flags,
+ arg_tbufsz,
+ hfs_sync_metadata, hfsmp->hfs_mp);
+ if (hfsmp->jnl)
+ journal_trim_set_callback(hfsmp->jnl, hfs_trim_callback, hfsmp);
}
+
+
+ if (write_jibp) {
+ jibp->flags = SWAP_BE32(jib_flags);
+ jibp->offset = SWAP_BE64(jib_offset);
+ jibp->size = SWAP_BE64(jib_size);
+
+ buf_bwrite(jinfo_bp);
+ } else {
+ buf_brelse(jinfo_bp);
+ }
+ jinfo_bp = NULL;
+ jibp = NULL;
- /* unlock catalog b-tree */
- (void) hfs_metafilelocking(VCBTOHFS(vcb), kHFSCatalogFileID, LK_RELEASE, current_proc());
+ //printf("hfs: journal @ 0x%x\n", hfsmp->jnl);
+
+ // if we expected the journal to be there and we couldn't
+ // create it or open it then we have to bail out.
+ if (hfsmp->jnl == NULL) {
+ printf("hfs: late jnl init: failed to open/create the journal (retval %d).\n", retval);
+ return EINVAL;
+ }
-Err_Exit:
- CLEAN_CATALOGDATA(&catInfo.nodeData);
+ return 0;
}
/*
- * This will return the correct logical block size for a given vnode.
- * For most files, it is the allocation block size, for meta data like
- * BTrees, this is kept as part of the BTree private nodeSize
+ * Calculate the allocation zone for metadata.
+ *
+ * This zone includes the following:
+ * Allocation Bitmap file
+ * Overflow Extents file
+ * Journal file
+ * Quota files
+ * Clustered Hot files
+ * Catalog file
+ *
+ * METADATA ALLOCATION ZONE
+ * ____________________________________________________________________________
+ * | | | | | | |
+ * | BM | JF | OEF | CATALOG |---> | HOT FILES |
+ * |____|____|_____|_______________|______________________________|___________|
+ *
+ * <------------------------------- N * 128 MB ------------------------------->
+ *
*/
-u_int32_t
-GetLogicalBlockSize(struct vnode *vp)
+#define GIGABYTE (u_int64_t)(1024*1024*1024)
+
+#define OVERFLOW_DEFAULT_SIZE (4*1024*1024)
+#define OVERFLOW_MAXIMUM_SIZE (128*1024*1024)
+#define JOURNAL_DEFAULT_SIZE (8*1024*1024)
+#define JOURNAL_MAXIMUM_SIZE (512*1024*1024)
+#define HOTBAND_MINIMUM_SIZE (10*1024*1024)
+#define HOTBAND_MAXIMUM_SIZE (512*1024*1024)
+
+/* Initialize the metadata zone.
+ *
+ * If the size of the volume is less than the minimum size for
+ * metadata zone, metadata zone is disabled.
+ *
+ * If disable is true, disable metadata zone unconditionally.
+ */
+void
+hfs_metadatazone_init(struct hfsmount *hfsmp, int disable)
{
-u_int32_t logBlockSize;
+ ExtendedVCB *vcb;
+ u_int64_t fs_size;
+ u_int64_t zonesize;
+ u_int64_t temp;
+ u_int64_t filesize;
+ u_int32_t blk;
+ int items, really_do_it=1;
+
+ vcb = HFSTOVCB(hfsmp);
+ fs_size = (u_int64_t)vcb->blockSize * (u_int64_t)vcb->allocLimit;
+
+ /*
+ * For volumes less than 10 GB, don't bother.
+ */
+ if (fs_size < ((u_int64_t)10 * GIGABYTE)) {
+ really_do_it = 0;
+ }
- DBG_ASSERT(vp != NULL);
+ /*
+ * Skip non-journaled volumes as well.
+ */
+ if (hfsmp->jnl == NULL) {
+ really_do_it = 0;
+ }
- /* start with default */
- logBlockSize = VTOHFS(vp)->hfs_logBlockSize;
+ /* If caller wants to disable metadata zone, do it */
+ if (disable == true) {
+ really_do_it = 0;
+ }
- if (vp->v_flag & VSYSTEM) {
- if (VTOH(vp)->fcbBTCBPtr != NULL) {
- BTreeInfoRec bTreeInfo;
+ /*
+ * Start with space for the boot blocks and Volume Header.
+ * 1536 = byte offset from start of volume to end of volume header:
+ * 1024 bytes is the offset from the start of the volume to the
+ * start of the volume header (defined by the volume format)
+ * + 512 bytes (the size of the volume header).
+ */
+ zonesize = roundup(1536, hfsmp->blockSize);
- /*
- * We do not lock the BTrees, because if we are getting block..then the tree
- * should be locked in the first place.
- * We just want the nodeSize wich will NEVER change..so even if the world
- * is changing..the nodeSize should remain the same. Which argues why lock
- * it in the first place??
- */
-
- (void) BTGetInformation (VTOFCB(vp), kBTreeInfoVersion, &bTreeInfo);
-
- logBlockSize = bTreeInfo.nodeSize;
+ /*
+ * Add the on-disk size of allocation bitmap.
+ */
+ zonesize += hfsmp->hfs_allocation_cp->c_datafork->ff_blocks * hfsmp->blockSize;
+
+ /*
+ * Add space for the Journal Info Block and Journal (if they're in
+ * this file system).
+ */
+ if (hfsmp->jnl && hfsmp->jvp == hfsmp->hfs_devvp) {
+ zonesize += hfsmp->blockSize + hfsmp->jnl_size;
+ }
+
+ /*
+ * Add the existing size of the Extents Overflow B-tree.
+ * (It rarely grows, so don't bother reserving additional room for it.)
+ */
+ zonesize += hfsmp->hfs_extents_cp->c_datafork->ff_blocks * hfsmp->blockSize;
+
+ /*
+ * If there is an Attributes B-tree, leave room for 11 clumps worth.
+ * newfs_hfs allocates one clump, and leaves a gap of 10 clumps.
+ * When installing a full OS install onto a 20GB volume, we use
+ * 7 to 8 clumps worth of space (depending on packages), so that leaves
+ * us with another 3 or 4 clumps worth before we need another extent.
+ */
+ if (hfsmp->hfs_attribute_cp) {
+ zonesize += 11 * hfsmp->hfs_attribute_cp->c_datafork->ff_clumpsize;
+ }
+
+ /*
+ * Leave room for 11 clumps of the Catalog B-tree.
+ * Again, newfs_hfs allocates one clump plus a gap of 10 clumps.
+ * When installing a full OS install onto a 20GB volume, we use
+ * 7 to 8 clumps worth of space (depending on packages), so that leaves
+ * us with another 3 or 4 clumps worth before we need another extent.
+ */
+ zonesize += 11 * hfsmp->hfs_catalog_cp->c_datafork->ff_clumpsize;
+
+ /*
+ * Add space for hot file region.
+ *
+ * ...for now, use 5 MB per 1 GB (0.5 %)
+ */
+ filesize = (fs_size / 1024) * 5;
+ if (filesize > HOTBAND_MAXIMUM_SIZE)
+ filesize = HOTBAND_MAXIMUM_SIZE;
+ else if (filesize < HOTBAND_MINIMUM_SIZE)
+ filesize = HOTBAND_MINIMUM_SIZE;
+ /*
+ * Calculate user quota file requirements.
+ */
+ if (hfsmp->hfs_flags & HFS_QUOTAS) {
+ items = QF_USERS_PER_GB * (fs_size / GIGABYTE);
+ if (items < QF_MIN_USERS)
+ items = QF_MIN_USERS;
+ else if (items > QF_MAX_USERS)
+ items = QF_MAX_USERS;
+ if (!powerof2(items)) {
+ int x = items;
+ items = 4;
+ while (x>>1 != 1) {
+ x = x >> 1;
+ items = items << 1;
+ }
+ }
+ filesize += (items + 1) * sizeof(struct dqblk);
+ /*
+ * Calculate group quota file requirements.
+ *
+ */
+ items = QF_GROUPS_PER_GB * (fs_size / GIGABYTE);
+ if (items < QF_MIN_GROUPS)
+ items = QF_MIN_GROUPS;
+ else if (items > QF_MAX_GROUPS)
+ items = QF_MAX_GROUPS;
+ if (!powerof2(items)) {
+ int x = items;
+ items = 4;
+ while (x>>1 != 1) {
+ x = x >> 1;
+ items = items << 1;
+ }
+ }
+ filesize += (items + 1) * sizeof(struct dqblk);
+ }
+ zonesize += filesize;
- } else if (H_FILEID(VTOH(vp)) == kHFSAllocationFileID) {
- logBlockSize = VTOVCB(vp)->vcbVBMIOSize;
+ /*
+ * Round up entire zone to a bitmap block's worth.
+ * The extra space goes to the catalog file and hot file area.
+ */
+ temp = zonesize;
+ zonesize = roundup(zonesize, (u_int64_t)vcb->vcbVBMIOSize * 8 * vcb->blockSize);
+ hfsmp->hfs_min_alloc_start = zonesize / vcb->blockSize;
+ /*
+ * If doing the round up for hfs_min_alloc_start would push us past
+ * allocLimit, then just reset it back to 0. Though using a value
+ * bigger than allocLimit would not cause damage in the block allocator
+ * code, this value could get stored in the volume header and make it out
+ * to disk, making the volume header technically corrupt.
+ */
+ if (hfsmp->hfs_min_alloc_start >= hfsmp->allocLimit) {
+ hfsmp->hfs_min_alloc_start = 0;
+ }
+
+ if (really_do_it == 0) {
+ /* If metadata zone needs to be disabled because the
+ * volume was truncated, clear the bit and zero out
+ * the values that are no longer needed.
+ */
+ if (hfsmp->hfs_flags & HFS_METADATA_ZONE) {
+ /* Disable metadata zone */
+ hfsmp->hfs_flags &= ~HFS_METADATA_ZONE;
+
+ /* Zero out mount point values that are not required */
+ hfsmp->hfs_catalog_maxblks = 0;
+ hfsmp->hfs_hotfile_maxblks = 0;
+ hfsmp->hfs_hotfile_start = 0;
+ hfsmp->hfs_hotfile_end = 0;
+ hfsmp->hfs_hotfile_freeblks = 0;
+ hfsmp->hfs_metazone_start = 0;
+ hfsmp->hfs_metazone_end = 0;
}
+
+ return;
}
+
+ temp = zonesize - temp; /* temp has extra space */
+ filesize += temp / 3;
+ hfsmp->hfs_catalog_maxblks += (temp - (temp / 3)) / vcb->blockSize;
- DBG_ASSERT(logBlockSize > 0);
+ hfsmp->hfs_hotfile_maxblks = filesize / vcb->blockSize;
+
+ /* Convert to allocation blocks. */
+ blk = zonesize / vcb->blockSize;
+
+ /* The default metadata zone location is at the start of volume. */
+ hfsmp->hfs_metazone_start = 1;
+ hfsmp->hfs_metazone_end = blk - 1;
- return logBlockSize;
+ /* The default hotfile area is at the end of the zone. */
+ hfsmp->hfs_hotfile_start = blk - (filesize / vcb->blockSize);
+ hfsmp->hfs_hotfile_end = hfsmp->hfs_metazone_end;
+ hfsmp->hfs_hotfile_freeblks = hfs_hotfile_freeblocks(hfsmp);
+#if 0
+ printf("hfs: metadata zone is %d to %d\n", hfsmp->hfs_metazone_start, hfsmp->hfs_metazone_end);
+ printf("hfs: hot file band is %d to %d\n", hfsmp->hfs_hotfile_start, hfsmp->hfs_hotfile_end);
+ printf("hfs: hot file band free blocks = %d\n", hfsmp->hfs_hotfile_freeblks);
+#endif
+ hfsmp->hfs_flags |= HFS_METADATA_ZONE;
+}
+
+
+static u_int32_t
+hfs_hotfile_freeblocks(struct hfsmount *hfsmp)
+{
+ ExtendedVCB *vcb = HFSTOVCB(hfsmp);
+ int lockflags;
+ int freeblocks;
+
+ lockflags = hfs_systemfile_lock(hfsmp, SFL_BITMAP, HFS_EXCLUSIVE_LOCK);
+ freeblocks = MetaZoneFreeBlocks(vcb);
+ hfs_systemfile_unlock(hfsmp, lockflags);
+
+ /* Minus Extents overflow file reserve. */
+ freeblocks -=
+ hfsmp->hfs_overflow_maxblks - VTOF(hfsmp->hfs_extents_vp)->ff_blocks;
+ /* Minus catalog file reserve. */
+ freeblocks -=
+ hfsmp->hfs_catalog_maxblks - VTOF(hfsmp->hfs_catalog_vp)->ff_blocks;
+ if (freeblocks < 0)
+ freeblocks = 0;
+
+ return MIN(freeblocks, hfsmp->hfs_hotfile_maxblks);
}
/*
- * Map HFS Common errors (negative) to BSD error codes (positive).
- * Positive errors (ie BSD errors) are passed through unchanged.
+ * Determine if a file is a "virtual" metadata file.
+ * This includes journal and quota files.
*/
-short MacToVFSError(OSErr err)
+int
+hfs_virtualmetafile(struct cnode *cp)
{
- if (err >= 0) {
- if (err > 0) {
- DBG_ERR(("MacToVFSError: passing error #%d unchanged...\n", err));
- };
- return err;
- };
+ const char * filename;
- if (err != 0) {
- DBG_ERR(("MacToVFSError: mapping error code %d...\n", err));
- };
-
- switch (err) {
- case dirFulErr: /* -33 */
- case dskFulErr: /* -34 */
- case btNoSpaceAvail: /* -32733 */
- case fxOvFlErr: /* -32750 */
- return ENOSPC; /* +28 */
-
- case btBadNode: /* -32731 */
- case ioErr: /* -36 */
- return EIO; /* +5 */
-
- case mFulErr: /* -41 */
- case memFullErr: /* -108 */
- return ENOMEM; /* +12 */
-
- case tmfoErr: /* -42 */
- /* Consider EMFILE (Too many open files, 24)? */
- return ENFILE; /* +23 */
-
- case nsvErr: /* -35 */
- case fnfErr: /* -43 */
- case dirNFErr: /* -120 */
- case fidNotFound: /* -1300 */
- return ENOENT; /* +2 */
-
- case wPrErr: /* -44 */
- case vLckdErr: /* -46 */
- case fsDSIntErr: /* -127 */
- return EROFS; /* +30 */
-
- case opWrErr: /* -49 */
- case fLckdErr: /* -45 */
- return EACCES; /* +13 */
-
- case permErr: /* -54 */
- case wrPermErr: /* -61 */
- return EPERM; /* +1 */
-
- case fBsyErr: /* -47 */
- return EBUSY; /* +16 */
-
- case dupFNErr: /* -48 */
- case fidExists: /* -1301 */
- case cmExists: /* -32718 */
- case btExists: /* -32734 */
- return EEXIST; /* +17 */
-
- case rfNumErr: /* -51 */
- return EBADF; /* +9 */
-
- case notAFileErr: /* -1302 */
- return EISDIR; /* +21 */
-
- case cmNotFound: /* -32719 */
- case btNotFound: /* -32735 */
- return ENOENT; /* 28 */
-
- case cmNotEmpty: /* -32717 */
- return ENOTEMPTY; /* 66 */
-
- case cmFThdDirErr: /* -32714 */
- return EISDIR; /* 21 */
-
- case fxRangeErr: /* -32751 */
- return EIO; /* 5 */
-
- case bdNamErr: /* -37 */
- return ENAMETOOLONG; /* 63 */
-
- case fnOpnErr: /* -38 */
- case eofErr: /* -39 */
- case posErr: /* -40 */
- case paramErr: /* -50 */
- case badMDBErr: /* -60 */
- case badMovErr: /* -122 */
- case sameFileErr: /* -1306 */
- case badFidErr: /* -1307 */
- case fileBoundsErr: /* -1309 */
- return EINVAL; /* +22 */
-
- default:
- DBG_UTILS(("Unmapped MacOS error: %d\n", err));
- return EIO; /* +5 */
- }
-}
+ if (cp->c_parentcnid != kHFSRootFolderID)
+ return (0);
-/*
- * All of our debugging functions
- */
+ filename = (const char *)cp->c_desc.cd_nameptr;
+ if (filename == NULL)
+ return (0);
-#if HFS_DIAGNOSTIC
+ if ((strncmp(filename, ".journal", sizeof(".journal")) == 0) ||
+ (strncmp(filename, ".journal_info_block", sizeof(".journal_info_block")) == 0) ||
+ (strncmp(filename, ".quota.user", sizeof(".quota.user")) == 0) ||
+ (strncmp(filename, ".quota.group", sizeof(".quota.group")) == 0) ||
+ (strncmp(filename, ".hotfiles.btree", sizeof(".hotfiles.btree")) == 0))
+ return (1);
-void debug_vn_status (char* introStr, struct vnode *vn)
-{
- DBG_VOP(("%s:\t",introStr));
- if (vn != NULL)
- {
- if (vn->v_tag != VT_HFS)
- {
- DBG_VOP(("NON-HFS VNODE Ox%08lX\n", (unsigned long)vn));
- }
- else if(vn->v_tag==VT_HFS && (vn->v_data==NULL || VTOH((vn))->h_valid != HFS_VNODE_MAGIC))
- {
- DBG_VOP(("BAD VNODE PRIVATE DATA!!!!\n"));
- }
- else
- {
- DBG_VOP(("r: %d & ", vn->v_usecount));
- if (lockstatus(&VTOH(vn)->h_lock))
- {
- DBG_VOP_CONT(("is L\n"));
- }
- else
- {
- DBG_VOP_CONT(("is U\n"));
- }
- }
- }
- else
- {
- DBG_VOP(("vnode is NULL\n"));
- };
+ return (0);
}
-void debug_vn_print (char* introStr, struct vnode *vn)
-{
-// DBG_FUNC_NAME("DBG_VN_PRINT");
- DBG_ASSERT (vn != NULL);
- DBG_VFS(("%s: ",introStr));
- DBG_VFS_CONT(("vnode: 0x%x is a ", (uint)vn));
- switch (vn->v_tag)
- {
- case VT_UFS:
- DBG_VFS_CONT(("%s","UFS"));
- break;
- case VT_HFS:
- DBG_VFS_CONT(("%s","HFS"));
- break;
- default:
- DBG_VFS_CONT(("%s","UNKNOWN"));
- break;
- }
-
- DBG_VFS_CONT((" vnode\n"));
- if (vn->v_tag==VT_HFS)
- {
- if (vn->v_data==NULL)
- {
- DBG_VFS(("BAD VNODE PRIVATE DATA!!!!\n"));
- }
- else
- {
- DBG_VFS((" Name: %s Id: %ld ",H_NAME(VTOH(vn)), H_FILEID(VTOH(vn))));
- }
- }
- else
- DBG_VFS((" "));
-
- DBG_VFS_CONT(("Refcount: %d\n", vn->v_usecount));
- if (VOP_ISLOCKED(vn))
- {
- DBG_VFS((" The vnode is locked\n"));
- }
- else
- {
- DBG_VFS((" The vnode is not locked\n"));
- }
-}
-void debug_rename_test_locks (char* introStr,
- struct vnode *fvp,
- struct vnode *fdvp,
- struct vnode *tvp,
- struct vnode *tdvp,
- int fstatus,
- int fdstatus,
- int tstatus,
- int tdstatus
-)
+//
+// Fire off a timed callback to sync the disk if the
+// volume is on ejectable media.
+//
+ __private_extern__
+void
+hfs_sync_ejectable(struct hfsmount *hfsmp)
{
- DBG_VOP(("\t%s: ", introStr));
- if (fvp) {if(lockstatus(&VTOH(fvp)->h_lock)){DBG_VFS_CONT(("L"));} else {DBG_VFS_CONT(("U"));}} else { DBG_VFS_CONT(("X"));};
- if (fdvp) {if(lockstatus(&VTOH(fdvp)->h_lock)){DBG_VFS_CONT(("L"));} else {DBG_VFS_CONT(("U"));}} else { DBG_VFS_CONT(("X"));};
- if (tvp) {if(lockstatus(&VTOH(tvp)->h_lock)){DBG_VFS_CONT(("L"));} else {DBG_VFS_CONT(("U"));}} else { DBG_VFS_CONT(("X"));};
- if (tdvp) {if(lockstatus(&VTOH(tdvp)->h_lock)){DBG_VFS_CONT(("L"));} else {DBG_VFS_CONT(("U"));}} else { DBG_VFS_CONT(("X"));};
- DBG_VFS_CONT(("\n"));
-
- if (fvp) {
- if (lockstatus(&VTOH(fvp)->h_lock)) {
- if (fstatus==VOPDBG_UNLOCKED) {
- DBG_VOP(("\tfvp should be NOT LOCKED and it is\n"));
- }
- } else if (fstatus == VOPDBG_LOCKED) {
- DBG_VOP(("\tfvp should be LOCKED and it isnt\n"));
- }
- }
-
- if (fdvp) {
- if (lockstatus(&VTOH(fdvp)->h_lock)) {
- if (fdstatus==VOPDBG_UNLOCKED) {
- DBG_VOP(("\tfdvp should be NOT LOCKED and it is\n"));
- }
- } else if (fdstatus == VOPDBG_LOCKED) {
- DBG_VOP(("\tfdvp should be LOCKED and it isnt\n"));
- }
- }
+ if (hfsmp->hfs_syncer) {
+ clock_sec_t secs;
+ clock_usec_t usecs;
+ uint64_t now;
+
+ clock_get_calendar_microtime(&secs, &usecs);
+ now = ((uint64_t)secs * 1000000ULL) + (uint64_t)usecs;
+
+ if (hfsmp->hfs_sync_incomplete && hfsmp->hfs_mp->mnt_pending_write_size >= hfsmp->hfs_max_pending_io) {
+ // if we have a sync scheduled but i/o is starting to pile up,
+ // don't call thread_call_enter_delayed() again because that
+ // will defer the sync.
+ return;
+ }
- if (tvp) {
- if (lockstatus(&VTOH(tvp)->h_lock)) {
- if (tstatus==VOPDBG_UNLOCKED) {
- DBG_VOP(("\ttvp should be NOT LOCKED and it is\n"));
- }
- } else if (tstatus == VOPDBG_LOCKED) {
- DBG_VOP(("\ttvp should be LOCKED and it isnt\n"));
- }
- }
+ if (hfsmp->hfs_sync_scheduled == 0) {
+ uint64_t deadline;
- if (tdvp) {
- if (lockstatus(&VTOH(tdvp)->h_lock)) {
- if (tdstatus==VOPDBG_UNLOCKED) {
- DBG_VOP(("\ttdvp should be NOT LOCKED and it is\n"));
- }
- } else if (tdstatus == VOPDBG_LOCKED) {
- DBG_VOP(("\ttdvp should be LOCKED and it isnt\n"));
+ hfsmp->hfs_last_sync_request_time = now;
- }
- }
+ clock_interval_to_deadline(HFS_META_DELAY, HFS_MILLISEC_SCALE, &deadline);
+ /*
+ * Increment hfs_sync_scheduled on the assumption that we're the
+ * first thread to schedule the timer. If some other thread beat
+ * us, then we'll decrement it. If we *were* the first to
+ * schedule the timer, then we need to keep track that the
+ * callback is waiting to complete.
+ */
+ OSIncrementAtomic((volatile SInt32 *)&hfsmp->hfs_sync_scheduled);
+ if (thread_call_enter_delayed(hfsmp->hfs_syncer, deadline))
+ OSDecrementAtomic((volatile SInt32 *)&hfsmp->hfs_sync_scheduled);
+ else
+ OSIncrementAtomic((volatile SInt32 *)&hfsmp->hfs_sync_incomplete);
+ }
+ }
}
-#endif /* HFS_DIAGNOSTIC */
-#if HFS_DIAGNOSTIC
-void debug_check_buffersizes(struct vnode *vp, struct hfsnode *hp, struct buf *bp) {
- DBG_ASSERT(bp->b_validoff == 0);
- DBG_ASSERT(bp->b_dirtyoff == 0);
- DBG_ASSERT((bp->b_bcount == HTOHFS(hp)->hfs_logBlockSize) ||
- ((bp->b_bcount % 512 == 0) &&
- (bp->b_validend > 0) &&
- (bp->b_dirtyend > 0) &&
- (bp->b_bcount < HTOHFS(hp)->hfs_logBlockSize)));
-
- if (bp->b_validend == 0) {
- DBG_ASSERT(bp->b_dirtyend == 0);
- } else {
- DBG_ASSERT(bp->b_validend == bp->b_bcount);
- DBG_ASSERT(bp->b_dirtyend <= bp->b_bcount);
- };
-}
+int
+hfs_start_transaction(struct hfsmount *hfsmp)
+{
+ int ret, unlock_on_err=0;
+ void * thread = current_thread();
+#ifdef HFS_CHECK_LOCK_ORDER
+ /*
+ * You cannot start a transaction while holding a system
+ * file lock. (unless the transaction is nested.)
+ */
+ if (hfsmp->jnl && journal_owner(hfsmp->jnl) != thread) {
+ if (hfsmp->hfs_catalog_cp && hfsmp->hfs_catalog_cp->c_lockowner == thread) {
+ panic("hfs_start_transaction: bad lock order (cat before jnl)\n");
+ }
+ if (hfsmp->hfs_attribute_cp && hfsmp->hfs_attribute_cp->c_lockowner == thread) {
+ panic("hfs_start_transaction: bad lock order (attr before jnl)\n");
+ }
+ if (hfsmp->hfs_extents_cp && hfsmp->hfs_extents_cp->c_lockowner == thread) {
+ panic("hfs_start_transaction: bad lock order (ext before jnl)\n");
+ }
+ }
+#endif /* HFS_CHECK_LOCK_ORDER */
-void debug_check_blocksizes(struct vnode *vp) {
- struct hfsnode *hp = VTOH(vp);
- struct buf *bp;
+ if (hfsmp->jnl == NULL || journal_owner(hfsmp->jnl) != thread) {
+ hfs_lock_global (hfsmp, HFS_SHARED_LOCK);
+ OSAddAtomic(1, (SInt32 *)&hfsmp->hfs_active_threads);
+ unlock_on_err = 1;
+ }
- if (vp->v_flag & VSYSTEM) return;
+ /* If a downgrade to read-only mount is in progress, no other
+ * process than the downgrade process is allowed to modify
+ * the file system.
+ */
+ if ((hfsmp->hfs_flags & HFS_RDONLY_DOWNGRADE) &&
+ (hfsmp->hfs_downgrading_proc != thread)) {
+ ret = EROFS;
+ goto out;
+ }
- for (bp = vp->v_cleanblkhd.lh_first; bp != NULL; bp = bp->b_vnbufs.le_next) {
- debug_check_buffersizes(vp, hp, bp);
- };
+ if (hfsmp->jnl) {
+ ret = journal_start_transaction(hfsmp->jnl);
+ if (ret == 0) {
+ OSAddAtomic(1, &hfsmp->hfs_global_lock_nesting);
+ }
+ } else {
+ ret = 0;
+ }
- for (bp = vp->v_dirtyblkhd.lh_first; bp != NULL; bp = bp->b_vnbufs.le_next) {
- debug_check_buffersizes(vp, hp, bp);
- };
+out:
+ if (ret != 0 && unlock_on_err) {
+ hfs_unlock_global (hfsmp);
+ OSAddAtomic(-1, (SInt32 *)&hfsmp->hfs_active_threads);
+ }
+
+ return ret;
}
-void debug_check_catalogdata(struct CatalogNodeData *cat) {
+int
+hfs_end_transaction(struct hfsmount *hfsmp)
+{
+ int need_unlock=0, ret;
- if (cat->cnm_nameptr == NULL) {
- DBG_ASSERT((cat->cnm_flags & kCatNameIsAllocated) == 0);
- }
- else if (cat->cnm_nameptr == cat->cnm_namespace) {
- DBG_ASSERT((cat->cnm_flags & kCatNameIsAllocated) == 0);
- }
- else {
- DBG_ASSERT((cat->cnm_flags & kCatNameIsAllocated) == kCatNameIsAllocated);
- }
+ if ((hfsmp->jnl == NULL) || ( journal_owner(hfsmp->jnl) == current_thread()
+ && (OSAddAtomic(-1, &hfsmp->hfs_global_lock_nesting) == 1)) ) {
+ need_unlock = 1;
+ }
- if (cat->cnm_nameptr) {
- DBG_ASSERT(strlen(cat->cnm_nameptr) == cat->cnm_length);
+ if (hfsmp->jnl) {
+ ret = journal_end_transaction(hfsmp->jnl);
+ } else {
+ ret = 0;
}
-
- if (cat->cnm_flags & kCatNameIsConsumed) {
- DBG_ASSERT((cat->cnm_flags & kCatNameIsAllocated) == 0);
- }
- if (cat->cnm_flags & kCatNameNoCopyName) {
- DBG_ASSERT((cat->cnm_flags & (kCatNameIsAllocated|kCatNameIsConsumed|kCatNameIsMangled)) == 0);
- DBG_ASSERT(cat->cnm_length == 0);
- DBG_ASSERT(cat->cnm_nameptr == 0);
- DBG_ASSERT(strlen(cat->cnm_namespace) == 0);
- }
+ if (need_unlock) {
+ OSAddAtomic(-1, (SInt32 *)&hfsmp->hfs_active_threads);
+ hfs_unlock_global (hfsmp);
+ hfs_sync_ejectable(hfsmp);
+ }
+ return ret;
}
-extern void hfs_vhash_dbg(struct hfsnode *hp);
-/* Checks the valicity of a hfs vnode */
-void debug_check_vnode(struct vnode *vp, int stage) {
- struct hfsnode *hp;
- u_long size;
- int i;
+/*
+ * Flush the contents of the journal to the disk.
+ *
+ * Input:
+ * wait_for_IO -
+ * If TRUE, wait to write in-memory journal to the disk
+ * consistently, and also wait to write all asynchronous
+ * metadata blocks to its corresponding locations
+ * consistently on the disk. This means that the journal
+ * is empty at this point and does not contain any
+ * transactions. This is overkill in normal scenarios
+ * but is useful whenever the metadata blocks are required
+ * to be consistent on-disk instead of just the journal
+ * being consistent; like before live verification
+ * and live volume resizing.
+ *
+ * If FALSE, only wait to write in-memory journal to the
+ * disk consistently. This means that the journal still
+ * contains uncommitted transactions and the file system
+ * metadata blocks in the journal transactions might be
+ * written asynchronously to the disk. But there is no
+ * guarantee that they are written to the disk before
+ * returning to the caller. Note that this option is
+ * sufficient for file system data integrity as it
+ * guarantees consistent journal content on the disk.
+ */
+int
+hfs_journal_flush(struct hfsmount *hfsmp, boolean_t wait_for_IO)
+{
+ int ret;
- /* vcb stuff */
- if (VTOHFS(vp)->hfs_mount_flags & kHFSBootVolumeInconsistentMask)
- DEBUG_BREAK_MSG(("Volume is damaged!"));
+ /* Only peek at hfsmp->jnl while holding the global lock */
+ hfs_lock_global (hfsmp, HFS_SHARED_LOCK);
+ if (hfsmp->jnl) {
+ ret = journal_flush(hfsmp->jnl, wait_for_IO);
+ } else {
+ ret = 0;
+ }
+ hfs_unlock_global (hfsmp);
- /* vnode stuff */
- if (vp==NULL)
- DEBUG_BREAK_MSG(("Null vnode"));
- if (vp->v_tag != VT_HFS)
- DEBUG_BREAK_MSG(("Not a HFS vnode, it is a %d", vp->v_tag));
- if (vp->v_data==NULL)
- DEBUG_BREAK_MSG(("v_data is NULL"));
-
- /* hfsnode stuff */
- hp = VTOH(vp);
- if (hp->h_valid != HFS_VNODE_MAGIC)
- DEBUG_BREAK_MSG(("Bad Formed HFS node"));
- if (hp->h_vp==NULL || hp->h_vp!=vp)
- DEBUG_BREAK_MSG(("Bad hfsnode vnode pte"));
- if (hp->h_meta == NULL)
- DEBUG_BREAK_MSG(("Bad hfsnode meta ptr"));
- switch (H_FORKTYPE(hp)) {
- case kDataFork:
- case kRsrcFork:
- if ((hp->h_meta->h_siblinghead.cqh_first == NULL) || (hp->h_meta->h_siblinghead.cqh_last == NULL))
- DEBUG_BREAK_MSG(("Null sibling header"));
- if ((hp->h_sibling.cqe_next==NULL) || (hp->h_sibling.cqe_prev==NULL))
- DEBUG_BREAK_MSG(("Null sibling list"));
- if (hp->h_meta->h_usecount<1 || hp->h_meta->h_usecount>2)
- DEBUG_BREAK_MSG(("Bad sibling usecount"));
- break;
- case kDirectory:
- case kSysFile:
- if ((hp->h_meta->h_siblinghead.cqh_first != NULL) || (hp->h_meta->h_siblinghead.cqh_last != NULL))
- DEBUG_BREAK_MSG(("Non Null sibling header"));
- if ((hp->h_sibling.cqe_next!=NULL) || (hp->h_sibling.cqe_prev!=NULL))
- DEBUG_BREAK_MSG(("Null sibling list"));
- if (hp->h_meta->h_usecount!=1)
- DEBUG_BREAK_MSG(("Bad usecount"));
-
- break;
- default:
- DEBUG_BREAK_MSG(("Bad hfsnode fork type"));
- }
-
- /* hfsmeta stuff */
- if (hp->h_meta->h_devvp == NULL)
- DEBUG_BREAK_MSG(("Bad hfsnode dev vnode"));
- if (H_DEV(hp) == 0)
- DEBUG_BREAK_MSG(("Bad dev id"));
- if (H_FILEID(hp) == 0)
- DEBUG_BREAK_MSG(("Bad file id"));
-
- if (((hp->h_meta->h_metaflags & IN_DATANODE)==0) && (H_DIRID(hp) == 0) && (H_FILEID(hp) != 1))
- DEBUG_BREAK_MSG(("Bad dir id"));
-
- if (hp->h_meta->h_namePtr == NULL && hp->h_meta->h_namelen!=0)
- DEBUG_BREAK_MSG(("hfs meta h_namelen is not 0"));
- if (hp->h_meta->h_namePtr != NULL && strlen(hp->h_meta->h_namePtr) != hp->h_meta->h_namelen)
- DEBUG_BREAK_MSG(("Bad hfs meta h_namelen"));
-
- /* Check the hash */
- hfs_vhash_dbg(hp);
-
- /* Check to see if we want to compare with the disk */
- if (stage > 200) {
- int retval;
- hfsCatalogInfo catInfo;
-
- INIT_CATALOGDATA(&catInfo.nodeData, 0);
- catInfo.hint = 0;
-
- if (hfs_metafilelocking(VTOHFS(vp), kHFSCatalogFileID, LK_SHARED, current_proc()))
- return;
+ return ret;
+}
- if (hfs_getcatalog(VTOVCB(vp), H_DIRID(hp), hp->h_meta->h_namePtr, hp->h_meta->h_namelen, &catInfo))
- DEBUG_BREAK_MSG(("Could not find hfsnode Catalog record"));
- (void) hfs_metafilelocking(VTOHFS(vp), kHFSCatalogFileID, LK_RELEASE, current_proc());
+/*
+ * hfs_erase_unused_nodes
+ *
+ * Check wheter a volume may suffer from unused Catalog B-tree nodes that
+ * are not zeroed (due to <rdar://problem/6947811>). If so, just write
+ * zeroes to the unused nodes.
+ *
+ * How do we detect when a volume needs this repair? We can't always be
+ * certain. If a volume was created after a certain date, then it may have
+ * been created with the faulty newfs_hfs. Since newfs_hfs only created one
+ * clump, we can assume that if a Catalog B-tree is larger than its clump size,
+ * that means that the entire first clump must have been written to, which means
+ * there shouldn't be unused and unwritten nodes in that first clump, and this
+ * repair is not needed.
+ *
+ * We have defined a bit in the Volume Header's attributes to indicate when the
+ * unused nodes have been repaired. A newer newfs_hfs will set this bit.
+ * As will fsck_hfs when it repairs the unused nodes.
+ */
+int hfs_erase_unused_nodes(struct hfsmount *hfsmp)
+{
+ int result;
+ struct filefork *catalog;
+ int lockflags;
+
+ if (hfsmp->vcbAtrb & kHFSUnusedNodeFixMask)
+ {
+ /* This volume has already been checked and repaired. */
+ return 0;
+ }
- if (H_FILEID(hp) != catInfo.nodeData.cnd_nodeID)
- DEBUG_BREAK_MSG(("hfsnode catalog node id mismatch"));
- if (H_DIRID(hp) != catInfo.nodeData.cnm_parID)
- DEBUG_BREAK_MSG(("hfsnode catalog dir id mismatch"));
- if (strcmp(hp->h_meta->h_namePtr, catInfo.nodeData.cnm_nameptr) != 0)
- DEBUG_BREAK_MSG(("hfsnode catalog name mismatch"));
- /* Check dates too??? */
+ if ((hfsmp->localCreateDate < kHFSUnusedNodesFixDate))
+ {
+ /* This volume is too old to have had the problem. */
+ hfsmp->vcbAtrb |= kHFSUnusedNodeFixMask;
+ return 0;
+ }
- CLEAN_CATALOGDATA(&catInfo.nodeData);
+ catalog = hfsmp->hfs_catalog_cp->c_datafork;
+ if (catalog->ff_size > catalog->ff_clumpsize)
+ {
+ /* The entire first clump must have been in use at some point. */
+ hfsmp->vcbAtrb |= kHFSUnusedNodeFixMask;
+ return 0;
+ }
+
+ /*
+ * If we get here, we need to zero out those unused nodes.
+ *
+ * We start a transaction and lock the catalog since we're going to be
+ * making on-disk changes. But note that BTZeroUnusedNodes doens't actually
+ * do its writing via the journal, because that would be too much I/O
+ * to fit in a transaction, and it's a pain to break it up into multiple
+ * transactions. (It behaves more like growing a B-tree would.)
+ */
+ printf("hfs_erase_unused_nodes: updating volume %s.\n", hfsmp->vcbVN);
+ result = hfs_start_transaction(hfsmp);
+ if (result)
+ goto done;
+ lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK);
+ result = BTZeroUnusedNodes(catalog);
+ vnode_waitforwrites(hfsmp->hfs_catalog_vp, 0, 0, 0, "hfs_erase_unused_nodes");
+ hfs_systemfile_unlock(hfsmp, lockflags);
+ hfs_end_transaction(hfsmp);
+ if (result == 0)
+ hfsmp->vcbAtrb |= kHFSUnusedNodeFixMask;
+ printf("hfs_erase_unused_nodes: done updating volume %s.\n", hfsmp->vcbVN);
- }
+done:
+ return result;
+}
- /* Check Extents */
- {
- for(i = 0, size = 0; i < kHFSPlusExtentDensity; i++)
- {
- size += hp->fcbExtents[i].blockCount;
- }
+extern time_t snapshot_timestamp;
- if (hp->fcbEOF > hp->fcbPLen)
- DEBUG_BREAK_MSG(("fcbPLen is smaller than fcbEOF"));
+int
+check_for_tracked_file(struct vnode *vp, time_t ctime, uint64_t op_type, void *arg)
+{
+ int tracked_error = 0, snapshot_error = 0;
+
+ if (vp == NULL) {
+ return 0;
+ }
+
+ if (VTOC(vp)->c_flags & UF_TRACKED) {
+ // the file has the tracked bit set, so send an event to the tracked-file handler
+ int error;
+
+ // printf("hfs: tracked-file: encountered a file with the tracked bit set! (vp %p)\n", vp);
+ error = resolve_nspace_item(vp, op_type | NAMESPACE_HANDLER_TRACK_EVENT);
+ if (error) {
+ if (error == EAGAIN) {
+ printf("hfs: tracked-file: timed out waiting for namespace handler...\n");
+
+ } else if (error == EINTR) {
+ // printf("hfs: tracked-file: got a signal while waiting for namespace handler...\n");
+ tracked_error = EINTR;
+ }
+ }
+ }
- if (hp->fcbExtents[kHFSPlusExtentDensity-1].blockCount == 0) {
- if ((off_t)size * (off_t)VTOVCB(vp)->blockSize != hp->fcbPLen)
- DEBUG_BREAK_MSG(("fcbPLen does not match extents"));
- } else {
- if ( hp->fcbPLen < (off_t)size * (off_t)VTOVCB(vp)->blockSize)
- DEBUG_BREAK_MSG(("fcbPLen is smaller than extents"));
- }
- for(i = 0; i < kHFSPlusExtentDensity; i++)
- {
- if (hp->fcbExtents[i].blockCount == 0 || hp->fcbExtents[i].startBlock == 0)
- break;
- }
- if ((VTOVCB(vp)->vcbSigWord == kHFSSigWord) && i > kHFSExtentDensity)
- DEBUG_BREAK_MSG(("Illegal value in extents for ordinary HFS"));
- if (i > kHFSPlusExtentDensity) {
- for(; i < kHFSPlusExtentDensity; i++)
- {
- if (hp->fcbExtents[i].blockCount != 0 || hp->fcbExtents[i].startBlock != 0)
- DEBUG_BREAK_MSG(("Illegal value in extents"));
- }
- }
+ if (ctime != 0 && snapshot_timestamp != 0 && (ctime <= snapshot_timestamp || vnode_needssnapshots(vp))) {
+ // the change time is within this epoch
+ int error;
+
+ error = resolve_nspace_item_ext(vp, op_type | NAMESPACE_HANDLER_SNAPSHOT_EVENT, arg);
+ if (error == EDEADLK) {
+ snapshot_error = 0;
+ } else if (error) {
+ if (error == EAGAIN) {
+ printf("hfs: cow-snapshot: timed out waiting for namespace handler...\n");
+ } else if (error == EINTR) {
+ // printf("hfs: cow-snapshot: got a signal while waiting for namespace handler...\n");
+ snapshot_error = EINTR;
+ }
+ }
}
+
+ if (tracked_error) return tracked_error;
+ if (snapshot_error) return snapshot_error;
+
+ return 0;
+}
-
- /* BTree stuff */
- if (0 && vp->v_flag & VSYSTEM) {
- BTreeInfoRec info;
-
- BTGetInformation(hp, 0, &info);
- if (hp->fcbBTCBPtr == NULL)
- DEBUG_BREAK_MSG(("Null fcbBTCBPtr"));
- if (H_HINT(hp) == 0)
- DEBUG_BREAK_MSG(("hint is 0"));
- if (H_HINT(hp) > info.numNodes)
- DEBUG_BREAK_MSG(("hint > numNodes"));
- }
+int
+check_for_dataless_file(struct vnode *vp, uint64_t op_type)
+{
+ int error;
+ if (vp == NULL || (VTOC(vp)->c_flags & UF_COMPRESSED) == 0 || VTOCMP(vp) == NULL || VTOCMP(vp)->cmp_type != DATALESS_CMPFS_TYPE) {
+ // there's nothing to do, it's not dataless
+ return 0;
+ }
+
+ // printf("hfs: dataless: encountered a file with the dataless bit set! (vp %p)\n", vp);
+ error = resolve_nspace_item(vp, op_type | NAMESPACE_HANDLER_NSPACE_EVENT);
+ if (error == EDEADLK && op_type == NAMESPACE_HANDLER_WRITE_OP) {
+ error = 0;
+ } else if (error) {
+ if (error == EAGAIN) {
+ printf("hfs: dataless: timed out waiting for namespace handler...\n");
+ // XXXdbg - return the fabled ENOTPRESENT (i.e. EJUKEBOX)?
+ return 0;
+ } else if (error == EINTR) {
+ // printf("hfs: dataless: got a signal while waiting for namespace handler...\n");
+ return EINTR;
+ }
+ } else if (VTOC(vp)->c_flags & UF_COMPRESSED) {
+ //
+ // if we're here, the dataless bit is still set on the file
+ // which means it didn't get handled. we return an error
+ // but it's presently ignored by all callers of this function.
+ //
+ // XXXdbg - EDATANOTPRESENT is what we really need...
+ //
+ return EBADF;
+ }
+
+ return error;
}
-
-#endif /* HFS_DIAGNOSTIC */