1*0Sstevel@tonic-gatepackage File::Temp; 2*0Sstevel@tonic-gate 3*0Sstevel@tonic-gate=head1 NAME 4*0Sstevel@tonic-gate 5*0Sstevel@tonic-gateFile::Temp - return name and handle of a temporary file safely 6*0Sstevel@tonic-gate 7*0Sstevel@tonic-gate=begin __INTERNALS 8*0Sstevel@tonic-gate 9*0Sstevel@tonic-gate=head1 PORTABILITY 10*0Sstevel@tonic-gate 11*0Sstevel@tonic-gateThis module is designed to be portable across operating systems 12*0Sstevel@tonic-gateand it currently supports Unix, VMS, DOS, OS/2, Windows and 13*0Sstevel@tonic-gateMac OS (Classic). When 14*0Sstevel@tonic-gateporting to a new OS there are generally three main issues 15*0Sstevel@tonic-gatethat have to be solved: 16*0Sstevel@tonic-gate 17*0Sstevel@tonic-gate=over 4 18*0Sstevel@tonic-gate 19*0Sstevel@tonic-gate=item * 20*0Sstevel@tonic-gate 21*0Sstevel@tonic-gateCan the OS unlink an open file? If it can not then the 22*0Sstevel@tonic-gateC<_can_unlink_opened_file> method should be modified. 23*0Sstevel@tonic-gate 24*0Sstevel@tonic-gate=item * 25*0Sstevel@tonic-gate 26*0Sstevel@tonic-gateAre the return values from C<stat> reliable? By default all the 27*0Sstevel@tonic-gatereturn values from C<stat> are compared when unlinking a temporary 28*0Sstevel@tonic-gatefile using the filename and the handle. Operating systems other than 29*0Sstevel@tonic-gateunix do not always have valid entries in all fields. If C<unlink0> fails 30*0Sstevel@tonic-gatethen the C<stat> comparison should be modified accordingly. 31*0Sstevel@tonic-gate 32*0Sstevel@tonic-gate=item * 33*0Sstevel@tonic-gate 34*0Sstevel@tonic-gateSecurity. Systems that can not support a test for the sticky bit 35*0Sstevel@tonic-gateon a directory can not use the MEDIUM and HIGH security tests. 36*0Sstevel@tonic-gateThe C<_can_do_level> method should be modified accordingly. 37*0Sstevel@tonic-gate 38*0Sstevel@tonic-gate=back 39*0Sstevel@tonic-gate 40*0Sstevel@tonic-gate=end __INTERNALS 41*0Sstevel@tonic-gate 42*0Sstevel@tonic-gate=head1 SYNOPSIS 43*0Sstevel@tonic-gate 44*0Sstevel@tonic-gate use File::Temp qw/ tempfile tempdir /; 45*0Sstevel@tonic-gate 46*0Sstevel@tonic-gate $dir = tempdir( CLEANUP => 1 ); 47*0Sstevel@tonic-gate ($fh, $filename) = tempfile( DIR => $dir ); 48*0Sstevel@tonic-gate 49*0Sstevel@tonic-gate ($fh, $filename) = tempfile( $template, DIR => $dir); 50*0Sstevel@tonic-gate ($fh, $filename) = tempfile( $template, SUFFIX => '.dat'); 51*0Sstevel@tonic-gate 52*0Sstevel@tonic-gate $fh = tempfile(); 53*0Sstevel@tonic-gate 54*0Sstevel@tonic-gateObject interface: 55*0Sstevel@tonic-gate 56*0Sstevel@tonic-gate require File::Temp; 57*0Sstevel@tonic-gate use File::Temp (); 58*0Sstevel@tonic-gate 59*0Sstevel@tonic-gate $fh = new File::Temp($template); 60*0Sstevel@tonic-gate $fname = $fh->filename; 61*0Sstevel@tonic-gate 62*0Sstevel@tonic-gate $tmp = new File::Temp( UNLINK => 0, SUFFIX => '.dat' ); 63*0Sstevel@tonic-gate print $tmp "Some data\n"; 64*0Sstevel@tonic-gate print "Filename is $tmp\n"; 65*0Sstevel@tonic-gate 66*0Sstevel@tonic-gate 67*0Sstevel@tonic-gateMkTemp family: 68*0Sstevel@tonic-gate 69*0Sstevel@tonic-gate use File::Temp qw/ :mktemp /; 70*0Sstevel@tonic-gate 71*0Sstevel@tonic-gate ($fh, $file) = mkstemp( "tmpfileXXXXX" ); 72*0Sstevel@tonic-gate ($fh, $file) = mkstemps( "tmpfileXXXXXX", $suffix); 73*0Sstevel@tonic-gate 74*0Sstevel@tonic-gate $tmpdir = mkdtemp( $template ); 75*0Sstevel@tonic-gate 76*0Sstevel@tonic-gate $unopened_file = mktemp( $template ); 77*0Sstevel@tonic-gate 78*0Sstevel@tonic-gatePOSIX functions: 79*0Sstevel@tonic-gate 80*0Sstevel@tonic-gate use File::Temp qw/ :POSIX /; 81*0Sstevel@tonic-gate 82*0Sstevel@tonic-gate $file = tmpnam(); 83*0Sstevel@tonic-gate $fh = tmpfile(); 84*0Sstevel@tonic-gate 85*0Sstevel@tonic-gate ($fh, $file) = tmpnam(); 86*0Sstevel@tonic-gate $fh = tmpfile(); 87*0Sstevel@tonic-gate 88*0Sstevel@tonic-gate 89*0Sstevel@tonic-gateCompatibility functions: 90*0Sstevel@tonic-gate 91*0Sstevel@tonic-gate $unopened_file = File::Temp::tempnam( $dir, $pfx ); 92*0Sstevel@tonic-gate 93*0Sstevel@tonic-gate=head1 DESCRIPTION 94*0Sstevel@tonic-gate 95*0Sstevel@tonic-gateC<File::Temp> can be used to create and open temporary files in a safe 96*0Sstevel@tonic-gateway. There is both a function interface and an object-oriented 97*0Sstevel@tonic-gateinterface. The File::Temp constructor or the tempfile() function can 98*0Sstevel@tonic-gatebe used to return the name and the open filehandle of a temporary 99*0Sstevel@tonic-gatefile. The tempdir() function can be used to create a temporary 100*0Sstevel@tonic-gatedirectory. 101*0Sstevel@tonic-gate 102*0Sstevel@tonic-gateThe security aspect of temporary file creation is emphasized such that 103*0Sstevel@tonic-gatea filehandle and filename are returned together. This helps guarantee 104*0Sstevel@tonic-gatethat a race condition can not occur where the temporary file is 105*0Sstevel@tonic-gatecreated by another process between checking for the existence of the 106*0Sstevel@tonic-gatefile and its opening. Additional security levels are provided to 107*0Sstevel@tonic-gatecheck, for example, that the sticky bit is set on world writable 108*0Sstevel@tonic-gatedirectories. See L<"safe_level"> for more information. 109*0Sstevel@tonic-gate 110*0Sstevel@tonic-gateFor compatibility with popular C library functions, Perl implementations of 111*0Sstevel@tonic-gatethe mkstemp() family of functions are provided. These are, mkstemp(), 112*0Sstevel@tonic-gatemkstemps(), mkdtemp() and mktemp(). 113*0Sstevel@tonic-gate 114*0Sstevel@tonic-gateAdditionally, implementations of the standard L<POSIX|POSIX> 115*0Sstevel@tonic-gatetmpnam() and tmpfile() functions are provided if required. 116*0Sstevel@tonic-gate 117*0Sstevel@tonic-gateImplementations of mktemp(), tmpnam(), and tempnam() are provided, 118*0Sstevel@tonic-gatebut should be used with caution since they return only a filename 119*0Sstevel@tonic-gatethat was valid when function was called, so cannot guarantee 120*0Sstevel@tonic-gatethat the file will not exist by the time the caller opens the filename. 121*0Sstevel@tonic-gate 122*0Sstevel@tonic-gate=cut 123*0Sstevel@tonic-gate 124*0Sstevel@tonic-gate# 5.6.0 gives us S_IWOTH, S_IWGRP, our and auto-vivifying filehandls 125*0Sstevel@tonic-gate# People would like a version on 5.005 so give them what they want :-) 126*0Sstevel@tonic-gateuse 5.005; 127*0Sstevel@tonic-gateuse strict; 128*0Sstevel@tonic-gateuse Carp; 129*0Sstevel@tonic-gateuse File::Spec 0.8; 130*0Sstevel@tonic-gateuse File::Path qw/ rmtree /; 131*0Sstevel@tonic-gateuse Fcntl 1.03; 132*0Sstevel@tonic-gateuse Errno; 133*0Sstevel@tonic-gaterequire VMS::Stdio if $^O eq 'VMS'; 134*0Sstevel@tonic-gate 135*0Sstevel@tonic-gate# Need the Symbol package if we are running older perl 136*0Sstevel@tonic-gaterequire Symbol if $] < 5.006; 137*0Sstevel@tonic-gate 138*0Sstevel@tonic-gate### For the OO interface 139*0Sstevel@tonic-gateuse base qw/ IO::Handle /; 140*0Sstevel@tonic-gateuse overload '""' => "STRINGIFY"; 141*0Sstevel@tonic-gate 142*0Sstevel@tonic-gate 143*0Sstevel@tonic-gate# use 'our' on v5.6.0 144*0Sstevel@tonic-gateuse vars qw($VERSION @EXPORT_OK %EXPORT_TAGS $DEBUG); 145*0Sstevel@tonic-gate 146*0Sstevel@tonic-gate$DEBUG = 0; 147*0Sstevel@tonic-gate 148*0Sstevel@tonic-gate# We are exporting functions 149*0Sstevel@tonic-gate 150*0Sstevel@tonic-gateuse base qw/Exporter/; 151*0Sstevel@tonic-gate 152*0Sstevel@tonic-gate# Export list - to allow fine tuning of export table 153*0Sstevel@tonic-gate 154*0Sstevel@tonic-gate@EXPORT_OK = qw{ 155*0Sstevel@tonic-gate tempfile 156*0Sstevel@tonic-gate tempdir 157*0Sstevel@tonic-gate tmpnam 158*0Sstevel@tonic-gate tmpfile 159*0Sstevel@tonic-gate mktemp 160*0Sstevel@tonic-gate mkstemp 161*0Sstevel@tonic-gate mkstemps 162*0Sstevel@tonic-gate mkdtemp 163*0Sstevel@tonic-gate unlink0 164*0Sstevel@tonic-gate }; 165*0Sstevel@tonic-gate 166*0Sstevel@tonic-gate# Groups of functions for export 167*0Sstevel@tonic-gate 168*0Sstevel@tonic-gate%EXPORT_TAGS = ( 169*0Sstevel@tonic-gate 'POSIX' => [qw/ tmpnam tmpfile /], 170*0Sstevel@tonic-gate 'mktemp' => [qw/ mktemp mkstemp mkstemps mkdtemp/], 171*0Sstevel@tonic-gate ); 172*0Sstevel@tonic-gate 173*0Sstevel@tonic-gate# add contents of these tags to @EXPORT 174*0Sstevel@tonic-gateExporter::export_tags('POSIX','mktemp'); 175*0Sstevel@tonic-gate 176*0Sstevel@tonic-gate# Version number 177*0Sstevel@tonic-gate 178*0Sstevel@tonic-gate$VERSION = '0.14'; 179*0Sstevel@tonic-gate 180*0Sstevel@tonic-gate# This is a list of characters that can be used in random filenames 181*0Sstevel@tonic-gate 182*0Sstevel@tonic-gatemy @CHARS = (qw/ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 183*0Sstevel@tonic-gate a b c d e f g h i j k l m n o p q r s t u v w x y z 184*0Sstevel@tonic-gate 0 1 2 3 4 5 6 7 8 9 _ 185*0Sstevel@tonic-gate /); 186*0Sstevel@tonic-gate 187*0Sstevel@tonic-gate# Maximum number of tries to make a temp file before failing 188*0Sstevel@tonic-gate 189*0Sstevel@tonic-gateuse constant MAX_TRIES => 10; 190*0Sstevel@tonic-gate 191*0Sstevel@tonic-gate# Minimum number of X characters that should be in a template 192*0Sstevel@tonic-gateuse constant MINX => 4; 193*0Sstevel@tonic-gate 194*0Sstevel@tonic-gate# Default template when no template supplied 195*0Sstevel@tonic-gate 196*0Sstevel@tonic-gateuse constant TEMPXXX => 'X' x 10; 197*0Sstevel@tonic-gate 198*0Sstevel@tonic-gate# Constants for the security level 199*0Sstevel@tonic-gate 200*0Sstevel@tonic-gateuse constant STANDARD => 0; 201*0Sstevel@tonic-gateuse constant MEDIUM => 1; 202*0Sstevel@tonic-gateuse constant HIGH => 2; 203*0Sstevel@tonic-gate 204*0Sstevel@tonic-gate# OPENFLAGS. If we defined the flag to use with Sysopen here this gives 205*0Sstevel@tonic-gate# us an optimisation when many temporary files are requested 206*0Sstevel@tonic-gate 207*0Sstevel@tonic-gatemy $OPENFLAGS = O_CREAT | O_EXCL | O_RDWR; 208*0Sstevel@tonic-gate 209*0Sstevel@tonic-gateunless ($^O eq 'MacOS') { 210*0Sstevel@tonic-gate for my $oflag (qw/ FOLLOW BINARY LARGEFILE EXLOCK NOINHERIT /) { 211*0Sstevel@tonic-gate my ($bit, $func) = (0, "Fcntl::O_" . $oflag); 212*0Sstevel@tonic-gate no strict 'refs'; 213*0Sstevel@tonic-gate $OPENFLAGS |= $bit if eval { 214*0Sstevel@tonic-gate # Make sure that redefined die handlers do not cause problems 215*0Sstevel@tonic-gate # eg CGI::Carp 216*0Sstevel@tonic-gate local $SIG{__DIE__} = sub {}; 217*0Sstevel@tonic-gate local $SIG{__WARN__} = sub {}; 218*0Sstevel@tonic-gate $bit = &$func(); 219*0Sstevel@tonic-gate 1; 220*0Sstevel@tonic-gate }; 221*0Sstevel@tonic-gate } 222*0Sstevel@tonic-gate} 223*0Sstevel@tonic-gate 224*0Sstevel@tonic-gate# On some systems the O_TEMPORARY flag can be used to tell the OS 225*0Sstevel@tonic-gate# to automatically remove the file when it is closed. This is fine 226*0Sstevel@tonic-gate# in most cases but not if tempfile is called with UNLINK=>0 and 227*0Sstevel@tonic-gate# the filename is requested -- in the case where the filename is to 228*0Sstevel@tonic-gate# be passed to another routine. This happens on windows. We overcome 229*0Sstevel@tonic-gate# this by using a second open flags variable 230*0Sstevel@tonic-gate 231*0Sstevel@tonic-gatemy $OPENTEMPFLAGS = $OPENFLAGS; 232*0Sstevel@tonic-gateunless ($^O eq 'MacOS') { 233*0Sstevel@tonic-gate for my $oflag (qw/ TEMPORARY /) { 234*0Sstevel@tonic-gate my ($bit, $func) = (0, "Fcntl::O_" . $oflag); 235*0Sstevel@tonic-gate no strict 'refs'; 236*0Sstevel@tonic-gate $OPENTEMPFLAGS |= $bit if eval { 237*0Sstevel@tonic-gate # Make sure that redefined die handlers do not cause problems 238*0Sstevel@tonic-gate # eg CGI::Carp 239*0Sstevel@tonic-gate local $SIG{__DIE__} = sub {}; 240*0Sstevel@tonic-gate local $SIG{__WARN__} = sub {}; 241*0Sstevel@tonic-gate $bit = &$func(); 242*0Sstevel@tonic-gate 1; 243*0Sstevel@tonic-gate }; 244*0Sstevel@tonic-gate } 245*0Sstevel@tonic-gate} 246*0Sstevel@tonic-gate 247*0Sstevel@tonic-gate# INTERNAL ROUTINES - not to be used outside of package 248*0Sstevel@tonic-gate 249*0Sstevel@tonic-gate# Generic routine for getting a temporary filename 250*0Sstevel@tonic-gate# modelled on OpenBSD _gettemp() in mktemp.c 251*0Sstevel@tonic-gate 252*0Sstevel@tonic-gate# The template must contain X's that are to be replaced 253*0Sstevel@tonic-gate# with the random values 254*0Sstevel@tonic-gate 255*0Sstevel@tonic-gate# Arguments: 256*0Sstevel@tonic-gate 257*0Sstevel@tonic-gate# TEMPLATE - string containing the XXXXX's that is converted 258*0Sstevel@tonic-gate# to a random filename and opened if required 259*0Sstevel@tonic-gate 260*0Sstevel@tonic-gate# Optionally, a hash can also be supplied containing specific options 261*0Sstevel@tonic-gate# "open" => if true open the temp file, else just return the name 262*0Sstevel@tonic-gate# default is 0 263*0Sstevel@tonic-gate# "mkdir"=> if true, we are creating a temp directory rather than tempfile 264*0Sstevel@tonic-gate# default is 0 265*0Sstevel@tonic-gate# "suffixlen" => number of characters at end of PATH to be ignored. 266*0Sstevel@tonic-gate# default is 0. 267*0Sstevel@tonic-gate# "unlink_on_close" => indicates that, if possible, the OS should remove 268*0Sstevel@tonic-gate# the file as soon as it is closed. Usually indicates 269*0Sstevel@tonic-gate# use of the O_TEMPORARY flag to sysopen. 270*0Sstevel@tonic-gate# Usually irrelevant on unix 271*0Sstevel@tonic-gate 272*0Sstevel@tonic-gate# Optionally a reference to a scalar can be passed into the function 273*0Sstevel@tonic-gate# On error this will be used to store the reason for the error 274*0Sstevel@tonic-gate# "ErrStr" => \$errstr 275*0Sstevel@tonic-gate 276*0Sstevel@tonic-gate# "open" and "mkdir" can not both be true 277*0Sstevel@tonic-gate# "unlink_on_close" is not used when "mkdir" is true. 278*0Sstevel@tonic-gate 279*0Sstevel@tonic-gate# The default options are equivalent to mktemp(). 280*0Sstevel@tonic-gate 281*0Sstevel@tonic-gate# Returns: 282*0Sstevel@tonic-gate# filehandle - open file handle (if called with doopen=1, else undef) 283*0Sstevel@tonic-gate# temp name - name of the temp file or directory 284*0Sstevel@tonic-gate 285*0Sstevel@tonic-gate# For example: 286*0Sstevel@tonic-gate# ($fh, $name) = _gettemp($template, "open" => 1); 287*0Sstevel@tonic-gate 288*0Sstevel@tonic-gate# for the current version, failures are associated with 289*0Sstevel@tonic-gate# stored in an error string and returned to give the reason whilst debugging 290*0Sstevel@tonic-gate# This routine is not called by any external function 291*0Sstevel@tonic-gatesub _gettemp { 292*0Sstevel@tonic-gate 293*0Sstevel@tonic-gate croak 'Usage: ($fh, $name) = _gettemp($template, OPTIONS);' 294*0Sstevel@tonic-gate unless scalar(@_) >= 1; 295*0Sstevel@tonic-gate 296*0Sstevel@tonic-gate # the internal error string - expect it to be overridden 297*0Sstevel@tonic-gate # Need this in case the caller decides not to supply us a value 298*0Sstevel@tonic-gate # need an anonymous scalar 299*0Sstevel@tonic-gate my $tempErrStr; 300*0Sstevel@tonic-gate 301*0Sstevel@tonic-gate # Default options 302*0Sstevel@tonic-gate my %options = ( 303*0Sstevel@tonic-gate "open" => 0, 304*0Sstevel@tonic-gate "mkdir" => 0, 305*0Sstevel@tonic-gate "suffixlen" => 0, 306*0Sstevel@tonic-gate "unlink_on_close" => 0, 307*0Sstevel@tonic-gate "ErrStr" => \$tempErrStr, 308*0Sstevel@tonic-gate ); 309*0Sstevel@tonic-gate 310*0Sstevel@tonic-gate # Read the template 311*0Sstevel@tonic-gate my $template = shift; 312*0Sstevel@tonic-gate if (ref($template)) { 313*0Sstevel@tonic-gate # Use a warning here since we have not yet merged ErrStr 314*0Sstevel@tonic-gate carp "File::Temp::_gettemp: template must not be a reference"; 315*0Sstevel@tonic-gate return (); 316*0Sstevel@tonic-gate } 317*0Sstevel@tonic-gate 318*0Sstevel@tonic-gate # Check that the number of entries on stack are even 319*0Sstevel@tonic-gate if (scalar(@_) % 2 != 0) { 320*0Sstevel@tonic-gate # Use a warning here since we have not yet merged ErrStr 321*0Sstevel@tonic-gate carp "File::Temp::_gettemp: Must have even number of options"; 322*0Sstevel@tonic-gate return (); 323*0Sstevel@tonic-gate } 324*0Sstevel@tonic-gate 325*0Sstevel@tonic-gate # Read the options and merge with defaults 326*0Sstevel@tonic-gate %options = (%options, @_) if @_; 327*0Sstevel@tonic-gate 328*0Sstevel@tonic-gate # Make sure the error string is set to undef 329*0Sstevel@tonic-gate ${$options{ErrStr}} = undef; 330*0Sstevel@tonic-gate 331*0Sstevel@tonic-gate # Can not open the file and make a directory in a single call 332*0Sstevel@tonic-gate if ($options{"open"} && $options{"mkdir"}) { 333*0Sstevel@tonic-gate ${$options{ErrStr}} = "doopen and domkdir can not both be true\n"; 334*0Sstevel@tonic-gate return (); 335*0Sstevel@tonic-gate } 336*0Sstevel@tonic-gate 337*0Sstevel@tonic-gate # Find the start of the end of the Xs (position of last X) 338*0Sstevel@tonic-gate # Substr starts from 0 339*0Sstevel@tonic-gate my $start = length($template) - 1 - $options{"suffixlen"}; 340*0Sstevel@tonic-gate 341*0Sstevel@tonic-gate # Check that we have at least MINX x X (eg 'XXXX") at the end of the string 342*0Sstevel@tonic-gate # (taking suffixlen into account). Any fewer is insecure. 343*0Sstevel@tonic-gate 344*0Sstevel@tonic-gate # Do it using substr - no reason to use a pattern match since 345*0Sstevel@tonic-gate # we know where we are looking and what we are looking for 346*0Sstevel@tonic-gate 347*0Sstevel@tonic-gate if (substr($template, $start - MINX + 1, MINX) ne 'X' x MINX) { 348*0Sstevel@tonic-gate ${$options{ErrStr}} = "The template must contain at least ". 349*0Sstevel@tonic-gate MINX . " 'X' characters\n"; 350*0Sstevel@tonic-gate return (); 351*0Sstevel@tonic-gate } 352*0Sstevel@tonic-gate 353*0Sstevel@tonic-gate # Replace all the X at the end of the substring with a 354*0Sstevel@tonic-gate # random character or just all the XX at the end of a full string. 355*0Sstevel@tonic-gate # Do it as an if, since the suffix adjusts which section to replace 356*0Sstevel@tonic-gate # and suffixlen=0 returns nothing if used in the substr directly 357*0Sstevel@tonic-gate # and generate a full path from the template 358*0Sstevel@tonic-gate 359*0Sstevel@tonic-gate my $path = _replace_XX($template, $options{"suffixlen"}); 360*0Sstevel@tonic-gate 361*0Sstevel@tonic-gate 362*0Sstevel@tonic-gate # Split the path into constituent parts - eventually we need to check 363*0Sstevel@tonic-gate # whether the directory exists 364*0Sstevel@tonic-gate # We need to know whether we are making a temp directory 365*0Sstevel@tonic-gate # or a tempfile 366*0Sstevel@tonic-gate 367*0Sstevel@tonic-gate my ($volume, $directories, $file); 368*0Sstevel@tonic-gate my $parent; # parent directory 369*0Sstevel@tonic-gate if ($options{"mkdir"}) { 370*0Sstevel@tonic-gate # There is no filename at the end 371*0Sstevel@tonic-gate ($volume, $directories, $file) = File::Spec->splitpath( $path, 1); 372*0Sstevel@tonic-gate 373*0Sstevel@tonic-gate # The parent is then $directories without the last directory 374*0Sstevel@tonic-gate # Split the directory and put it back together again 375*0Sstevel@tonic-gate my @dirs = File::Spec->splitdir($directories); 376*0Sstevel@tonic-gate 377*0Sstevel@tonic-gate # If @dirs only has one entry (i.e. the directory template) that means 378*0Sstevel@tonic-gate # we are in the current directory 379*0Sstevel@tonic-gate if ($#dirs == 0) { 380*0Sstevel@tonic-gate $parent = File::Spec->curdir; 381*0Sstevel@tonic-gate } else { 382*0Sstevel@tonic-gate 383*0Sstevel@tonic-gate if ($^O eq 'VMS') { # need volume to avoid relative dir spec 384*0Sstevel@tonic-gate $parent = File::Spec->catdir($volume, @dirs[0..$#dirs-1]); 385*0Sstevel@tonic-gate $parent = 'sys$disk:[]' if $parent eq ''; 386*0Sstevel@tonic-gate } else { 387*0Sstevel@tonic-gate 388*0Sstevel@tonic-gate # Put it back together without the last one 389*0Sstevel@tonic-gate $parent = File::Spec->catdir(@dirs[0..$#dirs-1]); 390*0Sstevel@tonic-gate 391*0Sstevel@tonic-gate # ...and attach the volume (no filename) 392*0Sstevel@tonic-gate $parent = File::Spec->catpath($volume, $parent, ''); 393*0Sstevel@tonic-gate } 394*0Sstevel@tonic-gate 395*0Sstevel@tonic-gate } 396*0Sstevel@tonic-gate 397*0Sstevel@tonic-gate } else { 398*0Sstevel@tonic-gate 399*0Sstevel@tonic-gate # Get rid of the last filename (use File::Basename for this?) 400*0Sstevel@tonic-gate ($volume, $directories, $file) = File::Spec->splitpath( $path ); 401*0Sstevel@tonic-gate 402*0Sstevel@tonic-gate # Join up without the file part 403*0Sstevel@tonic-gate $parent = File::Spec->catpath($volume,$directories,''); 404*0Sstevel@tonic-gate 405*0Sstevel@tonic-gate # If $parent is empty replace with curdir 406*0Sstevel@tonic-gate $parent = File::Spec->curdir 407*0Sstevel@tonic-gate unless $directories ne ''; 408*0Sstevel@tonic-gate 409*0Sstevel@tonic-gate } 410*0Sstevel@tonic-gate 411*0Sstevel@tonic-gate # Check that the parent directories exist 412*0Sstevel@tonic-gate # Do this even for the case where we are simply returning a name 413*0Sstevel@tonic-gate # not a file -- no point returning a name that includes a directory 414*0Sstevel@tonic-gate # that does not exist or is not writable 415*0Sstevel@tonic-gate 416*0Sstevel@tonic-gate unless (-d $parent) { 417*0Sstevel@tonic-gate ${$options{ErrStr}} = "Parent directory ($parent) is not a directory"; 418*0Sstevel@tonic-gate return (); 419*0Sstevel@tonic-gate } 420*0Sstevel@tonic-gate unless (-w _) { 421*0Sstevel@tonic-gate ${$options{ErrStr}} = "Parent directory ($parent) is not writable\n"; 422*0Sstevel@tonic-gate return (); 423*0Sstevel@tonic-gate } 424*0Sstevel@tonic-gate 425*0Sstevel@tonic-gate 426*0Sstevel@tonic-gate # Check the stickiness of the directory and chown giveaway if required 427*0Sstevel@tonic-gate # If the directory is world writable the sticky bit 428*0Sstevel@tonic-gate # must be set 429*0Sstevel@tonic-gate 430*0Sstevel@tonic-gate if (File::Temp->safe_level == MEDIUM) { 431*0Sstevel@tonic-gate my $safeerr; 432*0Sstevel@tonic-gate unless (_is_safe($parent,\$safeerr)) { 433*0Sstevel@tonic-gate ${$options{ErrStr}} = "Parent directory ($parent) is not safe ($safeerr)"; 434*0Sstevel@tonic-gate return (); 435*0Sstevel@tonic-gate } 436*0Sstevel@tonic-gate } elsif (File::Temp->safe_level == HIGH) { 437*0Sstevel@tonic-gate my $safeerr; 438*0Sstevel@tonic-gate unless (_is_verysafe($parent, \$safeerr)) { 439*0Sstevel@tonic-gate ${$options{ErrStr}} = "Parent directory ($parent) is not safe ($safeerr)"; 440*0Sstevel@tonic-gate return (); 441*0Sstevel@tonic-gate } 442*0Sstevel@tonic-gate } 443*0Sstevel@tonic-gate 444*0Sstevel@tonic-gate 445*0Sstevel@tonic-gate # Now try MAX_TRIES time to open the file 446*0Sstevel@tonic-gate for (my $i = 0; $i < MAX_TRIES; $i++) { 447*0Sstevel@tonic-gate 448*0Sstevel@tonic-gate # Try to open the file if requested 449*0Sstevel@tonic-gate if ($options{"open"}) { 450*0Sstevel@tonic-gate my $fh; 451*0Sstevel@tonic-gate 452*0Sstevel@tonic-gate # If we are running before perl5.6.0 we can not auto-vivify 453*0Sstevel@tonic-gate if ($] < 5.006) { 454*0Sstevel@tonic-gate $fh = &Symbol::gensym; 455*0Sstevel@tonic-gate } 456*0Sstevel@tonic-gate 457*0Sstevel@tonic-gate # Try to make sure this will be marked close-on-exec 458*0Sstevel@tonic-gate # XXX: Win32 doesn't respect this, nor the proper fcntl, 459*0Sstevel@tonic-gate # but may have O_NOINHERIT. This may or may not be in Fcntl. 460*0Sstevel@tonic-gate local $^F = 2; 461*0Sstevel@tonic-gate 462*0Sstevel@tonic-gate # Store callers umask 463*0Sstevel@tonic-gate my $umask = umask(); 464*0Sstevel@tonic-gate 465*0Sstevel@tonic-gate # Set a known umask 466*0Sstevel@tonic-gate umask(066); 467*0Sstevel@tonic-gate 468*0Sstevel@tonic-gate # Attempt to open the file 469*0Sstevel@tonic-gate my $open_success = undef; 470*0Sstevel@tonic-gate if ( $^O eq 'VMS' and $options{"unlink_on_close"} ) { 471*0Sstevel@tonic-gate # make it auto delete on close by setting FAB$V_DLT bit 472*0Sstevel@tonic-gate $fh = VMS::Stdio::vmssysopen($path, $OPENFLAGS, 0600, 'fop=dlt'); 473*0Sstevel@tonic-gate $open_success = $fh; 474*0Sstevel@tonic-gate } else { 475*0Sstevel@tonic-gate my $flags = ( $options{"unlink_on_close"} ? 476*0Sstevel@tonic-gate $OPENTEMPFLAGS : 477*0Sstevel@tonic-gate $OPENFLAGS ); 478*0Sstevel@tonic-gate $open_success = sysopen($fh, $path, $flags, 0600); 479*0Sstevel@tonic-gate } 480*0Sstevel@tonic-gate if ( $open_success ) { 481*0Sstevel@tonic-gate 482*0Sstevel@tonic-gate # Reset umask 483*0Sstevel@tonic-gate umask($umask) if defined $umask; 484*0Sstevel@tonic-gate 485*0Sstevel@tonic-gate # Opened successfully - return file handle and name 486*0Sstevel@tonic-gate return ($fh, $path); 487*0Sstevel@tonic-gate 488*0Sstevel@tonic-gate } else { 489*0Sstevel@tonic-gate # Reset umask 490*0Sstevel@tonic-gate umask($umask) if defined $umask; 491*0Sstevel@tonic-gate 492*0Sstevel@tonic-gate # Error opening file - abort with error 493*0Sstevel@tonic-gate # if the reason was anything but EEXIST 494*0Sstevel@tonic-gate unless ($!{EEXIST}) { 495*0Sstevel@tonic-gate ${$options{ErrStr}} = "Could not create temp file $path: $!"; 496*0Sstevel@tonic-gate return (); 497*0Sstevel@tonic-gate } 498*0Sstevel@tonic-gate 499*0Sstevel@tonic-gate # Loop round for another try 500*0Sstevel@tonic-gate 501*0Sstevel@tonic-gate } 502*0Sstevel@tonic-gate } elsif ($options{"mkdir"}) { 503*0Sstevel@tonic-gate 504*0Sstevel@tonic-gate # Store callers umask 505*0Sstevel@tonic-gate my $umask = umask(); 506*0Sstevel@tonic-gate 507*0Sstevel@tonic-gate # Set a known umask 508*0Sstevel@tonic-gate umask(066); 509*0Sstevel@tonic-gate 510*0Sstevel@tonic-gate # Open the temp directory 511*0Sstevel@tonic-gate if (mkdir( $path, 0700)) { 512*0Sstevel@tonic-gate # created okay 513*0Sstevel@tonic-gate # Reset umask 514*0Sstevel@tonic-gate umask($umask) if defined $umask; 515*0Sstevel@tonic-gate 516*0Sstevel@tonic-gate return undef, $path; 517*0Sstevel@tonic-gate } else { 518*0Sstevel@tonic-gate 519*0Sstevel@tonic-gate # Reset umask 520*0Sstevel@tonic-gate umask($umask) if defined $umask; 521*0Sstevel@tonic-gate 522*0Sstevel@tonic-gate # Abort with error if the reason for failure was anything 523*0Sstevel@tonic-gate # except EEXIST 524*0Sstevel@tonic-gate unless ($!{EEXIST}) { 525*0Sstevel@tonic-gate ${$options{ErrStr}} = "Could not create directory $path: $!"; 526*0Sstevel@tonic-gate return (); 527*0Sstevel@tonic-gate } 528*0Sstevel@tonic-gate 529*0Sstevel@tonic-gate # Loop round for another try 530*0Sstevel@tonic-gate 531*0Sstevel@tonic-gate } 532*0Sstevel@tonic-gate 533*0Sstevel@tonic-gate } else { 534*0Sstevel@tonic-gate 535*0Sstevel@tonic-gate # Return true if the file can not be found 536*0Sstevel@tonic-gate # Directory has been checked previously 537*0Sstevel@tonic-gate 538*0Sstevel@tonic-gate return (undef, $path) unless -e $path; 539*0Sstevel@tonic-gate 540*0Sstevel@tonic-gate # Try again until MAX_TRIES 541*0Sstevel@tonic-gate 542*0Sstevel@tonic-gate } 543*0Sstevel@tonic-gate 544*0Sstevel@tonic-gate # Did not successfully open the tempfile/dir 545*0Sstevel@tonic-gate # so try again with a different set of random letters 546*0Sstevel@tonic-gate # No point in trying to increment unless we have only 547*0Sstevel@tonic-gate # 1 X say and the randomness could come up with the same 548*0Sstevel@tonic-gate # file MAX_TRIES in a row. 549*0Sstevel@tonic-gate 550*0Sstevel@tonic-gate # Store current attempt - in principal this implies that the 551*0Sstevel@tonic-gate # 3rd time around the open attempt that the first temp file 552*0Sstevel@tonic-gate # name could be generated again. Probably should store each 553*0Sstevel@tonic-gate # attempt and make sure that none are repeated 554*0Sstevel@tonic-gate 555*0Sstevel@tonic-gate my $original = $path; 556*0Sstevel@tonic-gate my $counter = 0; # Stop infinite loop 557*0Sstevel@tonic-gate my $MAX_GUESS = 50; 558*0Sstevel@tonic-gate 559*0Sstevel@tonic-gate do { 560*0Sstevel@tonic-gate 561*0Sstevel@tonic-gate # Generate new name from original template 562*0Sstevel@tonic-gate $path = _replace_XX($template, $options{"suffixlen"}); 563*0Sstevel@tonic-gate 564*0Sstevel@tonic-gate $counter++; 565*0Sstevel@tonic-gate 566*0Sstevel@tonic-gate } until ($path ne $original || $counter > $MAX_GUESS); 567*0Sstevel@tonic-gate 568*0Sstevel@tonic-gate # Check for out of control looping 569*0Sstevel@tonic-gate if ($counter > $MAX_GUESS) { 570*0Sstevel@tonic-gate ${$options{ErrStr}} = "Tried to get a new temp name different to the previous value $MAX_GUESS times.\nSomething wrong with template?? ($template)"; 571*0Sstevel@tonic-gate return (); 572*0Sstevel@tonic-gate } 573*0Sstevel@tonic-gate 574*0Sstevel@tonic-gate } 575*0Sstevel@tonic-gate 576*0Sstevel@tonic-gate # If we get here, we have run out of tries 577*0Sstevel@tonic-gate ${ $options{ErrStr} } = "Have exceeded the maximum number of attempts (" 578*0Sstevel@tonic-gate . MAX_TRIES . ") to open temp file/dir"; 579*0Sstevel@tonic-gate 580*0Sstevel@tonic-gate return (); 581*0Sstevel@tonic-gate 582*0Sstevel@tonic-gate} 583*0Sstevel@tonic-gate 584*0Sstevel@tonic-gate# Internal routine to return a random character from the 585*0Sstevel@tonic-gate# character list. Does not do an srand() since rand() 586*0Sstevel@tonic-gate# will do one automatically 587*0Sstevel@tonic-gate 588*0Sstevel@tonic-gate# No arguments. Return value is the random character 589*0Sstevel@tonic-gate 590*0Sstevel@tonic-gate# No longer called since _replace_XX runs a few percent faster if 591*0Sstevel@tonic-gate# I inline the code. This is important if we are creating thousands of 592*0Sstevel@tonic-gate# temporary files. 593*0Sstevel@tonic-gate 594*0Sstevel@tonic-gatesub _randchar { 595*0Sstevel@tonic-gate 596*0Sstevel@tonic-gate $CHARS[ int( rand( $#CHARS ) ) ]; 597*0Sstevel@tonic-gate 598*0Sstevel@tonic-gate} 599*0Sstevel@tonic-gate 600*0Sstevel@tonic-gate# Internal routine to replace the XXXX... with random characters 601*0Sstevel@tonic-gate# This has to be done by _gettemp() every time it fails to 602*0Sstevel@tonic-gate# open a temp file/dir 603*0Sstevel@tonic-gate 604*0Sstevel@tonic-gate# Arguments: $template (the template with XXX), 605*0Sstevel@tonic-gate# $ignore (number of characters at end to ignore) 606*0Sstevel@tonic-gate 607*0Sstevel@tonic-gate# Returns: modified template 608*0Sstevel@tonic-gate 609*0Sstevel@tonic-gatesub _replace_XX { 610*0Sstevel@tonic-gate 611*0Sstevel@tonic-gate croak 'Usage: _replace_XX($template, $ignore)' 612*0Sstevel@tonic-gate unless scalar(@_) == 2; 613*0Sstevel@tonic-gate 614*0Sstevel@tonic-gate my ($path, $ignore) = @_; 615*0Sstevel@tonic-gate 616*0Sstevel@tonic-gate # Do it as an if, since the suffix adjusts which section to replace 617*0Sstevel@tonic-gate # and suffixlen=0 returns nothing if used in the substr directly 618*0Sstevel@tonic-gate # Alternatively, could simply set $ignore to length($path)-1 619*0Sstevel@tonic-gate # Don't want to always use substr when not required though. 620*0Sstevel@tonic-gate 621*0Sstevel@tonic-gate if ($ignore) { 622*0Sstevel@tonic-gate substr($path, 0, - $ignore) =~ s/X(?=X*\z)/$CHARS[ int( rand( $#CHARS ) ) ]/ge; 623*0Sstevel@tonic-gate } else { 624*0Sstevel@tonic-gate $path =~ s/X(?=X*\z)/$CHARS[ int( rand( $#CHARS ) ) ]/ge; 625*0Sstevel@tonic-gate } 626*0Sstevel@tonic-gate 627*0Sstevel@tonic-gate return $path; 628*0Sstevel@tonic-gate} 629*0Sstevel@tonic-gate 630*0Sstevel@tonic-gate# internal routine to check to see if the directory is safe 631*0Sstevel@tonic-gate# First checks to see if the directory is not owned by the 632*0Sstevel@tonic-gate# current user or root. Then checks to see if anyone else 633*0Sstevel@tonic-gate# can write to the directory and if so, checks to see if 634*0Sstevel@tonic-gate# it has the sticky bit set 635*0Sstevel@tonic-gate 636*0Sstevel@tonic-gate# Will not work on systems that do not support sticky bit 637*0Sstevel@tonic-gate 638*0Sstevel@tonic-gate#Args: directory path to check 639*0Sstevel@tonic-gate# Optionally: reference to scalar to contain error message 640*0Sstevel@tonic-gate# Returns true if the path is safe and false otherwise. 641*0Sstevel@tonic-gate# Returns undef if can not even run stat() on the path 642*0Sstevel@tonic-gate 643*0Sstevel@tonic-gate# This routine based on version written by Tom Christiansen 644*0Sstevel@tonic-gate 645*0Sstevel@tonic-gate# Presumably, by the time we actually attempt to create the 646*0Sstevel@tonic-gate# file or directory in this directory, it may not be safe 647*0Sstevel@tonic-gate# anymore... Have to run _is_safe directly after the open. 648*0Sstevel@tonic-gate 649*0Sstevel@tonic-gatesub _is_safe { 650*0Sstevel@tonic-gate 651*0Sstevel@tonic-gate my $path = shift; 652*0Sstevel@tonic-gate my $err_ref = shift; 653*0Sstevel@tonic-gate 654*0Sstevel@tonic-gate # Stat path 655*0Sstevel@tonic-gate my @info = stat($path); 656*0Sstevel@tonic-gate unless (scalar(@info)) { 657*0Sstevel@tonic-gate $$err_ref = "stat(path) returned no values"; 658*0Sstevel@tonic-gate return 0; 659*0Sstevel@tonic-gate }; 660*0Sstevel@tonic-gate return 1 if $^O eq 'VMS'; # owner delete control at file level 661*0Sstevel@tonic-gate 662*0Sstevel@tonic-gate # Check to see whether owner is neither superuser (or a system uid) nor me 663*0Sstevel@tonic-gate # Use the real uid from the $< variable 664*0Sstevel@tonic-gate # UID is in [4] 665*0Sstevel@tonic-gate if ($info[4] > File::Temp->top_system_uid() && $info[4] != $<) { 666*0Sstevel@tonic-gate 667*0Sstevel@tonic-gate Carp::cluck(sprintf "uid=$info[4] topuid=%s \$<=$< path='$path'", 668*0Sstevel@tonic-gate File::Temp->top_system_uid()); 669*0Sstevel@tonic-gate 670*0Sstevel@tonic-gate $$err_ref = "Directory owned neither by root nor the current user" 671*0Sstevel@tonic-gate if ref($err_ref); 672*0Sstevel@tonic-gate return 0; 673*0Sstevel@tonic-gate } 674*0Sstevel@tonic-gate 675*0Sstevel@tonic-gate # check whether group or other can write file 676*0Sstevel@tonic-gate # use 066 to detect either reading or writing 677*0Sstevel@tonic-gate # use 022 to check writability 678*0Sstevel@tonic-gate # Do it with S_IWOTH and S_IWGRP for portability (maybe) 679*0Sstevel@tonic-gate # mode is in info[2] 680*0Sstevel@tonic-gate if (($info[2] & &Fcntl::S_IWGRP) || # Is group writable? 681*0Sstevel@tonic-gate ($info[2] & &Fcntl::S_IWOTH) ) { # Is world writable? 682*0Sstevel@tonic-gate # Must be a directory 683*0Sstevel@tonic-gate unless (-d _) { 684*0Sstevel@tonic-gate $$err_ref = "Path ($path) is not a directory" 685*0Sstevel@tonic-gate if ref($err_ref); 686*0Sstevel@tonic-gate return 0; 687*0Sstevel@tonic-gate } 688*0Sstevel@tonic-gate # Must have sticky bit set 689*0Sstevel@tonic-gate unless (-k _) { 690*0Sstevel@tonic-gate $$err_ref = "Sticky bit not set on $path when dir is group|world writable" 691*0Sstevel@tonic-gate if ref($err_ref); 692*0Sstevel@tonic-gate return 0; 693*0Sstevel@tonic-gate } 694*0Sstevel@tonic-gate } 695*0Sstevel@tonic-gate 696*0Sstevel@tonic-gate return 1; 697*0Sstevel@tonic-gate} 698*0Sstevel@tonic-gate 699*0Sstevel@tonic-gate# Internal routine to check whether a directory is safe 700*0Sstevel@tonic-gate# for temp files. Safer than _is_safe since it checks for 701*0Sstevel@tonic-gate# the possibility of chown giveaway and if that is a possibility 702*0Sstevel@tonic-gate# checks each directory in the path to see if it is safe (with _is_safe) 703*0Sstevel@tonic-gate 704*0Sstevel@tonic-gate# If _PC_CHOWN_RESTRICTED is not set, does the full test of each 705*0Sstevel@tonic-gate# directory anyway. 706*0Sstevel@tonic-gate 707*0Sstevel@tonic-gate# Takes optional second arg as scalar ref to error reason 708*0Sstevel@tonic-gate 709*0Sstevel@tonic-gatesub _is_verysafe { 710*0Sstevel@tonic-gate 711*0Sstevel@tonic-gate # Need POSIX - but only want to bother if really necessary due to overhead 712*0Sstevel@tonic-gate require POSIX; 713*0Sstevel@tonic-gate 714*0Sstevel@tonic-gate my $path = shift; 715*0Sstevel@tonic-gate print "_is_verysafe testing $path\n" if $DEBUG; 716*0Sstevel@tonic-gate return 1 if $^O eq 'VMS'; # owner delete control at file level 717*0Sstevel@tonic-gate 718*0Sstevel@tonic-gate my $err_ref = shift; 719*0Sstevel@tonic-gate 720*0Sstevel@tonic-gate # Should Get the value of _PC_CHOWN_RESTRICTED if it is defined 721*0Sstevel@tonic-gate # and If it is not there do the extensive test 722*0Sstevel@tonic-gate my $chown_restricted; 723*0Sstevel@tonic-gate $chown_restricted = &POSIX::_PC_CHOWN_RESTRICTED() 724*0Sstevel@tonic-gate if eval { &POSIX::_PC_CHOWN_RESTRICTED(); 1}; 725*0Sstevel@tonic-gate 726*0Sstevel@tonic-gate # If chown_resticted is set to some value we should test it 727*0Sstevel@tonic-gate if (defined $chown_restricted) { 728*0Sstevel@tonic-gate 729*0Sstevel@tonic-gate # Return if the current directory is safe 730*0Sstevel@tonic-gate return _is_safe($path,$err_ref) if POSIX::sysconf( $chown_restricted ); 731*0Sstevel@tonic-gate 732*0Sstevel@tonic-gate } 733*0Sstevel@tonic-gate 734*0Sstevel@tonic-gate # To reach this point either, the _PC_CHOWN_RESTRICTED symbol 735*0Sstevel@tonic-gate # was not avialable or the symbol was there but chown giveaway 736*0Sstevel@tonic-gate # is allowed. Either way, we now have to test the entire tree for 737*0Sstevel@tonic-gate # safety. 738*0Sstevel@tonic-gate 739*0Sstevel@tonic-gate # Convert path to an absolute directory if required 740*0Sstevel@tonic-gate unless (File::Spec->file_name_is_absolute($path)) { 741*0Sstevel@tonic-gate $path = File::Spec->rel2abs($path); 742*0Sstevel@tonic-gate } 743*0Sstevel@tonic-gate 744*0Sstevel@tonic-gate # Split directory into components - assume no file 745*0Sstevel@tonic-gate my ($volume, $directories, undef) = File::Spec->splitpath( $path, 1); 746*0Sstevel@tonic-gate 747*0Sstevel@tonic-gate # Slightly less efficient than having a function in File::Spec 748*0Sstevel@tonic-gate # to chop off the end of a directory or even a function that 749*0Sstevel@tonic-gate # can handle ../ in a directory tree 750*0Sstevel@tonic-gate # Sometimes splitdir() returns a blank at the end 751*0Sstevel@tonic-gate # so we will probably check the bottom directory twice in some cases 752*0Sstevel@tonic-gate my @dirs = File::Spec->splitdir($directories); 753*0Sstevel@tonic-gate 754*0Sstevel@tonic-gate # Concatenate one less directory each time around 755*0Sstevel@tonic-gate foreach my $pos (0.. $#dirs) { 756*0Sstevel@tonic-gate # Get a directory name 757*0Sstevel@tonic-gate my $dir = File::Spec->catpath($volume, 758*0Sstevel@tonic-gate File::Spec->catdir(@dirs[0.. $#dirs - $pos]), 759*0Sstevel@tonic-gate '' 760*0Sstevel@tonic-gate ); 761*0Sstevel@tonic-gate 762*0Sstevel@tonic-gate print "TESTING DIR $dir\n" if $DEBUG; 763*0Sstevel@tonic-gate 764*0Sstevel@tonic-gate # Check the directory 765*0Sstevel@tonic-gate return 0 unless _is_safe($dir,$err_ref); 766*0Sstevel@tonic-gate 767*0Sstevel@tonic-gate } 768*0Sstevel@tonic-gate 769*0Sstevel@tonic-gate return 1; 770*0Sstevel@tonic-gate} 771*0Sstevel@tonic-gate 772*0Sstevel@tonic-gate 773*0Sstevel@tonic-gate 774*0Sstevel@tonic-gate# internal routine to determine whether unlink works on this 775*0Sstevel@tonic-gate# platform for files that are currently open. 776*0Sstevel@tonic-gate# Returns true if we can, false otherwise. 777*0Sstevel@tonic-gate 778*0Sstevel@tonic-gate# Currently WinNT, OS/2 and VMS can not unlink an opened file 779*0Sstevel@tonic-gate# On VMS this is because the O_EXCL flag is used to open the 780*0Sstevel@tonic-gate# temporary file. Currently I do not know enough about the issues 781*0Sstevel@tonic-gate# on VMS to decide whether O_EXCL is a requirement. 782*0Sstevel@tonic-gate 783*0Sstevel@tonic-gatesub _can_unlink_opened_file { 784*0Sstevel@tonic-gate 785*0Sstevel@tonic-gate if ($^O eq 'MSWin32' || $^O eq 'os2' || $^O eq 'VMS' || $^O eq 'dos' || $^O eq 'MacOS') { 786*0Sstevel@tonic-gate return 0; 787*0Sstevel@tonic-gate } else { 788*0Sstevel@tonic-gate return 1; 789*0Sstevel@tonic-gate } 790*0Sstevel@tonic-gate 791*0Sstevel@tonic-gate} 792*0Sstevel@tonic-gate 793*0Sstevel@tonic-gate# internal routine to decide which security levels are allowed 794*0Sstevel@tonic-gate# see safe_level() for more information on this 795*0Sstevel@tonic-gate 796*0Sstevel@tonic-gate# Controls whether the supplied security level is allowed 797*0Sstevel@tonic-gate 798*0Sstevel@tonic-gate# $cando = _can_do_level( $level ) 799*0Sstevel@tonic-gate 800*0Sstevel@tonic-gatesub _can_do_level { 801*0Sstevel@tonic-gate 802*0Sstevel@tonic-gate # Get security level 803*0Sstevel@tonic-gate my $level = shift; 804*0Sstevel@tonic-gate 805*0Sstevel@tonic-gate # Always have to be able to do STANDARD 806*0Sstevel@tonic-gate return 1 if $level == STANDARD; 807*0Sstevel@tonic-gate 808*0Sstevel@tonic-gate # Currently, the systems that can do HIGH or MEDIUM are identical 809*0Sstevel@tonic-gate if ( $^O eq 'MSWin32' || $^O eq 'os2' || $^O eq 'cygwin' || $^O eq 'dos' || $^O eq 'MacOS' || $^O eq 'mpeix') { 810*0Sstevel@tonic-gate return 0; 811*0Sstevel@tonic-gate } else { 812*0Sstevel@tonic-gate return 1; 813*0Sstevel@tonic-gate } 814*0Sstevel@tonic-gate 815*0Sstevel@tonic-gate} 816*0Sstevel@tonic-gate 817*0Sstevel@tonic-gate# This routine sets up a deferred unlinking of a specified 818*0Sstevel@tonic-gate# filename and filehandle. It is used in the following cases: 819*0Sstevel@tonic-gate# - Called by unlink0 if an opened file can not be unlinked 820*0Sstevel@tonic-gate# - Called by tempfile() if files are to be removed on shutdown 821*0Sstevel@tonic-gate# - Called by tempdir() if directories are to be removed on shutdown 822*0Sstevel@tonic-gate 823*0Sstevel@tonic-gate# Arguments: 824*0Sstevel@tonic-gate# _deferred_unlink( $fh, $fname, $isdir ); 825*0Sstevel@tonic-gate# 826*0Sstevel@tonic-gate# - filehandle (so that it can be expclicitly closed if open 827*0Sstevel@tonic-gate# - filename (the thing we want to remove) 828*0Sstevel@tonic-gate# - isdir (flag to indicate that we are being given a directory) 829*0Sstevel@tonic-gate# [and hence no filehandle] 830*0Sstevel@tonic-gate 831*0Sstevel@tonic-gate# Status is not referred to since all the magic is done with an END block 832*0Sstevel@tonic-gate 833*0Sstevel@tonic-gate{ 834*0Sstevel@tonic-gate # Will set up two lexical variables to contain all the files to be 835*0Sstevel@tonic-gate # removed. One array for files, another for directories 836*0Sstevel@tonic-gate # They will only exist in this block 837*0Sstevel@tonic-gate # This means we only have to set up a single END block to remove all files 838*0Sstevel@tonic-gate # @files_to_unlink contains an array ref with the filehandle and filename 839*0Sstevel@tonic-gate my (@files_to_unlink, @dirs_to_unlink); 840*0Sstevel@tonic-gate 841*0Sstevel@tonic-gate # Set up an end block to use these arrays 842*0Sstevel@tonic-gate END { 843*0Sstevel@tonic-gate # Files 844*0Sstevel@tonic-gate foreach my $file (@files_to_unlink) { 845*0Sstevel@tonic-gate # close the filehandle without checking its state 846*0Sstevel@tonic-gate # in order to make real sure that this is closed 847*0Sstevel@tonic-gate # if its already closed then I dont care about the answer 848*0Sstevel@tonic-gate # probably a better way to do this 849*0Sstevel@tonic-gate close($file->[0]); # file handle is [0] 850*0Sstevel@tonic-gate 851*0Sstevel@tonic-gate if (-f $file->[1]) { # file name is [1] 852*0Sstevel@tonic-gate unlink $file->[1] or warn "Error removing ".$file->[1]; 853*0Sstevel@tonic-gate } 854*0Sstevel@tonic-gate } 855*0Sstevel@tonic-gate # Dirs 856*0Sstevel@tonic-gate foreach my $dir (@dirs_to_unlink) { 857*0Sstevel@tonic-gate if (-d $dir) { 858*0Sstevel@tonic-gate rmtree($dir, $DEBUG, 0); 859*0Sstevel@tonic-gate } 860*0Sstevel@tonic-gate } 861*0Sstevel@tonic-gate 862*0Sstevel@tonic-gate } 863*0Sstevel@tonic-gate 864*0Sstevel@tonic-gate # This is the sub called to register a file for deferred unlinking 865*0Sstevel@tonic-gate # This could simply store the input parameters and defer everything 866*0Sstevel@tonic-gate # until the END block. For now we do a bit of checking at this 867*0Sstevel@tonic-gate # point in order to make sure that (1) we have a file/dir to delete 868*0Sstevel@tonic-gate # and (2) we have been called with the correct arguments. 869*0Sstevel@tonic-gate sub _deferred_unlink { 870*0Sstevel@tonic-gate 871*0Sstevel@tonic-gate croak 'Usage: _deferred_unlink($fh, $fname, $isdir)' 872*0Sstevel@tonic-gate unless scalar(@_) == 3; 873*0Sstevel@tonic-gate 874*0Sstevel@tonic-gate my ($fh, $fname, $isdir) = @_; 875*0Sstevel@tonic-gate 876*0Sstevel@tonic-gate warn "Setting up deferred removal of $fname\n" 877*0Sstevel@tonic-gate if $DEBUG; 878*0Sstevel@tonic-gate 879*0Sstevel@tonic-gate # If we have a directory, check that it is a directory 880*0Sstevel@tonic-gate if ($isdir) { 881*0Sstevel@tonic-gate 882*0Sstevel@tonic-gate if (-d $fname) { 883*0Sstevel@tonic-gate 884*0Sstevel@tonic-gate # Directory exists so store it 885*0Sstevel@tonic-gate # first on VMS turn []foo into [.foo] for rmtree 886*0Sstevel@tonic-gate $fname = VMS::Filespec::vmspath($fname) if $^O eq 'VMS'; 887*0Sstevel@tonic-gate push (@dirs_to_unlink, $fname); 888*0Sstevel@tonic-gate 889*0Sstevel@tonic-gate } else { 890*0Sstevel@tonic-gate carp "Request to remove directory $fname could not be completed since it does not exist!\n" if $^W; 891*0Sstevel@tonic-gate } 892*0Sstevel@tonic-gate 893*0Sstevel@tonic-gate } else { 894*0Sstevel@tonic-gate 895*0Sstevel@tonic-gate if (-f $fname) { 896*0Sstevel@tonic-gate 897*0Sstevel@tonic-gate # file exists so store handle and name for later removal 898*0Sstevel@tonic-gate push(@files_to_unlink, [$fh, $fname]); 899*0Sstevel@tonic-gate 900*0Sstevel@tonic-gate } else { 901*0Sstevel@tonic-gate carp "Request to remove file $fname could not be completed since it is not there!\n" if $^W; 902*0Sstevel@tonic-gate } 903*0Sstevel@tonic-gate 904*0Sstevel@tonic-gate } 905*0Sstevel@tonic-gate 906*0Sstevel@tonic-gate } 907*0Sstevel@tonic-gate 908*0Sstevel@tonic-gate 909*0Sstevel@tonic-gate} 910*0Sstevel@tonic-gate 911*0Sstevel@tonic-gate=head1 OO INTERFACE 912*0Sstevel@tonic-gate 913*0Sstevel@tonic-gateThis is the primary interface for interacting with 914*0Sstevel@tonic-gateC<File::Temp>. Using the OO interface a temporary file can be created 915*0Sstevel@tonic-gatewhen the object is constructed and the file can be removed when the 916*0Sstevel@tonic-gateobject is no longer required. 917*0Sstevel@tonic-gate 918*0Sstevel@tonic-gateNote that there is no method to obtain the filehandle from the 919*0Sstevel@tonic-gateC<File::Temp> object. The object itself acts as a filehandle. Also, 920*0Sstevel@tonic-gatethe object is configured such that it stringifies to the name of the 921*0Sstevel@tonic-gatetemporary file. 922*0Sstevel@tonic-gate 923*0Sstevel@tonic-gate=over 4 924*0Sstevel@tonic-gate 925*0Sstevel@tonic-gate=item B<new> 926*0Sstevel@tonic-gate 927*0Sstevel@tonic-gateCreate a temporary file object. 928*0Sstevel@tonic-gate 929*0Sstevel@tonic-gate my $tmp = new File::Temp(); 930*0Sstevel@tonic-gate 931*0Sstevel@tonic-gateby default the object is constructed as if C<tempfile> 932*0Sstevel@tonic-gatewas called without options, but with the additional behaviour 933*0Sstevel@tonic-gatethat the temporary file is removed by the object destructor 934*0Sstevel@tonic-gateif UNLINK is set to true (the default). 935*0Sstevel@tonic-gate 936*0Sstevel@tonic-gateSupported arguments are the same as for C<tempfile>: UNLINK 937*0Sstevel@tonic-gate(defaulting to true), DIR and SUFFIX. Additionally, the filename 938*0Sstevel@tonic-gatetemplate is specified using the TEMPLATE option. The OPEN option 939*0Sstevel@tonic-gateis not supported (the file is always opened). 940*0Sstevel@tonic-gate 941*0Sstevel@tonic-gate $tmp = new File::Temp( TEMPLATE => 'tempXXXXX', 942*0Sstevel@tonic-gate DIR => 'mydir', 943*0Sstevel@tonic-gate SUFFIX => '.dat'); 944*0Sstevel@tonic-gate 945*0Sstevel@tonic-gateArguments are case insensitive. 946*0Sstevel@tonic-gate 947*0Sstevel@tonic-gate=cut 948*0Sstevel@tonic-gate 949*0Sstevel@tonic-gatesub new { 950*0Sstevel@tonic-gate my $proto = shift; 951*0Sstevel@tonic-gate my $class = ref($proto) || $proto; 952*0Sstevel@tonic-gate 953*0Sstevel@tonic-gate # read arguments and convert keys to upper case 954*0Sstevel@tonic-gate my %args = @_; 955*0Sstevel@tonic-gate %args = map { uc($_), $args{$_} } keys %args; 956*0Sstevel@tonic-gate 957*0Sstevel@tonic-gate # see if they are unlinking (defaulting to yes) 958*0Sstevel@tonic-gate my $unlink = (exists $args{UNLINK} ? $args{UNLINK} : 1 ); 959*0Sstevel@tonic-gate delete $args{UNLINK}; 960*0Sstevel@tonic-gate 961*0Sstevel@tonic-gate # template (store it in an error so that it will 962*0Sstevel@tonic-gate # disappear from the arg list of tempfile 963*0Sstevel@tonic-gate my @template = ( exists $args{TEMPLATE} ? $args{TEMPLATE} : () ); 964*0Sstevel@tonic-gate delete $args{TEMPLATE}; 965*0Sstevel@tonic-gate 966*0Sstevel@tonic-gate # Protect OPEN 967*0Sstevel@tonic-gate delete $args{OPEN}; 968*0Sstevel@tonic-gate 969*0Sstevel@tonic-gate # Open the file and retain file handle and file name 970*0Sstevel@tonic-gate my ($fh, $path) = tempfile( @template, %args ); 971*0Sstevel@tonic-gate 972*0Sstevel@tonic-gate print "Tmp: $fh - $path\n" if $DEBUG; 973*0Sstevel@tonic-gate 974*0Sstevel@tonic-gate # Store the filename in the scalar slot 975*0Sstevel@tonic-gate ${*$fh} = $path; 976*0Sstevel@tonic-gate 977*0Sstevel@tonic-gate # Store unlink information in hash slot (plus other constructor info) 978*0Sstevel@tonic-gate %{*$fh} = %args; 979*0Sstevel@tonic-gate ${*$fh}{UNLINK} = $unlink; 980*0Sstevel@tonic-gate 981*0Sstevel@tonic-gate bless $fh, $class; 982*0Sstevel@tonic-gate 983*0Sstevel@tonic-gate return $fh; 984*0Sstevel@tonic-gate} 985*0Sstevel@tonic-gate 986*0Sstevel@tonic-gate=item B<filename> 987*0Sstevel@tonic-gate 988*0Sstevel@tonic-gateReturn the name of the temporary file associated with this object. 989*0Sstevel@tonic-gate 990*0Sstevel@tonic-gate $filename = $tmp->filename; 991*0Sstevel@tonic-gate 992*0Sstevel@tonic-gateThis method is called automatically when the object is used as 993*0Sstevel@tonic-gatea string. 994*0Sstevel@tonic-gate 995*0Sstevel@tonic-gate=cut 996*0Sstevel@tonic-gate 997*0Sstevel@tonic-gatesub filename { 998*0Sstevel@tonic-gate my $self = shift; 999*0Sstevel@tonic-gate return ${*$self}; 1000*0Sstevel@tonic-gate} 1001*0Sstevel@tonic-gate 1002*0Sstevel@tonic-gatesub STRINGIFY { 1003*0Sstevel@tonic-gate my $self = shift; 1004*0Sstevel@tonic-gate return $self->filename; 1005*0Sstevel@tonic-gate} 1006*0Sstevel@tonic-gate 1007*0Sstevel@tonic-gate=item B<DESTROY> 1008*0Sstevel@tonic-gate 1009*0Sstevel@tonic-gateWhen the object goes out of scope, the destructor is called. This 1010*0Sstevel@tonic-gatedestructor will attempt to unlink the file (using C<unlink1>) 1011*0Sstevel@tonic-gateif the constructor was called with UNLINK set to 1 (the default state 1012*0Sstevel@tonic-gateif UNLINK is not specified). 1013*0Sstevel@tonic-gate 1014*0Sstevel@tonic-gateNo error is given if the unlink fails. 1015*0Sstevel@tonic-gate 1016*0Sstevel@tonic-gate=cut 1017*0Sstevel@tonic-gate 1018*0Sstevel@tonic-gatesub DESTROY { 1019*0Sstevel@tonic-gate my $self = shift; 1020*0Sstevel@tonic-gate if (${*$self}{UNLINK}) { 1021*0Sstevel@tonic-gate print "# ---------> Unlinking $self\n" if $DEBUG; 1022*0Sstevel@tonic-gate 1023*0Sstevel@tonic-gate # The unlink1 may fail if the file has been closed 1024*0Sstevel@tonic-gate # by the caller. This leaves us with the decision 1025*0Sstevel@tonic-gate # of whether to refuse to remove the file or simply 1026*0Sstevel@tonic-gate # do an unlink without test. Seems to be silly 1027*0Sstevel@tonic-gate # to do this when we are trying to be careful 1028*0Sstevel@tonic-gate # about security 1029*0Sstevel@tonic-gate unlink1( $self, $self->filename ) 1030*0Sstevel@tonic-gate or unlink($self->filename); 1031*0Sstevel@tonic-gate } 1032*0Sstevel@tonic-gate} 1033*0Sstevel@tonic-gate 1034*0Sstevel@tonic-gate=back 1035*0Sstevel@tonic-gate 1036*0Sstevel@tonic-gate=head1 FUNCTIONS 1037*0Sstevel@tonic-gate 1038*0Sstevel@tonic-gateThis section describes the recommended interface for generating 1039*0Sstevel@tonic-gatetemporary files and directories. 1040*0Sstevel@tonic-gate 1041*0Sstevel@tonic-gate=over 4 1042*0Sstevel@tonic-gate 1043*0Sstevel@tonic-gate=item B<tempfile> 1044*0Sstevel@tonic-gate 1045*0Sstevel@tonic-gateThis is the basic function to generate temporary files. 1046*0Sstevel@tonic-gateThe behaviour of the file can be changed using various options: 1047*0Sstevel@tonic-gate 1048*0Sstevel@tonic-gate ($fh, $filename) = tempfile(); 1049*0Sstevel@tonic-gate 1050*0Sstevel@tonic-gateCreate a temporary file in the directory specified for temporary 1051*0Sstevel@tonic-gatefiles, as specified by the tmpdir() function in L<File::Spec>. 1052*0Sstevel@tonic-gate 1053*0Sstevel@tonic-gate ($fh, $filename) = tempfile($template); 1054*0Sstevel@tonic-gate 1055*0Sstevel@tonic-gateCreate a temporary file in the current directory using the supplied 1056*0Sstevel@tonic-gatetemplate. Trailing `X' characters are replaced with random letters to 1057*0Sstevel@tonic-gategenerate the filename. At least four `X' characters must be present 1058*0Sstevel@tonic-gateat the end of the template. 1059*0Sstevel@tonic-gate 1060*0Sstevel@tonic-gate ($fh, $filename) = tempfile($template, SUFFIX => $suffix) 1061*0Sstevel@tonic-gate 1062*0Sstevel@tonic-gateSame as previously, except that a suffix is added to the template 1063*0Sstevel@tonic-gateafter the `X' translation. Useful for ensuring that a temporary 1064*0Sstevel@tonic-gatefilename has a particular extension when needed by other applications. 1065*0Sstevel@tonic-gateBut see the WARNING at the end. 1066*0Sstevel@tonic-gate 1067*0Sstevel@tonic-gate ($fh, $filename) = tempfile($template, DIR => $dir); 1068*0Sstevel@tonic-gate 1069*0Sstevel@tonic-gateTranslates the template as before except that a directory name 1070*0Sstevel@tonic-gateis specified. 1071*0Sstevel@tonic-gate 1072*0Sstevel@tonic-gate ($fh, $filename) = tempfile($template, UNLINK => 1); 1073*0Sstevel@tonic-gate 1074*0Sstevel@tonic-gateReturn the filename and filehandle as before except that the file is 1075*0Sstevel@tonic-gateautomatically removed when the program exits. Default is for the file 1076*0Sstevel@tonic-gateto be removed if a file handle is requested and to be kept if the 1077*0Sstevel@tonic-gatefilename is requested. In a scalar context (where no filename is 1078*0Sstevel@tonic-gatereturned) the file is always deleted either on exit or when it is closed. 1079*0Sstevel@tonic-gate 1080*0Sstevel@tonic-gateIf the template is not specified, a template is always 1081*0Sstevel@tonic-gateautomatically generated. This temporary file is placed in tmpdir() 1082*0Sstevel@tonic-gate(L<File::Spec>) unless a directory is specified explicitly with the 1083*0Sstevel@tonic-gateDIR option. 1084*0Sstevel@tonic-gate 1085*0Sstevel@tonic-gate $fh = tempfile( $template, DIR => $dir ); 1086*0Sstevel@tonic-gate 1087*0Sstevel@tonic-gateIf called in scalar context, only the filehandle is returned 1088*0Sstevel@tonic-gateand the file will automatically be deleted when closed (see 1089*0Sstevel@tonic-gatethe description of tmpfile() elsewhere in this document). 1090*0Sstevel@tonic-gateThis is the preferred mode of operation, as if you only 1091*0Sstevel@tonic-gatehave a filehandle, you can never create a race condition 1092*0Sstevel@tonic-gateby fumbling with the filename. On systems that can not unlink 1093*0Sstevel@tonic-gatean open file or can not mark a file as temporary when it is opened 1094*0Sstevel@tonic-gate(for example, Windows NT uses the C<O_TEMPORARY> flag) 1095*0Sstevel@tonic-gatethe file is marked for deletion when the program ends (equivalent 1096*0Sstevel@tonic-gateto setting UNLINK to 1). The C<UNLINK> flag is ignored if present. 1097*0Sstevel@tonic-gate 1098*0Sstevel@tonic-gate (undef, $filename) = tempfile($template, OPEN => 0); 1099*0Sstevel@tonic-gate 1100*0Sstevel@tonic-gateThis will return the filename based on the template but 1101*0Sstevel@tonic-gatewill not open this file. Cannot be used in conjunction with 1102*0Sstevel@tonic-gateUNLINK set to true. Default is to always open the file 1103*0Sstevel@tonic-gateto protect from possible race conditions. A warning is issued 1104*0Sstevel@tonic-gateif warnings are turned on. Consider using the tmpnam() 1105*0Sstevel@tonic-gateand mktemp() functions described elsewhere in this document 1106*0Sstevel@tonic-gateif opening the file is not required. 1107*0Sstevel@tonic-gate 1108*0Sstevel@tonic-gateOptions can be combined as required. 1109*0Sstevel@tonic-gate 1110*0Sstevel@tonic-gate=cut 1111*0Sstevel@tonic-gate 1112*0Sstevel@tonic-gatesub tempfile { 1113*0Sstevel@tonic-gate 1114*0Sstevel@tonic-gate # Can not check for argument count since we can have any 1115*0Sstevel@tonic-gate # number of args 1116*0Sstevel@tonic-gate 1117*0Sstevel@tonic-gate # Default options 1118*0Sstevel@tonic-gate my %options = ( 1119*0Sstevel@tonic-gate "DIR" => undef, # Directory prefix 1120*0Sstevel@tonic-gate "SUFFIX" => '', # Template suffix 1121*0Sstevel@tonic-gate "UNLINK" => 0, # Do not unlink file on exit 1122*0Sstevel@tonic-gate "OPEN" => 1, # Open file 1123*0Sstevel@tonic-gate ); 1124*0Sstevel@tonic-gate 1125*0Sstevel@tonic-gate # Check to see whether we have an odd or even number of arguments 1126*0Sstevel@tonic-gate my $template = (scalar(@_) % 2 == 1 ? shift(@_) : undef); 1127*0Sstevel@tonic-gate 1128*0Sstevel@tonic-gate # Read the options and merge with defaults 1129*0Sstevel@tonic-gate %options = (%options, @_) if @_; 1130*0Sstevel@tonic-gate 1131*0Sstevel@tonic-gate # First decision is whether or not to open the file 1132*0Sstevel@tonic-gate if (! $options{"OPEN"}) { 1133*0Sstevel@tonic-gate 1134*0Sstevel@tonic-gate warn "tempfile(): temporary filename requested but not opened.\nPossibly unsafe, consider using tempfile() with OPEN set to true\n" 1135*0Sstevel@tonic-gate if $^W; 1136*0Sstevel@tonic-gate 1137*0Sstevel@tonic-gate } 1138*0Sstevel@tonic-gate 1139*0Sstevel@tonic-gate if ($options{"DIR"} and $^O eq 'VMS') { 1140*0Sstevel@tonic-gate 1141*0Sstevel@tonic-gate # on VMS turn []foo into [.foo] for concatenation 1142*0Sstevel@tonic-gate $options{"DIR"} = VMS::Filespec::vmspath($options{"DIR"}); 1143*0Sstevel@tonic-gate } 1144*0Sstevel@tonic-gate 1145*0Sstevel@tonic-gate # Construct the template 1146*0Sstevel@tonic-gate 1147*0Sstevel@tonic-gate # Have a choice of trying to work around the mkstemp/mktemp/tmpnam etc 1148*0Sstevel@tonic-gate # functions or simply constructing a template and using _gettemp() 1149*0Sstevel@tonic-gate # explicitly. Go for the latter 1150*0Sstevel@tonic-gate 1151*0Sstevel@tonic-gate # First generate a template if not defined and prefix the directory 1152*0Sstevel@tonic-gate # If no template must prefix the temp directory 1153*0Sstevel@tonic-gate if (defined $template) { 1154*0Sstevel@tonic-gate if ($options{"DIR"}) { 1155*0Sstevel@tonic-gate 1156*0Sstevel@tonic-gate $template = File::Spec->catfile($options{"DIR"}, $template); 1157*0Sstevel@tonic-gate 1158*0Sstevel@tonic-gate } 1159*0Sstevel@tonic-gate 1160*0Sstevel@tonic-gate } else { 1161*0Sstevel@tonic-gate 1162*0Sstevel@tonic-gate if ($options{"DIR"}) { 1163*0Sstevel@tonic-gate 1164*0Sstevel@tonic-gate $template = File::Spec->catfile($options{"DIR"}, TEMPXXX); 1165*0Sstevel@tonic-gate 1166*0Sstevel@tonic-gate } else { 1167*0Sstevel@tonic-gate 1168*0Sstevel@tonic-gate $template = File::Spec->catfile(File::Spec->tmpdir, TEMPXXX); 1169*0Sstevel@tonic-gate 1170*0Sstevel@tonic-gate } 1171*0Sstevel@tonic-gate 1172*0Sstevel@tonic-gate } 1173*0Sstevel@tonic-gate 1174*0Sstevel@tonic-gate # Now add a suffix 1175*0Sstevel@tonic-gate $template .= $options{"SUFFIX"}; 1176*0Sstevel@tonic-gate 1177*0Sstevel@tonic-gate # Determine whether we should tell _gettemp to unlink the file 1178*0Sstevel@tonic-gate # On unix this is irrelevant and can be worked out after the file is 1179*0Sstevel@tonic-gate # opened (simply by unlinking the open filehandle). On Windows or VMS 1180*0Sstevel@tonic-gate # we have to indicate temporary-ness when we open the file. In general 1181*0Sstevel@tonic-gate # we only want a true temporary file if we are returning just the 1182*0Sstevel@tonic-gate # filehandle - if the user wants the filename they probably do not 1183*0Sstevel@tonic-gate # want the file to disappear as soon as they close it. 1184*0Sstevel@tonic-gate # For this reason, tie unlink_on_close to the return context regardless 1185*0Sstevel@tonic-gate # of OS. 1186*0Sstevel@tonic-gate my $unlink_on_close = ( wantarray ? 0 : 1); 1187*0Sstevel@tonic-gate 1188*0Sstevel@tonic-gate # Create the file 1189*0Sstevel@tonic-gate my ($fh, $path, $errstr); 1190*0Sstevel@tonic-gate croak "Error in tempfile() using $template: $errstr" 1191*0Sstevel@tonic-gate unless (($fh, $path) = _gettemp($template, 1192*0Sstevel@tonic-gate "open" => $options{'OPEN'}, 1193*0Sstevel@tonic-gate "mkdir"=> 0 , 1194*0Sstevel@tonic-gate "unlink_on_close" => $unlink_on_close, 1195*0Sstevel@tonic-gate "suffixlen" => length($options{'SUFFIX'}), 1196*0Sstevel@tonic-gate "ErrStr" => \$errstr, 1197*0Sstevel@tonic-gate ) ); 1198*0Sstevel@tonic-gate 1199*0Sstevel@tonic-gate # Set up an exit handler that can do whatever is right for the 1200*0Sstevel@tonic-gate # system. This removes files at exit when requested explicitly or when 1201*0Sstevel@tonic-gate # system is asked to unlink_on_close but is unable to do so because 1202*0Sstevel@tonic-gate # of OS limitations. 1203*0Sstevel@tonic-gate # The latter should be achieved by using a tied filehandle. 1204*0Sstevel@tonic-gate # Do not check return status since this is all done with END blocks. 1205*0Sstevel@tonic-gate _deferred_unlink($fh, $path, 0) if $options{"UNLINK"}; 1206*0Sstevel@tonic-gate 1207*0Sstevel@tonic-gate # Return 1208*0Sstevel@tonic-gate if (wantarray()) { 1209*0Sstevel@tonic-gate 1210*0Sstevel@tonic-gate if ($options{'OPEN'}) { 1211*0Sstevel@tonic-gate return ($fh, $path); 1212*0Sstevel@tonic-gate } else { 1213*0Sstevel@tonic-gate return (undef, $path); 1214*0Sstevel@tonic-gate } 1215*0Sstevel@tonic-gate 1216*0Sstevel@tonic-gate } else { 1217*0Sstevel@tonic-gate 1218*0Sstevel@tonic-gate # Unlink the file. It is up to unlink0 to decide what to do with 1219*0Sstevel@tonic-gate # this (whether to unlink now or to defer until later) 1220*0Sstevel@tonic-gate unlink0($fh, $path) or croak "Error unlinking file $path using unlink0"; 1221*0Sstevel@tonic-gate 1222*0Sstevel@tonic-gate # Return just the filehandle. 1223*0Sstevel@tonic-gate return $fh; 1224*0Sstevel@tonic-gate } 1225*0Sstevel@tonic-gate 1226*0Sstevel@tonic-gate 1227*0Sstevel@tonic-gate} 1228*0Sstevel@tonic-gate 1229*0Sstevel@tonic-gate=item B<tempdir> 1230*0Sstevel@tonic-gate 1231*0Sstevel@tonic-gateThis is the recommended interface for creation of temporary directories. 1232*0Sstevel@tonic-gateThe behaviour of the function depends on the arguments: 1233*0Sstevel@tonic-gate 1234*0Sstevel@tonic-gate $tempdir = tempdir(); 1235*0Sstevel@tonic-gate 1236*0Sstevel@tonic-gateCreate a directory in tmpdir() (see L<File::Spec|File::Spec>). 1237*0Sstevel@tonic-gate 1238*0Sstevel@tonic-gate $tempdir = tempdir( $template ); 1239*0Sstevel@tonic-gate 1240*0Sstevel@tonic-gateCreate a directory from the supplied template. This template is 1241*0Sstevel@tonic-gatesimilar to that described for tempfile(). `X' characters at the end 1242*0Sstevel@tonic-gateof the template are replaced with random letters to construct the 1243*0Sstevel@tonic-gatedirectory name. At least four `X' characters must be in the template. 1244*0Sstevel@tonic-gate 1245*0Sstevel@tonic-gate $tempdir = tempdir ( DIR => $dir ); 1246*0Sstevel@tonic-gate 1247*0Sstevel@tonic-gateSpecifies the directory to use for the temporary directory. 1248*0Sstevel@tonic-gateThe temporary directory name is derived from an internal template. 1249*0Sstevel@tonic-gate 1250*0Sstevel@tonic-gate $tempdir = tempdir ( $template, DIR => $dir ); 1251*0Sstevel@tonic-gate 1252*0Sstevel@tonic-gatePrepend the supplied directory name to the template. The template 1253*0Sstevel@tonic-gateshould not include parent directory specifications itself. Any parent 1254*0Sstevel@tonic-gatedirectory specifications are removed from the template before 1255*0Sstevel@tonic-gateprepending the supplied directory. 1256*0Sstevel@tonic-gate 1257*0Sstevel@tonic-gate $tempdir = tempdir ( $template, TMPDIR => 1 ); 1258*0Sstevel@tonic-gate 1259*0Sstevel@tonic-gateUsing the supplied template, create the temporary directory in 1260*0Sstevel@tonic-gatea standard location for temporary files. Equivalent to doing 1261*0Sstevel@tonic-gate 1262*0Sstevel@tonic-gate $tempdir = tempdir ( $template, DIR => File::Spec->tmpdir); 1263*0Sstevel@tonic-gate 1264*0Sstevel@tonic-gatebut shorter. Parent directory specifications are stripped from the 1265*0Sstevel@tonic-gatetemplate itself. The C<TMPDIR> option is ignored if C<DIR> is set 1266*0Sstevel@tonic-gateexplicitly. Additionally, C<TMPDIR> is implied if neither a template 1267*0Sstevel@tonic-gatenor a directory are supplied. 1268*0Sstevel@tonic-gate 1269*0Sstevel@tonic-gate $tempdir = tempdir( $template, CLEANUP => 1); 1270*0Sstevel@tonic-gate 1271*0Sstevel@tonic-gateCreate a temporary directory using the supplied template, but 1272*0Sstevel@tonic-gateattempt to remove it (and all files inside it) when the program 1273*0Sstevel@tonic-gateexits. Note that an attempt will be made to remove all files from 1274*0Sstevel@tonic-gatethe directory even if they were not created by this module (otherwise 1275*0Sstevel@tonic-gatewhy ask to clean it up?). The directory removal is made with 1276*0Sstevel@tonic-gatethe rmtree() function from the L<File::Path|File::Path> module. 1277*0Sstevel@tonic-gateOf course, if the template is not specified, the temporary directory 1278*0Sstevel@tonic-gatewill be created in tmpdir() and will also be removed at program exit. 1279*0Sstevel@tonic-gate 1280*0Sstevel@tonic-gate=cut 1281*0Sstevel@tonic-gate 1282*0Sstevel@tonic-gate# ' 1283*0Sstevel@tonic-gate 1284*0Sstevel@tonic-gatesub tempdir { 1285*0Sstevel@tonic-gate 1286*0Sstevel@tonic-gate # Can not check for argument count since we can have any 1287*0Sstevel@tonic-gate # number of args 1288*0Sstevel@tonic-gate 1289*0Sstevel@tonic-gate # Default options 1290*0Sstevel@tonic-gate my %options = ( 1291*0Sstevel@tonic-gate "CLEANUP" => 0, # Remove directory on exit 1292*0Sstevel@tonic-gate "DIR" => '', # Root directory 1293*0Sstevel@tonic-gate "TMPDIR" => 0, # Use tempdir with template 1294*0Sstevel@tonic-gate ); 1295*0Sstevel@tonic-gate 1296*0Sstevel@tonic-gate # Check to see whether we have an odd or even number of arguments 1297*0Sstevel@tonic-gate my $template = (scalar(@_) % 2 == 1 ? shift(@_) : undef ); 1298*0Sstevel@tonic-gate 1299*0Sstevel@tonic-gate # Read the options and merge with defaults 1300*0Sstevel@tonic-gate %options = (%options, @_) if @_; 1301*0Sstevel@tonic-gate 1302*0Sstevel@tonic-gate # Modify or generate the template 1303*0Sstevel@tonic-gate 1304*0Sstevel@tonic-gate # Deal with the DIR and TMPDIR options 1305*0Sstevel@tonic-gate if (defined $template) { 1306*0Sstevel@tonic-gate 1307*0Sstevel@tonic-gate # Need to strip directory path if using DIR or TMPDIR 1308*0Sstevel@tonic-gate if ($options{'TMPDIR'} || $options{'DIR'}) { 1309*0Sstevel@tonic-gate 1310*0Sstevel@tonic-gate # Strip parent directory from the filename 1311*0Sstevel@tonic-gate # 1312*0Sstevel@tonic-gate # There is no filename at the end 1313*0Sstevel@tonic-gate $template = VMS::Filespec::vmspath($template) if $^O eq 'VMS'; 1314*0Sstevel@tonic-gate my ($volume, $directories, undef) = File::Spec->splitpath( $template, 1); 1315*0Sstevel@tonic-gate 1316*0Sstevel@tonic-gate # Last directory is then our template 1317*0Sstevel@tonic-gate $template = (File::Spec->splitdir($directories))[-1]; 1318*0Sstevel@tonic-gate 1319*0Sstevel@tonic-gate # Prepend the supplied directory or temp dir 1320*0Sstevel@tonic-gate if ($options{"DIR"}) { 1321*0Sstevel@tonic-gate 1322*0Sstevel@tonic-gate $template = File::Spec->catdir($options{"DIR"}, $template); 1323*0Sstevel@tonic-gate 1324*0Sstevel@tonic-gate } elsif ($options{TMPDIR}) { 1325*0Sstevel@tonic-gate 1326*0Sstevel@tonic-gate # Prepend tmpdir 1327*0Sstevel@tonic-gate $template = File::Spec->catdir(File::Spec->tmpdir, $template); 1328*0Sstevel@tonic-gate 1329*0Sstevel@tonic-gate } 1330*0Sstevel@tonic-gate 1331*0Sstevel@tonic-gate } 1332*0Sstevel@tonic-gate 1333*0Sstevel@tonic-gate } else { 1334*0Sstevel@tonic-gate 1335*0Sstevel@tonic-gate if ($options{"DIR"}) { 1336*0Sstevel@tonic-gate 1337*0Sstevel@tonic-gate $template = File::Spec->catdir($options{"DIR"}, TEMPXXX); 1338*0Sstevel@tonic-gate 1339*0Sstevel@tonic-gate } else { 1340*0Sstevel@tonic-gate 1341*0Sstevel@tonic-gate $template = File::Spec->catdir(File::Spec->tmpdir, TEMPXXX); 1342*0Sstevel@tonic-gate 1343*0Sstevel@tonic-gate } 1344*0Sstevel@tonic-gate 1345*0Sstevel@tonic-gate } 1346*0Sstevel@tonic-gate 1347*0Sstevel@tonic-gate # Create the directory 1348*0Sstevel@tonic-gate my $tempdir; 1349*0Sstevel@tonic-gate my $suffixlen = 0; 1350*0Sstevel@tonic-gate if ($^O eq 'VMS') { # dir names can end in delimiters 1351*0Sstevel@tonic-gate $template =~ m/([\.\]:>]+)$/; 1352*0Sstevel@tonic-gate $suffixlen = length($1); 1353*0Sstevel@tonic-gate } 1354*0Sstevel@tonic-gate if ( ($^O eq 'MacOS') && (substr($template, -1) eq ':') ) { 1355*0Sstevel@tonic-gate # dir name has a trailing ':' 1356*0Sstevel@tonic-gate ++$suffixlen; 1357*0Sstevel@tonic-gate } 1358*0Sstevel@tonic-gate 1359*0Sstevel@tonic-gate my $errstr; 1360*0Sstevel@tonic-gate croak "Error in tempdir() using $template: $errstr" 1361*0Sstevel@tonic-gate unless ((undef, $tempdir) = _gettemp($template, 1362*0Sstevel@tonic-gate "open" => 0, 1363*0Sstevel@tonic-gate "mkdir"=> 1 , 1364*0Sstevel@tonic-gate "suffixlen" => $suffixlen, 1365*0Sstevel@tonic-gate "ErrStr" => \$errstr, 1366*0Sstevel@tonic-gate ) ); 1367*0Sstevel@tonic-gate 1368*0Sstevel@tonic-gate # Install exit handler; must be dynamic to get lexical 1369*0Sstevel@tonic-gate if ( $options{'CLEANUP'} && -d $tempdir) { 1370*0Sstevel@tonic-gate _deferred_unlink(undef, $tempdir, 1); 1371*0Sstevel@tonic-gate } 1372*0Sstevel@tonic-gate 1373*0Sstevel@tonic-gate # Return the dir name 1374*0Sstevel@tonic-gate return $tempdir; 1375*0Sstevel@tonic-gate 1376*0Sstevel@tonic-gate} 1377*0Sstevel@tonic-gate 1378*0Sstevel@tonic-gate=back 1379*0Sstevel@tonic-gate 1380*0Sstevel@tonic-gate=head1 MKTEMP FUNCTIONS 1381*0Sstevel@tonic-gate 1382*0Sstevel@tonic-gateThe following functions are Perl implementations of the 1383*0Sstevel@tonic-gatemktemp() family of temp file generation system calls. 1384*0Sstevel@tonic-gate 1385*0Sstevel@tonic-gate=over 4 1386*0Sstevel@tonic-gate 1387*0Sstevel@tonic-gate=item B<mkstemp> 1388*0Sstevel@tonic-gate 1389*0Sstevel@tonic-gateGiven a template, returns a filehandle to the temporary file and the name 1390*0Sstevel@tonic-gateof the file. 1391*0Sstevel@tonic-gate 1392*0Sstevel@tonic-gate ($fh, $name) = mkstemp( $template ); 1393*0Sstevel@tonic-gate 1394*0Sstevel@tonic-gateIn scalar context, just the filehandle is returned. 1395*0Sstevel@tonic-gate 1396*0Sstevel@tonic-gateThe template may be any filename with some number of X's appended 1397*0Sstevel@tonic-gateto it, for example F</tmp/temp.XXXX>. The trailing X's are replaced 1398*0Sstevel@tonic-gatewith unique alphanumeric combinations. 1399*0Sstevel@tonic-gate 1400*0Sstevel@tonic-gate=cut 1401*0Sstevel@tonic-gate 1402*0Sstevel@tonic-gate 1403*0Sstevel@tonic-gate 1404*0Sstevel@tonic-gatesub mkstemp { 1405*0Sstevel@tonic-gate 1406*0Sstevel@tonic-gate croak "Usage: mkstemp(template)" 1407*0Sstevel@tonic-gate if scalar(@_) != 1; 1408*0Sstevel@tonic-gate 1409*0Sstevel@tonic-gate my $template = shift; 1410*0Sstevel@tonic-gate 1411*0Sstevel@tonic-gate my ($fh, $path, $errstr); 1412*0Sstevel@tonic-gate croak "Error in mkstemp using $template: $errstr" 1413*0Sstevel@tonic-gate unless (($fh, $path) = _gettemp($template, 1414*0Sstevel@tonic-gate "open" => 1, 1415*0Sstevel@tonic-gate "mkdir"=> 0 , 1416*0Sstevel@tonic-gate "suffixlen" => 0, 1417*0Sstevel@tonic-gate "ErrStr" => \$errstr, 1418*0Sstevel@tonic-gate ) ); 1419*0Sstevel@tonic-gate 1420*0Sstevel@tonic-gate if (wantarray()) { 1421*0Sstevel@tonic-gate return ($fh, $path); 1422*0Sstevel@tonic-gate } else { 1423*0Sstevel@tonic-gate return $fh; 1424*0Sstevel@tonic-gate } 1425*0Sstevel@tonic-gate 1426*0Sstevel@tonic-gate} 1427*0Sstevel@tonic-gate 1428*0Sstevel@tonic-gate 1429*0Sstevel@tonic-gate=item B<mkstemps> 1430*0Sstevel@tonic-gate 1431*0Sstevel@tonic-gateSimilar to mkstemp(), except that an extra argument can be supplied 1432*0Sstevel@tonic-gatewith a suffix to be appended to the template. 1433*0Sstevel@tonic-gate 1434*0Sstevel@tonic-gate ($fh, $name) = mkstemps( $template, $suffix ); 1435*0Sstevel@tonic-gate 1436*0Sstevel@tonic-gateFor example a template of C<testXXXXXX> and suffix of C<.dat> 1437*0Sstevel@tonic-gatewould generate a file similar to F<testhGji_w.dat>. 1438*0Sstevel@tonic-gate 1439*0Sstevel@tonic-gateReturns just the filehandle alone when called in scalar context. 1440*0Sstevel@tonic-gate 1441*0Sstevel@tonic-gate=cut 1442*0Sstevel@tonic-gate 1443*0Sstevel@tonic-gatesub mkstemps { 1444*0Sstevel@tonic-gate 1445*0Sstevel@tonic-gate croak "Usage: mkstemps(template, suffix)" 1446*0Sstevel@tonic-gate if scalar(@_) != 2; 1447*0Sstevel@tonic-gate 1448*0Sstevel@tonic-gate 1449*0Sstevel@tonic-gate my $template = shift; 1450*0Sstevel@tonic-gate my $suffix = shift; 1451*0Sstevel@tonic-gate 1452*0Sstevel@tonic-gate $template .= $suffix; 1453*0Sstevel@tonic-gate 1454*0Sstevel@tonic-gate my ($fh, $path, $errstr); 1455*0Sstevel@tonic-gate croak "Error in mkstemps using $template: $errstr" 1456*0Sstevel@tonic-gate unless (($fh, $path) = _gettemp($template, 1457*0Sstevel@tonic-gate "open" => 1, 1458*0Sstevel@tonic-gate "mkdir"=> 0 , 1459*0Sstevel@tonic-gate "suffixlen" => length($suffix), 1460*0Sstevel@tonic-gate "ErrStr" => \$errstr, 1461*0Sstevel@tonic-gate ) ); 1462*0Sstevel@tonic-gate 1463*0Sstevel@tonic-gate if (wantarray()) { 1464*0Sstevel@tonic-gate return ($fh, $path); 1465*0Sstevel@tonic-gate } else { 1466*0Sstevel@tonic-gate return $fh; 1467*0Sstevel@tonic-gate } 1468*0Sstevel@tonic-gate 1469*0Sstevel@tonic-gate} 1470*0Sstevel@tonic-gate 1471*0Sstevel@tonic-gate=item B<mkdtemp> 1472*0Sstevel@tonic-gate 1473*0Sstevel@tonic-gateCreate a directory from a template. The template must end in 1474*0Sstevel@tonic-gateX's that are replaced by the routine. 1475*0Sstevel@tonic-gate 1476*0Sstevel@tonic-gate $tmpdir_name = mkdtemp($template); 1477*0Sstevel@tonic-gate 1478*0Sstevel@tonic-gateReturns the name of the temporary directory created. 1479*0Sstevel@tonic-gateReturns undef on failure. 1480*0Sstevel@tonic-gate 1481*0Sstevel@tonic-gateDirectory must be removed by the caller. 1482*0Sstevel@tonic-gate 1483*0Sstevel@tonic-gate=cut 1484*0Sstevel@tonic-gate 1485*0Sstevel@tonic-gate#' # for emacs 1486*0Sstevel@tonic-gate 1487*0Sstevel@tonic-gatesub mkdtemp { 1488*0Sstevel@tonic-gate 1489*0Sstevel@tonic-gate croak "Usage: mkdtemp(template)" 1490*0Sstevel@tonic-gate if scalar(@_) != 1; 1491*0Sstevel@tonic-gate 1492*0Sstevel@tonic-gate my $template = shift; 1493*0Sstevel@tonic-gate my $suffixlen = 0; 1494*0Sstevel@tonic-gate if ($^O eq 'VMS') { # dir names can end in delimiters 1495*0Sstevel@tonic-gate $template =~ m/([\.\]:>]+)$/; 1496*0Sstevel@tonic-gate $suffixlen = length($1); 1497*0Sstevel@tonic-gate } 1498*0Sstevel@tonic-gate if ( ($^O eq 'MacOS') && (substr($template, -1) eq ':') ) { 1499*0Sstevel@tonic-gate # dir name has a trailing ':' 1500*0Sstevel@tonic-gate ++$suffixlen; 1501*0Sstevel@tonic-gate } 1502*0Sstevel@tonic-gate my ($junk, $tmpdir, $errstr); 1503*0Sstevel@tonic-gate croak "Error creating temp directory from template $template\: $errstr" 1504*0Sstevel@tonic-gate unless (($junk, $tmpdir) = _gettemp($template, 1505*0Sstevel@tonic-gate "open" => 0, 1506*0Sstevel@tonic-gate "mkdir"=> 1 , 1507*0Sstevel@tonic-gate "suffixlen" => $suffixlen, 1508*0Sstevel@tonic-gate "ErrStr" => \$errstr, 1509*0Sstevel@tonic-gate ) ); 1510*0Sstevel@tonic-gate 1511*0Sstevel@tonic-gate return $tmpdir; 1512*0Sstevel@tonic-gate 1513*0Sstevel@tonic-gate} 1514*0Sstevel@tonic-gate 1515*0Sstevel@tonic-gate=item B<mktemp> 1516*0Sstevel@tonic-gate 1517*0Sstevel@tonic-gateReturns a valid temporary filename but does not guarantee 1518*0Sstevel@tonic-gatethat the file will not be opened by someone else. 1519*0Sstevel@tonic-gate 1520*0Sstevel@tonic-gate $unopened_file = mktemp($template); 1521*0Sstevel@tonic-gate 1522*0Sstevel@tonic-gateTemplate is the same as that required by mkstemp(). 1523*0Sstevel@tonic-gate 1524*0Sstevel@tonic-gate=cut 1525*0Sstevel@tonic-gate 1526*0Sstevel@tonic-gatesub mktemp { 1527*0Sstevel@tonic-gate 1528*0Sstevel@tonic-gate croak "Usage: mktemp(template)" 1529*0Sstevel@tonic-gate if scalar(@_) != 1; 1530*0Sstevel@tonic-gate 1531*0Sstevel@tonic-gate my $template = shift; 1532*0Sstevel@tonic-gate 1533*0Sstevel@tonic-gate my ($tmpname, $junk, $errstr); 1534*0Sstevel@tonic-gate croak "Error getting name to temp file from template $template: $errstr" 1535*0Sstevel@tonic-gate unless (($junk, $tmpname) = _gettemp($template, 1536*0Sstevel@tonic-gate "open" => 0, 1537*0Sstevel@tonic-gate "mkdir"=> 0 , 1538*0Sstevel@tonic-gate "suffixlen" => 0, 1539*0Sstevel@tonic-gate "ErrStr" => \$errstr, 1540*0Sstevel@tonic-gate ) ); 1541*0Sstevel@tonic-gate 1542*0Sstevel@tonic-gate return $tmpname; 1543*0Sstevel@tonic-gate} 1544*0Sstevel@tonic-gate 1545*0Sstevel@tonic-gate=back 1546*0Sstevel@tonic-gate 1547*0Sstevel@tonic-gate=head1 POSIX FUNCTIONS 1548*0Sstevel@tonic-gate 1549*0Sstevel@tonic-gateThis section describes the re-implementation of the tmpnam() 1550*0Sstevel@tonic-gateand tmpfile() functions described in L<POSIX> 1551*0Sstevel@tonic-gateusing the mkstemp() from this module. 1552*0Sstevel@tonic-gate 1553*0Sstevel@tonic-gateUnlike the L<POSIX|POSIX> implementations, the directory used 1554*0Sstevel@tonic-gatefor the temporary file is not specified in a system include 1555*0Sstevel@tonic-gatefile (C<P_tmpdir>) but simply depends on the choice of tmpdir() 1556*0Sstevel@tonic-gatereturned by L<File::Spec|File::Spec>. On some implementations this 1557*0Sstevel@tonic-gatelocation can be set using the C<TMPDIR> environment variable, which 1558*0Sstevel@tonic-gatemay not be secure. 1559*0Sstevel@tonic-gateIf this is a problem, simply use mkstemp() and specify a template. 1560*0Sstevel@tonic-gate 1561*0Sstevel@tonic-gate=over 4 1562*0Sstevel@tonic-gate 1563*0Sstevel@tonic-gate=item B<tmpnam> 1564*0Sstevel@tonic-gate 1565*0Sstevel@tonic-gateWhen called in scalar context, returns the full name (including path) 1566*0Sstevel@tonic-gateof a temporary file (uses mktemp()). The only check is that the file does 1567*0Sstevel@tonic-gatenot already exist, but there is no guarantee that that condition will 1568*0Sstevel@tonic-gatecontinue to apply. 1569*0Sstevel@tonic-gate 1570*0Sstevel@tonic-gate $file = tmpnam(); 1571*0Sstevel@tonic-gate 1572*0Sstevel@tonic-gateWhen called in list context, a filehandle to the open file and 1573*0Sstevel@tonic-gatea filename are returned. This is achieved by calling mkstemp() 1574*0Sstevel@tonic-gateafter constructing a suitable template. 1575*0Sstevel@tonic-gate 1576*0Sstevel@tonic-gate ($fh, $file) = tmpnam(); 1577*0Sstevel@tonic-gate 1578*0Sstevel@tonic-gateIf possible, this form should be used to prevent possible 1579*0Sstevel@tonic-gaterace conditions. 1580*0Sstevel@tonic-gate 1581*0Sstevel@tonic-gateSee L<File::Spec/tmpdir> for information on the choice of temporary 1582*0Sstevel@tonic-gatedirectory for a particular operating system. 1583*0Sstevel@tonic-gate 1584*0Sstevel@tonic-gate=cut 1585*0Sstevel@tonic-gate 1586*0Sstevel@tonic-gatesub tmpnam { 1587*0Sstevel@tonic-gate 1588*0Sstevel@tonic-gate # Retrieve the temporary directory name 1589*0Sstevel@tonic-gate my $tmpdir = File::Spec->tmpdir; 1590*0Sstevel@tonic-gate 1591*0Sstevel@tonic-gate croak "Error temporary directory is not writable" 1592*0Sstevel@tonic-gate if $tmpdir eq ''; 1593*0Sstevel@tonic-gate 1594*0Sstevel@tonic-gate # Use a ten character template and append to tmpdir 1595*0Sstevel@tonic-gate my $template = File::Spec->catfile($tmpdir, TEMPXXX); 1596*0Sstevel@tonic-gate 1597*0Sstevel@tonic-gate if (wantarray() ) { 1598*0Sstevel@tonic-gate return mkstemp($template); 1599*0Sstevel@tonic-gate } else { 1600*0Sstevel@tonic-gate return mktemp($template); 1601*0Sstevel@tonic-gate } 1602*0Sstevel@tonic-gate 1603*0Sstevel@tonic-gate} 1604*0Sstevel@tonic-gate 1605*0Sstevel@tonic-gate=item B<tmpfile> 1606*0Sstevel@tonic-gate 1607*0Sstevel@tonic-gateReturns the filehandle of a temporary file. 1608*0Sstevel@tonic-gate 1609*0Sstevel@tonic-gate $fh = tmpfile(); 1610*0Sstevel@tonic-gate 1611*0Sstevel@tonic-gateThe file is removed when the filehandle is closed or when the program 1612*0Sstevel@tonic-gateexits. No access to the filename is provided. 1613*0Sstevel@tonic-gate 1614*0Sstevel@tonic-gateIf the temporary file can not be created undef is returned. 1615*0Sstevel@tonic-gateCurrently this command will probably not work when the temporary 1616*0Sstevel@tonic-gatedirectory is on an NFS file system. 1617*0Sstevel@tonic-gate 1618*0Sstevel@tonic-gate=cut 1619*0Sstevel@tonic-gate 1620*0Sstevel@tonic-gatesub tmpfile { 1621*0Sstevel@tonic-gate 1622*0Sstevel@tonic-gate # Simply call tmpnam() in a list context 1623*0Sstevel@tonic-gate my ($fh, $file) = tmpnam(); 1624*0Sstevel@tonic-gate 1625*0Sstevel@tonic-gate # Make sure file is removed when filehandle is closed 1626*0Sstevel@tonic-gate # This will fail on NFS 1627*0Sstevel@tonic-gate unlink0($fh, $file) 1628*0Sstevel@tonic-gate or return undef; 1629*0Sstevel@tonic-gate 1630*0Sstevel@tonic-gate return $fh; 1631*0Sstevel@tonic-gate 1632*0Sstevel@tonic-gate} 1633*0Sstevel@tonic-gate 1634*0Sstevel@tonic-gate=back 1635*0Sstevel@tonic-gate 1636*0Sstevel@tonic-gate=head1 ADDITIONAL FUNCTIONS 1637*0Sstevel@tonic-gate 1638*0Sstevel@tonic-gateThese functions are provided for backwards compatibility 1639*0Sstevel@tonic-gatewith common tempfile generation C library functions. 1640*0Sstevel@tonic-gate 1641*0Sstevel@tonic-gateThey are not exported and must be addressed using the full package 1642*0Sstevel@tonic-gatename. 1643*0Sstevel@tonic-gate 1644*0Sstevel@tonic-gate=over 4 1645*0Sstevel@tonic-gate 1646*0Sstevel@tonic-gate=item B<tempnam> 1647*0Sstevel@tonic-gate 1648*0Sstevel@tonic-gateReturn the name of a temporary file in the specified directory 1649*0Sstevel@tonic-gateusing a prefix. The file is guaranteed not to exist at the time 1650*0Sstevel@tonic-gatethe function was called, but such guarantees are good for one 1651*0Sstevel@tonic-gateclock tick only. Always use the proper form of C<sysopen> 1652*0Sstevel@tonic-gatewith C<O_CREAT | O_EXCL> if you must open such a filename. 1653*0Sstevel@tonic-gate 1654*0Sstevel@tonic-gate $filename = File::Temp::tempnam( $dir, $prefix ); 1655*0Sstevel@tonic-gate 1656*0Sstevel@tonic-gateEquivalent to running mktemp() with $dir/$prefixXXXXXXXX 1657*0Sstevel@tonic-gate(using unix file convention as an example) 1658*0Sstevel@tonic-gate 1659*0Sstevel@tonic-gateBecause this function uses mktemp(), it can suffer from race conditions. 1660*0Sstevel@tonic-gate 1661*0Sstevel@tonic-gate=cut 1662*0Sstevel@tonic-gate 1663*0Sstevel@tonic-gatesub tempnam { 1664*0Sstevel@tonic-gate 1665*0Sstevel@tonic-gate croak 'Usage tempnam($dir, $prefix)' unless scalar(@_) == 2; 1666*0Sstevel@tonic-gate 1667*0Sstevel@tonic-gate my ($dir, $prefix) = @_; 1668*0Sstevel@tonic-gate 1669*0Sstevel@tonic-gate # Add a string to the prefix 1670*0Sstevel@tonic-gate $prefix .= 'XXXXXXXX'; 1671*0Sstevel@tonic-gate 1672*0Sstevel@tonic-gate # Concatenate the directory to the file 1673*0Sstevel@tonic-gate my $template = File::Spec->catfile($dir, $prefix); 1674*0Sstevel@tonic-gate 1675*0Sstevel@tonic-gate return mktemp($template); 1676*0Sstevel@tonic-gate 1677*0Sstevel@tonic-gate} 1678*0Sstevel@tonic-gate 1679*0Sstevel@tonic-gate=back 1680*0Sstevel@tonic-gate 1681*0Sstevel@tonic-gate=head1 UTILITY FUNCTIONS 1682*0Sstevel@tonic-gate 1683*0Sstevel@tonic-gateUseful functions for dealing with the filehandle and filename. 1684*0Sstevel@tonic-gate 1685*0Sstevel@tonic-gate=over 4 1686*0Sstevel@tonic-gate 1687*0Sstevel@tonic-gate=item B<unlink0> 1688*0Sstevel@tonic-gate 1689*0Sstevel@tonic-gateGiven an open filehandle and the associated filename, make a safe 1690*0Sstevel@tonic-gateunlink. This is achieved by first checking that the filename and 1691*0Sstevel@tonic-gatefilehandle initially point to the same file and that the number of 1692*0Sstevel@tonic-gatelinks to the file is 1 (all fields returned by stat() are compared). 1693*0Sstevel@tonic-gateThen the filename is unlinked and the filehandle checked once again to 1694*0Sstevel@tonic-gateverify that the number of links on that file is now 0. This is the 1695*0Sstevel@tonic-gateclosest you can come to making sure that the filename unlinked was the 1696*0Sstevel@tonic-gatesame as the file whose descriptor you hold. 1697*0Sstevel@tonic-gate 1698*0Sstevel@tonic-gate unlink0($fh, $path) or die "Error unlinking file $path safely"; 1699*0Sstevel@tonic-gate 1700*0Sstevel@tonic-gateReturns false on error. The filehandle is not closed since on some 1701*0Sstevel@tonic-gateoccasions this is not required. 1702*0Sstevel@tonic-gate 1703*0Sstevel@tonic-gateOn some platforms, for example Windows NT, it is not possible to 1704*0Sstevel@tonic-gateunlink an open file (the file must be closed first). On those 1705*0Sstevel@tonic-gateplatforms, the actual unlinking is deferred until the program ends and 1706*0Sstevel@tonic-gategood status is returned. A check is still performed to make sure that 1707*0Sstevel@tonic-gatethe filehandle and filename are pointing to the same thing (but not at 1708*0Sstevel@tonic-gatethe time the end block is executed since the deferred removal may not 1709*0Sstevel@tonic-gatehave access to the filehandle). 1710*0Sstevel@tonic-gate 1711*0Sstevel@tonic-gateAdditionally, on Windows NT not all the fields returned by stat() can 1712*0Sstevel@tonic-gatebe compared. For example, the C<dev> and C<rdev> fields seem to be 1713*0Sstevel@tonic-gatedifferent. Also, it seems that the size of the file returned by stat() 1714*0Sstevel@tonic-gatedoes not always agree, with C<stat(FH)> being more accurate than 1715*0Sstevel@tonic-gateC<stat(filename)>, presumably because of caching issues even when 1716*0Sstevel@tonic-gateusing autoflush (this is usually overcome by waiting a while after 1717*0Sstevel@tonic-gatewriting to the tempfile before attempting to C<unlink0> it). 1718*0Sstevel@tonic-gate 1719*0Sstevel@tonic-gateFinally, on NFS file systems the link count of the file handle does 1720*0Sstevel@tonic-gatenot always go to zero immediately after unlinking. Currently, this 1721*0Sstevel@tonic-gatecommand is expected to fail on NFS disks. 1722*0Sstevel@tonic-gate 1723*0Sstevel@tonic-gate=cut 1724*0Sstevel@tonic-gate 1725*0Sstevel@tonic-gatesub unlink0 { 1726*0Sstevel@tonic-gate 1727*0Sstevel@tonic-gate croak 'Usage: unlink0(filehandle, filename)' 1728*0Sstevel@tonic-gate unless scalar(@_) == 2; 1729*0Sstevel@tonic-gate 1730*0Sstevel@tonic-gate # Read args 1731*0Sstevel@tonic-gate my ($fh, $path) = @_; 1732*0Sstevel@tonic-gate 1733*0Sstevel@tonic-gate cmpstat($fh, $path) or return 0; 1734*0Sstevel@tonic-gate 1735*0Sstevel@tonic-gate # attempt remove the file (does not work on some platforms) 1736*0Sstevel@tonic-gate if (_can_unlink_opened_file()) { 1737*0Sstevel@tonic-gate # XXX: do *not* call this on a directory; possible race 1738*0Sstevel@tonic-gate # resulting in recursive removal 1739*0Sstevel@tonic-gate croak "unlink0: $path has become a directory!" if -d $path; 1740*0Sstevel@tonic-gate unlink($path) or return 0; 1741*0Sstevel@tonic-gate 1742*0Sstevel@tonic-gate # Stat the filehandle 1743*0Sstevel@tonic-gate my @fh = stat $fh; 1744*0Sstevel@tonic-gate 1745*0Sstevel@tonic-gate print "Link count = $fh[3] \n" if $DEBUG; 1746*0Sstevel@tonic-gate 1747*0Sstevel@tonic-gate # Make sure that the link count is zero 1748*0Sstevel@tonic-gate # - Cygwin provides deferred unlinking, however, 1749*0Sstevel@tonic-gate # on Win9x the link count remains 1 1750*0Sstevel@tonic-gate # On NFS the link count may still be 1 but we cant know that 1751*0Sstevel@tonic-gate # we are on NFS 1752*0Sstevel@tonic-gate return ( $fh[3] == 0 or $^O eq 'cygwin' ? 1 : 0); 1753*0Sstevel@tonic-gate 1754*0Sstevel@tonic-gate } else { 1755*0Sstevel@tonic-gate _deferred_unlink($fh, $path, 0); 1756*0Sstevel@tonic-gate return 1; 1757*0Sstevel@tonic-gate } 1758*0Sstevel@tonic-gate 1759*0Sstevel@tonic-gate} 1760*0Sstevel@tonic-gate 1761*0Sstevel@tonic-gate=item B<cmpstat> 1762*0Sstevel@tonic-gate 1763*0Sstevel@tonic-gateCompare C<stat> of filehandle with C<stat> of provided filename. This 1764*0Sstevel@tonic-gatecan be used to check that the filename and filehandle initially point 1765*0Sstevel@tonic-gateto the same file and that the number of links to the file is 1 (all 1766*0Sstevel@tonic-gatefields returned by stat() are compared). 1767*0Sstevel@tonic-gate 1768*0Sstevel@tonic-gate cmpstat($fh, $path) or die "Error comparing handle with file"; 1769*0Sstevel@tonic-gate 1770*0Sstevel@tonic-gateReturns false if the stat information differs or if the link count is 1771*0Sstevel@tonic-gategreater than 1. 1772*0Sstevel@tonic-gate 1773*0Sstevel@tonic-gateOn certain platofms, eg Windows, not all the fields returned by stat() 1774*0Sstevel@tonic-gatecan be compared. For example, the C<dev> and C<rdev> fields seem to be 1775*0Sstevel@tonic-gatedifferent in Windows. Also, it seems that the size of the file 1776*0Sstevel@tonic-gatereturned by stat() does not always agree, with C<stat(FH)> being more 1777*0Sstevel@tonic-gateaccurate than C<stat(filename)>, presumably because of caching issues 1778*0Sstevel@tonic-gateeven when using autoflush (this is usually overcome by waiting a while 1779*0Sstevel@tonic-gateafter writing to the tempfile before attempting to C<unlink0> it). 1780*0Sstevel@tonic-gate 1781*0Sstevel@tonic-gateNot exported by default. 1782*0Sstevel@tonic-gate 1783*0Sstevel@tonic-gate=cut 1784*0Sstevel@tonic-gate 1785*0Sstevel@tonic-gatesub cmpstat { 1786*0Sstevel@tonic-gate 1787*0Sstevel@tonic-gate croak 'Usage: cmpstat(filehandle, filename)' 1788*0Sstevel@tonic-gate unless scalar(@_) == 2; 1789*0Sstevel@tonic-gate 1790*0Sstevel@tonic-gate # Read args 1791*0Sstevel@tonic-gate my ($fh, $path) = @_; 1792*0Sstevel@tonic-gate 1793*0Sstevel@tonic-gate warn "Comparing stat\n" 1794*0Sstevel@tonic-gate if $DEBUG; 1795*0Sstevel@tonic-gate 1796*0Sstevel@tonic-gate # Stat the filehandle - which may be closed if someone has manually 1797*0Sstevel@tonic-gate # closed the file. Can not turn off warnings without using $^W 1798*0Sstevel@tonic-gate # unless we upgrade to 5.006 minimum requirement 1799*0Sstevel@tonic-gate my @fh; 1800*0Sstevel@tonic-gate { 1801*0Sstevel@tonic-gate local ($^W) = 0; 1802*0Sstevel@tonic-gate @fh = stat $fh; 1803*0Sstevel@tonic-gate } 1804*0Sstevel@tonic-gate return unless @fh; 1805*0Sstevel@tonic-gate 1806*0Sstevel@tonic-gate if ($fh[3] > 1 && $^W) { 1807*0Sstevel@tonic-gate carp "unlink0: fstat found too many links; SB=@fh" if $^W; 1808*0Sstevel@tonic-gate } 1809*0Sstevel@tonic-gate 1810*0Sstevel@tonic-gate # Stat the path 1811*0Sstevel@tonic-gate my @path = stat $path; 1812*0Sstevel@tonic-gate 1813*0Sstevel@tonic-gate unless (@path) { 1814*0Sstevel@tonic-gate carp "unlink0: $path is gone already" if $^W; 1815*0Sstevel@tonic-gate return; 1816*0Sstevel@tonic-gate } 1817*0Sstevel@tonic-gate 1818*0Sstevel@tonic-gate # this is no longer a file, but may be a directory, or worse 1819*0Sstevel@tonic-gate unless (-f _) { 1820*0Sstevel@tonic-gate confess "panic: $path is no longer a file: SB=@fh"; 1821*0Sstevel@tonic-gate } 1822*0Sstevel@tonic-gate 1823*0Sstevel@tonic-gate # Do comparison of each member of the array 1824*0Sstevel@tonic-gate # On WinNT dev and rdev seem to be different 1825*0Sstevel@tonic-gate # depending on whether it is a file or a handle. 1826*0Sstevel@tonic-gate # Cannot simply compare all members of the stat return 1827*0Sstevel@tonic-gate # Select the ones we can use 1828*0Sstevel@tonic-gate my @okstat = (0..$#fh); # Use all by default 1829*0Sstevel@tonic-gate if ($^O eq 'MSWin32') { 1830*0Sstevel@tonic-gate @okstat = (1,2,3,4,5,7,8,9,10); 1831*0Sstevel@tonic-gate } elsif ($^O eq 'os2') { 1832*0Sstevel@tonic-gate @okstat = (0, 2..$#fh); 1833*0Sstevel@tonic-gate } elsif ($^O eq 'VMS') { # device and file ID are sufficient 1834*0Sstevel@tonic-gate @okstat = (0, 1); 1835*0Sstevel@tonic-gate } elsif ($^O eq 'dos') { 1836*0Sstevel@tonic-gate @okstat = (0,2..7,11..$#fh); 1837*0Sstevel@tonic-gate } elsif ($^O eq 'mpeix') { 1838*0Sstevel@tonic-gate @okstat = (0..4,8..10); 1839*0Sstevel@tonic-gate } 1840*0Sstevel@tonic-gate 1841*0Sstevel@tonic-gate # Now compare each entry explicitly by number 1842*0Sstevel@tonic-gate for (@okstat) { 1843*0Sstevel@tonic-gate print "Comparing: $_ : $fh[$_] and $path[$_]\n" if $DEBUG; 1844*0Sstevel@tonic-gate # Use eq rather than == since rdev, blksize, and blocks (6, 11, 1845*0Sstevel@tonic-gate # and 12) will be '' on platforms that do not support them. This 1846*0Sstevel@tonic-gate # is fine since we are only comparing integers. 1847*0Sstevel@tonic-gate unless ($fh[$_] eq $path[$_]) { 1848*0Sstevel@tonic-gate warn "Did not match $_ element of stat\n" if $DEBUG; 1849*0Sstevel@tonic-gate return 0; 1850*0Sstevel@tonic-gate } 1851*0Sstevel@tonic-gate } 1852*0Sstevel@tonic-gate 1853*0Sstevel@tonic-gate return 1; 1854*0Sstevel@tonic-gate} 1855*0Sstevel@tonic-gate 1856*0Sstevel@tonic-gate=item B<unlink1> 1857*0Sstevel@tonic-gate 1858*0Sstevel@tonic-gateSimilar to C<unlink0> except after file comparison using cmpstat, the 1859*0Sstevel@tonic-gatefilehandle is closed prior to attempting to unlink the file. This 1860*0Sstevel@tonic-gateallows the file to be removed without using an END block, but does 1861*0Sstevel@tonic-gatemean that the post-unlink comparison of the filehandle state provided 1862*0Sstevel@tonic-gateby C<unlink0> is not available. 1863*0Sstevel@tonic-gate 1864*0Sstevel@tonic-gate unlink1($fh, $path) or die "Error closing and unlinking file"; 1865*0Sstevel@tonic-gate 1866*0Sstevel@tonic-gateUsually called from the object destructor when using the OO interface. 1867*0Sstevel@tonic-gate 1868*0Sstevel@tonic-gateNot exported by default. 1869*0Sstevel@tonic-gate 1870*0Sstevel@tonic-gate=cut 1871*0Sstevel@tonic-gate 1872*0Sstevel@tonic-gatesub unlink1 { 1873*0Sstevel@tonic-gate croak 'Usage: unlink1(filehandle, filename)' 1874*0Sstevel@tonic-gate unless scalar(@_) == 2; 1875*0Sstevel@tonic-gate 1876*0Sstevel@tonic-gate # Read args 1877*0Sstevel@tonic-gate my ($fh, $path) = @_; 1878*0Sstevel@tonic-gate 1879*0Sstevel@tonic-gate cmpstat($fh, $path) or return 0; 1880*0Sstevel@tonic-gate 1881*0Sstevel@tonic-gate # Close the file 1882*0Sstevel@tonic-gate close( $fh ) or return 0; 1883*0Sstevel@tonic-gate 1884*0Sstevel@tonic-gate # remove the file 1885*0Sstevel@tonic-gate return unlink($path); 1886*0Sstevel@tonic-gate} 1887*0Sstevel@tonic-gate 1888*0Sstevel@tonic-gate=back 1889*0Sstevel@tonic-gate 1890*0Sstevel@tonic-gate=head1 PACKAGE VARIABLES 1891*0Sstevel@tonic-gate 1892*0Sstevel@tonic-gateThese functions control the global state of the package. 1893*0Sstevel@tonic-gate 1894*0Sstevel@tonic-gate=over 4 1895*0Sstevel@tonic-gate 1896*0Sstevel@tonic-gate=item B<safe_level> 1897*0Sstevel@tonic-gate 1898*0Sstevel@tonic-gateControls the lengths to which the module will go to check the safety of the 1899*0Sstevel@tonic-gatetemporary file or directory before proceeding. 1900*0Sstevel@tonic-gateOptions are: 1901*0Sstevel@tonic-gate 1902*0Sstevel@tonic-gate=over 8 1903*0Sstevel@tonic-gate 1904*0Sstevel@tonic-gate=item STANDARD 1905*0Sstevel@tonic-gate 1906*0Sstevel@tonic-gateDo the basic security measures to ensure the directory exists and 1907*0Sstevel@tonic-gateis writable, that the umask() is fixed before opening of the file, 1908*0Sstevel@tonic-gatethat temporary files are opened only if they do not already exist, and 1909*0Sstevel@tonic-gatethat possible race conditions are avoided. Finally the L<unlink0|"unlink0"> 1910*0Sstevel@tonic-gatefunction is used to remove files safely. 1911*0Sstevel@tonic-gate 1912*0Sstevel@tonic-gate=item MEDIUM 1913*0Sstevel@tonic-gate 1914*0Sstevel@tonic-gateIn addition to the STANDARD security, the output directory is checked 1915*0Sstevel@tonic-gateto make sure that it is owned either by root or the user running the 1916*0Sstevel@tonic-gateprogram. If the directory is writable by group or by other, it is then 1917*0Sstevel@tonic-gatechecked to make sure that the sticky bit is set. 1918*0Sstevel@tonic-gate 1919*0Sstevel@tonic-gateWill not work on platforms that do not support the C<-k> test 1920*0Sstevel@tonic-gatefor sticky bit. 1921*0Sstevel@tonic-gate 1922*0Sstevel@tonic-gate=item HIGH 1923*0Sstevel@tonic-gate 1924*0Sstevel@tonic-gateIn addition to the MEDIUM security checks, also check for the 1925*0Sstevel@tonic-gatepossibility of ``chown() giveaway'' using the L<POSIX|POSIX> 1926*0Sstevel@tonic-gatesysconf() function. If this is a possibility, each directory in the 1927*0Sstevel@tonic-gatepath is checked in turn for safeness, recursively walking back to the 1928*0Sstevel@tonic-gateroot directory. 1929*0Sstevel@tonic-gate 1930*0Sstevel@tonic-gateFor platforms that do not support the L<POSIX|POSIX> 1931*0Sstevel@tonic-gateC<_PC_CHOWN_RESTRICTED> symbol (for example, Windows NT) it is 1932*0Sstevel@tonic-gateassumed that ``chown() giveaway'' is possible and the recursive test 1933*0Sstevel@tonic-gateis performed. 1934*0Sstevel@tonic-gate 1935*0Sstevel@tonic-gate=back 1936*0Sstevel@tonic-gate 1937*0Sstevel@tonic-gateThe level can be changed as follows: 1938*0Sstevel@tonic-gate 1939*0Sstevel@tonic-gate File::Temp->safe_level( File::Temp::HIGH ); 1940*0Sstevel@tonic-gate 1941*0Sstevel@tonic-gateThe level constants are not exported by the module. 1942*0Sstevel@tonic-gate 1943*0Sstevel@tonic-gateCurrently, you must be running at least perl v5.6.0 in order to 1944*0Sstevel@tonic-gaterun with MEDIUM or HIGH security. This is simply because the 1945*0Sstevel@tonic-gatesafety tests use functions from L<Fcntl|Fcntl> that are not 1946*0Sstevel@tonic-gateavailable in older versions of perl. The problem is that the version 1947*0Sstevel@tonic-gatenumber for Fcntl is the same in perl 5.6.0 and in 5.005_03 even though 1948*0Sstevel@tonic-gatethey are different versions. 1949*0Sstevel@tonic-gate 1950*0Sstevel@tonic-gateOn systems that do not support the HIGH or MEDIUM safety levels 1951*0Sstevel@tonic-gate(for example Win NT or OS/2) any attempt to change the level will 1952*0Sstevel@tonic-gatebe ignored. The decision to ignore rather than raise an exception 1953*0Sstevel@tonic-gateallows portable programs to be written with high security in mind 1954*0Sstevel@tonic-gatefor the systems that can support this without those programs failing 1955*0Sstevel@tonic-gateon systems where the extra tests are irrelevant. 1956*0Sstevel@tonic-gate 1957*0Sstevel@tonic-gateIf you really need to see whether the change has been accepted 1958*0Sstevel@tonic-gatesimply examine the return value of C<safe_level>. 1959*0Sstevel@tonic-gate 1960*0Sstevel@tonic-gate $newlevel = File::Temp->safe_level( File::Temp::HIGH ); 1961*0Sstevel@tonic-gate die "Could not change to high security" 1962*0Sstevel@tonic-gate if $newlevel != File::Temp::HIGH; 1963*0Sstevel@tonic-gate 1964*0Sstevel@tonic-gate=cut 1965*0Sstevel@tonic-gate 1966*0Sstevel@tonic-gate{ 1967*0Sstevel@tonic-gate # protect from using the variable itself 1968*0Sstevel@tonic-gate my $LEVEL = STANDARD; 1969*0Sstevel@tonic-gate sub safe_level { 1970*0Sstevel@tonic-gate my $self = shift; 1971*0Sstevel@tonic-gate if (@_) { 1972*0Sstevel@tonic-gate my $level = shift; 1973*0Sstevel@tonic-gate if (($level != STANDARD) && ($level != MEDIUM) && ($level != HIGH)) { 1974*0Sstevel@tonic-gate carp "safe_level: Specified level ($level) not STANDARD, MEDIUM or HIGH - ignoring\n" if $^W; 1975*0Sstevel@tonic-gate } else { 1976*0Sstevel@tonic-gate # Dont allow this on perl 5.005 or earlier 1977*0Sstevel@tonic-gate if ($] < 5.006 && $level != STANDARD) { 1978*0Sstevel@tonic-gate # Cant do MEDIUM or HIGH checks 1979*0Sstevel@tonic-gate croak "Currently requires perl 5.006 or newer to do the safe checks"; 1980*0Sstevel@tonic-gate } 1981*0Sstevel@tonic-gate # Check that we are allowed to change level 1982*0Sstevel@tonic-gate # Silently ignore if we can not. 1983*0Sstevel@tonic-gate $LEVEL = $level if _can_do_level($level); 1984*0Sstevel@tonic-gate } 1985*0Sstevel@tonic-gate } 1986*0Sstevel@tonic-gate return $LEVEL; 1987*0Sstevel@tonic-gate } 1988*0Sstevel@tonic-gate} 1989*0Sstevel@tonic-gate 1990*0Sstevel@tonic-gate=item TopSystemUID 1991*0Sstevel@tonic-gate 1992*0Sstevel@tonic-gateThis is the highest UID on the current system that refers to a root 1993*0Sstevel@tonic-gateUID. This is used to make sure that the temporary directory is 1994*0Sstevel@tonic-gateowned by a system UID (C<root>, C<bin>, C<sys> etc) rather than 1995*0Sstevel@tonic-gatesimply by root. 1996*0Sstevel@tonic-gate 1997*0Sstevel@tonic-gateThis is required since on many unix systems C</tmp> is not owned 1998*0Sstevel@tonic-gateby root. 1999*0Sstevel@tonic-gate 2000*0Sstevel@tonic-gateDefault is to assume that any UID less than or equal to 10 is a root 2001*0Sstevel@tonic-gateUID. 2002*0Sstevel@tonic-gate 2003*0Sstevel@tonic-gate File::Temp->top_system_uid(10); 2004*0Sstevel@tonic-gate my $topid = File::Temp->top_system_uid; 2005*0Sstevel@tonic-gate 2006*0Sstevel@tonic-gateThis value can be adjusted to reduce security checking if required. 2007*0Sstevel@tonic-gateThe value is only relevant when C<safe_level> is set to MEDIUM or higher. 2008*0Sstevel@tonic-gate 2009*0Sstevel@tonic-gate=back 2010*0Sstevel@tonic-gate 2011*0Sstevel@tonic-gate=cut 2012*0Sstevel@tonic-gate 2013*0Sstevel@tonic-gate{ 2014*0Sstevel@tonic-gate my $TopSystemUID = 10; 2015*0Sstevel@tonic-gate sub top_system_uid { 2016*0Sstevel@tonic-gate my $self = shift; 2017*0Sstevel@tonic-gate if (@_) { 2018*0Sstevel@tonic-gate my $newuid = shift; 2019*0Sstevel@tonic-gate croak "top_system_uid: UIDs should be numeric" 2020*0Sstevel@tonic-gate unless $newuid =~ /^\d+$/s; 2021*0Sstevel@tonic-gate $TopSystemUID = $newuid; 2022*0Sstevel@tonic-gate } 2023*0Sstevel@tonic-gate return $TopSystemUID; 2024*0Sstevel@tonic-gate } 2025*0Sstevel@tonic-gate} 2026*0Sstevel@tonic-gate 2027*0Sstevel@tonic-gate=head1 WARNING 2028*0Sstevel@tonic-gate 2029*0Sstevel@tonic-gateFor maximum security, endeavour always to avoid ever looking at, 2030*0Sstevel@tonic-gatetouching, or even imputing the existence of the filename. You do not 2031*0Sstevel@tonic-gateknow that that filename is connected to the same file as the handle 2032*0Sstevel@tonic-gateyou have, and attempts to check this can only trigger more race 2033*0Sstevel@tonic-gateconditions. It's far more secure to use the filehandle alone and 2034*0Sstevel@tonic-gatedispense with the filename altogether. 2035*0Sstevel@tonic-gate 2036*0Sstevel@tonic-gateIf you need to pass the handle to something that expects a filename 2037*0Sstevel@tonic-gatethen, on a unix system, use C<"/dev/fd/" . fileno($fh)> for arbitrary 2038*0Sstevel@tonic-gateprograms, or more generally C<< "+<=&" . fileno($fh) >> for Perl 2039*0Sstevel@tonic-gateprograms. You will have to clear the close-on-exec bit on that file 2040*0Sstevel@tonic-gatedescriptor before passing it to another process. 2041*0Sstevel@tonic-gate 2042*0Sstevel@tonic-gate use Fcntl qw/F_SETFD F_GETFD/; 2043*0Sstevel@tonic-gate fcntl($tmpfh, F_SETFD, 0) 2044*0Sstevel@tonic-gate or die "Can't clear close-on-exec flag on temp fh: $!\n"; 2045*0Sstevel@tonic-gate 2046*0Sstevel@tonic-gate=head2 Temporary files and NFS 2047*0Sstevel@tonic-gate 2048*0Sstevel@tonic-gateSome problems are associated with using temporary files that reside 2049*0Sstevel@tonic-gateon NFS file systems and it is recommended that a local filesystem 2050*0Sstevel@tonic-gateis used whenever possible. Some of the security tests will most probably 2051*0Sstevel@tonic-gatefail when the temp file is not local. Additionally, be aware that 2052*0Sstevel@tonic-gatethe performance of I/O operations over NFS will not be as good as for 2053*0Sstevel@tonic-gatea local disk. 2054*0Sstevel@tonic-gate 2055*0Sstevel@tonic-gate=head1 HISTORY 2056*0Sstevel@tonic-gate 2057*0Sstevel@tonic-gateOriginally began life in May 1999 as an XS interface to the system 2058*0Sstevel@tonic-gatemkstemp() function. In March 2000, the OpenBSD mkstemp() code was 2059*0Sstevel@tonic-gatetranslated to Perl for total control of the code's 2060*0Sstevel@tonic-gatesecurity checking, to ensure the presence of the function regardless of 2061*0Sstevel@tonic-gateoperating system and to help with portability. 2062*0Sstevel@tonic-gate 2063*0Sstevel@tonic-gate=head1 SEE ALSO 2064*0Sstevel@tonic-gate 2065*0Sstevel@tonic-gateL<POSIX/tmpnam>, L<POSIX/tmpfile>, L<File::Spec>, L<File::Path> 2066*0Sstevel@tonic-gate 2067*0Sstevel@tonic-gateSee L<IO::File> and L<File::MkTemp> for different implementations of 2068*0Sstevel@tonic-gatetemporary file handling. 2069*0Sstevel@tonic-gate 2070*0Sstevel@tonic-gate=head1 AUTHOR 2071*0Sstevel@tonic-gate 2072*0Sstevel@tonic-gateTim Jenness E<lt>tjenness@cpan.orgE<gt> 2073*0Sstevel@tonic-gate 2074*0Sstevel@tonic-gateCopyright (C) 1999-2003 Tim Jenness and the UK Particle Physics and 2075*0Sstevel@tonic-gateAstronomy Research Council. All Rights Reserved. This program is free 2076*0Sstevel@tonic-gatesoftware; you can redistribute it and/or modify it under the same 2077*0Sstevel@tonic-gateterms as Perl itself. 2078*0Sstevel@tonic-gate 2079*0Sstevel@tonic-gateOriginal Perl implementation loosely based on the OpenBSD C code for 2080*0Sstevel@tonic-gatemkstemp(). Thanks to Tom Christiansen for suggesting that this module 2081*0Sstevel@tonic-gateshould be written and providing ideas for code improvements and 2082*0Sstevel@tonic-gatesecurity enhancements. 2083*0Sstevel@tonic-gate 2084*0Sstevel@tonic-gate=cut 2085*0Sstevel@tonic-gate 2086*0Sstevel@tonic-gate1; 2087