diff options
Diffstat (limited to 'lib/fstream')
| -rw-r--r-- | lib/fstream/Makemodule.am | 17 | ||||
| -rw-r--r-- | lib/fstream/internal.h | 25 | ||||
| -rw-r--r-- | lib/fstream/istream.c | 91 | ||||
| -rw-r--r-- | lib/fstream/ostream.c | 84 | ||||
| -rw-r--r-- | lib/fstream/printf.c | 30 | ||||
| -rw-r--r-- | lib/fstream/unix/istream.c | 124 | ||||
| -rw-r--r-- | lib/fstream/unix/ostream.c | 152 | ||||
| -rw-r--r-- | lib/fstream/win32/istream.c | 120 | ||||
| -rw-r--r-- | lib/fstream/win32/ostream.c | 189 | 
9 files changed, 832 insertions, 0 deletions
| 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 <goliath@infraroot.at> + */ +#ifndef INTERNAL_H +#define INTERNAL_H + +#include "config.h" +#include "compat.h" +#include "fstream.h" + +#include <string.h> +#include <stdlib.h> +#include <stdarg.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <stdio.h> + +#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 <goliath@infraroot.at> + */ +#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 <goliath@infraroot.at> + */ +#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 <goliath@infraroot.at> + */ +#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 <goliath@infraroot.at> + */ +#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 <goliath@infraroot.at> + */ +#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 <goliath@infraroot.at> + */ +#include "../internal.h" + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> + +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 <goliath@infraroot.at> + */ +#include "../internal.h" + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> + +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; +} | 
