Generic framework for reading compressed files using standard I/O
authorH. Peter Anvin <hpa@zytor.com>
Fri, 29 Feb 2008 19:57:36 +0000 (11:57 -0800)
committerH. Peter Anvin <hpa@zytor.com>
Fri, 29 Feb 2008 19:57:36 +0000 (11:57 -0800)
Add zopen(), zfopen(), and zloadfile() to transparently open and
uncompress a gzip file (adding support for other formats is quite
trivial.

Once a file handle or file pointer is received, it can be treated like
any other one (fstat will report it as a socket, since the length is
unknown.)

com32/include/syslinux/loadfile.h
com32/lib/Makefile
com32/lib/sys/file.h
com32/lib/sys/fileread.c
com32/lib/sys/zfile.c [new file with mode: 0644]
com32/lib/sys/zfopen.c [new file with mode: 0644]
com32/lib/syslinux/floadfile.c [new file with mode: 0644]
com32/lib/syslinux/loadfile.c
com32/lib/syslinux/zloadfile.c [new file with mode: 0644]

index c565cc9..1a04c51 100644 (file)
@@ -9,6 +9,7 @@
 #define LOADFILE_ZERO_PAD      64
 
 int loadfile(const char *, void **, size_t *);
+int zloadfile(const char *, void **, size_t *);
 int floadfile(FILE *, void **, size_t *, const void *, size_t);
 
 #endif
index 3dec5c3..6c08c22 100644 (file)
@@ -29,7 +29,11 @@ LIBOBJS = \
        sys/entry.o sys/exit.o sys/argv.o sys/times.o                   \
        sys/fileinfo.o sys/opendev.o sys/read.o sys/write.o sys/ftell.o \
        sys/close.o sys/open.o sys/fileread.o sys/fileclose.o           \
-       sys/isatty.o sys/fstat.o sys/openconsole.o sys/line_input.o     \
+       sys/isatty.o sys/fstat.o                                        \
+       \
+       sys/zfile.o sys/zfopen.o                                        \
+       \
+       sys/openconsole.o sys/line_input.o                              \
        sys/colortable.o sys/screensize.o                               \
        \
        sys/stdcon_read.o sys/stdcon_write.o sys/rawcon_read.o          \
@@ -78,7 +82,8 @@ LIBOBJS = \
        syslinux/run_default.o syslinux/run_command.o                   \
        syslinux/cleanup.o syslinux/localboot.o syslinux/runimage.o     \
        \
-       syslinux/loadfile.o                                             \
+       syslinux/loadfile.o syslinux/floadfile.o syslinux/zloadfile.o   \
+       \
        syslinux/load_linux.o syslinux/initramfs.o                      \
        syslinux/initramfs_file.o syslinux/initramfs_loadfile.o         \
        syslinux/initramfs_archive.o                                    \
index bca40f7..11dacd8 100644 (file)
@@ -80,6 +80,11 @@ struct file_info {
   const struct input_dev *iop; /* Input operations */
   const struct output_dev *oop;        /* Output operations */
 
+  /* Output file data */
+  struct {
+    int rows, cols;            /* Rows and columns */
+  } o;
+
   /* Structure used for input blocking */
   struct {
     int blocklg2;              /* Blocksize log 2 */
@@ -89,13 +94,9 @@ struct file_info {
     uint16_t _filler;          /* Unused */
     size_t nbytes;             /* Number of bytes available in buffer */
     char *datap;               /* Current data pointer */
+    void *pvt;                 /* Private pointer for driver */
     char buf[MAXBLOCK];
   } i;
-
-  /* Output file data */
-  struct {
-    int rows, cols;            /* Rows and columns */
-  } o;
 };
 
 extern struct file_info __file_info[NFILES];
index 5e528b1..79d912d 100644 (file)
 #include <minmax.h>
 #include "file.h"
 
-ssize_t __file_read(struct file_info *fp, void *buf, size_t count)
+int __file_get_block(struct file_info *fp)
 {
   com32sys_t ireg, oreg;
-  char *bufp = buf;
-  size_t n = 0;
-  size_t ncopy;
 
   memset(&ireg, 0, sizeof ireg);
   ireg.eax.w[0] = 0x0007;      /* Read file */
   ireg.ebx.w[0] = OFFS(__com32.cs_bounce);
   ireg.es = SEG(__com32.cs_bounce);
+  ireg.esi.w[0] = fp->i.filedes;
+  ireg.ecx.w[0] = MAXBLOCK >> fp->i.blocklg2;
+
+  __intcall(0x22, &ireg, &oreg);
+
+  if ( oreg.eflags.l & EFLAGS_CF ) {
+    errno = EIO;
+    return -1;
+  }
+
+  fp->i.filedes = oreg.esi.w[0];
+  fp->i.nbytes = oreg.ecx.l;
+  fp->i.datap = fp->i.buf;
+  memcpy(fp->i.buf, __com32.cs_bounce, fp->i.nbytes);
+
+  return 0;
+}
+
+ssize_t __file_read(struct file_info *fp, void *buf, size_t count)
+{
+  char *bufp = buf;
+  ssize_t n = 0;
+  size_t ncopy;
 
   while ( count ) {
     if ( fp->i.nbytes == 0 ) {
       if ( fp->i.offset >= fp->i.length || !fp->i.filedes )
        return n;               /* As good as it gets... */
 
-      ireg.esi.w[0] = fp->i.filedes;
-      ireg.ecx.w[0] = MAXBLOCK >> fp->i.blocklg2;
-
-      __intcall(0x22, &ireg, &oreg);
-
-      if ( oreg.eflags.l & EFLAGS_CF ) {
-       errno = EIO;
-       return -1;
-      }
-
-      fp->i.filedes = oreg.esi.w[0];
-      fp->i.nbytes = oreg.ecx.l;
-      fp->i.datap = fp->i.buf;
-      memcpy(fp->i.buf, __com32.cs_bounce, fp->i.nbytes);
+      if ( __file_get_block(fp) )
+       return n ? n : -1;
     }
 
     ncopy = min(count, fp->i.nbytes);
diff --git a/com32/lib/sys/zfile.c b/com32/lib/sys/zfile.c
new file mode 100644 (file)
index 0000000..9dbee9b
--- /dev/null
@@ -0,0 +1,171 @@
+/* ----------------------------------------------------------------------- *
+ *
+ *   Copyright 2003-2008 H. Peter Anvin - All Rights Reserved
+ *
+ *   Permission is hereby granted, free of charge, to any person
+ *   obtaining a copy of this software and associated documentation
+ *   files (the "Software"), to deal in the Software without
+ *   restriction, including without limitation the rights to use,
+ *   copy, modify, merge, publish, distribute, sublicense, and/or
+ *   sell copies of the Software, and to permit persons to whom
+ *   the Software is furnished to do so, subject to the following
+ *   conditions:
+ *
+ *   The above copyright notice and this permission notice shall
+ *   be included in all copies or substantial portions of the Software.
+ *
+ *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ *   OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ *   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ *   HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ *   WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ *   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ *   OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * ----------------------------------------------------------------------- */
+
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <syslinux/zio.h>
+
+#include "file.h"
+#include "zlib.h"
+
+/*
+ * zopen.c
+ *
+ * Open an ordinary file, possibly compressed; if so, insert
+ * an appropriate decompressor.
+ */
+
+int __file_get_block(struct file_info *fp);
+int __file_close(struct file_info *fp);
+
+static ssize_t gzip_file_read(struct file_info *, void *, size_t);
+static int gzip_file_close(struct file_info *);
+
+static const struct input_dev gzip_file_dev = {
+  .dev_magic = __DEV_MAGIC,
+  .flags     = __DEV_FILE | __DEV_INPUT,
+  .fileflags = O_RDONLY,
+  .read      = gzip_file_read,
+  .close     = gzip_file_close,
+  .open      = NULL,
+};
+
+static int gzip_file_init(struct file_info *fp)
+{
+  z_streamp zs = calloc(1, sizeof(z_stream));
+
+  if (!zs)
+    return -1;
+
+  fp->i.pvt = zs;
+
+  zs->next_in  = (void *)fp->i.datap;
+  zs->avail_in = fp->i.nbytes;
+
+  if (inflateInit2(zs, 15+32) != Z_OK) {
+    errno = EIO;
+    return -1;
+  }
+
+  fp->iop = &gzip_file_dev;
+  fp->i.length = -1;           /* Unknown */
+
+  return 0;
+}
+
+static ssize_t gzip_file_read(struct file_info *fp, void *ptr, size_t n)
+{
+  z_streamp zs = fp->i.pvt;
+  int rv;
+  ssize_t bytes;
+  ssize_t nout = 0;
+  unsigned char *p = ptr;
+
+  while ( n ) {
+    zs->next_out = p;
+    zs->avail_out = n;
+
+    if (!zs->avail_in && fp->i.filedes) {
+      if (__file_get_block(fp))
+       return nout ? nout : -1;
+
+      zs->next_in = (void *)fp->i.datap;
+      zs->avail_in = fp->i.nbytes;
+    }
+
+    rv = inflate(zs, Z_SYNC_FLUSH);
+
+    bytes = n - zs->avail_out;
+    nout += bytes;
+    p += bytes;
+    n -= bytes;
+
+    switch (rv) {
+    case Z_DATA_ERROR:
+    case Z_NEED_DICT:
+    case Z_BUF_ERROR:
+    case Z_STREAM_ERROR:
+    default:
+      errno = EIO;
+      return nout ? nout : -1;
+    case Z_MEM_ERROR:
+      errno = ENOMEM;
+      return nout ? nout : -1;
+    case Z_STREAM_END:
+      return nout;
+    case Z_OK:
+      break;
+    }
+  }
+
+  return nout;
+}
+
+static int gzip_file_close(struct file_info *fp)
+{
+  z_streamp zs = fp->i.pvt;
+
+  inflateEnd(zs);
+  free(zs);
+  return __file_close(fp);
+}
+
+int zopen(const char *pathname, int flags, ...)
+{
+  int fd, rv;
+  struct file_info *fp;
+
+  /* We don't actually give a hoot about the creation bits... */
+  fd = open(pathname, flags, 0);
+
+  if ( fd < 0 )
+    return -1;
+
+  fp = &__file_info[fd];
+
+  /* Need to get the first block into the buffer, but not consumed */
+  if ( __file_get_block(fp) )
+    goto err;
+
+  if (fp->i.nbytes >= 14 &&
+      (uint8_t)fp->i.buf[0] == 037 &&
+      (uint8_t)fp->i.buf[1] == 0213 && /* gzip */
+      fp->i.buf[2] == 8) /* deflate */
+    rv = gzip_file_init(fp);
+  else
+    rv = 0;                    /* Plain file */
+
+  if (!rv)
+    return fd;
+
+ err:
+  close(fd);
+  return -1;
+}
diff --git a/com32/lib/sys/zfopen.c b/com32/lib/sys/zfopen.c
new file mode 100644 (file)
index 0000000..dfd45de
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * zfopen.c
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <syslinux/zio.h>
+
+FILE *zfopen(const char *file, const char *mode)
+{
+  int flags = O_RDONLY;
+  int plus = 0;
+  int fd;
+
+  while ( *mode ) {
+    switch ( *mode ) {
+    case 'r':
+      flags = O_RDONLY;
+      break;
+    case 'w':
+      flags = O_WRONLY|O_CREAT|O_TRUNC;
+      break;
+    case 'a':
+      flags = O_WRONLY|O_CREAT|O_APPEND;
+      break;
+    case '+':
+      plus = 1;
+      break;
+    }
+    mode++;
+  }
+
+  if ( plus ) {
+    flags = (flags & ~(O_RDONLY|O_WRONLY)) | O_RDWR;
+  }
+
+  fd = zopen(file, flags, 0666);
+
+  if ( fd < 0 )
+    return NULL;
+  else
+    return fdopen(fd, mode);
+}
diff --git a/com32/lib/syslinux/floadfile.c b/com32/lib/syslinux/floadfile.c
new file mode 100644 (file)
index 0000000..ea10829
--- /dev/null
@@ -0,0 +1,108 @@
+/* ----------------------------------------------------------------------- *
+ *
+ *   Copyright 2005-2008 H. Peter Anvin - All Rights Reserved
+ *
+ *   Permission is hereby granted, free of charge, to any person
+ *   obtaining a copy of this software and associated documentation
+ *   files (the "Software"), to deal in the Software without
+ *   restriction, including without limitation the rights to use,
+ *   copy, modify, merge, publish, distribute, sublicense, and/or
+ *   sell copies of the Software, and to permit persons to whom
+ *   the Software is furnished to do so, subject to the following
+ *   conditions:
+ *
+ *   The above copyright notice and this permission notice shall
+ *   be included in all copies or substantial portions of the Software.
+ *
+ *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ *   OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ *   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ *   HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ *   WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ *   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ *   OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * floadfile.c
+ *
+ * Read the contents of a data file into a malloc'd buffer
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+#include <syslinux/loadfile.h>
+
+#define INCREMENTAL_CHUNK 1024*1024
+
+int floadfile(FILE *f, void **ptr, size_t *len, const void *prefix,
+             size_t prefix_len)
+{
+  struct stat st;
+  void *data, *dp;
+  size_t alen, clen, rlen, xlen;
+
+  clen = alen = 0;
+  data = NULL;
+
+  if ( fstat(fileno(f), &st) )
+    goto err;
+
+  if (!S_ISREG(st.st_mode)) {
+    /* Not a regular file, we can't assume we know the file size */
+    if (prefix_len) {
+      clen = alen = prefix_len;
+      data = malloc(prefix_len);
+      if (!data)
+       goto err;
+
+      memcpy(data, prefix, prefix_len);
+    }
+
+    do {
+      alen += INCREMENTAL_CHUNK;
+      dp = realloc(data, alen);
+      if (!dp)
+       goto err;
+      data = dp;
+
+      rlen = fread((char *)data+clen, 1, alen-clen, f);
+      clen += rlen;
+    } while (clen == alen);
+
+    *len = clen;
+    xlen = (clen + LOADFILE_ZERO_PAD-1) & ~(LOADFILE_ZERO_PAD-1);
+    dp = realloc(data, xlen);
+    if (dp)
+      data = dp;
+    *ptr = data;
+  } else {
+    *len = clen = st.st_size + prefix_len - ftell(f);
+    xlen = (clen + LOADFILE_ZERO_PAD-1) & ~(LOADFILE_ZERO_PAD-1);
+
+    *ptr = data = malloc(xlen);
+    if ( !data )
+      return -1;
+
+    memcpy(data, prefix, prefix_len);
+
+    if ( (off_t)fread((char *)data+prefix_len, 1, clen-prefix_len, f)
+        != clen-prefix_len )
+      goto err;
+  }
+
+  memset((char *)data + clen, 0, xlen-clen);
+  return 0;
+
+ err:
+  if (data)
+    free(data);
+  return -1;
+}
index f5479da..42a1fd6 100644 (file)
@@ -2,11 +2,26 @@
  *
  *   Copyright 2005-2008 H. Peter Anvin - 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 as published by
- *   the Free Software Foundation, Inc., 53 Temple Place Ste 330,
- *   Boston MA 02111-1307, USA; either version 2 of the License, or
- *   (at your option) any later version; incorporated herein by reference.
+ *   Permission is hereby granted, free of charge, to any person
+ *   obtaining a copy of this software and associated documentation
+ *   files (the "Software"), to deal in the Software without
+ *   restriction, including without limitation the rights to use,
+ *   copy, modify, merge, publish, distribute, sublicense, and/or
+ *   sell copies of the Software, and to permit persons to whom
+ *   the Software is furnished to do so, subject to the following
+ *   conditions:
+ *
+ *   The above copyright notice and this permission notice shall
+ *   be included in all copies or substantial portions of the Software.
+ *
+ *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ *   OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ *   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ *   HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ *   WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ *   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ *   OTHER DEALINGS IN THE SOFTWARE.
  *
  * ----------------------------------------------------------------------- */
 
@@ -41,68 +56,3 @@ int loadfile(const char *filename, void **ptr, size_t *len)
 
   return rv;
 }
-
-int floadfile(FILE *f, void **ptr, size_t *len, const void *prefix,
-             size_t prefix_len)
-{
-  struct stat st;
-  void *data, *dp;
-  size_t alen, clen, rlen, xlen;
-
-  clen = alen = 0;
-  data = NULL;
-
-  if ( fstat(fileno(f), &st) )
-    goto err;
-
-  if (!S_ISREG(st.st_mode)) {
-    /* Not a regular file, we can't assume we know the file size */
-    if (prefix_len) {
-      clen = alen = prefix_len;
-      data = malloc(prefix_len);
-      if (!data)
-       goto err;
-
-      memcpy(data, prefix, prefix_len);
-    }
-
-    do {
-      alen += INCREMENTAL_CHUNK;
-      dp = realloc(data, alen);
-      if (!dp)
-       goto err;
-      data = dp;
-
-      rlen = fread((char *)data+clen, 1, alen-clen, f);
-      clen += rlen;
-    } while (clen == alen);
-
-    *len = clen;
-    xlen = (clen + LOADFILE_ZERO_PAD-1) & ~(LOADFILE_ZERO_PAD-1);
-    dp = realloc(data, xlen);
-    if (dp)
-      data = dp;
-    *ptr = data;
-  } else {
-    *len = clen = st.st_size + prefix_len - ftell(f);
-    xlen = (clen + LOADFILE_ZERO_PAD-1) & ~(LOADFILE_ZERO_PAD-1);
-
-    *ptr = data = malloc(xlen);
-    if ( !data )
-      return -1;
-
-    memcpy(data, prefix, prefix_len);
-
-    if ( (off_t)fread((char *)data+prefix_len, 1, clen-prefix_len, f)
-        != clen-prefix_len )
-      goto err;
-  }
-
-  memset((char *)data + clen, 0, xlen-clen);
-  return 0;
-
- err:
-  if (data)
-    free(data);
-  return -1;
-}
diff --git a/com32/lib/syslinux/zloadfile.c b/com32/lib/syslinux/zloadfile.c
new file mode 100644 (file)
index 0000000..8dff193
--- /dev/null
@@ -0,0 +1,59 @@
+/* ----------------------------------------------------------------------- *
+ *
+ *   Copyright 2005-2008 H. Peter Anvin - All Rights Reserved
+ *
+ *   Permission is hereby granted, free of charge, to any person
+ *   obtaining a copy of this software and associated documentation
+ *   files (the "Software"), to deal in the Software without
+ *   restriction, including without limitation the rights to use,
+ *   copy, modify, merge, publish, distribute, sublicense, and/or
+ *   sell copies of the Software, and to permit persons to whom
+ *   the Software is furnished to do so, subject to the following
+ *   conditions:
+ *
+ *   The above copyright notice and this permission notice shall
+ *   be included in all copies or substantial portions of the Software.
+ *
+ *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ *   OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ *   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ *   HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ *   WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ *   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ *   OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * zloadfile.c
+ *
+ * Read the contents of a possibly compressed data file into a malloc'd buffer
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <syslinux/zio.h>
+
+#include <syslinux/loadfile.h>
+
+#define INCREMENTAL_CHUNK 1024*1024
+
+int zloadfile(const char *filename, void **ptr, size_t *len)
+{
+  FILE *f;
+  int rv;
+
+  f = zfopen(filename, "r");
+  if ( !f )
+    return -1;
+
+  rv = floadfile(f, ptr, len, NULL, 0);
+  fclose(f);
+
+  return rv;
+}