package main import ( "bytes" "fmt" "path" "sync" p9 "github.com/Harvey-OS/ninep/protocol" "git.samanthony.xyz/buth/back/auth" ) type File interface { Qid() (p9.QID, error) Stat() (p9.Dir, error) Remove() error Open(p9.Mode) error Read(p9.Offset, p9.Count) ([]byte, error) Write(p9.Offset, []byte) (p9.Count, error) } type Dir interface { File Create(string, p9.Perm, p9.Mode) (File, error) Walk(string) (File, error) } // Authfs is the root p9.NineServer. type Authfs struct { *RootDir *UsersDir *SessionsDir mu sync.Mutex // guards below files map[p9.FID]File } func NewAuthfs(rootPath string) (*Authfs, error) { root := NewRootDir() users, err := NewUsersDir(rootPath, root.Version) if err != nil { return nil, err } return &Authfs{ root, users, NewSessionsDir(root.Version), sync.Mutex{}, make(map[p9.FID]File), }, nil } func (fs *Authfs) Close() { fs.RootDir.Close() fs.UsersDir.Close() fs.SessionsDir.Close() } func (fs *Authfs) Rversion(msize p9.MaxSize, version string) (p9.MaxSize, string, error) { if version != "9P2000" { return 0, "", fmt.Errorf("%v not supported; only 9P2000", version) } return msize, version, nil } func (fs *Authfs) Rattach(fid, afid p9.FID, uname, aname string) (p9.QID, error) { if afid != p9.NOFID { return p9.QID{}, fmt.Errorf("authfs: no authentication required") } var ( f File err error ok bool ) aname = path.Clean(path.Join("/", aname)) dir, file := path.Split(aname) switch dir { case "/": switch file { case "": f = fs.RootDir case "users": f = fs.UsersDir case "sessions": f = fs.SessionsDir default: return p9.QID{}, ErrFileNotExist } case "/users/": f, err = fs.UsersDir.Walk(file) if err != nil { return p9.QID{}, err } case "/sessions/": sessid, err := auth.ParseSessId(file) if err != nil { return p9.QID{}, err } f, ok = fs.SessionsDir.Get(sessid) if !ok { return p9.QID{}, ErrFileNotExist } default: return p9.QID{}, ErrFileNotExist } if f == nil { panic("unreachable") } qid, err := f.Qid() if err != nil { return p9.QID{}, err } if err := fs.attach(fid, f); err != nil { return p9.QID{}, err } return qid, nil } func (fs *Authfs) Rwalk(fid, newfid p9.FID, paths []string) ([]p9.QID, error) { f, ok := fs.getFile(fid) if !ok { return nil, ErrFidNotExist } var qids []p9.QID for i := range paths { dir, ok := f.(Dir) if !ok { return qids, ErrWalkNonDir } var err error f, err = dir.Walk(paths[i]) if err != nil { return qids, err } qid, err := f.Qid() if err != nil { return qids, err } qids = append(qids, qid) } fs.mu.Lock() defer fs.mu.Unlock() if newfid != fid { if _, ok := fs.files[newfid]; ok { return nil, ErrNewfidExist } } fs.files[newfid] = f return qids, nil } func (fs *Authfs) Ropen(fid p9.FID, mode p9.Mode) (p9.QID, p9.MaxSize, error) { f, ok := fs.getFile(fid) if !ok { return p9.QID{}, 0, ErrFidNotExist } if err := f.Open(mode); err != nil { return p9.QID{}, 0, err } qid, err := f.Qid() return qid, 0, err } func (fs *Authfs) Rcreate(fid p9.FID, name string, perm p9.Perm, mode p9.Mode) (p9.QID, p9.MaxSize, error) { f, ok := fs.getFile(fid) if !ok { return p9.QID{}, 0, ErrFidNotExist } dir, ok := f.(Dir) if !ok { return p9.QID{}, 0, ErrCreateNonDir } child, err := dir.Create(name, perm, mode) if err != nil { return p9.QID{}, 0, err } qid, err := child.Qid() return qid, 0, err } func (fs *Authfs) Rstat(fid p9.FID) ([]byte, error) { f, ok := fs.getFile(fid) if !ok { return nil, ErrFidNotExist } info, err := f.Stat() if err != nil { return nil, err } var buf bytes.Buffer p9.Marshaldir(&buf, info) return buf.Bytes(), nil } func (fs *Authfs) Rwstat(fid p9.FID, b []byte) error { return ErrPerm } func (fs *Authfs) Rclunk(fid p9.FID) error { _, err := fs.clunk(fid) return err } func (fs *Authfs) Rremove(fid p9.FID) error { f, err := fs.clunk(fid) if err != nil { return err } return f.Remove() } func (fs *Authfs) Rread(fid p9.FID, off p9.Offset, n p9.Count) ([]byte, error) { f, ok := fs.getFile(fid) if !ok { return nil, ErrFidNotExist } return f.Read(off, n) } func (fs *Authfs) Rwrite(fid p9.FID, off p9.Offset, b []byte) (p9.Count, error) { f, ok := fs.getFile(fid) if !ok { return nil, ErrFidNotExist } return f.Write(off, b) } func (fs *Authfs) Rflush(o p9.Tag) error { return nil } func (fs *Authfs) attach(fid p9.FID, f File) error { fs.mu.Lock() defer fs.mu.Unlock() if _, exists := fs.files[fid]; exists { return ErrFidExist } fs.files[fid] = f return nil } func (fs *Authfs) getFile(fid p9.FID) (File, bool) { fs.mu.Lock() defer fs.mu.Unlock() f, ok := fs.files[fid] return f, ok } func (fs *Authfs) clunk(fid p9.FID) (File, error) { fs.mu.Lock() defer fs.mu.Unlock() f, ok := fs.files[fid] if !ok { return nil, ErrFidNotExist } delete(fs.files, fid) return f, nil }