package main import ( "context" "sync" "github.com/docker-archive/go-p9p" ) // Session implements the p9p.Session interface. type Session struct { mu sync.Mutex fids map[p9p.Fid]Fid *Root } type Fid struct { File uname string } type File interface { Qid() p9p.Qid Stat() (p9p.Dir, error) Open(mode p9p.Flag) error Remove() error Read(ctx context.Context, p []byte, offset int64) (n int, err error) Write(ctx context.Context, p []byte, offset int64) (n int, err error) } type Dir interface { File Create(ctx context.Context, name string, perm uint32, mode p9p.Flag) (File, error) Walk(ctx context.Context, name string) (File, error) } func NewSession(root *Root) p9p.Session { return &Session{ fids: make(map[p9p.Fid]Fid), Root: root, } } func (s *Session) Auth(ctx context.Context, afid p9p.Fid, uname, aname string) (p9p.Qid, error) { return p9p.Qid{}, p9p.MessageRerror{"authfs: no authentication required"} } func (s *Session) Attach(ctx context.Context, fid, afid p9p.Fid, uname, aname string) (p9p.Qid, error) { s.mu.Lock() defer s.mu.Unlock() if _, exists := s.fids[fid]; exists { return p9p.Qid{}, p9p.ErrDupfid } f := Fid{s.root, uname} s.fids[fid] = f return f.Qid(), nil } func (s *Session) Clunk(ctx context.Context, fid p9p.Fid) error { s.mu.Lock() defer s.mu.Unlock() if _, ok := s.fids[fid]; ok { delete(s.fids, fid) return nil } else { return p9p.ErrUnknownfid } } func (s *Session) Remove(ctx context.Context, fid p9p.Fid) error { s.mu.Lock() defer s.mu.Unlock() f, ok := s.fids[fid] if !ok { return p9p.ErrUnknownfid } delete(s.fids, fid) // clunk return f.Remove() } func (s *Session) Walk(ctx context.Context, fid, newfid p9p.Fid, names ...string) ([]p9p.Qid, error) { s.mu.Lock() defer s.mu.Unlock() f, ok := s.fids[fid] if !ok { return nil, p9p.ErrUnknownfid } if _, exists := s.fids[newfid]; exists { return nil, p9p.ErrDupfid } var ( qids []p9p.Qid err error dot = f ) defer func() { s.fids[newfid] = dot }() for _, name := range names { dir, ok := dot.File.(Dir) if !ok { return qids, p9p.ErrWalknodir } dot.File, err = dir.Walk(ctx, name) if err != nil { return qids, err } qids = append(qids, dot.File.Qid()) } return qids, nil } func (s *Session) Read(ctx context.Context, fid p9p.Fid, p []byte, offset int64) (n int, err error) { s.mu.Lock() defer s.mu.Unlock() if f, ok := s.fids[fid]; ok { return f.File.Read(ctx, p, offset) } else { return 0, p9p.ErrUnknownfid } } func (s *Session) Write(ctx context.Context, fid p9p.Fid, p []byte, offset int64) (n int, err error) { s.mu.Lock() defer s.mu.Unlock() if f, ok := s.fids[fid]; ok { return f.File.Write(ctx, p, offset) } else { return 0, p9p.ErrUnknownfid } } func (s *Session) Open(ctx context.Context, fid p9p.Fid, mode p9p.Flag) (p9p.Qid, uint32, error) { s.mu.Lock() defer s.mu.Unlock() f, ok := s.fids[fid] if !ok { return p9p.Qid{}, 0, p9p.ErrUnknownfid } if err := f.Open(mode); err != nil { return p9p.Qid{}, 0, err } return f.File.Qid(), 0, nil } func (s *Session) Create(ctx context.Context, parent p9p.Fid, name string, perm uint32, mode p9p.Flag) (p9p.Qid, uint32, error) { s.mu.Lock() defer s.mu.Unlock() f, ok := s.fids[parent] if !ok { return p9p.Qid{}, 0, p9p.ErrUnknownfid } dir, ok := f.File.(Dir) if !ok { return p9p.Qid{}, 0, p9p.ErrCreatenondir } child, err := dir.Create(ctx, name, perm, mode) return child.Qid(), 0, err } func (s *Session) Stat(ctx context.Context, fid p9p.Fid) (p9p.Dir, error) { s.mu.Lock() defer s.mu.Unlock() if f, ok := s.fids[fid]; ok { return f.File.Stat() } else { return p9p.Dir{}, p9p.ErrUnknownfid } } func (s *Session) WStat(ctx context.Context, fid p9p.Fid, dir p9p.Dir) error { return p9p.ErrPerm } func (s *Session) Version() (msize int, version string) { return p9p.DefaultMSize, p9p.DefaultVersion }