Cours ‐ Manipulation sur les fichiers - vbridonneau/CoursSysteme GitHub Wiki
Un descripteur de fichier est ce qui permet d'identifier un fichier. Il s'agit d'un entier que l'on associe à un fichier afin de pouvoir le manipuler. Il en existe 3 spéciaux associées aux entrées/sorties standards:
Descripteur | Valeur | Description |
---|---|---|
STDIN_FILENO |
0 | Entrée standard |
STDOUT_FILENO |
1 | Sortie standard |
STDERR_FILENO |
2 | Erreur standard |
Ces valeurs sont définies dans le header unistd.h
.
Dans ce qui suit, nous allons voir différentes opérations permettants de modifier ou de s'informer à propos d'un fichier.
Pour ouvrir un fichier, on utilise l'appel système open
.
Un exmple d'utilisation est le suivant:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main() {
/* Crée le fichier */
int filedesc = open("./fichier.txt", O_CREAT | O_RDWR | O_APPEND);
/* Vérifie s'il y a une erreur */
if (filedesc < 0) {
perror("open");
exit(EXIT_FAILURE);
}
return EXIT_SUCCESS;
}
Lorsque l'on crée un fichier avec le code ci-dessus, il est possible que le fichier existe déjà.
Dans ce cas, le flag O_APPEND
signifie que chaque ajout dans le fichier se fera à la fin de ce dernier.
De plus le curseur pointant sur l'endroit où l'on dans le fichier est positionné à la fin de ce dernier à chaque fois qu'une écriture y est faite.
Il existe d'autres flags intéressants que l'on peut combiner avec l'opérateur |
pour obtenir différents comportements :
Flag | Description |
---|---|
O_RDONLY |
Donne le droit en lectere seule |
O_WRONLY |
Donne le droit en écriture seule |
O_RDWR |
Donne le droit en lecture et écriture |
O_APPEND |
Ajoute à la fin du fichier (à chaque write ) |
O_TRUNC |
Réinitialise le contenue du fichier |
O_CREAT |
Crée le fichier s'il n'existe pas |
O_PATH |
N'ouvre pas le fichier (utile si l'on souhaite avoir des informations dessus sans le modifier) |
Plusieurs choses sont à notées:
- Soit
O_RDONLY
,O_WRONLY
ouO_RDWR
doit être renseigné. -
O_CREAT
est nécessaire pour que le fichier soit créé. - Utiliser le flag
O_PATH
ne permettra pas de modifier le fichier. Ce flag permet la récupération d'information sur un fichier par exemple.
Pour fermer un fichier ouvert avec open
, il est nécessaire de le refermer avec l'appel système close
comme suit :
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main() {
/* Crée le fichier */
int filedesc = open(...);
/* Manipulation du fichier */
/* Ferme le fichier */
if (close(filedesc) < 0) {
perror("close");
exit(EXIT_FAILURE);
}
return EXIT_SUCCESS;
}
De la même manière qu'il est possible d'écrire et de lire dans un fichier, il est également possible d'obtenir des informations relatives à ce dernier (sa taille, la date de sa dernière modification, ...).
Pour ce faire on dispose d'un appel système, fstat
, et d'une structure de donnée struct stat
dont le but est, à partir d'un descripteur de fichier, de pouvoir récupérer ses informations.
Si l'on dispose du nom du fichier, il est également possible d'utiliser l'appel système stat
au lieu de fstat
.
Leur définition est la suivante :
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int stat(const char *pathname, struct stat *statbuf);
int fstat(int fd, struct stat *statbuf);
Voici un exemple d'utilisation de la fonction stat
permettant de récupérer la taille et la date de modification d'un fichier :
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h> /* Contient la définition de EXIT_SUCCESS */
int main() {
/* Nom du fichier */
const char *fichier = "./fichier.txt";
/* Obtient les informations du fichier */
struct stat filestat;
if (stat(fichier, &filestat) < 0) {
perror("stat");
exit(EXIT_FAILURE);
}
/* Affiche la taille et la date de dernière modification du fichier */
printf("Taille du fichier : %ld octets\n", filestat.st_size);
printf("Date de dernière modification : %s", ctime(&filestat.st_mtime));
return EXIT_SUCCESS;
}
Notes : il est possible d'obtenir la date de création d'un fichier via un appel à la fonction statx
. Le moyen d'y parvenir est donné dans le manuel d'utilisation de cet appel système.
Pour créer un répertoire, il suffit d'utiliser l'appel système mkdir
:
#include <sys/stat.h>
#include <sys/types.h>
int mkdir(const char *pathname, mode_t mode);
Pour la création d'un répertoire, il faut renseigner les permissions que l'on souhaite attribuer au répertoire à créer.
Par exemple, si l'on veut créer un répertoire avec les droits d'accès en lecture, écriture et exécution à tout le monde, on donnera en argument la valeur 0777
(il y a une petite spécificité, mais on ne rentrera pas dans le détail ici).
Un exemple d'utilisation :
#include <sys/stat.h>
#include <sys/types.h>
#include <stdlib.h>
#include <stdio.h>
int main() {
/* Créé le répertoire */
int res = mkdir("./rep", 0777);
/* Vérifie qu'il n'y a pas d'erreur */
if (res < 0) {
perror("mkdir");
exit(EXIT_FAILURE);
}
return EXIT_SUCCESS;
}
Une erreur possible pour cet appel système serait que le répertoire à créer existe déjà.
Dans ce cas, errno
aura la valeur EEXIST
.
Lorsque l'on manipule des fichiers, il est important de se rensigner sur les permissions que l'on a sur eux.
Les droits que l'on peut avoir sur un fichier son au nombre de trois : droit en lecture, droit en écriture et droit en exécution.
On peut obtenir l'information concernant les droits d'accès sur un fichier avec la commande ls -l
:
» ls -l
drwxrwxrwx ExosTP
-rw-rw-r-- Ressources.md
-rw-rw-r-- Sujet.md
-rw-rw-r-- exemple.c
-rwxrwxrwx exemple
On peut voir que cette commande nous a renvoyé, pour chaque fichier du répertoire courrant, les droits en lecture (r
), les droits en écritures (w
) et les droits en exécution (x
).
Lorsque l'on parle de droits d'accès à un fichier, il est important de savoir de qui l'on parle. Si l'on reprend l'exemple donné précédemment on peut voir qu'il y a trois fois l'information pour chaque droit d'accès. Ces droits correspondent à des groupes d'utilisateurs différents. Ces groupes sont :
Groupe | Description |
---|---|
Utilisateur | Il correspond à l'utilisateur qui possède le fichier |
Groupe | Il correspond au groupe qui possède le fichier |
Autre | Il correspond aux autres utilisateurs |
Il est parfois pratique de pouvoir changer les droits d'accès des fichiers de manière numérique. Si l'on reprend les droits d'accès (lecture, écriture et exécution), chacun d'eux est associé à un nombre :
Droit | Code |
---|---|
lecture | 4 |
écriture | 2 |
exécution | 1 |
Lorsque l'on veut donner plusieurs droits simultanément, il suffit d'additionner le code pour obtenir les nouveaux droits.
Par exemple, un droit en lecture et en écriture correspond à la valeur 6 (4 + 2).
Lorsque l'on veut préciser les droits d'accès de l'utilisateur, du groupe et des autres, on peut condenser ces informations en un seul nombre.
C'est d'ailleurs ce qui est fait lorsque l'on donne les droits d'accès lors de l'appel à mkdir
ou lorsque l'on change ces droits avec chmod
par exemple.
Dans ce cas, on utilisera la notation 0xyz
ou x
correspndra au code des droits de l'utilisateur, y
celui du groupe et z
celui des autres.
Pour changer les droits d'accès sur un fichier, il est possible d'utiliser la commande chmod
.
Pour ce faire, il faut préciser le nom du fichier ainsi que les nouveaux droits d'accès sur ce fichier.
Voici quelques exemples d'utilisation :
chmod 0755 repertoire # Donne à l'utilisateur tous les droits et aux autres seulement les droits de pouvoir lire et parcourir le répertoire
chmod u=rwx fichier # Donne au propriétaire du fichier tous les droits
chmod a-w fichier # Enlève aux autres utilisateurs le droit de pouvoir écrire dans le fichier
Lorsque le fichier que l'on manipule est un répertoire, le droit en exécution correspond au droit de pouvoir parcourir le répertoire.
Ce répertoire contient en majorité des fichiers et des programmes scpéciaux. Ils offrent des fonctionnalités bien précise et chacun d'eux possède une documentation dans la section 4 du manuel. Voici un tableau résumant le fonctionnement de certains d'entre eux.
Fichier | Description |
---|---|
null |
Ignore les données écrites |
full |
Simule un disque plein |
random |
Génération de bytes aléatoires |
urandom |
Amélioration de random
|
Utile lorsque l'on n'a pas besoin du résultat de la sortie d'un programme. Les données écrites ici sont ignorées. Un exemple d'utilisation :
./programmeLong > resultat.txt 2> /dev/null
Lorque l'on souhaite vérifier le comportement d'un programme lorsque le disque est plein, il peut être utile d'utiliser le fichier full
.
L'écriture dans ce fichier lève automatiquement une erreur avec le signal ENOSPC
(No space left on device ou Pas assez d'espace dique).
./monProgramme > /dev/full
L'usage des fichiers random
et urandom
permet de générer des flux de bytes aléatoires.
Pour ce faire, ils utilisent le bruit produits par des éléments du système d'exploitation afin de générer des nombres pseudo-aléatoire.
Ces nombres sont particulièrement utile pour générer des système cryptographique sécurisés.
Il faut néanmoins faire attention, car la génération de ces nombres reposent sur le fait que suffisement d'entropie a été générée pour que de les nombres soit suffisement "aléatoires", ce qui n'est pas le cas lors du lancement de système d'exploitation.
Lorsque le système est jeune, il est préférable d'utiliser l'appel système getrandom
devrait être utilisé à la place.
Ce répertoire contient des fichiers temporaires. Lorsque le système s'éteint ou est redémarré, les fichiers contenus dans ce répertoire sont supprimés.
C'est là ou sont montées les clefs usb et/ou les disques durs/partitions windows.
Ainsi, si votre ordinateur est en dual boot avec Windows ou que vous utilisez la WSL, les partitions que Windows utilisent (souvent notées C
ou D
) sont montées dans ce répertoire.