Secure temporary file: Difference between revisions

From Rosetta Code
Content added Content deleted
No edit summary
Line 44: Line 44:
In both cases, the temporary file will be deleted automatically when the file is closed. The invisible file will not be accessible on UNIX-like systems. You can use os.link to preserve the visible temporary file contents.
In both cases, the temporary file will be deleted automatically when the file is closed. The invisible file will not be accessible on UNIX-like systems. You can use os.link to preserve the visible temporary file contents.



<pre>
>>> import tempfile
>>> import tempfile
>>> invisible = tempfile.TemporaryFile()
>>> invisible = tempfile.TemporaryFile()
>>> invisible.name
>>> invisible.name
'<fdopen>'
'<fdopen>'
>>> visible = tempfile.NamedTemporaryFile()
>>> visible = tempfile.NamedTemporaryFile()
>>> visible.name
>>> visible.name
'/tmp/tmpZNfc_s'
'/tmp/tmpZNfc_s'
>>> visible.close()
>>> visible.close()
>>> invisible.close()
>>> invisible.close()

</pre>


More low level way, if you have special needs. This was the only option before Python 2.3:
More low level way, if you have special needs. This was the only option before Python 2.3:



<pre>
fd, path = tempfile.mkstemp()
fd, path = tempfile.mkstemp()
try:
try:
# use the path or the file descriptor
# use the path or the file descriptor
finally:
finally:
os.close(fd)
os.close(fd)

</pre>

=={{header|UNIX Shell}}==

UNIX shell scripts cannot guarantee secure, race-free, exclusive access to an open file descriptor.
The best approach to working around this limitation is to create a directory (the ''mkdir'' command
is a wrapper around the atomic ''mkdir()'' system call) and then perform all temporary file operations
thereunder.

RESTOREUMASK=$(umask)
TRY=0
while :; do
let TRY+=1 ## No spaces around += operator in let statement!
umask 0077
MYTMP=${TMPDIR:-/tmp}/$(basename $0).$$.$(date +%s).$TRY
trap "rm -fr $MYTMP" EXIT
mkdir "$MYTMP" 2>/dev/null && break
done
umask "$RESTOREUMASK"
cd ""$MYTMP" || {
echo "Temporary directory failure on $MYTMP" >&2
exit 1; }

Note that the shell special variable $$ (the PID of the currently ''exec()''-ed shell) is unique at any given
moment on a UNIX system, and $(date +%s) gives the time represented as a number of seconds since the UNIX epoch
(GNU date or any other with the %s extension). So any name collision here is most likely "enemy action."
This code will loop, picking new names and resetting the ''trap'' (clean-up command) until it succeeds in making
a directory. (Simple changes to the code could limit the number of attempts or implement a timeout).

Revision as of 01:42, 10 April 2008

Task
Secure temporary file
You are encouraged to solve this task according to the task description, using any language you may know.

Create a temporary file, securely and exclusively (opening it such that there are no possible race conditions). It's fine assuming local filesystem semantics (NFS or other networking filesystems can have signficantly more complicated semantics for satisfying the "no race conditions" criteria). The function should automatically resolve name collisions and should only fail in cases where permission is denied, the filesystem is read-only or full, or similar conditions exist (returning an error or raising an exception as appropriate to the language/environment).

Ada

Ada creates a temporary file whenever the create procedure is called with file name. That temporary file is automatically deleted at the end of the program creating the file.

This example creates a temporary file, writes to the file, then reads from the file.

with Ada.Text_Io; use Ada.Text_Io;

procedure Temp_File is
   Temp : File_Type;
   Contents : String(1..80);
   Length : Natural;
begin
   -- Create a temporary file
   Create(File => Temp);
   Put_Line(File => Temp, Item => "Hello World");
   Reset(File => Temp, Mode => In_File);
   Get_Line(File => Temp, Item => Contents, Last => Length);
   Put_Line(Contents(1..Length));  
end Temp_File;


D

Works with: Tango

<d>module tempfile ; import tango.io.TempFile, tango.io.Stdout ;

void main(char[][] args) {

 // create a temporary file that will be deleted automatically when out of scope
 auto tempTransient = new TempFile(TempFile.Transient) ;
 Stdout(tempTransient.path()).newline ;
 // create a temporary file, still persist after the TempFile object has been destroyed
 auto tempPermanent = new TempFile(TempFile.Permanent) ;
 Stdout(tempPermanent.path()).newline ;  
 // both can only be accessed by the current user (the program?). 

}</d>

Python

Works with: Python version 2.3+

In both cases, the temporary file will be deleted automatically when the file is closed. The invisible file will not be accessible on UNIX-like systems. You can use os.link to preserve the visible temporary file contents.


>>> import tempfile
>>> invisible = tempfile.TemporaryFile()
>>> invisible.name
'<fdopen>'
>>> visible = tempfile.NamedTemporaryFile()
>>> visible.name
'/tmp/tmpZNfc_s'
>>> visible.close()
>>> invisible.close()


More low level way, if you have special needs. This was the only option before Python 2.3:


fd, path = tempfile.mkstemp()
try:
    # use the path or the file descriptor
finally:
    os.close(fd)


UNIX Shell

UNIX shell scripts cannot guarantee secure, race-free, exclusive access to an open file descriptor. The best approach to working around this limitation is to create a directory (the mkdir command is a wrapper around the atomic mkdir() system call) and then perform all temporary file operations thereunder.

RESTOREUMASK=$(umask)
TRY=0
while :; do
   let TRY+=1  ## No spaces around += operator in let statement!
   umask 0077
   MYTMP=${TMPDIR:-/tmp}/$(basename $0).$$.$(date +%s).$TRY
   trap "rm -fr $MYTMP" EXIT
   mkdir "$MYTMP" 2>/dev/null && break
   done
umask "$RESTOREUMASK"
cd ""$MYTMP" || {
   echo "Temporary directory failure on $MYTMP" >&2
   exit 1; }

Note that the shell special variable $$ (the PID of the currently exec()-ed shell) is unique at any given moment on a UNIX system, and $(date +%s) gives the time represented as a number of seconds since the UNIX epoch (GNU date or any other with the %s extension). So any name collision here is most likely "enemy action." This code will loop, picking new names and resetting the trap (clean-up command) until it succeeds in making a directory. (Simple changes to the code could limit the number of attempts or implement a timeout).