fix(backends): copy links when the filesystem rejects symlinks (CIFS/SMB) (#10890)#10893
fix(backends): copy links when the filesystem rejects symlinks (CIFS/SMB) (#10890)#10893localai-bot wants to merge 4 commits into
Conversation
… symlinks (#10890) Backend installation extracts the OCI image tar via containerd's archive.Apply, which calls os.Symlink directly. On filesystems that do not support symlinks (notably CIFS/SMB mounts, commonly used to back the /backends volume) the syscall fails with "operation not supported" and the whole install aborts, leaving an empty backend directory. The CUDA llama.cpp image trips this on the libcublas.so -> libcublas.so.12.x symlink. When archive.Apply fails with a link-unsupported error, reset the staging directory and re-extract with a pure-Go walker that still attempts real symlinks/hardlinks first and degrades to copying the link target's contents in place when the filesystem rejects them. mutate.Extract already flattened the layers, so the tar carries no whiteouts to interpret. Link copies are deferred to a second pass so forward references resolve. Signed-off-by: Ettore Di Giacinto <mudler@localai.io> Assisted-by: Claude:opus-4.8 [Claude Code]
Signed-off-by: Ettore Di Giacinto <mudler@localai.io> Assisted-by: Claude:opus-4.8 [Claude Code]
safeJoin sanitized "../.." entries by clamping them under root instead of rejecting them, so a malicious entry was silently redirected rather than refused. Join without the leading-slash trick and reject any entry whose cleaned path resolves outside root; absolute link targets are still mapped under root (image-root relative) rather than escaping. Signed-off-by: Ettore Di Giacinto <mudler@localai.io> Assisted-by: Claude:opus-4.8 [Claude Code]
Use hdr.FileInfo().Mode() instead of converting the int64 tar mode to os.FileMode (removes two G115 overflow findings), and annotate the tar extraction file operations with justified #nosec comments: every path is validated by safeJoin against the extraction root before use (G304/G305). Signed-off-by: Ettore Di Giacinto <mudler@localai.io> Assisted-by: Claude:opus-4.8 [Claude Code]
|
@localai-bot this is not right, OCI images have layers and this as-is its not unpacking layers. |
|
@localai-org-maint-bot to you to pick it up |
|
I picked this up and confirmed the current fallback is not safe to merge as-is. Besides bypassing OCI layer application, I prepared a replacement locally that:
Focused fallback tests pass (6/6), all |
|
@mudler update: the layer-aware replacement is rebased cleanly onto current master 2b61e4b and remains staged in the isolated worktree. Fresh verification passes: the layered-image and whiteout regression, all 16 pkg/oci specs, go vet ./pkg/oci, golangci-lint with 0 issues, all 58 ./core suites, and e2e 94/94. The mandatory coverage hook is still the only blocker: all suites pass, but this host aggregates 41.2% against the current 54.2% baseline and exits nonzero; the master workflow for 2b61e4b is still queued. I have not bypassed the hook or lowered the baseline, and have not pushed an unverified commit. |
Description
Fixes #10890.
Backend installation on a CIFS/SMB-backed
/backendsvolume fails for every backend with:The OCI image tar is extracted via containerd's
archive.Apply, which creates symlinks withos.Symlink. CIFS/SMB mounts (a common way to store/backendson a NAS) do not support symlinks and reject the syscall withoperation not supported, aborting the whole install and leaving an empty backend directory. The CUDA llama.cpp image trips on thelibcublas.so -> libcublas.so.12.xsymlink.Fix
When
archive.Applyfails with a link-unsupported error (ENOTSUP/EOPNOTSUPP/EPERM, or the matching "operation not supported/permitted" text), the staging directory is reset and re-extracted with a pure-Go tar walker that:mutate.Extractalready flattens the layers before this point, so the tar carries no whiteouts to interpret; a plain walker is sufficient. The primaryarchive.Applypath is unchanged, so this only affects filesystems that were already failing 100% of installs. The staging directory is the ephemeral, per-install*.install-tmpdir, so wiping it before the fallback pass is safe.Tests
Added
pkg/oci/extract_internal_test.go(Ginkgo/Gomega):isLinkUnsupportedErrortruth table (syscall errnos + wrapped*os.LinkError+ negatives).safeJoinkeeps entries in-root and rejects traversal.extractTarCopyingLinkspreserves symlinks on a symlink-capable FS, and, withsymlink()stubbed to returnENOTSUP, materialises the target as a real regular file copy with the right bytes.Note
Built and verified via CI (no Go toolchain in the authoring sandbox).
Assisted-by: Claude:opus-4.8 [Claude Code]