github.com/moby/docker@v26.1.3+incompatible/pkg/archive/archive_linux_test.go (about) 1 package archive // import "github.com/docker/docker/pkg/archive" 2 3 import ( 4 "archive/tar" 5 "bytes" 6 "io" 7 "os" 8 "path/filepath" 9 "syscall" 10 "testing" 11 12 "github.com/containerd/containerd/pkg/userns" 13 "github.com/docker/docker/pkg/system" 14 "github.com/google/go-cmp/cmp/cmpopts" 15 "golang.org/x/sys/unix" 16 "gotest.tools/v3/assert" 17 is "gotest.tools/v3/assert/cmp" 18 "gotest.tools/v3/skip" 19 ) 20 21 // setupOverlayTestDir creates files in a directory with overlay whiteouts 22 // Tree layout 23 // 24 // . 25 // ├── d1 # opaque, 0700 26 // │ └── f1 # empty file, 0600 27 // ├── d2 # opaque, 0750 28 // │ └── f1 # empty file, 0660 29 // └── d3 # 0700 30 // └── f1 # whiteout, 0644 31 func setupOverlayTestDir(t *testing.T, src string) { 32 skip.If(t, os.Getuid() != 0, "skipping test that requires root") 33 skip.If(t, userns.RunningInUserNS(), "skipping test that requires initial userns (trusted.overlay.opaque xattr cannot be set in userns, even with Ubuntu kernel)") 34 // Create opaque directory containing single file and permission 0700 35 err := os.Mkdir(filepath.Join(src, "d1"), 0o700) 36 assert.NilError(t, err) 37 38 err = system.Lsetxattr(filepath.Join(src, "d1"), "trusted.overlay.opaque", []byte("y"), 0) 39 assert.NilError(t, err) 40 41 err = os.WriteFile(filepath.Join(src, "d1", "f1"), []byte{}, 0o600) 42 assert.NilError(t, err) 43 44 // Create another opaque directory containing single file but with permission 0750 45 err = os.Mkdir(filepath.Join(src, "d2"), 0o750) 46 assert.NilError(t, err) 47 48 err = system.Lsetxattr(filepath.Join(src, "d2"), "trusted.overlay.opaque", []byte("y"), 0) 49 assert.NilError(t, err) 50 51 err = os.WriteFile(filepath.Join(src, "d2", "f1"), []byte{}, 0o660) 52 assert.NilError(t, err) 53 54 // Create regular directory with deleted file 55 err = os.Mkdir(filepath.Join(src, "d3"), 0o700) 56 assert.NilError(t, err) 57 58 err = system.Mknod(filepath.Join(src, "d3", "f1"), unix.S_IFCHR, 0) 59 assert.NilError(t, err) 60 } 61 62 func checkOpaqueness(t *testing.T, path string, opaque string) { 63 xattrOpaque, err := system.Lgetxattr(path, "trusted.overlay.opaque") 64 assert.NilError(t, err) 65 66 if string(xattrOpaque) != opaque { 67 t.Fatalf("Unexpected opaque value: %q, expected %q", string(xattrOpaque), opaque) 68 } 69 } 70 71 func checkOverlayWhiteout(t *testing.T, path string) { 72 stat, err := os.Stat(path) 73 assert.NilError(t, err) 74 75 statT, ok := stat.Sys().(*syscall.Stat_t) 76 if !ok { 77 t.Fatalf("Unexpected type: %t, expected *syscall.Stat_t", stat.Sys()) 78 } 79 if statT.Rdev != 0 { 80 t.Fatalf("Non-zero device number for whiteout") 81 } 82 } 83 84 func checkFileMode(t *testing.T, path string, perm os.FileMode) { 85 stat, err := os.Stat(path) 86 assert.NilError(t, err) 87 88 if stat.Mode() != perm { 89 t.Fatalf("Unexpected file mode for %s: %o, expected %o", path, stat.Mode(), perm) 90 } 91 } 92 93 func TestOverlayTarUntar(t *testing.T) { 94 restore := overrideUmask(0) 95 defer restore() 96 97 src, err := os.MkdirTemp("", "docker-test-overlay-tar-src") 98 assert.NilError(t, err) 99 defer os.RemoveAll(src) 100 101 setupOverlayTestDir(t, src) 102 103 dst, err := os.MkdirTemp("", "docker-test-overlay-tar-dst") 104 assert.NilError(t, err) 105 defer os.RemoveAll(dst) 106 107 options := &TarOptions{ 108 Compression: Uncompressed, 109 WhiteoutFormat: OverlayWhiteoutFormat, 110 } 111 reader, err := TarWithOptions(src, options) 112 assert.NilError(t, err) 113 archive, err := io.ReadAll(reader) 114 reader.Close() 115 assert.NilError(t, err) 116 117 // The archive should encode opaque directories and file whiteouts 118 // in AUFS format. 119 entries := make(map[string]struct{}) 120 rdr := tar.NewReader(bytes.NewReader(archive)) 121 for { 122 h, err := rdr.Next() 123 if err == io.EOF { 124 break 125 } 126 assert.NilError(t, err) 127 assert.Check(t, is.Equal(h.Devmajor, int64(0)), "unexpected device file in archive") 128 assert.Check(t, is.DeepEqual(h.PAXRecords, map[string]string(nil), cmpopts.EquateEmpty())) 129 entries[h.Name] = struct{}{} 130 } 131 132 assert.DeepEqual(t, entries, map[string]struct{}{ 133 "d1/": {}, 134 "d1/" + WhiteoutOpaqueDir: {}, 135 "d1/f1": {}, 136 "d2/": {}, 137 "d2/" + WhiteoutOpaqueDir: {}, 138 "d2/f1": {}, 139 "d3/": {}, 140 "d3/" + WhiteoutPrefix + "f1": {}, 141 }) 142 143 err = Untar(bytes.NewReader(archive), dst, options) 144 assert.NilError(t, err) 145 146 checkFileMode(t, filepath.Join(dst, "d1"), 0o700|os.ModeDir) 147 checkFileMode(t, filepath.Join(dst, "d2"), 0o750|os.ModeDir) 148 checkFileMode(t, filepath.Join(dst, "d3"), 0o700|os.ModeDir) 149 checkFileMode(t, filepath.Join(dst, "d1", "f1"), 0o600) 150 checkFileMode(t, filepath.Join(dst, "d2", "f1"), 0o660) 151 checkFileMode(t, filepath.Join(dst, "d3", "f1"), os.ModeCharDevice|os.ModeDevice) 152 153 checkOpaqueness(t, filepath.Join(dst, "d1"), "y") 154 checkOpaqueness(t, filepath.Join(dst, "d2"), "y") 155 checkOpaqueness(t, filepath.Join(dst, "d3"), "") 156 checkOverlayWhiteout(t, filepath.Join(dst, "d3", "f1")) 157 } 158 159 func TestOverlayTarAUFSUntar(t *testing.T) { 160 restore := overrideUmask(0) 161 defer restore() 162 163 src, err := os.MkdirTemp("", "docker-test-overlay-tar-src") 164 assert.NilError(t, err) 165 defer os.RemoveAll(src) 166 167 setupOverlayTestDir(t, src) 168 169 dst, err := os.MkdirTemp("", "docker-test-overlay-tar-dst") 170 assert.NilError(t, err) 171 defer os.RemoveAll(dst) 172 173 archive, err := TarWithOptions(src, &TarOptions{ 174 Compression: Uncompressed, 175 WhiteoutFormat: OverlayWhiteoutFormat, 176 }) 177 assert.NilError(t, err) 178 defer archive.Close() 179 180 err = Untar(archive, dst, &TarOptions{ 181 Compression: Uncompressed, 182 WhiteoutFormat: AUFSWhiteoutFormat, 183 }) 184 assert.NilError(t, err) 185 186 checkFileMode(t, filepath.Join(dst, "d1"), 0o700|os.ModeDir) 187 checkFileMode(t, filepath.Join(dst, "d1", WhiteoutOpaqueDir), 0o700) 188 checkFileMode(t, filepath.Join(dst, "d2"), 0o750|os.ModeDir) 189 checkFileMode(t, filepath.Join(dst, "d2", WhiteoutOpaqueDir), 0o750) 190 checkFileMode(t, filepath.Join(dst, "d3"), 0o700|os.ModeDir) 191 checkFileMode(t, filepath.Join(dst, "d1", "f1"), 0o600) 192 checkFileMode(t, filepath.Join(dst, "d2", "f1"), 0o660) 193 checkFileMode(t, filepath.Join(dst, "d3", WhiteoutPrefix+"f1"), 0o600) 194 }