Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions internal/gcs-sidecar/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"github.com/Microsoft/hcsshim/internal/copyfile"
"github.com/Microsoft/hcsshim/internal/fsformatter"
"github.com/Microsoft/hcsshim/internal/gcs/prot"
"github.com/Microsoft/hcsshim/internal/guestpath"
hcsschema "github.com/Microsoft/hcsshim/internal/hcs/schema2"
"github.com/Microsoft/hcsshim/internal/log"
oci "github.com/Microsoft/hcsshim/internal/oci"
Expand Down Expand Up @@ -137,6 +138,15 @@ func (b *Bridge) createContainer(req *request) (err error) {
return fmt.Errorf("CreateContainer operation is denied by policy: %w", err)
}

// Create the source directory for each mapped directory if it does not
// already exist. In non-confidential WCOW the host does this for
// sandbox:// mounts by exec'ing `cmd /c mkdir ... & dir ...` inside the
// UVM (see resources_wcow.go:setupMounts), but for confidential, we
// handle this here in the sidecar GCS.
if err := createSandboxMountSourceDirs(ctx, container.MappedDirectories); err != nil {
return fmt.Errorf("failed to create mapped directory source directories: %w", err)
}

commandLine := len(spec.Process.Args) > 0
c := &Container{
id: containerID,
Expand Down Expand Up @@ -262,6 +272,32 @@ func stageDLL(ctx context.Context, srcPath, dstDir string) (bool, error) {
return true, nil
}

// createSandboxMountSourceDirs creates source directories for sandbox
// mounts if they do not already exist.
func createSandboxMountSourceDirs(ctx context.Context, mappedDirectories []hcsschema.MappedDirectory) error {
for _, md := range mappedDirectories {
source := md.HostPath
if strings.EqualFold(source, guestpath.WCOWSandboxMountPath) ||
strings.HasPrefix(strings.ToLower(source), strings.ToLower(guestpath.WCOWSandboxMountPath+`\`)) {

// do this stat rather than call MkdirAll unconditionally,
// since the latter will fail with a source file (not dir)
if _, err := os.Stat(source); err == nil {
log.G(ctx).WithField("source", source).Debug("source of mapped directory mount exists, not creating directories")
continue
} else if !os.IsNotExist(err) {
return fmt.Errorf("failed to stat mapped directory source %q: %w", source, err)
}

if err := os.MkdirAll(source, 0755); err != nil {
return fmt.Errorf("failed to create mapped directory source %q: %w", source, err)
}
log.G(ctx).WithField("source", source).Debug("created mapped directory source directory")
}
}
return nil
}

// processParamEnvToOCIEnv converts an Environment field from ProcessParameters
// (a map from environment variable to value) into an array of environment
// variable assignments (where each is in the form "<variable>=<value>") which
Expand Down
44 changes: 44 additions & 0 deletions internal/gcs-sidecar/handlers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,60 @@ import (
"context"
"encoding/json"
"io"
"os"
"path/filepath"
"strings"
"testing"
"time"

"github.com/Microsoft/go-winio/pkg/guid"
"github.com/Microsoft/hcsshim/internal/gcs/prot"
"github.com/Microsoft/hcsshim/internal/guestpath"
hcsschema "github.com/Microsoft/hcsshim/internal/hcs/schema2"
"github.com/Microsoft/hcsshim/internal/protocol/guestrequest"
"github.com/Microsoft/hcsshim/internal/protocol/guestresource"
"github.com/Microsoft/hcsshim/pkg/securitypolicy"
)

func TestCreateSandboxMountSourceDirs(t *testing.T) {
testRoot := filepath.Join(guestpath.WCOWSandboxMountPath, filepath.Base(t.TempDir()))
if err := os.MkdirAll(testRoot, 0755); err != nil {
t.Fatalf("failed to create test root: %v", err)
}
t.Cleanup(func() { _ = os.RemoveAll(testRoot) })

existingFile := filepath.Join(testRoot, "existing-file")
if err := os.WriteFile(existingFile, nil, 0644); err != nil {
t.Fatalf("failed to create existing source file: %v", err)
}

createdDir := filepath.Join(strings.ToLower(testRoot), "created", "nested")
outsideDir := filepath.Join(t.TempDir(), "outside")
mappedDirectories := []hcsschema.MappedDirectory{
{HostPath: createdDir},
{HostPath: existingFile},
{HostPath: outsideDir},
}

if err := createSandboxMountSourceDirs(context.Background(), mappedDirectories); err != nil {
t.Fatalf("createSandboxMountSourceDirs returned error: %v", err)
}

if info, err := os.Stat(createdDir); err != nil {
t.Fatalf("failed to stat created sandbox directory: %v", err)
} else if !info.IsDir() {
t.Fatalf("sandbox source %q is not a directory", createdDir)
}
if info, err := os.Stat(existingFile); err != nil {
t.Fatalf("failed to stat existing sandbox source file: %v", err)
} else if info.IsDir() {
t.Fatalf("existing sandbox source file %q was replaced by a directory", existingFile)
}
if _, err := os.Stat(outsideDir); !os.IsNotExist(err) {
t.Fatalf("outside source %q was created or returned unexpected error: %v", outsideDir, err)
}
}

// buildModifySettingsRequest creates a serialized ModifySettings request message
// for the given resource type and settings.
func buildModifySettingsRequest(t *testing.T, resourceType guestrequest.ResourceType, requestType guestrequest.RequestType, settings interface{}) []byte {
Expand Down
3 changes: 3 additions & 0 deletions internal/guestpath/paths.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ const (
// WCOWRootPrefixInUVM is the path inside UVM where WCOW container's root
// file system will be mounted
WCOWRootPrefixInUVM = `C:\c`
// WCOWSandboxMountPath is the path inside the UVM where WCOW sandbox mounts
// are created.
WCOWSandboxMountPath = `C:\SandboxMounts`
// SandboxMountPrefix is mount prefix used in container spec to mark a
// sandbox-mount
SandboxMountPrefix = "sandbox://"
Expand Down
41 changes: 23 additions & 18 deletions internal/hcsoci/resources_wcow.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,6 @@ import (
"github.com/Microsoft/hcsshim/internal/uvm/scsi"
)

const wcowSandboxMountPath = "C:\\SandboxMounts"

func allocateWindowsResources(ctx context.Context, coi *createOptionsInternal, r *resources.Resources, isSandbox bool) error {
if coi.Spec.Root == nil {
coi.Spec.Root = &specs.Root{}
Expand Down Expand Up @@ -203,23 +201,30 @@ func setupMounts(ctx context.Context, coi *createOptionsInternal, r *resources.R
// so first convert to a path in the sandboxmounts path itself.
sandboxPath := convertToWCOWSandboxMountPath(mount.Source)

// Now we need to exec a process in the vm that will make these directories as theres
// Now we need to exec a process in the vm that will make these directories as there's
// no functionality in the Windows gcs to create an arbitrary directory.
//
// Create the directory, but also run dir afterwards regardless of if mkdir succeeded to handle the case where the directory already exists
// e.g. from a previous container specifying the same mount (and thus creating the same directory).
b := &bytes.Buffer{}
stderr, err := cmd.CreatePipeAndListen(b, false)
if err != nil {
return err
}
req := &cmd.CmdProcessRequest{
Args: []string{"cmd", "/c", "mkdir", sandboxPath, "&", "dir", sandboxPath},
Stderr: stderr,
}
exitCode, err := coi.HostingSystem.ExecInUVM(ctx, req)
if err != nil {
return errors.Wrapf(err, "failed to create sandbox mount directory in utility VM with exit code %d %q", exitCode, b.String())
// We do not need to do this for Confidential WCOW, because in that case the gcs-sidecar
// handles the create container request and will do this for us. This way the policy
// does not have to have exceptions for allowing such mkdir commands.
//
// Create the directory, but also run dir afterwards regardless of if mkdir succeeded to
// handle the case where the directory already exists e.g. from a previous container
// specifying the same mount (and thus creating the same directory).
if !coi.HostingSystem.HasConfidentialPolicy() {
b := &bytes.Buffer{}
stderr, err := cmd.CreatePipeAndListen(b, false)
if err != nil {
return err
}
req := &cmd.CmdProcessRequest{
Args: []string{"cmd", "/c", "mkdir", sandboxPath, "&", "dir", sandboxPath},
Stderr: stderr,
}
exitCode, err := coi.HostingSystem.ExecInUVM(ctx, req)
if err != nil {
return errors.Wrapf(err, "failed to create sandbox mount directory in utility VM with exit code %d %q", exitCode, b.String())
}
}
} else if np, ok := uvm.ParseNamedPipe(coi.HostingSystem, mount); ok {
if !np.UVMPipe {
Expand Down Expand Up @@ -248,5 +253,5 @@ func setupMounts(ctx context.Context, coi *createOptionsInternal, r *resources.R

func convertToWCOWSandboxMountPath(source string) string {
subPath := strings.TrimPrefix(source, guestpath.SandboxMountPrefix)
return filepath.Join(wcowSandboxMountPath, subPath)
return filepath.Join(guestpath.WCOWSandboxMountPath, subPath)
}
Loading