commit - 0a9b2988af0043c8cace650ce0a94b3e41f55e21
commit + 74ec7ac9af14822d5ae949cdd3cb58e9ffb6ea76
blob - d6e65d033474011bd20818b6180c6152fe523906
blob + 52786490fc789cd9c9104fda5820ec0ddf135dbe
--- channel.go
+++ channel.go
return fmt.Errorf("message large than buffer:", n)
}
- log.Println("channel: readFcall", fcall)
- return ch.codec.Unmarshal(ch.rdbuf[:n], fcall)
+ if err := ch.codec.Unmarshal(ch.rdbuf[:n], fcall); err != nil {
+ return err
+ }
+ log.Println("channel: recv", fcall)
+ return nil
}
func (ch *channel) writeFcall(ctx context.Context, fcall *Fcall) error {
return ErrClosed
default:
}
- log.Println("channel: writeFcall", fcall)
+ log.Println("channel: send", fcall)
deadline, ok := ctx.Deadline()
if !ok {
}
}
- log.Println("msg", n, msize, mbody, len(p), p)
return n, nil
}
blob - 4a1ab7014946f4804f93da8f94cc92ba0d4d27a4
blob + 9e1f5f447816c2a1ba015bc2513c4dc1638d1d4f
--- client.go
+++ client.go
panic("not implemented")
}
-func (c *client) Stat(context.Context, Fid) (Dir, error) {
- panic("not implemented")
+func (c *client) Stat(ctx context.Context, fid Fid) (Dir, error) {
+ fcall := newFcall(MessageTstat{Fid: fid})
+
+ resp, err := c.transport.send(ctx, fcall)
+ if err != nil {
+ return Dir{}, err
+ }
+
+ respmsg, ok := resp.Message.(*MessageRstat)
+ if !ok {
+ return Dir{}, fmt.Errorf("invalid rpc response for stat message: %v", resp)
+ }
+
+ return respmsg.Stat, nil
}
func (c *client) WStat(context.Context, Fid, Dir) error {
blob - 9c73f8688214ada8d9cd4d8faf0ac0083b926f72
blob + 6b1b96c9ed0775de822f9583ffd5c41b2da5ad13
--- cmd/9pr/main.go
+++ cmd/9pr/main.go
completer := readline.NewPrefixCompleter(
readline.PcItem("ls"),
// readline.PcItem("find"),
- // readline.PcItem("stat"),
+ readline.PcItem("stat"),
readline.PcItem("cat"),
readline.PcItem("cd"),
readline.PcItem("pwd"),
cmd = commander.cmdpwd
case "cat":
cmd = commander.cmdcat
+ case "stat":
+ cmd = commander.cmdstat
default:
cmd = func(ctx context.Context, args ...string) error {
return fmt.Errorf("command not implemented")
}
defer c.session.Clunk(ctx, targetfid)
- qid, iounit, err := c.session.Open(ctx, targetfid, p9pnew.OREAD)
+ _, iounit, err := c.session.Open(ctx, targetfid, p9pnew.OREAD)
if err != nil {
return err
}
c.pwdfid = targetfid
return nil
+}
+
+func (c *fsCommander) cmdstat(ctx context.Context, args ...string) error {
+ ps := []string{c.pwd}
+ if len(args) > 0 {
+ ps = args
+ }
+
+ wr := tabwriter.NewWriter(c.stdout, 0, 8, 8, ' ', 0)
+
+ for _, p := range ps {
+ targetfid := c.nextfid
+ c.nextfid++
+ components := strings.Split(strings.Trim(p, "/"), "/")
+ if _, err := c.session.Walk(ctx, c.rootfid, targetfid, components...); err != nil {
+ return err
+ }
+ defer c.session.Clunk(ctx, targetfid)
+
+ d, err := c.session.Stat(ctx, targetfid)
+ if err != nil {
+ return err
+ }
+
+ fmt.Fprintf(wr, "%v\t%v\t%v\t%s\n", os.FileMode(d.Mode), d.Length, d.ModTime, d.Name)
+ }
+
+ return wr.Flush()
}
func (c *fsCommander) cmdpwd(ctx context.Context, args ...string) error {
blob - 165a918e4c7c269d32ed20dab90b79e57a3aa8e1
blob + 2946cb9286fea0999eccc58cd13bb1147f8ca13d
--- encoding.go
+++ encoding.go
if err != nil {
return err
}
- case Message, *Qid, *Dir:
- // BUG(stevvooe): The encoding for Dir is incorrect. Under certain
- // cases, we need to include size field and in other cases, such
- // as Twstat, we need the size twice. See bugs in
- // http://man.cat-v.org/plan_9/5/stat to make sense of this.
+ case *Dir:
+ // NOTE(stevvooe): See bugs in http://man.cat-v.org/plan_9/5/stat
+ // to make sense of this. The field has been included here but we
+ // need to make sure to double emit it for Rstat.
elements, err := fields9p(v)
if err != nil {
return err
}
+ elements = append([]interface{}{uint16(size9p(elements...))}, elements...)
+
if err := e.encode(elements...); err != nil {
return err
}
+ case Message:
+ elements, err := fields9p(v)
+ if err != nil {
+ return err
+ }
+
+ switch v.(type) {
+ case MessageRstat, *MessageRstat:
+ // encode an size header in front of the dir field
+ elements = append([]interface{}{uint16(size9p(elements...))}, elements...)
+ }
+
+ 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(v.Type, v.Version, v.Path); err != nil {
+ return err
+ }
case *[]Qid:
if err := e.encode(*v); err != nil {
return err
if err := dec.decode(elements...); err != nil {
return err
}
- case Message, *Qid:
+ case Message:
elements, err := fields9p(v)
if err != nil {
return err
+ }
+
+ // special case twstat and rstat for size fields. See bugs in
+ // http://man.cat-v.org/plan_9/5/stat to make sense of this.
+ switch v.(type) {
+ case *MessageRstat, MessageRstat:
+ // decode extra size header for stat structure.
+ var ll uint16
+ if err := d.decode(&ll); err != nil {
+ return err
+ }
}
if err := d.decode(elements...); err != nil {
return err
}
+ case *Qid:
+ if err := d.decode(&v.Type, &v.Version, &v.Path); err != nil {
+ return err
+ }
default:
if err := binary.Read(d.rd, binary.LittleEndian, v); err != nil {
return err
s += size9p(elements...)
case *[]byte:
- s += size9p(uint16(0), *v)
+ s += size9p(uint32(0), *v)
case *[]Qid:
s += size9p(*v)
case []Qid:
s += size9p(elements...)
case time.Time, *time.Time:
s += size9p(uint32(0))
- case Message, *Qid, *Dir:
+ case Qid:
+ s += size9p(v.Type, v.Version, v.Path)
+ case 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.
panic(err)
}
+ s += size9p(elements...) + size9p(uint16(0))
+ case Message:
+
+ // special case twstat and rstat for size fields. See bugs in
+ // http://man.cat-v.org/plan_9/5/stat to make sense of this.
+ switch v.(type) {
+ case *MessageRstat, MessageRstat:
+ s += size9p(uint16(0)) // for extra size field before 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.
+ elements, err := fields9p(v)
+ if err != nil {
+ // BUG(stevvooe): The options here are to return 0, panic or
+ // make this return an error. Ideally, we make it safe to
+ // return 0 and have the rest of the package do the right
+ // thing. For now, we do this, but may want to panic until
+ // things are stable.
+ panic(err)
+ }
+
s += size9p(elements...)
+ case *Qid:
+ s += size9p(*v)
+ case *Dir:
+ s += size9p(*v)
case Fcall:
s += size9p(v.Type, v.Tag, v.Message)
case *Fcall:
for i := 0; i < rv.NumField(); i++ {
f := rv.Field(i)
- s += fmt.Sprintf(" %v=%v", strings.ToLower(rv.Type().Field(i).Name), f)
+ s += fmt.Sprintf(" %v=%v", strings.ToLower(rv.Type().Field(i).Name), f.Interface())
}
return s
blob - 31ff66b1312bc7cae6d6b1ec9f8cc17ece4b5744
blob + 2310a2c5f859b1b9f2486c722d01e703a67ca0a7
--- encoding_test.go
+++ encoding_test.go
},
marshaled: []byte{
0x75, 0xb4, 0x15,
- 0x12, 0x0,
+ 0x12, 0x0, 0x0, 0x0,
0x61, 0x20, 0x6c, 0x6f, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x62, 0x79, 0x74, 0x65, 0x20, 0x64, 0x61, 0x74, 0x61},
},
{
},
marshaled: []byte{
0x7d, 0xb4, 0x15,
- // 0x40, 0x0, // TODO(stevvooe): Include Dir size. Not straightforward.
+ 0x42, 0x0, // TODO(stevvooe): Include Dir size. Not straightforward.
+ 0x40, 0x0, // TODO(stevvooe): Include Dir size. Not straightforward.
0xff, 0xff, // type
0xff, 0xff, 0xff, 0xff, // dev
0x80, 0xff, 0xff, 0xff, 0xff, // qid.type, qid.version
blob - 5689477107327c6ad5e089a80ad44d7096a206a3
blob + 9d1fc1aba43ebbc74dc6a62fe3de23ff731f0761
--- fcall.go
+++ fcall.go
}
func newFcall(msg Message) *Fcall {
+ var tag Tag
+
+ switch msg.Type() {
+ case Tversion, Rversion:
+ tag = NOTAG
+ }
+
return &Fcall{
Type: msg.Type(),
- Tag: NOTAG,
+ Tag: tag,
Message: msg,
}
}
func (fc *Fcall) String() string {
- return fmt.Sprintf("%8d %v(%v) %v", size9p(fc), fc.Type, fc.Tag, string9p(fc.Message))
+ return fmt.Sprintf("%v(%v) %v", fc.Type, fc.Tag, string9p(fc.Message))
}
type Message interface {
blob - f7b340c21172f8a3c4fa08da574a2301fc5d9910
blob + 5bdc01d70056b90a64fa6838de37153d3aec0cdb
--- transport.go
+++ transport.go
log.Println("wait...")
select {
case req := <-t.requests:
+ if req.fcall.Tag == NOTAG {
+ // NOTE(stevvooe): We disallow fcalls with NOTAG to come
+ // through this path since we can't join the tagged response
+ // with the waiting caller. This is typically used for the
+ // Tversion/Rversion round trip to setup a session.
+ //
+ // It may be better to allow these through but block all
+ // requests until a notag message has a response.
- log.Println("send", req.fcall)
- if req.fcall.Type != Tversion {
- tags++
- req.fcall.Tag = tags
- outstanding[req.fcall.Tag] = req
- } else {
- // TODO(stevvooe): Man this protocol is bad. Version messages
- // have no response tag. Effectively, the client can only have
- // one version call outstanding at a time. We have to create
- // an entire special code path to handle it. The client
- // shouldn't proceed until the version reply is completed.
- req.fcall.Tag = NOTAG
+ req.err <- fmt.Errorf("disallowed tag through transport")
+ continue
}
+ // BUG(stevvooe): This is an awful tag allocation procedure.
+ // Replace this with something that let's us allocate tags and
+ // associate data with them, returning to them to a pool when
+ // complete. Such a system would provide a lot of information
+ // about outstanding requests.
+ tags++
+ req.fcall.Tag = tags
+ outstanding[req.fcall.Tag] = req
+
// TODO(stevvooe): Consider the case of requests that never
// receive a response. We need to remove the fcall context from
// the tag map and dealloc the tag. We may also want to send a
blob - 604d967a1ddcf792eb4bba17b3110e4ac5814534
blob + 3eb82c83c1cc430459f85eda47c9ceefcff1f5d6
--- types.go
+++ types.go
}
func (qid Qid) String() string {
- return fmt.Sprintf("Qid(%v, version=%x, path=%x)",
+ return fmt.Sprintf("qid(%v, v=%x, p=%x)",
qid.Type, qid.Version, qid.Path)
}
MUID string
}
+func (d Dir) String() string {
+ return fmt.Sprintf("dir(%v mode=%v atime=%v mtime=%v length=%v name=%v uid=%v gid=%v muid=%v)",
+ d.Qid, d.Mode, d.AccessTime, d.ModTime, d.Length, d.Name, d.UID, d.GID, d.MUID)
+}
+
//
type Tag uint16