On Wed, Mar 10, 2021 at 01:11:30PM -0500, Josh Rickmar wrote:
> On Tue, Mar 09, 2021 at 09:36:49PM -0800, Mike Larkin wrote:
> > On Mon, Mar 08, 2021 at 05:10:27PM -0500, Josh Rickmar wrote:
> > > On Mon, Mar 08, 2021 at 11:03:10PM +0100, Klemens Nanni wrote:
> > > > On Mon, Mar 08, 2021 at 04:50:53PM -0500, Josh Rickmar wrote:
> > > > > >Synopsis:    vmm/vmd fails to boot bsd.rd
> > > > > >Category:    vmm
> > > > > >Environment:
> > > > >       System      : OpenBSD 6.9
> > > > >       Details     : OpenBSD 6.9-beta (GENERIC.MP) #385: Mon Mar  8 
> > > > > 12:57:12 MST 2021
> > > > >                        
> > > > > [email protected]:/usr/src/sys/arch/amd64/compile/GENERIC.MP
> > > > >
> > > > >       Architecture: OpenBSD.amd64
> > > > >       Machine     : amd64
> > > > > >Description:
> > > > >
> > > > > vmm/vmd fails to boot /bsd.rd from a recent snapshot, however, bsd.sp
> > > > > is able to be booted in this manner.
> > > > This is most likely due to the recent switch to compressed bsd.rd;
> > > > dry a gzcat(1)ed copy of bsd.rd instead.
> > >
> > > Ah, yes this works.
> > >
> > > Is this expected behavior or should vmd be taught how to read the
> > > compressed kernel?
> > >
> > 
> > Sure. A diff would be welcome (libz is already in the tree and ready to use 
> > for
> > this).
> 
> I expect this may need some cleanup, but with this patch I am able to
> boot the compressed bsd.rd.
> 
> It replaces passing the kernel image around as a FILE* to a wrapper
> struct that may represent either a FILE* or gzFile.  The struct points
> to a function pointer to dispatch to the correct read or seek
> functions.
> 
> This isn't wrapping gztell, which is used to discover the size of the
> bios firmware image, and so that will continue to error if you try to
> load a compressed bios.  I don't think we would want to wrap that,
> since seeking to the end to discover the size would result in
> decompressing everything twice.

Hmm, let's rename "stdio" to "stream" for the regular uncompressed
files.  Otherwise this diff is the same as before.

diff b711551f3ad0c8a480c9d1297568b8616c06bdec /usr/src
blob - 1770ac337a9996c76fb09de6f04909b3bb890658
file + usr.sbin/vmd/Makefile
--- usr.sbin/vmd/Makefile
+++ usr.sbin/vmd/Makefile
@@ -14,7 +14,7 @@ CFLAGS+=      -Wmissing-declarations
 CFLAGS+=       -Wshadow -Wpointer-arith -Wcast-qual
 CFLAGS+=       -Wsign-compare
 
-LDADD+=                -lutil -lpthread -levent
+LDADD+=                -lutil -lpthread -levent -lz
 DPADD+=                ${LIBUTIL} ${LIBPTHREAD} ${LIBEVENT}
 
 YFLAGS=
blob - de7fd5f270dc1b900c11ed082fb0b4f94acc01b0
file + usr.sbin/vmd/loadfile.h
--- usr.sbin/vmd/loadfile.h
+++ usr.sbin/vmd/loadfile.h
@@ -73,8 +73,10 @@
 #define PML2_PAGE 0x13000
 #define NPTE_PG (PAGE_SIZE / sizeof(uint64_t))
 
-int loadfile_elf(FILE *, struct vm_create_params *,
+struct bootimage;
+
+int loadfile_elf(struct bootimage *, struct vm_create_params *,
     struct vcpu_reg_state *, uint32_t, uint32_t, unsigned int);
 
-size_t mread(FILE *, paddr_t, size_t);
+size_t mread(struct bootimage *, paddr_t, size_t);
 
blob - 116094260948b50b9f1eda63d3241b77bfddb39d
file + usr.sbin/vmd/loadfile_elf.c
--- usr.sbin/vmd/loadfile_elf.c
+++ usr.sbin/vmd/loadfile_elf.c
@@ -115,8 +115,8 @@ union {
 
 static void setsegment(struct mem_segment_descriptor *, uint32_t,
     size_t, int, int, int, int);
-static int elf32_exec(FILE *, Elf32_Ehdr *, u_long *, int);
-static int elf64_exec(FILE *, Elf64_Ehdr *, u_long *, int);
+static int elf32_exec(struct bootimage *, Elf32_Ehdr *, u_long *, int);
+static int elf64_exec(struct bootimage *, Elf64_Ehdr *, u_long *, int);
 static size_t create_bios_memmap(struct vm_create_params *, bios_memmap_t *);
 static uint32_t push_bootargs(bios_memmap_t *, size_t, bios_bootmac_t *);
 static size_t push_stack(uint32_t, uint32_t, uint32_t, uint32_t);
@@ -263,7 +263,7 @@ push_pt_64(void)
  *  various error codes returned from read(2) or loadelf functions
  */
 int
-loadfile_elf(FILE *fp, struct vm_create_params *vcp,
+loadfile_elf(struct bootimage *f, struct vm_create_params *vcp,
     struct vcpu_reg_state *vrs, uint32_t bootdev, uint32_t howto,
     unsigned int bootdevice)
 {
@@ -274,17 +274,17 @@ loadfile_elf(FILE *fp, struct vm_create_params *vcp,
        bios_memmap_t memmap[VMM_MAX_MEM_RANGES + 1];
        bios_bootmac_t bm, *bootmac = NULL;
 
-       if ((r = fread(&hdr, 1, sizeof(hdr), fp)) != sizeof(hdr))
+       if ((r = f->ops->read(f, &hdr, sizeof(hdr))) != sizeof(hdr))
                return 1;
 
        memset(&marks, 0, sizeof(marks));
        if (memcmp(hdr.elf32.e_ident, ELFMAG, SELFMAG) == 0 &&
            hdr.elf32.e_ident[EI_CLASS] == ELFCLASS32) {
-               r = elf32_exec(fp, &hdr.elf32, marks, LOAD_ALL);
+               r = elf32_exec(f, &hdr.elf32, marks, LOAD_ALL);
                is_i386 = 1;
        } else if (memcmp(hdr.elf64.e_ident, ELFMAG, SELFMAG) == 0 &&
            hdr.elf64.e_ident[EI_CLASS] == ELFCLASS64) {
-               r = elf64_exec(fp, &hdr.elf64, marks, LOAD_ALL);
+               r = elf64_exec(f, &hdr.elf64, marks, LOAD_ALL);
        } else
                errno = ENOEXEC;
 
@@ -490,7 +490,7 @@ push_stack(uint32_t bootargsz, uint32_t end, uint32_t 
  * into the guest address space at paddr 'addr'.
  *
  * Parameters:
- *  fd: file descriptor of the kernel image file to read from.
+ *  f: kernel image file to read from.
  *  addr: guest paddr_t to load to
  *  sz: number of bytes to load
  *
@@ -498,7 +498,7 @@ push_stack(uint32_t bootargsz, uint32_t end, uint32_t 
  *  returns 'sz' if successful, or 0 otherwise.
  */
 size_t
-mread(FILE *fp, paddr_t addr, size_t sz)
+mread(struct bootimage *f, paddr_t addr, size_t sz)
 {
        size_t ct;
        size_t i, rd, osz;
@@ -518,7 +518,7 @@ mread(FILE *fp, paddr_t addr, size_t sz)
                else
                        ct = sz;
 
-               if (fread(buf, 1, ct, fp) != ct) {
+               if (f->ops->read(f, buf, ct) != ct) {
                        log_warn("%s: error %d in mread", __progname, errno);
                        return (0);
                }
@@ -542,7 +542,7 @@ mread(FILE *fp, paddr_t addr, size_t sz)
                else
                        ct = PAGE_SIZE;
 
-               if (fread(buf, 1, ct, fp) != ct) {
+               if (f->ops->read(f, buf, ct) != ct) {
                        log_warn("%s: error %d in mread", __progname, errno);
                        return (0);
                }
@@ -665,7 +665,7 @@ mbcopy(void *src, paddr_t dst, int sz)
  *  1 if unsuccessful
  */
 static int
-elf64_exec(FILE *fp, Elf64_Ehdr *elf, u_long *marks, int flags)
+elf64_exec(struct bootimage *f, Elf64_Ehdr *elf, u_long *marks, int flags)
 {
        Elf64_Shdr *shp;
        Elf64_Phdr *phdr;
@@ -680,12 +680,12 @@ elf64_exec(FILE *fp, Elf64_Ehdr *elf, u_long *marks, i
        sz = elf->e_phnum * sizeof(Elf64_Phdr);
        phdr = malloc(sz);
 
-       if (fseeko(fp, (off_t)elf->e_phoff, SEEK_SET) == -1)  {
+       if (f->ops->seek(f, (off_t)elf->e_phoff, SEEK_SET) == -1)  {
                free(phdr);
                return 1;
        }
 
-       if (fread(phdr, 1, sz, fp) != sz) {
+       if (f->ops->read(f, phdr, sz) != sz) {
                free(phdr);
                return 1;
        }
@@ -725,12 +725,12 @@ elf64_exec(FILE *fp, Elf64_Ehdr *elf, u_long *marks, i
                    (IS_DATA(phdr[i]) && (flags & LOAD_DATA))) {
 
                        /* Read in segment. */
-                       if (fseeko(fp, (off_t)phdr[i].p_offset,
+                       if (f->ops->seek(f, (off_t)phdr[i].p_offset,
                            SEEK_SET) == -1) {
                                free(phdr);
                                return 1;
                        }
-                       if (mread(fp, phdr[i].p_paddr, phdr[i].p_filesz) !=
+                       if (mread(f, phdr[i].p_paddr, phdr[i].p_filesz) !=
                            phdr[i].p_filesz) {
                                free(phdr);
                                return 1;
@@ -770,14 +770,14 @@ elf64_exec(FILE *fp, Elf64_Ehdr *elf, u_long *marks, i
                maxp += sizeof(Elf64_Ehdr);
 
        if (flags & (LOAD_SYM | COUNT_SYM)) {
-               if (fseeko(fp, (off_t)elf->e_shoff, SEEK_SET) == -1)  {
+               if (f->ops->seek(f, (off_t)elf->e_shoff, SEEK_SET) == -1)  {
                        warn("lseek section headers");
                        return 1;
                }
                sz = elf->e_shnum * sizeof(Elf64_Shdr);
                shp = malloc(sz);
 
-               if (fread(shp, 1, sz, fp) != sz) {
+               if (f->ops->read(f, shp, sz) != sz) {
                        free(shp);
                        return 1;
                }
@@ -787,13 +787,13 @@ elf64_exec(FILE *fp, Elf64_Ehdr *elf, u_long *marks, i
 
                size_t shstrsz = shp[elf->e_shstrndx].sh_size;
                char *shstr = malloc(shstrsz);
-               if (fseeko(fp, (off_t)shp[elf->e_shstrndx].sh_offset,
+               if (f->ops->seek(f, (off_t)shp[elf->e_shstrndx].sh_offset,
                    SEEK_SET) == -1) {
                        free(shstr);
                        free(shp);
                        return 1;
                }
-               if (fread(shstr, 1, shstrsz, fp) != shstrsz) {
+               if (f->ops->read(f, shstr, shstrsz) != shstrsz) {
                        free(shstr);
                        free(shp);
                        return 1;
@@ -816,13 +816,14 @@ elf64_exec(FILE *fp, Elf64_Ehdr *elf, u_long *marks, i
                            !strcmp(shstr + shp[i].sh_name, ".debug_line") ||
                            !strcmp(shstr + shp[i].sh_name, ELF_CTF)) {
                                if (havesyms && (flags & LOAD_SYM)) {
-                                       if (fseeko(fp, (off_t)shp[i].sh_offset,
+                                       if (f->ops->seek(f,
+                                           (off_t)shp[i].sh_offset,
                                            SEEK_SET) == -1) {
                                                free(shstr);
                                                free(shp);
                                                return 1;
                                        }
-                                       if (mread(fp, maxp,
+                                       if (mread(f, maxp,
                                            shp[i].sh_size) != shp[i].sh_size) {
                                                free(shstr);
                                                free(shp);
@@ -875,7 +876,7 @@ elf64_exec(FILE *fp, Elf64_Ehdr *elf, u_long *marks, i
  * This function is used for 32 bit kernels.
  *
  * Parameters:
- *  fd: file descriptor of the kernel to load
+ *  f: kernel file to load
  *  elf: ELF header of the kernel
  *  marks: array to store the offsets of various kernel structures
  *      (start, bss, etc)
@@ -887,7 +888,7 @@ elf64_exec(FILE *fp, Elf64_Ehdr *elf, u_long *marks, i
  *  1 if unsuccessful
  */
 static int
-elf32_exec(FILE *fp, Elf32_Ehdr *elf, u_long *marks, int flags)
+elf32_exec(struct bootimage *f, Elf32_Ehdr *elf, u_long *marks, int flags)
 {
        Elf32_Shdr *shp;
        Elf32_Phdr *phdr;
@@ -902,12 +903,12 @@ elf32_exec(FILE *fp, Elf32_Ehdr *elf, u_long *marks, i
        sz = elf->e_phnum * sizeof(Elf32_Phdr);
        phdr = malloc(sz);
 
-       if (fseeko(fp, (off_t)elf->e_phoff, SEEK_SET) == -1)  {
+       if (f->ops->seek(f, (off_t)elf->e_phoff, SEEK_SET) == -1) {
                free(phdr);
                return 1;
        }
 
-       if (fread(phdr, 1, sz, fp) != sz) {
+       if (f->ops->read(f, phdr, sz) != sz) {
                free(phdr);
                return 1;
        }
@@ -947,12 +948,12 @@ elf32_exec(FILE *fp, Elf32_Ehdr *elf, u_long *marks, i
                    (IS_DATA(phdr[i]) && (flags & LOAD_DATA))) {
 
                        /* Read in segment. */
-                       if (fseeko(fp, (off_t)phdr[i].p_offset,
+                       if (f->ops->seek(f, (off_t)phdr[i].p_offset,
                            SEEK_SET) == -1) {
                                free(phdr);
                                return 1;
                        }
-                       if (mread(fp, phdr[i].p_paddr, phdr[i].p_filesz) !=
+                       if (mread(f, phdr[i].p_paddr, phdr[i].p_filesz) !=
                            phdr[i].p_filesz) {
                                free(phdr);
                                return 1;
@@ -992,14 +993,14 @@ elf32_exec(FILE *fp, Elf32_Ehdr *elf, u_long *marks, i
                maxp += sizeof(Elf32_Ehdr);
 
        if (flags & (LOAD_SYM | COUNT_SYM)) {
-               if (fseeko(fp, (off_t)elf->e_shoff, SEEK_SET) == -1)  {
+               if (f->ops->seek(f, (off_t)elf->e_shoff, SEEK_SET) == -1) {
                        warn("lseek section headers");
                        return 1;
                }
                sz = elf->e_shnum * sizeof(Elf32_Shdr);
                shp = malloc(sz);
 
-               if (fread(shp, 1, sz, fp) != sz) {
+               if (f->ops->read(f, shp, sz) != sz) {
                        free(shp);
                        return 1;
                }
@@ -1009,13 +1010,13 @@ elf32_exec(FILE *fp, Elf32_Ehdr *elf, u_long *marks, i
 
                size_t shstrsz = shp[elf->e_shstrndx].sh_size;
                char *shstr = malloc(shstrsz);
-               if (fseeko(fp, (off_t)shp[elf->e_shstrndx].sh_offset,
+               if (f->ops->seek(f, (off_t)shp[elf->e_shstrndx].sh_offset,
                    SEEK_SET) == -1) {
                        free(shstr);
                        free(shp);
                        return 1;
                }
-               if (fread(shstr, 1, shstrsz, fp) != shstrsz) {
+               if (f->ops->read(f, shstr, shstrsz) != shstrsz) {
                        free(shstr);
                        free(shp);
                        return 1;
@@ -1037,13 +1038,14 @@ elf32_exec(FILE *fp, Elf32_Ehdr *elf, u_long *marks, i
                            shp[i].sh_type == SHT_STRTAB ||
                            !strcmp(shstr + shp[i].sh_name, ".debug_line")) {
                                if (havesyms && (flags & LOAD_SYM)) {
-                                       if (fseeko(fp, (off_t)shp[i].sh_offset,
+                                       if (f->ops->seek(f,
+                                           (off_t)shp[i].sh_offset,
                                            SEEK_SET) == -1) {
                                                free(shstr);
                                                free(shp);
                                                return 1;
                                        }
-                                       if (mread(fp, maxp,
+                                       if (mread(f, maxp,
                                            shp[i].sh_size) != shp[i].sh_size) {
                                                free(shstr);
                                                free(shp);
blob - e825f203ebbbb6d8c5dab5c1594dc815f92fd867
file + usr.sbin/vmd/vm.c
--- usr.sbin/vmd/vm.c
+++ usr.sbin/vmd/vm.c
@@ -84,7 +84,7 @@ void vcpu_exit_inout(struct vm_run_params *);
 int vcpu_exit_eptviolation(struct vm_run_params *);
 uint8_t vcpu_exit_pci(struct vm_run_params *);
 int vcpu_pic_intr(uint32_t, uint32_t, uint8_t);
-int loadfile_bios(FILE *, struct vcpu_reg_state *);
+int loadfile_bios(struct bootimage *, struct vcpu_reg_state *);
 int send_vm(int, struct vm_create_params *);
 int dump_send_header(int);
 int dump_vmr(int , struct vm_mem_range *);
@@ -212,7 +212,7 @@ static const struct vcpu_reg_state vcpu_init_flat16 = 
  * directly into memory.
  *
  * Parameters:
- *  fp: file of a kernel file to load
+ *  f: BIOS file to load
  *  (out) vrs: register state to set on init for this kernel
  *
  * Return values:
@@ -220,7 +220,7 @@ static const struct vcpu_reg_state vcpu_init_flat16 = 
  *  various error codes returned from read(2) or loadelf functions
  */
 int
-loadfile_bios(FILE *fp, struct vcpu_reg_state *vrs)
+loadfile_bios(struct bootimage *f, struct vcpu_reg_state *vrs)
 {
        off_t    size, off;
 
@@ -228,8 +228,10 @@ loadfile_bios(FILE *fp, struct vcpu_reg_state *vrs)
        memcpy(vrs, &vcpu_init_flat16, sizeof(*vrs));
 
        /* Get the size of the BIOS image and seek to the beginning */
-       if (fseeko(fp, 0, SEEK_END) == -1 || (size = ftello(fp)) == -1 ||
-           fseeko(fp, 0, SEEK_SET) == -1)
+       if (f->type != FILE_STREAM)
+               return (-1); /* XXX: we don't wrap gztell */
+       if (f->ops->seek(f, 0, SEEK_END) == -1 || (size = ftello(f->f)) == -1 ||
+           f->ops->seek(f, 0, SEEK_SET) == -1)
                return (-1);
 
        /* The BIOS image must end at 1M */
@@ -237,7 +239,7 @@ loadfile_bios(FILE *fp, struct vcpu_reg_state *vrs)
                return (-1);
 
        /* Read BIOS image into memory */
-       if (mread(fp, off, size) != (size_t)size) {
+       if (mread(f, off, size) != (size_t)size) {
                errno = EIO;
                return (-1);
        }
@@ -277,7 +279,7 @@ start_vm(struct vmd_vm *vm, int fd)
        struct vcpu_reg_state    vrs;
        int                      nicfds[VMM_MAX_NICS_PER_VM];
        int                      ret;
-       FILE                    *fp;
+       struct bootimage        *f;
        struct vmboot_params     vmboot;
        size_t                   i;
        struct vm_rwregs_params  vrp;
@@ -332,13 +334,13 @@ start_vm(struct vmd_vm *vm, int fd)
                memcpy(&vrs, &vcpu_init_flat64, sizeof(vrs));
 
                /* Find and open kernel image */
-               if ((fp = vmboot_open(vm->vm_kernel,
+               if ((f = vmboot_open(vm->vm_kernel,
                    vm->vm_disks[0], vmc->vmc_diskbases[0],
                    vmc->vmc_disktypes[0], &vmboot)) == NULL)
                        fatalx("failed to open kernel - exiting");
 
                /* Load kernel image */
-               ret = loadfile_elf(fp, vcp, &vrs,
+               ret = loadfile_elf(f, vcp, &vrs,
                    vmboot.vbp_bootdev, vmboot.vbp_howto, vmc->vmc_bootdevice);
 
                /*
@@ -346,12 +348,12 @@ start_vm(struct vmd_vm *vm, int fd)
                 * with vm->vm_kernel and not loaded from the disk)
                 */
                if (ret && errno == ENOEXEC && vm->vm_kernel != -1)
-                       ret = loadfile_bios(fp, &vrs);
+                       ret = loadfile_bios(f, &vrs);
 
                if (ret)
                        fatal("failed to load kernel or BIOS - exiting");
 
-               vmboot_close(fp, &vmboot);
+               vmboot_close(f, &vmboot);
        }
 
        if (vm->vm_kernel != -1)
blob - 349635fc33db2552a87ce87a66fa5289f6b345bd
file + usr.sbin/vmd/vmboot.c
--- usr.sbin/vmd/vmboot.c
+++ usr.sbin/vmd/vmboot.c
@@ -32,6 +32,7 @@
 #include <fcntl.h>
 #include <err.h>
 #include <vis.h>
+#include <zlib.h>
 
 #include "vmd.h"
 #include "vmboot.h"
@@ -46,6 +47,9 @@ int    vmboot_strategy(void *, int, daddr_t, size_t, voi
 off_t   vmboot_findopenbsd(struct open_file *, off_t, struct disklabel *);
 void   *vmboot_loadfile(struct open_file *, char *, size_t *);
 
+static struct bootimage        *vmboot_fdopen(int);
+static struct bootimage        *wrap_stream(FILE *);
+
 int
 vmboot_bootcmd(char *line, struct vmboot_params *bp)
 {
@@ -384,7 +388,7 @@ vmboot_loadfile(struct open_file *f, char *file, size_
        return (p);
 }
 
-FILE *
+struct bootimage *
 vmboot_open(int kernel_fd, int *disk_fd, int nfd, unsigned int disk_type,
     struct vmboot_params *vmboot)
 {
@@ -392,6 +396,7 @@ vmboot_open(int kernel_fd, int *disk_fd, int nfd, unsi
        char                    *buf = NULL;
        size_t                   size;
        FILE                    *fp = NULL;
+       struct bootimage        *f = NULL;
        struct disklabel         dl;
        struct virtio_backing   *vfp;
        off_t                    sz;
@@ -402,7 +407,7 @@ vmboot_open(int kernel_fd, int *disk_fd, int nfd, unsi
 
        /* First open kernel directly if specified by fd */
        if (kernel_fd != -1)
-               return (fdopen(kernel_fd, "r"));
+               return (vmboot_fdopen(kernel_fd));
 
        if (disk_fd == NULL || nfd < 1)
                return (NULL);
@@ -481,21 +486,127 @@ vmboot_open(int kernel_fd, int *disk_fd, int nfd, unsi
                    vmboot->vbp_device, vmboot->vbp_image);
        }
 
-       return (fp);
+       if ((f = wrap_stream(fp)) == NULL)
+               goto fail;
+
+       return (f);
   fail:
-       vmboot_close(fp, vmboot);
+       vmboot_close(f, vmboot);
        return (NULL);
 }
 
 void
-vmboot_close(FILE *fp, struct vmboot_params *vmboot)
+vmboot_close(struct bootimage *f, struct vmboot_params *vmboot)
 {
        struct virtio_backing   *vfp = vmboot->vbp_arg;
 
-       if (fp != NULL)
-               fclose(fp);
+       switch (f->type) {
+       case FILE_STREAM:
+               fclose(f->f);
+               break;
+       case FILE_GZIP:
+               gzclose(f->gzf);
+               break;
+       default:
+               /* can't happen */
+               errx(2, "invalid file type");
+       }
+
        if (vfp != NULL)
                vfp->close(vfp->p, 1);
+       free(f);
        free(vmboot->vbp_arg);
        free(vmboot->vbp_buf);
 }
+
+size_t
+stream_read(struct bootimage *f, void *ptr, size_t nbytes)
+{
+       if (f->type != FILE_STREAM)
+               return (0);
+       return (fread(ptr, 1, nbytes, f->f));
+}
+
+int
+stream_seek(struct bootimage *f, off_t offset, int whence)
+{
+       if (f->type != FILE_STREAM)
+               return (-1); /* XXX set errno? */
+       return (fseeko(f->f, offset, whence));
+}
+
+
+size_t
+gzip_read(struct bootimage *f, void *ptr, size_t nbytes)
+{
+       if (f->type != FILE_GZIP)
+               return (0); /* XXX set errno? */
+       return ((ssize_t)gzread(f->gzf, ptr, nbytes));
+}
+
+int
+gzip_seek(struct bootimage *f, off_t offset, int whence)
+{
+       if (f->type != FILE_GZIP)
+               return (-1); /* XXX set errno? */
+       return ((int)gzseek(f->gzf, offset, whence));
+}
+
+static const struct bootimage_ops stream_ops = {
+       stream_read,
+       stream_seek,
+};
+static const struct bootimage_ops gzip_ops = {
+       gzip_read,
+       gzip_seek,
+};
+
+static const u_char gz_magic[2] = {0x1f, 0x8b}; /* gzip magic header */
+
+static struct bootimage *
+vmboot_fdopen(int fd)
+{
+       struct bootimage *f;
+       struct stat sb;
+       u_char magic[2];
+
+       if (fstat(fd, &sb) == -1)
+               return (NULL);
+       if (S_ISDIR(sb.st_mode)) {
+               errno = EISDIR;
+               return (NULL);
+       }
+
+       if ((f = calloc(1, sizeof *f)) == NULL)
+               return (NULL);
+
+       if (pread(fd, magic, sizeof(magic), 0) != 2)
+               return NULL;
+       if (magic[0] == gz_magic[0] && magic[1] == gz_magic[1]) {
+               f->type = FILE_GZIP;
+               f->ops = &gzip_ops;
+               if ((f->gzf = gzdopen(fd, "r")) != NULL)
+                       return f;
+       } else {
+               f->type = FILE_STREAM;
+               f->ops = &stream_ops;
+               if ((f->f = fdopen(fd, "r")) != NULL)
+                       return f;
+       }
+
+       free(f);
+       return NULL;
+}
+
+static struct bootimage *
+wrap_stream(FILE *fp)
+{
+       struct bootimage *f;
+
+       if ((f = calloc(1, sizeof *f)) == NULL)
+               return (NULL);
+       f->type = FILE_STREAM;
+       f->ops = &stream_ops;
+       f->f = fp;
+       return (f);
+}
blob - 325d40d1ace0714e86d20fac20f3eaabd406d721
file + usr.sbin/vmd/vmd.h
--- usr.sbin/vmd/vmd.h
+++ usr.sbin/vmd/vmd.h
@@ -30,6 +30,7 @@
 #include <limits.h>
 #include <stdio.h>
 #include <pthread.h>
+#include <zlib.h>
 
 #include "proc.h"
 
@@ -475,9 +476,31 @@ int         config_getif(struct privsep *, struct imsg *);
 int     config_getcdrom(struct privsep *, struct imsg *);
 
 /* vmboot.c */
-FILE   *vmboot_open(int, int *, int, unsigned int, struct vmboot_params *);
-void    vmboot_close(FILE *, struct vmboot_params *);
+struct bootimage_ops;
 
+struct bootimage {
+       int                      type;
+#define FILE_STREAM            0
+#define FILE_GZIP              1
+       FILE                    *f;
+       gzFile                  *gzf;
+       struct bootimage_ops const *ops;
+};
+
+struct bootimage_ops {
+       size_t  (*read)(struct bootimage *, void *, size_t);
+       int     (*seek)(struct bootimage *, off_t, int);
+};
+
+size_t stream_read(struct bootimage *f, void *, size_t);
+int    stream_seek(struct bootimage *, off_t, int);
+size_t gzip_read(struct bootimage *f, void *, size_t);
+int    gzip_seek(struct bootimage *, off_t, int);
+
+struct bootimage *vmboot_open(int, int *, int, unsigned int,
+    struct vmboot_params *);
+void   vmboot_close(struct bootimage *, struct vmboot_params *);
+
 /* parse.y */
 int     parse_config(const char *);
 int     cmdline_symset(char *);

Reply via email to