From 7e05e809b7579ad0473fff6ce466c19d417d4e93 Mon Sep 17 00:00:00 2001 From: Sebastian Lackner Date: Sat, 5 Jan 2019 15:44:32 +0100 Subject: main: Run 'ensure fds' code early during the program startup. The files are apparently processed in alphabetic order, so cli_args.go is processed before main.go. In order to run before the go-fuse imports, put the 'ensure fds' code in a separate package. Debug messages are omitted to avoid additional imports (that might contain other code messing up our file descriptors). --- internal/ensurefds012/ensurefds012.go | 52 +++++++++++++++++++++++++++++++++++ internal/fusefrontend/dircache.go | 4 +++ 2 files changed, 56 insertions(+) create mode 100644 internal/ensurefds012/ensurefds012.go (limited to 'internal') diff --git a/internal/ensurefds012/ensurefds012.go b/internal/ensurefds012/ensurefds012.go new file mode 100644 index 0000000..7872eb2 --- /dev/null +++ b/internal/ensurefds012/ensurefds012.go @@ -0,0 +1,52 @@ +package ensurefds012 + +// Package ensurefds012 ensures that file descriptors 0,1,2 are open. It opens +// multiple copies of /dev/null as required. +// The Go stdlib as well as the gocryptfs code rely on the fact that +// fds 0,1,2 are always open. +// +// Use like this: +// +// import _ "github.com/rfjakob/gocryptfs/internal/ensurefds012" +// +// The import line MUST be in the alphabitcally first source code file of +// package main! +// +// You can test if it works as expected by inserting a long sleep into main, +// startings gocryptfs with all fds closed like this, +// +// $ ./gocryptfs 0<&- 1>&- 2>&- +// +// and then checking the open fds. It should look like this: +// +// $ ls -l /proc/$(pgrep gocryptfs)/fd +// total 0 +// lrwx------. 1 jakob jakob 64 Jan 5 15:54 0 -> /dev/null +// lrwx------. 1 jakob jakob 64 Jan 5 15:54 1 -> /dev/null +// lrwx------. 1 jakob jakob 64 Jan 5 15:54 2 -> /dev/null +// l-wx------. 1 jakob jakob 64 Jan 5 15:54 3 -> /dev/null +// lrwx------. 1 jakob jakob 64 Jan 5 15:54 4 -> 'anon_inode:[eventpoll]' +// +// See https://github.com/rfjakob/gocryptfs/issues/320 for details. + +import ( + "os" + "syscall" + + "github.com/rfjakob/gocryptfs/internal/exitcodes" +) + +func init() { + fd, err := syscall.Open("/dev/null", syscall.O_RDWR, 0) + if err != nil { + os.Exit(exitcodes.DevNull) + } + for fd <= 2 { + fd, err = syscall.Dup(fd) + if err != nil { + os.Exit(exitcodes.DevNull) + } + } + // Close excess fd (usually fd 3) + syscall.Close(fd) +} diff --git a/internal/fusefrontend/dircache.go b/internal/fusefrontend/dircache.go index 6d0d570..68fd99c 100644 --- a/internal/fusefrontend/dircache.go +++ b/internal/fusefrontend/dircache.go @@ -35,6 +35,8 @@ type dirCacheEntryStruct struct { func (e *dirCacheEntryStruct) Clear() { // An earlier clear may have already closed the fd, or the cache // has never been filled (fd is 0 in that case). + // Note: package ensurefds012, imported from main, guarantees that dirCache + // can never get fds 0,1,2. if e.fd > 0 { err := syscall.Close(e.fd) if err != nil { @@ -72,6 +74,8 @@ func (d *dirCacheStruct) Clear() { // Store the entry in the cache. The passed "fd" will be Dup()ed, and the caller // can close their copy at will. func (d *dirCacheStruct) Store(dirRelPath string, fd int, iv []byte) { + // Note: package ensurefds012, imported from main, guarantees that dirCache + // can never get fds 0,1,2. if fd <= 0 || len(iv) != nametransform.DirIVLen { log.Panicf("Store sanity check failed: fd=%d len=%d", fd, len(iv)) } -- cgit v1.2.3