Skip to content

WCOW: apply Linux layers and cross-OS COPY --from (partial #4537)#6880

Open
rzlink wants to merge 1 commit into
moby:masterfrom
rzlink:wcow-linux-layers
Open

WCOW: apply Linux layers and cross-OS COPY --from (partial #4537)#6880
rzlink wants to merge 1 commit into
moby:masterfrom
rzlink:wcow-linux-layers

Conversation

@rzlink

@rzlink rzlink commented Jun 16, 2026

Copy link
Copy Markdown
Collaborator

Part of #4537 (partial implementation — see "Scope" below).
Relates to #5758

Summary

Adds cross-platform layer support so a Windows BuildKit worker can apply Linux image layers and read them through cross-OS COPY --from. Before this change util/winlayers only handled the LCOW direction (Windows layers on Linux hosts); the Windows-applies-Linux direction was unimplemented.

This implements the core of #5758 (basic cross-platform builds from Windows) for alpine-class base images: the hcsshim::ProcessBaseLayer ... cannot find the path specified failure reported there is exactly what the base-layer finalization path below resolves.

How it works

  • applyLinuxLayer wraps the Linux tar under Files/ and imports it via hcsshim.ociwclayer.ImportLayerFromTar. When the ProcessBaseLayer step rejects Linux content, the snapshot is finalized with hcsshim.ConvertToBaseLayer and stamped with a .cross-os-linux marker.

  • A windows-layer mount on a marked parent bypasses HCS and bind-filters the parent's Files/ directly, since HCS PrepareLayer cannot expose Linux content as a Windows volume.

  • The boolean windowsLayerMode is replaced by an OS-aware context API (SetLayerOS / GetLayerOS), and cross-platform layers are tagged in both directions.

Scope

#4537 asks for the cross-OS layer transform to work both directions on a Windows worker:

  • Apply / read Linux layers (this PR): pull a Linux image and read files out of it via COPY --from=<linux-stage> into a Windows final stage.
  • Emit / generate Linux layers (NOT in this PR): a Windows worker producing a Linux image — applying a Linux base, running COPY/file operations on it, and committing/diffing it back into a Linux-format layer without HCS.

Only the first direction is implemented here. The emit/generate direction requires non-HCS writable-layer storage (e.g. an in-process plain-directory snapshotter) plus a Windows-tolerant Linux applier/differ, and is left to a follow-up. Cross-OS RUN remains unsupported in both directions (no Linux kernel on a Windows worker), as noted in #4537.

Therefore this PR is one half of it.

Tests

  • Unit coverage for the tar transforms, the context API, PAX records, and winDiffer.Compare() dispatch (util/winlayers/winlayers_test.go).
  • testCopyCrossPlatformMultiStage integration test exercising a real cross-OS COPY --from build.

Known limitations

Some Linux base images do not yet round-trip cleanly on NTFS — e.g. paths containing characters NTFS forbids (: in dpkg metadata) and certain relative symlinks (/etc/os-release). Handling those is deliberately left to a follow-up PR, so this does not yet fully close #5758 for ubuntu/debian-class images.

Add cross-platform layer transforms so a Windows BuildKit worker can apply
Linux layers and read them via cross-OS COPY. Previously only the LCOW
direction (Windows layers on Linux hosts) was supported.

* util/winlayers: OS-aware context API (SetLayerOS / GetLayerOS) replaces
  the boolean windowsLayerMode; add applyLinuxLayer that wraps a Linux tar
  under Files/ and imports it via hcsshim.ociwclayer.ImportLayerFromTar;
  drop the Linux-only build tags from the applier.
* util/winlayers: when ImportLayerFromTar's ProcessBaseLayer step rejects
  Linux content, strip everything except the Files/ tree and finalize the
  snapshot with hcsshim.ConvertToBaseLayer (which generates the registry
  hives and layer VHDs); stamp it with a .cross-os-linux marker.
* snapshot/localmounter_windows.go: when a windows-layer mount targets a
  parent carrying the marker, bypass HCS and bind-filter the parent's
  Files/ directly, since HCS PrepareLayer cannot expose Linux content as a
  Windows volume.
* source/containerimage/pull.go, cache/blobs.go, cache/refs.go: tag
  cross-platform layers in both directions.
* Tests: unit coverage for the tar transforms, context API, PAX records and
  winDiffer.Compare() dispatch; testCopyCrossPlatformMultiStage integration
  test exercising real cross-OS COPY.

Signed-off-by: Dawei Wei <wei.dawei.cn@gmail.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds bidirectional cross-platform layer handling so BuildKit can apply and read image layers whose OS differs from the worker host (notably enabling a Windows worker to ingest Linux layers and support COPY --from across OS boundaries). It replaces the prior Windows-layer boolean mode with an OS-aware context mechanism and threads the layer OS through pull/cache/apply/diff paths.

Changes:

  • Introduces an OS-aware winlayers context API (SetLayerOS / GetLayerOS) and routes apply/diff behavior based on layer OS vs host OS.
  • Implements Windows-host application of Linux layers by wrapping Linux tars as Windows layers and importing via HCS, plus adds a marker file and Windows localmounter bypass for reads.
  • Adds unit tests for tar transforms and dispatch, plus an integration test exercising cross-OS multi-stage COPY --from.

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
util/winlayers/winlayers_test.go Adds unit coverage for context API, tar wrapping/filtering, and differ dispatch.
util/winlayers/differ.go Routes Compare based on layer OS and refactors Windows tar wrapping into writeWrappedWindowsLayer.
util/winlayers/context.go Replaces boolean “windows layer mode” with OS string in context and host-aware transformation checks.
util/winlayers/apply_windows.go Adds Windows-only Linux-layer import via HCS and writes a cross-OS marker; parses parent layer paths.
util/winlayers/apply_other.go Non-Windows stubs for Windows-only Linux-layer import helpers.
util/winlayers/applier.go Splits apply into Windows-layer-on-Linux vs Linux-layer-on-Windows handlers; adds Linux-to-Windows tar wrapping pipeline.
source/containerimage/pull.go Sets layer type based on requested platform OS when pulling cross-platform images.
snapshot/localmounter_windows.go Adds cross-OS marker detection to bypass HCS and bind-mount parent Files/ for reading.
frontend/dockerfile/dockerfile_test.go Adds integration test for cross-OS multi-stage COPY --from.
cache/refs.go Propagates layer OS into context using the new API.
cache/manager_test.go Adds tests around layer type propagation and cross-platform detection.
cache/blobs.go Treats non-empty layer type as “cross-platform” for diffing decisions and sets layer OS in context.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread util/winlayers/applier.go
Comment on lines +128 to +131
bklog.G(ctx).Infof("linuxlayer-apply: enter desc.digest=%s desc.size=%d media=%s mounts=%d", desc.Digest, desc.Size, desc.MediaType, len(mounts))
for i, m := range mounts {
bklog.G(ctx).Infof("linuxlayer-apply: mount[%d] type=%s source=%s target=%s opts=%v", i, m.Type, m.Source, m.Target, m.Options)
}
Comment on lines +159 to +164
parents := parseParentLayerPaths(m.Options)
if len(parents) == 0 {
return "", false
}
parent := parents[0]
if _, err := os.Stat(filepath.Join(parent, crossOSLinuxMarker)); err != nil {
Comment on lines +113 to +125
func getParentLayerPaths(mounts []mount.Mount) []string {
for _, m := range mounts {
for _, opt := range m.Options {
if after, ok := strings.CutPrefix(opt, "parentLayerPaths="); ok {
var paths []string
if err := json.Unmarshal([]byte(after), &paths); err == nil {
return paths
}
}
}
}
return nil
}
Comment on lines +32 to +38
// ProcessBaseLayer checks the process token, not the thread impersonation
// token RunWithPrivileges sets, so enable privileges process-wide.
requiredPrivileges := []string{winio.SeBackupPrivilege, winio.SeRestorePrivilege, winio.SeSecurityPrivilege}
if err := winio.EnableProcessPrivileges(requiredPrivileges); err != nil {
return 0, errors.Wrap(err, "failed to enable required privileges")
}
defer winio.DisableProcessPrivileges(requiredPrivileges)
@rzlink rzlink changed the title WCOW: support Linux layers WCOW: apply Linux layers and cross-OS COPY --from (partial #4537) Jun 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

windows: support basic cross-platform builds from windows

2 participants