tags:

views:

441

answers:

10

I have two directories in the same parent directory. Call the parent directory base and the children directories alpha and bravo. I want to replace alpha with bravo. The simplest method is:

rm -rf alpha
mv bravo alpha

The mv command is atomic, but the rm -rf is not. Is there a simple way in bash to atomically replace alpha with bravo? If not, is there a complicated way?

ADDENDUM:

By the by, it's not an insurmountable problem if the directory doesn't exist for a short period. There's only one place that tries to access alpha, and it checks if alpha exists before doing anything critical. If not, it gives an error message. But it would be nice if there was a way to do this. :) Maybe there's some way to modify the inodes directly, or something...

+1  A: 

I don't believe there's any atomic way to do this. Your best bet is to do something like this:

mv alpha delme
mv bravo alpha
rm -rf delme
Chris Charabaruk
+5  A: 

If you mean atomic across both operations, I don't believe so. The closest would be:

mv alpha delta
mv bravo alpha
rm -rf delta

but that would still have a small window where alpha didn't exist.

To minimize the likelihood of anything trying to use alpha while it's not there you could (if you have the authority):

nice --20 ( mv alpha delta ; mv bravo alpha )
rm -rf delta

which will crank up your process priority substantially while the mv operations are happening.

If, as you say in your addendum, there's only one place that checks alpha and it errors if it's not there, you could change that code to not error immediately, but try again in a short time (easily sub-second for two mv operations) - these retries should alleviate any problem unless you're replacing alpha very frequently.

paxdiablo
That's about as fast as you can do it in the shell; you could write a custom piece of C to move the two directories which would shave a few milliseconds off the time interval, or use a Perl script (or choose your own poison). There'd be no point in rewriting 'rm -fr', though.
Jonathan Leffler
+1  A: 

Even if you were accessing the inodes directly there would still be no way to atomically swap the inode values in user-space.

Tyler McHenry
+1  A: 

If you change the question slightly... let's say alpha is a link to directory alpha_1 and you want alpha to now refer to alpha_2,

ln -f -s alpha_2 alpha
rm -rf alpha_1

would work. Since alpha is always a link, I believe the swap can be atomic. Now when you have a new version, alpha_3

ln -f -s alpha_3 alpha
rm -rf alpha_2
Doug Currie
linux VFS doesn't support multiple directory hardlinks. Some other *nixes have limited support, restricted to the superuser. You also still would have to collect all the now orphaned links of the subdirectories and files.
JimB
Yes, it should be a soft link to be generally applicable. I have edited my response. However I don't believe there will be any orphans as long as alpha is always a link, which is what I meant by changing the question slightly. Of course, you will always have to remove the previous version of the dir
Doug Currie
Very close; turns out you need the -n flag too, otherwise you'll end up creating a symlink under the original directory. I actually tried your idea before posting the question, and it didn't work, but when I looked again and noticed the -n flag, that did it.Also, poop on whoever voted you down :)
dirtside
process A tries to do something within alphawhatever you do after this point might be atomic or not, you can still erase the directory while it is used. Being atomic is useless, what you need is serialisation, not atomicity, unless your code accessing alpha is also atomic.
shodanex
+3  A: 

Use a separate, guaranteed atomic, operation to act as a semaphore.

So, if the creating and removing a file operations are atomic:

1) create a file called "semaphore".

2) If and only if that is successful (no conflict with existing file), do the operation (either process alpha or move the directory, depending on the process)

3) rm semaphore.

Oddthinking
A: 

Something to keep in mind is that if your process has any of the files in alpha open when this move/delete occurs the process will not notice and any data written will be lost when the file is closed and finally removed.

Brian C. Lane
+1  A: 

Worrying about the atomic nature of the operation is meaningless. The thing is, the access to alpha by the other task will not be atomic anyway.

Oddthinking's semaphore approach is the only way to go.

If you can't modify the other task then you'll have to ensure it's not running before doing the replacement.

Loren Pechtel
A: 

Why don't you just do something like:

rm -rf alpha/*
mv bravo/* alpha/
rm -rf bravo/

That would mean that everything in alpha is destroyed, alpha never gets deleted, and all the contents get moved.

Dan Fego
A: 

The SQLite documentation section File Locking and Concurrency in SQLite Version 3 has a well-written description of its escalating locking protocol to control concurrent reading, exclusive writing, and rollback after a crash. Some of those ideas apply here.

seh
A: 

mv and ln can be used for atomic operations. I've used ln(1) to deploy web applications atomically.

The correct way to replace a symlink is with ln -nsf

ln -nsf <target> <link_name>

e.g.

$ mkdir dir1
$ mkdir dir2
$ ln -s dir1 mylink
$ ls -l mylink
lrwxrwxrwx  1 phil phil 4 Nov 16 14:45 mylink -> dir1
$ ln -nsf dir2 mylink
$ ls -l mylink
lrwxrwxrwx  1 phil phil 4 Nov 16 14:46 mylink -> dir2
Phil