diff_test.go 7.1 KB

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