【问题标题】:plain C: opening a directory with fopen()普通 C:使用 fopen() 打开目录
【发布时间】:2013-08-14 02:51:38
【问题描述】:

我有一个程序可以打开一个文件并检查它的长度。

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 是无符号的,0xFFFFFFFFFFFFFFFF=264-1 在 64 位系统上)。

不幸的是,上述代码中的条件 (flen == ((size_t)-1)) 没有捕捉到这种情况,flen == 0xFFFFFFFF 也没有(编辑:应该是 0xFFFFFFFFFFFFFFFF)。 printf()-以%x%d 为格式字符串的命令表明比较的两边应该具有相同的值。

为什么比较运算符的行为如此奇怪,即使双方都是同一类型(size_t)?我使用 gcc 4.8.1 作为编译器。

【问题讨论】:

  • 你不是说sizeof(size_t)-1吗?
  • @ScottyBauer 不,他没有。
  • 另外,FILE fd 应为 FILE *fd。而ftell() 不会返回size_t,而是long。并且 -1 不是 64 位上的 0xFFFFFFFF(假设 2 的补码),而是 0xFFFFFFFFFFFFFFFF
  • @ScottyBauer Google“C 类型转换”。
  • 你可以open()一个目录(因此fopen()它),但这就是你可以用它做的所有事情。目的是允许您在文件描述符上使用fchdir()。要读取目录,您需要使用 opendir()readdir() 等。要查找其大小,您可以使用 fstat(fileno(fd)) — 尽管使用名称 fd 代替文件描述符而不是文件描述符让我印象深刻作为烂命名(我会使用FILE *fp = fopen("file", "r"); int fd = fileno(fp);)。

标签: c linux types io


【解决方案1】:

来自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。)

【讨论】:

    【解决方案2】:

    C99 标准(或 C2011 标准)中不存在目录。所以根据定义,fopen-ing 目录要么是实现特定的,要么是未定义的行为。

    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); };
    

    顺便说一句,在具有 libc-2.17 和 3.10.6 内核的 Linux/Debian/Sid/AMD64 上,当 argv[1]/tmp 时,代码运行正常;令人惊讶的是,flenLONG_MAX0x7fffffffffffffff

    顺便说一句,在 Linux 上,目录是文件的一种特殊情况。在文件路径上使用stat(2)(在file descriptor 上使用fstat,可能从一些FILE* 使用fileno(3) 获得)以了解有关某个文件的更多元数据,包括其“类型”(通过其模式) .您希望 opendir(3)readdir(3)closedir(3) 对目录内容进行操作。另见inode(7)

    【讨论】:

    • 谢谢,我现在通过将 flen 的类型更改为 int32_t 解决了这个问题,这在 32 位和 64 位系统上运行良好(使用 long 在 64 上不起作用)位系统)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-23
    • 2011-04-03
    • 1970-01-01
    • 1970-01-01
    • 2015-07-28
    • 1970-01-01
    相关资源
    最近更新 更多