Rewrite filediffer to C and make it work properly.
authorMilan Broz <gmazyland@gmail.com>
Thu, 5 Aug 2010 16:41:28 +0000 (16:41 +0000)
committerMilan Broz <gmazyland@gmail.com>
Thu, 5 Aug 2010 16:41:28 +0000 (16:41 +0000)
git-svn-id: https://cryptsetup.googlecode.com/svn/trunk@322 36d66b0a-2a48-0410-832c-cd162a569da5

ChangeLog
tests/Makefile.am
tests/compat-test
tests/differ.c [new file with mode: 0644]
tests/fileDiffer.py [deleted file]

index e7b35ad..2164c75 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,6 @@
 2010-08-05  Milan Broz  <mbroz@redhat.com>
        * Wipe iteration and salt after KillSlot in LUKS header.
+       * Rewrite file differ test to C (and fix it to really work).
 
 2010-07-28  Arno Wagner <arno@wagner.name>
        * Add FAQ (Frequently Asked Questions) file to distribution.
index e28f15a..da9ee2e 100644 (file)
@@ -1,13 +1,16 @@
 TESTS = api-test compat-test align-test
 
-EXTRA_DIST = fileDiffer.py compatimage.img.bz2 align-test compat-test
+EXTRA_DIST = compatimage.img.bz2 align-test compat-test
+
+differ_SOURCES = differ.c
+differ_CFLAGS = -Wall -O2
 
 api_test_SOURCES = api-test.c
 api_test_LDADD = ../lib/libcryptsetup.la
 api_test_LDFLAGS = -static
 api_test_CFLAGS = -g -Wall -O0 -I$(top_srcdir)/lib/
 
-check_PROGRAMS = api-test
+check_PROGRAMS = api-test differ
 
 compatimage.img:
        @bzip2 -k -d compatimage.img.bz2
index b7f6589..0f1ef67 100755 (executable)
@@ -7,15 +7,14 @@ DEV_NAME=dummy
 DEV_NAME2=dummy2
 ORIG_IMG=luks-test-orig
 IMG=luks-test
-IMG1=luks-test1
 KEY1=key1
 
 LUKS_HEADER="S0-5 S6-7 S8-39 S40-71 S72-103 S104-107 S108-111 R112-131 R132-163 S164-167 S168-207 A0-591"
-KEY_SLOT0="S208-211 S212-215 R216-247 S248-251 S251-255"
+KEY_SLOT0="S208-211 S212-215 R216-247 A248-251 A251-255"
 KEY_MATERIAL0="R4096-68096"
 KEY_MATERIAL0_EXT="R4096-68096"
 
-KEY_SLOT1="S256-259 S260-263 R264-295 S296-299 S300-303"
+KEY_SLOT1="S256-259 S260-263 R264-295 A296-299 A300-303"
 KEY_MATERIAL1="R69632-133632"
 KEY_MATERIAL1_EXT="S69632-133632"
 
@@ -24,7 +23,7 @@ function remove_mapping()
        [ -b /dev/mapper/$DEV_NAME2 ] && dmsetup remove $DEV_NAME2
        [ -b /dev/mapper/$DEV_NAME ] && dmsetup remove $DEV_NAME
        losetup -d $LOOPDEV >/dev/null 2>&1
-       rm -f $ORIG_IMG $IMG $IMG1 $KEY1 >/dev/null 2>&1
+       rm -f $ORIG_IMG $IMG $KEY1 >/dev/null 2>&1
 }
 
 function fail()
@@ -36,32 +35,41 @@ function fail()
 
 function prepare()
 {
-       if [ $(id -u) != 0 ]; then
-               echo "WARNING: You must be root to run this test, test skipped."
-               exit 0
-       fi
-
        [ -b /dev/mapper/$DEV_NAME ] && dmsetup remove $DEV_NAME
 
-       if [ ! -e $KEY1 ]; then
-               dd if=/dev/urandom of=$KEY1 count=1 bs=32 >/dev/null 2>&1
-       fi
-
-       if [ ! -e $IMG ]; then
+       case "$2" in
+       wipe)
+               remove_mapping
+               dd if=/dev/zero of=$IMG bs=1k count=10000 >/dev/null 2>&1
+               sync
+               losetup $LOOPDEV $IMG
+               ;;
+       new)
+               remove_mapping
                bzip2 -cd compatimage.img.bz2 > $IMG
-               losetup -d $LOOPDEV >/dev/null 2>&1
                losetup $LOOPDEV $IMG
+               ;;
+       reuse | *)
+               if [ ! -e $IMG ]; then
+                       bzip2 -cd compatimage.img.bz2 > $IMG
+                       losetup $LOOPDEV $IMG
+               fi
+               ;;
+       esac
+
+       if [ ! -e $KEY1 ]; then
+               dd if=/dev/urandom of=$KEY1 count=1 bs=32 >/dev/null 2>&1
        fi
 
        cp $IMG $ORIG_IMG
-
        [ -n "$1" ] && echo "CASE: $1"
 }
 
 function check()
 {
        sync
-       ./fileDiffer.py $IMG $ORIG_IMG $1|| fail
+       [ -z "$1" ] && return
+       ./differ $ORIG_IMG $IMG $1 || fail
 }
 
 function check_exists()
@@ -70,22 +78,27 @@ function check_exists()
        check $1
 }
 
+if [ $(id -u) != 0 ]; then
+       echo "WARNING: You must be root to run this test, test skipped."
+       exit 0
+fi
+
 # LUKS tests
 
-prepare        "[1] open - compat image - acceptance check"
+prepare        "[1] open - compat image - acceptance check" new
 echo "compatkey" | $CRYPTSETUP luksOpen $LOOPDEV $DEV_NAME || fail
 check_exists
 
-prepare "[2] open - compat image - denial check"
+prepare "[2] open - compat image - denial check" new
 echo "wrongkey" | $CRYPTSETUP luksOpen $LOOPDEV $DEV_NAME && fail
 check
 
 # All headers items and first key material section must change
-prepare "[3] format"
+prepare "[3] format" wipe
 echo "key0" | $CRYPTSETUP -i 1000 -c aes-cbc-essiv:sha256 -s 128 luksFormat $LOOPDEV || fail
 check "$LUKS_HEADER $KEY_SLOT0 $KEY_MATERIAL0"
 
-prepare "[4] format using hash sha512"
+prepare "[4] format using hash sha512" wipe
 echo "key0" | $CRYPTSETUP -i 1000 -h sha512 -c aes-cbc-essiv:sha256 -s 128 luksFormat $LOOPDEV || fail
 check "$LUKS_HEADER $KEY_SLOT0 $KEY_MATERIAL0"
 
@@ -101,13 +114,13 @@ echo "key1" | $CRYPTSETUP luksOpen $LOOPDEV $DEV_NAME || fail
 
 # Unsuccessful Key Delete - nothing may change
 prepare "[7] unsuccessful delete"
-echo "invalid" | $CRYPTSETUP luksDelKey $LOOPDEV 1 && fail
+echo "invalid" | $CRYPTSETUP luksKillSlot $LOOPDEV 1 && fail
 check
 
 # Delete Key Test
 # Key Slot 1 and key material section 1 must change, the rest must not
 prepare "[8] successful delete"
-$CRYPTSETUP -q luksDelKey $LOOPDEV 1 || fail
+$CRYPTSETUP -q luksKillSlot $LOOPDEV 1 || fail
 check "$KEY_SLOT1 $KEY_MATERIAL1_EXT"
 echo "key1" | $CRYPTSETUP luksOpen $LOOPDEV $DEV_NAME && fail
 echo "key0" | $CRYPTSETUP luksOpen $LOOPDEV $DEV_NAME || fail
@@ -120,24 +133,24 @@ $CRYPTSETUP -d $KEY1 luksOpen $LOOPDEV $DEV_NAME || fail
 
 # Key Slot 1 and key material section 1 must change, the rest must not
 prepare "[10] delete key test with key1 as remaining key"
-$CRYPTSETUP -d $KEY1 luksDelKey $LOOPDEV 0 || fail
+$CRYPTSETUP -d $KEY1 luksKillSlot $LOOPDEV 0 || fail
 check "$KEY_SLOT0 $KEY_MATERIAL0_EXT"
 echo "key0" | $CRYPTSETUP luksOpen $LOOPDEV $DEV_NAME && fail
 $CRYPTSETUP luksOpen -d $KEY1 $LOOPDEV $DEV_NAME || fail
 
 # Delete last slot
-prepare "[11] delete last key"
+prepare "[11] delete last key" wipe
 echo "key0" | $CRYPTSETUP luksFormat $LOOPDEV || fail
 echo "key0" | $CRYPTSETUP luksKillSlot $LOOPDEV 0 || fail
 echo "key0" | $CRYPTSETUP luksOpen $LOOPDEV $DEV_NAME && fail
 
 # Format test for ESSIV, and some other parameters.
-prepare "[12] parameter variation test"
+prepare "[12] parameter variation test" wipe
 $CRYPTSETUP -q -i 1000 -c aes-cbc-essiv:sha256 -s 128 luksFormat $LOOPDEV $KEY1 || fail
 check "$LUKS_HEADER $KEY_SLOT0 $KEY_MATERIAL0"
 $CRYPTSETUP -d $KEY1 luksOpen $LOOPDEV $DEV_NAME || fail
 
-prepare        "[13] open/close - stacked devices"
+prepare        "[13] open/close - stacked devices" wipe
 echo "key0" | $CRYPTSETUP -q luksFormat $LOOPDEV || fail
 echo "key0" | $CRYPTSETUP -q luksOpen $LOOPDEV $DEV_NAME || fail
 echo "key0" | $CRYPTSETUP -q luksFormat /dev/mapper/$DEV_NAME || fail
@@ -145,7 +158,7 @@ echo "key0" | $CRYPTSETUP -q luksOpen /dev/mapper/$DEV_NAME $DEV_NAME2 || fail
 $CRYPTSETUP -q luksClose  $DEV_NAME2 || fail
 $CRYPTSETUP -q luksClose  $DEV_NAME || fail
 
-prepare        "[14] format/open - passphrase on stdin & new line"
+prepare        "[14] format/open - passphrase on stdin & new line" wipe
 # stdin defined by "-" must take even newline
 echo -n $'foo\nbar' | $CRYPTSETUP -q luksFormat $LOOPDEV - || fail
 echo -n $'foo\nbar' | $CRYPTSETUP -q --key-file=- luksOpen $LOOPDEV $DEV_NAME || fail
diff --git a/tests/differ.c b/tests/differ.c
new file mode 100644 (file)
index 0000000..1c33069
--- /dev/null
@@ -0,0 +1,165 @@
+/*
+ * cryptsetup file differ check (rewritten Clemens' fileDiffer in Python)
+ *
+ * Copyright (C) 2010 Red Hat, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+
+struct ffile {
+       char *name;
+       int fd;
+       unsigned char *addr;
+       size_t size;
+};
+
+enum df { OK , FAIL };
+
+static void print_diff(off_t from, int max,
+                      const unsigned char *o,
+                      const unsigned char *n)
+{
+       int i, len = max;
+
+       if (len > 16)
+               len = 16;
+
+       printf("OLD:");
+       for (i = 0; i < len; i++)
+               printf(" %02x", o[from + i]);
+       printf("%s\n    ", max != len ? " ..." : "");
+       for (i = 0; i < len; i++)
+               printf(" %2c", o[from + i] > ' ' ? o[from + i]: '.');
+       printf("\nNEW:");
+       for (i = 0; i < len; i++)
+               printf(" %02x", n[from + i]);
+       printf("%s\n    ", max != len ? " ..." : "");
+       for (i = 0; i < len; i++)
+               printf(" %2c", n[from + i] > ' ' ? n[from + i]: '.');
+       printf("\n");
+}
+
+/*
+ * Xfrom-to (e.g. R10-15)
+ * A - change allowed
+ * S - change required, semantic
+ * R - change required, random
+ * F - change forbidden
+ */
+static enum df check(const char *range, unsigned char *o, unsigned char *n)
+{
+       char strict;
+       unsigned long long from, to;
+       enum df ret;
+
+       if (sscanf(range, "%c%llu-%llu", &strict, &from, &to) != 3) {
+               printf("Unknown range format %s.\n", range);
+               return FAIL;
+       }
+
+       switch (toupper(strict)) {
+       case 'A':
+               ret = OK;
+               break;
+       case 'S':
+               ret = memcmp(&o[from], &n[from], to - from + 1) != 0 ? OK : FAIL;
+               break;
+       case 'R': /* FIXME - random test */
+               ret = memcmp(&o[from], &n[from], to - from + 1) != 0 ? OK : FAIL;
+               break;
+       case 'F':
+               ret = memcmp(&o[from], &n[from], to - from + 1) == 0 ? OK : FAIL;
+               break;
+       default:
+               ret = FAIL;
+               break;
+       }
+
+       if (ret == FAIL)
+               print_diff(from,  to - from + 1, o, n);
+
+       return ret;
+}
+
+static int open_mmap(struct ffile *f)
+{
+       struct stat st;
+
+       f->fd = open(f->name, O_RDONLY);
+       if (f->fd == -1 || fstat(f->fd, &st) == -1)
+               return 0;
+
+       f->size = st.st_size;
+       f->addr = mmap(NULL, f->size, PROT_READ, MAP_PRIVATE, f->fd, 0);
+
+       return (f->addr == MAP_FAILED) ? 0 : 1;
+}
+
+static void close_mmap(struct ffile *f)
+{
+       if (f->addr != MAP_FAILED && !munmap(f->addr, f->size))
+               f->addr = MAP_FAILED;
+
+       if (f->fd != -1 && !close(f->fd))
+               f->fd = -1;
+}
+
+int main(int argc, char *argv[])
+{
+       int i, r = 1;
+       struct ffile file_old = {
+               .fd = -1,
+               .addr = MAP_FAILED,
+       };
+       struct ffile file_new = {
+               .fd = -1,
+               .addr = MAP_FAILED,
+       };
+
+       if (argc < 3) {
+               printf("Use: differ old_file new_file change_list.\n");
+               goto bad;
+       }
+
+       file_old.name = argv[1];
+       if (!open_mmap(&file_old))
+               goto bad;
+
+       file_new.name = argv[2];
+       if (!open_mmap(&file_new))
+               goto bad;
+
+       for (i = 3; i < argc; i++)
+               if (check(argv[i], file_old.addr, file_new.addr) == FAIL) {
+                       printf ("FAILED for %s\n", argv[i]);
+                       r = 1;
+                       goto bad;
+               }
+
+       r = 0;
+bad:
+       close_mmap(&file_new);
+       close_mmap(&file_old);
+
+       return r;
+}
diff --git a/tests/fileDiffer.py b/tests/fileDiffer.py
deleted file mode 100755 (executable)
index 43f4987..0000000
+++ /dev/null
@@ -1,129 +0,0 @@
-#!/usr/bin/python
-
-#
-# Usage: fileDiffer <afile> <bfile> <list of disk changes>
-# 
-
-# LUKS 
-# quick regression test suite
-# Tests LUKS images for changes at certain disk offsets
-#
-# Does fast python code has to look ugly or is it just me?
-
-import sys
-
-class changes:
-       pass
-
-def parseArgs(args):
-       aFileName = args[1]
-       bFileName = args[2]
-       changelist = []
-       args[0:3] = []
-       for i in args:
-               mychanges = changes();
-               if i.startswith('A'):
-                       mychanges.mode = 'ALLOWED'
-               if i.startswith('R'):
-                       mychanges.mode = 'REQUIRED'
-                       mychanges.strictness = 'RANDOM'
-               if i.startswith('S'):
-                       mychanges.mode = 'REQUIRED'
-                       mychanges.strictness = 'SEMANTIC'
-               
-               dashIndex = i.find('-')
-               if dashIndex == -1:                     
-                       mychanges.starts = int(i[1:])
-                       mychanges.ends = mychanges.starts
-               else:
-                       mychanges.starts = int(i[1:dashIndex])
-                       mychanges.ends = int(i[dashIndex+1:])
-               mychanges.miss = 0
-               changelist.append(mychanges)
-       mychanges = changes();
-       mychanges.starts = 0
-#      mychanges.ends will be fixed later
-       mychanges.mode = 'FORBIDDEN'    
-       changelist.append(mychanges)
-       return [aFileName, bFileName, changelist]
-
-def mode(i):
-       for c in changelist:
-               if i >= c.starts and i<=c.ends:
-                       return c
-def cleanchanges(i):
-       newchangelist=[]        
-       for c in changelist:
-               if i <= c.starts or i <= c.ends:
-                       newchangelist.append(c)
-       return newchangelist
-
-[aFileName, bFileName, changelist] = parseArgs(sys.argv)
-
-aFile = open(aFileName,'r')
-bFile = open(bFileName,'r')
-
-aString = aFile.read()
-bString = bFile.read()
-
-if len(aString) != len(bString): 
-       sys.exit("Mismatch different file sizes")
-
-fileLen = len(aString)
-fileLen10th = fileLen/10
-
-# Create a catch all entry
-changelist[-1].ends = fileLen
-
-print "Changes list: (FORBIDDEN default)"
-print "start\tend\tmode\t\tstrictness"
-for i in changelist:
-       if i.mode == 'REQUIRED':
-               print "%d\t%d\t%s\t%s" % (i.starts, i.ends, i.mode, i.strictness)
-       else:
-               print "%d\t%d\t%s" % (i.starts, i.ends, i.mode)
-
-
-filepos = 0
-fileLen10thC = 0
-print "[..........]"
-sys.stdout.write("[")
-sys.stdout.flush()
-
-modeNotTrivial = 1
-while filepos < fileLen:
-
-       if modeNotTrivial == 1:
-               c = mode(filepos)
-#      print (filepos, c.mode)
-       if c.mode == 'REQUIRED':
-               if aString[filepos] == bString[filepos]:
-                       c.miss = c.miss + 1
-       else:
-               if aString[filepos] != bString[filepos] and c.mode != 'ALLOWED':
-                       sys.exit("Mismatch at %d: change forbidden" % filepos)
-
-       # Do some maintaince, print progress bar, and clean changelist
-       #
-       # Maintaining two counters appears to be faster than modulo operation
-       if fileLen10thC == fileLen10th:
-               fileLen10thC = 0
-               sys.stdout.write(".")
-               sys.stdout.flush()
-               changelist = cleanchanges(filepos)
-               if len(changelist) == 1:
-                       modeNotTrivial = 0
-       filepos = filepos + 1
-       fileLen10thC = fileLen10thC + 1
-
-for c in changelist:
-       if c.mode == 'REQUIRED':
-               if c.strictness == 'SEMANTIC' and c.miss == (c.ends-c.starts+1):
-                       sys.exit("Mismatch: not even a single change in region %d-%d." % (c.starts, c.ends))
-               # This is not correct. We should do a statistical test
-               # of the sampled data against the hypothetical distribution
-               # of collision. Chi-Square Test.
-               if c.strictness == 'RANDOM' and c.miss == (c.ends-c.starts+1):
-                       sys.exit("Mismatch: not even a single change in region %d-%d." % (c.starts, c.ends))
-
-print ".] - everything ok"