+ uap = (struct a *)u.u_ap;
+ ip = namei(uchar, LOOKUP | LOCKPARENT, 0);
+ if (ip == NULL)
+ return;
+ dp = u.u_pdir;
+ oldparent = 0, doingdirectory = 0;
+ if ((ip->i_mode&IFMT) == IFDIR) {
+ register struct direct *d;
+
+ d = &u.u_dent;
+ /*
+ * Avoid "." and ".." for obvious reasons.
+ */
+ if (d->d_name[0] == '.') {
+ if (d->d_namlen == 1 ||
+ (d->d_namlen == 2 && d->d_name[1] == '.')) {
+ u.u_error = EINVAL;
+ iput(ip);
+ return;
+ }
+ }
+ oldparent = dp->i_number;
+ doingdirectory++;
+ }
+ irele(dp);
+
+ /*
+ * 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_nlink++;
+ ip->i_flag |= ICHG;
+ iupdat(ip, &time, &time, 1);
+ iunlock(ip);
+
+ /*
+ * When the target exists, both the directory
+ * and target inodes are returned locked.
+ */
+ u.u_dirp = (caddr_t)uap->to;
+ xp = namei(uchar, CREATE | LOCKPARENT, 0);
+ if (u.u_error)
+ goto out;
+ dp = u.u_pdir;
+ /*
+ * 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.
+ */
+ parentdifferent = oldparent != dp->i_number;
+ if (xp == NULL) {
+ if (dp->i_dev != ip->i_dev) {
+ u.u_error = EXDEV;
+ goto bad;
+ }
+ /*
+ * Account for ".." in directory.
+ * When source and destination have the
+ * same parent we don't fool with the
+ * link count -- this isn't required
+ * because we do a similar check below.
+ */
+ if (doingdirectory && parentdifferent) {
+ dp->i_nlink++;
+ dp->i_flag |= ICHG;
+ iupdat(dp, &time, &time, 1);
+ }
+ direnter(ip);
+ if (u.u_error)
+ goto out;
+ } else {
+ if (xp->i_dev != dp->i_dev || xp->i_dev != ip->i_dev) {
+ u.u_error = EXDEV;
+ goto bad;
+ }
+ /*
+ * Target must be empty if a directory.
+ * Also, insure source and target are
+ * compatible (both directories, or both
+ * not directories).
+ */
+ if ((xp->i_mode&IFMT) == IFDIR) {
+ if (!dirempty(xp)) {
+ u.u_error = ENOTEMPTY;
+ goto bad;
+ }
+ if (!doingdirectory) {
+ u.u_error = ENOTDIR;
+ goto bad;
+ }
+ } else if (doingdirectory) {
+ u.u_error = EISDIR;
+ goto bad;
+ }
+ dirrewrite(dp, ip);
+ if (u.u_error)
+ goto bad1;
+ /*
+ * If this is a directory we know it is
+ * empty and we can squash the inode and
+ * any space associated with it. Otherwise,
+ * we've got a plain file and the link count
+ * simply needs to be adjusted.
+ */
+ if (doingdirectory) {
+ xp->i_nlink = 0;
+ itrunc(xp, (u_long)0);
+ } else
+ xp->i_nlink--;
+ xp->i_flag |= ICHG;
+ iput(xp);
+ }
+
+ /*
+ * 3) Unlink the source.
+ */
+ u.u_dirp = uap->from;
+ dp = namei(uchar, DELETE, 0);
+ /*
+ * Insure directory entry still exists and
+ * has not changed since the start of all
+ * this. If either has occured, forget about
+ * about deleting the original entry and just
+ * adjust the link count in the inode.
+ */
+ if (dp == NULL || u.u_dent.d_ino != ip->i_number) {
+ ip->i_nlink--;
+ ip->i_flag |= ICHG;
+ } else {
+ /*
+ * If source is a directory, must adjust
+ * link count of parent directory also.
+ * If target didn't exist and source and
+ * target have the same parent, then we
+ * needn't touch the link count, it all
+ * balances out in the end. Otherwise, we
+ * must do so to reflect deletion of ".."
+ * done above.
+ */
+ if (doingdirectory && (xp != NULL || parentdifferent)) {
+ dp->i_nlink--;
+ dp->i_flag |= ICHG;
+ }
+ if (dirremove()) {
+ ip->i_nlink--;
+ ip->i_flag |= ICHG;
+ }
+ }
+ irele(ip);
+ if (dp)
+ iput(dp);
+
+ /*
+ * 4) Renaming a directory with the parent
+ * different requires ".." to be rewritten.
+ * The window is still there for ".." to
+ * be inconsistent, but this is unavoidable,
+ * and a lot shorter than when it was done
+ * in a user process.
+ */
+ if (doingdirectory && parentdifferent && u.u_error == 0) {
+ struct dirtemplate dirbuf;
+
+ u.u_dirp = uap->to;
+ ip = namei(uchar, LOOKUP | LOCKPARENT, 0);
+ if (ip == NULL) {
+ printf("rename: .. went away\n");
+ return;
+ }
+ dp = u.u_pdir;
+ if ((ip->i_mode&IFMT) != IFDIR) {
+ printf("rename: .. not a directory\n");
+ goto stuck;
+ }
+ u.u_error = rdwri(UIO_READ, ip, (caddr_t)&dirbuf,
+ sizeof (struct dirtemplate), (off_t)0, 1, (int *)0);
+ if (u.u_error == 0) {
+ dirbuf.dotdot_ino = dp->i_number;
+ (void) rdwri(UIO_WRITE, ip, (caddr_t)&dirbuf,
+ sizeof (struct dirtemplate), (off_t)0, 1, (int *)0);
+ }
+stuck:
+ irele(dp);
+ iput(ip);
+ }
+ return;
+bad:
+ iput(u.u_pdir);
+bad1:
+ if (xp)
+ irele(xp);
+out:
+ ip->i_nlink--;
+ ip->i_flag |= ICHG;
+ irele(ip);