/* * 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_readwrite.c 1.0 * * (c) 1990, 1992 NeXT Computer, Inc. All Rights Reserved * (c) 1998 Apple Computer, Inc. All Rights Reserved * * * hfs_readwrite.c -- vnode operations to deal with reading and writing files. * * MODIFICATION HISTORY: * 9-Nov-1999 Scott Roberts hfs_allocate now returns sizes based on allocation block boundaries (#2398794) * 3-Feb-1999 Pat Dirks Merged in Joe's change to hfs_truncate to skip vinvalbuf if LEOF isn't changing (#2302796) * Removed superfluous (and potentially dangerous) second call to vinvalbuf() in hfs_truncate. * 2-Dec-1998 Pat Dirks Added support for read/write bootstrap ioctls. * 10-Nov-1998 Pat Dirks Changed read/write/truncate logic to optimize block sizes for first extents of a file. * Changed hfs_strategy to correct I/O sizes from cluser code I/O requests in light of * different block sizing. Changed bexpand to handle RELEASE_BUFFER flag. * 22-Sep-1998 Don Brady Changed truncate zero-fill to use bwrite after several bawrites have been queued. * 11-Sep-1998 Pat Dirks Fixed buffering logic to not rely on B_CACHE, which is set for empty buffers that * have been pre-read by cluster_read (use b_validend > 0 instead). * 27-Aug-1998 Pat Dirks Changed hfs_truncate to use cluster_write in place of bawrite where possible. * 25-Aug-1998 Pat Dirks Changed hfs_write to do small device-block aligned writes into buffers without doing * read-ahead of the buffer. Added bexpand to deal with incomplete [dirty] buffers. * Fixed can_cluster macro to use MAXPHYSIO instead of MAXBSIZE. * 19-Aug-1998 Don Brady Remove optimization in hfs_truncate that prevented extra physical blocks from * being truncated (radar #2265750). Also set fcb->fcbEOF before calling vinvalbuf. * 7-Jul-1998 Pat Dirks Added code to honor IO_NOZEROFILL in hfs_truncate. * 16-Jul-1998 Don Brady In hfs_bmap use MAXPHYSIO instead of MAXBSIZE when calling MapFileBlockC (radar #2263753). * 16-Jul-1998 Don Brady Fix error handling in hfs_allocate (radar #2252265). * 04-Jul-1998 chw Synchronized options in hfs_allocate with flags in call to ExtendFileC * 25-Jun-1998 Don Brady Add missing blockNo incrementing to zero fill loop in hfs_truncate. * 22-Jun-1998 Don Brady Add bp = NULL assignment after brelse in hfs_read. * 4-Jun-1998 Pat Dirks Split off from hfs_vnodeops.c */ #include #include #include #include #include #include #include #include //#include #include #include #include #include #include #include #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 /* this needs to go in the mount structure */ }; 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 /***************************************************************************** * * Operations on vnodes * *****************************************************************************/ /* #% read vp L L L # vop_read { IN struct vnode *vp; INOUT struct uio *uio; IN int ioflag; IN struct ucred *cred; */ int hfs_read(ap) struct vop_read_args /* { struct vnode *a_vp; struct uio *a_uio; int a_ioflag; struct ucred *a_cred; } */ *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 /* Can only read files */ 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 they didn't ask for any data, then we are done. */ if (uio->uio_resid == 0) { DBG_VOP_LOCKS_TEST(E_NONE); return (E_NONE); } /* cant read from a negative offset */ 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; /* * We should only get non-zero b_resid when an I/O retval * has occurred, which should cause us to break above. * However, if the short read did not cause an retval, * then we want to ensure that we do not uiomove bad * or uninitialized data. */ ioxfersize -= bp->b_resid; if (ioxfersize < moveSize) { /* XXX PPD This should take the offset into account, too! */ 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); /* Start of loop resets bp to NULL before reaching outside this block... */ } 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); } /* * Write data to a file or directory. #% write vp L L L # vop_write { IN struct vnode *vp; INOUT struct uio *uio; IN int ioflag; IN struct ucred *cred; */ int hfs_write(ap) struct vop_write_args /* { struct vnode *a_vp; struct uio *a_uio; int a_ioflag; struct ucred *a_cred; } */ *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) { /* Can only write files */ 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; /* * Maybe this should be above the vnode op call, but so long as * file servers have no limits, I don't think it matters. */ 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)); /* NOTE: In the following loop there are two positions tracked: currOffset is the current I/O starting offset. currOffset is never >LEOF; the LEOF is nudged along with currOffset as data is zeroed or written. uio->uio_offset is the start of the current I/O operation. It may be arbitrarily beyond currOffset. The following is true at all times: currOffset <= LEOF <= uio->uio_offset <= writelimit */ 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; /* Now test if we need to extend the file */ /* Doing so will adjust the fcbPLen for us */ 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)); /* lock extents b-tree (also protects volume bitmap) */ 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; /* * if the write starts beyond the current EOF then * we we'll zero fill from the current EOF to where the write begins */ 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); /* XXX check errors */ } 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)); /* Make any adjustments for boundary conditions */ if (currOffset + (off_t)xfersize > writelimit) { xfersize = writelimit - currOffset; DBG_RW(("\ttrimming xfersize to 0x%lX to match writelimit (uio_resid)...\n", xfersize)); }; /* * There is no need to read into bp if: * We start on a block boundary and will overwrite the whole block * * OR */ 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; /* XXX */ 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; /* XXX */ break; } } else { /* * This I/O transfer is not sufficiently aligned, so read the affected block into a buffer: */ 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; } } } /* See if we are starting to write within file boundaries: If not, then we need to present a "hole" for the area between the current EOF and the start of the current I/O operation: Note that currOffset is only less than uio_offset if uio_offset > LEOF... */ 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); //DBG_RW(("\tissuing bwrite\n")); } else if ((xfersize + blkoffset) == fragSize) { //DBG_RW(("\tissuing bawrite\n")); bp->b_flags |= B_AGE; bawrite(bp); } else { //DBG_RW(("\tissuing bdwrite\n")); bdwrite(bp); }; /* Update the EOF if we just extended the file (the PEOF has already been moved out and the block mapping table has been updated): */ 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); /* XXX check errors */ }; if (retval || (resid == 0)) break; hp->h_nodeflags |= IN_CHANGE | IN_UPDATE; }; }; /* * If we successfully wrote any data, and we are not the superuser * we clear the setuid and setgid bits as a precaution against * tampering. */ 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); } /* #% ioctl vp U U U # vop_ioctl { IN struct vnode *vp; IN u_long command; IN caddr_t data; IN int fflag; IN struct ucred *cred; IN struct proc *p; */ /* ARGSUSED */ int hfs_ioctl(ap) struct vop_ioctl_args /* { struct vnode *a_vp; int a_command; caddr_t a_data; int a_fflag; struct ucred *a_cred; struct proc *a_p; } */ *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: /* F_READBOOTBLOCKS */ case 3: /* F_WRITEBOOTBLOCKS */ { 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; /* F_WRITEBOOTSTRAP / F_READBOOTSTRAP */ 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; } /* ARGSUSED */ int hfs_select(ap) struct vop_select_args /* { struct vnode *a_vp; int a_which; int a_fflags; struct ucred *a_cred; struct proc *a_p; } */ *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); /* * We should really check to see if I/O is possible. */ DBG_VOP_LOCKS_TEST(1); return (1); } /* * Mmap a file * * NB Currently unsupported. # XXX - not used # vop_mmap { IN struct vnode *vp; IN int fflags; IN struct ucred *cred; IN struct proc *p; */ /* ARGSUSED */ int hfs_mmap(ap) struct vop_mmap_args /* { struct vnode *a_vp; int a_fflags; struct ucred *a_cred; struct proc *a_p; } */ *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); } /* * Seek on a file * * Nothing to do, so just return. # XXX - not used # Needs work: Is newoff right? What's it mean? # vop_seek { IN struct vnode *vp; IN off_t oldoff; IN off_t newoff; IN struct ucred *cred; */ /* ARGSUSED */ int hfs_seek(ap) struct vop_seek_args /* { struct vnode *a_vp; off_t a_oldoff; off_t a_newoff; struct ucred *a_cred; } */ *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); } /* * Bmap converts a the logical block number of a file to its physical block * number on the disk. */ /* * vp - address of vnode file the file * bn - which logical block to convert to a physical block number. * vpp - returns the vnode for the block special file holding the filesystem * containing the file of interest * bnp - address of where to return the filesystem physical block number #% bmap vp L L L #% bmap vpp - U - # vop_bmap { IN struct vnode *vp; IN daddr_t bn; OUT struct vnode **vpp; IN daddr_t *bnp; OUT int *runp; */ /* * Converts a logical block number to a physical block, and optionally returns * the amount of remaining blocks in a run. The logical block is based on hfsNode.logBlockSize. * The physical block number is based on the device block size, currently its 512. * The block run is returned in logical blocks, and is the REMAINING amount of blocks */ int hfs_bmap(ap) struct vop_bmap_args /* { struct vnode *a_vp; daddr_t a_bn; struct vnode **a_vpp; daddr_t *a_bnp; int *a_runp; } */ *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)); /* * Check for underlying vnode requests and ensure that logical * to physical mapping is requested. */ 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) { /* Figure out how many read ahead blocks there are */ if (ap->a_runp != NULL) { if (can_cluster(logBlockSize)) { /* Make sure this result never goes negative: */ *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); /* At least *ap->a_runp blocks left and ... */ if (can_cluster(logBlockSize)) { DBG_ASSERT(bytesContAvail - (*ap->a_runp * logBlockSize) < (2*logBlockSize)); /* ... at most 1 logical block accounted for by current block */ /* ... plus some sub-logical block sized piece */ }; }; return (retval); } /* blktooff converts logical block number to file offset */ int hfs_blktooff(ap) struct vop_blktooff_args /* { struct vnode *a_vp; daddr_t a_lblkno; off_t *a_offset; } */ *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 /* { struct vnode *a_vp; off_t a_offset; daddr_t *a_lblkno; } */ *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 /* { struct vnode *a_vp; off_t a_foffset; size_t a_size; daddr_t *a_bpn; size_t *a_run; void *a_poff; } */ *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)); /* * Check for underlying vnode requests and ensure that logical * to physical mapping is requested. */ 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); } /* * Calculate the logical to physical mapping if not done already, * then call the device strategy routine. # #vop_strategy { # IN struct buf *bp; */ int hfs_strategy(ap) struct vop_strategy_args /* { struct buf *a_bp; } */ *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"); // DBG_VOP_PRINT_FUNCNAME();DBG_VOP_CONT(("\n")); 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) { /* * if we have a page list associated with this bp, * then go through cluste_bp since it knows how to * deal with a page request that might span non-contiguous * physical blocks on the disk... */ retval = cluster_bp(bp); vp = hp->h_meta->h_devvp; bp->b_dev = vp->v_rdev; return (retval); } /* * If we don't already know the filesystem relative block number * then get it using VOP_BMAP(). If VOP_BMAP() returns the block * number as -1 then we've got a hole in the file. HFS filesystems * don't allow files with holes, so we shouldn't ever see this. */ 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) { /* Record the exact size of the I/O transfer about to be made: */ 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); } /* #% reallocblks vp L L L # vop_reallocblks { IN struct vnode *vp; IN struct cluster_save *buflist; */ int hfs_reallocblks(ap) struct vop_reallocblks_args /* { struct vnode *a_vp; struct cluster_save *a_buflist; } */ *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); /* Currently no support for clustering */ /* XXX */ DBG_VOP_LOCKS_TEST(ENOSPC); return (ENOSPC); } /* # #% truncate vp L L L # vop_truncate { IN struct vnode *vp; IN off_t length; IN int flags; (IO_SYNC) IN struct ucred *cred; IN struct proc *p; }; * Truncate the hfsnode hp to at most length size, freeing (or adding) the * disk blocks. */ int hfs_truncate(ap) struct vop_truncate_args /* { struct vnode *a_vp; off_t a_length; int a_flags; struct ucred *a_cred; struct proc *a_p; } */ *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); /* hfs doesn't support truncating of directories */ } tv = time; retval = E_NONE; DBG_RW(("%s: truncate from Ox%lX to Ox%X bytes\n", funcname, fcb->fcbPLen, length)); /* * we cannot just check if fcb->fcbEOF == length (as an optimization) * since there may be extra physical blocks that also need truncation */ if (fcb->fcbEOF > fcb->fcbMaxEOF) fcb->fcbMaxEOF = fcb->fcbEOF; /* * Lengthen the size of the file. We must ensure that the * last byte of the file is allocated. Since the smallest * value of fcbEOF is 0, length will be at least 1. */ if (length > fcb->fcbEOF) { off_t filePosition; daddr_t logBlockNo; long logBlockSize; long blkOffset; off_t bytestoclear; int blockZeroCount; struct buf *bp=NULL; /* * If we don't have enough physical space then * we need to extend the physical size. */ if (length > fcb->fcbPLen) { /* lock extents b-tree (also protects volume bitmap) */ 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, /* allocate all requested bytes or none */ &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 { /* * zero out any new logical space... */ 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); /* wait after we issue 32 requests */ 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); /* XXX check errors */ } else { /* Shorten the size of the file */ if (fcb->fcbEOF > length) { /* * Any buffers that are past the truncation point need to be * invalidated (to maintain buffer cache consistency). For * simplicity, we invalidate all the buffers by calling vinvalbuf. */ if (UBCISVALID(vp)) ubc_setsize(vp, (off_t)length); /* XXX check errors */ vflags = ((length > 0) ? V_SAVE : 0) | V_SAVEMETA; retval = vinvalbuf(vp, vflags, ap->a_cred, ap->a_p, 0, 0); } /* * For a TBE process the deallocation of the file blocks is * delayed until the file is closed. And hfs_close calls * truncate with the IO_NDELAY flag set. So when IO_NDELAY * isn't set, we make sure this isn't a TBE process. */ if ((ap->a_flags & IO_NDELAY) || (!ISSET(ap->a_p->p_flag, P_TBE))) { /* lock extents b-tree (also protects volume bitmap) */ 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); } /* # #% allocate vp L L L # vop_allocate { IN struct vnode *vp; IN off_t length; IN int flags; IN struct ucred *cred; IN struct proc *p; }; * allocate the hfsnode hp to at most length size */ int hfs_allocate(ap) struct vop_allocate_args /* { struct vnode *a_vp; off_t a_length; u_int32_t a_flags; off_t *a_bytesallocated; struct ucred *a_cred; struct proc *a_p; } */ *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; /* For call to ExtendFileC */ 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); /* Set the number of bytes allocated to 0 so that the caller will know that we did nothing. ExtendFileC will fill this in for us if we actually allocate space */ *(ap->a_bytesallocated) = 0; /* Now for some error checking */ 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); /* hfs doesn't support truncating of directories */ } /* Fill in the flags word for the call to Extend the file */ 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 no changes are necesary, then we're done */ if (fcb->fcbPLen == length) goto Std_Exit; /* * Lengthen the size of the file. We must ensure that the * last byte of the file is allocated. Since the smallest * value of fcbPLen is 0, length will be at least 1. */ if (length > fcb->fcbPLen) { moreBytesRequested = length - fcb->fcbPLen; /* lock extents b-tree (also protects volume bitmap) */ 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 we get an error and no changes were made then exit * otherwise we must do the VOP_UPDATE to reflect the changes */ if (retval && (startingPEOF == fcb->fcbPLen)) goto Err_Exit; /* * Adjust actualBytesAdded to be allocation block aligned, not * clump size aligned. * NOTE: So what we are reporting does not affect reality * until the file is closed, when we truncate the file to allocation * block size. */ 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 { /* Shorten the size of the file */ if (fcb->fcbEOF > length) { /* * Any buffers that are past the truncation point need to be * invalidated (to maintain buffer cache consistency). For * simplicity, we invalidate all the buffers by calling vinvalbuf. */ vflags = ((length > 0) ? V_SAVE : 0) | V_SAVEMETA; (void) vinvalbuf(vp, vflags, ap->a_cred, ap->a_p, 0, 0); } /* lock extents b-tree (also protects volume bitmap) */ 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 we get an error and no changes were made then exit * otherwise we must do the VOP_UPDATE to reflect the changes */ if (retval && (startingPEOF == fcb->fcbPLen)) goto Err_Exit; if (fcb->fcbFlags & fcbModifiedMask) hp->h_nodeflags |= IN_MODIFIED; DBG_ASSERT(length <= fcb->fcbPLen) // DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG if (fcb->fcbEOF > fcb->fcbPLen) { fcb->fcbEOF = fcb->fcbPLen; fcb->fcbMaxEOF = fcb->fcbPLen; if (UBCISVALID(vp)) ubc_setsize(vp, (off_t)fcb->fcbEOF); /* XXX check errors */ } } 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); } /* pagein for HFS filesystem, similar to hfs_read(), but without cluster_read() */ int hfs_pagein(ap) struct vop_pagein_args /* { struct vnode *a_vp, upl_t a_pl, vm_offset_t a_pl_offset, off_t a_f_offset, size_t a_size, struct ucred *a_cred, int a_flags } */ *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); } /* * pageout for HFS filesystem. */ int hfs_pageout(ap) struct vop_pageout_args /* { struct vnode *a_vp, upl_t a_pl, vm_offset_t a_pl_offset, off_t a_f_offset, size_t a_size, struct ucred *a_cred, int a_flags } */ *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 we successfully wrote any data, and we are not the superuser * we clear the setuid and setgid bits as a precaution against * tampering. */ 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); } /* * Intercept B-Tree node writes to unswap them if necessary. # #vop_bwrite { # IN struct buf *bp; */ int hfs_bwrite(ap) struct vop_bwrite_args /* { struct buf *a_bp; } */ *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 /* Trap B-Tree writes */ if ((H_FILEID(VTOH(vp)) == kHFSExtentsFileID) || (H_FILEID(VTOH(vp)) == kHFSCatalogFileID)) { /* Swap if the B-Tree node is in native byte order */ if (((UInt16 *)((char *)bp->b_data + bp->b_bcount - 2))[0] == 0x000e) { /* Prepare the block pointer */ 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; /* not found in cache ==> came from disk */ block.blockSize = bp->b_bcount; /* Endian un-swap B-Tree node */ SWAP_BT_NODE (&block, ISHFSPLUS (VTOVCB(vp)), H_FILEID(VTOH(vp)), 1); } /* We don't check to make sure that it's 0x0e00 because it could be all zeros */ } #endif retval = vn_bwrite (ap); return (retval); }