以原子方式更改文件的属性
Atomically change attributes of a file
我想自动更改文件的某些属性。 (背景:这是针对用户空间 NFS 实现的,其中 SETATTR 调用在文件上设置了多个属性)。
我未能解决的问题是以原子方式进行更新。也就是说,stat()
s 或 rename()
s 文件的不同进程不应看到部分更新的属性。
不同的简化(即没有错误检查和符号链接处理)方法及其缺点是:
使用传统函数:
int setattr(char *path, attributes attrs)
{
lchown(path, attr.owner, attr.group);
chmod(path, attr.mode)
utime(path, ...)
return 0
}
这不是原子的(并且无法在符号链接上正常工作)。
使用 fchown()
等
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;
}
这有点原子性,但如果文件的模式不允许打开,或者文件是符号链接(在这种情况下,应该修改符号链接的属性而不是目标的属性),则会失败。将 O_PATH
添加到 open()
调用中也无济于事,因为 fchown()
和 fchmod()
然后会失败。
使用 fchownat()
等
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;
}
这看起来最有希望,但这又不是原子的。
我是不是遗漏了什么,或者没有一种方法可以满足我的要求?
你不能,而且你展示的示例中 none 是原子的。在 chown 和 chmod 或 chmod 和 utimes 调用之间总是有其他线程访问文件的机会。
在Linux 下有文件系统句柄的概念(与文件描述符相对),用于用户空间NFS 服务器等情况。目前我在 google 中唯一能找到的是 xfsprogs 的联机帮助页:handle manpage。但我认为这已在近年来 Linux 中得到推广,以处理更多文件系统。
不幸的是,如果不为它编写您自己的自定义系统调用或内核模块,这是不可能的。
虽然 Linux 内核本身确实有能力做到这一点,但它不会为它公开任何用户空间 API。此任务将需要一次设置所有属性的单个系统调用,或某种 "locking" 机制来防止文件被其他人访问(即 open
/stat
)过程,即使他们有权这样做。由于 Linux 不提供这样的系统调用,也不提供这样的 "locking" 机制,因此您无法从用户空间实现您想要实现的目标。
That is, a different process that stat()
s or rename()
s the file should not see partially updated attributes.
如果该文件已经存在,那么在修改它之前您几乎无法阻止另一个进程访问它。那艘船已经起航了。
rename
是原子的。
标准溶液是:
- 在目标目录中创建一个具有唯一临时名称的文件。必须在同一个目录才能保证在filesystem/same-mount点,这样就不需要文件内容(inode)拷贝了。
- 填充文件。如果文件是使用缓冲 I/O(除
write
、pwrite
、writev
或其他 系统调用 之外的任何其他内容)编写的,则需要被刷新或关闭(这会导致刷新,最终会执行上述系统调用之一)。
- 如有必要,设置正确的权限和属性。创建文件时可以设置权限。
rename
将文件更改为最终文件名。 rename
基本上以原子方式为文件内容(inode)创建一个新文件名(硬链接)并删除旧文件名。
这样当文件存在时它是完整的并且具有正确的权限和属性。
我想自动更改文件的某些属性。 (背景:这是针对用户空间 NFS 实现的,其中 SETATTR 调用在文件上设置了多个属性)。
我未能解决的问题是以原子方式进行更新。也就是说,stat()
s 或 rename()
s 文件的不同进程不应看到部分更新的属性。
不同的简化(即没有错误检查和符号链接处理)方法及其缺点是:
使用传统函数:
int setattr(char *path, attributes attrs) { lchown(path, attr.owner, attr.group); chmod(path, attr.mode) utime(path, ...) return 0 }
这不是原子的(并且无法在符号链接上正常工作)。
使用
fchown()
等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; }
这有点原子性,但如果文件的模式不允许打开,或者文件是符号链接(在这种情况下,应该修改符号链接的属性而不是目标的属性),则会失败。将
O_PATH
添加到open()
调用中也无济于事,因为fchown()
和fchmod()
然后会失败。使用
fchownat()
等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; }
这看起来最有希望,但这又不是原子的。
我是不是遗漏了什么,或者没有一种方法可以满足我的要求?
你不能,而且你展示的示例中 none 是原子的。在 chown 和 chmod 或 chmod 和 utimes 调用之间总是有其他线程访问文件的机会。
在Linux 下有文件系统句柄的概念(与文件描述符相对),用于用户空间NFS 服务器等情况。目前我在 google 中唯一能找到的是 xfsprogs 的联机帮助页:handle manpage。但我认为这已在近年来 Linux 中得到推广,以处理更多文件系统。
不幸的是,如果不为它编写您自己的自定义系统调用或内核模块,这是不可能的。
虽然 Linux 内核本身确实有能力做到这一点,但它不会为它公开任何用户空间 API。此任务将需要一次设置所有属性的单个系统调用,或某种 "locking" 机制来防止文件被其他人访问(即 open
/stat
)过程,即使他们有权这样做。由于 Linux 不提供这样的系统调用,也不提供这样的 "locking" 机制,因此您无法从用户空间实现您想要实现的目标。
That is, a different process that
stat()
s orrename()
s the file should not see partially updated attributes.
如果该文件已经存在,那么在修改它之前您几乎无法阻止另一个进程访问它。那艘船已经起航了。
rename
是原子的。
标准溶液是:
- 在目标目录中创建一个具有唯一临时名称的文件。必须在同一个目录才能保证在filesystem/same-mount点,这样就不需要文件内容(inode)拷贝了。
- 填充文件。如果文件是使用缓冲 I/O(除
write
、pwrite
、writev
或其他 系统调用 之外的任何其他内容)编写的,则需要被刷新或关闭(这会导致刷新,最终会执行上述系统调用之一)。 - 如有必要,设置正确的权限和属性。创建文件时可以设置权限。
rename
将文件更改为最终文件名。rename
基本上以原子方式为文件内容(inode)创建一个新文件名(硬链接)并删除旧文件名。
这样当文件存在时它是完整的并且具有正确的权限和属性。