copy_unix_test.go 28 KB

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