Domanda

Problema:

Su una macchina Linux voglio leggere la stringa di destinazione di un collegamento.Dalla documentazione ho trovato il seguente codice di esempio (senza elaborazione degli errori):

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

Il problema è che sb.st_size restituisce 0 per i collegamenti sul mio sistema.

Allora come si alloca la memoria dinamicamente per readline su tali sistemi?

Molte grazie!


Una possibile soluzione:

Per riferimento futuro.Usando i punti fatti da 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
È stato utile?

Soluzione

POSIX dice che il campo st_size per un collegamento simbolico deve essere impostato sulla lunghezza del percorso nel collegamento (senza '\0').Tuttavia, il filesystem /proc su Linux non è conforme a POSIX.(Ha più violazioni di questa, come quando si leggono determinati file un byte alla volta.)

Puoi allocare un buffer di una certa dimensione, provare a readlink() e riprovare con un buffer più grande se il buffer non era abbastanza grande (readlink() ha restituito tanti byte quanti ne sono contenuti nel buffer), finché il buffer non è abbastanza grande.

In alternativa, puoi utilizzare PATH_MAX e interrompere la portabilità su sistemi in cui non è una costante del tempo di compilazione o in cui il percorso potrebbe essere più lungo di quello (POSIX lo consente).

Altri suggerimenti

Le altre risposte non lo menzionano, ma c'è la funzione realpath, che fa esattamente quello che vuoi, che è specificato da POSIX.1-2001.

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

dalla manpage:

realpath () espande tutti i collegamenti simbolici e risolve i riferimenti a /./, /../ e caratteri "/" aggiuntivi nella stringa con terminazione null denominata by path per produrre un percorso assoluto canonizzato.

realpath gestisce anche l'allocazione dinamica della memoria per te, se lo desideri.Di nuovo, estratto dalla manpage:

Se resolved_path è specificato come NULL, allora realpath () usa malloc (3) per allocare un buffer fino a PATH_MAX byte per contenere il file percorso percorso risolto e restituisce un puntatore a questo buffer.Il chiamante dovrebbe deallocare questo buffer usando free (3).

Come esempio semplice 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 non fornisce la risposta corretta su / proc.

Invece puoi malloc PATH_MAX o pathconf (_PC_PATH_MAX) byte.Dovrebbe essere sufficiente per la maggior parte dei casi.Se vuoi essere in grado di gestire percorsi più lunghi di quello, puoi chiamare readlink in un ciclo e riallocare il tuo buffer se il valore restituito da readlink indica che il buffer è troppo corto.Nota però che molte altre funzioni POSIX presumono semplicemente che PATH_MAX sia sufficiente.

Sono un po 'perplesso sul motivo per cui st_size è zero. Per POSIX:

Per i collegamenti simbolici, il membro st_mode deve contenere informazioni significative quando utilizzato con le macro del tipo di file. I bit della modalità file in st_mode non sono specificati. I membri della struttura st_ino, st_dev, st_uid, st_gid, st_atim, st_ctim e st_mtim devono avere valori significativi e il valore del membro st_nlink deve essere impostato sul numero di collegamenti (reali) al collegamento simbolico. Il valore del membro st_size deve essere impostato sulla lunghezza del percorso contenuto nel collegamento simbolico senza includere alcun byte nullo di terminazione.

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

Se st_size non funziona, penso che la tua unica opzione sia quella di allocare dinamicamente un buffer e continuare a ridimensionarlo più grande fintanto che il valore di ritorno di readlink è uguale alla dimensione del buffer.

La manpage per readlink(2) dice che verrà troncato silenziosamente se il buffer è troppo piccolo.Se vuoi veramente essere illimitato (e non ti dispiace pagare qualche costo per il lavoro extra) puoi iniziare con una data dimensione di allocazione e continuare ad aumentarla e riprovare la chiamata readlink.Puoi smettere di aumentare il buffer quando la chiamata successiva a readlink restituisce la stessa stringa che ha fatto per l'ultima iterazione.

Che cosa stai cercando di ottenere esattamente con lstat?

Dovresti essere in grado di ottenere l'obiettivo con solo quanto segue

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

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

Se stai cercando di ottenere la lunghezza della dimensione del nome del file, non credo che st_size sia la variabile giusta per questo ... Ma forse è una domanda diversa.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top