diff --git a/firmware/components/libgit2/CMakeLists.txt b/firmware/components/libgit2/CMakeLists.txt index 96d174a..454ec97 100644 --- a/firmware/components/libgit2/CMakeLists.txt +++ b/firmware/components/libgit2/CMakeLists.txt @@ -107,13 +107,18 @@ target_compile_options(${COMPONENT_LIB} PRIVATE -Wno-error=implicit-int ) -# FATFS can't do POSIX rename-replace (f_rename fails if the target exists) and -# has no hardlinks, so libgit2's p_rename (link-then-rename) can't overwrite the -# config/refs/HEAD/index files its lock→commit writes depend on. Compile -# posix.c's p_rename under a throwaway name — scoped to this ONE file so no other -# TU is touched — and provide our own remove-then-rename p_rename in esp_stubs.c -# (p_rename is the single atomic-rename path: filebuf/futils/indexer/refdb_fs). +# posix.c holds three low-level fs primitives we must replace for FATFS; compile +# the originals under throwaway names — scoped to this ONE file so no other TU is +# touched — and provide FATFS-correct versions in esp_stubs.c: +# p_rename — FATFS f_rename can't replace an existing target and FAT has no +# hardlinks, so libgit2's link-then-rename can't overwrite config/refs/HEAD/ +# index during its lock→commit. Ours does remove-then-rename. +# p_open / p_creat — libgit2 creates objects/packs mode 0444; FATFS turns that +# into AM_RDO and then won't f_unlink them (and chmod can't clear it), so +# objects become undeletable (breaks re-clone/fetch/repack). Ours force +# owner-write so nothing is ever created read-only. set_source_files_properties( "${LG2}/src/util/posix.c" - PROPERTIES COMPILE_DEFINITIONS "p_rename=libgit2_unused_p_rename" + PROPERTIES COMPILE_DEFINITIONS + "p_rename=libgit2_unused_p_rename;p_open=libgit2_unused_p_open;p_creat=libgit2_unused_p_creat" ) diff --git a/firmware/components/libgit2/esp_stubs.c b/firmware/components/libgit2/esp_stubs.c index 546f66f..129401d 100644 --- a/firmware/components/libgit2/esp_stubs.c +++ b/firmware/components/libgit2/esp_stubs.c @@ -14,8 +14,17 @@ #include #include #include -#include /* stat() for the existence-gated utimes() below */ -#include /* remove(), rename() for the p_rename replacement */ +#include /* stat() + S_IWUSR for utimes()/p_open() below */ +#include /* remove(), rename() for the p_rename replacement */ +#include /* open(), O_* flags for the p_open()/p_creat() shims */ +#include /* va_list for the variadic p_open() */ + +#ifndef O_BINARY +#define O_BINARY 0 /* no text/binary distinction on esp-idf */ +#endif +#ifndef O_CLOEXEC +#define O_CLOEXEC 0 /* no exec() on esp-idf, so close-on-exec is a no-op */ +#endif /* One implicit root user/group. */ uid_t getuid(void) { return 0; } @@ -106,3 +115,32 @@ int p_rename(const char *from, const char *to) (void)remove(to); /* ignore ENOENT when `to` doesn't exist yet */ return rename(from, to) == 0 ? 0 : -1; } + +/* libgit2 creates loose objects and packfiles with mode 0444 (read-only) — the + * git convention that objects are immutable. FATFS honours that as the AM_RDO + * attribute and then refuses to f_unlink the file (EACCES), and esp-idf's FATFS + * VFS chmod() can't clear AM_RDO — so a written object can NEVER be deleted, + * which breaks re-clone recovery and (later) fetch/repack. We force owner-write + * into every create mode so libgit2's files stay writable and therefore + * deletable. Immutability is only a safety hint on an appliance where nothing + * but libgit2 touches these files. posix.c's originals are compiled as + * libgit2_unused_p_open/p_creat (see the component CMakeLists), so these are the + * definitions every other TU links against. Mirrors posix.c's p_open/p_creat. */ +int p_open(const char *path, int flags, ...) +{ + mode_t mode = 0; + if (flags & O_CREAT) { + va_list arg_list; + va_start(arg_list, flags); + mode = (mode_t)va_arg(arg_list, int); + va_end(arg_list); + mode |= S_IWUSR; /* never create read-only → FATFS won't set AM_RDO */ + } + return open(path, flags | O_BINARY | O_CLOEXEC, mode); +} + +int p_creat(const char *path, mode_t mode) +{ + return open(path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_CLOEXEC, + mode | S_IWUSR); +}