diff options
Diffstat (limited to 'lib/tmpdir.rb')
| -rw-r--r-- | lib/tmpdir.rb | 131 |
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 |
