📄 temp.pm
字号:
if ( $open_success ) { # Reset umask umask($umask); # Opened successfully - return file handle and name return ($fh, $path); } else { # Reset umask umask($umask); # Error opening file - abort with error # if the reason was anything but EEXIST unless ($!{EEXIST}) { ${$options{ErrStr}} = "Could not create temp file $path: $!"; return (); } # Loop round for another try } } elsif ($options{"mkdir"}) { # Store callers umask my $umask = umask(); # Set a known umask umask(066); # Open the temp directory if (mkdir( $path, 0700)) { # created okay # Reset umask umask($umask); return undef, $path; } else { # Reset umask umask($umask); # Abort with error if the reason for failure was anything # except EEXIST unless ($!{EEXIST}) { ${$options{ErrStr}} = "Could not create directory $path: $!"; return (); } # Loop round for another try } } else { # Return true if the file can not be found # Directory has been checked previously return (undef, $path) unless -e $path; # Try again until MAX_TRIES } # Did not successfully open the tempfile/dir # so try again with a different set of random letters # No point in trying to increment unless we have only # 1 X say and the randomness could come up with the same # file MAX_TRIES in a row. # Store current attempt - in principal this implies that the # 3rd time around the open attempt that the first temp file # name could be generated again. Probably should store each # attempt and make sure that none are repeated my $original = $path; my $counter = 0; # Stop infinite loop my $MAX_GUESS = 50; do { # Generate new name from original template $path = _replace_XX($template, $options{"suffixlen"}); $counter++; } until ($path ne $original || $counter > $MAX_GUESS); # Check for out of control looping if ($counter > $MAX_GUESS) { ${$options{ErrStr}} = "Tried to get a new temp name different to the previous value $MAX_GUESS times.\nSomething wrong with template?? ($template)"; return (); } } # If we get here, we have run out of tries ${ $options{ErrStr} } = "Have exceeded the maximum number of attempts (" . MAX_TRIES . ") to open temp file/dir"; return ();}# Internal routine to return a random character from the# character list. Does not do an srand() since rand()# will do one automatically# No arguments. Return value is the random character# No longer called since _replace_XX runs a few percent faster if# I inline the code. This is important if we are creating thousands of# temporary files.sub _randchar { $CHARS[ int( rand( $#CHARS ) ) ];}# Internal routine to replace the XXXX... with random characters# This has to be done by _gettemp() every time it fails to # open a temp file/dir# Arguments: $template (the template with XXX), # $ignore (number of characters at end to ignore)# Returns: modified templatesub _replace_XX { croak 'Usage: _replace_XX($template, $ignore)' unless scalar(@_) == 2; my ($path, $ignore) = @_; # Do it as an if, since the suffix adjusts which section to replace # and suffixlen=0 returns nothing if used in the substr directly # Alternatively, could simply set $ignore to length($path)-1 # Don't want to always use substr when not required though. if ($ignore) { substr($path, 0, - $ignore) =~ s/X(?=X*\z)/$CHARS[ int( rand( $#CHARS ) ) ]/ge; } else { $path =~ s/X(?=X*\z)/$CHARS[ int( rand( $#CHARS ) ) ]/ge; } return $path;}# internal routine to check to see if the directory is safe# First checks to see if the directory is not owned by the# current user or root. Then checks to see if anyone else# can write to the directory and if so, checks to see if# it has the sticky bit set# Will not work on systems that do not support sticky bit#Args: directory path to check# Optionally: reference to scalar to contain error message# Returns true if the path is safe and false otherwise.# Returns undef if can not even run stat() on the path# This routine based on version written by Tom Christiansen# Presumably, by the time we actually attempt to create the# file or directory in this directory, it may not be safe# anymore... Have to run _is_safe directly after the open.sub _is_safe { my $path = shift; my $err_ref = shift; # Stat path my @info = stat($path); unless (scalar(@info)) { $$err_ref = "stat(path) returned no values"; return 0; }; return 1 if $^O eq 'VMS'; # owner delete control at file level # Check to see whether owner is neither superuser (or a system uid) nor me # Use the real uid from the $< variable # UID is in [4] if ($info[4] > File::Temp->top_system_uid() && $info[4] != $<) { Carp::cluck(sprintf "uid=$info[4] topuid=%s \$<=$< path='$path'", File::Temp->top_system_uid()); $$err_ref = "Directory owned neither by root nor the current user" if ref($err_ref); return 0; } # check whether group or other can write file # use 066 to detect either reading or writing # use 022 to check writability # Do it with S_IWOTH and S_IWGRP for portability (maybe) # mode is in info[2] if (($info[2] & &Fcntl::S_IWGRP) || # Is group writable? ($info[2] & &Fcntl::S_IWOTH) ) { # Is world writable? # Must be a directory unless (-d _) { $$err_ref = "Path ($path) is not a directory" if ref($err_ref); return 0; } # Must have sticky bit set unless (-k _) { $$err_ref = "Sticky bit not set on $path when dir is group|world writable" if ref($err_ref); return 0; } } return 1;}# Internal routine to check whether a directory is safe# for temp files. Safer than _is_safe since it checks for # the possibility of chown giveaway and if that is a possibility# checks each directory in the path to see if it is safe (with _is_safe)# If _PC_CHOWN_RESTRICTED is not set, does the full test of each# directory anyway.# Takes optional second arg as scalar ref to error reasonsub _is_verysafe { # Need POSIX - but only want to bother if really necessary due to overhead require POSIX; my $path = shift; print "_is_verysafe testing $path\n" if $DEBUG; return 1 if $^O eq 'VMS'; # owner delete control at file level my $err_ref = shift; # Should Get the value of _PC_CHOWN_RESTRICTED if it is defined # and If it is not there do the extensive test my $chown_restricted; $chown_restricted = &POSIX::_PC_CHOWN_RESTRICTED() if eval { &POSIX::_PC_CHOWN_RESTRICTED(); 1}; # If chown_resticted is set to some value we should test it if (defined $chown_restricted) { # Return if the current directory is safe return _is_safe($path,$err_ref) if POSIX::sysconf( $chown_restricted ); } # To reach this point either, the _PC_CHOWN_RESTRICTED symbol # was not avialable or the symbol was there but chown giveaway # is allowed. Either way, we now have to test the entire tree for # safety. # Convert path to an absolute directory if required unless (File::Spec->file_name_is_absolute($path)) { $path = File::Spec->rel2abs($path); } # Split directory into components - assume no file my ($volume, $directories, undef) = File::Spec->splitpath( $path, 1); # Slightly less efficient than having a a function in File::Spec # to chop off the end of a directory or even a function that # can handle ../ in a directory tree # Sometimes splitdir() returns a blank at the end # so we will probably check the bottom directory twice in some cases my @dirs = File::Spec->splitdir($directories); # Concatenate one less directory each time around foreach my $pos (0.. $#dirs) { # Get a directory name my $dir = File::Spec->catpath($volume, File::Spec->catdir(@dirs[0.. $#dirs - $pos]), '' ); print "TESTING DIR $dir\n" if $DEBUG; # Check the directory return 0 unless _is_safe($dir,$err_ref); } return 1;}# internal routine to determine whether unlink works on this# platform for files that are currently open.# Returns true if we can, false otherwise.# Currently WinNT, OS/2 and VMS can not unlink an opened file# On VMS this is because the O_EXCL flag is used to open the# temporary file. Currently I do not know enough about the issues# on VMS to decide whether O_EXCL is a requirement.sub _can_unlink_opened_file { if ($^O eq 'MSWin32' || $^O eq 'os2' || $^O eq 'VMS' || $^O eq 'dos') { return 0; } else { return 1; }}# internal routine to decide which security levels are allowed# see safe_level() for more information on this# Controls whether the supplied security level is allowed# $cando = _can_do_level( $level )sub _can_do_level { # Get security level my $level = shift; # Always have to be able to do STANDARD return 1 if $level == STANDARD; # Currently, the systems that can do HIGH or MEDIUM are identical if ( $^O eq 'MSWin32' || $^O eq 'os2' || $^O eq 'cygwin' || $^O eq 'dos') { return 0; } else { return 1; }}# This routine sets up a deferred unlinking of a specified# filename and filehandle. It is used in the following cases:# - Called by unlink0 if an opened file can not be unlinked# - Called by tempfile() if files are to be removed on shutdown# - Called by tempdir() if directories are to be removed on shutdown# Arguments:# _deferred_unlink( $fh, $fname, $isdir );## - filehandle (so that it can be expclicitly closed if open# - filename (the thing we want to remove)# - isdir (flag to indicate that we are being given a directory)# [and hence no filehandle]# Status is not referred to since all the magic is done with an END block{ # Will set up two lexical variables to contain all the files to be # removed. One array for files, another for directories # They will only exist in this block # This means we only have to set up a single END block to remove all files # @files_to_unlink contains an array ref with the filehandle and filename my (@files_to_unlink, @dirs_to_unlink); # Set up an end block to use these arrays END { # Files foreach my $file (@files_to_unlink) { # close the filehandle without checking its state # in order to make real sure that this is closed # if its already closed then I dont care about the answer # probably a better way to do this close($file->[0]); # file handle is [0] if (-f $file->[1]) { # file name is [1] unlink $file->[1] or warn "Error removing ".$file->[1]; } } # Dirs foreach my $dir (@dirs_to_unlink) { if (-d $dir) { rmtree($dir, $DEBUG, 1); } } } # This is the sub called to register a file for deferred unlinking # This could simply store the input parameters and defer everything # until the END block. For now we do a bit of checking at this # point in order to make sure that (1) we have a file/dir to delete # and (2) we have been called with the correct arguments. sub _deferred_unlink { croak 'Usage: _deferred_unlink($fh, $fname, $isdir)' unless scalar(@_) == 3; my ($fh, $fname, $isdir) = @_; warn "Setting up deferred removal of $fname\n" if $DEBUG; # If we have a directory, check that it is a directory if ($isdir) { if (-d $fname) { # Directory exists so store it # first on VMS turn []foo into [.foo] for rmtree $fname = VMS::Filespec::vmspath($fname) if $^O eq 'VMS'; push (@dirs_to_unlink, $fname); } else { carp "Request to remove directory $fname could not be completed since it does not exist!\n" if $^W; } } else { if (-f $fname) { # file exists so store handle and name for later removal push(@files_to_unlink, [$fh, $fname]); } else { carp "Request to remove file $fname could not be completed since it is not there!\n" if $^W; } } }}=head1 FUNCTIONSThis section describes the recommended interface for generatingtemporary files and directories.=over 4=item B<tempfile>This is the basic function to generate temporary files.The behaviour of the file can be changed using various options: ($fh, $filename) = tempfile();Create a temporary file in the directory specified for temporaryfiles, as specified by the tmpdir() function in L<File::Spec>. ($fh, $filename) = tempfile($template);Create a temporary file in the current directory using the suppliedtemplate. Trailing `X' characters are replaced with random letters togenerate the filename. At least four `X' characters must be presentin the template. ($fh, $filename) = tempfile($template, SUFFIX => $suffix)Same as previously, except that a suffix is added to the templateafter the `X' translation. Useful for ensuring that a temporaryfilename has a particular extension when needed by other applications.But see the WARNING at the end. ($fh, $filename) = tempfile($template, DIR => $dir);Translates the template as before except that a directory nameis specified.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -