int nsock, npipe, nsyml, ztype[NLEVEL];
long nfreezone;
-int repair, automatic, listing, listsuper; /* flags */
+int repair, notrepaired = 0, automatic, listing, listsuper; /* flags */
+int preen = 0, markdirty = 0;
int firstlist; /* has the listing header been printed? */
unsigned part_offset; /* sector offset for this partition */
char answer[] = "Answer questions with y or n. Then hit RETURN";
_PROTOTYPE(bit_nr getnumber, (char *s));
_PROTOTYPE(char **getlist, (char ***argv, char *type));
_PROTOTYPE(void lsuper, (void));
-_PROTOTYPE(void getsuper, (void));
+#define SUPER_GET 0
+#define SUPER_PUT 1
+_PROTOTYPE(void rw_super, (int mode));
_PROTOTYPE(void chksuper, (void));
_PROTOTYPE(void lsi, (char **clist));
_PROTOTYPE(bitchunk_t *allocbitmap, (int nblk));
{
register int c, answerchar;
static int note = 0;
+ int yes;
if (!repair) {
printf("\n");
if ((c = answerchar = getchar()) == 'q' || c == 'Q') exit(1);
if(c == 'A') { automatic = 1; c = 'y'; }
while (!eoln(c)) c = getchar();
- return !(answerchar == 'n' || answerchar == 'N');
+ yes = !(answerchar == 'n' || answerchar == 'N');
+ if(!yes) notrepaired = 1;
+ return yes;
}
/* Convert string to integer. Representation is octal. */
/* Open the device. */
void devopen()
{
- if ((dev = open(fsck_device, repair ? O_RDWR : O_RDONLY)) < 0) {
+ if ((dev = open(fsck_device,
+ (repair || markdirty) ? O_RDWR : O_RDONLY)) < 0) {
perror(fsck_device);
fatal("couldn't open device to fsck");
}
devwrite(0, OFFSET_SUPER_BLOCK, (char *) &sb, sizeof(sb));
return;
}
+ printf("flags = ");
+ if(sb.s_flags & MFSFLAG_CLEAN) printf("CLEAN "); else printf("DIRTY ");
+ printf("\n");
} while (yes("Do you want to try again"));
if (repair) exit(0);
}
/* Get the super block from either disk or user. Do some initial checks. */
-void getsuper()
+void rw_super(int put)
{
if(lseek(dev, OFFSET_SUPER_BLOCK, SEEK_SET) < 0) {
perror("lseek");
fatal("couldn't seek to super block.");
}
+ if(put == SUPER_PUT) {
+ if(write(dev, &sb, sizeof(sb)) != sizeof(sb)) {
+ fatal("couldn't write super block.");
+ }
+ return;
+ }
if(read(dev, &sb, sizeof(sb)) != sizeof(sb)) {
fatal("couldn't read super block.");
}
printf("warning: expected max size to be %ld ", maxsize);
printf("instead of %ld\n", sb.s_max_size);
}
+
+ if(sb.s_flags & MFSFLAG_MANDATORY_MASK) {
+ fatal("unsupported feature bits - newer fsck needed");
+ }
}
int inoblock(int inn)
int w = nblk * WORDS_PER_BLOCK;
bit_nr phys = 0;
- printf("Checking %s map\n", type);
+ printf("Checking %s map. ", type);
+ if(!preen) printf("\n");
+ fflush(stdout);
loadbitmap(dmap, blkno, nblk);
do {
if (*p != *q) chkword(*p, *q, bit, type, &nerr, &report, phys);
register ino_t ino = 1;
mode_t mode;
- printf("Checking inode list\n");
+ printf("Checking inode list. ");
+ if(!preen) printf("\n");
+ fflush(stdout);
do
if (!bitset(imap, (bit_nr) ino)) {
devread(inoblock(ino), inooff(ino), (char *) &mode,
}
}
while (++ino <= sb.s_ninodes && ino != 0);
- printf("\n");
+ if(!preen) printf("\n");
}
/* Allocate an array to maintain the inode reference counts in. */
/* Print the totals of all the objects found. */
void printtotal()
{
+ if(preen) {
+ printf("%d files, %d directories, %d free inodes, %d free zones\n",
+ nregular, ndirectory, nfreeinode, nfreezone);
+ return;
+ }
+
printf("blocksize = %5d ", block_size);
printf("zonesize = %5d\n", ZONE_SIZE);
printf("\n");
devopen();
- getsuper();
+ rw_super(SUPER_GET);
if(block_size < _MIN_BLOCK_SIZE)
fatal("funny block size");
chksuper();
+ if(markdirty) {
+ if(sb.s_flags & MFSFLAG_CLEAN) {
+ sb.s_flags &= ~MFSFLAG_CLEAN;
+ rw_super(SUPER_PUT);
+ printf("\n----- FILE SYSTEM MARKED DIRTY -----\n\n");
+ } else {
+ printf("Filesystem is already dirty.\n");
+ }
+ }
+
+ /* If preening, skip fsck if clean flag is on. */
+ if(preen) {
+ if(sb.s_flags & MFSFLAG_CLEAN) {
+ printf("%s: clean\n", f);
+ return;
+ }
+ printf("%s: dirty, performing fsck\n", f);
+ }
+
lsi(clist);
getbitmaps();
chkcount();
chkmap(imap, spec_imap, (bit_nr) 0, BLK_IMAP, N_IMAP, "inode");
chkilist();
+ if(preen) printf("\n");
printtotal();
putbitmaps();
freecount();
- devclose();
- if (changed) printf("----- FILE SYSTEM HAS BEEN MODIFIED -----\n\n");
+ if (changed) printf("\n----- FILE SYSTEM HAS BEEN MODIFIED -----\n\n");
+
+ /* If we were told to repair the FS, and the user never stopped us from
+ * doing it, and the FS wasn't marked clean, we can mark the FS as clean.
+ */
+ if(repair && !(sb.s_flags & MFSFLAG_CLEAN)) {
+ if(notrepaired) {
+ printf("\n----- FILE SYSTEM STILL DIRTY -----\n\n");
+ } else {
+ sync(); /* update FS on disk before clean flag */
+ sb.s_flags |= MFSFLAG_CLEAN;
+ rw_super(SUPER_PUT);
+ printf("\n----- FILE SYSTEM MARKED CLEAN -----\n\n");
+ }
+ }
+
+ devclose();
}
int main(argc, argv)
prog = *argv++;
while ((arg = *argv++) != 0)
if (arg[0] == '-' && arg[1] != 0 && arg[2] == 0) switch (arg[1]) {
+ case 'd': markdirty = 1; break;
+ case 'p': preen = repair = automatic = 1; break;
case 'y':
- case 'p':
case 'a': automatic ^= 1; break;
case 'c':
clist = getlist(&argv, "inode");
devgiven = 1;
}
if (!devgiven) {
- printf("Usage: fsck [-yfpacilrsz] file\n");
+ printf("Usage: fsck [-dyfpacilrsz] file\n");
exit(1);
}
return(0);
unsigned int rem;
u64_t resize;
+
if ((fd = open(device, O_RDONLY)) == -1) {
- if (errno != ENOENT)
- perror("sizeup open");
- return 0;
+ if (errno != ENOENT)
+ perror("sizeup open");
+ return 0;
}
if (ioctl(fd, DIOCGETP, &entry) == -1) {
- perror("sizeup ioctl");
- if(fstat(fd, &st) < 0) {
- perror("fstat");
- entry.size = cvu64(0);
- } else {
- fprintf(stderr, "used fstat instead\n");
- entry.size = cvu64(st.st_size);
- }
+ perror("sizeup ioctl");
+ if(fstat(fd, &st) < 0) {
+ perror("fstat");
+ entry.size = cvu64(0);
+ } else {
+ fprintf(stderr, "used fstat instead\n");
+ entry.size = cvu64(st.st_size);
+ }
}
+
close(fd);
d = div64u(entry.size, block_size);
rem = rem64u(entry.size, block_size);
for (cp = buf; cp < &buf[block_size]; cp++) *cp = 0;
sup = (struct super_block *) buf; /* lint - might use a union */
+ /* The assumption is that mkfs will create a clean FS. */
+ sup->s_flags = MFSFLAG_CLEAN;
+
sup->s_ninodes = inodes;
if (fs_version == 1) {
sup->s_nzones = zones;
.include <bsd.own.mk>
PROGRAMS= at_wini bios_wini cdprobe dev2name floppy loadramdisk mount \
- pci procfs sh service sysenv mfs
+ pci procfs sh service sysenv mfs fsck.mfs
SCRIPTS=newroot
.if ${MKSMALL} != "yes"
image: proto.gen mtab rc $(EXTRA)
mkfs.mfs image proto.gen || { rm -f image; false; }
+ if fsck.mfs -s image | grep -q CLEAN; \
+ then : ; \
+ else echo "CLEAN sanity check of image failed." ; \
+ echo "(Perhaps install current mkfs and fsck.)" ; \
+ fi
ahci: ../ahci/ahci
install ${STRIPFLAG} ../$@/$@ $@
../../commands/mount/mount:
$(MAKE) -C ../../commands/mount
+fsck.mfs: ../../commands/fsck.mfs/fsck.mfs
+ install ${STRIPFLAG} ../../commands/$@/$@ $@
+
+../../commands/fsck.mfs/fsck.mfs:
+ $(MAKE) -C ../../commands/fsck.mfs
+
newroot: ../../commands/newroot/newroot.sh
install ${STRIPFLAG} ../../commands/$@/$@.sh $@
sh ---755 0 0 sh
service ---755 0 0 service
sysenv ---755 0 0 sysenv
+ fsck.mfs ---755 0 0 fsck.mfs
$
sbin d--755 0 0
@ACPI@
exec 2>/dev/log
exec </dev/null
+FSCK=/bin/fsck.mfs
ACPI=/sbin/acpi
if [ -e $ACPI -a -n "`sysenv acpi`" ]
then
loadramdisk "$ramimagename"
fi
echo "Root device name is $rootdevname"
+if [ -e $FSCK ]
+then $FSCK -p $rootdevname
+fi
/bin/newroot $bin_img"$rootdevname"
/bin/mount -e -t procfs none /proc || echo "WARNING: couldn't mount procfs"
+
exec /bin/sh /etc/rc "$@"
install -m 644 -o root -g operator descr /usr/lib/
-installforce:: $(ETC)/rc $(ETC)/rs.inet $(ETC)/rs.single $(ETC)/system.conf $(USRETC)/rc $(USR)/Makefile installpw
+installforce:: $(ETC)/rc $(ETC)/rs.inet $(ETC)/rs.single $(ETC)/system.conf $(ETC)/rc.subr.minix $(USRETC)/rc $(USR)/Makefile installpw
installpw::
if [ ! -d $(ETC) ]; then mkdir $(ETC); chmod 755 $(ETC); fi
$(ETC)/system.conf: system.conf .PHONY
install -m 644 -o root -g operator $> $@
+$(ETC)/rc.subr.minix: rc.subr.minix .PHONY
+ install -m 644 -o root -g operator $> $@
+
$(USRETC)/rc: usr/rc .PHONY
install -m 755 -o root -g operator $> $@
mountfstab()
{
fsck_opts=""
- fflag=""
+ fflag="-p"
while getopts "fo:" opt
do case $opt
shift `expr $OPTIND - 1`
- # Make fsck necessary for unclean shutdown
- msg="The system was not properly shut down. Checking file systems."
- if shutdown -C
- then echo "$msg"
- fflag="-f"
- fi
-
fstabfile="$1"
if [ ! -f $fstabfile ]
# This line's parameters
dev="$1"; mp="$2"; fstype="$3"
+ # Don't fsck / as it's already mounted
+ if [ "$mp" = "/" ]; then continue; fi
+
# Sanity checks
if [ ! -b $dev ]; then echo "$dev missing"; continue; fi
if [ ! -d $mp ]; then echo "$mp missing"; continue; fi
# Do fsck if necessary or requested
if [ -n "$fflag" ]
- then echo "Checking $fstype $dev"
- if ! fsck.$fstype $fflag $fsck_opts -p $dev
- then echo "$dev fail"
- continue
- fi
+ then fsck.$fstype $fflag $fsck_opts $dev
fi
# Skip the actual mount for /, it's already mounted
* invalidate: remove all the cache blocks on some device
*
* Private functions:
- * rw_block: read or write a block from the disk itself
+ * read_block: read or write a block from the disk itself
*/
#include "fs.h"
#include "inode.h"
FORWARD _PROTOTYPE( void rm_lru, (struct buf *bp) );
-FORWARD _PROTOTYPE( void rw_block, (struct buf *, int) );
+FORWARD _PROTOTYPE( void read_block, (struct buf *) );
PRIVATE int vmcache = 0; /* are we using vm's secondary cache? (initially not) */
+PRIVATE block_t super_start = 0, super_end = 0;
+
/*===========================================================================*
* get_block *
*===========================================================================*/
/* PREFETCH: don't do i/o. */
bp->b_dev = NO_DEV;
} else if (only_search == NORMAL) {
- rw_block(bp, READING);
+ read_block(bp);
} else if(only_search == NO_READ) {
/* we want this block, but its contents
* will be overwritten. VM has to forget
}
/*===========================================================================*
- * rw_block *
+ * read_block *
*===========================================================================*/
-PRIVATE void rw_block(bp, rw_flag)
+PRIVATE void read_block(bp)
register struct buf *bp; /* buffer pointer */
-int rw_flag; /* READING or WRITING */
{
/* Read or write a disk block. This is the only routine in which actual disk
* I/O is invoked. If an error occurs, a message is printed here, but the error
if ( (dev = bp->b_dev) != NO_DEV) {
pos = mul64u(bp->b_blocknr, fs_block_size);
- if (rw_flag == READING)
- r = bdev_read(dev, pos, bp->b_data, fs_block_size,
- BDEV_NOFLAGS);
- else
- r = bdev_write(dev, pos, bp->b_data, fs_block_size,
- BDEV_NOFLAGS);
+ r = bdev_read(dev, pos, bp->b_data, fs_block_size,
+ BDEV_NOFLAGS);
if (r < 0) {
printf("MFS(%d) I/O error on device %d/%d, block %u\n",
SELF_E, major(dev), minor(dev), bp->b_blocknr);
bp->b_dev = NO_DEV; /* invalidate block */
/* Report read errors to interested parties. */
- if (rw_flag == READING) rdwt_err = r;
+ rdwt_err = r;
}
}
-
- MARKCLEAN(bp);
}
/*===========================================================================*
vm_forgetblocks();
}
+/*===========================================================================*
+ * block_write_ok *
+ *===========================================================================*/
+int block_write_ok(struct buf *bp)
+{
+ if(superblock.s_dev != bp->b_dev) return 1;
+
+ if(bp->b_blocknr >= super_start && bp->b_blocknr <= super_end) {
+ printf("MFS: blocking write to superblock on mounted filesystem dev 0x%x.\n", bp->b_dev);
+ return 0;
+ }
+
+ if(superblock.s_rd_only) {
+ printf("MFS: blocking write to mounted readonly filesystem 0x%x.\n", bp->b_dev);
+ printf("This shouldn't happen.\n");
+ return 0;
+ }
+
+ return 1;
+}
+
/*===========================================================================*
* flushall *
*===========================================================================*/
dirtylistsize = nr_bufs;
}
- for (bp = &buf[0], ndirty = 0; bp < &buf[nr_bufs]; bp++)
- if (ISDIRTY(bp) && bp->b_dev == dev) dirty[ndirty++] = bp;
+ for (bp = &buf[0], ndirty = 0; bp < &buf[nr_bufs]; bp++) {
+ if (ISDIRTY(bp) && bp->b_dev == dev) {
+ if(!block_write_ok(bp)) {
+ printf("MFS: LATE: ignoring changes in block %d\n", bp->b_blocknr);
+ MARKCLEAN(bp);
+ continue;
+ }
+ dirty[ndirty++] = bp;
+ }
+ }
rw_scattered(dev, dirty, ndirty, WRITING);
}
buf_pool(bufs);
fs_block_size = blocksize;
+ super_start = SUPER_BLOCK_BYTES / fs_block_size;
+ super_end = (SUPER_BLOCK_BYTES + _MIN_BLOCK_SIZE - 1) / fs_block_size;
}
/*===========================================================================*
#include <minix/vfsif.h>
#include <minix/bdev.h>
+PRIVATE int cleanmount = 1;
/*===========================================================================*
* fs_readsuper *
return(r);
}
+ /* Remember whether we were mounted cleanly so we know what to
+ * do at unmount time
+ */
+ if(superblock.s_flags & MFSFLAG_CLEAN)
+ cleanmount = 1;
+
+ /* clean check: if rw and not clean, switch to readonly */
+ if(!(superblock.s_flags & MFSFLAG_CLEAN) && !readonly) {
+ if(bdev_close(fs_dev) != OK)
+ panic("couldn't bdev_close after found unclean FS");
+ readonly = 1;
+
+ if (bdev_open(fs_dev, R_BIT) != OK) {
+ panic("couldn't bdev_open after found unclean FS");
+ return(EINVAL);
+ }
+ printf("MFS: WARNING: FS 0x%x unclean, mounting readonly\n", fs_dev);
+ }
+
set_blocksize(&superblock);
/* Get the root inode of the mounted file system. */
fs_m_out.RES_CONREQS = 1; /* We can handle only 1 request at a time */
+ /* Mark it dirty */
+ if(!superblock.s_rd_only) {
+ superblock.s_flags &= ~MFSFLAG_CLEAN;
+ if(write_super(&superblock) != OK)
+ panic("mounting: couldn't write dirty superblock");
+ }
+
return(r);
}
/* force any cached blocks out of memory */
(void) fs_sync();
+ /* Mark it clean if we're allowed to write _and_ it was clean originally. */
+ if(cleanmount && !superblock.s_rd_only) {
+ superblock.s_flags |= MFSFLAG_CLEAN;
+ write_super(&superblock);
+ }
+
/* Close the device the file system lives on. */
bdev_close(fs_dev);
_PROTOTYPE( void set_blocksize, (struct super_block *) );
_PROTOTYPE( void rw_scattered, (dev_t dev,
struct buf **bufq, int bufqsize, int rw_flag) );
+_PROTOTYPE( int block_write_ok, (struct buf *bp) );
/* inode.c */
_PROTOTYPE( struct inode *alloc_inode, (dev_t dev, mode_t bits) );
_PROTOTYPE( unsigned int get_block_size, (dev_t dev) );
_PROTOTYPE( struct super_block *get_super, (dev_t dev) );
_PROTOTYPE( int read_super, (struct super_block *sp) );
+_PROTOTYPE( int write_super, (struct super_block *sp) );
/* stats.c */
_PROTOTYPE( bit_t count_free_bits, (struct super_block *sp, int map));
/* Copy a chunk from the block buffer to user space. */
r = sys_safecopyto(VFS_PROC_NR, gid, (vir_bytes) buf_off,
(vir_bytes) (bp->b_data+off), (size_t) chunk, D);
+ } else if(!block_write_ok(bp)) {
+ /* Let cache layer veto writing to this block */
+ printf("MFS: block write not allowed\n");
+ r = EPERM;
} else {
/* Copy a chunk from user space to the block buffer. */
r = sys_safecopyfrom(VFS_PROC_NR, gid, (vir_bytes) buf_off,
#include "fs.h"
#include <string.h>
+#include <assert.h>
#include <minix/com.h>
#include <minix/u64.h>
#include <minix/bdev.h>
panic("request for super_block of NO_DEV");
if(superblock.s_dev != dev)
- panic("wrong superblock: %d", (int) dev);
+ panic("wrong superblock: 0x%x", (int) dev);
return(&superblock);
}
/*===========================================================================*
- * read_super *
+ * rw_super *
*===========================================================================*/
-PUBLIC int read_super(sp)
-register struct super_block *sp; /* pointer to a superblock */
+PRIVATE int rw_super(struct super_block *sp, int writing)
{
-/* Read a superblock. */
- dev_t dev;
- unsigned int magic;
- int version, native, r;
+/* Read/write a superblock. */
+ int r;
static char *sbbuf;
- block_t offset;
+ dev_t save_dev = sp->s_dev;
+
+/* To keep the 1kb on disk clean, only read/write up to and including
+ * this field.
+ */
+#define LAST_ONDISK_FIELD s_disk_version
+ int ondisk_bytes = (int) ((char *) &sp->LAST_ONDISK_FIELD - (char *) sp)
+ + sizeof(sp->LAST_ONDISK_FIELD);
STATICINIT(sbbuf, _MIN_BLOCK_SIZE);
- dev = sp->s_dev; /* save device (will be overwritten by copy) */
- if (dev == NO_DEV)
+ assert(ondisk_bytes > 0);
+ assert(ondisk_bytes < _MIN_BLOCK_SIZE);
+ assert(ondisk_bytes < sizeof(struct super_block));
+
+ if (sp->s_dev == NO_DEV)
panic("request for super_block of NO_DEV");
-
- r = bdev_read(dev, cvu64(SUPER_BLOCK_BYTES), sbbuf, _MIN_BLOCK_SIZE,
- BDEV_NOFLAGS);
+
+ if(writing) {
+ memset(sbbuf, 0, _MIN_BLOCK_SIZE);
+ memcpy(sbbuf, sp, ondisk_bytes);
+ r = bdev_write(sp->s_dev, cvu64(SUPER_BLOCK_BYTES), sbbuf, _MIN_BLOCK_SIZE,
+ BDEV_NOFLAGS);
+ } else {
+ r = bdev_read(sp->s_dev, cvu64(SUPER_BLOCK_BYTES), sbbuf, _MIN_BLOCK_SIZE,
+ BDEV_NOFLAGS);
+ memset(sp, 0, sizeof(*sp));
+ memcpy(sp, sbbuf, ondisk_bytes);
+ sp->s_dev = save_dev;
+ }
+
if (r != _MIN_BLOCK_SIZE)
return(EINVAL);
-
- memcpy(sp, sbbuf, sizeof(*sp));
- sp->s_dev = NO_DEV; /* restore later */
+
+ return OK;
+}
+
+/*===========================================================================*
+ * read_super *
+ *===========================================================================*/
+PUBLIC int read_super(struct super_block *sp)
+{
+ unsigned int magic;
+ block_t offset;
+ int version, native, r;
+
+ if((r=rw_super(sp, 0)) != OK)
+ return r;
+
magic = sp->s_magic; /* determines file system type */
/* Get file system version and type. */
"or invalid first data zone, or zone size too large\n");
return(EINVAL);
}
- sp->s_dev = dev; /* restore device number */
+
+
+ /* Check any flags we don't understand but are required to. Currently
+ * these don't exist so all such unknown bits are fatal.
+ */
+ if(sp->s_flags & MFSFLAG_MANDATORY_MASK) {
+ printf("MFS: unsupported feature flags on this FS.\n"
+ "Please use a newer MFS to mount it.\n");
+ return(EINVAL);
+ }
+
return(OK);
}
+/*===========================================================================*
+ * write_super *
+ *===========================================================================*/
+PUBLIC int write_super(struct super_block *sp)
+{
+ if(sp->s_rd_only)
+ panic("can't write superblock of readonly filesystem");
+ return rw_super(sp, 1);
+}
+
short s_zmap_blocks; /* # of blocks used by zone bit map */
zone1_t s_firstdatazone_old; /* number of first data zone (small) */
short s_log_zone_size; /* log2 of blocks/zone */
- short s_pad; /* try to avoid compiler-dependent padding */
+ unsigned short s_flags; /* FS state flags */
off_t s_max_size; /* maximum file size on this device */
zone_t s_zones; /* number of zones (replaces s_nzones in V2) */
short s_magic; /* magic number to recognize super-blocks */
unsigned short s_block_size; /* block size in bytes. */
char s_disk_version; /* filesystem format sub-version */
- /* The following items are only used when the super_block is in memory. */
+ /* The following items are only used when the super_block is in memory.
+ * If this ever changes, i.e. more fields after s_disk_version has to go to
+ * disk, update LAST_ONDISK_FIELD in super.c as that controls which part of the
+ * struct is copied to and from disk.
+ */
/*struct inode *s_isup;*/ /* inode for root dir of mounted file sys */
/*struct inode *s_imount;*/ /* inode mounted on */
#define IMAP 0 /* operating on the inode bit map */
#define ZMAP 1 /* operating on the zone bit map */
+/* s_flags contents; undefined flags are guaranteed to be zero on disk
+ * (not counting future versions of mfs setting them!)
+ */
+#define MFSFLAG_CLEAN (1L << 0) /* 0: dirty; 1: FS was unmounted cleanly */
+
+/* Future compatability (or at least, graceful failure):
+ * if any of these bits are on, and the MFS or fsck
+ * implementation doesn't understand them, do not mount/fsck
+ * the FS.
+ */
+#define MFSFLAG_MANDATORY_MASK 0xff00
+
#endif