@@ -13,6 +13,7 @@ import (
13
13
"os/signal"
14
14
"path/filepath"
15
15
"runtime"
16
+ "strings"
16
17
"syscall"
17
18
"time"
18
19
@@ -28,6 +29,7 @@ import (
28
29
"github.com/gitpod-io/gitpod/common-go/log"
29
30
"github.com/gitpod-io/gitpod/workspacekit/pkg/lift"
30
31
"github.com/gitpod-io/gitpod/workspacekit/pkg/seccomp"
32
+ "github.com/gitpod-io/gitpod/ws-daemon/api"
31
33
daemonapi "github.com/gitpod-io/gitpod/ws-daemon/api"
32
34
)
33
35
@@ -70,7 +72,7 @@ var ring0Cmd = &cobra.Command{
70
72
}
71
73
defer conn .Close ()
72
74
73
- _ , err = client .PrepareForUserNS (ctx , & daemonapi.PrepareForUserNSRequest {})
75
+ prep , err : = client .PrepareForUserNS (ctx , & daemonapi.PrepareForUserNSRequest {})
74
76
if err != nil {
75
77
log .WithError (err ).Fatal ("cannot prepare for user namespaces" )
76
78
return
@@ -95,7 +97,7 @@ var ring0Cmd = &cobra.Command{
95
97
cmd .Stdin = os .Stdin
96
98
cmd .Stdout = os .Stdout
97
99
cmd .Stderr = os .Stderr
98
- cmd .Env = os .Environ ()
100
+ cmd .Env = append ( os .Environ (), "WORKSPACEKIT_FSSHIFT=" + prep . FsShift . String () )
99
101
100
102
if err := cmd .Start (); err != nil {
101
103
log .WithError (err ).Error ("failed to start ring0" )
@@ -183,11 +185,11 @@ var ring1Cmd = &cobra.Command{
183
185
}
184
186
defer conn .Close ()
185
187
188
+ mapping := []* daemonapi.WriteIDMappingRequest_Mapping {
189
+ {ContainerId : 0 , HostId : 33333 , Size : 1 },
190
+ {ContainerId : 1 , HostId : 100000 , Size : 65534 },
191
+ }
186
192
if ! ring1Opts .MappingEstablished {
187
- mapping := []* daemonapi.WriteIDMappingRequest_Mapping {
188
- {ContainerId : 0 , HostId : 33333 , Size : 1 },
189
- {ContainerId : 1 , HostId : 100000 , Size : 65534 },
190
- }
191
193
_ , err = client .WriteIDMapping (ctx , & daemonapi.WriteIDMappingRequest {Pid : int64 (os .Getpid ()), Gid : false , Mapping : mapping })
192
194
if err != nil {
193
195
log .WithError (err ).Error ("cannot establish UID mapping" )
@@ -223,23 +225,46 @@ var ring1Cmd = &cobra.Command{
223
225
log .WithError (err ).Fatal ("cannot create tempdir" )
224
226
}
225
227
226
- mnts := []struct {
228
+ var fsshift api.FSShiftMethod
229
+ if v , ok := api .FSShiftMethod_value [os .Getenv ("WORKSPACEKIT_FSSHIFT" )]; ! ok {
230
+ log .WithField ("fsshift" , os .Getenv ("WORKSPACEKIT_FSSHIFT" )).Fatal ("unknown FS shift method" )
231
+ } else {
232
+ fsshift = api .FSShiftMethod (v )
233
+ }
234
+
235
+ type mnte struct {
227
236
Target string
228
237
Source string
229
238
FSType string
230
239
Flags uintptr
231
- }{
232
- // TODO(cw): pull mark mount location from config
233
- {Target : "/" , Source : "/.workspace/mark" , FSType : "shiftfs" },
234
- {Target : "/sys" , Flags : unix .MS_BIND | unix .MS_REC },
235
- {Target : "/dev" , Flags : unix .MS_BIND | unix .MS_REC },
236
- // TODO(cw): only mount /workspace if it's in the mount table, i.e. this isn't an FWB workspace
237
- {Target : "/workspace" , Flags : unix .MS_BIND | unix .MS_REC },
238
- {Target : "/etc/hosts" , Flags : unix .MS_BIND | unix .MS_REC },
239
- {Target : "/etc/hostname" , Flags : unix .MS_BIND | unix .MS_REC },
240
- {Target : "/etc/resolv.conf" , Flags : unix .MS_BIND | unix .MS_REC },
241
- {Target : "/tmp" , Source : "tmpfs" , FSType : "tmpfs" },
242
240
}
241
+
242
+ var mnts []mnte
243
+
244
+ switch fsshift {
245
+ case api .FSShiftMethod_FUSE :
246
+ mnts = append (mnts ,
247
+ mnte {Target : "/" , Source : "/.workspace/mark" , Flags : unix .MS_BIND | unix .MS_REC },
248
+ )
249
+ case api .FSShiftMethod_SHIFTFS :
250
+ mnts = append (mnts ,
251
+ mnte {Target : "/" , Source : "/.workspace/mark" , FSType : "shiftfs" },
252
+ )
253
+ default :
254
+ log .WithField ("fsshift" , fsshift ).Fatal ("unknown FS shift method" )
255
+ }
256
+
257
+ mnts = append (mnts ,
258
+ mnte {Target : "/sys" , Flags : unix .MS_BIND | unix .MS_REC },
259
+ mnte {Target : "/dev" , Flags : unix .MS_BIND | unix .MS_REC },
260
+ // TODO(cw): only mount /workspace if it's in the mount table, i.e. this isn't an FWB workspace
261
+ mnte {Target : "/workspace" , Flags : unix .MS_BIND | unix .MS_REC },
262
+ mnte {Target : "/etc/hosts" , Flags : unix .MS_BIND | unix .MS_REC },
263
+ mnte {Target : "/etc/hostname" , Flags : unix .MS_BIND | unix .MS_REC },
264
+ mnte {Target : "/etc/resolv.conf" , Flags : unix .MS_BIND | unix .MS_REC },
265
+ mnte {Target : "/tmp" , Source : "tmpfs" , FSType : "tmpfs" },
266
+ )
267
+
243
268
for _ , m := range mnts {
244
269
dst := filepath .Join (ring2Root , m .Target )
245
270
_ = os .MkdirAll (dst , 0644 )
@@ -265,6 +290,14 @@ var ring1Cmd = &cobra.Command{
265
290
}
266
291
}
267
292
293
+ env := make ([]string , 0 , len (os .Environ ()))
294
+ for _ , e := range os .Environ () {
295
+ if strings .HasPrefix (e , "WORKSPACEKIT_" ) {
296
+ continue
297
+ }
298
+ env = append (env , e )
299
+ }
300
+
268
301
socketFN := filepath .Join (os .TempDir (), fmt .Sprintf ("workspacekit-ring1-%d.unix" , time .Now ().UnixNano ()))
269
302
skt , err := net .Listen ("unix" , socketFN )
270
303
if err != nil {
@@ -283,7 +316,7 @@ var ring1Cmd = &cobra.Command{
283
316
cmd .Stdin = os .Stdin
284
317
cmd .Stdout = os .Stdout
285
318
cmd .Stderr = os .Stderr
286
- cmd .Env = os . Environ ()
319
+ cmd .Env = env
287
320
if err := cmd .Start (); err != nil {
288
321
log .WithError (err ).Error ("failed to start the child process" )
289
322
failed = true
@@ -358,8 +391,9 @@ var ring1Cmd = &cobra.Command{
358
391
359
392
log .Info ("signaling to child process" )
360
393
_ , err = msgutil .MarshalToWriter (ring2Conn , ringSyncMsg {
361
- Stage : 1 ,
362
- Rootfs : ring2Root ,
394
+ Stage : 1 ,
395
+ Rootfs : ring2Root ,
396
+ FSShift : fsshift ,
363
397
})
364
398
if err != nil {
365
399
log .WithError (err ).Error ("cannot send ring sync msg to ring2" )
@@ -504,7 +538,7 @@ var ring2Cmd = &cobra.Command{
504
538
return
505
539
}
506
540
507
- err = pivotRoot (msg .Rootfs )
541
+ err = pivotRoot (msg .Rootfs , msg . FSShift )
508
542
if err != nil {
509
543
log .WithError (err ).Error ("cannot pivot root" )
510
544
failed = true
@@ -562,13 +596,27 @@ var ring2Cmd = &cobra.Command{
562
596
// filesystem, and everything else is cleaned up.
563
597
//
564
598
// copied from runc: https://github.com/opencontainers/runc/blob/cf6c074115d00c932ef01dedb3e13ba8b8f964c3/libcontainer/rootfs_linux.go#L760
565
- func pivotRoot (rootfs string ) error {
599
+ func pivotRoot (rootfs string , fsshift api. FSShiftMethod ) error {
566
600
// While the documentation may claim otherwise, pivot_root(".", ".") is
567
601
// actually valid. What this results in is / being the new root but
568
602
// /proc/self/cwd being the old root. Since we can play around with the cwd
569
603
// with pivot_root this allows us to pivot without creating directories in
570
604
// the rootfs. Shout-outs to the LXC developers for giving us this idea.
571
605
606
+ if fsshift == api .FSShiftMethod_FUSE {
607
+ err := unix .Chroot (rootfs )
608
+ if err != nil {
609
+ return fmt .Errorf ("cannot chroot: %v" , err )
610
+ }
611
+
612
+ err = unix .Chdir ("/" )
613
+ if err != nil {
614
+ return fmt .Errorf ("cannot chdir to new root :%v" , err )
615
+ }
616
+
617
+ return nil
618
+ }
619
+
572
620
oldroot , err := unix .Open ("/" , unix .O_DIRECTORY | unix .O_RDONLY , 0 )
573
621
if err != nil {
574
622
return err
@@ -616,6 +664,7 @@ func pivotRoot(rootfs string) error {
616
664
if err := unix .Chdir ("/" ); err != nil {
617
665
return fmt .Errorf ("chdir / %s" , err )
618
666
}
667
+
619
668
return nil
620
669
}
621
670
@@ -631,8 +680,9 @@ func sleepForDebugging() {
631
680
}
632
681
633
682
type ringSyncMsg struct {
634
- Stage int `json:"stage"`
635
- Rootfs string `json:"rootfs"`
683
+ Stage int `json:"stage"`
684
+ Rootfs string `json:"rootfs"`
685
+ FSShift api.FSShiftMethod `json:"fsshift"`
636
686
}
637
687
638
688
// ConnectToInWorkspaceDaemonService attempts to connect to the InWorkspaceService offered by the ws-daemon.
0 commit comments