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