We talked about it in the bug, Andriy / Warner proposed a concrete
implementation, and Kirk was supportive. So let's actually do the thing!
This Differential is prompted by real-world pain with a sometimes-RO device
that does not report WP (nor SWP) correctly. (It is sometimes RW; we cannot
blacklist it with a quirk.)
Basic `ggate` example repro from the PR comment #5 seems to work fine.
```
$ dd if=/dev/zero of=./image bs=1M count=100
$ newfs_msdos ./image
$ ggatel create -o ro ./image
ggate0
$ mount -t msdosfs /dev/ggate0 /mnt/tst ; echo $?
g_vfs_done():ggate0[WRITE(offset=512, length=4096)]error = 1
mount_msdosfs: /dev/ggate0: Operation not permitted
1
$ sysctl vfs.numdirtybuffers
vfs.numdirtybuffers: 0
```
Further testing with `gnop` to ensure similar handling across a range of possible errors.
==========================================================================
ENODEV:
```
$ mdconfig -a -t swap -s 100M
md0
$ newfs_msdos /dev/md0
// ENODEV = 19
$ gnop create -e 19 -r 0 -w 100 -v md0
GEOM_NOP: Device md0.nop created.
$ mount -t msdosfs /dev/md0.nop /mnt/tst ; echo $?
g_vfs_done():md0.nop[WRITE(offset=512, length=4096)]error = 19
vfs_donmount: R/W mount failed, possibly R/O media, trying R/O mount
0
// Oops, autoro.
$ mount
...
/dev/md0.nop on /mnt/tst (msdosfs, local, read-only)
$ umount /mnt/tst
// Try again with explicit -o rw.
$ mount -t msdosfs -o rw /dev/md0.nop /mnt/tst ; echo $?
g_vfs_done():md0.nop[WRITE(offset=512, length=4096)]error = 19
mount_msdosfs: /dev/md0.nop: Operation not supported by device
1
$ sysctl vfs.numdirtybuffers
vfs.numdirtybuffers: 0
```
EIO:
```
$ gnop destroy md0.nop
$ gnop create -e 5 -r 0 -w 100 -v md0
$ mount -t msdosfs -o rw /dev/md0.nop /mnt/tst ; echo $?
g_vfs_done():md0.nop[WRITE(offset=512, length=4096)]error = 5
mount_msdosfs: /dev/md0.nop: Input/output error
1
```
EACCES:
```
$ gnop destroy md0.nop
$ gnop create -e 13 -r 0 -w 100 -v md0
$ mount -t msdosfs -o rw /dev/md0.nop /mnt/tst ; echo $? ; sysctl vfs.numdirtybuffers
g_vfs_done():md0.nop[WRITE(offset=512, length=4096)]error = 13
mount_msdosfs: /dev/md0.nop: Permission denied
1
vfs.numdirtybuffers: 0
```
ENXIO:
```
$ gnop destroy md0.nop
$ gnop create -e 6 -r 0 -w 100 -v md0
$ mount -t msdosfs -o rw /dev/md0.nop /mnt/tst ; echo $? ; sysctl vfs.numdirtybuffers
g_vfs_done():md0.nop[WRITE(offset=512, length=4096)]error = 6
mount_msdosfs: /dev/md0.nop: Device not configured
1
vfs.numdirtybuffers: 0
```
Finally, test the ro -> rw upgrade path.
==========================================================================
```
$ gnop destroy md0.nop
$ gnop create -e 19 -r 0 -w 100 -v md0
$ mount -t msdosfs -o ro /dev/md0.nop /mnt/tst ; echo $?
0
$ mount
/dev/md0.nop on /mnt/tst (msdosfs, local, read-only)
$ mount -u -o current,rw /mnt/tst ; echo $?
g_vfs_done():md0.nop[WRITE(offset=512, length=4096)]error = 19
mount_msdosfs: /dev/md0.nop: Operation not supported by device
1
$ mount
/dev/md0.nop on /mnt/tst (msdosfs, local, read-only)
$ sysctl vfs.numdirtybuffers
vfs.numdirtybuffers: 0
```
Looks good.
Future work would be extending something like this to other filesystems that
set a dirty bit on mount.