summaryrefslogtreecommitdiff
path: root/lib/tmpdir.rb
diff options
context:
space:
mode:
Diffstat (limited to 'lib/tmpdir.rb')
-rw-r--r--lib/tmpdir.rb49
1 files changed, 36 insertions, 13 deletions
diff --git a/lib/tmpdir.rb b/lib/tmpdir.rb
index db5ed978cd..f78fd721b7 100644
--- a/lib/tmpdir.rb
+++ b/lib/tmpdir.rb
@@ -16,15 +16,14 @@ class Dir
# Class variables are inaccessible from non-main Ractor.
# And instance variables too, in Ruby 3.0.
- # System-wide temporary directory path
- SYSTMPDIR = (defined?(Etc.systmpdir) ? Etc.systmpdir.freeze : '/tmp')
- private_constant :SYSTMPDIR
-
##
# Returns the operating system's temporary file path.
+ #
+ # require 'tmpdir'
+ # Dir.tmpdir # => "/tmp"
def self.tmpdir
- ['TMPDIR', 'TMP', 'TEMP', ['system temporary path', SYSTMPDIR], ['/tmp']*2, ['.']*2].find do |name, dir|
+ Tmpname::TMPDIR_CANDIDATES.find do |name, dir|
unless dir
next if !(dir = ENV[name] rescue next) or dir.empty?
end
@@ -33,7 +32,9 @@ class Dir
case
when !stat.directory?
warn "#{name} is not a directory: #{dir}"
- when !stat.writable?
+ 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}"
@@ -45,6 +46,11 @@ class Dir
# 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.
#
@@ -88,20 +94,21 @@ class Dir
# FileUtils.remove_entry dir
# end
#
- def self.mktmpdir(prefix_suffix=nil, *rest, **options)
+ def self.mktmpdir(prefix_suffix=nil, *rest, **options, &block)
base = nil
path = Tmpname.create(prefix_suffix || "d", *rest, **options) {|path, _, _, d|
base = d
mkdir(path, 0700)
}
- if block_given?
+ if block
begin
yield path.dup
ensure
unless base
- stat = File.stat(File.dirname(path))
+ base = File.dirname(path)
+ stat = File.stat(base)
if stat.world_writable? and !stat.sticky?
- raise ArgumentError, "parent directory is world writable but not sticky"
+ raise ArgumentError, "parent directory is world writable but not sticky: #{base}"
end
end
FileUtils.remove_entry path
@@ -115,6 +122,18 @@ class Dir
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
@@ -138,8 +157,12 @@ class Dir
# Generates and yields random names to create a temporary name
def create(basename, tmpdir=nil, max_try: nil, **opts)
- origdir = tmpdir
- tmpdir ||= tmpdir()
+ if tmpdir
+ origdir = tmpdir = File.path(tmpdir)
+ raise ArgumentError, "empty parent path" if tmpdir.empty?
+ else
+ tmpdir = tmpdir()
+ end
n = nil
prefix, suffix = basename
prefix = (String.try_convert(prefix) or
@@ -158,7 +181,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