Example #1
0
// AppToSystemdMountUnits prepare bind mount unit for empty or host kind mounting
// between stage1 rootfs and chrooted filesystem for application
func AppToSystemdMountUnits(root string, appName types.ACName, volumes []types.Volume, ra *schema.RuntimeApp, unitsDir string) error {
	app := ra.App

	vols := make(map[types.ACName]types.Volume)
	for _, v := range volumes {
		vols[v.Name] = v
	}

	mounts, err := initcommon.GenerateMounts(ra, vols)
	if err != nil {
		return err
	}

	for _, m := range mounts {
		vol := vols[m.Volume]

		// source relative to stage1 rootfs to relative pod root
		whatPath := filepath.Join(stage1MntDir, vol.Name.String())
		whatFullPath := filepath.Join(root, whatPath)

		// destination relative to stage1 rootfs and relative to pod root
		wherePath := filepath.Join(common.RelAppRootfsPath(appName), m.Path)
		whereFullPath := filepath.Join(root, wherePath)

		// assertion to make sure that "what" exists (created earlier by PodToSystemdHostMountUnits)
		log.Printf("checking required source path: %q", whatFullPath)
		if _, err := os.Stat(whatFullPath); os.IsNotExist(err) {
			return fmt.Errorf("bug: missing source for volume %v", vol.Name)
		}

		// optionally prepare app directory
		log.Printf("optionally preparing destination path: %q", whereFullPath)
		err := os.MkdirAll(whereFullPath, 0700)
		if err != nil {
			return fmt.Errorf("failed to prepare dir for mount %v: %v", m.Volume, err)
		}

		// install new mount unit for bind mount /mnt/volumeName -> /opt/stage2/{app-id}/rootfs/{{mountPoint.Path}}
		mu, err := installNewMountUnit(
			root,      // where put a mount unit
			whatPath,  // what - stage1 rootfs /mnt/VolumeName
			wherePath, // where - inside chroot app filesystem
			"bind",    // fstype
			"bind",    // options
			serviceUnitName(appName),
			unitsDir,
		)
		if err != nil {
			return fmt.Errorf("cannot install new mount unit for app %q: %v", appName.String(), err)
		}

		// TODO(iaguis) when we update util-linux to 2.27, this code can go
		// away and we can bind-mount RO with one unit file.
		// http://ftp.kernel.org/pub/linux/utils/util-linux/v2.27/v2.27-ReleaseNotes
		if initcommon.IsMountReadOnly(vol, app.MountPoints) {
			opts := []*unit.UnitOption{
				unit.NewUnitOption("Unit", "Description", fmt.Sprintf("Remount read-only unit for %s", wherePath)),
				unit.NewUnitOption("Unit", "DefaultDependencies", "false"),
				unit.NewUnitOption("Unit", "After", mu),
				unit.NewUnitOption("Unit", "Wants", mu),
				unit.NewUnitOption("Service", "ExecStart", fmt.Sprintf("/usr/bin/mount -o remount,ro %s", wherePath)),
				unit.NewUnitOption("Install", "RequiredBy", mu),
			}

			remountUnitPath := filepath.Join(root, unitsDir, unit.UnitNamePathEscape(wherePath+"-remount.service"))
			if err := writeUnit(opts, remountUnitPath); err != nil {
				return err
			}
		}
	}
	return nil
}
Example #2
0
File: kvm.go Project: nhlfr/rkt
func mountSharedVolumes(root string, p *stage1commontypes.Pod, ra *schema.RuntimeApp) error {
	app := ra.App
	appName := ra.Name
	volumes := p.Manifest.Volumes
	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)
	}

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

		absRoot, err := filepath.Abs(p.Root) // Absolute path to the pod's rootfs.
		if err != nil {
			return errwrap.Wrap(errors.New("could not get pod's root absolute path"), err)
		}

		absAppRootfs := common.AppRootfsPath(absRoot, appName)
		if err != nil {
			return fmt.Errorf(`could not evaluate absolute path for application rootfs in app: %v`, appName)
		}

		mntPath, err := stage1initcommon.EvaluateSymlinksInsideApp(absAppRootfs, m.Path)
		if err != nil {
			return errwrap.Wrap(fmt.Errorf("could not evaluate path %v", m.Path), err)
		}
		absDestination := filepath.Join(absAppRootfs, mntPath)
		shPath := filepath.Join(sharedVolPath, vol.Name.String())
		if err := stage1initcommon.PrepareMountpoints(shPath, absDestination, &vol, m.DockerImplicit); err != nil {
			return 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)
		}
		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, vol.Recursive); err != nil {
			return errwrap.Wrap(fmt.Errorf("could not bind mount path %v (s: %v, d: %v)", m.Path, source, absDestination), err)
		}
	}
	return nil
}
Example #3
0
File: pod.go Project: aaronlevy/rkt
// appToNspawnArgs transforms the given app manifest, with the given associated
// app name, into a subset of applicable systemd-nspawn argument
func (p *Pod) appToNspawnArgs(ra *schema.RuntimeApp) ([]string, error) {
	var args []string
	appName := ra.Name
	app := ra.App

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

	vols := make(map[types.ACName]types.Volume)
	for _, v := range p.Manifest.Volumes {
		vols[v.Name] = v
	}

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

		if vol.Kind == "empty" {
			if err := os.MkdirAll(filepath.Join(sharedVolPath, vol.Name.String()), sharedVolPerm); err != nil {
				return nil, fmt.Errorf("could not create shared volume %q: %v", vol.Name, err)
			}
		}

		opt := make([]string, 4)

		if initcommon.IsMountReadOnly(vol, app.MountPoints) {
			opt[0] = "--bind-ro="
		} else {
			opt[0] = "--bind="
		}

		switch vol.Kind {
		case "host":
			opt[1] = vol.Source
		case "empty":
			absRoot, err := filepath.Abs(p.Root)
			if err != nil {
				return nil, fmt.Errorf("cannot get pod's root absolute path: %v\n", err)
			}
			opt[1] = filepath.Join(common.SharedVolumesPath(absRoot), vol.Name.String())
		default:
			return nil, fmt.Errorf(`invalid volume kind %q. Must be one of "host" or "empty".`, vol.Kind)
		}
		opt[2] = ":"
		opt[3] = filepath.Join(common.RelAppRootfsPath(appName), m.Path)

		args = append(args, strings.Join(opt, ""))
	}

	for _, i := range app.Isolators {
		switch v := i.Value().(type) {
		case types.LinuxCapabilitiesSet:
			var caps []string
			// TODO: cleanup the API on LinuxCapabilitiesSet to give strings easily.
			for _, c := range v.Set() {
				caps = append(caps, string(c))
			}
			if i.Name == types.LinuxCapabilitiesRetainSetName {
				capList := strings.Join(caps, ",")
				args = append(args, "--capability="+capList)
			}
		}
	}

	return args, nil
}
Example #4
0
func mountSharedVolumes(root string, p *stage1commontypes.Pod, ra *schema.RuntimeApp) error {
	app := ra.App
	appName := ra.Name
	volumes := p.Manifest.Volumes
	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)
	}

	imageManifest := p.Images[appName.String()]
	mounts := stage1initcommon.GenerateMounts(ra, vols, imageManifest)
	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
}