github.com/moby/docker@v26.1.3+incompatible/pkg/archive/copy_unix_test.go (about)

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