commit cdf1992058283659a9c9e3f5eec9dc17fa8c95a5
parent 3bbf3f7f0a91c7b7b9e3da8ed2b088eaa100c5cb
Author: Matsuda Kenji <info@mtkn.jp>
Date: Mon, 8 Jan 2024 12:14:49 +0900
add mount
Diffstat:
M | main.go | | | 121 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------- |
A | path_test.go | | | 43 | +++++++++++++++++++++++++++++++++++++++++++ |
2 files changed, 153 insertions(+), 11 deletions(-)
diff --git a/main.go b/main.go
@@ -16,10 +16,29 @@ import (
"git.mtkn.jp/lib9p/client"
)
+func absPath(cwd, pth string) string {
+ if len(pth) == 0 || pth[0] != '/' {
+ pth = path.Join("/" + cwd, pth)
+ }
+ pth = path.Clean(pth)
+ return pth
+}
+
+func relPath(cwd, pth string) string {
+ cwd, pth = absPath("/", cwd), absPath("/", pth)
+ pth = strings.TrimPrefix(pth, cwd)
+ if len(pth) == 0 || pth == "/" {
+ pth = "."
+ } else if pth[0] == '/' {
+ pth = pth[1:]
+ }
+ return pth
+}
+
type stat struct {
- cwd string // current working directory.
+ cwd string // current working directory. absolute path.
f fs.File
- fsys fs.FS
+ fstab map[string]fs.FS
}
type token struct {
@@ -33,6 +52,13 @@ type cmd struct {
}
func (c *cmd) run(s *stat) error {
+ defer func() {
+ p := recover()
+ if p != nil {
+ log.Printf("s.cwd: %q", s.cwd)
+ panic(p)
+ }
+ }()
if len(c.args) < 1 {
return errors.New("no command specified")
}
@@ -40,16 +66,33 @@ func (c *cmd) run(s *stat) error {
case "cd":
var p string
if len(c.args) == 1 {
- p = "."
+ p = "/"
} else if len(c.args) == 2 {
- p = path.Join(s.cwd, c.args[1])
+ p = absPath(s.cwd, c.args[1])
} else {
return fmt.Errorf("usage: cd <dir>")
}
if s.f != nil {
s.f.Close()
}
- f, err := s.fsys.Open(p)
+ var fsys fs.FS
+ var n int
+ var subpath string
+ for mtpt, f := range s.fstab {
+ if strings.HasPrefix(p, mtpt) && len(mtpt) > n {
+ fsys = f
+ n = len(mtpt)
+ subpath = strings.TrimPrefix(p, mtpt)
+ }
+ }
+ fi, err := fs.Stat(fsys, subpath)
+ if err != nil {
+ return err
+ }
+ if !fi.IsDir() {
+ return fmt.Errorf("not a directory")
+ }
+ f, err := fsys.Open(subpath)
if err != nil {
return err
}
@@ -60,7 +103,21 @@ func (c *cmd) run(s *stat) error {
fmt.Println(s.cwd)
return nil
case "ls":
- files, err := fs.Glob(s.fsys, s.cwd + "/*")
+ p := s.cwd
+ if len(c.args) > 1 {
+ p = absPath(p, c.args[1])
+ }
+ var fsys fs.FS
+ var n int
+ var subpath string
+ for mtpt, f := range s.fstab {
+ if strings.HasPrefix(p, mtpt) && len(mtpt) > n {
+ fsys = f
+ n = len(mtpt)
+ subpath = strings.TrimPrefix(p, mtpt)
+ }
+ }
+ files, err := fs.Glob(fsys, path.Join(subpath, "*"))
if err != nil {
return err
}
@@ -68,6 +125,47 @@ func (c *cmd) run(s *stat) error {
fmt.Println(f)
}
return nil
+ case "mount":
+ if len(c.args) != 3 {
+ return fmt.Errorf("usage: mount servename mtpt")
+ }
+ servname, mtpt := c.args[1], c.args[2]
+ conn, err := net.Dial("tcp", servname)
+ if err != nil {
+ return err
+ }
+ var p string
+ if len(mtpt) != 0 && mtpt[0] == '/' {
+ p = absPath("", mtpt)
+ } else {
+ p = absPath(s.cwd, mtpt)
+ }
+ var fsys fs.FS
+ var n int
+ var mpath string
+ for m, f := range s.fstab {
+ if strings.HasPrefix(p, m) && len(m) > n {
+ fsys = f
+ n = len(m)
+ mpath = m
+ }
+ }
+ fi, err := fs.Stat(fsys, relPath(mpath, p))
+ if err != nil {
+ return err
+ }
+ if !fi.IsDir() {
+ return fmt.Errorf("mtpt not a directory")
+ }
+ cfsys, err := client.Mount(context.TODO(), conn, conn, "kenji", "")
+ if err != nil {
+ return err
+ }
+ s.fstab[p] = lib9p.ExportFS{cfsys}
+ return nil
+ case "fstab" :
+ fmt.Println(s.fstab)
+ return nil
default:
return fmt.Errorf("unknown command %v", c.args[0])
}
@@ -79,17 +177,19 @@ func main() {
log.Fatalf("dial: %v", err)
}
defer conn.Close()
- fsys, err := client.Mount(conn, conn, "kenji", "")
+ ctx, cancel := context.WithCancel(context.Background())
+ fsys, err := client.Mount(ctx, conn, conn, "kenji", "")
if err != nil {
log.Fatalf("mount: %v", err)
}
- ctx, cancel := context.WithCancel(context.Background())
defer cancel()
tc := runTokenizer(ctx, os.Stdin)
cc := runParser(ctx, tc)
s := &stat{
- fsys: lib9p.ExportFS{fsys},
+ cwd: "/",
+ fstab: make(map[string]fs.FS),
}
+ s.fstab["/"] = lib9p.ExportFS{fsys}
for {
fmt.Printf("9%% ")
c := <-cc
@@ -187,4 +287,4 @@ M:
}
}()
return cc
-}
-\ No newline at end of file
+}
diff --git a/path_test.go b/path_test.go
@@ -0,0 +1,42 @@
+package main
+
+import (
+ "testing"
+)
+
+func TestAbsPath(t *testing.T) {
+ tests := []struct {
+ cwd, pth, want string
+ }{
+ {"/a", "../../", "/"},
+ {"/a", "b/../c", "/a/c"},
+ {"/", ".", "/"},
+ {"/", "", "/"},
+ {"", "", "/"},
+ {"", "/a/b/c", "/a/b/c"},
+ }
+ for _, test := range tests {
+ got := absPath(test.cwd, test.pth)
+ if got != test.want {
+ t.Errorf("absPath(%q, %q) = %q, want: %q",
+ test.cwd, test.pth, got, test.want)
+ }
+ }
+}
+
+func TestRelPath(t *testing.T) {
+ tests := []struct {
+ cwd, pth, want string
+ }{
+ {"/a", "/a/b/c", "b/c"},
+ {"/a", "/c", "c"},
+ {"/", ".", "."},
+ }
+ for _, test := range tests {
+ got := relPath(test.cwd, test.pth)
+ if got != test.want {
+ t.Errorf("relPath(%q, %q) = %q, want: %q",
+ test.cwd, test.pth, got, test.want)
+ }
+ }
+}
+\ No newline at end of file