xp = VTOI(fvp); dp = VTOI(fdvp);
ufs_rename(v)	void *v;{	struct vop_rename_args  /* {		struct vnode *a_fdvp;		struct vnode *a_fvp;		struct componentname *a_fcnp;		struct vnode *a_tdvp;		struct vnode *a_tvp;		struct componentname *a_tcnp;	} */ *ap = v;	struct vnode *tvp = ap->a_tvp;	struct vnode *tdvp = ap->a_tdvp;	struct vnode *fvp = ap->a_fvp;	struct vnode *fdvp = ap->a_fdvp;	struct componentname *tcnp = ap->a_tcnp;	struct componentname *fcnp = ap->a_fcnp;	struct proc *p = fcnp->cn_proc;	struct inode *ip, *xp, *dp;	struct direct newdir;	int doingdirectory = 0, oldparent = 0, newparent = 0;	int error = 0;#ifdef DIAGNOSTIC	if ((tcnp->cn_flags & HASBUF) == 0 ||	    (fcnp->cn_flags & HASBUF) == 0)		panic("ufs_rename: no name");#endif	/*	 * Check for cross-device rename.	 */	if ((fvp->v_mount != tdvp->v_mount) ||	    (tvp && (fvp->v_mount != tvp->v_mount))) {		error = EXDEV;abortit:		VOP_ABORTOP(tdvp, tcnp); /* XXX, why not in NFS? */		if (tdvp == tvp)			vrele(tdvp);		else			vput(tdvp);		if (tvp)			vput(tvp);		VOP_ABORTOP(fdvp, fcnp); /* XXX, why not in NFS? */		vrele(fdvp);		vrele(fvp);		return (error);	}	if (tvp && ((VTOI(tvp)->i_ffs_flags & (IMMUTABLE | APPEND)) ||	    (VTOI(tdvp)->i_ffs_flags & APPEND))) {		error = EPERM;		goto abortit;	}	/*	 * Check if just deleting a link name or if we've lost a race.	 * If another process completes the same rename after we've looked	 * up the source and have blocked looking up the target, then the	 * source and target inodes may be identical now although the	 * names were never linked.	 */	if (fvp == tvp) {		if (fvp->v_type == VDIR) {			/*			 * Linked directories are impossible, so we must			 * have lost the race.  Pretend that the rename			 * completed before the lookup.			 */			error = ENOENT;			goto abortit;		}		/* Release destination completely. */		VOP_ABORTOP(tdvp, tcnp);		vput(tdvp);		vput(tvp);		/*		 * Delete source.  There is another race now that everything		 * is unlocked, but this doesn't cause any new complications.		 * Relookup() may find a file that is unrelated to the		 * original one, or it may fail.  Too bad.		 */		vrele(fvp);		fcnp->cn_flags &= ~MODMASK;		fcnp->cn_flags |= LOCKPARENT | LOCKLEAF;		if ((fcnp->cn_flags & SAVESTART) == 0)			panic("ufs_rename: lost from startdir");		fcnp->cn_nameiop = DELETE;		(void) relookup(fdvp, &fvp, fcnp);		vrele(fdvp);		if (fvp == NULL) {			return (ENOENT);		}		return (VOP_REMOVE(fdvp, fvp, fcnp));	}	if ((error = vn_lock(fvp, LK_EXCLUSIVE, p)) != 0)		goto abortit;	/* fvp, tdvp, tvp now locked */	dp = VTOI(fdvp);	ip = VTOI(fvp);	if ((nlink_t)ip->i_ffs_nlink >= LINK_MAX) {		VOP_UNLOCK(fvp, 0, p);		error = EMLINK;		goto abortit;	}	if ((ip->i_ffs_flags & (IMMUTABLE | APPEND)) ||	    (dp->i_ffs_flags & APPEND)) {		VOP_UNLOCK(fvp, 0, p);		error = EPERM;		goto abortit;	}	if ((ip->i_ffs_mode & IFMT) == IFDIR) {		error = VOP_ACCESS(fvp, VWRITE, tcnp->cn_cred, tcnp->cn_proc);		if (!error && tvp)			error = VOP_ACCESS(tvp, VWRITE, tcnp->cn_cred, tcnp->cn_proc);		if (error) {			VOP_UNLOCK(fvp, 0, p);			error = EACCES;			goto abortit;		}		/*		 * Avoid ".", "..", and aliases of "." for obvious reasons.		 */		if ((fcnp->cn_namelen == 1 && fcnp->cn_nameptr[0] == '.') ||		    dp == ip ||		    (fcnp->cn_flags & ISDOTDOT) ||		    (tcnp->cn_flags & ISDOTDOT) ||		    (ip->i_flag & IN_RENAME)) {			VOP_UNLOCK(fvp, 0, p);			error = EINVAL;			goto abortit;		}		ip->i_flag |= IN_RENAME;		oldparent = dp->i_number;		doingdirectory = 1;	}	VN_KNOTE(fdvp, NOTE_WRITE);		/* XXX right place? */	/*	 * When the target exists, both the directory	 * and target vnodes are returned locked.	 */	dp = VTOI(tdvp);	xp = NULL;	if (tvp)		xp = VTOI(tvp);	/*	 * 1) Bump link count while we're moving stuff	 *    around.  If we crash somewhere before	 *    completing our work, the link count	 *    may be wrong, but correctable.	 */	ip->i_effnlink++;	ip->i_ffs_nlink++;	ip->i_flag |= IN_CHANGE;	if (DOINGSOFTDEP(fvp))		softdep_change_linkcnt(ip);	if ((error = UFS_UPDATE(ip, !DOINGSOFTDEP(fvp))) != 0) {		VOP_UNLOCK(fvp, 0, p);		goto bad;	}	/*	 * If ".." must be changed (ie the directory gets a new	 * parent) then the source directory must not be in the	 * directory hierarchy above the target, as this would	 * orphan everything below the source directory. Also	 * the user must have write permission in the source so	 * as to be able to change "..". We must repeat the call 	 * to namei, as the parent directory is unlocked by the	 * call to checkpath().	 */	error = VOP_ACCESS(fvp, VWRITE, tcnp->cn_cred, tcnp->cn_proc);	VOP_UNLOCK(fvp, 0, p);	/* tdvp and tvp locked */	if (oldparent != dp->i_number)		newparent = dp->i_number;	if (doingdirectory && newparent) {		if (error)	/* write access check above */			goto bad;		if (xp != NULL)			vput(tvp);		/* Only tdvp is locked */		if ((error = ufs_checkpath(ip, dp, tcnp->cn_cred)) != 0)			goto out;		if ((tcnp->cn_flags & SAVESTART) == 0)			panic("ufs_rename: lost to startdir");		if ((error = relookup(tdvp, &tvp, tcnp)) != 0)			goto out;		dp = VTOI(tdvp);		xp = NULL;		if (tvp)			xp = VTOI(tvp);	}	/*	 * 2) If target doesn't exist, link the target	 *    to the source and unlink the source. 	 *    Otherwise, rewrite the target directory	 *    entry to reference the source inode and	 *    expunge the original entry's existence.	 */	if (xp == NULL) {		if (dp->i_dev != ip->i_dev)			panic("rename: EXDEV");		/*		 * Account for ".." in new directory.		 * When source and destination have the same		 * parent we don't fool with the link count.		 */		if (doingdirectory && newparent) {			if ((nlink_t)dp->i_ffs_nlink >= LINK_MAX) {				error = EMLINK;				goto bad;			}			dp->i_effnlink++;			dp->i_ffs_nlink++;			dp->i_flag |= IN_CHANGE;			if (DOINGSOFTDEP(tdvp))                               softdep_change_linkcnt(dp);			if ((error = UFS_UPDATE(dp, !DOINGSOFTDEP(tdvp))) 			    != 0) {				dp->i_effnlink--;				dp->i_ffs_nlink--;				dp->i_flag |= IN_CHANGE;				if (DOINGSOFTDEP(tdvp))					softdep_change_linkcnt(dp);				goto bad;			}		}		ufs_makedirentry(ip, tcnp, &newdir);		if ((error = ufs_direnter(tdvp, NULL, &newdir, tcnp, NULL)) != 0) {			if (doingdirectory && newparent) {				dp->i_effnlink--;				dp->i_ffs_nlink--;				dp->i_flag |= IN_CHANGE;				if (DOINGSOFTDEP(tdvp))					softdep_change_linkcnt(dp);				(void)UFS_UPDATE(dp, 1);			}			goto bad;		}		VN_KNOTE(tdvp, NOTE_WRITE);		vput(tdvp);	} else {		if (xp->i_dev != dp->i_dev || xp->i_dev != ip->i_dev)			panic("rename: EXDEV");		/*		 * Short circuit rename(foo, foo).		 */		if (xp->i_number == ip->i_number)			panic("ufs_rename: same file");		/*		 * If the parent directory is "sticky", then the user must		 * own the parent directory, or the destination of the rename,		 * otherwise the destination may not be changed (except by		 * root). This implements append-only directories.		 */		if ((dp->i_ffs_mode & S_ISTXT) && tcnp->cn_cred->cr_uid != 0 &&		    tcnp->cn_cred->cr_uid != dp->i_ffs_uid &&		    xp->i_ffs_uid != tcnp->cn_cred->cr_uid) {			error = EPERM;			goto bad;		}		/*		 * Target must be empty if a directory and have no links		 * to it. Also, ensure source and target are compatible		 * (both directories, or both not directories).		 */		if ((xp->i_ffs_mode & IFMT) == IFDIR) {			if (xp->i_effnlink > 2 ||			    !ufs_dirempty(xp, dp->i_number, tcnp->cn_cred)) {				error = ENOTEMPTY;				goto bad;			}			if (!doingdirectory) {				error = ENOTDIR;				goto bad;			}			cache_purge(tdvp);		} else if (doingdirectory) {			error = EISDIR;			goto bad;		}				if ((error = ufs_dirrewrite(dp, xp, ip->i_number,                   IFTODT(ip->i_ffs_mode), (doingdirectory && newparent) ?		   newparent : doingdirectory)) != 0)                        goto bad;		if (doingdirectory) {			if (!newparent) {				dp->i_effnlink--;				if (DOINGSOFTDEP(tdvp))					softdep_change_linkcnt(dp);			}			xp->i_effnlink--;			if (DOINGSOFTDEP(tvp))				softdep_change_linkcnt(xp);		}		if (doingdirectory && !DOINGSOFTDEP(tvp)) {		       /*			* Truncate inode. The only stuff left in the directory			* is "." and "..". The "." reference is inconsequential                        * since we are quashing it. We have removed the "."                        * reference and the reference in the parent directory,                        * but there may be other hard links. The soft                        * dependency code will arrange to do these operations                        * after the parent directory entry has been deleted on                        * disk, so when running with that code we avoid doing                        * them now.                        */			if (!newparent) {				dp->i_ffs_nlink--;				dp->i_flag |= IN_CHANGE;			}			xp->i_ffs_nlink--;			xp->i_flag |= IN_CHANGE;			if ((error = UFS_TRUNCATE(VTOI(tvp), (off_t)0, IO_SYNC,			        tcnp->cn_cred)) != 0)				goto bad;                }		VN_KNOTE(tdvp, NOTE_WRITE);	        vput(tdvp);		VN_KNOTE(tvp, NOTE_DELETE);		vput(tvp);		xp = NULL;	}	/*	 * 3) Unlink the source.	 */	fcnp->cn_flags &= ~MODMASK;	fcnp->cn_flags |= LOCKPARENT | LOCKLEAF;	if ((fcnp->cn_flags & SAVESTART) == 0)		panic("ufs_rename: lost from startdir");	(void) relookup(fdvp, &fvp, fcnp);	vrele(fdvp);	if (fvp != NULL) {		xp = VTOI(fvp);		dp = VTOI(fdvp);	} else {		/*		 * From name has disappeared.		 */		if (doingdirectory)			panic("ufs_rename: lost dir entry");		vrele(ap->a_fvp);		return (0);	}	/*	 * Ensure that the directory entry still exists and has not	 * changed while the new name has been entered. If the source is	 * a file then the entry may have been unlinked or renamed. In	 * either case there is no further work to be done. If the source	 * is a directory then it cannot have been rmdir'ed; the IN_RENAME 	 * flag ensures that it cannot be moved by another rename or removed	 * by a rmdir.	 */	if (xp != ip) {		if (doingdirectory)			panic("ufs_rename: lost dir entry");	} else {		/*		 * If the source is a directory with a		 * new parent, the link count of the old		 * parent directory must be decremented		 * and ".." set to point to the new parent.		 */		if (doingdirectory && newparent) {			xp->i_offset = mastertemplate.dot_reclen;			ufs_dirrewrite(xp, dp, newparent, DT_DIR, 0);			cache_purge(fdvp);		}		error = ufs_dirremove(fdvp, xp, fcnp->cn_flags, 0);		xp->i_flag &= ~IN_RENAME;	}	VN_KNOTE(fvp, NOTE_RENAME);	if (dp)		vput(fdvp);	if (xp)		vput(fvp);	vrele(ap->a_fvp);	return (error);bad:	if (xp)		vput(ITOV(xp));	vput(ITOV(dp));out:	vrele(fdvp);	if (doingdirectory)		ip->i_flag &= ~IN_RENAME;	if (vn_lock(fvp, LK_EXCLUSIVE, p) == 0) {		ip->i_effnlink--;		ip->i_ffs_nlink--;		ip->i_flag |= IN_CHANGE;		ip->i_flag &= ~IN_RENAME;		if (DOINGSOFTDEP(fvp))			softdep_change_linkcnt(ip);		vput(fvp);	} else		vrele(fvp);	return (error);}