【问题标题】:Howto use readlink with dynamic memory allocation如何将 readlink 与动态内存分配一起使用
【发布时间】:2012-03-12 05:24:36
【问题描述】:

问题:

在 linux 机器上,我想读取链接的目标字符串。从文档中我发现了以下代码示例(没有错误处理):

struct stat sb;
ssize_t r;
char * linkname;

lstat("<some link>", &sb);
linkname = malloc(sb.st_size + 1);
r = readlink("/proc/self/exe", linkname, sb.st_size + 1);

问题是 sb.st_size 为我系统上的链接返回 0。

那么如何在这样的系统上为 readline 动态分配内存呢?

非常感谢!


一种可能的解决方案:

供日后参考。使用 jilles 提出的观点:

struct stat sb;
ssize_t r = INT_MAX;
int linkSize = 0;
const int growthRate = 255;

char * linkTarget = NULL;

// get length of the pathname the link points to
if (lstat("/proc/self/exe", &sb) == -1) {   // could not lstat: insufficient permissions on directory?
    perror("lstat");
    return;
}

// read the link target into a string
linkSize = sb.st_size + 1 - growthRate;
while (r >= linkSize) { // i.e. symlink increased in size since lstat() or non-POSIX compliant filesystem
    // allocate sufficient memory to hold the link
    linkSize += growthRate;
    free(linkTarget);
    linkTarget = malloc(linkSize);
    if (linkTarget == NULL) {           // insufficient memory
        fprintf(stderr, "setProcessName(): insufficient memory\n");
        return;
    }

    // read the link target into variable linkTarget
    r = readlink("/proc/self/exe", linkTarget, linkSize);
    if (r < 0) {        // readlink failed: link was deleted?
        perror("lstat");
        return;
    }
}
linkTarget[r] = '\0';   // readlink does not null-terminate the string

【问题讨论】:

    标签: c linux glibc uclibc


    【解决方案1】:

    其他答案未提及,但有 realpath 函数,它完全符合您的要求,由 POSIX.1-2001 指定。

    char *realpath(const char *path, char *resolved_path);
    

    来自手册页:

    realpath() 扩展所有符号链接并解析对 /./、/../ 和名为 通过路径生成规范化的绝对​​路径名。

    realpath 还可以根据需要为您处理动态内存分配。同样,摘自手册页:

    如果将 resolved_pa​​th 指定为 NULL,则 realpath() 使用 malloc(3) 分配一个最多 PATH_MAX 字节的缓冲区来保存 解析路径名,并返回指向此缓冲区的指针。呼叫者,召集者 应该使用 free(3) 释放这个缓冲区。

    作为一个简单完整的例子:

    #include <limits.h>
    #include <stdlib.h>
    #include <stdio.h>    
    
    int
    resolve_link (const char *filename)
    {
      char *res = realpath(filename, NULL);
      if (res == NULL)
        {
          perror("realpath failed");
          return -1;
        }
    
      printf("%s -> %s\n", filename, res);
      free(res);
    
      return 0;
    }
    
    int
    main (void)
    {
      resolve_link("/proc/self/exe");
      return 0;
    }
    

    【讨论】:

    • 我希望更多的库函数可以选择自动分配字符串缓冲区。他们为什么不呢?
    【解决方案2】:

    st_size 在 /proc 上没有给出正确答案。

    相反,您可以 malloc PATH_MAX 或 pathconf(_PC_PATH_MAX) 字节。对于大多数情况,这应该足够了。如果您希望能够处理比这更长的路径,您可以在循环中调用 readlink 并在 readlink 返回值指示缓冲区太短时重新分配缓冲区。请注意,尽管许多其他 POSIX 函数只是假设 PATH_MAX 就足够了。

    【讨论】:

    • 不幸的是 PATH_MAX 没有帮助。在我的系统上是 4096,但在 ext3 文件系统上,没有什么能阻止你拥有比这更长的路径。
    【解决方案3】:

    manpage for readlink(2) 表示如果缓冲区太小,它将静默截断。如果您真的想要无限制(并且不介意为额外的工作付出一些成本),您可以从给定的分配大小开始并不断增加它并重新尝试readlink 调用。当下次调用 readlink 返回与上次迭代相同的字符串时,您可以停止增加缓冲区。

    【讨论】:

      【解决方案4】:

      我有点疑惑为什么st_size 是零。根据 POSIX:

      对于符号链接,st_mode 成员在与文件类型宏一起使用时应包含有意义的信息。 st_mode 中的文件模式位未指定。结构成员 st_ino、st_dev、st_uid、st_gid、st_atim、st_ctim 和 st_mtim 应具有有意义的值,并且 st_nlink 成员的值应设置为符号链接的(硬)链接数。 st_size 成员的值应设置为符号链接中包含的路径名的长度,不包括任何终止空字节。

      来源:http://pubs.opengroup.org/onlinepubs/9699919799/functions/lstat.html

      如果st_size 不起作用,我认为您唯一的选择是动态分配缓冲区并不断调整其大小,只要readlink 的返回值等于缓冲区大小。

      【讨论】:

      • 即使st_size 确实有效,链接目标也可以在lstat() 调用和readlink() 调用之间更改。
      • 确实如此,在这种情况下,您必须检测截断并重试。
      【解决方案5】:

      POSIX 表示符号链接的st_size 字段应设置为链接中路径名的长度(不带'\0')。但是,Linux 上的 /proc 文件系统不符合 POSIX。 (它有更多的违规行为,例如一次读取一个字节的某些文件。)

      您可以分配一个特定大小的缓冲区,尝试readlink(),如果缓冲区不够大(readlink() 返回的字节数与缓冲区的大小一样),则尝试使用更大的缓冲区,直到缓冲区变大够了。

      或者,您可以使用PATH_MAX 并在它不是编译时常量或路径名可能比它长的系统中破坏可移植性(POSIX 也允许)。

      【讨论】:

      • 感谢您对 /proc 文件系统的看法。我没有意识到这一点。所以 st_size “应该”在除 /proc 之外的任何地方工作。看起来必须使用迭代方法作为通用代码的后备解决方案。
      • @ArikRaffaelFunke:无论如何,st_size 字段最多只是指示性的,因为可以在lstat() 调用和readlink() 调用之间更改链接。所以无论如何你都需要做好处理尺寸不足的准备。
      【解决方案6】:

      你到底想用 lstat 实现什么?

      您应该能够通过以下方式获得目标

      char buffer[1024];
      ssize_t r = readlink ("/proc/self/exe", buffer, 1024);
      buffer[r] = 0;
      
      printf ("%s\n", buffer);
      

      如果您尝试获取文件名大小的长度,我认为 st_size 不是正确的变量...但这可能是一个不同的问题。

      【讨论】:

      • 这无济于事,因为我正在寻找一种动态分配所需大小的方法。您隐含地将路径长度限制为 1024 个字符。这在大多数情况下就足够了 - 但不能保证。
      猜你喜欢
      • 1970-01-01
      • 2023-04-07
      • 1970-01-01
      • 2016-02-01
      • 1970-01-01
      • 2011-02-13
      • 2015-02-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多