Skip to content

Set file permissions accoding to umask #325

Closed

Description

@funkey

Zarr uses temporary files that are moved in place to write meta-files and chunks. As a side effect, the created containers are only readable by the user who created them (as is default for temporary files). A better approach might be to set the file permissions after the move according to the users umask.

Activity

  1. alimanfoo commented on Nov 8, 2018

    @alimanfoo
    Member

    Thanks @funkey.

    Do you know if there a reliable way to determine what the current user's umask is? I found something like this, but don't know if it's the best way, and involves temporarily obliterating the umask just to find out what it is, which seems a little awkward:

    import os
    umask = os.umask(0)
    os.umask(umask)

    Is this something that can be done once and cached somewhere, or would we need to query the umask before every time you want to change a file's permissions?

    Also wondering if file permissions should be surfaced as an argument to the DirectoryStore constructor, so users can control explicitly if they want to?

  2. funkey commented on Nov 8, 2018

    @funkey
    ContributorAuthor

    I think this is the only way to get the umask, this seems to go back to the POSIX standard (see man umask).

    Caching should be fine, I don't think the umask of a process can be changed from the outside.

    Having file permissions as an optional argument to DirectoryStore sounds like a good idea.

  3. alimanfoo commented on Nov 8, 2018

    @alimanfoo
    Member

    Cool. How about adding in a chmod argument to the DirectoryStore constructor. If None (default) then files will be set to permissions based on the user's umask. If False then permissions will not be changed (current behaviour). If some other value then file permissions will be changed to that value, assuming the value is a numeric mode that can be passed to os.chmod(). Thoughts?

  4. funkey commented on Nov 8, 2018

    @funkey
    ContributorAuthor

    That sounds very reasonable to me!

  5. alimanfoo commented on Nov 8, 2018

    @alimanfoo
    Member

    Great, thanks @funkey. A PR would be welcome if you have the time, otherwise we can label this as "help wanted".

  6. jakirkham commented on Nov 8, 2018

    @jakirkham
    Member

    What if we just used a normal file (instead of a temp file)? Would that solve this issue?

  7. funkey commented on Nov 8, 2018

    @funkey
    ContributorAuthor

    Actually, yes. That might be the easiest solution.

  8. alimanfoo commented on Nov 8, 2018

    @alimanfoo
    Member
  9. jakirkham commented on Nov 9, 2018

    @jakirkham
    Member

    Meaning the randomized part? We could use a UUID.

  10. alimanfoo commented on Nov 9, 2018

    @alimanfoo
    Member

    So the benefit of using a normal file is (1) no need to query current umask, and (2) no need to chmod?

    I.e., in the default case (file should be created according to current umask) would you generate a temporary file name using UUID, then use the built-in open() function to open it? (Then use os.replace() to move it into place when writing is finished successfully, or os.remove() if any errors occur.)

    In the case where you want to allow the user to provide an explicit file mode, would you open the file with the lower-level os.open() function? Or open with built-in open() then os.chmod() if write succeeds? Or do we not worry about supporting this now (it's beyond the scope of the original issue)?

  11. alimanfoo commented on Nov 9, 2018

    @alimanfoo
    Member

    Thinking about this has actually reminded me that we might want to reconsider the strategy of using a different randomly-named temporary file for each atomic write. I've tried to write up the issue in #328. It's somewhat orthogonal to the issue here of file permissions, however if we are considering changes to the way files are created then #328 is also relevant.

  12. jakirkham commented on Jan 13, 2019

    @jakirkham
    Member

    So the benefit of using a normal file is (1) no need to query current umask, and (2) no need to chmod?

    That's right.

    I.e., in the default case (file should be created according to current umask) would you generate a temporary file name using UUID, then use the built-in open() function to open it? (Then use os.replace() to move it into place when writing is finished successfully, or os.remove() if any errors occur.)

    Sounds like a good plan to me.

    In the case where you want to allow the user to provide an explicit file mode, would you open the file with the lower-level os.open() function? Or open with built-in open() then os.chmod() if write succeeds? Or do we not worry about supporting this now (it's beyond the scope of the original issue)?

    Wouldn't worry about this yet. This basically hasn't come up AFAICT. Except of course because temp files are a little unusual. Moving away from temp files should eliminate the need to think about this.

  13. eddienko commented on Jun 26, 2019

    @eddienko

    This is bothering me a lot, since the directory storage created by Zarr cannot be shared between members of the group. See my quick fix in the PR, does that seem reasonable?

  14. added a commit that references this issue on Jul 3, 2019
    6d49909
  15. lilahasa commented on Jul 15, 2019

    @lilahasa

    +1 can this be included this in the next release?

  16. added a commit that references this issue on Jul 22, 2019
    74e063b
  17. manolinux commented on Jul 29, 2019

    @manolinux

    +1 In my opinion, this is absolutely necessary in UNIX enviroments, should be released ASAP ...

  18. manolinux commented on Jul 29, 2019

    @manolinux

    Just in case someone needs it:

    I've solved this in a dirty way ... The problem comes with tempfile__mkstemp_inner, that creates the temp file with 0600 permissions. I've set up this to use current mask (mine takes into account group is 0007):

    def _mkstemp_inner(dir, pre, suf, flags, output_type):
        """Code common to mkstemp, TemporaryFile, and NamedTemporaryFile."""
    
        names = tempfile._get_candidate_names()
        if output_type is bytes:
            names = map(tempfile._os.fsencode, names)
    
        for seq in range(tempfile.TMP_MAX):
            name = next(names)
            file = tempfile._os.path.join(dir, pre + name + suf)
            try:
                umask = os.umask(0)
                os.umask(umask)
                fd = tempfile._os.open(file, flags, 0o666 & ~umask)
            except FileExistsError:
                continue    # try again
            except PermissionError:
                # This exception is thrown when a directory with the chosen name
                # already exists on windows.
                if (tempfile._os.name == 'nt' and tempfile._os.path.isdir(dir) and
                    tempfile._os.access(dir, tempfile._os.W_OK)):
                    continue
                else:
                    raise
            return (fd, tempfile._os.path.abspath(file))
    
        raise tempfile.FileExistsError(tempfile._errno.EEXIST,
                              "No usable temporary file name found")
    
    tempfile._mkstemp_inner = _mkstemp_inner
    
  19. d-v-b commented on Aug 31, 2019

    @d-v-b
    Contributor

    +1 A fix to this would be greatly appreciated.

  20. adcroft commented on Oct 9, 2019

    @adcroft

    +1 We also ran into this limitation just recently. A fix seems necessary to be able share data as expected @raphaeldussin

  21. raphaeldussin commented on Oct 9, 2019

    @raphaeldussin
    Contributor

    +1 with @d-v-b and @adcroft

    this seems like a blocking issue for collaborative work

  22. added a commit that references this issue on Oct 28, 2019
    e6667aa
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions