Handle memdisk images compressed with zip as well as with gzip.
authorhpa <hpa>
Tue, 27 Apr 2004 06:48:59 +0000 (06:48 +0000)
committerhpa <hpa>
Tue, 27 Apr 2004 06:48:59 +0000 (06:48 +0000)
NEWS
memdisk/inflate.c
memdisk/init.S16
memdisk/memdisk.h
memdisk/setup.c
memdisk/unzip.c

diff --git a/NEWS b/NEWS
index 3f1682b..a89b153 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -2,6 +2,12 @@ Starting with 1.47, changes marked with SYSLINUX/PXELINUX/ISOLINUX
 apply to that specific program only; other changes apply to all of
 them.
 
+Changes in 2.10:
+       * MEMDISK: Handle images compressed with zip as well as with
+         gzip.  Some Windows-based image tools apparently generate
+         these kinds of images by default.  Patch by Patrick
+         LoPresti.
+
 Changes in 2.09:
        * SYSLINUX: Remove residual setuid crap from
          syslinux-nomtools.
index 9d701d6..2974970 100644 (file)
@@ -1055,71 +1055,10 @@ makecrc(void)
 /*
  * Do the uncompression!
  */
-int gunzip(void)
+int gunzip()
 {
-    uch flags;
-    unsigned char magic[2]; /* magic header */
-    char method;
-    ulg orig_crc = 0;       /* original crc */
-    ulg orig_len = 0;       /* original uncompressed length */
     int res;
 
-    magic[0] = (unsigned char)get_byte();
-    magic[1] = (unsigned char)get_byte();
-    method = (unsigned char)get_byte();
-
-    if (magic[0] != 037 ||
-       ((magic[1] != 0213) && (magic[1] != 0236))) {
-           error("bad gzip magic numbers");
-           return -1;
-    }
-
-    /* We only support method #8, DEFLATED */
-    if (method != 8)  {
-           error("internal error, invalid method");
-           return -1;
-    }
-
-    flags  = (uch)get_byte();
-    if ((flags & ENCRYPTED) != 0) {
-           error("Input is encrypted");
-           return -1;
-    }
-    if ((flags & CONTINUATION) != 0) {
-           error("Multi part input");
-           return -1;
-    }
-    if ((flags & RESERVED) != 0) {
-           error("Input has invalid flags");
-           return -1;
-    }
-
-    /* Ignore timestamp */
-    (void)get_byte();
-    (void)get_byte();
-    (void)get_byte();
-    (void)get_byte();
-
-    (void)get_byte();  /* Ignore extra flags for the moment */
-    (void)get_byte();  /* Ignore OS type for the moment */
-
-    if ((flags & EXTRA_FIELD) != 0) {
-           unsigned len = (unsigned)get_byte();
-           len |= ((unsigned)get_byte())<<8;
-           while (len--) (void)get_byte();
-    }
-
-    /* Get original file name if it was truncated */
-    if ((flags & ORIG_NAME) != 0) {
-           /* Discard the old name */
-           while (get_byte() != 0) /* null */ ;
-    } 
-
-    /* Discard file comment if any */
-    if ((flags & COMMENT) != 0) {
-           while (get_byte() != 0) /* null */ ;
-    }
-
     /* Decompress */
     if ((res = inflate())) {
            switch (res) {
@@ -1139,29 +1078,6 @@ int gunzip(void)
            }
            return -1;
     }
-           
-    /* Get the crc and original length */
-    /* crc32  (see algorithm.doc)
-     * uncompressed input size modulo 2^32
-     */
-    orig_crc = (ulg) get_byte();
-    orig_crc |= (ulg) get_byte() << 8;
-    orig_crc |= (ulg) get_byte() << 16;
-    orig_crc |= (ulg) get_byte() << 24;
-    
-    orig_len = (ulg) get_byte();
-    orig_len |= (ulg) get_byte() << 8;
-    orig_len |= (ulg) get_byte() << 16;
-    orig_len |= (ulg) get_byte() << 24;
-    
-    /* Validate decompression */
-    if (orig_crc != CRC_VALUE) {
-           error("crc error");
-           return -1;
-    }
-    if (orig_len != bytes_out) {
-           error("length error");
-           return -1;
-    }
+
     return 0;
 }
index a70f08d..0a4a330 100644 (file)
@@ -42,7 +42,7 @@ boot_flag:    .word 0xAA55
 _start:
                jmp     start
 
-# This is the setup header, and it must start at %cs:2 (old 0x9020:2)
+ # This is the setup header, and it must start at %cs:2 (old 0x9020:2)
        
                .ascii  "HdrS"          # header signature
                .word   0x0203          # header version number (>= 0x0105)
@@ -57,7 +57,7 @@ type_of_loader:       .byte   0               # = 0, old one (LILO, Loadlin,
                                        # See Documentation/i386/boot.txt for
                                        # assigned ids
        
-# flags, unused bits must be zero (RFU) bit within loadflags
+ # flags, unused bits must be zero (RFU) bit within loadflags
 loadflags:
 LOADED_HIGH    = 1                     # If set, the kernel is loaded high
 CAN_USE_HEAP   = 0x80                  # If set, the loader also has set
index ace53b0..5377120 100644 (file)
@@ -47,6 +47,10 @@ memcpy_endptr(void *__d, const void *__s, unsigned int __n)
 }
 
 /* Decompression */
-void *unzip(void *indata, unsigned long zbytes, void *target);
+extern int check_zip(void *indata, uint32_t size, uint32_t *zbytes_p,
+                     uint32_t *dbytes_p, uint32_t *orig_crc,
+                     uint32_t *offset_p);
+extern void *unzip(void *indata, uint32_t zbytes, uint32_t dbytes,
+                   uint32_t orig_crc, void *target);
 
 #endif
index aff0548..b3bcc9d 100644 (file)
@@ -232,14 +232,23 @@ void unzip_if_needed(uint32_t *where_p, uint32_t *size_p)
 {
   uint32_t where = *where_p;
   uint32_t size = *size_p;
+  uint32_t zbytes;
   uint32_t startrange, endrange;
   uint32_t gzdatasize, gzwhere;
+  uint32_t orig_crc, offset;
   uint32_t target = 0;
   int i, okmem;
 
   /* Is it a gzip image? */
-  if ( *(uint16_t *)where == 0x8b1f ) {
-    gzdatasize = *(uint32_t *)(where + size - 4);
+  if (check_zip ((void *)where, size, &zbytes, &gzdatasize,
+                 &orig_crc, &offset) == 0) {
+
+    if (offset + zbytes > size) {
+      /* Assertion failure; check_zip is supposed to guarantee this
+         never happens. */
+      puts("internal error: check_zip returned nonsense\n");
+      die();
+    }
 
     /* Find a good place to put it: search memory ranges in descending order
        until we find one that is legal and fits */
@@ -312,7 +321,8 @@ void unzip_if_needed(uint32_t *where_p, uint32_t *size_p)
           target, gzdatasize);
 
     *size_p  = gzdatasize;
-    *where_p = (uint32_t)unzip((void *)where, size, (void *)target);
+    *where_p = (uint32_t)unzip((void *)(where + offset), zbytes,
+                               gzdatasize, orig_crc, (void *)target);
   }
 }
 
index de49e2a..b7d2fdd 100644 (file)
@@ -177,6 +177,171 @@ static void error(char *x)
   die();
 }
 
+/* GZIP header */
+struct gzip_header {
+  uint16_t magic;
+  uint8_t method;
+  uint8_t flags;
+  uint32_t timestamp;
+  uint8_t extra_flags;
+  uint8_t os_type;
+} __attribute__ ((packed));
+/* (followed by optional and variable length "extra", "original name",
+   and "comment" fields) */
+
+struct gzip_trailer {
+  uint32_t crc;
+  uint32_t dbytes;
+} __attribute__ ((packed));
+
+/* PKZIP header.  See
+ * <http://www.pkware.com/products/enterprise/white_papers/appnote.html>.
+ */
+struct pkzip_header {
+  uint32_t magic;
+  uint16_t version;
+  uint16_t flags;
+  uint16_t method;
+  uint16_t modified_time;
+  uint16_t modified_date;
+  uint32_t crc;
+  uint32_t zbytes;
+  uint32_t dbytes;
+  uint16_t filename_len;
+  uint16_t extra_len;
+} __attribute__ ((packed));
+/* (followed by optional and variable length "filename" and "extra"
+   fields) */
+
+/* gzip flag byte */
+#define ASCII_FLAG   0x01 /* bit 0 set: file probably ASCII text */
+#define CONTINUATION 0x02 /* bit 1 set: continuation of multi-part gzip file */
+#define EXTRA_FIELD  0x04 /* bit 2 set: extra field present */
+#define ORIG_NAME    0x08 /* bit 3 set: original file name present */
+#define COMMENT      0x10 /* bit 4 set: file comment present */
+#define ENCRYPTED    0x20 /* bit 5 set: file is encrypted */
+#define RESERVED     0xC0 /* bit 6,7:   reserved */
+
+/* pkzip flag byte */
+#define PK_ENCRYPTED     0x01  /* bit 0 set: file is encrypted */
+#define PK_DATADESC       0x08  /* bit 3 set: file has trailing "data
+                                   descriptor" */
+#define PK_UNSUPPORTED    0xFFF0 /* All other bits must be zero */
+
+
+/* Return 0 if (indata, size) points to a ZIP file, and fill in
+   compressed data size, uncompressed data size, CRC, and offset of
+   data.
+
+   If indata is not a ZIP file, return -1. */
+int check_zip(void *indata, uint32_t size, uint32_t *zbytes_p,
+              uint32_t *dbytes_p, uint32_t *orig_crc, uint32_t *offset_p) {
+  struct gzip_header *gzh = (struct gzip_header *)indata;
+  struct pkzip_header *pkzh = (struct pkzip_header *)indata;
+  uint32_t offset;
+
+  if (gzh->magic == 0x8b1f) {
+    struct gzip_trailer *gzt = indata + size - sizeof (struct gzip_trailer);
+    /* We only support method #8, DEFLATED */
+    if (gzh->method != 8)  {
+      error("gzip file uses invalid method");
+      return -1;
+    }
+    if (gzh->flags & ENCRYPTED) {
+      error("gzip file is encrypted; not supported");
+      return -1;
+    }
+    if (gzh->flags & CONTINUATION) {
+      error("gzip file is a continuation file; not supported");
+      return -1;
+    }
+    if (gzh->flags & RESERVED) {
+      error("gzip file has unsupported flags");
+      return -1;
+    }
+    offset = sizeof (*gzh);
+    if (gzh->flags & EXTRA_FIELD) {
+      /* Skip extra field */
+      unsigned len = *(unsigned *)(indata + offset);
+      offset += 2 + len;
+    }
+    if (gzh->flags & ORIG_NAME) {
+      /* Discard the old name */
+      uint8_t *p = indata;
+      while (p[offset] != 0 && offset < size) {
+        offset++;
+      }
+      offset++;
+    }
+    
+    if (gzh->flags & COMMENT) {
+      /* Discard the comment */
+      uint8_t *p = indata;
+      while (p[offset] != 0 && offset < size) {
+        offset++;
+      }
+      offset++;
+    }
+
+    if (offset > size) {
+      error ("gzip file corrupt");
+      return -1;
+    }
+    *zbytes_p = size - offset - sizeof (struct gzip_trailer);
+    *dbytes_p = gzt->dbytes;
+    *orig_crc = gzt->crc;
+    *offset_p = offset;
+    return 0;
+  }
+  else if (pkzh->magic == 0x04034b50UL) {
+    /* Magic number matches pkzip file. */
+    
+    offset = sizeof (*pkzh);
+    if (pkzh->flags & PK_ENCRYPTED) {
+      error("pkzip file is encrypted; not supported");
+      return -1;
+    }
+    if (pkzh->flags & PK_DATADESC) {
+      error("pkzip file uses data_descriptor field; not supported");
+      return -1;
+    }
+    if (pkzh->flags & PK_UNSUPPORTED) {
+      error("pkzip file has unsupported flags");
+      return -1;
+    }
+
+    /* We only support method #8, DEFLATED */
+    if (pkzh->method != 8) {
+      error("pkzip file uses invalid method");
+      return -1;
+    }
+    /* skip header */
+    offset = sizeof (*pkzh);
+    /* skip filename */
+    offset += pkzh->filename_len;
+    /* skip extra field */
+    offset += pkzh->extra_len;
+
+    if (offset + pkzh->zbytes > size) {
+      error ("pkzip file corrupt");
+      return -1;
+    }
+
+    *zbytes_p = pkzh->zbytes;
+    *dbytes_p = pkzh->dbytes;
+    *orig_crc = pkzh->crc;
+    *offset_p = offset;
+    return 0;
+  }
+  else {
+    /* Magic number does not match. */
+    return -1;
+  }
+
+  error ("Internal error in check_zip");
+  return -1;
+}
+
 /*
  * Decompress the image, trying to flush the end of it as close
  * to end_mem as possible.  Return a pointer to the data block,
@@ -184,18 +349,19 @@ static void error(char *x)
  */
 extern void _end;
 
-void *unzip(void *indata, unsigned long zbytes, void *target)
+void *unzip(void *indata, uint32_t zbytes, uint32_t dbytes,
+            uint32_t orig_crc, void *target)
 {
-  /* The uncompressed length of a gzip file is the last four bytes */
-  unsigned long dbytes = *(uint32_t *)((char *)indata + zbytes - 4);
-
   /* Set up the heap; it's the 64K after the bounce buffer */
   free_mem_ptr = (ulg)sys_bounce + 0x10000;
   free_mem_end_ptr = free_mem_ptr + 0x10000;
 
   /* Set up input buffer */
   inbuf  = indata;
-  insize = inbytes = zbytes;
+  /* Sometimes inflate() looks beyond the end of the compressed data,
+     but it always backs up before it is done.  So we give it 4 bytes
+     of slack. */
+  insize = inbytes = zbytes + 4;
 
   /* Set up output buffer */
   outcnt = 0;
@@ -206,8 +372,16 @@ void *unzip(void *indata, unsigned long zbytes, void *target)
   makecrc();
   gunzip();
 
+  /* Verify that gunzip() consumed the entire input. */
+  if (inbytes != 4)
+    error("compressed data length error");
+
+  /* Check the uncompressed data length and CRC. */
   if ( bytes_out != dbytes )
-    error("length error");
+    error("uncompressed data length error");
+
+  if (orig_crc != CRC_VALUE)
+    error("crc error");
 
   puts("ok\n");