9sh

9P shell
Log | Files | Refs

commit a93eebf289aea525a9d752739504bc49affae3d9
parent cdf1992058283659a9c9e3f5eec9dc17fa8c95a5
Author: Matsuda Kenji <info@mtkn.jp>
Date:   Mon,  8 Jan 2024 14:54:34 +0900

add unionFS

Diffstat:
Mmain.go | 24+++---------------------
Mpath_test.go | 12++++++------
Aunionfs.go | 95+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aunionfs_test.go | 13+++++++++++++
4 files changed, 117 insertions(+), 27 deletions(-)

diff --git a/main.go b/main.go @@ -16,24 +16,6 @@ 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. absolute path. @@ -66,7 +48,7 @@ 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 = absPath(s.cwd, c.args[1]) } else { @@ -186,10 +168,10 @@ func main() { tc := runTokenizer(ctx, os.Stdin) cc := runParser(ctx, tc) s := &stat{ - cwd: "/", + cwd: ".", fstab: make(map[string]fs.FS), } - s.fstab["/"] = lib9p.ExportFS{fsys} + s.fstab["."] = lib9p.ExportFS{fsys} for { fmt.Printf("9%% ") c := <-cc diff --git a/path_test.go b/path_test.go @@ -8,12 +8,12 @@ func TestAbsPath(t *testing.T) { tests := []struct { cwd, pth, want string }{ - {"/a", "../../", "/"}, - {"/a", "b/../c", "/a/c"}, - {"/", ".", "/"}, - {"/", "", "/"}, - {"", "", "/"}, - {"", "/a/b/c", "/a/b/c"}, + {"/a", "../../", "."}, + {"/a", "b/../c", "a/c"}, + {"/", ".", "."}, + {"/", "", "."}, + {"", "", "."}, + {"", "/a/b/c", "a/b/c"}, } for _, test := range tests { got := absPath(test.cwd, test.pth) diff --git a/unionfs.go b/unionfs.go @@ -0,0 +1,94 @@ +package main + +import ( + "fmt" + "io/fs" + "path" + "strings" + + "git.mtkn.jp/lib9p" +) + +func absPath(cwd, pth string) string { + if len(pth) == 0 || pth[0] != '/' { + pth = path.Join(cwd, pth) + } + pth = path.Clean(pth) + if len(pth) == 0 || pth == "/" { + pth = "." + } else if pth[0] == '/' { + pth = pth[1:] + } + 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 unionFS struct { + // fstab holds mount points and mounted fs at that point. + // mount points must be valid in terms of fs.ValidPath. + fstab map[string]lib9p.FS +} + +func (fsys *unionFS) OpenFile(name string, omode lib9p.OpenMode) (lib9p.File, error) { + if !fs.ValidPath(name) { + return nil, &fs.PathError{ + Op: "OpenFile", + Path: name, + Err: fs.ErrInvalid, + } + } + var ( + subfs lib9p.FS + subroot = name + subpath string + ) + for subroot != "" { + if subfs = fsys.fstab[subroot]; subfs != nil { + subpath = strings.TrimPrefix(name, subroot) + if len(subpath) == 0 || subpath == "/" { + subpath = "." + } else if subpath[0] == '/' { + subpath = subpath[1:] + } + break + } + subroot = path.Dir(subroot) + } + if !fs.ValidPath(subroot) || !fs.ValidPath(subpath) { + panic(fmt.Errorf("path malfold: %q, %q\nfstab, name: %v, %s", + fsys.fstab, name, subroot, subpath)) + } + if subfs == nil { + return nil, &fs.PathError{ + Op: "OpenFile", + Path: name, + Err: fs.ErrNotExist, + } + } + return subfs.OpenFile(name, omode) +} + +func (fsys *unionFS) Mount(fs9 lib9p.FS, mtpt string) error { + if _, ok := fsys.fstab[mtpt]; ok { + return fmt.Errorf("mtpt already used") + } + fi, err := fs.Stat(lib9p.ExportFS{fsys}, mtpt) + if err != nil { + return fmt.Errorf("stat mtpt: %v", err) + } + if !fi.IsDir() { + return fmt.Errorf("not a directory") + } + fsys.fstab[mtpt] = fs9 + return nil +} +\ No newline at end of file diff --git a/unionfs_test.go b/unionfs_test.go @@ -0,0 +1,12 @@ +package main + +import ( + "testing" + + "git.mtkn.jp/lib9p" +) + +func TestUnionFS(t *testing.T) { + var ufs *unionFS + var _ lib9p.FS = ufs +} +\ No newline at end of file