commit a93eebf289aea525a9d752739504bc49affae3d9
parent cdf1992058283659a9c9e3f5eec9dc17fa8c95a5
Author: Matsuda Kenji <info@mtkn.jp>
Date: Mon, 8 Jan 2024 14:54:34 +0900
add unionFS
Diffstat:
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