@@ -137,6 +137,8 @@ mount_tab_free (MountTab tab)
137
137
{
138
138
int i ;
139
139
140
+ /* An allocated MountTab always ends with a zeroed MountInfo, so we can tell
141
+ when to stop freeing memory. */
140
142
for (i = 0 ; tab [i ].mountpoint != NULL ; i ++ )
141
143
free (tab [i ].mountpoint );
142
144
free (tab );
@@ -155,15 +157,17 @@ cleanup_mount_tabp (void *p)
155
157
156
158
typedef struct MountInfoLine MountInfoLine ;
157
159
struct MountInfoLine {
158
- const char * mountpoint ;
159
- const char * options ;
160
+ char * mountpoint ;
161
+ unsigned long options ;
160
162
bool covered ;
161
163
int id ;
162
164
int parent_id ;
163
165
MountInfoLine * first_child ;
164
166
MountInfoLine * next_sibling ;
165
167
};
166
168
169
+ typedef MountInfoLine * MountInfoLines ;
170
+
167
171
static unsigned int
168
172
count_lines (const char * data )
169
173
{
@@ -211,7 +215,7 @@ collect_mounts (MountInfo *info, MountInfoLine *line)
211
215
if (!line -> covered )
212
216
{
213
217
info -> mountpoint = xstrdup (line -> mountpoint );
214
- info -> options = decode_mountoptions ( line -> options ) ;
218
+ info -> options = line -> options ;
215
219
info ++ ;
216
220
}
217
221
@@ -225,21 +229,15 @@ collect_mounts (MountInfo *info, MountInfoLine *line)
225
229
return info ;
226
230
}
227
231
228
- static MountTab
229
- parse_mountinfo (int proc_fd ,
230
- const char * root_mount )
232
+ static MountInfoLines
233
+ read_mountinfo (int proc_fd ,
234
+ unsigned int * mount_count )
231
235
{
232
236
cleanup_free char * mountinfo = NULL ;
233
- cleanup_free MountInfoLine * lines = NULL ;
234
- cleanup_free MountInfoLine * * by_id = NULL ;
235
- cleanup_mount_tab MountTab mount_tab = NULL ;
236
- MountInfo * end_tab ;
237
- int n_mounts ;
237
+ unsigned int n_lines ;
238
+ MountInfoLine * lines ;
238
239
char * line ;
239
240
int i ;
240
- int max_id ;
241
- unsigned int n_lines ;
242
- int root ;
243
241
244
242
mountinfo = load_file_at (proc_fd , "self/mountinfo" );
245
243
if (mountinfo == NULL )
@@ -248,10 +246,8 @@ parse_mountinfo (int proc_fd,
248
246
n_lines = count_lines (mountinfo );
249
247
lines = xcalloc (n_lines * sizeof (MountInfoLine ));
250
248
251
- max_id = 0 ;
252
249
line = mountinfo ;
253
250
i = 0 ;
254
- root = -1 ;
255
251
while (* line != 0 )
256
252
{
257
253
int rc , consumed = 0 ;
@@ -289,30 +285,68 @@ parse_mountinfo (int proc_fd,
289
285
options_end = rest ;
290
286
291
287
* mountpoint_end = 0 ;
292
- lines [i ].mountpoint = unescape_inline (mountpoint );
288
+ lines [i ].mountpoint = xstrdup ( unescape_inline (mountpoint ) );
293
289
294
290
* options_end = 0 ;
295
- lines [i ].options = options ;
291
+ lines [i ].options = decode_mountoptions (options );
292
+
293
+ i ++ ;
294
+ line = next_line ;
295
+ }
296
+ assert (i == n_lines );
297
+
298
+ * mount_count = n_lines ;
299
+ return lines ;
300
+ }
301
+
302
+ static int
303
+ max_mount_id (MountInfoLines lines ,
304
+ unsigned int n_lines )
305
+ {
306
+ int i ;
307
+ int max_id ;
296
308
309
+ max_id = 0 ;
310
+ for (i = 0 ; i < n_lines ; i ++ )
311
+ {
297
312
if (lines [i ].id > max_id )
298
313
max_id = lines [i ].id ;
299
314
if (lines [i ].parent_id > max_id )
300
315
max_id = lines [i ].parent_id ;
316
+ }
317
+ return max_id ;
318
+ }
301
319
320
+ static MountTab
321
+ parse_mountinfo (MountInfoLines lines ,
322
+ unsigned int n_lines ,
323
+ const char * root_mount )
324
+ {
325
+ int root ;
326
+ int i ;
327
+ int max_id ;
328
+ cleanup_mount_tab MountTab mount_tab = NULL ;
329
+ cleanup_free MountInfoLine * * by_id = NULL ;
330
+ int n_mounts ;
331
+ MountInfo * end_tab ;
332
+
333
+ root = -1 ;
334
+ for (i = 0 ; i < n_lines ; i ++ )
335
+ {
302
336
if (path_equal (lines [i ].mountpoint , root_mount ))
303
337
root = i ;
304
-
305
- i ++ ;
306
- line = next_line ;
307
338
}
308
- assert (i == n_lines );
309
-
310
339
if (root == -1 )
311
340
{
312
- mount_tab = xcalloc (sizeof (MountInfo ) * (1 ));
341
+ /* Allocate one more than required, so cleanup_mount_tabp knows when to
342
+ stop freeing memory. */
343
+ mount_tab = xcalloc (sizeof (MountInfo ));
313
344
return steal_pointer (& mount_tab );
314
345
}
315
346
347
+ /* Allocate one more than required, so we can use IDs as indexes into
348
+ by_id. */
349
+ max_id = max_mount_id (lines , n_lines );
316
350
by_id = xcalloc ((max_id + 1 ) * sizeof (MountInfoLine * ));
317
351
for (i = 0 ; i < n_lines ; i ++ )
318
352
by_id [lines [i ].id ] = & lines [i ];
@@ -338,18 +372,18 @@ parse_mountinfo (int proc_fd,
338
372
sibling = parent -> first_child ;
339
373
while (sibling != NULL )
340
374
{
341
- /* If this mountpoint is a path prefix of the sibling,
342
- * say this->mp= /foo/bar and sibling->mp= /foo, then it is
343
- * covered by the sibling, and we drop it. */
375
+ /* If this mountpoint is a path prefix of the sibling, say
376
+ * this->mountpoint == " /foo/bar" and sibling->mountpoiunt == " /foo",
377
+ * then it is covered by the sibling, and we drop it. */
344
378
if (has_path_prefix (this -> mountpoint , sibling -> mountpoint ))
345
379
{
346
380
covered = TRUE;
347
381
break ;
348
382
}
349
383
350
- /* If the sibling is a path prefix of this mount point,
351
- * say this->mp= /foo and sibling->mp= /foo/bar, then the sibling
352
- * is covered, and we drop it.
384
+ /* If the sibling is a path prefix of this mount point, say
385
+ * this->mountpoint == " /foo" and sibling->mountpoint == " /foo/bar",
386
+ * then the sibling is covered, and we drop it.
353
387
*/
354
388
if (has_path_prefix (sibling -> mountpoint , this -> mountpoint ))
355
389
* to_sibling = sibling -> next_sibling ;
@@ -365,20 +399,102 @@ parse_mountinfo (int proc_fd,
365
399
}
366
400
367
401
n_mounts = count_mounts (& lines [root ]);
368
- mount_tab = xcalloc (sizeof (MountInfo ) * (n_mounts + 1 ));
402
+ /* Allocate one more than required, so cleanup_mount_tabp knows when to stop
403
+ freeing memory. */
404
+ mount_tab = xcalloc ((n_mounts + 1 ) * sizeof (MountInfo ));
369
405
370
406
end_tab = collect_mounts (& mount_tab [0 ], & lines [root ]);
371
407
assert (end_tab == & mount_tab [n_mounts ]);
372
408
373
409
return steal_pointer (& mount_tab );
374
410
}
375
411
412
+ static int
413
+ find_parent (MountInfoLines lines ,
414
+ unsigned int line_count ,
415
+ char * mountpoint )
416
+ {
417
+ cleanup_free char * prefix = NULL ;
418
+ int parent_id ;
419
+ char * start ;
420
+ bool mount_found ;
421
+ int i ;
422
+
423
+ prefix = xcalloc (strlen (mountpoint ) + 1 );
424
+ prefix [0 ] = '/' ;
425
+
426
+ parent_id = 0 ;
427
+ start = mountpoint ;
428
+ do
429
+ {
430
+ start = index (start , '/' );
431
+ if (start != NULL )
432
+ {
433
+ memcpy (prefix , mountpoint , start - mountpoint );
434
+ start ++ ;
435
+ }
436
+ else
437
+ strcpy (prefix , mountpoint );
438
+
439
+ do
440
+ {
441
+ mount_found = FALSE;
442
+ for (i = 0 ; i < line_count ; i ++ )
443
+ {
444
+ if (strcmp (lines [i ].mountpoint , prefix ) == 0 && lines [i ].parent_id == parent_id )
445
+ {
446
+ parent_id = lines [i ].id ;
447
+ mount_found = 1 ;
448
+ break ;
449
+ }
450
+ }
451
+ }
452
+ while (mount_found );
453
+ }
454
+ while (start != NULL );
455
+
456
+ return parent_id ;
457
+ }
458
+
459
+ static MountInfoLines
460
+ add_mountinfo (MountInfoLines old_lines ,
461
+ unsigned int line_count ,
462
+ const char * src ,
463
+ char * dest )
464
+ {
465
+ MountInfoLines new_lines ;
466
+ int parent_id ;
467
+ int i ;
468
+
469
+ parent_id = find_parent (old_lines , line_count , dest );
470
+
471
+ new_lines = xcalloc ((line_count + 1 ) * sizeof (MountInfoLine ));
472
+ for (i = 0 ; i < line_count ; i ++ )
473
+ {
474
+ new_lines [i ].mountpoint = old_lines [i ].mountpoint ;
475
+ new_lines [i ].options = old_lines [i ].options ;
476
+ new_lines [i ].id = old_lines [i ].id ;
477
+ new_lines [i ].parent_id = old_lines [i ].parent_id ;
478
+ }
479
+ new_lines [line_count ].mountpoint = xstrdup (dest );
480
+ new_lines [line_count ].options = new_lines [parent_id ].options ;
481
+ new_lines [line_count ].id = max_mount_id (old_lines , line_count ) + 1 ;
482
+ new_lines [line_count ].parent_id = parent_id ;
483
+
484
+ free (old_lines );
485
+
486
+ return new_lines ;
487
+ }
488
+
376
489
int
377
490
bind_mount (int proc_fd ,
378
491
const char * src ,
379
492
const char * dest ,
380
493
bind_option_t options )
381
494
{
495
+ static MountInfoLines mountinfo = NULL ;
496
+ static unsigned int mount_count = 0 ;
497
+
382
498
bool readonly = (options & BIND_READONLY ) != 0 ;
383
499
bool devices = (options & BIND_DEVICES ) != 0 ;
384
500
bool recursive = (options & BIND_RECURSIVE ) != 0 ;
@@ -387,36 +503,40 @@ bind_mount (int proc_fd,
387
503
cleanup_free char * resolved_dest = NULL ;
388
504
int i ;
389
505
390
- if (src )
391
- {
392
- if (mount (src , dest , NULL , MS_SILENT | MS_BIND | (recursive ? MS_REC : 0 ), NULL ) != 0 )
393
- return 1 ;
394
- }
506
+ if (mountinfo == NULL )
507
+ mountinfo = read_mountinfo (proc_fd , & mount_count );
395
508
396
- /* The mount operation will resolve any symlinks in the destination
397
- path, so to find it in the mount table we need to do that too. */
509
+ /* The mount operation will resolve any symlinks in the destination path, so
510
+ we need to do that too. */
398
511
resolved_dest = realpath (dest , NULL );
399
512
if (resolved_dest == NULL )
400
513
return 2 ;
401
514
402
- mount_tab = parse_mountinfo (proc_fd , resolved_dest );
403
- if (mount_tab [0 ].mountpoint == NULL )
515
+ if (src )
404
516
{
405
- errno = EINVAL ;
406
- return 2 ; /* No mountpoint at dest */
517
+ if (mount (src , dest , NULL , MS_SILENT | MS_BIND | (recursive ? MS_REC : 0 ), NULL ) != 0 )
518
+ return 1 ;
519
+
520
+ mountinfo = add_mountinfo (mountinfo , mount_count , src , resolved_dest );
521
+ mount_count ++ ;
407
522
}
408
523
524
+ mount_tab = parse_mountinfo (mountinfo , mount_count , resolved_dest );
409
525
assert (path_equal (mount_tab [0 ].mountpoint , resolved_dest ));
526
+
410
527
current_flags = mount_tab [0 ].options ;
411
528
new_flags = current_flags | (devices ? 0 : MS_NODEV ) | MS_NOSUID | (readonly ? MS_RDONLY : 0 );
412
529
if (new_flags != current_flags &&
413
530
mount ("none" , resolved_dest ,
414
531
NULL , MS_SILENT | MS_BIND | MS_REMOUNT | new_flags , NULL ) != 0 )
415
532
return 3 ;
416
533
417
- /* We need to work around the fact that a bind mount does not apply the flags, so we need to manually
418
- * apply the flags to all submounts in the recursive case.
419
- * Note: This does not apply the flags to mounts which are later propagated into this namespace.
534
+ /* We need to work around the fact that a bind mount does not apply the
535
+ * flags, so we need to manually apply the flags to all submounts in the
536
+ * recursive case.
537
+ *
538
+ * Note: This does not apply the flags to mounts that are later propagated
539
+ * into this namespace.
420
540
*/
421
541
if (recursive )
422
542
{
0 commit comments