File Coverage

blib/lib/Filesys/Ext2.pm
Criterion Covered Total %
statement 56 92 60.8
branch 15 44 34.0
condition 0 17 0.0
subroutine 10 15 66.6
pod 5 5 100.0
total 86 173 49.7


line stmt bran cond sub pod time code
1             package Filesys::Ext2;
2             require 5;
3 2     2   3528 use strict;
  2         5  
  2         109  
4 2     2   14 use vars q($VERSION);
  2         3  
  2         143  
5 2     2   3775 use IO::Handle;
  2         29276  
  2         99  
6 2     2   9359 use IO::Select;
  2         12383  
  2         164  
7 2     2   2270 use IPC::Open3;
  2         8462  
  2         271  
8             $VERSION = 0.20;
9             local($_);
10              
11             #XXX You may want to change this default if you installed
12             #XXX e2fsprogs in a non-standard location
13             local $ENV{PATH} = '/usr/bin/';
14              
15             my %attr = (
16             s => 0x00000001, u => 0x00000002, c => 0x00000004, S => 0x00000008,
17             i => 0x00000010, a => 0x00000020, d => 0x00000040, A => 0x00000080,
18             Z => 0x00000100, X => 0x00000400, E => 0x00000800,
19             I => 0x00001000, j => 0x00004000, t => 0x00008000,
20             D => 0x00010000, T => 0x00020000,
21             );
22              
23             sub import{
24 2     2   19 no strict 'refs';
  2         5  
  2         2841  
25 3     3   242 my $caller = caller(1);
26 3         6 shift;
27            
28 3 100       8 @_ = map { $_ eq ':all' ? qw(chattr lsattr stat lstat calcSymMask) : $_ } @_;
  3         35  
29 3         10964 foreach( @_ ){
30 11 100       22 if(ref($_) eq 'HASH'){
31 1 50       6 if(exists($_->{PATH})){
32 1         27 $ENV{PATH} = $_->{PATH};
33             }
34             }
35             else{
36 10         14 *{$caller."::$_"} = \&{'Filesys::Ext2::'.$_};
  10         2397  
  10         28  
37             }
38             }
39             }
40              
41             sub chattr($$;@){
42 1     1 1 9 my($mask, @files) = @_;
43 16         24 my @mask = $mask =~ /^\d+/ ?
44 1 50       10 '=' . join('', grep { y/+// } _calcSymMask($mask)) :
45             split(/\s+|(?=[+-=])/, $mask);
46              
47 1         8 my($R, $E) = _multi("chattr", @mask, @files);
48              
49 1 50       0 die($E) if $?;
50 0         0 return 0;
51             }
52              
53             sub lsattr(@){
54 0 0   0 1 0 my $dir = '-d' if grep { -d } @_;
  0         0  
55              
56 0         0 my %attr;
57              
58             #Skip things that we know lsattr will croak on
59 0 0 0     0 $attr{$_} = undef for grep{ -l || ! (-d || -f || -r) } @_;
  0         0  
60 0 0 0     0 my($R, $E) = _multi("lsattr", $dir, grep { (-d || -f || -r) && ! -l } @_);
  0   0     0  
61              
62 0 0       0 die($E) if($?);
63              
64 0         0 foreach( split(/\n\r?|\r/, $R) ){
65 0         0 my($val, $key) = split(/\s+/, $_, 2);
66 0   0     0 $attr{$key} = (_calcBitMask($val)||"0 but true");
67             }
68              
69 0         0 my @mask = @attr{@_};
70              
71 0 0       0 return wantarray ? @mask : $mask[0];
72             }
73              
74             sub stat($) {
75 0     0 1 0 my $lsattr = eval { lsattr($_[0]) };
  0         0  
76 0         0 my $stat = CORE::stat($_[0]);
77 0 0       0 if( $@ ){
78 0 0       0 return wantarray ? () : 0;
79             }
80             else{
81 0 0 0     0 return wantarray ? (CORE::stat(_), $lsattr) : $stat && ($@ ? 0 : 1);
82             }
83             }
84            
85             sub lstat($) {
86 0     0 1 0 my $lsattr = eval { lsattr($_[0]) };
  0         0  
87 0         0 my $stat = CORE::lstat($_[0]);
88 0 0       0 if( $@ ){
89 0 0       0 return wantarray ? () : 0;
90             }
91             else{
92 0 0 0     0 return wantarray ? (CORE::stat(_), $lsattr) : $stat && ($@ ? 0 : 1);
93             }
94             }
95            
96             sub calcSymMask($) {
97 0     0 1 0 my @F = _calcSymMask($_[0]);
98 0 0       0 return @F if wantarray;
99            
100 0         0 $_ = join('', @F);
101 0         0 y/+//d;
102 0         0 s/(?<=-)[sucSiadAZXEIjtDT]//g;
103 0         0 return $_;
104             }
105              
106             sub _multi{
107 1     1   3 my($WFH, $RFH, $EFH, $ERR, $OUT);
108              
109             #XXX splice to limited no. of files at a time?
110 1         10 my $pid = open3(
111             $WFH = new IO::Handle,
112             $RFH = new IO::Handle,
113             $EFH = new IO::Handle,
114             @_
115             );
116              
117 1         4727 $_->autoflush() for $RFH, $EFH;
118              
119 1         192 my $selector = IO::Select->new();
120 1         24 $selector->add($RFH, $EFH);
121 1         85 while( my @ready = $selector->can_read ){
122 2         3293 foreach my $fh ( @ready ){
123 3 100       29 if( fileno($fh) == fileno($RFH) ){
124 1         8 my $ret = $RFH->sysread($_, 1024);
125 1         12 $OUT .= $_;
126 1 50       56 $selector->remove($fh) unless $ret;
127             }
128 3 100       49 if( fileno($fh) == fileno($EFH) ){
129 2         18 my $ret = $EFH->sysread($_, 1024);
130 2         29 $ERR .= $_;
131 2 100       12 $selector->remove($fh) unless $ret;
132             }
133             }
134             }
135            
136 1         63 waitpid $pid, 0;
137            
138 1         88 return $OUT, $ERR;
139             }
140              
141             sub _calcBitMask($) {
142 0     0   0 my $bitmask;
143 0         0 while ( my($key, $val) = each(%attr) ){
144 0         0 $bitmask += (index($_[0], $key)>=0) * $val;
145             }
146 0         0 return $bitmask;
147             }
148              
149             sub _calcSymMask($) {
150 1     1   2 my @mask;
151 1         12 foreach ( sort { $attr{$a} <=> $attr{$b} } keys %attr ){
  46         56  
152 16 50       47 push @mask, ($_[0] & $attr{$_} ? "+$_" : "-$_");
153             }
154 1         8 return @mask;
155             }
156              
157             1;
158             __END__