1
2
3
4
19
20 package fs
21
22 import (
23 "os"
24 "path/filepath"
25 "runtime"
26 "syscall"
27 "testing"
28
29 "github.com/containerd/continuity/fs/fstest"
30 "github.com/containerd/continuity/sysx"
31 "golang.org/x/sys/unix"
32 )
33
34 func assertXAttr(t *testing.T, dir, xattr, xval string, xerr error) {
35 t.Helper()
36 value, err := sysx.Getxattr(dir, xattr)
37 switch {
38 case xerr == nil && err != nil:
39 t.Errorf("err=%v; expected val=%s", err, xval)
40 case xerr == nil && err == nil && string(value) != xval:
41 t.Errorf("val=%s; expected val=%s", value, xval)
42 case xerr != nil && err != xerr:
43 t.Errorf("val=%s, err=%v; expected err=%v", value, err, xerr)
44 }
45 }
46
47 func TestCopyDirWithXAttrExcludes(t *testing.T) {
48 src := t.TempDir()
49 if err := fstest.Apply(
50 fstest.SetXAttr(".", "user.test-1", "one"),
51 fstest.SetXAttr(".", "user.test-2", "two"),
52 fstest.SetXAttr(".", "user.test-x", "three-four-five"),
53 ).Apply(src); err != nil {
54 t.Fatal(err)
55 }
56
57 t.Run("none", func(t *testing.T) {
58 dst := t.TempDir()
59 err := CopyDir(dst, src, WithXAttrExclude())
60 if err != nil {
61 t.Fatal(err)
62 }
63 assertXAttr(t, dst, "user.test-1", "one", nil)
64 assertXAttr(t, dst, "user.test-2", "two", nil)
65 assertXAttr(t, dst, "user.test-x", "three-four-five", nil)
66 })
67
68 t.Run("some", func(t *testing.T) {
69 dst := t.TempDir()
70 err := CopyDir(dst, src, WithXAttrExclude("user.test-x"))
71 if err != nil {
72 t.Fatal(err)
73 }
74 assertXAttr(t, dst, "user.test-1", "one", nil)
75 assertXAttr(t, dst, "user.test-2", "two", nil)
76 assertXAttr(t, dst, "user.test-x", "", sysx.ENODATA)
77 })
78 }
79
80 func TestCopyIrregular(t *testing.T) {
81 var prepared int
82 prepareSrc := func(src string) {
83 f0Pipe := filepath.Join(src, "f0.pipe")
84 if err := unix.Mkfifo(f0Pipe, 0o600); err != nil {
85 t.Fatal(err)
86 }
87 prepared++
88 f1Normal := filepath.Join(src, "f1.normal")
89 if err := os.WriteFile(f1Normal, []byte("content of f1.normal"), 0o600); err != nil {
90 t.Fatal(err)
91 }
92 prepared++
93 if runtime.GOOS != "darwin" {
94 f2Socket := filepath.Join(src, "f2.sock")
95 if err := unix.Mknod(f2Socket, 0o600|unix.S_IFSOCK, 0); err != nil {
96 t.Fatal(err)
97 }
98 prepared++
99 }
100 f3Dev := filepath.Join(src, "f3.dev")
101 if err := unix.Mknod(f3Dev, 0o600|unix.S_IFCHR, 42); err != nil {
102 t.Logf("skipping testing S_IFCHR: %v", err)
103 } else {
104 prepared++
105 }
106 }
107
108 verifyDst := func(dst string) {
109 entries, err := os.ReadDir(dst)
110 if err != nil {
111 t.Fatal(err)
112 }
113 var verified int
114 for _, f := range entries {
115 name := f.Name()
116 full := filepath.Join(dst, name)
117 fi, err := os.Stat(full)
118 if err != nil {
119 t.Fatal(err)
120 }
121 mode := fi.Mode()
122 switch name {
123 case "f0.pipe":
124 if mode&os.ModeNamedPipe != os.ModeNamedPipe {
125 t.Fatalf("unexpected mode of %s: %v", name, mode)
126 }
127 case "f1.normal":
128 b, err := os.ReadFile(full)
129 if err != nil {
130 t.Fatal(err)
131 }
132 if string(b) != "content of f1.normal" {
133 t.Fatalf("unexpected content of %s: %q", name, string(b))
134 }
135 case "f2.sock":
136 if mode&os.ModeSocket != os.ModeSocket {
137 t.Fatalf("unexpected mode of %s: %v", name, mode)
138 }
139 case "f3.dev":
140 if mode&os.ModeDevice != os.ModeDevice {
141 t.Fatalf("unexpected mode of %s: %v", name, mode)
142 }
143 sys, ok := fi.Sys().(*syscall.Stat_t)
144 if !ok {
145 t.Fatalf("unexpected type: %v", fi.Sys())
146 }
147 if sys.Rdev != 42 {
148 t.Fatalf("unexpected rdev of %s: %d", name, sys.Rdev)
149 }
150 }
151 verified++
152 }
153 if verified != prepared {
154 t.Fatalf("prepared %d files, verified %d files", prepared, verified)
155 }
156 }
157
158 src := t.TempDir()
159 dst := t.TempDir()
160 prepareSrc(src)
161 if err := CopyDir(dst, src); err != nil {
162 t.Fatal(err)
163 }
164 verifyDst(dst)
165 }
166
View as plain text