mirror of
https://github.com/morgan9e/systemd
synced 2026-04-15 00:47:10 +09:00
fs-util: add XO_REGULAR flag for xopenat()
If this flag is set we guarantee that the fd returned refers to a regular file. If the file exists and is not one, fails.
This commit is contained in:
@@ -1123,6 +1123,9 @@ int xopenat_full(int dir_fd, const char *path, int open_flags, XOpenFlags xopen_
|
||||
|
||||
assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
|
||||
|
||||
/* An inode cannot be both a directory and a regular file at the same time. */
|
||||
assert(!(FLAGS_SET(open_flags, O_DIRECTORY) && FLAGS_SET(xopen_flags, XO_REGULAR)));
|
||||
|
||||
/* This is like openat(), but has a few tricks up its sleeves, extending behaviour:
|
||||
*
|
||||
* • O_DIRECTORY|O_CREAT is supported, which causes a directory to be created, and immediately
|
||||
@@ -1134,6 +1137,8 @@ int xopenat_full(int dir_fd, const char *path, int open_flags, XOpenFlags xopen_
|
||||
*
|
||||
* • If XO_NOCOW is specified will turn on the NOCOW btrfs flag on the file, if available.
|
||||
*
|
||||
* • if XO_REGULAR is specified will return an error if inode is not a regular file.
|
||||
*
|
||||
* • If mode is specified as MODE_INVALID, we'll use 0755 for dirs, and 0644 for regular files.
|
||||
*/
|
||||
|
||||
@@ -1142,6 +1147,13 @@ int xopenat_full(int dir_fd, const char *path, int open_flags, XOpenFlags xopen_
|
||||
|
||||
if (isempty(path)) {
|
||||
assert(!FLAGS_SET(open_flags, O_CREAT|O_EXCL));
|
||||
|
||||
if (FLAGS_SET(xopen_flags, XO_REGULAR)) {
|
||||
r = fd_verify_regular(dir_fd);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return fd_reopen(dir_fd, open_flags & ~O_NOFOLLOW);
|
||||
}
|
||||
|
||||
@@ -1171,10 +1183,66 @@ int xopenat_full(int dir_fd, const char *path, int open_flags, XOpenFlags xopen_
|
||||
open_flags &= ~(O_EXCL|O_CREAT);
|
||||
}
|
||||
|
||||
fd = openat_report_new(dir_fd, path, open_flags, mode, &made_file);
|
||||
if (fd < 0) {
|
||||
r = fd;
|
||||
goto error;
|
||||
if (FLAGS_SET(xopen_flags, XO_REGULAR)) {
|
||||
/* Guarantee we return a regular fd only, and don't open the file unless we verified it
|
||||
* first */
|
||||
|
||||
if (FLAGS_SET(open_flags, O_PATH)) {
|
||||
fd = openat(dir_fd, path, open_flags, mode);
|
||||
if (fd < 0) {
|
||||
r = -errno;
|
||||
goto error;
|
||||
}
|
||||
|
||||
r = fd_verify_regular(fd);
|
||||
if (r < 0)
|
||||
goto error;
|
||||
|
||||
} else if (FLAGS_SET(open_flags, O_CREAT|O_EXCL)) {
|
||||
/* In O_EXCL mode we can just create the thing, everything is dealt with for us */
|
||||
fd = openat(dir_fd, path, open_flags, mode);
|
||||
if (fd < 0) {
|
||||
r = -errno;
|
||||
goto error;
|
||||
}
|
||||
|
||||
made_file = true;
|
||||
} else {
|
||||
/* Otherwise pin the inode first via O_PATH */
|
||||
_cleanup_close_ int inode_fd = openat(dir_fd, path, O_PATH|O_CLOEXEC|(open_flags & O_NOFOLLOW));
|
||||
if (inode_fd < 0) {
|
||||
if (errno != ENOENT || !FLAGS_SET(open_flags, O_CREAT)) {
|
||||
r = -errno;
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Doesn't exist yet, then try to create it */
|
||||
fd = openat(dir_fd, path, open_flags|O_CREAT|O_EXCL, mode);
|
||||
if (fd < 0) {
|
||||
r = -errno;
|
||||
goto error;
|
||||
}
|
||||
|
||||
made_file = true;
|
||||
} else {
|
||||
/* OK, we pinned it. Now verify it's actually a regular file, and then reopen it */
|
||||
r = fd_verify_regular(inode_fd);
|
||||
if (r < 0)
|
||||
goto error;
|
||||
|
||||
fd = fd_reopen(inode_fd, open_flags & ~(O_NOFOLLOW|O_CREAT));
|
||||
if (fd < 0) {
|
||||
r = fd;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
fd = openat_report_new(dir_fd, path, open_flags, mode, &made_file);
|
||||
if (fd < 0) {
|
||||
r = fd;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
if (call_label_ops_post) {
|
||||
|
||||
@@ -134,9 +134,10 @@ int posix_fallocate_loop(int fd, uint64_t offset, uint64_t size);
|
||||
int parse_cifs_service(const char *s, char **ret_host, char **ret_service, char **ret_path);
|
||||
|
||||
typedef enum XOpenFlags {
|
||||
XO_LABEL = 1 << 0,
|
||||
XO_SUBVOLUME = 1 << 1,
|
||||
XO_NOCOW = 1 << 2,
|
||||
XO_LABEL = 1 << 0, /* When creating: relabel */
|
||||
XO_SUBVOLUME = 1 << 1, /* When creating as directory: make it a subvolume */
|
||||
XO_NOCOW = 1 << 2, /* Enable NOCOW mode after opening */
|
||||
XO_REGULAR = 1 << 3, /* Fail if the inode is not a regular file */
|
||||
} XOpenFlags;
|
||||
|
||||
int open_mkdir_at_full(int dirfd, const char *path, int flags, XOpenFlags xopen_flags, mode_t mode);
|
||||
|
||||
Reference in New Issue
Block a user