有没有比简单地尝试打开文件更好的方法?
int exists(const char *fname)
{
FILE *file;
if ((file = fopen(fname, "r")))
{
fclose(file);
return 1;
}
return 0;
}
fopen()
/fclose()
方法的一个问题是,即使文件存在,您也可能无法打开文件进行读取。例如,/dev/kmem
存在,但大多数进程甚至无法打开它进行读取。 /etc/shadow
是另一个这样的文件。当然,stat()
和 access()
都依赖于能够访问包含文件的目录;如果您不能这样做(对包含文件的目录没有执行权限),那么所有的赌注都将被取消。
if (file = fopen(fname, "r"))
将发出警告。在 if 语句 if ((file = fopen(fname, "r")))
内的语句周围使用括号
(())
正在解决症状,而不是问题。只需将其分成几行;一条额外的线不会有那么大的伤害。 file = fopen(fname, "r");
if (file)
查找 unistd.h
中的 access()
函数。您可以将您的功能替换为
if (access(fname, F_OK) == 0) {
// file exists
} else {
// file doesn't exist
}
在 Windows (VC) 下 unistd.h
不存在。为了使其工作,有必要定义:
#ifdef WIN32
#include <io.h>
#define F_OK 0
#define access _access
#endif
您还可以使用 R_OK
、W_OK
和 X_OK
代替 F_OK
来检查读取权限、写入权限和执行权限(分别)而不是是否存在,您可以将它们中的任何一个组合在一起(即使用 R_OK|W_OK
检查读取 和 写入权限)
更新:请注意,在 Windows 上,您不能使用 W_OK
可靠地测试写入权限,因为访问函数不考虑 DACL。 access( fname, W_OK )
可能会返回 0(成功),因为该文件没有设置只读属性,但您仍然可能没有写入该文件的权限。
像这样使用 stat
:
#include <sys/stat.h> // stat
#include <stdbool.h> // bool type
bool file_exists (char *filename) {
struct stat buffer;
return (stat (filename, &buffer) == 0);
}
并这样称呼它:
#include <stdio.h> // printf
int main(int ac, char **av) {
if (ac != 2)
return 1;
if (file_exists(av[1]))
printf("%s exists\n", av[1]);
else
printf("%s does not exist\n", av[1]);
return 0;
}
access()
也有可能存在问题,并且有一些选项可用于使 access()
和 stat()
处理大文件(大于 2 GB)。
stat
没有遭受与 access
相同的 TOCTOU 漏洞吗? (我不清楚它会更好。)
stat()
和 access()
都存在 TOCTOU 漏洞(lstat()
也是如此,但 fstat()
是安全的)。这取决于您要根据文件的存在与否执行什么操作。对 open()
使用正确的选项通常是处理问题的最佳方法,但制定正确的选项可能会很棘手。另请参见关于 EAFP(请求宽恕比许可更容易)和 LBYL(在您跳跃之前查看)的讨论——例如,参见 LBYL vs EAFP in Java。
open()
TOC 中的文件(然后 open()
结果成为文件存在的检查),然后使用它TOU 中的描述符。这样即使文件在 TOU 中不再存在,您仍然可以通过文件描述符访问它。只要有打开它的进程,它的内容就会被保留。
通常当你想检查一个文件是否存在时,这是因为你想要创建那个文件,如果它不存在。如果您不想想要创建该文件,Graeme Perrow's answer 很好,但如果您这样做,它很容易受到竞争条件的影响:另一个进程可能会在您检查文件是否存在之间创建该文件,然后您实际上打开它来写它。 (别笑……如果创建的文件是符号链接,这可能会产生不好的安全隐患!)
如果要检查是否存在并在文件不存在时创建文件,以原子方式创建,以便没有竞争条件,请使用以下命令:
#include <fcntl.h>
#include <errno.h>
fd = open(pathname, O_CREAT | O_WRONLY | O_EXCL, S_IRUSR | S_IWUSR);
if (fd < 0) {
/* failure */
if (errno == EEXIST) {
/* the file already existed */
...
}
} else {
/* now you can use the file */
}
open(2)
中记录了一种解决方法(在 Linux 上;您的操作系统的手册页可能会有所不同),但它相当难看,并且可能无法抵抗恶意攻击者。
FILE*
一起使用,您需要使用 posix 方法 fdopen(fd,"flags")
来生成 FILE*
是的。使用 stat()
。请参阅 stat(2)
的手册页。
如果文件不存在,stat()
将失败,否则很可能成功。如果它确实存在,但您对它所在的目录没有读取权限,它也会失败,但在这种情况下,任何方法都会失败(如何根据访问权限检查您可能看不到的目录的内容?简单地说,你不能)。
哦,正如其他人提到的,您也可以使用access()
。但是我更喜欢 stat()
,因为如果文件存在,它将立即为我提供很多有用的信息(上次更新时间、文件大小、拥有文件的所有者和/或组、访问权限等) .
access()
检查文件的文件访问权限,这些权限存储在该文件的 inode 中,而不是其目录条目中(至少对于所有具有 inode- 的文件系统)像结构)。因此,access()
必须以与 stat()
访问它的方式完全相同的方式访问 inode。 所以你所说的只有在你不检查任何权限的情况下才成立!实际上在某些系统上 access()
甚至是在 stat()
之上实现的(例如 GNU Hurd 上的 glibc 就是这样做的方式),因此首先无法保证。
FILE *file;
if((file = fopen("sample.txt","r"))!=NULL)
{
// file exists
fclose(file);
}
else
{
//File not found, no memory leak since 'file' == NULL
//fclose(file) would cause an error
}
fopen()
是标准 C,它不会去任何地方。它只是被微软“弃用”。除非您需要特定于平台的不可移植代码,否则不要使用 fopen_s()
。
您可以使用 realpath() 函数。
resolved_file = realpath(file_path, NULL);
if (!resolved_keyfile) {
/*File dosn't exists*/
perror(keyfile);
return -1;
}
我认为 unistd.h
中的 access() 函数是 Linux
的不错选择(您也可以使用 stat)。
你可以像这样使用它:
#include <stdio.h>
#include <stdlib.h>
#include<unistd.h>
void fileCheck(const char *fileName);
int main (void) {
char *fileName = "/etc/sudoers";
fileCheck(fileName);
return 0;
}
void fileCheck(const char *fileName){
if(!access(fileName, F_OK )){
printf("The File %s\t was Found\n",fileName);
}else{
printf("The File %s\t not Found\n",fileName);
}
if(!access(fileName, R_OK )){
printf("The File %s\t can be read\n",fileName);
}else{
printf("The File %s\t cannot be read\n",fileName);
}
if(!access( fileName, W_OK )){
printf("The File %s\t it can be Edited\n",fileName);
}else{
printf("The File %s\t it cannot be Edited\n",fileName);
}
if(!access( fileName, X_OK )){
printf("The File %s\t is an Executable\n",fileName);
}else{
printf("The File %s\t is not an Executable\n",fileName);
}
}
你得到以下输出:
The File /etc/sudoers was Found
The File /etc/sudoers cannot be read
The File /etc/sudoers it cannot be Edited
The File /etc/sudoers is not an Executable
从 Visual C++ 帮助中,我倾向于使用
/* ACCESS.C: This example uses _access to check the
* file named "ACCESS.C" to see if it exists and if
* writing is allowed.
*/
#include <io.h>
#include <stdio.h>
#include <stdlib.h>
void main( void )
{
/* Check for existence */
if( (_access( "ACCESS.C", 0 )) != -1 )
{
printf( "File ACCESS.C exists\n" );
/* Check for write permission */
if( (_access( "ACCESS.C", 2 )) != -1 )
printf( "File ACCESS.C has write permission\n" );
}
}
另外值得注意的是 _access(const char *path,
int mode
)
的众数值:
00:仅存在
02:写权限
04:读取权限
06:读写权限
因为在文件存在但无法按请求打开的情况下,您的 fopen
可能会失败。
编辑:只需阅读 Mecki 的帖子。 stat()
看起来确实是一种更简洁的方法。哼哼。
void main
?
main()
只有两个有效签名,它们都返回 int
。如果编译器/环境支持 void main()
,那么它只是作为非官方扩展。
access()
检查文件是否存在),但在 SUID 或 SGID 程序中,即使这样也可能不正确。如果测试文件位于真实 UID 或真实 GID 无法访问的目录中,则access()
可能会在该文件确实存在时报告无此类文件。深奥和不可能?是的。