Index: sys/kern/vfs_mount.c =================================================================== --- sys/kern/vfs_mount.c +++ sys/kern/vfs_mount.c @@ -546,6 +546,31 @@ uma_zfree(mount_zone, mp); } +static bool +vfs_should_downgrade_to_ro_mount(uint64_t fsflags, int error) +{ + /* This is an upgrade of an exisiting mount. */ + if ((fsflags & MNT_UPDATE) != 0) + return (false); + /* This is already an R/O mount. */ + if ((fsflags & MNT_RDONLY) != 0) + return (false); + + switch (error) { + case ENODEV: /* generic, geom, ... */ + case EACCES: /* cam/scsi, ... */ + case EROFS: /* md, mmcsd, ... */ + /* + * These errors can be returned by the storage layer to signal + * that the media is read-only. No harm in the R/O mount + * attempt if the error is returned for some other reason. + */ + return (true); + default: + return (false); + } +} + int vfs_donmount(struct thread *td, uint64_t fsflags, struct uio *fsoptions) { @@ -553,10 +578,12 @@ struct vfsopt *opt, *tmp_opt; char *fstype, *fspath, *errmsg; int error, fstypelen, fspathlen, errmsg_len, errmsg_pos; + int force_rw; errmsg = fspath = NULL; errmsg_len = fspathlen = 0; errmsg_pos = -1; + force_rw = 0; error = vfs_buildopts(fsoptions, &optlist); if (error) @@ -648,16 +675,23 @@ free(opt->name, M_MOUNT); opt->name = strdup("nonosymfollow", M_MOUNT); } - else if (strcmp(opt->name, "noro") == 0) + else if (strcmp(opt->name, "noro") == 0) { fsflags &= ~MNT_RDONLY; - else if (strcmp(opt->name, "rw") == 0) + force_rw = 1; + } + else if (strcmp(opt->name, "rw") == 0) { fsflags &= ~MNT_RDONLY; - else if (strcmp(opt->name, "ro") == 0) + force_rw = 1; + } + else if (strcmp(opt->name, "ro") == 0) { fsflags |= MNT_RDONLY; + force_rw = 0; + } else if (strcmp(opt->name, "rdonly") == 0) { free(opt->name, M_MOUNT); opt->name = strdup("ro", M_MOUNT); fsflags |= MNT_RDONLY; + force_rw = 0; } else if (strcmp(opt->name, "suiddir") == 0) fsflags |= MNT_SUIDDIR; @@ -682,6 +716,18 @@ } error = vfs_domount(td, fstype, fspath, fsflags, &optlist); + + /* + * See if we can mount in the read-only mode if the error code suggests + * that it could be possible and the mount options allow for that. + * Never try it if 'rw' or 'noro' have been explicitly requested. + */ + if (!force_rw && vfs_should_downgrade_to_ro_mount(fsflags, error)) { + printf("%s: R/W mount failed, possibly R/O media," + " trying R/O mount\n", __func__); + fsflags |= MNT_RDONLY; + error = vfs_domount(td, fstype, fspath, fsflags, &optlist); + } bail: /* copyout the errmsg */ if (errmsg_pos != -1 && ((2 * errmsg_pos + 1) < fsoptions->uio_iovcnt)