Fichiers et Répertoires

Guillaume Chanel

Remerciements à Jean-Luc Falcone

Cours système d'exploitation by Guillaume Chanel, Jean-Luc Falcone and University of Geneva is licensed under CC BY-NC-SA 4.0

Inodes

Schéma global

Schema des inodes

Inode

  • Les inodes sont des structures de données contenant des informations sur un "fichier" (fichier, directory, socket, device, pipe, etc.).
  • Ils ne contiennent pas le nom du fichier.
  • Ils contiennent généralement (POSIX) des informations sur:
    • Numéro d'inode
    • Périphérique contenant le fichier (device ID)
    • Propriétaire et groupe
    • Permissions
    • Taille du fichier
    • Temps d'accès et de modification
    • Nombre de liens pointant vers l'inode
    • Pointeurs vers les données

Temps de l'inode

Un inode contient trois temps différents:

atimedate du dernier accès à l'inode (ou aux données)
mtimedate de la dernière modification des données
ctimedate de la dernière modifications des méta-données
Information

Pour gagner en performance, il est possible de désactiver la mise à jour de atime lorsque la partition est montée.

Inspecter un inode en shell (stat)

La commande stat permet d'afficher des données sur un inode.

$ touch /tmp/myfile  # met à jour les dates (crée un fichier si inexistant)
$ stat /tmp/myfile
  File: /tmp/myfile
  Size: 0               Blocks: 0          IO Block: 4096   regular empty file
Device: 2fh/47d Inode: 422766      Links: 1
Access: (0644/-rw-r--r--)  Uid: ( 1000/  chanel)   Gid: ( 1000/  chanel)
Access: 2019-07-19 16:56:35.540113838 +0200
Modify: 2019-07-19 16:56:35.540113838 +0200
Change: 2019-07-19 16:56:35.540113838 +0200
 Birth: -

Lien dur (2)

  • Les entrées des répertoires sont des liens pointant vers des inodes.
  • On peut créer plusiers liens vers un fichier
  • Un fichier est "effacé" lorsqu'il n'y a plus de liens pointant sur son inode (cf unlink).

On peut créer un lien dur avec la commande ln:

$ ln /tmp/myfile /tmp/newlink
$ stat /tmp/newlink
  File: /tmp/myfile
  Size: 0               Blocks: 0          IO Block: 4096   regular empty file
Device: 2fh/47d Inode: 422766      Links: 2
Access: (0644/-rw-r--r--)  Uid: ( 1000/  chanel)   Gid: ( 1000/  chanel)
Access: 2019-07-19 16:56:35.540113838 +0200
Modify: 2019-07-19 16:56:35.540113838 +0200
Change: 2019-07-19 16:56:35.540113838 +0200
 Birth: -

Lien dur (3)

Limitations

Un répertoire ne peux posséder qu'un seul lien dur.

Tous les liens durs pointant sur un inode doivent se trouver sur le même système de fichier que cet inode.

Utilité

En général, assez peu utiles (préférer les liens symboliques, voir slides suivantes).

Permet de faire un snapshot, par exemple pour archiver un répertoire: http://www.mikerubel.org/computers/rsync_snapshots/ .

Lien symbolique (2)

  • Un lien symbolique possède son propre inode qui pointe vers un nom
  • Contrairement aux liens durs, on peut créer des symlinks:
    • Vers un répertoire
    • Vers un fichier/répertoire sur un autre système de fichier.

On peut créer un lien dur avec la commande ln en utilisant l'option -s:

$ ln -s /tmp/myfile /tmp/newlink  # (le lien dure précédent à été supprimé)
$ stat /tmp/newlink  # comme indiqué ci-dessous on a bien à faire à un autre inode
  File: /tmp/newlink -> /tmp/myfile
    Size: 11              Blocks: 0          IO Block: 4096   symbolic link
  Device: 2fh/47d Inode: 563876      Links: 1
  Access: (0777/lrwxrwxrwx)  Uid: ( 1000/  chanel)   Gid: ( 1000/  chanel)
  Access: 2019-07-19 18:16:26.343945684 +0200
  Modify: 2019-07-19 18:16:13.240259297 +0200
  Change: 2019-07-19 18:16:13.240259297 +0200
   Birth: -

Objets trouvés (Lost+Found)

  • A la suite d'une erreur du système de fichier (p.e. suite à une mise hors-tension brutale), un inode peut se retrouver sans lien.
  • Lors d'un contrôle de fichier (fsck), il sera copié dans le répertoire lost+found à la racine du système de fichier.

Structure stat (sys/stat.h)

struct stat{
  dev_t     st_dev;     //device ID
  ino_t     st_ino;     //i-node number
  mode_t    st_mode;    //protection and type
  nlink_t   st_nlink;   //number of hard links
  uid_t     st_uid;     //user ID of owner
  gid_t     st_gid;     //group ID of owner
  dev_t     st_rdev;    //device type (if special file)
  off_t     st_size;    //total size, in bytes
  blksize_t st_blksize; //blocksize for filesystem I/O
  blkcnt_t  st_blocks;  //number of 512B blocks
  time_t    st_atime;   //time of last access
  time_t    st_mtime;   //time of last modification
  time_t    st_ctime;   //time of last change
};

Appel système stat

L'appel système stat() permet de garnir une structure stat:

int stat(const char *path, struct stat *buf);

La fonction retourne 0 si tout s'est bien passé ou -1 en cas d'erreur (cf. errno)

struct stat infos;
char *filename = "/tmp/foo.txt";
if( stat( filename, &infos ) < 0 )
    fprintf( stderr, "Cannot stat %s: %s\n", filename, strerror(errno) );
else
    printf( "Filesize: %d\n", infos.st_size );

Déterminer le type d'un inode

  • Le champ st_mode est un champs de bits contenant les permissions et le type d'un inode.
  • Il existe plusieurs macro POSIX permettant de tester les types:
S_ISREG(m)fichier de données ?
S_ISDIR(m)répertoire ?
S_ISCHR(m)character device ?
S_ISBLK(m)block device ?
S_ISFIFO(m)FIFO (named pipe) ?
S_ISLNK(m)lien symbolique ?
S_ISSOCK(m)socket?
if( S_ISDIR( info.st_mode ) ) {
    printf( "L'inode est un repertoire.\n" );
}

Déterminer les permissions d'un inode

On peut utiliser plusieurs flags pour accéder aux valeurs du champs de bits:

S_IRUSR00400owner has read permission
S_IWUSR00200owner has write permission
S_IXUSR00100owner has execute permission
S_IRGRP00040group has read permission
S_IWGRP00020group has write permission
S_IXGRP00010group has execute permission
S_IROTH00004others have read permission
S_IWOTH00002others have write permission
S_IXOTH00001others have execute permission

Appel système lstat

  • Si le A est un lien symbolique vers B, stat("A",...) retourne les informations sur l'inode de B.
  • On peut éviter ce comportement et obtenir les informations sur le lien symbolique lui-même grâce à lstat():
int lstat(const char *path, struct stat *buf);
  • Le reste du comportement est identique à stat().

Appel système fstat

  • Parfois on veut connaîtres les informations sur un fichiers déjà ouvert (cf suite du cours).
  • L'appel fstat() fonctionne comme stat() mais permet d'utiliser un descripteur de fichier à la place d'un nom:
int fstat(int fd, struct stat *buf);

Appel système access

  • On peut tester si le processus en cours à le droit de lire/écrire/exécuter un fichier grâce à l'appel système access():
int access(const char *pathname, int mode);
  • Le paramètre mode est un champs de bits formés des flags:
    R_OKlecture possible
    W_OKécriture possible
    X_OKéxécution possible
  • On peut aussi tester le flag F_OK (seulement) qui indique si le fichier existe.
  • Le test se fait en fonction de l'utilisateur/groupe vrai.
  • access() retourne 0 si le test réussit, -1 sinon (cf errno)

Appel système access (2)

char *fn = "/tmp/foo.txt";
if ( access( fn, R_OK|W_OK ) == 0 )
    printf( "On peut lire et ecrire sur %s\n", fn );
else if ( errno == EACCES )
    printf("Pas le droit de lire et/ou d'ecrire sur %s\n", fn);
else
    perror( fn );

Appel système chmod

On peut changer les permissions d'un fichier grâce à l'appel système chmod, similaire à la commande shell du même nom:

int chmod(const char *path, mode_t mode);

//Utilise un descripteur de fichier ouvert
int fchmod(int fd, mode_t mode);

Le paramètre mode est un champs de bits formés des mêmes flags que le champs st_mode de la structure stat.

Répertoires / Directories

Les répertoires (directories)

  • représentent l'organisation des fichiers sous forme d'arborescence
  • sont des inodes dont le contenu est une liste d'entrées dirent associants un lien à un inode
  • chaque liste d'entrée contient au moins . et ..

Inodes de répertoires

Schema des inodes de repertoire

Structure dirent

Les entrées d'un répertoire sont représentées par la structure:


struct dirent {                  /* dirent.h */
    ino_t   d_ino;               /* inode number */
    off_t   d_off;               /* opaque value used to get next dirent (do not use) */
    unsigned short  d_reclen;    /* length of this record */
    unsinged char   d_type;      /* type of file; not supported by all file systems */
    char            d_name[256]; /* filename (NULL terminated), sometimes d_name[0] */
};
                    

Seulement deux champs sont décrit par POSIX: d_ino et d_name.

Ne jamais compter sur la taille du tableau d_name, uniquement sur la constante MAX_NAME qui indique la longueur maximale des noms d'entrées ou sur strlen

Structure dir_ent (3)

Le champs d_type est un champs de bits contenant des informations sur le type de l'inode associé:

DT_DIRRépertoire
DT_LNKLien symbolique
DT_REGFichier de données
DT_UNKNOWNType inconnu
DT_...Voir man readdir pour tous les types.
Attention
  • Même sous GNU/Linux, tous les systèmes de fichiers ne donnent pas un accès au type par la structure dir_ent
  • Dans ce cas, le d_type est toujours égal à DT_UNKNOWN.

Flot de répertoires

Pour accéder aux entrées d'un répertoire, il faut:

  1. "Ouvrir" le répertoire avec opendir()
  2. "Lire" l'entrée suivante avec readdir()
  3. Répéter 2, jusqu'à épuisement des entrées ou tout autre critère
  4. "Fermer" le répertoire avec closedir()

Ouvrir un répertoire (opendir)

  • On peut ouvrir un répertoire grâce aux fonctions:
  • 
    DIR *opendir(const char *name);
    DIR *fdopendir(int fd);
                            
  • DIR est un type opaque
  • En cas d'erreur DIR sera NULL
  • Exemples de codes d'erreurs (voir man):
  • EACCESSopération interdite (permissions)
    ENOENTLe répertoire n'existe pas ou le nom est une chaîne vide.
    ENOTDIRLe nom existe mais n'est pas un répertoire.

Lire l'entrée suivante (readdir)

  • On peut lire l'entrée suivante d'un répertoire ouvert avec:
  • struct dirent *readdir(DIR *dirp);
  • Retourne soit:
    • un pointeur sur une instance de la structure dirent
    • NULL s'il n'y a plus d'entrée ou en cas d'erreur.
  • A chaque appel, une nouvelle entrée est retournée (s'il y en a encore).
  • Un seul code d'erreur:
    EBADFLe descripteur dirp n'est pas valide.

Lire l'entrée suivante (readdir)

Attention
  • La structure retournée est susceptible d'être modifiée par chaque appel.
  • Ne jamais appeler free sur le pointeur retourné.
  • readdir n'est pas thread-safe.

Fermer un répertoire (closedir)

  • On peut fermer un répertoire ouvert avec:
  • int closedir(DIR *dirp);
  • Retourne 0 en cas de succès et -1 en cas d'erreur.
  • Un seul code d'erreur:
    EBADFLe descripteur dirp n'est pas valide.

Exemple (examples/listDir.c)

Créer un répertoire (mkdir)

  • On peut créer un répertoire avec:
  • int mkdir(const char *pathname, mode_t mode);
  • pathname est le nom du répertoire
  • mode spécifie les permissions à utiliser, il est modifié par le umask du processus:
  • mode & ~umask & 0777
  • Retourne 0 en cas de succès et -1 en cas d'erreur
  • voir man 2 mkdir pour les codes d'erreurs

Effacer un répertoire (rmdir)

  • On efface un répertoire vide avec:
  • int rmdir(const char *pathname);
  • Retourne 0 en cas de succès et -1 en cas d'erreur
  • voir man 2 rmdir pour les codes d'erreurs