views:

256

answers:

2

I'm working on a Perl-based file synchronization tool. It downloads files into a temporary directory (which is guaranteed to be on the same filesystem as the real file) and then moves the temporary files into place over the old ones, preserving metadata like permissions, ownership, and ACLs. I'm wondering how to achieve that last step on Linux.

On Mac OS X, at least in C, I would use the exchangedata function. This takes two filenames as arguments and swaps their contents, leaving all metadata (besides mtime) intact. It guarantees that the operation is atomic—all readers will see either the old file or the new one, never something in between. Unfortunately, I don't think it's available on Linux.

I know that rename moves atomically, but it doesn't preserve metadata. On the other hand, I could open the file and overwrite the data with the contents of the new one, which would preserve all metadata but would not be an atomic operation. Any suggestions on tackling this problem?

+5  A: 

The only approach I see here is to read the metadata from the file you are replacing, apply that to the temporary file, and then rename the temporary file over the old file. (rename preserves the source file attributes, obviously.)

jrockway
It's hard to know what all the proper metadata is, though. There's file attributes, extended attributes, ACLs, security labels, file capabilities, and other filesystem- or system-specific things...
ephemient
That sounds like a good list of things to start with. "The enemy of good is great."
jrockway
Yeah, if there is no complete solution (which there doesn't seem to be, so far), then getting the problem at least partly right is better than nothing. Even `rsync` has a limited set of metadata that it knows/cares about.
ephemient
+2  A: 

Filesystem-specific, but...

The XFS_IOC_SWAPEXT ioctl swaps the extents of two file descriptors on XFS.

#include <xfs/xfs.h>
#include <xfs/xfs_dfrag.h>

xfs_swapext_t sx = {
    ...,
    .sx_fdtarget = fd1,
    .sx_fdtmp    = fd2,
    ...
};
xfs_swapext(fd1, &sx);

See the sources to xfs_fsr for example usage.

ephemient