lib9p

Go 9P library.
Log | Files | Refs | LICENSE

commit 71591b179b8665ee7e40531f92b5051c47a78297
parent 9dcc186047d9863578b9ec63ef4751eb15882663
Author: Matsuda Kenji <info@mtkn.jp>
Date:   Fri,  3 Nov 2023 16:25:02 +0900

implement semfs

Diffstat:
Acmd/semfs/fs.go | 160+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acmd/semfs/main.go | 43+++++++++++++++++++++++++++++++++++++++++++
2 files changed, 203 insertions(+), 0 deletions(-)

diff --git a/cmd/semfs/fs.go b/cmd/semfs/fs.go @@ -0,0 +1,160 @@ +package main + +import ( + "context" + "errors" + "io" + "io/fs" + "strings" + "strconv" + "time" + + "git.mtkn.jp/lib9p" + "log" +) + +type semFS struct { + semfiles []*semFile +} + +func (fsys *semFS) OpenFile(name string, omode lib9p.OpenMode, perm fs.FileMode) (lib9p.File, error) { + wnames := strings.Split(name, "/") + if len(wnames) != 1 { + return nil, errors.New("not found") + } + switch wnames[0] { + case "", ".", "/": + return fsys, nil + default: + for _, f := range fsys.semfiles { + if f.name == wnames[0] { + return f, nil + } + } + return nil, errors.New("not found") + } +} + +func (root *semFS) Stat() (*lib9p.FileInfo, error) { + t := uint32(time.Now().Unix()) + return &lib9p.FileInfo{Stat: lib9p.Stat{ + Qid: lib9p.Qid{Type: lib9p.QTDIR, Vers: 0, Path: 0}, + Mode: fs.ModeDir | 0777, + Atime: t, + Mtime: t, + Length: 0, + Name: ".", + }}, nil +} + +func (root *semFS) Close() error { return nil } +func (root *semFS) Create(name string, uid string, mode lib9p.OpenMode, perm lib9p.FileMode) (lib9p.File, error) { + for _, f := range root.semfiles { + if f.name == name { + return nil, errors.New("file already exists") + } + } + ctx, cancel := context.WithCancel(context.Background()) + newfile := &semFile{ + name: name, + sem: 0, + path: uint64(len(root.semfiles)+1), + cancel: cancel, + } + newfile.start(ctx) + root.semfiles = append(root.semfiles, newfile) + return newfile, nil +} +func (root *semFS) Read(p []byte) (n int, err error) { + return 0, errors.New("is a directory") +} + +func (root *semFS) ReadDir(n int) ([]*lib9p.DirEntry, error) { + de := make([]*lib9p.DirEntry, len(root.semfiles)) + for i, f := range root.semfiles { + fi, err := f.Stat() + if err != nil { + return nil, err + } + de[i] = fi + } + return de, nil +} + +type semFile struct { + name string + sem int + path uint64 + offset int + rchan chan<- chan struct{} + wchan chan<- int + queue []chan struct{} + cancel context.CancelFunc +} + +func (f *semFile) start(ctx context.Context) { + rchan := make(chan chan struct{}) + wchan := make(chan int) + go func(rchan <-chan chan struct{}, wchan <-chan int) { + for { + select { + case <-ctx.Done(): + return + case c := <-rchan: + if f.sem > 0 { + f.sem-- + close(c) + } else { + f.queue = append(f.queue, c) + } + case n := <- wchan: + f.sem += n + for len(f.queue) > 0 && f.sem > 0 { + close(f.queue[0]) + f.queue = f.queue[1:] + f.sem-- + } + } + } + }(rchan, wchan) + f.rchan = rchan + f.wchan = wchan +} + +func (f *semFile) Stat() (*lib9p.FileInfo, error) { + t := uint32(time.Now().Unix()) + return &lib9p.FileInfo{Stat: lib9p.Stat{ + Qid: lib9p.Qid{Type: lib9p.QTFILE, Vers: 0, Path: f.path}, + Mode: 0666, + Atime: t, + Mtime: t, + Length: int64(f.sem), + Name: f.name, + }}, nil +} + +func (f *semFile) WStat(stat *lib9p.Stat) error { return nil } + +func (f *semFile) Close() error { + return nil +} + +func (f *semFile) Read(p []byte) (n int, err error) { + if len(p) > 0 { + log.Println("chan") + c := make(chan struct{}) + f.rchan <- c + <-c + return 0, io.EOF + } + return 0, nil +} + +func (f *semFile) Write(p []byte) (int, error) { + n, err := strconv.Atoi(strings.Split(string(p), "\n")[0]) + if err != nil { + return 0, errors.New("invalid sem") + } + f.wchan <- n + return len(p), nil +} diff --git a/cmd/semfs/main.go b/cmd/semfs/main.go @@ -0,0 +1,43 @@ +package main + +import ( + "context" + "flag" + "fmt" + "log" + "net" + "os" + + "git.mtkn.jp/lib9p" +) + +var dFlag = flag.Bool("D", false, "Prints chatty message to the stderr.") + +func main() { + flag.Parse() + if flag.NArg() != 0 { + fmt.Fprintf(os.Stderr, "usage: %s [-D]\n", os.Args[0]) + os.Exit(1) + } + listener, err := net.Listen("tcp", "127.0.0.1:5640") + if err != nil { + log.Fatalf("listen tcp: %v", err) + } + fsys := new(semFS) + for { + conn, err := listener.Accept() + if err != nil { + log.Printf("accept connection: %v", err) + continue + } + go handle(conn, fsys) + } +} + +func handle(conn net.Conn, fsys *semFS) { + srv := lib9p.NewServer(fsys, 8*1024, conn, conn) + if *dFlag { + srv.Chatty() + } + srv.Serve(context.Background()) +}