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