class PEROBS::RobustFile

Public Class Methods

write(name, string) click to toggle source

This is a more robust version of File::write. It first writes to a temporary file and them atomically renames the new file to the potentially existing old file. This should significantly increase our chances that we never end up with a corrupted file due to problems during the file write.

# File lib/perobs/RobustFile.rb, line 39
def RobustFile::write(name, string)
  tmp_name = name + '.atomic'

  # Ensure that no file with the temporary name exists.
  if File.exist?(tmp_name)
    PEROBS.log.warn "Found old temporary file #{tmp_name}"
    unless File.exist?(name)
      # If we have a file with the temporary name but no file with the
      # actual name, rename the temporary file to the actual name. We have
      # no idea if that temporary file is good enough, but it's better to
      # have a file with the actual name as backup in case this file write
      # fails.
      begin
        File.rename(tmp_name, name)
      rescue IOError => e
        raise IOError "Could not name old temporary file to #{name}: " +
          e.message
      end
      Log.warn "Found old temporary file but no corresponding original file"
    else
      # Delete the old temporary file.
      begin
        File.delete(tmp_name)
      rescue IOError => e
        PEROBS.log.warn "Could not delete old temporary file " +
          "#{tmp_name}: #{e.message}"
      end
    end
  end

  # Write the temporary file.
  begin
    File.write(tmp_name, string)
  rescue IOError => e
    raise IOError "Could not write file #{tmp_name}: #{e.message}"
  end

  # If the temporary file was written successfully we can rename it to the
  # actual file name and atomically replace the old file if it exists.
  begin
    File.rename(tmp_name, name)
  rescue IOError => e
     raise IOError "Could not rename #{tmp_name} to #{name}: #{e.message}"
  end
end