Skip to content
This repository has been archived by the owner on Feb 24, 2020. It is now read-only.

Commit

Permalink
kvm: refactor volume mounting
Browse files Browse the repository at this point in the history
  • Loading branch information
jellonek committed Apr 8, 2016
1 parent cb7aa7b commit 7f3631b
Show file tree
Hide file tree
Showing 5 changed files with 146 additions and 150 deletions.
136 changes: 0 additions & 136 deletions stage1/init/common/kvm_mount.go

This file was deleted.

4 changes: 2 additions & 2 deletions stage1/init/common/mount.go
Expand Up @@ -62,10 +62,10 @@ func convertedFromDocker(ra *schema.RuntimeApp) bool {
return ok
}

// generateMounts maps MountPoint paths to volumes, returning a list of mounts,
// GenerateMounts maps MountPoint paths to volumes, returning a list of mounts,
// each with a parameter indicating if it's an implicit empty volume from a
// Docker image.
func generateMounts(ra *schema.RuntimeApp, volumes map[types.ACName]types.Volume) []mountWrapper {
func GenerateMounts(ra *schema.RuntimeApp, volumes map[types.ACName]types.Volume) []mountWrapper {
app := ra.App

var genMnts []mountWrapper
Expand Down
16 changes: 4 additions & 12 deletions stage1/init/common/pod.go
Expand Up @@ -48,7 +48,7 @@ import (
const (
// FlavorFile names the file storing the pod's flavor
FlavorFile = "flavor"
sharedVolPerm = os.FileMode(0755)
SharedVolPerm = os.FileMode(0755)
)

var (
Expand Down Expand Up @@ -455,14 +455,6 @@ func appToSystemd(p *stage1commontypes.Pod, ra *schema.RuntimeApp, interactive b
return errwrap.Wrap(errors.New("failed to link service want"), err)
}

if flavor == "kvm" {
// bind mount all shared volumes (we don't use mechanism for bind-mounting given by nspawn)
if err := MountSharedVolumes(common.Stage1RootfsPath(p.Root), appName, p.Manifest.Volumes, ra); err != nil {
return errwrap.Wrap(errors.New("failed to prepare mount units"), err)
}

}

if err = writeAppReaper(p, appName.String()); err != nil {
return errwrap.Wrap(fmt.Errorf("failed to write app %q reaper service", appName), err)
}
Expand Down Expand Up @@ -625,10 +617,10 @@ func appToNspawnArgs(p *stage1commontypes.Pod, ra *schema.RuntimeApp) ([]string,
app := ra.App

sharedVolPath := common.SharedVolumesPath(p.Root)
if err := os.MkdirAll(sharedVolPath, sharedVolPerm); err != nil {
if err := os.MkdirAll(sharedVolPath, SharedVolPerm); err != nil {
return nil, errwrap.Wrap(errors.New("could not create shared volumes directory"), err)
}
if err := os.Chmod(sharedVolPath, sharedVolPerm); err != nil {
if err := os.Chmod(sharedVolPath, SharedVolPerm); err != nil {
return nil, errwrap.Wrap(fmt.Errorf("could not change permissions of %q", sharedVolPath), err)
}

Expand All @@ -637,7 +629,7 @@ func appToNspawnArgs(p *stage1commontypes.Pod, ra *schema.RuntimeApp) ([]string,
vols[v.Name] = v
}

mounts := generateMounts(ra, vols)
mounts := GenerateMounts(ra, vols)
for _, m := range mounts {
vol := vols[m.Volume]

Expand Down
8 changes: 8 additions & 0 deletions stage1/init/init.go
Expand Up @@ -628,6 +628,14 @@ func stage1() int {
return 1
}

// prepare mounts for kvm flavor
if flavor == "kvm" {
if err := KvmPrepareMounts(s1Root, p); err != nil {
log.PrintE("could not prepare mounts", err)
return 1
}
}

err = stage1common.WithClearedCloExec(lfd, func() error {
return syscall.Exec(args[0], args, env)
})
Expand Down
132 changes: 132 additions & 0 deletions stage1/init/kvm.go
Expand Up @@ -18,8 +18,15 @@ package main

import (
"errors"
"fmt"
"os"
"path/filepath"
"strconv"
"strings"
"syscall"

"github.com/appc/spec/schema"
"github.com/appc/spec/schema/types"
"github.com/coreos/rkt/common"
"github.com/coreos/rkt/networking"
stage1commontypes "github.com/coreos/rkt/stage1/common/types"
Expand All @@ -40,3 +47,128 @@ func KvmNetworkingToSystemd(p *stage1commontypes.Pod, n *networking.Networking)

return nil
}

func mountSharedVolumes(root string, volumes []types.Volume, ra *schema.RuntimeApp) error {
app := ra.App
appName := ra.Name
vols := make(map[types.ACName]types.Volume)
for _, v := range volumes {
vols[v.Name] = v
}

sharedVolPath := common.SharedVolumesPath(root)
if err := os.MkdirAll(sharedVolPath, stage1initcommon.SharedVolPerm); err != nil {
return errwrap.Wrap(errors.New("could not create shared volumes directory"), err)
}
if err := os.Chmod(sharedVolPath, stage1initcommon.SharedVolPerm); err != nil {
return errwrap.Wrap(fmt.Errorf("could not change permissions of %q", sharedVolPath), err)
}

mounts := stage1initcommon.GenerateMounts(ra, vols)
for _, m := range mounts {
vol := vols[m.Volume]

if vol.Kind == "empty" {
p := filepath.Join(sharedVolPath, vol.Name.String())
if err := os.MkdirAll(p, stage1initcommon.SharedVolPerm); err != nil {
return errwrap.Wrap(fmt.Errorf("could not create shared volume %q", vol.Name), err)
}
if err := os.Chown(p, *vol.UID, *vol.GID); err != nil {
return errwrap.Wrap(fmt.Errorf("could not change owner of %q", p), err)
}
mod, err := strconv.ParseUint(*vol.Mode, 8, 32)
if err != nil {
return errwrap.Wrap(fmt.Errorf("invalid mode %q for volume %q", *vol.Mode, vol.Name), err)
}
if err := os.Chmod(p, os.FileMode(mod)); err != nil {
return errwrap.Wrap(fmt.Errorf("could not change permissions of %q", p), err)
}
}

readOnly := stage1initcommon.IsMountReadOnly(vol, app.MountPoints)
var source string
switch vol.Kind {
case "host":
source = vol.Source
case "empty":
source = filepath.Join(common.SharedVolumesPath(root), vol.Name.String())
default:
return fmt.Errorf(`invalid volume kind %q. Must be one of "host" or "empty"`, vol.Kind)
}
absAppRootfs, err := filepath.Abs(common.AppRootfsPath(root, appName))
if err != nil {
return fmt.Errorf(`could not evaluate absolute path for application rootfs in app: %v`, appName)
}

absDestination, err := filepath.Abs(filepath.Join(absAppRootfs, m.Path))
if err != nil {
return fmt.Errorf(`could not evaluate absolute path for application volume path %q in: %v`, m.Path, appName)
}
if !strings.HasPrefix(absDestination, absAppRootfs) {
return fmt.Errorf("path escapes app's root: %v", absDestination)
}
if cleanedSource, err := filepath.EvalSymlinks(source); err != nil {
return errwrap.Wrap(fmt.Errorf("could not resolve symlink for source: %v", source), err)
} else if err := ensureDestinationExists(cleanedSource, absDestination); err != nil {
return errwrap.Wrap(fmt.Errorf("could not create destination mount point: %v", absDestination), err)
} else if err := doBindMount(cleanedSource, absDestination, readOnly); err != nil {
return errwrap.Wrap(fmt.Errorf("could not bind mount path %v (s: %v, d: %v)", m.Path, source, absDestination), err)
}
}
return nil
}

func doBindMount(source, destination string, readOnly bool) error {
if err := syscall.Mount(source, destination, "bind", syscall.MS_BIND, ""); err != nil {
return err
}
if readOnly {
return syscall.Mount(source, destination, "bind", syscall.MS_REMOUNT|syscall.MS_RDONLY|syscall.MS_BIND, "")
}
return nil
}

func ensureDestinationExists(source, destination string) error {
fileInfo, err := os.Stat(source)
if err != nil {
return errwrap.Wrap(fmt.Errorf("could not stat source location: %v", source), err)
}

targetPathParent, _ := filepath.Split(destination)
if err := os.MkdirAll(targetPathParent, stage1initcommon.SharedVolPerm); err != nil {
return errwrap.Wrap(fmt.Errorf("could not create parent directory: %v", targetPathParent), err)
}

if fileInfo.IsDir() {
if err := os.Mkdir(destination, stage1initcommon.SharedVolPerm); !os.IsExist(err) {
return err
}
} else {
if file, err := os.OpenFile(destination, os.O_CREATE, stage1initcommon.SharedVolPerm); err != nil {
return err
} else {
file.Close()
}
}
return nil
}

func prepareMountsForApp(s1Root string, p *stage1commontypes.Pod, ra *schema.RuntimeApp) error {
// bind mount all shared volumes (we don't use mechanism for bind-mounting given by nspawn)
if err := mountSharedVolumes(s1Root, p.Manifest.Volumes, ra); err != nil {
return errwrap.Wrap(errors.New("failed to prepare mount point"), err)
}

return nil
}

func KvmPrepareMounts(s1Root string, p *stage1commontypes.Pod) error {
for i := range p.Manifest.Apps {
ra := &p.Manifest.Apps[i]
if err := prepareMountsForApp(s1Root, p, ra); err != nil {
return errwrap.Wrap(fmt.Errorf("failed prepare mounts for app %q", ra.Name), err)
}
}

return nil
}

0 comments on commit 7f3631b

Please sign in to comment.