25

私は、以下のコードで何が起こっているのかを正確に理解しようとしています。しかし、私はそれを理解することができません。

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

私の$mode値が33188だとしましょう

$ mode&07777は値=420を生成します

  • $ mode値は10進数ですか?

  • 07777を選択する理由と、ビット単位の演算を実行する理由。私はここの論理を理解することができません。

4

2 に答える 2

34

あなたの質問のモードは、644の権限(所有者には読み取り/書き込み、他のすべての人には読み取り専用)を持つ通常のファイルに対応していますが、私の言葉を信じないでください。

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

の値は10進整数と見なすことが$mode できますが、そうすることは特に啓蒙的ではありません。8進数の表現を見ると、もう少し馴染みのあるものになります。

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

07777とのビットごとのANDは、数値の2進表現の最後の12ビットを示します。Unixモードでは、この操作は許可ビットまたはモードビットを与え、タイプ情報を破棄します。

$ perl -e'printf "%d \ n"、(stat "foo")[2]&07777'#10進数、役に立たない
420
$ perl -e'printf "%o \ n"、(stat "foo")[2]&07777'#8進数、eureka!
644

これを行うためのより良い方法は以下のとおりです。詳細については、以下をお読みください。


モードビット

から返される3番目の要素( instatに対応)は、さまざまなビット位置がバイナリフラグであるビットフィールドです。st_modestruct stat

たとえば、st_modePOSIX名の1ビット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 EnterpriseLinuxおよびSystemVファミリの他のオペレーティングシステムでは、上記のプログラムの出力は次のようになります。

S_ISTXT-未定義
S_IFWHT-未定義
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進数(基数8)であるため、任意の桁は0〜7であり、桁の値は8 nです。ここで、nは基数ポイントの左側のゼロベースの桁数です。それらがビットにどのようにマッピングされるかを確認するために、8進数には各桁が3ビットに対応するという便利な特性があります。4、2、および1はすべて2の正確な累乗であるため、2進数では、それぞれ100、10、および1になります。2進数の7(= 4 + 2 + 1)は111なので、708は1110002です。後者の例は、前後の変換がいかに簡単であるかを示しています。

ビットフィールドを使用すると、その位置にあるビットの値が正確に何であるかは関係ありませんが、それがゼロ非ゼロかは関係ありません。

if ($mode & $mask) {

$modeに対応するビットが設定されているかどうかをテストします$mask。簡単な例として、4ビット整数1011とマスク0100が与えられた場合、それらのビット単位のANDは次のようになります。

  1011
& 0100
------
  0000

したがって、たとえば0010または1100のマスクとは対照的に、その位置のビットは明確です。

1011の最上位ビットをクリアすると次のようになります

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

~Perlではビット単位の補集合であることを思い出してください。

完全を期すために、ビット単位のORでビットを設定します。

$bits |= $mask;

8進数とファイルのアクセス許可

8進数の3ビットへの直接マッピングは、3つのグループで提供されるため、Unixパーミッションに便利です。たとえば、上記の出力を生成したプログラムの権限は次のとおりです。

-rwxr-xr-x1gbaconユーザー10962月24日20:34モードビット

つまり、所有者は読み取り、書き込み、および実行を行うことができます。しかし、他の誰もが読んで実行することができます。8進数では、これは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;

一部のマスクは複数ビットの省略形であるため、モードテストはより注意する必要があります。正確なマスクが返されることをテストすることで、すべてではなく一部のビットが設定されている場合の誤検知を回避できます。

ループはまた、検出されたすべてのヒットからビットをクリアするため、最後に各ビットを考慮したことを確認できます。出力は

モード33188で設定されたビット:
  -S_IFREG
  -S_IRUSR
  -S_IWUSR
  -S_IRGRP
  -S_IROTH

追加の警告はないので、すべてを取得しました。

その魔法07777

7777 8をバイナリに変換すると、が得られ0b111_111_111_111ます。78は1112であり、 4つの7は4×3のものに対応することを思い出してください。このマスクは、最後の12のセットビットを選択するのに役立ちます。以前に生成したビットマスクを振り返って

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

最後の9ビットは、ユーザー、グループ、およびその他の権限であることがわかります。それらの前の3つのビットは、setuid、setgroupid、およびスティッキービットと呼ばれることもあります。たとえば、sendmail私のシステムのフルモードは-rwxr-sr-xまたは3428510です。ビットごとのANDは次のようになります

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

破棄されるモードの上位ビットS_IFREGは、通常のファイルであることを示すインジケーターです。10進数または2進数の同じ情報と比較すると、8進数で表現されたモードがどれほど明確であるかに注意してください。

statドキュメントには、役立つ機能が記載されています。

…そしてS_IF*機能は

S_IMODE($mode)
$modeパーミッションビットとsetuid/setgid/stickyビットを含む 部分

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_IMODEFcntlモジュールから利用可能なその他の関数を使用すると、低レベルのビットのいじりが隠され、プログラムが必要とするドメインレベルの情報に焦点が当てられます。ドキュメントは続きます

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)

これらの定数と関数を使用すると、意図をより直接的に表現することで、プログラムがより明確になります。

于 2013-02-25T04:08:13.513 に答える
7

これは、 perldoc -f statで説明されています。これは、この例を見つけたと思います。

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ファイルのアクセス許可です。4208進数の10進数表現です0644

数値を2進数で印刷しようとすると、次のことがわかりやすくなります。

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

お気づきのように、ビット単位andで上記の番号の左端のビットが削除されます。これはおそらくファイルタイプを表しており、ファイルのアクセス許可のみが残ります。この番号07777は2進数です。

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

これは、ビット単位で「マスク」として機能しandます。1&1 = 1、および0&1 = 0であるため、07777の1と一致しないビットはすべて0に設定されます。

于 2013-02-24T19:51:56.413 に答える