You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
subgraph-oz/fs/item.go

185 lines
3.6 KiB

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
}