File Coverage

blib/lib/Filesys/POSIX/VFS.pm
Criterion Covered Total %
statement 80 81 98.7
branch 22 22 100.0
condition 1 2 50.0
subroutine 12 12 100.0
pod 0 6 0.0
total 115 123 93.5


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::VFS;
9              
10 24     24   96 use strict;
  24         28  
  24         695  
11 24     24   83 use warnings;
  24         26  
  24         474  
12              
13 24     24   84 use Filesys::POSIX::Bits;
  24         26  
  24         7324  
14 24     24   120 use Filesys::POSIX::Path ();
  24         29  
  24         314  
15 24     24   8571 use Filesys::POSIX::VFS::Inode ();
  24         49  
  24         471  
16              
17 24     24   117 use Filesys::POSIX::Error qw(throw);
  24         26  
  24         15512  
18              
19             sub new {
20 39     39 0 397 return bless {
21             'mounts' => [],
22             'devices' => {},
23             'vnodes' => {}
24             },
25             shift;
26             }
27              
28             sub statfs {
29 16     16 0 108 my ( $self, $start, %opts ) = @_;
30 16         23 my $inode = $start;
31 16         12 my $ret;
32              
33 16         38 while ( $inode->{'vnode'} ) {
34 0         0 $inode = $inode->{'vnode'};
35             }
36              
37 16 100       29 if ( $opts{'exact'} ) {
38 10         22 $ret = $self->{'vnodes'}->{$inode};
39             }
40             else {
41 6         14 $ret = $self->{'devices'}->{ $inode->{'dev'} };
42             }
43              
44 16 100       35 unless ($ret) {
45 4 100       14 throw &Errno::ENXIO unless $opts{'silent'};
46             }
47              
48 15         40 return $ret;
49             }
50              
51             sub mountlist {
52 1     1 0 2 my ($self) = @_;
53 1         1 return @{ $self->{'mounts'} };
  1         4  
54             }
55              
56             #
57             # It should be noted that any usage of pathnames in this module are entirely
58             # symbolic and are not used for canonical purposes. The higher-level
59             # filesystem layer should take on the responsibility of providing both the
60             # canonically-correct absolute pathnames for mount points, and helping locate
61             # the appropriate VFS mount point for querying purposes.
62             #
63             sub mount {
64 50     50 0 165 my ( $self, $dev, $path, $mountpoint, %data ) = @_;
65              
66 50 100       72 if ( grep { $_->{'dev'} eq $dev } @{ $self->{'mounts'} } ) {
  17         61  
  50         256  
67 2         8 throw &Errno::EBUSY;
68             }
69              
70 48   50     249 $data{'special'} ||= scalar $dev;
71              
72             #
73             # Generate a generic BSD-style filesystem type string.
74             #
75 48         163 my $type = lc ref $dev;
76 48         338 $type =~ s/^([a-z_][a-z0-9_]*::)*//;
77              
78             #
79             # Create a vnode record munged from the mountpoint and new
80             # filesystem root.
81             #
82 48         329 my $vnode = Filesys::POSIX::VFS::Inode->new( $mountpoint, $dev->{'root'} );
83              
84             #
85             # Associate the mountpoint and filesystem roots with this vnode.
86             #
87 48         117 $mountpoint->{'vnode'} = $vnode;
88 48         97 $dev->{'root'}->{'vnode'} = $vnode;
89              
90             #
91             # Generate the mount record.
92             #
93 39         194 my $mount = {
94             'mountpoint' => $mountpoint,
95             'root' => $dev->{'root'},
96             'special' => $data{'special'},
97             'dev' => $dev,
98             'type' => $type,
99             'path' => $path,
100             'vnode' => $vnode,
101              
102 48         199 'flags' => { map { $_ => $data{$_} } grep { $_ ne 'special' } keys %data }
  87         263  
103             };
104              
105             #
106             # Store the mount record in the ordered mount list.
107             #
108 48         81 push @{ $self->{'mounts'} }, $mount;
  48         111  
109              
110             #
111             # Associate the vnode with the mount rcord.
112             #
113 48         145 $self->{'vnodes'}->{$vnode} = $mount;
114              
115             #
116             # Finally, associate the filesystem with the mount record.
117             #
118 48         132 $self->{'devices'}->{$dev} = $mount;
119              
120 48         160 return $self;
121             }
122              
123             sub vnode {
124 1756     1756 0 1616 my ( $self, $start ) = @_;
125 1756         1494 my $inode = $start;
126              
127 1756 100       2621 return undef unless $inode;
128              
129 1665         2813 while ( $inode->{'vnode'} ) {
130 216         377 $inode = $inode->{'vnode'};
131             }
132              
133 1665         2691 my $mount = $self->{'devices'}->{ $inode->{'dev'} };
134              
135 1665 100       2594 if ( $mount->{'flags'}->{'noexec'} ) {
136 9         11 $inode->{'mode'} &= ~$S_IX;
137             }
138              
139 1665 100       2456 if ( $mount->{'flags'}->{'nosuid'} ) {
140 9         8 $inode->{'mode'} &= ~$S_ISUID;
141             }
142              
143 1665         1892 foreach (qw(uid gid)) {
144 3330 100       5935 if ( defined $mount->{'flags'}->{$_} ) {
145 18         23 $inode->{$_} = $mount->{'flags'}->{$_};
146             }
147             }
148              
149 1665         3508 return $inode;
150             }
151              
152             sub unmount {
153 3     3 0 3 my ( $self, $mount ) = @_;
154              
155             #
156             # First, check to see that the filesystem mount record found is a
157             # dependency for another mounted filesystem.
158             #
159 3         4 foreach ( @{ $self->{'mounts'} } ) {
  3         7  
160 8 100       14 next if $_ == $mount;
161              
162 5 100       18 throw &Errno::EBUSY if $_->{'mountpoint'}->{'dev'} == $mount->{'dev'};
163             }
164              
165             #
166             # Pluck the filesystem from the mount list.
167             #
168 2         7 for ( my $i = 0; $self->{'mounts'}->[$i]; $i++ ) {
169 5 100       14 next unless $self->{'mounts'}->[$i] eq $mount;
170 2         3 splice @{ $self->{'mounts'} }, $i;
  2         4  
171 2         3 last;
172             }
173              
174             #
175             # Untie the vnode reference from its original mount point and root.
176             #
177 2         5 delete $mount->{'mountpoint'}->{'vnode'};
178 2         3 delete $mount->{'root'}->{'vnode'};
179              
180             #
181             # Break references to the mount record from the per-vnode hash.
182             #
183 2         4 delete $self->{'vnodes'}->{ $mount->{'vnode'} };
184              
185             #
186             # Kill references to the mount record from the per-device hash.
187             #
188 2         5 delete $self->{'devices'}->{ $mount->{'dev'} };
189              
190 2         16 return $self;
191             }
192              
193             1;