diff options
Diffstat (limited to 'lib/tmpdir.rb')
| -rw-r--r-- | lib/tmpdir.rb | 110 |
1 files changed, 33 insertions, 77 deletions
diff --git a/lib/tmpdir.rb b/lib/tmpdir.rb index f78fd721b7..d14c446727 100644 --- a/lib/tmpdir.rb +++ b/lib/tmpdir.rb @@ -13,44 +13,32 @@ end class Dir - # Class variables are inaccessible from non-main Ractor. - # And instance variables too, in Ruby 3.0. + @@systmpdir ||= defined?(Etc.systmpdir) ? Etc.systmpdir : '/tmp' ## # Returns the operating system's temporary file path. - # - # require 'tmpdir' - # Dir.tmpdir # => "/tmp" def self.tmpdir - Tmpname::TMPDIR_CANDIDATES.find do |name, dir| - unless dir - next if !(dir = ENV[name] rescue next) or dir.empty? - end - dir = File.expand_path(dir) - stat = File.stat(dir) rescue next - case - when !stat.directory? - warn "#{name} is not a directory: #{dir}" - when !File.writable?(dir) - # We call File.writable?, not stat.writable?, because you can't tell if a dir is actually - # writable just from stat; OS mechanisms other than user/group/world bits can affect this. - warn "#{name} is not writable: #{dir}" - when stat.world_writable? && !stat.sticky? - warn "#{name} is world-writable: #{dir}" - else - break dir + if $SAFE > 0 + @@systmpdir.dup + else + 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 - end or raise ArgumentError, "could not find a temporary directory" + raise ArgumentError, "could not find a temporary directory" unless tmp + tmp + end end # Dir.mktmpdir creates a temporary directory. # - # require 'tmpdir' - # Dir.mktmpdir {|dir| - # # use the directory - # } - # # The directory is created with 0700 permission. # Application should not change the permission to make the temporary directory accessible from other users. # @@ -78,7 +66,7 @@ class Dir # # Dir.mktmpdir {|dir| # # use the directory... - # open("#{dir}/foo", "w") { something using the file } + # open("#{dir}/foo", "w") { ... } # } # # If a block is not given, @@ -88,27 +76,26 @@ class Dir # dir = Dir.mktmpdir # begin # # use the directory... - # open("#{dir}/foo", "w") { something using the file } + # open("#{dir}/foo", "w") { ... } # ensure # # remove the directory. # FileUtils.remove_entry dir # end # - def self.mktmpdir(prefix_suffix=nil, *rest, **options, &block) + def self.mktmpdir(prefix_suffix=nil, *rest) base = nil - path = Tmpname.create(prefix_suffix || "d", *rest, **options) {|path, _, _, d| + path = Tmpname.create(prefix_suffix || "d", *rest) {|_path, _, _, d| base = d - mkdir(path, 0700) + mkdir(_path, 0700) } - if block + if block_given? begin - yield path.dup + yield path ensure unless base - base = File.dirname(path) - stat = File.stat(base) + stat = File.stat(File.dirname(path)) if stat.world_writable? and !stat.sticky? - raise ArgumentError, "parent directory is world writable but not sticky: #{base}" + raise ArgumentError, "parent directory is world writable but not sticky" end end FileUtils.remove_entry path @@ -118,62 +105,31 @@ class Dir end end - # Temporary name generator module Tmpname # :nodoc: module_function - # System-wide temporary directory path - systmpdir = (defined?(Etc.systmpdir) ? Etc.systmpdir.freeze : '/tmp') - - # Temporary directory candidates consisting of environment variable - # names or description and path pairs. - TMPDIR_CANDIDATES = [ - 'TMPDIR', 'TMP', 'TEMP', - ['system temporary path', systmpdir], - %w[/tmp /tmp], - %w[. .], - ].each(&:freeze).freeze - def tmpdir Dir.tmpdir end - # Unusable characters as path name - UNUSABLE_CHARS = "^,-.0-9A-Z_a-z~" - - # Dedicated random number generator - RANDOM = Object.new - class << RANDOM # :nodoc: - # Maximum random number - MAX = 36**6 # < 0x100000000 - - # Returns new random string upto 6 bytes - def next - (::Random.urandom(4).unpack1("L")%MAX).to_s(36) - end - end - RANDOM.freeze - private_constant :RANDOM - - # Generates and yields random names to create a temporary name def create(basename, tmpdir=nil, max_try: nil, **opts) - if tmpdir - origdir = tmpdir = File.path(tmpdir) - raise ArgumentError, "empty parent path" if tmpdir.empty? + if $SAFE > 0 and tmpdir.tainted? + tmpdir = '/tmp' else - tmpdir = tmpdir() + 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(UNUSABLE_CHARS) + prefix = prefix.delete("#{File::SEPARATOR}#{File::ALT_SEPARATOR}") suffix &&= (String.try_convert(suffix) or raise ArgumentError, "unexpected suffix: #{suffix.inspect}") - suffix &&= suffix.delete(UNUSABLE_CHARS) + suffix &&= suffix.delete("#{File::SEPARATOR}#{File::ALT_SEPARATOR}") begin t = Time.now.strftime("%Y%m%d") - path = "#{prefix}#{t}-#{$$}-#{RANDOM.next}"\ + path = "#{prefix}#{t}-#{$$}-#{rand(0x100000000).to_s(36)}"\ "#{n ? %[-#{n}] : ''}#{suffix||''}" path = File.join(tmpdir, path) yield(path, n, opts, origdir) @@ -181,7 +137,7 @@ class Dir n ||= 0 n += 1 retry if !max_try or n < max_try - raise "cannot generate temporary name using '#{basename}' under '#{tmpdir}'" + raise "cannot generate temporary name using `#{basename}' under `#{tmpdir}'" end path end |
