From 79f55b0b233258623062db3c916783b4d14bf14a Mon Sep 17 00:00:00 2001 From: Sam Anthony Date: Sat, 7 Mar 2026 18:33:37 -0500 Subject: authfs: implement p9p.Session --- Makefile | 9 ++- archetypes/default.md | 5 -- back/auth/auth.go | 17 ++++ back/cmd/authfs/main.go | 66 ++++++++++++++++ back/cmd/authfs/root.go | 21 +++++ back/cmd/authfs/session.go | 187 ++++++++++++++++++++++++++++++++++++++++++++ cmd/buthd/main.go | 16 ---- front/archetypes/default.md | 5 ++ front/hugo.toml | 3 + go.mod | 7 +- go.sum | 4 + hugo.toml | 3 - 12 files changed, 315 insertions(+), 28 deletions(-) delete mode 100644 archetypes/default.md create mode 100644 back/auth/auth.go create mode 100644 back/cmd/authfs/main.go create mode 100644 back/cmd/authfs/root.go create mode 100644 back/cmd/authfs/session.go delete mode 100644 cmd/buthd/main.go create mode 100644 front/archetypes/default.md create mode 100644 front/hugo.toml create mode 100644 go.sum delete mode 100644 hugo.toml diff --git a/Makefile b/Makefile index 5f7f1bd..7da056a 100644 --- a/Makefile +++ b/Makefile @@ -1,9 +1,12 @@ -all: doc public bin/webshopd +CMD = $(wildcard back/cmd/*) +BIN = $(addprefix bin/, $(notdir ${CMD})) + +all: doc public ${BIN} public: - hugo build + hugo build front -bin/%: cmd/% +${BIN}: bin/%: back/cmd/% go build -o $@ ./$< doc: doc/arch.png diff --git a/archetypes/default.md b/archetypes/default.md deleted file mode 100644 index 25b6752..0000000 --- a/archetypes/default.md +++ /dev/null @@ -1,5 +0,0 @@ -+++ -date = '{{ .Date }}' -draft = true -title = '{{ replace .File.ContentBaseName "-" " " | title }}' -+++ diff --git a/back/auth/auth.go b/back/auth/auth.go new file mode 100644 index 0000000..5685001 --- /dev/null +++ b/back/auth/auth.go @@ -0,0 +1,17 @@ +package auth + +import "fmt" + +const ( + // Maximum number of bytes in a Username. + MaxUsernameSize = 64 +) + +type Username string + +func ValidiateUsername(s string) (Username, error) { + if len(s) > MaxUsernameSize { + return "", fmt.Errorf("username longer than %d bytes: %q", MaxUsernameSize, s) + } + return Username(s), nil +} diff --git a/back/cmd/authfs/main.go b/back/cmd/authfs/main.go new file mode 100644 index 0000000..f8618df --- /dev/null +++ b/back/cmd/authfs/main.go @@ -0,0 +1,66 @@ +/* +This is the authfs daemon. It stores the user database and client +sessions, and it serves a 9P filesystem for managing them. + +User data (usernames and password hashes) are stored on disk. Sessions +are stored in memory. +*/ +package main + +import ( + "context" + "flag" + "log" + "net" + "os" + + "github.com/docker-archive/go-p9p" +) + +const ( + defaultNetwork = "unix" + defaultAddress = "authfs.sock" +) + +var ( + network, address string + + outlog = log.New(os.Stdout, "authfs [info]: ", log.LstdFlags) + errlog = log.New(os.Stderr, "authfs [error]: ", log.LstdFlags|log.Llongfile) +) + +func main() { + flag.StringVar(&network, "net", defaultNetwork, "network transport protocol: {tcp, unix}") + flag.StringVar(&address, "addr", defaultAddress, "IP address or Unix socket path to listen on") + flag.Parse() + + ln, err := net.Listen(network, address) + if err != nil { + errlog.Fatal(err) + } + defer logErr(ln.Close()) + + for { + if conn, err := ln.Accept(); err == nil { + outlog.Println("connected", conn.RemoteAddr()) + go handle(conn) + } else { + errlog.Println(err) + } + } +} + +func handle(conn net.Conn) { + defer logErr(conn.Close()) + ctx := context.Background() + handler := p9p.Dispatch(NewSession()) + if err := p9p.ServeConn(ctx, conn, handler); err != nil { + errlog.Printf("%v: %v\n", conn.RemoteAddr(), err) + } +} + +func logErr(err error) { + if err != nil { + errlog.Println(err) + } +} diff --git a/back/cmd/authfs/root.go b/back/cmd/authfs/root.go new file mode 100644 index 0000000..70ca643 --- /dev/null +++ b/back/cmd/authfs/root.go @@ -0,0 +1,21 @@ +package main + +const rootQid = p9p.Qid{ + Type: QTDIR, + Version: 0x0, + Path: 0x1, +} + +// Root is the root Dir which contains /users and /sessions. +type Root struct { + path string +} + +func (r Root) Qid() p9p.Qid { return rootQid } + +func (r Root) Stat() (p9p.Dir, error) { + return p9p.Dir{ + Qid: rootQid, + Mode: DMDIR | DMREAD, + +} diff --git a/back/cmd/authfs/session.go b/back/cmd/authfs/session.go new file mode 100644 index 0000000..2d4eb98 --- /dev/null +++ b/back/cmd/authfs/session.go @@ -0,0 +1,187 @@ +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 +} diff --git a/cmd/buthd/main.go b/cmd/buthd/main.go deleted file mode 100644 index 60ab749..0000000 --- a/cmd/buthd/main.go +++ /dev/null @@ -1,16 +0,0 @@ -// Buthd is the API server. -package main - -import ( - "fmt" - "html" - "log" - "net/http" -) - -func main() { - http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { - fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path)) - }) - log.Fatal(http.ListenAndServe(":8081", nil)) -} diff --git a/front/archetypes/default.md b/front/archetypes/default.md new file mode 100644 index 0000000..25b6752 --- /dev/null +++ b/front/archetypes/default.md @@ -0,0 +1,5 @@ ++++ +date = '{{ .Date }}' +draft = true +title = '{{ replace .File.ContentBaseName "-" " " | title }}' ++++ diff --git a/front/hugo.toml b/front/hugo.toml new file mode 100644 index 0000000..7e568b8 --- /dev/null +++ b/front/hugo.toml @@ -0,0 +1,3 @@ +baseURL = 'https://example.org/' +languageCode = 'en-us' +title = 'My New Hugo Site' diff --git a/go.mod b/go.mod index 4d48f6f..d3c82e3 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,8 @@ -module git.samanthony.xyz/webshop +module git.samanthony.xyz/buth go 1.25.5 + +require ( + github.com/docker-archive/go-p9p v0.0.0-20191112112554-37d97cf40d03 // indirect + github.com/docker/go-p9p v0.0.0-20191112112554-37d97cf40d03 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..af05c12 --- /dev/null +++ b/go.sum @@ -0,0 +1,4 @@ +github.com/docker-archive/go-p9p v0.0.0-20191112112554-37d97cf40d03 h1:TjD+kWlpl4Am4fG67bL5qbgQ+O0AkmwaGMs3Fch7vdc= +github.com/docker-archive/go-p9p v0.0.0-20191112112554-37d97cf40d03/go.mod h1:3BkbhyQV/9GwNZlaGozfPD5bqbNNndle48j1laRrqmQ= +github.com/docker/go-p9p v0.0.0-20191112112554-37d97cf40d03 h1:HiIKimWyR71ORJgvm/aWL/cqeYMpOy4eObwJogG8FAw= +github.com/docker/go-p9p v0.0.0-20191112112554-37d97cf40d03/go.mod h1:GDue7j/yh3AtNoUK0ihznL9JiZVn92CV9bUrYaD4NOc= diff --git a/hugo.toml b/hugo.toml deleted file mode 100644 index 7e568b8..0000000 --- a/hugo.toml +++ /dev/null @@ -1,3 +0,0 @@ -baseURL = 'https://example.org/' -languageCode = 'en-us' -title = 'My New Hugo Site' -- cgit v1.2.3