-
Notifications
You must be signed in to change notification settings - Fork 18k
proposal: os: convenience function for one-off Root operations #73168
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
Comments
Related Documentation (Emoji vote if this was helpful or unhelpful; more detailed feedback welcome in this discussion.) |
Does it make sense to reuse the |
@earthboundkid It sounds like a goal of this proposal is to minimize the amount of added API, in which case adding a new single-purpose type which duplicates all the methods of |
The more I think about it, the more I like the AlwayReopen bool idea. You might want to always reopen even if you had a normal root. And it solves the problem with platforms that don't have native roots. |
As I read just the original proposal text, before reading the subsequent discussion about how it might actually work, the mental model I developed was:
Under that mental model, there could potentially be a new method that somehow reports which of the three states the root is currently in1 but I'm not sure I understand the use-case of either moving back from "opened" to "pre-open" or, alternatively, forcing the root to stay in the "pre-open" state after a single successful method call has complete made on it (which is how I understood this "AlwaysReopen" idea). EDIT: Of course immediately after posting this comment I realized what I was missing: under what I described, you'd presumably still need to call Footnotes
|
In the scenario of having
|
To elaborate on this, JS/Plan 9 could return a Root with AlwaysReopen set even from |
The goal of this proposal is to make it simple to replace an unsafe one-line file operation using filepath.Join with a safe one-line operation using os.Root. For example: // unsafe
f, err := os.Create(filepath.Join(baseDir, untrustedPath))
// safer
f, err := os.InRoot(baseDir).Create(untrustedPath) The idea behind
I don't understand how
The main lessening in security guarantees is on GOOS=js, where the lack of openat or any equivalent means we can't defend against TOCTOU races in symlink resolution. That's related to but not the same as the fact that we re-resolve the root path itself on each operation. |
If a function took a Root, it might want to how it behaves, since there's a difference between all operations being guaranteed to be in the same directory and it not being guaranteed if the directory is moved/renamed. |
Although in my earlier comment I missed the detail about how the thing gets closed, in retrospect I think that mental model was still mostly okay, with some adjustments:
With this in mind, I do now agree with @earthboundkid that I'd like some way either to ask an I think I personally prefer the "force into opened" model, since that avoids directly exposing the state and encourages writing functions that will do the right thing regardless of which flavor of Root they are passed: // ForceOpened upgrades an [Root] created by [InRoot] to provide the same
// guarantees as a root created by [OpenRoot].
//
// If the root was created by [OpenRoot] or has already been upgraded
// using this function, the call immediately succeeds having made no change.
//
// If this function succeeds then the Root must be explicitly closed using
// [Root.Close].
func (r *Root) ForceOpened() error These details aside though, it does seem a little concerning that there will now be Existing code that is relying on these guarantees would presumably now need to be updated to either check whether the root is "opened", use something like the
|
I think it would be useful to understand an realistic concrete scenario in which code would want to distinguish between an opened-on-demand Root from If we're really concerned about misuse, I suppose there could be a vet check to verify that the result of Edit: A less invasive alternative to a vet check might be to say that the Root returned by |
It is true that the concerns I raised at the end of my last comment are theoretical rather than practical. In large part that is because This new flavor of A function that takes an
So indeed, there seem to be fewer cases where this matters than I initially thought, and so perhaps the tradeoff is worth it. I'm still on the fence about it -- weakening the guarantees of a security-oriented feature in a way the compiler can't recognize purely for convenience does still give me pause -- but I'm not as concerned as I was before! I think I've ended up pretty neutral on this feature, so I'll bow out of the discussion here. |
I propose adding the following function:
os.Root
(#67002) supports performing filesystem operations within the context of some directory.Performing a single operation in a Root is substantially more verbose than doing so outside of one. Compare:
If the
Root
will be reused for many operations, the additional overhead of opening and closing it is not so significant. For a single operation, however, it's enough to be annoying.We have an
os.OpenInRoot
convenience function for opening files, but this covers only one operation. We could add similar functions for other operations (os.CreateInRoot
,os.RemoveInRoot
, etc.), but that would be a lot of new functions.This has come up in the context of #73126, which proposes adding
Root.ReadFile
andRoot.WriteFile
methods: #73126 (comment) asks if we can have top-level convenience functions as well, since opening and closing the root for short operations is inconvenient.I've also had this raised in a Google-internal discussion: We have a linter which recommends using the
github.com/google/safeopen
package for certain operations (recommending, for example, thatos.Create(filepath.Join(a, b))
could besafeopen.CreateBeneath(a, b)
). We're reluctant to change the linter to recommendos.Root
in cases where a one-liner becomes several lines opening and closing a Root.The
InRoot
function allows performing operations in a root with slightly less verbosity than the non-rooted equivalent usingfilepath.Join
. To continue the above example:The text was updated successfully, but these errors were encountered: