lib9p

Go 9P library.
Log | Files | Refs

commit 9eb89f2b634166824f52ccdd00f4b51c79792789
parent 10d2025ddb638345e7f6cbff1b6c783c2a7ff088
Author: Matsuda Kenji <info@mtkn.jp>
Date:   Mon, 11 Sep 2023 12:42:54 +0900

implementing wstat (wip)

Diffstat:
Mdiskfs/file.go | 5+++++
Mfcall.go | 5+++--
Mfile.go | 11+++++++++++
Mserver.go | 88+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mstat.go | 1+
5 files changed, 108 insertions(+), 2 deletions(-)

diff --git a/diskfs/file.go b/diskfs/file.go @@ -164,3 +164,7 @@ func (f *File) ReadDir(n int) ([]*lib9p.DirEntry, error) { f.diroff += n return childEntry[:n], nil } + +func (f *File) WStat(s *lib9p.Stat) error { + return fmt.Errorf("unimplemented") +} +\ No newline at end of file diff --git a/fcall.go b/fcall.go @@ -1231,7 +1231,7 @@ func (msg *RStat) marshal() []byte { } func (msg *RStat) String() string { - return fmt.Sprintf("Rstat tag %d Stat %s", msg.Tag(), msg.stat) + return fmt.Sprintf("Rstat tag %d stat %s", msg.Tag(), msg.stat) } type TWStat struct{ @@ -1253,6 +1253,7 @@ func (msg *TWStat) Size() uint32 { func (msg *TWStat) Type() MsgType { return Twstat } func (msg *TWStat) Tag() uint16 { return msg.tag } func (msg *TWStat) Fid() uint32 { return msg.fid } +func (msg *TWStat) Stat() *Stat { return msg.stat } func (msg *TWStat) marshal() []byte { buf := make([]byte, msg.Size()) pbit32(buf[0:4], msg.Size()) @@ -1269,7 +1270,7 @@ func (msg *TWStat) marshal() []byte { } func (msg *TWStat) String() string { - return fmt.Sprintf("Twstat tag %d fid %d Stat %s", msg.Tag(), msg.Fid(), msg.stat) + return fmt.Sprintf("Twstat tag %d fid %d stat %s", msg.Tag(), msg.Fid(), msg.stat) } diff --git a/file.go b/file.go @@ -3,6 +3,7 @@ package lib9p import "fmt" type File interface { + Parent() (File, error) Child() ([]File, error) // Children Stat() (*FileInfo, error) @@ -10,6 +11,16 @@ type File interface { Read(b []byte) (int, error) } +type WriterStatFile interface { + File + + // WStat set file Stat to stat. + // After successful call, the file's Stat() method should return + // the same Stat as stat. + // If there is an error, file's status must remain the same as before. + WStat(stat *Stat) error +} + // Walkfile walks file tree starting at f, following path specified by name. // It returns the destination File and nil, or nil with non-nil error. func walkfile(f File, name string) (File, error) { diff --git a/server.go b/server.go @@ -447,6 +447,90 @@ func sStat(s *Server, r *Req) { func rStat(r *Req, err error) {} +func sWStat(s *Server, r *Req) { + ifcall := r.ifcall.(*TWStat) + fidNum := ifcall.Fid() + fidStruct, ok := s.fPool.lookup(fidNum) + if !ok { + respond(r, fmt.Errorf("unknown fid %d", fidNum)) + return + } + + wsfile, ok := fidStruct.File.(WriterStatFile) + if !ok { + respond(r, fmt.Errorf("operation not supported")) + return + } + + wstat := ifcall.Stat() + fi, err := fidStruct.File.Stat() + if err != nil { + respond(r, fmt.Errorf("stat: %v", err)) + return + } + newStat := fi.Sys().(*Stat) + + if wstat.Type != ^uint16(0) || wstat.Dev != ^uint32(0) || + wstat.Qid.Type != QidType(^uint8(0)) || wstat.Qid.Vers != ^uint32(0) || + wstat.Qid.Path != ^uint64(0) || wstat.Atime != ^uint32(0) || + wstat.Uid != "" || wstat.Muid != "" { + respond(r, fmt.Errorf("operation not permitted")) + return + } + + if wstat.Name != "" { + parent, err := fidStruct.File.Parent() + if err != nil { + respond(r, fmt.Errorf("get parent: %v", err)) + return + } + if ok, err := hasPerm(parent, fidStruct.Uid, AWRITE); !ok || err != nil { + if err != nil { + log.Printf("hasPerm: %v", err) + } + respond(r, fmt.Errorf("permission denied")) + return + } + children, err := parent.Child() + if err != nil { + respond(r, fmt.Errorf("get children: %v", err)) + return + } + for _, f := range children { + s, err := f.Stat() + if err != nil { + respond(r, fmt.Errorf("stat: %v", err)) + return + } + if s.Name() == wstat.Name { + respond(r, fmt.Errorf("file already exists")) + return + } + } + newStat.Name = wstat.Name + } + + // TODO: WIP + if wstat.Length != ^int64(0) { log.Printf("length: %T, %d", wstat.Length, wstat.Length) } + if wstat.Mode != FileMode(^uint32(0)) {} + if wstat.Mtime != ^uint32(0) {} + if wstat.Gid != "" {} + + err = wsfile.WStat(newStat) + if err != nil { + respond(r, fmt.Errorf("wstat: %v", err)) + return + } + + ofcall := new(RWStat) + ofcall.tag = ifcall.Tag() + r.ofcall = ofcall + + respond(r, nil) +} + +func rWStat(r *Req, err error) {} + func (s *Server) Serve() { for { r, err := s.getReq() @@ -483,6 +567,8 @@ func (s *Server) Serve() { sClunk(s, r) case *TStat: sStat(s, r) + case *TWStat: + sWStat(s, r) } } } @@ -522,6 +608,8 @@ func respond(r *Req, err error) { rClunk(r, err) case *RStat: rStat(r, err) + case *RWStat: + rWStat(r, err) } if chatty9P { fmt.Fprintf(os.Stderr, "--> %s\n", r.ofcall) diff --git a/stat.go b/stat.go @@ -51,6 +51,7 @@ type Stat struct { Mode FileMode Atime uint32 Mtime uint32 + //TODO: In 9P protocol Length is unsigned integer. Length int64 Name string Uid string