mirror of
https://github.com/morgan9e/systemd
synced 2026-04-14 08:25:20 +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);
|
||||
|
||||
@@ -713,6 +713,34 @@ TEST(xopenat_full) {
|
||||
assert_se((fd2 = xopenat_full(fd, "", O_RDWR|O_CLOEXEC, 0, 0644)) >= 0);
|
||||
}
|
||||
|
||||
TEST(xopenat_regular) {
|
||||
|
||||
_cleanup_close_ int fd = -EBADF;
|
||||
|
||||
assert_se(xopenat_full(AT_FDCWD, "/dev/null", O_RDWR|O_CLOEXEC, XO_REGULAR, 0) == -EBADFD);
|
||||
assert_se(xopenat_full(AT_FDCWD, "/proc", O_RDONLY|O_CLOEXEC, XO_REGULAR, 0) == -EISDIR);
|
||||
assert_se(xopenat_full(AT_FDCWD, "/proc/self", O_RDONLY|O_CLOEXEC, XO_REGULAR, 0) == -EISDIR);
|
||||
assert_se(xopenat_full(AT_FDCWD, "/proc/self", O_RDONLY|O_CLOEXEC|O_NOFOLLOW, XO_REGULAR, 0) == -ELOOP);
|
||||
|
||||
fd = xopenat_full(AT_FDCWD, "/proc/mounts", O_RDONLY|O_CLOEXEC, XO_REGULAR, 0);
|
||||
assert_se(fd >= 0);
|
||||
fd = safe_close(fd);
|
||||
|
||||
assert_se(xopenat_full(AT_FDCWD, "/proc/mounts", O_RDONLY|O_CLOEXEC|O_NOFOLLOW, XO_REGULAR, 0) == -ELOOP);
|
||||
|
||||
fd = xopenat_full(AT_FDCWD, "/proc/mounts", O_RDONLY|O_CLOEXEC|O_PATH, XO_REGULAR, 0);
|
||||
assert_se(fd >= 0);
|
||||
fd = safe_close(fd);
|
||||
|
||||
fd = xopenat_full(AT_FDCWD, "/tmp/xopenat-regular-test", O_RDWR|O_CLOEXEC|O_CREAT, XO_REGULAR, 0600);
|
||||
assert_se(fd >= 0);
|
||||
fd = safe_close(fd);
|
||||
|
||||
assert_se(xopenat_full(AT_FDCWD, "/tmp/xopenat-regular-test", O_RDWR|O_CLOEXEC|O_CREAT|O_EXCL, XO_REGULAR, 0600) == -EEXIST);
|
||||
|
||||
assert_se(unlink("/tmp/xopenat-regular-test") >= 0);
|
||||
}
|
||||
|
||||
TEST(xopenat_lock_full) {
|
||||
_cleanup_(rm_rf_physical_and_freep) char *t = NULL;
|
||||
_cleanup_close_ int tfd = -EBADF, fd = -EBADF;
|
||||
|
||||
Reference in New Issue
Block a user