feat: ports - git port
This commit is contained in:
@@ -43,3 +43,6 @@
|
|||||||
[submodule "www-client/netsurf/upstream"]
|
[submodule "www-client/netsurf/upstream"]
|
||||||
path = www-client/netsurf/upstream
|
path = www-client/netsurf/upstream
|
||||||
url = https://github.com/netsurf-browser/netsurf.git
|
url = https://github.com/netsurf-browser/netsurf.git
|
||||||
|
[submodule "dev-vcs/git/upstream"]
|
||||||
|
path = dev-vcs/git/upstream
|
||||||
|
url = https://github.com/git/git
|
||||||
|
|||||||
@@ -38,11 +38,21 @@ PORTS := \
|
|||||||
media-libs/libnsgif \
|
media-libs/libnsgif \
|
||||||
media-libs/libnsbmp \
|
media-libs/libnsbmp \
|
||||||
www-client/netsurf \
|
www-client/netsurf \
|
||||||
games-fps/doom
|
games-fps/doom \
|
||||||
|
dev-vcs/git
|
||||||
|
|
||||||
# Bundle directories are keyed by port name, not category path.
|
# Bundle directories are keyed by port name, not category path.
|
||||||
PORT_NAMES := $(notdir $(PORTS))
|
PORT_NAMES := $(notdir $(PORTS))
|
||||||
|
|
||||||
|
# Ports that install themselves rather than producing an app bundle: command
|
||||||
|
# line tools, which land in 0:/os/ with no manifest and no launcher icon.
|
||||||
|
# `install` below delegates to their own install target.
|
||||||
|
#
|
||||||
|
# www-client/netsurf is deliberately NOT here: it also has an install target,
|
||||||
|
# but it drops a resource tree next to the binary and is not something every
|
||||||
|
# image should carry, so it stays a manual `make -C www-client/netsurf install`.
|
||||||
|
SELF_INSTALL_PORTS := dev-vcs/git
|
||||||
|
|
||||||
.PHONY: all clean install $(PORTS) $(PORT_NAMES)
|
.PHONY: all clean install $(PORTS) $(PORT_NAMES)
|
||||||
|
|
||||||
all: $(PORTS)
|
all: $(PORTS)
|
||||||
@@ -68,6 +78,9 @@ install: all
|
|||||||
echo "installed port: $$p"; \
|
echo "installed port: $$p"; \
|
||||||
fi; \
|
fi; \
|
||||||
done
|
done
|
||||||
|
@for p in $(SELF_INSTALL_PORTS); do \
|
||||||
|
$(MAKE) -C $$p install; \
|
||||||
|
done
|
||||||
@echo "ports: installed into $(MONTAUKOS)/programs/bin/apps/"
|
@echo "ports: installed into $(MONTAUKOS)/programs/bin/apps/"
|
||||||
@echo "ports: run 'make ramdisk' (or 'make') in $(MONTAUKOS) to rebuild the image"
|
@echo "ports: run 'make ramdisk' (or 'make') in $(MONTAUKOS) to rebuild the image"
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,8 @@ git clone --recurse-submodules <url> ~/dev/mtkports
|
|||||||
cd ~/dev/mtkports
|
cd ~/dev/mtkports
|
||||||
make # build every port into out/apps/<name>/
|
make # build every port into out/apps/<name>/
|
||||||
make doom # build one port
|
make doom # build one port
|
||||||
make install # copy bundles into MontaukOS/programs/bin/apps/
|
make install # copy bundles into MontaukOS/programs/bin/apps/, and
|
||||||
|
# run the install target of ports that are not bundles
|
||||||
```
|
```
|
||||||
|
|
||||||
Then from the MontaukOS tree:
|
Then from the MontaukOS tree:
|
||||||
@@ -37,3 +38,4 @@ make ramdisk
|
|||||||
| libnsbmp | [netsurf-browser/libnsbmp](https://github.com/netsurf-browser/libnsbmp) | BMP/ICO decoder. |
|
| libnsbmp | [netsurf-browser/libnsbmp](https://github.com/netsurf-browser/libnsbmp) | BMP/ICO decoder. |
|
||||||
| netsurf | [netsurf-browser/netsurf](https://github.com/netsurf-browser/netsurf) | NetSurf web browser - monkey frontend only. **Not** an app bundle. |
|
| netsurf | [netsurf-browser/netsurf](https://github.com/netsurf-browser/netsurf) | NetSurf web browser - monkey frontend only. **Not** an app bundle. |
|
||||||
| doom | [ozkl/doomgeneric](https://github.com/ozkl/doomgeneric) | `doom1.wad` is proprietary and deliberately not bundled. |
|
| doom | [ozkl/doomgeneric](https://github.com/ozkl/doomgeneric) | `doom1.wad` is proprietary and deliberately not bundled. |
|
||||||
|
| git | [git/git](https://github.com/git/git) | v2.39.5. WIP - installs to `0:/os/git.elf`. Local repositories only. See [dev-vcs/git/README.md](dev-vcs/git/README.md). |
|
||||||
|
|||||||
@@ -0,0 +1,218 @@
|
|||||||
|
# git for MontaukOS
|
||||||
|
#
|
||||||
|
# Upstream: https://github.com/git/git (submodule at upstream/, pinned to
|
||||||
|
# v2.39.5).
|
||||||
|
#
|
||||||
|
# git is built as a plain system program (0:/os/git.elf), not an app bundle:
|
||||||
|
# it is a command-line tool driven from the terminal.
|
||||||
|
#
|
||||||
|
# CONFIGURATION
|
||||||
|
# The build must not pick up any host defaults. git derives its platform
|
||||||
|
# settings from `uname -s` through config.mak.uname, which on this host
|
||||||
|
# would answer "Linux" and switch on clock_gettime, getdelim, /dev/tty and
|
||||||
|
# friends -- none of which the Montauk libc has. Every uname_* variable is
|
||||||
|
# therefore overridden to a name config.mak.uname does not know, leaving
|
||||||
|
# the knobs below as the only configuration.
|
||||||
|
#
|
||||||
|
# The NO_* list is not "things git can live without" so much as an
|
||||||
|
# inventory of what MontaukOS does not provide yet: no threads, no
|
||||||
|
# sockets, no iconv, no regex, no file-backed mmap. git ships portable
|
||||||
|
# fallbacks for all of them (compat/), which is what makes this port
|
||||||
|
# tractable at all.
|
||||||
|
#
|
||||||
|
# COMPAT SHIMS (compat/) supply the headers and functions the Montauk libc
|
||||||
|
# has no equivalent of -- see compat/README for what each one does and, more
|
||||||
|
# importantly, what it does not.
|
||||||
|
|
||||||
|
UPSTREAM := upstream
|
||||||
|
COMPAT := $(CURDIR)/compat
|
||||||
|
|
||||||
|
PORT_NAME := git
|
||||||
|
|
||||||
|
include ../../mk/port.mk
|
||||||
|
|
||||||
|
GIT_BIN := $(UPSTREAM)/git
|
||||||
|
GIT_INSTALL := $(MONTAUKOS)/programs/bin/os
|
||||||
|
|
||||||
|
# Where git's compiled-in paths point on the target. programs/bin/os is the
|
||||||
|
# staging directory for 0:/os, so GIT_PREFIX and GIT_STAGE are two names for
|
||||||
|
# the same place -- one as the OS sees it, one as the build host does.
|
||||||
|
# RUNTIME_PREFIX is off (it wants /proc/self/exe), so these are baked in:
|
||||||
|
#
|
||||||
|
# $(GIT_PREFIX)/share/git-core/templates `git init` skeleton
|
||||||
|
# $(GIT_PREFIX)/etc/gitconfig system-wide config
|
||||||
|
# $(GIT_PREFIX)/libexec/git-core external subcommands (none yet;
|
||||||
|
# every command in this build is a
|
||||||
|
# builtin, which is what makes git
|
||||||
|
# usable without fork/exec)
|
||||||
|
GIT_PREFIX := 0:/os/git
|
||||||
|
GIT_STAGE := $(GIT_INSTALL)/git
|
||||||
|
|
||||||
|
# `git version` string. Without an override GIT-VERSION-GEN runs `git
|
||||||
|
# describe` on the checkout and, because the patch series is applied in the
|
||||||
|
# working tree, reports "2.39.5.dirty" -- true but unhelpful. Name the port
|
||||||
|
# instead: the tree really is patched, and this says by whom.
|
||||||
|
GIT_UPSTREAM_TAG := $(shell git -C $(UPSTREAM) describe --tags --abbrev=0 2>/dev/null)
|
||||||
|
GIT_VERSION_STR := $(patsubst v%,%,$(GIT_UPSTREAM_TAG))-montauk
|
||||||
|
|
||||||
|
# ==== Toolchain flags =================================================
|
||||||
|
# CFLAGS/LDFLAGS/EXTLIBS must be passed on git's make COMMAND LINE, not
|
||||||
|
# through the environment: its Makefile assigns `CFLAGS = -g -O2 -Wall`
|
||||||
|
# outright, and a plain assignment beats an environment value. Only a
|
||||||
|
# command-line assignment wins. Overriding them is safe -- git keeps its own
|
||||||
|
# required includes and defines in BASIC_CFLAGS, which is separate and is
|
||||||
|
# appended to ALL_CFLAGS after ours.
|
||||||
|
|
||||||
|
GIT_CFLAGS := \
|
||||||
|
-std=gnu99 \
|
||||||
|
$(PORT_CFLAGS_COMMON) \
|
||||||
|
-Wno-unused-parameter \
|
||||||
|
-Wno-sign-compare \
|
||||||
|
-I $(COMPAT) \
|
||||||
|
-include $(COMPAT)/montauk-paths.h \
|
||||||
|
-include $(COMPAT)/montauk-ownership.h \
|
||||||
|
-isystem $(shell $(CC) -print-file-name=include)
|
||||||
|
|
||||||
|
# crt1.o carries _start; -nostdlib (from PORT_LDFLAGS_COMMON) drops the
|
||||||
|
# driver's own startup files, so name them here.
|
||||||
|
GIT_CRT := \
|
||||||
|
$(SDK_LIB)/libc/crt1.o \
|
||||||
|
$(SDK_LIB)/libc/crti.o \
|
||||||
|
$(SDK_LIB)/libc/crtn.o
|
||||||
|
|
||||||
|
COMPAT_LIB := $(CURDIR)/compat/libgitcompat.a
|
||||||
|
|
||||||
|
GIT_LDFLAGS := $(PORT_LDFLAGS_COMMON) $(GIT_CRT)
|
||||||
|
|
||||||
|
# EXTLIBS lands on the link line after the objects, which is where the compat
|
||||||
|
# archive, zlib and libc have to sit for the static link to resolve. Naming
|
||||||
|
# libz.a here rather than letting ZLIB_PATH add -lz is deliberate: overriding
|
||||||
|
# EXTLIBS drops git's own `EXTLIBS += -lz` append. ZLIB_PATH is still set, for
|
||||||
|
# the -I it contributes to BASIC_CFLAGS.
|
||||||
|
GIT_EXTLIBS := $(COMPAT_LIB) $(PORT_SYSROOT_LIB)/libz.a $(LIBC_A)
|
||||||
|
|
||||||
|
# ==== Build configuration =============================================
|
||||||
|
#
|
||||||
|
# DEFAULT_PAGER=cat is load-bearing, not a preference: git_pager() treats the
|
||||||
|
# literal "cat" as "no pager at all" and skips the fork entirely (pager.c).
|
||||||
|
# Left at its default it would try to spawn `less` on every `git log` and
|
||||||
|
# fail, since MontaukOS cannot fork.
|
||||||
|
#
|
||||||
|
# OBJECT_CREATION_USES_RENAMES=1 because there are no hard links. Every loose
|
||||||
|
# object is written to a temp file and then finalized; by default git tries
|
||||||
|
# link()+unlink() first and only falls back to rename() when link fails, so
|
||||||
|
# without this every single object write makes a doomed syscall and travels an
|
||||||
|
# error path. The knob picks rename() up front, which is what happens anyway.
|
||||||
|
#
|
||||||
|
# DEFAULT_EDITOR is deliberately NOT set. With an empty environment TERM is
|
||||||
|
# unset, so git considers the terminal dumb, git_editor() returns NULL, and
|
||||||
|
# `git commit` with no -m says "terminal is dumb, but EDITOR unset. Please
|
||||||
|
# supply the message using either -m or -F option." Naming an editor here
|
||||||
|
# would replace that with a failed spawn.
|
||||||
|
|
||||||
|
GIT_ARGS := \
|
||||||
|
CC=$(CC) \
|
||||||
|
AR=$(AR) \
|
||||||
|
CFLAGS="$(GIT_CFLAGS)" \
|
||||||
|
LDFLAGS="$(GIT_LDFLAGS)" \
|
||||||
|
EXTLIBS="$(GIT_EXTLIBS)" \
|
||||||
|
uname_S=MontaukOS \
|
||||||
|
uname_M=x86_64 \
|
||||||
|
uname_O=MontaukOS \
|
||||||
|
uname_R=1 \
|
||||||
|
uname_P=x86_64 \
|
||||||
|
uname_V=1 \
|
||||||
|
prefix=$(GIT_PREFIX) \
|
||||||
|
GIT_VERSION=$(GIT_VERSION_STR) \
|
||||||
|
ZLIB_PATH=$(PORT_SYSROOT) \
|
||||||
|
BLK_SHA1=1 \
|
||||||
|
NO_CURL=1 \
|
||||||
|
NO_EXPAT=1 \
|
||||||
|
NO_OPENSSL=1 \
|
||||||
|
NO_ICONV=1 \
|
||||||
|
NO_GETTEXT=1 \
|
||||||
|
NO_PTHREADS=1 \
|
||||||
|
NO_PERL=1 \
|
||||||
|
NO_PYTHON=1 \
|
||||||
|
NO_TCLTK=1 \
|
||||||
|
NO_REGEX=1 \
|
||||||
|
NO_MMAP=1 \
|
||||||
|
NO_IPV6=1 \
|
||||||
|
NO_INET_NTOP=1 \
|
||||||
|
NO_INET_PTON=1 \
|
||||||
|
NO_HSTRERROR=1 \
|
||||||
|
NO_SYS_SELECT_H=1 \
|
||||||
|
NO_UNIX_SOCKETS=1 \
|
||||||
|
NO_SETITIMER=1 \
|
||||||
|
NO_STRUCT_ITIMERVAL=1 \
|
||||||
|
NO_LIBGEN_H=1 \
|
||||||
|
NO_MEMMEM=1 \
|
||||||
|
NO_STRCASESTR=1 \
|
||||||
|
NO_STRLCPY=1 \
|
||||||
|
NO_STRTOUMAX=1 \
|
||||||
|
NO_MKDTEMP=1 \
|
||||||
|
NO_INITGROUPS=1 \
|
||||||
|
NO_GECOS_IN_PWENT=1 \
|
||||||
|
NO_NSEC=1 \
|
||||||
|
NO_INSTALL_HARDLINKS=1 \
|
||||||
|
NO_TRUSTABLE_FILEMODE=1 \
|
||||||
|
NO_SYMLINK_HEAD=1 \
|
||||||
|
INTERNAL_QSORT=1 \
|
||||||
|
OBJECT_CREATION_USES_RENAMES=1 \
|
||||||
|
DEFAULT_PAGER=cat
|
||||||
|
|
||||||
|
GIT_MAKE := MAKEFLAGS= $(MAKE)
|
||||||
|
|
||||||
|
.PHONY: all clean git-build install uninstall
|
||||||
|
|
||||||
|
all: git-build
|
||||||
|
|
||||||
|
$(COMPAT_LIB): $(wildcard compat/*.c) $(wildcard compat/*.h)
|
||||||
|
$(CC) $(GIT_CFLAGS) -c compat/montauk-compat.c -o compat/montauk-compat.o
|
||||||
|
$(AR) rcs $@ compat/montauk-compat.o
|
||||||
|
@echo "Built: $@"
|
||||||
|
|
||||||
|
# The templates/ sub-make builds the skeleton `git init` copies into a new
|
||||||
|
# repository (info/exclude, the sample hooks, the description file) into
|
||||||
|
# templates/blt. Without it every `git init` warns that templates are
|
||||||
|
# missing. It is a subdirectory build, not a target of the top Makefile, so
|
||||||
|
# it is invoked directly -- `make templates` up there just matches the
|
||||||
|
# directory name and does nothing.
|
||||||
|
git-build: $(COMPAT_LIB)
|
||||||
|
@$(call ns_patch,$(UPSTREAM))
|
||||||
|
@# git's link rule knows nothing about EXTLIBS or the crt objects, so a
|
||||||
|
@# rebuilt compat archive -- or a rebuilt libc, which is the one that
|
||||||
|
@# actually catches people out -- would otherwise leave the old binary
|
||||||
|
@# in place. Drop it and let the sub-make link again.
|
||||||
|
@for lib in $(GIT_EXTLIBS) $(GIT_CRT); do \
|
||||||
|
if [ -f $(GIT_BIN) ] && [ "$$lib" -nt $(GIT_BIN) ]; then \
|
||||||
|
echo " RELINK: $$(basename $$lib) is newer than $(GIT_BIN)"; \
|
||||||
|
rm -f $(GIT_BIN); \
|
||||||
|
fi; \
|
||||||
|
done
|
||||||
|
$(GIT_MAKE) -C $(UPSTREAM) $(GIT_ARGS) git
|
||||||
|
$(GIT_MAKE) -C $(UPSTREAM)/templates
|
||||||
|
@test -f $(GIT_BIN) || { echo "git: expected $(GIT_BIN)" >&2; exit 1; }
|
||||||
|
@echo "Built: $(GIT_BIN) ($$(stat -c%s $(GIT_BIN)) bytes)"
|
||||||
|
|
||||||
|
# Installed as a system program, not an app bundle: git is a command line
|
||||||
|
# tool with no window. It carries no manifest and no .port marker, so
|
||||||
|
# remember to uninstall before cutting a release ISO.
|
||||||
|
#
|
||||||
|
# The binary is STRIPPED on the way in -- the debug info is five sixths of
|
||||||
|
# its size (17 MB -> 3 MB), and it all has to fit in the ramdisk.
|
||||||
|
install: git-build
|
||||||
|
mkdir -p $(GIT_INSTALL)
|
||||||
|
$(TOOLCHAIN_PREFIX)strip -o $(GIT_INSTALL)/git.elf $(GIT_BIN)
|
||||||
|
mkdir -p $(GIT_STAGE)/share/git-core
|
||||||
|
rm -rf $(GIT_STAGE)/share/git-core/templates
|
||||||
|
cp -rL $(UPSTREAM)/templates/blt $(GIT_STAGE)/share/git-core/templates
|
||||||
|
@echo "installed: 0:/os/git.elf + $(GIT_PREFIX)/share/git-core/templates"
|
||||||
|
|
||||||
|
uninstall:
|
||||||
|
rm -rf $(GIT_INSTALL)/git.elf $(GIT_STAGE)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
@$(GIT_MAKE) -C $(UPSTREAM) $(GIT_ARGS) clean >/dev/null 2>&1 || true
|
||||||
|
@git -C $(UPSTREAM) checkout -- . 2>/dev/null || true
|
||||||
|
rm -f $(COMPAT_LIB) compat/*.o
|
||||||
@@ -0,0 +1,143 @@
|
|||||||
|
# git for MontaukOS
|
||||||
|
|
||||||
|
Upstream [git/git](https://github.com/git/git), pinned at **v2.39.5**
|
||||||
|
(`upstream/`, submodule). Reports itself as `2.39.5-montauk`.
|
||||||
|
|
||||||
|
Installs as a system program, not an app bundle:
|
||||||
|
|
||||||
|
| Path | What |
|
||||||
|
|------|------|
|
||||||
|
| `0:/os/git.elf` | the binary (~3 MB stripped) |
|
||||||
|
| `0:/os/git/share/git-core/templates` | `git init` skeleton |
|
||||||
|
| `0:/os/git/etc/gitconfig` | system config (not created by the install) |
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make -C dev-vcs/git # build
|
||||||
|
make -C dev-vcs/git install # -> MontaukOS/programs/bin/os/
|
||||||
|
```
|
||||||
|
|
||||||
|
`make ports` from the MontaukOS tree does both (git is in the top-level
|
||||||
|
Makefile's `SELF_INSTALL_PORTS`).
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
First run on hardware: `git` started, died at exit 128, printed nothing. Two
|
||||||
|
bugs, both outside git and both now fixed -- **needs a retest**:
|
||||||
|
|
||||||
|
1. **`sanitize_stdfds()` opened `/dev/null`**, which MontaukOS does not have,
|
||||||
|
so `xopen()` died before any command dispatched. That function guards
|
||||||
|
against inheriting closed descriptors, which cannot happen on a system that
|
||||||
|
spawns rather than forks; `patches/0002` makes it a no-op.
|
||||||
|
2. **Nothing git wrote with `write(2, ...)` ever appeared** -- which is why
|
||||||
|
the fatal error was invisible. The libc's `read()`/`write()` passed fd 0/1/2
|
||||||
|
straight to `SYS_READ`/`SYS_FWRITE` as file handles, and the kernel hands
|
||||||
|
out file handles starting at 0, so those numbers named real files. A
|
||||||
|
program's third `open()` owned "stderr". Fixed in the OS: the kernel no
|
||||||
|
longer allocates handles 0/1/2, and the libc routes them to the terminal.
|
||||||
|
|
||||||
|
Second run got as far as `git init` (works, repository created) and then hit
|
||||||
|
`fatal: detected dubious ownership`. That is git's safe.directory check
|
||||||
|
comparing `lstat()`'s `st_uid` against `geteuid()`; MontaukOS stores no
|
||||||
|
per-file owner, so it is false for every directory on the system. The port now
|
||||||
|
answers that check directly -- see `compat/montauk-ownership.h`, which also
|
||||||
|
records what has to change when the OS grows real ownership.
|
||||||
|
|
||||||
|
Confirmed working on hardware so far: startup, error output, `git init`, and
|
||||||
|
quoted arguments. The rest is still only what the build configuration
|
||||||
|
implies.
|
||||||
|
|
||||||
|
## What should work
|
||||||
|
|
||||||
|
Everything git does inside one process and one filesystem:
|
||||||
|
|
||||||
|
- `init`, `add`, `rm`, `mv`, `status`, `diff`, `commit`, `log`, `show`
|
||||||
|
- `branch`, `checkout`, `switch`, `restore`, `merge`, `reset`, `revert`
|
||||||
|
- `tag`, `stash`, `cherry-pick`, `rebase` (the builtin, non-interactive path)
|
||||||
|
- `cat-file`, `rev-parse`, `ls-files`, `ls-tree`, `hash-object`, `fsck`
|
||||||
|
- `config`, `blame`, `grep`, `describe`, `bisect`
|
||||||
|
- `clone`/`fetch`/`push` **against a local path**, e.g.
|
||||||
|
`git clone 0:/users/dan/repo`
|
||||||
|
|
||||||
|
## What does not work, and why
|
||||||
|
|
||||||
|
**Anything that starts another process.** MontaukOS spawns (`SYS_SPAWN`); it
|
||||||
|
has no `fork()`, no `exec()` and no `pipe()`, and a spawned process cannot
|
||||||
|
inherit a pipe for its stdio. Every git feature built on sub-processes fails
|
||||||
|
with `ENOSYS` rather than misbehaving:
|
||||||
|
|
||||||
|
- hooks (`pre-commit`, `commit-msg`, ...) -- the samples are installed but
|
||||||
|
never run
|
||||||
|
- `$EDITOR`, so `git commit` needs `-m` or `-F`, and interactive rebase and
|
||||||
|
`git add -p` are out
|
||||||
|
- the pager (harmless -- output goes straight to the terminal, and
|
||||||
|
`DEFAULT_PAGER=cat` is compiled in anyway)
|
||||||
|
- aliases that shell out (`!command`), `git submodule`, `difftool`,
|
||||||
|
`mergetool`, `git gc` (it re-runs itself as `git repack`)
|
||||||
|
|
||||||
|
**Network transports.** No `http://`, `https://`, `git://` or `ssh://`. The
|
||||||
|
port is built `NO_CURL NO_OPENSSL`, and the socket shims fail: MontaukOS TCP
|
||||||
|
lives behind IPC handles rather than file descriptors, and DNS resolution is
|
||||||
|
inside the kernel with no userspace interface. Local paths are the transport.
|
||||||
|
|
||||||
|
**Symlinks.** The VFS cannot create them, so git behaves as if
|
||||||
|
`core.symlinks=false` -- a symlink in a tree checks out as a regular file
|
||||||
|
containing the link target.
|
||||||
|
|
||||||
|
**File modes.** Built `NO_TRUSTABLE_FILEMODE`, so the executable bit is not
|
||||||
|
read back from the filesystem; `core.fileMode` defaults to false.
|
||||||
|
|
||||||
|
**Threads.** `NO_PTHREADS`, so packing, delta search and `git grep` are
|
||||||
|
single-threaded. Correct, just slower.
|
||||||
|
|
||||||
|
**`mmap`.** `NO_MMAP`, so pack and index access reads into memory instead of
|
||||||
|
mapping. Large repositories will be heavier on RAM than on Linux.
|
||||||
|
|
||||||
|
## Before the first commit
|
||||||
|
|
||||||
|
The environment MontaukOS hands a process is empty, so the port sets `HOME`
|
||||||
|
itself from the session user (`0:/users/<name>`) before git reads any config
|
||||||
|
-- that is what makes `--global` work. Set an identity:
|
||||||
|
|
||||||
|
```
|
||||||
|
git config --global user.name "Your Name"
|
||||||
|
git config --global user.email [email protected]
|
||||||
|
```
|
||||||
|
|
||||||
|
This is not optional. Left unset, git builds a fallback address from the
|
||||||
|
session user and the hostname -- and since `gethostname()` answers `montauk`,
|
||||||
|
with no domain in it, git marks the address bogus, appends `.(none)` and
|
||||||
|
refuses:
|
||||||
|
|
||||||
|
```
|
||||||
|
fatal: unable to auto-detect email address (got 'admin@montauk.(none)')
|
||||||
|
```
|
||||||
|
|
||||||
|
That is stock git behaviour on any host without a domain name, not something
|
||||||
|
the port introduces.
|
||||||
|
|
||||||
|
## Quoting
|
||||||
|
|
||||||
|
Arguments are split by the C runtime, not by a shell. `crt1.c` understands
|
||||||
|
`'...'` and `"..."` (added for this port), so
|
||||||
|
|
||||||
|
```
|
||||||
|
git commit -m "a message with spaces"
|
||||||
|
```
|
||||||
|
|
||||||
|
reaches git as three arguments. There is no escaping and no substitution --
|
||||||
|
a literal quote character cannot be passed.
|
||||||
|
|
||||||
|
## How the port is put together
|
||||||
|
|
||||||
|
`Makefile` drives upstream's own build with the toolchain and a long list of
|
||||||
|
`NO_*` knobs; the header comment there explains the ones that are not
|
||||||
|
self-evident. Two mechanisms fill the gaps:
|
||||||
|
|
||||||
|
- `compat/` -- headers MontaukOS has no equivalent of, plus
|
||||||
|
`libgitcompat.a` with the POSIX functions the libc does not carry. See
|
||||||
|
`compat/README`.
|
||||||
|
- `patches/` -- two patches: calling `montauk_startup()` from `main()`, and
|
||||||
|
emptying `sanitize_stdfds()`, which cannot run without a `/dev/null`.
|
||||||
|
|
||||||
|
Everything else upstream needed was already reachable through git's own
|
||||||
|
portability knobs, which is the reason this port is small.
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
compat/ -- what MontaukOS does not have, and what git gets instead.
|
||||||
|
|
||||||
|
Two kinds of file live here.
|
||||||
|
|
||||||
|
HEADER OVERLAYS use #include_next to pull in the Montauk libc's own header
|
||||||
|
and then add what git needs on top. They are found first because -I compat
|
||||||
|
precedes the libc include path.
|
||||||
|
|
||||||
|
unistd.h identity (getuid/geteuid/...), symlink/readlink/link,
|
||||||
|
chown, fsync, process groups, execl/execlp, getpass, alarm
|
||||||
|
signal.h the sigaction() family and NSIG
|
||||||
|
time.h localtime_r/gmtime_r/ctime_r/asctime_r
|
||||||
|
sys/stat.h S_ISUID/S_ISGID/S_ISVTX and the S_IF* type constants
|
||||||
|
sys/time.h ITIMER_* selectors
|
||||||
|
sys/types.h the BSD aliases (u_char, u_int, ...) the ISC-derived
|
||||||
|
inet_ntop/inet_pton want
|
||||||
|
|
||||||
|
STANDALONE HEADERS are for things the libc has no header for at all:
|
||||||
|
|
||||||
|
poll.h struct pollfd; the implementation reports everything ready
|
||||||
|
pwd.h struct passwd, answered from the session user
|
||||||
|
grp.h struct group; every lookup fails (no group database)
|
||||||
|
termios.h accepted and ignored; there is no line discipline
|
||||||
|
netdb.h resolver types; every lookup fails
|
||||||
|
syslog.h discards (only `git daemon` logs, and it is not built)
|
||||||
|
sys/socket.h BSD socket API; every call fails with ENOSYS
|
||||||
|
sys/un.h struct sockaddr_un, for the type only
|
||||||
|
sys/ioctl.h TIOCGWINSZ, which IS implemented (SYS_TERMSIZE)
|
||||||
|
sys/utsname.h uname(), static MontaukOS strings
|
||||||
|
sys/resource.h getrlimit says "unlimited"; getrusage fails
|
||||||
|
sys/statvfs.h fails; no filesystem statistics syscall
|
||||||
|
netinet/in.h, netinet/tcp.h, arpa/inet.h address types and byte order
|
||||||
|
|
||||||
|
THESE FILES ARE NOT SHIMS. They are the port's own code:
|
||||||
|
|
||||||
|
montauk-paths.h force-included into every translation unit, ahead of
|
||||||
|
git-compat-util.h, so git recognises "0:/path" as an
|
||||||
|
absolute path. Without it git treats every MontaukOS
|
||||||
|
absolute path as relative and prepends the cwd to it.
|
||||||
|
montauk-ownership.h also force-included: answers git's safe.directory
|
||||||
|
"is this repository mine?" check with yes. MontaukOS
|
||||||
|
stores no per-file owner, so the default lstat-vs-geteuid
|
||||||
|
comparison is false for every directory on the system.
|
||||||
|
Read the header before changing it -- it says what this
|
||||||
|
gives up once the OS grows real ownership.
|
||||||
|
montauk-init.h declares montauk_startup(), called from main() by the
|
||||||
|
patch series.
|
||||||
|
|
||||||
|
montauk-compat.c the implementations, each commented with what it
|
||||||
|
really does. Built into libgitcompat.a, which sits on
|
||||||
|
the link line ahead of liblibc.a.
|
||||||
|
|
||||||
|
THE RULE followed throughout: a shim either does the real thing, or fails
|
||||||
|
with an errno git already knows how to report. Nothing silently pretends to
|
||||||
|
have done work it did not do -- with one deliberate exception, fsync(),
|
||||||
|
which reports success because git treats a failed fsync as fatal and would
|
||||||
|
abort every commit. That one is called out at its definition.
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
/*
|
||||||
|
* <arpa/inet.h> for the MontaukOS git port.
|
||||||
|
*
|
||||||
|
* inet_ntop/inet_pton are not declared here: the port builds with
|
||||||
|
* NO_INET_NTOP=1 / NO_INET_PTON=1, so git compiles its own portable versions
|
||||||
|
* (compat/inet_ntop.c, compat/inet_pton.c) and declares them itself in
|
||||||
|
* git-compat-util.h. Everything else an address needs comes from
|
||||||
|
* <netinet/in.h>.
|
||||||
|
*/
|
||||||
|
#ifndef _MONTAUK_GIT_ARPA_INET_H_
|
||||||
|
#define _MONTAUK_GIT_ARPA_INET_H_
|
||||||
|
|
||||||
|
#include <netinet/in.h>
|
||||||
|
|
||||||
|
in_addr_t inet_addr(const char *cp);
|
||||||
|
char *inet_ntoa(struct in_addr in);
|
||||||
|
|
||||||
|
#endif /* _MONTAUK_GIT_ARPA_INET_H_ */
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
* <grp.h> stub for the MontaukOS git port.
|
||||||
|
*
|
||||||
|
* Included by git-compat-util.h for the group lookups in daemon.c and
|
||||||
|
* setup.c's shared-repository handling. MontaukOS has no group database, so
|
||||||
|
* every lookup fails and git falls back to its "no group" paths.
|
||||||
|
*
|
||||||
|
* Implementation in montauk-compat.c.
|
||||||
|
*/
|
||||||
|
#ifndef _MONTAUK_GIT_GRP_H_
|
||||||
|
#define _MONTAUK_GIT_GRP_H_
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
struct group {
|
||||||
|
char *gr_name;
|
||||||
|
char *gr_passwd;
|
||||||
|
gid_t gr_gid;
|
||||||
|
char **gr_mem;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct group *getgrnam(const char *name);
|
||||||
|
struct group *getgrgid(gid_t gid);
|
||||||
|
|
||||||
|
#endif /* _MONTAUK_GIT_GRP_H_ */
|
||||||
@@ -0,0 +1,764 @@
|
|||||||
|
/*
|
||||||
|
* montauk-compat.c -- POSIX calls git needs that the Montauk libc does not
|
||||||
|
* have. Compiled into libgitcompat.a and placed on the link line ahead of
|
||||||
|
* liblibc.a.
|
||||||
|
*
|
||||||
|
* The rule followed here: a shim either does the real thing, or it fails with
|
||||||
|
* an errno git already knows how to report. Nothing pretends to have done
|
||||||
|
* work it did not do -- with one deliberate exception, fsync(), noted at its
|
||||||
|
* definition.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <pwd.h>
|
||||||
|
#include <grp.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
#include <poll.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <termios.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <sys/utsname.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* <montauk.h> is the C mirror of the syscall ABI (the C++ SDK headers under montauk/
|
||||||
|
* are C++). Only two calls are used below: SYS_GETUSER for the session user
|
||||||
|
* and SYS_TERMSIZE for the terminal geometry.
|
||||||
|
*/
|
||||||
|
#include <montauk.h>
|
||||||
|
|
||||||
|
/* ==== Identity =======================================================
|
||||||
|
*
|
||||||
|
* MontaukOS has named users but no numeric uid space: a process runs as
|
||||||
|
* whoever is logged into the session. A single fixed non-root id is reported,
|
||||||
|
* which makes git's two questions answer correctly -- "am I root?" (no, so it
|
||||||
|
* does not relax its safety checks) and "does this repository belong to me?"
|
||||||
|
* (yes, since every path reports the same owner).
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define MONTAUK_UID 1000
|
||||||
|
#define MONTAUK_GID 1000
|
||||||
|
|
||||||
|
uid_t getuid(void) { return MONTAUK_UID; }
|
||||||
|
uid_t geteuid(void) { return MONTAUK_UID; }
|
||||||
|
gid_t getgid(void) { return MONTAUK_GID; }
|
||||||
|
gid_t getegid(void) { return MONTAUK_GID; }
|
||||||
|
|
||||||
|
/* No process tree above us that userspace can see. */
|
||||||
|
pid_t getppid(void) { return 1; }
|
||||||
|
|
||||||
|
int setsid(void) { return getpid(); }
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Process groups do not exist: every process is its own group, and whatever
|
||||||
|
* is reading the terminal is the foreground job. Reporting getpid() from all
|
||||||
|
* three keeps progress.c's foreground test (tcgetpgrp(fd) == getpgid(0))
|
||||||
|
* true, so the progress meter is drawn instead of suppressed.
|
||||||
|
*/
|
||||||
|
pid_t getpgid(pid_t pid) { return pid ? pid : getpid(); }
|
||||||
|
pid_t getpgrp(void) { return getpid(); }
|
||||||
|
pid_t tcgetpgrp(int fd) { (void) fd; return getpid(); }
|
||||||
|
|
||||||
|
int setpgid(pid_t pid, pid_t pgid)
|
||||||
|
{
|
||||||
|
(void) pid; (void) pgid;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int gethostname(char *name, size_t len)
|
||||||
|
{
|
||||||
|
static const char host[] = "montauk";
|
||||||
|
|
||||||
|
if (!name || len == 0) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (len < sizeof(host)) {
|
||||||
|
errno = ENAMETOOLONG;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
memcpy(name, host, sizeof(host));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* No interval timers exist and SIGALRM is never delivered, so nothing is
|
||||||
|
* armed and nothing was pending. Only upload-pack's idle timeout calls this,
|
||||||
|
* and upload-pack needs sockets to be reachable at all.
|
||||||
|
*/
|
||||||
|
unsigned int alarm(unsigned int seconds)
|
||||||
|
{
|
||||||
|
(void) seconds;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ==== Passwd / group =================================================
|
||||||
|
*
|
||||||
|
* Answered from the session rather than from a database. git uses this for
|
||||||
|
* the fallback author identity and to expand "~"; a wrong-but-plausible entry
|
||||||
|
* beats a NULL return, which git reports as "You don't exist. Go away!".
|
||||||
|
*
|
||||||
|
* getpwnam() only recognises the current user: there is nobody else to look
|
||||||
|
* up, and inventing entries for arbitrary names would make "~someone" resolve
|
||||||
|
* to a directory that does not exist.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static char pw_name_buf[64];
|
||||||
|
static char pw_dir_buf[128];
|
||||||
|
|
||||||
|
static struct passwd *fill_passwd(void)
|
||||||
|
{
|
||||||
|
static struct passwd pw;
|
||||||
|
int len = (int) _mtk_syscall2(MTK_SYS_GETUSER,
|
||||||
|
(long) pw_name_buf,
|
||||||
|
(long) sizeof(pw_name_buf));
|
||||||
|
|
||||||
|
if (len <= 0 || pw_name_buf[0] == '\0')
|
||||||
|
strcpy(pw_name_buf, "user");
|
||||||
|
pw_name_buf[sizeof(pw_name_buf) - 1] = '\0';
|
||||||
|
|
||||||
|
snprintf(pw_dir_buf, sizeof(pw_dir_buf), "0:/users/%s", pw_name_buf);
|
||||||
|
|
||||||
|
pw.pw_name = pw_name_buf;
|
||||||
|
pw.pw_passwd = (char *) "";
|
||||||
|
pw.pw_uid = MONTAUK_UID;
|
||||||
|
pw.pw_gid = MONTAUK_GID;
|
||||||
|
pw.pw_gecos = pw_name_buf;
|
||||||
|
pw.pw_dir = pw_dir_buf;
|
||||||
|
pw.pw_shell = (char *) "";
|
||||||
|
|
||||||
|
return &pw;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Called first thing from main() (patches/0001-montauk-startup.patch).
|
||||||
|
*
|
||||||
|
* MontaukOS starts every process with an EMPTY environment -- SYS_SPAWN
|
||||||
|
* transfers nothing, so environ is bare no matter who launched git. Without
|
||||||
|
* HOME, git silently skips the per-user config: ~/.gitconfig never resolves,
|
||||||
|
* so `git config --global user.email ...` has nowhere to write, and the
|
||||||
|
* repository config becomes the only place a setting can live. Pointing HOME
|
||||||
|
* at the session user's directory is what makes --global work.
|
||||||
|
*
|
||||||
|
* This is deliberately NOT an __attribute__((constructor)): programs linked
|
||||||
|
* with programs/link.ld get no .init_array section (the script does not name
|
||||||
|
* one, so the linker drops it), which means constructors silently never run.
|
||||||
|
* A call from main() is the only reliable hook.
|
||||||
|
*
|
||||||
|
* An inherited HOME, should the environment ever start carrying one, wins.
|
||||||
|
*/
|
||||||
|
void montauk_startup(void)
|
||||||
|
{
|
||||||
|
if (getenv("HOME"))
|
||||||
|
return;
|
||||||
|
setenv("HOME", fill_passwd()->pw_dir, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct passwd *getpwuid(uid_t uid)
|
||||||
|
{
|
||||||
|
if (uid != MONTAUK_UID) {
|
||||||
|
errno = 0; /* "no such user", not an error, per POSIX */
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return fill_passwd();
|
||||||
|
}
|
||||||
|
|
||||||
|
struct passwd *getpwnam(const char *name)
|
||||||
|
{
|
||||||
|
struct passwd *pw = fill_passwd();
|
||||||
|
|
||||||
|
if (!name || strcmp(name, pw->pw_name) != 0) {
|
||||||
|
errno = 0;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return pw;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct group *getgrnam(const char *name)
|
||||||
|
{
|
||||||
|
(void) name;
|
||||||
|
errno = 0;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct group *getgrgid(gid_t gid)
|
||||||
|
{
|
||||||
|
(void) gid;
|
||||||
|
errno = 0;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ==== Filesystem =====================================================
|
||||||
|
*
|
||||||
|
* Symlinks have no representation in the MontaukOS VFS (the ramdisk drops
|
||||||
|
* them, ext2 support is read-side only for them), so these fail rather than
|
||||||
|
* silently creating something else. git's core.symlinks=false path handles
|
||||||
|
* the failure by writing a regular file whose contents are the link target.
|
||||||
|
*/
|
||||||
|
|
||||||
|
int symlink(const char *target, const char *linkpath)
|
||||||
|
{
|
||||||
|
(void) target; (void) linkpath;
|
||||||
|
errno = EPERM;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t readlink(const char *path, char *buf, size_t bufsiz)
|
||||||
|
{
|
||||||
|
(void) path; (void) buf; (void) bufsiz;
|
||||||
|
errno = EINVAL; /* "not a symbolic link" -- and nothing here is */
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int link(const char *oldpath, const char *newpath)
|
||||||
|
{
|
||||||
|
(void) oldpath; (void) newpath;
|
||||||
|
errno = EPERM; /* no hard links; callers fall back to copying */
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int chown(const char *path, uid_t owner, gid_t group)
|
||||||
|
{
|
||||||
|
(void) path; (void) owner; (void) group;
|
||||||
|
errno = EPERM;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int lchown(const char *path, uid_t owner, gid_t group)
|
||||||
|
{
|
||||||
|
(void) path; (void) owner; (void) group;
|
||||||
|
errno = EPERM;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fchown(int fd, uid_t owner, gid_t group)
|
||||||
|
{
|
||||||
|
(void) fd; (void) owner; (void) group;
|
||||||
|
errno = EPERM;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* fsync()/fdatasync() report success without doing anything, which is the one
|
||||||
|
* shim here that claims more than it delivers. Failing instead is worse: git
|
||||||
|
* treats a failed fsync as a fatal error and aborts the write, so every
|
||||||
|
* commit would fail. Data does reach the disk -- the kernel writes through
|
||||||
|
* its block cache -- but there is no per-descriptor barrier, so ordering
|
||||||
|
* across a power loss is not guaranteed. SYS_FS_SYNC (whole-filesystem
|
||||||
|
* flush, used by shutdown) is the closest thing that exists.
|
||||||
|
*/
|
||||||
|
int fsync(int fd)
|
||||||
|
{
|
||||||
|
(void) fd;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fdatasync(int fd)
|
||||||
|
{
|
||||||
|
(void) fd;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ==== Terminal input =================================================
|
||||||
|
*
|
||||||
|
* MontaukOS has no /dev/tty, so compat/terminal.c compiles its fallback
|
||||||
|
* branch, and every interactive prompt git makes -- passphrases included --
|
||||||
|
* arrives here.
|
||||||
|
*
|
||||||
|
* THE INPUT IS ECHOED. The libc reads stdin through SYS_GETCHAR and echoes
|
||||||
|
* each character itself; there is no line discipline to turn that off (see
|
||||||
|
* tcsetattr below, which is why it cannot help). Nothing in this port prompts
|
||||||
|
* for a secret today, since there are no network transports and no credential
|
||||||
|
* helper, but anything added later must not assume this hides what is typed.
|
||||||
|
*/
|
||||||
|
char *getpass(const char *prompt)
|
||||||
|
{
|
||||||
|
static char buf[256];
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
if (prompt && *prompt) {
|
||||||
|
fputs(prompt, stderr);
|
||||||
|
fflush(stderr);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fgets(buf, sizeof(buf), stdin))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
len = strlen(buf);
|
||||||
|
while (len > 0 && (buf[len - 1] == '\n' || buf[len - 1] == '\r'))
|
||||||
|
buf[--len] = '\0';
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ==== exec ===========================================================
|
||||||
|
*
|
||||||
|
* execl()/execlp() collect their variadic arguments into an argv and defer to
|
||||||
|
* the libc's execv()/execvp(). Those fail with ENOSYS -- MontaukOS spawns new
|
||||||
|
* processes (SYS_SPAWN) rather than replacing the current image -- so these
|
||||||
|
* fail too, which is what the one caller (`git help`, launching a man viewer)
|
||||||
|
* is prepared for.
|
||||||
|
*
|
||||||
|
* MAX_EXEC_ARGV is generous for that caller; a longer list is rejected
|
||||||
|
* outright rather than silently truncated into a different command.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define MAX_EXEC_ARGV 64
|
||||||
|
|
||||||
|
static int collect_exec_argv(const char *arg, va_list ap,
|
||||||
|
const char *argv[], int max)
|
||||||
|
{
|
||||||
|
int argc = 0;
|
||||||
|
|
||||||
|
argv[argc++] = arg;
|
||||||
|
while (argc < max) {
|
||||||
|
const char *next = va_arg(ap, const char *);
|
||||||
|
|
||||||
|
argv[argc++] = next;
|
||||||
|
if (!next)
|
||||||
|
return argc;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int execl(const char *path, const char *arg, ...)
|
||||||
|
{
|
||||||
|
const char *argv[MAX_EXEC_ARGV];
|
||||||
|
va_list ap;
|
||||||
|
int argc;
|
||||||
|
|
||||||
|
va_start(ap, arg);
|
||||||
|
argc = collect_exec_argv(arg, ap, argv, MAX_EXEC_ARGV);
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
if (argc < 0) {
|
||||||
|
errno = E2BIG;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return execv(path, (char *const *) argv);
|
||||||
|
}
|
||||||
|
|
||||||
|
int execlp(const char *file, const char *arg, ...)
|
||||||
|
{
|
||||||
|
const char *argv[MAX_EXEC_ARGV];
|
||||||
|
va_list ap;
|
||||||
|
int argc;
|
||||||
|
|
||||||
|
va_start(ap, arg);
|
||||||
|
argc = collect_exec_argv(arg, ap, argv, MAX_EXEC_ARGV);
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
if (argc < 0) {
|
||||||
|
errno = E2BIG;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return execvp(file, (char *const *) argv);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ==== Signals ========================================================
|
||||||
|
*
|
||||||
|
* sigaction() over signal(). MontaukOS delivers SIGINT and nothing else, and
|
||||||
|
* has no per-process signal mask, so sa_mask and sa_flags (including
|
||||||
|
* SA_RESTART) are accepted and ignored -- there are no restartable syscalls
|
||||||
|
* to interrupt. The sigset_t operations maintain a bitmask that nothing
|
||||||
|
* reads; they exist so callers that build a mask before installing a handler
|
||||||
|
* compile and behave predictably.
|
||||||
|
*/
|
||||||
|
|
||||||
|
int sigemptyset(sigset_t *set)
|
||||||
|
{
|
||||||
|
if (set)
|
||||||
|
*set = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sigfillset(sigset_t *set)
|
||||||
|
{
|
||||||
|
if (set)
|
||||||
|
*set = ~(sigset_t)0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sigaddset(sigset_t *set, int signum)
|
||||||
|
{
|
||||||
|
if (!set || signum <= 0 || signum >= (int)(8 * sizeof(sigset_t))) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
*set |= (sigset_t)1 << signum;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sigdelset(sigset_t *set, int signum)
|
||||||
|
{
|
||||||
|
if (!set || signum <= 0 || signum >= (int)(8 * sizeof(sigset_t))) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
*set &= ~((sigset_t)1 << signum);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sigismember(const sigset_t *set, int signum)
|
||||||
|
{
|
||||||
|
if (!set || signum <= 0 || signum >= (int)(8 * sizeof(sigset_t))) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return (*set & ((sigset_t)1 << signum)) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sigaction(int signum, const struct sigaction *act, struct sigaction *old)
|
||||||
|
{
|
||||||
|
sighandler_t prev;
|
||||||
|
|
||||||
|
if (act) {
|
||||||
|
prev = signal(signum, act->sa_handler);
|
||||||
|
} else {
|
||||||
|
/* Query only: read the handler back by reinstalling it. */
|
||||||
|
prev = signal(signum, SIG_DFL);
|
||||||
|
if (prev != SIG_ERR)
|
||||||
|
signal(signum, prev);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prev == SIG_ERR)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (old) {
|
||||||
|
memset(old, 0, sizeof(*old));
|
||||||
|
old->sa_handler = prev;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* No signal mask exists to block anything with; report an empty one. */
|
||||||
|
int sigprocmask(int how, const sigset_t *set, sigset_t *old)
|
||||||
|
{
|
||||||
|
(void) how; (void) set;
|
||||||
|
if (old)
|
||||||
|
*old = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ==== Time ===========================================================
|
||||||
|
*
|
||||||
|
* The libc's localtime()/gmtime() use a static buffer; copy it out. Safe
|
||||||
|
* here because the port is built NO_PTHREADS.
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct tm *localtime_r(const time_t *timep, struct tm *result)
|
||||||
|
{
|
||||||
|
struct tm *tmp = localtime(timep);
|
||||||
|
|
||||||
|
if (!tmp || !result)
|
||||||
|
return NULL;
|
||||||
|
*result = *tmp;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct tm *gmtime_r(const time_t *timep, struct tm *result)
|
||||||
|
{
|
||||||
|
struct tm *tmp = gmtime(timep);
|
||||||
|
|
||||||
|
if (!tmp || !result)
|
||||||
|
return NULL;
|
||||||
|
*result = *tmp;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *ctime_r(const time_t *timep, char *buf)
|
||||||
|
{
|
||||||
|
char *s = ctime(timep);
|
||||||
|
|
||||||
|
if (!s || !buf)
|
||||||
|
return NULL;
|
||||||
|
strcpy(buf, s); /* caller supplies the POSIX-required 26 bytes */
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *asctime_r(const struct tm *tm, char *buf)
|
||||||
|
{
|
||||||
|
char *s = asctime(tm);
|
||||||
|
|
||||||
|
if (!s || !buf)
|
||||||
|
return NULL;
|
||||||
|
strcpy(buf, s);
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ==== Terminal =======================================================
|
||||||
|
*
|
||||||
|
* There is no line discipline to query or configure: the libc reads stdin a
|
||||||
|
* character at a time through SYS_GETCHAR and echoes it itself. tcgetattr
|
||||||
|
* hands back a zeroed struct and tcsetattr accepts whatever it is given,
|
||||||
|
* because git treats a tcgetattr failure as "this is not a terminal" -- which
|
||||||
|
* would disable interactive prompting altogether -- and dies on some
|
||||||
|
* tcsetattr failures.
|
||||||
|
*
|
||||||
|
* The practical consequence: echo cannot be turned off, so a passphrase typed
|
||||||
|
* at a git prompt is visible. Nothing in this port prompts for one today
|
||||||
|
* (there are no network transports and no credential helper).
|
||||||
|
*/
|
||||||
|
|
||||||
|
int tcgetattr(int fd, struct termios *t)
|
||||||
|
{
|
||||||
|
if (!isatty(fd)) {
|
||||||
|
errno = ENOTTY;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (t)
|
||||||
|
memset(t, 0, sizeof(*t));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int tcsetattr(int fd, int actions, const struct termios *t)
|
||||||
|
{
|
||||||
|
(void) actions; (void) t;
|
||||||
|
if (!isatty(fd)) {
|
||||||
|
errno = ENOTTY;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The only ioctl git issues is TIOCGWINSZ, and MontaukOS answers that one
|
||||||
|
* directly through SYS_TERMSIZE, so the progress meter and `git column` get
|
||||||
|
* the real terminal width instead of the 80-column fallback. Every other
|
||||||
|
* request fails.
|
||||||
|
*/
|
||||||
|
int ioctl(int fd, unsigned long request, ...)
|
||||||
|
{
|
||||||
|
(void) fd;
|
||||||
|
|
||||||
|
if (request == TIOCGWINSZ) {
|
||||||
|
va_list ap;
|
||||||
|
struct winsize *ws;
|
||||||
|
int cols = 0, rows = 0;
|
||||||
|
|
||||||
|
va_start(ap, request);
|
||||||
|
ws = va_arg(ap, struct winsize *);
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
if (!ws) {
|
||||||
|
errno = EFAULT;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
mtk_termsize(&cols, &rows);
|
||||||
|
if (cols <= 0 || rows <= 0) {
|
||||||
|
errno = ENOTTY;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ws->ws_col = (unsigned short) cols;
|
||||||
|
ws->ws_row = (unsigned short) rows;
|
||||||
|
ws->ws_xpixel = 0;
|
||||||
|
ws->ws_ypixel = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
errno = ENOTTY;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ==== poll ===========================================================
|
||||||
|
*
|
||||||
|
* Only ever called on the pipe ends of a spawned sub-process, and MontaukOS
|
||||||
|
* has neither pipe() nor fork(), so this is unreachable in practice. It
|
||||||
|
* reports every descriptor ready rather than failing, so that a caller which
|
||||||
|
* does get here proceeds to the read() that reports the real error instead of
|
||||||
|
* spinning.
|
||||||
|
*/
|
||||||
|
int poll(struct pollfd *fds, nfds_t nfds, int timeout)
|
||||||
|
{
|
||||||
|
nfds_t i;
|
||||||
|
|
||||||
|
(void) timeout;
|
||||||
|
|
||||||
|
if (!fds)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
for (i = 0; i < nfds; i++)
|
||||||
|
fds[i].revents = fds[i].events;
|
||||||
|
|
||||||
|
return (int) nfds;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ==== Sockets ========================================================
|
||||||
|
*
|
||||||
|
* MontaukOS TCP lives behind IPC handles (montauk::socket), not file
|
||||||
|
* descriptors, and git hands transport descriptors to sub-processes. Until
|
||||||
|
* something bridges the two, every call fails with ENOSYS; `git clone
|
||||||
|
* git://...` reports that it cannot connect. Local paths are unaffected.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define SOCKET_STUB_BODY do { errno = ENOSYS; return -1; } while (0)
|
||||||
|
|
||||||
|
int socket(int domain, int type, int protocol)
|
||||||
|
{
|
||||||
|
(void) domain; (void) type; (void) protocol;
|
||||||
|
SOCKET_STUB_BODY;
|
||||||
|
}
|
||||||
|
|
||||||
|
int connect(int fd, const struct sockaddr *addr, socklen_t len)
|
||||||
|
{
|
||||||
|
(void) fd; (void) addr; (void) len;
|
||||||
|
SOCKET_STUB_BODY;
|
||||||
|
}
|
||||||
|
|
||||||
|
int bind(int fd, const struct sockaddr *addr, socklen_t len)
|
||||||
|
{
|
||||||
|
(void) fd; (void) addr; (void) len;
|
||||||
|
SOCKET_STUB_BODY;
|
||||||
|
}
|
||||||
|
|
||||||
|
int listen(int fd, int backlog)
|
||||||
|
{
|
||||||
|
(void) fd; (void) backlog;
|
||||||
|
SOCKET_STUB_BODY;
|
||||||
|
}
|
||||||
|
|
||||||
|
int accept(int fd, struct sockaddr *addr, socklen_t *len)
|
||||||
|
{
|
||||||
|
(void) fd; (void) addr; (void) len;
|
||||||
|
SOCKET_STUB_BODY;
|
||||||
|
}
|
||||||
|
|
||||||
|
int shutdown(int fd, int how)
|
||||||
|
{
|
||||||
|
(void) fd; (void) how;
|
||||||
|
SOCKET_STUB_BODY;
|
||||||
|
}
|
||||||
|
|
||||||
|
int setsockopt(int fd, int level, int name, const void *val, socklen_t len)
|
||||||
|
{
|
||||||
|
(void) fd; (void) level; (void) name; (void) val; (void) len;
|
||||||
|
SOCKET_STUB_BODY;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getsockopt(int fd, int level, int name, void *val, socklen_t *len)
|
||||||
|
{
|
||||||
|
(void) fd; (void) level; (void) name; (void) val; (void) len;
|
||||||
|
SOCKET_STUB_BODY;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getsockname(int fd, struct sockaddr *addr, socklen_t *len)
|
||||||
|
{
|
||||||
|
(void) fd; (void) addr; (void) len;
|
||||||
|
SOCKET_STUB_BODY;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getpeername(int fd, struct sockaddr *addr, socklen_t *len)
|
||||||
|
{
|
||||||
|
(void) fd; (void) addr; (void) len;
|
||||||
|
SOCKET_STUB_BODY;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t send(int fd, const void *buf, size_t len, int flags)
|
||||||
|
{
|
||||||
|
(void) fd; (void) buf; (void) len; (void) flags;
|
||||||
|
errno = ENOSYS;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t recv(int fd, void *buf, size_t len, int flags)
|
||||||
|
{
|
||||||
|
(void) fd; (void) buf; (void) len; (void) flags;
|
||||||
|
errno = ENOSYS;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ==== Name resolution ================================================
|
||||||
|
*
|
||||||
|
* DNS is resolved inside the kernel network stack; no resolver interface is
|
||||||
|
* exposed to userspace. Lookups therefore fail, which git reports as an
|
||||||
|
* unresolvable host.
|
||||||
|
*/
|
||||||
|
|
||||||
|
int h_errno;
|
||||||
|
|
||||||
|
struct hostent *gethostbyname(const char *name)
|
||||||
|
{
|
||||||
|
(void) name;
|
||||||
|
h_errno = HOST_NOT_FOUND;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct servent *getservbyname(const char *name, const char *proto)
|
||||||
|
{
|
||||||
|
(void) name; (void) proto;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getaddrinfo(const char *node, const char *service,
|
||||||
|
const struct addrinfo *hints, struct addrinfo **res)
|
||||||
|
{
|
||||||
|
(void) node; (void) service; (void) hints;
|
||||||
|
if (res)
|
||||||
|
*res = NULL;
|
||||||
|
return EAI_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void freeaddrinfo(struct addrinfo *res)
|
||||||
|
{
|
||||||
|
(void) res;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getnameinfo(const struct sockaddr *sa, socklen_t salen,
|
||||||
|
char *host, socklen_t hostlen,
|
||||||
|
char *serv, socklen_t servlen, int flags)
|
||||||
|
{
|
||||||
|
(void) sa; (void) salen; (void) host; (void) hostlen;
|
||||||
|
(void) serv; (void) servlen; (void) flags;
|
||||||
|
return EAI_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *gai_strerror(int errcode)
|
||||||
|
{
|
||||||
|
(void) errcode;
|
||||||
|
return "name resolution is not available on MontaukOS";
|
||||||
|
}
|
||||||
|
|
||||||
|
in_addr_t inet_addr(const char *cp)
|
||||||
|
{
|
||||||
|
(void) cp;
|
||||||
|
return INADDR_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *inet_ntoa(struct in_addr in)
|
||||||
|
{
|
||||||
|
static char buf[16];
|
||||||
|
unsigned int a = (unsigned int) in.s_addr;
|
||||||
|
|
||||||
|
snprintf(buf, sizeof(buf), "%u.%u.%u.%u",
|
||||||
|
a & 0xff, (a >> 8) & 0xff, (a >> 16) & 0xff, (a >> 24) & 0xff);
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ==== uname ==========================================================
|
||||||
|
*
|
||||||
|
* Static answers: there is no kernel interface for the release/version
|
||||||
|
* strings. Feeds `git version --build-options` and `git bugreport`.
|
||||||
|
*/
|
||||||
|
int uname(struct utsname *buf)
|
||||||
|
{
|
||||||
|
if (!buf) {
|
||||||
|
errno = EFAULT;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
memset(buf, 0, sizeof(*buf));
|
||||||
|
strcpy(buf->sysname, "MontaukOS");
|
||||||
|
strcpy(buf->nodename, "montauk");
|
||||||
|
strcpy(buf->release, "");
|
||||||
|
strcpy(buf->version, "");
|
||||||
|
strcpy(buf->machine, "x86_64");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
/*
|
||||||
|
* montauk-init.h -- one-time startup fixups, called from main().
|
||||||
|
*
|
||||||
|
* See montauk_startup() in montauk-compat.c for what it does and why it
|
||||||
|
* cannot be an __attribute__((constructor)): programs linked with
|
||||||
|
* programs/link.ld have no .init_array section, so constructors never run.
|
||||||
|
*/
|
||||||
|
#ifndef _MONTAUK_GIT_INIT_H_
|
||||||
|
#define _MONTAUK_GIT_INIT_H_
|
||||||
|
|
||||||
|
void montauk_startup(void);
|
||||||
|
|
||||||
|
#endif /* _MONTAUK_GIT_INIT_H_ */
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* montauk-ownership.h -- answer git's "is this repository mine?" question.
|
||||||
|
*
|
||||||
|
* FORCE-INCLUDED into every translation unit (-include, see the port
|
||||||
|
* Makefile), because it has to win the #ifndef guard in git-compat-util.h.
|
||||||
|
*
|
||||||
|
* git's safe.directory check exists because on a multi-user system a
|
||||||
|
* repository owned by somebody else can attack you the moment you run git in
|
||||||
|
* it -- its config names hooks, pagers and external diff commands that would
|
||||||
|
* run as you. The default implementation answers by comparing the owner
|
||||||
|
* reported by lstat() against geteuid().
|
||||||
|
*
|
||||||
|
* Neither side of that comparison means anything on MontaukOS. The VFS stores
|
||||||
|
* no owner at all -- stat() zero-fills st_uid, on the ramdisk, on ext2 and on
|
||||||
|
* FAT32 alike -- and there is one session user, so the compat layer reports a
|
||||||
|
* single fixed uid for everybody (see montauk-compat.c). The comparison is
|
||||||
|
* therefore 0 == 1000, false for EVERY repository: unpatched, git refuses to
|
||||||
|
* work in any directory on the system, which is what
|
||||||
|
*
|
||||||
|
* fatal: detected dubious ownership in repository at '0:/users/...'
|
||||||
|
*
|
||||||
|
* was. Picking the uid to match the zero from stat() would only paper over it,
|
||||||
|
* and would mean claiming to be root.
|
||||||
|
*
|
||||||
|
* So answer honestly: every file belongs to the one user, and the check has
|
||||||
|
* nothing to distinguish. This is the same seam Windows uses for the same
|
||||||
|
* reason (compat/mingw.h defines is_path_owned_by_current_sid here).
|
||||||
|
*
|
||||||
|
* WHAT THIS GIVES UP: nothing today, and something real later. When MontaukOS
|
||||||
|
* grows per-file ownership, this must go back to comparing owners -- otherwise
|
||||||
|
* git in another user's repository will run that repository's hooks as you.
|
||||||
|
*/
|
||||||
|
#ifndef _MONTAUK_GIT_OWNERSHIP_H_
|
||||||
|
#define _MONTAUK_GIT_OWNERSHIP_H_
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A macro, not a function: this header is force-included before anything
|
||||||
|
* declares struct strbuf, so the report parameter cannot be named in a
|
||||||
|
* prototype here. Both arguments are still evaluated-as-void so an unused
|
||||||
|
* variable at a call site does not start warning.
|
||||||
|
*/
|
||||||
|
#define is_path_owned_by_current_user(path, report) \
|
||||||
|
((void)(path), (void)(report), 1)
|
||||||
|
|
||||||
|
#endif /* _MONTAUK_GIT_OWNERSHIP_H_ */
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
* montauk-paths.h -- teach git what an absolute path looks like on MontaukOS.
|
||||||
|
*
|
||||||
|
* FORCE-INCLUDED into every translation unit (-include, see the port
|
||||||
|
* Makefile), because it has to win the #ifndef guards in git-compat-util.h.
|
||||||
|
*
|
||||||
|
* MontaukOS paths are drive-prefixed: "0:/users/dan/repo". getcwd() returns
|
||||||
|
* one of those, and the kernel's path resolver (kernel/src/Api/Path.hpp)
|
||||||
|
* accepts three forms -- "N:/abs", "/abs" (the current drive's root) and
|
||||||
|
* "rel/ative" against the process cwd.
|
||||||
|
*
|
||||||
|
* Without this header git's is_absolute_path() is just is_dir_sep(path[0]),
|
||||||
|
* so every MontaukOS absolute path reads as RELATIVE and git helpfully
|
||||||
|
* prepends the cwd to it: "0:/users/dan/repo/0:/users/dan/repo". That breaks
|
||||||
|
* setup_git_directory(), real_path() and every worktree path it stores.
|
||||||
|
*
|
||||||
|
* The fix reuses git's existing Windows abstraction rather than inventing
|
||||||
|
* one: has_dos_drive_prefix / skip_dos_drive_prefix / offset_1st_component
|
||||||
|
* are exactly the hooks the DOS "C:/" case goes through, and a MontaukOS
|
||||||
|
* drive differs only in being a digit run rather than a letter. The bodies
|
||||||
|
* below mirror compat/win32/path-utils.c, minus the UNC handling, which has
|
||||||
|
* no MontaukOS equivalent.
|
||||||
|
*/
|
||||||
|
#ifndef _MONTAUK_GIT_PATHS_H_
|
||||||
|
#define _MONTAUK_GIT_PATHS_H_
|
||||||
|
|
||||||
|
/*
|
||||||
|
* "12:" as well as "0:" -- ParseDrivePrefix() accepts a multi-digit drive
|
||||||
|
* number, so this must too, or a two-digit drive would silently be treated
|
||||||
|
* as a relative path.
|
||||||
|
*/
|
||||||
|
static inline int montauk_has_drive_prefix(const char *path)
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
if (!path)
|
||||||
|
return 0;
|
||||||
|
while (path[i] >= '0' && path[i] <= '9')
|
||||||
|
i++;
|
||||||
|
return i > 0 && path[i] == ':';
|
||||||
|
}
|
||||||
|
#define has_dos_drive_prefix montauk_has_drive_prefix
|
||||||
|
|
||||||
|
static inline int montauk_skip_drive_prefix(char **path)
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
if (!montauk_has_drive_prefix(*path))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
while ((*path)[i] >= '0' && (*path)[i] <= '9')
|
||||||
|
i++;
|
||||||
|
*path += i + 1; /* the digits and the ':' */
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
#define skip_dos_drive_prefix montauk_skip_drive_prefix
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Length of the leading "root" of the path: the drive prefix plus one
|
||||||
|
* separator if present. Mirrors win32_offset_1st_component().
|
||||||
|
*/
|
||||||
|
static inline int montauk_offset_1st_component(const char *path)
|
||||||
|
{
|
||||||
|
char *pos = (char *)path;
|
||||||
|
|
||||||
|
montauk_skip_drive_prefix(&pos);
|
||||||
|
return (int)(pos - path) + (*pos == '/');
|
||||||
|
}
|
||||||
|
#define offset_1st_component montauk_offset_1st_component
|
||||||
|
|
||||||
|
#endif /* _MONTAUK_GIT_PATHS_H_ */
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
/*
|
||||||
|
* <netdb.h> for the MontaukOS git port.
|
||||||
|
*
|
||||||
|
* The port builds NO_IPV6=1, so git resolves names through gethostbyname()
|
||||||
|
* and getservbyname() rather than getaddrinfo(). Both fail here: MontaukOS
|
||||||
|
* resolves DNS inside the kernel network stack, and no userspace resolver
|
||||||
|
* interface is exposed. `git clone git://host/repo` therefore reports that it
|
||||||
|
* cannot look up the host. getaddrinfo() is still declared because
|
||||||
|
* git-compat-util.h references the types unconditionally.
|
||||||
|
*
|
||||||
|
* Implementations in montauk-compat.c.
|
||||||
|
*/
|
||||||
|
#ifndef _MONTAUK_GIT_NETDB_H_
|
||||||
|
#define _MONTAUK_GIT_NETDB_H_
|
||||||
|
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
|
||||||
|
struct hostent {
|
||||||
|
char *h_name;
|
||||||
|
char **h_aliases;
|
||||||
|
int h_addrtype;
|
||||||
|
int h_length;
|
||||||
|
char **h_addr_list;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define h_addr h_addr_list[0]
|
||||||
|
|
||||||
|
struct servent {
|
||||||
|
char *s_name;
|
||||||
|
char **s_aliases;
|
||||||
|
int s_port;
|
||||||
|
char *s_proto;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct addrinfo {
|
||||||
|
int ai_flags;
|
||||||
|
int ai_family;
|
||||||
|
int ai_socktype;
|
||||||
|
int ai_protocol;
|
||||||
|
socklen_t ai_addrlen;
|
||||||
|
struct sockaddr *ai_addr;
|
||||||
|
char *ai_canonname;
|
||||||
|
struct addrinfo *ai_next;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define AI_PASSIVE 0x0001
|
||||||
|
#define AI_CANONNAME 0x0002
|
||||||
|
#define AI_NUMERICHOST 0x0004
|
||||||
|
#define AI_NUMERICSERV 0x0400
|
||||||
|
|
||||||
|
#define NI_MAXHOST 1025
|
||||||
|
#define NI_MAXSERV 32
|
||||||
|
#define NI_NUMERICHOST 1
|
||||||
|
#define NI_NUMERICSERV 2
|
||||||
|
#define NI_NAMEREQD 8
|
||||||
|
|
||||||
|
#define EAI_NONAME -2
|
||||||
|
#define EAI_AGAIN -3
|
||||||
|
#define EAI_FAIL -4
|
||||||
|
#define EAI_FAMILY -6
|
||||||
|
#define EAI_SYSTEM -11
|
||||||
|
|
||||||
|
#define HOST_NOT_FOUND 1
|
||||||
|
#define TRY_AGAIN 2
|
||||||
|
#define NO_RECOVERY 3
|
||||||
|
#define NO_DATA 4
|
||||||
|
|
||||||
|
extern int h_errno;
|
||||||
|
|
||||||
|
struct hostent *gethostbyname(const char *name);
|
||||||
|
struct servent *getservbyname(const char *name, const char *proto);
|
||||||
|
|
||||||
|
int getaddrinfo(const char *node, const char *service,
|
||||||
|
const struct addrinfo *hints, struct addrinfo **res);
|
||||||
|
void freeaddrinfo(struct addrinfo *res);
|
||||||
|
int getnameinfo(const struct sockaddr *sa, socklen_t salen,
|
||||||
|
char *host, socklen_t hostlen,
|
||||||
|
char *serv, socklen_t servlen, int flags);
|
||||||
|
const char *gai_strerror(int errcode);
|
||||||
|
|
||||||
|
#endif /* _MONTAUK_GIT_NETDB_H_ */
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
/*
|
||||||
|
* <netinet/in.h> for the MontaukOS git port.
|
||||||
|
*
|
||||||
|
* Address types only. See compat/sys/socket.h for why nothing here connects
|
||||||
|
* to anything yet. The byte-order helpers are real (x86-64 is little-endian,
|
||||||
|
* so they are byte swaps).
|
||||||
|
*/
|
||||||
|
#ifndef _MONTAUK_GIT_NETINET_IN_H_
|
||||||
|
#define _MONTAUK_GIT_NETINET_IN_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
|
||||||
|
typedef uint16_t in_port_t;
|
||||||
|
typedef uint32_t in_addr_t;
|
||||||
|
|
||||||
|
struct in_addr {
|
||||||
|
in_addr_t s_addr;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct in6_addr {
|
||||||
|
union {
|
||||||
|
uint8_t __u6_addr8[16];
|
||||||
|
uint16_t __u6_addr16[8];
|
||||||
|
uint32_t __u6_addr32[4];
|
||||||
|
} __in6_u;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define s6_addr __in6_u.__u6_addr8
|
||||||
|
#define s6_addr16 __in6_u.__u6_addr16
|
||||||
|
#define s6_addr32 __in6_u.__u6_addr32
|
||||||
|
|
||||||
|
struct sockaddr_in {
|
||||||
|
sa_family_t sin_family;
|
||||||
|
in_port_t sin_port;
|
||||||
|
struct in_addr sin_addr;
|
||||||
|
unsigned char sin_zero[8];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct sockaddr_in6 {
|
||||||
|
sa_family_t sin6_family;
|
||||||
|
in_port_t sin6_port;
|
||||||
|
uint32_t sin6_flowinfo;
|
||||||
|
struct in6_addr sin6_addr;
|
||||||
|
uint32_t sin6_scope_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define INADDR_ANY ((in_addr_t)0x00000000)
|
||||||
|
#define INADDR_LOOPBACK ((in_addr_t)0x7f000001)
|
||||||
|
#define INADDR_NONE ((in_addr_t)0xffffffff)
|
||||||
|
|
||||||
|
#define INET_ADDRSTRLEN 16
|
||||||
|
#define INET6_ADDRSTRLEN 46
|
||||||
|
|
||||||
|
#define IPPROTO_IP 0
|
||||||
|
#define IPPROTO_TCP 6
|
||||||
|
#define IPPROTO_UDP 17
|
||||||
|
|
||||||
|
static inline uint16_t htons(uint16_t x) { return __builtin_bswap16(x); }
|
||||||
|
static inline uint16_t ntohs(uint16_t x) { return __builtin_bswap16(x); }
|
||||||
|
static inline uint32_t htonl(uint32_t x) { return __builtin_bswap32(x); }
|
||||||
|
static inline uint32_t ntohl(uint32_t x) { return __builtin_bswap32(x); }
|
||||||
|
|
||||||
|
#endif /* _MONTAUK_GIT_NETINET_IN_H_ */
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
/*
|
||||||
|
* <netinet/tcp.h> for the MontaukOS git port.
|
||||||
|
*
|
||||||
|
* Socket option names only; setsockopt() itself fails here. See
|
||||||
|
* compat/sys/socket.h.
|
||||||
|
*/
|
||||||
|
#ifndef _MONTAUK_GIT_NETINET_TCP_H_
|
||||||
|
#define _MONTAUK_GIT_NETINET_TCP_H_
|
||||||
|
|
||||||
|
#define TCP_NODELAY 1
|
||||||
|
#define TCP_KEEPIDLE 4
|
||||||
|
#define TCP_KEEPINTVL 5
|
||||||
|
#define TCP_KEEPCNT 6
|
||||||
|
|
||||||
|
#endif /* _MONTAUK_GIT_NETINET_TCP_H_ */
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* <poll.h> for the MontaukOS git port.
|
||||||
|
*
|
||||||
|
* git polls only descriptors it created itself with pipe() -- the stdout and
|
||||||
|
* stderr of a spawned sub-process (run-command.c's pump loop, sideband
|
||||||
|
* demultiplexing). MontaukOS has neither pipe() nor fork(), so those code
|
||||||
|
* paths are unreachable at runtime; this header exists so they compile, and
|
||||||
|
* the implementation reports every descriptor ready so that a caller which
|
||||||
|
* somehow gets here makes progress into the read() that reports the real
|
||||||
|
* error, instead of spinning in the poll loop forever.
|
||||||
|
*
|
||||||
|
* git's own compat/poll (NO_POLL=1) is not used: it is written against
|
||||||
|
* select() and <sys/select.h>, neither of which exists here.
|
||||||
|
*
|
||||||
|
* Implementation in montauk-compat.c.
|
||||||
|
*/
|
||||||
|
#ifndef _MONTAUK_GIT_POLL_H_
|
||||||
|
#define _MONTAUK_GIT_POLL_H_
|
||||||
|
|
||||||
|
typedef unsigned long nfds_t;
|
||||||
|
|
||||||
|
struct pollfd {
|
||||||
|
int fd;
|
||||||
|
short events;
|
||||||
|
short revents;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define POLLIN 0x001
|
||||||
|
#define POLLPRI 0x002
|
||||||
|
#define POLLOUT 0x004
|
||||||
|
#define POLLERR 0x008
|
||||||
|
#define POLLHUP 0x010
|
||||||
|
#define POLLNVAL 0x020
|
||||||
|
|
||||||
|
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
|
||||||
|
|
||||||
|
#endif /* _MONTAUK_GIT_POLL_H_ */
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* <pwd.h> for the MontaukOS git port.
|
||||||
|
*
|
||||||
|
* MontaukOS has users (0:/users/<name>, see montauk/user.h) but no
|
||||||
|
* /etc/passwd database and no numeric uid model -- everything runs as the
|
||||||
|
* session user. getpwuid()/getpwnam() therefore answer from the current
|
||||||
|
* session rather than from a database, which is exactly what git wants them
|
||||||
|
* for: the fallback identity in ident.c ("You gotta tell me who you are") and
|
||||||
|
* the home directory behind "~".
|
||||||
|
*
|
||||||
|
* Implementation in montauk-compat.c.
|
||||||
|
*/
|
||||||
|
#ifndef _MONTAUK_GIT_PWD_H_
|
||||||
|
#define _MONTAUK_GIT_PWD_H_
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
struct passwd {
|
||||||
|
char *pw_name;
|
||||||
|
char *pw_passwd;
|
||||||
|
uid_t pw_uid;
|
||||||
|
gid_t pw_gid;
|
||||||
|
char *pw_gecos;
|
||||||
|
char *pw_dir;
|
||||||
|
char *pw_shell;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct passwd *getpwuid(uid_t uid);
|
||||||
|
struct passwd *getpwnam(const char *name);
|
||||||
|
|
||||||
|
#endif /* _MONTAUK_GIT_PWD_H_ */
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* <signal.h> overlay for the MontaukOS git port.
|
||||||
|
*
|
||||||
|
* The libc supplies signal()/raise()/kill() and the signal numbers; this adds
|
||||||
|
* the sigaction() family that git uses in a handful of places (fast-import's
|
||||||
|
* SIGUSR1 checkpoint handler, sigchain's SIGPIPE and SIGINT handling).
|
||||||
|
*
|
||||||
|
* MontaukOS delivers only SIGINT, and has no signal masks: a handler is
|
||||||
|
* either installed or it is not. sigaction() below is therefore a wrapper
|
||||||
|
* around signal() that honours sa_handler and ignores sa_mask and sa_flags,
|
||||||
|
* and the sigset_t operations manipulate a plain bitmask that nothing
|
||||||
|
* consults. See montauk-compat.c.
|
||||||
|
*/
|
||||||
|
#ifndef _MONTAUK_GIT_SIGNAL_H_
|
||||||
|
#define _MONTAUK_GIT_SIGNAL_H_
|
||||||
|
|
||||||
|
#include_next <signal.h>
|
||||||
|
|
||||||
|
/* One past the highest signal number the libc names (SIGSTOP, 19). */
|
||||||
|
#define NSIG 32
|
||||||
|
|
||||||
|
typedef unsigned long sigset_t;
|
||||||
|
|
||||||
|
struct sigaction {
|
||||||
|
sighandler_t sa_handler;
|
||||||
|
sigset_t sa_mask;
|
||||||
|
int sa_flags;
|
||||||
|
void (*sa_restorer)(void);
|
||||||
|
};
|
||||||
|
|
||||||
|
#define SA_RESTART 0x10000000
|
||||||
|
#define SA_NOCLDSTOP 0x00000001
|
||||||
|
#define SA_SIGINFO 0x00000004
|
||||||
|
#define SA_NODEFER 0x40000000
|
||||||
|
#define SA_RESETHAND 0x80000000
|
||||||
|
|
||||||
|
#define SIG_BLOCK 0
|
||||||
|
#define SIG_UNBLOCK 1
|
||||||
|
#define SIG_SETMASK 2
|
||||||
|
|
||||||
|
int sigemptyset(sigset_t *set);
|
||||||
|
int sigfillset(sigset_t *set);
|
||||||
|
int sigaddset(sigset_t *set, int signum);
|
||||||
|
int sigdelset(sigset_t *set, int signum);
|
||||||
|
int sigismember(const sigset_t *set, int signum);
|
||||||
|
int sigaction(int signum, const struct sigaction *act, struct sigaction *old);
|
||||||
|
int sigprocmask(int how, const sigset_t *set, sigset_t *old);
|
||||||
|
|
||||||
|
#endif /* _MONTAUK_GIT_SIGNAL_H_ */
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* <sys/ioctl.h> for the MontaukOS git port.
|
||||||
|
*
|
||||||
|
* git uses exactly one ioctl: TIOCGWINSZ, to find the terminal width for
|
||||||
|
* `git column`, the progress meter and `git help -a`. MontaukOS has no ioctl
|
||||||
|
* syscall, so the request fails and git falls back to its default 80 columns.
|
||||||
|
*
|
||||||
|
* Implementation in montauk-compat.c.
|
||||||
|
*/
|
||||||
|
#ifndef _MONTAUK_GIT_SYS_IOCTL_H_
|
||||||
|
#define _MONTAUK_GIT_SYS_IOCTL_H_
|
||||||
|
|
||||||
|
#define TIOCGWINSZ 0x5413
|
||||||
|
|
||||||
|
struct winsize {
|
||||||
|
unsigned short ws_row;
|
||||||
|
unsigned short ws_col;
|
||||||
|
unsigned short ws_xpixel;
|
||||||
|
unsigned short ws_ypixel;
|
||||||
|
};
|
||||||
|
|
||||||
|
int ioctl(int fd, unsigned long request, ...);
|
||||||
|
|
||||||
|
#endif /* _MONTAUK_GIT_SYS_IOCTL_H_ */
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
* <sys/resource.h> stub for the MontaukOS git port.
|
||||||
|
*
|
||||||
|
* Reached only through git-compat-util.h's unconditional include and
|
||||||
|
* trace2's resource accounting, which is compiled out without getrusage.
|
||||||
|
* MontaukOS keeps no per-process resource accounting, so getrlimit reports
|
||||||
|
* "unlimited" and everything else fails.
|
||||||
|
*/
|
||||||
|
#ifndef _MONTAUK_GIT_SYS_RESOURCE_H_
|
||||||
|
#define _MONTAUK_GIT_SYS_RESOURCE_H_
|
||||||
|
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#define RLIMIT_NOFILE 7
|
||||||
|
|
||||||
|
typedef unsigned long rlim_t;
|
||||||
|
|
||||||
|
#define RLIM_INFINITY (~(rlim_t)0)
|
||||||
|
|
||||||
|
struct rlimit {
|
||||||
|
rlim_t rlim_cur;
|
||||||
|
rlim_t rlim_max;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct rusage {
|
||||||
|
struct timeval ru_utime;
|
||||||
|
struct timeval ru_stime;
|
||||||
|
long ru_maxrss;
|
||||||
|
long ru_minflt;
|
||||||
|
long ru_majflt;
|
||||||
|
long ru_inblock;
|
||||||
|
long ru_oublock;
|
||||||
|
long ru_nvcsw;
|
||||||
|
long ru_nivcsw;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define RUSAGE_SELF 0
|
||||||
|
|
||||||
|
static inline int getrlimit(int resource, struct rlimit *rlim)
|
||||||
|
{
|
||||||
|
(void) resource;
|
||||||
|
if (rlim) {
|
||||||
|
rlim->rlim_cur = RLIM_INFINITY;
|
||||||
|
rlim->rlim_max = RLIM_INFINITY;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int setrlimit(int resource, const struct rlimit *rlim)
|
||||||
|
{
|
||||||
|
(void) resource; (void) rlim;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int getrusage(int who, struct rusage *usage)
|
||||||
|
{
|
||||||
|
(void) who; (void) usage;
|
||||||
|
errno = ENOSYS;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* _MONTAUK_GIT_SYS_RESOURCE_H_ */
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
/*
|
||||||
|
* <sys/socket.h> for the MontaukOS git port.
|
||||||
|
*
|
||||||
|
* MontaukOS does have TCP -- the kernel exposes it as IPC handles through
|
||||||
|
* montauk::socket(), not as BSD descriptors -- but nothing bridges the two
|
||||||
|
* into the fd space that read()/write() use, and git's transports assume a
|
||||||
|
* descriptor it can hand to a sub-process. Until that bridge exists, every
|
||||||
|
* call here fails with ENOSYS, which surfaces as "unable to connect" from
|
||||||
|
* `git clone git://...` rather than as a crash.
|
||||||
|
*
|
||||||
|
* That leaves the local transports -- init, add, commit, log, diff, and
|
||||||
|
* clone/fetch/push against a path on disk -- which are what this port is for.
|
||||||
|
*
|
||||||
|
* Implementations in montauk-compat.c.
|
||||||
|
*/
|
||||||
|
#ifndef _MONTAUK_GIT_SYS_SOCKET_H_
|
||||||
|
#define _MONTAUK_GIT_SYS_SOCKET_H_
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
typedef unsigned int socklen_t;
|
||||||
|
typedef unsigned short sa_family_t;
|
||||||
|
|
||||||
|
#define AF_UNSPEC 0
|
||||||
|
#define AF_UNIX 1
|
||||||
|
#define AF_LOCAL 1
|
||||||
|
#define AF_INET 2
|
||||||
|
#define AF_INET6 10
|
||||||
|
|
||||||
|
#define PF_UNSPEC AF_UNSPEC
|
||||||
|
#define PF_UNIX AF_UNIX
|
||||||
|
#define PF_INET AF_INET
|
||||||
|
#define PF_INET6 AF_INET6
|
||||||
|
|
||||||
|
#define SOCK_STREAM 1
|
||||||
|
#define SOCK_DGRAM 2
|
||||||
|
|
||||||
|
#define SOL_SOCKET 1
|
||||||
|
|
||||||
|
#define SO_REUSEADDR 2
|
||||||
|
#define SO_ERROR 4
|
||||||
|
#define SO_KEEPALIVE 9
|
||||||
|
#define SO_LINGER 13
|
||||||
|
|
||||||
|
#define SHUT_RD 0
|
||||||
|
#define SHUT_WR 1
|
||||||
|
#define SHUT_RDWR 2
|
||||||
|
|
||||||
|
#define MSG_PEEK 2
|
||||||
|
|
||||||
|
struct sockaddr {
|
||||||
|
sa_family_t sa_family;
|
||||||
|
char sa_data[14];
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Large enough for sockaddr_in6, aligned like the real thing. */
|
||||||
|
struct sockaddr_storage {
|
||||||
|
sa_family_t ss_family;
|
||||||
|
unsigned long __ss_align;
|
||||||
|
char __ss_padding[112];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct linger {
|
||||||
|
int l_onoff;
|
||||||
|
int l_linger;
|
||||||
|
};
|
||||||
|
|
||||||
|
int socket(int domain, int type, int protocol);
|
||||||
|
int connect(int fd, const struct sockaddr *addr, socklen_t len);
|
||||||
|
int bind(int fd, const struct sockaddr *addr, socklen_t len);
|
||||||
|
int listen(int fd, int backlog);
|
||||||
|
int accept(int fd, struct sockaddr *addr, socklen_t *len);
|
||||||
|
int shutdown(int fd, int how);
|
||||||
|
int setsockopt(int fd, int level, int name, const void *val, socklen_t len);
|
||||||
|
int getsockopt(int fd, int level, int name, void *val, socklen_t *len);
|
||||||
|
int getsockname(int fd, struct sockaddr *addr, socklen_t *len);
|
||||||
|
int getpeername(int fd, struct sockaddr *addr, socklen_t *len);
|
||||||
|
ssize_t send(int fd, const void *buf, size_t len, int flags);
|
||||||
|
ssize_t recv(int fd, void *buf, size_t len, int flags);
|
||||||
|
|
||||||
|
#endif /* _MONTAUK_GIT_SYS_SOCKET_H_ */
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* <sys/stat.h> overlay for the MontaukOS git port.
|
||||||
|
*
|
||||||
|
* The libc header comes in through #include_next; this only adds the mode
|
||||||
|
* bits it leaves out. They are not idle definitions:
|
||||||
|
*
|
||||||
|
* S_ISUID builtin/fast-import.c borrows it as a private in-memory flag
|
||||||
|
* (NO_DELTA), so it must exist even though no file ever carries
|
||||||
|
* it on MontaukOS.
|
||||||
|
* S_ISGID setup.c uses it for shared-repository directory modes.
|
||||||
|
* S_ISVTX referenced when normalising modes read from an index.
|
||||||
|
*
|
||||||
|
* The S_IF* type constants below round out the set for the same reason: git
|
||||||
|
* masks against them. MontaukOS never reports a fifo, socket or block device
|
||||||
|
* from stat(), and the libc's S_ISFIFO/S_ISSOCK/S_ISBLK are hard-coded false
|
||||||
|
* to say exactly that -- they are deliberately left alone here.
|
||||||
|
*/
|
||||||
|
#ifndef _MONTAUK_GIT_SYS_STAT_H_
|
||||||
|
#define _MONTAUK_GIT_SYS_STAT_H_
|
||||||
|
|
||||||
|
#include_next <sys/stat.h>
|
||||||
|
|
||||||
|
#ifndef S_ISUID
|
||||||
|
#define S_ISUID 04000
|
||||||
|
#endif
|
||||||
|
#ifndef S_ISGID
|
||||||
|
#define S_ISGID 02000
|
||||||
|
#endif
|
||||||
|
#ifndef S_ISVTX
|
||||||
|
#define S_ISVTX 01000
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef S_IFIFO
|
||||||
|
#define S_IFIFO 0010000
|
||||||
|
#endif
|
||||||
|
#ifndef S_IFBLK
|
||||||
|
#define S_IFBLK 0060000
|
||||||
|
#endif
|
||||||
|
#ifndef S_IFSOCK
|
||||||
|
#define S_IFSOCK 0140000
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* _MONTAUK_GIT_SYS_STAT_H_ */
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* <sys/statvfs.h> stub for the MontaukOS git port.
|
||||||
|
*
|
||||||
|
* git calls statvfs() in one place: fsmonitor's "is this a network
|
||||||
|
* filesystem" probe, which this port does not build. MontaukOS exposes no
|
||||||
|
* filesystem statistics syscall, so the call fails and callers take their
|
||||||
|
* "unknown filesystem" branch.
|
||||||
|
*/
|
||||||
|
#ifndef _MONTAUK_GIT_SYS_STATVFS_H_
|
||||||
|
#define _MONTAUK_GIT_SYS_STATVFS_H_
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
typedef unsigned long fsblkcnt_t;
|
||||||
|
typedef unsigned long fsfilcnt_t;
|
||||||
|
|
||||||
|
struct statvfs {
|
||||||
|
unsigned long f_bsize;
|
||||||
|
unsigned long f_frsize;
|
||||||
|
fsblkcnt_t f_blocks;
|
||||||
|
fsblkcnt_t f_bfree;
|
||||||
|
fsblkcnt_t f_bavail;
|
||||||
|
fsfilcnt_t f_files;
|
||||||
|
fsfilcnt_t f_ffree;
|
||||||
|
fsfilcnt_t f_favail;
|
||||||
|
unsigned long f_fsid;
|
||||||
|
unsigned long f_flag;
|
||||||
|
unsigned long f_namemax;
|
||||||
|
unsigned long f_type;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline int statvfs(const char *path, struct statvfs *buf)
|
||||||
|
{
|
||||||
|
(void) path; (void) buf;
|
||||||
|
errno = ENOSYS;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int fstatvfs(int fd, struct statvfs *buf)
|
||||||
|
{
|
||||||
|
(void) fd; (void) buf;
|
||||||
|
errno = ENOSYS;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* _MONTAUK_GIT_SYS_STATVFS_H_ */
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* <sys/time.h> overlay for the MontaukOS git port.
|
||||||
|
*
|
||||||
|
* Adds the ITIMER_* selectors on top of the libc header. MontaukOS has no
|
||||||
|
* interval timers, so the port is built NO_SETITIMER=1 and NO_STRUCT_ITIMERVAL=1
|
||||||
|
* -- git supplies its own struct itimerval and a setitimer() that returns
|
||||||
|
* success without arming anything. The selectors still have to exist, because
|
||||||
|
* the call sites name them (builtin/log.c's early-output timer, progress.c).
|
||||||
|
*
|
||||||
|
* What that costs: `git log`'s half-second early-output nudge never fires, so
|
||||||
|
* the first commits appear when the traversal produces them rather than on a
|
||||||
|
* timer. Nothing depends on it for correctness.
|
||||||
|
*/
|
||||||
|
#ifndef _MONTAUK_GIT_SYS_TIME_H_
|
||||||
|
#define _MONTAUK_GIT_SYS_TIME_H_
|
||||||
|
|
||||||
|
#include_next <sys/time.h>
|
||||||
|
|
||||||
|
#define ITIMER_REAL 0
|
||||||
|
#define ITIMER_VIRTUAL 1
|
||||||
|
#define ITIMER_PROF 2
|
||||||
|
|
||||||
|
#endif /* _MONTAUK_GIT_SYS_TIME_H_ */
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
/*
|
||||||
|
* <sys/types.h> overlay for the MontaukOS git port.
|
||||||
|
*
|
||||||
|
* Adds the historical BSD type aliases on top of the libc header. git's
|
||||||
|
* compat/inet_ntop.c and compat/inet_pton.c come from the ISC resolver and
|
||||||
|
* are written in terms of u_char/u_int; both are compiled here because the
|
||||||
|
* port sets NO_INET_NTOP=1 / NO_INET_PTON=1.
|
||||||
|
*/
|
||||||
|
#ifndef _MONTAUK_GIT_SYS_TYPES_H_
|
||||||
|
#define _MONTAUK_GIT_SYS_TYPES_H_
|
||||||
|
|
||||||
|
#include_next <sys/types.h>
|
||||||
|
|
||||||
|
typedef unsigned char u_char;
|
||||||
|
typedef unsigned short u_short;
|
||||||
|
typedef unsigned int u_int;
|
||||||
|
typedef unsigned long u_long;
|
||||||
|
|
||||||
|
typedef char *caddr_t;
|
||||||
|
|
||||||
|
#endif /* _MONTAUK_GIT_SYS_TYPES_H_ */
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
/*
|
||||||
|
* <sys/un.h> for the MontaukOS git port.
|
||||||
|
*
|
||||||
|
* Included unconditionally by git-compat-util.h. The port builds with
|
||||||
|
* NO_UNIX_SOCKETS=1, so nothing constructs one of these -- the type just has
|
||||||
|
* to exist.
|
||||||
|
*/
|
||||||
|
#ifndef _MONTAUK_GIT_SYS_UN_H_
|
||||||
|
#define _MONTAUK_GIT_SYS_UN_H_
|
||||||
|
|
||||||
|
#include <sys/socket.h>
|
||||||
|
|
||||||
|
struct sockaddr_un {
|
||||||
|
sa_family_t sun_family;
|
||||||
|
char sun_path[108];
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* _MONTAUK_GIT_SYS_UN_H_ */
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* <sys/utsname.h> for the MontaukOS git port.
|
||||||
|
*
|
||||||
|
* Used by help.c for the "uname" lines of `git version --build-options` and
|
||||||
|
* by the bugreport builtin. Answers static MontaukOS strings; there is no
|
||||||
|
* kernel interface for the release/version fields yet.
|
||||||
|
*
|
||||||
|
* Implementation in montauk-compat.c.
|
||||||
|
*/
|
||||||
|
#ifndef _MONTAUK_GIT_SYS_UTSNAME_H_
|
||||||
|
#define _MONTAUK_GIT_SYS_UTSNAME_H_
|
||||||
|
|
||||||
|
struct utsname {
|
||||||
|
char sysname[65];
|
||||||
|
char nodename[65];
|
||||||
|
char release[65];
|
||||||
|
char version[65];
|
||||||
|
char machine[65];
|
||||||
|
};
|
||||||
|
|
||||||
|
int uname(struct utsname *buf);
|
||||||
|
|
||||||
|
#endif /* _MONTAUK_GIT_SYS_UTSNAME_H_ */
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* <syslog.h> stub for the MontaukOS git port.
|
||||||
|
*
|
||||||
|
* git-compat-util.h includes this unconditionally, but the only caller is
|
||||||
|
* daemon.c (`git daemon`), which this port does not build -- there are no
|
||||||
|
* sockets to serve on. The declarations exist so the include resolves; the
|
||||||
|
* inline bodies discard everything, which is the honest behaviour on a system
|
||||||
|
* with no system logger.
|
||||||
|
*/
|
||||||
|
#ifndef _MONTAUK_GIT_SYSLOG_H_
|
||||||
|
#define _MONTAUK_GIT_SYSLOG_H_
|
||||||
|
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
#define LOG_EMERG 0
|
||||||
|
#define LOG_ALERT 1
|
||||||
|
#define LOG_CRIT 2
|
||||||
|
#define LOG_ERR 3
|
||||||
|
#define LOG_WARNING 4
|
||||||
|
#define LOG_NOTICE 5
|
||||||
|
#define LOG_INFO 6
|
||||||
|
#define LOG_DEBUG 7
|
||||||
|
|
||||||
|
#define LOG_PID 0x01
|
||||||
|
#define LOG_CONS 0x02
|
||||||
|
#define LOG_NDELAY 0x08
|
||||||
|
|
||||||
|
#define LOG_DAEMON (3 << 3)
|
||||||
|
#define LOG_USER (1 << 3)
|
||||||
|
|
||||||
|
static inline void openlog(const char *ident, int option, int facility)
|
||||||
|
{
|
||||||
|
(void) ident; (void) option; (void) facility;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void closelog(void) { }
|
||||||
|
|
||||||
|
static inline void syslog(int priority, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
(void) priority; (void) fmt;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* _MONTAUK_GIT_SYSLOG_H_ */
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* <termios.h> for the MontaukOS git port.
|
||||||
|
*
|
||||||
|
* compat/terminal.c uses tcgetattr/tcsetattr to turn off echo while reading a
|
||||||
|
* passphrase, and to put the terminal in raw mode for single-keystroke reads
|
||||||
|
* ("git add -p"). The MontaukOS terminal has no line discipline to configure:
|
||||||
|
* the libc reads stdin through SYS_GETCHAR and does its own echoing.
|
||||||
|
*
|
||||||
|
* These calls therefore succeed and do nothing rather than failing, because
|
||||||
|
* git treats a tcgetattr() failure as "not a terminal" and a tcsetattr()
|
||||||
|
* failure as fatal in some paths. The visible consequence is that a typed
|
||||||
|
* passphrase is echoed -- there is no credential helper in this port, so
|
||||||
|
* nothing prompts for one yet.
|
||||||
|
*
|
||||||
|
* Implementation in montauk-compat.c.
|
||||||
|
*/
|
||||||
|
#ifndef _MONTAUK_GIT_TERMIOS_H_
|
||||||
|
#define _MONTAUK_GIT_TERMIOS_H_
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
typedef unsigned int tcflag_t;
|
||||||
|
typedef unsigned char cc_t;
|
||||||
|
typedef unsigned int speed_t;
|
||||||
|
|
||||||
|
#define NCCS 32
|
||||||
|
|
||||||
|
struct termios {
|
||||||
|
tcflag_t c_iflag;
|
||||||
|
tcflag_t c_oflag;
|
||||||
|
tcflag_t c_cflag;
|
||||||
|
tcflag_t c_lflag;
|
||||||
|
cc_t c_line;
|
||||||
|
cc_t c_cc[NCCS];
|
||||||
|
speed_t c_ispeed;
|
||||||
|
speed_t c_ospeed;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Linux values, so code that masks bits it never applies still builds. */
|
||||||
|
#define ECHO 0000010
|
||||||
|
#define ECHONL 0000100
|
||||||
|
#define ICANON 0000002
|
||||||
|
#define ISIG 0000001
|
||||||
|
#define IEXTEN 0100000
|
||||||
|
#define IXON 0002000
|
||||||
|
#define ICRNL 0000400
|
||||||
|
#define OPOST 0000001
|
||||||
|
|
||||||
|
#define VMIN 6
|
||||||
|
#define VTIME 5
|
||||||
|
|
||||||
|
#define TCSANOW 0
|
||||||
|
#define TCSADRAIN 1
|
||||||
|
#define TCSAFLUSH 2
|
||||||
|
|
||||||
|
int tcgetattr(int fd, struct termios *t);
|
||||||
|
int tcsetattr(int fd, int actions, const struct termios *t);
|
||||||
|
|
||||||
|
#endif /* _MONTAUK_GIT_TERMIOS_H_ */
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
/*
|
||||||
|
* <time.h> overlay for the MontaukOS git port.
|
||||||
|
*
|
||||||
|
* Adds the reentrant conversions on top of the libc's own <time.h>. The libc
|
||||||
|
* has localtime()/gmtime() with the usual static buffer; the _r forms below
|
||||||
|
* are thin wrappers that copy that buffer out. Single-threaded programs see
|
||||||
|
* no difference, and git is built NO_PTHREADS here, so there is no shared
|
||||||
|
* buffer to race over.
|
||||||
|
*
|
||||||
|
* Implementations in montauk-compat.c.
|
||||||
|
*/
|
||||||
|
#ifndef _MONTAUK_GIT_TIME_H_
|
||||||
|
#define _MONTAUK_GIT_TIME_H_
|
||||||
|
|
||||||
|
#include_next <time.h>
|
||||||
|
|
||||||
|
struct tm *localtime_r(const time_t *timep, struct tm *result);
|
||||||
|
struct tm *gmtime_r(const time_t *timep, struct tm *result);
|
||||||
|
char *ctime_r(const time_t *timep, char *buf);
|
||||||
|
char *asctime_r(const struct tm *tm, char *buf);
|
||||||
|
|
||||||
|
#endif /* _MONTAUK_GIT_TIME_H_ */
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
/*
|
||||||
|
* <unistd.h> overlay for the MontaukOS git port.
|
||||||
|
*
|
||||||
|
* The Montauk libc's own <unistd.h> is pulled in with #include_next -- this
|
||||||
|
* file only ADDS the POSIX calls git expects and the libc does not declare.
|
||||||
|
* Keeping them here rather than in the libc is deliberate: each one below is
|
||||||
|
* either meaningless on MontaukOS today (there is one user, no groups, no
|
||||||
|
* symlinks in the ramdisk) or needs kernel support that does not exist, and a
|
||||||
|
* libc declaration would promise more than the system delivers.
|
||||||
|
*
|
||||||
|
* Implementations in montauk-compat.c, each documented there with what it
|
||||||
|
* actually does.
|
||||||
|
*/
|
||||||
|
#ifndef _MONTAUK_GIT_UNISTD_H_
|
||||||
|
#define _MONTAUK_GIT_UNISTD_H_
|
||||||
|
|
||||||
|
#include_next <unistd.h>
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
/* Identity. MontaukOS runs everything as the session user; these report a
|
||||||
|
* single fixed non-root id so that git's "am I root?" and "does this repo
|
||||||
|
* belong to me?" checks answer sensibly (yes, it is mine; no, I am not root). */
|
||||||
|
uid_t getuid(void);
|
||||||
|
uid_t geteuid(void);
|
||||||
|
gid_t getgid(void);
|
||||||
|
gid_t getegid(void);
|
||||||
|
pid_t getppid(void);
|
||||||
|
|
||||||
|
int gethostname(char *name, size_t len);
|
||||||
|
|
||||||
|
/* No interval timers and no SIGALRM delivery, so this arms nothing.
|
||||||
|
* upload-pack's idle timeout is its only caller here, and upload-pack cannot
|
||||||
|
* run without sockets anyway. */
|
||||||
|
unsigned int alarm(unsigned int seconds);
|
||||||
|
|
||||||
|
/* Symlinks. Neither the ramdisk nor the VFS represents them, so these fail;
|
||||||
|
* git falls back to writing a regular file with the link text, which is what
|
||||||
|
* core.symlinks=false already asks for. */
|
||||||
|
int symlink(const char *target, const char *linkpath);
|
||||||
|
ssize_t readlink(const char *path, char *buf, size_t bufsiz);
|
||||||
|
int link(const char *oldpath, const char *newpath);
|
||||||
|
|
||||||
|
/* Ownership. No uid/gid model to change anything to. */
|
||||||
|
int chown(const char *path, uid_t owner, gid_t group);
|
||||||
|
int lchown(const char *path, uid_t owner, gid_t group);
|
||||||
|
int fchown(int fd, uid_t owner, gid_t group);
|
||||||
|
|
||||||
|
/* Durability. The kernel writes through its block cache on close; there is no
|
||||||
|
* per-descriptor flush syscall, so these report success rather than failing a
|
||||||
|
* commit that is in fact on disk. See SYS_FS_SYNC for the whole-filesystem
|
||||||
|
* flush that shutdown uses. */
|
||||||
|
int fsync(int fd);
|
||||||
|
int fdatasync(int fd);
|
||||||
|
|
||||||
|
int setsid(void);
|
||||||
|
|
||||||
|
/* Process groups. MontaukOS has none: a process is its own group, and a
|
||||||
|
* program reading the terminal is by definition the foreground one. These
|
||||||
|
* answer consistently so that progress.c's "am I in the foreground?" test
|
||||||
|
* (tcgetpgrp(fd) == getpgid(0)) says yes and the progress meter is drawn. */
|
||||||
|
pid_t getpgid(pid_t pid);
|
||||||
|
pid_t getpgrp(void);
|
||||||
|
pid_t tcgetpgrp(int fd);
|
||||||
|
int setpgid(pid_t pid, pid_t pgid);
|
||||||
|
|
||||||
|
/* The variadic exec forms. The libc has execv/execvp (both of which fail:
|
||||||
|
* MontaukOS spawns rather than execs), so these only repack their arguments
|
||||||
|
* and hand them over -- they exist because `git help` calls execlp() to run
|
||||||
|
* the man viewer. */
|
||||||
|
int execl(const char *path, const char *arg, ...);
|
||||||
|
int execlp(const char *file, const char *arg, ...);
|
||||||
|
|
||||||
|
/* Reads a line from the terminal. It CANNOT suppress echo -- see the
|
||||||
|
* implementation. compat/terminal.c calls it for every interactive prompt on
|
||||||
|
* a platform without /dev/tty, which is what MontaukOS is. */
|
||||||
|
char *getpass(const char *prompt);
|
||||||
|
|
||||||
|
#endif /* _MONTAUK_GIT_UNISTD_H_ */
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
diff --git a/common-main.c b/common-main.c
|
||||||
|
index 0a22861f1..4653d18b5 100644
|
||||||
|
--- a/common-main.c
|
||||||
|
+++ b/common-main.c
|
||||||
|
@@ -1,6 +1,7 @@
|
||||||
|
#include "cache.h"
|
||||||
|
#include "exec-cmd.h"
|
||||||
|
#include "attr.h"
|
||||||
|
+#include <montauk-init.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Many parts of Git have subprograms communicate via pipe, expect the
|
||||||
|
@@ -28,6 +29,13 @@ int main(int argc, const char **argv)
|
||||||
|
int result;
|
||||||
|
struct strbuf tmp = STRBUF_INIT;
|
||||||
|
|
||||||
|
+ /*
|
||||||
|
+ * MontaukOS: fill in what the process environment cannot carry
|
||||||
|
+ * (HOME). Must run before any config is read. See
|
||||||
|
+ * compat/montauk-compat.c in the port.
|
||||||
|
+ */
|
||||||
|
+ montauk_startup();
|
||||||
|
+
|
||||||
|
trace2_initialize_clock();
|
||||||
|
|
||||||
|
/*
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
diff --git a/setup.c b/setup.c
|
||||||
|
index c3301f5ab..6652c1dff 100644
|
||||||
|
--- a/setup.c
|
||||||
|
+++ b/setup.c
|
||||||
|
@@ -1691,11 +1691,20 @@ const char *resolve_gitdir_gently(const char *suspect, int *return_error_code)
|
||||||
|
/* if any standard file descriptor is missing open it to /dev/null */
|
||||||
|
void sanitize_stdfds(void)
|
||||||
|
{
|
||||||
|
- int fd = xopen("/dev/null", O_RDWR);
|
||||||
|
- while (fd < 2)
|
||||||
|
- fd = xdup(fd);
|
||||||
|
- if (fd > 2)
|
||||||
|
- close(fd);
|
||||||
|
+ /*
|
||||||
|
+ * MontaukOS: nothing to sanitize, and nothing to sanitize it with.
|
||||||
|
+ *
|
||||||
|
+ * This guards against being started with 0/1/2 closed, so that a
|
||||||
|
+ * later open() cannot land on a standard descriptor and have die()
|
||||||
|
+ * write its message into a data file. On MontaukOS that cannot
|
||||||
|
+ * happen: the kernel gives every process the terminal on 0/1/2 (the
|
||||||
|
+ * libc routes those descriptors to SYS_PUTCHAR/SYS_GETCHAR rather
|
||||||
|
+ * than to any handle), and a process is spawned, never forked with a
|
||||||
|
+ * doctored descriptor table.
|
||||||
|
+ *
|
||||||
|
+ * The upstream body cannot run in any case -- there is no /dev/null,
|
||||||
|
+ * so xopen() would die here and take every git invocation with it.
|
||||||
|
+ */
|
||||||
|
}
|
||||||
|
|
||||||
|
int daemonize(void)
|
||||||
Submodule
+1
Submodule dev-vcs/git/upstream added at cc7d11c167
Reference in New Issue
Block a user