Re: [QUESTION] question about the errno of rename the parent dir to a subdir of a specified directory
From: zhangyi (F)
Date: Fri Dec 27 2019 - 07:48:12 EST
Hi,
Thanks for your detailed explanation, I will also check the freeBSD.
Thanks,
Yi.
On 2019/12/26 0:27, Al Viro wrote:
> On Wed, Dec 25, 2019 at 09:16:09PM +0800, zhangyi (F) wrote:
>> Hi,
>>
>> If we rename the parent-dir to a sub-dir of a specified directory, the
>> rename() syscall return -EINVAL because lock_rename() in lock_rename()
>> checks the relations of the sorece and dest dirs. But if the 'parent'
>> dir is a mountpoint, the rename() syscall return -EXDEV instead because
>> it checks the parent dir's mountpoint of the sorece and dest dirs.
>>
>> For example:
>> Case 1: rename() return -EINVAL
>> # mkdir -p parent/dir
>> # rename parent parent/dir/subdir parent
>> rename: parent: rename to parent/dir/subdir failed: Invalid argument
>
> That was rename("parent", "parent/dir/subdir") being told to sod off and
> not try to create loops.
>
>> Case 2: rename() return -EXDEV
>> # mkdir parent
>> # mount -t tmpfs test parent
>> # mkdir parent/dir
>> # rename parent parent/dir/subdir parent
>> rename: parent: rename to parent/dir/subdir failed: Invalid cross-device link
>>
>> In case 2, although 'parent' directory is a mountpoint, it acted as a root
>> dir of the "test tmpfs", so it should belongs to the same mounted fs of
>> 'dir' directoty, so I think it shall return -EINVAL.
>>
>> Is it a bug or just designed as this ?
>
> rename() operates on directory entries. Pathnames can refer to files (including
> directories) or they can refer to directory entries (links). rename() and other
> directory-modifying syscalls operate on the latter. In the second test two
> error conditions apply: in addition to attempted loop creation, we are asked to
> move the link 'parent' from whatever it's in (your current directory) to 'subdir'
> in the directory parent/dir, the latter being on a different filesystem.
>
> It's not "take the file old pathname refers to, move it to new place"; that's
> particularly obvious when you consider
>
> echo foo >a # create a file
> ln a b # now 'a' and 'b' both refer to it
> mv a c # or rename a c a, if you really want to touch util-linux rename(1)
>
> Desired result is, of course, 'a' disappearing, 'b' left as is and 'c' now refering
> to the same file. If you did mv b c as the last step, 'a' would be left as is,
> 'b' would disappear and 'c' added, refering to the same file. But the only
> difference between mv a c and mv b c is the first argument of rename(2) and
> in both cases it resolves to the same file. In other words, rename(2) can't
> operate on that level; to be of any use it has to interpret the pathnames
> as refering to directory entries.
>
> That, BTW, is the source of "the last component must not be . or .." - they
> do refer to directories just fine, but rename("dir1/.", "dir2/foo") is not just
> 'make the directory refered to by "dir1/." show up as "dir2/foo"' - it's
> 'rip the entry "." from the directory "dir1" and move it into directory "dir2"
> under the name "foo"'.
>
> So your second testcase is a genuine cross-filesystem move; you want a link
> to disappear from a directory on one filesystem and reappear in a directory
> on another. It doesn't matter what's mounted on top of that - directory
> entry refers to the mountpoint, not the thing mounted on it.
>
> And in cases when more than one error condition applies, portable userland
> should be ready to cope with the operating system returning any of those.
> Different Unices might return different applicable errors. In this particular
> case I would expect EXDEV to take precedence on the majority of implementations,
> but that's not guaranteed. Note, BTW, that there might be other errors
> applicable here and it's a sufficiently dark corner to expect differences
> (e.g. there might be a blanket ban on renaming mountpoints in general,
> POSIX being quiet on that topic be damned).
>
> That actually might be a good way to get into given Unix VFS - figuring out
> what happens in this implementation will tell you a lot about its pathname
> resolution, related kernel data structures and locking involved. Might
> send you away screaming, though - rename(2) is usually the worst part
> as it is, and bringing the mountpoint crossing into the game is likely
> to expose all kinds of interesting corner cases.
>
> .
>