Create lock files 644, only check read bit for locks, respect umask#17646
Create lock files 644, only check read bit for locks, respect umask#17646dcwatson wants to merge 4 commits intoastral-sh:mainfrom
Conversation
|
FWIW, I just verified the manual test from #17115 still works: |
EliteTK
left a comment
There was a problem hiding this comment.
Thanks.
Overall I don't see anything wrong with the idea. Changing the mode to 0o444 will be a good test to ensure that we can still take exclusive locks on MacOS.
I am also curious to see what error message we get if umask is 777, but I have a feeling that we'll never get this far in that case, although you could use fault injection with strace to test it.
crates/uv-fs/src/locked_file.rs
Outdated
| // Otherwise, create a temporary file with 666 permissions. We must set | ||
| // permissions _after_ creating the file, to override the `umask`. | ||
| // Otherwise, create a temporary file with 644 perms in the same directory as path. | ||
| let perms = Permissions::from_mode(0o644); |
There was a problem hiding this comment.
If we don't need to write, this should just be 0o444.
There was a problem hiding this comment.
I don't have strong feelings either way. 644 is a pretty typical "new file" permission, but you're right we don't strictly need the owner write bit. It does make deleting them annoying in zsh since it will prompt you 😅
There was a problem hiding this comment.
(I presume that could break cache cleans?)
There was a problem hiding this comment.
Regarding cache cleans, the behaviour described is just the behaviour from rm implementations. It's a courtesy to tell you that you're removing a write protected file. There's nothing stopping you from deleting it as deleting it is an operation on the directory not the file so only the directory permissions matter. As such, I imagine that cache cleans (unless they use rm -r or something) shouldn't be affected.
Regarding 0o444 vs 0o644. I don't think we have a test which specifically checks the case this code was initially written to address (please correct me if I'm wrong, I couldn't find anything). That is, originally the lock files were made with 0o600 permissions which caused locking issues if some other user had touched the cache first and written the lockfile. We support this presumably because of containers with delegated UIDs and also running as root.
So, if we have it at 0o644 we might end up regressing there.
I think people are already used to rm complaining when deleting .git directories so I am not sure it's that big of a deal here. There might be an argument here that this is a breaking change... not sure.
There was a problem hiding this comment.
I'm not sure a detail like this would warrant it, but we could add a UV_LOCK_MODE environment variable (a la UV_LOCK_TIMEOUT).
There was a problem hiding this comment.
@dcwatson if you don't mind, just push a commit for setting the mode to 0o444. I am curious to see the test suite results. We can always just drop that commit quite easily once we make a decision on this.
| .read(true) | ||
| .write(true) | ||
| .create(true) | ||
| .open(path.as_ref())?; |
There was a problem hiding this comment.
This should also have had .write(true) dropped. But instead, it should actually just use .mode(0o444).
There was a problem hiding this comment.
I initially dropped the .write(true) but tests failed on Windows. I added it back to both implementations (this and the #[cfg(not(unix))] one) since I'm not very familiar with Rust's filesystem calls, but you're probably right that it can be dropped from this case.
There was a problem hiding this comment.
Yeah I can understand why the windows variant didn't change since on windows we're not using flock. But keeping it in this branch of the unix version is definitely inconsistent.
There was a problem hiding this comment.
Changed to set DESIRED_MODE. What that mode should be seems like a bit of an open question, but for my purposes anything without world write/execute is good.
|
So Pending that, it seems like this change maybe won't regress #11324 for uv, but will regress it for using pip with uv's cache, which I had no idea we supported... Edit: Good news is, the tests pass on mac. |
|
I'm a little confused as to why the Edit: I just noticed the It does seem like pip creates I also ran these locally on my mac and couldn't reproduce, unless I manually set the lock to something like |
|
The tests are failing when we're asking pip to use a cache directory that uv has created the lockfile in. And the reason is probably this: https://github.com/tox-dev/filelock/blob/680b51c4b027b43bbf2d9bae2de45aebeebbdd03/src/filelock/_unix.py#L41 |
|
Does |
|
Oh hold on, I didn't notice the That makes this much simpler to understand then. Those tests are testing current uv vs previous uv. In the current uv we are only opening for reading, and only setting the read bits. In the previous version we were opening for reading and writing, and setting both bits. So, if we stick to 644 (or just 666 and let the umask handle dropping the group/other write bits) we would actually be breaking backwards compatibility with older uv versions in the case described in #11324. And, if we use 444, then we're just flat out breaking older uv versions accessing caches created by a newer uv. |
|
The good news is, the upcoming 0.10.0 release will incorporate breaking changes. On the other hand, this may be a bit too breaking, not sure. I'll sleep on it. |
|
If we went with 644, the only breaking change would be when using an old version with a new lock file, and doing so as separate users. I can definitely see putting it off until 0.10.0, but it still seems like an edge case with an easy fix (update uv). Our current situation is we can't use Anyway, I really appreciate you taking the time to consider this! I'm pushing because I love the project and want to use it at work 😄 |
|
The easiest path forward is to ship this behind a preview feature flag, would it unblock your adoption if you could change the behavior by setting an environment variable or configuration file option? |
|
Yes, we could definitely work with that. In that case, is it worth considering a |
For the current behaviour (without this PR applied), it only makes sense for write and read bits to be set or unset in tandem. The lockfile is never written to or read from, but we won't be able to open it for locking without both bits set1. Being able to configure the lockfile mode to 752 or 725 would be equivalent in that it would effectively work the same as 600. Moreover, it only makes sense for the lockfile's read and write permissions to be as permissive as a cache entry's read permissions. So, to avoid confusion, we'd probably want a cache-perms setting or something which lets you specify the permissions at the group, user and other level, rather than specific mode bits. But, instead, we could already do something like this most likely without breaking anything: let read_mode = 0o444 & !umask;
// Mirror the read permission into the write permissions
let write_mode = read_mode >> 1;
let desired_mode = read_mode | write_mode;The reason why this probably won't break anything is that if the umask restricts reading for group or other, then cache entries written by a uv running with such a umask would already not work for other users. So creating a lockfile with less restrictive permissions would only delay the inevitable permission errors down the road (although I'd like to test this a bit further to make sure there isn't actually a new failure case). With the above approach, we wouldn't need any new configuration, we could just use the umask to inform the lockfile mode. Furthermore, once the behaviour of using Unfortunately for your case, it would still set
Do you think this kind of approach would be sufficient for your purposes? I completely open to the possibility that I missed something in my analysis, so let me know if you think there's still another justification for a separate lockfile mode configurable or maybe even the cache permission setting described above. Regardless, the change I describe here should probably be a separate PR, and it should include adding some targetted tests to the test suite. On the other hand, we would be happy to accept this PR with the changes to the lockfile creation mode behind a preview flag and unconditional removal of the Footnotes
|
|
I think what you're saying makes sense in regard to lock files for caches not needing to be more permissive than the cache files themselves. However, I'm also not convinced we want to create lock files as My preference would be to drop the |
Good point. But I think this just reinforces that maybe we shouldn't have a
Sorry, you can ignore the thing about The only concern is that users trying to manually On the one hand,
Regarding how to add a preview flag, this is a recent PR which may help: #17464 But, it looks somewhat involved to get the flag to the right place... Feel free to discuss it with us. One last option is also that we take over and build on top of your PR.
I feel like any mode setting we introduce should probably also respect umask. |
This sounds ideal to me!
At first glance, it seems like it would need to be passed in to every acquire call? I didn't see a clean way to get settings into
That would be amazing if it's on the table. I'm not opposed to giving it a go with some guidance, but it will almost certainly be faster for you all to take it over. Happy to stub it out if it would be helpful, but it feels like the actual changes to
Funny, I feel the other way - that if I'm going to tell |
This comment was marked as off-topic.
This comment was marked as off-topic.
## Summary When opening lock files, don't open them for writing to avoid needing the write permission in the future. Related: #17646 ## Test Plan Test suite ran. Co-authored-by: Dan Watson <[email protected]>
Summary
Resolves #16769 by creating the temp lock files 644, and not requiring write access when trying to acquire a lock on a file. Additionally, I removed the forcing of permissions to override umask -- if a umask is set more restrictively it was probably for a reason, and now that failing to acquire a lock is just a warning, it doesn't seem worth it to override.
Test Plan
Manually, either via docker or CLI, as it requires separate users to both run a
uvcommand that takes a lock on the same project.I tested with a locally built
uv. I ran an initial sync usingroot, and verified the lock file was as expected:Then running a sync via my own user:
Finally, I modified the lock file so it was owner-read-only (
chmod 400), and verified a warning (but not an error) was logged when I ran a sync as my own user: