File Coverage

blib/lib/File/Rotate/Backup/Copy.pm
Criterion Covered Total %
statement 12 170 7.0
branch 0 66 0.0
condition 0 32 0.0
subroutine 4 20 20.0
pod 0 7 0.0
total 16 295 5.4


line stmt bran cond sub pod time code
1             # -*-perl-*-
2             # Creation date: 2003-04-12 22:43:55
3             # Authors: Don
4             # Change log:
5             # $Id: Copy.pm,v 1.10 2004/03/21 04:56:19 don Exp $
6              
7 1     1   6 use strict;
  1         1  
  1         54  
8              
9             { package File::Rotate::Backup::Copy;
10              
11 1     1   5 use vars qw($VERSION);
  1         2  
  1         87  
12             $VERSION = do { my @r=(q$Revision: 1.10 $=~/\d+/g); sprintf "%d."."%02d"x$#r,@r };
13              
14 1     1   6 use File::Spec;
  1         2  
  1         25  
15 1     1   5 use Fcntl ();
  1         1  
  1         2388  
16              
17             sub new {
18 0     0 0   my ($proto, $params) = @_;
19 0 0         $params = {} unless ref($params) eq 'HASH';
20 0   0       my $self = bless { _params => $params }, ref($proto) || $proto;
21 0           return $self;
22             }
23              
24             sub copy {
25 0     0 0   my ($self, $src, $dst) = @_;
26            
27 0 0 0       if (-l $src or -f $src) {
    0          
28 0           return $self->_copySymlinkOrFile($src, $dst);
29             } elsif (-d $src) {
30 0           return $self->_copyDirectoryRecursive($src, $dst);
31             }
32             }
33              
34             sub _copyDirectoryRecursive {
35 0     0     my ($self, $src, $dst) = @_;
36              
37 0           my ($src_vol, $src_dirs, $src_file) = File::Spec->splitpath($src);
38 0           my ($dst_vol, $dst_dirs, $dst_file) = File::Spec->splitpath($dst);
39              
40 0 0 0       if (-e $dst and -d $dst) {
41             # if dst is a directory, add file name to end of path
42 0           my $dir = File::Spec->catdir($dst_dirs, $dst_file);
43 0           $dst = File::Spec->catpath($dst_vol, $dir, $src_file);
44             }
45              
46 0           $self->_copyOneFile($src, $dst);
47              
48 0           my $cur_dir = File::Spec->curdir;
49 0           my $parent_dir = File::Spec->updir;
50 0           local(*DIR);
51              
52 0 0         opendir(DIR, $src) or return undef;
53 0 0         my @files = grep { $_ ne $cur_dir and $_ ne $parent_dir } readdir DIR;
  0            
54 0           closedir DIR;
55              
56 0           foreach my $file (@files) {
57 0           my $new_src_dir = File::Spec->catdir($src_dirs, $src_file);
58 0           my $src_path = File::Spec->catpath($src_vol, $new_src_dir, $file);
59 0           $self->copy($src_path, $dst);
60             }
61             }
62              
63             sub _copySymlinkOrFile {
64 0     0     my ($self, $src, $dst) = @_;
65 0           my ($src_vol, $src_dirs, $src_file) = File::Spec->splitpath($src);
66 0           my ($dst_vol, $dst_dirs, $dst_file) = File::Spec->splitpath($dst);
67              
68 0 0 0       if (-e $dst and -d $dst) {
69             # if dst is a directory, add file name to end of path
70 0           my $dir = File::Spec->catdir($dst_dirs, $dst_file);
71 0           $dst = File::Spec->catpath($dst_vol, $dir, $src_file);
72             }
73            
74             # FIXME: should handle $dst being a symlink
75              
76 0           $self->debugPrint(5, "src_path is $src_dirs, $src_file => $src\n");
77 0           $self->debugPrint(5, "dst_path is $dst_dirs, $dst_file => $dst\n");
78              
79 0           return $self->_copyOneFile($src, $dst);
80             }
81              
82             sub _copyOneFile {
83 0     0     my ($self, $src_path, $dst_path) = @_;
84              
85 0 0         if ($self->_isSameFile($src_path, $dst_path)) {
86 0           return 0;
87             }
88              
89             # find out what kind of file it is
90 0           my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
91             $atime,$mtime,$ctime,$blksize,$blocks)
92             = stat($src_path);
93              
94 0           my $permissions = $mode & 07777;
95              
96 0           $self->debugPrint(1, "$src_path ==> $dst_path\n");
97 0 0         if (-l $src_path) {
    0          
    0          
    0          
    0          
    0          
    0          
98             # symlink
99 0           $self->debugPrint(9, "$src_path is a symbolic link\n");
100 0           my $link_content = readlink $src_path;
101 0 0         return undef unless symlink $link_content, $dst_path;
102             # FIXME: set up owner and group of symlink
103             } elsif (-f $src_path) {
104             # need the full path here instead of the _ filehandle
105             # because the -l does an lstat
106              
107             # plain file
108 0           my $size = -s _;
109 0           $self->debugPrint(9, "$src_path is a plain file - $size bytes\n");
110 0 0         $self->_copyPlainFile($src_path, $dst_path) or return undef;
111 0           $self->_fixOwnerPermissionsTimestamp($dst_path);
112             } elsif (-d _) {
113             # directory
114 0           $self->debugPrint(9, "$src_path is a directory\n");
115 0 0         return undef unless mkdir $dst_path, 0777;
116 0           $self->_fixOwnerPermissionsTimestamp($dst_path);
117             } elsif (-p _) {
118             # don't copy pipes, sockets, and other special files for now
119            
120             # named pipe
121 0           $self->debugPrint(9, "$src_path is a named pipe\n");
122             } elsif (-S _) {
123             # socket
124 0           $self->debugPrint(9, "$src_path is a socket\n");
125             } elsif (-b _) {
126             # block special file
127 0           $self->debugPrint(9, "$src_path is a block special file\n");
128             } elsif (-c _) {
129             # character special file
130 0           $self->debugPrint(9,"$src_path is a character special file\n");
131             }
132              
133 0           $self->debugPrint(9, sprintf("$src_path has permissions %o\n", $permissions));
134              
135 0           return 1;
136             }
137              
138             sub _isSameFile {
139 0     0     my ($self, $src_file, $dst_file) = @_;
140 0           my ($src_dev, $src_ino);
141 0           my ($dst_dev, $dst_ino);
142              
143 0 0 0       if (-l $src_file or -l $dst_file) {
144 0           ($src_dev, $src_ino) = (lstat($src_file))[0,1];
145 0           ($dst_dev, $dst_ino) = (lstat($dst_file))[0,1];
146             } else {
147 0           ($src_dev, $src_ino) = (stat($src_file))[0,1];
148 0           ($dst_dev, $dst_ino) = (stat($dst_file))[0,1];
149             }
150              
151 0 0 0       if ($src_dev == $dst_dev and $src_ino == $dst_ino) {
152 0           return 1;
153             }
154              
155 0           return 0;
156             }
157            
158             sub _fixOwnerPermissionsTimestamp {
159 0     0     my ($self, $dst_file) = @_;
160 0           my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
161             $atime,$mtime,$ctime,$blksize,$blocks)
162             = stat(_);
163              
164 0           my $permissions = $mode & 07777;
165              
166 0           chown $uid, $gid, $dst_file;
167 0           chmod $permissions, $dst_file;
168 0           utime $atime, $mtime, $dst_file;
169             }
170              
171             sub _copyPlainFile {
172 0     0     my ($self, $src_path, $dst_path) = @_;
173            
174 0           local(*IN);
175 0           local(*OUT);
176 0 0         open(IN, '<' . $src_path) or return undef;
177 0 0         unless (open(OUT, '>' . $dst_path)) {
178 0           close IN;
179 0           return undef;
180             }
181              
182             # just in case this ever runs on windoze
183 0           binmode IN, ':raw';
184 0           binmode OUT, ':raw';
185            
186 0           my $buf;
187 0           while (read(IN, $buf, 1024)) {
188 0           print OUT $buf;
189             }
190 0           close IN;
191 0           close OUT;
192              
193 0           return 1;
194             }
195              
196             sub remove {
197 0     0 0   my ($self, $victim) = @_;
198              
199 0           $self->debugPrint(9, "remove() - passed $victim\n");
200              
201 0 0 0       if (not -l $victim and -d $victim) {
202 0           return $self->_removeDirectoryRecursive($victim);
203             } else {
204 0           $self->debugPrint(1, "Removing $victim\n");
205 0           my $params = $self->_getParams;
206 0 0         if ($$params{use_flock}) {
207 0           local(*FILE);
208 0           open(FILE, '+<' . $victim);
209 0 0         unless (CORE::flock(FILE, &Fcntl::LOCK_EX() | &Fcntl::LOCK_NB)) {
210             # can't get lock
211 0           close FILE;
212 0           $self->debugPrint(1, "Could not get lock on $victim -- not removing\n");
213 0           return undef;
214             }
215 0           my $rv = unlink $victim;
216 0           CORE::flock(FILE, &Fcntl::LOCK_UN);
217 0           close FILE;
218 0 0 0       if (not $rv and $$params{use_rm}) {
219             # added for v0.08
220 0           $self->debugPrint(1, "unlink() failed -- using /bin/rm\n");
221 0           $rv = not system("/bin/rm", "-f", $victim);
222             }
223 0           return $rv;
224             } else {
225 0           my $rv = unlink $victim;
226 0 0 0       if (not $rv and $$params{use_rm}) {
227             # added for v0.08
228 0           $self->debugPrint(1, "unlink() failed -- using /bin/rm\n");
229 0           $rv = not system("/bin/rm", "-f", $victim);
230             }
231 0           return $rv;
232             }
233             }
234             }
235              
236             sub _removeDirectoryRecursive {
237 0     0     my ($self, $dir) = @_;
238              
239 0           $self->debugPrint(9, "_removeDirectoryRecursive() - passed $dir\n");
240            
241 0           local(*DIR);
242 0           my $cur_dir = File::Spec->curdir;
243 0           my $parent_dir = File::Spec->updir;
244              
245 0 0         opendir(DIR, $dir) or return undef;
246 0 0         my @files = grep { $_ ne $cur_dir and $_ ne $parent_dir } readdir DIR;
  0            
247 0           closedir DIR;
248              
249 0           my ($vol, $dirs, $dir_file) = File::Spec->splitpath($dir);
250 0           foreach my $file (@files) {
251 0           my $victim_dir = File::Spec->catdir($dirs, $dir_file);
252 0           my $victim_path = File::Spec->catpath($vol, $victim_dir, $file);
253 0           $self->debugPrint(9, "Trying to remove $victim_path\n");
254 0           $self->remove($victim_path);
255             }
256              
257 0           $self->debugPrint(1, "Removing directory $dir\n");
258 0           rmdir $dir;
259              
260 0           return 1;
261             }
262              
263             sub move {
264 0     0 0   my ($self, $src, $dst) = @_;
265              
266             # FIXME: implement
267             }
268              
269             # expects full path for $src and $dst
270             sub _move {
271 0     0     my ($self, $src, $dst) = @_;
272             # HERE
273              
274 0           my ($src_dev, $src_ino);
275 0           my ($dst_dev, $dst_ino);
276              
277 0 0 0       if (-l $src or -l $dst) {
278 0           ($src_dev, $src_ino) = (lstat($src))[0,1];
279 0           ($dst_dev, $dst_ino) = (lstat($dst))[0,1];
280             } else {
281 0           ($src_dev, $src_ino) = (stat($src))[0,1];
282 0           ($dst_dev, $dst_ino) = (stat($dst))[0,1];
283             }
284              
285 0 0         if ($src_dev == $dst_dev) {
286             # same filesystem, so we can just do a rename
287 0           rename $src, $dst;
288             } else {
289             # HERE
290             }
291             }
292              
293             sub debugOn {
294 0     0 0   my ($self, $fh, $level) = @_;
295 0           $$self{_debug} = 1;
296 0           $$self{_debug_level} = $level;
297 0           $$self{_debug_fh} = $fh;
298             }
299              
300             sub debugOff {
301 0     0 0   my ($self) = @_;
302 0           undef $$self{_debug};
303 0           undef $$self{_debug_fh};
304             }
305              
306             sub debugPrint {
307 0     0 0   my ($self, $level, $str) = @_;
308 0 0         return undef unless $$self{_debug};
309 0 0         return undef unless $$self{_debug_level} >= $level;
310            
311 0           my $fh = $$self{_debug_fh};
312 0           print $fh $str;
313             }
314              
315             sub _getParams {
316 0     0     my ($self) = @_;
317 0   0       return $$self{_params} || {};
318             }
319             }
320              
321             1;
322              
323             __END__