lib9p

Go 9P library.
Log | Files | Refs

commit 3b765680088fc16e4fd3dbdcbb33e001cfe8a4db
parent 51a2c7b227325c8674efab941191cf31bd9dcf09
Author: Matsuda Kenji <info@mtkn.jp>
Date:   Sat, 29 Jul 2023 18:45:37 +0900

add open messages

Diffstat:
Mfcall.go | 99++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Mfid.go | 21+++++++++++++++++----
Mfile.go | 97++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------
Mparse.go | 2++
Mserver.go | 106++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
5 files changed, 292 insertions(+), 33 deletions(-)

diff --git a/fcall.go b/fcall.go @@ -403,7 +403,7 @@ type RError struct { } func newRError(buf []byte) *RError { panic("not implemented") } -func (msg *RError) Size() uint32 { return uint32(4+1+2+2+len(msg.EName())) } +func (msg *RError) Size() uint32 { return uint32(4 + 1 + 2 + 2 + len(msg.EName())) } func (msg *RError) Type() MsgType { return Rerror } func (msg *RError) Tag() uint16 { return msg.tag } func (msg *RError) EName() string { return msg.ename.Error() } @@ -531,6 +531,9 @@ func (msg *RWalk) conv2M() []byte { } cur += 13 } + if cur != len(buf) { + panic("length of buf and cursor position don't match") + } return buf } func (msg *RWalk) String() string { @@ -541,6 +544,100 @@ func (msg *RWalk) String() string { return s } +type TOpen struct { + size uint32 + tag uint16 + fid uint32 + mode OpenMode +} + +func newTOpen(buf []byte) *TOpen { + cur := 0 + msg := new(TOpen) + msg.size = gbit32(buf[cur : cur+4]) + cur += 4 + cur += 1 // type + msg.tag = gbit16(buf[cur : cur+2]) + cur += 2 + msg.fid = gbit32(buf[cur : cur+4]) + cur += 4 + msg.mode = OpenMode(buf[cur]) + cur += 1 + if cur != len(buf) { + panic("length of buf and cursor position don't match") + } + return msg +} +func (msg *TOpen) Size() uint32 { return msg.size } +func (msg *TOpen) Type() MsgType { return Topen } +func (msg *TOpen) Tag() uint16 { return msg.tag } +func (msg *TOpen) Fid() uint32 { return msg.fid } +func (msg *TOpen) Mode() OpenMode { return msg.mode } +func (msg *TOpen) conv2M() []byte { + cur := 0 + buf := make([]byte, msg.Size()) + pbit32(buf[cur:cur+4], msg.Size()) + cur += 4 + buf[cur] = uint8(Topen) + cur += 1 + pbit16(buf[cur:cur+2], msg.Tag()) + cur += 2 + pbit32(buf[cur:cur+4], msg.Fid()) + cur += 4 + buf[cur] = uint8(msg.Mode()) + cur += 1 + if cur != len(buf) { + panic("length of buf and cursor position don't match") + } + return buf +} + +func (msg *TOpen) String() string { + return fmt.Sprintf("Topen tag %d fid %d mode %#o", + msg.Tag(), msg.Fid(), msg.Mode()) +} + +type ROpen struct { + tag uint16 + qid *Qid + iounit uint32 +} + +func newROpen(buf []byte) *ROpen { panic("not implemented") } +func (msg *ROpen) Size() uint32 { + return uint32(4 + 1 + 2 + 13 + 4) +} +func (msg *ROpen) Type() MsgType { return Ropen } +func (msg *ROpen) Tag() uint16 { return msg.tag } +func (msg *ROpen) Qid() *Qid { return msg.qid } +func (msg *ROpen) IoUnit() uint32 { return msg.iounit } +func (msg *ROpen) conv2M() []byte { + cur := 0 + buf := make([]byte, msg.Size()) + pbit32(buf[cur:cur+4], msg.Size()) + cur += 4 + buf[cur] = uint8(msg.Type()) + cur += 1 + pbit16(buf[cur:cur+2], msg.Tag()) + cur += 2 + for i, bit := range msg.Qid().conv2M() { + buf[cur+i] = bit + } + cur += 13 + pbit32(buf[cur:cur+4], msg.IoUnit()) + cur += 4 + if cur != len(buf) { + panic("length of buf and cursor position don't match") + } + + return buf +} +func (msg *ROpen) String() string { + return fmt.Sprintf("Ropen tag %d qid %v iounit %d", + msg.Tag(), msg.Qid(), msg.IoUnit()) +} + + type TStat struct { size uint32 tag uint16 diff --git a/fid.go b/fid.go @@ -2,6 +2,7 @@ package lib9p import ( "fmt" + "io/fs" ) // QidType represents the type of Qid. the 'QT' prefix may be redundant. @@ -18,20 +19,32 @@ const ( QTFILE = 0x00 /* type bits for plain file */ ) -type OpenMode int32 +type OpenMode int32 // defined by 9P -const ( +const ( // TODO: is the mode should be implemented using interfaces? OREAD OpenMode = 0 OWRITE = 1 ORDWR = 2 OEXEC = 3 OTRUNC = 16 - OCEXEC = 32 ORCLOSE = 64 - ORDIRECT = 128 ) +type ReadFile interface { fs.File } +type WriteFile interface { + fs.File + Write([]byte) (int, error) +} +type ReadWriteFile interface { + ReadFile + WriteFile +} +type ExecuteFile interface { + fs.File + Execute(args... any) (any, error) +} + type Fid struct { Fid uint32 OMode OpenMode /* -1 = not open */ diff --git a/file.go b/file.go @@ -13,8 +13,39 @@ const ( DMTMP = 0x04000000 ) +const ( + AEXEC fs.FileMode = 1 << iota + AWRITE + AREAD +) + +func hasPerm(f *File, uid string, p fs.FileMode) (bool, error) { + fi, err := f.Stat() + if err != nil { + return false, fmt.Errorf("stat: %v", err) + } + st := (*stat)(fi) + fp := fi.Mode().Perm() + m := fp & 7 // other + if (p & m) == p { + return true, nil + } + if st.uid == uid { + m |= (fp >> 6) & 7 + if (p & m) == p { + return true, nil + } + } + if st.gid == uid { + m |= (fp >> 3) & 7 + if (p & m) == p { + return true, nil + } + } + return false, nil +} + type stat struct { - // size uint16 t uint16 dev uint32 qid *Qid @@ -38,41 +69,57 @@ func (s *stat) size() uint16 { } func (s *stat) conv2M() []byte { + cur := 0 size := s.size() msg := make([]byte, 2+size) - pbit16(msg[:2], size) - pbit16(msg[2:4], s.t) - pbit32(msg[4:8], s.dev) - msg[8] = uint8(s.qid.Type()) - pbit32(msg[9:13], s.qid.Vers()) - pbit64(msg[13:21], s.qid.Path()) - pbit32(msg[21:25], uint32(s.mode)) - pbit32(msg[25:29], uint32(s.aTime.Unix())) - pbit32(msg[29:33], uint32(s.mTime.Unix())) - pbit64(msg[33:41], uint64(s.length)) - pbit16(msg[41:43], uint16(len(s.name))) + pbit16(msg[cur:cur+2], size) + cur += 2 + pbit16(msg[cur:cur+2], s.t) + cur += 2 + pbit32(msg[cur:cur+4], s.dev) + cur += 4 + msg[cur] = uint8(s.qid.Type()) + cur += 1 + pbit32(msg[cur:cur+4], s.qid.Vers()) + cur += 4 + pbit64(msg[cur:cur+8], s.qid.Path()) + cur += 8 + pbit32(msg[cur:cur+4], uint32(s.mode)) + cur += 4 + pbit32(msg[cur:cur+4], uint32(s.aTime.Unix())) + cur += 4 + pbit32(msg[cur:cur+4], uint32(s.mTime.Unix())) + cur += 4 + pbit64(msg[cur:cur+8], uint64(s.length)) + cur += 8 + pbit16(msg[cur:cur+2], uint16(len(s.name))) + cur += 2 for i := 0; i < len(s.name); i++ { - msg[43+i] = s.name[i] + msg[cur+i] = s.name[i] } - uidOffset := 43 + len(s.name) - pbit16(msg[uidOffset:uidOffset+2], uint16(len(s.uid))) + cur += len(s.name) + pbit16(msg[cur:cur+2], uint16(len(s.uid))) + cur += 2 for i := 0; i < len(s.uid); i++ { - msg[uidOffset+2+i] = s.uid[i] + msg[cur+i] = s.uid[i] } - gidOffset := uidOffset + 2 + len(s.uid) - pbit16(msg[gidOffset:gidOffset+2], uint16(len(s.gid))) + cur += len(s.uid) + pbit16(msg[cur:cur+2], uint16(len(s.gid))) + cur += 2 for i := 0; i < len(s.gid); i++ { - msg[gidOffset+2+i] = s.gid[i] + msg[cur+i] = s.gid[i] } - muidOffset := gidOffset + 2 + len(s.gid) - pbit16(msg[muidOffset:muidOffset+2], uint16(len(s.muid))) + cur += len(s.gid) + pbit16(msg[cur:cur+2], uint16(len(s.muid))) + cur += 2 for i := 0; i < len(s.muid); i++ { - msg[muidOffset+2+i] = s.muid[i] + msg[cur+i] = s.muid[i] } + cur += len(s.muid) - if len(msg) != muidOffset+2+len(s.muid) { - panic(fmt.Errorf("stat offset %d and msg length %d not match", - muidOffset+2+len(s.muid), len(msg))) + if len(msg) != cur { + panic(fmt.Errorf("cursor position %d and msg length %d don't match", + cur, len(msg))) } return msg } diff --git a/parse.go b/parse.go @@ -52,6 +52,8 @@ func unmarshal(buf []byte) (Msg, error) { return newTWalk(buf), nil case Rwalk: return newRWalk(buf), nil + case Topen: + return newTOpen(buf), nil case Tstat: return newTStat(buf), nil case Rstat: diff --git a/server.go b/server.go @@ -153,7 +153,39 @@ func sAttach(s *Server, r *Req) { func rAttach(r *Req, err error) {} func sWalk(s *Server, r *Req) { - // TODO: wip + ifcall, ok := r.ifcall.(*TWalk) + if !ok { + panic("not TWalk") + } + oldFid, ok := s.fPool.lookupFid(ifcall.Fid()) + if !ok { + respond(r, fmt.Errorf("unknown fid")) + return + } + _, ok = s.fPool.lookupFid(ifcall.NewFid()) + if ok { + respond(r, fmt.Errorf("duplicate fid")) + return + } + newFid, err := s.fPool.allocFid(ifcall.NewFid()) + if err != nil { + log.Printf("allocFid: %v", err) + respond(r, fmt.Errorf("internal error")) + return + } + + for i := 0; i < int(ifcall.NWName()); i++ { + // s.FS.walk() TODO: implement + } + newFid.File, err = s.FS.Open(".") + if err != nil { + log.Printf("open root dir: %v", err) + respond(r, fmt.Errorf("internal error")) + return + } + newFid.Uid = oldFid.Uid + newFid.Qid = newFid.File.qid + ofcall := &RWalk{ tag: r.ifcall.Tag(), } @@ -163,12 +195,76 @@ func sWalk(s *Server, r *Req) { func rWalk(r *Req, err error) {} -func sOpen(s *Server, r *Req) {} +func sOpen(s *Server, r *Req) { + ifcall, ok := r.ifcall.(*TOpen) + if !ok { + panic("not TOpen") + } + fidNum := ifcall.Fid() + fidStruct, ok := s.fPool.lookupFid(fidNum) + if !ok { + respond(r, fmt.Errorf("unknown fid")) + return + } + if fidStruct.OMode != -1 { + respond(r, fmt.Errorf("already open")) + return + } + // TODO: this if statement is from plan9's lib9p. + // I don't understand this. + if fidStruct.Qid.t == QTDIR && ifcall.Mode() & ^ORCLOSE != OREAD { + respond(r, fmt.Errorf("is a directory")) + return + } + var p fs.FileMode + switch ifcall.Mode() { + default: + respond(r, fmt.Errorf("invalid mode")) + return + case OREAD: + p = AREAD + case OWRITE: + p = AWRITE + case ORDWR: + p = AREAD | AWRITE + case OEXEC: + p = AEXEC + } + if ifcall.Mode() & OTRUNC != 0 { + p |= AWRITE + } + if fidStruct.Qid.t & QTDIR != 0 && p != AREAD { + respond(r, fmt.Errorf("permission denied")) + return + } + + ok, err := hasPerm(fidStruct.File, fidStruct.Uid, p) + if err != nil { + log.Printf("hasPerm: %v", err) + respond(r, fmt.Errorf("internal error")) + return + } + if !ok { + respond(r, fmt.Errorf("permission denied")) + return + } + + if ifcall.Mode() & ORCLOSE != 0 { + panic("ORCLOSE not implemented") + } + + fidStruct.OMode = ifcall.Mode() + r.ofcall = &ROpen{ + qid: fidStruct.Qid, + iounit: s.MSize - 23, + } + respond(r, nil) +} func rOpen(r *Req, err error) {} func rError(r *Req, err error) { ofcall := &RError{ - tag: r.ifcall.Tag(), + tag: r.ifcall.Tag(), ename: err, } r.ofcall = ofcall @@ -229,6 +325,8 @@ func (s *Server) Serve() { sAttach(s, r) case *TWalk: sWalk(s, r) + case *TOpen: + sOpen(s, r) case *TStat: sStat(s, r) } @@ -260,6 +358,8 @@ func respond(r *Req, err error) { rAttach(r, err) case *RWalk: rWalk(r, err) + case *ROpen: + rOpen(r, err) case *RStat: rStat(r, err) }