Author: avg
Date: Tue Mar 27 14:31:42 2018
New Revision: 331616
URL: https://svnweb.freebsd.org/changeset/base/331616

Log:
  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

Modified:
  head/sbin/mount/mount.8
  head/sys/kern/vfs_mount.c

Modified: head/sbin/mount/mount.8
==============================================================================
--- head/sbin/mount/mount.8     Tue Mar 27 13:59:57 2018        (r331615)
+++ head/sbin/mount/mount.8     Tue Mar 27 14:31:42 2018        (r331616)
@@ -155,6 +155,10 @@ This flag indicates that the file system was mounted b
 .Xr automountd 8 .
 Automounted file systems are automatically unmounted by
 .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
 When used with the
 .Fl u

Modified: head/sys/kern/vfs_mount.c
==============================================================================
--- head/sys/kern/vfs_mount.c   Tue Mar 27 13:59:57 2018        (r331615)
+++ head/sys/kern/vfs_mount.c   Tue Mar 27 14:31:42 2018        (r331616)
@@ -81,6 +81,10 @@ static int   usermount = 0;
 SYSCTL_INT(_vfs, OID_AUTO, usermount, CTLFLAG_RW, &usermount, 0,
     "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_STATFS, "statfs", "statfs structure");
 static uma_zone_t mount_zone;
@@ -546,6 +550,31 @@ vfs_mount_destroy(struct mount *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
 vfs_donmount(struct thread *td, uint64_t fsflags, struct uio *fsoptions)
 {
@@ -553,10 +582,12 @@ vfs_donmount(struct thread *td, uint64_t fsflags, stru
        struct vfsopt *opt, *tmp_opt;
        char *fstype, *fspath, *errmsg;
        int error, fstypelen, fspathlen, errmsg_len, errmsg_pos;
+       bool autoro;
 
        errmsg = fspath = NULL;
        errmsg_len = fspathlen = 0;
        errmsg_pos = -1;
+       autoro = default_autoro;
 
        error = vfs_buildopts(fsoptions, &optlist);
        if (error)
@@ -648,17 +679,28 @@ vfs_donmount(struct thread *td, uint64_t fsflags, stru
                        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)
+                       autoro = false;
+               }
+               else if (strcmp(opt->name, "rw") == 0) {
                        fsflags &= ~MNT_RDONLY;
-               else if (strcmp(opt->name, "ro") == 0)
+                       autoro = false;
+               }
+               else if (strcmp(opt->name, "ro") == 0) {
                        fsflags |= MNT_RDONLY;
+                       autoro = false;
+               }
                else if (strcmp(opt->name, "rdonly") == 0) {
                        free(opt->name, M_MOUNT);
                        opt->name = strdup("ro", M_MOUNT);
                        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)
                        fsflags |= MNT_SUIDDIR;
                else if (strcmp(opt->name, "sync") == 0)
@@ -682,6 +724,19 @@ vfs_donmount(struct thread *td, uint64_t fsflags, stru
        }
 
        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:
        /* copyout the errmsg */
        if (errmsg_pos != -1 && ((2 * errmsg_pos + 1) < fsoptions->uio_iovcnt)
_______________________________________________
svn-src-head@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to