First pass at implementing Unix Domain Socket support. No tests.

This commit adds support for detecting the socket file type, connecting 
to a Unix Domain Socket, and providing bidirectional communication 
(without file descriptor transfer support).
This commit is contained in:
Robert Tonic
2019-08-27 13:08:56 -04:00
parent b4cdaef4a1
commit c319b360d1
3 changed files with 96 additions and 21 deletions
+16
View File
@@ -22,6 +22,7 @@ import (
"runtime"
"sync/atomic"
"syscall"
"net"
)
// ReadWriter implements io.ReadWriter, io.ReaderAt, and io.WriterAt for fd. It
@@ -185,6 +186,21 @@ func OpenAt(dir *FD, path string, flags int, mode uint32) (*FD, error) {
return New(f), nil
}
// OpenUnix TODO: DOC
func OpenUnix(path string) (*FD, error) {
addr, _ := net.ResolveUnixAddr("unix", path)
f, err := net.DialUnix("unix", nil, addr); if err != nil {
return nil, fmt.Errorf("unable to open socket: %q, err: %v", path, err)
}
fConnd, err := f.File(); if err != nil {
return nil, fmt.Errorf("unable to convert to os.File: %q, err: %v", path, err)
}
fdConnd, err := NewFromFile(fConnd); if err != nil {
return nil, fmt.Errorf("unable to convert os.File to fd.FD: %q, err: %v", path, err)
}
return fdConnd, nil
}
// Close closes the file descriptor contained in the FD.
//
// Close is safe to call multiple times, but will return an error after the
+28
View File
@@ -26,6 +26,31 @@ import (
// allowedSyscalls is the set of syscalls executed by the gofer.
var allowedSyscalls = seccomp.SyscallRules{
syscall.SYS_ACCEPT: {},
syscall.SYS_SOCKET: []seccomp.Rule{
{
seccomp.AllowValue(syscall.AF_UNIX),
},
},
syscall.SYS_CONNECT: []seccomp.Rule{
{
seccomp.AllowAny{},
},
},
syscall.SYS_SETSOCKOPT: []seccomp.Rule{
{
seccomp.AllowAny{},
},
},
syscall.SYS_GETSOCKNAME: []seccomp.Rule{
{
seccomp.AllowAny{},
},
},
syscall.SYS_GETPEERNAME: []seccomp.Rule{
{
seccomp.AllowAny{},
},
},
syscall.SYS_ARCH_PRCTL: []seccomp.Rule{
{seccomp.AllowValue(linux.ARCH_GET_FS)},
{seccomp.AllowValue(linux.ARCH_SET_FS)},
@@ -83,6 +108,9 @@ var allowedSyscalls = seccomp.SyscallRules{
seccomp.AllowAny{},
seccomp.AllowValue(syscall.F_GETFD),
},
{
seccomp.AllowAny{},
},
},
syscall.SYS_FSTAT: {},
syscall.SYS_FSTATFS: {},
+52 -21
View File
@@ -54,6 +54,7 @@ const (
regular fileType = iota
directory
symlink
socket
unknown
)
@@ -66,6 +67,8 @@ func (f fileType) String() string {
return "directory"
case symlink:
return "symlink"
case socket:
return "socket"
}
return "unknown"
}
@@ -124,30 +127,56 @@ func (a *attachPoint) Attach() (p9.File, error) {
if err != nil {
return nil, fmt.Errorf("stat file %q, err: %v", a.prefix, err)
}
mode := syscall.O_RDWR
if a.conf.ROMount || (stat.Mode&syscall.S_IFMT) == syscall.S_IFDIR {
mode = syscall.O_RDONLY
}
// Open the root directory.
f, err := fd.Open(a.prefix, openFlags|mode, 0)
if err != nil {
return nil, fmt.Errorf("unable to open file %q, err: %v", a.prefix, err)
}
// Apply the S_IFMT bitmask so we can detect file type appropriately
fmtStat := stat.Mode & syscall.S_IFMT
a.attachedMu.Lock()
defer a.attachedMu.Unlock()
if a.attached {
f.Close()
return nil, fmt.Errorf("attach point already attached, prefix: %s", a.prefix)
}
switch fmtStat{
case syscall.S_IFSOCK:
// Attempt to open a connection. Bubble up the failures.
f, err := fd.OpenUnix(a.prefix); if err != nil {
return nil, err
}
rv, err := newLocalFile(a, f, a.prefix, stat)
if err != nil {
return nil, err
// Close the connection if the UDS is already attached.
a.attachedMu.Lock()
defer a.attachedMu.Unlock()
if a.attached {
f.Close()
return nil, fmt.Errorf("attach point already attached, prefix: %s", a.prefix)
}
a.attached = true
// Return a localFile object to the caller with the UDS FD included.
return newLocalFile(a, f, a.prefix, stat)
default:
// Default to Read/Write permissions.
mode := syscall.O_RDWR
// If the configuration is Read Only & the mount point is a directory,
// set the mode to Read Only.
if a.conf.ROMount || fmtStat == syscall.S_IFDIR {
mode = syscall.O_RDONLY
}
// Open the mount point & capture the FD.
f, err := fd.Open(a.prefix, openFlags|mode, 0)
if err != nil {
return nil, fmt.Errorf("unable to open file %q, err: %v", a.prefix, err)
}
// If the mount point has already been attached, close the FD.
a.attachedMu.Lock()
defer a.attachedMu.Unlock()
if a.attached {
f.Close()
return nil, fmt.Errorf("attach point already attached, prefix: %s", a.prefix)
}
a.attached = true
// Return a localFile object to the caller with the mount point FD
return newLocalFile(a, f, a.prefix, stat)
}
a.attached = true
return rv, nil
}
// makeQID returns a unique QID for the given stat buffer.
@@ -304,6 +333,8 @@ func getSupportedFileType(stat syscall.Stat_t) (fileType, error) {
ft = directory
case syscall.S_IFLNK:
ft = symlink
case syscall.S_IFSOCK:
ft = socket
default:
return unknown, syscall.EPERM
}
@@ -1026,7 +1057,7 @@ func (l *localFile) Flush() error {
// Connect implements p9.File.
func (l *localFile) Connect(p9.ConnectFlags) (*fd.FD, error) {
return nil, syscall.ECONNREFUSED
return fd.OpenUnix(l.hostPath)
}
// Close implements p9.File.