Reputation: 3575
I want to atomically change some attributes of a file. (Background: This is for a userspace NFS implementation where the SETATTR call sets several attributes on a file).
The problem I fail to solve is to make the update in an atomic way. That is, a different process that stat()
s or rename()
s the file should not see partially updated attributes.
Different simplified (i.e. no error checking and symlink handling) approaches with their downsides are:
using traditional functions:
int setattr(char *path, attributes attrs)
{
lchown(path, attr.owner, attr.group);
chmod(path, attr.mode)
utime(path, ...)
return 0
}
This is not atomic (and fails to work properly on symlinks).
using fchown()
et al.
int setattr(char *path, attributes attrs)
{
int fd = open(path, O_WRONLY);
fchown(fd, attr.owner, attr.group);
fchmod(fd, attr.mode);
futimens(fd, ...);
return 0;
}
This is somewhat atomic, but fails if the file's mode doesn't allow opening, or the file is a symlink (in which case the symlink's attributes should be modified instead of the target's). Adding O_PATH
to the open()
call doesn't help here either, as fchown()
and fchmod()
then fail.
using fchownat()
et al.
int setattr(char *path, attributes attrs)
{
int fd = open(dirname(path), O_WRONLY | O_PATH);
fchownat(fd, basename(path), attr.owner, attr.group, AT_SYMLINK_NOFOLLOW);
fchmodat(fd, basename(path), attr.mode, 0);
utimensat(fd, basename(path), ..., AT_SYMLINK_NOFOLLOW);
return 0;
}
This looks most promising, but again this isn't atomic.
Am I missing something or isn't there an approach that does what I want?
Upvotes: 4
Views: 765
Reputation: 136286
That is, a different process that
stat()
s orrename()
s the file should not see partially updated attributes.
If the file already exists there is little you can do to stop another process from accessing it before you modify it. That ship has sailed.
rename
is atomic.
The standard solution has been:
write
, pwrite
, writev
or other syscalls) it needs to be flushed or closed (which causes a flush, which ends up doing one of the above syscalls).rename
the file to its final filename. rename
essentially atomically creates a new filename (hardlink) to the file content (inode) and removes the old filename.This way when the file exists it is complete and has correct permissions and attributes.
Upvotes: 2
Reputation: 69346
Unfortunately this is not possible without writing your own custom syscall or kernel module for it.
While the Linux kernel itself definitely has the ability to make this happen, it doesn't expose any userspace API for it. This task would require either a single syscall that sets all the attributes at once, or some kind of "locking" mechanism to prevent files from being accessed (that is, open
/stat
) by other processes even if they have the rights to do so. Since Linux does not provide such a syscall, nor such a "locking" mechanism, what you want to achieve is not possible from userspace.
Upvotes: 1
Reputation: 12332
You can't and none of the examples you've shown is atomic. There is always a chance some other thread accesses the file between the chown and chmod or chmod and utimes call.
Under Linux there is the concept of a filesystem handle (as opposed to a file descriptor) for such cases as a userspace NFS server. Only thing I can find in google at the moment is the manpage from xfsprogs: handle manpage. But I think this has been generalized in Linux in recent years to work with more filesystems.
Upvotes: 0