Strange behavior of mv(1)

Diomidis Spinellis dds at
Wed Nov 21 00:27:02 PST 2007

Xin LI wrote:
> It seems that mv(1) behaves differently when handling with respect to
> different filesystems.
> Test1: Moving within one filesystem:
> mkdir -p t/a/b
> mkdir -p b/c/d
> cd t/
> mv ../b a
> Resulting tree:
> 	t/
> 		a/
> 			b/
> 				c/
> 					d/
> However, with different filesystems:
> mkdir -p t/a/b
> mkdir -p /tmp/b/c/d
> cd t
> mv /tmp/b a
> We get:
> 	t/
> 		a/
> 			b/
> 				b/
> 					c/
> 						d/
> I think the second behavior is not correct?

Yes, the second behavior is not correct.  The first, behaves according
to the specification of mv in IEEE Std 1003.1, 2004 Edition:

"The mv utility shall perform actions equivalent to the rename()
function defined in the System Interfaces volume of IEEE Std
1003.1-2001, called with the following arguments:

    1.      The source_file operand is used as the old argument.
    2.      The destination path is used as the new argument."


"The destination path for each source_file shall be the concatenation of
the target directory, a single slash character, and the last pathname
component of the source_file."

mv -v and truss(1) show that this is the case:

rename("../b","a/b")                             = 0 (0x0)

The code used for running the second instance, misses implementing a
part of the rename(2) specification:

"If the directory named by the new argument exists, it shall be removed
and old renamed to new."

Currently mv first calls cp, which behaves as follows:

cp -PRpv /tmp/b a/b
/tmp/b -> a/b/b
/tmp/b/c -> a/b/b/c
/tmp/b/c/d -> a/b/b/c/d

If the destination is first removed, cp behaves as expected:

rm -rf a/b
cp -PRpv /tmp/b a/b
/tmp/b -> a/b
/tmp/b/c -> a/b/c
/tmp/b/c/d -> a/b/c/d

I'd be happy to fix it, if you file a PR assigned to me.

There are two issues related to the fix:
1. I think we should do more to preserve the atomicity requirements of 
rename(2) when we're copying files.  Currently if cp(1) or rm(1) fails, 
stuff is left lying around.  If the fix follows the same path, data will 
also get deleted.
2. How would you feel about linking with mv(1) the code of cp(1) and 
rm(1)?  On i386 the space overhead will be less than 25k, and we will 
save three fork/exec calls.

Diomidis Spinellis -

More information about the freebsd-current mailing list