Cours système d'exploitation by Guillaume Chanel, Jean-Luc Falcone and University of Geneva is licensed under CC BY-NC-SA 4.0
device ID)Un inode contient trois temps différents:
| atime | date du dernier accès à l'inode (ou aux données) |
|---|---|
| mtime | date de la dernière modification des données |
| ctime | date de la dernière modifications des méta-données |
| crtime | date de création du fichier (non POSIX) |
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: -
inodes.unlink).On peut créer un lien dur avec la commande ln:
$ ln /tmp/myfile /tmp/newlink
$ stat /tmp/myfile
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: -
Quelle sera la date de modification de /tmp/myfile si je modifie le contenu du lien dur /tmp/newlink ?
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/myfile # 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: -
link, symlink)unlink)int unlink(const char *pathname);
pathname est le nom à supprimerman 2 unlink pour les codes d'erreursfsck), il sera copié dans le répertoire lost+found à la racine du système de fichier.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
};
statL'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 );
st_mode est un champ de bits contenant les permissions et le type d'un inode.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" );
}
On peut utiliser plusieurs flags pour accéder aux valeurs du champs de bits:
S_IRUSR | 00400 | owner has read permission |
S_IWUSR | 00200 | owner has write permission |
S_IXUSR | 00100 | owner has execute permission |
S_IRGRP | 00040 | group has read permission |
S_IWGRP | 00020 | group has write permission |
S_IXGRP | 00010 | group has execute permission |
S_IROTH | 00004 | others have read permission |
S_IWOTH | 00002 | others have write permission |
S_IXOTH | 00001 | others have execute permission |
lstatstat("A",...) retourne les informations sur l'inode de B.lstat():int lstat(const char *path, struct stat *buf);
stat().fstatfstat() fonctionne comme stat() mais permet d'utiliser un descripteur de fichier à la place d'un nom:int fstat(int fd, struct stat *buf);
accessaccess():int access(const char *pathname, int mode);
mode est un champs de bits formés des flags:
R_OK | lecture possible |
W_OK | écriture possible |
X_OK | éxécution possible |
F_OK (seulement) qui indique si le fichier existe.access() retourne 0 si le test réussit, -1 sinon (cf errno)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 );
chmodOn 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.
dirent associants un lien à un inode. et ..Un processus possède un répertoire courant qui permet d'interpréter les chemins relatifs (e.g. src/monfichier.c).
Au démarrage du programme ce répertoire est celui depuis lequel le programme est lancé. Ce n'est donc pas forcément le répertoire de l'exécutable.
Pour connaitre le répertoire courant:
#include <unistd.h>
char *getcwd(char *buf, size_t size);
buf: chaine de caractère (déclarée par l'utilisateur) qui contiendra le répertoire courant;size: taille du buffer;buf sinon.Pour changer le répertoire courant:
#include <unistd.h>
int chdir(const char *path);
path: chaine de caractère indiquant le nouveau répertoire (relatif ou absolu)direntLes 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
dirent (3)Le champs d_type est un champs de bits contenant des informations sur le type de l'inode associé:
DT_DIR | Répertoire |
DT_LNK | Lien symbolique |
DT_REG | Fichier de données |
DT_UNKNOWN | Type inconnu |
DT_... | Voir man readdir pour tous les types. |
foo/
foo/goo/
foo/goo/bar.txt
foo/goo/baz.txt
Pour accéder aux entrées d'un répertoire, il faut:
opendir()readdir()closedir()Note: les fonctions ci-dessus ne sont pas des appels système. Pour manipuler des dossiers avec des appels système il faut utiliser les appels open et getdents. Toutefois en pratique on utilise les fonctions ci-dessus.
opendir)
DIR *opendir(const char *name);
DIR *fdopendir(int fd);
DIR est un type opaqueDIR sera NULLman):EACCESS | opération interdite (permissions) |
ENOENT | Le répertoire n'existe pas ou le nom est une chaîne vide. |
ENOTDIR | Le nom existe mais n'est pas un répertoire. |
readdir)struct dirent *readdir(DIR *dirp);
direntNULL s'il n'y a plus d'entrée ou en cas d'erreur.EBADF | Le descripteur dirp n'est pas valide. |
Question: Devez-vous libérer la mémoire de la structure retournée ?
readdir)closedir)int closedir(DIR *dirp);
EBADF | Le descripteur dirp n'est pas valide. |
examples/listDir.c)#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <string.h> //snprintf
#include <errno.h>
#include <dirent.h>
#include <limits.h> //PATH_MAX
static void list_dir (const char * dir_name){
DIR *d = opendir(dir_name);
struct dirent *entry;
const char *d_name; //nom d'une entrée
//En cas d'erreur d'ouverture
if (! d) {
fprintf(stderr, "Cannot open directory '%s': %s\n",
dir_name, strerror(errno));
exit(EXIT_FAILURE);
}
//Boucle sur chaque entrée
while( (entry = readdir(d)) != NULL ) {
// Obtient le nom de l'entrée et affiche
d_name = entry->d_name;
printf("%s/%s\n", dir_name, d_name);
//Est-ce que 'entry' est un sous-répertoire
if (entry->d_type & DT_DIR) {
//Est-ce que 'entry' n'est pas '..' ou '.'
if (strcmp(d_name, "..") != 0 && strcmp(d_name, ".") != 0) {
char path[PATH_MAX];
//forme le nom du sous-répertoire et affiche
int path_length = snprintf (path, PATH_MAX,
"%s/%s", dir_name, d_name);
printf("%s\n", path);
//Vérifie que le nom du sous-répertoire n'est pas trop long
if (path_length >= PATH_MAX) {
fprintf(stderr, "Path length has got too long.\n");
exit(EXIT_FAILURE);
}
//Appel récursif
list_dir(path);
}
}
} //while(1)
//On ferme le répertoite
if( closedir(d) ) {
fprintf(stderr, "Could not close '%s': %s\n",
dir_name, strerror (errno));
exit (EXIT_FAILURE);
}
}
int main () {
list_dir("/var/log/");
return EXIT_SUCCESS;
}
mkdir)int mkdir(const char *pathname, mode_t mode);
pathname est le nom du répertoiremode spécifie les permissions à utiliser, il est modifié par le umask du processus:mode & ~umask & 0777
man 2 mkdir pour les codes d'erreursrmdir)int rmdir(const char *pathname);
man 2 rmdir pour les codes d'erreurs