77 lines
3.3 KiB
Markdown
77 lines
3.3 KiB
Markdown
[Source](http://blog.moertel.com/posts/2005-08-22-how-to-change-symlinks-atomically.html "Permalink to How to change symlinks atomically - Tom Moertel’s Blog")
|
||
|
||
# How to change symlinks atomically - Tom Moertel’s Blog
|
||
|
||
[Tom Moertel's Blog][1]
|
||
|
||
[Home][1] [About][2] [Archive][3]
|
||
|
||
# How to change symlinks atomically
|
||
|
||
By [Tom Moertel][4]
|
||
|
||
Posted on August 22, 2005
|
||
|
||
Tags: [ruby][5], [symlink][6], [safe][7]
|
||
|
||
Many people don't realize that changing the target of a symbolic link (symlink) is not an atomic operation. "Changing" a symlink really means deleting it and creating a new link with the same file name. For example, if I have a symlink _current_ that points to a directory _old_, and I want to change it to point to a directory _new_, I might use the following command:
|
||
|
||
|
||
$ ln -snf new current
|
||
|
||
Strace shows what really happens when I run the command:
|
||
|
||
|
||
$ strace ln -snf new current 2>&1 | grep link
|
||
unlink("current") = 0
|
||
symlink("new", "current") = 0
|
||
|
||
First, the existing symlink is deleted via the _unlink_ system call. Then a new, identically named symlink is created via the _symlink_ system call. It's a two-step process, and in between the steps, **there is no symlink.**
|
||
|
||
This can be a problem if you expect the symlink to be there always, such as when using the link to point to the active version of a live web site. If you change the symlink while deploying a new version of your site, for example, the web server might try to dereference the link during the small window of time when it doesn't exist. Oops.
|
||
|
||
The solution to this problem is to effect the change by creating a new symlink and then renaming it over the old symlink. On Unix-like systems, renaming is an atomic operation, and thus the symlink "change" will be atomic too. By hand, the process looks like this:
|
||
|
||
|
||
$ ln -s new current_tmp && mv -Tf current_tmp current
|
||
|
||
In Ruby, I make atomic symlinking available everywhere by extending the Pathname class with a new method _atomic_symlink_:
|
||
|
||
|
||
require 'pathname'
|
||
|
||
class Pathname
|
||
def atomic_symlink(old)
|
||
suffix = [Array.new(6){rand(256).chr}.join].pack("m").strip.tr('/','_');
|
||
tmplink = Pathname.new(self.to_s + "_" + suffix)
|
||
tmplink.make_symlink(old)
|
||
begin
|
||
tmplink.rename(self)
|
||
rescue
|
||
# if rename fails, we must remove the temporary link manually
|
||
File.unlink(tmplink.to_s)
|
||
raise
|
||
end
|
||
end
|
||
end
|
||
|
||
This code is nothing more than a robustified version of the by-hand method. It picks better names for temporary links, and it cleans up after itself, should something go wrong, but otherwise it does the same thing.
|
||
|
||
Given how easy it is to change symlinks atomically, why do it any other way? Life is hard enough without having to worry about another race condition.
|
||
|
||
Please enable JavaScript to view the [comments powered by Disqus.][8] [comments powered by Disqus][9]
|
||
|
||
Site proudly generated by [Hakyll][10]
|
||
|
||
[1]: http://blog.moertel.com/
|
||
[2]: http://blog.moertel.com/about.html
|
||
[3]: http://blog.moertel.com/archive.html
|
||
[4]: https://plus.google.com/103957126301745359889?rel=author
|
||
[5]: http://blog.moertel.com/tags/ruby.html
|
||
[6]: http://blog.moertel.com/tags/symlink.html
|
||
[7]: http://blog.moertel.com/tags/safe.html
|
||
[8]: http://disqus.com/?ref_noscript
|
||
[9]: http://disqus.com
|
||
[10]: http://jaspervdj.be/hakyll
|
||
|