Move vfs.RestoreID to checkpoint.ResourceID.

... and use it everywhere in preference to its string. This is to be able to
reliably inspect pgalloc.MemoryFile.ResourceID().Path in cl/879147587.

PiperOrigin-RevId: 883274090
This commit is contained in:
Jamie Liu
2026-03-13 12:02:56 -07:00
committed by gVisor bot
parent cc3e875004
commit 84bc4e3f8f
29 changed files with 161 additions and 92 deletions
+11
View File
@@ -0,0 +1,11 @@
load("//tools:defs.bzl", "go_library")
package(default_applicable_licenses = ["//:license"])
licenses(["notice"])
go_library(
name = "checkpoint",
srcs = ["checkpoint.go"],
visibility = ["//pkg/sentry:internal"],
)
+42
View File
@@ -0,0 +1,42 @@
// Copyright 2026 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package checkpoint defines types that are used by multiple forms of gVisor
// checkpoints.
package checkpoint
import (
"fmt"
)
// ResourceID is a unique ID that is used to identify resources between save/restore sessions.
// Examples of resources are host files, gofer connection for mount points, etc.
//
// +stateify savable
type ResourceID struct {
// ContainerName is the name of the container that the resource belongs to.
ContainerName string `json:"container_name"`
// Path is the path of the resource. Path is never empty for a valid
// ResourceID.
Path string `json:"path"`
}
// Ok returns true if the ResourceID is valid and not the zero value.
func (id ResourceID) Ok() bool {
return id.Path != ""
}
func (id ResourceID) String() string {
return fmt.Sprintf("%s:%s", id.ContainerName, id.Path)
}
+1 -1
View File
@@ -77,7 +77,7 @@ func Import(ctx context.Context, fdTable *kernel.FDTable, fds map[int]*fd.FD, op
}
var appFile *vfs.FileDescription
fdOpts.RestoreKey = host.MakeRestoreID(opts.ContainerName, appFD)
fdOpts.RestoreKey = host.MakeResourceID(opts.ContainerName, appFD)
if opts.Console && appFD < 3 {
// Import the file as a host TTY file.
if ttyFile == nil {
+1
View File
@@ -65,6 +65,7 @@ go_library(
"//pkg/log",
"//pkg/refs",
"//pkg/safemem",
"//pkg/sentry/checkpoint",
"//pkg/sentry/fsimpl/lock",
"//pkg/sentry/fsutil",
"//pkg/sentry/kernel/auth",
+4 -3
View File
@@ -26,6 +26,7 @@ import (
"gvisor.dev/gvisor/pkg/context"
"gvisor.dev/gvisor/pkg/erofs"
"gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sentry/checkpoint"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
"gvisor.dev/gvisor/pkg/sentry/memmap"
"gvisor.dev/gvisor/pkg/sentry/vfs"
@@ -82,9 +83,9 @@ type filesystem struct {
//
// +stateify savable
type InternalFilesystemOptions struct {
// If UniqueID is non-empty, it is an opaque string used to reassociate the
// filesystem with a new image FD during restoration from checkpoint.
UniqueID vfs.RestoreID
// If UniqueID is non-empty, it is used to reassociate the filesystem with
// a new image FD during restoration from checkpoint.
UniqueID checkpoint.ResourceID
}
// Name implements vfs.FilesystemType.Name.
+1
View File
@@ -108,6 +108,7 @@ go_library(
"//pkg/rand",
"//pkg/refs",
"//pkg/safemem",
"//pkg/sentry/checkpoint",
"//pkg/sentry/fsimpl/host",
"//pkg/sentry/fsimpl/lock",
"//pkg/sentry/fsmetric",
+4 -3
View File
@@ -58,6 +58,7 @@ import (
"gvisor.dev/gvisor/pkg/lisafs"
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/pkg/refs"
"gvisor.dev/gvisor/pkg/sentry/checkpoint"
fslock "gvisor.dev/gvisor/pkg/sentry/fsimpl/lock"
"gvisor.dev/gvisor/pkg/sentry/fsutil"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
@@ -410,9 +411,9 @@ const (
//
// +stateify savable
type InternalFilesystemOptions struct {
// If UniqueID is non-empty, it is an opaque string used to reassociate the
// filesystem with a new server FD during restoration from checkpoint.
UniqueID vfs.RestoreID
// If UniqueID is non-empty, it is used to reassociate the filesystem with
// a new server FD during restoration from checkpoint.
UniqueID checkpoint.ResourceID
// If LeakConnection is true, do not close the connection to the server
// when the Filesystem is released. This is necessary for deployments in
+1
View File
@@ -42,6 +42,7 @@ go_library(
"//pkg/refs",
"//pkg/safemem",
"//pkg/sentry/arch",
"//pkg/sentry/checkpoint",
"//pkg/sentry/fsimpl/eventfd",
"//pkg/sentry/fsimpl/kernfs",
"//pkg/sentry/fsimpl/lock",
+4 -3
View File
@@ -30,6 +30,7 @@ import (
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/checkpoint"
"gvisor.dev/gvisor/pkg/sentry/fsimpl/eventfd"
"gvisor.dev/gvisor/pkg/sentry/fsimpl/kernfs"
"gvisor.dev/gvisor/pkg/sentry/fsutil"
@@ -117,7 +118,7 @@ type inode struct {
hostFD int `state:"nosave"`
// restoreKey is used to identify the `hostFD` after a restore is performed.
restoreKey vfs.RestoreID
restoreKey checkpoint.ResourceID
// ino is an inode number unique within this filesystem.
//
@@ -194,7 +195,7 @@ type inode struct {
termios linux.KernelTermios
}
func newInode(ctx context.Context, fs *filesystem, hostFD int, savable bool, restoreKey vfs.RestoreID, fileType linux.FileMode, isTTY bool, readonly bool) (*inode, error) {
func newInode(ctx context.Context, fs *filesystem, hostFD int, savable bool, restoreKey checkpoint.ResourceID, fileType linux.FileMode, isTTY bool, readonly bool) (*inode, error) {
// Determine if hostFD is seekable.
_, err := unix.Seek(hostFD, 0, linux.SEEK_CUR)
seekable := !linuxerr.Equals(linuxerr.ESPIPE, err)
@@ -249,7 +250,7 @@ type NewFDOptions struct {
// RestoreKey is only used when Savable==true. It uniquely identifies the
// host FD so that a mapping to the corresponding FD can be provided during
// restore.
RestoreKey vfs.RestoreID
RestoreKey checkpoint.ResourceID
// Restorable is true if hostFD may be restored. This can be set to false
// for host FDs that are not going to be present after restore.
+8 -6
View File
@@ -24,16 +24,18 @@ import (
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/pkg/safemem"
"gvisor.dev/gvisor/pkg/sentry/checkpoint"
"gvisor.dev/gvisor/pkg/sentry/hostfd"
"gvisor.dev/gvisor/pkg/sentry/vfs"
)
// MakeRestoreID creates a RestoreID for a given application FD. The application
// FD remains the same between restores, e.g. stdout=2 before and after restore,
// but the host FD that is maps to can change between restores. This ID is used
// to map application FDs to their respective FD after a restore happens.
func MakeRestoreID(containerName string, fd int) vfs.RestoreID {
return vfs.RestoreID{
// MakeResourceID creates a ResourceID for a given application FD. The
// application FD remains the same between restores, e.g. stdout=2 before and
// after restore, but the host FD that is maps to can change between restores.
// This ID is used to map application FDs to their respective FD after a
// restore happens.
func MakeResourceID(containerName string, fd int) checkpoint.ResourceID {
return checkpoint.ResourceID{
ContainerName: containerName,
Path: fmt.Sprintf("host:%d", fd),
}
+4
View File
@@ -98,6 +98,9 @@ go_library(
"tar.go",
"tmpfs.go",
],
imports = [
"gvisor.dev/gvisor/pkg/sentry/checkpoint",
],
visibility = ["//pkg/sentry:internal"],
deps = [
"//pkg/abi/linux",
@@ -112,6 +115,7 @@ go_library(
"//pkg/refs",
"//pkg/safemem",
"//pkg/sentry/arch",
"//pkg/sentry/checkpoint",
"//pkg/sentry/fsimpl/lock",
"//pkg/sentry/fsmetric",
"//pkg/sentry/fsutil",
+12 -11
View File
@@ -19,6 +19,7 @@ import (
"fmt"
"gvisor.dev/gvisor/pkg/context"
"gvisor.dev/gvisor/pkg/sentry/checkpoint"
"gvisor.dev/gvisor/pkg/sentry/pgalloc"
"gvisor.dev/gvisor/pkg/sentry/vfs"
)
@@ -26,16 +27,16 @@ import (
var _ vfs.FilesystemImplSaveRestoreExtension = (*filesystem)(nil)
// saveMf is called by stateify.
func (fs *filesystem) saveMf() string {
func (fs *filesystem) saveMf() checkpoint.ResourceID {
if !fs.mf.IsSavable() {
panic(fmt.Sprintf("Can't save tmpfs filesystem because its MemoryFile is not savable: %v", fs.mf))
}
return fs.mf.RestoreID()
return fs.mf.ResourceID()
}
// loadMf is called by stateify.
func (fs *filesystem) loadMf(ctx goContext.Context, restoreID string) {
if restoreID == "" {
func (fs *filesystem) loadMf(ctx goContext.Context, resourceID checkpoint.ResourceID) {
if !resourceID.Ok() {
fs.mf = pgalloc.MemoryFileFromContext(ctx)
return
}
@@ -43,9 +44,9 @@ func (fs *filesystem) loadMf(ctx goContext.Context, restoreID string) {
if mfmap == nil {
panic("CtxMemoryFileMap was not provided")
}
mf, ok := mfmap[restoreID]
mf, ok := mfmap[resourceID]
if !ok {
panic(fmt.Sprintf("Memory file for %q not found in CtxMemoryFileMap", restoreID))
panic(fmt.Sprintf("Memory file for %q not found in CtxMemoryFileMap", resourceID))
}
fs.mf = mf
}
@@ -62,18 +63,18 @@ func (d *dentry) loadParent(_ goContext.Context, parent *dentry) {
// PrepareSave implements vfs.FilesystemImplSaveRestoreExtension.PrepareSave.
func (fs *filesystem) PrepareSave(ctx context.Context) error {
restoreID := fs.mf.RestoreID()
if restoreID == "" {
resourceID := fs.mf.ResourceID()
if !resourceID.Ok() {
return nil
}
mfmap := pgalloc.MemoryFileMapFromContext(ctx)
if mfmap == nil {
return fmt.Errorf("CtxMemoryFileMap was not provided")
}
if _, ok := mfmap[restoreID]; ok {
return fmt.Errorf("memory file for %q already exists in CtxMemoryFileMap", restoreID)
if _, ok := mfmap[resourceID]; ok {
return fmt.Errorf("memory file for %q already exists in CtxMemoryFileMap", resourceID)
}
mfmap[restoreID] = fs.mf
mfmap[resourceID] = fs.mf
return nil
}
+4 -4
View File
@@ -67,7 +67,7 @@ type filesystem struct {
// mf is used to allocate memory that stores regular file contents. mf is
// immutable, except it is changed during restore.
mf *pgalloc.MemoryFile `state:".(string)"`
mf *pgalloc.MemoryFile `state:".(checkpoint.ResourceID)"`
// clock is a realtime clock used to set timestamps in file operations.
clock ktime.Clock
@@ -350,9 +350,9 @@ func (fs *filesystem) Release(ctx context.Context) {
fs.ovlWhiteout.inode.decLinksLocked(ctx)
}
fs.mu.Unlock()
if fs.mf.RestoreID() != "" {
// If RestoreID is set, then this is a private MemoryFile which needs to be
// destroyed since this tmpfs is the only user.
if fs.mf.ResourceID().Ok() {
// If ResourceID is set, then this is a private MemoryFile which needs
// to be destroyed since this tmpfs is the only user.
fs.mf.Destroy()
}
}
+1
View File
@@ -360,6 +360,7 @@ go_library(
"//pkg/safemem",
"//pkg/secio",
"//pkg/sentry/arch",
"//pkg/sentry/checkpoint",
"//pkg/sentry/devices/nvproxy/nvconf",
"//pkg/sentry/fdcollector",
"//pkg/sentry/fsimpl/kernfs",
+5 -4
View File
@@ -55,6 +55,7 @@ import (
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/pkg/refs"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/checkpoint"
"gvisor.dev/gvisor/pkg/sentry/devices/nvproxy/nvconf"
"gvisor.dev/gvisor/pkg/sentry/fsimpl/nsfs"
"gvisor.dev/gvisor/pkg/sentry/fsimpl/pipefs"
@@ -597,10 +598,10 @@ func (k *Kernel) Init(args InitKernelArgs) error {
// +stateify savable
type privateMemoryFileMetadata struct {
owners []string
owners []checkpoint.ResourceID
}
func savePrivateMFs(ctx context.Context, w io.Writer, mfsToSave map[string]*pgalloc.MemoryFile, mfOpts *pgalloc.SaveOpts) error {
func savePrivateMFs(ctx context.Context, w io.Writer, mfsToSave map[checkpoint.ResourceID]*pgalloc.MemoryFile, mfOpts *pgalloc.SaveOpts) error {
var meta privateMemoryFileMetadata
// Generate the order in which private memory files are saved.
for fsID := range mfsToSave {
@@ -664,7 +665,7 @@ func (k *Kernel) SaveTo(ctx context.Context, stateFile, pagesMetadata io.WriteCl
}
// Capture all private memory files.
mfsToSave := make(map[string]*pgalloc.MemoryFile)
mfsToSave := make(map[checkpoint.ResourceID]*pgalloc.MemoryFile)
vfsCtx := context.WithValue(ctx, pgalloc.CtxMemoryFileMap, mfsToSave)
// Prepare filesystems for saving. This must be done after
// invalidateUnsavableMappings(), since dropping memory mappings may
@@ -769,7 +770,7 @@ func (k *Kernel) BeforeResume(ctx context.Context) {
// pagesFile must be non-nil, saveMemoryFiles takes ownership of both
// pagesMetadata and pagesFile (even if it returns a non-nil error), and
// MemoryFile state will be saved to pagesMetadata and pagesFile.
func (k *Kernel) saveMemoryFiles(ctx context.Context, w io.Writer, pagesMetadata io.WriteCloser, pagesFile stateio.AsyncWriter, mfsToSave map[string]*pgalloc.MemoryFile, appMFExcludeCommittedZeroPages bool) error {
func (k *Kernel) saveMemoryFiles(ctx context.Context, w io.Writer, pagesMetadata io.WriteCloser, pagesFile stateio.AsyncWriter, mfsToSave map[checkpoint.ResourceID]*pgalloc.MemoryFile, appMFExcludeCommittedZeroPages bool) error {
memoryStart := time.Now()
pmw := w
+5 -4
View File
@@ -22,6 +22,7 @@ import (
"gvisor.dev/gvisor/pkg/cleanup"
"gvisor.dev/gvisor/pkg/context"
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/pkg/sentry/checkpoint"
"gvisor.dev/gvisor/pkg/sentry/pgalloc"
"gvisor.dev/gvisor/pkg/sentry/state/stateio"
"gvisor.dev/gvisor/pkg/state"
@@ -210,7 +211,7 @@ func (w *CheckpointWaitable) signal(gen CheckpointGeneration, err error) {
// loadPrivateMemoryFiles loads the private MemoryFiles from mfmap and it reads
// private MemoryFile metadata from `r`. This consumes bytes from `r`, so this
// must be called only once.
func loadPrivateMemoryFiles(ctx context.Context, r io.Reader, mfmap map[string]*pgalloc.MemoryFile, opts *pgalloc.LoadOpts) error {
func loadPrivateMemoryFiles(ctx context.Context, r io.Reader, mfmap map[checkpoint.ResourceID]*pgalloc.MemoryFile, opts *pgalloc.LoadOpts) error {
// Load the metadata.
var meta privateMemoryFileMetadata
if _, err := state.Load(ctx, r, &meta); err != nil {
@@ -258,7 +259,7 @@ func (k *Kernel) loadMemoryFiles(ctx context.Context, r io.Reader) error {
type AsyncMFLoader struct {
// privateMFsChan is used to tell the background goroutine about private
// MemoryFiles, once they are known. This channel is written to exactly once.
privateMFsChan chan map[string]*pgalloc.MemoryFile
privateMFsChan chan map[checkpoint.ResourceID]*pgalloc.MemoryFile
mainMFStartWg sync.WaitGroup
mainMetadataErr error
@@ -279,7 +280,7 @@ type AsyncMFLoader struct {
// pages.
func NewAsyncMFLoader(pagesMetadata io.ReadCloser, pagesFile stateio.AsyncReader, mainMF *pgalloc.MemoryFile, timeline *timing.Timeline) *AsyncMFLoader {
mfl := &AsyncMFLoader{
privateMFsChan: make(chan map[string]*pgalloc.MemoryFile, 1),
privateMFsChan: make(chan map[checkpoint.ResourceID]*pgalloc.MemoryFile, 1),
}
mfl.mainMFStartWg.Add(1)
mfl.metadataWg.Add(1)
@@ -351,7 +352,7 @@ func (mfl *AsyncMFLoader) backgroundGoroutine(pagesMetadata io.ReadCloser, pages
}
// KickoffPrivate notifies the background goroutine of the private MemoryFiles.
func (mfl *AsyncMFLoader) KickoffPrivate(mfmap map[string]*pgalloc.MemoryFile) {
func (mfl *AsyncMFLoader) KickoffPrivate(mfmap map[checkpoint.ResourceID]*pgalloc.MemoryFile) {
mfl.privateMFsChan <- mfmap
}
+4
View File
@@ -147,6 +147,9 @@ go_library(
"vma.go",
"vma_set.go",
],
imports = [
"gvisor.dev/gvisor/pkg/sentry/checkpoint",
],
visibility = ["//pkg/sentry:internal"],
deps = [
"//pkg/abi/linux",
@@ -159,6 +162,7 @@ go_library(
"//pkg/safecopy",
"//pkg/safemem",
"//pkg/sentry/arch",
"//pkg/sentry/checkpoint",
"//pkg/sentry/kernel/auth",
"//pkg/sentry/kernel/futex",
"//pkg/sentry/kernel/shm",
+1 -1
View File
@@ -330,7 +330,7 @@ type pma struct {
// file is the file mapped by this pma. Only pmas for which file is of type
// pgalloc.MemoryFile may be saved. pmas hold a reference to the
// corresponding file range while they exist.
file memmap.File `state:".(string)"`
file memmap.File `state:".(checkpoint.ResourceID)"`
// off is the offset into file at which this pma begins.
off uint64
+7 -6
View File
@@ -19,6 +19,7 @@ import (
"fmt"
"gvisor.dev/gvisor/pkg/context"
"gvisor.dev/gvisor/pkg/sentry/checkpoint"
"gvisor.dev/gvisor/pkg/sentry/pgalloc"
)
@@ -150,7 +151,7 @@ func (v *vma) loadRealPerms(_ goContext.Context, b int) {
}
}
func (p *pma) saveFile() string {
func (p *pma) saveFile() checkpoint.ResourceID {
mf, ok := p.file.(*pgalloc.MemoryFile)
if !ok {
// InvalidateUnsavable should have caused all such pmas to be
@@ -160,18 +161,18 @@ func (p *pma) saveFile() string {
if !mf.IsSavable() {
panic(fmt.Sprintf("Can't save pma because its MemoryFile is not savable: %v", mf))
}
return mf.RestoreID()
return mf.ResourceID()
}
func (p *pma) loadFile(ctx goContext.Context, restoreID string) {
if restoreID == "" {
func (p *pma) loadFile(ctx goContext.Context, resourceID checkpoint.ResourceID) {
if !resourceID.Ok() {
p.file = pgalloc.MemoryFileFromContext(ctx)
return
}
mfmap := pgalloc.MemoryFileMapFromContext(ctx)
mf, ok := mfmap[restoreID]
mf, ok := mfmap[resourceID]
if !ok {
panic(fmt.Sprintf("can't restore pma because its MemoryFile's restore ID %q was not found in CtxMemoryFileMap", restoreID))
panic(fmt.Sprintf("can't restore pma because its MemoryFile's resource ID %q was not found in CtxMemoryFileMap", resourceID))
}
p.file = mf
}
+1
View File
@@ -185,6 +185,7 @@ go_library(
"//pkg/ringdeque",
"//pkg/safemem",
"//pkg/sentry/arch",
"//pkg/sentry/checkpoint",
"//pkg/sentry/hostmm",
"//pkg/sentry/memmap",
"//pkg/sentry/state/stateio",
+4 -2
View File
@@ -16,6 +16,8 @@ package pgalloc
import (
"context"
"gvisor.dev/gvisor/pkg/sentry/checkpoint"
)
// contextID is this package's type for context.Context.Value keys.
@@ -53,9 +55,9 @@ func MemoryCgroupIDFromContext(ctx context.Context) uint32 {
// MemoryFileMapFromContext returns the memory file map used by ctx, or nil if
// no such map exists.
func MemoryFileMapFromContext(ctx context.Context) map[string]*MemoryFile {
func MemoryFileMapFromContext(ctx context.Context) map[checkpoint.ResourceID]*MemoryFile {
if v := ctx.Value(CtxMemoryFileMap); v != nil {
return v.(map[string]*MemoryFile)
return v.(map[checkpoint.ResourceID]*MemoryFile)
}
return nil
}
+4 -3
View File
@@ -38,6 +38,7 @@ import (
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/pkg/safemem"
"gvisor.dev/gvisor/pkg/sentry/checkpoint"
"gvisor.dev/gvisor/pkg/sentry/hostmm"
"gvisor.dev/gvisor/pkg/sentry/memmap"
"gvisor.dev/gvisor/pkg/sentry/usage"
@@ -361,9 +362,9 @@ type MemoryFileOpts struct {
// DiskBackedFile indicates that the MemoryFile is backed by a file on disk.
DiskBackedFile bool
// RestoreID is an opaque string used to reassociate the MemoryFile with its
// replacement during restore.
RestoreID string
// ResourceID is used to reassociate the MemoryFile with its replacement
// during restore.
ResourceID checkpoint.ResourceID
// If ExpectHugepages is true, MemoryFile will expect that the host will
// attempt to back AllocOpts.Huge == true allocations with huge pages. If
+4 -3
View File
@@ -33,6 +33,7 @@ import (
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/pkg/ringdeque"
"gvisor.dev/gvisor/pkg/sentry/checkpoint"
"gvisor.dev/gvisor/pkg/sentry/memmap"
"gvisor.dev/gvisor/pkg/sentry/state/stateio"
"gvisor.dev/gvisor/pkg/sentry/usage"
@@ -57,9 +58,9 @@ func (f *MemoryFile) IsSavable() bool {
return f.savable
}
// RestoreID returns the restore ID for f.
func (f *MemoryFile) RestoreID() string {
return f.opts.RestoreID
// ResourceID returns the resource ID for f.
func (f *MemoryFile) ResourceID() checkpoint.ResourceID {
return f.opts.ResourceID
}
// memoryFileSaved is the subset of MemoryFile that is stored in checkpoints.
+1
View File
@@ -189,6 +189,7 @@ go_library(
"//pkg/refs",
"//pkg/safemem",
"//pkg/sentry/arch",
"//pkg/sentry/checkpoint",
"//pkg/sentry/fsimpl/lock",
"//pkg/sentry/fsmetric",
"//pkg/sentry/kernel/auth",
+6 -5
View File
@@ -18,6 +18,7 @@ import (
goContext "context"
"gvisor.dev/gvisor/pkg/context"
"gvisor.dev/gvisor/pkg/sentry/checkpoint"
)
// contextID is this package's type for context.Context.Value keys.
@@ -30,9 +31,9 @@ const (
// CtxRoot is a Context.Value key for a VFS root.
CtxRoot
// CtxRestoreFilesystemFDMap is a Context.Value key for a map[string]int
// mapping filesystem unique IDs (cf. gofer.InternalFilesystemOptions.UniqueID)
// to host FDs.
// CtxRestoreFilesystemFDMap is a Context.Value key for a
// map[checkpoint.ResourceID]int mapping filesystem unique IDs (cf.
// gofer.InternalFilesystemOptions.UniqueID) to host FDs.
CtxRestoreFilesystemFDMap
)
@@ -49,8 +50,8 @@ func MountNamespaceFromContext(ctx goContext.Context) *MountNamespace {
// RestoreFilesystemFDMapFromContext returns the RestoreFilesystemFDMap used
// by ctx. If ctx is not associated with a RestoreFilesystemFDMap, returns nil.
func RestoreFilesystemFDMapFromContext(ctx goContext.Context) map[RestoreID]int {
fdmap, ok := ctx.Value(CtxRestoreFilesystemFDMap).(map[RestoreID]int)
func RestoreFilesystemFDMapFromContext(ctx goContext.Context) map[checkpoint.ResourceID]int {
fdmap, ok := ctx.Value(CtxRestoreFilesystemFDMap).(map[checkpoint.ResourceID]int)
if !ok {
return nil
}
-15
View File
@@ -179,18 +179,3 @@ func (epi *epollInterest) afterLoad(goContext.Context) {
// EpollInstance.ReadEvents() rechecks their readiness.
epi.waiter.NotifyEvent(waiter.EventMaskFromLinux(epi.mask))
}
// RestoreID is a unique ID that is used to identify resources between save/restore sessions.
// Example of resources are host files, gofer connection for mount points, etc.
//
// +stateify savable
type RestoreID struct {
// ContainerName is the name of the container that the resource belongs to.
ContainerName string
// Path is the path of the resource.
Path string
}
func (f RestoreID) String() string {
return fmt.Sprintf("%s:%s", f.ContainerName, f.Path)
}
+1
View File
@@ -57,6 +57,7 @@ go_library(
"//pkg/refs",
"//pkg/sentry/arch",
"//pkg/sentry/arch:registers_go_proto",
"//pkg/sentry/checkpoint",
"//pkg/sentry/control",
"//pkg/sentry/devices/memdev",
"//pkg/sentry/devices/nvproxy",
+5 -4
View File
@@ -28,6 +28,7 @@ import (
"gvisor.dev/gvisor/pkg/devutil"
"gvisor.dev/gvisor/pkg/fd"
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/pkg/sentry/checkpoint"
"gvisor.dev/gvisor/pkg/sentry/control"
"gvisor.dev/gvisor/pkg/sentry/devices/nvproxy/nvconf"
"gvisor.dev/gvisor/pkg/sentry/fsimpl/host"
@@ -241,8 +242,8 @@ func (r *restorer) restore(l *Loader) error {
})
defer cu.Clean()
fdmap := make(map[vfs.RestoreID]int)
mfmap := make(map[string]*pgalloc.MemoryFile)
fdmap := make(map[checkpoint.ResourceID]int)
mfmap := make(map[checkpoint.ResourceID]*pgalloc.MemoryFile)
for _, cont := range r.containers {
// TODO(b/298078576): Need to process hints here probably
mntr := newContainerMounter(cont, l.k, l.mountHints, l.sharedMounts, l.productName, cont.cid)
@@ -251,11 +252,11 @@ func (r *restorer) restore(l *Loader) error {
}
for i, fd := range cont.stdioFDs {
key := host.MakeRestoreID(cont.containerName, i)
key := host.MakeResourceID(cont.containerName, i)
fdmap[key] = fd.Release()
}
for _, customFD := range cont.passFDs {
key := host.MakeRestoreID(cont.containerName, customFD.guest)
key := host.MakeResourceID(cont.containerName, customFD.guest)
fdmap[key] = customFD.host.FD()
}
}
+15 -14
View File
@@ -36,6 +36,7 @@ import (
"gvisor.dev/gvisor/pkg/fspath"
"gvisor.dev/gvisor/pkg/fsutil"
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/pkg/sentry/checkpoint"
"gvisor.dev/gvisor/pkg/sentry/devices/memdev"
"gvisor.dev/gvisor/pkg/sentry/devices/nvproxy"
"gvisor.dev/gvisor/pkg/sentry/devices/nvproxy/nvconf"
@@ -511,7 +512,7 @@ func (c *containerMounter) createMountNamespace(ctx context.Context, conf *confi
InternalMount: true,
Data: strings.Join(data, ","),
InternalData: gofer.InternalFilesystemOptions{
UniqueID: vfs.RestoreID{
UniqueID: checkpoint.ResourceID{
ContainerName: c.containerName,
Path: "/",
},
@@ -527,7 +528,7 @@ func (c *containerMounter) createMountNamespace(ctx context.Context, conf *confi
InternalMount: true,
Data: fmt.Sprintf("ifd=%d", ioFD),
InternalData: erofs.InternalFilesystemOptions{
UniqueID: vfs.RestoreID{
UniqueID: checkpoint.ResourceID{
ContainerName: c.containerName,
Path: "/",
},
@@ -647,7 +648,7 @@ func (c *containerMounter) configureOverlay(ctx context.Context, conf *config.Co
}
if filestoreFD != nil {
// Create memory file for disk-backed overlays.
mf, err := createPrivateMemoryFile(filestoreFD.ReleaseToFile("overlay-filestore"), vfs.RestoreID{ContainerName: c.containerName, Path: dst})
mf, err := createPrivateMemoryFile(filestoreFD.ReleaseToFile("overlay-filestore"), checkpoint.ResourceID{ContainerName: c.containerName, Path: dst})
if err != nil {
return nil, nil, fmt.Errorf("failed to create memory file for overlay: %v", err)
}
@@ -950,7 +951,7 @@ func getMountNameAndOptions(spec *specs.Spec, conf *config.Config, m *mountInfo,
return "", nil, err
}
if m.filestoreFD != nil {
mf, err := createPrivateMemoryFile(m.filestoreFD.ReleaseToFile("tmpfs-filestore"), vfs.RestoreID{ContainerName: containerName, Path: m.mount.Destination})
mf, err := createPrivateMemoryFile(m.filestoreFD.ReleaseToFile("tmpfs-filestore"), checkpoint.ResourceID{ContainerName: containerName, Path: m.mount.Destination})
if err != nil {
return "", nil, fmt.Errorf("failed to create memory file for tmpfs: %v", err)
}
@@ -975,7 +976,7 @@ func getMountNameAndOptions(spec *specs.Spec, conf *config.Config, m *mountInfo,
}
data = append(data, goferMountData(m.goferFD.Release(), getMountAccessType(conf, m.hint), conf)...)
internalData = gofer.InternalFilesystemOptions{
UniqueID: vfs.RestoreID{
UniqueID: checkpoint.ResourceID{
ContainerName: containerName,
Path: m.mount.Destination,
},
@@ -994,7 +995,7 @@ func getMountNameAndOptions(spec *specs.Spec, conf *config.Config, m *mountInfo,
}
data = []string{fmt.Sprintf("ifd=%d", m.goferFD.Release())}
internalData = erofs.InternalFilesystemOptions{
UniqueID: vfs.RestoreID{
UniqueID: checkpoint.ResourceID{
ContainerName: containerName,
Path: m.mount.Destination,
},
@@ -1052,7 +1053,7 @@ func parseKeyValue(s string) (string, string, bool) {
return strings.TrimSpace(tokens[0]), strings.TrimSpace(tokens[1]), true
}
func createPrivateMemoryFile(file *os.File, restoreID vfs.RestoreID) (*pgalloc.MemoryFile, error) {
func createPrivateMemoryFile(file *os.File, resourceID checkpoint.ResourceID) (*pgalloc.MemoryFile, error) {
mfOpts := pgalloc.MemoryFileOpts{
// Private memory files are usually backed by files on disk. Ideally we
// would confirm with fstatfs(2) but that is prohibited by seccomp.
@@ -1064,7 +1065,7 @@ func createPrivateMemoryFile(file *os.File, restoreID vfs.RestoreID) (*pgalloc.M
// are expected to have performed the work around outside the sandbox.
DisableIMAWorkAround: true,
// Private memory files need to be restored correctly using this ID.
RestoreID: restoreID.String(),
ResourceID: resourceID,
}
return pgalloc.NewMemoryFile(file, mfOpts)
}
@@ -1392,10 +1393,10 @@ func (c *containerMounter) makeMountPoint(
// configureRestore returns an updated context.Context including filesystem
// state used by restore defined by conf.
func (c *containerMounter) configureRestore(fdmap map[vfs.RestoreID]int, mfmap map[string]*pgalloc.MemoryFile) error {
func (c *containerMounter) configureRestore(fdmap map[checkpoint.ResourceID]int, mfmap map[checkpoint.ResourceID]*pgalloc.MemoryFile) error {
// Compare createMountNamespace(); rootfs always consumes a gofer FD and a
// filestore FD is consumed if the rootfs GoferMountConf indicates so.
rootKey := vfs.RestoreID{ContainerName: c.containerName, Path: "/"}
rootKey := checkpoint.ResourceID{ContainerName: c.containerName, Path: "/"}
fdmap[rootKey] = c.goferFDs.remove()
if rootfsConf := c.goferMountConfs[0]; rootfsConf.IsFilestorePresent() {
@@ -1403,7 +1404,7 @@ func (c *containerMounter) configureRestore(fdmap map[vfs.RestoreID]int, mfmap m
if err != nil {
return fmt.Errorf("failed to create private memory file for mount rootfs: %w", err)
}
mfmap[rootKey.String()] = mf
mfmap[rootKey] = mf
}
// prepareMounts() consumes the remaining FDs for submounts.
mounts, err := c.prepareMounts()
@@ -1413,16 +1414,16 @@ func (c *containerMounter) configureRestore(fdmap map[vfs.RestoreID]int, mfmap m
for i := range mounts {
submount := &mounts[i]
if submount.goferFD != nil {
key := vfs.RestoreID{ContainerName: c.containerName, Path: submount.mount.Destination}
key := checkpoint.ResourceID{ContainerName: c.containerName, Path: submount.mount.Destination}
fdmap[key] = submount.goferFD.Release()
}
if submount.filestoreFD != nil {
key := vfs.RestoreID{ContainerName: c.containerName, Path: submount.mount.Destination}
key := checkpoint.ResourceID{ContainerName: c.containerName, Path: submount.mount.Destination}
mf, err := createPrivateMemoryFile(submount.filestoreFD.ReleaseToFile("overlay-filestore"), key)
if err != nil {
return fmt.Errorf("failed to create private memory file for mount %q: %w", submount.mount.Destination, err)
}
mfmap[key.String()] = mf
mfmap[key] = mf
}
}
return nil