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