mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Re-create mount points during restore
Mount points are created during container initialization and may not exist if the container is restored using the original image. Mark these directories and recreate them upon restore. PiperOrigin-RevId: 644514801
This commit is contained in:
committed by
gVisor bot
parent
6d021e7eb2
commit
5734bf093c
@@ -21,6 +21,7 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/context"
|
||||
"gvisor.dev/gvisor/pkg/errors/linuxerr"
|
||||
"gvisor.dev/gvisor/pkg/fsutil"
|
||||
"gvisor.dev/gvisor/pkg/lisafs"
|
||||
"gvisor.dev/gvisor/pkg/log"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
|
||||
"gvisor.dev/gvisor/pkg/sentry/vfs"
|
||||
@@ -531,19 +532,49 @@ func (fs *filesystem) restoreRoot(ctx context.Context, opts *vfs.CompleteRestore
|
||||
func (d *dentry) restoreFile(ctx context.Context, opts *vfs.CompleteRestoreOptions) error {
|
||||
switch dt := d.impl.(type) {
|
||||
case *lisafsDentry:
|
||||
inode, err := d.parent.Load().impl.(*lisafsDentry).controlFD.Walk(ctx, d.name)
|
||||
controlFD := d.parent.Load().impl.(*lisafsDentry).controlFD
|
||||
inode, err := controlFD.Walk(ctx, d.name)
|
||||
if err != nil {
|
||||
return err
|
||||
if !dt.isDir() || !dt.forMountpoint {
|
||||
return err
|
||||
}
|
||||
|
||||
// Recreate directories that were created during volume mounting, since
|
||||
// during restore we don't attempt to remount them.
|
||||
inode, err = controlFD.MkdirAt(ctx, d.name, linux.FileMode(d.mode.Load()), lisafs.UID(d.uid.Load()), lisafs.GID(d.gid.Load()))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return dt.restoreFile(ctx, &inode, opts)
|
||||
|
||||
case *directfsDentry:
|
||||
controlFD := d.parent.Load().impl.(*directfsDentry).controlFD
|
||||
childFD, err := tryOpen(func(flags int) (int, error) {
|
||||
return unix.Openat(d.parent.Load().impl.(*directfsDentry).controlFD, d.name, flags, 0)
|
||||
n, err := unix.Openat(controlFD, d.name, flags, 0)
|
||||
return n, err
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
if !dt.isDir() || !dt.forMountpoint {
|
||||
return err
|
||||
}
|
||||
|
||||
// Recreate directories that were created during volume mounting, since
|
||||
// during restore we don't attempt to remount them.
|
||||
if err := unix.Mkdirat(controlFD, d.name, d.mode.Load()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Try again...
|
||||
childFD, err = tryOpen(func(flags int) (int, error) {
|
||||
return unix.Openat(controlFD, d.name, flags, 0)
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return dt.restoreFile(ctx, childFD, opts)
|
||||
|
||||
default:
|
||||
panic("unknown dentry implementation")
|
||||
}
|
||||
|
||||
@@ -842,6 +842,7 @@ func (fs *filesystem) MkdirAt(ctx context.Context, rp *vfs.ResolvingPath, opts v
|
||||
if fs.opts.interop != InteropModeShared {
|
||||
parent.incLinks()
|
||||
}
|
||||
child.forMountpoint = opts.ForSyntheticMountpoint
|
||||
return child, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -996,6 +996,11 @@ type dentry struct {
|
||||
// a more in-depth discussion on this matter).
|
||||
watches vfs.Watches
|
||||
|
||||
// forMountpoint marks directories that were created for mount points during
|
||||
// container startup. This is used during restore, in case these mount points
|
||||
// need to be recreated.
|
||||
forMountpoint bool
|
||||
|
||||
// impl is the specific dentry implementation for non-synthetic dentries.
|
||||
// impl is immutable.
|
||||
//
|
||||
|
||||
@@ -1278,6 +1278,89 @@ func TestCheckpointRestoreExecKilled(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestCheckpointRestoreCreateMountPoint tests that mountpoints created during
|
||||
// container creation are re-created after checkpoint/restore.
|
||||
func TestCheckpointRestoreCreateMountPoint(t *testing.T) {
|
||||
dir, err := os.MkdirTemp(testutil.TmpDir(), "checkpoint-test")
|
||||
if err != nil {
|
||||
t.Fatalf("os.MkdirTemp() failed: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(dir)
|
||||
if err := os.Chmod(dir, 0777); err != nil {
|
||||
t.Fatalf("error chmoding file: %q, %v", dir, err)
|
||||
}
|
||||
|
||||
spec, conf := sleepSpecConf(t)
|
||||
|
||||
mountDest := filepath.Join(dir, "/foo-dir")
|
||||
spec.Mounts = append(spec.Mounts, specs.Mount{
|
||||
Destination: mountDest,
|
||||
Type: "tmpfs",
|
||||
})
|
||||
|
||||
_, bundleDir, cleanup, err := testutil.SetupContainer(spec, conf)
|
||||
if err != nil {
|
||||
t.Fatalf("error setting up container: %v", err)
|
||||
}
|
||||
defer cleanup()
|
||||
|
||||
// Create and start the container.
|
||||
args := Args{
|
||||
ID: testutil.RandomContainerID(),
|
||||
Spec: spec,
|
||||
BundleDir: bundleDir,
|
||||
}
|
||||
cont, err := New(conf, args)
|
||||
if err != nil {
|
||||
t.Fatalf("error creating container: %v", err)
|
||||
}
|
||||
defer cont.Destroy()
|
||||
if err := cont.Start(conf); err != nil {
|
||||
t.Fatalf("error starting container: %v", err)
|
||||
}
|
||||
if err := waitForProcessCount(cont, 1); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Check that mount point was created.
|
||||
if ws, err := execute(conf, cont, "/usr/bin/test", "-d", mountDest); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if ws != 0 {
|
||||
t.Fatalf("directory was not re-created upon restore, ws: %v", ws)
|
||||
}
|
||||
|
||||
// Checkpoint running container; save state into new file.
|
||||
if err := cont.Checkpoint(dir, false, statefile.Options{Compression: statefile.CompressionLevelDefault}, pgalloc.SaveOpts{}); err != nil {
|
||||
t.Fatalf("error checkpointing container to file: %v", err)
|
||||
}
|
||||
|
||||
// Remove directory created by the container.
|
||||
if err := os.RemoveAll(mountDest); err != nil {
|
||||
t.Fatalf("error removing mount point directory: %v", err)
|
||||
}
|
||||
|
||||
// Destroy the original container to restore it in place.
|
||||
cont.Destroy()
|
||||
cont = nil
|
||||
|
||||
cont2, err := New(conf, args)
|
||||
if err != nil {
|
||||
t.Fatalf("error creating container: %v", err)
|
||||
}
|
||||
defer cont2.Destroy()
|
||||
|
||||
if err := cont2.Restore(conf, dir, false /* direct */); err != nil {
|
||||
t.Fatalf("error restoring container: %v", err)
|
||||
}
|
||||
|
||||
// Check that mount point was re-created after restore.
|
||||
if ws, err := execute(conf, cont2, "/usr/bin/test", "-d", mountDest); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if ws != 0 {
|
||||
t.Fatalf("directory was not re-created upon restore, ws: %v", ws)
|
||||
}
|
||||
}
|
||||
|
||||
// TestUnixDomainSockets checks that Checkpoint/Restore works in cases
|
||||
// with filesystem Unix Domain Socket use.
|
||||
func TestUnixDomainSockets(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user