From f9a27d62f5c8cba0c668d8f770be583345f20246 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Sun, 22 Oct 2017 23:31:26 +0100 Subject: [PATCH] mount: add support for fsck - 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 | 17 +++++++- Kernel/include/kernel.h | 7 +++- Kernel/inode.c | 6 ++- Kernel/syscall_other.c | 86 ++++++++++++++++++++++++++--------------- 4 files changed, 80 insertions(+), 36 deletions(-) diff --git a/Kernel/filesys.c b/Kernel/filesys.c index b842c52b..405d3d63 100644 --- a/Kernel/filesys.c +++ b/Kernel/filesys.c @@ -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 } diff --git a/Kernel/include/kernel.h b/Kernel/include/kernel.h index 0e096d0a..de7cfa42 100644 --- a/Kernel/include/kernel.h +++ b/Kernel/include/kernel.h @@ -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 */ diff --git a/Kernel/inode.c b/Kernel/inode.c index dbb4d779..a22f7bc1 100644 --- a/Kernel/inode.c +++ b/Kernel/inode.c @@ -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)); diff --git a/Kernel/syscall_other.c b/Kernel/syscall_other.c index d5771dfa..bef27caa 100644 --- a/Kernel/syscall_other.c +++ b/Kernel/syscall_other.c @@ -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(); -- 2.34.1