-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: util/path: CheckSystemDriveAndRemoveDriveLetter
to preserve /
#5317
base: master
Are you sure you want to change the base?
Conversation
/
CheckSystemDriveAndRemoveDriveLetter
to preserve /
Looking into the CI failures... |
Found the regression caused by this, thankfully coz of the checked in integration tests! Dockerfile: FROM mcr.microsoft.com/windows/nanoserver:ltsc2022
COPY test1.txt /sample/
RUN type \sample\test1.txt
COPY test1.txt /
COPY test1.txt /test2.txt
RUN type test1.txt
RUN type test2.txt Build log:
Fixing. |
ac8552f
to
af77ee9
Compare
af77ee9
to
be65686
Compare
util/system/path.go
Outdated
// 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) + keepTrailingSlash(parts[0], inputOS), nil |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
filepath.Clean
is still current system dependent here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let me rethink the whole of this part, to a more simplified approach.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LMK what you think about this new update. I've made sure that the trailing slash is only kept in the form that it was in the original path \\
or /
; and since filepath.Clean()
is system dependent, it's only appended if it has not been cleaned up in the first place.
On Linux vs. Windows for instance:
p := "\\sample\\"
// on linux
fmt.Printf("%v\n", p == filepath.Clean(p)) // true
// on windows
fmt.Printf("%v\n", p == filepath.Clean(p)) // false
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@tonistiigi -- what do you think about handling this for WCOW scenarios only for now? So I made platform specific implementations of keepTrailingSlash
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this issue not appearing in linux? Isn't the issue in https://github.com/moby/buildkit/blob/v0.16.0/util/system/path.go#L75-L76 that is currently linux-only and doesn't handle inputOS
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the catch, yes, the issue also silently appears in Linux cross-builds. Silently coz there's no RUN
to assertain the failure.
Repro dockerfile without RUN
:
FROM mcr.microsoft.com/windows/nanoserver:ltsc2022
COPY test1.txt /sample/
#RUN type \sample\test1.txt
Cross-build:
$ docker buildx build --platform windows/amd64 `
--builder buildkitd-dev --no-cache --tag=windows-test . `
--progress plain `
--output type=local,dest=./output
#6 [2/2] COPY test1.txt /sample/
#6 DONE 0.1s
#7 exporting to client directory
#7 copying files 31B
#7 copying files 230.88MB 0.8s done
#7 DONE 0.8s
Checking results, sample
is a file instead of a directory:
$ ls -l output/sample
-rw-r--r-- 1 root root 6 Sep 24 08:57 output/sample
Fixing for Linux too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And as for this:
Isn't the issue in https://github.com/moby/buildkit/blob/v0.16.0/util/system/path.go#L75-L76 that is currently linux-only and doesn't handle inputOS .
by the time we get to calling NormalizePath
, origPath
will already be getting a stripped out /
, so there's nothing to keep.
> github.com/moby/buildkit/util/system.NormalizePath() /home/nandaa/dev/oss/buildkit/util/system/path.go:40 (PC: 0x1cfeb6d)
35: func NormalizePath(parent, newPath, inputOS string, keepSlash bool) (string, error) {
36: if inputOS == "" {
37: inputOS = "linux"
38: }
39:
=> 40: newPath = ToSlash(newPath, inputOS)
41: parent = ToSlash(parent, inputOS)
42: origPath := newPath
43:
44: if parent == "" {
45: parent = "/"
(dlv) p newPath
"/sample"
It is stripped out at CheckSystemDriveAndRemoveDriveLetter
a few steps before, L1787:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So, NormalizePath
expected that there will be a trailing slash, hence the keepSlash
bool, but it ends up being stripped away before we call into it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now fixed for both WCOW and cross-building from Linux, PTAL.
36e398e
to
1a5289c
Compare
1a5289c
to
c55dbc3
Compare
c55dbc3
to
288b5db
Compare
288b5db
to
c91001c
Compare
aca0ccb
to
e8ae74c
Compare
54b0fbd
to
4056da0
Compare
4056da0
to
b4437dc
Compare
CI failure seems unrelated?
|
util/system/path.go
Outdated
// and keeps it. See https://github.com/moby/buildkit/issues/5249 | ||
// Expects cleanedPath to have gone through filepath.Clean() | ||
// which returns the path with platform specific separators. | ||
func keepTrailingSlash(cleanedPath, origPath, inputOS string) string { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Would it make sense to create a CleanPath()
function in this package that incorporates keepTrailingSlash()
? We could then replace calls to filepath.Clean()
with CleanPath()
Perhaps something like: https://go.dev/play/p/lcvDPMnT8xS ? What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like that, looks neat, let me do that. Thanks!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps the only modification I'll do is to make our CleanPath
take an additional keepSlash
bool param, to be consistent with NormalizePath
; as much as rn, it's always true
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So, I've given it a little of some thought, I think we can minimize the surface area to just focus on our adaptation of filepath.Clean
to retain the trailing slash. If you don't need to retain the trailing slash, then you just call filepath.Clean
. I think this will improve composability and avoid any future surprises.
I have also dropped the inputOS
param since the initial intention for it was for the ToSlash
function. So we clean and pass it to ToSlash
as before.
As for exporting it, I'm not entirely sure. Can wait until the time it will be needed outside?
PTAL.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
here's the side-by-side - https://go.dev/play/p/UbsFcgnqvA3
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here are the two calls I'm investigating:
$ grep -rn "\.CheckSystemDriveAndRemoveDriveLetter("
solver/llbsolver/file/backend.go:381: s, err := system.CheckSystemDriveAndRemoveDriveLetter(s, runtime.GOOS)
frontend/dockerfile/dockerfile2llb/convert.go:1517: f, err := system.CheckSystemDriveAndRemoveDriveLetter(src.Path, d.platform.OS)
I have run both, hard-coding (swapping linux
with windows
) and the runs are successful for both permutations.
Results:
#6 [2/2] COPY test1.txt /sample/
#6 DONE 0.1s
#7 exporting to client directory
#7 copying files 19.71MB 0.1s
#7 copying files 230.88MB 0.9s done
#7 DONE 0.9s
$ ls output/sample
test1.txt
So, it's all good.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For completeness, also did the reverse on Windows side (swapping windows
with linux
) and it's successful:
PS> go test -v --run=TestIntegration/TestPreserveDestDirSlash/worker=containerd
<snip>
=== NAME TestIntegration
run.go:431: Skipped on windows
--- SKIP: TestIntegration (34.52s)
--- PASS: TestIntegration/TestPreserveDestDirSlash/worker=containerd/frontend=builtin (19.11s)
--- PASS: TestIntegration/TestPreserveDestDirSlash/worker=containerd/frontend=client (15.34s)
PASS
ok github.com/moby/buildkit/frontend/dockerfile 34.704s
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
buildkit can build Windows images on Linux. It uses the inputOS
to know the target OS. So when buildkitd runs on Linux and is adding a file inside a Windows image, this code will break if the path is a Windows style path of the form pth4 := "\\a\\b\\..\\c\\"
Please see the last examples from here: https://go.dev/play/p/ZnAPJ2__nis
This is the reason I added the call to cleaned := ToSlash(origPath, inputOS)
in my previous example.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was testing the cross-build case from Linux. Here's using the example path:
FROM mcr.microsoft.com/windows/nanoserver:ltsc2022
COPY test1.txt \\a\\b\\..\\c\\
Build command:
$ docker buildx build --platform windows/amd64 `
--builder buildkitd-dev --no-cache --tag=windows-test . `
--progress plain `
--output type=local,dest=./output
Build results:
#6 [2/2] COPY test1.txt \a\b\..\c\
#6 DONE 0.1s
#7 exporting to client directory
#7 copying files 21.05MB 0.1s
#7 copying files 230.88MB 0.9s done
#7 DONE 0.9s
$ ls output/
a License.txt ProgramData Users Windows
$ ls output/a/c
test1.txt
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
previous convo here - #5317 (comment)
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 and `/sample` on Linux. 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 moby#5249 PS. Also fixed for cross-building from Linux scenario, taking care for paths like `\\sample\\` that are not changed when run through `filepath.Clean()`. Signed-off-by: Anthony Nandaa <profnandaa@gmail.com>
b4437dc
to
31e2d43
Compare
PTAL again, addressed the comments. @tonistiigi @gabriel-samfira |
The call to CheckSystemDriveAndRemoveDriveLetter() does not preserve the trailing
/
or\\
. This happens becausefilepath.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 asCOPY testfile /testdir
, and iftestdir
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 tofilepath.Clean()
.Fixes #5249
PS.
Also fixed for cross-building Windows from Linux, that would fail silently:
Repro dockerfile without
RUN
:Build log:
Checking results,
sample
is a file instead of a directory:NOTE: also covered cases like, where platform-specific
filepath.Clean()
won't strip out the\\
on Linux: