summaryrefslogtreecommitdiffhomepage
path: root/runsc
diff options
context:
space:
mode:
Diffstat (limited to 'runsc')
-rw-r--r--runsc/boot/config.go3
-rw-r--r--runsc/boot/fs.go143
-rw-r--r--runsc/cmd/create.go3
-rw-r--r--runsc/cmd/run.go2
-rw-r--r--runsc/container/container.go26
-rw-r--r--runsc/container/container_test.go2
-rw-r--r--runsc/container/fs.go30
-rw-r--r--runsc/sandbox/sandbox.go7
-rw-r--r--runsc/specutils/specutils.go23
-rw-r--r--runsc/test/testutil/testutil.go1
10 files changed, 119 insertions, 121 deletions
diff --git a/runsc/boot/config.go b/runsc/boot/config.go
index 41af084b9..51d20d06d 100644
--- a/runsc/boot/config.go
+++ b/runsc/boot/config.go
@@ -192,9 +192,6 @@ type Config struct {
// disabled. Pardon the double negation, but default to enabled is important.
DisableSeccomp bool
- // SpecFile is the file containing the OCI spec.
- SpecFile string
-
// WatchdogAction sets what action the watchdog takes when triggered.
WatchdogAction watchdog.Action
diff --git a/runsc/boot/fs.go b/runsc/boot/fs.go
index 42e011beb..ea825e571 100644
--- a/runsc/boot/fs.go
+++ b/runsc/boot/fs.go
@@ -103,9 +103,14 @@ func createMountNamespace(userCtx context.Context, rootCtx context.Context, spec
return nil, fmt.Errorf("failed to create root mount namespace: %v", err)
}
- if err := setMounts(rootCtx, conf, mns, fds, mounts); err != nil {
- return nil, fmt.Errorf("failed to configure mounts: %v", err)
+ root := mns.Root()
+ defer root.DecRef()
+ for _, m := range mounts {
+ if err := mountSubmount(rootCtx, conf, mns, root, fds, m, mounts); err != nil {
+ return nil, fmt.Errorf("mount submount: %v", err)
+ }
}
+
if !fds.empty() {
return nil, fmt.Errorf("not all mount points were consumed, remaining: %v", fds)
}
@@ -184,17 +189,6 @@ func compileMounts(spec *specs.Spec) []specs.Mount {
return mounts
}
-// setMounts iterates over mounts and mounts them in the specified
-// mount namespace.
-func setMounts(ctx context.Context, conf *Config, mns *fs.MountNamespace, fds *fdDispenser, mounts []specs.Mount) error {
- for _, m := range mounts {
- if err := mountSubmount(ctx, conf, mns, fds, m, mounts, m.Destination); err != nil {
- return err
- }
- }
- return nil
-}
-
// createRootMount creates the root filesystem.
func createRootMount(ctx context.Context, spec *specs.Spec, conf *Config, fds *fdDispenser, mounts []specs.Mount) (*fs.Inode, error) {
// First construct the filesystem from the spec.Root.
@@ -207,9 +201,9 @@ func createRootMount(ctx context.Context, spec *specs.Spec, conf *Config, fds *f
fd := fds.remove()
log.Infof("Mounting root over 9P, ioFD: %d", fd)
- hostFS := mustFindFilesystem("9p")
+ p9FS := mustFindFilesystem("9p")
opts := p9MountOptions(fd, conf.FileAccess)
- rootInode, err = hostFS.Mount(ctx, rootDevice, mf, strings.Join(opts, ","))
+ rootInode, err = p9FS.Mount(ctx, rootDevice, mf, strings.Join(opts, ","))
if err != nil {
return nil, fmt.Errorf("failed to generate root mount point: %v", err)
}
@@ -294,7 +288,11 @@ func getMountNameAndOptions(conf *Config, m specs.Mount, fds *fdDispenser) (stri
return fsName, opts, useOverlay, err
}
-func mountSubmount(ctx context.Context, conf *Config, mns *fs.MountNamespace, fds *fdDispenser, m specs.Mount, mounts []specs.Mount, dest string) error {
+// mountSubmount mounts volumes inside the container's root. Because mounts may
+// be readonly, a lower ramfs overlay is added to create the mount point dir.
+// Another overlay is added with tmpfs on top if Config.Overlay is true.
+// 'm.Destination' must be an absolute path with '..' and symlinks resolved.
+func mountSubmount(ctx context.Context, conf *Config, mns *fs.MountNamespace, root *fs.Dirent, fds *fdDispenser, m specs.Mount, mounts []specs.Mount) error {
// Map mount type to filesystem name, and parse out the options that we are
// capable of dealing with.
fsName, opts, useOverlay, err := getMountNameAndOptions(conf, m, fds)
@@ -340,60 +338,16 @@ func mountSubmount(ctx context.Context, conf *Config, mns *fs.MountNamespace, fd
}
}
- // Create destination in case it doesn't exist. This is required, in addition
- // to 'addSubmountOverlay', in case there are symlinks to create directories
- // in the right location, e.g.
- // mount: /var/run/secrets, may be created in '/run/secrets' if
- // '/var/run' => '/var'.
- if err := mkdirAll(ctx, mns, dest); err != nil {
- return err
- }
-
- root := mns.Root()
- defer root.DecRef()
- dirent, err := mns.FindInode(ctx, root, nil, dest, linux.MaxSymlinkTraversals)
+ dirent, err := mns.FindInode(ctx, root, root, m.Destination, 0 /* maxTraversals */)
if err != nil {
- return fmt.Errorf("failed to find mount destination %q: %v", dest, err)
+ return fmt.Errorf("failed to find mount destination %q: %v", m.Destination, err)
}
defer dirent.DecRef()
if err := mns.Mount(ctx, dirent, inode); err != nil {
- return fmt.Errorf("failed to mount at destination %q: %v", dest, err)
+ return fmt.Errorf("failed to mount at destination %q: %v", m.Destination, err)
}
- log.Infof("Mounted %q to %q type %s", m.Source, dest, m.Type)
- return nil
-}
-
-func mkdirAll(ctx context.Context, mns *fs.MountNamespace, path string) error {
- log.Infof("mkdirAll called with path %s", path)
- root := mns.Root()
- defer root.DecRef()
-
- // Starting at the root, walk the path.
- parent := root
- ps := strings.Split(filepath.Clean(path), string(filepath.Separator))
- for _, pathElem := range ps {
- if pathElem == "" {
- // This will be case for the first and last element, if the path
- // begins or ends with '/'. Note that we always treat the path as
- // absolute, regardless of what the first character contains.
- continue
- }
- d, err := mns.FindInode(ctx, root, parent, pathElem, fs.DefaultTraversalLimit)
- if err == syserror.ENOENT {
- // If we encounter a path that does not exist, then
- // create it.
- if err := parent.CreateDirectory(ctx, root, pathElem, fs.FilePermsFromMode(0755)); err != nil {
- return fmt.Errorf("failed to create directory %q: %v", pathElem, err)
- }
- if d, err = parent.Walk(ctx, root, pathElem); err != nil {
- return fmt.Errorf("walk to %q failed: %v", pathElem, err)
- }
- } else if err != nil {
- return fmt.Errorf("failed to find inode %q: %v", pathElem, err)
- }
- parent = d
- }
+ log.Infof("Mounted %q to %q type %s", m.Source, m.Destination, m.Type)
return nil
}
@@ -437,14 +391,6 @@ func parseAndFilterOptions(opts []string, allowedKeys ...string) ([]string, erro
return out, nil
}
-func destinations(mounts []specs.Mount, extra ...string) []string {
- var ds []string
- for _, m := range mounts {
- ds = append(ds, m.Destination)
- }
- return append(ds, extra...)
-}
-
// mountDevice returns a device string based on the fs type and target
// of the mount.
func mountDevice(m specs.Mount) string {
@@ -544,7 +490,8 @@ func mustFindFilesystem(name string) fs.Filesystem {
func addSubmountOverlay(ctx context.Context, inode *fs.Inode, submounts []string) (*fs.Inode, error) {
// There is no real filesystem backing this ramfs tree, so we pass in
// "nil" here.
- mountTree, err := ramfs.MakeDirectoryTree(ctx, fs.NewNonCachingMountSource(nil, fs.MountSourceFlags{}), submounts)
+ msrc := fs.NewNonCachingMountSource(nil, fs.MountSourceFlags{})
+ mountTree, err := ramfs.MakeDirectoryTree(ctx, msrc, submounts)
if err != nil {
return nil, fmt.Errorf("error creating mount tree: %v", err)
}
@@ -608,12 +555,16 @@ func setupContainerFS(procArgs *kernel.CreateProcessArgs, spec *specs.Spec, conf
// namespace.
mns := k.RootMountNamespace()
if mns == nil {
+ // Setup the root container.
+
// Create the virtual filesystem.
mns, err := createMountNamespace(ctx, rootCtx, spec, conf, goferFDs)
if err != nil {
return fmt.Errorf("error creating mounts: %v", err)
}
k.SetRootMountNamespace(mns)
+
+ // We're done with root container.
return nil
}
@@ -627,42 +578,48 @@ func setupContainerFS(procArgs *kernel.CreateProcessArgs, spec *specs.Spec, conf
return fmt.Errorf("error creating filesystem for container: %v", err)
}
- // Make directories for submounts within the container.
- rootDir := mns.Root()
- defer rootDir.DecRef()
- containerRoot := filepath.Join(ChildContainersDir, cid)
- mkdirAll(ctx, mns, containerRoot)
+ globalRoot := mns.Root()
+ defer globalRoot.DecRef()
- // Mount the container's root filesystem to the newly created
- // mount point.
- containerRootDirent, err := mns.FindInode(ctx, rootDir, nil, containerRoot, linux.MaxSymlinkTraversals)
+ // Create mount point for the container's rootfs.
+ contDir, err := mns.FindInode(ctx, globalRoot, nil, ChildContainersDir, 0 /* TraversalLimit */)
if err != nil {
- return fmt.Errorf("failed to find mount destination: %q: %v", containerRoot, err)
+ return fmt.Errorf("couldn't find child container dir %q: %v", ChildContainersDir, err)
}
- if err := mns.Mount(ctx, containerRootDirent, rootInode); err != nil {
- return fmt.Errorf("failed to mount at destination %q: %v", containerRoot, err)
+ if err := contDir.CreateDirectory(ctx, globalRoot, cid, fs.FilePermsFromMode(0755)); err != nil {
+ return fmt.Errorf("create directory %q: %v", cid, err)
+ }
+ containerRoot, err := contDir.Walk(ctx, globalRoot, cid)
+ if err != nil {
+ return fmt.Errorf("walk to %q failed: %v", cid, err)
+ }
+ defer containerRoot.DecRef()
+
+ // Mount the container's root filesystem to the newly created mount point.
+ if err := mns.Mount(ctx, containerRoot, rootInode); err != nil {
+ return fmt.Errorf("mount container root: %v", err)
}
- containerRootDirent.DecRef()
// We have to re-walk to the dirent to find the mounted
// directory. The old dirent is invalid at this point.
- containerRootDirent, err = mns.FindInode(ctx, rootDir, nil, containerRoot, linux.MaxSymlinkTraversals)
+ containerRoot, err = contDir.Walk(ctx, globalRoot, cid)
if err != nil {
- return fmt.Errorf("failed to find mount destination2: %q: %v", containerRoot, err)
+ return fmt.Errorf("find container mount point %q: %v", cid, err)
}
- log.Infof("Mounted child's root fs to %q", containerRoot)
+
+ log.Infof("Mounted child's root fs to %q", filepath.Join(ChildContainersDir, cid))
// Mount all submounts.
mounts := compileMounts(spec)
for _, m := range mounts {
- dest := filepath.Join(containerRoot, m.Destination)
- if err := mountSubmount(rootCtx, conf, k.RootMountNamespace(), fds, m, mounts, dest); err != nil {
+ if err := mountSubmount(rootCtx, conf, k.RootMountNamespace(), containerRoot, fds, m, mounts); err != nil {
+ containerRoot.DecRef()
return fmt.Errorf("error mounting filesystem for container: %v", err)
}
}
// Set the procArgs root directory.
- procArgs.Root = containerRootDirent
+ procArgs.Root = containerRoot
return nil
}
@@ -686,7 +643,7 @@ func destroyContainerFS(ctx context.Context, cid string, k *kernel.Kernel) error
mnsRoot := mns.Root()
defer mnsRoot.DecRef()
containerRoot := path.Join(ChildContainersDir, cid)
- containerRootDirent, err := mns.FindInode(ctx, mnsRoot, nil, containerRoot, linux.MaxSymlinkTraversals)
+ containerRootDirent, err := mns.FindInode(ctx, mnsRoot, nil, containerRoot, 0 /* maxTraversals */)
if err == syserror.ENOENT {
// Container must have been destroyed already. That's fine.
return nil
@@ -720,7 +677,7 @@ func destroyContainerFS(ctx context.Context, cid string, k *kernel.Kernel) error
// Get a reference to the parent directory and remove the root
// container directory.
- containersDirDirent, err := mns.FindInode(ctx, mnsRoot, nil, ChildContainersDir, linux.MaxSymlinkTraversals)
+ containersDirDirent, err := mns.FindInode(ctx, mnsRoot, nil, ChildContainersDir, 0 /* maxTraversals */)
if err != nil {
return fmt.Errorf("error finding containers directory %q: %v", ChildContainersDir, err)
}
diff --git a/runsc/cmd/create.go b/runsc/cmd/create.go
index ecd76ee93..275a96f57 100644
--- a/runsc/cmd/create.go
+++ b/runsc/cmd/create.go
@@ -15,8 +15,6 @@
package cmd
import (
- "path/filepath"
-
"context"
"flag"
"github.com/google/subcommands"
@@ -93,7 +91,6 @@ func (c *Create) Execute(_ context.Context, f *flag.FlagSet, args ...interface{}
Fatalf("error reading spec: %v", err)
}
specutils.LogSpec(spec)
- conf.SpecFile = filepath.Join(bundleDir, "config.json")
// Create the container. A new sandbox will be created for the
// container unless the metadata specifies that it should be run in an
diff --git a/runsc/cmd/run.go b/runsc/cmd/run.go
index 826e6e875..9a87cf240 100644
--- a/runsc/cmd/run.go
+++ b/runsc/cmd/run.go
@@ -15,7 +15,6 @@
package cmd
import (
- "path/filepath"
"syscall"
"context"
@@ -73,7 +72,6 @@ func (r *Run) Execute(_ context.Context, f *flag.FlagSet, args ...interface{}) s
Fatalf("error reading spec: %v", err)
}
specutils.LogSpec(spec)
- conf.SpecFile = filepath.Join(bundleDir, "config.json")
ws, err := container.Run(id, spec, conf, bundleDir, r.consoleSocket, r.pidFile, r.userLog)
if err != nil {
diff --git a/runsc/container/container.go b/runsc/container/container.go
index 0ec4d03c1..f76bad1aa 100644
--- a/runsc/container/container.go
+++ b/runsc/container/container.go
@@ -271,6 +271,19 @@ func Create(id string, spec *specs.Spec, conf *boot.Config, bundleDir, consoleSo
// init container in the sandbox.
if specutils.ShouldCreateSandbox(spec) {
log.Debugf("Creating new sandbox for container %q", id)
+
+ // Setup rootfs and mounts. It returns a new mount list with destination
+ // paths resolved. Since the spec for the root container is read from disk,
+ // Write the new spec to a new file that will be used by the sandbox.
+ cleanMounts, err := setupFS(spec, conf, bundleDir)
+ if err != nil {
+ return nil, fmt.Errorf("setup mounts: %v", err)
+ }
+ spec.Mounts = cleanMounts
+ if err := specutils.WriteCleanSpec(bundleDir, spec); err != nil {
+ return nil, fmt.Errorf("writing clean spec: %v", err)
+ }
+
ioFiles, err := c.createGoferProcess(spec, conf, bundleDir)
if err != nil {
return nil, err
@@ -351,6 +364,15 @@ func (c *Container) Start(conf *boot.Config) error {
return err
}
} else {
+ // Setup rootfs and mounts. It returns a new mount list with destination
+ // paths resolved. Replace the original spec with new mount list and start
+ // container.
+ cleanMounts, err := setupFS(c.Spec, conf, c.BundleDir)
+ if err != nil {
+ return fmt.Errorf("setup mounts: %v", err)
+ }
+ c.Spec.Mounts = cleanMounts
+
// Create the gofer process.
ioFiles, err := c.createGoferProcess(c.Spec, conf, c.BundleDir)
if err != nil {
@@ -691,10 +713,6 @@ func (c *Container) waitForStopped() error {
}
func (c *Container) createGoferProcess(spec *specs.Spec, conf *boot.Config, bundleDir string) ([]*os.File, error) {
- if err := setupFS(spec, conf, bundleDir); err != nil {
- return nil, fmt.Errorf("failed to setup mounts: %v", err)
- }
-
// Start with the general config flags.
args := conf.ToFlags()
args = append(args, "gofer", "--bundle", bundleDir)
diff --git a/runsc/container/container_test.go b/runsc/container/container_test.go
index e2bb7d8ec..662591b3b 100644
--- a/runsc/container/container_test.go
+++ b/runsc/container/container_test.go
@@ -458,7 +458,7 @@ func TestAppExitStatus(t *testing.T) {
defer os.RemoveAll(rootDir2)
defer os.RemoveAll(bundleDir2)
- ws, err = Run(testutil.UniqueContainerID(), succSpec, conf, bundleDir2, "", "", "")
+ ws, err = Run(testutil.UniqueContainerID(), errSpec, conf, bundleDir2, "", "", "")
if err != nil {
t.Fatalf("error running container: %v", err)
}
diff --git a/runsc/container/fs.go b/runsc/container/fs.go
index 59edd9488..2ed42fd93 100644
--- a/runsc/container/fs.go
+++ b/runsc/container/fs.go
@@ -73,9 +73,13 @@ var optionsMap = map[string]mapping{
// This allows the gofer serving the containers to be chroot under this
// directory to create an extra layer to security in case the gofer gets
// compromised.
-func setupFS(spec *specs.Spec, conf *boot.Config, bundleDir string) error {
+// Returns list of mounts equivalent to 'spec.Mounts' with all destination paths
+// cleaned and with symlinks resolved.
+func setupFS(spec *specs.Spec, conf *boot.Config, bundleDir string) ([]specs.Mount, error) {
+ rv := make([]specs.Mount, 0, len(spec.Mounts))
for _, m := range spec.Mounts {
if m.Type != "bind" || !specutils.IsSupportedDevMount(m) {
+ rv = append(rv, m)
continue
}
@@ -83,39 +87,47 @@ func setupFS(spec *specs.Spec, conf *boot.Config, bundleDir string) error {
// container.
dst, err := resolveSymlinks(spec.Root.Path, m.Destination)
if err != nil {
- return fmt.Errorf("failed to resolve symlinks: %v", err)
+ return nil, fmt.Errorf("failed to resolve symlinks: %v", err)
}
flags := optionsToFlags(m.Options)
flags |= syscall.MS_BIND
log.Infof("Mounting src: %q, dst: %q, flags: %#x", m.Source, dst, flags)
if err := specutils.Mount(m.Source, dst, m.Type, flags); err != nil {
- return fmt.Errorf("failed to mount %v: %v", m, err)
+ return nil, fmt.Errorf("failed to mount %v: %v", m, err)
}
// Make the mount a slave, so that for recursive bind mount, umount won't
// propagate to the source.
flags = syscall.MS_SLAVE | syscall.MS_REC
if err := syscall.Mount("", dst, "", uintptr(flags), ""); err != nil {
- return fmt.Errorf("failed to rslave mount dst: %q, flags: %#x, err: %v", dst, flags, err)
+ return nil, fmt.Errorf("failed to rslave mount dst: %q, flags: %#x, err: %v", dst, flags, err)
}
+
+ cpy := m
+ relDst, err := filepath.Rel(spec.Root.Path, dst)
+ if err != nil {
+ panic(fmt.Sprintf("%q could not be made relative to %q: %v", dst, spec.Root.Path, err))
+ }
+ cpy.Destination = filepath.Join("/", relDst)
+ rv = append(rv, cpy)
}
// If root is read only, check if it needs to be remounted as readonly.
if spec.Root.Readonly {
isMountPoint, readonly, err := mountInfo(spec.Root.Path)
if err != nil {
- return err
+ return nil, err
}
if readonly {
- return nil
+ return rv, nil
}
if !isMountPoint {
// Readonly root is not a mount point nor read-only. Can't do much other
// than just logging a warning. The gofer will prevent files to be open
// in write mode.
log.Warningf("Mount where root is located is not read-only and cannot be changed: %q", spec.Root.Path)
- return nil
+ return rv, nil
}
// If root is a mount point but not read-only, we can change mount options
@@ -124,10 +136,10 @@ func setupFS(spec *specs.Spec, conf *boot.Config, bundleDir string) error {
flags := uintptr(syscall.MS_BIND | syscall.MS_REMOUNT | syscall.MS_RDONLY | syscall.MS_REC)
src := spec.Root.Path
if err := syscall.Mount(src, src, "bind", flags, ""); err != nil {
- return fmt.Errorf("failed to remount root as read-only with source: %q, target: %q, flags: %#x, err: %v", spec.Root.Path, spec.Root.Path, flags, err)
+ return nil, fmt.Errorf("failed to remount root as read-only with source: %q, target: %q, flags: %#x, err: %v", spec.Root.Path, spec.Root.Path, flags, err)
}
}
- return nil
+ return rv, nil
}
// mountInfo returns whether the path is a mount point and whether the mount
diff --git a/runsc/sandbox/sandbox.go b/runsc/sandbox/sandbox.go
index 6dc8cf7f0..923a52f7f 100644
--- a/runsc/sandbox/sandbox.go
+++ b/runsc/sandbox/sandbox.go
@@ -321,12 +321,9 @@ func (s *Sandbox) createSandboxProcess(spec *specs.Spec, conf *boot.Config, bund
nextFD++
// Open the spec file to donate to the sandbox.
- if conf.SpecFile == "" {
- return fmt.Errorf("conf.SpecFile must be set")
- }
- specFile, err := os.Open(conf.SpecFile)
+ specFile, err := specutils.OpenCleanSpec(bundleDir)
if err != nil {
- return fmt.Errorf("error opening spec file %q: %v", conf.SpecFile, err)
+ return fmt.Errorf("opening spec file: %v", err)
}
defer specFile.Close()
cmd.ExtraFiles = append(cmd.ExtraFiles, specFile)
diff --git a/runsc/specutils/specutils.go b/runsc/specutils/specutils.go
index 6b3e52021..b29802fde 100644
--- a/runsc/specutils/specutils.go
+++ b/runsc/specutils/specutils.go
@@ -170,6 +170,29 @@ func ReadSpecFromFile(bundleDir string, specFile *os.File) (*specs.Spec, error)
return &spec, nil
}
+// OpenCleanSpec opens spec file that has destination mount paths resolved to
+// their absolute location.
+func OpenCleanSpec(bundleDir string) (*os.File, error) {
+ f, err := os.Open(filepath.Join(bundleDir, "config.clean.json"))
+ if err != nil {
+ return nil, err
+ }
+ if _, err := f.Seek(0, os.SEEK_SET); err != nil {
+ f.Close()
+ return nil, fmt.Errorf("error seeking to beginning of file %q: %v", f.Name(), err)
+ }
+ return f, nil
+}
+
+// WriteCleanSpec writes a spec file that has destination mount paths resolved.
+func WriteCleanSpec(bundleDir string, spec *specs.Spec) error {
+ bytes, err := json.Marshal(spec)
+ if err != nil {
+ return err
+ }
+ return ioutil.WriteFile(filepath.Join(bundleDir, "config.clean.json"), bytes, 0755)
+}
+
// Capabilities takes in spec and returns a TaskCapabilities corresponding to
// the spec.
func Capabilities(specCaps *specs.LinuxCapabilities) (*auth.TaskCapabilities, error) {
diff --git a/runsc/test/testutil/testutil.go b/runsc/test/testutil/testutil.go
index b4664995c..4d7ac3bc9 100644
--- a/runsc/test/testutil/testutil.go
+++ b/runsc/test/testutil/testutil.go
@@ -179,7 +179,6 @@ func SetupContainerInRoot(rootDir string, spec *specs.Spec, conf *boot.Config) (
}
conf.RootDir = rootDir
- conf.SpecFile = filepath.Join(bundleDir, "config.json")
return bundleDir, nil
}