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 broken symlinks install when using install_umask or install_mode #4030
Fix broken symlinks install when using install_umask or install_mode #4030
Conversation
mesonbuild/minstall.py
Outdated
@@ -89,6 +89,44 @@ def sanitize_permissions(path, umask): | |||
except PermissionError as e: | |||
msg = '{!r}: Unable to set permissions {!r}: {}, ignoring...' | |||
print(msg.format(path, new_perms, e.strerror)) | |||
except FileNotFoundError as e: |
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.
As said, maybe never do this for symlinks at all?
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.
We could set follow_symlinks=False
on os.chmod
?
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.
Ah, right I can do that too... the idea here was i wasn't aware of symlinks having different permissions too, but so it seems possible in some platforms, so let's go with that indeed.
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.
Ops, not... So, NotImplementedError: chmod: follow_symlinks unavailable on this platform
will happen, thus we should ignore that 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.
Yes, looks like it makes more sense to ignore os.chmod/chown completely for symlinks.
mesonbuild/minstall.py
Outdated
@@ -120,6 +158,12 @@ def set_mode(path, mode, default_umask): | |||
except PermissionError as e: | |||
msg = '{!r}: Unable to set permissions {!r}: {}, ignoring...' | |||
print(msg.format(path, mode.perms_s, e.strerror)) | |||
except FileNotFoundError as e: | |||
if os.path.islink(path): |
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 again here... Probably it's just better not to even try.
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.
Yes.
@@ -98,7 +136,7 @@ def set_mode(path, mode, default_umask): | |||
# No chown() on Windows, and must set one of owner/group | |||
if not is_windows() and (mode.owner or mode.group) is not None: | |||
try: | |||
shutil.chown(path, mode.owner, mode.group) | |||
set_chown(path, mode.owner, mode.group, follow_symlinks=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.
Probaby here we could have follow_symlinks=os.file.isfile(path)
, however I think it doesn't really make much sense (not to say is wrong) to apply on the linked file.
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.
LGTM, plus changing the two places where you point out we should pass os.chmod(..., follow_symlinks=False)
.
mesonbuild/minstall.py
Outdated
@@ -89,6 +89,44 @@ def sanitize_permissions(path, umask): | |||
except PermissionError as e: | |||
msg = '{!r}: Unable to set permissions {!r}: {}, ignoring...' | |||
print(msg.format(path, new_perms, e.strerror)) | |||
except FileNotFoundError as e: |
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.
We could set follow_symlinks=False
on os.chmod
?
mesonbuild/minstall.py
Outdated
@@ -120,6 +158,12 @@ def set_mode(path, mode, default_umask): | |||
except PermissionError as e: | |||
msg = '{!r}: Unable to set permissions {!r}: {}, ignoring...' | |||
print(msg.format(path, mode.perms_s, e.strerror)) | |||
except FileNotFoundError as e: | |||
if os.path.islink(path): |
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.
Yes.
run_unittests.py
Outdated
Test that installation of broken symlinks works fine. | ||
https://github.com/mesonbuild/meson/issues/3914 | ||
''' | ||
testdir = os.path.join(self.common_test_dir, '199 install_mode') |
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.
There's no point adding two separate test cases for this, it only slows down the CI. Please replace 66 install subdir
above with 199 install_mode
and that should cover both.
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 was thinking the same, but at the same time they're eventually following two different codepaths when it comes to call set_mode
or sanitize_permissions
so I'd prefer to keep them both, but if you're fine with one only, that's ok.
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.
You're right, we should run both. But then let's factor out the test itself to not duplicate 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.
Ok, I've done that... Adding a function to perform them both. A class just for that was maybe too much.
mesonbuild/minstall.py
Outdated
if _group is None: | ||
raise LookupError("no such group: {!r}".format(group)) | ||
|
||
os.chown(path, _user, _group, dir_fd=dir_fd, follow_symlinks=follow_symlinks) |
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 noticed that this was taken from CPython and frankly I am shocked that this trivial change doesn't already exist upstream. Fancy opening an upstream bug with a patch so we can eventually get rid of this? :)
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.
Yes, I didn't want to risk to change the behavior at all, so I just reused that.
Actually there is, and I wanted to re-propose the PR already.
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.
Copying code verbatim from Python (or anywhere else for that matter) is ABSOLUTELY FORBIDDEN. At least it is a license violation and at worst it is illegal (assuming the licenses are not compatible, I don't know for sure and am not a lawyer). This will not be accepted UNDER ANY CIRCUMSTANCES!
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.
At least it is a license violation
For some reason I was under the impression that we're licensed under the same license as CPython.
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.
@jpakkane well, ok, I can rephrase that code.... Won't change much though, nor I though it was a license violation here.
e3598e7
to
f8cd612
Compare
I don't understand under which circumstances this would make sense? Symlinks always have all status bits set (at least that is what |
FreeBSD seems to allow symlinks to have permissions set, but that's the only operating system. We could revisit this if that turns out to be a useful edge-case. So you're right, we could just ignore |
Apparently OSX does as well but you can only change them on the command line if the symlink points to nonexisting files (i.e. is broken). 🤦♂️ |
The reason why I thought it's fine to not support this right now is because this is for We can, of course, add it if someone asks for it, but seems like needless complexity otherwise (especially if we can't "just" copy CPython code and change it) |
@jpakkane as I wrote in the first comments of the PR ideally I would have just skipped setting the permissions on the link themselves, then going deeper I noticed that in some platforms this something possible, thus it made sense to apply the permissions to the link itself, but not to the linked target. Of course we can go back to the simpler solution, but in general I think the way is done now should be quite conform to what is expected for the platform. EDIT: I've changed it to use another approach instead of using copying the shutil code. However, as said, this is not just about broken symlinks, what is fixed here is also the case where a symlink points to another position that is not managed by the installer, as in the current state it will try to change the mode of that file too, while this is wrong. |
b0fa3fc
to
76dbe4b
Compare
Add test to verify the installation of broken symlinks when a default_umask is set. Reusing the same code of other test, thus sharing the actual test code in a single function.
It's only supported by few platforms when the linked file exists, while it would cause an error otherwise. In any case just implement this via an helper set_chmod function that will handle the case where follow_symlinks is not supported by the platform and will just not set any mod for the link itself (as it would otherwise apply to the linked file). Fixes mesonbuild#3914
Since we're supposed to call this for each installed path, we only should go through what we've installed and not what this point to, as it might be outside our scope or not existent. To do this, since shutil.chown doesn't expose the follow_symlink that os.chown has, we can temporarily replace os.chown with a lambda that acutually passes all the values as we want them, and then restore it to the built-in functions. Not the nicest way, but fixes the issue without having to reimplement what shutil does. Fixes mesonbuild#3914
This could happen when setting an default install mode but with broken symlinks. Fixes mesonbuild#3914
76dbe4b
to
abf65c9
Compare
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.
All changes LGTM, needs @jpakkane approval.
Other fixes for #3914 to ignore setting the ownership and mode for broken symlinks after install.
Unfortunately the best way to do this was to cause some stdlib code duplication, because we need to be able to use
os.chown
withfollow_symlinks
set toFalse
, butshutil.chown
does not support this.In fact, in general I think we should just never to try to set the ownership of a linked file, as this might be pointing outside the install path (while it's correct to set it only to the link itself), and probably at the same time the same should be for the file-mode of linked files.