|
|
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: /* Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved */
23: /*
24: * Copyright (c) 1989, 1991, 1993, 1994
25: * The Regents of the University of California. All rights reserved.
26: *
27: * Redistribution and use in source and binary forms, with or without
28: * modification, are permitted provided that the following conditions
29: * are met:
30: * 1. Redistributions of source code must retain the above copyright
31: * notice, this list of conditions and the following disclaimer.
32: * 2. Redistributions in binary form must reproduce the above copyright
33: * notice, this list of conditions and the following disclaimer in the
34: * documentation and/or other materials provided with the distribution.
35: * 3. All advertising materials mentioning features or use of this software
36: * must display the following acknowledgement:
37: * This product includes software developed by the University of
38: * California, Berkeley and its contributors.
39: * 4. Neither the name of the University nor the names of its contributors
40: * may be used to endorse or promote products derived from this software
41: * without specific prior written permission.
42: *
43: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
44: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
45: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
46: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
47: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
48: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
49: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
50: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
51: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
52: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
53: * SUCH DAMAGE.
54: *
55: * @(#)ffs_vfsops.c 8.31 (Berkeley) 5/20/95
56: */
57:
58: #include <rev_endian_fs.h>
59: #include <mach_nbc.h>
60: #include <sys/param.h>
61: #include <sys/systm.h>
62: #include <sys/namei.h>
63: #include <sys/proc.h>
64: #include <sys/kernel.h>
65: #include <sys/vnode.h>
66: #include <sys/socket.h>
67: #include <sys/mount.h>
68: #include <sys/buf.h>
69: #include <sys/mbuf.h>
70: #include <sys/file.h>
71: #ifdef NeXT
72: #include <bsd/dev/disk.h>
73: #else
74: #include <sys/disklabel.h>
75: #endif
76: #include <sys/ioctl.h>
77: #include <sys/errno.h>
78: #include <sys/malloc.h>
79:
80: #include <miscfs/specfs/specdev.h>
81:
82: #include <ufs/ufs/quota.h>
83: #include <ufs/ufs/ufsmount.h>
84: #include <ufs/ufs/inode.h>
85: #include <ufs/ufs/ufs_extern.h>
86:
87: #include <ufs/ffs/fs.h>
88: #include <ufs/ffs/ffs_extern.h>
89: #if REV_ENDIAN_FS
90: #include <ufs/ufs/ufs_byte_order.h>
91: #include <architecture/byte_order.h>
92: #endif /* REV_ENDIAN_FS */
93:
94: int ffs_sbupdate __P((struct ufsmount *, int));
95:
96: struct vfsops ufs_vfsops = {
97: ffs_mount,
98: ufs_start,
99: ffs_unmount,
100: ufs_root,
101: ufs_quotactl,
102: ffs_statfs,
103: ffs_sync,
104: ffs_vget,
105: ffs_fhtovp,
106: ffs_vptofh,
107: ffs_init,
108: ffs_sysctl,
109: };
110:
111: extern u_long nextgennumber;
112:
113: /*
114: * Called by main() when ufs is going to be mounted as root.
115: */
116: ffs_mountroot()
117: {
118: extern struct vnode *rootvp;
119: struct fs *fs;
120: struct mount *mp;
121: struct proc *p = current_proc(); /* XXX */
122: struct ufsmount *ump;
123: u_int size;
124: int error;
125:
126: /*
127: * Get vnode for rootdev.
128: */
129: if (error = bdevvp(rootdev, &rootvp)) {
130: printf("ffs_mountroot: can't setup bdevvp");
131: return (error);
132: }
133: if (error = vfs_rootmountalloc("ufs", "root_device", &mp))
134: return (error);
135: if (error = ffs_mountfs(rootvp, mp, p)) {
136: mp->mnt_vfc->vfc_refcount--;
137: vfs_unbusy(mp, p);
138: _FREE_ZONE(mp, sizeof (struct mount), M_MOUNT);
139: return (error);
140: }
141: simple_lock(&mountlist_slock);
142: CIRCLEQ_INSERT_TAIL(&mountlist, mp, mnt_list);
143: simple_unlock(&mountlist_slock);
144: ump = VFSTOUFS(mp);
145: fs = ump->um_fs;
146: (void) copystr(mp->mnt_stat.f_mntonname, fs->fs_fsmnt, MNAMELEN - 1, 0);
147: (void)ffs_statfs(mp, &mp->mnt_stat, p);
148: vfs_unbusy(mp, p);
149: inittodr(fs->fs_time);
150: return (0);
151: }
152:
153: /*
154: * VFS Operations.
155: *
156: * mount system call
157: */
158: int
159: ffs_mount(mp, path, data, ndp, p)
160: register struct mount *mp;
161: char *path;
162: caddr_t data;
163: struct nameidata *ndp;
164: struct proc *p;
165: {
166: struct vnode *devvp;
167: struct ufs_args args;
168: struct ufsmount *ump;
169: register struct fs *fs;
170: u_int size;
171: int error, flags;
172: mode_t accessmode;
173:
174: if (error = copyin(data, (caddr_t)&args, sizeof (struct ufs_args)))
175: return (error);
176: /*
177: * If updating, check whether changing from read-only to
178: * read/write; if there is no device name, that's all we do.
179: */
180: if (mp->mnt_flag & MNT_UPDATE) {
181: ump = VFSTOUFS(mp);
182: fs = ump->um_fs;
183: if (fs->fs_ronly == 0 && (mp->mnt_flag & MNT_RDONLY)) {
184: flags = WRITECLOSE;
185: if (mp->mnt_flag & MNT_FORCE)
186: flags |= FORCECLOSE;
187: if (error = ffs_flushfiles(mp, flags, p))
188: return (error);
189: fs->fs_clean = 1;
190: fs->fs_ronly = 1;
191: if (error = ffs_sbupdate(ump, MNT_WAIT)) {
192: fs->fs_clean = 0;
193: fs->fs_ronly = 0;
194: return (error);
195: }
196: }
197: if ((mp->mnt_flag & MNT_RELOAD) &&
198: (error = ffs_reload(mp, ndp->ni_cnd.cn_cred, p)))
199: return (error);
200: if (fs->fs_ronly && (mp->mnt_flag & MNT_WANTRDWR)) {
201: /*
202: * If upgrade to read-write by non-root, then verify
203: * that user has necessary permissions on the device.
204: */
205: if (p->p_ucred->cr_uid != 0) {
206: devvp = ump->um_devvp;
207: vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY, p);
208: if (error = VOP_ACCESS(devvp, VREAD | VWRITE,
209: p->p_ucred, p)) {
210: VOP_UNLOCK(devvp, 0, p);
211: return (error);
212: }
213: VOP_UNLOCK(devvp, 0, p);
214: }
215: fs->fs_ronly = 0;
216: fs->fs_clean = 0;
217: (void) ffs_sbupdate(ump, MNT_WAIT);
218: }
219: if (args.fspec == 0) {
220: /*
221: * Process export requests.
222: */
223: return (vfs_export(mp, &ump->um_export, &args.export));
224: }
225: }
226: /*
227: * Not an update, or updating the name: look up the name
228: * and verify that it refers to a sensible block device.
229: */
230: NDINIT(ndp, LOOKUP, FOLLOW, UIO_USERSPACE, args.fspec, p);
231: if (error = namei(ndp))
232: return (error);
233: devvp = ndp->ni_vp;
234:
235: if (devvp->v_type != VBLK) {
236: vrele(devvp);
237: return (ENOTBLK);
238: }
239: if (major(devvp->v_rdev) >= nblkdev) {
240: vrele(devvp);
241: return (ENXIO);
242: }
243: /*
244: * If mount by non-root, then verify that user has necessary
245: * permissions on the device.
246: */
247: if (p->p_ucred->cr_uid != 0) {
248: accessmode = VREAD;
249: if ((mp->mnt_flag & MNT_RDONLY) == 0)
250: accessmode |= VWRITE;
251: vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY, p);
252: if (error = VOP_ACCESS(devvp, accessmode, p->p_ucred, p)) {
253: vput(devvp);
254: return (error);
255: }
256: VOP_UNLOCK(devvp, 0, p);
257: }
258: if ((mp->mnt_flag & MNT_UPDATE) == 0)
259: error = ffs_mountfs(devvp, mp, p);
260: else {
261: if (devvp != ump->um_devvp)
262: error = EINVAL; /* needs translation */
263: else
264: vrele(devvp);
265: }
266: if (error) {
267: vrele(devvp);
268: return (error);
269: }
270: ump = VFSTOUFS(mp);
271: fs = ump->um_fs;
272: (void) copyinstr(path, fs->fs_fsmnt, sizeof(fs->fs_fsmnt) - 1, &size);
273: bzero(fs->fs_fsmnt + size, sizeof(fs->fs_fsmnt) - size);
274: bcopy((caddr_t)fs->fs_fsmnt, (caddr_t)mp->mnt_stat.f_mntonname,
275: MNAMELEN);
276: (void) copyinstr(args.fspec, mp->mnt_stat.f_mntfromname, MNAMELEN - 1,
277: &size);
278: bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size);
279: (void)ffs_statfs(mp, &mp->mnt_stat, p);
280: return (0);
281: }
282:
283: /*
284: * Reload all incore data for a filesystem (used after running fsck on
285: * the root filesystem and finding things to fix). The filesystem must
286: * be mounted read-only.
287: *
288: * Things to do to update the mount:
289: * 1) invalidate all cached meta-data.
290: * 2) re-read superblock from disk.
291: * 3) re-read summary information from disk.
292: * 4) invalidate all inactive vnodes.
293: * 5) invalidate all cached file data.
294: * 6) re-read inode data for all active vnodes.
295: */
296: ffs_reload(mountp, cred, p)
297: register struct mount *mountp;
298: struct ucred *cred;
299: struct proc *p;
300: {
301: register struct vnode *vp, *nvp, *devvp;
302: struct inode *ip;
303: struct csum *space;
304: struct buf *bp;
305: struct fs *fs, *newfs;
306: #ifndef NeXT
307: struct partinfo dpart;
308: #endif /* NeXT */
309: int i, blks, size, error;
310: int32_t *lp;
311: #if REV_ENDIAN_FS
312: int rev_endian = (mountp->mnt_flag & MNT_REVEND);
313: #endif /* REV_ENDIAN_FS */
314:
315: if ((mountp->mnt_flag & MNT_RDONLY) == 0)
316: return (EINVAL);
317: /*
318: * Step 1: invalidate all cached meta-data.
319: */
320: devvp = VFSTOUFS(mountp)->um_devvp;
321: if (vinvalbuf(devvp, 0, cred, p, 0, 0))
322: panic("ffs_reload: dirty1");
323: /*
324: * Step 2: re-read superblock from disk.
325: */
326: #ifdef NeXT
327: VOP_DEVBLOCKSIZE(devvp,&size);
328: #else
329: if (VOP_IOCTL(devvp, DIOCGPART, (caddr_t)&dpart, FREAD, NOCRED, p) != 0)
330: size = DEV_BSIZE;
331: else
332: size = dpart.disklab->d_secsize;
333: #endif
334: if (error = bread(devvp, (ufs_daddr_t)(SBOFF/size), SBSIZE, NOCRED,&bp))
335: return (error);
336: newfs = (struct fs *)bp->b_data;
337: #if REV_ENDIAN_FS
338: if (rev_endian) {
339: byte_swap_sbin(newfs);
340: }
341: #endif /* REV_ENDIAN_FS */
342: if (newfs->fs_magic != FS_MAGIC || newfs->fs_bsize > MAXBSIZE ||
343: newfs->fs_bsize < sizeof(struct fs)) {
344: #if REV_ENDIAN_FS
345: if (rev_endian)
346: byte_swap_sbout(newfs);
347: #endif /* REV_ENDIAN_FS */
348:
349: brelse(bp);
350: return (EIO); /* XXX needs translation */
351: }
352: fs = VFSTOUFS(mountp)->um_fs;
353: /*
354: * Copy pointer fields back into superblock before copying in XXX
355: * new superblock. These should really be in the ufsmount. XXX
356: * Note that important parameters (eg fs_ncg) are unchanged.
357: */
358: bcopy(&fs->fs_csp[0], &newfs->fs_csp[0], sizeof(fs->fs_csp));
359: newfs->fs_maxcluster = fs->fs_maxcluster;
360: bcopy(newfs, fs, (u_int)fs->fs_sbsize);
361: if (fs->fs_sbsize < SBSIZE)
362: bp->b_flags |= B_INVAL;
363: #if REV_ENDIAN_FS
364: if (rev_endian)
365: byte_swap_sbout(newfs);
366: #endif /* REV_ENDIAN_FS */
367: brelse(bp);
368: mountp->mnt_maxsymlinklen = fs->fs_maxsymlinklen;
369: ffs_oldfscompat(fs);
370: /*
371: * Step 3: re-read summary information from disk.
372: */
373: blks = howmany(fs->fs_cssize, fs->fs_fsize);
374: space = fs->fs_csp[0];
375: for (i = 0; i < blks; i += fs->fs_frag) {
376: size = fs->fs_bsize;
377: if (i + fs->fs_frag > blks)
378: size = (blks - i) * fs->fs_fsize;
379: if (error = bread(devvp, fsbtodb(fs, fs->fs_csaddr + i), size,
380: NOCRED, &bp))
381: return (error);
382: #if REV_ENDIAN_FS
383: if (rev_endian) {
384: /* csum swaps */
385: byte_swap_ints((int *)bp->b_data, size / sizeof(int));
386: }
387: #endif /* REV_ENDIAN_FS */
388: bcopy(bp->b_data, fs->fs_csp[fragstoblks(fs, i)], (u_int)size);
389: #if REV_ENDIAN_FS
390: if (rev_endian) {
391: /* csum swaps */
392: byte_swap_ints((int *)bp->b_data, size / sizeof(int));
393: }
394: #endif /* REV_ENDIAN_FS */
395: brelse(bp);
396: }
397: /*
398: * We no longer know anything about clusters per cylinder group.
399: */
400: if (fs->fs_contigsumsize > 0) {
401: lp = fs->fs_maxcluster;
402: for (i = 0; i < fs->fs_ncg; i++)
403: *lp++ = fs->fs_contigsumsize;
404: }
405:
406: loop:
407: simple_lock(&mntvnode_slock);
408: for (vp = mountp->mnt_vnodelist.lh_first; vp != NULL; vp = nvp) {
409: if (vp->v_mount != mountp) {
410: simple_unlock(&mntvnode_slock);
411: goto loop;
412: }
413: nvp = vp->v_mntvnodes.le_next;
414: /*
415: * Step 4: invalidate all inactive vnodes.
416: */
417: if (vrecycle(vp, &mntvnode_slock, p))
418: goto loop;
419: /*
420: * Step 5: invalidate all cached file data.
421: */
422: simple_lock(&vp->v_interlock);
423: simple_unlock(&mntvnode_slock);
424: if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK, p)) {
425: goto loop;
426: }
427: if (vinvalbuf(vp, 0, cred, p, 0, 0))
428: panic("ffs_reload: dirty2");
429: /*
430: * Step 6: re-read inode data for all active vnodes.
431: */
432: ip = VTOI(vp);
433: if (error =
434: bread(devvp, fsbtodb(fs, ino_to_fsba(fs, ip->i_number)),
435: (int)fs->fs_bsize, NOCRED, &bp)) {
436: vput(vp);
437: return (error);
438: }
439: #if REV_ENDIAN_FS
440: if (rev_endian) {
441: byte_swap_inode_in(((struct dinode *)bp->b_data +
442: ino_to_fsbo(fs, ip->i_number)), ip);
443: } else {
444: #endif /* REV_ENDIAN_FS */
445: ip->i_din = *((struct dinode *)bp->b_data +
446: ino_to_fsbo(fs, ip->i_number));
447: #if REV_ENDIAN_FS
448: }
449: #endif /* REV_ENDIAN_FS */
450: brelse(bp);
451: vput(vp);
452: simple_lock(&mntvnode_slock);
453: }
454: simple_unlock(&mntvnode_slock);
455: return (0);
456: }
457:
458: /*
459: * Common code for mount and mountroot
460: */
461: int
462: ffs_mountfs(devvp, mp, p)
463: register struct vnode *devvp;
464: struct mount *mp;
465: struct proc *p;
466: {
467: register struct ufsmount *ump;
468: struct buf *bp;
469: register struct fs *fs;
470: dev_t dev;
471: #ifdef NeXT
472: struct buf *cgbp;
473: struct cg *cgp;
474: int32_t clustersumoff;
475: #else
476: struct partinfo dpart;
477: #endif /* NeXT */
478: caddr_t base, space;
479: int error, i, blks, size, ronly;
480: int32_t *lp;
481: struct ucred *cred;
482: extern struct vnode *rootvp;
483: u_int64_t maxfilesize; /* XXX */
484: u_int dbsize = DEV_BSIZE;
485: #if REV_ENDIAN_FS
486: int rev_endian=0;
487: #endif /* REV_ENDIAN_FS */
488: dev = devvp->v_rdev;
489: cred = p ? p->p_ucred : NOCRED;
490: /*
491: * Disallow multiple mounts of the same device.
492: * Disallow mounting of a device that is currently in use
493: * (except for root, which might share swap device for miniroot).
494: * Flush out any old buffers remaining from a previous use.
495: */
496: if (error = vfs_mountedon(devvp))
497: return (error);
498: if (vcount(devvp) > 1 && devvp != rootvp)
499: return (EBUSY);
500: if (error = vinvalbuf(devvp, V_SAVE, cred, p, 0, 0))
501: return (error);
502:
503: ronly = (mp->mnt_flag & MNT_RDONLY) != 0;
504: if (error = VOP_OPEN(devvp, ronly ? FREAD : FREAD|FWRITE, FSCRED, p))
505: return (error);
506: #ifdef NeXT
507: VOP_DEVBLOCKSIZE(devvp,&size);
508: #else
509: if (VOP_IOCTL(devvp, DIOCGPART, (caddr_t)&dpart, FREAD, NOCRED, p) != 0)
510: size = DEV_BSIZE;
511: else
512: size = dpart.disklab->d_secsize;
513: #endif
514:
515: bp = NULL;
516: ump = NULL;
517: if (error = bread(devvp, (ufs_daddr_t)(SBOFF/size), SBSIZE, cred, &bp))
518: goto out;
519: fs = (struct fs *)bp->b_data;
520: #if REV_ENDIAN_FS
521: if (fs->fs_magic != FS_MAGIC || fs->fs_bsize > MAXBSIZE ||
522: fs->fs_bsize < sizeof(struct fs)) {
523: int magic = fs->fs_magic;
524:
525: byte_swap_ints(&magic, 1);
526: if (magic != FS_MAGIC) {
527: error = EINVAL;
528: goto out;
529: }
530: byte_swap_sbin(fs);
531: if (fs->fs_magic != FS_MAGIC || fs->fs_bsize > MAXBSIZE ||
532: fs->fs_bsize < sizeof(struct fs)) {
533: byte_swap_sbout(fs);
534: error = EINVAL; /* XXX needs translation */
535: goto out;
536: }
537: rev_endian=1;
538: }
539: #endif /* REV_ENDIAN_FS */
540: if (fs->fs_magic != FS_MAGIC || fs->fs_bsize > MAXBSIZE ||
541: fs->fs_bsize < sizeof(struct fs)) {
542: #if REV_ENDIAN_FS
543: if (rev_endian)
544: byte_swap_sbout(fs);
545: #endif /* REV_ENDIAN_FS */
546: error = EINVAL; /* XXX needs translation */
547: goto out;
548: }
549:
550:
551: /* Let's figure out the devblock size the file system is with */
552: /* the device block size = fragment size / number of sectors per frag */
553:
554: dbsize = fs->fs_fsize / NSPF(fs);
555: if(dbsize <= 0 ) {
556: kprintf("device blocksize computaion failed\n");
557: } else {
558: if (VOP_IOCTL(devvp, DKIOCSETBLOCKSIZE, &dbsize, FWRITE, NOCRED,
559: p) != 0) {
560: kprintf("failed to set device blocksize\n");
561: }
562: /* force the specfs to reread blocksize from size() */
563: set_fsblocksize(devvp);
564: }
565:
566:
567: /* XXX updating 4.2 FFS superblocks trashes rotational layout tables */
568: if (fs->fs_postblformat == FS_42POSTBLFMT && !ronly) {
569: #if REV_ENDIAN_FS
570: if (rev_endian)
571: byte_swap_sbout(fs);
572: #endif /* REV_ENDIAN_FS */
573: error = EROFS; /* needs translation */
574: goto out;
575: }
576:
577: #ifdef NeXT
578: /* If we are not mounting read only, then check for overlap
579: * condition in cylinder group's free block map.
580: * If overlap exists, then force this into a read only mount
581: * to avoid further corruption. PR#2216969
582: */
583: if (ronly == 0){
584: if (error = bread (devvp, fsbtodb(fs, cgtod(fs, 0)),
585: (int)fs->fs_cgsize, NOCRED, &cgbp)) {
586: brelse(cgbp);
587: goto out;
588: }
589: cgp = (struct cg *)cgbp->b_data;
590: #if REV_ENDIAN_FS
591: if (rev_endian)
592: byte_swap_cgin(cgp,fs);
593: #endif /* REV_ENDIAN_FS */
594: if (!cg_chkmagic(cgp)){
595: #if REV_ENDIAN_FS
596: if (rev_endian)
597: byte_swap_cgout(cgp,fs);
598: #endif /* REV_ENDIAN_FS */
599: brelse(cgbp);
600: goto out;
601: }
602: if (cgp->cg_clustersumoff != 0) {
603: /* Check for overlap */
604: clustersumoff = cgp->cg_freeoff +
605: howmany(fs->fs_cpg * fs->fs_spc / NSPF(fs), NBBY);
606: clustersumoff = roundup(clustersumoff, sizeof(long));
607: if (cgp->cg_clustersumoff < clustersumoff) {
608: /* Overlap exists */
609: mp->mnt_flag |= MNT_RDONLY;
610: ronly = 1;
611: }
612: }
613: #if REV_ENDIAN_FS
614: if (rev_endian)
615: byte_swap_cgout(cgp,fs);
616: #endif /* REV_ENDIAN_FS */
617: brelse(cgbp);
618: }
619: #endif /* NeXT */
620:
621: ump = _MALLOC(sizeof *ump, M_UFSMNT, M_WAITOK);
622: bzero((caddr_t)ump, sizeof *ump);
623: ump->um_fs = _MALLOC((u_long)fs->fs_sbsize, M_UFSMNT,
624: M_WAITOK);
625: bcopy(bp->b_data, ump->um_fs, (u_int)fs->fs_sbsize);
626: if (fs->fs_sbsize < SBSIZE)
627: bp->b_flags |= B_INVAL;
628: #if REV_ENDIAN_FS
629: if (rev_endian)
630: byte_swap_sbout(fs);
631: #endif /* REV_ENDIAN_FS */
632: brelse(bp);
633: bp = NULL;
634: fs = ump->um_fs;
635: fs->fs_ronly = ronly;
636: size = fs->fs_cssize;
637: blks = howmany(size, fs->fs_fsize);
638: if (fs->fs_contigsumsize > 0)
639: size += fs->fs_ncg * sizeof(int32_t);
640: base = space = _MALLOC((u_long)size, M_UFSMNT, M_WAITOK);
641: base = space;
642: for (i = 0; i < blks; i += fs->fs_frag) {
643: size = fs->fs_bsize;
644: if (i + fs->fs_frag > blks)
645: size = (blks - i) * fs->fs_fsize;
646: if (error = bread(devvp, fsbtodb(fs, fs->fs_csaddr + i), size,
647: cred, &bp)) {
648: _FREE(base, M_UFSMNT);
649: goto out;
650: }
651: bcopy(bp->b_data, space, (u_int)size);
652: #if REV_ENDIAN_FS
653: if (rev_endian)
654: byte_swap_ints((int *) space, size / sizeof(int));
655: #endif /* REV_ENDIAN_FS */
656: fs->fs_csp[fragstoblks(fs, i)] = (struct csum *)space;
657: space += size;
658: brelse(bp);
659: bp = NULL;
660: }
661: if (fs->fs_contigsumsize > 0) {
662: fs->fs_maxcluster = lp = (int32_t *)space;
663: for (i = 0; i < fs->fs_ncg; i++)
664: *lp++ = fs->fs_contigsumsize;
665: }
666: mp->mnt_data = (qaddr_t)ump;
667: mp->mnt_stat.f_fsid.val[0] = (long)dev;
668: mp->mnt_stat.f_fsid.val[1] = mp->mnt_vfc->vfc_typenum;
669: #ifdef NeXT
670: #warning hardcoded max symlen
671: mp->mnt_maxsymlinklen = 60;
672: #else
673: mp->mnt_maxsymlinklen = fs->fs_maxsymlinklen;
674: #endif
675: #if REV_ENDIAN_FS
676: if (rev_endian)
677: mp->mnt_flag |= MNT_REVEND;
678: #endif /* REV_ENDIAN_FS */
679: ump->um_mountp = mp;
680: ump->um_dev = dev;
681: ump->um_devvp = devvp;
682: ump->um_nindir = fs->fs_nindir;
683: ump->um_bptrtodb = fs->fs_fsbtodb;
684: ump->um_seqinc = fs->fs_frag;
685: for (i = 0; i < MAXQUOTAS; i++)
686: ump->um_quotas[i] = NULLVP;
687: devvp->v_specflags |= SI_MOUNTEDON;
688: ffs_oldfscompat(fs);
689: ump->um_savedmaxfilesize = fs->fs_maxfilesize; /* XXX */
690: #ifdef NeXT
691: maxfilesize = (u_int64_t)0x100000000; /*4giga */
692: #else
693: maxfilesize = (u_int64_t)0x40000000 * fs->fs_bsize - 1; /* XXX */
694: #endif /* NeXT */
695: if (fs->fs_maxfilesize > maxfilesize) /* XXX */
696: fs->fs_maxfilesize = maxfilesize; /* XXX */
697: if (ronly == 0) {
698: fs->fs_clean = 0;
699: (void) ffs_sbupdate(ump, MNT_WAIT);
700: }
701: return (0);
702: out:
703: if (bp)
704: brelse(bp);
705: (void)VOP_CLOSE(devvp, ronly ? FREAD : FREAD|FWRITE, cred, p);
706: if (ump) {
707: _FREE(ump->um_fs, M_UFSMNT);
708: _FREE(ump, M_UFSMNT);
709: mp->mnt_data = (qaddr_t)0;
710: }
711: return (error);
712: }
713:
714: /*
715: * Sanity checks for old file systems.
716: *
717: * XXX - goes away some day.
718: */
719: ffs_oldfscompat(fs)
720: struct fs *fs;
721: {
722: int i;
723:
724: fs->fs_npsect = max(fs->fs_npsect, fs->fs_nsect); /* XXX */
725: fs->fs_interleave = max(fs->fs_interleave, 1); /* XXX */
726: if (fs->fs_postblformat == FS_42POSTBLFMT) /* XXX */
727: fs->fs_nrpos = 8; /* XXX */
728: if (fs->fs_inodefmt < FS_44INODEFMT) { /* XXX */
729: u_int64_t sizepb = fs->fs_bsize; /* XXX */
730: /* XXX */
731: fs->fs_maxfilesize = fs->fs_bsize * NDADDR - 1; /* XXX */
732: for (i = 0; i < NIADDR; i++) { /* XXX */
733: sizepb *= NINDIR(fs); /* XXX */
734: fs->fs_maxfilesize += sizepb; /* XXX */
735: } /* XXX */
736: fs->fs_qbmask = ~fs->fs_bmask; /* XXX */
737: fs->fs_qfmask = ~fs->fs_fmask; /* XXX */
738: } /* XXX */
739: return (0);
740: }
741:
742: /*
743: * unmount system call
744: */
745: int
746: ffs_unmount(mp, mntflags, p)
747: struct mount *mp;
748: int mntflags;
749: struct proc *p;
750: {
751: register struct ufsmount *ump;
752: register struct fs *fs;
753: int error, flags;
754: flags = 0;
755: if (mntflags & MNT_FORCE)
756: flags |= FORCECLOSE;
757: if (error = ffs_flushfiles(mp, flags, p))
758: return (error);
759: ump = VFSTOUFS(mp);
760: fs = ump->um_fs;
761: if (fs->fs_ronly == 0) {
762: fs->fs_clean = 1;
763: if (error = ffs_sbupdate(ump, MNT_WAIT)) {
764: fs->fs_clean = 0;
765: #ifndef NeXT
766: /* we can atleast cleanup ; as the media could be WP */
767: /* & during mount, we do not check for write failures */
768: /* FIXME LATER : the Correct fix would be to have */
769: /* mount detect the WP media and downgrade to readonly mount */
770: /* For now, here it is */
771: return (error);
772: #endif
773: }
774: }
775: ump->um_devvp->v_specflags &= ~SI_MOUNTEDON;
776: error = VOP_CLOSE(ump->um_devvp, fs->fs_ronly ? FREAD : FREAD|FWRITE,
777: NOCRED, p);
778: vrele(ump->um_devvp);
779:
780: _FREE(fs->fs_csp[0], M_UFSMNT);
781: _FREE(fs, M_UFSMNT);
782: _FREE(ump, M_UFSMNT);
783: mp->mnt_data = (qaddr_t)0;
784: #if REV_ENDIAN_FS
785: mp->mnt_flag &= ~MNT_REVEND;
786: #endif /* REV_ENDIAN_FS */
787: return (error);
788: }
789:
790: /*
791: * Flush out all the files in a filesystem.
792: */
793: ffs_flushfiles(mp, flags, p)
794: register struct mount *mp;
795: int flags;
796: struct proc *p;
797: {
798: register struct ufsmount *ump;
799: int i, error;
800:
801: ump = VFSTOUFS(mp);
802: #if QUOTA
803: if (mp->mnt_flag & MNT_QUOTA) {
804: if (error = vflush(mp, NULLVP, SKIPSYSTEM|flags))
805: return (error);
806: for (i = 0; i < MAXQUOTAS; i++) {
807: if (ump->um_quotas[i] == NULLVP)
808: continue;
809: quotaoff(p, mp, i);
810: }
811: /*
812: * Here we fall through to vflush again to ensure
813: * that we have gotten rid of all the system vnodes.
814: */
815: }
816: #endif
817: error = vflush(mp, NULLVP, flags);
818: return (error);
819: }
820:
821: /*
822: * Get file system statistics.
823: */
824: int
825: ffs_statfs(mp, sbp, p)
826: struct mount *mp;
827: register struct statfs *sbp;
828: struct proc *p;
829: {
830: register struct ufsmount *ump;
831: register struct fs *fs;
832:
833: ump = VFSTOUFS(mp);
834: fs = ump->um_fs;
835: if (fs->fs_magic != FS_MAGIC)
836: panic("ffs_statfs");
837: sbp->f_bsize = fs->fs_fsize;
838: sbp->f_iosize = fs->fs_bsize;
839: sbp->f_blocks = fs->fs_dsize;
840: sbp->f_bfree = fs->fs_cstotal.cs_nbfree * fs->fs_frag +
841: fs->fs_cstotal.cs_nffree;
842: sbp->f_bavail = freespace(fs, fs->fs_minfree);
843: sbp->f_files = fs->fs_ncg * fs->fs_ipg - ROOTINO;
844: sbp->f_ffree = fs->fs_cstotal.cs_nifree;
845: if (sbp != &mp->mnt_stat) {
846: sbp->f_type = mp->mnt_vfc->vfc_typenum;
847: bcopy((caddr_t)mp->mnt_stat.f_mntonname,
848: (caddr_t)&sbp->f_mntonname[0], MNAMELEN);
849: bcopy((caddr_t)mp->mnt_stat.f_mntfromname,
850: (caddr_t)&sbp->f_mntfromname[0], MNAMELEN);
851: }
852: return (0);
853: }
854:
855: /*
856: * Go through the disk queues to initiate sandbagged IO;
857: * go through the inodes to write those that have been modified;
858: * initiate the writing of the super block if it has been modified.
859: *
860: * Note: we are always called with the filesystem marked `MPBUSY'.
861: */
862: int
863: ffs_sync(mp, waitfor, cred, p)
864: struct mount *mp;
865: int waitfor;
866: struct ucred *cred;
867: struct proc *p;
868: {
869: struct vnode *nvp, *vp;
870: struct inode *ip;
871: struct ufsmount *ump = VFSTOUFS(mp);
872: struct fs *fs;
873: int error, allerror = 0;
874:
875: fs = ump->um_fs;
876: if (fs->fs_fmod != 0 && fs->fs_ronly != 0) { /* XXX */
877: printf("fs = %s\n", fs->fs_fsmnt);
878: panic("update: rofs mod");
879: }
880: /*
881: * Write back each (modified) inode.
882: */
883: simple_lock(&mntvnode_slock);
884: loop:
885: for (vp = mp->mnt_vnodelist.lh_first;
886: vp != NULL;
887: vp = nvp) {
888: /*
889: * If the vnode that we are about to sync is no longer
890: * associated with this mount point, start over.
891: */
892: if (vp->v_mount != mp)
893: goto loop;
894: simple_lock(&vp->v_interlock);
895: nvp = vp->v_mntvnodes.le_next;
896: ip = VTOI(vp);
897: if ((vp->v_type == VNON) || (ip->i_flag &
898: (IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE)) == 0 &&
899: vp->v_dirtyblkhd.lh_first == NULL) {
900: simple_unlock(&vp->v_interlock);
901: continue;
902: }
903: simple_unlock(&mntvnode_slock);
904: error = vget(vp, LK_EXCLUSIVE | LK_NOWAIT | LK_INTERLOCK, p);
905: if (error) {
906: simple_lock(&mntvnode_slock);
907: if (error == ENOENT)
908: goto loop;
909: continue;
910: }
911: if (error = VOP_FSYNC(vp, cred, waitfor, p))
912: allerror = error;
913: VOP_UNLOCK(vp, 0, p);
914: vrele(vp);
915: simple_lock(&mntvnode_slock);
916: }
917: simple_unlock(&mntvnode_slock);
918: /*
919: * Force stale file system control information to be flushed.
920: */
921: if (error = VOP_FSYNC(ump->um_devvp, cred, waitfor, p))
922: allerror = error;
923: #if QUOTA
924: qsync(mp);
925: #endif
926: /*
927: * Write back modified superblock.
928: */
929: if (fs->fs_fmod != 0) {
930: fs->fs_fmod = 0;
931: fs->fs_time = time.tv_sec;
932: if (error = ffs_sbupdate(ump, waitfor))
933: allerror = error;
934: }
935: return (allerror);
936: }
937:
938: /*
939: * Look up a FFS dinode number to find its incore vnode, otherwise read it
940: * in from disk. If it is in core, wait for the lock bit to clear, then
941: * return the inode locked. Detection and handling of mount points must be
942: * done by the calling routine.
943: */
944: int
945: ffs_vget(mp, ino, vpp)
946: struct mount *mp;
947: ino_t ino;
948: struct vnode **vpp;
949: {
950: struct proc *p = current_proc(); /* XXX */
951: struct fs *fs;
952: struct inode *ip;
953: struct ufsmount *ump;
954: struct buf *bp;
955: struct vnode *vp;
956: dev_t dev;
957: int i, type, error;
958:
959: ump = VFSTOUFS(mp);
960: dev = ump->um_dev;
961: if ((*vpp = ufs_ihashget(dev, ino)) != NULL) {
962: #if MACH_NBC
963: vp = *vpp;
964: if ((vp->v_type == VREG) && !(vp->v_vm_info)){
965: vm_info_init(vp);
966: }
967: #endif /* MACH_NBC */
968: return (0);
969: }
970: /* Allocate a new vnode/inode. */
971: type = ump->um_devvp->v_tag == VT_MFS ? M_MFSNODE : M_FFSNODE; /* XXX */
972: MALLOC_ZONE(ip, struct inode *, sizeof(struct inode), type, M_WAITOK);
973: if (error = getnewvnode(VT_UFS, mp, ffs_vnodeop_p, &vp)) {
974: FREE_ZONE(ip, sizeof(struct inode), type);
975: *vpp = NULL;
976: return (error);
977: }
978: bzero((caddr_t)ip, sizeof(struct inode));
979: lockinit(&ip->i_lock, PINOD, "inode", 0, 0);
980: vp->v_data = ip;
981: ip->i_vnode = vp;
982: ip->i_fs = fs = ump->um_fs;
983: ip->i_dev = dev;
984: ip->i_number = ino;
985: #if QUOTA
986: for (i = 0; i < MAXQUOTAS; i++)
987: ip->i_dquot[i] = NODQUOT;
988: #endif
989: /*
990: * Put it onto its hash chain and lock it so that other requests for
991: * this inode will block if they arrive while we are sleeping waiting
992: * for old data structures to be purged or for the contents of the
993: * disk portion of this inode to be read.
994: */
995: ufs_ihashins(ip);
996:
997: /* Read in the disk contents for the inode, copy into the inode. */
998: if (error = bread(ump->um_devvp, fsbtodb(fs, ino_to_fsba(fs, ino)),
999: (int)fs->fs_bsize, NOCRED, &bp)) {
1000: /*
1001: * The inode does not contain anything useful, so it would
1002: * be misleading to leave it on its hash chain. With mode
1003: * still zero, it will be unlinked and returned to the free
1004: * list by vput().
1005: */
1006: vput(vp);
1007: brelse(bp);
1008: *vpp = NULL;
1009: return (error);
1010: }
1011: #if REV_ENDIAN_FS
1012: if (mp->mnt_flag & MNT_REVEND) {
1013: byte_swap_inode_in(((struct dinode *)bp->b_data + ino_to_fsbo(fs, ino)),ip);
1014: } else {
1015: #endif /* REV_ENDIAN_FS */
1016: ip->i_din = *((struct dinode *)bp->b_data + ino_to_fsbo(fs, ino));
1017: #if REV_ENDIAN_FS
1018: }
1019: #endif /* REV_ENDIAN_FS */
1020: brelse(bp);
1021:
1022: /*
1023: * Initialize the vnode from the inode, check for aliases.
1024: * Note that the underlying vnode may have changed.
1025: */
1026: if (error = ufs_vinit(mp, ffs_specop_p, FFS_FIFOOPS, &vp)) {
1027: vput(vp);
1028: *vpp = NULL;
1029: return (error);
1030: }
1031: /*
1032: * Finish inode initialization now that aliasing has been resolved.
1033: */
1034: ip->i_devvp = ump->um_devvp;
1035: VREF(ip->i_devvp);
1036: /*
1037: * Set up a generation number for this inode if it does not
1038: * already have one. This should only happen on old filesystems.
1039: */
1040: if (ip->i_gen == 0) {
1041: if (++nextgennumber < (u_long)time.tv_sec)
1042: nextgennumber = time.tv_sec;
1043: ip->i_gen = nextgennumber;
1044: if ((vp->v_mount->mnt_flag & MNT_RDONLY) == 0)
1045: ip->i_flag |= IN_MODIFIED;
1046: }
1047: /*
1048: * Ensure that uid and gid are correct. This is a temporary
1049: * fix until fsck has been changed to do the update.
1050: */
1051: if (fs->fs_inodefmt < FS_44INODEFMT) { /* XXX */
1052: ip->i_uid = ip->i_din.di_ouid; /* XXX */
1053: ip->i_gid = ip->i_din.di_ogid; /* XXX */
1054: } /* XXX */
1055:
1056: *vpp = vp;
1057: #if MACH_NBC
1058: if ((vp->v_type == VREG) && !(vp->v_vm_info)){
1059: vm_info_init(vp);
1060: }
1061: #endif /* MACH_NBC */
1062: return (0);
1063: }
1064:
1065: /*
1066: * File handle to vnode
1067: *
1068: * Have to be really careful about stale file handles:
1069: * - check that the inode number is valid
1070: * - call ffs_vget() to get the locked inode
1071: * - check for an unallocated inode (i_mode == 0)
1072: * - check that the given client host has export rights and return
1073: * those rights via. exflagsp and credanonp
1074: */
1075: int
1076: ffs_fhtovp(mp, fhp, nam, vpp, exflagsp, credanonp)
1077: register struct mount *mp;
1078: struct fid *fhp;
1079: struct mbuf *nam;
1080: struct vnode **vpp;
1081: int *exflagsp;
1082: struct ucred **credanonp;
1083: {
1084: register struct ufid *ufhp;
1085: struct fs *fs;
1086:
1087: ufhp = (struct ufid *)fhp;
1088: fs = VFSTOUFS(mp)->um_fs;
1089: if (ufhp->ufid_ino < ROOTINO ||
1090: ufhp->ufid_ino >= fs->fs_ncg * fs->fs_ipg)
1091: return (ESTALE);
1092: return (ufs_check_export(mp, ufhp, nam, vpp, exflagsp, credanonp));
1093: }
1094:
1095: /*
1096: * Vnode pointer to File handle
1097: */
1098: /* ARGSUSED */
1099: ffs_vptofh(vp, fhp)
1100: struct vnode *vp;
1101: struct fid *fhp;
1102: {
1103: register struct inode *ip;
1104: register struct ufid *ufhp;
1105:
1106: ip = VTOI(vp);
1107: ufhp = (struct ufid *)fhp;
1108: ufhp->ufid_len = sizeof(struct ufid);
1109: ufhp->ufid_ino = ip->i_number;
1110: ufhp->ufid_gen = ip->i_gen;
1111: return (0);
1112: }
1113:
1114: /*
1115: * Initialize the filesystem; just use ufs_init.
1116: */
1117: int
1118: ffs_init(vfsp)
1119: struct vfsconf *vfsp;
1120: {
1121:
1122: return (ufs_init(vfsp));
1123: }
1124:
1125: /*
1126: * fast filesystem related variables.
1127: */
1128: ffs_sysctl(name, namelen, oldp, oldlenp, newp, newlen, p)
1129: int *name;
1130: u_int namelen;
1131: void *oldp;
1132: size_t *oldlenp;
1133: void *newp;
1134: size_t newlen;
1135: struct proc *p;
1136: {
1137: extern int doclusterread, doclusterwrite, doreallocblks, doasyncfree;
1138:
1139: /* all sysctl names at this level are terminal */
1140: if (namelen != 1)
1141: return (ENOTDIR); /* overloaded */
1142:
1143: switch (name[0]) {
1144: case FFS_CLUSTERREAD:
1145: return (sysctl_int(oldp, oldlenp, newp, newlen,
1146: &doclusterread));
1147: case FFS_CLUSTERWRITE:
1148: return (sysctl_int(oldp, oldlenp, newp, newlen,
1149: &doclusterwrite));
1150: case FFS_REALLOCBLKS:
1151: return (sysctl_int(oldp, oldlenp, newp, newlen,
1152: &doreallocblks));
1153: case FFS_ASYNCFREE:
1154: return (sysctl_int(oldp, oldlenp, newp, newlen, &doasyncfree));
1155: default:
1156: return (EOPNOTSUPP);
1157: }
1158: /* NOTREACHED */
1159: }
1160:
1161: /*
1162: * Write a superblock and associated information back to disk.
1163: */
1164: int
1165: ffs_sbupdate(mp, waitfor)
1166: struct ufsmount *mp;
1167: int waitfor;
1168: {
1169: register struct fs *dfs, *fs = mp->um_fs;
1170: register struct buf *bp;
1171: int blks;
1172: caddr_t space;
1173: int i, size, error, allerror = 0;
1174: #ifdef NeXT
1175: int devBlockSize=0;
1176: #endif /* NeXT */
1177: #if REV_ENDIAN_FS
1178: int rev_endian=(mp->um_mountp->mnt_flag & MNT_REVEND);
1179: #endif /* REV_ENDIAN_FS */
1180:
1181: /*
1182: * First write back the summary information.
1183: */
1184: blks = howmany(fs->fs_cssize, fs->fs_fsize);
1185: space = (caddr_t)fs->fs_csp[0];
1186: for (i = 0; i < blks; i += fs->fs_frag) {
1187: size = fs->fs_bsize;
1188: if (i + fs->fs_frag > blks)
1189: size = (blks - i) * fs->fs_fsize;
1190: bp = getblk(mp->um_devvp, fsbtodb(fs, fs->fs_csaddr + i),
1191: size, 0, 0);
1192: bcopy(space, bp->b_data, (u_int)size);
1193: #if REV_ENDIAN_FS
1194: if (rev_endian) {
1195: byte_swap_ints((int *)bp->b_data, size / sizeof(int));
1196: }
1197: #endif /* REV_ENDIAN_FS */
1198: space += size;
1199: if (waitfor != MNT_WAIT)
1200: bawrite(bp);
1201: else if (error = bwrite(bp))
1202: allerror = error;
1203: }
1204: /*
1205: * Now write back the superblock itself. If any errors occurred
1206: * up to this point, then fail so that the superblock avoids
1207: * being written out as clean.
1208: */
1209: if (allerror)
1210: return (allerror);
1211: #ifdef NeXT
1212: VOP_DEVBLOCKSIZE(mp->um_devvp,&devBlockSize);
1213: bp = getblk(mp->um_devvp, (SBOFF/devBlockSize), (int)fs->fs_sbsize, 0, 0);
1214: #else
1215: bp = getblk(mp->um_devvp, SBLOCK, (int)fs->fs_sbsize, 0, 0);
1216: #endif
1217: bcopy((caddr_t)fs, bp->b_data, (u_int)fs->fs_sbsize);
1218: /* Restore compatibility to old file systems. XXX */
1219: dfs = (struct fs *)bp->b_data; /* XXX */
1220: if (fs->fs_postblformat == FS_42POSTBLFMT) /* XXX */
1221: dfs->fs_nrpos = -1; /* XXX */
1222: #if REV_ENDIAN_FS
1223: /*
1224: * Swapping bytes here ; so that in case
1225: * of inode format < FS_44INODEFMT appropriate
1226: * fields get moved
1227: */
1228: if (rev_endian) {
1229: byte_swap_sbout((struct fs *)bp->b_data);
1230: }
1231: #endif /* REV_ENDIAN_FS */
1232: if (fs->fs_inodefmt < FS_44INODEFMT) { /* XXX */
1233: int32_t *lp, tmp; /* XXX */
1234: /* XXX */
1235: lp = (int32_t *)&dfs->fs_qbmask; /* XXX */
1236: tmp = lp[4]; /* XXX */
1237: for (i = 4; i > 0; i--) /* XXX */
1238: lp[i] = lp[i-1]; /* XXX */
1239: lp[0] = tmp; /* XXX */
1240: } /* XXX */
1241: #if REV_ENDIAN_FS
1242: /* Note that dfs is already swapped so swap the filesize
1243: * before writing
1244: */
1245: if (rev_endian) {
1246: dfs->fs_maxfilesize = NXSwapLongLong(mp->um_savedmaxfilesize); /* XXX */
1247: } else {
1248: #endif /* REV_ENDIAN_FS */
1249: dfs->fs_maxfilesize = mp->um_savedmaxfilesize; /* XXX */
1250: #if REV_ENDIAN_FS
1251: }
1252: #endif /* REV_ENDIAN_FS */
1253: if (waitfor != MNT_WAIT)
1254: bawrite(bp);
1255: else if (error = bwrite(bp))
1256: allerror = error;
1257:
1258: return (allerror);
1259: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.