1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
// Copyright (c) 2023 Nick Piaddo
// SPDX-License-Identifier: Apache-2.0 OR MIT
//! High-level API to mount/unmount devices.
//!
//! ## Description
//!
//! On Unix systems, files are organised around a file tree with `/` as its root. Files may be
//! spread out over several local or network devices, requiring specialised functions to connect
//! them to the file tree.
//!
//! The `mount` module provides the necessary tools:
//! - to attach, or detach, file systems found on a device to a node of the file tree (usually
//! called *mount point*),
//! - to make the contents of a device accessible at different nodes of the file tree (called *bind
//! mounts*),
//! - to move the location of a mount point in the tree,
//! - to create mirrors of a mount point that will propagate mount/unmount events within any of
//! its mirror group.
//!
//! The [`Mount`] struct is the main entry-point to perform the actions outlined above.
//!
//! ## Examples
//!
//! To mount/unmount a device, a user needs to have the necessary permissions by either being the
//! `root` user, or being authorized by the `root` user to perform the operations described below.
//!
//! **Note:** When configuring a [`Mount`] object, all the examples below use the
//! [`MountBuilder::dry_run`] function to simulate mount/unmount operations; remove it from code
//! listings to effectively perform the actions described.
//!
//! ### Mount a local device
//!
//! The following code shows how to attach a local device to the file tree. It tells the kernel to
//! mount the `ext4` file system found on `/dev/vda` at the `/mnt` directory.
//!
//! ```
//! use rsmount::device::BlockDevice;
//! use rsmount::flags::MountFlag;
//! use rsmount::fs::FileSystem;
//! use rsmount::mount::Mount;
//!
//! fn main() -> rsmount::Result<()> {
//! // Configure the `Mount` struct.
//! let block_device: BlockDevice = "/dev/vda".parse()?;
//! let mut mount = Mount::builder()
//! // Device to mount.
//! .source(block_device)
//! // Location of the mount point in the file tree.
//! .target("/mnt")
//! // Do not allow writing to the file system while it is mounted.
//! .mount_flags(vec![MountFlag::ReadOnly])
//! // Gives a hint about the file system used by the device (optional).
//! .file_system(FileSystem::Ext4)
//! // Skips all mount source preparation, mount option analysis, and the actual mounting
//! // process.
//! .dry_run()
//! .build()?;
//!
//! // Mount `/dev/vda` at `/mnt`.
//! mount.mount_device()?;
//!
//! Ok(())
//! }
//! ```
//!
//! ### Mount a network device
//!
//! You can also attach a network device.
//!
//! ```
//! use rsmount::device::NFS;
//! use rsmount::flags::MountFlag;
//! use rsmount::fs::FileSystem;
//! use rsmount::mount::Mount;
//!
//! fn main() -> rsmount::Result<()> {
//! // Configure the `Mount` struct.
//! let net_device: NFS = "knuth.cwi.nl:/nfs/share".parse()?;
//! let mut mount = Mount::builder()
//! // Device to mount.
//! .source(net_device)
//! // Location of the mount point in the file tree.
//! .target("/net/share")
//! // Do not allow writing to the file system while it is mounted.
//! .mount_flags(vec![MountFlag::ReadOnly])
//! // Gives a hint about the file system used by the device (optional).
//! .file_system(FileSystem::NFS)
//! // Skips all mount source preparation, mount option analysis, and the actual mounting
//! // process.
//! .dry_run()
//! .build()?;
//!
//! // Mount `knuth.cwi.nl:/nfs/share` at `/net/share`.
//! mount.mount_device()?;
//!
//! Ok(())
//! }
//! ```
//!
//! ### Mount a device in `/etc/fstab`
//!
//! In the examples above, we provided values for the `source` and `target` configuration methods.
//! If we however set only one of them, the resulting `Mount` will look for the missing parameter
//! in `/etc/fstab`.
//!
//! Assuming the following content in `/etc/fstab`...
//!
//! ```text
//! # /etc/fstab
//! # Alpine Linux 3.19 (installed from alpine-virt-3.19.1-x86_64.iso)
//! #
//!
//! UUID=dd476616-1ce4-415e-9dbd-8c2fa8f42f0f / ext4 rw,relatime 0 1
//! UUID=07aae9ba-12a1-4325-8b16-2ee214a6d7fd /boot ext4 noauto,rw,relatime 0 2
//! UUID=b9d72af2-f231-4cf8-9d0a-ba19e94a5087 swap swap defaults 0 0
//!
//! /dev/cdrom /media/cdrom iso9660 noauto,ro 0 0
//! /dev/usbdisk /media/usb vfat noauto 0 0
//! none /tmp tmpfs nosuid,nodev 0 0
//! ```
//!
//! ...we can manually mount the boot device mentioned in `/etc/fstab` by only configuring its target
//! mount point (i.e. `/boot`).
//!
//! The resulting [`Mount`] object will use the file system type (`ext4`), and mount options in the
//! file (`noauto,rw,relatime`) to complete the operation.
//!
//! ```
//! use rsmount::mount::Mount;
//!
//! fn main() -> rsmount::Result<()> {
//! // Configure the `Mount` struct to mount the boot device.
//! let mut mount = Mount::builder()
//! // Location of the mount point in the file tree.
//! .target("/boot")
//! // Skips all mount source preparation, mount option analysis, and the actual mounting
//! // process.
//! .dry_run()
//! .build()?;
//!
//! // Mount the device with UUID=07aae9ba-12a1-4325-8b16-2ee214a6d7fd
//! mount.mount_device()?;
//!
//! Ok(())
//! }
//! ```
//!
//! ### Override the mount options, and mount a device in `/etc/fstab`
//!
//! You can override the mount options set in `/etc/fstab` when mounting a device. In the example
//! below, the original mount option `noauto` is replaced by `ro,exec` before `/dev/usbdisk` is
//! mounted at `/media/usb`.
//!
//! ```text
//! ...snip...
//! /dev/usbdisk /media/usb vfat noauto 0 0
//! none /tmp tmpfs nosuid,nodev 0 0
//! ```
//!
//! ```
//! use rsmount::device::BlockDevice;
//! use rsmount::mount::Mount;
//! use rsmount::mount::MountOptionsMode;
//!
//! fn main() -> rsmount::Result<()> {
//! // Configure the `Mount` struct to mount the boot device.
//! let block_device: BlockDevice = "/dev/usbdisk".parse()?;
//! let mut mount = Mount::builder()
//! // Device to mount.
//! .source(block_device)
//! // Comma-separated list of file system independent mount options.
//! // `ro`: mount the filesystem as read-only.
//! // `exec`: permit the execution of binaries and other executable files on the mounted
//! // device.
//! .mount_options("ro,exec")
//! // Ignore options in `/etc/fstab` for `/dev/usbdisk`.
//! .mount_options_mode(vec![MountOptionsMode::IgnoreOptions])
//! // Skips all mount source preparation, mount option analysis, and the actual mounting
//! // process.
//! .dry_run()
//! .build()?;
//!
//! // Mount `/dev/usbdisk` at `/media/usb`.
//! mount.mount_device()?;
//!
//! Ok(())
//! }
//! ```
//!
//! ### Mount all devices with a specific file system
//!
//! Assuming the following content in `/etc/fstab`...
//!
//! ```text
//! # /etc/fstab
//!
//! UUID=dd476616-1ce4-415e-9dbd-8c2fa8f42f0f / ext4 rw,relatime 0 1
//! UUID=07aae9ba-12a1-4325-8b16-2ee214a6d7fd /boot ext4 noauto,rw,relatime 0 2
//! UUID=b9d72af2-f231-4cf8-9d0a-ba19e94a5087 swap swap defaults 0 0
//!
//! /dev/cdrom /media/cdrom iso9660 noauto,ro 0 0
//! /dev/usbdisk /media/usb vfat noauto 0 0
//! none /tmp tmpfs nosuid,nodev 0 0
//! ```
//!
//! ...we can manually mount all devices with an `ext4` file system with the code below.
//!
//! ```
//! use rsmount::mount::Mount;
//! use rsmount::mount::StepResult;
//!
//! fn main() -> rsmount::Result<()> {
//! let mut mount = Mount::builder()
//! .match_file_systems("ext4")
//! // Skips all mount source preparation, mount option analysis, and the actual mounting
//! // process.
//! .dry_run()
//! .build()?;
//!
//! for result in mount.seq_mount() {
//! match result {
//! StepResult::MountAlreadyDone(entry) => {
//! let source = entry.source().unwrap();
//! let mount_point = entry.target().unwrap();
//!
//! eprintln!("Already mounted: {} at {:?}", source, mount_point);
//! }
//! StepResult::MountFail(entry) => {
//! let source = entry.source().unwrap();
//! let mount_point = entry.target().unwrap();
//!
//! eprintln!("Failed to mount: {} at {:?}", source, mount_point);
//! }
//! StepResult::MountSkipped(entry) => {
//! let mount_point = entry.target().unwrap();
//! eprintln!("Skipped: {:?}", mount_point);
//! }
//! StepResult::MountSuccess(entry) => {
//! let source = entry.source().unwrap();
//! let mount_point = entry.target().unwrap();
//!
//! eprintln!("Mounted: {} at {:?}", source, mount_point);
//! }
//! _ => unreachable!(),
//! }
//! }
//!
//! // Example output
//! //
//! // Already mounted: UUID=dd476616-1ce4-415e-9dbd-8c2fa8f42f0f at "/"
//! // Mounted: UUID=07aae9ba-12a1-4325-8b16-2ee214a6d7fd at "/boot"
//! // Skipped: "swap"
//! // Skipped: "/media/cdrom"
//! // Skipped: "/media/usb"
//! // Skipped: "/tmp"
//!
//! Ok(())
//! }
//! ```
//!
//! ### Unmount a device
//!
//! This example shows how to unmount the network device mounted [above](#mount-a-network-device).
//!
//! ```
//! use rsmount::mount::Unmount;
//!
//! fn main() -> rsmount::Result<()> {
//! // Configure the `Unmount` struct to umount a device.
//! let mut unmount = Unmount::builder()
//! // Location of the mount point in the file tree.
//! .target("/net/share")
//! // Force an unmount (in case of an unreachable NFS system).
//! .force_unmount()
//! // Skips all mount source preparation, mount option analysis, and the actual mounting
//! // process.
//! .dry_run()
//! .build()?;
//!
//! // Unmount the shared NFS device at knuth.cwi.nl:/nfs/share
//! unmount.unmount_device()?;
//!
//! Ok(())
//! }
//! ```
//!
//! ### Create a bind mount
//!
//! A *bind mount* is a way to remount a part of the file tree at another location, where the same
//! contents are accessible in addition to at the source mount point, after the bind operation
//! is complete.
//!
//! Note that in the example, `/bind/mnt/boot` will not give access to files from devices mounted
//! on a subdirectory of `/boot` unless the mount flag
//! [`MountFlag::Recursive`](crate::core::flags::MountFlag::Recursive) is set when configuring
//! a [`Mount`].
//!
//! ```
//! use rsmount::device::MountPoint;
//! use rsmount::flags::MountFlag;
//! use rsmount::mount::Mount;
//!
//! fn main() -> rsmount::Result<()> {
//! // Configure the `Mount` struct to create a bind mount of the boot device.
//! let mount_point: MountPoint = "/boot".parse()?;
//! let mut mount = Mount::builder()
//! .source(mount_point)
//! // Location of the mount point in the file tree.
//! .target("/bind/mnt/boot")
//! // Create a bind mount.
//! // Do not allow writing to the file system while it is mounted.
//! .mount_flags(vec![MountFlag::Bind, MountFlag::ReadOnly])
//! // Skips all mount source preparation, mount option analysis, and the actual mounting
//! // process.
//! .dry_run()
//! .build()?;
//!
//! // Create a read-only bind mount of `/boot` at `/bind/mnt/boot`.
//! mount.mount_device()?;
//!
//! // From now on, we can also access the files in `/boot` from the `/bind/mnt/boot`
//! // directory...
//!
//! Ok(())
//! }
//! ```
//!
//! ### Mark a mount point as `shared`
//!
//! Since Linux 2.6.15, it is possible to mark a mount point and its submounts as `shared`, `slave`,
//! `slave-and-shared`, `private`, or `unbindable`. A shared mount point has the ability to
//! propagate any mount/unmount event occurring on it (or one of its submounts) to members of its
//! peer group. See the next section for an in-depth explanation of [mount
//! namespaces](#mount-namespaces).
//!
//! ```ignore
//! use rsmount::device::MountPoint;
//! use rsmount::flags::MountFlag;
//! use rsmount::mount::Mount;
//!
//! fn main() -> rsmount::Result<()> {
//! let mut mount = Mount::builder()
//! // For a device already mounted at `/mnt/usbdisk`
//! .target("/mnt/usbdisk")
//! // Set the propagation type of this mount point to shared. Mount and unmount events on
//! // sub mount points will propagate to its peers.
//! .mount_flags(vec![MountFlag::Remount, MountFlag::Shared])
//! // Skips all mount source preparation, mount option analysis, and the actual mounting
//! // process.
//! .dry_run()
//! .build()?;
//!
//! // Mark `/bind/mnt/boot` as `shared`
//! mount.mount_device()?;
//!
//! Ok(())
//! }
//! ```
//!
//! ## Mount namespaces
//!
//! *This section borrows heavily from the excellent series of articles: __[Namespaces in
//! operation](https://lwn.net/Articles/531114/)__ by Michael Kerrisk.*
//!
//! When the system is first booted, there is a single mount namespace, the so-called ***initial
//! namespace***. This namespace holds a list of mount points representing all bind mounts, and
//! devices attached to the file system tree. Each mount point is identified by a unique integer,
//! or mount ID.
//!
//! The diagram below shows a simplified view of an initial namespace (`ns1`) containing one mount
//! point `/mnt` with mount ID `21`.
//!
//! ![Diagram of the initial namespace as a container (ns1) with a rectangular box inside. The box
//! represents a mount point named /mnt with mount ID 21.][fig-01]
//!
//! New mount namespaces are created by using the `CLONE_NEWNS` flag with either the
//! [`clone()`](http://man7.org/linux/man-pages/man2/clone.2.html) system call, to create a new child
//! process in the new namespace, or the
//! [`unshare()`](http://man7.org/linux/man-pages/man2/unshare.2.html) system call, to move the
//! caller into the new namespace.
//!
//! When a new mount namespace is created, it receives a copy of the list of mount points,
//! replicated from the namespace of the caller of `clone()` or `unshare()`. To preserve the
//! uniqueness of mount IDs, each copy of a mount point is assigned a new identifier.
//!
//! The diagram below shows the state of the system after creating a new namespace (`ns2`), where
//! the copy of `/mnt` is assigned a value of `42` as its new mount ID.
//!
//! ![Diagram of two namespace containers. On the left, the initial namespace container (ns1) with
//! a mount point named /mnt with mount ID 21, represented as a rectangular box. On the right, the
//! new namespace (ns2) with a copy of the same mount point but with mount ID
//! 42.][fig-02]
//!
//! By default, changes to the list of mount points in a namespace are only visible to processes
//! within the namespace. Up until the introduction of ***shared subtrees***, the only way to make
//! a new disk visible in all mount namespaces was to mount the disk, separately, in each namespace.
//!
//! For example, in the diagram below, although a USB disk is mounted at `/mnt/usb` in the
//! namespace `ns1`, the new mount point is not automatically replicated in `ns2.`
//!
//! ![This diagram has the same structure as in the preceding image, except for a new mount point
//! named `/usb` with mount ID 22, in the ns1 namespace, connected to the `/mnt` mount point by
//! a vertical line.][fig-03]
//!
//!
//! ### Shared subtrees
//!
//! Shared subtrees allow us to perform a single mount operation that makes a new disk visible in
//! all, or some subset, of the mount namespaces in a system. The key benefit of shared subtrees is
//! to allow automatic, controlled propagation of mount and unmount events between namespaces. This
//! means, for example, that mounting a disk in one mount namespace can trigger a mount of that
//! same disk in all other namespaces.
//!
//! Under the shared subtrees feature, each mount point is marked with a *propagation type*, which
//! determines whether operations creating and/or removing mount points below a given mount point,
//! in a specific namespace, are propagated to other mount points in other namespaces.
//!
//! Linux kernel developers defined four propagation types:
//! - **shared**: A mount point marked `shared` propagates mount and unmount events to other mount
//! points belonging to its *peer group* (peer groups are described in the [next
//! section](#peer-groups)). When a mount point is added (or removed) below a `shared` mount
//! point, changes will propagate to its peer group, so that a mount or unmount will also take
//! place below each of the peer mount points. Event propagation works both ways, from the mount
//! point to its peers and vice-versa.
//! - **private**: A mount point marked `private` is the converse of a `shared` mount point. A
//! `private` mount point does neither propagate events to, nor receive propagation events from
//! peers.
//! - **slave**: A mount point marked `slave` sits midway between `shared` and `private`. A slave
//! mount has as master, a shared peer group whose members propagate mount and unmount events to
//! the slave. However, the slave mount does not propagate events to the master peer group.
//! - **unbindable**: Like a `private` mount point, a mount point marked `unbindable` does neither
//! propagate to, nor receive events from peers. In addition, an `unbindable` mount point can't be
//! the source for a bind mount operation.
//!
//! By default, the kernel marks new mount points `private`, as long as their parent mount (if they
//! have one) is not marked `shared`. In that case, they will also be marked `shared`. We can now
//! redraw below the previous diagram, incorporating each mount point's propagation type.
//!
//! ![This diagram is a copy of the previous one where mount points a represented by blue squares.
//! A legend was added at the bottom of the image, showing a blue square which represents a private
//! mount point, a yellow circle represents a shared mount point, a red diamond represents a slave
//! mount point, an orange oval represents a slave-shared mount point, and finally a purple hexagon
//! stands for an unbindable mount point.][fig-04]
//!
//! It is worth expanding on a few points that were glossed over.
//!
//! First, setting propagation types is a **per-mount-point** business. Within a namespace, some
//! mount points might be marked `shared`, while others are marked `private`, `slave` or
//! `unbindable`.
//!
//! Second, the propagation type of a marked mount point determines the propagation of
//! mount/unmount events of mount points **immediately below** it.
//!
//! In passing, it is perhaps worth clarifying that the word *event* is used here as an abstract
//! term, in the sense of *something happened*. The notion of event propagation does not imply some
//! sort of message passing between mount points. It rather carries the idea that a mount/unmount
//! operation, on one mount point, triggers a matching operation on at least one other mount point.
//!
//! Finally, it is possible for a mount point to be both the slave of a master peer group, as well
//! as share events with a set of peers of its own, a so-called **slave-and-shared** mount. In this
//! case, the mount point might receive propagation events from its master, and then forward them
//! to its peers.
//!
//! ### Peer groups
//!
//! A peer group is a set of mount points that propagate mount and unmount events to one another. A
//! peer group acquires a new member when a mount point with a `shared` propagation type is, either
//! replicated during the creation of a new namespace, or used as the source for a bind mount.
//! For a bind mount, the details are more complex than we describe here; you can find more
//! information in the kernel source file
//! [Documentation/filesystems/sharedsubtree.txt](https://www.kernel.org/doc/Documentation/filesystems/sharedsubtree.txt).
//!
//! In both cases, the new mount point is made a member of the same peer group as the existing
//! mount point. Conversely, a mount point ceases to be a member of a peer group when it is
//! unmounted, either explicitly, or implicitly when a mount namespace is torn down because the
//! last member process terminates or moves to another namespace.
//!
//! For example, let's assume we have a namespace (ns1) with a mount point `/mnt` (mount ID 21). If
//! we mark this mount point as `shared`, and then create a new process in a new namespace (ns2),
//! the list of mount points will be replicated. As we have explained earlier, each copy of a
//! mount point will be assigned a new mount ID. In this case, the copy of `/mnt` will be assigned
//! the mount ID 42.
//!
//! Since `/mnt` is `shared`, a new peer group (peer group 1) will be created with `/mnt` and its
//! copy as members (see diagram below). They will share every mount/unmount event as long as they
//! keep their `shared` propagation type.
//!
//! ![][fig-05]
//!
//! Now if we mount a USB disk below `/mnt` at `/mnt/usb` in ns1, an event will be propagated
//! to ns2; making the USB disk accessible from ns2 without manual intervention. The new mount
//! point will be automatically marked `shared`, and added, with its copy, to a new peer group
//! (peer group 2).
//!
//! ![][fig-06]
//!
//! ### Effects of propagation type transitions on peer groups
//!
//! In each namespace, processes equipped with the `CAP_SYS_ADMIN` capability can modify the propagation
//! type of their mount points. Here, we will study the effects a change of a mount point's
//! propagation type can have on peer groups.
//!
//! #### Effects of a `shared` mount point
//!
//! Let's start with `ns1` the initial namespace with a `shared` mount point `/mnt` (mount ID 21).
//! From `ns1`, we spawn two new child processes using the `CLONE_NEWNS` flag with the
//! [`clone()`](http://man7.org/linux/man-pages/man2/clone.2.html) system call. This will create
//! two new namespaces, `ns2`, and `ns3` triggering a replication of `/mnt` whose copies will be
//! assigned new mount IDs, respectively 22 and 23.
//!
//! Since `/mnt` is a `shared` mount point, a new peer group (peer group 1) is formed with all
//! replicas as its members. Each peer will share mount/unmount events with all its
//! counterparts, as shown by the fully connected graph of peers in the diagram below.
//!
//! ![][fig-07]
//!
//! #### Effects of a `slave` mount point
//!
//! Now, let's switch the propagation type of `/mnt` in `ns2` (mount ID 22) from `shared` to
//! `slave`, meaning from now on `/mnt` in `ns2` will receive mount/unmount events from its peers,
//! but won't send back new events occurring in `ns2`. This removes peer group arrows, in the
//! diagram above, pointing from `/mnt` with mount ID 22 to `/mnt` with mount ID 21, and `/mnt`
//! with mount ID 23. Bi-directional communication between the replicas with mount ID 21 and 23 is
//! preserved.
//!
//! ![][fig-08]
//!
//! If we mount a USB disk on `/mnt/usb` in `ns1`, the new mount point (`/usb`, mount ID
//! 24) will be propagated to its peers. `/mnt` (mount ID 21) in `ns1` is marked as `shared`, so
//! `/usb` (mount Id 24) will be marked `shared` as well. A new peer group (peer group 2) will
//! be created with copies of the new mount point.
//!
//! However, we previously switched the propagation type of `/mnt` (mount ID 22) in `ns2` to
//! `slave`, the `/usb` copy in `ns2` will also get a `slave` marking to conform to the state
//! of its parent mount point (see next diagram).
//!
//! ![][fig-09]
//!
//! If instead of mounting the USB disk on a mount point in `ns1`, we mount it on `/mnt/usb` in
//! `ns2`. The `/mnt` mount point (mount ID 22) in `ns2` has a `slave` propagation type. So, as we
//! stated in the section [Shared subtrees](#shared-subtree), the new mount point `/usb` (mount
//! ID 24) will be set to `private` since its parent mount point is not marked `shared`.
//!
//! As such, it will neither receive nor send mount/unmount events, and will not be replicated in
//! any other namespace. Therefore, there is no need to create a new peer group.
//!
//! Here is diagram depicting this case.
//!
//! ![][fig-10]
//!
//! #### Effects of a `slave-and-shared` mount point
//!
//! Let's revert to a new initial state, a namespace `ns1` with a `shared` mount point `/mnt` (mount
//! ID 21). From `ns1`, we spawn one child process using the `CLONE_NEWNS` flag with the
//! [`clone()`](http://man7.org/linux/man-pages/man2/clone.2.html) system call. This will create
//! a new namespace, `ns2`, triggering a replication of `/mnt` whose copy will be assigned a new
//! mount ID 22.
//!
//! They will both be members of peer group 1 with a bi-directional communication channel for
//! propagating mount/unmount events.
//!
//! ![][fig-11]
//!
//! Now, let's switch the propagation type of `/mnt` (mount ID 22) in `ns2` from `shared` to
//! `slave-and-shared`. This change has two consequences:
//! - from `ns1`'s point of view, the mount point in `ns2` (mount ID 22) is now `slave` to its
//! `/mnt` mount point (mount ID 21). In peer group 1, the previous bi-directional channel between
//! `/mnt` mount ID 21 and its copy `/mnt` mount ID 22 is now unidirectional, with `/mnt` mount ID 22
//! set as `slave`.
//! - from `ns2`'s point of view, `/mnt` mount ID 22 is a `shared` mount point, thus a new peer
//! group is created, adding it as a member (peer group 2). See the diagram below.
//!
//! ![][fig-12]
//!
//! For the purpose of our demonstration, we will call `ns2` the "child" namespace of `ns1`. From a
//! process in this "child" namespace, we spawn a new process in a new namespace, `ns3`.
//!
//! So, `ns3` is the "child" of `ns2`, and thus logically the "grandchild" of `ns1`. As usual the
//! list of mount points in `ns2` is copied to `ns3`, updating the mount IDs to keep their
//! uniqueness. In this case, a copy of `/mnt` mount ID 22 is created and is assigned the mount ID
//! 23. This new mount point will have the same propagation type as its parent, i.e.
//! `slave-and-shared`.
//!
//! As a mount point in a "grandchild" namespace of `ns1`, it will gain membership to peer
//! group 1 as a `slave` mount point, receiving but not transmitting mount/unmount events to peers
//! in its peer group.
//!
//! At the same time, as a mount point in a "child" namespace of `ns2`, it will be added to peer
//! group 2 as a `shared` mount point.
//!
//! ![][fig-13]
//!
//! How would this configuration fare if we were to mount a USB disk on `/mnt/usb` in `ns1`? This
//! is where thing get interesting!
//!
//! `/mnt` in `ns1` is a `shared` mount point, thus any mount point created directly below it will
//! be `shared`. In the example below, a mount point named `/usb` with mount ID 24 is created. Since it is
//! a new `shared` mount point, it is added to a new peer group (i.e. peer group 3).
//!
//! Now, time to deal with the mount event propagation to the copy of `/mnt` in `ns2`.
//!
//! Looking at peer group 1, we have `/mnt` mount ID 22 set as a `slave` mount point in `ns2`; propagating
//! the mount event to it should create a new `slave` mount point in `ns2` (`/usb` mount ID 25), and add a `slave` copy
//! to peer group 3.
//!
//! However to peer group 2, `/mnt` mount ID 22 is a `shared` mount point, so `/usb` mount ID 25
//! should also be marked `shared`. Taking this into account, `/usb` mount ID 25 adopts a
//! `slave-and-shared` marking. Its `shared` status implies the creation of a new peer group to
//! which it will added (peer group 4).
//!
//! With the same reasoning, we add a `slave-and-shared` `/usb` mount point with mount ID 26 to `ns3`, a
//! corresponding `slave` copy to peer group 3, and a `shared` copy to peer group 4.
//!
//! ![][fig-14]
//!
//! Now if instead of mounting the USB disk on `/mnt/usb` in `ns1` we chose to mount it on `/mnt/usb`
//! in `ns3`, what would happen?
//!
//! Well, that would create a new mount point `/usb` (mount ID 25) connected to `/mnt` mount ID 23,
//! which as a `slave` according to its status in peer group 1 shouldn't propagate the event. So,
//! `/mnt` mount ID 21 and 22 shouldn't be notified.
//!
//! However, `/mnt` mount ID 23 is a `shared` mount point from peer group 2's point of view, which
//! implies a bi-directional communication channel with `/mnt` mount ID 22. The `shared` status of
//! `/mnt` mount ID 22 in peer group 2 supersedes its `slave` status in peer group 1. We thus need to
//! create new `/usb` mount point in `ns2`.
//!
//! What propagation type should the new `/usb` mount points have? `/mnt` mount ID 22 and 23 have a
//! `slave-and-shared` propagation type, in this situation their `shared` status takes precedence
//! over their `slave` status when choosing the propagation type of a new child mount point. As we
//! have seen before, a child of a `shared` mount point is also `shared`. Furthermore, a `shared`
//! mount point triggers the creation of a new peer group, in this example peer group 3, to which it
//! is added.
//!
//! To sum up, we have to create two new `shared` `/usb` mount points (mount ID 25 and 26), one in
//! `ns3` the other in `ns2` connected to their respective `/mnt` parent. They will also gain
//! membership to a new peer group, peer group 3 (see the diagram below).
//!
//! ![][fig-15]
//!
//! #### Effects of a `private` mount point
//!
//! Back to an initial state as in the previous section ([Effects of a `slave-and-shared` mount
//! point](#effects-of-a-slave-and-shared-mount-point)):
//! - two namespaces `ns1` and `ns2`, each with a `shared` mount point `/mnt` (respectively mount
//! ID 21 and 22),
//! - one peer group (peer group 1) with the `shared` mount points as members.
//!
//! ![][fig-11]
//!
//! What effect would switching `/mnt` mount ID 22 in `ns2` from `shared` to `private`? We know
//! that a `private` mount point does neither propagate events to, nor receive propagation events
//! from peers. So, whatever happens to `/mnt` in `ns2` will not be propagated to its peer in `ns1`,
//! and vice-versa. A new child mount point of `/mnt` in `ns2` will inherit its `private` status,
//! not transmitting events.
//!
//! ![][fig-16]
//!
//! #### Effects of an `unbindable` mount point
//!
//! An `unbindable` mount point is first and foremost a `private` mount point, with the added
//! property of not accepting bind operations. As such, it has the same behaviour as a `private`
//! mount point (for more information see the [previous section](#effects-of-a-private-mount-point)).
pub use ErrorCode;
pub use ExitCode;
pub use ExitStatus;
pub use MountBuilderError;
use MntBuilder;
pub use MountBuilder;
pub use MountError;
pub use MountIterError;
pub use MountIter;
pub use MountNamespace;
pub use MountOptionsMode;
pub use MountSource;
pub use Mount;
pub use ProcessExitStatus;
pub use ReMountIterError;
pub use ReMountIter;
pub use StepResult;
pub use UMountIterError;
pub use UMountIter;
pub use UMountNamespace;
pub use UnmountBuilderError;
use UmntBuilder;
pub use UnmountBuilder;
pub use UnmountError;
pub use Unmount;