8

If there was a process continually writing to a file, and I wanted to take control of the file with root, I could do something like this:

sudo rm somefile; sudo touch somefile

Is it possible for the appending process to append to the file between these two commands? If so, is there a way to ensure that no other command gets run in between?

4
  • 4
    This might be an XY problem. Can you explain the underlying problem you are trying to solve? Sep 8 '15 at 16:48
  • 1
    @NateEldredge: Totally not. I can undestand why it seems that way based on my contrived hypothetical. Don't even have a problem. Just curious. Sep 8 '15 at 17:11
  • 5
    The process is actually probably going to keep writing to the deleted file and not even try to open the new one.
    – Random832
    Sep 8 '15 at 18:47
  • My assumption in the hypothetical turned out to be a bit wrong, but I guess you could change "append" to "open" or "touch" and it would still make sense. Sep 8 '15 at 19:51
12

A chained command line is basically a small shell script; it'll run the first command using the usual fork+exec procedure, wait for it to exit, then run the second in the same way. Between the two commands, there's some arbitrary amount of time that the shell takes in its bookkeeping and processing, during which ordinary multiprocessing takes place and arbitrary other processes may do arbitrary other things. So the answer is 'no'. (If you actually do this, you'll find that the directory entry for somefile vanishes, but the file itself remains (since it's opened by a process) until it's closed. Disk space used by the file will not be reclaimed until that happens. Meanwhile, the touch command will create a new, unrelated file with the same name and path.)

If you want to change ownership of the file to root, just do sudo chown root:root somefile (though I'm not sure how that will affect processes with an open filehandle to it). If you want to destroy the current file contents, try truncate -s 0 somefile (the running process will continue to append to the now-empty file). If it's something else, perhaps clarify what you want to do.

5
  • Thanks Tom. I wasn't so much concerned with my hypothetical situation. Was just wondering about whether doing something like this is possible. Sep 8 '15 at 15:45
  • Regarding the general question, I don't think there's really a way to ensure atomicity in a shell script. You can use lockfiles and such to ensure that two cooperating processes don't step on each other, and you can use permissions to ensure that an arbitrary process can't step on your work. But preventing anything else from running would mess heavily with the kernel's multiprocessing; I don't think ring-3 code can do that at all.
    – Tom Hunt
    Sep 8 '15 at 15:56
  • You can use mandatory file locks if they're enabled and available on your filesystem.
    – roaima
    Sep 8 '15 at 15:59
  • 5
    chowning somefile won't affect processes that have a file handle to somefile.
    – PSkocik
    Sep 8 '15 at 16:26
  • E.g., you can doexec 3>somefile; sudo chmod 0600 somefile; echo hello world >&3; sudo cat somefile; exec 3>&- .
    – PSkocik
    Sep 8 '15 at 16:29
6

No. A rm command followed by a touch command is not atomic at all. It will be a long time between the two commands - possibly in the milliseconds range. A lot can happen during that time. If you are unlucky, your sudo credentials might even expire.

A single program calling the unlink and open calls would leave a much shorter window for a race, but it could still happen.

A safer approach would be to create the new file with a temporary name and use the rename system call. "Overwriting" a name with the rename system call is guaranteed to be atomic. This could be achieved using touch and mv.

But a process which has opened the old file for writing can keep writing to it long after it has been deleted. This will be the case both for a file deleted using unlink and one deleted using rename.

3

Once a process has an filehandle open for reading it doesn't matter what you do with the ownership or permissions: the process can continue accessing the file. You can even delete the file and the process will be able to continue accessing it via the filehandle.

Consider the permissions to be a control gate to obtaining a filehandle.

If you want to create a file atomically, then instead of doing this:

sudo rm somefile
sudo touch somefile

you could consider this:

sudo touch anotherfile
sudo perl -e "rename 'anotherfile', 'somefile'"

which will atomically replace somefile with anotherfile. (I'd have preferred to use mv -f but I cannot find a statement that guarantees this calls the rename(2) system call.


Perhaps you could update your Question to explain what you mean by "take control of the file". You may have a XY Problem here.

3
  • Use mv -f to get a rename(2) system call. You're right that ln won't, because it always uses the link(2) system call, which can't atomically replace. ln -f just does unlink(2) on the target first. Sep 10 '15 at 1:05
  • @PeterCordes ah yes of course, thank you. I had ln on the brain. I wasn't sure how to get rename(2) guaranteed to be involved and it was too late to go digging further
    – roaima
    Sep 10 '15 at 6:39
  • POSIX guarantees that mv "shall perform actions equivalent to" rename(old, new). pubs.opengroup.org/onlinepubs/9699919799/utilities/mv.html. mv -f acts like mv -i if the destination is not writeable, but it's not like cp -f or ln -f where it will unlink first. Sep 10 '15 at 7:05
2

No, but

sudo rm somefile; sudo touch somefile

is safe in most situations.

Most processes open files and then use the acquired file descriptor to access the file contents.

If a process opens somefile, in sh:

 exec 3>somefile

Then another process is free to unlink(=remove) somefile and the filedescriptor on which somefile was open by the first process (3 in this case) will continue to refer to the original contents of somefile, which is now a file in limbo.

sudo touch somefile

will create a new, unrelated somefile and all processes that had the old somefile open and now are only using a filedescriptor to refer to it will not be affected because they're refering to a different file -- one that's now in limbo.

If a non-root process attemtpts to refer to somefile by name, they'll get an EPERM error, because the new somefile is root-owned.

If you want to prevent multiple processes under the same user (e.g., root) from corrupting a file, linux has mandatory and advisory file locking. You can use the flock command in shell scripts for advisory file locking (see the manpage for more info).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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