From 7cc5f1987adaa25228d00b38483cf87bbf9cc78b Mon Sep 17 00:00:00 2001 From: Valentin Rothberg Date: Tue, 7 Jun 2022 12:54:07 +0200 Subject: [PATCH] copier: add `NoOverwriteNonDirDir` option Similar to the `NoOverwriteDirNonDir` one, add an option that disables non-directories from being overwritten by directories. Required-for: containers/podman/issues/14420 Signed-off-by: Valentin Rothberg --- copier/copier.go | 10 +++++++--- copier/copier_test.go | 30 +++++++++++++++++++++++++++++- 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/copier/copier.go b/copier/copier.go index 8b6e1bf4628..23bf0fb4501 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 } @@ -1794,12 +1795,15 @@ func copierHandlerPut(bulkReader io.Reader, req request, idMappings *idtools.IDM } case tar.TypeDir: 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 st, stErr := os.Lstat(path); stErr == nil && !st.IsDir() { + if req.PutOptions.NoOverwriteNonDirDir { + break + } if err = os.Remove(path); err == nil { err = os.Mkdir(path, 0700) } + } else { + err = stErr } // either we removed it and retried, or it was a directory, // in which case we want to just add the new stuff under it diff --git a/copier/copier_test.go b/copier/copier_test.go index e3da633be8f..1647598fdeb 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,33 @@ 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}, + {Name: "test/content", Typeflag: tar.TypeReg, 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) {