]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/ufs/ffs/ffs_vfsops.c
xnu-344.21.73.tar.gz
[apple/xnu.git] / bsd / ufs / ffs / ffs_vfsops.c
index 10c78d5fb8e31730e74558fe569eec4682602a9e..6e58add473a5ff8cd33639a48db7b15ec0cf9c3b 100644 (file)
@@ -3,19 +3,22 @@
  *
  * @APPLE_LICENSE_HEADER_START@
  * 
- * The contents of this file constitute Original Code as defined in and
- * are subject to the Apple Public Source License Version 1.1 (the
- * "License").  You may not use this file except in compliance with the
- * License.  Please obtain a copy of the License at
- * http://www.apple.com/publicsource and read it before using this file.
+ * Copyright (c) 1999-2003 Apple Computer, Inc.  All Rights Reserved.
  * 
- * This Original Code and all software distributed under the License are
- * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
- * License for the specific language governing rights and limitations
- * under the License.
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
  * 
  * @APPLE_LICENSE_HEADER_END@
  */
@@ -72,6 +75,7 @@
 #include <sys/errno.h>
 #include <sys/malloc.h>
 #include <sys/ubc.h>
+#include <sys/quota.h>
 
 #include <miscfs/specfs/specdev.h>
 
@@ -126,8 +130,10 @@ ffs_mountroot()
                printf("ffs_mountroot: can't setup bdevvp");
                return (error);
        }
-       if (error = vfs_rootmountalloc("ufs", "root_device", &mp))
+       if (error = vfs_rootmountalloc("ufs", "root_device", &mp)) {
+               vrele(rootvp); /* release the reference from bdevvp() */
                return (error);
+       }
 
        /* Must set the MNT_ROOTFS flag before doing the actual mount */
        mp->mnt_flag |= MNT_ROOTFS;
@@ -135,6 +141,7 @@ ffs_mountroot()
        if (error = ffs_mountfs(rootvp, mp, p)) {
                mp->mnt_vfc->vfc_refcount--;
                vfs_unbusy(mp, p);
+               vrele(rootvp); /* release the reference from bdevvp() */
                _FREE_ZONE(mp, sizeof (struct mount), M_MOUNT);
                return (error);
        }
@@ -325,10 +332,11 @@ ffs_reload(mountp, cred, p)
 {
        register struct vnode *vp, *nvp, *devvp;
        struct inode *ip;
-       struct csum *space;
+       void *space;
        struct buf *bp;
        struct fs *fs, *newfs;
        int i, blks, size, error;
+       u_int64_t maxfilesize;                                  /* XXX */
        int32_t *lp;
 #if REV_ENDIAN_FS
        int rev_endian = (mountp->mnt_flag & MNT_REVEND);
@@ -373,7 +381,7 @@ ffs_reload(mountp, cred, p)
         * new superblock. These should really be in the ufsmount.      XXX
         * Note that important parameters (eg fs_ncg) are unchanged.
         */
-       bcopy(&fs->fs_csp[0], &newfs->fs_csp[0], sizeof(fs->fs_csp));
+       newfs->fs_csp = fs->fs_csp;
        newfs->fs_maxcluster = fs->fs_maxcluster;
        bcopy(newfs, fs, (u_int)fs->fs_sbsize);
        if (fs->fs_sbsize < SBSIZE)
@@ -385,11 +393,14 @@ ffs_reload(mountp, cred, p)
        brelse(bp);
        mountp->mnt_maxsymlinklen = fs->fs_maxsymlinklen;
        ffs_oldfscompat(fs);
+       maxfilesize = (u_int64_t)0x100000000;                  /* 4GB */
+       if (fs->fs_maxfilesize > maxfilesize)                   /* XXX */
+               fs->fs_maxfilesize = maxfilesize;               /* XXX */
        /*
         * Step 3: re-read summary information from disk.
         */
        blks = howmany(fs->fs_cssize, fs->fs_fsize);
-       space = fs->fs_csp[0];
+       space = fs->fs_csp;
        for (i = 0; i < blks; i += fs->fs_frag) {
                size = fs->fs_bsize;
                if (i + fs->fs_frag > blks)
@@ -405,7 +416,7 @@ ffs_reload(mountp, cred, p)
                        byte_swap_ints((int *)bp->b_data, size / sizeof(int));
                }
 #endif /* REV_ENDIAN_FS */
-               bcopy(bp->b_data, fs->fs_csp[fragstoblks(fs, i)], (u_int)size);
+               bcopy(bp->b_data, space, (u_int)size);
 #if REV_ENDIAN_FS
                if (rev_endian) {
                        /* csum swaps */
@@ -492,7 +503,7 @@ ffs_mountfs(devvp, mp, p)
        struct buf *cgbp;
        struct cg *cgp;
        int32_t clustersumoff;
-       caddr_t base, space;
+       void *space;
        int error, i, blks, size, ronly;
        int32_t *lp;
        struct ucred *cred;
@@ -600,6 +611,13 @@ ffs_mountfs(devvp, mp, p)
                set_fsblocksize(devvp);
         } 
 
+       /* cache the IO attributes */
+       error = vfs_init_io_attributes(devvp, mp);
+       if (error) {
+               printf("ffs_mountfs: vfs_init_io_attributes returned %d\n",
+                       error);
+               goto out;
+       }
 
        /* XXX updating 4.2 FFS superblocks trashes rotational layout tables */
        if (fs->fs_postblformat == FS_42POSTBLFMT && !ronly) {
@@ -672,15 +690,15 @@ ffs_mountfs(devvp, mp, p)
        blks = howmany(size, fs->fs_fsize);
        if (fs->fs_contigsumsize > 0)
                size += fs->fs_ncg * sizeof(int32_t);
-       base = space = _MALLOC((u_long)size, M_UFSMNT, M_WAITOK);
-       base = space;
+       space = _MALLOC((u_long)size, M_UFSMNT, M_WAITOK);
+       fs->fs_csp = space;
        for (i = 0; i < blks; i += fs->fs_frag) {
                size = fs->fs_bsize;
                if (i + fs->fs_frag > blks)
                        size = (blks - i) * fs->fs_fsize;
                if (error = bread(devvp, fsbtodb(fs, fs->fs_csaddr + i), size,
                    cred, &bp)) {
-                       _FREE(base, M_UFSMNT);
+                       _FREE(fs->fs_csp, M_UFSMNT);
                        goto out;
                }
                bcopy(bp->b_data, space, (u_int)size);
@@ -688,13 +706,12 @@ ffs_mountfs(devvp, mp, p)
                if (rev_endian)
                        byte_swap_ints((int *) space, size / sizeof(int));
 #endif /* REV_ENDIAN_FS */
-               fs->fs_csp[fragstoblks(fs, i)] = (struct csum *)space;
-               space += size;
+               space = (char *)space + size;
                brelse(bp);
                bp = NULL;
        }
        if (fs->fs_contigsumsize > 0) {
-               fs->fs_maxcluster = lp = (int32_t *)space;
+               fs->fs_maxcluster = lp = space;
                for (i = 0; i < fs->fs_ncg; i++)
                        *lp++ = fs->fs_contigsumsize;
        }
@@ -714,7 +731,7 @@ ffs_mountfs(devvp, mp, p)
        ump->um_bptrtodb = fs->fs_fsbtodb;
        ump->um_seqinc = fs->fs_frag;
        for (i = 0; i < MAXQUOTAS; i++)
-               ump->um_quotas[i] = NULLVP;
+               ump->um_qfiles[i].qf_vp = NULLVP;
        devvp->v_specflags |= SI_MOUNTEDON;
        ffs_oldfscompat(fs);
        ump->um_savedmaxfilesize = fs->fs_maxfilesize;          /* XXX */
@@ -781,10 +798,15 @@ ffs_unmount(mp, mntflags, p)
        register struct ufsmount *ump;
        register struct fs *fs;
        int error, flags;
+       int force;
+
        flags = 0;
-       if (mntflags & MNT_FORCE)
+       force = 0;
+       if (mntflags & MNT_FORCE) {
                flags |= FORCECLOSE;
-       if (error = ffs_flushfiles(mp, flags, p))
+               force = 1;
+       }
+       if ( (error = ffs_flushfiles(mp, flags, p)) && !force )
                return (error);
        ump = VFSTOUFS(mp);
        fs = ump->um_fs;
@@ -805,16 +827,18 @@ ffs_unmount(mp, mntflags, p)
        ump->um_devvp->v_specflags &= ~SI_MOUNTEDON;
        error = VOP_CLOSE(ump->um_devvp, fs->fs_ronly ? FREAD : FREAD|FWRITE,
                NOCRED, p);
+       if (error && !force)
+               return (error);
        vrele(ump->um_devvp);
 
-       _FREE(fs->fs_csp[0], M_UFSMNT);
+       _FREE(fs->fs_csp, M_UFSMNT);
        _FREE(fs, M_UFSMNT);
        _FREE(ump, M_UFSMNT);
        mp->mnt_data = (qaddr_t)0;
 #if REV_ENDIAN_FS
        mp->mnt_flag &= ~MNT_REVEND;
 #endif /* REV_ENDIAN_FS */
-       return (error);
+       return (0);
 }
 
 /*
@@ -834,7 +858,7 @@ ffs_flushfiles(mp, flags, p)
                if (error = vflush(mp, NULLVP, SKIPSYSTEM|flags))
                        return (error);
                for (i = 0; i < MAXQUOTAS; i++) {
-                       if (ump->um_quotas[i] == NULLVP)
+                       if (ump->um_qfiles[i].qf_vp == NULLVP)
                                continue;
                        quotaoff(p, mp, i);
                }
@@ -916,6 +940,8 @@ loop:
        for (vp = mp->mnt_vnodelist.lh_first;
             vp != NULL;
             vp = nvp) {
+               int didhold = 0;
+
                /*
                 * If the vnode that we are about to sync is no longer
                 * associated with this mount point, start over.
@@ -939,9 +965,12 @@ loop:
                                goto loop;
                        continue;
                }
+               didhold = ubc_hold(vp);
                if (error = VOP_FSYNC(vp, cred, waitfor, p))
                        allerror = error;
                VOP_UNLOCK(vp, 0, p);
+               if (didhold)
+                       ubc_rele(vp);
                vrele(vp);
                simple_lock(&mntvnode_slock);
        }
@@ -1004,30 +1033,58 @@ ffs_vget(mp, ino, vpp)
        /* Allocate a new vnode/inode. */
        type = ump->um_devvp->v_tag == VT_MFS ? M_MFSNODE : M_FFSNODE; /* XXX */
        MALLOC_ZONE(ip, struct inode *, sizeof(struct inode), type, M_WAITOK);
-       if (error = getnewvnode(VT_UFS, mp, ffs_vnodeop_p, &vp)) {
-               FREE_ZONE(ip, sizeof(struct inode), type);
-               *vpp = NULL;
-               return (error);
-       }
        bzero((caddr_t)ip, sizeof(struct inode));
        lockinit(&ip->i_lock, PINOD, "inode", 0, 0);
-       vp->v_data = ip;
-       ip->i_vnode = vp;
+       /* lock the inode */
+       lockmgr(&ip->i_lock, LK_EXCLUSIVE, (struct slock *)0, p);
+
        ip->i_fs = fs = ump->um_fs;
        ip->i_dev = dev;
        ip->i_number = ino;
+       ip->i_flag |= IN_ALLOC;
 #if QUOTA
        for (i = 0; i < MAXQUOTAS; i++)
                ip->i_dquot[i] = NODQUOT;
 #endif
+
+       /*
+        * MALLOC_ZONE is blocking call. Check for race.
+        */
+       if ((*vpp = ufs_ihashget(dev, ino)) != NULL) {
+               /* Clean up */
+               FREE_ZONE(ip, sizeof(struct inode), type);
+               vp = *vpp;
+               UBCINFOCHECK("ffs_vget", vp);
+               return (0);
+       }
+
        /*
-        * Put it onto its hash chain and lock it so that other requests for
+        * Put it onto its hash chain locked so that other requests for
         * this inode will block if they arrive while we are sleeping waiting
         * for old data structures to be purged or for the contents of the
         * disk portion of this inode to be read.
         */
        ufs_ihashins(ip);
 
+       if (error = getnewvnode(VT_UFS, mp, ffs_vnodeop_p, &vp)) {
+               ufs_ihashrem(ip);
+               if (ISSET(ip->i_flag, IN_WALLOC))
+                       wakeup(ip);
+               FREE_ZONE(ip, sizeof(struct inode), type);
+               *vpp = NULL;
+               return (error);
+       }
+       vp->v_data = ip;
+       ip->i_vnode = vp;
+
+       /*
+        * A vnode is associated with the inode now,
+        * vget() can deal with the serialization.
+        */
+       CLR(ip->i_flag, IN_ALLOC);
+       if (ISSET(ip->i_flag, IN_WALLOC))
+               wakeup(ip);
+
        /* Read in the disk contents for the inode, copy into the inode. */
        if (error = bread(ump->um_devvp, fsbtodb(fs, ino_to_fsba(fs, ino)),
            (int)fs->fs_bsize, NOCRED, &bp)) {
@@ -1200,7 +1257,7 @@ ffs_sbupdate(mp, waitfor)
        register struct fs *dfs, *fs = mp->um_fs;
        register struct buf *bp;
        int blks;
-       caddr_t space;
+       void *space;
        int i, size, error, allerror = 0;
        int devBlockSize=0;
 #if REV_ENDIAN_FS
@@ -1211,7 +1268,7 @@ ffs_sbupdate(mp, waitfor)
         * First write back the summary information.
         */
        blks = howmany(fs->fs_cssize, fs->fs_fsize);
-       space = (caddr_t)fs->fs_csp[0];
+       space = fs->fs_csp;
        for (i = 0; i < blks; i += fs->fs_frag) {
                size = fs->fs_bsize;
                if (i + fs->fs_frag > blks)
@@ -1224,7 +1281,7 @@ ffs_sbupdate(mp, waitfor)
                        byte_swap_ints((int *)bp->b_data, size / sizeof(int));
                }
 #endif /* REV_ENDIAN_FS */
-               space += size;
+               space = (char *)space + size;
                if (waitfor != MNT_WAIT)
                        bawrite(bp);
                else if (error = bwrite(bp))