【问题标题】:C - is calling getcwd causing a bug here?C - 在这里调用 getcwd 会导致错误吗?
【发布时间】:2020-07-21 23:47:03
【问题描述】:

我正在学习 stat 和 readlink 等系统调用。我尝试以两种不同的方式统计根目录:

  1. 只需统计到根目录的路径。很简单。
  2. 然后,绕道而行,fopen“/”,从我得到的 fd 中创建一个文件描述符路径,然后将其读取链接以获取“/”。然后统计。

我不明白这是我期望的工作方式,并且 inode 编号是相同的,除了当我在之后引入更多代码时,即 getcwd,inode 奇怪地不一样并且测试 2 失败。如果你同时运行这两个版本,不管有没有它说要删掉的部分,p2 的 printf 也会改变,你可以看到/ 之后有垃圾。我在这里做错了什么或这里发生了什么?这些初始语句之后的代码如何导致代码中的早期更改?

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>

int main()
{
    /* Stat root this way */
    struct stat st1;
    char *p1 = "/";
    if (!stat(p1, &st1))
        printf("PASS\n");
    else
        printf("FAIL - %m\n");

    /* Stat root another way */
    struct stat st2;
    char fdpath[100], p2[100];
    FILE *fp = fopen("/", "r");
    sprintf(fdpath, "/proc/self/fd/%d", fileno(fp));
    readlink(fdpath, p2, sizeof(p2));
    if (!stat(p2, &st2))
        printf("PASS\n");
    else
        printf("FAIL - %m\n");

    printf("p2 = %s\n", p2);

    /* Check inodes are the same */
    printf("    st1.st_ino = %ld\n", st1.st_ino);
    printf("    st2.st_ino = %ld\n", st2.st_ino);

    /* TRY WITHOUT THIS - Adding this makes the inodes different! Take it out and they're the same */
    char cwd_buf[100];
    char *cwd_ret = getcwd(cwd_buf, sizeof(cwd_buf));
    if (cwd_ret == NULL)
        printf("getcwd failed - %m\n");
    printf("cwd_ret = %s\n", cwd_ret);

    return 0;
}

【问题讨论】:

  • @LHLaurini,我注意到用!检查0是否成功了吗?
  • 你是对的。哎呀。
  • 如果您在适当的地方fclose(fp) 会发生什么? (就像你应该作为一个正直的软件公民一样。)
  • 请不要在致电printf()时使用%m
  • 关于:if (cwd_ret == NULL) printf("getcwd failed\n"); printf("cwd_ret = %s\n", cwd_ret);getcwd()调用失败时,不要尝试打印调用结果(这是一个NULL指针)

标签: c linux getcwd


【解决方案1】:

readlink 不会以空值终止它返回的字符串,因此p2 可能在/ 之后包含任意垃圾。

你应该这样做

ssize_t len = readlink(fdpath, p2, sizeof(p2));
if (len == -1) { /* error */ }
if (len == sizeof(p2)) { /* truncated */ }
p2[len] = '\0';
if (!stat(p2, &st2)) // ...

您也应该在许多其他地方进行错误检查。

在末尾添加和删除不相关的代码可能会改变堆栈布局(因为额外的局部变量),这意味着 p2 包含 不同 垃圾有和没有它。甚至在某些情况下,垃圾是空字节,因此字符串恰好被正确终止了。这种事情在 C 代码中经常发生,并且没有特别的意义。这是 C 标准语言中的未定义行为。不要想太多为什么会发生;只是尝试找到并修复错误,不要再次编写相同的错误:-)

【讨论】:

  • 谢谢。知道为什么这种行为是随机的,而且每当我不调用 getcwd 时它似乎都可以(也许不是专门)?
  • @AndrewCina:我只是补充说 :-)
  • 这对我来说还有更多需要关注+1!
【解决方案2】:

来自手册页:

readlink() 将符号链接路径名的内容放在缓冲区 buf 中,缓冲区大小为 bufsiz。 readlink() 不会将空字节附加到 buf。 它会(静默)截断内容(到 bufsiz 字符的长度),以防缓冲区太小而无法容纳所有内容。

所以如果你想使用p2作为字符串,你需要手动完成:

int size = readlink(fdpath, p2, sizeof(p2) - 1);
if (size < 0)
{
    // error
}
p2[size] = 0;

注意 -1: 应该为空终止符保留一个字节,即使字符串被截断。

【讨论】:

  • @AndrewCina 这是未定义的行为,因此会有所不同。在我的电脑上,'/' 字符后面的字节是带有额外代码的 0x03 和没有它的 0x00。所以这就是它之前工作的原因(“假”空终止符)。由于编译器不会初始化本地数组,所以那里可能有任何数据。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-03-14
  • 2011-02-08
  • 2016-05-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多