X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/d52fe63fc81f7e44faaae711812a211a78434976..55e303ae13a4cf49d70f2294092726f2fffb9ef2:/bsd/hfs/hfs_vfsutils.c diff --git a/bsd/hfs/hfs_vfsutils.c b/bsd/hfs/hfs_vfsutils.c index e69c2e046..98a559393 100644 --- a/bsd/hfs/hfs_vfsutils.c +++ b/bsd/hfs/hfs_vfsutils.c @@ -1,165 +1,69 @@ /* - * Copyright (c) 2000-2001 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2000-2003 Apple Computer, Inc. All rights reserved. * * @APPLE_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. + * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. * - * This Original Code and all software distributed under the License are - * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * 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. 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@ */ /* @(#)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 "hfs.h" +#include "hfs_catalog.h" #include "hfs_dbg.h" #include "hfs_mount.h" #include "hfs_endian.h" +#include "hfs_cnode.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 void ReleaseMetaFileVNode(struct vnode *vp); +static int hfs_late_journal_init(struct hfsmount *hfsmp, HFSPlusVolumeHeader *vhp, void *_args); -static int InitMetaFileVNode(struct vnode *vp, off_t eof, u_long clumpSize, const HFSPlusExtentRecord extents, - HFSCatalogNodeID fileID, void * keyCompareProc); +static void hfs_metadatazone_init(struct hfsmount *); +static u_int32_t hfs_hotfile_freeblocks(struct hfsmount *); -static void ReleaseMetaFileVNode(struct vnode *vp); -static void RemovedMetaDataDirectory(ExtendedVCB *vcb); -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); /* BTree accessor routines */ @@ -179,132 +83,155 @@ extern OSStatus ReleaseBTreeBlock(FileReference vp, BlockDescPtr blockPtr, Relea // // //******************************************************************************* +char hfs_catname[] = "Catalog B-tree"; +char hfs_extname[] = "Extents B-tree"; +char hfs_vbmname[] = "Volume Bitmap"; +char hfs_privdirname[] = + "\xE2\x90\x80\xE2\x90\x80\xE2\x90\x80\xE2\x90\x80HFS+ Private Data"; + +__private_extern__ OSErr hfs_MountHFSVolume(struct hfsmount *hfsmp, HFSMasterDirectoryBlock *mdb, 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(); + struct cat_desc cndesc; + struct cat_attr cnattr; + struct cat_fork fork; - if (hfsmp == nil || mdb == nil) /* exit if bad paramater */ + /* 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->vcbCrDate = 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->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)) + if (error || (utf8chars == 0)) (void) mac_roman_to_utf8(mdb->drVN, NAME_MAX, &utf8chars, vcb->vcbVN); - // Initialize our dirID/nodePtr cache associated with this volume. - err = InitMRUCache( sizeof(UInt32), kDefaultNumMRUCacheBlocks, &(vcb->hintCachePtr) ); - ReturnIfError( err ); - - hfsmp->hfs_logBlockSize = BestBlockSizeFit(vcb->blockSize, MAXBSIZE, hfsmp->hfs_phys_block_size); + hfsmp->hfs_logBlockSize = BestBlockSizeFit(vcb->blockSize, MAXBSIZE, hfsmp->hfs_phys_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); + bzero(&cndesc, sizeof(cndesc)); + cndesc.cd_parentcnid = kRootParID; + cndesc.cd_flags |= CD_ISMETA; + bzero(&cnattr, sizeof(cnattr)); + cnattr.ca_nlink = 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(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, &cndesc, 0, &cnattr, &fork, + &vcb->extentsRefNum); + if (error) goto MtVolErr; + error = MacToVFSError(BTOpenPath(VTOF(vcb->extentsRefNum), + (KeyCompareProcPtr)CompareExtentKeys)); + if (error) { + VOP_UNLOCK(vcb->extentsRefNum, 0, p); + goto MtVolErr; + } /* * 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(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, &cndesc, 0, &cnattr, &fork, + &vcb->catalogRefNum); + if (error) { + VOP_UNLOCK(vcb->extentsRefNum, 0, p); + goto MtVolErr; + } + error = MacToVFSError(BTOpenPath(VTOF(vcb->catalogRefNum), + (KeyCompareProcPtr)CompareCatalogKeys)); + if (error) { + VOP_UNLOCK(vcb->catalogRefNum, 0, p); + VOP_UNLOCK(vcb->extentsRefNum, 0, p); + goto MtVolErr; + } /* 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); - /* * all done with b-trees so we can unlock now... */ - VOP_UNLOCK(vcb->catalogRefNum, 0, p); - VOP_UNLOCK(vcb->extentsRefNum, 0, p); - - err = noErr; + VOP_UNLOCK(vcb->catalogRefNum, 0, p); + VOP_UNLOCK(vcb->extentsRefNum, 0, p); - if ( err == noErr ) + if ( error == noErr ) { if ( !(vcb->vcbAtrb & kHFSVolumeHardwareLockMask) ) // if the disk is not write protected { @@ -314,12 +241,12 @@ OSErr hfs_MountHFSVolume(struct hfsmount *hfsmp, HFSMasterDirectoryBlock *mdb, goto CmdDone; //-- Release any resources allocated so far before exiting with an error: -MtVolErr:; +MtVolErr: ReleaseMetaFileVNode(vcb->catalogRefNum); ReleaseMetaFileVNode(vcb->extentsRefNum); -CmdDone:; - return( err ); +CmdDone: + return (error); } //******************************************************************************* @@ -328,134 +255,227 @@ CmdDone:; // //******************************************************************************* +__private_extern__ OSErr hfs_MountHFSPlusVolume(struct hfsmount *hfsmp, HFSPlusVolumeHeader *vhp, - off_t embeddedOffset, off_t disksize, struct proc *p) + off_t embeddedOffset, u_int64_t disksize, struct proc *p, void *args) { - 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; + UInt32 blockSize; + u_int64_t volumesize; + struct BTreeInfoRec btinfo; + u_int16_t signature; + u_int16_t version; + int i; + OSErr retval; + + signature = SWAP_BE16(vhp->signature); + version = SWAP_BE16(vhp->version); + + if (signature == kHFSPlusSigWord) { + if (version != kHFSPlusVersion) { + printf("hfs_mount: invalid HFS+ version: %d\n", version); + return (EINVAL); + } + } else if (signature == kHFSXSigWord) { + if (version != kHFSXVersion) { + printf("hfs_mount: invalid HFSX version: %d\n", version); + return (EINVAL); + } + /* The in-memory signature is always 'H+'. */ + signature = kHFSPlusSigWord; + hfsmp->hfs_flags |= HFS_X; + } else { + printf("hfs_mount: invalid HFS+ sig 0x%04x\n", signature); 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)) + 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) return (EINVAL); /* Make sure we can live with the physical block size. */ if ((disksize & (hfsmp->hfs_phys_block_size - 1)) || (embeddedOffset & (hfsmp->hfs_phys_block_size - 1)) || - (SWAP_BE32(vhp->blockSize) < hfsmp->hfs_phys_block_size)) { + (blockSize < hfsmp->hfs_phys_block_size)) { return (ENXIO); } - /* * 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 = (UInt16)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 */ + if ((hfsmp->hfs_flags & HFS_READ_ONLY) == 0) + vcb->vcbWrCnt++; /* compensate for write of Volume Header on last flush */ VCB_LOCK_INIT(vcb); - /* 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->freeBlocks = SWAP_BE32(vhp->freeBlocks); + vcb->blockSize = blockSize; + vcb->encodingsBitmap = SWAP_BE64(vhp->encodingsBitmap); + vcb->localCreateDate = SWAP_BE32(vhp->createDate); - vcb->hfsPlusIOPosOffset = embeddedOffset; + vcb->hfsPlusIOPosOffset = embeddedOffset; - vcb->localCreateDate = SWAP_BE32 (vhp->createDate); /* in local time, not GMT! */ + /* Default to no free block reserve */ + vcb->reserveBlocks = 0; - /* 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_phys_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!!!! - - // 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 = kRootParID; + cndesc.cd_flags |= CD_ISMETA; + bzero(&cnattr, sizeof(cnattr)); + cnattr.ca_nlink = 1; + cnattr.ca_mode = S_IFREG; /* - * Set up Extents B-tree vnode... - */ - retval = GetInitializedVNode(hfsmp, &tmpvnode); + * Set up Extents B-tree vnode + */ + cndesc.cd_nameptr = hfs_extname; + cndesc.cd_namelen = strlen(hfs_extname); + cndesc.cd_cnid = cnattr.ca_fileid = kHFSExtentsFileID; + + cfork.cf_size = SWAP_BE64 (vhp->extentsFile.logicalSize); + 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, &cndesc, 0, &cnattr, &cfork, + &vcb->extentsRefNum); + 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; + retval = MacToVFSError(BTOpenPath(VTOF(vcb->extentsRefNum), + (KeyCompareProcPtr) CompareExtentKeysPlus)); + if (retval) { + VOP_UNLOCK(vcb->extentsRefNum, 0, p); + 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(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, &cndesc, 0, &cnattr, &cfork, + &vcb->catalogRefNum); + if (retval) { + VOP_UNLOCK(vcb->extentsRefNum, 0, p); + goto ErrorExit; + } + retval = MacToVFSError(BTOpenPath(VTOF(vcb->catalogRefNum), + (KeyCompareProcPtr) CompareExtendedCatalogKeys)); + if (retval) { + VOP_UNLOCK(vcb->catalogRefNum, 0, p); + VOP_UNLOCK(vcb->extentsRefNum, 0, p); + goto ErrorExit; + } + if ((hfsmp->hfs_flags & HFS_X) && + BTGetInformation(VTOF(vcb->catalogRefNum), 0, &btinfo) == 0) { + if (btinfo.keyCompareType == kHFSBinaryCompare) { + hfsmp->hfs_flags |= HFS_CASE_SENSITIVE; + /* Install a case-sensitive key compare */ + (void) BTOpenPath(VTOF(vcb->catalogRefNum), + (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; - - /* - * Now that Catalog file is open get the volume name from the catalog - */ - retval = MacToVFSError( GetVolumeNameFromCatalog(vcb) ); - if (retval != noErr) goto ErrorExit; + cndesc.cd_nameptr = hfs_vbmname; + cndesc.cd_namelen = strlen(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, &cndesc, 0, &cnattr, &cfork, + &vcb->allocationsRefNum); + if (retval) { + VOP_UNLOCK(vcb->catalogRefNum, 0, p); + VOP_UNLOCK(vcb->extentsRefNum, 0, p); + goto ErrorExit; + } + + /* Pick up volume name and create date */ + retval = cat_idlookup(hfsmp, kHFSRootFolderID, &cndesc, &cnattr, NULL); + if (retval) { + VOP_UNLOCK(vcb->allocationsRefNum, 0, p); + VOP_UNLOCK(vcb->catalogRefNum, 0, p); + VOP_UNLOCK(vcb->extentsRefNum, 0, p); + goto ErrorExit; + } + vcb->vcbCrDate = cnattr.ca_itime; + vcb->volumeNameEncodingHint = cndesc.cd_encoding; + bcopy(cndesc.cd_nameptr, vcb->vcbVN, min(255, cndesc.cd_namelen)); + cat_releasedesc(&cndesc); /* mark the volume dirty (clear clean unmount bit) */ vcb->vcbAtrb &= ~kHFSVolumeUnmountedMask; - - /* setup private/hidden directory for unlinked files */ - hfsmp->hfs_private_metadata_dir = FindMetaDataDirectory(vcb); + if (hfsmp->jnl && (hfsmp->hfs_flags & HFS_READ_ONLY) == 0) { + hfs_flushvolumeheader(hfsmp, TRUE, TRUE); + } /* * all done with metadata files so we can unlock now... @@ -464,15 +484,72 @@ OSErr hfs_MountHFSPlusVolume(struct hfsmount *hfsmp, HFSPlusVolumeHeader *vhp, VOP_UNLOCK(vcb->catalogRefNum, 0, p); VOP_UNLOCK(vcb->extentsRefNum, 0, p); - if ( !(vcb->vcbAtrb & kHFSVolumeHardwareLockMask) ) // if the disk is not write protected + // + // 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) { + hfsmp->jnl = NULL; + goto ErrorExit; + } else if (hfsmp->jnl) { + hfsmp->hfs_mp->mnt_flag |= MNT_JOURNALED; + } + } else if (hfsmp->jnl) { + struct cat_attr jinfo_attr, jnl_attr; + + // 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); + } + } + + /* + * Establish a metadata allocation zone. + */ + hfs_metadatazone_init(hfsmp); + + /* + * 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) { + vcb->nextAllocation = hfsmp->hfs_metazone_end + 1; + } + } + + /* setup private/hidden directory for unlinked files */ + FindMetaDataDirectory(vcb); + if (hfsmp->jnl && ((hfsmp->hfs_flags & HFS_READ_ONLY) == 0)) + hfs_remove_orphans(hfsmp); + + if ( !(vcb->vcbAtrb & kHFSVolumeHardwareLockMask) ) // if the disk is not write protected { - MarkVCBDirty( vcb ); // mark VCB dirty so it will be written + MarkVCBDirty( vcb ); // mark VCB dirty so it will be written } - - DBG_VFS(("hfs_MountHFSPlusVolume: returning (%d)\n", retval)); - return (0); + /* + * Allow hot file clustering if conditions allow. + */ + if ((hfsmp->hfs_flags & HFS_METADATA_ZONE) && + ((hfsmp->hfs_flags & HFS_READ_ONLY) == 0)) { + (void) hfs_recording_init(hfsmp, p); + } + + return (0); ErrorExit: /* @@ -480,10 +557,7 @@ ErrorExit: * release any resources that we aquired... */ - DBG_VFS(("hfs_MountHFSPlusVolume: fatal error (%d)\n", retval)); - - InvalidateCatalogCache(vcb); - + InvalidateCatalogCache(vcb); ReleaseMetaFileVNode(vcb->allocationsRefNum); ReleaseMetaFileVNode(vcb->catalogRefNum); ReleaseMetaFileVNode(vcb->extentsRefNum); @@ -499,90 +573,16 @@ 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) BTClosePath(fp); /* 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!"); - } - - 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"); + vrele(vp); + vgone(vp); } - - if (keyCompareProc != NULL) { - result = BTOpenPath(fcb, - (KeyCompareProcPtr) keyCompareProc, - GetBTreeBlock, - ReleaseBTreeBlock, - ExtendBTreeFile, - SetBTreeBlockSize); - result = MacToVFSError(result); - } - - return (result); } @@ -593,18 +593,20 @@ static int InitMetaFileVNode(struct vnode *vp, off_t eof, u_long clumpSize, cons * *************************************************************/ -short hfsUnmount( register struct hfsmount *hfsmp, struct proc *p) +__private_extern__ +int +hfsUnmount( register struct hfsmount *hfsmp, struct proc *p) { - ExtendedVCB *vcb = HFSTOVCB(hfsmp); - int retval = E_NONE; + ExtendedVCB *vcb = HFSTOVCB(hfsmp); + int retval = E_NONE; - (void) DisposeMRUCache(vcb->hintCachePtr); InvalidateCatalogCache( vcb ); - // XXX PPD: Should dispose of any allocated volume cache here: call DisposeVolumeCacheBlocks( vcb )? - - (void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_EXCLUSIVE, p); - (void) hfs_metafilelocking(hfsmp, kHFSExtentsFileID, LK_EXCLUSIVE, p); + if (hfsmp->hfc_filevp) { + ReleaseMetaFileVNode(hfsmp->hfc_filevp); + hfsmp->hfc_filevp = NULL; + } + if (vcb->vcbSigWord == kHFSPlusSigWord) ReleaseMetaFileVNode(vcb->allocationsRefNum); @@ -616,2615 +618,614 @@ short hfsUnmount( register struct hfsmount *hfsmp, struct proc *p) /* - * hfs_resolvelink - auto resolve HFS+ hardlinks - * - * Used after calling GetCatalogNode or GetCatalogOffspring + * Test is 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; + u_long blocks; - fip = (struct FInfo *) &cndp->cnd_finderInfo; + if (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/Unlock a metadata file. */ - -short hfs_getcatalog (ExtendedVCB *vcb, UInt32 parentDirID, char *name, short len, hfsCatalogInfo *catInfo) +__private_extern__ +int +hfs_metafilelocking(struct hfsmount *hfsmp, u_long fileID, u_int flags, struct proc *p) { - OSErr result; - UInt32 length; - - if (len == -1 ) { /* Convert it to MacOS terms */ - if (name) - length = strlen(name); - else - length = kUndefinedStrLen; - } - else - length = len; + ExtendedVCB *vcb; + struct vnode *vp = NULL; + int numOfLockedBuffs; + int retval = 0; + + vcb = HFSTOVCB(hfsmp); + + switch (fileID) { + case kHFSExtentsFileID: + vp = vcb->extentsRefNum; + break; - result = GetCatalogNode(vcb, parentDirID, name, length, catInfo->hint, &catInfo->nodeData, &catInfo->hint); + case kHFSCatalogFileID: + vp = vcb->catalogRefNum; + break; -#if HFS_DIAGNOSTICS - if (catInfo->nodeData.cnm_nameptr) { - DBG_ASSERT(strlen(catInfo->nodeData.cnm_nameptr) == catInfo->nodeData.cnm_length); + case kHFSAllocationFileID: + /* bitmap is covered by Extents B-tree locking */ + /* FALL THROUGH */ + default: + panic("hfs_lockmetafile: invalid fileID"); } -#endif - if (result == 0) - hfs_resolvelink(vcb, &catInfo->nodeData); + if ((flags & LK_TYPE_MASK) != LK_RELEASE) { + flags |= LK_RETRY; + } else if (hfsmp->jnl == NULL) { + struct timeval tv = time; + u_int32_t lastfsync = tv.tv_sec; + + (void) BTGetLastSync((FCB*)VTOF(vp), &lastfsync); + + numOfLockedBuffs = count_lock_queue(); + if ((numOfLockedBuffs > kMaxLockedMetaBuffers) || + ((numOfLockedBuffs > 1) && ((tv.tv_sec - lastfsync) > kMaxSecsForFsync))) { + hfs_btsync(vp, HFS_SYNCTRANS); + } + } + + retval = lockmgr(&VTOC(vp)->c_lock, flags, &vp->v_interlock, p); - return MacToVFSError(result); + return (retval); } +/* + * 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) +{ + struct lock__bsd__ *lkp; + int locked = false; + pid_t pid; + void * self; + + pid = current_proc()->p_pid; + self = (void *) current_act(); + lkp = &VTOC(vp)->c_lock; + simple_lock(&lkp->lk_interlock); + + if (shareable && (lkp->lk_sharecount > 0) && (lkp->lk_lockholder == LK_NOPROC)) + locked = true; + else if ((lkp->lk_exclusivecount > 0) && (lkp->lk_lockholder == pid) && (lkp->lk_lockthread == self)) + locked = true; -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? */ + simple_unlock(&lkp->lk_interlock); + + if (!locked) { + switch (VTOC(vp)->c_fileid) { + case 3: + DEBUG_BREAK_MSG((" #\n # RequireFileLock: extent btree vnode not locked! v: 0x%08X\n #\n", (u_int)vp)); + break; - /* - * 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); + case 4: + DEBUG_BREAK_MSG((" #\n # RequireFileLock: catalog btree vnode not locked! v: 0x%08X\n #\n", (u_int)vp)); + break; - if (result) - DBG_ERR(("on Delete, DeleteFile returned: %d: dirid: %ld name: %s\n", result, parentDirID, name)); - - return MacToVFSError(result); + default: + DEBUG_BREAK_MSG((" #\n # RequireFileLock: file (%d) not locked! v: 0x%08X\n #\n", VTOC(vp)->c_fileid, (u_int)vp)); + break; + } + } } +#endif -short hfsMoveRename (ExtendedVCB *vcb, UInt32 oldDirID, char *oldName, UInt32 newDirID, char *newName, UInt32 *hint) +/* + * There are three ways to qualify for ownership rights on an object: + * + * 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 hfsmount *hfsmp, uid_t cnode_uid, struct ucred *cred, + struct proc *p, int invokesuperuserstatus) { - OSErr result = noErr; + if ((cred->cr_uid == cnode_uid) || /* [1a] */ + (cnode_uid == UNKNOWNUID) || /* [1b] */ + ((HFSTOVFS(hfsmp)->mnt_flag & MNT_UNKNOWNPERMISSIONS) && /* [2] */ + ((cred->cr_uid == hfsmp->hfs_uid) || /* [2a] */ + (hfsmp->hfs_uid == UNKNOWNUID))) || /* [2b] */ + (invokesuperuserstatus && (suser(cred, &p->p_acflag) == 0))) { /* [3] */ + return (0); + } else { + return (EPERM); + } +} - 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)); - +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; - return MacToVFSError(result); -} + 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 */ + }; -/* XXX SER pass back the hint so other people can use it */ + /* Try the obvious winner first, to prevent 12K allocation blocks, for instance, + from being handled as two 6K logical blocks instead of 3 4K logical blocks. + Even though the former (the result of the loop below) is the larger allocation + block size, the latter is more efficient: */ + if (allocationBlockSize % PAGE_SIZE == 0) return PAGE_SIZE; + /* No clear winner exists: pick the largest even fraction <= MAXBSIZE: */ + baseBlockCount = allocationBlockSize / baseMultiple; /* Now guaranteed to be an even multiple */ -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; + for (blockCount = baseBlockCount; blockCount > 0; --blockCount) { + trialBlockSize = blockCount * baseMultiple; + if (allocationBlockSize % trialBlockSize == 0) { /* An even multiple? */ + if ((trialBlockSize <= blockSizeLimit) && + (trialBlockSize % baseMultiple == 0)) { + return trialBlockSize; + }; + }; + }; - result = CreateCatalogNode (vcb, dirID, name, type, &catalogNodeID, &catalogHint, tehint); - - return MacToVFSError(result); + /* 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; } -short hfsCreateFileID (ExtendedVCB *vcb, UInt32 parentDirID, StringPtr name, UInt32 catalogHint, UInt32 *fileIDPtr) +/* + * 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_privdir_desc. + */ +__private_extern__ +u_long +FindMetaDataDirectory(ExtendedVCB *vcb) { - return MacToVFSError(CreateFileIDRef(vcb, parentDirID, name, catalogHint, fileIDPtr)); -} - + struct hfsmount * hfsmp; + struct vnode * dvp = NULL; + struct cnode * dcp = NULL; + struct FndrDirInfo * fndrinfo; + struct cat_desc out_desc = {0}; + struct proc *p = current_proc(); + struct timeval tv; + cat_cookie_t cookie; + int error; + + if (vcb->vcbSigWord != kHFSPlusSigWord) + return (0); -/********************************************************************************/ -/* */ -/* hfs_vget_catinfo - Returns a vnode derived from a hfs catInfo struct */ -/* */ -/********************************************************************************/ + hfsmp = VCBTOHFS(vcb); -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 (hfsmp->hfs_privdir_desc.cd_parentcnid == 0) { + hfsmp->hfs_privdir_desc.cd_parentcnid = kRootDirID; + hfsmp->hfs_privdir_desc.cd_nameptr = hfs_privdirname; + hfsmp->hfs_privdir_desc.cd_namelen = strlen(hfs_privdirname); + hfsmp->hfs_privdir_desc.cd_flags = CD_ISDIR; + } - if (*target_vp == NULL) - retval = hfs_vcreate( VTOVCB(parent_vp), catInfo, forkType, target_vp); + /* Lock catalog b-tree */ + if (hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_SHARED, p) != 0) + return (0); - return (retval); -} + error = cat_lookup(hfsmp, &hfsmp->hfs_privdir_desc, 0, NULL, + &hfsmp->hfs_privdir_attr, NULL); + /* Unlock catalog b-tree */ + (void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, p); + if (error == 0) { + hfsmp->hfs_metadata_createdate = hfsmp->hfs_privdir_attr.ca_itime; + hfsmp->hfs_privdir_desc.cd_cnid = hfsmp->hfs_privdir_attr.ca_fileid; + /* + * Clear the system immutable flag if set... + */ + if ((hfsmp->hfs_privdir_attr.ca_flags & SF_IMMUTABLE) && + (hfsmp->hfs_flags & HFS_READ_ONLY) == 0) { + hfsmp->hfs_privdir_attr.ca_flags &= ~SF_IMMUTABLE; + + hfs_global_shared_lock_acquire(hfsmp); + if (hfsmp->jnl) { + if ((error = journal_start_transaction(hfsmp->jnl)) != 0) { + hfs_global_shared_lock_release(hfsmp); + return (hfsmp->hfs_privdir_attr.ca_fileid); + } + } + if (hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_SHARED, p) == 0) { + (void)cat_update(hfsmp, &hfsmp->hfs_privdir_desc, + &hfsmp->hfs_privdir_attr, NULL, NULL); + (void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, p); + } + if (hfsmp->jnl) { + journal_end_transaction(hfsmp->jnl); + } + hfs_global_shared_lock_release(hfsmp); + } + return (hfsmp->hfs_privdir_attr.ca_fileid); -/************************************************************************/ -/* 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 */ -/* */ -/* */ -/************************************************************************/ + } else if (hfsmp->hfs_flags & HFS_READ_ONLY) { -short hfs_vcreate(ExtendedVCB *vcb, hfsCatalogInfo *catInfo, UInt8 forkType, struct vnode **vpp) -{ - struct hfsnode *hp; - struct vnode *vp; - struct hfsmount *hfsmp; - struct hfsfilemeta *fm; - struct mount *mp; - struct vfsFCB *xfcb; - dev_t dev; - short retval; - - hfsmp = VCBTOHFS(vcb); - mp = HFSTOVFS(hfsmp); - dev = hfsmp->hfs_raw_dev; - - /* Check if unmount in progress */ - if (mp->mnt_kern_flag & MNTK_UNMOUNT) { - *vpp = NULL; - return (EPERM); + return (0); } - - /* - * 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; + + /* Setup the default attributes */ + bzero(&hfsmp->hfs_privdir_attr, sizeof(struct cat_attr)); + hfsmp->hfs_privdir_attr.ca_mode = S_IFDIR; + hfsmp->hfs_privdir_attr.ca_nlink = 2; + hfsmp->hfs_privdir_attr.ca_itime = vcb->vcbCrDate; + hfsmp->hfs_privdir_attr.ca_mtime = time.tv_sec; + + /* hidden and off the desktop view */ + fndrinfo = (struct FndrDirInfo *)&hfsmp->hfs_privdir_attr.ca_finderinfo; + fndrinfo->frLocation.v = SWAP_BE16 (22460); + fndrinfo->frLocation.h = SWAP_BE16 (22460); + fndrinfo->frFlags |= SWAP_BE16 (kIsInvisible + kNameLocked); + + // XXXdbg + hfs_global_shared_lock_acquire(hfsmp); + if (hfsmp->jnl) { + if ((error = journal_start_transaction(hfsmp->jnl)) != 0) { + hfs_global_shared_lock_release(hfsmp); return (0); + } + } + /* Reserve some space in the Catalog file. */ + if (cat_preflight(hfsmp, CAT_CREATE, &cookie, p) != 0) { + if (hfsmp->jnl) { + journal_end_transaction(hfsmp->jnl); } + hfs_global_shared_lock_release(hfsmp); + return (0); } - MALLOC_ZONE(hp, struct hfsnode *, sizeof(struct hfsnode), M_HFSNODE, M_WAITOK); - bzero((caddr_t)hp, sizeof(struct hfsnode)); - hp->h_nodeflags |= IN_ALLOCATING; - lockinit(&hp->h_lock, PINOD, "hfsnode", 0, 0); - H_FORKTYPE(hp) = forkType; - rl_init(&hp->h_invalidranges); + if (hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_EXCLUSIVE, p) == 0) { + error = cat_create(hfsmp, &hfsmp->hfs_privdir_desc, + &hfsmp->hfs_privdir_attr, &out_desc); - /* - * There were several blocking points since we first - * checked the hash. Now that we're through blocking, - * check the hash again in case we're racing for the - * same hnode. - */ - vp = hfs_vhashget(dev, catInfo->nodeData.cnd_nodeID, forkType); - if (vp != NULL) { - /* We lost the race, use the winner's vnode */ - FREE_ZONE(hp, sizeof(struct hfsnode), M_HFSNODE); - *vpp = vp; - UBCINFOCHECK("hfs_vcreate", vp); - return (0); - } - - /* - * Insert the hfsnode into the hash queue, also if meta exists - * add to sibling list and return the meta address - */ - fm = NULL; - if (SIBLING_FORKTYPE(forkType)) - hfs_vhashins_sibling(dev, catInfo->nodeData.cnd_nodeID, hp, &fm); - else - hfs_vhashins(dev, catInfo->nodeData.cnd_nodeID, hp); - - /* Allocate a new vnode. If unsuccesful, leave after freeing memory */ - if ((retval = getnewvnode(VT_HFS, mp, hfs_vnodeop_p, &vp))) { - hfs_vhashrem(hp); - if (hp->h_nodeflags & IN_WANT) { - hp->h_nodeflags &= ~IN_WANT; - wakeup(hp); - } - FREE_ZONE(hp, sizeof(struct hfsnode), M_HFSNODE); - *vpp = NULL; - return (retval); - } - hp->h_vp = vp; - vp->v_data = hp; - - hp->h_nodeflags &= ~IN_ALLOCATING; - if (hp->h_nodeflags & IN_WANT) { - hp->h_nodeflags &= ~IN_WANT; - wakeup((caddr_t)hp); - } - - /* - * 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); - - /* - * the vnode is finally alive, with the exception of the FCB below, - * It is finally locked and ready for its debutante ball - */ - hp->h_meta = fm; - }; - fm->h_usecount++; - - /* - * Init the File Control Block. - */ - CopyCatalogToFCB(catInfo, vp); - - /* - * Finish vnode initialization. - * Setting the v_type 'stamps' the vnode as 'complete', so should be done almost last. - * - * At this point the vnode should be locked and fully allocated. And ready to be used - * or accessed. (though having it locked prevents most of this, it - * can still be accessed through lists and hashs). - */ - vp->v_type = IFTOVT(hp->h_meta->h_mode); - if ((vp->v_type == VREG) - && (UBCINFOMISSING(vp) || UBCINFORECLAIMED(vp))) { - ubc_info_init(vp); - } - - /* - * Initialize the vnode from the inode, check for aliases, sets the VROOT flag. - * Note that the underlying vnode may have changed. - */ - if ((retval = hfs_vinit(mp, hfs_specop_p, hfs_fifoop_p, &vp))) { - vput(vp); - *vpp = NULL; - return (retval); + (void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, p); } - /* - * Finish inode initialization now that aliasing has been resolved. - */ - hp->h_meta->h_devvp = hfsmp->hfs_devvp; - VREF(hp->h_meta->h_devvp); - - *vpp = vp; - return 0; -} - -void CopyCatalogToObjectMeta(struct hfsCatalogInfo *catalogInfo, struct vnode *vp, struct hfsfilemeta *fm) -{ - 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); + cat_postflight(hfsmp, &cookie, p); - 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); - - - /* 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); - } - 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; - } -#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; - } - - 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; - } - - 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); - } -} - - -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) -{ - 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" : "") )); - - - switch (fileID) - { - case kHFSExtentsFileID: - vp = vcb->extentsRefNum; - break; - - case kHFSCatalogFileID: - vp = vcb->catalogRefNum; - break; - - case kHFSAllocationFileID: - /* bitmap is covered by Extents B-tree locking */ - /* FALL THROUGH */ - default: - panic("hfs_lockmetafile: invalid fileID"); - } - - 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; -} - - -/* - * 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 - * - */ -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; -} - - + if (error) { + if (hfsmp->jnl) { + journal_end_transaction(hfsmp->jnl); + } + hfs_global_shared_lock_release(hfsmp); -void CopyVNodeToCatalogNode (struct vnode *vp, struct CatalogNodeData *nodeData) -{ - ExtendedVCB *vcb; - FCB *fcb; - struct hfsnode *hp; - Boolean isHFSPlus, isResource; - HFSPlusExtentDescriptor *extents; - off_t fileReadLimit; - - hp = VTOH(vp); - vcb = HTOVCB(hp); - fcb = HTOFCB(hp); - isResource = (H_FORKTYPE(hp) == kRsrcFork); - isHFSPlus = (vcb->vcbSigWord == kHFSPlusSigWord); - - /* date and time of last fork modification */ - if (hp->h_meta->h_mtime != 0) - nodeData->cnd_contentModDate = to_hfs_time(hp->h_meta->h_mtime); - - if (isHFSPlus) { - /* Make sure that there is no nodeType/mode mismatch */ - if ((nodeData->cnd_type == kCatalogFolderNode) - && ((hp->h_meta->h_mode & IFMT) != IFDIR)) { - - DBG_ASSERT((hp->h_meta->h_mode & IFMT) == IFDIR); - hp->h_meta->h_mode &= ~IFMT; /* Clear the bad bits */ - hp->h_meta->h_mode |= IFDIR; /* Set the proper one */ - }; - /* date and time of last modification (any kind) */ - if (hp->h_meta->h_ctime != 0) - nodeData->cnd_attributeModDate = to_hfs_time(hp->h_meta->h_ctime); - /* date and time of last access (MacOS X only) */ - if (hp->h_meta->h_atime != 0) - nodeData->cnd_accessDate = to_hfs_time(hp->h_meta->h_atime); - /* hfs_setattr can change the create date */ - if (hp->h_meta->h_crtime != 0) - nodeData->cnd_createDate = to_hfs_time(hp->h_meta->h_crtime); - if (! (hp->h_meta->h_metaflags & IN_UNSETACCESS)) { - nodeData->cnd_adminFlags = hp->h_meta->h_pflags >> 16; - nodeData->cnd_ownerFlags = hp->h_meta->h_pflags & 0x000000FF; - nodeData->cnd_mode = hp->h_meta->h_mode; - nodeData->cnd_ownerID = hp->h_meta->h_uid; - nodeData->cnd_groupID = hp->h_meta->h_gid; - } - }; - - /* the rest only applies to files */ - if (nodeData->cnd_type == kCatalogFileNode) { - if (hp->h_meta->h_pflags & (SF_IMMUTABLE | UF_IMMUTABLE)) { - /* The file is locked: set the locked bit in the catalog. */ - nodeData->cnd_flags |= kHFSFileLockedMask; - } else { - /* The file is unlocked: make sure the locked bit in the catalog is clear. */ - nodeData->cnd_flags &= ~kHFSFileLockedMask; - }; - if (CIRCLEQ_EMPTY(&hp->h_invalidranges)) { - fileReadLimit = fcb->fcbEOF; - } else { - fileReadLimit = CIRCLEQ_FIRST(&hp->h_invalidranges)->rl_start; - }; - if (isResource) { - extents = nodeData->cnd_rsrcfork.extents; - nodeData->cnd_rsrcfork.logicalSize = fileReadLimit; - nodeData->cnd_rsrcfork.totalBlocks = fcb->fcbPLen / vcb->blockSize; - } else { - extents = nodeData->cnd_datafork.extents; - nodeData->cnd_datafork.logicalSize = fileReadLimit; - nodeData->cnd_datafork.totalBlocks = fcb->fcbPLen / vcb->blockSize; - }; - - 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 - } + return (0); } - } - - -/********************************************************************* - - 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); + hfsmp->hfs_privdir_desc.cd_hint = out_desc.cd_hint; + hfsmp->hfs_privdir_desc.cd_cnid = out_desc.cd_cnid; + hfsmp->hfs_privdir_attr.ca_fileid = out_desc.cd_cnid; + hfsmp->hfs_metadata_createdate = vcb->vcbCrDate; + + if (VFS_ROOT(HFSTOVFS(hfsmp), &dvp) == 0) { + dcp = VTOC(dvp); + dcp->c_childhint = out_desc.cd_hint; + dcp->c_nlink++; + dcp->c_entries++; + dcp->c_flag |= C_CHANGE | C_UPDATE; + tv = time; + (void) VOP_UPDATE(dvp, &tv, &tv, 0); + vput(dvp); } - 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; - }; - - /* Now copy it over */ - bcopy(name, tname, namelen+1); - - fm->h_namePtr = tname; - fm->h_namelen = namelen; - - simple_unlock(&hfsmp->hfs_renamelock); - - /* Lastly, free the old, if set */ - if (fname != NULL) - FREE(fname, M_TEMP); - -} - -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); - } - - 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); } - -#endif //HFS_DIAGNOSTIC - - - /* 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 (nodeData->cnm_length > MAXHFSVNODELEN) { - fm->h_metaflags |= IN_LONGNAME; - } else { - fm->h_metaflags &= ~IN_LONGNAME; - }; - - /* 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; + hfs_volupdate(hfsmp, VOL_MKDIR, 1); + if (hfsmp->jnl) { + journal_end_transaction(hfsmp->jnl); } - else { - fm->h_namePtr = nodeData->cnm_nameptr; - } - - fm->h_namelen = nodeData->cnm_length; - - nodeData->cnm_flags |= kCatNameIsConsumed; - nodeData->cnm_flags &= ~kCatNameIsAllocated; - nodeData->cnm_length = 0; - nodeData->cnm_nameptr = (char *)0; - nodeData->cnm_namespace[0] = 0; - - /* Lastly, free the old, if set */ - if (fname != NULL) - FREE(fname, M_TEMP); -} - - - -unsigned long DerivePermissionSummary(uid_t obj_uid, gid_t obj_gid, mode_t obj_mode, struct mount *mp, struct ucred *cred, struct proc *p) { - register gid_t *gp; - unsigned long permissions; - int i; - - /* User id 0 (root) always gets access. */ - if (cred->cr_uid == 0) { - permissions = R_OK | W_OK | X_OK; - goto Exit; - }; - - /* Otherwise, check the owner. */ - if (hfs_catalogentry_owner_rights(obj_uid, mp, cred, p, false) == 0) { - permissions = ((unsigned long)obj_mode & S_IRWXU) >> 6; - goto Exit; - } - - /* Otherwise, check the groups. */ - if (! (mp->mnt_flag & MNT_UNKNOWNPERMISSIONS)) { - for (i = 0, gp = cred->cr_groups; i < cred->cr_ngroups; i++, gp++) { - if (obj_gid == *gp) { - permissions = ((unsigned long)obj_mode & S_IRWXG) >> 3; - goto Exit; - } - }; - }; - - /* Otherwise, settle for 'others' access. */ - permissions = (unsigned long)obj_mode & S_IRWXO; + hfs_global_shared_lock_release(hfsmp); -Exit: - return permissions; -} - - - -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); - -#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); - -#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); - - 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; -} - - - -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; -} - - - -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; - }; - }; - - 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)); -} + cat_releasedesc(&out_desc); - - -void PackVolCommonAttributes(struct attrlist *alist, - struct vnode *root_vp, - struct hfsCatalogInfo *root_catInfo, - void **attrbufptrptr, - void **varbufptrptr) { - void *attrbufptr; - void *varbufptr; - attrgroup_t a; - struct hfsnode *root_hp = VTOH(root_vp); - struct mount *mp = VTOVFS(root_vp); - struct hfsmount *hfsmp = VTOHFS(root_vp); - ExtendedVCB *vcb = HFSTOVCB(hfsmp); - u_long attrlength; - - attrbufptr = *attrbufptrptr; - varbufptr = *varbufptrptr; - - if ((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; - - /* 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; + return (out_desc.cd_cnid); } - - -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); - }; +__private_extern__ +u_long +GetFileInfo(ExtendedVCB *vcb, u_int32_t dirid, char *name, + struct cat_attr *fattr, struct cat_fork *forkinfo) +{ + struct hfsmount * hfsmp; + struct vnode * dvp = NULL; + struct cnode * dcp = NULL; + struct FndrDirInfo * fndrinfo; + struct cat_desc jdesc; + struct timeval tv; + int error; - *attrbufptrptr = attrbufptr; - *varbufptrptr = varbufptr; -} - - + if (vcb->vcbSigWord != kHFSPlusSigWord) + return (0); + hfsmp = VCBTOHFS(vcb); -void PackVolumeInfo(struct attrlist *alist, - struct vnode *root_vp, - struct hfsCatalogInfo *root_catinfo, - void **attrbufptrptr, - void **varbufptrptr) { + memset(&jdesc, 0, sizeof(struct cat_desc)); + jdesc.cd_parentcnid = kRootDirID; + jdesc.cd_nameptr = name; + jdesc.cd_namelen = strlen(name); - PackVolCommonAttributes(alist, root_vp, root_catinfo, attrbufptrptr, varbufptrptr); - PackVolAttributeBlock(alist, root_vp, root_catinfo, attrbufptrptr, varbufptrptr); -}; + /* Lock catalog b-tree */ + error = hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_EXCLUSIVE, current_proc()); + if (error) + return (0); -// 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; + error = cat_lookup(hfsmp, &jdesc, 0, NULL, fattr, forkinfo); - case kCatalogFileNode: - /* Files in an HFS+ catalog can represent many things (regular files, symlinks, block/character devices, ...) */ - if ((HTOVCB(hp)->vcbSigWord == kHFSPlusSigWord) && - (catalogInfo->nodeData.cnd_mode & IFMT)) { - *((fsobj_type_t *)attrbufptr)++ = - IFTOVT((mode_t)catalogInfo->nodeData.cnd_mode); - } else { - *((fsobj_type_t *)attrbufptr)++ = VREG; - }; - break; - - default: - *((fsobj_type_t *)attrbufptr)++ = VNON; - break; - }; - } - if (a & ATTR_CMN_OBJTAG) *((fsobj_tag_t *)attrbufptr)++ = root_vp->v_tag; - if (a & ATTR_CMN_OBJID) { - u_int32_t cnid; - - /* For hard links use the link's cnid */ - if (catalogInfo->nodeData.cnd_iNodeNumCopy != 0) - cnid = catalogInfo->nodeData.cnd_linkCNID; - else - cnid = catalogInfo->nodeData.cnd_nodeID; - ((fsobj_id_t *)attrbufptr)->fid_objno = cnid; - ((fsobj_id_t *)attrbufptr)->fid_generation = 0; - ++((fsobj_id_t *)attrbufptr); - }; - if (a & ATTR_CMN_OBJPERMANENTID) { - u_int32_t cnid; - - /* For hard links use the link's cnid */ - if (catalogInfo->nodeData.cnd_iNodeNumCopy != 0) - cnid = catalogInfo->nodeData.cnd_linkCNID; - else - cnid = catalogInfo->nodeData.cnd_nodeID; - ((fsobj_id_t *)attrbufptr)->fid_objno = cnid; - ((fsobj_id_t *)attrbufptr)->fid_generation = 0; - ++((fsobj_id_t *)attrbufptr); - }; - if (a & ATTR_CMN_PAROBJID) - { - ((fsobj_id_t *)attrbufptr)->fid_objno = catalogInfo->nodeData.cnm_parID; - ((fsobj_id_t *)attrbufptr)->fid_generation = 0; - ++((fsobj_id_t *)attrbufptr); - }; - if (a & ATTR_CMN_SCRIPT) - { - if (HTOVCB(hp)->vcbSigWord == kHFSPlusSigWord) { - *((text_encoding_t *)attrbufptr)++ = catalogInfo->nodeData.cnd_textEncoding; - } else { - *((text_encoding_t *)attrbufptr)++ = VTOHFS(root_vp)->hfs_encoding; - } - }; - if (a & ATTR_CMN_CRTIME) - { - ((struct timespec *)attrbufptr)->tv_sec = to_bsd_time(catalogInfo->nodeData.cnd_createDate); - ((struct timespec *)attrbufptr)->tv_nsec = 0; - ++((struct timespec *)attrbufptr); - }; - if (a & ATTR_CMN_MODTIME) - { - ((struct timespec *)attrbufptr)->tv_sec = to_bsd_time(catalogInfo->nodeData.cnd_contentModDate); - ((struct timespec *)attrbufptr)->tv_nsec = 0; - ++((struct timespec *)attrbufptr); - }; - if (a & ATTR_CMN_CHGTIME) - { - ((struct timespec *)attrbufptr)->tv_sec = to_bsd_time(catalogInfo->nodeData.cnd_attributeModDate); - ((struct timespec *)attrbufptr)->tv_nsec = 0; - ++((struct timespec *)attrbufptr); - }; - if (a & ATTR_CMN_ACCTIME) - { - ((struct timespec *)attrbufptr)->tv_sec = to_bsd_time(catalogInfo->nodeData.cnd_accessDate); - ((struct timespec *)attrbufptr)->tv_nsec = 0; - ++((struct timespec *)attrbufptr); - }; - if (a & ATTR_CMN_BKUPTIME) - { - ((struct timespec *)attrbufptr)->tv_sec = to_bsd_time(catalogInfo->nodeData.cnd_backupDate); - ((struct timespec *)attrbufptr)->tv_nsec = 0; - ++((struct timespec *)attrbufptr); - }; - if (a & ATTR_CMN_FNDRINFO) - { - bcopy (&catalogInfo->nodeData.cnd_finderInfo, attrbufptr, sizeof(catalogInfo->nodeData.cnd_finderInfo)); - (char *)attrbufptr += sizeof(catalogInfo->nodeData.cnd_finderInfo); - }; - if (a & ATTR_CMN_OWNERID) { - if ((VTOVFS(root_vp)->mnt_flag & MNT_UNKNOWNPERMISSIONS) || - ((catalogInfo->nodeData.cnd_mode & IFMT) == 0)) { - *((uid_t *)attrbufptr)++ = - (VTOHFS(root_vp)->hfs_uid == UNKNOWNUID) ? console_user : VTOHFS(root_vp)->hfs_uid; - } else { - *((uid_t *)attrbufptr)++ = - (catalogInfo->nodeData.cnd_ownerID == UNKNOWNUID) ? console_user : catalogInfo->nodeData.cnd_ownerID; - }; - } - if (a & ATTR_CMN_GRPID) { - if ((VTOVFS(root_vp)->mnt_flag & MNT_UNKNOWNPERMISSIONS) || - ((catalogInfo->nodeData.cnd_mode & IFMT) == 0)) { - *((gid_t *)attrbufptr)++ = VTOHFS(root_vp)->hfs_gid; - } else { - *((gid_t *)attrbufptr)++ = catalogInfo->nodeData.cnd_groupID; - }; - } - if (a & ATTR_CMN_ACCESSMASK) { - if (((catalogInfo->nodeData.cnd_mode & IFMT) == 0) -#if OVERRIDE_UNKNOWN_PERMISSIONS - || (VTOVFS(root_vp)->mnt_flag & MNT_UNKNOWNPERMISSIONS) -#endif - ) { - switch (catalogInfo->nodeData.cnd_type) { - case kCatalogFileNode: - /* Files in an HFS+ catalog can represent many things (regular files, symlinks, block/character devices, ...) */ - *((u_long *)attrbufptr)++ = (u_long)(IFREG | (ACCESSPERMS & (u_long)(VTOHFS(root_vp)->hfs_file_mask))); - break; - - case kCatalogFolderNode: - *((u_long *)attrbufptr)++ = (u_long)(IFDIR | (ACCESSPERMS & (u_long)(VTOHFS(root_vp)->hfs_dir_mask))); - break; - - default: - *((u_long *)attrbufptr)++ = (u_long)((catalogInfo->nodeData.cnd_mode & IFMT) | - VTOHFS(root_vp)->hfs_dir_mask); - }; - } else { - *((u_long *)attrbufptr)++ = - (u_long)catalogInfo->nodeData.cnd_mode; - }; - } - if (a & ATTR_CMN_NAMEDATTRCOUNT) *((u_long *)attrbufptr)++ = 0; /* XXX PPD TBC */ - if (a & ATTR_CMN_NAMEDATTRLIST) - { - attrlength = 0; - ((struct attrreference *)attrbufptr)->attr_dataoffset = 0; - ((struct attrreference *)attrbufptr)->attr_length = attrlength; - - /* Advance beyond the space just allocated and round up to the next 4-byte boundary: */ - (char *)varbufptr += attrlength + ((4 - (attrlength & 3)) & 3); - ++((struct attrreference *)attrbufptr); - }; - if (a & ATTR_CMN_FLAGS) { - u_long flags; - - if (catalogInfo->nodeData.cnd_mode & IFMT) - flags = catalogInfo->nodeData.cnd_ownerFlags | - catalogInfo->nodeData.cnd_adminFlags << 16; - else - flags = 0; - - if (catalogInfo->nodeData.cnd_type == kCatalogFileNode) { - if (catalogInfo->nodeData.cnd_flags & kHFSFileLockedMask) - flags |= UF_IMMUTABLE; - else - flags &= ~UF_IMMUTABLE; - }; - *((u_long *)attrbufptr)++ = flags; - }; - if (a & ATTR_CMN_USERACCESS) { - if ((VTOVFS(root_vp)->mnt_flag & MNT_UNKNOWNPERMISSIONS) || - ((catalogInfo->nodeData.cnd_mode & IFMT) == 0)) { - *((u_long *)attrbufptr)++ = - DerivePermissionSummary((VTOHFS(root_vp)->hfs_uid == UNKNOWNUID) ? console_user : VTOHFS(root_vp)->hfs_uid, - VTOHFS(root_vp)->hfs_gid, -#if OVERRIDE_UNKNOWN_PERMISSIONS - (catalogInfo->nodeData.cnd_type == kCatalogFileNode) ? VTOHFS(root_vp)->hfs_file_mask : VTOHFS(root_vp)->hfs_dir_mask, -#else - (catalogInfo->nodeData.cnd_mode & IFMT) ? - (u_long)catalogInfo->nodeData.cnd_mode : - ((catalogInfo->nodeData.cnd_type == kCatalogFileNode) ? - VTOHFS(root_vp)->hfs_file_mask : - VTOHFS(root_vp)->hfs_dir_mask), -#endif - VTOVFS(root_vp), - current_proc()->p_ucred, - current_proc()); - } else { - *((u_long *)attrbufptr)++ = - DerivePermissionSummary((catalogInfo->nodeData.cnd_ownerID == UNKNOWNUID) ? console_user : catalogInfo->nodeData.cnd_ownerID, - catalogInfo->nodeData.cnd_groupID, - (mode_t)catalogInfo->nodeData.cnd_mode, - VTOVFS(root_vp), - current_proc()->p_ucred, - current_proc()); - }; - }; - }; - - *attrbufptrptr = attrbufptr; - *varbufptrptr = varbufptr; -} - - -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; - - hp = VTOH(vp); - - attrbufptr = *attrbufptrptr; - varbufptr = *varbufptrptr; - - 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()); - }; - }; - }; - - *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; - - attrbufptr = *attrbufptrptr; - a = alist->dirattr; - - 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; - }; - - *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; - - attrbufptr = *attrbufptrptr; - - 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; - }; - }; - }; - - *attrbufptrptr = attrbufptr; -} - - - -// 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 ) -{ - 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; - - 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; - - 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 */ - } - 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 0 -void PackForkAttributeBlock(struct attrlist *alist, - struct vnode *vp, - struct hfsCatalogInfo *catInfo, - void **attrbufptrptr, - void **varbufptrptr) { - /* XXX PPD TBC */ -} -#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) -{ - //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? */ - break; - } -} - - - -void PackAttributeBlock(struct attrlist *alist, - struct vnode *vp, - struct hfsCatalogInfo *catInfo, - void **attrbufptrptr, - void **varbufptrptr) -{ - 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; - }; - }; -}; - - - -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 */ - }; - - 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... */ - - 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 - }; - - DBG_ASSERT(a == 0); /* All common attributes for volumes must've been handled by now... */ - - vcb->vcbFlags |= 0xFF00; // Mark the VCB dirty - - VCB_UNLOCK(vcb); -} - - -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; - - 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 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; -} - - - -#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 - - - -#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 - - - -#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; -} -#endif - - - -void UnpackAttributeBlock(struct attrlist *alist, - struct vnode *vp, - struct hfsCatalogInfo *catInfo, - void **attrbufptrptr, - void **varbufptrptr) { - - - 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); - -#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 - -}; - - -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; - - if (allocationBlockSize % baseMultiple != 0) { - /* - Whoops: the allocation blocks aren't even multiples of the specified base: - no amount of dividing them into even parts will be a multiple, either then! - */ - return 512; /* Hope for the best */ - }; - - /* Try the obvious winner first, to prevent 12K allocation blocks, for instance, - from being handled as two 6K logical blocks instead of 3 4K logical blocks. - Even though the former (the result of the loop below) is the larger allocation - block size, the latter is more efficient: */ - if (allocationBlockSize % PAGE_SIZE == 0) return PAGE_SIZE; - - /* No clear winner exists: pick the largest even fraction <= MAXBSIZE: */ - baseBlockCount = allocationBlockSize / baseMultiple; /* Now guaranteed to be an even multiple */ - - for (blockCount = baseBlockCount; blockCount > 0; --blockCount) { - trialBlockSize = blockCount * baseMultiple; - if (allocationBlockSize % trialBlockSize == 0) { /* An even multiple? */ - if ((trialBlockSize <= blockSizeLimit) && - (trialBlockSize % baseMultiple == 0)) { - return trialBlockSize; - }; - }; - }; + (void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, current_proc()); - /* Note: we should never get here, since blockCount = 1 should always work, - but this is nice and safe and makes the compiler happy, too ... */ - return 512; + if (error == 0) { + return (fattr->ca_fileid); + } else if (hfsmp->hfs_flags & HFS_READ_ONLY) { + 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. + * On Journaled HFS, there can be orphaned files. These + * are files that were unlinked while busy. If the volume + * was not cleanly unmounted then some of these files may + * have persisted and need to be removed. */ -u_long -FindMetaDataDirectory(ExtendedVCB *vcb) +__private_extern__ +void +hfs_remove_orphans(struct hfsmount * hfsmp) { - char namep[32]; - hfsCatalogInfo catInfo; - HFSCatalogNodeID dirID; - u_int32_t metadata_createdate; - int retval; - - if (vcb->vcbSigWord != kHFSPlusSigWord) - return (0); + 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 = {0}; + int catlock = 0; + int catreserve = 0; + int started_tr = 0; + int shared_lock = 0; + int result; + + if (hfsmp->hfs_flags & HFS_CLEANED_ORPHANS) + return; - 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); + vcb = HFSTOVCB(hfsmp); + fcb = VTOF(vcb->catalogRefNum); + + btdata.bufferAddress = &filerec; + btdata.itemSize = sizeof(filerec); + btdata.itemCount = 1; + + MALLOC(iterator, struct BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK); + bzero(iterator, sizeof(*iterator)); + keyp = (HFSPlusCatalogKey*)&iterator->key; + keyp->parentID = hfsmp->hfs_privdir_desc.cd_cnid; + + result = hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_EXCLUSIVE, p); + if (result) + goto exit; + /* + * Position the iterator at the folder thread record. + * (i.e. one record before first child) + */ + result = BTSearchRecord(fcb, iterator, NULL, NULL, iterator); + + (void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, p); + if (result) + goto exit; + + /* Visit all the children in the HFS+ private directory. */ + for (;;) { + result = hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_EXCLUSIVE, p); + if (result) + goto exit; + + result = BTIterateRecord(fcb, kBTreeNextRecord, iterator, &btdata, NULL); + + (void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, p); + if (result) + break; + + if (keyp->parentID != hfsmp->hfs_privdir_desc.cd_cnid) + break; + if (filerec.recordType != kHFSPlusFileRecord) + continue; + + (void) utf8_encodestr(keyp->nodeName.unicode, keyp->nodeName.length * 2, + filename, &namelen, sizeof(filename), 0, 0); + + (void) sprintf(tempname, "%s%d", HFS_DELETE_PREFIX, filerec.fileID); + + /* + * Delete all files named "tempxxx", where + * xxx is the file's cnid in decimal. + * + */ + if (bcmp(tempname, filename, namelen) == 0) { + struct filefork dfork = {0}; + struct filefork rfork = {0}; + struct cnode cnode = {0}; + + // XXXdbg + hfs_global_shared_lock_acquire(hfsmp); + shared_lock = 1; + if (hfsmp->jnl) { + if (journal_start_transaction(hfsmp->jnl) != 0) { + goto exit; + } + started_tr = 1; } - } - } + + /* + * Reserve some space in the Catalog file. + */ + if (cat_preflight(hfsmp, CAT_DELETE, &cookie, p) != 0) { + goto exit; + } + catreserve = 1; - /* 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); + /* Lock catalog b-tree */ + if (hfs_metafilelocking(hfsmp, kHFSCatalogFileID, + LK_EXCLUSIVE, p) != 0) { + goto exit; + } + 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_privdir_desc.cd_cnid; + cnode.c_desc.cd_nameptr = 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; + } - return dirID; -} + /* 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) { + fsize -= HFS_BIGFILE_SIZE; + } else { + fsize = 0; + } + + if (TruncateFileC(vcb, (FCB*)&dfork, fsize, false) != 0) { + printf("error truncting 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) { + journal_end_transaction(hfsmp->jnl); + if (journal_start_transaction(hfsmp->jnl) != 0) { + started_tr = 0; + break; + } + } + } + } + + if (rfork.ff_blocks > 0) { + rfork.ff_cp = &cnode; + cnode.c_datafork = NULL; + cnode.c_rsrcfork = &rfork; + if (TruncateFileC(vcb, (FCB*)&rfork, 0, false) != 0) { + printf("error truncting rsrc fork!\n"); + break; + } + } + /* Remove the file record from the Catalog */ + if (cat_delete(hfsmp, &cnode.c_desc, &cnode.c_attr) != 0) { + printf("error deleting cat rec!\n"); + break; + } + + /* Update parent and volume counts */ + hfsmp->hfs_privdir_attr.ca_entries--; + (void)cat_update(hfsmp, &hfsmp->hfs_privdir_desc, + &hfsmp->hfs_privdir_attr, NULL, NULL); + hfs_volupdate(hfsmp, VOL_RMFILE, 0); + + /* Drop locks and end the transaction */ + (void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, p); + cat_postflight(hfsmp, &cookie, p); + catlock = catreserve = 0; + if (started_tr) { + journal_end_transaction(hfsmp->jnl); + started_tr = 0; + } + hfs_global_shared_lock_release(hfsmp); + shared_lock = 0; -static void -RemovedMetaDataDirectory(ExtendedVCB *vcb) -{ - char name[32]; - hfsCatalogInfo catInfo; - int retval; + } /* end if */ + } /* end for */ - strncpy(name, HFSPLUS_PRIVATE_DIR, sizeof(name)); - INIT_CATALOGDATA(&catInfo.nodeData, kCatNameNoCopyName); - - /* lock catalog b-tree */ - retval = hfs_metafilelocking(VCBTOHFS(vcb), kHFSCatalogFileID, LK_SHARED, current_proc()); - if (retval) goto Err_Exit; - - /* 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); +exit: + if (catlock) { + (void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, p); + } + if (catreserve) { + cat_postflight(hfsmp, &cookie, p); + } + if (started_tr) { + journal_end_transaction(hfsmp->jnl); + } + if (shared_lock) { + hfs_global_shared_lock_release(hfsmp); } - /* unlock catalog b-tree */ - (void) hfs_metafilelocking(VCBTOHFS(vcb), kHFSCatalogFileID, LK_RELEASE, current_proc()); - -Err_Exit: - CLEAN_CATALOGDATA(&catInfo.nodeData); + FREE(iterator, M_TEMP); + hfsmp->hfs_flags |= HFS_CLEANED_ORPHANS; } + /* * 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 @@ -3241,7 +1242,7 @@ u_int32_t logBlockSize; logBlockSize = VTOHFS(vp)->hfs_logBlockSize; if (vp->v_flag & VSYSTEM) { - if (VTOH(vp)->fcbBTCBPtr != NULL) { + if (VTOF(vp)->fcbBTCBPtr != NULL) { BTreeInfoRec bTreeInfo; /* @@ -3252,11 +1253,11 @@ u_int32_t logBlockSize; * it in the first place?? */ - (void) BTGetInformation (VTOFCB(vp), kBTreeInfoVersion, &bTreeInfo); + (void) BTGetInformation (VTOF(vp), kBTreeInfoVersion, &bTreeInfo); logBlockSize = bTreeInfo.nodeSize; - } else if (H_FILEID(VTOH(vp)) == kHFSAllocationFileID) { + } else if (VTOC(vp)->c_fileid == kHFSAllocationFileID) { logBlockSize = VTOVCB(vp)->vcbVBMIOSize; } } @@ -3266,468 +1267,757 @@ u_int32_t logBlockSize; return logBlockSize; } +__private_extern__ +u_int32_t +hfs_freeblks(struct hfsmount * hfsmp, int wantreserve) +{ + struct vcb_t *vcb = HFSTOVCB(hfsmp); + u_int32_t freeblks; + + freeblks = vcb->freeBlocks; + if (wantreserve) { + if (freeblks > vcb->reserveBlocks) + freeblks -= vcb->reserveBlocks; + else + freeblks = 0; + } + if (freeblks > vcb->loanedBlocks) + freeblks -= vcb->loanedBlocks; + else + freeblks = 0; + +#ifdef 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 statfs statbuf; /* 272 bytes */ + u_int32_t vfreeblks; + u_int32_t loanedblks; + struct mount * backingfs_mp; + + backingfs_mp = hfsmp->hfs_backingfs_rootvp->v_mount; + + if (VFS_STATFS(backingfs_mp, &statbuf, current_proc()) == 0) { + vfreeblks = statbuf.f_bavail; + /* Normalize block count if needed. */ + if (statbuf.f_bsize != vcb->blockSize) { + vfreeblks = ((u_int64_t)vfreeblks * (u_int64_t)statbuf.f_bsize) / vcb->blockSize; + } + if (vfreeblks > hfsmp->hfs_sparsebandblks) + vfreeblks -= hfsmp->hfs_sparsebandblks; + else + vfreeblks = 0; + + /* Take into account any delayed allocations. */ + loanedblks = 2 * vcb->loanedBlocks; + if (vfreeblks > loanedblks) + vfreeblks -= loanedblks; + else + vfreeblks = 0; + + freeblks = MIN(vfreeblks, freeblks); + } + } +#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) { - if (err > 0) { - DBG_ERR(("MacToVFSError: passing error #%d unchanged...\n", err)); - }; - return err; - }; + if (err >= 0) + return err; - 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 */ - - case fsBTBadNodeSize: + case dskFulErr: /* -34 */ + case btNoSpaceAvail: /* -32733 */ + return ENOSPC; + case fxOvFlErr: /* -32750 */ + return EOVERFLOW; + + case btBadNode: /* -32731 */ + return EBADF; + + case memFullErr: /* -108 */ + return ENOMEM; /* +12 */ + + case cmExists: /* -32718 */ + case btExists: /* -32734 */ + return EEXIST; /* +17 */ + + 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 ERANGE; + + case bdNamErr: /* -37 */ + return ENAMETOOLONG; /* 63 */ + + case paramErr: /* -50 */ + case fileBoundsErr: /* -1309 */ + return EINVAL; /* +22 */ + + case fsBTBadNodeSize: return ENXIO; - default: - DBG_UTILS(("Unmapped MacOS error: %d\n", err)); - return EIO; /* +5 */ + + default: + return EIO; /* +5 */ } } /* - * All of our debugging functions + * Get the directory entry name hint for a given index. + * The directory cnode (dcp) must be locked. */ +__private_extern__ +char * +hfs_getnamehint(struct cnode *dcp, int index) +{ + struct hfs_index *entry; + void *self; + + if (index > 0) { + self = current_act(); + SLIST_FOREACH(entry, &dcp->c_indexlist, hi_link) { + if ((entry->hi_index == index) + && (entry->hi_thread == self)) + return (entry->hi_name); + } + } -#if HFS_DIAGNOSTIC + return (NULL); +} -void debug_vn_status (char* introStr, struct vnode *vn) +/* + * Save a directory entry name hint for a given index. + * The directory cnode (dcp) must be locked. + */ +__private_extern__ +void +hfs_savenamehint(struct cnode *dcp, int index, const char * namehint) { - 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")); - }; + struct hfs_index *entry; + int len; + + if (index > 0) { + len = strlen(namehint); + MALLOC(entry, struct hfs_index *, len + sizeof(struct hfs_index), + M_TEMP, M_WAITOK); + entry->hi_index = index; + entry->hi_thread = current_act(); + bcopy(namehint, entry->hi_name, len + 1); + SLIST_INSERT_HEAD(&dcp->c_indexlist, entry, hi_link); + } } -void debug_vn_print (char* introStr, struct vnode *vn) +/* + * Release the directory entry name hint for a given index. + * The directory cnode (dcp) must be locked. + */ +__private_extern__ +void +hfs_relnamehint(struct cnode *dcp, int index) { -// 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")); - } + struct hfs_index *entry; + void *self; + + if (index > 0) { + self = current_act(); + SLIST_FOREACH(entry, &dcp->c_indexlist, hi_link) { + if ((entry->hi_index == index) + && (entry->hi_thread == self)) { + SLIST_REMOVE(&dcp->c_indexlist, entry, hfs_index, + hi_link); + FREE(entry, M_TEMP); + break; + } + } + } } -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 -) +/* + * Release all directory entry name hints. + */ +__private_extern__ +void +hfs_relnamehints(struct cnode *dcp) { - 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 (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 (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")); - - } - } - + struct hfs_index *entry; + struct hfs_index *next; + + if (!SLIST_EMPTY(&dcp->c_indexlist)) { + for(entry = SLIST_FIRST(&dcp->c_indexlist); + entry != NULL; + entry = next) { + next = SLIST_NEXT(entry, hi_link); + SLIST_REMOVE(&dcp->c_indexlist, entry, hfs_index, hi_link); + FREE(entry, M_TEMP); + } + } } -#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); - }; +/* + * Perform a case-insensitive compare of two UTF-8 filenames. + * + * Returns 0 if the strings match. + */ +__private_extern__ +int +hfs_namecmp(const char *str1, size_t len1, const char *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); } -void debug_check_blocksizes(struct vnode *vp) { - struct hfsnode *hp = VTOH(vp); - struct buf *bp; +__private_extern__ +int +hfs_early_journal_init(struct hfsmount *hfsmp, HFSPlusVolumeHeader *vhp, + void *_args, int embeddedOffset, int mdb_offset, + HFSMasterDirectoryBlock *mdbp, struct ucred *cred) +{ + JournalInfoBlock *jibp; + struct buf *jinfo_bp, *bp; + int sectors_per_fsblock, arg_flags=0, arg_tbufsz=0; + int retval, blksize = hfsmp->hfs_phys_block_size; + struct vnode *devvp; + struct hfs_mount_args *args = _args; + + devvp = hfsmp->hfs_devvp; + + if (args != NULL && (args->flags & HFSFSMNT_EXTENDED_ARGS)) { + arg_flags = args->journal_flags; + arg_tbufsz = args->journal_tbuffer_size; + } + + sectors_per_fsblock = SWAP_BE32(vhp->blockSize) / blksize; + + retval = meta_bread(devvp, + embeddedOffset/blksize + + (SWAP_BE32(vhp->journalInfoBlock)*sectors_per_fsblock), + SWAP_BE32(vhp->blockSize), cred, &jinfo_bp); + if (retval) + return retval; + + jibp = (JournalInfoBlock *)jinfo_bp->b_data; + jibp->flags = SWAP_BE32(jibp->flags); + jibp->offset = SWAP_BE64(jibp->offset); + jibp->size = SWAP_BE64(jibp->size); + + if (jibp->flags & kJIJournalInFSMask) { + hfsmp->jvp = hfsmp->hfs_devvp; + } else { + printf("hfs: journal not stored in fs! don't know what to do.\n"); + brelse(jinfo_bp); + return EINVAL; + } + + // save this off for the hack-y check in hfs_remove() + hfsmp->jnl_start = jibp->offset / SWAP_BE32(vhp->blockSize); + hfsmp->jnl_size = jibp->size; + + if (jibp->flags & kJIJournalNeedInitMask) { + printf("hfs: Initializing the journal (joffset 0x%llx sz 0x%llx)...\n", + jibp->offset + (off_t)embeddedOffset, jibp->size); + hfsmp->jnl = journal_create(hfsmp->jvp, + jibp->offset + (off_t)embeddedOffset, + jibp->size, + devvp, + blksize, + arg_flags, + arg_tbufsz, + hfs_sync_metadata, hfsmp->hfs_mp); + + // no need to start a transaction here... if this were to fail + // we'd just re-init it on the next mount. + jibp->flags &= ~kJIJournalNeedInitMask; + jibp->flags = SWAP_BE32(jibp->flags); + jibp->offset = SWAP_BE64(jibp->offset); + jibp->size = SWAP_BE64(jibp->size); + bwrite(jinfo_bp); + jinfo_bp = NULL; + jibp = NULL; + } else { + //printf("hfs: Opening the journal (joffset 0x%llx sz 0x%llx vhp_blksize %d)...\n", + // jibp->offset + (off_t)embeddedOffset, + // jibp->size, SWAP_BE32(vhp->blockSize)); + + hfsmp->jnl = journal_open(hfsmp->jvp, + jibp->offset + (off_t)embeddedOffset, + jibp->size, + devvp, + blksize, + arg_flags, + arg_tbufsz, + hfs_sync_metadata, hfsmp->hfs_mp); + + 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 = (embeddedOffset / blksize) + HFS_PRI_SECTOR(blksize); + } + retval = meta_bread(devvp, mdb_offset, blksize, cred, &bp); + if (retval) { + brelse(bp); + printf("hfs: failed to reload the mdb after opening the journal (retval %d)!\n", + retval); + return retval; + } + bcopy(bp->b_data + HFS_PRI_OFFSET(blksize), mdbp, 512); + brelse(bp); + bp = NULL; + } + } - if (vp->v_flag & VSYSTEM) return; - for (bp = vp->v_cleanblkhd.lh_first; bp != NULL; bp = bp->b_vnbufs.le_next) { - debug_check_buffersizes(vp, hp, bp); - }; + //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; + } - for (bp = vp->v_dirtyblkhd.lh_first; bp != NULL; bp = bp->b_vnbufs.le_next) { - debug_check_buffersizes(vp, hp, bp); - }; + return 0; } -void debug_check_catalogdata(struct CatalogNodeData *cat) { - if (cat->cnm_nameptr == NULL) { - DBG_ASSERT((cat->cnm_flags & kCatNameIsAllocated) == 0); +// +// 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) +{ + JournalInfoBlock *jibp; + struct buf *jinfo_bp, *bp; + int sectors_per_fsblock, arg_flags=0, arg_tbufsz=0; + int retval, need_flush = 0, write_jibp = 0; + struct vnode *devvp; + struct cat_attr jib_attr, jattr; + struct cat_fork jib_fork, jfork; + ExtendedVCB *vcb; + u_long fid; + struct hfs_mount_args *args = _args; + + devvp = hfsmp->hfs_devvp; + vcb = HFSTOVCB(hfsmp); + + if (args != NULL && (args->flags & HFSFSMNT_EXTENDED_ARGS)) { + if (args->journal_disable) { + return 0; + } + + 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; } - else if (cat->cnm_nameptr == cat->cnm_namespace) { - DBG_ASSERT((cat->cnm_flags & kCatNameIsAllocated) == 0); - } - else { - DBG_ASSERT((cat->cnm_flags & kCatNameIsAllocated) == kCatNameIsAllocated); - } - - if (cat->cnm_nameptr) { - DBG_ASSERT(strlen(cat->cnm_nameptr) == cat->cnm_length); + 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); } - - 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); - } -} + sectors_per_fsblock = SWAP_BE32(vhp->blockSize) / hfsmp->hfs_phys_block_size; + retval = meta_bread(devvp, + vcb->hfsPlusIOPosOffset / hfsmp->hfs_phys_block_size + + (SWAP_BE32(vhp->journalInfoBlock)*sectors_per_fsblock), + SWAP_BE32(vhp->blockSize), NOCRED, &jinfo_bp); + if (retval) { + printf("hfs: can't read journal info block. disabling journaling.\n"); + vcb->vcbAtrb &= ~kHFSVolumeJournaledMask; + return 0; + } + + jibp = (JournalInfoBlock *)jinfo_bp->b_data; + jibp->flags = SWAP_BE32(jibp->flags); + jibp->offset = SWAP_BE64(jibp->offset); + jibp->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); + brelse(jinfo_bp); + vcb->vcbAtrb &= ~kHFSVolumeJournaledMask; + return 0; + } + hfsmp->hfs_jnlfileid = fid; -extern void hfs_vhash_dbg(struct hfsnode *hp); + // make sure the journal file begins where we think it should. + if ((jibp->offset / (u_int64_t)vcb->blockSize) != jfork.cf_extents[0].startBlock) { + printf("hfs: The journal file moved (was: %lld; is: %d). Fixing up\n", + (jibp->offset / (u_int64_t)vcb->blockSize), jfork.cf_extents[0].startBlock); -/* Checks the valicity of a hfs vnode */ -void debug_check_vnode(struct vnode *vp, int stage) { - struct hfsnode *hp; - u_long size; - int i; + jibp->offset = (u_int64_t)jfork.cf_extents[0].startBlock * (u_int64_t)vcb->blockSize; + write_jibp = 1; + } - /* vcb stuff */ - if (VTOHFS(vp)->hfs_mount_flags & kHFSBootVolumeInconsistentMask) - DEBUG_BREAK_MSG(("Volume is damaged!")); + // check the size of the journal file. + if (jibp->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", + jibp->size, (u_int64_t)jfork.cf_extents[0].blockCount*vcb->blockSize); + + jibp->size = (u_int64_t)jfork.cf_extents[0].blockCount * vcb->blockSize; + write_jibp = 1; + } - /* 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")); + if (jibp->flags & kJIJournalInFSMask) { + hfsmp->jvp = hfsmp->hfs_devvp; + } else { + printf("hfs: journal not stored in fs! don't know what to do.\n"); + brelse(jinfo_bp); + return EINVAL; + } + + // save this off for the hack-y check in hfs_remove() + hfsmp->jnl_start = jibp->offset / SWAP_BE32(vhp->blockSize); + hfsmp->jnl_size = jibp->size; + + if (jibp->flags & kJIJournalNeedInitMask) { + printf("hfs: Initializing the journal (joffset 0x%llx sz 0x%llx)...\n", + jibp->offset + (off_t)vcb->hfsPlusIOPosOffset, jibp->size); + hfsmp->jnl = journal_create(hfsmp->jvp, + jibp->offset + (off_t)vcb->hfsPlusIOPosOffset, + jibp->size, + devvp, + hfsmp->hfs_phys_block_size, + arg_flags, + arg_tbufsz, + hfs_sync_metadata, hfsmp->hfs_mp); + + // no need to start a transaction here... if this were to fail + // we'd just re-init it on the next mount. + jibp->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", + // jibp->offset + (off_t)vcb->hfsPlusIOPosOffset, + // jibp->size, SWAP_BE32(vhp->blockSize)); + + hfsmp->jnl = journal_open(hfsmp->jvp, + jibp->offset + (off_t)vcb->hfsPlusIOPosOffset, + jibp->size, + devvp, + hfsmp->hfs_phys_block_size, + arg_flags, + arg_tbufsz, + hfs_sync_metadata, hfsmp->hfs_mp); + } + + + if (write_jibp) { + jibp->flags = SWAP_BE32(jibp->flags); + jibp->offset = SWAP_BE64(jibp->offset); + jibp->size = SWAP_BE64(jibp->size); - /* Check the hash */ - hfs_vhash_dbg(hp); + bwrite(jinfo_bp); + } else { + brelse(jinfo_bp); + } + jinfo_bp = NULL; + jibp = NULL; - /* Check to see if we want to compare with the disk */ - if (stage > 200) { - int retval; - hfsCatalogInfo catInfo; + //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: late jnl init: failed to open/create the journal (retval %d).\n", retval); + return EINVAL; + } - INIT_CATALOGDATA(&catInfo.nodeData, 0); - catInfo.hint = 0; + return 0; +} - if (hfs_metafilelocking(VTOHFS(vp), kHFSCatalogFileID, LK_SHARED, current_proc())) - return; +/* + * 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 -------------------------------> + * + */ +#define GIGABYTE (u_int64_t)(1024*1024*1024) - 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")); +#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) - (void) hfs_metafilelocking(VTOHFS(vp), kHFSCatalogFileID, LK_RELEASE, current_proc()); +static void +hfs_metadatazone_init(struct hfsmount *hfsmp) +{ + ExtendedVCB *vcb; + struct BTreeInfoRec btinfo; + u_int64_t fs_size; + u_int64_t zonesize; + u_int64_t temp; + u_int64_t filesize; + u_int32_t blk; + int items; - 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??? */ + vcb = HFSTOVCB(hfsmp); + fs_size = (u_int64_t)vcb->blockSize * (u_int64_t)vcb->totalBlocks; - CLEAN_CATALOGDATA(&catInfo.nodeData); + /* + * For volumes less than 10 GB, don't bother. + */ + if (fs_size < ((u_int64_t)10 * GIGABYTE)) + return; + /* + * Skip non-journaled volumes as well. + */ + if (hfsmp->jnl == NULL) + return; - } + /* + * Start with allocation bitmap (a fixed size). + */ + zonesize = roundup(vcb->totalBlocks / 8, vcb->vcbVBMIOSize); + /* + * Overflow Extents file gets 4 MB per 100 GB. + */ + items = fs_size / ((u_int64_t)100 * GIGABYTE); + filesize = (u_int64_t)(items + 1) * OVERFLOW_DEFAULT_SIZE; + if (filesize > OVERFLOW_MAXIMUM_SIZE) + filesize = OVERFLOW_MAXIMUM_SIZE; + zonesize += filesize; + hfsmp->hfs_overflow_maxblks = filesize / vcb->blockSize; - /* Check Extents */ - { - for(i = 0, size = 0; i < kHFSPlusExtentDensity; i++) - { - size += hp->fcbExtents[i].blockCount; - } + /* + * Plan for at least 8 MB of journal for each + * 100 GB of disk space (up to a 512 MB). + */ + items = fs_size / ((u_int64_t)100 * GIGABYTE); + filesize = (u_int64_t)(items + 1) * JOURNAL_DEFAULT_SIZE; + if (filesize > JOURNAL_MAXIMUM_SIZE) + filesize = JOURNAL_MAXIMUM_SIZE; + zonesize += filesize; - if (hp->fcbEOF > hp->fcbPLen) - DEBUG_BREAK_MSG(("fcbPLen is smaller than fcbEOF")); + /* + * Catalog file gets 10 MB per 1 GB. + * + * How about considering the current catalog size (used nodes * node size) + * and the current file data size to help estimate the required + * catalog size. + */ + filesize = MIN((fs_size / 1024) * 10, GIGABYTE); + hfsmp->hfs_catalog_maxblks = filesize / vcb->blockSize; + zonesize += filesize; - 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")); + /* + * 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. + */ + 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; + } } - 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")); - } - } + 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); + hfsmp->hfs_hotfile_maxblks = filesize / vcb->blockSize; + zonesize += filesize; - - /* 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")); - } + /* + * 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, vcb->vcbVBMIOSize * 8 * vcb->blockSize); + temp = zonesize - temp; /* temp has extra space */ + filesize += temp / 3; + hfsmp->hfs_catalog_maxblks += (temp - (temp / 3)) / 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; + + /* 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 freeblocks; + + freeblocks = MetaZoneFreeBlocks(vcb); + /* Minus Extents overflow file reserve. */ + freeblocks -= + hfsmp->hfs_overflow_maxblks - VTOF(vcb->extentsRefNum)->ff_blocks; + /* Minus catalog file reserve. */ + freeblocks -= + hfsmp->hfs_catalog_maxblks - VTOF(vcb->catalogRefNum)->ff_blocks; + if (freeblocks < 0) + freeblocks = 0; + + return MIN(freeblocks, hfsmp->hfs_hotfile_maxblks); +} + +/* + * Determine if a file is a "virtual" metadata file. + * This includes journal and quota files. + */ +__private_extern__ +int +hfs_virtualmetafile(struct cnode *cp) +{ + char * filename; + + + if (cp->c_parentcnid != kHFSRootFolderID) + return (0); + filename = cp->c_desc.cd_nameptr; + if (filename == NULL) + return (0); + + if ((strcmp(filename, ".journal") == 0) || + (strcmp(filename, ".journal_info_block") == 0) || + (strcmp(filename, ".quota.user") == 0) || + (strcmp(filename, ".quota.group") == 0) || + (strcmp(filename, ".hotfiles.btree") == 0)) + return (1); + + return (0); } -#endif /* HFS_DIAGNOSTIC */