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
Foi útil?

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.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top