| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package Sys::Export; |
|
2
|
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
our $VERSION = '0.003'; # VERSION |
|
4
|
|
|
|
|
|
|
# ABSTRACT: Export a subset of an OS file tree, for chroot/initrd |
|
5
|
|
|
|
|
|
|
|
|
6
|
9
|
|
|
9
|
|
754994
|
use v5.26; |
|
|
9
|
|
|
|
|
39
|
|
|
7
|
9
|
|
|
9
|
|
50
|
use warnings; |
|
|
9
|
|
|
|
|
22
|
|
|
|
9
|
|
|
|
|
634
|
|
|
8
|
9
|
|
|
9
|
|
76
|
use experimental qw( signatures ); |
|
|
9
|
|
|
|
|
20
|
|
|
|
9
|
|
|
|
|
60
|
|
|
9
|
9
|
|
|
9
|
|
1620
|
use Carp; |
|
|
9
|
|
|
|
|
27
|
|
|
|
9
|
|
|
|
|
798
|
|
|
10
|
9
|
|
|
9
|
|
75
|
use Scalar::Util qw( blessed looks_like_number ); |
|
|
9
|
|
|
|
|
21
|
|
|
|
9
|
|
|
|
|
510
|
|
|
11
|
9
|
|
|
9
|
|
52
|
use Exporter (); |
|
|
9
|
|
|
|
|
17
|
|
|
|
9
|
|
|
|
|
2150
|
|
|
12
|
|
|
|
|
|
|
BEGIN { |
|
13
|
|
|
|
|
|
|
# Fcntl happily exports macros that don't exist, then fails at runtime. |
|
14
|
|
|
|
|
|
|
# Replace non-existent test macros with 'false', and nonexistent modes with 0. |
|
15
|
9
|
|
|
9
|
|
87
|
require Fcntl; |
|
16
|
153
|
100
|
|
|
|
4865
|
eval { Fcntl->can($_)->($_ =~ /_IS/? (0) : ()); 1 }? Fcntl->import($_) : eval "sub $_ { 0 }" |
|
|
135
|
|
|
|
|
33105
|
|
|
17
|
9
|
100
|
|
4
|
0
|
62
|
for qw( S_ISREG S_ISDIR S_ISLNK S_ISBLK S_ISCHR S_ISFIFO S_ISSOCK S_ISWHT |
|
|
4
|
|
|
0
|
0
|
48
|
|
|
|
0
|
|
|
|
|
0
|
|
|
18
|
|
|
|
|
|
|
S_IFREG S_IFDIR S_IFLNK S_IFBLK S_IFCHR S_IFIFO S_IFSOCK S_IFWHT S_IFMT ); |
|
19
|
|
|
|
|
|
|
} |
|
20
|
|
|
|
|
|
|
our @EXPORT_OK= qw( |
|
21
|
|
|
|
|
|
|
isa_exporter isa_export_dst isa_userdb isa_user isa_group exporter isa_hash isa_array isa_int |
|
22
|
|
|
|
|
|
|
add skip find finish rewrite_path rewrite_user rewrite_group expand_stat_shorthand |
|
23
|
|
|
|
|
|
|
S_ISREG S_ISDIR S_ISLNK S_ISBLK S_ISCHR S_ISFIFO S_ISSOCK S_ISWHT |
|
24
|
|
|
|
|
|
|
S_IFREG S_IFDIR S_IFLNK S_IFBLK S_IFCHR S_IFIFO S_IFSOCK S_IFWHT S_IFMT |
|
25
|
|
|
|
|
|
|
); |
|
26
|
|
|
|
|
|
|
our %EXPORT_TAGS= ( |
|
27
|
|
|
|
|
|
|
basic_methods => [qw( exporter add skip find finish rewrite_path rewrite_user rewrite_group )], |
|
28
|
|
|
|
|
|
|
isa => [qw( isa_exporter isa_export_dst isa_userdb isa_user isa_group isa_hash isa_array isa_int )], |
|
29
|
|
|
|
|
|
|
stat_modes => [qw( S_IFREG S_IFDIR S_IFLNK S_IFBLK S_IFCHR S_IFIFO S_IFSOCK S_IFWHT S_IFMT )], |
|
30
|
|
|
|
|
|
|
stat_tests => [qw( S_ISREG S_ISDIR S_ISLNK S_ISBLK S_ISCHR S_ISFIFO S_ISSOCK S_ISWHT )], |
|
31
|
|
|
|
|
|
|
); |
|
32
|
|
|
|
|
|
|
my ($is_module_name, $require_module); |
|
33
|
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
# optional dependency on Module::Runtime. This way if there's any bug in my cheap |
|
35
|
|
|
|
|
|
|
# substitute, the fix is to just install the official module. |
|
36
|
|
|
|
|
|
|
if (eval { require Module::Runtime; }) { |
|
37
|
|
|
|
|
|
|
$is_module_name= \&Module::Runtime::is_module_name; |
|
38
|
|
|
|
|
|
|
$require_module= \&Module::Runtime::require_module; |
|
39
|
|
|
|
|
|
|
} else { |
|
40
|
|
|
|
|
|
|
$is_module_name= sub { $_[0] =~ /^[A-Z_a-z][0-9A-Z_a-z]*(?:::[0-9A-Z_a-z]+)*\z/ }; |
|
41
|
|
|
|
|
|
|
$require_module= sub { require( ($_[0] =~ s{::}{/}gr).'.pm' ) }; |
|
42
|
|
|
|
|
|
|
} |
|
43
|
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
sub import { |
|
46
|
15
|
|
|
15
|
|
58
|
my $class= $_[0]; |
|
47
|
15
|
|
|
|
|
40
|
my $caller= caller; |
|
48
|
15
|
|
|
|
|
31
|
my %ctor_opts; |
|
49
|
15
|
|
|
|
|
97
|
for (my $i= 1; $i < $#_; ++$i) { |
|
50
|
28
|
50
|
|
|
|
156
|
if (ref $_[$i] eq 'HASH') { |
|
|
|
100
|
|
|
|
|
|
|
51
|
0
|
|
|
|
|
0
|
%ctor_opts= ( %ctor_opts, %{ splice(@_, $i--, 1) } ); |
|
|
0
|
|
|
|
|
0
|
|
|
52
|
|
|
|
|
|
|
} |
|
53
|
|
|
|
|
|
|
elsif ($_[$i] =~ /^-(type|src|dst|tmp|src_userdb|dst_userdb|rewrite_path|rewrite_user|rewrite_group)\z/) { |
|
54
|
3
|
|
|
|
|
10
|
$ctor_opts{$1}= (splice @_, $i--, 2)[1]; |
|
55
|
|
|
|
|
|
|
} |
|
56
|
|
|
|
|
|
|
} |
|
57
|
15
|
100
|
|
|
|
91
|
if (keys %ctor_opts) { |
|
58
|
1
|
|
|
|
|
3
|
init_global_exporter(%ctor_opts); |
|
59
|
|
|
|
|
|
|
# caller requested the global exporter instance, so also include the standard methods |
|
60
|
|
|
|
|
|
|
# unless it looks like they were more selective about what to import. |
|
61
|
1
|
50
|
|
|
|
10
|
push @_, 'exporter', ':basic_methods' |
|
62
|
|
|
|
|
|
|
unless grep /^(add|:.*methods)\z/, @_; |
|
63
|
|
|
|
|
|
|
} |
|
64
|
15
|
|
|
|
|
186051
|
goto \&Exporter::import; |
|
65
|
|
|
|
|
|
|
} |
|
66
|
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
our $exporter; |
|
68
|
0
|
|
|
0
|
1
|
0
|
sub exporter { $exporter } |
|
69
|
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
our %osname_to_class= ( |
|
71
|
|
|
|
|
|
|
linux => 'Linux', |
|
72
|
|
|
|
|
|
|
); |
|
73
|
|
|
|
|
|
|
|
|
74
|
1
|
|
|
1
|
1
|
1
|
sub init_global_exporter(%config) { |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
6
|
|
|
75
|
1
|
|
33
|
|
|
3
|
my $type= delete $config{type} // $^O; |
|
76
|
|
|
|
|
|
|
# remap known OS names |
|
77
|
1
|
|
33
|
|
|
13
|
my $class= $osname_to_class{$type} // $type; |
|
78
|
|
|
|
|
|
|
# prefix bare names with namespace |
|
79
|
1
|
50
|
|
|
|
5
|
$class= "Sys::Export::$class" unless $class =~ /::/; |
|
80
|
1
|
50
|
|
|
|
2
|
$is_module_name->($class) or croak "Invalid module name '$class'"; |
|
81
|
|
|
|
|
|
|
# if it fails, die with 'croak' |
|
82
|
1
|
50
|
|
|
|
41
|
eval { $require_module->($class) } or croak "$@"; |
|
|
1
|
|
|
|
|
3
|
|
|
83
|
|
|
|
|
|
|
# now construct one |
|
84
|
1
|
|
|
|
|
6
|
$exporter= $class->new(%config); |
|
85
|
|
|
|
|
|
|
} |
|
86
|
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
|
|
88
|
3
|
|
|
3
|
1
|
17
|
sub add { $exporter->add(@_) } |
|
89
|
1
|
|
|
1
|
1
|
4
|
sub skip { $exporter->skip(@_) } |
|
90
|
1
|
|
|
1
|
1
|
8
|
sub find { $exporter->src_find(@_) } |
|
91
|
1
|
|
|
1
|
1
|
3
|
sub finish { $exporter->finish(@_) } |
|
92
|
0
|
|
|
0
|
1
|
0
|
sub rewrite_path { $exporter->rewrite_path(@_) } |
|
93
|
0
|
|
|
0
|
1
|
0
|
sub rewrite_user { $exporter->rewrite_user(@_) } |
|
94
|
0
|
|
|
0
|
1
|
0
|
sub rewrite_group { $exporter->rewrite_group(@_) } |
|
95
|
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
|
|
97
|
76
|
|
|
76
|
1
|
272
|
sub isa_hash :prototype($) { ref $_[0] eq 'HASH' } |
|
98
|
56
|
|
|
56
|
1
|
211
|
sub isa_array :prototype($) { ref $_[0] eq 'ARRAY' } |
|
99
|
25
|
50
|
|
25
|
1
|
128
|
sub isa_int :prototype($) { looks_like_number($_[0]) && int($_[0]) == $_[0] } |
|
100
|
0
|
0
|
|
0
|
1
|
0
|
sub isa_exporter :prototype($) { blessed($_[0]) && $_[0]->isa('Sys::Export::Exporter') } |
|
101
|
7
|
100
|
66
|
7
|
1
|
146
|
sub isa_export_dst :prototype($) { blessed($_[0]) && $_[0]->can('add') && $_[0]->can('finish') } |
|
102
|
11
|
100
|
66
|
11
|
1
|
79
|
sub isa_userdb :prototype($) { blessed($_[0]) && $_[0]->can('user') && $_[0]->can('group') } |
|
103
|
18
|
100
|
|
18
|
1
|
90
|
sub isa_user :prototype($) { blessed($_[0]) && $_[0]->isa('Sys::Export::Unix::UserDB::User') } |
|
104
|
18
|
100
|
|
18
|
1
|
103
|
sub isa_group :prototype($) { blessed($_[0]) && $_[0]->isa('Sys::Export::Unix::UserDB::Group') } |
|
105
|
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
|
|
107
|
4
|
|
|
4
|
|
8
|
sub _parse_major_minor_data($attrs, $data) { |
|
|
4
|
|
|
|
|
8
|
|
|
|
4
|
|
|
|
|
8
|
|
|
|
4
|
|
|
|
|
5
|
|
|
108
|
4
|
100
|
|
|
|
26
|
@{$attrs}{'rdev_major','rdev_minor'}= isa_array $data? @$data : split(/[,:]/, $data); |
|
|
4
|
|
|
|
|
17
|
|
|
109
|
|
|
|
|
|
|
} |
|
110
|
|
|
|
|
|
|
our %_mode_alias= ( |
|
111
|
|
|
|
|
|
|
file => [ S_IFREG, sub { 0666 & ~umask } ], |
|
112
|
|
|
|
|
|
|
dir => [ S_IFDIR, sub { 0777 & ~umask } ], |
|
113
|
|
|
|
|
|
|
sym => [ S_IFLNK, sub { 0777 } ], |
|
114
|
|
|
|
|
|
|
blk => [ S_IFBLK, sub { 0666 & ~umask }, \&_parse_major_minor_data, ], |
|
115
|
|
|
|
|
|
|
chr => [ S_IFCHR, sub { 0666 & ~umask }, \&_parse_major_minor_data, ], |
|
116
|
|
|
|
|
|
|
fifo => [ S_IFIFO, sub { 0666 & ~umask } ], |
|
117
|
|
|
|
|
|
|
sock => [ S_IFSOCK, sub { 0666 & ~umask } ], |
|
118
|
|
|
|
|
|
|
); |
|
119
|
|
|
|
|
|
|
our @_mode_by_int; |
|
120
|
|
|
|
|
|
|
$_mode_by_int[$_->[0]]= $_ for values %_mode_alias; |
|
121
|
|
|
|
|
|
|
$_mode_by_int[0]= undef; # don't map 0 to any mode |
|
122
|
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
sub expand_stat_shorthand { |
|
124
|
25
|
100
|
66
|
25
|
1
|
154603
|
@_= @{$_[0]} if @_ == 1 && isa_array $_[0]; |
|
|
16
|
|
|
|
|
69
|
|
|
125
|
25
|
100
|
100
|
|
|
91
|
my %attrs= @_ > 2 && isa_hash $_[-1]? %{ pop @_ } : (); |
|
|
9
|
|
|
|
|
26
|
|
|
126
|
25
|
|
|
|
|
61
|
my ($mode, $name, $data)= @_; |
|
127
|
25
|
|
|
|
|
52
|
my $mode_desc; |
|
128
|
25
|
50
|
|
|
|
51
|
if (isa_int $mode) { |
|
129
|
0
|
0
|
|
|
|
0
|
$mode_desc= $_mode_by_int[$mode & S_IFMT] |
|
130
|
|
|
|
|
|
|
or carp sprintf("Numeric mode %x doesn't match any known node types", $mode); |
|
131
|
|
|
|
|
|
|
} |
|
132
|
|
|
|
|
|
|
else { |
|
133
|
25
|
50
|
|
|
|
159
|
$mode =~ /^([a-z]+)([0-7]+)?\z/ |
|
134
|
|
|
|
|
|
|
or croak "Invalid mode '$mode': expected number, or prefix file/dir/sym/blk/chr/fifo/sock followed by octal permissions"; |
|
135
|
25
|
50
|
|
|
|
105
|
$mode_desc= $_mode_alias{$1} |
|
136
|
|
|
|
|
|
|
or croak "Unknown mode alias '$1'"; |
|
137
|
25
|
100
|
|
|
|
99
|
$mode= $mode_desc->[0] | (defined $2? oct($2) : $mode_desc->[1]->()); |
|
138
|
|
|
|
|
|
|
} |
|
139
|
25
|
|
|
|
|
65
|
$attrs{mode}= $mode; |
|
140
|
25
|
50
|
|
|
|
54
|
length $name or croak "Name must be nonzero length"; |
|
141
|
25
|
|
|
|
|
47
|
$attrs{name}= $name; |
|
142
|
25
|
100
|
|
|
|
78
|
if (defined $data) { |
|
143
|
16
|
100
|
66
|
|
|
75
|
if ($mode_desc && $mode_desc->[2]) { |
|
144
|
4
|
|
|
|
|
14
|
$mode_desc->[2]->(\%attrs, $data); |
|
145
|
|
|
|
|
|
|
} else { |
|
146
|
12
|
|
|
|
|
25
|
$attrs{data}= $data; |
|
147
|
|
|
|
|
|
|
} |
|
148
|
|
|
|
|
|
|
} |
|
149
|
25
|
|
|
|
|
175
|
return %attrs; |
|
150
|
|
|
|
|
|
|
} |
|
151
|
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
1; |
|
153
|
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
__END__ |