diff --git a/ApplyPatch.cpp b/ApplyPatch.cpp index f995c46..f6e8bfe 100755 --- a/ApplyPatch.cpp +++ b/ApplyPatch.cpp @@ -46,7 +46,7 @@ int ApplyPatchFn(const char* name, State* state, int argc, char * argv[]) { FILE *rm; int length; - rm = fopen(argv[i+6], "r"); + rm = fopen(argv[i+6], "rb"); fseek (rm, 0, SEEK_END); length = ftell (rm); fseek (rm, 0, SEEK_SET); @@ -69,7 +69,7 @@ int ApplyPatchFn(const char* name, State* state, int argc, char * argv[]) { FILE *rm; int length; - rm = fopen(bonus_filename, "r"); + rm = fopen(bonus_filename, "rb"); fseek (rm, 0, SEEK_END); length = ftell (rm); fseek (rm, 0, SEEK_SET); @@ -94,7 +94,11 @@ int ApplyPatchFn(const char* name, State* state, int argc, char * argv[]) { return -1; } else if (res != 0) { printf("creating cache dir %s\n", dirname.c_str()); +#ifdef __MINGW32__ + res = mkdir(dirname.c_str()); +#else res = mkdir(dirname.c_str(), CACHE_DIR_MODE); +#endif if (res != 0) { printf("mkdir \"%s\" failed: %s\n", diff --git a/Makefile b/Makefile index 9a38641..f7f10e4 100755 --- a/Makefile +++ b/Makefile @@ -1,3 +1,4 @@ + # Erfan Abdi CC = gcc PP = g++ @@ -25,11 +26,13 @@ endif SUBDIRS = applypatch android-base edify minzip otafault blockimg +.PHONY: all sub bindir scriptp clean + all:sub bindir scriptp BlockImageVerify.o BlockImageUpdate.o imgdiff.o ApplyPatch.o bin/BlockImageVerify$(EXE) bin/BlockImageUpdate$(EXE) bin/imgdiff$(EXE) bin/ApplyPatch$(EXE) sub: for dir in $(SUBDIRS); do \ - cd $$dir && make && cd ../; \ + $(MAKE) -C $$dir || exit $$?; \ done bindir: @@ -63,5 +66,5 @@ bin/ApplyPatch$(EXE):ApplyPatch.o applypatch/applypatch.o edify/expr.o android-b $(CROSS_COMPILE)$(PP) -o $@ $^ $(LDFLAGS) -s clean: - find -name '*.o' -exec rm {} \; + find . -name '*.o' -exec $(RM) {} + $(RMDIR) bin diff --git a/README.windows.md b/README.windows.md new file mode 100644 index 0000000..685a8b0 --- /dev/null +++ b/README.windows.md @@ -0,0 +1,92 @@ +# IMG Patch Tools — Windows Build + +Build instructions and notes for building `imgpatchtools` on Windows with MSYS2 / MinGW-w64. + +See [README.md](README.md) for what the tools do and how to invoke them. + +## Prerequisites + +Install [MSYS2](https://www.msys2.org/), then from the MSYS2 shell: + +```sh +pacman -S \ + mingw-w64-x86_64-gcc \ + mingw-w64-x86_64-make \ + mingw-w64-x86_64-openssl \ + mingw-w64-x86_64-zlib \ + mingw-w64-x86_64-bzip2 \ + mingw-w64-x86_64-pkgconf +``` + +A separate `make` (e.g. ezwinports) on `PATH` will also work, as will the MSYS2 shell `make`. + +## Build + +From a Git Bash, MSYS2, or compatible shell in the repository root: + +```sh +PATH="/c/msys64/mingw64/bin:$PATH" make +``` + +The `PATH` prefix is **required**. Without it, `cc1plus.exe` may load a conflicting runtime DLL from elsewhere on `PATH` and exit silently — make will report `Error 1` with no compiler diagnostics. + +Adjust the path if you installed MSYS2 somewhere other than `C:\msys64`. + +Build outputs: + +``` +bin/ApplyPatch.exe +bin/BlockImageUpdate.exe +bin/BlockImageVerify.exe +bin/imgdiff.exe +bin/scriptpatcher.sh +``` + +## Running + +Usage is the same as the Linux/macOS build — see [README.md](README.md). Example: + +```powershell +./bin/ApplyPatch.exe boot.img - 33554432 boot.img.p +``` + +The tools run under PowerShell, `cmd.exe`, Git Bash, or MSYS2. + +## Windows-specific changes + +These build under `__MINGW32__` guards and have no effect on Linux/macOS: + +| Area | Behavior on Windows | +|---|---| +| `err()` / `errx()` | Polyfilled in [applypatch/include/err.h](applypatch/include/err.h) | +| `fsync()` | Aliased to `_commit()` in [otafault/ota_io.cpp](otafault/ota_io.cpp) | +| `O_BINARY` | OR'd into all `ota_open()` calls so binary I/O isn't mangled by CRLF translation | +| Patch file reads | Opened with `"rb"` instead of `"r"` in [ApplyPatch.cpp](ApplyPatch.cpp) | +| `chown()` | No-op stub (Windows has no POSIX UID/GID) | +| `O_SYNC` | Defined to `0` (no-op flag) | +| `mkdir(path, mode)` | Calls `mkdir(path)` — file modes don't apply to NTFS | +| BLKDISCARD / eMMC TRIM | Skipped (`SUPPRESS_EMMC_WIPE` is auto-defined) | +| Directory `fsync()` | Skipped (no NTFS equivalent) | +| `FreeSpaceForFile()` | Compiled out (uses ``) — free-space checks are skipped | +| ``, `` | Not included (unused under the relevant guards) | + +## Known limitations + +These tools were designed to run on an Android device or a Linux host doing OTA work. On Windows they're useful for: + +- **Generating** patches with `imgdiff` from one image to another +- **Applying** patches to standalone files with `ApplyPatch` (e.g. patching a `boot.img` extracted from an OTA) +- **Validating** block-image OTAs offline with `BlockImageVerify` + +They are **not** suitable for running against a live block device or partition on Windows — code paths that touch raw block devices, `chown`, `BLKDISCARD`, free-space checks, and directory `fsync()` are stubbed out. Use the Linux build for any in-place partition work. + +## Troubleshooting + +**`make` exits with `Error 1` and no compiler output** +The `PATH` problem above. Re-run with `PATH="/c/msys64/mingw64/bin:$PATH" make`. + +**`bz error -4` when applying a patch** +Symptom of a binary file opened in text mode. All known sites are fixed; if you see this on a new code path, find the `fopen(..., "r")` or bare `open(..., O_RDONLY)` and add `"rb"` / `O_BINARY`. + +**`ApplyPatch.exe` crashes on multiple patches** +[ApplyPatch.cpp:64](ApplyPatch.cpp#L64) pushes a pointer to a stack-local `Value` into a vector inside the loop. Single-patch invocations work; multi-patch invocations have undefined behavior. This is a pre-existing upstream bug, not Windows-specific. diff --git a/applypatch/applypatch.cpp b/applypatch/applypatch.cpp index 8bce310..1c7fe1e 100644 --- a/applypatch/applypatch.cpp +++ b/applypatch/applypatch.cpp @@ -21,12 +21,19 @@ #include #include #include -#ifndef __APPLE__ +#if !defined(__APPLE__) && !defined(__MINGW32__) #include #endif #include #include +#ifdef __MINGW32__ +#ifndef O_SYNC +#define O_SYNC 0 +#endif +static inline int chown(const char*, unsigned, unsigned) { return 0; } +#endif + #include #include @@ -601,7 +608,7 @@ ssize_t MemorySink(const unsigned char* data, ssize_t len, void* token) { // Return the amount of free space (in bytes) on the filesystem // containing filename. filename must exist. Return -1 on error. -#ifndef __APPLE__ +#if !defined(__APPLE__) && !defined(__MINGW32__) size_t FreeSpaceForFile(const char* filename) { struct statfs sf; if (statfs(filename, &sf) != 0) { @@ -850,7 +857,7 @@ static int GenerateTarget(FileContents* source_file, } else { int enough_space = 0; if (retry > 0) { - #ifndef __APPLE__ + #if !defined(__APPLE__) && !defined(__MINGW32__) size_t free_space = FreeSpaceForFile(target_fs.c_str()); enough_space = (free_space > (256 << 10)) && // 256k (two-block) minimum @@ -891,7 +898,7 @@ static int GenerateTarget(FileContents* source_file, } made_copy = 1; unlink(source_filename); - #ifndef __APPLE__ + #if !defined(__APPLE__) && !defined(__MINGW32__) size_t free_space = FreeSpaceForFile(target_fs.c_str()); printf("(now %zu bytes free for target) ", free_space); #endif diff --git a/applypatch/imgdiff.cpp b/applypatch/imgdiff.cpp index 810d0bb..b5ef96e 100755 --- a/applypatch/imgdiff.cpp +++ b/applypatch/imgdiff.cpp @@ -130,6 +130,10 @@ #include #include +#ifdef __MINGW32__ +typedef unsigned char u_char; +#endif + #include "zlib.h" #include "imgdiff.h" #include "utils.h" diff --git a/applypatch/include/err.h b/applypatch/include/err.h new file mode 100644 index 0000000..07ed708 --- /dev/null +++ b/applypatch/include/err.h @@ -0,0 +1,36 @@ +#ifndef IMGPATCHTOOLS_ERR_H +#define IMGPATCHTOOLS_ERR_H + +#include +#include +#include +#include +#include + +static inline void err(int eval, const char *fmt, ...) { + int saved_errno = errno; + fprintf(stderr, "error: "); + if (fmt) { + va_list ap; + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fprintf(stderr, ": "); + } + fprintf(stderr, "%s\n", strerror(saved_errno)); + exit(eval); +} + +static inline void errx(int eval, const char *fmt, ...) { + fprintf(stderr, "error: "); + if (fmt) { + va_list ap; + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + } + fprintf(stderr, "\n"); + exit(eval); +} + +#endif diff --git a/blockimg/blockimg.cpp b/blockimg/blockimg.cpp index 52900fd..db3cdb5 100755 --- a/blockimg/blockimg.cpp +++ b/blockimg/blockimg.cpp @@ -29,8 +29,10 @@ #include #include #include +#ifndef __MINGW32__ #include #include +#endif #include #include #include @@ -73,7 +75,11 @@ _rc; \ }) #endif -#ifndef BLKDISCARD +#ifdef __MINGW32__ +#define SUPPRESS_EMMC_WIPE +#endif + +#if !defined(BLKDISCARD) && !defined(__MINGW32__) #define BLKDISCARD _IO(0x12,119) #endif @@ -215,6 +221,10 @@ static bool discard_blocks(int fd, off_t offset, uint64_t size) { return true; } +#ifdef __MINGW32__ + (void)fd; (void)offset; (void)size; + return true; +#else struct stat sb; if (fstat(fd, &sb) == -1) { printf("failed to fstat device to BLKDISCARD: %s\n", strerror(errno)); @@ -233,6 +243,7 @@ static bool discard_blocks(int fd, off_t offset, uint64_t size) { return false; } return true; +#endif } static bool check_lseek(int fd, off_t offset, int whence) { @@ -552,11 +563,17 @@ static void EnumerateStash(const std::string& dirname, StashCallback callback, v struct dirent* item; while ((item = readdir(directory.get())) != nullptr) { + std::string fn = dirname + "/" + std::string(item->d_name); +#ifdef __MINGW32__ + struct stat sb; + if (stat(fn.c_str(), &sb) != 0 || !S_ISREG(sb.st_mode)) { + continue; + } +#else if (item->d_type != DT_REG) { continue; } - - std::string fn = dirname + "/" + std::string(item->d_name); +#endif callback(fn, data); } } @@ -746,6 +763,7 @@ static int WriteStash(const std::string& base, const std::string& id, int blocks return -1; } +#ifndef __MINGW32__ std::string dname = GetStashFileName(base, "", ""); int dfd = TEMP_FAILURE_RETRY(open(dname.c_str(), O_RDONLY | O_DIRECTORY)); unique_fd dfd_holder(dfd); @@ -761,6 +779,7 @@ static int WriteStash(const std::string& base, const std::string& id, int blocks printf("fsync \"%s\" failed: %s\n", dname.c_str(), strerror(errno)); return -1; } +#endif return 0; } @@ -791,7 +810,11 @@ static int CreateStash(State* state, int maxblocks, const char* blockdev, std::s return -1; } else if (res != 0) { printf("creating stash %s\n", dirname.c_str()); +#ifdef __MINGW32__ + res = mkdir(dirname.c_str()); +#else res = mkdir(dirname.c_str(), STASH_DIRECTORY_MODE); +#endif if (res != 0) { ErrorAbort(state, kStashCreationFailure, "mkdir \"%s\" failed: %s\n", @@ -1449,7 +1472,12 @@ static int PerformBlockImageUpdate(const char* name, State* state, int argc, cha return -1; } else if (res != 0) { printf("creating cache dir %s\n", dirname.c_str()); +#ifdef __MINGW32__ + res = mkdir(dirname.c_str()); + (void)STASH_DIRECTORY_MODE; +#else res = mkdir(dirname.c_str(), STASH_DIRECTORY_MODE); +#endif if (res != 0) { ErrorAbort(state, kStashCreationFailure, "mkdir \"%s\" failed: %s\n", diff --git a/otafault/ota_io.cpp b/otafault/ota_io.cpp index 8607574..3dbd706 100755 --- a/otafault/ota_io.cpp +++ b/otafault/ota_io.cpp @@ -22,6 +22,11 @@ #include #include +#ifdef __MINGW32__ +#include +#define fsync(fd) _commit(fd) +#endif + //#include "config.h" #include "ota_io.h" @@ -50,15 +55,21 @@ void ota_set_fault_files() { bool have_eio_error = false; +#ifdef __MINGW32__ +#define OTA_BINARY_FLAG O_BINARY +#else +#define OTA_BINARY_FLAG 0 +#endif + int ota_open(const char* path, int oflags) { // Let the caller handle errors; we do not care if open succeeds or fails - int fd = open(path, oflags); + int fd = open(path, oflags | OTA_BINARY_FLAG); //filename_cache[fd] = path; return fd; } int ota_open(const char* path, int oflags, mode_t mode) { - int fd = open(path, oflags, mode); + int fd = open(path, oflags | OTA_BINARY_FLAG, mode); //filename_cache[fd] = path; return fd; }