@@ -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 ) ;
296292
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 (const MountInfoLines lines ,
304+ unsigned int n_lines )
305+ {
306+ int i ;
307+ int max_id ;
308+
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 (const 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,58 +399,153 @@ 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+ const char * mountpoint )
416+ {
417+ cleanup_free char * prefix = NULL ;
418+ int parent ;
419+ const char * start ;
420+ bool mount_found ;
421+ int i ;
422+
423+ prefix = xcalloc (strlen (mountpoint ) + 1 );
424+ prefix [0 ] = '/' ;
425+
426+ parent = -1 ;
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
445+ && (parent == -1 || lines [i ].parent_id == lines [parent ].id ))
446+ {
447+ parent = i ;
448+ mount_found = 1 ;
449+ break ;
450+ }
451+ }
452+ }
453+ while (mount_found );
454+ }
455+ while (start != NULL );
456+
457+ return parent ;
458+ }
459+
460+ static MountInfoLines
461+ add_mountinfo (MountInfoLines old_lines ,
462+ unsigned int line_count ,
463+ const char * src ,
464+ char * dest )
465+ {
466+ MountInfoLines new_lines ;
467+ int src_parent ;
468+ int dest_parent ;
469+ int i ;
470+
471+ src_parent = find_parent (old_lines , line_count , src );
472+ dest_parent = find_parent (old_lines , line_count , dest );
473+
474+ new_lines = xcalloc ((line_count + 1 ) * sizeof (MountInfoLine ));
475+ for (i = 0 ; i < line_count ; i ++ )
476+ {
477+ new_lines [i ].mountpoint = old_lines [i ].mountpoint ;
478+ new_lines [i ].options = old_lines [i ].options ;
479+ new_lines [i ].id = old_lines [i ].id ;
480+ new_lines [i ].parent_id = old_lines [i ].parent_id ;
481+ }
482+ new_lines [line_count ].mountpoint = xstrdup (dest );
483+ new_lines [line_count ].options = old_lines [src_parent ].options ;
484+ new_lines [line_count ].id = max_mount_id (old_lines , line_count ) + 1 ;
485+ new_lines [line_count ].parent_id = old_lines [dest_parent ].id ;
486+
487+ free (old_lines );
488+
489+ return new_lines ;
490+ }
491+
376492int
377493bind_mount (int proc_fd ,
378494 const char * src ,
379495 const char * dest ,
380496 bind_option_t options )
381497{
498+ static MountInfoLines mountinfo = NULL ;
499+ static unsigned int mount_count = 0 ;
500+
382501 bool readonly = (options & BIND_READONLY ) != 0 ;
383502 bool devices = (options & BIND_DEVICES ) != 0 ;
384503 bool recursive = (options & BIND_RECURSIVE ) != 0 ;
385504 unsigned long current_flags , new_flags ;
386505 cleanup_mount_tab MountTab mount_tab = NULL ;
506+ cleanup_free char * resolved_src = NULL ;
387507 cleanup_free char * resolved_dest = NULL ;
388508 int i ;
389509
390- if (src )
391- {
392- if (mount (src , dest , NULL , MS_SILENT | MS_BIND | (recursive ? MS_REC : 0 ), NULL ) != 0 )
393- return 1 ;
394- }
510+ if (mountinfo == NULL )
511+ mountinfo = read_mountinfo (proc_fd , & mount_count );
395512
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. */
513+ /* The mount operation will resolve any symlinks in the destination path, so
514+ we need to do that too. */
398515 resolved_dest = realpath (dest , NULL );
399516 if (resolved_dest == NULL )
400517 return 2 ;
401518
402- mount_tab = parse_mountinfo (proc_fd , resolved_dest );
403- if (mount_tab [0 ].mountpoint == NULL )
519+ if (src )
404520 {
405- errno = EINVAL ;
406- return 2 ; /* No mountpoint at dest */
521+ if (mount (src , dest , NULL , MS_SILENT | MS_BIND | (recursive ? MS_REC : 0 ), NULL ) != 0 )
522+ return 1 ;
523+
524+ /* The mount operation will resolve any symlinks in the source path, so
525+ we need to do that too. */
526+ resolved_src = realpath (src , NULL );
527+ if (resolved_src == NULL )
528+ return 4 ;
529+ mountinfo = add_mountinfo (mountinfo , mount_count , resolved_src , resolved_dest );
530+ mount_count ++ ;
407531 }
408532
533+ mount_tab = parse_mountinfo (mountinfo , mount_count , resolved_dest );
409534 assert (path_equal (mount_tab [0 ].mountpoint , resolved_dest ));
535+
410536 current_flags = mount_tab [0 ].options ;
411537 new_flags = current_flags | (devices ? 0 : MS_NODEV ) | MS_NOSUID | (readonly ? MS_RDONLY : 0 );
412538 if (new_flags != current_flags &&
413539 mount ("none" , resolved_dest ,
414540 NULL , MS_SILENT | MS_BIND | MS_REMOUNT | new_flags , NULL ) != 0 )
415541 return 3 ;
416542
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.
543+ /* We need to work around the fact that a bind mount does not apply the
544+ * flags, so we need to manually apply the flags to all submounts in the
545+ * recursive case.
546+ *
547+ * Note: This does not apply the flags to mounts that are later propagated
548+ * into this namespace.
420549 */
421550 if (recursive )
422551 {
0 commit comments