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 --- 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 ++++++++++++++++++++++++++++++++++++++++++++ 11 files changed, 864 insertions(+) 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 (limited to 'lib') 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