Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions ApplyPatch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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);
Expand All @@ -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",
Expand Down
7 changes: 5 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

# Erfan Abdi <erfangplus@gmail.com>
CC = gcc
PP = g++
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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
92 changes: 92 additions & 0 deletions README.windows.md
Original file line number Diff line number Diff line change
@@ -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 - <tgt_sha1> 33554432 <init_sha1> 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 `<sys/statfs.h>`) — free-space checks are skipped |
| `<sys/wait.h>`, `<sys/ioctl.h>` | 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.
15 changes: 11 additions & 4 deletions applypatch/applypatch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,19 @@
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#ifndef __APPLE__
#if !defined(__APPLE__) && !defined(__MINGW32__)
#include <sys/statfs.h>
#endif
#include <sys/types.h>
#include <unistd.h>

#ifdef __MINGW32__
#ifndef O_SYNC
#define O_SYNC 0
#endif
static inline int chown(const char*, unsigned, unsigned) { return 0; }
#endif

#include <memory>
#include <string>

Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions applypatch/imgdiff.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,10 @@
#include <unistd.h>
#include <sys/types.h>

#ifdef __MINGW32__
typedef unsigned char u_char;
#endif

#include "zlib.h"
#include "imgdiff.h"
#include "utils.h"
Expand Down
36 changes: 36 additions & 0 deletions applypatch/include/err.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#ifndef IMGPATCHTOOLS_ERR_H
#define IMGPATCHTOOLS_ERR_H

#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

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
34 changes: 31 additions & 3 deletions blockimg/blockimg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,10 @@
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#ifndef __MINGW32__
#include <sys/wait.h>
#include <sys/ioctl.h>
#endif
#include <time.h>
#include <unistd.h>
#include <limits.h>
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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));
Expand All @@ -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) {
Expand Down Expand Up @@ -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);
}
}
Expand Down Expand Up @@ -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);
Expand All @@ -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;
}
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down
15 changes: 13 additions & 2 deletions otafault/ota_io.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@
#include <sys/stat.h>
#include <unistd.h>

#ifdef __MINGW32__
#include <io.h>
#define fsync(fd) _commit(fd)
#endif

//#include "config.h"
#include "ota_io.h"

Expand Down Expand Up @@ -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; }

Expand Down