Skip to content
Draft
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
61 changes: 44 additions & 17 deletions pkg/backend/containerd/containerd.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,37 +36,64 @@ func NewMounter(socketPath string) backend.Mounter {
})
}

// shellQuote escapes a string for safe use in shell commands
// Uses POSIX-compliant single-quote escaping
func shellQuote(s string) string {
// Single quotes protect all characters except single quote itself
// To include a single quote: end quote, add escaped quote, start quote
// Example: "it's" becomes 'it'"'"'s'
return "'" + strings.ReplaceAll(s, "'", "'\"'\"'") + "'"
}

// mountInHostNamespace mounts directly in the host mount namespace using nsenter
// Uses batched approach: executes all mount commands in a single nsenter invocation
func mountInHostNamespace(ctx context.Context, mounts []mount.Mount, target string) error {
// For each mount, execute it in the host namespace
if len(mounts) == 0 {
return nil
}

// Build a shell script with all mount commands
// Chain commands with && to stop on first error (instead of set -e which may not work on all shells)
var scriptBuilder strings.Builder

for i, m := range mounts {
var args []string
if i > 0 {
scriptBuilder.WriteString(" && ")
}

scriptBuilder.WriteString("mount")

// Only add -t flag if type is specified
if m.Type != "" {
args = append(args, "-t", m.Type)
scriptBuilder.WriteString(fmt.Sprintf(" -t %s", shellQuote(m.Type)))
}

if len(m.Options) > 0 {
args = append(args, "-o", strings.Join(m.Options, ","))
scriptBuilder.WriteString(fmt.Sprintf(" -o %s", shellQuote(strings.Join(m.Options, ","))))
}

args = append(args, m.Source, target)
scriptBuilder.WriteString(fmt.Sprintf(" %s %s", shellQuote(m.Source), shellQuote(target)))

// Build the nsenter command
nsenterArgs := []string{"--mount=/host/proc/1/ns/mnt", "--", "mount"}
nsenterArgs = append(nsenterArgs, args...)
klog.V(5).Infof("batch mount command %d/%d", i+1, len(mounts))
}

cmd := exec.CommandContext(ctx, "nsenter", nsenterArgs...)
output, err := cmd.CombinedOutput()
if err != nil {
klog.Errorf("nsenter mount failed (attempt %d/%d): %s, output: %s, command: %v",
i+1, len(mounts), err, string(output), cmd.Args)
return fmt.Errorf("mount failed: %w, output: %s", err, string(output))
}
klog.V(4).Infof("mounted %s to %s with type %s and options %v using nsenter (mount %d/%d)",
m.Source, target, m.Type, m.Options, i+1, len(mounts))
script := scriptBuilder.String()
klog.V(4).Infof("executing batch mount with %d commands using nsenter", len(mounts))

// Execute all mounts in a single nsenter call
// Note: nsenter expects just the command name (sh), not the full path (/bin/sh)
cmd := exec.CommandContext(ctx, "nsenter",
"--mount=/host/proc/1/ns/mnt",
"--",
"sh", "-c", script)

output, err := cmd.CombinedOutput()
if err != nil {
klog.Errorf("batch nsenter mount failed: %s, output: %s, script:\n%s", err, string(output), script)
return fmt.Errorf("batch mount failed: %w, output: %s", err, string(output))
}

klog.V(4).Infof("successfully mounted %d layers to %s using batched nsenter", len(mounts), target)
return nil
}

Expand Down