+ if (xp->i_number == ip->i_number)
+ panic("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_mode & ISVTX) && tndp->ni_cred->cr_uid != 0 &&
+ tndp->ni_cred->cr_uid != dp->i_uid &&
+ xp->i_uid != tndp->ni_cred->cr_uid) {
+ error = EPERM;
+ goto bad;
+ }
+ /*
+ * Target must be empty if a directory
+ * and have no links to it.
+ * Also, insure source and target are
+ * compatible (both directories, or both
+ * not directories).
+ */
+ if ((xp->i_mode&IFMT) == IFDIR) {
+ if (!dirempty(xp, dp->i_number, tndp->ni_cred) ||
+ xp->i_nlink > 2) {
+ error = ENOTEMPTY;
+ goto bad;
+ }
+ if (!doingdirectory) {
+ error = ENOTDIR;
+ goto bad;
+ }
+ cache_purge(ITOV(dp));
+ } else if (doingdirectory) {
+ error = EISDIR;
+ goto bad;
+ }
+ if (error = dirrewrite(dp, ip, tndp))
+ goto bad;
+ vput(ITOV(dp));
+ /*
+ * Adjust the link count of the target to
+ * reflect the dirrewrite above. If this is
+ * a directory it is empty and there are
+ * no links to it, so we can squash the inode and
+ * any space associated with it. We disallowed
+ * renaming over top of a directory with links to
+ * it above, as the remaining link would point to
+ * a directory without "." or ".." entries.
+ */
+ xp->i_nlink--;
+ if (doingdirectory) {
+ if (--xp->i_nlink != 0)
+ panic("rename: linked directory");
+ error = itrunc(xp, (u_long)0, IO_SYNC);
+ }
+ xp->i_flag |= ICHG;
+ iput(xp);
+ xp = NULL;
+ }
+
+ /*
+ * 3) Unlink the source.
+ */
+ fndp->ni_nameiop = DELETE | LOCKPARENT | LOCKLEAF;
+ (void)namei(fndp);
+ if (fndp->ni_vp != NULL) {
+ xp = VTOI(fndp->ni_vp);
+ dp = VTOI(fndp->ni_dvp);
+ } else {
+ if (fndp->ni_dvp != NULL)
+ vput(fndp->ni_dvp);
+ xp = NULL;
+ dp = NULL;
+ }
+ /*
+ * 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; its link
+ * count of three would cause a rmdir to fail with ENOTEMPTY.
+ * The IRENAME flag ensures that it cannot be moved by another
+ * rename.
+ */
+ if (xp != ip) {
+ if (doingdirectory)
+ panic("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) {
+ dp->i_nlink--;
+ dp->i_flag |= ICHG;
+ error = vn_rdwr(UIO_READ, ITOV(xp), (caddr_t)&dirbuf,
+ sizeof (struct dirtemplate), (off_t)0,
+ UIO_SYSSPACE, IO_NODELOCKED,
+ tndp->ni_cred, (int *)0);
+ if (error == 0) {
+ if (dirbuf.dotdot_namlen != 2 ||
+ dirbuf.dotdot_name[0] != '.' ||
+ dirbuf.dotdot_name[1] != '.') {
+ dirbad(xp, 12, "rename: mangled dir");
+ } else {
+ dirbuf.dotdot_ino = newparent;
+ (void) vn_rdwr(UIO_WRITE, ITOV(xp),
+ (caddr_t)&dirbuf,
+ sizeof (struct dirtemplate),
+ (off_t)0, UIO_SYSSPACE,
+ IO_NODELOCKED|IO_SYNC,
+ tndp->ni_cred, (int *)0);
+ cache_purge(ITOV(dp));
+ }
+ }
+ }
+ error = dirremove(fndp);
+ if (!error) {
+ xp->i_nlink--;
+ xp->i_flag |= ICHG;