github.com/lacework-dev/go-moby@v20.10.12+incompatible/pkg/archive/copy_unix_test.go (about)

     1  // +build !windows
     2  
     3  // TODO Windows: Some of these tests may be salvageable and portable to Windows.
     4  
     5  package archive // import "github.com/docker/docker/pkg/archive"
     6  
     7  import (
     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/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 = 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  
    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
   301  //    exist. This should create a file with the name DST and copy the
   302  //    contents of the source file into it.
   303  func 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.
   345  func 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.
   378  func 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.
   407  func 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.
   444  func 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.
   494  func 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.
   545  func 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.
   589  func 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.
   631  func 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.
   666  func 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.
   711  func 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.
   757  func 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.
   805  func 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.
   852  func 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.
   888  func 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.
   938  func 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  }