- /*
- * When the target exists, both the directory
- * and target inodes are returned locked.
- */
- ndp->ni_nameiop = CREATE | LOCKPARENT | NOCACHE;
- ndp->ni_dirp = (caddr_t)uap->to;
- xp = namei(ndp);
- if (u.u_error) {
- error = u.u_error;
- goto out;
- }
- dp = ndp->ni_pdir;
- /*
- * If ".." must be changed (ie the directory gets a new
- * parent) then the source directory must not be in the
- * directory heirarchy 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().
- */
- parentdifferent = oldparent != dp->i_number;
- if (doingdirectory && parentdifferent) {
- if (access(ip, IWRITE))
- goto bad;
- do {
- dp = ndp->ni_pdir;
- if (xp != NULL)
- iput(xp);
- u.u_error = checkpath(ip, dp);
- if (u.u_error)
- goto out;
- xp = namei(ndp);
- if (u.u_error) {
- error = u.u_error;
- goto out;
- }
- } while (dp != ndp->ni_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.
- */
- if (xp == NULL) {
- if (dp->i_dev != ip->i_dev) {
- 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);
- }
- error = direnter(ip, ndp);
- if (error)
- goto out;
- } else {
- if (xp->i_dev != dp->i_dev || xp->i_dev != ip->i_dev) {
- error = EXDEV;
- goto bad;
- }
- /*
- * Short circuit rename(foo, foo).
- */
- if (xp->i_number == ip->i_number)
- 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) || xp->i_nlink > 2) {
- error = ENOTEMPTY;
- goto bad;
- }
- if (!doingdirectory) {
- error = ENOTDIR;
- goto bad;
- }
- } else if (doingdirectory) {
- error = EISDIR;
- goto bad;
- }
- dirrewrite(dp, ip, ndp);
- if (u.u_error) {
- error = u.u_error;
- goto bad1;
- }
- /*
- * 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 we've no way to determine if
- * we've got a link or the directory itself, and
- * if we get a link, then ".." will be screwed up.
- */
- xp->i_nlink--;
- if (doingdirectory) {
- if (--xp->i_nlink != 0)
- panic("rename: linked directory");
- itrunc(xp, (u_long)0);
- }
- xp->i_flag |= ICHG;
- iput(xp);
- xp = NULL;
- }
-
- /*
- * 3) Unlink the source.
- */
- ndp->ni_nameiop = DELETE | LOCKPARENT;
- ndp->ni_segflg = UIO_USERSPACE;
- ndp->ni_dirp = uap->from;
- zp = namei(ndp);
- dp = ndp->ni_pdir;
- /*
- * 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.
- */
- if (dp != NULL && zp == ip) {
- /*
- * 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(ndp)) {
- zp->i_nlink--;
- zp->i_flag |= ICHG;
- }
- if (error == 0) /* conservative */
- error = u.u_error;
- }
- if (zp != NULL)
- iput(zp);
- 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 && error == 0) {
- struct dirtemplate dirbuf;
-
- ndp->ni_nameiop = LOOKUP | LOCKPARENT;
- ndp->ni_segflg = UIO_USERSPACE;
- ndp->ni_dirp = uap->to;
- ip = namei(ndp);
- if (ip == NULL) {
- printf("rename: .. went away\n");
- return;
- }
- dp = ndp->ni_pdir;
- if ((ip->i_mode&IFMT) != IFDIR) {
- printf("rename: .. not a directory\n");
- goto stuck;
- }
- error = rdwri(UIO_READ, ip, (caddr_t)&dirbuf,
- sizeof (struct dirtemplate), (off_t)0, 1, (int *)0);
- if (error == 0) {
- dirbuf.dotdot_ino = dp->i_number;
- cacheinval(dp);
- (void) rdwri(UIO_WRITE, ip, (caddr_t)&dirbuf,
- sizeof (struct dirtemplate), (off_t)0, 1, (int *)0);
- }
-stuck:
- irele(dp);
- iput(ip);
- }
- goto done;
-
-bad:
- iput(dp);
-bad1:
- if (xp)
- iput(xp);
-out:
- ip->i_nlink--;
- ip->i_flag |= ICHG;
- irele(ip);
-done:
- if (error)
- u.u_error = error;