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