Como usar readlink com alocação de memória dinâmica
Pergunta
Problema:
Em uma máquina Linux, desejo ler a string de destino de um link.Na documentação, encontrei o seguinte exemplo de código (sem processamento de erros):
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);
O problema é que sb.st_size retorna 0 para links em meu sistema.
Então, como alguém aloca memória dinamicamente para readline em tais sistemas?
Muito obrigado!
Uma solução possível:
Para referência futura.Usando os pontos feitos por 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
Solução
POSIX diz que o campo st_size
para um link simbólico deve ser definido para o comprimento do nome do caminho no link (sem '\0'
).No entanto, o sistema de arquivos /proc
no Linux não é compatível com POSIX.(Tem mais violações do que apenas esta, como ao ler certos arquivos um byte por vez.)
Você pode alocar um buffer de um determinado tamanho, tente readlink()
e tente novamente com um buffer maior se o buffer não for grande o suficiente (readlink()
retornou tantos bytes quanto cabem no buffer), até que o buffer seja grande o suficiente.
Alternativamente, você pode usar PATH_MAX
e interromper a portabilidade para sistemas onde não é uma constante de tempo de compilação ou onde o nome do caminho pode ser maior do que isso (POSIX permite qualquer um).
Outras dicas
As outras respostas não mencionam isso, mas existe a função realpath
, que faz exatamente o que você deseja, que é especificada por POSIX.1-2001.
char *realpath(const char *path, char *resolved_path);
na página de manual:
realpath () expande todos os links simbólicos e resolve as referências para /./, /../ e caracteres '/' extras na string terminada em nulo chamada por caminho para produzir um nome de caminho absoluto canônico.
realpath
também lida com a alocação de memória dinâmica para você, se desejar.Mais uma vez, trecho da página de manual:
Se resolvido_path for especificado como NULL, então realpath () usa malloc (3) para alocar um buffer de até PATH_MAX bytes para conter o nome do caminho resolvido e retorna um ponteiro para este buffer.O chamador deve desalocar este buffer usando free (3).
Como um exemplo simples e completo:
#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;
}
st_size não fornece a resposta correta em / proc.
Em vez disso, você pode malloc PATH_MAX ou pathconf (_PC_PATH_MAX) bytes.Isso deve ser suficiente para a maioria dos casos.Se você deseja ser capaz de lidar com caminhos mais longos do que isso, pode chamar readlink em um loop e realocar seu buffer se o valor de retorno readlink indicar que o buffer é muito curto.Observe, porém, que muitas outras funções POSIX simplesmente assumem que PATH_MAX é suficiente.
Estou um pouco intrigado por que st_size
é zero. Por POSIX:
Para links simbólicos, o membro st_mode deve conter informações significativas quando usado com as macros de tipo de arquivo. Os bits do modo de arquivo em st_mode não são especificados. Os membros da estrutura st_ino, st_dev, st_uid, st_gid, st_atim, st_ctim e st_mtim devem ter valores significativos e o valor do membro st_nlink deve ser definido para o número de links (físicos) para o link simbólico. O valor do membro st_size deve ser definido como o comprimento do nome do caminho contido no link simbólico, não incluindo qualquer byte nulo de terminação.
Fonte: http://pubs.opengroup.org/onlinepubs/9699919799/ functions / lstat.html
Se st_size
não funcionar, acho que sua única opção é alocar dinamicamente um buffer e continuar redimensionando-o, desde que o valor de retorno de readlink
seja igual ao tamanho do buffer.
A página do manual para readlink(2)
informa que irá truncar silenciosamente se o buffer for muito pequeno.Se você realmente deseja ser ilimitado (e não se importa em pagar algum custo por trabalho extra), pode começar com um determinado tamanho de alocação e continuar aumentando-o, tentando novamente a chamada readlink
.Você pode parar de aumentar o buffer quando a próxima chamada para readlink
retornar a mesma string que fez na última iteração.
O que exatamente você está tentando alcançar com o lstat?
Você deve ser capaz de obter o alvo apenas com o seguinte
char buffer[1024];
ssize_t r = readlink ("/proc/self/exe", buffer, 1024);
buffer[r] = 0;
printf ("%s\n", buffer);
Se você está tentando obter o comprimento do tamanho do nome do arquivo, não acho que st_size seja a variável certa para isso ... Mas essa é possivelmente uma pergunta diferente.