commit 3b765680088fc16e4fd3dbdcbb33e001cfe8a4db
parent 51a2c7b227325c8674efab941191cf31bd9dcf09
Author: Matsuda Kenji <info@mtkn.jp>
Date: Sat, 29 Jul 2023 18:45:37 +0900
add open messages
Diffstat:
| M | fcall.go | | | 99 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- |
| M | fid.go | | | 21 | +++++++++++++++++---- |
| M | file.go | | | 97 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------- |
| M | parse.go | | | 2 | ++ |
| M | server.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)
}