ffs_vfsops.c revision 55029
1290650Shselasky/*
2321992Shselasky * Copyright (c) 1989, 1991, 1993, 1994
3290650Shselasky *	The Regents of the University of California.  All rights reserved.
4290650Shselasky *
5290650Shselasky * Redistribution and use in source and binary forms, with or without
6290650Shselasky * modification, are permitted provided that the following conditions
7290650Shselasky * are met:
8290650Shselasky * 1. Redistributions of source code must retain the above copyright
9290650Shselasky *    notice, this list of conditions and the following disclaimer.
10290650Shselasky * 2. Redistributions in binary form must reproduce the above copyright
11290650Shselasky *    notice, this list of conditions and the following disclaimer in the
12290650Shselasky *    documentation and/or other materials provided with the distribution.
13290650Shselasky * 3. All advertising materials mentioning features or use of this software
14290650Shselasky *    must display the following acknowledgement:
15290650Shselasky *	This product includes software developed by the University of
16290650Shselasky *	California, Berkeley and its contributors.
17290650Shselasky * 4. Neither the name of the University nor the names of its contributors
18290650Shselasky *    may be used to endorse or promote products derived from this software
19290650Shselasky *    without specific prior written permission.
20290650Shselasky *
21290650Shselasky * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22290650Shselasky * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23290650Shselasky * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24290650Shselasky * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25290650Shselasky * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26321992Shselasky * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27290650Shselasky * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28290650Shselasky * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29290650Shselasky * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30290650Shselasky * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31290650Shselasky * SUCH DAMAGE.
32290650Shselasky *
33290650Shselasky *	@(#)ffs_vfsops.c	8.31 (Berkeley) 5/20/95
34290650Shselasky * $FreeBSD: head/sys/ufs/ffs/ffs_vfsops.c 55029 1999-12-23 15:42:14Z bde $
35290650Shselasky */
36290650Shselasky
37290650Shselasky#include "opt_quota.h"
38290650Shselasky
39290650Shselasky#include <sys/param.h>
40290650Shselasky#include <sys/systm.h>
41290650Shselasky#include <sys/namei.h>
42290650Shselasky#include <sys/proc.h>
43290650Shselasky#include <sys/kernel.h>
44290650Shselasky#include <sys/vnode.h>
45290650Shselasky#include <sys/mount.h>
46290650Shselasky#include <sys/buf.h>
47290650Shselasky#include <sys/conf.h>
48290650Shselasky#include <sys/fcntl.h>
49290650Shselasky#include <sys/disklabel.h>
50290650Shselasky#include <sys/malloc.h>
51306233Shselasky
52290650Shselasky#include <ufs/ufs/quota.h>
53306233Shselasky#include <ufs/ufs/ufsmount.h>
54321992Shselasky#include <ufs/ufs/inode.h>
55321992Shselasky#include <ufs/ufs/ufs_extern.h>
56290650Shselasky
57290650Shselasky#include <ufs/ffs/fs.h>
58290650Shselasky#include <ufs/ffs/ffs_extern.h>
59290650Shselasky
60290650Shselasky#include <vm/vm.h>
61290650Shselasky#include <vm/vm_page.h>
62290650Shselasky#include <vm/vm_zone.h>
63290650Shselasky
64290650Shselaskystatic MALLOC_DEFINE(M_FFSNODE, "FFS node", "FFS vnode private part");
65306233Shselasky
66306233Shselaskystatic int	ffs_sbupdate __P((struct ufsmount *, int));
67306233Shselaskystatic int	ffs_reload __P((struct mount *,struct ucred *,struct proc *));
68306233Shselaskystatic int	ffs_oldfscompat __P((struct fs *));
69306233Shselaskystatic int	ffs_mount __P((struct mount *, char *, caddr_t,
70290650Shselasky				struct nameidata *, struct proc *));
71290650Shselaskystatic int	ffs_init __P((struct vfsconf *));
72290650Shselasky
73290650Shselaskystatic struct vfsops ufs_vfsops = {
74290650Shselasky	ffs_mount,
75290650Shselasky	ufs_start,
76290650Shselasky	ffs_unmount,
77290650Shselasky	ufs_root,
78290650Shselasky	ufs_quotactl,
79290650Shselasky	ffs_statfs,
80290650Shselasky	ffs_sync,
81290650Shselasky	ffs_vget,
82290650Shselasky	ffs_fhtovp,
83290650Shselasky	ufs_check_export,
84290650Shselasky	ffs_vptofh,
85290650Shselasky	ffs_init,
86290650Shselasky	vfs_stduninit,
87290650Shselasky	vfs_stdextattrctl,
88290650Shselasky};
89321992Shselasky
90321992ShselaskyVFS_SET(ufs_vfsops, ufs, 0);
91290650Shselasky
92290650Shselasky/*
93290650Shselasky * ffs_mount
94290650Shselasky *
95290650Shselasky * Called when mounting local physical media
96290650Shselasky *
97290650Shselasky * PARAMETERS:
98290650Shselasky *		mountroot
99290650Shselasky *			mp	mount point structure
100290650Shselasky *			path	NULL (flag for root mount!!!)
101290650Shselasky *			data	<unused>
102290650Shselasky *			ndp	<unused>
103290650Shselasky *			p	process (user credentials check [statfs])
104290650Shselasky *
105290650Shselasky *		mount
106290650Shselasky *			mp	mount point structure
107290650Shselasky *			path	path to mount point
108290650Shselasky *			data	pointer to argument struct in user space
109290650Shselasky *			ndp	mount point namei() return (used for
110290650Shselasky *				credentials on reload), reused to look
111290650Shselasky *				up block device.
112290650Shselasky *			p	process (user credentials check)
113290650Shselasky *
114290650Shselasky * RETURNS:	0	Success
115290650Shselasky *		!0	error number (errno.h)
116290650Shselasky *
117290650Shselasky * LOCK STATE:
118290650Shselasky *
119290650Shselasky *		ENTRY
120290650Shselasky *			mount point is locked
121290650Shselasky *		EXIT
122290650Shselasky *			mount point is locked
123290650Shselasky *
124290650Shselasky * NOTES:
125290650Shselasky *		A NULL path can be used for a flag since the mount
126290650Shselasky *		system call will fail with EFAULT in copyinstr in
127290650Shselasky *		namei() if it is a genuine NULL from the user.
128290650Shselasky */
129290650Shselaskystatic int
130290650Shselaskyffs_mount( mp, path, data, ndp, p)
131290650Shselasky        struct mount		*mp;	/* mount struct pointer*/
132290650Shselasky        char			*path;	/* path to mount point*/
133290650Shselasky        caddr_t			data;	/* arguments to FS specific mount*/
134290650Shselasky        struct nameidata	*ndp;	/* mount point credentials*/
135290650Shselasky        struct proc		*p;	/* process requesting mount*/
136290650Shselasky{
137290650Shselasky	size_t		size;
138290650Shselasky	int		err = 0;
139290650Shselasky	struct vnode	*devvp;
140290650Shselasky
141290650Shselasky	struct ufs_args args;
142290650Shselasky	struct ufsmount *ump = 0;
143290650Shselasky	register struct fs *fs;
144290650Shselasky	int error, flags, ronly = 0;
145290650Shselasky	mode_t accessmode;
146290650Shselasky
147290650Shselasky	/*
148290650Shselasky	 * Use NULL path to flag a root mount
149306233Shselasky	 */
150306233Shselasky	if( path == NULL) {
151308678Shselasky		/*
152308678Shselasky		 ***
153308678Shselasky		 * Mounting root file system
154308678Shselasky		 ***
155308678Shselasky		 */
156308678Shselasky
157290650Shselasky		if ((err = bdevvp(rootdev, &rootvp))) {
158290650Shselasky			printf("ffs_mountroot: can't find rootvp\n");
159290650Shselasky			return (err);
160290650Shselasky		}
161290650Shselasky
162290650Shselasky		if( ( err = ffs_mountfs(rootvp, mp, p, M_FFSNODE)) != 0) {
163290650Shselasky			/* fs specific cleanup (if any)*/
164290650Shselasky			goto error_1;
165290650Shselasky		}
166290650Shselasky
167290650Shselasky		goto dostatfs;		/* success*/
168290650Shselasky
169290650Shselasky	}
170290650Shselasky
171290650Shselasky	/*
172290650Shselasky	 ***
173290650Shselasky	 * Mounting non-root file system or updating a file system
174290650Shselasky	 ***
175290650Shselasky	 */
176290650Shselasky
177290650Shselasky	/* copy in user arguments*/
178306233Shselasky	err = copyin(data, (caddr_t)&args, sizeof (struct ufs_args));
179306233Shselasky	if (err)
180290650Shselasky		goto error_1;		/* can't get arguments*/
181290650Shselasky
182290650Shselasky	/*
183290650Shselasky	 * If updating, check whether changing from read-only to
184290650Shselasky	 * read/write; if there is no device name, that's all we do.
185290650Shselasky	 */
186290650Shselasky	if (mp->mnt_flag & MNT_UPDATE) {
187290650Shselasky		ump = VFSTOUFS(mp);
188290650Shselasky		fs = ump->um_fs;
189290650Shselasky		devvp = ump->um_devvp;
190290650Shselasky		err = 0;
191290650Shselasky		ronly = fs->fs_ronly;	/* MNT_RELOAD might change this */
192321992Shselasky		if (ronly == 0 && (mp->mnt_flag & MNT_RDONLY)) {
193321992Shselasky			flags = WRITECLOSE;
194321992Shselasky			if (mp->mnt_flag & MNT_FORCE)
195321992Shselasky				flags |= FORCECLOSE;
196321992Shselasky			if (mp->mnt_flag & MNT_SOFTDEP) {
197321992Shselasky				err = softdep_flushfiles(mp, flags, p);
198290650Shselasky			} else {
199290650Shselasky				err = ffs_flushfiles(mp, flags, p);
200290650Shselasky			}
201290650Shselasky			ronly = 1;
202290650Shselasky		}
203290650Shselasky		if (!err && (mp->mnt_flag & MNT_RELOAD))
204290650Shselasky			err = ffs_reload(mp, ndp->ni_cnd.cn_cred, p);
205290650Shselasky		if (err) {
206290650Shselasky			goto error_1;
207290650Shselasky		}
208290650Shselasky		if (ronly && (mp->mnt_kern_flag & MNTK_WANTRDWR)) {
209290650Shselasky			/*
210290650Shselasky			 * If upgrade to read-write by non-root, then verify
211290650Shselasky			 * that user has necessary permissions on the device.
212290650Shselasky			 */
213290650Shselasky			if (p->p_ucred->cr_uid != 0) {
214321992Shselasky				vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY, p);
215321992Shselasky				if ((error = VOP_ACCESS(devvp, VREAD | VWRITE,
216290650Shselasky				    p->p_ucred, p)) != 0) {
217290650Shselasky					VOP_UNLOCK(devvp, 0, p);
218290650Shselasky					return (error);
219290650Shselasky				}
220290650Shselasky				VOP_UNLOCK(devvp, 0, p);
221290650Shselasky			}
222290650Shselasky
223290650Shselasky			fs->fs_flags &= ~FS_UNCLEAN;
224290650Shselasky			if (fs->fs_clean == 0) {
225290650Shselasky				fs->fs_flags |= FS_UNCLEAN;
226290650Shselasky				if (mp->mnt_flag & MNT_FORCE) {
227290650Shselasky					printf(
228290650Shselasky"WARNING: %s was not properly dismounted\n",
229290650Shselasky					    fs->fs_fsmnt);
230290650Shselasky				} else {
231290650Shselasky					printf(
232290650Shselasky"WARNING: R/W mount of %s denied.  Filesystem is not clean - run fsck\n",
233290650Shselasky					    fs->fs_fsmnt);
234290650Shselasky					err = EPERM;
235290650Shselasky					goto error_1;
236321992Shselasky				}
237321992Shselasky			}
238321992Shselasky
239321992Shselasky			/* check to see if we need to start softdep */
240290650Shselasky			if (fs->fs_flags & FS_DOSOFTDEP) {
241290650Shselasky				err = softdep_mount(devvp, mp, fs, p->p_ucred);
242290650Shselasky				if (err)
243290650Shselasky					goto error_1;
244290650Shselasky			}
245290650Shselasky
246290650Shselasky			ronly = 0;
247290650Shselasky		}
248290650Shselasky		/*
249290650Shselasky		 * Soft updates is incompatible with "async",
250290650Shselasky		 * so if we are doing softupdates stop the user
251290650Shselasky		 * from setting the async flag in an update.
252290650Shselasky		 * Softdep_mount() clears it in an initial mount
253290650Shselasky		 * or ro->rw remount.
254290650Shselasky		 */
255290650Shselasky		if (mp->mnt_flag & MNT_SOFTDEP) {
256290650Shselasky			mp->mnt_flag &= ~MNT_ASYNC;
257290650Shselasky		}
258290650Shselasky		/* if not updating name...*/
259290650Shselasky		if (args.fspec == 0) {
260290650Shselasky			/*
261290650Shselasky			 * Process export requests.  Jumping to "success"
262290650Shselasky			 * will return the vfs_export() error code.
263290650Shselasky			 */
264290650Shselasky			err = vfs_export(mp, &ump->um_export, &args.export);
265290650Shselasky			goto success;
266290650Shselasky		}
267290650Shselasky	}
268290650Shselasky
269290650Shselasky	/*
270290650Shselasky	 * Not an update, or updating the name: look up the name
271290650Shselasky	 * and verify that it refers to a sensible block device.
272290650Shselasky	 */
273290650Shselasky	NDINIT(ndp, LOOKUP, FOLLOW, UIO_USERSPACE, args.fspec, p);
274290650Shselasky	err = namei(ndp);
275290650Shselasky	if (err) {
276290650Shselasky		/* can't get devvp!*/
277290650Shselasky		goto error_1;
278290650Shselasky	}
279290650Shselasky
280290650Shselasky	NDFREE(ndp, NDF_ONLY_PNBUF);
281290650Shselasky	devvp = ndp->ni_vp;
282290650Shselasky
283290650Shselasky	if (!vn_isdisk(devvp)) {
284321992Shselasky		err = ENOTBLK;
285321992Shselasky		goto error_2;
286321992Shselasky	}
287321992Shselasky
288321992Shselasky	/*
289290650Shselasky	 * If mount by non-root, then verify that user has necessary
290290650Shselasky	 * permissions on the device.
291290650Shselasky	 */
292290650Shselasky	if (p->p_ucred->cr_uid != 0) {
293290650Shselasky		accessmode = VREAD;
294290650Shselasky		if ((mp->mnt_flag & MNT_RDONLY) == 0)
295290650Shselasky			accessmode |= VWRITE;
296290650Shselasky		vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY, p);
297290650Shselasky		if ((error = VOP_ACCESS(devvp, accessmode, p->p_ucred, p)) != 0) {
298290650Shselasky			vput(devvp);
299290650Shselasky			return (error);
300290650Shselasky		}
301290650Shselasky		VOP_UNLOCK(devvp, 0, p);
302290650Shselasky	}
303290650Shselasky
304290650Shselasky	if (mp->mnt_flag & MNT_UPDATE) {
305290650Shselasky		/*
306290650Shselasky		 ********************
307290650Shselasky		 * UPDATE
308290650Shselasky		 * If it's not the same vnode, or at least the same device
309290650Shselasky		 * then it's not correct.
310290650Shselasky		 ********************
311290650Shselasky		 */
312290650Shselasky
313290650Shselasky		if (devvp != ump->um_devvp) {
314290650Shselasky			if ( devvp->v_rdev == ump->um_devvp->v_rdev) {
315290650Shselasky				vrele(devvp);
316321992Shselasky			} else {
317321992Shselasky				err = EINVAL;	/* needs translation */
318321992Shselasky			}
319290650Shselasky		} else
320290650Shselasky			vrele(devvp);
321321992Shselasky		/*
322290650Shselasky		 * Update device name only on success
323290650Shselasky		 */
324308678Shselasky		if( !err) {
325308678Shselasky			/* Save "mounted from" info for mount point (NULL pad)*/
326308678Shselasky			copyinstr(	args.fspec,
327308678Shselasky					mp->mnt_stat.f_mntfromname,
328308678Shselasky					MNAMELEN - 1,
329308678Shselasky					&size);
330308678Shselasky			bzero( mp->mnt_stat.f_mntfromname + size, MNAMELEN - size);
331308678Shselasky		}
332308678Shselasky	} else {
333308678Shselasky		/*
334308678Shselasky		 ********************
335308678Shselasky		 * NEW MOUNT
336308678Shselasky		 ********************
337308678Shselasky		 */
338308678Shselasky
339308678Shselasky		/*
340308678Shselasky		 * Since this is a new mount, we want the names for
341308678Shselasky		 * the device and the mount point copied in.  If an
342308678Shselasky		 * error occurs,  the mountpoint is discarded by the
343308678Shselasky		 * upper level code.
344308678Shselasky		 */
345308678Shselasky		/* Save "last mounted on" info for mount point (NULL pad)*/
346308678Shselasky		copyinstr(	path,				/* mount point*/
347308678Shselasky				mp->mnt_stat.f_mntonname,	/* save area*/
348308678Shselasky				MNAMELEN - 1,			/* max size*/
349308678Shselasky				&size);				/* real size*/
350308678Shselasky		bzero( mp->mnt_stat.f_mntonname + size, MNAMELEN - size);
351308678Shselasky
352308678Shselasky		/* Save "mounted from" info for mount point (NULL pad)*/
353308678Shselasky		copyinstr(	args.fspec,			/* device name*/
354308678Shselasky				mp->mnt_stat.f_mntfromname,	/* save area*/
355308678Shselasky				MNAMELEN - 1,			/* max size*/
356308678Shselasky				&size);				/* real size*/
357308678Shselasky		bzero( mp->mnt_stat.f_mntfromname + size, MNAMELEN - size);
358308678Shselasky
359308678Shselasky		err = ffs_mountfs(devvp, mp, p, M_FFSNODE);
360308678Shselasky	}
361308678Shselasky	if (err) {
362308678Shselasky		goto error_2;
363308678Shselasky	}
364308678Shselasky
365308678Shselaskydostatfs:
366308678Shselasky	/*
367308678Shselasky	 * Initialize FS stat information in mount struct; uses both
368308678Shselasky	 * mp->mnt_stat.f_mntonname and mp->mnt_stat.f_mntfromname
369308678Shselasky	 *
370308678Shselasky	 * This code is common to root and non-root mounts
371308678Shselasky	 */
372308678Shselasky	(void)VFS_STATFS(mp, &mp->mnt_stat, p);
373308678Shselasky
374308678Shselasky	goto success;
375321992Shselasky
376321992Shselasky
377321992Shselaskyerror_2:	/* error with devvp held*/
378321992Shselasky
379321992Shselasky	/* release devvp before failing*/
380308678Shselasky	vrele(devvp);
381290650Shselasky
382290650Shselaskyerror_1:	/* no state to back out*/
383290650Shselasky
384290650Shselaskysuccess:
385290650Shselasky	if (!err && path && (mp->mnt_flag & MNT_UPDATE)) {
386290650Shselasky		/* Update clean flag after changing read-onlyness. */
387329200Shselasky		fs = ump->um_fs;
388329200Shselasky		if (ronly != fs->fs_ronly) {
389329200Shselasky			fs->fs_ronly = ronly;
390329200Shselasky			fs->fs_clean = ronly &&
391329200Shselasky			    (fs->fs_flags & FS_UNCLEAN) == 0 ? 1 : 0;
392329200Shselasky			ffs_sbupdate(ump, MNT_WAIT);
393290650Shselasky		}
394329200Shselasky	}
395290650Shselasky	return (err);
396329200Shselasky}
397290650Shselasky
398290650Shselasky/*
399329200Shselasky * Reload all incore data for a filesystem (used after running fsck on
400290650Shselasky * the root filesystem and finding things to fix). The filesystem must
401329200Shselasky * be mounted read-only.
402290650Shselasky *
403290650Shselasky * Things to do to update the mount:
404329200Shselasky *	1) invalidate all cached meta-data.
405290650Shselasky *	2) re-read superblock from disk.
406290650Shselasky *	3) re-read summary information from disk.
407290650Shselasky *	4) invalidate all inactive vnodes.
408329200Shselasky *	5) invalidate all cached file data.
409290650Shselasky *	6) re-read inode data for all active vnodes.
410290650Shselasky */
411329200Shselaskystatic int
412290650Shselaskyffs_reload(mp, cred, p)
413290650Shselasky	register struct mount *mp;
414290650Shselasky	struct ucred *cred;
415290650Shselasky	struct proc *p;
416290650Shselasky{
417290650Shselasky	register struct vnode *vp, *nvp, *devvp;
418290650Shselasky	struct inode *ip;
419290650Shselasky	struct csum *space;
420290650Shselasky	struct buf *bp;
421290650Shselasky	struct fs *fs, *newfs;
422290650Shselasky	struct partinfo dpart;
423290650Shselasky	dev_t dev;
424290650Shselasky	int i, blks, size, error;
425290650Shselasky	int32_t *lp;
426290650Shselasky
427290650Shselasky	if ((mp->mnt_flag & MNT_RDONLY) == 0)
428290650Shselasky		return (EINVAL);
429290650Shselasky	/*
430290650Shselasky	 * Step 1: invalidate all cached meta-data.
431290650Shselasky	 */
432290650Shselasky	devvp = VFSTOUFS(mp)->um_devvp;
433290650Shselasky	vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY, p);
434290650Shselasky	error = vinvalbuf(devvp, 0, cred, p, 0, 0);
435290650Shselasky	VOP_UNLOCK(devvp, 0, p);
436290650Shselasky	if (error)
437290650Shselasky		panic("ffs_reload: dirty1");
438290650Shselasky
439321992Shselasky	dev = devvp->v_rdev;
440290650Shselasky
441290650Shselasky	/*
442290650Shselasky	 * Only VMIO the backing device if the backing device is a real
443290650Shselasky	 * block device.  See ffs_mountmfs() for more details.
444290650Shselasky	 */
445290650Shselasky	if (devvp->v_tag != VT_MFS && vn_isdisk(devvp)) {
446290650Shselasky		vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY, p);
447290650Shselasky		vfs_object_create(devvp, p, p->p_ucred);
448290650Shselasky		simple_lock(&devvp->v_interlock);
449329200Shselasky		VOP_UNLOCK(devvp, LK_INTERLOCK, p);
450329200Shselasky	}
451329200Shselasky
452329200Shselasky	/*
453329200Shselasky	 * Step 2: re-read superblock from disk.
454329200Shselasky	 */
455329200Shselasky	if (VOP_IOCTL(devvp, DIOCGPART, (caddr_t)&dpart, FREAD, NOCRED, p) != 0)
456329200Shselasky		size = DEV_BSIZE;
457329200Shselasky	else
458329200Shselasky		size = dpart.disklab->d_secsize;
459329200Shselasky	if ((error = bread(devvp, (ufs_daddr_t)(SBOFF/size), SBSIZE, NOCRED,&bp)) != 0)
460329200Shselasky		return (error);
461329200Shselasky	newfs = (struct fs *)bp->b_data;
462329200Shselasky	if (newfs->fs_magic != FS_MAGIC || newfs->fs_bsize > MAXBSIZE ||
463329200Shselasky		newfs->fs_bsize < sizeof(struct fs)) {
464329200Shselasky			brelse(bp);
465290650Shselasky			return (EIO);		/* XXX needs translation */
466290650Shselasky	}
467290650Shselasky	fs = VFSTOUFS(mp)->um_fs;
468290650Shselasky	/*
469290650Shselasky	 * Copy pointer fields back into superblock before copying in	XXX
470290650Shselasky	 * new superblock. These should really be in the ufsmount.	XXX
471290650Shselasky	 * Note that important parameters (eg fs_ncg) are unchanged.
472290650Shselasky	 */
473290650Shselasky	bcopy(&fs->fs_csp[0], &newfs->fs_csp[0], sizeof(fs->fs_csp));
474290650Shselasky	newfs->fs_maxcluster = fs->fs_maxcluster;
475290650Shselasky	bcopy(newfs, fs, (u_int)fs->fs_sbsize);
476290650Shselasky	if (fs->fs_sbsize < SBSIZE)
477290650Shselasky		bp->b_flags |= B_INVAL;
478290650Shselasky	brelse(bp);
479290650Shselasky	mp->mnt_maxsymlinklen = fs->fs_maxsymlinklen;
480290650Shselasky	ffs_oldfscompat(fs);
481306233Shselasky
482306233Shselasky	/*
483290650Shselasky	 * Step 3: re-read summary information from disk.
484290650Shselasky	 */
485290650Shselasky	blks = howmany(fs->fs_cssize, fs->fs_fsize);
486290650Shselasky	space = fs->fs_csp[0];
487290650Shselasky	for (i = 0; i < blks; i += fs->fs_frag) {
488290650Shselasky		size = fs->fs_bsize;
489290650Shselasky		if (i + fs->fs_frag > blks)
490290650Shselasky			size = (blks - i) * fs->fs_fsize;
491290650Shselasky		error = bread(devvp, fsbtodb(fs, fs->fs_csaddr + i), size,
492290650Shselasky		    NOCRED, &bp);
493290650Shselasky		if (error)
494290650Shselasky			return (error);
495329200Shselasky		bcopy(bp->b_data, fs->fs_csp[fragstoblks(fs, i)], (u_int)size);
496290650Shselasky		brelse(bp);
497329200Shselasky	}
498290650Shselasky	/*
499290650Shselasky	 * We no longer know anything about clusters per cylinder group.
500290650Shselasky	 */
501290650Shselasky	if (fs->fs_contigsumsize > 0) {
502290650Shselasky		lp = fs->fs_maxcluster;
503290650Shselasky		for (i = 0; i < fs->fs_ncg; i++)
504290650Shselasky			*lp++ = fs->fs_contigsumsize;
505290650Shselasky	}
506290650Shselasky
507290650Shselaskyloop:
508290650Shselasky	simple_lock(&mntvnode_slock);
509290650Shselasky	for (vp = mp->mnt_vnodelist.lh_first; vp != NULL; vp = nvp) {
510290650Shselasky		if (vp->v_mount != mp) {
511290650Shselasky			simple_unlock(&mntvnode_slock);
512290650Shselasky			goto loop;
513290650Shselasky		}
514290650Shselasky		nvp = vp->v_mntvnodes.le_next;
515290650Shselasky		/*
516290650Shselasky		 * Step 4: invalidate all inactive vnodes.
517290650Shselasky		 */
518290650Shselasky		if (vrecycle(vp, &mntvnode_slock, p))
519290650Shselasky			goto loop;
520290650Shselasky		/*
521290650Shselasky		 * Step 5: invalidate all cached file data.
522290650Shselasky		 */
523290650Shselasky		simple_lock(&vp->v_interlock);
524290650Shselasky		simple_unlock(&mntvnode_slock);
525308678Shselasky		if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK, p)) {
526308678Shselasky			goto loop;
527308678Shselasky		}
528290650Shselasky		if (vinvalbuf(vp, 0, cred, p, 0, 0))
529290650Shselasky			panic("ffs_reload: dirty2");
530290650Shselasky		/*
531290650Shselasky		 * Step 6: re-read inode data for all active vnodes.
532290650Shselasky		 */
533290650Shselasky		ip = VTOI(vp);
534290650Shselasky		error =
535321992Shselasky		    bread(devvp, fsbtodb(fs, ino_to_fsba(fs, ip->i_number)),
536321992Shselasky		    (int)fs->fs_bsize, NOCRED, &bp);
537308678Shselasky		if (error) {
538321992Shselasky			vput(vp);
539321992Shselasky			return (error);
540321992Shselasky		}
541321992Shselasky		ip->i_din = *((struct dinode *)bp->b_data +
542321992Shselasky		    ino_to_fsbo(fs, ip->i_number));
543290650Shselasky		ip->i_effnlink = ip->i_nlink;
544290650Shselasky		brelse(bp);
545290650Shselasky		vput(vp);
546290650Shselasky		simple_lock(&mntvnode_slock);
547290650Shselasky	}
548290650Shselasky	simple_unlock(&mntvnode_slock);
549290650Shselasky	return (0);
550290650Shselasky}
551290650Shselasky
552290650Shselasky/*
553290650Shselasky * Common code for mount and mountroot
554290650Shselasky */
555290650Shselaskyint
556290650Shselaskyffs_mountfs(devvp, mp, p, malloctype)
557290650Shselasky	register struct vnode *devvp;
558306233Shselasky	struct mount *mp;
559306233Shselasky	struct proc *p;
560306233Shselasky	struct malloc_type *malloctype;
561306233Shselasky{
562306233Shselasky	register struct ufsmount *ump;
563306233Shselasky	struct buf *bp;
564306233Shselasky	register struct fs *fs;
565306233Shselasky	dev_t dev;
566290650Shselasky	struct partinfo dpart;
567290650Shselasky	caddr_t base, space;
568290650Shselasky	int error, i, blks, size, ronly;
569290650Shselasky	int32_t *lp;
570290650Shselasky	struct ucred *cred;
571290650Shselasky	u_int64_t maxfilesize;					/* XXX */
572290650Shselasky	size_t strsize;
573290650Shselasky	int ncount;
574290650Shselasky
575290650Shselasky	dev = devvp->v_rdev;
576290650Shselasky	cred = p ? p->p_ucred : NOCRED;
577290650Shselasky	/*
578290650Shselasky	 * Disallow multiple mounts of the same device.
579290650Shselasky	 * Disallow mounting of a device that is currently in use
580290650Shselasky	 * (except for root, which might share swap device for miniroot).
581290650Shselasky	 * Flush out any old buffers remaining from a previous use.
582290650Shselasky	 */
583290650Shselasky	error = vfs_mountedon(devvp);
584290650Shselasky	if (error)
585290650Shselasky		return (error);
586290650Shselasky	ncount = vcount(devvp);
587290650Shselasky
588290650Shselasky	if (ncount > 1 && devvp != rootvp)
589290650Shselasky		return (EBUSY);
590290650Shselasky	vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY, p);
591290650Shselasky	error = vinvalbuf(devvp, V_SAVE, cred, p, 0, 0);
592290650Shselasky	VOP_UNLOCK(devvp, 0, p);
593290650Shselasky	if (error)
594290650Shselasky		return (error);
595290650Shselasky
596290650Shselasky	/*
597290650Shselasky	 * Only VMIO the backing device if the backing device is a real
598290650Shselasky	 * block device.  This excludes the original MFS implementation.
599290650Shselasky	 * Note that it is optional that the backing device be VMIOed.  This
600290650Shselasky	 * increases the opportunity for metadata caching.
601290650Shselasky	 */
602290650Shselasky	if (devvp->v_tag != VT_MFS && vn_isdisk(devvp)) {
603290650Shselasky		vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY, p);
604290650Shselasky		vfs_object_create(devvp, p, p->p_ucred);
605290650Shselasky		simple_lock(&devvp->v_interlock);
606290650Shselasky		VOP_UNLOCK(devvp, LK_INTERLOCK, p);
607290650Shselasky	}
608290650Shselasky
609290650Shselasky	ronly = (mp->mnt_flag & MNT_RDONLY) != 0;
610290650Shselasky	vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY, p);
611290650Shselasky	error = VOP_OPEN(devvp, ronly ? FREAD : FREAD|FWRITE, FSCRED, p);
612290650Shselasky	VOP_UNLOCK(devvp, 0, p);
613290650Shselasky	if (error)
614290650Shselasky		return (error);
615290650Shselasky	if (devvp->v_rdev->si_iosize_max > mp->mnt_iosize_max)
616290650Shselasky		mp->mnt_iosize_max = devvp->v_rdev->si_iosize_max;
617290650Shselasky	if (mp->mnt_iosize_max > MAXPHYS)
618290650Shselasky		mp->mnt_iosize_max = MAXPHYS;
619290650Shselasky
620290650Shselasky	if (VOP_IOCTL(devvp, DIOCGPART, (caddr_t)&dpart, FREAD, cred, p) != 0)
621290650Shselasky		size = DEV_BSIZE;
622290650Shselasky	else
623290650Shselasky		size = dpart.disklab->d_secsize;
624290650Shselasky
625290650Shselasky	bp = NULL;
626290650Shselasky	ump = NULL;
627306233Shselasky	if ((error = bread(devvp, SBLOCK, SBSIZE, cred, &bp)) != 0)
628306233Shselasky		goto out;
629306233Shselasky	fs = (struct fs *)bp->b_data;
630306233Shselasky	if (fs->fs_magic != FS_MAGIC || fs->fs_bsize > MAXBSIZE ||
631306233Shselasky	    fs->fs_bsize < sizeof(struct fs)) {
632306233Shselasky		error = EINVAL;		/* XXX needs translation */
633306233Shselasky		goto out;
634306233Shselasky	}
635306233Shselasky	fs->fs_fmod = 0;
636306233Shselasky	fs->fs_flags &= ~FS_UNCLEAN;
637306233Shselasky	if (fs->fs_clean == 0) {
638306233Shselasky		fs->fs_flags |= FS_UNCLEAN;
639306233Shselasky		if (ronly || (mp->mnt_flag & MNT_FORCE)) {
640306233Shselasky			printf(
641306233Shselasky"WARNING: %s was not properly dismounted\n",
642306233Shselasky			    fs->fs_fsmnt);
643306233Shselasky		} else {
644306233Shselasky			printf(
645306233Shselasky"WARNING: R/W mount of %s denied.  Filesystem is not clean - run fsck\n",
646306233Shselasky			    fs->fs_fsmnt);
647306233Shselasky			error = EPERM;
648308678Shselasky			goto out;
649308678Shselasky		}
650308678Shselasky	}
651308678Shselasky	/* XXX updating 4.2 FFS superblocks trashes rotational layout tables */
652308678Shselasky	if (fs->fs_postblformat == FS_42POSTBLFMT && !ronly) {
653308678Shselasky		error = EROFS;          /* needs translation */
654308678Shselasky		goto out;
655308678Shselasky	}
656308678Shselasky	ump = malloc(sizeof *ump, M_UFSMNT, M_WAITOK);
657308678Shselasky	bzero((caddr_t)ump, sizeof *ump);
658308678Shselasky	ump->um_malloctype = malloctype;
659308678Shselasky	ump->um_i_effnlink_valid = 1;
660308678Shselasky	ump->um_fs = malloc((u_long)fs->fs_sbsize, M_UFSMNT,
661308678Shselasky	    M_WAITOK);
662308678Shselasky	ump->um_blkatoff = ffs_blkatoff;
663308678Shselasky	ump->um_truncate = ffs_truncate;
664308678Shselasky	ump->um_update = ffs_update;
665308678Shselasky	ump->um_valloc = ffs_valloc;
666308678Shselasky	ump->um_vfree = ffs_vfree;
667308678Shselasky	bcopy(bp->b_data, ump->um_fs, (u_int)fs->fs_sbsize);
668308678Shselasky	if (fs->fs_sbsize < SBSIZE)
669308678Shselasky		bp->b_flags |= B_INVAL;
670308678Shselasky	brelse(bp);
671308678Shselasky	bp = NULL;
672308678Shselasky	fs = ump->um_fs;
673308678Shselasky	fs->fs_ronly = ronly;
674308678Shselasky	if (ronly == 0) {
675308678Shselasky		fs->fs_fmod = 1;
676308678Shselasky		fs->fs_clean = 0;
677306233Shselasky	}
678306233Shselasky	size = fs->fs_cssize;
679306233Shselasky	blks = howmany(size, fs->fs_fsize);
680306233Shselasky	if (fs->fs_contigsumsize > 0)
681306233Shselasky		size += fs->fs_ncg * sizeof(int32_t);
682306233Shselasky	base = space = malloc((u_long)size, M_UFSMNT, M_WAITOK);
683306233Shselasky	for (i = 0; i < blks; i += fs->fs_frag) {
684306233Shselasky		size = fs->fs_bsize;
685306233Shselasky		if (i + fs->fs_frag > blks)
686306233Shselasky			size = (blks - i) * fs->fs_fsize;
687306233Shselasky		if ((error = bread(devvp, fsbtodb(fs, fs->fs_csaddr + i), size,
688306233Shselasky		    cred, &bp)) != 0) {
689306233Shselasky			free(base, M_UFSMNT);
690306233Shselasky			goto out;
691306233Shselasky		}
692306233Shselasky		bcopy(bp->b_data, space, (u_int)size);
693306233Shselasky		fs->fs_csp[fragstoblks(fs, i)] = (struct csum *)space;
694306233Shselasky		space += size;
695306233Shselasky		brelse(bp);
696290650Shselasky		bp = NULL;
697290650Shselasky	}
698290650Shselasky	if (fs->fs_contigsumsize > 0) {
699290650Shselasky		fs->fs_maxcluster = lp = (int32_t *)space;
700290650Shselasky		for (i = 0; i < fs->fs_ncg; i++)
701290650Shselasky			*lp++ = fs->fs_contigsumsize;
702290650Shselasky	}
703306233Shselasky	mp->mnt_data = (qaddr_t)ump;
704306233Shselasky	mp->mnt_stat.f_fsid.val[0] = fs->fs_id[0];
705306233Shselasky	mp->mnt_stat.f_fsid.val[1] = fs->fs_id[1];
706306233Shselasky	if (fs->fs_id[0] == 0 || fs->fs_id[1] == 0 ||
707306233Shselasky	    vfs_getvfs(&mp->mnt_stat.f_fsid))
708290650Shselasky		vfs_getnewfsid(mp);
709290650Shselasky	mp->mnt_maxsymlinklen = fs->fs_maxsymlinklen;
710290650Shselasky	mp->mnt_flag |= MNT_LOCAL;
711290650Shselasky	ump->um_mountp = mp;
712290650Shselasky	ump->um_dev = dev;
713290650Shselasky	ump->um_devvp = devvp;
714290650Shselasky	ump->um_nindir = fs->fs_nindir;
715290650Shselasky	ump->um_bptrtodb = fs->fs_fsbtodb;
716290650Shselasky	ump->um_seqinc = fs->fs_frag;
717290650Shselasky	for (i = 0; i < MAXQUOTAS; i++)
718290650Shselasky		ump->um_quotas[i] = NULLVP;
719290650Shselasky	devvp->v_specmountpoint = mp;
720290650Shselasky	ffs_oldfscompat(fs);
721290650Shselasky
722290650Shselasky	/*
723290650Shselasky	 * Set FS local "last mounted on" information (NULL pad)
724329200Shselasky	 */
725329200Shselasky	copystr(	mp->mnt_stat.f_mntonname,	/* mount point*/
726329200Shselasky			fs->fs_fsmnt,			/* copy area*/
727329200Shselasky			sizeof(fs->fs_fsmnt) - 1,	/* max size*/
728290650Shselasky			&strsize);			/* real size*/
729290650Shselasky	bzero( fs->fs_fsmnt + strsize, sizeof(fs->fs_fsmnt) - strsize);
730290650Shselasky
731290650Shselasky	if( mp->mnt_flag & MNT_ROOTFS) {
732290650Shselasky		/*
733290650Shselasky		 * Root mount; update timestamp in mount structure.
734290650Shselasky		 * this will be used by the common root mount code
735290650Shselasky		 * to update the system clock.
736290650Shselasky		 */
737290650Shselasky		mp->mnt_time = fs->fs_time;
738290650Shselasky	}
739290650Shselasky
740290650Shselasky	ump->um_savedmaxfilesize = fs->fs_maxfilesize;		/* XXX */
741290650Shselasky	maxfilesize = (u_int64_t)0x40000000 * fs->fs_bsize - 1;	/* XXX */
742290650Shselasky	if (fs->fs_maxfilesize > maxfilesize)			/* XXX */
743290650Shselasky		fs->fs_maxfilesize = maxfilesize;		/* XXX */
744290650Shselasky	if (ronly == 0) {
745290650Shselasky		if ((fs->fs_flags & FS_DOSOFTDEP) &&
746290650Shselasky		    (error = softdep_mount(devvp, mp, fs, cred)) != 0) {
747290650Shselasky			free(base, M_UFSMNT);
748290650Shselasky			goto out;
749290650Shselasky		}
750290650Shselasky		fs->fs_clean = 0;
751321992Shselasky		(void) ffs_sbupdate(ump, MNT_WAIT);
752321992Shselasky	}
753290650Shselasky	return (0);
754290650Shselaskyout:
755290650Shselasky	devvp->v_specmountpoint = NULL;
756290650Shselasky	if (bp)
757290650Shselasky		brelse(bp);
758290650Shselasky	(void)VOP_CLOSE(devvp, ronly ? FREAD : FREAD|FWRITE, cred, p);
759329204Shselasky	if (ump) {
760329204Shselasky		free(ump->um_fs, M_UFSMNT);
761290650Shselasky		free(ump, M_UFSMNT);
762290650Shselasky		mp->mnt_data = (qaddr_t)0;
763290650Shselasky	}
764290650Shselasky	return (error);
765290650Shselasky}
766290650Shselasky
767308678Shselasky/*
768308678Shselasky * Sanity checks for old file systems.
769308678Shselasky *
770321992Shselasky * XXX - goes away some day.
771321992Shselasky */
772308678Shselaskystatic int
773290650Shselaskyffs_oldfscompat(fs)
774290650Shselasky	struct fs *fs;
775290650Shselasky{
776290650Shselasky
777290650Shselasky	fs->fs_npsect = max(fs->fs_npsect, fs->fs_nsect);	/* XXX */
778290650Shselasky	fs->fs_interleave = max(fs->fs_interleave, 1);		/* XXX */
779290650Shselasky	if (fs->fs_postblformat == FS_42POSTBLFMT)		/* XXX */
780290650Shselasky		fs->fs_nrpos = 8;				/* XXX */
781290650Shselasky	if (fs->fs_inodefmt < FS_44INODEFMT) {			/* XXX */
782290650Shselasky#if 0
783290650Shselasky		int i;						/* XXX */
784290650Shselasky		u_int64_t sizepb = fs->fs_bsize;		/* XXX */
785290650Shselasky								/* XXX */
786290650Shselasky		fs->fs_maxfilesize = fs->fs_bsize * NDADDR - 1;	/* XXX */
787290650Shselasky		for (i = 0; i < NIADDR; i++) {			/* XXX */
788290650Shselasky			sizepb *= NINDIR(fs);			/* XXX */
789290650Shselasky			fs->fs_maxfilesize += sizepb;		/* XXX */
790290650Shselasky		}						/* XXX */
791290650Shselasky#endif
792306233Shselasky		fs->fs_maxfilesize = (u_quad_t) 1LL << 39;
793306233Shselasky		fs->fs_qbmask = ~fs->fs_bmask;			/* XXX */
794306233Shselasky		fs->fs_qfmask = ~fs->fs_fmask;			/* XXX */
795290650Shselasky	}							/* XXX */
796306233Shselasky	return (0);
797306233Shselasky}
798290650Shselasky
799290650Shselasky/*
800290650Shselasky * unmount system call
801290650Shselasky */
802290650Shselaskyint
803290650Shselaskyffs_unmount(mp, mntflags, p)
804290650Shselasky	struct mount *mp;
805290650Shselasky	int mntflags;
806290650Shselasky	struct proc *p;
807290650Shselasky{
808290650Shselasky	register struct ufsmount *ump;
809290650Shselasky	register struct fs *fs;
810290650Shselasky	int error, flags;
811290650Shselasky
812290650Shselasky	flags = 0;
813290650Shselasky	if (mntflags & MNT_FORCE) {
814290650Shselasky		flags |= FORCECLOSE;
815290650Shselasky	}
816290650Shselasky	if (mp->mnt_flag & MNT_SOFTDEP) {
817290650Shselasky		if ((error = softdep_flushfiles(mp, flags, p)) != 0)
818290650Shselasky			return (error);
819290650Shselasky	} else {
820290650Shselasky		if ((error = ffs_flushfiles(mp, flags, p)) != 0)
821290650Shselasky			return (error);
822290650Shselasky	}
823290650Shselasky	ump = VFSTOUFS(mp);
824290650Shselasky	fs = ump->um_fs;
825290650Shselasky	if (fs->fs_ronly == 0) {
826290650Shselasky		fs->fs_clean = fs->fs_flags & FS_UNCLEAN ? 0 : 1;
827290650Shselasky		error = ffs_sbupdate(ump, MNT_WAIT);
828290650Shselasky		if (error) {
829290650Shselasky			fs->fs_clean = 0;
830290650Shselasky			return (error);
831290650Shselasky		}
832290650Shselasky	}
833290650Shselasky	ump->um_devvp->v_specmountpoint = NULL;
834290650Shselasky
835290650Shselasky	vinvalbuf(ump->um_devvp, V_SAVE, NOCRED, p, 0, 0);
836290650Shselasky	error = VOP_CLOSE(ump->um_devvp, fs->fs_ronly ? FREAD : FREAD|FWRITE,
837290650Shselasky		NOCRED, p);
838290650Shselasky
839290650Shselasky	vrele(ump->um_devvp);
840290650Shselasky
841290650Shselasky	free(fs->fs_csp[0], M_UFSMNT);
842290650Shselasky	free(fs, M_UFSMNT);
843290650Shselasky	free(ump, M_UFSMNT);
844306233Shselasky	mp->mnt_data = (qaddr_t)0;
845306233Shselasky	mp->mnt_flag &= ~MNT_LOCAL;
846306233Shselasky	return (error);
847290650Shselasky}
848306233Shselasky
849290650Shselasky/*
850306233Shselasky * Flush out all the files in a filesystem.
851306233Shselasky */
852306233Shselaskyint
853290650Shselaskyffs_flushfiles(mp, flags, p)
854290650Shselasky	register struct mount *mp;
855306233Shselasky	int flags;
856290650Shselasky	struct proc *p;
857290650Shselasky{
858306233Shselasky	register struct ufsmount *ump;
859290650Shselasky	int error;
860290650Shselasky
861306233Shselasky	ump = VFSTOUFS(mp);
862290650Shselasky#ifdef QUOTA
863290650Shselasky	if (mp->mnt_flag & MNT_QUOTA) {
864290650Shselasky		int i;
865290650Shselasky		error = vflush(mp, NULLVP, SKIPSYSTEM|flags);
866290650Shselasky		if (error)
867290650Shselasky			return (error);
868290650Shselasky		for (i = 0; i < MAXQUOTAS; i++) {
869290650Shselasky			if (ump->um_quotas[i] == NULLVP)
870290650Shselasky				continue;
871290650Shselasky			quotaoff(p, mp, i);
872290650Shselasky		}
873290650Shselasky		/*
874290650Shselasky		 * Here we fall through to vflush again to ensure
875290650Shselasky		 * that we have gotten rid of all the system vnodes.
876290650Shselasky		 */
877290650Shselasky	}
878290650Shselasky#endif
879290650Shselasky        /*
880290650Shselasky	 * Flush all the files.
881290650Shselasky	 */
882290650Shselasky	if ((error = vflush(mp, NULL, flags)) != 0)
883290650Shselasky		return (error);
884290650Shselasky	/*
885290650Shselasky	 * Flush filesystem metadata.
886290650Shselasky	 */
887290650Shselasky	vn_lock(ump->um_devvp, LK_EXCLUSIVE | LK_RETRY, p);
888290650Shselasky	error = VOP_FSYNC(ump->um_devvp, p->p_ucred, MNT_WAIT, p);
889290650Shselasky	VOP_UNLOCK(ump->um_devvp, 0, p);
890290650Shselasky	return (error);
891290650Shselasky}
892290650Shselasky
893290650Shselasky/*
894290650Shselasky * Get file system statistics.
895290650Shselasky */
896290650Shselaskyint
897290650Shselaskyffs_statfs(mp, sbp, p)
898290650Shselasky	struct mount *mp;
899290650Shselasky	register struct statfs *sbp;
900290650Shselasky	struct proc *p;
901290650Shselasky{
902290650Shselasky	register struct ufsmount *ump;
903290650Shselasky	register struct fs *fs;
904290650Shselasky
905290650Shselasky	ump = VFSTOUFS(mp);
906290650Shselasky	fs = ump->um_fs;
907290650Shselasky	if (fs->fs_magic != FS_MAGIC)
908290650Shselasky		panic("ffs_statfs");
909290650Shselasky	sbp->f_bsize = fs->fs_fsize;
910290650Shselasky	sbp->f_iosize = fs->fs_bsize;
911290650Shselasky	sbp->f_blocks = fs->fs_dsize;
912290650Shselasky	sbp->f_bfree = fs->fs_cstotal.cs_nbfree * fs->fs_frag +
913290650Shselasky		fs->fs_cstotal.cs_nffree;
914290650Shselasky	sbp->f_bavail = freespace(fs, fs->fs_minfree);
915290650Shselasky	sbp->f_files =  fs->fs_ncg * fs->fs_ipg - ROOTINO;
916290650Shselasky	sbp->f_ffree = fs->fs_cstotal.cs_nifree;
917290650Shselasky	if (sbp != &mp->mnt_stat) {
918290650Shselasky		sbp->f_type = mp->mnt_vfc->vfc_typenum;
919290650Shselasky		bcopy((caddr_t)mp->mnt_stat.f_mntonname,
920290650Shselasky			(caddr_t)&sbp->f_mntonname[0], MNAMELEN);
921290650Shselasky		bcopy((caddr_t)mp->mnt_stat.f_mntfromname,
922290650Shselasky			(caddr_t)&sbp->f_mntfromname[0], MNAMELEN);
923290650Shselasky	}
924290650Shselasky	return (0);
925290650Shselasky}
926290650Shselasky
927290650Shselasky/*
928290650Shselasky * Go through the disk queues to initiate sandbagged IO;
929290650Shselasky * go through the inodes to write those that have been modified;
930290650Shselasky * initiate the writing of the super block if it has been modified.
931290650Shselasky *
932290650Shselasky * Note: we are always called with the filesystem marked `MPBUSY'.
933290650Shselasky */
934290650Shselaskyint
935290650Shselaskyffs_sync(mp, waitfor, cred, p)
936290650Shselasky	struct mount *mp;
937290650Shselasky	int waitfor;
938290650Shselasky	struct ucred *cred;
939290650Shselasky	struct proc *p;
940290650Shselasky{
941290650Shselasky	struct vnode *nvp, *vp;
942290650Shselasky	struct inode *ip;
943290650Shselasky	struct ufsmount *ump = VFSTOUFS(mp);
944290650Shselasky	struct fs *fs;
945290650Shselasky	int error, allerror = 0;
946290650Shselasky
947290650Shselasky	fs = ump->um_fs;
948290650Shselasky	if (fs->fs_fmod != 0 && fs->fs_ronly != 0) {		/* XXX */
949290650Shselasky		printf("fs = %s\n", fs->fs_fsmnt);
950290650Shselasky		panic("ffs_sync: rofs mod");
951290650Shselasky	}
952290650Shselasky	/*
953290650Shselasky	 * Write back each (modified) inode.
954290650Shselasky	 */
955290650Shselasky	simple_lock(&mntvnode_slock);
956290650Shselaskyloop:
957306233Shselasky	for (vp = mp->mnt_vnodelist.lh_first; vp != NULL; vp = nvp) {
958306233Shselasky		/*
959306233Shselasky		 * If the vnode that we are about to sync is no longer
960290650Shselasky		 * associated with this mount point, start over.
961290650Shselasky		 */
962290650Shselasky		if (vp->v_mount != mp)
963290650Shselasky			goto loop;
964306233Shselasky		simple_lock(&vp->v_interlock);
965306233Shselasky		nvp = vp->v_mntvnodes.le_next;
966321992Shselasky		ip = VTOI(vp);
967321992Shselasky		if ((vp->v_type == VNON) || (((ip->i_flag &
968290650Shselasky		     (IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE)) == 0) &&
969290650Shselasky		    (TAILQ_EMPTY(&vp->v_dirtyblkhd) || (waitfor == MNT_LAZY)))) {
970290650Shselasky			simple_unlock(&vp->v_interlock);
971290650Shselasky			continue;
972290650Shselasky		}
973290650Shselasky		if (vp->v_type != VCHR) {
974290650Shselasky			simple_unlock(&mntvnode_slock);
975290650Shselasky			error =
976290650Shselasky			  vget(vp, LK_EXCLUSIVE | LK_NOWAIT | LK_INTERLOCK, p);
977290650Shselasky			if (error) {
978290650Shselasky				simple_lock(&mntvnode_slock);
979290650Shselasky				if (error == ENOENT)
980290650Shselasky					goto loop;
981290650Shselasky				continue;
982290650Shselasky			}
983290650Shselasky			if ((error = VOP_FSYNC(vp, cred, waitfor, p)) != 0)
984290650Shselasky				allerror = error;
985290650Shselasky			VOP_UNLOCK(vp, 0, p);
986290650Shselasky			vrele(vp);
987290650Shselasky			simple_lock(&mntvnode_slock);
988290650Shselasky		} else {
989290650Shselasky			simple_unlock(&mntvnode_slock);
990290650Shselasky			simple_unlock(&vp->v_interlock);
991306233Shselasky			/* UFS_UPDATE(vp, waitfor == MNT_WAIT); */
992306233Shselasky			UFS_UPDATE(vp, 0);
993306233Shselasky			simple_lock(&mntvnode_slock);
994290650Shselasky		}
995290650Shselasky	}
996290650Shselasky	simple_unlock(&mntvnode_slock);
997290650Shselasky	/*
998290650Shselasky	 * Force stale file system control information to be flushed.
999290650Shselasky	 */
1000290650Shselasky	if (waitfor != MNT_LAZY) {
1001290650Shselasky		if (ump->um_mountp->mnt_flag & MNT_SOFTDEP)
1002290650Shselasky			waitfor = MNT_NOWAIT;
1003290650Shselasky		vn_lock(ump->um_devvp, LK_EXCLUSIVE | LK_RETRY, p);
1004290650Shselasky		if ((error = VOP_FSYNC(ump->um_devvp, cred, waitfor, p)) != 0)
1005290650Shselasky			allerror = error;
1006290650Shselasky		VOP_UNLOCK(ump->um_devvp, 0, p);
1007290650Shselasky	}
1008290650Shselasky#ifdef QUOTA
1009290650Shselasky	qsync(mp);
1010290650Shselasky#endif
1011306233Shselasky	/*
1012306233Shselasky	 * Write back modified superblock.
1013306233Shselasky	 */
1014306233Shselasky	if (fs->fs_fmod != 0 && (error = ffs_sbupdate(ump, waitfor)) != 0)
1015306233Shselasky		allerror = error;
1016290650Shselasky	return (allerror);
1017290650Shselasky}
1018290650Shselasky
1019290650Shselasky/*
1020306233Shselasky * Look up a FFS dinode number to find its incore vnode, otherwise read it
1021290650Shselasky * in from disk.  If it is in core, wait for the lock bit to clear, then
1022290650Shselasky * return the inode locked.  Detection and handling of mount points must be
1023306233Shselasky * done by the calling routine.
1024290650Shselasky */
1025290650Shselaskystatic int ffs_inode_hash_lock;
1026290650Shselasky
1027290650Shselaskyint
1028306233Shselaskyffs_vget(mp, ino, vpp)
1029290650Shselasky	struct mount *mp;
1030290650Shselasky	ino_t ino;
1031290650Shselasky	struct vnode **vpp;
1032306233Shselasky{
1033290650Shselasky	struct fs *fs;
1034290650Shselasky	struct inode *ip;
1035290650Shselasky	struct ufsmount *ump;
1036290650Shselasky	struct buf *bp;
1037321992Shselasky	struct vnode *vp;
1038321992Shselasky	dev_t dev;
1039321992Shselasky	int error;
1040290650Shselasky
1041290650Shselasky	ump = VFSTOUFS(mp);
1042290650Shselasky	dev = ump->um_dev;
1043290650Shselaskyrestart:
1044290650Shselasky	if ((*vpp = ufs_ihashget(dev, ino)) != NULL) {
1045290650Shselasky		return (0);
1046290650Shselasky	}
1047290650Shselasky
1048290650Shselasky	/*
1049329204Shselasky	 * Lock out the creation of new entries in the FFS hash table in
1050329204Shselasky	 * case getnewvnode() or MALLOC() blocks, otherwise a duplicate
1051290650Shselasky	 * may occur!
1052290650Shselasky	 */
1053329204Shselasky	if (ffs_inode_hash_lock) {
1054329204Shselasky		while (ffs_inode_hash_lock) {
1055290650Shselasky			ffs_inode_hash_lock = -1;
1056290650Shselasky			tsleep(&ffs_inode_hash_lock, PVM, "ffsvgt", 0);
1057290650Shselasky		}
1058290650Shselasky		goto restart;
1059290650Shselasky	}
1060306233Shselasky	ffs_inode_hash_lock = 1;
1061290650Shselasky
1062306233Shselasky	/*
1063290650Shselasky	 * If this MALLOC() is performed after the getnewvnode()
1064290650Shselasky	 * it might block, leaving a vnode with a NULL v_data to be
1065290650Shselasky	 * found by ffs_sync() if a sync happens to fire right then,
1066290650Shselasky	 * which will cause a panic because ffs_sync() blindly
1067290650Shselasky	 * dereferences vp->v_data (as well it should).
1068306233Shselasky	 */
1069290650Shselasky	MALLOC(ip, struct inode *, sizeof(struct inode),
1070306233Shselasky	    ump->um_malloctype, M_WAITOK);
1071290650Shselasky
1072306233Shselasky	/* Allocate a new vnode/inode. */
1073290650Shselasky	error = getnewvnode(VT_UFS, mp, ffs_vnodeop_p, &vp);
1074290650Shselasky	if (error) {
1075290650Shselasky		if (ffs_inode_hash_lock < 0)
1076290650Shselasky			wakeup(&ffs_inode_hash_lock);
1077290650Shselasky		ffs_inode_hash_lock = 0;
1078290650Shselasky		*vpp = NULL;
1079290650Shselasky		FREE(ip, ump->um_malloctype);
1080290650Shselasky		return (error);
1081290650Shselasky	}
1082290650Shselasky	bzero((caddr_t)ip, sizeof(struct inode));
1083290650Shselasky	lockinit(&ip->i_lock, PINOD, "inode", 0, 0);
1084290650Shselasky	vp->v_data = ip;
1085290650Shselasky	ip->i_vnode = vp;
1086290650Shselasky	ip->i_fs = fs = ump->um_fs;
1087290650Shselasky	ip->i_dev = dev;
1088290650Shselasky	ip->i_number = ino;
1089290650Shselasky#ifdef QUOTA
1090290650Shselasky	{
1091290650Shselasky		int i;
1092290650Shselasky		for (i = 0; i < MAXQUOTAS; i++)
1093290650Shselasky			ip->i_dquot[i] = NODQUOT;
1094290650Shselasky	}
1095290650Shselasky#endif
1096290650Shselasky	/*
1097290650Shselasky	 * Put it onto its hash chain and lock it so that other requests for
1098290650Shselasky	 * this inode will block if they arrive while we are sleeping waiting
1099290650Shselasky	 * for old data structures to be purged or for the contents of the
1100290650Shselasky	 * disk portion of this inode to be read.
1101290650Shselasky	 */
1102290650Shselasky	ufs_ihashins(ip);
1103290650Shselasky
1104290650Shselasky	if (ffs_inode_hash_lock < 0)
1105290650Shselasky		wakeup(&ffs_inode_hash_lock);
1106290650Shselasky	ffs_inode_hash_lock = 0;
1107290650Shselasky
1108290650Shselasky	/* Read in the disk contents for the inode, copy into the inode. */
1109290650Shselasky	error = bread(ump->um_devvp, fsbtodb(fs, ino_to_fsba(fs, ino)),
1110290650Shselasky	    (int)fs->fs_bsize, NOCRED, &bp);
1111290650Shselasky	if (error) {
1112290650Shselasky		/*
1113290650Shselasky		 * The inode does not contain anything useful, so it would
1114290650Shselasky		 * be misleading to leave it on its hash chain. With mode
1115290650Shselasky		 * still zero, it will be unlinked and returned to the free
1116290650Shselasky		 * list by vput().
1117290650Shselasky		 */
1118290650Shselasky		brelse(bp);
1119290650Shselasky		vput(vp);
1120290650Shselasky		*vpp = NULL;
1121290650Shselasky		return (error);
1122290650Shselasky	}
1123290650Shselasky	ip->i_din = *((struct dinode *)bp->b_data + ino_to_fsbo(fs, ino));
1124290650Shselasky	if (DOINGSOFTDEP(vp))
1125290650Shselasky		softdep_load_inodeblock(ip);
1126290650Shselasky	else
1127290650Shselasky		ip->i_effnlink = ip->i_nlink;
1128290650Shselasky	bqrelse(bp);
1129321992Shselasky
1130321992Shselasky	/*
1131290650Shselasky	 * Initialize the vnode from the inode, check for aliases.
1132290650Shselasky	 * Note that the underlying vnode may have changed.
1133290650Shselasky	 */
1134290650Shselasky	error = ufs_vinit(mp, ffs_specop_p, ffs_fifoop_p, &vp);
1135290650Shselasky	if (error) {
1136290650Shselasky		vput(vp);
1137290650Shselasky		*vpp = NULL;
1138290650Shselasky		return (error);
1139290650Shselasky	}
1140290650Shselasky	/*
1141290650Shselasky	 * Finish inode initialization now that aliasing has been resolved.
1142290650Shselasky	 */
1143290650Shselasky	ip->i_devvp = ump->um_devvp;
1144290650Shselasky	VREF(ip->i_devvp);
1145290650Shselasky	/*
1146306233Shselasky	 * Set up a generation number for this inode if it does not
1147290650Shselasky	 * already have one. This should only happen on old filesystems.
1148306233Shselasky	 */
1149290650Shselasky	if (ip->i_gen == 0) {
1150306233Shselasky		ip->i_gen = random() / 2 + 1;
1151306233Shselasky		if ((vp->v_mount->mnt_flag & MNT_RDONLY) == 0)
1152290650Shselasky			ip->i_flag |= IN_MODIFIED;
1153290650Shselasky	}
1154290650Shselasky	/*
1155290650Shselasky	 * Ensure that uid and gid are correct. This is a temporary
1156290650Shselasky	 * fix until fsck has been changed to do the update.
1157290650Shselasky	 */
1158290650Shselasky	if (fs->fs_inodefmt < FS_44INODEFMT) {		/* XXX */
1159290650Shselasky		ip->i_uid = ip->i_din.di_ouid;		/* XXX */
1160290650Shselasky		ip->i_gid = ip->i_din.di_ogid;		/* XXX */
1161290650Shselasky	}						/* XXX */
1162290650Shselasky
1163290650Shselasky	*vpp = vp;
1164290650Shselasky	return (0);
1165306233Shselasky}
1166306233Shselasky
1167306233Shselasky/*
1168306233Shselasky * File handle to vnode
1169306233Shselasky *
1170306233Shselasky * Have to be really careful about stale file handles:
1171290650Shselasky * - check that the inode number is valid
1172290650Shselasky * - call ffs_vget() to get the locked inode
1173290650Shselasky * - check for an unallocated inode (i_mode == 0)
1174290650Shselasky * - check that the given client host has export rights and return
1175290650Shselasky *   those rights via. exflagsp and credanonp
1176290650Shselasky */
1177290650Shselaskyint
1178290650Shselaskyffs_fhtovp(mp, fhp, vpp)
1179290650Shselasky	register struct mount *mp;
1180290650Shselasky	struct fid *fhp;
1181290650Shselasky	struct vnode **vpp;
1182290650Shselasky{
1183290650Shselasky	register struct ufid *ufhp;
1184290650Shselasky	struct fs *fs;
1185290650Shselasky
1186290650Shselasky	ufhp = (struct ufid *)fhp;
1187290650Shselasky	fs = VFSTOUFS(mp)->um_fs;
1188290650Shselasky	if (ufhp->ufid_ino < ROOTINO ||
1189290650Shselasky	    ufhp->ufid_ino >= fs->fs_ncg * fs->fs_ipg)
1190290650Shselasky		return (ESTALE);
1191290650Shselasky	return (ufs_fhtovp(mp, ufhp, vpp));
1192290650Shselasky}
1193290650Shselasky
1194290650Shselasky/*
1195290650Shselasky * Vnode pointer to File handle
1196290650Shselasky */
1197290650Shselasky/* ARGSUSED */
1198290650Shselaskyint
1199290650Shselaskyffs_vptofh(vp, fhp)
1200290650Shselasky	struct vnode *vp;
1201290650Shselasky	struct fid *fhp;
1202290650Shselasky{
1203290650Shselasky	register struct inode *ip;
1204290650Shselasky	register struct ufid *ufhp;
1205290650Shselasky
1206290650Shselasky	ip = VTOI(vp);
1207290650Shselasky	ufhp = (struct ufid *)fhp;
1208306233Shselasky	ufhp->ufid_len = sizeof(struct ufid);
1209306233Shselasky	ufhp->ufid_ino = ip->i_number;
1210306233Shselasky	ufhp->ufid_gen = ip->i_gen;
1211306233Shselasky	return (0);
1212306233Shselasky}
1213290650Shselasky
1214290650Shselasky/*
1215290650Shselasky * Initialize the filesystem; just use ufs_init.
1216290650Shselasky */
1217290650Shselaskystatic int
1218290650Shselaskyffs_init(vfsp)
1219290650Shselasky	struct vfsconf *vfsp;
1220290650Shselasky{
1221290650Shselasky
1222290650Shselasky	softdep_initialize();
1223290650Shselasky	return (ufs_init(vfsp));
1224290650Shselasky}
1225290650Shselasky
1226290650Shselasky/*
1227290650Shselasky * Write a superblock and associated information back to disk.
1228290650Shselasky */
1229290650Shselaskystatic int
1230290650Shselaskyffs_sbupdate(mp, waitfor)
1231290650Shselasky	struct ufsmount *mp;
1232290650Shselasky	int waitfor;
1233290650Shselasky{
1234290650Shselasky	register struct fs *dfs, *fs = mp->um_fs;
1235290650Shselasky	register struct buf *bp;
1236290650Shselasky	int blks;
1237290650Shselasky	caddr_t space;
1238290650Shselasky	int i, size, error, allerror = 0;
1239290650Shselasky
1240290650Shselasky	/*
1241290650Shselasky	 * First write back the summary information.
1242290650Shselasky	 */
1243290650Shselasky	blks = howmany(fs->fs_cssize, fs->fs_fsize);
1244290650Shselasky	space = (caddr_t)fs->fs_csp[0];
1245290650Shselasky	for (i = 0; i < blks; i += fs->fs_frag) {
1246290650Shselasky		size = fs->fs_bsize;
1247290650Shselasky		if (i + fs->fs_frag > blks)
1248290650Shselasky			size = (blks - i) * fs->fs_fsize;
1249290650Shselasky		bp = getblk(mp->um_devvp, fsbtodb(fs, fs->fs_csaddr + i),
1250290650Shselasky		    size, 0, 0);
1251290650Shselasky		bcopy(space, bp->b_data, (u_int)size);
1252290650Shselasky		space += size;
1253290650Shselasky		if (waitfor != MNT_WAIT)
1254290650Shselasky			bawrite(bp);
1255290650Shselasky		else if ((error = bwrite(bp)) != 0)
1256290650Shselasky			allerror = error;
1257290650Shselasky	}
1258290650Shselasky	/*
1259290650Shselasky	 * Now write back the superblock itself. If any errors occurred
1260290650Shselasky	 * up to this point, then fail so that the superblock avoids
1261290650Shselasky	 * being written out as clean.
1262290650Shselasky	 */
1263290650Shselasky	if (allerror)
1264290650Shselasky		return (allerror);
1265290650Shselasky	bp = getblk(mp->um_devvp, SBLOCK, (int)fs->fs_sbsize, 0, 0);
1266290650Shselasky	fs->fs_fmod = 0;
1267290650Shselasky	fs->fs_time = time_second;
1268290650Shselasky	bcopy((caddr_t)fs, bp->b_data, (u_int)fs->fs_sbsize);
1269290650Shselasky	/* Restore compatibility to old file systems.		   XXX */
1270290650Shselasky	dfs = (struct fs *)bp->b_data;				/* XXX */
1271290650Shselasky	if (fs->fs_postblformat == FS_42POSTBLFMT)		/* XXX */
1272290650Shselasky		dfs->fs_nrpos = -1;				/* XXX */
1273290650Shselasky	if (fs->fs_inodefmt < FS_44INODEFMT) {			/* XXX */
1274290650Shselasky		int32_t *lp, tmp;				/* XXX */
1275290650Shselasky								/* XXX */
1276290650Shselasky		lp = (int32_t *)&dfs->fs_qbmask;		/* XXX */
1277290650Shselasky		tmp = lp[4];					/* XXX */
1278290650Shselasky		for (i = 4; i > 0; i--)				/* XXX */
1279290650Shselasky			lp[i] = lp[i-1];			/* XXX */
1280290650Shselasky		lp[0] = tmp;					/* XXX */
1281290650Shselasky	}							/* XXX */
1282290650Shselasky	dfs->fs_maxfilesize = mp->um_savedmaxfilesize;		/* XXX */
1283290650Shselasky	if (waitfor != MNT_WAIT)
1284290650Shselasky		bawrite(bp);
1285290650Shselasky	else if ((error = bwrite(bp)) != 0)
1286290650Shselasky		allerror = error;
1287290650Shselasky	return (allerror);
1288290650Shselasky}
1289290650Shselasky