github.com/adityamillind98/moby@v23.0.0-rc.4+incompatible/pkg/archive/copy_unix_test.go (about)

     1  //go:build !windows
     2  // +build !windows
     3  
     4  // TODO Windows: Some of these tests may be salvageable and portable to Windows.
     5  
     6  package archive // import "github.com/docker/docker/pkg/archive"
     7  
     8  import (
     9  	"bytes"
    10  	"crypto/sha256"
    11  	"encoding/hex"
    12  	"fmt"
    13  	"io"
    14  	"os"
    15  	"path/filepath"
    16  	"strings"
    17  	"testing"
    18  
    19  	"gotest.tools/v3/assert"
    20  )
    21  
    22  func removeAllPaths(paths ...string) {
    23  	for _, path := range paths {
    24  		os.RemoveAll(path)
    25  	}
    26  }
    27  
    28  func getTestTempDirs(t *testing.T) (tmpDirA, tmpDirB string) {
    29  	var err error
    30  
    31  	tmpDirA, err = os.MkdirTemp("", "archive-copy-test")
    32  	assert.NilError(t, err)
    33  
    34  	tmpDirB, err = os.MkdirTemp("", "archive-copy-test")
    35  	assert.NilError(t, err)
    36  
    37  	return
    38  }
    39  
    40  func isNotDir(err error) bool {
    41  	return strings.Contains(err.Error(), "not a directory")
    42  }
    43  
    44  func joinTrailingSep(pathElements ...string) string {
    45  	joined := filepath.Join(pathElements...)
    46  
    47  	return fmt.Sprintf("%s%c", joined, filepath.Separator)
    48  }
    49  
    50  func 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  
    87  func 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  
   103  func 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  
   125  func 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  
   131  func 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.
   146  func 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.
   157  func 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.
   171  func 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.
   217  func 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).
   262  func 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 exist.
   301  //
   302  // This should create a file with the name DST and copy the contents of the source
   303  // file into it.
   304  func TestCopyCaseA(t *testing.T) {
   305  	tmpDirA, tmpDirB := getTestTempDirs(t)
   306  	defer removeAllPaths(tmpDirA, tmpDirB)
   307  
   308  	// Load A with some sample files and directories.
   309  	createSampleDir(t, tmpDirA)
   310  
   311  	srcPath := filepath.Join(tmpDirA, "file1")
   312  	dstPath := filepath.Join(tmpDirB, "itWorks.txt")
   313  
   314  	var err error
   315  
   316  	if err = testCopyHelper(t, srcPath, dstPath); err != nil {
   317  		t.Fatalf("unexpected error %T: %s", err, err)
   318  	}
   319  
   320  	err = fileContentsEqual(t, srcPath, dstPath)
   321  	assert.NilError(t, err)
   322  	os.Remove(dstPath)
   323  
   324  	symlinkPath := filepath.Join(tmpDirA, "symlink3")
   325  	symlinkPath1 := filepath.Join(tmpDirA, "symlink4")
   326  	linkTarget := filepath.Join(tmpDirA, "file1")
   327  
   328  	if err = testCopyHelperFSym(t, symlinkPath, dstPath); err != nil {
   329  		t.Fatalf("unexpected error %T: %s", err, err)
   330  	}
   331  
   332  	err = fileContentsEqual(t, linkTarget, dstPath)
   333  	assert.NilError(t, err)
   334  	os.Remove(dstPath)
   335  	if err = testCopyHelperFSym(t, symlinkPath1, dstPath); err != nil {
   336  		t.Fatalf("unexpected error %T: %s", err, err)
   337  	}
   338  
   339  	err = fileContentsEqual(t, linkTarget, dstPath)
   340  	assert.NilError(t, err)
   341  }
   342  
   343  // B. SRC specifies a file and DST (with trailing path separator) doesn't exist.
   344  //
   345  // This should cause an error because the copy operation cannot create a directory
   346  // when copying a single file.
   347  func TestCopyCaseB(t *testing.T) {
   348  	tmpDirA, tmpDirB := getTestTempDirs(t)
   349  	defer removeAllPaths(tmpDirA, tmpDirB)
   350  
   351  	// Load A with some sample files and directories.
   352  	createSampleDir(t, tmpDirA)
   353  
   354  	srcPath := filepath.Join(tmpDirA, "file1")
   355  	dstDir := joinTrailingSep(tmpDirB, "testDir")
   356  
   357  	var err error
   358  
   359  	if err = testCopyHelper(t, srcPath, dstDir); err == nil {
   360  		t.Fatal("expected ErrDirNotExists error, but got nil instead")
   361  	}
   362  
   363  	if err != ErrDirNotExists {
   364  		t.Fatalf("expected ErrDirNotExists error, but got %T: %s", err, err)
   365  	}
   366  
   367  	symlinkPath := filepath.Join(tmpDirA, "symlink3")
   368  
   369  	if err = testCopyHelperFSym(t, symlinkPath, dstDir); err == nil {
   370  		t.Fatal("expected ErrDirNotExists error, but got nil instead")
   371  	}
   372  	if err != ErrDirNotExists {
   373  		t.Fatalf("expected ErrDirNotExists error, but got %T: %s", err, err)
   374  	}
   375  }
   376  
   377  // C. SRC specifies a file and DST exists as a file.
   378  //
   379  // This should overwrite the file at DST with the contents of the source file.
   380  func TestCopyCaseC(t *testing.T) {
   381  	tmpDirA, tmpDirB := getTestTempDirs(t)
   382  	defer removeAllPaths(tmpDirA, tmpDirB)
   383  
   384  	// Load A and B with some sample files and directories.
   385  	createSampleDir(t, tmpDirA)
   386  	createSampleDir(t, tmpDirB)
   387  
   388  	srcPath := filepath.Join(tmpDirA, "file1")
   389  	dstPath := filepath.Join(tmpDirB, "file2")
   390  
   391  	var err error
   392  
   393  	// Ensure they start out different.
   394  	if err = fileContentsEqual(t, srcPath, dstPath); err == nil {
   395  		t.Fatal("expected different file contents")
   396  	}
   397  
   398  	if err = testCopyHelper(t, srcPath, dstPath); err != nil {
   399  		t.Fatalf("unexpected error %T: %s", err, err)
   400  	}
   401  
   402  	err = fileContentsEqual(t, srcPath, dstPath)
   403  	assert.NilError(t, err)
   404  }
   405  
   406  // C. Symbol link following version: SRC specifies a file and DST exists as a file.
   407  //
   408  // This should overwrite the file at DST with the contents of the source file.
   409  func TestCopyCaseCFSym(t *testing.T) {
   410  	tmpDirA, tmpDirB := getTestTempDirs(t)
   411  	defer removeAllPaths(tmpDirA, tmpDirB)
   412  
   413  	// Load A and B with some sample files and directories.
   414  	createSampleDir(t, tmpDirA)
   415  	createSampleDir(t, tmpDirB)
   416  
   417  	symlinkPathBad := filepath.Join(tmpDirA, "symlink1")
   418  	symlinkPath := filepath.Join(tmpDirA, "symlink3")
   419  	linkTarget := filepath.Join(tmpDirA, "file1")
   420  	dstPath := filepath.Join(tmpDirB, "file2")
   421  
   422  	var err error
   423  
   424  	// first to test broken link
   425  	if err = testCopyHelperFSym(t, symlinkPathBad, dstPath); err == nil {
   426  		t.Fatalf("unexpected error %T: %s", err, err)
   427  	}
   428  
   429  	// test symbol link -> symbol link -> target
   430  	// Ensure they start out different.
   431  	if err = fileContentsEqual(t, linkTarget, dstPath); err == nil {
   432  		t.Fatal("expected different file contents")
   433  	}
   434  
   435  	if err = testCopyHelperFSym(t, symlinkPath, dstPath); err != nil {
   436  		t.Fatalf("unexpected error %T: %s", err, err)
   437  	}
   438  
   439  	err = fileContentsEqual(t, linkTarget, dstPath)
   440  	assert.NilError(t, err)
   441  }
   442  
   443  // D. SRC specifies a file and DST exists as a directory.
   444  //
   445  // This should place a copy of the source file inside it using the basename from
   446  // SRC. Ensure this works whether DST has a trailing path separator or not.
   447  func TestCopyCaseD(t *testing.T) {
   448  	tmpDirA, tmpDirB := getTestTempDirs(t)
   449  	defer removeAllPaths(tmpDirA, tmpDirB)
   450  
   451  	// Load A and B with some sample files and directories.
   452  	createSampleDir(t, tmpDirA)
   453  	createSampleDir(t, tmpDirB)
   454  
   455  	srcPath := filepath.Join(tmpDirA, "file1")
   456  	dstDir := filepath.Join(tmpDirB, "dir1")
   457  	dstPath := filepath.Join(dstDir, "file1")
   458  
   459  	var err error
   460  
   461  	// Ensure that dstPath doesn't exist.
   462  	if _, err = os.Stat(dstPath); !os.IsNotExist(err) {
   463  		t.Fatalf("did not expect dstPath %q to exist", dstPath)
   464  	}
   465  
   466  	if err = testCopyHelper(t, srcPath, dstDir); err != nil {
   467  		t.Fatalf("unexpected error %T: %s", err, err)
   468  	}
   469  
   470  	err = fileContentsEqual(t, srcPath, dstPath)
   471  	assert.NilError(t, err)
   472  
   473  	// Now try again but using a trailing path separator for dstDir.
   474  
   475  	if err = os.RemoveAll(dstDir); err != nil {
   476  		t.Fatalf("unable to remove dstDir: %s", err)
   477  	}
   478  
   479  	if err = os.MkdirAll(dstDir, os.FileMode(0755)); err != nil {
   480  		t.Fatalf("unable to make dstDir: %s", err)
   481  	}
   482  
   483  	dstDir = joinTrailingSep(tmpDirB, "dir1")
   484  
   485  	if err = testCopyHelper(t, srcPath, dstDir); err != nil {
   486  		t.Fatalf("unexpected error %T: %s", err, err)
   487  	}
   488  
   489  	err = fileContentsEqual(t, srcPath, dstPath)
   490  	assert.NilError(t, err)
   491  }
   492  
   493  // D. Symbol link following version: SRC specifies a file and DST exists as a directory.
   494  //
   495  // This should place a copy of the source file inside it using the basename from
   496  // SRC. Ensure this works whether DST has a trailing path separator or not.
   497  func TestCopyCaseDFSym(t *testing.T) {
   498  	tmpDirA, tmpDirB := getTestTempDirs(t)
   499  	defer removeAllPaths(tmpDirA, tmpDirB)
   500  
   501  	// Load A and B with some sample files and directories.
   502  	createSampleDir(t, tmpDirA)
   503  	createSampleDir(t, tmpDirB)
   504  
   505  	srcPath := filepath.Join(tmpDirA, "symlink4")
   506  	linkTarget := filepath.Join(tmpDirA, "file1")
   507  	dstDir := filepath.Join(tmpDirB, "dir1")
   508  	dstPath := filepath.Join(dstDir, "symlink4")
   509  
   510  	var err error
   511  
   512  	// Ensure that dstPath doesn't exist.
   513  	if _, err = os.Stat(dstPath); !os.IsNotExist(err) {
   514  		t.Fatalf("did not expect dstPath %q to exist", dstPath)
   515  	}
   516  
   517  	if err = testCopyHelperFSym(t, srcPath, dstDir); err != nil {
   518  		t.Fatalf("unexpected error %T: %s", err, err)
   519  	}
   520  
   521  	err = fileContentsEqual(t, linkTarget, dstPath)
   522  	assert.NilError(t, err)
   523  
   524  	// Now try again but using a trailing path separator for dstDir.
   525  
   526  	if err = os.RemoveAll(dstDir); err != nil {
   527  		t.Fatalf("unable to remove dstDir: %s", err)
   528  	}
   529  
   530  	if err = os.MkdirAll(dstDir, os.FileMode(0755)); err != nil {
   531  		t.Fatalf("unable to make dstDir: %s", err)
   532  	}
   533  
   534  	dstDir = joinTrailingSep(tmpDirB, "dir1")
   535  
   536  	if err = testCopyHelperFSym(t, srcPath, dstDir); err != nil {
   537  		t.Fatalf("unexpected error %T: %s", err, err)
   538  	}
   539  
   540  	err = fileContentsEqual(t, linkTarget, dstPath)
   541  	assert.NilError(t, err)
   542  }
   543  
   544  // E. SRC specifies a directory and DST does not exist.
   545  //
   546  // This should create a directory at DST and copy the contents of the SRC directory
   547  // into the DST directory. Ensure this works whether DST has a trailing path
   548  // separator or not.
   549  func TestCopyCaseE(t *testing.T) {
   550  	tmpDirA, tmpDirB := getTestTempDirs(t)
   551  	defer removeAllPaths(tmpDirA, tmpDirB)
   552  
   553  	// Load A with some sample files and directories.
   554  	createSampleDir(t, tmpDirA)
   555  
   556  	srcDir := filepath.Join(tmpDirA, "dir1")
   557  	dstDir := filepath.Join(tmpDirB, "testDir")
   558  
   559  	var err error
   560  
   561  	if err = testCopyHelper(t, srcDir, dstDir); err != nil {
   562  		t.Fatalf("unexpected error %T: %s", err, err)
   563  	}
   564  
   565  	if err = dirContentsEqual(t, dstDir, srcDir); err != nil {
   566  		t.Log("dir contents not equal")
   567  		logDirContents(t, tmpDirA)
   568  		logDirContents(t, tmpDirB)
   569  		t.Fatal(err)
   570  	}
   571  
   572  	// Now try again but using a trailing path separator for dstDir.
   573  
   574  	if err = os.RemoveAll(dstDir); err != nil {
   575  		t.Fatalf("unable to remove dstDir: %s", err)
   576  	}
   577  
   578  	dstDir = joinTrailingSep(tmpDirB, "testDir")
   579  
   580  	if err = testCopyHelper(t, srcDir, dstDir); err != nil {
   581  		t.Fatalf("unexpected error %T: %s", err, err)
   582  	}
   583  
   584  	err = dirContentsEqual(t, dstDir, srcDir)
   585  	assert.NilError(t, err)
   586  }
   587  
   588  // E. Symbol link following version: SRC specifies a directory and DST does not exist.
   589  //
   590  // This should create a directory at DST and copy the contents of the SRC directory
   591  // into the DST directory. Ensure this works whether DST has a trailing path
   592  // separator or	not.
   593  func TestCopyCaseEFSym(t *testing.T) {
   594  	tmpDirA, tmpDirB := getTestTempDirs(t)
   595  	defer removeAllPaths(tmpDirA, tmpDirB)
   596  
   597  	// Load A with some sample files and directories.
   598  	createSampleDir(t, tmpDirA)
   599  
   600  	srcDir := filepath.Join(tmpDirA, "dirSymlink")
   601  	linkTarget := filepath.Join(tmpDirA, "dir1")
   602  	dstDir := filepath.Join(tmpDirB, "testDir")
   603  
   604  	var err error
   605  
   606  	if err = testCopyHelperFSym(t, srcDir, dstDir); err != nil {
   607  		t.Fatalf("unexpected error %T: %s", err, err)
   608  	}
   609  
   610  	if err = dirContentsEqual(t, dstDir, linkTarget); err != nil {
   611  		t.Log("dir contents not equal")
   612  		logDirContents(t, tmpDirA)
   613  		logDirContents(t, tmpDirB)
   614  		t.Fatal(err)
   615  	}
   616  
   617  	// Now try again but using a trailing path separator for dstDir.
   618  
   619  	if err = os.RemoveAll(dstDir); err != nil {
   620  		t.Fatalf("unable to remove dstDir: %s", err)
   621  	}
   622  
   623  	dstDir = joinTrailingSep(tmpDirB, "testDir")
   624  
   625  	if err = testCopyHelperFSym(t, srcDir, dstDir); err != nil {
   626  		t.Fatalf("unexpected error %T: %s", err, err)
   627  	}
   628  
   629  	err = dirContentsEqual(t, dstDir, linkTarget)
   630  	assert.NilError(t, err)
   631  }
   632  
   633  // F. SRC specifies a directory and DST exists as a file.
   634  //
   635  // This should cause an	error as it is not possible to overwrite a file with a
   636  // directory.
   637  func TestCopyCaseF(t *testing.T) {
   638  	tmpDirA, tmpDirB := getTestTempDirs(t)
   639  	defer removeAllPaths(tmpDirA, tmpDirB)
   640  
   641  	// Load A and B with some sample files and directories.
   642  	createSampleDir(t, tmpDirA)
   643  	createSampleDir(t, tmpDirB)
   644  
   645  	srcDir := filepath.Join(tmpDirA, "dir1")
   646  	symSrcDir := filepath.Join(tmpDirA, "dirSymlink")
   647  	dstFile := filepath.Join(tmpDirB, "file1")
   648  
   649  	var err error
   650  
   651  	if err = testCopyHelper(t, srcDir, dstFile); err == nil {
   652  		t.Fatal("expected ErrCannotCopyDir error, but got nil instead")
   653  	}
   654  
   655  	if err != ErrCannotCopyDir {
   656  		t.Fatalf("expected ErrCannotCopyDir error, but got %T: %s", err, err)
   657  	}
   658  
   659  	// now test with symbol link
   660  	if err = testCopyHelperFSym(t, symSrcDir, dstFile); err == nil {
   661  		t.Fatal("expected ErrCannotCopyDir error, but got nil instead")
   662  	}
   663  
   664  	if err != ErrCannotCopyDir {
   665  		t.Fatalf("expected ErrCannotCopyDir error, but got %T: %s", err, err)
   666  	}
   667  }
   668  
   669  // G. SRC specifies a directory and DST exists as a directory.
   670  //
   671  // This should copy	the SRC directory and all its contents to the DST directory.
   672  // Ensure this works whether DST has a trailing path separator or not.
   673  func TestCopyCaseG(t *testing.T) {
   674  	tmpDirA, tmpDirB := getTestTempDirs(t)
   675  	defer removeAllPaths(tmpDirA, tmpDirB)
   676  
   677  	// Load A and B with some sample files and directories.
   678  	createSampleDir(t, tmpDirA)
   679  	createSampleDir(t, tmpDirB)
   680  
   681  	srcDir := filepath.Join(tmpDirA, "dir1")
   682  	dstDir := filepath.Join(tmpDirB, "dir2")
   683  	resultDir := filepath.Join(dstDir, "dir1")
   684  
   685  	var err error
   686  
   687  	if err = testCopyHelper(t, srcDir, dstDir); err != nil {
   688  		t.Fatalf("unexpected error %T: %s", err, err)
   689  	}
   690  
   691  	err = dirContentsEqual(t, resultDir, srcDir)
   692  	assert.NilError(t, err)
   693  
   694  	// Now try again but using a trailing path separator for dstDir.
   695  
   696  	if err = os.RemoveAll(dstDir); err != nil {
   697  		t.Fatalf("unable to remove dstDir: %s", err)
   698  	}
   699  
   700  	if err = os.MkdirAll(dstDir, os.FileMode(0755)); err != nil {
   701  		t.Fatalf("unable to make dstDir: %s", err)
   702  	}
   703  
   704  	dstDir = joinTrailingSep(tmpDirB, "dir2")
   705  
   706  	if err = testCopyHelper(t, srcDir, dstDir); err != nil {
   707  		t.Fatalf("unexpected error %T: %s", err, err)
   708  	}
   709  
   710  	err = dirContentsEqual(t, resultDir, srcDir)
   711  	assert.NilError(t, err)
   712  }
   713  
   714  // G. Symbol link version: SRC specifies a directory and DST exists as a directory.
   715  //
   716  // This should copy the SRC directory and all its contents to the DST directory.
   717  // Ensure this works whether DST has a trailing path separator or not.
   718  func TestCopyCaseGFSym(t *testing.T) {
   719  	tmpDirA, tmpDirB := getTestTempDirs(t)
   720  	defer removeAllPaths(tmpDirA, tmpDirB)
   721  
   722  	// Load A and B with some sample files and directories.
   723  	createSampleDir(t, tmpDirA)
   724  	createSampleDir(t, tmpDirB)
   725  
   726  	srcDir := filepath.Join(tmpDirA, "dirSymlink")
   727  	linkTarget := filepath.Join(tmpDirA, "dir1")
   728  	dstDir := filepath.Join(tmpDirB, "dir2")
   729  	resultDir := filepath.Join(dstDir, "dirSymlink")
   730  
   731  	var err error
   732  
   733  	if err = testCopyHelperFSym(t, srcDir, dstDir); err != nil {
   734  		t.Fatalf("unexpected error %T: %s", err, err)
   735  	}
   736  
   737  	err = dirContentsEqual(t, resultDir, linkTarget)
   738  	assert.NilError(t, err)
   739  
   740  	// Now try again but using a trailing path separator for dstDir.
   741  
   742  	if err = os.RemoveAll(dstDir); err != nil {
   743  		t.Fatalf("unable to remove dstDir: %s", err)
   744  	}
   745  
   746  	if err = os.MkdirAll(dstDir, os.FileMode(0755)); err != nil {
   747  		t.Fatalf("unable to make dstDir: %s", err)
   748  	}
   749  
   750  	dstDir = joinTrailingSep(tmpDirB, "dir2")
   751  
   752  	if err = testCopyHelperFSym(t, srcDir, dstDir); err != nil {
   753  		t.Fatalf("unexpected error %T: %s", err, err)
   754  	}
   755  
   756  	err = dirContentsEqual(t, resultDir, linkTarget)
   757  	assert.NilError(t, err)
   758  }
   759  
   760  // H. SRC specifies a directory's contents only and DST does not exist.
   761  //
   762  // This	should create a directory at DST and copy the contents of the SRC
   763  // directory (but not the directory itself) into the DST directory. Ensure
   764  // this works whether DST has a trailing path separator or not.
   765  func TestCopyCaseH(t *testing.T) {
   766  	tmpDirA, tmpDirB := getTestTempDirs(t)
   767  	defer removeAllPaths(tmpDirA, tmpDirB)
   768  
   769  	// Load A with some sample files and directories.
   770  	createSampleDir(t, tmpDirA)
   771  
   772  	srcDir := joinTrailingSep(tmpDirA, "dir1") + "."
   773  	dstDir := filepath.Join(tmpDirB, "testDir")
   774  
   775  	var err error
   776  
   777  	if err = testCopyHelper(t, srcDir, dstDir); err != nil {
   778  		t.Fatalf("unexpected error %T: %s", err, err)
   779  	}
   780  
   781  	if err = dirContentsEqual(t, dstDir, srcDir); err != nil {
   782  		t.Log("dir contents not equal")
   783  		logDirContents(t, tmpDirA)
   784  		logDirContents(t, tmpDirB)
   785  		t.Fatal(err)
   786  	}
   787  
   788  	// Now try again but using a trailing path separator for dstDir.
   789  
   790  	if err = os.RemoveAll(dstDir); err != nil {
   791  		t.Fatalf("unable to remove dstDir: %s", err)
   792  	}
   793  
   794  	dstDir = joinTrailingSep(tmpDirB, "testDir")
   795  
   796  	if err = testCopyHelper(t, srcDir, dstDir); err != nil {
   797  		t.Fatalf("unexpected error %T: %s", err, err)
   798  	}
   799  
   800  	if err = dirContentsEqual(t, dstDir, srcDir); err != nil {
   801  		t.Log("dir contents not equal")
   802  		logDirContents(t, tmpDirA)
   803  		logDirContents(t, tmpDirB)
   804  		t.Fatal(err)
   805  	}
   806  }
   807  
   808  // H. Symbol link following version: SRC specifies a directory's contents only and DST does not exist.
   809  //
   810  // This	should create a directory at DST and copy the contents of the SRC
   811  // directory (but not the directory itself) into the DST directory. Ensure
   812  // this works whether DST has a trailing path separator or not.
   813  func TestCopyCaseHFSym(t *testing.T) {
   814  	tmpDirA, tmpDirB := getTestTempDirs(t)
   815  	defer removeAllPaths(tmpDirA, tmpDirB)
   816  
   817  	// Load A with some sample files and directories.
   818  	createSampleDir(t, tmpDirA)
   819  
   820  	srcDir := joinTrailingSep(tmpDirA, "dirSymlink") + "."
   821  	linkTarget := filepath.Join(tmpDirA, "dir1")
   822  	dstDir := filepath.Join(tmpDirB, "testDir")
   823  
   824  	var err error
   825  
   826  	if err = testCopyHelperFSym(t, srcDir, dstDir); err != nil {
   827  		t.Fatalf("unexpected error %T: %s", err, err)
   828  	}
   829  
   830  	if err = dirContentsEqual(t, dstDir, linkTarget); err != nil {
   831  		t.Log("dir contents not equal")
   832  		logDirContents(t, tmpDirA)
   833  		logDirContents(t, tmpDirB)
   834  		t.Fatal(err)
   835  	}
   836  
   837  	// Now try again but using a trailing path separator for dstDir.
   838  
   839  	if err = os.RemoveAll(dstDir); err != nil {
   840  		t.Fatalf("unable to remove dstDir: %s", err)
   841  	}
   842  
   843  	dstDir = joinTrailingSep(tmpDirB, "testDir")
   844  
   845  	if err = testCopyHelperFSym(t, srcDir, dstDir); err != nil {
   846  		t.Fatalf("unexpected error %T: %s", err, err)
   847  	}
   848  
   849  	if err = dirContentsEqual(t, dstDir, linkTarget); err != nil {
   850  		t.Log("dir contents not equal")
   851  		logDirContents(t, tmpDirA)
   852  		logDirContents(t, tmpDirB)
   853  		t.Fatal(err)
   854  	}
   855  }
   856  
   857  // I. SRC specifies a directory's contents only and DST exists as a file.
   858  //
   859  // This	should cause an error as it is not possible to overwrite a file with a
   860  // directory.
   861  func TestCopyCaseI(t *testing.T) {
   862  	tmpDirA, tmpDirB := getTestTempDirs(t)
   863  	defer removeAllPaths(tmpDirA, tmpDirB)
   864  
   865  	// Load A and B with some sample files and directories.
   866  	createSampleDir(t, tmpDirA)
   867  	createSampleDir(t, tmpDirB)
   868  
   869  	srcDir := joinTrailingSep(tmpDirA, "dir1") + "."
   870  	symSrcDir := filepath.Join(tmpDirB, "dirSymlink")
   871  	dstFile := filepath.Join(tmpDirB, "file1")
   872  
   873  	var err error
   874  
   875  	if err = testCopyHelper(t, srcDir, 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  	// now try with symbol link of dir
   884  	if err = testCopyHelperFSym(t, symSrcDir, dstFile); err == nil {
   885  		t.Fatal("expected ErrCannotCopyDir error, but got nil instead")
   886  	}
   887  
   888  	if err != ErrCannotCopyDir {
   889  		t.Fatalf("expected ErrCannotCopyDir error, but got %T: %s", err, err)
   890  	}
   891  }
   892  
   893  // J. SRC specifies a directory's contents only and DST exists as a directory.
   894  //
   895  // This should copy the contents of the SRC directory (but not the directory
   896  // itself) into the DST directory. Ensure this works whether DST has a
   897  // trailing path separator or not.
   898  func TestCopyCaseJ(t *testing.T) {
   899  	tmpDirA, tmpDirB := getTestTempDirs(t)
   900  	defer removeAllPaths(tmpDirA, tmpDirB)
   901  
   902  	// Load A and B with some sample files and directories.
   903  	createSampleDir(t, tmpDirA)
   904  	createSampleDir(t, tmpDirB)
   905  
   906  	srcDir := joinTrailingSep(tmpDirA, "dir1") + "."
   907  	dstDir := filepath.Join(tmpDirB, "dir5")
   908  
   909  	var err error
   910  
   911  	// first to create an empty dir
   912  	if err = os.MkdirAll(dstDir, os.FileMode(0755)); err != nil {
   913  		t.Fatalf("unable to make dstDir: %s", err)
   914  	}
   915  
   916  	if err = testCopyHelper(t, srcDir, dstDir); err != nil {
   917  		t.Fatalf("unexpected error %T: %s", err, err)
   918  	}
   919  
   920  	err = dirContentsEqual(t, dstDir, srcDir)
   921  	assert.NilError(t, err)
   922  
   923  	// Now try again but using a trailing path separator for dstDir.
   924  
   925  	if err = os.RemoveAll(dstDir); err != nil {
   926  		t.Fatalf("unable to remove dstDir: %s", err)
   927  	}
   928  
   929  	if err = os.MkdirAll(dstDir, os.FileMode(0755)); err != nil {
   930  		t.Fatalf("unable to make dstDir: %s", err)
   931  	}
   932  
   933  	dstDir = joinTrailingSep(tmpDirB, "dir5")
   934  
   935  	if err = testCopyHelper(t, srcDir, dstDir); err != nil {
   936  		t.Fatalf("unexpected error %T: %s", err, err)
   937  	}
   938  
   939  	err = dirContentsEqual(t, dstDir, srcDir)
   940  	assert.NilError(t, err)
   941  }
   942  
   943  // J. Symbol link following version: SRC specifies a directory's contents only and DST exists as a directory.
   944  //
   945  // This should copy the contents of the SRC directory (but not the directory
   946  // itself) into the DST directory. Ensure this works whether DST has a
   947  // trailing path separator or not.
   948  func TestCopyCaseJFSym(t *testing.T) {
   949  	tmpDirA, tmpDirB := getTestTempDirs(t)
   950  	defer removeAllPaths(tmpDirA, tmpDirB)
   951  
   952  	// Load A and B with some sample files and directories.
   953  	createSampleDir(t, tmpDirA)
   954  	createSampleDir(t, tmpDirB)
   955  
   956  	srcDir := joinTrailingSep(tmpDirA, "dirSymlink") + "."
   957  	linkTarget := filepath.Join(tmpDirA, "dir1")
   958  	dstDir := filepath.Join(tmpDirB, "dir5")
   959  
   960  	var err error
   961  
   962  	// first to create an empty dir
   963  	if err = os.MkdirAll(dstDir, os.FileMode(0755)); err != nil {
   964  		t.Fatalf("unable to make dstDir: %s", err)
   965  	}
   966  
   967  	if err = testCopyHelperFSym(t, srcDir, dstDir); err != nil {
   968  		t.Fatalf("unexpected error %T: %s", err, err)
   969  	}
   970  
   971  	err = dirContentsEqual(t, dstDir, linkTarget)
   972  	assert.NilError(t, err)
   973  
   974  	// Now try again but using a trailing path separator for dstDir.
   975  
   976  	if err = os.RemoveAll(dstDir); err != nil {
   977  		t.Fatalf("unable to remove dstDir: %s", err)
   978  	}
   979  
   980  	if err = os.MkdirAll(dstDir, os.FileMode(0755)); err != nil {
   981  		t.Fatalf("unable to make dstDir: %s", err)
   982  	}
   983  
   984  	dstDir = joinTrailingSep(tmpDirB, "dir5")
   985  
   986  	if err = testCopyHelperFSym(t, srcDir, dstDir); err != nil {
   987  		t.Fatalf("unexpected error %T: %s", err, err)
   988  	}
   989  
   990  	err = dirContentsEqual(t, dstDir, linkTarget)
   991  	assert.NilError(t, err)
   992  }