Как использовать readlink с динамическим распределением памяти - PullRequest
4 голосов
/ 22 февраля 2012

Проблема:

На компьютере с 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 в таких системах?

Большое спасибо!


Одно из возможных решений:

Для дальнейшего использования.Используя очки, сделанные Джиллесом:

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

Ответы [ 6 ]

6 голосов
/ 22 февраля 2012

POSIX говорит, что поле st_size для символической ссылки должно быть установлено на длину пути в ссылке (без '\0').Однако файловая система /proc в Linux не совместима с POSIX.(Он имеет больше нарушений, чем только этот, например, при чтении определенных файлов по одному байту за раз.)

Вы можете выделить буфер определенного размера, попробуйте readlink() и повторите попытку с большим буфером, еслибуфер был недостаточно велик (readlink() вернул столько байтов, сколько умещалось в буфере), пока буфер не стал достаточно большим.

В качестве альтернативы вы можете использовать PATH_MAX и нарушать переносимость в системы, где его нет.константа времени компиляции или где путь может быть длиннее этого (POSIX разрешает либо).

2 голосов
/ 13 декабря 2014

Другие ответы не упоминают об этом, но есть функция realpath, которая делает именно то, что вы хотите, что указано в POSIX.1-2001.

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

с man-страницы:

realpath () разворачивает все символические ссылки и разрешает ссылки на /./, /../ и дополнительные символы '/' в строке с нулевым символом в конце с именем по пути для создания канонизированного абсолютного пути.

realpath также обрабатывает динамическое выделение памяти, если хотите. Опять же, выдержка из справочной страницы:

Если resolved_path указан как 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;
}
1 голос
/ 22 февраля 2012

st_size не дает правильного ответа для /proc.

Вместо этого вы можете использовать malloc PATH_MAX или pathconf (_PC_PATH_MAX) байтов.Этого должно быть достаточно для большинства случаев.Если вы хотите иметь возможность обрабатывать пути длиннее этого, вы можете вызвать readlink в цикле и перераспределить ваш буфер, если возвращаемое значение readlink указывает, что буфер слишком короткий.Обратите внимание, что многие другие функции POSIX просто предполагают, что PATH_MAX достаточно.

0 голосов
/ 22 февраля 2012

Справочная страница для readlink(2) говорит, что она будет молча усекаться, если буфер слишком мал. Если вы действительно хотите быть неограниченным (и не возражаете заплатить некоторую стоимость за дополнительную работу), вы можете начать с заданного размера выделения и продолжать увеличивать его, а затем повторить попытку вызова readlink. Вы можете остановить рост буфера, когда следующий вызов readlink вернет ту же строку, что и для последней итерации.

0 голосов
/ 22 февраля 2012

Я немного озадачен, почему 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 равно размеру буфера.

0 голосов
/ 22 февраля 2012

Чего именно вы пытаетесь достичь с помощью lstat?

Вы должны быть в состоянии получить цель с помощью следующих

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

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

Если вы пытаетесь определить длину размера имени файла, я не думаю, что st_size - правильная переменная для этого ... Но, возможно, это другой вопрос.

...