Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

I am trying to (as close as possibly) atomically change a symlink. I've tried:

ln -sf other_dir existing_symlink

That just put the new symlink in the directory that existing_symlink pointed to.

ln -sf other_dir new_symlink
mv -f new_symlink existing_symlink

That did the same thing: it moved the symlink into the directory.

cp -s other_dir existing_symlink

It refuses because it's a directory.

I've read that mv -T is was made for this, but busybox doesn't have the -T flag.

share|improve this question
add comment

4 Answers

up vote 2 down vote accepted

I don't see how you can get atomic operation. The man page for symlink(2) says it gives EEXIST if the target already exists. If the kernel doesn't support atomic operation, your userland limitations are irrelevant.

I also don't see how mv -T helps, even if you have it. Try it on a regular Linux box, one with GNU mv:

$ mkdir a b
$ ln -s a z
$ mv -T b z
mv: cannot overwrite non-directory `z' with directory `b'

I think you're going to have to do this in two steps: remove the old symlink and recreate it.

share|improve this answer
1  
Indeed, there is unfortunately no way you can modify a symbolic atomically. The best you can do is remove the old link and create a new one. GNU coreutils has an option to do this with a single command (ln -snf), but there are still two system calls under the hood. –  Gilles Dec 22 '10 at 19:13
add comment

This can indeed be done atomically with rename(2), by first creating the new symlink under a temporary name and then cleanly overwriting the old symlink in one go. As the man page states:

If newpath refers to a symbolic link the link will be overwritten.

In the shell, you would do this with mv -T as follows:

$ mkdir a b
$ ln -s a z
$ ln -s b z.new
$ mv -T z.new z

You can strace that last command to make sure it is indeed using rename(2) under the hood:

$ strace mv -T z.new z
lstat64("z.new", {st_mode=S_IFLNK|0777, st_size=1, ...}) = 0
lstat64("z", {st_mode=S_IFLNK|0777, st_size=1, ...}) = 0
rename("z.new", "z")                    = 0

Note that in the above, both mv -T and strace are Linux-specific. I don't know of a shell equivalent of mv -T on FreeBSD, for instance.

share|improve this answer
add comment

Picking up where Arto left off here, this is entirely possible, even without mv -T, you just need to create a new symlink with the same name as the target directory and mv it into the parent directory of your target:

mkdir -p tmp/real_dir1 tmp/real_dir2
touch tmp/real_dir1/a tmp/real_dir2/a
# start with ./target_dir pointing to tmp/real_dir1
ln -s tmp/real_dir1 target_dir
# create a symlink named target_dir in tmp, pointing to real_dir2
ln -sf tmp/real_dir2 tmp/target_dir
# atomically mv it into ./ replacing ./target_dir
mv tmp/target_dir ./

Code example taken via (http://axialcorps.wordpress.com/2013/07/03/atomically-replacing-files-and-directories/)

share|improve this answer
add comment

Have you tried ln -snf?

The option -n overwrites the destination rather than writing under it when the destination is a symbolic link to a directory.

Cheers

share|improve this answer
1  
ln -snf is not atomic: it unlinks the destination, then creates the desired symlink. –  Gilles Aug 13 '11 at 12:37
    
Given that the OP was interested in getting "as close as possib[e]" to atomically changing a symlink, this is a perfectly reasonable answer. If there is a better one that can get closer (or be) atomic, that one can be accepted. I don't think there's a need to downvote. –  Wilco Oct 29 '13 at 18:51
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.