X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/765c9de3b4af7c2078d16a03812ae2c7c2b24938..13f56ec4e58bf8687e2a68032c093c0213dd519b:/bsd/hfs/hfs_vfsutils.c diff --git a/bsd/hfs/hfs_vfsutils.c b/bsd/hfs/hfs_vfsutils.c index 29dab5856..103232431 100644 --- a/bsd/hfs/hfs_vfsutils.c +++ b/bsd/hfs/hfs_vfsutils.c @@ -1,172 +1,76 @@ /* - * 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 #include #include #include #include -#include #include -#include +#include #include +#include #include #include +#include +#include +#include +#include +#include +#include + +#include #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 @@ -179,149 +83,223 @@ extern OSStatus ReleaseBTreeBlock(FileReference vp, BlockDescPtr blockPtr, Relea // // //******************************************************************************* +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); } //******************************************************************************* @@ -331,159 +309,565 @@ CmdDone:; //******************************************************************************* 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 . */ + 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); } @@ -495,90 +879,19 @@ ErrorExit: */ 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); } @@ -589,3175 +902,2269 @@ static int InitMetaFileVNode(struct vnode *vp, off_t eof, u_long clumpSize, cons * *************************************************************/ -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 */ -/* */ -/* */ -/************************************************************************/ - -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; +#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 */ -#if HFS_DIAGNOSTIC - DBG_ASSERT(vcb != NULL); - DBG_ASSERT(catInfo != NULL); - DBG_ASSERT(vpp != NULL); - DBG_ASSERT((forkType == kDirectory) || (forkType == kDataFork) || (forkType == kRsrcFork)); - if (catInfo->nodeData.cnd_type == kCatalogFolderNode) { - DBG_ASSERT(forkType == kDirectory); - } else { - DBG_ASSERT(forkType != kDirectory); - } -#endif + if (hfsmp->hfs_catalog_cp) { + (void) hfs_lock(hfsmp->hfs_catalog_cp, locktype); + } else { + flags &= ~SFL_CATALOG; + } - if ( ! ((forkType == kDirectory) || (forkType == kDataFork) || (forkType == kRsrcFork))) - panic("Bad fork type"); - if (catInfo->nodeData.cnd_type == kCatalogFolderNode) { - if (forkType != kDirectory) - panic("non directory type"); - } else { - if (forkType != kDataFork && forkType != kRsrcFork) - panic("non fork type"); + /* + * 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; + } } - - hfsmp = VCBTOHFS(vcb); - mp = HFSTOVFS(hfsmp); - dev = hfsmp->hfs_raw_dev; + if (flags & SFL_ATTRIBUTE) { - /* Check if unmount in progress */ - if (mp->mnt_kern_flag & MNTK_UNMOUNT) { - *vpp = NULL; - return (EPERM); - } +#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 */ - /* - * 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); + 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 */ - /* Must malloc() here, since getnewvnode() can sleep */ - MALLOC_ZONE(hp, struct hfsnode *, sizeof(struct hfsnode), M_HFSNODE, M_WAITOK); - bzero((caddr_t)hp, sizeof(struct hfsnode)); - - /* - * Set that this node is in the process of being allocated - * Set it as soon as possible, so context switches well always hit upon it. - * if this is set then wakeup() MUST be called on hp after the flag is cleared - * DO NOT exit without clearing and waking up !!!! - */ - hp->h_nodeflags |= IN_ALLOCATING; /* Mark this as being allocating */ - lockinit(&hp->h_lock, PINOD, "hfsnode", 0, 0); - - - /* getnewvnode() does a VREF() on the vnode */ - /* Allocate a new vnode. If unsuccesful, leave after freeing memory */ - if ((retval = getnewvnode(VT_HFS, mp, hfs_vnodeop_p, &vp))) { - wakeup(hp); /* Shouldnt happen, but just to make sure */ - FREE_ZONE(hp, sizeof(struct hfsnode), M_HFSNODE); - *vpp = NULL; - return (retval); - }; - - /* - * Set the essentials before locking it down - */ - hp->h_vp = vp; /* Make HFSTOV work */ - vp->v_data = hp; /* Make VTOH work */ - H_FORKTYPE(hp) = forkType; - rl_init(&hp->h_invalidranges); - fm = NULL; - - /* - * Lock the hfsnode and insert the hfsnode into the hash queue, also if meta exists - * add to sibling list and return the meta address - */ - if (SIBLING_FORKTYPE(forkType)) - hfs_vhashins_sibling(dev, catInfo->nodeData.cnd_nodeID, hp, &fm); - else - hfs_vhashins(dev, catInfo->nodeData.cnd_nodeID, hp); - - /* - * If needed allocate and init the object meta data: - */ - 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 */ - /* - * 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 - */ - - /* 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_startup_cp) { + (void) hfs_lock(hfsmp->hfs_startup_cp, locktype); + } else { + flags &= ~SFL_STARTUP; + } /* - * the vnode is finally alive, with the exception of the FCB below, - * It is finally locked and ready for its debutante ball + * When the startup file has overflow extents then + * also acquire the extents b-tree lock if its not + * already requested. */ - 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); + if ((flags & SFL_EXTENTS) == 0 && + overflow_extents(VTOF(hfsmp->hfs_startup_vp))) { + flags |= SFL_EXTENTS; + } } - - /* - * Initialize the vnode from the inode, check for aliases, sets the VROOT flag. - * Note that the underlying vnode may have changed. + /* + * To prevent locks being taken in the wrong order, the extent lock + * gets a bitmap lock as well. */ - if ((retval = hfs_vinit(mp, hfs_specop_p, hfs_fifoop_p, &vp))) { - wakeup((caddr_t)hp); - vput(vp); - *vpp = NULL; - return (retval); + if (flags & (SFL_BITMAP | SFL_EXTENTS)) { + /* + * If there's no bitmap cnode, ignore the bitmap lock. + */ + 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; + } } - - /* - * Finish inode initialization now that aliasing has been resolved. - */ - hp->h_meta->h_devvp = hfsmp->hfs_devvp; - VREF(hp->h_meta->h_devvp); - -#if HFS_DIAGNOSTIC - hp->h_valid = HFS_VNODE_MAGIC; -#endif - hp->h_nodeflags &= ~IN_ALLOCATING; /* vnode is completely initialized */ - - /* Wake up anybody waiting for us to finish..see hfs_vhash.c */ - wakeup((caddr_t)hp); - -#if HFS_DIAGNOSTIC - - /* Lets do some testing here */ - DBG_ASSERT(hp->h_meta); - DBG_ASSERT(VTOH(vp)==hp); - DBG_ASSERT(HTOV(hp)==vp); - DBG_ASSERT(hp->h_meta->h_usecount>=1 && hp->h_meta->h_usecount<=2); - if (catInfo->nodeData.cnd_type == kCatalogFolderNode) { - DBG_ASSERT(vp->v_type == VDIR); - DBG_ASSERT(H_FORKTYPE(VTOH(vp)) == kDirectory); + if (flags & SFL_EXTENTS) { + /* + * Since the extents btree lock is recursive we always + * need exclusive access. + */ + if (hfsmp->hfs_extents_cp) { + (void) hfs_lock(hfsmp->hfs_extents_cp, HFS_EXCLUSIVE_LOCK); + } else { + flags &= ~SFL_EXTENTS; + } } -#endif // HFS_DIAGNOSTIC - - - *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_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; -} - - - -void CopyVNodeToCatalogNode (struct vnode *vp, struct CatalogNodeData *nodeData) +int +hfs_owner_rights(struct hfsmount *hfsmp, uid_t cnode_uid, kauth_cred_t cred, + __unused struct proc *p, int invokesuperuserstatus) { - 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; - }; - - bcopy ( fcb->fcbExtents, extents, sizeof(HFSPlusExtentRecord)); - - 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); - - /* Set this up as an alias */ - #if SUPPORTS_MAC_ALIASES - ((struct FInfo *)(&nodeData->cnd_finderInfo))->fdFlags |= SWAP_BE16 (kIsAlias); - #endif - } + 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); } - } - - -/********************************************************************* - - Sets the name in the filemeta structure - - XXX Does not preflight if changing from one size to another - XXX Currently not protected from context switching - -*********************************************************************/ - -void hfs_set_metaname(char *name, struct hfsfilemeta *fm, struct hfsmount *hfsmp) -{ -int namelen = strlen(name); -char *tname, *fname; - -#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 - - /* - * 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 - * - - */ +} - - /* Allocate the new memory */ - if (namelen > MAXHFSVNODELEN) { - /* - * Notice the we ALWAYS allocate, even if the new is less then the old, - * or even if they are the SAME - */ - MALLOC(tname, char *, namelen+1, M_TEMP, M_WAITOK); - } - else - tname = fm->h_fileName; - 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; - }; +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; - /* Now copy it over */ - bcopy(name, tname, namelen+1); + 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 */ + }; - fm->h_namePtr = tname; - fm->h_namelen = namelen; + /* 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; - simple_unlock(&hfsmp->hfs_renamelock); + /* No clear winner exists: pick the largest even fraction <= MAXBSIZE: */ + baseBlockCount = allocationBlockSize / baseMultiple; /* Now guaranteed to be an even multiple */ - /* Lastly, free the old, if set */ - if (fname != NULL) - FREE(fname, M_TEMP); + 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; } -void hfs_name_CatToMeta(CatalogNodeData *nodeData, struct hfsfilemeta *fm) -{ -char *fname; -#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); - } +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; - 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); } + if (vcb->vcbSigWord != kHFSPlusSigWord) + return (0); -#endif //HFS_DIAGNOSTIC - + hfsmp = VCBTOHFS(vcb); - /* 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; + memset(&jdesc, 0, sizeof(struct cat_desc)); + jdesc.cd_parentcnid = kRootDirID; + jdesc.cd_nameptr = (const u_int8_t *)name; + jdesc.cd_namelen = strlen(name); - /* Set the flag */ - if (nodeData->cnm_length > MAXHFSVNODELEN) { - fm->h_metaflags |= IN_LONGNAME; - } else { - fm->h_metaflags &= ~IN_LONGNAME; - }; + 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); - /* 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; + if (error == 0) { + return (fattr->ca_fileid); + } else if (hfsmp->hfs_flags & HFS_READ_ONLY) { + return (0); } - fm->h_namelen = nodeData->cnm_length; + return (0); /* XXX what callers expect on an error */ +} - 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); -} +/* + * 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) +{ + 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; + vcb = HFSTOVCB(hfsmp); + fcb = VTOF(hfsmp->hfs_catalog_vp); + btdata.bufferAddress = &filerec; + btdata.itemSize = sizeof(filerec); + btdata.itemCount = 1; -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; + 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'; - /* User id 0 (root) always gets access. */ - if (cred->cr_uid == 0) { - permissions = R_OK | W_OK | X_OK; - goto Exit; - }; + /* + * Position the iterator just before the first real temp file/dir. + */ + 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); + + /* + * Delete all files (and directories) named "tempxxx", + * where xxx is the file's cnid in decimal. + * + */ + 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; + } - /* 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; - } + /* 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; + } + } + } - /* 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; -} + 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; + } + } + /* 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; + if (mode == S_IFDIR) { + orphaned_dirs++; + } + else { + orphaned_files++; + } -int AttributeBlockSize(struct attrlist *attrlist) { - int size; - attrgroup_t a; - -#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); + /* 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 ((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); + (void)cat_update(hfsmp, &hfsmp->hfs_private_desc[FILE_HARDLINKS], + &hfsmp->hfs_private_attr[FILE_HARDLINKS], NULL, 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); + /* Drop locks and end the transaction */ + hfs_systemfile_unlock(hfsmp, lockflags); + cat_postflight(hfsmp, &cookie, p); + catlock = catreserve = 0; - size = 0; - - 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; -} + /* + 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); + } + if (started_tr) { + hfs_end_transaction(hfsmp); + started_tr = 0; + } + } /* 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); + } -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); - }; - }; - - return mp->mnt_stat.f_mntonname; + FREE(iterator, M_TEMP); + hfsmp->hfs_flags |= HFS_CLEANED_ORPHANS; } - -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: - */ - 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; - }; - }; +/* + * 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; - 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)); -} - + DBG_ASSERT(vp != NULL); + /* start with default */ + logBlockSize = VTOHFS(vp)->hfs_logBlockSize; -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; + if (vnode_issystem(vp)) { + if (VTOF(vp)->fcbBTCBPtr != NULL) { + BTreeInfoRec bTreeInfo; - attrbufptr = *attrbufptrptr; - varbufptr = *varbufptrptr; - - 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; + /* + * 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?? + */ - /* 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; -} - + (void) BTGetInformation (VTOF(vp), kBTreeInfoVersion, &bTreeInfo); + + logBlockSize = bTreeInfo.nodeSize; + } else if (VTOC(vp)->c_fileid == kHFSAllocationFileID) { + logBlockSize = VTOVCB(vp)->vcbVBMIOSize; + } + } -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); - }; + DBG_ASSERT(logBlockSize > 0); - *attrbufptrptr = attrbufptr; - *varbufptrptr = varbufptr; + return logBlockSize; } +u_int32_t +hfs_freeblks(struct hfsmount * hfsmp, int wantreserve) +{ + u_int32_t freeblks; + u_int32_t rsrvblks; + u_int32_t loanblks; + /* + * We don't bother taking the mount lock + * to look at these values since the values + * themselves are each updated atomically + * on aligned addresses. + */ + 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; +#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; + } -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 ) -{ - 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; + 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 - 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; + 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 flags; - - if (catalogInfo->nodeData.cnd_mode & IFMT) - flags = catalogInfo->nodeData.cnd_ownerFlags | - catalogInfo->nodeData.cnd_adminFlags << 16; + /* Take into account any delayed allocations. */ + loanedblks = 2 * hfsmp->loanedBlocks; + if (vfreeblks > loanedblks) + vfreeblks -= loanedblks; 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; + 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 */ + + return (freeblks); } +/* + * Map HFS Common errors (negative) to BSD error codes (positive). + * Positive errors (ie BSD errors) are passed through unchanged. + */ +short MacToVFSError(OSErr err) +{ + 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; -} - + } -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 (need_remove) + TAILQ_REMOVE(&dcp->c_hintlist, hint, dh_link); -#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 - -void UnpackAttributeBlock(struct attrlist *alist, - struct vnode *vp, - struct hfsCatalogInfo *catInfo, - void **attrbufptrptr, - void **varbufptrptr) { +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"; + } + 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 ). 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 */