From 2d16f8c202dc080056262bade9417f249a4ba827 Mon Sep 17 00:00:00 2001 From: Jack Nagel Date: Sat, 20 Sep 2014 17:11:16 -0500 Subject: [PATCH] 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. --- Library/Homebrew/extend/pathname.rb | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/Library/Homebrew/extend/pathname.rb b/Library/Homebrew/extend/pathname.rb index ca1d167c12..e65dc25ef0 100644 --- a/Library/Homebrew/extend/pathname.rb +++ b/Library/Homebrew/extend/pathname.rb @@ -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