Laboratoire 9 - Traitement des fichiers ouverts et des répertoires

Page content

Descripteur de fichier

  • Le programme permet de créer un fichier resultat qui contient Programme Programme mystère. En effet, on a deux descripteurs qui pointent sur la même entrée dans la table des fichiers ouverts (TFO) (qui dans notre cas est le fichier resultat). Le premier descripteur est utilisé pour écrire Programme mystère\n, le deuxième est utilisé pour reculer le curseur de 9 donc le curseur est positionné après Programme , et enfin le premier descripteur est réutilisé pour réecrire Programme mystère\n.

  • Le programme redir.c

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include<fcntl.h>

void main(int argc, char **argv) {
  if(argc < 4){
    printf("Usage : ./redir IN OUT commande [argument...]\n");
    exit(EXIT_FAILURE);
  }
  int fd_in, fd_out, retour_dup_0;
  fd_in = open(argv[1], O_RDONLY);
  if(fd_in == -1){
    perror(argv[1]);
  }
  fd_out = creat(argv[2], 0666);
  if(fd_out == -1){
    perror(argv[2]);
  }
  if(dup2(fd_in, 0) == -1 || dup2(fd_out, 1) == -1 || dup2(fd_out, 2) == -1){
	  perror("echec dup2");
  }
  close(fd_in);
  close(fd_out);
	execvp (argv[3], argv+3);
	perror("La commande ne peut pas être exécutée");
	exit(EXIT_FAILURE);
}
  • ./redir /dev/null /dev/tty ls -l /proc/self/fd/ affiche le contenu du fichier /proc/self/fd qui représente la table des descripteurs avec les fichiers sur lesquels ils pointent: 0 -> /dev/null 1 -> /dev/tty 2 -> /dev/tty 3 -> /proc/12375/fd

Mini tail

  • Version à un seul fichier
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>

int main(int argc, char* argv[]) {
	if (argc != 2) {
		return 1;
	}
	int file_descriptor;
	off_t old_size;
	struct stat file_stat;
	file_descriptor = open(argv[1], O_RDONLY);
	if (file_descriptor == -1) {
		perror("Erreur lors du open");
		return 1;
	}
	if (fstat(file_descriptor, &file_stat) == -1) {
		perror("Erreur lors du stat");
		return 1;
	}
    old_size = file_stat.st_size;
    
    char* buffer = NULL;
    while(1) {
        sleep(1);
		if  (fstat(file_descriptor, &file_stat) == -1) {
			perror("Erreur lors du stat");
			return 1;
		}
		if (file_stat.st_size > old_size) {
			int size = file_stat.st_size - old_size;
			buffer = malloc(size + 1);
			if (buffer == NULL) {
				perror("Erreur lors du malloc");
				return 1;
			}
			buffer[size] = '\0';
			lseek(file_descriptor, old_size, SEEK_SET);
			if (read(file_descriptor, buffer, size) == -1){
                perror("Erreur lors du read");
                return 1;
            }
			printf("%s\n", buffer);
		} else if (file_stat.st_size < old_size) {
			buffer = malloc(file_stat.st_size + 1);
			if (buffer == NULL) {
				perror("Erreur lors du malloc");
				return 1;
			}
			buffer[file_stat.st_size] = '\0';
			if (read(file_descriptor, buffer, file_stat.st_size) == -1){
                perror("Erreur lors du read");
                return 1;
            }
			printf("--- Le fichier a été tronqué! ---\n%s\n", buffer);
		}
		old_size = file_stat.st_size;
		free(buffer);
		buffer = NULL;
		lseek(file_descriptor, 0, SEEK_SET);
	}
}
  • Version avec plusieurs fichiers.
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>

int main(int argc, char* argv[]) {
	if (argc < 2) {
		return 1;
	}
	int fds[argc - 1];
	off_t prev_sizes[argc - 1];
	struct stat statbuf;
	int st;
	for (int i = 1; i < argc; i++) {
		fds[i-1] = open(argv[i], O_RDONLY);
		if (fds[i-1] == -1) {
			perror("Erreur lors de l'ouverture");
			return 1;
		}
		st = fstat(fds[i-1], &statbuf);
		if (st == -1) {
			perror("Erreur lors du stat");
			return 1;
		}
    	prev_sizes[i-1] = statbuf.st_size;
    }
    char* buf = NULL;
    while(1) {
        sleep(1);
		for(int i = 0; i < argc-1; i++) {
			st = fstat(fds[i], &statbuf);
			if (st == -1) {
				perror("Erreur lors du stat");
				return 1;
			}
			if (statbuf.st_size > prev_sizes[i]) {
				int size = statbuf.st_size - prev_sizes[i];
				buf = malloc(size + 1);
				if (buf == NULL) {
					perror("Erreur lors de l'allocation");
					return 1;
				}
				buf[size] = '\0';
				lseek(fds[i], prev_sizes[i], SEEK_SET);
				int rd = read(fds[i], buf, size);
				printf("%s:\n%s\n", argv[i+1], buf);
			} else if (statbuf.st_size < prev_sizes[i]) {
				buf = malloc(statbuf.st_size + 1);
				if (buf == NULL) {
					perror("Erreur lors de l'allocation");
					return 1;
				}
				buf[statbuf.st_size] = '\0';
				int rd = read(fds[i], buf, statbuf.st_size);
				printf("%s:---Fichier a été tronqué---\n%s\n", argv[i+1], buf);
			}
			prev_sizes[i] = statbuf.st_size;
			free(buf);
			buf = NULL;
			lseek(fds[i], 0, SEEK_SET);
		}
    }

}
  • Bonus: version utilisant inotify
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/inotify.h>
#include <unistd.h>
#include <fcntl.h>

struct file_watch {
	char* path;
	int fd;
	int wd;
	off_t size;
};

void handle_mod(int inotif, struct file_watch* watched, int num_wds) {
	struct inotify_event* event;
	char buf[sizeof(struct inotify_event)];
	int len = read(inotif, buf, sizeof(struct inotify_event));
	struct stat statbuf;
	char* content = NULL;
	char* msg = NULL;
	while (len > 0) {
		event = (struct inotify_event *)buf;
		for (int i = 0; i < num_wds; i++) {
			if (watched[i].wd == event->wd) {
				fstat(watched[i].fd, &statbuf);
				// Si la taille a augmentée, on affiche le nouveau contenu
				if (statbuf.st_size > watched[i].size) {
					msg = "";
					int size = statbuf.st_size - watched[i].size;
					content = malloc(size + 1);
					content[size] = '\0';
					read(watched[i].fd, content, size);
				// Si la taille a baissée, on affiche tout le contenu
				} else if (statbuf.st_size < watched[i].size) {
					msg = " file truncated";
					lseek(watched[i].fd, 0, SEEK_SET);
					content = malloc(statbuf.st_size + 1);
					content[statbuf.st_size] = '\0';
					read(watched[i].fd, content, statbuf.st_size);
				} else {
					continue;
				}
				printf("File: %s%s\n%s\n", watched[i].path, msg, content);
				free(content);
				content = NULL;
				watched[i].size = statbuf.st_size;
			}
		}

		len = read(inotif, buf, sizeof(struct inotify_event));
	}
}

int main(int argc, char* argv[]) {
	if (argc < 2) {
		return 1;
	}

	int inotif = inotify_init();
	if (inotif == -1) {
		perror(NULL);
		return -1;
	}
	struct file_watch* watched = malloc(sizeof(struct file_watch) * argc-1);
	struct stat statbuf;
	char* content = NULL;
	// pour chaque fichier, on ajoute un watcher inotify, on remplit le struct et on affiche le contenu initial
	for (int i = 1; i < argc; i++) {
		watched[i-1].path = argv[i];
		watched[i-1].wd = inotify_add_watch(inotif, argv[i], IN_MODIFY);
		if (watched[i-1].wd == -1) {
			perror("Add watch");
			free(watched);
			return -1;
		}

		watched[i-1].fd = open(watched[i-1].path, O_RDONLY);
		if (watched[i-1].fd == -1) {
			perror("Open");
			free(watched);
			return -1;
		}
		int st = fstat(watched[i-1].fd, &statbuf);
		if (st == -1) {
			perror("Stat");
			free(watched);
			return -1;
		}
		watched[i-1].size = statbuf.st_size;
		content = realloc(content, watched[i-1].size + 1);
		if (content == NULL) {
			perror("Realloc");
			free(watched);
			return -1;
		}
		content[watched[i-1].size] = '\0';
		int rd = read(watched[i-1].fd, content, watched[i-1].size);
		if (rd == -1) {
			perror("Read");
			free(watched);
			return -1;
		}
		printf("File: %s\n%s\n", watched[i-1].path, content);
	}
	free(content);

	while (1) {
		handle_mod(inotif, watched, argc-1);
	}
	return 0;
}