xref: /freebsd-src/sys/fs/ext2fs/ext2_vfsops.c (revision 56a8aca83ab5a93af05f4c8c3a358b71a8392af8)
1e09c00caSUlf Lilleengen /*-
2e09c00caSUlf Lilleengen  *  modified for EXT2FS support in Lites 1.1
3e09c00caSUlf Lilleengen  *
4e09c00caSUlf Lilleengen  *  Aug 1995, Godmar Back (gback@cs.utah.edu)
5e09c00caSUlf Lilleengen  *  University of Utah, Department of Computer Science
6e09c00caSUlf Lilleengen  */
7e09c00caSUlf Lilleengen /*-
851369649SPedro F. Giffuni  * SPDX-License-Identifier: BSD-3-Clause
951369649SPedro F. Giffuni  *
10e09c00caSUlf Lilleengen  * Copyright (c) 1989, 1991, 1993, 1994
11e09c00caSUlf Lilleengen  *	The Regents of the University of California.  All rights reserved.
12e09c00caSUlf Lilleengen  *
13e09c00caSUlf Lilleengen  * Redistribution and use in source and binary forms, with or without
14e09c00caSUlf Lilleengen  * modification, are permitted provided that the following conditions
15e09c00caSUlf Lilleengen  * are met:
16e09c00caSUlf Lilleengen  * 1. Redistributions of source code must retain the above copyright
17e09c00caSUlf Lilleengen  *    notice, this list of conditions and the following disclaimer.
18e09c00caSUlf Lilleengen  * 2. Redistributions in binary form must reproduce the above copyright
19e09c00caSUlf Lilleengen  *    notice, this list of conditions and the following disclaimer in the
20e09c00caSUlf Lilleengen  *    documentation and/or other materials provided with the distribution.
21fca15474SPedro F. Giffuni  * 3. Neither the name of the University nor the names of its contributors
22e09c00caSUlf Lilleengen  *    may be used to endorse or promote products derived from this software
23e09c00caSUlf Lilleengen  *    without specific prior written permission.
24e09c00caSUlf Lilleengen  *
25e09c00caSUlf Lilleengen  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26e09c00caSUlf Lilleengen  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27e09c00caSUlf Lilleengen  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28e09c00caSUlf Lilleengen  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29e09c00caSUlf Lilleengen  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30e09c00caSUlf Lilleengen  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31e09c00caSUlf Lilleengen  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32e09c00caSUlf Lilleengen  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33e09c00caSUlf Lilleengen  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34e09c00caSUlf Lilleengen  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35e09c00caSUlf Lilleengen  * SUCH DAMAGE.
36e09c00caSUlf Lilleengen  */
37e09c00caSUlf Lilleengen 
38e09c00caSUlf Lilleengen #include <sys/param.h>
39e09c00caSUlf Lilleengen #include <sys/systm.h>
40e09c00caSUlf Lilleengen #include <sys/namei.h>
41e09c00caSUlf Lilleengen #include <sys/priv.h>
42e09c00caSUlf Lilleengen #include <sys/proc.h>
43e09c00caSUlf Lilleengen #include <sys/kernel.h>
44e09c00caSUlf Lilleengen #include <sys/vnode.h>
45e09c00caSUlf Lilleengen #include <sys/mount.h>
46e09c00caSUlf Lilleengen #include <sys/bio.h>
47e09c00caSUlf Lilleengen #include <sys/buf.h>
48e09c00caSUlf Lilleengen #include <sys/conf.h>
49035e4e04SPedro F. Giffuni #include <sys/endian.h>
50e09c00caSUlf Lilleengen #include <sys/fcntl.h>
51e09c00caSUlf Lilleengen #include <sys/malloc.h>
52ebc94b66SFedor Uporov #include <sys/sdt.h>
53e09c00caSUlf Lilleengen #include <sys/stat.h>
54e09c00caSUlf Lilleengen #include <sys/mutex.h>
55e09c00caSUlf Lilleengen 
56e09c00caSUlf Lilleengen #include <geom/geom.h>
57e09c00caSUlf Lilleengen #include <geom/geom_vfs.h>
58e09c00caSUlf Lilleengen 
59e06e5241SFedor Uporov #include <fs/ext2fs/fs.h>
60e09c00caSUlf Lilleengen #include <fs/ext2fs/ext2_mount.h>
61e09c00caSUlf Lilleengen #include <fs/ext2fs/inode.h>
62e09c00caSUlf Lilleengen 
63e09c00caSUlf Lilleengen #include <fs/ext2fs/ext2fs.h>
6445641afbSJohn Baldwin #include <fs/ext2fs/ext2_dinode.h>
6545641afbSJohn Baldwin #include <fs/ext2fs/ext2_extern.h>
66e06e5241SFedor Uporov #include <fs/ext2fs/ext2_extents.h>
67e06e5241SFedor Uporov 
68ebc94b66SFedor Uporov SDT_PROVIDER_DECLARE(ext2fs);
69ebc94b66SFedor Uporov /*
70ebc94b66SFedor Uporov  * ext2fs trace probe:
71ebc94b66SFedor Uporov  * arg0: verbosity. Higher numbers give more verbose messages
72ebc94b66SFedor Uporov  * arg1: Textual message
73ebc94b66SFedor Uporov  */
74ebc94b66SFedor Uporov SDT_PROBE_DEFINE2(ext2fs, , vfsops, trace, "int", "char*");
75ebc94b66SFedor Uporov SDT_PROBE_DEFINE2(ext2fs, , vfsops, ext2_cg_validate_error, "char*", "int");
76ebc94b66SFedor Uporov SDT_PROBE_DEFINE1(ext2fs, , vfsops, ext2_compute_sb_data_error, "char*");
77ebc94b66SFedor Uporov 
78e09c00caSUlf Lilleengen static int	ext2_flushfiles(struct mount *mp, int flags, struct thread *td);
79e09c00caSUlf Lilleengen static int	ext2_mountfs(struct vnode *, struct mount *);
80e09c00caSUlf Lilleengen static int	ext2_reload(struct mount *mp, struct thread *td);
81e09c00caSUlf Lilleengen static int	ext2_sbupdate(struct ext2mount *, int);
82e09c00caSUlf Lilleengen static int	ext2_cgupdate(struct ext2mount *, int);
83e09c00caSUlf Lilleengen static vfs_unmount_t		ext2_unmount;
84e09c00caSUlf Lilleengen static vfs_root_t		ext2_root;
85e09c00caSUlf Lilleengen static vfs_statfs_t		ext2_statfs;
86e09c00caSUlf Lilleengen static vfs_sync_t		ext2_sync;
87e09c00caSUlf Lilleengen static vfs_vget_t		ext2_vget;
88e09c00caSUlf Lilleengen static vfs_fhtovp_t		ext2_fhtovp;
89e09c00caSUlf Lilleengen static vfs_mount_t		ext2_mount;
90e09c00caSUlf Lilleengen 
91e09c00caSUlf Lilleengen MALLOC_DEFINE(M_EXT2NODE, "ext2_node", "EXT2 vnode private part");
92e09c00caSUlf Lilleengen static MALLOC_DEFINE(M_EXT2MNT, "ext2_mount", "EXT2 mount structure");
93e09c00caSUlf Lilleengen 
94e09c00caSUlf Lilleengen static struct vfsops ext2fs_vfsops = {
95e09c00caSUlf Lilleengen 	.vfs_fhtovp =		ext2_fhtovp,
96e09c00caSUlf Lilleengen 	.vfs_mount =		ext2_mount,
97e09c00caSUlf Lilleengen 	.vfs_root =		ext2_root,	/* root inode via vget */
98e09c00caSUlf Lilleengen 	.vfs_statfs =		ext2_statfs,
99e09c00caSUlf Lilleengen 	.vfs_sync =		ext2_sync,
100e09c00caSUlf Lilleengen 	.vfs_unmount =		ext2_unmount,
101e09c00caSUlf Lilleengen 	.vfs_vget =		ext2_vget,
102e09c00caSUlf Lilleengen };
103e09c00caSUlf Lilleengen 
104e09c00caSUlf Lilleengen VFS_SET(ext2fs_vfsops, ext2fs, 0);
105e09c00caSUlf Lilleengen 
106e09c00caSUlf Lilleengen static int	ext2_check_sb_compat(struct ext2fs *es, struct cdev *dev,
107e09c00caSUlf Lilleengen 		    int ronly);
1086e38bf94SFedor Uporov static int	ext2_compute_sb_data(struct vnode * devvp,
109e09c00caSUlf Lilleengen 		    struct ext2fs * es, struct m_ext2fs * fs);
110e09c00caSUlf Lilleengen 
11184edda0aSJohn Baldwin static const char *ext2_opts[] = { "acls", "async", "noatime", "noclusterr",
11284edda0aSJohn Baldwin     "noclusterw", "noexec", "export", "force", "from", "multilabel",
11384edda0aSJohn Baldwin     "suiddir", "nosymfollow", "sync", "union", NULL };
114e09c00caSUlf Lilleengen 
115e09c00caSUlf Lilleengen /*
116e09c00caSUlf Lilleengen  * VFS Operations.
117e09c00caSUlf Lilleengen  *
118e09c00caSUlf Lilleengen  * mount system call
119e09c00caSUlf Lilleengen  */
120e09c00caSUlf Lilleengen static int
ext2_mount(struct mount * mp)121e09c00caSUlf Lilleengen ext2_mount(struct mount *mp)
122e09c00caSUlf Lilleengen {
123e09c00caSUlf Lilleengen 	struct vfsoptlist *opts;
124e09c00caSUlf Lilleengen 	struct vnode *devvp;
125e09c00caSUlf Lilleengen 	struct thread *td;
126f7a3729cSKevin Lo 	struct ext2mount *ump = NULL;
127e09c00caSUlf Lilleengen 	struct m_ext2fs *fs;
128e09c00caSUlf Lilleengen 	struct nameidata nd, *ndp = &nd;
129e09c00caSUlf Lilleengen 	accmode_t accmode;
130e09c00caSUlf Lilleengen 	char *path, *fspec;
131e09c00caSUlf Lilleengen 	int error, flags, len;
132e09c00caSUlf Lilleengen 
133e09c00caSUlf Lilleengen 	td = curthread;
134e09c00caSUlf Lilleengen 	opts = mp->mnt_optnew;
135e09c00caSUlf Lilleengen 
136e09c00caSUlf Lilleengen 	if (vfs_filteropt(opts, ext2_opts))
137e09c00caSUlf Lilleengen 		return (EINVAL);
138e09c00caSUlf Lilleengen 
139e09c00caSUlf Lilleengen 	vfs_getopt(opts, "fspath", (void **)&path, NULL);
140e09c00caSUlf Lilleengen 	/* Double-check the length of path.. */
141757224cbSPedro F. Giffuni 	if (strlen(path) >= MAXMNTLEN)
142e09c00caSUlf Lilleengen 		return (ENAMETOOLONG);
143e09c00caSUlf Lilleengen 
144e09c00caSUlf Lilleengen 	fspec = NULL;
145e09c00caSUlf Lilleengen 	error = vfs_getopt(opts, "from", (void **)&fspec, &len);
146e09c00caSUlf Lilleengen 	if (!error && fspec[len - 1] != '\0')
147e09c00caSUlf Lilleengen 		return (EINVAL);
148e09c00caSUlf Lilleengen 
149e09c00caSUlf Lilleengen 	/*
150e09c00caSUlf Lilleengen 	 * If updating, check whether changing from read-only to
151e09c00caSUlf Lilleengen 	 * read/write; if there is no device name, that's all we do.
152e09c00caSUlf Lilleengen 	 */
153e09c00caSUlf Lilleengen 	if (mp->mnt_flag & MNT_UPDATE) {
154e09c00caSUlf Lilleengen 		ump = VFSTOEXT2(mp);
155e09c00caSUlf Lilleengen 		fs = ump->um_e2fs;
156e09c00caSUlf Lilleengen 		error = 0;
157e09c00caSUlf Lilleengen 		if (fs->e2fs_ronly == 0 &&
158e09c00caSUlf Lilleengen 		    vfs_flagopt(opts, "ro", NULL, 0)) {
159e09c00caSUlf Lilleengen 			error = VFS_SYNC(mp, MNT_WAIT);
160e09c00caSUlf Lilleengen 			if (error)
161e09c00caSUlf Lilleengen 				return (error);
162e09c00caSUlf Lilleengen 			flags = WRITECLOSE;
163e09c00caSUlf Lilleengen 			if (mp->mnt_flag & MNT_FORCE)
164e09c00caSUlf Lilleengen 				flags |= FORCECLOSE;
165e09c00caSUlf Lilleengen 			error = ext2_flushfiles(mp, flags, td);
166c0f16c65SFedor Uporov 			if (error == 0 && fs->e2fs_wasvalid &&
167c0f16c65SFedor Uporov 			    ext2_cgupdate(ump, MNT_WAIT) == 0) {
168cd3acfe7SFedor Uporov 				fs->e2fs->e2fs_state =
169cd3acfe7SFedor Uporov 				    htole16((le16toh(fs->e2fs->e2fs_state) |
170cd3acfe7SFedor Uporov 				    E2FS_ISCLEAN));
171e09c00caSUlf Lilleengen 				ext2_sbupdate(ump, MNT_WAIT);
172e09c00caSUlf Lilleengen 			}
173e09c00caSUlf Lilleengen 			fs->e2fs_ronly = 1;
174e09c00caSUlf Lilleengen 			vfs_flagopt(opts, "ro", &mp->mnt_flag, MNT_RDONLY);
175e09c00caSUlf Lilleengen 			g_topology_lock();
176e09c00caSUlf Lilleengen 			g_access(ump->um_cp, 0, -1, 0);
177e09c00caSUlf Lilleengen 			g_topology_unlock();
178e09c00caSUlf Lilleengen 		}
179e09c00caSUlf Lilleengen 		if (!error && (mp->mnt_flag & MNT_RELOAD))
180e09c00caSUlf Lilleengen 			error = ext2_reload(mp, td);
181e09c00caSUlf Lilleengen 		if (error)
182e09c00caSUlf Lilleengen 			return (error);
183e09c00caSUlf Lilleengen 		devvp = ump->um_devvp;
184e09c00caSUlf Lilleengen 		if (fs->e2fs_ronly && !vfs_flagopt(opts, "ro", NULL, 0)) {
185e09c00caSUlf Lilleengen 			if (ext2_check_sb_compat(fs->e2fs, devvp->v_rdev, 0))
186e09c00caSUlf Lilleengen 				return (EPERM);
187e09c00caSUlf Lilleengen 
188e09c00caSUlf Lilleengen 			/*
189e09c00caSUlf Lilleengen 			 * If upgrade to read-write by non-root, then verify
190e09c00caSUlf Lilleengen 			 * that user has necessary permissions on the device.
191e09c00caSUlf Lilleengen 			 */
192e09c00caSUlf Lilleengen 			vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
193e09c00caSUlf Lilleengen 			error = VOP_ACCESS(devvp, VREAD | VWRITE,
194e09c00caSUlf Lilleengen 			    td->td_ucred, td);
195e09c00caSUlf Lilleengen 			if (error)
196e09c00caSUlf Lilleengen 				error = priv_check(td, PRIV_VFS_MOUNT_PERM);
197e09c00caSUlf Lilleengen 			if (error) {
198b249ce48SMateusz Guzik 				VOP_UNLOCK(devvp);
199e09c00caSUlf Lilleengen 				return (error);
200e09c00caSUlf Lilleengen 			}
201b249ce48SMateusz Guzik 			VOP_UNLOCK(devvp);
202e09c00caSUlf Lilleengen 			g_topology_lock();
203e09c00caSUlf Lilleengen 			error = g_access(ump->um_cp, 0, 1, 0);
204e09c00caSUlf Lilleengen 			g_topology_unlock();
205e09c00caSUlf Lilleengen 			if (error)
206e09c00caSUlf Lilleengen 				return (error);
207e09c00caSUlf Lilleengen 
208cd3acfe7SFedor Uporov 			if ((le16toh(fs->e2fs->e2fs_state) & E2FS_ISCLEAN) == 0 ||
209cd3acfe7SFedor Uporov 			    (le16toh(fs->e2fs->e2fs_state) & E2FS_ERRORS)) {
210e09c00caSUlf Lilleengen 				if (mp->mnt_flag & MNT_FORCE) {
211e09c00caSUlf Lilleengen 					printf(
212e09c00caSUlf Lilleengen "WARNING: %s was not properly dismounted\n", fs->e2fs_fsmnt);
213e09c00caSUlf Lilleengen 				} else {
214e09c00caSUlf Lilleengen 					printf(
215e09c00caSUlf Lilleengen "WARNING: R/W mount of %s denied.  Filesystem is not clean - run fsck\n",
216e09c00caSUlf Lilleengen 					    fs->e2fs_fsmnt);
217e09c00caSUlf Lilleengen 					return (EPERM);
218e09c00caSUlf Lilleengen 				}
219e09c00caSUlf Lilleengen 			}
220cd3acfe7SFedor Uporov 			fs->e2fs->e2fs_state =
221cd3acfe7SFedor Uporov 			    htole16(le16toh(fs->e2fs->e2fs_state) & ~E2FS_ISCLEAN);
222e09c00caSUlf Lilleengen 			(void)ext2_cgupdate(ump, MNT_WAIT);
223e09c00caSUlf Lilleengen 			fs->e2fs_ronly = 0;
224e09c00caSUlf Lilleengen 			MNT_ILOCK(mp);
225e09c00caSUlf Lilleengen 			mp->mnt_flag &= ~MNT_RDONLY;
226e09c00caSUlf Lilleengen 			MNT_IUNLOCK(mp);
227e09c00caSUlf Lilleengen 		}
228e09c00caSUlf Lilleengen 		if (vfs_flagopt(opts, "export", NULL, 0)) {
229e09c00caSUlf Lilleengen 			/* Process export requests in vfs_mount.c. */
230e09c00caSUlf Lilleengen 			return (error);
231e09c00caSUlf Lilleengen 		}
232e09c00caSUlf Lilleengen 	}
233e09c00caSUlf Lilleengen 
234e09c00caSUlf Lilleengen 	/*
235e09c00caSUlf Lilleengen 	 * Not an update, or updating the name: look up the name
236e09c00caSUlf Lilleengen 	 * and verify that it refers to a sensible disk device.
237e09c00caSUlf Lilleengen 	 */
238e09c00caSUlf Lilleengen 	if (fspec == NULL)
239e09c00caSUlf Lilleengen 		return (EINVAL);
2407e1d3eefSMateusz Guzik 	NDINIT(ndp, LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE, fspec);
241e09c00caSUlf Lilleengen 	if ((error = namei(ndp)) != 0)
242e09c00caSUlf Lilleengen 		return (error);
243bb92cd7bSMateusz Guzik 	NDFREE_PNBUF(ndp);
244e09c00caSUlf Lilleengen 	devvp = ndp->ni_vp;
245e09c00caSUlf Lilleengen 
2467ad2a82dSMateusz Guzik 	if (!vn_isdisk_error(devvp, &error)) {
247e09c00caSUlf Lilleengen 		vput(devvp);
248e09c00caSUlf Lilleengen 		return (error);
249e09c00caSUlf Lilleengen 	}
250e09c00caSUlf Lilleengen 
251e09c00caSUlf Lilleengen 	/*
252e09c00caSUlf Lilleengen 	 * If mount by non-root, then verify that user has necessary
253e09c00caSUlf Lilleengen 	 * permissions on the device.
254e09c00caSUlf Lilleengen 	 *
255e09c00caSUlf Lilleengen 	 * XXXRW: VOP_ACCESS() enough?
256e09c00caSUlf Lilleengen 	 */
257e09c00caSUlf Lilleengen 	accmode = VREAD;
258e09c00caSUlf Lilleengen 	if ((mp->mnt_flag & MNT_RDONLY) == 0)
259e09c00caSUlf Lilleengen 		accmode |= VWRITE;
260e09c00caSUlf Lilleengen 	error = VOP_ACCESS(devvp, accmode, td->td_ucred, td);
261e09c00caSUlf Lilleengen 	if (error)
262e09c00caSUlf Lilleengen 		error = priv_check(td, PRIV_VFS_MOUNT_PERM);
263e09c00caSUlf Lilleengen 	if (error) {
264e09c00caSUlf Lilleengen 		vput(devvp);
265e09c00caSUlf Lilleengen 		return (error);
266e09c00caSUlf Lilleengen 	}
267e09c00caSUlf Lilleengen 
268e09c00caSUlf Lilleengen 	if ((mp->mnt_flag & MNT_UPDATE) == 0) {
269e09c00caSUlf Lilleengen 		error = ext2_mountfs(devvp, mp);
270e09c00caSUlf Lilleengen 	} else {
271e09c00caSUlf Lilleengen 		if (devvp != ump->um_devvp) {
272e09c00caSUlf Lilleengen 			vput(devvp);
273e09c00caSUlf Lilleengen 			return (EINVAL);	/* needs translation */
274e09c00caSUlf Lilleengen 		} else
275e09c00caSUlf Lilleengen 			vput(devvp);
276e09c00caSUlf Lilleengen 	}
277e09c00caSUlf Lilleengen 	if (error) {
278e09c00caSUlf Lilleengen 		vrele(devvp);
279e09c00caSUlf Lilleengen 		return (error);
280e09c00caSUlf Lilleengen 	}
281e09c00caSUlf Lilleengen 	ump = VFSTOEXT2(mp);
282e09c00caSUlf Lilleengen 	fs = ump->um_e2fs;
283e09c00caSUlf Lilleengen 
284e09c00caSUlf Lilleengen 	/*
285e09c00caSUlf Lilleengen 	 * Note that this strncpy() is ok because of a check at the start
286e09c00caSUlf Lilleengen 	 * of ext2_mount().
287e09c00caSUlf Lilleengen 	 */
288e09c00caSUlf Lilleengen 	strncpy(fs->e2fs_fsmnt, path, MAXMNTLEN);
289e09c00caSUlf Lilleengen 	fs->e2fs_fsmnt[MAXMNTLEN - 1] = '\0';
290e09c00caSUlf Lilleengen 	vfs_mountedfrom(mp, fspec);
291e09c00caSUlf Lilleengen 	return (0);
292e09c00caSUlf Lilleengen }
293e09c00caSUlf Lilleengen 
294e09c00caSUlf Lilleengen static int
ext2_check_sb_compat(struct ext2fs * es,struct cdev * dev,int ronly)295e09c00caSUlf Lilleengen ext2_check_sb_compat(struct ext2fs *es, struct cdev *dev, int ronly)
296e09c00caSUlf Lilleengen {
2973d851dbeSPedro F. Giffuni 	uint32_t i, mask;
298e09c00caSUlf Lilleengen 
299cd3acfe7SFedor Uporov 	if (le16toh(es->e2fs_magic) != E2FS_MAGIC) {
300e09c00caSUlf Lilleengen 		printf("ext2fs: %s: wrong magic number %#x (expected %#x)\n",
301cd3acfe7SFedor Uporov 		    devtoname(dev), le16toh(es->e2fs_magic), E2FS_MAGIC);
302e09c00caSUlf Lilleengen 		return (1);
303e09c00caSUlf Lilleengen 	}
304cd3acfe7SFedor Uporov 	if (le32toh(es->e2fs_rev) > E2FS_REV0) {
305cd3acfe7SFedor Uporov 		mask = le32toh(es->e2fs_features_incompat) & ~(EXT2F_INCOMPAT_SUPP);
3063d851dbeSPedro F. Giffuni 		if (mask) {
3073d851dbeSPedro F. Giffuni 			printf("WARNING: mount of %s denied due to "
3083d851dbeSPedro F. Giffuni 			    "unsupported optional features:\n", devtoname(dev));
3093d851dbeSPedro F. Giffuni 			for (i = 0;
3103d851dbeSPedro F. Giffuni 			    i < sizeof(incompat)/sizeof(struct ext2_feature);
3113d851dbeSPedro F. Giffuni 			    i++)
3123d851dbeSPedro F. Giffuni 				if (mask & incompat[i].mask)
3133d851dbeSPedro F. Giffuni 					printf("%s ", incompat[i].name);
3143d851dbeSPedro F. Giffuni 			printf("\n");
315e09c00caSUlf Lilleengen 			return (1);
316e09c00caSUlf Lilleengen 		}
317cd3acfe7SFedor Uporov 		mask = le32toh(es->e2fs_features_rocompat) & ~EXT2F_ROCOMPAT_SUPP;
3183d851dbeSPedro F. Giffuni 		if (!ronly && mask) {
319e09c00caSUlf Lilleengen 			printf("WARNING: R/W mount of %s denied due to "
3203d851dbeSPedro F. Giffuni 			    "unsupported optional features:\n", devtoname(dev));
3213d851dbeSPedro F. Giffuni 			for (i = 0;
3223d851dbeSPedro F. Giffuni 			    i < sizeof(ro_compat)/sizeof(struct ext2_feature);
3233d851dbeSPedro F. Giffuni 			    i++)
3243d851dbeSPedro F. Giffuni 				if (mask & ro_compat[i].mask)
3253d851dbeSPedro F. Giffuni 					printf("%s ", ro_compat[i].name);
3263d851dbeSPedro F. Giffuni 			printf("\n");
327e09c00caSUlf Lilleengen 			return (1);
328e09c00caSUlf Lilleengen 		}
329e09c00caSUlf Lilleengen 	}
330e09c00caSUlf Lilleengen 	return (0);
331e09c00caSUlf Lilleengen }
332e09c00caSUlf Lilleengen 
333c0f16c65SFedor Uporov static e4fs_daddr_t
ext2_cg_location(struct m_ext2fs * fs,int number)3346e38bf94SFedor Uporov ext2_cg_location(struct m_ext2fs *fs, int number)
335c0f16c65SFedor Uporov {
336c0f16c65SFedor Uporov 	int cg, descpb, logical_sb, has_super = 0;
337c0f16c65SFedor Uporov 
338c0f16c65SFedor Uporov 	/*
339c0f16c65SFedor Uporov 	 * Adjust logical superblock block number.
340c0f16c65SFedor Uporov 	 * Godmar thinks: if the blocksize is greater than 1024, then
341c0f16c65SFedor Uporov 	 * the superblock is logically part of block zero.
342c0f16c65SFedor Uporov 	 */
34350be18ccSFedor Uporov 	logical_sb = fs->e2fs_bsize > SBLOCKSIZE ? 0 : 1;
344c0f16c65SFedor Uporov 
345c0f16c65SFedor Uporov 	if (!EXT2_HAS_INCOMPAT_FEATURE(fs, EXT2F_INCOMPAT_META_BG) ||
346cd3acfe7SFedor Uporov 	    number < le32toh(fs->e2fs->e3fs_first_meta_bg))
347c0f16c65SFedor Uporov 		return (logical_sb + number + 1);
348c0f16c65SFedor Uporov 
349c0f16c65SFedor Uporov 	if (EXT2_HAS_INCOMPAT_FEATURE(fs, EXT2F_INCOMPAT_64BIT))
350c0f16c65SFedor Uporov 		descpb = fs->e2fs_bsize / sizeof(struct ext2_gd);
351c0f16c65SFedor Uporov 	else
352c0f16c65SFedor Uporov 		descpb = fs->e2fs_bsize / E2FS_REV0_GD_SIZE;
353c0f16c65SFedor Uporov 
354c0f16c65SFedor Uporov 	cg = descpb * number;
355c0f16c65SFedor Uporov 
356c0f16c65SFedor Uporov 	if (ext2_cg_has_sb(fs, cg))
357c0f16c65SFedor Uporov 		has_super = 1;
358c0f16c65SFedor Uporov 
359c0f16c65SFedor Uporov 	return (has_super + cg * (e4fs_daddr_t)EXT2_BLOCKS_PER_GROUP(fs) +
360cd3acfe7SFedor Uporov 	    le32toh(fs->e2fs->e2fs_first_dblock));
361c0f16c65SFedor Uporov }
362c0f16c65SFedor Uporov 
3636e38bf94SFedor Uporov static int
ext2_cg_validate(struct m_ext2fs * fs)3646e38bf94SFedor Uporov ext2_cg_validate(struct m_ext2fs *fs)
3656e38bf94SFedor Uporov {
3666e38bf94SFedor Uporov 	uint64_t b_bitmap;
3676e38bf94SFedor Uporov 	uint64_t i_bitmap;
3686e38bf94SFedor Uporov 	uint64_t i_tables;
3696e38bf94SFedor Uporov 	uint64_t first_block, last_block, last_cg_block;
3706e38bf94SFedor Uporov 	struct ext2_gd *gd;
3716e38bf94SFedor Uporov 	unsigned int i, cg_count;
3726e38bf94SFedor Uporov 
373cd3acfe7SFedor Uporov 	first_block = le32toh(fs->e2fs->e2fs_first_dblock);
3746e38bf94SFedor Uporov 	last_cg_block = ext2_cg_number_gdb(fs, 0);
3756e38bf94SFedor Uporov 	cg_count = fs->e2fs_gcount;
3766e38bf94SFedor Uporov 
3776e38bf94SFedor Uporov 	for (i = 0; i < fs->e2fs_gcount; i++) {
3786e38bf94SFedor Uporov 		gd = &fs->e2fs_gd[i];
3796e38bf94SFedor Uporov 
3806e38bf94SFedor Uporov 		if (EXT2_HAS_INCOMPAT_FEATURE(fs, EXT2F_INCOMPAT_FLEX_BG) ||
3816e38bf94SFedor Uporov 		    i == fs->e2fs_gcount - 1) {
3826e38bf94SFedor Uporov 			last_block = fs->e2fs_bcount - 1;
3836e38bf94SFedor Uporov 		} else {
3846e38bf94SFedor Uporov 			last_block = first_block +
3856e38bf94SFedor Uporov 			    (EXT2_BLOCKS_PER_GROUP(fs) - 1);
3866e38bf94SFedor Uporov 		}
3876e38bf94SFedor Uporov 
3886e38bf94SFedor Uporov 		if ((cg_count == fs->e2fs_gcount) &&
389cd3acfe7SFedor Uporov 		    !(le16toh(gd->ext4bgd_flags) & EXT2_BG_INODE_ZEROED))
3906e38bf94SFedor Uporov 			cg_count = i;
3916e38bf94SFedor Uporov 
3926e38bf94SFedor Uporov 		b_bitmap = e2fs_gd_get_b_bitmap(gd);
3936e38bf94SFedor Uporov 		if (b_bitmap == 0) {
394ebc94b66SFedor Uporov 			SDT_PROBE2(ext2fs, , vfsops, ext2_cg_validate_error,
395ebc94b66SFedor Uporov 			    "block bitmap is zero", i);
3966e38bf94SFedor Uporov 			return (EINVAL);
3976e38bf94SFedor Uporov 		}
3986e38bf94SFedor Uporov 		if (b_bitmap <= last_cg_block) {
399ebc94b66SFedor Uporov 			SDT_PROBE2(ext2fs, , vfsops, ext2_cg_validate_error,
400ebc94b66SFedor Uporov 			    "block bitmap overlaps gds", i);
4016e38bf94SFedor Uporov 			return (EINVAL);
4026e38bf94SFedor Uporov 		}
4036e38bf94SFedor Uporov 		if (b_bitmap < first_block || b_bitmap > last_block) {
404ebc94b66SFedor Uporov 			SDT_PROBE2(ext2fs, , vfsops, ext2_cg_validate_error,
405ebc94b66SFedor Uporov 			    "block bitmap not in group", i);
4066e38bf94SFedor Uporov 			return (EINVAL);
4076e38bf94SFedor Uporov 		}
4086e38bf94SFedor Uporov 
4096e38bf94SFedor Uporov 		i_bitmap = e2fs_gd_get_i_bitmap(gd);
4106e38bf94SFedor Uporov 		if (i_bitmap == 0) {
411ebc94b66SFedor Uporov 			SDT_PROBE2(ext2fs, , vfsops, ext2_cg_validate_error,
412ebc94b66SFedor Uporov 			    "inode bitmap is zero", i);
4136e38bf94SFedor Uporov 			return (EINVAL);
4146e38bf94SFedor Uporov 		}
4156e38bf94SFedor Uporov 		if (i_bitmap <= last_cg_block) {
416ebc94b66SFedor Uporov 			SDT_PROBE2(ext2fs, , vfsops, ext2_cg_validate_error,
417ebc94b66SFedor Uporov 			    "inode bitmap overlaps gds", i);
4186e38bf94SFedor Uporov 			return (EINVAL);
4196e38bf94SFedor Uporov 		}
4206e38bf94SFedor Uporov 		if (i_bitmap < first_block || i_bitmap > last_block) {
421ebc94b66SFedor Uporov 			SDT_PROBE2(ext2fs, , vfsops, ext2_cg_validate_error,
422ebc94b66SFedor Uporov 			    "inode bitmap not in group blk", i);
4236e38bf94SFedor Uporov 			return (EINVAL);
4246e38bf94SFedor Uporov 		}
4256e38bf94SFedor Uporov 
4266e38bf94SFedor Uporov 		i_tables = e2fs_gd_get_i_tables(gd);
4276e38bf94SFedor Uporov 		if (i_tables == 0) {
428ebc94b66SFedor Uporov 			SDT_PROBE2(ext2fs, , vfsops, ext2_cg_validate_error,
429ebc94b66SFedor Uporov 			    "inode table is zero", i);
4306e38bf94SFedor Uporov 			return (EINVAL);
4316e38bf94SFedor Uporov 		}
4326e38bf94SFedor Uporov 		if (i_tables <= last_cg_block) {
433ebc94b66SFedor Uporov 			SDT_PROBE2(ext2fs, , vfsops, ext2_cg_validate_error,
43447f880ebSGordon Bergling 			    "inode tables overlaps gds", i);
4356e38bf94SFedor Uporov 			return (EINVAL);
4366e38bf94SFedor Uporov 		}
4376e38bf94SFedor Uporov 		if (i_tables < first_block ||
4386e38bf94SFedor Uporov 		    i_tables + fs->e2fs_itpg - 1 > last_block) {
439ebc94b66SFedor Uporov 			SDT_PROBE2(ext2fs, , vfsops, ext2_cg_validate_error,
440ebc94b66SFedor Uporov 			    "inode tables not in group blk", i);
4416e38bf94SFedor Uporov 			return (EINVAL);
4426e38bf94SFedor Uporov 		}
4436e38bf94SFedor Uporov 
4446e38bf94SFedor Uporov 		if (!EXT2_HAS_INCOMPAT_FEATURE(fs, EXT2F_INCOMPAT_FLEX_BG))
4456e38bf94SFedor Uporov 			first_block += EXT2_BLOCKS_PER_GROUP(fs);
4466e38bf94SFedor Uporov 	}
4476e38bf94SFedor Uporov 
4486e38bf94SFedor Uporov 	return (0);
4496e38bf94SFedor Uporov }
4506e38bf94SFedor Uporov 
451e09c00caSUlf Lilleengen /*
4522b3506d9SKevin Lo  * This computes the fields of the m_ext2fs structure from the
4532b3506d9SKevin Lo  * data in the ext2fs structure read in.
454e09c00caSUlf Lilleengen  */
455e09c00caSUlf Lilleengen static int
ext2_compute_sb_data(struct vnode * devvp,struct ext2fs * es,struct m_ext2fs * fs)4566e38bf94SFedor Uporov ext2_compute_sb_data(struct vnode *devvp, struct ext2fs *es,
457e09c00caSUlf Lilleengen     struct m_ext2fs *fs)
458e09c00caSUlf Lilleengen {
459e09c00caSUlf Lilleengen 	struct buf *bp;
4603acd9182SFedor Uporov 	uint32_t e2fs_descpb, e2fs_gdbcount_alloc;
4616e38bf94SFedor Uporov 	int i, j;
4626e38bf94SFedor Uporov 	int g_count = 0;
4636e38bf94SFedor Uporov 	int error;
464e09c00caSUlf Lilleengen 
4656e38bf94SFedor Uporov 	/* Check checksum features */
4666e38bf94SFedor Uporov 	if (EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_GDT_CSUM) &&
4676e38bf94SFedor Uporov 	    EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) {
468ebc94b66SFedor Uporov 		SDT_PROBE1(ext2fs, , vfsops, ext2_compute_sb_data_error,
469ebc94b66SFedor Uporov 		    "incorrect checksum features combination");
4706e38bf94SFedor Uporov 		return (EINVAL);
4713acd9182SFedor Uporov 	}
4726e38bf94SFedor Uporov 
4736e38bf94SFedor Uporov 	/* Precompute checksum seed for all metadata */
4746e38bf94SFedor Uporov 	ext2_sb_csum_set_seed(fs);
4756e38bf94SFedor Uporov 
4766e38bf94SFedor Uporov 	/* Verify sb csum if possible */
4776e38bf94SFedor Uporov 	if (EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) {
4786e38bf94SFedor Uporov 		error = ext2_sb_csum_verify(fs);
4796e38bf94SFedor Uporov 		if (error) {
4806e38bf94SFedor Uporov 			return (error);
4816e38bf94SFedor Uporov 		}
4826e38bf94SFedor Uporov 	}
4836e38bf94SFedor Uporov 
4846e38bf94SFedor Uporov 	/* Check for block size = 1K|2K|4K */
485cd3acfe7SFedor Uporov 	if (le32toh(es->e2fs_log_bsize) > 2) {
486ebc94b66SFedor Uporov 		SDT_PROBE1(ext2fs, , vfsops, ext2_compute_sb_data_error,
487ebc94b66SFedor Uporov 		    "bad block size");
4886e38bf94SFedor Uporov 		return (EINVAL);
4896e38bf94SFedor Uporov 	}
4906e38bf94SFedor Uporov 
491cd3acfe7SFedor Uporov 	fs->e2fs_bshift = EXT2_MIN_BLOCK_LOG_SIZE + le32toh(es->e2fs_log_bsize);
4924e1e0e25SPedro F. Giffuni 	fs->e2fs_bsize = 1U << fs->e2fs_bshift;
493cd3acfe7SFedor Uporov 	fs->e2fs_fsbtodb = le32toh(es->e2fs_log_bsize) + 1;
494e09c00caSUlf Lilleengen 	fs->e2fs_qbmask = fs->e2fs_bsize - 1;
4956e38bf94SFedor Uporov 
4966e38bf94SFedor Uporov 	/* Check for fragment size */
497cd3acfe7SFedor Uporov 	if (le32toh(es->e2fs_log_fsize) >
4986e38bf94SFedor Uporov 	    (EXT2_MAX_FRAG_LOG_SIZE - EXT2_MIN_BLOCK_LOG_SIZE)) {
499ebc94b66SFedor Uporov 		SDT_PROBE1(ext2fs, , vfsops, ext2_compute_sb_data_error,
500ebc94b66SFedor Uporov 		    "invalid log cluster size");
5016e38bf94SFedor Uporov 		return (EINVAL);
5026e38bf94SFedor Uporov 	}
5036e38bf94SFedor Uporov 
504cd3acfe7SFedor Uporov 	fs->e2fs_fsize = EXT2_MIN_FRAG_SIZE << le32toh(es->e2fs_log_fsize);
5056e38bf94SFedor Uporov 	if (fs->e2fs_fsize != fs->e2fs_bsize) {
506ebc94b66SFedor Uporov 		SDT_PROBE1(ext2fs, , vfsops, ext2_compute_sb_data_error,
507ebc94b66SFedor Uporov 		    "fragment size != block size");
5086e38bf94SFedor Uporov 		return (EINVAL);
5096e38bf94SFedor Uporov 	}
5106e38bf94SFedor Uporov 
511e09c00caSUlf Lilleengen 	fs->e2fs_fpb = fs->e2fs_bsize / fs->e2fs_fsize;
5126e38bf94SFedor Uporov 
5136e38bf94SFedor Uporov 	/* Check reserved gdt blocks for future filesystem expansion */
514cd3acfe7SFedor Uporov 	if (le16toh(es->e2fs_reserved_ngdb) > (fs->e2fs_bsize / 4)) {
515ebc94b66SFedor Uporov 		SDT_PROBE1(ext2fs, , vfsops, ext2_compute_sb_data_error,
516ebc94b66SFedor Uporov 		    "number of reserved GDT blocks too large");
5176e38bf94SFedor Uporov 		return (EINVAL);
5186e38bf94SFedor Uporov 	}
5196e38bf94SFedor Uporov 
520cd3acfe7SFedor Uporov 	if (le32toh(es->e2fs_rev) == E2FS_REV0) {
521e09c00caSUlf Lilleengen 		fs->e2fs_isize = E2FS_REV0_INODE_SIZE;
522e09c00caSUlf Lilleengen 	} else {
523cd3acfe7SFedor Uporov 		fs->e2fs_isize = le16toh(es->e2fs_inode_size);
524e09c00caSUlf Lilleengen 
525e09c00caSUlf Lilleengen 		/*
5266e38bf94SFedor Uporov 		 * Check first ino.
5276e38bf94SFedor Uporov 		 */
528cd3acfe7SFedor Uporov 		if (le32toh(es->e2fs_first_ino) < EXT2_FIRSTINO) {
529ebc94b66SFedor Uporov 			SDT_PROBE1(ext2fs, , vfsops, ext2_compute_sb_data_error,
530ebc94b66SFedor Uporov 			    "invalid first ino");
5316e38bf94SFedor Uporov 			return (EINVAL);
5326e38bf94SFedor Uporov 		}
5336e38bf94SFedor Uporov 
5346e38bf94SFedor Uporov 		/*
535e09c00caSUlf Lilleengen 		 * Simple sanity check for superblock inode size value.
536e09c00caSUlf Lilleengen 		 */
537035e4e04SPedro F. Giffuni 		if (EXT2_INODE_SIZE(fs) < E2FS_REV0_INODE_SIZE ||
538035e4e04SPedro F. Giffuni 		    EXT2_INODE_SIZE(fs) > fs->e2fs_bsize ||
539e09c00caSUlf Lilleengen 		    (fs->e2fs_isize & (fs->e2fs_isize - 1)) != 0) {
540ebc94b66SFedor Uporov 			SDT_PROBE1(ext2fs, , vfsops, ext2_compute_sb_data_error,
541ebc94b66SFedor Uporov 			    "invalid inode size");
542035e4e04SPedro F. Giffuni 			return (EINVAL);
543035e4e04SPedro F. Giffuni 		}
544512f29d1SFedor Uporov 	}
5456e38bf94SFedor Uporov 
5466e38bf94SFedor Uporov 	/* Check group descriptors */
547d23db91eSPedro F. Giffuni 	if (EXT2_HAS_INCOMPAT_FEATURE(fs, EXT2F_INCOMPAT_64BIT) &&
548cd3acfe7SFedor Uporov 	    le16toh(es->e3fs_desc_size) != E2FS_64BIT_GD_SIZE) {
549ebc94b66SFedor Uporov 		SDT_PROBE1(ext2fs, , vfsops, ext2_compute_sb_data_error,
550ebc94b66SFedor Uporov 		    "unsupported 64bit descriptor size");
551d23db91eSPedro F. Giffuni 		return (EINVAL);
552d23db91eSPedro F. Giffuni 	}
5536e38bf94SFedor Uporov 
554cd3acfe7SFedor Uporov 	fs->e2fs_bpg = le32toh(es->e2fs_bpg);
555cd3acfe7SFedor Uporov 	fs->e2fs_fpg = le32toh(es->e2fs_fpg);
5566e38bf94SFedor Uporov 	if (fs->e2fs_bpg == 0 || fs->e2fs_fpg == 0) {
557ebc94b66SFedor Uporov 		SDT_PROBE1(ext2fs, , vfsops, ext2_compute_sb_data_error,
558ebc94b66SFedor Uporov 		    "zero blocks/fragments per group");
559771ec59bSPedro F. Giffuni 		return (EINVAL);
5606ce04e59SFedor Uporov 	} else if (fs->e2fs_bpg != fs->e2fs_fpg) {
5616ce04e59SFedor Uporov 		SDT_PROBE1(ext2fs, , vfsops, ext2_compute_sb_data_error,
5626ce04e59SFedor Uporov 		    "blocks per group not equal fragments per group");
5636ce04e59SFedor Uporov 		return (EINVAL);
564771ec59bSPedro F. Giffuni 	}
5656ce04e59SFedor Uporov 
566c0f16c65SFedor Uporov 	if (fs->e2fs_bpg != fs->e2fs_bsize * 8) {
567ebc94b66SFedor Uporov 		SDT_PROBE1(ext2fs, , vfsops, ext2_compute_sb_data_error,
568ebc94b66SFedor Uporov 		    "non-standard group size unsupported");
569c0f16c65SFedor Uporov 		return (EINVAL);
570c0f16c65SFedor Uporov 	}
571035e4e04SPedro F. Giffuni 
572e09c00caSUlf Lilleengen 	fs->e2fs_ipb = fs->e2fs_bsize / EXT2_INODE_SIZE(fs);
5736e38bf94SFedor Uporov 	if (fs->e2fs_ipb == 0 ||
5746e38bf94SFedor Uporov 	    fs->e2fs_ipb > fs->e2fs_bsize / E2FS_REV0_INODE_SIZE) {
575ebc94b66SFedor Uporov 		SDT_PROBE1(ext2fs, , vfsops, ext2_compute_sb_data_error,
576ebc94b66SFedor Uporov 		    "bad inodes per block size");
577771ec59bSPedro F. Giffuni 		return (EINVAL);
578771ec59bSPedro F. Giffuni 	}
5796e38bf94SFedor Uporov 
580cd3acfe7SFedor Uporov 	fs->e2fs_ipg = le32toh(es->e2fs_ipg);
5816e38bf94SFedor Uporov 	if (fs->e2fs_ipg < fs->e2fs_ipb || fs->e2fs_ipg >  fs->e2fs_bsize * 8) {
582ebc94b66SFedor Uporov 		SDT_PROBE1(ext2fs, , vfsops, ext2_compute_sb_data_error,
583ebc94b66SFedor Uporov 		    "invalid inodes per group");
584771ec59bSPedro F. Giffuni 		return (EINVAL);
585771ec59bSPedro F. Giffuni 	}
5866e38bf94SFedor Uporov 
5876e38bf94SFedor Uporov 	fs->e2fs_itpg = fs->e2fs_ipg / fs->e2fs_ipb;
5886e38bf94SFedor Uporov 
589cd3acfe7SFedor Uporov 	fs->e2fs_bcount = le32toh(es->e2fs_bcount);
590cd3acfe7SFedor Uporov 	fs->e2fs_rbcount = le32toh(es->e2fs_rbcount);
591cd3acfe7SFedor Uporov 	fs->e2fs_fbcount = le32toh(es->e2fs_fbcount);
5926e38bf94SFedor Uporov 	if (EXT2_HAS_INCOMPAT_FEATURE(fs, EXT2F_INCOMPAT_64BIT)) {
593cd3acfe7SFedor Uporov 		fs->e2fs_bcount |= (uint64_t)(le32toh(es->e4fs_bcount_hi)) << 32;
594cd3acfe7SFedor Uporov 		fs->e2fs_rbcount |= (uint64_t)(le32toh(es->e4fs_rbcount_hi)) << 32;
595cd3acfe7SFedor Uporov 		fs->e2fs_fbcount |= (uint64_t)(le32toh(es->e4fs_fbcount_hi)) << 32;
5966e38bf94SFedor Uporov 	}
597771ec59bSPedro F. Giffuni 	if (fs->e2fs_rbcount > fs->e2fs_bcount ||
598771ec59bSPedro F. Giffuni 	    fs->e2fs_fbcount > fs->e2fs_bcount) {
599ebc94b66SFedor Uporov 		SDT_PROBE1(ext2fs, , vfsops, ext2_compute_sb_data_error,
600ebc94b66SFedor Uporov 		    "invalid block count");
601771ec59bSPedro F. Giffuni 		return (EINVAL);
602771ec59bSPedro F. Giffuni 	}
603cd3acfe7SFedor Uporov 
604cd3acfe7SFedor Uporov 	fs->e2fs_ficount = le32toh(es->e2fs_ficount);
605cd3acfe7SFedor Uporov 	if (fs->e2fs_ficount > le32toh(es->e2fs_icount)) {
606cd3acfe7SFedor Uporov 		SDT_PROBE1(ext2fs, , vfsops, ext2_compute_sb_data_error,
607cd3acfe7SFedor Uporov 		    "invalid number of free inodes");
608cd3acfe7SFedor Uporov 		return (EINVAL);
609cd3acfe7SFedor Uporov 	}
610cd3acfe7SFedor Uporov 
6113dd3a395SNeel Chauhan 	if (le32toh(es->e2fs_first_dblock) != (fs->e2fs_bsize > 1024 ? 0 : 1) ||
6123dd3a395SNeel Chauhan 	    le32toh(es->e2fs_first_dblock) >= fs->e2fs_bcount) {
613ebc94b66SFedor Uporov 		SDT_PROBE1(ext2fs, , vfsops, ext2_compute_sb_data_error,
614ebc94b66SFedor Uporov 		    "first data block out of range");
6156e38bf94SFedor Uporov 		return (EINVAL);
6166e38bf94SFedor Uporov 	}
6176e38bf94SFedor Uporov 
618cd3acfe7SFedor Uporov 	fs->e2fs_gcount = howmany(fs->e2fs_bcount -
619cd3acfe7SFedor Uporov 	    le32toh(es->e2fs_first_dblock), EXT2_BLOCKS_PER_GROUP(fs));
6206e38bf94SFedor Uporov 	if (fs->e2fs_gcount > ((uint64_t)1 << 32) - EXT2_DESCS_PER_BLOCK(fs)) {
621ebc94b66SFedor Uporov 		SDT_PROBE1(ext2fs, , vfsops, ext2_compute_sb_data_error,
622ebc94b66SFedor Uporov 		    "groups count too large");
6236e38bf94SFedor Uporov 		return (EINVAL);
6246e38bf94SFedor Uporov 	}
6256e38bf94SFedor Uporov 
6266e38bf94SFedor Uporov 	/* Check for extra isize in big inodes. */
6276e38bf94SFedor Uporov 	if (EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_EXTRA_ISIZE) &&
6286e38bf94SFedor Uporov 	    EXT2_INODE_SIZE(fs) < sizeof(struct ext2fs_dinode)) {
629ebc94b66SFedor Uporov 		SDT_PROBE1(ext2fs, , vfsops, ext2_compute_sb_data_error,
630ebc94b66SFedor Uporov 		    "no space for extra inode timestamps");
6316e38bf94SFedor Uporov 		return (EINVAL);
6326e38bf94SFedor Uporov 	}
6336e38bf94SFedor Uporov 
6346e38bf94SFedor Uporov 	/* s_resuid / s_resgid ? */
6356e38bf94SFedor Uporov 
6363acd9182SFedor Uporov 	if (EXT2_HAS_INCOMPAT_FEATURE(fs, EXT2F_INCOMPAT_64BIT)) {
6376e38bf94SFedor Uporov 		e2fs_descpb = fs->e2fs_bsize / E2FS_64BIT_GD_SIZE;
6383acd9182SFedor Uporov 		e2fs_gdbcount_alloc = howmany(fs->e2fs_gcount, e2fs_descpb);
6393acd9182SFedor Uporov 	} else {
6403acd9182SFedor Uporov 		e2fs_descpb = fs->e2fs_bsize / E2FS_REV0_GD_SIZE;
6413acd9182SFedor Uporov 		e2fs_gdbcount_alloc = howmany(fs->e2fs_gcount,
6423acd9182SFedor Uporov 		    fs->e2fs_bsize / sizeof(struct ext2_gd));
6433acd9182SFedor Uporov 	}
6443acd9182SFedor Uporov 	fs->e2fs_gdbcount = howmany(fs->e2fs_gcount, e2fs_descpb);
645f9834d10SPedro F. Giffuni 	fs->e2fs_gd = malloc(e2fs_gdbcount_alloc * fs->e2fs_bsize,
6463acd9182SFedor Uporov 	    M_EXT2MNT, M_WAITOK | M_ZERO);
647f9834d10SPedro F. Giffuni 	fs->e2fs_contigdirs = malloc(fs->e2fs_gcount *
648f3ee91edSPedro F. Giffuni 	    sizeof(*fs->e2fs_contigdirs), M_EXT2MNT, M_WAITOK | M_ZERO);
649e09c00caSUlf Lilleengen 
6503acd9182SFedor Uporov 	for (i = 0; i < fs->e2fs_gdbcount; i++) {
651e09c00caSUlf Lilleengen 		error = bread(devvp,
6526e38bf94SFedor Uporov 		    fsbtodb(fs, ext2_cg_location(fs, i)),
653e09c00caSUlf Lilleengen 		    fs->e2fs_bsize, NOCRED, &bp);
654e09c00caSUlf Lilleengen 		if (error) {
6559441309aSFedor Uporov 			/*
6569441309aSFedor Uporov 			 * fs->e2fs_gd and fs->e2fs_contigdirs
6579441309aSFedor Uporov 			 * will be freed later by the caller,
6589441309aSFedor Uporov 			 * because this function could be called from
6599441309aSFedor Uporov 			 * MNT_UPDATE path.
6609441309aSFedor Uporov 			 */
661e09c00caSUlf Lilleengen 			return (error);
662e09c00caSUlf Lilleengen 		}
6633acd9182SFedor Uporov 		if (EXT2_HAS_INCOMPAT_FEATURE(fs, EXT2F_INCOMPAT_64BIT)) {
6643acd9182SFedor Uporov 			memcpy(&fs->e2fs_gd[
665e09c00caSUlf Lilleengen 			    i * fs->e2fs_bsize / sizeof(struct ext2_gd)],
6663acd9182SFedor Uporov 			    bp->b_data, fs->e2fs_bsize);
6673acd9182SFedor Uporov 		} else {
6683acd9182SFedor Uporov 			for (j = 0; j < e2fs_descpb &&
6693acd9182SFedor Uporov 			    g_count < fs->e2fs_gcount; j++, g_count++)
6703acd9182SFedor Uporov 				memcpy(&fs->e2fs_gd[g_count],
6713acd9182SFedor Uporov 				    bp->b_data + j * E2FS_REV0_GD_SIZE,
6723acd9182SFedor Uporov 				    E2FS_REV0_GD_SIZE);
6733acd9182SFedor Uporov 		}
674e09c00caSUlf Lilleengen 		brelse(bp);
675e09c00caSUlf Lilleengen 		bp = NULL;
676e09c00caSUlf Lilleengen 	}
6776e38bf94SFedor Uporov 
6786e38bf94SFedor Uporov 	/* Validate cgs consistency */
6796e38bf94SFedor Uporov 	error = ext2_cg_validate(fs);
6806e38bf94SFedor Uporov 	if (error)
6816e38bf94SFedor Uporov 		return (error);
6826e38bf94SFedor Uporov 
6836e38bf94SFedor Uporov 	/* Verfy cgs csum */
684512f29d1SFedor Uporov 	if (EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_GDT_CSUM) ||
685512f29d1SFedor Uporov 	    EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) {
686d23db91eSPedro F. Giffuni 		error = ext2_gd_csum_verify(fs, devvp->v_rdev);
687d23db91eSPedro F. Giffuni 		if (error)
688d23db91eSPedro F. Giffuni 			return (error);
689d23db91eSPedro F. Giffuni 	}
690f3ee91edSPedro F. Giffuni 	/* Initialization for the ext2 Orlov allocator variant. */
691e09c00caSUlf Lilleengen 	fs->e2fs_total_dir = 0;
692f3ee91edSPedro F. Giffuni 	for (i = 0; i < fs->e2fs_gcount; i++)
693512f29d1SFedor Uporov 		fs->e2fs_total_dir += e2fs_gd_get_ndirs(&fs->e2fs_gd[i]);
694f3ee91edSPedro F. Giffuni 
695cd3acfe7SFedor Uporov 	if (le32toh(es->e2fs_rev) == E2FS_REV0 ||
696035e4e04SPedro F. Giffuni 	    !EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_LARGEFILE))
697e09c00caSUlf Lilleengen 		fs->e2fs_maxfilesize = 0x7fffffff;
6989824e4adSPedro F. Giffuni 	else {
6999824e4adSPedro F. Giffuni 		fs->e2fs_maxfilesize = 0xffffffffffff;
7009824e4adSPedro F. Giffuni 		if (EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_HUGE_FILE))
701e09c00caSUlf Lilleengen 			fs->e2fs_maxfilesize = 0x7fffffffffffffff;
7029824e4adSPedro F. Giffuni 	}
703cd3acfe7SFedor Uporov 	if (le32toh(es->e4fs_flags) & E2FS_UNSIGNED_HASH) {
7049824e4adSPedro F. Giffuni 		fs->e2fs_uhash = 3;
705cd3acfe7SFedor Uporov 	} else if ((le32toh(es->e4fs_flags) & E2FS_SIGNED_HASH) == 0) {
7069824e4adSPedro F. Giffuni #ifdef __CHAR_UNSIGNED__
707cd3acfe7SFedor Uporov 		es->e4fs_flags = htole32(le32toh(es->e4fs_flags) | E2FS_UNSIGNED_HASH);
7089824e4adSPedro F. Giffuni 		fs->e2fs_uhash = 3;
7099824e4adSPedro F. Giffuni #else
710cd3acfe7SFedor Uporov 		es->e4fs_flags = htole32(le32toh(es->e4fs_flags) | E2FS_SIGNED_HASH);
7119824e4adSPedro F. Giffuni #endif
7129824e4adSPedro F. Giffuni 	}
713512f29d1SFedor Uporov 	if (EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
714512f29d1SFedor Uporov 		error = ext2_sb_csum_verify(fs);
7159824e4adSPedro F. Giffuni 
716512f29d1SFedor Uporov 	return (error);
717e09c00caSUlf Lilleengen }
718e09c00caSUlf Lilleengen 
719e09c00caSUlf Lilleengen /*
720e09c00caSUlf Lilleengen  * Reload all incore data for a filesystem (used after running fsck on
721e09c00caSUlf Lilleengen  * the root filesystem and finding things to fix). The filesystem must
722e09c00caSUlf Lilleengen  * be mounted read-only.
723e09c00caSUlf Lilleengen  *
724e09c00caSUlf Lilleengen  * Things to do to update the mount:
725e09c00caSUlf Lilleengen  *	1) invalidate all cached meta-data.
726e09c00caSUlf Lilleengen  *	2) re-read superblock from disk.
7275b63c125SPedro F. Giffuni  *	3) invalidate all cluster summary information.
728e09c00caSUlf Lilleengen  *	4) invalidate all inactive vnodes.
729e09c00caSUlf Lilleengen  *	5) invalidate all cached file data.
730e09c00caSUlf Lilleengen  *	6) re-read inode data for all active vnodes.
731e09c00caSUlf Lilleengen  * XXX we are missing some steps, in particular # 3, this has to be reviewed.
732e09c00caSUlf Lilleengen  */
733e09c00caSUlf Lilleengen static int
ext2_reload(struct mount * mp,struct thread * td)734e09c00caSUlf Lilleengen ext2_reload(struct mount *mp, struct thread *td)
735e09c00caSUlf Lilleengen {
736e09c00caSUlf Lilleengen 	struct vnode *vp, *mvp, *devvp;
737e09c00caSUlf Lilleengen 	struct inode *ip;
738e09c00caSUlf Lilleengen 	struct buf *bp;
739e09c00caSUlf Lilleengen 	struct ext2fs *es;
740e09c00caSUlf Lilleengen 	struct m_ext2fs *fs;
7415b63c125SPedro F. Giffuni 	struct csum *sump;
7425b63c125SPedro F. Giffuni 	int error, i;
7435b63c125SPedro F. Giffuni 	int32_t *lp;
744e09c00caSUlf Lilleengen 
745e09c00caSUlf Lilleengen 	if ((mp->mnt_flag & MNT_RDONLY) == 0)
746e09c00caSUlf Lilleengen 		return (EINVAL);
747e09c00caSUlf Lilleengen 	/*
748e09c00caSUlf Lilleengen 	 * Step 1: invalidate all cached meta-data.
749e09c00caSUlf Lilleengen 	 */
750e09c00caSUlf Lilleengen 	devvp = VFSTOEXT2(mp)->um_devvp;
751e09c00caSUlf Lilleengen 	vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
752e09c00caSUlf Lilleengen 	if (vinvalbuf(devvp, 0, 0, 0) != 0)
753e09c00caSUlf Lilleengen 		panic("ext2_reload: dirty1");
754b249ce48SMateusz Guzik 	VOP_UNLOCK(devvp);
755e09c00caSUlf Lilleengen 
756e09c00caSUlf Lilleengen 	/*
757e09c00caSUlf Lilleengen 	 * Step 2: re-read superblock from disk.
758e09c00caSUlf Lilleengen 	 * constants have been adjusted for ext2
759e09c00caSUlf Lilleengen 	 */
76050be18ccSFedor Uporov 	if ((error = bread(devvp, SBLOCK, SBLOCKBLKSIZE, NOCRED, &bp)) != 0)
761e09c00caSUlf Lilleengen 		return (error);
76250be18ccSFedor Uporov 	es = (struct ext2fs *)((char *)bp->b_data + SBLOCKOFFSET);
763e09c00caSUlf Lilleengen 	if (ext2_check_sb_compat(es, devvp->v_rdev, 0) != 0) {
764e09c00caSUlf Lilleengen 		brelse(bp);
765e09c00caSUlf Lilleengen 		return (EIO);		/* XXX needs translation */
766e09c00caSUlf Lilleengen 	}
767e09c00caSUlf Lilleengen 	fs = VFSTOEXT2(mp)->um_e2fs;
768e09c00caSUlf Lilleengen 	bcopy(bp->b_data, fs->e2fs, sizeof(struct ext2fs));
769e09c00caSUlf Lilleengen 
7706e38bf94SFedor Uporov 	if ((error = ext2_compute_sb_data(devvp, es, fs)) != 0) {
771e09c00caSUlf Lilleengen 		brelse(bp);
772e09c00caSUlf Lilleengen 		return (error);
773e09c00caSUlf Lilleengen 	}
77450be18ccSFedor Uporov 
775e09c00caSUlf Lilleengen 	brelse(bp);
776e09c00caSUlf Lilleengen 
7775b63c125SPedro F. Giffuni 	/*
7785b63c125SPedro F. Giffuni 	 * Step 3: invalidate all cluster summary information.
7795b63c125SPedro F. Giffuni 	 */
7805b63c125SPedro F. Giffuni 	if (fs->e2fs_contigsumsize > 0) {
7815b63c125SPedro F. Giffuni 		lp = fs->e2fs_maxcluster;
7825b63c125SPedro F. Giffuni 		sump = fs->e2fs_clustersum;
7835b63c125SPedro F. Giffuni 		for (i = 0; i < fs->e2fs_gcount; i++, sump++) {
7845b63c125SPedro F. Giffuni 			*lp++ = fs->e2fs_contigsumsize;
7855b63c125SPedro F. Giffuni 			sump->cs_init = 0;
7865b63c125SPedro F. Giffuni 			bzero(sump->cs_sum, fs->e2fs_contigsumsize + 1);
7875b63c125SPedro F. Giffuni 		}
7885b63c125SPedro F. Giffuni 	}
7895b63c125SPedro F. Giffuni 
790e09c00caSUlf Lilleengen loop:
79171469bb3SKirk McKusick 	MNT_VNODE_FOREACH_ALL(vp, mp, mvp) {
792e09c00caSUlf Lilleengen 		/*
793e09c00caSUlf Lilleengen 		 * Step 4: invalidate all cached file data.
794e09c00caSUlf Lilleengen 		 */
795a92a971bSMateusz Guzik 		if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK)) {
79671469bb3SKirk McKusick 			MNT_VNODE_FOREACH_ALL_ABORT(mp, mvp);
797e09c00caSUlf Lilleengen 			goto loop;
798e09c00caSUlf Lilleengen 		}
799e09c00caSUlf Lilleengen 		if (vinvalbuf(vp, 0, 0, 0))
800e09c00caSUlf Lilleengen 			panic("ext2_reload: dirty2");
801e09c00caSUlf Lilleengen 
802e09c00caSUlf Lilleengen 		/*
803e09c00caSUlf Lilleengen 		 * Step 5: re-read inode data for all active vnodes.
804e09c00caSUlf Lilleengen 		 */
805e09c00caSUlf Lilleengen 		ip = VTOI(vp);
806e09c00caSUlf Lilleengen 		error = bread(devvp, fsbtodb(fs, ino_to_fsba(fs, ip->i_number)),
807e09c00caSUlf Lilleengen 		    (int)fs->e2fs_bsize, NOCRED, &bp);
808e09c00caSUlf Lilleengen 		if (error) {
809b935e867SMateusz Guzik 			vput(vp);
81071469bb3SKirk McKusick 			MNT_VNODE_FOREACH_ALL_ABORT(mp, mvp);
811e09c00caSUlf Lilleengen 			return (error);
812e09c00caSUlf Lilleengen 		}
813daa2d62dSFedor Uporov 
814daa2d62dSFedor Uporov 		error = ext2_ei2i((struct ext2fs_dinode *)((char *)bp->b_data +
815e09c00caSUlf Lilleengen 		    EXT2_INODE_SIZE(fs) * ino_to_fsbo(fs, ip->i_number)), ip);
816daa2d62dSFedor Uporov 
817e09c00caSUlf Lilleengen 		brelse(bp);
818b935e867SMateusz Guzik 		vput(vp);
819daa2d62dSFedor Uporov 
820daa2d62dSFedor Uporov 		if (error) {
821daa2d62dSFedor Uporov 			MNT_VNODE_FOREACH_ALL_ABORT(mp, mvp);
822daa2d62dSFedor Uporov 			return (error);
823daa2d62dSFedor Uporov 		}
824e09c00caSUlf Lilleengen 	}
825e09c00caSUlf Lilleengen 	return (0);
826e09c00caSUlf Lilleengen }
827e09c00caSUlf Lilleengen 
828e09c00caSUlf Lilleengen /*
829e09c00caSUlf Lilleengen  * Common code for mount and mountroot.
830e09c00caSUlf Lilleengen  */
831e09c00caSUlf Lilleengen static int
ext2_mountfs(struct vnode * devvp,struct mount * mp)832e09c00caSUlf Lilleengen ext2_mountfs(struct vnode *devvp, struct mount *mp)
833e09c00caSUlf Lilleengen {
834e09c00caSUlf Lilleengen 	struct ext2mount *ump;
835e09c00caSUlf Lilleengen 	struct buf *bp;
836e09c00caSUlf Lilleengen 	struct m_ext2fs *fs;
837e09c00caSUlf Lilleengen 	struct ext2fs *es;
838e09c00caSUlf Lilleengen 	struct cdev *dev = devvp->v_rdev;
839e09c00caSUlf Lilleengen 	struct g_consumer *cp;
840e09c00caSUlf Lilleengen 	struct bufobj *bo;
8415b63c125SPedro F. Giffuni 	struct csum *sump;
842e09c00caSUlf Lilleengen 	int error;
843e09c00caSUlf Lilleengen 	int ronly;
844bb9535bbSPedro F. Giffuni 	int i;
845bb9535bbSPedro F. Giffuni 	u_long size;
8465b63c125SPedro F. Giffuni 	int32_t *lp;
8471a125d6dSPedro F. Giffuni 	int32_t e2fs_maxcontig;
848e09c00caSUlf Lilleengen 
84950be18ccSFedor Uporov 	bp = NULL;
85050be18ccSFedor Uporov 	ump = NULL;
85150be18ccSFedor Uporov 
852e09c00caSUlf Lilleengen 	ronly = vfs_flagopt(mp->mnt_optnew, "ro", NULL, 0);
853e09c00caSUlf Lilleengen 	/* XXX: use VOP_ACESS to check FS perms */
854e09c00caSUlf Lilleengen 	g_topology_lock();
855e09c00caSUlf Lilleengen 	error = g_vfs_open(devvp, &cp, "ext2fs", ronly ? 0 : 1);
856e09c00caSUlf Lilleengen 	g_topology_unlock();
857b249ce48SMateusz Guzik 	VOP_UNLOCK(devvp);
858e09c00caSUlf Lilleengen 	if (error)
859e09c00caSUlf Lilleengen 		return (error);
860e09c00caSUlf Lilleengen 
86150be18ccSFedor Uporov 	if (PAGE_SIZE != SBLOCKBLKSIZE) {
86250be18ccSFedor Uporov 		printf("WARNING: Unsupported page size %d\n", PAGE_SIZE);
86350be18ccSFedor Uporov 		error = EINVAL;
86450be18ccSFedor Uporov 		goto out;
86550be18ccSFedor Uporov 	}
86650be18ccSFedor Uporov 	if (cp->provider->sectorsize > PAGE_SIZE) {
86750be18ccSFedor Uporov 		printf("WARNING: Device sectorsize(%d) is more than %d\n",
86850be18ccSFedor Uporov 		    cp->provider->sectorsize, PAGE_SIZE);
86950be18ccSFedor Uporov 		error = EINVAL;
87050be18ccSFedor Uporov 		goto out;
871e09c00caSUlf Lilleengen 	}
872e09c00caSUlf Lilleengen 
873e09c00caSUlf Lilleengen 	bo = &devvp->v_bufobj;
874e09c00caSUlf Lilleengen 	bo->bo_private = cp;
875e09c00caSUlf Lilleengen 	bo->bo_ops = g_vfs_bufops;
876e09c00caSUlf Lilleengen 	if (devvp->v_rdev->si_iosize_max != 0)
877e09c00caSUlf Lilleengen 		mp->mnt_iosize_max = devvp->v_rdev->si_iosize_max;
878cd853791SKonstantin Belousov 	if (mp->mnt_iosize_max > maxphys)
879cd853791SKonstantin Belousov 		mp->mnt_iosize_max = maxphys;
88050be18ccSFedor Uporov 	if ((error = bread(devvp, SBLOCK, SBLOCKBLKSIZE, NOCRED, &bp)) != 0)
881e09c00caSUlf Lilleengen 		goto out;
88250be18ccSFedor Uporov 	es = (struct ext2fs *)((char *)bp->b_data + SBLOCKOFFSET);
883e09c00caSUlf Lilleengen 	if (ext2_check_sb_compat(es, dev, ronly) != 0) {
884e09c00caSUlf Lilleengen 		error = EINVAL;		/* XXX needs translation */
885e09c00caSUlf Lilleengen 		goto out;
886e09c00caSUlf Lilleengen 	}
887cd3acfe7SFedor Uporov 	if ((le16toh(es->e2fs_state) & E2FS_ISCLEAN) == 0 ||
888cd3acfe7SFedor Uporov 	    (le16toh(es->e2fs_state) & E2FS_ERRORS)) {
889e09c00caSUlf Lilleengen 		if (ronly || (mp->mnt_flag & MNT_FORCE)) {
890e09c00caSUlf Lilleengen 			printf(
891e09c00caSUlf Lilleengen "WARNING: Filesystem was not properly dismounted\n");
892e09c00caSUlf Lilleengen 		} else {
893e09c00caSUlf Lilleengen 			printf(
894e09c00caSUlf Lilleengen "WARNING: R/W mount denied.  Filesystem is not clean - run fsck\n");
895e09c00caSUlf Lilleengen 			error = EPERM;
896e09c00caSUlf Lilleengen 			goto out;
897e09c00caSUlf Lilleengen 		}
898e09c00caSUlf Lilleengen 	}
8995ed5554fSPedro F. Giffuni 	ump = malloc(sizeof(*ump), M_EXT2MNT, M_WAITOK | M_ZERO);
900e09c00caSUlf Lilleengen 
901e09c00caSUlf Lilleengen 	/*
902e09c00caSUlf Lilleengen 	 * I don't know whether this is the right strategy. Note that
9032b3506d9SKevin Lo 	 * we dynamically allocate both an m_ext2fs and an ext2fs
904e09c00caSUlf Lilleengen 	 * while Linux keeps the super block in a locked buffer.
905e09c00caSUlf Lilleengen 	 */
906e09c00caSUlf Lilleengen 	ump->um_e2fs = malloc(sizeof(struct m_ext2fs),
907daf884faSPedro F. Giffuni 	    M_EXT2MNT, M_WAITOK | M_ZERO);
908e09c00caSUlf Lilleengen 	ump->um_e2fs->e2fs = malloc(sizeof(struct ext2fs),
909e09c00caSUlf Lilleengen 	    M_EXT2MNT, M_WAITOK);
910e09c00caSUlf Lilleengen 	mtx_init(EXT2_MTX(ump), "EXT2FS", "EXT2FS Lock", MTX_DEF);
911e09c00caSUlf Lilleengen 	bcopy(es, ump->um_e2fs->e2fs, (u_int)sizeof(struct ext2fs));
9126e38bf94SFedor Uporov 	if ((error = ext2_compute_sb_data(devvp, ump->um_e2fs->e2fs, ump->um_e2fs)))
913e09c00caSUlf Lilleengen 		goto out;
914e09c00caSUlf Lilleengen 
9155b63c125SPedro F. Giffuni 	/*
9165b63c125SPedro F. Giffuni 	 * Calculate the maximum contiguous blocks and size of cluster summary
9175ed5554fSPedro F. Giffuni 	 * array.  In FFS this is done by newfs; however, the superblock
9185ed5554fSPedro F. Giffuni 	 * in ext2fs doesn't have these variables, so we can calculate
9195b63c125SPedro F. Giffuni 	 * them here.
9205b63c125SPedro F. Giffuni 	 */
921cd853791SKonstantin Belousov 	e2fs_maxcontig = MAX(1, maxphys / ump->um_e2fs->e2fs_bsize);
9221a125d6dSPedro F. Giffuni 	ump->um_e2fs->e2fs_contigsumsize = MIN(e2fs_maxcontig, EXT2_MAXCONTIG);
923f784da88SKonstantin Belousov 	ump->um_e2fs->e2fs_maxsymlinklen = EXT2_MAXSYMLINKLEN;
9245b63c125SPedro F. Giffuni 	if (ump->um_e2fs->e2fs_contigsumsize > 0) {
9255b63c125SPedro F. Giffuni 		size = ump->um_e2fs->e2fs_gcount * sizeof(int32_t);
9265b63c125SPedro F. Giffuni 		ump->um_e2fs->e2fs_maxcluster = malloc(size, M_EXT2MNT, M_WAITOK);
9275b63c125SPedro F. Giffuni 		size = ump->um_e2fs->e2fs_gcount * sizeof(struct csum);
9285b63c125SPedro F. Giffuni 		ump->um_e2fs->e2fs_clustersum = malloc(size, M_EXT2MNT, M_WAITOK);
9295b63c125SPedro F. Giffuni 		lp = ump->um_e2fs->e2fs_maxcluster;
9305b63c125SPedro F. Giffuni 		sump = ump->um_e2fs->e2fs_clustersum;
9315b63c125SPedro F. Giffuni 		for (i = 0; i < ump->um_e2fs->e2fs_gcount; i++, sump++) {
9325b63c125SPedro F. Giffuni 			*lp++ = ump->um_e2fs->e2fs_contigsumsize;
9335b63c125SPedro F. Giffuni 			sump->cs_init = 0;
934f9834d10SPedro F. Giffuni 			sump->cs_sum = malloc((ump->um_e2fs->e2fs_contigsumsize + 1) *
9355b63c125SPedro F. Giffuni 			    sizeof(int32_t), M_EXT2MNT, M_WAITOK | M_ZERO);
9365b63c125SPedro F. Giffuni 		}
9375b63c125SPedro F. Giffuni 	}
9385b63c125SPedro F. Giffuni 
939e09c00caSUlf Lilleengen 	brelse(bp);
940e09c00caSUlf Lilleengen 	bp = NULL;
941e09c00caSUlf Lilleengen 	fs = ump->um_e2fs;
942e09c00caSUlf Lilleengen 	fs->e2fs_ronly = ronly;	/* ronly is set according to mnt_flags */
943e09c00caSUlf Lilleengen 
944e09c00caSUlf Lilleengen 	/*
945e09c00caSUlf Lilleengen 	 * If the fs is not mounted read-only, make sure the super block is
946e09c00caSUlf Lilleengen 	 * always written back on a sync().
947e09c00caSUlf Lilleengen 	 */
948cd3acfe7SFedor Uporov 	fs->e2fs_wasvalid = le16toh(fs->e2fs->e2fs_state) & E2FS_ISCLEAN ? 1 : 0;
949e09c00caSUlf Lilleengen 	if (ronly == 0) {
950cd3acfe7SFedor Uporov 		fs->e2fs_fmod = 1;	/* mark it modified and set fs invalid */
951cd3acfe7SFedor Uporov 		fs->e2fs->e2fs_state =
952cd3acfe7SFedor Uporov 		    htole16(le16toh(fs->e2fs->e2fs_state) & ~E2FS_ISCLEAN);
953e09c00caSUlf Lilleengen 	}
954e09c00caSUlf Lilleengen 	mp->mnt_data = ump;
955e09c00caSUlf Lilleengen 	mp->mnt_stat.f_fsid.val[0] = dev2udev(dev);
956e09c00caSUlf Lilleengen 	mp->mnt_stat.f_fsid.val[1] = mp->mnt_vfc->vfc_typenum;
957e09c00caSUlf Lilleengen 	MNT_ILOCK(mp);
958e09c00caSUlf Lilleengen 	mp->mnt_flag |= MNT_LOCAL;
959e09c00caSUlf Lilleengen 	MNT_IUNLOCK(mp);
960e09c00caSUlf Lilleengen 	ump->um_mountp = mp;
961e09c00caSUlf Lilleengen 	ump->um_dev = dev;
962e09c00caSUlf Lilleengen 	ump->um_devvp = devvp;
963e09c00caSUlf Lilleengen 	ump->um_bo = &devvp->v_bufobj;
964e09c00caSUlf Lilleengen 	ump->um_cp = cp;
965e09c00caSUlf Lilleengen 
966e09c00caSUlf Lilleengen 	/*
967e09c00caSUlf Lilleengen 	 * Setting those two parameters allowed us to use
968e09c00caSUlf Lilleengen 	 * ufs_bmap w/o changse!
969e09c00caSUlf Lilleengen 	 */
970e09c00caSUlf Lilleengen 	ump->um_nindir = EXT2_ADDR_PER_BLOCK(fs);
971cd3acfe7SFedor Uporov 	ump->um_bptrtodb = le32toh(fs->e2fs->e2fs_log_bsize) + 1;
972e09c00caSUlf Lilleengen 	ump->um_seqinc = EXT2_FRAGS_PER_BLOCK(fs);
973e09c00caSUlf Lilleengen 	if (ronly == 0)
974e09c00caSUlf Lilleengen 		ext2_sbupdate(ump, MNT_WAIT);
975e09c00caSUlf Lilleengen 	/*
976e09c00caSUlf Lilleengen 	 * Initialize filesystem stat information in mount struct.
977e09c00caSUlf Lilleengen 	 */
978e09c00caSUlf Lilleengen 	MNT_ILOCK(mp);
979dda11d4aSRick Macklem 	mp->mnt_kern_flag |= MNTK_LOOKUP_SHARED | MNTK_EXTENDED_SHARED |
980dda11d4aSRick Macklem 	    MNTK_USES_BCACHE;
981e09c00caSUlf Lilleengen 	MNT_IUNLOCK(mp);
982e09c00caSUlf Lilleengen 	return (0);
983e09c00caSUlf Lilleengen out:
984e09c00caSUlf Lilleengen 	if (bp)
985e09c00caSUlf Lilleengen 		brelse(bp);
986e09c00caSUlf Lilleengen 	if (cp != NULL) {
987e09c00caSUlf Lilleengen 		g_topology_lock();
988e09c00caSUlf Lilleengen 		g_vfs_close(cp);
989e09c00caSUlf Lilleengen 		g_topology_unlock();
990e09c00caSUlf Lilleengen 	}
991e09c00caSUlf Lilleengen 	if (ump) {
992e09c00caSUlf Lilleengen 		mtx_destroy(EXT2_MTX(ump));
993e09c00caSUlf Lilleengen 		free(ump->um_e2fs->e2fs_gd, M_EXT2MNT);
994e09c00caSUlf Lilleengen 		free(ump->um_e2fs->e2fs_contigdirs, M_EXT2MNT);
995e09c00caSUlf Lilleengen 		free(ump->um_e2fs->e2fs, M_EXT2MNT);
996e09c00caSUlf Lilleengen 		free(ump->um_e2fs, M_EXT2MNT);
997e09c00caSUlf Lilleengen 		free(ump, M_EXT2MNT);
998e09c00caSUlf Lilleengen 		mp->mnt_data = NULL;
999e09c00caSUlf Lilleengen 	}
1000e09c00caSUlf Lilleengen 	return (error);
1001e09c00caSUlf Lilleengen }
1002e09c00caSUlf Lilleengen 
1003e09c00caSUlf Lilleengen /*
1004e09c00caSUlf Lilleengen  * Unmount system call.
1005e09c00caSUlf Lilleengen  */
1006e09c00caSUlf Lilleengen static int
ext2_unmount(struct mount * mp,int mntflags)1007e09c00caSUlf Lilleengen ext2_unmount(struct mount *mp, int mntflags)
1008e09c00caSUlf Lilleengen {
1009e09c00caSUlf Lilleengen 	struct ext2mount *ump;
1010e09c00caSUlf Lilleengen 	struct m_ext2fs *fs;
10115b63c125SPedro F. Giffuni 	struct csum *sump;
10125b63c125SPedro F. Giffuni 	int error, flags, i, ronly;
1013e09c00caSUlf Lilleengen 
1014e09c00caSUlf Lilleengen 	flags = 0;
1015e09c00caSUlf Lilleengen 	if (mntflags & MNT_FORCE) {
1016e09c00caSUlf Lilleengen 		if (mp->mnt_flag & MNT_ROOTFS)
1017e09c00caSUlf Lilleengen 			return (EINVAL);
1018e09c00caSUlf Lilleengen 		flags |= FORCECLOSE;
1019e09c00caSUlf Lilleengen 	}
1020e09c00caSUlf Lilleengen 	if ((error = ext2_flushfiles(mp, flags, curthread)) != 0)
1021e09c00caSUlf Lilleengen 		return (error);
1022e09c00caSUlf Lilleengen 	ump = VFSTOEXT2(mp);
1023e09c00caSUlf Lilleengen 	fs = ump->um_e2fs;
1024e09c00caSUlf Lilleengen 	ronly = fs->e2fs_ronly;
1025e09c00caSUlf Lilleengen 	if (ronly == 0 && ext2_cgupdate(ump, MNT_WAIT) == 0) {
1026e09c00caSUlf Lilleengen 		if (fs->e2fs_wasvalid)
1027cd3acfe7SFedor Uporov 			fs->e2fs->e2fs_state =
1028cd3acfe7SFedor Uporov 			    htole16(le16toh(fs->e2fs->e2fs_state) | E2FS_ISCLEAN);
1029e09c00caSUlf Lilleengen 		ext2_sbupdate(ump, MNT_WAIT);
1030e09c00caSUlf Lilleengen 	}
1031e09c00caSUlf Lilleengen 
1032e09c00caSUlf Lilleengen 	g_topology_lock();
1033e09c00caSUlf Lilleengen 	g_vfs_close(ump->um_cp);
1034e09c00caSUlf Lilleengen 	g_topology_unlock();
1035e09c00caSUlf Lilleengen 	vrele(ump->um_devvp);
10365b63c125SPedro F. Giffuni 	sump = fs->e2fs_clustersum;
10375b63c125SPedro F. Giffuni 	for (i = 0; i < fs->e2fs_gcount; i++, sump++)
10385b63c125SPedro F. Giffuni 		free(sump->cs_sum, M_EXT2MNT);
10395b63c125SPedro F. Giffuni 	free(fs->e2fs_clustersum, M_EXT2MNT);
10405b63c125SPedro F. Giffuni 	free(fs->e2fs_maxcluster, M_EXT2MNT);
1041e09c00caSUlf Lilleengen 	free(fs->e2fs_gd, M_EXT2MNT);
1042e09c00caSUlf Lilleengen 	free(fs->e2fs_contigdirs, M_EXT2MNT);
1043e09c00caSUlf Lilleengen 	free(fs->e2fs, M_EXT2MNT);
1044e09c00caSUlf Lilleengen 	free(fs, M_EXT2MNT);
1045e09c00caSUlf Lilleengen 	free(ump, M_EXT2MNT);
1046e09c00caSUlf Lilleengen 	mp->mnt_data = NULL;
1047e09c00caSUlf Lilleengen 	return (error);
1048e09c00caSUlf Lilleengen }
1049e09c00caSUlf Lilleengen 
1050e09c00caSUlf Lilleengen /*
1051e09c00caSUlf Lilleengen  * Flush out all the files in a filesystem.
1052e09c00caSUlf Lilleengen  */
1053e09c00caSUlf Lilleengen static int
ext2_flushfiles(struct mount * mp,int flags,struct thread * td)1054e09c00caSUlf Lilleengen ext2_flushfiles(struct mount *mp, int flags, struct thread *td)
1055e09c00caSUlf Lilleengen {
1056e09c00caSUlf Lilleengen 	int error;
1057e09c00caSUlf Lilleengen 
1058e09c00caSUlf Lilleengen 	error = vflush(mp, 0, flags, td);
1059e09c00caSUlf Lilleengen 	return (error);
1060e09c00caSUlf Lilleengen }
1061bf9a211dSPedro F. Giffuni 
1062e09c00caSUlf Lilleengen /*
1063e09c00caSUlf Lilleengen  * Get filesystem statistics.
1064e09c00caSUlf Lilleengen  */
1065e09c00caSUlf Lilleengen int
ext2_statfs(struct mount * mp,struct statfs * sbp)1066e09c00caSUlf Lilleengen ext2_statfs(struct mount *mp, struct statfs *sbp)
1067e09c00caSUlf Lilleengen {
1068e09c00caSUlf Lilleengen 	struct ext2mount *ump;
1069e09c00caSUlf Lilleengen 	struct m_ext2fs *fs;
1070e09c00caSUlf Lilleengen 	uint32_t overhead, overhead_per_group, ngdb;
1071e09c00caSUlf Lilleengen 	int i, ngroups;
1072e09c00caSUlf Lilleengen 
1073e09c00caSUlf Lilleengen 	ump = VFSTOEXT2(mp);
1074e09c00caSUlf Lilleengen 	fs = ump->um_e2fs;
1075cd3acfe7SFedor Uporov 	if (le16toh(fs->e2fs->e2fs_magic) != E2FS_MAGIC)
1076757224cbSPedro F. Giffuni 		panic("ext2_statfs");
1077e09c00caSUlf Lilleengen 
1078e09c00caSUlf Lilleengen 	/*
1079e09c00caSUlf Lilleengen 	 * Compute the overhead (FS structures)
1080e09c00caSUlf Lilleengen 	 */
1081e09c00caSUlf Lilleengen 	overhead_per_group =
1082e09c00caSUlf Lilleengen 	    1 /* block bitmap */ +
1083e09c00caSUlf Lilleengen 	    1 /* inode bitmap */ +
1084e09c00caSUlf Lilleengen 	    fs->e2fs_itpg;
1085cd3acfe7SFedor Uporov 	overhead = le32toh(fs->e2fs->e2fs_first_dblock) +
1086e09c00caSUlf Lilleengen 	    fs->e2fs_gcount * overhead_per_group;
1087cd3acfe7SFedor Uporov 	if (le32toh(fs->e2fs->e2fs_rev) > E2FS_REV0 &&
1088cd3acfe7SFedor Uporov 	    le32toh(fs->e2fs->e2fs_features_rocompat) & EXT2F_ROCOMPAT_SPARSESUPER) {
1089e09c00caSUlf Lilleengen 		for (i = 0, ngroups = 0; i < fs->e2fs_gcount; i++) {
1090d23db91eSPedro F. Giffuni 			if (ext2_cg_has_sb(fs, i))
1091e09c00caSUlf Lilleengen 				ngroups++;
1092e09c00caSUlf Lilleengen 		}
1093e09c00caSUlf Lilleengen 	} else {
1094e09c00caSUlf Lilleengen 		ngroups = fs->e2fs_gcount;
1095e09c00caSUlf Lilleengen 	}
1096e09c00caSUlf Lilleengen 	ngdb = fs->e2fs_gdbcount;
1097cd3acfe7SFedor Uporov 	if (le32toh(fs->e2fs->e2fs_rev) > E2FS_REV0 &&
1098cd3acfe7SFedor Uporov 	    le32toh(fs->e2fs->e2fs_features_compat) & EXT2F_COMPAT_RESIZE)
1099cd3acfe7SFedor Uporov 		ngdb += le16toh(fs->e2fs->e2fs_reserved_ngdb);
1100e09c00caSUlf Lilleengen 	overhead += ngroups * (1 /* superblock */ + ngdb);
1101e09c00caSUlf Lilleengen 
1102e09c00caSUlf Lilleengen 	sbp->f_bsize = EXT2_FRAG_SIZE(fs);
1103e09c00caSUlf Lilleengen 	sbp->f_iosize = EXT2_BLOCK_SIZE(fs);
11043acd9182SFedor Uporov 	sbp->f_blocks = fs->e2fs_bcount - overhead;
11053acd9182SFedor Uporov 	sbp->f_bfree = fs->e2fs_fbcount;
11063acd9182SFedor Uporov 	sbp->f_bavail = sbp->f_bfree - fs->e2fs_rbcount;
1107cd3acfe7SFedor Uporov 	sbp->f_files = le32toh(fs->e2fs->e2fs_icount);
1108cd3acfe7SFedor Uporov 	sbp->f_ffree = fs->e2fs_ficount;
1109e09c00caSUlf Lilleengen 	return (0);
1110e09c00caSUlf Lilleengen }
1111e09c00caSUlf Lilleengen 
1112e09c00caSUlf Lilleengen /*
1113e09c00caSUlf Lilleengen  * Go through the disk queues to initiate sandbagged IO;
1114e09c00caSUlf Lilleengen  * go through the inodes to write those that have been modified;
1115e09c00caSUlf Lilleengen  * initiate the writing of the super block if it has been modified.
1116e09c00caSUlf Lilleengen  *
1117e09c00caSUlf Lilleengen  * Note: we are always called with the filesystem marked `MPBUSY'.
1118e09c00caSUlf Lilleengen  */
1119e09c00caSUlf Lilleengen static int
ext2_sync(struct mount * mp,int waitfor)1120e09c00caSUlf Lilleengen ext2_sync(struct mount *mp, int waitfor)
1121e09c00caSUlf Lilleengen {
1122e09c00caSUlf Lilleengen 	struct vnode *mvp, *vp;
1123e09c00caSUlf Lilleengen 	struct thread *td;
1124e09c00caSUlf Lilleengen 	struct inode *ip;
1125e09c00caSUlf Lilleengen 	struct ext2mount *ump = VFSTOEXT2(mp);
1126e09c00caSUlf Lilleengen 	struct m_ext2fs *fs;
1127e09c00caSUlf Lilleengen 	int error, allerror = 0;
1128e09c00caSUlf Lilleengen 
1129e09c00caSUlf Lilleengen 	td = curthread;
1130e09c00caSUlf Lilleengen 	fs = ump->um_e2fs;
1131e09c00caSUlf Lilleengen 	if (fs->e2fs_fmod != 0 && fs->e2fs_ronly != 0) {		/* XXX */
1132ebc94b66SFedor Uporov 		panic("ext2_sync: rofs mod fs=%s", fs->e2fs_fsmnt);
1133e09c00caSUlf Lilleengen 	}
1134e09c00caSUlf Lilleengen 
1135e09c00caSUlf Lilleengen 	/*
1136e09c00caSUlf Lilleengen 	 * Write back each (modified) inode.
1137e09c00caSUlf Lilleengen 	 */
1138e09c00caSUlf Lilleengen loop:
113971469bb3SKirk McKusick 	MNT_VNODE_FOREACH_ALL(vp, mp, mvp) {
114071469bb3SKirk McKusick 		if (vp->v_type == VNON) {
1141e09c00caSUlf Lilleengen 			VI_UNLOCK(vp);
1142e09c00caSUlf Lilleengen 			continue;
1143e09c00caSUlf Lilleengen 		}
1144e09c00caSUlf Lilleengen 		ip = VTOI(vp);
1145e09c00caSUlf Lilleengen 		if ((ip->i_flag &
1146e09c00caSUlf Lilleengen 		    (IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE)) == 0 &&
1147e09c00caSUlf Lilleengen 		    (vp->v_bufobj.bo_dirty.bv_cnt == 0 ||
1148e09c00caSUlf Lilleengen 		    waitfor == MNT_LAZY)) {
1149e09c00caSUlf Lilleengen 			VI_UNLOCK(vp);
1150e09c00caSUlf Lilleengen 			continue;
1151e09c00caSUlf Lilleengen 		}
1152a92a971bSMateusz Guzik 		error = vget(vp, LK_EXCLUSIVE | LK_NOWAIT | LK_INTERLOCK);
1153e09c00caSUlf Lilleengen 		if (error) {
1154e09c00caSUlf Lilleengen 			if (error == ENOENT) {
115571469bb3SKirk McKusick 				MNT_VNODE_FOREACH_ALL_ABORT(mp, mvp);
1156e09c00caSUlf Lilleengen 				goto loop;
1157e09c00caSUlf Lilleengen 			}
1158e09c00caSUlf Lilleengen 			continue;
1159e09c00caSUlf Lilleengen 		}
1160e09c00caSUlf Lilleengen 		if ((error = VOP_FSYNC(vp, waitfor, td)) != 0)
1161e09c00caSUlf Lilleengen 			allerror = error;
1162b935e867SMateusz Guzik 		vput(vp);
1163e09c00caSUlf Lilleengen 	}
1164e09c00caSUlf Lilleengen 
1165e09c00caSUlf Lilleengen 	/*
1166e09c00caSUlf Lilleengen 	 * Force stale filesystem control information to be flushed.
1167e09c00caSUlf Lilleengen 	 */
1168e09c00caSUlf Lilleengen 	if (waitfor != MNT_LAZY) {
1169e09c00caSUlf Lilleengen 		vn_lock(ump->um_devvp, LK_EXCLUSIVE | LK_RETRY);
1170e09c00caSUlf Lilleengen 		if ((error = VOP_FSYNC(ump->um_devvp, waitfor, td)) != 0)
1171e09c00caSUlf Lilleengen 			allerror = error;
1172b249ce48SMateusz Guzik 		VOP_UNLOCK(ump->um_devvp);
1173e09c00caSUlf Lilleengen 	}
1174e09c00caSUlf Lilleengen 
1175e09c00caSUlf Lilleengen 	/*
1176e09c00caSUlf Lilleengen 	 * Write back modified superblock.
1177e09c00caSUlf Lilleengen 	 */
1178e09c00caSUlf Lilleengen 	if (fs->e2fs_fmod != 0) {
1179e09c00caSUlf Lilleengen 		fs->e2fs_fmod = 0;
1180cd3acfe7SFedor Uporov 		fs->e2fs->e2fs_wtime = htole32(time_second);
1181e09c00caSUlf Lilleengen 		if ((error = ext2_cgupdate(ump, waitfor)) != 0)
1182e09c00caSUlf Lilleengen 			allerror = error;
1183e09c00caSUlf Lilleengen 	}
1184e09c00caSUlf Lilleengen 	return (allerror);
1185e09c00caSUlf Lilleengen }
1186e09c00caSUlf Lilleengen 
1187e09c00caSUlf Lilleengen /*
1188e09c00caSUlf Lilleengen  * Look up an EXT2FS dinode number to find its incore vnode, otherwise read it
1189e09c00caSUlf Lilleengen  * in from disk.  If it is in core, wait for the lock bit to clear, then
1190e09c00caSUlf Lilleengen  * return the inode locked.  Detection and handling of mount points must be
1191e09c00caSUlf Lilleengen  * done by the calling routine.
1192e09c00caSUlf Lilleengen  */
1193e09c00caSUlf Lilleengen static int
ext2_vget(struct mount * mp,ino_t ino,int flags,struct vnode ** vpp)1194e09c00caSUlf Lilleengen ext2_vget(struct mount *mp, ino_t ino, int flags, struct vnode **vpp)
1195e09c00caSUlf Lilleengen {
1196e09c00caSUlf Lilleengen 	struct m_ext2fs *fs;
1197e09c00caSUlf Lilleengen 	struct inode *ip;
1198e09c00caSUlf Lilleengen 	struct ext2mount *ump;
1199e09c00caSUlf Lilleengen 	struct buf *bp;
1200e09c00caSUlf Lilleengen 	struct vnode *vp;
1201e09c00caSUlf Lilleengen 	struct thread *td;
1202736da517SFedor Uporov 	unsigned int i, used_blocks;
1203736da517SFedor Uporov 	int error;
1204e09c00caSUlf Lilleengen 
1205e09c00caSUlf Lilleengen 	td = curthread;
1206e09c00caSUlf Lilleengen 	error = vfs_hash_get(mp, ino, flags, td, vpp, NULL, NULL);
1207e09c00caSUlf Lilleengen 	if (error || *vpp != NULL)
1208e09c00caSUlf Lilleengen 		return (error);
1209e09c00caSUlf Lilleengen 
1210e09c00caSUlf Lilleengen 	ump = VFSTOEXT2(mp);
1211e09c00caSUlf Lilleengen 	ip = malloc(sizeof(struct inode), M_EXT2NODE, M_WAITOK | M_ZERO);
1212e09c00caSUlf Lilleengen 
1213e09c00caSUlf Lilleengen 	/* Allocate a new vnode/inode. */
1214e09c00caSUlf Lilleengen 	if ((error = getnewvnode("ext2fs", mp, &ext2_vnodeops, &vp)) != 0) {
1215e09c00caSUlf Lilleengen 		*vpp = NULL;
1216e09c00caSUlf Lilleengen 		free(ip, M_EXT2NODE);
1217e09c00caSUlf Lilleengen 		return (error);
1218e09c00caSUlf Lilleengen 	}
1219e09c00caSUlf Lilleengen 	vp->v_data = ip;
1220e09c00caSUlf Lilleengen 	ip->i_vnode = vp;
1221e09c00caSUlf Lilleengen 	ip->i_e2fs = fs = ump->um_e2fs;
1222e09c00caSUlf Lilleengen 	ip->i_ump = ump;
1223e09c00caSUlf Lilleengen 	ip->i_number = ino;
12242bfd8992SKonstantin Belousov 	cluster_init_vn(&ip->i_clusterw);
1225e09c00caSUlf Lilleengen 
1226e09c00caSUlf Lilleengen 	lockmgr(vp->v_vnlock, LK_EXCLUSIVE, NULL);
1227e09c00caSUlf Lilleengen 	error = insmntque(vp, mp);
1228e09c00caSUlf Lilleengen 	if (error != 0) {
1229e09c00caSUlf Lilleengen 		free(ip, M_EXT2NODE);
1230e09c00caSUlf Lilleengen 		*vpp = NULL;
1231e09c00caSUlf Lilleengen 		return (error);
1232e09c00caSUlf Lilleengen 	}
1233e09c00caSUlf Lilleengen 	error = vfs_hash_insert(vp, ino, flags, td, vpp, NULL, NULL);
1234e09c00caSUlf Lilleengen 	if (error || *vpp != NULL)
1235e09c00caSUlf Lilleengen 		return (error);
1236e09c00caSUlf Lilleengen 
1237e09c00caSUlf Lilleengen 	/* Read in the disk contents for the inode, copy into the inode. */
1238e09c00caSUlf Lilleengen 	if ((error = bread(ump->um_devvp, fsbtodb(fs, ino_to_fsba(fs, ino)),
1239e09c00caSUlf Lilleengen 	    (int)fs->e2fs_bsize, NOCRED, &bp)) != 0) {
1240e09c00caSUlf Lilleengen 		/*
1241e09c00caSUlf Lilleengen 		 * The inode does not contain anything useful, so it would
1242e09c00caSUlf Lilleengen 		 * be misleading to leave it on its hash chain. With mode
1243e09c00caSUlf Lilleengen 		 * still zero, it will be unlinked and returned to the free
1244e09c00caSUlf Lilleengen 		 * list by vput().
1245e09c00caSUlf Lilleengen 		 */
1246e09c00caSUlf Lilleengen 		brelse(bp);
1247e09c00caSUlf Lilleengen 		vput(vp);
1248e09c00caSUlf Lilleengen 		*vpp = NULL;
1249e09c00caSUlf Lilleengen 		return (error);
1250e09c00caSUlf Lilleengen 	}
1251e09c00caSUlf Lilleengen 	/* convert ext2 inode to dinode */
1252512f29d1SFedor Uporov 	error = ext2_ei2i((struct ext2fs_dinode *)((char *)bp->b_data +
1253512f29d1SFedor Uporov 	    EXT2_INODE_SIZE(fs) * ino_to_fsbo(fs, ino)), ip);
1254512f29d1SFedor Uporov 	if (error) {
1255512f29d1SFedor Uporov 		brelse(bp);
1256512f29d1SFedor Uporov 		vput(vp);
1257512f29d1SFedor Uporov 		*vpp = NULL;
1258512f29d1SFedor Uporov 		return (error);
1259512f29d1SFedor Uporov 	}
1260e09c00caSUlf Lilleengen 	ip->i_block_group = ino_to_cg(fs, ino);
1261e09c00caSUlf Lilleengen 	ip->i_next_alloc_block = 0;
1262e09c00caSUlf Lilleengen 	ip->i_next_alloc_goal = 0;
1263e09c00caSUlf Lilleengen 
1264e09c00caSUlf Lilleengen 	/*
1265e09c00caSUlf Lilleengen 	 * Now we want to make sure that block pointers for unused
1266e09c00caSUlf Lilleengen 	 * blocks are zeroed out - ext2_balloc depends on this
1267e09c00caSUlf Lilleengen 	 * although for regular files and directories only
1268d7511a40SPedro F. Giffuni 	 *
1269ad3d96a7SPedro F. Giffuni 	 * If IN_E4EXTENTS is enabled, unused blocks are not zeroed
1270a7710d51SPedro F. Giffuni 	 * out because we could corrupt the extent tree.
1271e09c00caSUlf Lilleengen 	 */
1272ad3d96a7SPedro F. Giffuni 	if (!(ip->i_flag & IN_E4EXTENTS) &&
1273d7511a40SPedro F. Giffuni 	    (S_ISDIR(ip->i_mode) || S_ISREG(ip->i_mode))) {
1274ee7ae58aSPedro F. Giffuni 		used_blocks = howmany(ip->i_size, fs->e2fs_bsize);
1275e09c00caSUlf Lilleengen 		for (i = used_blocks; i < EXT2_NDIR_BLOCKS; i++)
1276e09c00caSUlf Lilleengen 			ip->i_db[i] = 0;
1277e09c00caSUlf Lilleengen 	}
12785679656eSFedor Uporov 
12795679656eSFedor Uporov 	bqrelse(bp);
12805679656eSFedor Uporov 
1281ebc94b66SFedor Uporov #ifdef EXT2FS_PRINT_EXTENTS
1282e09c00caSUlf Lilleengen 	ext2_print_inode(ip);
12835679656eSFedor Uporov 	error = ext4_ext_walk(ip);
12845679656eSFedor Uporov 	if (error) {
12855679656eSFedor Uporov 		vput(vp);
12865679656eSFedor Uporov 		*vpp = NULL;
12875679656eSFedor Uporov 		return (error);
12885679656eSFedor Uporov 	}
128933587684SPedro F. Giffuni #endif
1290e09c00caSUlf Lilleengen 
1291e09c00caSUlf Lilleengen 	/*
1292e09c00caSUlf Lilleengen 	 * Initialize the vnode from the inode, check for aliases.
1293e09c00caSUlf Lilleengen 	 * Note that the underlying vnode may have changed.
1294e09c00caSUlf Lilleengen 	 */
1295e09c00caSUlf Lilleengen 	if ((error = ext2_vinit(mp, &ext2_fifoops, &vp)) != 0) {
1296e09c00caSUlf Lilleengen 		vput(vp);
1297e09c00caSUlf Lilleengen 		*vpp = NULL;
1298e09c00caSUlf Lilleengen 		return (error);
1299e09c00caSUlf Lilleengen 	}
1300e09c00caSUlf Lilleengen 
1301e09c00caSUlf Lilleengen 	/*
1302007c6207SJohn Baldwin 	 * Finish inode initialization.
1303e09c00caSUlf Lilleengen 	 */
1304e09c00caSUlf Lilleengen 
1305829f0bcbSMateusz Guzik 	vn_set_state(vp, VSTATE_CONSTRUCTED);
1306e09c00caSUlf Lilleengen 	*vpp = vp;
1307e09c00caSUlf Lilleengen 	return (0);
1308e09c00caSUlf Lilleengen }
1309e09c00caSUlf Lilleengen 
1310e09c00caSUlf Lilleengen /*
1311e09c00caSUlf Lilleengen  * File handle to vnode
1312e09c00caSUlf Lilleengen  *
1313e09c00caSUlf Lilleengen  * Have to be really careful about stale file handles:
1314e09c00caSUlf Lilleengen  * - check that the inode number is valid
1315e09c00caSUlf Lilleengen  * - call ext2_vget() to get the locked inode
1316e09c00caSUlf Lilleengen  * - check for an unallocated inode (i_mode == 0)
1317e09c00caSUlf Lilleengen  * - check that the given client host has export rights and return
1318e09c00caSUlf Lilleengen  *   those rights via. exflagsp and credanonp
1319e09c00caSUlf Lilleengen  */
1320e09c00caSUlf Lilleengen static int
ext2_fhtovp(struct mount * mp,struct fid * fhp,int flags,struct vnode ** vpp)1321694a586aSRick Macklem ext2_fhtovp(struct mount *mp, struct fid *fhp, int flags, struct vnode **vpp)
1322e09c00caSUlf Lilleengen {
1323e09c00caSUlf Lilleengen 	struct inode *ip;
1324e09c00caSUlf Lilleengen 	struct ufid *ufhp;
1325e09c00caSUlf Lilleengen 	struct vnode *nvp;
1326e09c00caSUlf Lilleengen 	struct m_ext2fs *fs;
1327e09c00caSUlf Lilleengen 	int error;
1328e09c00caSUlf Lilleengen 
1329e09c00caSUlf Lilleengen 	ufhp = (struct ufid *)fhp;
1330e09c00caSUlf Lilleengen 	fs = VFSTOEXT2(mp)->um_e2fs;
1331bbfe24fbSJohn Baldwin 	if (ufhp->ufid_ino < EXT2_ROOTINO ||
1332cd3acfe7SFedor Uporov 	    ufhp->ufid_ino > fs->e2fs_gcount * fs->e2fs_ipg)
1333e09c00caSUlf Lilleengen 		return (ESTALE);
1334e09c00caSUlf Lilleengen 
1335e09c00caSUlf Lilleengen 	error = VFS_VGET(mp, ufhp->ufid_ino, LK_EXCLUSIVE, &nvp);
1336e09c00caSUlf Lilleengen 	if (error) {
1337e09c00caSUlf Lilleengen 		*vpp = NULLVP;
1338e09c00caSUlf Lilleengen 		return (error);
1339e09c00caSUlf Lilleengen 	}
1340e09c00caSUlf Lilleengen 	ip = VTOI(nvp);
1341e09c00caSUlf Lilleengen 	if (ip->i_mode == 0 ||
1342e09c00caSUlf Lilleengen 	    ip->i_gen != ufhp->ufid_gen || ip->i_nlink <= 0) {
1343e09c00caSUlf Lilleengen 		vput(nvp);
1344e09c00caSUlf Lilleengen 		*vpp = NULLVP;
1345e09c00caSUlf Lilleengen 		return (ESTALE);
1346e09c00caSUlf Lilleengen 	}
1347e09c00caSUlf Lilleengen 	*vpp = nvp;
1348*56a8aca8SPawel Jakub Dawidek 	vnode_create_vobject(*vpp, ip->i_size, curthread);
1349e09c00caSUlf Lilleengen 	return (0);
1350e09c00caSUlf Lilleengen }
1351e09c00caSUlf Lilleengen 
1352e09c00caSUlf Lilleengen /*
1353e09c00caSUlf Lilleengen  * Write a superblock and associated information back to disk.
1354e09c00caSUlf Lilleengen  */
1355e09c00caSUlf Lilleengen static int
ext2_sbupdate(struct ext2mount * mp,int waitfor)1356e09c00caSUlf Lilleengen ext2_sbupdate(struct ext2mount *mp, int waitfor)
1357e09c00caSUlf Lilleengen {
1358e09c00caSUlf Lilleengen 	struct m_ext2fs *fs = mp->um_e2fs;
1359e09c00caSUlf Lilleengen 	struct ext2fs *es = fs->e2fs;
1360e09c00caSUlf Lilleengen 	struct buf *bp;
1361e09c00caSUlf Lilleengen 	int error = 0;
1362e09c00caSUlf Lilleengen 
1363cd3acfe7SFedor Uporov 	es->e2fs_bcount = htole32(fs->e2fs_bcount & 0xffffffff);
1364cd3acfe7SFedor Uporov 	es->e2fs_rbcount = htole32(fs->e2fs_rbcount & 0xffffffff);
1365cd3acfe7SFedor Uporov 	es->e2fs_fbcount = htole32(fs->e2fs_fbcount & 0xffffffff);
13663acd9182SFedor Uporov 	if (EXT2_HAS_INCOMPAT_FEATURE(fs, EXT2F_INCOMPAT_64BIT)) {
1367cd3acfe7SFedor Uporov 		es->e4fs_bcount_hi = htole32(fs->e2fs_bcount >> 32);
1368cd3acfe7SFedor Uporov 		es->e4fs_rbcount_hi = htole32(fs->e2fs_rbcount >> 32);
1369cd3acfe7SFedor Uporov 		es->e4fs_fbcount_hi = htole32(fs->e2fs_fbcount >> 32);
13703acd9182SFedor Uporov 	}
13713acd9182SFedor Uporov 
1372cd3acfe7SFedor Uporov 	es->e2fs_ficount = htole32(fs->e2fs_ficount);
1373cd3acfe7SFedor Uporov 
1374512f29d1SFedor Uporov 	if (EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
1375512f29d1SFedor Uporov 		ext2_sb_csum_set(fs);
1376512f29d1SFedor Uporov 
137750be18ccSFedor Uporov 	error = bread(mp->um_devvp, SBLOCK, SBLOCKBLKSIZE, NOCRED, &bp);
137850be18ccSFedor Uporov 	if (error != 0)
137950be18ccSFedor Uporov 		return (error);
138050be18ccSFedor Uporov 
138150be18ccSFedor Uporov 	memcpy((char *)bp->b_data + SBLOCKOFFSET, (caddr_t)es,
138250be18ccSFedor Uporov 	    (u_int)sizeof(struct ext2fs));
1383e09c00caSUlf Lilleengen 	if (waitfor == MNT_WAIT)
1384e09c00caSUlf Lilleengen 		error = bwrite(bp);
1385e09c00caSUlf Lilleengen 	else
1386e09c00caSUlf Lilleengen 		bawrite(bp);
1387e09c00caSUlf Lilleengen 
1388e09c00caSUlf Lilleengen 	/*
1389e09c00caSUlf Lilleengen 	 * The buffers for group descriptors, inode bitmaps and block bitmaps
1390e09c00caSUlf Lilleengen 	 * are not busy at this point and are (hopefully) written by the
1391e09c00caSUlf Lilleengen 	 * usual sync mechanism. No need to write them here.
1392e09c00caSUlf Lilleengen 	 */
1393e09c00caSUlf Lilleengen 	return (error);
1394e09c00caSUlf Lilleengen }
1395e09c00caSUlf Lilleengen int
ext2_cgupdate(struct ext2mount * mp,int waitfor)1396e09c00caSUlf Lilleengen ext2_cgupdate(struct ext2mount *mp, int waitfor)
1397e09c00caSUlf Lilleengen {
1398e09c00caSUlf Lilleengen 	struct m_ext2fs *fs = mp->um_e2fs;
1399e09c00caSUlf Lilleengen 	struct buf *bp;
14003acd9182SFedor Uporov 	int i, j, g_count = 0, error = 0, allerror = 0;
1401e09c00caSUlf Lilleengen 
1402e09c00caSUlf Lilleengen 	allerror = ext2_sbupdate(mp, waitfor);
1403d23db91eSPedro F. Giffuni 
1404d23db91eSPedro F. Giffuni 	/* Update gd csums */
1405512f29d1SFedor Uporov 	if (EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_GDT_CSUM) ||
1406512f29d1SFedor Uporov 	    EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
1407d23db91eSPedro F. Giffuni 		ext2_gd_csum_set(fs);
1408d23db91eSPedro F. Giffuni 
1409e09c00caSUlf Lilleengen 	for (i = 0; i < fs->e2fs_gdbcount; i++) {
1410e09c00caSUlf Lilleengen 		bp = getblk(mp->um_devvp, fsbtodb(fs,
14116e38bf94SFedor Uporov 		    ext2_cg_location(fs, i)),
1412c0f16c65SFedor Uporov 		    fs->e2fs_bsize, 0, 0, 0);
14133acd9182SFedor Uporov 		if (EXT2_HAS_INCOMPAT_FEATURE(fs, EXT2F_INCOMPAT_64BIT)) {
14143acd9182SFedor Uporov 			memcpy(bp->b_data, &fs->e2fs_gd[
1415e09c00caSUlf Lilleengen 			    i * fs->e2fs_bsize / sizeof(struct ext2_gd)],
14163acd9182SFedor Uporov 			    fs->e2fs_bsize);
14173acd9182SFedor Uporov 		} else {
14183acd9182SFedor Uporov 			for (j = 0; j < fs->e2fs_bsize / E2FS_REV0_GD_SIZE &&
14193acd9182SFedor Uporov 			    g_count < fs->e2fs_gcount; j++, g_count++)
14203acd9182SFedor Uporov 				memcpy(bp->b_data + j * E2FS_REV0_GD_SIZE,
14213acd9182SFedor Uporov 				    &fs->e2fs_gd[g_count], E2FS_REV0_GD_SIZE);
14223acd9182SFedor Uporov 		}
1423e09c00caSUlf Lilleengen 		if (waitfor == MNT_WAIT)
1424e09c00caSUlf Lilleengen 			error = bwrite(bp);
1425e09c00caSUlf Lilleengen 		else
1426e09c00caSUlf Lilleengen 			bawrite(bp);
1427e09c00caSUlf Lilleengen 	}
1428e09c00caSUlf Lilleengen 
1429e09c00caSUlf Lilleengen 	if (!allerror && error)
1430e09c00caSUlf Lilleengen 		allerror = error;
1431e09c00caSUlf Lilleengen 	return (allerror);
1432e09c00caSUlf Lilleengen }
1433bf9a211dSPedro F. Giffuni 
1434e09c00caSUlf Lilleengen /*
1435e09c00caSUlf Lilleengen  * Return the root of a filesystem.
1436e09c00caSUlf Lilleengen  */
1437e09c00caSUlf Lilleengen static int
ext2_root(struct mount * mp,int flags,struct vnode ** vpp)1438e09c00caSUlf Lilleengen ext2_root(struct mount *mp, int flags, struct vnode **vpp)
1439e09c00caSUlf Lilleengen {
1440e09c00caSUlf Lilleengen 	struct vnode *nvp;
1441e09c00caSUlf Lilleengen 	int error;
1442e09c00caSUlf Lilleengen 
1443bbfe24fbSJohn Baldwin 	error = VFS_VGET(mp, EXT2_ROOTINO, LK_EXCLUSIVE, &nvp);
1444e09c00caSUlf Lilleengen 	if (error)
1445e09c00caSUlf Lilleengen 		return (error);
1446e09c00caSUlf Lilleengen 	*vpp = nvp;
1447e09c00caSUlf Lilleengen 	return (0);
1448e09c00caSUlf Lilleengen }
1449