summaryrefslogtreecommitdiff
path: root/src/syscall/srpc_nacl.go
diff options
context:
space:
mode:
authorTianon Gravi <admwiggin@gmail.com>2015-01-15 12:50:40 -0700
committerTianon Gravi <admwiggin@gmail.com>2015-01-15 12:50:40 -0700
commit2a0db60599fdd75b1bc3e297180fbe1282763759 (patch)
tree68d43c3e30d9ab961ddf6b7365201ca6b675b253 /src/syscall/srpc_nacl.go
parentef33cba3c8de6c431df56503df51fcd3a473c89e (diff)
parentf154da9e12608589e8d5f0508f908a0c3e88a1bb (diff)
downloadgolang-2a0db60599fdd75b1bc3e297180fbe1282763759.tar.gz
Merge tag 'upstream/1.4' into debian-experimental
* tag 'upstream/1.4': Imported Upstream version 1.4
Diffstat (limited to 'src/syscall/srpc_nacl.go')
-rw-r--r--src/syscall/srpc_nacl.go822
1 files changed, 822 insertions, 0 deletions
diff --git a/src/syscall/srpc_nacl.go b/src/syscall/srpc_nacl.go
new file mode 100644
index 000000000..dd07373d1
--- /dev/null
+++ b/src/syscall/srpc_nacl.go
@@ -0,0 +1,822 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Native Client SRPC message passing.
+// This code is needed to invoke SecureRandom, the NaCl equivalent of /dev/random.
+
+package syscall
+
+import (
+ "errors"
+ "sync"
+ "unsafe"
+)
+
+// An srpcClient represents the client side of an SRPC connection.
+type srpcClient struct {
+ fd int // to server
+ r msgReceiver
+ s msgSender
+ service map[string]srpcService // services by name
+
+ outMu sync.Mutex // protects writing to connection
+
+ mu sync.Mutex // protects following fields
+ muxer bool // is someone reading and muxing responses
+ pending map[uint32]*srpc
+ idGen uint32 // generator for request IDs
+}
+
+// An srpcService is a single method that the server offers.
+type srpcService struct {
+ num uint32 // method number
+ fmt string // argument format; see "parsing of RPC messages" below
+}
+
+// An srpc represents a single srpc issued by a client.
+type srpc struct {
+ Ret []interface{}
+ Done chan *srpc
+ Err error
+ c *srpcClient
+ id uint32
+}
+
+// newClient allocates a new SRPC client using the file descriptor fd.
+func newClient(fd int) (*srpcClient, error) {
+ c := new(srpcClient)
+ c.fd = fd
+ c.r.fd = fd
+ c.s.fd = fd
+ c.service = make(map[string]srpcService)
+ c.pending = make(map[uint32]*srpc)
+
+ // service discovery request
+ m := &msg{
+ isRequest: 1,
+ template: []interface{}{[]byte(nil)},
+ size: []int{4000}, // max size to accept for returned byte slice
+ }
+ if err := m.pack(); err != nil {
+ return nil, errors.New("Native Client SRPC service_discovery: preparing request: " + err.Error())
+ }
+ c.s.send(m)
+ m, err := c.r.recv()
+ if err != nil {
+ return nil, err
+ }
+ m.unpack()
+ if m.status != uint32(srpcOK) {
+ return nil, errors.New("Native Client SRPC service_discovery: " + srpcErrno(m.status).Error())
+ }
+ list := m.value[0].([]byte)
+ var n uint32
+ for len(list) > 0 {
+ var line []byte
+ i := byteIndex(list, '\n')
+ if i < 0 {
+ line, list = list, nil
+ } else {
+ line, list = list[:i], list[i+1:]
+ }
+ i = byteIndex(line, ':')
+ if i >= 0 {
+ c.service[string(line)] = srpcService{n, string(line[i+1:])}
+ }
+ n++
+ }
+
+ return c, nil
+}
+
+func byteIndex(b []byte, c byte) int {
+ for i, bi := range b {
+ if bi == c {
+ return i
+ }
+ }
+ return -1
+}
+
+var yourTurn srpc
+
+func (c *srpcClient) wait(r *srpc) {
+ var rx *srpc
+ for rx = range r.Done {
+ if rx != &yourTurn {
+ break
+ }
+ c.input()
+ }
+ return
+}
+
+func (c *srpcClient) input() {
+ // read message
+ m, err := c.r.recv()
+ if err != nil {
+ println("Native Client SRPC receive error:", err.Error())
+ return
+ }
+ if m.unpack(); m.status != uint32(srpcOK) {
+ println("Native Client SRPC receive error: invalid message: ", srpcErrno(m.status).Error())
+ return
+ }
+
+ // deliver to intended recipient
+ c.mu.Lock()
+ rpc, ok := c.pending[m.id]
+ if ok {
+ delete(c.pending, m.id)
+ }
+
+ // wake a new muxer if there are more RPCs to read
+ c.muxer = false
+ for _, rpc := range c.pending {
+ c.muxer = true
+ rpc.Done <- &yourTurn
+ break
+ }
+ c.mu.Unlock()
+ if !ok {
+ println("Native Client: unexpected response for ID", m.id)
+ return
+ }
+ rpc.Ret = m.value
+ rpc.Done <- rpc
+}
+
+// Wait blocks until the RPC has finished.
+func (r *srpc) Wait() {
+ r.c.wait(r)
+}
+
+// Start issues an RPC request for method name with the given arguments.
+// The RPC r must not be in use for another pending request.
+// To wait for the RPC to finish, receive from r.Done and then
+// inspect r.Ret and r.Errno.
+func (r *srpc) Start(name string, arg []interface{}) {
+ r.Err = nil
+ r.c.mu.Lock()
+ srv, ok := r.c.service[name]
+ if !ok {
+ r.c.mu.Unlock()
+ r.Err = srpcErrBadRPCNumber
+ r.Done <- r
+ return
+ }
+ r.c.pending[r.id] = r
+ if !r.c.muxer {
+ r.c.muxer = true
+ r.Done <- &yourTurn
+ }
+ r.c.mu.Unlock()
+
+ var m msg
+ m.id = r.id
+ m.isRequest = 1
+ m.rpc = srv.num
+ m.value = arg
+
+ // Fill in the return values and sizes to generate
+ // the right type chars. We'll take most any size.
+
+ // Skip over input arguments.
+ // We could check them against arg, but the server
+ // will do that anyway.
+ i := 0
+ for srv.fmt[i] != ':' {
+ i++
+ }
+ format := srv.fmt[i+1:]
+
+ // Now the return prototypes.
+ m.template = make([]interface{}, len(format))
+ m.size = make([]int, len(format))
+ for i := 0; i < len(format); i++ {
+ switch format[i] {
+ default:
+ println("Native Client SRPC: unexpected service type " + string(format[i]))
+ r.Err = srpcErrBadRPCNumber
+ r.Done <- r
+ return
+ case 'b':
+ m.template[i] = false
+ case 'C':
+ m.template[i] = []byte(nil)
+ m.size[i] = 1 << 30
+ case 'd':
+ m.template[i] = float64(0)
+ case 'D':
+ m.template[i] = []float64(nil)
+ m.size[i] = 1 << 30
+ case 'h':
+ m.template[i] = int(-1)
+ case 'i':
+ m.template[i] = int32(0)
+ case 'I':
+ m.template[i] = []int32(nil)
+ m.size[i] = 1 << 30
+ case 's':
+ m.template[i] = ""
+ m.size[i] = 1 << 30
+ }
+ }
+
+ if err := m.pack(); err != nil {
+ r.Err = errors.New("Native Client RPC Start " + name + ": preparing request: " + err.Error())
+ r.Done <- r
+ return
+ }
+
+ r.c.outMu.Lock()
+ r.c.s.send(&m)
+ r.c.outMu.Unlock()
+}
+
+// Call is a convenience wrapper that starts the RPC request,
+// waits for it to finish, and then returns the results.
+// Its implementation is:
+//
+// r.Start(name, arg)
+// r.Wait()
+// return r.Ret, r.Errno
+//
+func (c *srpcClient) Call(name string, arg ...interface{}) (ret []interface{}, err error) {
+ r := c.NewRPC(nil)
+ r.Start(name, arg)
+ r.Wait()
+ return r.Ret, r.Err
+}
+
+// NewRPC creates a new RPC on the client connection.
+func (c *srpcClient) NewRPC(done chan *srpc) *srpc {
+ if done == nil {
+ done = make(chan *srpc, 1)
+ }
+ c.mu.Lock()
+ id := c.idGen
+ c.idGen++
+ c.mu.Unlock()
+ return &srpc{Done: done, c: c, id: id}
+}
+
+// The current protocol number.
+// Kind of useless, since there have been backwards-incompatible changes
+// to the wire protocol that did not update the protocol number.
+// At this point it's really just a sanity check.
+const protocol = 0xc0da0002
+
+// An srpcErrno is an SRPC status code.
+type srpcErrno uint32
+
+const (
+ srpcOK srpcErrno = 256 + iota
+ srpcErrBreak
+ srpcErrMessageTruncated
+ srpcErrNoMemory
+ srpcErrProtocolMismatch
+ srpcErrBadRPCNumber
+ srpcErrBadArgType
+ srpcErrTooFewArgs
+ srpcErrTooManyArgs
+ srpcErrInArgTypeMismatch
+ srpcErrOutArgTypeMismatch
+ srpcErrInternalError
+ srpcErrAppError
+)
+
+var srpcErrstr = [...]string{
+ srpcOK - srpcOK: "ok",
+ srpcErrBreak - srpcOK: "break",
+ srpcErrMessageTruncated - srpcOK: "message truncated",
+ srpcErrNoMemory - srpcOK: "out of memory",
+ srpcErrProtocolMismatch - srpcOK: "protocol mismatch",
+ srpcErrBadRPCNumber - srpcOK: "invalid RPC method number",
+ srpcErrBadArgType - srpcOK: "unexpected argument type",
+ srpcErrTooFewArgs - srpcOK: "too few arguments",
+ srpcErrTooManyArgs - srpcOK: "too many arguments",
+ srpcErrInArgTypeMismatch - srpcOK: "input argument type mismatch",
+ srpcErrOutArgTypeMismatch - srpcOK: "output argument type mismatch",
+ srpcErrInternalError - srpcOK: "internal error",
+ srpcErrAppError - srpcOK: "application error",
+}
+
+func (e srpcErrno) Error() string {
+ if e < srpcOK || int(e-srpcOK) >= len(srpcErrstr) {
+ return "srpcErrno(" + itoa(int(e)) + ")"
+ }
+ return srpcErrstr[e-srpcOK]
+}
+
+// A msgHdr is the data argument to the imc_recvmsg
+// and imc_sendmsg system calls.
+type msgHdr struct {
+ iov *iov
+ niov int32
+ desc *int32
+ ndesc int32
+ flags uint32
+}
+
+// A single region for I/O.
+type iov struct {
+ base *byte
+ len int32
+}
+
+const maxMsgSize = 1<<16 - 4*4
+
+// A msgReceiver receives messages from a file descriptor.
+type msgReceiver struct {
+ fd int
+ data [maxMsgSize]byte
+ desc [8]int32
+ hdr msgHdr
+ iov iov
+}
+
+func (r *msgReceiver) recv() (*msg, error) {
+ // Init pointers to buffers where syscall recvmsg can write.
+ r.iov.base = &r.data[0]
+ r.iov.len = int32(len(r.data))
+ r.hdr.iov = &r.iov
+ r.hdr.niov = 1
+ r.hdr.desc = &r.desc[0]
+ r.hdr.ndesc = int32(len(r.desc))
+ n, _, e := Syscall(sys_imc_recvmsg, uintptr(r.fd), uintptr(unsafe.Pointer(&r.hdr)), 0)
+ if e != 0 {
+ println("Native Client imc_recvmsg: ", e.Error())
+ return nil, e
+ }
+
+ // Make a copy of the data so that the next recvmsg doesn't
+ // smash it. The system call did not update r.iov.len. Instead it
+ // returned the total byte count as n.
+ m := new(msg)
+ m.data = make([]byte, n)
+ copy(m.data, r.data[0:])
+
+ // Make a copy of the desc too.
+ // The system call *did* update r.hdr.ndesc.
+ if r.hdr.ndesc > 0 {
+ m.desc = make([]int32, r.hdr.ndesc)
+ copy(m.desc, r.desc[:])
+ }
+
+ return m, nil
+}
+
+// A msgSender sends messages on a file descriptor.
+type msgSender struct {
+ fd int
+ hdr msgHdr
+ iov iov
+}
+
+func (s *msgSender) send(m *msg) error {
+ if len(m.data) > 0 {
+ s.iov.base = &m.data[0]
+ }
+ s.iov.len = int32(len(m.data))
+ s.hdr.iov = &s.iov
+ s.hdr.niov = 1
+ s.hdr.desc = nil
+ s.hdr.ndesc = 0
+ _, _, e := Syscall(sys_imc_sendmsg, uintptr(s.fd), uintptr(unsafe.Pointer(&s.hdr)), 0)
+ if e != 0 {
+ println("Native Client imc_sendmsg: ", e.Error())
+ return e
+ }
+ return nil
+}
+
+// A msg is the Go representation of an SRPC message.
+type msg struct {
+ data []byte // message data
+ desc []int32 // message file descriptors
+
+ // parsed version of message
+ id uint32
+ isRequest uint32
+ rpc uint32
+ status uint32
+ value []interface{}
+ template []interface{}
+ size []int
+ format string
+ broken bool
+}
+
+// reading from a msg
+
+func (m *msg) uint32() uint32 {
+ if m.broken {
+ return 0
+ }
+ if len(m.data) < 4 {
+ m.broken = true
+ return 0
+ }
+ b := m.data[:4]
+ x := uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24
+ m.data = m.data[4:]
+ return x
+}
+
+func (m *msg) uint64() uint64 {
+ x := uint64(m.uint32()) | uint64(m.uint32())<<32
+ if m.broken {
+ return 0
+ }
+ return x
+}
+
+func (m *msg) bytes(n int) []byte {
+ if m.broken {
+ return nil
+ }
+ if len(m.data) < n {
+ m.broken = true
+ return nil
+ }
+ x := m.data[0:n]
+ m.data = m.data[n:]
+ return x
+}
+
+// writing to a msg
+
+func (m *msg) wuint32(x uint32) {
+ m.data = append(m.data, byte(x), byte(x>>8), byte(x>>16), byte(x>>24))
+}
+
+func (m *msg) wuint64(x uint64) {
+ lo := uint32(x)
+ hi := uint32(x >> 32)
+ m.data = append(m.data, byte(lo), byte(lo>>8), byte(lo>>16), byte(lo>>24), byte(hi), byte(hi>>8), byte(hi>>16), byte(hi>>24))
+}
+
+func (m *msg) wbytes(p []byte) {
+ m.data = append(m.data, p...)
+}
+
+func (m *msg) wstring(s string) {
+ m.data = append(m.data, s...)
+}
+
+// Parsing of RPC messages.
+//
+// Each message begins with
+// total_size uint32
+// total_descs uint32
+// fragment_size uint32
+// fragment_descs uint32
+//
+// If fragment_size < total_size or fragment_descs < total_descs, the actual
+// message is broken up in multiple messages; follow-up messages omit
+// the "total" fields and begin with the "fragment" fields.
+// We do not support putting fragmented messages back together.
+// To do this we would need to change the message receiver.
+//
+// After that size information, the message header follows:
+// protocol uint32
+// requestID uint32
+// isRequest uint32
+// rpcNumber uint32
+// status uint32
+// numValue uint32
+// numTemplate uint32
+//
+// After the header come numTemplate fixed-size arguments,
+// numValue fixed-size arguments, and then the variable-sized
+// part of the values. The templates describe the expected results
+// and have no associated variable sized data in the request.
+//
+// Each fixed-size argument has the form:
+// tag uint32 // really a char, like 'b' or 'C'
+// pad uint32 // unused
+// val1 uint32
+// val2 uint32
+//
+// The tags are:
+// 'b': bool; val1 == 0 or 1
+// 'C': []byte; val1 == len, data in variable-sized section
+// 'd': float64; (val1, val2) is data
+// 'D': []float64; val1 == len, data in variable-sized section
+// 'h': int; val1 == file descriptor
+// 'i': int32; descriptor in next entry in m.desc
+// 'I': []int; val1 == len, data in variable-sized section
+// 's': string; val1 == len, data in variable-sized section
+//
+
+func (m *msg) pack() error {
+ m.data = m.data[:0]
+ m.desc = m.desc[:0]
+
+ // sizes, to fill in later
+ m.wuint32(0)
+ m.wuint32(0)
+ m.wuint32(0)
+ m.wuint32(0)
+
+ // message header
+ m.wuint32(protocol)
+ m.wuint32(m.id)
+ m.wuint32(m.isRequest)
+ m.wuint32(m.rpc)
+ m.wuint32(m.status)
+ m.wuint32(uint32(len(m.value)))
+ m.wuint32(uint32(len(m.template)))
+
+ // fixed-size templates
+ for i, x := range m.template {
+ var tag, val1, val2 uint32
+ switch x.(type) {
+ default:
+ return errors.New("unexpected template type")
+ case bool:
+ tag = 'b'
+ case []byte:
+ tag = 'C'
+ val1 = uint32(m.size[i])
+ case float64:
+ tag = 'd'
+ case []float64:
+ tag = 'D'
+ val1 = uint32(m.size[i])
+ case int:
+ tag = 'h'
+ case int32:
+ tag = 'i'
+ case []int32:
+ tag = 'I'
+ val1 = uint32(m.size[i])
+ case string:
+ tag = 's'
+ val1 = uint32(m.size[i])
+ }
+ m.wuint32(tag)
+ m.wuint32(0)
+ m.wuint32(val1)
+ m.wuint32(val2)
+ }
+
+ // fixed-size values
+ for _, x := range m.value {
+ var tag, val1, val2 uint32
+ switch x := x.(type) {
+ default:
+ return errors.New("unexpected value type")
+ case bool:
+ tag = 'b'
+ if x {
+ val1 = 1
+ }
+ case []byte:
+ tag = 'C'
+ val1 = uint32(len(x))
+ case float64:
+ tag = 'd'
+ v := float64bits(x)
+ val1 = uint32(v)
+ val2 = uint32(v >> 32)
+ case []float64:
+ tag = 'D'
+ val1 = uint32(len(x))
+ case int32:
+ tag = 'i'
+ m.desc = append(m.desc, x)
+ case []int32:
+ tag = 'I'
+ val1 = uint32(len(x))
+ case string:
+ tag = 's'
+ val1 = uint32(len(x) + 1)
+ }
+ m.wuint32(tag)
+ m.wuint32(0)
+ m.wuint32(val1)
+ m.wuint32(val2)
+ }
+
+ // variable-length data for values
+ for _, x := range m.value {
+ switch x := x.(type) {
+ case []byte:
+ m.wbytes(x)
+ case []float64:
+ for _, f := range x {
+ m.wuint64(float64bits(f))
+ }
+ case []int32:
+ for _, j := range x {
+ m.wuint32(uint32(j))
+ }
+ case string:
+ m.wstring(x)
+ m.wstring("\x00")
+ }
+ }
+
+ // fill in sizes
+ data := m.data
+ m.data = m.data[:0]
+ m.wuint32(uint32(len(data)))
+ m.wuint32(uint32(len(m.desc)))
+ m.wuint32(uint32(len(data)))
+ m.wuint32(uint32(len(m.desc)))
+ m.data = data
+
+ return nil
+}
+
+func (m *msg) unpack() error {
+ totalSize := m.uint32()
+ totalDesc := m.uint32()
+ fragSize := m.uint32()
+ fragDesc := m.uint32()
+ if totalSize != fragSize || totalDesc != fragDesc {
+ return errors.New("Native Client: fragmented RPC messages not supported")
+ }
+ if m.uint32() != protocol {
+ return errors.New("Native Client: RPC protocol mismatch")
+ }
+
+ // message header
+ m.id = m.uint32()
+ m.isRequest = m.uint32()
+ m.rpc = m.uint32()
+ m.status = m.uint32()
+ m.value = make([]interface{}, m.uint32())
+ m.template = make([]interface{}, m.uint32())
+ m.size = make([]int, len(m.template))
+ if m.broken {
+ return errors.New("Native Client: malformed message")
+ }
+
+ // fixed-size templates
+ for i := range m.template {
+ tag := m.uint32()
+ m.uint32() // padding
+ val1 := m.uint32()
+ m.uint32() // val2
+ switch tag {
+ default:
+ return errors.New("Native Client: unexpected template type " + string(rune(tag)))
+ case 'b':
+ m.template[i] = false
+ case 'C':
+ m.template[i] = []byte(nil)
+ m.size[i] = int(val1)
+ case 'd':
+ m.template[i] = float64(0)
+ case 'D':
+ m.template[i] = []float64(nil)
+ m.size[i] = int(val1)
+ case 'i':
+ m.template[i] = int32(0)
+ case 'I':
+ m.template[i] = []int32(nil)
+ m.size[i] = int(val1)
+ case 'h':
+ m.template[i] = int(0)
+ case 's':
+ m.template[i] = ""
+ m.size[i] = int(val1)
+ }
+ }
+
+ // fixed-size values
+ var (
+ strsize []uint32
+ d int
+ )
+ for i := range m.value {
+ tag := m.uint32()
+ m.uint32() // padding
+ val1 := m.uint32()
+ val2 := m.uint32()
+ switch tag {
+ default:
+ return errors.New("Native Client: unexpected value type " + string(rune(tag)))
+ case 'b':
+ m.value[i] = val1 > 0
+ case 'C':
+ m.value[i] = []byte(nil)
+ strsize = append(strsize, val1)
+ case 'd':
+ m.value[i] = float64frombits(uint64(val1) | uint64(val2)<<32)
+ case 'D':
+ m.value[i] = make([]float64, val1)
+ case 'i':
+ m.value[i] = int32(val1)
+ case 'I':
+ m.value[i] = make([]int32, val1)
+ case 'h':
+ m.value[i] = int(m.desc[d])
+ d++
+ case 's':
+ m.value[i] = ""
+ strsize = append(strsize, val1)
+ }
+ }
+
+ // variable-sized parts of values
+ for i, x := range m.value {
+ switch x := x.(type) {
+ case []byte:
+ m.value[i] = m.bytes(int(strsize[0]))
+ strsize = strsize[1:]
+ case []float64:
+ for i := range x {
+ x[i] = float64frombits(m.uint64())
+ }
+ case []int32:
+ for i := range x {
+ x[i] = int32(m.uint32())
+ }
+ case string:
+ m.value[i] = string(m.bytes(int(strsize[0])))
+ strsize = strsize[1:]
+ }
+ }
+
+ if len(m.data) > 0 {
+ return errors.New("Native Client: junk at end of message")
+ }
+ return nil
+}
+
+func float64bits(x float64) uint64 {
+ return *(*uint64)(unsafe.Pointer(&x))
+}
+
+func float64frombits(x uint64) float64 {
+ return *(*float64)(unsafe.Pointer(&x))
+}
+
+// At startup, connect to the name service.
+var nsClient = nsConnect()
+
+func nsConnect() *srpcClient {
+ var ns int32 = -1
+ _, _, errno := Syscall(sys_nameservice, uintptr(unsafe.Pointer(&ns)), 0, 0)
+ if errno != 0 {
+ println("Native Client nameservice:", errno.Error())
+ return nil
+ }
+
+ sock, _, errno := Syscall(sys_imc_connect, uintptr(ns), 0, 0)
+ if errno != 0 {
+ println("Native Client nameservice connect:", errno.Error())
+ return nil
+ }
+
+ c, err := newClient(int(sock))
+ if err != nil {
+ println("Native Client nameservice init:", err.Error())
+ return nil
+ }
+
+ return c
+}
+
+const (
+ nsSuccess = 0
+ nsNameNotFound = 1
+ nsDuplicateName = 2
+ nsInsufficientResources = 3
+ nsPermissionDenied = 4
+ nsInvalidArgument = 5
+)
+
+func openNamedService(name string, mode int32) (fd int, err error) {
+ if nsClient == nil {
+ return 0, errors.New("no name service")
+ }
+ ret, err := nsClient.Call("lookup:si:ih", name, int32(mode))
+ if err != nil {
+ return 0, err
+ }
+ status := ret[0].(int32)
+ fd = ret[1].(int)
+ switch status {
+ case nsSuccess:
+ // ok
+ case nsNameNotFound:
+ return -1, ENOENT
+ case nsDuplicateName:
+ return -1, EEXIST
+ case nsInsufficientResources:
+ return -1, EWOULDBLOCK
+ case nsPermissionDenied:
+ return -1, EPERM
+ case nsInvalidArgument:
+ return -1, EINVAL
+ default:
+ return -1, EINVAL
+ }
+ return fd, nil
+}