Skip to content

Commit 94e3b6b

Browse files
committed
copier: retain symlink target w/ follow-link
* add unit test [NO NEW TESTS NEEDED] Signed-off-by: danishprakash <[email protected]>
1 parent 0cbe852 commit 94e3b6b

File tree

2 files changed

+103
-1
lines changed

2 files changed

+103
-1
lines changed

copier/copier.go

+11-1
Original file line numberDiff line numberDiff line change
@@ -1307,7 +1307,17 @@ func copierHandlerGet(bulkWriter io.Writer, req request, pm *fileutils.PatternMa
13071307
// cases where this was a symlink that we
13081308
// dereferenced, be sure to use the name of the
13091309
// link.
1310-
if err := copierHandlerGetOne(info, "", filepath.Base(queue[i]), item, req.GetOptions, tw, hardlinkChecker, idMappings); err != nil {
1310+
1311+
// If following link, pass symlink target for
1312+
// the link to be generated on the destination
1313+
var symlinkTarget string
1314+
if req.GetOptions.NoDerefSymlinks && info.Mode()&os.ModeType == os.ModeSymlink {
1315+
symlinkTarget, err = os.Readlink(item)
1316+
if err != nil {
1317+
return err
1318+
}
1319+
}
1320+
if err := copierHandlerGetOne(info, symlinkTarget, filepath.Base(queue[i]), item, req.GetOptions, tw, hardlinkChecker, idMappings); err != nil {
13111321
if req.GetOptions.IgnoreUnreadable && errorIsPermission(err) {
13121322
continue
13131323
}

copier/copier_test.go

+92
Original file line numberDiff line numberDiff line change
@@ -1871,3 +1871,95 @@ func testRemove(t *testing.T) {
18711871
})
18721872
}
18731873
}
1874+
1875+
func TestGetNoDerefSymlink(t *testing.T) {
1876+
couldChroot := canChroot
1877+
canChroot = false
1878+
testGetNoDerefSymlink(t)
1879+
canChroot = couldChroot
1880+
}
1881+
1882+
func testGetNoDerefSymlink(t *testing.T) {
1883+
var testArchives = []struct {
1884+
name string
1885+
rootOnly bool
1886+
headers []tar.Header
1887+
contents map[string][]byte
1888+
}{
1889+
{
1890+
name: "regular",
1891+
rootOnly: false,
1892+
headers: []tar.Header{
1893+
{Name: "link-b", Typeflag: tar.TypeSymlink, Linkname: "../file-doesnt-exist", Size: 23, Mode: 0777, ModTime: testDate},
1894+
},
1895+
contents: map[string][]byte{
1896+
"archive-a": testArchiveSlice,
1897+
},
1898+
},
1899+
}
1900+
1901+
topdir := "."
1902+
for _, testArchive := range testArchives {
1903+
if uid != 0 && testArchive.rootOnly {
1904+
t.Logf("test archive %q can only be tested with root privileges, skipping", testArchive.name)
1905+
continue
1906+
}
1907+
1908+
dir, err := makeContextFromArchive(t, makeArchive(testArchive.headers, testArchive.contents), topdir)
1909+
require.NoErrorf(t, err, "error creating context from archive %q", testArchive.name)
1910+
1911+
root := dir
1912+
1913+
for _, noDerefSymlinks := range []bool{true, false} {
1914+
for _, testItem := range testArchive.headers {
1915+
name := filepath.FromSlash(testItem.Name)
1916+
name = filepath.Join(root, topdir, name)
1917+
if !t.Failed() && testItem.Typeflag == tar.TypeSymlink {
1918+
t.Run(fmt.Sprintf("testSymlink"), func(t *testing.T) {
1919+
var getErr error
1920+
var wg sync.WaitGroup
1921+
getOptions := GetOptions{}
1922+
getOptions.NoDerefSymlinks = noDerefSymlinks
1923+
pipeReader, pipeWriter := io.Pipe()
1924+
wg.Add(1)
1925+
go func() {
1926+
getErr = Get(root, topdir, getOptions, []string{name}, pipeWriter)
1927+
pipeWriter.Close()
1928+
wg.Done()
1929+
}()
1930+
tr := tar.NewReader(pipeReader)
1931+
hdr, err := tr.Next()
1932+
actualContents := []string{}
1933+
for err == nil {
1934+
actualContents = append(actualContents, filepath.FromSlash(hdr.Linkname))
1935+
hdr, err = tr.Next()
1936+
}
1937+
sort.Strings(actualContents)
1938+
wg.Wait()
1939+
1940+
assert.Equal(t, io.EOF.Error(), err.Error(), "expected EOF at end of archive, got %q", err.Error())
1941+
1942+
// We stat the target(name) and then assert the err
1943+
// If we're following links (noDerefSymlinks: false)
1944+
// we expect an error because copier would create
1945+
// a link with a target which is non-existent (../file-b).
1946+
// https://github.com/containers/podman/issues/16585
1947+
//
1948+
// But if we're not following links, copier would
1949+
// create the link by following the target and stat
1950+
// should therefore return success.
1951+
_, err = os.Stat(name)
1952+
if !noDerefSymlinks {
1953+
fmt.Println(">>>>>>>> NAME: ", name)
1954+
assert.ErrorContains(t, getErr, fmt.Sprintf("copier: get: lstat %q", name))
1955+
} else {
1956+
assert.NoErrorf(t, getErr, "unexpected error from Get(%q): %v", name, getErr)
1957+
}
1958+
1959+
pipeReader.Close()
1960+
})
1961+
}
1962+
}
1963+
}
1964+
}
1965+
}

0 commit comments

Comments
 (0)