mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-08-08 02:40:28 +00:00
[metal] Get zipos working again for legacy BIOS boot
examples/hellolua.c now runs again when booted as a legacy BIOS disk image. - output number of sectors to load as a patchable variable; for now the patching is done by tool/build/zipcopy.c - remove references to _ezip - add tweaks to metal mmap() - implement lseek() & fstat() for metal memory files
This commit is contained in:
parent
4c74f09393
commit
44dc43fcb5
13 changed files with 160 additions and 55 deletions
72
ape/ape.S
72
ape/ape.S
|
@ -152,10 +152,13 @@ ape_mz:
|
|||
.short 0x0800 // MZ: increases cs load lower bound
|
||||
.short 0x0040 // MZ: reloc table offset
|
||||
.short 0 // MZ: overlay number
|
||||
.ascii "'<<'@'\n"
|
||||
.org 0x24 // MZ: bytes reserved for you
|
||||
.ascii "JT" // MZ: OEM identifier
|
||||
.short 0 // MZ: OEM information
|
||||
.ascii "' <<'@'\n"
|
||||
.short 1 // MZ: OEM information
|
||||
__ape_com_sectors:
|
||||
.short v_ape_allsectors // total number of sectors to load;
|
||||
.endobj __ape_com_sectors,globl // to be patched by Cosmo tools
|
||||
.org 0x40-4 // MZ: bytes reserved for you
|
||||
#if SupportsWindows() || SupportsMetal()
|
||||
.long RVA(ape_pe) // PE: the new technology
|
||||
|
@ -220,7 +223,7 @@ stub: mov $0x40,%dl // *literally* dos
|
|||
nop // system five bootpoint
|
||||
.org 0x48,0x90 // note ⌂ELF means JG 47
|
||||
jmp 3f // MZ also means pop r10
|
||||
2: sub $8,%rsp // a.k.a. dec %ax sub %sp
|
||||
2: push %rax // a.k.a. push %ax
|
||||
xor %edx,%edx // MZ ate BIOS drive code
|
||||
3: .byte 0xbd,0,0 // a.k.a. mov imm,%bp
|
||||
jmp pc // real mode, is real
|
||||
|
@ -291,7 +294,11 @@ pc: cld
|
|||
mov $1,%al // current sector
|
||||
xor %cx,%cx // current cylinder
|
||||
xor %dh,%dh // current head
|
||||
mov $v_ape_realsectors,%di // total sectors
|
||||
mov $v_ape_maxrealsectors,%di // no. sectors for base memory
|
||||
mov REAL(__ape_com_sectors),%bp
|
||||
cmp %bp,%di
|
||||
jb 3f
|
||||
mov %bp,%di
|
||||
3: call pcread
|
||||
mov %es,%si // addr += 512
|
||||
add $512>>4,%si
|
||||
|
@ -417,13 +424,12 @@ pcread: push %ax
|
|||
ror %cl
|
||||
or %al,%cl
|
||||
xor %bx,%bx // es:bx is destination addr
|
||||
mov $1,%al // read only one disk sector
|
||||
mov $2,%ah // read disk sectors ordinal
|
||||
mov $0x0201,%ax // read only one disk sector
|
||||
int $0x13
|
||||
pop %cx
|
||||
pop %ax
|
||||
jc 9f
|
||||
inc %al // ++sector
|
||||
inc %ax // ++sector
|
||||
cmp mm+"struct mman::pc_drive_last_sector",%al
|
||||
jbe 2f
|
||||
mov $1,%al
|
||||
|
@ -480,7 +486,7 @@ sinit4: mov $4,%cx
|
|||
jz 1f
|
||||
push %cx
|
||||
push %si
|
||||
xchg %ax,%di
|
||||
xchg %ax,%dx
|
||||
mov $REAL(sconf),%si
|
||||
call sinit
|
||||
pop %si
|
||||
|
@ -491,18 +497,16 @@ sinit4: mov $4,%cx
|
|||
|
||||
// Initializes Serial Line Communications 8250 UART 16550A
|
||||
//
|
||||
// @param word di tty port
|
||||
// @param word dx tty port
|
||||
// @param char (*{es:,e,r}si)[4] register initial values
|
||||
// @clob dx
|
||||
// @mode long,legacy,real
|
||||
// @see www.lammertbies.nl/comm/info/serial-uart.html
|
||||
sinit: mov %di,%dx
|
||||
test %dx,%dx
|
||||
sinit: test %dx,%dx
|
||||
jz 2f
|
||||
push %dx
|
||||
push %si
|
||||
xor %cx,%cx
|
||||
mov $UART_LCR,%cl
|
||||
add %cx,%dx
|
||||
add $UART_LCR,%dx
|
||||
lodsb %ds:(%si),%al
|
||||
pop %si
|
||||
or $UART_DLAB,%al
|
||||
|
@ -1515,7 +1519,11 @@ cpyhi: push %es
|
|||
call unreal
|
||||
mov $IMAGE_BASE_REAL,%esi
|
||||
mov $IMAGE_BASE_PHYSICAL,%edi
|
||||
mov $v_ape_realdwords,%ecx
|
||||
mov $v_ape_maxrealsectors,%ecx
|
||||
cmp %bp,%cx
|
||||
jb 1f
|
||||
mov %bp,%cx
|
||||
1: shl $9-2,%ecx // convert sector count to dword count
|
||||
cld
|
||||
rep movsl %ds:(%esi),%es:(%edi)
|
||||
sti
|
||||
|
@ -1545,13 +1553,16 @@ unreal: push %ds
|
|||
pop %ds
|
||||
ret
|
||||
|
||||
// Reads any remaining program pages into memory which have not yet
|
||||
// been read by the boot sector.
|
||||
// Reads any remaining program & data sectors into memory which have not
|
||||
// yet been read by the boot sector.
|
||||
//
|
||||
// @clob eax, ecx, dx, esi, edi, bp
|
||||
loadhi: mov $v_ape_highsectors,%bp
|
||||
test %bp,%bp
|
||||
jz 9f
|
||||
// @return ebp physical address after end of loaded sectors
|
||||
// @clob eax, ecx, dx, esi, edi
|
||||
loadhi:
|
||||
#define SEG 0x79000
|
||||
mov $IMAGE_BASE_PHYSICAL+v_ape_maxrealbytes-SEG,%edi
|
||||
sub $v_ape_maxrealsectors,%bp
|
||||
jbe 9f
|
||||
mov $mm+"struct mman::pc_drive",%si
|
||||
cld
|
||||
lodsb //← pc_drive
|
||||
|
@ -1565,8 +1576,6 @@ loadhi: mov $v_ape_highsectors,%bp
|
|||
xchg %ax,%cx
|
||||
mov (%si),%dh #← pc_drive_next_head
|
||||
push %es
|
||||
#define SEG 0x79000
|
||||
mov $IMAGE_BASE_PHYSICAL+v_ape_realbytes-SEG,%edi
|
||||
push $SEG>>4
|
||||
pop %es
|
||||
0: call pcread
|
||||
|
@ -1584,7 +1593,8 @@ loadhi: mov $v_ape_highsectors,%bp
|
|||
dec %bp
|
||||
jnz 0b
|
||||
pop %es
|
||||
9: ret
|
||||
9: lea SEG(%edi),%ebp
|
||||
ret
|
||||
|
||||
// Initializes long mode paging.
|
||||
pinit: push %ds
|
||||
|
@ -1638,6 +1648,8 @@ golong: cli
|
|||
.endfn golong
|
||||
|
||||
// Long mode is long.
|
||||
//
|
||||
// @param ebp physical address after end of loaded sectors
|
||||
.code64
|
||||
long: movabs $BANE+PHYSICAL(0f),%rax
|
||||
jmp *%rax
|
||||
|
@ -1653,12 +1665,12 @@ long: movabs $BANE+PHYSICAL(0f),%rax
|
|||
xor %r13d,%r13d
|
||||
xor %r14d,%r14d
|
||||
xor %r15d,%r15d
|
||||
xor %ebx,%ebx
|
||||
xor %ebp,%ebp
|
||||
mov $mm,%rdi
|
||||
mov %cr3,%rsi
|
||||
mov $IMAGE_BASE_PHYSICAL,%edx
|
||||
lea v_ape_allbytes(%rdx),%ecx
|
||||
mov %ebp,%ecx
|
||||
xor %ebx,%ebx
|
||||
xor %ebp,%ebp
|
||||
call __map_phdrs
|
||||
push $0x037f
|
||||
fldcw (%rsp)
|
||||
|
@ -1792,9 +1804,9 @@ kernel: movabs $ape_stack_vaddr,%rsp
|
|||
.endm
|
||||
.ldsvar _end
|
||||
.ldsvar _etext
|
||||
.ldsvar v_ape_realsectors
|
||||
.ldsvar v_ape_realbytes
|
||||
.ldsvar v_ape_highsectors
|
||||
.ldsvar v_ape_maxrealsectors
|
||||
.ldsvar v_ape_maxrealbytes
|
||||
.ldsvar v_ape_allsectors
|
||||
.ldsvar ape_idata_ro
|
||||
.ldsvar ape_piro
|
||||
.ldsvar ape_piro_end
|
||||
|
|
11
ape/ape.lds
11
ape/ape.lds
|
@ -436,7 +436,6 @@ SECTIONS {
|
|||
/*END: NT FORK COPYING */
|
||||
_edata = .;
|
||||
PROVIDE(edata = .);
|
||||
_ezip = .; /* <-- very deprecated */
|
||||
} :Ram
|
||||
|
||||
. = ALIGN(CONSTANT(COMMONPAGESIZE));
|
||||
|
@ -632,12 +631,10 @@ APE_DECLARE_FIXED_DECIMAL(IDENTITY, blink_xnu_aarch64_size);
|
|||
#endif /* APE_IS_SHELL_SCRIPT */
|
||||
|
||||
#if SupportsMetal()
|
||||
v_ape_realsectors = MIN(0x70000 - IMAGE_BASE_REAL, ROUNDUP(RVA(_ezip), 512)) / 512;
|
||||
v_ape_realbytes = v_ape_realsectors * 512;
|
||||
v_ape_realdwords = v_ape_realsectors * (512 / 4);
|
||||
v_ape_allsectors = ROUNDUP(RVA(_ezip), 512) / 512;
|
||||
v_ape_allbytes = v_ape_allsectors * 512;
|
||||
v_ape_highsectors = MIN(0xffff, v_ape_allsectors - v_ape_realsectors);
|
||||
v_ape_maxrealbytes = ROUNDUP(0x70000 - IMAGE_BASE_REAL, 512);
|
||||
v_ape_maxrealsectors = v_ape_maxrealbytes / 512;
|
||||
v_ape_allbytes = ROUNDUP(_edata - __executable_start, 512);
|
||||
v_ape_allsectors = v_ape_allbytes / 512;
|
||||
TSSDESCSTUB2(_tss, _tss, _tss_end ? _tss_end - _tss - 1 : 0);
|
||||
#endif
|
||||
|
||||
|
|
|
@ -9,7 +9,6 @@ extern unsigned char __privileged_start[] __attribute__((__weak__));
|
|||
extern unsigned char _ehead[] __attribute__((__weak__));
|
||||
extern unsigned char _etext[] __attribute__((__weak__));
|
||||
extern unsigned char _edata[] __attribute__((__weak__));
|
||||
extern unsigned char _ezip[] __attribute__((__weak__));
|
||||
extern unsigned char _end[] __attribute__((__weak__));
|
||||
extern unsigned char _ereal[] __attribute__((__weak__));
|
||||
extern unsigned char _tdata_start[] __attribute__((__weak__));
|
||||
|
|
Binary file not shown.
|
@ -17,22 +17,33 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/metalfile.internal.h"
|
||||
#include "libc/calls/struct/stat.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/s.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
int sys_fstat_metal(int fd, struct stat *st) {
|
||||
if (fd < 0) return einval();
|
||||
if (fd < g_fds.n && g_fds.p[fd].kind == kFdSerial) {
|
||||
bzero(st, sizeof(*st));
|
||||
st->st_dev = g_fds.p[fd].handle;
|
||||
st->st_rdev = g_fds.p[fd].handle;
|
||||
st->st_nlink = 1;
|
||||
st->st_mode = S_IFCHR | 0600;
|
||||
st->st_blksize = 1;
|
||||
return 0;
|
||||
} else {
|
||||
return ebadf();
|
||||
struct MetalFile *file;
|
||||
if (fd < 0 || fd >= g_fds.n) return einval();
|
||||
switch (g_fds.p[fd].kind) {
|
||||
case kFdSerial:
|
||||
bzero(st, sizeof(*st));
|
||||
st->st_dev = g_fds.p[fd].handle;
|
||||
st->st_rdev = g_fds.p[fd].handle;
|
||||
st->st_nlink = 1;
|
||||
st->st_mode = S_IFCHR | 0600;
|
||||
st->st_blksize = 1;
|
||||
return 0;
|
||||
case kFdFile:
|
||||
file = (struct MetalFile *)g_fds.p[fd].handle;
|
||||
bzero(st, sizeof(*st));
|
||||
st->st_nlink = 1;
|
||||
st->st_size = file->size;
|
||||
st->st_mode = S_IFREG | 0600;
|
||||
st->st_blksize = 1;
|
||||
return 0;
|
||||
default:
|
||||
return ebadf();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,6 +43,7 @@ forceinline bool __isfdkind(int fd, int kind) {
|
|||
int _check_interrupts(int);
|
||||
int sys_close_nt(struct Fd *, int);
|
||||
int sys_openat_metal(int, const char *, int, unsigned);
|
||||
int64_t sys_lseek_metal(struct Fd *, int64_t, int);
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
|
|
50
libc/calls/lseek-metal.c
Normal file
50
libc/calls/lseek-metal.c
Normal file
|
@ -0,0 +1,50 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/metalfile.internal.h"
|
||||
#include "libc/calls/struct/fd.internal.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
int64_t sys_lseek_metal(struct Fd *fd, int64_t offset, int whence) {
|
||||
size_t new_pos;
|
||||
struct MetalFile *file;
|
||||
switch (fd->kind) {
|
||||
case kFdFile:
|
||||
file = (struct MetalFile *)fd->handle;
|
||||
switch (whence) {
|
||||
case SEEK_SET:
|
||||
new_pos = offset;
|
||||
break;
|
||||
case SEEK_CUR:
|
||||
new_pos = file->pos + offset;
|
||||
break;
|
||||
case SEEK_END:
|
||||
new_pos = file->size + offset;
|
||||
break;
|
||||
default:
|
||||
return einval();
|
||||
}
|
||||
if (new_pos > file->size) return einval();
|
||||
file->pos = new_pos;
|
||||
return new_pos;
|
||||
default:
|
||||
return ebadf();
|
||||
}
|
||||
}
|
|
@ -77,9 +77,11 @@
|
|||
*/
|
||||
int64_t lseek(int fd, int64_t offset, int whence) {
|
||||
int64_t rc;
|
||||
if (fd < g_fds.n && g_fds.p[fd].kind == kFdZip) {
|
||||
if (fd >= 0 && fd < g_fds.n && g_fds.p[fd].kind == kFdZip) {
|
||||
rc = _weaken(__zipos_seek)(
|
||||
(struct ZiposHandle *)(intptr_t)g_fds.p[fd].handle, offset, whence);
|
||||
} else if (fd >= 0 && fd < g_fds.n && IsMetal()) {
|
||||
rc = sys_lseek_metal(g_fds.p + fd, offset, whence);
|
||||
} else if (IsLinux() || IsXnu() || IsFreebsd() || IsOpenbsd()) {
|
||||
rc = sys_lseek(fd, offset, whence, 0);
|
||||
} else if (IsNetbsd()) {
|
||||
|
|
|
@ -53,7 +53,7 @@ void *__ape_com_base;
|
|||
size_t __ape_com_size = 0;
|
||||
|
||||
textstartup dontasan void InitializeMetalFile(void) {
|
||||
if (IsMetal()) {
|
||||
if (IsMetal() && _weaken(__ape_com_sectors)) {
|
||||
/*
|
||||
* Copy out a pristine image of the program — before the program might
|
||||
* decide to modify its own .data section.
|
||||
|
@ -63,8 +63,7 @@ textstartup dontasan void InitializeMetalFile(void) {
|
|||
* The zipos code will automatically arrange to do this. Alternatively,
|
||||
* user code can __static_yoink this symbol.
|
||||
*/
|
||||
size_t size = ROUNDUP(_ezip - __executable_start, 4096);
|
||||
// TODO(jart): Restore support for ZIPOS on metal.
|
||||
size_t size = ROUNDUP((size_t)__ape_com_sectors * 512, 4096);
|
||||
void *copied_base;
|
||||
struct DirectMap dm;
|
||||
dm = sys_mmap_metal(NULL, size, PROT_READ | PROT_WRITE,
|
||||
|
|
|
@ -11,6 +11,7 @@ struct MetalFile {
|
|||
|
||||
extern void *__ape_com_base;
|
||||
extern size_t __ape_com_size;
|
||||
extern uint16_t __ape_com_sectors; // ape/ape.S
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/metalfile.internal.h"
|
||||
|
@ -46,6 +47,10 @@ dontasan struct DirectMap sys_mmap_metal(void *vaddr, size_t size, int prot,
|
|||
struct mman *mm;
|
||||
struct DirectMap res;
|
||||
uint64_t addr, faddr = 0, page, e, *pte, *fdpte, *pml4t;
|
||||
if ((uintptr_t)BANE + size < (uintptr_t)BANE) {
|
||||
assert(false);
|
||||
return bad_mmap();
|
||||
}
|
||||
mm = __get_mm();
|
||||
pml4t = __get_pml4t();
|
||||
size = ROUNDUP(size, 4096);
|
||||
|
@ -69,11 +74,14 @@ dontasan struct DirectMap sys_mmap_metal(void *vaddr, size_t size, int prot,
|
|||
} else {
|
||||
addr = ROUNDUP(addr, 4096);
|
||||
}
|
||||
for (i = 0; i < size; i += 4096) {
|
||||
i = 0;
|
||||
while (i < size) {
|
||||
pte = __get_virtual(mm, pml4t, addr + i, false);
|
||||
if (pte && (*pte & (PAGE_V | PAGE_RSRV))) {
|
||||
addr = MAX(addr, sys_mmap_metal_break) + i + 4096;
|
||||
i = 0;
|
||||
} else {
|
||||
i += 4096;
|
||||
}
|
||||
}
|
||||
sys_mmap_metal_break = MAX(addr + size, sys_mmap_metal_break);
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
│ OTHER DEALINGS IN THE SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/runtime/pc.internal.h"
|
||||
|
||||
|
|
|
@ -23,11 +23,15 @@
|
|||
#include "libc/elf/struct/shdr.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/fmt/magnumstrs.internal.h"
|
||||
#include "libc/intrin/newbie.h"
|
||||
#include "libc/limits.h"
|
||||
#include "libc/nt/struct/imagedosheader.internal.h"
|
||||
#include "libc/runtime/pc.internal.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/map.h"
|
||||
#include "libc/sysv/consts/msync.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/consts/prot.h"
|
||||
#include "libc/zip.internal.h"
|
||||
|
@ -109,6 +113,25 @@ static void GetOpts(int argc, char *argv[]) {
|
|||
outpath = argv[optind + 1];
|
||||
}
|
||||
|
||||
static void BiosZipFix(unsigned long cdest) {
|
||||
struct NtImageDosHeader head;
|
||||
size_t hd_sz = sizeof(head);
|
||||
size_t e_oemid = offsetof(struct NtImageDosHeader, e_oemid);
|
||||
size_t e_oeminfo = offsetof(struct NtImageDosHeader, e_oeminfo);
|
||||
if (outsize < hd_sz) return;
|
||||
if (pread(outfd, &head, hd_sz, 0) != hd_sz) return;
|
||||
if (READ64LE((uint8_t *)&head) != READ64LE("MZqFpD='") &&
|
||||
READ64LE((uint8_t *)&head) != READ64LE("jartsr='")) return;
|
||||
if (le16toh(head.e_oemid) != READ16LE("JT") ||
|
||||
le16toh(head.e_oeminfo) != 1) return;
|
||||
// patch executable head so that it will load the right count of sectors
|
||||
// when run as a legacy BIOS disk image
|
||||
head.e_res2[0] = htole16(ROUNDUP(cdest, 0x200) / 0x200);
|
||||
if (pwrite(outfd, &head, hd_sz, 0) != hd_sz) {
|
||||
SysDie(outpath, "head pwrite");
|
||||
}
|
||||
}
|
||||
|
||||
static void CopyZip(void) {
|
||||
char *secstrs;
|
||||
int rela, recs;
|
||||
|
@ -176,7 +199,7 @@ static void CopyZip(void) {
|
|||
}
|
||||
|
||||
// write output
|
||||
if ((outfd = open(outpath, O_WRONLY | O_CREAT, 0644)) == -1) {
|
||||
if ((outfd = open(outpath, O_RDWR | O_CREAT, 0644)) == -1) {
|
||||
SysDie(outpath, "open");
|
||||
}
|
||||
if ((outsize = lseek(outfd, 0, SEEK_END)) == -1) {
|
||||
|
@ -205,6 +228,7 @@ static void CopyZip(void) {
|
|||
if (pwrite(outfd, eocd, length, cdest) != length) {
|
||||
SysDie(outpath, "eocd pwrite");
|
||||
}
|
||||
BiosZipFix(cdest);
|
||||
if (close(outfd)) {
|
||||
SysDie(outpath, "close");
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue