@@ -161,64 +161,79 @@ impl Filesystem {
161161 }
162162}
163163
164+ /// Keep only the specified subset of [`MountInfo`] instances.
165+ ///
166+ /// If `paths` is non-empty, this function excludes any [`MountInfo`]
167+ /// that is not mounted at the specified path.
168+ ///
169+ /// The `opt` argument specifies a variety of ways of excluding
170+ /// [`MountInfo`] instances; see [`Options`] for more information.
171+ ///
172+ /// Finally, if there are duplicate entries, the one with the shorter
173+ /// path is kept.
164174fn filter_mount_list ( vmi : Vec < MountInfo > , paths : & [ String ] , opt : & Options ) -> Vec < MountInfo > {
165- vmi. into_iter ( )
166- . filter_map ( |mi| {
167- if ( mi. remote && opt. show_local_fs )
168- || ( mi. dummy && !opt. show_all_fs && !opt. show_listed_fs )
169- || !opt. fs_selector . should_select ( & mi. fs_type )
170- {
171- None
172- } else {
173- if paths. is_empty ( ) {
174- // No path specified
175- return Some ( ( mi. dev_id . clone ( ) , mi) ) ;
176- }
177- if paths. contains ( & mi. mount_dir ) {
178- // One or more paths have been provided
179- Some ( ( mi. dev_id . clone ( ) , mi) )
180- } else {
181- // Not a path we want to see
182- None
183- }
184- }
185- } )
186- . fold (
187- HashMap :: < String , Cell < MountInfo > > :: new ( ) ,
188- |mut acc, ( id, mi) | {
189- #[ allow( clippy:: map_entry) ]
190- {
191- if acc. contains_key ( & id) {
192- let seen = acc[ & id] . replace ( mi. clone ( ) ) ;
193- let target_nearer_root = seen. mount_dir . len ( ) > mi. mount_dir . len ( ) ;
194- // With bind mounts, prefer items nearer the root of the source
195- let source_below_root = !seen. mount_root . is_empty ( )
196- && !mi. mount_root . is_empty ( )
197- && seen. mount_root . len ( ) < mi. mount_root . len ( ) ;
198- // let "real" devices with '/' in the name win.
199- if ( !mi. dev_name . starts_with ( '/' ) || seen. dev_name . starts_with ( '/' ) )
200- // let points towards the root of the device win.
201- && ( !target_nearer_root || source_below_root)
202- // let an entry over-mounted on a new device win...
203- && ( seen. dev_name == mi. dev_name
204- /* ... but only when matching an existing mnt point,
205- to avoid problematic replacement when given
206- inaccurate mount lists, seen with some chroot
207- environments for example. */
208- || seen. mount_dir != mi. mount_dir )
209- {
210- acc[ & id] . replace ( seen) ;
211- }
212- } else {
213- acc. insert ( id, Cell :: new ( mi) ) ;
214- }
215- acc
216- }
217- } ,
218- )
219- . into_iter ( )
220- . map ( |ent| ent. 1 . into_inner ( ) )
221- . collect :: < Vec < _ > > ( )
175+ let mut mount_info_by_id = HashMap :: < String , Cell < MountInfo > > :: new ( ) ;
176+ for mi in vmi {
177+ // Don't show remote filesystems if `--local` has been given.
178+ if mi. remote && opt. show_local_fs {
179+ continue ;
180+ }
181+
182+ // Don't show pseudo filesystems unless `--all` has been given.
183+ if mi. dummy && !opt. show_all_fs && !opt. show_listed_fs {
184+ continue ;
185+ }
186+
187+ // Don't show filesystems if they have been explicitly excluded.
188+ if !opt. fs_selector . should_select ( & mi. fs_type ) {
189+ continue ;
190+ }
191+
192+ // Don't show filesystems other than the ones specified on the
193+ // command line, if any.
194+ if !paths. is_empty ( ) && !paths. contains ( & mi. mount_dir ) {
195+ continue ;
196+ }
197+
198+ // If the device ID has not been encountered yet, just store it.
199+ let id = mi. dev_id . clone ( ) ;
200+ #[ allow( clippy:: map_entry) ]
201+ if !mount_info_by_id. contains_key ( & id) {
202+ mount_info_by_id. insert ( id, Cell :: new ( mi) ) ;
203+ continue ;
204+ }
205+
206+ // Otherwise, if we have seen the current device ID before,
207+ // then check if we need to update it or keep the previously
208+ // seen one.
209+ let seen = mount_info_by_id[ & id] . replace ( mi. clone ( ) ) ;
210+ let target_nearer_root = seen. mount_dir . len ( ) > mi. mount_dir . len ( ) ;
211+ // With bind mounts, prefer items nearer the root of the source
212+ let source_below_root = !seen. mount_root . is_empty ( )
213+ && !mi. mount_root . is_empty ( )
214+ && seen. mount_root . len ( ) < mi. mount_root . len ( ) ;
215+ // let "real" devices with '/' in the name win.
216+ if ( !mi. dev_name . starts_with ( '/' ) || seen. dev_name . starts_with ( '/' ) )
217+ // let points towards the root of the device win.
218+ && ( !target_nearer_root || source_below_root)
219+ // let an entry over-mounted on a new device win...
220+ && ( seen. dev_name == mi. dev_name
221+ /* ... but only when matching an existing mnt point,
222+ to avoid problematic replacement when given
223+ inaccurate mount lists, seen with some chroot
224+ environments for example. */
225+ || seen. mount_dir != mi. mount_dir )
226+ {
227+ mount_info_by_id[ & id] . replace ( seen) ;
228+ }
229+ }
230+
231+ // Take ownership of the `MountInfo` instances and collect them
232+ // into a `Vec`.
233+ mount_info_by_id
234+ . into_values ( )
235+ . map ( |m| m. into_inner ( ) )
236+ . collect ( )
222237}
223238
224239/// Convert `value` to a human readable string based on `base`.
0 commit comments