|
|
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:
1.1.1.2 ! root 441: #if MACH_NBC
1.1 root 442: (void)vnode_uncache(vp); /* XXX PPD Is this the right place to call this? It'll VOP_UNLOCK the vnode */
1.1.1.2 ! root 443: #endif /* MACH_NBC */
1.1 root 444:
445: #if DIAGNOSTIC
446: if (uio->uio_rw != UIO_WRITE)
447: panic("%s: mode", funcname);
448: #endif
449:
450: ioflag = ap->a_ioflag;
451: uio = ap->a_uio;
452: vp = ap->a_vp;
453:
454: if (ioflag & IO_APPEND)
455: uio->uio_offset = fcb->fcbEOF;
456: if ((hp->h_meta->h_pflags & APPEND) && uio->uio_offset != fcb->fcbEOF)
457: return (EPERM);
458:
459: writelimit = uio->uio_offset + uio->uio_resid;
460:
461: /*
462: * Maybe this should be above the vnode op call, but so long as
463: * file servers have no limits, I don't think it matters.
464: */
465: p = uio->uio_procp;
466: if (vp->v_type == VREG && p &&
467: writelimit > p->p_rlimit[RLIMIT_FSIZE].rlim_cur) {
468: psignal(p, SIGXFSZ);
469: return (EFBIG);
470: };
471: VOP_DEVBLOCKSIZE(hp->h_devvp, &devBlockSize);
472:
473: resid = uio->uio_resid;
474: origFileSize = fcb->fcbPLen;
475: flags = ioflag & IO_SYNC ? B_SYNC : 0;
476:
477: DBG_VOP(("\tLEOF is 0x%lX, PEOF is 0x%lX.\n", fcb->fcbEOF, fcb->fcbPLen));
478:
479: /*
480: NOTE: In the following loop there are two positions tracked:
481: currOffset is the current I/O starting offset. currOffset is never >LEOF; the
482: LEOF is nudged along with currOffset as data is zeroed or written.
483: uio->uio_offset is the start of the current I/O operation. It may be arbitrarily
484: beyond currOffset.
485:
486: The following is true at all times:
487:
488: currOffset <= LEOF <= uio->uio_offset <= writelimit
489: */
490: currOffset = MIN(uio->uio_offset, fcb->fcbEOF);
491:
492: DBG_VOP(("\tstarting I/O loop at 0x%lX.\n", (u_long)currOffset));
493:
494: cp = CURRENT_PROC;
495:
496: for (retval = 0; uio->uio_resid > 0;) {
497: /* Now test if we need to extend the file */
498: /* Doing so will adjust the fcbPLen for us */
499: if (writelimit > (off_t)fcb->fcbPLen) {
500: /* XXX PPD Allocate only 2GB at a time until ExtendFileC's interface changes... */
501: bytesToAdd = writelimit - (off_t)fcb->fcbPLen;
502: if (bytesToAdd > (off_t)0x80000000) bytesToAdd = (off_t)0x80000000;
503: DBG_VOP(("\textending file by 0x%lX bytes; 0x%lX blocks free", (unsigned long)bytesToAdd, (unsigned long)vcb->vcbFreeBks));
504: /* lock extents b-tree (also protects volume bitmap) */
505: retval = hfs_metafilelocking(HTOHFS(hp), kHFSExtentsFileID, LK_EXCLUSIVE, cp);
506: if (retval != E_NONE)
507: break;
508:
509: retval = MacToVFSError(
510: ExtendFileC (vcb,
511: fcb,
512: (unsigned long)bytesToAdd,
513: kEFContigBit,
514: &actualBytesAdded));
515:
516: (void) hfs_metafilelocking(HTOHFS(hp), kHFSExtentsFileID, LK_RELEASE, cp);
517: DBG_VOP_CONT(("\tactual bytes added = 0x%lX bytes, retval = %d...\n", actualBytesAdded, retval));
518: if ((actualBytesAdded == 0) && (retval == 0)) retval = ENOSPC;
519: if (retval != E_NONE)
520: break;
521:
522: UpdateBlockMappingTable(hp);
523:
524: /* We successfully extended the file: take a fresh look to see how things have changed... */
525: continue;
526: };
527:
528: /* What block are we starting the write */
529: MapFileOffset(hp, currOffset, &logBlockNo, &fragSize, &blkoffset);
530:
531: xfersize = fragSize - blkoffset;
532:
533: DBG_VOP(("\tcurrOffset = Ox%lX, logBlockNo = Ox%X, blkoffset = Ox%lX, xfersize = Ox%lX, fragSize = Ox%lX.\n",
534: (unsigned long)currOffset, logBlockNo, blkoffset, xfersize, fragSize));
535:
536: /* Make any adjustments for boundary conditions */
537: if (currOffset + (off_t)xfersize > writelimit) {
538: xfersize = writelimit - currOffset;
539: DBG_VOP(("\ttrimming xfersize to 0x%lX to match writelimit (uio_resid)...\n", xfersize));
540: };
541:
542: /*
543: * There is no need to read into bp if:
544: * We start on a block boundary and will overwrite the whole block
545: *
546: * OR
547: *
548: * The transfer starts on a device block boundary and is an even
549: * multiple of the device block size (in which case we'll write
550: * through the data presented)
551: *
552: */
553: if ((blkoffset == 0) && ((xfersize >= fragSize) || (xfersize % devBlockSize == 0))) {
554: DBG_VOP(("\tRequesting %ld-byte block Ox%lX w/o read...\n", fragSize, (long)logBlockNo));
555: bp = getblk(vp, logBlockNo, fragSize, 0, 0);
556: retval = 0;
557: if (bp->b_blkno == -1) {
558: brelse(bp);
559: retval = EIO; /* XXX */
560: break;
561: }
562:
563: if (bp->b_flags & (B_DELWRI | B_DONE | B_CACHE)) {
564: /*
565: This buffer is fully pre-read and may already have some modified data in it:
566: it doesn't need special-casing for write-through.
567: */
568: } else {
569: /* This is an empty buffer just allocated for our use:
570: b_validoff, b_validend, b_dirtyoff, and b_dirtyend are all zero,
571: which is exactly right (b_bcount is set to fragSize). */
572:
573: /* XXX PPD Can't this be skipped if b_blkno != logBlockNo? */
574:
575: /* Setting up the physical block number is required
576: (1) to keep cluster_write informed about which blocks can be clustered, and
577: (2) to avoid a second call to bmap() in strategy() on the write.
578: */
579: if ((retval = VOP_BMAP(vp, logBlockNo, NULL, &bp->b_blkno, NULL))) {
580: brelse(bp);
581: break;
582: };
583:
584: bp->b_bcount = 0; /* No valid data in block yet */
585: bp->b_validend = 0; /* No valid data in block yet */
586: };
587: } else {
588: /*
589: * This I/O transfer is not sufficiently aligned, so read the affected block into a buffer:
590: */
591: DBG_VOP(("\tRequesting block Ox%X, size = 0x%08lX...\n", logBlockNo, fragSize));
592: retval = bread(vp, logBlockNo, fragSize, ap->a_cred, &bp);
593: if (retval != E_NONE) {
594: if (bp) brelse(bp);
595: break;
596: }
597: }
598:
599: /* Fix up the anciliary fields for this buffer, depending on whether they've been initialized yet: */
600: if (bp->b_validend > 0) {
601: /*
602: b_validoff, b_validend, b_dirtyoff, and b_dirtyend are valid for blocks in the cache:
603: The only blocks that are incomplete (b_validend < fragSize) are blocks that have been
604: partially written from the start of the buffer:
605: */
606: if (bp->b_validend < bp->b_bcount) {
607: DBG_ASSERT((bp->b_dirtyoff == 0) && (bp->b_validoff == 0) && (bp->b_dirtyend <= bp->b_validend));
608: /* Incomplete blocks must have only device-block multiples of data... */
609: DBG_ASSERT((bp->b_validend % devBlockSize) == 0);
610: bp->b_bcount = bp->b_validend;
611:
612: if ((bp->b_validend < blkoffset) || /* ... valid data does not overlap (or at least abut) start of new write */
613: (bp->b_bufsize < (blkoffset + xfersize)) || /* ... or isn't enough buffer space to hold entire transfer */
614: ((blkoffset + xfersize) % devBlockSize != 0)) { /* ... or won't leave blocksize-multiple of dirty bytes */
615: /* We stumbled onto an incomplete [but successfully acquired] buffer:
616: try to get the full contents now because it's the only way to get
617: the data in this logical block...
618: */
619: retval = bexpand(bp, fragSize, &bp, 0);
620: };
621: };
622: } else {
623: /* This buffer was either just allocated or just read in its entirity [see b_bcount]:
624: b_validoff, b_validend, b_dirtyoff, and b_dirtyend are all zeroed, which is almost correct. */
625: DBG_ASSERT(bp->b_validoff == 0);
626: bp->b_validend = bp->b_bcount; /* b_bcount > 0 iff block was actually read from disk in bread */
627: DBG_ASSERT(bp->b_dirtyoff == 0);
628: DBG_ASSERT(bp->b_dirtyend == 0);
629: };
630:
631: /* See if we are starting to write within file boundaries:
632: If not, then we need to present a "hole" for the area between
633: the current EOF and the start of the current I/O operation:
634:
635: Note that currOffset is only less than uio_offset if uio_offset > LEOF...
636: */
637: if (uio->uio_offset > currOffset) {
638: clearSize = MIN(uio->uio_offset - currOffset, xfersize);
639: DBG_VOP(("\tzeroing Ox%lX bytes Ox%lX bytes into block Ox%X...\n", clearSize, blkoffset, logBlockNo));
640: bzero(bp->b_data + blkoffset, clearSize);
641: currOffset += clearSize;
642: blkoffset += clearSize;
643: xfersize -= clearSize;
644: };
645:
646: if (xfersize > 0) {
647: DBG_VOP(("\tCopying Ox%lX bytes Ox%lX bytes into block Ox%X... ioflag == 0x%X\n",
648: xfersize, blkoffset, logBlockNo, ioflag));
649: retval = uiomove((caddr_t)bp->b_data + blkoffset, (int)xfersize, uio);
650: currOffset += xfersize;
651: };
652:
653: if (blkoffset + xfersize > bp->b_dirtyend) bp->b_dirtyend = blkoffset + xfersize; /* Newly written data is now [also] dirty */
654: if (bp->b_dirtyend > bp->b_validend) bp->b_validend = bp->b_dirtyend; /* Data just written is now valid, too */
655: bp->b_bcount = bp->b_validend;
656:
657: DBG_ASSERT((bp->b_bcount % devBlockSize) == 0);
658: DBG_ASSERT(bp->b_bcount == bp->b_validend);
659: if (ioflag & IO_SYNC) {
660: (void)bwrite(bp);
661: //DBG_VOP(("\tissuing bwrite\n"));
662: } else if ((xfersize + blkoffset) == fragSize) {
663: if (doclusterwrite && can_cluster(fragSize)) {
664: //DBG_VOP(("\tissuing cluster_write\n"));
665: cluster_write(bp, fcb->fcbPLen, devBlockSize);
666: } else {
667: //DBG_VOP(("\tissuing bawrite\n"));
668: bp->b_flags |= B_AGE;
669: bawrite(bp);
670: }
671: } else {
672: //DBG_VOP(("\tissuing bdwrite\n"));
673: bdwrite(bp);
674: };
675:
676: /* Update the EOF if we just extended the file
677: (the PEOF has already been moved out and the block mapping table has been updated): */
678: if (currOffset > fcb->fcbEOF) {
679: DBG_VOP(("\textending EOF to 0x%lX...\n", fcb->fcbEOF));
680: fcb->fcbEOF = currOffset;
681: #if MACH_NBC
682: if ((vp->v_type == VREG) && (vp->v_vm_info && !(vp->v_vm_info->mapped))) {
683: #endif /* MACH_NBC */
684: vnode_pager_setsize(vp, (u_long)fcb->fcbEOF);
685: #if MACH_NBC
686: }
687: #endif /* MACH_NBC */
688: };
689:
690: if (retval || (resid == 0))
691: break;
692: hp->h_meta->h_nodeflags |= IN_CHANGE | IN_UPDATE;
693: };
694: /*
695: * If we successfully wrote any data, and we are not the superuser
696: * we clear the setuid and setgid bits as a precaution against
697: * tampering.
698: */
699: if (resid > uio->uio_resid && ap->a_cred && ap->a_cred->cr_uid != 0)
700: hp->h_meta->h_mode &= ~(ISUID | ISGID);
701: if (retval) {
702: if (ioflag & IO_UNIT) {
703: (void)VOP_TRUNCATE(vp, origFileSize,
704: ioflag & IO_SYNC, ap->a_cred, uio->uio_procp);
705: uio->uio_offset -= resid - uio->uio_resid;
706: uio->uio_resid = resid;
707: }
708: } else if (resid > uio->uio_resid && (ioflag & IO_SYNC))
709: retval = VOP_UPDATE(vp, &time, &time, 1);
710:
711: #if DIAGNOSTIC
712: debug_check_blocksizes(vp);
713: #endif
714:
715: DBG_VOP_LOCKS_TEST(retval);
716: return (retval);
717: }
718:
719:
720: /*
721:
722: #% ioctl vp U U U
723: #
724: vop_ioctl {
725: IN struct vnode *vp;
726: IN u_long command;
727: IN caddr_t data;
728: IN int fflag;
729: IN struct ucred *cred;
730: IN struct proc *p;
731:
732: */
733:
734:
735: /* ARGSUSED */
736: int
737: hfs_ioctl(ap)
738: struct vop_ioctl_args /* {
739: struct vnode *a_vp;
740: int a_command;
741: caddr_t a_data;
742: int a_fflag;
743: struct ucred *a_cred;
744: struct proc *a_p;
745: } */ *ap;
746: {
747: DBG_FUNC_NAME("ioctl");
748: DBG_VOP_LOCKS_DECL(1);
749: DBG_VOP_PRINT_FUNCNAME();
750: DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP_CONT(("\n"));
751:
752: DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_UNLOCKED, VOPDBG_UNLOCKED, VOPDBG_UNLOCKED, VOPDBG_POS);
753:
754: switch (ap->a_command) {
755:
756: case 1:
757: { register struct hfsnode *hp;
758: register struct vnode *vp;
759: register struct radvisory *ra;
760: FCB *fcb;
761: int devBlockSize = 0;
762: int error;
763: long size;
764: daddr_t lbn;
765:
766: vp = ap->a_vp;
767:
768: VOP_LEASE(vp, ap->a_p, ap->a_cred, LEASE_READ);
769: vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, ap->a_p);
770:
771: ra = (struct radvisory *)(ap->a_data);
772: hp = VTOH(vp);
773:
774: fcb = HTOFCB(hp);
775:
776: if ((u_int64_t)ra->ra_offset >= (u_int64_t)fcb->fcbEOF) {
777: VOP_UNLOCK(vp, 0, ap->a_p);
778: DBG_VOP_LOCKS_TEST(EFBIG);
779: return (EFBIG);
780: }
781: VOP_DEVBLOCKSIZE(hp->h_devvp, &devBlockSize);
782: size = hp->h_meta->h_logBlockSize;
783:
784: if ( !(can_cluster(size))) {
785: VOP_UNLOCK(vp, 0, ap->a_p);
786: DBG_VOP_LOCKS_TEST(EINVAL);
787: return (EINVAL);
788: }
789: size = MIN(size, MAXBSIZE);
790: lbn = ra->ra_offset / size;
791:
792: error = advisory_read(vp, fcb->fcbEOF, lbn, size, size, ra->ra_count, devBlockSize);
793: VOP_UNLOCK(vp, 0, ap->a_p);
794:
795: DBG_VOP_LOCKS_TEST(error);
796: return (error);
797: }
798:
799: case 2: /* F_READBOOTBLOCKS */
800: case 3: /* F_WRITEBOOTBLOCKS */
801: {
802: struct vnode *vp = ap->a_vp;
803: struct hfsnode *hp = VTOH(vp);
804: struct fbootstraptransfer *btd = (struct fbootstraptransfer *)ap->a_data;
805: u_long devBlockSize;
806: int error;
807: struct iovec aiov;
808: struct uio auio;
809: u_long blockNumber;
810: u_long blockOffset;
811: u_long xfersize;
812: struct buf *bp;
813:
814: if ((vp->v_flag & VROOT) == 0) return EINVAL;
815: if (btd->fbt_offset + btd->fbt_length > 1024) return EINVAL;
816:
817: aiov.iov_base = btd->fbt_buffer;
818: aiov.iov_len = btd->fbt_length;
819:
820: auio.uio_iov = &aiov;
821: auio.uio_iovcnt = 1;
822: auio.uio_offset = btd->fbt_offset;
823: auio.uio_resid = btd->fbt_length;
824: auio.uio_segflg = UIO_USERSPACE;
825: auio.uio_rw = (ap->a_command == 3) ? UIO_WRITE : UIO_READ; /* F_WRITEBOOTSTRAP / F_READBOOTSTRAP */
826: auio.uio_procp = ap->a_p;
827:
828: VOP_DEVBLOCKSIZE(hp->h_devvp, &devBlockSize);
829:
830: while (auio.uio_resid > 0) {
831: blockNumber = auio.uio_offset / devBlockSize;
832: error = bread(hp->h_devvp, blockNumber, devBlockSize, ap->a_cred, &bp);
833: if (error) {
834: if (bp) brelse(bp);
835: return error;
836: };
837:
838: blockOffset = auio.uio_offset % devBlockSize;
839: xfersize = devBlockSize - blockOffset;
840: error = uiomove((caddr_t)bp->b_data + blockOffset, (int)xfersize, &auio);
841: if (error) {
842: brelse(bp);
843: return error;
844: };
845: if (auio.uio_rw == UIO_WRITE) {
846: error = bwrite(bp);
847: if (error) return error;
848: } else {
849: brelse(bp);
850: };
851: };
852: };
853: return 0;
854:
855: default:
856: DBG_VOP_LOCKS_TEST(ENOTTY);
857: return (ENOTTY);
858: }
859:
860: return 0;
861: }
862:
863: /* ARGSUSED */
864: int
865: hfs_select(ap)
866: struct vop_select_args /* {
867: struct vnode *a_vp;
868: int a_which;
869: int a_fflags;
870: struct ucred *a_cred;
871: struct proc *a_p;
872: } */ *ap;
873: {
874: DBG_FUNC_NAME("select");
875: DBG_VOP_LOCKS_DECL(1);
876: DBG_VOP_PRINT_FUNCNAME();
877: DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP_CONT(("\n"));
878:
879: DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_LOCKED, VOPDBG_IGNORE, VOPDBG_IGNORE, VOPDBG_POS);
880:
881: /*
882: * We should really check to see if I/O is possible.
883: */
884: DBG_VOP_LOCKS_TEST(1);
885: return (1);
886: }
887:
888:
889:
890: /*
891: * Mmap a file
892: *
893: * NB Currently unsupported.
894: # XXX - not used
895: #
896: vop_mmap {
897: IN struct vnode *vp;
898: IN int fflags;
899: IN struct ucred *cred;
900: IN struct proc *p;
901:
902: */
903:
904: /* ARGSUSED */
905:
906: int
907: hfs_mmap(ap)
908: struct vop_mmap_args /* {
909: struct vnode *a_vp;
910: int a_fflags;
911: struct ucred *a_cred;
912: struct proc *a_p;
913: } */ *ap;
914: {
915: DBG_FUNC_NAME("mmap");
916: DBG_VOP_LOCKS_DECL(1);
917: DBG_VOP_PRINT_FUNCNAME();
918: DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP_CONT(("\n"));
919:
920: DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_IGNORE, VOPDBG_IGNORE, VOPDBG_IGNORE, VOPDBG_POS);
921:
922: DBG_VOP_LOCKS_TEST(EINVAL);
923: return (EINVAL);
924: }
925:
926:
927:
928: /*
929: * Seek on a file
930: *
931: * Nothing to do, so just return.
932: # XXX - not used
933: # Needs work: Is newoff right? What's it mean?
934: #
935: vop_seek {
936: IN struct vnode *vp;
937: IN off_t oldoff;
938: IN off_t newoff;
939: IN struct ucred *cred;
940: */
941: /* ARGSUSED */
942: int
943: hfs_seek(ap)
944: struct vop_seek_args /* {
945: struct vnode *a_vp;
946: off_t a_oldoff;
947: off_t a_newoff;
948: struct ucred *a_cred;
949: } */ *ap;
950: {
951: DBG_FUNC_NAME("seek");
952: DBG_VOP_LOCKS_DECL(1);
953: DBG_VOP_PRINT_FUNCNAME();
954: DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP_CONT(("\n"));
955:
956: DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_IGNORE, VOPDBG_IGNORE, VOPDBG_IGNORE, VOPDBG_POS);
957:
958: DBG_VOP_LOCKS_TEST(E_NONE);
959: return (E_NONE);
960: }
961:
962:
963: /*
964: * Bmap converts a the logical block number of a file to its physical block
965: * number on the disk.
966: */
967:
968: /*
969: * vp - address of vnode file the file
970: * bn - which logical block to convert to a physical block number.
971: * vpp - returns the vnode for the block special file holding the filesystem
972: * containing the file of interest
973: * bnp - address of where to return the filesystem physical block number
974: #% bmap vp L L L
975: #% bmap vpp - U -
976: #
977: vop_bmap {
978: IN struct vnode *vp;
979: IN daddr_t bn;
980: OUT struct vnode **vpp;
981: IN daddr_t *bnp;
982: OUT int *runp;
983: */
984: /*
985: * Converts a logical block number to a physical block, and optionally returns
986: * the amount of remaining blocks in a run. The logical block is based on hfsNode.logBlockSize.
987: * The physical block number is based on the device block size, currently its 512.
988: * The block run is returned in logical blocks, and is the REMAINING amount of blocks
989: */
990:
991: int
992: hfs_bmap(ap)
993: struct vop_bmap_args /* {
994: struct vnode *a_vp;
995: daddr_t a_bn;
996: struct vnode **a_vpp;
997: daddr_t *a_bnp;
998: int *a_runp;
999: } */ *ap;
1000: {
1001: struct hfsnode *hp = VTOH(ap->a_vp);
1002: struct hfsmount *hfsmp = VTOHFS(ap->a_vp);
1003: int retval = E_NONE;
1004: daddr_t logBlockSize;
1005: UInt32 bytesContAvail = 0;
1006: struct proc *p = NULL;
1007: int lockExtBtree;
1008:
1009: #define DEBUG_BMAP 0
1010: #if DEBUG_BMAP
1011: DBG_FUNC_NAME("bmap");
1012: DBG_VOP_LOCKS_DECL(2);
1013: DBG_VOP_PRINT_FUNCNAME();
1014: DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP((": %d --> ", ap->a_bn));
1015:
1016: DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_POS);
1017: if (ap->a_vpp != NULL) {
1018: DBG_VOP_LOCKS_INIT(1,*ap->a_vpp, VOPDBG_IGNORE, VOPDBG_UNLOCKED, VOPDBG_IGNORE, VOPDBG_POS);
1019: } else {
1020: DBG_VOP_LOCKS_INIT(1,NULL, VOPDBG_IGNORE, VOPDBG_IGNORE, VOPDBG_IGNORE, VOPDBG_POS);
1021: };
1022: #endif
1023:
1024: /*
1025: * Check for underlying vnode requests and ensure that logical
1026: * to physical mapping is requested.
1027: */
1028: if (ap->a_vpp != NULL)
1029: *ap->a_vpp = VTOH(ap->a_vp)->h_devvp;
1030: if (ap->a_bnp == NULL)
1031: return (0);
1032:
1033: lockExtBtree = hasOverflowExtents(hp);
1034: if (lockExtBtree)
1035: {
1036: p = CURRENT_PROC;
1037: retval = hfs_metafilelocking(hfsmp, kHFSExtentsFileID, LK_EXCLUSIVE | LK_CANRECURSE, p);
1038: if (retval)
1039: return (retval);
1040: }
1041:
1042:
1043: if (ap->a_bn < hp->h_uniformblocksizestart) {
1044: int targetLogicalBlockNo = ap->a_bn;
1045: off_t fileOffset = 0;
1046: int extent;
1047: long extentSize;
1048:
1049: logBlockSize = MAXLOGBLOCKSIZE;
1050: for (extent = 0; extent < LOGBLOCKMAPENTRIES; ++extent) {
1051: if ((hp->h_logicalblocktable[extent].logicalBlockCount > 0) &&
1052: (targetLogicalBlockNo < hp->h_logicalblocktable[extent].logicalBlockCount)) {
1053: retval = MacToVFSError(
1054: MapFileBlockC (HFSTOVCB(hfsmp),
1055: HTOFCB(hp),
1056: MAXPHYSIO,
1057: fileOffset + (targetLogicalBlockNo * MAXLOGBLOCKSIZE),
1058: (UInt32 *)ap->a_bnp,
1059: &bytesContAvail));
1060: /* Take the current FCB's extent length info: we could be mid-update */
1061: if (HTOVCB(hp)->vcbSigWord == kHFSSigWord) {
1062: extentSize = hp->h_xfcb->fcb_fcb.fcbExtRec[extent].blockCount * HTOVCB(hp)->blockSize;
1063: } else {
1064: extentSize = hp->h_xfcb->fcb_extFCB.extents[extent].blockCount * HTOVCB(hp)->blockSize;
1065: };
1066: DBG_ASSERT((bytesContAvail == MAXPHYSIO) || (bytesContAvail == (extentSize - (targetLogicalBlockNo * MAXLOGBLOCKSIZE))));
1067: break;
1068: };
1069: targetLogicalBlockNo -= hp->h_logicalblocktable[extent].logicalBlockCount;
1070: fileOffset += hp->h_logicalblocktable[extent].extentLength;
1071: };
1072: } else {
1073: logBlockSize = hp->h_meta->h_logBlockSize;
1074: retval = MacToVFSError(
1075: MapFileBlockC (HFSTOVCB(hfsmp),
1076: HTOFCB(hp),
1077: MAXPHYSIO,
1078: hp->h_optimizedblocksizelimit +
1079: ((ap->a_bn - hp->h_uniformblocksizestart) * logBlockSize),
1080: (UInt32 *)ap->a_bnp,
1081: &bytesContAvail));
1082:
1083: };
1084:
1085: if (lockExtBtree) (void) hfs_metafilelocking(hfsmp, kHFSExtentsFileID, LK_RELEASE, p);
1086:
1087: if (retval == E_NONE) {
1088: /* Figure out how many read ahead blocks there are */
1089: if (ap->a_runp != NULL) {
1090: if (can_cluster(logBlockSize)) {
1091: /* Make sure this result never goes negative: */
1092: *ap->a_runp = (bytesContAvail < logBlockSize) ? 0 : (bytesContAvail / logBlockSize) - 1;
1093: } else {
1094: *ap->a_runp = 0;
1095: };
1096: };
1097: };
1098:
1099: #if DEBUG_BMAP
1100: DBG_VOP(("%d:%d.\n", *ap->a_bnp, (bytesContAvail < logBlockSize) ? 0 : (bytesContAvail / logBlockSize) - 1));
1101:
1102: DBG_VOP_LOCKS_TEST(retval);
1103: #endif
1104:
1105: if (ap->a_runp) {
1106: DBG_ASSERT((*ap->a_runp * logBlockSize) < bytesContAvail); /* At least *ap->a_runp blocks left and ... */
1107: if (can_cluster(logBlockSize)) {
1108: DBG_ASSERT(bytesContAvail - (*ap->a_runp * logBlockSize) < (2*logBlockSize)); /* ... at most 1 logical block accounted for by current block */
1109: /* ... plus some sub-logical block sized piece */
1110: };
1111: };
1112:
1113: return (retval);
1114: }
1115:
1116:
1117: /*
1118: * Calculate the logical to physical mapping if not done already,
1119: * then call the device strategy routine.
1120: #
1121: #vop_strategy {
1122: # IN struct buf *bp;
1123: */
1124: int
1125: hfs_strategy(ap)
1126: struct vop_strategy_args /* {
1127: struct buf *a_bp;
1128: } */ *ap;
1129: {
1130: register struct buf *bp = ap->a_bp;
1131: register struct vnode *vp = bp->b_vp;
1132: register struct hfsnode *hp;
1133: long logBlockSize;
1134: int retval = 0;
1135: DBG_FUNC_NAME("strategy");
1136:
1137: // DBG_VOP_PRINT_FUNCNAME();DBG_VOP_CONT(("\n"));
1138:
1139: hp = VTOH(vp);
1140: if (vp->v_type == VBLK || vp->v_type == VCHR)
1141: panic("hfs_strategy: device vnode passed!");
1142:
1143: /*
1144: * If we don't already know the filesystem relative block number
1145: * then get it using VOP_BMAP(). If VOP_BMAP() returns the block
1146: * number as -1 then we've got a hole in the file. HFS filesystems
1147: * don't allow files with holes, so we shouldn't ever see this.
1148: */
1149: if (bp->b_blkno == bp->b_lblkno) {
1150: if ((retval = VOP_BMAP(vp, bp->b_lblkno, NULL, &bp->b_blkno, NULL))) {
1151: bp->b_error = retval;
1152: bp->b_flags |= B_ERROR;
1153: biodone(bp);
1154: return (retval);
1155: }
1156: if ((long)bp->b_blkno == -1)
1157: clrbuf(bp);
1158: }
1159: if ((long)bp->b_blkno == -1) {
1160: biodone(bp);
1161: return (0);
1162: }
1163:
1164: /* Make sure some over-eager cluster code didn't generate an excessively large read: */
1165: if (bp->b_bcount != hp->h_meta->h_logBlockSize) {
1166: logBlockSize = LogicalBlockSize(hp, bp->b_lblkno);
1167: if ((bp->b_bcount > logBlockSize) && !(can_cluster(logBlockSize))) bp->b_bcount = logBlockSize;
1168: };
1169:
1170: if (bp->b_validend == 0) {
1171: /* Record the exact size of the I/O transfer about to be made: */
1172: DBG_ASSERT(bp->b_validoff == 0);
1173: bp->b_validend = bp->b_bcount;
1174: DBG_ASSERT(bp->b_dirtyoff == 0);
1175: };
1176:
1177: vp = hp->h_devvp;
1178: bp->b_dev = vp->v_rdev;
1179: 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));
1180:
1181: return VOCALL (vp->v_op, VOFFSET(vop_strategy), ap);
1182: }
1183:
1184:
1185: /*
1186: #% reallocblks vp L L L
1187: #
1188: vop_reallocblks {
1189: IN struct vnode *vp;
1190: IN struct cluster_save *buflist;
1191:
1192: */
1193:
1194: int
1195: hfs_reallocblks(ap)
1196: struct vop_reallocblks_args /* {
1197: struct vnode *a_vp;
1198: struct cluster_save *a_buflist;
1199: } */ *ap;
1200: {
1201: DBG_FUNC_NAME("reallocblks");
1202: DBG_VOP_LOCKS_DECL(1);
1203: DBG_VOP_PRINT_FUNCNAME();
1204: DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP_CONT(("\n"));
1205:
1206: DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_POS);
1207:
1208: /* Currently no support for clustering */ /* XXX */
1209: DBG_VOP_LOCKS_TEST(ENOSPC);
1210: return (ENOSPC);
1211: }
1212:
1213:
1214:
1215: /*
1216: #
1217: #% truncate vp L L L
1218: #
1219: vop_truncate {
1220: IN struct vnode *vp;
1221: IN off_t length;
1222: IN int flags; (IO_SYNC)
1223: IN struct ucred *cred;
1224: IN struct proc *p;
1225: };
1226: * Truncate the hfsnode hp to at most length size, freeing (or adding) the
1227: * disk blocks.
1228: */
1229: int hfs_truncate(ap)
1230: struct vop_truncate_args /* {
1231: struct vnode *a_vp;
1232: off_t a_length;
1233: int a_flags;
1234: struct ucred *a_cred;
1235: struct proc *a_p;
1236: } */ *ap;
1237: {
1238: register struct vnode *vp = ap->a_vp;
1239: register struct hfsnode *hp = VTOH(vp);
1240: off_t length = ap->a_length;
1241: long vflags;
1242: struct timeval tv;
1243: int retval;
1244: FCB *fcb;
1245: off_t bytesToAdd;
1246: UInt32 actualBytesAdded;
1247: long devBlockSize = 512;
1248: DBG_FUNC_NAME("truncate");
1249: DBG_VOP_LOCKS_DECL(1);
1250: DBG_VOP_PRINT_FUNCNAME();
1251: DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP_CONT(("\n"));
1252: DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_POS);
1253:
1254: #if DIAGNOSTIC
1255: debug_check_blocksizes(ap->a_vp);
1256: #endif
1257:
1258: if (length < 0) {
1259: DBG_VOP_LOCKS_TEST(EINVAL);
1260: return (EINVAL);
1261: }
1262:
1263: if (length > (off_t)MAXHFSFILESIZE) { /* XXX need to distinguish between hfs and hfs+ */
1264: DBG_VOP_LOCKS_TEST(EFBIG);
1265: return (EFBIG);
1266: }
1267:
1268: if (vp->v_type != VREG && vp->v_type != VLNK) {
1269: DBG_VOP_LOCKS_TEST(EISDIR);
1270: return (EISDIR); /* hfs doesn't support truncating of directories */
1271: }
1272:
1273: fcb = HTOFCB(hp);
1274: tv = time;
1275: retval = E_NONE;
1276:
1277: DBG_VOP(("%s: truncate from Ox%lX to Ox%X bytes\n", funcname, fcb->fcbPLen, (u_int)length));
1278:
1279: /*
1280: * we cannot just check if fcb->fcbEOF == length (as an optimization)
1281: * since there may be extra physical blocks that also need truncation
1282: */
1283:
1284: #if MACH_NBC
1285: retval = mapfs_trunc(vp, (vm_offset_t)length);
1286: if (retval) {
1287: DBG_VOP_LOCKS_TEST(retval);
1288: return (retval);
1289: }
1290: #endif /* MACH_NBC */
1291:
1292: /*
1293: * Lengthen the size of the file. We must ensure that the
1294: * last byte of the file is allocated. Since the smallest
1295: * value of fcbEOF is 0, length will be at least 1.
1296: */
1297: if ((u_long)length > fcb->fcbEOF) {
1298: off_t filePosition;
1299: daddr_t logBlockNo;
1300: long logBlockSize;
1301: long blkOffset;
1302: off_t bytestoclear;
1303: int blockZeroCount;
1304: struct buf *bp=NULL;
1305:
1306: /*
1307: * If we don't have enough physical space then
1308: * we need to extend the physical size.
1309: */
1310: if ((u_long)length > fcb->fcbPLen) {
1311: /* lock extents b-tree (also protects volume bitmap) */
1312: retval = hfs_metafilelocking(HTOHFS(hp), kHFSExtentsFileID, LK_EXCLUSIVE, ap->a_p);
1313: if (retval) goto Err_Exit;
1314:
1315: while (((u_long)length > fcb->fcbPLen) && (retval == E_NONE)) {
1316: /* XXX PPD Allocate only 2GB at a time until ExtendFileC's interface changes... */
1317: bytesToAdd = length - (off_t)fcb->fcbPLen;
1318: if (bytesToAdd > (off_t)0x80000000) bytesToAdd = (off_t)0x80000000;
1319: retval = MacToVFSError(
1320: ExtendFileC (HTOVCB(hp),
1321: fcb,
1322: (u_long)bytesToAdd,
1323: kEFAllMask, /* allocate all requested bytes or none */
1324: &actualBytesAdded));
1325:
1326: if (actualBytesAdded == 0 && retval == E_NONE) {
1327: if ((u_long)length > fcb->fcbPLen)
1328: length = (off_t)fcb->fcbPLen;
1329: break;
1330: }
1331:
1332: /* If we just successfully extended the file by only 2GB, try again (just to be sure): */
1333: if (bytesToAdd == (off_t)0x80000000) continue;
1334: }
1335: (void) hfs_metafilelocking(HTOHFS(hp), kHFSExtentsFileID, LK_RELEASE, ap->a_p);
1336: if (retval) goto Err_Exit;
1337:
1338: DBG_ASSERT((u_long)length <= fcb->fcbPLen);
1339:
1340: UpdateBlockMappingTable(hp);
1341:
1342: #if MACH_NBC
1343: if ((vp->v_type == VREG) && (vp->v_vm_info && !(vp->v_vm_info->mapped))) {
1344: #endif /* MACH_NBC */
1345: vnode_pager_setsize(vp, (u_long)length);
1346: vnode_uncache(vp);
1347: #if MACH_NBC
1348: }
1349: #endif /* !MACH_NBC */
1350: }
1351:
1352: if (! (ap->a_flags & IO_NOZEROFILL)) {
1353: /*
1354: * zero out any new logical space...
1355: */
1356: VOP_DEVBLOCKSIZE(hp->h_devvp, &devBlockSize);
1357:
1358: bytestoclear = length - (off_t)fcb->fcbEOF;
1359: filePosition = (off_t)fcb->fcbEOF;
1360: while (bytestoclear > 0) {
1361: MapFileOffset(hp, filePosition, &logBlockNo, &logBlockSize, &blkOffset);
1362: blockZeroCount = MIN(bytestoclear, logBlockSize - blkOffset);
1363: if ((blkOffset == 0) && (bytestoclear >= logBlockSize)) {
1364: bp = getblk(vp, logBlockNo, logBlockSize, 0, 0);
1365: retval = 0;
1366: if (bp->b_flags & (B_DELWRI | B_DONE | B_CACHE)) {
1367: /*
1368: This buffer is fully pre-read and may already have some modified data in it:
1369: it doesn't need special-casing for write-through.
1370: */
1371: } else {
1372: /* This is an empty buffer just allocated for our use: */
1373:
1374: /* XXX PPD Can't this be skipped if b_blkno != logBlockNo? */
1375:
1376: /* Setting up the physical block number is required
1377: (1) to keep cluster_write informed about which blocks can be clustered, and
1378: (2) to avoid a second call to bmap() in strategy() on the write.
1379: */
1380: if ((retval = VOP_BMAP(vp, logBlockNo, NULL, &bp->b_blkno, NULL))) {
1381: brelse(bp);
1382: break;
1383: };
1384:
1385: bp->b_bcount = 0; /* No valid data in block yet */
1386: bp->b_validend = 0; /* No valid data in block yet */
1387: };
1388: } else {
1389: retval = bread(vp, logBlockNo, logBlockSize, ap->a_cred, &bp);
1390: if (retval) {
1391: brelse(bp);
1392: goto Err_Exit;
1393: }
1394: }
1395:
1396: /*
1397: Fix up the anciliary fields for this buffer, depending on whether they've been initialized yet,
1398: and check to see whether we need to get more data than what was just found in the cache:
1399: */
1400: if (bp->b_validend > 0) {
1401: /*
1402: b_validoff, b_validend, b_dirtyoff, and b_dirtyend are valid for blocks in the cache:
1403: The only blocks that are incomplete (b_validend < fragSize) are blocks that have been
1404: partially written from the start of the buffer:
1405: */
1406: if (bp->b_validend < bp->b_bcount) {
1407: /*
1408: We stumbled onto an incomplete [but successfully acquired] buffer:
1409: try to get the full contents now because it's the only way to get
1410: the data in this logical block...
1411: */
1412: DBG_ASSERT((bp->b_dirtyoff == 0) && (bp->b_validoff == 0) && (bp->b_dirtyend <= bp->b_validend));
1413: /* Incomplete blocks must have only device-block multiples of data... */
1414: DBG_ASSERT((bp->b_validend % devBlockSize) == 0);
1415: bp->b_bcount = bp->b_validend;
1416:
1417: retval = bexpand(bp, logBlockSize, &bp, 0);
1418: };
1419: } else {
1420: /* This buffer was either just allocated or just read in its entirity [see b_bcount]: */
1421: DBG_ASSERT(bp->b_validoff == 0);
1422: bp->b_validend = bp->b_bcount; /* b_bcount > 0 iff block was actually read from disk in bread */
1423: DBG_ASSERT(bp->b_dirtyoff == 0);
1424: DBG_ASSERT(bp->b_dirtyend == 0);
1425: };
1426:
1427: bzero((char *)bp->b_data + blkOffset, blockZeroCount);
1428:
1429: if (blkOffset + blockZeroCount > bp->b_dirtyend) bp->b_dirtyend = blkOffset + blockZeroCount;
1430: if (bp->b_dirtyend > bp->b_validend) bp->b_validend = bp->b_dirtyend;
1431: if (bp->b_validend > bp->b_bcount) bp->b_bcount = bp->b_validend;
1432: DBG_ASSERT(bp->b_bcount % devBlockSize == 0);
1433: DBG_ASSERT(bp->b_bcount == bp->b_validend);
1434: bp->b_flags |= B_DIRTY | B_AGE;
1435: if (ap->a_flags & IO_SYNC) {
1436: bwrite(bp);
1437: } else if (doclusterwrite && can_cluster(logBlockSize)) {
1438: cluster_write(bp, fcb->fcbPLen, devBlockSize);
1439: } else if (logBlockNo % 32) {
1440: bawrite(bp);
1441: } else {
1442: bwrite(bp); /* wait after we issue 32 requests */
1443: };
1444:
1445: bytestoclear -= blockZeroCount;
1446: if (blkOffset > 0)
1447: blkOffset = 0;
1448:
1449: filePosition += blockZeroCount;
1450: }
1451: }
1452:
1453: fcb->fcbEOF = (u_long)length;
1454:
1455: } else { /* Shorten the size of the file */
1456:
1457: if (fcb->fcbEOF > (u_long)length) {
1458: /*
1459: * Any buffers that are past the truncation point need to be
1460: * invalidated (to maintain buffer cache consistency). For
1461: * simplicity, we invalidate all the buffers by calling vinvalbuf.
1462: */
1463: vflags = ((length > 0) ? V_SAVE : 0); /* XXX PPD Should we set SAVE_META? */
1464: retval = vinvalbuf(vp, vflags, ap->a_cred, ap->a_p, 0, 0);
1465: }
1466: /* lock extents b-tree (also protects volume bitmap) */
1467: retval = hfs_metafilelocking(HTOHFS(hp), kHFSExtentsFileID, LK_EXCLUSIVE, ap->a_p);
1468: if (retval) goto Err_Exit;
1469:
1470: retval = MacToVFSError(
1471: TruncateFileC(
1472: HTOVCB(hp),
1473: fcb,
1474: (u_long)length,
1475: false));
1476: (void) hfs_metafilelocking(HTOHFS(hp), kHFSExtentsFileID, LK_RELEASE, ap->a_p);
1477: if (retval) goto Err_Exit;
1478:
1479: fcb->fcbEOF = (u_long)length;
1480: if (fcb->fcbFlags &fcbModifiedMask)
1481: hp->h_meta->h_nodeflags |= IN_MODIFIED;
1482:
1483: UpdateBlockMappingTable(hp);
1484:
1485: #if MACH_NBC
1486: if ((vp->v_type == VREG) && (vp->v_vm_info && !(vp->v_vm_info->mapped))) {
1487: #endif /* MACH_NBC */
1488: vnode_pager_setsize(vp, (u_long)length);
1489: #if MACH_NBC
1490: }
1491: #endif /* MACH_NBC */
1492:
1493: }
1494:
1495: hp->h_meta->h_nodeflags |= IN_CHANGE | IN_UPDATE;
1496: retval = VOP_UPDATE(vp, &tv, &tv, MNT_WAIT);
1497:
1498: Err_Exit:;
1499:
1500: #if DIAGNOSTIC
1501: debug_check_blocksizes(ap->a_vp);
1502: #endif
1503:
1504: DBG_VOP_LOCKS_TEST(retval);
1505: return (retval);
1506: }
1507:
1508:
1509:
1510: /*
1511: #
1512: #% allocate vp L L L
1513: #
1514: vop_allocate {
1515: IN struct vnode *vp;
1516: IN off_t length;
1517: IN int flags;
1518: IN struct ucred *cred;
1519: IN struct proc *p;
1520: };
1521: * allocate the hfsnode hp to at most length size
1522: */
1523: int hfs_allocate(ap)
1524: struct vop_allocate_args /* {
1525: struct vnode *a_vp;
1526: off_t a_length;
1527: u_int32_t a_flags;
1528: off_t *a_bytesallocated;
1529: struct ucred *a_cred;
1530: struct proc *a_p;
1531: } */ *ap;
1532: {
1533: register struct vnode *vp = ap->a_vp;
1534: register struct hfsnode *hp = VTOH(vp);
1535: off_t length = ap->a_length;
1536: long vflags;
1537: u_long startingPEOF;
1538: struct timeval tv;
1539: int retval, retval2;
1540: FCB *fcb;
1541: UInt32 actualBytesAdded;
1542: UInt32 extendFlags =0; /* For call to ExtendFileC */
1543: DBG_FUNC_NAME("allocate");
1544: DBG_VOP_LOCKS_DECL(1);
1545: DBG_VOP_PRINT_FUNCNAME();
1546: DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP_CONT(("\n"));
1547: DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_POS);
1548:
1549: /* Set the number of bytes allocated to 0 so that the caller will know that we
1550: did nothing. ExtendFileC will fill this in for us if we actually allocate space */
1551:
1552: *(ap->a_bytesallocated) = 0;
1553:
1554: /* Now for some error checking */
1555:
1556: if (length < (off_t)0) {
1557: DBG_VOP_LOCKS_TEST(EINVAL);
1558: return (EINVAL);
1559: }
1560:
1561: if (length > (u_int64_t)0x7FFFFFFF) { /* XXX need to distinguish between hfs and hfs+ */
1562: DBG_VOP_LOCKS_TEST(EFBIG);
1563: return (EFBIG);
1564: }
1565:
1566: if (vp->v_type != VREG && vp->v_type != VLNK) {
1567: DBG_VOP_LOCKS_TEST(EISDIR);
1568: return (EISDIR); /* hfs doesn't support truncating of directories */
1569: }
1570:
1571: /* Fill in the flags word for the call to Extend the file */
1572:
1573: if (ap->a_flags & ALLOCATECONTIG) {
1574: extendFlags |= kEFContigMask;
1575: }
1576:
1577: if (ap->a_flags & ALLOCATEALL) {
1578: extendFlags |= kEFAllMask;
1579: }
1580:
1581: fcb = HTOFCB(hp);
1582: tv = time;
1583: retval = E_NONE;
1584: startingPEOF = fcb->fcbPLen;
1585:
1586: if (ap->a_flags & ALLOCATEFROMPEOF) {
1587: length += fcb->fcbPLen;
1588: }
1589:
1590: DBG_VOP(("%s: allocate from Ox%lX to Ox%X bytes\n", funcname, fcb->fcbPLen, (u_int)length));
1591:
1592: /* If no changes are necesary, then we're done */
1593: if (fcb->fcbPLen == (u_long)length)
1594: goto Std_Exit;
1595:
1596: /*
1597: * Lengthen the size of the file. We must ensure that the
1598: * last byte of the file is allocated. Since the smallest
1599: * value of fcbPLen is 0, length will be at least 1.
1600: */
1601: if ((u_long)length > fcb->fcbPLen) {
1602:
1603: /* lock extents b-tree (also protects volume bitmap) */
1604: retval = hfs_metafilelocking(HTOHFS(hp), kHFSExtentsFileID, LK_EXCLUSIVE, ap->a_p);
1605: if (retval) goto Err_Exit;
1606:
1607: retval = MacToVFSError(
1608: ExtendFileC(HTOVCB(hp),
1609: fcb,
1610: (u_long)length - fcb->fcbPLen,
1611: extendFlags,
1612: &actualBytesAdded));
1613:
1614: *(ap->a_bytesallocated) = actualBytesAdded;
1615:
1616: (void) hfs_metafilelocking(HTOHFS(hp), kHFSExtentsFileID, LK_RELEASE, ap->a_p);
1617:
1618: DBG_ASSERT((u_long)length <= fcb->fcbPLen);
1619:
1620: UpdateBlockMappingTable(hp);
1621:
1622: /*
1623: * if we get an error and no changes were made then exit
1624: * otherwise we must do the VOP_UPDATE to reflect the changes
1625: */
1626: if (retval && (startingPEOF == fcb->fcbPLen)) goto Err_Exit;
1627:
1628: } else { /* Shorten the size of the file */
1629:
1630: if (fcb->fcbEOF > (u_long)length) {
1631: /*
1632: * Any buffers that are past the truncation point need to be
1633: * invalidated (to maintain buffer cache consistency). For
1634: * simplicity, we invalidate all the buffers by calling vinvalbuf.
1635: */
1636: vflags = ((length > 0) ? V_SAVE : 0) | V_SAVEMETA;
1637: (void) vinvalbuf(vp, vflags, ap->a_cred, ap->a_p, 0, 0);
1638: }
1639:
1640: /* lock extents b-tree (also protects volume bitmap) */
1641: retval = hfs_metafilelocking(HTOHFS(hp), kHFSExtentsFileID, LK_EXCLUSIVE, ap->a_p);
1642: if (retval) goto Err_Exit;
1643:
1644: retval = MacToVFSError(
1645: TruncateFileC(
1646: HTOVCB(hp),
1647: fcb,
1648: (u_long)length,
1649: false));
1650: (void) hfs_metafilelocking(HTOHFS(hp), kHFSExtentsFileID, LK_RELEASE, ap->a_p);
1651:
1652: /*
1653: * if we get an error and no changes were made then exit
1654: * otherwise we must do the VOP_UPDATE to reflect the changes
1655: */
1656: if (retval && (startingPEOF == fcb->fcbPLen)) goto Err_Exit;
1657: if (fcb->fcbFlags & fcbModifiedMask)
1658: hp->h_meta->h_nodeflags |= IN_MODIFIED;
1659:
1660: DBG_ASSERT((u_long)length <= fcb->fcbPLen) // DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG
1661:
1662: if (fcb->fcbEOF > fcb->fcbPLen) {
1663: fcb->fcbEOF = fcb->fcbPLen;
1664:
1665: UpdateBlockMappingTable(hp);
1666:
1667: #if MACH_NBC
1668: if ((vp->v_type == VREG) && (vp->v_vm_info && !(vp->v_vm_info->mapped))) {
1669: #endif /* MACH_NBC */
1670: vnode_pager_setsize(vp, (u_long)fcb->fcbEOF);
1671: #if MACH_NBC
1672: }
1673: #endif /* MACH_NBC */
1674: }
1675: (void)vnode_uncache(vp);
1676: }
1677:
1678: Std_Exit:
1679: hp->h_meta->h_nodeflags |= IN_CHANGE | IN_UPDATE;
1680: retval2 = VOP_UPDATE(vp, &tv, &tv, MNT_WAIT);
1681:
1682: if (retval == 0) retval = retval2;
1683:
1684: Err_Exit:
1685: DBG_VOP_LOCKS_TEST(retval);
1686: return (retval);
1687: }
1688:
1689:
1690:
1691:
1692: /* Pass-through pagein for HFS filesystem */
1693: int
1694: hfs_pagein(ap)
1695: struct vop_pagein_args /* {
1696: struct vnode *a_vp;
1697: struct uio *a_uio;
1698: int a_ioflag;
1699: struct ucred *a_cred;
1700: } */ *ap;
1701: {
1702: /* pass thru to read */
1703: return (VOP_READ(ap->a_vp, ap->a_uio, ap->a_ioflag, ap->a_cred));
1704: }
1705:
1706: /* Pass-through pageout for HFS filesystem */
1707: int
1708: hfs_pageout(ap)
1709: struct vop_pageout_args /* {
1710: struct vnode *a_vp;
1711: struct uio *a_uio;
1712: int a_ioflag;
1713: struct ucred *a_cred;
1714: } */ *ap;
1715: {
1716: /* pass thru to write */
1717: return (VOP_WRITE(ap->a_vp, ap->a_uio, ap->a_ioflag, ap->a_cred));
1718: }
1719:
1720:
1721:
1722: /*
1723: Try to expand the range of a buffer.
1724:
1725: Possible values for 'flags' are:
1726:
1727: RELEASE_BUFFER - to specify that the expanded buffer need not be held
1728:
1729: Calling bexpand with nbpp == &bp to expand a given buffer is OK;
1730: even on errors, the current buffer (bp) is always brelse-ed!
1731: */
1732: int
1733: bexpand(struct buf *bp, int newsize, struct buf **nbpp, long flags)
1734: {
1735: struct vnode *ovp;
1736: daddr_t olblkno;
1737: struct ucred *ocred;
1738: int odirtyend;
1739: int releasebp = 1;
1740: struct buf *tbp = NULL;
1741: struct buf *nbp = NULL;
1742: int retval = 0;
1743:
1744: DBG_ASSERT(bp != NULL);
1745: if (nbpp == NULL) DBG_ASSERT(flags & RELEASE_BUFFER);
1746:
1747: if (bp->b_validend >= newsize) {
1748: /* Sufficient amount already read or written into buffer: */
1749: nbp = bp; /* nbp is checked against bp in exit path */
1750: retval = 0;
1751: goto stdexit;
1752: };
1753:
1754: if (nbpp) *nbpp = NULL; /* Just to be clean, and to ensure != bp... */
1755:
1756: ovp = bp->b_vp;
1757: olblkno = bp->b_lblkno;
1758: ocred = bp->b_rcred;
1759: odirtyend = bp->b_dirtyend;
1760:
1761: if (bp->b_flags & B_DELWRI) {
1762: /* This buffer holds dirtied data that must be preserved: */
1763: /* XXX PPD 9/28/98 Should assert that b_dirtyoff == 0 here, but not this late in the game... */
1764: tbp = geteblk(odirtyend); /* Grab a new [temporary] buffer big enough to
1765: hold the dirty parts of this buffer for a sec. */
1766: if (tbp == NULL) {
1767: retval = ENOMEM;
1768: goto errexit;
1769: };
1770: bcopy(bp->b_data, tbp->b_data, odirtyend);
1771: };
1772:
1773: /* It's now safe to trash the entire current contents of the buffer */
1774: bp->b_flags |= B_INVAL;
1775: DBG_ASSERT((bp->b_validend == 0) || (bp->b_validend == bp->b_bcount));
1776: brelse(bp);
1777: releasebp = 0;
1778:
1779: // if (((flags & RELEASE_BUFFER) == 0) || (tbp != NULL)) {
1780: if (1) {
1781: retval = bread(ovp, olblkno, newsize, ocred, &nbp); /* Read in the requested data */
1782: if (retval != 0) goto errexit;
1783:
1784: nbp->b_validoff = 0;
1785: nbp->b_validend = newsize;
1786: nbp->b_dirtyoff = 0;
1787: nbp->b_dirtyend = 0;
1788:
1789: if (tbp) {
1790: /* Restore the modified data from the old buffer: */
1791: bcopy(tbp->b_data, nbp->b_data, odirtyend);
1792: nbp->b_dirtyend = odirtyend;
1793: DBG_ASSERT((bp->b_validend == 0) || (bp->b_validend == bp->b_bcount));
1794: bdwrite(nbp); /* Mark this block dirty and move to appropriate buffer */
1795:
1796: if (flags & RELEASE_BUFFER) {
1797: nbp = NULL; /* Forget about the new buffer just written */
1798: } else {
1799: /* Get the buffer back on behalf of our caller, even reading it back in in the
1800: unlikely case it's been flushed and re-used since the bdwrite(), above: */
1801: retval = bread(ovp, olblkno, newsize, ocred, nbpp);
1802: if (retval != 0) goto errexit;
1803: nbp->b_validoff = 0;
1804: nbp->b_validend = newsize;
1805: nbp->b_dirtyoff = 0;
1806: nbp->b_dirtyend = odirtyend;
1807: };
1808:
1809: brelse(tbp);
1810: tbp = NULL; /* Just in case we hit errors later */
1811: };
1812: };
1813: goto stdexit;
1814:
1815: errexit:
1816: if (tbp) brelse(tbp);
1817:
1818: if (releasebp && (bp != nbp)) {
1819: DBG_ASSERT((bp->b_validend == 0) || (bp->b_validend == bp->b_bcount));
1820: brelse(bp);
1821: };
1822:
1823: stdexit:;
1824: if (nbpp) *nbpp = nbp;
1825:
1826: if ((flags & RELEASE_BUFFER) && (nbp != NULL)) {
1827: brelse(nbp);
1828: };
1829:
1830: #if DIAGNOSTIC
1831: debug_check_blocksizes(bp->b_vp);
1832: #endif
1833:
1834: return retval;
1835: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.