diff --git a/copier/copier.go b/copier/copier.go index 8b6e1bf4628..7abad5ad289 100644 --- a/copier/copier.go +++ b/copier/copier.go @@ -344,6 +344,7 @@ type PutOptions struct { IgnoreXattrErrors bool // ignore any errors encountered when attempting to set extended attributes IgnoreDevices bool // ignore items which are character or block devices NoOverwriteDirNonDir bool // instead of quietly overwriting directories with non-directories, return an error + NoOverwriteNonDirDir bool // instead of quietly overwriting non-directories with directories, return an error Rename map[string]string // rename items with the specified names, or under the specified names } @@ -1796,7 +1797,9 @@ func copierHandlerPut(bulkReader io.Reader, req request, idMappings *idtools.IDM if err = os.Mkdir(path, 0700); err != nil && os.IsExist(err) { var st os.FileInfo if st, err = os.Lstat(path); err == nil && !st.IsDir() { - // it's not a directory, so remove it and mkdir + if req.PutOptions.NoOverwriteNonDirDir { + return errors.Wrapf(err, "copier: put: error creating directory at %q", path) + } if err = os.Remove(path); err == nil { err = os.Mkdir(path, 0700) } diff --git a/copier/copier_test.go b/copier/copier_test.go index e3da633be8f..6f814d48371 100644 --- a/copier/copier_test.go +++ b/copier/copier_test.go @@ -536,9 +536,10 @@ func testPut(t *testing.T) { } } + // Overwrite directory for _, overwrite := range []bool{false, true} { for _, typeFlag := range []byte{tar.TypeReg, tar.TypeLink, tar.TypeSymlink, tar.TypeChar, tar.TypeBlock, tar.TypeFifo} { - t.Run(fmt.Sprintf("overwrite=%v,type=%c", overwrite, typeFlag), func(t *testing.T) { + t.Run(fmt.Sprintf("overwrite (dir)=%v,type=%c", overwrite, typeFlag), func(t *testing.T) { archive := makeArchiveSlice([]tar.Header{ {Name: "target", Typeflag: tar.TypeSymlink, Mode: 0755, Linkname: "target", ModTime: testDate}, {Name: "target", Typeflag: tar.TypeDir, Mode: 0755, ModTime: testDate}, @@ -563,6 +564,32 @@ func testPut(t *testing.T) { } } + // Overwrite non-directory + for _, overwrite := range []bool{false, true} { + for _, typeFlag := range []byte{tar.TypeReg, tar.TypeLink, tar.TypeSymlink, tar.TypeChar, tar.TypeBlock, tar.TypeFifo} { + t.Run(fmt.Sprintf("overwrite (non-dir)=%v,type=%c", overwrite, typeFlag), func(t *testing.T) { + archive := makeArchiveSlice([]tar.Header{ + {Name: "target", Typeflag: tar.TypeSymlink, Mode: 0755, Linkname: "target", ModTime: testDate}, + {Name: "target", Typeflag: tar.TypeReg, Mode: 0755, ModTime: testDate}, + {Name: "target", Typeflag: tar.TypeSymlink, Mode: 0755, Linkname: "target", ModTime: testDate}, + {Name: "target", Typeflag: tar.TypeReg, Size: 123, Mode: 0755, ModTime: testDate}, + {Name: "test", Typeflag: typeFlag, Size: 0, Mode: 0755, Linkname: "target", ModTime: testDate}, + {Name: "test", Typeflag: tar.TypeDir, Size: 0, Mode: 0755, ModTime: testDate}, + }) + tmp, err := ioutil.TempDir("", "copier-test-") + require.NoErrorf(t, err, "error creating temporary directory") + defer os.RemoveAll(tmp) + err = Put(tmp, tmp, PutOptions{UIDMap: uidMap, GIDMap: gidMap, NoOverwriteNonDirDir: !overwrite}, bytes.NewReader(archive)) + if overwrite { + if unwrapError(err) != syscall.EPERM { + assert.Nilf(t, err, "expected to overwrite file with type %c: %v", typeFlag, err) + } + } else { + assert.Errorf(t, err, "expected an error trying to overwrite file of type %c", typeFlag) + } + }) + } + } for _, ignoreDevices := range []bool{false, true} { for _, typeFlag := range []byte{tar.TypeChar, tar.TypeBlock} { t.Run(fmt.Sprintf("ignoreDevices=%v,type=%c", ignoreDevices, typeFlag), func(t *testing.T) {