aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--internal/ctlsocksrv/ctlsock_listen.go44
-rw-r--r--internal/fusefrontend_reverse/node.go4
-rw-r--r--internal/fusefrontend_reverse/node_helpers.go3
-rw-r--r--internal/fusefrontend_reverse/virtualconf.go9
-rw-r--r--internal/fusefrontend_reverse/virtualnode.go4
-rw-r--r--mount.go8
-rw-r--r--tests/cli/cli_test.go37
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
}
diff --git a/mount.go b/mount.go
index ea0281a..61079a9 100644
--- a/mount.go
+++ b/mount.go
@@ -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)
+}