diff --git a/LICENSES/LICENSE.ninep b/LICENSES/LICENSE.ninep new file mode 120000 index 000000000..eb182b876 --- /dev/null +++ b/LICENSES/LICENSE.ninep @@ -0,0 +1 @@ +../src/vendor/github.com/Harvey-OS/ninep/LICENSE \ No newline at end of file diff --git a/doc/content/articles/tutorials/cc.article b/doc/content/articles/tutorials/cc.article index e46ecc6ab..47c7408d6 100644 --- a/doc/content/articles/tutorials/cc.article +++ b/doc/content/articles/tutorials/cc.article @@ -228,6 +228,29 @@ the receive command. .mega cc/recv2.mm +** Full filesystem access + +The `cc`mount` can be used to mount the guest filesystem on the host. This is +accomplished by a 9p server integrated into miniccc that serves the guest's +filesystem over the existing connection to the command and control server. +`cc`mount` is fully integrated with namespaces -- you may mount a VM's +filesystem to the head node, regardless of which host is actually running the +VM. In order to mount the filesystem, you'll need to use either the name or +UUID of the VM: + + cc mount vm-0 /mnt + +To list existing mounts, run it with no arguments: + + cc mount + +You can unmount a specific VM or mount by path using: + + clear cc mount [uuid or name or path]. + +Without an argument, `clear`cc`mount` clears all mounts. This also occurs when +you call `clear`cc`. + * Examining Responses We've seen the use of `cc`responses` several times so far. The `responses` diff --git a/src/miniccc/mux.go b/src/miniccc/mux.go index 2ad23fde3..9065722aa 100644 --- a/src/miniccc/mux.go +++ b/src/miniccc/mux.go @@ -60,6 +60,8 @@ func mux() { _, err = remote.Write(m.Tunnel) case ron.MESSAGE_PIPE: pipeMessage(&m) + case ron.MESSAGE_UFS: + ufsMessage(&m) default: err = fmt.Errorf("unknown message type: %v", m.Type) } diff --git a/src/miniccc/ufs.go b/src/miniccc/ufs.go new file mode 100644 index 000000000..3bc0ddeb8 --- /dev/null +++ b/src/miniccc/ufs.go @@ -0,0 +1,100 @@ +// Copyright (2014) Sandia Corporation. +// Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation, +// the U.S. Government retains certain rights in this software. + +package main + +import ( + log "minilog" + "net" + "os" + "path/filepath" + "ron" + "runtime" + + "github.com/Harvey-OS/ninep/filesystem" + "github.com/Harvey-OS/ninep/protocol" +) + +var rootFS struct { + // embed + *protocol.Server + + running bool + + // active connection, set when running + remote, local net.Conn +} + +// ufsMessage handles a message from the server and relays it to UFS +func ufsMessage(m *ron.Message) { + switch m.UfsMode { + case ron.UFS_OPEN: + if rootFS.running { + log.Error("ufs is already running") + return + } + + if rootFS.Server == nil { + log.Info("init rootFS") + root := "/" + if runtime.GOOS == "windows" { + // TODO: what if there is more that one volume? + root = filepath.VolumeName(os.Getenv("SYSTEMROOT")) + "\\" + } + + fs, err := ufs.NewServer(ufs.Root(root), ufs.Trace(log.Debug)) + if err != nil { + log.Error("unable to create file server: %v", err) + return + } + + ps, err := protocol.NewServer(fs, protocol.Trace(log.Debug)) + if err != nil { + log.Error("unable to create ninep server: %v", err) + return + } + rootFS.Server = ps + + log.Info("init'd rootFS") + } + + rootFS.running = true + + rootFS.remote, rootFS.local = net.Pipe() + + go ron.Trunk(rootFS.remote, client.UUID, ufsSendMessage) + + log.Info("accepting tunneled connection") + + if err := rootFS.Accept(rootFS.local); err != nil { + log.Error("ufs error: %v", err) + rootFS.running = false + } + case ron.UFS_CLOSE: + if !rootFS.running { + log.Error("ufs not running") + return + } + + rootFS.running = false + rootFS.remote.Close() + case ron.UFS_DATA: + if !rootFS.running { + log.Error("ufs not running") + return + } + + // relay the Tunnel data from ron + rootFS.remote.Write(m.Tunnel) + } +} + +// ufsSendMessage tweaks the message generated by ron.Trunk before calling +// sendMessage. +func ufsSendMessage(m *ron.Message) error { + m.Type = ron.MESSAGE_UFS + m.UfsMode = ron.UFS_DATA + + return sendMessage(m) +} diff --git a/src/minimega/cc_cli.go b/src/minimega/cc_cli.go index 91f2cbe4b..e8f501734 100644 --- a/src/minimega/cc_cli.go +++ b/src/minimega/cc_cli.go @@ -11,20 +11,32 @@ import ( "fmt" "minicli" log "minilog" + "net" + "os" "ron" "sort" "strconv" "strings" + "syscall" ) +type ccMount struct { + // Name of the VM, kept to help with unmount + Name string + // Addr for UFS + Addr string + // Path where the filesystem is mounted + Path string +} + var ccCLIHandlers = []minicli.Handler{ { // cc HelpShort: "command and control commands", HelpLong: ` -Command and control for virtual machines running the miniccc client. Commands -may include regular commands, backgrounded commands, and any number of sent -and/or received files. Commands will be executed in command creation order. For -example, to send a file 'foo' and display the contents on a remote VM: +Command and control for VMs running the miniccc client. Commands may include +regular commands, backgrounded commands, and any number of sent and/or received +files. Commands will be executed in command creation order. For example, to +send a file 'foo' and display the contents on a remote VM: cc send foo cc exec cat foo @@ -59,14 +71,20 @@ treated as tags: cc filter foo=bar -When a namespace is active, there is an implicit filter for vms with the -provided namespace. +"cc mount" allows direct access to a guest's filesystem over the command and +control connection. When given a VM uuid or name and a path, the VM's +filesystem is mounted to the local machine at the provided path. "cc mount" +without arguments displays the existing mounts. Users can use "clear cc mount" +to unmount the filesystem of one or all VMs. This should be done before killing +or stopping the VM ("clear namespace " will handle this automatically). For more documentation, see the article "Command and Control API Tutorial".`, Patterns: []string{ "cc", "cc ", "cc ", + "cc [filter]...", + "cc ", "cc [prefix]", @@ -81,10 +99,6 @@ For more documentation, see the article "Command and Control API Tutorial".`, "cc level ", - "cc ", - - "cc [filter]...", - "cc [raw,]", "cc ", @@ -95,6 +109,20 @@ For more documentation, see the article "Command and Control API Tutorial".`, }, Call: wrapBroadcastCLI(cliCC), }, + { // cc mount + HelpShort: "list mounted filesystems", + Patterns: []string{ + "cc mount", + }, + Call: cliCCMount, + }, + { // cc mount uuid + HelpShort: "mount VM filesystem", + Patterns: []string{ + "cc mount [path]", + }, + Call: cliCCMountUUID, + }, { // clear cc HelpShort: "reset command and control state", HelpLong: ` @@ -107,7 +135,14 @@ See "help cc" for more information.`, "clear cc ", "clear cc ", }, - Call: wrapBroadcastCLI(cliCCClear), + Call: wrapSimpleCLI(cliCCClear), + }, + { // clear cc mount + HelpShort: "unmount VM filesystem", + Patterns: []string{ + "clear cc mount [uuid or name or path]", + }, + Call: cliCCClearMount, }, } @@ -599,26 +634,302 @@ func cliCCListen(ns *Namespace, c *minicli.Command, resp *minicli.Response) erro return ns.ccServer.Listen(port) } +// cliCCMount needs to collect mounts from both the local ccMounts for the +// namespace and across the cluster. +func cliCCMount(c *minicli.Command, respChan chan<- minicli.Responses) { + ns := GetNamespace() + + // makeResponse creates a response from the namespace's ccMounts + makeResponse := func() *minicli.Response { + resp := &minicli.Response{Host: hostname} + + resp.Header = []string{"name", "uuid", "addr", "path"} + + for uuid, mnt := range ns.ccMounts { + resp.Tabular = append(resp.Tabular, []string{ + mnt.Name, + uuid, + mnt.Addr, + mnt.Path, + }) + } + + return resp + } + + // local behavior, see cli.go + if c.Source != "" { + respChan <- minicli.Responses{makeResponse()} + return + } + + var res minicli.Responses + + // LOCK: this is a CLI handler so we already hold the cmdLock. + for resps := range runCommands(namespaceCommands(ns, c)...) { + for _, resp := range resps { + res = append(res, resp) + } + } + + // if local node is not in namespace, append local response too + if !ns.Hosts[hostname] { + res = append(res, makeResponse()) + } + + respChan <- res + + return +} + +func cliCCMountUUID(c *minicli.Command, respChan chan<- minicli.Responses) { + ns := GetNamespace() + + resp := &minicli.Response{Host: hostname} + + id := c.StringArgs["uuid"] + path := c.StringArgs["path"] + + if path == "" && c.Source == "" { + // TODO: we could generate a sane default + resp.Error = "must provide a mount path" + + respChan <- minicli.Responses{resp} + return + } + + if _, err := os.Stat(path); path != "" && os.IsNotExist(err) { + resp.Error = "mount point does not exist" + + respChan <- minicli.Responses{resp} + return + } + + var vm VM + + // If we're doing the local behavior, only look at the local VMs. + // Otherwise, look globally. See note in cli.go. + if c.Source == "" { + // LOCK: this is a CLI handler so we already hold the cmdLock. + for _, vm2 := range globalVMs(ns) { + if vm2.GetName() == id || vm2.GetUUID() == id { + vm = vm2 + break + } + } + } else { + vm = ns.VMs.FindVM(id) + } + + if vm == nil { + resp.Error = vmNotFound(id).Error() + + respChan <- minicli.Responses{resp} + return + } + + // sanity check + if c.Source != "" && vm.GetHost() != hostname { + resp.Error = "holy heisenvm" + + respChan <- minicli.Responses{resp} + return + } + + if vm.GetHost() == hostname { + // VM is running locally + if mnt, ok := ns.ccMounts[vm.GetUUID()]; ok { + resp.Error = fmt.Sprintf("already connected to %v", mnt.Addr) + + respChan <- minicli.Responses{resp} + return + } + + // Start UFS + port, err := ns.ccServer.ListenUFS(vm.GetUUID()) + if err != nil { + resp.Error = err.Error() + + respChan <- minicli.Responses{resp} + return + } + + log.Debug("ufs for %v started on %v", vm.GetUUID(), port) + + mnt := ccMount{ + Name: vm.GetName(), + Addr: fmt.Sprintf("%v:%v", vm.GetHost(), port), + Path: path, + } + + if path == "" { + ns.ccMounts[vm.GetUUID()] = mnt + + resp.Response = strconv.Itoa(port) + + respChan <- minicli.Responses{resp} + return + } + + log.Info("mount for %v from :%v to %v", vm.GetUUID(), port, path) + + // do the mount + opts := fmt.Sprintf("trans=tcp,port=%v,version=9p2000", port) + + if err := syscall.Mount("127.0.0.1", path, "9p", 0, opts); err != nil { + if err := ns.ccServer.DisconnectUFS(vm.GetUUID()); err != nil { + // zombie UFS + log.Error("unable to disconnect ufs for %v: %v", vm.GetUUID(), err) + } + resp.Error = err.Error() + + respChan <- minicli.Responses{resp} + return + } + + ns.ccMounts[vm.GetUUID()] = mnt + + respChan <- minicli.Responses{resp} + return + } + + if mnt, ok := ns.ccMounts[vm.GetUUID()]; ok { + resp.Error = fmt.Sprintf("already connected to %v", mnt.Addr) + + respChan <- minicli.Responses{resp} + return + } + + // VM is running on a remote host + cmd := minicli.MustCompilef("namespace %v cc mount %v", ns.Name, vm.GetUUID()) + cmd.SetSource(ns.Name) + cmd.SetRecord(false) + + respChan2, err := meshageSend(cmd, vm.GetHost()) + if err != nil { + resp.Error = err.Error() + + respChan <- minicli.Responses{resp} + return + } + + var port int + + for resps := range respChan2 { + for _, resp := range resps { + // error from previous response... there should only be one + if err != nil { + continue + } + + if resp.Error != "" { + err = errors.New(resp.Error) + } else { + port, err = strconv.Atoi(resp.Response) + } + } + } + + if err != nil { + resp.Error = err.Error() + } else if port == 0 { + resp.Error = "unable to find UFS port" + } else { + log.Info("remote mount for %v from %v:%v to %v", vm.GetUUID(), vm.GetHost(), port, path) + + addr, err := net.ResolveIPAddr("ip", vm.GetHost()) + if err != nil { + resp.Error = err.Error() + + respChan <- minicli.Responses{resp} + return + } + + log.Info("resolved host %v to %v", vm.GetHost(), addr) + + // do the (remote) mount + opts := fmt.Sprintf("trans=tcp,port=%v,version=9p2000", port) + + if err := syscall.Mount(addr.IP.String(), path, "9p", 0, opts); err != nil { + resp.Error = err.Error() + } else { + ns.ccMounts[vm.GetUUID()] = ccMount{ + Name: vm.GetName(), + Addr: fmt.Sprintf("%v:%v", vm.GetHost(), port), + Path: path, + } + } + } + + respChan <- minicli.Responses{resp} + return +} + func cliCCClear(ns *Namespace, c *minicli.Command, resp *minicli.Response) error { - for what := range ccCliSubHandlers { - // We only want to clear something if it was specified on the - // command line or if we're clearing everything (nothing was - // specified). - if c.BoolArgs[what] || len(c.BoolArgs) == 0 { - log.Info("clearing %v in namespace `%v`", what, ns.Name) - - switch what { - case "filter": - ns.ccFilter = nil - case "commands": - ns.ccServer.ClearCommands() - case "responses": - ns.ccServer.ClearResponses() - case "prefix": - ns.ccPrefix = "" + // local behavior, see cli.go + if c.Source != "" { + for what := range ccCliSubHandlers { + // We only want to clear something if it was specified on the + // command line or if we're clearing everything (nothing was + // specified). + if c.BoolArgs[what] || len(c.BoolArgs) == 0 { + log.Info("clearing %v in namespace `%v`", what, ns.Name) + + switch what { + case "filter": + ns.ccFilter = nil + case "commands": + ns.ccServer.ClearCommands() + case "responses": + ns.ccServer.ClearResponses() + case "prefix": + ns.ccPrefix = "" + } } } + + if len(c.BoolArgs) == 0 { + // clear mounts too (not a sub handler) + if err := ns.clearCCMount(""); err != nil { + return err + } + } + + return nil + } + + // local clean up + if err := ns.clearCCMount(""); err != nil { + return err + } + + // fan out behavior + // LOCK: this is a CLI handler so we already hold the cmdLock. + return consume(runCommands(namespaceCommands(ns, c)...)) +} + +func cliCCClearMount(c *minicli.Command, respChan chan<- minicli.Responses) { + ns := GetNamespace() + + resp := &minicli.Response{Host: hostname} + + id := c.StringArgs["uuid"] + + if err := ns.clearCCMount(id); err != nil { + resp.Error = err.Error() + + respChan <- minicli.Responses{resp} + return + } + + if c.Source == "" { + // LOCK: this is a CLI handler so we already hold the cmdLock. + err := consume(runCommands(namespaceCommands(ns, c)...)) + if err != nil { + resp.Error = err.Error() + } } - return nil + respChan <- minicli.Responses{resp} + return } diff --git a/src/minimega/namespace.go b/src/minimega/namespace.go index 82aec2599..132f69172 100644 --- a/src/minimega/namespace.go +++ b/src/minimega/namespace.go @@ -16,6 +16,7 @@ import ( "runtime" "sort" "sync" + "syscall" "time" "vlans" ) @@ -72,6 +73,8 @@ type Namespace struct { ccServer *ron.Server ccFilter *ron.Filter ccPrefix string + + ccMounts map[string]ccMount } type NamespaceInfo struct { @@ -115,6 +118,7 @@ func NewNamespace(name string) *Namespace { }, vmConfig: NewVMConfig(), savedVMConfig: make(map[string]VMConfig), + ccMounts: make(map[string]ccMount), } if name == DefaultNamespace { @@ -172,6 +176,9 @@ func (n *Namespace) Destroy() error { n.vmID.Stop() + // unmount + n.clearCCMount("") + // Stop all captures n.captures.StopAll() n.counter.Stop() @@ -573,6 +580,39 @@ func (n *Namespace) Snapshot(dir string) error { return nil } +func (ns *Namespace) clearCCMount(s string) error { + for uuid, mnt := range ns.ccMounts { + switch s { + case "", uuid, mnt.Name, mnt.Path: + // match + default: + continue + } + + if mnt.Path != "" { + if err := syscall.Unmount(mnt.Path, 0); err != nil { + return err + } + } + + vm := ns.VMs.FindVM(uuid) + if vm == nil { + // VM was mounted from remote host + delete(ns.ccMounts, uuid) + continue + } + + // VM is running locally + if err := ns.ccServer.DisconnectUFS(uuid); err != nil { + return err + } + + delete(ns.ccMounts, uuid) + } + + return nil +} + // GetNamespace returns the active namespace. func GetNamespace() *Namespace { namespaceLock.Lock() diff --git a/src/ron/client.go b/src/ron/client.go index 974ec2579..7ae08b32f 100644 --- a/src/ron/client.go +++ b/src/ron/client.go @@ -17,14 +17,6 @@ import ( "time" ) -const ( - PIPE_NEW_READER = iota - PIPE_NEW_WRITER - PIPE_CLOSE_READER - PIPE_CLOSE_WRITER - PIPE_DATA -) - type Client struct { UUID string Arch string @@ -80,6 +72,9 @@ type client struct { pipeLock sync.Mutex pipeReaders map[string]*miniplumber.Reader pipeWriters map[string]chan<- string + + ufsListener net.Listener + ufsConn net.Conn } func (c *client) sendMessage(m *Message) error { diff --git a/src/ron/message.go b/src/ron/message.go new file mode 100644 index 000000000..011f2103b --- /dev/null +++ b/src/ron/message.go @@ -0,0 +1,79 @@ +// Copyright (2015) Sandia Corporation. +// Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation, +// the U.S. Government retains certain rights in this software. + +package ron + +type Type int + +// Message types to inform the mux on either end how to route the message +const ( + MESSAGE_COMMAND Type = iota + MESSAGE_CLIENT + MESSAGE_TUNNEL + MESSAGE_FILE + MESSAGE_PIPE + MESSAGE_UFS +) + +// Pipe modes +const ( + PIPE_NEW_READER = iota + PIPE_NEW_WRITER + PIPE_CLOSE_READER + PIPE_CLOSE_WRITER + PIPE_DATA +) + +// UFS modes +const ( + UFS_OPEN = iota + UFS_CLOSE + UFS_DATA +) + +type Message struct { + Type Type + UUID string + Error string + + // MESSAGE_COMMAND + Commands map[int]*Command + + // MESSAGE_CLIENT + Client *Client + + // MESSAGE_FILE + File []byte + Filename string + + // MESSAGE_TUNNEL and MESSAGE_UFS + Tunnel []byte + + // MESSAGE_PIPE + Pipe string + PipeMode int + PipeData string + + // MESSAGE_UFS + UfsMode int +} + +func (t Type) String() string { + switch t { + case MESSAGE_COMMAND: + return "COMMAND" + case MESSAGE_CLIENT: + return "CLIENT" + case MESSAGE_TUNNEL: + return "TUNNEL" + case MESSAGE_FILE: + return "FILE" + case MESSAGE_PIPE: + return "PIPE" + case MESSAGE_UFS: + return "UFS" + } + + return "UNKNOWN" +} diff --git a/src/ron/ron.go b/src/ron/ron.go index 083fffbe1..16329f317 100644 --- a/src/ron/ron.go +++ b/src/ron/ron.go @@ -10,17 +10,6 @@ import ( "strings" ) -type Type int - -// Ron message types to inform the mux on either end how to route the message -const ( - MESSAGE_COMMAND Type = iota - MESSAGE_CLIENT - MESSAGE_TUNNEL - MESSAGE_FILE - MESSAGE_PIPE -) - const ( HEARTBEAT_RATE = 5 REAPER_RATE = 30 @@ -41,37 +30,6 @@ type VM interface { SetTag(string, string) } -type Message struct { - Type Type - UUID string - Commands map[int]*Command - Client *Client - File []byte - Filename string - Error string - Tunnel []byte - Pipe string - PipeMode int - PipeData string -} - -func (t Type) String() string { - switch t { - case MESSAGE_COMMAND: - return "COMMAND" - case MESSAGE_CLIENT: - return "CLIENT" - case MESSAGE_TUNNEL: - return "TUNNEL" - case MESSAGE_FILE: - return "FILE" - case MESSAGE_PIPE: - return "PIPE" - } - - return "UNKNOWN" -} - func unmangle(uuid string) string { // string must be in the form: // XXXXXXXX-XXXX-XXXX-YYYY-YYYYYYYYYYYY diff --git a/src/ron/server.go b/src/ron/server.go index 8db59beee..cb1ef1021 100644 --- a/src/ron/server.go +++ b/src/ron/server.go @@ -712,6 +712,8 @@ func (s *Server) clientHandler(c *client) { // this shouldn't be sent via the client... case MESSAGE_PIPE: c.pipeHandler(s.plumber, &m) + case MESSAGE_UFS: + c.ufsMessage(&m) default: err = fmt.Errorf("unknown message type: %v", m.Type) } diff --git a/src/ron/tunnel.go b/src/ron/tunnel.go index b5d8a49c0..c4215b6dc 100644 --- a/src/ron/tunnel.go +++ b/src/ron/tunnel.go @@ -8,7 +8,6 @@ import ( "fmt" "io" log "minilog" - "net" ) const ( @@ -59,7 +58,7 @@ func (s *Server) Reverse(filter *Filter, source int, host string, dest int) erro // Trunk reads data from remote, constructs a *Message, and sends it using fn. // Returns the first error. -func Trunk(remote net.Conn, uuid string, fn func(*Message) error) { +func Trunk(remote io.ReadCloser, uuid string, fn func(*Message) error) { var n int var err error diff --git a/src/ron/ufs.go b/src/ron/ufs.go new file mode 100644 index 000000000..2bc6679a6 --- /dev/null +++ b/src/ron/ufs.go @@ -0,0 +1,133 @@ +// Copyright (2015) Sandia Corporation. +// Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation, +// the U.S. Government retains certain rights in this software. + +package ron + +import ( + "errors" + "fmt" + log "minilog" + "net" +) + +// ListenUFS starts a listener to connect to UFS running on the VM specified by +// the UUID. Returns the TCP port or an error. +func (s *Server) ListenUFS(uuid string) (int, error) { + s.clientLock.Lock() + defer s.clientLock.Unlock() + + c, ok := s.clients[uuid] + if !ok { + return 0, fmt.Errorf("no such client: %v", uuid) + } + + m := &Message{ + Type: MESSAGE_UFS, + UUID: uuid, + UfsMode: UFS_OPEN, + } + if err := c.sendMessage(m); err != nil { + return 0, err + } + + // Listen on random tcp port + l, err := net.Listen("tcp4", ":0") + if err != nil { + return 0, err + } + + c.Lock() + defer c.Unlock() + c.ufsListener = l + + addr := l.Addr().(*net.TCPAddr) + + go func() { + defer l.Close() + log.Info("waiting for connections to ufs on %v", addr) + + for { + conn, err := l.Accept() + if err != nil { + log.Error("accept failed: %v", err) + + c.Lock() + defer c.Unlock() + + c.ufsListener = nil + return + } + + log.Info("new connection from %v", conn.RemoteAddr()) + c.Lock() + c.ufsConn = conn + c.Unlock() + + // blocks until connection is done + Trunk(conn, c.UUID, func(m *Message) error { + m.Type = MESSAGE_UFS + m.UfsMode = UFS_DATA + + return c.sendMessage(m) + }) + + c.Lock() + c.ufsConn = nil + c.Unlock() + + conn.Close() + + m := &Message{ + Type: MESSAGE_UFS, + UUID: uuid, + UfsMode: UFS_CLOSE, + } + if err := c.sendMessage(m); err != nil { + log.Error("unable to close: %v", err) + } + } + }() + + return addr.Port, nil +} + +func (s *Server) DisconnectUFS(uuid string) error { + s.clientLock.Lock() + defer s.clientLock.Unlock() + + c, ok := s.clients[uuid] + if !ok { + return fmt.Errorf("no such client: %v", uuid) + } + + c.Lock() + defer c.Unlock() + + if c.ufsListener == nil { + return errors.New("ufs is not running") + } + + m := &Message{ + Type: MESSAGE_UFS, + UUID: uuid, + UfsMode: UFS_CLOSE, + } + if err := c.sendMessage(m); err != nil { + log.Error("unable to close: %v", err) + } + + c.ufsListener.Close() + c.ufsListener = nil + + if c.ufsConn != nil { + c.ufsConn.Close() + c.ufsConn = nil + } + + return nil +} + +func (c *client) ufsMessage(m *Message) { + c.ufsConn.Write(m.Tunnel) +} diff --git a/src/vendor/github.com/Harvey-OS/ninep/.gitignore b/src/vendor/github.com/Harvey-OS/ninep/.gitignore new file mode 100644 index 000000000..15c5a2a8c --- /dev/null +++ b/src/vendor/github.com/Harvey-OS/ninep/.gitignore @@ -0,0 +1,3 @@ +*~ +*.test +*.swp diff --git a/src/vendor/github.com/Harvey-OS/ninep/.mention-bot b/src/vendor/github.com/Harvey-OS/ninep/.mention-bot new file mode 100644 index 000000000..7508ad36d --- /dev/null +++ b/src/vendor/github.com/Harvey-OS/ninep/.mention-bot @@ -0,0 +1,4 @@ +{ + "requiredOrgs": ["Harvey-OS"], // mention-bot will only mention user who are a member of one of these organizations + "actions": ["opened"] // List of PR actions that mention-bot will listen to, default is "opened" +} diff --git a/src/vendor/github.com/Harvey-OS/ninep/.pullapprove.yml b/src/vendor/github.com/Harvey-OS/ninep/.pullapprove.yml new file mode 100644 index 000000000..65aa02df5 --- /dev/null +++ b/src/vendor/github.com/Harvey-OS/ninep/.pullapprove.yml @@ -0,0 +1,19 @@ +approve_by_comment: true +approve_regex: '^(LGTM|:\+1:|\+1)' +reject_regex: '^(NMW|:-1:|-1)' +reset_on_push: false +reviewers: + members: + - 0intro + - aki5 + - dancrossnyc + - elbing + - floren + - fuchicar + - hdonnay + - keedon + - rminnich + - sevki + - hagna + name: Harvey-OS/core + required: 1 diff --git a/src/vendor/github.com/Harvey-OS/ninep/.travis.yml b/src/vendor/github.com/Harvey-OS/ninep/.travis.yml new file mode 100644 index 000000000..a689a83c7 --- /dev/null +++ b/src/vendor/github.com/Harvey-OS/ninep/.travis.yml @@ -0,0 +1,12 @@ +language: go +go: + - 1.7.x + - 1.8.x + - 1.9.x + - 1.10.x +install: + - export GOPATH="$HOME/gopath" + - mkdir -p "$GOPATH/src/github.com/Harvey-OS/ninep" + - go get -v -t -d github.com/Harvey-OS/ninep/... +script: + - go test github.com/Harvey-OS/ninep/... diff --git a/src/vendor/github.com/Harvey-OS/ninep/AUTHORS b/src/vendor/github.com/Harvey-OS/ninep/AUTHORS new file mode 100644 index 000000000..47db3dc08 --- /dev/null +++ b/src/vendor/github.com/Harvey-OS/ninep/AUTHORS @@ -0,0 +1,13 @@ +# This is the official list of Ninep authors for copyright purposes. +# This file is distinct from the CONTRIBUTORS files. +# See the latter for an explanation. + +# Names should be added to this file as +# Name or Organization +# The email address is not required for organizations. + +# Please keep the list sorted. + +Andrey Mirtchovski +Latchesar Ionkov +Roger Peppe diff --git a/src/vendor/github.com/Harvey-OS/ninep/CONTRIBUTORS b/src/vendor/github.com/Harvey-OS/ninep/CONTRIBUTORS new file mode 100644 index 000000000..755f1eec9 --- /dev/null +++ b/src/vendor/github.com/Harvey-OS/ninep/CONTRIBUTORS @@ -0,0 +1,18 @@ +# This is the official list of people who can contribute +# (and typically have contributed) code to the Ninep repository. +# The AUTHORS file lists the copyright holders; this file +# lists people. For example, Google employees are listed here +# but not in AUTHORS, because Google holds the copyright. +# +# The submission process automatically checks to make sure +# that people submitting code are listed in this file (by email address). +# XXX more bumph here? +# Names should be added to this file like so: +# Name + +# Please keep the list sorted. + +Andrey Mirtchovski +Latchesar Ionkov +Akshat Kumar +Roger Peppe diff --git a/src/vendor/github.com/Harvey-OS/ninep/LICENSE b/src/vendor/github.com/Harvey-OS/ninep/LICENSE new file mode 100644 index 000000000..587191749 --- /dev/null +++ b/src/vendor/github.com/Harvey-OS/ninep/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2012 The Ninep Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * The names of Ninep's contributors may not be used to endorse +or promote products derived from this software without specific prior +written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/src/vendor/github.com/Harvey-OS/ninep/README.md b/src/vendor/github.com/Harvey-OS/ninep/README.md new file mode 100644 index 000000000..382e26acd --- /dev/null +++ b/src/vendor/github.com/Harvey-OS/ninep/README.md @@ -0,0 +1,3 @@ +# ninep +Package for implementing clients and servers of the 9P and 9P2000 distributed resource protocols in Go. + diff --git a/src/vendor/github.com/Harvey-OS/ninep/cmd/ufs/.gitignore b/src/vendor/github.com/Harvey-OS/ninep/cmd/ufs/.gitignore new file mode 100644 index 000000000..f92eb037f --- /dev/null +++ b/src/vendor/github.com/Harvey-OS/ninep/cmd/ufs/.gitignore @@ -0,0 +1,2 @@ +/ufs + diff --git a/src/vendor/github.com/Harvey-OS/ninep/cmd/ufs/ufs.go b/src/vendor/github.com/Harvey-OS/ninep/cmd/ufs/ufs.go new file mode 100644 index 000000000..d86b0dbca --- /dev/null +++ b/src/vendor/github.com/Harvey-OS/ninep/cmd/ufs/ufs.go @@ -0,0 +1,54 @@ +// UFS is a userspace server which exports a filesystem over 9p2000. +// +// By default, it will export / over a TCP on port 5640 under the username +// of "harvey". +package main + +import ( + "flag" + "log" + "net" + + "github.com/Harvey-OS/ninep/debug" + "github.com/Harvey-OS/ninep/filesystem" + "github.com/Harvey-OS/ninep/protocol" +) + +var ( + ntype = flag.String("ntype", "tcp4", "Default network type") + naddr = flag.String("addr", ":5640", "Network address") + root = flag.String("root", "/", "filesystem root") + trace = flag.Bool("trace", false, "enable debug messages") +) + +func checkErr(format string, err error) { + if err != nil { + log.Fatalf(format, err) + } +} + +func main() { + flag.Parse() + + tracer := func(format string, args ...interface{}) {} + if *trace { + tracer = log.Printf + } + + ln, err := net.Listen(*ntype, *naddr) + checkErr("Listen failed: %v", err) + + fs, err := ufs.NewServer(ufs.Root(*root), ufs.Trace(tracer)) + checkErr("ufs.NewServer failed: %v", err) + + var ninefs protocol.NineServer = fs + if *trace { + ninefs, err = debug.NewServer(ninefs, debug.Trace(tracer)) + checkErr("debug.NewServer failed: %v", err) + } + + s, err := protocol.NewServer(ninefs, protocol.Trace(tracer)) + checkErr("protocol.NewServer failed: %v", err) + + checkErr("Serve failed: %v", s.Serve(ln)) +} diff --git a/src/vendor/github.com/Harvey-OS/ninep/debug/debug.go b/src/vendor/github.com/Harvey-OS/ninep/debug/debug.go new file mode 100644 index 000000000..f8b82ed09 --- /dev/null +++ b/src/vendor/github.com/Harvey-OS/ninep/debug/debug.go @@ -0,0 +1,184 @@ +// Copyright 2009 The Ninep Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package debug + +import ( + "bytes" + "errors" + + "github.com/Harvey-OS/ninep/protocol" +) + +type Server struct { + protocol.NineServer + + trace protocol.Tracer +} + +type ServerOpt func(*Server) error + +func NewServer(s protocol.NineServer, opts ...ServerOpt) (*Server, error) { + s2 := &Server{ + NineServer: s, + trace: nologf, + } + + for _, opt := range opts { + if err := opt(s2); err != nil { + return nil, err + } + } + + return s2, nil +} + +func Trace(tracer protocol.Tracer) ServerOpt { + return func(s *Server) error { + if tracer == nil { + return errors.New("tracer cannot be nil") + } + s.trace = tracer + return nil + } +} + +// nologf does nothing and is the default trace function +func nologf(format string, args ...interface{}) {} + +func (s *Server) Rversion(msize protocol.MaxSize, version string) (protocol.MaxSize, string, error) { + s.trace(">>> Tversion %v %v\n", msize, version) + msize, version, err := s.NineServer.Rversion(msize, version) + if err == nil { + s.trace("<<< Rversion %v %v\n", msize, version) + } else { + s.trace("<<< Error %v\n", err) + } + return msize, version, err +} + +func (s *Server) Rattach(fid protocol.FID, afid protocol.FID, uname string, aname string) (protocol.QID, error) { + s.trace(">>> Tattach fid %v, afid %v, uname %v, aname %v\n", fid, afid, + uname, aname) + qid, err := s.NineServer.Rattach(fid, afid, uname, aname) + if err == nil { + s.trace("<<< Rattach %v\n", qid) + } else { + s.trace("<<< Error %v\n", err) + } + return qid, err +} + +func (s *Server) Rflush(o protocol.Tag) error { + s.trace(">>> Tflush tag %v\n", o) + err := s.NineServer.Rflush(o) + if err == nil { + s.trace("<<< Rflush\n") + } else { + s.trace("<<< Error %v\n", err) + } + return err +} + +func (s *Server) Rwalk(fid protocol.FID, newfid protocol.FID, paths []string) ([]protocol.QID, error) { + s.trace(">>> Twalk fid %v, newfid %v, paths %v\n", fid, newfid, paths) + qid, err := s.NineServer.Rwalk(fid, newfid, paths) + if err == nil { + s.trace("<<< Rwalk %v\n", qid) + } else { + s.trace("<<< Error %v\n", err) + } + return qid, err +} + +func (s *Server) Ropen(fid protocol.FID, mode protocol.Mode) (protocol.QID, protocol.MaxSize, error) { + s.trace(">>> Topen fid %v, mode %v\n", fid, mode) + qid, iounit, err := s.NineServer.Ropen(fid, mode) + if err == nil { + s.trace("<<< Ropen %v %v\n", qid, iounit) + } else { + s.trace("<<< Error %v\n", err) + } + return qid, iounit, err +} + +func (s *Server) Rcreate(fid protocol.FID, name string, perm protocol.Perm, mode protocol.Mode) (protocol.QID, protocol.MaxSize, error) { + s.trace(">>> Tcreate fid %v, name %v, perm %v, mode %v\n", fid, name, + perm, mode) + qid, iounit, err := s.NineServer.Rcreate(fid, name, perm, mode) + if err == nil { + s.trace("<<< Rcreate %v %v\n", qid, iounit) + } else { + s.trace("<<< Error %v\n", err) + } + return qid, iounit, err +} + +func (s *Server) Rclunk(fid protocol.FID) error { + s.trace(">>> Tclunk fid %v\n", fid) + err := s.NineServer.Rclunk(fid) + if err == nil { + s.trace("<<< Rclunk\n") + } else { + s.trace("<<< Error %v\n", err) + } + return err +} + +func (s *Server) Rstat(fid protocol.FID) ([]byte, error) { + s.trace(">>> Tstat fid %v\n", fid) + b, err := s.NineServer.Rstat(fid) + if err == nil { + dir, _ := protocol.Unmarshaldir(bytes.NewBuffer(b)) + s.trace("<<< Rstat %v\n", dir) + } else { + s.trace("<<< Error %v\n", err) + } + return b, err +} + +func (s *Server) Rwstat(fid protocol.FID, b []byte) error { + dir, _ := protocol.Unmarshaldir(bytes.NewBuffer(b)) + s.trace(">>> Twstat fid %v, %v\n", fid, dir) + err := s.NineServer.Rwstat(fid, b) + if err == nil { + s.trace("<<< Rwstat\n") + } else { + s.trace("<<< Error %v\n", err) + } + return err +} + +func (s *Server) Rremove(fid protocol.FID) error { + s.trace(">>> Tremove fid %v\n", fid) + err := s.NineServer.Rremove(fid) + if err == nil { + s.trace("<<< Rremove\n") + } else { + s.trace("<<< Error %v\n", err) + } + return err +} + +func (s *Server) Rread(fid protocol.FID, o protocol.Offset, c protocol.Count) ([]byte, error) { + s.trace(">>> Tread fid %v, off %v, count %v\n", fid, o, c) + b, err := s.NineServer.Rread(fid, o, c) + if err == nil { + s.trace("<<< Rread %v\n", len(b)) + } else { + s.trace("<<< Error %v\n", err) + } + return b, err +} + +func (s *Server) Rwrite(fid protocol.FID, o protocol.Offset, b []byte) (protocol.Count, error) { + s.trace(">>> Twrite fid %v, off %v, count %v\n", fid, o, len(b)) + c, err := s.NineServer.Rwrite(fid, o, b) + if err == nil { + s.trace("<<< Rwrite %v\n", c) + } else { + s.trace("<<< Error %v\n", err) + } + return c, err +} diff --git a/src/vendor/github.com/Harvey-OS/ninep/filesystem/filesystem.go b/src/vendor/github.com/Harvey-OS/ninep/filesystem/filesystem.go new file mode 100644 index 000000000..49031eef7 --- /dev/null +++ b/src/vendor/github.com/Harvey-OS/ninep/filesystem/filesystem.go @@ -0,0 +1,484 @@ +// Copyright 2009 The Ninep Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ufs + +import ( + "bytes" + "errors" + "fmt" + "io" + "log" + "os" + "path" + "path/filepath" + "sync" + "time" + + "github.com/Harvey-OS/ninep/protocol" +) + +type file struct { + protocol.QID + fullName string + file *os.File + // We can't know how big a serialized dentry is until we serialize it. + // At that point it might be too big. We save it here if that happens, + // and on the next directory read we start with that. + oflow []byte +} + +type FileServer struct { + root *file + rootPath string + Versioned bool + IOunit protocol.MaxSize + + // mu guards below + mu sync.Mutex + files map[protocol.FID]*file + + trace protocol.Tracer +} + +type ServerOpt func(*FileServer) error + +func stat(s string) (*protocol.Dir, protocol.QID, error) { + var q protocol.QID + st, err := os.Lstat(s) + if err != nil { + return nil, q, fmt.Errorf("does not exist") + } + d, err := dirTo9p2000Dir(st) + if err != nil { + return nil, q, nil + } + q = fileInfoToQID(st) + return d, q, nil +} + +func NewServer(opts ...ServerOpt) (*FileServer, error) { + s := &FileServer{ + files: make(map[protocol.FID]*file), + trace: nologf, + } + + for _, opt := range opts { + if err := opt(s); err != nil { + return nil, err + } + } + + return s, nil +} + +func Root(root string) ServerOpt { + return func(s *FileServer) error { + s.rootPath = root + return nil + } +} + +func IOunit(size protocol.MaxSize) ServerOpt { + return func(s *FileServer) error { + s.IOunit = size + return nil + } +} + +func Trace(tracer protocol.Tracer) ServerOpt { + return func(s *FileServer) error { + if tracer == nil { + return errors.New("tracer cannot be nil") + } + s.trace = tracer + return nil + } +} + +// nologf does nothing and is the default trace function +func nologf(format string, args ...interface{}) {} + +func (e *FileServer) Rversion(msize protocol.MaxSize, version string) (protocol.MaxSize, string, error) { + if version != "9P2000" { + return 0, "", fmt.Errorf("%v not supported; only 9P2000", version) + } + e.Versioned = true + return msize, version, nil +} + +func (e *FileServer) getFile(fid protocol.FID) (*file, error) { + e.mu.Lock() + defer e.mu.Unlock() + f, ok := e.files[fid] + if !ok { + return nil, fmt.Errorf("does not exist") + } + + return f, nil +} + +func (e *FileServer) Rattach(fid protocol.FID, afid protocol.FID, uname string, aname string) (protocol.QID, error) { + if afid != protocol.NOFID { + return protocol.QID{}, fmt.Errorf("We don't do auth attach") + } + // There should be no .. or other such junk in the Aname. Clean it up anyway. + aname = path.Join("/", aname) + aname = path.Join(e.rootPath, aname) + st, err := os.Stat(aname) + if err != nil { + return protocol.QID{}, err + } + r := &file{fullName: aname} + r.QID = fileInfoToQID(st) + e.files[fid] = r + e.root = r + return r.QID, nil +} + +func (e *FileServer) Rflush(o protocol.Tag) error { + return nil +} + +func (e *FileServer) Rwalk(fid protocol.FID, newfid protocol.FID, paths []string) ([]protocol.QID, error) { + e.mu.Lock() + f, ok := e.files[fid] + e.mu.Unlock() + if !ok { + return nil, fmt.Errorf("does not exist") + } + if len(paths) == 0 { + e.mu.Lock() + defer e.mu.Unlock() + _, ok := e.files[newfid] + if ok { + return nil, fmt.Errorf("FID in use: clone walk, fid %d newfid %d", fid, newfid) + } + nf := *f + e.files[newfid] = &nf + return []protocol.QID{}, nil + } + p := f.fullName + q := make([]protocol.QID, len(paths)) + + var i int + for i = range paths { + p = path.Join(p, paths[i]) + st, err := os.Lstat(p) + if err != nil { + // From the RFC: If the first element cannot be walked for any + // reason, Rerror is returned. Otherwise, the walk will return an + // Rwalk message containing nwqid qids corresponding, in order, to + // the files that are visited by the nwqid successful elementwise + // walks; nwqid is therefore either nwname or the index of the + // first elementwise walk that failed. The value of nwqid cannot be + // zero unless nwname is zero. Also, nwqid will always be less than + // or equal to nwname. Only if it is equal, however, will newfid be + // affected, in which case newfid will represent the file reached + // by the final elementwise walk requested in the message. + // + // to sum up: if any walks have succeeded, you return the QIDS for + // one more than the last successful walk + if i == 0 { + return nil, fmt.Errorf("file does not exist") + } + // we only get here if i is > 0 and less than nwname, + // so the i should be safe. + return q[:i], nil + } + q[i] = fileInfoToQID(st) + } + e.mu.Lock() + defer e.mu.Unlock() + // this is quite unlikely, which is why we don't bother checking for it first. + if fid != newfid { + if _, ok := e.files[newfid]; ok { + return nil, fmt.Errorf("FID in use: walk to %v, fid %v, newfid %v", paths, fid, newfid) + } + } + e.files[newfid] = &file{fullName: p, QID: q[i]} + return q, nil +} + +func (e *FileServer) Ropen(fid protocol.FID, mode protocol.Mode) (protocol.QID, protocol.MaxSize, error) { + e.mu.Lock() + f, ok := e.files[fid] + e.mu.Unlock() + if !ok { + return protocol.QID{}, 0, fmt.Errorf("does not exist") + } + + var err error + f.file, err = os.OpenFile(f.fullName, modeToUnixFlags(mode), 0) + if err != nil { + return protocol.QID{}, 0, err + } + + return f.QID, e.IOunit, nil +} +func (e *FileServer) Rcreate(fid protocol.FID, name string, perm protocol.Perm, mode protocol.Mode) (protocol.QID, protocol.MaxSize, error) { + f, err := e.getFile(fid) + if err != nil { + return protocol.QID{}, 0, err + } + if f.file != nil { + return protocol.QID{}, 0, fmt.Errorf("FID already open") + } + n := path.Join(f.fullName, name) + if perm&protocol.Perm(protocol.DMDIR) != 0 { + p := os.FileMode(int(perm) & 0777) + err := os.Mkdir(n, p) + _, q, err := stat(n) + if err != nil { + return protocol.QID{}, 0, err + } + f.file, err = os.Open(n) + if err != nil { + return protocol.QID{}, 0, err + } + f.fullName = n + f.QID = q + return q, 8000, err + } + + m := modeToUnixFlags(mode) | os.O_CREATE | os.O_TRUNC + p := os.FileMode(perm) & 0777 + of, err := os.OpenFile(n, m, p) + if err != nil { + return protocol.QID{}, 0, err + } + _, q, err := stat(n) + if err != nil { + return protocol.QID{}, 0, err + } + f.fullName = n + f.QID = q + f.file = of + return q, 8000, err +} +func (e *FileServer) Rclunk(fid protocol.FID) error { + _, err := e.clunk(fid) + return err +} + +func (e *FileServer) Rstat(fid protocol.FID) ([]byte, error) { + f, err := e.getFile(fid) + if err != nil { + return []byte{}, err + } + st, err := os.Lstat(f.fullName) + if err != nil { + return []byte{}, fmt.Errorf("ENOENT") + } + d, err := dirTo9p2000Dir(st) + if err != nil { + return []byte{}, nil + } + var b bytes.Buffer + protocol.Marshaldir(&b, *d) + return b.Bytes(), nil +} +func (e *FileServer) Rwstat(fid protocol.FID, b []byte) error { + var changed bool + f, err := e.getFile(fid) + if err != nil { + return err + } + dir, err := protocol.Unmarshaldir(bytes.NewBuffer(b)) + if err != nil { + return err + } + if dir.Mode != 0xFFFFFFFF { + changed = true + mode := dir.Mode & 0777 + if err := os.Chmod(f.fullName, os.FileMode(mode)); err != nil { + return err + } + } + + // Try to find local uid, gid by name. + if dir.User != "" || dir.Group != "" { + return fmt.Errorf("Permission denied") + changed = true + } + + /* + if uid != ninep.NOUID || gid != ninep.NOUID { + changed = true + e := os.Chown(fid.path, int(uid), int(gid)) + if e != nil { + req.RespondError(toError(e)) + return + } + } + */ + + if dir.Name != "" { + changed = true + // If we path.Join dir.Name to / before adding it to + // the fid path, that ensures nobody gets to walk out of the + // root of this server. + newname := path.Join(path.Dir(f.fullName), path.Join("/", dir.Name)) + + // absolute renaming. Ufs can do this, so let's support it. + // We'll allow an absolute path in the Name and, if it is, + // we will make it relative to root. This is a gigantic performance + // improvement in systems that allow it. + if filepath.IsAbs(dir.Name) { + newname = path.Join(e.rootPath, dir.Name) + } + + // If to exists, and to is a directory, we can't do the + // rename, since os.Rename will move from into to. + + st, err := os.Stat(newname) + if err == nil && st.IsDir() { + return fmt.Errorf("is a directory") + } + if err := os.Rename(f.fullName, newname); err != nil { + return err + } + f.fullName = newname + } + + if dir.Length != 0xFFFFFFFFFFFFFFFF { + changed = true + if err := os.Truncate(f.fullName, int64(dir.Length)); err != nil { + return err + } + } + + // If either mtime or atime need to be changed, then + // we must change both. + if dir.Mtime != ^uint32(0) || dir.Atime != ^uint32(0) { + changed = true + mt, at := time.Unix(int64(dir.Mtime), 0), time.Unix(int64(dir.Atime), 0) + if cmt, cat := (dir.Mtime == ^uint32(0)), (dir.Atime == ^uint32(0)); cmt || cat { + st, err := os.Stat(f.fullName) + if err != nil { + return err + } + switch cmt { + case true: + mt = st.ModTime() + default: + //at = atime(st.Sys().(*syscall.Stat_t)) + } + } + if err := os.Chtimes(f.fullName, at, mt); err != nil { + return err + } + } + + if !changed && f.file != nil { + f.file.Sync() + } + return nil +} + +func (e *FileServer) clunk(fid protocol.FID) (*file, error) { + e.mu.Lock() + defer e.mu.Unlock() + f, ok := e.files[fid] + if !ok { + return nil, fmt.Errorf("does not exist") + } + delete(e.files, fid) + // What do we do if we can't close it? + // All I can think of is to log it. + if f.file != nil { + if err := f.file.Close(); err != nil { + log.Printf("Close of %v failed: %v", f.fullName, err) + } + } + return f, nil +} + +// Rremove removes the file. The question of whether the file continues to be accessible +// is system dependent. +func (e *FileServer) Rremove(fid protocol.FID) error { + f, err := e.clunk(fid) + if err != nil { + return err + } + return os.Remove(f.fullName) +} + +func (e *FileServer) Rread(fid protocol.FID, o protocol.Offset, c protocol.Count) ([]byte, error) { + f, err := e.getFile(fid) + if err != nil { + return nil, err + } + if f.file == nil { + return nil, fmt.Errorf("FID not open") + } + if f.QID.Type&protocol.QTDIR != 0 { + if o == 0 { + f.oflow = nil + if err := resetDir(f); err != nil { + return nil, err + } + } + + // We make the assumption that they can always fit at least one + // directory entry into a read. If that assumption does not hold + // so many things are broken that we can't fix them here. + // But we'll drop out of the loop below having returned nothing + // anyway. + b := bytes.NewBuffer(f.oflow) + f.oflow = nil + pos := 0 + + for { + if b.Len() > int(c) { + f.oflow = b.Bytes()[pos:] + return b.Bytes()[:pos], nil + } + pos += b.Len() + st, err := f.file.Readdir(1) + if err == io.EOF { + return b.Bytes(), nil + } + if err != nil { + return nil, err + } + + d9p, err := dirTo9p2000Dir(st[0]) + if err != nil { + return nil, err + } + protocol.Marshaldir(b, *d9p) + // We're not quite doing the array right. + // What does work is returning one thing so, for now, do that. + return b.Bytes(), nil + } + return b.Bytes(), nil + } + + // N.B. even if they ask for 0 bytes on some file systems it is important to pass + // through a zero byte read (not Unix, of course). + b := make([]byte, c) + n, err := f.file.ReadAt(b, int64(o)) + if err != nil && err != io.EOF { + return nil, err + } + return b[:n], nil +} + +func (e *FileServer) Rwrite(fid protocol.FID, o protocol.Offset, b []byte) (protocol.Count, error) { + f, err := e.getFile(fid) + if err != nil { + return -1, err + } + if f.file == nil { + return -1, fmt.Errorf("FID not open") + } + + // N.B. even if they ask for 0 bytes on some file systems it is important to pass + // through a zero byte write (not Unix, of course). Also, let the underlying file system + // manage the error if the open mode was wrong. No need to duplicate the logic. + + n, err := f.file.WriteAt(b, int64(o)) + return protocol.Count(n), err +} diff --git a/src/vendor/github.com/Harvey-OS/ninep/filesystem/filesystem_test.go b/src/vendor/github.com/Harvey-OS/ninep/filesystem/filesystem_test.go new file mode 100644 index 000000000..b7b46f279 --- /dev/null +++ b/src/vendor/github.com/Harvey-OS/ninep/filesystem/filesystem_test.go @@ -0,0 +1,318 @@ +package ufs + +import ( + "bytes" + "fmt" + "io/ioutil" + "net" + "os" + "path" + "strings" + "testing" + + "github.com/Harvey-OS/ninep/protocol" +) + +func print(f string, args ...interface{}) { + fmt.Printf(f+"\n", args...) +} + +func TestNew(t *testing.T) { + n, err := NewServer() + if err != nil { + t.Fatal(err) + } + t.Logf("n is %v", n) +} + +// a simple prototype file system. +type makeit struct { + n string // name + m os.FileMode // mode + s string // for symlinks or content +} + +var tests = []makeit{ + { + n: "ro", + m: 0444, + s: "", + }, + { + n: "rw", + m: 0666, + s: "", + }, + { + n: "wo", + m: 0222, + s: "", + }, +} + +func TestMount(t *testing.T) { + /* Create the simple file system. */ + tmpdir, err := ioutil.TempDir(os.TempDir(), "hi.dir") + if err != nil { + t.Fatalf("%v", err) + } + + for i := range tests { + if err := ioutil.WriteFile(path.Join(tmpdir, tests[i].n), []byte("hi"), tests[i].m); err != nil { + t.Fatalf("%v", err) + } + } + + p, p2 := net.Pipe() + + c, err := protocol.NewClient(func(c *protocol.Client) error { + c.FromNet, c.ToNet = p, p + return nil + }, + func(c *protocol.Client) error { + c.Msize = 8192 + c.Trace = print //t.Logf + return nil + }) + if err != nil { + t.Fatalf("%v", err) + } + t.Logf("Client is %v", c.String()) + + fs, err := NewServer() + if err != nil { + t.Fatal(err) + } + n, err := protocol.NewServer(fs, protocol.Trace(print)) + if err != nil { + t.Fatal(err) + } + + if err := n.Accept(p2); err != nil { + t.Fatalf("Accept: want nil, got %v", err) + } + + t.Logf("n is %v", n) + + m, v, err := c.CallTversion(8000, "9P2000") + if err != nil { + t.Fatalf("CallTversion: want nil, got %v", err) + } + t.Logf("CallTversion: msize %v version %v", m, v) + + t.Logf("Server is %v", n.String()) + + a, err := c.CallTattach(0, protocol.NOFID, "/", "") + if err != nil { + t.Fatalf("CallTattach: want nil, got %v", err) + } + + t.Logf("Attach is %v", a) + w, err := c.CallTwalk(0, 1, []string{"hi", "there"}) + if err == nil { + t.Fatalf("CallTwalk(0,1,[\"hi\", \"there\"]): want err, got nil") + } + if len(w) > 0 { + t.Fatalf("CallTwalk(0,1,[\"hi\", \"there\"]): want 0 QIDs, got %v", w) + } + t.Logf("Walk is %v", w) + ro := strings.Split(path.Join(tmpdir, "ro"), "/") + + w, err = c.CallTwalk(0, 1, ro) + if err != nil { + t.Fatalf("CallTwalk(0,1,%v): want nil, got %v", ro, err) + } + t.Logf("Walk is %v", w) + + of, _, err := c.CallTopen(22, protocol.OREAD) + if err == nil { + t.Fatalf("CallTopen(22, protocol.OREAD): want err, got nil") + } + of, _, err = c.CallTopen(1, protocol.OWRITE) + if err == nil { + t.Fatalf("CallTopen(0, protocol.OWRITE): want err, got nil") + } + of, _, err = c.CallTopen(1, protocol.OREAD) + if err != nil { + t.Fatalf("CallTopen(1, protocol.OREAD): want nil, got %v", nil) + } + t.Logf("Open is %v", of) + + b, err := c.CallTread(22, 0, 0) + if err == nil { + t.Fatalf("CallTread(22, 0, 0): want err, got nil") + } + b, err = c.CallTread(1, 1, 1) + if err != nil { + t.Fatalf("CallTread(1, 1, 1): want nil, got %v", err) + } + t.Logf("read is %v", string(b)) + + /* make sure Twrite fails */ + if _, err = c.CallTwrite(1, 0, b); err == nil { + t.Fatalf("CallTwrite(1, 0, b): want err, got nil") + } + + d, err := c.CallTstat(1) + if err != nil { + t.Fatalf("CallTstat(1): want nil, got %v", err) + } + t.Logf("stat is %v", d) + + d, err = c.CallTstat(22) + if err == nil { + t.Fatalf("CallTstat(22): want err, got nil)") + } + t.Logf("stat is %v", d) + + if err := c.CallTclunk(22); err == nil { + t.Fatalf("CallTclunk(22): want err, got nil") + } + if err := c.CallTclunk(1); err != nil { + t.Fatalf("CallTclunk(1): want nil, got %v", err) + } + if _, err := c.CallTread(1, 1, 22); err == nil { + t.Fatalf("CallTread(1, 1, 22) after clunk: want err, got nil") + } + + d, err = c.CallTstat(1) + if err == nil { + t.Fatalf("CallTstat(1): after clunk: want err, got nil") + } + t.Logf("stat is %v", d) + + // fun with write + rw := strings.Split(path.Join(tmpdir, "rw"), "/") + w, err = c.CallTwalk(0, 1, rw) + if err != nil { + t.Fatalf("CallTwalk(0,1,%v): want nil, got %v", rw, err) + } + t.Logf("Walk is %v", w) + + of, _, err = c.CallTopen(1, protocol.OREAD) + if err != nil { + t.Fatalf("CallTopen(1, protocol.OREAD): want nil, got %v", nil) + } + if err := c.CallTclunk(1); err != nil { + t.Fatalf("CallTclunk(1): want nil, got %v", err) + } + w, err = c.CallTwalk(0, 1, rw) + if err != nil { + t.Fatalf("CallTwalk(0,1,%v): want nil, got %v", rw, err) + } + t.Logf("Walk is %v", w) + + of, _, err = c.CallTopen(1, protocol.OWRITE) + if err != nil { + t.Fatalf("CallTopen(0, protocol.OWRITE): want nil, got %v", err) + } + t.Logf("open OWRITE of is %v", of) + if _, err = c.CallTwrite(1, 1, []byte("there")); err != nil { + t.Fatalf("CallTwrite(1, 0, \"there\"): want nil, got %v", err) + } + if _, err = c.CallTwrite(22, 1, []byte("there")); err == nil { + t.Fatalf("CallTwrite(22, 1, \"there\"): want err, got nil") + } + + // readdir test. + w, err = c.CallTwalk(0, 2, strings.Split(tmpdir, "/")) + if err != nil { + t.Fatalf("CallTwalk(0,2,strings.Split(tmpdir, \"/\")): want nil, got %v", err) + } + t.Logf("Walk is %v", w) + of, _, err = c.CallTopen(2, protocol.OWRITE) + if err == nil { + t.Fatalf("CallTopen(2, protocol.OWRITE) on /: want err, got nil") + } + of, _, err = c.CallTopen(2, protocol.ORDWR) + if err == nil { + t.Fatalf("CallTopen(2, protocol.ORDWR) on /: want err, got nil") + } + of, _, err = c.CallTopen(2, protocol.OREAD) + if err != nil { + t.Fatalf("CallTopen(1, protocol.OREAD): want nil, got %v", nil) + } + var o protocol.Offset + var iter int + for iter < 10 { + iter++ + b, err = c.CallTread(2, o, 256) + // EOF is indicated by a zero length read, not an actual error + if len(b) == 0 { + break + } + if err != nil { + t.Fatalf("CallTread(2, 0, 256): want nil, got %v", err) + } + + dent, err := protocol.Unmarshaldir(bytes.NewBuffer(b)) + if err != nil { + t.Errorf("Unmarshalldir: want nil, got %v", err) + } + t.Logf("dir read is %v", dent) + o += protocol.Offset(len(b)) + } + + if iter > 9 { + t.Errorf("Too many reads from the directory: want 3, got %v", iter) + } + if err := c.CallTclunk(2); err != nil { + t.Fatalf("CallTclunk(1): want nil, got %v", err) + } + // Create tests + w, err = c.CallTwalk(0, 3, []string{}) + if err != nil { + t.Fatalf("CallTwalk(0,3,[]string{}): want nil, got %v", err) + } + w, err = c.CallTwalk(3, 3, strings.Split(tmpdir, "/")) + if err != nil { + t.Fatalf("CallTwalk(0,3,[]string{}): want nil, got %v", err) + } + t.Logf("Walk is %v", w) + of, _, err = c.CallTcreate(3, "xxx", 077, 0) + if err != nil { + t.Fatalf("CallTcreate(\"xxx\", 077, 0): want nil, got %v", err) + } + xxx := path.Join(tmpdir, "xxx") + fi, err := os.Stat(xxx) + if err != nil { + t.Fatalf("After create, check %v: want nil, got %v", xxx, err) + } + t.Logf("Stat of created file: %v", fi) + + // Test mkdir + w, err = c.CallTwalk(0, 4, strings.Split(tmpdir, "/")) + if err != nil { + t.Fatalf("CallTwalk(0,4,%v): want nil, got %v", tmpdir, err) + } + of, _, err = c.CallTcreate(4, "yyy", protocol.DMDIR|0777, 0) + if err != nil { + t.Fatalf("CallTcreate(\"yyy\", 0777, 0): want nil, got %v", err) + } + yyy := path.Join(tmpdir, "yyy") + fi, err = os.Stat(yyy) + if err != nil { + t.Fatalf("After create, check %v: want nil, got %v", yyy, err) + } + if !fi.IsDir() { + t.Fatalf("After mkdir, %v, not a directory", yyy) + } + t.Logf("Stat of created file: %v", fi) + + // test remove + if err = c.CallTremove(3); err != nil { + t.Fatalf("CallTremove(3) failed: want nil, got %v", err) + } + if err = c.CallTclunk(3); err == nil { + t.Errorf("CallTclunk(3) failed: want err, got nil") + } + if _, err := os.Stat(xxx); err == nil { + t.Fatalf("After remove(%v); stat returns nil, not err", xxx) + } + if err = c.CallTremove(4); err != nil { + t.Fatalf("CallTremove(4) failed: want nil, got %v", err) + } + if _, err := os.Stat(yyy); err == nil { + t.Fatalf("After remove(%v); stat returns nil, not err", yyy) + } +} diff --git a/src/vendor/github.com/Harvey-OS/ninep/filesystem/filesystem_unix.go b/src/vendor/github.com/Harvey-OS/ninep/filesystem/filesystem_unix.go new file mode 100644 index 000000000..32add46d7 --- /dev/null +++ b/src/vendor/github.com/Harvey-OS/ninep/filesystem/filesystem_unix.go @@ -0,0 +1,18 @@ +// Copyright 2012 The Ninep Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// This code is imported from the old ninep repo, +// with some changes. + +// +build !windows + +package ufs + +import "io" + +// resetDir seeks to the beginning of the file so that the file list can be +// read again. +func resetDir(f *file) error { + _, err := f.file.Seek(0, io.SeekStart) + return err +} diff --git a/src/vendor/github.com/Harvey-OS/ninep/filesystem/filesystem_windows.go b/src/vendor/github.com/Harvey-OS/ninep/filesystem/filesystem_windows.go new file mode 100644 index 000000000..70cc38833 --- /dev/null +++ b/src/vendor/github.com/Harvey-OS/ninep/filesystem/filesystem_windows.go @@ -0,0 +1,25 @@ +// Copyright 2012 The Ninep Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// This code is imported from the old ninep repo, +// with some changes. + +// +build windows + +package ufs + +import "os" + +// resetDir closes the underlying file and reopens it so it can be read again. +// This is because Windows doesn't seem to support calling Seek on a directory +// handle. +func resetDir(f *file) error { + f2, err := os.OpenFile(f.fullName, os.O_RDONLY, 0) + if err != nil { + return err + } + + f.file.Close() + f.file = f2 + return nil +} diff --git a/src/vendor/github.com/Harvey-OS/ninep/filesystem/ninep.go b/src/vendor/github.com/Harvey-OS/ninep/filesystem/ninep.go new file mode 100644 index 000000000..bb119c20b --- /dev/null +++ b/src/vendor/github.com/Harvey-OS/ninep/filesystem/ninep.go @@ -0,0 +1,81 @@ +// Copyright 2012 The Ninep Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// This code is imported from the old ninep repo, +// with some changes. + +package ufs + +import ( + "flag" + "os" + + "github.com/Harvey-OS/ninep/protocol" +) + +var ( + user = flag.String("user", "harvey", "Default user name") +) + +func modeToUnixFlags(mode protocol.Mode) int { + ret := int(0) + switch mode & 3 { + case protocol.OREAD: + ret = os.O_RDONLY + break + + case protocol.ORDWR: + ret = os.O_RDWR + break + + case protocol.OWRITE: + ret = os.O_WRONLY + break + + case protocol.OEXEC: + ret = os.O_RDONLY + break + } + + if mode&protocol.OTRUNC != 0 { + ret |= os.O_TRUNC + } + + return ret +} + +func dirToQIDType(d os.FileInfo) uint8 { + ret := uint8(0) + if d.IsDir() { + ret |= protocol.QTDIR + } + + if d.Mode()&os.ModeSymlink != 0 { + ret |= protocol.QTSYMLINK + } + + return ret +} + +func dirTo9p2000Mode(d os.FileInfo) uint32 { + ret := uint32(d.Mode() & 0777) + if d.IsDir() { + ret |= protocol.DMDIR + } + return ret +} + +func dirTo9p2000Dir(fi os.FileInfo) (*protocol.Dir, error) { + d := &protocol.Dir{} + d.QID = fileInfoToQID(fi) + d.Mode = dirTo9p2000Mode(fi) + // TODO: use info on systems that have it. + d.Atime = uint32(fi.ModTime().Unix()) // uint32(atime(sysMode).Unix()) + d.Mtime = uint32(fi.ModTime().Unix()) + d.Length = uint64(fi.Size()) + d.Name = fi.Name() + d.User = *user + d.Group = *user + + return d, nil +} diff --git a/src/vendor/github.com/Harvey-OS/ninep/filesystem/ninep_unix.go b/src/vendor/github.com/Harvey-OS/ninep/filesystem/ninep_unix.go new file mode 100644 index 000000000..3b69c509e --- /dev/null +++ b/src/vendor/github.com/Harvey-OS/ninep/filesystem/ninep_unix.go @@ -0,0 +1,34 @@ +// Copyright 2012 The Ninep Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// This code is imported from the old ninep repo, +// with some changes. + +// +build !windows + +package ufs + +import ( + "os" + "syscall" + + "github.com/Harvey-OS/ninep/protocol" +) + +func fileInfoToQID(d os.FileInfo) protocol.QID { + var qid protocol.QID + sysif := d.Sys() + + // on systems with inodes, use it. + if sysif != nil { + stat := sysif.(*syscall.Stat_t) + qid.Path = uint64(stat.Ino) + } else { + qid.Path = uint64(d.ModTime().UnixNano()) + } + + qid.Version = uint32(d.ModTime().UnixNano() / 1000000) + qid.Type = dirToQIDType(d) + + return qid +} diff --git a/src/vendor/github.com/Harvey-OS/ninep/filesystem/ninep_windows.go b/src/vendor/github.com/Harvey-OS/ninep/filesystem/ninep_windows.go new file mode 100644 index 000000000..75e1094ad --- /dev/null +++ b/src/vendor/github.com/Harvey-OS/ninep/filesystem/ninep_windows.go @@ -0,0 +1,25 @@ +// Copyright 2012 The Ninep Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// This code is imported from the old ninep repo, +// with some changes. + +// +build windows + +package ufs + +import ( + "os" + + "github.com/Harvey-OS/ninep/protocol" +) + +func fileInfoToQID(d os.FileInfo) protocol.QID { + var qid protocol.QID + + qid.Path = uint64(d.ModTime().UnixNano()) + qid.Version = uint32(d.ModTime().UnixNano() / 1000000) + qid.Type = dirToQIDType(d) + + return qid +} diff --git a/src/vendor/github.com/Harvey-OS/ninep/protocol/client.go b/src/vendor/github.com/Harvey-OS/ninep/protocol/client.go new file mode 100644 index 000000000..9f67f3cf0 --- /dev/null +++ b/src/vendor/github.com/Harvey-OS/ninep/protocol/client.go @@ -0,0 +1,175 @@ +// Copyright 2012 The Ninep Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// This code is imported from the old ninep repo, +// with some changes. + +package protocol + +import ( + "bytes" + "fmt" + "io" + "log" + "runtime" + "sync/atomic" +) + +// Client implements a 9p client. It has a chan containing all tags, +// a scalar FID which is incremented to provide new FIDS (all FIDS for a given +// client are unique), an array of MaxTag-2 RPC structs, a ReadWriteCloser +// for IO, and two channels for a server goroutine: one down which RPCalls are +// pushed and another from which RPCReplys return. +// Once a client is marked Dead all further requests to it will fail. +// The ToNet/FromNet are separate so we can use io.Pipe for testing. +type Client struct { + Tags chan Tag + FID uint64 + RPC []*RPCCall + ToNet io.WriteCloser + FromNet io.ReadCloser + FromClient chan *RPCCall + FromServer chan *RPCReply + Msize uint32 + Dead bool + Trace Tracer +} + +func NewClient(opts ...ClientOpt) (*Client, error) { + var c = &Client{} + + c.Tags = make(chan Tag, NumTags) + for i := 1; i < int(NOTAG); i++ { + c.Tags <- Tag(i) + } + c.FID = 1 + c.RPC = make([]*RPCCall, NumTags) + for _, o := range opts { + if err := o(c); err != nil { + return nil, err + } + } + c.FromClient = make(chan *RPCCall, NumTags) + c.FromServer = make(chan *RPCReply) + go c.IO() + go c.readNetPackets() + return c, nil +} + +// GetTag gets a tag to be used to identify a message. +func (c *Client) GetTag() Tag { + t := <-c.Tags + if false { + runtime.SetFinalizer(&t, func(t *Tag) { + c.Tags <- *t + }) + } + return t +} + +// GetFID gets a fid to be used to identify a resource for a 9p client. +// For a given lifetime of a 9p client, FIDS are unique (i.e. not reused as in +// many 9p client libraries). +func (c *Client) GetFID() FID { + return FID(atomic.AddUint64(&c.FID, 1)) +} + +func (c *Client) readNetPackets() { + if c.FromNet == nil { + if c.Trace != nil { + c.Trace("c.FromNet is nil, marking dead") + } + c.Dead = true + return + } + defer c.FromNet.Close() + defer close(c.FromServer) + if c.Trace != nil { + c.Trace("Starting readNetPackets") + } + for !c.Dead { + l := make([]byte, 7) + if c.Trace != nil { + c.Trace("Before read") + } + + if n, err := c.FromNet.Read(l); err != nil || n < 7 { + log.Printf("readNetPackets: short read: %v", err) + c.Dead = true + return + } + if c.Trace != nil { + c.Trace("Server reads %v", l) + } + s := int64(l[0]) + int64(l[1])<<8 + int64(l[2])<<16 + int64(l[3])<<24 + b := bytes.NewBuffer(l) + r := io.LimitReader(c.FromNet, s-7) + if _, err := io.Copy(b, r); err != nil { + log.Printf("readNetPackets: short read: %v", err) + c.Dead = true + return + } + if c.Trace != nil { + c.Trace("readNetPackets: got %v, len %d, sending to IO", RPCNames[MType(l[4])], b.Len()) + } + c.FromServer <- &RPCReply{b: b.Bytes()} + } + if c.Trace != nil { + c.Trace("Client %v is all done", c) + } + +} + +func (c *Client) IO() { + go func() { + for { + r := <-c.FromClient + t := <-c.Tags + if c.Trace != nil { + c.Trace(fmt.Sprintf("Tag for request is %v", t)) + } + r.b[5] = uint8(t) + r.b[6] = uint8(t >> 8) + if c.Trace != nil { + c.Trace(fmt.Sprintf("Tag for request is %v", t)) + } + c.RPC[int(t)-1] = r + if c.Trace != nil { + c.Trace("Write %v to ToNet", r.b) + } + if _, err := c.ToNet.Write(r.b); err != nil { + c.Dead = true + log.Fatalf("Write to server: %v", err) + return + } + } + }() + + for { + r := <-c.FromServer + if c.Trace != nil { + c.Trace("Read %v FromServer", r.b) + } + t := Tag(r.b[5]) | Tag(r.b[6])<<8 + if c.Trace != nil { + c.Trace(fmt.Sprintf("Tag for reply is %v", t)) + } + if t < 1 { + panic(fmt.Sprintf("tag %d < 1", t)) + } + if int(t-1) >= len(c.RPC) { + panic(fmt.Sprintf("tag %d >= len(c.RPC) %d", t, len(c.RPC))) + } + c.Trace("RPC %v ", c.RPC[t-1]) + rrr := c.RPC[t-1] + c.Trace("rrr %v ", rrr) + rrr.Reply <- r.b + c.Tags <- t + } +} + +func (c *Client) String() string { + z := map[bool]string{false: "Alive", true: "Dead"} + return fmt.Sprintf("%v tags available, Msize %v, %v FromNet %v ToNet %v", len(c.Tags), c.Msize, z[c.Dead], + c.FromNet, c.ToNet) +} diff --git a/src/vendor/github.com/Harvey-OS/ninep/protocol/gen.go b/src/vendor/github.com/Harvey-OS/ninep/protocol/gen.go new file mode 100644 index 000000000..17e6cf2ba --- /dev/null +++ b/src/vendor/github.com/Harvey-OS/ninep/protocol/gen.go @@ -0,0 +1,556 @@ +// Copyright 2015 The Ninep Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +// gen is an rpc generator for the Plan 9 style XDR. It uses the types and structs +// defined in types. go. A core function, gen, creates the needed lists of +// parameters, code, and variable list for calling a Marshall function; and the +// return declaration, code, and return value list for an unmarshall function. +// You can think of an RPC as a pipline: +// marshal(parms) -> b[]byte over a network -> unmarshal -> dispatch -> reply(parms) -> unmarshal +// Since we have T messages and R messages in 9p, we adopt the following naming convention for, e.g., Version: +// MarshalTPktVersion +// UnmarshalTpktVersion +// MarshalRPktVersion +// UnmarshalRPktVersion +// +// A caller uses the MarshalT* and UnmarshallR* information. A dispatcher +// uses the UnmarshalT* and MarshalR* information. +// Hence the caller needs the call MarshalT params, and UnmarshalR* returns; +// a dispatcher needs the UnmarshalT returns, and the MarshalR params. +package main + +import ( + "bytes" + "flag" + "fmt" + "io" + "io/ioutil" + "log" + "reflect" + "text/template" + + "github.com/Harvey-OS/ninep/protocol" +) + +const ( + header = ` +package protocol +import ( +"bytes" +"fmt" +_ "log" +) +` +) + +type emitter struct { + Name string + MFunc string + // Encoders always return []byte + MParms *bytes.Buffer + MList *bytes.Buffer + MLsep string + MCode *bytes.Buffer + + // Decoders always take []byte as parms. + UFunc string + UList *bytes.Buffer + UCode *bytes.Buffer + URet *bytes.Buffer + inBWrite bool +} + +type call struct { + T *emitter + R *emitter +} + +type pack struct { + n string + t interface{} + tn string + r interface{} + rn string +} + +const ( + serverError = `func ServerError (b *bytes.Buffer, s string) { + var u [8]byte + // This can't really happen. + if _, err := b.Read(u[:2]); err != nil { + return + } + t := Tag(uint16(u[0])|uint16(u[1])<<8) + MarshalRerrorPkt (b, t, s) +} +` +) + +var ( + doDebug = flag.Bool("d", false, "Debug prints") + debug = nodebug //log.Printf + packages = []*pack{ + {n: "error", t: protocol.RerrorPkt{}, tn: "Rerror", r: protocol.RerrorPkt{}, rn: "Rerror"}, + {n: "version", t: protocol.TversionPkt{}, tn: "Tversion", r: protocol.RversionPkt{}, rn: "Rversion"}, + {n: "attach", t: protocol.TattachPkt{}, tn: "Tattach", r: protocol.RattachPkt{}, rn: "Rattach"}, + {n: "flush", t: protocol.TflushPkt{}, tn: "Tflush", r: protocol.RflushPkt{}, rn: "Rflush"}, + {n: "walk", t: protocol.TwalkPkt{}, tn: "Twalk", r: protocol.RwalkPkt{}, rn: "Rwalk"}, + {n: "open", t: protocol.TopenPkt{}, tn: "Topen", r: protocol.RopenPkt{}, rn: "Ropen"}, + {n: "create", t: protocol.TcreatePkt{}, tn: "Tcreate", r: protocol.RcreatePkt{}, rn: "Rcreate"}, + {n: "stat", t: protocol.TstatPkt{}, tn: "Tstat", r: protocol.RstatPkt{}, rn: "Rstat"}, + {n: "wstat", t: protocol.TwstatPkt{}, tn: "Twstat", r: protocol.RwstatPkt{}, rn: "Rwstat"}, + {n: "clunk", t: protocol.TclunkPkt{}, tn: "Tclunk", r: protocol.RclunkPkt{}, rn: "Rclunk"}, + {n: "remove", t: protocol.TremovePkt{}, tn: "Tremove", r: protocol.RremovePkt{}, rn: "Rremove"}, + {n: "read", t: protocol.TreadPkt{}, tn: "Tread", r: protocol.RreadPkt{}, rn: "Rread"}, + {n: "write", t: protocol.TwritePkt{}, tn: "Twrite", r: protocol.RwritePkt{}, rn: "Rwrite"}, + } + msfunc = template.Must(template.New("ms").Parse(`func Marshal{{.MFunc}} (b *bytes.Buffer, {{.MParms}}) { +var l uint64 +b.Reset() +b.Write([]byte{0,0,}) +{{.MCode}} +l = uint64(b.Len()) - 2 +copy(b.Bytes(), []byte{uint8(l), uint8(l>>8)}) +return +} +`)) + usfunc = template.Must(template.New("us").Parse(`func Unmarshal{{.UFunc}} (b *bytes.Buffer) ({{.URet}} err error) { +var u [8]uint8 +var l uint64 +_ = b.Next(2) // eat the length too +{{.UCode}} +return +} +`)) + + mfunc = template.Must(template.New("mt").Parse(`func Marshal{{.MFunc}}Pkt (b *bytes.Buffer, t Tag, {{.MParms}}) { +var l uint64 +b.Reset() +b.Write([]byte{0,0,0,0, +uint8({{.Name}}), +byte(t), byte(t>>8), +{{.MCode}} +{ +l = uint64(b.Len()) +copy(b.Bytes(), []byte{uint8(l), uint8(l>>8), uint8(l>>16), uint8(l>>24)}) +} +return +} +`)) + ufunc = template.Must(template.New("mr").Parse(`func Unmarshal{{.UFunc}}Pkt (b *bytes.Buffer) ({{.URet}} t Tag, err error) { +var u [8]uint8 +var l uint64 +if _, err = b.Read(u[:2]); err != nil { +err = fmt.Errorf("pkt too short for Tag; need 2, have %d", b.Len()) +return +} +l = uint64(u[0]) | uint64(u[1])<<8 +t = Tag(l) +{{.UCode}} +if b.Len() > 0 { +err = fmt.Errorf("Packet too long: %d bytes left over after decode", b.Len()) +} +return +} +`)) + sfunc = template.Must(template.New("s").Parse(`func (s *Server) Srv{{.R.UFunc}}(b*bytes.Buffer) (err error) { + {{.T.MList}}{{.T.MLsep}} t, err := Unmarshal{{.T.MFunc}}Pkt(b) + //if err != nil { + //} + if {{.R.MList}}{{.R.MLsep}} err := s.NS.{{.R.MFunc}}({{.T.MList}}); err != nil { + MarshalRerrorPkt(b, t, fmt.Sprintf("%v", err)) +} else { + Marshal{{.R.MFunc}}Pkt(b, t, {{.R.MList}}) +} + return nil +} +`)) + cfunc = template.Must(template.New("s").Parse(` +func (c *Client)Call{{.T.MFunc}} ({{.T.MParms}}) ({{.R.URet}} err error) { +var b = bytes.Buffer{} +if c.Trace != nil {c.Trace("%v", {{.T.MFunc}})} +t := Tag(0) +r := make (chan []byte) +if c.Trace != nil { c.Trace(":tag %v, FID %v", t, c.FID)} +Marshal{{.T.MFunc}}Pkt(&b, t, {{.T.MList}}) +c.FromClient <- &RPCCall{b: b.Bytes(), Reply: r} +bb := <-r +if MType(bb[4]) == Rerror { + s, _, err := UnmarshalRerrorPkt(bytes.NewBuffer(bb[5:])) + if err != nil { + return {{.R.UList}} err + } + return {{.R.UList}} fmt.Errorf("%v", s) +} else { + {{.R.MList}}{{.R.MLsep}} _, err = Unmarshal{{.R.UFunc}}Pkt(bytes.NewBuffer(bb[5:])) +} +return {{.R.UList}} err +} +`)) +) + +func nodebug(string, ...interface{}) { +} + +func newCall(p *pack) *call { + c := &call{} + // We set inBWrite to true because the prologue marshal code sets up some default writes to b + c.T = &emitter{"T" + p.n, p.tn, &bytes.Buffer{}, &bytes.Buffer{}, "", &bytes.Buffer{}, p.tn, &bytes.Buffer{}, &bytes.Buffer{}, &bytes.Buffer{}, true} + c.R = &emitter{"R" + p.n, p.rn, &bytes.Buffer{}, &bytes.Buffer{}, "", &bytes.Buffer{}, p.rn, &bytes.Buffer{}, &bytes.Buffer{}, &bytes.Buffer{}, true} + return c +} + +func emitEncodeInt(v interface{}, n string, l int, e *emitter) { + debug("emit %v, %v", n, l) + for i := 0; i < l; i++ { + if !e.inBWrite { + e.MCode.WriteString("\tb.Write([]byte{") + e.inBWrite = true + } + e.MCode.WriteString(fmt.Sprintf("\tuint8(%v>>%v),\n", n, i*8)) + } +} + +func emitDecodeInt(v interface{}, n string, l int, e *emitter) { + t := reflect.ValueOf(v).Type().Name() + debug("emit reflect.ValueOf(v) %s %v, %v", t, n, l) + e.UCode.WriteString(fmt.Sprintf("\tif _, err = b.Read(u[:%v]); err != nil {\n\t\terr = fmt.Errorf(\"pkt too short for uint%v: need %v, have %%d\", b.Len())\n\treturn\n\t}\n", l, l*8, l)) + e.UCode.WriteString(fmt.Sprintf("\t%v = %s(u[0])\n", n, t)) + for i := 1; i < l; i++ { + e.UCode.WriteString(fmt.Sprintf("\t%v |= %s(u[%d])<<%v\n", n, t, i, i*8)) + } +} + +// TODO: templates. +func emitEncodeString(v interface{}, n string, e *emitter) { + if !e.inBWrite { + e.MCode.WriteString("\tb.Write([]byte{") + e.inBWrite = true + } + e.MCode.WriteString(fmt.Sprintf("\tuint8(len(%v)),uint8(len(%v)>>8),\n", n, n)) + e.MCode.WriteString("\t})\n") + e.inBWrite = false + e.MCode.WriteString(fmt.Sprintf("\tb.Write([]byte(%v))\n", n)) +} + +func emitDecodeString(n string, e *emitter) { + var l uint64 + emitDecodeInt(l, "l", 2, e) + e.UCode.WriteString(fmt.Sprintf("\tif b.Len() < int(l) {\n\t\terr = fmt.Errorf(\"pkt too short for string: need %%d, have %%d\", l, b.Len())\n\treturn\n\t}\n")) + e.UCode.WriteString(fmt.Sprintf("\t%v = string(b.Bytes()[:l])\n", n)) + e.UCode.WriteString("\t_ = b.Next(int(l))\n") +} + +func genEncodeStruct(v interface{}, n string, e *emitter) error { + debug("genEncodeStruct(%T, %v, %v)", v, n, e) + t := reflect.ValueOf(v) + for i := 0; i < t.NumField(); i++ { + f := t.Field(i) + fn := t.Type().Field(i).Name + debug("genEncodeStruct %T n %v field %d %v %v\n", t, n, i, f.Type(), f.Type().Name()) + genEncodeData(f.Interface(), n+fn, e) + } + return nil +} + +func genDecodeStruct(v interface{}, n string, e *emitter) error { + debug("genDecodeStruct(%T, %v, %v)", v, n, "") + t := reflect.ValueOf(v) + for i := 0; i < t.NumField(); i++ { + f := t.Field(i) + fn := t.Type().Field(i).Name + debug("genDecodeStruct %T n %v field %d %v %v\n", t, n, i, f.Type(), f.Type().Name()) + genDecodeData(f.Interface(), n+fn, e) + } + return nil +} + +// TODO: there has to be a smarter way to do slices. +func genEncodeSlice(v interface{}, n string, e *emitter) error { + t := fmt.Sprintf("%T", v) + switch t { + case "[]string": + var u uint16 + emitEncodeInt(u, fmt.Sprintf("len(%v)", n), 2, e) + if e.inBWrite { + e.MCode.WriteString("\t})\n") + e.inBWrite = false + } + e.MCode.WriteString(fmt.Sprintf("for i := range %v {\n", n)) + var s string + genEncodeData(s, n+"[i]", e) + e.MCode.WriteString("}\n") + case "[]protocol.QID": + var u uint16 + emitEncodeInt(u, fmt.Sprintf("len(%v)", n), 2, e) + if e.inBWrite { + e.MCode.WriteString("\t})\n") + e.inBWrite = false + } + e.MCode.WriteString(fmt.Sprintf("for i := range %v {\n", n)) + genEncodeData(protocol.QID{}, n+"[i]", e) + e.MCode.WriteString("\t})\n") + e.inBWrite = false + e.MCode.WriteString("}\n") + case "[]byte", "[]uint8": + var u uint32 + emitEncodeInt(u, fmt.Sprintf("len(%v)", n), 4, e) + if e.inBWrite { + e.MCode.WriteString("\t})\n") + e.inBWrite = false + } + e.MCode.WriteString(fmt.Sprintf("\tb.Write(%v)\n", n)) + case "[]protocol.DataCnt16": + var u uint16 + emitEncodeInt(u, fmt.Sprintf("len(%v)", n), 2, e) + if e.inBWrite { + e.MCode.WriteString("\t})\n") + e.inBWrite = false + } + e.MCode.WriteString(fmt.Sprintf("\tb.Write(%v)\n", n)) + default: + log.Printf("genEncodeSlice: Can't handle slice of %s", t) + } + return nil +} + +func genDecodeSlice(v interface{}, n string, e *emitter) error { + // Sadly, []byte is not encoded like []everything else. + t := fmt.Sprintf("%T", v) + switch t { + case "[]string": + var u uint64 + emitDecodeInt(u, "l", 2, e) + e.UCode.WriteString(fmt.Sprintf("\t%v = make([]string, l)\n", n)) + e.UCode.WriteString(fmt.Sprintf("for i := range %v {\n", n)) + var s string + genDecodeData(s, n+"[i]", e) + e.UCode.WriteString("}\n") + case "[]protocol.QID": + var u uint64 + emitDecodeInt(u, "l", 2, e) + e.UCode.WriteString(fmt.Sprintf("\t%v = make([]QID, l)\n", n)) + e.UCode.WriteString(fmt.Sprintf("for i := range %v {\n", n)) + genDecodeData(protocol.QID{}, n+"[i]", e) + e.UCode.WriteString("}\n") + case "[]byte", "[]uint8": + var u uint64 + emitDecodeInt(u, "l", 4, e) + e.UCode.WriteString(fmt.Sprintf("\t%v = b.Bytes()[:l]\n", n)) + e.UCode.WriteString("\t_ = b.Next(int(l))\n") + case "[]protocol.DataCnt16": + var u uint64 + emitDecodeInt(u, "l", 2, e) + e.UCode.WriteString(fmt.Sprintf("\t%v = b.Bytes()[:l]\n", n)) + e.UCode.WriteString("\t_ = b.Next(int(l))\n") + default: + log.Printf("genDecodeSlice: Can't handle slice of %v", t) + } + return nil +} + +func genEncodeData(v interface{}, n string, e *emitter) error { + debug("genEncodeData(%T, %v, %v)", v, n, e) + s := reflect.ValueOf(v).Kind() + switch s { + case reflect.Uint8: + emitEncodeInt(v, n, 1, e) + case reflect.Uint16: + emitEncodeInt(v, n, 2, e) + case reflect.Uint32, reflect.Int32: + emitEncodeInt(v, n, 4, e) + case reflect.Uint64: + emitEncodeInt(v, n, 8, e) + case reflect.String: + emitEncodeString(v, n, e) + case reflect.Struct: + if n != "" { + n = n + "." + } + return genEncodeStruct(v, n, e) + case reflect.Slice: + return genEncodeSlice(v, n, e) + default: + log.Printf("genEncodeData: Can't handle type %T", v) + } + return nil +} + +func genDecodeData(v interface{}, n string, e *emitter) error { + debug("genEncodeData(%T, %v, %v)", v, n, "") //e) + s := reflect.ValueOf(v).Kind() + switch s { + case reflect.Uint8: + emitDecodeInt(v, n, 1, e) + case reflect.Uint16: + emitDecodeInt(v, n, 2, e) + case reflect.Uint32, reflect.Int32: + emitDecodeInt(v, n, 4, e) + case reflect.Uint64: + emitDecodeInt(v, n, 8, e) + case reflect.String: + emitDecodeString(n, e) + case reflect.Struct: + if n != "" { + n = n + "." + } + debug("----------> call gendecodstruct(%v, %v, e)", v, n) + return genDecodeStruct(v, n, e) + case reflect.Slice: + return genDecodeSlice(v, n, e) + default: + log.Printf("genDecodeData: Can't handle type %T", v) + } + return nil +} + +// Well, it's odd, but Name sometimes comes back empty. +// I don't know why. +func tn(f reflect.Value) string { + n := f.Type().Name() + if n == "" { + t := f.Type().String() + switch t { + case "[]protocol.QID": + n = "[]QID" + case "[]protocol.DataCnt16": + n = "[]byte" + default: + n = t + } + } + return n +} + +// genParms writes the parameters for declarations (name and type) +// a list of names (for calling the encoder) +func genParms(v interface{}, n string, e *emitter) error { + t := reflect.ValueOf(v) + for i := 0; i < t.NumField(); i++ { + f := t.Field(i) + fn := t.Type().Field(i).Name + e.MList.WriteString(e.MLsep + fn) + e.MParms.WriteString(e.MLsep + fn + " " + tn(f)) + e.MLsep = ", " + } + return nil +} + +// genRets writes the rets for declarations (name and type) +// a list of names +func genRets(v interface{}, n string, e *emitter) error { + t := reflect.ValueOf(v) + for i := 0; i < t.NumField(); i++ { + f := t.Field(i) + fn := t.Type().Field(i).Name + e.UList.WriteString(fn + ", ") + e.URet.WriteString(fn + " " + tn(f) + ", ") + } + return nil +} + +// genMsgRPC generates the call and reply declarations and marshalers. We don't think of encoders as too separate +// because the 9p encoding is so simple. +func genMsgRPC(b io.Writer, p *pack) (*call, error) { + + c := newCall(p) + + if err := genEncodeStruct(p.r, "", c.R); err != nil { + log.Fatalf("%v", err) + } + if c.R.inBWrite { + c.R.MCode.WriteString("\t})\n") + c.R.inBWrite = false + } + if err := genDecodeStruct(p.r, "", c.R); err != nil { + log.Fatalf("%v", err) + } + + if err := genEncodeStruct(p.t, "", c.T); err != nil { + log.Fatalf("%v", err) + } + if c.T.inBWrite { + c.T.MCode.WriteString("\t})\n") + c.T.inBWrite = false + } + + if err := genDecodeStruct(p.t, "", c.T); err != nil { + log.Fatalf("%v", err) + } + + if err := genParms(p.t, p.tn, c.T); err != nil { + log.Fatalf("%v", err) + } + + if err := genRets(p.t, p.tn, c.T); err != nil { + log.Fatalf("%v", err) + } + + if err := genParms(p.r, p.rn, c.R); err != nil { + log.Fatalf("%v", err) + } + + if err := genRets(p.r, p.rn, c.R); err != nil { + log.Fatalf("%v", err) + } + + //log.Print("e %v d %v", c.T, c.R) + + // log.Print("------------------", c.T.MParms, "0", c.T.MList, "1", c.R.URet, "2", c.R.UList) + // log.Print("------------------", c.T.MCode) + mfunc.Execute(b, c.R) + ufunc.Execute(b, c.R) + + if p.n == "error" { + return c, nil + } + + mfunc.Execute(b, c.T) + ufunc.Execute(b, c.T) + sfunc.Execute(b, c) + cfunc.Execute(b, c) + return nil, nil + +} + +func main() { + flag.Parse() + if *doDebug { + debug = log.Printf + } + var b = bytes.NewBufferString(header) + for _, p := range packages { + _, err := genMsgRPC(b, p) + if err != nil { + log.Fatalf("%v", err) + } + } + b.WriteString(serverError) + + // yeah, it's a hack. + dir := &emitter{"dir", "dir", &bytes.Buffer{}, &bytes.Buffer{}, "", &bytes.Buffer{}, "dir", &bytes.Buffer{}, &bytes.Buffer{}, &bytes.Buffer{}, false} + if err := genEncodeStruct(protocol.DirPkt{}, "", dir); err != nil { + log.Fatalf("%v", err) + } + if err := genDecodeStruct(protocol.DirPkt{}, "", dir); err != nil { + log.Fatalf("%v", err) + } + if err := genParms(protocol.DirPkt{}, "dir", dir); err != nil { + log.Fatalf("%v", err) + } + + if err := genRets(protocol.DirPkt{}, "dir", dir); err != nil { + log.Fatalf("%v", err) + } + + msfunc.Execute(b, dir) + usfunc.Execute(b, dir) + + if err := ioutil.WriteFile("genout.go", b.Bytes(), 0600); err != nil { + log.Fatalf("%v", err) + } + +} diff --git a/src/vendor/github.com/Harvey-OS/ninep/protocol/genout.go b/src/vendor/github.com/Harvey-OS/ninep/protocol/genout.go new file mode 100644 index 000000000..05107d609 --- /dev/null +++ b/src/vendor/github.com/Harvey-OS/ninep/protocol/genout.go @@ -0,0 +1,1935 @@ + +package protocol +import ( +"bytes" +"fmt" +_ "log" +) +func MarshalRerrorPkt (b *bytes.Buffer, t Tag, Error string) { +var l uint64 +b.Reset() +b.Write([]byte{0,0,0,0, +uint8(Rerror), +byte(t), byte(t>>8), + uint8(len(Error)),uint8(len(Error)>>8), + }) + b.Write([]byte(Error)) + +{ +l = uint64(b.Len()) +copy(b.Bytes(), []byte{uint8(l), uint8(l>>8), uint8(l>>16), uint8(l>>24)}) +} +return +} +func UnmarshalRerrorPkt (b *bytes.Buffer) (Error string, t Tag, err error) { +var u [8]uint8 +var l uint64 +if _, err = b.Read(u[:2]); err != nil { +err = fmt.Errorf("pkt too short for Tag; need 2, have %d", b.Len()) +return +} +l = uint64(u[0]) | uint64(u[1])<<8 +t = Tag(l) + if _, err = b.Read(u[:2]); err != nil { + err = fmt.Errorf("pkt too short for uint16: need 2, have %d", b.Len()) + return + } + l = uint64(u[0]) + l |= uint64(u[1])<<8 + if b.Len() < int(l) { + err = fmt.Errorf("pkt too short for string: need %d, have %d", l, b.Len()) + return + } + Error = string(b.Bytes()[:l]) + _ = b.Next(int(l)) + +if b.Len() > 0 { +err = fmt.Errorf("Packet too long: %d bytes left over after decode", b.Len()) +} +return +} +func MarshalRversionPkt (b *bytes.Buffer, t Tag, RMsize MaxSize, RVersion string) { +var l uint64 +b.Reset() +b.Write([]byte{0,0,0,0, +uint8(Rversion), +byte(t), byte(t>>8), + uint8(RMsize>>0), + uint8(RMsize>>8), + uint8(RMsize>>16), + uint8(RMsize>>24), + uint8(len(RVersion)),uint8(len(RVersion)>>8), + }) + b.Write([]byte(RVersion)) + +{ +l = uint64(b.Len()) +copy(b.Bytes(), []byte{uint8(l), uint8(l>>8), uint8(l>>16), uint8(l>>24)}) +} +return +} +func UnmarshalRversionPkt (b *bytes.Buffer) (RMsize MaxSize, RVersion string, t Tag, err error) { +var u [8]uint8 +var l uint64 +if _, err = b.Read(u[:2]); err != nil { +err = fmt.Errorf("pkt too short for Tag; need 2, have %d", b.Len()) +return +} +l = uint64(u[0]) | uint64(u[1])<<8 +t = Tag(l) + if _, err = b.Read(u[:4]); err != nil { + err = fmt.Errorf("pkt too short for uint32: need 4, have %d", b.Len()) + return + } + RMsize = MaxSize(u[0]) + RMsize |= MaxSize(u[1])<<8 + RMsize |= MaxSize(u[2])<<16 + RMsize |= MaxSize(u[3])<<24 + if _, err = b.Read(u[:2]); err != nil { + err = fmt.Errorf("pkt too short for uint16: need 2, have %d", b.Len()) + return + } + l = uint64(u[0]) + l |= uint64(u[1])<<8 + if b.Len() < int(l) { + err = fmt.Errorf("pkt too short for string: need %d, have %d", l, b.Len()) + return + } + RVersion = string(b.Bytes()[:l]) + _ = b.Next(int(l)) + +if b.Len() > 0 { +err = fmt.Errorf("Packet too long: %d bytes left over after decode", b.Len()) +} +return +} +func MarshalTversionPkt (b *bytes.Buffer, t Tag, TMsize MaxSize, TVersion string) { +var l uint64 +b.Reset() +b.Write([]byte{0,0,0,0, +uint8(Tversion), +byte(t), byte(t>>8), + uint8(TMsize>>0), + uint8(TMsize>>8), + uint8(TMsize>>16), + uint8(TMsize>>24), + uint8(len(TVersion)),uint8(len(TVersion)>>8), + }) + b.Write([]byte(TVersion)) + +{ +l = uint64(b.Len()) +copy(b.Bytes(), []byte{uint8(l), uint8(l>>8), uint8(l>>16), uint8(l>>24)}) +} +return +} +func UnmarshalTversionPkt (b *bytes.Buffer) (TMsize MaxSize, TVersion string, t Tag, err error) { +var u [8]uint8 +var l uint64 +if _, err = b.Read(u[:2]); err != nil { +err = fmt.Errorf("pkt too short for Tag; need 2, have %d", b.Len()) +return +} +l = uint64(u[0]) | uint64(u[1])<<8 +t = Tag(l) + if _, err = b.Read(u[:4]); err != nil { + err = fmt.Errorf("pkt too short for uint32: need 4, have %d", b.Len()) + return + } + TMsize = MaxSize(u[0]) + TMsize |= MaxSize(u[1])<<8 + TMsize |= MaxSize(u[2])<<16 + TMsize |= MaxSize(u[3])<<24 + if _, err = b.Read(u[:2]); err != nil { + err = fmt.Errorf("pkt too short for uint16: need 2, have %d", b.Len()) + return + } + l = uint64(u[0]) + l |= uint64(u[1])<<8 + if b.Len() < int(l) { + err = fmt.Errorf("pkt too short for string: need %d, have %d", l, b.Len()) + return + } + TVersion = string(b.Bytes()[:l]) + _ = b.Next(int(l)) + +if b.Len() > 0 { +err = fmt.Errorf("Packet too long: %d bytes left over after decode", b.Len()) +} +return +} +func (s *Server) SrvRversion(b*bytes.Buffer) (err error) { + TMsize, TVersion, t, err := UnmarshalTversionPkt(b) + //if err != nil { + //} + if RMsize, RVersion, err := s.NS.Rversion(TMsize, TVersion); err != nil { + MarshalRerrorPkt(b, t, fmt.Sprintf("%v", err)) +} else { + MarshalRversionPkt(b, t, RMsize, RVersion) +} + return nil +} + +func (c *Client)CallTversion (TMsize MaxSize, TVersion string) (RMsize MaxSize, RVersion string, err error) { +var b = bytes.Buffer{} +if c.Trace != nil {c.Trace("%v", Tversion)} +t := Tag(0) +r := make (chan []byte) +if c.Trace != nil { c.Trace(":tag %v, FID %v", t, c.FID)} +MarshalTversionPkt(&b, t, TMsize, TVersion) +c.FromClient <- &RPCCall{b: b.Bytes(), Reply: r} +bb := <-r +if MType(bb[4]) == Rerror { + s, _, err := UnmarshalRerrorPkt(bytes.NewBuffer(bb[5:])) + if err != nil { + return RMsize, RVersion, err + } + return RMsize, RVersion, fmt.Errorf("%v", s) +} else { + RMsize, RVersion, _, err = UnmarshalRversionPkt(bytes.NewBuffer(bb[5:])) +} +return RMsize, RVersion, err +} +func MarshalRattachPkt (b *bytes.Buffer, t Tag, QID QID) { +var l uint64 +b.Reset() +b.Write([]byte{0,0,0,0, +uint8(Rattach), +byte(t), byte(t>>8), + uint8(QID.Type>>0), + uint8(QID.Version>>0), + uint8(QID.Version>>8), + uint8(QID.Version>>16), + uint8(QID.Version>>24), + uint8(QID.Path>>0), + uint8(QID.Path>>8), + uint8(QID.Path>>16), + uint8(QID.Path>>24), + uint8(QID.Path>>32), + uint8(QID.Path>>40), + uint8(QID.Path>>48), + uint8(QID.Path>>56), + }) + +{ +l = uint64(b.Len()) +copy(b.Bytes(), []byte{uint8(l), uint8(l>>8), uint8(l>>16), uint8(l>>24)}) +} +return +} +func UnmarshalRattachPkt (b *bytes.Buffer) (QID QID, t Tag, err error) { +var u [8]uint8 +var l uint64 +if _, err = b.Read(u[:2]); err != nil { +err = fmt.Errorf("pkt too short for Tag; need 2, have %d", b.Len()) +return +} +l = uint64(u[0]) | uint64(u[1])<<8 +t = Tag(l) + if _, err = b.Read(u[:1]); err != nil { + err = fmt.Errorf("pkt too short for uint8: need 1, have %d", b.Len()) + return + } + QID.Type = uint8(u[0]) + if _, err = b.Read(u[:4]); err != nil { + err = fmt.Errorf("pkt too short for uint32: need 4, have %d", b.Len()) + return + } + QID.Version = uint32(u[0]) + QID.Version |= uint32(u[1])<<8 + QID.Version |= uint32(u[2])<<16 + QID.Version |= uint32(u[3])<<24 + if _, err = b.Read(u[:8]); err != nil { + err = fmt.Errorf("pkt too short for uint64: need 8, have %d", b.Len()) + return + } + QID.Path = uint64(u[0]) + QID.Path |= uint64(u[1])<<8 + QID.Path |= uint64(u[2])<<16 + QID.Path |= uint64(u[3])<<24 + QID.Path |= uint64(u[4])<<32 + QID.Path |= uint64(u[5])<<40 + QID.Path |= uint64(u[6])<<48 + QID.Path |= uint64(u[7])<<56 + +if b.Len() > 0 { +err = fmt.Errorf("Packet too long: %d bytes left over after decode", b.Len()) +} +return +} +func MarshalTattachPkt (b *bytes.Buffer, t Tag, SFID FID, AFID FID, Uname string, Aname string) { +var l uint64 +b.Reset() +b.Write([]byte{0,0,0,0, +uint8(Tattach), +byte(t), byte(t>>8), + uint8(SFID>>0), + uint8(SFID>>8), + uint8(SFID>>16), + uint8(SFID>>24), + uint8(AFID>>0), + uint8(AFID>>8), + uint8(AFID>>16), + uint8(AFID>>24), + uint8(len(Uname)),uint8(len(Uname)>>8), + }) + b.Write([]byte(Uname)) + b.Write([]byte{ uint8(len(Aname)),uint8(len(Aname)>>8), + }) + b.Write([]byte(Aname)) + +{ +l = uint64(b.Len()) +copy(b.Bytes(), []byte{uint8(l), uint8(l>>8), uint8(l>>16), uint8(l>>24)}) +} +return +} +func UnmarshalTattachPkt (b *bytes.Buffer) (SFID FID, AFID FID, Uname string, Aname string, t Tag, err error) { +var u [8]uint8 +var l uint64 +if _, err = b.Read(u[:2]); err != nil { +err = fmt.Errorf("pkt too short for Tag; need 2, have %d", b.Len()) +return +} +l = uint64(u[0]) | uint64(u[1])<<8 +t = Tag(l) + if _, err = b.Read(u[:4]); err != nil { + err = fmt.Errorf("pkt too short for uint32: need 4, have %d", b.Len()) + return + } + SFID = FID(u[0]) + SFID |= FID(u[1])<<8 + SFID |= FID(u[2])<<16 + SFID |= FID(u[3])<<24 + if _, err = b.Read(u[:4]); err != nil { + err = fmt.Errorf("pkt too short for uint32: need 4, have %d", b.Len()) + return + } + AFID = FID(u[0]) + AFID |= FID(u[1])<<8 + AFID |= FID(u[2])<<16 + AFID |= FID(u[3])<<24 + if _, err = b.Read(u[:2]); err != nil { + err = fmt.Errorf("pkt too short for uint16: need 2, have %d", b.Len()) + return + } + l = uint64(u[0]) + l |= uint64(u[1])<<8 + if b.Len() < int(l) { + err = fmt.Errorf("pkt too short for string: need %d, have %d", l, b.Len()) + return + } + Uname = string(b.Bytes()[:l]) + _ = b.Next(int(l)) + if _, err = b.Read(u[:2]); err != nil { + err = fmt.Errorf("pkt too short for uint16: need 2, have %d", b.Len()) + return + } + l = uint64(u[0]) + l |= uint64(u[1])<<8 + if b.Len() < int(l) { + err = fmt.Errorf("pkt too short for string: need %d, have %d", l, b.Len()) + return + } + Aname = string(b.Bytes()[:l]) + _ = b.Next(int(l)) + +if b.Len() > 0 { +err = fmt.Errorf("Packet too long: %d bytes left over after decode", b.Len()) +} +return +} +func (s *Server) SrvRattach(b*bytes.Buffer) (err error) { + SFID, AFID, Uname, Aname, t, err := UnmarshalTattachPkt(b) + //if err != nil { + //} + if QID, err := s.NS.Rattach(SFID, AFID, Uname, Aname); err != nil { + MarshalRerrorPkt(b, t, fmt.Sprintf("%v", err)) +} else { + MarshalRattachPkt(b, t, QID) +} + return nil +} + +func (c *Client)CallTattach (SFID FID, AFID FID, Uname string, Aname string) (QID QID, err error) { +var b = bytes.Buffer{} +if c.Trace != nil {c.Trace("%v", Tattach)} +t := Tag(0) +r := make (chan []byte) +if c.Trace != nil { c.Trace(":tag %v, FID %v", t, c.FID)} +MarshalTattachPkt(&b, t, SFID, AFID, Uname, Aname) +c.FromClient <- &RPCCall{b: b.Bytes(), Reply: r} +bb := <-r +if MType(bb[4]) == Rerror { + s, _, err := UnmarshalRerrorPkt(bytes.NewBuffer(bb[5:])) + if err != nil { + return QID, err + } + return QID, fmt.Errorf("%v", s) +} else { + QID, _, err = UnmarshalRattachPkt(bytes.NewBuffer(bb[5:])) +} +return QID, err +} +func MarshalRflushPkt (b *bytes.Buffer, t Tag, ) { +var l uint64 +b.Reset() +b.Write([]byte{0,0,0,0, +uint8(Rflush), +byte(t), byte(t>>8), + }) + +{ +l = uint64(b.Len()) +copy(b.Bytes(), []byte{uint8(l), uint8(l>>8), uint8(l>>16), uint8(l>>24)}) +} +return +} +func UnmarshalRflushPkt (b *bytes.Buffer) ( t Tag, err error) { +var u [8]uint8 +var l uint64 +if _, err = b.Read(u[:2]); err != nil { +err = fmt.Errorf("pkt too short for Tag; need 2, have %d", b.Len()) +return +} +l = uint64(u[0]) | uint64(u[1])<<8 +t = Tag(l) + +if b.Len() > 0 { +err = fmt.Errorf("Packet too long: %d bytes left over after decode", b.Len()) +} +return +} +func MarshalTflushPkt (b *bytes.Buffer, t Tag, OTag Tag) { +var l uint64 +b.Reset() +b.Write([]byte{0,0,0,0, +uint8(Tflush), +byte(t), byte(t>>8), + uint8(OTag>>0), + uint8(OTag>>8), + }) + +{ +l = uint64(b.Len()) +copy(b.Bytes(), []byte{uint8(l), uint8(l>>8), uint8(l>>16), uint8(l>>24)}) +} +return +} +func UnmarshalTflushPkt (b *bytes.Buffer) (OTag Tag, t Tag, err error) { +var u [8]uint8 +var l uint64 +if _, err = b.Read(u[:2]); err != nil { +err = fmt.Errorf("pkt too short for Tag; need 2, have %d", b.Len()) +return +} +l = uint64(u[0]) | uint64(u[1])<<8 +t = Tag(l) + if _, err = b.Read(u[:2]); err != nil { + err = fmt.Errorf("pkt too short for uint16: need 2, have %d", b.Len()) + return + } + OTag = Tag(u[0]) + OTag |= Tag(u[1])<<8 + +if b.Len() > 0 { +err = fmt.Errorf("Packet too long: %d bytes left over after decode", b.Len()) +} +return +} +func (s *Server) SrvRflush(b*bytes.Buffer) (err error) { + OTag, t, err := UnmarshalTflushPkt(b) + //if err != nil { + //} + if err := s.NS.Rflush(OTag); err != nil { + MarshalRerrorPkt(b, t, fmt.Sprintf("%v", err)) +} else { + MarshalRflushPkt(b, t, ) +} + return nil +} + +func (c *Client)CallTflush (OTag Tag) ( err error) { +var b = bytes.Buffer{} +if c.Trace != nil {c.Trace("%v", Tflush)} +t := Tag(0) +r := make (chan []byte) +if c.Trace != nil { c.Trace(":tag %v, FID %v", t, c.FID)} +MarshalTflushPkt(&b, t, OTag) +c.FromClient <- &RPCCall{b: b.Bytes(), Reply: r} +bb := <-r +if MType(bb[4]) == Rerror { + s, _, err := UnmarshalRerrorPkt(bytes.NewBuffer(bb[5:])) + if err != nil { + return err + } + return fmt.Errorf("%v", s) +} else { + _, err = UnmarshalRflushPkt(bytes.NewBuffer(bb[5:])) +} +return err +} +func MarshalRwalkPkt (b *bytes.Buffer, t Tag, QIDs []QID) { +var l uint64 +b.Reset() +b.Write([]byte{0,0,0,0, +uint8(Rwalk), +byte(t), byte(t>>8), + uint8(len(QIDs)>>0), + uint8(len(QIDs)>>8), + }) +for i := range QIDs { + b.Write([]byte{ uint8(QIDs[i].Type>>0), + uint8(QIDs[i].Version>>0), + uint8(QIDs[i].Version>>8), + uint8(QIDs[i].Version>>16), + uint8(QIDs[i].Version>>24), + uint8(QIDs[i].Path>>0), + uint8(QIDs[i].Path>>8), + uint8(QIDs[i].Path>>16), + uint8(QIDs[i].Path>>24), + uint8(QIDs[i].Path>>32), + uint8(QIDs[i].Path>>40), + uint8(QIDs[i].Path>>48), + uint8(QIDs[i].Path>>56), + }) +} + +{ +l = uint64(b.Len()) +copy(b.Bytes(), []byte{uint8(l), uint8(l>>8), uint8(l>>16), uint8(l>>24)}) +} +return +} +func UnmarshalRwalkPkt (b *bytes.Buffer) (QIDs []QID, t Tag, err error) { +var u [8]uint8 +var l uint64 +if _, err = b.Read(u[:2]); err != nil { +err = fmt.Errorf("pkt too short for Tag; need 2, have %d", b.Len()) +return +} +l = uint64(u[0]) | uint64(u[1])<<8 +t = Tag(l) + if _, err = b.Read(u[:2]); err != nil { + err = fmt.Errorf("pkt too short for uint16: need 2, have %d", b.Len()) + return + } + l = uint64(u[0]) + l |= uint64(u[1])<<8 + QIDs = make([]QID, l) +for i := range QIDs { + if _, err = b.Read(u[:1]); err != nil { + err = fmt.Errorf("pkt too short for uint8: need 1, have %d", b.Len()) + return + } + QIDs[i].Type = uint8(u[0]) + if _, err = b.Read(u[:4]); err != nil { + err = fmt.Errorf("pkt too short for uint32: need 4, have %d", b.Len()) + return + } + QIDs[i].Version = uint32(u[0]) + QIDs[i].Version |= uint32(u[1])<<8 + QIDs[i].Version |= uint32(u[2])<<16 + QIDs[i].Version |= uint32(u[3])<<24 + if _, err = b.Read(u[:8]); err != nil { + err = fmt.Errorf("pkt too short for uint64: need 8, have %d", b.Len()) + return + } + QIDs[i].Path = uint64(u[0]) + QIDs[i].Path |= uint64(u[1])<<8 + QIDs[i].Path |= uint64(u[2])<<16 + QIDs[i].Path |= uint64(u[3])<<24 + QIDs[i].Path |= uint64(u[4])<<32 + QIDs[i].Path |= uint64(u[5])<<40 + QIDs[i].Path |= uint64(u[6])<<48 + QIDs[i].Path |= uint64(u[7])<<56 +} + +if b.Len() > 0 { +err = fmt.Errorf("Packet too long: %d bytes left over after decode", b.Len()) +} +return +} +func MarshalTwalkPkt (b *bytes.Buffer, t Tag, SFID FID, NewFID FID, Paths []string) { +var l uint64 +b.Reset() +b.Write([]byte{0,0,0,0, +uint8(Twalk), +byte(t), byte(t>>8), + uint8(SFID>>0), + uint8(SFID>>8), + uint8(SFID>>16), + uint8(SFID>>24), + uint8(NewFID>>0), + uint8(NewFID>>8), + uint8(NewFID>>16), + uint8(NewFID>>24), + uint8(len(Paths)>>0), + uint8(len(Paths)>>8), + }) +for i := range Paths { + b.Write([]byte{ uint8(len(Paths[i])),uint8(len(Paths[i])>>8), + }) + b.Write([]byte(Paths[i])) +} + +{ +l = uint64(b.Len()) +copy(b.Bytes(), []byte{uint8(l), uint8(l>>8), uint8(l>>16), uint8(l>>24)}) +} +return +} +func UnmarshalTwalkPkt (b *bytes.Buffer) (SFID FID, NewFID FID, Paths []string, t Tag, err error) { +var u [8]uint8 +var l uint64 +if _, err = b.Read(u[:2]); err != nil { +err = fmt.Errorf("pkt too short for Tag; need 2, have %d", b.Len()) +return +} +l = uint64(u[0]) | uint64(u[1])<<8 +t = Tag(l) + if _, err = b.Read(u[:4]); err != nil { + err = fmt.Errorf("pkt too short for uint32: need 4, have %d", b.Len()) + return + } + SFID = FID(u[0]) + SFID |= FID(u[1])<<8 + SFID |= FID(u[2])<<16 + SFID |= FID(u[3])<<24 + if _, err = b.Read(u[:4]); err != nil { + err = fmt.Errorf("pkt too short for uint32: need 4, have %d", b.Len()) + return + } + NewFID = FID(u[0]) + NewFID |= FID(u[1])<<8 + NewFID |= FID(u[2])<<16 + NewFID |= FID(u[3])<<24 + if _, err = b.Read(u[:2]); err != nil { + err = fmt.Errorf("pkt too short for uint16: need 2, have %d", b.Len()) + return + } + l = uint64(u[0]) + l |= uint64(u[1])<<8 + Paths = make([]string, l) +for i := range Paths { + if _, err = b.Read(u[:2]); err != nil { + err = fmt.Errorf("pkt too short for uint16: need 2, have %d", b.Len()) + return + } + l = uint64(u[0]) + l |= uint64(u[1])<<8 + if b.Len() < int(l) { + err = fmt.Errorf("pkt too short for string: need %d, have %d", l, b.Len()) + return + } + Paths[i] = string(b.Bytes()[:l]) + _ = b.Next(int(l)) +} + +if b.Len() > 0 { +err = fmt.Errorf("Packet too long: %d bytes left over after decode", b.Len()) +} +return +} +func (s *Server) SrvRwalk(b*bytes.Buffer) (err error) { + SFID, NewFID, Paths, t, err := UnmarshalTwalkPkt(b) + //if err != nil { + //} + if QIDs, err := s.NS.Rwalk(SFID, NewFID, Paths); err != nil { + MarshalRerrorPkt(b, t, fmt.Sprintf("%v", err)) +} else { + MarshalRwalkPkt(b, t, QIDs) +} + return nil +} + +func (c *Client)CallTwalk (SFID FID, NewFID FID, Paths []string) (QIDs []QID, err error) { +var b = bytes.Buffer{} +if c.Trace != nil {c.Trace("%v", Twalk)} +t := Tag(0) +r := make (chan []byte) +if c.Trace != nil { c.Trace(":tag %v, FID %v", t, c.FID)} +MarshalTwalkPkt(&b, t, SFID, NewFID, Paths) +c.FromClient <- &RPCCall{b: b.Bytes(), Reply: r} +bb := <-r +if MType(bb[4]) == Rerror { + s, _, err := UnmarshalRerrorPkt(bytes.NewBuffer(bb[5:])) + if err != nil { + return QIDs, err + } + return QIDs, fmt.Errorf("%v", s) +} else { + QIDs, _, err = UnmarshalRwalkPkt(bytes.NewBuffer(bb[5:])) +} +return QIDs, err +} +func MarshalRopenPkt (b *bytes.Buffer, t Tag, OQID QID, IOUnit MaxSize) { +var l uint64 +b.Reset() +b.Write([]byte{0,0,0,0, +uint8(Ropen), +byte(t), byte(t>>8), + uint8(OQID.Type>>0), + uint8(OQID.Version>>0), + uint8(OQID.Version>>8), + uint8(OQID.Version>>16), + uint8(OQID.Version>>24), + uint8(OQID.Path>>0), + uint8(OQID.Path>>8), + uint8(OQID.Path>>16), + uint8(OQID.Path>>24), + uint8(OQID.Path>>32), + uint8(OQID.Path>>40), + uint8(OQID.Path>>48), + uint8(OQID.Path>>56), + uint8(IOUnit>>0), + uint8(IOUnit>>8), + uint8(IOUnit>>16), + uint8(IOUnit>>24), + }) + +{ +l = uint64(b.Len()) +copy(b.Bytes(), []byte{uint8(l), uint8(l>>8), uint8(l>>16), uint8(l>>24)}) +} +return +} +func UnmarshalRopenPkt (b *bytes.Buffer) (OQID QID, IOUnit MaxSize, t Tag, err error) { +var u [8]uint8 +var l uint64 +if _, err = b.Read(u[:2]); err != nil { +err = fmt.Errorf("pkt too short for Tag; need 2, have %d", b.Len()) +return +} +l = uint64(u[0]) | uint64(u[1])<<8 +t = Tag(l) + if _, err = b.Read(u[:1]); err != nil { + err = fmt.Errorf("pkt too short for uint8: need 1, have %d", b.Len()) + return + } + OQID.Type = uint8(u[0]) + if _, err = b.Read(u[:4]); err != nil { + err = fmt.Errorf("pkt too short for uint32: need 4, have %d", b.Len()) + return + } + OQID.Version = uint32(u[0]) + OQID.Version |= uint32(u[1])<<8 + OQID.Version |= uint32(u[2])<<16 + OQID.Version |= uint32(u[3])<<24 + if _, err = b.Read(u[:8]); err != nil { + err = fmt.Errorf("pkt too short for uint64: need 8, have %d", b.Len()) + return + } + OQID.Path = uint64(u[0]) + OQID.Path |= uint64(u[1])<<8 + OQID.Path |= uint64(u[2])<<16 + OQID.Path |= uint64(u[3])<<24 + OQID.Path |= uint64(u[4])<<32 + OQID.Path |= uint64(u[5])<<40 + OQID.Path |= uint64(u[6])<<48 + OQID.Path |= uint64(u[7])<<56 + if _, err = b.Read(u[:4]); err != nil { + err = fmt.Errorf("pkt too short for uint32: need 4, have %d", b.Len()) + return + } + IOUnit = MaxSize(u[0]) + IOUnit |= MaxSize(u[1])<<8 + IOUnit |= MaxSize(u[2])<<16 + IOUnit |= MaxSize(u[3])<<24 + +if b.Len() > 0 { +err = fmt.Errorf("Packet too long: %d bytes left over after decode", b.Len()) +} +return +} +func MarshalTopenPkt (b *bytes.Buffer, t Tag, OFID FID, Omode Mode) { +var l uint64 +b.Reset() +b.Write([]byte{0,0,0,0, +uint8(Topen), +byte(t), byte(t>>8), + uint8(OFID>>0), + uint8(OFID>>8), + uint8(OFID>>16), + uint8(OFID>>24), + uint8(Omode>>0), + }) + +{ +l = uint64(b.Len()) +copy(b.Bytes(), []byte{uint8(l), uint8(l>>8), uint8(l>>16), uint8(l>>24)}) +} +return +} +func UnmarshalTopenPkt (b *bytes.Buffer) (OFID FID, Omode Mode, t Tag, err error) { +var u [8]uint8 +var l uint64 +if _, err = b.Read(u[:2]); err != nil { +err = fmt.Errorf("pkt too short for Tag; need 2, have %d", b.Len()) +return +} +l = uint64(u[0]) | uint64(u[1])<<8 +t = Tag(l) + if _, err = b.Read(u[:4]); err != nil { + err = fmt.Errorf("pkt too short for uint32: need 4, have %d", b.Len()) + return + } + OFID = FID(u[0]) + OFID |= FID(u[1])<<8 + OFID |= FID(u[2])<<16 + OFID |= FID(u[3])<<24 + if _, err = b.Read(u[:1]); err != nil { + err = fmt.Errorf("pkt too short for uint8: need 1, have %d", b.Len()) + return + } + Omode = Mode(u[0]) + +if b.Len() > 0 { +err = fmt.Errorf("Packet too long: %d bytes left over after decode", b.Len()) +} +return +} +func (s *Server) SrvRopen(b*bytes.Buffer) (err error) { + OFID, Omode, t, err := UnmarshalTopenPkt(b) + //if err != nil { + //} + if OQID, IOUnit, err := s.NS.Ropen(OFID, Omode); err != nil { + MarshalRerrorPkt(b, t, fmt.Sprintf("%v", err)) +} else { + MarshalRopenPkt(b, t, OQID, IOUnit) +} + return nil +} + +func (c *Client)CallTopen (OFID FID, Omode Mode) (OQID QID, IOUnit MaxSize, err error) { +var b = bytes.Buffer{} +if c.Trace != nil {c.Trace("%v", Topen)} +t := Tag(0) +r := make (chan []byte) +if c.Trace != nil { c.Trace(":tag %v, FID %v", t, c.FID)} +MarshalTopenPkt(&b, t, OFID, Omode) +c.FromClient <- &RPCCall{b: b.Bytes(), Reply: r} +bb := <-r +if MType(bb[4]) == Rerror { + s, _, err := UnmarshalRerrorPkt(bytes.NewBuffer(bb[5:])) + if err != nil { + return OQID, IOUnit, err + } + return OQID, IOUnit, fmt.Errorf("%v", s) +} else { + OQID, IOUnit, _, err = UnmarshalRopenPkt(bytes.NewBuffer(bb[5:])) +} +return OQID, IOUnit, err +} +func MarshalRcreatePkt (b *bytes.Buffer, t Tag, OQID QID, IOUnit MaxSize) { +var l uint64 +b.Reset() +b.Write([]byte{0,0,0,0, +uint8(Rcreate), +byte(t), byte(t>>8), + uint8(OQID.Type>>0), + uint8(OQID.Version>>0), + uint8(OQID.Version>>8), + uint8(OQID.Version>>16), + uint8(OQID.Version>>24), + uint8(OQID.Path>>0), + uint8(OQID.Path>>8), + uint8(OQID.Path>>16), + uint8(OQID.Path>>24), + uint8(OQID.Path>>32), + uint8(OQID.Path>>40), + uint8(OQID.Path>>48), + uint8(OQID.Path>>56), + uint8(IOUnit>>0), + uint8(IOUnit>>8), + uint8(IOUnit>>16), + uint8(IOUnit>>24), + }) + +{ +l = uint64(b.Len()) +copy(b.Bytes(), []byte{uint8(l), uint8(l>>8), uint8(l>>16), uint8(l>>24)}) +} +return +} +func UnmarshalRcreatePkt (b *bytes.Buffer) (OQID QID, IOUnit MaxSize, t Tag, err error) { +var u [8]uint8 +var l uint64 +if _, err = b.Read(u[:2]); err != nil { +err = fmt.Errorf("pkt too short for Tag; need 2, have %d", b.Len()) +return +} +l = uint64(u[0]) | uint64(u[1])<<8 +t = Tag(l) + if _, err = b.Read(u[:1]); err != nil { + err = fmt.Errorf("pkt too short for uint8: need 1, have %d", b.Len()) + return + } + OQID.Type = uint8(u[0]) + if _, err = b.Read(u[:4]); err != nil { + err = fmt.Errorf("pkt too short for uint32: need 4, have %d", b.Len()) + return + } + OQID.Version = uint32(u[0]) + OQID.Version |= uint32(u[1])<<8 + OQID.Version |= uint32(u[2])<<16 + OQID.Version |= uint32(u[3])<<24 + if _, err = b.Read(u[:8]); err != nil { + err = fmt.Errorf("pkt too short for uint64: need 8, have %d", b.Len()) + return + } + OQID.Path = uint64(u[0]) + OQID.Path |= uint64(u[1])<<8 + OQID.Path |= uint64(u[2])<<16 + OQID.Path |= uint64(u[3])<<24 + OQID.Path |= uint64(u[4])<<32 + OQID.Path |= uint64(u[5])<<40 + OQID.Path |= uint64(u[6])<<48 + OQID.Path |= uint64(u[7])<<56 + if _, err = b.Read(u[:4]); err != nil { + err = fmt.Errorf("pkt too short for uint32: need 4, have %d", b.Len()) + return + } + IOUnit = MaxSize(u[0]) + IOUnit |= MaxSize(u[1])<<8 + IOUnit |= MaxSize(u[2])<<16 + IOUnit |= MaxSize(u[3])<<24 + +if b.Len() > 0 { +err = fmt.Errorf("Packet too long: %d bytes left over after decode", b.Len()) +} +return +} +func MarshalTcreatePkt (b *bytes.Buffer, t Tag, OFID FID, Name string, CreatePerm Perm, Omode Mode) { +var l uint64 +b.Reset() +b.Write([]byte{0,0,0,0, +uint8(Tcreate), +byte(t), byte(t>>8), + uint8(OFID>>0), + uint8(OFID>>8), + uint8(OFID>>16), + uint8(OFID>>24), + uint8(len(Name)),uint8(len(Name)>>8), + }) + b.Write([]byte(Name)) + b.Write([]byte{ uint8(CreatePerm>>0), + uint8(CreatePerm>>8), + uint8(CreatePerm>>16), + uint8(CreatePerm>>24), + uint8(Omode>>0), + }) + +{ +l = uint64(b.Len()) +copy(b.Bytes(), []byte{uint8(l), uint8(l>>8), uint8(l>>16), uint8(l>>24)}) +} +return +} +func UnmarshalTcreatePkt (b *bytes.Buffer) (OFID FID, Name string, CreatePerm Perm, Omode Mode, t Tag, err error) { +var u [8]uint8 +var l uint64 +if _, err = b.Read(u[:2]); err != nil { +err = fmt.Errorf("pkt too short for Tag; need 2, have %d", b.Len()) +return +} +l = uint64(u[0]) | uint64(u[1])<<8 +t = Tag(l) + if _, err = b.Read(u[:4]); err != nil { + err = fmt.Errorf("pkt too short for uint32: need 4, have %d", b.Len()) + return + } + OFID = FID(u[0]) + OFID |= FID(u[1])<<8 + OFID |= FID(u[2])<<16 + OFID |= FID(u[3])<<24 + if _, err = b.Read(u[:2]); err != nil { + err = fmt.Errorf("pkt too short for uint16: need 2, have %d", b.Len()) + return + } + l = uint64(u[0]) + l |= uint64(u[1])<<8 + if b.Len() < int(l) { + err = fmt.Errorf("pkt too short for string: need %d, have %d", l, b.Len()) + return + } + Name = string(b.Bytes()[:l]) + _ = b.Next(int(l)) + if _, err = b.Read(u[:4]); err != nil { + err = fmt.Errorf("pkt too short for uint32: need 4, have %d", b.Len()) + return + } + CreatePerm = Perm(u[0]) + CreatePerm |= Perm(u[1])<<8 + CreatePerm |= Perm(u[2])<<16 + CreatePerm |= Perm(u[3])<<24 + if _, err = b.Read(u[:1]); err != nil { + err = fmt.Errorf("pkt too short for uint8: need 1, have %d", b.Len()) + return + } + Omode = Mode(u[0]) + +if b.Len() > 0 { +err = fmt.Errorf("Packet too long: %d bytes left over after decode", b.Len()) +} +return +} +func (s *Server) SrvRcreate(b*bytes.Buffer) (err error) { + OFID, Name, CreatePerm, Omode, t, err := UnmarshalTcreatePkt(b) + //if err != nil { + //} + if OQID, IOUnit, err := s.NS.Rcreate(OFID, Name, CreatePerm, Omode); err != nil { + MarshalRerrorPkt(b, t, fmt.Sprintf("%v", err)) +} else { + MarshalRcreatePkt(b, t, OQID, IOUnit) +} + return nil +} + +func (c *Client)CallTcreate (OFID FID, Name string, CreatePerm Perm, Omode Mode) (OQID QID, IOUnit MaxSize, err error) { +var b = bytes.Buffer{} +if c.Trace != nil {c.Trace("%v", Tcreate)} +t := Tag(0) +r := make (chan []byte) +if c.Trace != nil { c.Trace(":tag %v, FID %v", t, c.FID)} +MarshalTcreatePkt(&b, t, OFID, Name, CreatePerm, Omode) +c.FromClient <- &RPCCall{b: b.Bytes(), Reply: r} +bb := <-r +if MType(bb[4]) == Rerror { + s, _, err := UnmarshalRerrorPkt(bytes.NewBuffer(bb[5:])) + if err != nil { + return OQID, IOUnit, err + } + return OQID, IOUnit, fmt.Errorf("%v", s) +} else { + OQID, IOUnit, _, err = UnmarshalRcreatePkt(bytes.NewBuffer(bb[5:])) +} +return OQID, IOUnit, err +} +func MarshalRstatPkt (b *bytes.Buffer, t Tag, B []byte) { +var l uint64 +b.Reset() +b.Write([]byte{0,0,0,0, +uint8(Rstat), +byte(t), byte(t>>8), + uint8(len(B)>>0), + uint8(len(B)>>8), + }) + b.Write(B) + +{ +l = uint64(b.Len()) +copy(b.Bytes(), []byte{uint8(l), uint8(l>>8), uint8(l>>16), uint8(l>>24)}) +} +return +} +func UnmarshalRstatPkt (b *bytes.Buffer) (B []byte, t Tag, err error) { +var u [8]uint8 +var l uint64 +if _, err = b.Read(u[:2]); err != nil { +err = fmt.Errorf("pkt too short for Tag; need 2, have %d", b.Len()) +return +} +l = uint64(u[0]) | uint64(u[1])<<8 +t = Tag(l) + if _, err = b.Read(u[:2]); err != nil { + err = fmt.Errorf("pkt too short for uint16: need 2, have %d", b.Len()) + return + } + l = uint64(u[0]) + l |= uint64(u[1])<<8 + B = b.Bytes()[:l] + _ = b.Next(int(l)) + +if b.Len() > 0 { +err = fmt.Errorf("Packet too long: %d bytes left over after decode", b.Len()) +} +return +} +func MarshalTstatPkt (b *bytes.Buffer, t Tag, OFID FID) { +var l uint64 +b.Reset() +b.Write([]byte{0,0,0,0, +uint8(Tstat), +byte(t), byte(t>>8), + uint8(OFID>>0), + uint8(OFID>>8), + uint8(OFID>>16), + uint8(OFID>>24), + }) + +{ +l = uint64(b.Len()) +copy(b.Bytes(), []byte{uint8(l), uint8(l>>8), uint8(l>>16), uint8(l>>24)}) +} +return +} +func UnmarshalTstatPkt (b *bytes.Buffer) (OFID FID, t Tag, err error) { +var u [8]uint8 +var l uint64 +if _, err = b.Read(u[:2]); err != nil { +err = fmt.Errorf("pkt too short for Tag; need 2, have %d", b.Len()) +return +} +l = uint64(u[0]) | uint64(u[1])<<8 +t = Tag(l) + if _, err = b.Read(u[:4]); err != nil { + err = fmt.Errorf("pkt too short for uint32: need 4, have %d", b.Len()) + return + } + OFID = FID(u[0]) + OFID |= FID(u[1])<<8 + OFID |= FID(u[2])<<16 + OFID |= FID(u[3])<<24 + +if b.Len() > 0 { +err = fmt.Errorf("Packet too long: %d bytes left over after decode", b.Len()) +} +return +} +func (s *Server) SrvRstat(b*bytes.Buffer) (err error) { + OFID, t, err := UnmarshalTstatPkt(b) + //if err != nil { + //} + if B, err := s.NS.Rstat(OFID); err != nil { + MarshalRerrorPkt(b, t, fmt.Sprintf("%v", err)) +} else { + MarshalRstatPkt(b, t, B) +} + return nil +} + +func (c *Client)CallTstat (OFID FID) (B []byte, err error) { +var b = bytes.Buffer{} +if c.Trace != nil {c.Trace("%v", Tstat)} +t := Tag(0) +r := make (chan []byte) +if c.Trace != nil { c.Trace(":tag %v, FID %v", t, c.FID)} +MarshalTstatPkt(&b, t, OFID) +c.FromClient <- &RPCCall{b: b.Bytes(), Reply: r} +bb := <-r +if MType(bb[4]) == Rerror { + s, _, err := UnmarshalRerrorPkt(bytes.NewBuffer(bb[5:])) + if err != nil { + return B, err + } + return B, fmt.Errorf("%v", s) +} else { + B, _, err = UnmarshalRstatPkt(bytes.NewBuffer(bb[5:])) +} +return B, err +} +func MarshalRwstatPkt (b *bytes.Buffer, t Tag, ) { +var l uint64 +b.Reset() +b.Write([]byte{0,0,0,0, +uint8(Rwstat), +byte(t), byte(t>>8), + }) + +{ +l = uint64(b.Len()) +copy(b.Bytes(), []byte{uint8(l), uint8(l>>8), uint8(l>>16), uint8(l>>24)}) +} +return +} +func UnmarshalRwstatPkt (b *bytes.Buffer) ( t Tag, err error) { +var u [8]uint8 +var l uint64 +if _, err = b.Read(u[:2]); err != nil { +err = fmt.Errorf("pkt too short for Tag; need 2, have %d", b.Len()) +return +} +l = uint64(u[0]) | uint64(u[1])<<8 +t = Tag(l) + +if b.Len() > 0 { +err = fmt.Errorf("Packet too long: %d bytes left over after decode", b.Len()) +} +return +} +func MarshalTwstatPkt (b *bytes.Buffer, t Tag, OFID FID, B []byte) { +var l uint64 +b.Reset() +b.Write([]byte{0,0,0,0, +uint8(Twstat), +byte(t), byte(t>>8), + uint8(OFID>>0), + uint8(OFID>>8), + uint8(OFID>>16), + uint8(OFID>>24), + uint8(len(B)>>0), + uint8(len(B)>>8), + }) + b.Write(B) + +{ +l = uint64(b.Len()) +copy(b.Bytes(), []byte{uint8(l), uint8(l>>8), uint8(l>>16), uint8(l>>24)}) +} +return +} +func UnmarshalTwstatPkt (b *bytes.Buffer) (OFID FID, B []byte, t Tag, err error) { +var u [8]uint8 +var l uint64 +if _, err = b.Read(u[:2]); err != nil { +err = fmt.Errorf("pkt too short for Tag; need 2, have %d", b.Len()) +return +} +l = uint64(u[0]) | uint64(u[1])<<8 +t = Tag(l) + if _, err = b.Read(u[:4]); err != nil { + err = fmt.Errorf("pkt too short for uint32: need 4, have %d", b.Len()) + return + } + OFID = FID(u[0]) + OFID |= FID(u[1])<<8 + OFID |= FID(u[2])<<16 + OFID |= FID(u[3])<<24 + if _, err = b.Read(u[:2]); err != nil { + err = fmt.Errorf("pkt too short for uint16: need 2, have %d", b.Len()) + return + } + l = uint64(u[0]) + l |= uint64(u[1])<<8 + B = b.Bytes()[:l] + _ = b.Next(int(l)) + +if b.Len() > 0 { +err = fmt.Errorf("Packet too long: %d bytes left over after decode", b.Len()) +} +return +} +func (s *Server) SrvRwstat(b*bytes.Buffer) (err error) { + OFID, B, t, err := UnmarshalTwstatPkt(b) + //if err != nil { + //} + if err := s.NS.Rwstat(OFID, B); err != nil { + MarshalRerrorPkt(b, t, fmt.Sprintf("%v", err)) +} else { + MarshalRwstatPkt(b, t, ) +} + return nil +} + +func (c *Client)CallTwstat (OFID FID, B []byte) ( err error) { +var b = bytes.Buffer{} +if c.Trace != nil {c.Trace("%v", Twstat)} +t := Tag(0) +r := make (chan []byte) +if c.Trace != nil { c.Trace(":tag %v, FID %v", t, c.FID)} +MarshalTwstatPkt(&b, t, OFID, B) +c.FromClient <- &RPCCall{b: b.Bytes(), Reply: r} +bb := <-r +if MType(bb[4]) == Rerror { + s, _, err := UnmarshalRerrorPkt(bytes.NewBuffer(bb[5:])) + if err != nil { + return err + } + return fmt.Errorf("%v", s) +} else { + _, err = UnmarshalRwstatPkt(bytes.NewBuffer(bb[5:])) +} +return err +} +func MarshalRclunkPkt (b *bytes.Buffer, t Tag, ) { +var l uint64 +b.Reset() +b.Write([]byte{0,0,0,0, +uint8(Rclunk), +byte(t), byte(t>>8), + }) + +{ +l = uint64(b.Len()) +copy(b.Bytes(), []byte{uint8(l), uint8(l>>8), uint8(l>>16), uint8(l>>24)}) +} +return +} +func UnmarshalRclunkPkt (b *bytes.Buffer) ( t Tag, err error) { +var u [8]uint8 +var l uint64 +if _, err = b.Read(u[:2]); err != nil { +err = fmt.Errorf("pkt too short for Tag; need 2, have %d", b.Len()) +return +} +l = uint64(u[0]) | uint64(u[1])<<8 +t = Tag(l) + +if b.Len() > 0 { +err = fmt.Errorf("Packet too long: %d bytes left over after decode", b.Len()) +} +return +} +func MarshalTclunkPkt (b *bytes.Buffer, t Tag, OFID FID) { +var l uint64 +b.Reset() +b.Write([]byte{0,0,0,0, +uint8(Tclunk), +byte(t), byte(t>>8), + uint8(OFID>>0), + uint8(OFID>>8), + uint8(OFID>>16), + uint8(OFID>>24), + }) + +{ +l = uint64(b.Len()) +copy(b.Bytes(), []byte{uint8(l), uint8(l>>8), uint8(l>>16), uint8(l>>24)}) +} +return +} +func UnmarshalTclunkPkt (b *bytes.Buffer) (OFID FID, t Tag, err error) { +var u [8]uint8 +var l uint64 +if _, err = b.Read(u[:2]); err != nil { +err = fmt.Errorf("pkt too short for Tag; need 2, have %d", b.Len()) +return +} +l = uint64(u[0]) | uint64(u[1])<<8 +t = Tag(l) + if _, err = b.Read(u[:4]); err != nil { + err = fmt.Errorf("pkt too short for uint32: need 4, have %d", b.Len()) + return + } + OFID = FID(u[0]) + OFID |= FID(u[1])<<8 + OFID |= FID(u[2])<<16 + OFID |= FID(u[3])<<24 + +if b.Len() > 0 { +err = fmt.Errorf("Packet too long: %d bytes left over after decode", b.Len()) +} +return +} +func (s *Server) SrvRclunk(b*bytes.Buffer) (err error) { + OFID, t, err := UnmarshalTclunkPkt(b) + //if err != nil { + //} + if err := s.NS.Rclunk(OFID); err != nil { + MarshalRerrorPkt(b, t, fmt.Sprintf("%v", err)) +} else { + MarshalRclunkPkt(b, t, ) +} + return nil +} + +func (c *Client)CallTclunk (OFID FID) ( err error) { +var b = bytes.Buffer{} +if c.Trace != nil {c.Trace("%v", Tclunk)} +t := Tag(0) +r := make (chan []byte) +if c.Trace != nil { c.Trace(":tag %v, FID %v", t, c.FID)} +MarshalTclunkPkt(&b, t, OFID) +c.FromClient <- &RPCCall{b: b.Bytes(), Reply: r} +bb := <-r +if MType(bb[4]) == Rerror { + s, _, err := UnmarshalRerrorPkt(bytes.NewBuffer(bb[5:])) + if err != nil { + return err + } + return fmt.Errorf("%v", s) +} else { + _, err = UnmarshalRclunkPkt(bytes.NewBuffer(bb[5:])) +} +return err +} +func MarshalRremovePkt (b *bytes.Buffer, t Tag, ) { +var l uint64 +b.Reset() +b.Write([]byte{0,0,0,0, +uint8(Rremove), +byte(t), byte(t>>8), + }) + +{ +l = uint64(b.Len()) +copy(b.Bytes(), []byte{uint8(l), uint8(l>>8), uint8(l>>16), uint8(l>>24)}) +} +return +} +func UnmarshalRremovePkt (b *bytes.Buffer) ( t Tag, err error) { +var u [8]uint8 +var l uint64 +if _, err = b.Read(u[:2]); err != nil { +err = fmt.Errorf("pkt too short for Tag; need 2, have %d", b.Len()) +return +} +l = uint64(u[0]) | uint64(u[1])<<8 +t = Tag(l) + +if b.Len() > 0 { +err = fmt.Errorf("Packet too long: %d bytes left over after decode", b.Len()) +} +return +} +func MarshalTremovePkt (b *bytes.Buffer, t Tag, OFID FID) { +var l uint64 +b.Reset() +b.Write([]byte{0,0,0,0, +uint8(Tremove), +byte(t), byte(t>>8), + uint8(OFID>>0), + uint8(OFID>>8), + uint8(OFID>>16), + uint8(OFID>>24), + }) + +{ +l = uint64(b.Len()) +copy(b.Bytes(), []byte{uint8(l), uint8(l>>8), uint8(l>>16), uint8(l>>24)}) +} +return +} +func UnmarshalTremovePkt (b *bytes.Buffer) (OFID FID, t Tag, err error) { +var u [8]uint8 +var l uint64 +if _, err = b.Read(u[:2]); err != nil { +err = fmt.Errorf("pkt too short for Tag; need 2, have %d", b.Len()) +return +} +l = uint64(u[0]) | uint64(u[1])<<8 +t = Tag(l) + if _, err = b.Read(u[:4]); err != nil { + err = fmt.Errorf("pkt too short for uint32: need 4, have %d", b.Len()) + return + } + OFID = FID(u[0]) + OFID |= FID(u[1])<<8 + OFID |= FID(u[2])<<16 + OFID |= FID(u[3])<<24 + +if b.Len() > 0 { +err = fmt.Errorf("Packet too long: %d bytes left over after decode", b.Len()) +} +return +} +func (s *Server) SrvRremove(b*bytes.Buffer) (err error) { + OFID, t, err := UnmarshalTremovePkt(b) + //if err != nil { + //} + if err := s.NS.Rremove(OFID); err != nil { + MarshalRerrorPkt(b, t, fmt.Sprintf("%v", err)) +} else { + MarshalRremovePkt(b, t, ) +} + return nil +} + +func (c *Client)CallTremove (OFID FID) ( err error) { +var b = bytes.Buffer{} +if c.Trace != nil {c.Trace("%v", Tremove)} +t := Tag(0) +r := make (chan []byte) +if c.Trace != nil { c.Trace(":tag %v, FID %v", t, c.FID)} +MarshalTremovePkt(&b, t, OFID) +c.FromClient <- &RPCCall{b: b.Bytes(), Reply: r} +bb := <-r +if MType(bb[4]) == Rerror { + s, _, err := UnmarshalRerrorPkt(bytes.NewBuffer(bb[5:])) + if err != nil { + return err + } + return fmt.Errorf("%v", s) +} else { + _, err = UnmarshalRremovePkt(bytes.NewBuffer(bb[5:])) +} +return err +} +func MarshalRreadPkt (b *bytes.Buffer, t Tag, Data []uint8) { +var l uint64 +b.Reset() +b.Write([]byte{0,0,0,0, +uint8(Rread), +byte(t), byte(t>>8), + uint8(len(Data)>>0), + uint8(len(Data)>>8), + uint8(len(Data)>>16), + uint8(len(Data)>>24), + }) + b.Write(Data) + +{ +l = uint64(b.Len()) +copy(b.Bytes(), []byte{uint8(l), uint8(l>>8), uint8(l>>16), uint8(l>>24)}) +} +return +} +func UnmarshalRreadPkt (b *bytes.Buffer) (Data []uint8, t Tag, err error) { +var u [8]uint8 +var l uint64 +if _, err = b.Read(u[:2]); err != nil { +err = fmt.Errorf("pkt too short for Tag; need 2, have %d", b.Len()) +return +} +l = uint64(u[0]) | uint64(u[1])<<8 +t = Tag(l) + if _, err = b.Read(u[:4]); err != nil { + err = fmt.Errorf("pkt too short for uint32: need 4, have %d", b.Len()) + return + } + l = uint64(u[0]) + l |= uint64(u[1])<<8 + l |= uint64(u[2])<<16 + l |= uint64(u[3])<<24 + Data = b.Bytes()[:l] + _ = b.Next(int(l)) + +if b.Len() > 0 { +err = fmt.Errorf("Packet too long: %d bytes left over after decode", b.Len()) +} +return +} +func MarshalTreadPkt (b *bytes.Buffer, t Tag, OFID FID, Off Offset, Len Count) { +var l uint64 +b.Reset() +b.Write([]byte{0,0,0,0, +uint8(Tread), +byte(t), byte(t>>8), + uint8(OFID>>0), + uint8(OFID>>8), + uint8(OFID>>16), + uint8(OFID>>24), + uint8(Off>>0), + uint8(Off>>8), + uint8(Off>>16), + uint8(Off>>24), + uint8(Off>>32), + uint8(Off>>40), + uint8(Off>>48), + uint8(Off>>56), + uint8(Len>>0), + uint8(Len>>8), + uint8(Len>>16), + uint8(Len>>24), + }) + +{ +l = uint64(b.Len()) +copy(b.Bytes(), []byte{uint8(l), uint8(l>>8), uint8(l>>16), uint8(l>>24)}) +} +return +} +func UnmarshalTreadPkt (b *bytes.Buffer) (OFID FID, Off Offset, Len Count, t Tag, err error) { +var u [8]uint8 +var l uint64 +if _, err = b.Read(u[:2]); err != nil { +err = fmt.Errorf("pkt too short for Tag; need 2, have %d", b.Len()) +return +} +l = uint64(u[0]) | uint64(u[1])<<8 +t = Tag(l) + if _, err = b.Read(u[:4]); err != nil { + err = fmt.Errorf("pkt too short for uint32: need 4, have %d", b.Len()) + return + } + OFID = FID(u[0]) + OFID |= FID(u[1])<<8 + OFID |= FID(u[2])<<16 + OFID |= FID(u[3])<<24 + if _, err = b.Read(u[:8]); err != nil { + err = fmt.Errorf("pkt too short for uint64: need 8, have %d", b.Len()) + return + } + Off = Offset(u[0]) + Off |= Offset(u[1])<<8 + Off |= Offset(u[2])<<16 + Off |= Offset(u[3])<<24 + Off |= Offset(u[4])<<32 + Off |= Offset(u[5])<<40 + Off |= Offset(u[6])<<48 + Off |= Offset(u[7])<<56 + if _, err = b.Read(u[:4]); err != nil { + err = fmt.Errorf("pkt too short for uint32: need 4, have %d", b.Len()) + return + } + Len = Count(u[0]) + Len |= Count(u[1])<<8 + Len |= Count(u[2])<<16 + Len |= Count(u[3])<<24 + +if b.Len() > 0 { +err = fmt.Errorf("Packet too long: %d bytes left over after decode", b.Len()) +} +return +} +func (s *Server) SrvRread(b*bytes.Buffer) (err error) { + OFID, Off, Len, t, err := UnmarshalTreadPkt(b) + //if err != nil { + //} + if Data, err := s.NS.Rread(OFID, Off, Len); err != nil { + MarshalRerrorPkt(b, t, fmt.Sprintf("%v", err)) +} else { + MarshalRreadPkt(b, t, Data) +} + return nil +} + +func (c *Client)CallTread (OFID FID, Off Offset, Len Count) (Data []uint8, err error) { +var b = bytes.Buffer{} +if c.Trace != nil {c.Trace("%v", Tread)} +t := Tag(0) +r := make (chan []byte) +if c.Trace != nil { c.Trace(":tag %v, FID %v", t, c.FID)} +MarshalTreadPkt(&b, t, OFID, Off, Len) +c.FromClient <- &RPCCall{b: b.Bytes(), Reply: r} +bb := <-r +if MType(bb[4]) == Rerror { + s, _, err := UnmarshalRerrorPkt(bytes.NewBuffer(bb[5:])) + if err != nil { + return Data, err + } + return Data, fmt.Errorf("%v", s) +} else { + Data, _, err = UnmarshalRreadPkt(bytes.NewBuffer(bb[5:])) +} +return Data, err +} +func MarshalRwritePkt (b *bytes.Buffer, t Tag, RLen Count) { +var l uint64 +b.Reset() +b.Write([]byte{0,0,0,0, +uint8(Rwrite), +byte(t), byte(t>>8), + uint8(RLen>>0), + uint8(RLen>>8), + uint8(RLen>>16), + uint8(RLen>>24), + }) + +{ +l = uint64(b.Len()) +copy(b.Bytes(), []byte{uint8(l), uint8(l>>8), uint8(l>>16), uint8(l>>24)}) +} +return +} +func UnmarshalRwritePkt (b *bytes.Buffer) (RLen Count, t Tag, err error) { +var u [8]uint8 +var l uint64 +if _, err = b.Read(u[:2]); err != nil { +err = fmt.Errorf("pkt too short for Tag; need 2, have %d", b.Len()) +return +} +l = uint64(u[0]) | uint64(u[1])<<8 +t = Tag(l) + if _, err = b.Read(u[:4]); err != nil { + err = fmt.Errorf("pkt too short for uint32: need 4, have %d", b.Len()) + return + } + RLen = Count(u[0]) + RLen |= Count(u[1])<<8 + RLen |= Count(u[2])<<16 + RLen |= Count(u[3])<<24 + +if b.Len() > 0 { +err = fmt.Errorf("Packet too long: %d bytes left over after decode", b.Len()) +} +return +} +func MarshalTwritePkt (b *bytes.Buffer, t Tag, OFID FID, Off Offset, Data []uint8) { +var l uint64 +b.Reset() +b.Write([]byte{0,0,0,0, +uint8(Twrite), +byte(t), byte(t>>8), + uint8(OFID>>0), + uint8(OFID>>8), + uint8(OFID>>16), + uint8(OFID>>24), + uint8(Off>>0), + uint8(Off>>8), + uint8(Off>>16), + uint8(Off>>24), + uint8(Off>>32), + uint8(Off>>40), + uint8(Off>>48), + uint8(Off>>56), + uint8(len(Data)>>0), + uint8(len(Data)>>8), + uint8(len(Data)>>16), + uint8(len(Data)>>24), + }) + b.Write(Data) + +{ +l = uint64(b.Len()) +copy(b.Bytes(), []byte{uint8(l), uint8(l>>8), uint8(l>>16), uint8(l>>24)}) +} +return +} +func UnmarshalTwritePkt (b *bytes.Buffer) (OFID FID, Off Offset, Data []uint8, t Tag, err error) { +var u [8]uint8 +var l uint64 +if _, err = b.Read(u[:2]); err != nil { +err = fmt.Errorf("pkt too short for Tag; need 2, have %d", b.Len()) +return +} +l = uint64(u[0]) | uint64(u[1])<<8 +t = Tag(l) + if _, err = b.Read(u[:4]); err != nil { + err = fmt.Errorf("pkt too short for uint32: need 4, have %d", b.Len()) + return + } + OFID = FID(u[0]) + OFID |= FID(u[1])<<8 + OFID |= FID(u[2])<<16 + OFID |= FID(u[3])<<24 + if _, err = b.Read(u[:8]); err != nil { + err = fmt.Errorf("pkt too short for uint64: need 8, have %d", b.Len()) + return + } + Off = Offset(u[0]) + Off |= Offset(u[1])<<8 + Off |= Offset(u[2])<<16 + Off |= Offset(u[3])<<24 + Off |= Offset(u[4])<<32 + Off |= Offset(u[5])<<40 + Off |= Offset(u[6])<<48 + Off |= Offset(u[7])<<56 + if _, err = b.Read(u[:4]); err != nil { + err = fmt.Errorf("pkt too short for uint32: need 4, have %d", b.Len()) + return + } + l = uint64(u[0]) + l |= uint64(u[1])<<8 + l |= uint64(u[2])<<16 + l |= uint64(u[3])<<24 + Data = b.Bytes()[:l] + _ = b.Next(int(l)) + +if b.Len() > 0 { +err = fmt.Errorf("Packet too long: %d bytes left over after decode", b.Len()) +} +return +} +func (s *Server) SrvRwrite(b*bytes.Buffer) (err error) { + OFID, Off, Data, t, err := UnmarshalTwritePkt(b) + //if err != nil { + //} + if RLen, err := s.NS.Rwrite(OFID, Off, Data); err != nil { + MarshalRerrorPkt(b, t, fmt.Sprintf("%v", err)) +} else { + MarshalRwritePkt(b, t, RLen) +} + return nil +} + +func (c *Client)CallTwrite (OFID FID, Off Offset, Data []uint8) (RLen Count, err error) { +var b = bytes.Buffer{} +if c.Trace != nil {c.Trace("%v", Twrite)} +t := Tag(0) +r := make (chan []byte) +if c.Trace != nil { c.Trace(":tag %v, FID %v", t, c.FID)} +MarshalTwritePkt(&b, t, OFID, Off, Data) +c.FromClient <- &RPCCall{b: b.Bytes(), Reply: r} +bb := <-r +if MType(bb[4]) == Rerror { + s, _, err := UnmarshalRerrorPkt(bytes.NewBuffer(bb[5:])) + if err != nil { + return RLen, err + } + return RLen, fmt.Errorf("%v", s) +} else { + RLen, _, err = UnmarshalRwritePkt(bytes.NewBuffer(bb[5:])) +} +return RLen, err +} +func ServerError (b *bytes.Buffer, s string) { + var u [8]byte + // This can't really happen. + if _, err := b.Read(u[:2]); err != nil { + return + } + t := Tag(uint16(u[0])|uint16(u[1])<<8) + MarshalRerrorPkt (b, t, s) +} +func Marshaldir (b *bytes.Buffer, D Dir) { +var l uint64 +b.Reset() +b.Write([]byte{0,0,}) + b.Write([]byte{ uint8(D.Type>>0), + uint8(D.Type>>8), + uint8(D.Dev>>0), + uint8(D.Dev>>8), + uint8(D.Dev>>16), + uint8(D.Dev>>24), + uint8(D.QID.Type>>0), + uint8(D.QID.Version>>0), + uint8(D.QID.Version>>8), + uint8(D.QID.Version>>16), + uint8(D.QID.Version>>24), + uint8(D.QID.Path>>0), + uint8(D.QID.Path>>8), + uint8(D.QID.Path>>16), + uint8(D.QID.Path>>24), + uint8(D.QID.Path>>32), + uint8(D.QID.Path>>40), + uint8(D.QID.Path>>48), + uint8(D.QID.Path>>56), + uint8(D.Mode>>0), + uint8(D.Mode>>8), + uint8(D.Mode>>16), + uint8(D.Mode>>24), + uint8(D.Atime>>0), + uint8(D.Atime>>8), + uint8(D.Atime>>16), + uint8(D.Atime>>24), + uint8(D.Mtime>>0), + uint8(D.Mtime>>8), + uint8(D.Mtime>>16), + uint8(D.Mtime>>24), + uint8(D.Length>>0), + uint8(D.Length>>8), + uint8(D.Length>>16), + uint8(D.Length>>24), + uint8(D.Length>>32), + uint8(D.Length>>40), + uint8(D.Length>>48), + uint8(D.Length>>56), + uint8(len(D.Name)),uint8(len(D.Name)>>8), + }) + b.Write([]byte(D.Name)) + b.Write([]byte{ uint8(len(D.User)),uint8(len(D.User)>>8), + }) + b.Write([]byte(D.User)) + b.Write([]byte{ uint8(len(D.Group)),uint8(len(D.Group)>>8), + }) + b.Write([]byte(D.Group)) + b.Write([]byte{ uint8(len(D.ModUser)),uint8(len(D.ModUser)>>8), + }) + b.Write([]byte(D.ModUser)) + +l = uint64(b.Len()) - 2 +copy(b.Bytes(), []byte{uint8(l), uint8(l>>8)}) +return +} +func Unmarshaldir (b *bytes.Buffer) (D Dir, err error) { +var u [8]uint8 +var l uint64 +_ = b.Next(2) // eat the length too + if _, err = b.Read(u[:2]); err != nil { + err = fmt.Errorf("pkt too short for uint16: need 2, have %d", b.Len()) + return + } + D.Type = uint16(u[0]) + D.Type |= uint16(u[1])<<8 + if _, err = b.Read(u[:4]); err != nil { + err = fmt.Errorf("pkt too short for uint32: need 4, have %d", b.Len()) + return + } + D.Dev = uint32(u[0]) + D.Dev |= uint32(u[1])<<8 + D.Dev |= uint32(u[2])<<16 + D.Dev |= uint32(u[3])<<24 + if _, err = b.Read(u[:1]); err != nil { + err = fmt.Errorf("pkt too short for uint8: need 1, have %d", b.Len()) + return + } + D.QID.Type = uint8(u[0]) + if _, err = b.Read(u[:4]); err != nil { + err = fmt.Errorf("pkt too short for uint32: need 4, have %d", b.Len()) + return + } + D.QID.Version = uint32(u[0]) + D.QID.Version |= uint32(u[1])<<8 + D.QID.Version |= uint32(u[2])<<16 + D.QID.Version |= uint32(u[3])<<24 + if _, err = b.Read(u[:8]); err != nil { + err = fmt.Errorf("pkt too short for uint64: need 8, have %d", b.Len()) + return + } + D.QID.Path = uint64(u[0]) + D.QID.Path |= uint64(u[1])<<8 + D.QID.Path |= uint64(u[2])<<16 + D.QID.Path |= uint64(u[3])<<24 + D.QID.Path |= uint64(u[4])<<32 + D.QID.Path |= uint64(u[5])<<40 + D.QID.Path |= uint64(u[6])<<48 + D.QID.Path |= uint64(u[7])<<56 + if _, err = b.Read(u[:4]); err != nil { + err = fmt.Errorf("pkt too short for uint32: need 4, have %d", b.Len()) + return + } + D.Mode = uint32(u[0]) + D.Mode |= uint32(u[1])<<8 + D.Mode |= uint32(u[2])<<16 + D.Mode |= uint32(u[3])<<24 + if _, err = b.Read(u[:4]); err != nil { + err = fmt.Errorf("pkt too short for uint32: need 4, have %d", b.Len()) + return + } + D.Atime = uint32(u[0]) + D.Atime |= uint32(u[1])<<8 + D.Atime |= uint32(u[2])<<16 + D.Atime |= uint32(u[3])<<24 + if _, err = b.Read(u[:4]); err != nil { + err = fmt.Errorf("pkt too short for uint32: need 4, have %d", b.Len()) + return + } + D.Mtime = uint32(u[0]) + D.Mtime |= uint32(u[1])<<8 + D.Mtime |= uint32(u[2])<<16 + D.Mtime |= uint32(u[3])<<24 + if _, err = b.Read(u[:8]); err != nil { + err = fmt.Errorf("pkt too short for uint64: need 8, have %d", b.Len()) + return + } + D.Length = uint64(u[0]) + D.Length |= uint64(u[1])<<8 + D.Length |= uint64(u[2])<<16 + D.Length |= uint64(u[3])<<24 + D.Length |= uint64(u[4])<<32 + D.Length |= uint64(u[5])<<40 + D.Length |= uint64(u[6])<<48 + D.Length |= uint64(u[7])<<56 + if _, err = b.Read(u[:2]); err != nil { + err = fmt.Errorf("pkt too short for uint16: need 2, have %d", b.Len()) + return + } + l = uint64(u[0]) + l |= uint64(u[1])<<8 + if b.Len() < int(l) { + err = fmt.Errorf("pkt too short for string: need %d, have %d", l, b.Len()) + return + } + D.Name = string(b.Bytes()[:l]) + _ = b.Next(int(l)) + if _, err = b.Read(u[:2]); err != nil { + err = fmt.Errorf("pkt too short for uint16: need 2, have %d", b.Len()) + return + } + l = uint64(u[0]) + l |= uint64(u[1])<<8 + if b.Len() < int(l) { + err = fmt.Errorf("pkt too short for string: need %d, have %d", l, b.Len()) + return + } + D.User = string(b.Bytes()[:l]) + _ = b.Next(int(l)) + if _, err = b.Read(u[:2]); err != nil { + err = fmt.Errorf("pkt too short for uint16: need 2, have %d", b.Len()) + return + } + l = uint64(u[0]) + l |= uint64(u[1])<<8 + if b.Len() < int(l) { + err = fmt.Errorf("pkt too short for string: need %d, have %d", l, b.Len()) + return + } + D.Group = string(b.Bytes()[:l]) + _ = b.Next(int(l)) + if _, err = b.Read(u[:2]); err != nil { + err = fmt.Errorf("pkt too short for uint16: need 2, have %d", b.Len()) + return + } + l = uint64(u[0]) + l |= uint64(u[1])<<8 + if b.Len() < int(l) { + err = fmt.Errorf("pkt too short for string: need %d, have %d", l, b.Len()) + return + } + D.ModUser = string(b.Bytes()[:l]) + _ = b.Next(int(l)) + +return +} diff --git a/src/vendor/github.com/Harvey-OS/ninep/protocol/pprof.go b/src/vendor/github.com/Harvey-OS/ninep/protocol/pprof.go new file mode 100644 index 000000000..18e1fc49e --- /dev/null +++ b/src/vendor/github.com/Harvey-OS/ninep/protocol/pprof.go @@ -0,0 +1,17 @@ +// +build pprof + +package protocol + +import ( + "log" + "net/http" + _ "net/http/pprof" + +) + + +func init() { + go func() { + log.Println(http.ListenAndServe(":6060", nil)) + }() +} diff --git a/src/vendor/github.com/Harvey-OS/ninep/protocol/protocol.go b/src/vendor/github.com/Harvey-OS/ninep/protocol/protocol.go new file mode 100644 index 000000000..03f0633da --- /dev/null +++ b/src/vendor/github.com/Harvey-OS/ninep/protocol/protocol.go @@ -0,0 +1,343 @@ +// Package protocol implements the 9p protocol using the stubs. + +//go:generate go run gen.go + +package protocol + +import "bytes" + +// 9P2000 message types +const ( + Tversion MType = 100 + iota + Rversion + Tauth + Rauth + Tattach + Rattach + Terror + Rerror + Tflush + Rflush + Twalk + Rwalk + Topen + Ropen + Tcreate + Rcreate + Tread + Rread + Twrite + Rwrite + Tclunk + Rclunk + Tremove + Rremove + Tstat + Rstat + Twstat + Rwstat + Tlast +) + +const ( + MSIZE = 2*1048576 + IOHDRSZ // default message size (1048576+IOHdrSz) + IOHDRSZ = 24 // the non-data size of the Twrite messages + PORT = 564 // default port for 9P file servers + NumFID = 1 << 16 + QIDLen = 13 +) + +// QID types +const ( + QTDIR = 0x80 // directories + QTAPPEND = 0x40 // append only files + QTEXCL = 0x20 // exclusive use files + QTMOUNT = 0x10 // mounted channel + QTAUTH = 0x08 // authentication file + QTTMP = 0x04 // non-backed-up file + QTSYMLINK = 0x02 // symbolic link (Unix, 9P2000.u) + QTLINK = 0x01 // hard link (Unix, 9P2000.u) + QTFILE = 0x00 +) + +// Flags for the mode field in Topen and Tcreate messages +const ( + OREAD = 0x0 // open read-only + OWRITE = 0x1 // open write-only + ORDWR = 0x2 // open read-write + OEXEC = 0x3 // execute (== read but check execute permission) + OTRUNC = 0x10 // or'ed in (except for exec), truncate file first + OCEXEC = 0x20 // or'ed in, close on exec + ORCLOSE = 0x40 // or'ed in, remove on close + OAPPEND = 0x80 // or'ed in, append only + OEXCL = 0x1000 // or'ed in, exclusive client use +) + +// File modes +const ( + DMDIR = 0x80000000 // mode bit for directories + DMAPPEND = 0x40000000 // mode bit for append only files + DMEXCL = 0x20000000 // mode bit for exclusive use files + DMMOUNT = 0x10000000 // mode bit for mounted channel + DMAUTH = 0x08000000 // mode bit for authentication file + DMTMP = 0x04000000 // mode bit for non-backed-up file + DMREAD = 0x4 // mode bit for read permission + DMWRITE = 0x2 // mode bit for write permission + DMEXEC = 0x1 // mode bit for execute permission +) + +const ( + NOTAG Tag = 0xFFFF // no tag specified + NOFID FID = 0xFFFFFFFF // no fid specified + // We reserve tag NOTAG and tag 0. 0 is a troublesome value to pass + // around, since it is also a default value and using it can hide errors + // in the code. + NumTags = 1<<16 - 2 +) + +// Error values +const ( + EPERM = 1 + ENOENT = 2 + EIO = 5 + EACCES = 13 + EEXIST = 17 + ENOTDIR = 20 + EINVAL = 22 +) + +// Types contained in 9p messages. +type ( + MType uint8 + Mode uint8 + NumEntries uint16 + Tag uint16 + FID uint32 + MaxSize uint32 + Count int32 + Perm uint32 + Offset uint64 + Data []byte + // Some []byte fields are encoded with a 16-bit length, e.g. stat data. + // We use this type to tag such fields. The parameters are still []byte, + // this was just the only way I could think of to make the stub generator do the right + // thing. + DataCnt16 byte // []byte with a 16-bit count. +) + +// Error represents a 9P2000 error +type Error struct { + Err string +} + +// File identifier +type QID struct { + Type uint8 // type of the file (high 8 bits of the mode) + Version uint32 // version number for the path + Path uint64 // server's unique identification of the file +} + +// Dir describes a file +type Dir struct { + Type uint16 + Dev uint32 + QID QID // file's QID + Mode uint32 // permissions and flags + Atime uint32 // last access time in seconds + Mtime uint32 // last modified time in seconds + Length uint64 // file length in bytes + Name string // file name + User string // owner name + Group string // group name + ModUser string // name of the last user that modified the file +} + +type Dispatcher func(s *Server, b *bytes.Buffer, t MType) error + +// N.B. In all packets, the wire order is assumed to be the order in which you +// put struct members. +// In an earlier version of this code we got really fancy and made it so you +// could have identically named fields in the R and T packets. It's only an issue +// in a trivial number of packets so we place the burden on you, the user, to make +// the names different. Also, you can't name struct members with the same names as the +// type. Sorry. But it keeps gen.go so much simpler. + +type TversionPkt struct { + TMsize MaxSize + TVersion string +} + +type RversionPkt struct { + RMsize MaxSize + RVersion string +} + +type TattachPkt struct { + SFID FID + AFID FID + Uname string + Aname string +} + +type RattachPkt struct { + QID QID +} + +type TflushPkt struct { + OTag Tag +} + +type RflushPkt struct { +} + +type TwalkPkt struct { + SFID FID + NewFID FID + Paths []string +} + +type RwalkPkt struct { + QIDs []QID +} + +type TopenPkt struct { + OFID FID + Omode Mode +} + +type RopenPkt struct { + OQID QID + IOUnit MaxSize +} + +type TcreatePkt struct { + OFID FID + Name string + CreatePerm Perm + Omode Mode +} + +type RcreatePkt struct { + OQID QID + IOUnit MaxSize +} + +type TclunkPkt struct { + OFID FID +} + +type RclunkPkt struct { +} + +type TremovePkt struct { + OFID FID +} + +type RremovePkt struct { +} + +type TstatPkt struct { + OFID FID +} + +type RstatPkt struct { + B []DataCnt16 +} + +type TwstatPkt struct { + OFID FID + B []DataCnt16 +} + +type RwstatPkt struct { +} + +type TreadPkt struct { + OFID FID + Off Offset + Len Count +} + +type RreadPkt struct { + Data []byte +} + +type TwritePkt struct { + OFID FID + Off Offset + Data []byte +} + +type RwritePkt struct { + RLen Count +} + +type RerrorPkt struct { + Error string +} + +type DirPkt struct { + D Dir +} + +type RPCCall struct { + b []byte + Reply chan []byte +} + +type RPCReply struct { + b []byte +} + +/* rpc servers */ +type ClientOpt func(*Client) error +type ServerOpt func(*Server) error +type Tracer func(string, ...interface{}) + +type NineServer interface { + Rversion(MaxSize, string) (MaxSize, string, error) + Rattach(FID, FID, string, string) (QID, error) + Rwalk(FID, FID, []string) ([]QID, error) + Ropen(FID, Mode) (QID, MaxSize, error) + Rcreate(FID, string, Perm, Mode) (QID, MaxSize, error) + Rstat(FID) ([]byte, error) + Rwstat(FID, []byte) error + Rclunk(FID) error + Rremove(FID) error + Rread(FID, Offset, Count) ([]byte, error) + Rwrite(FID, Offset, []byte) (Count, error) + Rflush(Otag Tag) error +} + +var ( + RPCNames = map[MType]string{ + Tversion: "Tversion", + Rversion: "Rversion", + Tauth: "Tauth", + Rauth: "Rauth", + Tattach: "Tattach", + Rattach: "Rattach", + Terror: "Terror", + Rerror: "Rerror", + Tflush: "Tflush", + Rflush: "Rflush", + Twalk: "Twalk", + Rwalk: "Rwalk", + Topen: "Topen", + Ropen: "Ropen", + Tcreate: "Tcreate", + Rcreate: "Rcreate", + Tread: "Tread", + Rread: "Rread", + Twrite: "Twrite", + Rwrite: "Rwrite", + Tclunk: "Tclunk", + Rclunk: "Rclunk", + Tremove: "Tremove", + Rremove: "Rremove", + Tstat: "Tstat", + Rstat: "Rstat", + Twstat: "Twstat", + Rwstat: "Rwstat", + } +) diff --git a/src/vendor/github.com/Harvey-OS/ninep/protocol/protocol_test.go b/src/vendor/github.com/Harvey-OS/ninep/protocol/protocol_test.go new file mode 100644 index 000000000..2ada3de64 --- /dev/null +++ b/src/vendor/github.com/Harvey-OS/ninep/protocol/protocol_test.go @@ -0,0 +1,520 @@ +// Copyright 2009 The Ninep Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package protocol + +import ( + "bytes" + "fmt" + "net" + "os" + "reflect" + "testing" +) + +var ( + removedFID2 bool +) + +func print(f string, args ...interface{}) { + fmt.Fprintf(os.Stderr, f+"\n", args...) +} + +func newEcho() *echo { + return &echo{ + qids: make(map[FID]QID), + } +} + +// Two files, dotu was true. +var testunpackbytes = []byte{ + 79, 0, 0, 0, 0, 0, 0, 0, 0, 228, 193, 233, 248, 44, 145, 3, 0, 0, 0, 0, 0, 164, 1, 0, 0, 0, 0, 0, 0, 47, 117, 180, 83, 102, 3, 0, 0, 0, 0, 0, 0, 6, 0, 112, 97, 115, 115, 119, 100, 4, 0, 110, 111, 110, 101, 4, 0, 110, 111, 110, 101, 4, 0, 110, 111, 110, 101, 0, 0, 232, 3, 0, 0, 232, 3, 0, 0, 255, 255, 255, 255, 78, 0, 0, 0, 0, 0, 0, 0, 0, 123, 171, 233, 248, 42, 145, 3, 0, 0, 0, 0, 0, 164, 1, 0, 0, 0, 0, 0, 0, 41, 117, 180, 83, 195, 0, 0, 0, 0, 0, 0, 0, 5, 0, 104, 111, 115, 116, 115, 4, 0, 110, 111, 110, 101, 4, 0, 110, 111, 110, 101, 4, 0, 110, 111, 110, 101, 0, 0, 232, 3, 0, 0, 232, 3, 0, 0, 255, 255, 255, 255, +} + +/* +func testUnpackDir(t *testing.T) { + b := testunpackbytes + for len(b) > 0 { + var err error + if _, b, _, err = UnpackDir(b, true); err != nil { + t.Fatalf("Unpackdir: %v", err) + } + } +} +*/ +func TestEncode(t *testing.T) { + // The traces used in this array came from running 9p servers and clients. + // Except for flush, which we made up. + // TODO: put the replies in, then the decode testing when we get decode done. + var tests = []struct { + n string + b []byte + f func(b *bytes.Buffer) + }{ + { + "TVersion test with 8192 byte msize and 9P2000", + []byte{19, 0, 0, 0, 100, 0x55, 0xaa, 0, 32, 0, 0, 6, 0, 57, 80, 50, 48, 48, 48}, + func(b *bytes.Buffer) { MarshalTversionPkt(b, Tag(0xaa55), 8192, "9P2000") }, + }, + { + "RVersion test with 8192 byte msize and 9P2000", + []byte{19, 0, 0, 0, 101, 0xaa, 0x55, 0, 32, 0, 0, 6, 0, 57, 80, 50, 48, 48, 48}, + func(b *bytes.Buffer) { MarshalRversionPkt(b, Tag(0x55aa), 8192, "9P2000") }, + }, + /* + { + "Twalk tag 0 fid 0 newfid 1 to null", + []byte{23, 0, 0, 0, 110, 0xaa, 0x55, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 4, 0, 110, 117, 108, 108}, + func(b *bytes.Buffer) { MarshalTwalkPkt(b, Tag(0x55aa), 0, 1, []string{"null",}) }, + }, + { + "Flush test with tag 1 and oldtag 2", + []byte{9, 0, 0, 0, 108, 1, 0, 2, 0}, + []interface{}{Tflush, Tag(1), Tag(2)}, + }, + { + "Auth test with tag 0, fid 0,uname rminnich", + []byte{21, 0, 0, 0, 102, 0, 0, 0, 0, 0, 0, 8, 0, 114, 109, 105, 110, 110, 105, 99, 104}, + []interface{}{Tauth, Tag(0), FID(0), "rminnich"}, + }, + { + "Attach test with tag 0, fid 0, afid -1, uname rminnich", + []byte{28, 0, 0, 0, 104, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 8, 0, 114, 109, 105, 110, 110, 105, 99, 104, 1, 0, 47}, + []interface{}{Tattach, Tag(0), FID(0), NOFID, "rminnich", "/"}, + }, + { + "Tauth with an rerror of no user required", + //Tauth tag 1 afid 45 uname 'rminnich' nuname 4294967295 aname '' + []byte{23,0,0,0,102,1,0,45,0,0,0,8,0,114,109,105,110,110,105,99,104,0,0}, + []interface{}{Tauth, Tag(1), FID(45), "rminnich", ""}, + // [39 0 0 0 107 1 0 30 0 110 111 32 97 117 116 104 101 110 116 105 99 97 116 105 111 110 32 114 101 113 117 105 114 101 100 58 32 50 50] + //Rerror tag 1 ename 'no authentication required: 22' ecode 0 + }, + { + "Tattach from Harvey to ninep: Tattach tag 1 fid 48 afid 4294967295 uname 'rminnich' nuname 4294967295 aname ''", + []byte{27,0,0,0,104,1,0,48,0,0,0,255,255,255,255,8,0,114,109,105,110,110,105,99,104,0,0}, + []interface{}{Tattach, Tag(1), FID(48), NOFID, "rminnich", ""}, + // 20 0 0 0 105 1 0 128 99 207 44 145 115 221 96 0 0 0 0 0] + // Rattach tag 1 aqid (60dd73 912ccf63 'd') + }, + { + "Topen tag 0 fid 1 mode 2", + []byte{12, 0, 0, 0, 112, 0, 0, 1, 0, 0, 0, 2}, + []interface{}{Topen, Tag(0), FID(1), Mode(2)}, + }, + { + "Tread tag 0 fid 1 offset 0 count 8192", + []byte{23, 0, 0, 0, 116, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0}, + []interface{}{Tread, Tag(0), FID(1), Offset(0), Count(8192)}, + }, + { + "Tstat tag 1 fid 49", + []byte{11, 0, 0, 0, 124, 1, 0, 49, 0, 0, 0}, + // Rstat + // + //[84,0,0,0,125,1,0,75,0,73,0,0,0,0,0,0,0,128,99,207,44,145,115,221,96,0,0,0,0,0,253,1,0,128,109,185,47,86,196,66,41,86,0,16,0,0,0,0,0,0,6,0,104,97,114,118,101,121,8,0,114,109,105,110,110,105,99,104,8,0,114,109,105,110,110,105,99,104,4,0,110,111,110,101] + + //Rstat tag 1 st ('harvey' 'rminnich' 'rminnich' 'none' q (60dd73 912ccf63 'd') m d775 at 1445968237 mt 1445544644 l 4096 t 0 d 0 ext ) + []interface{}{Tstat, Tag(1), FID(49)}, + }, + { + "Twrite tag 3 fid 139 offset 0 count 3", + []byte{26, 0, 0, 0, 118, 3, 0, 139, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 104, 105, 10}, + // rwrite []byte{11,0,0,0,119,3,0,3,0,0,0} + []interface{}{Twrite, Tag(3), FID(139), Offset(0), Count(3), []byte("hi\n")}, + }, + { + "Tclunk tag 1 fid 49", + []byte{11, 0, 0, 0, 120, 1, 0, 49, 0, 0, 0}, + // rclunk 7 0 0 0 121 1 0] + []interface{}{Tclunk, Tag(1), FID(49)}, + }, + { + "Tremove tag 1 fid 49", + []byte{11, 0, 0, 0, 122, 1, 0, 49, 0, 0, 0}, + // rclunk 7 0 0 0 121 1 0] + []interface{}{Tremove, Tag(1), FID(49)}, + }, + { + "Twstat tag 3 fid 49 ", + //Twstat tag 3 fid 49 st ('' '' '' '' q (ffffffffffffffff ffffffff 'daAltL') m daAltDSPL777 at 4294967295 mt 1445968327 l 18446744073709551615 t 65535 d 4294967295 ext ) + []byte{62, 0, 0, 0, 126, 3, 0, 49, 0, 0, 0, 49, 0, 47, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 199, 185, 47, 86, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0}, + // Rwstat [11 0 0 0 120 3 0 49 0 0 0] + []interface{}{Twstat, Tag(3), FID(49), &Dir{ /* TODO: remove this size + Size: 47, + Type: math.MaxUint16, + Dev: math.MaxUint32, + Qid: Qid{Type: math.MaxUint8, Version: math.MaxUint32, Path: math.MaxUint64}, + Mode: math.MaxUint32, + Atime: 4294967295, + Mtime: 1445968327, + Length: 18446744073709551615, + Name: "", + Uid: "", + Gid: "", + Muid: "", + }, + }, + }, + { + "Tcreate tag 3 fid 74 name 'y' perm 666 mode 0", + []byte{19,0,0,0,114,3,0,74,0,0,0,1,0,121,182,1,0,0,0}, + []interface{}{Tcreate, Tag(3), FID(74), "y", Perm(0666), Mode(0)}, + /// rcreate [24 0 0 0 115 3 0 0 226 200 71 172 45 166 98 0 0 0 0 0 0 0 0 0] + // Rcreate tag 3 qid (62a62d ac47c8e2 '') iounit 0 + },*/ + } + + for _, v := range tests { + var b bytes.Buffer + v.f(&b) + if !reflect.DeepEqual(v.b, b.Bytes()) { + t.Errorf("Mismatch on %v: Got\n%v[%v], want\n%v[%v]", v.n, b.Bytes(), len(b.Bytes()), v.b, len(v.b)) + } + } + +} + +/* +func testDecode(t *testing.T) { + var tests = []struct { + n string + b []byte + f func(b *bytes.Buffer) error + }{ + { + "TVersion test with 8192 byte msize and 9P2000", + []byte{19, 0, 0, 0, 100, 255, 255, 0, 32, 0, 0, 6, 0, 57, 80, 50, 48, 48, 48}, + func (b *bytes.Buffer){ MarshalTversionPkt(b, NOTAG, 8192, "9P2000")}, + }, + + for _, v := range tests { + var b bytes.Buffer + v.f(&b) + if !reflect.DeepEqual(v.b, b.Bytes()) { + t.Errorf("Mismatch on %v: Got %v[%v], want %v[%v]", v.n, b.Bytes(), len(b.Bytes()), v.b, len(v.b)) + } + } + +} +*/ + +func TestTags(t *testing.T) { + c, err := NewClient() + if err != nil { + t.Fatalf("%v", err) + } + _ = c.GetTag() + if len(c.Tags) != NumTags-1 { + t.Errorf("Got one tag, len(tags) is %d, want %d", len(c.Tags), NumTags-1) + } +} + +type echo struct { + qids map[FID]QID +} + +func (e *echo) Rversion(msize MaxSize, version string) (MaxSize, string, error) { + if version != "9P2000" { + return 0, "", fmt.Errorf("%v not supported; only 9P2000", version) + } + return msize, version, nil +} + +func (e *echo) Rattach(FID, FID, string, string) (QID, error) { + return QID{}, nil +} + +func (e *echo) Rflush(o Tag) error { + switch o { + case 3: + // Make it fancier, later. + return nil + } + return fmt.Errorf("Rflush: bad Tag %v", o) +} + +func (e *echo) Rwalk(fid FID, newfid FID, paths []string) ([]QID, error) { + //fmt.Printf("walk(%d, %d, %d, %v\n", fid, newfid, len(paths), paths) + if len(paths) > 1 { + return nil, nil + } + switch paths[0] { + case "null": + return []QID{QID{Type: 0, Version: 0, Path: 0xaa55}}, nil + } + return nil, nil +} + +func (e *echo) Ropen(fid FID, mode Mode) (QID, MaxSize, error) { + //fmt.Printf("open(%v, %v\n", fid, mode) + return QID{}, 4000, nil +} +func (e *echo) Rcreate(fid FID, name string, perm Perm, mode Mode) (QID, MaxSize, error) { + //fmt.Printf("open(%v, %v\n", fid, mode) + return QID{}, 5000, nil +} +func (e *echo) Rclunk(f FID) error { + switch int(f) { + case 2: + // Make it fancier, later. + if removedFID2 { + return fmt.Errorf("Clunk: bad FID %v", f) + } + return nil + } + //fmt.Printf("clunk(%v)\n", f) + return fmt.Errorf("Clunk: bad FID %v", f) +} +func (e *echo) Rstat(f FID) ([]byte, error) { + switch int(f) { + case 2: + // Make it fancier, later. + return []byte{}, nil + } + //fmt.Printf("stat(%v)\n", f) + return []byte{}, fmt.Errorf("Stat: bad FID %v", f) +} +func (e *echo) Rwstat(f FID, s []byte) error { + switch int(f) { + case 2: + // Make it fancier, later. + return nil + } + //fmt.Printf("stat(%v)\n", f)y + return fmt.Errorf("Wstat: bad FID %v", f) +} +func (e *echo) Rremove(f FID) error { + switch int(f) { + case 2: + // Make it fancier, later. + removedFID2 = true + return nil + } + //fmt.Printf("remove(%v)\n", f) + return fmt.Errorf("Remove: bad FID %v", f) +} +func (e *echo) Rread(f FID, o Offset, c Count) ([]byte, error) { + switch int(f) { + case 2: + // Make it fancier, later. + return []byte("HI"), nil + } + return nil, fmt.Errorf("Read: bad FID %v", f) +} + +func (e *echo) Rwrite(f FID, o Offset, b []byte) (Count, error) { + switch int(f) { + case 2: + // Make it fancier, later. + return Count(len(b)), nil + } + return -1, fmt.Errorf("Write: bad FID %v", f) +} +func TestTManyRPCs(t *testing.T) { + p, p2 := net.Pipe() + + c, err := NewClient(func(c *Client) error { + c.FromNet, c.ToNet = p, p + return nil + }, + func(c *Client) error { + c.Msize = 8192 + c.Trace = print // t.Logf + return nil + }) + if err != nil { + t.Fatalf("%v", err) + } + t.Logf("Client is %v", c.String()) + + e := newEcho() + s, err := NewServer(e, Trace(print)) + if err != nil { + t.Fatalf("NewServer: want nil, got %v", err) + } + + if err := s.Accept(p2); err != nil { + t.Fatalf("Accept: want nil, got %v", err) + } + + for i := 0; i < 256*1024; i++ { + _, _, err := c.CallTversion(8000, "9P2000") + if err != nil { + t.Fatalf("CallTversion: want nil, got %v", err) + } + } +} + +func TestTMessages(t *testing.T) { + p, p2 := net.Pipe() + + c, err := NewClient(func(c *Client) error { + c.FromNet, c.ToNet = p, p + return nil + }, + func(c *Client) error { + c.Msize = 8192 + c.Trace = print // t.Logf + return nil + }) + if err != nil { + t.Fatalf("%v", err) + } + t.Logf("Client is %v", c.String()) + + e := newEcho() + s, err := NewServer(e, Trace(print)) + if err != nil { + t.Fatalf("NewServer: want nil, got %v", err) + } + + if err := s.Accept(p2); err != nil { + t.Fatalf("Accept: want nil, got %v", err) + } + + // If things really go to hell, change this to true. + if false { + m, v, err := c.CallTversion(8000, "9P2000") + if err != nil { + t.Fatalf("CallTversion: want nil, got %v", err) + } + t.Logf("CallTversion: msize %v version %v", m, v) + t.Fatalf("Quit early") + } + + m, v, err := c.CallTversion(8000, "9p3000") + if err == nil { + t.Fatalf("CallTversion: want err, got nil") + } + t.Logf("CallTversion: wanted an error and got %v", err) + + m, v, err = c.CallTversion(8000, "9P2000") + if err != nil { + t.Fatalf("CallTversion: want nil, got %v", err) + } + t.Logf("CallTversion: msize %v version %v", m, v) + + t.Logf("Server is %v", s.String()) + a, err := c.CallTattach(0, 0, "", "") + if err != nil { + t.Fatalf("CallTattach: want nil, got %v", err) + } + t.Logf("Attach is %v", a) + w, err := c.CallTwalk(0, 1, []string{"hi", "there"}) + // There should never be an error. The indication of a failed walk is that + // the number of QIDS does not match. + if err != nil { + t.Fatalf("CallTwalk(0,1,[\"hi\", \"there\"]): want nil, got %v", err) + } + if len(w) != 0 { + t.Fatalf("CallTwalk(0,1,[\"hi\", \"there\"]): want 0 QIDS, got back %d", len(w)) + } + t.Logf("Walk is %v", w) + + w, err = c.CallTwalk(0, 1, []string{"null"}) + if err != nil { + t.Errorf("CallTwalk(0,1,\"null\"): want nil, got err %v", err) + } + if len(w) != 1 { + t.Errorf("CallTwalk(0,1,\"null\"): want 1 QIDs, got back %d", len(w)) + } + t.Logf("Walk is %v", w) + + q, iounit, err := c.CallTopen(1, 1) + if err != nil { + t.Fatalf("CallTopen: want nil, got %v", err) + } + t.Logf("Open is %v %v", q, iounit) + + d, err := c.CallTread(FID(2), 0, 5) + if err != nil { + t.Fatalf("CallTread: want nil, got %v", err) + } + t.Logf("Read is %v", d) + + _, err = c.CallTwrite(FID(2), 0, d) + if err != nil { + t.Fatalf("CallTread: want nil, got %v", err) + } + t.Logf("Read is %v", s) + + if err := c.CallTclunk(FID(2)); err != nil { + t.Fatalf("CallTclunk: want nil, got %v", err) + } + if err := c.CallTremove(FID(1)); err == nil { + t.Fatalf("CallTremove: want err, got nil") + } + if err := c.CallTremove(FID(2)); err != nil { + t.Fatalf("CallTremove: want nil, got %v", err) + } + if err := c.CallTclunk(FID(2)); err == nil { + t.Fatalf("Callclunk on removed file: want err, got nil") + } + if err := c.CallTremove(FID(1)); err == nil { + t.Fatalf("CallTremove: want err, got nil") + } + st, err := c.CallTstat(FID(2)) + if err != nil { + t.Fatalf("CallTstat: want nil, got %v", err) + } + t.Logf("Stat: Got %v", st) + + if _, err := c.CallTstat(FID(1)); err == nil { + t.Fatalf("CallTstat: want err, got nil") + } + if err := c.CallTwstat(FID(2), []byte{}); err != nil { + t.Fatalf("CallTwstat: want nil, got %v", err) + } + + if err := c.CallTwstat(FID(1), []byte{}); err == nil { + t.Fatalf("CallTwstat: want err, got nil") + } + if err := c.CallTflush(3); err != nil { + t.Fatalf("CallTflush: want nil, got %v", err) + } + + if err := c.CallTflush(2); err == nil { + t.Fatalf("CallTflush: want err, got nil") + } +} + +func BenchmarkNull(b *testing.B) { + p, p2 := net.Pipe() + + c, err := NewClient(func(c *Client) error { + c.FromNet, c.ToNet = p, p + return nil + }, + func(c *Client) error { + c.Msize = 8192 + return nil + }) + if err != nil { + b.Fatalf("%v", err) + } + b.Logf("Client is %v", c.String()) + + e := newEcho() + s, err := NewServer(e, func(s *Server) error { + s.NS = e + return nil + }) + + if err != nil { + b.Fatalf("NewServer: want nil, got %v", err) + } + + if err := s.Accept(p2); err != nil { + b.Fatalf("Accept: want nil, got %v", err) + } + + b.Logf("%d iterations", b.N) + for i := 0; i < b.N; i++ { + if _, err := c.CallTread(FID(2), 0, 5); err != nil { + b.Fatalf("CallTread: want nil, got %v", err) + } + } + +} diff --git a/src/vendor/github.com/Harvey-OS/ninep/protocol/server.go b/src/vendor/github.com/Harvey-OS/ninep/protocol/server.go new file mode 100644 index 000000000..3a3ae7de4 --- /dev/null +++ b/src/vendor/github.com/Harvey-OS/ninep/protocol/server.go @@ -0,0 +1,291 @@ +// Copyright 2012 The Ninep Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// This code is imported from the old ninep repo, +// with some changes. + +package protocol + +import ( + "bytes" + "errors" + "fmt" + "io" + "net" + "sync" + "time" +) + +const DefaultAddr = ":5640" + +// Server is a 9p server. +// For now it's extremely serial. But we will use a chan for replies to ensure that +// we can go to a more concurrent one later. +type Server struct { + NS NineServer + D Dispatcher + + // TCP address to listen on, default is DefaultAddr + Addr string + + // trace function for logging + trace Tracer + + // mu guards below + mu sync.Mutex + + listeners map[net.Listener]struct{} +} + +type conn struct { + // server on which the connection arrived. + server *Server + + // rwc is the underlying network connection. + rwc net.Conn + + // remoteAddr is rwc.RemoteAddr().String(). See note in net/http/server.go. + remoteAddr string + + // replies + replies chan RPCReply + + // dead is set to true when we finish reading packets. + dead bool +} + +func NewServer(ns NineServer, opts ...ServerOpt) (*Server, error) { + s := &Server{ + NS: ns, + D: Dispatch, + trace: nologf, + } + + for _, o := range opts { + if err := o(s); err != nil { + return nil, err + } + } + return s, nil +} + +func Trace(tracer Tracer) ServerOpt { + return func(s *Server) error { + if tracer == nil { + return errors.New("tracer cannot be nil") + } + s.trace = tracer + return nil + } +} + +// nologf does nothing and is the default trace function +func nologf(format string, args ...interface{}) {} + +func (s *Server) newConn(rwc net.Conn) *conn { + c := &conn{ + server: s, + rwc: rwc, + replies: make(chan RPCReply, NumTags), + } + + return c +} + +// trackListener from http.Server +func (s *Server) trackListener(ln net.Listener, add bool) { + s.mu.Lock() + defer s.mu.Unlock() + + if s.listeners == nil { + s.listeners = make(map[net.Listener]struct{}) + } + + if add { + s.listeners[ln] = struct{}{} + } else { + delete(s.listeners, ln) + } +} + +// closeListenersLocked from http.Server +func (s *Server) closeListenersLocked() error { + var err error + for ln := range s.listeners { + if cerr := ln.Close(); cerr != nil && err == nil { + err = cerr + } + delete(s.listeners, ln) + } + return err +} + +// ListenAndServe starts a new Listener on e.Addr and then calls serve. +func (s *Server) ListenAndServe() error { + addr := s.Addr + if addr == "" { + addr = DefaultAddr + } + + ln, err := net.Listen("tcp", addr) + if err != nil { + return err + } + + return s.Serve(ln) +} + +// Serve accepts incoming connections on the Listener and calls e.Accept on +// each connection. +func (s *Server) Serve(ln net.Listener) error { + defer ln.Close() + + var tempDelay time.Duration // how long to sleep on accept failure + + s.trackListener(ln, true) + defer s.trackListener(ln, false) + + // from http.Server.Serve + for { + conn, err := ln.Accept() + if err != nil { + if ne, ok := err.(net.Error); ok && ne.Temporary() { + if tempDelay == 0 { + tempDelay = 5 * time.Millisecond + } else { + tempDelay *= 2 + } + if max := 1 * time.Second; tempDelay > max { + tempDelay = max + } + s.trace("ufs: Accept error: %v; retrying in %v", err, tempDelay) + time.Sleep(tempDelay) + continue + } + return err + } + tempDelay = 0 + + if err := s.Accept(conn); err != nil { + return err + } + } +} + +// Accept a new connection, typically called via Serve but may be called +// directly if there's a connection from an exotic listener. +func (s *Server) Accept(conn net.Conn) error { + c := s.newConn(conn) + + go c.serve() + return nil +} + +// Shutdown closes all active listeners. It does not close all active +// connections but probably should. +func (s *Server) Shutdown() error { + s.mu.Lock() + defer s.mu.Unlock() + + return s.closeListenersLocked() +} + +func (s *Server) String() string { + // TODO + return "" +} + +func (c *conn) String() string { + return fmt.Sprintf("Dead %v %d replies pending", c.dead, len(c.replies)) +} + +func (c *conn) logf(format string, args ...interface{}) { + // prepend some info about the conn + c.server.trace("[%v] "+format, append([]interface{}{c.remoteAddr}, args...)...) +} + +func (c *conn) serve() { + if c.rwc == nil { + c.dead = true + return + } + + c.remoteAddr = c.rwc.RemoteAddr().String() + + defer c.rwc.Close() + + c.logf("Starting readNetPackets") + + for !c.dead { + l := make([]byte, 7) + if n, err := c.rwc.Read(l); err != nil || n < 7 { + c.logf("readNetPackets: short read: %v", err) + c.dead = true + return + } + sz := int64(l[0]) + int64(l[1])<<8 + int64(l[2])<<16 + int64(l[3])<<24 + t := MType(l[4]) + b := bytes.NewBuffer(l[5:]) + r := io.LimitReader(c.rwc, sz-7) + if _, err := io.Copy(b, r); err != nil { + c.logf("readNetPackets: short read: %v", err) + c.dead = true + return + } + c.logf("readNetPackets: got %v, len %d, sending to IO", RPCNames[MType(l[4])], b.Len()) + //panic(fmt.Sprintf("packet is %v", b.Bytes()[:])) + //panic(fmt.Sprintf("s is %v", s)) + if err := c.server.D(c.server, b, t); err != nil { + c.logf("%v: %v", RPCNames[MType(l[4])], err) + } + c.logf("readNetPackets: Write %v back", b) + amt, err := c.rwc.Write(b.Bytes()) + if err != nil { + c.logf("readNetPackets: write error: %v", err) + c.dead = true + return + } + c.logf("Returned %v amt %v", b, amt) + } +} + +func (s *Server) NineServer() NineServer { + return s.NS +} + +// Dispatch dispatches request to different functions. +// It's also the the first place we try to establish server semantics. +// We could do this with interface assertions and such a la rsc/fuse +// but most people I talked do disliked that. So we don't. If you want +// to make things optional, just define the ones you want to implement in this case. +func Dispatch(s *Server, b *bytes.Buffer, t MType) error { + switch t { + case Tversion: + return s.SrvRversion(b) + case Tattach: + return s.SrvRattach(b) + case Tflush: + return s.SrvRflush(b) + case Twalk: + return s.SrvRwalk(b) + case Topen: + return s.SrvRopen(b) + case Tcreate: + return s.SrvRcreate(b) + case Tclunk: + return s.SrvRclunk(b) + case Tstat: + return s.SrvRstat(b) + case Twstat: + return s.SrvRwstat(b) + case Tremove: + return s.SrvRremove(b) + case Tread: + return s.SrvRread(b) + case Twrite: + return s.SrvRwrite(b) + } + // This has been tested by removing Attach from the switch. + ServerError(b, fmt.Sprintf("Dispatch: %v not supported", RPCNames[t])) + return nil +} diff --git a/src/vendor/github.com/Harvey-OS/ninep/protocol/types.go b/src/vendor/github.com/Harvey-OS/ninep/protocol/types.go new file mode 100644 index 000000000..da6c2bc38 --- /dev/null +++ b/src/vendor/github.com/Harvey-OS/ninep/protocol/types.go @@ -0,0 +1,40 @@ +// Copyright 2009 The Ninep Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package protocol + + +// A File is defined by a QID. File Servers never see a FID. +// There are two channels. The first is for normal requests. +// The second is for Flushes. File server code always +// checks the flush channel first. At the same time, server code +// puts the flush into both channels, so the server code has some +// idea when the flush entered the queue. This is very similar +// to how MSI-X works on PCIe ... +type File struct { +} + +/* +// A FileOp is a function to call, an abort channel, and a reply channel +type FileOp struct { + f func() error + abort chan abort + reply chan +} +*/ + +// A service is a closure which returns an error or nil. +// It writes its result down the provided channel. +// It looks for flushes on the flushchan before doing its +// function, and will respond to all flushes while any are pending. +type Service func(func() error, chan FID) + +// Server maintains file system server state. This is inclusive of RPC +// server state plus more. In our case when we walk to a fid we kick +// off a goroutine to manage it. As a result we need a map of Tag to FID +// so we know what to do about Tflush. +type FileServer struct { + Server + Versioned bool +} diff --git a/src/vendor/versions b/src/vendor/versions index 1a39b7812..82850d846 100644 --- a/src/vendor/versions +++ b/src/vendor/versions @@ -17,3 +17,5 @@ db96a2b759cdef4f11a34506a42eb8d1290c598e github.com/miekg/dns fe81982801b5861ad01e643527daac63629b7fdd github.com/goftp/server 88609521dc4b6c858fd4c98b628147da928ce4ac github.com/peterh/liner + +35ad2879c0a351783e807c89f139dd9a1aed52c4 github.com/Harvey-OS/ninep (jcrussell/ninep)