diff options
Diffstat (limited to 'lib/puppet/file_system')
-rw-r--r-- | lib/puppet/file_system/file19.rb | 41 | ||||
-rw-r--r-- | lib/puppet/file_system/file19windows.rb | 1 | ||||
-rw-r--r-- | lib/puppet/file_system/tempfile.rb | 20 | ||||
-rw-r--r-- | lib/puppet/file_system/uniquefile.rb | 190 |
4 files changed, 231 insertions, 21 deletions
diff --git a/lib/puppet/file_system/file19.rb b/lib/puppet/file_system/file19.rb index fce9a6a82..8872ba6fc 100644 --- a/lib/puppet/file_system/file19.rb +++ b/lib/puppet/file_system/file19.rb @@ -2,4 +2,45 @@ class Puppet::FileSystem::File19 < Puppet::FileSystem::FileImpl def binread(path) path.binread end + + # Provide an encoding agnostic version of compare_stream + # + # The FileUtils implementation in Ruby 2.0+ was modified in a manner where + # it cannot properly compare File and StringIO instances. To sidestep that + # issue this method reimplements the faster 2.0 version that will correctly + # compare binary File and StringIO streams. + def compare_stream(path, stream) + open(path, 0, 'rb') do |this| + bsize = stream_blksize(this, stream) + sa = "".force_encoding('ASCII-8BIT') + sb = "".force_encoding('ASCII-8BIT') + begin + this.read(bsize, sa) + stream.read(bsize, sb) + return true if sa.empty? && sb.empty? + end while sa == sb + false + end + end + + private + def stream_blksize(*streams) + streams.each do |s| + next unless s.respond_to?(:stat) + size = blksize(s.stat) + return size if size + end + default_blksize() + end + + def blksize(st) + s = st.blksize + return nil unless s + return nil if s == 0 + s + end + + def default_blksize + 1024 + end end diff --git a/lib/puppet/file_system/file19windows.rb b/lib/puppet/file_system/file19windows.rb index 7ebba2cf4..7ae984f48 100644 --- a/lib/puppet/file_system/file19windows.rb +++ b/lib/puppet/file_system/file19windows.rb @@ -26,7 +26,6 @@ class Puppet::FileSystem::File19Windows < Puppet::FileSystem::File19 dest_exists = exist?(dest) # returns false on dangling symlink dest_stat = Puppet::Util::Windows::File.stat(dest) if dest_exists - dest_symlink = Puppet::Util::Windows::File.symlink?(dest) # silent fail to preserve semantics of original FileUtils return 0 if dest_exists && dest_stat.ftype == 'directory' diff --git a/lib/puppet/file_system/tempfile.rb b/lib/puppet/file_system/tempfile.rb deleted file mode 100644 index 6766a7aec..000000000 --- a/lib/puppet/file_system/tempfile.rb +++ /dev/null @@ -1,20 +0,0 @@ -require 'tempfile' - -class Puppet::FileSystem::Tempfile - - # Variation of Tempfile.open which ensures that the tempfile is closed and - # unlinked before returning - # - # @param identifier [String] additional part of generated pathname - # @yieldparam file [File] the temporary file object - # @return result of the passed block - # @api private - def self.open(identifier) - file = ::Tempfile.new(identifier) - - yield file - - ensure - file.close! - end -end diff --git a/lib/puppet/file_system/uniquefile.rb b/lib/puppet/file_system/uniquefile.rb new file mode 100644 index 000000000..2b1311509 --- /dev/null +++ b/lib/puppet/file_system/uniquefile.rb @@ -0,0 +1,190 @@ +require 'puppet/file_system' +require 'delegate' +require 'tmpdir' + +# A class that provides `Tempfile`-like capabilities, but does not attempt to +# manage the deletion of the file for you. API is identical to the +# normal `Tempfile` class. +# +# @api public +class Puppet::FileSystem::Uniquefile < DelegateClass(File) + # Convenience method which ensures that the file is closed and + # unlinked before returning + # + # @param identifier [String] additional part of generated pathname + # @yieldparam file [File] the temporary file object + # @return result of the passed block + # @api private + def self.open_tmp(identifier) + f = new(identifier) + yield f + ensure + if f + f.close! + end + end + + def initialize(basename, *rest) + create_tmpname(basename, *rest) do |tmpname, n, opts| + mode = File::RDWR|File::CREAT|File::EXCL + perm = 0600 + if opts + mode |= opts.delete(:mode) || 0 + opts[:perm] = perm + perm = nil + else + opts = perm + end + self.class.locking(tmpname) do + @tmpfile = File.open(tmpname, mode, opts) + @tmpname = tmpname + end + @mode = mode & ~(File::CREAT|File::EXCL) + perm or opts.freeze + @opts = opts + end + + super(@tmpfile) + end + + # Opens or reopens the file with mode "r+". + def open + @tmpfile.close if @tmpfile + @tmpfile = File.open(@tmpname, @mode, @opts) + __setobj__(@tmpfile) + end + + def _close + begin + @tmpfile.close if @tmpfile + ensure + @tmpfile = nil + end + end + protected :_close + + def close(unlink_now=false) + if unlink_now + close! + else + _close + end + end + + def close! + _close + unlink + end + + def unlink + return unless @tmpname + begin + File.unlink(@tmpname) + rescue Errno::ENOENT + rescue Errno::EACCES + # may not be able to unlink on Windows; just ignore + return + end + @tmpname = nil + end + alias delete unlink + + # Returns the full path name of the temporary file. + # This will be nil if #unlink has been called. + def path + @tmpname + end + + private + + def make_tmpname(prefix_suffix, n) + case prefix_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 + t = Time.now.strftime("%Y%m%d") + path = "#{prefix}#{t}-#{$$}-#{rand(0x100000000).to_s(36)}" + path << "-#{n}" if n + path << suffix + end + + def create_tmpname(basename, *rest) + if opts = try_convert_to_hash(rest[-1]) + opts = opts.dup if rest.pop.equal?(opts) + max_try = opts.delete(:max_try) + opts = [opts] + else + opts = [] + end + tmpdir, = *rest + if $SAFE > 0 and tmpdir.tainted? + tmpdir = '/tmp' + else + tmpdir ||= tmpdir() + end + n = nil + begin + path = File.expand_path(make_tmpname(basename, n), tmpdir) + yield(path, n, *opts) + 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 + + def try_convert_to_hash(h) + begin + h.to_hash + rescue NoMethodError => e + nil + end + end + + @@systmpdir ||= defined?(Etc.systmpdir) ? Etc.systmpdir : '/tmp' + + def tmpdir + tmp = '.' + if $SAFE > 0 + tmp = @@systmpdir + else + for dir in [ENV['TMPDIR'], ENV['TMP'], ENV['TEMP'], @@systmpdir, '/tmp'] + if dir and stat = File.stat(dir) and stat.directory? and stat.writable? + tmp = dir + break + end rescue nil + end + File.expand_path(tmp) + end + end + + + class << self + # yields with locking for +tmpname+ and returns the result of the + # block. + def locking(tmpname) + lock = tmpname + '.lock' + mkdir(lock) + yield + ensure + rmdir(lock) if lock + end + + def mkdir(*args) + Dir.mkdir(*args) + end + + def rmdir(*args) + Dir.rmdir(*args) + end + end + +end
\ No newline at end of file |