2015-11-10 14:48:14 +03:00
|
|
|
# frozen_string_literal: true
|
2003-07-21 19:34:18 +04:00
|
|
|
#
|
|
|
|
# tmpdir - retrieve temporary directory path
|
|
|
|
#
|
|
|
|
# $Id$
|
|
|
|
#
|
|
|
|
|
2007-08-15 19:38:18 +04:00
|
|
|
require 'fileutils'
|
2014-04-29 09:19:38 +04:00
|
|
|
begin
|
|
|
|
require 'etc.so'
|
|
|
|
rescue LoadError # rescue LoadError for miniruby
|
|
|
|
end
|
2007-08-15 19:38:18 +04:00
|
|
|
|
2003-07-21 19:34:18 +04:00
|
|
|
class Dir
|
2003-07-23 20:37:35 +04:00
|
|
|
|
2010-05-08 07:25:17 +04:00
|
|
|
@@systmpdir ||= defined?(Etc.systmpdir) ? Etc.systmpdir : '/tmp'
|
2003-07-23 20:37:35 +04:00
|
|
|
|
2005-12-15 06:41:13 +03:00
|
|
|
##
|
|
|
|
# Returns the operating system's temporary file path.
|
|
|
|
|
2014-09-20 21:35:06 +04:00
|
|
|
def self.tmpdir
|
2022-10-25 10:39:12 +03:00
|
|
|
['TMPDIR', 'TMP', 'TEMP', ['system temporary path', @@systmpdir], ['/tmp']*2, ['.']*2].find do |name, dir|
|
|
|
|
unless dir
|
|
|
|
next if !(dir = ENV[name]) or dir.empty?
|
|
|
|
end
|
2019-09-21 05:06:22 +03:00
|
|
|
dir = File.expand_path(dir)
|
2020-07-16 11:45:08 +03:00
|
|
|
stat = File.stat(dir) rescue next
|
|
|
|
case
|
|
|
|
when !stat.directory?
|
|
|
|
warn "#{name} is not a directory: #{dir}"
|
|
|
|
when !stat.writable?
|
|
|
|
warn "#{name} is not writable: #{dir}"
|
|
|
|
when stat.world_writable? && !stat.sticky?
|
|
|
|
warn "#{name} is world-writable: #{dir}"
|
|
|
|
else
|
2022-09-20 10:53:39 +03:00
|
|
|
break dir
|
2020-07-16 11:45:08 +03:00
|
|
|
end
|
2022-10-25 10:07:09 +03:00
|
|
|
end or raise ArgumentError, "could not find a temporary directory"
|
2003-07-21 19:34:18 +04:00
|
|
|
end
|
2007-08-15 19:38:18 +04:00
|
|
|
|
|
|
|
# Dir.mktmpdir creates a temporary directory.
|
|
|
|
#
|
|
|
|
# The directory is created with 0700 permission.
|
2013-05-19 07:10:21 +04:00
|
|
|
# Application should not change the permission to make the temporary directory accessible from other users.
|
2007-08-21 16:15:34 +04:00
|
|
|
#
|
|
|
|
# The prefix and suffix of the name of the directory is specified by
|
|
|
|
# the optional first argument, <i>prefix_suffix</i>.
|
|
|
|
# - If it is not specified or nil, "d" is used as the prefix and no suffix is used.
|
|
|
|
# - If it is a string, it is used as the prefix and no suffix is used.
|
|
|
|
# - If it is an array, first element is used as the prefix and second element is used as a suffix.
|
|
|
|
#
|
|
|
|
# Dir.mktmpdir {|dir| dir is ".../d..." }
|
|
|
|
# Dir.mktmpdir("foo") {|dir| dir is ".../foo..." }
|
2007-10-24 10:03:48 +04:00
|
|
|
# Dir.mktmpdir(["foo", "bar"]) {|dir| dir is ".../foo...bar" }
|
2007-08-15 19:38:18 +04:00
|
|
|
#
|
|
|
|
# The directory is created under Dir.tmpdir or
|
2007-08-18 07:24:41 +04:00
|
|
|
# the optional second argument <i>tmpdir</i> if non-nil value is given.
|
2007-08-15 19:38:18 +04:00
|
|
|
#
|
2007-08-21 16:15:34 +04:00
|
|
|
# Dir.mktmpdir {|dir| dir is "#{Dir.tmpdir}/d..." }
|
|
|
|
# Dir.mktmpdir(nil, "/var/tmp") {|dir| dir is "/var/tmp/d..." }
|
|
|
|
#
|
2007-08-15 19:38:18 +04:00
|
|
|
# If a block is given,
|
|
|
|
# it is yielded with the path of the directory.
|
2007-08-16 08:30:18 +04:00
|
|
|
# The directory and its contents are removed
|
2012-03-12 02:37:04 +04:00
|
|
|
# using FileUtils.remove_entry before Dir.mktmpdir returns.
|
2007-08-15 19:38:18 +04:00
|
|
|
# The value of the block is returned.
|
|
|
|
#
|
|
|
|
# Dir.mktmpdir {|dir|
|
|
|
|
# # use the directory...
|
2021-11-05 03:45:41 +03:00
|
|
|
# open("#{dir}/foo", "w") { something using the file }
|
2007-08-15 19:38:18 +04:00
|
|
|
# }
|
|
|
|
#
|
|
|
|
# If a block is not given,
|
|
|
|
# The path of the directory is returned.
|
|
|
|
# In this case, Dir.mktmpdir doesn't remove the directory.
|
|
|
|
#
|
|
|
|
# dir = Dir.mktmpdir
|
|
|
|
# begin
|
|
|
|
# # use the directory...
|
2021-11-05 03:45:41 +03:00
|
|
|
# open("#{dir}/foo", "w") { something using the file }
|
2007-08-15 19:38:18 +04:00
|
|
|
# ensure
|
|
|
|
# # remove the directory.
|
2012-03-12 02:37:04 +04:00
|
|
|
# FileUtils.remove_entry dir
|
2007-08-15 19:38:18 +04:00
|
|
|
# end
|
|
|
|
#
|
2019-04-08 02:44:49 +03:00
|
|
|
def self.mktmpdir(prefix_suffix=nil, *rest, **options)
|
2019-01-23 09:06:47 +03:00
|
|
|
base = nil
|
2019-04-08 02:44:49 +03:00
|
|
|
path = Tmpname.create(prefix_suffix || "d", *rest, **options) {|path, _, _, d|
|
2019-01-23 09:06:47 +03:00
|
|
|
base = d
|
|
|
|
mkdir(path, 0700)
|
|
|
|
}
|
2009-09-08 17:38:01 +04:00
|
|
|
if block_given?
|
|
|
|
begin
|
2020-05-29 05:49:10 +03:00
|
|
|
yield path.dup
|
2009-09-08 17:38:01 +04:00
|
|
|
ensure
|
2019-01-23 09:06:47 +03:00
|
|
|
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
|
2012-03-12 02:19:06 +04:00
|
|
|
end
|
|
|
|
FileUtils.remove_entry path
|
2009-09-08 17:38:01 +04:00
|
|
|
end
|
2007-08-21 16:15:34 +04:00
|
|
|
else
|
2009-09-08 17:38:01 +04:00
|
|
|
path
|
2007-08-21 16:15:34 +04:00
|
|
|
end
|
2009-09-08 17:38:01 +04:00
|
|
|
end
|
|
|
|
|
2022-10-25 07:39:56 +03:00
|
|
|
# Temporary name generator
|
2009-09-11 13:46:53 +04:00
|
|
|
module Tmpname # :nodoc:
|
2009-09-08 17:38:01 +04:00
|
|
|
module_function
|
|
|
|
|
|
|
|
def tmpdir
|
|
|
|
Dir.tmpdir
|
|
|
|
end
|
|
|
|
|
2022-10-25 07:39:56 +03:00
|
|
|
# Unusable characters as path name
|
2021-03-31 19:17:45 +03:00
|
|
|
UNUSABLE_CHARS = "^,-.0-9A-Z_a-z~"
|
2019-06-21 17:03:36 +03:00
|
|
|
|
2022-10-25 07:39:56 +03:00
|
|
|
# Dedicated random number generator
|
|
|
|
RANDOM = Random.new
|
2022-10-25 08:02:44 +03:00
|
|
|
class << RANDOM # :nodoc:
|
2022-10-25 07:39:56 +03:00
|
|
|
# Maximum random number
|
2020-01-29 04:12:35 +03:00
|
|
|
MAX = 36**6 # < 0x100000000
|
2022-10-25 07:39:56 +03:00
|
|
|
|
|
|
|
# Returns new random string upto 6 bytes
|
2020-01-29 04:12:35 +03:00
|
|
|
def next
|
|
|
|
rand(MAX).to_s(36)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
private_constant :RANDOM
|
|
|
|
|
2022-10-25 07:39:56 +03:00
|
|
|
# Generates and yields random names to create a temporary name
|
2014-09-20 21:35:06 +04:00
|
|
|
def create(basename, tmpdir=nil, max_try: nil, **opts)
|
2019-09-21 05:06:22 +03:00
|
|
|
origdir = tmpdir
|
|
|
|
tmpdir ||= tmpdir()
|
2009-09-08 17:38:01 +04:00
|
|
|
n = nil
|
2017-11-15 11:02:09 +03:00
|
|
|
prefix, suffix = basename
|
|
|
|
prefix = (String.try_convert(prefix) or
|
|
|
|
raise ArgumentError, "unexpected prefix: #{prefix.inspect}")
|
2019-06-21 17:03:36 +03:00
|
|
|
prefix = prefix.delete(UNUSABLE_CHARS)
|
2017-11-15 11:02:09 +03:00
|
|
|
suffix &&= (String.try_convert(suffix) or
|
|
|
|
raise ArgumentError, "unexpected suffix: #{suffix.inspect}")
|
2019-06-21 17:03:36 +03:00
|
|
|
suffix &&= suffix.delete(UNUSABLE_CHARS)
|
2007-08-15 19:38:18 +04:00
|
|
|
begin
|
2017-11-15 11:02:09 +03:00
|
|
|
t = Time.now.strftime("%Y%m%d")
|
2020-01-29 04:12:35 +03:00
|
|
|
path = "#{prefix}#{t}-#{$$}-#{RANDOM.next}"\
|
2017-11-15 11:02:09 +03:00
|
|
|
"#{n ? %[-#{n}] : ''}#{suffix||''}"
|
|
|
|
path = File.join(tmpdir, path)
|
2019-01-23 09:06:47 +03:00
|
|
|
yield(path, n, opts, origdir)
|
2009-09-08 17:38:01 +04:00
|
|
|
rescue Errno::EEXIST
|
|
|
|
n ||= 0
|
|
|
|
n += 1
|
|
|
|
retry if !max_try or n < max_try
|
|
|
|
raise "cannot generate temporary name using `#{basename}' under `#{tmpdir}'"
|
2007-08-15 19:38:18 +04:00
|
|
|
end
|
|
|
|
path
|
|
|
|
end
|
|
|
|
end
|
2003-07-21 19:34:18 +04:00
|
|
|
end
|