diff --git a/Makefile b/Makefile index ecce9bc..ecc7922 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ TARGET = umd_livepatch C_OBJS = patch.o io_funcs.o main.o \ - stdio_glue.o umdiff/file.o + stdio_glue.o umdiff/file.o umdiff/patch.o OBJS = $(C_OBJS) imports.o all: $(TARGET).prx INCDIR = $(ARKROOT)/common/include $(ARKROOT)/core/systemctrl/include $(LIBRSYNC)/src diff --git a/umdiff/Makefile b/umdiff/Makefile index b7a0800..3bfa8c2 100644 --- a/umdiff/Makefile +++ b/umdiff/Makefile @@ -1,5 +1,5 @@ TARGET = umdiff -C_OBJS = main.c.o compare.c.o file.c.o patch.c.o +C_OBJS = main.c.o rdiff.c.o compare.c.o file.c.o patch.c.o OBJS = $(C_OBJS) CMAKE := cmake diff --git a/umdiff/file.c b/umdiff/file.c index 37ff0c6..392d2e9 100644 --- a/umdiff/file.c +++ b/umdiff/file.c @@ -6,6 +6,8 @@ * * The functions defined here control reading and writing UMDiff files from * in-memory structure @ref umdiff_File. + * + * @warning This file is reused by umd_livepatch. Do not allocate on the heap! */ #include "umdiff.h" @@ -28,7 +30,7 @@ _impl_umdiff_Header_rectify(umdiff_Header *hdr) long len_preamble = sizeof(umdiff_Header) + (sizeof(umdiff_Command) * hdr->cmd_count); - strncpy(hdr->magic, umdiff_File_$MAGIC, 7); + memcpy(hdr->magic, umdiff_File_$MAGIC, 7); hdr->version = umdiff_File_$VERSION; hdr->data_start = _impl_umdiff_alignSector$(len_preamble); } diff --git a/umdiff/patch.c b/umdiff/patch.c index e69de29..0037148 100644 --- a/umdiff/patch.c +++ b/umdiff/patch.c @@ -0,0 +1,82 @@ +/** + * @file patch.c + * @author Karim Vergnes + * @copyright GPLv2 + * @brief UMDiff patch algorithms + * + * Functions to redirect read requests and reconstruct the result file given + * a set of UMDiff commands. + * + * @warning This file is reused by umd_livepatch. Do not allocate on the heap! + */ + +#include "umdiff.h" + +static long +_impl_umdiff_Command_read(umdiff_Command cmd, void *dest, long count, long offset, + umdiff_ReadCallback read_source, + umdiff_ReadCallback read_patchFile) +{ + long rel_offset = offset - (cmd.sector_start * ISO_SECTOR_SIZE); + + //TODO: implement read system + // 1. perform first truncated read + // 2. if repeat-length remains, perform second full read + // 3. if repeat-length still remains, memcpy second read range onwards + // -> saved a bunch of i/o calls at the guest ptr's expense! +} + +static umdiff_Command * +_impl_umdiff_File_readIndexCmds(umdiff_File *file, long offset_sector, + umdiff_ReadCallback read_patchFile) +{ + int res; + int index = offset_sector / 1024; + + // stateful optimization: don't re-read the same commands + if (index == file->last_index) + return 0; + + if (file->mode == umdiff_FileFlags_HEADER_ONLY) { + res = read_patchFile(file->commands, sizeof(umdiff_Command) * 1024, + file->hdr.index[index]); + return file->commands; + } else { + return file->commands + file->hdr.index[index] - umdiff_File_$COMMANDS_START; + } + + file->last_index = index; +} + +int +umdiff_File_readPatched(umdiff_File *file, void *dest, long count, long offset, + umdiff_ReadCallback read_source, + umdiff_ReadCallback read_patchFile) +{ + long res; + umdiff_Command *commands; + umdiff_Command *curCommand; + +#define $offset_sectors (offset / ISO_SECTOR_SIZE) + + while (count > 0) { + commands = _impl_umdiff_File_readIndexCmds(file, $offset_sectors, + read_patchFile); + curCommand = 0; + + for (int i = 0; i < 1024; i++) { + if (commands[i].sector_start <= $offset_sectors) { + curCommand = &commands[i]; + break; + } + } + res = _impl_umdiff_Command_read(*curCommand, dest, count, offset, + read_source, read_patchFile); + dest += res; + count -= res; + } + + return res; +} + +// vim: ft=c.doxygen diff --git a/umdiff/rdiff.c b/umdiff/rdiff.c new file mode 100644 index 0000000..adf1dde --- /dev/null +++ b/umdiff/rdiff.c @@ -0,0 +1,15 @@ +/** + * @file rdiff.c + * @author Karim Vergnes + * @copyright GPLv2 + * @brief rsync diff parser + * + * This file parses and converts rdiff commands into equivalent UMDiff commands + * and manages the corresponding data bucket. + */ + +#include + + + +// vim: ft=c.doxygen diff --git a/umdiff/umdiff.h b/umdiff/umdiff.h index ba19e88..1e0e6be 100644 --- a/umdiff/umdiff.h +++ b/umdiff/umdiff.h @@ -18,7 +18,7 @@ #define umdiff_File_$VERSION 0x00 // Commands shall start immediately after the full header -#define umdiff_File_$COMMANDS_START (sizeof lp_UMDiffHeader) +#define umdiff_File_$COMMANDS_START (sizeof(umdiff_Header)) /** * @brief Index of commands at 1024 sector interval. @@ -46,7 +46,7 @@ typedef long umdiff_CmdIndex[1024]; * we gain a worst-case patch application process in O(1) space and O(log(n)) * time on PSP. */ -typedef struct +typedef struct __attribute__((packed)) { char magic[7]; /* = 0x7f 'UMDiff' */ char version; @@ -101,16 +101,40 @@ typedef enum { * This struct represents the contents of a UMDiff file, loaded in memory. * If read from a file, only the header is guaranteed to be populated. * + * If commands is set to point to a 1024-command buffer, and mode + * is set to @ref umdiff_FileFlags_HEADER_ONLY, it will be used as a holding + * buffer when reading commands from file. + * * @see umdiff_FileFlags for the possible cases. */ typedef struct { umdiff_Header hdr; + + /** + * @brief List of loaded commands from file + * + * If mode is set to @ref umdiff_FileFlags_HEADER_ONLY, this is + * expected to be a pre-allocated buffer holding 1024 commands. + * Otherwise, it contains all commands in the file. + */ umdiff_Command *commands; - long data_len; char *data; + long data_len; + + /** Keeps track of the last command batch pulled from index. */ + int last_index; + /** Which loading mode was used when this file was opened. */ umdiff_FileFlags mode; } umdiff_File; +/** + * @brief Callback function to read from a source. + * + * This is used by @ref umdiff_File_readPatched to perform an actual read from + * a data source, be it the patch file or original media. + */ +typedef int (*umdiff_ReadCallback)(void *dest, long count, long offset); + /** * @brief Load a UMDiff file from an open file descriptor. * @@ -141,6 +165,25 @@ umdiff_File_load(umdiff_File *file, int fd, umdiff_FileFlags flags); int umdiff_File_write(umdiff_File *file, int outfd); +/** + * @brief Perform a read at a given offset and size with patches applied. + * + * This function maps a given read request into the patched data, using the + * provided callback functions to carry out the actual read commands needed. + * + * @param file The @ref umdiff_File to perform the patch with. + * @param dest Like pread(2), the buffer to write the data into. + * @param count Like pread(2), the amount of data to read. + * @param offset Like pread(2), the offset at which to start reading. + * + * @param read_source The function to read the source media. + * @param read_patchFile The function to read the patch file from storage. + */ +int +umdiff_File_readPatched(umdiff_File *file, void *dest, long count, long offset, + umdiff_ReadCallback read_source, + umdiff_ReadCallback read_patchFile); + #endif //__UMDIFF_H // vim: ft=c.doxygen