Diapositiva 1 - Valter Crescenzi
Transcript
Diapositiva 1 - Valter Crescenzi
Corso di Programmazione Concorrente Gestione Risorse UNIX Valter Crescenzi http://crescenzi.dia.uniroma3.it Controllo degli Accessi alle Risorse in UNIX Modello semplice ed efficace Utenti Gli utenti UNIX sono raccolti in gruppi Ciascun utente può afferire in più gruppi Ogni utente possiede un userid Ogni gruppo possiede un groupid Risorse sono possedute da un utente sono possedute da un gruppo hanno un userid ed un groupid Permessi UNIX I permessi di ogni risorsa sono descritti da una maschera di nove bit, tre gruppi di tre bit relativi ai permessi: del possessore della risorsa di chi afferisce allo stesso gruppo della risorsa di tutti gli altri utente gruppo r r w x w x resto r w x Permessi di un Processo sulle Risorse Processo attivato da un utente eredita: user identifier group identifier Tutte le risorse da lui allocate ereditano il suo userid e groupid Risorse file semafori segmenti di memoria condivisa … Esempio Processo A (uid=505 guid=100) crea un segmento in shared memory con diritti rw-rw-r-- (0664): Processo B (uid=507 guid=100) accede al segmento: stesso user id = lettura/scrittura stesso group id = lettura/scrittura altri = solo lettura scrittura ok lettura ok Processo C (uid=510 guid=171) accede al segmento: lettura ok Chiamate di Sistema per I/O #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> Consultare le man pages! int creat(char *path, mode_t mode); mode_t umask(mode_t mask); int unlink(char *path); int rename(char *old, char *new); int open(char *path, int oflag, ...); int close(int fd); int read(int fd, char *buf, unsigned nbyte); int write(int fd, char *buf, unsigned nbyte); off_t lseek(int fd, off_t offset, int whence); int link(char *path1, char *path2); int chmod(char *path, mode_t mode); int stat(char *path, struct stat *buf); int access(const char *pathname, int mode); int mkdir(const char *pathname, mode_t mode);qq Descrittori di File int open(char *path, int oflag); Un descrittore di file è un intero non negativo usato dal SO come indice di una tabella dei descrittori I primi tre sono predefiniti: 0 stdin (standard input) 1 stdout (standard output) 2 stderr (standard error) N.B. le funzioni che creano nuovi descrittori scelgono sempre il più piccolo intero disponibile Tabella dei Descrittori dei File Il SO tiene traccia dei file aperti da ciascun processo mantenendo aggiornata una tabella dei descrittori di file Un descrittore di file permette di referenziare la struttura dati che il file-system usa per gestire i file Non ci interessa conoscerne il dettaglio. Infatti: Una volta ottenuto un descrittore tramite la open(), la create(), o la pipe() (ed anche la socket()), per le operazioni successive si specifica il file mediante il suo descrittore un processo eredita dal padre la propria tabella dei descrittori Tabella dei Descrittori dei File Standard Input 0 Terminal Input Standard Output 1 Terminal Output Standard Error 2 Terminal Output 3 × 4 fd … Apertura/Creazione/Chiusura #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int int int int man 2 open creat(char *path, mode_t mode); open(char *path, int flag); open(char *path, int flag, mode_t mode); close(int fd); La open() riceve il nome del file da aprire o creare, i permessi mode e la modalità di apertura flag La creat() è equivalente ad una open() con flag=O_CREAT|O_WRONLY|O_TRUNC restituiscono un descrittore di file -1 in caso di errore flag: O_RDONLY O_WRONLY O_RDWR O_CREAT O_TRUNC O_APPEND O_EXCL … mode: S_IRUSR S_IWUSR S_IXUSR S_IRGRP S_IWGRP S_IXGRP S_IROTH S_IWOTH S_IXOTH … 0400 0200 0100 0040 0020 0010 0004 0002 0001 create-file.c …include omessi… int main (int argc, char* argv[]) { /* The path at which to create the new file. char* path = argv[1]; */ /* The permissions for the new file. */ mode_t mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH; /* Create the file. */ int fd = open (path, O_WRONLY|O_EXCL|O_CREAT, mode); if (fd == -1) { // An error occurred perror ("open"); return 1; } return 0; } create-file.c: esempio di esecuzione $ gcc create-file.c -o create-file $ ./create-file testfile $ ls -l create-file -rwxr-xr-x 1 crescenz crescenz $ ./create-file testfile open: File exists $ 11575 May 2 12:55 create-file Permessi dei Nuovi File mode_t umask(mode_t mask); Per motivi di sicurezza ad ogni processo è associata una maschera di nove bit (umask) che disabilita automaticamente alcuni dei permessi dei nuovi file umask(S_IRWXO|S_IWGRP) (od anche da shell umask specifica che i bit ad 1 sono i permessi da disabilitare automaticamente 027) i permessi del possessore del file non sono mascherati la scrittura è disabilitata per gli afferenti al gruppo del file tutti i permessi sono negati per tutti gli altri La funzione restituisce il vecchio valore della umask Cambiare i Permessi #include <sys/type<s.h> #include <sys/stat.h> int chmod(const char *path, mode_t mode); int fchmod(int fildes, mode_t mode); cambia i permessi del file specificato (tramite nome path, o descrittore di file fildes) a mode mode: S_IRUSR S_IWUSR S_IXUSR S_IRGRP S_IWGRP 0400 0200 0100 0040 0020 S_IXGRP S_IROTH S_IWOTH S_IXOTH 0010 0004 0002 0001 S_ISUID 04000 set user ID on exec S_ISGID 02000 set group ID on exec S_ISVTX 01000 sticky bit Controllare i Permessi #include <unistd.h> int access(const char *pathname, int mode); controlla che il processo chiamante può accedere al file specificato da pathname secondo le modalità espresse in mode: R_OK: permesso di lettura W_OK: permesso di scrittura X_OK: permesso di esecuzione F_OK: controlla l’esistenza del file restituisce 0 in caso affermativo, -1 in caso contrario check-access.c (1) #include <errno.h> #include <stdio.h> #include <unistd.h> int main (int argc, char* argv[]) { char* path = argv[1]; int rval; rval = access (path, F_OK); //Check file existence if (rval == 0) printf ("%s exists\n", path); else { if (errno == ENOENT) printf ("%s does not exist\n", path); else if (errno == EACCES) printf ("%s is not accessible\n", path); return 0; } … check-access.c (2) … rval = access (path, R_OK); // Check read access if (rval == 0) printf ("%s is readable\n", path); else printf ("%s is not readable (access denied)\n", path); rval = access (path, W_OK); // Check write access if (rval == 0) printf ("%s is writable\n", path); else if (errno == EACCES) printf ("%s is not writable (access denied)\n", path); else if (errno == EROFS) printf ("%s is not writable (read-only filesystem)\n", path); return 0; } check-access.c: Esempio di Esecuzione $ gcc check-access.c -o check-access $ ./check-access /mnt/cdrom/README.txt /mnt/cdrom/README.txt exists /mnt/cdrom/README.txt is readable /mnt/cdrom/README.txt is not writable ( read-only $ filesystem) Lettura #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> man 2 read ssize_t read(int fd, void *buf, size_t count); Legge dal file fd e copia in buf un max di count byte restituisce 0 per EOF, -1 se c’è un errore, oppure il numero di byte effettivamente letti N.B. se fd corrisponde ad un terminale, si consideri che l’input dalla tastiera è trasmesso solo alla ricezione di un carattere di new line hexdump.c …include omessi… int main (int argc, char* argv[]) { unsigned char buffer[16]; size_t offset = 0, bytes_read; int i; int fd = open (argv[1], O_RDONLY); /* Read from the file, one chunk at a time. Continue until it reads less than we asked for. This indicates EOF. */ do { bytes_read = read (fd, buffer, sizeof (buffer)); printf ("0x%06x : ", offset); // Print offset and bytes for (i = 0; i < bytes_read; ++i) printf ("%02x ", buffer[i]); printf ("\n"); offset += bytes_read; // update our position in the file } while (bytes_read == sizeof (buffer)); close (fd); return 0; } hexdump.c: Esempio di Esecuzione [crescenz@diadema]$ 0x000000 : 7f 45 4c 0x000010 : 02 00 03 0x000020 : 5c 1e 00 0x000030 : 21 00 1e ... ... ./hexdump hexdump 46 01 01 01 00 00 00 01 00 00 00 30 00 00 00 00 00 34 00 06 00 00 00 34 00 83 00 00 00 04 20 00 00 08 00 00 00 34 07 34 00 00 00 80 00 00 28 04 00 00 00 08 Scrittura #include <sys/types.h> man 2 write #include <sys/stat.h> #include <fcntl.h> ssize_t write(int fd, const void *buf, size_t count) Scrive sul file fd e copia da buf un max di count byte restituisce -1 se c’è un errore, oppure il numero di byte effettivamente scritti nel caso che siano stati scritti meno byte di quanti richiesti, bisogna ripetere la chiamate per i byte rimanenti esistono molti possibili errori write-all.c #include <assert.h> #include <unistd.h> /* Write all of COUNT bytes from BUFFER to file descr. FD. Returns -1 on error, or the number of bytes written. */ ssize_t write_all (int fd, const void* buffer, size_t count) { size_t left_to_write = count; while (left_to_write > 0) { size_t written = write (fd, buffer, count); if (written == -1) // An error occurred; bail return -1; else /* Keep count of how much more we need to write. */ left_to_write -= written; } /* We should have written no more than COUNT bytes! */ assert (left_to_write == 0); /* The number of bytes written is exactly COUNT. */ return count; } Ottenere Informazioni su un File #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> int stat(const char *file_name, struct stat *buf); int fstat(int filedes, struct stat *buf); int lstat(const char *file_name, struct stat *buf); restituisce nella struttura stat informazioni sul file specificato (tramite nome file_name, o descrittore di file filedes) restituisce 0 in caso di successo, -1 in caso di errore usare lstat su di un link simbolico perché stat dereferenzia il link automaticamente La Struttura stat I campi più utili sono st_mode: i permessi del file st_uid: uid del possessore st_gid: gid del possessore st_size: dimensioni in byte st_atime: tempo dell’ultimo accesso st_mtime: tempo dell’ultima modifica Inoltre st_mode contiene altre informazioni sul tipo di file accessibili con macro booleane S_ISBLK(mode) è un dispositivo a blocchi? S_ISCHR(mode) è un dispositivo a caratteri? S_ISDIR(mode) è una directory? S_ISFIFO(mode) è una pipe con nome? S_ISLNK(mode) è link simbolico? S_ISREG(mode) è file ordinario? S_ISSOCK(mode) è una socket? read-file.c char* read_file (const char* filename, size_t* length) { int fd; struct stat file_info; char* buffer; fd = open (filename, O_RDONLY); fstat (fd, &file_info); // Get info about the file *length = file_info.st_size; if (!S_ISREG (file_info.st_mode)) { close (fd); // It’s not an ordinary file, give up return NULL; } buffer = (char*) malloc (*length); //All the file… read (fd, buffer, *length); // …into the buffer close (fd); return buffer; } Low Level I/O e Standard C Library (1) Le funzioni della Standard C Library sono implementate sopra le chiamate di sistema fornite dal Kernel per il Low Level I/O E’ possibile utilizzare funzioni di entrambe le tipologie convertendo i descrittori di file agli stream FILE* della libreria standard e viceversa Da stream a descrittore di file: FILE* stream = fopen(filename, “w”); int file_descriptor = fileno(stream); write(file_descriptor, buffer, count); Low Level I/O e Standard C Library (2) Quindi fileno() permette di accedere ad un descrittore di file sottostante uno stream Viceversa, fdopen() dato un descrittore gli costruisce sopra uno stream Da descrittore di file a stream: int file_descriptor = open(filename,O_WRONLY|O_CREATE); FILE* stream = fdopen(file_descriptor,“w”); fprintf(stream, “%d”, 123); Ovviamente chiudendo un descrittore non è possibile più usare lo stream da cui è stato derivato e viceversa sendfile: Trasferimento Dati Efficiente #include <sys/sendfile.h> ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count); Funzione che rende possibile copiare un file direttamente dal descrittore in_fd al descrittore out_fs count: il numero di byte da copiare offset: offset sul file di input a partire dal quale leggere Senza questa funzione la copia richiederebbe un buffer temporaneo per allocare i dati letti prima di copiarli nella destinazione Soluzione efficiente perché elimina molte chiamate di sistema copy.c …include omessi… int main (int argc, char* argv[]) { int read_fd; int write_fd; struct stat stat_buf; off_t offset = 0; read_fd = open(argv[1], O_RDONLY); fstat(read_fd, &stat_buf); write_fd = open(argv[2],O_WRONLY|O_CREAT,stat_buf.st_mode); sendfile(write_fd,read_fd,&offset,stat_buf.st_size); close(read_fd); close(write_fd); return 0; } Esercizi Esercizio: scrivere un programma che trovi tutti i file con permessi tali da poter essere acceduti da estranei in una directory data od in una delle sue sottodirectory, ricorsivamente. Esercizio: scrivere un programma per trovare un file di nome dato in una directory od in una delle sue sottodirectory, ricorsivamente. Esercizio: confrontare le prestazioni di un programma che copia un file utilizzando un buffer temporaneo rispetto ad un analogo programma che usa la funzione sendfile().