Tim Bray provides Apple some instructions on how to update files containing important data (like iCal calendars). His directions are fine at a high level, but they're actually not quite detailed enough to implement from.
So here's how you safely update Unix files:
- create a new scratch file in the same directory as the target file (ideally mode 0600).
- write the data to it; optionally
fsync(); optionally verify the data. - copy the current version's permissions to the scratch file.
-
link(2)the current version of the file to the backup file name. rename(2)the scratch file to the real filename.fsync()your ex-scratch file, now real file, again.
(Add extra steps to taste if you want to keep more than one backup file.)
The important bit, and the important difference, is the
link(2)
and the
rename(2)
. This sequence insures that if the system crashes, no matter when, you will always have some version of the file around; you will never be left with
foo.old
and
foo.new
but no
foo
. (This guarantee is often important when doing updating things like system binaries or
/etc/passwd
.)
For shell scripts, this is:
cp foo /etc/.newfile && \ cmp -s foo /etc/.newfile && \ ln -f /etc/foo /etc/foo.bak && \ mv -f /etc/.newfile /etc/foo sync
I see a lot of shell scripts that leave out the
cmp
. I always think that they're being awfully optimistic about
cp
's error handling. (Technically you should have a
sync
between the
cp
and the
cmp
because that's the only insurance you have against physical disk errors on the write; what
cp
has written has not necessarily hit the platters and thus run into any physical media problems by the time it exits successfully. Issues like this give paranoid sysadmins nervous tics.)
I have omitted the step to synchronize permissions and ownership, because I don't know of a good utility to copy them from file A to file B.