diff options
author | Daan De Meyer <daan.j.demeyer@gmail.com> | 2023-04-10 11:50:34 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-04-10 11:50:34 +0200 |
commit | 25d598e144611b98b3abc6f56826c5a4e302470a (patch) | |
tree | 55060573b97749dec628cecdf3505bfbfdbfaf9a | |
parent | 7b411cf8421ef3bf6c05edcf131f63b8e7bae8ac (diff) | |
parent | 61d9982c510474f69455f30a55fcbebeaa15bf38 (diff) | |
download | systemd-25d598e144611b98b3abc6f56826c5a4e302470a.tar.gz |
Merge pull request #27184 from yuwata/xfopenat-full
fileio: introduce xfopenat_full()
-rw-r--r-- | src/basic/fileio.c | 194 | ||||
-rw-r--r-- | src/basic/fileio.h | 28 |
2 files changed, 126 insertions, 96 deletions
diff --git a/src/basic/fileio.c b/src/basic/fileio.c index 2eefaf0428..48ffb4e5e6 100644 --- a/src/basic/fileio.c +++ b/src/basic/fileio.c @@ -44,20 +44,6 @@ * can detect EOFs. */ #define READ_VIRTUAL_BYTES_MAX (4U*1024U*1024U - 2U) -int fopen_unlocked_at(int dir_fd, const char *path, const char *options, int flags, FILE **ret) { - int r; - - assert(ret); - - r = xfopenat(dir_fd, path, options, flags, ret); - if (r < 0) - return r; - - (void) __fsetlocking(*ret, FSETLOCKING_BYCALLER); - - return 0; -} - int fdopen_unlocked(int fd, const char *options, FILE **ret) { assert(ret); @@ -267,7 +253,8 @@ int write_string_file_ts_at( const struct timespec *ts) { _cleanup_fclose_ FILE *f = NULL; - int q, r, fd; + _cleanup_close_ int fd = -EBADF; + int q, r; assert(fn); assert(line); @@ -304,11 +291,9 @@ int write_string_file_ts_at( goto fail; } - r = fdopen_unlocked(fd, "w", &f); - if (r < 0) { - safe_close(fd); + r = take_fdopen_unlocked(&fd, "w", &f); + if (r < 0) goto fail; - } if (flags & WRITE_STRING_FILE_DISABLE_BUFFER) setvbuf(f, NULL, _IONBF, 0); @@ -761,62 +746,19 @@ int read_full_file_full( size_t *ret_size) { _cleanup_fclose_ FILE *f = NULL; + XfopenFlags xflags = XFOPEN_UNLOCKED; int r; assert(filename); assert(ret_contents); - r = xfopenat(dir_fd, filename, "re", 0, &f); - if (r < 0) { - _cleanup_close_ int sk = -EBADF; - - /* ENXIO is what Linux returns if we open a node that is an AF_UNIX socket */ - if (r != -ENXIO) - return r; - - /* If this is enabled, let's try to connect to it */ - if (!FLAGS_SET(flags, READ_FULL_FILE_CONNECT_SOCKET)) - return -ENXIO; - - /* Seeking is not supported on AF_UNIX sockets */ - if (offset != UINT64_MAX) - return -ENXIO; - - sk = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0); - if (sk < 0) - return -errno; - - if (bind_name) { - /* If the caller specified a socket name to bind to, do so before connecting. This is - * useful to communicate some minor, short meta-information token from the client to - * the server. */ - union sockaddr_union bsa; - - r = sockaddr_un_set_path(&bsa.un, bind_name); - if (r < 0) - return r; - - if (bind(sk, &bsa.sa, r) < 0) - return -errno; - } - - r = connect_unix_path(sk, dir_fd, filename); - if (IN_SET(r, -ENOTSOCK, -EINVAL)) /* propagate original error if this is not a socket after all */ - return -ENXIO; - if (r < 0) - return r; - - if (shutdown(sk, SHUT_WR) < 0) - return -errno; + if (FLAGS_SET(flags, READ_FULL_FILE_CONNECT_SOCKET) && /* If this is enabled, let's try to connect to it */ + offset == UINT64_MAX) /* Seeking is not supported on AF_UNIX sockets */ + xflags |= XFOPEN_SOCKET; - f = fdopen(sk, "r"); - if (!f) - return -errno; - - TAKE_FD(sk); - } - - (void) __fsetlocking(f, FSETLOCKING_BYCALLER); + r = xfopenat_full(dir_fd, filename, "re", 0, xflags, bind_name, &f); + if (r < 0) + return r; return read_full_stream_full(f, filename, offset, size, flags, ret_contents, ret_size); } @@ -923,8 +865,7 @@ int get_proc_field(const char *filename, const char *pattern, const char *termin } DIR *xopendirat(int fd, const char *name, int flags) { - int nfd; - DIR *d; + _cleanup_close_ int nfd = -EBADF; assert(!(flags & O_CREAT)); @@ -935,13 +876,7 @@ DIR *xopendirat(int fd, const char *name, int flags) { if (nfd < 0) return NULL; - d = fdopendir(nfd); - if (!d) { - safe_close(nfd); - return NULL; - } - - return d; + return take_fdopendir(&nfd); } int fopen_mode_to_flags(const char *mode) { @@ -990,33 +925,111 @@ int fopen_mode_to_flags(const char *mode) { return flags; } -int xfopenat(int dir_fd, const char *path, const char *mode, int flags, FILE **ret) { +static int xfopenat_regular(int dir_fd, const char *path, const char *mode, int open_flags, FILE **ret) { FILE *f; /* A combination of fopen() with openat() */ - if (dir_fd == AT_FDCWD && flags == 0) { + assert(dir_fd >= 0 || dir_fd == AT_FDCWD); + assert(path); + assert(mode); + assert(ret); + + if (dir_fd == AT_FDCWD && open_flags == 0) f = fopen(path, mode); - if (!f) - return -errno; - } else { - int fd, mode_flags; + else { + _cleanup_close_ int fd = -EBADF; + int mode_flags; mode_flags = fopen_mode_to_flags(mode); if (mode_flags < 0) return mode_flags; - fd = openat(dir_fd, path, mode_flags | flags); + fd = openat(dir_fd, path, mode_flags | open_flags); if (fd < 0) return -errno; - f = fdopen(fd, mode); - if (!f) { - safe_close(fd); + f = take_fdopen(&fd, mode); + } + if (!f) + return -errno; + + *ret = f; + return 0; +} + +static int xfopenat_unix_socket(int dir_fd, const char *path, const char *bind_name, FILE **ret) { + _cleanup_close_ int sk = -EBADF; + FILE *f; + int r; + + assert(dir_fd >= 0 || dir_fd == AT_FDCWD); + assert(path); + assert(ret); + + sk = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0); + if (sk < 0) + return -errno; + + if (bind_name) { + /* If the caller specified a socket name to bind to, do so before connecting. This is + * useful to communicate some minor, short meta-information token from the client to + * the server. */ + union sockaddr_union bsa; + + r = sockaddr_un_set_path(&bsa.un, bind_name); + if (r < 0) + return r; + + if (bind(sk, &bsa.sa, r) < 0) return -errno; - } } + r = connect_unix_path(sk, dir_fd, path); + if (r < 0) + return r; + + if (shutdown(sk, SHUT_WR) < 0) + return -errno; + + f = take_fdopen(&sk, "r"); + if (!f) + return -errno; + + *ret = f; + return 0; +} + +int xfopenat_full( + int dir_fd, + const char *path, + const char *mode, + int open_flags, + XfopenFlags flags, + const char *bind_name, + FILE **ret) { + + FILE *f = NULL; /* avoid false maybe-uninitialized warning */ + int r; + + assert(dir_fd >= 0 || dir_fd == AT_FDCWD); + assert(path); + assert(mode); + assert(ret); + + r = xfopenat_regular(dir_fd, path, mode, open_flags, &f); + if (r == -ENXIO && FLAGS_SET(flags, XFOPEN_SOCKET)) { + /* ENXIO is what Linux returns if we open a node that is an AF_UNIX socket */ + r = xfopenat_unix_socket(dir_fd, path, bind_name, &f); + if (IN_SET(r, -ENOTSOCK, -EINVAL)) + return -ENXIO; /* propagate original error if this is not a socket after all */ + } + if (r < 0) + return r; + + if (FLAGS_SET(flags, XFOPEN_UNLOCKED)) + (void) __fsetlocking(f, FSETLOCKING_BYCALLER); + *ret = f; return 0; } @@ -1041,11 +1054,10 @@ int fdopen_independent(int fd, const char *mode, FILE **ret) { if (copy_fd < 0) return copy_fd; - f = fdopen(copy_fd, mode); + f = take_fdopen(©_fd, mode); if (!f) return -errno; - TAKE_FD(copy_fd); *ret = TAKE_PTR(f); return 0; } diff --git a/src/basic/fileio.h b/src/basic/fileio.h index 8901a43c41..769bf394fd 100644 --- a/src/basic/fileio.h +++ b/src/basic/fileio.h @@ -43,10 +43,6 @@ typedef enum { READ_FULL_FILE_FAIL_WHEN_LARGER = 1 << 5, /* fail loading if file is larger than specified size */ } ReadFullFileFlags; -int fopen_unlocked_at(int dir_fd, const char *path, const char *options, int flags, FILE **ret); -static inline int fopen_unlocked(const char *path, const char *options, FILE **ret) { - return fopen_unlocked_at(AT_FDCWD, path, options, 0, ret); -} int fdopen_unlocked(int fd, const char *options, FILE **ret); int take_fdopen_unlocked(int *fd, const char *options, FILE **ret); FILE* take_fdopen(int *fd, const char *options); @@ -107,7 +103,29 @@ 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); + +typedef enum XfopenFlags { + XFOPEN_UNLOCKED = 1 << 0, /* call __fsetlocking(FSETLOCKING_BYCALLER) after opened */ + XFOPEN_SOCKET = 1 << 1, /* also try to open unix socket */ +} XfopenFlags; + +int xfopenat_full( + int dir_fd, + const char *path, + const char *mode, + int open_flags, + XfopenFlags flags, + const char *bind_name, + FILE **ret); +static inline int xfopenat(int dir_fd, const char *path, const char *mode, int open_flags, FILE **ret) { + return xfopenat_full(dir_fd, path, mode, open_flags, 0, NULL, ret); +} +static inline int fopen_unlocked_at(int dir_fd, const char *path, const char *mode, int open_flags, FILE **ret) { + return xfopenat_full(dir_fd, path, mode, open_flags, XFOPEN_UNLOCKED, NULL, ret); +} +static inline int fopen_unlocked(const char *path, const char *mode, FILE **ret) { + return fopen_unlocked_at(AT_FDCWD, path, mode, 0, ret); +} int fdopen_independent(int fd, const char *mode, FILE **ret); |