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/fs.go

226 lines
5.1 KiB

package fs
import (
"fmt"
"os"
"os/user"
"path"
"strconv"
"strings"
"syscall"
"github.com/op/go-logging"
"github.com/subgraph/oz"
)
type directory struct {
path string
empty bool
}
type Filesystem struct {
log *logging.Logger
user *user.User
name string
base string
root string
xpra string
userID string
noDefaults bool
noSysAndProc bool
whitelist []*mountItem
blacklist []*mountItem
}
func (fs *Filesystem) Root() string {
return fs.root
}
func (fs *Filesystem) Xpra() string {
return fs.xpra
}
func (fs *Filesystem) addWhitelist(path, target string, readonly bool) error {
item, err := fs.newItem(path, target, readonly)
if err != nil {
return err
}
fs.whitelist = append(fs.whitelist, item)
return nil
}
func (fs *Filesystem) addBlacklist(path string) error {
item, err := fs.newItem(path, "", false)
if err != nil {
return err
}
fs.blacklist = append(fs.blacklist, item)
return nil
}
func (fs *Filesystem) newItem(path, target string, readonly bool) (*mountItem, error) {
p, err := fs.resolveVars(path)
if err != nil {
return nil, err
}
return &mountItem{
path: p,
target: target,
//readonly: readonly,
fs: fs,
}, nil
}
func NewFromProfile(profile *oz.Profile, user *user.User, log *logging.Logger) *Filesystem {
fs := NewFilesystem(profile.Name, user, log)
for _, wl := range profile.Whitelist {
fs.addWhitelist(wl.Path, wl.Path, wl.ReadOnly)
}
for _, bl := range profile.Blacklist {
fs.addBlacklist(bl.Path)
}
fs.noDefaults = profile.NoDefaults
fs.noSysAndProc = profile.NoSysProc
if profile.XServer.Enabled {
fs.xpra = path.Join(user.HomeDir, ".Xoz", profile.Name)
}
return fs
}
func NewFilesystem(name string, user *user.User, log *logging.Logger) *Filesystem {
fs := new(Filesystem)
fs.log = log
fs.name = name
if log == nil {
fs.log = logging.MustGetLogger("oz")
}
fs.base = path.Join("/srv/oz", name)
fs.root = path.Join(fs.base, "rootfs")
fs.user = user
fs.userID = strconv.Itoa(os.Getuid())
return fs
}
/*
func xcreateEmptyDirectories(base string, paths []string) error {
for _, p := range paths {
target := path.Join(base, p)
if err := createEmptyDir(p, target); err != nil {
return err
}
}
return nil
}
func createEmptyDir(source, target string) error {
return nil
}
func createSubdirs(base string, subdirs []string) error {
for _, sdir := range subdirs {
path := path.Join(base, sdir)
if err := createDirTree(path); err != nil {
return err
}
}
return nil
}
func createDirTree(path string) error {
st, err := os.Stat(path)
if err == nil {
if !st.IsDir() {
return fmt.Errorf("cannot create directory %s because path already exists and is not directory", path)
}
return nil
}
if !os.IsNotExist(err) {
return fmt.Errorf("unexpected error attempting Stat() on path %s: %v", path, err)
}
if err := os.MkdirAll(path, 0755); err != nil {
return fmt.Errorf("error creating directory tree %s: %v", path, err)
}
return nil
}
*/
// bindMount performs a bind mount of the source path item so that it is visible
// at the target path. By default the mount is flagged MS_NOSUID and MS_NODEV
// but additional flags can be passed in extraFlags. If the readonly flag is
// set the bind mount is remounted as MS_RDONLY.
func bindMount(source, target string, readonly bool, extraFlags uintptr) error {
flags := syscall.MS_BIND | syscall.MS_NOSUID | syscall.MS_NODEV | extraFlags
if err := syscall.Mount(source, target, "", flags, ""); err != nil {
return fmt.Errorf("failed to bind mount %s to %s: %v", source, target, err)
}
if readonly {
flags |= syscall.MS_RDONLY | syscall.MS_REMOUNT
if err := syscall.Mount("", target, "", flags, ""); err != nil {
return fmt.Errorf("failed to remount %s as RDONLY: %v", target, err)
}
}
return nil
}
func createEmptyFile(name string, mode os.FileMode) error {
if err := os.MkdirAll(path.Dir(name), 0750); err != nil {
return err
}
fd, err := os.Create(name)
if err != nil {
return err
}
if err := fd.Close(); err != nil {
return err
}
if err := os.Chmod(name, mode); err != nil {
return err
}
return nil
}
func copyPathPermissions(root, src string) error {
current := "/"
for _, part := range strings.Split(src, "/") {
if part == "" {
continue
}
current = path.Join(current, part)
target := path.Join(root, current)
if err := copyFilePermissions(current, target); err != nil {
return err
}
}
return nil
}
func copyFilePermissions(src, target string) error {
fi, err := os.Stat(src)
if err != nil {
return err
}
return copyFileInfo(fi, target)
}
func copyFileInfo(info os.FileInfo, target string) error {
st := info.Sys().(*syscall.Stat_t)
os.Chown(target, int(st.Uid), int(st.Gid))
os.Chmod(target, info.Mode().Perm())
return nil
}
func createSubdirs(base string, uid, gid int, mode os.FileMode, subdirs ...string) error {
dir := base
for _, sd := range subdirs {
dir = path.Join(dir, sd)
if err := os.Mkdir(dir, mode); err != nil && !os.IsExist(err) {
return err
}
if err := os.Chown(dir, uid, gid); err != nil {
return err
}
}
return nil
}