Index: sys/fs/msdosfs/fat.h =================================================================== --- sys/fs/msdosfs/fat.h +++ sys/fs/msdosfs/fat.h @@ -103,7 +103,13 @@ int freeclusterchain(struct msdosfsmount *pmp, u_long startchain); int extendfile(struct denode *dep, u_long count, struct buf **bpp, u_long *ncp, int flags); void fc_purge(struct denode *dep, u_int frcn); -int markvoldirty(struct msdosfsmount *pmp, int dirty); +int markvoldirty_upgrade(struct msdosfsmount *pmp, bool dirty, bool rw_upgrade); + +static inline int +markvoldirty(struct msdosfsmount *pmp, bool dirty) +{ + return (markvoldirty_upgrade(pmp, dirty, false)); +} #endif /* _KERNEL || MAKEFS */ #endif /* !_FS_MSDOSFS_FAT_H_ */ Index: sys/fs/msdosfs/msdosfs_fat.c =================================================================== --- sys/fs/msdosfs/msdosfs_fat.c +++ sys/fs/msdosfs/msdosfs_fat.c @@ -1121,7 +1121,7 @@ * ? (other errors from called routines) */ int -markvoldirty(struct msdosfsmount *pmp, int dirty) +markvoldirty_upgrade(struct msdosfsmount *pmp, bool dirty, bool rw_upgrade) { struct buf *bp; u_long bn, bo, bsize, byteoffset, fatval; @@ -1134,8 +1134,11 @@ if (FAT12(pmp)) return (0); - /* Can't change the bit on a read-only filesystem. */ - if (pmp->pm_flags & MSDOSFSMNT_RONLY) + /* + * Can't change the bit on a read-only filesystem, except as part of + * ro->rw upgrade. + */ + if ((pmp->pm_flags & MSDOSFSMNT_RONLY) != 0 && !rw_upgrade) return (EROFS); /* @@ -1145,10 +1148,8 @@ byteoffset = FATOFS(pmp, 1); fatblock(pmp, byteoffset, &bn, &bsize, &bo); error = bread(pmp->pm_devvp, bn, bsize, NOCRED, &bp); - if (error) { - brelse(bp); + if (error) return (error); - } /* * Get the current value of the FAT entry and set/clear the relevant @@ -1174,5 +1175,24 @@ } /* Write out the modified FAT block synchronously. */ - return (bwrite(bp)); + error = bwrite(bp); + if (error != 0) { + int terror; + /* + * Get the block back and discard it without write to disk. + * + * If we're now discovering that this media is readonly (the + * WriteProtect discovery earlier didn't discover this info) or + * just gone, throw away our unimportant attempt to modify the + * clean bit and bail higher up the stack (failing mount, or + * failing RO -> RW transition, at least). See PR 210316 for + * more context. + */ + terror = getblkx(pmp->pm_devvp, bn, bsize, 0, 0, GB_UNMAPPED, + &bp); + KASSERT(terror == 0, ("getblkx: %d for just written block %lu " + "(handling failed write %d)\n", terror, bn, error)); + bfinval(bp); + } + return (error); } Index: sys/fs/msdosfs/msdosfs_vfsops.c =================================================================== --- sys/fs/msdosfs/msdosfs_vfsops.c +++ sys/fs/msdosfs/msdosfs_vfsops.c @@ -311,16 +311,25 @@ if (error) return (error); + /* Now that the volume is modifiable, mark it dirty. */ + error = markvoldirty_upgrade(pmp, true, true); + if (error) { + /* + * If dirtying the superblock failed, drop GEOM + * 'w' refs (we're still RO). + */ + g_topology_lock(); + (void)g_access(pmp->pm_cp, 0, -1, 0); + g_topology_unlock(); + + return (error); + } + pmp->pm_fmod = 1; pmp->pm_flags &= ~MSDOSFSMNT_RONLY; MNT_ILOCK(mp); mp->mnt_flag &= ~MNT_RDONLY; MNT_IUNLOCK(mp); - - /* Now that the volume is modifiable, mark it dirty. */ - error = markvoldirty(pmp, 1); - if (error) - return (error); } } /* @@ -701,10 +710,8 @@ if (ronly) pmp->pm_flags |= MSDOSFSMNT_RONLY; else { - if ((error = markvoldirty(pmp, 1)) != 0) { - (void)markvoldirty(pmp, 0); + if ((error = markvoldirty(pmp, 1)) != 0) goto error_exit; - } pmp->pm_fmod = 1; } mp->mnt_data = pmp; Index: sys/kern/vfs_bio.c =================================================================== --- sys/kern/vfs_bio.c +++ sys/kern/vfs_bio.c @@ -2488,6 +2488,31 @@ bp->b_flags &= ~B_DEFERRED; } +/* + * bfinval: + * + * Forced invalidation of dirty buffer contents. The buffer is + * invalidated and released. + */ +void +bfinval(struct buf *bp) +{ + CTR3(KTR_BUF, "bfinval(%p) vp %p flags %X", bp, bp->b_vp, bp->b_flags); + + /* + * Remove from vnode's dirty list and adjust numdirtybuffers + * accounting. + */ + bundirty(bp); + + /* + * Fire it into the sun. + */ + bp->b_flags |= B_INVAL | B_RELBUF | B_NOCACHE; + bp->b_flags &= ~(B_ASYNC | B_CACHE); + brelse(bp); +} + /* * bawrite: * Index: sys/sys/buf.h =================================================================== --- sys/sys/buf.h +++ sys/sys/buf.h @@ -534,6 +534,7 @@ int bbarrierwrite(struct buf *); void bdirty(struct buf *); void bundirty(struct buf *); +void bfinval(struct buf *); void bufstrategy(struct bufobj *, struct buf *); void brelse(struct buf *); void bqrelse(struct buf *);