Commit Diff


commit - ba353f358bc496e69ab151ff377a4d041f306ad5
commit + deb98ab43c4d46aadb8214e05c8178b2eceb1fae
blob - 452188d9186f024575da5ff0019c5a295a5dfa61
blob + 95fb256b47676759a4ae4face3f337bfea447540
--- client.go
+++ client.go
@@ -1,9 +1,6 @@
 package p9pnew
 
 import (
-	"fmt"
-	"log"
-
 	"golang.org/x/net/context"
 
 	"net"
@@ -43,178 +40,199 @@ func (c *client) Version() (int, string) {
 }
 
 func (c *client) Auth(ctx context.Context, afid Fid, uname, aname string) (Qid, error) {
-	panic("not implemented")
+	m := MessageTauth{
+		Afid:  afid,
+		Uname: uname,
+		Aname: aname,
+	}
+
+	resp, err := c.transport.send(ctx, m)
+	if err != nil {
+		return Qid{}, nil
+	}
+
+	rauth, ok := resp.(MessageRauth)
+	if !ok {
+		return Qid{}, ErrUnexpectedMsg
+	}
+
+	return rauth.Qid, nil
 }
 
 func (c *client) Attach(ctx context.Context, fid, afid Fid, uname, aname string) (Qid, error) {
-	log.Println("client attach", fid, aname)
-	fcall := &Fcall{
-		Type: Tattach,
-		Message: &MessageTattach{
-			Fid:   fid,
-			Afid:  afid,
-			Uname: uname,
-			Aname: aname,
-		},
+	m := MessageTattach{
+		Fid:   fid,
+		Afid:  afid,
+		Uname: uname,
+		Aname: aname,
 	}
 
-	resp, err := c.transport.send(ctx, fcall)
+	resp, err := c.transport.send(ctx, m)
 	if err != nil {
 		return Qid{}, err
 	}
 
-	mrr, ok := resp.Message.(*MessageRattach)
+	rattach, ok := resp.(MessageRattach)
 	if !ok {
-		return Qid{}, fmt.Errorf("invalid rpc response for attach message: %v", resp)
+		return Qid{}, ErrUnexpectedMsg
 	}
 
-	return mrr.Qid, nil
+	return rattach.Qid, nil
 }
 
 func (c *client) Clunk(ctx context.Context, fid Fid) error {
-	fcall := newFcall(&MessageTclunk{
+	resp, err := c.transport.send(ctx, MessageTclunk{
 		Fid: fid,
 	})
-
-	resp, err := c.transport.send(ctx, fcall)
 	if err != nil {
 		return err
 	}
 
-	if resp.Type != Rclunk {
-		return fmt.Errorf("incorrect response type: %v", resp)
+	_, ok := resp.(MessageRclunk)
+	if !ok {
+		return ErrUnexpectedMsg
 	}
 
 	return nil
 }
 
 func (c *client) Remove(ctx context.Context, fid Fid) error {
-	panic("not implemented")
+	resp, err := c.transport.send(ctx, MessageTremove{
+		Fid: fid,
+	})
+	if err != nil {
+		return err
+	}
+
+	_, ok := resp.(MessageRremove)
+	if !ok {
+		return ErrUnexpectedMsg
+	}
+
+	return nil
 }
 
 func (c *client) Walk(ctx context.Context, fid Fid, newfid Fid, names ...string) ([]Qid, error) {
 	if len(names) > 16 {
-		return nil, fmt.Errorf("too many elements in wname")
+		return nil, ErrWalkLimit
 	}
 
-	fcall := &Fcall{
-		Type: Twalk,
-		Message: &MessageTwalk{
-			Fid:    fid,
-			Newfid: newfid,
-			Wnames: names,
-		},
-	}
-
-	resp, err := c.transport.send(ctx, fcall)
+	resp, err := c.transport.send(ctx, MessageTwalk{
+		Fid:    fid,
+		Newfid: newfid,
+		Wnames: names,
+	})
 	if err != nil {
 		return nil, err
 	}
 
-	mrr, ok := resp.Message.(*MessageRwalk)
+	rwalk, ok := resp.(MessageRwalk)
 	if !ok {
-		return nil, fmt.Errorf("invalid rpc response for walk message: %v", resp)
+		return nil, ErrUnexpectedMsg
 	}
 
-	return mrr.Qids, nil
+	return rwalk.Qids, nil
 }
 
 func (c *client) Read(ctx context.Context, fid Fid, p []byte, offset int64) (n int, err error) {
-	// TODO(stevvooe): Split up reads into multiple messages based on iounit.
-	// For now, we just support full blast. I mean, why not?
-	fcall := &Fcall{
-		Type: Tread,
-		Message: &MessageTread{
-			Fid:    fid,
-			Offset: uint64(offset),
-			Count:  uint32(len(p)),
-		},
-	}
-
-	resp, err := c.transport.send(ctx, fcall)
+	resp, err := c.transport.send(ctx, MessageTread{
+		Fid:    fid,
+		Offset: uint64(offset),
+		Count:  uint32(len(p)),
+	})
 	if err != nil {
 		return 0, err
 	}
 
-	mrr, ok := resp.Message.(*MessageRread)
+	rread, ok := resp.(MessageRread)
 	if !ok {
-		return 0, fmt.Errorf("invalid rpc response for read message: %v", resp)
+		return 0, ErrUnexpectedMsg
 	}
 
-	return copy(p, mrr.Data), nil
+	return copy(p, rread.Data), nil
 }
 
 func (c *client) Write(ctx context.Context, fid Fid, p []byte, offset int64) (n int, err error) {
-	// TODO(stevvooe): Split up writes into multiple messages based on iounit.
-	// For now, we just support full blast. I mean, why not?
-	fcall := &Fcall{
-		Type: Twrite,
-		Message: &MessageTwrite{
-			Fid:    fid,
-			Offset: uint64(offset),
-			Data:   p,
-		},
-	}
-
-	resp, err := c.transport.send(ctx, fcall)
+	resp, err := c.transport.send(ctx, MessageTwrite{
+		Fid:    fid,
+		Offset: uint64(offset),
+		Data:   p,
+	})
 	if err != nil {
 		return 0, err
 	}
 
-	mrr, ok := resp.Message.(*MessageRwrite)
+	rwrite, ok := resp.(MessageRwrite)
 	if !ok {
-		return 0, fmt.Errorf("invalid rpc response for write message: %v", resp)
+		return 0, ErrUnexpectedMsg
 	}
 
-	return int(mrr.Count), nil
+	return int(rwrite.Count), nil
 }
 
-func (c *client) Open(ctx context.Context, fid Fid, mode uint8) (Qid, uint32, error) {
-	fcall := newFcall(&MessageTopen{
+func (c *client) Open(ctx context.Context, fid Fid, mode Flag) (Qid, uint32, error) {
+	resp, err := c.transport.send(ctx, MessageTopen{
 		Fid:  fid,
 		Mode: mode,
 	})
-
-	resp, err := c.transport.send(ctx, fcall)
 	if err != nil {
 		return Qid{}, 0, err
 	}
 
-	respmsg, ok := resp.Message.(*MessageRopen)
+	ropen, ok := resp.(MessageRopen)
 	if !ok {
-		return Qid{}, 0, fmt.Errorf("invalid rpc response for open message: %v", resp)
+		return Qid{}, 0, ErrUnexpectedMsg
 	}
 
-	return respmsg.Qid, respmsg.IOUnit, nil
+	return ropen.Qid, ropen.IOUnit, nil
 }
 
-func (c *client) Create(ctx context.Context, parent Fid, name string, perm uint32, mode uint32) (Qid, uint32, error) {
-	panic("not implemented")
+func (c *client) Create(ctx context.Context, parent Fid, name string, perm uint32, mode Flag) (Qid, uint32, error) {
+	resp, err := c.transport.send(ctx, MessageTcreate{
+		Fid:  parent,
+		Name: name,
+		Perm: perm,
+		Mode: mode,
+	})
+	if err != nil {
+		return Qid{}, 0, err
+	}
+
+	rcreate, ok := resp.(MessageRcreate)
+	if !ok {
+		return Qid{}, 0, ErrUnexpectedMsg
+	}
+
+	return rcreate.Qid, rcreate.IOUnit, nil
 }
 
 func (c *client) Stat(ctx context.Context, fid Fid) (Dir, error) {
-	fcall := newFcall(MessageTstat{Fid: fid})
-
-	resp, err := c.transport.send(ctx, fcall)
+	resp, err := c.transport.send(ctx, MessageTstat{Fid: fid})
 	if err != nil {
 		return Dir{}, err
 	}
 
-	respmsg, ok := resp.Message.(*MessageRstat)
+	rstat, ok := resp.(MessageRstat)
 	if !ok {
-		return Dir{}, fmt.Errorf("invalid rpc response for stat message: %v", resp)
+		return Dir{}, ErrUnexpectedMsg
 	}
 
-	return respmsg.Stat, nil
+	return rstat.Stat, nil
 }
 
-func (c *client) WStat(context.Context, Fid, Dir) error {
-	panic("not implemented")
-}
+func (c *client) WStat(ctx context.Context, fid Fid, dir Dir) error {
+	resp, err := c.transport.send(ctx, MessageTwstat{
+		Fid:  fid,
+		Stat: dir,
+	})
+	if err != nil {
+		return err
+	}
 
-func (c *client) flush(ctx context.Context, tag Tag) error {
-	// TODO(stevvooe): We need to fire and forget flush messages when a call
-	// context gets cancelled.
+	_, ok := resp.(MessageRwstat)
+	if !ok {
+		return ErrUnexpectedMsg
+	}
 
-	panic("not implemented")
+	return nil
 }
blob - 2da30033b5ccc50bec99a071ca5793ebeaec32f0
blob + 91c7ce686abe1d94fdf3f2832597df6565738a67
--- cmd/9pr/main.go
+++ cmd/9pr/main.go
@@ -187,7 +187,7 @@ func (c *fsCommander) cmdls(ctx context.Context, args 
 
 		if iounit < 1 {
 			msize, _ := c.session.Version()
-			iounit = msize - 24 // size of message max minus fcall io header (Rread)
+			iounit = uint32(msize - 24) // size of message max minus fcall io header (Rread)
 		}
 
 		p := make([]byte, iounit)
@@ -198,10 +198,10 @@ func (c *fsCommander) cmdls(ctx context.Context, args 
 		}
 
 		rd := bytes.NewReader(p[:n])
-
+		codec := p9pnew.NewCodec() // TODO(stevvooe): Need way to resolve codec based on session.
 		for {
 			var d p9pnew.Dir
-			if err := p9pnew.DecodeDir(rd, &d); err != nil {
+			if err := p9pnew.DecodeDir(codec, rd, &d); err != nil {
 				if err == io.EOF {
 					break
 				}
@@ -318,7 +318,7 @@ func (c *fsCommander) cmdcat(ctx context.Context, args
 
 	if iounit < 1 {
 		msize, _ := c.session.Version()
-		iounit = msize - 24 // size of message max minus fcall io header (Rread)
+		iounit = uint32(msize - 24) // size of message max minus fcall io header (Rread)
 	}
 
 	b := make([]byte, iounit)
blob - 5d884701a2cbc04f08d9dff5e5ddfcf4c4e27e27
blob + 82597e172e28481545b1081509d53f1f7a7c13bb
--- dispatcher.go
+++ dispatcher.go
@@ -33,7 +33,7 @@ func (d *dispatcher) handle(ctx context.Context, req *
 
 		resp = newFcall(MessageRauth{Qid: qid})
 	case Tattach:
-		reqmsg, ok := req.Message.(*MessageTattach)
+		reqmsg, ok := req.Message.(MessageTattach)
 		if !ok {
 			return nil, fmt.Errorf("bad message: %v message=%#v", req, req.Message)
 		}
@@ -47,7 +47,7 @@ func (d *dispatcher) handle(ctx context.Context, req *
 			Qid: qid,
 		})
 	case Twalk:
-		reqmsg, ok := req.Message.(*MessageTwalk)
+		reqmsg, ok := req.Message.(MessageTwalk)
 		if !ok {
 			return nil, fmt.Errorf("bad message: %v message=%#v", req, req.Message)
 		}
@@ -62,11 +62,11 @@ func (d *dispatcher) handle(ctx context.Context, req *
 			return nil, err
 		}
 
-		resp = newFcall(&MessageRwalk{
+		resp = newFcall(MessageRwalk{
 			Qids: qids,
 		})
 	case Topen:
-		reqmsg, ok := req.Message.(*MessageTopen)
+		reqmsg, ok := req.Message.(MessageTopen)
 		if !ok {
 			return nil, fmt.Errorf("bad message: %v message=%v", req, req.Message)
 		}
@@ -76,17 +76,17 @@ func (d *dispatcher) handle(ctx context.Context, req *
 			return nil, err
 		}
 
-		resp = newFcall(&MessageRopen{
+		resp = newFcall(MessageRopen{
 			Qid:    qid,
 			IOUnit: iounit,
 		})
 	case Tcreate:
-		reqmsg, ok := req.Message.(*MessageTcreate)
+		reqmsg, ok := req.Message.(MessageTcreate)
 		if !ok {
 			return nil, fmt.Errorf("bad message: %v message=%v", req, req.Message)
 		}
 
-		qid, iounit, err := d.session.Create(ctx, reqmsg.Fid, reqmsg.Name, reqmsg.Perm, uint32(reqmsg.Mode))
+		qid, iounit, err := d.session.Create(ctx, reqmsg.Fid, reqmsg.Name, reqmsg.Perm, reqmsg.Mode)
 		if err != nil {
 			return nil, err
 		}
@@ -97,7 +97,7 @@ func (d *dispatcher) handle(ctx context.Context, req *
 		})
 
 	case Tread:
-		reqmsg, ok := req.Message.(*MessageTread)
+		reqmsg, ok := req.Message.(MessageTread)
 		if !ok {
 			return nil, fmt.Errorf("bad message: %v message=%v", req, req.Message)
 		}
@@ -112,7 +112,7 @@ func (d *dispatcher) handle(ctx context.Context, req *
 			Data: p[:n],
 		})
 	case Twrite:
-		reqmsg, ok := req.Message.(*MessageTwrite)
+		reqmsg, ok := req.Message.(MessageTwrite)
 		if !ok {
 			return nil, fmt.Errorf("bad message: %v message=%v", req, req.Message)
 		}
@@ -126,7 +126,7 @@ func (d *dispatcher) handle(ctx context.Context, req *
 			Count: uint32(n),
 		})
 	case Tclunk:
-		reqmsg, ok := req.Message.(*MessageTclunk)
+		reqmsg, ok := req.Message.(MessageTclunk)
 		if !ok {
 			return nil, fmt.Errorf("bad message: %v message=%v", req, req.Message)
 		}
@@ -137,9 +137,9 @@ func (d *dispatcher) handle(ctx context.Context, req *
 			return nil, err
 		}
 
-		resp = newFcall(&MessageRclunk{})
+		resp = newFcall(MessageRclunk{})
 	case Tremove:
-		reqmsg, ok := req.Message.(*MessageTremove)
+		reqmsg, ok := req.Message.(MessageTremove)
 		if !ok {
 			return nil, fmt.Errorf("bad message: %v message=%v", req, req.Message)
 		}
@@ -150,7 +150,7 @@ func (d *dispatcher) handle(ctx context.Context, req *
 
 		resp = newFcall(&MessageRremove{})
 	case Tstat:
-		reqmsg, ok := req.Message.(*MessageTstat)
+		reqmsg, ok := req.Message.(MessageTstat)
 		if !ok {
 			return nil, fmt.Errorf("bad message: %v message=%v", req, req.Message)
 		}
@@ -160,7 +160,7 @@ func (d *dispatcher) handle(ctx context.Context, req *
 			return nil, err
 		}
 
-		resp = newFcall(&MessageRstat{
+		resp = newFcall(MessageRstat{
 			Stat: dir,
 		})
 	case Twstat:
blob - bfce22d1890ddf9139f07f5af8b9f279299a429c
blob + 326a180bfe49d872d8cbfae19d0b2e3f5e0d92fb
--- encoding.go
+++ encoding.go
@@ -19,6 +19,9 @@ type Codec interface {
 
 	// Marshal the value v into a byte slice.
 	Marshal(v interface{}) ([]byte, error)
+
+	// Size returns the encoded size for the target of v.
+	Size(v interface{}) int
 }
 
 func NewCodec() Codec {
@@ -41,8 +44,43 @@ func (c codec9p) Marshal(v interface{}) ([]byte, error
 	}
 
 	return b.Bytes(), nil
+}
+
+func (c codec9p) Size(v interface{}) int {
+	return int(size9p(v))
+}
+
+// DecodeDir decodes a directory entry from rd using the provided codec.
+func DecodeDir(codec Codec, rd io.Reader, d *Dir) error {
+	var ll uint16
+
+	// pull the size off the wire
+	if err := binary.Read(rd, binary.LittleEndian, &ll); err != nil {
+		return err
+	}
+
+	p := make([]byte, ll+2)
+	binary.LittleEndian.PutUint16(p, ll) // must have size at start
+
+	// read out the rest of the record
+	if _, err := io.ReadFull(rd, p[2:]); err != nil {
+		return err
+	}
+
+	return codec.Unmarshal(p, d)
 }
 
+// EncodeDir writes the directory to wr.
+func EncodeDir(codec Codec, wr io.Writer, d *Dir) error {
+	p, err := codec.Marshal(d)
+	if err != nil {
+		return err
+	}
+
+	_, err = wr.Write(p)
+	return err
+}
+
 type encoder struct {
 	wr io.Writer
 }
@@ -50,8 +88,8 @@ type encoder struct {
 func (e *encoder) encode(vs ...interface{}) error {
 	for _, v := range vs {
 		switch v := v.(type) {
-		case uint8, uint16, uint32, uint64, FcallType, Tag, QType, Fid,
-			*uint8, *uint16, *uint32, *uint64, *FcallType, *Tag, *QType, *Fid:
+		case uint8, uint16, uint32, uint64, FcallType, Tag, QType, Fid, Flag,
+			*uint8, *uint16, *uint32, *uint64, *FcallType, *Tag, *QType, *Fid, *Flag:
 			if err := binary.Write(e.wr, binary.LittleEndian, v); err != nil {
 				return err
 			}
@@ -188,7 +226,7 @@ type decoder struct {
 func (d *decoder) decode(vs ...interface{}) error {
 	for _, v := range vs {
 		switch v := v.(type) {
-		case *uint8, *uint16, *uint32, *uint64, *FcallType, *Tag, *QType, *Fid:
+		case *uint8, *uint16, *uint32, *uint64, *FcallType, *Tag, *QType, *Fid, *Flag:
 			if err := binary.Read(d.rd, binary.LittleEndian, v); err != nil {
 				return err
 			}
@@ -352,8 +390,8 @@ func size9p(vs ...interface{}) uint32 {
 		}
 
 		switch v := v.(type) {
-		case uint8, uint16, uint32, uint64, FcallType, Tag, QType, Fid,
-			*uint8, *uint16, *uint32, *uint64, *FcallType, *Tag, *QType, *Fid:
+		case uint8, uint16, uint32, uint64, FcallType, Tag, QType, Fid, Flag,
+			*uint8, *uint16, *uint32, *uint64, *FcallType, *Tag, *QType, *Fid, *Flag:
 			s += uint32(binary.Size(v))
 		case []byte:
 			s += uint32(binary.Size(uint32(0)) + len(v))
blob - 000834bd884dcf2b5745120f03727e76a12f91f3
blob + 07a2464c0eb591b5e43f6f1877c7987e72d339a6
--- errors.go
+++ errors.go
@@ -2,13 +2,8 @@ package p9pnew
 
 import "errors"
 
-// common errors returned by Session interface methods
 var (
-	ErrClosed = errors.New("closed")
-)
-
-// 9p wire errors returned by Session interface methods
-var (
+	// 9p wire errors returned by Session interface methods
 	ErrBadattach    = new9pError("unknown specifier in attach")
 	ErrBadoffset    = new9pError("bad offset")
 	ErrBadcount     = new9pError("bad count")
@@ -30,9 +25,12 @@ var (
 	ErrWalknodir    = new9pError("walk in non-directory")
 
 	// extra errors not part of the normal protocol
-	ErrTimeout    = new9pError("fcall timeout") // returned when timing out on the fcall
-	ErrUnknownTag = new9pError("unknown tag")
-	ErrUnknownMsg = new9pError("unknown message") // returned when encountering unknown message type
+	ErrTimeout       = new9pError("fcall timeout") // returned when timing out on the fcall
+	ErrUnknownTag    = new9pError("unknown tag")
+	ErrUnknownMsg    = new9pError("unknown message")    // returned when encountering unknown message type
+	ErrUnexpectedMsg = new9pError("unexpected message") // returned when an unexpected message is encountered
+	ErrWalkLimit     = new9pError("too many wnames in walk")
+	ErrClosed        = errors.New("closed")
 )
 
 // new9pError returns a new 9p error ready for the wire.
blob - a3c74c87f6bcdac3e0cbeefd612a8da2a49002b6
blob + 259e3bf04f20fa65449c543557ecd1a4ac6d0363
--- messages.go
+++ messages.go
@@ -190,7 +190,7 @@ type MessageRwalk struct {
 
 type MessageTopen struct {
 	Fid  Fid
-	Mode uint8
+	Mode Flag
 }
 
 type MessageRopen struct {
@@ -202,7 +202,7 @@ type MessageTcreate struct {
 	Fid  Fid
 	Name string
 	Perm uint32
-	Mode uint8
+	Mode Flag
 }
 
 type MessageRcreate struct {
blob - 975a0aad7cfa145559b902deb64d7dfc36c64140
blob + 2fac1b1c58190f943b61509cb4cb951170a95b4f
--- session.go
+++ session.go
@@ -25,8 +25,8 @@ type Session interface {
 	Walk(ctx context.Context, fid Fid, newfid Fid, names ...string) ([]Qid, error)
 	Read(ctx context.Context, fid Fid, p []byte, offset int64) (n int, err error)
 	Write(ctx context.Context, fid Fid, p []byte, offset int64) (n int, err error)
-	Open(ctx context.Context, fid Fid, mode uint8) (Qid, uint32, error)
-	Create(ctx context.Context, parent Fid, name string, perm uint32, mode uint32) (Qid, uint32, error)
+	Open(ctx context.Context, fid Fid, mode Flag) (Qid, uint32, error)
+	Create(ctx context.Context, parent Fid, name string, perm uint32, mode Flag) (Qid, uint32, error)
 	Stat(context.Context, Fid) (Dir, error)
 	WStat(context.Context, Fid, Dir) error
 
blob - 3caf076db643d3f72ce17f7e3d2b78ebcb38c588
blob + 9387f48e851e79a80260517317a258511a43253c
--- transport.go
+++ transport.go
@@ -9,11 +9,11 @@ import (
 )
 
 // roundTripper manages the request and response from the client-side. A
-// roundTripper must abide by many of the rules of http.RoundTripper.
+// roundTripper must abide by similar rules to the http.RoundTripper.
 // Typically, the roundTripper will manage tag assignment and message
 // serialization.
 type roundTripper interface {
-	send(ctx context.Context, fc *Fcall) (*Fcall, error)
+	send(ctx context.Context, msg Message) (Message, error)
 }
 
 // transport plays the role of being a client channel manager. It multiplexes
@@ -28,6 +28,8 @@ type transport struct {
 	tags uint16
 }
 
+var _ roundTripper = &transport{}
+
 func newTransport(ctx context.Context, ch *channel) roundTripper {
 	t := &transport{
 		ctx:      ctx,
@@ -57,10 +59,10 @@ func newFcallRequest(ctx context.Context, fcall *Fcall
 	}
 }
 
-func (t *transport) send(ctx context.Context, fcall *Fcall) (*Fcall, error) {
+func (t *transport) send(ctx context.Context, msg Message) (Message, error) {
+	fcall := newFcall(msg)
 	req := newFcallRequest(ctx, fcall)
 
-	log.Println("dispatch", fcall)
 	// dispatch the request.
 	select {
 	case <-t.closed:
@@ -70,7 +72,6 @@ func (t *transport) send(ctx context.Context, fcall *F
 	case t.requests <- req:
 	}
 
-	log.Println("wait", fcall)
 	// wait for the response.
 	select {
 	case <-t.closed:
@@ -80,18 +81,17 @@ func (t *transport) send(ctx context.Context, fcall *F
 	case err := <-req.err:
 		return nil, err
 	case resp := <-req.response:
-		log.Println("resp", resp)
 		if resp.Type == Rerror {
 			// pack the error into something useful
-			respmesg, ok := resp.Message.(*MessageRerror)
+			respmesg, ok := resp.Message.(MessageRerror)
 			if !ok {
 				return nil, fmt.Errorf("invalid error response: %v", resp)
 			}
 
-			return nil, new9pError(respmesg.Ename)
+			return nil, respmesg
 		}
 
-		return resp, nil
+		return resp.Message, nil
 	}
 }
 
@@ -178,14 +178,10 @@ func (t *transport) handle() {
 			// the tag map and dealloc the tag. We may also want to send a
 			// flush for the tag.
 			if err := t.ch.WriteFcall(req.ctx, req.fcall); err != nil {
-				log.Println("error writing fcall", err, req.fcall)
 				delete(outstanding, req.fcall.Tag)
 				req.err <- err
 			}
-
-			log.Println("sent", req.fcall)
 		case b := <-responses:
-			log.Println("recv", b)
 			req, ok := outstanding[b.Tag]
 			if !ok {
 				panic("unknown tag received")
@@ -203,6 +199,12 @@ func (t *transport) handle() {
 	}
 }
 
+func (t *transport) flush(ctx context.Context, tag Tag) error {
+	// TODO(stevvooe): We need to fire and forget flush messages when a call
+	// context gets cancelled.
+	panic("not implemented")
+}
+
 func (t *transport) Close() error {
 	select {
 	case <-t.closed:
blob - 61918a21255f3ab026be2362a5ccebb7a78d1780
blob + ae206a0a4d8cb57bea8614eb2f1beb2b5933d616
--- types.go
+++ types.go
@@ -36,11 +36,14 @@ const (
 	DMEXEC  = 0x1 // mode bit for execute permission
 )
 
+// Flag defines the flag type for use with open and create
+type Flag uint8
+
 const (
-	OREAD  = 0x00 // open for read
-	OWRITE = 0x01 // write
-	ORDWR  = 0x02 // read and write
-	OEXEC  = 0x03 // execute, == read but check execute permission
+	OREAD  Flag = 0x00 // open for read
+	OWRITE      = 0x01 // write
+	ORDWR       = 0x02 // read and write
+	OEXEC       = 0x03 // execute, == read but check execute permission
 
 	// PROPOSAL(stevvooe): Possible protocal extension to allow the create of
 	// symlinks. Initially, the link is created with no value. Read and write
@@ -53,16 +56,17 @@ const (
 	ORCLOSE = 0x40 // or'ed in, remove on close
 )
 
+// QType indicates the type of a resource within the Qid.
 type QType uint8
 
 const (
 	QTDIR    QType = 0x80 // type bit for directories
-	QTAPPEND QType = 0x40 // type bit for append only files
-	QTEXCL   QType = 0x20 // type bit for exclusive use files
-	QTMOUNT  QType = 0x10 // type bit for mounted channel
-	QTAUTH   QType = 0x08 // type bit for authentication file
-	QTTMP    QType = 0x04 // type bit for not-backed-up file
-	QTFILE   QType = 0x00 // plain file
+	QTAPPEND       = 0x40 // type bit for append only files
+	QTEXCL         = 0x20 // type bit for exclusive use files
+	QTMOUNT        = 0x10 // type bit for mounted channel
+	QTAUTH         = 0x08 // type bit for authentication file
+	QTTMP          = 0x04 // type bit for not-backed-up file
+	QTFILE         = 0x00 // plain file
 )
 
 func (qt QType) String() string {
blob - c4c7133658b6c3bd3b428135ca0b61e56b00b033
blob + afcc1d2bf0f3c6eafe7ca67bd79d5221bee7fd82
--- version.go
+++ version.go
@@ -31,7 +31,7 @@ func clientnegotiate(ctx context.Context, ch Channel, 
 	}
 
 	switch v := resp.Message.(type) {
-	case *MessageRversion:
+	case MessageRversion:
 
 		if v.Version != version {
 			// TODO(stevvooe): A stubborn client indeed!
@@ -47,7 +47,7 @@ func clientnegotiate(ctx context.Context, ch Channel, 
 	case error:
 		return "", v
 	default:
-		return "", fmt.Errorf("invalid rpc response for version message: %v", resp)
+		return "", ErrUnexpectedMsg
 	}
 }