Skip to content
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

fileutils: Use ModeType as the mask for mode checks #3

Merged
merged 1 commit into from
May 4, 2020

Conversation

wking
Copy link
Contributor

@wking wking commented Apr 6, 2018

POSIX provides S_IS*(m) macros to portably interpret the mode type, but does not define values for each type. @alban pointed out that st_mode is not a bitfield on Linux 1. For example, Linux defines:

S_IFBLK 060000
S_IFDIR 040000
S_IFCHR 020000

So m&S_IFCHR == S_IFCHR, for example, would succeed for both character and block devices. Go translates the system values to a platform-agnostic bitfield, so the previous approach works on Go. But it may be confusing for people used to the native non-bitfield mode, so this pull request moves us to an approach that does not rely on Go's using a bitfield.

@wking wking force-pushed the mode-type-comparison branch from 11f4828 to e48df7f Compare April 6, 2018 17:38
@wking
Copy link
Contributor Author

wking commented Apr 6, 2018

Hang on, I'm going to drop some of these syscall constants too…

POSIX provides S_IS*(m) macros to portably interpret the mode type,
but does not define values for each type [1].  Alban pointed out that
st_mode is not a bitfield on Linux [2].  For example, Linux defines
[3]:

  S_IFBLK 060000
  S_IFDIR 040000
  S_IFCHR 020000

So 'm&S_IFCHR == S_IFCHR', for example, would succeed for both
character and block devices.  Go translates the system values to a
platform-agnostic bitfield [4], so the previous approach works on Go.
But it may be confusing for people used to the native non-bitfield
mode, so this commit moves us to an approach that does not rely on
Go's using a bitfield.

I've also dropped the 07000 portion of the previous 07777 mask in
favor of the cross-platform ModePerm mask.  This avoids an internal
magic number, and the sticky, suid, and sgid bits don't make sense for
device nodes.  And I'm using some contants from os instead of their
syscall analogs.  We can't drop the syscall dependency, because we're
still using syscall to construct the Mknod arguments, but with this
commit we're no longer using it to inspect the source file type.

[1]: http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sys_stat.h.html
[2]: opencontainers/runtime-tools#308 (comment)
[3]: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/uapi/linux/stat.h?h=v4.16#n9
[4]: https://github.com/golang/go/blob/b0d437f866eb8987cde7e6550cacd77876f36d4b/src/os/types.go#L45
@wking wking force-pushed the mode-type-comparison branch from e48df7f to 8bad3ca Compare April 6, 2018 17:57
@wking
Copy link
Contributor Author

wking commented Apr 6, 2018

Hang on, I'm going to drop some of these syscall constants too…

Ok, I've pushed that along a bit with e48df7f8bad3ca. Let me know if you like that as it stands, or if you'd like me to restore some of the syscall constants, or if you'd like me to push further to drop syscall entirely in favor of x/sys/unix.

@thaJeztah
Copy link

@mrunalp PTAL

@mrunalp
Copy link
Owner

mrunalp commented May 4, 2020

LGTM

@mrunalp mrunalp merged commit 7be891c into mrunalp:master May 4, 2020
@thaJeztah
Copy link

Thanks! Do you think it's worth considering tagging a v1.0.0 release for this repository? It looks like it's been in use for quite a while by runc, and no considerable changes during that time

@kolyshkin
Copy link
Contributor

It looks like it's been in use for quite a while by runc, and no considerable changes during that time

In case there are no users except runc, I'd rather merge this one to runc. Easier to maintain that way.

@thaJeztah
Copy link

thaJeztah commented May 16, 2020

kolyshkin added a commit to kolyshkin/fileutils that referenced this pull request May 16, 2020
This subtle bug keeps lurking in because error checking for `Mkdir()`
and `MkdirAll()` is slightly different wrt `EEXIST`/`IsExist`:

 - for `Mkdir()`, `IsExist` error should (usually) be ignored
   (unless you want to make sure directory was not there before)
   as it means "the destination directory was already there";

 - for `MkdirAll()`, `IsExist` error should NEVER be ignored.

This commit removes ignoring the IsExist error, as it should not
be ignored.

For more details, a quote from my runc commit 6f82d4b (July 2015):

    TL;DR: check for IsExist(err) after a failed MkdirAll() is both
    redundant and wrong -- so two reasons to remove it.

    Quoting MkdirAll documentation:

    > MkdirAll creates a directory named path, along with any necessary
    > parents, and returns nil, or else returns an error. If path
    > is already a directory, MkdirAll does nothing and returns nil.

    This means two things:

    1. If a directory to be created already exists, no error is
    returned.

    2. If the error returned is IsExist (EEXIST), it means there exists
    a non-directory with the same name as MkdirAll need to use for
    directory. Example: we want to MkdirAll("a/b"), but file "a"
    (or "a/b") already exists, so MkdirAll fails.

    The above is a theory, based on quoted documentation and my UNIX
    knowledge.

    3. In practice, though, current MkdirAll implementation [1] returns
    ENOTDIR in most of cases described in mrunalp#2, with the exception when
    there is a race between MkdirAll and someone else creating the
    last component of MkdirAll argument as a file. In this very case
    MkdirAll() will indeed return EEXIST.

    Because of mrunalp#1, IsExist check after MkdirAll is not needed.

    Because of mrunalp#2 and mrunalp#3, ignoring IsExist error is just plain wrong,
    as directory we require is not created. It's cleaner to report
    the error now.

    Note this error is all over the tree, I guess due to copy-paste,
    or trying to follow the same usage pattern as for Mkdir(),
    or some not quite correct examples on the Internet.

    [1] https://github.com/golang/go/blob/f9ed2f75/src/os/path.go

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
@kolyshkin kolyshkin mentioned this pull request May 16, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants