diff options
-rw-r--r-- | internal/ctlsocksrv/ctlsock_listen.go | 44 | ||||
-rw-r--r-- | internal/fusefrontend_reverse/node.go | 4 | ||||
-rw-r--r-- | internal/fusefrontend_reverse/node_helpers.go | 3 | ||||
-rw-r--r-- | internal/fusefrontend_reverse/virtualconf.go | 9 | ||||
-rw-r--r-- | internal/fusefrontend_reverse/virtualnode.go | 4 | ||||
-rw-r--r-- | mount.go | 8 | ||||
-rw-r--r-- | tests/cli/cli_test.go | 37 |
7 files changed, 103 insertions, 6 deletions
diff --git a/internal/ctlsocksrv/ctlsock_listen.go b/internal/ctlsocksrv/ctlsock_listen.go new file mode 100644 index 0000000..94ffca4 --- /dev/null +++ b/internal/ctlsocksrv/ctlsock_listen.go @@ -0,0 +1,44 @@ +package ctlsocksrv + +import ( + "errors" + "io/fs" + "net" + "os" + "syscall" + "time" + + "github.com/rfjakob/gocryptfs/v2/internal/tlog" +) + +// cleanupOrphanedSocket deletes an orphaned socket file at `path`. +// The file at `path` will only be deleted if: +// 1) It is a socket file +// 2) Connecting to it results in ECONNREFUSED +func cleanupOrphanedSocket(path string) { + fi, err := os.Stat(path) + if err != nil { + return + } + if fi.Mode().Type() != fs.ModeSocket { + return + } + conn, err := net.DialTimeout("unix", path, time.Second) + if err == nil { + // This socket file is still active. Don't delete it. + conn.Close() + return + } + if errors.Is(err, syscall.ECONNREFUSED) { + tlog.Info.Printf("ctlsock: deleting orphaned socket file %q\n", path) + err = os.Remove(path) + if err != nil { + tlog.Warn.Printf("ctlsock: deleting socket file failed: %v", path) + } + } +} + +func Listen(path string) (net.Listener, error) { + cleanupOrphanedSocket(path) + return net.Listen("unix", path) +} diff --git a/internal/fusefrontend_reverse/node.go b/internal/fusefrontend_reverse/node.go index 22ad975..30654e0 100644 --- a/internal/fusefrontend_reverse/node.go +++ b/internal/fusefrontend_reverse/node.go @@ -69,6 +69,10 @@ func (n *Node) Lookup(ctx context.Context, cName string, out *fuse.EntryOut) (ch n.translateSize(d.dirfd, cName, d.pName, &out.Attr) } + if rn.args.ForceOwner != nil { + out.Owner = *rn.args.ForceOwner + } + // Usually we always create a new Node ID by always incrementing the generation // number. // diff --git a/internal/fusefrontend_reverse/node_helpers.go b/internal/fusefrontend_reverse/node_helpers.go index 6bba097..30361bc 100644 --- a/internal/fusefrontend_reverse/node_helpers.go +++ b/internal/fusefrontend_reverse/node_helpers.go @@ -201,6 +201,9 @@ func (n *Node) lookupConf(ctx context.Context, out *fuse.EntryOut) (ch *fs.Inode // Get unique inode number rn.inoMap.TranslateStat(&st) out.Attr.FromStat(&st) + if rn.args.ForceOwner != nil { + out.Owner = *rn.args.ForceOwner + } // Create child node id := rn.uniqueStableAttr(uint32(st.Mode), st.Ino) node := &VirtualConfNode{path: p} diff --git a/internal/fusefrontend_reverse/virtualconf.go b/internal/fusefrontend_reverse/virtualconf.go index 3643fad..ea358dd 100644 --- a/internal/fusefrontend_reverse/virtualconf.go +++ b/internal/fusefrontend_reverse/virtualconf.go @@ -18,6 +18,11 @@ type VirtualConfNode struct { path string } +// rootNode returns the Root Node of the filesystem. +func (n *VirtualConfNode) rootNode() *RootNode { + return n.Root().Operations().(*RootNode) +} + func (n *VirtualConfNode) Open(ctx context.Context, flags uint32) (fh fs.FileHandle, fuseFlags uint32, errno syscall.Errno) { fd, err := syscall.Open(n.path, syscall.O_RDONLY, 0) if err != nil { @@ -35,6 +40,10 @@ func (n *VirtualConfNode) Getattr(ctx context.Context, fh fs.FileHandle, out *fu return fs.ToErrno(err) } out.FromStat(&st) + rn := n.rootNode() + if rn.args.ForceOwner != nil { + out.Owner = *rn.args.ForceOwner + } return 0 } diff --git a/internal/fusefrontend_reverse/virtualnode.go b/internal/fusefrontend_reverse/virtualnode.go index 922cfa7..732564a 100644 --- a/internal/fusefrontend_reverse/virtualnode.go +++ b/internal/fusefrontend_reverse/virtualnode.go @@ -100,7 +100,9 @@ func (n *Node) newVirtualMemNode(content []byte, parentStat *syscall.Stat_t, ino st.Nlink = 1 var a fuse.Attr a.FromStat(st) - + if rn.args.ForceOwner != nil { + a.Owner = *rn.args.ForceOwner + } vf = &VirtualMemNode{content: content, attr: a} return } @@ -5,7 +5,6 @@ import ( "log" "log/syslog" "math" - "net" "os" "os/exec" "os/signal" @@ -91,16 +90,15 @@ func doMount(args *argContainer) { // We must use an absolute path because we cd to / when daemonizing. // This messes up the delete-on-close logic in the unix socket object. args.ctlsock, _ = filepath.Abs(args.ctlsock) - var sock net.Listener - sock, err = net.Listen("unix", args.ctlsock) + + args._ctlsockFd, err = ctlsocksrv.Listen(args.ctlsock) if err != nil { tlog.Fatal.Printf("ctlsock: %v", err) os.Exit(exitcodes.CtlSock) } - args._ctlsockFd = sock // Close also deletes the socket file defer func() { - err = sock.Close() + err = args._ctlsockFd.Close() if err != nil { tlog.Warn.Printf("ctlsock close: %v", err) } diff --git a/tests/cli/cli_test.go b/tests/cli/cli_test.go index 686d14c..b2f9f40 100644 --- a/tests/cli/cli_test.go +++ b/tests/cli/cli_test.go @@ -4,6 +4,7 @@ package cli import ( "bytes" "encoding/hex" + "errors" "fmt" "io/ioutil" "os" @@ -1033,3 +1034,39 @@ func TestMountCreat(t *testing.T) { test_helpers.UnmountPanic(mnt) } } + +// https://github.com/rfjakob/gocryptfs/issues/776 +func TestOrphanedSocket(t *testing.T) { + cDir := test_helpers.InitFS(t) + ctlSock := cDir + ".sock" + mnt := cDir + ".mnt" + test_helpers.MountOrFatal(t, cDir, mnt, "-extpass", "echo test", "-wpanic=false", "-ctlsock", ctlSock) + + mnt2 := cDir + ".mnt2" + err := test_helpers.Mount(cDir, mnt2, false, "-extpass", "echo test", "-wpanic=false", "-ctlsock", ctlSock) + exitCode := test_helpers.ExtractCmdExitCode(err) + if exitCode != exitcodes.CtlSock { + t.Errorf("wrong exit code: want=%d, have=%d", exitcodes.CtlSock, exitCode) + } + test_helpers.UnmountPanic(mnt) + + // Unmount returns before the gocryptfs process has terminated and before the + // socket file has been deleted. Wait out the deletion. + for i := 0; i < 100; i++ { + _, err := os.Stat(ctlSock) + if errors.Is(err, os.ErrNotExist) { + break + } + time.Sleep(time.Millisecond) + } + + // Create orphaned socket file + err = syscall.Mknod(ctlSock, syscall.S_IFSOCK|0666, 0) + if err != nil { + t.Fatal(err) + } + + // Should delete the socket file automatically and the mount should work + test_helpers.MountOrFatal(t, cDir, mnt, "-extpass", "echo test", "-wpanic=false", "-ctlsock", ctlSock) + test_helpers.UnmountPanic(mnt) +} |