理解和从统计函数输出的文件模式值进行解码(understanding and decoding th

2019-07-20 11:32发布

我一直在试图了解究竟是什么在下文提到的代码发生。 但我不能够理解它。

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

比方说,我的$模式值33188

$模式&07777产生一个值= 420

  • 是$模式值的十进制数?

  • 为什么我们选择07777,为什么我们正在做位与操作。 我不能underand这里的逻辑。

Answer 1:

从你的问题的模式对应于具有644级权限(可读写的所有者和只读其他人)一个普通的文件,但不要把我的话。

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

的值$mode 可以被看作是一个十进制整数,但这样做没有特别的启发。 眼看着八进制表示给出了一些更熟悉。

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

位与,07777给出了一个数字的二进制表示的最后12位。 使用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

一个更好的方式来做到这一点的下方。 经过对所有的细节。


模式位

从返回的第三元件stat (其对应于st_modestruct 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企业版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,并且具有位值8 n,其中n是地方小数点左侧的从零开始编号。 看看他们是如何映射到位,八进制有每个数字对应的三位方便特性。 四,二,和1都是2的确切权力,所以在二进制,它们分别是100,10,1。 在二进制七(= 4 + 2 + 1)为111,所以然后70 图8是111000 2。 后者示例说明如何来回转换是直接的。

与位域,你不关心到底是什么在那个位置有点值,但它是否为零或非零,所以

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,紧凑的简写。 在上面的表而言,在模式设定位

  • 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

转换7777 8二进制给人0b111_111_111_111 。 回想一下,7 8为111 2,和四个7S对应于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或34285 10。 该位与工程以是

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

这被丢弃在模式最高位为S_IFREG ,它是一个普通文件的指示。 请注意,当与十进制或二进制相同的信息相比,八进制表示的模式是如何更清晰的。

stat资料中提到一个有用的功能。

...和S_IF*功能

S_IMODE($mode)
的部分$mode包含权限位和的setuid / setgid的/粘性位

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包含可被比特相与与(例如)文件类型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) 

使用这些常量和功能将让你的程序更清晰更直接地表达你的意图。



Answer 2:

它在解释的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", 4200644这是你的文件的权限。 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。



文章来源: understanding and decoding the file mode value from stat function output