Module Name: src Committed By: rmind Date: Sat Nov 23 16:35:32 UTC 2013
Modified Files: src/sys/fs/tmpfs: tmpfs.h tmpfs_fifoops.c tmpfs_rename.c tmpfs_specops.c tmpfs_subr.c tmpfs_vfsops.c tmpfs_vnops.c Log Message: - Simplify tmpfs_update(), eliminate tmpfs_note_t::tn_status and deferred timestamp updates. Fix some incorrect updates and plug some missing ones. Should fix PR/48385. - tmpfs_rmdir: avoid O(n) scan when the directory is not empty and whiteout entries were never added. To generate a diff of this commit: cvs rdiff -u -r1.47 -r1.48 src/sys/fs/tmpfs/tmpfs.h cvs rdiff -u -r1.9 -r1.10 src/sys/fs/tmpfs/tmpfs_fifoops.c cvs rdiff -u -r1.5 -r1.6 src/sys/fs/tmpfs/tmpfs_rename.c cvs rdiff -u -r1.10 -r1.11 src/sys/fs/tmpfs/tmpfs_specops.c cvs rdiff -u -r1.89 -r1.90 src/sys/fs/tmpfs/tmpfs_subr.c cvs rdiff -u -r1.54 -r1.55 src/sys/fs/tmpfs/tmpfs_vfsops.c cvs rdiff -u -r1.106 -r1.107 src/sys/fs/tmpfs/tmpfs_vnops.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/fs/tmpfs/tmpfs.h diff -u src/sys/fs/tmpfs/tmpfs.h:1.47 src/sys/fs/tmpfs/tmpfs.h:1.48 --- src/sys/fs/tmpfs/tmpfs.h:1.47 Mon Nov 18 01:39:34 2013 +++ src/sys/fs/tmpfs/tmpfs.h Sat Nov 23 16:35:32 2013 @@ -1,4 +1,4 @@ -/* $NetBSD: tmpfs.h,v 1.47 2013/11/18 01:39:34 rmind Exp $ */ +/* $NetBSD: tmpfs.h,v 1.48 2013/11/23 16:35:32 rmind Exp $ */ /* * Copyright (c) 2005, 2006, 2007 The NetBSD Foundation, Inc. @@ -101,9 +101,6 @@ typedef struct tmpfs_node { ino_t tn_id; uint32_t tn_gen; - /* Inode status flags (for operations in delayed manner). */ - unsigned tn_status; - /* The inode size. */ off_t tn_size; @@ -183,20 +180,18 @@ CTASSERT(TMPFS_MAXNAMLEN < UINT16_MAX); /* Mark to indicate that the number is not set. */ #define TMPFS_DIRSEQ_NONE (1U << 31) -/* Status flags. */ -#define TMPFS_NODE_ACCESSED 0x01 -#define TMPFS_NODE_MODIFIED 0x02 -#define TMPFS_NODE_CHANGED 0x04 - -#define TMPFS_NODE_STATUSALL \ - (TMPFS_NODE_ACCESSED | TMPFS_NODE_MODIFIED | TMPFS_NODE_CHANGED) +/* Flags: time update requests. */ +#define TMPFS_UPDATE_ATIME 0x01 +#define TMPFS_UPDATE_MTIME 0x02 +#define TMPFS_UPDATE_CTIME 0x04 /* - * Bit indicating vnode reclamation. + * Bits indicating vnode reclamation and whiteout use for the directory. * We abuse tmpfs_node_t::tn_gen for that. */ #define TMPFS_RECLAIMING_BIT (1U << 31) -#define TMPFS_NODE_GEN_MASK (TMPFS_RECLAIMING_BIT - 1) +#define TMPFS_WHITEOUT_BIT (1U << 30) +#define TMPFS_NODE_GEN_MASK (TMPFS_WHITEOUT_BIT - 1) #define TMPFS_NODE_RECLAIMING(node) \ (((node)->tn_gen & TMPFS_RECLAIMING_BIT) != 0) @@ -249,7 +244,7 @@ int tmpfs_alloc_node(tmpfs_mount_t *, e mode_t, char *, dev_t, tmpfs_node_t **); void tmpfs_free_node(tmpfs_mount_t *, tmpfs_node_t *); -int tmpfs_alloc_file(vnode_t *, vnode_t **, struct vattr *, +int tmpfs_construct_node(vnode_t *, vnode_t **, struct vattr *, struct componentname *, char *); int tmpfs_vnode_get(struct mount *, tmpfs_node_t *, vnode_t **); @@ -268,7 +263,6 @@ tmpfs_dirent_t *tmpfs_dir_lookupbyseq(tm int tmpfs_dir_getdents(tmpfs_node_t *, struct uio *, off_t *); int tmpfs_reg_resize(vnode_t *, off_t); -int tmpfs_truncate(vnode_t *, off_t); int tmpfs_chflags(vnode_t *, int, kauth_cred_t, lwp_t *); int tmpfs_chmod(vnode_t *, mode_t, kauth_cred_t, lwp_t *); @@ -277,8 +271,7 @@ int tmpfs_chsize(vnode_t *, u_quad_t, k int tmpfs_chtimes(vnode_t *, const struct timespec *, const struct timespec *, const struct timespec *, int, kauth_cred_t, lwp_t *); -void tmpfs_update(vnode_t *, const struct timespec *, - const struct timespec *, const struct timespec *, int); +void tmpfs_update(vnode_t *, unsigned); /* * Prototypes for tmpfs_mem.c. Index: src/sys/fs/tmpfs/tmpfs_fifoops.c diff -u src/sys/fs/tmpfs/tmpfs_fifoops.c:1.9 src/sys/fs/tmpfs/tmpfs_fifoops.c:1.10 --- src/sys/fs/tmpfs/tmpfs_fifoops.c:1.9 Tue May 24 20:17:49 2011 +++ src/sys/fs/tmpfs/tmpfs_fifoops.c Sat Nov 23 16:35:32 2013 @@ -1,4 +1,4 @@ -/* $NetBSD: tmpfs_fifoops.c,v 1.9 2011/05/24 20:17:49 rmind Exp $ */ +/* $NetBSD: tmpfs_fifoops.c,v 1.10 2013/11/23 16:35:32 rmind Exp $ */ /* * Copyright (c) 2005 The NetBSD Foundation, Inc. @@ -35,7 +35,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: tmpfs_fifoops.c,v 1.9 2011/05/24 20:17:49 rmind Exp $"); +__KERNEL_RCSID(0, "$NetBSD: tmpfs_fifoops.c,v 1.10 2013/11/23 16:35:32 rmind Exp $"); #include <sys/param.h> #include <sys/vnode.h> @@ -103,10 +103,8 @@ tmpfs_fifo_close(void *v) struct vnode *a_vp; int a_fflag; kauth_cred_t a_cred; - } */ *ap = v; - vnode_t *vp = ap->a_vp; + } */ *ap __unused = v; - tmpfs_update(vp, NULL, NULL, NULL, UPDATE_CLOSE); return VOCALL(fifo_vnodeop_p, VOFFSET(vop_close), v); } @@ -121,7 +119,7 @@ tmpfs_fifo_read(void *v) } */ *ap = v; vnode_t *vp = ap->a_vp; - VP_TO_TMPFS_NODE(vp)->tn_status |= TMPFS_NODE_ACCESSED; + tmpfs_update(vp, TMPFS_UPDATE_ATIME); return VOCALL(fifo_vnodeop_p, VOFFSET(vop_read), v); } @@ -136,6 +134,6 @@ tmpfs_fifo_write(void *v) } */ *ap = v; vnode_t *vp = ap->a_vp; - VP_TO_TMPFS_NODE(vp)->tn_status |= TMPFS_NODE_MODIFIED; + tmpfs_update(vp, TMPFS_UPDATE_MTIME); return VOCALL(fifo_vnodeop_p, VOFFSET(vop_write), v); } Index: src/sys/fs/tmpfs/tmpfs_rename.c diff -u src/sys/fs/tmpfs/tmpfs_rename.c:1.5 src/sys/fs/tmpfs/tmpfs_rename.c:1.6 --- src/sys/fs/tmpfs/tmpfs_rename.c:1.5 Fri Nov 8 15:44:23 2013 +++ src/sys/fs/tmpfs/tmpfs_rename.c Sat Nov 23 16:35:32 2013 @@ -1,4 +1,4 @@ -/* $NetBSD: tmpfs_rename.c,v 1.5 2013/11/08 15:44:23 rmind Exp $ */ +/* $NetBSD: tmpfs_rename.c,v 1.6 2013/11/23 16:35:32 rmind Exp $ */ /*- * Copyright (c) 2012 The NetBSD Foundation, Inc. @@ -34,7 +34,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: tmpfs_rename.c,v 1.5 2013/11/08 15:44:23 rmind Exp $"); +__KERNEL_RCSID(0, "$NetBSD: tmpfs_rename.c,v 1.6 2013/11/23 16:35:32 rmind Exp $"); #include <sys/param.h> #include <sys/errno.h> @@ -266,6 +266,8 @@ tmpfs_gro_rename(struct mount *mp, kauth struct vnode *tdvp, struct componentname *tcnp, void *tde, struct vnode *tvp) { + tmpfs_node_t *fdnode = VP_TO_TMPFS_DIR(fdvp); + tmpfs_node_t *tdnode = VP_TO_TMPFS_DIR(tdvp); struct tmpfs_dirent **fdep = fde; struct tmpfs_dirent **tdep = tde; char *newname; @@ -313,9 +315,6 @@ tmpfs_gro_rename(struct mount *mp, kauth * source entry and reattach it to the target directory. */ if (fdvp != tdvp) { - tmpfs_node_t *fdnode = VP_TO_TMPFS_DIR(fdvp); - tmpfs_node_t *tdnode = VP_TO_TMPFS_DIR(tdvp); - tmpfs_dir_detach(fdnode, *fdep); tmpfs_dir_attach(tdnode, *fdep, VP_TO_TMPFS_NODE(fvp)); } else if (tvp == NULL) { @@ -334,7 +333,7 @@ tmpfs_gro_rename(struct mount *mp, kauth * XXX What if the target is a directory with whiteout entries? */ if (tvp != NULL) { - tmpfs_node_t *tdnode = VP_TO_TMPFS_DIR(tdvp); + tdnode = VP_TO_TMPFS_DIR(tdvp); KASSERT((*tdep) != NULL); KASSERT((*tdep)->td_node == VP_TO_TMPFS_NODE(tvp)); @@ -346,11 +345,6 @@ tmpfs_gro_rename(struct mount *mp, kauth /* * Decrement the extra link count for `.' so * the vnode will be recycled when released. - * - * XXX Why not just release directory vnodes - * when their link count is 1 instead of 0 in - * tmpfs_inactive, since `.' is being treated - * specially anyway? */ VP_TO_TMPFS_NODE(tvp)->tn_links--; } @@ -373,11 +367,16 @@ tmpfs_gro_rename(struct mount *mp, kauth (*fdep)->td_namelen = (uint16_t)tcnp->cn_namelen; (void)memcpy(newname, tcnp->cn_nameptr, tcnp->cn_namelen); (*fdep)->td_name = newname; - - VP_TO_TMPFS_NODE(fvp)->tn_status |= TMPFS_NODE_CHANGED; - VP_TO_TMPFS_NODE(tdvp)->tn_status |= TMPFS_NODE_MODIFIED; } + /* + * Update the timestamps of both parent directories and + * the renamed file itself. + */ + tmpfs_update(fdvp, TMPFS_UPDATE_MTIME | TMPFS_UPDATE_CTIME); + tmpfs_update(tdvp, TMPFS_UPDATE_MTIME | TMPFS_UPDATE_CTIME); + tmpfs_update(fvp, TMPFS_UPDATE_CTIME); + VN_KNOTE(fvp, NOTE_RENAME); genfs_rename_cache_purge(fdvp, fvp, tdvp, tvp); @@ -412,6 +411,7 @@ tmpfs_gro_remove(struct mount *mp, kauth tmpfs_dir_detach(dnode, *dep); tmpfs_free_dirent(VFS_TO_TMPFS(mp), *dep); + tmpfs_update(dvp, TMPFS_UPDATE_MTIME | TMPFS_UPDATE_CTIME); return 0; } Index: src/sys/fs/tmpfs/tmpfs_specops.c diff -u src/sys/fs/tmpfs/tmpfs_specops.c:1.10 src/sys/fs/tmpfs/tmpfs_specops.c:1.11 --- src/sys/fs/tmpfs/tmpfs_specops.c:1.10 Tue May 24 20:17:49 2011 +++ src/sys/fs/tmpfs/tmpfs_specops.c Sat Nov 23 16:35:32 2013 @@ -1,4 +1,4 @@ -/* $NetBSD: tmpfs_specops.c,v 1.10 2011/05/24 20:17:49 rmind Exp $ */ +/* $NetBSD: tmpfs_specops.c,v 1.11 2013/11/23 16:35:32 rmind Exp $ */ /* * Copyright (c) 2005 The NetBSD Foundation, Inc. @@ -35,7 +35,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: tmpfs_specops.c,v 1.10 2011/05/24 20:17:49 rmind Exp $"); +__KERNEL_RCSID(0, "$NetBSD: tmpfs_specops.c,v 1.11 2013/11/23 16:35:32 rmind Exp $"); #include <sys/param.h> #include <sys/vnode.h> @@ -106,10 +106,8 @@ tmpfs_spec_close(void *v) struct vnode *a_vp; int a_fflag; kauth_cred_t a_cred; - } */ *ap = v; - vnode_t *vp = ap->a_vp; + } */ *ap __unused = v; - tmpfs_update(vp, NULL, NULL, NULL, UPDATE_CLOSE); return VOCALL(spec_vnodeop_p, VOFFSET(vop_close), v); } @@ -124,7 +122,7 @@ tmpfs_spec_read(void *v) } */ *ap = v; vnode_t *vp = ap->a_vp; - VP_TO_TMPFS_NODE(vp)->tn_status |= TMPFS_NODE_ACCESSED; + tmpfs_update(vp, TMPFS_UPDATE_ATIME); return VOCALL(spec_vnodeop_p, VOFFSET(vop_read), v); } @@ -139,6 +137,6 @@ tmpfs_spec_write(void *v) } */ *ap = v; vnode_t *vp = ap->a_vp; - VP_TO_TMPFS_NODE(vp)->tn_status |= TMPFS_NODE_MODIFIED; + tmpfs_update(vp, TMPFS_UPDATE_MTIME); return VOCALL(spec_vnodeop_p, VOFFSET(vop_write), v); } Index: src/sys/fs/tmpfs/tmpfs_subr.c diff -u src/sys/fs/tmpfs/tmpfs_subr.c:1.89 src/sys/fs/tmpfs/tmpfs_subr.c:1.90 --- src/sys/fs/tmpfs/tmpfs_subr.c:1.89 Thu Nov 21 14:39:09 2013 +++ src/sys/fs/tmpfs/tmpfs_subr.c Sat Nov 23 16:35:32 2013 @@ -1,4 +1,4 @@ -/* $NetBSD: tmpfs_subr.c,v 1.89 2013/11/21 14:39:09 rmind Exp $ */ +/* $NetBSD: tmpfs_subr.c,v 1.90 2013/11/23 16:35:32 rmind Exp $ */ /* * Copyright (c) 2005-2013 The NetBSD Foundation, Inc. @@ -59,7 +59,7 @@ * reference counting and link counting. That is, an inode can only be * destroyed if its associated vnode is inactive. The destruction is * done on vnode reclamation i.e. tmpfs_reclaim(). It should be noted - * that tmpfs_node_t::tn_links being 0 is a destruction criterion. + * that tmpfs_node_t::tn_links being 0 is a destruction criterion. * * If an inode has references within the file system (tn_links > 0) and * its inactive vnode gets reclaimed/recycled - then the association is @@ -74,7 +74,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: tmpfs_subr.c,v 1.89 2013/11/21 14:39:09 rmind Exp $"); +__KERNEL_RCSID(0, "$NetBSD: tmpfs_subr.c,v 1.90 2013/11/23 16:35:32 rmind Exp $"); #include <sys/param.h> #include <sys/cprng.h> @@ -132,7 +132,6 @@ tmpfs_alloc_node(tmpfs_mount_t *tmp, enu /* Generic initialization. */ nnode->tn_type = type; nnode->tn_size = 0; - nnode->tn_status = 0; nnode->tn_flags = 0; nnode->tn_lockf = NULL; @@ -244,6 +243,7 @@ tmpfs_free_node(tmpfs_mount_t *tmp, tmpf } break; case VDIR: + KASSERT(node->tn_size == 0); KASSERT(node->tn_spec.tn_dir.tn_seq_arena == NULL); KASSERT(TAILQ_EMPTY(&node->tn_spec.tn_dir.tn_dir)); KASSERT(node->tn_spec.tn_dir.tn_parent == NULL || @@ -252,6 +252,7 @@ tmpfs_free_node(tmpfs_mount_t *tmp, tmpf default: break; } + KASSERT(node->tn_links == 0); mutex_destroy(&node->tn_vlock); tmpfs_node_put(tmp, node); @@ -340,13 +341,13 @@ again: } /* - * tmpfs_alloc_file: allocate a new file of specified type and adds it + * tmpfs_construct_node: allocate a new file of specified type and adds it * into the parent directory. * * => Credentials of the caller are used. */ int -tmpfs_alloc_file(vnode_t *dvp, vnode_t **vpp, struct vattr *vap, +tmpfs_construct_node(vnode_t *dvp, vnode_t **vpp, struct vattr *vap, struct componentname *cnp, char *target) { tmpfs_mount_t *tmp = VFS_TO_TMPFS(dvp->v_mount); @@ -403,6 +404,9 @@ tmpfs_alloc_file(vnode_t *dvp, vnode_t * /* Make node opaque if requested. */ if (cnp->cn_flags & ISWHITEOUT) node->tn_flags |= UF_OPAQUE; + + /* Update the parent's timestamps. */ + tmpfs_update(dvp, TMPFS_UPDATE_MTIME | TMPFS_UPDATE_CTIME); out: vput(dvp); return error; @@ -452,8 +456,8 @@ tmpfs_free_dirent(tmpfs_mount_t *tmp, tm * and attach the entry into the directory, specified by vnode. * * => Increases link count on the associated node. - * => Increases link count on directory node, if our node is VDIR. - * It is caller's responsibility to check for the LINK_MAX limit. + * => Increases link count on directory node if our node is VDIR. + * => It is caller's responsibility to check for the LINK_MAX limit. * => Triggers kqueue events here. */ void @@ -477,12 +481,14 @@ tmpfs_dir_attach(tmpfs_node_t *dnode, tm /* Save the hint (might overwrite). */ node->tn_dirent_hint = de; + } else if ((dnode->tn_gen & TMPFS_WHITEOUT_BIT) == 0) { + /* Flag that there are whiteout entries. */ + atomic_or_32(&dnode->tn_gen, TMPFS_WHITEOUT_BIT); } /* Insert the entry to the directory (parent of inode). */ TAILQ_INSERT_TAIL(&dnode->tn_spec.tn_dir.tn_dir, de, td_entries); dnode->tn_size += sizeof(tmpfs_dirent_t); - dnode->tn_status |= TMPFS_NODE_STATUSALL; uvm_vnp_setsize(dvp, dnode->tn_size); if (node != TMPFS_NODE_WHITEOUT && node->tn_type == VDIR) { @@ -548,9 +554,7 @@ tmpfs_dir_detach(tmpfs_node_t *dnode, tm dnode->tn_spec.tn_dir.tn_readdir_lastp = NULL; } TAILQ_REMOVE(&dnode->tn_spec.tn_dir.tn_dir, de, td_entries); - dnode->tn_size -= sizeof(tmpfs_dirent_t); - dnode->tn_status |= TMPFS_NODE_STATUSALL; tmpfs_dir_putseq(dnode, de); if (dvp) { @@ -584,7 +588,6 @@ tmpfs_dir_lookup(tmpfs_node_t *node, str continue; break; } - node->tn_status |= TMPFS_NODE_ACCESSED; return de; } @@ -853,7 +856,7 @@ tmpfs_dir_getdents(tmpfs_node_t *node, s uio->uio_offset = de ? tmpfs_dir_getseq(node, de) : TMPFS_DIRSEQ_EOF; node->tn_spec.tn_dir.tn_readdir_lastp = de; done: - node->tn_status |= TMPFS_NODE_ACCESSED; + tmpfs_update(node->tn_vnode, TMPFS_UPDATE_ATIME); kmem_free(dentp, sizeof(struct dirent)); if (error == EJUSTRETURN) { @@ -921,8 +924,6 @@ tmpfs_reg_resize(struct vnode *vp, off_t /* * tmpfs_chflags: change flags of the given vnode. - * - * => Caller should perform tmpfs_update(). */ int tmpfs_chflags(vnode_t *vp, int flags, kauth_cred_t cred, lwp_t *l) @@ -977,15 +978,13 @@ tmpfs_chflags(vnode_t *vp, int flags, ka } else { node->tn_flags = flags; } - node->tn_status |= TMPFS_NODE_CHANGED; + tmpfs_update(vp, TMPFS_UPDATE_CTIME); VN_KNOTE(vp, NOTE_ATTRIB); return 0; } /* * tmpfs_chmod: change access mode on the given vnode. - * - * => Caller should perform tmpfs_update(). */ int tmpfs_chmod(vnode_t *vp, mode_t mode, kauth_cred_t cred, lwp_t *l) @@ -1009,7 +1008,7 @@ tmpfs_chmod(vnode_t *vp, mode_t mode, ka return error; } node->tn_mode = (mode & ALLPERMS); - node->tn_status |= TMPFS_NODE_CHANGED; + tmpfs_update(vp, TMPFS_UPDATE_CTIME); VN_KNOTE(vp, NOTE_ATTRIB); return 0; } @@ -1019,7 +1018,6 @@ tmpfs_chmod(vnode_t *vp, mode_t mode, ka * * => At least one of uid or gid must be different than VNOVAL. * => Attribute is unchanged for VNOVAL case. - * => Caller should perform tmpfs_update(). */ int tmpfs_chown(vnode_t *vp, uid_t uid, gid_t gid, kauth_cred_t cred, lwp_t *l) @@ -1054,7 +1052,7 @@ tmpfs_chown(vnode_t *vp, uid_t uid, gid_ } node->tn_uid = uid; node->tn_gid = gid; - node->tn_status |= TMPFS_NODE_CHANGED; + tmpfs_update(vp, TMPFS_UPDATE_CTIME); VN_KNOTE(vp, NOTE_ATTRIB); return 0; } @@ -1066,6 +1064,8 @@ int tmpfs_chsize(vnode_t *vp, u_quad_t size, kauth_cred_t cred, lwp_t *l) { tmpfs_node_t *node = VP_TO_TMPFS_NODE(vp); + const off_t length = size; + int error; KASSERT(VOP_ISLOCKED(vp)); @@ -1096,8 +1096,19 @@ tmpfs_chsize(vnode_t *vp, u_quad_t size, return EPERM; } - /* Note: tmpfs_truncate() will raise NOTE_EXTEND and NOTE_ATTRIB. */ - return tmpfs_truncate(vp, size); + if (length < 0) { + return EINVAL; + } + if (node->tn_size == length) { + return 0; + } + + /* Note: tmpfs_reg_resize() will raise NOTE_EXTEND and NOTE_ATTRIB. */ + if ((error = tmpfs_reg_resize(vp, length)) != 0) { + return error; + } + tmpfs_update(vp, TMPFS_UPDATE_CTIME | TMPFS_UPDATE_MTIME); + return 0; } /* @@ -1126,75 +1137,40 @@ tmpfs_chtimes(vnode_t *vp, const struct if (error) return error; - if (atime->tv_sec != VNOVAL && atime->tv_nsec != VNOVAL) - node->tn_status |= TMPFS_NODE_ACCESSED; - - if (mtime->tv_sec != VNOVAL && mtime->tv_nsec != VNOVAL) - node->tn_status |= TMPFS_NODE_MODIFIED; - - if (btime->tv_sec == VNOVAL && btime->tv_nsec == VNOVAL) - btime = NULL; - - tmpfs_update(vp, atime, mtime, btime, 0); + if (atime->tv_sec != VNOVAL) { + node->tn_atime = *atime; + } + if (mtime->tv_sec != VNOVAL) { + node->tn_mtime = *mtime; + } + if (btime->tv_sec != VNOVAL) { + node->tn_birthtime = *btime; + } VN_KNOTE(vp, NOTE_ATTRIB); return 0; } /* - * tmpfs_update: update timestamps, et al. + * tmpfs_update: update the timestamps as indicated by the flags. */ void -tmpfs_update(vnode_t *vp, const struct timespec *acc, - const struct timespec *mod, const struct timespec *birth, int flags) +tmpfs_update(vnode_t *vp, unsigned tflags) { tmpfs_node_t *node = VP_TO_TMPFS_NODE(vp); struct timespec nowtm; - /* KASSERT(VOP_ISLOCKED(vp)); */ - - if (flags & UPDATE_CLOSE) { - /* XXX Need to do anything special? */ - } - if ((node->tn_status & TMPFS_NODE_STATUSALL) == 0) { + if (tflags == 0) { return; } - if (birth != NULL) { - node->tn_birthtime = *birth; - } vfs_timestamp(&nowtm); - if (node->tn_status & TMPFS_NODE_ACCESSED) { - node->tn_atime = acc ? *acc : nowtm; + if (tflags & TMPFS_UPDATE_ATIME) { + node->tn_atime = nowtm; } - if (node->tn_status & TMPFS_NODE_MODIFIED) { - node->tn_mtime = mod ? *mod : nowtm; + if (tflags & TMPFS_UPDATE_MTIME) { + node->tn_mtime = nowtm; } - if (node->tn_status & TMPFS_NODE_CHANGED) { + if (tflags & TMPFS_UPDATE_CTIME) { node->tn_ctime = nowtm; } - - node->tn_status &= ~TMPFS_NODE_STATUSALL; -} - -int -tmpfs_truncate(vnode_t *vp, off_t length) -{ - tmpfs_node_t *node = VP_TO_TMPFS_NODE(vp); - int error; - - if (length < 0) { - error = EINVAL; - goto out; - } - if (node->tn_size == length) { - error = 0; - goto out; - } - error = tmpfs_reg_resize(vp, length); - if (error == 0) { - node->tn_status |= TMPFS_NODE_CHANGED | TMPFS_NODE_MODIFIED; - } -out: - tmpfs_update(vp, NULL, NULL, NULL, 0); - return error; } Index: src/sys/fs/tmpfs/tmpfs_vfsops.c diff -u src/sys/fs/tmpfs/tmpfs_vfsops.c:1.54 src/sys/fs/tmpfs/tmpfs_vfsops.c:1.55 --- src/sys/fs/tmpfs/tmpfs_vfsops.c:1.54 Sun Nov 10 12:46:19 2013 +++ src/sys/fs/tmpfs/tmpfs_vfsops.c Sat Nov 23 16:35:32 2013 @@ -1,4 +1,4 @@ -/* $NetBSD: tmpfs_vfsops.c,v 1.54 2013/11/10 12:46:19 rmind Exp $ */ +/* $NetBSD: tmpfs_vfsops.c,v 1.55 2013/11/23 16:35:32 rmind Exp $ */ /* * Copyright (c) 2005, 2006, 2007 The NetBSD Foundation, Inc. @@ -42,7 +42,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: tmpfs_vfsops.c,v 1.54 2013/11/10 12:46:19 rmind Exp $"); +__KERNEL_RCSID(0, "$NetBSD: tmpfs_vfsops.c,v 1.55 2013/11/23 16:35:32 rmind Exp $"); #include <sys/param.h> #include <sys/types.h> @@ -237,8 +237,14 @@ tmpfs_unmount(struct mount *mp, int mntf tmpfs_dir_detach(node, de); tmpfs_free_dirent(tmp, de); } + /* Extra virtual entry (itself for the root). */ + node->tn_links--; } + /* Release the reference on root (diagnostic). */ + node = tmp->tm_root; + node->tn_links--; + /* Second round, destroy all inodes. */ while ((node = LIST_FIRST(&tmp->tm_nodes)) != NULL) { tmpfs_free_node(tmp, node); Index: src/sys/fs/tmpfs/tmpfs_vnops.c diff -u src/sys/fs/tmpfs/tmpfs_vnops.c:1.106 src/sys/fs/tmpfs/tmpfs_vnops.c:1.107 --- src/sys/fs/tmpfs/tmpfs_vnops.c:1.106 Fri Nov 8 15:44:23 2013 +++ src/sys/fs/tmpfs/tmpfs_vnops.c Sat Nov 23 16:35:32 2013 @@ -1,4 +1,4 @@ -/* $NetBSD: tmpfs_vnops.c,v 1.106 2013/11/08 15:44:23 rmind Exp $ */ +/* $NetBSD: tmpfs_vnops.c,v 1.107 2013/11/23 16:35:32 rmind Exp $ */ /* * Copyright (c) 2005, 2006, 2007 The NetBSD Foundation, Inc. @@ -35,7 +35,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: tmpfs_vnops.c,v 1.106 2013/11/08 15:44:23 rmind Exp $"); +__KERNEL_RCSID(0, "$NetBSD: tmpfs_vnops.c,v 1.107 2013/11/23 16:35:32 rmind Exp $"); #include <sys/param.h> #include <sys/dirent.h> @@ -313,7 +313,7 @@ tmpfs_create(void *v) KASSERT(VOP_ISLOCKED(dvp)); KASSERT(vap->va_type == VREG || vap->va_type == VSOCK); - return tmpfs_alloc_file(dvp, vpp, vap, cnp, NULL); + return tmpfs_construct_node(dvp, vpp, vap, cnp, NULL); } int @@ -334,7 +334,7 @@ tmpfs_mknod(void *v) vput(dvp); return EINVAL; } - return tmpfs_alloc_file(dvp, vpp, vap, cnp, NULL); + return tmpfs_construct_node(dvp, vpp, vap, cnp, NULL); } int @@ -380,8 +380,6 @@ tmpfs_close(void *v) vnode_t *vp = ap->a_vp; KASSERT(VOP_ISLOCKED(vp)); - - tmpfs_update(vp, NULL, NULL, NULL, UPDATE_CLOSE); return 0; } @@ -441,8 +439,6 @@ tmpfs_getattr(void *v) vattr_null(vap); - tmpfs_update(vp, NULL, NULL, NULL, 0); - vap->va_type = vp->v_type; vap->va_mode = node->tn_mode; vap->va_nlink = node->tn_links; @@ -468,9 +464,6 @@ tmpfs_getattr(void *v) return 0; } -#define GOODTIME(tv) ((tv)->tv_sec != VNOVAL || (tv)->tv_nsec != VNOVAL) -/* XXX Should this operation be atomic? I think it should, but code in - * XXX other places (e.g., ufs) doesn't seem to be... */ int tmpfs_setattr(void *v) { @@ -490,31 +483,32 @@ tmpfs_setattr(void *v) /* Abort if any unsettable attribute is given. */ if (vap->va_type != VNON || vap->va_nlink != VNOVAL || vap->va_fsid != VNOVAL || vap->va_fileid != VNOVAL || - vap->va_blocksize != VNOVAL || GOODTIME(&vap->va_ctime) || + vap->va_blocksize != VNOVAL || vap->va_ctime.tv_sec != VNOVAL || vap->va_gen != VNOVAL || vap->va_rdev != VNOVAL || vap->va_bytes != VNOVAL) { return EINVAL; } - if (error == 0 && (vap->va_flags != VNOVAL)) + + if (error == 0 && vap->va_flags != VNOVAL) error = tmpfs_chflags(vp, vap->va_flags, cred, l); - if (error == 0 && (vap->va_size != VNOVAL)) + if (error == 0 && vap->va_size != VNOVAL) error = tmpfs_chsize(vp, vap->va_size, cred, l); if (error == 0 && (vap->va_uid != VNOVAL || vap->va_gid != VNOVAL)) error = tmpfs_chown(vp, vap->va_uid, vap->va_gid, cred, l); - if (error == 0 && (vap->va_mode != VNOVAL)) + if (error == 0 && vap->va_mode != VNOVAL) error = tmpfs_chmod(vp, vap->va_mode, cred, l); - if (error == 0 && (GOODTIME(&vap->va_atime) || GOODTIME(&vap->va_mtime) - || GOODTIME(&vap->va_birthtime))) { + const bool chsometime = + vap->va_atime.tv_sec != VNOVAL || + vap->va_mtime.tv_sec != VNOVAL || + vap->va_birthtime.tv_sec != VNOVAL; + if (error == 0 && chsometime) { error = tmpfs_chtimes(vp, &vap->va_atime, &vap->va_mtime, &vap->va_birthtime, vap->va_vaflags, cred, l); - if (error == 0) - return 0; } - tmpfs_update(vp, NULL, NULL, NULL, 0); return error; } @@ -543,8 +537,12 @@ tmpfs_read(void *v) return EINVAL; } + /* Note: reading zero bytes should not update atime. */ + if (uio->uio_resid == 0) { + return 0; + } + node = VP_TO_TMPFS_NODE(vp); - node->tn_status |= TMPFS_NODE_ACCESSED; uobj = node->tn_spec.tn_reg.tn_aobj; error = 0; @@ -561,6 +559,8 @@ tmpfs_read(void *v) error = ubc_uiomove(uobj, uio, len, IO_ADV_DECODE(ioflag), UBC_READ | UBC_PARTIALOK | UBC_UNMAP_FLAG(vp)); } + + tmpfs_update(vp, TMPFS_UPDATE_ATIME); return error; } @@ -579,7 +579,6 @@ tmpfs_write(void *v) tmpfs_node_t *node; struct uvm_object *uobj; off_t oldsize; - bool extended; int error; KASSERT(VOP_ISLOCKED(vp)); @@ -599,8 +598,7 @@ tmpfs_write(void *v) uio->uio_offset = node->tn_size; } - extended = uio->uio_offset + uio->uio_resid > node->tn_size; - if (extended) { + if (uio->uio_offset + uio->uio_resid > node->tn_size) { error = tmpfs_reg_resize(vp, uio->uio_offset + uio->uio_resid); if (error) goto out; @@ -622,8 +620,7 @@ tmpfs_write(void *v) (void)tmpfs_reg_resize(vp, oldsize); } - node->tn_status |= TMPFS_NODE_ACCESSED | TMPFS_NODE_MODIFIED | - (extended ? TMPFS_NODE_CHANGED : 0); + tmpfs_update(vp, TMPFS_UPDATE_MTIME | TMPFS_UPDATE_CTIME); VN_KNOTE(vp, NOTE_WRITE); out: if (error) { @@ -647,9 +644,8 @@ tmpfs_fsync(void *v) } */ *ap = v; vnode_t *vp = ap->a_vp; - /* Nothing to do. Just update. */ + /* Nothing to do. Should be up to date. */ KASSERT(VOP_ISLOCKED(vp)); - tmpfs_update(vp, NULL, NULL, NULL, 0); return 0; } @@ -706,11 +702,14 @@ tmpfs_remove(void *v) /* * Remove the entry from the directory (drops the link count) and - * destroy it or replace it with a whiteout. - * Note: the inode referred by it will not be destroyed - * until the vnode is reclaimed/recycled. + * destroy it or replace with a whiteout. + * + * Note: the inode referred by it will not be destroyed until the + * vnode is reclaimed/recycled. */ + tmpfs_dir_detach(dnode, de); + if (ap->a_cnp->cn_flags & DOWHITEOUT) tmpfs_dir_attach(dnode, de, TMPFS_NODE_WHITEOUT); else @@ -718,9 +717,9 @@ tmpfs_remove(void *v) if (node->tn_links > 0) { /* We removed a hard link. */ - node->tn_status |= TMPFS_NODE_CHANGED; - tmpfs_update(vp, NULL, NULL, NULL, 0); + tmpfs_update(vp, TMPFS_UPDATE_CTIME); } + tmpfs_update(dvp, TMPFS_UPDATE_MTIME | TMPFS_UPDATE_CTIME); error = 0; out: /* Drop the references and unlock the vnodes. */ @@ -786,13 +785,13 @@ tmpfs_link(void *v) * It will increase the inode link count. */ tmpfs_dir_attach(dnode, de, node); + tmpfs_update(dvp, TMPFS_UPDATE_MTIME | TMPFS_UPDATE_CTIME); /* Update the timestamps and trigger the event. */ if (node->tn_vnode) { VN_KNOTE(node->tn_vnode, NOTE_LINK); } - node->tn_status |= TMPFS_NODE_CHANGED; - tmpfs_update(vp, NULL, NULL, NULL, 0); + tmpfs_update(vp, TMPFS_UPDATE_CTIME); error = 0; out: VOP_UNLOCK(vp); @@ -815,7 +814,7 @@ tmpfs_mkdir(void *v) struct vattr *vap = ap->a_vap; KASSERT(vap->va_type == VDIR); - return tmpfs_alloc_file(dvp, vpp, vap, cnp, NULL); + return tmpfs_construct_node(dvp, vpp, vap, cnp, NULL); } int @@ -839,19 +838,25 @@ tmpfs_rmdir(void *v) KASSERT(node->tn_spec.tn_dir.tn_parent == dnode); /* - * Directories with more than two non-whiteout - * entries ('.' and '..') cannot be removed. + * Directories with more than two entries ('.' and '..') cannot be + * removed. There may be whiteout entries, which we will destroy. */ if (node->tn_size > 0) { - KASSERT(error == 0); + /* + * If never had whiteout entries, the directory is certainly + * not empty. Otherwise, scan for any non-whiteout entry. + */ + if ((node->tn_gen & TMPFS_WHITEOUT_BIT) == 0) { + error = ENOTEMPTY; + goto out; + } TAILQ_FOREACH(de, &node->tn_spec.tn_dir.tn_dir, td_entries) { if (de->td_node != TMPFS_NODE_WHITEOUT) { error = ENOTEMPTY; - break; + goto out; } } - if (error) - goto out; + KASSERT(error == 0); } /* Lookup the directory entry (check the cached hint first). */ @@ -870,7 +875,6 @@ tmpfs_rmdir(void *v) /* Decrement the link count for the virtual '.' entry. */ node->tn_links--; - node->tn_status |= TMPFS_NODE_STATUSALL; /* Detach the directory entry from the directory. */ tmpfs_dir_detach(dnode, de); @@ -880,8 +884,9 @@ tmpfs_rmdir(void *v) /* * Destroy the directory entry or replace it with a whiteout. - * Note: the inode referred by it will not be destroyed - * until the vnode is reclaimed. + * + * Note: the inode referred by it will not be destroyed until the + * vnode is reclaimed. */ if (ap->a_cnp->cn_flags & DOWHITEOUT) tmpfs_dir_attach(dnode, de, TMPFS_NODE_WHITEOUT); @@ -894,7 +899,9 @@ tmpfs_rmdir(void *v) tmpfs_dir_detach(node, de); tmpfs_free_dirent(tmp, de); } + tmpfs_update(dvp, TMPFS_UPDATE_MTIME | TMPFS_UPDATE_CTIME); + KASSERT(node->tn_size == 0); KASSERT(node->tn_links == 0); out: /* Release the nodes. */ @@ -920,7 +927,7 @@ tmpfs_symlink(void *v) char *target = ap->a_target; KASSERT(vap->va_type == VLNK); - return tmpfs_alloc_file(dvp, vpp, vap, cnp, target); + return tmpfs_construct_node(dvp, vpp, vap, cnp, target); } int @@ -1026,7 +1033,7 @@ tmpfs_readlink(void *v) } else { error = 0; } - node->tn_status |= TMPFS_NODE_ACCESSED; + tmpfs_update(vp, TMPFS_UPDATE_ATIME); return error; } @@ -1185,14 +1192,17 @@ tmpfs_getpages(void *v) return EBUSY; if ((flags & PGO_NOTIMESTAMP) == 0) { + u_int tflags = 0; + if ((vp->v_mount->mnt_flag & MNT_NOATIME) == 0) - node->tn_status |= TMPFS_NODE_ACCESSED; + tflags |= TMPFS_UPDATE_ATIME; if ((access_type & VM_PROT_WRITE) != 0) { - node->tn_status |= TMPFS_NODE_MODIFIED; + tflags |= TMPFS_UPDATE_MTIME; if (vp->v_mount->mnt_flag & MNT_RELATIME) - node->tn_status |= TMPFS_NODE_ACCESSED; + tflags |= TMPFS_UPDATE_ATIME; } + tmpfs_update(vp, tflags); } /* @@ -1289,6 +1299,7 @@ tmpfs_whiteout(void *v) tmpfs_free_dirent(tmp, de); break; } + tmpfs_update(dvp, TMPFS_UPDATE_MTIME | TMPFS_UPDATE_CTIME); return 0; } @@ -1302,9 +1313,9 @@ tmpfs_print(void *v) tmpfs_node_t *node = VP_TO_TMPFS_NODE(vp); printf("tag VT_TMPFS, tmpfs_node %p, flags 0x%x, links %d\n" - "\tmode 0%o, owner %d, group %d, size %" PRIdMAX ", status 0x%x", + "\tmode 0%o, owner %d, group %d, size %" PRIdMAX, node, node->tn_flags, node->tn_links, node->tn_mode, node->tn_uid, - node->tn_gid, (uintmax_t)node->tn_size, node->tn_status); + node->tn_gid, (uintmax_t)node->tn_size); if (vp->v_type == VFIFO) { VOCALL(fifo_vnodeop_p, VOFFSET(vop_print), v); }