Sto provando a scrivere un piccolo programma C che emula il comando unix ls -l
. Per fare ciò, sto usando il syscall stat(2)
e ho avuto una piccola intesa scrivendo i permessi. Ho una variabile mode_t
che detiene i permessi dei file da st_mode
e non sarebbe difficile analizzare questo valore nella rappresentazione della stringa s, ma mi stavo chiedendo se c'è un modo migliore per farlo.Stampa dei permessi dei file come 'ls -l' usando stat (2) in C
risposta
esempio da google
#include <unistd.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
int main(int argc, char **argv)
{
if(argc != 2)
return 1;
struct stat fileStat;
if(stat(argv[1],&fileStat) < 0)
return 1;
printf("Information for %s\n",argv[1]);
printf("---------------------------\n");
printf("File Size: \t\t%d bytes\n",fileStat.st_size);
printf("Number of Links: \t%d\n",fileStat.st_nlink);
printf("File inode: \t\t%d\n",fileStat.st_ino);
printf("File Permissions: \t");
printf((S_ISDIR(fileStat.st_mode)) ? "d" : "-");
printf((fileStat.st_mode & S_IRUSR) ? "r" : "-");
printf((fileStat.st_mode & S_IWUSR) ? "w" : "-");
printf((fileStat.st_mode & S_IXUSR) ? "x" : "-");
printf((fileStat.st_mode & S_IRGRP) ? "r" : "-");
printf((fileStat.st_mode & S_IWGRP) ? "w" : "-");
printf((fileStat.st_mode & S_IXGRP) ? "x" : "-");
printf((fileStat.st_mode & S_IROTH) ? "r" : "-");
printf((fileStat.st_mode & S_IWOTH) ? "w" : "-");
printf((fileStat.st_mode & S_IXOTH) ? "x" : "-");
printf("\n\n");
printf("The file %s a symbolic link\n", (S_ISLNK(fileStat.st_mode)) ? "is" : "is not");
return 0;
}
risultato:
Information for 2.c --------------------------- File Size: 1223 bytes Number of Links: 1 File inode: 39977236 File Permissions: -rw-r--r-- The file is not a symbolic link
Le basi sono abbastanza semplici; i bit più difficili sono i bit SUID e SGID e il bit appiccicoso, che modifica i bit 'x'. Prendi in considerazione di suddividere le autorizzazioni in 3 cifre ottali per utente, gruppo, proprietario e utilizzale per indicizzare in una matrice di stringhe di 3 caratteri come rwx
e ---
. Quindi regolare i bit x
appropriati in base agli altri bit di modalità. Il tipo di file dovrà essere trattato separatamente, ma è possibile utilizzare uno spostamento a 12 bit a destra (possibilmente con mascheramento) e una tabella di 16 voci per gestire i 16 possibili valori (non tutti sono validi su un dato sistema) . Oppure puoi gestire tipi noti come mostrato nel codice qui sotto.
+----+---+---+---+---+
|type|SSS|USR|GRP|OTH|
+----+---+---+---+---+
I 4 tipi punte, le tre S-bit (setuid, setgid, appiccicoso) e l'utente, gruppo e altri bit.
Questo è il codice che uso per convertire mode_t
in una stringa. È stato scritto per un programma ben threadless, quindi utilizza dati statici; sarebbe banale per modificarlo per prendere la stringa di output come parametro di input:
/* Convert a mode field into "ls -l" type perms field. */
static char *lsperms(int mode)
{
static const char *rwx[] = {"---", "--x", "-w-", "-wx",
"r--", "r-x", "rw-", "rwx"};
static char bits[11];
bits[0] = filetypeletter(mode);
strcpy(&bits[1], rwx[(mode >> 6)& 7]);
strcpy(&bits[4], rwx[(mode >> 3)& 7]);
strcpy(&bits[7], rwx[(mode & 7)]);
if (mode & S_ISUID)
bits[3] = (mode & S_IXUSR) ? 's' : 'S';
if (mode & S_ISGID)
bits[6] = (mode & S_IXGRP) ? 's' : 'l';
if (mode & S_ISVTX)
bits[9] = (mode & S_IXOTH) ? 't' : 'T';
bits[10] = '\0';
return(bits);
}
static int filetypeletter(int mode)
{
char c;
if (S_ISREG(mode))
c = '-';
else if (S_ISDIR(mode))
c = 'd';
else if (S_ISBLK(mode))
c = 'b';
else if (S_ISCHR(mode))
c = 'c';
#ifdef S_ISFIFO
else if (S_ISFIFO(mode))
c = 'p';
#endif /* S_ISFIFO */
#ifdef S_ISLNK
else if (S_ISLNK(mode))
c = 'l';
#endif /* S_ISLNK */
#ifdef S_ISSOCK
else if (S_ISSOCK(mode))
c = 's';
#endif /* S_ISSOCK */
#ifdef S_ISDOOR
/* Solaris 2.6, etc. */
else if (S_ISDOOR(mode))
c = 'D';
#endif /* S_ISDOOR */
else
{
/* Unknown type -- possibly a regular file? */
c = '?';
}
return(c);
}
Apprezzo la profondità fornita dalla tua risposta! Impara qualcosa di nuovo ogni giorno! – cheezone
clang '' -Weverything'' si è giustamente lamentato :) –
@ ArranCudbard-Bell: ho ripulito un corpus di comandi con qualcosa di analogo a "clang -Weverything" e può essere un po 'doloroso a volte. In realtà non ho provato direttamente "clang -Weverything"; potrebbe essere meno oneroso delle opzioni che sto usando (circa 18 '-W *' flags; '-Wconversion' è la più grande causa di problemi per il mio codice). –
Non amo la sintassi if/ else if
.
Preferisco usare l'istruzione switch
. Dopo aver lottato un po 'ho trovato il modo in cui possiamo farlo usando diverse macro, ad esempio:
S_ISCHR (mode)
è equivalente a:
((mode & S_IFMT) == S_IFCHR)
Questo ci permette di costruire un'istruzione switch in questo modo:
char f_type(mode_t mode)
{
char c;
switch (mode & S_IFMT)
{
case S_IFBLK:
c = 'b';
break;
case S_IFCHR:
c = 'c';
break;
case S_IFDIR:
c = 'd';
break;
case S_IFIFO:
c = 'p';
break;
case S_IFLNK:
c = 'l';
break;
case S_IFREG:
c = '-';
break;
case S_IFSOCK:
c = 's';
break;
default:
c = '?';
break;
}
return (c);
}
Che a mio parere è un po 'più elegante dell'approccio if/else if
.
Hai scaricato il codice assembly per determinare quale è più efficiente? (ad esempio 'gcc -S -masm = intel -O2 -o filemode.asm filemode.c') –
Grazie per la risposta. Questo ha aiutato un sacco. – cheezone
Si noti che poiché il codice usa 'stat()' invece di 'lstat()', l'unica volta che segnalerà 'symlink' è quando il collegamento simbolico è interrotto. Altrimenti, segnalerà il file alla fine del collegamento simbolico. –