github.com/Heebron/moby@v0.0.0-20221111184709-6eab4f55faf7/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  	t.Logf("logging directory contents: %q", dirPath)
   105  	err := filepath.WalkDir(dirPath, func(path string, info os.DirEntry, err error) error {
   106  		if err != nil {
   107  			t.Errorf("stat error for path %q: %s", path, err)
   108  			return nil
   109  		}
   110  
   111  		if info.IsDir() {
   112  			path = joinTrailingSep(path)
   113  		}
   114  
   115  		t.Logf("\t%s", path)
   116  
   117  		return nil
   118  	})
   119  	assert.NilError(t, err)
   120  }
   121  
   122  func testCopyHelper(t *testing.T, srcPath, dstPath string) (err error) {
   123  	t.Logf("copying from %q to %q (not follow symbol link)", srcPath, dstPath)
   124  
   125  	return CopyResource(srcPath, dstPath, false)
   126  }
   127  
   128  func testCopyHelperFSym(t *testing.T, srcPath, dstPath string) (err error) {
   129  	t.Logf("copying from %q to %q (follow symbol link)", srcPath, dstPath)
   130  
   131  	return CopyResource(srcPath, dstPath, true)
   132  }
   133  
   134  // Basic assumptions about SRC and DST:
   135  // 1. SRC must exist.
   136  // 2. If SRC ends with a trailing separator, it must be a directory.
   137  // 3. DST parent directory must exist.
   138  // 4. If DST exists as a file, it must not end with a trailing separator.
   139  
   140  // First get these easy error cases out of the way.
   141  
   142  // Test for error when SRC does not exist.
   143  func TestCopyErrSrcNotExists(t *testing.T) {
   144  	tmpDirA, tmpDirB := getTestTempDirs(t)
   145  	defer removeAllPaths(tmpDirA, tmpDirB)
   146  
   147  	if _, err := CopyInfoSourcePath(filepath.Join(tmpDirA, "file1"), false); !os.IsNotExist(err) {
   148  		t.Fatalf("expected IsNotExist error, but got %T: %s", err, err)
   149  	}
   150  }
   151  
   152  // Test for error when SRC ends in a trailing
   153  // path separator but it exists as a file.
   154  func TestCopyErrSrcNotDir(t *testing.T) {
   155  	tmpDirA, tmpDirB := getTestTempDirs(t)
   156  	defer removeAllPaths(tmpDirA, tmpDirB)
   157  
   158  	// Load A with some sample files and directories.
   159  	createSampleDir(t, tmpDirA)
   160  
   161  	if _, err := CopyInfoSourcePath(joinTrailingSep(tmpDirA, "file1"), false); !isNotDir(err) {
   162  		t.Fatalf("expected IsNotDir error, but got %T: %s", err, err)
   163  	}
   164  }
   165  
   166  // Test for error when SRC is a valid file or directory,
   167  // but the DST parent directory does not exist.
   168  func TestCopyErrDstParentNotExists(t *testing.T) {
   169  	tmpDirA, tmpDirB := getTestTempDirs(t)
   170  	defer removeAllPaths(tmpDirA, tmpDirB)
   171  
   172  	// Load A with some sample files and directories.
   173  	createSampleDir(t, tmpDirA)
   174  
   175  	srcInfo := CopyInfo{Path: filepath.Join(tmpDirA, "file1"), Exists: true, IsDir: false}
   176  
   177  	// Try with a file source.
   178  	content, err := TarResource(srcInfo)
   179  	if err != nil {
   180  		t.Fatalf("unexpected error %T: %s", err, err)
   181  	}
   182  	defer content.Close()
   183  
   184  	// Copy to a file whose parent does not exist.
   185  	if err = CopyTo(content, srcInfo, filepath.Join(tmpDirB, "fakeParentDir", "file1")); err == nil {
   186  		t.Fatal("expected IsNotExist error, but got nil instead")
   187  	}
   188  
   189  	if !os.IsNotExist(err) {
   190  		t.Fatalf("expected IsNotExist error, but got %T: %s", err, err)
   191  	}
   192  
   193  	// Try with a directory source.
   194  	srcInfo = CopyInfo{Path: filepath.Join(tmpDirA, "dir1"), Exists: true, IsDir: true}
   195  
   196  	content, err = TarResource(srcInfo)
   197  	if err != nil {
   198  		t.Fatalf("unexpected error %T: %s", err, err)
   199  	}
   200  	defer content.Close()
   201  
   202  	// Copy to a directory whose parent does not exist.
   203  	if err = CopyTo(content, srcInfo, joinTrailingSep(tmpDirB, "fakeParentDir", "fakeDstDir")); err == nil {
   204  		t.Fatal("expected IsNotExist error, but got nil instead")
   205  	}
   206  
   207  	if !os.IsNotExist(err) {
   208  		t.Fatalf("expected IsNotExist error, but got %T: %s", err, err)
   209  	}
   210  }
   211  
   212  // Test for error when DST ends in a trailing
   213  // path separator but exists as a file.
   214  func TestCopyErrDstNotDir(t *testing.T) {
   215  	tmpDirA, tmpDirB := getTestTempDirs(t)
   216  	defer removeAllPaths(tmpDirA, tmpDirB)
   217  
   218  	// Load A and B with some sample files and directories.
   219  	createSampleDir(t, tmpDirA)
   220  	createSampleDir(t, tmpDirB)
   221  
   222  	// Try with a file source.
   223  	srcInfo := CopyInfo{Path: filepath.Join(tmpDirA, "file1"), Exists: true, IsDir: false}
   224  
   225  	content, err := TarResource(srcInfo)
   226  	if err != nil {
   227  		t.Fatalf("unexpected error %T: %s", err, err)
   228  	}
   229  	defer content.Close()
   230  
   231  	if err = CopyTo(content, srcInfo, joinTrailingSep(tmpDirB, "file1")); err == nil {
   232  		t.Fatal("expected IsNotDir error, but got nil instead")
   233  	}
   234  
   235  	if !isNotDir(err) {
   236  		t.Fatalf("expected IsNotDir error, but got %T: %s", err, err)
   237  	}
   238  
   239  	// Try with a directory source.
   240  	srcInfo = CopyInfo{Path: filepath.Join(tmpDirA, "dir1"), Exists: true, IsDir: true}
   241  
   242  	content, err = TarResource(srcInfo)
   243  	if err != nil {
   244  		t.Fatalf("unexpected error %T: %s", err, err)
   245  	}
   246  	defer content.Close()
   247  
   248  	if err = CopyTo(content, srcInfo, joinTrailingSep(tmpDirB, "file1")); err == nil {
   249  		t.Fatal("expected IsNotDir error, but got nil instead")
   250  	}
   251  
   252  	if !isNotDir(err) {
   253  		t.Fatalf("expected IsNotDir error, but got %T: %s", err, err)
   254  	}
   255  }
   256  
   257  // Test to check if CopyTo works with a long (>100 characters) destination file name.
   258  // This is a regression (see https://github.com/docker/for-linux/issues/484).
   259  func TestCopyLongDstFilename(t *testing.T) {
   260  	const longName = "a_very_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_long_filename_that_is_101_characters"
   261  	tmpDirA, tmpDirB := getTestTempDirs(t)
   262  	defer removeAllPaths(tmpDirA, tmpDirB)
   263  
   264  	// Load A with some sample files and directories.
   265  	createSampleDir(t, tmpDirA)
   266  
   267  	srcInfo := CopyInfo{Path: filepath.Join(tmpDirA, "file1"), Exists: true, IsDir: false}
   268  
   269  	content, err := TarResource(srcInfo)
   270  	if err != nil {
   271  		t.Fatalf("unexpected error %T: %s", err, err)
   272  	}
   273  	defer content.Close()
   274  
   275  	err = CopyTo(content, srcInfo, filepath.Join(tmpDirB, longName))
   276  	if err != nil {
   277  		t.Fatalf("unexpected error %T: %s", err, err)
   278  	}
   279  }
   280  
   281  // Possibilities are reduced to the remaining 10 cases:
   282  //
   283  //  case | srcIsDir | onlyDirContents | dstExists | dstIsDir | dstTrSep | action
   284  // ===================================================================================================
   285  //   A   |  no      |  -              |  no       |  -       |  no      |  create file
   286  //   B   |  no      |  -              |  no       |  -       |  yes     |  error
   287  //   C   |  no      |  -              |  yes      |  no      |  -       |  overwrite file
   288  //   D   |  no      |  -              |  yes      |  yes     |  -       |  create file in dst dir
   289  //   E   |  yes     |  no             |  no       |  -       |  -       |  create dir, copy contents
   290  //   F   |  yes     |  no             |  yes      |  no      |  -       |  error
   291  //   G   |  yes     |  no             |  yes      |  yes     |  -       |  copy dir and contents
   292  //   H   |  yes     |  yes            |  no       |  -       |  -       |  create dir, copy contents
   293  //   I   |  yes     |  yes            |  yes      |  no      |  -       |  error
   294  //   J   |  yes     |  yes            |  yes      |  yes     |  -       |  copy dir contents
   295  //
   296  
   297  // A. SRC specifies a file and DST (no trailing path separator) doesn't exist.
   298  //
   299  // This should create a file with the name DST and copy the contents of the source
   300  // file into it.
   301  func TestCopyCaseA(t *testing.T) {
   302  	tmpDirA, tmpDirB := getTestTempDirs(t)
   303  	defer removeAllPaths(tmpDirA, tmpDirB)
   304  
   305  	// Load A with some sample files and directories.
   306  	createSampleDir(t, tmpDirA)
   307  
   308  	srcPath := filepath.Join(tmpDirA, "file1")
   309  	dstPath := filepath.Join(tmpDirB, "itWorks.txt")
   310  
   311  	var err error
   312  
   313  	if err = testCopyHelper(t, srcPath, dstPath); err != nil {
   314  		t.Fatalf("unexpected error %T: %s", err, err)
   315  	}
   316  
   317  	err = fileContentsEqual(t, srcPath, dstPath)
   318  	assert.NilError(t, err)
   319  	os.Remove(dstPath)
   320  
   321  	symlinkPath := filepath.Join(tmpDirA, "symlink3")
   322  	symlinkPath1 := filepath.Join(tmpDirA, "symlink4")
   323  	linkTarget := filepath.Join(tmpDirA, "file1")
   324  
   325  	if err = testCopyHelperFSym(t, symlinkPath, dstPath); err != nil {
   326  		t.Fatalf("unexpected error %T: %s", err, err)
   327  	}
   328  
   329  	err = fileContentsEqual(t, linkTarget, dstPath)
   330  	assert.NilError(t, err)
   331  	os.Remove(dstPath)
   332  	if err = testCopyHelperFSym(t, symlinkPath1, dstPath); err != nil {
   333  		t.Fatalf("unexpected error %T: %s", err, err)
   334  	}
   335  
   336  	err = fileContentsEqual(t, linkTarget, dstPath)
   337  	assert.NilError(t, err)
   338  }
   339  
   340  // B. SRC specifies a file and DST (with trailing path separator) doesn't exist.
   341  //
   342  // This should cause an error because the copy operation cannot create a directory
   343  // when copying a single file.
   344  func TestCopyCaseB(t *testing.T) {
   345  	tmpDirA, tmpDirB := getTestTempDirs(t)
   346  	defer removeAllPaths(tmpDirA, tmpDirB)
   347  
   348  	// Load A with some sample files and directories.
   349  	createSampleDir(t, tmpDirA)
   350  
   351  	srcPath := filepath.Join(tmpDirA, "file1")
   352  	dstDir := joinTrailingSep(tmpDirB, "testDir")
   353  
   354  	var err error
   355  
   356  	if err = testCopyHelper(t, srcPath, dstDir); err == nil {
   357  		t.Fatal("expected ErrDirNotExists error, but got nil instead")
   358  	}
   359  
   360  	if err != ErrDirNotExists {
   361  		t.Fatalf("expected ErrDirNotExists error, but got %T: %s", err, err)
   362  	}
   363  
   364  	symlinkPath := filepath.Join(tmpDirA, "symlink3")
   365  
   366  	if err = testCopyHelperFSym(t, symlinkPath, dstDir); err == nil {
   367  		t.Fatal("expected ErrDirNotExists error, but got nil instead")
   368  	}
   369  	if err != ErrDirNotExists {
   370  		t.Fatalf("expected ErrDirNotExists error, but got %T: %s", err, err)
   371  	}
   372  }
   373  
   374  // C. SRC specifies a file and DST exists as a file.
   375  //
   376  // This should overwrite the file at DST with the contents of the source file.
   377  func TestCopyCaseC(t *testing.T) {
   378  	tmpDirA, tmpDirB := getTestTempDirs(t)
   379  	defer removeAllPaths(tmpDirA, tmpDirB)
   380  
   381  	// Load A and B with some sample files and directories.
   382  	createSampleDir(t, tmpDirA)
   383  	createSampleDir(t, tmpDirB)
   384  
   385  	srcPath := filepath.Join(tmpDirA, "file1")
   386  	dstPath := filepath.Join(tmpDirB, "file2")
   387  
   388  	var err error
   389  
   390  	// Ensure they start out different.
   391  	if err = fileContentsEqual(t, srcPath, dstPath); err == nil {
   392  		t.Fatal("expected different file contents")
   393  	}
   394  
   395  	if err = testCopyHelper(t, srcPath, dstPath); err != nil {
   396  		t.Fatalf("unexpected error %T: %s", err, err)
   397  	}
   398  
   399  	err = fileContentsEqual(t, srcPath, dstPath)
   400  	assert.NilError(t, err)
   401  }
   402  
   403  // C. Symbol link following version: SRC specifies a file and DST exists as a file.
   404  //
   405  // This should overwrite the file at DST with the contents of the source file.
   406  func TestCopyCaseCFSym(t *testing.T) {
   407  	tmpDirA, tmpDirB := getTestTempDirs(t)
   408  	defer removeAllPaths(tmpDirA, tmpDirB)
   409  
   410  	// Load A and B with some sample files and directories.
   411  	createSampleDir(t, tmpDirA)
   412  	createSampleDir(t, tmpDirB)
   413  
   414  	symlinkPathBad := filepath.Join(tmpDirA, "symlink1")
   415  	symlinkPath := filepath.Join(tmpDirA, "symlink3")
   416  	linkTarget := filepath.Join(tmpDirA, "file1")
   417  	dstPath := filepath.Join(tmpDirB, "file2")
   418  
   419  	var err error
   420  
   421  	// first to test broken link
   422  	if err = testCopyHelperFSym(t, symlinkPathBad, dstPath); err == nil {
   423  		t.Fatalf("unexpected error %T: %s", err, err)
   424  	}
   425  
   426  	// test symbol link -> symbol link -> target
   427  	// Ensure they start out different.
   428  	if err = fileContentsEqual(t, linkTarget, dstPath); err == nil {
   429  		t.Fatal("expected different file contents")
   430  	}
   431  
   432  	if err = testCopyHelperFSym(t, symlinkPath, dstPath); err != nil {
   433  		t.Fatalf("unexpected error %T: %s", err, err)
   434  	}
   435  
   436  	err = fileContentsEqual(t, linkTarget, dstPath)
   437  	assert.NilError(t, err)
   438  }
   439  
   440  // D. SRC specifies a file and DST exists as a directory.
   441  //
   442  // This should place a copy of the source file inside it using the basename from
   443  // SRC. Ensure 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: SRC specifies a file and DST exists as a directory.
   491  //
   492  // This should place a copy of the source file inside it using the basename from
   493  // SRC. Ensure 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.
   542  //
   543  // This should create a directory at DST and copy the contents of the SRC directory
   544  // into the DST directory. Ensure this works whether DST has a trailing path
   545  // separator or not.
   546  func TestCopyCaseE(t *testing.T) {
   547  	tmpDirA, tmpDirB := getTestTempDirs(t)
   548  	defer removeAllPaths(tmpDirA, tmpDirB)
   549  
   550  	// Load A with some sample files and directories.
   551  	createSampleDir(t, tmpDirA)
   552  
   553  	srcDir := filepath.Join(tmpDirA, "dir1")
   554  	dstDir := filepath.Join(tmpDirB, "testDir")
   555  
   556  	var err error
   557  
   558  	if err = testCopyHelper(t, srcDir, dstDir); err != nil {
   559  		t.Fatalf("unexpected error %T: %s", err, err)
   560  	}
   561  
   562  	if err = dirContentsEqual(t, dstDir, srcDir); err != nil {
   563  		t.Log("dir contents not equal")
   564  		logDirContents(t, tmpDirA)
   565  		logDirContents(t, tmpDirB)
   566  		t.Fatal(err)
   567  	}
   568  
   569  	// Now try again but using a trailing path separator for dstDir.
   570  
   571  	if err = os.RemoveAll(dstDir); err != nil {
   572  		t.Fatalf("unable to remove dstDir: %s", err)
   573  	}
   574  
   575  	dstDir = joinTrailingSep(tmpDirB, "testDir")
   576  
   577  	if err = testCopyHelper(t, srcDir, dstDir); err != nil {
   578  		t.Fatalf("unexpected error %T: %s", err, err)
   579  	}
   580  
   581  	err = dirContentsEqual(t, dstDir, srcDir)
   582  	assert.NilError(t, err)
   583  }
   584  
   585  // E. Symbol link following version: SRC specifies a directory and DST does not exist.
   586  //
   587  // This should create a directory at DST and copy the contents of the SRC directory
   588  // into the DST directory. Ensure this works whether DST has a trailing path
   589  // separator or	not.
   590  func TestCopyCaseEFSym(t *testing.T) {
   591  	tmpDirA, tmpDirB := getTestTempDirs(t)
   592  	defer removeAllPaths(tmpDirA, tmpDirB)
   593  
   594  	// Load A with some sample files and directories.
   595  	createSampleDir(t, tmpDirA)
   596  
   597  	srcDir := filepath.Join(tmpDirA, "dirSymlink")
   598  	linkTarget := filepath.Join(tmpDirA, "dir1")
   599  	dstDir := filepath.Join(tmpDirB, "testDir")
   600  
   601  	var err error
   602  
   603  	if err = testCopyHelperFSym(t, srcDir, dstDir); err != nil {
   604  		t.Fatalf("unexpected error %T: %s", err, err)
   605  	}
   606  
   607  	if err = dirContentsEqual(t, dstDir, linkTarget); err != nil {
   608  		t.Log("dir contents not equal")
   609  		logDirContents(t, tmpDirA)
   610  		logDirContents(t, tmpDirB)
   611  		t.Fatal(err)
   612  	}
   613  
   614  	// Now try again but using a trailing path separator for dstDir.
   615  
   616  	if err = os.RemoveAll(dstDir); err != nil {
   617  		t.Fatalf("unable to remove dstDir: %s", err)
   618  	}
   619  
   620  	dstDir = joinTrailingSep(tmpDirB, "testDir")
   621  
   622  	if err = testCopyHelperFSym(t, srcDir, dstDir); err != nil {
   623  		t.Fatalf("unexpected error %T: %s", err, err)
   624  	}
   625  
   626  	err = dirContentsEqual(t, dstDir, linkTarget)
   627  	assert.NilError(t, err)
   628  }
   629  
   630  // F. SRC specifies a directory and DST exists as a file.
   631  //
   632  // This should cause an	error as it is not possible to overwrite a file with a
   633  // directory.
   634  func TestCopyCaseF(t *testing.T) {
   635  	tmpDirA, tmpDirB := getTestTempDirs(t)
   636  	defer removeAllPaths(tmpDirA, tmpDirB)
   637  
   638  	// Load A and B with some sample files and directories.
   639  	createSampleDir(t, tmpDirA)
   640  	createSampleDir(t, tmpDirB)
   641  
   642  	srcDir := filepath.Join(tmpDirA, "dir1")
   643  	symSrcDir := filepath.Join(tmpDirA, "dirSymlink")
   644  	dstFile := filepath.Join(tmpDirB, "file1")
   645  
   646  	var err error
   647  
   648  	if err = testCopyHelper(t, srcDir, dstFile); err == nil {
   649  		t.Fatal("expected ErrCannotCopyDir error, but got nil instead")
   650  	}
   651  
   652  	if err != ErrCannotCopyDir {
   653  		t.Fatalf("expected ErrCannotCopyDir error, but got %T: %s", err, err)
   654  	}
   655  
   656  	// now test with symbol link
   657  	if err = testCopyHelperFSym(t, symSrcDir, dstFile); err == nil {
   658  		t.Fatal("expected ErrCannotCopyDir error, but got nil instead")
   659  	}
   660  
   661  	if err != ErrCannotCopyDir {
   662  		t.Fatalf("expected ErrCannotCopyDir error, but got %T: %s", err, err)
   663  	}
   664  }
   665  
   666  // G. SRC specifies a directory and DST exists as a directory.
   667  //
   668  // This should copy	the SRC directory and all its contents to the DST directory.
   669  // Ensure this works whether DST has a trailing path separator or not.
   670  func TestCopyCaseG(t *testing.T) {
   671  	tmpDirA, tmpDirB := getTestTempDirs(t)
   672  	defer removeAllPaths(tmpDirA, tmpDirB)
   673  
   674  	// Load A and B with some sample files and directories.
   675  	createSampleDir(t, tmpDirA)
   676  	createSampleDir(t, tmpDirB)
   677  
   678  	srcDir := filepath.Join(tmpDirA, "dir1")
   679  	dstDir := filepath.Join(tmpDirB, "dir2")
   680  	resultDir := filepath.Join(dstDir, "dir1")
   681  
   682  	var err error
   683  
   684  	if err = testCopyHelper(t, srcDir, dstDir); err != nil {
   685  		t.Fatalf("unexpected error %T: %s", err, err)
   686  	}
   687  
   688  	err = dirContentsEqual(t, resultDir, srcDir)
   689  	assert.NilError(t, err)
   690  
   691  	// Now try again but using a trailing path separator for dstDir.
   692  
   693  	if err = os.RemoveAll(dstDir); err != nil {
   694  		t.Fatalf("unable to remove dstDir: %s", err)
   695  	}
   696  
   697  	if err = os.MkdirAll(dstDir, os.FileMode(0755)); err != nil {
   698  		t.Fatalf("unable to make dstDir: %s", err)
   699  	}
   700  
   701  	dstDir = joinTrailingSep(tmpDirB, "dir2")
   702  
   703  	if err = testCopyHelper(t, srcDir, dstDir); err != nil {
   704  		t.Fatalf("unexpected error %T: %s", err, err)
   705  	}
   706  
   707  	err = dirContentsEqual(t, resultDir, srcDir)
   708  	assert.NilError(t, err)
   709  }
   710  
   711  // G. Symbol link version: SRC specifies a directory and DST exists as a directory.
   712  //
   713  // This should copy the SRC directory and all its contents to the DST directory.
   714  // Ensure this works whether DST has a trailing path separator or not.
   715  func TestCopyCaseGFSym(t *testing.T) {
   716  	tmpDirA, tmpDirB := getTestTempDirs(t)
   717  	defer removeAllPaths(tmpDirA, tmpDirB)
   718  
   719  	// Load A and B with some sample files and directories.
   720  	createSampleDir(t, tmpDirA)
   721  	createSampleDir(t, tmpDirB)
   722  
   723  	srcDir := filepath.Join(tmpDirA, "dirSymlink")
   724  	linkTarget := filepath.Join(tmpDirA, "dir1")
   725  	dstDir := filepath.Join(tmpDirB, "dir2")
   726  	resultDir := filepath.Join(dstDir, "dirSymlink")
   727  
   728  	var err error
   729  
   730  	if err = testCopyHelperFSym(t, srcDir, dstDir); err != nil {
   731  		t.Fatalf("unexpected error %T: %s", err, err)
   732  	}
   733  
   734  	err = dirContentsEqual(t, resultDir, linkTarget)
   735  	assert.NilError(t, err)
   736  
   737  	// Now try again but using a trailing path separator for dstDir.
   738  
   739  	if err = os.RemoveAll(dstDir); err != nil {
   740  		t.Fatalf("unable to remove dstDir: %s", err)
   741  	}
   742  
   743  	if err = os.MkdirAll(dstDir, os.FileMode(0755)); err != nil {
   744  		t.Fatalf("unable to make dstDir: %s", err)
   745  	}
   746  
   747  	dstDir = joinTrailingSep(tmpDirB, "dir2")
   748  
   749  	if err = testCopyHelperFSym(t, srcDir, dstDir); err != nil {
   750  		t.Fatalf("unexpected error %T: %s", err, err)
   751  	}
   752  
   753  	err = dirContentsEqual(t, resultDir, linkTarget)
   754  	assert.NilError(t, err)
   755  }
   756  
   757  // H. SRC specifies a directory's contents only and DST does not exist.
   758  //
   759  // This	should create a directory at DST and copy the contents of the SRC
   760  // directory (but not the directory itself) into the DST directory. Ensure
   761  // this works whether DST has a trailing path separator or not.
   762  func TestCopyCaseH(t *testing.T) {
   763  	tmpDirA, tmpDirB := getTestTempDirs(t)
   764  	defer removeAllPaths(tmpDirA, tmpDirB)
   765  
   766  	// Load A with some sample files and directories.
   767  	createSampleDir(t, tmpDirA)
   768  
   769  	srcDir := joinTrailingSep(tmpDirA, "dir1") + "."
   770  	dstDir := filepath.Join(tmpDirB, "testDir")
   771  
   772  	var err error
   773  
   774  	if err = testCopyHelper(t, srcDir, dstDir); err != nil {
   775  		t.Fatalf("unexpected error %T: %s", err, err)
   776  	}
   777  
   778  	if err = dirContentsEqual(t, dstDir, srcDir); err != nil {
   779  		t.Log("dir contents not equal")
   780  		logDirContents(t, tmpDirA)
   781  		logDirContents(t, tmpDirB)
   782  		t.Fatal(err)
   783  	}
   784  
   785  	// Now try again but using a trailing path separator for dstDir.
   786  
   787  	if err = os.RemoveAll(dstDir); err != nil {
   788  		t.Fatalf("unable to remove dstDir: %s", err)
   789  	}
   790  
   791  	dstDir = joinTrailingSep(tmpDirB, "testDir")
   792  
   793  	if err = testCopyHelper(t, srcDir, dstDir); err != nil {
   794  		t.Fatalf("unexpected error %T: %s", err, err)
   795  	}
   796  
   797  	if err = dirContentsEqual(t, dstDir, srcDir); err != nil {
   798  		t.Log("dir contents not equal")
   799  		logDirContents(t, tmpDirA)
   800  		logDirContents(t, tmpDirB)
   801  		t.Fatal(err)
   802  	}
   803  }
   804  
   805  // H. Symbol link following version: SRC specifies a directory's contents only and DST does not exist.
   806  //
   807  // This	should create a directory at DST and copy the contents of the SRC
   808  // directory (but not the directory itself) into the DST directory. Ensure
   809  // this works whether DST has a trailing path separator or not.
   810  func TestCopyCaseHFSym(t *testing.T) {
   811  	tmpDirA, tmpDirB := getTestTempDirs(t)
   812  	defer removeAllPaths(tmpDirA, tmpDirB)
   813  
   814  	// Load A with some sample files and directories.
   815  	createSampleDir(t, tmpDirA)
   816  
   817  	srcDir := joinTrailingSep(tmpDirA, "dirSymlink") + "."
   818  	linkTarget := filepath.Join(tmpDirA, "dir1")
   819  	dstDir := filepath.Join(tmpDirB, "testDir")
   820  
   821  	var err error
   822  
   823  	if err = testCopyHelperFSym(t, srcDir, dstDir); err != nil {
   824  		t.Fatalf("unexpected error %T: %s", err, err)
   825  	}
   826  
   827  	if err = dirContentsEqual(t, dstDir, linkTarget); err != nil {
   828  		t.Log("dir contents not equal")
   829  		logDirContents(t, tmpDirA)
   830  		logDirContents(t, tmpDirB)
   831  		t.Fatal(err)
   832  	}
   833  
   834  	// Now try again but using a trailing path separator for dstDir.
   835  
   836  	if err = os.RemoveAll(dstDir); err != nil {
   837  		t.Fatalf("unable to remove dstDir: %s", err)
   838  	}
   839  
   840  	dstDir = joinTrailingSep(tmpDirB, "testDir")
   841  
   842  	if err = testCopyHelperFSym(t, srcDir, dstDir); err != nil {
   843  		t.Fatalf("unexpected error %T: %s", err, err)
   844  	}
   845  
   846  	if err = dirContentsEqual(t, dstDir, linkTarget); err != nil {
   847  		t.Log("dir contents not equal")
   848  		logDirContents(t, tmpDirA)
   849  		logDirContents(t, tmpDirB)
   850  		t.Fatal(err)
   851  	}
   852  }
   853  
   854  // I. SRC specifies a directory's contents only and DST exists as a file.
   855  //
   856  // This	should cause an error as it is not possible to overwrite a file with a
   857  // directory.
   858  func TestCopyCaseI(t *testing.T) {
   859  	tmpDirA, tmpDirB := getTestTempDirs(t)
   860  	defer removeAllPaths(tmpDirA, tmpDirB)
   861  
   862  	// Load A and B with some sample files and directories.
   863  	createSampleDir(t, tmpDirA)
   864  	createSampleDir(t, tmpDirB)
   865  
   866  	srcDir := joinTrailingSep(tmpDirA, "dir1") + "."
   867  	symSrcDir := filepath.Join(tmpDirB, "dirSymlink")
   868  	dstFile := filepath.Join(tmpDirB, "file1")
   869  
   870  	var err error
   871  
   872  	if err = testCopyHelper(t, srcDir, dstFile); err == nil {
   873  		t.Fatal("expected ErrCannotCopyDir error, but got nil instead")
   874  	}
   875  
   876  	if err != ErrCannotCopyDir {
   877  		t.Fatalf("expected ErrCannotCopyDir error, but got %T: %s", err, err)
   878  	}
   879  
   880  	// now try with symbol link of dir
   881  	if err = testCopyHelperFSym(t, symSrcDir, dstFile); err == nil {
   882  		t.Fatal("expected ErrCannotCopyDir error, but got nil instead")
   883  	}
   884  
   885  	if err != ErrCannotCopyDir {
   886  		t.Fatalf("expected ErrCannotCopyDir error, but got %T: %s", err, err)
   887  	}
   888  }
   889  
   890  // J. SRC specifies a directory's contents only and DST exists as a directory.
   891  //
   892  // This should copy the contents of the SRC directory (but not the directory
   893  // itself) into the DST directory. Ensure this works whether DST has a
   894  // trailing path separator or not.
   895  func TestCopyCaseJ(t *testing.T) {
   896  	tmpDirA, tmpDirB := getTestTempDirs(t)
   897  	defer removeAllPaths(tmpDirA, tmpDirB)
   898  
   899  	// Load A and B with some sample files and directories.
   900  	createSampleDir(t, tmpDirA)
   901  	createSampleDir(t, tmpDirB)
   902  
   903  	srcDir := joinTrailingSep(tmpDirA, "dir1") + "."
   904  	dstDir := filepath.Join(tmpDirB, "dir5")
   905  
   906  	var err error
   907  
   908  	// first to create an empty dir
   909  	if err = os.MkdirAll(dstDir, os.FileMode(0755)); err != nil {
   910  		t.Fatalf("unable to make dstDir: %s", err)
   911  	}
   912  
   913  	if err = testCopyHelper(t, srcDir, dstDir); err != nil {
   914  		t.Fatalf("unexpected error %T: %s", err, err)
   915  	}
   916  
   917  	err = dirContentsEqual(t, dstDir, srcDir)
   918  	assert.NilError(t, err)
   919  
   920  	// Now try again but using a trailing path separator for dstDir.
   921  
   922  	if err = os.RemoveAll(dstDir); err != nil {
   923  		t.Fatalf("unable to remove dstDir: %s", err)
   924  	}
   925  
   926  	if err = os.MkdirAll(dstDir, os.FileMode(0755)); err != nil {
   927  		t.Fatalf("unable to make dstDir: %s", err)
   928  	}
   929  
   930  	dstDir = joinTrailingSep(tmpDirB, "dir5")
   931  
   932  	if err = testCopyHelper(t, srcDir, dstDir); err != nil {
   933  		t.Fatalf("unexpected error %T: %s", err, err)
   934  	}
   935  
   936  	err = dirContentsEqual(t, dstDir, srcDir)
   937  	assert.NilError(t, err)
   938  }
   939  
   940  // J. Symbol link following version: SRC specifies a directory's contents only and DST exists as a directory.
   941  //
   942  // This should copy the contents of the SRC directory (but not the directory
   943  // itself) into the DST directory. Ensure this works whether DST has a
   944  // trailing path separator or not.
   945  func TestCopyCaseJFSym(t *testing.T) {
   946  	tmpDirA, tmpDirB := getTestTempDirs(t)
   947  	defer removeAllPaths(tmpDirA, tmpDirB)
   948  
   949  	// Load A and B with some sample files and directories.
   950  	createSampleDir(t, tmpDirA)
   951  	createSampleDir(t, tmpDirB)
   952  
   953  	srcDir := joinTrailingSep(tmpDirA, "dirSymlink") + "."
   954  	linkTarget := filepath.Join(tmpDirA, "dir1")
   955  	dstDir := filepath.Join(tmpDirB, "dir5")
   956  
   957  	var err error
   958  
   959  	// first to create an empty dir
   960  	if err = os.MkdirAll(dstDir, os.FileMode(0755)); err != nil {
   961  		t.Fatalf("unable to make dstDir: %s", err)
   962  	}
   963  
   964  	if err = testCopyHelperFSym(t, srcDir, dstDir); err != nil {
   965  		t.Fatalf("unexpected error %T: %s", err, err)
   966  	}
   967  
   968  	err = dirContentsEqual(t, dstDir, linkTarget)
   969  	assert.NilError(t, err)
   970  
   971  	// Now try again but using a trailing path separator for dstDir.
   972  
   973  	if err = os.RemoveAll(dstDir); err != nil {
   974  		t.Fatalf("unable to remove dstDir: %s", err)
   975  	}
   976  
   977  	if err = os.MkdirAll(dstDir, os.FileMode(0755)); err != nil {
   978  		t.Fatalf("unable to make dstDir: %s", err)
   979  	}
   980  
   981  	dstDir = joinTrailingSep(tmpDirB, "dir5")
   982  
   983  	if err = testCopyHelperFSym(t, srcDir, dstDir); err != nil {
   984  		t.Fatalf("unexpected error %T: %s", err, err)
   985  	}
   986  
   987  	err = dirContentsEqual(t, dstDir, linkTarget)
   988  	assert.NilError(t, err)
   989  }