cpp
我正在使用的文件创建一个目录,即,mkdir( path, ... )
其中路径来自环境变量(例如getenv( "FOO" );
)。
例如,上面创建的$FOO
is/foo
和path
是 `/foo/newPath/'。
对于我的问题场景,可能/foo/oldPath/
存在并具有内容(假设没有其他子目录),在这种情况下,我想将文件从 移动/foo/oldPath/
到/foo/newPath
.
我的问题是:因为 是/foo/newPath/
作为 ie 的子目录创建的$FOO
,/foo/newPath/
并且/foo/oldPath/
具有相同的父目录,那么是否可以保证两个目录位于同一“已安装的文件系统”上?我对 Linux 上的挂载点和文件系统的理解充其量是微不足道的。
这个问题背后的上下文是:如果/foo/newPath/
和/foo/oldPath/
保证位于同一个安装的文件系统上,我可以使用rename()
比其他替代方案更轻松地执行文件移动。该函数的手册页指出,如果oldPath
和newPath
不在同一个“已安装的文件系统”上,它将失败。
答案1
他们不能保证这一点。这可能/foo/oldPath
是一个挂载点。
mount | grep 'on /foo/oldPath'
不过,可以通过运行“无输出应表明该oldPath
目录不是挂载点”来轻松检查这一点。
如果您使用嵌套目录,则需要更加小心,因为您可以在任何地方拥有挂载点。
我不确定这是否是自动的,但值得注意的是,安装中的第三个字段(以空格分隔)是每行的安装点,因此 cut -d ' ' -f 3
可以使用 an 来提取路径(如果您需要验证它是不仅仅是另一个挂载点的子字符串,例如/foo/oldPath/nested/mountPoint
)
如果您想将其转换为 C/C++ 代码,您也许可以使用system("mount | grep 'on /foo/oldPath'")
,但我不会对此发誓。如果您需要的话,您可能会在 StackOverflow 上获得更多实现细节。
答案2
您不应该尝试这样做,原因有多种(其他答案中有详细说明):/foo/oldPath
本身可能是一个挂载点,或者可能存在覆盖文件系统并阻止移动文件。您甚至可能会遇到单个文件上的绑定挂载,这在重命名文件时也会导致问题。
您不应尝试提前确定文件是否可以重命名,而应尝试重命名它们并处理错误。rename
如果发生错误,将返回 -1;如果由于交叉安装问题而无法重命名,errno
则将设置为。EXDEV
然后您可以以另一种方式继续(复制和删除)。
一般来说,要确定两个文件系统对象是否位于同一文件系统上,您应该运行stat
在它们上并查看设备标识符(st_dev
中的字段struct stat
)。同一文件系统上的两个文件系统对象将具有相同的设备标识符。
答案3
请注意,当您在overlayfs上运行时,您不能依赖rename()
预先存在的目录。
我认为如果您不编写通用文件管理工具,您通常可以忽略这种可能性......例如,包管理器...在这种情况下,您显然可能不认为它是可修复的(出于原子性原因)并希望依赖redirect_dir
overlayfs的新格式。
所以这更像是一个迂腐的说明,让你知道这是 Linux,如果我们不愿意,我们就不会遵守 POSIX,而且“保证”是一个非常强烈的词:)。
https://www.kernel.org/doc/Documentation/filesystems/overlayfs.txt
除非启用“redirect_dir”功能,否则在较低目录或合并目录上重命名(2) 将因 EXDEV 失败。
Overlayfs 在其他细节上已经不符合 unix 文件系统的通常期望:
非目录的对象(文件、符号链接、设备专用文件等)根据需要从上层或下层文件系统中呈现。当以需要写访问的方式访问下层文件系统中的文件时,例如打开写访问、更改某些元数据等,该文件首先从下层文件系统复制到上层文件系统(copy_up)...
copy_up 操作本质上是创建一个新的、相同的文件并将其移至旧名称。 任何引用此索引节点的打开文件都将访问旧数据。