mirror of
https://github.com/systemd/systemd-stable.git
synced 2024-12-24 21:34:08 +03:00
fs-util: add openat_report_new() wrapper around openat()
This is a wrapper around openat(). It works mostly the same, except for one thing: it race-freely reports whether we just created the indicated file in case O_CREAT is passed without O_EXCL.
This commit is contained in:
parent
f5d0f21c37
commit
ca8503f168
@ -1083,3 +1083,42 @@ int open_mkdir_at(int dirfd, const char *path, int flags, mode_t mode) {
|
||||
|
||||
return TAKE_FD(fd);
|
||||
}
|
||||
|
||||
int openat_report_new(int dirfd, const char *pathname, int flags, mode_t mode, bool *ret_newly_created) {
|
||||
unsigned attempts = 7;
|
||||
|
||||
/* Just like openat(), but adds one thing: optionally returns whether we created the file anew or if
|
||||
* it already existed before. This is only relevant of O_CREAT is set without O_EXCL, and thus will
|
||||
* shortcut to openat() otherwise */
|
||||
|
||||
if (!FLAGS_SET(flags, O_CREAT) || FLAGS_SET(flags, O_EXCL) || !ret_newly_created)
|
||||
return RET_NERRNO(openat(dirfd, pathname, flags, mode));
|
||||
|
||||
for (;;) {
|
||||
int fd;
|
||||
|
||||
/* First, attempt to open without O_CREAT/O_EXCL, i.e. open existing file */
|
||||
fd = openat(dirfd, pathname, flags & ~(O_CREAT | O_EXCL), mode);
|
||||
if (fd >= 0) {
|
||||
*ret_newly_created = false;
|
||||
return fd;
|
||||
}
|
||||
if (errno != ENOENT)
|
||||
return -errno;
|
||||
|
||||
/* So the file didn't exist yet, hence create it with O_CREAT/O_EXCL. */
|
||||
fd = openat(dirfd, pathname, flags | O_CREAT | O_EXCL, mode);
|
||||
if (fd >= 0) {
|
||||
*ret_newly_created = true;
|
||||
return fd;
|
||||
}
|
||||
if (errno != EEXIST)
|
||||
return -errno;
|
||||
|
||||
/* Hmm, so now we got EEXIST? So it apparently exists now? If so, let's try to open again
|
||||
* without the two flags. But let's not spin forever, hnce put a limit on things */
|
||||
|
||||
if (--attempts == 0) /* Give up eventually, somebody is playing with us */
|
||||
return -EEXIST;
|
||||
}
|
||||
}
|
||||
|
@ -110,3 +110,5 @@ 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);
|
||||
|
||||
int open_mkdir_at(int dirfd, const char *path, int flags, mode_t mode);
|
||||
|
||||
int openat_report_new(int dirfd, const char *pathname, int flags, mode_t mode, bool *ret_newly_created);
|
||||
|
@ -970,6 +970,56 @@ TEST(open_mkdir_at) {
|
||||
assert_se(subsubdir_fd >= 0);
|
||||
}
|
||||
|
||||
TEST(openat_report_new) {
|
||||
_cleanup_free_ char *j = NULL;
|
||||
_cleanup_(rm_rf_physical_and_freep) char *d = NULL;
|
||||
_cleanup_close_ int fd = -1;
|
||||
bool b;
|
||||
|
||||
assert_se(mkdtemp_malloc(NULL, &d) >= 0);
|
||||
|
||||
j = path_join(d, "test");
|
||||
assert_se(j);
|
||||
|
||||
fd = openat_report_new(AT_FDCWD, j, O_RDWR|O_CREAT, 0666, &b);
|
||||
assert_se(fd >= 0);
|
||||
fd = safe_close(fd);
|
||||
assert_se(b);
|
||||
|
||||
fd = openat_report_new(AT_FDCWD, j, O_RDWR|O_CREAT, 0666, &b);
|
||||
assert_se(fd >= 0);
|
||||
fd = safe_close(fd);
|
||||
assert_se(!b);
|
||||
|
||||
fd = openat_report_new(AT_FDCWD, j, O_RDWR|O_CREAT, 0666, &b);
|
||||
assert_se(fd >= 0);
|
||||
fd = safe_close(fd);
|
||||
assert_se(!b);
|
||||
|
||||
assert_se(unlink(j) >= 0);
|
||||
|
||||
fd = openat_report_new(AT_FDCWD, j, O_RDWR|O_CREAT, 0666, &b);
|
||||
assert_se(fd >= 0);
|
||||
fd = safe_close(fd);
|
||||
assert_se(b);
|
||||
|
||||
fd = openat_report_new(AT_FDCWD, j, O_RDWR|O_CREAT, 0666, &b);
|
||||
assert_se(fd >= 0);
|
||||
fd = safe_close(fd);
|
||||
assert_se(!b);
|
||||
|
||||
assert_se(unlink(j) >= 0);
|
||||
|
||||
fd = openat_report_new(AT_FDCWD, j, O_RDWR|O_CREAT, 0666, NULL);
|
||||
assert_se(fd >= 0);
|
||||
fd = safe_close(fd);
|
||||
|
||||
fd = openat_report_new(AT_FDCWD, j, O_RDWR|O_CREAT, 0666, &b);
|
||||
assert_se(fd >= 0);
|
||||
fd = safe_close(fd);
|
||||
assert_se(!b);
|
||||
}
|
||||
|
||||
static int intro(void) {
|
||||
arg_test_dir = saved_argv[1];
|
||||
return EXIT_SUCCESS;
|
||||
|
Loading…
Reference in New Issue
Block a user