ext2_vfsops.c revision 232703
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: head/sys/fs/ext2fs/ext2_vfsops.c 232703 2012-03-08 21:06:05Z pfg $ 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; 11596749Siedowse struct ext2mount *ump = 0; 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.. */ 13073286Sadrian if (strlen(path) >= MAXMNTLEN - 1) 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; 32112115Sdyson 322202283Slulf fs->e2fs_bsize = EXT2_MIN_BLOCK_SIZE << es->e2fs_log_bsize; 323202283Slulf fs->e2fs_bshift = EXT2_MIN_BLOCK_LOG_SIZE + es->e2fs_log_bsize; 324202283Slulf fs->e2fs_fsbtodb = es->e2fs_log_bsize + 1; 325202283Slulf fs->e2fs_qbmask = fs->e2fs_bsize - 1; 326202283Slulf fs->e2fs_blocksize_bits = es->e2fs_log_bsize + 10; 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) { 334218176Sjhb fs->e2fs_first_inode = EXT2_FIRSTINO; 335202283Slulf fs->e2fs_isize = E2FS_REV0_INODE_SIZE ; 336193382Sstas } else { 337202283Slulf fs->e2fs_first_inode = es->e2fs_first_ino; 338202283Slulf fs->e2fs_isize = es->e2fs_inode_size; 33912115Sdyson 340193382Sstas /* 341193382Sstas * Simple sanity check for superblock inode size value. 342193382Sstas */ 343232703Spfg if (EXT2_INODE_SIZE(fs) < E2FS_REV0_INODE_SIZE || 344232703Spfg EXT2_INODE_SIZE(fs) > fs->e2fs_bsize || 345202283Slulf (fs->e2fs_isize & (fs->e2fs_isize - 1)) != 0) { 346232703Spfg printf("ext2fs: invalid inode size %d\n", 347202283Slulf fs->e2fs_isize); 348193382Sstas return (EIO); 349193382Sstas } 350193377Sstas } 351232703Spfg /* Check for extra isize in big inodes. */ 352232703Spfg if (EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT4F_ROCOMPAT_EXTRA_ISIZE) && 353232703Spfg EXT2_INODE_SIZE(fs) < sizeof(struct ext2fs_dinode)) { 354232703Spfg printf("ext2fs: no space for extra inode timestamps\n"); 355232703Spfg return (EINVAL); 356232703Spfg } 357232703Spfg 358202283Slulf fs->e2fs_ipb = fs->e2fs_bsize / EXT2_INODE_SIZE(fs); 359202283Slulf fs->e2fs_itpg = fs->e2fs_ipg /fs->e2fs_ipb; 360228583Spfg fs->e2fs_descpb = fs->e2fs_bsize / sizeof(struct ext2_gd); 361193382Sstas /* s_resuid / s_resgid ? */ 362202283Slulf fs->e2fs_gcount = (es->e2fs_bcount - es->e2fs_first_dblock + 363193382Sstas EXT2_BLOCKS_PER_GROUP(fs) - 1) / EXT2_BLOCKS_PER_GROUP(fs); 364202283Slulf db_count = (fs->e2fs_gcount + EXT2_DESC_PER_BLOCK(fs) - 1) / 365193382Sstas EXT2_DESC_PER_BLOCK(fs); 366202283Slulf fs->e2fs_gdbcount = db_count; 367202283Slulf fs->e2fs_gd = malloc(db_count * fs->e2fs_bsize, 368193382Sstas M_EXT2MNT, M_WAITOK); 369232703Spfg fs->e2fs_contigdirs = malloc(fs->e2fs_gcount * 370232703Spfg sizeof(*fs->e2fs_contigdirs), M_EXT2MNT, M_WAITOK); 37112115Sdyson 372193382Sstas /* 373193382Sstas * Adjust logic_sb_block. 374193382Sstas * Godmar thinks: if the blocksize is greater than 1024, then 375193382Sstas * the superblock is logically part of block zero. 37612115Sdyson */ 377202283Slulf if(fs->e2fs_bsize > SBSIZE) 378193382Sstas logic_sb_block = 0; 379193382Sstas for (i = 0; i < db_count; i++) { 380202283Slulf error = bread(devvp , 381202283Slulf fsbtodb(fs, logic_sb_block + i + 1 ), 382202283Slulf fs->e2fs_bsize, NOCRED, &bp); 383202283Slulf if (error) { 384202283Slulf free(fs->e2fs_gd, M_EXT2MNT); 385202283Slulf brelse(bp); 386202283Slulf return (error); 387193382Sstas } 388202283Slulf e2fs_cgload((struct ext2_gd *)bp->b_data, 389202283Slulf &fs->e2fs_gd[ 390202283Slulf i * fs->e2fs_bsize / sizeof(struct ext2_gd)], 391202283Slulf fs->e2fs_bsize); 392202283Slulf brelse(bp); 393202283Slulf bp = NULL; 39412115Sdyson } 395202283Slulf fs->e2fs_total_dir = 0; 396202283Slulf for (i=0; i < fs->e2fs_gcount; i++){ 397202283Slulf fs->e2fs_total_dir += fs->e2fs_gd[i].ext2bgd_ndirs; 398202283Slulf fs->e2fs_contigdirs[i] = 0; 399193382Sstas } 400202283Slulf if (es->e2fs_rev == E2FS_REV0 || 401232703Spfg !EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_LARGEFILE)) 402202283Slulf fs->e2fs_maxfilesize = 0x7fffffff; 403193382Sstas else 404202283Slulf fs->e2fs_maxfilesize = 0x7fffffffffffffff; 405193382Sstas return (0); 40612115Sdyson} 40712115Sdyson 40812115Sdyson/* 40912115Sdyson * Reload all incore data for a filesystem (used after running fsck on 41012115Sdyson * the root filesystem and finding things to fix). The filesystem must 41112115Sdyson * be mounted read-only. 41212115Sdyson * 41312115Sdyson * Things to do to update the mount: 41412115Sdyson * 1) invalidate all cached meta-data. 41512115Sdyson * 2) re-read superblock from disk. 416228539Spfg * 3) invalidate all cluster summary information. 41712115Sdyson * 4) invalidate all inactive vnodes. 41812115Sdyson * 5) invalidate all cached file data. 41912115Sdyson * 6) re-read inode data for all active vnodes. 420202283Slulf * XXX we are missing some steps, in particular # 3, this has to be reviewed. 42112115Sdyson */ 42212911Sphkstatic int 423140736Sphkext2_reload(struct mount *mp, struct thread *td) 42412115Sdyson{ 425154152Stegge struct vnode *vp, *mvp, *devvp; 42612115Sdyson struct inode *ip; 42712115Sdyson struct buf *bp; 428202283Slulf struct ext2fs *es; 429202283Slulf struct m_ext2fs *fs; 430228539Spfg struct csum *sump; 431228539Spfg int error, i; 432228539Spfg int32_t *lp; 43312115Sdyson 434122114Sbde if ((mp->mnt_flag & MNT_RDONLY) == 0) 43512115Sdyson return (EINVAL); 43612115Sdyson /* 43712115Sdyson * Step 1: invalidate all cached meta-data. 43812115Sdyson */ 439122114Sbde devvp = VFSTOEXT2(mp)->um_devvp; 440175202Sattilio vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY); 441183754Sattilio if (vinvalbuf(devvp, 0, 0, 0) != 0) 44212115Sdyson panic("ext2_reload: dirty1"); 443175294Sattilio VOP_UNLOCK(devvp, 0); 444125786Sbde 44512115Sdyson /* 44612115Sdyson * Step 2: re-read superblock from disk. 44712115Sdyson * constants have been adjusted for ext2 44812115Sdyson */ 44943301Sdillon if ((error = bread(devvp, SBLOCK, SBSIZE, NOCRED, &bp)) != 0) 45012115Sdyson return (error); 451202283Slulf es = (struct ext2fs *)bp->b_data; 45255313Sbde if (ext2_check_sb_compat(es, devvp->v_rdev, 0) != 0) { 45312115Sdyson brelse(bp); 45412115Sdyson return (EIO); /* XXX needs translation */ 45512115Sdyson } 456122114Sbde fs = VFSTOEXT2(mp)->um_e2fs; 457202283Slulf bcopy(bp->b_data, fs->e2fs, sizeof(struct ext2fs)); 45812115Sdyson 45943301Sdillon if((error = compute_sb_data(devvp, es, fs)) != 0) { 46012115Sdyson brelse(bp); 461193382Sstas return (error); 46212115Sdyson } 46312115Sdyson#ifdef UNKLAR 46412115Sdyson if (fs->fs_sbsize < SBSIZE) 46512115Sdyson bp->b_flags |= B_INVAL; 46612115Sdyson#endif 46712115Sdyson brelse(bp); 46812115Sdyson 469228539Spfg /* 470228539Spfg * Step 3: invalidate all cluster summary information. 471228539Spfg */ 472228539Spfg if (fs->e2fs_contigsumsize > 0) { 473228539Spfg lp = fs->e2fs_maxcluster; 474228539Spfg sump = fs->e2fs_clustersum; 475228539Spfg for (i = 0; i < fs->e2fs_gcount; i++, sump++) { 476228539Spfg *lp++ = fs->e2fs_contigsumsize; 477228539Spfg sump->cs_init = 0; 478228539Spfg bzero(sump->cs_sum, fs->e2fs_contigsumsize + 1); 479228539Spfg } 480228539Spfg } 481228539Spfg 48212115Sdysonloop: 483122091Skan MNT_ILOCK(mp); 484154152Stegge MNT_VNODE_FOREACH(vp, mp, mvp) { 485120751Sjeff VI_LOCK(vp); 486143509Sjeff if (vp->v_iflag & VI_DOOMED) { 487120783Sjeff VI_UNLOCK(vp); 488120783Sjeff continue; 489120783Sjeff } 490122091Skan MNT_IUNLOCK(mp); 49112115Sdyson /* 492143509Sjeff * Step 4: invalidate all cached file data. 49312115Sdyson */ 49483366Sjulian if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK, td)) { 495154152Stegge MNT_VNODE_FOREACH_ABORT(mp, mvp); 49612115Sdyson goto loop; 49739678Sbde } 498183754Sattilio if (vinvalbuf(vp, 0, 0, 0)) 49912115Sdyson panic("ext2_reload: dirty2"); 500193382Sstas 50112115Sdyson /* 502143509Sjeff * Step 5: re-read inode data for all active vnodes. 50312115Sdyson */ 50412115Sdyson ip = VTOI(vp); 505193382Sstas error = bread(devvp, fsbtodb(fs, ino_to_fsba(fs, ip->i_number)), 506202283Slulf (int)fs->e2fs_bsize, NOCRED, &bp); 50739678Sbde if (error) { 508175294Sattilio VOP_UNLOCK(vp, 0); 509121925Skan vrele(vp); 510154152Stegge MNT_VNODE_FOREACH_ABORT(mp, mvp); 51112115Sdyson return (error); 51212115Sdyson } 513202283Slulf ext2_ei2i((struct ext2fs_dinode *) ((char *)bp->b_data + 514187395Sstas EXT2_INODE_SIZE(fs) * ino_to_fsbo(fs, ip->i_number)), ip); 51512115Sdyson brelse(bp); 516175294Sattilio VOP_UNLOCK(vp, 0); 517121925Skan vrele(vp); 518122091Skan MNT_ILOCK(mp); 51912115Sdyson } 520122091Skan MNT_IUNLOCK(mp); 52112115Sdyson return (0); 52212115Sdyson} 52312115Sdyson 52412115Sdyson/* 525193382Sstas * Common code for mount and mountroot. 52612115Sdyson */ 52712911Sphkstatic int 528193382Sstasext2_mountfs(struct vnode *devvp, struct mount *mp) 52912115Sdyson{ 53096752Siedowse struct ext2mount *ump; 53112115Sdyson struct buf *bp; 532202283Slulf struct m_ext2fs *fs; 533202283Slulf struct ext2fs *es; 534130585Sphk struct cdev *dev = devvp->v_rdev; 535137039Sphk struct g_consumer *cp; 536137039Sphk struct bufobj *bo; 537228539Spfg struct csum *sump; 53896749Siedowse int error; 53912115Sdyson int ronly; 540228539Spfg int i, size; 541228539Spfg int32_t *lp; 54212115Sdyson 543138493Sphk ronly = vfs_flagopt(mp->mnt_optnew, "ro", NULL, 0); 544137039Sphk /* XXX: use VOP_ACESS to check FS perms */ 545137039Sphk DROP_GIANT(); 546137039Sphk g_topology_lock(); 547137039Sphk error = g_vfs_open(devvp, &cp, "ext2fs", ronly ? 0 : 1); 548137039Sphk g_topology_unlock(); 549137039Sphk PICKUP_GIANT(); 550175294Sattilio VOP_UNLOCK(devvp, 0); 55153059Sphk if (error) 55212115Sdyson return (error); 553149960Srodrigc 554149960Srodrigc /* XXX: should we check for some sectorsize or 512 instead? */ 555149960Srodrigc if (((SBSIZE % cp->provider->sectorsize) != 0) || 556149960Srodrigc (SBSIZE < cp->provider->sectorsize)) { 557149960Srodrigc DROP_GIANT(); 558149960Srodrigc g_topology_lock(); 559183754Sattilio g_vfs_close(cp); 560149960Srodrigc g_topology_unlock(); 561149960Srodrigc PICKUP_GIANT(); 562149960Srodrigc return (EINVAL); 563149960Srodrigc } 564149960Srodrigc 565137039Sphk bo = &devvp->v_bufobj; 566137039Sphk bo->bo_private = cp; 567137039Sphk bo->bo_ops = g_vfs_bufops; 56893430Sbde if (devvp->v_rdev->si_iosize_max != 0) 56993430Sbde mp->mnt_iosize_max = devvp->v_rdev->si_iosize_max; 57093430Sbde if (mp->mnt_iosize_max > MAXPHYS) 57193430Sbde mp->mnt_iosize_max = MAXPHYS; 57293430Sbde 57312115Sdyson bp = NULL; 57412115Sdyson ump = NULL; 57543301Sdillon if ((error = bread(devvp, SBLOCK, SBSIZE, NOCRED, &bp)) != 0) 57612115Sdyson goto out; 577202283Slulf es = (struct ext2fs *)bp->b_data; 57855313Sbde if (ext2_check_sb_compat(es, dev, ronly) != 0) { 57912115Sdyson error = EINVAL; /* XXX needs translation */ 58012115Sdyson goto out; 58112115Sdyson } 582202283Slulf if ((es->e2fs_state & E2FS_ISCLEAN) == 0 || 583202283Slulf (es->e2fs_state & E2FS_ERRORS)) { 58439670Sbde if (ronly || (mp->mnt_flag & MNT_FORCE)) { 585193628Sstas printf( 586193628Sstas"WARNING: Filesystem was not properly dismounted\n"); 58739670Sbde } else { 588193628Sstas printf( 589193628Sstas"WARNING: R/W mount denied. Filesystem is not clean - run fsck\n"); 59039670Sbde error = EPERM; 59139670Sbde goto out; 59239670Sbde } 59339670Sbde } 594228583Spfg ump = malloc(sizeof(*ump), M_EXT2MNT, M_WAITOK | M_ZERO); 595193382Sstas 596193382Sstas /* 597193382Sstas * I don't know whether this is the right strategy. Note that 598193382Sstas * we dynamically allocate both an ext2_sb_info and an ext2_super_block 599193382Sstas * while Linux keeps the super block in a locked buffer. 60012115Sdyson */ 601202283Slulf ump->um_e2fs = malloc(sizeof(struct m_ext2fs), 602111119Simp M_EXT2MNT, M_WAITOK); 603202283Slulf ump->um_e2fs->e2fs = malloc(sizeof(struct ext2fs), 604111119Simp M_EXT2MNT, M_WAITOK); 605202283Slulf mtx_init(EXT2_MTX(ump), "EXT2FS", "EXT2FS Lock", MTX_DEF); 606202283Slulf bcopy(es, ump->um_e2fs->e2fs, (u_int)sizeof(struct ext2fs)); 607202283Slulf if ((error = compute_sb_data(devvp, ump->um_e2fs->e2fs, ump->um_e2fs))) 60839671Sbde goto out; 609193382Sstas 610228539Spfg /* 611228539Spfg * Calculate the maximum contiguous blocks and size of cluster summary 612228583Spfg * array. In FFS this is done by newfs; however, the superblock 613228583Spfg * in ext2fs doesn't have these variables, so we can calculate 614228539Spfg * them here. 615228539Spfg */ 616228539Spfg ump->um_e2fs->e2fs_maxcontig = MAX(1, MAXPHYS / ump->um_e2fs->e2fs_bsize); 617228539Spfg if (ump->um_e2fs->e2fs_maxcontig > 0) 618228539Spfg ump->um_e2fs->e2fs_contigsumsize = 619228539Spfg MIN(ump->um_e2fs->e2fs_maxcontig, EXT2_MAXCONTIG); 620228539Spfg else 621228539Spfg ump->um_e2fs->e2fs_contigsumsize = 0; 622228539Spfg if (ump->um_e2fs->e2fs_contigsumsize > 0) { 623228539Spfg size = ump->um_e2fs->e2fs_gcount * sizeof(int32_t); 624228539Spfg ump->um_e2fs->e2fs_maxcluster = malloc(size, M_EXT2MNT, M_WAITOK); 625228539Spfg size = ump->um_e2fs->e2fs_gcount * sizeof(struct csum); 626228539Spfg ump->um_e2fs->e2fs_clustersum = malloc(size, M_EXT2MNT, M_WAITOK); 627228539Spfg lp = ump->um_e2fs->e2fs_maxcluster; 628228539Spfg sump = ump->um_e2fs->e2fs_clustersum; 629228539Spfg for (i = 0; i < ump->um_e2fs->e2fs_gcount; i++, sump++) { 630228539Spfg *lp++ = ump->um_e2fs->e2fs_contigsumsize; 631228539Spfg sump->cs_init = 0; 632228539Spfg sump->cs_sum = malloc((ump->um_e2fs->e2fs_contigsumsize + 1) * 633228539Spfg sizeof(int32_t), M_EXT2MNT, M_WAITOK | M_ZERO); 634228539Spfg } 635228539Spfg } 636228539Spfg 63712115Sdyson brelse(bp); 63812115Sdyson bp = NULL; 63912115Sdyson fs = ump->um_e2fs; 640202283Slulf fs->e2fs_ronly = ronly; /* ronly is set according to mnt_flags */ 641193382Sstas 642193382Sstas /* 643193382Sstas * If the fs is not mounted read-only, make sure the super block is 644193382Sstas * always written back on a sync(). 64512115Sdyson */ 646202283Slulf fs->e2fs_wasvalid = fs->e2fs->e2fs_state & E2FS_ISCLEAN ? 1 : 0; 64712115Sdyson if (ronly == 0) { 648202283Slulf fs->e2fs_fmod = 1; /* mark it modified */ 649202283Slulf fs->e2fs->e2fs_state &= ~E2FS_ISCLEAN; /* set fs invalid */ 65012115Sdyson } 651172697Salfred mp->mnt_data = ump; 65250256Sbde mp->mnt_stat.f_fsid.val[0] = dev2udev(dev); 65338909Sbde mp->mnt_stat.f_fsid.val[1] = mp->mnt_vfc->vfc_typenum; 65412115Sdyson mp->mnt_maxsymlinklen = EXT2_MAXSYMLINKLEN; 655162647Stegge MNT_ILOCK(mp); 65612115Sdyson mp->mnt_flag |= MNT_LOCAL; 657162647Stegge MNT_IUNLOCK(mp); 65812115Sdyson ump->um_mountp = mp; 65912115Sdyson ump->um_dev = dev; 66012115Sdyson ump->um_devvp = devvp; 661137320Sphk ump->um_bo = &devvp->v_bufobj; 662137320Sphk ump->um_cp = cp; 663193382Sstas 664193382Sstas /* 665193382Sstas * Setting those two parameters allowed us to use 666193382Sstas * ufs_bmap w/o changse! 667193382Sstas */ 66812115Sdyson ump->um_nindir = EXT2_ADDR_PER_BLOCK(fs); 669202283Slulf ump->um_bptrtodb = fs->e2fs->e2fs_log_bsize + 1; 67012115Sdyson ump->um_seqinc = EXT2_FRAGS_PER_BLOCK(fs); 671187396Sstas if (ronly == 0) 67234430Seivind ext2_sbupdate(ump, MNT_WAIT); 673202283Slulf /* 674202283Slulf * Initialize filesystem stat information in mount struct. 675202283Slulf */ 676202283Slulf MNT_ILOCK(mp); 677202283Slulf mp->mnt_kern_flag |= MNTK_MPSAFE | MNTK_LOOKUP_SHARED | 678202283Slulf MNTK_EXTENDED_SHARED; 679202283Slulf MNT_IUNLOCK(mp); 68012115Sdyson return (0); 68112115Sdysonout: 68212115Sdyson if (bp) 68312115Sdyson brelse(bp); 684137039Sphk if (cp != NULL) { 685137039Sphk DROP_GIANT(); 686137039Sphk g_topology_lock(); 687183754Sattilio g_vfs_close(cp); 688137039Sphk g_topology_unlock(); 689137039Sphk PICKUP_GIANT(); 690137039Sphk } 69112115Sdyson if (ump) { 692202283Slulf mtx_destroy(EXT2_MTX(ump)); 693202283Slulf free(ump->um_e2fs->e2fs_gd, M_EXT2MNT); 694202283Slulf free(ump->um_e2fs->e2fs_contigdirs, M_EXT2MNT); 695202283Slulf free(ump->um_e2fs->e2fs, M_EXT2MNT); 696193382Sstas free(ump->um_e2fs, M_EXT2MNT); 697193382Sstas free(ump, M_EXT2MNT); 698172697Salfred mp->mnt_data = NULL; 69912115Sdyson } 70012115Sdyson return (error); 70112115Sdyson} 70212115Sdyson 70312115Sdyson/* 704193382Sstas * Unmount system call. 70512115Sdyson */ 70612911Sphkstatic int 707193382Sstasext2_unmount(struct mount *mp, int mntflags) 70812115Sdyson{ 70996752Siedowse struct ext2mount *ump; 710202283Slulf struct m_ext2fs *fs; 711228539Spfg struct csum *sump; 712228539Spfg int error, flags, i, ronly; 71312115Sdyson 71412115Sdyson flags = 0; 71512115Sdyson if (mntflags & MNT_FORCE) { 71612115Sdyson if (mp->mnt_flag & MNT_ROOTFS) 71712115Sdyson return (EINVAL); 71812115Sdyson flags |= FORCECLOSE; 71912115Sdyson } 720191990Sattilio if ((error = ext2_flushfiles(mp, flags, curthread)) != 0) 72112115Sdyson return (error); 72296749Siedowse ump = VFSTOEXT2(mp); 72312115Sdyson fs = ump->um_e2fs; 724202283Slulf ronly = fs->e2fs_ronly; 725202283Slulf if (ronly == 0 && ext2_cgupdate(ump, MNT_WAIT) == 0) { 726202283Slulf if (fs->e2fs_wasvalid) 727202283Slulf fs->e2fs->e2fs_state |= E2FS_ISCLEAN; 728202283Slulf ext2_sbupdate(ump, MNT_WAIT); 72912115Sdyson } 73027881Sdyson 731137039Sphk DROP_GIANT(); 732137039Sphk g_topology_lock(); 733183754Sattilio g_vfs_close(ump->um_cp); 734137039Sphk g_topology_unlock(); 735137039Sphk PICKUP_GIANT(); 73612115Sdyson vrele(ump->um_devvp); 737228539Spfg sump = fs->e2fs_clustersum; 738228539Spfg for (i = 0; i < fs->e2fs_gcount; i++, sump++) 739228539Spfg free(sump->cs_sum, M_EXT2MNT); 740228539Spfg free(fs->e2fs_clustersum, M_EXT2MNT); 741228539Spfg free(fs->e2fs_maxcluster, M_EXT2MNT); 742202283Slulf free(fs->e2fs_gd, M_EXT2MNT); 743202283Slulf free(fs->e2fs_contigdirs, M_EXT2MNT); 744202283Slulf free(fs->e2fs, M_EXT2MNT); 745193382Sstas free(fs, M_EXT2MNT); 746193382Sstas free(ump, M_EXT2MNT); 747172697Salfred mp->mnt_data = NULL; 748162647Stegge MNT_ILOCK(mp); 74912115Sdyson mp->mnt_flag &= ~MNT_LOCAL; 750162647Stegge MNT_IUNLOCK(mp); 75112115Sdyson return (error); 75212115Sdyson} 75312115Sdyson 75412115Sdyson/* 75512115Sdyson * Flush out all the files in a filesystem. 75612115Sdyson */ 75712911Sphkstatic int 758193382Sstasext2_flushfiles(struct mount *mp, int flags, struct thread *td) 75912115Sdyson{ 76012147Sdyson int error; 76112115Sdyson 762132023Salfred error = vflush(mp, 0, flags, td); 76312115Sdyson return (error); 76412115Sdyson} 76512115Sdyson/* 76612115Sdyson * Get file system statistics. 76712115Sdyson */ 768202283Slulfint 769193382Sstasext2_statfs(struct mount *mp, struct statfs *sbp) 77012115Sdyson{ 77196752Siedowse struct ext2mount *ump; 772202283Slulf struct m_ext2fs *fs; 773202283Slulf uint32_t overhead, overhead_per_group, ngdb; 774202283Slulf int i, ngroups; 77512115Sdyson 77696749Siedowse ump = VFSTOEXT2(mp); 77712115Sdyson fs = ump->um_e2fs; 778202283Slulf if (fs->e2fs->e2fs_magic != E2FS_MAGIC) 779202283Slulf panic("ext2fs_statvfs"); 78012115Sdyson 78112115Sdyson /* 78212115Sdyson * Compute the overhead (FS structures) 78312115Sdyson */ 784202283Slulf overhead_per_group = 785202283Slulf 1 /* block bitmap */ + 786202283Slulf 1 /* inode bitmap */ + 787202283Slulf fs->e2fs_itpg; 788202283Slulf overhead = fs->e2fs->e2fs_first_dblock + 789202283Slulf fs->e2fs_gcount * overhead_per_group; 790202283Slulf if (fs->e2fs->e2fs_rev > E2FS_REV0 && 791202283Slulf fs->e2fs->e2fs_features_rocompat & EXT2F_ROCOMPAT_SPARSESUPER) { 792202283Slulf for (i = 0, ngroups = 0; i < fs->e2fs_gcount; i++) { 793202283Slulf if (cg_has_sb(i)) 794202283Slulf ngroups++; 795202283Slulf } 796202283Slulf } else { 797202283Slulf ngroups = fs->e2fs_gcount; 798202283Slulf } 799202283Slulf ngdb = fs->e2fs_gdbcount; 800202283Slulf if (fs->e2fs->e2fs_rev > E2FS_REV0 && 801202283Slulf fs->e2fs->e2fs_features_compat & EXT2F_COMPAT_RESIZE) 802202283Slulf ngdb += fs->e2fs->e2fs_reserved_ngdb; 803202283Slulf overhead += ngroups * (1 /* superblock */ + ngdb); 80412115Sdyson 805187396Sstas sbp->f_bsize = EXT2_FRAG_SIZE(fs); 80612115Sdyson sbp->f_iosize = EXT2_BLOCK_SIZE(fs); 807202283Slulf sbp->f_blocks = fs->e2fs->e2fs_bcount - overhead; 808202283Slulf sbp->f_bfree = fs->e2fs->e2fs_fbcount; 809202283Slulf sbp->f_bavail = sbp->f_bfree - fs->e2fs->e2fs_rbcount; 810202283Slulf sbp->f_files = fs->e2fs->e2fs_icount; 811202283Slulf sbp->f_ffree = fs->e2fs->e2fs_ficount; 81212115Sdyson return (0); 81312115Sdyson} 81412115Sdyson 81512115Sdyson/* 81612115Sdyson * Go through the disk queues to initiate sandbagged IO; 81712115Sdyson * go through the inodes to write those that have been modified; 81812115Sdyson * initiate the writing of the super block if it has been modified. 81912115Sdyson * 82012115Sdyson * Note: we are always called with the filesystem marked `MPBUSY'. 82112115Sdyson */ 82212911Sphkstatic int 823193382Sstasext2_sync(struct mount *mp, int waitfor) 82412115Sdyson{ 825154152Stegge struct vnode *mvp, *vp; 826191990Sattilio struct thread *td; 82739678Sbde struct inode *ip; 82896749Siedowse struct ext2mount *ump = VFSTOEXT2(mp); 829202283Slulf struct m_ext2fs *fs; 83012115Sdyson int error, allerror = 0; 83112115Sdyson 832191990Sattilio td = curthread; 83312115Sdyson fs = ump->um_e2fs; 834202283Slulf if (fs->e2fs_fmod != 0 && fs->e2fs_ronly != 0) { /* XXX */ 835202283Slulf printf("fs = %s\n", fs->e2fs_fsmnt); 83639678Sbde panic("ext2_sync: rofs mod"); 83712115Sdyson } 838193382Sstas 83912115Sdyson /* 84012115Sdyson * Write back each (modified) inode. 84112115Sdyson */ 842122091Skan MNT_ILOCK(mp); 84312115Sdysonloop: 844154152Stegge MNT_VNODE_FOREACH(vp, mp, mvp) { 845120751Sjeff VI_LOCK(vp); 846143509Sjeff if (vp->v_type == VNON || (vp->v_iflag & VI_DOOMED)) { 847120783Sjeff VI_UNLOCK(vp); 848120783Sjeff continue; 849120783Sjeff } 850122091Skan MNT_IUNLOCK(mp); 85112115Sdyson ip = VTOI(vp); 852137008Sphk if ((ip->i_flag & 85312115Sdyson (IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE)) == 0 && 854136943Sphk (vp->v_bufobj.bo_dirty.bv_cnt == 0 || 855137008Sphk waitfor == MNT_LAZY)) { 856103938Sjeff VI_UNLOCK(vp); 857122091Skan MNT_ILOCK(mp); 85812115Sdyson continue; 85939678Sbde } 86083366Sjulian error = vget(vp, LK_EXCLUSIVE | LK_NOWAIT | LK_INTERLOCK, td); 86139678Sbde if (error) { 862122091Skan MNT_ILOCK(mp); 863154152Stegge if (error == ENOENT) { 864154152Stegge MNT_VNODE_FOREACH_ABORT_ILOCKED(mp, mvp); 86539678Sbde goto loop; 866154152Stegge } 86739678Sbde continue; 86839678Sbde } 869140048Sphk if ((error = VOP_FSYNC(vp, waitfor, td)) != 0) 87012115Sdyson allerror = error; 871175294Sattilio VOP_UNLOCK(vp, 0); 872121874Skan vrele(vp); 873122091Skan MNT_ILOCK(mp); 87412115Sdyson } 875122091Skan MNT_IUNLOCK(mp); 876193382Sstas 87712115Sdyson /* 87812115Sdyson * Force stale file system control information to be flushed. 87912115Sdyson */ 88039678Sbde if (waitfor != MNT_LAZY) { 881175202Sattilio vn_lock(ump->um_devvp, LK_EXCLUSIVE | LK_RETRY); 882140048Sphk if ((error = VOP_FSYNC(ump->um_devvp, waitfor, td)) != 0) 88339678Sbde allerror = error; 884175294Sattilio VOP_UNLOCK(ump->um_devvp, 0); 88539678Sbde } 886193382Sstas 88739678Sbde /* 88839678Sbde * Write back modified superblock. 88939678Sbde */ 890202283Slulf if (fs->e2fs_fmod != 0) { 891202283Slulf fs->e2fs_fmod = 0; 892202283Slulf fs->e2fs->e2fs_wtime = time_second; 893202283Slulf if ((error = ext2_cgupdate(ump, waitfor)) != 0) 89439678Sbde allerror = error; 89539678Sbde } 89612115Sdyson return (allerror); 89712115Sdyson} 89812115Sdyson 89912115Sdyson/* 900108533Sschweikh * Look up an EXT2FS dinode number to find its incore vnode, otherwise read it 90112115Sdyson * in from disk. If it is in core, wait for the lock bit to clear, then 90212115Sdyson * return the inode locked. Detection and handling of mount points must be 90312115Sdyson * done by the calling routine. 90412115Sdyson */ 90512911Sphkstatic int 906193382Sstasext2_vget(struct mount *mp, ino_t ino, int flags, struct vnode **vpp) 90712115Sdyson{ 908202283Slulf struct m_ext2fs *fs; 90996752Siedowse struct inode *ip; 91096749Siedowse struct ext2mount *ump; 91112115Sdyson struct buf *bp; 91212115Sdyson struct vnode *vp; 913130585Sphk struct cdev *dev; 914193382Sstas struct thread *td; 91530280Sphk int i, error; 91612115Sdyson int used_blocks; 91712115Sdyson 918167497Stegge td = curthread; 919167497Stegge error = vfs_hash_get(mp, ino, flags, td, vpp, NULL, NULL); 920143619Sphk if (error || *vpp != NULL) 92192462Smckusick return (error); 92212115Sdyson 923143578Sphk ump = VFSTOEXT2(mp); 924143578Sphk dev = ump->um_dev; 92512406Sdyson 92636102Sbde /* 927184205Sdes * If this malloc() is performed after the getnewvnode() 92836102Sbde * it might block, leaving a vnode with a NULL v_data to be 92936102Sbde * found by ext2_sync() if a sync happens to fire right then, 93036102Sbde * which will cause a panic because ext2_sync() blindly 93136102Sbde * dereferences vp->v_data (as well it should). 93236102Sbde */ 933143578Sphk ip = malloc(sizeof(struct inode), M_EXT2NODE, M_WAITOK | M_ZERO); 93436102Sbde 93512115Sdyson /* Allocate a new vnode/inode. */ 936138290Sphk if ((error = getnewvnode("ext2fs", mp, &ext2_vnodeops, &vp)) != 0) { 93712115Sdyson *vpp = NULL; 938143578Sphk free(ip, M_EXT2NODE); 93912115Sdyson return (error); 94012115Sdyson } 94112115Sdyson vp->v_data = ip; 94212115Sdyson ip->i_vnode = vp; 94312115Sdyson ip->i_e2fs = fs = ump->um_e2fs; 944202283Slulf ip->i_ump = ump; 94512115Sdyson ip->i_number = ino; 946143578Sphk 947175635Sattilio lockmgr(vp->v_vnlock, LK_EXCLUSIVE, NULL); 948167497Stegge error = insmntque(vp, mp); 949167497Stegge if (error != 0) { 950167497Stegge free(ip, M_EXT2NODE); 951167497Stegge *vpp = NULL; 952167497Stegge return (error); 953167497Stegge } 954167497Stegge error = vfs_hash_insert(vp, ino, flags, td, vpp, NULL, NULL); 955143663Sphk if (error || *vpp != NULL) 956143578Sphk return (error); 95712406Sdyson 95812115Sdyson /* Read in the disk contents for the inode, copy into the inode. */ 95943301Sdillon if ((error = bread(ump->um_devvp, fsbtodb(fs, ino_to_fsba(fs, ino)), 960202283Slulf (int)fs->e2fs_bsize, NOCRED, &bp)) != 0) { 96112115Sdyson /* 96212115Sdyson * The inode does not contain anything useful, so it would 96312115Sdyson * be misleading to leave it on its hash chain. With mode 96412115Sdyson * still zero, it will be unlinked and returned to the free 96512115Sdyson * list by vput(). 96612115Sdyson */ 967202283Slulf brelse(bp); 96812115Sdyson vput(vp); 96912115Sdyson *vpp = NULL; 97012115Sdyson return (error); 97112115Sdyson } 97212115Sdyson /* convert ext2 inode to dinode */ 973202283Slulf ext2_ei2i((struct ext2fs_dinode *) ((char *)bp->b_data + EXT2_INODE_SIZE(fs) * 97496749Siedowse ino_to_fsbo(fs, ino)), ip); 97512115Sdyson ip->i_block_group = ino_to_cg(fs, ino); 97612115Sdyson ip->i_next_alloc_block = 0; 97712115Sdyson ip->i_next_alloc_goal = 0; 978193382Sstas 979193382Sstas /* 980193382Sstas * Now we want to make sure that block pointers for unused 981193382Sstas * blocks are zeroed out - ext2_balloc depends on this 982193382Sstas * although for regular files and directories only 983193382Sstas */ 98412115Sdyson if(S_ISDIR(ip->i_mode) || S_ISREG(ip->i_mode)) { 985202283Slulf used_blocks = (ip->i_size+fs->e2fs_bsize-1) / fs->e2fs_bsize; 986228583Spfg for (i = used_blocks; i < EXT2_NDIR_BLOCKS; i++) 98712115Sdyson ip->i_db[i] = 0; 98812115Sdyson } 98912115Sdyson/* 99012115Sdyson ext2_print_inode(ip); 99112115Sdyson*/ 992202283Slulf bqrelse(bp); 99312115Sdyson 99412115Sdyson /* 99512115Sdyson * Initialize the vnode from the inode, check for aliases. 99612115Sdyson * Note that the underlying vnode may have changed. 99712115Sdyson */ 998138290Sphk if ((error = ext2_vinit(mp, &ext2_fifoops, &vp)) != 0) { 99912115Sdyson vput(vp); 100012115Sdyson *vpp = NULL; 100112115Sdyson return (error); 100212115Sdyson } 1003193382Sstas 100412115Sdyson /* 1005217582Sjhb * Finish inode initialization. 100612115Sdyson */ 1007193382Sstas 100812115Sdyson /* 100912115Sdyson * Set up a generation number for this inode if it does not 101012115Sdyson * already have one. This should only happen on old filesystems. 101112115Sdyson */ 101212115Sdyson if (ip->i_gen == 0) { 101331485Sbde ip->i_gen = random() / 2 + 1; 101412115Sdyson if ((vp->v_mount->mnt_flag & MNT_RDONLY) == 0) 101512115Sdyson ip->i_flag |= IN_MODIFIED; 101612115Sdyson } 101712115Sdyson *vpp = vp; 101812115Sdyson return (0); 101912115Sdyson} 102012115Sdyson 102112115Sdyson/* 102212115Sdyson * File handle to vnode 102312115Sdyson * 102412115Sdyson * Have to be really careful about stale file handles: 102512115Sdyson * - check that the inode number is valid 102612115Sdyson * - call ext2_vget() to get the locked inode 102712115Sdyson * - check for an unallocated inode (i_mode == 0) 102812115Sdyson * - check that the given client host has export rights and return 102912115Sdyson * those rights via. exflagsp and credanonp 103012115Sdyson */ 103112911Sphkstatic int 1032222167Srmacklemext2_fhtovp(struct mount *mp, struct fid *fhp, int flags, struct vnode **vpp) 103312115Sdyson{ 103496749Siedowse struct inode *ip; 103596752Siedowse struct ufid *ufhp; 103696749Siedowse struct vnode *nvp; 1037202283Slulf struct m_ext2fs *fs; 103896749Siedowse int error; 103912115Sdyson 104012115Sdyson ufhp = (struct ufid *)fhp; 104196749Siedowse fs = VFSTOEXT2(mp)->um_e2fs; 1042221128Sjhb if (ufhp->ufid_ino < EXT2_ROOTINO || 1043202283Slulf ufhp->ufid_ino > fs->e2fs_gcount * fs->e2fs->e2fs_ipg) 104412115Sdyson return (ESTALE); 104596749Siedowse 104696749Siedowse error = VFS_VGET(mp, ufhp->ufid_ino, LK_EXCLUSIVE, &nvp); 104796749Siedowse if (error) { 104896749Siedowse *vpp = NULLVP; 104996749Siedowse return (error); 105096749Siedowse } 105196749Siedowse ip = VTOI(nvp); 105296749Siedowse if (ip->i_mode == 0 || 105396749Siedowse ip->i_gen != ufhp->ufid_gen || ip->i_nlink <= 0) { 105496749Siedowse vput(nvp); 105596749Siedowse *vpp = NULLVP; 105696749Siedowse return (ESTALE); 105796749Siedowse } 105896749Siedowse *vpp = nvp; 1059140768Sphk vnode_create_vobject(*vpp, 0, curthread); 106096749Siedowse return (0); 106112115Sdyson} 106212115Sdyson 106312115Sdyson/* 106412115Sdyson * Write a superblock and associated information back to disk. 106512115Sdyson */ 106612911Sphkstatic int 1067193382Sstasext2_sbupdate(struct ext2mount *mp, int waitfor) 106812115Sdyson{ 1069202283Slulf struct m_ext2fs *fs = mp->um_e2fs; 1070202283Slulf struct ext2fs *es = fs->e2fs; 107196752Siedowse struct buf *bp; 107241591Sarchie int error = 0; 1073193382Sstas 1074111856Sjeff bp = getblk(mp->um_devvp, SBLOCK, SBSIZE, 0, 0, 0); 1075202283Slulf bcopy((caddr_t)es, bp->b_data, (u_int)sizeof(struct ext2fs)); 107612115Sdyson if (waitfor == MNT_WAIT) 107712115Sdyson error = bwrite(bp); 107812115Sdyson else 107912115Sdyson bawrite(bp); 108012115Sdyson 108127881Sdyson /* 108227881Sdyson * The buffers for group descriptors, inode bitmaps and block bitmaps 108327881Sdyson * are not busy at this point and are (hopefully) written by the 1084193382Sstas * usual sync mechanism. No need to write them here. 1085193382Sstas */ 108612115Sdyson return (error); 108712115Sdyson} 1088202283Slulfint 1089202283Slulfext2_cgupdate(struct ext2mount *mp, int waitfor) 1090202283Slulf{ 1091202283Slulf struct m_ext2fs *fs = mp->um_e2fs; 1092202283Slulf struct buf *bp; 1093202283Slulf int i, error = 0, allerror = 0; 109496749Siedowse 1095202283Slulf allerror = ext2_sbupdate(mp, waitfor); 1096202283Slulf for (i = 0; i < fs->e2fs_gdbcount; i++) { 1097202283Slulf bp = getblk(mp->um_devvp, fsbtodb(fs, 1098202283Slulf fs->e2fs->e2fs_first_dblock + 1099202283Slulf 1 /* superblock */ + i), fs->e2fs_bsize, 0, 0, 0); 1100202283Slulf e2fs_cgsave(&fs->e2fs_gd[ 1101202283Slulf i * fs->e2fs_bsize / sizeof(struct ext2_gd)], 1102202283Slulf (struct ext2_gd *)bp->b_data, fs->e2fs_bsize); 1103202283Slulf if (waitfor == MNT_WAIT) 1104202283Slulf error = bwrite(bp); 1105202283Slulf else 1106202283Slulf bawrite(bp); 1107202283Slulf } 1108202283Slulf 1109202283Slulf if (!allerror && error) 1110202283Slulf allerror = error; 1111202283Slulf return (allerror); 1112202283Slulf} 111396749Siedowse/* 111496749Siedowse * Return the root of a filesystem. 111596749Siedowse */ 111696749Siedowsestatic int 1117193382Sstasext2_root(struct mount *mp, int flags, struct vnode **vpp) 111896749Siedowse{ 111996749Siedowse struct vnode *nvp; 112096749Siedowse int error; 112196749Siedowse 1122221128Sjhb error = VFS_VGET(mp, EXT2_ROOTINO, LK_EXCLUSIVE, &nvp); 112396749Siedowse if (error) 112496749Siedowse return (error); 112596749Siedowse *vpp = nvp; 112696749Siedowse return (0); 112796749Siedowse} 1128