VFS accounting: check if nullfs mp* really exist
authorFrancois Tigeot <ftigeot@wolfpond.org>
Fri, 17 Feb 2012 22:58:58 +0000 (23:58 +0100)
committerFran├žois Tigeot <ftigeot@wolfpond.org>
Sat, 18 Feb 2012 09:12:11 +0000 (10:12 +0100)
* Nullfs are a clever hack, they forget their mp after mount and let the
  non-nullfs underlying mount point do all the work

* Nevertheless, we need to get the real struct mount associated to a mount
  point to count the space used by PFSes. We cache it in the vnode.

* But since the vnode we use really belongs to the lowest underlying
  mount point, the upper-level mp may already have been freed.

* We need to first check if it still valid; a new function,
  mountlist_exists() now exists for that purpose.

* Should fix issue #2266

sys/kern/vfs_mount.c
sys/kern/vfs_vopops.c
sys/sys/mount.h

index ad2dfdb..18c83fe 100644 (file)
@@ -845,6 +845,29 @@ mountlist_remove(struct mount *mp)
        lwkt_reltoken(&mountlist_token);
 }
 
+/*
+ * mountlist_exists (MP SAFE)
+ *
+ * Checks if a node exists in the mountlist.
+ * This function is mainly used by VFS accounting code to check if a
+ * cached nullfs struct mount pointer is still valid at use time
+ */
+int
+mountlist_exists(struct mount *mp)
+{
+       int node_exists = 0;
+       struct mountscan_info *msi;
+
+       lwkt_gettoken(&mountlist_token);
+       TAILQ_FOREACH(msi, &mountscan_list, msi_entry) {
+               if (msi->msi_node == mp) {
+                       node_exists = 1;
+               }
+       }
+       lwkt_reltoken(&mountlist_token);
+       return(node_exists);
+}
+
 /*
  * mountlist_scan (MP SAFE)
  *
index f6c893e..dd156ca 100644 (file)
@@ -439,7 +439,10 @@ vop_write(struct vop_ops *ops, struct vnode *vp, struct uio *uio, int ioflag,
        if ((error == 0) && do_accounting) {
                size_after = vp->v_filesize;
                /* does this vnode belong to a pfs/nullfs mount ? */
-               if (vp->v_pfsmp != NULL) {
+               /* XXX: vp->v_pfsmp may point to a freed structure
+               * we use mountlist_exists() to check if it is valid
+               * before using it */
+               if ((vp->v_pfsmp != NULL) && (mountlist_exists(vp->v_pfsmp))) {
                        /* yes, use a copy of the real mp */
                        mp = vp->v_pfsmp;
                } else {
index 30c0760..5eadb1e 100644 (file)
@@ -752,6 +752,7 @@ void        mountlist_insert(struct mount *, int);
 int    mountlist_interlock(int (*callback)(struct mount *), struct mount *);
 struct mount *mountlist_boot_getfirst(void);
 void   mountlist_remove(struct mount *mp);
+int    mountlist_exists(struct mount *mp);
 int    mountlist_scan(int (*callback)(struct mount *, void *), void *, int);
 struct mount *mount_get_by_nc(struct namecache *ncp);
 #else /* !_KERNEL */