ext2_vfsops.c revision 187395
1139778Simp/*-
212115Sdyson *  modified for EXT2FS support in Lites 1.1
312115Sdyson *
412115Sdyson *  Aug 1995, Godmar Back (gback@cs.utah.edu)
512115Sdyson *  University of Utah, Department of Computer Science
612115Sdyson */
7139778Simp/*-
812115Sdyson * Copyright (c) 1989, 1991, 1993, 1994
912115Sdyson *	The Regents of the University of California.  All rights reserved.
1012115Sdyson *
1112115Sdyson * Redistribution and use in source and binary forms, with or without
1212115Sdyson * modification, are permitted provided that the following conditions
1312115Sdyson * are met:
1412115Sdyson * 1. Redistributions of source code must retain the above copyright
1512115Sdyson *    notice, this list of conditions and the following disclaimer.
1612115Sdyson * 2. Redistributions in binary form must reproduce the above copyright
1712115Sdyson *    notice, this list of conditions and the following disclaimer in the
1812115Sdyson *    documentation and/or other materials provided with the distribution.
1912115Sdyson * 4. Neither the name of the University nor the names of its contributors
2012115Sdyson *    may be used to endorse or promote products derived from this software
2112115Sdyson *    without specific prior written permission.
2212115Sdyson *
2312115Sdyson * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2412115Sdyson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2512115Sdyson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2612115Sdyson * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2712115Sdyson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2812115Sdyson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2912115Sdyson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3012115Sdyson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3112115Sdyson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3212115Sdyson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3312115Sdyson * SUCH DAMAGE.
3412115Sdyson *
3512115Sdyson *	@(#)ffs_vfsops.c	8.8 (Berkeley) 4/18/94
3693016Sbde * $FreeBSD: head/sys/gnu/fs/ext2fs/ext2_vfsops.c 187395 2009-01-18 14:04:56Z stas $
3712115Sdyson */
3812115Sdyson
39147408Simp/*-
40147408Simp * COPYRIGHT.INFO says this has some GPL'd code from ext2_super.c in it
41147408Simp *
42147408Simp *      This program is free software; you can redistribute it and/or modify
43147408Simp *      it under the terms of the GNU General Public License as published by
44147408Simp *      the Free Software Foundation; either version 2 of the License.
45147408Simp *
46147408Simp *      This program is distributed in the hope that it will be useful,
47147408Simp *      but WITHOUT ANY WARRANTY; without even the implied warranty of
48147408Simp *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
49147408Simp *      GNU General Public License for more details.
50147408Simp *
51147408Simp *      You should have received a copy of the GNU General Public License
52147408Simp *      along with this program; if not, write to the Free Software
53147408Simp *      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
54147408Simp *
55147408Simp */
56147408Simp
5712115Sdyson#include <sys/param.h>
5812115Sdyson#include <sys/systm.h>
5912115Sdyson#include <sys/namei.h>
60164033Srwatson#include <sys/priv.h>
6112115Sdyson#include <sys/proc.h>
6212115Sdyson#include <sys/kernel.h>
6312115Sdyson#include <sys/vnode.h>
6412115Sdyson#include <sys/mount.h>
6560041Sphk#include <sys/bio.h>
6612115Sdyson#include <sys/buf.h>
6729906Skato#include <sys/conf.h>
6824131Sbde#include <sys/fcntl.h>
6912115Sdyson#include <sys/malloc.h>
7012115Sdyson#include <sys/stat.h>
7171576Sjasone#include <sys/mutex.h>
7212115Sdyson
73137039Sphk#include <geom/geom.h>
74137039Sphk#include <geom/geom_vfs.h>
75137039Sphk
76147393Srodrigc#include <gnu/fs/ext2fs/ext2_mount.h>
77147393Srodrigc#include <gnu/fs/ext2fs/inode.h>
7812115Sdyson
79147393Srodrigc#include <gnu/fs/ext2fs/fs.h>
80147393Srodrigc#include <gnu/fs/ext2fs/ext2_extern.h>
81147393Srodrigc#include <gnu/fs/ext2fs/ext2_fs.h>
82147393Srodrigc#include <gnu/fs/ext2fs/ext2_fs_sb.h>
8312115Sdyson
8492728Salfredstatic int ext2_flushfiles(struct mount *mp, int flags, struct thread *td);
85183754Sattiliostatic int ext2_mountfs(struct vnode *, struct mount *);
86140736Sphkstatic int ext2_reload(struct mount *mp, struct thread *td);
8796749Siedowsestatic int ext2_sbupdate(struct ext2mount *, int);
8812115Sdyson
89116271Sphkstatic vfs_unmount_t		ext2_unmount;
90116271Sphkstatic vfs_root_t		ext2_root;
91116271Sphkstatic vfs_statfs_t		ext2_statfs;
92116271Sphkstatic vfs_sync_t		ext2_sync;
93116271Sphkstatic vfs_vget_t		ext2_vget;
94116271Sphkstatic vfs_fhtovp_t		ext2_fhtovp;
95132902Sphkstatic vfs_mount_t		ext2_mount;
96116271Sphk
97151897SrwatsonMALLOC_DEFINE(M_EXT2NODE, "ext2_node", "EXT2 vnode private part");
98151897Srwatsonstatic MALLOC_DEFINE(M_EXT2MNT, "ext2_mount", "EXT2 mount structure");
9930280Sphk
10012911Sphkstatic struct vfsops ext2fs_vfsops = {
101116271Sphk	.vfs_fhtovp =		ext2_fhtovp,
102132902Sphk	.vfs_mount =		ext2_mount,
103116271Sphk	.vfs_root =		ext2_root,	/* root inode via vget */
104116271Sphk	.vfs_statfs =		ext2_statfs,
105116271Sphk	.vfs_sync =		ext2_sync,
106116271Sphk	.vfs_unmount =		ext2_unmount,
107116271Sphk	.vfs_vget =		ext2_vget,
10812115Sdyson};
10912115Sdyson
11038909SbdeVFS_SET(ext2fs_vfsops, ext2fs, 0);
111137039Sphk
11212115Sdyson#define bsd_malloc malloc
11312115Sdyson#define bsd_free free
11412115Sdyson
115130585Sphkstatic int	ext2_check_sb_compat(struct ext2_super_block *es, struct cdev *dev,
11693014Sbde		    int ronly);
11792728Salfredstatic int	compute_sb_data(struct vnode * devvp,
11893014Sbde		    struct ext2_super_block * es, struct ext2_sb_info * fs);
11916322Sgpalmer
120177645Sjhbstatic const char *ext2_opts[] = { "from", "export", "acls", "noexec",
121171852Sjhb    "noatime", "union", "suiddir", "multilabel", "nosymfollow",
122171852Sjhb    "noclusterr", "noclusterw", "force", NULL };
123164385Srodrigc
12412115Sdyson/*
12512115Sdyson * VFS Operations.
12612115Sdyson *
12712115Sdyson * mount system call
12812115Sdyson */
12912911Sphkstatic int
130132902Sphkext2_mount(mp, td)
13196752Siedowse	struct mount *mp;
13283366Sjulian	struct thread *td;
13312115Sdyson{
13497255Smux	struct vfsoptlist *opts;
13512115Sdyson	struct vnode *devvp;
13696749Siedowse	struct ext2mount *ump = 0;
13796752Siedowse	struct ext2_sb_info *fs;
13897255Smux	char *path, *fspec;
13997255Smux	int error, flags, len;
140184413Strasz	accmode_t accmode;
141132902Sphk	struct nameidata nd, *ndp = &nd;
14212115Sdyson
14397255Smux	opts = mp->mnt_optnew;
14497255Smux
145138493Sphk	if (vfs_filteropt(opts, ext2_opts))
146138493Sphk		return (EINVAL);
147138493Sphk
14897255Smux	vfs_getopt(opts, "fspath", (void **)&path, NULL);
14973286Sadrian	/* Double-check the length of path.. */
15073286Sadrian	if (strlen(path) >= MAXMNTLEN - 1)
15173286Sadrian		return (ENAMETOOLONG);
15297255Smux
15397255Smux	fspec = NULL;
15497255Smux	error = vfs_getopt(opts, "from", (void **)&fspec, &len);
15597255Smux	if (!error && fspec[len - 1] != '\0')
15697255Smux		return (EINVAL);
15797255Smux
15812115Sdyson	/*
15912115Sdyson	 * If updating, check whether changing from read-only to
16012115Sdyson	 * read/write; if there is no device name, that's all we do.
16112115Sdyson	 */
16212115Sdyson	if (mp->mnt_flag & MNT_UPDATE) {
16396749Siedowse		ump = VFSTOEXT2(mp);
16412115Sdyson		fs = ump->um_e2fs;
16512115Sdyson		error = 0;
166138493Sphk		if (fs->s_rd_only == 0 &&
167138493Sphk		    vfs_flagopt(opts, "ro", NULL, 0)) {
168140048Sphk			error = VFS_SYNC(mp, MNT_WAIT, td);
169137039Sphk			if (error)
170137039Sphk				return (error);
17112115Sdyson			flags = WRITECLOSE;
17212115Sdyson			if (mp->mnt_flag & MNT_FORCE)
17312115Sdyson				flags |= FORCECLOSE;
174184554Sattilio			if (vfs_busy(mp, MBF_NOWAIT))
17512115Sdyson				return (EBUSY);
17683366Sjulian			error = ext2_flushfiles(mp, flags, td);
177182542Sattilio			vfs_unbusy(mp);
17839670Sbde			if (!error && fs->s_wasvalid) {
17939670Sbde				fs->s_es->s_state |= EXT2_VALID_FS;
18039670Sbde				ext2_sbupdate(ump, MNT_WAIT);
18139670Sbde			}
18239670Sbde			fs->s_rd_only = 1;
183138493Sphk			vfs_flagopt(opts, "ro", &mp->mnt_flag, MNT_RDONLY);
184137039Sphk			DROP_GIANT();
185137039Sphk			g_topology_lock();
186137039Sphk			g_access(ump->um_cp, 0, -1, 0);
187137039Sphk			g_topology_unlock();
188137039Sphk			PICKUP_GIANT();
18912115Sdyson		}
19012115Sdyson		if (!error && (mp->mnt_flag & MNT_RELOAD))
191140736Sphk			error = ext2_reload(mp, td);
19212115Sdyson		if (error)
19312115Sdyson			return (error);
19457839Sbde		devvp = ump->um_devvp;
195138493Sphk		if (fs->s_rd_only && !vfs_flagopt(opts, "ro", NULL, 0)) {
196138493Sphk			if (ext2_check_sb_compat(fs->s_es, devvp->v_rdev, 0))
197138493Sphk				return (EPERM);
19839028Sbde			/*
19939028Sbde			 * If upgrade to read-write by non-root, then verify
20039028Sbde			 * that user has necessary permissions on the device.
20139028Sbde			 */
202175202Sattilio			vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
203164033Srwatson			error = VOP_ACCESS(devvp, VREAD | VWRITE,
204164033Srwatson			    td->td_ucred, td);
205164033Srwatson			if (error)
206164033Srwatson				error = priv_check(td, PRIV_VFS_MOUNT_PERM);
207164033Srwatson			if (error) {
208175294Sattilio				VOP_UNLOCK(devvp, 0);
209164033Srwatson				return (error);
21039028Sbde			}
211175294Sattilio			VOP_UNLOCK(devvp, 0);
212137039Sphk			DROP_GIANT();
213137039Sphk			g_topology_lock();
214137039Sphk			error = g_access(ump->um_cp, 0, 1, 0);
215137039Sphk			g_topology_unlock();
216137039Sphk			PICKUP_GIANT();
217137039Sphk			if (error)
218137039Sphk				return (error);
21939028Sbde
22039670Sbde			if ((fs->s_es->s_state & EXT2_VALID_FS) == 0 ||
22139670Sbde			    (fs->s_es->s_state & EXT2_ERROR_FS)) {
22239670Sbde				if (mp->mnt_flag & MNT_FORCE) {
22339670Sbde					printf(
22439670Sbde"WARNING: %s was not properly dismounted\n",
22539670Sbde					    fs->fs_fsmnt);
22639670Sbde				} else {
22739670Sbde					printf(
22839670Sbde"WARNING: R/W mount of %s denied.  Filesystem is not clean - run fsck\n",
22939670Sbde					    fs->fs_fsmnt);
23039670Sbde					return (EPERM);
23139670Sbde				}
23239670Sbde			}
23312115Sdyson			fs->s_es->s_state &= ~EXT2_VALID_FS;
23412115Sdyson			ext2_sbupdate(ump, MNT_WAIT);
23539670Sbde			fs->s_rd_only = 0;
236162647Stegge			MNT_ILOCK(mp);
237138493Sphk			mp->mnt_flag &= ~MNT_RDONLY;
238162647Stegge			MNT_IUNLOCK(mp);
23912115Sdyson		}
240158924Srodrigc		if (vfs_flagopt(opts, "export", NULL, 0)) {
241158924Srodrigc			/* Process export requests in vfs_mount.c. */
242158924Srodrigc			return (error);
24312115Sdyson		}
24412115Sdyson	}
24512115Sdyson	/*
24612115Sdyson	 * Not an update, or updating the name: look up the name
247125786Sbde	 * and verify that it refers to a sensible disk device.
24812115Sdyson	 */
24997255Smux	if (fspec == NULL)
25097255Smux		return (EINVAL);
251149720Sssouhlal	NDINIT(ndp, LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE, fspec, td);
25243301Sdillon	if ((error = namei(ndp)) != 0)
25312115Sdyson		return (error);
25454655Seivind	NDFREE(ndp, NDF_ONLY_PNBUF);
25512115Sdyson	devvp = ndp->ni_vp;
25612115Sdyson
25755756Sphk	if (!vn_isdisk(devvp, &error)) {
258149720Sssouhlal		vput(devvp);
25955756Sphk		return (error);
26012115Sdyson	}
26139028Sbde
26239028Sbde	/*
26339028Sbde	 * If mount by non-root, then verify that user has necessary
26439028Sbde	 * permissions on the device.
265164033Srwatson	 *
266164033Srwatson	 * XXXRW: VOP_ACCESS() enough?
26739028Sbde	 */
268184413Strasz	accmode = VREAD;
269164033Srwatson	if ((mp->mnt_flag & MNT_RDONLY) == 0)
270184413Strasz		accmode |= VWRITE;
271184413Strasz	error = VOP_ACCESS(devvp, accmode, td->td_ucred, td);
272164033Srwatson	if (error)
273164033Srwatson		error = priv_check(td, PRIV_VFS_MOUNT_PERM);
274164033Srwatson	if (error) {
275164033Srwatson		vput(devvp);
276164033Srwatson		return (error);
27739028Sbde	}
27839028Sbde
27929888Skato	if ((mp->mnt_flag & MNT_UPDATE) == 0) {
280183754Sattilio		error = ext2_mountfs(devvp, mp);
28129888Skato	} else {
282149771Sssouhlal		if (devvp != ump->um_devvp) {
283149720Sssouhlal			vput(devvp);
284149771Sssouhlal			return (EINVAL);	/* needs translation */
285149771Sssouhlal		} else
286149771Sssouhlal			vput(devvp);
28712115Sdyson	}
28812115Sdyson	if (error) {
28912115Sdyson		vrele(devvp);
29012115Sdyson		return (error);
29112115Sdyson	}
29296749Siedowse	ump = VFSTOEXT2(mp);
29312115Sdyson	fs = ump->um_e2fs;
29473286Sadrian	/*
29573286Sadrian	 * Note that this strncpy() is ok because of a check at the start
29673286Sadrian	 * of ext2_mount().
29773286Sadrian	 */
29873286Sadrian	strncpy(fs->fs_fsmnt, path, MAXMNTLEN);
29973286Sadrian	fs->fs_fsmnt[MAXMNTLEN - 1] = '\0';
300138493Sphk	vfs_mountedfrom(mp, fspec);
30112115Sdyson	return (0);
30212115Sdyson}
30312115Sdyson
30412115Sdyson/*
30512115Sdyson * checks that the data in the descriptor blocks make sense
30612115Sdyson * this is taken from ext2/super.c
30712115Sdyson */
30812115Sdysonstatic int ext2_check_descriptors (struct ext2_sb_info * sb)
30912115Sdyson{
31012115Sdyson        int i;
31112115Sdyson        int desc_block = 0;
31212115Sdyson        unsigned long block = sb->s_es->s_first_data_block;
31312115Sdyson        struct ext2_group_desc * gdp = NULL;
31412115Sdyson
31512115Sdyson        /* ext2_debug ("Checking group descriptors"); */
31612115Sdyson
31712115Sdyson        for (i = 0; i < sb->s_groups_count; i++)
31812115Sdyson        {
31912115Sdyson		/* examine next descriptor block */
32012115Sdyson                if ((i % EXT2_DESC_PER_BLOCK(sb)) == 0)
32112115Sdyson                        gdp = (struct ext2_group_desc *)
32212115Sdyson				sb->s_group_desc[desc_block++]->b_data;
32312115Sdyson                if (gdp->bg_block_bitmap < block ||
32412115Sdyson                    gdp->bg_block_bitmap >= block + EXT2_BLOCKS_PER_GROUP(sb))
32512115Sdyson                {
32612115Sdyson                        printf ("ext2_check_descriptors: "
32712115Sdyson                                    "Block bitmap for group %d"
32839671Sbde                                    " not in group (block %lu)!\n",
32912115Sdyson                                    i, (unsigned long) gdp->bg_block_bitmap);
33012115Sdyson                        return 0;
33112115Sdyson                }
33212115Sdyson                if (gdp->bg_inode_bitmap < block ||
33312115Sdyson                    gdp->bg_inode_bitmap >= block + EXT2_BLOCKS_PER_GROUP(sb))
33412115Sdyson                {
33512115Sdyson                        printf ("ext2_check_descriptors: "
33612115Sdyson                                    "Inode bitmap for group %d"
33739671Sbde                                    " not in group (block %lu)!\n",
33812115Sdyson                                    i, (unsigned long) gdp->bg_inode_bitmap);
33912115Sdyson                        return 0;
34012115Sdyson                }
34112115Sdyson                if (gdp->bg_inode_table < block ||
34212115Sdyson                    gdp->bg_inode_table + sb->s_itb_per_group >=
34312115Sdyson                    block + EXT2_BLOCKS_PER_GROUP(sb))
34412115Sdyson                {
34512115Sdyson                        printf ("ext2_check_descriptors: "
34612115Sdyson                                    "Inode table for group %d"
34739671Sbde                                    " not in group (block %lu)!\n",
34812115Sdyson                                    i, (unsigned long) gdp->bg_inode_table);
34912115Sdyson                        return 0;
35012115Sdyson                }
35112115Sdyson                block += EXT2_BLOCKS_PER_GROUP(sb);
35212115Sdyson                gdp++;
35312115Sdyson        }
35412115Sdyson        return 1;
35512115Sdyson}
35612115Sdyson
35755313Sbdestatic int
35855313Sbdeext2_check_sb_compat(es, dev, ronly)
35955313Sbde	struct ext2_super_block *es;
360130585Sphk	struct cdev *dev;
36155313Sbde	int ronly;
36255313Sbde{
36355313Sbde
36455313Sbde	if (es->s_magic != EXT2_SUPER_MAGIC) {
36555313Sbde		printf("ext2fs: %s: wrong magic number %#x (expected %#x)\n",
36655313Sbde		    devtoname(dev), es->s_magic, EXT2_SUPER_MAGIC);
36755313Sbde		return (1);
36855313Sbde	}
36955313Sbde	if (es->s_rev_level > EXT2_GOOD_OLD_REV) {
37055313Sbde		if (es->s_feature_incompat & ~EXT2_FEATURE_INCOMPAT_SUPP) {
37155313Sbde			printf(
37255313Sbde"WARNING: mount of %s denied due to unsupported optional features\n",
37355313Sbde			    devtoname(dev));
37455313Sbde			return (1);
37555313Sbde		}
37655313Sbde		if (!ronly &&
37755313Sbde		    (es->s_feature_ro_compat & ~EXT2_FEATURE_RO_COMPAT_SUPP)) {
37855313Sbde			printf(
37955313Sbde"WARNING: R/W mount of %s denied due to unsupported optional features\n",
38055313Sbde			    devtoname(dev));
38155313Sbde			return (1);
38255313Sbde		}
38355313Sbde	}
38455313Sbde	return (0);
38555313Sbde}
38655313Sbde
38712115Sdyson/*
38812115Sdyson * this computes the fields of the  ext2_sb_info structure from the
38912115Sdyson * data in the ext2_super_block structure read in
39012115Sdyson */
39112115Sdysonstatic int compute_sb_data(devvp, es, fs)
39212115Sdyson	struct vnode * devvp;
39312115Sdyson	struct ext2_super_block * es;
39412115Sdyson	struct ext2_sb_info * fs;
39512115Sdyson{
39612115Sdyson    int db_count, error;
39712115Sdyson    int i, j;
39812115Sdyson    int logic_sb_block = 1;	/* XXX for now */
39912115Sdyson
40012115Sdyson#if 1
40112115Sdyson#define V(v)
40212115Sdyson#else
40312115Sdyson#define V(v)  printf(#v"= %d\n", fs->v);
40412115Sdyson#endif
40512115Sdyson
40612115Sdyson    fs->s_blocksize = EXT2_MIN_BLOCK_SIZE << es->s_log_block_size;
40712115Sdyson    V(s_blocksize)
40812115Sdyson    fs->s_bshift = EXT2_MIN_BLOCK_LOG_SIZE + es->s_log_block_size;
40912115Sdyson    V(s_bshift)
41012115Sdyson    fs->s_fsbtodb = es->s_log_block_size + 1;
41112115Sdyson    V(s_fsbtodb)
41212115Sdyson    fs->s_qbmask = fs->s_blocksize - 1;
41312115Sdyson    V(s_bmask)
41412115Sdyson    fs->s_blocksize_bits = EXT2_BLOCK_SIZE_BITS(es);
41512115Sdyson    V(s_blocksize_bits)
41612115Sdyson    fs->s_frag_size = EXT2_MIN_FRAG_SIZE << es->s_log_frag_size;
41712115Sdyson    V(s_frag_size)
41812115Sdyson    if (fs->s_frag_size)
41912115Sdyson	fs->s_frags_per_block = fs->s_blocksize / fs->s_frag_size;
42012115Sdyson    V(s_frags_per_block)
42112115Sdyson    fs->s_blocks_per_group = es->s_blocks_per_group;
42212115Sdyson    V(s_blocks_per_group)
42312115Sdyson    fs->s_frags_per_group = es->s_frags_per_group;
42412115Sdyson    V(s_frags_per_group)
42512115Sdyson    fs->s_inodes_per_group = es->s_inodes_per_group;
42612115Sdyson    V(s_inodes_per_group)
427187395Sstas    fs->s_inode_size = es->s_inode_size;
428187395Sstas    V(s_inode_size)
429187395Sstas    fs->s_first_inode = es->s_first_ino;
430187395Sstas    V(s_first_inode);
431187395Sstas    fs->s_inodes_per_block = fs->s_blocksize / EXT2_INODE_SIZE(fs);
43212115Sdyson    V(s_inodes_per_block)
43312115Sdyson    fs->s_itb_per_group = fs->s_inodes_per_group /fs->s_inodes_per_block;
43412115Sdyson    V(s_itb_per_group)
43512115Sdyson    fs->s_desc_per_block = fs->s_blocksize / sizeof (struct ext2_group_desc);
43612115Sdyson    V(s_desc_per_block)
43712115Sdyson    /* s_resuid / s_resgid ? */
43812115Sdyson    fs->s_groups_count = (es->s_blocks_count -
43912115Sdyson			  es->s_first_data_block +
44012115Sdyson			  EXT2_BLOCKS_PER_GROUP(fs) - 1) /
44112115Sdyson			 EXT2_BLOCKS_PER_GROUP(fs);
44212115Sdyson    V(s_groups_count)
44312115Sdyson    db_count = (fs->s_groups_count + EXT2_DESC_PER_BLOCK(fs) - 1) /
44412115Sdyson	EXT2_DESC_PER_BLOCK(fs);
44512115Sdyson    fs->s_db_per_group = db_count;
44612115Sdyson    V(s_db_per_group)
44712115Sdyson
44812115Sdyson    fs->s_group_desc = bsd_malloc(db_count * sizeof (struct buf *),
449111119Simp		M_EXT2MNT, M_WAITOK);
45012115Sdyson
45112115Sdyson    /* adjust logic_sb_block */
45212115Sdyson    if(fs->s_blocksize > SBSIZE)
45312115Sdyson	/* Godmar thinks: if the blocksize is greater than 1024, then
45412115Sdyson	   the superblock is logically part of block zero.
45512115Sdyson	 */
45612115Sdyson        logic_sb_block = 0;
45712115Sdyson
45812115Sdyson    for (i = 0; i < db_count; i++) {
45912115Sdyson	error = bread(devvp , fsbtodb(fs, logic_sb_block + i + 1),
46012115Sdyson		fs->s_blocksize, NOCRED, &fs->s_group_desc[i]);
46112115Sdyson	if(error) {
46212115Sdyson	    for (j = 0; j < i; j++)
46312115Sdyson		brelse(fs->s_group_desc[j]);
46496749Siedowse	    bsd_free(fs->s_group_desc, M_EXT2MNT);
46512115Sdyson	    printf("EXT2-fs: unable to read group descriptors (%d)\n", error);
46612115Sdyson	    return EIO;
46712115Sdyson	}
46827881Sdyson	LCK_BUF(fs->s_group_desc[i])
46912115Sdyson    }
47012115Sdyson    if(!ext2_check_descriptors(fs)) {
47112115Sdyson	    for (j = 0; j < db_count; j++)
47227881Sdyson		    ULCK_BUF(fs->s_group_desc[j])
47396749Siedowse	    bsd_free(fs->s_group_desc, M_EXT2MNT);
47412115Sdyson	    printf("EXT2-fs: (ext2_check_descriptors failure) "
47512115Sdyson		   "unable to read group descriptors\n");
47612115Sdyson	    return EIO;
47712115Sdyson    }
47812115Sdyson
47912115Sdyson    for (i = 0; i < EXT2_MAX_GROUP_LOADED; i++) {
48012115Sdyson	    fs->s_inode_bitmap_number[i] = 0;
48112115Sdyson	    fs->s_inode_bitmap[i] = NULL;
48212115Sdyson	    fs->s_block_bitmap_number[i] = 0;
48312115Sdyson	    fs->s_block_bitmap[i] = NULL;
48412115Sdyson    }
48512115Sdyson    fs->s_loaded_inode_bitmaps = 0;
48612115Sdyson    fs->s_loaded_block_bitmaps = 0;
487125991Stjr    if (es->s_rev_level == EXT2_GOOD_OLD_REV || (es->s_feature_ro_compat &
488125991Stjr        EXT2_FEATURE_RO_COMPAT_LARGE_FILE) == 0)
489125991Stjr	fs->fs_maxfilesize = 0x7fffffff;
490125991Stjr    else
491125991Stjr	fs->fs_maxfilesize = 0x7fffffffffffffff;
49212115Sdyson    return 0;
49312115Sdyson}
49412115Sdyson
49512115Sdyson/*
49612115Sdyson * Reload all incore data for a filesystem (used after running fsck on
49712115Sdyson * the root filesystem and finding things to fix). The filesystem must
49812115Sdyson * be mounted read-only.
49912115Sdyson *
50012115Sdyson * Things to do to update the mount:
50112115Sdyson *	1) invalidate all cached meta-data.
50212115Sdyson *	2) re-read superblock from disk.
50312115Sdyson *	3) re-read summary information from disk.
50412115Sdyson *	4) invalidate all inactive vnodes.
50512115Sdyson *	5) invalidate all cached file data.
50612115Sdyson *	6) re-read inode data for all active vnodes.
50712115Sdyson */
50812911Sphkstatic int
509140736Sphkext2_reload(struct mount *mp, struct thread *td)
51012115Sdyson{
511154152Stegge	struct vnode *vp, *mvp, *devvp;
51212115Sdyson	struct inode *ip;
51312115Sdyson	struct buf *bp;
51412115Sdyson	struct ext2_super_block * es;
51512115Sdyson	struct ext2_sb_info *fs;
51612147Sdyson	int error;
51712115Sdyson
518122114Sbde	if ((mp->mnt_flag & MNT_RDONLY) == 0)
51912115Sdyson		return (EINVAL);
52012115Sdyson	/*
52112115Sdyson	 * Step 1: invalidate all cached meta-data.
52212115Sdyson	 */
523122114Sbde	devvp = VFSTOEXT2(mp)->um_devvp;
524175202Sattilio	vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
525183754Sattilio	if (vinvalbuf(devvp, 0, 0, 0) != 0)
52612115Sdyson		panic("ext2_reload: dirty1");
527175294Sattilio	VOP_UNLOCK(devvp, 0);
528125786Sbde
52912115Sdyson	/*
53012115Sdyson	 * Step 2: re-read superblock from disk.
53112115Sdyson	 * constants have been adjusted for ext2
53212115Sdyson	 */
53343301Sdillon	if ((error = bread(devvp, SBLOCK, SBSIZE, NOCRED, &bp)) != 0)
53412115Sdyson		return (error);
53512115Sdyson	es = (struct ext2_super_block *)bp->b_data;
53655313Sbde	if (ext2_check_sb_compat(es, devvp->v_rdev, 0) != 0) {
53712115Sdyson		brelse(bp);
53812115Sdyson		return (EIO);		/* XXX needs translation */
53912115Sdyson	}
540122114Sbde	fs = VFSTOEXT2(mp)->um_e2fs;
54112115Sdyson	bcopy(bp->b_data, fs->s_es, sizeof(struct ext2_super_block));
54212115Sdyson
54343301Sdillon	if((error = compute_sb_data(devvp, es, fs)) != 0) {
54412115Sdyson		brelse(bp);
54512115Sdyson		return error;
54612115Sdyson	}
54712115Sdyson#ifdef UNKLAR
54812115Sdyson	if (fs->fs_sbsize < SBSIZE)
54912115Sdyson		bp->b_flags |= B_INVAL;
55012115Sdyson#endif
55112115Sdyson	brelse(bp);
55212115Sdyson
55312115Sdysonloop:
554122091Skan	MNT_ILOCK(mp);
555154152Stegge	MNT_VNODE_FOREACH(vp, mp, mvp) {
556120751Sjeff		VI_LOCK(vp);
557143509Sjeff		if (vp->v_iflag & VI_DOOMED) {
558120783Sjeff			VI_UNLOCK(vp);
559120783Sjeff			continue;
560120783Sjeff		}
561122091Skan		MNT_IUNLOCK(mp);
56212115Sdyson		/*
563143509Sjeff		 * Step 4: invalidate all cached file data.
56412115Sdyson		 */
56583366Sjulian		if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK, td)) {
566154152Stegge			MNT_VNODE_FOREACH_ABORT(mp, mvp);
56712115Sdyson			goto loop;
56839678Sbde		}
569183754Sattilio		if (vinvalbuf(vp, 0, 0, 0))
57012115Sdyson			panic("ext2_reload: dirty2");
57112115Sdyson		/*
572143509Sjeff		 * Step 5: re-read inode data for all active vnodes.
57312115Sdyson		 */
57412115Sdyson		ip = VTOI(vp);
57539678Sbde		error =
57612115Sdyson		    bread(devvp, fsbtodb(fs, ino_to_fsba(fs, ip->i_number)),
57739678Sbde		    (int)fs->s_blocksize, NOCRED, &bp);
57839678Sbde		if (error) {
579175294Sattilio			VOP_UNLOCK(vp, 0);
580121925Skan			vrele(vp);
581154152Stegge			MNT_VNODE_FOREACH_ABORT(mp, mvp);
58212115Sdyson			return (error);
58312115Sdyson		}
58496749Siedowse		ext2_ei2i((struct ext2_inode *) ((char *)bp->b_data +
585187395Sstas		    EXT2_INODE_SIZE(fs) * ino_to_fsbo(fs, ip->i_number)), ip);
58612115Sdyson		brelse(bp);
587175294Sattilio		VOP_UNLOCK(vp, 0);
588121925Skan		vrele(vp);
589122091Skan		MNT_ILOCK(mp);
59012115Sdyson	}
591122091Skan	MNT_IUNLOCK(mp);
59212115Sdyson	return (0);
59312115Sdyson}
59412115Sdyson
59512115Sdyson/*
59612115Sdyson * Common code for mount and mountroot
59712115Sdyson */
59812911Sphkstatic int
599183754Sattilioext2_mountfs(devvp, mp)
60096752Siedowse	struct vnode *devvp;
60112115Sdyson	struct mount *mp;
60212115Sdyson{
60396752Siedowse	struct ext2mount *ump;
60412115Sdyson	struct buf *bp;
60596752Siedowse	struct ext2_sb_info *fs;
60612115Sdyson	struct ext2_super_block * es;
607130585Sphk	struct cdev *dev = devvp->v_rdev;
608137039Sphk	struct g_consumer *cp;
609137039Sphk	struct bufobj *bo;
61096749Siedowse	int error;
61112115Sdyson	int ronly;
61212115Sdyson
613138493Sphk	ronly = vfs_flagopt(mp->mnt_optnew, "ro", NULL, 0);
614137039Sphk	/* XXX: use VOP_ACESS to check FS perms */
615137039Sphk	DROP_GIANT();
616137039Sphk	g_topology_lock();
617137039Sphk	error = g_vfs_open(devvp, &cp, "ext2fs", ronly ? 0 : 1);
618137039Sphk	g_topology_unlock();
619137039Sphk	PICKUP_GIANT();
620175294Sattilio	VOP_UNLOCK(devvp, 0);
62153059Sphk	if (error)
62212115Sdyson		return (error);
623149960Srodrigc
624149960Srodrigc	/* XXX: should we check for some sectorsize or 512 instead? */
625149960Srodrigc	if (((SBSIZE % cp->provider->sectorsize) != 0) ||
626149960Srodrigc	    (SBSIZE < cp->provider->sectorsize)) {
627149960Srodrigc		DROP_GIANT();
628149960Srodrigc		g_topology_lock();
629183754Sattilio		g_vfs_close(cp);
630149960Srodrigc		g_topology_unlock();
631149960Srodrigc		PICKUP_GIANT();
632149960Srodrigc		return (EINVAL);
633149960Srodrigc	}
634149960Srodrigc
635137039Sphk	bo = &devvp->v_bufobj;
636137039Sphk	bo->bo_private = cp;
637137039Sphk	bo->bo_ops = g_vfs_bufops;
63893430Sbde	if (devvp->v_rdev->si_iosize_max != 0)
63993430Sbde		mp->mnt_iosize_max = devvp->v_rdev->si_iosize_max;
64093430Sbde	if (mp->mnt_iosize_max > MAXPHYS)
64193430Sbde		mp->mnt_iosize_max = MAXPHYS;
64293430Sbde
64312115Sdyson	bp = NULL;
64412115Sdyson	ump = NULL;
64543301Sdillon	if ((error = bread(devvp, SBLOCK, SBSIZE, NOCRED, &bp)) != 0)
64612115Sdyson		goto out;
64712115Sdyson	es = (struct ext2_super_block *)bp->b_data;
64855313Sbde	if (ext2_check_sb_compat(es, dev, ronly) != 0) {
64912115Sdyson		error = EINVAL;		/* XXX needs translation */
65012115Sdyson		goto out;
65112115Sdyson	}
65239670Sbde	if ((es->s_state & EXT2_VALID_FS) == 0 ||
65339670Sbde	    (es->s_state & EXT2_ERROR_FS)) {
65439670Sbde		if (ronly || (mp->mnt_flag & MNT_FORCE)) {
65539670Sbde			printf(
65639670Sbde"WARNING: Filesystem was not properly dismounted\n");
65739670Sbde		} else {
65839670Sbde			printf(
65939670Sbde"WARNING: R/W mount denied.  Filesystem is not clean - run fsck\n");
66039670Sbde			error = EPERM;
66139670Sbde			goto out;
66239670Sbde		}
66339670Sbde	}
664111119Simp	ump = bsd_malloc(sizeof *ump, M_EXT2MNT, M_WAITOK);
66512115Sdyson	bzero((caddr_t)ump, sizeof *ump);
66612115Sdyson	/* I don't know whether this is the right strategy. Note that
667108533Sschweikh	   we dynamically allocate both an ext2_sb_info and an ext2_super_block
66812115Sdyson	   while Linux keeps the super block in a locked buffer
66912115Sdyson	 */
67012115Sdyson	ump->um_e2fs = bsd_malloc(sizeof(struct ext2_sb_info),
671111119Simp		M_EXT2MNT, M_WAITOK);
67212115Sdyson	ump->um_e2fs->s_es = bsd_malloc(sizeof(struct ext2_super_block),
673111119Simp		M_EXT2MNT, M_WAITOK);
67412115Sdyson	bcopy(es, ump->um_e2fs->s_es, (u_int)sizeof(struct ext2_super_block));
67539671Sbde	if ((error = compute_sb_data(devvp, ump->um_e2fs->s_es, ump->um_e2fs)))
67639671Sbde		goto out;
67739671Sbde	/*
67839671Sbde	 * We don't free the group descriptors allocated by compute_sb_data()
67939671Sbde	 * until ext2_unmount().  This is OK since the mount will succeed.
68039671Sbde	 */
68112115Sdyson	brelse(bp);
68212115Sdyson	bp = NULL;
68312115Sdyson	fs = ump->um_e2fs;
68412115Sdyson	fs->s_rd_only = ronly;	/* ronly is set according to mnt_flags */
68512115Sdyson	/* if the fs is not mounted read-only, make sure the super block is
68612115Sdyson	   always written back on a sync()
68712115Sdyson	 */
68839670Sbde	fs->s_wasvalid = fs->s_es->s_state & EXT2_VALID_FS ? 1 : 0;
68912115Sdyson	if (ronly == 0) {
69012115Sdyson		fs->s_dirt = 1;		/* mark it modified */
69112115Sdyson		fs->s_es->s_state &= ~EXT2_VALID_FS;	/* set fs invalid */
69212115Sdyson	}
693172697Salfred	mp->mnt_data = ump;
69450256Sbde	mp->mnt_stat.f_fsid.val[0] = dev2udev(dev);
69538909Sbde	mp->mnt_stat.f_fsid.val[1] = mp->mnt_vfc->vfc_typenum;
69612115Sdyson	mp->mnt_maxsymlinklen = EXT2_MAXSYMLINKLEN;
697162647Stegge	MNT_ILOCK(mp);
69812115Sdyson	mp->mnt_flag |= MNT_LOCAL;
699162647Stegge	MNT_IUNLOCK(mp);
70012115Sdyson	ump->um_mountp = mp;
70112115Sdyson	ump->um_dev = dev;
70212115Sdyson	ump->um_devvp = devvp;
703137320Sphk	ump->um_bo = &devvp->v_bufobj;
704137320Sphk	ump->um_cp = cp;
70596749Siedowse	/* setting those two parameters allowed us to use
70612115Sdyson	   ufs_bmap w/o changse !
70712115Sdyson	*/
70812115Sdyson	ump->um_nindir = EXT2_ADDR_PER_BLOCK(fs);
70912115Sdyson	ump->um_bptrtodb = fs->s_es->s_log_block_size + 1;
71012115Sdyson	ump->um_seqinc = EXT2_FRAGS_PER_BLOCK(fs);
71134430Seivind	if (ronly == 0)
71234430Seivind		ext2_sbupdate(ump, MNT_WAIT);
71312115Sdyson	return (0);
71412115Sdysonout:
71512115Sdyson	if (bp)
71612115Sdyson		brelse(bp);
717137039Sphk	if (cp != NULL) {
718137039Sphk		DROP_GIANT();
719137039Sphk		g_topology_lock();
720183754Sattilio		g_vfs_close(cp);
721137039Sphk		g_topology_unlock();
722137039Sphk		PICKUP_GIANT();
723137039Sphk	}
72412115Sdyson	if (ump) {
72596749Siedowse		bsd_free(ump->um_e2fs->s_es, M_EXT2MNT);
72696749Siedowse		bsd_free(ump->um_e2fs, M_EXT2MNT);
72796749Siedowse		bsd_free(ump, M_EXT2MNT);
728172697Salfred		mp->mnt_data = NULL;
72912115Sdyson	}
73012115Sdyson	return (error);
73112115Sdyson}
73212115Sdyson
73312115Sdyson/*
73412115Sdyson * unmount system call
73512115Sdyson */
73612911Sphkstatic int
73783366Sjulianext2_unmount(mp, mntflags, td)
73812115Sdyson	struct mount *mp;
73912115Sdyson	int mntflags;
74083366Sjulian	struct thread *td;
74112115Sdyson{
74296752Siedowse	struct ext2mount *ump;
74396752Siedowse	struct ext2_sb_info *fs;
74412115Sdyson	int error, flags, ronly, i;
74512115Sdyson
74612115Sdyson	flags = 0;
74712115Sdyson	if (mntflags & MNT_FORCE) {
74812115Sdyson		if (mp->mnt_flag & MNT_ROOTFS)
74912115Sdyson			return (EINVAL);
75012115Sdyson		flags |= FORCECLOSE;
75112115Sdyson	}
75283366Sjulian	if ((error = ext2_flushfiles(mp, flags, td)) != 0)
75312115Sdyson		return (error);
75496749Siedowse	ump = VFSTOEXT2(mp);
75512115Sdyson	fs = ump->um_e2fs;
75612115Sdyson	ronly = fs->s_rd_only;
75739670Sbde	if (ronly == 0) {
75839670Sbde		if (fs->s_wasvalid)
75939670Sbde			fs->s_es->s_state |= EXT2_VALID_FS;
76012115Sdyson		ext2_sbupdate(ump, MNT_WAIT);
76112115Sdyson	}
76227881Sdyson
76312115Sdyson	/* release buffers containing group descriptors */
76412115Sdyson	for(i = 0; i < fs->s_db_per_group; i++)
76527881Sdyson		ULCK_BUF(fs->s_group_desc[i])
76696749Siedowse	bsd_free(fs->s_group_desc, M_EXT2MNT);
76727881Sdyson
76812115Sdyson	/* release cached inode/block bitmaps */
76912115Sdyson        for (i = 0; i < EXT2_MAX_GROUP_LOADED; i++)
77012115Sdyson                if (fs->s_inode_bitmap[i])
77127881Sdyson			ULCK_BUF(fs->s_inode_bitmap[i])
77227881Sdyson
77312115Sdyson        for (i = 0; i < EXT2_MAX_GROUP_LOADED; i++)
77412115Sdyson                if (fs->s_block_bitmap[i])
77527881Sdyson			ULCK_BUF(fs->s_block_bitmap[i])
77612115Sdyson
777137039Sphk	DROP_GIANT();
778137039Sphk	g_topology_lock();
779183754Sattilio	g_vfs_close(ump->um_cp);
780137039Sphk	g_topology_unlock();
781137039Sphk	PICKUP_GIANT();
78212115Sdyson	vrele(ump->um_devvp);
78396749Siedowse	bsd_free(fs->s_es, M_EXT2MNT);
78496749Siedowse	bsd_free(fs, M_EXT2MNT);
78596749Siedowse	bsd_free(ump, M_EXT2MNT);
786172697Salfred	mp->mnt_data = NULL;
787162647Stegge	MNT_ILOCK(mp);
78812115Sdyson	mp->mnt_flag &= ~MNT_LOCAL;
789162647Stegge	MNT_IUNLOCK(mp);
79012115Sdyson	return (error);
79112115Sdyson}
79212115Sdyson
79312115Sdyson/*
79412115Sdyson * Flush out all the files in a filesystem.
79512115Sdyson */
79612911Sphkstatic int
79783366Sjulianext2_flushfiles(mp, flags, td)
79896752Siedowse	struct mount *mp;
79912115Sdyson	int flags;
80083366Sjulian	struct thread *td;
80112115Sdyson{
80212147Sdyson	int error;
80312115Sdyson
804132023Salfred	error = vflush(mp, 0, flags, td);
80512115Sdyson	return (error);
80612115Sdyson}
80712115Sdyson
80812115Sdyson/*
80912115Sdyson * Get file system statistics.
81012115Sdyson * taken from ext2/super.c ext2_statfs
81112115Sdyson */
81212911Sphkstatic int
81383366Sjulianext2_statfs(mp, sbp, td)
81412115Sdyson	struct mount *mp;
81596752Siedowse	struct statfs *sbp;
81683366Sjulian	struct thread *td;
81712115Sdyson{
81812115Sdyson        unsigned long overhead;
81996752Siedowse	struct ext2mount *ump;
82096752Siedowse	struct ext2_sb_info *fs;
82196752Siedowse	struct ext2_super_block *es;
82268291Sbde	int i, nsb;
82312115Sdyson
82496749Siedowse	ump = VFSTOEXT2(mp);
82512115Sdyson	fs = ump->um_e2fs;
82612115Sdyson	es = fs->s_es;
82712115Sdyson
82812115Sdyson	if (es->s_magic != EXT2_SUPER_MAGIC)
82912115Sdyson		panic("ext2_statfs - magic number spoiled");
83012115Sdyson
83112115Sdyson	/*
83212115Sdyson	 * Compute the overhead (FS structures)
83312115Sdyson	 */
83468291Sbde	if (es->s_feature_ro_compat & EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER) {
83568291Sbde		nsb = 0;
83668291Sbde		for (i = 0 ; i < fs->s_groups_count; i++)
83768291Sbde			if (ext2_group_sparse(i))
83868291Sbde				nsb++;
83968291Sbde	} else
84068291Sbde		nsb = fs->s_groups_count;
84112115Sdyson	overhead = es->s_first_data_block +
84268291Sbde	    /* Superblocks and block group descriptors: */
84368291Sbde	    nsb * (1 + fs->s_db_per_group) +
84468291Sbde	    /* Inode bitmap, block bitmap, and inode table: */
84568291Sbde	    fs->s_groups_count * (1 + 1 + fs->s_itb_per_group);
84612115Sdyson
84712115Sdyson	sbp->f_bsize = EXT2_FRAG_SIZE(fs);
84812115Sdyson	sbp->f_iosize = EXT2_BLOCK_SIZE(fs);
84912115Sdyson	sbp->f_blocks = es->s_blocks_count - overhead;
85012115Sdyson	sbp->f_bfree = es->s_free_blocks_count;
85112115Sdyson	sbp->f_bavail = sbp->f_bfree - es->s_r_blocks_count;
85212115Sdyson	sbp->f_files = es->s_inodes_count;
85312115Sdyson	sbp->f_ffree = es->s_free_inodes_count;
85412115Sdyson	return (0);
85512115Sdyson}
85612115Sdyson
85712115Sdyson/*
85812115Sdyson * Go through the disk queues to initiate sandbagged IO;
85912115Sdyson * go through the inodes to write those that have been modified;
86012115Sdyson * initiate the writing of the super block if it has been modified.
86112115Sdyson *
86212115Sdyson * Note: we are always called with the filesystem marked `MPBUSY'.
86312115Sdyson */
86412911Sphkstatic int
865140048Sphkext2_sync(mp, waitfor, td)
86612115Sdyson	struct mount *mp;
86712115Sdyson	int waitfor;
86883366Sjulian	struct thread *td;
86912115Sdyson{
870154152Stegge	struct vnode *mvp, *vp;
87139678Sbde	struct inode *ip;
87296749Siedowse	struct ext2mount *ump = VFSTOEXT2(mp);
87339678Sbde	struct ext2_sb_info *fs;
87412115Sdyson	int error, allerror = 0;
87512115Sdyson
87612115Sdyson	fs = ump->um_e2fs;
87739678Sbde	if (fs->s_dirt != 0 && fs->s_rd_only != 0) {		/* XXX */
87839678Sbde		printf("fs = %s\n", fs->fs_fsmnt);
87939678Sbde		panic("ext2_sync: rofs mod");
88012115Sdyson	}
88112115Sdyson	/*
88212115Sdyson	 * Write back each (modified) inode.
88312115Sdyson	 */
884122091Skan	MNT_ILOCK(mp);
88512115Sdysonloop:
886154152Stegge	MNT_VNODE_FOREACH(vp, mp, mvp) {
887120751Sjeff		VI_LOCK(vp);
888143509Sjeff		if (vp->v_type == VNON || (vp->v_iflag & VI_DOOMED)) {
889120783Sjeff			VI_UNLOCK(vp);
890120783Sjeff			continue;
891120783Sjeff		}
892122091Skan		MNT_IUNLOCK(mp);
89312115Sdyson		ip = VTOI(vp);
894137008Sphk		if ((ip->i_flag &
89512115Sdyson		    (IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE)) == 0 &&
896136943Sphk		    (vp->v_bufobj.bo_dirty.bv_cnt == 0 ||
897137008Sphk		    waitfor == MNT_LAZY)) {
898103938Sjeff			VI_UNLOCK(vp);
899122091Skan			MNT_ILOCK(mp);
90012115Sdyson			continue;
90139678Sbde		}
90283366Sjulian		error = vget(vp, LK_EXCLUSIVE | LK_NOWAIT | LK_INTERLOCK, td);
90339678Sbde		if (error) {
904122091Skan			MNT_ILOCK(mp);
905154152Stegge			if (error == ENOENT) {
906154152Stegge				MNT_VNODE_FOREACH_ABORT_ILOCKED(mp, mvp);
90739678Sbde				goto loop;
908154152Stegge			}
90939678Sbde			continue;
91039678Sbde		}
911140048Sphk		if ((error = VOP_FSYNC(vp, waitfor, td)) != 0)
91212115Sdyson			allerror = error;
913175294Sattilio		VOP_UNLOCK(vp, 0);
914121874Skan		vrele(vp);
915122091Skan		MNT_ILOCK(mp);
91612115Sdyson	}
917122091Skan	MNT_IUNLOCK(mp);
91812115Sdyson	/*
91912115Sdyson	 * Force stale file system control information to be flushed.
92012115Sdyson	 */
92139678Sbde	if (waitfor != MNT_LAZY) {
922175202Sattilio		vn_lock(ump->um_devvp, LK_EXCLUSIVE | LK_RETRY);
923140048Sphk		if ((error = VOP_FSYNC(ump->um_devvp, waitfor, td)) != 0)
92439678Sbde			allerror = error;
925175294Sattilio		VOP_UNLOCK(ump->um_devvp, 0);
92639678Sbde	}
92739678Sbde	/*
92839678Sbde	 * Write back modified superblock.
92939678Sbde	 */
93039678Sbde	if (fs->s_dirt != 0) {
93139678Sbde		fs->s_dirt = 0;
93239678Sbde		fs->s_es->s_wtime = time_second;
93339678Sbde		if ((error = ext2_sbupdate(ump, waitfor)) != 0)
93439678Sbde			allerror = error;
93539678Sbde	}
93612115Sdyson	return (allerror);
93712115Sdyson}
93812115Sdyson
93912115Sdyson/*
940108533Sschweikh * Look up an EXT2FS dinode number to find its incore vnode, otherwise read it
94112115Sdyson * in from disk.  If it is in core, wait for the lock bit to clear, then
94212115Sdyson * return the inode locked.  Detection and handling of mount points must be
94312115Sdyson * done by the calling routine.
94412115Sdyson */
94512911Sphkstatic int
94692462Smckusickext2_vget(mp, ino, flags, vpp)
94712115Sdyson	struct mount *mp;
94812115Sdyson	ino_t ino;
94992462Smckusick	int flags;
95012115Sdyson	struct vnode **vpp;
95112115Sdyson{
95296752Siedowse	struct ext2_sb_info *fs;
95396752Siedowse	struct inode *ip;
95496749Siedowse	struct ext2mount *ump;
95512115Sdyson	struct buf *bp;
95612115Sdyson	struct vnode *vp;
957130585Sphk	struct cdev *dev;
95830280Sphk	int i, error;
95912115Sdyson	int used_blocks;
960167497Stegge	struct thread *td;
96112115Sdyson
962167497Stegge	td = curthread;
963167497Stegge	error = vfs_hash_get(mp, ino, flags, td, vpp, NULL, NULL);
964143619Sphk	if (error || *vpp != NULL)
96592462Smckusick		return (error);
96612115Sdyson
967143578Sphk	ump = VFSTOEXT2(mp);
968143578Sphk	dev = ump->um_dev;
96912406Sdyson
97036102Sbde	/*
971184205Sdes	 * If this malloc() is performed after the getnewvnode()
97236102Sbde	 * it might block, leaving a vnode with a NULL v_data to be
97336102Sbde	 * found by ext2_sync() if a sync happens to fire right then,
97436102Sbde	 * which will cause a panic because ext2_sync() blindly
97536102Sbde	 * dereferences vp->v_data (as well it should).
97636102Sbde	 */
977143578Sphk	ip = malloc(sizeof(struct inode), M_EXT2NODE, M_WAITOK | M_ZERO);
97836102Sbde
97912115Sdyson	/* Allocate a new vnode/inode. */
980138290Sphk	if ((error = getnewvnode("ext2fs", mp, &ext2_vnodeops, &vp)) != 0) {
98112115Sdyson		*vpp = NULL;
982143578Sphk		free(ip, M_EXT2NODE);
98312115Sdyson		return (error);
98412115Sdyson	}
98512115Sdyson	vp->v_data = ip;
98612115Sdyson	ip->i_vnode = vp;
98712115Sdyson	ip->i_e2fs = fs = ump->um_e2fs;
98812115Sdyson	ip->i_number = ino;
989143578Sphk
990175635Sattilio	lockmgr(vp->v_vnlock, LK_EXCLUSIVE, NULL);
991167497Stegge	error = insmntque(vp, mp);
992167497Stegge	if (error != 0) {
993167497Stegge		free(ip, M_EXT2NODE);
994167497Stegge		*vpp = NULL;
995167497Stegge		return (error);
996167497Stegge	}
997167497Stegge	error = vfs_hash_insert(vp, ino, flags, td, vpp, NULL, NULL);
998143663Sphk	if (error || *vpp != NULL)
999143578Sphk		return (error);
100012406Sdyson
100112115Sdyson	/* Read in the disk contents for the inode, copy into the inode. */
100212115Sdyson#if 0
100312115Sdysonprintf("ext2_vget(%d) dbn= %d ", ino, fsbtodb(fs, ino_to_fsba(fs, ino)));
100412115Sdyson#endif
100543301Sdillon	if ((error = bread(ump->um_devvp, fsbtodb(fs, ino_to_fsba(fs, ino)),
100643301Sdillon	    (int)fs->s_blocksize, NOCRED, &bp)) != 0) {
100712115Sdyson		/*
100812115Sdyson		 * The inode does not contain anything useful, so it would
100912115Sdyson		 * be misleading to leave it on its hash chain. With mode
101012115Sdyson		 * still zero, it will be unlinked and returned to the free
101112115Sdyson		 * list by vput().
101212115Sdyson		 */
101312115Sdyson		vput(vp);
101412115Sdyson		brelse(bp);
101512115Sdyson		*vpp = NULL;
101612115Sdyson		return (error);
101712115Sdyson	}
101812115Sdyson	/* convert ext2 inode to dinode */
1019187395Sstas	ext2_ei2i((struct ext2_inode *) ((char *)bp->b_data + EXT2_INODE_SIZE(fs) *
102096749Siedowse			ino_to_fsbo(fs, ino)), ip);
102112115Sdyson	ip->i_block_group = ino_to_cg(fs, ino);
102212115Sdyson	ip->i_next_alloc_block = 0;
102312115Sdyson	ip->i_next_alloc_goal = 0;
102412115Sdyson	ip->i_prealloc_count = 0;
102512115Sdyson	ip->i_prealloc_block = 0;
102612115Sdyson        /* now we want to make sure that block pointers for unused
102712115Sdyson           blocks are zeroed out - ext2_balloc depends on this
102812115Sdyson	   although for regular files and directories only
102912115Sdyson	*/
103012115Sdyson	if(S_ISDIR(ip->i_mode) || S_ISREG(ip->i_mode)) {
103112115Sdyson		used_blocks = (ip->i_size+fs->s_blocksize-1) / fs->s_blocksize;
103212115Sdyson		for(i = used_blocks; i < EXT2_NDIR_BLOCKS; i++)
103312115Sdyson			ip->i_db[i] = 0;
103412115Sdyson	}
103512115Sdyson/*
103612115Sdyson	ext2_print_inode(ip);
103712115Sdyson*/
103812115Sdyson	brelse(bp);
103912115Sdyson
104012115Sdyson	/*
104112115Sdyson	 * Initialize the vnode from the inode, check for aliases.
104212115Sdyson	 * Note that the underlying vnode may have changed.
104312115Sdyson	 */
1044138290Sphk	if ((error = ext2_vinit(mp, &ext2_fifoops, &vp)) != 0) {
104512115Sdyson		vput(vp);
104612115Sdyson		*vpp = NULL;
104712115Sdyson		return (error);
104812115Sdyson	}
104912115Sdyson	/*
105012115Sdyson	 * Finish inode initialization now that aliasing has been resolved.
105112115Sdyson	 */
105212115Sdyson	ip->i_devvp = ump->um_devvp;
105312115Sdyson	/*
105412115Sdyson	 * Set up a generation number for this inode if it does not
105512115Sdyson	 * already have one. This should only happen on old filesystems.
105612115Sdyson	 */
105712115Sdyson	if (ip->i_gen == 0) {
105831485Sbde		ip->i_gen = random() / 2 + 1;
105912115Sdyson		if ((vp->v_mount->mnt_flag & MNT_RDONLY) == 0)
106012115Sdyson			ip->i_flag |= IN_MODIFIED;
106112115Sdyson	}
106212115Sdyson	*vpp = vp;
106312115Sdyson	return (0);
106412115Sdyson}
106512115Sdyson
106612115Sdyson/*
106712115Sdyson * File handle to vnode
106812115Sdyson *
106912115Sdyson * Have to be really careful about stale file handles:
107012115Sdyson * - check that the inode number is valid
107112115Sdyson * - call ext2_vget() to get the locked inode
107212115Sdyson * - check for an unallocated inode (i_mode == 0)
107312115Sdyson * - check that the given client host has export rights and return
107412115Sdyson *   those rights via. exflagsp and credanonp
107512115Sdyson */
107612911Sphkstatic int
107751138Salfredext2_fhtovp(mp, fhp, vpp)
107896752Siedowse	struct mount *mp;
107912115Sdyson	struct fid *fhp;
108012115Sdyson	struct vnode **vpp;
108112115Sdyson{
108296749Siedowse	struct inode *ip;
108396752Siedowse	struct ufid *ufhp;
108496749Siedowse	struct vnode *nvp;
108512115Sdyson	struct ext2_sb_info *fs;
108696749Siedowse	int error;
108712115Sdyson
108812115Sdyson	ufhp = (struct ufid *)fhp;
108996749Siedowse	fs = VFSTOEXT2(mp)->um_e2fs;
109012115Sdyson	if (ufhp->ufid_ino < ROOTINO ||
109196880Siedowse	    ufhp->ufid_ino > fs->s_groups_count * fs->s_es->s_inodes_per_group)
109212115Sdyson		return (ESTALE);
109396749Siedowse
109496749Siedowse	error = VFS_VGET(mp, ufhp->ufid_ino, LK_EXCLUSIVE, &nvp);
109596749Siedowse	if (error) {
109696749Siedowse		*vpp = NULLVP;
109796749Siedowse		return (error);
109896749Siedowse	}
109996749Siedowse	ip = VTOI(nvp);
110096749Siedowse	if (ip->i_mode == 0 ||
110196749Siedowse	    ip->i_gen != ufhp->ufid_gen || ip->i_nlink <= 0) {
110296749Siedowse		vput(nvp);
110396749Siedowse		*vpp = NULLVP;
110496749Siedowse		return (ESTALE);
110596749Siedowse	}
110696749Siedowse	*vpp = nvp;
1107140768Sphk	vnode_create_vobject(*vpp, 0, curthread);
110896749Siedowse	return (0);
110912115Sdyson}
111012115Sdyson
111112115Sdyson/*
111212115Sdyson * Write a superblock and associated information back to disk.
111312115Sdyson */
111412911Sphkstatic int
111512115Sdysonext2_sbupdate(mp, waitfor)
111696749Siedowse	struct ext2mount *mp;
111712115Sdyson	int waitfor;
111812115Sdyson{
111996752Siedowse	struct ext2_sb_info *fs = mp->um_e2fs;
112096752Siedowse	struct ext2_super_block *es = fs->s_es;
112196752Siedowse	struct buf *bp;
112241591Sarchie	int error = 0;
112312115Sdyson/*
112412115Sdysonprintf("\nupdating superblock, waitfor=%s\n", waitfor == MNT_WAIT ? "yes":"no");
112512115Sdyson*/
1126111856Sjeff	bp = getblk(mp->um_devvp, SBLOCK, SBSIZE, 0, 0, 0);
112712115Sdyson	bcopy((caddr_t)es, bp->b_data, (u_int)sizeof(struct ext2_super_block));
112812115Sdyson	if (waitfor == MNT_WAIT)
112912115Sdyson		error = bwrite(bp);
113012115Sdyson	else
113112115Sdyson		bawrite(bp);
113212115Sdyson
113327881Sdyson	/*
113427881Sdyson	 * The buffers for group descriptors, inode bitmaps and block bitmaps
113527881Sdyson	 * are not busy at this point and are (hopefully) written by the
113627881Sdyson	 * usual sync mechanism. No need to write them here
113712115Sdyson		 */
113812115Sdyson
113912115Sdyson	return (error);
114012115Sdyson}
114196749Siedowse
114296749Siedowse/*
114396749Siedowse * Return the root of a filesystem.
114496749Siedowse */
114596749Siedowsestatic int
1146144059Sjeffext2_root(mp, flags, vpp, td)
114796749Siedowse	struct mount *mp;
1148144059Sjeff	int flags;
114996749Siedowse	struct vnode **vpp;
1150132023Salfred	struct thread *td;
115196749Siedowse{
115296749Siedowse	struct vnode *nvp;
115396749Siedowse	int error;
115496749Siedowse
115596749Siedowse	error = VFS_VGET(mp, (ino_t)ROOTINO, LK_EXCLUSIVE, &nvp);
115696749Siedowse	if (error)
115796749Siedowse		return (error);
115896749Siedowse	*vpp = nvp;
115996749Siedowse	return (0);
116096749Siedowse}
1161