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
commit2d16f8c202dc080056262bade9417f249a4ba827 (patch)
tree24c9f86cc1a4ab4ad163d3ce3177c7d21e0d8a6e /Library
parent6ad82e65da50b3ebd86dffa3e27e31b0f6dac318 (diff)
downloadbrew-2d16f8c202dc080056262bade9417f249a4ba827.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