#include <sys/param.h>
#include <sys/systm.h>
#include <sys/buf.h>
#include <sys/kernel.h>
#include <sys/mount.h>
#include <sys/vnode.h>
#include "hfs.h"
#include "hfs_cnode.h"
#include "hfs_dbg.h"
#include "hfs_endian.h"
#include "hfscommon/headers/FileMgrInternal.h"
#include "hfscommon/headers/BTreesPrivate.h"
#define FORCESYNCBTREEWRITES 0
static int ClearBTNodes(struct vnode *vp, long blksize, off_t offset, off_t amount);
__private_extern__
OSStatus SetBTreeBlockSize(FileReference vp, ByteCount blockSize, ItemCount minBlockCount)
{
BTreeControlBlockPtr bTreePtr;
DBG_ASSERT(vp != NULL);
DBG_ASSERT(blockSize >= kMinNodeSize);
if (blockSize > MAXBSIZE )
return (fsBTBadNodeSize);
bTreePtr = (BTreeControlBlockPtr)VTOF(vp)->fcbBTCBPtr;
bTreePtr->nodeSize = blockSize;
return (E_NONE);
}
__private_extern__
OSStatus GetBTreeBlock(FileReference vp, UInt32 blockNum, GetBlockOptions options, BlockDescriptor *block)
{
OSStatus retval = E_NONE;
struct buf *bp = NULL;
if (options & kGetEmptyBlock)
bp = getblk(vp, blockNum, block->blockSize, 0, 0, BLK_META);
else
retval = meta_bread(vp, blockNum, block->blockSize, 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;
block->blockReadFromDisk = (bp->b_flags & B_CACHE) == 0;
block->isModified = 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)), VTOC(vp)->c_fileid, 3);
} else if (*((UInt16 *)((char *)block->buffer + (block->blockSize - sizeof (UInt16)))) == 0x0e00) {
SWAP_BT_NODE (block, ISHFSPLUS(VTOVCB(vp)), VTOC(vp)->c_fileid, 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);
}
__private_extern__
void ModifyBlockStart(FileReference vp, BlockDescPtr blockPtr)
{
struct hfsmount *hfsmp = VTOHFS(vp);
struct buf *bp = NULL;
if (hfsmp->jnl == NULL) {
return;
}
bp = (struct buf *) blockPtr->blockHeader;
if (bp == NULL) {
panic("ModifyBlockStart: null bp for blockdescptr 0x%x?!?\n", blockPtr);
return;
}
journal_modify_block_start(hfsmp->jnl, bp);
blockPtr->isModified = 1;
}
__private_extern__
OSStatus ReleaseBTreeBlock(FileReference vp, BlockDescPtr blockPtr, ReleaseBlockOptions options)
{
struct hfsmount *hfsmp = VTOHFS(vp);
extern int bdwrite_internal(struct buf *, int);
OSStatus retval = E_NONE;
struct buf *bp = NULL;
bp = (struct buf *) blockPtr->blockHeader;
if (bp == NULL) {
retval = -1;
goto exit;
}
if (options & kTrashBlock) {
bp->b_flags |= B_INVAL;
if (hfsmp->jnl && (bp->b_flags & B_LOCKED)) {
journal_kill_block(hfsmp->jnl, bp);
} else {
brelse(bp);
}
} else {
if (options & kForceWriteBlock) {
if (hfsmp->jnl) {
if (blockPtr->isModified == 0) {
panic("hfs: releaseblock: modified is 0 but forcewrite set! bp 0x%x\n", bp);
}
retval = journal_modify_block_end(hfsmp->jnl, bp);
blockPtr->isModified = 0;
} else {
retval = VOP_BWRITE(bp);
}
} else if (options & kMarkBlockDirty) {
if ((options & kLockTransaction) && hfsmp->jnl == NULL) {
extern int count_lock_queue __P((void));
if (count_lock_queue() > kMaxLockedMetaBuffers) {
hfs_btsync(vp, HFS_SYNCTRANS);
(void) BTSetLastSync(VTOF(vp), time.tv_sec - (kMaxSecsForFsync + 1));
}
bp->b_flags |= B_LOCKED;
}
if (hfsmp->jnl) {
if (blockPtr->isModified == 0) {
panic("hfs: releaseblock: modified is 0 but markdirty set! bp 0x%x\n", bp);
}
retval = journal_modify_block_end(hfsmp->jnl, bp);
blockPtr->isModified = 0;
} else if (bdwrite_internal(bp, 1) != 0) {
hfs_btsync(vp, 0);
(void) BTSetLastSync(VTOF(vp), time.tv_sec - (kMaxSecsForFsync + 1));
bp->b_flags &= ~B_LOCKED;
bawrite(bp);
}
} else {
if (hfsmp->jnl && blockPtr->isModified) {
journal_modify_block_end(hfsmp->jnl, bp);
blockPtr->isModified = 0;
} else {
brelse(bp);
}
};
};
exit:
return (retval);
}
__private_extern__
OSStatus ExtendBTreeFile(FileReference vp, FSSize minEOF, FSSize maxEOF)
{
#pragma unused (maxEOF)
OSStatus retval, ret;
UInt64 actualBytesAdded, origSize;
UInt64 bytesToAdd;
u_int32_t startAllocation;
u_int32_t fileblocks;
BTreeInfoRec btInfo;
ExtendedVCB *vcb;
FCB *filePtr;
struct proc *p = NULL;
UInt64 trim = 0;
filePtr = GetFileControlBlock(vp);
if ( minEOF > filePtr->fcbEOF )
{
bytesToAdd = minEOF - filePtr->fcbEOF;
if (bytesToAdd < filePtr->ff_clumpsize)
bytesToAdd = filePtr->ff_clumpsize; }
else
{
return -1;
}
vcb = VTOVCB(vp);
if(VTOC(vp)->c_fileid != kHFSExtentsFileID)
{
p = current_proc();
retval = hfs_metafilelocking(VTOHFS(vp), kHFSExtentsFileID, LK_EXCLUSIVE, p);
if (retval)
return (retval);
}
(void) BTGetInformation(filePtr, 0, &btInfo);
#if 0 // XXXdbg
if (vcb->blockSize >= btInfo.nodeSize) {
extendFlags = 0;
} else {
extendFlags = kEFContigMask;
}
#endif
origSize = filePtr->fcbEOF;
fileblocks = filePtr->ff_blocks;
startAllocation = vcb->nextAllocation;
do {
retval = ExtendFileC(vcb, filePtr, bytesToAdd, 0, kEFContigMask, &actualBytesAdded);
if (retval == dskFulErr && actualBytesAdded == 0) {
if (bytesToAdd == btInfo.nodeSize || bytesToAdd < (minEOF - origSize)) {
break;
} else {
bytesToAdd >>= 1;
if (bytesToAdd < btInfo.nodeSize) {
bytesToAdd = btInfo.nodeSize;
} else if ((bytesToAdd % btInfo.nodeSize) != 0) {
bytesToAdd -= (bytesToAdd % btInfo.nodeSize);
}
}
}
} while (retval == dskFulErr && actualBytesAdded == 0);
if ((retval == 0) &&
(vcb->nextAllocation > startAllocation) &&
((vcb->nextAllocation + fileblocks) < vcb->totalBlocks)) {
vcb->nextAllocation += fileblocks;
}
filePtr->fcbEOF = (u_int64_t)filePtr->ff_blocks * (u_int64_t)vcb->blockSize;
if (filePtr->fcbEOF >= minEOF && retval != 0) {
retval = 0;
}
if ((filePtr->fcbEOF < minEOF) || (actualBytesAdded % btInfo.nodeSize) != 0) {
if (filePtr->fcbEOF < minEOF) {
retval = dskFulErr;
if (filePtr->fcbEOF < origSize) {
panic("hfs: btree file eof %lld less than orig size %lld!\n",
filePtr->fcbEOF, origSize);
}
trim = filePtr->fcbEOF - origSize;
if (trim != actualBytesAdded) {
panic("hfs: trim == %lld but actualBytesAdded == %lld\n",
trim, actualBytesAdded);
}
} else {
trim = (actualBytesAdded % btInfo.nodeSize);
}
ret = TruncateFileC(vcb, filePtr, filePtr->fcbEOF - trim, 0);
filePtr->fcbEOF = (u_int64_t)filePtr->ff_blocks * (u_int64_t)vcb->blockSize;
if ((filePtr->fcbEOF % btInfo.nodeSize) != 0) {
panic("hfs: truncate file didn't! fcbEOF %lld nsize %d fcb 0x%x\n",
filePtr->fcbEOF, btInfo.nodeSize, filePtr);
}
if (ret) {
panic("hfs: error truncating btree files (sz 0x%llx, trim %lld, ret %d)\n",
filePtr->fcbEOF, trim, ret);
return ret;
}
actualBytesAdded -= trim;
}
if(VTOC(vp)->c_fileid != kHFSExtentsFileID) {
(void) BTFlushPath(VTOF(vcb->extentsRefNum));
(void) VOP_FSYNC(vcb->extentsRefNum, NOCRED, MNT_WAIT, p);
(void) hfs_metafilelocking(VTOHFS(vp), kHFSExtentsFileID, LK_RELEASE, p);
}
if ((filePtr->fcbEOF % btInfo.nodeSize) != 0) {
panic("hfs: extendbtree: fcb 0x%x has eof 0x%llx not a multiple of 0x%x (trim %llx)\n",
filePtr, filePtr->fcbEOF, btInfo.nodeSize, trim);
}
if ((VTOC(vp)->c_fileid == kHFSExtentsFileID) ||
(VTOC(vp)->c_fileid == kHFSCatalogFileID) ||
(VTOC(vp)->c_fileid == kHFSAttributesFileID)
) {
MarkVCBDirty( vcb );
ret = hfs_flushvolumeheader(VCBTOHFS(vcb), MNT_WAIT, HFS_ALTFLUSH);
}
ret = ClearBTNodes(vp, btInfo.nodeSize, filePtr->fcbEOF - actualBytesAdded, actualBytesAdded);
if (ret)
return (ret);
return retval;
}
static int
ClearBTNodes(struct vnode *vp, long blksize, off_t offset, off_t amount)
{
struct hfsmount *hfsmp = VTOHFS(vp);
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;
if (hfsmp->jnl) {
}
bzero((char *)bp->b_data, blksize);
bp->b_flags |= B_AGE;
if (hfsmp->jnl) {
if ((blk % 32) == 0)
VOP_BWRITE(bp);
else
bawrite(bp);
} else {
if ((blk % 32) == 0)
VOP_BWRITE(bp);
else
bawrite(bp);
}
--blkcnt;
++blk;
}
return (0);
}