summaryrefslogtreecommitdiff
path: root/lib/tmpdir.rb
diff options
context:
space:
mode:
Diffstat (limited to 'lib/tmpdir.rb')
-rw-r--r--lib/tmpdir.rb131
1 files changed, 73 insertions, 58 deletions
diff --git a/lib/tmpdir.rb b/lib/tmpdir.rb
index 95ef08cfd6..d14c446727 100644
--- a/lib/tmpdir.rb
+++ b/lib/tmpdir.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: true
#
# tmpdir - retrieve temporary directory path
#
@@ -5,49 +6,41 @@
#
require 'fileutils'
+begin
+ require 'etc.so'
+rescue LoadError # rescue LoadError for miniruby
+end
class Dir
- @@systmpdir = '/tmp'
-
- begin
- require 'Win32API'
- max_pathlen = 260
- windir = ' '*(max_pathlen+1)
- begin
- getdir = Win32API.new('kernel32', 'GetSystemWindowsDirectory', 'PL', 'L')
- rescue RuntimeError
- getdir = Win32API.new('kernel32', 'GetWindowsDirectory', 'PL', 'L')
- end
- len = getdir.call(windir, windir.size)
- windir = File.expand_path(windir[0, len])
- temp = File.join(windir, 'temp')
- @@systmpdir = temp if File.directory?(temp) and File.writable?(temp)
- rescue LoadError
- end
+ @@systmpdir ||= defined?(Etc.systmpdir) ? Etc.systmpdir : '/tmp'
##
# Returns the operating system's temporary file path.
- def Dir::tmpdir
- tmp = '.'
+ def self.tmpdir
if $SAFE > 0
- tmp = @@systmpdir
+ @@systmpdir.dup
else
- for dir in [ENV['TMPDIR'], ENV['TMP'], ENV['TEMP'],
- ENV['USERPROFILE'], @@systmpdir, '/tmp']
- if dir and File.directory?(dir) and File.writable?(dir)
- tmp = dir
- break
- end
+ tmp = nil
+ [ENV['TMPDIR'], ENV['TMP'], ENV['TEMP'], @@systmpdir, '/tmp', '.'].each do |dir|
+ next if !dir
+ dir = File.expand_path(dir)
+ if stat = File.stat(dir) and stat.directory? and stat.writable? and
+ (!stat.world_writable? or stat.sticky?)
+ tmp = dir
+ break
+ end rescue nil
end
+ raise ArgumentError, "could not find a temporary directory" unless tmp
+ tmp
end
- File.expand_path(tmp)
end
# Dir.mktmpdir creates a temporary directory.
#
# The directory is created with 0700 permission.
+ # Application should not change the permission to make the temporary directory accessible from other users.
#
# The prefix and suffix of the name of the directory is specified by
# the optional first argument, <i>prefix_suffix</i>.
@@ -68,7 +61,7 @@ class Dir
# If a block is given,
# it is yielded with the path of the directory.
# The directory and its contents are removed
- # using FileUtils.remove_entry_secure before Dir.mktmpdir returns.
+ # using FileUtils.remove_entry before Dir.mktmpdir returns.
# The value of the block is returned.
#
# Dir.mktmpdir {|dir|
@@ -86,45 +79,67 @@ class Dir
# open("#{dir}/foo", "w") { ... }
# ensure
# # remove the directory.
- # FileUtils.remove_entry_secure dir
+ # FileUtils.remove_entry dir
# end
#
- def Dir.mktmpdir(prefix_suffix=nil, tmpdir=nil)
- case prefix_suffix
- when nil
- prefix = "d"
- suffix = ""
- when String
- prefix = prefix_suffix
- suffix = ""
- when Array
- prefix = prefix_suffix[0]
- suffix = prefix_suffix[1]
- else
- raise ArgumentError, "unexpected prefix_suffix: #{prefix_suffix.inspect}"
- end
- tmpdir ||= Dir.tmpdir
- t = Time.now.strftime("%Y%m%d")
- n = nil
- begin
- path = "#{tmpdir}/#{prefix}#{t}-#{$$}-#{rand(0x100000000).to_s(36)}"
- path << "-#{n}" if n
- path << suffix
- Dir.mkdir(path, 0700)
- rescue Errno::EEXIST
- n ||= 0
- n += 1
- retry
- end
-
+ def self.mktmpdir(prefix_suffix=nil, *rest)
+ base = nil
+ path = Tmpname.create(prefix_suffix || "d", *rest) {|_path, _, _, d|
+ base = d
+ mkdir(_path, 0700)
+ }
if block_given?
begin
yield path
ensure
- FileUtils.remove_entry_secure path
+ unless base
+ stat = File.stat(File.dirname(path))
+ if stat.world_writable? and !stat.sticky?
+ raise ArgumentError, "parent directory is world writable but not sticky"
+ end
+ end
+ FileUtils.remove_entry path
end
else
path
end
end
+
+ module Tmpname # :nodoc:
+ module_function
+
+ def tmpdir
+ Dir.tmpdir
+ end
+
+ def create(basename, tmpdir=nil, max_try: nil, **opts)
+ if $SAFE > 0 and tmpdir.tainted?
+ tmpdir = '/tmp'
+ else
+ origdir = tmpdir
+ tmpdir ||= tmpdir()
+ end
+ n = nil
+ prefix, suffix = basename
+ prefix = (String.try_convert(prefix) or
+ raise ArgumentError, "unexpected prefix: #{prefix.inspect}")
+ prefix = prefix.delete("#{File::SEPARATOR}#{File::ALT_SEPARATOR}")
+ suffix &&= (String.try_convert(suffix) or
+ raise ArgumentError, "unexpected suffix: #{suffix.inspect}")
+ suffix &&= suffix.delete("#{File::SEPARATOR}#{File::ALT_SEPARATOR}")
+ begin
+ t = Time.now.strftime("%Y%m%d")
+ path = "#{prefix}#{t}-#{$$}-#{rand(0x100000000).to_s(36)}"\
+ "#{n ? %[-#{n}] : ''}#{suffix||''}"
+ path = File.join(tmpdir, path)
+ yield(path, n, opts, origdir)
+ rescue Errno::EEXIST
+ n ||= 0
+ n += 1
+ retry if !max_try or n < max_try
+ raise "cannot generate temporary name using `#{basename}' under `#{tmpdir}'"
+ end
+ path
+ end
+ end
end