Make Pathname#atomic_write truly atomic

As we know, files cannot be moved across filesystems atomically. In that
case, FileUtils.mv will make a copy. But if we create the temp file in
the same directory as the target, we can avoid this and use File.rename
directly.

Additionally, the rename should be the absolute last step, so that the
original file is preserved if altering ownership and permissions fails.
This commit is contained in:
Jack Nagel 2014-09-20 17:11:16 -05:00
parent 6ad82e65da
commit 2d16f8c202

View File

@ -105,10 +105,9 @@ class Pathname
# NOTE always overwrites
def atomic_write content
require "tempfile"
tf = Tempfile.new(basename.to_s)
tf = Tempfile.new(basename.to_s, dirname)
tf.binmode
tf.write(content)
tf.close
begin
old_stat = stat
@ -116,16 +115,18 @@ class Pathname
old_stat = default_stat
end
FileUtils.mv tf.path, self
uid = Process.uid
gid = Process.groups.delete(old_stat.gid) { Process.gid }
begin
chown(uid, gid)
chmod(old_stat.mode)
tf.chown(uid, gid)
tf.chmod(old_stat.mode)
rescue Errno::EPERM
end
File.rename(tf.path, self)
ensure
tf.close!
end
def default_stat