Beispiel #1
0
// MountGC removes mounts from pods that couldn't be GCed cleanly.
func MountGC(path, uuid string) error {
	mnts, err := mountinfo.ParseMounts(0)
	if err != nil {
		return errwrap.Wrap(fmt.Errorf("error getting mounts for pod %s from mountinfo", uuid), err)
	}
	mnts = mnts.Filter(mountinfo.HasPrefix(path))

	for i := len(mnts) - 1; i >= 0; i-- {
		mnt := mnts[i]
		if mnt.NeedsRemountPrivate() {
			if err := syscall.Mount("", mnt.MountPoint, "", syscall.MS_PRIVATE, ""); err != nil {
				return errwrap.Wrap(fmt.Errorf("could not remount at %v", mnt.MountPoint), err)
			}
		}
	}

	for _, mnt := range mnts {
		if err := syscall.Unmount(mnt.MountPoint, 0); err != nil {
			if err != syscall.ENOENT && err != syscall.EINVAL {
				return errwrap.Wrap(fmt.Errorf("could not unmount %v", mnt.MountPoint), err)
			}
		}
	}
	return nil
}
Beispiel #2
0
func doBindMount(source, destination string, readOnly bool, recursive *bool) error {
	var flags uintptr = syscall.MS_BIND

	// Enable recursive by default and remove it if explicitly requested
	recursiveBool := recursive == nil || *recursive == true
	if recursiveBool {
		flags |= syscall.MS_REC
	}

	if err := syscall.Mount(source, destination, "bind", flags, ""); err != nil {
		return errwrap.Wrap(fmt.Errorf("error mounting %s", destination), err)
	}

	// Linux can't bind-mount with readonly in a single operation, so remount +ro
	if readOnly {
		if err := syscall.Mount("", destination, "none", syscall.MS_REMOUNT|syscall.MS_RDONLY|syscall.MS_BIND, ""); err != nil {
			return errwrap.Wrap(fmt.Errorf("error remounting read-only %s", destination), err)
		}
	}

	if readOnly && recursiveBool {
		// Sub-mounts are still read-write, so find them and remount them read-only

		mnts, err := mountinfo.ParseMounts(0)
		if err != nil {
			return errwrap.Wrap(fmt.Errorf("error getting mounts under %q from mountinfo", source), err)
		}
		mnts = mnts.Filter(mountinfo.HasPrefix(source + "/"))

		for _, mnt := range mnts {
			innerAbsPath := destination + strings.Replace(mnt.MountPoint, source, "", -1)
			if err := syscall.Mount("", innerAbsPath, "none", syscall.MS_REMOUNT|syscall.MS_RDONLY|syscall.MS_BIND, ""); err != nil {
				return errwrap.Wrap(fmt.Errorf("error remounting child mount %s read-only", innerAbsPath), err)
			}
		}
	}

	return nil
}
Beispiel #3
0
func evaluateMounts(rfs string, app string, p *stage1commontypes.Pod) ([]flyMount, error) {
	namedVolumeMounts := map[types.ACName]volumeMountTuple{}

	// Insert the PodManifest's first RuntimeApp's Mounts
	for _, m := range p.Manifest.Apps[0].Mounts {
		_, exists := namedVolumeMounts[m.Volume]
		if exists {
			return nil, fmt.Errorf("duplicate mount given: %q", m.Volume)
		}
		namedVolumeMounts[m.Volume] = volumeMountTuple{M: m}
		diag.Printf("adding %+v", namedVolumeMounts[m.Volume])
	}

	// Merge command-line Mounts with ImageManifest's MountPoints
	var imAppManifestMPs []types.MountPoint
	if imApp := p.Images[app].App; imApp != nil {
		imAppManifestMPs = imApp.MountPoints
		if err := addMountPoints(namedVolumeMounts, imAppManifestMPs); err != nil {
			return nil, err
		}
	}

	// Merge command-line Mounts with PodManifest's RuntimeApp's App's MountPoints
	raApp := p.Manifest.Apps[0]
	if err := addMountPoints(namedVolumeMounts, raApp.App.MountPoints); err != nil {
		return nil, err
	}

	// Insert the command-line Volumes
	for _, v := range p.Manifest.Volumes {
		// Check if we have a mount for this volume
		tuple, exists := namedVolumeMounts[v.Name]
		if !exists {
			return nil, fmt.Errorf("missing mount for volume %q", v.Name)
		} else if tuple.M.Volume != v.Name {
			// assertion regarding the implementation, should never happen
			return nil, fmt.Errorf("mismatched volume:mount pair: %q != %q", v.Name, tuple.M.Volume)
		}
		namedVolumeMounts[v.Name] = volumeMountTuple{V: v, M: tuple.M}
		diag.Printf("adding %+v", namedVolumeMounts[v.Name])
	}

	// Merge command-line Volumes with ImageManifest's MountPoints
	for _, mp := range imAppManifestMPs {
		// Check if we have a volume for this mountpoint
		tuple, exists := namedVolumeMounts[mp.Name]
		if !exists || tuple.V.Name == "" {
			return nil, fmt.Errorf("missing volume for mountpoint %q", mp.Name)
		}

		// If empty, fill in ReadOnly bit
		if tuple.V.ReadOnly == nil {
			v := tuple.V
			v.ReadOnly = &mp.ReadOnly
			namedVolumeMounts[mp.Name] = volumeMountTuple{M: tuple.M, V: v}
			diag.Printf("adding %+v", namedVolumeMounts[mp.Name])
		}
	}

	// Gather host mounts which we make MS_SHARED if passed as a volume source
	hostMounts := map[string]struct{}{}
	mnts, err := mountinfo.ParseMounts(0)
	if err != nil {
		return nil, errwrap.Wrap(errors.New("can't gather host mounts"), err)
	}
	for _, m := range mnts {
		hostMounts[m.MountPoint] = struct{}{}
	}

	argFlyMounts := []flyMount{}
	for _, tuple := range namedVolumeMounts {
		if _, isHostMount := hostMounts[tuple.V.Source]; isHostMount {
			// Mark the host mount as SHARED so the container's changes to the mount are propagated to the host
			argFlyMounts = append(argFlyMounts,
				flyMount{"", "", tuple.V.Source, "none", syscall.MS_REC | syscall.MS_SHARED},
			)
		}

		var (
			flags     uintptr = syscall.MS_BIND
			recursive         = tuple.V.Recursive != nil && *tuple.V.Recursive
			ro                = tuple.V.ReadOnly != nil && *tuple.V.ReadOnly
		)

		// If Recursive is not set, default to non-recursive.
		if recursive {
			flags |= syscall.MS_REC
		}

		argFlyMounts = append(argFlyMounts,
			flyMount{tuple.V.Source, rfs, tuple.M.Path, "none", flags},
		)

		if ro {
			argFlyMounts = append(argFlyMounts,
				flyMount{"", rfs, tuple.M.Path, "none", flags | syscall.MS_REMOUNT | syscall.MS_RDONLY},
			)

			if recursive {
				// Every sub-mount needs to be remounted read-only separately
				mnts, err := mountinfo.ParseMounts(0)
				if err != nil {
					return nil, errwrap.Wrap(fmt.Errorf("error getting mounts under %q from mountinfo", tuple.V.Source), err)
				}
				mnts = mnts.Filter(mountinfo.HasPrefix(tuple.V.Source + "/"))

				for _, mnt := range mnts {
					innerRelPath := tuple.M.Path + strings.Replace(mnt.MountPoint, tuple.V.Source, "", -1)
					argFlyMounts = append(argFlyMounts,
						flyMount{"", rfs, innerRelPath, "none", flags | syscall.MS_REMOUNT | syscall.MS_RDONLY},
					)
				}
			}
		}
	}
	return argFlyMounts, nil
}
Beispiel #4
0
func runExport(cmd *cobra.Command, args []string) (exit int) {
	if len(args) != 2 {
		cmd.Usage()
		return 254
	}

	outACI := args[1]
	ext := filepath.Ext(outACI)
	if ext != schema.ACIExtension {
		stderr.Printf("extension must be %s (given %s)", schema.ACIExtension, outACI)
		return 254
	}

	p, err := pkgPod.PodFromUUIDString(getDataDir(), args[0])
	if err != nil {
		stderr.PrintE("problem retrieving pod", err)
		return 254
	}
	defer p.Close()

	state := p.State()
	if state != pkgPod.Exited && state != pkgPod.ExitedGarbage {
		stderr.Print("pod is not exited. Only exited pods can be exported")
		return 254
	}

	app, err := getApp(p)
	if err != nil {
		stderr.PrintE("unable to find app", err)
		return 254
	}

	root := common.AppPath(p.Path(), app.Name)
	manifestPath := filepath.Join(common.AppInfoPath(p.Path(), app.Name), aci.ManifestFile)
	if p.UsesOverlay() {
		tmpDir := filepath.Join(getDataDir(), "tmp")
		if err := os.MkdirAll(tmpDir, common.DefaultRegularDirPerm); err != nil {
			stderr.PrintE("unable to create temp directory", err)
			return 254
		}
		podDir, err := ioutil.TempDir(tmpDir, fmt.Sprintf("rkt-export-%s", p.UUID))
		if err != nil {
			stderr.PrintE("unable to create export temp directory", err)
			return 254
		}
		defer func() {
			if err := os.RemoveAll(podDir); err != nil {
				stderr.PrintE("problem removing temp directory", err)
				exit = 1
			}
		}()
		mntDir := filepath.Join(podDir, "rootfs")
		if err := os.Mkdir(mntDir, common.DefaultRegularDirPerm); err != nil {
			stderr.PrintE("unable to create rootfs directory inside temp directory", err)
			return 254
		}

		if err := mountOverlay(p, app, mntDir); err != nil {
			stderr.PrintE(fmt.Sprintf("couldn't mount directory at %s", mntDir), err)
			return 254
		}
		defer func() {
			if err := syscall.Unmount(mntDir, 0); err != nil {
				stderr.PrintE(fmt.Sprintf("error unmounting directory %s", mntDir), err)
				exit = 1
			}
		}()
		root = podDir
	} else {
		// trailing filepath separator so we don't match the appRootfs path
		appRootfs := common.AppRootfsPath(p.Path(), app.Name) + string(filepath.Separator)
		mnts, err := mountinfo.ParseMounts(0)
		if err != nil {
			stderr.PrintE("error parsing mountpoints", err)
			return 254
		}
		mnts = mnts.Filter(mountinfo.HasPrefix(appRootfs))
		if len(mnts) > 0 {
			stderr.Printf("pod has remaining mountpoints. Only pods using overlayfs or with no mountpoints can be exported")
			return 254
		}
	}

	// Check for user namespace (--private-user), if in use get uidRange
	var uidRange *user.UidRange
	privUserFile := filepath.Join(p.Path(), common.PrivateUsersPreparedFilename)
	privUserContent, err := ioutil.ReadFile(privUserFile)
	if err == nil {
		uidRange = user.NewBlankUidRange()
		// The file was found, save uid & gid shift and count
		if err := uidRange.Deserialize(privUserContent); err != nil {
			stderr.PrintE(fmt.Sprintf("problem deserializing the content of %s", common.PrivateUsersPreparedFilename), err)
			return 254
		}
	}

	if err = buildAci(root, manifestPath, outACI, uidRange); err != nil {
		stderr.PrintE("error building aci", err)
		return 254
	}
	return 0
}