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/*- 8187396Sstas * 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$ 3712115Sdyson */ 3812115Sdyson 3912115Sdyson#include <sys/param.h> 4012115Sdyson#include <sys/systm.h> 4112115Sdyson#include <sys/namei.h> 42164033Srwatson#include <sys/priv.h> 4312115Sdyson#include <sys/proc.h> 4412115Sdyson#include <sys/kernel.h> 4512115Sdyson#include <sys/vnode.h> 4612115Sdyson#include <sys/mount.h> 4760041Sphk#include <sys/bio.h> 4812115Sdyson#include <sys/buf.h> 4929906Skato#include <sys/conf.h> 50232703Spfg#include <sys/endian.h> 5124131Sbde#include <sys/fcntl.h> 5212115Sdyson#include <sys/malloc.h> 5312115Sdyson#include <sys/stat.h> 5471576Sjasone#include <sys/mutex.h> 5512115Sdyson 56137039Sphk#include <geom/geom.h> 57137039Sphk#include <geom/geom_vfs.h> 58137039Sphk 59202283Slulf#include <fs/ext2fs/ext2_mount.h> 60202283Slulf#include <fs/ext2fs/inode.h> 6112115Sdyson 62202283Slulf#include <fs/ext2fs/fs.h> 63218176Sjhb#include <fs/ext2fs/ext2fs.h> 64218176Sjhb#include <fs/ext2fs/ext2_dinode.h> 65202283Slulf#include <fs/ext2fs/ext2_extern.h> 6612115Sdyson 67193382Sstasstatic int ext2_flushfiles(struct mount *mp, int flags, struct thread *td); 68193382Sstasstatic int ext2_mountfs(struct vnode *, struct mount *); 69193382Sstasstatic int ext2_reload(struct mount *mp, struct thread *td); 70193382Sstasstatic int ext2_sbupdate(struct ext2mount *, int); 71202283Slulfstatic int ext2_cgupdate(struct ext2mount *, int); 72116271Sphkstatic vfs_unmount_t ext2_unmount; 73116271Sphkstatic vfs_root_t ext2_root; 74116271Sphkstatic vfs_statfs_t ext2_statfs; 75116271Sphkstatic vfs_sync_t ext2_sync; 76116271Sphkstatic vfs_vget_t ext2_vget; 77116271Sphkstatic vfs_fhtovp_t ext2_fhtovp; 78132902Sphkstatic vfs_mount_t ext2_mount; 79116271Sphk 80151897SrwatsonMALLOC_DEFINE(M_EXT2NODE, "ext2_node", "EXT2 vnode private part"); 81151897Srwatsonstatic MALLOC_DEFINE(M_EXT2MNT, "ext2_mount", "EXT2 mount structure"); 8230280Sphk 8312911Sphkstatic struct vfsops ext2fs_vfsops = { 84116271Sphk .vfs_fhtovp = ext2_fhtovp, 85132902Sphk .vfs_mount = ext2_mount, 86116271Sphk .vfs_root = ext2_root, /* root inode via vget */ 87116271Sphk .vfs_statfs = ext2_statfs, 88116271Sphk .vfs_sync = ext2_sync, 89116271Sphk .vfs_unmount = ext2_unmount, 90116271Sphk .vfs_vget = ext2_vget, 9112115Sdyson}; 9212115Sdyson 9338909SbdeVFS_SET(ext2fs_vfsops, ext2fs, 0); 94137039Sphk 95202283Slulfstatic int ext2_check_sb_compat(struct ext2fs *es, struct cdev *dev, 9693014Sbde int ronly); 9792728Salfredstatic int compute_sb_data(struct vnode * devvp, 98202283Slulf struct ext2fs * es, struct m_ext2fs * fs); 9916322Sgpalmer 100217702Sjhbstatic const char *ext2_opts[] = { "acls", "async", "noatime", "noclusterr", 101217702Sjhb "noclusterw", "noexec", "export", "force", "from", "multilabel", 102217702Sjhb "suiddir", "nosymfollow", "sync", "union", NULL }; 103187396Sstas 10412115Sdyson/* 10512115Sdyson * VFS Operations. 10612115Sdyson * 10712115Sdyson * mount system call 10812115Sdyson */ 10912911Sphkstatic int 110193382Sstasext2_mount(struct mount *mp) 11112115Sdyson{ 11297255Smux struct vfsoptlist *opts; 11312115Sdyson struct vnode *devvp; 114191990Sattilio struct thread *td; 115238697Skevlo struct ext2mount *ump = NULL; 116202283Slulf struct m_ext2fs *fs; 117193382Sstas struct nameidata nd, *ndp = &nd; 118193382Sstas accmode_t accmode; 11997255Smux char *path, *fspec; 12097255Smux int error, flags, len; 12112115Sdyson 122191990Sattilio td = curthread; 12397255Smux opts = mp->mnt_optnew; 12497255Smux 125138493Sphk if (vfs_filteropt(opts, ext2_opts)) 126138493Sphk return (EINVAL); 127138493Sphk 12897255Smux vfs_getopt(opts, "fspath", (void **)&path, NULL); 12973286Sadrian /* Double-check the length of path.. */ 130246258Spfg if (strlen(path) >= MAXMNTLEN) 13173286Sadrian return (ENAMETOOLONG); 13297255Smux 13397255Smux fspec = NULL; 13497255Smux error = vfs_getopt(opts, "from", (void **)&fspec, &len); 13597255Smux if (!error && fspec[len - 1] != '\0') 13697255Smux return (EINVAL); 13797255Smux 13812115Sdyson /* 13912115Sdyson * If updating, check whether changing from read-only to 14012115Sdyson * read/write; if there is no device name, that's all we do. 14112115Sdyson */ 14212115Sdyson if (mp->mnt_flag & MNT_UPDATE) { 14396749Siedowse ump = VFSTOEXT2(mp); 144202283Slulf fs = ump->um_e2fs; 14512115Sdyson error = 0; 146202283Slulf if (fs->e2fs_ronly == 0 && 147138493Sphk vfs_flagopt(opts, "ro", NULL, 0)) { 148191990Sattilio error = VFS_SYNC(mp, MNT_WAIT); 149137039Sphk if (error) 150137039Sphk return (error); 15112115Sdyson flags = WRITECLOSE; 15212115Sdyson if (mp->mnt_flag & MNT_FORCE) 15312115Sdyson flags |= FORCECLOSE; 15483366Sjulian error = ext2_flushfiles(mp, flags, td); 155202283Slulf if ( error == 0 && fs->e2fs_wasvalid && ext2_cgupdate(ump, MNT_WAIT) == 0) { 156202283Slulf fs->e2fs->e2fs_state |= E2FS_ISCLEAN; 15739670Sbde ext2_sbupdate(ump, MNT_WAIT); 15839670Sbde } 159202283Slulf fs->e2fs_ronly = 1; 160138493Sphk vfs_flagopt(opts, "ro", &mp->mnt_flag, MNT_RDONLY); 161137039Sphk DROP_GIANT(); 162137039Sphk g_topology_lock(); 163137039Sphk g_access(ump->um_cp, 0, -1, 0); 164137039Sphk g_topology_unlock(); 165137039Sphk PICKUP_GIANT(); 16612115Sdyson } 16712115Sdyson if (!error && (mp->mnt_flag & MNT_RELOAD)) 168140736Sphk error = ext2_reload(mp, td); 16912115Sdyson if (error) 17012115Sdyson return (error); 17157839Sbde devvp = ump->um_devvp; 172202283Slulf if (fs->e2fs_ronly && !vfs_flagopt(opts, "ro", NULL, 0)) { 173202283Slulf if (ext2_check_sb_compat(fs->e2fs, devvp->v_rdev, 0)) 174138493Sphk return (EPERM); 175193382Sstas 17639028Sbde /* 17739028Sbde * If upgrade to read-write by non-root, then verify 17839028Sbde * that user has necessary permissions on the device. 17939028Sbde */ 180175202Sattilio vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY); 181164033Srwatson error = VOP_ACCESS(devvp, VREAD | VWRITE, 182164033Srwatson td->td_ucred, td); 183164033Srwatson if (error) 184164033Srwatson error = priv_check(td, PRIV_VFS_MOUNT_PERM); 185164033Srwatson if (error) { 186175294Sattilio VOP_UNLOCK(devvp, 0); 187164033Srwatson return (error); 18839028Sbde } 189175294Sattilio VOP_UNLOCK(devvp, 0); 190137039Sphk DROP_GIANT(); 191137039Sphk g_topology_lock(); 192137039Sphk error = g_access(ump->um_cp, 0, 1, 0); 193137039Sphk g_topology_unlock(); 194137039Sphk PICKUP_GIANT(); 195137039Sphk if (error) 196137039Sphk return (error); 19739028Sbde 198202283Slulf if ((fs->e2fs->e2fs_state & E2FS_ISCLEAN) == 0 || 199202283Slulf (fs->e2fs->e2fs_state & E2FS_ERRORS)) { 20039670Sbde if (mp->mnt_flag & MNT_FORCE) { 201193628Sstas printf( 202202283Slulf"WARNING: %s was not properly dismounted\n", fs->e2fs_fsmnt); 20339670Sbde } else { 204193628Sstas printf( 205193628Sstas"WARNING: R/W mount of %s denied. Filesystem is not clean - run fsck\n", 206202283Slulf fs->e2fs_fsmnt); 20739670Sbde return (EPERM); 20839670Sbde } 20939670Sbde } 210202283Slulf fs->e2fs->e2fs_state &= ~E2FS_ISCLEAN; 211202283Slulf (void)ext2_cgupdate(ump, MNT_WAIT); 212202283Slulf fs->e2fs_ronly = 0; 213162647Stegge MNT_ILOCK(mp); 214138493Sphk mp->mnt_flag &= ~MNT_RDONLY; 215162647Stegge MNT_IUNLOCK(mp); 21612115Sdyson } 217158924Srodrigc if (vfs_flagopt(opts, "export", NULL, 0)) { 218158924Srodrigc /* Process export requests in vfs_mount.c. */ 219158924Srodrigc return (error); 22012115Sdyson } 22112115Sdyson } 222193382Sstas 22312115Sdyson /* 22412115Sdyson * Not an update, or updating the name: look up the name 225125786Sbde * and verify that it refers to a sensible disk device. 22612115Sdyson */ 22797255Smux if (fspec == NULL) 22897255Smux return (EINVAL); 229149720Sssouhlal NDINIT(ndp, LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE, fspec, td); 23043301Sdillon if ((error = namei(ndp)) != 0) 23112115Sdyson return (error); 23254655Seivind NDFREE(ndp, NDF_ONLY_PNBUF); 23312115Sdyson devvp = ndp->ni_vp; 23412115Sdyson 23555756Sphk if (!vn_isdisk(devvp, &error)) { 236149720Sssouhlal vput(devvp); 23755756Sphk return (error); 23812115Sdyson } 23939028Sbde 24039028Sbde /* 24139028Sbde * If mount by non-root, then verify that user has necessary 24239028Sbde * permissions on the device. 243164033Srwatson * 244164033Srwatson * XXXRW: VOP_ACCESS() enough? 24539028Sbde */ 246184413Strasz accmode = VREAD; 247164033Srwatson if ((mp->mnt_flag & MNT_RDONLY) == 0) 248184413Strasz accmode |= VWRITE; 249184413Strasz error = VOP_ACCESS(devvp, accmode, td->td_ucred, td); 250164033Srwatson if (error) 251164033Srwatson error = priv_check(td, PRIV_VFS_MOUNT_PERM); 252164033Srwatson if (error) { 253164033Srwatson vput(devvp); 254164033Srwatson return (error); 25539028Sbde } 25639028Sbde 25729888Skato if ((mp->mnt_flag & MNT_UPDATE) == 0) { 258183754Sattilio error = ext2_mountfs(devvp, mp); 25929888Skato } else { 260149771Sssouhlal if (devvp != ump->um_devvp) { 261149720Sssouhlal vput(devvp); 262149771Sssouhlal return (EINVAL); /* needs translation */ 263149771Sssouhlal } else 264149771Sssouhlal vput(devvp); 26512115Sdyson } 26612115Sdyson if (error) { 26712115Sdyson vrele(devvp); 26812115Sdyson return (error); 26912115Sdyson } 27096749Siedowse ump = VFSTOEXT2(mp); 27112115Sdyson fs = ump->um_e2fs; 272193382Sstas 27373286Sadrian /* 27473286Sadrian * Note that this strncpy() is ok because of a check at the start 27573286Sadrian * of ext2_mount(). 27673286Sadrian */ 277202283Slulf strncpy(fs->e2fs_fsmnt, path, MAXMNTLEN); 278202283Slulf fs->e2fs_fsmnt[MAXMNTLEN - 1] = '\0'; 279138493Sphk vfs_mountedfrom(mp, fspec); 28012115Sdyson return (0); 28112115Sdyson} 28212115Sdyson 283193382Sstasstatic int 284202283Slulfext2_check_sb_compat(struct ext2fs *es, struct cdev *dev, int ronly) 28512115Sdyson{ 28612115Sdyson 287202283Slulf if (es->e2fs_magic != E2FS_MAGIC) { 28855313Sbde printf("ext2fs: %s: wrong magic number %#x (expected %#x)\n", 289202283Slulf devtoname(dev), es->e2fs_magic, E2FS_MAGIC); 29055313Sbde return (1); 29155313Sbde } 292202283Slulf if (es->e2fs_rev > E2FS_REV0) { 293202283Slulf if (es->e2fs_features_incompat & ~EXT2F_INCOMPAT_SUPP) { 294193628Sstas printf( 295193628Sstas"WARNING: mount of %s denied due to unsupported optional features\n", 296193628Sstas devtoname(dev)); 29755313Sbde return (1); 29855313Sbde } 29955313Sbde if (!ronly && 300202283Slulf (es->e2fs_features_rocompat & ~EXT2F_ROCOMPAT_SUPP)) { 301193382Sstas printf("WARNING: R/W mount of %s denied due to " 302193382Sstas "unsupported optional features\n", devtoname(dev)); 30355313Sbde return (1); 30455313Sbde } 30555313Sbde } 30655313Sbde return (0); 30755313Sbde} 30855313Sbde 30912115Sdyson/* 310193382Sstas * This computes the fields of the ext2_sb_info structure from the 311193382Sstas * data in the ext2_super_block structure read in. 31212115Sdyson */ 313193382Sstasstatic int 314202283Slulfcompute_sb_data(struct vnode *devvp, struct ext2fs *es, 315202283Slulf struct m_ext2fs *fs) 31612115Sdyson{ 317193382Sstas int db_count, error; 318202283Slulf int i; 319193382Sstas int logic_sb_block = 1; /* XXX for now */ 320202283Slulf struct buf *bp; 321246258Spfg uint32_t e2fs_descpb; 32212115Sdyson 323202283Slulf fs->e2fs_bshift = EXT2_MIN_BLOCK_LOG_SIZE + es->e2fs_log_bsize; 324246564Spfg fs->e2fs_bsize = 1U << fs->e2fs_bshift; 325202283Slulf fs->e2fs_fsbtodb = es->e2fs_log_bsize + 1; 326202283Slulf fs->e2fs_qbmask = fs->e2fs_bsize - 1; 327202283Slulf fs->e2fs_fsize = EXT2_MIN_FRAG_SIZE << es->e2fs_log_fsize; 328202283Slulf if (fs->e2fs_fsize) 329202283Slulf fs->e2fs_fpb = fs->e2fs_bsize / fs->e2fs_fsize; 330202283Slulf fs->e2fs_bpg = es->e2fs_bpg; 331202283Slulf fs->e2fs_fpg = es->e2fs_fpg; 332202283Slulf fs->e2fs_ipg = es->e2fs_ipg; 333202283Slulf if (es->e2fs_rev == E2FS_REV0) { 334202283Slulf fs->e2fs_isize = E2FS_REV0_INODE_SIZE ; 335193382Sstas } else { 336202283Slulf fs->e2fs_isize = es->e2fs_inode_size; 33712115Sdyson 338193382Sstas /* 339193382Sstas * Simple sanity check for superblock inode size value. 340193382Sstas */ 341232703Spfg if (EXT2_INODE_SIZE(fs) < E2FS_REV0_INODE_SIZE || 342232703Spfg EXT2_INODE_SIZE(fs) > fs->e2fs_bsize || 343202283Slulf (fs->e2fs_isize & (fs->e2fs_isize - 1)) != 0) { 344232703Spfg printf("ext2fs: invalid inode size %d\n", 345202283Slulf fs->e2fs_isize); 346193382Sstas return (EIO); 347193382Sstas } 348193377Sstas } 349232703Spfg /* Check for extra isize in big inodes. */ 350244475Spfg if (EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_EXTRA_ISIZE) && 351232703Spfg EXT2_INODE_SIZE(fs) < sizeof(struct ext2fs_dinode)) { 352232703Spfg printf("ext2fs: no space for extra inode timestamps\n"); 353232703Spfg return (EINVAL); 354232703Spfg } 355232703Spfg 356202283Slulf fs->e2fs_ipb = fs->e2fs_bsize / EXT2_INODE_SIZE(fs); 357202283Slulf fs->e2fs_itpg = fs->e2fs_ipg /fs->e2fs_ipb; 358193382Sstas /* s_resuid / s_resgid ? */ 359202283Slulf fs->e2fs_gcount = (es->e2fs_bcount - es->e2fs_first_dblock + 360193382Sstas EXT2_BLOCKS_PER_GROUP(fs) - 1) / EXT2_BLOCKS_PER_GROUP(fs); 361246258Spfg e2fs_descpb = fs->e2fs_bsize / sizeof(struct ext2_gd); 362246258Spfg db_count = (fs->e2fs_gcount + e2fs_descpb - 1) / e2fs_descpb; 363202283Slulf fs->e2fs_gdbcount = db_count; 364202283Slulf fs->e2fs_gd = malloc(db_count * fs->e2fs_bsize, 365193382Sstas M_EXT2MNT, M_WAITOK); 366232703Spfg fs->e2fs_contigdirs = malloc(fs->e2fs_gcount * 367232703Spfg sizeof(*fs->e2fs_contigdirs), M_EXT2MNT, M_WAITOK); 36812115Sdyson 369193382Sstas /* 370193382Sstas * Adjust logic_sb_block. 371193382Sstas * Godmar thinks: if the blocksize is greater than 1024, then 372193382Sstas * the superblock is logically part of block zero. 37312115Sdyson */ 374202283Slulf if(fs->e2fs_bsize > SBSIZE) 375193382Sstas logic_sb_block = 0; 376193382Sstas for (i = 0; i < db_count; i++) { 377202283Slulf error = bread(devvp , 378202283Slulf fsbtodb(fs, logic_sb_block + i + 1 ), 379202283Slulf fs->e2fs_bsize, NOCRED, &bp); 380202283Slulf if (error) { 381202283Slulf free(fs->e2fs_gd, M_EXT2MNT); 382202283Slulf brelse(bp); 383202283Slulf return (error); 384193382Sstas } 385202283Slulf e2fs_cgload((struct ext2_gd *)bp->b_data, 386202283Slulf &fs->e2fs_gd[ 387202283Slulf i * fs->e2fs_bsize / sizeof(struct ext2_gd)], 388202283Slulf fs->e2fs_bsize); 389202283Slulf brelse(bp); 390202283Slulf bp = NULL; 39112115Sdyson } 392202283Slulf fs->e2fs_total_dir = 0; 393202283Slulf for (i=0; i < fs->e2fs_gcount; i++){ 394202283Slulf fs->e2fs_total_dir += fs->e2fs_gd[i].ext2bgd_ndirs; 395202283Slulf fs->e2fs_contigdirs[i] = 0; 396193382Sstas } 397202283Slulf if (es->e2fs_rev == E2FS_REV0 || 398232703Spfg !EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_LARGEFILE)) 399202283Slulf fs->e2fs_maxfilesize = 0x7fffffff; 400254260Spfg else { 401254260Spfg fs->e2fs_maxfilesize = 0xffffffffffff; 402254260Spfg if (EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_HUGE_FILE)) 403254260Spfg fs->e2fs_maxfilesize = 0x7fffffffffffffff; 404254260Spfg } 405252890Spfg if (es->e4fs_flags & E2FS_UNSIGNED_HASH) { 406252890Spfg fs->e2fs_uhash = 3; 407252890Spfg } else if ((es->e4fs_flags & E2FS_SIGNED_HASH) == 0) { 408252890Spfg#ifdef __CHAR_UNSIGNED__ 409252890Spfg es->e4fs_flags |= E2FS_UNSIGNED_HASH; 410252890Spfg fs->e2fs_uhash = 3; 411252890Spfg#else 412252890Spfg es->e4fs_flags |= E2FS_SIGNED_HASH; 413252890Spfg#endif 414252890Spfg } 415252890Spfg 416193382Sstas return (0); 41712115Sdyson} 41812115Sdyson 41912115Sdyson/* 42012115Sdyson * Reload all incore data for a filesystem (used after running fsck on 42112115Sdyson * the root filesystem and finding things to fix). The filesystem must 42212115Sdyson * be mounted read-only. 42312115Sdyson * 42412115Sdyson * Things to do to update the mount: 42512115Sdyson * 1) invalidate all cached meta-data. 42612115Sdyson * 2) re-read superblock from disk. 427228539Spfg * 3) invalidate all cluster summary information. 42812115Sdyson * 4) invalidate all inactive vnodes. 42912115Sdyson * 5) invalidate all cached file data. 43012115Sdyson * 6) re-read inode data for all active vnodes. 431202283Slulf * XXX we are missing some steps, in particular # 3, this has to be reviewed. 43212115Sdyson */ 43312911Sphkstatic int 434140736Sphkext2_reload(struct mount *mp, struct thread *td) 43512115Sdyson{ 436154152Stegge struct vnode *vp, *mvp, *devvp; 43712115Sdyson struct inode *ip; 43812115Sdyson struct buf *bp; 439202283Slulf struct ext2fs *es; 440202283Slulf struct m_ext2fs *fs; 441228539Spfg struct csum *sump; 442228539Spfg int error, i; 443228539Spfg int32_t *lp; 44412115Sdyson 445122114Sbde if ((mp->mnt_flag & MNT_RDONLY) == 0) 44612115Sdyson return (EINVAL); 44712115Sdyson /* 44812115Sdyson * Step 1: invalidate all cached meta-data. 44912115Sdyson */ 450122114Sbde devvp = VFSTOEXT2(mp)->um_devvp; 451175202Sattilio vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY); 452183754Sattilio if (vinvalbuf(devvp, 0, 0, 0) != 0) 45312115Sdyson panic("ext2_reload: dirty1"); 454175294Sattilio VOP_UNLOCK(devvp, 0); 455125786Sbde 45612115Sdyson /* 45712115Sdyson * Step 2: re-read superblock from disk. 45812115Sdyson * constants have been adjusted for ext2 45912115Sdyson */ 46043301Sdillon if ((error = bread(devvp, SBLOCK, SBSIZE, NOCRED, &bp)) != 0) 46112115Sdyson return (error); 462202283Slulf es = (struct ext2fs *)bp->b_data; 46355313Sbde if (ext2_check_sb_compat(es, devvp->v_rdev, 0) != 0) { 46412115Sdyson brelse(bp); 46512115Sdyson return (EIO); /* XXX needs translation */ 46612115Sdyson } 467122114Sbde fs = VFSTOEXT2(mp)->um_e2fs; 468202283Slulf bcopy(bp->b_data, fs->e2fs, sizeof(struct ext2fs)); 46912115Sdyson 47043301Sdillon if((error = compute_sb_data(devvp, es, fs)) != 0) { 47112115Sdyson brelse(bp); 472193382Sstas return (error); 47312115Sdyson } 47412115Sdyson#ifdef UNKLAR 47512115Sdyson if (fs->fs_sbsize < SBSIZE) 47612115Sdyson bp->b_flags |= B_INVAL; 47712115Sdyson#endif 47812115Sdyson brelse(bp); 47912115Sdyson 480228539Spfg /* 481228539Spfg * Step 3: invalidate all cluster summary information. 482228539Spfg */ 483228539Spfg if (fs->e2fs_contigsumsize > 0) { 484228539Spfg lp = fs->e2fs_maxcluster; 485228539Spfg sump = fs->e2fs_clustersum; 486228539Spfg for (i = 0; i < fs->e2fs_gcount; i++, sump++) { 487228539Spfg *lp++ = fs->e2fs_contigsumsize; 488228539Spfg sump->cs_init = 0; 489228539Spfg bzero(sump->cs_sum, fs->e2fs_contigsumsize + 1); 490228539Spfg } 491228539Spfg } 492228539Spfg 49312115Sdysonloop: 494234386Smckusick MNT_VNODE_FOREACH_ALL(vp, mp, mvp) { 49512115Sdyson /* 496143509Sjeff * Step 4: invalidate all cached file data. 49712115Sdyson */ 49883366Sjulian if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK, td)) { 499234386Smckusick MNT_VNODE_FOREACH_ALL_ABORT(mp, mvp); 50012115Sdyson goto loop; 50139678Sbde } 502183754Sattilio if (vinvalbuf(vp, 0, 0, 0)) 50312115Sdyson panic("ext2_reload: dirty2"); 504193382Sstas 50512115Sdyson /* 506143509Sjeff * Step 5: re-read inode data for all active vnodes. 50712115Sdyson */ 50812115Sdyson ip = VTOI(vp); 509193382Sstas error = bread(devvp, fsbtodb(fs, ino_to_fsba(fs, ip->i_number)), 510202283Slulf (int)fs->e2fs_bsize, NOCRED, &bp); 51139678Sbde if (error) { 512175294Sattilio VOP_UNLOCK(vp, 0); 513121925Skan vrele(vp); 514234386Smckusick MNT_VNODE_FOREACH_ALL_ABORT(mp, mvp); 51512115Sdyson return (error); 51612115Sdyson } 517202283Slulf ext2_ei2i((struct ext2fs_dinode *) ((char *)bp->b_data + 518187395Sstas EXT2_INODE_SIZE(fs) * ino_to_fsbo(fs, ip->i_number)), ip); 51912115Sdyson brelse(bp); 520175294Sattilio VOP_UNLOCK(vp, 0); 521121925Skan vrele(vp); 52212115Sdyson } 52312115Sdyson return (0); 52412115Sdyson} 52512115Sdyson 52612115Sdyson/* 527193382Sstas * Common code for mount and mountroot. 52812115Sdyson */ 52912911Sphkstatic int 530193382Sstasext2_mountfs(struct vnode *devvp, struct mount *mp) 53112115Sdyson{ 53296752Siedowse struct ext2mount *ump; 53312115Sdyson struct buf *bp; 534202283Slulf struct m_ext2fs *fs; 535202283Slulf struct ext2fs *es; 536130585Sphk struct cdev *dev = devvp->v_rdev; 537137039Sphk struct g_consumer *cp; 538137039Sphk struct bufobj *bo; 539228539Spfg struct csum *sump; 54096749Siedowse int error; 54112115Sdyson int ronly; 542228539Spfg int i, size; 543228539Spfg int32_t *lp; 544246563Spfg int32_t e2fs_maxcontig; 54512115Sdyson 546138493Sphk ronly = vfs_flagopt(mp->mnt_optnew, "ro", NULL, 0); 547137039Sphk /* XXX: use VOP_ACESS to check FS perms */ 548137039Sphk DROP_GIANT(); 549137039Sphk g_topology_lock(); 550137039Sphk error = g_vfs_open(devvp, &cp, "ext2fs", ronly ? 0 : 1); 551137039Sphk g_topology_unlock(); 552137039Sphk PICKUP_GIANT(); 553175294Sattilio VOP_UNLOCK(devvp, 0); 55453059Sphk if (error) 55512115Sdyson return (error); 556149960Srodrigc 557149960Srodrigc /* XXX: should we check for some sectorsize or 512 instead? */ 558149960Srodrigc if (((SBSIZE % cp->provider->sectorsize) != 0) || 559149960Srodrigc (SBSIZE < cp->provider->sectorsize)) { 560149960Srodrigc DROP_GIANT(); 561149960Srodrigc g_topology_lock(); 562183754Sattilio g_vfs_close(cp); 563149960Srodrigc g_topology_unlock(); 564149960Srodrigc PICKUP_GIANT(); 565149960Srodrigc return (EINVAL); 566149960Srodrigc } 567149960Srodrigc 568137039Sphk bo = &devvp->v_bufobj; 569137039Sphk bo->bo_private = cp; 570137039Sphk bo->bo_ops = g_vfs_bufops; 57193430Sbde if (devvp->v_rdev->si_iosize_max != 0) 57293430Sbde mp->mnt_iosize_max = devvp->v_rdev->si_iosize_max; 57393430Sbde if (mp->mnt_iosize_max > MAXPHYS) 57493430Sbde mp->mnt_iosize_max = MAXPHYS; 57593430Sbde 57612115Sdyson bp = NULL; 57712115Sdyson ump = NULL; 57843301Sdillon if ((error = bread(devvp, SBLOCK, SBSIZE, NOCRED, &bp)) != 0) 57912115Sdyson goto out; 580202283Slulf es = (struct ext2fs *)bp->b_data; 58155313Sbde if (ext2_check_sb_compat(es, dev, ronly) != 0) { 58212115Sdyson error = EINVAL; /* XXX needs translation */ 58312115Sdyson goto out; 58412115Sdyson } 585202283Slulf if ((es->e2fs_state & E2FS_ISCLEAN) == 0 || 586202283Slulf (es->e2fs_state & E2FS_ERRORS)) { 58739670Sbde if (ronly || (mp->mnt_flag & MNT_FORCE)) { 588193628Sstas printf( 589193628Sstas"WARNING: Filesystem was not properly dismounted\n"); 59039670Sbde } else { 591193628Sstas printf( 592193628Sstas"WARNING: R/W mount denied. Filesystem is not clean - run fsck\n"); 59339670Sbde error = EPERM; 59439670Sbde goto out; 59539670Sbde } 59639670Sbde } 597228583Spfg ump = malloc(sizeof(*ump), M_EXT2MNT, M_WAITOK | M_ZERO); 598193382Sstas 599193382Sstas /* 600193382Sstas * I don't know whether this is the right strategy. Note that 601193382Sstas * we dynamically allocate both an ext2_sb_info and an ext2_super_block 602193382Sstas * while Linux keeps the super block in a locked buffer. 60312115Sdyson */ 604202283Slulf ump->um_e2fs = malloc(sizeof(struct m_ext2fs), 605111119Simp M_EXT2MNT, M_WAITOK); 606202283Slulf ump->um_e2fs->e2fs = malloc(sizeof(struct ext2fs), 607111119Simp M_EXT2MNT, M_WAITOK); 608202283Slulf mtx_init(EXT2_MTX(ump), "EXT2FS", "EXT2FS Lock", MTX_DEF); 609202283Slulf bcopy(es, ump->um_e2fs->e2fs, (u_int)sizeof(struct ext2fs)); 610202283Slulf if ((error = compute_sb_data(devvp, ump->um_e2fs->e2fs, ump->um_e2fs))) 61139671Sbde goto out; 612193382Sstas 613228539Spfg /* 614228539Spfg * Calculate the maximum contiguous blocks and size of cluster summary 615228583Spfg * array. In FFS this is done by newfs; however, the superblock 616228583Spfg * in ext2fs doesn't have these variables, so we can calculate 617228539Spfg * them here. 618228539Spfg */ 619246563Spfg e2fs_maxcontig = MAX(1, MAXPHYS / ump->um_e2fs->e2fs_bsize); 620246563Spfg ump->um_e2fs->e2fs_contigsumsize = MIN(e2fs_maxcontig, EXT2_MAXCONTIG); 621228539Spfg if (ump->um_e2fs->e2fs_contigsumsize > 0) { 622228539Spfg size = ump->um_e2fs->e2fs_gcount * sizeof(int32_t); 623228539Spfg ump->um_e2fs->e2fs_maxcluster = malloc(size, M_EXT2MNT, M_WAITOK); 624228539Spfg size = ump->um_e2fs->e2fs_gcount * sizeof(struct csum); 625228539Spfg ump->um_e2fs->e2fs_clustersum = malloc(size, M_EXT2MNT, M_WAITOK); 626228539Spfg lp = ump->um_e2fs->e2fs_maxcluster; 627228539Spfg sump = ump->um_e2fs->e2fs_clustersum; 628228539Spfg for (i = 0; i < ump->um_e2fs->e2fs_gcount; i++, sump++) { 629228539Spfg *lp++ = ump->um_e2fs->e2fs_contigsumsize; 630228539Spfg sump->cs_init = 0; 631228539Spfg sump->cs_sum = malloc((ump->um_e2fs->e2fs_contigsumsize + 1) * 632228539Spfg sizeof(int32_t), M_EXT2MNT, M_WAITOK | M_ZERO); 633228539Spfg } 634228539Spfg } 635228539Spfg 63612115Sdyson brelse(bp); 63712115Sdyson bp = NULL; 63812115Sdyson fs = ump->um_e2fs; 639202283Slulf fs->e2fs_ronly = ronly; /* ronly is set according to mnt_flags */ 640193382Sstas 641193382Sstas /* 642193382Sstas * If the fs is not mounted read-only, make sure the super block is 643193382Sstas * always written back on a sync(). 64412115Sdyson */ 645202283Slulf fs->e2fs_wasvalid = fs->e2fs->e2fs_state & E2FS_ISCLEAN ? 1 : 0; 64612115Sdyson if (ronly == 0) { 647202283Slulf fs->e2fs_fmod = 1; /* mark it modified */ 648202283Slulf fs->e2fs->e2fs_state &= ~E2FS_ISCLEAN; /* set fs invalid */ 64912115Sdyson } 650172697Salfred mp->mnt_data = ump; 65150256Sbde mp->mnt_stat.f_fsid.val[0] = dev2udev(dev); 65238909Sbde mp->mnt_stat.f_fsid.val[1] = mp->mnt_vfc->vfc_typenum; 65312115Sdyson mp->mnt_maxsymlinklen = EXT2_MAXSYMLINKLEN; 654162647Stegge MNT_ILOCK(mp); 65512115Sdyson mp->mnt_flag |= MNT_LOCAL; 656162647Stegge MNT_IUNLOCK(mp); 65712115Sdyson ump->um_mountp = mp; 65812115Sdyson ump->um_dev = dev; 65912115Sdyson ump->um_devvp = devvp; 660137320Sphk ump->um_bo = &devvp->v_bufobj; 661137320Sphk ump->um_cp = cp; 662193382Sstas 663193382Sstas /* 664193382Sstas * Setting those two parameters allowed us to use 665193382Sstas * ufs_bmap w/o changse! 666193382Sstas */ 66712115Sdyson ump->um_nindir = EXT2_ADDR_PER_BLOCK(fs); 668202283Slulf ump->um_bptrtodb = fs->e2fs->e2fs_log_bsize + 1; 66912115Sdyson ump->um_seqinc = EXT2_FRAGS_PER_BLOCK(fs); 670187396Sstas if (ronly == 0) 67134430Seivind ext2_sbupdate(ump, MNT_WAIT); 672202283Slulf /* 673202283Slulf * Initialize filesystem stat information in mount struct. 674202283Slulf */ 675202283Slulf MNT_ILOCK(mp); 676242833Sattilio mp->mnt_kern_flag |= MNTK_LOOKUP_SHARED | MNTK_EXTENDED_SHARED; 677202283Slulf MNT_IUNLOCK(mp); 67812115Sdyson return (0); 67912115Sdysonout: 68012115Sdyson if (bp) 68112115Sdyson brelse(bp); 682137039Sphk if (cp != NULL) { 683137039Sphk DROP_GIANT(); 684137039Sphk g_topology_lock(); 685183754Sattilio g_vfs_close(cp); 686137039Sphk g_topology_unlock(); 687137039Sphk PICKUP_GIANT(); 688137039Sphk } 68912115Sdyson if (ump) { 690202283Slulf mtx_destroy(EXT2_MTX(ump)); 691202283Slulf free(ump->um_e2fs->e2fs_gd, M_EXT2MNT); 692202283Slulf free(ump->um_e2fs->e2fs_contigdirs, M_EXT2MNT); 693202283Slulf free(ump->um_e2fs->e2fs, M_EXT2MNT); 694193382Sstas free(ump->um_e2fs, M_EXT2MNT); 695193382Sstas free(ump, M_EXT2MNT); 696172697Salfred mp->mnt_data = NULL; 69712115Sdyson } 69812115Sdyson return (error); 69912115Sdyson} 70012115Sdyson 70112115Sdyson/* 702193382Sstas * Unmount system call. 70312115Sdyson */ 70412911Sphkstatic int 705193382Sstasext2_unmount(struct mount *mp, int mntflags) 70612115Sdyson{ 70796752Siedowse struct ext2mount *ump; 708202283Slulf struct m_ext2fs *fs; 709228539Spfg struct csum *sump; 710228539Spfg int error, flags, i, ronly; 71112115Sdyson 71212115Sdyson flags = 0; 71312115Sdyson if (mntflags & MNT_FORCE) { 71412115Sdyson if (mp->mnt_flag & MNT_ROOTFS) 71512115Sdyson return (EINVAL); 71612115Sdyson flags |= FORCECLOSE; 71712115Sdyson } 718191990Sattilio if ((error = ext2_flushfiles(mp, flags, curthread)) != 0) 71912115Sdyson return (error); 72096749Siedowse ump = VFSTOEXT2(mp); 72112115Sdyson fs = ump->um_e2fs; 722202283Slulf ronly = fs->e2fs_ronly; 723202283Slulf if (ronly == 0 && ext2_cgupdate(ump, MNT_WAIT) == 0) { 724202283Slulf if (fs->e2fs_wasvalid) 725202283Slulf fs->e2fs->e2fs_state |= E2FS_ISCLEAN; 726202283Slulf ext2_sbupdate(ump, MNT_WAIT); 72712115Sdyson } 72827881Sdyson 729137039Sphk DROP_GIANT(); 730137039Sphk g_topology_lock(); 731183754Sattilio g_vfs_close(ump->um_cp); 732137039Sphk g_topology_unlock(); 733137039Sphk PICKUP_GIANT(); 73412115Sdyson vrele(ump->um_devvp); 735228539Spfg sump = fs->e2fs_clustersum; 736228539Spfg for (i = 0; i < fs->e2fs_gcount; i++, sump++) 737228539Spfg free(sump->cs_sum, M_EXT2MNT); 738228539Spfg free(fs->e2fs_clustersum, M_EXT2MNT); 739228539Spfg free(fs->e2fs_maxcluster, M_EXT2MNT); 740202283Slulf free(fs->e2fs_gd, M_EXT2MNT); 741202283Slulf free(fs->e2fs_contigdirs, M_EXT2MNT); 742202283Slulf free(fs->e2fs, M_EXT2MNT); 743193382Sstas free(fs, M_EXT2MNT); 744193382Sstas free(ump, M_EXT2MNT); 745172697Salfred mp->mnt_data = NULL; 746162647Stegge MNT_ILOCK(mp); 74712115Sdyson mp->mnt_flag &= ~MNT_LOCAL; 748162647Stegge MNT_IUNLOCK(mp); 74912115Sdyson return (error); 75012115Sdyson} 75112115Sdyson 75212115Sdyson/* 75312115Sdyson * Flush out all the files in a filesystem. 75412115Sdyson */ 75512911Sphkstatic int 756193382Sstasext2_flushfiles(struct mount *mp, int flags, struct thread *td) 75712115Sdyson{ 75812147Sdyson int error; 75912115Sdyson 760132023Salfred error = vflush(mp, 0, flags, td); 76112115Sdyson return (error); 76212115Sdyson} 76312115Sdyson/* 764251612Spfg * Get filesystem statistics. 76512115Sdyson */ 766202283Slulfint 767193382Sstasext2_statfs(struct mount *mp, struct statfs *sbp) 76812115Sdyson{ 76996752Siedowse struct ext2mount *ump; 770202283Slulf struct m_ext2fs *fs; 771202283Slulf uint32_t overhead, overhead_per_group, ngdb; 772202283Slulf int i, ngroups; 77312115Sdyson 77496749Siedowse ump = VFSTOEXT2(mp); 77512115Sdyson fs = ump->um_e2fs; 776202283Slulf if (fs->e2fs->e2fs_magic != E2FS_MAGIC) 777246258Spfg panic("ext2_statfs"); 77812115Sdyson 77912115Sdyson /* 78012115Sdyson * Compute the overhead (FS structures) 78112115Sdyson */ 782202283Slulf overhead_per_group = 783202283Slulf 1 /* block bitmap */ + 784202283Slulf 1 /* inode bitmap */ + 785202283Slulf fs->e2fs_itpg; 786202283Slulf overhead = fs->e2fs->e2fs_first_dblock + 787202283Slulf fs->e2fs_gcount * overhead_per_group; 788202283Slulf if (fs->e2fs->e2fs_rev > E2FS_REV0 && 789202283Slulf fs->e2fs->e2fs_features_rocompat & EXT2F_ROCOMPAT_SPARSESUPER) { 790202283Slulf for (i = 0, ngroups = 0; i < fs->e2fs_gcount; i++) { 791202283Slulf if (cg_has_sb(i)) 792202283Slulf ngroups++; 793202283Slulf } 794202283Slulf } else { 795202283Slulf ngroups = fs->e2fs_gcount; 796202283Slulf } 797202283Slulf ngdb = fs->e2fs_gdbcount; 798202283Slulf if (fs->e2fs->e2fs_rev > E2FS_REV0 && 799202283Slulf fs->e2fs->e2fs_features_compat & EXT2F_COMPAT_RESIZE) 800202283Slulf ngdb += fs->e2fs->e2fs_reserved_ngdb; 801202283Slulf overhead += ngroups * (1 /* superblock */ + ngdb); 80212115Sdyson 803187396Sstas sbp->f_bsize = EXT2_FRAG_SIZE(fs); 80412115Sdyson sbp->f_iosize = EXT2_BLOCK_SIZE(fs); 805202283Slulf sbp->f_blocks = fs->e2fs->e2fs_bcount - overhead; 806202283Slulf sbp->f_bfree = fs->e2fs->e2fs_fbcount; 807202283Slulf sbp->f_bavail = sbp->f_bfree - fs->e2fs->e2fs_rbcount; 808202283Slulf sbp->f_files = fs->e2fs->e2fs_icount; 809202283Slulf sbp->f_ffree = fs->e2fs->e2fs_ficount; 81012115Sdyson return (0); 81112115Sdyson} 81212115Sdyson 81312115Sdyson/* 81412115Sdyson * Go through the disk queues to initiate sandbagged IO; 81512115Sdyson * go through the inodes to write those that have been modified; 81612115Sdyson * initiate the writing of the super block if it has been modified. 81712115Sdyson * 81812115Sdyson * Note: we are always called with the filesystem marked `MPBUSY'. 81912115Sdyson */ 82012911Sphkstatic int 821193382Sstasext2_sync(struct mount *mp, int waitfor) 82212115Sdyson{ 823154152Stegge struct vnode *mvp, *vp; 824191990Sattilio struct thread *td; 82539678Sbde struct inode *ip; 82696749Siedowse struct ext2mount *ump = VFSTOEXT2(mp); 827202283Slulf struct m_ext2fs *fs; 82812115Sdyson int error, allerror = 0; 82912115Sdyson 830191990Sattilio td = curthread; 83112115Sdyson fs = ump->um_e2fs; 832202283Slulf if (fs->e2fs_fmod != 0 && fs->e2fs_ronly != 0) { /* XXX */ 833202283Slulf printf("fs = %s\n", fs->e2fs_fsmnt); 83439678Sbde panic("ext2_sync: rofs mod"); 83512115Sdyson } 836193382Sstas 83712115Sdyson /* 83812115Sdyson * Write back each (modified) inode. 83912115Sdyson */ 84012115Sdysonloop: 841234386Smckusick MNT_VNODE_FOREACH_ALL(vp, mp, mvp) { 842234386Smckusick if (vp->v_type == VNON) { 843120783Sjeff VI_UNLOCK(vp); 844120783Sjeff continue; 845120783Sjeff } 84612115Sdyson ip = VTOI(vp); 847137008Sphk if ((ip->i_flag & 84812115Sdyson (IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE)) == 0 && 849136943Sphk (vp->v_bufobj.bo_dirty.bv_cnt == 0 || 850137008Sphk waitfor == MNT_LAZY)) { 851103938Sjeff VI_UNLOCK(vp); 85212115Sdyson continue; 85339678Sbde } 85483366Sjulian error = vget(vp, LK_EXCLUSIVE | LK_NOWAIT | LK_INTERLOCK, td); 85539678Sbde if (error) { 856154152Stegge if (error == ENOENT) { 857234386Smckusick MNT_VNODE_FOREACH_ALL_ABORT(mp, mvp); 85839678Sbde goto loop; 859154152Stegge } 86039678Sbde continue; 86139678Sbde } 862140048Sphk if ((error = VOP_FSYNC(vp, waitfor, td)) != 0) 86312115Sdyson allerror = error; 864175294Sattilio VOP_UNLOCK(vp, 0); 865121874Skan vrele(vp); 86612115Sdyson } 867193382Sstas 86812115Sdyson /* 869251612Spfg * Force stale filesystem control information to be flushed. 87012115Sdyson */ 87139678Sbde if (waitfor != MNT_LAZY) { 872175202Sattilio vn_lock(ump->um_devvp, LK_EXCLUSIVE | LK_RETRY); 873140048Sphk if ((error = VOP_FSYNC(ump->um_devvp, waitfor, td)) != 0) 87439678Sbde allerror = error; 875175294Sattilio VOP_UNLOCK(ump->um_devvp, 0); 87639678Sbde } 877193382Sstas 87839678Sbde /* 87939678Sbde * Write back modified superblock. 88039678Sbde */ 881202283Slulf if (fs->e2fs_fmod != 0) { 882202283Slulf fs->e2fs_fmod = 0; 883202283Slulf fs->e2fs->e2fs_wtime = time_second; 884202283Slulf if ((error = ext2_cgupdate(ump, waitfor)) != 0) 88539678Sbde allerror = error; 88639678Sbde } 88712115Sdyson return (allerror); 88812115Sdyson} 88912115Sdyson 89012115Sdyson/* 891108533Sschweikh * Look up an EXT2FS dinode number to find its incore vnode, otherwise read it 89212115Sdyson * in from disk. If it is in core, wait for the lock bit to clear, then 89312115Sdyson * return the inode locked. Detection and handling of mount points must be 89412115Sdyson * done by the calling routine. 89512115Sdyson */ 89612911Sphkstatic int 897193382Sstasext2_vget(struct mount *mp, ino_t ino, int flags, struct vnode **vpp) 89812115Sdyson{ 899202283Slulf struct m_ext2fs *fs; 90096752Siedowse struct inode *ip; 90196749Siedowse struct ext2mount *ump; 90212115Sdyson struct buf *bp; 90312115Sdyson struct vnode *vp; 904130585Sphk struct cdev *dev; 905193382Sstas struct thread *td; 90630280Sphk int i, error; 90712115Sdyson int used_blocks; 90812115Sdyson 909167497Stegge td = curthread; 910167497Stegge error = vfs_hash_get(mp, ino, flags, td, vpp, NULL, NULL); 911143619Sphk if (error || *vpp != NULL) 91292462Smckusick return (error); 91312115Sdyson 914143578Sphk ump = VFSTOEXT2(mp); 915143578Sphk dev = ump->um_dev; 916143578Sphk ip = malloc(sizeof(struct inode), M_EXT2NODE, M_WAITOK | M_ZERO); 91736102Sbde 91812115Sdyson /* Allocate a new vnode/inode. */ 919138290Sphk if ((error = getnewvnode("ext2fs", mp, &ext2_vnodeops, &vp)) != 0) { 92012115Sdyson *vpp = NULL; 921143578Sphk free(ip, M_EXT2NODE); 92212115Sdyson return (error); 92312115Sdyson } 92412115Sdyson vp->v_data = ip; 92512115Sdyson ip->i_vnode = vp; 92612115Sdyson ip->i_e2fs = fs = ump->um_e2fs; 927202283Slulf ip->i_ump = ump; 92812115Sdyson ip->i_number = ino; 929143578Sphk 930175635Sattilio lockmgr(vp->v_vnlock, LK_EXCLUSIVE, NULL); 931167497Stegge error = insmntque(vp, mp); 932167497Stegge if (error != 0) { 933167497Stegge free(ip, M_EXT2NODE); 934167497Stegge *vpp = NULL; 935167497Stegge return (error); 936167497Stegge } 937167497Stegge error = vfs_hash_insert(vp, ino, flags, td, vpp, NULL, NULL); 938143663Sphk if (error || *vpp != NULL) 939143578Sphk return (error); 94012406Sdyson 94112115Sdyson /* Read in the disk contents for the inode, copy into the inode. */ 94243301Sdillon if ((error = bread(ump->um_devvp, fsbtodb(fs, ino_to_fsba(fs, ino)), 943202283Slulf (int)fs->e2fs_bsize, NOCRED, &bp)) != 0) { 94412115Sdyson /* 94512115Sdyson * The inode does not contain anything useful, so it would 94612115Sdyson * be misleading to leave it on its hash chain. With mode 94712115Sdyson * still zero, it will be unlinked and returned to the free 94812115Sdyson * list by vput(). 94912115Sdyson */ 950202283Slulf brelse(bp); 95112115Sdyson vput(vp); 95212115Sdyson *vpp = NULL; 95312115Sdyson return (error); 95412115Sdyson } 95512115Sdyson /* convert ext2 inode to dinode */ 956202283Slulf ext2_ei2i((struct ext2fs_dinode *) ((char *)bp->b_data + EXT2_INODE_SIZE(fs) * 95796749Siedowse ino_to_fsbo(fs, ino)), ip); 95812115Sdyson ip->i_block_group = ino_to_cg(fs, ino); 95912115Sdyson ip->i_next_alloc_block = 0; 96012115Sdyson ip->i_next_alloc_goal = 0; 961193382Sstas 962193382Sstas /* 963193382Sstas * Now we want to make sure that block pointers for unused 964193382Sstas * blocks are zeroed out - ext2_balloc depends on this 965193382Sstas * although for regular files and directories only 966254260Spfg * 967254260Spfg * If EXT4_EXTENTS flag is enabled, unused blocks aren't 968254260Spfg * zeroed out because we could corrupt the extent tree. 969193382Sstas */ 970254260Spfg if (!(ip->i_flags & EXT4_EXTENTS) && 971254260Spfg (S_ISDIR(ip->i_mode) || S_ISREG(ip->i_mode))) { 972202283Slulf used_blocks = (ip->i_size+fs->e2fs_bsize-1) / fs->e2fs_bsize; 973228583Spfg for (i = used_blocks; i < EXT2_NDIR_BLOCKS; i++) 97412115Sdyson ip->i_db[i] = 0; 97512115Sdyson } 97612115Sdyson/* 97712115Sdyson ext2_print_inode(ip); 97812115Sdyson*/ 979202283Slulf bqrelse(bp); 98012115Sdyson 98112115Sdyson /* 98212115Sdyson * Initialize the vnode from the inode, check for aliases. 98312115Sdyson * Note that the underlying vnode may have changed. 98412115Sdyson */ 985138290Sphk if ((error = ext2_vinit(mp, &ext2_fifoops, &vp)) != 0) { 98612115Sdyson vput(vp); 98712115Sdyson *vpp = NULL; 98812115Sdyson return (error); 98912115Sdyson } 990193382Sstas 99112115Sdyson /* 992217582Sjhb * Finish inode initialization. 99312115Sdyson */ 994193382Sstas 99512115Sdyson /* 99612115Sdyson * Set up a generation number for this inode if it does not 99712115Sdyson * already have one. This should only happen on old filesystems. 99812115Sdyson */ 99912115Sdyson if (ip->i_gen == 0) { 1000252397Spfg ip->i_gen = random() + 1; 100112115Sdyson if ((vp->v_mount->mnt_flag & MNT_RDONLY) == 0) 100212115Sdyson ip->i_flag |= IN_MODIFIED; 100312115Sdyson } 100412115Sdyson *vpp = vp; 100512115Sdyson return (0); 100612115Sdyson} 100712115Sdyson 100812115Sdyson/* 100912115Sdyson * File handle to vnode 101012115Sdyson * 101112115Sdyson * Have to be really careful about stale file handles: 101212115Sdyson * - check that the inode number is valid 101312115Sdyson * - call ext2_vget() to get the locked inode 101412115Sdyson * - check for an unallocated inode (i_mode == 0) 101512115Sdyson * - check that the given client host has export rights and return 101612115Sdyson * those rights via. exflagsp and credanonp 101712115Sdyson */ 101812911Sphkstatic int 1019222167Srmacklemext2_fhtovp(struct mount *mp, struct fid *fhp, int flags, struct vnode **vpp) 102012115Sdyson{ 102196749Siedowse struct inode *ip; 102296752Siedowse struct ufid *ufhp; 102396749Siedowse struct vnode *nvp; 1024202283Slulf struct m_ext2fs *fs; 102596749Siedowse int error; 102612115Sdyson 102712115Sdyson ufhp = (struct ufid *)fhp; 102896749Siedowse fs = VFSTOEXT2(mp)->um_e2fs; 1029221128Sjhb if (ufhp->ufid_ino < EXT2_ROOTINO || 1030202283Slulf ufhp->ufid_ino > fs->e2fs_gcount * fs->e2fs->e2fs_ipg) 103112115Sdyson return (ESTALE); 103296749Siedowse 103396749Siedowse error = VFS_VGET(mp, ufhp->ufid_ino, LK_EXCLUSIVE, &nvp); 103496749Siedowse if (error) { 103596749Siedowse *vpp = NULLVP; 103696749Siedowse return (error); 103796749Siedowse } 103896749Siedowse ip = VTOI(nvp); 103996749Siedowse if (ip->i_mode == 0 || 104096749Siedowse ip->i_gen != ufhp->ufid_gen || ip->i_nlink <= 0) { 104196749Siedowse vput(nvp); 104296749Siedowse *vpp = NULLVP; 104396749Siedowse return (ESTALE); 104496749Siedowse } 104596749Siedowse *vpp = nvp; 1046140768Sphk vnode_create_vobject(*vpp, 0, curthread); 104796749Siedowse return (0); 104812115Sdyson} 104912115Sdyson 105012115Sdyson/* 105112115Sdyson * Write a superblock and associated information back to disk. 105212115Sdyson */ 105312911Sphkstatic int 1054193382Sstasext2_sbupdate(struct ext2mount *mp, int waitfor) 105512115Sdyson{ 1056202283Slulf struct m_ext2fs *fs = mp->um_e2fs; 1057202283Slulf struct ext2fs *es = fs->e2fs; 105896752Siedowse struct buf *bp; 105941591Sarchie int error = 0; 1060193382Sstas 1061111856Sjeff bp = getblk(mp->um_devvp, SBLOCK, SBSIZE, 0, 0, 0); 1062202283Slulf bcopy((caddr_t)es, bp->b_data, (u_int)sizeof(struct ext2fs)); 106312115Sdyson if (waitfor == MNT_WAIT) 106412115Sdyson error = bwrite(bp); 106512115Sdyson else 106612115Sdyson bawrite(bp); 106712115Sdyson 106827881Sdyson /* 106927881Sdyson * The buffers for group descriptors, inode bitmaps and block bitmaps 107027881Sdyson * are not busy at this point and are (hopefully) written by the 1071193382Sstas * usual sync mechanism. No need to write them here. 1072193382Sstas */ 107312115Sdyson return (error); 107412115Sdyson} 1075202283Slulfint 1076202283Slulfext2_cgupdate(struct ext2mount *mp, int waitfor) 1077202283Slulf{ 1078202283Slulf struct m_ext2fs *fs = mp->um_e2fs; 1079202283Slulf struct buf *bp; 1080202283Slulf int i, error = 0, allerror = 0; 108196749Siedowse 1082202283Slulf allerror = ext2_sbupdate(mp, waitfor); 1083202283Slulf for (i = 0; i < fs->e2fs_gdbcount; i++) { 1084202283Slulf bp = getblk(mp->um_devvp, fsbtodb(fs, 1085202283Slulf fs->e2fs->e2fs_first_dblock + 1086202283Slulf 1 /* superblock */ + i), fs->e2fs_bsize, 0, 0, 0); 1087202283Slulf e2fs_cgsave(&fs->e2fs_gd[ 1088202283Slulf i * fs->e2fs_bsize / sizeof(struct ext2_gd)], 1089202283Slulf (struct ext2_gd *)bp->b_data, fs->e2fs_bsize); 1090202283Slulf if (waitfor == MNT_WAIT) 1091202283Slulf error = bwrite(bp); 1092202283Slulf else 1093202283Slulf bawrite(bp); 1094202283Slulf } 1095202283Slulf 1096202283Slulf if (!allerror && error) 1097202283Slulf allerror = error; 1098202283Slulf return (allerror); 1099202283Slulf} 110096749Siedowse/* 110196749Siedowse * Return the root of a filesystem. 110296749Siedowse */ 110396749Siedowsestatic int 1104193382Sstasext2_root(struct mount *mp, int flags, struct vnode **vpp) 110596749Siedowse{ 110696749Siedowse struct vnode *nvp; 110796749Siedowse int error; 110896749Siedowse 1109221128Sjhb error = VFS_VGET(mp, EXT2_ROOTINO, LK_EXCLUSIVE, &nvp); 111096749Siedowse if (error) 111196749Siedowse return (error); 111296749Siedowse *vpp = nvp; 111396749Siedowse return (0); 111496749Siedowse} 1115