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

WIP: Improved Stat() and Mode() handling for added files #1413

Closed
wants to merge 9 commits into from

Conversation

wking
Copy link
Contributor

@wking wking commented Jun 22, 2015

Don't merge this yet, it's a work in progress.

Things like adding named-pipes don't work at the moment, because we
currently assume files are either regular files or directories, and
don't check mode flags for other options (symlinks, named-pipes,
etc.). This branch tries to shift us towards explicit mode handling
so we can gracefully die when we hit file types that don't yet have
IPFS analogs.

…tories

For example, we want to crash if we hit a named pipe, not block
forever trying to read from it.

License: MIT
Signed-off-by: W. Trevor King <wking@tremily.us>
We need Stat() calls to figure out whether a file is a directory,
regular file, or other object (e.g. a symlink, named pipe, etc.).
This commit shifts our File interface to more closely match os.File.

I'm a bit concerned that the backing Stat calls on any underlying
os.File is done at wrapper-creation time and not at wrapper.Stat()
time.  That means we'll get stale data with a workflow like:

1. Create a new commands/files/... File (e.g. via NewSerialFile)
2. Edit the backing file (e.g. appending data to it).
3. Call f.Stat() on our wrapping file to get a FileInfo type.

In that case, fi.Size() and fi.ModTime() on the returned FileInfo from
(3) will reflect the values that the file had at (1), and not the
values that it should have at (3).  Maybe we're mostly using our File
wrappers as read- or write-only entities and then throwing them away,
in which case this discrepancy may not be too important.  But it's
still a race hole I'd like to close.

License: MIT
Signed-off-by: W. Trevor King <wking@tremily.us>
Without the O_NONBLOCK flag:

  Open("/path/to/named-pipe")

will hang in a syscall (on Linux 4.1.0-rc8 anyway).  The Go
stack-trace looks like:

  syscall.Syscall(0x2, 0xc20969e870, 0x80000, 0x0, 0x0, 0xc2096ad040, 0xc2089a7a78)
          /usr/lib/go/src/syscall/asm_linux_amd64.s:21 +0x5 fp=0xc2089a79f8 sp=0xc2089a79f0
  syscall.open(0xc20969e840, 0x8, 0x80000, 0x0, 0x0, 0x0, 0x0)
          /usr/lib/go/src/syscall/zsyscall_linux_amd64.go:16 +0xaa fp=0xc2089a7a58 sp=0xc2089a79f8
  syscall.Open(0xc20969e840, 0x8, 0x80000, 0x0, 0x12, 0x0, 0x0)
          /usr/lib/go/src/syscall/syscall_linux.go:23 +0x5a fp=0xc2089a7a98 sp=0xc2089a7a58
  os.OpenFile(0xc20969e840, 0x8, 0x0, 0x0, 0xc2089a7bd0, 0x0, 0x0)
          /usr/lib/go/src/os/file_unix.go:78 +0x6f fp=0xc2089a7af0 sp=0xc2089a7a98
  os.Open(0xc20969e840, 0x8, 0xc2089a7bd0, 0x0, 0x0)
          /usr/lib/go/src/os/file.go:239 +0x55 fp=0xc2089a7b30 sp=0xc2089a7af0
  github.com/ipfs/go-ipfs/commands/files.(*serialFile).NextFile(0xc20806dd80, 0x0, 0x0, 0x0, 0x0)
          /home/wking/src/go/src/github.com/ipfs/go-ipfs/commands/files/serialfile.go:90 +0x3e0 fp=0xc2089a7c60 sp=0xc2089a7b30
  ...

With this non-blocking open, the open (unsurprisingly ;) doesn't
block.  I haven't audited downstream usuage to make sure it handles a
non-blocking file, but with this change I get:

  $ ipfs add -r /path/to/dir
  Error: `/path/to/dir/named-pipe` is neither a directory nor a file

instead of the hung syscall when the named pipe has no data in it.

License: MIT
Signed-off-by: W. Trevor King <wking@tremily.us>
Without the O_NONBLOCK flag:

  Open("/path/to/named-pipe")

will hang in a syscall (on Linux 4.1.0-rc8 anyway).  The Go
stack-trace looks like:

  syscall.Syscall(0x2, 0xc208080d51, 0x80000, 0x0, 0x0, 0x1, 0x41a31f)
          /usr/lib/go/src/syscall/asm_linux_amd64.s:21 +0x5 fp=0xc2080a75c8 sp=0xc2080a75c0
  syscall.open(0x7ffc89956140, 0x8, 0x80000, 0x100000000, 0x0, 0x0, 0x0)
          /usr/lib/go/src/syscall/zsyscall_linux_amd64.go:16 +0xaa fp=0xc2080a7628 sp=0xc2080a75c8
  syscall.Open(0x7ffc89956140, 0x8, 0x80000, 0x0, 0x6, 0x0, 0x0)
          /usr/lib/go/src/syscall/syscall_linux.go:23 +0x5a fp=0xc2080a7668 sp=0xc2080a7628
  os.OpenFile(0x7ffc89956140, 0x8, 0x0, 0x0, 0x7f0f795e2408, 0x0, 0x0)
          /usr/lib/go/src/os/file_unix.go:78 +0x6f fp=0xc2080a76c0 sp=0xc2080a7668
  os.Open(0x7ffc89956140, 0x8, 0xc3b680, 0x0, 0x0)
          /usr/lib/go/src/os/file.go:239 +0x55 fp=0xc2080a7700 sp=0xc2080a76c0
  github.com/ipfs/go-ipfs/commands/cli.appendFile(0xc208080d70, 0x0, 0x1, 0xc20802a930, 0x1, 0x3, 0x11c3460, 0xc208080d01, 0x0, 0x0, ...)
          /home/wking/src/go/src/github.com/ipfs/go-ipfs/commands/cli/parse.go:342 +0xbb fp=0xc2080a7810 sp=0xc2080a7700
  ...

With this non-blocking open, the open (unsurprisingly ;) doesn't
block.  I haven't audited downstream usuage to make sure it handles a
non-blocking file, but with this change I get:

  $ ipfs add -r /path/to/namrd-pipe
  Error: `/path/to/named-pipe` is neither a directory nor a file

instead of the hung syscall when the named pipe has no data in it.

License: MIT
Signed-off-by: W. Trevor King <wking@tremily.us>
@jbenet jbenet added the status/in-progress In progress label Jun 22, 2015
@whyrusleeping whyrusleeping mentioned this pull request Jun 23, 2015
34 tasks
@wking wking force-pushed the tk/check-for-non-file-or-dir-modes branch 3 times, most recently from d593388 to dd9bad0 Compare June 24, 2015 07:34
License: MIT
Signed-off-by: W. Trevor King <wking@tremily.us>
For streaming responses (e.g. 'ipfs add -r /path/to/root/directory'),
we may hit errors later on in the stream, after we've already written
the initial 200 response and started in on the streaming body
(e.g. /path/to/root/directory/a/b/c is an unsupported file type).  We
should check for that sort of error, and set a trailer [1] if there
were any errors.  Unfortunately, it's a bit awkward to set these
headers in Go 1.4 and earlier [2], but [2] does have a workaround for
Go 1.4 (it's not clear if the workaround is compatible with Go 1.3).

[1]: http://tools.ietf.org/html/rfc2616#section-14.40
[2]: golang/go#7759

License: MIT
Signed-off-by: W. Trevor King <wking@tremily.us>
Make sure we neither hang nor silently tread named pipes as regular
files.

License: MIT
Signed-off-by: W. Trevor King <wking@tremily.us>
Make sure we neither hang nor silently tread named pipes as regular
files.

License: MIT
Signed-off-by: W. Trevor King <wking@tremily.us>
On Linux, open() will follow symlinks unless you use the O_NOFOLLOW
flag (added in Linux 2.1.126).  Then it will error out with ELOOP
unless you've set O_PATH.  Unfortunately, Go doesn't define O_PATH and
has no plans to do so [1].

The other tricky bit here is extracting the errno [2] from the
*PathError [3] returned by os.Open [4].  The new files.Errno tries to
cast the error instance to an *os.PathError.  If that succeeds, it
looks at the *os.PathError's Err attribute and tries to cast that to
an Errno.  If that succeeds it returns the extracted errno.

With this commit, we now have File object for the symlinks with the
correct filename and mode.  I've also created a string-based reader to
pass along the link target, so the adder will have it available when
it gains support for these types of files.

[1]: golang/go#7830
[2]: https://golang.org/pkg/syscall/#Errno
[3]: https://golang.org/pkg/os/#PathError
[4]: https://golang.org/pkg/os/#Open

License: MIT
Signed-off-by: W. Trevor King <wking@tremily.us>
@@ -335,8 +340,16 @@ func appendStdinAsString(args []string, stdin *os.File) ([]string, *os.File, err
func appendFile(args []files.File, inputs []string, argDef *cmds.Argument, recursive bool) ([]files.File, []string, error) {
path := inputs[0]

file, err := os.Open(path)
file, err := os.OpenFile(path, (os.O_RDONLY | syscall.O_NOFOLLOW | syscall.O_NONBLOCK), 0)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets just call stat before we call open. It might introduce a slight race, but its better than breaking our cross platformability with importing syscall.

@whyrusleeping
Copy link
Member

@wking update? whats needed here?

@whyrusleeping whyrusleeping mentioned this pull request Jul 1, 2015
44 tasks
@jbenet
Copy link
Member

jbenet commented Aug 2, 2015

ping

@whyrusleeping
Copy link
Member

closing, old PR cleanup time.

@jbenet jbenet removed the status/in-progress In progress label Oct 18, 2015
@Kubuxu Kubuxu deleted the tk/check-for-non-file-or-dir-modes branch February 27, 2017 20:42
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.

3 participants