X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=blobdiff_plain;f=filedir.tex;h=481ef534fd92d789e0b1fcebd06d648cf775a14a;hp=d811bfccee48f6f716d831bd1cb07487bb5be0b9;hb=adbc2a4478d606025705a406603e6097584b9fa0;hpb=056bbc90c8a0710b57fa7b13f5f0dfdad1b3ff3f diff --git a/filedir.tex b/filedir.tex index d811bfc..481ef53 100644 --- a/filedir.tex +++ b/filedir.tex @@ -1,61 +1,1437 @@ -\chapter{Files e directories} +%% filedir.tex +%% +%% Copyright (C) 2000-2002 Simone Piccardi. Permission is granted to +%% copy, distribute and/or modify this document under the terms of the GNU Free +%% Documentation License, Version 1.1 or any later version published by the +%% Free Software Foundation; with the Invariant Sections being "Prefazione", +%% with no Front-Cover Texts, and with no Back-Cover Texts. A copy of the +%% license is included in the section entitled "GNU Free Documentation +%% License". +%% +\chapter{File e directory} \label{cha:files_and_dirs} In questo capitolo tratteremo in dettaglio le modalità con cui si gestiscono -files e directories, ed in particolare esamineremo come è strutturato il -sistema base di protezioni e controllo di accesso ai files, e tutta -l'interfaccia che permette la manipolazione dei vari attributi di files e -directories. Tutto quello che riguarda invece la manipolazione dei contenuti è -lasciato ai capitoli successivi. +file e directory, iniziando dalle funzioni di libreria che si usano per +copiarli, spostarli e cambiarne i nomi. Esamineremo poi l'interfaccia che +permette la manipolazione dei vari attributi di file e directory ed alla fine +faremo una trattazione dettagliata su come è strutturato il sistema base di +protezioni e controllo dell'accesso ai file e sulle funzioni che ne permettono +la gestione. Tutto quello che riguarda invece la manipolazione del contenuto +dei file è lasciato ai capitoli successivi. -\section{La manipolazione delle caratteristiche dei files} -\label{sec:filedir_infos} -Come spiegato in \secref{sec:fileintr_filesystem} tutte le informazioni -generali relative alle caratteristiche di ciascun file sono mantenute -nell'inode. Vedremo in questa sezione come sia possibile accedervi usando la -funzione \texttt{stat} ed esamineremo alcune funzioni utilizzabili per -manipolare una parte di questa informazione. Tutto quello che invece riguarda -il meccanismo di controllo di accesso ad i file e le relative funzioni di -manipolazione sarà invece esaminanto in \secref{sec:filedir_access_control}. +\section{La gestione di file e directory} +\label{sec:file_dir} +Come già accennato in \secref{sec:file_filesystem} in un sistema unix-like la +gestione dei file ha delle caratteristiche specifiche che derivano +direttamente dall'architettura del sistema. -\subsection{Le funzioni \texttt{stat}, \texttt{fstat} e \texttt{lstat}} -\label{sec:filedir_stat} +In questa sezione esamineremo le funzioni usate per la manipolazione di file e +directory, per la creazione di link simbolici e diretti, per la gestione e la +lettura delle directory. -La lettura delle informazioni relative ai file è fatta attraverso la famiglia -delle funzioni \texttt{stat}, questa è la funzione che il comando \texttt{ls} -usa per poter stampare tutti i dati dei files; il prototipo della funzione è -il seguente; +In particolare ci soffermeremo sulle conseguenze che derivano +dall'architettura dei filesystem illustrata nel capitolo precedente per quanto +riguarda il comportamento delle varie funzioni. + + +\subsection{Le funzioni \func{link} e \func{unlink}} +\label{sec:file_link} + +Una caratteristica comune a diversi sistemi operativi è quella di poter creare +dei nomi fittizi (come gli alias del MacOS o i collegamenti di Windows o i +nomi logici del VMS) che permettono di fare riferimento allo stesso file +chiamandolo con nomi diversi o accedendovi da directory diverse. + +Questo è possibile anche in ambiente Unix, dove tali collegamenti sono +usualmente chiamati \textit{link}; ma data l'architettura del sistema riguardo +la gestione dei file (ed in particolare quanto trattato in +\secref{sec:file_arch_func}) ci sono due metodi sostanzialmente diversi per +fare questa operazione. + +Come spiegato in \secref{sec:file_filesystem} l'accesso al contenuto di un +file su disco avviene passando attraverso il suo inode\index{inode}, che è la +struttura usata dal kernel che lo identifica univocamente all'interno di un +singolo filesystem. Il nome del file che si trova nella voce di una directory +è solo un'etichetta, mantenuta all'interno della directory, che viene +associata ad un puntatore che fa riferimento al suddetto inode. + +Questo significa che, fintanto che si resta sullo stesso filesystem, la +realizzazione di un link è immediata, ed uno stesso file può avere tanti nomi +diversi, dati da altrettante diverse associazioni allo stesso +inode\index{inode} di etichette diverse in directory diverse. Si noti anche +che nessuno di questi nomi viene ad assumere una particolare preferenza o +originalità rispetto agli altri, in quanto tutti fanno comunque riferimento +allo stesso inode\index{inode}. + +Per aggiungere ad una directory una voce che faccia riferimento ad un +inode\index{inode} già esistente si utilizza la funzione \func{link}; si suole +chiamare questo tipo di associazione un collegamento diretto (o \textit{hard + link}). Il prototipo della funzione è: \begin{prototype}{unistd.h} -{int stat(const char *file\_name, struct stat *buf)} +{int link(const char *oldpath, const char *newpath)} + Crea un nuovo collegamento diretto. + + \bodydesc{La funzione restituisce 0 in caso di successo e -1 in caso di + errore nel qual caso \var{errno} viene impostata ai valori: + \begin{errlist} + \item[\errcode{EXDEV}] \param{oldpath} e \param{newpath} non sono sullo + stesso filesystem. + \item[\errcode{EPERM}] il filesystem che contiene \param{oldpath} e + \param{newpath} non supporta i link diretti o è una directory. + \item[\errcode{EEXIST}] un file (o una directory) con quel nome esiste di + già. + \item[\errcode{EMLINK}] ci sono troppi link al file \param{oldpath} (il + numero massimo è specificato dalla variabile \const{LINK\_MAX}, vedi + \secref{sec:sys_limits}). + \end{errlist} + ed inoltre \errval{EACCES}, \errval{ENAMETOOLONG}, \errval{ENOTDIR}, + \errval{EFAULT}, \errval{ENOMEM}, \errval{EROFS}, \errval{ELOOP}, + \errval{ENOSPC}, \errval{EIO}.} +\end{prototype} + +La funzione crea sul pathname \param{newpath} un collegamento diretto al file +indicato da \param{oldpath}. Per quanto detto la creazione di un nuovo +collegamento diretto non copia il contenuto del file, ma si limita a creare +una voce nella directory specificata da \param{newpath} e ad aumentare di uno +il numero di riferimenti al file (riportato nel campo \var{st\_nlink} della +struttura \struct{stat}, vedi \secref{sec:file_stat}) aggiungendo il nuovo +nome ai precedenti. Si noti che uno stesso file può essere così chiamato con +vari nomi in diverse directory. + +Per quanto dicevamo in \secref{sec:file_filesystem} la creazione di un +collegamento diretto è possibile solo se entrambi i pathname sono nello stesso +filesystem; inoltre il filesystem deve supportare i collegamenti diretti (il +meccanismo non è disponibile ad esempio con il filesystem \acr{vfat} di +Windows). + +La funzione inoltre opera sia sui file ordinari che sugli altri oggetti del +filesystem, con l'eccezione delle directory. In alcune versioni di Unix solo +l'amministratore è in grado di creare un collegamento diretto ad un'altra +directory: questo viene fatto perché con una tale operazione è possibile +creare dei \textit{loop} nel filesystem (vedi l'esempio mostrato in +\secref{sec:file_symlink}, dove riprenderemo il discorso) che molti programmi +non sono in grado di gestire e la cui rimozione diventerebbe estremamente +complicata (in genere per questo tipo di errori occorre far girare il +programma \cmd{fsck} per riparare il filesystem). + +Data la pericolosità di questa operazione e la disponibilità dei link +simbolici che possono fornire la stessa funzionalità senza questi problemi, +nei filesystem usati in Linux questa caratteristica è stata completamente +disabilitata, e al tentativo di creare un link diretto ad una directory la +funzione restituisce l'errore \errcode{EPERM}. + +La rimozione di un file (o più precisamente della voce che lo referenzia +all'interno di una directory) si effettua con la funzione \funcd{unlink}; il +suo prototipo è il seguente: +\begin{prototype}{unistd.h}{int unlink(const char *pathname)} + + Cancella un file. + + \bodydesc{La funzione restituisce zero in caso di successo e -1 per un + errore, nel qual caso il file non viene toccato. La variabile + \var{errno} viene impostata secondo i seguenti codici di errore: + \begin{errlist} + \item[\errcode{EISDIR}] \param{pathname} si riferisce ad una directory. + \footnotemark + \item[\errcode{EROFS}] \param{pathname} è su un filesystem montato in sola + lettura. + \item[\errcode{EISDIR}] \param{pathname} fa riferimento a una directory. + \end{errlist} + ed inoltre: \errval{EACCES}, \errval{EFAULT}, \errval{ENOENT}, + \errval{ENOTDIR}, \errval{ENOMEM}, \errval{EROFS}, \errval{ELOOP}, + \errval{EIO}.} +\end{prototype} + +\footnotetext{questo è un valore specifico ritornato da Linux che non consente + l'uso di \func{unlink} con le directory (vedi \secref{sec:file_remove}). Non + è conforme allo standard POSIX, che prescrive invece l'uso di + \errcode{EPERM} in caso l'operazione non sia consentita o il processo non + abbia privilegi sufficienti.} + +La funzione cancella il nome specificato da \param{pathname} nella relativa +directory e decrementa il numero di riferimenti nel relativo +inode\index{inode}. Nel caso di link simbolico cancella il link simbolico; nel +caso di socket\index{socket}, fifo o file di dispositivo\index{file!di + dispositivo} rimuove il nome, ma come per i file i processi che hanno aperto +uno di questi oggetti possono continuare ad utilizzarlo. + +Per cancellare una voce in una directory è necessario avere il permesso di +scrittura su di essa, dato che si va a rimuovere una voce dal suo contenuto, e +il diritto di esecuzione sulla directory che la contiene (affronteremo in +dettaglio l'argomento dei permessi di file e directory in +\secref{sec:file_access_control}). Se inoltre lo \textit{sticky} bit (vedi +\secref{sec:file_sticky}) è impostato occorrerà anche essere proprietari del +file o proprietari della directory (o root, per cui nessuna delle restrizioni +è applicata). + +Una delle caratteristiche di queste funzioni è che la creazione/rimozione del +nome dalla directory e l'incremento/decremento del numero di riferimenti +nell'inode\index{inode} devono essere effettuati in maniera atomica (si veda +\secref{sec:proc_atom_oper}) senza possibili interruzioni fra le due +operazioni. Per questo entrambe queste funzioni sono realizzate tramite una +singola system call. + +Si ricordi infine che un file non viene eliminato dal disco fintanto che tutti +i riferimenti ad esso sono stati cancellati: solo quando il \textit{link + count} mantenuto nell'inode\index{inode} diventa zero lo spazio occupato su +disco viene rimosso (si ricordi comunque che a questo si aggiunge sempre +un'ulteriore condizione,\footnote{come vedremo in + \secref{cha:file_unix_interface} il kernel mantiene anche una tabella dei + file aperti nei vari processi, che a sua volta contiene i riferimenti agli + inode ad essi relativi. Prima di procedere alla cancellazione dello spazio + occupato su disco dal contenuto di un file il kernel controlla anche questa + tabella, per verificare che anche in essa non ci sia più nessun riferimento + all'inode in questione.} e cioè che non ci siano processi che abbiano il +suddetto file aperto). + +Questa proprietà viene spesso usata per essere sicuri di non lasciare file +temporanei su disco in caso di crash dei programmi; la tecnica è quella di +aprire il file e chiamare \func{unlink} subito dopo, in questo modo il +contenuto del file è sempre disponibile all'interno del processo attraverso il +suo file descriptor (vedi \secref{sec:file_fd}) fintanto che il processo non +chiude il file, ma non ne resta traccia in nessuna directory, e lo spazio +occupato su disco viene immediatamente rilasciato alla conclusione del +processo (quando tutti i file vengono chiusi). + + +\subsection{Le funzioni \func{remove} e \func{rename}} +\label{sec:file_remove} + +Al contrario di quanto avviene con altri Unix, in Linux non è possibile usare +\func{unlink} sulle directory; per cancellare una directory si può usare la +funzione \func{rmdir} (vedi \secref{sec:file_dir_creat_rem}), oppure la +funzione \funcd{remove}. + +Questa è la funzione prevista dallo standard ANSI C per cancellare un file o +una directory (e funziona anche per i sistemi che non supportano i link +diretti). Per i file è identica a \func{unlink} e per le directory è identica +a \func{rmdir}; il suo prototipo è: +\begin{prototype}{stdio.h}{int remove(const char *pathname)} + Cancella un nome dal filesystem. + + \bodydesc{La funzione restituisce zero in caso di successo e -1 per un + errore, nel qual caso il file non viene toccato. + + I codici di errore riportati in \var{errno} sono quelli della chiamata + utilizzata, pertanto si può fare riferimento a quanto illustrato nelle + descrizioni di \func{unlink} e \func{rmdir}.} +\end{prototype} + +La funzione utilizza la funzione \func{unlink}\footnote{questo vale usando le + \acr{glibc}; nelle libc4 e nelle libc5 la funzione \func{remove} è un + semplice alias alla funzione \func{unlink} e quindi non può essere usata per + le directory.} per cancellare i file e la funzione \func{rmdir} per +cancellare le directory; si tenga presente che per alcune implementazioni del +protocollo NFS utilizzare questa funzione può comportare la scomparsa di file +ancora in uso. + +Per cambiare nome ad un file o a una directory (che devono comunque essere +nello stesso filesystem) si usa invece la funzione \funcd{rename},\footnote{la + funzione è definita dallo standard ANSI C, ma si applica solo per i file, lo + standard POSIX estende la funzione anche alle directory.} il cui prototipo +è: +\begin{prototype}{stdio.h} + {int rename(const char *oldpath, const char *newpath)} + + Rinomina un file. + + \bodydesc{La funzione restituisce zero in caso di successo e -1 per un + errore, nel qual caso il file non viene toccato. La variabile + \var{errno} viene impostata secondo i seguenti codici di errore: + \begin{errlist} + \item[\errcode{EISDIR}] \param{newpath} è una directory mentre + \param{oldpath} non è una directory. + \item[\errcode{EXDEV}] \param{oldpath} e \param{newpath} non sono sullo + stesso filesystem. + \item[\errcode{ENOTEMPTY}] \param{newpath} è una directory già esistente e + non vuota. + \item[\errcode{EBUSY}] o \param{oldpath} o \param{newpath} sono in uso da + parte di qualche processo (come directory di lavoro o come radice) o del + sistema (come mount point). + \item[\errcode{EINVAL}] \param{newpath} contiene un prefisso di + \param{oldpath} o più in generale si è cercato di creare una directory come + sotto-directory di se stessa. + \item[\errcode{ENOTDIR}] Uno dei componenti dei pathname non è una directory + o \param{oldpath} è una directory e \param{newpath} esiste e non è una + directory. + \end{errlist} + ed inoltre \errval{EACCES}, \errval{EPERM}, \errval{EMLINK}, + \errval{ENOENT}, \errval{ENOMEM}, \errval{EROFS}, \errval{ELOOP} e + \errval{ENOSPC}.} +\end{prototype} + +La funzione rinomina il file \param{oldpath} in \param{newpath}, eseguendo se +necessario lo spostamento di un file fra directory diverse. Eventuali altri +link diretti allo stesso file non vengono influenzati. + +Il comportamento della funzione è diverso a seconda che si voglia rinominare +un file o una directory; se ci riferisce a un file allora \param{newpath}, se +esiste, non deve essere una directory (altrimenti si ha l'errore +\errcode{EISDIR}). Nel caso \param{newpath} indichi un file esistente questo +viene cancellato e rimpiazzato (atomicamente). + +Se \param{oldpath} è una directory allora \param{newpath}, se esiste, deve +essere una directory vuota, altrimenti si avranno gli errori \errcode{ENOTDIR} +(se non è una directory) o \errcode{ENOTEMPTY} (se non è vuota). Chiaramente +\param{newpath} non può contenere \param{oldpath} altrimenti si avrà un errore +\errcode{EINVAL}. + +Se \param{oldpath} si riferisce a un link simbolico questo sarà rinominato; se +\param{newpath} è un link simbolico verrà cancellato come qualunque altro +file. Infine qualora \param{oldpath} e \param{newpath} siano due nomi dello +stesso file lo standard POSIX prevede che la funzione non dia errore, e non +faccia nulla, lasciando entrambi i nomi; Linux segue questo standard, anche +se, come fatto notare dal manuale delle \textit{glibc}, il comportamento più +ragionevole sarebbe quello di cancellare \param{oldpath}. + +Il vantaggio nell'uso di questa funzione al posto della chiamata successiva di +\func{link} e \func{unlink} è che l'operazione è eseguita atomicamente, non +può esistere cioè nessun istante in cui un altro processo può trovare attivi +entrambi i nomi dello stesso file, o, in caso di sostituzione di un file +esistente, non trovare quest'ultimo prima che la sostituzione sia stata +eseguita. + +In ogni caso se \param{newpath} esiste e l'operazione fallisce per un qualche +motivo (come un crash del kernel), \func{rename} garantisce di lasciare +presente un'istanza di \param{newpath}. Tuttavia nella sovrascrittura potrà +esistere una finestra in cui sia \param{oldpath} che \param{newpath} fanno +riferimento allo stesso file. + + +\subsection{I link simbolici} +\label{sec:file_symlink} + +Come abbiamo visto in \secref{sec:file_link} la funzione \func{link} crea +riferimenti agli inode\index{inode}, pertanto può funzionare soltanto per file +che risiedono sullo stesso filesystem e solo per un filesystem di tipo Unix. +Inoltre abbiamo visto che in Linux non è consentito eseguire un link diretto +ad una directory. + +Per ovviare a queste limitazioni i sistemi Unix supportano un'altra forma di +link (i cosiddetti \textit{soft link} o \textit{symbolic link}), che sono, +come avviene in altri sistemi operativi, dei file speciali che contengono +semplicemente il riferimento ad un altro file (o directory). In questo modo è +possibile effettuare link anche attraverso filesystem diversi, a file posti in +filesystem che non supportano i link diretti, a delle directory, ed anche a +file che non esistono ancora. + +Il sistema funziona in quanto i link simbolici sono contrassegnati come tali +al kernel (analogamente a quanto avviene per le directory) per cui per alcune +funzioni di libreria (come \func{open} o \func{stat}) dare come parametro un +link simbolico comporta l'applicazione della funzione al file da esso +specificato. La funzione che permette di creare un nuovo link simbolico è +\funcd{symlink}; il suo prototipo è: +\begin{prototype}{unistd.h} + {int symlink(const char *oldpath, const char *newpath)} + Crea un nuovo link simbolico di nome \param{newpath} il cui contenuto è + \param{oldpath}. + + \bodydesc{La funzione restituisce zero in caso di successo e -1 per un + errore, nel qual caso la variabile \var{errno} assumerà i valori: + \begin{errlist} + \item[\errcode{EPERM}] il filesystem che contiene \param{newpath} non + supporta i link simbolici. + \item[\errcode{ENOENT}] una componente di \param{newpath} non esiste o + \param{oldpath} è una stringa vuota. + \item[\errcode{EEXIST}] esiste già un file \param{newpath}. + \item[\errcode{EROFS}] \param{newpath} è su un filesystem montato in sola + lettura. + \end{errlist} + ed inoltre \errval{EFAULT}, \errval{EACCES}, \errval{ENAMETOOLONG}, + \errval{ENOTDIR}, \errval{ENOMEM}, \errval{ELOOP}, \errval{ENOSPC} e + \errval{EIO}.} +\end{prototype} + +Si tenga presente che la funzione non effettua nessun controllo sull'esistenza +di un file di nome \param{oldpath}, ma si limita ad inserire quella stringa +nel link simbolico. Pertanto un link simbolico può anche riferirsi ad un file +che non esiste: in questo caso si ha quello che viene chiamato un +\textit{dangling link}, letteralmente un \textsl{link ciondolante}. + +Come accennato i link simbolici sono risolti automaticamente dal kernel +all'invocazione delle varie system call; in \tabref{tab:file_symb_effect} si è +riportato un elenco dei comportamenti delle varie funzioni di libreria che +operano sui file nei confronti della risoluzione dei link simbolici, +specificando quali seguono il link simbolico e quali invece possono operare +direttamente sul suo contenuto. +\begin{table}[htb] + \centering + \footnotesize + \begin{tabular}[c]{|l|c|c|} + \hline + \textbf{Funzione} & \textbf{Segue il link} & \textbf{Non segue il link} \\ + \hline + \hline + \func{access} & $\bullet$ & \\ + \func{chdir} & $\bullet$ & \\ + \func{chmod} & $\bullet$ & \\ + \func{chown} & & $\bullet$ \\ + \func{creat} & $\bullet$ & \\ + \func{exec} & $\bullet$ & \\ + \func{lchown} & $\bullet$ & $\bullet$ \\ + \func{link} & & \\ + \func{lstat} & & $\bullet$ \\ + \func{mkdir} & $\bullet$ & \\ + \func{mkfifo} & $\bullet$ & \\ + \func{mknod} & $\bullet$ & \\ + \func{open} & $\bullet$ & \\ + \func{opendir} & $\bullet$ & \\ + \func{pathconf} & $\bullet$ & \\ + \func{readlink} & & $\bullet$ \\ + \func{remove} & & $\bullet$ \\ + \func{rename} & & $\bullet$ \\ + \func{stat} & $\bullet$ & \\ + \func{truncate} & $\bullet$ & \\ + \func{unlink} & & $\bullet$ \\ + \hline + \end{tabular} + \caption{Uso dei link simbolici da parte di alcune funzioni.} + \label{tab:file_symb_effect} +\end{table} + +Si noti che non si è specificato il comportamento delle funzioni che operano +con i file descriptor, in quanto la risoluzione del link simbolico viene in +genere effettuata dalla funzione che restituisce il file descriptor +(normalmente la \func{open}, vedi \secref{sec:file_open}) e tutte le +operazioni seguenti fanno riferimento solo a quest'ultimo. + +Dato che, come indicato in \tabref{tab:file_symb_effect}, funzioni come la +\func{open} seguono i link simbolici, occorrono funzioni apposite per accedere +alle informazioni del link invece che a quelle del file a cui esso fa +riferimento. Quando si vuole leggere il contenuto di un link simbolico si usa +la funzione \funcd{readlink}, il cui prototipo è: +\begin{prototype}{unistd.h} +{int readlink(const char *path, char *buff, size\_t size)} + Legge il contenuto del link simbolico indicato da \param{path} nel buffer + \param{buff} di dimensione \param{size}. + + \bodydesc{La funzione restituisce il numero di caratteri letti dentro + \param{buff} o -1 per un errore, nel qual caso la variabile + \var{errno} assumerà i valori: + \begin{errlist} + \item[\errcode{EINVAL}] \param{path} non è un link simbolico o \param{size} + non è positiva. + \end{errlist} + ed inoltre \errval{ENOTDIR}, \errval{ENAMETOOLONG}, \errval{ENOENT}, + \errval{EACCES}, \errval{ELOOP}, \errval{EIO}, \errval{EFAULT} e + \errval{ENOMEM}.} +\end{prototype} + +La funzione apre il link simbolico, ne legge il contenuto, lo scrive nel +buffer, e lo richiude. Si tenga presente che la funzione non termina la +stringa con un carattere nullo e la tronca alla dimensione specificata da +\param{size} per evitare di sovrascrivere oltre le dimensioni del buffer. + + +\begin{figure}[htb] + \centering + \includegraphics[width=9cm]{img/link_loop} + \caption{Esempio di loop nel filesystem creato con un link simbolico.} + \label{fig:file_link_loop} +\end{figure} + +Un caso comune che si può avere con i link simbolici è la creazione dei +cosiddetti \textit{loop}. La situazione è illustrata in +\figref{fig:file_link_loop}, che riporta la struttura della directory +\file{/boot}. Come si vede si è creato al suo interno un link simbolico che +punta di nuovo a \file{/boot}.\footnote{il loop mostrato in + \figref{fig:file_link_loop} è un usato per poter permettere a \cmd{grub} (un + bootloader in grado di leggere direttamente da vari filesystem il file da + lanciare come sistema operativo) di vedere i file contenuti nella directory + \file{/boot} con lo stesso pathname con cui verrebbero visti dal sistema + operativo, anche se essi si trovano, come accade spesso, su una partizione + separata (che \cmd{grub}, all'avvio, vede come radice).} + +Questo può causare problemi per tutti quei programmi che effettuano la +scansione di una directory senza tener conto dei link simbolici, ad esempio se +lanciassimo un comando del tipo \code{grep -r linux *}, il loop nella +directory porterebbe il comando ad esaminare \file{/boot}, \file{/boot/boot}, +\file{/boot/boot/boot} e così via. + +Per questo motivo il kernel e le librerie prevedono che nella risoluzione di +un pathname possano essere seguiti un numero limitato di link simbolici, il +cui valore limite è specificato dalla costante \const{MAXSYMLINKS}. Qualora +questo limite venga superato viene generato un errore ed \var{errno} viene +impostata al valore \errcode{ELOOP}. + +Un punto da tenere sempre presente è che, come abbiamo accennato, un link +simbolico può fare riferimento anche ad un file che non esiste; ad esempio +possiamo creare un file temporaneo nella nostra directory con un link del +tipo: +\begin{verbatim} +$ ln -s /tmp/tmp_file temporaneo +\end{verbatim}%$ +anche se \file{/tmp/tmp\_file} non esiste. Questo può generare confusione, in +quanto aprendo in scrittura \file{temporaneo} verrà creato +\file{/tmp/tmp\_file} e scritto; ma accedendo in sola lettura a +\file{temporaneo}, ad esempio con \cmd{cat}, otterremmo: +\begin{verbatim} +$ cat temporaneo +cat: temporaneo: No such file or directory +\end{verbatim}%$ +con un errore che può sembrare sbagliato, dato che un'ispezione con \cmd{ls} +ci mostrerebbe invece l'esistenza di \file{temporaneo}. + + +\subsection{La creazione e la cancellazione delle directory} +\label{sec:file_dir_creat_rem} + +Benché in sostanza le directory non siano altro che dei file contenenti +elenchi di nomi ed inode, non è possibile trattarle come file ordinari e +devono essere create direttamente dal kernel attraverso una opportuna system +call.\footnote{questo permette anche, attraverso l'uso del VFS, l'utilizzo di + diversi formati per la gestione dei suddetti elenchi.} La funzione usata +per creare una directory è \funcd{mkdir}, ed il suo prototipo è: +\begin{functions} + \headdecl{sys/stat.h} + \headdecl{sys/types.h} + \funcdecl{int mkdir(const char *dirname, mode\_t mode)} + + Crea una nuova directory. + + \bodydesc{La funzione restituisce zero in caso di successo e -1 per un + errore, nel qual caso \var{errno} assumerà i valori: + \begin{errlist} + \item[\errcode{EEXIST}] Un file (o una directory) con quel nome esiste di + già. + \item[\errcode{EACCES}] + Non c'è il permesso di scrittura per la directory in cui si vuole inserire + la nuova directory. + \item[\errcode{EMLINK}] La directory in cui si vuole creare la nuova + directory contiene troppi file. Sotto Linux questo normalmente non avviene + perché il filesystem standard consente la creazione di un numero di file + maggiore di quelli che possono essere contenuti nel disco, ma potendo + avere a che fare anche con filesystem di altri sistemi questo errore può + presentarsi. + \item[\errcode{ENOSPC}] Non c'è abbastanza spazio sul file system per creare + la nuova directory o si è esaurita la quota disco dell'utente. + \end{errlist} + ed inoltre anche \errval{EPERM}, \errval{EFAULT}, \errval{ENAMETOOLONG}, + \errval{ENOENT}, \errval{ENOTDIR}, \errval{ENOMEM}, \errval{ELOOP}, + \errval{EROFS}.} +\end{functions} + +La funzione crea una nuova directory vuota, che contiene cioè solo le due voci +standard (\file{.} e \file{..}), con il nome indicato dall'argomento +\param{dirname}. Il nome può essere indicato sia come pathname assoluto che +relativo. + +I permessi di accesso alla directory (vedi \secref{sec:file_access_control}) +sono specificati da \param{mode}, i cui possibili valori sono riportati in +\tabref{tab:file_permission_const}; questi sono modificati dalla maschera di +creazione dei file (si veda \secref{sec:file_umask}). La titolarità della +nuova directory è impostata secondo quanto riportato in +\secref{sec:file_ownership}. + +La funzione per la cancellazione di una directory è \funcd{rmdir}, il suo +prototipo è: +\begin{prototype}{sys/stat.h}{int rmdir(const char *dirname)} + Cancella una directory. + + \bodydesc{La funzione restituisce zero in caso di successo e -1 per un + errore, nel qual caso \var{errno} assumerà i valori: + \begin{errlist} + \item[\errcode{EPERM}] Il filesystem non supporta la cancellazione di + directory, oppure la directory che contiene \param{dirname} ha lo sticky + bit impostato e l'user-ID effettivo del processo non corrisponde al + proprietario della directory. + \item[\errcode{EACCES}] Non c'è il permesso di scrittura per la directory + che contiene la directory che si vuole cancellare, o non c'è il permesso + di attraversare (esecuzione) una delle directory specificate in + \param{dirname}. + \item[\errcode{EBUSY}] La directory specificata è la directory di lavoro o la + radice di qualche processo. + \item[\errcode{ENOTEMPTY}] La directory non è vuota. + \end{errlist} + ed inoltre anche \errval{EFAULT}, \errval{ENAMETOOLONG}, \errval{ENOENT}, + \errval{ENOTDIR}, \errval{ENOMEM}, \errval{ELOOP}, \errval{EROFS}.} +\end{prototype} + +La funzione cancella la directory \param{dirname}, che deve essere vuota (la +directory deve cioè contenere soltanto le due voci standard \file{.} e +\file{..}). Il nome può essere indicato con il pathname assoluto o relativo. + +La modalità con cui avviene la cancellazione è analoga a quella di +\func{unlink}: fintanto che il numero di link all'inode\index{inode} della +directory non diventa nullo e nessun processo ha la directory aperta lo spazio +occupato su disco non viene rilasciato. Se un processo ha la directory aperta +la funzione rimuove il link all'inode\index{inode} e nel caso sia l'ultimo, +pure le voci standard \file{.} e \file{..}, a questo punto il kernel non +consentirà di creare più nuovi file nella directory. + + +\subsection{La creazione di file speciali} +\label{sec:file_mknod} + +Finora abbiamo parlato esclusivamente di file, directory e link simbolici; in +\secref{sec:file_file_types} abbiamo visto però che il sistema prevede pure +degli altri tipi di file speciali, come i file di dispositivo +\index{file!di dispositivo} +e le fifo (i socket\index{socket} sono un caso a parte, che +vedremo in \capref{cha:socket_intro}). + +La manipolazione delle caratteristiche di questi file e la loro cancellazione +può essere effettuata con le stesse funzioni che operano sui file regolari; ma +quando li si devono creare sono necessarie delle funzioni apposite. La prima +di queste funzioni è \funcd{mknod}, il suo prototipo è: +\begin{functions} + \headdecl{sys/types.h} + \headdecl{sys/stat.h} + \headdecl{fnctl.h} + \headdecl{unistd.h} + \funcdecl{int mknod(const char *pathname, mode\_t mode, dev\_t dev)} + + Crea un inode, si usa per creare i file speciali. + + \bodydesc{La funzione restituisce zero in caso di successo e -1 per un + errore, nel qual caso \var{errno} assumerà i valori: + \begin{errlist} + \item[\errcode{EPERM}] Non si hanno privilegi sufficienti a creare l'inode, o + il filesystem su cui si è cercato di creare \func{pathname} non supporta + l'operazione. + \item[\errcode{EINVAL}] Il valore di \param{mode} non indica un file, una + fifo o un dispositivo. + \item[\errcode{EEXIST}] \param{pathname} esiste già o è un link simbolico. + \end{errlist} + ed inoltre anche \errval{EFAULT}, \errval{EACCES}, \errval{ENAMETOOLONG}, + \errval{ENOENT}, \errval{ENOTDIR}, \errval{ENOMEM}, \errval{ELOOP}, + \errval{ENOSPC}, \errval{EROFS}.} +\end{functions} + +La funzione permette di creare un file speciale, ma si può usare anche per +creare file regolari e fifo; l'argomento \param{mode} specifica il tipo di +file che si vuole creare ed i relativi permessi, secondo i valori riportati in +\tabref{tab:file_mode_flags}, che vanno combinati con un OR binario. I +permessi sono comunque modificati nella maniera usuale dal valore di +\var{umask} (si veda \secref{sec:file_umask}). + +Per il tipo di file può essere specificato solo uno fra: \const{S\_IFREG} per +un file regolare (che sarà creato vuoto), \const{S\_IFBLK} per un device a +blocchi, \const{S\_IFCHR} per un device a caratteri e \const{S\_IFIFO} per una +fifo. Un valore diverso comporterà l'errore \errcode{EINVAL}. Qualora si sia +specificato in \param{mode} un file di dispositivo, il valore di \param{dev} +viene usato per indicare a quale dispositivo si fa riferimento. + +Solo l'amministratore può creare un file di dispositivo o un file regolare +usando questa funzione; ma in Linux\footnote{la funzione non è prevista dallo + standard POSIX, e deriva da SVr4, con appunto questa differenza e diversi + codici di errore.} l'uso per la creazione di una fifo è consentito anche +agli utenti normali. + +I nuovi inode\index{inode} creati con \func{mknod} apparterranno al +proprietario e al gruppo del processo che li ha creati, a meno che non si sia +attivato il bit \acr{sgid} per la directory o sia stata attivata la semantica +BSD per il filesystem (si veda \secref{sec:file_ownership}) in cui si va a +creare l'inode\index{inode}. + +Per creare una fifo (un file speciale, su cui torneremo in dettaglio in +\secref{sec:ipc_named_pipe}) lo standard POSIX specifica l'uso della funzione +\funcd{mkfifo}, il cui prototipo è: +\begin{functions} + \headdecl{sys/types.h} \headdecl{sys/stat.h} + + \funcdecl{int mkfifo(const char *pathname, mode\_t mode)} + + Crea una fifo. + + \bodydesc{La funzione restituisce zero in caso di successo e -1 per un + errore, nel qual caso \var{errno} assumerà i valori \errval{EACCES}, + \errval{EEXIST}, \errval{ENAMETOOLONG}, \errval{ENOENT}, \errval{ENOSPC}, + \errval{ENOTDIR} e \errval{EROFS}.} +\end{functions} + +La funzione crea la fifo \param{pathname} con i permessi \param{mode}. Come +per \func{mknod} il file \param{pathname} non deve esistere (neanche come link +simbolico); al solito i permessi specificati da \param{mode} vengono +modificati dal valore di \var{umask}. + + + +\subsection{Accesso alle directory} +\label{sec:file_dir_read} + +Benché le directory alla fine non siano altro che dei file che contengono +delle liste di nomi ed inode, per il ruolo che rivestono nella struttura del +sistema, non possono essere trattate come dei normali file di dati. Ad +esempio, onde evitare inconsistenze all'interno del filesystem, solo il kernel +può scrivere il contenuto di una directory, e non può essere un processo a +inserirvi direttamente delle voci con le usuali funzioni di scrittura. + +Ma se la scrittura e l'aggiornamento dei dati delle directory è compito del +kernel, sono molte le situazioni in cui i processi necessitano di poterne +leggere il contenuto. Benché questo possa essere fatto direttamente (vedremo +in \secref{sec:file_open} che è possibile aprire una directory come se fosse +un file, anche se solo in sola lettura) in generale il formato con cui esse +sono scritte può dipendere dal tipo di filesystem, tanto che, come riportato +in \tabref{tab:file_file_operations}, il VFS del kernel prevede una apposita +funzione per la lettura delle directory. + +Tutto questo si riflette nello standard POSIX\footnote{le funzioni sono + previste pure in BSD e SVID.} che ha introdotto una apposita interfaccia per +la lettura delle directory, basata sui cosiddetti \textit{directory stream} +(chiamati così per l'analogia con i file stream dell'interfaccia standard di +\capref{cha:files_std_interface}). La prima funzione di questa interfaccia è +\funcd{opendir}, il cui prototipo è: +\begin{functions} + \headdecl{sys/types.h} \headdecl{dirent.h} + + \funcdecl{DIR * opendir(const char *dirname)} + + Apre un \textit{directory stream}. + + \bodydesc{La funzione restituisce un puntatore al \textit{directory stream} + in caso di successo e \val{NULL} per un errore, nel qual caso \var{errno} + assumerà i valori \errval{EACCES}, \errval{EMFILE}, \errval{ENFILE}, + \errval{ENOENT}, \errval{ENOMEM} e \errval{ENOTDIR}.} +\end{functions} + +La funzione apre un \textit{directory stream} per la directory +\param{dirname}, ritornando il puntatore ad un oggetto di tipo \type{DIR} (che +è il tipo opaco\index{tipo!opaco} usato dalle librerie per gestire i +\textit{directory stream}) da usare per tutte le operazioni successive, la +funzione inoltre posiziona lo stream sulla prima voce contenuta nella +directory. + +Dato che le directory sono comunque dei file, in alcuni casi può servire +conoscere il \textit{file descriptor} associato ad un \textit{directory + stream}, a questo scopo si può usare la funzione \funcd{dirfd}, il cui +prototipo è: +\begin{functions} + \headdecl{sys/types.h} \headdecl{dirent.h} + + \funcdecl{int dirfd(DIR * dir)} + + Restituisce il file descriptor associato ad un \textit{directory stream}. + + \bodydesc{La funzione restituisce il file descriptor (un valore positivo) in + caso di successo e -1 in caso di errore.} +\end{functions} + +La funzione\footnote{questa funzione è una estensione di BSD non presente in + POSIX, introdotta con BSD 4.3-Reno; è presente in Linux con le libc5 (a + partire dalla versione 5.1.2) e con le \acr{glibc}.} restituisce il file +descriptor associato al \textit{directory stream} \param{dir}, essa è +disponibile solo definendo \macro{\_BSD\_SOURCE} o \macro{\_SVID\_SOURCE}. Di +solito si utilizza questa funzione in abbinamento alla funzione \func{fchdir} +per cambiare la directory di lavoro (vedi \secref{sec:file_work_dir}) a quella +relativa allo stream che si sta esaminando. + +La lettura di una voce della directory viene effettuata attraverso la funzione +\funcd{readdir}; il suo prototipo è: +\begin{functions} + \headdecl{sys/types.h} \headdecl{dirent.h} + + \funcdecl{struct dirent *readdir(DIR *dir)} + + Legge una voce dal \textit{directory stream}. + + \bodydesc{La funzione restituisce il puntatore alla struttura contenente i + dati in caso di successo e \val{NULL} altrimenti, in caso di descrittore + non valido \var{errno} assumerà il valore \errval{EBADF}, il valore + \val{NULL} viene restituito anche quando si raggiunge la fine dello + stream.} +\end{functions} + +La funzione legge la voce corrente nella directory, posizionandosi sulla voce +successiva. I dati vengono memorizzati in una struttura \struct{dirent} (la +cui definizione\footnote{la definizione è quella usata a Linux, che si trova + nel file \file{/usr/include/bits/dirent.h}, essa non contempla la presenza + del campo \var{d\_namlen} che indica la lunghezza del nome del file (ed + infatti la macro \macro{\_DIRENT\_HAVE\_D\_NAMLEN} non è definita).} è +riportata in \figref{fig:file_dirent_struct}). La funzione restituisce il +puntatore alla struttura; si tenga presente però che quest'ultima è allocata +staticamente, per cui viene sovrascritta tutte le volte che si ripete la +lettura di una voce sullo stesso stream. + +Di questa funzione esiste anche una versione rientrante, \func{readdir\_r}, +che non usa una struttura allocata staticamente, e può essere utilizzata anche +con i thread; il suo prototipo è: +\begin{functions} + \headdecl{sys/types.h} \headdecl{dirent.h} + + \funcdecl{int readdir\_r(DIR *dir, struct dirent *entry, + struct dirent **result)} + + Legge una voce dal \textit{directory stream}. + + \bodydesc{La funzione restituisce 0 in caso di successo e -1 in caso di + errore, gli errori sono gli stessi di \func{readdir}.} +\end{functions} + +La funzione restituisce in \param{result} (come \textit{value result + argument}) l'indirizzo dove sono stati salvati i dati, che di norma +corrisponde a quello della struttura precedentemente allocata e specificata +dall'argomento \param{entry} (anche se non è assicurato che la funzione usi lo +spazio fornito dall'utente). + +I vari campi di \struct{dirent} contengono le informazioni relative alle voci +presenti nella directory; sia BSD che SVr4\footnote{POSIX prevede invece solo + la presenza del campo \var{d\_fileno}, identico \var{d\_ino}, che in Linux è + definito come alias di quest'ultimo. Il campo \var{d\_name} è considerato + dipendente dall'implementazione.} prevedono che siano sempre presenti il +campo \var{d\_name}, che contiene il nome del file nella forma di una stringa +terminata da uno zero,\footnote{lo standard POSIX non specifica una lunghezza, + ma solo un limite \const{NAME\_MAX}; in SVr4 la lunghezza del campo è + definita come \code{NAME\_MAX+1} che di norma porta al valore di 256 byte + usato anche in Linux.} ed il campo \var{d\_ino}, che contiene il numero di +inode cui il file è associato (di solito corrisponde al campo \var{st\_ino} di +\struct{stat}). + +\begin{figure}[!htb] + \footnotesize \centering + \begin{minipage}[c]{15cm} + \begin{lstlisting}[labelstep=0]{}%,frame=,indent=1cm]{} +struct dirent { + ino_t d_ino; /* inode number */ + off_t d_off; /* offset to the next dirent */ + unsigned short int d_reclen; /* length of this record */ + unsigned char d_type; /* type of file */ + char d_name[256]; /* We must not include limits.h! */ +}; + \end{lstlisting} + \end{minipage} + \normalsize + \caption{La struttura \structd{dirent} per la lettura delle informazioni dei + file.} + \label{fig:file_dirent_struct} +\end{figure} + +La presenza di ulteriori campi opzionali è segnalata dalla definizione di +altrettante macro nella forma \code{\_DIRENT\_HAVE\_D\_XXX} dove \code{XXX} è +il nome del relativo campo; nel nostro caso sono definite le macro +\macro{\_DIRENT\_HAVE\_D\_TYPE}, \macro{\_DIRENT\_HAVE\_D\_OFF} e +\macro{\_DIRENT\_HAVE\_D\_RECLEN}. + +\begin{table}[htb] + \centering + \footnotesize + \begin{tabular}[c]{|l|l|} + \hline + \textbf{Valore} & \textbf{Significato} \\ + \hline + \hline + \const{DT\_UNKNOWN} & tipo sconosciuto. \\ + \const{DT\_REG} & file normale. \\ + \const{DT\_DIR} & directory. \\ + \const{DT\_FIFO} & fifo. \\ + \const{DT\_SOCK} & socket. \\ + \const{DT\_CHR} & dispositivo a caratteri. \\ + \const{DT\_BLK} & dispositivo a blocchi. \\ + \hline + \end{tabular} + \caption{Costanti che indicano i vari tipi di file nel campo \var{d\_type} + della struttura \struct{dirent}.} + \label{tab:file_dtype_macro} +\end{table} + +Per quanto riguarda il significato dei campi opzionali, il campo \var{d\_type} +indica il tipo di file (fifo, directory, link simbolico, ecc.); i suoi +possibili valori\footnote{fino alla versione 2.1 delle \acr{glibc} questo + campo, pur presente nella struttura, non è implementato, e resta sempre al + valore \const{DT\_UNKNOWN}.} sono riportati in +\tabref{tab:file_dtype_macro}; per la conversione da e verso l'analogo valore +mantenuto dentro il campo \var{st\_mode} di \struct{stat} sono definite anche +due macro di conversione \macro{IFTODT} e \macro{DTTOIF}: +\begin{functions} + \funcdecl{int IFTODT(mode\_t MODE)} Converte il tipo di file dal formato di + \var{st\_mode} a quello di \var{d\_type}. + + \funcdecl{mode\_t DTTOIF(int DTYPE)} Converte il tipo di file dal formato di + \var{d\_type} a quello di \var{st\_mode}. +\end{functions} + +Il campo \var{d\_off} contiene invece la posizione della voce successiva della +directory, mentre il campo \var{d\_reclen} la lunghezza totale della voce +letta. Con questi due campi diventa possibile, determinando la posizione delle +varie voci, spostarsi all'interno dello stream usando la funzione +\func{seekdir},\footnote{sia questa funzione che \func{telldir}, sono + estensioni prese da BSD, non previste dallo standard POSIX.} il cui +prototipo è: +\begin{prototype}{dirent.h}{void seekdir(DIR *dir, off\_t offset)} + Cambia la posizione all'interno di un \textit{directory stream}. +\end{prototype} + +La funzione non ritorna nulla e non segnala errori, è però necessario che il +valore dell'argomento \param{offset} sia valido per lo stream \param{dir}; +esso pertanto deve essere stato ottenuto o dal valore di \var{d\_off} di +\struct{dirent} o dal valore restituito dalla funzione \func{telldir}, che +legge la posizione corrente; il prototipo di quest'ultima è: +\begin{prototype}{dirent.h}{off\_t telldir(DIR *dir)} + Ritorna la posizione corrente in un \textit{directory stream}. + + \bodydesc{La funzione restituisce la posizione corrente nello stream (un + numero positivo) in caso di successo, e -1 altrimenti, nel qual caso + \var{errno} assume solo il valore di \errval{EBADF}, corrispondente ad un + valore errato per \param{dir}.} +\end{prototype} + +La sola funzione di posizionamento nello stream prevista dallo standard POSIX +è \funcd{rewinddir}, che riporta la posizione a quella iniziale; il suo +prototipo è: +\begin{functions} + \headdecl{sys/types.h} \headdecl{dirent.h} + + \funcdecl{void rewinddir(DIR *dir)} + + Si posiziona all'inizio di un \textit{directory stream}. +\end{functions} + + +Una volta completate le operazioni si può chiudere il \textit{directory + stream} con la funzione \funcd{closedir}, il cui prototipo è: +\begin{functions} + \headdecl{sys/types.h} \headdecl{dirent.h} + + \funcdecl{int closedir(DIR * dir)} + + Chiude un \textit{directory stream}. + + \bodydesc{La funzione restituisce 0 in caso di successo e -1 altrimenti, nel + qual caso \var{errno} assume il valore \errval{EBADF}.} +\end{functions} + +A parte queste funzioni di base in BSD 4.3 è stata introdotta un'altra +funzione che permette di eseguire una scansione completa (con tanto di ricerca +ed ordinamento) del contenuto di una directory; la funzione è +\funcd{scandir}\footnote{in Linux questa funzione è stata introdotta fin dalle + libc4.} ed il suo prototipo è: +\begin{prototype}{dirent.h}{int scandir(const char *dir, + struct dirent ***namelist, int(*select)(const struct dirent *), + int(*compar)(const struct dirent **, const struct dirent **))} + + Esegue una scansione di un \textit{directory stream}. + + \bodydesc{La funzione restituisce in caso di successo il numero di voci + trovate, e -1 altrimenti.} +\end{prototype} + +Al solito, per la presenza fra gli argomenti di due puntatori a funzione, il +prototipo non è molto comprensibile; queste funzioni però sono quelle che +controllano rispettivamente la selezione di una voce (\param{select}) e +l'ordinamento di tutte le voci selezionate (\param{compar}). + +La funzione legge tutte le voci della directory indicata dall'argomento +\param{dir}, passando ciascuna di esse come argomento alla funzione di +\param{select}; se questa ritorna un valore diverso da zero la voce viene +inserita in una struttura allocata dinamicamente con \func{malloc}, qualora si +specifichi un valore \val{NULL} per \func{select} vengono selezionate tutte le +voci. Tutte le voci selezionate vengono poi inserite un una lista (anch'essa +allocata con \func{malloc}, che viene riordinata tramite \func{qsort} usando +la funzione \param{compar} come criterio di ordinamento; alla fine l'indirizzo +della lista ordinata è restituito nell'argomento \param{namelist}. + +Per l'ordinamento sono disponibili anche due funzioni predefinite, +\funcd{alphasort} e \funcd{versionsort}, i cui prototipi sono: +\begin{functions} + \headdecl{dirent.h} + + \funcdecl{int alphasort(const void *a, const void *b)} + + \funcdecl{int versionsort(const void *a, const void *b)} + + Funzioni per l'ordinamento delle voci di \textit{directory stream}. + + \bodydesc{Le funzioni restituiscono un valore minore, uguale o maggiore di + zero qualora il primo argomento sia rispettivamente minore, uguale o + maggiore del secondo.} +\end{functions} + +La funzione \func{alphasort} deriva da BSD ed è presente in Linux fin dalle +libc4\footnote{la versione delle libc4 e libc5 usa però come argomenti dei + puntatori a delle strutture \struct{dirent}; le glibc usano il prototipo + originario di BSD, mostrato anche nella definizione, che prevede puntatori a + \ctyp{void}.} e deve essere specificata come argomento \param{compare} per +ottenere un ordinamento alfabetico (secondo il valore del campo \var{d\_name} +delle varie voci). Le \acr{glibc} prevedono come estensione\footnote{le glibc, + a partire dalla versione 2.1, effettuano anche l'ordinamento alfabetico + tenendo conto delle varie localizzazioni, usando \func{strcoll} al posto di + \func{strcmp}.} anche \func{versionsort}, che ordina i nomi tenendo conto +del numero di versione (cioè qualcosa per cui \file{file10} viene comunque +dopo \func{file4}.) + +Un semplice esempio dell'uso di queste funzioni è riportato in +\figref{fig:file_my_ls}, dove si è riportata la sezione principale di un +programma che, usando la routine di scansione illustrata in +\figref{fig:file_dirscan}, stampa i nomi dei file contenuti in una directory e +la relativa dimensione (in sostanza una versione semplificata del comando +\cmd{ls}). + +\begin{figure}[!htb] + \footnotesize + \begin{lstlisting}{} +#include +#include +#include /* directory */ +#include /* C standard library */ +#include + +/* computation function for DirScan */ +int do_ls(struct dirent * direntry); +/* main body */ +int main(int argc, char *argv[]) +{ + ... + if ((argc - optind) != 1) { /* There must be remaing parameters */ + printf("Wrong number of arguments %d\n", argc - optind); + usage(); + } + DirScan(argv[1], do_ls); + exit(0); +} +/* + * Routine to print file name and size inside DirScan + */ +int do_ls(struct dirent * direntry) +{ + struct stat data; + + stat(direntry->d_name, &data); /* get stat data */ + printf("File: %s \t size: %d\n", direntry->d_name, data.st_size); + return 0; +} + \end{lstlisting} + \caption{Esempio di codice per eseguire la lista dei file contenuti in una + directory.} + \label{fig:file_my_ls} +\end{figure} + +Il programma è estremamente semplice; in \figref{fig:file_my_ls} si è omessa +la parte di gestione delle opzioni (che prevede solo l'uso di una funzione per +la stampa della sintassi, anch'essa omessa) ma il codice completo potrà essere +trovato coi sorgenti allegati nel file \file{myls.c}. + +In sostanza tutto quello che fa il programma, dopo aver controllato +(\texttt{\small 10--13}) di avere almeno un parametro (che indicherà la +directory da esaminare) è chiamare (\texttt{\small 14}) la funzione +\func{DirScan} per eseguire la scansione, usando la funzione \code{do\_ls} +(\texttt{\small 20--26}) per fare tutto il lavoro. + +Quest'ultima si limita (\texttt{\small 23}) a chiamare \func{stat} sul file +indicato dalla directory entry passata come argomento (il cui nome è appunto +\var{direntry->d\_name}), memorizzando in una opportuna struttura \var{data} i +dati ad esso relativi, per poi provvedere (\texttt{\small 24}) a stampare il +nome del file e la dimensione riportata in \var{data}. + +Dato che la funzione verrà chiamata all'interno di \func{DirScan} per ogni +voce presente questo è sufficiente a stampare la lista completa dei file e +delle relative dimensioni. Si noti infine come si restituisca sempre 0 come +valore di ritorno per indicare una esecuzione senza errori. + +\begin{figure}[!htb] + \footnotesize + \begin{lstlisting}{} +#include +#include +#include /* directory */ +#include /* C standard library */ +#include + +/* + * Function DirScan: + * + * Input: the directory name and a computation function + * Return: 0 if OK, -1 on errors + */ +int DirScan(char * dirname, int(*compute)(struct dirent *)) +{ + DIR * dir; + struct dirent *direntry; + + if ( (dir = opendir(dirname)) == NULL) { /* oper directory */ + printf("Opening %s\n", dirname); /* on error print messages */ + perror("Cannot open directory"); /* and then return */ + return -1; + } + fd = dirfd(dir); /* get file descriptor */ + fchdir(fd); /* change directory */ + /* loop on directory entries */ + while ( (direntry = readdir(dir)) != NULL) { /* read entry */ + if (compute(direntry)) { /* execute function on it */ + return -1; /* on error return */ + } + } + closedir(dir); + return 0; +} + + \end{lstlisting} + \caption{Codice della routine di scansione di una directory contenuta nel + file \file{DirScan.c}.} + \label{fig:file_dirscan} +\end{figure} + +Tutto il grosso del lavoro è svolto dalla funzione \func{DirScan}, riportata +in \figref{fig:file_dirscan}. La funzione è volutamente generica e permette di +eseguire una funzione, passata come secondo argomento, su tutte le voci di una +directory. La funzione inizia con l'aprire (\texttt{\small 19--23}) uno +stream sulla directory passata come primo argomento, stampando un messaggio in +caso di errore. + +Il passo successivo (\texttt{\small 24--25}) è cambiare directory di lavoro +(vedi \secref{sec:file_work_dir}), usando in sequenza le funzione \func{dirfd} +e \func{fchdir} (in realtà si sarebbe potuto usare direttamente \func{chdir} +su \var{dirname}), in modo che durante il successivo ciclo (\texttt{\small + 27--31}) sulle singole voci dello stream ci si trovi all'interno della +directory.\footnote{questo è essenziale al funzionamento della funzione + \code{do\_ls} (e ad ogni funzione che debba usare il campo \var{d\_name}, in + quanto i nomi dei file memorizzati all'interno di una struttura + \struct{dirent} sono sempre relativi alla directory in questione, e senza + questo posizionamento non si sarebbe potuto usare \func{stat} per ottenere + le dimensioni.} + +Avendo usato lo stratagemma di fare eseguire tutte le manipolazioni necessarie +alla funzione passata come secondo argomento, il ciclo di scansione della +directory è molto semplice; si legge una voce alla volta (\texttt{\small 27}) +all'interno di una istruzione di \code{while} e fintanto che si riceve una +voce valida (cioè un puntatore diverso da \val{NULL}) si esegue +(\texttt{\small 27}) la funzione di elaborazione \var{compare} (che nel nostro +caso sarà \code{do\_ls}), ritornando con un codice di errore (\texttt{\small + 28}) qualora questa presenti una anomalia (identificata da un codice di +ritorno negativo). + +Una volta terminato il ciclo la funzione si conclude con la chiusura +(\texttt{\small 32}) dello stream\footnote{nel nostro caso, uscendo subito + dopo la chiamata, questo non servirebbe, in generale però l'operazione è + necessaria, dato che la funzione può essere invocata molte volte all'interno + dello stesso processo, per cui non chiudere gli stream comporterebbe un + consumo progressivo di risorse, con conseguente rischio di esaurimento delle + stesse} e la restituzione (\texttt{\small 33}) del codice di operazioni +concluse con successo. + + +\subsection{La directory di lavoro} +\label{sec:file_work_dir} + +A ciascun processo è associata una directory nel filesystem che è chiamata +directory corrente o directory di lavoro (\textit{current working directory}) +che è quella a cui si fa riferimento quando un pathname è espresso in forma +relativa, dove il ``\textsl{relativa}'' fa riferimento appunto a questa +directory. + +Quando un utente effettua il login, questa directory viene impostata alla +\textit{home directory} del suo account. Il comando \cmd{cd} della shell +consente di cambiarla a piacere, spostandosi da una directory ad un'altra, il +comando \cmd{pwd} la stampa sul terminale. Siccome la directory corrente +resta la stessa quando viene creato un processo figlio (vedi +\secref{sec:proc_fork}), la directory corrente della shell diventa anche la +directory corrente di qualunque comando da essa lanciato. + +In genere il kernel tiene traccia per ciascun processo dell'inode\index{inode} +della directory di lavoro corrente, per ottenere il pathname occorre usare una +apposita funzione di libreria, \funcd{getcwd}, il cui prototipo è: +\begin{prototype}{unistd.h}{char *getcwd(char *buffer, size\_t size)} + Legge il pathname della directory di lavoro corrente. + + \bodydesc{La funzione restituisce il puntatore \param{buffer} se riesce, + \val{NULL} se fallisce, in quest'ultimo caso la variabile + \var{errno} è impostata con i seguenti codici di errore: + \begin{errlist} + \item[\errcode{EINVAL}] L'argomento \param{size} è zero e \param{buffer} non + è nullo. + \item[\errcode{ERANGE}] L'argomento \param{size} è più piccolo della + lunghezza del pathname. + \item[\errcode{EACCES}] Manca il permesso di lettura o di ricerca su uno dei + componenti del pathname (cioè su una delle directory superiori alla + corrente). + \end{errlist}} +\end{prototype} + +La funzione restituisce il pathname completo della directory di lavoro +corrente nella stringa puntata da \param{buffer}, che deve essere +precedentemente allocata, per una dimensione massima di \param{size}. Il +buffer deve essere sufficientemente lungo da poter contenere il pathname +completo più lo zero di terminazione della stringa. Qualora esso ecceda le +dimensioni specificate con \param{size} la funzione restituisce un errore. + +Si può anche specificare un puntatore nullo come +\param{buffer},\footnote{questa è un'estensione allo standard POSIX.1, + supportata da Linux.} nel qual caso la stringa sarà allocata automaticamente +per una dimensione pari a \param{size} qualora questa sia diversa da zero, o +della lunghezza esatta del pathname altrimenti. In questo caso ci si deve +ricordare di disallocare la stringa una volta cessato il suo utilizzo. + +Di questa funzione esiste una versione \code{char *getwd(char *buffer)} +fatta per compatibilità all'indietro con BSD, che non consente di specificare +la dimensione del buffer; esso deve essere allocato in precedenza ed avere una +dimensione superiore a \const{PATH\_MAX} (di solito 256 byte, vedi +\secref{sec:sys_limits}); il problema è che in Linux non esiste una dimensione +superiore per un pathname, per cui non è detto che il buffer sia sufficiente a +contenere il nome del file, e questa è la ragione principale per cui questa +funzione è deprecata. + +Una seconda funzione simile è \code{char *get\_current\_dir\_name(void)} che è +sostanzialmente equivalente ad una \code{getcwd(NULL, 0)}, con la sola +differenza che essa ritorna il valore della variabile di ambiente \val{PWD}, +che essendo costruita dalla shell può contenere un pathname comprendente anche +dei link simbolici. Usando \func{getcwd} infatti, essendo il pathname ricavato +risalendo all'indietro l'albero della directory, si perderebbe traccia di ogni +passaggio attraverso eventuali link simbolici. + +Per cambiare la directory di lavoro corrente si può usare la funzione +\funcd{chdir} (equivalente del comando di shell \cmd{cd}) il cui nome sta +appunto per \textit{change directory}, il suo prototipo è: +\begin{prototype}{unistd.h}{int chdir(const char *pathname)} + Cambia la directory di lavoro corrente in \param{pathname}. - La funzione restituisce zero in caso di successo e -1 per un errore, in caso - di errore \texttt{errno} viene settato ai valori: + \bodydesc{La funzione restituisce 0 in caso di successo e -1 per un errore, + nel qual caso \var{errno} assumerà i valori: \begin{errlist} - \item \texttt{EACCESS} Non c'è il permesso di accedere al file. - \item \texttt{ENOTDIR} Una componente del pathname non è una directory. - \item \texttt{EMLOOP} Ci sono troppi link simbolici nel pathname. - \item \texttt{EFAULT} I puntatori usati sono fuori dallo spazio di indirizzi - del processo. - \item \texttt{ENOMEM} il kernel non ha a disposizione memoria sufficiente a - completare l'operazione. - \item \texttt{ENAMETOOLONG} Il filename è troppo lungo. + \item[\errcode{ENOTDIR}] Non si è specificata una directory. + \item[\errcode{EACCES}] Manca il permesso di ricerca su uno dei componenti + di \param{path}. \end{errlist} + ed inoltre \errval{EFAULT}, \errval{ENAMETOOLONG}, \errval{ENOENT}, + \errval{ENOMEM}, \errval{ELOOP} e \errval{EIO}.} +\end{prototype} +\noindent ed ovviamente \param{pathname} deve indicare una directory per la +quale si hanno i permessi di accesso. + +Dato che anche le directory sono file, è possibile riferirsi ad esse anche +tramite il file descriptor, e non solo tramite il pathname, per fare questo si +usa \funcd{fchdir}, il cui prototipo è: +\begin{prototype}{unistd.h}{int fchdir(int fd)} + Identica a \func{chdir}, ma usa il file descriptor \param{fd} invece del + pathname. + + \bodydesc{La funzione restituisce zero in caso di successo e -1 per un + errore, in caso di errore \var{errno} assumerà i valori \errval{EBADF} o + \errval{EACCES}.} +\end{prototype} +\noindent anche in questo caso \param{fd} deve essere un file descriptor +valido che fa riferimento ad una directory. Inoltre l'unico errore di accesso +possibile (tutti gli altri sarebbero occorsi all'apertura di \param{fd}), è +quello in cui il processo non ha il permesso di accesso alla directory +specificata da \param{fd}. + + + +\subsection{I file temporanei} +\label{sec:file_temp_file} + +In molte occasioni è utile poter creare dei file temporanei; benché la cosa +sembri semplice, in realtà il problema è più sottile di quanto non appaia a +prima vista. Infatti anche se sembrerebbe banale generare un nome a caso e +creare il file dopo aver controllato che questo non esista, nel momento fra il +controllo e la creazione si ha giusto lo spazio per una possibile \textit{race + condition}\index{race condition} (si ricordi quanto visto in +\secref{sec:proc_race_cond}). + +Le \acr{glibc} provvedono varie funzioni per generare nomi di file temporanei, +di cui si abbia certezza di unicità (al momento della generazione); la prima +di queste funzioni è \funcd{tmpnam} il cui prototipo è: +\begin{prototype}{stdio.h}{char *tmpnam(char *string)} + Restituisce il puntatore ad una stringa contente un nome di file valido e + non esistente al momento dell'invocazione. + + \bodydesc{La funzione ritorna il puntatore alla stringa con il nome o + \val{NULL} in caso di fallimento. Non sono definiti errori.} +\end{prototype} +\noindent se si è passato un puntatore \param{string} non nullo questo deve +essere di dimensione \const{L\_tmpnam} (costante definita in \file{stdio.h}, +come \const{P\_tmpdir} e \const{TMP\_MAX}) ed il nome generato vi verrà +copiato automaticamente; altrimenti il nome sarà generato in un buffer statico +interno che verrà sovrascritto ad una chiamata successiva. Successive +invocazioni della funzione continueranno a restituire nomi unici fino ad un +massimo di \const{TMP\_MAX} volte. Al nome viene automaticamente aggiunto come +prefisso la directory specificata da \const{P\_tmpdir}. + +Di questa funzione esiste una versione rientrante, \func{tmpnam\_r}, che non +fa nulla quando si passa \val{NULL} come parametro. Una funzione simile, +\funcd{tempnam}, permette di specificare un prefisso per il file +esplicitamente, il suo prototipo è: +\begin{prototype}{stdio.h}{char *tempnam(const char *dir, const char *pfx)} + Restituisce il puntatore ad una stringa contente un nome di file valido e + non esistente al momento dell'invocazione. + + \bodydesc{La funzione ritorna il puntatore alla stringa con il nome o + \val{NULL} in caso di fallimento, \var{errno} viene impostata a + \errval{ENOMEM} qualora fallisca l'allocazione della stringa.} \end{prototype} -La struttura \texttt{stat} è definita nell'header \texttt{sys/stat.h} e in -generale dipende dall'implementazione, la versione usata da Linux è mostrata -in \nfig, così come riportata dalla man page (in realtà la definizione -effettivamente usata nel kernel dipende dall'archietettura e ha altri campi -riservati per estensioni come tempo più precisi, o per il padding dei campi). +La funzione alloca con \code{malloc} la stringa in cui restituisce il nome, +per cui è sempre rientrante, occorre però ricordarsi di disallocare il +puntatore che restituisce. L'argomento \param{pfx} specifica un prefisso di +massimo 5 caratteri per il nome provvisorio. La funzione assegna come +directory per il file temporaneo (verificando che esista e sia accessibili), +la prima valida delle seguenti: +\begin{itemize*} +\item La variabile di ambiente \const{TMPNAME} (non ha effetto se non è + definita o se il programma chiamante è \acr{suid} o \acr{sgid}, vedi + \secref{sec:file_suid_sgid}). +\item il valore dell'argomento \param{dir} (se diverso da \val{NULL}). +\item Il valore della costante \const{P\_tmpdir}. +\item la directory \file{/tmp}. +\end{itemize*} + +In ogni caso, anche se la generazione del nome è casuale, ed è molto difficile +ottenere un nome duplicato, nulla assicura che un altro processo non possa +avere creato, fra l'ottenimento del nome e l'apertura del file, un altro file +con lo stesso nome; per questo motivo quando si usa il nome ottenuto da una di +queste funzioni occorre sempre aprire il nuovo file in modalità di esclusione +(cioè con l'opzione \const{O\_EXCL} per i file descriptor o con il flag +\code{x} per gli stream) che fa fallire l'apertura in caso il file sia già +esistente. + +Per evitare di dovere effettuare a mano tutti questi controlli, lo standard +POSIX definisce la funzione \funcd{tempfile}, il cui prototipo è: +\begin{prototype}{stdio.h}{FILE *tmpfile (void)} + Restituisce un file temporaneo aperto in lettura/scrittura. + + \bodydesc{La funzione ritorna il puntatore allo stream associato al file + temporaneo in caso di successo e \val{NULL} in caso di errore, nel qual + caso \var{errno} assumerà i valori: + \begin{errlist} + \item[\errcode{EINTR}] La funzione è stata interrotta da un segnale. + \item[\errcode{EEXIST}] Non è stato possibile generare un nome univoco. + \end{errlist} + ed inoltre \errval{EFAULT}, \errval{EMFILE}, \errval{ENFILE}, + \errval{ENOSPC}, \errval{EROFS} e \errval{EACCES}.} +\end{prototype} +\noindent essa restituisce direttamente uno stream già aperto (in modalità +\code{r+b}, si veda \secref{sec:file_fopen}) e pronto per l'uso, che viene +automaticamente cancellato alla sua chiusura o all'uscita dal programma. Lo +standard non specifica in quale directory verrà aperto il file, ma le +\acr{glibc} prima tentano con \const{P\_tmpdir} e poi con \file{/tmp}. Questa +funzione è rientrante e non soffre di problemi di \textit{race + condition}\index{race condition}. + +Alcune versioni meno recenti di Unix non supportano queste funzioni; in questo +caso si possono usare le vecchie funzioni \funcd{mktemp} e \func{mkstemp} che +modificano una stringa di input che serve da modello e che deve essere +conclusa da 6 caratteri \code{X} che verranno sostituiti da un codice +unico. La prima delle due è analoga a \funcd{tmpnam} e genera un nome casuale, +il suo prototipo è: +\begin{prototype}{stlib.h}{char *mktemp(char *template)} + Genera un filename univoco sostituendo le \code{XXXXXX} finali di + \param{template}. + + \bodydesc{La funzione ritorna il puntatore \param{template} in caso di + successo e \val{NULL} in caso di errore, nel qual caso \var{errno} + assumerà i valori: + \begin{errlist} + \item[\errcode{EINVAL}] \param{template} non termina con \code{XXXXXX}. + \end{errlist}} +\end{prototype} +\noindent dato che \param{template} deve poter essere modificata dalla +funzione non si può usare una stringa costante. Tutte le avvertenze riguardo +alle possibili \textit{race condition}\index{race condition} date per +\func{tmpnam} continuano a valere; inoltre in alcune vecchie implementazioni +il valore di usato per sostituire le \code{XXXXXX} viene formato con il +\acr{pid} del processo più una lettera, il che mette a disposizione solo 26 +possibilità diverse per il nome del file, e rende il nome temporaneo facile da +indovinare. Per tutti questi motivi la funzione è deprecata e non dovrebbe mai +essere usata. + +La seconda funzione, \funcd{mkstemp} è sostanzialmente equivalente a +\func{tmpfile}, ma restituisce un file descriptor invece di uno stream; il suo +prototipo è: +\begin{prototype}{stlib.h}{int mkstemp(char *template)} + Genera un file temporaneo con un nome ottenuto sostituendo le \code{XXXXXX} + finali di \param{template}. + + \bodydesc{La funzione ritorna il file descriptor in caso successo e + -1 in caso di errore, nel qual caso \var{errno} assumerà i valori: + \begin{errlist} + \item[\errcode{EINVAL}] \param{template} non termina con \code{XXXXXX}. + \item[\errcode{EEXIST}] non è riuscita a creare un file temporaneo, il + contenuto di \param{template} è indefinito. + \end{errlist}} +\end{prototype} +\noindent come per \func{mktemp} anche in questo caso \param{template} non può +essere una stringa costante. La funzione apre un file in lettura/scrittura con +la funzione \func{open}, usando l'opzione \const{O\_EXCL} (si veda +\secref{sec:file_open}), in questo modo al ritorno della funzione si ha la +certezza di essere i soli utenti del file. I permessi sono impostati al valore +\code{0600}\footnote{questo è vero a partire dalle \acr{glibc} 2.0.7, le + versioni precedenti delle \acr{glibc} e le vecchie \acr{libc5} e \acr{libc4} + usavano il valore \code{0666} che permetteva a chiunque di leggere i + contenuti del file.} (si veda \secref{sec:file_perm_overview}). + +In OpenBSD è stata introdotta un'altra funzione\footnote{introdotta anche in + Linux a partire dalle \acr{glibc} 2.1.91.} simile alle precedenti, +\funcd{mkdtemp}, che crea una directory temporanea; il suo prototipo è: +\begin{prototype}{stlib.h}{char *mkdtemp(char *template)} + Genera una directory temporaneo il cui nome è ottenuto sostituendo le + \code{XXXXXX} finali di \param{template}. + + \bodydesc{La funzione ritorna il puntatore al nome della directory in caso + successo e \val{NULL} in caso di errore, nel qual caso \var{errno} + assumerà i valori: + \begin{errlist} + \item[\errcode{EINVAL}] \param{template} non termina con \code{XXXXXX}. + \end{errlist} + più gli altri eventuali codici di errore di \func{mkdir}.} +\end{prototype} +\noindent la directory è creata con permessi \code{0700} (al solito si veda +\capref{cha:file_unix_interface} per i dettagli); dato che la creazione della +directory è sempre esclusiva i precedenti problemi di \textit{race + condition}\index{race condition} non si pongono. + + +\section{La manipolazione delle caratteristiche dei files} +\label{sec:file_infos} + +Come spiegato in \secref{sec:file_filesystem} tutte le informazioni generali +relative alle caratteristiche di ciascun file, a partire dalle informazioni +relative al controllo di accesso, sono mantenute nell'inode\index{inode}. + +Vedremo in questa sezione come sia possibile leggere tutte queste informazioni +usando la funzione \func{stat}, che permette l'accesso a tutti i dati +memorizzati nell'inode\index{inode}; esamineremo poi le varie funzioni usate +per manipolare tutte queste informazioni (eccetto quelle che riguardano la +gestione del controllo di accesso, trattate in in +\secref{sec:file_access_control}). + + +\subsection{Le funzioni \func{stat}, \func{fstat} e \func{lstat}} +\label{sec:file_stat} + +La lettura delle informazioni relative ai file è fatta attraverso la famiglia +delle funzioni \func{stat} (\funcd{stat}, \funcd{fstat} e \funcd{lstat}); +questa è la funzione che ad esempio usa il comando \cmd{ls} per poter ottenere +e mostrare tutti i dati dei files. I prototipi di queste funzioni sono i +seguenti: +\begin{functions} + \headdecl{sys/types.h} + \headdecl{sys/stat.h} + \headdecl{unistd.h} + + \funcdecl{int stat(const char *file\_name, struct stat *buf)} Legge le + informazione del file specificato da \param{file\_name} e le inserisce in + \param{buf}. + + \funcdecl{int lstat(const char *file\_name, struct stat *buf)} Identica a + \func{stat} eccetto che se il \param{file\_name} è un link simbolico vengono + lette le informazioni relativa ad esso e non al file a cui fa riferimento. + + \funcdecl{int fstat(int filedes, struct stat *buf)} Identica a \func{stat} + eccetto che si usa con un file aperto, specificato tramite il suo file + descriptor \param{filedes}. + + \bodydesc{Le funzioni restituiscono 0 in caso di successo e -1 per un + errore, nel qual caso \var{errno} assumerà uno dei valori: \errval{EBADF}, + \errval{ENOENT}, \errval{ENOTDIR}, \errval{ELOOP}, \errval{EFAULT}, + \errval{EACCES}, \errval{ENOMEM}, \errval{ENAMETOOLONG}.} +\end{functions} +\noindent il loro comportamento è identico, solo che operano rispettivamente +su un file, su un link simbolico e su un file descriptor. + +La struttura \struct{stat} usata da queste funzioni è definita nell'header +\file{sys/stat.h} e in generale dipende dall'implementazione; la versione +usata da Linux è mostrata in \figref{fig:file_stat_struct}, così come +riportata dalla pagina di manuale di \func{stat} (in realtà la definizione +effettivamente usata nel kernel dipende dall'architettura e ha altri campi +riservati per estensioni come tempi più precisi, o per il padding dei campi). \begin{figure}[!htb] \footnotesize \centering \begin{minipage}[c]{15cm} - \begin{lstlisting}[]{} + \begin{lstlisting}[labelstep=0]{}%,frame=,indent=1cm]{} struct stat { dev_t st_dev; /* device */ ino_t st_ino; /* inode */ @@ -74,244 +1450,1028 @@ struct stat { \end{lstlisting} \end{minipage} \normalsize - \caption{La struttura \texttt{stat} per la lettura delle informazioni dei - file} - \label{fig:filedir_stat_struct} + \caption{La struttura \structd{stat} per la lettura delle informazioni dei + file.} + \label{fig:file_stat_struct} \end{figure} -Si noti come i vari membri della struttura siano specificati come tipi nativi -del sistema (di quelli definiti in \tabref{tab:xxx_sys_types}, e dichiarati in -\texttt{sys/types.h}) +Si noti come i vari membri della struttura siano specificati come tipi +primitivi del sistema (di quelli definiti in +\tabref{tab:intro_primitive_types}, e dichiarati in \file{sys/types.h}). +\subsection{I tipi di file} +\label{sec:file_types} +Come riportato in \tabref{tab:file_file_types} in Linux oltre ai file e alle +directory esistono altri oggetti che possono stare su un filesystem. Il tipo +di file è ritornato dalla \func{stat} come maschera binaria nel campo +\var{st\_mode} (che che contiene anche le informazioni relative ai permessi). -\subsection{I tipi di file} -\label{sec:filedir_file_types} +Dato che il valore numerico può variare a seconda delle implementazioni, lo +standard POSIX definisce un insieme di macro per verificare il tipo di file, +queste vengono usate anche da Linux che supporta pure le estensioni allo +standard per i link simbolici e i socket\index{socket} definite da BSD; +l'elenco completo delle macro con cui è possibile estrarre l'informazione da +\var{st\_mode} è riportato in \tabref{tab:file_type_macro}. +\begin{table}[htb] + \centering + \footnotesize + \begin{tabular}[c]{|l|l|} + \hline + \textbf{Macro} & \textbf{Tipo del file} \\ + \hline + \hline + \macro{S\_ISREG(m)} & file regolare \\ + \macro{S\_ISDIR(m)} & directory \\ + \macro{S\_ISCHR(m)} & dispositivo a caratteri \\ + \macro{S\_ISBLK(m)} & dispositivo a blocchi\\ + \macro{S\_ISFIFO(m)} & fifo \\ + \macro{S\_ISLNK(m)} & link simbolico \\ + \macro{S\_ISSOCK(m)} & socket\index{socket} \\ + \hline + \end{tabular} + \caption{Macro per i tipi di file (definite in \texttt{sys/stat.h}).} + \label{tab:file_type_macro} +\end{table} + +Oltre alle macro di \tabref{tab:file_type_macro} è possibile usare +direttamente il valore di \var{st\_mode} per ricavare il tipo di file +controllando direttamente i vari bit in esso memorizzati. Per questo sempre in +\file{sys/stat.h} sono definite le costanti numeriche riportate in +\tabref{tab:file_mode_flags}. + +Il primo valore dell'elenco di \tabref{tab:file_mode_flags} è la maschera +binaria che permette di estrarre i bit nei quali viene memorizzato il tipo di +file, i valori successivi sono le costanti corrispondenti ai singoli bit, e +possono essere usati per effettuare la selezione sul tipo di file voluto, con +un'opportuna combinazione. + +\begin{table}[htb] + \centering + \footnotesize + \begin{tabular}[c]{|l|c|l|} + \hline + \textbf{Flag} & \textbf{Valore} & \textbf{Significato} \\ + \hline + \hline + \const{S\_IFMT} & 0170000 & maschera per i bit del tipo di file \\ + \const{S\_IFSOCK} & 0140000 & socket\index{socket} \\ + \const{S\_IFLNK} & 0120000 & link simbolico \\ + \const{S\_IFREG} & 0100000 & file regolare \\ + \const{S\_IFBLK} & 0060000 & dispositivo a blocchi \\ + \const{S\_IFDIR} & 0040000 & directory \\ + \const{S\_IFCHR} & 0020000 & dispositivo a caratteri \\ + \const{S\_IFIFO} & 0010000 & fifo \\ + \hline + \const{S\_ISUID} & 0004000 & set UID bit \\ + \const{S\_ISGID} & 0002000 & set GID bit \\ + \const{S\_ISVTX} & 0001000 & sticky bit \\ + \hline +% \const{S\_IRWXU} & 00700 & bitmask per i permessi del proprietario \\ + \const{S\_IRUSR} & 00400 & il proprietario ha permesso di lettura \\ + \const{S\_IWUSR} & 00200 & il proprietario ha permesso di scrittura \\ + \const{S\_IXUSR} & 00100 & il proprietario ha permesso di esecuzione\\ + \hline +% \const{S\_IRWXG} & 00070 & bitmask per i permessi del gruppo \\ + \const{S\_IRGRP} & 00040 & il gruppo ha permesso di lettura \\ + \const{S\_IWGRP} & 00020 & il gruppo ha permesso di scrittura \\ + \const{S\_IXGRP} & 00010 & il gruppo ha permesso di esecuzione \\ + \hline +% \const{S\_IRWXO} & 00007 & bitmask per i permessi di tutti gli altri\\ + \const{S\_IROTH} & 00004 & gli altri hanno permesso di lettura \\ + \const{S\_IWOTH} & 00002 & gli altri hanno permesso di esecuzione \\ + \const{S\_IXOTH} & 00001 & gli altri hanno permesso di esecuzione \\ + \hline + \end{tabular} + \caption{Costanti per l'identificazione dei vari bit che compongono il campo + \var{st\_mode} (definite in \file{sys/stat.h}).} + \label{tab:file_mode_flags} +\end{table} + +Ad esempio se si volesse impostare una condizione che permetta di controllare +se un file è una directory o un file ordinario si potrebbe definire la macro +di preprocessore: +\begin{lstlisting}[labelstep=0,frame=,indent=1cm]{} +#define IS_FILE_DIR(x) (((x) & S_IFMT) & (S_IFDIR | S_IFREG)) +\end{lstlisting} +in cui prima si estraggono da \var{st\_mode} i bit relativi al tipo di file e +poi si effettua il confronto con la combinazione di tipi scelta. + + +\subsection{Le dimensioni dei file} +\label{sec:file_file_size} -Come riportato in \tabref{tab:fileintr_file_types} in linux oltre ai file e -alle directory esistono vari altri oggetti che possono stare su un filesystem; -il tipo di file è ritornato dalla \texttt{stat} nel campo \texttt{st\_mode}, -dato che il valore numerico può variare a seconda delle implementazioni +Il campo \var{st\_size} contiene la dimensione del file in byte (se si tratta +di un file regolare, nel caso di un link simbolico la dimensione è quella del +pathname che contiene, per le fifo è sempre nullo). +Il campo \var{st\_blocks} definisce la lunghezza del file in blocchi di 512 +byte. Il campo \var{st\_blksize} infine definisce la dimensione preferita per +i trasferimenti sui file (che è la dimensione usata anche dalle librerie del C +per l'interfaccia degli stream); scrivere sul file a blocchi di dati di +dimensione inferiore sarebbe inefficiente. + +Si tenga conto che la lunghezza del file riportata in \var{st\_size} non è +detto che corrisponda all'occupazione dello spazio su disco per via della +possibile esistenza dei cosiddetti \textit{holes} (letteralmente +\textsl{buchi}) che si formano tutte le volte che si va a scrivere su un file +dopo aver eseguito una \func{lseek} (vedi \secref{sec:file_lseek}) oltre la +sua fine. + +In questo caso si avranno risultati differenti a seconda del modo in cui si +calcola la lunghezza del file, ad esempio il comando \cmd{du}, (che riporta il +numero di blocchi occupati) potrà dare una dimensione inferiore, mentre se si +legge dal file (ad esempio usando il comando \cmd{wc -c}), dato che in tal +caso per le parti non scritte vengono restituiti degli zeri, si avrà lo stesso +risultato di \cmd{ls}. + +Se è sempre possibile allargare un file, scrivendoci sopra od usando la +funzione \func{lseek} per spostarsi oltre la sua fine, esistono anche casi in +cui si può avere bisogno di effettuare un troncamento, scartando i dati +presenti al di là della dimensione scelta come nuova fine del file. + +Un file può sempre essere troncato a zero aprendolo con il flag +\const{O\_TRUNC}, ma questo è un caso particolare; per qualunque altra +dimensione si possono usare le due funzioni \funcd{truncate} e +\funcd{ftruncate}, i cui prototipi sono: +\begin{functions} + \headdecl{unistd.h} \funcdecl{int truncate(const char *file\_name, off\_t + length)} Fa si che la dimensione del file \param{file\_name} sia troncata + ad un valore massimo specificato da \param{lenght}. + + \funcdecl{int ftruncate(int fd, off\_t length))} Identica a \func{truncate} + eccetto che si usa con un file aperto, specificato tramite il suo file + descriptor \param{fd}. + + \bodydesc{Le funzioni restituiscono zero in caso di successo e -1 per un + errore, nel qual caso \var{errno} viene impostata opportunamente; per + \func{ftruncate} si hanno i valori: + \begin{errlist} + \item[\errcode{EBADF}] \param{fd} non è un file descriptor. + \item[\errcode{EINVAL}] \param{fd} è un riferimento ad un + socket\index{socket}, non a un file o non è aperto in scrittura. + \end{errlist} + per \func{truncate} si hanno: + \begin{errlist} + \item[\errcode{EACCES}] il file non ha permesso di scrittura o non si ha il + permesso di esecuzione una delle directory del pathname. + \item[\errcode{ETXTBSY}] Il file è un programma in esecuzione. + \end{errlist} + ed anche \errval{ENOTDIR}, \errval{ENAMETOOLONG}, \errval{ENOENT}, + \errval{EROFS}, \errval{EIO}, \errval{EFAULT}, \errval{ELOOP}.} +\end{functions} + +Se il file è più lungo della lunghezza specificata i dati in eccesso saranno +perduti; il comportamento in caso di lunghezza inferiore non è specificato e +dipende dall'implementazione: il file può essere lasciato invariato o esteso +fino alla lunghezza scelta; in quest'ultimo caso lo spazio viene riempito con +zeri (e in genere si ha la creazione di un \textit{hole} nel file). -\subsection{La dimensione dei file} -\label{sec:filedir_file_size} \subsection{I tempi dei file} -\label{sec:filedir_file_times} +\label{sec:file_file_times} + +Il sistema mantiene per ciascun file tre tempi. Questi sono registrati +nell'inode\index{inode} insieme agli altri attributi del file e possono essere +letti tramite la funzione \func{stat}, che li restituisce attraverso tre campi +della struttura \struct{stat} di \figref{fig:file_stat_struct}. Il significato +di detti tempi e dei relativi campi è riportato nello schema in +\tabref{tab:file_file_times}, dove è anche riportato un esempio delle funzioni +che effettuano cambiamenti su di essi. + +\begin{table}[htb] + \centering + \footnotesize + \begin{tabular}[c]{|c|l|l|c|} + \hline + \textbf{Membro} & \textbf{Significato} & \textbf{Funzione} + & \textbf{Opzione di \cmd{ls}} \\ + \hline + \hline + \var{st\_atime}& ultimo accesso ai dati del file &\func{read}, + \func{utime} & \cmd{-u}\\ + \var{st\_mtime}& ultima modifica ai dati del file &\func{write}, + \func{utime} & default\\ + \var{st\_ctime}& ultima modifica ai dati dell'inode&\func{chmod}, + \func{utime} & \cmd{-c} \\ + \hline + \end{tabular} + \caption{I tre tempi associati a ciascun file.} + \label{tab:file_file_times} +\end{table} + +Il primo punto da tenere presente è la differenza fra il cosiddetto tempo di +modifica (il \textit{modification time} \var{st\_mtime}) e il tempo di +cambiamento di stato (il \textit{change time} \var{st\_ctime}). Il primo +infatti fa riferimento ad una modifica del contenuto di un file, mentre il +secondo ad una modifica dell'inode\index{inode}; siccome esistono molte +operazioni (come la funzione \func{link} e molte altre che vedremo in seguito) +che modificano solo le informazioni contenute nell'inode\index{inode} senza +toccare il file, diventa necessario l'utilizzo di un altro tempo. + +Il sistema non tiene conto dell'ultimo accesso all'inode\index{inode}, +pertanto funzioni come \func{access} o \func{stat} non hanno alcuna influenza +sui tre tempi. Il tempo di ultimo accesso (ai dati) viene di solito usato per +cancellare i file che non servono più dopo un certo lasso di tempo (ad esempio +\cmd{leafnode} cancella i vecchi articoli sulla base di questo tempo). + +Il tempo di ultima modifica invece viene usato da \cmd{make} per decidere +quali file necessitano di essere ricompilati o (talvolta insieme anche al +tempo di cambiamento di stato) per decidere quali file devono essere +archiviati per il backup. Il comando \cmd{ls} (quando usato con le opzioni +\cmd{-l} o \cmd{-t}) mostra i tempi dei file secondo lo schema riportato +nell'ultima colonna di \tabref{tab:file_file_times}. + +\begin{table}[htb] + \centering + \footnotesize + \begin{tabular}[c]{|l|c|c|c|c|c|c|l|} + \hline + \multicolumn{1}{|p{3cm}|}{\centering{\vspace{6pt}\textbf{Funzione}}} & + \multicolumn{3}{|p{3.6cm}|}{\centering{ + \textbf{File o directory del riferimento}}}& + \multicolumn{3}{|p{3.6cm}|}{\centering{ + \textbf{Directory contenente il riferimento}}} + &\multicolumn{1}{|p{3.6cm}|}{\centering{\vspace{6pt}\textbf{Note}}} \\ + \cline{2-7} + \cline{2-7} + \multicolumn{1}{|p{3cm}|}{} + &\multicolumn{1}{|p{.9cm}|}{\centering{\textsl{(a)}}} + &\multicolumn{1}{|p{.9cm}|}{\centering{\textsl{(m)}}} + &\multicolumn{1}{|p{.9cm}|}{\centering{\textsl{(c)}}} + &\multicolumn{1}{|p{.9cm}|}{\centering{\textsl{(a)}}} + &\multicolumn{1}{|p{.9cm}|}{\centering{\textsl{(m)}}} + &\multicolumn{1}{|p{.9cm}|}{\centering{\textsl{(c)}}} + &\multicolumn{1}{|p{3cm}|}{} \\ + \hline + \hline + \func{chmod}, \func{fchmod} + & & &$\bullet$& & & & \\ + \func{chown}, \func{fchown} + & & &$\bullet$& & & & \\ + \func{creat} + &$\bullet$&$\bullet$&$\bullet$& &$\bullet$&$\bullet$& con + \const{O\_CREATE} \\ \func{creat} + & &$\bullet$&$\bullet$& &$\bullet$&$\bullet$& + con \const{O\_TRUNC} \\ \func{exec} + &$\bullet$& & & & & & \\ + \func{lchown} + & & &$\bullet$& & & & \\ + \func{link} + & & &$\bullet$& &$\bullet$&$\bullet$& \\ + \func{mkdir} + &$\bullet$&$\bullet$&$\bullet$& &$\bullet$&$\bullet$& \\ + \func{mkfifo} + &$\bullet$&$\bullet$&$\bullet$& &$\bullet$&$\bullet$& \\ + \func{open} + &$\bullet$&$\bullet$&$\bullet$& &$\bullet$&$\bullet$& con + \const{O\_CREATE} \\ \func{open} + & &$\bullet$&$\bullet$& & & & con + \const{O\_TRUNC} \\ \func{pipe} + &$\bullet$&$\bullet$&$\bullet$& & & & \\ + \func{read} + &$\bullet$& & & & & & \\ + \func{remove} + & & &$\bullet$& &$\bullet$&$\bullet$& se esegue + \func{unlink}\\ \func{remove} + & & & & &$\bullet$&$\bullet$& se esegue + \func{rmdir}\\ \func{rename} + & & &$\bullet$& &$\bullet$&$\bullet$& per entrambi + gli argomenti\\ \func{rmdir} + & & & & &$\bullet$&$\bullet$& \\ + \func{truncate}, \func{ftruncate} + & &$\bullet$&$\bullet$& & & & \\ + \func{unlink} + & & &$\bullet$& &$\bullet$&$\bullet$& \\ + \func{utime} + &$\bullet$&$\bullet$&$\bullet$& & & & \\ + \func{write} + & &$\bullet$&$\bullet$& & & & \\ + \hline + \end{tabular} + \caption{Prospetto dei cambiamenti effettuati sui tempi di ultimo + accesso \textsl{(a)}, ultima modifica \textsl{(m)} e ultimo cambiamento + \textsl{(c)} dalle varie funzioni operanti su file e directory.} + \label{tab:file_times_effects} +\end{table} + +L'effetto delle varie funzioni di manipolazione dei file sui tempi è +illustrato in \tabref{tab:file_times_effects}. Si sono riportati gli effetti +sia per il file a cui si fa riferimento, sia per la directory che lo contiene; +questi ultimi possono essere capiti se si tiene conto di quanto già detto, e +cioè che anche le directory sono file (che contengono una lista di nomi) che +il sistema tratta in maniera del tutto analoga a tutti gli altri. + +Per questo motivo tutte le volte che compiremo un'operazione su un file che +comporta una modifica del nome contenuto nella directory, andremo anche a +scrivere sulla directory che lo contiene cambiandone il tempo di modifica. Un +esempio di questo può essere la cancellazione di un file, invece leggere o +scrivere o cambiare i permessi di un file ha effetti solo sui tempi di +quest'ultimo. + +Si noti infine come \var{st\_ctime} non abbia nulla a che fare con il tempo di +creazione del file, usato in molti altri sistemi operativi, ma che in Unix non +esiste. Per questo motivo quando si copia un file, a meno di preservare +esplicitamente i tempi (ad esempio con l'opzione \cmd{-p} di \cmd{cp}) esso +avrà sempre il tempo corrente come data di ultima modifica. + + +\subsection{La funzione \func{utime}} +\label{sec:file_utime} + +I tempi di ultimo accesso e modifica possono essere cambiati usando la +funzione \funcd{utime}, il cui prototipo è: +\begin{prototype}{utime.h} +{int utime(const char *filename, struct utimbuf *times)} + +Cambia i tempi di ultimo accesso e modifica dell'inode\index{inode} +specificato da \param{filename} secondo i campi \var{actime} e \var{modtime} +di \param{times}. Se questa è \val{NULL} allora viene usato il tempo corrente. + +\bodydesc{La funzione restituisce 0 in caso di successo e -1 in caso di + errore, nel qual caso \var{errno} assumerà uno dei valori: + \begin{errlist} + \item[\errcode{EACCES}] non si ha il permesso di scrittura sul file. + \item[\errcode{ENOENT}] \param{filename} non esiste. + \end{errlist}} +\end{prototype} + +La funzione prende come argomento \param{times} una struttura +\struct{utimebuf}, la cui definizione è riportata in +\figref{fig:struct_utimebuf}, con la quale si possono specificare i nuovi +valori che si vogliono impostare per tempi. + +\begin{figure}[!htb] + \footnotesize \centering + \begin{minipage}[c]{15cm} + \begin{lstlisting}[labelstep=0]{}%,frame=,indent=1cm]{} +struct utimbuf { + time_t actime; /* access time */ + time_t modtime; /* modification time */ +}; + \end{lstlisting} + \end{minipage} + \normalsize + \caption{La struttura \structd{utimbuf}, usata da \func{utime} per modificare + i tempi dei file.} + \label{fig:struct_utimebuf} +\end{figure} -\subsection{La funzione \texttt{utime}} -\label{sec:filedir_utime} +L'effetto della funzione e i privilegi necessari per eseguirla dipendono da +cosa è l'argomento \param{times}; se è \val{NULL} la funzione imposta il +tempo corrente ed è sufficiente avere accesso in scrittura al file; se invece +si è specificato un valore la funzione avrà successo solo se si è proprietari +del file (o si hanno i privilegi di amministratore). +Si tenga presente che non è comunque possibile specificare il tempo di +cambiamento di stato del file, che viene comunque cambiato dal kernel tutte le +volte che si modifica l'inode\index{inode} (quindi anche alla chiamata di +\func{utime}). Questo serve anche come misura di sicurezza per evitare che si +possa modificare un file nascondendo completamente le proprie tracce. In +realtà la cosa resta possibile, se si è in grado di accedere al file di +dispositivo, scrivendo direttamente sul disco senza passare attraverso il +filesystem, ma ovviamente in questo modo la cosa è molto più complicata da +realizzare. \section{Il controllo di accesso ai file} -\label{sec:filedir_access_control} +\label{sec:file_access_control} + +Una delle caratteristiche fondamentali di tutti i sistemi unix-like è quella +del controllo di accesso ai file, che viene implementato per qualunque +filesystem standard.\footnote{per standard si intende che implementa le + caratteristiche previste dallo standard POSIX. In Linux sono disponibili + anche una serie di altri filesystem, come quelli di Windows e del Mac, che + non supportano queste caratteristiche.} In questa sezione ne esamineremo i +concetti essenziali e le funzioni usate per gestirne i vari aspetti. + + +\subsection{I permessi per l'accesso ai file} +\label{sec:file_perm_overview} + +Ad ogni file Linux associa sempre l'utente che ne è proprietario (il +cosiddetto \textit{owner}) ed un gruppo di appartenenza, secondo il meccanismo +degli identificatori di utente e gruppo (\acr{uid} e \acr{gid}). Questi valori +sono accessibili da programma tramite la funzione \func{stat}, e sono +mantenuti nei campi \var{st\_uid} e \var{st\_gid} della struttura +\struct{stat} (si veda \secref{sec:file_stat}).\footnote{Questo è vero solo + per filesystem di tipo Unix, ad esempio non è vero per il filesystem vfat di + Windows, che non fornisce nessun supporto per l'accesso multiutente, e per + il quale i permessi vengono assegnati in maniera fissa con un opzione in + fase di montaggio.} + +Il controllo di accesso ai file segue un modello abbastanza semplice che +prevede tre permessi fondamentali strutturati su tre livelli di accesso. +Esistono varie estensioni a questo modello,\footnote{come le \textit{Access + Control List} che possono essere aggiunte al filesystem standard con + opportune patch, la cui introduzione nei kernel ufficiali è iniziata con la + serie 2.5.x. per arrivare a meccanismi di controllo ancora più sofisticati + come il \textit{mandatory access control} di SE-Linux.} ma nella maggior +parte dei casi il meccanismo standard è più che sufficiente a soddisfare tutte +le necessità più comuni. I tre permessi di base associati ad ogni file sono: +\begin{itemize*} +\item il permesso di lettura (indicato con la lettera \texttt{r}, dall'inglese + \textit{read}). +\item il permesso di scrittura (indicato con la lettera \texttt{w}, + dall'inglese \textit{write}). +\item il permesso di esecuzione (indicato con la lettera \texttt{x}, + dall'inglese \textit{execute}). +\end{itemize*} +mentre i tre livelli su cui sono divisi i privilegi sono: +\begin{itemize*} +\item i privilegi per l'utente proprietario del file. +\item i privilegi per un qualunque utente faccia parte del gruppo cui + appartiene il file. +\item i privilegi per tutti gli altri utenti. +\end{itemize*} + +L'insieme dei permessi viene espresso con un numero a 12 bit; di questi i nove +meno significativi sono usati a gruppi di tre per indicare i permessi base di +lettura, scrittura ed esecuzione e sono applicati rispettivamente +rispettivamente al proprietario, al gruppo, a tutti gli altri. + +\begin{figure}[htb] + \centering + \includegraphics[width=6cm]{img/fileperm} + \caption{Lo schema dei bit utilizzati per specificare i permessi di un file + contenuti nel campo \var{st\_mode} di \struct{fstat}.} + \label{fig:file_perm_bit} +\end{figure} + +I restanti tre bit (noti come \acr{suid}, \acr{sgid}, e \textsl{sticky}) sono +usati per indicare alcune caratteristiche più complesse del meccanismo del +controllo di accesso su cui torneremo in seguito (in +\secref{sec:file_suid_sgid} e \secref{sec:file_sticky}); lo schema di +allocazione dei bit è riportato in \figref{fig:file_perm_bit}. + +Anche i permessi, come tutte le altre informazioni pertinenti al file, sono +memorizzati nell'inode\index{inode}; in particolare essi sono contenuti in +alcuni bit del campo \var{st\_mode} della struttura \struct{stat} (si veda di +nuovo \figref{fig:file_stat_struct}). + +In genere ci si riferisce ai tre livelli dei privilegi usando le lettere +\cmd{u} (per \textit{user}), \cmd{g} (per \textit{group}) e \cmd{o} (per +\textit{other}), inoltre se si vuole indicare tutti i raggruppamenti insieme +si usa la lettera \cmd{a} (per \textit{all}). Si tenga ben presente questa +distinzione dato che in certi casi, mutuando la terminologia in uso nel VMS, +si parla dei permessi base come di permessi per \textit{owner}, \textit{group} +ed \textit{all}, le cui iniziali possono dar luogo a confusione. Le costanti +che permettono di accedere al valore numerico di questi bit nel campo +\var{st\_mode} sono riportate in \tabref{tab:file_bit_perm}. + +\begin{table}[htb] + \centering + \footnotesize + \begin{tabular}[c]{|c|l|} + \hline + \textbf{\var{st\_mode}} bit & \textbf{Significato} \\ + \hline + \hline + \const{S\_IRUSR} & \textit{user-read}, l'utente può leggere \\ + \const{S\_IWUSR} & \textit{user-write}, l'utente può scrivere \\ + \const{S\_IXUSR} & \textit{user-execute}, l'utente può eseguire \\ + \hline + \const{S\_IRGRP} & \textit{group-read}, il gruppo può leggere \\ + \const{S\_IWGRP} & \textit{group-write}, il gruppo può scrivere \\ + \const{S\_IXGRP} & \textit{group-execute}, il gruppo può eseguire\\ + \hline + \const{S\_IROTH} & \textit{other-read}, tutti possono leggere \\ + \const{S\_IWOTH} & \textit{other-write}, tutti possono scrivere \\ + \const{S\_IXOTH} & \textit{other-execute}, tutti possono eseguire\\ + \hline + \end{tabular} + \caption{I bit dei permessi di accesso ai file, come definiti in + \texttt{}} + \label{tab:file_bit_perm} +\end{table} + +I permessi vengono usati in maniera diversa dalle varie funzioni, e a seconda +che si riferiscano a dei file, dei link simbolici o delle directory; qui ci +limiteremo ad un riassunto delle regole generali, entrando nei dettagli più +avanti. + +La prima regola è che per poter accedere ad un file attraverso il suo pathname +occorre il permesso di esecuzione in ciascuna delle directory che compongono +il pathname; lo stesso vale per aprire un file nella directory corrente (per +la quale appunto serve il diritto di esecuzione). + +Per una directory infatti il permesso di esecuzione significa che essa può +essere attraversata nella risoluzione del pathname, ed è distinto dal permesso +di lettura che invece implica che si può leggere il contenuto della directory. + +Questo significa che se si ha il permesso di esecuzione senza permesso di +lettura si potrà lo stesso aprire un file in una directory (se si hanno i +permessi opportuni per il medesimo) ma non si potrà vederlo con \cmd{ls} +(mentre per crearlo occorrerà anche il permesso di scrittura per la +directory). + +Avere il permesso di lettura per un file consente di aprirlo con le opzioni +(si veda quanto riportato in \tabref{tab:file_open_flags}) di sola lettura o +di lettura/scrittura e leggerne il contenuto. Avere il permesso di scrittura +consente di aprire un file in sola scrittura o lettura/scrittura e modificarne +il contenuto, lo stesso permesso è necessario per poter troncare il file. + +Non si può creare un file fintanto che non si disponga del permesso di +esecuzione e di quello di scrittura per la directory di destinazione; gli +stessi permessi occorrono per cancellare un file da una directory (si ricordi +che questo non implica necessariamente la rimozione del contenuto del file dal +disco), non è necessario nessun tipo di permesso per il file stesso (infatti +esso non viene toccato, viene solo modificato il contenuto della directory, +rimuovendo la voce che ad esso fa riferimento). + +Per poter eseguire un file (che sia un programma compilato od uno script di +shell, od un altro tipo di file eseguibile riconosciuto dal kernel), occorre +avere il permesso di esecuzione, inoltre solo i file regolari possono essere +eseguiti. + +I permessi per un link simbolico sono ignorati, contano quelli del file a cui +fa riferimento; per questo in genere il comando \cmd{ls} riporta per un link +simbolico tutti i permessi come concessi; utente e gruppo a cui esso +appartiene vengono pure ignorati quando il link viene risolto, vengono +controllati solo quando viene richiesta la rimozione del link e quest'ultimo è +in una directory con lo \textsl{sticky bit} impostato (si veda +\secref{sec:file_sticky}). + +La procedura con cui il kernel stabilisce se un processo possiede un certo +permesso (di lettura, scrittura o esecuzione) si basa sul confronto fra +l'utente e il gruppo a cui il file appartiene (i valori di \var{st\_uid} e +\var{st\_gid} accennati in precedenza) e l'user-ID effettivo, il group-ID +effettivo e gli eventuali group-ID supplementari del processo.\footnote{in + realtà Linux, per quanto riguarda l'accesso ai file, utilizza gli gli + identificatori del gruppo \textit{filesystem} (si ricordi quanto esposto in + \secref{sec:proc_perms}), ma essendo questi del tutto equivalenti ai primi, + eccetto il caso in cui si voglia scrivere un server NFS, ignoreremo questa + differenza.} + +Per una spiegazione dettagliata degli identificatori associati ai processi si +veda \secref{sec:proc_perms}; normalmente, a parte quanto vedremo in +\secref{sec:file_suid_sgid}, l'user-ID effettivo e il group-ID effettivo +corrispondono ai valori dell'\acr{uid} e del \acr{gid} dell'utente che ha +lanciato il processo, mentre i group-ID supplementari sono quelli dei gruppi +cui l'utente appartiene. + +I passi attraverso i quali viene stabilito se il processo possiede il diritto +di accesso sono i seguenti: +\begin{enumerate} +\item Se l'user-ID effettivo del processo è zero (corrispondente + all'amministratore) l'accesso è sempre garantito senza nessun ulteriore + controllo. Per questo motivo \textsl{root} ha piena libertà di accesso a + tutti i file. +\item Se l'user-ID effettivo del processo è uguale all'\acr{uid} del + proprietario del file (nel qual caso si dice che il processo è proprietario + del file) allora: + \begin{itemize*} + \item se il relativo\footnote{per relativo si intende il bit di user-read se + il processo vuole accedere in scrittura, quello di user-write per + l'accesso in scrittura, etc.} bit dei permessi d'accesso dell'utente è + impostato, l'accesso è consentito + \item altrimenti l'accesso è negato + \end{itemize*} +\item Se il group-ID effettivo del processo o uno dei group-ID supplementari + dei processi corrispondono al \acr{gid} del file allora: + \begin{itemize*} + \item se il bit dei permessi d'accesso del gruppo è impostato, l'accesso è + consentito, + \item altrimenti l'accesso è negato + \end{itemize*} +\item se il bit dei permessi d'accesso per tutti gli altri è impostato, + l'accesso è consentito, altrimenti l'accesso è negato. +\end{enumerate} + +Si tenga presente che questi passi vengono eseguiti esattamente in +quest'ordine. Questo vuol dire che se un processo è il proprietario di un file, +l'accesso è consentito o negato solo sulla base dei permessi per l'utente; i +permessi per il gruppo non vengono neanche controllati. Lo stesso vale se il +processo appartiene ad un gruppo appropriato, in questo caso i permessi per +tutti gli altri non vengono controllati. -In unix è implementata da qualunque filesystem standard una forma elementare -(ma adatta alla maggior parte delle esigenze) di controllo di accesso ai -files. Torneremo sull'argomento in dettaglio più avanti (vedi -\secref{sec:filedir_access_control}), qui ci limitiamo ad una introduzione dei -concetti essenziali. +\subsection{I bit \acr{suid} e \acr{sgid}} +\label{sec:file_suid_sgid} -Si tenga conto poi che quanto diremo è vero solo per filesystem di tipo Unix, -e non è detto che sia applicabile (ed infatti non è vero per il filesystem di -Windows) a un filesystem qualunque. Esistono inoltre estensioni che permettono -di implementare le ACL (\textit{Access Control List}) che sono un meccanismo -di controllo di accesso molto più sofisticato. +Come si è accennato (in \secref{sec:file_perm_overview}) nei dodici bit del +campo \var{st\_mode} di \struct{stat} che vengono usati per il controllo di +accesso oltre ai bit dei permessi veri e propri, ci sono altri tre bit che +vengono usati per indicare alcune proprietà speciali dei file. Due di questi +sono i bit detti \acr{suid} (da \textit{set-user-ID bit}) e \acr{sgid} (da +\textit{set-group-ID bit}) che sono identificati dalle costanti +\const{S\_ISUID} e \const{S\_ISGID}. -Ad ogni file Unix associa sempre l'utente che ne è proprietario (il cosiddetto -\textit{owner}) e il gruppo di appartenenza, secondo il meccanismo degli uid e -gid accennato in \secref{sec:intro_multiuser}, e un insieme di permessi che -sono divisi in tre classi, e cioè attribuiti rispettivamente al proprietario, -a qualunque utente faccia parte del gruppo cui appartiene il file, e a tutti -gli altri utenti. +Come spiegato in dettaglio in \secref{sec:proc_exec}, quando si lancia un +programma il comportamento normale del kernel è quello di impostare gli +identificatori del gruppo \textit{effective} del nuovo processo al valore dei +corrispondenti del gruppo \textit{real} del processo corrente, che normalmente +corrispondono dell'utente con cui si è entrati nel sistema. -I permessi sono espressi da un insieme di 12 bit: di questi i nove meno -significativi sono usati a gruppi di tre per indicare i permessi base di -lettura, scrittura ed esecuzione (indicati rispettivamente con le lettere -\textit{w}, \textit{r} \textit{x}) applicabili rispettivamente al -proprietario, al gruppo, a tutti (una descrizione più dettagliata dei vari -permessi associati ai file è riportata in \secref{sec:filedir_suid_sgid}). I -restanti tre bit sono usati per indicare alcune caratteristiche più complesse -(\textit{suid}, \textit{sgid}, e \textit{sticky}) su cui pure torneremo in -seguito (vedi \secref{sec:filedir_suid_sgid} e \secref{sec:filedir_sticky}). +Se però il file del programma (che ovviamente deve essere +eseguibile\footnote{per motivi di sicurezza il kernel ignora i bit \acr{suid} + e \acr{sgid} per gli script eseguibili.}) ha il bit \acr{suid} impostato, il +kernel assegnerà come user-ID effettivo al nuovo processo l'\acr{uid} del +proprietario del file al posto dell'\acr{uid} del processo originario. Avere +il bit \acr{sgid} impostato ha lo stesso effetto sul group-ID effettivo del +processo. -Tutte queste informazioni sono tenute per ciascun file nell'inode. Quando un -processo cerca l'accesso al file esso controlla i propri uid e gid -confrontandoli con quelli del file e se l'operazione richiesta è compatibile -con i permessi associati al file essa viene eseguita, altrimenti viene -bloccata ed è restituito un errore di \texttt{EPERM}. Questo procedimento non -viene eseguito per l'amministratore di sistema (il cui uid è zero) il quale ha -pertanto accesso senza restrizione a qualunque file del sistema. +I bit \acr{suid} e \acr{sgid} vengono usati per permettere agli utenti normali +di usare programmi che abbisognano di privilegi speciali; l'esempio classico è +il comando \cmd{passwd} che ha la necessità di modificare il file delle +password, quest'ultimo ovviamente può essere scritto solo dall'amministratore, +ma non è necessario chiamare l'amministratore per cambiare la propria +password. Infatti il comando \cmd{passwd} appartiene a root ma ha il bit +\acr{suid} impostato per cui quando viene lanciato da un utente normale parte +con i privilegi di root. -% In realtà il procedimento è più complesso di quanto descritto in maniera -% elementare qui; inoltre ad un processo sono associati diversi identificatori, -% torneremo su questo in maggiori dettagli in seguito in \secref{sec:proc_perms}. +Chiaramente avere un processo che ha privilegi superiori a quelli che avrebbe +normalmente l'utente che lo ha lanciato comporta vari rischi, e questo tipo di +programmi devono essere scritti accuratamente per evitare che possano essere +usati per guadagnare privilegi non consentiti (l'argomento è affrontato in +dettaglio in \secref{sec:proc_perms}). +La presenza dei bit \acr{suid} e \acr{sgid} su un file può essere rilevata con +il comando \cmd{ls -l}, che visualizza una lettera \cmd{s} al posto della +\cmd{x} in corrispondenza dei permessi di utente o gruppo. La stessa lettera +\cmd{s} può essere usata nel comando \cmd{chmod} per impostare questi bit. +Infine questi bit possono essere controllati all'interno di \var{st\_mode} con +l'uso delle due costanti \const{S\_ISUID} e \const{S\_IGID}, i cui valori sono +riportati in \tabref{tab:file_mode_flags}. +Gli stessi bit vengono ad assumere in significato completamente diverso per le +directory, normalmente infatti Linux usa la convenzione di SVr4 per indicare +con questi bit l'uso della semantica BSD nella creazione di nuovi file (si +veda \secref{sec:file_ownership} per una spiegazione dettagliata al +proposito). -\subsection{I flag \texttt{suid} e \texttt{sgid}} -\label{sec:filedir_suid_sgid} +Infine Linux utilizza il bit \acr{sgid} per una ulteriore estensione mutuata +da SVr4. Il caso in cui un file ha il bit \acr{sgid} impostato senza che lo +sia anche il corrispondente bit di esecuzione viene utilizzato per attivare +per quel file il \textit{mandatory locking} (affronteremo questo argomento in +dettaglio più avanti, in \secref{sec:file_mand_locking}). -\subsection{La titolarità di nuovi files e directory} -\label{sec:filedir_ownership} -\subsection{La funzione \texttt{access}} -\label{sec:filedir_access} +\subsection{Il bit \textsl{sticky}} +\label{sec:file_sticky} -\subsection{La funzione \texttt{umask}} -\label{sec:filedir_umask} +L'ultimo dei bit rimanenti, identificato dalla costante \const{S\_ISVTX}, è in +parte un rimasuglio delle origini dei sistemi Unix. A quell'epoca infatti la +memoria virtuale e l'accesso ai files erano molto meno sofisticati e per +ottenere la massima velocità possibile per i programmi usati più comunemente +si poteva impostare questo bit. -\subsection{Le funzioni \texttt{chmod} e \texttt{fchmod}} -\label{sec:filedir_chmod} +L'effetto di questo bit era che il segmento di testo del programma (si veda +\secref{sec:proc_mem_layout} per i dettagli) veniva scritto nella swap la +prima volta che questo veniva lanciato, e vi permaneva fino al riavvio della +macchina (da questo il nome di \textsl{sticky bit}); essendo la swap un file +continuo indicizzato direttamente in questo modo si poteva risparmiare in +tempo di caricamento rispetto alla ricerca del file su disco. Lo +\textsl{sticky bit} è indicato usando la lettera \cmd{t} al posto della +\cmd{x} nei permessi per gli altri. -\subsection{Il flag \texttt{sticky}} -\label{sec:filedir_sticky} +Ovviamente per evitare che gli utenti potessero intasare la swap solo +l'amministratore era in grado di impostare questo bit, che venne chiamato +anche con il nome di \textit{saved text bit}, da cui deriva quello della +costante. Le attuali implementazioni di memoria virtuale e filesystem rendono +sostanzialmente inutile questo procedimento. -\subsection{Le funzioni \texttt{chown}, \texttt{fchown} e \texttt{lchown}} -\label{sec:filedir_chown} +Benché ormai non venga più utilizzato per i file, lo \textsl{sticky bit} ha +invece assunto un uso importante per le directory;\footnote{lo \textsl{sticky + bit} per le directory è un'estensione non definita nello standard POSIX, + Linux però la supporta, così come BSD e SVr4.} in questo caso se tale bit è +impostato un file potrà essere rimosso dalla directory soltanto se l'utente ha +il permesso di scrittura su di essa ed inoltre è vera una delle seguenti +condizioni: +\begin{itemize*} +\item l'utente è proprietario del file +\item l'utente è proprietario della directory +\item l'utente è l'amministratore +\end{itemize*} +un classico esempio di directory che ha questo bit impostato è \file{/tmp}, i +permessi infatti di solito sono i seguenti: +\begin{verbatim} +$ ls -ld /tmp +drwxrwxrwt 6 root root 1024 Aug 10 01:03 /tmp +\end{verbatim}%$ +quindi con lo \textsl{sticky bit} bit impostato. In questo modo qualunque +utente nel sistema può creare dei file in questa directory (che, come +suggerisce il nome, è normalmente utilizzata per la creazione di file +temporanei), ma solo l'utente che ha creato un certo file potrà cancellarlo o +rinominarlo. In questo modo si evita che un utente possa, più o meno +consapevolmente, cancellare i file temporanei creati degli altri utenti. -\section{La manipolazione delle directories} -\label{sec:filedir_dir_handling} +\subsection{La titolarità di nuovi file e directory} +\label{sec:file_ownership} -\subsection{Le funzioni \texttt{mkdir} e \texttt{rmdir}} -\label{sec:filedir_dir_creat_rem} +Vedremo in \secref{sec:file_base_func} con quali funzioni si possono creare +nuovi file, in tale occasione vedremo che è possibile specificare in sede di +creazione quali permessi applicare ad un file, però non si può indicare a +quale utente e gruppo esso deve appartenere. Lo stesso problema di presenta +per la creazione di nuove directory (procedimento descritto in +\secref{sec:file_dir_creat_rem}). -Per creare una nuova directory si può usare la seguente funzione, omonima -dell'analogo comando di shell \texttt{mkdir}; per accedere ai tipi usati -programma deve includere il file \texttt{sys/types.h}. +Lo standard POSIX prescrive che l'\acr{uid} del nuovo file corrisponda +all'user-ID effettivo del processo che lo crea; per il \acr{gid} invece prevede +due diverse possibilità: +\begin{itemize*} +\item il \acr{gid} del file corrisponde al group-ID effettivo del processo. +\item il \acr{gid} del file corrisponde al \acr{gid} della directory in cui + esso è creato. +\end{itemize*} +in genere BSD usa sempre la seconda possibilità, che viene per questo chiamata +semantica BSD. Linux invece segue quella che viene chiamata semantica SVr4; di +norma cioè il nuovo file viene creato, seguendo la prima opzione, con il +\acr{gid} del processo, se però la directory in cui viene creato il file ha il +bit \acr{sgid} impostato allora viene usata la seconda opzione. -\begin{prototype}{sys/stat.h} -{int mkdir (const char * dirname, mode\_t mode)} - Questa funzione crea una nuova directory vuota con il nome indicato da - \texttt{dirname}, assegnandole i permessi indicati da \texttt{mode}. Il nome - può essere indicato con il pathname assoluto o relativo. +Usare la semantica BSD ha il vantaggio che il \acr{gid} viene sempre +automaticamente propagato, restando coerente a quello della directory di +partenza, in tutte le sotto-directory. + +La semantica SVr4 offre la possibilità di scegliere, ma per ottenere lo stesso +risultato di coerenza che si ha con BSD necessita che per le nuove directory +venga anche propagato anche il bit \acr{sgid}. Questo è il comportamento +predefinito del comando \cmd{mkdir}, ed è in questo modo ad esempio che Debian +assicura che le sotto-directory create nella home di un utente restino sempre +con il \acr{gid} del gruppo primario dello stesso. + + +\subsection{La funzione \func{access}} +\label{sec:file_access} + +Come visto in \secref{sec:file_access_control} il controllo di accesso ad un +file viene fatto utilizzando l'user-ID ed il group-ID effettivo del processo; ci +sono casi però in cui si può voler effettuare il controllo con l'user-ID reale +ed il group-ID reale, vale a dire usando i valori di \acr{uid} e \acr{gid} +relativi all'utente che ha lanciato il programma, e che, come accennato in +\secref{sec:file_suid_sgid} e spiegato in dettaglio in +\secref{sec:proc_perms}, non è detto siano uguali a quelli effettivi. + +Per far questo si può usare la funzione \funcd{access}, il cui prototipo è: +\begin{prototype}{unistd.h} +{int access(const char *pathname, int mode)} + +Verifica i permessi di accesso. - La funzione restituisce zero in caso di successo e -1 per un errore, in caso - di errore \texttt{errno} viene settata secondo i codici di errore standard - di accesso ai files (trattati in dettaglio in - \secref{sec:filedir_access_control}) ai quali si aggiungono i seguenti: +\bodydesc{La funzione ritorna 0 se l'accesso è consentito, -1 se l'accesso non + è consentito ed in caso di errore; nel qual caso la variabile \var{errno} + assumerà i valori: \begin{errlist} - \item \texttt{EACCESS} - Non c'è il permesso di scrittura per la directory in cui si vuole inserire - la nuova directory. - \item \texttt{EEXIST} Un file (o una directory) con quel nome esiste di già. - \item \texttt{EMLINK} La directory in cui si vuole creare la nuova directory - contiene troppi file. Sotto Linux questo normalmente non avviene perché il - filesystem standard consente la creazione di un numero di file maggiore di - quelli che possono essere contenuti nell'hard-disk, ma potendo avere a che - fare anche con filesystem di altri sistemi questo errore può presentarsi. - \item \texttt{ENOSPC} Non c'è abbastanza spazio sul file system per creare - la nuova directory. - \item \texttt{EROFS} La directory su cui si vuole inserire la nuova - directory è su un filesystem montato readonly. + \item[\errcode{EINVAL}] il valore di \param{mode} non è valido. + \item[\errcode{EACCES}] l'accesso al file non è consentito, o non si ha il + permesso di attraversare una delle directory di \param{pathname}. + \item[\errcode{EROFS}] si è richiesto l'accesso in scrittura per un file su + un filesystem montato in sola lettura. \end{errlist} + ed inoltre \errval{EFAULT}, \errval{ENAMETOOLONG}, \errval{ENOENT}, + \errval{ENOTDIR}, \errval{ELOOP}, \errval{EIO}.} \end{prototype} - - -\subsection{Accesso alle directory} -\label{sec:filedir_dir_read} -Benché le directory siano oggetti del filesystem come tutti gli altri non ha -ovviamente senso aprirle come fossero dei file di dati. Può però essere utile -poterne leggere il contenuto ad esempio per fare la lista dei file che esse -contengono o ricerche sui medesimi. +La funzione verifica i permessi di accesso, indicati da \param{mode}, per il +file indicato da \param{pathname}. I valori possibili per l'argomento +\param{mode} sono esprimibili come combinazione delle costanti numeriche +riportate in \tabref{tab:file_access_mode_val} (attraverso un OR binario delle +stesse). I primi tre valori implicano anche la verifica dell'esistenza del +file, se si vuole verificare solo quest'ultima si può usare \const{F\_OK}, o +anche direttamente \func{stat}. Nel caso in cui \param{pathname} si riferisca +ad un link simbolico, questo viene seguito ed il controllo è fatto sul file a +cui esso fa riferimento. -Per accedere al contenuto delle directory si usano i cosiddetti -\textit{directory streams} (chiamati così per l'analogia con i file stream); -la funzione \texttt{opendir} apre uno di questi stream e la funzione -\texttt{readdir} legge il contenuto della directory, i cui elementi sono le -\textit{directory entries} (da distinguersi da quelle della cache di cui -parlavamo in \secref{sec:fileintr_vfs}) in una opportuna struttura -\texttt{struct dirent}. +La funzione controlla solo i bit dei permessi di accesso, si ricordi che il +fatto che una directory abbia permesso di scrittura non significa che ci si +possa scrivere come in un file, e il fatto che un file abbia permesso di +esecuzione non comporta che contenga un programma eseguibile. La funzione +ritorna zero solo se tutte i permessi controllati sono disponibili, in caso +contrario (o di errore) ritorna -1. +\begin{table}[htb] + \centering + \footnotesize + \begin{tabular}{|c|l|} + \hline + \textbf{\param{mode}} & \textbf{Significato} \\ + \hline + \hline + \const{R\_OK} & verifica il permesso di lettura \\ + \const{W\_OK} & verifica il permesso di scritture \\ + \const{X\_OK} & verifica il permesso di esecuzione \\ + \const{F\_OK} & verifica l'esistenza del file \\ + \hline + \end{tabular} + \caption{Valori possibile per l'argomento \param{mode} della funzione + \func{access}.} + \label{tab:file_access_mode_val} +\end{table} +Un esempio tipico per l'uso di questa funzione è quello di un processo che sta +eseguendo un programma coi privilegi di un altro utente (ad esempio attraverso +l'uso del \acr{suid} bit) che vuole controllare se l'utente originale ha i +permessi per accedere ad un certo file. -\subsection{La directory di lavoro} -\label{sec:filedir_work_dir} -A ciascun processo è associato ad una directory nel filesystem che è chiamata -directory corrente o directory di lavoro (\textit{current working directory}) -che è quella a cui si fa riferimento quando un filename è espresso in forma -relativa (relativa appunto a questa directory). - -Quando un utente effettua il login questa directory viene settata alla -cosiddetta \textit{home directory} del suo account, il comando \texttt{cd} -della shell consente di cambiarla a piacere, spostandosi da una directory ad -un'altra. Siccome la directory corrente resta la stessa quando viene creato -un processo figlio, la directory corrente della shell diventa anche la -directory corrente di qualunque comando da essa lanciato. +\subsection{Le funzioni \func{chmod} e \func{fchmod}} +\label{sec:file_chmod} -Le funzioni qui descritte servono esaminare e cambiare la directory di lavoro -corrente. - -\begin{prototype}{unistd.h}{char * getcwd (char * buffer, size\_t size)} - Restituisce il filename completo della directory di lavoro corrente nella - stringa puntata da \texttt{buffer}, che deve essere precedentemente - allocata, per una dimensione massima di \texttt{size}. Si può anche - specificare un puntatore nullo come \textit{buffer}, nel qual caso la - stringa sarà allocata automaticamente per una dimensione pari a - \texttt{size} qualora questa sia diversa da zero, o della lunghezza esatta - del pathname altrimenti. In questo caso si deve ricordare di disallocare la - stringa una volta cessato il suo utilizzo. - - La funzione restituisce il puntatore \texttt{buffer} se riesce, - \texttt{NULL} se fallisce, in quest'ultimo caso la variabile - \texttt{errno} è settata con i seguenti codici di errore: +Per cambiare i permessi di un file il sistema mette ad disposizione due +funzioni \funcd{chmod} e \funcd{fchmod}, che operano rispettivamente su un +filename e su un file descriptor, i loro prototipi sono: +\begin{functions} + \headdecl{sys/types.h} + \headdecl{sys/stat.h} + + \funcdecl{int chmod(const char *path, mode\_t mode)} Cambia i permessi del + file indicato da \param{path} al valore indicato da \param{mode}. + + \funcdecl{int fchmod(int fd, mode\_t mode)} Analoga alla precedente, ma usa + il file descriptor \param{fd} per indicare il file. + + \bodydesc{Le funzioni restituiscono zero in caso di successo e -1 per + un errore, in caso di errore \var{errno} può assumere i valori: \begin{errlist} - \item \texttt{EINVAL} L'argomento \texttt{size} è zero e \texttt{buffer} non - è nullo. - \item \texttt{ERANGE} L'argomento \texttt{size} è più piccolo della - lunghezza del pathname. - \item \texttt{EACCESS} Manca il permesso di lettura o di ricerca su uno dei - componenti del pathname (cioè su una delle directory superiori alla - corrente). + \item[\errcode{EPERM}] L'user-ID effettivo non corrisponde a quello del + proprietario del file o non è zero. + \item[\errcode{EROFS}] Il file è su un filesystem in sola lettura. \end{errlist} -\end{prototype} + ed inoltre \errval{EIO}; \func{chmod} restituisce anche \errval{EFAULT}, + \errval{ENAMETOOLONG}, \errval{ENOENT}, \errval{ENOMEM}, \errval{ENOTDIR}, + \errval{EACCES}, \errval{ELOOP}; \func{fchmod} anche \errval{EBADF}.} +\end{functions} -Di questa funzione esiste una versione \texttt{char * getwd(char * buffer)} -fatta per compatibilità all'indietro con BSD, che non consente di specificare -la dimensione del buffer; esso deve essere allocato in precedenza ed avere una -dimensione superiore a \texttt{PATH\_MAX} (di solito 256 bytes, vedi -\secref{sec:xxx_limits}; il problema è che in Linux non esiste una dimensione -superiore per un pathname, per cui non è detto che il buffer sia sufficiente a -contenere il nome del file, e questa è la ragione principale per cui questa -funzione è deprecata. +Entrambe le funzioni utilizzano come secondo argomento \param{mode}, una +variabile dell'apposito tipo primitivo \type{mode\_t} (vedi +\tabref{tab:intro_primitive_types}) utilizzato per specificare i permessi sui +file. + +\begin{table}[!htb] + \centering + \footnotesize + \begin{tabular}[c]{|c|c|l|} + \hline + \textbf{\param{mode}} & \textbf{Valore} & \textbf{Significato} \\ + \hline + \hline + \const{S\_ISUID} & 04000 & set user ID \\ + \const{S\_ISGID} & 02000 & set group ID \\ + \const{S\_ISVTX} & 01000 & sticky bit \\ + \hline + \const{S\_IRWXU} & 00700 & l'utente ha tutti i permessi \\ + \const{S\_IRUSR} & 00400 & l'utente ha il permesso di lettura \\ + \const{S\_IWUSR} & 00200 & l'utente ha il permesso di scrittura \\ + \const{S\_IXUSR} & 00100 & l'utente ha il permesso di esecuzione \\ + \hline + \const{S\_IRWXG} & 00070 & il gruppo ha tutti i permessi \\ + \const{S\_IRGRP} & 00040 & il gruppo ha il permesso di lettura \\ + \const{S\_IWGRP} & 00020 & il gruppo ha il permesso di scrittura \\ + \const{S\_IXGRP} & 00010 & il gruppo ha il permesso di esecuzione \\ + \hline + \const{S\_IRWXO} & 00007 & gli altri hanno tutti i permessi \\ + \const{S\_IROTH} & 00004 & gli altri hanno il permesso di lettura \\ + \const{S\_IWOTH} & 00002 & gli altri hanno il permesso di scrittura \\ + \const{S\_IXOTH} & 00001 & gli altri hanno il permesso di esecuzione \\ + \hline + \end{tabular} + \caption{Valori delle costanti usate per indicare i vari bit di + \param{mode} utilizzato per impostare i permessi dei file.} + \label{tab:file_permission_const} +\end{table} + +Le costanti con cui specificare i singoli bit di \param{mode} sono riportate +in \tabref{tab:file_permission_const}. Il valore di \param{mode} può essere +ottenuto combinando fra loro con un OR binario le costanti simboliche relative +ai vari bit, o specificato direttamente, come per l'omonimo comando di shell, +con un valore numerico (la shell lo vuole in ottale, dato che i bit dei +permessi sono divisibili in gruppi di tre), che si può calcolare direttamente +usando lo schema si utilizzo dei bit illustrato in \figref{fig:file_perm_bit}. + +Ad esempio i permessi standard assegnati ai nuovi file (lettura e scrittura +per il proprietario, sola lettura per il gruppo e gli altri) sono +corrispondenti al valore ottale $0644$, un programma invece avrebbe anche il +bit di esecuzione attivo, con un valore di $0755$, se si volesse attivare il +bit \acr{suid} il valore da fornire sarebbe $4755$. + +Il cambiamento dei permessi di un file eseguito attraverso queste funzioni ha +comunque alcune limitazioni, previste per motivi di sicurezza. L'uso delle +funzioni infatti è possibile solo se l'user-ID effettivo del processo +corrisponde a quello del proprietario del file o dell'amministratore, +altrimenti esse falliranno con un errore di \errcode{EPERM}. + +Ma oltre a questa regola generale, di immediata comprensione, esistono delle +limitazioni ulteriori. Per questo motivo, anche se si è proprietari del file, +non tutti i valori possibili di \param{mode} sono permessi o hanno effetto; +in particolare accade che: +\begin{enumerate} +\item siccome solo l'amministratore può impostare lo \textit{sticky bit}, se + l'user-ID effettivo del processo non è zero esso viene automaticamente + cancellato (senza notifica di errore) qualora sia stato indicato in + \param{mode}. +\item per quanto detto in \secref{sec:file_ownership} riguardo la creazione + dei nuovi file, si può avere il caso in cui il file creato da un processo è + assegnato a un gruppo per il quale il processo non ha privilegi. Per evitare + che si possa assegnare il bit \acr{sgid} ad un file appartenente a un gruppo + per cui non si hanno diritti, questo viene automaticamente cancellato da + \param{mode} (senza notifica di errore) qualora il gruppo del file non + corrisponda a quelli associati al processo (la cosa non avviene quando + l'user-ID effettivo del processo è zero). +\end{enumerate} + +Per alcuni filesystem\footnote{il filesystem \acr{ext2} supporta questa + caratteristica, che è mutuata da BSD.} è inoltre prevista una ulteriore +misura di sicurezza, volta a scongiurare l'abuso dei bit \acr{suid} e +\acr{sgid}; essa consiste nel cancellare automaticamente questi bit dai +permessi di un file qualora un processo che non appartenga all'amministratore +effettui una scrittura. In questo modo anche se un utente malizioso scopre un +file \acr{suid} su cui può scrivere, un'eventuale modifica comporterà la +perdita di questo privilegio. -Una seconda funzione simile è \texttt{char * get\_current\_dir\_name(void)} -che è sostanzialmente equivalente ad una \texttt{getcwd(NULL, 0)}, con la sola -differenza che essa ritorna il valore della variabile di ambiente -\texttt{PWD}, che essendo costruita dalla shell può contenere anche dei -riferimenti simbolici. - -Come già detto in unix anche le directory sono file, è possibile pertanto -riferirsi ad esse tramite il file descriptor dell'interfaccia a basso livello, -e non solo tramite il filename; per questo motivo ci sono due diverse funzioni -per cambiare directory di lavoro. - -\begin{prototype}{unistd.h}{int chdir (const char * pathname)} - Come dice il nome (che significa \textit{change directory}) questa funzione - serve a cambiare la directory di lavoro a quella specificata dal pathname - contenuto nella stringa \texttt{pathname}. +\subsection{La funzione \func{umask}} +\label{sec:file_umask} + +Oltre che dai valori indicati in sede di creazione, i permessi assegnati ai +nuovi file sono controllati anche da una maschera di bit impostata con la +funzione \funcd{umask}, il cui prototipo è: +\begin{prototype}{stat.h} +{mode\_t umask(mode\_t mask)} + +Imposta la maschera dei permessi dei bit al valore specificato da \param{mask} +(di cui vengono presi solo i 9 bit meno significativi). + + \bodydesc{La funzione ritorna il precedente valore della maschera. È una + delle poche funzioni che non restituisce codici di errore.} \end{prototype} + +Questa maschera è una caratteristica di ogni processo\footnote{è infatti + contenuta nel campo \param{umask} di \struct{fs\_struct}, vedi + \figref{fig:proc_task_struct}.} e viene utilizzata per impedire che alcuni +permessi possano essere assegnati ai nuovi file in sede di creazione. I bit +indicati nella maschera vengono infatti esclusi quando un nuovo file viene +creato. + +In genere questa maschera serve per impostare un valore predefinito dei +permessi che ne escluda alcuni (usualmente quello di scrittura per il gruppo e +gli altri, corrispondente ad un valore di $022$). Essa è utile perché le +routine dell'interfaccia ANSI C degli stream non prevedono l'esistenza dei +permessi, e pertanto tutti i nuovi file vengono sempre creati con un valore di +$666$ (cioè permessi di lettura e scrittura per tutti, si veda +\tabref{tab:file_permission_const} per un confronto); in questo modo è +possibile cancellare automaticamente i permessi non voluti, senza doverlo fare +esplicitamente. + +In genere il valore di \func{umask} viene stabilito una volta per tutte al +login a $022$, e di norma gli utenti non hanno motivi per modificarlo. Se però +si vuole che un processo possa creare un file che chiunque possa leggere +allora occorrerà cambiare il valore di \func{umask}. + + +\subsection{Le funzioni \func{chown}, \func{fchown} e \func{lchown}} +\label{sec:file_chown} + +Come per i permessi, il sistema fornisce anche delle funzioni che permettano +di cambiare utente e gruppo cui il file appartiene; le funzioni in questione +sono tre: \funcd{chown}, \funcd{fchown} e \funcd{lchown}, ed i loro prototipi +sono: +\begin{functions} + \headdecl{sys/types.h} + \headdecl{sys/stat.h} -\begin{prototype}{unistd.h}{int fchdir (int filedes)} - Analoga alla precedente, ma usa un file descriptor invece del pathname. + \funcdecl{int chown(const char *path, uid\_t owner, gid\_t group)} + \funcdecl{int fchown(int fd, uid\_t owner, gid\_t group)} + \funcdecl{int lchown(const char *path, uid\_t owner, gid\_t group)} + + Le funzioni cambiano utente e gruppo di appartenenza di un file ai valori + specificati dalle variabili \param{owner} e \param{group}. - Entrambe le funzioni restituiscono zero in caso di successo e -1 per un - errore, in caso di errore \texttt{errno} viene settata secondo i codici di - errore standard di accesso ai files (trattati in dettaglio in - \secref{sec:filedir_access_control}) ai quali si aggiunge il codice - \texttt{ENOTDIR} nel caso il \texttt{filename} indichi un file che non sia - una directory. -\end{prototype} + \bodydesc{Le funzioni restituiscono zero in caso di successo e -1 per + un errore, in caso di errore \var{errno} può assumere i valori: + \begin{errlist} + \item[\errcode{EPERM}] L'user-ID effettivo non corrisponde a quello del + proprietario del file o non è zero, o utente e gruppo non sono validi + \end{errlist} + Oltre a questi entrambe restituiscono gli errori \errval{EROFS} e + \errval{EIO}; \func{chown} restituisce anche \errval{EFAULT}, + \errval{ENAMETOOLONG}, \errval{ENOENT}, \errval{ENOMEM}, \errval{ENOTDIR}, + \errval{EACCES}, \errval{ELOOP}; \func{fchown} anche \errval{EBADF}.} +\end{functions} +In Linux soltanto l'amministratore può cambiare il proprietario di un file, +seguendo la semantica di BSD che non consente agli utenti di assegnare i loro +file ad altri (per evitare eventuali aggiramenti delle quote). +L'amministratore può cambiare il gruppo di un file, il proprietario può +cambiare il gruppo dei file che gli appartengono solo se il nuovo gruppo è il +suo gruppo primario o uno dei gruppi a cui appartiene. +La funzione \func{chown} segue i link simbolici, per operare direttamente su +un link simbolico si deve usare la funzione \func{lchown}.\footnote{fino alla + versione 2.1.81 in Linux \func{chown} non seguiva i link simbolici, da + allora questo comportamento è stato assegnato alla funzione \func{lchown}, + introdotta per l'occasione, ed è stata creata una nuova system call per + \func{chown} che seguisse i link simbolici.} La funzione \func{fchown} opera +su un file aperto, essa è mutuata da BSD, ma non è nello standard POSIX. +Un'altra estensione rispetto allo standard POSIX è che specificando -1 come +valore per \param{owner} e \param{group} i valori restano immutati. +Quando queste funzioni sono chiamate con successo da un processo senza i +privilegi di root entrambi i bit \acr{suid} e \acr{sgid} vengono +cancellati. Questo non avviene per il bit \acr{sgid} nel caso in cui esso +sia usato (in assenza del corrispondente permesso di esecuzione) per indicare +che per il file è attivo il \textit{mandatory locking}. %La struttura fondamentale che contiene i dati essenziali relativi ai file è il %cosiddetto \textit{inode}; questo conterrà informazioni come il @@ -319,3 +2479,165 @@ per cambiare directory di lavoro. %completo vedi \ntab), i permessi (vedi \secref{sec:file_perms}), le date (vedi %\secref{sec:file_times}). + +\subsection{Un quadro d'insieme sui permessi} +\label{sec:file_riepilogo} + +Avendo affrontato in maniera separata il comportamento delle varie funzioni ed +il significato dei singoli bit dei permessi sui file, vale la pena fare un +riepilogo in cui si riassumono le caratteristiche di ciascuno di essi, in modo +da poter fornire un quadro d'insieme. + +In \tabref{tab:file_fileperm_bits} si sono riassunti gli effetti dei vari bit +per un file; per quanto riguarda l'applicazione dei permessi per proprietario, +gruppo ed altri si ricordi quanto illustrato in +\secref{sec:file_perm_overview}. Si rammenti che il valore dei permessi non ha +alcun effetto qualora il processo possieda i privilegi di amministratore. + +\begin{table}[!htb] + \centering + \footnotesize + \begin{tabular}[c]{|c|c|c|c|c|c|c|c|c|c|c|c|l|} + \hline + \multicolumn{3}{|c|}{}& + \multicolumn{3}{|c|}{user}& + \multicolumn{3}{|c|}{group}& + \multicolumn{3}{|c|}{other}& + \multirow{2}{*}{\textbf{Operazioni possibili}} \\ + \cline{1-12} + \acr{s}&\acr{s}&\acr{t}&r&w&x&r&w&x&r&w&x& \\ + \hline + \hline + 1&-&-&-&-&-&-&-&-&-&-&-&Se eseguito ha i permessi del proprietario\\ + -&1&-&-&-&1&-&-&-&-&-&-&Se eseguito ha i permessi del gruppo proprietario\\ + -&1&-&-&-&0&-&-&-&-&-&-&Il \textit{mandatory locking} è abilitato\\ + -&-&1&-&-&-&-&-&-&-&-&-&Non utilizzato\\ + -&-&-&1&-&-&-&-&-&-&-&-&Permesso di lettura per il proprietario\\ + -&-&-&-&1&-&-&-&-&-&-&-&Permesso di lettura per il gruppo proprietario\\ + -&-&-&-&-&1&-&-&-&-&-&-&Permesso di lettura per tutti gli altri\\ + -&-&-&-&-&-&1&-&-&-&-&-&Permesso di scrittura per il proprietario\\ + -&-&-&-&-&-&-&1&-&-&-&-&Permesso di scrittura per il gruppo proprietario\\ + -&-&-&-&-&-&-&-&1&-&-&-&Permesso di scrittura per tutti gli altri \\ + -&-&-&-&-&-&-&-&-&1&-&-&Permesso di esecuzione per il proprietario\\ + -&-&-&-&-&-&-&-&-&-&1&-&Permesso di esecuzione per il gruppo proprietario\\ + -&-&-&-&-&-&-&-&-&-&-&1&Permesso di esecuzione per tutti gli altri\\ + \hline + \end{tabular} + \caption{Tabella riassuntiva del significato dei bit dei permessi per un + file.} + \label{tab:file_fileperm_bits} +\end{table} + +Per compattezza, nella tabella si sono specificati i bit di \acr{suid}, +\acr{sgid} e \acr{sticky} con la notazione illustrata anche in +\figref{fig:file_perm_bit}. + +In \tabref{tab:file_dirperm_bits} si sono invece riassunti gli effetti dei +vari bit dei permessi per una directory; anche in questo caso si sono +specificati i bit di \acr{suid}, \acr{sgid} e \acr{stiky} con la notazione +compatta illustrata in \figref{fig:file_perm_bit}. + +\begin{table}[!htb] + \centering + \footnotesize + \begin{tabular}[c]{|c|c|c|c|c|c|c|c|c|c|c|c|l|} + \hline + \multicolumn{3}{|c|}{}& + \multicolumn{3}{|c|}{user}& + \multicolumn{3}{|c|}{group}& + \multicolumn{3}{|c|}{other}& + \multirow{2}{*}{\textbf{Operazioni possibili}} \\ + \cline{1-12} + \acr{s}&\acr{s}&\acr{t}&r&w&x&r&w&x&r&w&x& \\ + \hline + \hline + 1&-&-&-&-&-&-&-&-&-&-&-&Non utilizzato\\ + -&1&-&-&-&-&-&-&-&-&-&-&Propaga il gruppo proprietario ai nuovi file creati\\ + -&-&1&-&-&-&-&-&-&-&-&-&Limita l'accesso in scrittura dei file nella directory\\ + -&-&-&1&-&-&-&-&-&-&-&-&Permesso di visualizzazione per il proprietario\\ + -&-&-&-&1&-&-&-&-&-&-&-&Permesso di visualizzazione per il gruppo proprietario\\ + -&-&-&-&-&1&-&-&-&-&-&-&Permesso di visualizzazione per tutti gli altri\\ + -&-&-&-&-&-&1&-&-&-&-&-&Permesso di aggiornamento per il proprietario\\ + -&-&-&-&-&-&-&1&-&-&-&-&Permesso di aggiornamento per il gruppo proprietario\\ + -&-&-&-&-&-&-&-&1&-&-&-&Permesso di aggiornamento per tutti gli altri \\ + -&-&-&-&-&-&-&-&-&1&-&-&Permesso di attraversamento per il proprietario\\ + -&-&-&-&-&-&-&-&-&-&1&-&Permesso di attraversamento per il gruppo proprietario\\ + -&-&-&-&-&-&-&-&-&-&-&1&Permesso di attraversamento per tutti gli altri\\ + \hline + \end{tabular} + \caption{Tabella riassuntiva del significato dei bit dei permessi per una + directory.} + \label{tab:file_dirperm_bits} +\end{table} + +Nelle tabelle si è indicato con $-$ il fatto che il valore degli altri bit non +è influente rispetto a quanto indicato in ciascuna riga; l'operazione fa +riferimento soltanto alla combinazione di bit per i quali il valore è +riportato esplicitamente. + + +\subsection{La funzione \func{chroot}} +\label{sec:file_chroot} + +Benché non abbia niente a che fare con permessi, utenti e gruppi, la funzione +\func{chroot} viene usata spesso per restringere le capacità di accesso di un +programma ad una sezione limitata del filesystem, per cui ne parleremo in +questa sezione. + +Come accennato in \secref{sec:proc_fork} ogni processo oltre ad una directory +di lavoro corrente, ha anche una directory radice,\footnote{entrambe sono + contenute in due campi di \struct{fs\_struct}, vedi + \figref{fig:proc_task_struct}.} che è la directory che per il processo +costituisce la radice dell'albero dei file e rispetto alla quale vengono +risolti i pathname assoluti (si ricordi quanto detto in +\secref{sec:file_organization}). La radice viene ereditata dal padre per ogni +processo figlio, e quindi di norma coincide con la \file{/} del sistema. + +In certe situazioni però per motivi di sicurezza non si vuole che un processo +possa accedere a tutto il filesystem; per questo si può cambiare la directory +radice con la funzione \funcd{chroot}, il cui prototipo è: +\begin{prototype}{unistd.h}{int chroot(const char *path)} + Cambia la directory radice del processo a quella specificata da + \param{path}. + +\bodydesc{La funzione restituisce zero in caso di successo e -1 per + un errore, in caso di errore \var{errno} può assumere i valori: + \begin{errlist} + \item[\errcode{EPERM}] L'user-ID effettivo del processo non è zero. + \end{errlist} + ed inoltre \errval{EFAULT}, \errval{ENAMETOOLONG}, \errval{ENOENT}, + \errval{ENOMEM}, \errval{ENOTDIR}, \errval{EACCES}, \errval{ELOOP}; + \errval{EROFS} e \errval{EIO}.} +\end{prototype} +\noindent in questo modo la directory radice del processo diventerà +\param{path} (che ovviamente deve esistere) ed ogni pathname assoluto sarà +risolto a partire da essa, rendendo impossibile accedere alla parte di albero +sovrastante; si ha cioè quella che viene chiamata una \textit{chroot jail}. + +Solo l'amministratore può usare questa funzione, e la nuova radice, per quanto +detto in \secref{sec:proc_fork}, sarà ereditata da tutti i processi figli. Si +tenga presente che la funzione non cambia la directory di lavoro corrente, che +potrebbe restare fuori dalla \textit{chroot jail}. + +Questo è il motivo per cui la funzione è efficace solo se dopo averla eseguita +si cedono i privilegi di root. Infatti se in qualche modo il processo ha una +directory di lavoro corrente fuori dalla \textit{chroot jail}, potrà comunque +accedere a tutto il filesystem usando pathname relativi. + +Ma quando ad un processo restano i privilegi di root esso potrà sempre portare +la directory di lavoro corrente fuori dalla \textit{chroot jail} creando una +sotto-directory ed eseguendo una \func{chroot} su di essa. Per questo motivo +l'uso di questa funzione non ha molto senso quando un processo necessita dei +privilegi di root per le sue normali operazioni. + +Un caso tipico di uso di \func{chroot} è quello di un server FTP anonimo, in +questo caso infatti si vuole che il server veda solo i file che deve +trasferire, per cui in genere si esegue una \func{chroot} sulla directory che +contiene i file. Si tenga presente però che in questo caso occorrerà +replicare all'interno della \textit{chroot jail} tutti i file (in genere +programmi e librerie) di cui il server potrebbe avere bisogno. + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: "gapil" +%%% End: