Appels systèmes fichiers
TBD commencer par un strace fopen et voir que c'est open qui est utilisé.
TBD faire un fopen et voir avec un strace que c'est bien open qui est utilisé.
La création et gestion des fichiers est du ressort du noyau. Il faut donc faire un appel système à chaque manipulation.
La gestion effective des fichiers lorsque l'on code en C se fait souvent via d'autres fonctions (que nous verrons plus tard), plus pratiques à utiliser.
Les cinq principaux appels systèmes pour gérer un fichier sont :
- open : ouvre un fichier et rend un file descriptor
- close : ferme un fichier via son file descriptor
- read : lit du fichier
- write : écrit dans un fichier
- lseek : se positionne dans un fichier, si disponible
Il en existe d'autres, comme dup dont le but est de dupliquer des file descriptors.
La section 2 de la commande man
renseigne sur les appels systèmes. Faites l'essai pour les 5 fonctions ci-dessus
TBD : un buffer ?
Gestion des fichiers
TBD ici. faire les programmes et expliquer. Puis strace/ltrace après chacun.
Plusieurs programmes :
Ouverture et/ou création
#include
#include
#include
#include
#include
#include
extern int errno;
int main()
{
int fd = open("mon-fichier.txt",
O_WRONLY|O_CREAT|O_TRUNC,
S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH | S_IWOTH);
//printf("fd = %d\n", fd);
if (fd == -1) {
perror("Erreur");
}
close(fd);
return 0;
}
une fois compilé, on peut exécuter le code. Il crée un fichier nommé mon-fichier.txt
dans le dossier courant. En lisant la page man 2 open
(la section 2 est pour les appels systèmes) on voit que l'on :
- crée ou ouvre un fichier en écriture
- on lui donne des droits.
- modifiez le contenu de
mon-fichier.txt
dans un éditeur puis re-exécutez le code. Le fichier devrait à nouveau être vide - changez le code pour que les autres n'aient pas le droit de lire le fichier créé
En utilisant ltrace
pour voir les appels aux bibliothèques :
$ ltrace ./a.out
__libc_start_main(0xaaaacf2e0854, 1, 0xffffc805d068, 0 <unfinished ...>
open("mon-fichier.txt", 577, 0646) = 3
printf("fd = %d\n", 3fd = 3
) = 7
close(3) = 0
__cxa_finalize(0xaaaacf2f1048, 0xaaaacf2e0800, 0x10dd0, 1) = 1
+++ exited (status 0) +++
On voit qu'il y en à 3 : open
, printf
et close
. Ils correspondent à des appels à la libc. Pour utiliser des fichiers, le process doit demander au noyau de le faire via des appels systèmes. On peut les voir avec la commande strace
.
$ strace ./a.out
execve("./a.out", ["./a.out"], 0xfffffb9635d0 /* 46 vars */) = 0
brk(NULL) = 0xaaab1d30f000
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xffff94947000
faccessat(AT_FDCWD, "/etc/ld.so.preload", R_OK) = -1 ENOENT (Aucun fichier ou dossier de ce type)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
newfstatat(3, "", {st_mode=S_IFREG|0644, st_size=72775, ...}, AT_EMPTY_PATH) = 0
mmap(NULL, 72775, PROT_READ, MAP_PRIVATE, 3, 0) = 0xffff94900000
close(3) = 0
openat(AT_FDCWD, "/lib/aarch64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0\267\0\1\0\0\0\340u\2\0\0\0\0\0"..., 832) = 832
newfstatat(3, "", {st_mode=S_IFREG|0755, st_size=1641496, ...}, AT_EMPTY_PATH) = 0
mmap(NULL, 1810024, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xffff94746000
mmap(0xffff94750000, 1744488, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0) = 0xffff94750000
munmap(0xffff94746000, 40960) = 0
munmap(0xffff948fa000, 24168) = 0
mprotect(0xffff948d9000, 61440, PROT_NONE) = 0
mmap(0xffff948e8000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x188000) = 0xffff948e8000
mmap(0xffff948ee000, 48744, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0xffff948ee000
close(3) = 0
set_tid_address(0xffff94947f30) = 1858948
set_robust_list(0xffff94947f40, 24) = 0
rseq(0xffff94948600, 0x20, 0, 0xd428bc00) = 0
mprotect(0xffff948e8000, 16384, PROT_READ) = 0
mprotect(0xaaaae5610000, 4096, PROT_READ) = 0
mprotect(0xffff9494c000, 8192, PROT_READ) = 0
prlimit64(0, RLIMIT_STACK, NULL, {rlim_cur=8192*1024, rlim_max=RLIM64_INFINITY}) = 0
munmap(0xffff94900000, 72775) = 0
openat(AT_FDCWD, "mon-fichier.txt", O_WRONLY|O_CREAT|O_TRUNC, 0646) = 3
close(3) = 0
exit_group(0) = ?
+++ exited with 0 +++
Tout le début des appels systèmes est là pour mettre en mémoire le programme, charger la bibliothèque partagée libc
et faire les différents liens pour permettre son exécution.
Les 3 derniers appels sont en revanche issus de notre code :
- le dernier
exit_group(0)
est le code de sortie (ligne 23 du code) - l'avant dernier
close(3)
correspond à la fermeture de notre fichier (ligne 23 du code) - l'avant avant dernier
openat(AT_FDCWD, "mon-fichier.txt", O_WRONLY|O_CREAT|O_TRUNC, 0646) = 3
correspond à l'ouverture du fichier (ligne 12 du code)
On voit que le compilateur a utilisé :
- l'appel système
openat
- il a donné un numéro octal (il commence par 0) pour les permissions.
Et que le file descriptor de notre fichier ouvert est 3.
Dé-commentez la ligne 16 du code et trouvez l'appel système correspondant.
solution
solution
C'est un write dans stdout (de file descriptor 1).
Autre
TBD lseek et monter ce que ça donne sur un fichier ? adapter fseek