1
0
mirror of https://github.com/systemd/systemd.git synced 2025-02-22 09:57:34 +03:00

Merge pull request #27184 from yuwata/xfopenat-full

fileio: introduce xfopenat_full()
This commit is contained in:
Daan De Meyer 2023-04-10 11:50:34 +02:00 committed by GitHub
commit 25d598e144
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 127 additions and 97 deletions

View File

@ -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;
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;
/* 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;
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,32 +925,110 @@ 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);
return -errno;
}
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(&copy_fd, mode);
if (!f)
return -errno;
TAKE_FD(copy_fd);
*ret = TAKE_PTR(f);
return 0;
}

View File

@ -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);