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:
parent
6ad82e65da
commit
2d16f8c202
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user