/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * io_stdin.c * * Copyright (C) 2019 David Oberhollenzer */ #include "common.h" #include #include #include typedef struct { sqfs_file_t base; const sparse_map_t *map; sqfs_u64 offset; sqfs_u64 real_size; sqfs_u64 apparent_size; FILE *fp; } sqfs_file_stdinout_t; static void stdinout_destroy(sqfs_object_t *base) { free(base); } static sqfs_u64 stdinout_get_size(const sqfs_file_t *base) { return ((const sqfs_file_stdinout_t *)base)->apparent_size; } static int stdinout_truncate(sqfs_file_t *base, sqfs_u64 size) { (void)base; (void)size; return SQFS_ERROR_IO; } static int stdin_read_at(sqfs_file_t *base, sqfs_u64 offset, void *buffer, size_t size) { sqfs_file_stdinout_t *file = (sqfs_file_stdinout_t *)base; size_t temp_size = 0; sqfs_u8 *temp = NULL; sqfs_u64 diff; size_t ret; if (offset < file->offset) return SQFS_ERROR_IO; if (offset > file->offset) { temp_size = 1024; temp = alloca(temp_size); } if (offset >= file->real_size || (offset + size) > file->real_size) return SQFS_ERROR_OUT_OF_BOUNDS; while (size > 0) { if (ferror(file->fp)) return SQFS_ERROR_IO; if (feof(file->fp)) return SQFS_ERROR_OUT_OF_BOUNDS; if (offset > file->offset) { diff = file->offset - offset; diff = diff > (sqfs_u64)temp_size ? temp_size : diff; ret = fread(temp, 1, diff, file->fp); } else { ret = fread(buffer, 1, size, file->fp); } if (offset <= file->offset) { buffer = (char *)buffer + ret; size -= ret; offset += ret; } file->offset += ret; } return 0; } static int stdin_read_condensed(sqfs_file_t *base, sqfs_u64 offset, void *buffer, size_t size) { sqfs_file_stdinout_t *file = (sqfs_file_stdinout_t *)base; sqfs_u64 poffset = 0, src_start; size_t dst_start, diff, count; const sparse_map_t *it; int err; memset(buffer, 0, size); for (it = file->map; it != NULL; it = it->next) { if (it->offset + it->count <= offset) { poffset += it->count; continue; } if (it->offset >= offset + size) { poffset += it->count; continue; } count = size; if (offset + count >= it->offset + it->count) count = it->offset + it->count - offset; if (it->offset < offset) { diff = offset - it->offset; src_start = poffset + diff; dst_start = 0; count -= diff; } else if (it->offset > offset) { diff = it->offset - offset; src_start = poffset; dst_start = diff; } else { src_start = poffset; dst_start = 0; } err = stdin_read_at(base, src_start, (char *)buffer + dst_start, count); if (err) return err; poffset += it->count; } return 0; } static int stdin_write_at(sqfs_file_t *base, sqfs_u64 offset, const void *buffer, size_t size) { (void)base; (void)offset; (void)buffer; (void)size; return SQFS_ERROR_IO; } sqfs_file_t *sqfs_get_stdin_file(FILE *fp, const sparse_map_t *map, sqfs_u64 size) { sqfs_file_stdinout_t *file = calloc(1, sizeof(*file)); sqfs_file_t *base = (sqfs_file_t *)file; const sparse_map_t *it; if (file == NULL) return NULL; file->apparent_size = size; file->map = map; file->fp = fp; if (map != NULL) { for (it = map; it != NULL; it = it->next) file->real_size += map->count; } else { file->real_size = size; } ((sqfs_object_t *)base)->destroy = stdinout_destroy; base->write_at = stdin_write_at; base->get_size = stdinout_get_size; base->truncate = stdinout_truncate; if (map == NULL) { base->read_at = stdin_read_at; } else { base->read_at = stdin_read_condensed; } return base; }