From ac8552f21eab124d5f61ab68d24a673909efc703 Mon Sep 17 00:00:00 2001 From: Anthony Nandaa Date: Tue, 10 Sep 2024 00:55:17 -0700 Subject: [PATCH] fix: util/path: CheckSystemDriveAndRemoveDriveLetter to preserve `/` The call to CheckSystemDriveAndRemoveDriveLetter() does not preserve the trailing `/` or `\\`. This happens because `filepath.Clean()` strips away any trailing slashes. For example `/sample/` will be `\\sample` on Windows. This function was mainly written for Windows scenarios, which have System Drive Letters like C:/, etc. This was causing cases like `COPY testfile /testdir/` to be intepreted as `COPY testfile /testdir`, and if `testdir` is not explictly created before the call, it ends up being treated as a destination file other than a directory. Fix this by checking that if we have a trailing `/` or `\\`, we preserve it after the call to `filepath.Clean()`. Fixes #5249 Signed-off-by: Anthony Nandaa --- frontend/dockerfile/dockerfile_test.go | 37 ++++++++++++++++++++++++++ util/system/path.go | 18 +++++++++++-- 2 files changed, 53 insertions(+), 2 deletions(-) diff --git a/frontend/dockerfile/dockerfile_test.go b/frontend/dockerfile/dockerfile_test.go index e12c0dd88286a..5274b7a877038 100644 --- a/frontend/dockerfile/dockerfile_test.go +++ b/frontend/dockerfile/dockerfile_test.go @@ -150,6 +150,7 @@ var allTests = integration.TestFuncs( testNamedMultiplatformInputContext, testNamedFilteredContext, testEmptyDestDir, + testPreserveDestDirSlash, testCopyLinkDotDestDir, testCopyLinkEmptyDestDir, testCopyChownCreateDest, @@ -545,6 +546,42 @@ RUN cmd /V:on /C "set /p tfcontent= Fail func CheckSystemDriveAndRemoveDriveLetter(path string, inputOS string) (string, error) { + if inputOS == "" { inputOS = "linux" } @@ -193,9 +194,11 @@ func CheckSystemDriveAndRemoveDriveLetter(path string, inputOS string) (string, } parts := strings.SplitN(path, ":", 2) + + // we need to preserve the `/` or `\\` at the end because #5249 // Path does not have a drive letter. Just return it. if len(parts) < 2 { - return ToSlash(filepath.Clean(path), inputOS), nil + return ToSlash(filepath.Clean(path), inputOS) + pathHasTrailingSlash(parts[0]), nil } // We expect all paths to be in C: @@ -220,5 +223,16 @@ func CheckSystemDriveAndRemoveDriveLetter(path string, inputOS string) (string, // // We must return the second element of the split path, as is, without attempting to convert // it to an absolute path. We have no knowledge of the CWD; that is treated elsewhere. - return ToSlash(filepath.Clean(parts[1]), inputOS), nil + return ToSlash(filepath.Clean(parts[1]), inputOS) + pathHasTrailingSlash(parts[1]), nil +} + +// Checks if a path other than / or \\ has a trailing slash +// at the end, and returns it to be retained after running +// through the ToSlash(filepath.Clean()) functions which strips them off. +// eg. "/sample/" -> "/sample". +func pathHasTrailingSlash(path string) string { + if len(path) > 1 && (strings.HasSuffix(path, "/") || strings.HasSuffix(path, "\\")) { + return "/" + } + return "" }