diff --git a/Makefile b/Makefile index 8e938db..ecce9bc 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,6 @@ TARGET = umd_livepatch -C_OBJS = patch.o io_funcs.o main.o +C_OBJS = patch.o io_funcs.o main.o \ + stdio_glue.o umdiff/file.o OBJS = $(C_OBJS) imports.o all: $(TARGET).prx INCDIR = $(ARKROOT)/common/include $(ARKROOT)/core/systemctrl/include $(LIBRSYNC)/src diff --git a/stdio_glue.c b/stdio_glue.c new file mode 100644 index 0000000..2f9cd38 --- /dev/null +++ b/stdio_glue.c @@ -0,0 +1,33 @@ +/** + * @file stdio_glue.c + * @author Karim Vergnes + * @copyright GPLv2 + * @brief Glue code between POSIX stdio and PSP kernel + * + * This module allows me to reuse code from the UMDiff utility, by redirecting + * POSIX standard I/O functions to their PSP kernel counterparts. + * Probably won't work for more complex programs. + */ + +#include +#include + +off_t +lseek(int fd, off_t offset, int whence) +{ + return sceIoLseek(fd, offset, whence); +} + +int +read(int fd, void *buf, size_t count) +{ + return sceIoRead(fd, buf, count); +} + +int +write(int fd, const void *buf, size_t count) +{ + return sceIoWrite(fd, buf, count); +} + +// vim: ft=c.doxygen diff --git a/umdiff/file.c b/umdiff/file.c new file mode 100644 index 0000000..2d09018 --- /dev/null +++ b/umdiff/file.c @@ -0,0 +1,85 @@ +/** + * @file file.c + * @author Karim Vergnes + * @copyright GPLv2 + * @brief Functions to read and write UMDiff files + * + * The functions defined here control reading and writing UMDiff files from + * in-memory structure @ref umdiff_File. + */ + +#include "umdiff.h" + +#include +#include +#include + +typedef union { + char data[sizeof(umdiff_Header)]; + umdiff_Header hdr; +} _impl_umdiff_RawHeader; + +#define _impl_umdiff_alignSector$(x) \ + ( x + ISO_SECTOR_SIZE - (x % ISO_SECTOR_SIZE) ) + +void +_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); + hdr->version = umdiff_File_$VERSION; + hdr->data_start = _impl_umdiff_alignSector$(len_preamble); +} + +int +umdiff_File_load(umdiff_File *file, int fd, umdiff_FileFlags flags) +{ + _impl_umdiff_RawHeader rheader; + int ret; + + if (!file) return 1; + + ret = read(fd, &rheader.data, sizeof(umdiff_Header)); + file->hdr = rheader.hdr; + + if (strncmp(file->hdr.magic, umdiff_File_$MAGIC, 7)) + return 1; + if (file->hdr.version != umdiff_File_$VERSION) + return 1; + + if (flags >= umdiff_FileFlags_HEADER_AND_COMMANDS) { + ret = read(fd, file->commands, sizeof(umdiff_Command) * file->hdr.cmd_count); + } + if (flags == umdiff_FileFlags_LOAD_FULL) { + file->data_len = lseek(fd, 0, SEEK_END) - file->hdr.data_start; + ret = lseek(fd, file->hdr.data_start, SEEK_SET); + ret = read(fd, file->data, file->data_len); + } + + return 0; +} + +int +umdiff_File_write(umdiff_File *file, int outfd) +{ + _impl_umdiff_RawHeader rheader; + int ret; + + if (!file || file->mode != umdiff_FileFlags_LOAD_FULL + || !file->commands || !file->data + || !file->hdr.cmd_count || !file->hdr.index[0]) + return 1; + + rheader.hdr = file->hdr; + + _impl_umdiff_Header_rectify(&rheader.hdr); + + ret = write(outfd, rheader.data, sizeof(umdiff_Header)); + ret = write(outfd, file->commands, sizeof(umdiff_Command) * rheader.hdr.cmd_count); + ret = lseek(outfd, rheader.hdr.data_start, SEEK_SET); + ret = write(outfd, file->data, file->data_len); + + return 0; +} diff --git a/umdiff/main.c b/umdiff/main.c index 132475f..7526234 100644 --- a/umdiff/main.c +++ b/umdiff/main.c @@ -9,11 +9,25 @@ * in the umd_livepatch module. */ +#include + #include "usage.rl.h" +#include "rdiff.h" int umdiff_delta(char *source, char *target, char *output) { + int source_fd, target_fd, output_fd; + umdiff_File *result; + + source_fd = open(source, O_RDONLY); + target_fd = open(target, O_RDONLY); + + result = umdiff_File_fromCompare(source_fd, target_fd); + + output_fd = open(output, O_WRONLY|O_CREAT|O_TRUNC); + umdiff_File_write(result, output_fd); + return 1; } diff --git a/umdiff/patch.c b/umdiff/patch.c new file mode 100644 index 0000000..e69de29 diff --git a/umdiff/rdiff.c b/umdiff/rdiff.c index dbd99ef..3a918dc 100644 --- a/umdiff/rdiff.c +++ b/umdiff/rdiff.c @@ -8,12 +8,10 @@ * and convert it in-memory into the UMDiff format. */ -#include "umdiff.h" +#include "rdiff.h" #include -#define ISO_SECTOR_SIZE 2048 - typedef union { int fd; void *opaque; diff --git a/umdiff/rdiff.h b/umdiff/rdiff.h new file mode 100644 index 0000000..d46cdd1 --- /dev/null +++ b/umdiff/rdiff.h @@ -0,0 +1,29 @@ +#ifndef __RDIFF_H +#define __RDIFF_H + +/** + * @file rdiff.h + * @author Karim Vergnes + * @copyright GPLv2 + * @brief rsync-based diff calculator functions + * + * Generator functions to create a @ref umdiff_File object from computing the + * difference between two files, using librsync algorithms. + */ + +#include "umdiff.h" + + +/** + * @brief Generate a UMDiff file from a source and target file contents. + * + * This function invokes a signature and delta job from librsync under the hood. + * This is the optimum approach, as it includes our optimum parameters, + * ensuring computed blocks line up with ISO9660 sectors. + */ +umdiff_File * +umdiff_File_fromCompare(int source_fd, int target_fd); + +#endif //__RDIFF_H + +// vim: ft=c.doxygen diff --git a/umdiff/umdiff.h b/umdiff/umdiff.h index fbd4191..ba19e88 100644 --- a/umdiff/umdiff.h +++ b/umdiff/umdiff.h @@ -10,7 +10,15 @@ * This file contains all required types to read and parse a UMDiff file. */ -#define UMDIFF_VERSION 0x00 +#ifndef ISO_SECTOR_SIZE +# define ISO_SECTOR_SIZE 2048 +#endif + +#define umdiff_File_$MAGIC "\x7fUMDiff" +#define umdiff_File_$VERSION 0x00 + +// Commands shall start immediately after the full header +#define umdiff_File_$COMMANDS_START (sizeof lp_UMDiffHeader) /** * @brief Index of commands at 1024 sector interval. @@ -42,7 +50,7 @@ typedef struct __attribute__((packed)) { char magic[7]; /* = 0x7f 'UMDiff' */ char version; - long cmd_len; + long cmd_count; long data_start; umdiff_CmdIndex index; } umdiff_Header; @@ -58,7 +66,7 @@ __attribute__((packed)) { * - sector_count: How many sectors on the original disc the patch * command spans. * - patch_start: Where in the patch source the substitute data is - * found, in sectors. + * found, in sectors (past @ref data_start for in-file patches). * - patch_sector_count: How many sectors on the patch source the * substitute data occupies. If smaller than sector_count, then * the substitute is a repeating pattern of that length. @@ -75,14 +83,64 @@ typedef struct { long data_source; /* 0 = patchfile, 1 = source */ } umdiff_Command; -// Commands shall start immediately after the full header -#define UMDIFF_COMMANDS_START (sizeof lp_UMDiffHeader) +/** + * @brief Flags to control loading a UMDiff file. + * + * In order to adapt to memory-constrained environments, one can choose to + * not load certain parts of a UMDiff file, instead resorting to in-situ reads. + */ +typedef enum { + umdiff_FileFlags_HEADER_ONLY, + umdiff_FileFlags_HEADER_AND_COMMANDS, + umdiff_FileFlags_LOAD_FULL +} umdiff_FileFlags; +/** + * @brief In-memory structure for a UMDiff file. + * + * 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. + * + * @see umdiff_FileFlags for the possible cases. + */ typedef struct { - umdiff_Header *hdr; + umdiff_Header hdr; umdiff_Command *commands; + long data_len; + char *data; + umdiff_FileFlags mode; } umdiff_File; +/** + * @brief Load a UMDiff file from an open file descriptor. + * + * This function takes a pre-allocated @ref umdiff_File structure, and populates + * it from an open file descriptor, according to the given mode flags. + * + * @param file The target @ref umdiff_File to load into. + * @param fd The open file descriptor to read from. + * @param flags Flags to control how much data to read in memory. + * + * @return 0 on success, any other value indicates an error. + */ +int +umdiff_File_load(umdiff_File *file, int fd, umdiff_FileFlags flags); + +/** + * @brief Write a fully-populated UMDiff into a file descriptor. + * + * This function takes a fully populated @ref umdiff_File structure, and writes + * it down into an open file descriptor. The output header will be set with + * correct values, only cmd_len and index must both be set. + * + * @param file The @ref umdiff_File to write from. Must be fully populated. + * @param outfd The open file descriptor to write into. + * + * @return 0 on success, any other value indicates an error. + */ +int +umdiff_File_write(umdiff_File *file, int outfd); + #endif //__UMDIFF_H // vim: ft=c.doxygen