Pregunta

Problema:

En una máquina Linux, quiero leer la cadena de destino de un enlace.De la documentación he encontrado el siguiente código de muestra (sin procesamiento de errores):

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);

El problema es que sb.st_size devuelve 0 para los enlaces en mi sistema.

Entonces, ¿cómo se asigna memoria dinámicamente para readline en tales sistemas?

¡Muchas gracias!


Una posible solución:

Para referencia futura.Usando los puntos hechos 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
¿Fue útil?

Solución

POSIX dice que el campo st_size para un enlace simbólico se establecerá en la longitud del nombre de la ruta en el enlace (sin '\0').Sin embargo, el sistema de archivos /proc en Linux no es compatible con POSIX.(Tiene más infracciones que solo esta, como cuando se leen ciertos archivos un byte a la vez).

Puede asignar un búfer de cierto tamaño, pruebe readlink() y vuelva a intentarlo con un búfer más grande si el búfer no era lo suficientemente grande (readlink() devolvió tantos bytes como caben en el búfer), hasta que el búfer sea lo suficientemente grande.

Alternativamente, puede usar PATH_MAX y romper la portabilidad en sistemas donde no es una constante en tiempo de compilación o donde la ruta puede ser más larga que eso (POSIX lo permite).

Otros consejos

Las otras respuestas no lo mencionan, pero existe la función realpath, que hace exactamente lo que quieres, que está especificada por POSIX.1-2001.

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

de la página de manual:

realpath () expande todos los enlaces simbólicos y resuelve las referencias a /./, /../ y caracteres '/' adicionales en la cadena terminada en nulo llamada por ruta para producir un nombre de ruta absoluto canonicalizado.

realpath también maneja la asignación de memoria dinámica por usted, si lo desea.Nuevamente, extracto de la página de manual:

Si resolve_path se especifica como NULL, realpath () usa malloc (3) para asignar un búfer de hasta PATH_MAX bytes para contener el nombre de ruta resuelto y devuelve un puntero a este búfer.Llamador debería desasignar este búfer usando free (3).

Como ejemplo simple y 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 no da la respuesta correcta en / proc.

En su lugar, puede malloc PATH_MAX o pathconf (_PC_PATH_MAX) bytes.Eso debería ser suficiente para la mayoría de los casos.Si desea poder manejar rutas más largas que eso, puede llamar a readlink en un bucle y reasignar su búfer si el valor de retorno de readlink indica que el búfer es demasiado corto.Sin embargo, tenga en cuenta que muchas otras funciones POSIX simplemente asumen que PATH_MAX es suficiente.

Estoy un poco desconcertado sobre por qué st_size es cero. Por POSIX:

Para enlaces simbólicos, el miembro st_mode debe contener información significativa cuando se usa con macros de tipo de archivo. Los bits del modo de archivo en st_mode no están especificados. Los miembros de la estructura st_ino, st_dev, st_uid, st_gid, st_atim, st_ctim y st_mtim deberán tener valores significativos y el valor del miembro st_nlink se establecerá en el número de enlaces (duros) al enlace simbólico. El valor del miembro st_size se establecerá con la longitud del nombre de ruta contenido en el enlace simbólico sin incluir ningún byte nulo de terminación.

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

Si st_size no funciona, creo que su única opción es asignar dinámicamente un búfer y seguir redimensionándolo siempre que el valor de retorno de readlink sea igual al tamaño del búfer.

La página de manual de readlink(2) dice que se truncará silenciosamente si el búfer es demasiado pequeño.Si realmente desea ser ilimitado (y no le importa pagar un costo por trabajo adicional), puede comenzar con un tamaño de asignación dado y seguir aumentando y volver a intentar la llamada readlink.Puede dejar de hacer crecer el búfer cuando la próxima llamada a readlink devuelva la misma cadena que lo hizo para la última iteración.

¿Qué está intentando conseguir exactamente con lstat?

Debería poder obtener el objetivo con solo lo siguiente

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

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

Si está tratando de obtener la longitud del tamaño del nombre del archivo, no creo que st_size sea la variable correcta para eso ... Pero esa es posiblemente una pregunta diferente.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top