linux - texto - saber codificacion de un archivo
entendiendo y decodificando el valor del modo de archivo de la salida de la funciĆ³n stat (2)
He estado tratando de entender qué está sucediendo exactamente en el código mencionado a continuación. Pero no soy capaz de entenderlo.
$mode = (stat($filename))[2];
printf "Permissions are %04o/n", $mode & 07777;
Digamos que mi valor de $ modo es 33188
$ mode & 07777 produce un valor = 420
¿Es el valor $ mode un número decimal?
Por qué estamos eligiendo 07777 y por qué estamos haciendo un bitwise y operación. No soy capaz de entender la lógica aquí.
El modo de su pregunta corresponde a un archivo normal con 644 permisos (lectura-escritura para el propietario y solo lectura para todos los demás), pero no confíe en mi palabra.
$ touch foo $ chmod 644 foo $ perl -le ''print +(stat "foo")[2]'' 33188
El valor de $mode
puede verse como un entero decimal, pero hacerlo no es particularmente esclarecedor. Ver la representación octal da algo un poco más familiar.
$ perl -e ''printf "%o/n", (stat "foo")[2]'' 100644
Bitwise AND con 07777 da los últimos doce bits de la representación binaria de un número. Con un modo Unix, esta operación otorga los bits de permiso o modo y descarta cualquier tipo de información.
$ 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
A continuación se muestra una mejor manera de hacer esto. Siga leyendo para todos los detalles.
Bits de modo
El tercer elemento devuelto desde stat
(que corresponde a st_mode
en struct stat
) es un campo de bit donde las diferentes posiciones de bit son banderas binarias.
Por ejemplo, un bit en st_mode
POSIX se denomina S_IWUSR
. Un archivo o directorio cuyo modo tiene este conjunto de bits es grabable por su propietario. Un bit relacionado es S_IROTH
que cuando se establece significa que otros usuarios ( es decir , ni el propietario ni el grupo) pueden leer ese archivo o directorio en particular.
La documentación de perlfunc para stat
proporciona los nombres de los bits de modo comúnmente disponibles. Podemos examinar sus valores.
#! /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;
En Red Hat Enterprise Linux y otros sistemas operativos de la familia System V, la salida del programa anterior será
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
Twiddling poco
Los números anteriores son octales (base 8), por lo que cualquier dígito dado debe ser 0-7 y tiene un valor de posición 8 n , donde n es el número de lugares con base cero a la izquierda del punto radix. Para ver cómo se asignan a bits, octal tiene la propiedad conveniente de que cada dígito corresponde a tres bits. Cuatro, dos y 1 son todos los poderes exactos de dos, por lo que en binario, son 100, 10 y 1 respectivamente. Siete (= 4 + 2 + 1) en binario es 111, entonces 70 8 es 111000 2 . El último ejemplo muestra cómo convertir de un lado a otro es sencillo.
Con un campo de bits, no importa exactamente cuál es el valor de un bit en esa posición, pero si es cero o no es cero, entonces
if ($mode & $mask) {
comprueba si algún bit en $mode
correspondiente a $mask
está establecido. Para un ejemplo simple, dado el entero 1011 de 4 bits y una máscara 0100, su AND a nivel de bits es
1011
& 0100
------
0000
Así que el bit en esa posición es claro, a diferencia de una máscara de, digamos, 0010 o 1100.
Limpiando el bit más significativo de 1011 parece
1011 1011
& ~(1000) = & 0111
------
0011
Recordemos que ~
en Perl es un complemento bit a bit.
Para completar, establezca un bit con bitwise O como en
$bits |= $mask;
Permisos octales y de archivo
La asignación directa de un dígito octal a tres bits es conveniente para los permisos de Unix porque vienen en grupos de tres. Por ejemplo, los permisos para el programa que produjo la salida anterior son
-rwxr-xr-x 1 gbacon users 1096 Feb 24 20:34 modebits
Es decir, el propietario puede leer, escribir y ejecutar; pero todos los demás pueden leer y ejecutar. En octal, esto es 755, una taquigrafía compacta. En términos de la tabla anterior, los bits establecidos en el modo son
-
S_IRUSR
-
S_IWUSR
-
S_IXUSR
-
S_IRGRP
-
S_IXGRP
-
S_IROTH
-
S_IXOTH
Podemos descomponer el modo de su pregunta agregando algunas líneas al programa anterior.
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;
La prueba de modo debe ser más cuidadosa porque algunas de las máscaras son abreviadas para varios bits. La prueba de que obtenemos la máscara exacta de nuevo evita falsos positivos cuando se establecen algunos de los bits pero no todos.
El bucle también borra los bits de todos los aciertos detectados para que al final podamos verificar que hemos contabilizado cada bit. La salida es
Bits set in mode 33188: - S_IFREG - S_IRUSR - S_IWUSR - S_IRGRP - S_IROTH
Sin advertencia adicional, así que tenemos todo.
Esa magia 07777
La conversión de 7777 8 a binario da 0b111_111_111_111
. Recuerde que 7 8 es 111 2 , y cuatro 7s corresponden a 4 × 3 unos. Esta máscara es útil para seleccionar los bits establecidos en los últimos doce. Mirando hacia atrás a las máscaras de bits que generamos anteriormente.
S_ISUID - 4000 S_ISGID - 2000 S_ISVTX - 1000 S_IRWXU - 700 S_IRWXG - 70 S_IRWXO - 7
vemos que los últimos 9 bits son los permisos para usuario, grupo y otros. Los tres bits que preceden a estos son setuid, setgroupid y lo que a veces se denomina bit fijo. Por ejemplo, el modo completo de sendmail
en mi sistema es -rwxr-sr-x
o 34285 10 . El bit a bit y funciona para ser
(dec) (oct) (bin)
34285 102755 1000010111101101
& 4095 = & 7777 = & 111111111111
------- -------- ------------------
1517 = 2755 = 10111101101
El bit alto en el modo que se descarta es S_IFREG
, el indicador de que es un archivo normal. Observe cuánto más claro está el modo expresado en octal cuando se compara con la misma información en decimal o en binario.
La documentación stat
menciona una función útil.
… Y las funciones
S_IF*
son
S_IMODE($mode)
la parte de$mode
contiene los bits de permiso y los bits setuid / setgid / sticky
En ext/Fcntl/Fcntl.xs
, encontramos su implementación y una constante familiar en la última línea.
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);
Para evitar la mala práctica de los números mágicos en el código fuente, escriba
my $permissions = S_IMODE $mode;
El uso de S_IMODE
y otras funciones disponibles desde el módulo Fcntl también oculta la combinación de bits de bajo nivel y se centra en la información de nivel de dominio que el programa desea. La documentacion continua
S_IFMT($mode)
la parte de$mode
contiene el tipo de archivo que puede ser bit-anded con (por ejemplo)S_IFREG
o con las siguientes funciones
# 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)
El uso de estas constantes y funciones hará que sus programas sean más claros al expresar más directamente su intención.
Se explica en perldoc -f stat , que es donde supongo que encontró este ejemplo:
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 salida de printf "%04o", 420
es 0644
que corresponde a los permisos de su archivo. 420
es solo la representación decimal del número octal 0644
.
Si intenta imprimir los números en forma binaria, es más fácil ver:
perl -lwe ''printf "%016b/n", 33188''
1000000110100100
perl -lwe ''printf "%016b/n", 33188 & 07777''
0000000110100100
Como se dará cuenta, a nivel de bits and
elimina el bit situado más a la izquierda en el número anterior, que presumiblemente representa el tipo de archivo, dejándolo solo con los permisos de archivo. Este número 07777 es el número binario:
perl -lwe ''printf "%016b/n", 07777''
0000111111111111
Que actúa como una "máscara" en el bit a bit and
. Como 1 & 1 = 1, y 0 & 1 = 0, significa que cualquier bit que no coincida con un 1 en 07777 se establece en 0.