Feature #5976 ยป patch.diff
| lib/pstore.rb | ||
|---|---|---|
|
@filename = file
|
||
|
@abort = false
|
||
|
@ultra_safe = false
|
||
|
@modified = false
|
||
|
@thread_safe = thread_safe
|
||
|
@lock = Mutex.new
|
||
|
end
|
||
| ... | ... | |
|
def in_transaction_wr
|
||
|
in_transaction
|
||
|
raise PStore::Error, "in read-only transaction" if @rdonly
|
||
|
@modified = true
|
||
|
end
|
||
|
private :in_transaction, :in_transaction_wr
|
||
| ... | ... | |
|
@lock.synchronize do
|
||
|
@rdonly = read_only
|
||
|
@abort = false
|
||
|
@modified = false
|
||
|
file = open_and_lock_file(@filename, read_only)
|
||
|
if file
|
||
|
begin
|
||
|
@table, checksum, original_data_size = load_data(file, read_only)
|
||
|
@table = load_data(file)
|
||
|
catch(:pstore_abort_transaction) do
|
||
|
value = yield(self)
|
||
|
end
|
||
|
if !@abort && !read_only
|
||
|
save_data(checksum, original_data_size, file)
|
||
|
save_data(file)
|
||
|
end
|
||
|
ensure
|
||
|
file.close if !file.closed?
|
||
| ... | ... | |
|
end
|
||
|
private
|
||
|
# Constant for relieving Ruby's garbage collector.
|
||
|
# Constants for compatibility.
|
||
|
EMPTY_STRING = ""
|
||
|
EMPTY_MARSHAL_DATA = Marshal.dump({})
|
||
|
EMPTY_MARSHAL_CHECKSUM = Digest::MD5.digest(EMPTY_MARSHAL_DATA)
|
||
| ... | ... | |
|
end
|
||
|
# Load the given PStore file.
|
||
|
# If +read_only+ is true, the unmarshalled Hash will be returned.
|
||
|
# If +read_only+ is false, a 3-tuple will be returned: the unmarshalled
|
||
|
# Hash, an MD5 checksum of the data, and the size of the data.
|
||
|
def load_data(file, read_only)
|
||
|
if read_only
|
||
|
begin
|
||
|
table = load(file)
|
||
|
raise Error, "PStore file seems to be corrupted." unless table.is_a?(Hash)
|
||
|
rescue EOFError
|
||
|
# This seems to be a newly-created file.
|
||
|
table = {}
|
||
|
end
|
||
|
table
|
||
|
else
|
||
|
data = file.read
|
||
|
if data.empty?
|
||
|
# This seems to be a newly-created file.
|
||
|
table = {}
|
||
|
checksum = empty_marshal_checksum
|
||
|
size = empty_marshal_data.bytesize
|
||
|
else
|
||
|
table = load(data)
|
||
|
checksum = Digest::MD5.digest(data)
|
||
|
size = data.bytesize
|
||
|
raise Error, "PStore file seems to be corrupted." unless table.is_a?(Hash)
|
||
|
end
|
||
|
data.replace(EMPTY_STRING)
|
||
|
[table, checksum, size]
|
||
|
# The unmarshalled Hash will be returned.
|
||
|
def load_data(file)
|
||
|
begin
|
||
|
table = load(file)
|
||
|
raise Error, "PStore file seems to be corrupted." unless table.is_a?(Hash)
|
||
|
rescue EOFError
|
||
|
# This seems to be a newly-created file.
|
||
|
table = {}
|
||
|
end
|
||
|
table
|
||
|
end
|
||
|
def on_windows?
|
||
| ... | ... | |
|
is_windows
|
||
|
end
|
||
|
def save_data(original_checksum, original_file_size, file)
|
||
|
def save_data(file)
|
||
|
new_data = dump(@table)
|
||
|
if new_data.bytesize != original_file_size || Digest::MD5.digest(new_data) != original_checksum
|
||
|
if @modified
|
||
|
if @ultra_safe && !on_windows?
|
||
|
# Windows doesn't support atomic file renames.
|
||
|
save_data_with_atomic_file_rename_strategy(new_data, file)
|
||
| ... | ... | |
|
end
|
||
|
end
|
||
|
new_data.replace(EMPTY_STRING)
|
||
|
new_data.clear
|
||
|
end
|
||
|
def save_data_with_atomic_file_rename_strategy(data, file)
|
||