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().