fileio: revamp search_and_fopen()

Let's modernize and clean up search_and_fopen a bit: let's add support
for regular open() (instead of fopen()), as well as access() (if caller
just wants to check if a file exists without opening it.

This unifies much of the code involved, which previously was duplicated
in search_and_fopen() and search_and_fopen_nulstr()
This commit is contained in:
Lennart Poettering
2023-10-05 19:01:28 +02:00
parent 27a1719bba
commit 2c07d314b2
3 changed files with 141 additions and 60 deletions
+112 -58
View File
@@ -1076,117 +1076,171 @@ int fdopen_independent(int fd, const char *mode, FILE **ret) {
return 0;
}
static int search_and_fopen_internal(
static int search_and_open_internal(
const char *path,
const char *mode,
int mode, /* if ret_fd is NULL this is an [FRWX]_OK mode for access(), otherwise an open mode for open() */
const char *root,
char **search,
FILE **ret,
int *ret_fd,
char **ret_path) {
int r;
assert(!ret_fd || !FLAGS_SET(mode, O_CREAT)); /* We don't support O_CREAT for this */
assert(path);
assert(mode);
assert(ret);
if (path_is_absolute(path)) {
_cleanup_close_ int fd = -EBADF;
if (ret_fd)
/* We only specify 0777 here to appease static analyzers, it's never used since we
* don't support O_CREAT here */
r = fd = RET_NERRNO(open(path, mode, 0777));
else
r = RET_NERRNO(access(path, mode));
if (r < 0)
return r;
if (ret_path) {
r = path_simplify_alloc(path, ret_path);
if (r < 0)
return r;
}
if (ret_fd)
*ret_fd = TAKE_FD(fd);
return 0;
}
if (!path_strv_resolve_uniq(search, root))
return -ENOMEM;
STRV_FOREACH(i, search) {
_cleanup_close_ int fd = -EBADF;
_cleanup_free_ char *p = NULL;
FILE *f;
p = path_join(root, *i, path);
if (!p)
return -ENOMEM;
f = fopen(p, mode);
if (f) {
if (ret_fd)
/* as above, 0777 is static analyzer appeasement */
r = fd = RET_NERRNO(open(p, mode, 0777));
else
r = RET_NERRNO(access(p, F_OK));
if (r >= 0) {
if (ret_path)
*ret_path = path_simplify(TAKE_PTR(p));
*ret = f;
if (ret_fd)
*ret_fd = TAKE_FD(fd);
return 0;
}
if (errno != ENOENT)
return -errno;
if (r != -ENOENT)
return r;
}
return -ENOENT;
}
int search_and_fopen(
const char *filename,
const char *mode,
int search_and_open(
const char *path,
int mode,
const char *root,
const char **search,
FILE **ret,
char **search,
int *ret_fd,
char **ret_path) {
_cleanup_strv_free_ char **copy = NULL;
int r;
assert(filename);
assert(mode);
assert(ret);
if (path_is_absolute(filename)) {
_cleanup_fclose_ FILE *f = NULL;
f = fopen(filename, mode);
if (!f)
return -errno;
if (ret_path) {
r = path_simplify_alloc(filename, ret_path);
if (r < 0)
return r;
}
*ret = TAKE_PTR(f);
return 0;
}
assert(path);
copy = strv_copy((char**) search);
if (!copy)
return -ENOMEM;
return search_and_fopen_internal(filename, mode, root, copy, ret, ret_path);
return search_and_open_internal(path, mode, root, copy, ret_fd, ret_path);
}
int search_and_fopen_nulstr(
const char *filename,
static int search_and_fopen_internal(
const char *path,
const char *mode,
const char *root,
const char *search,
FILE **ret,
char **search,
FILE **ret_file,
char **ret_path) {
_cleanup_strv_free_ char **s = NULL;
_cleanup_free_ char *found_path = NULL;
_cleanup_close_ int fd = -EBADF;
int r;
if (path_is_absolute(filename)) {
_cleanup_fclose_ FILE *f = NULL;
assert(path);
assert(mode || !ret_file);
f = fopen(filename, mode);
r = search_and_open(
path,
mode ? fopen_mode_to_flags(mode) : 0,
root,
search,
ret_file ? &fd : NULL,
ret_path ? &found_path : NULL);
if (r < 0)
return r;
if (ret_file) {
FILE *f = take_fdopen(&fd, mode);
if (!f)
return -errno;
if (ret_path) {
r = path_simplify_alloc(filename, ret_path);
if (r < 0)
return r;
}
*ret = TAKE_PTR(f);
return 0;
*ret_file = f;
}
s = strv_split_nulstr(search);
if (!s)
if (ret_path)
*ret_path = TAKE_PTR(found_path);
return 0;
}
int search_and_fopen(
const char *path,
const char *mode,
const char *root,
const char **search,
FILE **ret_file,
char **ret_path) {
_cleanup_strv_free_ char **copy = NULL;
assert(path);
assert(mode || !ret_file);
copy = strv_copy((char**) search);
if (!copy)
return -ENOMEM;
return search_and_fopen_internal(filename, mode, root, s, ret, ret_path);
return search_and_fopen_internal(path, mode, root, copy, ret_file, ret_path);
}
int search_and_fopen_nulstr(
const char *path,
const char *mode,
const char *root,
const char *search,
FILE **ret_file,
char **ret_path) {
_cleanup_strv_free_ char **l = NULL;
assert(path);
assert(mode || !ret_file);
l = strv_split_nulstr(search);
if (!l)
return -ENOMEM;
return search_and_fopen_internal(path, mode, root, l, ret_file, ret_path);
}
int fflush_and_check(FILE *f) {
+6 -2
View File
@@ -129,8 +129,12 @@ static inline int fopen_unlocked(const char *path, const char *mode, FILE **ret)
int fdopen_independent(int fd, const char *mode, FILE **ret);
int search_and_fopen(const char *path, const char *mode, const char *root, const char **search, FILE **ret, char **ret_path);
int search_and_fopen_nulstr(const char *path, const char *mode, const char *root, const char *search, FILE **ret, char **ret_path);
int search_and_open(const char *path, int mode, const char *root, char **search, int *ret_fd, char **ret_path);
static inline int search_and_access(const char *path, int mode, const char *root, char**search, char **ret_path) {
return search_and_open(path, mode, root, search, NULL, ret_path);
}
int search_and_fopen(const char *path, const char *mode, const char *root, const char **search, FILE **ret_file, char **ret_path);
int search_and_fopen_nulstr(const char *path, const char *mode, const char *root, const char *search, FILE **ret_file, char **ret_path);
int fflush_and_check(FILE *f);
int fflush_sync_and_check(FILE *f);
+23
View File
@@ -582,12 +582,23 @@ TEST(search_and_fopen) {
f = safe_fclose(f);
p = mfree(p);
r = search_and_fopen(basename(name), NULL, NULL, (const char**) dirs, NULL, &p);
assert_se(r >= 0);
assert_se(e = path_startswith(p, "/tmp/"));
assert_se(streq(basename(name), e));
p = mfree(p);
r = search_and_fopen(name, "re", NULL, (const char**) dirs, &f, &p);
assert_se(r >= 0);
assert_se(path_equal(name, p));
f = safe_fclose(f);
p = mfree(p);
r = search_and_fopen(name, NULL, NULL, (const char**) dirs, NULL, &p);
assert_se(r >= 0);
assert_se(path_equal(name, p));
p = mfree(p);
r = search_and_fopen(basename(name), "re", "/", (const char**) dirs, &f, &p);
assert_se(r >= 0);
assert_se(e = path_startswith(p, "/tmp/"));
@@ -595,16 +606,28 @@ TEST(search_and_fopen) {
f = safe_fclose(f);
p = mfree(p);
r = search_and_fopen(basename(name), NULL, "/", (const char**) dirs, NULL, &p);
assert_se(r >= 0);
assert_se(e = path_startswith(p, "/tmp/"));
assert_se(streq(basename(name), e));
p = mfree(p);
r = search_and_fopen("/a/file/which/does/not/exist/i/guess", "re", NULL, (const char**) dirs, &f, &p);
assert_se(r == -ENOENT);
r = search_and_fopen("/a/file/which/does/not/exist/i/guess", NULL, NULL, (const char**) dirs, NULL, &p);
assert_se(r == -ENOENT);
r = search_and_fopen("afilewhichdoesnotexistiguess", "re", NULL, (const char**) dirs, &f, &p);
assert_se(r == -ENOENT);
r = search_and_fopen("afilewhichdoesnotexistiguess", NULL, NULL, (const char**) dirs, NULL, &p);
assert_se(r == -ENOENT);
r = unlink(name);
assert_se(r == 0);
r = search_and_fopen(basename(name), "re", NULL, (const char**) dirs, &f, &p);
assert_se(r == -ENOENT);
r = search_and_fopen(basename(name), NULL, NULL, (const char**) dirs, NULL, &p);
assert_se(r == -ENOENT);
}
TEST(search_and_fopen_nulstr) {