[comp.bugs.sys5] mv and mvdir bugs/fixes

scl@virginia.UUCP (01/29/87)

I personally believe that /etc/mvdir, /etc/link, and /etc/unlink are all
very risky programs to have lying around, even when they can only be
used by root.  I must admit that we have needed /etc/link on several
occasions to restore . and .. in a few directories, but I think that
SysV should never have allowed . and .. to disappear in the first place!
(Has anyone else had this happen on a sysV system?  Occasionally after
a power failure we are left with directories missing . and/or ..)

A big fault with /etc/mvdir is that it allows you to create recursive
directories, i.e. move a directory into its own subtree.  This is disastrous
because it creates a structure that is unreachable from the root of the
file system.  I think that this is one reason why AT&T doesn't allow mv to do
anything other than rename directories.  The other reason is the hassle of
changing .. to point to the new parent directory, which is trickier than it looks.
We also have the problem of being careful not to allow anyone to move . or ..

Suppose we want to allow mv to move directories.  It must be setuid to root
since only root can link(2) or unlink(2) directories.
Suppose we want to move /a/b/d to another directory /a/b/c yielding a/b/c/d.
We use the command "mv /a/b/d /a/b/c"
Our mv command does extensive sanity checks and then performs the move
with the following sequence of system calls.

link("/a/b/d", "/a/b/c/d");
unlink("/a/b/d");
unlink("/a/b/c/d/..");
link ("/a/b/c", "/a/b/c/d/..");

This has the desired result.

Looking at the same example, let's suppose our working directory is /a/b/d.
We might be tempted to do the following:
mv . /a/b/c
This is certainly an error but it gets caught right away.
link (".", "/a/b/c/.");   /* fails because /a/b/c/. exists */

But what about "mv . /a/b/c/d" ?

link (".", "/a/b/c/d");
unlink (".");
unlink ("/a/b/c/d/..");
link ("/a/b/c", "/a/b/c/d/..");

This is a distaster because a/b/d still exists. a/b/c/d also exists, but neither
a/b/d/. nor a/b/c/d/. exist.  What a mess!  So we must never allow the last
component of the source directory to be . (or .. for that matter).
But how about the destination?  As it turns out it is fine to move things 
to . or ..  Our home directory is /a/b/c and the command is
"mv ../d ."

link ("../d", "./d");
unlink ("../d");
unlink ("./d/..");
link (".", "./d/..");

This works perfectly.

But we still have a subtle bug.  Our current directory is a/b/d.  Our command is
mv ../d ../c

link ("../d", "../c/d");
unlink ("../d");
unlink ("../c/d/..");
link ("../c" "../c/d/..");

This fails!  Note that after the first link and unlink our current
working directory is now /a/b/c/d  (What else could it be?)
When we unlink ../c/d/.. we are unlinking a/b/c/d/.. and thus we are
also unlinking .. !  The final link fails because .. no longer exists!
To get around this last problem we must change our strategy somewhat.

link ("../d", "../c/d");
unlink ("../d");
chdir ("../c");
unlink ("./d/..");
link (".", "./d/..");

And the problem is solved.

I have rewritten mv on our system to allow directories to be moved, and avoided
all the pitfalls mentioned above.  I followed the strategy already used by AT&T.
Mv itself is not set uid, but whenever a directory is being moved, mv employs a
setuid-to-root program to do it.  I also put in the Berkeley "-i" option which
causes mv and cp to ask before clobbering a file.  If you have a sysV source license
I will gladly send you the sources.  By the way, there's a bug in the SysV mv
(at least on rel2) that I fixed.  Suppose the following exist:  a file x, and two
directories y and y/x.  If you are root you can "mv x y" and mv will unlink the
directory y/x.  Could be real fun if you inadvertently "mv etc /"

I personally believe that AT&T limited mv to renaming directories because
it avoids the recursion problem and the need to repair the .. entry.
Besides, there's always "cpio -pl" (yuck).

The convenience of moving directories around is outweighs any so-called
"new" security problems.  If a directory is writable by me then by god I should
certainly be able move other directories in and out of there if I want.  If you
don't like it, don't give out write permission in that directory.
-- 
Steve Losen

lou@hoxna.UUCP (02/17/87)

In article <138@virginia.acc.virginia.edu>, scl@virginia.UUCP writes:
> [ intersting article discussing 'mv' deleted
> Besides, there's always "cpio -pl" (yuck).
> The convenience of moving directories around is outweighs any so-called
> "new" security problems.  

	A small quibble : Since niether your version nor the original
appear to do more than rename the directory, what's the advantage ?
If you really want to make new files, you still need 'yuckie' cpio.
Anyway, link will still fail across file-systems, and most of the time
I move directories it seems to be across file systems - users to new
places, utilities to new homes, etc.

                                                 lou @ hoxna