| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | # Paranoid::Filesystem -- Filesystem support for paranoid programs | 
| 2 |  |  |  |  |  |  | # | 
| 3 |  |  |  |  |  |  | # $Id: lib/Paranoid/Filesystem.pm, 2.08 2020/12/31 12:10:06 acorliss Exp $ | 
| 4 |  |  |  |  |  |  | # | 
| 5 |  |  |  |  |  |  | # This software is free software.  Similar to Perl, you can redistribute it | 
| 6 |  |  |  |  |  |  | # and/or modify it under the terms of either: | 
| 7 |  |  |  |  |  |  | # | 
| 8 |  |  |  |  |  |  | #   a)     the GNU General Public License | 
| 9 |  |  |  |  |  |  | #           as published by the | 
| 10 |  |  |  |  |  |  | #          Free Software Foundation ; either version 1 | 
| 11 |  |  |  |  |  |  | #          , or any later version | 
| 12 |  |  |  |  |  |  | #          , or | 
| 13 |  |  |  |  |  |  | #   b)     the "Artistic License 2.0 | 
| 14 |  |  |  |  |  |  | #          ", | 
| 15 |  |  |  |  |  |  | # | 
| 16 |  |  |  |  |  |  | # subject to the following additional term:  No trademark rights to | 
| 17 |  |  |  |  |  |  | # "Paranoid" have been or are conveyed under any of the above licenses. | 
| 18 |  |  |  |  |  |  | # However, "Paranoid" may be used fairly to describe this unmodified | 
| 19 |  |  |  |  |  |  | # software, in good faith, but not as a trademark. | 
| 20 |  |  |  |  |  |  | # | 
| 21 |  |  |  |  |  |  | # (c) 2005 - 2020, Arthur Corliss (corliss@digitalmages.com) | 
| 22 |  |  |  |  |  |  | # (tm) 2008 - 2020, Paranoid Inc. (www.paranoid.com) | 
| 23 |  |  |  |  |  |  | # | 
| 24 |  |  |  |  |  |  | ##################################################################### | 
| 25 |  |  |  |  |  |  |  | 
| 26 |  |  |  |  |  |  | ##################################################################### | 
| 27 |  |  |  |  |  |  | # | 
| 28 |  |  |  |  |  |  | # Environment definitions | 
| 29 |  |  |  |  |  |  | # | 
| 30 |  |  |  |  |  |  | ##################################################################### | 
| 31 |  |  |  |  |  |  |  | 
| 32 |  |  |  |  |  |  | package Paranoid::Filesystem; | 
| 33 |  |  |  |  |  |  |  | 
| 34 | 14 |  |  | 14 |  | 5667 | use 5.008; | 
|  | 14 |  |  |  |  | 54 |  | 
| 35 |  |  |  |  |  |  |  | 
| 36 | 14 |  |  | 14 |  | 74 | use strict; | 
|  | 14 |  |  |  |  | 27 |  | 
|  | 14 |  |  |  |  | 258 |  | 
| 37 | 14 |  |  | 14 |  | 59 | use warnings; | 
|  | 14 |  |  |  |  | 28 |  | 
|  | 14 |  |  |  |  | 366 |  | 
| 38 | 14 |  |  | 14 |  | 62 | use vars qw($VERSION @EXPORT @EXPORT_OK %EXPORT_TAGS); | 
|  | 14 |  |  |  |  | 48 |  | 
|  | 14 |  |  |  |  | 999 |  | 
| 39 | 14 |  |  | 14 |  | 100 | use base qw(Exporter); | 
|  | 14 |  |  |  |  | 26 |  | 
|  | 14 |  |  |  |  | 1055 |  | 
| 40 | 14 |  |  | 14 |  | 94 | use Cwd qw(realpath); | 
|  | 14 |  |  |  |  | 22 |  | 
|  | 14 |  |  |  |  | 813 |  | 
| 41 | 14 |  |  | 14 |  | 3719 | use Errno qw(:POSIX); | 
|  | 14 |  |  |  |  | 9959 |  | 
|  | 14 |  |  |  |  | 5282 |  | 
| 42 | 14 |  |  | 14 |  | 115 | use Fcntl qw(:DEFAULT :seek :flock :mode); | 
|  | 14 |  |  |  |  | 23 |  | 
|  | 14 |  |  |  |  | 7602 |  | 
| 43 | 14 |  |  | 14 |  | 106 | use Paranoid; | 
|  | 14 |  |  |  |  | 29 |  | 
|  | 14 |  |  |  |  | 687 |  | 
| 44 | 14 |  |  | 14 |  | 90 | use Paranoid::Debug qw(:all); | 
|  | 14 |  |  |  |  | 40 |  | 
|  | 14 |  |  |  |  | 2319 |  | 
| 45 | 14 |  |  | 14 |  | 4109 | use Paranoid::Process qw(ptranslateUser ptranslateGroup); | 
|  | 14 |  |  |  |  | 37 |  | 
|  | 14 |  |  |  |  | 1016 |  | 
| 46 | 14 |  |  | 14 |  | 4061 | use Paranoid::Input; | 
|  | 14 |  |  |  |  | 34 |  | 
|  | 14 |  |  |  |  | 831 |  | 
| 47 | 14 |  |  | 14 |  | 4761 | use Paranoid::IO; | 
|  | 14 |  |  |  |  | 30 |  | 
|  | 14 |  |  |  |  | 1161 |  | 
| 48 | 14 |  |  | 14 |  | 7239 | use Paranoid::Glob; | 
|  | 14 |  |  |  |  | 43 |  | 
|  | 14 |  |  |  |  | 1328 |  | 
| 49 |  |  |  |  |  |  |  | 
| 50 |  |  |  |  |  |  | ($VERSION) = ( q$Revision: 2.08 $ =~ /(\d+(?:\.\d+)+)/sm ); | 
| 51 |  |  |  |  |  |  |  | 
| 52 |  |  |  |  |  |  | @EXPORT = qw( | 
| 53 |  |  |  |  |  |  | preadDir     psubdirs    pfiles | 
| 54 |  |  |  |  |  |  | pmkdir       prm         prmR      ptouch | 
| 55 |  |  |  |  |  |  | ptouchR      pchmod      pchmodR   pchown | 
| 56 |  |  |  |  |  |  | pchownR      pwhich | 
| 57 |  |  |  |  |  |  | ); | 
| 58 |  |  |  |  |  |  | @EXPORT_OK = ( | 
| 59 |  |  |  |  |  |  | @EXPORT, qw( | 
| 60 |  |  |  |  |  |  | ptranslateLink | 
| 61 |  |  |  |  |  |  | pcleanPath | 
| 62 |  |  |  |  |  |  | ptranslatePerms | 
| 63 |  |  |  |  |  |  | ) ); | 
| 64 |  |  |  |  |  |  | %EXPORT_TAGS = ( all => [@EXPORT_OK], ); | 
| 65 |  |  |  |  |  |  |  | 
| 66 | 14 |  |  | 14 |  | 116 | use constant PERMMASK => 0777; | 
|  | 14 |  |  |  |  | 29 |  | 
|  | 14 |  |  |  |  | 66375 |  | 
| 67 |  |  |  |  |  |  |  | 
| 68 |  |  |  |  |  |  | ##################################################################### | 
| 69 |  |  |  |  |  |  | # | 
| 70 |  |  |  |  |  |  | # Module code follows | 
| 71 |  |  |  |  |  |  | # | 
| 72 |  |  |  |  |  |  | ##################################################################### | 
| 73 |  |  |  |  |  |  |  | 
| 74 |  |  |  |  |  |  | sub pmkdir ($;$\%) { | 
| 75 |  |  |  |  |  |  |  | 
| 76 |  |  |  |  |  |  | # Purpose:  Simulates a 'mkdir -p' command in pure Perl | 
| 77 |  |  |  |  |  |  | # Returns:  True (1) if all targets were successfully created, | 
| 78 |  |  |  |  |  |  | #           False (0) if there are any errors | 
| 79 |  |  |  |  |  |  | # Usage:    $rv = pmkdir("/foo/{a1,b2}"); | 
| 80 |  |  |  |  |  |  | # Usage:    $rv = pmkdir("/foo", 0750); | 
| 81 |  |  |  |  |  |  | # Usage:    $rv = pmkdir("/foo", 0750, %errors); | 
| 82 |  |  |  |  |  |  |  | 
| 83 | 7 |  |  | 7 | 1 | 47 | my $path = shift; | 
| 84 | 7 |  |  |  |  | 12 | my $mode = shift; | 
| 85 | 7 |  | 50 |  |  | 32 | my $eref = shift || {}; | 
| 86 | 7 |  |  |  |  | 14 | my ( $dirs, $directory, $subdir, @parts, $i ); | 
| 87 | 7 |  |  |  |  | 12 | my $rv = 1; | 
| 88 |  |  |  |  |  |  |  | 
| 89 | 7 |  |  |  |  | 20 | pdebug( 'entering w/(%s)(%s)', PDLEVEL1, $path, $mode ); | 
| 90 | 7 |  |  |  |  | 19 | pIn(); | 
| 91 |  |  |  |  |  |  |  | 
| 92 |  |  |  |  |  |  | # Create a glob object if we weren't handed one. | 
| 93 | 7 | 100 |  |  |  | 15 | if ( defined $path ) { | 
| 94 | 6 | 100 |  |  |  | 30 | $dirs = | 
| 95 |  |  |  |  |  |  | ref $path eq 'Paranoid::Glob' | 
| 96 |  |  |  |  |  |  | ? $path | 
| 97 |  |  |  |  |  |  | : Paranoid::Glob->new( globs => [$path] ); | 
| 98 |  |  |  |  |  |  | } | 
| 99 |  |  |  |  |  |  |  | 
| 100 |  |  |  |  |  |  | # Leave Paranoid::Glob's errors in place if there was a problem | 
| 101 | 7 | 100 |  |  |  | 15 | $rv = 0 unless defined $dirs; | 
| 102 |  |  |  |  |  |  |  | 
| 103 |  |  |  |  |  |  | # Set and detaint mode | 
| 104 | 7 | 100 |  |  |  | 16 | if ($rv) { | 
| 105 | 6 | 100 |  |  |  | 49 | $mode = ptranslatePerms( defined $mode ? $mode : umask ^ PERMMASK ); | 
| 106 | 6 | 100 |  |  |  | 16 | unless ( detaint( $mode, 'int' ) ) { | 
| 107 | 1 |  |  |  |  | 4 | Paranoid::ERROR = | 
| 108 |  |  |  |  |  |  | pdebug( 'invalid mode argument passed', PDLEVEL1 ); | 
| 109 | 1 |  |  |  |  | 3 | $rv = 0; | 
| 110 |  |  |  |  |  |  | } | 
| 111 |  |  |  |  |  |  | } | 
| 112 |  |  |  |  |  |  |  | 
| 113 |  |  |  |  |  |  | # Start creating directories | 
| 114 | 7 | 100 |  |  |  | 16 | if ($rv) { | 
| 115 |  |  |  |  |  |  |  | 
| 116 |  |  |  |  |  |  | # Iterate over each directory in the glob | 
| 117 | 5 |  |  |  |  | 11 | foreach $directory (@$dirs) { | 
| 118 | 11 |  |  |  |  | 36 | pdebug( 'processing %s', PDLEVEL2, $directory ); | 
| 119 |  |  |  |  |  |  |  | 
| 120 |  |  |  |  |  |  | # Skip directories already present | 
| 121 | 11 | 50 |  |  |  | 198 | next if -d $directory; | 
| 122 |  |  |  |  |  |  |  | 
| 123 |  |  |  |  |  |  | # Otherwise, split so we can backtrack to the first available | 
| 124 |  |  |  |  |  |  | # subdirectory and start creating subdirectories from there | 
| 125 | 11 |  |  |  |  | 128 | @parts = split m#/+#s, $directory; | 
| 126 | 11 | 50 |  |  |  | 33 | $i = $parts[0] eq '' ? 1 : 0; | 
| 127 | 11 |  | 100 |  |  | 335 | $i++ while $i < $#parts and -d join '/', @parts[ 0 .. $i ]; | 
| 128 | 11 |  |  |  |  | 43 | while ( $i <= $#parts ) { | 
| 129 | 17 |  |  |  |  | 55 | $subdir = join '/', @parts[ 0 .. $i ]; | 
| 130 | 17 | 50 |  |  |  | 198 | unless ( -d $subdir ) { | 
| 131 | 17 | 50 |  |  |  | 750 | if ( mkdir $subdir, $mode ) { | 
| 132 |  |  |  |  |  |  |  | 
| 133 |  |  |  |  |  |  | # Make sure perms are applied | 
| 134 | 17 |  |  |  |  | 282 | chmod $mode, $subdir; | 
| 135 |  |  |  |  |  |  |  | 
| 136 |  |  |  |  |  |  | } else { | 
| 137 |  |  |  |  |  |  |  | 
| 138 |  |  |  |  |  |  | # Error out and halt all work | 
| 139 | 0 |  |  |  |  | 0 | Paranoid::ERROR = pdebug( 'failed to create %s: %s', | 
| 140 |  |  |  |  |  |  | PDLEVEL1, $subdir, $! ); | 
| 141 | 0 |  |  |  |  | 0 | $rv = 0; | 
| 142 | 0 |  |  |  |  | 0 | last; | 
| 143 |  |  |  |  |  |  | } | 
| 144 |  |  |  |  |  |  | } | 
| 145 | 17 |  |  |  |  | 76 | $i++; | 
| 146 |  |  |  |  |  |  | } | 
| 147 |  |  |  |  |  |  | } | 
| 148 |  |  |  |  |  |  | } | 
| 149 |  |  |  |  |  |  |  | 
| 150 | 7 |  |  |  |  | 25 | pOut(); | 
| 151 | 7 |  |  |  |  | 22 | pdebug( 'leaving w/rv: %s', PDLEVEL1, $rv ); | 
| 152 |  |  |  |  |  |  |  | 
| 153 | 7 |  |  |  |  | 51 | return $rv; | 
| 154 |  |  |  |  |  |  | } | 
| 155 |  |  |  |  |  |  |  | 
| 156 |  |  |  |  |  |  | sub prm ($;\%) { | 
| 157 |  |  |  |  |  |  |  | 
| 158 |  |  |  |  |  |  | # Purpose:  Simulates a "rm -f" command in pure Perl | 
| 159 |  |  |  |  |  |  | # Returns:  True (1) if all targets were successfully removed, | 
| 160 |  |  |  |  |  |  | #           False (0) if there are any errors | 
| 161 |  |  |  |  |  |  | # Usage:    $rv = prm("/foo"); | 
| 162 |  |  |  |  |  |  | # Usage:    $rv = prm("/foo", %errors); | 
| 163 |  |  |  |  |  |  |  | 
| 164 | 7 |  |  | 7 | 1 | 505 | my $target = shift; | 
| 165 | 7 |  |  |  |  | 10 | my $errRef = shift; | 
| 166 | 7 |  |  |  |  | 11 | my $rv     = 1; | 
| 167 | 7 |  |  |  |  | 11 | my ( $glob, $tglob, @fstat ); | 
| 168 |  |  |  |  |  |  |  | 
| 169 | 7 |  |  |  |  | 22 | pdebug( 'entering w/(%s)(%s)', PDLEVEL1, $target, $errRef ); | 
| 170 | 7 |  |  |  |  | 17 | pIn(); | 
| 171 |  |  |  |  |  |  |  | 
| 172 |  |  |  |  |  |  | # Prep error hash | 
| 173 | 7 | 50 |  |  |  | 16 | $errRef = {} unless defined $errRef; | 
| 174 | 7 |  |  |  |  | 15 | %$errRef = (); | 
| 175 |  |  |  |  |  |  |  | 
| 176 |  |  |  |  |  |  | # Create a glob object if we weren't handed one. | 
| 177 | 7 | 50 |  |  |  | 15 | if ( defined $target ) { | 
| 178 | 7 | 100 |  |  |  | 25 | $glob = | 
| 179 |  |  |  |  |  |  | ref $target eq 'Paranoid::Glob' | 
| 180 |  |  |  |  |  |  | ? $target | 
| 181 |  |  |  |  |  |  | : Paranoid::Glob->new( globs => [$target] ); | 
| 182 |  |  |  |  |  |  | } | 
| 183 | 7 | 50 |  |  |  | 16 | $rv = 0 unless defined $glob; | 
| 184 |  |  |  |  |  |  |  | 
| 185 |  |  |  |  |  |  | # Start removing files | 
| 186 | 7 | 50 |  |  |  | 16 | if ($rv) { | 
| 187 |  |  |  |  |  |  |  | 
| 188 |  |  |  |  |  |  | # Consolidate the entries | 
| 189 | 7 |  |  |  |  | 22 | $glob->consolidate; | 
| 190 |  |  |  |  |  |  |  | 
| 191 |  |  |  |  |  |  | # Iterate over entries | 
| 192 | 7 |  |  |  |  | 16 | foreach ( reverse @$glob ) { | 
| 193 | 21 |  |  |  |  | 88 | pdebug( 'processing %s', PDLEVEL2, $_ ); | 
| 194 |  |  |  |  |  |  |  | 
| 195 |  |  |  |  |  |  | # Stat the file | 
| 196 | 21 |  |  |  |  | 360 | @fstat = lstat $_; | 
| 197 |  |  |  |  |  |  |  | 
| 198 | 21 | 100 |  |  |  | 68 | unless (@fstat) { | 
| 199 |  |  |  |  |  |  |  | 
| 200 |  |  |  |  |  |  | # If the file is missing, consider the removal successful and | 
| 201 |  |  |  |  |  |  | # move on. | 
| 202 | 2 | 50 |  |  |  | 17 | next if $! == ENOENT; | 
| 203 |  |  |  |  |  |  |  | 
| 204 |  |  |  |  |  |  | # Report remaining errors (permission denied, etc.) | 
| 205 | 0 |  |  |  |  | 0 | $rv = 0; | 
| 206 | 0 |  |  |  |  | 0 | $$errRef{$_} = $!; | 
| 207 | 0 |  |  |  |  | 0 | Paranoid::ERROR = | 
| 208 |  |  |  |  |  |  | pdebug( 'failed to remove %s: %s', PDLEVEL1, $_, $! ); | 
| 209 | 0 |  |  |  |  | 0 | next; | 
| 210 |  |  |  |  |  |  | } | 
| 211 |  |  |  |  |  |  |  | 
| 212 | 19 | 100 |  |  |  | 65 | if ( S_ISDIR( $fstat[2] ) ) { | 
| 213 |  |  |  |  |  |  |  | 
| 214 |  |  |  |  |  |  | # Remove directories | 
| 215 | 12 | 100 |  |  |  | 467 | unless ( rmdir $_ ) { | 
| 216 |  |  |  |  |  |  |  | 
| 217 |  |  |  |  |  |  | # Record errors | 
| 218 | 1 |  |  |  |  | 10 | $rv = 0; | 
| 219 | 1 |  |  |  |  | 16 | $$errRef{$_} = $!; | 
| 220 | 1 |  |  |  |  | 5 | Paranoid::ERROR = | 
| 221 |  |  |  |  |  |  | pdebug( 'failed to remove %s: %s', PDLEVEL1, $_, $! ); | 
| 222 |  |  |  |  |  |  | } | 
| 223 |  |  |  |  |  |  |  | 
| 224 |  |  |  |  |  |  | } else { | 
| 225 |  |  |  |  |  |  |  | 
| 226 |  |  |  |  |  |  | # Remove all non-directories | 
| 227 | 7 | 50 |  |  |  | 261 | unless ( unlink $_ ) { | 
| 228 |  |  |  |  |  |  |  | 
| 229 |  |  |  |  |  |  | # Record errors | 
| 230 | 0 |  |  |  |  | 0 | $rv = 0; | 
| 231 | 0 |  |  |  |  | 0 | $$errRef{$_} = $!; | 
| 232 | 0 |  |  |  |  | 0 | Paranoid::ERROR = | 
| 233 |  |  |  |  |  |  | pdebug( 'failed to remove %s: %s', PDLEVEL1, $_, $! ); | 
| 234 |  |  |  |  |  |  | } | 
| 235 |  |  |  |  |  |  | } | 
| 236 |  |  |  |  |  |  | } | 
| 237 |  |  |  |  |  |  | } | 
| 238 |  |  |  |  |  |  |  | 
| 239 | 7 |  |  |  |  | 37 | pOut(); | 
| 240 | 7 |  |  |  |  | 23 | pdebug( 'leaving w/rv: %s', PDLEVEL1, $rv ); | 
| 241 |  |  |  |  |  |  |  | 
| 242 | 7 |  |  |  |  | 35 | return $rv; | 
| 243 |  |  |  |  |  |  | } | 
| 244 |  |  |  |  |  |  |  | 
| 245 |  |  |  |  |  |  | sub prmR ($;$\%) { | 
| 246 |  |  |  |  |  |  |  | 
| 247 |  |  |  |  |  |  | # Purpose:  Recursively calls prm to simulate "rm -rf" | 
| 248 |  |  |  |  |  |  | # Returns:  True (1) if all targets were successfully removed, | 
| 249 |  |  |  |  |  |  | #           False (0) if there are any errors | 
| 250 |  |  |  |  |  |  | # Usage:    $rv = prmR("/foo"); | 
| 251 |  |  |  |  |  |  | # Usage:    $rv = prmR("/foo", 1); | 
| 252 |  |  |  |  |  |  | # Usage:    $rv = prmR("/foo", 1, %errors); | 
| 253 |  |  |  |  |  |  |  | 
| 254 | 4 |  |  | 4 | 1 | 576 | my $target = shift; | 
| 255 | 4 |  |  |  |  | 9 | my $follow = shift; | 
| 256 | 4 |  |  |  |  | 5 | my $errRef = shift; | 
| 257 | 4 |  |  |  |  | 6 | my $rv     = 1; | 
| 258 | 4 |  |  |  |  | 7 | my ( $glob, $tglob ); | 
| 259 |  |  |  |  |  |  |  | 
| 260 | 4 |  |  |  |  | 14 | pdebug( 'entering w/(%s)(%s)(%s)', PDLEVEL1, $target, $follow, $errRef ); | 
| 261 | 4 |  |  |  |  | 12 | pIn(); | 
| 262 |  |  |  |  |  |  |  | 
| 263 |  |  |  |  |  |  | # Prep error hash | 
| 264 | 4 | 50 |  |  |  | 10 | $errRef = {} unless defined $errRef; | 
| 265 | 4 |  |  |  |  | 10 | %$errRef = (); | 
| 266 |  |  |  |  |  |  |  | 
| 267 |  |  |  |  |  |  | # Create a glob object if we weren't handed one. | 
| 268 | 4 | 50 |  |  |  | 8 | if ( defined $target ) { | 
| 269 | 4 | 50 |  |  |  | 27 | $glob = | 
| 270 |  |  |  |  |  |  | ref $target eq 'Paranoid::Glob' | 
| 271 |  |  |  |  |  |  | ? $target | 
| 272 |  |  |  |  |  |  | : Paranoid::Glob->new( globs => [$target] ); | 
| 273 |  |  |  |  |  |  | } | 
| 274 | 4 | 50 |  |  |  | 12 | $rv = 0 unless defined $glob; | 
| 275 |  |  |  |  |  |  |  | 
| 276 | 4 | 50 |  |  |  | 9 | if ($rv) { | 
| 277 |  |  |  |  |  |  |  | 
| 278 |  |  |  |  |  |  | # Load the directory tree and execute prm | 
| 279 | 4 |  | 33 |  |  | 17 | $rv = $glob->recurse( $follow, 1 ) && prm( $glob, %$errRef ); | 
| 280 |  |  |  |  |  |  | } | 
| 281 |  |  |  |  |  |  |  | 
| 282 | 4 |  |  |  |  | 13 | pOut(); | 
| 283 | 4 |  |  |  |  | 11 | pdebug( 'leaving w/rv: %s', PDLEVEL1, $rv ); | 
| 284 |  |  |  |  |  |  |  | 
| 285 | 4 |  |  |  |  | 21 | return $rv; | 
| 286 |  |  |  |  |  |  | } | 
| 287 |  |  |  |  |  |  |  | 
| 288 |  |  |  |  |  |  | sub preadDir ($\@;$) { | 
| 289 |  |  |  |  |  |  |  | 
| 290 |  |  |  |  |  |  | # Purpose:  Populates the passed array ref with a list of all the | 
| 291 |  |  |  |  |  |  | #           directory entries (minus the '.' & '..') in the passed | 
| 292 |  |  |  |  |  |  | #           directory | 
| 293 |  |  |  |  |  |  | # Returns:  True (1) if the read was successful, | 
| 294 |  |  |  |  |  |  | #           False (0) if there are any errors | 
| 295 |  |  |  |  |  |  | # Usage:    $rv = preadDir("/tmp", @entries); | 
| 296 |  |  |  |  |  |  | # Usage:    $rv = preadDir("/tmp", @entries, 1); | 
| 297 |  |  |  |  |  |  |  | 
| 298 | 9 |  |  | 9 | 1 | 511 | my ( $dir, $aref, $noLinks ) = @_; | 
| 299 | 9 |  |  |  |  | 18 | my $rv = 1; | 
| 300 | 9 |  |  |  |  | 11 | my $fh; | 
| 301 |  |  |  |  |  |  |  | 
| 302 | 9 |  |  |  |  | 30 | pdebug( 'entering w/(%s)(%s)(%s)', PDLEVEL1, $dir, $aref, $noLinks ); | 
| 303 | 9 |  |  |  |  | 22 | pIn(); | 
| 304 | 9 |  |  |  |  | 20 | @$aref = (); | 
| 305 |  |  |  |  |  |  |  | 
| 306 |  |  |  |  |  |  | # Validate directory and exit early, if need be | 
| 307 | 9 | 100 | 66 |  |  | 253 | unless ( defined $dir and -e $dir and -d _ and -r _ ) { | 
|  |  |  | 100 |  |  |  |  | 
|  |  |  | 66 |  |  |  |  | 
| 308 | 4 |  |  |  |  | 15 | $rv = 0; | 
| 309 | 4 | 50 |  |  |  | 26 | Paranoid::ERROR = pdebug( ( | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
| 310 |  |  |  |  |  |  | !defined $dir ? 'undefined value passed as directory name' | 
| 311 |  |  |  |  |  |  | : !-e _         ? 'directory (%s) does not exist' | 
| 312 |  |  |  |  |  |  | : !-d _         ? '%s is not a directory' | 
| 313 |  |  |  |  |  |  | : 'directory (%s) is not readable by the effective user' | 
| 314 |  |  |  |  |  |  | ), | 
| 315 |  |  |  |  |  |  | PDLEVEL1, $dir | 
| 316 |  |  |  |  |  |  | ); | 
| 317 |  |  |  |  |  |  | } | 
| 318 |  |  |  |  |  |  |  | 
| 319 | 9 | 100 |  |  |  | 26 | if ($rv) { | 
| 320 |  |  |  |  |  |  |  | 
| 321 |  |  |  |  |  |  | # Read the directory's contents | 
| 322 | 5 |  |  |  |  | 191 | $rv = opendir $fh, $dir; | 
| 323 |  |  |  |  |  |  |  | 
| 324 | 5 | 50 |  |  |  | 19 | if ($rv) { | 
| 325 |  |  |  |  |  |  |  | 
| 326 |  |  |  |  |  |  | # Get the list, filtering out '.' & '..' | 
| 327 | 5 |  |  |  |  | 111 | foreach ( readdir $fh ) { | 
| 328 | 23 | 100 |  |  |  | 115 | push @$aref, "$dir/$_" unless m/^\.\.?$/s; | 
| 329 |  |  |  |  |  |  | } | 
| 330 | 5 |  |  |  |  | 64 | closedir $fh; | 
| 331 |  |  |  |  |  |  |  | 
| 332 |  |  |  |  |  |  | # Filter out symlinks, if necessary | 
| 333 | 5 | 50 |  |  |  | 18 | @$aref = grep { !-l $_ } @$aref if $noLinks; | 
|  | 0 |  |  |  |  | 0 |  | 
| 334 |  |  |  |  |  |  |  | 
| 335 |  |  |  |  |  |  | } else { | 
| 336 | 0 |  |  |  |  | 0 | Paranoid::ERROR = pdebug( 'error opening directory (%s): %s', | 
| 337 |  |  |  |  |  |  | PDLEVEL1, $dir, $! ); | 
| 338 |  |  |  |  |  |  | } | 
| 339 |  |  |  |  |  |  | } | 
| 340 |  |  |  |  |  |  |  | 
| 341 | 9 |  |  |  |  | 32 | pdebug( 'returning %d entries', PDLEVEL2, scalar @$aref ); | 
| 342 |  |  |  |  |  |  |  | 
| 343 | 9 |  |  |  |  | 24 | pOut(); | 
| 344 | 9 |  |  |  |  | 25 | pdebug( 'leaving w/rv: %s', PDLEVEL1, $rv ); | 
| 345 |  |  |  |  |  |  |  | 
| 346 | 9 |  |  |  |  | 47 | return $rv; | 
| 347 |  |  |  |  |  |  | } | 
| 348 |  |  |  |  |  |  |  | 
| 349 |  |  |  |  |  |  | sub psubdirs ($\@;$) { | 
| 350 |  |  |  |  |  |  |  | 
| 351 |  |  |  |  |  |  | # Purpose:  Performs a preadDir but filters out all non-directory entries | 
| 352 |  |  |  |  |  |  | #           so that only subdirectory entries are returned.  Can | 
| 353 |  |  |  |  |  |  | #           optionally filter out symlinks to directories as well. | 
| 354 |  |  |  |  |  |  | # Returns:  True (1) if the directory read was successful, | 
| 355 |  |  |  |  |  |  | #           False (0) if there are any errors | 
| 356 |  |  |  |  |  |  | # Usage:    $rv = psubdirs($dir, @entries); | 
| 357 |  |  |  |  |  |  | # Usage:    $rv = psubdirs($dir, @entries, 1); | 
| 358 |  |  |  |  |  |  |  | 
| 359 | 3 |  |  | 3 | 1 | 11 | my ( $dir, $aref, $noLinks ) = @_; | 
| 360 | 3 |  |  |  |  | 7 | my $rv = 0; | 
| 361 |  |  |  |  |  |  |  | 
| 362 |  |  |  |  |  |  | # Validate arguments | 
| 363 | 3 | 50 |  |  |  | 8 | $noLinks = 0 unless defined $noLinks; | 
| 364 |  |  |  |  |  |  |  | 
| 365 | 3 |  |  |  |  | 11 | pdebug( 'entering w/(%s)(%s)(%s)', PDLEVEL1, $dir, $aref, $noLinks ); | 
| 366 | 3 |  |  |  |  | 9 | pIn(); | 
| 367 |  |  |  |  |  |  |  | 
| 368 |  |  |  |  |  |  | # Empty target array and retrieve list | 
| 369 | 3 |  |  |  |  | 8 | $rv = preadDir( $dir, @$aref, $noLinks ); | 
| 370 |  |  |  |  |  |  |  | 
| 371 |  |  |  |  |  |  | # Filter out all non-directories | 
| 372 | 3 | 100 |  |  |  | 13 | @$aref = grep { -d $_ } @$aref if $rv; | 
|  | 5 |  |  |  |  | 82 |  | 
| 373 |  |  |  |  |  |  |  | 
| 374 | 3 |  |  |  |  | 13 | pdebug( 'returning %d entries', PDLEVEL2, scalar @$aref ); | 
| 375 |  |  |  |  |  |  |  | 
| 376 | 3 |  |  |  |  | 8 | pOut(); | 
| 377 | 3 |  |  |  |  | 17 | pdebug( 'leaving w/rv: %s', PDLEVEL1, $rv ); | 
| 378 |  |  |  |  |  |  |  | 
| 379 | 3 |  |  |  |  | 16 | return $rv; | 
| 380 |  |  |  |  |  |  | } | 
| 381 |  |  |  |  |  |  |  | 
| 382 |  |  |  |  |  |  | sub pfiles ($\@;$) { | 
| 383 |  |  |  |  |  |  |  | 
| 384 |  |  |  |  |  |  | # Purpose:  Performs a preadDir but filters out all directory entries | 
| 385 |  |  |  |  |  |  | #           so that only file entries are returned.  Can | 
| 386 |  |  |  |  |  |  | #           optionally filter out symlinks to files as well. | 
| 387 |  |  |  |  |  |  | # Returns:  True (1) if the directory read was successful, | 
| 388 |  |  |  |  |  |  | #           False (0) if there are any errors | 
| 389 |  |  |  |  |  |  | # Usage:    $rv = pfiles($dir, @entries); | 
| 390 |  |  |  |  |  |  | # Usage:    $rv = pfiles($dir, @entries, 1); | 
| 391 |  |  |  |  |  |  |  | 
| 392 | 3 |  |  | 3 | 1 | 10 | my ( $dir, $aref, $noLinks ) = @_; | 
| 393 | 3 |  |  |  |  | 6 | my $rv = 0; | 
| 394 |  |  |  |  |  |  |  | 
| 395 |  |  |  |  |  |  | # Validate arguments | 
| 396 | 3 | 50 |  |  |  | 10 | $noLinks = 0 unless defined $noLinks; | 
| 397 |  |  |  |  |  |  |  | 
| 398 | 3 |  |  |  |  | 10 | pdebug( 'entering w/(%s)(%s)', PDLEVEL1, $dir, $aref ); | 
| 399 | 3 |  |  |  |  | 11 | pIn(); | 
| 400 |  |  |  |  |  |  |  | 
| 401 |  |  |  |  |  |  | # Empty target array and retrieve list | 
| 402 | 3 |  |  |  |  | 6 | @$aref = (); | 
| 403 | 3 |  |  |  |  | 7 | $rv = preadDir( $dir, @$aref, $noLinks ); | 
| 404 |  |  |  |  |  |  |  | 
| 405 |  |  |  |  |  |  | # Filter out all non-files | 
| 406 | 3 | 100 |  |  |  | 16 | @$aref = grep { -f $_ } @$aref if $rv; | 
|  | 4 |  |  |  |  | 64 |  | 
| 407 |  |  |  |  |  |  |  | 
| 408 | 3 |  |  |  |  | 14 | pdebug( 'returning %d entries', PDLEVEL2, scalar @$aref ); | 
| 409 |  |  |  |  |  |  |  | 
| 410 | 3 |  |  |  |  | 9 | pOut(); | 
| 411 | 3 |  |  |  |  | 10 | pdebug( 'leaving w/rv: %s', PDLEVEL1, $rv ); | 
| 412 |  |  |  |  |  |  |  | 
| 413 | 3 |  |  |  |  | 14 | return $rv; | 
| 414 |  |  |  |  |  |  | } | 
| 415 |  |  |  |  |  |  |  | 
| 416 |  |  |  |  |  |  | sub pcleanPath { | 
| 417 |  |  |  |  |  |  |  | 
| 418 |  |  |  |  |  |  | # Purpose:  Removes/resolves directory artifacts like '/../', etc. | 
| 419 |  |  |  |  |  |  | # Returns:  Filtered string | 
| 420 |  |  |  |  |  |  | # Usage:    $filename = pcleanPath($filename); | 
| 421 |  |  |  |  |  |  |  | 
| 422 | 9 |  |  | 9 | 1 | 3541 | my $filename = shift; | 
| 423 |  |  |  |  |  |  |  | 
| 424 | 9 |  |  |  |  | 31 | pdebug( 'entering w/(%s)', PDLEVEL1, $filename ); | 
| 425 | 9 |  |  |  |  | 27 | pIn(); | 
| 426 |  |  |  |  |  |  |  | 
| 427 | 9 | 100 |  |  |  | 20 | if ( defined $filename ) { | 
| 428 |  |  |  |  |  |  |  | 
| 429 |  |  |  |  |  |  | # Strip all //+, /./, and /{parent}/../ | 
| 430 | 8 |  |  |  |  | 40 | while ( $filename =~ m#/\.?/+#s ) { $filename =~ s#/\.?/+#/#sg } | 
|  | 1 |  |  |  |  | 11 |  | 
| 431 | 8 |  |  |  |  | 31 | while ( $filename =~ m#/(?:(?!\.\.)[^/]{2,}|[^/])/\.\./#s ) { | 
| 432 | 6 |  |  |  |  | 37 | $filename =~ s#/(?:(?!\.\.)[^/]{2,}|[^/])/\.\./#/#sg; | 
| 433 |  |  |  |  |  |  | } | 
| 434 |  |  |  |  |  |  |  | 
| 435 |  |  |  |  |  |  | # Strip trailing /. and leading /../ | 
| 436 | 8 |  |  |  |  | 21 | $filename =~ s#/\.$##s; | 
| 437 | 8 |  |  |  |  | 19 | while ( $filename =~ m#^/\.\./#s ) { $filename =~ s#^/\.\./#/#s } | 
|  | 3 |  |  |  |  | 12 |  | 
| 438 |  |  |  |  |  |  |  | 
| 439 |  |  |  |  |  |  | # Strip any ^[^/]+/../ | 
| 440 | 8 |  |  |  |  | 19 | while ( $filename =~ m#^[^/]+/\.\./#s ) { | 
| 441 | 1 |  |  |  |  | 7 | $filename =~ s#^[^/]+/\.\./##s; | 
| 442 |  |  |  |  |  |  | } | 
| 443 |  |  |  |  |  |  |  | 
| 444 |  |  |  |  |  |  | # Strip any trailing /^[^/]+/..$ | 
| 445 | 8 |  |  |  |  | 21 | while ( $filename =~ m#/[^/]+/\.\.$#s ) { | 
| 446 | 1 |  |  |  |  | 7 | $filename =~ s#/[^/]+/\.\.$##s; | 
| 447 |  |  |  |  |  |  | } | 
| 448 |  |  |  |  |  |  | } | 
| 449 |  |  |  |  |  |  |  | 
| 450 | 9 |  |  |  |  | 23 | pOut(); | 
| 451 | 9 |  |  |  |  | 20 | pdebug( 'leaving w/rv: %s', PDLEVEL1, $filename ); | 
| 452 |  |  |  |  |  |  |  | 
| 453 | 9 |  |  |  |  | 33 | return $filename; | 
| 454 |  |  |  |  |  |  | } | 
| 455 |  |  |  |  |  |  |  | 
| 456 |  |  |  |  |  |  | sub ptranslateLink { | 
| 457 |  |  |  |  |  |  |  | 
| 458 |  |  |  |  |  |  | # Purpose:  Performs either a full (realpath) or a partial one (last | 
| 459 |  |  |  |  |  |  | #           filename element only) on the passed filename | 
| 460 |  |  |  |  |  |  | # Returns:  Altered filename if successful, undef if there are any | 
| 461 |  |  |  |  |  |  | #           failures | 
| 462 |  |  |  |  |  |  | # Usage:    $filename = ptranslateLink($filename); | 
| 463 |  |  |  |  |  |  | # Usage:    $filename = ptranslateLink($filename, 1); | 
| 464 |  |  |  |  |  |  |  | 
| 465 | 2 |  |  | 2 | 1 | 579 | my $link           = shift; | 
| 466 | 2 |  | 50 |  |  | 14 | my $fullyTranslate = shift || 0; | 
| 467 | 2 |  |  |  |  | 4 | my $nLinks         = 0; | 
| 468 | 2 |  |  |  |  | 4 | my ( $i, $target ); | 
| 469 |  |  |  |  |  |  |  | 
| 470 | 2 |  |  |  |  | 9 | pdebug( 'entering w/(%s)(%s)', PDLEVEL1, $link, $fullyTranslate ); | 
| 471 | 2 |  |  |  |  | 7 | pIn(); | 
| 472 |  |  |  |  |  |  |  | 
| 473 |  |  |  |  |  |  | # Validate link and exit early, if need be | 
| 474 | 2 | 50 | 50 |  |  | 43 | unless ( defined $link and scalar lstat $link ) { | 
| 475 | 0 |  |  |  |  | 0 | Paranoid::ERROR = pdebug( 'link (%s) does not exist on filesystem', | 
| 476 |  |  |  |  |  |  | PDLEVEL1, $link ); | 
| 477 | 0 |  |  |  |  | 0 | pOut(); | 
| 478 | 0 |  |  |  |  | 0 | pdebug( 'leaving w/rv: undef', PDLEVEL1 ); | 
| 479 | 0 |  |  |  |  | 0 | return undef; | 
| 480 |  |  |  |  |  |  | } | 
| 481 |  |  |  |  |  |  |  | 
| 482 |  |  |  |  |  |  | # Check every element in the path for symlinks and translate it if | 
| 483 |  |  |  |  |  |  | # if a full translation was requested | 
| 484 | 2 | 50 |  |  |  | 8 | if ($fullyTranslate) { | 
| 485 |  |  |  |  |  |  |  | 
| 486 |  |  |  |  |  |  | # Resolve the link | 
| 487 | 0 |  |  |  |  | 0 | $target = realpath($link); | 
| 488 |  |  |  |  |  |  |  | 
| 489 |  |  |  |  |  |  | # Make sure we got an answer | 
| 490 | 0 | 0 |  |  |  | 0 | if ( defined $target ) { | 
| 491 |  |  |  |  |  |  |  | 
| 492 |  |  |  |  |  |  | # Save the answer | 
| 493 | 0 |  |  |  |  | 0 | $link = $target; | 
| 494 |  |  |  |  |  |  |  | 
| 495 |  |  |  |  |  |  | } else { | 
| 496 |  |  |  |  |  |  |  | 
| 497 |  |  |  |  |  |  | # Report our inability to resolve the link | 
| 498 | 0 |  |  |  |  | 0 | Paranoid::ERROR = | 
| 499 |  |  |  |  |  |  | pdebug( 'link (%s) couldn\'t be resolved fully: %s', | 
| 500 |  |  |  |  |  |  | PDLEVEL1, $link, $! ); | 
| 501 | 0 |  |  |  |  | 0 | $link = undef; | 
| 502 |  |  |  |  |  |  | } | 
| 503 |  |  |  |  |  |  |  | 
| 504 |  |  |  |  |  |  | } else { | 
| 505 |  |  |  |  |  |  |  | 
| 506 |  |  |  |  |  |  | # Is the file passed a symlink? | 
| 507 | 2 | 50 |  |  |  | 24 | if ( -l $link ) { | 
| 508 |  |  |  |  |  |  |  | 
| 509 |  |  |  |  |  |  | # Yes it is, let's get the target | 
| 510 | 2 |  |  |  |  | 24 | $target = readlink $link; | 
| 511 | 2 |  |  |  |  | 11 | pdebug( 'last element is a link to %s', PDLEVEL1, $target ); | 
| 512 |  |  |  |  |  |  |  | 
| 513 |  |  |  |  |  |  | # Is the target a relative filename? | 
| 514 | 2 | 50 |  |  |  | 10 | if ( $target =~ m#^(?:\.\.?/|[^/])#s ) { | 
| 515 |  |  |  |  |  |  |  | 
| 516 |  |  |  |  |  |  | # Yupper, replace the filename with the target | 
| 517 | 2 |  |  |  |  | 16 | $link =~ s#[^/]+$#$target#s; | 
| 518 |  |  |  |  |  |  |  | 
| 519 |  |  |  |  |  |  | } else { | 
| 520 |  |  |  |  |  |  |  | 
| 521 |  |  |  |  |  |  | # The target is fully qualified, so replace link entirely | 
| 522 | 0 |  |  |  |  | 0 | $link = $target; | 
| 523 |  |  |  |  |  |  | } | 
| 524 |  |  |  |  |  |  | } | 
| 525 |  |  |  |  |  |  | } | 
| 526 |  |  |  |  |  |  |  | 
| 527 | 2 | 50 |  |  |  | 12 | $link = pcleanPath($link) if defined $link; | 
| 528 |  |  |  |  |  |  |  | 
| 529 | 2 |  |  |  |  | 7 | pOut(); | 
| 530 | 2 |  |  |  |  | 6 | pdebug( 'leaving w/rv: %s', PDLEVEL1, $link ); | 
| 531 |  |  |  |  |  |  |  | 
| 532 | 2 |  |  |  |  | 8 | return $link; | 
| 533 |  |  |  |  |  |  | } | 
| 534 |  |  |  |  |  |  |  | 
| 535 |  |  |  |  |  |  | sub ptouch ($;$\%) { | 
| 536 |  |  |  |  |  |  |  | 
| 537 |  |  |  |  |  |  | # Purpose:  Simulates a "touch" command in pure Perl | 
| 538 |  |  |  |  |  |  | # Returns:  True (1) if all targets were successfully touched, | 
| 539 |  |  |  |  |  |  | #           False (0) if there are any errors | 
| 540 |  |  |  |  |  |  | # Usage:    $rv = ptouch("/foo/*"); | 
| 541 |  |  |  |  |  |  | # Usage:    $rv = ptouch("/foo/*", $tstamp); | 
| 542 |  |  |  |  |  |  | # Usage:    $rv = ptouch("/foo/*", $tstamp, %errors); | 
| 543 |  |  |  |  |  |  |  | 
| 544 | 7 |  |  | 7 | 1 | 22 | my $target = shift; | 
| 545 | 7 |  |  |  |  | 13 | my $stamp  = shift; | 
| 546 | 7 |  |  |  |  | 11 | my $errRef = shift; | 
| 547 | 7 |  |  |  |  | 12 | my $rv     = 1; | 
| 548 | 7 |  |  |  |  | 10 | my $irv    = 1; | 
| 549 | 7 |  |  |  |  | 15 | my ( $glob, $tglob, $fh ); | 
| 550 |  |  |  |  |  |  |  | 
| 551 | 7 |  |  |  |  | 25 | pdebug( 'entering w/(%s)(%s)(%s)', PDLEVEL1, $target, $stamp, $errRef ); | 
| 552 | 7 |  |  |  |  | 19 | pIn(); | 
| 553 |  |  |  |  |  |  |  | 
| 554 |  |  |  |  |  |  | # Prep error hash | 
| 555 | 7 | 100 |  |  |  | 15 | $errRef = {} unless defined $errRef; | 
| 556 | 7 |  |  |  |  | 13 | %$errRef = (); | 
| 557 |  |  |  |  |  |  |  | 
| 558 |  |  |  |  |  |  | # Create a glob object if we weren't handed one. | 
| 559 | 7 | 50 |  |  |  | 15 | if ( defined $target ) { | 
| 560 | 7 | 100 |  |  |  | 38 | $glob = | 
| 561 |  |  |  |  |  |  | ref $target eq 'Paranoid::Glob' | 
| 562 |  |  |  |  |  |  | ? $target | 
| 563 |  |  |  |  |  |  | : Paranoid::Glob->new( globs => [$target] ); | 
| 564 |  |  |  |  |  |  | } | 
| 565 | 7 | 50 |  |  |  | 17 | $rv = 0 unless defined $glob; | 
| 566 |  |  |  |  |  |  |  | 
| 567 | 7 | 50 |  |  |  | 13 | if ($rv) { | 
| 568 |  |  |  |  |  |  |  | 
| 569 |  |  |  |  |  |  | # Apply the default timestamp if omitted | 
| 570 | 7 | 100 |  |  |  | 16 | $stamp = time unless defined $stamp; | 
| 571 |  |  |  |  |  |  |  | 
| 572 | 7 | 50 |  |  |  | 23 | unless ( detaint( $stamp, 'int' ) ) { | 
| 573 | 0 |  |  |  |  | 0 | Paranoid::ERROR = pdebug( 'Invalid characters in timestamp: %s', | 
| 574 |  |  |  |  |  |  | PDLEVEL2, $stamp ); | 
| 575 | 0 |  |  |  |  | 0 | $rv = 0; | 
| 576 |  |  |  |  |  |  | } | 
| 577 |  |  |  |  |  |  | } | 
| 578 |  |  |  |  |  |  |  | 
| 579 |  |  |  |  |  |  | # Start touching stuff | 
| 580 | 7 | 50 |  |  |  | 17 | if ($rv) { | 
| 581 |  |  |  |  |  |  |  | 
| 582 |  |  |  |  |  |  | # Consolidate the entries | 
| 583 | 7 |  |  |  |  | 27 | $glob->consolidate; | 
| 584 |  |  |  |  |  |  |  | 
| 585 |  |  |  |  |  |  | # Iterate over entries | 
| 586 | 7 |  |  |  |  | 16 | foreach $target (@$glob) { | 
| 587 | 16 |  |  |  |  | 55 | pdebug( 'processing %s', PDLEVEL2, $target ); | 
| 588 | 16 |  |  |  |  | 20 | $irv = 1; | 
| 589 |  |  |  |  |  |  |  | 
| 590 |  |  |  |  |  |  | # Create the target if it does not exist | 
| 591 | 16 | 100 |  |  |  | 253 | unless ( -e $target ) { | 
| 592 | 4 |  |  |  |  | 18 | pdebug( 'creating empty file (%s)', PDLEVEL2, $target ); | 
| 593 | 4 |  | 66 |  |  | 16 | $fh = popen( $target, O_CREAT | O_EXCL | O_RDWR ) | 
| 594 |  |  |  |  |  |  | || popen( $target, O_RDWR ); | 
| 595 | 4 | 100 |  |  |  | 11 | if ( defined $fh ) { | 
| 596 | 2 |  |  |  |  | 8 | pclose($target); | 
| 597 |  |  |  |  |  |  | } else { | 
| 598 | 2 |  |  |  |  | 11 | $$errRef{$target} = $!; | 
| 599 | 2 |  |  |  |  | 5 | $irv = $rv = 0; | 
| 600 |  |  |  |  |  |  | } | 
| 601 |  |  |  |  |  |  | } | 
| 602 |  |  |  |  |  |  |  | 
| 603 |  |  |  |  |  |  | # Touch the file | 
| 604 | 16 | 100 |  |  |  | 46 | if ($irv) { | 
| 605 | 14 | 50 |  |  |  | 245 | unless ( utime $stamp, $stamp, $target ) { | 
| 606 | 0 |  |  |  |  | 0 | $$errRef{$target} = $!; | 
| 607 | 0 |  |  |  |  | 0 | $rv = 0; | 
| 608 |  |  |  |  |  |  | } | 
| 609 |  |  |  |  |  |  | } | 
| 610 |  |  |  |  |  |  | } | 
| 611 |  |  |  |  |  |  | } | 
| 612 |  |  |  |  |  |  |  | 
| 613 | 7 |  |  |  |  | 30 | pOut(); | 
| 614 | 7 |  |  |  |  | 21 | pdebug( 'leaving w/rv: %s', PDLEVEL1, $rv ); | 
| 615 |  |  |  |  |  |  |  | 
| 616 | 7 |  |  |  |  | 47 | return $rv; | 
| 617 |  |  |  |  |  |  | } | 
| 618 |  |  |  |  |  |  |  | 
| 619 |  |  |  |  |  |  | sub ptouchR ($;$$\%) { | 
| 620 |  |  |  |  |  |  |  | 
| 621 |  |  |  |  |  |  | # Purpose:  Calls ptouch recursively | 
| 622 |  |  |  |  |  |  | # Returns:  True (1) if all targets were successfully touched, | 
| 623 |  |  |  |  |  |  | #           False (0) if there are any errors | 
| 624 |  |  |  |  |  |  | # Usage:    $rv = ptouchR("/foo"); | 
| 625 |  |  |  |  |  |  | # Usage:    $rv = ptouchR("/foo", $tstamp); | 
| 626 |  |  |  |  |  |  | # Usage:    $rv = ptouchR("/foo", $tstamp, $follow); | 
| 627 |  |  |  |  |  |  | # Usage:    $rv = ptouchR("/foo", $tstamp, $follow, %errors); | 
| 628 |  |  |  |  |  |  |  | 
| 629 | 3 |  |  | 3 | 1 | 8 | my $target = shift; | 
| 630 | 3 |  |  |  |  | 4 | my $stamp  = shift; | 
| 631 | 3 |  |  |  |  | 6 | my $follow = shift; | 
| 632 | 3 |  |  |  |  | 4 | my $errRef = shift; | 
| 633 | 3 |  |  |  |  | 4 | my $rv     = 1; | 
| 634 | 3 |  |  |  |  | 6 | my ( $glob, $tglob ); | 
| 635 |  |  |  |  |  |  |  | 
| 636 | 3 |  |  |  |  | 10 | pdebug( 'entering w/(%s)(%s)(%s)(%s)', | 
| 637 |  |  |  |  |  |  | PDLEVEL1, $target, $stamp, $follow, $errRef ); | 
| 638 | 3 |  |  |  |  | 8 | pIn(); | 
| 639 |  |  |  |  |  |  |  | 
| 640 |  |  |  |  |  |  | # Prep error hash | 
| 641 | 3 | 100 |  |  |  | 7 | $errRef = {} unless defined $errRef; | 
| 642 | 3 |  |  |  |  | 6 | %$errRef = (); | 
| 643 |  |  |  |  |  |  |  | 
| 644 |  |  |  |  |  |  | # Create a glob object if we weren't handed one. | 
| 645 | 3 | 50 |  |  |  | 8 | if ( defined $target ) { | 
| 646 | 3 | 100 |  |  |  | 18 | $glob = | 
| 647 |  |  |  |  |  |  | ref $target eq 'Paranoid::Glob' | 
| 648 |  |  |  |  |  |  | ? $target | 
| 649 |  |  |  |  |  |  | : Paranoid::Glob->new( globs => [$target] ); | 
| 650 |  |  |  |  |  |  | } | 
| 651 | 3 | 50 |  |  |  | 10 | $rv = 0 unless defined $glob; | 
| 652 |  |  |  |  |  |  |  | 
| 653 | 3 | 50 |  |  |  | 7 | if ($rv) { | 
| 654 |  |  |  |  |  |  |  | 
| 655 |  |  |  |  |  |  | # Load the directory tree and execute prm | 
| 656 | 3 |  | 66 |  |  | 14 | $rv = $glob->recurse( $follow, 1 ) | 
| 657 |  |  |  |  |  |  | && ptouch( $glob, $stamp, %$errRef ); | 
| 658 |  |  |  |  |  |  | } | 
| 659 |  |  |  |  |  |  |  | 
| 660 | 3 |  |  |  |  | 9 | pOut(); | 
| 661 | 3 |  |  |  |  | 8 | pdebug( 'leaving w/rv: %s', PDLEVEL1, $rv ); | 
| 662 |  |  |  |  |  |  |  | 
| 663 | 3 |  |  |  |  | 16 | return $rv; | 
| 664 |  |  |  |  |  |  | } | 
| 665 |  |  |  |  |  |  |  | 
| 666 |  |  |  |  |  |  | sub ptranslatePerms { | 
| 667 |  |  |  |  |  |  |  | 
| 668 |  |  |  |  |  |  | # Purpose:  Translates symbolic permissions (as supported by userland | 
| 669 |  |  |  |  |  |  | #           chmod, etc.) into the octal permissions. | 
| 670 |  |  |  |  |  |  | # Returns:  Numeric permissions if valid symbolic permissions were passed, | 
| 671 |  |  |  |  |  |  | #           undef otherwise | 
| 672 |  |  |  |  |  |  | # Usage:    $perm = ptranslatePerms('ug+srw'); | 
| 673 |  |  |  |  |  |  |  | 
| 674 | 21 |  |  | 21 | 1 | 4071 | my $perm = shift; | 
| 675 | 21 |  |  |  |  | 33 | my $rv   = undef; | 
| 676 | 21 |  |  |  |  | 37 | my ( @tmp, $o, $p ); | 
| 677 |  |  |  |  |  |  |  | 
| 678 | 21 |  |  |  |  | 63 | pdebug( 'entering w/(%s)', PDLEVEL1, $perm ); | 
| 679 | 21 |  |  |  |  | 58 | pIn(); | 
| 680 |  |  |  |  |  |  |  | 
| 681 |  |  |  |  |  |  | # Validate permissions string | 
| 682 | 21 | 100 | 66 |  |  | 196 | if ( defined $perm and $perm =~ /^\d+$/s ) { | 
|  |  | 100 | 66 |  |  |  |  | 
| 683 |  |  |  |  |  |  |  | 
| 684 | 12 | 100 |  |  |  | 34 | if ( $perm =~ /^0/s ) { | 
| 685 | 2 | 100 |  |  |  | 9 | if ( $perm =~ /^0[0-8]{3,4}$/s ) { | 
| 686 |  |  |  |  |  |  |  | 
| 687 |  |  |  |  |  |  | # String representation of octal number | 
| 688 | 1 |  |  |  |  | 74 | eval "\$perm = $perm;"; | 
| 689 | 1 |  |  |  |  | 10 | detaint( $perm, 'int', $p ); | 
| 690 |  |  |  |  |  |  |  | 
| 691 |  |  |  |  |  |  | } else { | 
| 692 | 1 |  |  |  |  | 4 | pdebug( 'invalid octal presentation: %s', PDLEVEL1, $perm ); | 
| 693 |  |  |  |  |  |  | } | 
| 694 |  |  |  |  |  |  |  | 
| 695 |  |  |  |  |  |  | } else { | 
| 696 |  |  |  |  |  |  |  | 
| 697 |  |  |  |  |  |  | # Probably a converted integer already, treat it as verbatim | 
| 698 | 10 |  |  |  |  | 50 | detaint( $perm, 'int', $p ); | 
| 699 |  |  |  |  |  |  | } | 
| 700 |  |  |  |  |  |  |  | 
| 701 |  |  |  |  |  |  | } elsif ( defined $perm and $perm =~ /^([ugo]+)([+\-])([rwxst]+)$/s ) { | 
| 702 |  |  |  |  |  |  |  | 
| 703 |  |  |  |  |  |  | # Translate symbolic representation | 
| 704 | 6 |  |  |  |  | 15 | $o = $p = 00; | 
| 705 | 6 |  |  |  |  | 38 | @tmp = ( $1, $2, $3 ); | 
| 706 | 6 | 100 |  |  |  | 26 | $o = S_IRWXU if $tmp[0] =~ /u/s; | 
| 707 | 6 | 100 |  |  |  | 17 | $o |= S_IRWXG if $tmp[0] =~ /g/s; | 
| 708 | 6 | 100 |  |  |  | 30 | $o |= S_IRWXO if $tmp[0] =~ /o/s; | 
| 709 | 6 | 50 |  |  |  | 20 | $p = ( S_IRUSR | S_IRGRP | S_IROTH ) if $tmp[2] =~ /r/s; | 
| 710 | 6 | 100 |  |  |  | 19 | $p |= ( S_IWUSR | S_IWGRP | S_IWOTH ) if $tmp[2] =~ /w/s; | 
| 711 | 6 | 50 |  |  |  | 21 | $p |= ( S_IXUSR | S_IXGRP | S_IXOTH ) if $tmp[2] =~ /x/s; | 
| 712 | 6 |  |  |  |  | 10 | $p &= $o; | 
| 713 | 6 | 100 |  |  |  | 16 | $p |= S_ISVTX if $tmp[2] =~ /t/s; | 
| 714 | 6 | 50 | 66 |  |  | 29 | $p |= S_ISGID if $tmp[2] =~ /s/s && $tmp[0] =~ /g/s; | 
| 715 | 6 | 100 | 66 |  |  | 27 | $p |= S_ISUID if $tmp[2] =~ /s/s && $tmp[0] =~ /u/s; | 
| 716 |  |  |  |  |  |  |  | 
| 717 |  |  |  |  |  |  | } else { | 
| 718 |  |  |  |  |  |  |  | 
| 719 |  |  |  |  |  |  | # Report invalid characters in permission string | 
| 720 | 3 |  |  |  |  | 10 | Paranoid::ERROR = | 
| 721 |  |  |  |  |  |  | pdebug( 'invalid permissions (%s)', PDLEVEL1, $perm ); | 
| 722 |  |  |  |  |  |  |  | 
| 723 |  |  |  |  |  |  | } | 
| 724 | 21 |  |  |  |  | 34 | $rv = $p; | 
| 725 |  |  |  |  |  |  |  | 
| 726 | 21 |  |  |  |  | 60 | pOut(); | 
| 727 | 21 | 100 |  |  |  | 147 | pdebug( ( | 
| 728 |  |  |  |  |  |  | defined $rv | 
| 729 |  |  |  |  |  |  | ? sprintf( 'leaving w/rv: %04o', $rv ) | 
| 730 |  |  |  |  |  |  | : 'leaving w/rv: undef' | 
| 731 |  |  |  |  |  |  | ), | 
| 732 |  |  |  |  |  |  | PDLEVEL1 | 
| 733 |  |  |  |  |  |  | ); | 
| 734 |  |  |  |  |  |  |  | 
| 735 | 21 |  |  |  |  | 65 | return $rv; | 
| 736 |  |  |  |  |  |  | } | 
| 737 |  |  |  |  |  |  |  | 
| 738 |  |  |  |  |  |  | sub pchmod ($$;\%) { | 
| 739 |  |  |  |  |  |  |  | 
| 740 |  |  |  |  |  |  | # Purpose:  Simulates a "chmod" command in pure Perl | 
| 741 |  |  |  |  |  |  | # Returns:  True (1) if all targets were successfully chmod'd, | 
| 742 |  |  |  |  |  |  | #           False (0) if there are any errors | 
| 743 |  |  |  |  |  |  | # Usage:    $rv = pchmod("/foo", $perms); | 
| 744 |  |  |  |  |  |  | # Usage:    $rv = pchmod("/foo", $perms, %errors); | 
| 745 |  |  |  |  |  |  |  | 
| 746 | 8 |  |  | 8 | 1 | 16 | my $target = shift; | 
| 747 | 8 |  |  |  |  | 17 | my $perms  = shift; | 
| 748 | 8 |  |  |  |  | 14 | my $errRef = shift; | 
| 749 | 8 |  |  |  |  | 11 | my $rv     = 1; | 
| 750 | 8 |  |  |  |  | 19 | my ( $glob, $tglob, @fstat ); | 
| 751 | 8 |  |  |  |  | 0 | my ( $ptrans, $cperms, $addPerms, @tmp ); | 
| 752 |  |  |  |  |  |  |  | 
| 753 | 8 |  |  |  |  | 25 | pdebug( 'entering w/(%s)(%s)(%s)', PDLEVEL1, $target, $perms, $errRef ); | 
| 754 | 8 |  |  |  |  | 20 | pIn(); | 
| 755 |  |  |  |  |  |  |  | 
| 756 |  |  |  |  |  |  | # Prep error hash | 
| 757 | 8 | 100 |  |  |  | 19 | $errRef = {} unless defined $errRef; | 
| 758 | 8 |  |  |  |  | 15 | %$errRef = (); | 
| 759 |  |  |  |  |  |  |  | 
| 760 |  |  |  |  |  |  | # Create a glob object if we weren't handed one. | 
| 761 | 8 | 50 |  |  |  | 16 | if ( defined $target ) { | 
| 762 | 8 | 100 |  |  |  | 27 | $glob = | 
| 763 |  |  |  |  |  |  | ref $target eq 'Paranoid::Glob' | 
| 764 |  |  |  |  |  |  | ? $target | 
| 765 |  |  |  |  |  |  | : Paranoid::Glob->new( globs => [$target] ); | 
| 766 |  |  |  |  |  |  | } | 
| 767 | 8 | 50 |  |  |  | 17 | $rv = 0 unless defined $glob; | 
| 768 |  |  |  |  |  |  |  | 
| 769 |  |  |  |  |  |  | # Convert perms if they're symbolic | 
| 770 | 8 | 50 | 33 |  |  | 32 | if ( defined $perms and defined( $ptrans = ptranslatePerms($perms) ) ) { | 
| 771 | 8 | 100 |  |  |  | 32 | if ( $perms =~ /[ugo]+[+-]/si ) { | 
| 772 | 3 | 50 |  |  |  | 10 | $addPerms = $perms =~ /-/s ? 0 : 1; | 
| 773 |  |  |  |  |  |  | } else { | 
| 774 | 5 |  |  |  |  | 11 | $ptrans = undef; | 
| 775 |  |  |  |  |  |  | } | 
| 776 |  |  |  |  |  |  | } else { | 
| 777 | 0 |  |  |  |  | 0 | pdebug( 'invalid permissions passed: %s', PDLEVEL1, $perms ); | 
| 778 | 0 |  |  |  |  | 0 | $rv = 0; | 
| 779 |  |  |  |  |  |  | } | 
| 780 |  |  |  |  |  |  |  | 
| 781 | 8 | 50 |  |  |  | 33 | if ($rv) { | 
| 782 |  |  |  |  |  |  |  | 
| 783 |  |  |  |  |  |  | # Consolidate the entries | 
| 784 | 8 |  |  |  |  | 34 | $glob->consolidate; | 
| 785 |  |  |  |  |  |  |  | 
| 786 |  |  |  |  |  |  | # Iterate over entries | 
| 787 | 8 |  |  |  |  | 19 | foreach (@$glob) { | 
| 788 | 20 |  |  |  |  | 73 | pdebug( 'processing %s', PDLEVEL2, $_ ); | 
| 789 |  |  |  |  |  |  |  | 
| 790 | 20 | 100 |  |  |  | 50 | if ( defined $ptrans ) { | 
| 791 |  |  |  |  |  |  |  | 
| 792 |  |  |  |  |  |  | # Get the current file mode | 
| 793 | 8 |  |  |  |  | 134 | @fstat = stat $_; | 
| 794 | 8 | 100 |  |  |  | 28 | unless (@fstat) { | 
| 795 | 1 |  |  |  |  | 3 | $rv = 0; | 
| 796 | 1 |  |  |  |  | 47 | $$errRef{$_} = $!; | 
| 797 | 1 |  |  |  |  | 7 | Paranoid::ERROR = | 
| 798 |  |  |  |  |  |  | pdebug( 'failed to adjust permissions of %s: %s', | 
| 799 |  |  |  |  |  |  | PDLEVEL1, $_, $! ); | 
| 800 | 1 |  |  |  |  | 4 | next; | 
| 801 |  |  |  |  |  |  | } | 
| 802 |  |  |  |  |  |  |  | 
| 803 |  |  |  |  |  |  | # If ptrans is defined we're going to do relative | 
| 804 |  |  |  |  |  |  | # application of permissions | 
| 805 |  |  |  |  |  |  | pdebug( | 
| 806 | 7 | 50 |  |  |  | 46 | $addPerms | 
| 807 |  |  |  |  |  |  | ? sprintf( 'adding perms %04o',   $ptrans ) | 
| 808 |  |  |  |  |  |  | : sprintf( 'removing perms %04o', $ptrans ), | 
| 809 |  |  |  |  |  |  | PDLEVEL2 | 
| 810 |  |  |  |  |  |  | ); | 
| 811 |  |  |  |  |  |  |  | 
| 812 |  |  |  |  |  |  | # Get the current permissions | 
| 813 | 7 |  |  |  |  | 15 | $cperms = $fstat[2] & PERMMASK; | 
| 814 | 7 |  |  |  |  | 32 | pdebug( | 
| 815 |  |  |  |  |  |  | sprintf( 'current permissions of %s: %04o', $_, $cperms ), | 
| 816 |  |  |  |  |  |  | PDLEVEL2 | 
| 817 |  |  |  |  |  |  | ); | 
| 818 | 7 | 50 |  |  |  | 17 | $cperms = | 
| 819 |  |  |  |  |  |  | $addPerms | 
| 820 |  |  |  |  |  |  | ? ( $cperms | $ptrans ) | 
| 821 |  |  |  |  |  |  | : ( $cperms & ( PERMMASK ^ $ptrans ) ); | 
| 822 | 7 |  |  |  |  | 28 | pdebug( sprintf( 'new permissions of %s: %04o', $_, $cperms ), | 
| 823 |  |  |  |  |  |  | PDLEVEL2 ); | 
| 824 | 7 | 50 |  |  |  | 161 | unless ( chmod $cperms, $_ ) { | 
| 825 | 0 |  |  |  |  | 0 | $rv = 0; | 
| 826 | 0 |  |  |  |  | 0 | $$errRef{$_} = $!; | 
| 827 | 0 |  |  |  |  | 0 | Paranoid::ERROR = | 
| 828 |  |  |  |  |  |  | pdebug( 'failed to adjust permissions of %s: %s', | 
| 829 |  |  |  |  |  |  | PDLEVEL1, $_, $! ); | 
| 830 |  |  |  |  |  |  | } | 
| 831 |  |  |  |  |  |  |  | 
| 832 |  |  |  |  |  |  | } else { | 
| 833 |  |  |  |  |  |  |  | 
| 834 |  |  |  |  |  |  | # Otherwise, the permissions are explicit | 
| 835 |  |  |  |  |  |  | # | 
| 836 |  |  |  |  |  |  | # Detaint number mode | 
| 837 | 12 | 50 |  |  |  | 35 | if ( detaint( $perms, 'int' ) ) { | 
| 838 |  |  |  |  |  |  |  | 
| 839 |  |  |  |  |  |  | # Detainted, now apply | 
| 840 | 12 |  |  |  |  | 60 | pdebug( | 
| 841 |  |  |  |  |  |  | sprintf( | 
| 842 |  |  |  |  |  |  | 'assigning permissions of %04o to %s', | 
| 843 |  |  |  |  |  |  | $perms, $_ | 
| 844 |  |  |  |  |  |  | ), | 
| 845 |  |  |  |  |  |  | PDLEVEL2 | 
| 846 |  |  |  |  |  |  | ); | 
| 847 | 12 | 100 |  |  |  | 269 | unless ( chmod $perms, $_ ) { | 
| 848 | 2 |  |  |  |  | 6 | $rv = 0; | 
| 849 | 2 |  |  |  |  | 16 | $$errRef{$_} = $!; | 
| 850 |  |  |  |  |  |  | } | 
| 851 |  |  |  |  |  |  | } else { | 
| 852 |  |  |  |  |  |  |  | 
| 853 |  |  |  |  |  |  | # Detainting failed -- report | 
| 854 | 0 |  |  |  |  | 0 | $$errRef{$_} = $!; | 
| 855 | 0 |  |  |  |  | 0 | Paranoid::ERROR = | 
| 856 |  |  |  |  |  |  | pdebug( 'failed to detaint permissions mode', | 
| 857 |  |  |  |  |  |  | PDLEVEL1 ); | 
| 858 | 0 |  |  |  |  | 0 | $rv = 0; | 
| 859 |  |  |  |  |  |  | } | 
| 860 |  |  |  |  |  |  | } | 
| 861 |  |  |  |  |  |  | } | 
| 862 |  |  |  |  |  |  | } | 
| 863 |  |  |  |  |  |  |  | 
| 864 | 8 |  |  |  |  | 36 | pOut(); | 
| 865 | 8 |  |  |  |  | 22 | pdebug( 'leaving w/rv: %s', PDLEVEL1, $rv ); | 
| 866 |  |  |  |  |  |  |  | 
| 867 | 8 |  |  |  |  | 50 | return $rv; | 
| 868 |  |  |  |  |  |  | } | 
| 869 |  |  |  |  |  |  |  | 
| 870 |  |  |  |  |  |  | sub pchmodR ($$;$\%) { | 
| 871 |  |  |  |  |  |  |  | 
| 872 |  |  |  |  |  |  | # Purpose:  Recursively calls pchmod | 
| 873 |  |  |  |  |  |  | # Returns:  True (1) if all targets were successfully chmod'd, | 
| 874 |  |  |  |  |  |  | #           False (0) if there are any errors | 
| 875 |  |  |  |  |  |  | # Usage:    $rv = pchmodR("/foo", $perms); | 
| 876 |  |  |  |  |  |  | # Usage:    $rv = pchmodR("/foo", $perms, $follow); | 
| 877 |  |  |  |  |  |  | # Usage:    $rv = pchmodR("/foo", $perms, $follow, %errors); | 
| 878 |  |  |  |  |  |  |  | 
| 879 | 4 |  |  | 4 | 1 | 25 | my $target = shift; | 
| 880 | 4 |  |  |  |  | 10 | my $perms  = shift; | 
| 881 | 4 |  |  |  |  | 7 | my $follow = shift; | 
| 882 | 4 |  |  |  |  | 6 | my $errRef = shift; | 
| 883 | 4 |  |  |  |  | 5 | my $rv     = 1; | 
| 884 | 4 |  |  |  |  | 9 | my ( $glob, $tglob ); | 
| 885 |  |  |  |  |  |  |  | 
| 886 | 4 |  |  |  |  | 15 | pdebug( 'entering w/(%s)(%s)(%s)(%s)', | 
| 887 |  |  |  |  |  |  | PDLEVEL1, $target, $perms, $follow, $errRef ); | 
| 888 | 4 |  |  |  |  | 13 | pIn(); | 
| 889 |  |  |  |  |  |  |  | 
| 890 |  |  |  |  |  |  | # Prep error hash | 
| 891 | 4 | 100 |  |  |  | 11 | $errRef = {} unless defined $errRef; | 
| 892 | 4 |  |  |  |  | 9 | %$errRef = (); | 
| 893 |  |  |  |  |  |  |  | 
| 894 |  |  |  |  |  |  | # Create a glob object if we weren't handed one. | 
| 895 | 4 | 50 |  |  |  | 10 | if ( defined $target ) { | 
| 896 | 4 | 50 |  |  |  | 29 | $glob = | 
| 897 |  |  |  |  |  |  | ref $target eq 'Paranoid::Glob' | 
| 898 |  |  |  |  |  |  | ? $target | 
| 899 |  |  |  |  |  |  | : Paranoid::Glob->new( globs => [$target] ); | 
| 900 |  |  |  |  |  |  | } | 
| 901 | 4 | 50 |  |  |  | 14 | $rv = 0 unless defined $glob; | 
| 902 |  |  |  |  |  |  |  | 
| 903 | 4 | 50 |  |  |  | 10 | if ($rv) { | 
| 904 |  |  |  |  |  |  |  | 
| 905 |  |  |  |  |  |  | # Load the directory tree and execute pchmod | 
| 906 | 4 |  | 66 |  |  | 25 | $rv = $glob->recurse( $follow, 1 ) | 
| 907 |  |  |  |  |  |  | && pchmod( $glob, $perms, %$errRef ); | 
| 908 |  |  |  |  |  |  | } | 
| 909 |  |  |  |  |  |  |  | 
| 910 | 4 |  |  |  |  | 13 | pOut(); | 
| 911 | 4 |  |  |  |  | 11 | pdebug( 'leaving w/rv: %s', PDLEVEL1, $rv ); | 
| 912 |  |  |  |  |  |  |  | 
| 913 | 4 |  |  |  |  | 27 | return $rv; | 
| 914 |  |  |  |  |  |  | } | 
| 915 |  |  |  |  |  |  |  | 
| 916 |  |  |  |  |  |  | sub pchown ($$;$\%) { | 
| 917 |  |  |  |  |  |  |  | 
| 918 |  |  |  |  |  |  | # Purpose:  Simulates a "chown" command in pure Perl | 
| 919 |  |  |  |  |  |  | # Returns:  True (1) if all targets were successfully owned, | 
| 920 |  |  |  |  |  |  | #           False (0) if there are any errors | 
| 921 |  |  |  |  |  |  | # Usage:    $rv = pchown("/foo", $user); | 
| 922 |  |  |  |  |  |  | # Usage:    $rv = pchown("/foo", $user, $group); | 
| 923 |  |  |  |  |  |  | # Usage:    $rv = pchown("/foo", $user, $group, %errors); | 
| 924 |  |  |  |  |  |  |  | 
| 925 | 0 |  |  | 0 | 1 | 0 | my $target = shift; | 
| 926 | 0 |  |  |  |  | 0 | my $user   = shift; | 
| 927 | 0 |  |  |  |  | 0 | my $group  = shift; | 
| 928 | 0 |  |  |  |  | 0 | my $errRef = shift; | 
| 929 | 0 |  |  |  |  | 0 | my $rv     = 1; | 
| 930 | 0 |  |  |  |  | 0 | my ( $glob, $tglob, @fstat ); | 
| 931 |  |  |  |  |  |  |  | 
| 932 | 0 |  |  |  |  | 0 | pdebug( 'entering w/(%s)(%s)(%s)(%s)', | 
| 933 |  |  |  |  |  |  | PDLEVEL1, $target, $user, $group, $errRef ); | 
| 934 | 0 |  |  |  |  | 0 | pIn(); | 
| 935 |  |  |  |  |  |  |  | 
| 936 |  |  |  |  |  |  | # Translate to UID/GID | 
| 937 | 0 | 0 |  |  |  | 0 | $user  = -1 unless defined $user; | 
| 938 | 0 | 0 |  |  |  | 0 | $group = -1 unless defined $group; | 
| 939 | 0 | 0 |  |  |  | 0 | $user  = ptranslateUser($user)   unless $user  =~ /^-?\d+$/s; | 
| 940 | 0 | 0 |  |  |  | 0 | $group = ptranslateGroup($group) unless $group =~ /^-?\d+$/s; | 
| 941 | 0 | 0 | 0 |  |  | 0 | unless ( defined $user and defined $group ) { | 
| 942 | 0 |  |  |  |  | 0 | $rv = 0; | 
| 943 | 0 |  |  |  |  | 0 | Paranoid::ERROR = | 
| 944 |  |  |  |  |  |  | pdebug( 'unsuccessful at translating uid/gid', PDLEVEL1 ); | 
| 945 |  |  |  |  |  |  | } | 
| 946 |  |  |  |  |  |  |  | 
| 947 |  |  |  |  |  |  | # Prep error hash | 
| 948 | 0 | 0 |  |  |  | 0 | $errRef = {} unless defined $errRef; | 
| 949 | 0 |  |  |  |  | 0 | %$errRef = (); | 
| 950 |  |  |  |  |  |  |  | 
| 951 |  |  |  |  |  |  | # Create a glob object if we weren't handed one. | 
| 952 | 0 | 0 |  |  |  | 0 | if ( defined $target ) { | 
| 953 | 0 | 0 |  |  |  | 0 | $glob = | 
| 954 |  |  |  |  |  |  | ref $target eq 'Paranoid::Glob' | 
| 955 |  |  |  |  |  |  | ? $target | 
| 956 |  |  |  |  |  |  | : Paranoid::Glob->new( globs => [$target] ); | 
| 957 |  |  |  |  |  |  | } | 
| 958 | 0 | 0 |  |  |  | 0 | $rv = 0 unless defined $glob; | 
| 959 |  |  |  |  |  |  |  | 
| 960 | 0 | 0 | 0 |  |  | 0 | if ( $rv and ( $user != -1 or $group != -1 ) ) { | 
|  |  |  | 0 |  |  |  |  | 
| 961 |  |  |  |  |  |  |  | 
| 962 |  |  |  |  |  |  | # Proceed | 
| 963 | 0 |  |  |  |  | 0 | pdebug( 'UID: %s GID: %s', PDLEVEL2, $user, $group ); | 
| 964 |  |  |  |  |  |  |  | 
| 965 |  |  |  |  |  |  | # Consolidate the entries | 
| 966 | 0 |  |  |  |  | 0 | $glob->consolidate; | 
| 967 |  |  |  |  |  |  |  | 
| 968 |  |  |  |  |  |  | # Process the list | 
| 969 | 0 |  |  |  |  | 0 | foreach (@$glob) { | 
| 970 |  |  |  |  |  |  |  | 
| 971 | 0 |  |  |  |  | 0 | pdebug( 'processing %s', PDLEVEL2, $_ ); | 
| 972 |  |  |  |  |  |  |  | 
| 973 | 0 | 0 |  |  |  | 0 | unless ( chown $user, $group, $_ ) { | 
| 974 | 0 |  |  |  |  | 0 | $rv = 0; | 
| 975 | 0 |  |  |  |  | 0 | $$errRef{$_} = $!; | 
| 976 | 0 |  |  |  |  | 0 | Paranoid::ERROR = | 
| 977 |  |  |  |  |  |  | pdebug( 'failed to adjust ownership of %s: %s', | 
| 978 |  |  |  |  |  |  | PDLEVEL1, $_, $! ); | 
| 979 |  |  |  |  |  |  | } | 
| 980 |  |  |  |  |  |  | } | 
| 981 |  |  |  |  |  |  | } | 
| 982 |  |  |  |  |  |  |  | 
| 983 | 0 |  |  |  |  | 0 | pOut(); | 
| 984 | 0 |  |  |  |  | 0 | pdebug( 'leaving w/rv: %s', PDLEVEL1, $rv ); | 
| 985 |  |  |  |  |  |  |  | 
| 986 | 0 |  |  |  |  | 0 | return $rv; | 
| 987 |  |  |  |  |  |  | } | 
| 988 |  |  |  |  |  |  |  | 
| 989 |  |  |  |  |  |  | sub pchownR ($$;$$\%) { | 
| 990 |  |  |  |  |  |  |  | 
| 991 |  |  |  |  |  |  | # Purpose:  Calls pchown recursively | 
| 992 |  |  |  |  |  |  | # Returns:  True (1) if all targets were successfully owned, | 
| 993 |  |  |  |  |  |  | #           False (0) if there are any errors | 
| 994 |  |  |  |  |  |  | # Usage:    $rv = pchownR("/foo", $user); | 
| 995 |  |  |  |  |  |  | # Usage:    $rv = pchownR("/foo", $user, $group); | 
| 996 |  |  |  |  |  |  | # Usage:    $rv = pchownR("/foo", $user, $group, $follow); | 
| 997 |  |  |  |  |  |  | # Usage:    $rv = pchownR("/foo", $user, $group, $follow, %errors); | 
| 998 |  |  |  |  |  |  |  | 
| 999 | 0 |  |  | 0 | 1 | 0 | my $target = shift; | 
| 1000 | 0 |  |  |  |  | 0 | my $user   = shift; | 
| 1001 | 0 |  |  |  |  | 0 | my $group  = shift; | 
| 1002 | 0 |  |  |  |  | 0 | my $follow = shift; | 
| 1003 | 0 |  |  |  |  | 0 | my $errRef = shift; | 
| 1004 | 0 |  |  |  |  | 0 | my $rv     = 1; | 
| 1005 | 0 |  |  |  |  | 0 | my ( $glob, $tglob ); | 
| 1006 |  |  |  |  |  |  |  | 
| 1007 | 0 |  |  |  |  | 0 | pdebug( 'entering w/(%s)(%s)(%s)(%s)(%s)', | 
| 1008 |  |  |  |  |  |  | PDLEVEL1, $target, $user, $group, $follow, $errRef ); | 
| 1009 | 0 |  |  |  |  | 0 | pIn(); | 
| 1010 |  |  |  |  |  |  |  | 
| 1011 |  |  |  |  |  |  | # Prep error hash | 
| 1012 | 0 | 0 |  |  |  | 0 | $errRef = {} unless defined $errRef; | 
| 1013 | 0 |  |  |  |  | 0 | %$errRef = (); | 
| 1014 |  |  |  |  |  |  |  | 
| 1015 |  |  |  |  |  |  | # Create a glob object if we weren't handed one. | 
| 1016 | 0 | 0 |  |  |  | 0 | if ( defined $target ) { | 
| 1017 | 0 | 0 |  |  |  | 0 | $glob = | 
| 1018 |  |  |  |  |  |  | ref $target eq 'Paranoid::Glob' | 
| 1019 |  |  |  |  |  |  | ? $target | 
| 1020 |  |  |  |  |  |  | : Paranoid::Glob->new( globs => [$target] ); | 
| 1021 |  |  |  |  |  |  | } | 
| 1022 | 0 | 0 |  |  |  | 0 | $rv = 0 unless defined $glob; | 
| 1023 |  |  |  |  |  |  |  | 
| 1024 | 0 | 0 |  |  |  | 0 | if ($rv) { | 
| 1025 |  |  |  |  |  |  |  | 
| 1026 |  |  |  |  |  |  | # Load the directory tree and execute pchown | 
| 1027 | 0 |  | 0 |  |  | 0 | $rv = $glob->recurse( $follow, 1 ) | 
| 1028 |  |  |  |  |  |  | && pchown( $glob, $user, $group, %$errRef ); | 
| 1029 |  |  |  |  |  |  | } | 
| 1030 |  |  |  |  |  |  |  | 
| 1031 | 0 |  |  |  |  | 0 | pOut(); | 
| 1032 | 0 |  |  |  |  | 0 | pdebug( 'leaving w/rv: %s', PDLEVEL1, $rv ); | 
| 1033 |  |  |  |  |  |  |  | 
| 1034 | 0 |  |  |  |  | 0 | return $rv; | 
| 1035 |  |  |  |  |  |  | } | 
| 1036 |  |  |  |  |  |  |  | 
| 1037 |  |  |  |  |  |  | sub pwhich { | 
| 1038 |  |  |  |  |  |  |  | 
| 1039 |  |  |  |  |  |  | # Purpose:  Simulates a "which" command in pure Perl | 
| 1040 |  |  |  |  |  |  | # Returns:  The full path to the requested program if successful | 
| 1041 |  |  |  |  |  |  | #           undef if not found | 
| 1042 |  |  |  |  |  |  | # Usage:    $filename = pwhich('ls'); | 
| 1043 |  |  |  |  |  |  |  | 
| 1044 | 2 |  |  | 2 | 1 | 1237 | my $binary      = shift; | 
| 1045 | 2 |  |  |  |  | 22 | my @directories = grep /^.+$/s, split /:/s, $ENV{PATH}; | 
| 1046 | 2 |  |  |  |  | 6 | my $match       = undef; | 
| 1047 |  |  |  |  |  |  |  | 
| 1048 | 2 |  |  |  |  | 7 | pdebug( 'entering w/(%s)', PDLEVEL1, $binary ); | 
| 1049 | 2 |  |  |  |  | 5 | pIn(); | 
| 1050 |  |  |  |  |  |  |  | 
| 1051 |  |  |  |  |  |  | # Try to detaint filename | 
| 1052 | 2 | 50 |  |  |  | 9 | if ( detaint( $binary, 'filename', $b ) ) { | 
| 1053 |  |  |  |  |  |  |  | 
| 1054 |  |  |  |  |  |  | # Success -- start searching directories in PATH | 
| 1055 | 2 |  |  |  |  | 6 | foreach (@directories) { | 
| 1056 | 3 |  |  |  |  | 11 | pdebug( 'searching %s', PDLEVEL2, $_ ); | 
| 1057 | 3 | 100 | 66 |  |  | 149 | if ( -r "$_/$b" && -x _ ) { | 
| 1058 | 1 |  |  |  |  | 27 | $match = "$_/$b"; | 
| 1059 | 1 |  |  |  |  | 9 | $match =~ s#/+#/#sg; | 
| 1060 | 1 |  |  |  |  | 4 | last; | 
| 1061 |  |  |  |  |  |  | } | 
| 1062 |  |  |  |  |  |  | } | 
| 1063 |  |  |  |  |  |  |  | 
| 1064 |  |  |  |  |  |  | } else { | 
| 1065 |  |  |  |  |  |  |  | 
| 1066 |  |  |  |  |  |  | # Report detaint failure | 
| 1067 | 0 |  |  |  |  | 0 | Paranoid::ERROR = pdebug( 'failed to detaint %s', PDLEVEL1, $binary ); | 
| 1068 |  |  |  |  |  |  | } | 
| 1069 |  |  |  |  |  |  |  | 
| 1070 | 2 |  |  |  |  | 11 | pOut(); | 
| 1071 | 2 |  |  |  |  | 12 | pdebug( 'leaving w/rv: %s', PDLEVEL1, $match ); | 
| 1072 |  |  |  |  |  |  |  | 
| 1073 | 2 |  |  |  |  | 7 | return $match; | 
| 1074 |  |  |  |  |  |  | } | 
| 1075 |  |  |  |  |  |  |  | 
| 1076 |  |  |  |  |  |  | 1; | 
| 1077 |  |  |  |  |  |  |  | 
| 1078 |  |  |  |  |  |  | __END__ |