9sh

9P shell
Log | Files | Refs

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 }