// 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 }
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 }
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 }
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 }