commit - e9f5e41480fec822e81050643644fd58a98ac861
commit + d6198009fad10d2c6e0819feb0fa20608628dbcf
blob - f1889d8ce531e8e636d504b713ea89ca3146a979
blob + a984f15e43f90d71c7c82e281df8cf2cbeb640ee
--- encoding.go
+++ encoding.go
"encoding/binary"
"fmt"
"io"
+ "log"
"reflect"
+ "time"
)
// NOTE(stevvooe): This file covers 9p encoding and decoding (despite just
if err := e.encode(elements...); err != nil {
return err
}
+ case *[]byte:
+ if err := e.encode(uint16(len(*v))); err != nil {
+ return err
+ }
+
+ if err := e.encode(*v); err != nil {
+ return err
+ }
case *string:
if err := e.encode(*v); err != nil {
return err
if err != nil {
return err
}
- case Message:
- // walk the fields of the message to get the total size. we just
- // use the field order from the message struct. We may add tag
- // ignoring if needed.
+ case Message, *Qid, *Dir:
elements, err := fields9p(v)
if err != nil {
return err
if err := e.encode(elements...); err != nil {
return err
}
+ case *[]Qid:
+ if err := e.encode(*v); err != nil {
+ return err
+ }
+ case []Qid:
+ if err := e.encode(uint16(len(v))); err != nil {
+ return err
+ }
+
+ elements := make([]interface{}, len(v))
+ for i := range v {
+ elements[i] = &v[i]
+ }
+
+ if err := e.encode(elements...); err != nil {
+ return err
+ }
+ case time.Time:
+ if err := e.encode(uint32(v.Unix())); err != nil {
+ return err
+ }
+ case *time.Time:
+ if err := e.encode(*v); err != nil {
+ return err
+ }
case Fcall:
if err := e.encode(&v); err != nil {
return err
// read9p extracts values from rd and unmarshals them to the targets of vs.
func (d *decoder) decode(vs ...interface{}) error {
for _, v := range vs {
+ before := fmt.Sprintf("%#v", v)
switch v := v.(type) {
case *string:
var ll uint16
}
if err := d.decode(elements...); err != nil {
+ return err
+ }
+ case *[]byte:
+ var ll uint16
+
+ if err := d.decode(&ll); err != nil {
+ return err
+ }
+
+ *v = make([]byte, int(ll))
+
+ if err := binary.Read(d.rd, binary.LittleEndian, v); err != nil {
return err
}
case *Fcall:
if err := d.decode(v.Message); err != nil {
return err
}
- case Message:
+ case *[]Qid:
+ var ll uint16
+
+ if err := d.decode(&ll); err != nil {
+ return err
+ }
+
+ elements := make([]interface{}, int(ll))
+ *v = make([]Qid, int(ll))
+ for i := range elements {
+ elements[i] = &(*v)[i]
+ }
+
+ if err := d.decode(elements...); err != nil {
+ return err
+ }
+ case *time.Time:
+ var epoch uint32
+ if err := d.decode(&epoch); err != nil {
+ return err
+ }
+
+ *v = time.Unix(int64(epoch), 0).UTC()
+ case Message, *Qid, *Dir:
elements, err := fields9p(v)
if err != nil {
return err
return err
}
}
+ log.Printf("Decode: %v -> %#v", before, v)
}
return nil
}
s += size9p(elements...)
- case Message:
+ case *[]byte:
+ s += size9p(uint16(0), *v)
+ case *[]Qid:
+ s += size9p(*v)
+ case []Qid:
+ s += size9p(uint16(0))
+ elements := make([]interface{}, len(v))
+ for i := range elements {
+ elements[i] = &v[i]
+ }
+ s += size9p(elements...)
+ case time.Time, *time.Time:
+ s += size9p(uint32(0))
+ case Message, *Qid, *Dir:
// walk the fields of the message to get the total size. we just
// use the field order from the message struct. We may add tag
// ignoring if needed.
rv := reflect.Indirect(reflect.ValueOf(v))
if rv.Kind() != reflect.Struct {
+ panic("asdf")
return nil, fmt.Errorf("cannot extract fields from non-struct: %v", rv)
}
}
if !f.CanSet() {
+ panic("asdf")
return nil, fmt.Errorf("cannot set %v", f)
}
blob - fb38ea4090dfd47997571c4808dcd1bf418408b6
blob + 9a676b70dc7a2076e7b7b306bda3332a1794a936
--- encoding_test.go
+++ encoding_test.go
"bytes"
"reflect"
"testing"
+ "time"
)
func TestEncodeDecode(t *testing.T) {
0x1, 0x0, 0x62, // "b"
0x1, 0x0, 0x63}, // "c"
},
+ {
+ description: "Rwalk call",
+ target: &Fcall{
+ Type: Rwalk,
+ Tag: 5556,
+ Message: &MessageRwalk{
+ Qids: []Qid{
+ Qid{
+ Type: QTDIR,
+ Path: 1111,
+ Version: 11112,
+ },
+ Qid{Type: QTFILE,
+ Version: 1112,
+ Path: 11114},
+ },
+ },
+ },
+ marshaled: []byte{
+ 0x23, 0x0, 0x0, 0x0,
+ 0x6f, 0xb4, 0x15,
+ 0x2, 0x0,
+ 0x80, 0x68, 0x2b, 0x0, 0x0, 0x57, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x58, 0x4, 0x0, 0x0, 0x6a, 0x2b, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+ },
+ {
+ description: "Rread fcall",
+ target: &Fcall{
+ Type: Rread,
+ Tag: 5556,
+ Message: &MessageRread{
+ Data: []byte("a lot of byte data"),
+ },
+ },
+ marshaled: []byte{
+ 0x1b, 0x0, 0x0, 0x0,
+ 0x75, 0xb4, 0x15,
+ 0x12, 0x0,
+ 0x61, 0x20, 0x6c, 0x6f, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x62, 0x79, 0x74, 0x65, 0x20, 0x64, 0x61, 0x74, 0x61},
+ },
+ {
+ description: "",
+ target: &Fcall{
+ Type: Rstat,
+ Tag: 5556,
+ Message: &MessageRstat{
+ Stat: Dir{
+ Type: ^uint16(0),
+ Dev: ^uint32(0),
+ Qid: Qid{
+ Type: QTDIR,
+ Version: ^uint32(0),
+ Path: ^uint64(0),
+ },
+ Mode: DMDIR | DMREAD,
+ AccessTime: time.Date(2006, 01, 02, 03, 04, 05, 0, time.UTC),
+ ModTime: time.Date(2006, 01, 02, 03, 04, 05, 0, time.UTC),
+ Length: ^uint64(0),
+ Name: "somedir",
+ UID: "uid",
+ GID: "gid",
+ MUID: "muid",
+ },
+ },
+ },
+ marshaled: []byte{
+ 0x47, 0x0, 0x0, 0x0,
+ 0x7d, 0xb4, 0x15,
+ 0xff, 0xff, // type
+ 0xff, 0xff, 0xff, 0xff, // dev
+ 0x80, 0xff, 0xff, 0xff, 0xff, // qid.type, qid.version
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // qid.path
+ 0x4, 0x0, 0x0, 0x80, // mode
+ 0x25, 0x98, 0xb8, 0x43, // atime
+ 0x25, 0x98, 0xb8, 0x43, // mtime
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // length
+ 0x7, 0x0, 0x73, 0x6f, 0x6d, 0x65, 0x64, 0x69, 0x72,
+ 0x3, 0x0, 0x75, 0x69, 0x64, // uid
+ 0x3, 0x0, 0x67, 0x69, 0x64, // gid
+ 0x4, 0x0, 0x6d, 0x75, 0x69, 0x64}, // muid
+ },
} {
t.Logf("target under test: %v", testcase.target)
fatalf := func(format string, args ...interface{}) {
t.Fatalf(testcase.description+": "+format, args...)
}
- // check that size9p is working correctly
- if int(size9p(testcase.target)) != len(testcase.marshaled) {
- fatalf("size not correct: %v != %v", int(size9p(testcase.target)), len(testcase.marshaled))
- }
-
t.Logf("expecting message of %v bytes", len(testcase.marshaled))
var b bytes.Buffer
}
if !bytes.Equal(b.Bytes(), testcase.marshaled) {
- fatalf("unexpected bytes for fcall: %#v != %#v", b.Bytes(), testcase.marshaled)
+ fatalf("unexpected bytes for fcall: \n%#v != \n%#v", b.Bytes(), testcase.marshaled)
}
+ // check that size9p is working correctly
+ if int(size9p(testcase.target)) != len(testcase.marshaled) {
+ fatalf("size not correct: %v != %v", int(size9p(testcase.target)), len(testcase.marshaled))
+ }
+
var v interface{}
targetType := reflect.TypeOf(testcase.target)
}
if !reflect.DeepEqual(v, testcase.target) {
- fatalf("not equal: %#v != %#v", v, testcase.target)
+ fatalf("not equal: %v != %v", v, testcase.target)
}
+
t.Logf("%#v", v)
}
blob - 9cb51454c9c0fd95c63d2083b8bbf90ce6cc2db5
blob + 6dfb0d4b0a2d2b11bb9074dafa15a299b06f0777
--- fcall.go
+++ fcall.go
case Rerror:
case Tflush:
- return &MessageFlush{}, nil
+ return &MessageTflush{}, nil
case Rflush:
return nil, nil // No message body for this response.
case Twalk:
case Tread:
case Rread:
-
+ return &MessageRread{}, nil
case Twrite:
case Rwrite:
case Tstat:
case Rstat:
-
+ return &MessageRstat{}, nil
case Twstat:
case Rwstat:
}
func (MessageVersion) message9p() {}
+
func (mv MessageVersion) String() string {
return fmt.Sprintf("msize=%v version=%v", mv.MSize, mv.Version)
}
Qid Qid
}
-type MessageError struct {
+type MessageRerror struct {
Ename string
}
+// MessageTflush handles the content for the Tflush message type.
+type MessageTflush struct {
+ Oldtag Tag
+}
+
+func (MessageTflush) message9p() {}
+
type MessageTattach struct {
Fid Fid
Afid Fid
func (MessageTwalk) message9p() {}
type MessageRwalk struct {
- Qid []Qid
+ Qids []Qid
}
func (MessageRwalk) message9p() {}
Mode uint8
}
+func (MessageTopen) message9p() {}
+
type MessageRopen struct {
Qid Qid
Msize uint32
}
+func (MessageRopen) message9p() {}
+
type MessageTcreate struct {
Fid Fid
Name string
Mode uint8
}
+func (MessageTcreate) message9p() {}
+
+type MessageRcreate struct {
+ Qid Qid
+ IOUnit uint32
+}
+
+func (MessageRcreate) message9p() {}
+
type MessageTread struct {
Fid Fid
Offset uint64
Count uint32
}
+func (MessageTread) message9p() {}
+
type MessageRread struct {
Data []byte
}
-// MessageFlush handles the content for the Tflush message type.
-type MessageFlush struct {
- Oldtag Tag
+func (MessageRread) message9p() {}
+
+type MessageTwrite struct {
+ Fid Fid
+ Offset uint64
+ Data []byte
}
-func (MessageFlush) message9p() {}
+func (MessageTwrite) message9p() {}
+
+type MessageRwrite struct {
+ Count uint32
+}
+
+func (MessageRwrite) message9p() {}
+
+type MessageTclunk struct {
+ Fid Fid
+}
+
+type MessageTremove struct {
+ Fid Fid
+}
+
+type MessageTstat struct {
+ Fid Fid
+}
+
+type MessageRstat struct {
+ Stat Dir
+}
+
+func (MessageRstat) message9p() {}
+
+type MessageTwstat struct {
+ Fid Fid
+ Stat Dir
+}
blob - 524e7b4ec21e4fcfb010d59e7ba62e07fb3a78f1
blob + a721ab636b2e82ae5b06a92c70f3c5b17e96a47e
--- types.go
+++ types.go
)
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 files */
- DMREAD = 0x4 /* mode bit for read permission */
- DMWRITE = 0x2 /* mode bit for write permission */
- DMEXEC = 0x1 /* mode bit for execute permission */
+ 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 files
+ DMREAD = 0x4 // mode bit for read permission
+ DMWRITE = 0x2 // mode bit for write permission
+ DMEXEC = 0x1 // mode bit for execute permission
)
const (
- OREAD = 0 /* open for read */
- OWRITE = 1 /* write */
- ORDWR = 2 /* read and write */
- OEXEC = 3 /* execute, == read but check execute permission */
- OTRUNC = 16 /* or'ed in (except for exec), truncate file first */
- OCEXEC = 32 /* or'ed in, close on exec */
- ORCLOSE = 64 /* or'ed in, remove on close */
- OEXCL = 0x1000 /* or'ed in, exclusive use (create only) */
+ OREAD = 0 // open for read
+ OWRITE = 1 // write
+ ORDWR = 2 // read and write
+ OEXEC = 3 // execute, == read but check execute permission
+ OTRUNC = 16 // or'ed in (except for exec), truncate file first
+ OCEXEC = 32 // or'ed in, close on exec
+ ORCLOSE = 64 // or'ed in, remove on close
+ OEXCL = 0x1000 // or'ed in, exclusive use (create only)
)
type QType uint8
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 */
+ QTFILE QType = 0x00 // plain file
)
type Fid uint32
}
type Dir struct {
- Type uint16
- Dev uint32
- Qid Qid
- Mode uint32
- Length uint64
- Name string
+ Type uint16
+ Dev uint32
+ Qid Qid
+ Mode uint32
+ AccessTime time.Time
+ ModTime time.Time
+ Length uint64
+ Name string
+ UID string
+ GID string
+ MUID string
- AccessTime time.Time // TODO(stevvooe): Need special serialization type.
- ModificationTime time.Time
-
- /* Not really used for our implementation */
- UID string
- GID string
- MUID string
-
// TODO(stevvooe): 9p2000.u/L should go here.
}