lib9p

Go 9P library.
Log | Files | Refs | LICENSE

commit 7f859b6f3974be1a81eb24098f1d6dfc00658690
parent 0d0f72add8ba7cde8e315b016bcb0c7840f7ebec
Author: Matsuda Kenji <info@mtkn.jp>
Date:   Fri, 12 Jan 2024 15:31:16 +0900

add fid.qidpath

Diffstat:
Mfid.go | 1+
Mserver.go | 88++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------------
Mserver_test.go | 6++++++
3 files changed, 65 insertions(+), 30 deletions(-)

diff --git a/fid.go b/fid.go @@ -71,6 +71,7 @@ type fid struct { fid uint32 omode OpenMode /* -1 = not open */ path string // The path from the root of the FS. + qidpath uint64 fs FS // The associated FS. file File // The associated File. uid string // The user id derived from the attach message. diff --git a/server.go b/server.go @@ -280,6 +280,10 @@ func sAuth(ctx context.Context, c *conn, rc <-chan *request) { setError(r, r.err) } else { c.s.Auth(ctx, r) + // TODO: move this code to c.s.Auth + if rauth, ok := r.ofcall.(*RAuth); ok { + r.afid.qidpath = rauth.Aqid.Path + } } select { case c.respChan <- r: @@ -347,6 +351,7 @@ func sAttach(ctx context.Context, c *conn, rc <-chan *request) { r.err = fmt.Errorf("stat root: %v", err) goto resp } + r.fid.qidpath = fi.Sys().(*Stat).Qid.Path r.ofcall = &RAttach{ Qid: fi.Sys().(*Stat).Qid, } @@ -404,7 +409,6 @@ func sWalk(ctx context.Context, c *conn, rc <-chan *request) { newFid *fid wqids []Qid cwdp string - n int ) ifcall := r.ifcall.(*TWalk) oldFid, ok := c.fPool.lookup(ifcall.Fid) @@ -434,9 +438,9 @@ func sWalk(ctx context.Context, c *conn, rc <-chan *request) { goto resp } } - wqids = make([]Qid, len(ifcall.Wnames)) + wqids = make([]Qid, 0, len(ifcall.Wnames)) cwdp = oldFid.path - for i, name := range ifcall.Wnames { + for _, name := range ifcall.Wnames { cwdp = path.Clean(path.Join(cwdp, name)) if cwdp == ".." { cwdp = "." // parent of the root is itself. @@ -445,14 +449,18 @@ func sWalk(ctx context.Context, c *conn, rc <-chan *request) { if err != nil { break } - wqids[i] = stat.Sys().(*Stat).Qid - n++ + wqids = append(wqids, stat.Sys().(*Stat).Qid) + } + if len(wqids) == 0 { + newFid.qidpath = oldFid.qidpath + } else { + newFid.qidpath = wqids[len(wqids)-1].Path } newFid.path = cwdp newFid.uid = oldFid.uid newFid.fs = oldFid.fs r.ofcall = &RWalk{ - Qids: wqids[:n], + Qids: wqids, } resp: if r.ofcall == nil { @@ -573,34 +581,32 @@ func sOpen(ctx context.Context, c *conn, rc <-chan *request) { resp: if r.err != nil { setError(r, r.err) - select { - case c.respChan <- r: - case <-ctx.Done(): - return - } - continue + goto send } if _, ok := r.fid.file.(*AuthFile); ok { r.fid.omode = ifcall.Mode - select { - case c.respChan <- r: - case <-ctx.Done(): - return - } - continue + goto send } r.fid.file, err = r.fid.fs.OpenFile(r.fid.path, ModeToFlag(ifcall.Mode)) if err != nil { setError(r, err) - select { - case c.respChan <- r: - case <-ctx.Done(): - return - } - continue + goto send + } + fi, err = r.fid.file.Stat() + if err != nil { + r.fid.file.Close() + setError(r, err) + goto send + } + if fi.Sys().(*Stat).Qid.Path != r.fid.qidpath { + log.Println("open:", fi.Sys().(*Stat).Qid.Path, r.fid.qidpath) + r.fid.file.Close() + setError(r, fmt.Errorf("qid path mismatch")) + goto send } // omode should be set after successfully opening it. r.fid.omode = ifcall.Mode + send: select { case c.respChan <- r: case <-ctx.Done(): @@ -676,6 +682,7 @@ func sCreate(ctx context.Context, c *conn, rc <-chan *request) { r.err = fmt.Errorf("stat: %v", err) goto resp } + r.fid.qidpath = fi.Sys().(*Stat).Qid.Path r.ofcall = &RCreate{ Qid: fi.Sys().(*Stat).Qid, Iounit: c.mSize() - IOHDRSZ, @@ -954,7 +961,8 @@ func sRemove(ctx context.Context, c *conn, rc <-chan *request) { } var ( parentPath string - pstat fs.FileInfo + pfi fs.FileInfo + fi fs.FileInfo err error rfs RemoverFS ) @@ -969,21 +977,33 @@ func sRemove(ctx context.Context, c *conn, rc <-chan *request) { r.fid.file.Close() } parentPath = path.Dir(r.fid.path) - pstat, err = fs.Stat(ExportFS{r.fid.fs}, parentPath) + pfi, err = fs.Stat(ExportFS{r.fid.fs}, parentPath) if err != nil { r.err = fmt.Errorf("stat parent: %v", err) goto resp } - if !hasPerm(r.fid.fs, pstat, r.fid.uid, AWRITE) { + if !hasPerm(r.fid.fs, pfi, r.fid.uid, AWRITE) { r.err = ErrPerm goto resp } + // BUG: race. Remove call below uses r.fid.path, so I need to + // check whether the underlying qid is the same. + // But other connection can move the same file and then create + // new one with the same name. + fi, err = fs.Stat(ExportFS{FS: r.fid.fs}, r.fid.path) + if err != nil { + r.err = err + goto resp + } + if r.fid.qidpath != fi.Sys().(*Stat).Qid.Path { + r.err = fmt.Errorf("qid path mismatch") + goto resp + } rfs, ok = r.fid.fs.(RemoverFS) if !ok { r.err = ErrOperation goto resp } - // TODO: this assumes files can be identified by its path. // I think the argument of RemoverFS.Remove should be Qid.Path. if err = rfs.Remove(r.fid.path); err != nil { r.err = fmt.Errorf("remove: %v", err) @@ -1072,7 +1092,6 @@ func sWStat(ctx context.Context, c *conn, rc <-chan *request) { r.err = fmt.Errorf("open: %v", err) goto resp } - defer r.fid.file.Close() } wsfile, ok = r.fid.file.(WriterStatFile) if !ok { @@ -1086,6 +1105,10 @@ func sWStat(ctx context.Context, c *conn, rc <-chan *request) { goto resp } newStat = fi.Sys().(*Stat) + if r.fid.qidpath != newStat.Qid.Path { + r.err = fmt.Errorf("qid mismatch") + goto resp + } if wstat.Type != ^uint16(0) && wstat.Type != newStat.Type || wstat.Dev != ^uint32(0) && wstat.Dev != newStat.Dev || wstat.Qid.Type != QidType(^uint8(0)) && wstat.Qid.Type != newStat.Qid.Type || @@ -1178,9 +1201,14 @@ func sWStat(ctx context.Context, c *conn, rc <-chan *request) { r.err = fmt.Errorf("wstat: %v", err) goto resp } + if path.Base(r.fid.path) != newStat.Name { + r.fid.path = path.Join(path.Dir(r.fid.path), newStat.Name) + } r.ofcall = &RWstat{} - // TODO: update r.fid.path resp: + if r.fid.omode == -1 && r.fid.file != nil { + r.fid.file.Close() + } if r.err != nil { setError(r, r.err) } diff --git a/server_test.go b/server_test.go @@ -432,6 +432,7 @@ func TestSOpen(t *testing.T) { continue } fid.file = f + fid.qidpath = f.stat.Qid.Path tc <- &request{ifcall: test.input} ofcall := (<-rc).ofcall switch test.wantMsg.(type) { @@ -791,6 +792,11 @@ func TestSRemove(t *testing.T) { } defer f.Close() fid.file = f + fi, err := f.Stat() + if err != nil { + t.Fatal(i, err) + } + fid.qidpath = fi.Sys().(*Stat).Qid.Path fid.uid = test.ruid fid.fs = testfs tc <- &request{ifcall: test.ifcall}