使用fopen()函数打开一个目录的纯C语言实现

9
我有一个程序,它会打开一个文件并检查它的长度。
FILE* fd = fopen(argv[1], "rb");
fseek(fd, 0, SEEK_END);
size_t flen = ftell(fd);
if (flen == ((size_t)-1)) {
    printf("%s is a directory.\n", argv[1]);
    fclose(fd);
    exit(1);
}

现在,在Linux下,fopen()打开目录时返回有效的文件描述符。这导致寻找操作返回-1(或者,因为size_t是无符号的,在64位系统上0xFFFFFFFFFFFFFFFF= 264 - 1)。

不幸的是,上面的代码中的条件(flen == ((size_t)-1))没有捕获到这种情况,flen == 0xFFFFFFFF也是如此(编辑:应为0xFFFFFFFFFFFFFFFF)。 printf()命令使用%x%d作为格式字符串,显示比较的两侧应具有相同的值。

为什么即使两个方面都是相同类型的(size_t),比较运算符也会以这种奇怪的方式行事?我使用gcc 4.8.1编译器。


你是不是指的是 sizeof(size_t)-1 - Scotty Bauer
@ScottyBauer 不,他没有。 - user529758
7
同时,“文件fd”应该是“文件*fd”。而“ftell()”不返回“size_t”,而是返回“long”。在64位(假设使用2的补码)中,-1不是“0xFFFFFFFF”,而是“0xFFFFFFFFFFFFFFFF”。 - user529758
@H2CO3 我不熟悉那个语句,它是什么意思?特别是 flen == ((size_t)-1) - Scotty Bauer
1
@ScottyBauer 谷歌搜索“C类型转换”。 - user529758
2
你可以使用 open() 函数打开一个目录(从而使用 fopen() 函数),但这也是你能做的全部。其目的是允许您在文件描述符上使用 fchdir() 函数。如果要读取目录,则需要使用 opendir()readdir() 等函数。如果要查找其大小,则可以使用 fstat(fileno(fd)) 函数,尽管我认为将 fd 用于 FILE * 而不是文件描述符的命名方式很糟糕(我会使用 FILE *fp = fopen("file", "r"); int fd = fileno(fp);)。 - Jonathan Leffler
2个回答

9

来源于http://pubs.opengroup.org/onlinepubs/7908799/xsh/fopen.html:

The fopen() function will fail if: 
[EISDIR] The named file is a directory and mode requires write access.

至少在Linux上,如果您尝试使用fopen("dirname", "wb"),则会收到EISDIR错误。我还尝试了一个带有d--------访问权限的目录,但仍然收到EISDIR(而不是EACCES)。


8

C99标准(或C2011标准)中不存在目录。因此,使用fopen打开目录要么是实现特定的行为,要么是未定义行为。

fopen(3)可能会失败(返回null结果)。fseek(3)也可能会失败(返回-1)。然后您应该最好检查errno(3)或使用perror(3)

ftell的文档说明返回一个long,失败时返回-1L。在64位Linux上,这是0xffffffffffffffff

您的代码应该改为:

FILE* fd = fopen(argv[1], "rb");
if (!fd) 
  { perror(argv[1]); exit(EXIT_FAILURE); };
if (fseek(fd, 0, SEEK_END)<0) 
  { perror("fseek"); exit(EXIT_FAILURE); };
long flen = ftell(fd);
if (flen == -1L)
  { perror("ftell"); exit(EXIT_FAILURE); };

顺便提一句,在Linux/Debian/Sid/AMD64上,使用libc-2.17和3.10.6内核时,当argv[1]/tmp时,该代码运行正常;令人惊讶的是,flenLONG_MAX,即0x7fffffffffffffff

顺便说一下,在Linux中,目录是文件的一种特殊情况。对于文件路径,请使用stat(2)(对于文件描述符,可以通过某个FILE*使用fileno(3)获得)获取有关某个文件的更多元数据,包括其“类型”(通过其模式)。您需要使用opendir(3)readdir(3)closedir(3)来操作目录内容。另请参见inode(7)


谢谢,我现在已经解决了,通过将 flen 的类型更改为 int32_t,它可以在 32 位和 64 位系统上很好地工作(使用 long 在 64 位系统上无法正常工作)。 - Heinrich supports Monica

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接