mralbert has asked for the wisdom of the Perl Monks concerning the following question:

I wrote some code in a cgi script to allow a user to upload an image to my website. It is taken from web examples. It works fine on my offline Windows Apache emulator but gives the following error message when installed and executed on my website:

CGI open of tmpfile: Read-only file system

when the command:

my $query = new CGI;

is the next one to be executed. I have been searching everywhere to find out what I have to do to fix this and tried all of the suggestions but nothing seems to work. I've taken care to untaint the variables used in the file name and create folders that the CGI documentation says is where the software tries to place the temporary file while uploading it. Help tickets to my web host don't yield an answer either but they don't say if their drives are simply not set to allow this kind of upload. Any help will be appreciated.

Replies are listed 'Best First'.
Re: On uploading a file
by mr_mischief (Monsignor) on Dec 14, 2010 at 21:14 UTC
    There is, as you said, documentation in CGI about where temporary files go. You can find it with the -private_tempfiles pragma docs. It seems that if your hosting company allows use of CGI.pm they might want to make sure you can write temporary files to one of the stated places in some way. Having a read-only /tmp for example would seem to be pretty useless for its intended purpose.
Re: On uploading a file
by afoken (Chancellor) on Dec 15, 2010 at 11:02 UTC

    CGI has a list of hardcoded paths it tries when it needs a temporary directory. In the current version 3.50, the relevant code is:

    package CGITempFile; sub find_tempdir { $SL = $CGI::SL; $MAC = $CGI::OS eq 'MACINTOSH'; my ($vol) = $MAC ? MacPerl::Volumes() =~ /:(.*)/ : ""; unless (defined $TMPDIRECTORY) { @TEMP=("${SL}usr${SL}tmp","${SL}var${SL}tmp", "C:${SL}temp","${SL}tmp","${SL}temp", "${vol}${SL}Temporary Items", "${SL}WWW_ROOT", "${SL}SYS\$SCRATCH", "C:${SL}system${SL}temp"); if( $CGI::OS eq 'WINDOWS' ){ # PeterH: These evars may not exist if this is invoked within + a service and untainting # is in effect - with 'use warnings' the undefined array entr +ies causes Perl to die unshift(@TEMP,$ENV{TEMP}) if defined $ENV{TEMP}; unshift(@TEMP,$ENV{TMP}) if defined $ENV{TMP}; unshift(@TEMP,$ENV{WINDIR} . $SL . 'TEMP') if defined $ENV{WI +NDIR}; } unshift(@TEMP,$ENV{'TMPDIR'}) if defined $ENV{'TMPDIR'}; # this feature was supposed to provide per-user tmpfiles, but # it is problematic. # unshift(@TEMP,(getpwuid($<))[7].'/tmp') if $CGI::OS eq 'UNIX' +; # Rob: getpwuid() is unfortunately UNIX specific. On brain dead OS +'es this # : can generate a 'getpwuid() not implemented' exception, even + though # : it's never called. Found under DOS/Win with the DJGPP perl + port. # : Refer to getpwuid() only at run-time if we're fortunate and + have UNIX. # unshift(@TEMP,(eval {(getpwuid($>))[7]}).'/tmp') if $CGI::OS eq +'UNIX' and $> != 0; for (@TEMP) { do {$TMPDIRECTORY = $_; last} if -d $_ && -w _; } } $TMPDIRECTORY = $MAC ? "" : "." unless $TMPDIRECTORY; }

    $SL is the OS-specific path separator, typically a slash or backslash.

    This code usually "just works", but of course the -d and -w tests in the last part will probably fail to detect a read-only filesystem.

    Find out where CGI attempts to store its temporary files, then ask your hoster why the filesystem is read-only, and where you should store temporary files instead.

    The following script should give you the location, upload it as CGI, run it, and remove it.

    #!/usr/bin/perl -w # ^-- change as needed use strict; use CGI qw(header); print CGI::header('text/plain'),CGITempFile->find_tempdir();

    Once you know a "better", i.e. writeable location, setting $ENV{'TMPDIR'} before loading CGI should help. Modify your code to start like this:

    #!/usr/bin/perl -T # ^-- change as needed use strict; BEGIN { $ENV{'TMPDIR'}='/home/mralbert/cgitemp'; # ^-- change as needed } use CGI ...

    If your hoster does not give you a useful answer, consider changing the hoster. As a temporary workaround, create a directory in a writeable location, add a .htaccess file as shown below to deny access, chmod it the directory to 1777 (i.e. world writeable plus sticky), and use that directory as shown above.

    order deny,allow deny from all

    Alexander

    --
    Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)
Re: On uploading a file
by mralbert (Initiate) on Dec 16, 2010 at 22:03 UTC
    I tried this out and it works! I can now upload images. Many, many thanks. Mark