copy_unix_test.go 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989
  1. //go:build !windows
  2. // +build !windows
  3. // TODO Windows: Some of these tests may be salvageable and portable to Windows.
  4. package archive // import "github.com/docker/docker/pkg/archive"
  5. import (
  6. "bytes"
  7. "crypto/sha256"
  8. "encoding/hex"
  9. "fmt"
  10. "io"
  11. "os"
  12. "path/filepath"
  13. "strings"
  14. "testing"
  15. "gotest.tools/v3/assert"
  16. )
  17. func removeAllPaths(paths ...string) {
  18. for _, path := range paths {
  19. os.RemoveAll(path)
  20. }
  21. }
  22. func getTestTempDirs(t *testing.T) (tmpDirA, tmpDirB string) {
  23. var err error
  24. tmpDirA, err = os.MkdirTemp("", "archive-copy-test")
  25. assert.NilError(t, err)
  26. tmpDirB, err = os.MkdirTemp("", "archive-copy-test")
  27. assert.NilError(t, err)
  28. return
  29. }
  30. func isNotDir(err error) bool {
  31. return strings.Contains(err.Error(), "not a directory")
  32. }
  33. func joinTrailingSep(pathElements ...string) string {
  34. joined := filepath.Join(pathElements...)
  35. return fmt.Sprintf("%s%c", joined, filepath.Separator)
  36. }
  37. func fileContentsEqual(t *testing.T, filenameA, filenameB string) (err error) {
  38. t.Logf("checking for equal file contents: %q and %q\n", filenameA, filenameB)
  39. fileA, err := os.Open(filenameA)
  40. if err != nil {
  41. return
  42. }
  43. defer fileA.Close()
  44. fileB, err := os.Open(filenameB)
  45. if err != nil {
  46. return
  47. }
  48. defer fileB.Close()
  49. hasher := sha256.New()
  50. if _, err = io.Copy(hasher, fileA); err != nil {
  51. return
  52. }
  53. hashA := hasher.Sum(nil)
  54. hasher.Reset()
  55. if _, err = io.Copy(hasher, fileB); err != nil {
  56. return
  57. }
  58. hashB := hasher.Sum(nil)
  59. if !bytes.Equal(hashA, hashB) {
  60. err = fmt.Errorf("file content hashes not equal - expected %s, got %s", hex.EncodeToString(hashA), hex.EncodeToString(hashB))
  61. }
  62. return
  63. }
  64. func dirContentsEqual(t *testing.T, newDir, oldDir string) (err error) {
  65. t.Logf("checking for equal directory contents: %q and %q\n", newDir, oldDir)
  66. var changes []Change
  67. if changes, err = ChangesDirs(newDir, oldDir); err != nil {
  68. return
  69. }
  70. if len(changes) != 0 {
  71. err = fmt.Errorf("expected no changes between directories, but got: %v", changes)
  72. }
  73. return
  74. }
  75. func logDirContents(t *testing.T, dirPath string) {
  76. t.Logf("logging directory contents: %q", dirPath)
  77. err := filepath.WalkDir(dirPath, func(path string, info os.DirEntry, err error) error {
  78. if err != nil {
  79. t.Errorf("stat error for path %q: %s", path, err)
  80. return nil
  81. }
  82. if info.IsDir() {
  83. path = joinTrailingSep(path)
  84. }
  85. t.Logf("\t%s", path)
  86. return nil
  87. })
  88. assert.NilError(t, err)
  89. }
  90. func testCopyHelper(t *testing.T, srcPath, dstPath string) (err error) {
  91. t.Logf("copying from %q to %q (not follow symbol link)", srcPath, dstPath)
  92. return CopyResource(srcPath, dstPath, false)
  93. }
  94. func testCopyHelperFSym(t *testing.T, srcPath, dstPath string) (err error) {
  95. t.Logf("copying from %q to %q (follow symbol link)", srcPath, dstPath)
  96. return CopyResource(srcPath, dstPath, true)
  97. }
  98. // Basic assumptions about SRC and DST:
  99. // 1. SRC must exist.
  100. // 2. If SRC ends with a trailing separator, it must be a directory.
  101. // 3. DST parent directory must exist.
  102. // 4. If DST exists as a file, it must not end with a trailing separator.
  103. // First get these easy error cases out of the way.
  104. // Test for error when SRC does not exist.
  105. func TestCopyErrSrcNotExists(t *testing.T) {
  106. tmpDirA, tmpDirB := getTestTempDirs(t)
  107. defer removeAllPaths(tmpDirA, tmpDirB)
  108. if _, err := CopyInfoSourcePath(filepath.Join(tmpDirA, "file1"), false); !os.IsNotExist(err) {
  109. t.Fatalf("expected IsNotExist error, but got %T: %s", err, err)
  110. }
  111. }
  112. // Test for error when SRC ends in a trailing
  113. // path separator but it exists as a file.
  114. func TestCopyErrSrcNotDir(t *testing.T) {
  115. tmpDirA, tmpDirB := getTestTempDirs(t)
  116. defer removeAllPaths(tmpDirA, tmpDirB)
  117. // Load A with some sample files and directories.
  118. createSampleDir(t, tmpDirA)
  119. if _, err := CopyInfoSourcePath(joinTrailingSep(tmpDirA, "file1"), false); !isNotDir(err) {
  120. t.Fatalf("expected IsNotDir error, but got %T: %s", err, err)
  121. }
  122. }
  123. // Test for error when SRC is a valid file or directory,
  124. // but the DST parent directory does not exist.
  125. func TestCopyErrDstParentNotExists(t *testing.T) {
  126. tmpDirA, tmpDirB := getTestTempDirs(t)
  127. defer removeAllPaths(tmpDirA, tmpDirB)
  128. // Load A with some sample files and directories.
  129. createSampleDir(t, tmpDirA)
  130. srcInfo := CopyInfo{Path: filepath.Join(tmpDirA, "file1"), Exists: true, IsDir: false}
  131. // Try with a file source.
  132. content, err := TarResource(srcInfo)
  133. if err != nil {
  134. t.Fatalf("unexpected error %T: %s", err, err)
  135. }
  136. defer content.Close()
  137. // Copy to a file whose parent does not exist.
  138. if err = CopyTo(content, srcInfo, filepath.Join(tmpDirB, "fakeParentDir", "file1")); err == nil {
  139. t.Fatal("expected IsNotExist error, but got nil instead")
  140. }
  141. if !os.IsNotExist(err) {
  142. t.Fatalf("expected IsNotExist error, but got %T: %s", err, err)
  143. }
  144. // Try with a directory source.
  145. srcInfo = CopyInfo{Path: filepath.Join(tmpDirA, "dir1"), Exists: true, IsDir: true}
  146. content, err = TarResource(srcInfo)
  147. if err != nil {
  148. t.Fatalf("unexpected error %T: %s", err, err)
  149. }
  150. defer content.Close()
  151. // Copy to a directory whose parent does not exist.
  152. if err = CopyTo(content, srcInfo, joinTrailingSep(tmpDirB, "fakeParentDir", "fakeDstDir")); err == nil {
  153. t.Fatal("expected IsNotExist error, but got nil instead")
  154. }
  155. if !os.IsNotExist(err) {
  156. t.Fatalf("expected IsNotExist error, but got %T: %s", err, err)
  157. }
  158. }
  159. // Test for error when DST ends in a trailing
  160. // path separator but exists as a file.
  161. func TestCopyErrDstNotDir(t *testing.T) {
  162. tmpDirA, tmpDirB := getTestTempDirs(t)
  163. defer removeAllPaths(tmpDirA, tmpDirB)
  164. // Load A and B with some sample files and directories.
  165. createSampleDir(t, tmpDirA)
  166. createSampleDir(t, tmpDirB)
  167. // Try with a file source.
  168. srcInfo := CopyInfo{Path: filepath.Join(tmpDirA, "file1"), Exists: true, IsDir: false}
  169. content, err := TarResource(srcInfo)
  170. if err != nil {
  171. t.Fatalf("unexpected error %T: %s", err, err)
  172. }
  173. defer content.Close()
  174. if err = CopyTo(content, srcInfo, joinTrailingSep(tmpDirB, "file1")); err == nil {
  175. t.Fatal("expected IsNotDir error, but got nil instead")
  176. }
  177. if !isNotDir(err) {
  178. t.Fatalf("expected IsNotDir error, but got %T: %s", err, err)
  179. }
  180. // Try with a directory source.
  181. srcInfo = CopyInfo{Path: filepath.Join(tmpDirA, "dir1"), Exists: true, IsDir: true}
  182. content, err = TarResource(srcInfo)
  183. if err != nil {
  184. t.Fatalf("unexpected error %T: %s", err, err)
  185. }
  186. defer content.Close()
  187. if err = CopyTo(content, srcInfo, joinTrailingSep(tmpDirB, "file1")); err == nil {
  188. t.Fatal("expected IsNotDir error, but got nil instead")
  189. }
  190. if !isNotDir(err) {
  191. t.Fatalf("expected IsNotDir error, but got %T: %s", err, err)
  192. }
  193. }
  194. // Test to check if CopyTo works with a long (>100 characters) destination file name.
  195. // This is a regression (see https://github.com/docker/for-linux/issues/484).
  196. func TestCopyLongDstFilename(t *testing.T) {
  197. const longName = "a_very_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_long_filename_that_is_101_characters"
  198. tmpDirA, tmpDirB := getTestTempDirs(t)
  199. defer removeAllPaths(tmpDirA, tmpDirB)
  200. // Load A with some sample files and directories.
  201. createSampleDir(t, tmpDirA)
  202. srcInfo := CopyInfo{Path: filepath.Join(tmpDirA, "file1"), Exists: true, IsDir: false}
  203. content, err := TarResource(srcInfo)
  204. if err != nil {
  205. t.Fatalf("unexpected error %T: %s", err, err)
  206. }
  207. defer content.Close()
  208. err = CopyTo(content, srcInfo, filepath.Join(tmpDirB, longName))
  209. if err != nil {
  210. t.Fatalf("unexpected error %T: %s", err, err)
  211. }
  212. }
  213. // Possibilities are reduced to the remaining 10 cases:
  214. //
  215. // case | srcIsDir | onlyDirContents | dstExists | dstIsDir | dstTrSep | action
  216. // ===================================================================================================
  217. // A | no | - | no | - | no | create file
  218. // B | no | - | no | - | yes | error
  219. // C | no | - | yes | no | - | overwrite file
  220. // D | no | - | yes | yes | - | create file in dst dir
  221. // E | yes | no | no | - | - | create dir, copy contents
  222. // F | yes | no | yes | no | - | error
  223. // G | yes | no | yes | yes | - | copy dir and contents
  224. // H | yes | yes | no | - | - | create dir, copy contents
  225. // I | yes | yes | yes | no | - | error
  226. // J | yes | yes | yes | yes | - | copy dir contents
  227. //
  228. // A. SRC specifies a file and DST (no trailing path separator) doesn't exist.
  229. //
  230. // This should create a file with the name DST and copy the contents of the source
  231. // file into it.
  232. func TestCopyCaseA(t *testing.T) {
  233. tmpDirA, tmpDirB := getTestTempDirs(t)
  234. defer removeAllPaths(tmpDirA, tmpDirB)
  235. // Load A with some sample files and directories.
  236. createSampleDir(t, tmpDirA)
  237. srcPath := filepath.Join(tmpDirA, "file1")
  238. dstPath := filepath.Join(tmpDirB, "itWorks.txt")
  239. var err error
  240. if err = testCopyHelper(t, srcPath, dstPath); err != nil {
  241. t.Fatalf("unexpected error %T: %s", err, err)
  242. }
  243. err = fileContentsEqual(t, srcPath, dstPath)
  244. assert.NilError(t, err)
  245. os.Remove(dstPath)
  246. symlinkPath := filepath.Join(tmpDirA, "symlink3")
  247. symlinkPath1 := filepath.Join(tmpDirA, "symlink4")
  248. linkTarget := filepath.Join(tmpDirA, "file1")
  249. if err = testCopyHelperFSym(t, symlinkPath, dstPath); err != nil {
  250. t.Fatalf("unexpected error %T: %s", err, err)
  251. }
  252. err = fileContentsEqual(t, linkTarget, dstPath)
  253. assert.NilError(t, err)
  254. os.Remove(dstPath)
  255. if err = testCopyHelperFSym(t, symlinkPath1, dstPath); err != nil {
  256. t.Fatalf("unexpected error %T: %s", err, err)
  257. }
  258. err = fileContentsEqual(t, linkTarget, dstPath)
  259. assert.NilError(t, err)
  260. }
  261. // B. SRC specifies a file and DST (with trailing path separator) doesn't exist.
  262. //
  263. // This should cause an error because the copy operation cannot create a directory
  264. // when copying a single file.
  265. func TestCopyCaseB(t *testing.T) {
  266. tmpDirA, tmpDirB := getTestTempDirs(t)
  267. defer removeAllPaths(tmpDirA, tmpDirB)
  268. // Load A with some sample files and directories.
  269. createSampleDir(t, tmpDirA)
  270. srcPath := filepath.Join(tmpDirA, "file1")
  271. dstDir := joinTrailingSep(tmpDirB, "testDir")
  272. var err error
  273. if err = testCopyHelper(t, srcPath, dstDir); err == nil {
  274. t.Fatal("expected ErrDirNotExists error, but got nil instead")
  275. }
  276. if err != ErrDirNotExists {
  277. t.Fatalf("expected ErrDirNotExists error, but got %T: %s", err, err)
  278. }
  279. symlinkPath := filepath.Join(tmpDirA, "symlink3")
  280. if err = testCopyHelperFSym(t, symlinkPath, dstDir); err == nil {
  281. t.Fatal("expected ErrDirNotExists error, but got nil instead")
  282. }
  283. if err != ErrDirNotExists {
  284. t.Fatalf("expected ErrDirNotExists error, but got %T: %s", err, err)
  285. }
  286. }
  287. // C. SRC specifies a file and DST exists as a file.
  288. //
  289. // This should overwrite the file at DST with the contents of the source file.
  290. func TestCopyCaseC(t *testing.T) {
  291. tmpDirA, tmpDirB := getTestTempDirs(t)
  292. defer removeAllPaths(tmpDirA, tmpDirB)
  293. // Load A and B with some sample files and directories.
  294. createSampleDir(t, tmpDirA)
  295. createSampleDir(t, tmpDirB)
  296. srcPath := filepath.Join(tmpDirA, "file1")
  297. dstPath := filepath.Join(tmpDirB, "file2")
  298. var err error
  299. // Ensure they start out different.
  300. if err = fileContentsEqual(t, srcPath, dstPath); err == nil {
  301. t.Fatal("expected different file contents")
  302. }
  303. if err = testCopyHelper(t, srcPath, dstPath); err != nil {
  304. t.Fatalf("unexpected error %T: %s", err, err)
  305. }
  306. err = fileContentsEqual(t, srcPath, dstPath)
  307. assert.NilError(t, err)
  308. }
  309. // C. Symbol link following version: SRC specifies a file and DST exists as a file.
  310. //
  311. // This should overwrite the file at DST with the contents of the source file.
  312. func TestCopyCaseCFSym(t *testing.T) {
  313. tmpDirA, tmpDirB := getTestTempDirs(t)
  314. defer removeAllPaths(tmpDirA, tmpDirB)
  315. // Load A and B with some sample files and directories.
  316. createSampleDir(t, tmpDirA)
  317. createSampleDir(t, tmpDirB)
  318. symlinkPathBad := filepath.Join(tmpDirA, "symlink1")
  319. symlinkPath := filepath.Join(tmpDirA, "symlink3")
  320. linkTarget := filepath.Join(tmpDirA, "file1")
  321. dstPath := filepath.Join(tmpDirB, "file2")
  322. var err error
  323. // first to test broken link
  324. if err = testCopyHelperFSym(t, symlinkPathBad, dstPath); err == nil {
  325. t.Fatalf("unexpected error %T: %s", err, err)
  326. }
  327. // test symbol link -> symbol link -> target
  328. // Ensure they start out different.
  329. if err = fileContentsEqual(t, linkTarget, dstPath); err == nil {
  330. t.Fatal("expected different file contents")
  331. }
  332. if err = testCopyHelperFSym(t, symlinkPath, dstPath); err != nil {
  333. t.Fatalf("unexpected error %T: %s", err, err)
  334. }
  335. err = fileContentsEqual(t, linkTarget, dstPath)
  336. assert.NilError(t, err)
  337. }
  338. // D. SRC specifies a file and DST exists as a directory.
  339. //
  340. // This should place a copy of the source file inside it using the basename from
  341. // SRC. Ensure this works whether DST has a trailing path separator or not.
  342. func TestCopyCaseD(t *testing.T) {
  343. tmpDirA, tmpDirB := getTestTempDirs(t)
  344. defer removeAllPaths(tmpDirA, tmpDirB)
  345. // Load A and B with some sample files and directories.
  346. createSampleDir(t, tmpDirA)
  347. createSampleDir(t, tmpDirB)
  348. srcPath := filepath.Join(tmpDirA, "file1")
  349. dstDir := filepath.Join(tmpDirB, "dir1")
  350. dstPath := filepath.Join(dstDir, "file1")
  351. var err error
  352. // Ensure that dstPath doesn't exist.
  353. if _, err = os.Stat(dstPath); !os.IsNotExist(err) {
  354. t.Fatalf("did not expect dstPath %q to exist", dstPath)
  355. }
  356. if err = testCopyHelper(t, srcPath, dstDir); err != nil {
  357. t.Fatalf("unexpected error %T: %s", err, err)
  358. }
  359. err = fileContentsEqual(t, srcPath, dstPath)
  360. assert.NilError(t, err)
  361. // Now try again but using a trailing path separator for dstDir.
  362. if err = os.RemoveAll(dstDir); err != nil {
  363. t.Fatalf("unable to remove dstDir: %s", err)
  364. }
  365. if err = os.MkdirAll(dstDir, os.FileMode(0755)); err != nil {
  366. t.Fatalf("unable to make dstDir: %s", err)
  367. }
  368. dstDir = joinTrailingSep(tmpDirB, "dir1")
  369. if err = testCopyHelper(t, srcPath, dstDir); err != nil {
  370. t.Fatalf("unexpected error %T: %s", err, err)
  371. }
  372. err = fileContentsEqual(t, srcPath, dstPath)
  373. assert.NilError(t, err)
  374. }
  375. // D. Symbol link following version: SRC specifies a file and DST exists as a directory.
  376. //
  377. // This should place a copy of the source file inside it using the basename from
  378. // SRC. Ensure this works whether DST has a trailing path separator or not.
  379. func TestCopyCaseDFSym(t *testing.T) {
  380. tmpDirA, tmpDirB := getTestTempDirs(t)
  381. defer removeAllPaths(tmpDirA, tmpDirB)
  382. // Load A and B with some sample files and directories.
  383. createSampleDir(t, tmpDirA)
  384. createSampleDir(t, tmpDirB)
  385. srcPath := filepath.Join(tmpDirA, "symlink4")
  386. linkTarget := filepath.Join(tmpDirA, "file1")
  387. dstDir := filepath.Join(tmpDirB, "dir1")
  388. dstPath := filepath.Join(dstDir, "symlink4")
  389. var err error
  390. // Ensure that dstPath doesn't exist.
  391. if _, err = os.Stat(dstPath); !os.IsNotExist(err) {
  392. t.Fatalf("did not expect dstPath %q to exist", dstPath)
  393. }
  394. if err = testCopyHelperFSym(t, srcPath, dstDir); err != nil {
  395. t.Fatalf("unexpected error %T: %s", err, err)
  396. }
  397. err = fileContentsEqual(t, linkTarget, dstPath)
  398. assert.NilError(t, err)
  399. // Now try again but using a trailing path separator for dstDir.
  400. if err = os.RemoveAll(dstDir); err != nil {
  401. t.Fatalf("unable to remove dstDir: %s", err)
  402. }
  403. if err = os.MkdirAll(dstDir, os.FileMode(0755)); err != nil {
  404. t.Fatalf("unable to make dstDir: %s", err)
  405. }
  406. dstDir = joinTrailingSep(tmpDirB, "dir1")
  407. if err = testCopyHelperFSym(t, srcPath, dstDir); err != nil {
  408. t.Fatalf("unexpected error %T: %s", err, err)
  409. }
  410. err = fileContentsEqual(t, linkTarget, dstPath)
  411. assert.NilError(t, err)
  412. }
  413. // E. SRC specifies a directory and DST does not exist.
  414. //
  415. // This should create a directory at DST and copy the contents of the SRC directory
  416. // into the DST directory. Ensure this works whether DST has a trailing path
  417. // separator or not.
  418. func TestCopyCaseE(t *testing.T) {
  419. tmpDirA, tmpDirB := getTestTempDirs(t)
  420. defer removeAllPaths(tmpDirA, tmpDirB)
  421. // Load A with some sample files and directories.
  422. createSampleDir(t, tmpDirA)
  423. srcDir := filepath.Join(tmpDirA, "dir1")
  424. dstDir := filepath.Join(tmpDirB, "testDir")
  425. var err error
  426. if err = testCopyHelper(t, srcDir, dstDir); err != nil {
  427. t.Fatalf("unexpected error %T: %s", err, err)
  428. }
  429. if err = dirContentsEqual(t, dstDir, srcDir); err != nil {
  430. t.Log("dir contents not equal")
  431. logDirContents(t, tmpDirA)
  432. logDirContents(t, tmpDirB)
  433. t.Fatal(err)
  434. }
  435. // Now try again but using a trailing path separator for dstDir.
  436. if err = os.RemoveAll(dstDir); err != nil {
  437. t.Fatalf("unable to remove dstDir: %s", err)
  438. }
  439. dstDir = joinTrailingSep(tmpDirB, "testDir")
  440. if err = testCopyHelper(t, srcDir, dstDir); err != nil {
  441. t.Fatalf("unexpected error %T: %s", err, err)
  442. }
  443. err = dirContentsEqual(t, dstDir, srcDir)
  444. assert.NilError(t, err)
  445. }
  446. // E. Symbol link following version: SRC specifies a directory and DST does not exist.
  447. //
  448. // This should create a directory at DST and copy the contents of the SRC directory
  449. // into the DST directory. Ensure this works whether DST has a trailing path
  450. // separator or not.
  451. func TestCopyCaseEFSym(t *testing.T) {
  452. tmpDirA, tmpDirB := getTestTempDirs(t)
  453. defer removeAllPaths(tmpDirA, tmpDirB)
  454. // Load A with some sample files and directories.
  455. createSampleDir(t, tmpDirA)
  456. srcDir := filepath.Join(tmpDirA, "dirSymlink")
  457. linkTarget := filepath.Join(tmpDirA, "dir1")
  458. dstDir := filepath.Join(tmpDirB, "testDir")
  459. var err error
  460. if err = testCopyHelperFSym(t, srcDir, dstDir); err != nil {
  461. t.Fatalf("unexpected error %T: %s", err, err)
  462. }
  463. if err = dirContentsEqual(t, dstDir, linkTarget); err != nil {
  464. t.Log("dir contents not equal")
  465. logDirContents(t, tmpDirA)
  466. logDirContents(t, tmpDirB)
  467. t.Fatal(err)
  468. }
  469. // Now try again but using a trailing path separator for dstDir.
  470. if err = os.RemoveAll(dstDir); err != nil {
  471. t.Fatalf("unable to remove dstDir: %s", err)
  472. }
  473. dstDir = joinTrailingSep(tmpDirB, "testDir")
  474. if err = testCopyHelperFSym(t, srcDir, dstDir); err != nil {
  475. t.Fatalf("unexpected error %T: %s", err, err)
  476. }
  477. err = dirContentsEqual(t, dstDir, linkTarget)
  478. assert.NilError(t, err)
  479. }
  480. // F. SRC specifies a directory and DST exists as a file.
  481. //
  482. // This should cause an error as it is not possible to overwrite a file with a
  483. // directory.
  484. func TestCopyCaseF(t *testing.T) {
  485. tmpDirA, tmpDirB := getTestTempDirs(t)
  486. defer removeAllPaths(tmpDirA, tmpDirB)
  487. // Load A and B with some sample files and directories.
  488. createSampleDir(t, tmpDirA)
  489. createSampleDir(t, tmpDirB)
  490. srcDir := filepath.Join(tmpDirA, "dir1")
  491. symSrcDir := filepath.Join(tmpDirA, "dirSymlink")
  492. dstFile := filepath.Join(tmpDirB, "file1")
  493. var err error
  494. if err = testCopyHelper(t, srcDir, dstFile); err == nil {
  495. t.Fatal("expected ErrCannotCopyDir error, but got nil instead")
  496. }
  497. if err != ErrCannotCopyDir {
  498. t.Fatalf("expected ErrCannotCopyDir error, but got %T: %s", err, err)
  499. }
  500. // now test with symbol link
  501. if err = testCopyHelperFSym(t, symSrcDir, dstFile); err == nil {
  502. t.Fatal("expected ErrCannotCopyDir error, but got nil instead")
  503. }
  504. if err != ErrCannotCopyDir {
  505. t.Fatalf("expected ErrCannotCopyDir error, but got %T: %s", err, err)
  506. }
  507. }
  508. // G. SRC specifies a directory and DST exists as a directory.
  509. //
  510. // This should copy the SRC directory and all its contents to the DST directory.
  511. // Ensure this works whether DST has a trailing path separator or not.
  512. func TestCopyCaseG(t *testing.T) {
  513. tmpDirA, tmpDirB := getTestTempDirs(t)
  514. defer removeAllPaths(tmpDirA, tmpDirB)
  515. // Load A and B with some sample files and directories.
  516. createSampleDir(t, tmpDirA)
  517. createSampleDir(t, tmpDirB)
  518. srcDir := filepath.Join(tmpDirA, "dir1")
  519. dstDir := filepath.Join(tmpDirB, "dir2")
  520. resultDir := filepath.Join(dstDir, "dir1")
  521. var err error
  522. if err = testCopyHelper(t, srcDir, dstDir); err != nil {
  523. t.Fatalf("unexpected error %T: %s", err, err)
  524. }
  525. err = dirContentsEqual(t, resultDir, srcDir)
  526. assert.NilError(t, err)
  527. // Now try again but using a trailing path separator for dstDir.
  528. if err = os.RemoveAll(dstDir); err != nil {
  529. t.Fatalf("unable to remove dstDir: %s", err)
  530. }
  531. if err = os.MkdirAll(dstDir, os.FileMode(0755)); err != nil {
  532. t.Fatalf("unable to make dstDir: %s", err)
  533. }
  534. dstDir = joinTrailingSep(tmpDirB, "dir2")
  535. if err = testCopyHelper(t, srcDir, dstDir); err != nil {
  536. t.Fatalf("unexpected error %T: %s", err, err)
  537. }
  538. err = dirContentsEqual(t, resultDir, srcDir)
  539. assert.NilError(t, err)
  540. }
  541. // G. Symbol link version: SRC specifies a directory and DST exists as a directory.
  542. //
  543. // This should copy the SRC directory and all its contents to the DST directory.
  544. // Ensure this works whether DST has a trailing path separator or not.
  545. func TestCopyCaseGFSym(t *testing.T) {
  546. tmpDirA, tmpDirB := getTestTempDirs(t)
  547. defer removeAllPaths(tmpDirA, tmpDirB)
  548. // Load A and B with some sample files and directories.
  549. createSampleDir(t, tmpDirA)
  550. createSampleDir(t, tmpDirB)
  551. srcDir := filepath.Join(tmpDirA, "dirSymlink")
  552. linkTarget := filepath.Join(tmpDirA, "dir1")
  553. dstDir := filepath.Join(tmpDirB, "dir2")
  554. resultDir := filepath.Join(dstDir, "dirSymlink")
  555. var err error
  556. if err = testCopyHelperFSym(t, srcDir, dstDir); err != nil {
  557. t.Fatalf("unexpected error %T: %s", err, err)
  558. }
  559. err = dirContentsEqual(t, resultDir, linkTarget)
  560. assert.NilError(t, err)
  561. // Now try again but using a trailing path separator for dstDir.
  562. if err = os.RemoveAll(dstDir); err != nil {
  563. t.Fatalf("unable to remove dstDir: %s", err)
  564. }
  565. if err = os.MkdirAll(dstDir, os.FileMode(0755)); err != nil {
  566. t.Fatalf("unable to make dstDir: %s", err)
  567. }
  568. dstDir = joinTrailingSep(tmpDirB, "dir2")
  569. if err = testCopyHelperFSym(t, srcDir, dstDir); err != nil {
  570. t.Fatalf("unexpected error %T: %s", err, err)
  571. }
  572. err = dirContentsEqual(t, resultDir, linkTarget)
  573. assert.NilError(t, err)
  574. }
  575. // H. SRC specifies a directory's contents only and DST does not exist.
  576. //
  577. // This should create a directory at DST and copy the contents of the SRC
  578. // directory (but not the directory itself) into the DST directory. Ensure
  579. // this works whether DST has a trailing path separator or not.
  580. func TestCopyCaseH(t *testing.T) {
  581. tmpDirA, tmpDirB := getTestTempDirs(t)
  582. defer removeAllPaths(tmpDirA, tmpDirB)
  583. // Load A with some sample files and directories.
  584. createSampleDir(t, tmpDirA)
  585. srcDir := joinTrailingSep(tmpDirA, "dir1") + "."
  586. dstDir := filepath.Join(tmpDirB, "testDir")
  587. var err error
  588. if err = testCopyHelper(t, srcDir, dstDir); err != nil {
  589. t.Fatalf("unexpected error %T: %s", err, err)
  590. }
  591. if err = dirContentsEqual(t, dstDir, srcDir); err != nil {
  592. t.Log("dir contents not equal")
  593. logDirContents(t, tmpDirA)
  594. logDirContents(t, tmpDirB)
  595. t.Fatal(err)
  596. }
  597. // Now try again but using a trailing path separator for dstDir.
  598. if err = os.RemoveAll(dstDir); err != nil {
  599. t.Fatalf("unable to remove dstDir: %s", err)
  600. }
  601. dstDir = joinTrailingSep(tmpDirB, "testDir")
  602. if err = testCopyHelper(t, srcDir, dstDir); err != nil {
  603. t.Fatalf("unexpected error %T: %s", err, err)
  604. }
  605. if err = dirContentsEqual(t, dstDir, srcDir); err != nil {
  606. t.Log("dir contents not equal")
  607. logDirContents(t, tmpDirA)
  608. logDirContents(t, tmpDirB)
  609. t.Fatal(err)
  610. }
  611. }
  612. // H. Symbol link following version: SRC specifies a directory's contents only and DST does not exist.
  613. //
  614. // This should create a directory at DST and copy the contents of the SRC
  615. // directory (but not the directory itself) into the DST directory. Ensure
  616. // this works whether DST has a trailing path separator or not.
  617. func TestCopyCaseHFSym(t *testing.T) {
  618. tmpDirA, tmpDirB := getTestTempDirs(t)
  619. defer removeAllPaths(tmpDirA, tmpDirB)
  620. // Load A with some sample files and directories.
  621. createSampleDir(t, tmpDirA)
  622. srcDir := joinTrailingSep(tmpDirA, "dirSymlink") + "."
  623. linkTarget := filepath.Join(tmpDirA, "dir1")
  624. dstDir := filepath.Join(tmpDirB, "testDir")
  625. var err error
  626. if err = testCopyHelperFSym(t, srcDir, dstDir); err != nil {
  627. t.Fatalf("unexpected error %T: %s", err, err)
  628. }
  629. if err = dirContentsEqual(t, dstDir, linkTarget); err != nil {
  630. t.Log("dir contents not equal")
  631. logDirContents(t, tmpDirA)
  632. logDirContents(t, tmpDirB)
  633. t.Fatal(err)
  634. }
  635. // Now try again but using a trailing path separator for dstDir.
  636. if err = os.RemoveAll(dstDir); err != nil {
  637. t.Fatalf("unable to remove dstDir: %s", err)
  638. }
  639. dstDir = joinTrailingSep(tmpDirB, "testDir")
  640. if err = testCopyHelperFSym(t, srcDir, dstDir); err != nil {
  641. t.Fatalf("unexpected error %T: %s", err, err)
  642. }
  643. if err = dirContentsEqual(t, dstDir, linkTarget); err != nil {
  644. t.Log("dir contents not equal")
  645. logDirContents(t, tmpDirA)
  646. logDirContents(t, tmpDirB)
  647. t.Fatal(err)
  648. }
  649. }
  650. // I. SRC specifies a directory's contents only and DST exists as a file.
  651. //
  652. // This should cause an error as it is not possible to overwrite a file with a
  653. // directory.
  654. func TestCopyCaseI(t *testing.T) {
  655. tmpDirA, tmpDirB := getTestTempDirs(t)
  656. defer removeAllPaths(tmpDirA, tmpDirB)
  657. // Load A and B with some sample files and directories.
  658. createSampleDir(t, tmpDirA)
  659. createSampleDir(t, tmpDirB)
  660. srcDir := joinTrailingSep(tmpDirA, "dir1") + "."
  661. symSrcDir := filepath.Join(tmpDirB, "dirSymlink")
  662. dstFile := filepath.Join(tmpDirB, "file1")
  663. var err error
  664. if err = testCopyHelper(t, srcDir, dstFile); err == nil {
  665. t.Fatal("expected ErrCannotCopyDir error, but got nil instead")
  666. }
  667. if err != ErrCannotCopyDir {
  668. t.Fatalf("expected ErrCannotCopyDir error, but got %T: %s", err, err)
  669. }
  670. // now try with symbol link of dir
  671. if err = testCopyHelperFSym(t, symSrcDir, dstFile); err == nil {
  672. t.Fatal("expected ErrCannotCopyDir error, but got nil instead")
  673. }
  674. if err != ErrCannotCopyDir {
  675. t.Fatalf("expected ErrCannotCopyDir error, but got %T: %s", err, err)
  676. }
  677. }
  678. // J. SRC specifies a directory's contents only and DST exists as a directory.
  679. //
  680. // This should copy the contents of the SRC directory (but not the directory
  681. // itself) into the DST directory. Ensure this works whether DST has a
  682. // trailing path separator or not.
  683. func TestCopyCaseJ(t *testing.T) {
  684. tmpDirA, tmpDirB := getTestTempDirs(t)
  685. defer removeAllPaths(tmpDirA, tmpDirB)
  686. // Load A and B with some sample files and directories.
  687. createSampleDir(t, tmpDirA)
  688. createSampleDir(t, tmpDirB)
  689. srcDir := joinTrailingSep(tmpDirA, "dir1") + "."
  690. dstDir := filepath.Join(tmpDirB, "dir5")
  691. var err error
  692. // first to create an empty dir
  693. if err = os.MkdirAll(dstDir, os.FileMode(0755)); err != nil {
  694. t.Fatalf("unable to make dstDir: %s", err)
  695. }
  696. if err = testCopyHelper(t, srcDir, dstDir); err != nil {
  697. t.Fatalf("unexpected error %T: %s", err, err)
  698. }
  699. err = dirContentsEqual(t, dstDir, srcDir)
  700. assert.NilError(t, err)
  701. // Now try again but using a trailing path separator for dstDir.
  702. if err = os.RemoveAll(dstDir); err != nil {
  703. t.Fatalf("unable to remove dstDir: %s", err)
  704. }
  705. if err = os.MkdirAll(dstDir, os.FileMode(0755)); err != nil {
  706. t.Fatalf("unable to make dstDir: %s", err)
  707. }
  708. dstDir = joinTrailingSep(tmpDirB, "dir5")
  709. if err = testCopyHelper(t, srcDir, dstDir); err != nil {
  710. t.Fatalf("unexpected error %T: %s", err, err)
  711. }
  712. err = dirContentsEqual(t, dstDir, srcDir)
  713. assert.NilError(t, err)
  714. }
  715. // J. Symbol link following version: SRC specifies a directory's contents only and DST exists as a directory.
  716. //
  717. // This should copy the contents of the SRC directory (but not the directory
  718. // itself) into the DST directory. Ensure this works whether DST has a
  719. // trailing path separator or not.
  720. func TestCopyCaseJFSym(t *testing.T) {
  721. tmpDirA, tmpDirB := getTestTempDirs(t)
  722. defer removeAllPaths(tmpDirA, tmpDirB)
  723. // Load A and B with some sample files and directories.
  724. createSampleDir(t, tmpDirA)
  725. createSampleDir(t, tmpDirB)
  726. srcDir := joinTrailingSep(tmpDirA, "dirSymlink") + "."
  727. linkTarget := filepath.Join(tmpDirA, "dir1")
  728. dstDir := filepath.Join(tmpDirB, "dir5")
  729. var err error
  730. // first to create an empty dir
  731. if err = os.MkdirAll(dstDir, os.FileMode(0755)); err != nil {
  732. t.Fatalf("unable to make dstDir: %s", err)
  733. }
  734. if err = testCopyHelperFSym(t, srcDir, dstDir); err != nil {
  735. t.Fatalf("unexpected error %T: %s", err, err)
  736. }
  737. err = dirContentsEqual(t, dstDir, linkTarget)
  738. assert.NilError(t, err)
  739. // Now try again but using a trailing path separator for dstDir.
  740. if err = os.RemoveAll(dstDir); err != nil {
  741. t.Fatalf("unable to remove dstDir: %s", err)
  742. }
  743. if err = os.MkdirAll(dstDir, os.FileMode(0755)); err != nil {
  744. t.Fatalf("unable to make dstDir: %s", err)
  745. }
  746. dstDir = joinTrailingSep(tmpDirB, "dir5")
  747. if err = testCopyHelperFSym(t, srcDir, dstDir); err != nil {
  748. t.Fatalf("unexpected error %T: %s", err, err)
  749. }
  750. err = dirContentsEqual(t, dstDir, linkTarget)
  751. assert.NilError(t, err)
  752. }