Fonctions système concernant les fichiers

© D. Mathieu     mathieu@romarin.univ-aix.fr
I.U.T.d'Aix en Provence - Département Informatique
Créé le 04/08/2000 - Dernière mise à jour : 07/09/2001

Sommaire

Introduction
Protection des fichiers
Masque d'accès aux fichiers, masque d'état
Types de fichier
File descriptor
Organisation de quelques fichiers inclus
Protection des fichiers en lecture/écriture
Compatibilité Unix-Windows

Introduction

    Les fonctions systèmes [1] essentielles concernant les fichiers sont les suivantes :
 
nom de la fonction
fichier permettant d'accéder à son prototype
chmod()   /usr/include/sys/stat.h
close()   /usr/include/unistd.h
creat()   /usr/include/fcntl.h [2]
dup()   /usr/include/unistd.h
dup2()   /usr/include/unistd.h
fchmod()   /usr/include/sys/stat.h
fcntl()   /usr/include/fcntl.h
flock()   /usr/include/sys/file.h
fstat()   /usr/include/sys/stat.h
lseek()   /usr/include/unistd.h
open()   /usr/include/fcntl.h
pipe()   /usr/include/unistd.h
read()   /usr/include/unistd.h
stat()   /usr/include/sys/stat.h
umask()   /usr/include/sys/stat.h
unlink()   /usr/include/unistd.h
write()   /usr/include/unistd.h

    Pour pouvoir être utilisées, certaines de ces fonctions peuvent de plus nécessiter l'inclusion d'autres fichiers parmi lesquels :
 
/usr/include/g++-2[3]/cerrno[4]  // conformément à la norme C++
/usr/include/g++-2/climits[5]   // conformément à la norme C++
/usr/include/sys/types.h       // types comme mode_t, etc.
/usr/include/posix1_lim.h

    En C++, on utilisera de préférence les flux (streams). En C, on préférera les fichiers de type FILE et les opérations standard ANSI (fopen(), fread(), etc…). En système, on utilisera les fonctions système.

    En cas d'erreur en cours d'exécution, la plupart des fonctions système renvoient la valeur -1 et positionnent dans la variable errno (accessible par inclusion du fichier <cerrno>, mais défini dans le fichier <asm/errno.h>), un code d'erreur précisant la cause exacte de l'erreur. Cette information n'est parfois pas assez précise et  doit être complétée par la consultation du manuel (man 2 nom_de_la_fonction ou commande xman).

Sommaire

Protection des fichiers

    A chaque fichier est associé un masque de droits d'accès[6], constitué de 12 bits qui se répartissent ainsi :
 
bits 0, 1, 2
bits 3, 4, 5
bits 6, 7, 8
bit 9 
bit 10
bit 11
 exécution, accès en écriture et lecture pour tous les utilisateurs,
 exécution, accès en écriture et lecture pour le groupe du propriétaire,
 exécution, accès en écriture et lecture pour le propriétaire,
 bit de "collage" (sticky-bit)
 bit SET-GUID
 bit SET-UID

    On a l'habitude de représenter les neuf premiers bits des masques de protection dans l'ordre des bits décroissants, en représentant l'état des bits par les symboles r, w et x lorsque les bits sont positionnés et - lorsqu'ils sont à la valeur 0. Ainsi, rwxrw-r-- est un masque autorisant l'accès du fichier correspondant en lecture à tout le monde, en écriture seulement au propriétaire et au groupe auquel il appartient, et limite les droits d'exécution au seul propriétaire.

   i Il est fréquent d'exprimer un masque de droits d'accès directement par la valeur numérique des groupes de trois bits, en utilisant la numération octale. Ainsi, la valeur du masque ci-dessus (rwxrw-r--) correspond à 0764. Attention : seul le 0 qui précède le nombre indique qu'il s'agit d'une valeur octale.

   i Les trois bits de fort poids n'ont de signification que pour un fichier exécutable. S'ils sont positionnés, les bits SET-UID/SET-GUID sont représentés par s à la place des x correspondants. S'il est positionné, le sticky-bit est représenté par t à la place de x des utilisateurs :

Sommaire

Masque d'accès aux fichiers, masque d'état

    Une fois qu'un fichier est ouvert, sont conservées dans une table du système des indicateurs concernant :     Ces différentes caractéristiques sont positionnables et modifiables par les fonctions open() et fcntl().

Sommaire

Types de fichier

    En réalité, ces 12 bits sont précédés de 4 autres bits (bits 12 à 15) indiquant le type du fichier : normal, répertoire, fichier spécial bloc ou caractère, FIFO. Ainsi, les droits d'un fichier sont codés sur 2 octets.

     Le fichier /usr/include/linux/stat.h contient toutes les constantes symboliques permettant de positionner ou d'extraire ces différents bits, par exemple :
 
#define S_IRWXU 00700 /* read,write,execute perm: owner */
#define S_IRUSR 00400 /* read, permission: owner */
#define S_IWUSR 00200 /* write, permission: owner */
#define S_IXUSR 00100 /* execute/search, permission: owner */

#define S_IRWXG 00070 /* read,write,execute perm: group */
#define S_IRGRP 00040 /* read, permission: group */
#define S_IWGRP 00020 /* write, permission: group */
#define S_IXGRP 00010 /* execute/search, permission: group */

#define S_IRWXO 00007 /* read,write,execute perm: other */
#define S_IROTH 00004 /* read, permission: other */
#define S_IWOTH 00002 /* write, permission: other */
#define S_IXOTH 00001 /* execute/search, permission: other */

ainsi que des macros permettant de tester le type du fichier :
 
#define S_IFMT  00170000
#define S_IFSOCK 0140000
#define S_IFLNK  0120000
#define S_IFREG  0100000
#define S_IFBLK  0060000
#define S_IFDIR  0040000
#define S_IFCHR  0020000
#define S_IFIFO  0010000
#define S_ISUID  0004000
#define S_ISGID  0002000
#define S_ISVTX  0001000

#define S_ISLNK(m)      (((m) & S_IFMT) == S_IFLNK)
#define S_ISREG(m)      (((m) & S_IFMT) == S_IFREG)
#define S_ISDIR(m)      (((m) & S_IFMT) == S_IFDIR)
#define S_ISCHR(m)      (((m) & S_IFMT) == S_IFCHR)
#define S_ISBLK(m)      (((m) & S_IFMT) == S_IFBLK)
#define S_ISFIFO(m)     (((m) & S_IFMT) == S_IFIFO)
#define S_ISSOCK(m)     (((m) & S_IFMT) == S_IFSOCK)

    La signification de ces macros est obtenu par la commande man 2 stat :
 
S_ISLNK(m)  is it a symbolic link?
S_ISREG(m)  regular file?
S_ISDIR(m)  directory?
S_ISCHR(m)  character device?
S_ISBLK(m)  block device?
S_ISFIFO(m) fifo?
S_ISSOCK(m) socket?

m est la valeur du champ st_mode de la structure stat renvoyée par la fonction système stat() :
 
struct stat
{
    ......
    umode_t       st_mode;     /* protection */
    ......
};

Sommaire

File descriptor

    Pour désigner un fichier fermé, il faut utiliser son nom, éventuellement précédé de son chemin d'accès. Par exemple :
 
... open ("/users/prof/mathieu/PRIVE/tp/tpsys/dirfile/exo_01.cxx", ...

    A tout fichier (au sens large : périphérique, fichier disque, socket, pipe, etc.) ouvert correspond un entier qui doit être utilisé dans toutes les opérations à effectuer sur ce fichier : le descripteur de fichier (file descriptor). Cet entier est attribué par le système lorsque le fichier est ouvert.

Sommaire

Organisation de quelques fichiers inclus

    Les imbrications des fichiers inclus sont assez complexes et variables d'une version à l'autre.

    La majorité des types importants sont à rechercher dans la hiérarchie[8] suivante :
 
<sys/types.h>
    <features.h>
    <bits/types.h>

    Voici quelques types définis dans le fichier <sys/types.h> :
 
...
#include <features.h>
#include <bits/types.h>
typedef __u_char u_char;
typedef __u_short u_short;
...
#ifdef  __USE_BSD
    ...
    # include <endian.h>
    ...
#endif /* Use BSD.  */

    Le fichier <features.h> contient de nombreuses macros qui définissent l'environnement :
 
/* These are defined by the user (or the compiler) to specify the desired environment:
__STRICT_ANSI__     ISO Standard C.
_ISOC9X_SOURCE      Extensions to ISO C 89 from ISO C 9x.
_POSIX_SOURCE       IEEE Std 1003.1.
_POSIX_C_SOURCE     If ==1, like _POSIX_SOURCE;
                    if >=2 add IEEE Std 1003.2;
                    if >=199309L, add IEEE Std 1003.1b-1993;
                    if >=199506L, add IEEE Std 1003.1c-1995
_XOPEN_SOURCE        Includes POSIX and XPG things.  Set to 500 if
                        Single Unix conformance is wanted.
_XOPEN_SOURCE_EXTENDED XPG things and X/Open Unix extensions.
_LARGEFILE_SOURCE    Some more functions for correct standard I/O.
_LARGEFILE64_SOURCE  Additional functionality from LFS for large files.
_FILE_OFFSET_BITS=N  Select default filesystem interface.
_BSD_SOURCE          ISO C, POSIX, and 4.3BSD things.
_SVID_SOURCE         ISO C, POSIX, and SVID things.
_GNU_SOURCE          All of the above, plus GNU extensions.
_REENTRANT           Select additionally reentrant object.
_THREAD_SAFE         Same as _REENTRANT, often used by other systems.
...
These are defined by this file and are used by the header files to decide what 
            to declare or define:
__USE_ISOC9X         Define ISO C 9X things.
__USE_POSIX          Define IEEE Std 1003.1 things.
__USE_POSIX2         Define IEEE Std 1003.2 things.
__USE_POSIX199309    Define IEEE Std 1003.1, and .1b things.
__USE_POSIX199506    Define IEEE Std 1003.1, .1b, .1c and .1i things.
__USE_XOPEN          Define XPG things.
__USE_XOPEN_EXTENDED Define X/Open Unix things.
__USE_UNIX98         Define Single Unix V2 things.
__USE_LARGEFILE64    Define LFS things with separate names.
__USE_FILE_OFFSET64  Define 64bit interface as default.
__USE_BSD            Define 4.3BSD things.
__USE_SVID           Define SVID things.
__USE_MISC           Define things common to BSD and System V Unix.
__USE_GNU            Define GNU extensions.
__USE_REENTRANT      Define reentrant/thread-safe *_r functions.
__USE_REENTRANT      Define reentrant/thread-safe *_r functions.
__FAVOR_BSD          Favor 4.3BSD things in cases of conflict.
...
#if (_POSIX_C_SOURCE - 0) >= 199309L
   # define __USE_POSIX199309      1
#endif
#if (_POSIX_C_SOURCE - 0) >= 199506L
   # define __USE_POSIX199506      1
#endif
...

    Le fichier <bits/types.h> contient quelques types utilisés dans différents fichiers en fonction des macros qui définissent l'environnement. L'utilisateur ne devrait pas avoir à les utiliser :
 
...
/* Convenience types.  */
typedef unsigned char  __u_char;
typedef unsigned short __u_short;
typedef unsigned int   __u_int;
typedef unsigned long  __u_long;
...

    De nombreuses constantes importantes sont à rechercher dans la hiérarchie suivante :
 
<limits.h>
    <bits/posix1_lim.h>
    <sys/file.h>            voir plus loin la fonction flock()
...

    Voici quelques types définis dans le fichier <limits.h> :
 
...
#ifdef  __USE_POSIX
    /* POSIX adds things to <limits.h>.  */
    # include <bits/posix1_lim.h>
#endif
...
/* Number of bits in a `char'.  */
#  define CHAR_BIT      8
/* Minimum and maximum values a `signed char' can hold.  */
#  define SCHAR_MIN     (-128)
#  define SCHAR_MAX     127
/* Maximum value an `unsigned char' can hold.  (Minimum is 0.)  */
#  define UCHAR_MAX     255
/* Minimum and maximum values a `char' can hold.  */
#  ifdef __CHAR_UNSIGNED__
#   define CHAR_MIN     0
#   define CHAR_MAX     UCHAR_MAX
#  else
#   define CHAR_MIN     SCHAR_MIN
#   define CHAR_MAX     SCHAR_MAX
#  endif
/* Minimum and maximum values a `signed short int' can hold.  */
#  define SHRT_MIN      (-32768)
#  define SHRT_MAX      32767
....
/* Minimum and maximum values a `signed int' can hold.  */
#  define INT_MIN       (-INT_MAX - 1)
#  define INT_MAX       2147483647

     Le fichier <bits/posix1_lim.h> contient des constantes définies dans la norme Posix :
 
...
#define _POSIX_FD_SETSIZE       _POSIX_OPEN_MAX
/* Number of bytes in a filename.  */
#define _POSIX_NAME_MAX         14
/* Number of bytes in a pathname.  */
#define _POSIX_PATH_MAX         255
#define SSIZE_MAX               INTMAX
...

     Le fichier <bits/stdio_lim.h> contient quelques constantes concernant les fichiers :
 
...
# define L_tmpnam 20
# define TMP_MAX 238328
# define FILENAME_MAX 4095
...
#if defined __need_FOPEN_MAX || defined _STDIO_H
# undef  FOPEN_MAX
# define FOPEN_MAX 256
...

    Tous les codes d'erreurs sont déclarés dans le fichier <asm/errno.h> :
 
#ifndef _I386_ERRNO_H
#define _I386_ERRNO_H
#define EPERM            1      /* Operation not permitted */
#define ENOENT           2      /* No such file or directory */
#define ESRCH            3      /* No such process */
#define EINTR            4      /* Interrupted system call */
#define EIO              5      /* I/O error */
...
...
#define ENOTNAM         118     /* Not a XENIX named type file */
#define ENAVAIL         119     /* No XENIX semaphores available */
#define EISNAM          120     /* Is a named type file */
#define EREMOTEIO       121     /* Remote I/O error */
#define EDQUOT          122     /* Quota exceeded */
#define ENOMEDIUM       123     /* No medium found */
#define EMEDIUMTYPE     124     /* Wrong medium type */
#endif

    Il est souvent nécessaire de s'y référer pour trouver la signification d'une erreur système lorsque seule la valeur numérique de  la variable errno est connue. Rappelons encore que errno est accessible par le fichier /usr/include/g++-2/cerrno, qui inclut le fichier /usr/include/errno.h, qui inclut lui-même /usr/include/asm/errno.h.

Sommaire

Protection des fichiers en lecture/écriture.

    Il existe trois fonctions qui permettent de protéger les opérations d'entrées/sorties sur fichiers : flock(2), fcntl(2) et lockf(3), cette dernière n'étant pas une fonction système mais une fonction C. Ces protections sont des exclusions mutuelles, permettant une concurrence entre différents processus de la même machine. La concurrence entre plusieurs threads au sein du même processus ne sera pas abordée ici. La concurrence entre plusieurs processus sur des systèmes distincts (systèmes répartis) n'est possible qu'au moyen des fonctions flock(2) et lockf(3) (cette dernière utilise probablement la fonction fcntl()).

    Les mécanismes mis en place par ces trois fonctions sont semblables. Cependant il est extrêmement important de noter que les opérations de (dé)verrouillage des fichiers mis en place par les fonctions fcntl(2) et lockf(3) sont compatibles entre eux (un fichier verrouillé par la fonction fcntl() peut être déverrouillé par la fonction lockf(3), et inversement). En revanche, les opérations réalisées au moyen de la fonction flock(2) sont totalement ignorées par la fonction fcntl(2) et inversement. Ces mécanismes de protection/exclusion mutuelle (advisory lock) ne sont actifs qu'entre processus qui les utilisent : s'il n'utilise pas ces fonctions, un processus peut lire/écrire sans gène un fichier par ailleurs verrouillé.

    Avant de présenter les détails des différentes fonctions, il faut rappeler le principe des " lecteurs-rédacteurs" : lorsqu'un lecteur lit une information, il ne la modifie pas, donc un nombre quelconque de lecteurs peut être autorisé à lire simultanément la même information. Au contraire un rédacteur, modifiant l'information, ne peut être autorisé à écrire que si aucun autre rédacteur n'est déjà en train de la modifier lui-même, et si aucun lecteur n'est en train de la consulter. C'est exactement à ce schéma que doit correspondre l'utilisation en lecture/écriture d'un fichier simultanément par plusieurs processus. Les différentes fonctions de dé/verrouillage des fichiers présentent donc des caractéristiques générales :

Remarque : lorsqu'un processus se termine sans avoir supprimé les verrous qu'il a installés sur un fichier, le système Unix le fait lui-même. Ces verrous sont aussi supprimés dès que le processus ferme n'importe lequel des descripteurs de fichier associés à ce fichier (par close(fd)).

Sommaire

Compatibilité Unix-Windows

    Plusieurs des fonctions étudiées ci-dessus existent aussi sous Windows, et sont par exemple accessibles en Visual C++, avec un comportement analogue, parfois très simplifié. Il s'agit :     La fonction Visual C++ LockFile() est à peu près équivalente à la fonction flock(3) d'Unix.
Sommaire

[1] Toutes les informations dans ce document sont conformes à l'installation RedHat Linux Release 6.0 - Kernel 2.2.15 sur un i686.

[2] En fait, le fichier <fcntl.h> sert de relais pour inclure de nombreux autres fichiers y compris  <sys/fcntl.h>

[3] Noter que le compilateur g++ est à peu près conforme à la norme C++. A ce titre, il interprète les directives d'inclusion de fichiers selon qu'ils ont ou non l'extension .h :

#include <cerrno>   est équivalent à   #include "/usr/include/g++-2/cerrno" et
#include <errno.h>  est équivalent à   #include "/usr/include/errno.h"

[4] en C standard, fichier /usr/include/errno.h

[5] en C standard, fichier /usr/include/limits.h

[6] lié au fichier, donc stocké dans son i-node.

[7] cu(1) est une commande permettant d’appeler un système distant (par téléphone)

[8] tous ces fichiers sont dans le nœud /usr/include/ qui est représenté par <>

© D. Mathieu     mathieu@romarin.univ-aix.fr
I.U.T.d'Aix en Provence - Département Informatique