diff --git a/src/runtime/virtcontainers/container.go b/src/runtime/virtcontainers/container.go index 56967defc2db..3ba9435e6404 100644 --- a/src/runtime/virtcontainers/container.go +++ b/src/runtime/virtcontainers/container.go @@ -472,18 +472,36 @@ func (c *Container) shareFiles(ctx context.Context, m Mount, idx int, hostShared } else { // These mounts are created in the shared dir mountDest := filepath.Join(hostMountDir, filename) - if err := bindMount(ctx, m.Source, mountDest, m.ReadOnly, "private"); err != nil { - return "", false, err - } - // Save HostPath mount value into the mount list of the container. - c.mounts[idx].HostPath = mountDest - // bindmount remount event is not propagated to mount subtrees, so we have to remount the shared dir mountpoint directly. - if m.ReadOnly { - mountDest = filepath.Join(hostSharedDir, filename) - if err := remountRo(c.ctx, mountDest); err != nil { + if !m.ReadOnly { + if err := bindMount(c.ctx, m.Source, mountDest, false, "private"); err != nil { return "", false, err } + } else { + // For RO mounts, bindmount remount event is not propagated to mount subtrees, + // and it doesn't present in the virtiofsd standalone mount namespace either. + // So we end up a bit tricky: + // 1. make a private bind mount to the mount source + // 2. make another ro bind mount on the private mount + // 3. move the ro bind mount to mountDest + // 4. umount the private bind mount created in step 1 + privateDest := filepath.Join(getPrivatePath(c.sandboxID), filename) + if err := bindMount(c.ctx, m.Source, privateDest, false, "private"); err != nil { + return "", false, err + } + defer func() { + syscall.Unmount(privateDest, syscall.MNT_DETACH|UmountNoFollow) + }() + if err := bindMount(c.ctx, privateDest, privateDest, true, "private"); err != nil { + return "", false, err + } + if err := moveMount(c.ctx, privateDest, mountDest); err != nil { + return "", false, err + } + + syscall.Unmount(privateDest, syscall.MNT_DETACH|UmountNoFollow) } + // Save HostPath mount value into the mount list of the container. + c.mounts[idx].HostPath = mountDest } return guestDest, false, nil diff --git a/src/runtime/virtcontainers/kata_agent.go b/src/runtime/virtcontainers/kata_agent.go index 5781f58ee99e..33001a035055 100644 --- a/src/runtime/virtcontainers/kata_agent.go +++ b/src/runtime/virtcontainers/kata_agent.go @@ -152,9 +152,10 @@ var kataHostSharedDir = func() string { } // Shared path handling: -// 1. create two directories for each sandbox: +// 1. create three directories for each sandbox: // -. /run/kata-containers/shared/sandboxes/$sbx_id/mounts/, a directory to hold all host/guest shared mounts // -. /run/kata-containers/shared/sandboxes/$sbx_id/shared/, a host/guest shared directory (9pfs/virtiofs source dir) +// -. /run/kata-containers/shared/sandboxes/$sbx_id/private/, a directory to hold all temporary private mounts when creating ro mounts // // 2. /run/kata-containers/shared/sandboxes/$sbx_id/mounts/ is bind mounted readonly to /run/kata-containers/shared/sandboxes/$sbx_id/shared/, so guest cannot modify it // @@ -167,6 +168,10 @@ func getMountPath(id string) string { return filepath.Join(kataHostSharedDir(), id, "mounts") } +func getPrivatePath(id string) string { + return filepath.Join(kataHostSharedDir(), id, "private") +} + func getSandboxPath(id string) string { return filepath.Join(kataHostSharedDir(), id) } diff --git a/src/runtime/virtcontainers/mount.go b/src/runtime/virtcontainers/mount.go index 2a402bfa5443..05de15754415 100644 --- a/src/runtime/virtcontainers/mount.go +++ b/src/runtime/virtcontainers/mount.go @@ -212,6 +212,42 @@ func isDeviceMapper(major, minor int) (bool, error) { const mountPerm = os.FileMode(0755) +func evalMountPath(source, destination string) (string, string, error) { + if source == "" { + return "", "", fmt.Errorf("source must be specified") + } + if destination == "" { + return "", "", fmt.Errorf("destination must be specified") + } + + absSource, err := filepath.EvalSymlinks(source) + if err != nil { + return "", "", fmt.Errorf("Could not resolve symlink for source %v", source) + } + + if err := ensureDestinationExists(absSource, destination); err != nil { + return "", "", fmt.Errorf("Could not create destination mount point %v: %v", destination, err) + } + + return absSource, destination, nil +} + +// moveMount moves a mountpoint to another path with some bookkeeping: +// * evaluate all symlinks +// * ensure the source exists +// * recursively create the destination +func moveMount(ctx context.Context, source, destination string) error { + span, _ := trace(ctx, "moveMount") + defer span.End() + + source, destination, err := evalMountPath(source, destination) + if err != nil { + return err + } + + return syscall.Mount(source, destination, "move", syscall.MS_MOVE, "") +} + // bindMount bind mounts a source in to a destination. This will // do some bookkeeping: // * evaluate all symlinks @@ -222,20 +258,9 @@ func bindMount(ctx context.Context, source, destination string, readonly bool, p span, _ := trace(ctx, "bindMount") defer span.End() - if source == "" { - return fmt.Errorf("source must be specified") - } - if destination == "" { - return fmt.Errorf("destination must be specified") - } - - absSource, err := filepath.EvalSymlinks(source) + absSource, destination, err := evalMountPath(source, destination) if err != nil { - return fmt.Errorf("Could not resolve symlink for source %v", source) - } - - if err := ensureDestinationExists(absSource, destination); err != nil { - return fmt.Errorf("Could not create destination mount point %v: %v", destination, err) + return err } if err := syscall.Mount(absSource, destination, "bind", syscall.MS_BIND, ""); err != nil {