vfs_donmount: in certain cases try r/o mount if r/w mount fails
If the operation is not an update, if neither r/w nor r/o mode is explicitly requested, if the error code hints at the possibility of the media being read-only, and if the fallback is allowed, then we can try to automatically downgrade to the readonly mode. This is especially useful for auto-mounting of removable media that sometimes can happen to be write-protected. The fallback to r/o is not enabled by default. It can be requested on a per-mount basis with a new mount option, 'autoro'. Or it can be globally allowed by setting vfs.default_autoro. Reviewed by: cem, kib MFC after: 3 weeks Relnotes: yes Differential Revision: https://reviews.freebsd.org/D13361
This commit is contained in:
parent
5a2fc46447
commit
31260bf042
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=331616
@ -155,6 +155,10 @@ This flag indicates that the file system was mounted by
|
|||||||
.Xr automountd 8 .
|
.Xr automountd 8 .
|
||||||
Automounted file systems are automatically unmounted by
|
Automounted file systems are automatically unmounted by
|
||||||
.Xr autounmountd 8 .
|
.Xr autounmountd 8 .
|
||||||
|
.It Cm autoro
|
||||||
|
Mount the file system read-write.
|
||||||
|
If that fails with an error that suggests that the media could be read-only,
|
||||||
|
then automatically try to mount the file system read-only.
|
||||||
.It Cm current
|
.It Cm current
|
||||||
When used with the
|
When used with the
|
||||||
.Fl u
|
.Fl u
|
||||||
|
@ -81,6 +81,10 @@ static int usermount = 0;
|
|||||||
SYSCTL_INT(_vfs, OID_AUTO, usermount, CTLFLAG_RW, &usermount, 0,
|
SYSCTL_INT(_vfs, OID_AUTO, usermount, CTLFLAG_RW, &usermount, 0,
|
||||||
"Unprivileged users may mount and unmount file systems");
|
"Unprivileged users may mount and unmount file systems");
|
||||||
|
|
||||||
|
static bool default_autoro = false;
|
||||||
|
SYSCTL_BOOL(_vfs, OID_AUTO, default_autoro, CTLFLAG_RW, &default_autoro, 0,
|
||||||
|
"Retry failed r/w mount as r/o if no explicit ro/rw option is specified");
|
||||||
|
|
||||||
MALLOC_DEFINE(M_MOUNT, "mount", "vfs mount structure");
|
MALLOC_DEFINE(M_MOUNT, "mount", "vfs mount structure");
|
||||||
MALLOC_DEFINE(M_STATFS, "statfs", "statfs structure");
|
MALLOC_DEFINE(M_STATFS, "statfs", "statfs structure");
|
||||||
static uma_zone_t mount_zone;
|
static uma_zone_t mount_zone;
|
||||||
@ -546,6 +550,31 @@ vfs_mount_destroy(struct mount *mp)
|
|||||||
uma_zfree(mount_zone, mp);
|
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 was returned for some other reason.
|
||||||
|
*/
|
||||||
|
return (true);
|
||||||
|
default:
|
||||||
|
return (false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
vfs_donmount(struct thread *td, uint64_t fsflags, struct uio *fsoptions)
|
vfs_donmount(struct thread *td, uint64_t fsflags, struct uio *fsoptions)
|
||||||
{
|
{
|
||||||
@ -553,10 +582,12 @@ vfs_donmount(struct thread *td, uint64_t fsflags, struct uio *fsoptions)
|
|||||||
struct vfsopt *opt, *tmp_opt;
|
struct vfsopt *opt, *tmp_opt;
|
||||||
char *fstype, *fspath, *errmsg;
|
char *fstype, *fspath, *errmsg;
|
||||||
int error, fstypelen, fspathlen, errmsg_len, errmsg_pos;
|
int error, fstypelen, fspathlen, errmsg_len, errmsg_pos;
|
||||||
|
bool autoro;
|
||||||
|
|
||||||
errmsg = fspath = NULL;
|
errmsg = fspath = NULL;
|
||||||
errmsg_len = fspathlen = 0;
|
errmsg_len = fspathlen = 0;
|
||||||
errmsg_pos = -1;
|
errmsg_pos = -1;
|
||||||
|
autoro = default_autoro;
|
||||||
|
|
||||||
error = vfs_buildopts(fsoptions, &optlist);
|
error = vfs_buildopts(fsoptions, &optlist);
|
||||||
if (error)
|
if (error)
|
||||||
@ -648,16 +679,27 @@ vfs_donmount(struct thread *td, uint64_t fsflags, struct uio *fsoptions)
|
|||||||
free(opt->name, M_MOUNT);
|
free(opt->name, M_MOUNT);
|
||||||
opt->name = strdup("nonosymfollow", 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;
|
fsflags &= ~MNT_RDONLY;
|
||||||
else if (strcmp(opt->name, "rw") == 0)
|
autoro = false;
|
||||||
|
}
|
||||||
|
else if (strcmp(opt->name, "rw") == 0) {
|
||||||
fsflags &= ~MNT_RDONLY;
|
fsflags &= ~MNT_RDONLY;
|
||||||
else if (strcmp(opt->name, "ro") == 0)
|
autoro = false;
|
||||||
|
}
|
||||||
|
else if (strcmp(opt->name, "ro") == 0) {
|
||||||
fsflags |= MNT_RDONLY;
|
fsflags |= MNT_RDONLY;
|
||||||
|
autoro = false;
|
||||||
|
}
|
||||||
else if (strcmp(opt->name, "rdonly") == 0) {
|
else if (strcmp(opt->name, "rdonly") == 0) {
|
||||||
free(opt->name, M_MOUNT);
|
free(opt->name, M_MOUNT);
|
||||||
opt->name = strdup("ro", M_MOUNT);
|
opt->name = strdup("ro", M_MOUNT);
|
||||||
fsflags |= MNT_RDONLY;
|
fsflags |= MNT_RDONLY;
|
||||||
|
autoro = false;
|
||||||
|
}
|
||||||
|
else if (strcmp(opt->name, "autoro") == 0) {
|
||||||
|
vfs_freeopt(optlist, opt);
|
||||||
|
autoro = true;
|
||||||
}
|
}
|
||||||
else if (strcmp(opt->name, "suiddir") == 0)
|
else if (strcmp(opt->name, "suiddir") == 0)
|
||||||
fsflags |= MNT_SUIDDIR;
|
fsflags |= MNT_SUIDDIR;
|
||||||
@ -682,6 +724,19 @@ vfs_donmount(struct thread *td, uint64_t fsflags, struct uio *fsoptions)
|
|||||||
}
|
}
|
||||||
|
|
||||||
error = vfs_domount(td, fstype, fspath, fsflags, &optlist);
|
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 "[no]{ro|rw}" has been explicitly requested and not
|
||||||
|
* overridden by "autoro".
|
||||||
|
*/
|
||||||
|
if (autoro && 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:
|
bail:
|
||||||
/* copyout the errmsg */
|
/* copyout the errmsg */
|
||||||
if (errmsg_pos != -1 && ((2 * errmsg_pos + 1) < fsoptions->uio_iovcnt)
|
if (errmsg_pos != -1 && ((2 * errmsg_pos + 1) < fsoptions->uio_iovcnt)
|
||||||
|
Loading…
Reference in New Issue
Block a user