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