mount: add support for fsck
authorAlan Cox <alan@linux.intel.com>
Sun, 22 Oct 2017 22:31:26 +0000 (23:31 +0100)
committerAlan Cox <alan@linux.intel.com>
Sun, 22 Oct 2017 22:31:26 +0000 (23:31 +0100)
- Dirty/clean status handling
- Write back superblocks when we go clean but not if we are clean (or bad)
- Add full checks so remount ro works for all cases

This allows you to fsck -a `prtroot` in /etc/rc to recover the system if
needed. Before rebooting you need to remount filesystems r/o or unmount them.

This last bit wants wiring into reboot so it does a runlevel change to init
unless -f is used. We can then umount everything nicely (by adding umount -a)
and remount the rootfs r/o as well as doing a killall etc when we shutdown.

Kernel/filesys.c
Kernel/include/kernel.h
Kernel/inode.c
Kernel/syscall_other.c

index b842c52..405d3d6 100644 (file)
@@ -495,7 +495,7 @@ fsptr getdev(uint16_t dev)
     rdtime(&t);
     mnt->m_fs.s_time = t.low;
     mnt->m_fs.s_timeh = t.high;
-    mnt->m_fs.s_fmod = true;
+    mnt->m_fs.s_fmod = FMOD_DIRTY;
     return &mnt->m_fs;
 }
 
@@ -537,7 +537,7 @@ tryagain:
     }
     /* We must scan the inodes, and fill up the table */
 
-    _sync();           /* Make on-disk inodes consistent */
+    sync();           /* Make on-disk inodes consistent */
     k = 0;
     for(blk = 2; blk < dev->s_isize; blk++) {
         buf = bread(devno, blk, 0);
@@ -1175,12 +1175,25 @@ bool fmount(uint16_t dev, inoptr ino, uint16_t flags)
         return true; // failure
     }
 
+    if (fp->s_fmod == FMOD_DIRTY) {
+        kputs("warning: mounting dirty file system, forcing r/o.\n");
+        flags |= MS_RDONLY;
+    }
+    if (!(flags & MS_RDONLY))
+        /* Dirty - and will write dirty mark back to media */
+        fp->s_fmod = FMOD_DIRTY;
+    else       /* Clean in memory, don't write it back to media */
+        fp->s_fmod = FMOD_CLEAN;
     fp->s_mntpt = ino;
     if(ino)
         ++ino->c_refs;
     m->m_flags = flags;
     /* Makes our entry findable */
     m->m_dev = dev;
+
+    /* Mark the filesystem dirty on disk */
+    sync();
+
     return false; // success
 }
 
index 0e096d0..de7cfa4 100644 (file)
@@ -301,6 +301,11 @@ typedef struct filesys { // note: exists in mem and on disk
     int16_t       s_ninode;
     uint16_t      s_inode[FILESYS_TABSIZE];
     uint8_t       s_fmod;
+    /* 0 is 'legacy' and never written to disk */
+#define FMOD_GO_CLEAN  0       /* Write a clean to the disk (internal) */
+#define FMOD_DIRTY     1       /* Mounted or uncleanly unmounted from r/w */
+#define FMOD_CLEAN     2       /* Clean. Used internally to mean don't
+                                  update the super block */
     uint8_t       s_timeh;     /* bits 32-40: FIXME - wire up */
     uint32_t      s_time;
     blkno_t       s_tfree;
@@ -393,7 +398,7 @@ struct mount {
 #define A_FTRACE               18      /* Unimplemented: 
                                           Hook to the syscall trace debug */
 
-#define AD_NOSYNC              1       /* Unimplemented */
+#define AD_NOSYNC              1
                                           
 /* Process table entry */
 
index dbb4d77..a22f7bc 100644 (file)
@@ -354,8 +354,10 @@ void sync(void)
                }
        for (m = fs_tab; m < fs_tab + NMOUNTS; m++) {
                if (m->m_dev != NO_DEVICE &&
-                       m->m_fs.s_fmod) {
-                       m->m_fs.s_fmod = 0;
+                       m->m_fs.s_fmod != FMOD_CLEAN) {
+                       /* GO_CLEAN means write a CLEAN to the media */
+                       if (m->m_fs.s_fmod == FMOD_GO_CLEAN)
+                               m->m_fs.s_fmod = FMOD_CLEAN;
                        /* FIXME: I/O error handling */
                        buf = bread(m->m_dev, 1, 1);
                        blkfromk(&m->m_fs, buf, 0, sizeof(struct filesys));
index d5771df..bef27ca 100644 (file)
@@ -350,42 +350,35 @@ arg_t _mount(void)
   _umount (spec)                   Function 34
   char *spec;
  ********************************************/
+
 #define spec (char *)udata.u_argn
 #define flags (uint16_t)udata.u_argn1
 
-arg_t _umount(void)
+static int do_umount(uint16_t dev)
 {
-       inoptr sino;
-       uint16_t dev;
-       inoptr ptr;
        struct mount *mnt;
-       uint8_t rm;
-
-       if (esuper())
-               return (-1);
-
-       if (!(sino = n_open(spec, NULLINOPTR)))
-               return (-1);
-
-       if (getmode(sino) != MODE_R(F_BDEV)) {
-               udata.u_error = ENOTBLK;
-               goto nogood;
-       }
-
-       dev = (int) sino->c_node.i_addr[0];
-       if (!validdev(dev)) {
-               udata.u_error = ENXIO;
-               goto nogood;
-       }
-
-       rm = flags & MS_REMOUNT;
+       uint8_t rm = flags & MS_REMOUNT;
+       inoptr ptr;
 
        mnt = fs_tab_get(dev);
        if (mnt == NULL) {
                udata.u_error = EINVAL;
-               goto nogood;
+               return -1;
        }
 
+       /* If anything on this file system is open for write then you
+          can't remount it read only */
+       if (flags & (MS_RDONLY|MS_REMOUNT) == (MS_RDONLY|MS_REMOUNT)) {
+               for (ptr = i_tab ; ptr < i_tab + ITABSIZE; ++ptr) {
+                       if (ptr->c_dev == dev && ptr->c_writers) {
+                               udata.u_error = EBUSY;
+                               return -1;
+                       }
+               }
+       }
+
+       /* Sweep the inode table. If unmounting look for any references
+          and if so fail. If remounting update the CRDONLY flags */
        for (ptr = i_tab; ptr < i_tab + ITABSIZE; ++ptr) {
                if (ptr->c_refs > 0 && ptr->c_dev == dev) {
                        if (rm) {
@@ -394,28 +387,58 @@ arg_t _umount(void)
                                        ptr->c_flags |= CRDONLY;
                        } else {
                                udata.u_error = EBUSY;
-                               goto nogood;
+                               return -1;
                        }
                }
        }
 
+       if (!rm)
+               mnt->m_fs.s_fmod = FMOD_GO_CLEAN;
+
        sync();
 
-       if (flags & MS_REMOUNT) {
+       if (rm) {
                mnt->m_flags &= ~(MS_RDONLY|MS_NOSUID);
                mnt->m_flags |= flags & (MS_RDONLY|MS_NOSUID);
+               /* You can choose to remount a corrupt fs r/o in which case
+                  it gets marked clean. We may want to rethink that FIXME */
+               if (mnt->m_flags & MS_RDONLY)
+                       mnt->m_fs.s_fmod = FMOD_GO_CLEAN;
                return 0;
        }
 
        i_deref(mnt->m_fs.s_mntpt);
        /* Vanish the entry */
        mnt->m_dev = NO_DEVICE;
-       i_deref(sino);
        return 0;
+}
 
-      nogood:
+arg_t _umount(void)
+{
+       inoptr sino;
+       uint16_t dev;
+       arg_t ret = -1;
+
+       if (esuper())
+               return -1;
+
+       if (!(sino = n_open(spec, NULLINOPTR)))
+               return -1;
+
+       if (getmode(sino) != MODE_R(F_BDEV)) {
+               udata.u_error = ENOTBLK;
+               goto nogood;
+       }
+
+       dev = (int) sino->c_node.i_addr[0];
+       if (!validdev(dev)) {
+               udata.u_error = ENXIO;
+               goto nogood;
+       }
+       ret = do_umount(dev);
+nogood:
        i_deref(sino);
-       return -1;
+       return ret;
 }
 
 #undef spec
@@ -481,7 +504,8 @@ arg_t _uadmin(void)
 {
        if (esuper())
                return -1;
-       sync();
+       if (func != AD_NOSYNC)
+               sync();
        /* Wants moving into machine specific files */
        if (cmd == A_SHUTDOWN || cmd == A_DUMP)
                trap_monitor();