archive_unix_test.go 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. // +build !windows
  2. package archive
  3. import (
  4. "bytes"
  5. "fmt"
  6. "io/ioutil"
  7. "os"
  8. "path/filepath"
  9. "runtime"
  10. "syscall"
  11. "testing"
  12. "github.com/docker/docker/pkg/system"
  13. )
  14. func TestCanonicalTarNameForPath(t *testing.T) {
  15. cases := []struct{ in, expected string }{
  16. {"foo", "foo"},
  17. {"foo/bar", "foo/bar"},
  18. {"foo/dir/", "foo/dir/"},
  19. }
  20. for _, v := range cases {
  21. if out, err := CanonicalTarNameForPath(v.in); err != nil {
  22. t.Fatalf("cannot get canonical name for path: %s: %v", v.in, err)
  23. } else if out != v.expected {
  24. t.Fatalf("wrong canonical tar name. expected:%s got:%s", v.expected, out)
  25. }
  26. }
  27. }
  28. func TestCanonicalTarName(t *testing.T) {
  29. cases := []struct {
  30. in string
  31. isDir bool
  32. expected string
  33. }{
  34. {"foo", false, "foo"},
  35. {"foo", true, "foo/"},
  36. {"foo/bar", false, "foo/bar"},
  37. {"foo/bar", true, "foo/bar/"},
  38. }
  39. for _, v := range cases {
  40. if out, err := canonicalTarName(v.in, v.isDir); err != nil {
  41. t.Fatalf("cannot get canonical name for path: %s: %v", v.in, err)
  42. } else if out != v.expected {
  43. t.Fatalf("wrong canonical tar name. expected:%s got:%s", v.expected, out)
  44. }
  45. }
  46. }
  47. func TestChmodTarEntry(t *testing.T) {
  48. cases := []struct {
  49. in, expected os.FileMode
  50. }{
  51. {0000, 0000},
  52. {0777, 0777},
  53. {0644, 0644},
  54. {0755, 0755},
  55. {0444, 0444},
  56. }
  57. for _, v := range cases {
  58. if out := chmodTarEntry(v.in); out != v.expected {
  59. t.Fatalf("wrong chmod. expected:%v got:%v", v.expected, out)
  60. }
  61. }
  62. }
  63. func TestTarWithHardLink(t *testing.T) {
  64. origin, err := ioutil.TempDir("", "docker-test-tar-hardlink")
  65. if err != nil {
  66. t.Fatal(err)
  67. }
  68. defer os.RemoveAll(origin)
  69. if err := ioutil.WriteFile(filepath.Join(origin, "1"), []byte("hello world"), 0700); err != nil {
  70. t.Fatal(err)
  71. }
  72. if err := os.Link(filepath.Join(origin, "1"), filepath.Join(origin, "2")); err != nil {
  73. t.Fatal(err)
  74. }
  75. var i1, i2 uint64
  76. if i1, err = getNlink(filepath.Join(origin, "1")); err != nil {
  77. t.Fatal(err)
  78. }
  79. // sanity check that we can hardlink
  80. if i1 != 2 {
  81. t.Skipf("skipping since hardlinks don't work here; expected 2 links, got %d", i1)
  82. }
  83. dest, err := ioutil.TempDir("", "docker-test-tar-hardlink-dest")
  84. if err != nil {
  85. t.Fatal(err)
  86. }
  87. defer os.RemoveAll(dest)
  88. // we'll do this in two steps to separate failure
  89. fh, err := Tar(origin, Uncompressed)
  90. if err != nil {
  91. t.Fatal(err)
  92. }
  93. // ensure we can read the whole thing with no error, before writing back out
  94. buf, err := ioutil.ReadAll(fh)
  95. if err != nil {
  96. t.Fatal(err)
  97. }
  98. bRdr := bytes.NewReader(buf)
  99. err = Untar(bRdr, dest, &TarOptions{Compression: Uncompressed})
  100. if err != nil {
  101. t.Fatal(err)
  102. }
  103. if i1, err = getInode(filepath.Join(dest, "1")); err != nil {
  104. t.Fatal(err)
  105. }
  106. if i2, err = getInode(filepath.Join(dest, "2")); err != nil {
  107. t.Fatal(err)
  108. }
  109. if i1 != i2 {
  110. t.Errorf("expected matching inodes, but got %d and %d", i1, i2)
  111. }
  112. }
  113. func getNlink(path string) (uint64, error) {
  114. stat, err := os.Stat(path)
  115. if err != nil {
  116. return 0, err
  117. }
  118. statT, ok := stat.Sys().(*syscall.Stat_t)
  119. if !ok {
  120. return 0, fmt.Errorf("expected type *syscall.Stat_t, got %t", stat.Sys())
  121. }
  122. // We need this conversion on ARM64
  123. return uint64(statT.Nlink), nil
  124. }
  125. func getInode(path string) (uint64, error) {
  126. stat, err := os.Stat(path)
  127. if err != nil {
  128. return 0, err
  129. }
  130. statT, ok := stat.Sys().(*syscall.Stat_t)
  131. if !ok {
  132. return 0, fmt.Errorf("expected type *syscall.Stat_t, got %t", stat.Sys())
  133. }
  134. return statT.Ino, nil
  135. }
  136. func TestTarWithBlockCharFifo(t *testing.T) {
  137. origin, err := ioutil.TempDir("", "docker-test-tar-hardlink")
  138. if err != nil {
  139. t.Fatal(err)
  140. }
  141. defer os.RemoveAll(origin)
  142. if err := ioutil.WriteFile(filepath.Join(origin, "1"), []byte("hello world"), 0700); err != nil {
  143. t.Fatal(err)
  144. }
  145. if err := system.Mknod(filepath.Join(origin, "2"), syscall.S_IFBLK, int(system.Mkdev(int64(12), int64(5)))); err != nil {
  146. t.Fatal(err)
  147. }
  148. if err := system.Mknod(filepath.Join(origin, "3"), syscall.S_IFCHR, int(system.Mkdev(int64(12), int64(5)))); err != nil {
  149. t.Fatal(err)
  150. }
  151. if err := system.Mknod(filepath.Join(origin, "4"), syscall.S_IFIFO, int(system.Mkdev(int64(12), int64(5)))); err != nil {
  152. t.Fatal(err)
  153. }
  154. dest, err := ioutil.TempDir("", "docker-test-tar-hardlink-dest")
  155. if err != nil {
  156. t.Fatal(err)
  157. }
  158. defer os.RemoveAll(dest)
  159. // we'll do this in two steps to separate failure
  160. fh, err := Tar(origin, Uncompressed)
  161. if err != nil {
  162. t.Fatal(err)
  163. }
  164. // ensure we can read the whole thing with no error, before writing back out
  165. buf, err := ioutil.ReadAll(fh)
  166. if err != nil {
  167. t.Fatal(err)
  168. }
  169. bRdr := bytes.NewReader(buf)
  170. err = Untar(bRdr, dest, &TarOptions{Compression: Uncompressed})
  171. if err != nil {
  172. t.Fatal(err)
  173. }
  174. changes, err := ChangesDirs(origin, dest)
  175. if err != nil {
  176. t.Fatal(err)
  177. }
  178. if len(changes) > 0 {
  179. t.Fatalf("Tar with special device (block, char, fifo) should keep them (recreate them when untar) : %v", changes)
  180. }
  181. }
  182. // TestTarUntarWithXattr is Unix as Lsetxattr is not supported on Windows
  183. func TestTarUntarWithXattr(t *testing.T) {
  184. if runtime.GOOS == "solaris" {
  185. t.Skip()
  186. }
  187. origin, err := ioutil.TempDir("", "docker-test-untar-origin")
  188. if err != nil {
  189. t.Fatal(err)
  190. }
  191. defer os.RemoveAll(origin)
  192. if err := ioutil.WriteFile(filepath.Join(origin, "1"), []byte("hello world"), 0700); err != nil {
  193. t.Fatal(err)
  194. }
  195. if err := ioutil.WriteFile(filepath.Join(origin, "2"), []byte("welcome!"), 0700); err != nil {
  196. t.Fatal(err)
  197. }
  198. if err := ioutil.WriteFile(filepath.Join(origin, "3"), []byte("will be ignored"), 0700); err != nil {
  199. t.Fatal(err)
  200. }
  201. if err := system.Lsetxattr(filepath.Join(origin, "2"), "security.capability", []byte{0x00}, 0); err != nil {
  202. t.Fatal(err)
  203. }
  204. for _, c := range []Compression{
  205. Uncompressed,
  206. Gzip,
  207. } {
  208. changes, err := tarUntar(t, origin, &TarOptions{
  209. Compression: c,
  210. ExcludePatterns: []string{"3"},
  211. })
  212. if err != nil {
  213. t.Fatalf("Error tar/untar for compression %s: %s", c.Extension(), err)
  214. }
  215. if len(changes) != 1 || changes[0].Path != "/3" {
  216. t.Fatalf("Unexpected differences after tarUntar: %v", changes)
  217. }
  218. capability, _ := system.Lgetxattr(filepath.Join(origin, "2"), "security.capability")
  219. if capability == nil && capability[0] != 0x00 {
  220. t.Fatalf("Untar should have kept the 'security.capability' xattr.")
  221. }
  222. }
  223. }