package fs import ( "fmt" "os" "path" "path/filepath" "strings" "syscall" "github.com/op/go-logging" ) type MountFlag int func (mf MountFlag) isSet(f MountFlag) bool { return mf&f == f } const ( MountReadOnly MountFlag = 1 << iota MountCreateIfAbsent ) type mountItem struct { path string target string flags MountFlag fs *Filesystem } func (mi *mountItem) targetPath() string { root := mi.fs.root if mi.target != "" { return path.Join(root, mi.target) } return path.Join(root, mi.path) } func (mi *mountItem) bind() error { if strings.Contains(mi.path, "*") { return mi.bindGlobbed() } return mi.bindItem() } func (mi *mountItem) bindGlobbed() error { if mi.target != "" { mi.fs.log.Warning("Ignoring target directory (%s) for mount item containing glob character: (%s)", mi.target, mi.path) mi.target = "" } globbed, err := filepath.Glob(mi.path) if err != nil { return err } savedPath := mi.path for _, p := range globbed { if strings.Contains(p, "*") { // XXX continue } mi.path = p if err := mi.bind(); err != nil { // XXX mi.path = savedPath return err } } mi.path = savedPath return nil } func (mi *mountItem) readSourceInfo(src string) (os.FileInfo, error) { if fi, err := os.Stat(src); err == nil { return fi, nil } else if !os.IsNotExist(err) { return nil, err } if !mi.flags.isSet(MountCreateIfAbsent) { return nil, fmt.Errorf("source path (%s) does not exist", src) } home := mi.fs.user.HomeDir if !strings.HasPrefix(src, home) { return nil, fmt.Errorf("mount item (%s) has flag MountCreateIfAbsent, but is not child of home directory (%s)", src, home) } if err := os.MkdirAll(src, 0750); err != nil { return nil, err } pinfo, err := os.Stat(path.Dir(src)) if err != nil { return nil, err } if err := copyFileInfo(pinfo, src); err != nil { return nil, err } return os.Stat(src) } func (mi *mountItem) bindItem() error { src, err := filepath.EvalSymlinks(mi.path) if err != nil { return fmt.Errorf("error resolving symlinks for path (%s): %v", mi.path, err) } sinfo, err := mi.readSourceInfo(src) if err != nil { // XXX return err } target := mi.targetPath() _, err = os.Stat(target) if err == nil || !os.IsNotExist(err) { mi.fs.log.Warning("Target (%s) already exists, ignoring", target) return nil } if sinfo.IsDir() { if err := os.MkdirAll(target, sinfo.Mode().Perm()); err != nil { return err } } else { if err := createEmptyFile(target, 0750); err != nil { return err } } if err := copyPathPermissions(mi.fs.root, src); err != nil { return fmt.Errorf("failed to copy path permissions for (%s): %v", src, err) } return bindMount(src, target, mi.flags.isSet(MountReadOnly), 0) } func (mi *mountItem) blacklist() error { if strings.Contains(mi.path, "*") { return mi.blacklistGlobbed() } return blacklistItem(mi.path, mi.fs.log) } func (mi *mountItem) blacklistGlobbed() error { globbed, err := filepath.Glob(mi.path) if err != nil { // XXX } for _, p := range globbed { if err := blacklistItem(p, mi.fs.log); err != nil { return err } } return nil } func blacklistItem(path string, log *logging.Logger) error { p, err := filepath.EvalSymlinks(path) if err != nil { log.Warning("Symlink evaluation failed for path: %s", path) return err } fi, err := os.Stat(p) if err != nil { if os.IsNotExist(err) { log.Info("Blacklist item (%s) does not exist", p) return nil } return err } src := emptyFilePath if fi.IsDir() { src = emptyDirPath } if err := syscall.Mount(src, p, "none", syscall.MS_BIND, "mode=400,gid=0"); err != nil { // XXX warning return err } // XXX log success return nil }