Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 22 Aug 2010 05:36:07 +0000 (UTC)
From:      Ed Schouten <ed@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r211598 - head/sys/fs/tmpfs
Message-ID:  <201008220536.o7M5a7D1092448@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
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,
 };
 



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201008220536.o7M5a7D1092448>