From owner-svn-src-all@FreeBSD.ORG Sun Aug 22 05:36:07 2010 Return-Path: Delivered-To: svn-src-all@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 486391065673; Sun, 22 Aug 2010 05:36:07 +0000 (UTC) (envelope-from ed@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:4f8:fff6::2c]) by mx1.freebsd.org (Postfix) with ESMTP id 379D58FC0A; Sun, 22 Aug 2010 05:36:07 +0000 (UTC) Received: from svn.freebsd.org (localhost [127.0.0.1]) by svn.freebsd.org (8.14.3/8.14.3) with ESMTP id o7M5a7kJ092452; Sun, 22 Aug 2010 05:36:07 GMT (envelope-from ed@svn.freebsd.org) Received: (from ed@localhost) by svn.freebsd.org (8.14.3/8.14.3/Submit) id o7M5a7D1092448; Sun, 22 Aug 2010 05:36:07 GMT (envelope-from ed@svn.freebsd.org) Message-Id: <201008220536.o7M5a7D1092448@svn.freebsd.org> From: Ed Schouten Date: Sun, 22 Aug 2010 05:36:07 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Subject: svn commit: r211598 - head/sys/fs/tmpfs X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 22 Aug 2010 05:36:07 -0000 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, };