Josh Rickmar writes:

> 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.

I believe you can simplify this and assume the file is gzip compressed
and wrap the file descriptor with a call to gzdopen(3) to create a
gzFile. The gz{read,write,tell,etc.}(3) calls should operate on both
gzip compressed and non-compressed files (in "transparent mode").

That's at least my experience using gzdopen(3) and gzread(3).

>
> 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 *);


--
-Dave Voutila

Reply via email to