diff_test.go 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  1. package archive
  2. import (
  3. "archive/tar"
  4. "io"
  5. "io/ioutil"
  6. "os"
  7. "path/filepath"
  8. "reflect"
  9. "testing"
  10. "github.com/docker/docker/pkg/ioutils"
  11. )
  12. func TestApplyLayerInvalidFilenames(t *testing.T) {
  13. for i, headers := range [][]*tar.Header{
  14. {
  15. {
  16. Name: "../victim/dotdot",
  17. Typeflag: tar.TypeReg,
  18. Mode: 0644,
  19. },
  20. },
  21. {
  22. {
  23. // Note the leading slash
  24. Name: "/../victim/slash-dotdot",
  25. Typeflag: tar.TypeReg,
  26. Mode: 0644,
  27. },
  28. },
  29. } {
  30. if err := testBreakout("applylayer", "docker-TestApplyLayerInvalidFilenames", headers); err != nil {
  31. t.Fatalf("i=%d. %v", i, err)
  32. }
  33. }
  34. }
  35. func TestApplyLayerInvalidHardlink(t *testing.T) {
  36. for i, headers := range [][]*tar.Header{
  37. { // try reading victim/hello (../)
  38. {
  39. Name: "dotdot",
  40. Typeflag: tar.TypeLink,
  41. Linkname: "../victim/hello",
  42. Mode: 0644,
  43. },
  44. },
  45. { // try reading victim/hello (/../)
  46. {
  47. Name: "slash-dotdot",
  48. Typeflag: tar.TypeLink,
  49. // Note the leading slash
  50. Linkname: "/../victim/hello",
  51. Mode: 0644,
  52. },
  53. },
  54. { // try writing victim/file
  55. {
  56. Name: "loophole-victim",
  57. Typeflag: tar.TypeLink,
  58. Linkname: "../victim",
  59. Mode: 0755,
  60. },
  61. {
  62. Name: "loophole-victim/file",
  63. Typeflag: tar.TypeReg,
  64. Mode: 0644,
  65. },
  66. },
  67. { // try reading victim/hello (hardlink, symlink)
  68. {
  69. Name: "loophole-victim",
  70. Typeflag: tar.TypeLink,
  71. Linkname: "../victim",
  72. Mode: 0755,
  73. },
  74. {
  75. Name: "symlink",
  76. Typeflag: tar.TypeSymlink,
  77. Linkname: "loophole-victim/hello",
  78. Mode: 0644,
  79. },
  80. },
  81. { // Try reading victim/hello (hardlink, hardlink)
  82. {
  83. Name: "loophole-victim",
  84. Typeflag: tar.TypeLink,
  85. Linkname: "../victim",
  86. Mode: 0755,
  87. },
  88. {
  89. Name: "hardlink",
  90. Typeflag: tar.TypeLink,
  91. Linkname: "loophole-victim/hello",
  92. Mode: 0644,
  93. },
  94. },
  95. { // Try removing victim directory (hardlink)
  96. {
  97. Name: "loophole-victim",
  98. Typeflag: tar.TypeLink,
  99. Linkname: "../victim",
  100. Mode: 0755,
  101. },
  102. {
  103. Name: "loophole-victim",
  104. Typeflag: tar.TypeReg,
  105. Mode: 0644,
  106. },
  107. },
  108. } {
  109. if err := testBreakout("applylayer", "docker-TestApplyLayerInvalidHardlink", headers); err != nil {
  110. t.Fatalf("i=%d. %v", i, err)
  111. }
  112. }
  113. }
  114. func TestApplyLayerInvalidSymlink(t *testing.T) {
  115. for i, headers := range [][]*tar.Header{
  116. { // try reading victim/hello (../)
  117. {
  118. Name: "dotdot",
  119. Typeflag: tar.TypeSymlink,
  120. Linkname: "../victim/hello",
  121. Mode: 0644,
  122. },
  123. },
  124. { // try reading victim/hello (/../)
  125. {
  126. Name: "slash-dotdot",
  127. Typeflag: tar.TypeSymlink,
  128. // Note the leading slash
  129. Linkname: "/../victim/hello",
  130. Mode: 0644,
  131. },
  132. },
  133. { // try writing victim/file
  134. {
  135. Name: "loophole-victim",
  136. Typeflag: tar.TypeSymlink,
  137. Linkname: "../victim",
  138. Mode: 0755,
  139. },
  140. {
  141. Name: "loophole-victim/file",
  142. Typeflag: tar.TypeReg,
  143. Mode: 0644,
  144. },
  145. },
  146. { // try reading victim/hello (symlink, symlink)
  147. {
  148. Name: "loophole-victim",
  149. Typeflag: tar.TypeSymlink,
  150. Linkname: "../victim",
  151. Mode: 0755,
  152. },
  153. {
  154. Name: "symlink",
  155. Typeflag: tar.TypeSymlink,
  156. Linkname: "loophole-victim/hello",
  157. Mode: 0644,
  158. },
  159. },
  160. { // try reading victim/hello (symlink, hardlink)
  161. {
  162. Name: "loophole-victim",
  163. Typeflag: tar.TypeSymlink,
  164. Linkname: "../victim",
  165. Mode: 0755,
  166. },
  167. {
  168. Name: "hardlink",
  169. Typeflag: tar.TypeLink,
  170. Linkname: "loophole-victim/hello",
  171. Mode: 0644,
  172. },
  173. },
  174. { // try removing victim directory (symlink)
  175. {
  176. Name: "loophole-victim",
  177. Typeflag: tar.TypeSymlink,
  178. Linkname: "../victim",
  179. Mode: 0755,
  180. },
  181. {
  182. Name: "loophole-victim",
  183. Typeflag: tar.TypeReg,
  184. Mode: 0644,
  185. },
  186. },
  187. } {
  188. if err := testBreakout("applylayer", "docker-TestApplyLayerInvalidSymlink", headers); err != nil {
  189. t.Fatalf("i=%d. %v", i, err)
  190. }
  191. }
  192. }
  193. func TestApplyLayerWhiteouts(t *testing.T) {
  194. wd, err := ioutil.TempDir("", "graphdriver-test-whiteouts")
  195. if err != nil {
  196. return
  197. }
  198. defer os.RemoveAll(wd)
  199. base := []string{
  200. ".baz",
  201. "bar/",
  202. "bar/bax",
  203. "bar/bay/",
  204. "baz",
  205. "foo/",
  206. "foo/.abc",
  207. "foo/.bcd/",
  208. "foo/.bcd/a",
  209. "foo/cde/",
  210. "foo/cde/def",
  211. "foo/cde/efg",
  212. "foo/fgh",
  213. "foobar",
  214. }
  215. type tcase struct {
  216. change, expected []string
  217. }
  218. tcases := []tcase{
  219. {
  220. base,
  221. base,
  222. },
  223. {
  224. []string{
  225. ".bay",
  226. ".wh.baz",
  227. "foo/",
  228. "foo/.bce",
  229. "foo/.wh..wh..opq",
  230. "foo/cde/",
  231. "foo/cde/efg",
  232. },
  233. []string{
  234. ".bay",
  235. ".baz",
  236. "bar/",
  237. "bar/bax",
  238. "bar/bay/",
  239. "foo/",
  240. "foo/.bce",
  241. "foo/cde/",
  242. "foo/cde/efg",
  243. "foobar",
  244. },
  245. },
  246. {
  247. []string{
  248. ".bay",
  249. ".wh..baz",
  250. ".wh.foobar",
  251. "foo/",
  252. "foo/.abc",
  253. "foo/.wh.cde",
  254. "bar/",
  255. },
  256. []string{
  257. ".bay",
  258. "bar/",
  259. "bar/bax",
  260. "bar/bay/",
  261. "foo/",
  262. "foo/.abc",
  263. "foo/.bce",
  264. },
  265. },
  266. {
  267. []string{
  268. ".abc",
  269. ".wh..wh..opq",
  270. "foobar",
  271. },
  272. []string{
  273. ".abc",
  274. "foobar",
  275. },
  276. },
  277. }
  278. for i, tc := range tcases {
  279. l, err := makeTestLayer(tc.change)
  280. if err != nil {
  281. t.Fatal(err)
  282. }
  283. _, err = UnpackLayer(wd, l, nil)
  284. if err != nil {
  285. t.Fatal(err)
  286. }
  287. err = l.Close()
  288. if err != nil {
  289. t.Fatal(err)
  290. }
  291. paths, err := readDirContents(wd)
  292. if err != nil {
  293. t.Fatal(err)
  294. }
  295. if !reflect.DeepEqual(tc.expected, paths) {
  296. t.Fatalf("invalid files for layer %d: expected %q, got %q", i, tc.expected, paths)
  297. }
  298. }
  299. }
  300. func makeTestLayer(paths []string) (rc io.ReadCloser, err error) {
  301. tmpDir, err := ioutil.TempDir("", "graphdriver-test-mklayer")
  302. if err != nil {
  303. return
  304. }
  305. defer func() {
  306. if err != nil {
  307. os.RemoveAll(tmpDir)
  308. }
  309. }()
  310. for _, p := range paths {
  311. if p[len(p)-1] == filepath.Separator {
  312. if err = os.MkdirAll(filepath.Join(tmpDir, p), 0700); err != nil {
  313. return
  314. }
  315. } else {
  316. if err = ioutil.WriteFile(filepath.Join(tmpDir, p), nil, 0600); err != nil {
  317. return
  318. }
  319. }
  320. }
  321. archive, err := Tar(tmpDir, Uncompressed)
  322. if err != nil {
  323. return
  324. }
  325. return ioutils.NewReadCloserWrapper(archive, func() error {
  326. err := archive.Close()
  327. os.RemoveAll(tmpDir)
  328. return err
  329. }), nil
  330. }
  331. func readDirContents(root string) ([]string, error) {
  332. var files []string
  333. err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
  334. if err != nil {
  335. return err
  336. }
  337. if path == root {
  338. return nil
  339. }
  340. rel, err := filepath.Rel(root, path)
  341. if err != nil {
  342. return err
  343. }
  344. if info.IsDir() {
  345. rel = rel + "/"
  346. }
  347. files = append(files, rel)
  348. return nil
  349. })
  350. if err != nil {
  351. return nil, err
  352. }
  353. return files, nil
  354. }