stat 与带有 EEXIST 的 mkdir
如果文件夹不存在,我需要创建它,所以我使用:
bool mkdir_if_not_exist(const char *dir)
{
bool ret = false;
if (dir) {
// first check if folder exists
struct stat folder_info;
if (stat(dir, &folder_info) != 0) {
if (errno == ENOENT) { // create folder
if (mkdir(dir, S_IRWXU | S_IXGRP | S_IRGRP | S_IROTH | S_IXOTH) ?!= 0) // 755
perror("mkdir");
else
ret = true;
} else
perror("stat");
} else
ret = true; ?// dir exists
}
return ret;
}
仅在程序第一次运行期间创建文件夹 - 之后它只是一个检查。 建议跳过 stat 调用并调用 mkdir 并根据 EEXIST 检查 errno。 它能带来真正的好处吗?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
更重要的是,使用
stat
+mkdir
方法时,存在竞争条件:在stat
和mkdir
之间> 另一个程序可以执行mkdir
,因此您的mkdir
仍可能因EEXIST
而失败。More important, with the
stat
+mkdir
approach, there is a race condition: in between thestat
and themkdir
another program could do themkdir
, so yourmkdir
could still fail withEEXIST
.有一个小小的好处。查找“LBYL 与 EAFP” 或“三思而后行”与“更容易”请求宽恕而不是许可”。
轻微的好处是
stat()
系统调用必须解析目录名称并获取 inode - 或者在本例中为丢失的 inode - 然后mkdir()
也必须这样做。诚然,mkdir()
所需的数据已经在内核缓冲池中,但它仍然涉及对指定路径的两次遍历,而不是一次。因此,在这种情况下,使用 EAFP 比使用 LBYL 的效率稍高一些。然而,这在普通计划中是否真的具有可测量的效果是非常值得商榷的。如果您什么都不做,只是到处创建目录,那么您可能会发现一个好处。但如果您在程序启动时创建单个目录,这绝对是一个很小的影响,基本上是无法测量的。
您可能需要处理
strcmp(dir, "/some/where/or/another") == 0
但尽管"/some/where"
存在的情况,"/some/where/or"
和(必然)"/some/where/or/another"
都不存在。您当前的代码不处理路径中间丢失的目录。它只是报告mkdir()
将报告的 ENOENT。您的代码看起来也不会检查dir
实际上是一个目录 - 它只是假设如果它存在,那么它就是一个目录。正确处理这些变化是比较棘手的。There's a slight benefit. Look up 'LBYL vs EAFP' or 'Look Before You Leap' vs 'Easier to Ask Forgiveness than Permission'.
The slight benefit is that the
stat()
system call has to parse the directory name and get to the inode - or the missing inode in this case - and thenmkdir()
has to do the same. Granted, the data needed bymkdir()
is already in the kernel buffer pool, but it still involves two traversals of the path specified instead of one. So, in this case, it is slightly more efficient to use EAFP than to use LBYL as you do.However, whether that is really a measurable effect in the average program is highly debatable. If you are doing nothing but create directories all over the place, then you might detect a benefit. But it is definitely a small effect, essentially unmeasurable, if you create a single directory at the start of a program.
You might need to deal with the case where
strcmp(dir, "/some/where/or/another") == 0
but although"/some/where"
exists, neither"/some/where/or"
nor (of necessity)"/some/where/or/another"
exist. Your current code does not handle missing directories in the middle of the path. It just reports the ENOENT thatmkdir()
would report. Your code that looks does not check thatdir
actually is a directory, either - it just assumes that if it exists, it is a directory. Handling these variations properly is trickier.类似于 按顺序使用 stat 和 mkdir 的竞争条件,您的解决方案不正确,不仅因为竞争条件(正如这里的其他答案已经指出的那样),而且因为您从不检查现有文件是否是目录 或不。
当重新实现 UNIX 现有命令行工具中已经广泛使用的功能时,首先了解这些工具是如何实现的总是有帮助的。
例如,看一下
mkdir(1)
-p
选项 在 BSD 中实现(OpenBSD 中的bin/mkdir/mkdir.c#mkpath
和 NetBSD),所有这些,在mkdir(2)
's 错误,似乎立即调用stat(2)
随后运行S_ISDIR
宏确保现有文件是一个目录,而不仅仅是任何其他类型的文件。Similar to Race condition with stat and mkdir in sequence, your solution is incorrect not only due to the race condition (as already pointed out by the other answers over here), but also because you never check whether the existing file is a directory or not.
When re-implementing functionality that's already widely available in existing command-line tools in UNIX, it always helps to see how it was implemented in those tools in the first place.
For example, take a look at how
mkdir(1)
-p
option is implemented across the BSDs (bin/mkdir/mkdir.c#mkpath
in OpenBSD and NetBSD), all of which, onmkdir(2)
's error, appear to immediately callstat(2)
to subsequently run theS_ISDIR
macro to ensure that the existing file is a directory, and not just any other type of a file.