File Coverage

blib/lib/Filesys/POSIX/Real/Directory.pm
Criterion Covered Total %
statement 85 113 75.2
branch 23 42 54.7
condition 3 6 50.0
subroutine 18 21 85.7
pod 12 14 85.7
total 141 196 71.9


line stmt bran cond sub pod time code
1             # Copyright (c) 2014, cPanel, Inc.
2             # All rights reserved.
3             # http://cpanel.net/
4             #
5             # This is free software; you can redistribute it and/or modify it under the same
6             # terms as Perl itself. See the LICENSE file for further details.
7              
8             package Filesys::POSIX::Real::Directory;
9              
10 11     11   428 use strict;
  11         11  
  11         324  
11 11     11   38 use warnings;
  11         10  
  11         262  
12              
13 11     11   41 use Filesys::POSIX::Bits;
  11         12  
  11         2702  
14 11     11   55 use Filesys::POSIX::Directory ();
  11         11  
  11         160  
15              
16 11     11   36 use Errno qw(ENOENT);
  11         9  
  11         11491  
17              
18             our @ISA = qw(Filesys::POSIX::Directory);
19              
20             sub new {
21 19     19 0 1154 my ( $class, $path, $inode ) = @_;
22              
23 19 100       274 return bless {
24             'path' => $path,
25             'inode' => $inode,
26             'mtime' => 0,
27             'overlays' => {},
28             'skipped' => {},
29             'members' => {
30             '.' => $inode,
31             '..' => $inode->{'parent'} ? $inode->{'parent'} : $inode
32             }
33             }, $class;
34             }
35              
36             sub _sync_all {
37 1     1   1 my ($self) = @_;
38 1 50       19 my $mtime = ( lstat $self->{'path'} )[9] or Carp::confess("$!");
39              
40 1 50       4 return unless $mtime > $self->{'mtime'};
41              
42 1         2 $self->open;
43              
44 1         3 while ( defined( my $item = $self->read ) ) {
45 5         10 $self->_sync_member($item);
46             }
47              
48 1         3 $self->close;
49              
50 1         2 $self->{'mtime'} = $mtime;
51             }
52              
53             sub _sync_member {
54 35     35   41 my ( $self, $name ) = @_;
55 35         135 my $subpath = "$self->{'path'}/$name";
56 35         852 my @st = lstat $subpath;
57              
58 35 100 66     282 if ( scalar @st == 0 && $!{'ENOENT'} ) {
59 16         255 delete $self->{'members'}->{$name};
60 16         25 return;
61             }
62              
63 19 50       41 Carp::confess($!) unless @st;
64              
65 19 100       60 if ( exists $self->{'members'}->{$name} ) {
66 2         6 $self->{'members'}->{$name}->update(@st);
67             }
68             else {
69 17         114 $self->{'members'}->{$name} = Filesys::POSIX::Real::Inode->from_disk(
70             $subpath,
71             'st_info' => \@st,
72             'dev' => $self->{'inode'}->{'dev'},
73             'parent' => $self->{'inode'}
74             );
75             }
76             }
77              
78             sub get {
79 51     51 1 58 my ( $self, $name ) = @_;
80 51 100       171 return $self->{'overlays'}->{$name} if exists $self->{'overlays'}->{$name};
81              
82 29 100       103 $self->_sync_member($name) unless exists $self->{'members'}->{$name};
83 29         104 return $self->{'members'}->{$name};
84             }
85              
86             sub set {
87 9     9 1 15 my ( $self, $name, $inode ) = @_;
88 9         29 $self->{'overlays'}->{$name} = $inode;
89 9         29 return $inode;
90             }
91              
92             sub rename_member {
93 2     2 1 5 my ( $self, undef, $olddir, $oldname, $newname ) = @_;
94             return rename( $olddir->path . '/' . $oldname, $self->path . '/' . $newname )
95 2   33     7 && do {
96             $olddir->_sync_member($oldname);
97             $self->_sync_member($newname);
98             1;
99             };
100             }
101              
102             sub exists {
103 8     8 1 12 my ( $self, $name ) = @_;
104 8 50       34 return 1 if exists $self->{'overlays'}->{$name};
105              
106 8         37 $self->_sync_member($name);
107 8         40 return exists $self->{'members'}->{$name};
108             }
109              
110             sub delete {
111 0     0 1 0 my ( $self, $name ) = @_;
112              
113 0 0       0 if ( exists $self->{'overlays'}->{$name} ) {
114 0         0 my $inode = $self->{'overlays'}->{$name};
115 0         0 delete $self->{'overlays'}->{$name};
116 0         0 return $inode;
117             }
118              
119 0 0       0 my $member = $self->{'members'}->{$name} or return;
120 0         0 my $subpath = "$self->{'path'}/$name";
121              
122 0 0       0 if ( $member->dir ) {
123 0         0 rmdir($subpath);
124             }
125             else {
126 0         0 unlink($subpath);
127             }
128              
129 0 0       0 if ($!) {
130 0 0       0 Carp::confess($!) unless $!{'ENOENT'};
131             }
132              
133 0         0 my $now = time;
134 0         0 @{ $self->{'inode'} }{qw(mtime ctime)} = ( $now, $now );
  0         0  
135              
136 0         0 my $inode = $self->{'members'}->{$name};
137 0         0 delete $self->{'members'}->{$name};
138              
139 0         0 return $inode;
140             }
141              
142             sub detach {
143 1     1 1 1 my ( $self, $name ) = @_;
144              
145 1         5 foreach my $table (qw(overlays members)) {
146 2 100       8 next unless exists $self->{$table}->{$name};
147              
148 1         3 my $inode = $self->{$table}->{$name};
149 1         5 delete $self->{$table}->{$name};
150 1         3 return $inode;
151             }
152             }
153              
154             sub list {
155 1     1 1 7 my ( $self, $name ) = @_;
156 1         3 $self->_sync_all;
157              
158 1         6 my %union = ( %{ $self->{'members'} }, %{ $self->{'overlays'} } );
  1         4  
  1         4  
159              
160 1         5 return keys %union;
161             }
162              
163             sub count {
164 0     0 1 0 scalar( shift->list );
165             }
166              
167             sub open {
168 7     7 1 28 my ($self) = @_;
169              
170 7         340 @{ $self->{'skipped'} }{ keys %{ $self->{'overlays'} } } =
  7         19  
  7         50  
171 7         18 values %{ $self->{'overlays'} };
172              
173 7         33 $self->close;
174              
175 7 50       224 opendir( $self->{'dh'}, $self->{'path'} ) or Carp::confess("$!");
176              
177 7         81 return $self;
178             }
179              
180             sub rewind {
181 0     0 1 0 my ($self) = @_;
182              
183 0         0 @{ $self->{'skipped'} }{ keys %{ $self->{'overlays'} } } =
  0         0  
  0         0  
184 0         0 values %{ $self->{'overlays'} };
185              
186 0 0       0 if ( $self->{'dh'} ) {
187 0         0 rewinddir $self->{'dh'};
188             }
189              
190 0         0 return;
191             }
192              
193             sub read {
194 45     45 1 70 my ($self) = @_;
195 45         38 my $item;
196              
197 45 50       93 if ( $self->{'dh'} ) {
198 45         202 $item = readdir $self->{'dh'};
199             }
200              
201 45 100       73 if ( defined $item ) {
202 37         45 delete $self->{'skipped'}->{$item};
203             }
204             else {
205 8         9 $item = each %{ $self->{'skipped'} };
  8         17  
206             }
207              
208 45 50       70 if (wantarray) {
209 0         0 return ( $item, $self->get($item) );
210             }
211              
212 45         104 return $item;
213             }
214              
215             sub close {
216 13     13 1 16 my ($self) = @_;
217              
218 13 100       34 if ( $self->{'dh'} ) {
219 6         63 closedir $self->{'dh'};
220 6         21 delete $self->{'dh'};
221             }
222              
223 13         30 return;
224             }
225              
226             sub path {
227 4     4 0 9 my ($self) = @_;
228 4         77 return $self->{'path'};
229             }
230              
231             1;