@@ -227,6 +227,25 @@ ostree_sysroot_new_default (void)
227
227
return ostree_sysroot_new (NULL );
228
228
}
229
229
230
+ static gboolean
231
+ _ostree_in_root_mount_namespace (gboolean * out_val , GError * * error )
232
+ {
233
+ /* glnx_readlinkat_malloc does not use cancellable acually. */
234
+ g_autofree char * mntns_pid1
235
+ = glnx_readlinkat_malloc (AT_FDCWD , "/proc/1/ns/mnt" , NULL , error );
236
+ if (!mntns_pid1 )
237
+ return glnx_prefix_error (error , "Reading /proc/1/ns/mnt" );
238
+ /* mount namespace is per-thread, not per-process */
239
+ g_autofree char * cur_thread = g_strdup_printf ("/proc/%d/ns/mnt" , gettid ());
240
+ g_autofree char * mntns_cur
241
+ = glnx_readlinkat_malloc (AT_FDCWD , cur_thread , NULL , error );
242
+ if (!mntns_cur )
243
+ return glnx_prefix_error (error , "Reading %s" , cur_thread );
244
+
245
+ * out_val = g_str_equal (mntns_pid1 , mntns_cur );
246
+ return TRUE;
247
+ }
248
+
230
249
/**
231
250
* ostree_sysroot_set_mount_namespace_in_use:
232
251
*
@@ -251,9 +270,41 @@ ostree_sysroot_set_mount_namespace_in_use (OstreeSysroot *self)
251
270
/* Must be before we're loaded, as otherwise we'd have to close/reopen all our
252
271
fds, e.g. the repo */
253
272
g_return_if_fail (self -> loadstate < OSTREE_SYSROOT_LOAD_STATE_LOADED );
273
+ gboolean in_root ;
274
+ g_autoptr (GError ) local_error = NULL ;
275
+ g_assert (_ostree_in_root_mount_namespace (& in_root , & local_error ) && !in_root );
254
276
self -> mount_namespace_in_use = TRUE;
255
277
}
256
278
279
+ gboolean
280
+ _ostree_sysroot_enter_mount_namespace (OstreeSysroot * self , GError * * error )
281
+ {
282
+ /* Do nothing if we're not privileged */
283
+ if (getuid () != 0 )
284
+ return TRUE;
285
+
286
+ /* We also assume operating on non-booted roots won't have a readonly sysroot */
287
+ if (!self -> root_is_ostree_booted )
288
+ return TRUE;
289
+
290
+ // If the mount namespaces are the same, we need to unshare().
291
+ gboolean in_root ;
292
+ g_return_val_if_fail (_ostree_in_root_mount_namespace (& in_root , error ), FALSE);
293
+ if (in_root )
294
+ {
295
+ if (unshare (CLONE_NEWNS ) < 0 )
296
+ return glnx_throw_errno_prefix (error , "Failed to invoke unshare(CLONE_NEWNS)" );
297
+
298
+ /* Ensure what we do in our mount namespace do not leak to outside */
299
+ if (mount (NULL , "/" , NULL , MS_PRIVATE | MS_REC | MS_SILENT , NULL ) < 0 )
300
+ return glnx_throw_errno_prefix (error , "Failed to set the mount propagation to private" );
301
+ }
302
+
303
+ ostree_sysroot_set_mount_namespace_in_use (self );
304
+
305
+ return TRUE;
306
+ }
307
+
257
308
/**
258
309
* ostree_sysroot_initialize_with_mount_namespace:
259
310
*
@@ -284,32 +335,7 @@ ostree_sysroot_initialize_with_mount_namespace (OstreeSysroot *self, GCancellabl
284
335
if (!ostree_sysroot_initialize (self , error ))
285
336
return FALSE;
286
337
287
- /* Do nothing if we're not privileged */
288
- if (getuid () != 0 )
289
- return TRUE;
290
-
291
- /* We also assume operating on non-booted roots won't have a readonly sysroot */
292
- if (!self -> root_is_ostree_booted )
293
- return TRUE;
294
-
295
- g_autofree char * mntns_pid1
296
- = glnx_readlinkat_malloc (AT_FDCWD , "/proc/1/ns/mnt" , cancellable , error );
297
- if (!mntns_pid1 )
298
- return glnx_prefix_error (error , "Reading /proc/1/ns/mnt" );
299
- g_autofree char * mntns_self
300
- = glnx_readlinkat_malloc (AT_FDCWD , "/proc/self/ns/mnt" , cancellable , error );
301
- if (!mntns_self )
302
- return glnx_prefix_error (error , "Reading /proc/self/ns/mnt" );
303
-
304
- // If the mount namespaces are the same, we need to unshare().
305
- if (strcmp (mntns_pid1 , mntns_self ) == 0 )
306
- {
307
- if (unshare (CLONE_NEWNS ) < 0 )
308
- return glnx_throw_errno_prefix (error , "Failed to invoke unshare(CLONE_NEWNS)" );
309
- }
310
-
311
- ostree_sysroot_set_mount_namespace_in_use (self );
312
- return TRUE;
338
+ return _ostree_sysroot_enter_mount_namespace (self , error );
313
339
}
314
340
315
341
/**
@@ -374,11 +400,94 @@ remount_writable (const char *path, gboolean *did_remount, GError **error)
374
400
return TRUE;
375
401
}
376
402
403
+ static gboolean
404
+ _ostree_sysroot_invisible (OstreeSysroot * self , gboolean * out_val , GError * * error )
405
+ {
406
+ if (!self -> root_is_ostree_booted )
407
+ return FALSE;
408
+
409
+ if (!glnx_fstatat_allow_noent (self -> sysroot_fd , "ostree/repo" , NULL , 0 , error ))
410
+ return FALSE;
411
+
412
+ if (errno == 0 )
413
+ {
414
+ * out_val = FALSE;
415
+ return TRUE;
416
+ }
417
+
418
+ // root_is_ostree_booted is true so we can use AT_FDCWD here
419
+ if (!glnx_fstatat_allow_noent (AT_FDCWD , OTCORE_RUN_OSTREE_PRIVATE "/sysroot-ns" , NULL , 0 , error ))
420
+ return FALSE;
421
+
422
+ if (errno != 0 )
423
+ {
424
+ * out_val = FALSE;
425
+ return TRUE;
426
+ }
427
+
428
+ * out_val = TRUE;
429
+ return TRUE;
430
+ }
431
+
432
+ /* Make /sysroot visible */
433
+ gboolean
434
+ _ostree_sysroot_ensure_visible (OstreeSysroot * self , GError * * error )
435
+ {
436
+ if (!ostree_sysroot_initialize (self , error ))
437
+ return FALSE;
438
+
439
+ /* Do nothing if no mount namespace is in use */
440
+ if (!self -> mount_namespace_in_use )
441
+ return TRUE;
442
+
443
+ /* If we aren't operating on a booted system, then we don't
444
+ * do anything with mounts.
445
+ */
446
+ if (!self -> root_is_ostree_booted )
447
+ return TRUE;
448
+
449
+ gboolean invisible ;
450
+ if (!_ostree_sysroot_invisible (self , & invisible , error ))
451
+ return FALSE;
452
+ /* Handle invisible sysroot */
453
+ if (invisible )
454
+ {
455
+ glnx_autofd int sysroot_ns_fd = -1 ;
456
+ if (!glnx_openat_rdonly (AT_FDCWD , OTCORE_RUN_OSTREE_PRIVATE "/sysroot-ns" , TRUE, & sysroot_ns_fd , error ))
457
+ return FALSE;
458
+
459
+ g_autofree char * cur_ns = g_strdup_printf ("/proc/%d/ns/mnt" , gettid ());
460
+ glnx_autofd int cur_ns_fd = -1 ;
461
+ if (!glnx_openat_rdonly (AT_FDCWD , cur_ns , TRUE, & cur_ns_fd , error ))
462
+ return FALSE;
463
+
464
+ if (setns (sysroot_ns_fd , CLONE_NEWNS ) < 0 )
465
+ return glnx_throw_errno_prefix (error , "setns" );
466
+
467
+ glnx_autofd int tree_fd = (int )syscall (SYS_open_tree , AT_FDCWD , "/" , 1 /* OPEN_TREE_CLONE */ | O_CLOEXEC );
468
+ if (tree_fd < 0 )
469
+ return glnx_throw_errno_prefix (error , "open_tree" );
470
+
471
+ if (setns (cur_ns_fd , CLONE_NEWNS ) < 0 )
472
+ abort (); // it's unsafe to continue if we cannot switch back
473
+
474
+ if (syscall (SYS_move_mount , tree_fd , "" , AT_FDCWD , "/sysroot" , 4 /* MOVE_MOUNT_F_EMPTY_PATH */ ) < 0 )
475
+ return glnx_throw_errno_prefix (error , "move_mount" );
476
+ }
477
+
478
+ /* Now close and reopen our file descriptors */
479
+ ostree_sysroot_unload (self );
480
+ if (!ensure_sysroot_fd (self , error ))
481
+ return FALSE;
482
+
483
+ return TRUE;
484
+ }
485
+
377
486
/* Remount /sysroot read-write if necessary */
378
487
gboolean
379
488
_ostree_sysroot_ensure_writable (OstreeSysroot * self , GError * * error )
380
489
{
381
- if (!ostree_sysroot_initialize (self , error ))
490
+ if (!_ostree_sysroot_ensure_visible (self , error ))
382
491
return FALSE;
383
492
384
493
/* Do nothing if no mount namespace is in use */
@@ -1063,6 +1172,22 @@ ostree_sysroot_initialize (OstreeSysroot *self, GError **error)
1063
1172
g_debug ("root_is_ostree_booted: %d" , self -> root_is_ostree_booted );
1064
1173
self -> loadstate = OSTREE_SYSROOT_LOAD_STATE_INIT ;
1065
1174
}
1175
+ else
1176
+ {
1177
+ return TRUE;
1178
+ }
1179
+
1180
+ gboolean invisible ;
1181
+ if (!_ostree_sysroot_invisible (self , & invisible , error ))
1182
+ return FALSE;
1183
+
1184
+ if (invisible )
1185
+ {
1186
+ if (!_ostree_sysroot_enter_mount_namespace (self , error ))
1187
+ return FALSE;
1188
+ if (!_ostree_sysroot_ensure_visible (self , error ))
1189
+ return FALSE;
1190
+ }
1066
1191
1067
1192
return TRUE;
1068
1193
}
0 commit comments