On Sat, 2003-06-07 at 10:13, Poul-Henning Kamp wrote:

> In message <[EMAIL PROTECTED]>, David Yeske writes:
>> imgact_gzip.c seems to be pretty stale.  Has anyone considered fixing this?  If 
>> this were fixed
>> then kldload() / linker_load_module() could deal with a gzipped .ko file, and 
>> gzipped elf
>> executables would work also?
>
> At least originally imgact_gzip.c was heavily a.out aware.

Interesting.

Making imgact_gzip elf-aware would not make the kernel capable of loading gzipped 
modules,
only executables. There's a separate link_elf.c that the kernel uses for linking ELF 
images
into itself (rather than activating ELF executables at exec() time, with imgact_elf)

I've been fiddling a little with compressed data in the kernel already, and was able 
to hack together
a patch for link_elf pretty quickly. The "quickly" means that there's no boot loader 
support, and the
gzip handling is quite braindamaged, extracting the entire zipped file into allocated 
memory before
parsing the ELF structure. This is mainly because the ELF parsing bits of link_elf 
assume they can
make random access to the file. It'd take a bit of rework to make it work with a 
serial data stream.
The whole thing's very rough around the edges, but I can gzip most of 
/boot/kernel/*.ko, and load
the gzipped versions.

I can polish this up, and/or add gzipped executable support, if there's any interest 
in reviewing or
committing it.

The patch adds "GZLOADER" and "INFLATE" options for the kernel, removing "GZIP" (which 
was
busted anyway, and considered "inflate.c" to be part of the ELF support, while it's 
pretty much a
standalone decompressor.) There's a "COMPAT_GZAOUT" option added, but it's just as 
bust as
GZIP was before.

E&OE. Patch may crash your kernel, delete your data, make your cat unwell, etc.

Cheers,
Peter.
Index: conf/files
===================================================================
RCS file: /pub/FreeBSD/development/FreeBSD-CVS/src/sys/conf/files,v
retrieving revision 1.791
diff -u -r1.791 files
--- conf/files  9 Jun 2003 19:25:06 -0000       1.791
+++ conf/files  11 Jun 2003 12:48:40 -0000
@@ -1011,7 +1011,7 @@
 isofs/cd9660/cd9660_vnops.c    optional cd9660
 kern/imgact_elf.c      standard
 kern/imgact_shell.c    standard
-kern/inflate.c         optional gzip
+kern/inflate.c         optional inflate
 kern/init_main.c       standard
 kern/init_sysent.c     standard
 kern/kern_acct.c       standard
Index: conf/files.i386
===================================================================
RCS file: /pub/FreeBSD/development/FreeBSD-CVS/src/sys/conf/files.i386,v
retrieving revision 1.445
diff -u -r1.445 files.i386
--- conf/files.i386     31 May 2003 17:06:19 -0000      1.445
+++ conf/files.i386     11 Jun 2003 12:51:27 -0000
@@ -407,7 +407,7 @@
 isa/syscons_isa.c              optional        sc
 isa/vga_isa.c                  optional        vga
 kern/imgact_aout.c             optional        compat_aout
-kern/imgact_gzip.c             optional        gzip
+kern/imgact_gzip.c             optional        compat_gzaout
 libkern/divdi3.c               standard
 libkern/moddi3.c               standard
 libkern/qdivrem.c              standard
Index: conf/options
===================================================================
RCS file: /pub/FreeBSD/development/FreeBSD-CVS/src/sys/conf/options,v
retrieving revision 1.393
diff -u -r1.393 options
--- conf/options        18 May 2003 03:46:30 -0000      1.393
+++ conf/options        11 Jun 2003 12:54:32 -0000
@@ -603,3 +603,8 @@
 # options for hifn driver
 HIFN_DEBUG             opt_hifn.h
 HIFN_RNDTEST           opt_hifn.h
+
+# options for gzip/"inflate" related functionality
+INFLATE                        opt_inflate.h
+COMPAT_GZAOUT          opt_gzaout.h
+GZLOADER               opt_gzloader.h
Index: kern/link_elf.c
===================================================================
RCS file: /pub/FreeBSD/development/FreeBSD-CVS/src/sys/kern/link_elf.c,v
retrieving revision 1.73
diff -u -r1.73 link_elf.c
--- kern/link_elf.c     12 May 2003 15:08:10 -0000      1.73
+++ kern/link_elf.c     11 Jun 2003 13:24:50 -0000
@@ -28,6 +28,7 @@
 
 #include "opt_ddb.h"
 #include "opt_mac.h"
+#include "opt_gzloader.h"
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -42,6 +43,10 @@
 #include <sys/vnode.h>
 #include <sys/linker.h>
 
+#ifdef GZLOADER
+#include <sys/inflate.h>
+#endif
+
 #include <machine/elf.h>
 #ifdef GPROF
 #include <machine/profile.h>
@@ -98,9 +103,40 @@
 #endif
 } *elf_file_t;
 
+struct vnreader {
+       struct vnode *vnodep;
+       struct thread *thread;
+};
+
+#ifdef GZLOADER
+#define MAXGZPAGES (1024 * 1024 / PAGE_SIZE) // Allow modules up to 1MB (uncompressed)
+
+struct gzreader {
+       /* reading from gzipped file. */
+       int error;
+       struct vnode *vn;
+       unsigned char *inPage;
+       struct thread *td;
+       int inPageSize;
+       int inPageOffset;
+       off_t inFileOffset;
+       int inPageCount;
+
+       /* gzip context */
+       struct inflate inflator;
+
+       /* Writing to inflated output */
+       int outPageRes;
+       int outPageCount;
+       int outPageOffset; // Size of last page.
+       unsigned char *pages[MAXGZPAGES];
+};
+#endif
+
 static int     link_elf_link_common_finish(linker_file_t);
 static int     link_elf_link_preload(linker_class_t cls,
                                      const char*, linker_file_t*);
+
 static int     link_elf_link_preload_finish(linker_file_t);
 static int     link_elf_load_file(linker_class_t, const char*, linker_file_t*);
 static int     link_elf_lookup_symbol(linker_file_t, const char*,
@@ -118,6 +154,22 @@
                                void *);
 static void    link_elf_reloc_local(linker_file_t);
 
+#ifdef GZLOADER
+static int     link_gz_link_preload_finish(linker_file_t);
+static int     link_gz_load_file(linker_class_t, const char*, linker_file_t*);
+static int     link_gz_link_preload(linker_class_t cls,
+                                     const char*, linker_file_t*);
+static void    release_gzreader(struct gzreader *zr);
+static int     gzreadfunc(void *, unsigned char *, int, off_t, int *);
+static int     gzin(void *vp);
+static int     gzout(void *vp, unsigned char *data, unsigned long size);
+#endif
+static int link_elf_load_object(void *, int (*)(void *, unsigned char *, int, off_t, 
int *), 
+       const char *filename, linker_file_t* result);
+
+static int vnreadfunc(void *, unsigned char *, int, off_t, int *);
+
+
 static kobj_method_t link_elf_methods[] = {
     KOBJMETHOD(linker_lookup_symbol,   link_elf_lookup_symbol),
     KOBJMETHOD(linker_symbol_values,   link_elf_symbol_values),
@@ -140,6 +192,37 @@
     link_elf_methods, sizeof(struct elf_file)
 };
 
+#ifdef GZLOADER
+/*
+ * The gzip loader is almost the same as the ELF loader, only when it comes to
+ * reading the file from disk. Symbol lookups, unloading, etc, are all thesame
+ * as for ELF. For the moment, preloaded files aren't supported: some work in
+ * the boot loader is required.
+ */
+
+static kobj_method_t link_gz_methods[] = {
+    KOBJMETHOD(linker_lookup_symbol,   link_elf_lookup_symbol),
+    KOBJMETHOD(linker_symbol_values,   link_elf_symbol_values),
+    KOBJMETHOD(linker_search_symbol,   link_elf_search_symbol),
+    KOBJMETHOD(linker_unload,          link_elf_unload_file),
+    KOBJMETHOD(linker_load_file,       link_gz_load_file),
+    KOBJMETHOD(linker_link_preload,    link_gz_link_preload),
+    KOBJMETHOD(linker_link_preload_finish, link_gz_link_preload_finish),
+    KOBJMETHOD(linker_lookup_set,      link_elf_lookup_set),
+    KOBJMETHOD(linker_each_function_name, link_elf_each_function_name),
+    { 0, 0 }
+};
+
+static struct linker_class link_gz_class = {
+#if ELF_TARG_CLASS == ELFCLASS32
+    "gzelf32",
+#else
+    "gzelf64",
+#endif
+    link_gz_methods, sizeof(struct elf_file)
+};
+#endif
+
 static int             parse_dynamic(elf_file_t ef);
 static int             relocate_file(elf_file_t ef);
 static int             link_elf_preload_parse_symbols(elf_file_t ef);
@@ -255,6 +338,9 @@
     char       *modname;
 
     linker_add_class(&link_elf_class);
+#ifdef GZLOADER
+    linker_add_class(&link_gz_class);
+#endif
 
     dp = (Elf_Dyn*) &_DYNAMIC;
     modname = NULL;
@@ -524,11 +610,10 @@
 }
 
 static int
-link_elf_load_file(linker_class_t cls, const char* filename,
-       linker_file_t* result)
+link_elf_load_object(void *readCookie,
+       int (*readfunc)(void *, unsigned char *data, int, off_t, int *),
+       const char *filename, linker_file_t* result)
 {
-    struct nameidata nd;
-    struct thread* td = curthread;     /* XXX */
     Elf_Ehdr *hdr;
     caddr_t firstpage;
     int nbytes, i;
@@ -544,7 +629,6 @@
     Elf_Addr base_vaddr;
     Elf_Addr base_vlimit;
     int error = 0;
-    int resid, flags;
     elf_file_t ef;
     linker_file_t lf;
     Elf_Shdr *shdr;
@@ -552,26 +636,13 @@
     int symstrindex;
     int symcnt;
     int strcnt;
+    int resid;
 
     GIANT_REQUIRED;
 
     shdr = NULL;
     lf = NULL;
 
-    NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, filename, td);
-    flags = FREAD;
-    error = vn_open(&nd, &flags, 0);
-    if (error)
-       return error;
-    NDFREE(&nd, NDF_ONLY_PNBUF);
-#ifdef MAC
-    error = mac_check_kld_load(curthread->td_ucred, nd.ni_vp);
-    if (error) {
-       firstpage = NULL;
-       goto out;
-    }
-#endif
-
     /*
      * Read the elf header from the file.
      */
@@ -581,9 +652,7 @@
        goto out;
     }
     hdr = (Elf_Ehdr *)firstpage;
-    error = vn_rdwr(UIO_READ, nd.ni_vp, firstpage, PAGE_SIZE, 0,
-                   UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, NOCRED,
-                   &resid, td);
+    error = readfunc(readCookie, firstpage, PAGE_SIZE, 0, &resid);
     nbytes = PAGE_SIZE - resid;
     if (error)
        goto out;
@@ -727,10 +796,8 @@
      */
     for (i = 0; i < 2; i++) {
        caddr_t segbase = mapbase + segs[i]->p_vaddr - base_vaddr;
-       error = vn_rdwr(UIO_READ, nd.ni_vp,
-                       segbase, segs[i]->p_filesz, segs[i]->p_offset,
-                       UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, NOCRED,
-                       &resid, td);
+       error = readfunc(readCookie, 
+                       segbase, segs[i]->p_filesz, segs[i]->p_offset, &resid);
        if (error) {
            goto out;
        }
@@ -790,10 +857,7 @@
        error = ENOMEM;
        goto out;
     }
-    error = vn_rdwr(UIO_READ, nd.ni_vp,
-                   (caddr_t)shdr, nbytes, hdr->e_shoff,
-                   UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, NOCRED,
-                   &resid, td);
+    error = readfunc(readCookie, (caddr_t)shdr, nbytes, hdr->e_shoff, &resid);
     if (error)
        goto out;
     symtabindex = -1;
@@ -816,16 +880,12 @@
        error = ENOMEM;
        goto out;
     }
-    error = vn_rdwr(UIO_READ, nd.ni_vp,
-                   ef->symbase, symcnt, shdr[symtabindex].sh_offset,
-                   UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, NOCRED,
-                   &resid, td);
+    error = readfunc(readCookie, ef->symbase, symcnt,
+               shdr[symtabindex].sh_offset, &resid);
     if (error)
        goto out;
-    error = vn_rdwr(UIO_READ, nd.ni_vp,
-                   ef->strbase, strcnt, shdr[symstrindex].sh_offset,
-                   UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, NOCRED,
-                   &resid, td);
+    error = readfunc(readCookie, ef->strbase, strcnt,
+               shdr[symstrindex].sh_offset, &resid);
     if (error)
        goto out;
 
@@ -849,12 +909,54 @@
        free(shdr, M_LINKER);
     if (firstpage)
        free(firstpage, M_LINKER);
-    VOP_UNLOCK(nd.ni_vp, 0, td);
-    vn_close(nd.ni_vp, FREAD, td->td_ucred, td);
 
     return error;
 }
 
+static int
+vnreadfunc(void *readCookie, unsigned char *data, int len, off_t offset, int *residp)
+{
+    struct vnreader *vnr = (struct vnreader *)readCookie;
+
+    return vn_rdwr(UIO_READ, vnr->vnodep, data, len, offset,
+           UIO_SYSSPACE, IO_NODELOCKED, vnr->thread->td_ucred, NOCRED,
+           residp, vnr->thread);
+}
+
+static int
+link_elf_load_file(linker_class_t cls, const char* filename,
+       linker_file_t* result)
+{
+    struct nameidata nd;
+    struct vnreader vr;
+    int error, flags;
+    struct thread *td = curthread;
+
+    NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, filename, td);
+    flags = FREAD;
+    error = vn_open(&nd, &flags, 0);
+    if (error)
+       return error;
+    NDFREE(&nd, NDF_ONLY_PNBUF);
+#ifdef MAC
+    error = mac_check_kld_load(curthread->td_ucred, nd.ni_vp);
+    if (error) {
+       firstpage = NULL;
+       goto out;
+    }
+#endif
+    vr.vnodep = nd.ni_vp;
+    vr.thread = curthread;
+    error = link_elf_load_object(&vr, vnreadfunc, filename, result);
+#ifdef MAC
+out:
+#endif
+    vn_close(nd.ni_vp, FREAD, td->td_ucred, td);
+    VOP_UNLOCK(nd.ni_vp, 0, td);
+    return error;
+}
+
+
 static void
 link_elf_unload_file(linker_file_t file)
 {
@@ -1310,3 +1412,206 @@
        }
     }
 }
+
+#ifdef GZLOADER
+static int
+gzreadfunc(void *readCookie, unsigned char *data, int len, off_t offset, int *residp)
+{
+    struct gzreader *zr = (struct gzreader *)readCookie;
+    int pageId, pageOff, pageSize, copySize;
+
+    while (len) {
+       pageId = offset / PAGE_SIZE;
+       /* Cannot read beyond last page. */
+       if (pageId < 0 || pageId >= zr->outPageCount)
+           break;
+       pageOff = offset % PAGE_SIZE;
+
+       if (pageId == zr->outPageCount - 1) {
+           pageSize = zr->outPageOffset;
+           /* Last page may not be a full page size */
+           if (pageOff >= zr->outPageOffset)
+               break;
+       } else {
+           pageSize = PAGE_SIZE;
+       }
+       copySize = MIN(pageSize - pageOff, len);
+       bcopy(zr->pages[pageId] + pageOff, data, copySize);
+       len -= copySize;
+       offset += copySize;
+       data += copySize;
+    }
+    if (residp)
+       *residp = len;
+    return 0;
+}
+
+static int
+link_gz_load_file(linker_class_t cls, const char* filename,
+       linker_file_t* result)
+{
+    int resid;
+    const unsigned char *p;
+    struct nameidata nd;
+    struct gzreader *zr = 0;
+    int error, flags;
+    struct thread *td = curthread;
+
+    NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, filename, td);
+    flags = FREAD;
+    error = vn_open(&nd, &flags, 0);
+    if (error)
+       return error;
+    NDFREE(&nd, NDF_ONLY_PNBUF);
+#ifdef MAC
+    error = mac_check_kld_load(td->td_ucred, nd.ni_vp);
+    if (error)
+       goto out;
+#endif
+
+    zr = malloc(sizeof *zr, M_LINKER, M_WAITOK);
+    if (zr == 0)
+       goto out;
+
+    bzero(zr, sizeof *zr);
+    zr->td = td;
+    zr->vn = nd.ni_vp;
+    zr->inflator.gz_private = zr;
+    zr->inflator.gz_input = gzin;
+    zr->inflator.gz_output = gzout;
+
+    /*
+     * XXX: Would it be better to map the VM pages of the vnode, rather than
+     * using malloc/vn_rdwr()?
+     */
+    zr->inPage = malloc(PAGE_SIZE, M_LINKER, M_WAITOK);
+    if (zr->inPage == 0)
+       goto out;
+
+    error = vn_rdwr(UIO_READ, nd.ni_vp, zr->inPage, PAGE_SIZE, 0,
+           UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, NOCRED, &resid, td);
+    if (error)
+       goto out;
+    zr->inFileOffset = zr->inPageSize = PAGE_SIZE - resid;
+
+    p = zr->inPage;
+
+    /* Magic from kern/imgact_gzip.c */
+    if (p[0] != 0x1f || p[1] != 0x8b || p[2] != 0x08 /* gzip magic */
+           || p[9] != 0x03 /* Compression type */
+           || p[3] & ~0x18 /* Extra fields: just support filename and comment */
+           ) {
+       error = ENOEXEC;
+       goto out;
+    }
+
+    zr->inPageOffset = 10;
+
+    /* Skip filename in gzip file if present */
+    if (p[3] & 0x8) {
+       while (p[zr->inPageOffset++]) {
+           if (zr->inPageOffset == zr->inPageSize) {
+               error = ENOEXEC;
+               goto out;
+           }
+       }
+    }
+
+    /* Skip comment in gzip file if present */
+    if (p[3] & 0x10) {
+       while (p[zr->inPageOffset++]) {
+           if (zr->inPageOffset == zr->inPageSize) {
+               error = ENOEXEC;
+               goto out;
+           }
+       }
+    }
+    error = inflate(&zr->inflator); /* inflate the entire file */
+    if (error)
+       goto out;
+    if (zr->error) {
+       error = zr->error;
+       goto out;
+    }
+    error = link_elf_load_object(zr, gzreadfunc, filename, result);
+out:
+    vn_close(nd.ni_vp, FREAD, td->td_ucred, td);
+    VOP_UNLOCK(nd.ni_vp, 0, td);
+    if (zr)
+       release_gzreader(zr);
+    return error;
+}
+
+static void
+release_gzreader(struct gzreader *zr)
+{
+    if (zr->inPage)
+       free(zr->inPage, M_LINKER);
+    while (zr->outPageCount--)
+       free(zr->pages[zr->outPageCount], M_LINKER);
+    free(zr, M_LINKER);
+}
+
+static int
+link_gz_link_preload(linker_class_t cls,
+                     const char* filename, linker_file_t *result)
+{
+    return ENOENT;
+}
+
+static int
+link_gz_link_preload_finish(linker_file_t l)
+{
+    return ENOENT;
+}
+
+static int
+gzin(void *vp)
+{
+    struct gzreader *zr = (struct gzreader *) vp;
+    if (zr->inPageSize == zr->inPageOffset) {
+       int resid;
+       /* We have consumed the entire page. */
+       zr->error = vn_rdwr(UIO_READ, zr->vn, zr->inPage, PAGE_SIZE,
+                           zr->inFileOffset, UIO_SYSSPACE, IO_NODELOCKED,
+                           zr->td->td_ucred, NOCRED, &resid, zr->td);
+       if (zr->error)
+           return GZ_EOF;
+       if (resid == PAGE_SIZE)
+           return GZ_EOF;
+       zr->inFileOffset += PAGE_SIZE - resid;
+       zr->inPageOffset = 0;
+    }
+    return zr->inPage[zr->inPageOffset++];
+}
+
+static int
+gzout(void *vp, unsigned char *data, unsigned long size)
+{
+    unsigned char *page;
+    int space;
+
+    struct gzreader *zr = (struct gzreader *) vp;
+    while (size) {
+       if (zr->outPageCount == 0 || zr->outPageOffset == PAGE_SIZE) {
+           /* We need a new page to generate output into. */
+           if (zr->outPageCount == MAXGZPAGES)
+               return ENOEXEC;
+           zr->pages[zr->outPageCount] = malloc(PAGE_SIZE, M_LINKER, M_WAITOK);
+           if (zr->pages[zr->outPageCount] == 0)
+               return ENOEXEC;
+           zr->outPageOffset = 0;
+           zr->outPageCount++; /* That's one more to free. */
+       }
+
+       /* Copy inflated data into our image */
+       page = zr->pages[zr->outPageCount - 1];
+       space = MIN(PAGE_SIZE - zr->outPageOffset, size);
+       bcopy(data, page + zr->outPageOffset, space);
+       data += space;
+       zr->outPageOffset += space;
+       size -= space;
+    }
+    return 0;
+}
+#endif
Index: nfsclient/nfs_vnops.c
===================================================================
RCS file: /pub/FreeBSD/development/FreeBSD-CVS/src/sys/nfsclient/nfs_vnops.c,v
retrieving revision 1.205
diff -u -r1.205 nfs_vnops.c
--- nfsclient/nfs_vnops.c       15 May 2003 21:12:08 -0000      1.205
+++ nfsclient/nfs_vnops.c       16 May 2003 21:29:55 -0000
@@ -670,6 +670,13 @@
                (error = nfs_vinvalbuf(vp, V_SAVE, ap->a_cred,
                 ap->a_td, 1)) == EINTR)
                return (error);
+
+       /*
+        * It's likely that changing the file's mode will affect it's
+        * accessibilty: invalidate access cache
+        */
+       if (vap->va_mode != (mode_t)VNOVAL)
+           np->n_modestamp = 0;
        error = nfs_setattrrpc(vp, vap, ap->a_cred, ap->a_td);
        if (error && vap->va_size != VNOVAL) {
                np->n_size = np->n_vattr.va_size = tsize;
_______________________________________________
[EMAIL PROTECTED] mailing list
http://lists.freebsd.org/mailman/listinfo/freebsd-current
To unsubscribe, send any mail to "[EMAIL PROTECTED]"

Reply via email to