File Coverage

blib/lib/Filesys/POSIX/Snapshot/Inode.pm
Criterion Covered Total %
statement 63 64 98.4
branch 18 18 100.0
condition 7 9 77.7
subroutine 12 12 100.0
pod 1 3 33.3
total 101 106 95.2


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::Snapshot::Inode;
9              
10 1     1   4 use strict;
  1         0  
  1         21  
11 1     1   3 use warnings;
  1         0  
  1         16  
12              
13 1     1   3 use Filesys::POSIX::Bits;
  1         1  
  1         211  
14 1     1   4 use Filesys::POSIX::Mem::Inode ();
  1         1  
  1         12  
15 1     1   3 use Filesys::POSIX::Mem::Directory ();
  1         1  
  1         8  
16 1     1   2 use Filesys::POSIX::Mem::Bucket ();
  1         2  
  1         14  
17              
18 1     1   3 use Filesys::POSIX::Error qw(throw);
  1         1  
  1         430  
19              
20             our @ISA = qw(Filesys::POSIX::Mem::Inode);
21              
22             sub from_inode {
23 46     46 0 52 my ( $class, $inode, %opts ) = @_;
24              
25 46         65 my $new_inode = bless {
26             'inode' => $inode,
27             'copied' => 0
28             }, $class;
29              
30 46         70 my @ATTRIBUTES = qw(
31             size atime mtime ctime uid gid mode dev rdev parent
32             );
33              
34 46         28 @{$new_inode}{@ATTRIBUTES} = @{$inode}{@ATTRIBUTES};
  46         144  
  46         70  
35              
36             #
37             # Overwrite any values within the current inode with ones optionally passed
38             # in %opts.
39             #
40 46         54 foreach my $attribute (@ATTRIBUTES) {
41             $new_inode->{$attribute} = $opts{$attribute}
42 460 100       551 if exists $opts{$attribute};
43             }
44              
45             #
46             # Copy the symlink destination from the given inode provided, if
47             # present.
48             #
49 46 100       75 if ( $inode->link ) {
50 1         3 $new_inode->{'dest'} = $inode->readlink;
51             }
52              
53             #
54             # If it was specified at mount time that we should snapshot directory
55             # listings immediately, then do so.
56             #
57 46 100 66     69 if ( $inode->dir && $opts{'dev'}->{'flags'}->{'immediate_dir_copy'} ) {
58 15         27 $new_inode->{'directory'} = $new_inode->_copy_dir( $inode->directory );
59             }
60              
61 46         102 return $new_inode;
62             }
63              
64             sub _copy_dir {
65 17     17   11 my ( $self, $directory ) = @_;
66              
67 17         24 my $new_directory = Filesys::POSIX::Mem::Directory->new;
68              
69 17         27 $directory->open;
70              
71 17         22 while ( defined( my $item = $directory->read ) ) {
72 77 100 100     204 next if $item eq '.' || $item eq '..';
73              
74 43         59 my $inode = $directory->get($item);
75              
76             $new_directory->set(
77             $item,
78             __PACKAGE__->from_inode(
79             $inode,
80             'parent' => $self,
81 43         62 'dev' => $self->{'dev'}
82             )
83             );
84             }
85              
86 17         25 $directory->close;
87              
88 17         19 return $new_directory;
89             }
90              
91             sub _copy_file {
92 1     1   7 my ($self) = @_;
93              
94 1         2 my $dev_flags = $self->{'dev'}->{'flags'};
95 1         2 my $inode_file = $self->{'inode'}->open($O_RDONLY);
96              
97             my $bucket = Filesys::POSIX::Mem::Bucket->new(
98             'inode' => $self,
99             'max' => $dev_flags->{'bucket_max'},
100 1         5 'dir' => $dev_flags->{'bucket_dir'}
101             );
102              
103 1         5 while ( my $len = $inode_file->read( my $buf, 4096 ) ) {
104 0         0 $bucket->write( $buf, $len );
105             }
106              
107 1         3 $inode_file->close;
108 1         2 $bucket->close;
109              
110 1         2 return $bucket;
111             }
112              
113             sub directory {
114 10     10 1 45 my ($self) = @_;
115              
116 10 100       17 throw &Errno::ENOTDIR unless $self->dir;
117              
118 9 100       16 unless ( $self->{'directory'} ) {
119 2         5 $self->{'directory'} = $self->_copy_dir( $self->{'inode'}->directory );
120             }
121              
122 9         11 return $self->{'directory'};
123             }
124              
125             sub open {
126 4     4 0 3 my ( $self, $flags ) = @_;
127              
128 4 100       8 unless ( $self->{'bucket'} ) {
129 2 100 66     7 if ( $self->file && $flags & ( $O_WRONLY | $O_RDWR ) ) {
130 1         3 $self->{'bucket'} = $self->_copy_file;
131             }
132             }
133              
134 4 100       5 if ( $self->{'bucket'} ) {
135 3         7 return $self->{'bucket'}->open($flags);
136             }
137              
138 1         4 return $self->{'inode'}->open($flags);
139             }
140              
141             1;