summaryrefslogtreecommitdiff
path: root/lib/tmpdir.rb
diff options
context:
space:
mode:
Diffstat (limited to 'lib/tmpdir.rb')
-rw-r--r--lib/tmpdir.rb110
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