#include <sys/param.h>
#include <sys/systm.h>
#include <sys/buf.h>
#include <sys/mount.h>
#include <sys/vnode.h>
#include "hfs.h"
#include "hfs_dbg.h"
#include "hfs_endian.h"
#include "hfscommon/headers/FileMgrInternal.h"
#include "hfscommon/headers/BTreesPrivate.h"
#define FORCESYNCBTREEWRITES 0
static OSStatus FlushAlternate( ExtendedVCB *vcb );
static int ClearBTNodes(struct vnode *vp, long blksize, off_t offset, off_t amount);
OSStatus SetBTreeBlockSize(FileReference vp, ByteCount blockSize, ItemCount minBlockCount)
{
BTreeControlBlockPtr bTreePtr;
DBG_ASSERT(vp != NULL);
DBG_ASSERT(VTOFCB(vp) != NULL);
DBG_ASSERT(VTOFCB(vp)->fcbBTCBPtr != NULL);
DBG_ASSERT(blockSize >= kMinNodeSize);
if (blockSize > MAXBSIZE )
return (fsBTBadNodeSize);
DBG_TREE(("SetBlockSizeProc: blockSize=%ld for file %ld\n", blockSize, H_FILEID(VTOH(vp))));
bTreePtr = (BTreeControlBlockPtr)(VTOH(vp)->fcbBTCBPtr);
bTreePtr->nodeSize = blockSize;
return (E_NONE);
}
OSStatus GetBTreeBlock(FileReference vp, UInt32 blockNum, GetBlockOptions options, BlockDescriptor *block)
{
OSStatus retval = E_NONE;
struct buf *bp = NULL;
if (options & kGetEmptyBlock)
bp = getblk (vp,
IOBLKNOFORBLK(blockNum, VTOHFS(vp)->hfs_phys_block_size),
IOBYTECCNTFORBLK(blockNum, block->blockSize, VTOHFS(vp)->hfs_phys_block_size),
0,
0,
BLK_META);
else
retval = meta_bread(vp,
IOBLKNOFORBLK(blockNum, VTOHFS(vp)->hfs_phys_block_size),
IOBYTECCNTFORBLK(blockNum, block->blockSize, VTOHFS(vp)->hfs_phys_block_size),
NOCRED,
&bp);
DBG_ASSERT(bp != NULL);
DBG_ASSERT(bp->b_data != NULL);
DBG_ASSERT(bp->b_bcount == block->blockSize);
DBG_ASSERT(bp->b_lblkno == blockNum);
if (bp == NULL)
retval = -1;
if (retval == E_NONE) {
block->blockHeader = bp;
block->buffer = bp->b_data + IOBYTEOFFSETFORBLK(bp->b_blkno, VTOHFS(vp)->hfs_phys_block_size);
block->blockReadFromDisk = (bp->b_flags & B_CACHE) == 0;
#if BYTE_ORDER == LITTLE_ENDIAN
if (!(options & kGetEmptyBlock)) {
if ((((BTNodeDescriptor *)block->buffer)->kind == kBTHeaderNode) &&
(((BTHeaderRec *)((char *)block->buffer + 14))->nodeSize != bp->b_bcount) &&
(SWAP_BE16 (((BTHeaderRec *)((char *)block->buffer + 14))->nodeSize) != bp->b_bcount)) {
SWAP_BT_NODE (block, ISHFSPLUS(VTOVCB(vp)), H_FILEID(VTOH(vp)), 3);
} else if (*((UInt16 *)((char *)block->buffer + (block->blockSize - sizeof (UInt16)))) == 0x0e00) {
SWAP_BT_NODE (block, ISHFSPLUS(VTOVCB(vp)), H_FILEID(VTOH(vp)), 0);
#if 0
} else if (*((UInt16 *)((char *)block->buffer + (block->blockSize - sizeof (UInt16)))) != 0x000e) {
panic ("%s Corrupt B-Tree node detected!\n", "GetBTreeBlock:");
#endif
}
}
#endif
} else {
if (bp)
brelse(bp);
block->blockHeader = NULL;
block->buffer = NULL;
}
return (retval);
}
OSStatus ReleaseBTreeBlock(FileReference vp, BlockDescPtr blockPtr, ReleaseBlockOptions options)
{
OSStatus retval = E_NONE;
struct buf *bp = NULL;
bp = (struct buf *) blockPtr->blockHeader;
if (bp == NULL) {
DBG_TREE(("ReleaseBlockProc: blockHeader is zero!\n"));
retval = -1;
goto exit;
}
if (options & kTrashBlock) {
bp->b_flags |= B_INVAL;
brelse(bp);
} else {
if (options & kForceWriteBlock) {
bp->b_flags |= B_DIRTY;
retval = VOP_BWRITE(bp);
} else if (options & kMarkBlockDirty) {
bp->b_flags |= B_DIRTY;
#if FORCESYNCBTREEWRITES
VOP_BWRITE(bp);
#else
if (options & kLockTransaction) {
extern int count_lock_queue __P((void));
if (count_lock_queue() > kMaxLockedMetaBuffers)
hfs_fsync_transaction(vp);
bp->b_flags |= B_LOCKED;
};
bdwrite(bp);
#endif
} else {
brelse(bp);
};
};
exit:
return (retval);
}
OSStatus ExtendBTreeFile(FileReference vp, FSSize minEOF, FSSize maxEOF)
{
#pragma unused (maxEOF)
OSStatus retval;
UInt64 actualBytesAdded;
UInt64 bytesToAdd;
UInt32 extendFlags;
BTreeInfoRec btInfo;
ExtendedVCB *vcb;
FCB *filePtr;
struct proc *p = NULL;
filePtr = GetFileControlBlock(vp);
if ( minEOF > filePtr->fcbEOF )
{
bytesToAdd = minEOF - filePtr->fcbEOF;
if (bytesToAdd < filePtr->fcbClmpSize)
bytesToAdd = filePtr->fcbClmpSize; }
else
{
DBG_TREE((" ExtendBTreeFile: minEOF is smaller than current size!"));
return -1;
}
vcb = FCBTOVCB(filePtr);
if(H_FILEID(filePtr) != kHFSExtentsFileID)
{
p = current_proc();
retval = hfs_metafilelocking(VTOHFS(vp), kHFSExtentsFileID, LK_EXCLUSIVE, p);
if (retval)
return (retval);
}
(void) BTGetInformation(filePtr, 0, &btInfo);
if (vcb->blockSize >= btInfo.nodeSize) {
extendFlags = 0;
} else {
extendFlags = kEFAllMask | kEFContigMask;
}
retval = ExtendFileC(vcb, filePtr, bytesToAdd, 0, extendFlags, &actualBytesAdded);
if(H_FILEID(filePtr) != kHFSExtentsFileID)
(void) hfs_metafilelocking(VTOHFS(vp), kHFSExtentsFileID, LK_RELEASE, p);
if (retval)
return (retval);
if (actualBytesAdded < bytesToAdd)
DBG_TREE((" ExtendBTreeFile: actualBytesAdded < bytesToAdd!"));
filePtr->fcbEOF = filePtr->fcbPLen;
retval = ClearBTNodes(vp, btInfo.nodeSize, filePtr->fcbEOF - actualBytesAdded, actualBytesAdded);
if (retval)
return (retval);
if ((H_FILEID(filePtr) == kHFSExtentsFileID) ||
(H_FILEID(filePtr) == kHFSCatalogFileID) ||
(H_FILEID(filePtr) == kHFSAttributesFileID)
) {
MarkVCBDirty( vcb );
if (vcb->vcbSigWord == kHFSPlusSigWord) {
retval = hfs_flushvolumeheader(VCBTOHFS(vcb), 0);
} else {
retval = hfs_flushMDB(VCBTOHFS(vcb), 0);
}
if (retval == 0) {
retval = FlushAlternate(vcb);
}
}
return retval;
}
static OSStatus
FlushAlternate( ExtendedVCB *vcb )
{
void *maindata;
void *altdata;
int result;
result = GetBlock_glue(gbDefault,
(vcb->hfsPlusIOPosOffset / kHFSBlockSize) + kMasterDirectoryBlock,
(Ptr *)&maindata, kNoFileReference, vcb);
if (result) return (result);
result = GetBlock_glue( gbDefault, vcb->altIDSector,
(Ptr *)&altdata, kNoFileReference, vcb );
if (result == 0) {
bcopy(maindata, altdata, kMDBSize);
result = RelBlock_glue( (Ptr)altdata, rbWriteMask );
}
(void) RelBlock_glue( (Ptr)maindata, rbFreeMask );
return (result);
}
static int
ClearBTNodes(struct vnode *vp, long blksize, off_t offset, off_t amount)
{
struct buf *bp = NULL;
daddr_t blk;
daddr_t blkcnt;
blk = offset / blksize;
blkcnt = amount / blksize;
while (blkcnt > 0) {
bp = getblk(vp, blk, blksize, 0, 0, BLK_META);
if (bp == NULL)
continue;
bzero((char *)bp->b_data, blksize);
bp->b_flags |= (B_DIRTY | B_AGE);
if ((blk % 32) == 0)
VOP_BWRITE(bp);
else
bawrite(bp);
--blkcnt;
++blk;
}
return (0);
}