aboutsummaryrefslogtreecommitdiffstats
path: root/Library
diff options
context:
space:
mode:
authorJack Nagel2014-09-20 17:11:16 -0500
committerJack Nagel2014-09-20 17:11:16 -0500
commit42c2b474b5739c30406bd79cc91672dd3b7aa4fb (patch)
tree8a44395126fb81372be6cede9d33cd353556ee73 /Library
parent5c751175aeef5458eebc4b1ab2c19473237ffb92 (diff)
downloadhomebrew-42c2b474b5739c30406bd79cc91672dd3b7aa4fb.tar.bz2
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.
Diffstat (limited to 'Library')
-rw-r--r--Library/Homebrew/extend/pathname.rb13
1 files changed, 7 insertions, 6 deletions
diff --git a/Library/Homebrew/extend/pathname.rb b/Library/Homebrew/extend/pathname.rb
index ca1d167c1..e65dc25ef 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