#include <sys/param.h>
#include <sys/systm.h>
#include <sys/resourcevar.h>
#include <sys/kernel.h>
#include <sys/fcntl.h>
#include <sys/stat.h>
#include <sys/buf.h>
#include <sys/proc.h>
#include <sys/vnode.h>
#include <sys/uio.h>
#include <miscfs/specfs/specdev.h>
#include <sys/ubc.h>
#include <vm/vm_pageout.h>
#include <sys/kdebug.h>
#include "hfs.h"
#include "hfs_dbg.h"
#include "hfs_endian.h"
#include "hfscommon/headers/FileMgrInternal.h"
#include "hfscommon/headers/BTreesInternal.h"
#define can_cluster(size) ((((size & (4096-1))) == 0) && (size <= (MAXPHYSIO/2)))
enum {
MAXHFSFILESIZE = 0x7FFFFFFF
};
extern u_int32_t GetLogicalBlockSize(struct vnode *vp);
#if DBG_VOP_TEST_LOCKS
extern void DbgVopTest(int maxSlots, int retval, VopDbgStoreRec *VopDbgStore, char *funcname);
#endif
#if HFS_DIAGNOSTIC
void debug_check_blocksizes(struct vnode *vp);
#endif
int
hfs_read(ap)
struct vop_read_args *ap;
{
register struct vnode *vp;
struct hfsnode *hp;
register struct uio *uio;
struct buf *bp;
daddr_t logBlockNo;
u_long fragSize, moveSize, startOffset, ioxfersize;
long devBlockSize = 0;
off_t bytesRemaining;
int retval;
u_short mode;
FCB *fcb;
DBG_FUNC_NAME("hfs_read");
DBG_VOP_LOCKS_DECL(1);
DBG_VOP_PRINT_FUNCNAME();
DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP_CONT(("\n"));
DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_POS);
vp = ap->a_vp;
hp = VTOH(vp);
fcb = HTOFCB(hp);
mode = hp->h_meta->h_mode;
uio = ap->a_uio;
#if HFS_DIAGNOSTIC
if (uio->uio_rw != UIO_READ)
panic("%s: mode", funcname);
#endif
if (ap->a_vp->v_type != VREG && ap->a_vp->v_type != VLNK) {
DBG_VOP_LOCKS_TEST(EISDIR);
return (EISDIR);
}
DBG_RW(("\tfile size Ox%X\n", (u_int)fcb->fcbEOF));
DBG_RW(("\tstarting at offset Ox%X of file, length Ox%X\n", (u_int)uio->uio_offset, (u_int)uio->uio_resid));
#if HFS_DIAGNOSTIC
debug_check_blocksizes(vp);
#endif
if (uio->uio_resid == 0) {
DBG_VOP_LOCKS_TEST(E_NONE);
return (E_NONE);
}
if (uio->uio_offset < 0) {
DBG_VOP_LOCKS_TEST(EINVAL);
return (EINVAL);
}
if (uio->uio_offset > fcb->fcbEOF) {
if ( (!ISHFSPLUS(VTOVCB(vp))) && (uio->uio_offset > (off_t)MAXHFSFILESIZE))
retval = EFBIG;
else
retval = E_NONE;
DBG_VOP_LOCKS_TEST(retval);
return (retval);
}
VOP_DEVBLOCKSIZE(hp->h_meta->h_devvp, &devBlockSize);
KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW, 12)) | DBG_FUNC_START,
(int)uio->uio_offset, uio->uio_resid, (int)fcb->fcbEOF, (int)fcb->fcbPLen, 0);
if (UBCISVALID(vp))
retval = cluster_read(vp, uio, (off_t)fcb->fcbEOF, devBlockSize, 0);
else {
for (retval = 0, bp = NULL; uio->uio_resid > 0; bp = NULL) {
if ((bytesRemaining = (fcb->fcbEOF - uio->uio_offset)) <= 0)
break;
logBlockNo = (daddr_t)(uio->uio_offset / PAGE_SIZE_64);
startOffset = (u_long) (uio->uio_offset & PAGE_MASK_64);
fragSize = PAGE_SIZE;
if (((logBlockNo * PAGE_SIZE) + fragSize) < fcb->fcbEOF)
ioxfersize = fragSize;
else {
ioxfersize = fcb->fcbEOF - (logBlockNo * PAGE_SIZE);
ioxfersize = (ioxfersize + (devBlockSize - 1)) & ~(devBlockSize - 1);
}
DBG_RW(("\tat logBlockNo Ox%X, with Ox%lX left to read\n", logBlockNo, (UInt32)uio->uio_resid));
moveSize = ioxfersize;
DBG_RW(("\tmoveSize = Ox%lX; ioxfersize = Ox%lX; startOffset = Ox%lX.\n",
moveSize, ioxfersize, startOffset));
DBG_ASSERT(moveSize >= startOffset);
moveSize -= startOffset;
if (bytesRemaining < moveSize)
moveSize = bytesRemaining;
if (uio->uio_resid < moveSize) {
moveSize = uio->uio_resid;
DBG_RW(("\treducing moveSize to Ox%lX (uio->uio_resid).\n", moveSize));
};
if (moveSize == 0) {
break;
};
DBG_RW(("\tat logBlockNo Ox%X, extent of Ox%lX, xfer of Ox%lX; moveSize = Ox%lX\n", logBlockNo, fragSize, ioxfersize, moveSize));
if (( uio->uio_offset + fragSize) >= fcb->fcbEOF) {
retval = bread(vp, logBlockNo, ioxfersize, NOCRED, &bp);
} else if (logBlockNo - 1 == vp->v_lastr && !(vp->v_flag & VRAOFF)) {
daddr_t nextLogBlockNo = logBlockNo + 1;
int nextsize;
if (((nextLogBlockNo * PAGE_SIZE) +
(daddr_t)fragSize) < fcb->fcbEOF)
nextsize = fragSize;
else {
nextsize = fcb->fcbEOF - (nextLogBlockNo * PAGE_SIZE);
nextsize = (nextsize + (devBlockSize - 1)) & ~(devBlockSize - 1);
}
retval = breadn(vp, logBlockNo, ioxfersize, &nextLogBlockNo, &nextsize, 1, NOCRED, &bp);
} else {
retval = bread(vp, logBlockNo, ioxfersize, NOCRED, &bp);
};
if (retval != E_NONE) {
if (bp) {
brelse(bp);
bp = NULL;
}
break;
};
vp->v_lastr = logBlockNo;
ioxfersize -= bp->b_resid;
if (ioxfersize < moveSize) {
if (ioxfersize == 0)
break;
moveSize = ioxfersize;
}
if ((startOffset + moveSize) > bp->b_bcount)
panic("hfs_read: bad startOffset or moveSize\n");
DBG_RW(("\tcopying Ox%lX bytes from %lX; resid = Ox%lX...\n", moveSize, (char *)bp->b_data + startOffset, bp->b_resid));
if ((retval = uiomove((caddr_t)bp->b_data + startOffset, (int)moveSize, uio)))
break;
if (S_ISREG(mode) &&
(((startOffset + moveSize) == fragSize) || (uio->uio_offset == fcb->fcbEOF))) {
bp->b_flags |= B_AGE;
};
DBG_ASSERT(bp->b_bcount == bp->b_validend);
brelse(bp);
}
if (bp != NULL) {
DBG_ASSERT(bp->b_bcount == bp->b_validend);
brelse(bp);
};
}
if (HTOVCB(hp)->vcbSigWord == kHFSPlusSigWord)
hp->h_nodeflags |= IN_ACCESS;
DBG_VOP_LOCKS_TEST(retval);
#if HFS_DIAGNOSTIC
debug_check_blocksizes(vp);
#endif
KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW, 12)) | DBG_FUNC_END,
(int)uio->uio_offset, uio->uio_resid, (int)fcb->fcbEOF, (int)fcb->fcbPLen, 0);
return (retval);
}
int
hfs_write(ap)
struct vop_write_args *ap;
{
struct hfsnode *hp = VTOH(ap->a_vp);
struct uio *uio = ap->a_uio;
struct vnode *vp = ap->a_vp ;
struct vnode *dev;
struct buf *bp;
struct proc *p, *cp;
struct timeval tv;
FCB *fcb = HTOFCB(hp);
ExtendedVCB *vcb = HTOVCB(hp);
long devBlockSize = 0;
daddr_t logBlockNo;
long fragSize;
off_t origFileSize, currOffset, writelimit, bytesToAdd;
off_t actualBytesAdded;
u_long blkoffset, resid, xfersize, clearSize;
int flags, ioflag;
int retval;
DBG_FUNC_NAME("hfs_write");
DBG_VOP_LOCKS_DECL(1);
DBG_VOP_PRINT_FUNCNAME();
DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP_CONT(("\n"));
DBG_RW(("\thfsnode 0x%x (%s)\n", (u_int)hp, H_NAME(hp)));
DBG_RW(("\tstarting at offset Ox%lX of file, length Ox%lX\n", (UInt32)uio->uio_offset, (UInt32)uio->uio_resid));
DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_POS);
dev = hp->h_meta->h_devvp;
#if HFS_DIAGNOSTIC
debug_check_blocksizes(vp);
#endif
if (uio->uio_offset < 0) {
DBG_VOP_LOCKS_TEST(EINVAL);
return (EINVAL);
}
if (uio->uio_resid == 0) {
DBG_VOP_LOCKS_TEST(E_NONE);
return (E_NONE);
}
if (ap->a_vp->v_type != VREG && ap->a_vp->v_type != VLNK) {
DBG_VOP_LOCKS_TEST(EISDIR);
return (EISDIR);
};
#if HFS_DIAGNOSTIC
if (uio->uio_rw != UIO_WRITE)
panic("%s: mode", funcname);
#endif
ioflag = ap->a_ioflag;
uio = ap->a_uio;
vp = ap->a_vp;
if (ioflag & IO_APPEND)
uio->uio_offset = fcb->fcbEOF;
if ((hp->h_meta->h_pflags & APPEND) && uio->uio_offset != fcb->fcbEOF)
return (EPERM);
writelimit = uio->uio_offset + uio->uio_resid;
p = uio->uio_procp;
if (vp->v_type == VREG && p &&
writelimit > p->p_rlimit[RLIMIT_FSIZE].rlim_cur) {
psignal(p, SIGXFSZ);
return (EFBIG);
};
VOP_DEVBLOCKSIZE(hp->h_meta->h_devvp, &devBlockSize);
resid = uio->uio_resid;
origFileSize = fcb->fcbPLen;
flags = ioflag & IO_SYNC ? B_SYNC : 0;
DBG_RW(("\tLEOF is 0x%lX, PEOF is 0x%lX.\n", fcb->fcbEOF, fcb->fcbPLen));
currOffset = MIN(uio->uio_offset, fcb->fcbEOF);
DBG_RW(("\tstarting I/O loop at 0x%lX.\n", (u_long)currOffset));
cp = current_proc();
KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW, 0)) | DBG_FUNC_START,
(int)uio->uio_offset, uio->uio_resid, (int)fcb->fcbEOF, (int)fcb->fcbPLen, 0);
retval = 0;
if (fcb->fcbEOF > fcb->fcbMaxEOF)
fcb->fcbMaxEOF = fcb->fcbEOF;
while (writelimit > (off_t)fcb->fcbPLen) {
bytesToAdd = writelimit - fcb->fcbPLen;
DBG_RW(("\textending file by 0x%lX bytes; 0x%lX blocks free",
(unsigned long)bytesToAdd, (unsigned long)vcb->freeBlocks));
retval = hfs_metafilelocking(HTOHFS(hp), kHFSExtentsFileID, LK_EXCLUSIVE, cp);
if (retval != E_NONE)
break;
retval = MacToVFSError(
ExtendFileC (vcb,
fcb,
bytesToAdd,
kEFContigBit,
&actualBytesAdded));
(void) hfs_metafilelocking(HTOHFS(hp), kHFSExtentsFileID, LK_RELEASE, cp);
DBG_VOP_CONT(("\tactual bytes added = 0x%lX bytes, retval = %d...\n", actualBytesAdded, retval));
if ((actualBytesAdded == 0) && (retval == E_NONE))
retval = ENOSPC;
if (retval != E_NONE)
break;
KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW, 0)) | DBG_FUNC_NONE,
(int)uio->uio_offset, uio->uio_resid, (int)fcb->fcbEOF, (int)fcb->fcbPLen, 0);
};
if (UBCISVALID(vp) && retval == E_NONE) {
off_t filesize;
off_t zero_off;
int lflag;
if (writelimit > fcb->fcbEOF)
filesize = writelimit;
else
filesize = fcb->fcbEOF;
lflag = (ioflag & IO_SYNC);
if (uio->uio_offset > fcb->fcbMaxEOF) {
zero_off = fcb->fcbMaxEOF;
lflag |= IO_HEADZEROFILL;
} else
zero_off = 0;
retval = cluster_write(vp, uio, fcb->fcbEOF, filesize, zero_off,
(off_t)0, devBlockSize, lflag);
if (uio->uio_offset > fcb->fcbEOF) {
fcb->fcbEOF = uio->uio_offset;
if (fcb->fcbEOF > fcb->fcbMaxEOF)
fcb->fcbMaxEOF = fcb->fcbEOF;
ubc_setsize(vp, (off_t)fcb->fcbEOF);
}
if (resid > uio->uio_resid)
hp->h_nodeflags |= IN_CHANGE | IN_UPDATE;
} else {
while (retval == E_NONE && uio->uio_resid > 0) {
logBlockNo = currOffset / PAGE_SIZE;
blkoffset = currOffset & PAGE_MASK;
if (((off_t)(fcb->fcbPLen) - currOffset) < PAGE_SIZE_64)
fragSize = (off_t)(fcb->fcbPLen) - ((off_t)logBlockNo * PAGE_SIZE_64);
else
fragSize = PAGE_SIZE;
xfersize = fragSize - blkoffset;
DBG_RW(("\tcurrOffset = Ox%lX, logBlockNo = Ox%X, blkoffset = Ox%lX, xfersize = Ox%lX, fragSize = Ox%lX.\n",
(unsigned long)currOffset, logBlockNo, blkoffset, xfersize, fragSize));
if (currOffset + (off_t)xfersize > writelimit) {
xfersize = writelimit - currOffset;
DBG_RW(("\ttrimming xfersize to 0x%lX to match writelimit (uio_resid)...\n", xfersize));
};
if ((blkoffset == 0) && (xfersize >= fragSize)) {
DBG_RW(("\tRequesting %ld-byte block Ox%lX w/o read...\n", fragSize, (long)logBlockNo));
bp = getblk(vp, logBlockNo, fragSize, 0, 0, BLK_READ);
retval = 0;
if (bp->b_blkno == -1) {
brelse(bp);
retval = EIO;
break;
}
} else {
if (currOffset == fcb->fcbEOF && blkoffset == 0) {
bp = getblk(vp, logBlockNo, fragSize, 0, 0, BLK_READ);
retval = 0;
if (bp->b_blkno == -1) {
brelse(bp);
retval = EIO;
break;
}
} else {
DBG_VOP(("\tRequesting block Ox%X, size = 0x%08lX...\n", logBlockNo, fragSize));
retval = bread(vp, logBlockNo, fragSize, ap->a_cred, &bp);
if (retval != E_NONE) {
if (bp)
brelse(bp);
break;
}
}
}
if (uio->uio_offset > currOffset) {
clearSize = MIN(uio->uio_offset - currOffset, xfersize);
DBG_RW(("\tzeroing Ox%lX bytes Ox%lX bytes into block Ox%X...\n", clearSize, blkoffset, logBlockNo));
bzero(bp->b_data + blkoffset, clearSize);
currOffset += clearSize;
blkoffset += clearSize;
xfersize -= clearSize;
};
if (xfersize > 0) {
DBG_RW(("\tCopying Ox%lX bytes Ox%lX bytes into block Ox%X... ioflag == 0x%X\n",
xfersize, blkoffset, logBlockNo, ioflag));
retval = uiomove((caddr_t)bp->b_data + blkoffset, (int)xfersize, uio);
currOffset += xfersize;
};
DBG_ASSERT((bp->b_bcount % devBlockSize) == 0);
if (ioflag & IO_SYNC) {
(void)VOP_BWRITE(bp);
} else if ((xfersize + blkoffset) == fragSize) {
bp->b_flags |= B_AGE;
bawrite(bp);
} else {
bdwrite(bp);
};
if (currOffset > fcb->fcbEOF) {
DBG_VOP(("\textending EOF to 0x%lX...\n", (UInt32)fcb->fcbEOF));
fcb->fcbEOF = currOffset;
if (fcb->fcbEOF > fcb->fcbMaxEOF)
fcb->fcbMaxEOF = fcb->fcbEOF;
if (UBCISVALID(vp))
ubc_setsize(vp, (off_t)fcb->fcbEOF);
};
if (retval || (resid == 0))
break;
hp->h_nodeflags |= IN_CHANGE | IN_UPDATE;
};
};
if (resid > uio->uio_resid && ap->a_cred && ap->a_cred->cr_uid != 0)
hp->h_meta->h_mode &= ~(ISUID | ISGID);
if (retval) {
if (ioflag & IO_UNIT) {
(void)VOP_TRUNCATE(vp, origFileSize,
ioflag & IO_SYNC, ap->a_cred, uio->uio_procp);
uio->uio_offset -= resid - uio->uio_resid;
uio->uio_resid = resid;
}
} else if (resid > uio->uio_resid && (ioflag & IO_SYNC)) {
tv = time;
retval = VOP_UPDATE(vp, &tv, &tv, 1);
}
#if HFS_DIAGNOSTIC
debug_check_blocksizes(vp);
#endif
KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW, 0)) | DBG_FUNC_END,
(int)uio->uio_offset, uio->uio_resid, (int)fcb->fcbEOF, (int)fcb->fcbPLen, 0);
DBG_VOP_LOCKS_TEST(retval);
return (retval);
}
int
hfs_ioctl(ap)
struct vop_ioctl_args *ap;
{
DBG_FUNC_NAME("hfs_ioctl");
DBG_VOP_LOCKS_DECL(1);
DBG_VOP_PRINT_FUNCNAME();
DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP_CONT(("\n"));
DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_UNLOCKED, VOPDBG_UNLOCKED, VOPDBG_UNLOCKED, VOPDBG_POS);
switch (ap->a_command) {
case 1:
{ register struct hfsnode *hp;
register struct vnode *vp;
register struct radvisory *ra;
FCB *fcb;
int devBlockSize = 0;
int error;
vp = ap->a_vp;
VOP_LEASE(vp, ap->a_p, ap->a_cred, LEASE_READ);
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, ap->a_p);
ra = (struct radvisory *)(ap->a_data);
hp = VTOH(vp);
fcb = HTOFCB(hp);
if (ra->ra_offset >= fcb->fcbEOF) {
VOP_UNLOCK(vp, 0, ap->a_p);
DBG_VOP_LOCKS_TEST(EFBIG);
return (EFBIG);
}
VOP_DEVBLOCKSIZE(hp->h_meta->h_devvp, &devBlockSize);
error = advisory_read(vp, fcb->fcbEOF, ra->ra_offset, ra->ra_count, devBlockSize);
VOP_UNLOCK(vp, 0, ap->a_p);
DBG_VOP_LOCKS_TEST(error);
return (error);
}
case 2:
case 3:
{
struct vnode *vp = ap->a_vp;
struct hfsnode *hp = VTOH(vp);
struct fbootstraptransfer *btd = (struct fbootstraptransfer *)ap->a_data;
u_long devBlockSize;
int error;
struct iovec aiov;
struct uio auio;
u_long blockNumber;
u_long blockOffset;
u_long xfersize;
struct buf *bp;
if ((vp->v_flag & VROOT) == 0) return EINVAL;
if (btd->fbt_offset + btd->fbt_length > 1024) return EINVAL;
aiov.iov_base = btd->fbt_buffer;
aiov.iov_len = btd->fbt_length;
auio.uio_iov = &aiov;
auio.uio_iovcnt = 1;
auio.uio_offset = btd->fbt_offset;
auio.uio_resid = btd->fbt_length;
auio.uio_segflg = UIO_USERSPACE;
auio.uio_rw = (ap->a_command == 3) ? UIO_WRITE : UIO_READ;
auio.uio_procp = ap->a_p;
VOP_DEVBLOCKSIZE(hp->h_meta->h_devvp, &devBlockSize);
while (auio.uio_resid > 0) {
blockNumber = auio.uio_offset / devBlockSize;
error = bread(hp->h_meta->h_devvp, blockNumber, devBlockSize, ap->a_cred, &bp);
if (error) {
if (bp) brelse(bp);
return error;
};
blockOffset = auio.uio_offset % devBlockSize;
xfersize = devBlockSize - blockOffset;
error = uiomove((caddr_t)bp->b_data + blockOffset, (int)xfersize, &auio);
if (error) {
brelse(bp);
return error;
};
if (auio.uio_rw == UIO_WRITE) {
error = VOP_BWRITE(bp);
if (error) return error;
} else {
brelse(bp);
};
};
};
return 0;
default:
DBG_VOP_LOCKS_TEST(ENOTTY);
return (ENOTTY);
}
return 0;
}
int
hfs_select(ap)
struct vop_select_args *ap;
{
DBG_FUNC_NAME("hfs_select");
DBG_VOP_LOCKS_DECL(1);
DBG_VOP_PRINT_FUNCNAME();
DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP_CONT(("\n"));
DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_LOCKED, VOPDBG_IGNORE, VOPDBG_IGNORE, VOPDBG_POS);
DBG_VOP_LOCKS_TEST(1);
return (1);
}
int
hfs_mmap(ap)
struct vop_mmap_args *ap;
{
DBG_FUNC_NAME("hfs_mmap");
DBG_VOP_LOCKS_DECL(1);
DBG_VOP_PRINT_FUNCNAME();
DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP_CONT(("\n"));
DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_IGNORE, VOPDBG_IGNORE, VOPDBG_IGNORE, VOPDBG_POS);
DBG_VOP_LOCKS_TEST(EINVAL);
return (EINVAL);
}
int
hfs_seek(ap)
struct vop_seek_args *ap;
{
DBG_FUNC_NAME("hfs_seek");
DBG_VOP_LOCKS_DECL(1);
DBG_VOP_PRINT_FUNCNAME();
DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP_CONT(("\n"));
DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_IGNORE, VOPDBG_IGNORE, VOPDBG_IGNORE, VOPDBG_POS);
DBG_VOP_LOCKS_TEST(E_NONE);
return (E_NONE);
}
int
hfs_bmap(ap)
struct vop_bmap_args *ap;
{
struct hfsnode *hp = VTOH(ap->a_vp);
struct hfsmount *hfsmp = VTOHFS(ap->a_vp);
int retval = E_NONE;
daddr_t logBlockSize;
size_t bytesContAvail = 0;
struct proc *p = NULL;
int lockExtBtree;
#define DEBUG_BMAP 0
#if DEBUG_BMAP
DBG_FUNC_NAME("hfs_bmap");
DBG_VOP_LOCKS_DECL(2);
DBG_VOP_PRINT_FUNCNAME();
DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);
DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_POS);
if (ap->a_vpp != NULL) {
DBG_VOP_LOCKS_INIT(1,*ap->a_vpp, VOPDBG_IGNORE, VOPDBG_UNLOCKED, VOPDBG_IGNORE, VOPDBG_POS);
} else {
DBG_VOP_LOCKS_INIT(1,NULL, VOPDBG_IGNORE, VOPDBG_IGNORE, VOPDBG_IGNORE, VOPDBG_POS);
};
#endif
DBG_IO(("\tMapped blk %d --> ", ap->a_bn));
if (ap->a_vpp != NULL)
*ap->a_vpp = VTOH(ap->a_vp)->h_meta->h_devvp;
if (ap->a_bnp == NULL)
return (0);
lockExtBtree = hasOverflowExtents(hp);
if (lockExtBtree)
{
p = current_proc();
retval = hfs_metafilelocking(hfsmp, kHFSExtentsFileID, LK_EXCLUSIVE | LK_CANRECURSE, p);
if (retval)
return (retval);
}
logBlockSize = GetLogicalBlockSize(ap->a_vp);
retval = MacToVFSError(
MapFileBlockC (HFSTOVCB(hfsmp),
HTOFCB(hp),
MAXPHYSIO,
(off_t)(ap->a_bn * logBlockSize),
ap->a_bnp,
&bytesContAvail));
if (lockExtBtree) (void) hfs_metafilelocking(hfsmp, kHFSExtentsFileID, LK_RELEASE, p);
if (retval == E_NONE) {
if (ap->a_runp != NULL) {
if (can_cluster(logBlockSize)) {
*ap->a_runp = (bytesContAvail < logBlockSize) ? 0 : (bytesContAvail / logBlockSize) - 1;
} else {
*ap->a_runp = 0;
};
};
};
DBG_IO(("%d:%d.\n", *ap->a_bnp, (bytesContAvail < logBlockSize) ? 0 : (bytesContAvail / logBlockSize) - 1));
#if DEBUG_BMAP
DBG_VOP_LOCKS_TEST(retval);
#endif
if (ap->a_runp) {
DBG_ASSERT((*ap->a_runp * logBlockSize) < bytesContAvail);
if (can_cluster(logBlockSize)) {
DBG_ASSERT(bytesContAvail - (*ap->a_runp * logBlockSize) < (2*logBlockSize));
};
};
return (retval);
}
int
hfs_blktooff(ap)
struct vop_blktooff_args *ap;
{
if (ap->a_vp == NULL)
return (EINVAL);
*ap->a_offset = (off_t)ap->a_lblkno * PAGE_SIZE_64;
return(0);
}
int
hfs_offtoblk(ap)
struct vop_offtoblk_args *ap;
{
long lbsize, boff;
if (ap->a_vp == NULL)
return (EINVAL);
*ap->a_lblkno = ap->a_offset / PAGE_SIZE_64;
return(0);
}
int
hfs_cmap(ap)
struct vop_cmap_args *ap;
{
struct hfsnode *hp = VTOH(ap->a_vp);
struct hfsmount *hfsmp = VTOHFS(ap->a_vp);
size_t bytesContAvail = 0;
int retval = E_NONE;
int lockExtBtree;
struct proc *p = NULL;
#define DEBUG_CMAP 0
#if DEBUG_CMAP
DBG_FUNC_NAME("hfs_cmap");
DBG_VOP_LOCKS_DECL(2);
DBG_VOP_PRINT_FUNCNAME();
DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);
DBG_VOP_LOCKS_INIT(0, ap->a_vp, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_POS);
#endif
DBG_IO(("\tMapped offset %qx --> ", ap->a_foffset));
if (ap->a_bpn == NULL)
return (0);
if (lockExtBtree = hasOverflowExtents(hp))
{
p = current_proc();
if (retval = hfs_metafilelocking(hfsmp, kHFSExtentsFileID, LK_EXCLUSIVE | LK_CANRECURSE, p))
return (retval);
}
retval = MacToVFSError(
MapFileBlockC (HFSTOVCB(hfsmp),
HTOFCB(hp),
ap->a_size,
ap->a_foffset,
ap->a_bpn,
&bytesContAvail));
if (lockExtBtree) (void) hfs_metafilelocking(hfsmp, kHFSExtentsFileID, LK_RELEASE, p);
if ((retval == E_NONE) && (ap->a_run))
*ap->a_run = bytesContAvail;
if (ap->a_poff)
*(int *)ap->a_poff = 0;
DBG_IO(("%d:%d.\n", *ap->a_bpn, bytesContAvail));
#if DEBUG_BMAP
DBG_VOP_LOCKS_TEST(retval);
#endif
return (retval);
}
int
hfs_strategy(ap)
struct vop_strategy_args *ap;
{
register struct buf *bp = ap->a_bp;
register struct vnode *vp = bp->b_vp;
register struct hfsnode *hp;
int retval = 0;
DBG_FUNC_NAME("hfs_strategy");
hp = VTOH(vp);
if ( !(bp->b_flags & B_VECTORLIST)) {
if (vp->v_type == VBLK || vp->v_type == VCHR)
panic("hfs_strategy: device vnode passed!");
if (bp->b_flags & B_PAGELIST) {
retval = cluster_bp(bp);
vp = hp->h_meta->h_devvp;
bp->b_dev = vp->v_rdev;
return (retval);
}
if (bp->b_blkno == bp->b_lblkno) {
if ((retval = VOP_BMAP(vp, bp->b_lblkno, NULL, &bp->b_blkno, NULL))) {
bp->b_error = retval;
bp->b_flags |= B_ERROR;
biodone(bp);
return (retval);
}
if ((long)bp->b_blkno == -1)
clrbuf(bp);
}
if ((long)bp->b_blkno == -1) {
biodone(bp);
return (0);
}
if (bp->b_validend == 0) {
DBG_ASSERT(bp->b_validoff == 0);
bp->b_validend = bp->b_bcount;
DBG_ASSERT(bp->b_dirtyoff == 0);
};
}
vp = hp->h_meta->h_devvp;
bp->b_dev = vp->v_rdev;
DBG_IO(("\t\t>>>%s: continuing w/ vp: 0x%x with logBlk Ox%X and phyBlk Ox%X\n", funcname, (u_int)vp, bp->b_lblkno, bp->b_blkno));
return VOCALL (vp->v_op, VOFFSET(vop_strategy), ap);
}
int
hfs_reallocblks(ap)
struct vop_reallocblks_args *ap;
{
DBG_FUNC_NAME("hfs_reallocblks");
DBG_VOP_LOCKS_DECL(1);
DBG_VOP_PRINT_FUNCNAME();
DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP_CONT(("\n"));
DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_POS);
DBG_VOP_LOCKS_TEST(ENOSPC);
return (ENOSPC);
}
int hfs_truncate(ap)
struct vop_truncate_args *ap;
{
register struct vnode *vp = ap->a_vp;
register struct hfsnode *hp = VTOH(vp);
off_t length = ap->a_length;
long vflags;
struct timeval tv;
int retval;
FCB *fcb;
off_t bytesToAdd;
off_t actualBytesAdded;
DBG_FUNC_NAME("hfs_truncate");
DBG_VOP_LOCKS_DECL(1);
DBG_VOP_PRINT_FUNCNAME();
DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP_CONT(("\n"));
DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_POS);
#if HFS_DIAGNOSTIC
debug_check_blocksizes(ap->a_vp);
#endif
fcb = HTOFCB(hp);
KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW, 7)) | DBG_FUNC_START,
(int)length, fcb->fcbEOF, fcb->fcbPLen, 0, 0);
if (length < 0) {
DBG_VOP_LOCKS_TEST(EINVAL);
return (EINVAL);
}
if ((!ISHFSPLUS(VTOVCB(vp))) && (length > (off_t)MAXHFSFILESIZE)) {
DBG_VOP_LOCKS_TEST(EFBIG);
return (EFBIG);
}
if (vp->v_type != VREG && vp->v_type != VLNK) {
DBG_VOP_LOCKS_TEST(EISDIR);
return (EISDIR);
}
tv = time;
retval = E_NONE;
DBG_RW(("%s: truncate from Ox%lX to Ox%X bytes\n", funcname, fcb->fcbPLen, length));
if (fcb->fcbEOF > fcb->fcbMaxEOF)
fcb->fcbMaxEOF = fcb->fcbEOF;
if (length > fcb->fcbEOF) {
off_t filePosition;
daddr_t logBlockNo;
long logBlockSize;
long blkOffset;
off_t bytestoclear;
int blockZeroCount;
struct buf *bp=NULL;
if (length > fcb->fcbPLen) {
retval = hfs_metafilelocking(HTOHFS(hp), kHFSExtentsFileID, LK_EXCLUSIVE, ap->a_p);
if (retval)
goto Err_Exit;
while ((length > fcb->fcbPLen) && (retval == E_NONE)) {
bytesToAdd = length - fcb->fcbPLen;
retval = MacToVFSError(
ExtendFileC (HTOVCB(hp),
fcb,
bytesToAdd,
kEFAllMask,
&actualBytesAdded));
if (actualBytesAdded == 0 && retval == E_NONE) {
if (length > fcb->fcbPLen)
length = fcb->fcbPLen;
break;
}
}
(void) hfs_metafilelocking(HTOHFS(hp), kHFSExtentsFileID, LK_RELEASE, ap->a_p);
if (retval)
goto Err_Exit;
DBG_ASSERT(length <= fcb->fcbPLen);
KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW, 7)) | DBG_FUNC_NONE,
(int)length, fcb->fcbEOF, fcb->fcbPLen, 0, 0);
}
if (! (ap->a_flags & IO_NOZEROFILL)) {
if (UBCISVALID(vp) && retval == E_NONE) {
u_long devBlockSize;
if (length > fcb->fcbMaxEOF) {
VOP_DEVBLOCKSIZE(hp->h_meta->h_devvp, &devBlockSize);
retval = cluster_write(vp, (struct uio *) 0, fcb->fcbEOF, length, fcb->fcbMaxEOF,
(off_t)0, devBlockSize, ((ap->a_flags & IO_SYNC) | IO_HEADZEROFILL));
if (retval)
goto Err_Exit;
}
} else {
bytestoclear = length - fcb->fcbEOF;
filePosition = fcb->fcbEOF;
while (bytestoclear > 0) {
logBlockNo = (daddr_t)(filePosition / PAGE_SIZE_64);
blkOffset = (long)(filePosition & PAGE_MASK_64);
if (((off_t)(fcb->fcbPLen) - ((off_t)logBlockNo * (off_t)PAGE_SIZE)) < PAGE_SIZE_64)
logBlockSize = (off_t)(fcb->fcbPLen) - ((off_t)logBlockNo * PAGE_SIZE_64);
else
logBlockSize = PAGE_SIZE;
if (logBlockSize < blkOffset)
panic("hfs_truncate: bad logBlockSize computed\n");
blockZeroCount = MIN(bytestoclear, logBlockSize - blkOffset);
if (blkOffset == 0 && ((bytestoclear >= logBlockSize) || filePosition >= fcb->fcbEOF)) {
bp = getblk(vp, logBlockNo, logBlockSize, 0, 0, BLK_WRITE);
retval = 0;
} else {
retval = bread(vp, logBlockNo, logBlockSize, ap->a_cred, &bp);
if (retval) {
brelse(bp);
goto Err_Exit;
}
}
bzero((char *)bp->b_data + blkOffset, blockZeroCount);
bp->b_flags |= B_DIRTY | B_AGE;
if (ap->a_flags & IO_SYNC)
VOP_BWRITE(bp);
else if (logBlockNo % 32)
bawrite(bp);
else
VOP_BWRITE(bp);
bytestoclear -= blockZeroCount;
filePosition += blockZeroCount;
}
};
}
fcb->fcbEOF = length;
if (fcb->fcbEOF > fcb->fcbMaxEOF)
fcb->fcbMaxEOF = fcb->fcbEOF;
if (UBCISVALID(vp))
ubc_setsize(vp, (off_t)fcb->fcbEOF);
} else {
if (fcb->fcbEOF > length) {
if (UBCISVALID(vp))
ubc_setsize(vp, (off_t)length);
vflags = ((length > 0) ? V_SAVE : 0) | V_SAVEMETA;
retval = vinvalbuf(vp, vflags, ap->a_cred, ap->a_p, 0, 0);
}
if ((ap->a_flags & IO_NDELAY) || (!ISSET(ap->a_p->p_flag, P_TBE))) {
retval = hfs_metafilelocking(HTOHFS(hp), kHFSExtentsFileID, LK_EXCLUSIVE, ap->a_p);
if (retval)
goto Err_Exit;
retval = MacToVFSError(
TruncateFileC(
HTOVCB(hp),
fcb,
length,
false));
(void) hfs_metafilelocking(HTOHFS(hp), kHFSExtentsFileID, LK_RELEASE, ap->a_p);
if (retval)
goto Err_Exit;
fcb->fcbMaxEOF = length;
}
fcb->fcbEOF = length;
if (fcb->fcbFlags & fcbModifiedMask)
hp->h_nodeflags |= IN_MODIFIED;
}
hp->h_nodeflags |= IN_CHANGE | IN_UPDATE;
retval = VOP_UPDATE(vp, &tv, &tv, MNT_WAIT);
if (retval) {
DBG_ERR(("Could not update truncate"));
KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW, 7)) | DBG_FUNC_NONE,
-1, -1, -1, retval, 0);
}
Err_Exit:;
#if HFS_DIAGNOSTIC
debug_check_blocksizes(ap->a_vp);
#endif
KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW, 7)) | DBG_FUNC_END,
(int)length, fcb->fcbEOF, fcb->fcbPLen, retval, 0);
DBG_VOP_LOCKS_TEST(retval);
return (retval);
}
int hfs_allocate(ap)
struct vop_allocate_args *ap;
{
register struct vnode *vp = ap->a_vp;
register struct hfsnode *hp = VTOH(vp);
off_t length = ap->a_length;
off_t startingPEOF;
off_t moreBytesRequested;
off_t actualBytesAdded;
long vflags;
struct timeval tv;
int retval, retval2;
FCB *fcb;
UInt32 extendFlags =0;
DBG_FUNC_NAME("hfs_allocate");
DBG_VOP_LOCKS_DECL(1);
DBG_VOP_PRINT_FUNCNAME();
DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP_CONT(("\n"));
DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_POS);
*(ap->a_bytesallocated) = 0;
if (length < (off_t)0) {
DBG_VOP_LOCKS_TEST(EINVAL);
return (EINVAL);
}
if (vp->v_type != VREG && vp->v_type != VLNK) {
DBG_VOP_LOCKS_TEST(EISDIR);
return (EISDIR);
}
if (ap->a_flags & ALLOCATECONTIG) {
extendFlags |= kEFContigMask;
}
if (ap->a_flags & ALLOCATEALL) {
extendFlags |= kEFAllMask;
}
fcb = HTOFCB(hp);
tv = time;
retval = E_NONE;
startingPEOF = fcb->fcbPLen;
if (ap->a_flags & ALLOCATEFROMPEOF) {
length += fcb->fcbPLen;
}
DBG_RW(("%s: allocate from Ox%lX to Ox%X bytes\n", funcname, fcb->fcbPLen, (u_int)length));
if (fcb->fcbPLen == length)
goto Std_Exit;
if (length > fcb->fcbPLen) {
moreBytesRequested = length - fcb->fcbPLen;
retval = hfs_metafilelocking(HTOHFS(hp), kHFSExtentsFileID, LK_EXCLUSIVE, ap->a_p);
if (retval) goto Err_Exit;
retval = MacToVFSError(
ExtendFileC(HTOVCB(hp),
fcb,
moreBytesRequested,
extendFlags,
&actualBytesAdded));
*(ap->a_bytesallocated) = actualBytesAdded;
(void) hfs_metafilelocking(HTOHFS(hp), kHFSExtentsFileID, LK_RELEASE, ap->a_p);
DBG_ASSERT(length <= fcb->fcbPLen);
if (retval && (startingPEOF == fcb->fcbPLen)) goto Err_Exit;
if ((actualBytesAdded != 0) && (moreBytesRequested < actualBytesAdded)) {
u_long blks, blocksize;
blocksize = VTOVCB(vp)->blockSize;
blks = moreBytesRequested / blocksize;
if ((blks * blocksize) != moreBytesRequested)
blks++;
*(ap->a_bytesallocated) = blks * blocksize;
}
} else {
if (fcb->fcbEOF > length) {
vflags = ((length > 0) ? V_SAVE : 0) | V_SAVEMETA;
(void) vinvalbuf(vp, vflags, ap->a_cred, ap->a_p, 0, 0);
}
retval = hfs_metafilelocking(HTOHFS(hp), kHFSExtentsFileID, LK_EXCLUSIVE, ap->a_p);
if (retval) goto Err_Exit;
retval = MacToVFSError(
TruncateFileC(
HTOVCB(hp),
fcb,
length,
false));
(void) hfs_metafilelocking(HTOHFS(hp), kHFSExtentsFileID, LK_RELEASE, ap->a_p);
if (retval && (startingPEOF == fcb->fcbPLen)) goto Err_Exit;
if (fcb->fcbFlags & fcbModifiedMask)
hp->h_nodeflags |= IN_MODIFIED;
DBG_ASSERT(length <= fcb->fcbPLen)
if (fcb->fcbEOF > fcb->fcbPLen) {
fcb->fcbEOF = fcb->fcbPLen;
fcb->fcbMaxEOF = fcb->fcbPLen;
if (UBCISVALID(vp))
ubc_setsize(vp, (off_t)fcb->fcbEOF);
}
}
Std_Exit:
hp->h_nodeflags |= IN_CHANGE | IN_UPDATE;
retval2 = VOP_UPDATE(vp, &tv, &tv, MNT_WAIT);
if (retval == 0) retval = retval2;
Err_Exit:
DBG_VOP_LOCKS_TEST(retval);
return (retval);
}
int
hfs_pagein(ap)
struct vop_pagein_args *ap;
{
register struct vnode *vp;
struct hfsnode *hp;
FCB *fcb;
long devBlockSize = 0;
int retval;
DBG_FUNC_NAME("hfs_pagein");
DBG_VOP_LOCKS_DECL(1);
DBG_VOP_PRINT_FUNCNAME();
DBG_VOP_PRINT_VNODE_INFO(vp);DBG_VOP_CONT(("\n"));
DBG_VOP_LOCKS_INIT(0,vp, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_POS);
vp = ap->a_vp;
hp = VTOH(vp);
fcb = HTOFCB(hp);
if (vp->v_type != VREG && vp->v_type != VLNK)
panic("hfs_pagein: vp not UBC type\n");
DBG_VOP(("\tfile size Ox%X\n", (u_int)fcb->fcbEOF));
DBG_VOP(("\tstarting at offset Ox%X of file, length Ox%X\n", (u_int)ap->a_f_offset, (u_int)ap->a_size));
#if HFS_DIAGNOSTIC
debug_check_blocksizes(vp);
#endif
VOP_DEVBLOCKSIZE(hp->h_meta->h_devvp, &devBlockSize);
retval = cluster_pagein(vp, ap->a_pl, ap->a_pl_offset, ap->a_f_offset,
ap->a_size, (off_t)fcb->fcbEOF, devBlockSize,
ap->a_flags);
#if HFS_DIAGNOSTIC
debug_check_blocksizes(vp);
#endif
DBG_VOP_LOCKS_TEST(retval);
return (retval);
}
int
hfs_pageout(ap)
struct vop_pageout_args *ap;
{
struct vnode *vp = ap->a_vp;
struct hfsnode *hp = VTOH(vp);
FCB *fcb = HTOFCB(hp);
int retval;
long devBlockSize = 0;
DBG_FUNC_NAME("hfs_pageout");
DBG_VOP_LOCKS_DECL(1);
DBG_VOP_PRINT_FUNCNAME();
DBG_VOP_PRINT_VNODE_INFO(vp);DBG_VOP_CONT(("\n"));
DBG_VOP(("\thfsnode 0x%x (%s)\n", (u_int)hp, H_NAME(hp)));
DBG_VOP(("\tstarting at offset Ox%lX of file, length Ox%lX\n",
(UInt32)ap->a_f_offset, (UInt32)ap->a_size));
DBG_VOP_LOCKS_INIT(0, vp, VOPDBG_LOCKED,
VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_POS);
#if HFS_DIAGNOSTIC
debug_check_blocksizes(vp);
#endif
if (UBCINVALID(vp))
panic("hfs_pageout: Not a VREG: vp=%x", vp);
VOP_DEVBLOCKSIZE(hp->h_meta->h_devvp, &devBlockSize);
retval = cluster_pageout(vp, ap->a_pl, ap->a_pl_offset, ap->a_f_offset, ap->a_size,
(off_t)fcb->fcbEOF, devBlockSize, ap->a_flags);
if (retval == 0 && ap->a_cred && ap->a_cred->cr_uid != 0)
hp->h_meta->h_mode &= ~(ISUID | ISGID);
#if HFS_DIAGNOSTIC
debug_check_blocksizes(vp);
#endif
DBG_VOP_LOCKS_TEST(retval);
return (retval);
}
int
hfs_bwrite(ap)
struct vop_bwrite_args *ap;
{
register struct buf *bp = ap->a_bp;
register struct vnode *vp = bp->b_vp;
BlockDescriptor block;
int retval = 0;
DBG_FUNC_NAME("hfs_bwrite");
#if BYTE_ORDER == LITTLE_ENDIAN
if ((H_FILEID(VTOH(vp)) == kHFSExtentsFileID) ||
(H_FILEID(VTOH(vp)) == kHFSCatalogFileID)) {
if (((UInt16 *)((char *)bp->b_data + bp->b_bcount - 2))[0] == 0x000e) {
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;
block.blockSize = bp->b_bcount;
SWAP_BT_NODE (&block, ISHFSPLUS (VTOVCB(vp)), H_FILEID(VTOH(vp)), 1);
}
}
#endif
retval = vn_bwrite (ap);
return (retval);
}