comprendre et décoder la valeur du mode fichier à partir de la sortie de la fonction stat

j'ai essayé de comprendre ce qui se passe exactement dans le code mentionné ci-dessous. Mais je ne suis pas en mesure de le comprendre.

$mode = (stat($filename))[2];
printf "Permissions are %04on", $mode & 07777;

disons que ma valeur $mode est 33188

$mode & 07777 donne une valeur = 420

  • la valeur du mode $est-elle un nombre décimal ?

  • pourquoi nous choisissons 07777 et pourquoi nous faisons un bitwise et operation. Je ne suis pas en mesure de underand la logique dans ici.

20
demandé sur Greg Bacon 2013-02-24 23:26:58

2 réponses

le mode de votre question correspond à un fichier régulier avec 644 permissions (read-write pour le propriétaire et read-only pour tous les autres), mais ne me croyez pas sur parole.

$ touch foo
$ chmod 644 foo
$ perl -le 'print +(stat "foo")[2]'
33188

La valeur $mode être considéré comme un nombre décimal, mais ce n'est pas particulièrement éclairante. Voir la représentation octale donne quelque chose d'un peu plus familier.

$ perl -e 'printf "%o\n", (stat "foo")[2]'
100644

Bitwise et avec 07777 donne les douze derniers bits d'un nombre représentation binaire. Avec un mode Unix, cette opération donne la permission ou le mode bits et écarte toute information de type.

$ perl -e 'printf "%d\n", (stat "foo")[2] & 07777'  # decimal, not useful
420
$ perl -e 'printf "%o\n", (stat "foo")[2] & 07777'  # octal, eureka!
644

une meilleure façon de faire ceci est ci-dessous. Lire la suite pour tous les détails.


Mode Bits

Le troisième élément retourné à partir de stat (ce qui correspond à st_modestruct stat) est un champ de bits les différentes positions de bits binaires drapeaux.

par exemple, un peu dans st_mode POSIX names S_IWUSR. Un fichier ou un répertoire dont le mode a ce bit défini est accessible en écriture par son propriétaire. Un binaire est S_IROTH que lorsque l'ensemble des moyens autres utilisateurs ( i.e., ni le propriétaire ni le groupe) peuvent lire ce fichier ou répertoire particulier.

documentation perlfunc pour stat donne les noms des bits de mode couramment disponibles. Nous pouvons examiner leurs valeurs.

#! /usr/bin/env perl

use strict;
use warnings;
use Fcntl ':mode';

my $perldoc_f_stat = q(
  # Permissions: read, write, execute, for user, group, others.
  S_IRWXU S_IRUSR S_IWUSR S_IXUSR
  S_IRWXG S_IRGRP S_IWGRP S_IXGRP
  S_IRWXO S_IROTH S_IWOTH S_IXOTH

  # Setuid/Setgid/Stickiness/SaveText.
  # Note that the exact meaning of these is system dependent.
  S_ISUID S_ISGID S_ISVTX S_ISTXT

  # File types.  Not necessarily all are available on your system.
  S_IFREG S_IFDIR S_IFLNK S_IFBLK S_IFCHR S_IFIFO S_IFSOCK S_IFWHT S_ENFMT
);

my %mask;
foreach my $sym ($perldoc_f_stat =~ /\b(S_I\w+)\b/g) {
  my $val = eval { no strict 'refs'; &$sym() };
  if (defined $val) {
    $mask{$sym} = $val;
  }
  else {
    printf "%-10s - undefined\n", $sym;
  }
}

my @descending = sort { $mask{$b} <=> $mask{$a} } keys %mask;
printf "%-10s - %9o\n", $_, $mask{$_} for @descending;

sur Red Hat Enterprise Linux et autres systèmes d'exploitation dans la famille System V, la sortie du programme ci-dessus sera

S_ISTXT    - undefined
S_IFWHT    - undefined
S_IFSOCK   -    140000
S_IFLNK    -    120000
S_IFREG    -    100000
S_IFBLK    -     60000
S_IFDIR    -     40000
S_IFCHR    -     20000
S_IFIFO    -     10000
S_ISUID    -      4000
S_ISGID    -      2000
S_ISVTX    -      1000
S_IRWXU    -       700
S_IRUSR    -       400
S_IWUSR    -       200
S_IXUSR    -       100
S_IRWXG    -        70
S_IRGRP    -        40
S_IWGRP    -        20
S_IXGRP    -        10
S_IRWXO    -         7
S_IROTH    -         4
S_IWOTH    -         2
S_IXOTH    -         1

Bit tripoter

les nombres ci-dessus sont octal (base 8), donc n'importe quel chiffre donné doit être 0-7 et a la valeur de place 8 n, où n est le nombre à base zéro de places à gauche du point radix. Pour voir comment ils mappent en bits, octal a la propriété commode que chaque chiffre correspond à trois bits. Quatre, deux, et 1 sont tous puissance exacte de deux, donc en binaire, ils sont respectivement de 100, 10 et 1. Sept (= 4 + 2 + 1) en binaire est 111, donc alors 70 8 est 111000 2. Ce dernier exemple montre comment la conversion en arrière est simple.

Avec un champ de bits, vous ne vous souciez pas exactement la valeur d'un bit dans cette position, mais si elle est nulle ou non nulle, donc

if ($mode & $mask) {

vérifie si peu dans $mode correspondant à $mask est réglé. Pour un exemple simple, étant donné le 4-bit integer 1011 et un masque 0100, leur bitwise et est

  1011
& 0100
------
  0000

donc le morceau dans cette position est clair-par opposition à un masque de, disons, 0010 ou 1100.

Supprimer le bit le plus significatif de 1011 ressemble

    1011      1011
& ~(1000) = & 0111
            ------
              0011

Rappeler que l' ~ en Perl est complément bit à bit.

pour être complet, définissez un bit avec bitwise ou comme

$bits |= $mask;

Octal et les autorisations de fichier

la correspondance directe d'un chiffre octal à trois bits est pratique pour les permissions Unix car elles viennent en groupes de trois. Par exemple, les permissions pour le programme qui a produit la sortie ci-dessus sont

-rwxr-xr-x 1 gbacon users 1096 Feb 24 20:34 modebits

Qui est, le propriétaire peut lire, écrire, et exécuter; mais tout le monde peut lire et exécuter. En octal, c'est 755-un raccourci compact. En termes du tableau ci-dessus, les bits de jeu dans le mode

  • S_IRUSR
  • S_IWUSR
  • S_IXUSR
  • S_IRGRP
  • S_IXGRP
  • S_IROTH
  • S_IXOTH

Nous pouvons décomposer le mode de votre question, en ajoutant quelques lignes le programme ci-dessus.

my $mode = 33188;
print "\nBits set in mode $mode:\n";
foreach my $sym (@descending) {
    if (($mode & $mask{$sym}) == $mask{$sym}) {
        print "  - $sym\n";
        $mode &= ~$mask{$sym};
    }
}

printf "extra bits: %o\n", $mode if $mode;

le test de mode doit être plus prudent car certains masques sont en raccourci pour plusieurs bits. Test que nous obtenons le exact mask back évite les faux positifs quand certains bits sont mis mais pas tous.

la boucle efface également les bits de toutes les occurrences détectées de sorte qu'à la fin nous pouvons vérifier que nous avons pris en compte chaque bit. La sortie est

Bits set in mode 33188:
  - S_IFREG
  - S_IRUSR
  - S_IWUSR
  - S_IRGRP
  - S_IROTH

Aucun avertissement supplémentaire, de sorte que nous avons tout.

Que la magie 07777

conversion 7777 8 binaire donne 0b111_111_111_111. Rappelez-vous que 7 8 111 2, et quatre 7s correspondent à 4 × 3 uns. Ce masque est utile pour sélectionner les bits définis dans les douze derniers. En regardant en arrière les masques de bits que nous avons généré plus tôt

S_ISUID    -      4000
S_ISGID    -      2000
S_ISVTX    -      1000
S_IRWXU    -       700
S_IRWXG    -        70
S_IRWXO    -         7

nous voyons que les 9 derniers bits sont les permissions pour l'utilisateur, le groupe et l'autre. Les trois bits qui les précèdent sont le setuid, le setgroupid, et ce qu'on appelle parfois le sticky bit. Par exemple, le mode complet de sendmail sur mon système est -rwxr-sr-x ou 3428510. Le bitwise et travaille à

  (dec)      (oct)                (bin)
  34285     102755     1000010111101101
&  4095 = &   7777 = &     111111111111
-------   --------   ------------------
   1517 =     2755 =        10111101101

Le high bit dans le mode qui est éliminé est S_IFREG, l'indicateur que c'est un fichier régulier. Remarquez combien le mode exprimé en octal est plus clair par rapport à la même information en décimal ou binaire.

stat documentation mentionne une fonction utile.

... et l' S_IF* fonctions

S_IMODE($mode)

la partie de $mode contenant les bits de permission et les bits setuid/setgid/sticky

ext/Fcntl/Fcntl.xs, nous trouvons son implémentation et une constante familière sur la dernière ligne.

void
S_IMODE(...)
    PREINIT:
        dXSTARG;
        SV *mode;
    PPCODE:
        if (items > 0)
            mode = ST(0);
        else {
            mode = &PL_sv_undef;
            EXTEND(SP, 1);
        }
        PUSHu(SvUV(mode) & 07777);

Pour éviter la mauvaise pratique de des numéros de magie dans le code source, écrire

my $permissions = S_IMODE $mode;

en utilisant S_IMODE et d'autres fonctions disponibles dans le module Fcntl cachent également le niveau bas de bit twiddling et se concentre sur le niveau du domaine informations le programme se veut. La documentation continue

S_IFMT($mode)

la partie de $mode contenant le type de fichier qui peut être bits-anded avec (par exemple) S_IFREG ou avec les fonctions suivantes

# The operators -f, -d, -l, -b, -c, -p, and -S.
S_ISREG($mode) S_ISDIR($mode) S_ISLNK($mode)
S_ISBLK($mode) S_ISCHR($mode) S_ISFIFO($mode) S_ISSOCK($mode)

# No direct -X operator counterpart, but for the first one
# the -g operator is often equivalent.  The ENFMT stands for
# record flocking enforcement, a platform-dependent feature.
S_ISENFMT($mode) S_ISWHT($mode)

L'utilisation de ces constantes et fonctions rendra vos programmes plus clairs en exprimant plus directement votre intention.

27
répondu Greg Bacon 2014-01-07 17:14:21

C'est expliqué dans perldoc-F stat, ce qui est où je suppose que vous avez trouvé cet exemple:

Because the mode contains both the file type and its
permissions, you should mask off the file type portion and
(s)printf using a "%o" if you want to see the real permissions.

La sortie de printf "%04o", 4200644 quelles sont les permissions sur votre fichier. 420 est juste la représentation décimale du nombre octal 0644.

si vous essayez d'imprimer les nombres sous forme binaire, il est plus facile de voir:

perl -lwe 'printf "%016b\n", 33188'
1000000110100100
perl -lwe 'printf "%016b\n", 33188 & 07777'
0000000110100100

Comme vous le remarquerez, au niveau du bit and supprime le bit le plus à gauche dans le nombre ci-dessus, qui représente probablement le type de fichier, vous laissant seulement les permissions de fichier. Ce nombre 07777 est le nombre binaire:

perl -lwe 'printf "%016b\n", 07777'
0000111111111111

qui agit comme un" masque " dans le bitwise and. Puisque 1 & 1 = 1, et 0 & 1 = 0, cela signifie que tout bit qui n'est pas assorti d'un 1 en 07777 est défini à 0.

7
répondu TLP 2013-02-24 19:51:56