summaryrefslogtreecommitdiffstats
path: root/back
diff options
context:
space:
mode:
Diffstat (limited to 'back')
-rw-r--r--back/auth/auth.go17
-rw-r--r--back/cmd/authfs/main.go66
-rw-r--r--back/cmd/authfs/root.go21
-rw-r--r--back/cmd/authfs/session.go187
4 files changed, 291 insertions, 0 deletions
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
+}