Source to bsd/hfs/hfs_vfsutils.c
/*
* Copyright (c) 2000 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.
*
* This 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.
*
* @APPLE_LICENSE_HEADER_END@
*/
/* @(#)hfs_vfsutils.c 4.0
*
* (c) 1997-2000 Apple Computer, Inc. All Rights Reserved
*
* hfs_vfsutils.c -- Routines that go between the HFS layer and the VFS.
*
* Change History (most recent first):
*
* 22-Jan-2000 Don Brady Remove calls to MountCheck.
* 7-Sep-1999 Don Brady Add HFS Plus hard-link support.
* 25-Aug-1999 Don Brady Dont't use vcbAlBlSt for HFS plus volumes (2350009).
* 9-Aug-1999 Pat Dirks Added support for ATTR_VOL_ENCODINGSUSED [#2357367].
* 16-Jul-1999 Pat Dirks Fixed PackCommonCatalogInfoAttributeBlock to return full range of possible vnode types [#2317604]
* 15-Jun-1999 Pat Dirks Added support for return of mounted device in hfs_getattrlist [#2345297].
* 9-Jun-1999 Don Brady Cleanup vcb accesses in hfs_MountHFSVolume.
* 3-Jun-1999 Don Brady Remove references to unused/legacy vcb fields (eg vcbXTClpSiz).
* 21-May-1999 Don Brady Add call to hfs_vinit in hfsGet to support mknod.
* 6-Apr-1999 Don Brady Fixed de-reference of NULL dvp in hfsGet.
* 22-Mar-1999 Don Brady Add support for UFS delete semantics.
* 1-Mar-1999 Scott Roberts Dont double MALLOC on long names.
* 23-Feb-1999 Pat Dirks Change incrementing of meta refcount to be done BEFORE lock is acquired.
* 2-Feb-1999 Pat Dirks For volume ATTR_CMN_SCRIPT use vcb->volumeNameEncodingHint instead of 0.
* 10-Mar-1999 Don Brady Removing obsolete code.
* 2-Feb-1999 Don Brady For volume ATTR_CMN_SCRIPT use vcb->volumeNameEncodingHint instead of 0.
* 18-Jan-1999 Pat Dirks Changed CopyCatalogToHFSNode to start with ACCESSPERMS instead of adding
* write access only for unlocked files (now handled via IMMUTABLE setting)
* 7-Dec-1998 Pat Dirks Changed PackCatalogInfoFileAttributeBlock to return proper I/O block size.
* 7-Dec-1998 Don Brady Pack the real text encoding instead of zero.
* 16-Dec-1998 Don Brady Use the root's crtime intead of vcb create time for getattrlist.
* 16-Dec-1998 Don Brady Use the root's crtime intead of vcb create time for getattrlist.
* 2-Dec-1998 Scott Roberts Copy the mdbVN correctly into the vcb.
* 3-Dec-1998 Pat Dirks Added support for ATTR_VOL_MOUNTFLAGS.
* 20-Nov-1998 Don Brady Add support for UTF-8 names.
* 18-Nov-1998 Pat Dirks Changed UnpackCommonAttributeBlock to call wait for hfs_chflags to update catalog entry when changing flags
* 13-Nov-1998 Pat Dirks Changed BestBlockSizeFit to try PAGE_SIZE only and skip check for MAXBSIZE.
* 10-Nov-1998 Pat Dirks Changed CopyCatalogToHFSNode to ensure consistency between lock flag and IMMUTABLE bits.
* 10-Nov-1998 Pat Dirks Added MapFileOffset(), LogicalBlockSize() and UpdateBlockMappingTable() routines.
* 18-Nov-1998 Pat Dirks Changed PackVolAttributeBlock to return proper logical block size
* for ATTR_VOL_IOBLOCKSIZE attribute.
* 3-Nov-1998 Umesh Vaishampayan Changes to deal with "struct timespec"
* change in the kernel.
* 23-Sep-1998 Don Brady In UnpackCommonAttributeBlock simplified setting of gid, uid and mode.
* 10-Nov-1998 Pat Dirks Added MapFileOffset(), LogicalBlockSize() and UpdateBlockMappingTable() routines.
* 17-Sep-1998 Pat Dirks Changed BestBlockSizeFit to try MAXBSIZE and PAGE_SIZE first.
* 8-Sep-1998 Don Brady Fix CopyVNodeToCatalogNode to use h_mtime for contentModDate (instead of h_ctime).
* 4-Sep-1998 Pat Dirks Added BestBlockSizeFit routine.
* 18-Aug-1998 Don Brady Change DEBUG_BREAK_MSG to a DBG_UTILS in MacToVFSError (radar #2262802).
* 30-Jun-1998 Don Brady Add calls to MacToVFSError to hfs/hfsplus mount routines (for radar #2249539).
* 22-Jun-1998 Don Brady Add more error cases to MacToVFSError; all HFS Common errors are negative.
* Changed hfsDelete to call DeleteFile for files.
* 4-Jun-1998 Pat Dirks Changed incorrect references to 'vcbAlBlkSize' to 'blockSize';
* Added hfsCreateFileID.
* 4-Jun-1998 Don Brady Add hfsMoveRename to replace hfsMove and hfsRename. Use VPUT/VRELE macros
* instead of vput/vrele to catch bad ref counts.
* 28-May-1998 Pat Dirks Adjusted for change in definition of ATTR_CMN_NAME and removed ATTR_CMN_RAWDEVICE.
* 7-May-1998 Don Brady Added check for NULL vp to hfs_metafilelocking (radar #2233832).
* 24-Apr-1998 Pat Dirks Fixed AttributeBlockSize to return only length of variable attribute block.
* 4/21/1998 Don Brady Add SUPPORTS_MAC_ALIASES conditional (for radar #2225419).
* 4/21/1998 Don Brady Map cmNotEmpty errors to ENOTEMPTY (radar #2229259).
* 4/21/1998 Don Brady Fix up time/date conversions.
* 4/20/1998 Don Brady Remove course-grained hfs metadata locking.
* 4/18/1998 Don Brady Add VCB locking.
* 4/17/1998 Pat Dirks Fixed PackFileAttributeBlock to return more up-to-date EOF/PEOF info from vnode.
* 4/15/1998 Don Brady Add hasOverflowExtents and hfs_metafilelocking. Use ExtendBTreeFile instead
* of SetEndOfForkProc. Set forktype for system files.
* 4/14/1998 Deric Horn PackCatalogInfoAttributeBlock(), and related packing routines to
* pack attribute data given hfsCatalogInfo, without the objects vnode;
* 4/14/1998 Scott Roberts Add execute priviledges to all hfs objects.
* 4/9/1998 Don Brady Add MDB/VolumeHeader flushing to hfsUnmount;
* 4/8/1998 Don Brady Make sure vcbVRefNum field gets initialized (use MAKE_VREFNUM).
* 4/6/1998 Don Brady Removed calls to CreateVolumeCatalogCache (obsolete).
* 4/06/1998 Scott Roberts Added complex file support.
* 4/02/1998 Don Brady UpdateCatalogNode now takes parID and name as input.
* 3/31/1998 Don Brady Sync up with final HFSVolumes.h header file.
* 3/31/1998 Don Brady Check result from UFSToHFSStr to make sure hfs/hfs+ names are not greater
* than 31 characters.
* 3/30/1998 Don Brady In InitMetaFileVNode set VSYSTEM bit in vnode's v_flag.
* 3/26/1998 Don Brady Cleaned up hfs_MountXXX routines. Removed CloseBtreeFile and OpenBTreeFile.
* Simplified hfsUnmount (removed MacOS specific code).
* 3/17/1998 Don Brady AttributeBlockSize calculation did not account for the size field (4bytes).
* PackVolCommonAttributes and PackCommonAttributeBlock for ATTR_CMN_NAME
* were not setting up the name correctly.
* 3/17/1998 Don Brady Changed CreateCatalogNode interface to take kCatalogFolderNode and
* kCatalogFileNode as type input. Also, force MountCheck to always run.
* 12-nov-1997 Scott Roberts Initially created file.
* 17-Mar-98 ser Broke out and created CopyCatalogToHFSNode()
*
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/stat.h>
#include <sys/attr.h>
#include <sys/mount.h>
#include <sys/lock.h>
#include <kern/mapfs.h>
#include "hfs.h"
#include "hfs_dbg.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
#define kMaxLockedMetaBuffers 32 /* number of locked buffer caches to hold for meta data */
extern int (**hfs_vnodeop_p)();
extern int (**hfs_specop_p)();
extern int (**hfs_fifoop_p)();
extern int count_lock_queue __P((void));
OSErr ValidMasterDirectoryBlock( HFSMasterDirectoryBlock *mdb );
UInt16 DivUp( UInt32 byteRun, UInt32 blockSize );
/* 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)(), int (**fifoops)(), struct vnode **vpp);
extern int readlinknode(ExtendedVCB *vcb, hfsCatalogInfo *catInfo, UInt32 *nodeID);
extern UInt16 CountRootFiles(ExtendedVCB *vcb);
extern OSErr GetVolumeNameFromCatalog(ExtendedVCB *vcb);
static int InitMetaFileVNode(struct vnode *vp, off_t eof, u_long clumpSize, const HFSPlusExtentRecord extents,
HFSCatalogNodeID fileID, void * keyCompareProc);
static void ReleaseMetaFileVNode(struct vnode *vp);
void CopyCatalogToObjectMeta(struct hfsCatalogInfo *catalogInfo, struct vnode *vp, struct hfsfilemeta *fm);
void CopyCatalogToFCB(struct hfsCatalogInfo *catalogInfo, struct vnode *vp);
void hfs_set_metaname(char *name, struct hfsfilemeta *fm);
u_int32_t GetLogicalBlockSize(struct vnode *vp);
/* BTree accessor routines */
extern OSStatus GetBTreeBlock(FileReference vp, UInt32 blockNum, GetBlockOptions options, BlockDescriptor *block);
extern OSStatus SetBTreeBlockSize(FileReference vp, ByteCount blockSize, ItemCount minBlockCount);
extern OSStatus ExtendBTreeFile(FileReference vp, FSSize minEOF, FSSize maxEOF);
extern OSStatus ReleaseBTreeBlock(FileReference vp, BlockDescPtr blockPtr, ReleaseBlockOptions options);
//*******************************************************************************
// Routine: hfs_MountHFSVolume
//
//
//*******************************************************************************
OSErr hfs_MountHFSVolume(struct hfsmount *hfsmp, HFSMasterDirectoryBlock *mdb,
u_long sectors, struct proc *p)
{
ExtendedVCB *vcb = HFSTOVCB(hfsmp);
struct vnode *tmpvnode;
OSErr err;
HFSPlusExtentRecord extents;
DBG_FUNC_NAME("hfs_MountHFSVolume");
DBG_PRINT_FUNC_NAME();
if (hfsmp == nil || mdb == nil) /* exit if bad paramater */
return (EINVAL);
err = ValidMasterDirectoryBlock( mdb ); /* make sure this is an HFS disk */
if (err)
return MacToVFSError(err);
/* don't mount volume if its dirty, it must be cleaned by fsck_hfs */
if ((mdb->drAtrb & kHFSVolumeUnmountedMask) == 0)
return (EINVAL);
/*
* The MDB seems OK: transfer info from it into VCB
* Note - the VCB starts out clear (all zeros)
*
*/
DBG_ASSERT((hfsmp->hfs_raw_dev & 0xFFFF0000) == 0);
vcb->vcbVRefNum = MAKE_VREFNUM(hfsmp->hfs_raw_dev);
vcb->vcbSigWord = mdb->drSigWord;
vcb->vcbCrDate = LocalToUTC(mdb->drCrDate);
vcb->vcbLsMod = LocalToUTC(mdb->drLsMod);
vcb->vcbAtrb = mdb->drAtrb;
vcb->vcbNmFls = mdb->drNmFls;
vcb->vcbVBMSt = mdb->drVBMSt;
vcb->nextAllocation = mdb->drAllocPtr;
vcb->totalBlocks = mdb->drNmAlBlks;
vcb->blockSize = mdb->drAlBlkSiz;
vcb->vcbClpSiz = mdb->drClpSiz;
vcb->vcbAlBlSt= mdb->drAlBlSt;
vcb->vcbNxtCNID = mdb->drNxtCNID;
vcb->freeBlocks = mdb->drFreeBks;
vcb->vcbVolBkUp = LocalToUTC(mdb->drVolBkUp);
vcb->vcbVSeqNum = mdb->drVSeqNum;
vcb->vcbWrCnt = mdb->drWrCnt;
vcb->vcbNmRtDirs = mdb->drNmRtDirs;
vcb->vcbFilCnt = mdb->drFilCnt;
vcb->vcbDirCnt = mdb->drDirCnt;
bcopy(mdb->drFndrInfo, vcb->vcbFndrInfo, sizeof(vcb->vcbFndrInfo));
vcb->nextAllocation = mdb->drAllocPtr;
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
*/
/* XXX need to supply real UTF-8 string! */
bcopy( &mdb->drVN[1], vcb->vcbVN, mdb->drVN[0]);
vcb->vcbVN[mdb->drVN[0]] = '\0';
vcb->altIDSector = sectors - 2;
// 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);
// XXX PPD: Should check here for hardware lock flag and set flags in VCB/MP appropriately
VCB_LOCK_INIT(vcb);
/*
* Set up Extents B-tree vnode...
*/
err = GetInitializedVNode(hfsmp, &tmpvnode);
if (err) goto MtVolErr;
HFSToHFSPlusExtents(mdb->drXTExtRec, extents);
err = InitMetaFileVNode(tmpvnode, mdb->drXTFlSize, mdb->drXTClpSiz, extents,
kHFSExtentsFileID, CompareExtentKeys);
if (err) goto MtVolErr;
/*
* Set up Catalog B-tree vnode...
*/
err = GetInitializedVNode(hfsmp, &tmpvnode);
if (err) goto MtVolErr;
HFSToHFSPlusExtents(mdb->drCTExtRec, extents);
err = InitMetaFileVNode(tmpvnode, mdb->drCTFlSize, mdb->drCTClpSiz, extents,
kHFSCatalogFileID, CompareCatalogKeys);
if (err) goto MtVolErr;
/* mark the volume dirty (clear clean unmount bit) */
vcb->vcbAtrb &= ~kHFSVolumeUnmountedMask;
/*
* all done with b-trees so we can unlock now...
*/
VOP_UNLOCK(vcb->catalogRefNum, 0, p);
VOP_UNLOCK(vcb->extentsRefNum, 0, p);
err = noErr;
if ( err == noErr )
{
if ( !(vcb->vcbAtrb & kHFSVolumeHardwareLockMask) ) // if the disk is not write protected
{
MarkVCBDirty( vcb ); // mark VCB dirty so it will be written
}
}
goto CmdDone;
//-- Release any resources allocated so far before exiting with an error:
MtVolErr:;
ReleaseMetaFileVNode(vcb->catalogRefNum);
ReleaseMetaFileVNode(vcb->extentsRefNum);
CmdDone:;
return( err );
}
//*******************************************************************************
// Routine: hfs_MountHFSPlusVolume
//
//
//*******************************************************************************
OSErr hfs_MountHFSPlusVolume(struct hfsmount *hfsmp, HFSPlusVolumeHeader *vhp,
u_long embBlkOffset, u_long sectors, struct proc *p)
{
register ExtendedVCB *vcb;
HFSPlusForkData *fdp;
struct vnode *tmpvnode;
OSErr retval;
if (hfsmp == nil || vhp == nil) /* exit if bad paramater */
return (EINVAL);
DBG_VFS(("hfs_MountHFSPlusVolume: signature=0x%x, version=%d, blockSize=%ld\n", vhp->signature, vhp->version, vhp->blockSize));
retval = ValidVolumeHeader(vhp); /* make sure this is an HFS Plus disk */
if (retval)
return MacToVFSError(retval);
/* don't mount a writable volume if its dirty, it must be cleaned by fsck_hfs */
if (hfsmp->hfs_fs_ronly == 0 && (vhp->attributes & kHFSVolumeUnmountedMask) == 0)
return (EINVAL);
/*
* 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 = vhp->signature;
vcb->vcbCrDate = LocalToUTC(vhp->createDate); // NOTE: local time, not GMT!
vcb->vcbLsMod = vhp->modifyDate;
vcb->vcbAtrb = (UInt16) vhp->attributes; // VCB only uses lower 16 bits
vcb->vcbClpSiz = vhp->rsrcClumpSize;
vcb->vcbNxtCNID = vhp->nextCatalogID;
vcb->vcbVolBkUp = vhp->backupDate;
vcb->vcbWrCnt = vhp->writeCount;
vcb->vcbFilCnt = vhp->fileCount;
vcb->vcbDirCnt = vhp->folderCount;
/* copy 32 bytes of Finder info */
bcopy(vhp->finderInfo, vcb->vcbFndrInfo, sizeof(vhp->finderInfo));
vcb->vcbAlBlSt = 0; /* hfs+ allocation blocks start at first block of volume */
vcb->vcbWrCnt++; /* compensate for write of Volume Header on last flush */
VCB_LOCK_INIT(vcb);
/* Now fill in the Extended VCB info */
vcb->nextAllocation = vhp->nextAllocation;
vcb->totalBlocks = vhp->totalBlocks;
vcb->freeBlocks = vhp->freeBlocks;
vcb->blockSize = vhp->blockSize;
vcb->checkedDate = vhp->checkedDate;
vcb->encodingsBitmap = vhp->encodingsBitmap;
vcb->hfsPlusIOPosOffset = embBlkOffset * 512;
vcb->altIDSector = embBlkOffset + sectors - 2;
/* 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);
// 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;
/*
* Set up Extents B-tree vnode...
*/
retval = GetInitializedVNode(hfsmp, &tmpvnode);
if (retval) goto ErrorExit;
fdp = &vhp->extentsFile;
retval = InitMetaFileVNode(tmpvnode, fdp->logicalSize, fdp->clumpSize, fdp->extents,
kHFSExtentsFileID, CompareExtentKeysPlus);
if (retval) goto ErrorExit;
/*
* Set up Catalog B-tree vnode...
*/
retval = GetInitializedVNode(hfsmp, &tmpvnode);
if (retval) goto ErrorExit;
fdp = &vhp->catalogFile;
retval = InitMetaFileVNode(tmpvnode, fdp->logicalSize, fdp->clumpSize, fdp->extents,
kHFSCatalogFileID, CompareExtendedCatalogKeys);
if (retval) goto ErrorExit;
/*
* Set up Allocation file vnode...
*/
retval = GetInitializedVNode(hfsmp, &tmpvnode);
if (retval) goto ErrorExit;
fdp = &vhp->allocationFile;
retval = InitMetaFileVNode(tmpvnode, fdp->logicalSize, fdp->clumpSize, fdp->extents,
kHFSAllocationFileID, NULL);
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;
/* 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);
/*
* all done with metadata files so we can unlock now...
*/
VOP_UNLOCK(vcb->allocationsRefNum, 0, p);
VOP_UNLOCK(vcb->catalogRefNum, 0, p);
VOP_UNLOCK(vcb->extentsRefNum, 0, p);
if ( !(vcb->vcbAtrb & kHFSVolumeHardwareLockMask) ) // if the disk is not write protected
{
MarkVCBDirty( vcb ); // mark VCB dirty so it will be written
}
DBG_VFS(("hfs_MountHFSPlusVolume: returning (%d)\n", retval));
return (0);
ErrorExit:
/*
* A fatal error occured and the volume cannot be mounted
* release any resources that we aquired...
*/
DBG_VFS(("hfs_MountHFSPlusVolume: fatal error (%d)\n", retval));
InvalidateCatalogCache(vcb);
ReleaseMetaFileVNode(vcb->allocationsRefNum);
ReleaseMetaFileVNode(vcb->catalogRefNum);
ReleaseMetaFileVNode(vcb->extentsRefNum);
return (retval);
}
/*
* ReleaseMetaFileVNode
*
* vp L - -
*/
static void ReleaseMetaFileVNode(struct vnode *vp)
{
if (vp)
{
FCB *fcb = VTOFCB(vp);
if (fcb->fcbBTCBPtr != NULL)
(void) BTClosePath(fcb); /* ignore errors since there is only one path open */
/* 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) */
if (keyCompareProc != NULL) {
result = BTOpenPath(fcb,
(KeyCompareProcPtr) keyCompareProc,
GetBTreeBlock,
ReleaseBTreeBlock,
ExtendBTreeFile,
SetBTreeBlockSize);
result = MacToVFSError(result);
}
return (result);
}
/*************************************************************
*
* Unmounts a hfs volume.
* At this point vflush() has been called (to dump all non-metadata files)
*
*************************************************************/
short hfsUnmount( register struct hfsmount *hfsmp, struct proc *p)
{
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 (vcb->vcbSigWord == kHFSPlusSigWord)
ReleaseMetaFileVNode(vcb->allocationsRefNum);
ReleaseMetaFileVNode(vcb->catalogRefNum);
ReleaseMetaFileVNode(vcb->extentsRefNum);
return (retval);
}
/*
* 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
*/
short hfsLookup (ExtendedVCB *vcb, UInt32 parentDirID, char *name, short len, hfsCatalogInfo *catInfo)
{
OSErr result;
UInt32 length;
struct FInfo *fip;
if (len == -1 ) { /* Convert it to MacOS terms */
if (name)
length = strlen(name);
else
length = kUndefinedStrLen;
}
else
length = len;
result = GetCatalogNode(vcb, parentDirID, name, length, catInfo->hint, &catInfo->spec, &catInfo->nodeData, &catInfo->hint);
#if HFS_HARDLINKS
if (result)
goto exit;
fip = (struct FInfo *) &catInfo->nodeData.cnd_finderInfo;
/*
* if we encounter a link node (hardlink) then auto resolve it...
*/
if ((catInfo->nodeData.cnd_type == kCatalogFileNode) &&
(fip->fdType == kHardLinkFileType) &&
(fip->fdCreator == kHardLinkCreator) &&
(fip->fdFlags & kIsAlias)
) {
FSSpec nodeSpec;
UInt32 dataNodeID;
result = readlinknode(vcb, catInfo, &dataNodeID);
if (result)
goto exit;
/*
* Get nodeData from the data node file.
* Use a local spec so that catInfo->spec is preserved
*/
result = GetCatalogNode(vcb, dataNodeID, NULL, 0, 0, &nodeSpec, &catInfo->nodeData, &catInfo->hint);
/* make sure there's at lease 1 reference */
if (result == 0) {
if (catInfo->nodeData.cnd_linkCount == 0)
catInfo->nodeData.cnd_linkCount = 1;
/* Node should be in private metadata dir */
DBG_ASSERT(nodeSpec.parID == VCBTOHFS(vcb)->hfs_private_metadata_dir);
}
}
exit:
#endif
if (result)
DBG_ERR(("on Lookup, GetCatalogNode returned: %d: dirid: %ld name: %s\n", result, parentDirID, name));
return MacToVFSError(result);
}
short hfsDelete (ExtendedVCB *vcb, UInt32 parentDirID, StringPtr name, short isfile, UInt32 catalogHint)
{
OSErr result = noErr;
/* XXX have all the file's blocks been flushed/trashed? */
/*
* DeleteFile will delete the catalog node and then
* free up any disk space used by the file.
*/
if (isfile)
result = DeleteFile(vcb, parentDirID, name, catalogHint);
else /* is a directory */
result = DeleteCatalogNode(vcb, parentDirID, name, catalogHint);
if (result)
DBG_ERR(("on Delete, DeleteFile returned: %d: dirid: %ld name: %s\n", result, parentDirID, name));
return MacToVFSError(result);
}
short hfsMoveRename (ExtendedVCB *vcb, UInt32 oldDirID, char *oldName, UInt32 newDirID, char *newName, UInt32 *hint)
{
OSErr result = noErr;
result = MoveRenameCatalogNode(vcb, oldDirID,oldName, *hint, newDirID, newName, hint);
if (result)
DBG_ERR(("on hfsMoveRename, MoveRenameCatalogNode returned: %d: newdirid: %ld newname: %s\n", result, newDirID, newName));
return MacToVFSError(result);
}
/* XXX SER pass back the hint so other people can use it */
short hfsCreate(ExtendedVCB *vcb, UInt32 dirID, char *name, int mode)
{
OSErr result = noErr;
HFSCatalogNodeID catalogNodeID;
UInt32 catalogHint;
UInt32 type;
/* just test for directories, the default is to create a file (like symlinks) */
if ((mode & IFMT) == IFDIR)
type = kCatalogFolderNode;
else
type = kCatalogFileNode;
result = CreateCatalogNode (vcb, dirID, name, type, &catalogNodeID, &catalogHint);
return MacToVFSError(result);
}
short hfsCreateFileID (ExtendedVCB *vcb, UInt32 parentDirID, StringPtr name, UInt32 catalogHint, UInt32 *fileIDPtr)
{
return MacToVFSError(CreateFileIDRef(vcb, parentDirID, name, catalogHint, fileIDPtr));
}
/********************************************************************************/
/* */
/* hfs_vget_catinfo - Returns a vnode derived from a hfs catInfo struct */
/* */
/********************************************************************************/
int hfs_vget_catinfo(struct vnode *parent_vp, struct hfsCatalogInfo *catInfo, u_int32_t forkType, struct vnode **target_vp)
{
int retval = E_NONE;
*target_vp = hfs_vhashget(H_DEV(VTOH(parent_vp)), catInfo->nodeData.cnd_nodeID, forkType);
if (*target_vp == NULL) {
if (forkType == kAnyFork)
if (catInfo->nodeData.cnd_type == kCatalogFolderNode)
forkType = kDirectory;
else
forkType = kDataFork;
retval = hfs_vcreate( VTOVCB(parent_vp), catInfo, forkType, target_vp);
};
return (retval);
}
/********************************************************************************/
/* */
/* hfs_vget_fork - Returns a vnode derived from a sibling */
/* vp is locked */
/* */
/********************************************************************************/
int hfs_vget_sibling(struct vnode *vp, u_int16_t forkType, struct vnode **vpp)
{
struct vnode * target_vp = NULL;
int retval = E_NONE;
DBG_ASSERT(vp != NULL);
DBG_ASSERT(VTOH(vp) != NULL);
DBG_ASSERT(VTOH(vp)->h_meta != NULL);
DBG_ASSERT(forkType==kDataFork || forkType==kRsrcFork);
target_vp = hfs_vhashget(H_DEV(VTOH(vp)), H_FILEID(VTOH(vp)), forkType);
/*
* If not in the hash, then we have to create it
*/
if (target_vp == NULL) {
struct proc *p = current_proc();
hfsCatalogInfo catInfo;
/* lock catalog b-tree */
retval = hfs_metafilelocking(VTOHFS(vp), kHFSCatalogFileID, LK_SHARED, p);
if (retval)
goto Err_Exit;
catInfo.hint = H_HINT(VTOH(vp));
retval = hfsLookup (VTOVCB(vp), H_DIRID(VTOH(vp)), H_NAME(VTOH(vp)), VTOH(vp)->h_meta->h_namelen, &catInfo);
/* unlock catalog b-tree */
(void) hfs_metafilelocking(VTOHFS(vp), kHFSCatalogFileID, LK_RELEASE, p);
if (retval)
goto Err_Exit;
retval = hfs_vcreate( VTOVCB(vp), &catInfo, forkType, &target_vp);
};
Err_Exit:
if (!retval) {
DBG_ASSERT(target_vp!=NULL);
} else {
DBG_ASSERT(target_vp==NULL);
}
*vpp = target_vp;
return (retval);
}
/************************************************************************/
/* hfs_vcreate - Returns a vnode derived from hfs */
/* */
/* When creating the vnode, care must be made to set the */
/* correct fields in the correct order. Calls to malloc() */
/* and other subroutines, can cause a context switch, */
/* and the fields must be ready for the possibility */
/* */
/* */
/************************************************************************/
short hfs_vcreate(ExtendedVCB *vcb, hfsCatalogInfo *catInfo, UInt8 forkType, struct vnode **vpp)
{
struct hfsnode *hp;
struct vnode *vp;
struct hfsmount *hfsmp;
struct hfsfilemeta *fm;
struct mount *mp;
struct vfsFCB *xfcb;
dev_t dev;
short retval;
#if HFS_DIAGNOSTIC
DBG_ASSERT(vcb != NULL);
DBG_ASSERT(catInfo != NULL);
DBG_ASSERT(vpp != NULL);
DBG_ASSERT((forkType == kDirectory) || (forkType == kDataFork) || (forkType == kRsrcFork));
if (catInfo->nodeData.cnd_type == kCatalogFolderNode) {
DBG_ASSERT(forkType == kDirectory);
} else {
DBG_ASSERT(forkType != kDirectory);
}
#endif
hfsmp = VCBTOHFS(vcb);
mp = HFSTOVFS(hfsmp);
dev = hfsmp->hfs_raw_dev;
/* Check if unmount in progress */
if (mp->mnt_flag & MNT_UNMOUNT) {
return (EPERM);
}
DBG_UTILS(("\thfs_vcreate: On '%s' with forktype of %d, nodeType of 0x%08lX\n", catInfo->spec.name, forkType, (unsigned long)catInfo->nodeData.cnd_type));
/* Must malloc() here, since getnewvnode() can sleep */
MALLOC(hp, struct hfsnode *, sizeof(struct hfsnode), M_HFSNODE, M_WAITOK);
bzero((caddr_t)hp, sizeof(struct hfsnode));
/*
* Set that this node is in the process of being allocated
* Set it as soon as possible, so context switches well always hit upon it.
* if this is set then wakeup() MUST be called on hp after the flag is cleared
* DO NOT exit without clearing and waking up !!!!
*/
hp->h_nodeflags |= IN_ALLOCATING; /* Mark this as being allocating */
lockinit(&hp->h_lock, PINOD, "hfsnode", 0, 0);
/* getnewvnode() does a VREF() on the vnode */
/* Allocate a new vnode. If unsuccesful, leave after freeing memory */
if ((retval = getnewvnode(VT_HFS, mp, hfs_vnodeop_p, &vp))) {
wakeup(hp); /* Shouldnt happen, but just to make sure */
FREE (hp, M_HFSNODE);
*vpp = NULL;
return (retval);
};
/*
* Set the essentials before locking it down
*/
hp->h_vp = vp; /* Make HFSTOV work */
vp->v_data = hp; /* Make VTOH work */
H_FORKTYPE(hp) = forkType;
fm = NULL;
/*
* Lock the hfsnode and insert the hfsnode into the hash queue, also if meta exists
* add to sibling list and return the meta address
*/
if (SIBLING_FORKTYPE(forkType))
hfs_vhashins_sibling(dev, catInfo->nodeData.cnd_nodeID, hp, &fm);
else
hfs_vhashins(dev, catInfo->nodeData.cnd_nodeID, hp);
/*
* If needed allocate and init the object meta data:
*/
if (fm == NULL) {
/* Allocate it....remember we can do a context switch here */
MALLOC(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);
};
simple_lock_init(&fm->h_metalock);
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);
if (forkType != kDirectory)
UpdateBlockMappingTable(hp);
/*
* 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);
/*
* 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))) {
wakeup((caddr_t)hp);
vput(vp);
*vpp = NULL;
return (retval);
}
/*
* Finish inode initialization now that aliasing has been resolved.
*/
hp->h_meta->h_devvp = hfsmp->hfs_devvp;
VREF(hp->h_meta->h_devvp);
hp->h_valid = HFS_VNODE_MAGIC;
hp->h_nodeflags &= ~IN_ALLOCATING; /* vnode is completely initialized */
/* Wake up anybody waiting for us to finish..see hfs_vhash.c */
wakeup((caddr_t)hp);
#if HFS_DIAGNOSTIC
/* Lets do some testing here */
DBG_ASSERT(hp->h_meta);
DBG_ASSERT(VTOH(vp)==hp);
DBG_ASSERT(HTOV(hp)==vp);
DBG_ASSERT(hp->h_meta->h_usecount>=1 && hp->h_meta->h_usecount<=2);
if (catInfo->nodeData.cnd_type == kCatalogFolderNode) {
DBG_ASSERT(vp->v_type == VDIR);
DBG_ASSERT(H_FORKTYPE(VTOH(vp)) == kDirectory);
}
#endif // HFS_DIAGNOSTIC
*vpp = vp;
return 0;
}
void CopyCatalogToObjectMeta(struct hfsCatalogInfo *catalogInfo, struct vnode *vp, struct hfsfilemeta *fm)
{
ExtendedVCB *vcb = VTOVCB(vp);
Boolean isHFSPlus, isDirectory;
ushort finderFlags;
DBG_ASSERT (fm != NULL);
DBG_UTILS(("\tCopying to file's meta data: name:%s, nodeid:%ld\n", catalogInfo->spec.name, catalogInfo->nodeData.cnd_nodeID));
isHFSPlus = (vcb->vcbSigWord == kHFSPlusSigWord);
isDirectory = (catalogInfo->nodeData.cnd_type == kCatalogFolderNode);
finderFlags = ((struct FInfo *)(&catalogInfo->nodeData.cnd_finderInfo))->fdFlags;
/* Copy over the dirid, and hint */
fm->h_nodeID = catalogInfo->nodeData.cnd_nodeID;
fm->h_dirID = catalogInfo->spec.parID;
fm->h_hint = catalogInfo->hint;
/* Copy over the name */
hfs_set_metaname(catalogInfo->spec.name, fm);
DBG_ASSERT (fm->h_namelen == strlen(catalogInfo->spec.name));
DBG_ASSERT (strcmp(fm->h_namePtr, catalogInfo->spec.name) == 0);
/* 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_permissions & IFMT)) {
fm->h_uid = catalogInfo->nodeData.cnd_ownerID;
fm->h_gid = catalogInfo->nodeData.cnd_groupID;
/* The 32-bit permissions field is unpacked to yield the two significant bytes of flags and the
mode as follows:
+------------------------------------+
permissions: | A | B | mode |
+------------------------------------+
|
V
+------------------------------------+
fm->h_pflags: |XXXXXXXX| A |XXXXXXXX| B |
+------------------------------------+
*/
fm->h_pflags = (((catalogInfo->nodeData.cnd_permissions & 0xFF000000) >> 8) | /* A */
((catalogInfo->nodeData.cnd_permissions & 0x00FF0000) >> 16)); /* B */
fm->h_mode = (mode_t)(catalogInfo->nodeData.cnd_permissions & 0x0000FFFF);
fm->h_rdev = catalogInfo->nodeData.cnd_specialDevice;
#if HFS_HARDLINKS
if (catalogInfo->nodeData.cnd_type == kCatalogFileNode &&
catalogInfo->nodeData.cnd_linkCount > 0) {
fm->h_nlink = catalogInfo->nodeData.cnd_linkCount;
fm->h_metaflags |= IN_DATANODE;
}
#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;
/* 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_valence = 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 = vcb->blockSize *
(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 = catalogInfo->nodeData.cnd_rsrcfork.totalBlocks * vcb->blockSize;
fcb->fcbFlags |= fcbResourceMask;
} else {
fcb->fcbEOF = catalogInfo->nodeData.cnd_datafork.logicalSize;
fcb->fcbPLen = catalogInfo->nodeData.cnd_datafork.totalBlocks * 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;
}
void CopyVNodeToCatalogNode (struct vnode *vp, struct CatalogNodeData *nodeData)
{
ExtendedVCB *vcb;
FCB *fcb;
struct hfsnode *hp;
Boolean isHFSPlus, isResource;
HFSPlusExtentDescriptor *extents;
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 */
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) */
nodeData->cnd_attributeModDate = to_hfs_time(hp->h_meta->h_ctime);
/* date and time of last access (MacOS X only) */
nodeData->cnd_accessDate = to_hfs_time(hp->h_meta->h_atime);
if (! (hp->h_meta->h_metaflags & IN_UNSETACCESS)) {
/* This is a tricky stuff-job: the pflags longword has two bytes of significance and they're
combined with the mode field to yield a 32-bit permissions field as follows:
+------------------------------------+
hp->h_meta->h_pflags: |XXXXXXXX| A |XXXXXXXX| B |
+------------------------------------+
|
V
+------------------------------------+
permissions: | A | B | mode |
+------------------------------------+
*/
nodeData->cnd_permissions = (((hp->h_meta->h_pflags << 8) & 0xFF000000) | /* A */
((hp->h_meta->h_pflags << 16) & 0x00FF0000) | /* B */
(hp->h_meta->h_mode & 0x0000FFFF));
nodeData->cnd_ownerID = hp->h_meta->h_uid;
nodeData->cnd_groupID = hp->h_meta->h_gid;
nodeData->cnd_specialDevice = hp->h_meta->h_rdev;
};
};
/* 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 (isResource) {
extents = nodeData->cnd_rsrcfork.extents;
nodeData->cnd_rsrcfork.logicalSize = fcb->fcbEOF;
nodeData->cnd_rsrcfork.totalBlocks = fcb->fcbPLen / vcb->blockSize;
} else {
extents = nodeData->cnd_datafork.extents;
nodeData->cnd_datafork.logicalSize = fcb->fcbEOF;
nodeData->cnd_datafork.totalBlocks = fcb->fcbPLen / vcb->blockSize;
};
bcopy ( fcb->fcbExtents, extents, sizeof(HFSPlusExtentRecord));
if (vp->v_type == VLNK) {
((struct FInfo *)(&nodeData->cnd_finderInfo))->fdType = kSymLinkFileType;
((struct FInfo *)(&nodeData->cnd_finderInfo))->fdCreator = kSymLinkCreator;
/* Set this up as an alias */
#if SUPPORTS_MAC_ALIASES
((struct FInfo *)(&nodeData->cnd_finderInfo))->fdFlags |= kIsAlias;
#endif
}
#if HFS_HARDLINKS
nodeData->cnd_linkCount = hp->h_meta->h_nlink;
#endif
}
}
void MapFileOffset(struct hfsnode *hp,
off_t filePosition,
daddr_t *logBlockNumber,
long *blockSize,
long *blockOffset) {
off_t extentOffset = filePosition;
daddr_t precedingBlockCount;
unsigned long logicalBlockSize = 0;
unsigned long spaceRemaining;
DBG_IO(("MapFileOffset: hp = 0x%08lX, vp = 0x%08lX ('%s'):\n", (u_long)hp, (u_long)HTOV(hp), H_NAME(hp)));
// DBG_IO(("\tfilePosition 0x%08lX", (u_long)filePosition));
if (filePosition < hp->h_optimizedblocksizelimit) {
int extent;
precedingBlockCount = 0;
for (extent = 0; extent < LOGBLOCKMAPENTRIES; ++extent) {
if ((hp->h_logicalblocktable[extent].logicalBlockCount > 0) &&
(extentOffset < hp->h_logicalblocktable[extent].extentLength)) {
logicalBlockSize = MAXLOGBLOCKSIZE;
spaceRemaining = hp->h_logicalblocktable[extent].extentLength - ((extentOffset / logicalBlockSize) * logicalBlockSize);
*blockSize = MIN(logicalBlockSize, spaceRemaining);
// DBG_IO(("\tUsing entry #%d: preceding blocks = 0x%X, position = 0x%8lX:\n",
// extent, precedingBlockCount, (u_long)extentOffset));
break;
};
precedingBlockCount += hp->h_logicalblocktable[extent].logicalBlockCount;
extentOffset -= hp->h_logicalblocktable[extent].extentLength;
};
DBG_ASSERT(logicalBlockSize > 0);
} else {
// DBG_IO(("\tUsing h_uniformblocksizestart = 0x%X and h_optimizedblocksizelimit = 0x%lX:\n",
// hp->h_uniformblocksizestart, (u_long)hp->h_optimizedblocksizelimit));
precedingBlockCount = hp->h_uniformblocksizestart;
extentOffset -= hp->h_optimizedblocksizelimit;
logicalBlockSize = GetLogicalBlockSize(HTOV(hp));
*blockSize = logicalBlockSize;
};
*logBlockNumber = precedingBlockCount + (extentOffset / logicalBlockSize);
*blockOffset = extentOffset % logicalBlockSize;
DBG_IO(("\tfilePosition 0x%08lX -> logBlockNo = 0x%X, blockSize = 0x%lX, blockOffset = 0x%lX.\n",
(u_long)filePosition,
*logBlockNumber,
*blockSize,
*blockOffset));
}
long LogicalBlockSize(struct hfsnode *hp, daddr_t logicalBlockNumber) {
if (logicalBlockNumber < hp->h_uniformblocksizestart) {
off_t fragmentOffset = 0;
int extent;
for (extent = 0; extent < LOGBLOCKMAPENTRIES; ++extent) {
if ((hp->h_logicalblocktable[extent].logicalBlockCount > 0) &&
(logicalBlockNumber < hp->h_logicalblocktable[extent].logicalBlockCount)) {
// *filePosition = fragmentOffset + (logicalBlockNumber * MAXLOGBLOCKSIZE);
return MIN(MAXLOGBLOCKSIZE, hp->h_logicalblocktable[extent].extentLength - (logicalBlockNumber * MAXLOGBLOCKSIZE));
};
logicalBlockNumber -= hp->h_logicalblocktable[extent].logicalBlockCount;
fragmentOffset += hp->h_logicalblocktable[extent].extentLength;
};
} else {
// *filePosition = hp->h_optimizedblocksizelimit + ((logicalBlockNumber - hp->h_uniformblocksizestart) * HTOHFS(hp)->logBlockSize);
return (GetLogicalBlockSize(HTOV(hp)));
};
DBG_ASSERT(0 /* cannot be reached */);
return 0;
};
void UpdateBlockMappingTableEntry(struct hfsnode *hp, int index, daddr_t firstFragmentBlockNumber, off_t newFragmentLength) {
/* Compute the number of logical blocks, rounding up to include any small trailing fragments: */
hp->h_logicalblocktable[index].logicalBlockCount = (newFragmentLength + MAXLOGBLOCKSIZE - 1) / MAXLOGBLOCKSIZE;
DBG_IO(("\tblocktable[%d]: length = 0x%lX, # blocks = 0x%lX.\n",
index,
(u_long)newFragmentLength,
(u_long)hp->h_logicalblocktable[index].logicalBlockCount));
#if 0
currentFragmentLength = hp->h_logicalblocktable[index].extentLength;
if ((newFragmentLength > currentFragmentLength) && (currentFragmentLength > 0)) {
daddr_t currentLastBlockNumber;
long currentLastBlockSize;
long newLastBlockSize;
struct buf *bp;
int retval;
currentLastBlockNumber = firstFragmentBlockNumber + (currentFragmentLength / MAXLOGBLOCKSIZE);
currentLastBlockSize = currentFragmentLength % MAXLOGBLOCKSIZE;
if (currentLastBlockSize > 0) {
newLastBlockSize = MIN(MAXLOGBLOCKSIZE,
newFragmentLength -
((currentLastBlockNumber - firstFragmentBlockNumber) * MAXLOGBLOCKSIZE));
if (newLastBlockSize != currentLastBlockSize) {
DBG_IO(("\t\t(Adjusting block 0x%lX from 0x%lX to 0x%lX...)\n",
(unsigned long)currentLastBlockNumber,
currentLastBlockSize,
newLastBlockSize));
retval = bread(HTOV(hp), currentLastBlockNumber, currentLastBlockSize, NOCRED, &bp);
if (retval == 0) {
bexpand(bp, newLastBlockSize, NULL, RELEASE_BUFFER);
} else {
DBG_IO(("\t\terror (%d.) acquiring block 0x%lX; bp = 0x%08lX\n",
retval,
(unsigned long)currentLastBlockNumber,
(unsigned long)bp));
if (bp) brelse(bp);
};
};
};
};
#endif
hp->h_logicalblocktable[index].extentLength = newFragmentLength;
}
void UpdateBlockMappingTable(struct hfsnode *hp)
{
ExtendedVCB *vcb = HTOVCB(hp);
FCB *fcb = HTOFCB(hp);
Boolean isHFSPlus;
off_t rangeMappedSoFar;
u_long logicalBlocksMappedSoFar;
int i;
off_t newFragmentLength;
isHFSPlus = (vcb->vcbSigWord == kHFSPlusSigWord);
DBG_IO(("UpdateBlockMappingTable: hp = 0x%08lX, vp = 0x%08lX ('%s'):\n", (u_long)hp, (u_long)HTOV(hp), H_NAME(hp)));
if (H_FORKTYPE(hp) != kDirectory) {
rangeMappedSoFar = 0;
logicalBlocksMappedSoFar = 0;
if ( ! isHFSPlus) {
DBG_ASSERT(kHFSExtentDensity <= LOGBLOCKMAPENTRIES);
for (i = 0; i < kHFSExtentDensity; ++i) {
newFragmentLength = fcb->fcbExtents[i].blockCount * vcb->blockSize;
UpdateBlockMappingTableEntry(hp, i, logicalBlocksMappedSoFar, newFragmentLength);
rangeMappedSoFar += newFragmentLength;
logicalBlocksMappedSoFar += hp->h_logicalblocktable[i].logicalBlockCount;
}
/* Zero out any remaining entries: */
for (i = kHFSExtentDensity; i < LOGBLOCKMAPENTRIES; ++i) {
hp->h_logicalblocktable[i].extentLength = 0;
};
#if HFS_DIAGNOSTIC
if (rangeMappedSoFar < fcb->fcbPLen) DBG_ASSERT(hp->h_logicalblocktable[kHFSExtentDensity-1].extentLength > 0);
#endif
} else {
for (i = 0; i < kHFSPlusExtentDensity; ++i) {
newFragmentLength = fcb->fcbExtents[i].blockCount * vcb->blockSize;
UpdateBlockMappingTableEntry(hp, i, logicalBlocksMappedSoFar, newFragmentLength);
rangeMappedSoFar += newFragmentLength;
logicalBlocksMappedSoFar += hp->h_logicalblocktable[i].logicalBlockCount;
}
/* No need to zero out the remaining entries [there are none]: */
DBG_ASSERT(LOGBLOCKMAPENTRIES <= kHFSPlusExtentDensity);
#if HFS_DIAGNOSTIC
if (rangeMappedSoFar < fcb->fcbPLen) DBG_ASSERT(hp->h_logicalblocktable[kHFSPlusExtentDensity-1].extentLength > 0);
#endif
};
hp->h_optimizedblocksizelimit = rangeMappedSoFar;
hp->h_uniformblocksizestart = logicalBlocksMappedSoFar;
DBG_IO(("\th_optimizedblocksizelimit = 0x%lX, h_uniformblocksizestart = 0x%lX.\n",
(u_long)hp->h_optimizedblocksizelimit, (u_long)hp->h_uniformblocksizestart));
#if BYPASSBLOCKINGOPTIMIZATION
hp->h_optimizedblocksizelimit = 0;
hp->h_uniformblocksizestart = 0;
#endif
};
}
/*********************************************************************
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)
{
int namelen = strlen(name);
char *tname, *fname;
#if HFS_DIAGNOSTIC
DBG_ASSERT(name != NULL);
DBG_ASSERT(fm != NULL);
if (fm->h_namePtr) {
DBG_ASSERT(fm->h_namelen == strlen(fm->h_namePtr));
if (strlen(fm->h_namePtr) > MAXHFSVNODELEN)
DBG_ASSERT(fm->h_metaflags & IN_LONGNAME);
};
if (fm->h_metaflags & IN_LONGNAME) {
DBG_ASSERT(fm->h_namePtr != (char *)fm->h_fileName);
DBG_ASSERT(fm->h_namePtr != NULL);
};
#endif //HFS_DIAGNOSTIC
/*
* Details that have to be dealt with:
* 1. No name is allocated. fm->h_namePtr should be NULL
* 2. A name is being changed and:
* a. it was in static space and now cannot fit
* b. It was malloc'd and now will fit in the static
* c. It did and will fit in the static
* This could be a little smarter:
* - Dont re'malloc if the new name is smaller (but then wasting memory)
* - If its a longname but the same size, we still free and malloc
* -
*/
/* Allocate the new memory */
if (namelen > MAXHFSVNODELEN) {
/*
* Notice the we ALWAYS allocate, even if the new is less then the old,
* or even if they are the SAME
*/
MALLOC(tname, char *, namelen+1, M_TEMP, M_WAITOK);
}
else
tname = fm->h_fileName;
simple_lock(&fm->h_metalock);
/* 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(&fm->h_metalock);
/* Lastly, free the old, if set */
if (fname != NULL)
FREE(fname, M_TEMP);
}
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_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_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_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 = 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 = attrlist->dirattr) != 0) {
if (a & ATTR_DIR_LINKCOUNT) size += sizeof(u_long);
if (a & ATTR_DIR_ENTRYCOUNT) 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;
}
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) {
attrlength = root_hp->h_meta->h_namelen+1;
((struct attrreference *)attrbufptr)->attr_dataoffset = (char *)varbufptr - (char *)attrbufptr;
((struct attrreference *)attrbufptr)->attr_length = attrlength;
(void) strncpy((unsigned char *)varbufptr, H_NAME(root_hp), 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)++ = hfsmp->hfs_raw_dev;
if (a & ATTR_CMN_FSID) {
*((fsid_t *)attrbufptr) = mp->mnt_stat.f_fsid;
if (vcb->vcbSigWord == kHFSSigWord) ((fsid_t *)attrbufptr)->val[0] |= 0x40000000;
++((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 local Mac OS time */
if (a & ATTR_CMN_CRTIME) {
/*
* HFS Plus stores the volume create date in *local*
* time in the volume header. So don't use the create
* date from the vcb. Use the root's crtime instead.
*/
if (vcb->vcbSigWord == kHFSPlusSigWord) {
((struct timespec *)attrbufptr)->tv_sec = root_hp->h_meta->h_crtime;
} else {
((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) *((uid_t *)attrbufptr)++ = root_hp->h_meta->h_uid;
if (a & ATTR_CMN_GRPID) *((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;
};
*attrbufptrptr = attrbufptr;
*varbufptrptr = varbufptr;
}
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;
VCB_UNLOCK(vcb);
};
*attrbufptrptr = attrbufptr;
*varbufptrptr = varbufptr;
}
void PackVolumeInfo(struct attrlist *alist,
struct vnode *root_vp,
struct hfsCatalogInfo *root_catinfo,
void **attrbufptrptr,
void **varbufptrptr) {
PackVolCommonAttributes(alist, root_vp, root_catinfo, attrbufptrptr, varbufptrptr);
PackVolAttributeBlock(alist, root_vp, root_catinfo, attrbufptrptr, varbufptrptr);
};
// Pack the common attribute contents of an objects hfsCatalogInfo
void PackCommonCatalogInfoAttributeBlock(struct attrlist *alist,
struct vnode *root_vp,
struct hfsCatalogInfo *catalogInfo,
void **attrbufptrptr,
void **varbufptrptr )
{
struct hfsnode *hp;
void *attrbufptr;
void *varbufptr;
attrgroup_t a;
u_long attrlength;
hp = VTOH(root_vp);
attrbufptr = *attrbufptrptr;
varbufptr = *varbufptrptr;
if ((a = alist->commonattr) != 0)
{
if (a & ATTR_CMN_NAME)
{
attrlength = strlen(catalogInfo->spec.name) + 1;
((struct attrreference *)attrbufptr)->attr_dataoffset = (char *)varbufptr - (char *)attrbufptr;
((struct attrreference *)attrbufptr)->attr_length = attrlength;
(void) strncpy((unsigned char *)varbufptr, (unsigned char *) &(catalogInfo->spec.name), attrlength);
// ((char *) varbufptr)[attrlength-1] = 0; // Now it's a C string
/* 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;
if (VTOVCB(root_vp)->vcbSigWord == kHFSSigWord) ((fsid_t *)attrbufptr)->val[0] |= 0x40000000;
++((fsid_t *)attrbufptr);
};
if (a & ATTR_CMN_OBJTYPE)
{
switch (catalogInfo->nodeData.cnd_type) {
case kCatalogFolderNode:
*((fsobj_type_t *)attrbufptr)++ = VDIR;
break;
case kCatalogFileNode:
/* Files in an HFS+ catalog can represent many things (regular files, symlinks, block/character devices, ... */
if ((HTOVCB(hp)->vcbSigWord == kHFSPlusSigWord) &&
(catalogInfo->nodeData.cnd_permissions & IFMT)) {
*((fsobj_type_t *)attrbufptr)++ =
IFTOVT((mode_t)(catalogInfo->nodeData.cnd_permissions & 0x0000FFFF));
} 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)
{
((fsobj_id_t *)attrbufptr)->fid_objno = catalogInfo->nodeData.cnd_nodeID;
((fsobj_id_t *)attrbufptr)->fid_generation = 0;
++((fsobj_id_t *)attrbufptr);
};
if (a & ATTR_CMN_OBJPERMANENTID)
{
((fsobj_id_t *)attrbufptr)->fid_objno = catalogInfo->nodeData.cnd_nodeID;
((fsobj_id_t *)attrbufptr)->fid_generation = 0;
++((fsobj_id_t *)attrbufptr);
};
if (a & ATTR_CMN_PAROBJID)
{
((fsobj_id_t *)attrbufptr)->fid_objno = catalogInfo->spec.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) {
*((uid_t *)attrbufptr)++ = catalogInfo->nodeData.cnd_ownerID;
}
if (a & ATTR_CMN_GRPID) {
*((gid_t *)attrbufptr)++ = catalogInfo->nodeData.cnd_groupID;
}
if (a & ATTR_CMN_ACCESSMASK) {
*((u_long *)attrbufptr)++ =
(u_long)(mode_t)(catalogInfo->nodeData.cnd_permissions & 0x0000FFFF);
}
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)++ =
(u_long) ((((catalogInfo->nodeData.cnd_permissions & 0xFFFF0000) >> 8) & 0x00FF0000) |
(((catalogInfo->nodeData.cnd_permissions & 0xFFFF0000) >> 24) & 0x000000FF));
};
*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) {
attrlength = hp->h_meta->h_namelen + 1;
((struct attrreference *)attrbufptr)->attr_dataoffset = (char *)varbufptr - (char *)attrbufptr;
((struct attrreference *)attrbufptr)->attr_length = attrlength;
(void) strncpy((unsigned char *)varbufptr, H_NAME(hp), 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(vp)->mnt_stat.f_fsid;
if (VTOVCB(vp)->vcbSigWord == kHFSSigWord) ((fsid_t *)attrbufptr)->val[0] |= 0x40000000;
++((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) {
((fsobj_id_t *)attrbufptr)->fid_objno = H_FILEID(hp);
((fsobj_id_t *)attrbufptr)->fid_generation = 0;
++((fsobj_id_t *)attrbufptr);
};
if (a & ATTR_CMN_OBJPERMANENTID) {
((fsobj_id_t *)attrbufptr)->fid_objno = H_FILEID(hp);
((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) *((uid_t *)attrbufptr)++ = hp->h_meta->h_uid;
if (a & ATTR_CMN_GRPID) *((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;
};
*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->spec.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;
};
*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->spec.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;
};
*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)(catInfo->nodeData.cnd_datafork.totalBlocks * vcb->blockSize) +
(off_t)(catInfo->nodeData.cnd_rsrcfork.totalBlocks * 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 *)attrbufptr)++ =
(u_long)catInfo->nodeData.cnd_specialDevice;
}
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)(catInfo->nodeData.cnd_datafork.totalBlocks * 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)(catInfo->nodeData.cnd_rsrcfork.totalBlocks * 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)(catInfo->nodeData.cnd_rsrcfork.totalBlocks * vcb->blockSize);
break;
case kRsrcFork:
*((off_t *)attrbufptr)++ =
(off_t)fcb->fcbPLen +
(off_t)(catInfo->nodeData.cnd_datafork.totalBlocks * vcb->blockSize);
break;
default:
*((off_t *)attrbufptr)++ =
(off_t)(catInfo->nodeData.cnd_datafork.totalBlocks * vcb->blockSize) +
(off_t)(catInfo->nodeData.cnd_rsrcfork.totalBlocks * 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 *)attrbufptr)++ = (u_long)catInfo->nodeData.cnd_specialDevice;
}
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)
#if MACH_NBC
if ((vp->v_type == VREG) && vp->v_vm_info && vp->v_vm_info->mapped &&
(!vp->v_vm_info->filesize)) {
*((off_t *)attrbufptr)++ = vp->v_vm_info->vnode_size;
}
else
#endif /* MACH_NBC */
*((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)(catInfo->nodeData.cnd_datafork.totalBlocks * 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)
#if MACH_NBC
if ((vp->v_type == VREG) && vp->v_vm_info && vp->v_vm_info->mapped &&
(!vp->v_vm_info->filesize)) {
*((off_t *)attrbufptr)++ = vp->v_vm_info->vnode_size;
}
else
#endif /* MACH_NBC */
*((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)(catInfo->nodeData.cnd_rsrcfork.totalBlocks * 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;
text_encoding_t nameEncoding;
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) {
/* XXX PPD No use for this info right now... */
nameEncoding = *(((text_encoding_t *)attrbufptr)++);
#if HFS_DIAGNOSTIC
a &= ~ATTR_CMN_SCRIPT;
#endif
};
if (a & ATTR_CMN_CRTIME) {
/*
* HFS Plus stores the volume create date in *local*
* time in the volume header. So don't set the create
* date in the vcb. Set the root's crtime instead.
*/
if (vcb->vcbSigWord == kHFSPlusSigWord) {
VTOH(root_vp)->h_meta->h_crtime =
(UInt32)((struct timespec *)attrbufptr)->tv_sec;
} else {
vcb->vcbCrDate = to_hfs_time((UInt32)((struct timespec *)attrbufptr)->tv_sec);
}
++((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) {
/* XXX PPD No use for this info right now... */
++((text_encoding_t *)attrbufptr);
#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);
#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);
#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);
#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;
};
};
};
/* 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;
}
/*
* To make the HFS filesystem follow UFS unlink semantics, a remove of
* an active vnode is translated to a move/rename so the file appears
* deleted. The destination folder for these move/renames is setup here
* and a reference to it is place in hfsmp->hfs_private_metadata_dir.
*/
u_long
FindMetaDataDirectory(ExtendedVCB *vcb)
{
char namep[32];
hfsCatalogInfo catInfo;
HFSCatalogNodeID dirID;
int retval;
dirID = 0;
strncpy(namep, HFSPLUSMETADATAFOLDER, sizeof(namep));
/* lock catalog b-tree */
retval = hfs_metafilelocking(VCBTOHFS(vcb), kHFSCatalogFileID, LK_SHARED, current_proc());
if (retval)
goto Err_Exit;
catInfo.hint = kNoHint;
if (hfsLookup(vcb, kRootDirID, namep, -1, &catInfo) == 0)
dirID = catInfo.nodeData.cnd_nodeID;
else if (VCBTOHFS(vcb)->hfs_fs_ronly == 0) {
if (CreateCatalogNode(vcb, kRootDirID, namep, kCatalogFolderNode, &dirID, &catInfo.hint) == 0) {
catInfo.hint = kNoHint;
if (hfsLookup(vcb, kRootDirID, namep, -1, &catInfo) == 0) {
/* directory with no permissions owned by root */
catInfo.nodeData.cnd_permissions = IFDIR;
/* hidden and off the desktop view */
((struct DInfo *)(&catInfo.nodeData.cnd_finderInfo))->frLocation.v = 22460;
((struct DInfo *)(&catInfo.nodeData.cnd_finderInfo))->frLocation.h = 22460;
((struct DInfo *)(&catInfo.nodeData.cnd_finderInfo))->frFlags |= (kIsInvisible + kNameLocked);
(void) UpdateCatalogNode(vcb, kRootDirID, namep, catInfo.hint, &catInfo.nodeData);
}
}
}
/* unlock catalog b-tree */
(void) hfs_metafilelocking(VCBTOHFS(vcb), kHFSCatalogFileID, LK_RELEASE, current_proc());
Err_Exit:
return dirID;
}
/*
* This will return the correct logical block size for a given vnode.
* For most files, it is the allocation block size, for meta data like
* BTrees, this is kept as part of the BTree private nodeSize
*/
u_int32_t
GetLogicalBlockSize(struct vnode *vp)
{
u_int32_t logBlockSize;
DBG_ASSERT(vp != NULL);
if ((vp->v_flag & VSYSTEM) && (VTOH(vp)->fcbBTCBPtr!=NULL)) {
BTreeInfoRec bTreeInfo;
int retval;
/*
* We do not lock the BTrees, because if we are getting block..then the tree
* should be locked in the first place.
* We just want the nodeSize wich will NEVER change..so even if the world
* is changing..the nodeSize should remain the same. Which argues why lock
* it in the first place??
*/
(void) BTGetInformation (VTOFCB(vp), kBTreeInfoVersion, &bTreeInfo);
logBlockSize = bTreeInfo.nodeSize;
}
else
logBlockSize = VTOHFS(vp)->hfs_logBlockSize;
DBG_ASSERT(logBlockSize > 0);
return logBlockSize;
}
/*
* 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) {
DBG_ERR(("MacToVFSError: mapping error code %d...\n", err));
};
switch (err) {
case dirFulErr: /* -33 */
case dskFulErr: /* -34 */
case btNoSpaceAvail: /* -32733 */
case fxOvFlErr: /* -32750 */
return ENOSPC; /* +28 */
case btBadNode: /* -32731 */
case ioErr: /* -36 */
return EIO; /* +5 */
case mFulErr: /* -41 */
case memFullErr: /* -108 */
return ENOMEM; /* +12 */
case tmfoErr: /* -42 */
/* Consider EMFILE (Too many open files, 24)? */
return ENFILE; /* +23 */
case nsvErr: /* -35 */
case fnfErr: /* -43 */
case dirNFErr: /* -120 */
case fidNotFound: /* -1300 */
return ENOENT; /* +2 */
case wPrErr: /* -44 */
case vLckdErr: /* -46 */
case fsDSIntErr: /* -127 */
return EROFS; /* +30 */
case opWrErr: /* -49 */
case fLckdErr: /* -45 */
return EACCES; /* +13 */
case permErr: /* -54 */
case wrPermErr: /* -61 */
return EPERM; /* +1 */
case fBsyErr: /* -47 */
return EBUSY; /* +16 */
case dupFNErr: /* -48 */
case fidExists: /* -1301 */
case cmExists: /* -32718 */
case btExists: /* -32734 */
return EEXIST; /* +17 */
case rfNumErr: /* -51 */
return EBADF; /* +9 */
case notAFileErr: /* -1302 */
return EISDIR; /* +21 */
case cmNotFound: /* -32719 */
case btNotFound: /* -32735 */
return ENOENT; /* 28 */
case cmNotEmpty: /* -32717 */
return ENOTEMPTY; /* 66 */
case cmFThdDirErr: /* -32714 */
return EISDIR; /* 21 */
case fxRangeErr: /* -32751 */
return EIO; /* 5 */
case bdNamErr: /* -37 */
return ENAMETOOLONG; /* 63 */
case fnOpnErr: /* -38 */
case eofErr: /* -39 */
case posErr: /* -40 */
case paramErr: /* -50 */
case badMDBErr: /* -60 */
case badMovErr: /* -122 */
case sameFileErr: /* -1306 */
case badFidErr: /* -1307 */
case fileBoundsErr: /* -1309 */
return EINVAL; /* +22 */
default:
DBG_UTILS(("Unmapped MacOS error: %d\n", err));
return EIO; /* +5 */
}
}
/*
* All of our debugging functions
*/
#if HFS_DIAGNOSTIC
void debug_vn_status (char* introStr, struct vnode *vn)
{
DBG_VOP(("%s:\t",introStr));
if (vn != NULL)
{
if (vn->v_tag != VT_HFS)
{
DBG_VOP(("NON-HFS VNODE Ox%08lX\n", (unsigned long)vn));
}
else if(vn->v_tag==VT_HFS && (vn->v_data==NULL || VTOH((vn))->h_valid != HFS_VNODE_MAGIC))
{
DBG_VOP(("BAD VNODE PRIVATE DATA!!!!\n"));
}
else
{
DBG_VOP(("r: %d & ", vn->v_usecount));
if (lockstatus(&VTOH(vn)->h_lock))
{
DBG_VOP_CONT(("is L\n"));
}
else
{
DBG_VOP_CONT(("is U\n"));
}
}
}
else
{
DBG_VOP(("vnode is NULL\n"));
};
}
void debug_vn_print (char* introStr, struct vnode *vn)
{
// DBG_FUNC_NAME("DBG_VN_PRINT");
DBG_ASSERT (vn != NULL);
DBG_VFS(("%s: ",introStr));
DBG_VFS_CONT(("vnode: 0x%x is a ", (uint)vn));
switch (vn->v_tag)
{
case VT_UFS:
DBG_VFS_CONT(("%s","UFS"));
break;
case VT_HFS:
DBG_VFS_CONT(("%s","HFS"));
break;
default:
DBG_VFS_CONT(("%s","UNKNOWN"));
break;
}
DBG_VFS_CONT((" vnode\n"));
if (vn->v_tag==VT_HFS)
{
if (vn->v_data==NULL)
{
DBG_VFS(("BAD VNODE PRIVATE DATA!!!!\n"));
}
else
{
DBG_VFS((" Name: %s Id: %ld ",H_NAME(VTOH(vn)), H_FILEID(VTOH(vn))));
}
}
else
DBG_VFS((" "));
DBG_VFS_CONT(("Refcount: %d\n", vn->v_usecount));
if (VOP_ISLOCKED(vn))
{
DBG_VFS((" The vnode is locked\n"));
}
else
{
DBG_VFS((" The vnode is not locked\n"));
}
}
void debug_rename_test_locks (char* introStr,
struct vnode *fvp,
struct vnode *fdvp,
struct vnode *tvp,
struct vnode *tdvp,
int fstatus,
int fdstatus,
int tstatus,
int tdstatus
)
{
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"));
}
}
}
#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);
if (bp->b_lblkno < hp->h_uniformblocksizestart) {
DBG_ASSERT((bp->b_bcount == MAXLOGBLOCKSIZE) ||
(bp->b_bcount == LogicalBlockSize(hp, bp->b_lblkno)) ||
((bp->b_bcount % 512 == 0) &&
(bp->b_validend > 0) &&
(bp->b_dirtyend >= 0) && /* Could be partial block due to file growth */
(bp->b_bcount < LogicalBlockSize(hp, bp->b_lblkno))));
} else {
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);
};
if ((bp->b_lblkno == 0x21) || (bp->b_lblkno == 0x22)) DBG_ASSERT((hp->h_uniformblocksizestart > 0x21) || (bp->b_bcount != MAXLOGBLOCKSIZE));
}
void debug_check_blocksizes(struct vnode *vp) {
struct hfsnode *hp = VTOH(vp);
struct buf *bp;
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);
};
for (bp = vp->v_dirtyblkhd.lh_first; bp != NULL; bp = bp->b_vnbufs.le_next) {
debug_check_buffersizes(vp, hp, bp);
};
}
extern void hfs_vhash_dbg(struct hfsnode *hp);
/* Checks the valicity of a hfs vnode */
void debug_check_vnode(struct vnode *vp, int stage) {
struct hfsnode *hp;
u_long size;
int i;
/* vcb stuff */
if (VTOHFS(vp)->hfs_mount_flags & kHFSBootVolumeInconsistentMask)
DEBUG_BREAK_MSG(("Volume is damaged!"));
/* vnode stuff */
if (vp==NULL)
DEBUG_BREAK_MSG(("Null vnode"));
if (vp->v_tag != VT_HFS)
DEBUG_BREAK_MSG(("Not a HFS vnode, it is a %d", vp->v_tag));
if (vp->v_data==NULL)
DEBUG_BREAK_MSG(("v_data is NULL"));
/* hfsnode stuff */
hp = VTOH(vp);
if (hp->h_valid != HFS_VNODE_MAGIC)
DEBUG_BREAK_MSG(("Bad Formed HFS node"));
if (hp->h_vp==NULL || hp->h_vp!=vp)
DEBUG_BREAK_MSG(("Bad hfsnode vnode pte"));
if (hp->h_meta == NULL)
DEBUG_BREAK_MSG(("Bad hfsnode meta ptr"));
switch (H_FORKTYPE(hp)) {
case kDataFork:
case kRsrcFork:
if ((hp->h_meta->h_siblinghead.cqh_first == NULL) || (hp->h_meta->h_siblinghead.cqh_last == NULL))
DEBUG_BREAK_MSG(("Null sibling header"));
if ((hp->h_sibling.cqe_next==NULL) || (hp->h_sibling.cqe_prev==NULL))
DEBUG_BREAK_MSG(("Null sibling list"));
if (hp->h_meta->h_usecount<1 || hp->h_meta->h_usecount>2)
DEBUG_BREAK_MSG(("Bad sibling usecount"));
break;
case kDirectory:
case kSysFile:
if ((hp->h_meta->h_siblinghead.cqh_first != NULL) || (hp->h_meta->h_siblinghead.cqh_last != NULL))
DEBUG_BREAK_MSG(("Non Null sibling header"));
if ((hp->h_sibling.cqe_next!=NULL) || (hp->h_sibling.cqe_prev!=NULL))
DEBUG_BREAK_MSG(("Null sibling list"));
if (hp->h_meta->h_usecount!=1)
DEBUG_BREAK_MSG(("Bad usecount"));
break;
default:
DEBUG_BREAK_MSG(("Bad hfsnode fork type"));
}
/* hfsmeta stuff */
if (hp->h_meta->h_devvp == NULL)
DEBUG_BREAK_MSG(("Bad hfsnode dev vnode"));
if (H_DEV(hp) == 0)
DEBUG_BREAK_MSG(("Bad dev id"));
if (H_FILEID(hp) == 0)
DEBUG_BREAK_MSG(("Bad file id"));
if (((hp->h_meta->h_metaflags & IN_DATANODE)==0) && (H_DIRID(hp) == 0) && (H_FILEID(hp) != 1))
DEBUG_BREAK_MSG(("Bad dir id"));
if (hp->h_meta->h_namePtr == NULL && hp->h_meta->h_namelen!=0)
DEBUG_BREAK_MSG(("hfs meta h_namelen is not 0"));
if (hp->h_meta->h_namePtr != NULL && strlen(hp->h_meta->h_namePtr) != hp->h_meta->h_namelen)
DEBUG_BREAK_MSG(("Bad hfs meta h_namelen"));
/* Check the hash */
hfs_vhash_dbg(hp);
/* Check to see if we want to compare with the disk */
if (stage > 200) {
int retval;
hfsCatalogInfo catInfo;
if (hfs_metafilelocking(VTOHFS(vp), kHFSCatalogFileID, LK_SHARED, current_proc()))
return;
catInfo.hint = 0;
if (hfsLookup(VTOVCB(vp), H_DIRID(hp), hp->h_meta->h_namePtr, hp->h_meta->h_namelen, &catInfo))
DEBUG_BREAK_MSG(("Could not find hfsnode Catalog record"));
(void) hfs_metafilelocking(VTOHFS(vp), kHFSCatalogFileID, LK_RELEASE, current_proc());
if (H_FILEID(hp) != catInfo.nodeData.cnd_nodeID)
DEBUG_BREAK_MSG(("hfsnode catalog node id mismatch"));
if (H_DIRID(hp) != catInfo.spec.parID)
DEBUG_BREAK_MSG(("hfsnode catalog dir id mismatch"));
if (strcmp(hp->h_meta->h_namePtr, catInfo.spec.name) != 0)
DEBUG_BREAK_MSG(("hfsnode catalog name mismatch"));
/* Check dates too??? */
}
for(i = 0, size = 0; i < kHFSPlusExtentDensity; i++)
{
size += hp->fcbExtents[i].blockCount;
}
if (size*VTOVCB(vp)->blockSize < hp->fcbPLen)
DEBUG_BREAK_MSG(("fcbPLen too large"));
if (hp->fcbEOF > hp->fcbPLen)
DEBUG_BREAK_MSG(("fcbPLen is smaller than fcbEOF"));
if (hp->fcbExtents[kHFSPlusExtentDensity-1].blockCount == 0) {
if (size*VTOVCB(vp)->blockSize != hp->fcbPLen)
DEBUG_BREAK_MSG(("fcbPLen does not match extents"));
} else {
if ( hp->fcbPLen < size*VTOVCB(vp)->blockSize)
DEBUG_BREAK_MSG(("fcbPLen is smaller than extents"));
}
for(i = 0; i < kHFSPlusExtentDensity; i++)
{
if (hp->fcbExtents[i].blockCount == 0 || hp->fcbExtents[i].startBlock == 0)
break;
}
if ((VTOVCB(vp)->vcbSigWord == kHFSSigWord) && i > kHFSExtentDensity)
DEBUG_BREAK_MSG(("Illegal value in extents for ordinary HFS"));
if (i > kHFSPlusExtentDensity) {
for(; i < kHFSPlusExtentDensity; i++)
{
if (hp->fcbExtents[i].blockCount != 0 || hp->fcbExtents[i].startBlock != 0)
DEBUG_BREAK_MSG(("Illegal value in extents"));
}
}
/* BTree stuff */
if (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"));
}
}
#endif /* HFS_DIAGNOSTIC */