Server owns the fid map: validate newfids, GC stale fids
- attach: reject if fc.Fid already in use (was silently overwriting) - walk: reject if fc.Newfid already in use (both clone and full walk) - open: validate fid path exists; delete stale fid if path is gone - stat: validate fid path exists; delete stale fid if path is gone - wstat: after rename, GC all fids on this connection that reference the old path tree (was previously updating paths — now just deletes) The server fully owns the fid map. The client proposes fid numbers, the server validates and accepts or rejects. Stale fids (pointing to renamed/deleted paths) are garbage collected when detected.
This commit is contained in:
parent
8601562962
commit
05e0aba155
|
|
@ -131,7 +131,7 @@ func New(cfg Config) *Server {
|
|||
plan9.Tread: func(cs *connState, fc *plan9.Fcall, ctx context.Context) *plan9.Fcall { return read(cfg.RootTree, cs, fc, ctx) },
|
||||
plan9.Twrite: func(cs *connState, fc *plan9.Fcall, _ context.Context) *plan9.Fcall { return write(cfg.RootTree, cs, fc) },
|
||||
plan9.Tstat: func(cs *connState, fc *plan9.Fcall, _ context.Context) *plan9.Fcall { return stat(cfg.RootTree, cs, fc) },
|
||||
plan9.Twstat: func(cs *connState, fc *plan9.Fcall, _ context.Context) *plan9.Fcall { return s.wstat(cs, fc) },
|
||||
plan9.Twstat: func(cs *connState, fc *plan9.Fcall, _ context.Context) *plan9.Fcall { return wstat(cfg.RootTree, cs, fc) },
|
||||
plan9.Tclunk: func(cs *connState, fc *plan9.Fcall, _ context.Context) *plan9.Fcall { return clunk(cfg.RootTree, cs, fc) },
|
||||
plan9.Tremove: func(cs *connState, fc *plan9.Fcall, _ context.Context) *plan9.Fcall { return remove(cfg.RootTree, cs, fc) },
|
||||
}
|
||||
|
|
@ -335,6 +335,9 @@ func pathBase(path string) string {
|
|||
func (s *Server) attach(cs *connState, fc *plan9.Fcall) *plan9.Fcall {
|
||||
cs.mu.Lock()
|
||||
defer cs.mu.Unlock()
|
||||
if _, exists := cs.fids[fc.Fid]; exists {
|
||||
return errFcall(fc, "fid already in use")
|
||||
}
|
||||
cs.uname = fc.Uname
|
||||
qid := plan9.Qid{Type: QTDir, Path: 0}
|
||||
cs.fids[fc.Fid] = &fid{path: "/", qid: qid}
|
||||
|
|
@ -355,6 +358,9 @@ func walk(root *fs.Tree, cs *connState, fc *plan9.Fcall) *plan9.Fcall {
|
|||
}
|
||||
newf := &fid{path: f.path, qid: f.qid}
|
||||
if len(fc.Wname) == 0 {
|
||||
if _, exists := cs.fids[fc.Newfid]; exists {
|
||||
return errFcall(fc, "fid already in use")
|
||||
}
|
||||
cs.fids[fc.Newfid] = newf
|
||||
return &plan9.Fcall{Type: plan9.Rwalk, Tag: fc.Tag, Wqid: []plan9.Qid{}}
|
||||
}
|
||||
|
|
@ -383,6 +389,9 @@ func walk(root *fs.Tree, cs *connState, fc *plan9.Fcall) *plan9.Fcall {
|
|||
cur = next
|
||||
}
|
||||
if len(wqids) == len(fc.Wname) {
|
||||
if _, exists := cs.fids[fc.Newfid]; exists {
|
||||
return errFcall(fc, "fid already in use")
|
||||
}
|
||||
newf.path = cur
|
||||
newf.qid = wqids[len(wqids)-1]
|
||||
cs.fids[fc.Newfid] = newf
|
||||
|
|
@ -397,6 +406,10 @@ func (s *Server) open(cs *connState, fc *plan9.Fcall) *plan9.Fcall {
|
|||
if !ok {
|
||||
return errFcall(fc, "bad fid")
|
||||
}
|
||||
if _, err := s.rootTree.Stat(f.path[1:]); err != nil {
|
||||
delete(cs.fids, fc.Fid)
|
||||
return errFcall(fc, "file not found")
|
||||
}
|
||||
uid, gid := fileOwnerGroup(s.rootTree, f.path)
|
||||
perm := uint32(makeStat(s.rootTree, f.path).Mode) & 0777
|
||||
|
||||
|
|
@ -581,6 +594,12 @@ func stat(root *fs.Tree, cs *connState, fc *plan9.Fcall) *plan9.Fcall {
|
|||
if !ok {
|
||||
return errFcall(fc, "bad fid")
|
||||
}
|
||||
if _, err := root.Stat(f.path[1:]); err != nil {
|
||||
cs.mu.Lock()
|
||||
delete(cs.fids, fc.Fid)
|
||||
cs.mu.Unlock()
|
||||
return errFcall(fc, "file not found")
|
||||
}
|
||||
dir := makeStat(root, f.path)
|
||||
stat, err := dir.Bytes()
|
||||
if err != nil {
|
||||
|
|
@ -589,7 +608,7 @@ func stat(root *fs.Tree, cs *connState, fc *plan9.Fcall) *plan9.Fcall {
|
|||
return &plan9.Fcall{Type: plan9.Rstat, Tag: fc.Tag, Stat: stat}
|
||||
}
|
||||
|
||||
func (s *Server) wstat(cs *connState, fc *plan9.Fcall) *plan9.Fcall {
|
||||
func wstat(root *fs.Tree, cs *connState, fc *plan9.Fcall) *plan9.Fcall {
|
||||
cs.mu.Lock()
|
||||
f, ok := cs.fids[fc.Fid]
|
||||
cs.mu.Unlock()
|
||||
|
|
@ -604,26 +623,18 @@ func (s *Server) wstat(cs *connState, fc *plan9.Fcall) *plan9.Fcall {
|
|||
if newDir.Name == "" || newDir.Name == oldName {
|
||||
return &plan9.Fcall{Type: plan9.Rwstat, Tag: fc.Tag}
|
||||
}
|
||||
if err := s.rootTree.Rename(f.path[1:], newDir.Name); err != nil {
|
||||
if err := root.Rename(f.path[1:], newDir.Name); err != nil {
|
||||
return errFcall(fc, err.Error())
|
||||
}
|
||||
|
||||
relPath := f.path[1:]
|
||||
oldPath := f.path
|
||||
newPath := "/" + relPath[:len(relPath)-len(oldName)] + newDir.Name
|
||||
s.mu.RLock()
|
||||
conns := s.conns
|
||||
s.mu.RUnlock()
|
||||
for _, c := range conns {
|
||||
c.mu.Lock()
|
||||
for _, fid := range c.fids {
|
||||
if fid.path == oldPath || strings.HasPrefix(fid.path, oldPath+"/") {
|
||||
fid.path = newPath + fid.path[len(oldPath):]
|
||||
fid.qid.Path = qidPath(fid.path)
|
||||
}
|
||||
// GC stale fids on this connection referencing the old path
|
||||
oldPrefix := f.path
|
||||
cs.mu.Lock()
|
||||
for id, fid := range cs.fids {
|
||||
if fid.path == oldPrefix || strings.HasPrefix(fid.path, oldPrefix+"/") {
|
||||
delete(cs.fids, id)
|
||||
}
|
||||
c.mu.Unlock()
|
||||
}
|
||||
cs.mu.Unlock()
|
||||
return &plan9.Fcall{Type: plan9.Rwstat, Tag: fc.Tag}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue