From 1e255d0f6c472bb3c710aea1ea8dc5d27c0fba4a Mon Sep 17 00:00:00 2001 From: David Oberhollenzer Date: Fri, 4 Sep 2020 18:26:16 +0200 Subject: Add stream I/O abstraction library Signed-off-by: David Oberhollenzer --- Makefile.am | 1 + include/compat.h | 2 + include/fstream.h | 252 ++++++++++++++++++++++++++++++++++++++++++++ lib/compat/Makemodule.am | 1 + lib/compat/w32_perror.c | 31 ++++++ lib/fstream/Makemodule.am | 17 +++ lib/fstream/internal.h | 25 +++++ lib/fstream/istream.c | 91 ++++++++++++++++ lib/fstream/ostream.c | 84 +++++++++++++++ lib/fstream/printf.c | 30 ++++++ lib/fstream/unix/istream.c | 124 ++++++++++++++++++++++ lib/fstream/unix/ostream.c | 152 ++++++++++++++++++++++++++ lib/fstream/win32/istream.c | 120 +++++++++++++++++++++ lib/fstream/win32/ostream.c | 189 +++++++++++++++++++++++++++++++++ 14 files changed, 1119 insertions(+) create mode 100644 include/fstream.h create mode 100644 lib/compat/w32_perror.c create mode 100644 lib/fstream/Makemodule.am create mode 100644 lib/fstream/internal.h create mode 100644 lib/fstream/istream.c create mode 100644 lib/fstream/ostream.c create mode 100644 lib/fstream/printf.c create mode 100644 lib/fstream/unix/istream.c create mode 100644 lib/fstream/unix/ostream.c create mode 100644 lib/fstream/win32/istream.c create mode 100644 lib/fstream/win32/ostream.c diff --git a/Makefile.am b/Makefile.am index 8492596..d21a3ba 100644 --- a/Makefile.am +++ b/Makefile.am @@ -26,6 +26,7 @@ include lib/lz4/Makemodule.am include lib/sqfs/Makemodule.am if BUILD_TOOLS +include lib/fstream/Makemodule.am include lib/fstree/Makemodule.am include lib/common/Makemodule.am include lib/tar/Makemodule.am diff --git a/include/compat.h b/include/compat.h index ee47ef9..343dcf5 100644 --- a/include/compat.h +++ b/include/compat.h @@ -163,6 +163,8 @@ int fchownat(int dirfd, const char *path, int uid, int gid, int flags); int fchmodat(int dirfd, const char *path, int mode, int flags); int chdir(const char *path); + +void w32_perror(const char *str); #else #include #include diff --git a/include/fstream.h b/include/fstream.h new file mode 100644 index 0000000..cc562ca --- /dev/null +++ b/include/fstream.h @@ -0,0 +1,252 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * fstream.h + * + * Copyright (C) 2019 David Oberhollenzer + */ +#ifndef FSTREAM_H +#define FSTREAM_H + +#include "sqfs/predef.h" + +/** + * @struct ostream_t + * + * @extends sqfs_object_t + * + * @brief An append-only data stream. + */ +typedef struct ostream_t { + sqfs_object_t base; + + int (*append)(struct ostream_t *strm, const void *data, size_t size); + + int (*append_sparse)(struct ostream_t *strm, size_t size); + + int (*flush)(struct ostream_t *strm); + + const char *(*get_filename)(struct ostream_t *strm); +} ostream_t; + +/** + * @struct istream_t + * + * @extends sqfs_object_t + * + * @brief A sequential, read-only data stream. + */ +typedef struct istream_t { + sqfs_object_t base; + + size_t buffer_used; + size_t buffer_offset; + bool eof; + + sqfs_u8 *buffer; + + int (*precache)(struct istream_t *strm); + + const char *(*get_filename)(struct istream_t *strm); +} istream_t; + + +enum { + OSTREAM_OPEN_OVERWRITE = 0x01, + OSTREAM_OPEN_SPARSE = 0x02, +}; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Create an output stream that writes to a file. + * + * @memberof ostream_t + * + * If the file does not yet exist, it is created. If it does exist this + * function fails, unless the flag OSTREAM_OPEN_OVERWRITE is set, in which + * case the file is opened and its contents are discarded. + * + * If the flag OSTREAM_OPEN_SPARSE is set, the underlying implementation tries + * to support sparse output files. If the flag is not set, holes will always + * be filled with zero bytes. + * + * @param path A path to the file to open or create. + * @param flags A combination of flags controling how to open/create the file. + * + * @return A pointer to an output stream on success, NULL on failure. + */ +SQFS_INTERNAL ostream_t *ostream_open_file(const char *path, int flags); + +/** + * @brief Create an output stream that writes to standard output. + * + * @memberof ostream_t + * + * @return A pointer to an output stream on success, NULL on failure. + */ +SQFS_INTERNAL ostream_t *ostream_open_stdout(void); + +/** + * @brief Create an input stream that reads from a file. + * + * @memberof istream_t + * + * @param path A path to the file to open or create. + * + * @return A pointer to an output stream on success, NULL on failure. + */ +SQFS_INTERNAL istream_t *istream_open_file(const char *path); + +/** + * @brief Create an input stream that reads from standard input. + * + * @memberof istream_t + * + * @return A pointer to an input stream on success, NULL on failure. + */ +SQFS_INTERNAL istream_t *istream_open_stdin(void); + +/** + * @brief Append a block of data to an output stream. + * + * @memberof ostream_t + * + * @param strm A pointer to an output stream. + * @param data A pointer to the data block to append. + * @param size The number of bytes to append. + * + * @return Zero on success, -1 on failure. + */ +SQFS_INTERNAL int ostream_append(ostream_t *strm, const void *data, + size_t size); + +/** + * @brief Append a number of zero bytes to an output stream. + * + * @memberof ostream_t + * + * If the unerlying implementation supports sparse files, this function can be + * used to create a "hole". If the implementation does not support it, a + * fallback is used that just appends a block of zeros manualy. + * + * @param strm A pointer to an output stream. + * @param size The number of zero bytes to append. + * + * @return Zero on success, -1 on failure. + */ +SQFS_INTERNAL int ostream_append_sparse(ostream_t *strm, size_t size); + +/** + * @brief Process all pending, buffered data and flush it to disk. + * + * @memberof ostream_t + * + * If the stream performs some kind of transformation (e.g. transparent data + * compression), flushing caues the wrapped format to insert a termination + * token. Only call this function when you are absolutely DONE appending data, + * shortly before destroying the stream. + * + * @param strm A pointer to an output stream. + * + * @return Zero on success, -1 on failure. + */ +SQFS_INTERNAL int ostream_flush(ostream_t *strm); + +/** + * @brief Get the underlying filename of a output stream. + * + * @memberof ostream_t + * + * @param strm The output stream to get the filename from. + * + * @return A string holding the underlying filename. + */ +SQFS_INTERNAL const char *ostream_get_filename(ostream_t *strm); + +/** + * @brief Printf like function that appends to an output stream + * + * @memberof ostream_t + * + * @param strm The output stream to append to. + * @param fmt A printf style format string. + * + * @return The number of characters written on success, -1 on failure. + */ +SQFS_INTERNAL int ostream_printf(ostream_t *strm, const char *fmt, ...); + +/** + * @brief Read data from an input stream + * + * @memberof istream_t + * + * @param strm A pointer to an input stream. + * @param data A buffer to read into. + * @param size The number of bytes to read into the buffer. + * + * @return The number of bytes actually read on success, -1 on failure, + * 0 on end-of-file. + */ +SQFS_INTERNAL sqfs_s32 istream_read(istream_t *strm, void *data, size_t size); + +/** + * @brief Adjust and refill the internal buffer of an input stream + * + * @memberof istream_t + * + * This function resets the buffer offset of an input stream (moving any unread + * data up front if it has to) and calls an internal callback of the input + * stream to fill the rest of the buffer to the extent possible. + * + * @param strm A pointer to an input stream. + * + * @return 0 on success, -1 on failure. + */ +SQFS_INTERNAL int istream_precache(istream_t *strm); + +/** + * @brief Get the underlying filename of an input stream. + * + * @memberof istream_t + * + * @param strm The input stream to get the filename from. + * + * @return A string holding the underlying filename. + */ +SQFS_INTERNAL const char *istream_get_filename(istream_t *strm); + +/** + * @brief Skip over a number of bytes in an input stream. + * + * @memberof istream_t + * + * @param strm A pointer to an input stream. + * @param size The number of bytes to seek forward. + * + * @return Zero on success, -1 on failure. + */ +SQFS_INTERNAL int istream_skip(istream_t *strm, sqfs_u64 size); + +/** + * @brief Read data from an input stream and append it to an output stream + * + * @memberof ostream_t + * + * @param out A pointer to an output stream to append to. + * @param in A pointer to an input stream to read from. + * @param size The number of bytes to copy over. + * + * @return The number of bytes copied on success, -1 on failure, + * 0 on end-of-file. + */ +SQFS_INTERNAL sqfs_s32 ostream_append_from_istream(ostream_t *out, + istream_t *in, + sqfs_u32 size); + +#ifdef __cplusplus +} +#endif + +#endif /* FSTREAM_H */ diff --git a/lib/compat/Makemodule.am b/lib/compat/Makemodule.am index 7137f2c..c1be522 100644 --- a/lib/compat/Makemodule.am +++ b/lib/compat/Makemodule.am @@ -2,5 +2,6 @@ libcompat_a_SOURCES = lib/compat/getline.c lib/compat/getsubopt.c libcompat_a_SOURCES += lib/compat/strndup.c lib/compat/mockups.c libcompat_a_SOURCES += lib/compat/chdir.c include/compat.h libcompat_a_SOURCES += lib/compat/path_to_windows.c +libcompat_a_SOURCES += lib/compat/w32_perror.c noinst_LIBRARIES += libcompat.a diff --git a/lib/compat/w32_perror.c b/lib/compat/w32_perror.c new file mode 100644 index 0000000..1268501 --- /dev/null +++ b/lib/compat/w32_perror.c @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * w32_perror.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "config.h" +#include "compat.h" + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include + +void w32_perror(const char *str) +{ + DWORD nStatus = GetLastError(); + LPVOID msg = NULL; + + FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, nStatus, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR)&msg, 0, NULL); + + fprintf(stderr, "%s: %s\n", str, (const char *)msg); + + if (msg != NULL) + LocalFree(msg); +} +#endif diff --git a/lib/fstream/Makemodule.am b/lib/fstream/Makemodule.am new file mode 100644 index 0000000..27e4701 --- /dev/null +++ b/lib/fstream/Makemodule.am @@ -0,0 +1,17 @@ +libfstream_a_SOURCES = include/fstream.h +libfstream_a_SOURCES += lib/fstream/internal.h +libfstream_a_SOURCES += lib/fstream/ostream.c lib/fstream/printf.c +libfstream_a_SOURCES += lib/fstream/istream.c +libfstream_a_CFLAGS = $(AM_CFLAGS) $(ZLIB_CFLAGS) $(XZ_CFLAGS) +libfstream_a_CPPFLAGS = $(AM_CPPFLAGS) + +if WINDOWS +libfstream_a_SOURCES += lib/fstream/win32/ostream.c +libfstream_a_SOURCES += lib/fstream/win32/istream.c +libfstream_a_CFLAGS += -DWINVER=0x0600 -D_WIN32_WINNT=0x0600 +else +libfstream_a_SOURCES += lib/fstream/unix/ostream.c +libfstream_a_SOURCES += lib/fstream/unix/istream.c +endif + +noinst_LIBRARIES += libfstream.a diff --git a/lib/fstream/internal.h b/lib/fstream/internal.h new file mode 100644 index 0000000..e8b0c07 --- /dev/null +++ b/lib/fstream/internal.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * internal.h + * + * Copyright (C) 2019 David Oberhollenzer + */ +#ifndef INTERNAL_H +#define INTERNAL_H + +#include "config.h" +#include "compat.h" +#include "fstream.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#define BUFSZ (262144) + +#endif /* INTERNAL_H */ diff --git a/lib/fstream/istream.c b/lib/fstream/istream.c new file mode 100644 index 0000000..6318a23 --- /dev/null +++ b/lib/fstream/istream.c @@ -0,0 +1,91 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * istream.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "internal.h" + + +sqfs_s32 istream_read(istream_t *strm, void *data, size_t size) +{ + sqfs_s32 total = 0; + size_t diff; + + if (size > 0x7FFFFFFF) + size = 0x7FFFFFFF; + + while (size > 0) { + if (strm->buffer_offset >= strm->buffer_used) { + if (istream_precache(strm)) + return -1; + + if (strm->buffer_used == 0) + break; + } + + diff = strm->buffer_used - strm->buffer_offset; + if (diff > size) + diff = size; + + memcpy(data, strm->buffer + strm->buffer_offset, diff); + data = (char *)data + diff; + strm->buffer_offset += diff; + size -= diff; + total += diff; + } + + return total; +} + +int istream_precache(istream_t *strm) +{ + if (strm->buffer_offset >= strm->buffer_used) { + strm->buffer_offset = 0; + strm->buffer_used = 0; + } else if (strm->buffer_offset > 0) { + memmove(strm->buffer, + strm->buffer + strm->buffer_offset, + strm->buffer_used - strm->buffer_offset); + + strm->buffer_used -= strm->buffer_offset; + strm->buffer_offset = 0; + } + + if (strm->eof) + return 0; + + return strm->precache(strm); +} + +const char *istream_get_filename(istream_t *strm) +{ + return strm->get_filename(strm); +} + +int istream_skip(istream_t *strm, sqfs_u64 size) +{ + size_t diff; + + while (size > 0) { + if (strm->buffer_offset >= strm->buffer_used) { + if (istream_precache(strm)) + return -1; + + if (strm->buffer_used == 0) { + fprintf(stderr, "%s: unexpected end-of-file\n", + strm->get_filename(strm)); + return -1; + } + } + + diff = strm->buffer_used - strm->buffer_offset; + if ((sqfs_u64)diff > size) + diff = size; + + strm->buffer_offset += diff; + size -= diff; + } + + return 0; +} diff --git a/lib/fstream/ostream.c b/lib/fstream/ostream.c new file mode 100644 index 0000000..afe76e8 --- /dev/null +++ b/lib/fstream/ostream.c @@ -0,0 +1,84 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * ostream.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "internal.h" + + +static int append_sparse_fallback(ostream_t *strm, size_t size) +{ + char buffer[512]; + size_t diff; + + memset(buffer, 0, sizeof(buffer)); + + while (size > 0) { + diff = size < sizeof(buffer) ? size : sizeof(buffer); + + if (strm->append(strm, buffer, diff)) + return -1; + + size -= diff; + } + + return 0; +} + + +int ostream_append(ostream_t *strm, const void *data, size_t size) +{ + return strm->append(strm, data, size); +} + +int ostream_append_sparse(ostream_t *strm, size_t size) +{ + if (strm->append_sparse == NULL) + return append_sparse_fallback(strm, size); + + return strm->append_sparse(strm, size); +} + +int ostream_flush(ostream_t *strm) +{ + return strm->flush(strm); +} + +const char *ostream_get_filename(ostream_t *strm) +{ + return strm->get_filename(strm); +} + +sqfs_s32 ostream_append_from_istream(ostream_t *out, istream_t *in, + sqfs_u32 size) +{ + sqfs_s32 total = 0; + size_t diff; + + if (size > 0x7FFFFFFF) + size = 0x7FFFFFFF; + + while (size > 0) { + if (in->buffer_offset >= in->buffer_used) { + if (istream_precache(in)) + return -1; + + if (in->buffer_used == 0) + break; + } + + diff = in->buffer_used - in->buffer_offset; + if (diff > size) + diff = size; + + if (out->append(out, in->buffer + in->buffer_offset, diff)) + return -1; + + in->buffer_offset += diff; + size -= diff; + total += diff; + } + + return total; +} diff --git a/lib/fstream/printf.c b/lib/fstream/printf.c new file mode 100644 index 0000000..3850487 --- /dev/null +++ b/lib/fstream/printf.c @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * printf.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "internal.h" + +int ostream_printf(ostream_t *strm, const char *fmt, ...) +{ + char *temp = NULL; + va_list ap; + int ret; + + va_start(ap, fmt); + + ret = vasprintf(&temp, fmt, ap); + if (ret < 0) + perror(strm->get_filename(strm)); + va_end(ap); + + if (ret < 0) + return -1; + + if (strm->append(strm, temp, ret)) + ret = -1; + + free(temp); + return ret; +} diff --git a/lib/fstream/unix/istream.c b/lib/fstream/unix/istream.c new file mode 100644 index 0000000..5898141 --- /dev/null +++ b/lib/fstream/unix/istream.c @@ -0,0 +1,124 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * istream.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "../internal.h" + +typedef struct { + istream_t base; + char *path; + int fd; + bool eof; + + sqfs_u8 buffer[BUFSZ]; +} file_istream_t; + +static int file_precache(istream_t *strm) +{ + file_istream_t *file = (file_istream_t *)strm; + ssize_t ret; + size_t diff; + + while (strm->buffer_used < sizeof(file->buffer)) { + diff = sizeof(file->buffer) - strm->buffer_used; + + ret = read(file->fd, strm->buffer + strm->buffer_used, diff); + + if (ret == 0) { + file->eof = true; + break; + } + + if (ret < 0) { + if (errno == EINTR) + continue; + + perror(file->path); + return -1; + } + + strm->buffer_used += ret; + } + + return 0; +} + +static const char *file_get_filename(istream_t *strm) +{ + file_istream_t *file = (file_istream_t *)strm; + + return file->path; +} + +static void file_destroy(sqfs_object_t *obj) +{ + file_istream_t *file = (file_istream_t *)obj; + + if (file->fd != STDIN_FILENO) + close(file->fd); + + free(file->path); + free(file); +} + +istream_t *istream_open_file(const char *path) +{ + file_istream_t *file = calloc(1, sizeof(*file)); + sqfs_object_t *obj = (sqfs_object_t *)file; + istream_t *strm = (istream_t *)file; + + if (file == NULL) { + perror(path); + return NULL; + } + + file->path = strdup(path); + if (file->path == NULL) { + perror(path); + goto fail_free; + } + + file->fd = open(path, O_RDONLY); + if (file->fd < 0) { + perror(path); + goto fail_path; + } + + strm->buffer = file->buffer; + strm->precache = file_precache; + strm->get_filename = file_get_filename; + obj->destroy = file_destroy; + return strm; +fail_path: + free(file->path); +fail_free: + free(file); + return NULL; +} + +istream_t *istream_open_stdin(void) +{ + file_istream_t *file = calloc(1, sizeof(*file)); + sqfs_object_t *obj = (sqfs_object_t *)file; + istream_t *strm = (istream_t *)file; + + if (file == NULL) + goto fail; + + file->path = strdup("stdin"); + if (file->path == NULL) + goto fail; + + file->fd = STDIN_FILENO; + strm->buffer = file->buffer; + strm->precache = file_precache; + strm->get_filename = file_get_filename; + obj->destroy = file_destroy; + return strm; +fail: + perror("creating file wrapper for stdin"); + free(file); + return NULL; +} diff --git a/lib/fstream/unix/ostream.c b/lib/fstream/unix/ostream.c new file mode 100644 index 0000000..dac675f --- /dev/null +++ b/lib/fstream/unix/ostream.c @@ -0,0 +1,152 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * ostream.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "../internal.h" + +typedef struct { + ostream_t base; + char *path; + int fd; +} file_ostream_t; + +static int file_append(ostream_t *strm, const void *data, size_t size) +{ + file_ostream_t *file = (file_ostream_t *)strm; + ssize_t ret; + + while (size > 0) { + ret = write(file->fd, data, size); + + if (ret == 0) { + fprintf(stderr, "%s: truncated data write.\n", + file->path); + return -1; + } + + if (ret < 0) { + if (errno == EINTR) + continue; + + perror(file->path); + return -1; + } + + size -= ret; + data = (const char *)data + ret; + } + + return 0; +} + +static int file_append_sparse(ostream_t *strm, size_t size) +{ + file_ostream_t *file = (file_ostream_t *)strm; + + if (lseek(file->fd, size, SEEK_CUR) == (off_t)-1) { + perror(file->path); + return -1; + } + + return 0; +} + +static int file_flush(ostream_t *strm) +{ + file_ostream_t *file = (file_ostream_t *)strm; + + if (fsync(file->fd) != 0) { + perror(file->path); + return -1; + } + + return 0; +} + +static void file_destroy(sqfs_object_t *obj) +{ + file_ostream_t *file = (file_ostream_t *)obj; + + if (file->fd != STDOUT_FILENO) + close(file->fd); + + free(file->path); + free(file); +} + +static const char *file_get_filename(ostream_t *strm) +{ + file_ostream_t *file = (file_ostream_t *)strm; + + return file->path; +} + +ostream_t *ostream_open_file(const char *path, int flags) +{ + file_ostream_t *file = calloc(1, sizeof(*file)); + sqfs_object_t *obj = (sqfs_object_t *)file; + ostream_t *strm = (ostream_t *)file; + + if (file == NULL) { + perror(path); + return NULL; + } + + file->path = strdup(path); + if (file->path == NULL) { + perror(path); + goto fail_free; + } + + if (flags & OSTREAM_OPEN_OVERWRITE) { + file->fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644); + } else { + file->fd = open(path, O_WRONLY | O_CREAT | O_EXCL, 0644); + } + + if (file->fd < 0) { + perror(path); + goto fail_path; + } + + if (flags & OSTREAM_OPEN_SPARSE) + strm->append_sparse = file_append_sparse; + + strm->append = file_append; + strm->flush = file_flush; + strm->get_filename = file_get_filename; + obj->destroy = file_destroy; + return strm; +fail_path: + free(file->path); +fail_free: + free(file); + return NULL; +} + +ostream_t *ostream_open_stdout(void) +{ + file_ostream_t *file = calloc(1, sizeof(*file)); + sqfs_object_t *obj = (sqfs_object_t *)file; + ostream_t *strm = (ostream_t *)file; + + if (file == NULL) + goto fail; + + file->path = strdup("stdout"); + if (file->path == NULL) + goto fail; + + file->fd = STDOUT_FILENO; + strm->append = file_append; + strm->flush = file_flush; + strm->get_filename = file_get_filename; + obj->destroy = file_destroy; + return strm; +fail: + perror("creating file wrapper for stdout"); + free(file); + return NULL; +} diff --git a/lib/fstream/win32/istream.c b/lib/fstream/win32/istream.c new file mode 100644 index 0000000..96c9f1c --- /dev/null +++ b/lib/fstream/win32/istream.c @@ -0,0 +1,120 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * istream.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "../internal.h" + +#define WIN32_LEAN_AND_MEAN +#include + +typedef struct { + istream_t base; + char *path; + HANDLE hnd; + + sqfs_u8 buffer[BUFSZ]; +} file_istream_t; + +static int file_precache(istream_t *strm) +{ + file_istream_t *file = (file_istream_t *)strm; + DWORD diff, actual; + HANDLE hnd; + + hnd = file->path == NULL ? GetStdHandle(STD_INPUT_HANDLE) : file->hnd; + + while (strm->buffer_used < sizeof(file->buffer)) { + diff = sizeof(file->buffer) - strm->buffer_used; + + if (!ReadFile(hnd, strm->buffer + strm->buffer_used, + diff, &actual, NULL)) { + w32_perror(file->path == NULL ? "stdin" : file->path); + return -1; + } + + if (actual == 0) { + strm->eof = true; + break; + } + + strm->buffer_used += actual; + } + + return 0; +} + +static const char *file_get_filename(istream_t *strm) +{ + file_istream_t *file = (file_istream_t *)strm; + + return file->path == NULL ? "stdin" : file->path; +} + +static void file_destroy(sqfs_object_t *obj) +{ + file_istream_t *file = (file_istream_t *)obj; + + if (file->path != NULL) { + CloseHandle(file->hnd); + free(file->path); + } + + free(file); +} + +istream_t *istream_open_file(const char *path) +{ + file_istream_t *file = calloc(1, sizeof(*file)); + sqfs_object_t *obj = (sqfs_object_t *)file; + istream_t *strm = (istream_t *)file; + + if (file == NULL) { + perror(path); + return NULL; + } + + file->path = strdup(path); + if (file->path == NULL) { + perror(path); + goto fail_free; + } + + file->hnd = CreateFile(path, GENERIC_READ, FILE_SHARE_READ, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + + if (file->hnd == INVALID_HANDLE_VALUE) { + perror(path); + goto fail_path; + } + + strm->buffer = file->buffer; + strm->precache = file_precache; + strm->get_filename = file_get_filename; + obj->destroy = file_destroy; + return strm; +fail_path: + free(file->path); +fail_free: + free(file); + return NULL; +} + +istream_t *istream_open_stdin(void) +{ + file_istream_t *file = calloc(1, sizeof(*file)); + sqfs_object_t *obj = (sqfs_object_t *)file; + istream_t *strm = (istream_t *)file; + + if (file == NULL) { + perror("stdin"); + return NULL; + } + + strm->buffer = file->buffer; + strm->precache = file_precache; + strm->get_filename = file_get_filename; + obj->destroy = file_destroy; + return strm; +} diff --git a/lib/fstream/win32/ostream.c b/lib/fstream/win32/ostream.c new file mode 100644 index 0000000..e593f7e --- /dev/null +++ b/lib/fstream/win32/ostream.c @@ -0,0 +1,189 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * ostream.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "../internal.h" + +#define WIN32_LEAN_AND_MEAN +#include + +typedef struct { + ostream_t base; + char *path; + HANDLE hnd; +} file_ostream_t; + +static int w32_append(HANDLE hnd, const char *filename, + const void *data, size_t size) +{ + DWORD diff; + + while (size > 0) { + if (!WriteFile(hnd, data, size, &diff, NULL)) { + w32_perror(filename); + return -1; + } + + size -= diff; + data = (const char *)data + diff; + } + + return 0; +} + +static int w32_flush(HANDLE hnd, const char *filename) +{ + if (FlushFileBuffers(hnd) != 0) { + w32_perror(filename); + return -1; + } + + return 0; +} + +/*****************************************************************************/ + +static int file_append(ostream_t *strm, const void *data, size_t size) +{ + file_ostream_t *file = (file_ostream_t *)strm; + + return w32_append(file->hnd, file->path, data, size); +} + +static int file_append_sparse(ostream_t *strm, size_t size) +{ + file_ostream_t *file = (file_ostream_t *)strm; + LARGE_INTEGER pos; + + pos.QuadPart = size; + + if (!SetFilePointerEx(file->hnd, pos, NULL, FILE_CURRENT)) + goto fail; + + if (!SetEndOfFile(file->hnd)) + goto fail; + + return 0; +fail: + w32_perror(file->path); + return -1; +} + +static int file_flush(ostream_t *strm) +{ + file_ostream_t *file = (file_ostream_t *)strm; + + return w32_flush(file->hnd, file->path); +} + +static void file_destroy(sqfs_object_t *obj) +{ + file_ostream_t *file = (file_ostream_t *)obj; + + CloseHandle(file->hnd); + free(file->path); + free(file); +} + +static const char *file_get_filename(ostream_t *strm) +{ + file_ostream_t *file = (file_ostream_t *)strm; + + return file->path; +} + +/*****************************************************************************/ + +static int stdout_append(ostream_t *strm, const void *data, size_t size) +{ + (void)strm; + return w32_append(GetStdHandle(STD_OUTPUT_HANDLE), "stdout", + data, size); +} + +static int stdout_flush(ostream_t *strm) +{ + (void)strm; + return w32_flush(GetStdHandle(STD_OUTPUT_HANDLE), "stdout"); +} + +static void stdout_destroy(sqfs_object_t *obj) +{ + free(obj); +} + +static const char *stdout_get_filename(ostream_t *strm) +{ + (void)strm; + return "stdout"; +} + +/*****************************************************************************/ + +ostream_t *ostream_open_file(const char *path, int flags) +{ + file_ostream_t *file = calloc(1, sizeof(*file)); + sqfs_object_t *obj = (sqfs_object_t *)file; + ostream_t *strm = (ostream_t *)file; + int access_flags, creation_mode; + + if (file == NULL) { + perror(path); + return NULL; + } + + file->path = strdup(path); + if (file->path == NULL) { + perror(path); + goto fail_free; + } + + access_flags = GENERIC_WRITE; + + if (flags & OSTREAM_OPEN_OVERWRITE) { + creation_mode = CREATE_ALWAYS; + } else { + creation_mode = CREATE_NEW; + } + + file->hnd = CreateFile(path, access_flags, 0, NULL, creation_mode, + FILE_ATTRIBUTE_NORMAL, NULL); + + if (file->hnd == INVALID_HANDLE_VALUE) { + w32_perror(path); + goto fail_path; + } + + if (flags & OSTREAM_OPEN_SPARSE) + strm->append_sparse = file_append_sparse; + + strm->append = file_append; + strm->flush = file_flush; + strm->get_filename = file_get_filename; + obj->destroy = file_destroy; + return strm; +fail_path: + free(file->path); +fail_free: + free(file); + return NULL; +} + +ostream_t *ostream_open_stdout(void) +{ + ostream_t *strm = calloc(1, sizeof(strm)); + sqfs_object_t *obj = (sqfs_object_t *)strm; + + if (strm == NULL) { + perror("creating stdout file wrapper"); + return NULL; + } + + strm->append = stdout_append; + strm->flush = stdout_flush; + strm->get_filename = stdout_get_filename; + obj->destroy = stdout_destroy; + return strm; +} -- cgit v1.2.3