123456789_123456789_123456789_123456789_123456789_

Class: Gem::AtomicFileWriter

Relationships & Source Files
Inherits: Object
Defined in: lib/rubygems/util/atomic_file_writer.rb

Class Method Summary

Class Method Details

.open(file_name)

Write to a file atomically. Useful for situations where you don’t want other processes or threads to see half-written files.

[ GitHub ]

  
# File 'lib/rubygems/util/atomic_file_writer.rb', line 14

def self.open(file_name)
  require "securerandom" unless defined?(SecureRandom)

  old_stat = begin
               File.stat(file_name)
             rescue SystemCallError
               nil
             end

  # Names can't be longer than 255B
  tmp_suffix = ".tmp.#{SecureRandom.hex}"
  dirname = File.dirname(file_name)
  basename = File.basename(file_name)
  tmp_path = File.join(dirname, ".#{basename.byteslice(0, 254 - tmp_suffix.bytesize)}#{tmp_suffix}")

  flags = File::RDWR | File::CREAT | File::EXCL | File::BINARY
  flags |= File::SHARE_DELETE if defined?(File::SHARE_DELETE)

  File.open(tmp_path, flags) do |temp_file|
    temp_file.binmode
    if old_stat
      # Set correct permissions on new file
      begin
        File.chown(old_stat.uid, old_stat.gid, temp_file.path)
        # This operation will affect filesystem ACL's
        File.chmod(old_stat.mode, temp_file.path)
      rescue Errno::EPERM, Errno::EACCES
        # Changing file ownership failed, moving on.
      end
    end

    return_val = yield temp_file
  rescue StandardError => error
    begin
      temp_file.close
    rescue StandardError
      nil
    end

    begin
      File.unlink(temp_file.path)
    rescue StandardError
      nil
    end

    raise error
  else
    begin
      File.rename(temp_file.path, file_name)
    rescue StandardError
      begin
        File.unlink(temp_file.path)
      rescue StandardError
      end

      raise
    end

    return_val
  end
end