|
|
1.1 ! root 1: /* ! 2: * Copyright (c) 1999 Apple Computer, Inc. All rights reserved. ! 3: * ! 4: * @APPLE_LICENSE_HEADER_START@ ! 5: * ! 6: * Portions Copyright (c) 1999 Apple Computer, Inc. All Rights ! 7: * Reserved. This file contains Original Code and/or Modifications of ! 8: * Original Code as defined in and that are subject to the Apple Public ! 9: * Source License Version 1.1 (the "License"). You may not use this file ! 10: * except in compliance with the License. Please obtain a copy of the ! 11: * License at http://www.apple.com/publicsource and read it before using ! 12: * this file. ! 13: * ! 14: * The Original Code and all software distributed under the License are ! 15: * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER ! 16: * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, ! 17: * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, ! 18: * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the ! 19: * License for the specific language governing rights and limitations ! 20: * under the License. ! 21: * ! 22: * @APPLE_LICENSE_HEADER_END@ ! 23: */ ! 24: /*- ! 25: * Copyright (c) 1993 ! 26: * The Regents of the University of California. All rights reserved. ! 27: * ! 28: * Redistribution and use in source and binary forms, with or without ! 29: * modification, are permitted provided that the following conditions ! 30: * are met: ! 31: * 1. Redistributions of source code must retain the above copyright ! 32: * notice, this list of conditions and the following disclaimer. ! 33: * 2. Redistributions in binary form must reproduce the above copyright ! 34: * notice, this list of conditions and the following disclaimer in the ! 35: * documentation and/or other materials provided with the distribution. ! 36: * 3. All advertising materials mentioning features or use of this software ! 37: * must display the following acknowledgement: ! 38: * This product includes software developed by the University of ! 39: * California, Berkeley and its contributors. ! 40: * 4. Neither the name of the University nor the names of its contributors ! 41: * may be used to endorse or promote products derived from this software ! 42: * without specific prior written permission. ! 43: * ! 44: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ! 45: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ! 46: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ! 47: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE ! 48: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ! 49: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS ! 50: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ! 51: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ! 52: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ! 53: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ! 54: * SUCH DAMAGE. ! 55: * ! 56: * @(#)hfs_readwrite.c 1.0 ! 57: * derived from @(#)ufs_readwrite.c 8.11 (Berkeley) 5/8/95 ! 58: * ! 59: * (c) 1998 Apple Computer, Inc. All Rights Reserved ! 60: * (c) 1990, 1992 NeXT Computer, Inc. All Rights Reserved ! 61: * ! 62: * ! 63: * hfs_readwrite.c -- vnode operations to deal with reading and writing files. ! 64: * ! 65: * MODIFICATION HISTORY: ! 66: * 3-Feb-1999 Pat Dirks Merged in Joe's change to hfs_truncate to skip vinvalbuf if LEOF isn't changing (#2302796) ! 67: * Removed superfluous (and potentially dangerous) second call to vinvalbuf() in hfs_truncate. ! 68: * 2-Dec-1998 Pat Dirks Added support for read/write bootstrap ioctls. ! 69: * 10-Nov-1998 Pat Dirks Changed read/write/truncate logic to optimize block sizes for first extents of a file. ! 70: * Changed hfs_strategy to correct I/O sizes from cluser code I/O requests in light of ! 71: * different block sizing. Changed bexpand to handle RELEASE_BUFFER flag. ! 72: * 22-Sep-1998 Don Brady Changed truncate zero-fill to use bwrite after several bawrites have been queued. ! 73: * 11-Sep-1998 Pat Dirks Fixed buffering logic to not rely on B_CACHE, which is set for empty buffers that ! 74: * have been pre-read by cluster_read (use b_validend > 0 instead). ! 75: * 27-Aug-1998 Pat Dirks Changed hfs_truncate to use cluster_write in place of bawrite where possible. ! 76: * 25-Aug-1998 Pat Dirks Changed hfs_write to do small device-block aligned writes into buffers without doing ! 77: * read-ahead of the buffer. Added bexpand to deal with incomplete [dirty] buffers. ! 78: * Fixed can_cluster macro to use MAXPHYSIO instead of MAXBSIZE. ! 79: * 19-Aug-1998 Don Brady Remove optimization in hfs_truncate that prevented extra physical blocks from ! 80: * being truncated (radar #2265750). Also set fcb->fcbEOF before calling vinvalbuf. ! 81: * 7-Jul-1998 Pat Dirks Added code to honor IO_NOZEROFILL in hfs_truncate. ! 82: * 16-Jul-1998 Don Brady In hfs_bmap use MAXPHYSIO instead of MAXBSIZE when calling MapFileBlockC (radar #2263753). ! 83: * 16-Jul-1998 Don Brady Fix error handling in hfs_allocate (radar #2252265). ! 84: * 04-Jul-1998 chw Synchronized options in hfs_allocate with flags in call to ExtendFileC ! 85: * 25-Jun-1998 Don Brady Add missing blockNo incrementing to zero fill loop in hfs_truncate. ! 86: * 22-Jun-1998 Don Brady Add bp = NULL assignment after brelse in hfs_read. ! 87: * 4-Jun-1998 Pat Dirks Split off from hfs_vnodeops.c ! 88: */ ! 89: ! 90: #include <sys/param.h> ! 91: #include <sys/systm.h> ! 92: #include <sys/resourcevar.h> ! 93: #include <sys/kernel.h> ! 94: #include <sys/file.h> ! 95: #include <sys/stat.h> ! 96: #include <sys/buf.h> ! 97: #include <sys/proc.h> ! 98: #include <sys/conf.h> ! 99: #include <sys/mount.h> ! 100: #include <mach/machine/vm_types.h> ! 101: #include <sys/vnode.h> ! 102: //#include <sys/vnode_if.h> ! 103: #include <sys/malloc.h> ! 104: #include <sys/namei.h> ! 105: #include <sys/signalvar.h> ! 106: #include <sys/uio.h> ! 107: #include <sys/time.h> ! 108: #include <sys/attr.h> ! 109: #include <miscfs/specfs/specdev.h> ! 110: ! 111: ! 112: #include <sys/vm.h> ! 113: #include <libkern/libkern.h> ! 114: #include <mach/machine/simple_lock.h> ! 115: #include <machine/spl.h> ! 116: #include <mach/features.h> ! 117: #if MACH_NBC ! 118: #include <kern/mapfs.h> ! 119: #endif /* MACH_NBC */ ! 120: ! 121: #include "hfs.h" ! 122: #include "hfs_dbg.h" ! 123: #include "hfscommon/headers/FileMgrInternal.h" ! 124: #include "hfscommon/headers/CatalogPrivate.h" ! 125: ! 126: #define can_cluster(size) ((((size & (4096-1))) == 0) && (size <= (MAXPHYSIO/2))) ! 127: ! 128: enum { ! 129: MAXHFSFILESIZE = 0x7FFFFFFF /* this needs to go in the mount structure */ ! 130: }; ! 131: ! 132: extern void vnode_pager_setsize( struct vnode *vp, u_long nsize); ! 133: extern int vnode_uncache( struct vnode *vp); ! 134: ! 135: /* ! 136: * Enabling cluster read/write operations. ! 137: */ ! 138: extern int doclusterread; ! 139: extern int doclusterwrite; ! 140: ! 141: #if DBG_VOP_TEST_LOCKS ! 142: extern void DbgVopTest(int maxSlots, int retval, VopDbgStoreRec *VopDbgStore, char *funcname); ! 143: #endif ! 144: ! 145: int bexpand(struct buf *bp, int newsize, struct buf **nbpp, long flags); ! 146: ! 147: #if DIAGNOSTIC ! 148: void debug_check_blocksizes(struct vnode *vp); ! 149: #endif ! 150: ! 151: /***************************************************************************** ! 152: * ! 153: * Operations on vnodes ! 154: * ! 155: *****************************************************************************/ ! 156: ! 157: /* ! 158: #% read vp L L L ! 159: # ! 160: vop_read { ! 161: IN struct vnode *vp; ! 162: INOUT struct uio *uio; ! 163: IN int ioflag; ! 164: IN struct ucred *cred; ! 165: ! 166: */ ! 167: ! 168: int ! 169: hfs_read(ap) ! 170: struct vop_read_args /* { ! 171: struct vnode *a_vp; ! 172: struct uio *a_uio; ! 173: int a_ioflag; ! 174: struct ucred *a_cred; ! 175: } */ *ap; ! 176: { ! 177: register struct vnode *vp; ! 178: struct hfsnode *hp; ! 179: register struct uio *uio; ! 180: struct buf *bp; ! 181: daddr_t logBlockNo; ! 182: u_long fragSize, moveSize, startOffset, ioxfersize; ! 183: long devBlockSize = 0; ! 184: off_t bytesRemaining; ! 185: int retval; ! 186: u_short mode; ! 187: FCB *fcb; ! 188: Boolean firstpass; /* Used for cluster reading */ ! 189: int seq; /* Also used for cluster reading */ ! 190: ! 191: DBG_FUNC_NAME("hfs_read"); ! 192: DBG_VOP_LOCKS_DECL(1); ! 193: DBG_VOP_PRINT_FUNCNAME(); ! 194: DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP_CONT(("\n")); ! 195: DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_POS); ! 196: ! 197: vp = ap->a_vp; ! 198: hp = VTOH(vp); ! 199: fcb = HTOFCB(hp); ! 200: mode = hp->h_meta->h_mode; ! 201: uio = ap->a_uio; ! 202: ! 203: #if DIAGNOSTIC ! 204: if (uio->uio_rw != UIO_READ) ! 205: panic("%s: mode", funcname); ! 206: #endif ! 207: ! 208: /* Can only read files */ ! 209: if (ap->a_vp->v_type != VREG && ap->a_vp->v_type != VLNK) { ! 210: DBG_VOP_LOCKS_TEST(EISDIR); ! 211: return (EISDIR); ! 212: } ! 213: DBG_VOP(("\tfile size Ox%lX\n", (UInt32)fcb->fcbEOF)); ! 214: DBG_VOP(("\tstarting at offset Ox%X of file, length Ox%X\n", (u_int)uio->uio_offset, (u_int)uio->uio_resid)); ! 215: ! 216: #if DIAGNOSTIC ! 217: debug_check_blocksizes(vp); ! 218: #endif ! 219: ! 220: /* ! 221: * If they didn't ask for any data, then we are done. ! 222: */ ! 223: if (uio->uio_resid == 0) { ! 224: DBG_VOP_LOCKS_TEST(E_NONE); ! 225: return (E_NONE); ! 226: } ! 227: ! 228: /* cant read from a negative offset */ ! 229: if (uio->uio_offset < 0) { ! 230: DBG_VOP_LOCKS_TEST(EINVAL); ! 231: return (EINVAL); ! 232: } ! 233: ! 234: if ((u_int64_t)uio->uio_offset > (u_int64_t)fcb->fcbEOF) { ! 235: if ((u_int64_t)uio->uio_offset > (u_int64_t)MAXHFSFILESIZE) ! 236: retval = EFBIG; ! 237: else ! 238: retval = E_NONE; ! 239: ! 240: DBG_VOP_LOCKS_TEST(retval); ! 241: return (retval); ! 242: } ! 243: ! 244: VOP_DEVBLOCKSIZE(hp->h_devvp, &devBlockSize); ! 245: ! 246: for (retval = 0, bp = NULL, firstpass = TRUE; uio->uio_resid > 0; bp = NULL) { ! 247: ! 248: if ((bytesRemaining = (fcb->fcbEOF - uio->uio_offset)) <= 0) ! 249: break; ! 250: ! 251: MapFileOffset(hp, uio->uio_offset, &logBlockNo, &fragSize, &startOffset); ! 252: ! 253: DBG_VOP(("\tat logBlockNo Ox%X, with Ox%lX left to read\n", logBlockNo, (UInt32)uio->uio_resid)); ! 254: moveSize = ioxfersize = fragSize; ! 255: DBG_VOP(("\tmoveSize = Ox%lX; ioxfersize = Ox%lX; startOffset = Ox%lX.\n", ! 256: moveSize, ioxfersize, startOffset)); ! 257: DBG_ASSERT(moveSize >= startOffset); ! 258: moveSize -= startOffset; ! 259: if (bytesRemaining < moveSize) moveSize = bytesRemaining; ! 260: ! 261: if (uio->uio_resid < moveSize) { ! 262: moveSize = uio->uio_resid; ! 263: DBG_VOP(("\treducing moveSize to Ox%lX (uio->uio_resid).\n", moveSize)); ! 264: }; ! 265: ! 266: if (moveSize == 0) { ! 267: break; ! 268: }; ! 269: DBG_VOP(("\tat logBlockNo Ox%X, extent of Ox%lX, xfer of Ox%lX; moveSize = Ox%lX\n", logBlockNo, fragSize, ioxfersize, moveSize)); ! 270: if (( uio->uio_offset + fragSize) >= fcb->fcbEOF) { ! 271: retval = bread(vp, logBlockNo, ioxfersize, NOCRED, &bp); ! 272: } else if (doclusterread && !(vp->v_flag & VRAOFF) && can_cluster(fragSize)) { ! 273: retval = cluster_read(vp, fcb->fcbEOF, logBlockNo, ioxfersize, NOCRED, &bp, ! 274: devBlockSize, firstpass, (uio->uio_resid + startOffset), &seq); ! 275: } else if (logBlockNo - 1 == vp->v_lastr && !(vp->v_flag & VRAOFF)) { ! 276: daddr_t nextLogBlockNo = logBlockNo + 1; ! 277: long nextsize; ! 278: int nextsize_for_bsd; ! 279: long nextblkoffset; ! 280: ! 281: MapFileOffset(hp, uio->uio_offset + fragSize, &nextLogBlockNo, &nextsize, &nextblkoffset); ! 282: /* Yuck! This copy exists just because I refuse to write the interface to MapFileOffset relying on 'int' to be 'long'... */ ! 283: nextsize_for_bsd = nextsize; ! 284: retval = breadn(vp, logBlockNo, ioxfersize, &nextLogBlockNo, &nextsize_for_bsd, 1, NOCRED, &bp); ! 285: } else { ! 286: retval = bread(vp, logBlockNo, ioxfersize, NOCRED, &bp); ! 287: }; ! 288: ! 289: if (retval != E_NONE) { ! 290: if (bp) { ! 291: brelse(bp); ! 292: bp = NULL; ! 293: } ! 294: break; ! 295: }; ! 296: ! 297: firstpass = FALSE; ! 298: ! 299: if (bp->b_validend > 0) { ! 300: /* ! 301: b_validoff, b_validend, b_dirtyoff, and b_dirtyend are valid for blocks in the cache: ! 302: The only blocks that are incomplete (b_validend < fragSize) are blocks that have been ! 303: partially written from the start of the buffer: ! 304: */ ! 305: if (bp->b_validend < bp->b_bcount) { ! 306: DBG_ASSERT((bp->b_dirtyoff == 0) && (bp->b_validoff == 0) && (bp->b_dirtyend <= bp->b_validend)); ! 307: /* Incomplete blocks must have only device-block multiples of data... */ ! 308: DBG_ASSERT((bp->b_validend % devBlockSize) == 0); ! 309: bp->b_bcount = bp->b_validend; ! 310: ! 311: if (bp->b_validend < (startOffset + moveSize)) { /* buffer doesn't hold enough valid data for this read request */ ! 312: /* We stumbled onto an incomplete [but successfully acquired] buffer: ! 313: try to get the full contents now because it's the only way to get ! 314: the data in this logical block... ! 315: */ ! 316: retval = bexpand(bp, ioxfersize, &bp, 0); ! 317: }; ! 318: }; ! 319: } else { ! 320: /* This block was newly allocated; b_validoff, b_validend, b_dirtyoff, and b_dirtyend are ! 321: all set to zero, which is fine except for b_validend: */ ! 322: DBG_ASSERT(bp->b_validoff == 0); ! 323: bp->b_validend = bp->b_bcount; ! 324: DBG_ASSERT(bp->b_dirtyoff == 0); ! 325: DBG_ASSERT(bp->b_dirtyend == 0); ! 326: }; ! 327: ! 328: vp->v_lastr = logBlockNo; ! 329: ! 330: /* ! 331: * We should only get non-zero b_resid when an I/O retval ! 332: * has occurred, which should cause us to break above. ! 333: * However, if the short read did not cause an retval, ! 334: * then we want to ensure that we do not uiomove bad ! 335: * or uninitialized data. ! 336: */ ! 337: ioxfersize -= bp->b_resid; ! 338: if (ioxfersize < moveSize) { /* XXX PPD This should take the offset into account, too! */ ! 339: if (ioxfersize == 0) ! 340: break; ! 341: moveSize = ioxfersize; ! 342: } ! 343: ! 344: DBG_VOP(("\tcopying Ox%lX bytes from %p; resid = Ox%lX...\n", moveSize, (char *)bp->b_data + startOffset, bp->b_resid)); ! 345: if ((retval = ! 346: uiomove((caddr_t)bp->b_data + startOffset, (int)moveSize, uio))) ! 347: break; ! 348: ! 349: if (S_ISREG(mode) && ! 350: (((startOffset + moveSize) == fragSize) || (uio->uio_offset == fcb->fcbEOF))) { ! 351: bp->b_flags |= B_AGE; ! 352: }; ! 353: ! 354: DBG_ASSERT(bp->b_bcount == bp->b_validend); ! 355: brelse(bp); ! 356: /* Start of loop resets bp to NULL before reaching outside this block... */ ! 357: } ! 358: ! 359: if (bp != NULL) { ! 360: DBG_ASSERT(bp->b_bcount == bp->b_validend); ! 361: brelse(bp); ! 362: }; ! 363: if (HTOVCB(hp)->vcbSigWord == kHFSPlusSigWord) ! 364: hp->h_meta->h_nodeflags |= IN_ACCESS; ! 365: ! 366: DBG_VOP_LOCKS_TEST(retval); ! 367: ! 368: #if DIAGNOSTIC ! 369: debug_check_blocksizes(vp); ! 370: #endif ! 371: ! 372: return (retval); ! 373: } ! 374: ! 375: /* ! 376: * Write data to a file or directory. ! 377: #% write vp L L L ! 378: # ! 379: vop_write { ! 380: IN struct vnode *vp; ! 381: INOUT struct uio *uio; ! 382: IN int ioflag; ! 383: IN struct ucred *cred; ! 384: ! 385: */ ! 386: int ! 387: hfs_write(ap) ! 388: struct vop_write_args /* { ! 389: struct vnode *a_vp; ! 390: struct uio *a_uio; ! 391: int a_ioflag; ! 392: struct ucred *a_cred; ! 393: } */ *ap; ! 394: { ! 395: struct hfsnode *hp = VTOH(ap->a_vp); ! 396: struct uio *uio = ap->a_uio; ! 397: struct vnode *vp = ap->a_vp ; ! 398: struct vnode *dev; ! 399: struct buf *bp; ! 400: struct proc *p, *cp; ! 401: FCB *fcb = HTOFCB(hp); ! 402: ExtendedVCB *vcb = HTOVCB(hp); ! 403: long devBlockSize = 0; ! 404: daddr_t logBlockNo; ! 405: long fragSize; ! 406: off_t origFileSize, currOffset, writelimit, bytesToAdd; ! 407: u_long blkoffset, resid, xfersize, clearSize; ! 408: UInt32 actualBytesAdded; ! 409: int flags, ioflag; ! 410: int retval; ! 411: DBG_FUNC_NAME("write"); ! 412: DBG_VOP_LOCKS_DECL(1); ! 413: DBG_VOP_PRINT_FUNCNAME(); ! 414: DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP_CONT(("\n")); ! 415: DBG_VOP(("\thfsnode 0x%x (%s)\n", (u_int)hp, H_NAME(hp))); ! 416: DBG_VOP(("\tstarting at offset Ox%lX of file, length Ox%lX\n", (UInt32)uio->uio_offset, (UInt32)uio->uio_resid)); ! 417: ! 418: DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_POS); ! 419: ! 420: dev = hp->h_devvp; ! 421: ! 422: #if DIAGNOSTIC ! 423: debug_check_blocksizes(vp); ! 424: #endif ! 425: ! 426: if (uio->uio_offset < 0) { ! 427: DBG_VOP_LOCKS_TEST(EINVAL); ! 428: return (EINVAL); ! 429: } ! 430: ! 431: if (uio->uio_resid == 0) { ! 432: DBG_VOP_LOCKS_TEST(E_NONE); ! 433: return (E_NONE); ! 434: } ! 435: ! 436: if (ap->a_vp->v_type != VREG && ap->a_vp->v_type != VLNK) { /* Can only write files */ ! 437: DBG_VOP_LOCKS_TEST(EISDIR); ! 438: return (EISDIR); ! 439: }; ! 440: ! 441: (void)vnode_uncache(vp); /* XXX PPD Is this the right place to call this? It'll VOP_UNLOCK the vnode */ ! 442: ! 443: #if DIAGNOSTIC ! 444: if (uio->uio_rw != UIO_WRITE) ! 445: panic("%s: mode", funcname); ! 446: #endif ! 447: ! 448: ioflag = ap->a_ioflag; ! 449: uio = ap->a_uio; ! 450: vp = ap->a_vp; ! 451: ! 452: if (ioflag & IO_APPEND) ! 453: uio->uio_offset = fcb->fcbEOF; ! 454: if ((hp->h_meta->h_pflags & APPEND) && uio->uio_offset != fcb->fcbEOF) ! 455: return (EPERM); ! 456: ! 457: writelimit = uio->uio_offset + uio->uio_resid; ! 458: ! 459: /* ! 460: * Maybe this should be above the vnode op call, but so long as ! 461: * file servers have no limits, I don't think it matters. ! 462: */ ! 463: p = uio->uio_procp; ! 464: if (vp->v_type == VREG && p && ! 465: writelimit > p->p_rlimit[RLIMIT_FSIZE].rlim_cur) { ! 466: psignal(p, SIGXFSZ); ! 467: return (EFBIG); ! 468: }; ! 469: VOP_DEVBLOCKSIZE(hp->h_devvp, &devBlockSize); ! 470: ! 471: resid = uio->uio_resid; ! 472: origFileSize = fcb->fcbPLen; ! 473: flags = ioflag & IO_SYNC ? B_SYNC : 0; ! 474: ! 475: DBG_VOP(("\tLEOF is 0x%lX, PEOF is 0x%lX.\n", fcb->fcbEOF, fcb->fcbPLen)); ! 476: ! 477: /* ! 478: NOTE: In the following loop there are two positions tracked: ! 479: currOffset is the current I/O starting offset. currOffset is never >LEOF; the ! 480: LEOF is nudged along with currOffset as data is zeroed or written. ! 481: uio->uio_offset is the start of the current I/O operation. It may be arbitrarily ! 482: beyond currOffset. ! 483: ! 484: The following is true at all times: ! 485: ! 486: currOffset <= LEOF <= uio->uio_offset <= writelimit ! 487: */ ! 488: currOffset = MIN(uio->uio_offset, fcb->fcbEOF); ! 489: ! 490: DBG_VOP(("\tstarting I/O loop at 0x%lX.\n", (u_long)currOffset)); ! 491: ! 492: cp = CURRENT_PROC; ! 493: ! 494: for (retval = 0; uio->uio_resid > 0;) { ! 495: /* Now test if we need to extend the file */ ! 496: /* Doing so will adjust the fcbPLen for us */ ! 497: if (writelimit > (off_t)fcb->fcbPLen) { ! 498: /* XXX PPD Allocate only 2GB at a time until ExtendFileC's interface changes... */ ! 499: bytesToAdd = writelimit - (off_t)fcb->fcbPLen; ! 500: if (bytesToAdd > (off_t)0x80000000) bytesToAdd = (off_t)0x80000000; ! 501: DBG_VOP(("\textending file by 0x%lX bytes; 0x%lX blocks free", (unsigned long)bytesToAdd, (unsigned long)vcb->vcbFreeBks)); ! 502: /* lock extents b-tree (also protects volume bitmap) */ ! 503: retval = hfs_metafilelocking(HTOHFS(hp), kHFSExtentsFileID, LK_EXCLUSIVE, cp); ! 504: if (retval != E_NONE) ! 505: break; ! 506: ! 507: retval = MacToVFSError( ! 508: ExtendFileC (vcb, ! 509: fcb, ! 510: (unsigned long)bytesToAdd, ! 511: kEFContigBit, ! 512: &actualBytesAdded)); ! 513: ! 514: (void) hfs_metafilelocking(HTOHFS(hp), kHFSExtentsFileID, LK_RELEASE, cp); ! 515: DBG_VOP_CONT(("\tactual bytes added = 0x%lX bytes, retval = %d...\n", actualBytesAdded, retval)); ! 516: if ((actualBytesAdded == 0) && (retval == 0)) retval = ENOSPC; ! 517: if (retval != E_NONE) ! 518: break; ! 519: ! 520: UpdateBlockMappingTable(hp); ! 521: ! 522: /* We successfully extended the file: take a fresh look to see how things have changed... */ ! 523: continue; ! 524: }; ! 525: ! 526: /* What block are we starting the write */ ! 527: MapFileOffset(hp, currOffset, &logBlockNo, &fragSize, &blkoffset); ! 528: ! 529: xfersize = fragSize - blkoffset; ! 530: ! 531: DBG_VOP(("\tcurrOffset = Ox%lX, logBlockNo = Ox%X, blkoffset = Ox%lX, xfersize = Ox%lX, fragSize = Ox%lX.\n", ! 532: (unsigned long)currOffset, logBlockNo, blkoffset, xfersize, fragSize)); ! 533: ! 534: /* Make any adjustments for boundary conditions */ ! 535: if (currOffset + (off_t)xfersize > writelimit) { ! 536: xfersize = writelimit - currOffset; ! 537: DBG_VOP(("\ttrimming xfersize to 0x%lX to match writelimit (uio_resid)...\n", xfersize)); ! 538: }; ! 539: ! 540: /* ! 541: * There is no need to read into bp if: ! 542: * We start on a block boundary and will overwrite the whole block ! 543: * ! 544: * OR ! 545: * ! 546: * The transfer starts on a device block boundary and is an even ! 547: * multiple of the device block size (in which case we'll write ! 548: * through the data presented) ! 549: * ! 550: */ ! 551: if ((blkoffset == 0) && ((xfersize >= fragSize) || (xfersize % devBlockSize == 0))) { ! 552: DBG_VOP(("\tRequesting %ld-byte block Ox%lX w/o read...\n", fragSize, (long)logBlockNo)); ! 553: bp = getblk(vp, logBlockNo, fragSize, 0, 0); ! 554: retval = 0; ! 555: if (bp->b_blkno == -1) { ! 556: brelse(bp); ! 557: retval = EIO; /* XXX */ ! 558: break; ! 559: } ! 560: ! 561: if (bp->b_flags & (B_DELWRI | B_DONE | B_CACHE)) { ! 562: /* ! 563: This buffer is fully pre-read and may already have some modified data in it: ! 564: it doesn't need special-casing for write-through. ! 565: */ ! 566: } else { ! 567: /* This is an empty buffer just allocated for our use: ! 568: b_validoff, b_validend, b_dirtyoff, and b_dirtyend are all zero, ! 569: which is exactly right (b_bcount is set to fragSize). */ ! 570: ! 571: /* XXX PPD Can't this be skipped if b_blkno != logBlockNo? */ ! 572: ! 573: /* Setting up the physical block number is required ! 574: (1) to keep cluster_write informed about which blocks can be clustered, and ! 575: (2) to avoid a second call to bmap() in strategy() on the write. ! 576: */ ! 577: if ((retval = VOP_BMAP(vp, logBlockNo, NULL, &bp->b_blkno, NULL))) { ! 578: brelse(bp); ! 579: break; ! 580: }; ! 581: ! 582: bp->b_bcount = 0; /* No valid data in block yet */ ! 583: bp->b_validend = 0; /* No valid data in block yet */ ! 584: }; ! 585: } else { ! 586: /* ! 587: * This I/O transfer is not sufficiently aligned, so read the affected block into a buffer: ! 588: */ ! 589: DBG_VOP(("\tRequesting block Ox%X, size = 0x%08lX...\n", logBlockNo, fragSize)); ! 590: retval = bread(vp, logBlockNo, fragSize, ap->a_cred, &bp); ! 591: if (retval != E_NONE) { ! 592: if (bp) brelse(bp); ! 593: break; ! 594: } ! 595: } ! 596: ! 597: /* Fix up the anciliary fields for this buffer, depending on whether they've been initialized yet: */ ! 598: if (bp->b_validend > 0) { ! 599: /* ! 600: b_validoff, b_validend, b_dirtyoff, and b_dirtyend are valid for blocks in the cache: ! 601: The only blocks that are incomplete (b_validend < fragSize) are blocks that have been ! 602: partially written from the start of the buffer: ! 603: */ ! 604: if (bp->b_validend < bp->b_bcount) { ! 605: DBG_ASSERT((bp->b_dirtyoff == 0) && (bp->b_validoff == 0) && (bp->b_dirtyend <= bp->b_validend)); ! 606: /* Incomplete blocks must have only device-block multiples of data... */ ! 607: DBG_ASSERT((bp->b_validend % devBlockSize) == 0); ! 608: bp->b_bcount = bp->b_validend; ! 609: ! 610: if ((bp->b_validend < blkoffset) || /* ... valid data does not overlap (or at least abut) start of new write */ ! 611: (bp->b_bufsize < (blkoffset + xfersize)) || /* ... or isn't enough buffer space to hold entire transfer */ ! 612: ((blkoffset + xfersize) % devBlockSize != 0)) { /* ... or won't leave blocksize-multiple of dirty bytes */ ! 613: /* We stumbled onto an incomplete [but successfully acquired] buffer: ! 614: try to get the full contents now because it's the only way to get ! 615: the data in this logical block... ! 616: */ ! 617: retval = bexpand(bp, fragSize, &bp, 0); ! 618: }; ! 619: }; ! 620: } else { ! 621: /* This buffer was either just allocated or just read in its entirity [see b_bcount]: ! 622: b_validoff, b_validend, b_dirtyoff, and b_dirtyend are all zeroed, which is almost correct. */ ! 623: DBG_ASSERT(bp->b_validoff == 0); ! 624: bp->b_validend = bp->b_bcount; /* b_bcount > 0 iff block was actually read from disk in bread */ ! 625: DBG_ASSERT(bp->b_dirtyoff == 0); ! 626: DBG_ASSERT(bp->b_dirtyend == 0); ! 627: }; ! 628: ! 629: /* See if we are starting to write within file boundaries: ! 630: If not, then we need to present a "hole" for the area between ! 631: the current EOF and the start of the current I/O operation: ! 632: ! 633: Note that currOffset is only less than uio_offset if uio_offset > LEOF... ! 634: */ ! 635: if (uio->uio_offset > currOffset) { ! 636: clearSize = MIN(uio->uio_offset - currOffset, xfersize); ! 637: DBG_VOP(("\tzeroing Ox%lX bytes Ox%lX bytes into block Ox%X...\n", clearSize, blkoffset, logBlockNo)); ! 638: bzero(bp->b_data + blkoffset, clearSize); ! 639: currOffset += clearSize; ! 640: blkoffset += clearSize; ! 641: xfersize -= clearSize; ! 642: }; ! 643: ! 644: if (xfersize > 0) { ! 645: DBG_VOP(("\tCopying Ox%lX bytes Ox%lX bytes into block Ox%X... ioflag == 0x%X\n", ! 646: xfersize, blkoffset, logBlockNo, ioflag)); ! 647: retval = uiomove((caddr_t)bp->b_data + blkoffset, (int)xfersize, uio); ! 648: currOffset += xfersize; ! 649: }; ! 650: ! 651: if (blkoffset + xfersize > bp->b_dirtyend) bp->b_dirtyend = blkoffset + xfersize; /* Newly written data is now [also] dirty */ ! 652: if (bp->b_dirtyend > bp->b_validend) bp->b_validend = bp->b_dirtyend; /* Data just written is now valid, too */ ! 653: bp->b_bcount = bp->b_validend; ! 654: ! 655: DBG_ASSERT((bp->b_bcount % devBlockSize) == 0); ! 656: DBG_ASSERT(bp->b_bcount == bp->b_validend); ! 657: if (ioflag & IO_SYNC) { ! 658: (void)bwrite(bp); ! 659: //DBG_VOP(("\tissuing bwrite\n")); ! 660: } else if ((xfersize + blkoffset) == fragSize) { ! 661: if (doclusterwrite && can_cluster(fragSize)) { ! 662: //DBG_VOP(("\tissuing cluster_write\n")); ! 663: cluster_write(bp, fcb->fcbPLen, devBlockSize); ! 664: } else { ! 665: //DBG_VOP(("\tissuing bawrite\n")); ! 666: bp->b_flags |= B_AGE; ! 667: bawrite(bp); ! 668: } ! 669: } else { ! 670: //DBG_VOP(("\tissuing bdwrite\n")); ! 671: bdwrite(bp); ! 672: }; ! 673: ! 674: /* Update the EOF if we just extended the file ! 675: (the PEOF has already been moved out and the block mapping table has been updated): */ ! 676: if (currOffset > fcb->fcbEOF) { ! 677: DBG_VOP(("\textending EOF to 0x%lX...\n", fcb->fcbEOF)); ! 678: fcb->fcbEOF = currOffset; ! 679: #if MACH_NBC ! 680: if ((vp->v_type == VREG) && (vp->v_vm_info && !(vp->v_vm_info->mapped))) { ! 681: #endif /* MACH_NBC */ ! 682: vnode_pager_setsize(vp, (u_long)fcb->fcbEOF); ! 683: #if MACH_NBC ! 684: } ! 685: #endif /* MACH_NBC */ ! 686: }; ! 687: ! 688: if (retval || (resid == 0)) ! 689: break; ! 690: hp->h_meta->h_nodeflags |= IN_CHANGE | IN_UPDATE; ! 691: }; ! 692: /* ! 693: * If we successfully wrote any data, and we are not the superuser ! 694: * we clear the setuid and setgid bits as a precaution against ! 695: * tampering. ! 696: */ ! 697: if (resid > uio->uio_resid && ap->a_cred && ap->a_cred->cr_uid != 0) ! 698: hp->h_meta->h_mode &= ~(ISUID | ISGID); ! 699: if (retval) { ! 700: if (ioflag & IO_UNIT) { ! 701: (void)VOP_TRUNCATE(vp, origFileSize, ! 702: ioflag & IO_SYNC, ap->a_cred, uio->uio_procp); ! 703: uio->uio_offset -= resid - uio->uio_resid; ! 704: uio->uio_resid = resid; ! 705: } ! 706: } else if (resid > uio->uio_resid && (ioflag & IO_SYNC)) ! 707: retval = VOP_UPDATE(vp, &time, &time, 1); ! 708: ! 709: #if DIAGNOSTIC ! 710: debug_check_blocksizes(vp); ! 711: #endif ! 712: ! 713: DBG_VOP_LOCKS_TEST(retval); ! 714: return (retval); ! 715: } ! 716: ! 717: ! 718: /* ! 719: ! 720: #% ioctl vp U U U ! 721: # ! 722: vop_ioctl { ! 723: IN struct vnode *vp; ! 724: IN u_long command; ! 725: IN caddr_t data; ! 726: IN int fflag; ! 727: IN struct ucred *cred; ! 728: IN struct proc *p; ! 729: ! 730: */ ! 731: ! 732: ! 733: /* ARGSUSED */ ! 734: int ! 735: hfs_ioctl(ap) ! 736: struct vop_ioctl_args /* { ! 737: struct vnode *a_vp; ! 738: int a_command; ! 739: caddr_t a_data; ! 740: int a_fflag; ! 741: struct ucred *a_cred; ! 742: struct proc *a_p; ! 743: } */ *ap; ! 744: { ! 745: DBG_FUNC_NAME("ioctl"); ! 746: DBG_VOP_LOCKS_DECL(1); ! 747: DBG_VOP_PRINT_FUNCNAME(); ! 748: DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP_CONT(("\n")); ! 749: ! 750: DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_UNLOCKED, VOPDBG_UNLOCKED, VOPDBG_UNLOCKED, VOPDBG_POS); ! 751: ! 752: switch (ap->a_command) { ! 753: ! 754: case 1: ! 755: { register struct hfsnode *hp; ! 756: register struct vnode *vp; ! 757: register struct radvisory *ra; ! 758: FCB *fcb; ! 759: int devBlockSize = 0; ! 760: int error; ! 761: long size; ! 762: daddr_t lbn; ! 763: ! 764: vp = ap->a_vp; ! 765: ! 766: VOP_LEASE(vp, ap->a_p, ap->a_cred, LEASE_READ); ! 767: vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, ap->a_p); ! 768: ! 769: ra = (struct radvisory *)(ap->a_data); ! 770: hp = VTOH(vp); ! 771: ! 772: fcb = HTOFCB(hp); ! 773: ! 774: if ((u_int64_t)ra->ra_offset >= (u_int64_t)fcb->fcbEOF) { ! 775: VOP_UNLOCK(vp, 0, ap->a_p); ! 776: DBG_VOP_LOCKS_TEST(EFBIG); ! 777: return (EFBIG); ! 778: } ! 779: VOP_DEVBLOCKSIZE(hp->h_devvp, &devBlockSize); ! 780: size = hp->h_meta->h_logBlockSize; ! 781: ! 782: if ( !(can_cluster(size))) { ! 783: VOP_UNLOCK(vp, 0, ap->a_p); ! 784: DBG_VOP_LOCKS_TEST(EINVAL); ! 785: return (EINVAL); ! 786: } ! 787: size = MIN(size, MAXBSIZE); ! 788: lbn = ra->ra_offset / size; ! 789: ! 790: error = advisory_read(vp, fcb->fcbEOF, lbn, size, size, ra->ra_count, devBlockSize); ! 791: VOP_UNLOCK(vp, 0, ap->a_p); ! 792: ! 793: DBG_VOP_LOCKS_TEST(error); ! 794: return (error); ! 795: } ! 796: ! 797: case 2: /* F_READBOOTBLOCKS */ ! 798: case 3: /* F_WRITEBOOTBLOCKS */ ! 799: { ! 800: struct vnode *vp = ap->a_vp; ! 801: struct hfsnode *hp = VTOH(vp); ! 802: struct fbootstraptransfer *btd = (struct fbootstraptransfer *)ap->a_data; ! 803: u_long devBlockSize; ! 804: int error; ! 805: struct iovec aiov; ! 806: struct uio auio; ! 807: u_long blockNumber; ! 808: u_long blockOffset; ! 809: u_long xfersize; ! 810: struct buf *bp; ! 811: ! 812: if ((vp->v_flag & VROOT) == 0) return EINVAL; ! 813: if (btd->fbt_offset + btd->fbt_length > 1024) return EINVAL; ! 814: ! 815: aiov.iov_base = btd->fbt_buffer; ! 816: aiov.iov_len = btd->fbt_length; ! 817: ! 818: auio.uio_iov = &aiov; ! 819: auio.uio_iovcnt = 1; ! 820: auio.uio_offset = btd->fbt_offset; ! 821: auio.uio_resid = btd->fbt_length; ! 822: auio.uio_segflg = UIO_USERSPACE; ! 823: auio.uio_rw = (ap->a_command == 3) ? UIO_WRITE : UIO_READ; /* F_WRITEBOOTSTRAP / F_READBOOTSTRAP */ ! 824: auio.uio_procp = ap->a_p; ! 825: ! 826: VOP_DEVBLOCKSIZE(hp->h_devvp, &devBlockSize); ! 827: ! 828: while (auio.uio_resid > 0) { ! 829: blockNumber = auio.uio_offset / devBlockSize; ! 830: error = bread(hp->h_devvp, blockNumber, devBlockSize, ap->a_cred, &bp); ! 831: if (error) { ! 832: if (bp) brelse(bp); ! 833: return error; ! 834: }; ! 835: ! 836: blockOffset = auio.uio_offset % devBlockSize; ! 837: xfersize = devBlockSize - blockOffset; ! 838: error = uiomove((caddr_t)bp->b_data + blockOffset, (int)xfersize, &auio); ! 839: if (error) { ! 840: brelse(bp); ! 841: return error; ! 842: }; ! 843: if (auio.uio_rw == UIO_WRITE) { ! 844: error = bwrite(bp); ! 845: if (error) return error; ! 846: } else { ! 847: brelse(bp); ! 848: }; ! 849: }; ! 850: }; ! 851: return 0; ! 852: ! 853: default: ! 854: DBG_VOP_LOCKS_TEST(ENOTTY); ! 855: return (ENOTTY); ! 856: } ! 857: ! 858: return 0; ! 859: } ! 860: ! 861: /* ARGSUSED */ ! 862: int ! 863: hfs_select(ap) ! 864: struct vop_select_args /* { ! 865: struct vnode *a_vp; ! 866: int a_which; ! 867: int a_fflags; ! 868: struct ucred *a_cred; ! 869: struct proc *a_p; ! 870: } */ *ap; ! 871: { ! 872: DBG_FUNC_NAME("select"); ! 873: DBG_VOP_LOCKS_DECL(1); ! 874: DBG_VOP_PRINT_FUNCNAME(); ! 875: DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP_CONT(("\n")); ! 876: ! 877: DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_LOCKED, VOPDBG_IGNORE, VOPDBG_IGNORE, VOPDBG_POS); ! 878: ! 879: /* ! 880: * We should really check to see if I/O is possible. ! 881: */ ! 882: DBG_VOP_LOCKS_TEST(1); ! 883: return (1); ! 884: } ! 885: ! 886: ! 887: ! 888: /* ! 889: * Mmap a file ! 890: * ! 891: * NB Currently unsupported. ! 892: # XXX - not used ! 893: # ! 894: vop_mmap { ! 895: IN struct vnode *vp; ! 896: IN int fflags; ! 897: IN struct ucred *cred; ! 898: IN struct proc *p; ! 899: ! 900: */ ! 901: ! 902: /* ARGSUSED */ ! 903: ! 904: int ! 905: hfs_mmap(ap) ! 906: struct vop_mmap_args /* { ! 907: struct vnode *a_vp; ! 908: int a_fflags; ! 909: struct ucred *a_cred; ! 910: struct proc *a_p; ! 911: } */ *ap; ! 912: { ! 913: DBG_FUNC_NAME("mmap"); ! 914: DBG_VOP_LOCKS_DECL(1); ! 915: DBG_VOP_PRINT_FUNCNAME(); ! 916: DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP_CONT(("\n")); ! 917: ! 918: DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_IGNORE, VOPDBG_IGNORE, VOPDBG_IGNORE, VOPDBG_POS); ! 919: ! 920: DBG_VOP_LOCKS_TEST(EINVAL); ! 921: return (EINVAL); ! 922: } ! 923: ! 924: ! 925: ! 926: /* ! 927: * Seek on a file ! 928: * ! 929: * Nothing to do, so just return. ! 930: # XXX - not used ! 931: # Needs work: Is newoff right? What's it mean? ! 932: # ! 933: vop_seek { ! 934: IN struct vnode *vp; ! 935: IN off_t oldoff; ! 936: IN off_t newoff; ! 937: IN struct ucred *cred; ! 938: */ ! 939: /* ARGSUSED */ ! 940: int ! 941: hfs_seek(ap) ! 942: struct vop_seek_args /* { ! 943: struct vnode *a_vp; ! 944: off_t a_oldoff; ! 945: off_t a_newoff; ! 946: struct ucred *a_cred; ! 947: } */ *ap; ! 948: { ! 949: DBG_FUNC_NAME("seek"); ! 950: DBG_VOP_LOCKS_DECL(1); ! 951: DBG_VOP_PRINT_FUNCNAME(); ! 952: DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP_CONT(("\n")); ! 953: ! 954: DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_IGNORE, VOPDBG_IGNORE, VOPDBG_IGNORE, VOPDBG_POS); ! 955: ! 956: DBG_VOP_LOCKS_TEST(E_NONE); ! 957: return (E_NONE); ! 958: } ! 959: ! 960: ! 961: /* ! 962: * Bmap converts a the logical block number of a file to its physical block ! 963: * number on the disk. ! 964: */ ! 965: ! 966: /* ! 967: * vp - address of vnode file the file ! 968: * bn - which logical block to convert to a physical block number. ! 969: * vpp - returns the vnode for the block special file holding the filesystem ! 970: * containing the file of interest ! 971: * bnp - address of where to return the filesystem physical block number ! 972: #% bmap vp L L L ! 973: #% bmap vpp - U - ! 974: # ! 975: vop_bmap { ! 976: IN struct vnode *vp; ! 977: IN daddr_t bn; ! 978: OUT struct vnode **vpp; ! 979: IN daddr_t *bnp; ! 980: OUT int *runp; ! 981: */ ! 982: /* ! 983: * Converts a logical block number to a physical block, and optionally returns ! 984: * the amount of remaining blocks in a run. The logical block is based on hfsNode.logBlockSize. ! 985: * The physical block number is based on the device block size, currently its 512. ! 986: * The block run is returned in logical blocks, and is the REMAINING amount of blocks ! 987: */ ! 988: ! 989: int ! 990: hfs_bmap(ap) ! 991: struct vop_bmap_args /* { ! 992: struct vnode *a_vp; ! 993: daddr_t a_bn; ! 994: struct vnode **a_vpp; ! 995: daddr_t *a_bnp; ! 996: int *a_runp; ! 997: } */ *ap; ! 998: { ! 999: struct hfsnode *hp = VTOH(ap->a_vp); ! 1000: struct hfsmount *hfsmp = VTOHFS(ap->a_vp); ! 1001: int retval = E_NONE; ! 1002: daddr_t logBlockSize; ! 1003: UInt32 bytesContAvail = 0; ! 1004: struct proc *p = NULL; ! 1005: int lockExtBtree; ! 1006: ! 1007: #define DEBUG_BMAP 0 ! 1008: #if DEBUG_BMAP ! 1009: DBG_FUNC_NAME("bmap"); ! 1010: DBG_VOP_LOCKS_DECL(2); ! 1011: DBG_VOP_PRINT_FUNCNAME(); ! 1012: DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP((": %d --> ", ap->a_bn)); ! 1013: ! 1014: DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_POS); ! 1015: if (ap->a_vpp != NULL) { ! 1016: DBG_VOP_LOCKS_INIT(1,*ap->a_vpp, VOPDBG_IGNORE, VOPDBG_UNLOCKED, VOPDBG_IGNORE, VOPDBG_POS); ! 1017: } else { ! 1018: DBG_VOP_LOCKS_INIT(1,NULL, VOPDBG_IGNORE, VOPDBG_IGNORE, VOPDBG_IGNORE, VOPDBG_POS); ! 1019: }; ! 1020: #endif ! 1021: ! 1022: /* ! 1023: * Check for underlying vnode requests and ensure that logical ! 1024: * to physical mapping is requested. ! 1025: */ ! 1026: if (ap->a_vpp != NULL) ! 1027: *ap->a_vpp = VTOH(ap->a_vp)->h_devvp; ! 1028: if (ap->a_bnp == NULL) ! 1029: return (0); ! 1030: ! 1031: lockExtBtree = hasOverflowExtents(hp); ! 1032: if (lockExtBtree) ! 1033: { ! 1034: p = CURRENT_PROC; ! 1035: retval = hfs_metafilelocking(hfsmp, kHFSExtentsFileID, LK_EXCLUSIVE | LK_CANRECURSE, p); ! 1036: if (retval) ! 1037: return (retval); ! 1038: } ! 1039: ! 1040: ! 1041: if (ap->a_bn < hp->h_uniformblocksizestart) { ! 1042: int targetLogicalBlockNo = ap->a_bn; ! 1043: off_t fileOffset = 0; ! 1044: int extent; ! 1045: long extentSize; ! 1046: ! 1047: logBlockSize = MAXLOGBLOCKSIZE; ! 1048: for (extent = 0; extent < LOGBLOCKMAPENTRIES; ++extent) { ! 1049: if ((hp->h_logicalblocktable[extent].logicalBlockCount > 0) && ! 1050: (targetLogicalBlockNo < hp->h_logicalblocktable[extent].logicalBlockCount)) { ! 1051: retval = MacToVFSError( ! 1052: MapFileBlockC (HFSTOVCB(hfsmp), ! 1053: HTOFCB(hp), ! 1054: MAXPHYSIO, ! 1055: fileOffset + (targetLogicalBlockNo * MAXLOGBLOCKSIZE), ! 1056: (UInt32 *)ap->a_bnp, ! 1057: &bytesContAvail)); ! 1058: /* Take the current FCB's extent length info: we could be mid-update */ ! 1059: if (HTOVCB(hp)->vcbSigWord == kHFSSigWord) { ! 1060: extentSize = hp->h_xfcb->fcb_fcb.fcbExtRec[extent].blockCount * HTOVCB(hp)->blockSize; ! 1061: } else { ! 1062: extentSize = hp->h_xfcb->fcb_extFCB.extents[extent].blockCount * HTOVCB(hp)->blockSize; ! 1063: }; ! 1064: DBG_ASSERT((bytesContAvail == MAXPHYSIO) || (bytesContAvail == (extentSize - (targetLogicalBlockNo * MAXLOGBLOCKSIZE)))); ! 1065: break; ! 1066: }; ! 1067: targetLogicalBlockNo -= hp->h_logicalblocktable[extent].logicalBlockCount; ! 1068: fileOffset += hp->h_logicalblocktable[extent].extentLength; ! 1069: }; ! 1070: } else { ! 1071: logBlockSize = hp->h_meta->h_logBlockSize; ! 1072: retval = MacToVFSError( ! 1073: MapFileBlockC (HFSTOVCB(hfsmp), ! 1074: HTOFCB(hp), ! 1075: MAXPHYSIO, ! 1076: hp->h_optimizedblocksizelimit + ! 1077: ((ap->a_bn - hp->h_uniformblocksizestart) * logBlockSize), ! 1078: (UInt32 *)ap->a_bnp, ! 1079: &bytesContAvail)); ! 1080: ! 1081: }; ! 1082: ! 1083: if (lockExtBtree) (void) hfs_metafilelocking(hfsmp, kHFSExtentsFileID, LK_RELEASE, p); ! 1084: ! 1085: if (retval == E_NONE) { ! 1086: /* Figure out how many read ahead blocks there are */ ! 1087: if (ap->a_runp != NULL) { ! 1088: if (can_cluster(logBlockSize)) { ! 1089: /* Make sure this result never goes negative: */ ! 1090: *ap->a_runp = (bytesContAvail < logBlockSize) ? 0 : (bytesContAvail / logBlockSize) - 1; ! 1091: } else { ! 1092: *ap->a_runp = 0; ! 1093: }; ! 1094: }; ! 1095: }; ! 1096: ! 1097: #if DEBUG_BMAP ! 1098: DBG_VOP(("%d:%d.\n", *ap->a_bnp, (bytesContAvail < logBlockSize) ? 0 : (bytesContAvail / logBlockSize) - 1)); ! 1099: ! 1100: DBG_VOP_LOCKS_TEST(retval); ! 1101: #endif ! 1102: ! 1103: if (ap->a_runp) { ! 1104: DBG_ASSERT((*ap->a_runp * logBlockSize) < bytesContAvail); /* At least *ap->a_runp blocks left and ... */ ! 1105: if (can_cluster(logBlockSize)) { ! 1106: DBG_ASSERT(bytesContAvail - (*ap->a_runp * logBlockSize) < (2*logBlockSize)); /* ... at most 1 logical block accounted for by current block */ ! 1107: /* ... plus some sub-logical block sized piece */ ! 1108: }; ! 1109: }; ! 1110: ! 1111: return (retval); ! 1112: } ! 1113: ! 1114: ! 1115: /* ! 1116: * Calculate the logical to physical mapping if not done already, ! 1117: * then call the device strategy routine. ! 1118: # ! 1119: #vop_strategy { ! 1120: # IN struct buf *bp; ! 1121: */ ! 1122: int ! 1123: hfs_strategy(ap) ! 1124: struct vop_strategy_args /* { ! 1125: struct buf *a_bp; ! 1126: } */ *ap; ! 1127: { ! 1128: register struct buf *bp = ap->a_bp; ! 1129: register struct vnode *vp = bp->b_vp; ! 1130: register struct hfsnode *hp; ! 1131: long logBlockSize; ! 1132: int retval = 0; ! 1133: DBG_FUNC_NAME("strategy"); ! 1134: ! 1135: // DBG_VOP_PRINT_FUNCNAME();DBG_VOP_CONT(("\n")); ! 1136: ! 1137: hp = VTOH(vp); ! 1138: if (vp->v_type == VBLK || vp->v_type == VCHR) ! 1139: panic("hfs_strategy: device vnode passed!"); ! 1140: ! 1141: /* ! 1142: * If we don't already know the filesystem relative block number ! 1143: * then get it using VOP_BMAP(). If VOP_BMAP() returns the block ! 1144: * number as -1 then we've got a hole in the file. HFS filesystems ! 1145: * don't allow files with holes, so we shouldn't ever see this. ! 1146: */ ! 1147: if (bp->b_blkno == bp->b_lblkno) { ! 1148: if ((retval = VOP_BMAP(vp, bp->b_lblkno, NULL, &bp->b_blkno, NULL))) { ! 1149: bp->b_error = retval; ! 1150: bp->b_flags |= B_ERROR; ! 1151: biodone(bp); ! 1152: return (retval); ! 1153: } ! 1154: if ((long)bp->b_blkno == -1) ! 1155: clrbuf(bp); ! 1156: } ! 1157: if ((long)bp->b_blkno == -1) { ! 1158: biodone(bp); ! 1159: return (0); ! 1160: } ! 1161: ! 1162: /* Make sure some over-eager cluster code didn't generate an excessively large read: */ ! 1163: if (bp->b_bcount != hp->h_meta->h_logBlockSize) { ! 1164: logBlockSize = LogicalBlockSize(hp, bp->b_lblkno); ! 1165: if ((bp->b_bcount > logBlockSize) && !(can_cluster(logBlockSize))) bp->b_bcount = logBlockSize; ! 1166: }; ! 1167: ! 1168: if (bp->b_validend == 0) { ! 1169: /* Record the exact size of the I/O transfer about to be made: */ ! 1170: DBG_ASSERT(bp->b_validoff == 0); ! 1171: bp->b_validend = bp->b_bcount; ! 1172: DBG_ASSERT(bp->b_dirtyoff == 0); ! 1173: }; ! 1174: ! 1175: vp = hp->h_devvp; ! 1176: bp->b_dev = vp->v_rdev; ! 1177: DBG_VOP(("\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)); ! 1178: ! 1179: return VOCALL (vp->v_op, VOFFSET(vop_strategy), ap); ! 1180: } ! 1181: ! 1182: ! 1183: /* ! 1184: #% reallocblks vp L L L ! 1185: # ! 1186: vop_reallocblks { ! 1187: IN struct vnode *vp; ! 1188: IN struct cluster_save *buflist; ! 1189: ! 1190: */ ! 1191: ! 1192: int ! 1193: hfs_reallocblks(ap) ! 1194: struct vop_reallocblks_args /* { ! 1195: struct vnode *a_vp; ! 1196: struct cluster_save *a_buflist; ! 1197: } */ *ap; ! 1198: { ! 1199: DBG_FUNC_NAME("reallocblks"); ! 1200: DBG_VOP_LOCKS_DECL(1); ! 1201: DBG_VOP_PRINT_FUNCNAME(); ! 1202: DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP_CONT(("\n")); ! 1203: ! 1204: DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_POS); ! 1205: ! 1206: /* Currently no support for clustering */ /* XXX */ ! 1207: DBG_VOP_LOCKS_TEST(ENOSPC); ! 1208: return (ENOSPC); ! 1209: } ! 1210: ! 1211: ! 1212: ! 1213: /* ! 1214: # ! 1215: #% truncate vp L L L ! 1216: # ! 1217: vop_truncate { ! 1218: IN struct vnode *vp; ! 1219: IN off_t length; ! 1220: IN int flags; (IO_SYNC) ! 1221: IN struct ucred *cred; ! 1222: IN struct proc *p; ! 1223: }; ! 1224: * Truncate the hfsnode hp to at most length size, freeing (or adding) the ! 1225: * disk blocks. ! 1226: */ ! 1227: int hfs_truncate(ap) ! 1228: struct vop_truncate_args /* { ! 1229: struct vnode *a_vp; ! 1230: off_t a_length; ! 1231: int a_flags; ! 1232: struct ucred *a_cred; ! 1233: struct proc *a_p; ! 1234: } */ *ap; ! 1235: { ! 1236: register struct vnode *vp = ap->a_vp; ! 1237: register struct hfsnode *hp = VTOH(vp); ! 1238: off_t length = ap->a_length; ! 1239: long vflags; ! 1240: struct timeval tv; ! 1241: int retval; ! 1242: FCB *fcb; ! 1243: off_t bytesToAdd; ! 1244: UInt32 actualBytesAdded; ! 1245: long devBlockSize = 512; ! 1246: DBG_FUNC_NAME("truncate"); ! 1247: DBG_VOP_LOCKS_DECL(1); ! 1248: DBG_VOP_PRINT_FUNCNAME(); ! 1249: DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP_CONT(("\n")); ! 1250: DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_POS); ! 1251: ! 1252: #if DIAGNOSTIC ! 1253: debug_check_blocksizes(ap->a_vp); ! 1254: #endif ! 1255: ! 1256: if (length < 0) { ! 1257: DBG_VOP_LOCKS_TEST(EINVAL); ! 1258: return (EINVAL); ! 1259: } ! 1260: ! 1261: if (length > (off_t)MAXHFSFILESIZE) { /* XXX need to distinguish between hfs and hfs+ */ ! 1262: DBG_VOP_LOCKS_TEST(EFBIG); ! 1263: return (EFBIG); ! 1264: } ! 1265: ! 1266: if (vp->v_type != VREG && vp->v_type != VLNK) { ! 1267: DBG_VOP_LOCKS_TEST(EISDIR); ! 1268: return (EISDIR); /* hfs doesn't support truncating of directories */ ! 1269: } ! 1270: ! 1271: fcb = HTOFCB(hp); ! 1272: tv = time; ! 1273: retval = E_NONE; ! 1274: ! 1275: DBG_VOP(("%s: truncate from Ox%lX to Ox%X bytes\n", funcname, fcb->fcbPLen, (u_int)length)); ! 1276: ! 1277: /* ! 1278: * we cannot just check if fcb->fcbEOF == length (as an optimization) ! 1279: * since there may be extra physical blocks that also need truncation ! 1280: */ ! 1281: ! 1282: #if MACH_NBC ! 1283: retval = mapfs_trunc(vp, (vm_offset_t)length); ! 1284: if (retval) { ! 1285: DBG_VOP_LOCKS_TEST(retval); ! 1286: return (retval); ! 1287: } ! 1288: #endif /* MACH_NBC */ ! 1289: ! 1290: /* ! 1291: * Lengthen the size of the file. We must ensure that the ! 1292: * last byte of the file is allocated. Since the smallest ! 1293: * value of fcbEOF is 0, length will be at least 1. ! 1294: */ ! 1295: if ((u_long)length > fcb->fcbEOF) { ! 1296: off_t filePosition; ! 1297: daddr_t logBlockNo; ! 1298: long logBlockSize; ! 1299: long blkOffset; ! 1300: off_t bytestoclear; ! 1301: int blockZeroCount; ! 1302: struct buf *bp=NULL; ! 1303: ! 1304: /* ! 1305: * If we don't have enough physical space then ! 1306: * we need to extend the physical size. ! 1307: */ ! 1308: if ((u_long)length > fcb->fcbPLen) { ! 1309: /* lock extents b-tree (also protects volume bitmap) */ ! 1310: retval = hfs_metafilelocking(HTOHFS(hp), kHFSExtentsFileID, LK_EXCLUSIVE, ap->a_p); ! 1311: if (retval) goto Err_Exit; ! 1312: ! 1313: while (((u_long)length > fcb->fcbPLen) && (retval == E_NONE)) { ! 1314: /* XXX PPD Allocate only 2GB at a time until ExtendFileC's interface changes... */ ! 1315: bytesToAdd = length - (off_t)fcb->fcbPLen; ! 1316: if (bytesToAdd > (off_t)0x80000000) bytesToAdd = (off_t)0x80000000; ! 1317: retval = MacToVFSError( ! 1318: ExtendFileC (HTOVCB(hp), ! 1319: fcb, ! 1320: (u_long)bytesToAdd, ! 1321: kEFAllMask, /* allocate all requested bytes or none */ ! 1322: &actualBytesAdded)); ! 1323: ! 1324: if (actualBytesAdded == 0 && retval == E_NONE) { ! 1325: if ((u_long)length > fcb->fcbPLen) ! 1326: length = (off_t)fcb->fcbPLen; ! 1327: break; ! 1328: } ! 1329: ! 1330: /* If we just successfully extended the file by only 2GB, try again (just to be sure): */ ! 1331: if (bytesToAdd == (off_t)0x80000000) continue; ! 1332: } ! 1333: (void) hfs_metafilelocking(HTOHFS(hp), kHFSExtentsFileID, LK_RELEASE, ap->a_p); ! 1334: if (retval) goto Err_Exit; ! 1335: ! 1336: DBG_ASSERT((u_long)length <= fcb->fcbPLen); ! 1337: ! 1338: UpdateBlockMappingTable(hp); ! 1339: ! 1340: #if MACH_NBC ! 1341: if ((vp->v_type == VREG) && (vp->v_vm_info && !(vp->v_vm_info->mapped))) { ! 1342: #endif /* MACH_NBC */ ! 1343: vnode_pager_setsize(vp, (u_long)length); ! 1344: vnode_uncache(vp); ! 1345: #if MACH_NBC ! 1346: } ! 1347: #endif /* !MACH_NBC */ ! 1348: } ! 1349: ! 1350: if (! (ap->a_flags & IO_NOZEROFILL)) { ! 1351: /* ! 1352: * zero out any new logical space... ! 1353: */ ! 1354: VOP_DEVBLOCKSIZE(hp->h_devvp, &devBlockSize); ! 1355: ! 1356: bytestoclear = length - (off_t)fcb->fcbEOF; ! 1357: filePosition = (off_t)fcb->fcbEOF; ! 1358: while (bytestoclear > 0) { ! 1359: MapFileOffset(hp, filePosition, &logBlockNo, &logBlockSize, &blkOffset); ! 1360: blockZeroCount = MIN(bytestoclear, logBlockSize - blkOffset); ! 1361: if ((blkOffset == 0) && (bytestoclear >= logBlockSize)) { ! 1362: bp = getblk(vp, logBlockNo, logBlockSize, 0, 0); ! 1363: retval = 0; ! 1364: if (bp->b_flags & (B_DELWRI | B_DONE | B_CACHE)) { ! 1365: /* ! 1366: This buffer is fully pre-read and may already have some modified data in it: ! 1367: it doesn't need special-casing for write-through. ! 1368: */ ! 1369: } else { ! 1370: /* This is an empty buffer just allocated for our use: */ ! 1371: ! 1372: /* XXX PPD Can't this be skipped if b_blkno != logBlockNo? */ ! 1373: ! 1374: /* Setting up the physical block number is required ! 1375: (1) to keep cluster_write informed about which blocks can be clustered, and ! 1376: (2) to avoid a second call to bmap() in strategy() on the write. ! 1377: */ ! 1378: if ((retval = VOP_BMAP(vp, logBlockNo, NULL, &bp->b_blkno, NULL))) { ! 1379: brelse(bp); ! 1380: break; ! 1381: }; ! 1382: ! 1383: bp->b_bcount = 0; /* No valid data in block yet */ ! 1384: bp->b_validend = 0; /* No valid data in block yet */ ! 1385: }; ! 1386: } else { ! 1387: retval = bread(vp, logBlockNo, logBlockSize, ap->a_cred, &bp); ! 1388: if (retval) { ! 1389: brelse(bp); ! 1390: goto Err_Exit; ! 1391: } ! 1392: } ! 1393: ! 1394: /* ! 1395: Fix up the anciliary fields for this buffer, depending on whether they've been initialized yet, ! 1396: and check to see whether we need to get more data than what was just found in the cache: ! 1397: */ ! 1398: if (bp->b_validend > 0) { ! 1399: /* ! 1400: b_validoff, b_validend, b_dirtyoff, and b_dirtyend are valid for blocks in the cache: ! 1401: The only blocks that are incomplete (b_validend < fragSize) are blocks that have been ! 1402: partially written from the start of the buffer: ! 1403: */ ! 1404: if (bp->b_validend < bp->b_bcount) { ! 1405: /* ! 1406: We stumbled onto an incomplete [but successfully acquired] buffer: ! 1407: try to get the full contents now because it's the only way to get ! 1408: the data in this logical block... ! 1409: */ ! 1410: DBG_ASSERT((bp->b_dirtyoff == 0) && (bp->b_validoff == 0) && (bp->b_dirtyend <= bp->b_validend)); ! 1411: /* Incomplete blocks must have only device-block multiples of data... */ ! 1412: DBG_ASSERT((bp->b_validend % devBlockSize) == 0); ! 1413: bp->b_bcount = bp->b_validend; ! 1414: ! 1415: retval = bexpand(bp, logBlockSize, &bp, 0); ! 1416: }; ! 1417: } else { ! 1418: /* This buffer was either just allocated or just read in its entirity [see b_bcount]: */ ! 1419: DBG_ASSERT(bp->b_validoff == 0); ! 1420: bp->b_validend = bp->b_bcount; /* b_bcount > 0 iff block was actually read from disk in bread */ ! 1421: DBG_ASSERT(bp->b_dirtyoff == 0); ! 1422: DBG_ASSERT(bp->b_dirtyend == 0); ! 1423: }; ! 1424: ! 1425: bzero((char *)bp->b_data + blkOffset, blockZeroCount); ! 1426: ! 1427: if (blkOffset + blockZeroCount > bp->b_dirtyend) bp->b_dirtyend = blkOffset + blockZeroCount; ! 1428: if (bp->b_dirtyend > bp->b_validend) bp->b_validend = bp->b_dirtyend; ! 1429: if (bp->b_validend > bp->b_bcount) bp->b_bcount = bp->b_validend; ! 1430: DBG_ASSERT(bp->b_bcount % devBlockSize == 0); ! 1431: DBG_ASSERT(bp->b_bcount == bp->b_validend); ! 1432: bp->b_flags |= B_DIRTY | B_AGE; ! 1433: if (ap->a_flags & IO_SYNC) { ! 1434: bwrite(bp); ! 1435: } else if (doclusterwrite && can_cluster(logBlockSize)) { ! 1436: cluster_write(bp, fcb->fcbPLen, devBlockSize); ! 1437: } else if (logBlockNo % 32) { ! 1438: bawrite(bp); ! 1439: } else { ! 1440: bwrite(bp); /* wait after we issue 32 requests */ ! 1441: }; ! 1442: ! 1443: bytestoclear -= blockZeroCount; ! 1444: if (blkOffset > 0) ! 1445: blkOffset = 0; ! 1446: ! 1447: filePosition += blockZeroCount; ! 1448: } ! 1449: } ! 1450: ! 1451: fcb->fcbEOF = (u_long)length; ! 1452: ! 1453: } else { /* Shorten the size of the file */ ! 1454: ! 1455: if (fcb->fcbEOF > (u_long)length) { ! 1456: /* ! 1457: * Any buffers that are past the truncation point need to be ! 1458: * invalidated (to maintain buffer cache consistency). For ! 1459: * simplicity, we invalidate all the buffers by calling vinvalbuf. ! 1460: */ ! 1461: vflags = ((length > 0) ? V_SAVE : 0); /* XXX PPD Should we set SAVE_META? */ ! 1462: retval = vinvalbuf(vp, vflags, ap->a_cred, ap->a_p, 0, 0); ! 1463: } ! 1464: /* lock extents b-tree (also protects volume bitmap) */ ! 1465: retval = hfs_metafilelocking(HTOHFS(hp), kHFSExtentsFileID, LK_EXCLUSIVE, ap->a_p); ! 1466: if (retval) goto Err_Exit; ! 1467: ! 1468: retval = MacToVFSError( ! 1469: TruncateFileC( ! 1470: HTOVCB(hp), ! 1471: fcb, ! 1472: (u_long)length, ! 1473: false)); ! 1474: (void) hfs_metafilelocking(HTOHFS(hp), kHFSExtentsFileID, LK_RELEASE, ap->a_p); ! 1475: if (retval) goto Err_Exit; ! 1476: ! 1477: fcb->fcbEOF = (u_long)length; ! 1478: if (fcb->fcbFlags &fcbModifiedMask) ! 1479: hp->h_meta->h_nodeflags |= IN_MODIFIED; ! 1480: ! 1481: UpdateBlockMappingTable(hp); ! 1482: ! 1483: #if MACH_NBC ! 1484: if ((vp->v_type == VREG) && (vp->v_vm_info && !(vp->v_vm_info->mapped))) { ! 1485: #endif /* MACH_NBC */ ! 1486: vnode_pager_setsize(vp, (u_long)length); ! 1487: #if MACH_NBC ! 1488: } ! 1489: #endif /* MACH_NBC */ ! 1490: ! 1491: } ! 1492: ! 1493: hp->h_meta->h_nodeflags |= IN_CHANGE | IN_UPDATE; ! 1494: retval = VOP_UPDATE(vp, &tv, &tv, MNT_WAIT); ! 1495: ! 1496: Err_Exit:; ! 1497: ! 1498: #if DIAGNOSTIC ! 1499: debug_check_blocksizes(ap->a_vp); ! 1500: #endif ! 1501: ! 1502: DBG_VOP_LOCKS_TEST(retval); ! 1503: return (retval); ! 1504: } ! 1505: ! 1506: ! 1507: ! 1508: /* ! 1509: # ! 1510: #% allocate vp L L L ! 1511: # ! 1512: vop_allocate { ! 1513: IN struct vnode *vp; ! 1514: IN off_t length; ! 1515: IN int flags; ! 1516: IN struct ucred *cred; ! 1517: IN struct proc *p; ! 1518: }; ! 1519: * allocate the hfsnode hp to at most length size ! 1520: */ ! 1521: int hfs_allocate(ap) ! 1522: struct vop_allocate_args /* { ! 1523: struct vnode *a_vp; ! 1524: off_t a_length; ! 1525: u_int32_t a_flags; ! 1526: off_t *a_bytesallocated; ! 1527: struct ucred *a_cred; ! 1528: struct proc *a_p; ! 1529: } */ *ap; ! 1530: { ! 1531: register struct vnode *vp = ap->a_vp; ! 1532: register struct hfsnode *hp = VTOH(vp); ! 1533: off_t length = ap->a_length; ! 1534: long vflags; ! 1535: u_long startingPEOF; ! 1536: struct timeval tv; ! 1537: int retval, retval2; ! 1538: FCB *fcb; ! 1539: UInt32 actualBytesAdded; ! 1540: UInt32 extendFlags =0; /* For call to ExtendFileC */ ! 1541: DBG_FUNC_NAME("allocate"); ! 1542: DBG_VOP_LOCKS_DECL(1); ! 1543: DBG_VOP_PRINT_FUNCNAME(); ! 1544: DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP_CONT(("\n")); ! 1545: DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_POS); ! 1546: ! 1547: /* Set the number of bytes allocated to 0 so that the caller will know that we ! 1548: did nothing. ExtendFileC will fill this in for us if we actually allocate space */ ! 1549: ! 1550: *(ap->a_bytesallocated) = 0; ! 1551: ! 1552: /* Now for some error checking */ ! 1553: ! 1554: if (length < (off_t)0) { ! 1555: DBG_VOP_LOCKS_TEST(EINVAL); ! 1556: return (EINVAL); ! 1557: } ! 1558: ! 1559: if (length > (u_int64_t)0x7FFFFFFF) { /* XXX need to distinguish between hfs and hfs+ */ ! 1560: DBG_VOP_LOCKS_TEST(EFBIG); ! 1561: return (EFBIG); ! 1562: } ! 1563: ! 1564: if (vp->v_type != VREG && vp->v_type != VLNK) { ! 1565: DBG_VOP_LOCKS_TEST(EISDIR); ! 1566: return (EISDIR); /* hfs doesn't support truncating of directories */ ! 1567: } ! 1568: ! 1569: /* Fill in the flags word for the call to Extend the file */ ! 1570: ! 1571: if (ap->a_flags & ALLOCATECONTIG) { ! 1572: extendFlags |= kEFContigMask; ! 1573: } ! 1574: ! 1575: if (ap->a_flags & ALLOCATEALL) { ! 1576: extendFlags |= kEFAllMask; ! 1577: } ! 1578: ! 1579: fcb = HTOFCB(hp); ! 1580: tv = time; ! 1581: retval = E_NONE; ! 1582: startingPEOF = fcb->fcbPLen; ! 1583: ! 1584: if (ap->a_flags & ALLOCATEFROMPEOF) { ! 1585: length += fcb->fcbPLen; ! 1586: } ! 1587: ! 1588: DBG_VOP(("%s: allocate from Ox%lX to Ox%X bytes\n", funcname, fcb->fcbPLen, (u_int)length)); ! 1589: ! 1590: /* If no changes are necesary, then we're done */ ! 1591: if (fcb->fcbPLen == (u_long)length) ! 1592: goto Std_Exit; ! 1593: ! 1594: /* ! 1595: * Lengthen the size of the file. We must ensure that the ! 1596: * last byte of the file is allocated. Since the smallest ! 1597: * value of fcbPLen is 0, length will be at least 1. ! 1598: */ ! 1599: if ((u_long)length > fcb->fcbPLen) { ! 1600: ! 1601: /* lock extents b-tree (also protects volume bitmap) */ ! 1602: retval = hfs_metafilelocking(HTOHFS(hp), kHFSExtentsFileID, LK_EXCLUSIVE, ap->a_p); ! 1603: if (retval) goto Err_Exit; ! 1604: ! 1605: retval = MacToVFSError( ! 1606: ExtendFileC(HTOVCB(hp), ! 1607: fcb, ! 1608: (u_long)length - fcb->fcbPLen, ! 1609: extendFlags, ! 1610: &actualBytesAdded)); ! 1611: ! 1612: *(ap->a_bytesallocated) = actualBytesAdded; ! 1613: ! 1614: (void) hfs_metafilelocking(HTOHFS(hp), kHFSExtentsFileID, LK_RELEASE, ap->a_p); ! 1615: ! 1616: DBG_ASSERT((u_long)length <= fcb->fcbPLen); ! 1617: ! 1618: UpdateBlockMappingTable(hp); ! 1619: ! 1620: /* ! 1621: * if we get an error and no changes were made then exit ! 1622: * otherwise we must do the VOP_UPDATE to reflect the changes ! 1623: */ ! 1624: if (retval && (startingPEOF == fcb->fcbPLen)) goto Err_Exit; ! 1625: ! 1626: } else { /* Shorten the size of the file */ ! 1627: ! 1628: if (fcb->fcbEOF > (u_long)length) { ! 1629: /* ! 1630: * Any buffers that are past the truncation point need to be ! 1631: * invalidated (to maintain buffer cache consistency). For ! 1632: * simplicity, we invalidate all the buffers by calling vinvalbuf. ! 1633: */ ! 1634: vflags = ((length > 0) ? V_SAVE : 0) | V_SAVEMETA; ! 1635: (void) vinvalbuf(vp, vflags, ap->a_cred, ap->a_p, 0, 0); ! 1636: } ! 1637: ! 1638: /* lock extents b-tree (also protects volume bitmap) */ ! 1639: retval = hfs_metafilelocking(HTOHFS(hp), kHFSExtentsFileID, LK_EXCLUSIVE, ap->a_p); ! 1640: if (retval) goto Err_Exit; ! 1641: ! 1642: retval = MacToVFSError( ! 1643: TruncateFileC( ! 1644: HTOVCB(hp), ! 1645: fcb, ! 1646: (u_long)length, ! 1647: false)); ! 1648: (void) hfs_metafilelocking(HTOHFS(hp), kHFSExtentsFileID, LK_RELEASE, ap->a_p); ! 1649: ! 1650: /* ! 1651: * if we get an error and no changes were made then exit ! 1652: * otherwise we must do the VOP_UPDATE to reflect the changes ! 1653: */ ! 1654: if (retval && (startingPEOF == fcb->fcbPLen)) goto Err_Exit; ! 1655: if (fcb->fcbFlags & fcbModifiedMask) ! 1656: hp->h_meta->h_nodeflags |= IN_MODIFIED; ! 1657: ! 1658: DBG_ASSERT((u_long)length <= fcb->fcbPLen) // DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG ! 1659: ! 1660: if (fcb->fcbEOF > fcb->fcbPLen) { ! 1661: fcb->fcbEOF = fcb->fcbPLen; ! 1662: ! 1663: UpdateBlockMappingTable(hp); ! 1664: ! 1665: #if MACH_NBC ! 1666: if ((vp->v_type == VREG) && (vp->v_vm_info && !(vp->v_vm_info->mapped))) { ! 1667: #endif /* MACH_NBC */ ! 1668: vnode_pager_setsize(vp, (u_long)fcb->fcbEOF); ! 1669: #if MACH_NBC ! 1670: } ! 1671: #endif /* MACH_NBC */ ! 1672: } ! 1673: (void)vnode_uncache(vp); ! 1674: } ! 1675: ! 1676: Std_Exit: ! 1677: hp->h_meta->h_nodeflags |= IN_CHANGE | IN_UPDATE; ! 1678: retval2 = VOP_UPDATE(vp, &tv, &tv, MNT_WAIT); ! 1679: ! 1680: if (retval == 0) retval = retval2; ! 1681: ! 1682: Err_Exit: ! 1683: DBG_VOP_LOCKS_TEST(retval); ! 1684: return (retval); ! 1685: } ! 1686: ! 1687: ! 1688: ! 1689: ! 1690: /* Pass-through pagein for HFS filesystem */ ! 1691: int ! 1692: hfs_pagein(ap) ! 1693: struct vop_pagein_args /* { ! 1694: struct vnode *a_vp; ! 1695: struct uio *a_uio; ! 1696: int a_ioflag; ! 1697: struct ucred *a_cred; ! 1698: } */ *ap; ! 1699: { ! 1700: /* pass thru to read */ ! 1701: return (VOP_READ(ap->a_vp, ap->a_uio, ap->a_ioflag, ap->a_cred)); ! 1702: } ! 1703: ! 1704: /* Pass-through pageout for HFS filesystem */ ! 1705: int ! 1706: hfs_pageout(ap) ! 1707: struct vop_pageout_args /* { ! 1708: struct vnode *a_vp; ! 1709: struct uio *a_uio; ! 1710: int a_ioflag; ! 1711: struct ucred *a_cred; ! 1712: } */ *ap; ! 1713: { ! 1714: /* pass thru to write */ ! 1715: return (VOP_WRITE(ap->a_vp, ap->a_uio, ap->a_ioflag, ap->a_cred)); ! 1716: } ! 1717: ! 1718: ! 1719: ! 1720: /* ! 1721: Try to expand the range of a buffer. ! 1722: ! 1723: Possible values for 'flags' are: ! 1724: ! 1725: RELEASE_BUFFER - to specify that the expanded buffer need not be held ! 1726: ! 1727: Calling bexpand with nbpp == &bp to expand a given buffer is OK; ! 1728: even on errors, the current buffer (bp) is always brelse-ed! ! 1729: */ ! 1730: int ! 1731: bexpand(struct buf *bp, int newsize, struct buf **nbpp, long flags) ! 1732: { ! 1733: struct vnode *ovp; ! 1734: daddr_t olblkno; ! 1735: struct ucred *ocred; ! 1736: int odirtyend; ! 1737: int releasebp = 1; ! 1738: struct buf *tbp = NULL; ! 1739: struct buf *nbp = NULL; ! 1740: int retval = 0; ! 1741: ! 1742: DBG_ASSERT(bp != NULL); ! 1743: if (nbpp == NULL) DBG_ASSERT(flags & RELEASE_BUFFER); ! 1744: ! 1745: if (bp->b_validend >= newsize) { ! 1746: /* Sufficient amount already read or written into buffer: */ ! 1747: nbp = bp; /* nbp is checked against bp in exit path */ ! 1748: retval = 0; ! 1749: goto stdexit; ! 1750: }; ! 1751: ! 1752: if (nbpp) *nbpp = NULL; /* Just to be clean, and to ensure != bp... */ ! 1753: ! 1754: ovp = bp->b_vp; ! 1755: olblkno = bp->b_lblkno; ! 1756: ocred = bp->b_rcred; ! 1757: odirtyend = bp->b_dirtyend; ! 1758: ! 1759: if (bp->b_flags & B_DELWRI) { ! 1760: /* This buffer holds dirtied data that must be preserved: */ ! 1761: /* XXX PPD 9/28/98 Should assert that b_dirtyoff == 0 here, but not this late in the game... */ ! 1762: tbp = geteblk(odirtyend); /* Grab a new [temporary] buffer big enough to ! 1763: hold the dirty parts of this buffer for a sec. */ ! 1764: if (tbp == NULL) { ! 1765: retval = ENOMEM; ! 1766: goto errexit; ! 1767: }; ! 1768: bcopy(bp->b_data, tbp->b_data, odirtyend); ! 1769: }; ! 1770: ! 1771: /* It's now safe to trash the entire current contents of the buffer */ ! 1772: bp->b_flags |= B_INVAL; ! 1773: DBG_ASSERT((bp->b_validend == 0) || (bp->b_validend == bp->b_bcount)); ! 1774: brelse(bp); ! 1775: releasebp = 0; ! 1776: ! 1777: // if (((flags & RELEASE_BUFFER) == 0) || (tbp != NULL)) { ! 1778: if (1) { ! 1779: retval = bread(ovp, olblkno, newsize, ocred, &nbp); /* Read in the requested data */ ! 1780: if (retval != 0) goto errexit; ! 1781: ! 1782: nbp->b_validoff = 0; ! 1783: nbp->b_validend = newsize; ! 1784: nbp->b_dirtyoff = 0; ! 1785: nbp->b_dirtyend = 0; ! 1786: ! 1787: if (tbp) { ! 1788: /* Restore the modified data from the old buffer: */ ! 1789: bcopy(tbp->b_data, nbp->b_data, odirtyend); ! 1790: nbp->b_dirtyend = odirtyend; ! 1791: DBG_ASSERT((bp->b_validend == 0) || (bp->b_validend == bp->b_bcount)); ! 1792: bdwrite(nbp); /* Mark this block dirty and move to appropriate buffer */ ! 1793: ! 1794: if (flags & RELEASE_BUFFER) { ! 1795: nbp = NULL; /* Forget about the new buffer just written */ ! 1796: } else { ! 1797: /* Get the buffer back on behalf of our caller, even reading it back in in the ! 1798: unlikely case it's been flushed and re-used since the bdwrite(), above: */ ! 1799: retval = bread(ovp, olblkno, newsize, ocred, nbpp); ! 1800: if (retval != 0) goto errexit; ! 1801: nbp->b_validoff = 0; ! 1802: nbp->b_validend = newsize; ! 1803: nbp->b_dirtyoff = 0; ! 1804: nbp->b_dirtyend = odirtyend; ! 1805: }; ! 1806: ! 1807: brelse(tbp); ! 1808: tbp = NULL; /* Just in case we hit errors later */ ! 1809: }; ! 1810: }; ! 1811: goto stdexit; ! 1812: ! 1813: errexit: ! 1814: if (tbp) brelse(tbp); ! 1815: ! 1816: if (releasebp && (bp != nbp)) { ! 1817: DBG_ASSERT((bp->b_validend == 0) || (bp->b_validend == bp->b_bcount)); ! 1818: brelse(bp); ! 1819: }; ! 1820: ! 1821: stdexit:; ! 1822: if (nbpp) *nbpp = nbp; ! 1823: ! 1824: if ((flags & RELEASE_BUFFER) && (nbp != NULL)) { ! 1825: brelse(nbp); ! 1826: }; ! 1827: ! 1828: #if DIAGNOSTIC ! 1829: debug_check_blocksizes(bp->b_vp); ! 1830: #endif ! 1831: ! 1832: return retval; ! 1833: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.