2 # tempfile - manipulates temporary files
11 # A class for managing temporary files. This library is written to be
13 class Tempfile < DelegateClass(File)
16 class FinalizerData < Struct.new :tmpname, :tmpfile, :mutex # :nodoc:
19 # Creates a temporary file of mode 0600 in the temporary directory
20 # whose name is basename.pid.n and opens with mode "w+". A Tempfile
21 # object works just like a File object.
23 # If tmpdir is omitted, the temporary directory is determined by
24 # Dir::tmpdir provided by 'tmpdir.rb'.
25 # When $SAFE > 0 and the given tmpdir is tainted, it uses
26 # /tmp. (Note that ENV values are tainted by default)
27 def initialize(basename, tmpdir=Dir::tmpdir)
28 if $SAFE > 0 and tmpdir.tainted?
34 @tmpname = File.join(tmpdir, make_tmpname_secure(basename))
35 @tmpfile = File.open(@tmpname, File::RDWR|File::CREAT|File::EXCL, 0600)
38 retry if failure < MAX_TRY
39 raise "cannot generate tempfile `%s': #{e.message}" % @tmpname
44 @data = FinalizerData[@tmpname, @tmpfile, @mutex]
45 @clean_proc = Tempfile.callback(@data)
46 ObjectSpace.define_finalizer(self, @clean_proc)
50 # Now we have all the File/IO methods defined, you must not
51 # carelessly put bare puts(), etc. after this.
55 @@sequence_mutex = Mutex.new
56 def make_tmpname_secure(basename) #:nodoc:
58 File.open("/dev/urandom", "rb") do |random|
59 basename = "#{random.read(16).unpack('H*')}_#{basename}"
63 sequence_number = @@sequence_mutex.synchronize { @@sequence_number += 1 }
64 make_tmpname(basename, sequence_number)
66 private :make_tmpname_secure
68 def make_tmpname(basename, n)
69 "#{basename}.#{$$}.#{n}"
73 # Opens or reopens the file with mode "r+".
76 @tmpfile.close if @tmpfile
77 @tmpfile = File.open(@tmpname, 'r+')
78 @data.tmpfile = @tmpfile
84 @tmpfile.close if @tmpfile
85 @data.tmpfile = @tmpfile = nil
89 # Closes the file. If the optional flag is true, unlinks the file
92 # If you don't explicitly unlink the temporary file, the removal
93 # will be delayed until the object is finalized.
94 def close(unlink_now=false)
98 @mutex.synchronize { _close }
102 # Closes and unlinks the file.
104 @mutex.synchronize do
106 @data.mutex = nil # @clean_proc does not need to acquire the lock here
108 ObjectSpace.undefine_finalizer(self)
112 # Unlinks the file. On UNIX-like systems, it is often a good idea
113 # to unlink a temporary file immediately after creating and opening
114 # it, because it leaves other programs zero chance to access the
117 @mutex.synchronize do
120 File.unlink(@tmpname)
123 @data = @tmpname = nil
124 ObjectSpace.undefine_finalizer(self)
126 # may not be able to unlink on Windows; just ignore
132 # Returns the full path name of the temporary file.
134 @mutex.synchronize { @tmpname.dup }
137 # Returns the size of the temporary file. As a side effect, the IO
138 # buffer is flushed before determining the size.
140 @mutex.synchronize do
152 def callback(data) # :nodoc:
156 data.mutex.lock if data.mutex
158 print "removing ", data.tmpname, "..." if $DEBUG
160 data.tmpfile.close if data.tmpfile
162 # keep this order for thread safeness
164 File.unlink(data.tmpname)
165 rescue Errno::ENOENT, Errno::ENOTDIR, Errno::EISDIR
168 print "done\n" if $DEBUG
170 data.mutex.unlock if data.mutex
176 # If no block is given, this is a synonym for new().
178 # If a block is given, it will be passed tempfile as an argument,
179 # and the tempfile will automatically be closed when the block
180 # terminates. In this case, open() returns nil.
182 tempfile = new(*args)
201 f = Tempfile.new("foo")
205 p f.gets # => "foo\n"