6

问题:

在 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
4

6 回答 6

7

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

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

或者,您可以使用PATH_MAX和破坏对不是编译时常量或路径名可能长于该值的系统的可移植性(POSIX 也允许)。

于 2012-02-21T22:03:24.450 回答
3

其他答案没有提到它,但是有一个realpath功能,它完全符合您的要求,由 POSIX.1-2001 指定。

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

从手册页:

realpath() 扩展所有符号链接并解析对 /./、/../ 和由 path 命名的以 null 结尾的字符串中的额外“/”字符的引用,以生成规范化的绝对​​路径名。

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;
}
于 2014-12-13T15:19:17.640 回答
1

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

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

于 2012-02-21T21:37:22.593 回答
0

我有点疑惑为什么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等于缓冲区大小就继续调整其大小。

于 2012-02-21T22:07:00.967 回答
0

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

于 2012-02-21T22:09:07.077 回答
-1

你到底想用 lstat 实现什么?

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

char buffer[1024];
ssize_t r = readlink ("/proc/self/exe", buffer, 1024);
buffer[r] = 0;

printf ("%s\n", buffer);

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

于 2012-02-21T21:35:58.897 回答