5

A cpp file I'm working with creates a directory, i.e. mkdir( path, ... ), where path comes from an environment variable (e.g. getenv( "FOO" );).

As an example, say $FOO is /foo, and path, created above, is `/foo/newPath/'.

For my question scenario, it is possible that /foo/oldPath/ exists and has content (assume no further subdirectories), in which case I want to move files from /foo/oldPath/ to /foo/newPath.

My question is: because /foo/newPath/ is created as a subdirectory of $FOO, i.e. /foo/newPath/ and /foo/oldPath/ have the same parent directory, is it then guaranteed that both directories are on the same "mounted file system"? My understanding of mount points and file systems on Linux is tenuous at best.

The context behind this question is: if /foo/newPath/ and /foo/oldPath/ are guaranteed to be on the same mounted file system, I can use rename() to more easily perform the file movement than other alternatives. The man page of the function says that it will fail if oldPath and newPath are not on the same "mounted file system."

13

They are not guaranteed that. It is possible that /foo/oldPath is a mount point.

This can, however, be easily checked by running mount | grep 'on /foo/oldPath' No output should indicate that the oldPath directory is not a mount point.

You will need to be more careful if you are using nested directories, since you can have a mount point anywhere.

I'm not sure whether this is automated, but it's worth noting that the 3rd field from mount (space-separated) is the mount point for each line, so utilizing an cut -d ' ' -f 3 could be used to extract the path (should you need to verify it's not just a substring of another mount point, like /foo/oldPath/nested/mountPoint)

If you'd like to translate this into C/C++ code, you may be able to use system("mount | grep 'on /foo/oldPath'"), but I won't swear by that. You may have better luck on StackOverflow for more implementation detail there if you need it.

  • There's also the mountpoint command that's specifically for this, not that I recommend doing this with a command from C/C++. – JoL May 4 '18 at 0:37
  • 1
    Note that it is also possible for two filesystems to be mounted onto the same directory. @sourcejedi mentions overlayfs in his answer. On some unixen there is also unionfs. So even if oldPath is not a mount point it is not guaranteed to be on the same filesystem as newPath – slebetman May 4 '18 at 2:47
  • 7
    A better way to check for different file systems is to compare the st_dev attribute of the return values of the respective *stat(2) calls. Alternatively rename(2) will simply fail with EXDEV for source and target files on different file systems. – David Foerster May 4 '18 at 5:28
  • 2
    Also, the system() Isn't recommended stackoverflow.com/q/19913446/3701431 – Sergiy Kolodyazhnyy May 4 '18 at 5:58
  • 1
    You forgot that directory names can contain spaces. – Emil Vikström May 4 '18 at 8:06
5

You shouldn’t attempt this, for a variety of reasons (detailed in the other answers): /foo/oldPath could itself be a mountpoint, or an overlay file system could be present and prevent moving files. You can even encounter bind mounts, on single files, which will also cause problems when renaming files.

Instead of trying to determine ahead of time whether the files can be renamed, you should try to rename them and deal with the errors. rename will return -1 if an error occurs, and errno will be set to EXDEV if the rename isn’t possible because of cross-mount issues. You can then proceed in another fashion (copy and delete).

In general, to determine whether two file system objects are on the same file system, you should run stat on them and look at the device identifier (the st_dev field in struct stat). Two file system objects on the same file system will have the same device identifier.

4

Note that when you run on overlayfs, you cannot rely on rename() of pre-existing directories.

I think you could usually ignore this possibility, if you're not writing a general purpose file management tool... For example, a package manager... in which case you apparently might not consider it fixable (for atomicity reasons) and want to rely on the new redirect_dir format of overlayfs.

So this is more a pedantic note to let you know this is Linux, we don't comply with POSIX if we don't want to, and "guaranteed" is a very strong word :).


https://www.kernel.org/doc/Documentation/filesystems/overlayfs.txt

Unless "redirect_dir" feature is enabled, rename(2) on a lower or merged directory will fail with EXDEV.

Overlayfs already does not match usual expectations of a unix filesystem in other details:

Objects that are not directories (files, symlinks, device-special files etc.) are presented either from the upper or lower filesystem as appropriate. When a file in the lower filesystem is accessed in a way the requires write-access, such as opening for write access, changing some metadata etc., the file is first copied from the lower filesystem to the upper filesystem (copy_up)...

The copy_up operation essentially creates a new, identical file and moves it over to the old name. Any open files referring to this inode will access the old data.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.