From 0a38e6b9a37ba16d6dd6e50323c5441914b40af9 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 8 Jan 2019 18:23:40 +0100 Subject: [PATCH] fileio: add an openat() flavour for fopen() This adds xfopenat() which is to fopen() what xopendirat() is to opendir(), i.e. the "at" counterpart to fopen(). (Similar to the xopendir() case, we prefix this with "x", in case libc gains this natively eventually.) --- src/basic/fileio.c | 75 ++++++++++++++++++++++++++++++++++++++++++++++ src/basic/fileio.h | 1 + 2 files changed, 76 insertions(+) diff --git a/src/basic/fileio.c b/src/basic/fileio.c index 714a00cd9ae..044b31d5842 100644 --- a/src/basic/fileio.c +++ b/src/basic/fileio.c @@ -680,6 +680,81 @@ DIR *xopendirat(int fd, const char *name, int flags) { return d; } +static int mode_to_flags(const char *mode) { + const char *p; + int flags; + + if ((p = startswith(mode, "r+"))) + flags = O_RDWR; + else if ((p = startswith(mode, "r"))) + flags = O_RDONLY; + else if ((p = startswith(mode, "w+"))) + flags = O_RDWR|O_CREAT|O_TRUNC; + else if ((p = startswith(mode, "w"))) + flags = O_WRONLY|O_CREAT|O_TRUNC; + else if ((p = startswith(mode, "a+"))) + flags = O_RDWR|O_CREAT|O_APPEND; + else if ((p = startswith(mode, "a"))) + flags = O_WRONLY|O_CREAT|O_APPEND; + else + return -EINVAL; + + for (; *p != 0; p++) { + + switch (*p) { + + case 'e': + flags |= O_CLOEXEC; + break; + + case 'x': + flags |= O_EXCL; + break; + + case 'm': + /* ignore this here, fdopen() might care later though */ + break; + + case 'c': /* not sure what to do about this one */ + default: + return -EINVAL; + } + } + + return flags; +} + +int xfopenat(int dir_fd, const char *path, const char *mode, int flags, FILE **ret) { + FILE *f; + + /* A combination of fopen() with openat() */ + + if (dir_fd == AT_FDCWD && flags == 0) { + f = fopen(path, mode); + if (!f) + return -errno; + } else { + int fd, mode_flags; + + mode_flags = mode_to_flags(mode); + if (mode_flags < 0) + return mode_flags; + + fd = openat(dir_fd, path, mode_flags | flags); + if (fd < 0) + return -errno; + + f = fdopen(fd, mode); + if (!f) { + safe_close(fd); + return -errno; + } + } + + *ret = f; + return 0; +} + static int search_and_fopen_internal(const char *path, const char *mode, const char *root, char **search, FILE **_f) { char **i; diff --git a/src/basic/fileio.h b/src/basic/fileio.h index 31bfef33ac9..d5bfe1f0faf 100644 --- a/src/basic/fileio.h +++ b/src/basic/fileio.h @@ -69,6 +69,7 @@ int executable_is_script(const char *path, char **interpreter); int get_proc_field(const char *filename, const char *pattern, const char *terminator, char **field); DIR *xopendirat(int dirfd, const char *name, int flags); +int xfopenat(int dir_fd, const char *path, const char *mode, int flags, FILE **ret); int search_and_fopen(const char *path, const char *mode, const char *root, const char **search, FILE **_f); int search_and_fopen_nulstr(const char *path, const char *mode, const char *root, const char *search, FILE **_f);