@@ -137,6 +137,8 @@ mount_tab_free (MountTab tab)
137137{
138138 int i ;
139139
140+ /* An allocated MountTab always ends with a zeroed MountInfo, so we can tell
141+ when to stop freeing memory. */
140142 for (i = 0 ; tab [i ].mountpoint != NULL ; i ++ )
141143 free (tab [i ].mountpoint );
142144 free (tab );
@@ -155,15 +157,17 @@ cleanup_mount_tabp (void *p)
155157
156158typedef struct MountInfoLine MountInfoLine ;
157159struct MountInfoLine {
158- const char * mountpoint ;
159- const char * options ;
160+ char * mountpoint ;
161+ unsigned long options ;
160162 bool covered ;
161163 int id ;
162164 int parent_id ;
163165 MountInfoLine * first_child ;
164166 MountInfoLine * next_sibling ;
165167};
166168
169+ typedef MountInfoLine * MountInfoLines ;
170+
167171static unsigned int
168172count_lines (const char * data )
169173{
@@ -211,7 +215,7 @@ collect_mounts (MountInfo *info, MountInfoLine *line)
211215 if (!line -> covered )
212216 {
213217 info -> mountpoint = xstrdup (line -> mountpoint );
214- info -> options = decode_mountoptions ( line -> options ) ;
218+ info -> options = line -> options ;
215219 info ++ ;
216220 }
217221
@@ -225,21 +229,15 @@ collect_mounts (MountInfo *info, MountInfoLine *line)
225229 return info ;
226230}
227231
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 )
231235{
232236 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 ;
238239 char * line ;
239240 int i ;
240- int max_id ;
241- unsigned int n_lines ;
242- int root ;
243241
244242 mountinfo = load_file_at (proc_fd , "self/mountinfo" );
245243 if (mountinfo == NULL )
@@ -248,10 +246,8 @@ parse_mountinfo (int proc_fd,
248246 n_lines = count_lines (mountinfo );
249247 lines = xcalloc (n_lines * sizeof (MountInfoLine ));
250248
251- max_id = 0 ;
252249 line = mountinfo ;
253250 i = 0 ;
254- root = -1 ;
255251 while (* line != 0 )
256252 {
257253 int rc , consumed = 0 ;
@@ -289,30 +285,68 @@ parse_mountinfo (int proc_fd,
289285 options_end = rest ;
290286
291287 * mountpoint_end = 0 ;
292- lines [i ].mountpoint = unescape_inline (mountpoint );
288+ lines [i ].mountpoint = xstrdup ( unescape_inline (mountpoint ) );
293289
294290 * 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 ;
296308
309+ max_id = 0 ;
310+ for (i = 0 ; i < n_lines ; i ++ )
311+ {
297312 if (lines [i ].id > max_id )
298313 max_id = lines [i ].id ;
299314 if (lines [i ].parent_id > max_id )
300315 max_id = lines [i ].parent_id ;
316+ }
317+ return max_id ;
318+ }
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 ;
301332
333+ root = -1 ;
334+ for (i = 0 ; i < n_lines ; i ++ )
335+ {
302336 if (path_equal (lines [i ].mountpoint , root_mount ))
303337 root = i ;
304-
305- i ++ ;
306- line = next_line ;
307338 }
308- assert (i == n_lines );
309-
310339 if (root == -1 )
311340 {
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 ));
313344 return steal_pointer (& mount_tab );
314345 }
315346
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 );
316350 by_id = xcalloc ((max_id + 1 ) * sizeof (MountInfoLine * ));
317351 for (i = 0 ; i < n_lines ; i ++ )
318352 by_id [lines [i ].id ] = & lines [i ];
@@ -338,18 +372,18 @@ parse_mountinfo (int proc_fd,
338372 sibling = parent -> first_child ;
339373 while (sibling != NULL )
340374 {
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. */
344378 if (has_path_prefix (this -> mountpoint , sibling -> mountpoint ))
345379 {
346380 covered = TRUE;
347381 break ;
348382 }
349383
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.
353387 */
354388 if (has_path_prefix (sibling -> mountpoint , this -> mountpoint ))
355389 * to_sibling = sibling -> next_sibling ;
@@ -365,20 +399,100 @@ parse_mountinfo (int proc_fd,
365399 }
366400
367401 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 ));
369405
370406 end_tab = collect_mounts (& mount_tab [0 ], & lines [root ]);
371407 assert (end_tab == & mount_tab [n_mounts ]);
372408
373409 return steal_pointer (& mount_tab );
374410}
375411
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+ unsigned int options )
465+ {
466+ MountInfoLines new_lines ;
467+ int i ;
468+
469+ new_lines = xcalloc ((line_count + 1 ) * sizeof (MountInfoLine ));
470+ for (i = 0 ; i < line_count ; i ++ )
471+ {
472+ new_lines [i ].mountpoint = old_lines [i ].mountpoint ;
473+ new_lines [i ].options = old_lines [i ].options ;
474+ new_lines [i ].id = old_lines [i ].id ;
475+ new_lines [i ].parent_id = old_lines [i ].parent_id ;
476+ }
477+ free (old_lines );
478+
479+ new_lines [i ].mountpoint = xstrdup (dest );
480+ new_lines [i ].options = options ;
481+ new_lines [i ].id = max_mount_id (old_lines , line_count ) + 1 ;
482+ new_lines [i ].parent_id = find_parent (old_lines , line_count , dest );
483+
484+ return new_lines ;
485+ }
486+
376487int
377488bind_mount (int proc_fd ,
378489 const char * src ,
379490 const char * dest ,
380491 bind_option_t options )
381492{
493+ static MountInfoLines mountinfo = NULL ;
494+ static unsigned int mount_count = 0 ;
495+
382496 bool readonly = (options & BIND_READONLY ) != 0 ;
383497 bool devices = (options & BIND_DEVICES ) != 0 ;
384498 bool recursive = (options & BIND_RECURSIVE ) != 0 ;
@@ -387,36 +501,43 @@ bind_mount (int proc_fd,
387501 cleanup_free char * resolved_dest = NULL ;
388502 int i ;
389503
390- if (src )
391- {
392- if (mount (src , dest , NULL , MS_SILENT | MS_BIND | (recursive ? MS_REC : 0 ), NULL ) != 0 )
393- return 1 ;
394- }
504+ if (mountinfo == NULL ) {
505+ mountinfo = read_mountinfo (proc_fd , & mount_count );
506+ }
395507
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. */
508+ /* The mount operation will resolve any symlinks in the destination path, so
509+ we need to do that too. */
398510 resolved_dest = realpath (dest , NULL );
399511 if (resolved_dest == NULL )
400512 return 2 ;
401513
402- mount_tab = parse_mountinfo (proc_fd , resolved_dest );
403- if (mount_tab [0 ].mountpoint == NULL )
514+ if (src )
404515 {
405- errno = EINVAL ;
406- return 2 ; /* No mountpoint at dest */
516+ unsigned int options = MS_BIND | (recursive ? MS_REC : 0 );
517+
518+ if (mount (src , dest , NULL , MS_SILENT | options , NULL ) != 0 )
519+ return 1 ;
520+ /* XXXXXXXXXXXXXXXXXXXX Are we setting options correctly here? */
521+ mountinfo = add_mountinfo (mountinfo , mount_count , src , resolved_dest , options );
522+ mount_count ++ ;
407523 }
408524
525+ mount_tab = parse_mountinfo (mountinfo , mount_count , resolved_dest );
409526 assert (path_equal (mount_tab [0 ].mountpoint , resolved_dest ));
527+
410528 current_flags = mount_tab [0 ].options ;
411529 new_flags = current_flags | (devices ? 0 : MS_NODEV ) | MS_NOSUID | (readonly ? MS_RDONLY : 0 );
412530 if (new_flags != current_flags &&
413531 mount ("none" , resolved_dest ,
414532 NULL , MS_SILENT | MS_BIND | MS_REMOUNT | new_flags , NULL ) != 0 )
415533 return 3 ;
416534
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.
535+ /* We need to work around the fact that a bind mount does not apply the
536+ * flags, so we need to manually apply the flags to all submounts in the
537+ * recursive case.
538+ *
539+ * Note: This does not apply the flags to mounts that are later propagated
540+ * into this namespace.
420541 */
421542 if (recursive )
422543 {
0 commit comments