Files
Zoey GreerandHan-Wen Nienhuys 34ff8fc355 fuse: reserve fd without opening /dev/null
This enables using go-fuse in a init binary that is spawned directly from
the kernel.

See also https://github.com/hanwen/go-fuse/pull/525.

Change-Id: Ibbaa04af993677536bd4ae8a173a5a6d93118940
2024-08-16 14:06:07 +02:00

73 lines
1.5 KiB
Go

package fuse
import (
"fmt"
"net"
"os"
"syscall"
)
var reservedFDs []*os.File
func init() {
// Both Darwin and Linux invoke a subprocess with one
// inherited file descriptor to create the mount. To protect
// against deadlock, we must ensure that file descriptor 3
// never points to a FUSE filesystem. We do this by simply
// grabbing fd 3 and never releasing it. (This is not
// completely foolproof: a preceding init routine could grab fd 3,
// and then release it later.)
for {
r, w, err := os.Pipe()
if err != nil {
panic(fmt.Sprintf("os.Pipe(): %v", err))
}
w.Close()
if r.Fd() > 3 {
r.Close()
break
}
reservedFDs = append(reservedFDs, r)
}
}
func getConnection(local *os.File) (int, error) {
conn, err := net.FileConn(local)
if err != nil {
return 0, err
}
defer conn.Close()
unixConn := conn.(*net.UnixConn)
var data [4]byte
control := make([]byte, 4*256)
_, oobn, _, _, err := unixConn.ReadMsgUnix(data[:], control[:])
if err != nil {
return 0, err
}
messages, err := syscall.ParseSocketControlMessage(control[:oobn])
if err != nil {
return 0, err
}
if len(messages) != 1 {
return 0, fmt.Errorf("getConnection: expect 1 control message, got %#v", messages)
}
message := messages[0]
fds, err := syscall.ParseUnixRights(&message)
if err != nil {
return 0, err
}
if len(fds) != 1 {
return 0, fmt.Errorf("getConnection: expect 1 fd, got %#v", fds)
}
fd := fds[0]
if fd < 0 {
return 0, fmt.Errorf("getConnection: fd < 0: %d", fd)
}
return fd, nil
}