From 8704807cb3481227b408ac998a3e9fe5c950be77 Mon Sep 17 00:00:00 2001 From: Ben Gras Date: Fri, 8 Nov 2013 16:37:36 +0000 Subject: [PATCH] testfs: test MFS format doesn't change, using mkfs This test tests mkfs.mfs will generate the same FS image given the same input files. mkproto creates a proto file (normalizing directory entry order). The assumption is that a change in the output flags a tacit change in FS format, and that a FS format change will cause the image to change. . Changes to mkfs.mfs that innocently change the format can change the sha1 output in the script along with it. . The assumption is that corresponding versions of mkfs.mfs and MFS will always work together; otherwise a lot breaks (ramdisk etc.) . Therefore, as long as a generated FS image stays the same with the same input now, incompatible MFS changes will still be flagged, even if they work together with the current mkfs.mfs. Change-Id: I120cd1d9347aedaf438c8847f0e7b8eccf086e9d --- test/Makefile | 2 +- test/run | 2 +- test/testfs.sh | 73 +++++++++++++++++++++++++++++++++ usr.sbin/mkfs.mfs/mkfs.c | 21 +++++++--- usr.sbin/mkfs.mfs/v3/mkfs.mfs.1 | 3 ++ usr.sbin/mkproto/mkproto.c | 48 ++++++++++++++++++++-- 6 files changed, 137 insertions(+), 12 deletions(-) create mode 100755 test/testfs.sh diff --git a/test/Makefile b/test/Makefile index ffeda323e..fccab7e9e 100644 --- a/test/Makefile +++ b/test/Makefile @@ -74,7 +74,7 @@ PROGS+= test${t} PROGS+= t10a t11a t11b t40a t40b t40c t40d t40e t40f t60a t60b \ t67a t67b t68a t68b -SCRIPTS+= run testinterp.sh testsh1.sh testsh2.sh +SCRIPTS+= run testinterp.sh testsh1.sh testsh2.sh testfs.sh .if ${MKPIC} == "yes" # Build them as dynamic executables by default if shared libraries diff --git a/test/run b/test/run index 453126e08..ba57609dc 100755 --- a/test/run +++ b/test/run @@ -25,7 +25,7 @@ alltests=" 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 \ 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 \ 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 \ 61 62 63 64 65 66 67 68 69 70 71 72 75 \ - sh1.sh sh2.sh interp.sh" + sh1.sh sh2.sh interp.sh fs.sh" tests_no=`expr 0` # test mmap only if enabled in sysenv diff --git a/test/testfs.sh b/test/testfs.sh new file mode 100755 index 000000000..09891d0dc --- /dev/null +++ b/test/testfs.sh @@ -0,0 +1,73 @@ +#!/bin/sh + +# expected sha1sum of the FS image +expect=2544d27de0e45dbbaf5fd273cc746b3c03be3e51 + +set -e + +echo -n "mkfs test " + +testdir=fstest +protofile=proto +fsimage=fsimage +rm -rf $testdir $protofile $fsimage + +if [ -d $testdir ] +then echo "dir?" + exit 1 +fi + +mkdir -p $testdir $testdir/contents $testdir/modes + +if [ ! -d $testdir ] +then echo "no dir?" + exit 1 +fi + +# Make some small & big & bigger files + +prevf=$testdir/contents/file +echo "Test contents 123" >$prevf +for double in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 +do fn=$testdir/contents/fn.$double + cat $prevf $prevf >$fn + prevf=$fn +done + +# Make some files with various modes & mtimes + +for many in 0 1 2 3 4 5 6 7 8 9 +do for m1 in 0 1 2 3 4 5 6 7 + do for m2 in 0 1 2 3 4 5 6 7 + do for m3 in 0 1 2 3 4 5 6 7 + do + mode=${m1}${m2}${m3} + fn=$testdir/modes/m${mode}${many} + echo "$many $m1 $m2 $m3 $mode" > $fn + chmod $mode $fn + done + done + done +done + +# Make an MFS filesystem image out of it + +BS=4096 +BLOCKS=15000 +INODES=6000 +dd if=/dev/zero seek=$BLOCKS of=$fsimage count=1 bs=$BS >/dev/null 2>&1 + +# -s keeps modes +mkproto -s -b $BLOCKS -i $INODES $testdir >$protofile + +mkfs.mfs -T 1 -b $BLOCKS -i $INODES $fsimage $protofile >/dev/null 2>&1 +sum="`sha1 $fsimage | awk '{ print $4 }'`" +if [ $sum != $expect ] +then echo sum $sum is not expected $expect + exit 1 +fi + +echo ok + +exit 0 + diff --git a/usr.sbin/mkfs.mfs/mkfs.c b/usr.sbin/mkfs.mfs/mkfs.c index 36f506cf4..8fd95c3a5 100644 --- a/usr.sbin/mkfs.mfs/mkfs.c +++ b/usr.sbin/mkfs.mfs/mkfs.c @@ -142,7 +142,7 @@ static ssize_t mkfs_write(void * buf, size_t count); int main(int argc, char *argv[]) { - int nread, mode, usrid, grpid, ch, extra_space_percent; + int nread, mode, usrid, grpid, ch, extra_space_percent, Tflag = 0; block_t blocks, maxblocks, bblocks; ino_t inodes, root_inum; char *token[MAX_TOKENS], line[LINE_LEN], *sfx; @@ -160,7 +160,7 @@ main(int argc, char *argv[]) #endif zone_shift = 0; extra_space_percent = 0; - while ((ch = getopt(argc, argv, "B:b:di:ltvx:z:I:")) != EOF) + while ((ch = getopt(argc, argv, "B:b:di:ltvx:z:I:T:")) != EOF) switch (ch) { #ifndef MFS_STATIC_BLOCK_SIZE case 'B': @@ -189,6 +189,10 @@ main(int argc, char *argv[]) case 'b': blocks = bblocks = strtoul(optarg, (char **) NULL, 0); break; + case 'T': + Tflag = 1; + current_time = strtoul(optarg, (char **) NULL, 0); + break; case 'd': dflag = 1; break; @@ -212,12 +216,17 @@ main(int argc, char *argv[]) * identical. First you set the time of the mkfs binary to what you * want, then go. */ - current_time = time((time_t *) 0); /* time mkfs is being run */ - if(dflag) { + if(Tflag) { + if(dflag) + errx(1, "-T and -d both specify a time and so are mutually exclusive"); + } else if(dflag) { struct stat statbuf; if (stat(progname, &statbuf)) { - perror("stat of itself"); - } else current_time = statbuf.st_mtime; + err(1, "stat of itself"); + } + current_time = statbuf.st_mtime; + } else { + current_time = time((time_t *) 0); /* time mkfs is being run */ } /* Percentage of extra size must be nonnegative. diff --git a/usr.sbin/mkfs.mfs/v3/mkfs.mfs.1 b/usr.sbin/mkfs.mfs/v3/mkfs.mfs.1 index f93970844..aabf46db6 100644 --- a/usr.sbin/mkfs.mfs/v3/mkfs.mfs.1 +++ b/usr.sbin/mkfs.mfs/v3/mkfs.mfs.1 @@ -13,6 +13,7 @@ .Op Fl z Ar zone_shift .Op Fl x Ar extra_space .Op Fl I Ar fs_offset +.Op Fl T Ar timestamp .Ar special .Op Ar prototype .Sh OPTIONS @@ -38,6 +39,8 @@ Filesystem block size (in bytes) Filesystem size (in blocks) .It Fl I Ar fs_offset Write filesystem starting at offset (in bytes) +.It Fl T Ar timestamp +Use timestamp for inode times .It Fl x Ar extra_space Extra space after dynamic sizing (blocks and inodes) .It Fl z Ar zone_shift diff --git a/usr.sbin/mkproto/mkproto.c b/usr.sbin/mkproto/mkproto.c index 8c6ab56e3..a0b5e9ba3 100644 --- a/usr.sbin/mkproto/mkproto.c +++ b/usr.sbin/mkproto/mkproto.c @@ -10,6 +10,8 @@ #include #include #include +#include +#include /* The default values for the prototype file */ #define DEF_UID 2 /* bin */ @@ -41,6 +43,12 @@ void display_attrib(const char *name, struct stat *st); void usage(char *binname); void open_outfile(void); +#define MAXNAME 256 + +struct mkproto_dirent { + char d_name[MAXNAME]; +}; + int main(argc, argv) int argc; char *argv[]; @@ -127,16 +135,27 @@ char *argv[]; return(0); } +/* Compare dirent objects for order */ +static int cmp_dirent(const void *d1, const void *d2) +{ + struct mkproto_dirent *dp1 = (struct mkproto_dirent *) d1, + *dp2 = (struct mkproto_dirent *) d2; + return strcmp(dp1->d_name, dp2->d_name); +} + /* Output the prototype spec for this directory. */ void descend(dirname) char *dirname; { - struct dirent *dp; + struct dirent *rdp; + struct mkproto_dirent *dirents = NULL, *dp; + int reserved_dirents = 0; DIR *dirp; char *name, *temp, *tempend; int i; struct stat st; mode_t mode; + int entries = 0; dirp = opendir(dirname); if (dirp == NULL) { @@ -148,8 +167,29 @@ char *dirname; strcpy(temp, dirname); strcat(temp, "/"); tempend = &temp[strlen(temp)]; + + /* read all directory entries */ + for (rdp = readdir(dirp); rdp != NULL; rdp = readdir(dirp)) { + if(entries >= reserved_dirents) { + int newreserved = (2*(reserved_dirents+1)); + if(!(dirents = realloc(dirents, newreserved * + sizeof(*dirents)))) { + errx(1, "realloc failed on dirents"); + } + reserved_dirents = newreserved; + } + assert(entries < reserved_dirents); + assert(strlen(rdp->d_name) < sizeof(dp->d_name)); + dp = &dirents[entries]; + strcpy(dp->d_name, rdp->d_name); + entries++; + } + closedir(dirp); - for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) { + /* normalize (sort) them */ + qsort(dirents, entries, sizeof(*dp), cmp_dirent); + + for (dp = dirents; entries > 0; dp++, entries--) { name = dp->d_name; count++; @@ -202,8 +242,8 @@ char *dirname; fprintf(outfile, " /dev/null"); fprintf(stderr,"File\n\t%s\n has an invalid mode, made empty.\n",temp); } - closedir(dirp); free(temp); + free(dirents); tabs--; } @@ -221,7 +261,7 @@ struct stat *st; if (same_prot) prot = st->st_mode & 0777; /***** This one is a bit shady *****/ for (i = 0; i < tabs; i++) fprintf(outfile, "%s", indentstr); - fprintf(outfile, "%s%s%c%c%c%3o %d %d", + fprintf(outfile, "%s%s%c%c%c%03o %d %d", name, *name == '\0' ? "" : indentstr, /* stop the tab for a null name */ (st->st_mode & S_IFMT) == S_IFDIR ? 'd' :