понимание и декодирование значения режима файла из вывода функции stat
Я пытался понять, что именно происходит в приведенном ниже коде. Но я не могу этого понять.
$mode = (stat($filename))[2];
printf "Permissions are %04on", $mode & 07777;
допустим, мое значение $ mode равно 33188
$mode & 07777 дает значение = 420
является ли значение $mode десятичным числом ?
почему мы выбираем 07777 и почему мы делаем побитовое и операцию. Я не в состоянии underand в логика здесь.
2 ответов
режим из вашего вопроса соответствует обычному файлу с 644 разрешениями (чтение-запись для владельца и только для чтения для всех остальных), но не верьте мне на слово.
$ touch foo $ chmod 644 foo $ perl -le 'print +(stat "foo")[2]' 33188
значение $mode
can рассматриваться как десятичное целое число, но это не особенно актуальным. Видение восьмеричного представления дает что-то более знакомое.
$ perl -e 'printf "%o\n", (stat "foo")[2]' 100644
побитовое и с 07777 дает последние двенадцать битов числа двоичное представление. В режиме Unix эта операция дает бит разрешения или режима и отбрасывает любую информацию о типе.
$ 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
более хороший способ сделать это ниже. Читайте дальше для всех деталей.
Mode Bits
третий элемент, возвращенный из stat
(что соответствует st_mode
на struct stat
) является битовое поле где различные разрядные позиции являются двоичными флагами.
например, один бит в st_mode
POSIX имена S_IWUSR
. Файл или каталог, режим которого этот бит доступен для записи своего владельца. Связанный бит S_IROTH
что при установке означает, что другие пользователи (то есть, ни владелец, ни в группе) не может читать этот конкретный файл или каталог.
на документация perlfunc для stat
дает имена общедоступных битов режима. Мы можем исследовать их ценности.
#! /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;
на Red Hat Enterprise Linux и других операционные системы в семействе System V, выход вышеуказанной программы будет
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
бит сложа
цифры выше восьмеричные (база 8), поэтому любая цифра должна быть 0-7 и имеет значение места 8n, где n - это нулевое число мест слева от точки радиуса. Чтобы увидеть, как они сопоставляются с битами, octal имеет удобное свойство, что каждая цифра соответствует трем битам. Четыре, два, и 1 все точные степени двух, поэтому в двоичном формате они равны 100, 10 и 1 соответственно. Семь (= 4 + 2 + 1) в двоичном - 111, так что 708 это 1110002. Последний пример показывает, как преобразование назад и вперед просто.
С битовым полем, вам все равно точно что значение бита в этой позиции-но ли это ноль или не ноль, так
if ($mode & $mask) {
проверяет, есть ли бит в $mode
соответствует $mask
установлен. Для простого примера, учитывая 4-разрядное целое число 1011 и маску 0100, их побитовое и равно
1011
& 0100
------
0000
таким образом, бит в этой позиции ясен-в отличие от маски, скажем, 0010 или 1100.
очистка самого значительного бита 1011 выглядит как
1011 1011
& ~(1000) = & 0111
------
0011
Напомним, что ~
в Perl является побитовым дополнением.
для полноты установите бит с побитовым или как в
$bits |= $mask;
восьмеричной и права доступа к файлам
прямое сопоставление восьмеричной цифры с тремя битами удобно для разрешений Unix, потому что они входят в группы по три. Например, разрешения для программы, которая произвела вывод выше, являются
-rwxr-xr-x 1 gbacon users 1096 Feb 24 20:34 modebits
то есть, владелец может читать, писать и выполнять, но и все остальные могут читать и исполнять. В восьмеричном это 755-компактная стенография. С точки зрения приведенной выше таблицы, набор бит в режиме are
S_IRUSR
S_IWUSR
S_IXUSR
S_IRGRP
S_IXGRP
S_IROTH
S_IXOTH
мы можем разложить режим из вашего вопроса, добавив несколько строк в программе.
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;
тест режима должен быть более осторожным, потому что некоторые из масок являются сокращением для нескольких битов. Проверка, что мы получаем точная маска назад избегает ложных срабатываний, когда некоторые из битов установлены, но не все.
цикл также очищает биты от всех обнаруженных попаданий, поэтому в конце мы можем проверить, что мы учитывали каждый бит. Выход
Bits set in mode 33188: - S_IFREG - S_IRUSR - S_IWUSR - S_IRGRP - S_IROTH
без дополнительного предупреждения, поэтому мы получили все.
эта магия 07777
преобразование 77778 двоичным дает 0b111_111_111_111
. Напомним, что 78 is 1112, а четыре 7-ки соответствуют 4×3 единицы. Эта маска полезна для выбора набора битов в последних двенадцати. Оглядываясь на битовые маски, которые мы создали ранее
S_ISUID - 4000 S_ISGID - 2000 S_ISVTX - 1000 S_IRWXU - 700 S_IRWXG - 70 S_IRWXO - 7
мы видим, что последние 9 биты разрешений для пользователя, группы и прочих. Три бита, предшествующие этим, - setuid, setgroupid и то, что иногда называют липким битом. Например, полный режим sendmail
в моей системе есть -rwxr-sr-x
или 3428510. Побитовое и работает до будь
(dec) (oct) (bin)
34285 102755 1000010111101101
& 4095 = & 7777 = & 111111111111
------- -------- ------------------
1517 = 2755 = 10111101101
высокий бит в режиме, который отбрасывается S_IFREG
индикатор, что это обычный файл. Обратите внимание, насколько четче режим, выраженный в восьмеричном, по сравнению с той же информацией в десятичном или двоичном формате.
на stat
документация упоминает полезную функцию.
... и
S_IF*
функции
S_IMODE($mode)
часть$mode
содержит биты разрешений и setuid/setgid / sticky bits
на ext/Fcntl/Fcntl.xs
, мы находим его реализацию и знакомую константу на последней строке.
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);
чтобы избежать плохой практики магия чисел в исходном коде написать
my $permissions = S_IMODE $mode;
используя S_IMODE
и другие функции, доступные из модуля Fcntl, также скрывают низкоуровневый бит и фокусируются на информации на уровне домена программа хочет. Документация продолжается
S_IFMT($mode)
часть$mode
содержащий тип файла, который может быть бит-anded с (например)S_IFREG
или со следующими функциями# 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)
использование этих констант и функций сделает ваши программы более ясными, более прямо выражая ваше намерение.
это объясняется в perldoc -F в стат, где, я полагаю, вы нашли такой пример:
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.
выход printf "%04o", 420
is 0644
это разрешения на ваш файл. 420
это просто десятичное представление восьмеричного числа 0644
.
если вы попытаетесь напечатать числа в двоичной форме, то будет легче увидеть:
perl -lwe 'printf "%016b\n", 33188'
1000000110100100
perl -lwe 'printf "%016b\n", 33188 & 07777'
0000000110100100
как вы заметите, побитовый and
удаляет самый левый бит в числе, которых предположительно представляет тип файла, оставляя вам только права доступа к файлам. Это число 07777 является двоичным числом:
perl -lwe 'printf "%016b\n", 07777'
0000111111111111
, который действует как "маска" в побитового and
. Поскольку 1 & 1 = 1 и 0 & 1 = 0, это означает, что любой бит, который не соответствует 1 в 07777, установлен в 0.