commit - ba353f358bc496e69ab151ff377a4d041f306ad5
commit + deb98ab43c4d46aadb8214e05c8178b2eceb1fae
blob - 452188d9186f024575da5ff0019c5a295a5dfa61
blob + 95fb256b47676759a4ae4face3f337bfea447540
--- client.go
+++ client.go
package p9pnew
import (
- "fmt"
- "log"
-
"golang.org/x/net/context"
"net"
}
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
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)
}
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
}
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
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)
}
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)
}
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)
}
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
}
})
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)
}
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)
}
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)
}
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)
}
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)
}
return nil, err
}
- resp = newFcall(&MessageRstat{
+ resp = newFcall(MessageRstat{
Stat: dir,
})
case Twstat:
blob - bfce22d1890ddf9139f07f5af8b9f279299a429c
blob + 326a180bfe49d872d8cbfae19d0b2e3f5e0d92fb
--- encoding.go
+++ encoding.go
// 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 {
}
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
}
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
}
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
}
}
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
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")
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
type MessageTopen struct {
Fid Fid
- Mode uint8
+ Mode Flag
}
type MessageRopen struct {
Fid Fid
Name string
Perm uint32
- Mode uint8
+ Mode Flag
}
type MessageRcreate struct {
blob - 975a0aad7cfa145559b902deb64d7dfc36c64140
blob + 2fac1b1c58190f943b61509cb4cb951170a95b4f
--- session.go
+++ session.go
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
)
// 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
tags uint16
}
+var _ roundTripper = &transport{}
+
func newTransport(ctx context.Context, ch *channel) roundTripper {
t := &transport{
ctx: ctx,
}
}
-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:
case t.requests <- req:
}
- log.Println("wait", fcall)
// wait for the response.
select {
case <-t.closed:
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
}
}
// 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")
}
}
+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
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
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
}
switch v := resp.Message.(type) {
- case *MessageRversion:
+ case MessageRversion:
if v.Version != version {
// TODO(stevvooe): A stubborn client indeed!
case error:
return "", v
default:
- return "", fmt.Errorf("invalid rpc response for version message: %v", resp)
+ return "", ErrUnexpectedMsg
}
}