@@ -13,9 +13,12 @@ import (
1313 "context"
1414 "fmt"
1515 "io"
16+ "io/ioutil"
1617 "log"
1718 "os"
1819 "path/filepath"
20+ "runtime"
21+ "strconv"
1922 "strings"
2023 "syscall"
2124 "time"
@@ -29,6 +32,82 @@ type Server struct {
2932 pb.UnimplementedFilesystemServiceServer
3033}
3134
35+ // findContainerPID finds the PID of a container by searching /proc for ARCA_CONTAINER_ID
36+ func findContainerPID (containerID string ) (int , error ) {
37+ entries , err := ioutil .ReadDir ("/proc" )
38+ if err != nil {
39+ return 0 , fmt .Errorf ("failed to read /proc: %w" , err )
40+ }
41+
42+ for _ , entry := range entries {
43+ if ! entry .IsDir () {
44+ continue
45+ }
46+
47+ // Check if directory name is numeric (PID)
48+ pid , err := strconv .Atoi (entry .Name ())
49+ if err != nil {
50+ continue
51+ }
52+
53+ // Read /proc/[pid]/environ to find ARCA_CONTAINER_ID
54+ environPath := filepath .Join ("/proc" , entry .Name (), "environ" )
55+ environData , err := ioutil .ReadFile (environPath )
56+ if err != nil {
57+ continue
58+ }
59+
60+ // environ is null-separated key=value pairs
61+ for _ , env := range bytes .Split (environData , []byte {0 }) {
62+ if bytes .HasPrefix (env , []byte ("ARCA_CONTAINER_ID=" )) {
63+ foundID := string (bytes .TrimPrefix (env , []byte ("ARCA_CONTAINER_ID=" )))
64+ if foundID == containerID {
65+ return pid , nil
66+ }
67+ }
68+ }
69+ }
70+
71+ return 0 , fmt .Errorf ("container PID not found for ID: %s" , containerID )
72+ }
73+
74+ // withMountNamespace executes a function inside a mount namespace
75+ // Pattern similar to WireGuard's netns switching but for mount namespaces
76+ func withMountNamespace (nsPath string , fn func () error ) error {
77+ // Lock goroutine to OS thread for namespace operations
78+ runtime .LockOSThread ()
79+ defer runtime .UnlockOSThread ()
80+
81+ // Open current mount namespace to return to it later
82+ rootMntFd , err := unix .Open ("/proc/self/ns/mnt" , unix .O_RDONLY , 0 )
83+ if err != nil {
84+ return fmt .Errorf ("failed to open root mount namespace: %w" , err )
85+ }
86+ defer unix .Close (rootMntFd )
87+
88+ // Open target mount namespace
89+ targetMntFd , err := unix .Open (nsPath , unix .O_RDONLY , 0 )
90+ if err != nil {
91+ return fmt .Errorf ("failed to open target mount namespace: %w" , err )
92+ }
93+ defer unix .Close (targetMntFd )
94+
95+ // Enter target mount namespace
96+ if err := unix .Setns (targetMntFd , unix .CLONE_NEWNS ); err != nil {
97+ return fmt .Errorf ("failed to enter mount namespace: %w" , err )
98+ }
99+
100+ // Ensure we return to root namespace
101+ defer func () {
102+ if err := unix .Setns (rootMntFd , unix .CLONE_NEWNS ); err != nil {
103+ log .Printf ("Warning: failed to return to root mount namespace: %v" , err )
104+ }
105+ }()
106+
107+ // Execute the function inside the namespace
108+ return fn ()
109+ }
110+
32111// SyncFilesystem flushes all filesystem buffers to disk
33112// Calls the sync() syscall to ensure all cached writes are persisted
34113func (s * Server ) SyncFilesystem (ctx context.Context , req * pb.SyncFilesystemRequest ) (* pb.SyncFilesystemResponse , error ) {
@@ -357,3 +436,122 @@ func (s *Server) WriteArchive(ctx context.Context, req *pb.WriteArchiveRequest)
357436 Success : true ,
358437 }, nil
359438}
439+
440+ // CreateBindMount creates a bind mount from source to target
441+ // Works like "mount --bind /source /target" inside the container
442+ // Used for file bind mounts (VirtioFS only supports directory shares)
443+ func (s * Server ) CreateBindMount (ctx context.Context , req * pb.CreateBindMountRequest ) (* pb.CreateBindMountResponse , error ) {
444+ log .Printf ("CreateBindMount: containerID=%s source=%s target=%s readOnly=%v" , req .ContainerId , req .Source , req .Target , req .ReadOnly )
445+
446+ // Find container PID by searching /proc for ARCA_CONTAINER_ID environment variable
447+ containerPID , err := findContainerPID (req .ContainerId )
448+ if err != nil {
449+ errMsg := fmt .Sprintf ("failed to find container PID: %v" , err )
450+ log .Printf ("ERROR: %s" , errMsg )
451+ return & pb.CreateBindMountResponse {
452+ Success : false ,
453+ Error : errMsg ,
454+ }, nil
455+ }
456+ log .Printf ("✓ Found container PID: %d" , containerPID )
457+
458+ // Resolve target path to absolute VM path
459+ // Target is container-relative (e.g., "/test.txt"), resolve to VM path (e.g., "/run/container/{id}/rootfs/test.txt")
460+ targetAbsolute := fmt .Sprintf ("/run/container/%s/rootfs%s" , req .ContainerId , req .Target )
461+ log .Printf ("Resolved target path: %s -> %s" , req .Target , targetAbsolute )
462+
463+ // All bind mount operations must happen inside the container's mount namespace
464+ // The source file is mounted via VirtioFS inside the container's mount namespace
465+ // The target file must be created inside the container's mount namespace
466+ mntNsPath := fmt .Sprintf ("/proc/%d/ns/mnt" , containerPID )
467+ log .Printf ("Will perform all operations in container mount namespace: %s" , mntNsPath )
468+
469+ // Perform all operations inside the container's mount namespace
470+ err = withMountNamespace (mntNsPath , func () error {
471+ log .Printf ("Inside container mount namespace" )
472+
473+ // Validate source exists
474+ sourceInfo , err := os .Stat (req .Source )
475+ if err != nil {
476+ log .Printf ("ERROR: source path does not exist: %v" , err )
477+ return fmt .Errorf ("source path does not exist: %w" , err )
478+ }
479+ log .Printf ("✓ Source exists: %s (isDir=%v, size=%d)" , req .Source , sourceInfo .IsDir (), sourceInfo .Size ())
480+
481+ // Ensure parent directory of target exists
482+ targetParent := filepath .Dir (targetAbsolute )
483+ if err := os .MkdirAll (targetParent , 0755 ); err != nil {
484+ log .Printf ("ERROR: failed to create target parent directory: %v" , err )
485+ return fmt .Errorf ("failed to create target parent directory: %w" , err )
486+ }
487+ log .Printf ("✓ Target parent directory exists: %s" , targetParent )
488+
489+ // Check if target exists
490+ targetInfo , err := os .Stat (targetAbsolute )
491+ if err != nil {
492+ if os .IsNotExist (err ) {
493+ // Create target matching source type
494+ if sourceInfo .IsDir () {
495+ log .Printf ("Creating target directory: %s" , targetAbsolute )
496+ if err := os .Mkdir (targetAbsolute , 0755 ); err != nil {
497+ log .Printf ("ERROR: failed to create target directory: %v" , err )
498+ return fmt .Errorf ("failed to create target directory: %w" , err )
499+ }
500+ log .Printf ("✓ Target directory created" )
501+ } else {
502+ // Create empty file
503+ log .Printf ("Creating target file: %s" , targetAbsolute )
504+ f , err := os .Create (targetAbsolute )
505+ if err != nil {
506+ log .Printf ("ERROR: failed to create target file: %v" , err )
507+ return fmt .Errorf ("failed to create target file: %w" , err )
508+ }
509+ f .Close ()
510+ log .Printf ("✓ Target file created" )
511+ }
512+ } else {
513+ log .Printf ("ERROR: failed to stat target: %v" , err )
514+ return fmt .Errorf ("failed to stat target: %w" , err )
515+ }
516+ } else {
517+ log .Printf ("✓ Target already exists: isDir=%v" , targetInfo .IsDir ())
518+ // Target exists - verify types match
519+ if sourceInfo .IsDir () != targetInfo .IsDir () {
520+ log .Printf ("ERROR: source and target type mismatch" )
521+ return fmt .Errorf ("source and target must both be files or both be directories" )
522+ }
523+ }
524+
525+ // Perform bind mount using syscall (we're already in the namespace)
526+ log .Printf ("Performing bind mount: %s -> %s" , req .Source , targetAbsolute )
527+ if err := unix .Mount (req .Source , targetAbsolute , "" , unix .MS_BIND , "" ); err != nil {
528+ log .Printf ("ERROR: failed to bind mount: %v" , err )
529+ return fmt .Errorf ("failed to bind mount: %w" , err )
530+ }
531+ log .Printf ("✓ Bind mount successful" )
532+
533+ // If read-only, remount with read-only flag
534+ if req .ReadOnly {
535+ log .Printf ("Remounting as read-only..." )
536+ if err := unix .Mount ("" , targetAbsolute , "" , unix .MS_BIND | unix .MS_REMOUNT | unix .MS_RDONLY , "" ); err != nil {
537+ log .Printf ("ERROR: failed to remount as read-only: %v" , err )
538+ return fmt .Errorf ("failed to remount as read-only: %w" , err )
539+ }
540+ log .Printf ("✓ Remounted as read-only" )
541+ }
542+
543+ log .Printf ("Bind mount created successfully: %s -> %s (container path: %s)" , req .Source , targetAbsolute , req .Target )
544+ return nil
545+ })
546+
547+ if err != nil {
548+ return & pb.CreateBindMountResponse {
549+ Success : false ,
550+ Error : err .Error (),
551+ }, nil
552+ }
553+
554+ return & pb.CreateBindMountResponse {
555+ Success : true ,
556+ }, nil
557+ }
0 commit comments