Partie 2 - Fichiers¶
Pour les exercices ne demandant pas d’écrire un code C, créez un fichier Markdown (p2e[numero exo].md) pour lister les commandes utilisées et les résultats.
p2e1 — Métadonnées et types (shell)¶
Créez 3 fichiers :
un fichier texte :
echo "hello" > p2e1.txtun fichier binaire :
head -c 5000 /dev/urandom > p2e1.datun script shell
p2e1.shque vous rendrez exécutable (chmod +x p2e1.sh) contenant :
#!/usr/bin/env bash
echo "coucou"
Affichez leurs métadonnées avec stat et leur nature avec file.
Comparez les champs clés (type, taille, blocs, droits).
Commandes :
stat FICHIER: taille logique, blocs, mode (permissions), inode.ls -lhi: taille lisible, numéros d’inodes.file FICHIER: type de contenu (texte ASCII, binaire, script, etc.).
p2e2 — Liens physiques et symboliques (shell)¶
Créez un fichier p2e2_source.txt.
Créez un lien symbolique p2e2_sym.txt et un lien physique p2e2_hard.txt vers p2e2_source.txt.
Observez ls -li / stat des 3 fichiers. Modifiez le contenu d’un des fichiers et regardez le contenu dans les autres fichiers.
Supprimez le fichier p2e2_source.txt puis observez à nouveau ls -li, stat et cat sur p2e2_hard.txt et p2e2_symbolique.txt
Commandes :
ln SOURCE DEST: voirmanpour les options physique/symbolique
p2e3 — I/O fichier (libc/stdio)¶
Écrire un programme p2e3.c qui utilise les fonctions de la libc pour manipuler les fichiers (fopen, fclose, …). Il doit : ouvrir un fichier p2e3_anneau.txt en écriture et écrire :
Un Anneau pour les gouverner tous,
un Anneau pour les trouver,
un Anneau pour les amener tous et dans les ténèbres les lier.
Puis ferme le fichier et l’ouvre en lecture pour afficher son contenu ligne par ligne avec fgets.
Fonctions :
FILE *fopen(const char *pathname, const char *mode)pour ouvrir le fichier avec droits d’écriture/de lecture ("w"/"r"pour écriture/lecture en binaire)int fprintf(FILE *stream, const char *format, ...)pour écrire une chaine formatéeformatdans le flux (fichier/stdout/stderr)int fputs(const char *s, FILE *stream)écritsdans le flux (fichier/stdout/stderr)char *fgets(char *s, int size, FILE *stream)écrit dansssizeoctets depuis le fluxstreamint fclose(FILE *stream)ferme le flux
p2e4 — I/O texte POSIX (bas niveau)¶
Écrire un programme p2e4.c qui utilise les fonctions POSIX pour manipuler les fichiers (open, close, …). Il doit : ouvrir un fichier p2e4_nazg.txt en écriture et écrire :
Ash nazg durbatulûk,
ash nazg gimbatul,
ash nazg thrakatulûk agh burzum-ishi krimpatul
Puis ferme le fichier et l’ouvre en lecture pour afficher son contenu avec read.
Pour les includes :
#define _POSIX_C_SOURCE 200809L // utilise la version de 2008 de POSIX
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
Fonctions :
int open(const char*, int flags, mode_t mode)pour ouvrir le fichier, à la création les flags àO_WRONLY|O_CREAT|O_TRUNCet le mode à0644(mode obligatoire si création du fichier avecO_CREAT)int open(const char *pathname, int flags)pour ouvrir le fichier avec le flagO_RDONLYpour la lecturessize_t write(int fd, const void *buf, size_t count)pour écrire dansfd(file descriptor)countoctets debufssize_t read(int fd, void*, size_t): lectures partielles possibles → boucler jusqu’à0(EOF)int close(int fd)
p2e5 — I/O binaire (libc)¶
Écrire un programme p2e5.c qui initialise un tableau d’int32_t avec {INT32_MIN, INT32_MIN + 1, -2048, -1, 0, 1, 2048, INT32_MAX - 1, INT32_MAX} (int32_t, INT32_MIN/MAX disponibles avec #include <stdint.h>) puis l’écrit dans un fichier binaire p2e5.dat via fwrite.
Puis ouvre le fichier pour lire les valeurs avec fread.
Vérifier la taille du fichier avec stat et comparer à sizeof(int32_t)*9.
Inspecter le contenu du fichier avec xxd p2e5.dat ou hexdump -C p2e5.dat et od -t d4 p2e5.dat.
xxd affiche les octets dans l’ordre mémoire. En little-endian, un mot 32 bits 0xAABBCCDD est stocké DD CC BB AA. Pour reconstituer la valeur, il faut réassembler les 4 octets dans l’ordre inverse (ou utiliser od/hexdump formatés).
Fonctions :
FILE *fopen(const char *pathname, const char *mode)pour ouvrir le fichier avec droits d’écriture/de lecture ("wb"/"rb"pour écriture/lecture en binaire)size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream)pour écrite le contenu pointé parptrdont les éléments ont une taille desizeoctets (icisizeof(int32_t)) et au nombre denmemb(le nombre d’éléments dans le tableau) à écrire dans le fluxstreamint fclose(FILE *stream)ferme le fluxsize_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream)lit le contenu du fluxstreamet écrit dansptrles données de taillesizeau nombre denmemb
p2e6 — Renommer un fichier (libc)¶
Écrire un programme p2e6.c : p2e6 OLD NEW → qui renomme le fichier OLD en NEW, si NEW n’existe pas, sinon, affiche un message d’erreur.
Fonctions :
int access(const char *path, int amode)avecamode = F_OK, retourne 0 si le fichierpathexisteint rename(const char *old, const char *new)renommeoldennew
p2e7 — Lecture partielle (seek POSIX)¶
Écrire un programme p2e7 qui lit les 10 octets après les 15 premiers d’un fichier avec les fonctions POSIX.
Avec un fichier contenant :
ABCDEFGHIJKLMNOPQRSTUVWXYZ
Le programme affiche :
PQRSTUVWXY
Fonctions :
int fd = open(path, O_RDONLY)pour ouvrir le fichier en lectureoff_t lseek(int fd, off_t offset, int whence)pour se déplacer au 15ème octet (offset),whence=SEEK_SET(pour le début du fichier)ssize_t read(int fd, void *buf, size_t count)pour lirecountoctets defdà écrire dansbufint close(int fd)pour fermer le fichier
p2e8 — cat (libC)¶
Afficher le contenu d’un fichier avec préfixe N: à largeur fixe (min. 4), similaire à un cat -n, une ligne par getline. Utilisez les fonctions de la lib C :
FILE* fopen(..., "r")etfclosessize_t getline(char **lineptr, size_t *n, FILE *stream)retournessize_t(nombre de caractères lus) ou -1 (si EOF/erreur, vérifierfeofvsferror) et si lineptr estNULL,getlineallouenoctets danslineptrqu’il faudra libérer.lineptrcontient ensuite la prochaine ligne du fluxstream
p2e9 — head -n (libC)¶
Écrire un programme p2e9 [-n N] FILE qui affiche les N premières lignes du fichier FILE (défaut 10 lignes).
int strcmp(const char *s1, const char *s2)retourne 0 sis1ets2sont égauxlong strtol(const char *nptr, char **endptr, int base)retourne un long int correspondant au nombre écrit dansnptravec la base donnée, pour convertirargv[i]en nombre :n = (int)strtol(argv[++i], NULL, 10);
p2e10 — Créer une arborescence + liens¶
p2e10 rep_name nb_d nb_f crée :
un répertoire
rep_namepour
d ∈ [1..nb_d]:rep_name/sous_rep_d/pour chaque
f ∈ [1..nb_f]: fichier videsrep_name/sous_rep_d/sous_rep_d_fichier_f.txtau niveau racine
rep_name/:lien physique
lien_physique_vers_sous_rep_1_fichier_1.txt→sous_rep_1/sous_rep_1_fichier_1.txtlien symbolique
lien_symbolique_vers_sous_rep_1_fichier_2.txt→sous_rep_1/sous_rep_1_fichier_2.txt
Fonctions :
int mkdir(const char *path, mode_t mode);(ex :mode=0755)Création fichier :
open(path, O_WRONLY|O_CREAT|O_TRUNC, 0644)puiscloseLiens :
int link(const char *oldpath, const char *newpath);(physique)int symlink(const char *target, const char *linkpath);(symbolique)Construction de chemins :
snprintfdans des buffers dimensionnés.
p2e11 — Copier un fichier (POSIX)¶
Écrire p2e11 SOURCE DEST qui copie SOURCE vers DEST en utilisant les appels système POSIX de bas niveau.
Fonctions :
int open(const char *path, int flags, ...);Source :
open(path, O_RDONLY)Dest :
open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644)
ssize_t read(int fd, void *buf, size_t count);ssize_t write(int fd, const void *buf, size_t count);int close(int fd);
Utilisez un tampon (char buffer[]) de 8192 octets (2 pages mémoire).
// Active les extensions POSIX récentes (getline, etc.)
#define _POSIX_C_SOURCE 200809L
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
/**
* Écrit exactement `count` octets de `buf` dans le descripteur `fd`,
* en répétant les appels à write() si nécessaire (cas d'écritures partielles
* ou interruptions par un signal EINTR).
* Retourne le nombre total d'octets écrits, ou -1 en cas d'erreur.
*/
ssize_t write_full(int fd, const void *buf, size_t count) {
size_t done = 0;
while (done < count) {
ssize_t w = write(fd, (const char *)buf + done, count - done);
if (w > 0) {
done += (size_t)w;
} else if (w < 0 && errno == EINTR) {
continue;
} else {
return -1;
}
}
return (ssize_t)done;
}
int main(int argc, char **argv) {
if (argc != 3) {
fprintf(stderr, "Usage: %s SOURCE DEST\n", argv[0]);
return EXIT_FAILURE;
}
// TODO
return EXIT_SUCCESS;
}
Pour le fichier, en prendre un de plus de 8192 octets et vérifier que tout est bien copié :
# pour télécharger Dracula de Bram Stoker (domaine public - Project Gutenberg)
wget https://www.gutenberg.org/cache/epub/345/pg345.txt
$ # -Wconversion vérifie les types
$ gcc -std=c2x -Wall -Wextra -pedantic -Wconversion -g p2e11.c -o p2e11
$ strace ./p2e11 pg345.txt dracula.txt
...
...
$ # si les valeurs de sha256sum sont les mêmes, le contenu est le même
$ sha256sum dracula.txt pg345.txt
50a66d0773e9476e97bd731947ba03ecf7f7f92eefc59c0027acf48129dc6cbf dracula.txt
50a66d0773e9476e97bd731947ba03ecf7f7f92eefc59c0027acf48129dc6cbf pg345.txt
$ # si cmp n'affiche rien, les fichiers sont les mêmes
$ cmp dracula.txt pg345.txt
p2e12 — Éviter les accès concurrents¶
Écrire un programme p2e12 qui ouvre un fichier p2e12.txt, lit un entier, l’incrémente et réécrit la valeur, en utilisant flock pour empêcher deux exécutions simultanées.
Lancer plusieurs instances du programme en parallèle et observer que la valeur finale reste cohérente :
> # lance 1000 instances en parallèle (-P100 tente d'en lancer 100 simultanées)
> seq 1 1000 | xargs -n1 -P100 -I{} ./p2e12 > /dev/null 2>&1