9sh

9P shell
Log | Files | Refs

commit cdf1992058283659a9c9e3f5eec9dc17fa8c95a5
parent 3bbf3f7f0a91c7b7b9e3da8ed2b088eaa100c5cb
Author: Matsuda Kenji <info@mtkn.jp>
Date:   Mon,  8 Jan 2024 12:14:49 +0900

add mount

Diffstat:
Mmain.go | 121+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------
Apath_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