unionfs.go (2078B)
1 package main 2 3 import ( 4 "fmt" 5 "io/fs" 6 "path" 7 "strings" 8 9 "git.mtkn.jp/lib9p" 10 ) 11 12 func absPath(cwd, pth string) string { 13 if len(pth) == 0 || pth[0] != '/' { 14 pth = path.Join(cwd, pth) 15 } 16 pth = path.Clean(pth) 17 if len(pth) == 0 || pth == "/" { 18 pth = "." 19 } else if pth[0] == '/' { 20 pth = pth[1:] 21 } 22 return pth 23 } 24 25 func relPath(cwd, pth string) string { 26 cwd, pth = absPath("/", cwd), absPath("/", pth) 27 pth = strings.TrimPrefix(pth, cwd) 28 if len(pth) == 0 || pth == "/" { 29 pth = "." 30 } else if pth[0] == '/' { 31 pth = pth[1:] 32 } 33 return pth 34 } 35 36 type unionFS struct { 37 // fstab holds mount points and mounted fs at that point. 38 // mount points must be valid in terms of fs.ValidPath. 39 fstab map[string]lib9p.FS 40 } 41 42 func (fsys *unionFS) OpenFile(name string, omode lib9p.OpenMode) (lib9p.File, error) { 43 if !fs.ValidPath(name) { 44 return nil, &fs.PathError{ 45 Op: "OpenFile", 46 Path: name, 47 Err: fs.ErrInvalid, 48 } 49 } 50 var ( 51 subfs lib9p.FS 52 subroot = name 53 subpath string 54 ) 55 for subroot != "" { 56 if subfs = fsys.fstab[subroot]; subfs != nil { 57 subpath = strings.TrimPrefix(name, subroot) 58 if len(subpath) == 0 || subpath == "/" { 59 subpath = "." 60 } else if subpath[0] == '/' { 61 subpath = subpath[1:] 62 } 63 break 64 } 65 subroot = path.Dir(subroot) 66 } 67 if !fs.ValidPath(subroot) || !fs.ValidPath(subpath) { 68 panic(fmt.Errorf("path malfold: %q, %q\nfstab, name: %v, %s", 69 fsys.fstab, name, subroot, subpath)) 70 } 71 if subfs == nil { 72 return nil, &fs.PathError{ 73 Op: "OpenFile", 74 Path: name, 75 Err: fs.ErrNotExist, 76 } 77 } 78 f, err := subfs.OpenFile(subpath, omode) 79 return f, err 80 } 81 82 func (fsys *unionFS) Mount(fs9 lib9p.FS, mtpt string) error { 83 if !fs.ValidPath(mtpt) { 84 return &fs.PathError{ 85 Op: "mount", 86 Path: mtpt, 87 Err: fs.ErrInvalid, 88 } 89 } 90 if _, ok := fsys.fstab[mtpt]; ok { 91 return fmt.Errorf("mtpt already used") 92 } 93 fi, err := fs.Stat(lib9p.ExportFS{fsys}, mtpt) 94 if err != nil { 95 return fmt.Errorf("stat mtpt: %v", err) 96 } 97 if !fi.IsDir() { 98 return fmt.Errorf("not a directory") 99 } 100 fsys.fstab[mtpt] = fs9 101 return nil 102 }