diff options
| author | James O'Doherty <james@theodohertyfamily.com> | 2026-05-29 19:21:49 -0400 |
|---|---|---|
| committer | James O'Doherty <james@theodohertyfamily.com> | 2026-05-29 19:21:49 -0400 |
| commit | 70096b533d42b684ab13651aaae884047e01e43d (patch) | |
| tree | 2646cf017a7b903c6e1f3c1be981b1d21fa4a51b /internal/namespace/namespace.go | |
| parent | 284ed362550e1fccc62ecd876dbd3f4c8fc721e2 (diff) | |
refactor: optimize file cleanups, propagate exit codes, and fix Makefile
- Unlink the temporary bootstrap launcher binary immediately after opening a read-only descriptor to it, then execute via `/proc/self/fd/<fd>` to ensure zero-disk footprint on execution.
- Unlink temporary `/tmp/resolvconf*` files immediately after successful bind-mounting over `/etc/resolv.conf`.
- Prune parent ephemeral profile directories when unpinning a namespace, leaving zero directories behind once empty.
- Propagate the exact exit status of the wrapped command to the host process using `errors.As` and `*exec.ExitError` instead of defaulting to exit code 1.
- Added E2E automated test `TestExitCodePropagation` to verify exit status delivery.
- Added the `$(BINARY)` target to `.PHONY` in the Makefile to delegate dependency tracking to Go's compiler cache, ensuring modified Go files are rebuilt during `make test`.
Diffstat (limited to 'internal/namespace/namespace.go')
| -rw-r--r-- | internal/namespace/namespace.go | 22 |
1 files changed, 21 insertions, 1 deletions
diff --git a/internal/namespace/namespace.go b/internal/namespace/namespace.go index a1e7ad9..e2ef2f1 100644 --- a/internal/namespace/namespace.go +++ b/internal/namespace/namespace.go @@ -100,16 +100,36 @@ func Bootstrap() error { // 2. Write the embedded launcher binary to the temp file. if _, err := tmpFile.Write(launcherBytes); err != nil { _ = tmpFile.Close() + _ = os.Remove(launcherPath) return fmt.Errorf("failed to write launcher binary: %w", err) } // Ensure the binary is executable (0700) if err := tmpFile.Chmod(0700); err != nil { _ = tmpFile.Close() + _ = os.Remove(launcherPath) return fmt.Errorf("failed to set launcher permissions: %w", err) } + + // 2b. Open a read-only fd of the launcher to exec + execFd, err := syscall.Open(launcherPath, syscall.O_RDONLY, 0) + if err != nil { + _ = tmpFile.Close() + _ = os.Remove(launcherPath) + return fmt.Errorf("failed to open launcher for exec: %w", err) + } + + // Close the write file descriptor (to avoid ETXTBSY) _ = tmpFile.Close() + // Unlink the file from disk (makes it invisible and ensures it is deleted on exit) + _ = os.Remove(launcherPath) + + // Clear close-on-exec so it remains open across syscall.Exec + if flags, err := unix.FcntlInt(uintptr(execFd), unix.F_GETFD, 0); err == nil { + _, _ = unix.FcntlInt(uintptr(execFd), unix.F_SETFD, flags&^unix.FD_CLOEXEC) + } + // 3. Prepare arguments for the launcher. // The launcher expects: launcher <command_to_run> [args...] args := []string{self} @@ -153,7 +173,7 @@ func Bootstrap() error { } } - err = syscall.Exec(launcherPath, args, env) + err = syscall.Exec(fmt.Sprintf("/proc/self/fd/%d", execFd), args, env) if err != nil { return fmt.Errorf("launcher exec failed: %w", err) } |
