Skip to content

Commit 321de60

Browse files
kaovilaiclaude
andcommitted
Fix xattr copy failures on SELinux systems
When copying the buildkit-qemu-emulator binary on systems with SELinux enabled, the copy operation fails with "operation not supported" errors when attempting to copy security.selinux xattrs. This change adds an XAttrErrorHandler to the copy.Copy call that ignores ENOTSUP errors, allowing the copy to succeed on SELinux-enabled systems. Fixes #5544 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]> Signed-off-by: Tiger Kaovilai <[email protected]>
1 parent 16e8c26 commit 321de60

File tree

2 files changed

+81
-0
lines changed

2 files changed

+81
-0
lines changed

solver/llbsolver/ops/exec_binfmt.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"os/exec"
77
"path/filepath"
88
"strings"
9+
"syscall"
910

1011
"github.com/containerd/containerd/v2/core/mount"
1112
"github.com/containerd/platforms"
@@ -64,6 +65,15 @@ func (m *staticEmulatorMount) Mount() ([]mount.Mount, func() error, error) {
6465
if err := copy.Copy(context.TODO(), filepath.Dir(m.path), filepath.Base(m.path), tmpdir, qemuMountName, func(ci *copy.CopyInfo) {
6566
m := 0555
6667
ci.Mode = &m
68+
ci.XAttrErrorHandler = func(dst, src, xattrKey string, err error) error {
69+
// Ignore ENOTSUP (operation not supported) errors when copying xattrs
70+
// This is needed for systems with SELinux enabled where security.selinux
71+
// xattrs cannot be modified
72+
if errors.Is(err, syscall.ENOTSUP) {
73+
return nil
74+
}
75+
return err
76+
}
6777
}, copy.WithChown(uid, gid)); err != nil {
6878
return nil, nil, err
6979
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package ops
2+
3+
import (
4+
"syscall"
5+
"testing"
6+
7+
"github.com/pkg/errors"
8+
"github.com/stretchr/testify/require"
9+
)
10+
11+
// TestXAttrErrorHandler tests the XAttrErrorHandler logic used in exec_binfmt.go
12+
func TestXAttrErrorHandler(t *testing.T) {
13+
tests := []struct {
14+
name string
15+
inputErr error
16+
shouldIgnore bool
17+
description string
18+
}{
19+
{
20+
name: "ENOTSUP error should be ignored",
21+
inputErr: syscall.ENOTSUP,
22+
shouldIgnore: true,
23+
description: "ENOTSUP errors occur on SELinux systems and should be ignored",
24+
},
25+
{
26+
name: "errors.Is should work with wrapped ENOTSUP",
27+
inputErr: errors.Wrap(syscall.ENOTSUP, "failed to set xattr"),
28+
shouldIgnore: true,
29+
description: "Wrapped ENOTSUP errors should also be ignored",
30+
},
31+
{
32+
name: "EPERM error should not be ignored",
33+
inputErr: syscall.EPERM,
34+
shouldIgnore: false,
35+
description: "Other permission errors should be propagated",
36+
},
37+
{
38+
name: "EIO error should not be ignored",
39+
inputErr: syscall.EIO,
40+
shouldIgnore: false,
41+
description: "I/O errors should be propagated",
42+
},
43+
{
44+
name: "nil error should return nil",
45+
inputErr: nil,
46+
shouldIgnore: true,
47+
description: "nil errors should return nil",
48+
},
49+
}
50+
51+
for _, tt := range tests {
52+
t.Run(tt.name, func(t *testing.T) {
53+
// This mimics the XAttrErrorHandler in exec_binfmt.go
54+
handler := func(dst, src, xattrKey string, err error) error {
55+
if errors.Is(err, syscall.ENOTSUP) {
56+
return nil
57+
}
58+
return err
59+
}
60+
61+
result := handler("dst", "src", "security.selinux", tt.inputErr)
62+
63+
if tt.shouldIgnore {
64+
require.NoError(t, result, tt.description)
65+
} else {
66+
require.Error(t, result, tt.description)
67+
require.True(t, errors.Is(result, tt.inputErr), "error should be preserved")
68+
}
69+
})
70+
}
71+
}

0 commit comments

Comments
 (0)