Author: ed
Date: Sun Aug 22 05:36:06 2010
New Revision: 211598
URL: http://svn.freebsd.org/changeset/base/211598

Log:
  Add support for whiteouts on tmpfs.
  
  Right now unionfs only allows filesystems to be mounted on top of
  another if it supports whiteouts. Even though I have sent a patch to
  daichi@ to let unionfs work without it, we'd better also add support for
  whiteouts to tmpfs.
  
  This patch implements .vop_whiteout and makes necessary changes to
  lookup() and readdir() to take them into account. We must also make sure
  that when adding or removing a file, we honour the componentname's
  DOWHITEOUT and ISWHITEOUT, to prevent duplicate filenames.
  
  MFC after:    1 month

Modified:
  head/sys/fs/tmpfs/tmpfs.h
  head/sys/fs/tmpfs/tmpfs_subr.c
  head/sys/fs/tmpfs/tmpfs_vnops.c

Modified: head/sys/fs/tmpfs/tmpfs.h
==============================================================================
--- head/sys/fs/tmpfs/tmpfs.h   Sun Aug 22 01:40:59 2010        (r211597)
+++ head/sys/fs/tmpfs/tmpfs.h   Sun Aug 22 05:36:06 2010        (r211598)
@@ -72,7 +72,8 @@ struct tmpfs_dirent {
        * td_namelen field must always be used when accessing its value. */
        char *                          td_name;
 
-       /* Pointer to the node this entry refers to. */
+       /* Pointer to the node this entry refers to.  In case this field
+        * is NULL, the node is a whiteout. */
        struct tmpfs_node *             td_node;
 };
 
@@ -434,6 +435,8 @@ int tmpfs_dir_getdotdent(struct tmpfs_no
 int    tmpfs_dir_getdotdotdent(struct tmpfs_node *, struct uio *);
 struct tmpfs_dirent *  tmpfs_dir_lookupbycookie(struct tmpfs_node *, off_t);
 int    tmpfs_dir_getdents(struct tmpfs_node *, struct uio *, off_t *);
+int    tmpfs_dir_whiteout_add(struct vnode *, struct componentname *);
+void   tmpfs_dir_whiteout_remove(struct vnode *, struct componentname *);
 int    tmpfs_reg_resize(struct vnode *, off_t);
 int    tmpfs_chflags(struct vnode *, int, struct ucred *, struct thread *);
 int    tmpfs_chmod(struct vnode *, mode_t, struct ucred *, struct thread *);

Modified: head/sys/fs/tmpfs/tmpfs_subr.c
==============================================================================
--- head/sys/fs/tmpfs/tmpfs_subr.c      Sun Aug 22 01:40:59 2010        
(r211597)
+++ head/sys/fs/tmpfs/tmpfs_subr.c      Sun Aug 22 05:36:06 2010        
(r211598)
@@ -261,7 +261,8 @@ tmpfs_alloc_dirent(struct tmpfs_mount *t
        memcpy(nde->td_name, name, len);
 
        nde->td_node = node;
-       node->tn_links++;
+       if (node != NULL)
+               node->tn_links++;
 
        *de = nde;
 
@@ -287,9 +288,10 @@ tmpfs_free_dirent(struct tmpfs_mount *tm
                struct tmpfs_node *node;
 
                node = de->td_node;
-
-               MPASS(node->tn_links > 0);
-               node->tn_links--;
+               if (node != NULL) {
+                       MPASS(node->tn_links > 0);
+                       node->tn_links--;
+               }
        }
 
        free(de->td_name, M_TMPFSNAME);
@@ -518,6 +520,8 @@ tmpfs_alloc_file(struct vnode *dvp, stru
        /* Now that all required items are allocated, we can proceed to
         * insert the new node into the directory, an operation that
         * cannot fail. */
+       if (cnp->cn_flags & ISWHITEOUT)
+               tmpfs_dir_whiteout_remove(dvp, cnp);
        tmpfs_dir_attach(dvp, de);
 
 out:
@@ -768,39 +772,44 @@ tmpfs_dir_getdents(struct tmpfs_node *no
 
                /* Create a dirent structure representing the current
                 * tmpfs_node and fill it. */
-               d.d_fileno = de->td_node->tn_id;
-               switch (de->td_node->tn_type) {
-               case VBLK:
-                       d.d_type = DT_BLK;
-                       break;
-
-               case VCHR:
-                       d.d_type = DT_CHR;
-                       break;
-
-               case VDIR:
-                       d.d_type = DT_DIR;
-                       break;
-
-               case VFIFO:
-                       d.d_type = DT_FIFO;
-                       break;
-
-               case VLNK:
-                       d.d_type = DT_LNK;
-                       break;
-
-               case VREG:
-                       d.d_type = DT_REG;
-                       break;
-
-               case VSOCK:
-                       d.d_type = DT_SOCK;
-                       break;
-
-               default:
-                       panic("tmpfs_dir_getdents: type %p %d",
-                           de->td_node, (int)de->td_node->tn_type);
+               if (de->td_node == NULL) {
+                       d.d_fileno = 1;
+                       d.d_type = DT_WHT;
+               } else {
+                       d.d_fileno = de->td_node->tn_id;
+                       switch (de->td_node->tn_type) {
+                       case VBLK:
+                               d.d_type = DT_BLK;
+                               break;
+
+                       case VCHR:
+                               d.d_type = DT_CHR;
+                               break;
+
+                       case VDIR:
+                               d.d_type = DT_DIR;
+                               break;
+
+                       case VFIFO:
+                               d.d_type = DT_FIFO;
+                               break;
+
+                       case VLNK:
+                               d.d_type = DT_LNK;
+                               break;
+
+                       case VREG:
+                               d.d_type = DT_REG;
+                               break;
+
+                       case VSOCK:
+                               d.d_type = DT_SOCK;
+                               break;
+
+                       default:
+                               panic("tmpfs_dir_getdents: type %p %d",
+                                   de->td_node, (int)de->td_node->tn_type);
+                       }
                }
                d.d_namlen = de->td_namelen;
                MPASS(de->td_namelen < sizeof(d.d_name));
@@ -837,6 +846,31 @@ tmpfs_dir_getdents(struct tmpfs_node *no
        return error;
 }
 
+int
+tmpfs_dir_whiteout_add(struct vnode *dvp, struct componentname *cnp)
+{
+       struct tmpfs_dirent *de;
+       int error;
+
+       error = tmpfs_alloc_dirent(VFS_TO_TMPFS(dvp->v_mount), NULL,
+           cnp->cn_nameptr, cnp->cn_namelen, &de);
+       if (error != 0)
+               return (error);
+       tmpfs_dir_attach(dvp, de);
+       return (0);
+}
+
+void
+tmpfs_dir_whiteout_remove(struct vnode *dvp, struct componentname *cnp)
+{
+       struct tmpfs_dirent *de;
+
+       de = tmpfs_dir_lookup(VP_TO_TMPFS_DIR(dvp), NULL, cnp);
+       MPASS(de != NULL && de->td_node == NULL);
+       tmpfs_dir_detach(dvp, de);
+       tmpfs_free_dirent(VFS_TO_TMPFS(dvp->v_mount), de, TRUE);
+}
+
 /* --------------------------------------------------------------------- */
 
 /*

Modified: head/sys/fs/tmpfs/tmpfs_vnops.c
==============================================================================
--- head/sys/fs/tmpfs/tmpfs_vnops.c     Sun Aug 22 01:40:59 2010        
(r211597)
+++ head/sys/fs/tmpfs/tmpfs_vnops.c     Sun Aug 22 05:36:06 2010        
(r211598)
@@ -109,14 +109,19 @@ tmpfs_lookup(struct vop_cachedlookup_arg
                error = 0;
        } else {
                de = tmpfs_dir_lookup(dnode, NULL, cnp);
-               if (de == NULL) {
+               if (de != NULL && de->td_node == NULL)
+                       cnp->cn_flags |= ISWHITEOUT;
+               if (de == NULL || de->td_node == NULL) {
                        /* The entry was not found in the directory.
                         * This is OK if we are creating or renaming an
                         * entry and are working on the last component of
                         * the path name. */
                        if ((cnp->cn_flags & ISLASTCN) &&
                            (cnp->cn_nameiop == CREATE || \
-                           cnp->cn_nameiop == RENAME)) {
+                           cnp->cn_nameiop == RENAME ||
+                           (cnp->cn_nameiop == DELETE &&
+                           cnp->cn_flags & DOWHITEOUT &&
+                           cnp->cn_flags & ISWHITEOUT))) {
                                error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred,
                                    cnp->cn_thread);
                                if (error != 0)
@@ -834,6 +839,8 @@ tmpfs_remove(struct vop_remove_args *v)
        /* Remove the entry from the directory; as it is a file, we do not
         * have to change the number of hard links of the directory. */
        tmpfs_dir_detach(dvp, de);
+       if (v->a_cnp->cn_flags & DOWHITEOUT)
+               tmpfs_dir_whiteout_add(dvp, v->a_cnp);
 
        /* Free the directory entry we just deleted.  Note that the node
         * referred by it will not be removed until the vnode is really
@@ -904,6 +911,8 @@ tmpfs_link(struct vop_link_args *v)
                goto out;
 
        /* Insert the new directory entry into the appropriate directory. */
+       if (cnp->cn_flags & ISWHITEOUT)
+               tmpfs_dir_whiteout_remove(dvp, cnp);
        tmpfs_dir_attach(dvp, de);
 
        /* vp link count has changed, so update node times. */
@@ -1099,6 +1108,10 @@ tmpfs_rename(struct vop_rename_args *v)
                /* Do the move: just remove the entry from the source directory
                 * and insert it into the target one. */
                tmpfs_dir_detach(fdvp, de);
+               if (fcnp->cn_flags & DOWHITEOUT)
+                       tmpfs_dir_whiteout_add(fdvp, fcnp);
+               if (tcnp->cn_flags & ISWHITEOUT)
+                       tmpfs_dir_whiteout_remove(tdvp, tcnp);
                tmpfs_dir_attach(tdvp, de);
        }
 
@@ -1223,6 +1236,8 @@ tmpfs_rmdir(struct vop_rmdir_args *v)
 
        /* Detach the directory entry from the directory (dnode). */
        tmpfs_dir_detach(dvp, de);
+       if (v->a_cnp->cn_flags & DOWHITEOUT)
+               tmpfs_dir_whiteout_add(dvp, v->a_cnp);
 
        /* No vnode should be allocated for this entry from this point */
        TMPFS_NODE_LOCK(node);
@@ -1541,6 +1556,29 @@ tmpfs_vptofh(struct vop_vptofh_args *ap)
        return (0);
 }
 
+static int
+tmpfs_whiteout(struct vop_whiteout_args *ap)
+{
+       struct vnode *dvp = ap->a_dvp;
+       struct componentname *cnp = ap->a_cnp;
+       struct tmpfs_dirent *de;
+
+       switch (ap->a_flags) {
+       case LOOKUP:
+               return (0);
+       case CREATE:
+               de = tmpfs_dir_lookup(VP_TO_TMPFS_DIR(dvp), NULL, cnp);
+               if (de != NULL)
+                       return (de->td_node == NULL ? 0 : EEXIST);
+               return (tmpfs_dir_whiteout_add(dvp, cnp));
+       case DELETE:
+               tmpfs_dir_whiteout_remove(dvp, cnp);
+               return (0);
+       default:
+               panic("tmpfs_whiteout: unknown op");
+       }
+}
+
 /* --------------------------------------------------------------------- */
 
 /*
@@ -1573,6 +1611,7 @@ struct vop_vector tmpfs_vnodeop_entries 
        .vop_print =                    tmpfs_print,
        .vop_pathconf =                 tmpfs_pathconf,
        .vop_vptofh =                   tmpfs_vptofh,
+       .vop_whiteout =                 tmpfs_whiteout,
        .vop_bmap =                     VOP_EOPNOTSUPP,
 };
 
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to