Change the magic number used to introduce a trie file, so that instead master
authorsimon <simon@cda61777-01e9-0310-a592-d414129be87e>
Sat, 8 Dec 2012 12:05:33 +0000 (12:05 +0000)
committersimon <simon@cda61777-01e9-0310-a592-d414129be87e>
Sat, 8 Dec 2012 12:05:33 +0000 (12:05 +0000)
of a single unsigned long it's a big structure filled with various
integer and pointer types. The idea is that when we check it on
reading an existing index file, it should point out any ABI mismatches
between the platform which generated the index and the one reading it.

In the course of this work I've had to actually add code to check the
magic numbers, which previously weren't checked at all and so would
have been useful only to file(1).

git-svn-id: svn://svn.tartarus.org/sgt/agedu@9723 cda61777-01e9-0310-a592-d414129be87e

agedu.c
trie.c
trie.h

diff --git a/agedu.c b/agedu.c
index 411ae6a..1847e2c 100644 (file)
--- a/agedu.c
+++ b/agedu.c
@@ -1272,6 +1272,11 @@ int main(int argc, char **argv)
                perror(PNAME ": mmap");
                return 1;
            }
+            if (!trie_check_magic(mappedfile)) {
+               fprintf(stderr, "%s: %s: magic numbers did not match\n"
+                        "%s: check that the index was built by this version of agedu on this platform\n", PNAME, filename, PNAME);
+               return 1;
+            }
            pathsep = trie_pathsep(mappedfile);
 
            /*
@@ -1355,6 +1360,21 @@ int main(int argc, char **argv)
                }
                return 1;
            }
+           if (!trie_check_magic(mappedfile)) {
+               fprintf(stderr, "%s: %s: magic numbers did not match\n"
+                        "%s: check that the index was built by this version of agedu on this platform\n", PNAME, filename, PNAME);
+               if (!querydir) {
+                   printf("Status: 500\nContent-type: text/html\n\n"
+                          "<html><head>"
+                          "<title>500 Internal Server Error</title>"
+                          "</head><body>"
+                          "<h1>500 Internal Server Error</h1>"
+                          "<p><code>agedu</code> suffered an internal error."
+                          "</body></html>\n");
+                   return 0;
+               }
+               return 1;
+           }
            pathsep = trie_pathsep(mappedfile);
 
            maxpathlen = trie_maxpathlen(mappedfile);
@@ -1557,6 +1577,11 @@ int main(int argc, char **argv)
                perror(PNAME ": mmap");
                return 1;
            }
+            if (!trie_check_magic(mappedfile)) {
+               fprintf(stderr, "%s: %s: magic numbers did not match\n"
+                        "%s: check that the index was built by this version of agedu on this platform\n", PNAME, filename, PNAME);
+               return 1;
+            }
            pathsep = trie_pathsep(mappedfile);
 
            maxpathlen = trie_maxpathlen(mappedfile);
@@ -1589,6 +1614,11 @@ int main(int argc, char **argv)
                perror(PNAME ": mmap");
                return 1;
            }
+            if (!trie_check_magic(mappedfile)) {
+               fprintf(stderr, "%s: %s: magic numbers did not match\n"
+                        "%s: check that the index was built by this version of agedu on this platform\n", PNAME, filename, PNAME);
+               return 1;
+            }
            pathsep = trie_pathsep(mappedfile);
 
            dcfg.address = httpserveraddr;
diff --git a/trie.c b/trie.c
index d170458..86ccd41 100644 (file)
--- a/trie.c
+++ b/trie.c
@@ -95,8 +95,54 @@ struct trie_string {
     char string[];
 };
 
+static const char magic_ident_string[16] = "agedu index file";
+struct trie_magic {
+    /*
+     * 'Magic numbers' to go at the start of an agedu index file.
+     * These are checked (using trie_check_magic) by every agedu mode
+     * which reads a pre-existing index.
+     *
+     * As well as identifying an agedu file from any other kind of
+     * file, this magic-number structure is also intended to detect
+     * agedu files which were written on the wrong platform and hence
+     * whose structure layouts are incompatible. To make that as
+     * reliable as possible, I design the structure of magic numbers
+     * as follows: it contains one of each integer type I might use,
+     * each containing a different magic number, and each followed by
+     * a char to indicate where it ends in the file. One byte is set
+     * to the length of the magic-number structure itself, which means
+     * that no two structures of different lengths can possibly
+     * compare equal even if by some chance they match up to the
+     * length of the shorter one. Finally, the whole magic number
+     * structure is memset to another random byte value before
+     * initialising any of these fields, so that padding in between
+     * can be readily identified.
+     */
+    char ident[16];                    /* human-readable string */
+
+    unsigned char magic_len;
+
+    unsigned long long longlong_magic;
+    unsigned char postlonglong_char_magic;
+
+    off_t offset_magic;
+    unsigned char postoffset_char_magic;
+
+    size_t size_magic;
+    unsigned char postsize_char_magic;
+
+    void *null_pointer;
+    unsigned char postptr_char_magic;
+
+    unsigned long long_magic;
+    unsigned char postlong_char_magic;
+
+    unsigned short short_magic;
+    unsigned char postshort_char_magic;
+};
+
 struct trie_header {
-    unsigned long magic;
+    struct trie_magic magic;
     off_t root, indexroot;
     int count;
     size_t maxpathlen;
@@ -118,9 +164,42 @@ union trie_node {
        char string[1];
     } str;
 };
-#define TRIE_MAGIC 0x75646761UL
 #define TRIE_ALIGN alignof(union trie_node)
 
+static void setup_magic(struct trie_magic *th)
+{
+    /*
+     * Magic values are chosen so that every byte value used is
+     * distinct (so that we can't fail to spot endianness issues), and
+     * we cast 64 bits of data into each integer type just in case
+     * the platform makes it longer than we expect it to be.
+     */
+
+    memset(th, 0xCDU, sizeof(*th));
+
+    th->magic_len = sizeof(*th);
+
+    memcpy(th->ident, magic_ident_string, 16);
+
+    th->longlong_magic = 0x5583F34D5D84F73CULL;
+    th->postlonglong_char_magic = 0xDDU;
+
+    th->offset_magic = (off_t)0xB39BF9AD56D48E0BULL;
+    th->postoffset_char_magic = 0x95U;
+
+    th->size_magic = (size_t)0x6EC752B0EEAEBAC1ULL;
+    th->postsize_char_magic = 0x77U;
+
+    th->null_pointer = NULL;
+    th->postptr_char_magic = 0x71U;
+
+    th->long_magic = (unsigned long)0xA81A5E1F44334716ULL;
+    th->postlong_char_magic = 0x99U;
+
+    th->short_magic = (unsigned short)0x0C8BD7984B68D9FCULL;
+    th->postshort_char_magic = 0x35U;
+}
+
 /* ----------------------------------------------------------------------
  * Trie-building functions.
  */
@@ -187,7 +266,7 @@ triebuild *triebuild_new(int fd)
     tb->switchsize = 0;
     tb->maxpathlen = 0;
 
-    th.magic = TRIE_MAGIC;
+    setup_magic(&th.magic);
     th.root = th.count = 0;
     th.indexroot = 0;
     th.maxpathlen = 0;
@@ -364,7 +443,7 @@ int triebuild_finish(triebuild *tb)
 {
     struct trie_header th;
 
-    th.magic = TRIE_MAGIC;
+    setup_magic(&th.magic);
     th.root = triebuild_unwind(tb, 0, &th.count);
     th.indexroot = 0;
     th.maxpathlen = tb->maxpathlen;
@@ -502,6 +581,15 @@ void trie_fake_dir_atimes(void *t)
 #define NODE(t, off, type) \
     ((const struct type *)((const char *)(t) + (off)))
 
+int trie_check_magic(const void *t)
+{
+    const struct trie_header *hdr = NODE(t, 0, trie_header);
+    struct trie_magic magic;
+
+    setup_magic(&magic);
+    return !memcmp(&magic, &hdr->magic, sizeof(magic));
+}
+
 size_t trie_maxpathlen(const void *t)
 {
     const struct trie_header *hdr = NODE(t, 0, trie_header);
diff --git a/trie.h b/trie.h
index ea24ef8..e27885f 100644 (file)
--- a/trie.h
+++ b/trie.h
@@ -66,6 +66,14 @@ void trie_fake_dir_atimes(void *t);
  */
 
 /*
+ * Check the magic numbers at the start of the file. This should also
+ * verify that the file was built on a platform whose structure layout
+ * matches that of the agedu reading it. Returns nonzero on successful
+ * match, zero on mismatch.
+ */
+int trie_check_magic(const void *t);
+
+/*
  * Return the path separator character in use in the trie.
  */
 char trie_pathsep(const void *t);