6602216 ufs vnevent_remove/vnevent_rename needs to occur before namespace manipulation
authorOwen Roberts <Owen.Roberts@Sun.Com>
Fri, 16 Apr 2010 04:19:23 -0700
changeset 12170 1eb0a8fe1e90
parent 12169 b5c6274610de
child 12171 8e002df2d0b6
6602216 ufs vnevent_remove/vnevent_rename needs to occur before namespace manipulation
usr/src/uts/common/fs/ufs/ufs_dir.c
usr/src/uts/common/fs/ufs/ufs_vnops.c
usr/src/uts/common/sys/fs/ufs_inode.h
--- a/usr/src/uts/common/fs/ufs/ufs_dir.c	Thu Apr 15 18:43:44 2010 -0700
+++ b/usr/src/uts/common/fs/ufs/ufs_dir.c	Fri Apr 16 04:19:23 2010 -0700
@@ -19,8 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1984, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
@@ -179,7 +178,8 @@
 	char *namep,
 	struct inode **ipp,
 	struct cred *cr,
-	int skipdnlc)			/* skip the 1st level dnlc */
+	int skipdnlc,			/* skip the 1st level dnlc */
+	int skipcaching)		/* force directory caching off */
 {
 	uint64_t handle;
 	struct fbuf *fbp;		/* a buffer of directory entries */
@@ -313,7 +313,7 @@
 	 * Any attempt after the disable time has expired will enable
 	 * the caching again.
 	 */
-	if (dp->i_size >= ufs_min_dir_cache) {
+	if (!skipcaching && (dp->i_size >= ufs_min_dir_cache)) {
 		/*
 		 * if the directory caching disable time has expired
 		 * enable the caching again.
@@ -690,7 +690,7 @@
 		if (tdp->i_ufsvfs)
 			ulp = &tdp->i_ufsvfs->vfs_ulockfs;
 		rw_exit(&tdp->i_rwlock);
-		if (err = ufs_dirlook(tdp, namep, ipp, cr, 0)) {
+		if (err = ufs_dirlook(tdp, namep, ipp, cr, 0, 0)) {
 			if (err == EAGAIN)
 				return (err);
 
@@ -802,7 +802,6 @@
 
 /*
  * Write a new directory entry for DE_LINK, DE_SYMLINK or DE_RENAME operations.
- * If tvpp is non-null, return with the pointer to the target vnode.
  */
 int
 ufs_direnter_lr(
@@ -811,8 +810,7 @@
 	enum de_op op,		/* entry operation */
 	struct inode *sdp,	/* source inode parent if rename */
 	struct inode *sip,	/* source inode */
-	struct cred *cr,	/* user credentials */
-	vnode_t **tvpp)		/* Return: (held) vnode of (existing) target */
+	struct cred *cr)	/* user credentials */
 {
 	struct inode *tip;	/* inode of (existing) target file */
 	char *s;
@@ -968,19 +966,10 @@
 	/*
 	 * If we renamed a file over the top of an existing file,
 	 * or linked a file to an existing file (or tried to),
-	 * then set *tvpp to the target vnode, if tvpp is non-null
-	 * otherwise, release and delete (or just release) the inode.
-	 *
-	 * N.B., by returning the target's vnode pointer to the caller,
-	 * that caller becomes responsible for doing the VN_RELE.
+	 * then release and delete (or just release) the inode.
 	 */
-	if (tip) {
-		if ((err == 0) && (tvpp != NULL)) {
-			*tvpp = ITOV(tip);
-		} else {
-			VN_RELE(ITOV(tip));
-		}
-	}
+	if (tip)
+		VN_RELE(ITOV(tip));
 
 out2:
 	if (err) {
@@ -2412,9 +2401,6 @@
 /*
  * Delete a directory entry.  If oip is nonzero the entry is checked
  * to make sure it still reflects oip.
- *
- * If vpp is non-null, return the ptr of the (held) vnode associated with
- * the removed name.  The caller is responsible for doing the VN_RELE().
  */
 int
 ufs_dirremove(
@@ -2423,8 +2409,7 @@
 	struct inode *oip,
 	struct vnode *cdir,
 	enum dr_op op,
-	struct cred *cr,
-	vnode_t **vpp)	/* Return (held) vnode ptr of removed file/dir */
+	struct cred *cr)
 {
 	struct direct *ep, *pep, *nep;
 	struct inode *ip;
@@ -2761,18 +2746,11 @@
 	rw_exit(&dp->i_ufsvfs->vfs_dqrwlock);
 
 	/*
-	 * If no error and vpp is non-NULL, return the vnode ptr to the caller.
-	 * The caller becomes responsible for the VN_RELE().  Otherwise,
 	 * Release (and delete) the inode after we drop vfs_dqrwlock to
 	 * avoid deadlock since ufs_delete() grabs vfs_dqrwlock as reader.
 	 */
-	if (ip) {
-		if ((err == 0) && (vpp != NULL)) {
-			*vpp = ITOV(ip);
-		} else {
-			VN_RELE(vp);
-		}
-	}
+	if (ip)
+		VN_RELE(vp);
 
 	return (err);
 }
--- a/usr/src/uts/common/fs/ufs/ufs_vnops.c	Thu Apr 15 18:43:44 2010 -0700
+++ b/usr/src/uts/common/fs/ufs/ufs_vnops.c	Fri Apr 16 04:19:23 2010 -0700
@@ -20,8 +20,7 @@
  */
 
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1984, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
@@ -180,6 +179,8 @@
 static	int ufs_setsecattr(struct vnode *, vsecattr_t *, int, struct cred *,
 		caller_context_t *);
 static	int ufs_priv_access(void *, int, struct cred *);
+static	int ufs_eventlookup(struct vnode *, char *, struct cred *,
+    struct vnode **);
 extern int as_map_locked(struct as *, caddr_t, size_t, int ((*)()), void *);
 
 /*
@@ -2854,7 +2855,7 @@
 	if (error)
 		goto out;
 
-	error = ufs_dirlook(ip, nm, &xip, cr, 1);
+	error = ufs_dirlook(ip, nm, &xip, cr, 1, 0);
 
 fastpath:
 	if (error == 0) {
@@ -3209,6 +3210,14 @@
 	if (ufsvfsp->vfs_delete.uq_ne > ufs_idle_max)
 		ufs_delete_drain(vp->v_vfsp, 1, 1);
 
+	error = ufs_eventlookup(vp, nm, cr, &rmvp);
+	if (rmvp != NULL) {
+		/* Only send the event if there were no errors */
+		if (error == 0)
+			vnevent_remove(rmvp, vp, nm, ct);
+		VN_RELE(rmvp);
+	}
+
 retry_remove:
 	error = ufs_lockfs_begin(ufsvfsp, &ulp, ULOCKFS_REMOVE_MASK);
 	if (error)
@@ -3227,7 +3236,7 @@
 	if (indeadlock)
 		goto retry_remove;
 	error = ufs_dirremove(ip, nm, (struct inode *)0, (struct vnode *)0,
-	    DR_REMOVE, cr, &rmvp);
+	    DR_REMOVE, cr);
 	rw_exit(&ip->i_rwlock);
 
 	if (ulp) {
@@ -3235,15 +3244,6 @@
 		ufs_lockfs_end(ulp);
 	}
 
-	/*
-	 * This must be called after the remove transaction is closed.
-	 */
-	if (rmvp != NULL) {
-		/* Only send the event if there were no errors */
-		if (error == 0)
-			vnevent_remove(rmvp, vp, nm, ct);
-		VN_RELE(rmvp);
-	}
 out:
 	return (error);
 }
@@ -3314,7 +3314,7 @@
 	if (indeadlock)
 		goto retry_link;
 	error = ufs_direnter_lr(tdp, tnm, DE_LINK, (struct inode *)0,
-	    sip, cr, NULL);
+	    sip, cr);
 	rw_exit(&tdp->i_rwlock);
 
 unlock:
@@ -3363,6 +3363,7 @@
 	struct inode *ip = NULL;	/* check inode */
 	struct inode *sdp;		/* old (source) parent inode */
 	struct inode *tdp;		/* new (target) parent inode */
+	struct vnode *svp = NULL;	/* source vnode */
 	struct vnode *tvp = NULL;	/* target vnode, if it exists */
 	struct vnode *realvp;
 	struct ufsvfs *ufsvfsp;
@@ -3375,10 +3376,39 @@
 	krwlock_t *first_lock;
 	krwlock_t *second_lock;
 	krwlock_t *reverse_lock;
+	int serr, terr;
 
 	sdp = VTOI(sdvp);
 	slot.fbp = NULL;
 	ufsvfsp = sdp->i_ufsvfs;
+
+	if (VOP_REALVP(tdvp, &realvp, ct) == 0)
+		tdvp = realvp;
+
+	terr = ufs_eventlookup(tdvp, tnm, cr, &tvp);
+	serr = ufs_eventlookup(sdvp, snm, cr, &svp);
+
+	if ((serr == 0) && ((terr == 0) || (terr == ENOENT))) {
+		if (tvp != NULL)
+			vnevent_rename_dest(tvp, tdvp, tnm, ct);
+
+		/*
+		 * Notify the target directory of the rename event
+		 * if source and target directories are not the same.
+		 */
+		if (sdvp != tdvp)
+			vnevent_rename_dest_dir(tdvp, ct);
+
+		if (svp != NULL)
+			vnevent_rename_src(svp, sdvp, snm, ct);
+	}
+
+	if (tvp != NULL)
+		VN_RELE(tvp);
+
+	if (svp != NULL)
+		VN_RELE(svp);
+
 retry_rename:
 	error = ufs_lockfs_begin(ufsvfsp, &ulp, ULOCKFS_RENAME_MASK);
 	if (error)
@@ -3411,7 +3441,7 @@
 	 * Look up inode of file we're supposed to rename.
 	 */
 	gethrestime(&now);
-	if (error = ufs_dirlook(sdp, snm, &sip, cr, 0)) {
+	if (error = ufs_dirlook(sdp, snm, &sip, cr, 0, 0)) {
 		if (error == EAGAIN) {
 			if (ulp) {
 				TRANS_END_CSYNC(ufsvfsp, error, issync,
@@ -3665,11 +3695,9 @@
 	}
 
 	/*
-	 * Link source to the target.  If a target exists, return its
-	 * vnode pointer in tvp.  We'll release it after sending the
-	 * vnevent.
+	 * Link source to the target.
 	 */
-	if (error = ufs_direnter_lr(tdp, tnm, DE_RENAME, sdp, sip, cr, &tvp)) {
+	if (error = ufs_direnter_lr(tdp, tnm, DE_RENAME, sdp, sip, cr)) {
 		/*
 		 * ESAME isn't really an error; it indicates that the
 		 * operation should not be done because the source and target
@@ -3688,7 +3716,7 @@
 	 * the source inode.
 	 */
 	if ((error = ufs_dirremove(sdp, snm, sip, (struct vnode *)0,
-	    DR_RENAME, cr, NULL)) == ENOENT)
+	    DR_RENAME, cr)) == ENOENT)
 		error = 0;
 
 errout:
@@ -3700,43 +3728,14 @@
 		rw_exit(&sdp->i_rwlock);
 	}
 
+	VN_RELE(ITOV(sip));
+
 unlock:
 	if (ulp) {
 		TRANS_END_CSYNC(ufsvfsp, error, issync, TOP_RENAME, trans_size);
 		ufs_lockfs_end(ulp);
 	}
 
-	/*
-	 * If no errors, send the appropriate events on the source
-	 * and destination (a.k.a, target) vnodes, if they exist.
-	 * This has to be done after the rename transaction has closed.
-	 */
-	if (error == 0) {
-		if (tvp != NULL)
-			vnevent_rename_dest(tvp, tdvp, tnm, ct);
-
-		/*
-		 * Notify the target directory of the rename event
-		 * if source and target directories are not same.
-		 */
-		if (sdvp != tdvp)
-			vnevent_rename_dest_dir(tdvp, ct);
-
-		/*
-		 * Note that if ufs_direnter_lr() returned ESAME then
-		 * this event will still be sent.  This isn't expected
-		 * to be a problem for anticipated usage by consumers.
-		 */
-		if (sip != NULL)
-			vnevent_rename_src(ITOV(sip), sdvp, snm, ct);
-	}
-
-	if (tvp != NULL)
-		VN_RELE(tvp);
-
-	if (sip != NULL)
-		VN_RELE(ITOV(sip));
-
 out:
 	return (error);
 }
@@ -3843,6 +3842,14 @@
 	if (ufsvfsp->vfs_delete.uq_ne > ufs_idle_max)
 		ufs_delete_drain(vp->v_vfsp, 1, 1);
 
+	error = ufs_eventlookup(vp, nm, cr, &rmvp);
+	if (rmvp != NULL) {
+		/* Only send the event if there were no errors */
+		if (error == 0)
+			vnevent_rmdir(rmvp, vp, nm, ct);
+		VN_RELE(rmvp);
+	}
+
 retry_rmdir:
 	error = ufs_lockfs_begin(ufsvfsp, &ulp, ULOCKFS_RMDIR_MASK);
 	if (error)
@@ -3860,8 +3867,8 @@
 	ufs_tryirwlock_trans(&ip->i_rwlock, RW_WRITER, TOP_RMDIR, retry);
 	if (indeadlock)
 		goto retry_rmdir;
-	error = ufs_dirremove(ip, nm, (struct inode *)0, cdir, DR_RMDIR, cr,
-	    &rmvp);
+	error = ufs_dirremove(ip, nm, (struct inode *)0, cdir, DR_RMDIR, cr);
+
 	rw_exit(&ip->i_rwlock);
 
 	if (ulp) {
@@ -3870,15 +3877,6 @@
 		ufs_lockfs_end(ulp);
 	}
 
-	/*
-	 * This must be done AFTER the rmdir transaction has closed.
-	 */
-	if (rmvp != NULL) {
-		/* Only send the event if there were no errors */
-		if (error == 0)
-			vnevent_rmdir(rmvp, vp, nm, ct);
-		VN_RELE(rmvp);
-	}
 out:
 	return (error);
 }
@@ -4232,7 +4230,7 @@
 	 */
 
 	rw_enter(&dip->i_rwlock, RW_WRITER);
-	error = ufs_direnter_lr(dip, linkname, DE_SYMLINK, NULL, ip, cr, NULL);
+	error = ufs_direnter_lr(dip, linkname, DE_SYMLINK, NULL, ip, cr);
 	rw_exit(&dip->i_rwlock);
 
 	/*
@@ -6562,3 +6560,85 @@
 
 	return (err);
 }
+
+/*
+ * Locate the vnode to be used for an event notification. As this will
+ * be called prior to the name space change perform basic verification
+ * that the change will be allowed.
+ */
+
+static int
+ufs_eventlookup(struct vnode *dvp, char *nm, struct cred *cr,
+    struct vnode **vpp)
+{
+	int	namlen;
+	int	error;
+	struct vnode	*vp;
+	struct inode	*ip;
+	struct inode	*xip;
+	struct ufsvfs	*ufsvfsp;
+	struct ulockfs	*ulp;
+
+	ip = VTOI(dvp);
+	*vpp = NULL;
+
+	if ((namlen = strlen(nm)) == 0)
+		return (EINVAL);
+
+	if (nm[0] == '.') {
+		if (namlen == 1)
+			return (EINVAL);
+		else if ((namlen == 2) && nm[1] == '.') {
+			return (EEXIST);
+		}
+	}
+
+	/*
+	 * Check accessibility and write access of parent directory as we
+	 * only want to post the event if we're able to make a change.
+	 */
+	if (error = ufs_diraccess(ip, IEXEC|IWRITE, cr))
+		return (error);
+
+	if (vp = dnlc_lookup(dvp, nm)) {
+		if (vp == DNLC_NO_VNODE) {
+			VN_RELE(vp);
+			return (ENOENT);
+		}
+
+		*vpp = vp;
+		return (0);
+	}
+
+	/*
+	 * Keep the idle queue from getting too long by idling two
+	 * inodes before attempting to allocate another.
+	 * This operation must be performed before entering lockfs
+	 * or a transaction.
+	 */
+	if (ufs_idle_q.uq_ne > ufs_idle_q.uq_hiwat)
+		if ((curthread->t_flag & T_DONTBLOCK) == 0) {
+			ins.in_lidles.value.ul += ufs_lookup_idle_count;
+			ufs_idle_some(ufs_lookup_idle_count);
+		}
+
+	ufsvfsp = ip->i_ufsvfs;
+
+retry_lookup:
+	if (error = ufs_lockfs_begin(ufsvfsp, &ulp, ULOCKFS_LOOKUP_MASK))
+		return (error);
+
+	if ((error = ufs_dirlook(ip, nm, &xip, cr, 1, 1)) == 0) {
+		vp = ITOV(xip);
+		*vpp = vp;
+	}
+
+	if (ulp) {
+		ufs_lockfs_end(ulp);
+	}
+
+	if (error == EAGAIN)
+		goto retry_lookup;
+
+	return (error);
+}
--- a/usr/src/uts/common/sys/fs/ufs_inode.h	Thu Apr 15 18:43:44 2010 -0700
+++ b/usr/src/uts/common/sys/fs/ufs_inode.h	Fri Apr 16 04:19:23 2010 -0700
@@ -19,8 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1983, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
@@ -849,17 +848,17 @@
 
 extern	int	ufs_diraccess(struct inode *, int, struct cred *);
 extern	int	ufs_dirlook(struct inode *, char *, struct inode **,
-    cred_t *, int);
+    cred_t *, int, int);
 extern	int	ufs_direnter_cm(struct inode *, char *, enum de_op,
     struct vattr *, struct inode **, cred_t *, int);
 extern	int	ufs_direnter_lr(struct inode *, char *, enum de_op,
-    struct inode *, struct inode *, cred_t *, vnode_t **);
+    struct inode *, struct inode *, cred_t *);
 extern	int	ufs_dircheckpath(ino_t, struct inode *, struct inode *,
     struct cred *);
 extern	int	ufs_dirmakeinode(struct inode *, struct inode **,
     struct vattr *, enum de_op, cred_t *);
 extern	int	ufs_dirremove(struct inode *, char *, struct inode *,
-    vnode_t *, enum dr_op, cred_t *, vnode_t **);
+    vnode_t *, enum dr_op, cred_t *);
 extern  int	ufs_dircheckforname(struct inode *, char *, int,
     struct ufs_slot *, struct inode **, struct cred *, int);
 extern	int	ufs_xattrdirempty(struct inode *, ino_t, cred_t *);