-
Notifications
You must be signed in to change notification settings - Fork 2.5k
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
Do not glob value of symlink on cp #3834
Conversation
cmd/podman/cp.go
Outdated
fi, err := os.Lstat(srcPath) | ||
switch errors.Cause(err).(type) { | ||
case *os.PathError: | ||
glob = append(glob, srcPath) |
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.
Maybe my headache today is getting the better of me, but why do you append the glob if lstat returns a PathError (which I think is it's main error?)? I'd thought you'd only continue on for nil, and hit default for the others?
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.
@TomSweeneyRedHat I wanted the code after the switch to throw the expected errors etc. Trying to keep the patch as small as possible.
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.
OK, I'm a bit more confused. What I thought should happen is line 218 and 219 should disappear. I thought if we couldn't resolve the file or symlink, it should die. That would make the patch even smaller.... Back to dig up some more tylenol.
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 would remove the glob functionality. The functionality does not seem to be mentioned in the Podman documentation and it seems the functionality is not present in Docker (please correct me if I'm wrong).
If the functionality needs to be kept, to avoid surprises podman should only do globbing on exactly the path that the user provided on the command line. As soon as symlinks have been followed the path could look quite different. I think at this point in the code the variable srcPath
could have some other value than what the user provided on the command line.
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.
@eriksjolund I made the mistake of assuming '*' was expected as it was in the code. :)
For the container vibrant_brattain:
$ sudo docker cp vibrant_brattain:/etc/* /tmp
Error: No such container:path: vibrant_brattain:/etc/*
So this is a feature we've introduced. I'll back it out and test.
@mheon Sound good?
The * character isn't strictly a glob - it's a wildcard to indicate the
entire directory should be copied, AFAIK. It is only allowed to exist after
a / and at the very end of the path. I think we can detect and special case
that and avoid glob entirely.
…On Fri, Aug 16, 2019, 14:50 Erik Sjölund ***@***.***> wrote:
***@***.**** commented on this pull request.
------------------------------
In cmd/podman/cp.go
<#3834 (comment)>:
> @@ -210,10 +210,26 @@ func copyBetweenHostAndContainer(runtime *libpod.Runtime, src string, dest strin
srcPath = cleanedPath
}
}
- glob, err = filepath.Glob(srcPath)
- if err != nil {
- return errors.Wrapf(err, "invalid glob %q", srcPath)
+
+ // Don't glob srcPath if it's the value of a symlink
+ // Fix for https://bugzilla.redhat.com/show_bug.cgi?id=1741709
+ fi, err := os.Lstat(srcPath)
+ switch errors.Cause(err).(type) {
+ case *os.PathError:
+ glob = append(glob, srcPath)
I would remove the glob functionality. The functionality does not seem to
be mentioned in the Podman documentation and it seems the functionality is
not present in Docker (please correct me if I'm wrong).
If the functionality needs to be kept, to avoid surprises podman should
only do globbing on exactly the path that the user provided on the command
line. As soon as symlinks have been followed the path could look quite
different. I think at this point in the code the variable srcPath could
have some other value than what the user provided on the command line.
—
You are receiving this because your review was requested.
Reply to this email directly, view it on GitHub
<#3834>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AB3AOCCUQUUPWJD4T6F2U4DQE3ZH5ANCNFSM4IMLMFIA>
.
|
@mheon I'm working on another version of this patch that walks the directory if given path/* Latest code matches docker cli behavior |
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 can’t see how this fixes the primary cause of the original problem:
- Lines 200/206 have resolved the symlink in
srcPath
already, and we are now checking whether the target of the symlink ends with/*
. - The
copy
function called on each of the entries infiles
will continue to resolve symlinks (in the host context).
So I would expect (without testing it, to be honest) that this does not change the behavior of the reproducers at all.
Still, getting rid of the Glob
should make the actual fix much easier to implement, so this could be nice in that sense.
…
Where’s the corresponding code/documentation? I can’t find any code handling |
… now I have finally read all comments and all of that is already known. I’m sorry. |
The glob issue looks fixed but the
|
Hey @jwhonce feel free to use this for testing. The second test ("will not recognize symlink pointing into host space" is currently failing, consistent with my comment above. Once that's fixed, it might still fail because I don't know exactly what error message to expect. All other tests pass right now and should continue passing. Feel free to git-add this and include it in your PR. |
@ed so you would expect the above command to create |
I would expect this to fail because /etc/machine-id does not exist on alpine. What happens instead is that the host's /etc/machine-id gets copied into the current directory. A perhaps-clearer example:
|
Yes I agree that is bad. |
FWIW it's something in the glob expansion because directly specifying the file will (correctly) fail:
...and, while I'm at it, so does specifying a completely-nonexistent file, but it fails with the same lstat error, which I find disconcerting:
|
@edsantiago That should be fine - expected, I'd say. We unconditionally use a scoped symlink resolution function to find the target file in the container, even if no symlinks are present in the path. It's easier to maintain things that way. |
@mheon thanks; it's just that, from an end-user perspective, that's a pretty difficult message to grok. |
@edsantiago I can add the other "path" to the error message. I.e., the path from the user's POV vs. the containers. Just giving the input path makes debugging much harder because then the path in the error message is not what really failed on the file system. |
What about refusing to copy symlinks, or in just copying the symlink, not the content. |
I still think this is solvable with just a little figuring-out of glob expansion. I think that for the most part podman is doing the right thing, it's just that something is wonky with expanding '*'. I don't know how to fix it, but suspect that there's just too much indirection going on: I think
Hope this makes sense. |
At present cp does not use the container mount ns. That needs to be fixed.
…On Mon, Aug 19, 2019, 13:58 Ed Santiago ***@***.***> wrote:
I still think this is solvable with just a little figuring-out of glob
expansion. I think that for the most part podman is doing the right thing,
it's just that something is wonky with expanding '*'. I don't know how to
fix it, but suspect that there's just too much indirection going on: I
think * is somehow triggering nested processing before podman switches
into container mount space. My reasoning:
$ echo "This file exists only on the host" >/tmp/hostfile
$ podman run --name foo alpine sh -c 'ln -s /tmp/hostfile /tmp/link-in-container'
$ podman cp foo:/sdfsdf .
Error: error evaluating symlinks "": lstat /home/esm/.local/share/containers/storage/overlay/90222cdf3b4b5b05b493d3e2a6f9ae986c3214d831b0937c270c657b1ff8049b/merged/sdfsdf: no such file or directory
! ^^^ GOOD! This shows that podman-cp, when given an explicit path, is correctly
! checking in container-mountspace and realizing the symlink is invalid
$ podman cp foo:/tmp/\* .
! ^^^ this one not only succeeds, it creates the file >>hostfile<< (not link-in-container)
! ...implying that something has expanded the glob (* -> link-in-container)
! **and then run readlink() on that result** (yielding "hostfile"). I believe this
! is the crux of the incorrect behavior.
Hope this makes sense.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#3834>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AB3AOCCWMOKHL7H5FS3YQ7TQFLNMBANCNFSM4IMLMFIA>
.
|
I think there is two situations here. 1 is if the container is running. then cp running in the same mount namespace as the container would be good. |
There's some more serious weirdness when cp'ing into the container: link resolution is taking a wrong turn somewhere, creating files and directories that IMHO it shouldn't.
In the above, podman resolved the '*' symlink and created the file 'nonesuch'. This is weird; I was expecting podman to overwrite the '*' symlink and make it an actual file.
This one is weirder: podman cp read the '* I believe that the first case should transform '*' from a symlink into a file, and the second should fail with ENOTDIR. |
As I understand it Podman aims to be a drop-in replacement for Docker. Another thing: The handling of
That would mean somedir/- is the same as - |
@jwhonce What is the state of this one? Is it ready for merge? |
Still #3834 (review), and Ed’s testing confirms that. |
cmd/podman/cp.go
Outdated
if err != nil { | ||
return err | ||
func copy(src, destPath, dest string, idMappingOpts storage.IDMappingOptions, chownOpts *idtools.IDPair, extract, isFromHostToCtr bool) (err error) { | ||
// Don't symlink process /dev/stdin |
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.
This comment says “what”, but not “why”. I have no idea “why”; why does this matter, and/or hurt?
cmd/podman/cp.go
Outdated
func copy(src, destPath, dest string, idMappingOpts storage.IDMappingOptions, chownOpts *idtools.IDPair, extract, isFromHostToCtr bool) (err error) { | ||
// Don't symlink process /dev/stdin | ||
srcPath := os.Stdin.Name() | ||
srcfi, _ := os.Stat(srcPath) |
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.
This is an extra system call on /dev/stdin
we do even in cases when src
is not /dev/stdin
; it would be nice to avoid that. (If this code needs to exist at all.)
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: jwhonce The full list of commands accepted by this bot can be found here. The pull request process is described here
Needs approval from an approver in each of these files:
Approvers can indicate their approval by writing |
Fixes #3829
Fixes https://bugzilla.redhat.com/show_bug.cgi?id=1741709
Signed-off-by: Jhon Honce [email protected]