From 6d11ec36e6b6e17978797ef8062d22175717f894 Mon Sep 17 00:00:00 2001 From: Timo Teräs Date: Fri, 14 Feb 2020 13:53:29 +0200 Subject: rename all iostream source to io_*.c --- src/Makefile | 7 +- src/archive.c | 444 ------------------------------------------------------- src/gunzip.c | 252 ------------------------------- src/io_archive.c | 444 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/io_gunzip.c | 252 +++++++++++++++++++++++++++++++ src/io_url.c | 156 +++++++++++++++++++ src/url.c | 156 ------------------- 7 files changed, 856 insertions(+), 855 deletions(-) delete mode 100644 src/archive.c delete mode 100644 src/gunzip.c create mode 100644 src/io_archive.c create mode 100644 src/io_gunzip.c create mode 100644 src/io_url.c delete mode 100644 src/url.c diff --git a/src/Makefile b/src/Makefile index 36242d7..16b9f4c 100644 --- a/src/Makefile +++ b/src/Makefile @@ -31,9 +31,10 @@ apk-objs := apk.o \ app_index.o app_fetch.o app_verify.o app_dot.o \ app_audit.o -libapk.so-objs := common.o database.o package.o archive.o \ - version.o io.o url.o gunzip.o blob.o hash.o print.o \ - commit.o solver.o +libapk.so-objs := common.o database.o package.o commit.o solver.o \ + version.o blob.o hash.o print.o \ + io.o io_url.o io_gunzip.o io_archive.o + libapk.so-libs := libfetch/libfetch.a ifeq ($(TEST),y) diff --git a/src/archive.c b/src/archive.c deleted file mode 100644 index 226bc23..0000000 --- a/src/archive.c +++ /dev/null @@ -1,444 +0,0 @@ -/* archive.c - Alpine Package Keeper (APK) - * - * Copyright (C) 2005-2008 Natanael Copa - * Copyright (C) 2008-2011 Timo Teräs - * 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 version 2 as published - * by the Free Software Foundation. See http://www.gnu.org/ for details. - */ - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "apk_defines.h" -#include "apk_print.h" -#include "apk_archive.h" -#include "apk_openssl.h" - -struct tar_header { - /* ustar header, Posix 1003.1 */ - char name[100]; /* 0-99 */ - char mode[8]; /* 100-107 */ - char uid[8]; /* 108-115 */ - char gid[8]; /* 116-123 */ - char size[12]; /* 124-135 */ - char mtime[12]; /* 136-147 */ - char chksum[8]; /* 148-155 */ - char typeflag; /* 156-156 */ - char linkname[100]; /* 157-256 */ - char magic[8]; /* 257-264 */ - char uname[32]; /* 265-296 */ - char gname[32]; /* 297-328 */ - char devmajor[8]; /* 329-336 */ - char devminor[8]; /* 337-344 */ - char prefix[155]; /* 345-499 */ - char padding[12]; /* 500-511 */ -}; - -#define GET_OCTAL(s) get_octal(s, sizeof(s)) -#define PUT_OCTAL(s,v) put_octal(s, sizeof(s), v) - -static unsigned int get_octal(char *s, size_t l) -{ - apk_blob_t b = APK_BLOB_PTR_LEN(s, l); - return apk_blob_pull_uint(&b, 8); -} - -static void put_octal(char *s, size_t l, size_t value) -{ - char *ptr = &s[l - 1]; - - *(ptr--) = '\0'; - while (value != 0 && ptr >= s) { - *(ptr--) = '0' + (value % 8); - value /= 8; - } - while (ptr >= s) - *(ptr--) = '0'; -} - -static int blob_realloc(apk_blob_t *b, size_t newsize) -{ - char *tmp; - if (b->len >= newsize) return 0; - tmp = realloc(b->ptr, newsize); - if (!tmp) return -ENOMEM; - b->ptr = tmp; - b->len = newsize; - return 0; -} - -static void handle_extended_header(struct apk_file_info *fi, apk_blob_t hdr) -{ - apk_blob_t name, value; - - while (1) { - char *start = hdr.ptr; - unsigned int len = apk_blob_pull_uint(&hdr, 10); - apk_blob_pull_char(&hdr, ' '); - if (!apk_blob_split(hdr, APK_BLOB_STR("="), &name, &hdr)) break; - if (len < hdr.ptr - start + 1) break; - len -= hdr.ptr - start + 1; - if (hdr.len < len) break; - value = APK_BLOB_PTR_LEN(hdr.ptr, len); - hdr = APK_BLOB_PTR_LEN(hdr.ptr+len, hdr.len-len); - apk_blob_pull_char(&hdr, '\n'); - if (APK_BLOB_IS_NULL(hdr)) break; - value.ptr[value.len] = 0; - - if (apk_blob_compare(name, APK_BLOB_STR("path")) == 0) { - fi->name = value.ptr; - } else if (apk_blob_compare(name, APK_BLOB_STR("linkpath")) == 0) { - fi->link_target = value.ptr; - } else if (apk_blob_pull_blob_match(&name, APK_BLOB_STR("SCHILY.xattr."))) { - name.ptr[name.len] = 0; - *apk_xattr_array_add(&fi->xattrs) = (struct apk_xattr) { - .name = name.ptr, - .value = value, - }; - } else if (apk_blob_pull_blob_match(&name, APK_BLOB_STR("APK-TOOLS.checksum."))) { - int type = APK_CHECKSUM_NONE; - if (apk_blob_compare(name, APK_BLOB_STR("SHA1")) == 0) - type = APK_CHECKSUM_SHA1; - else if (apk_blob_compare(name, APK_BLOB_STR("MD5")) == 0) - type = APK_CHECKSUM_MD5; - if (type > fi->csum.type) { - fi->csum.type = type; - apk_blob_pull_hexdump(&value, APK_BLOB_CSUM(fi->csum)); - if (APK_BLOB_IS_NULL(value)) fi->csum.type = APK_CHECKSUM_NONE; - } - } - } -} - -int apk_tar_parse(struct apk_istream *is, apk_archive_entry_parser parser, - void *ctx, struct apk_id_cache *idc) -{ - struct apk_file_info entry; - struct apk_segment_istream segment; - struct tar_header buf; - int end = 0, r; - size_t toskip, paxlen = 0; - apk_blob_t pax = APK_BLOB_NULL, longname = APK_BLOB_NULL; - char filename[sizeof buf.name + sizeof buf.prefix + 2]; - - if (IS_ERR_OR_NULL(is)) return PTR_ERR(is) ?: -EINVAL; - - memset(&entry, 0, sizeof(entry)); - entry.name = buf.name; - while ((r = apk_istream_read(is, &buf, 512)) == 512) { - if (buf.name[0] == '\0') { - if (end) break; - end++; - continue; - } - - entry = (struct apk_file_info){ - .size = GET_OCTAL(buf.size), - .uid = apk_resolve_uid(idc, buf.uname, GET_OCTAL(buf.uid)), - .gid = apk_resolve_gid(idc, buf.gname, GET_OCTAL(buf.gid)), - .mode = GET_OCTAL(buf.mode) & 07777, - .mtime = GET_OCTAL(buf.mtime), - .name = entry.name, - .uname = buf.uname, - .gname = buf.gname, - .device = makedev(GET_OCTAL(buf.devmajor), - GET_OCTAL(buf.devminor)), - .xattrs = entry.xattrs, - }; - if (buf.prefix[0] && buf.typeflag != 'x' && buf.typeflag != 'g') { - snprintf(filename, sizeof filename, "%.*s/%.*s", - (int) sizeof buf.prefix, buf.prefix, - (int) sizeof buf.name, buf.name); - entry.name = filename; - } - buf.mode[0] = 0; /* to nul terminate 100-byte buf.name */ - buf.magic[0] = 0; /* to nul terminate 100-byte buf.linkname */ - apk_xattr_array_resize(&entry.xattrs, 0); - - if (entry.size >= SSIZE_MAX-512) goto err; - - if (paxlen) { - handle_extended_header(&entry, APK_BLOB_PTR_LEN(pax.ptr, paxlen)); - apk_fileinfo_hash_xattr(&entry); - } - - toskip = (entry.size + 511) & -512; - switch (buf.typeflag) { - case 'L': /* GNU long name extension */ - if ((r = blob_realloc(&longname, entry.size+1)) != 0 || - (r = apk_istream_read(is, longname.ptr, entry.size)) != entry.size) - goto err; - entry.name = longname.ptr; - entry.name[entry.size] = 0; - toskip -= entry.size; - break; - case 'K': /* GNU long link target extension - ignored */ - break; - case '0': - case '7': /* regular file */ - entry.mode |= S_IFREG; - break; - case '1': /* hard link */ - entry.mode |= S_IFREG; - if (!entry.link_target) entry.link_target = buf.linkname; - break; - case '2': /* symbolic link */ - entry.mode |= S_IFLNK; - if (!entry.link_target) entry.link_target = buf.linkname; - break; - case '3': /* char device */ - entry.mode |= S_IFCHR; - break; - case '4': /* block device */ - entry.mode |= S_IFBLK; - break; - case '5': /* directory */ - entry.mode |= S_IFDIR; - break; - case '6': /* fifo */ - entry.mode |= S_IFIFO; - break; - case 'g': /* global pax header */ - break; - case 'x': /* file specific pax header */ - paxlen = entry.size; - if ((r = blob_realloc(&pax, (paxlen + 511) & -512)) != 0 || - (r = apk_istream_read(is, pax.ptr, paxlen)) != paxlen) - goto err; - toskip -= entry.size; - break; - default: - break; - } - - if (strnlen(entry.name, PATH_MAX) >= PATH_MAX-10 || - (entry.link_target && strnlen(entry.link_target, PATH_MAX) >= PATH_MAX-10)) { - r = -ENAMETOOLONG; - goto err; - } - - if (entry.mode & S_IFMT) { - apk_istream_segment(&segment, is, entry.size, entry.mtime); - r = parser(ctx, &entry, &segment.is); - if (r != 0) goto err; - apk_istream_close(&segment.is); - - entry.name = buf.name; - toskip -= entry.size; - paxlen = 0; - } - - if (toskip && (r = apk_istream_read(is, NULL, toskip)) != toskip) - goto err; - } - - /* Read remaining end-of-archive records, to ensure we read all of - * the file. The underlying istream is likely doing checksumming. */ - if (r == 512) { - while ((r = apk_istream_read(is, &buf, 512)) == 512) { - if (buf.name[0] != 0) break; - } - } - if (r == 0) goto ok; -err: - /* Check that there was no partial (or non-zero) record */ - if (r >= 0) r = -EBADMSG; -ok: - free(pax.ptr); - free(longname.ptr); - apk_fileinfo_free(&entry); - apk_istream_close(is); - return r; -} - -int apk_tar_write_entry(struct apk_ostream *os, const struct apk_file_info *ae, - const char *data) -{ - struct tar_header buf; - - memset(&buf, 0, sizeof(buf)); - if (ae != NULL) { - const unsigned char *src; - int chksum, i; - - if (S_ISREG(ae->mode)) - buf.typeflag = '0'; - else - return -1; - - if (ae->name != NULL) - strlcpy(buf.name, ae->name, sizeof buf.name); - - strlcpy(buf.uname, ae->uname ?: "root", sizeof buf.uname); - strlcpy(buf.gname, ae->gname ?: "root", sizeof buf.gname); - - PUT_OCTAL(buf.size, ae->size); - PUT_OCTAL(buf.uid, ae->uid); - PUT_OCTAL(buf.gid, ae->gid); - PUT_OCTAL(buf.mode, ae->mode & 07777); - PUT_OCTAL(buf.mtime, ae->mtime ?: time(NULL)); - - /* Checksum */ - strcpy(buf.magic, "ustar "); - memset(buf.chksum, ' ', sizeof(buf.chksum)); - src = (const unsigned char *) &buf; - for (i = chksum = 0; i < sizeof(buf); i++) - chksum += src[i]; - put_octal(buf.chksum, sizeof(buf.chksum)-1, chksum); - } - - if (apk_ostream_write(os, &buf, sizeof(buf)) != sizeof(buf)) - return -1; - - if (ae == NULL) { - /* End-of-archive is two empty headers */ - if (apk_ostream_write(os, &buf, sizeof(buf)) != sizeof(buf)) - return -1; - } else if (data != NULL) { - if (apk_ostream_write(os, data, ae->size) != ae->size) - return -1; - if (apk_tar_write_padding(os, ae) != 0) - return -1; - } - - return 0; -} - -int apk_tar_write_padding(struct apk_ostream *os, const struct apk_file_info *ae) -{ - static char padding[512]; - int pad; - - pad = 512 - (ae->size & 511); - if (pad != 512 && - apk_ostream_write(os, padding, pad) != pad) - return -1; - - return 0; -} - -int apk_archive_entry_extract(int atfd, const struct apk_file_info *ae, - const char *extract_name, const char *link_target, - struct apk_istream *is, - apk_progress_cb cb, void *cb_ctx) -{ - struct apk_xattr *xattr; - const char *fn = extract_name ?: ae->name; - int fd, r = -1, atflags = 0, ret = 0; - - if (unlinkat(atfd, fn, 0) != 0 && errno != ENOENT) return -errno; - - switch (ae->mode & S_IFMT) { - case S_IFDIR: - r = mkdirat(atfd, fn, ae->mode & 07777); - if (r < 0 && errno != EEXIST) - ret = -errno; - break; - case S_IFREG: - if (ae->link_target == NULL) { - int flags = O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC | O_EXCL; - - fd = openat(atfd, fn, flags, ae->mode & 07777); - if (fd < 0) { - ret = -errno; - break; - } - r = apk_istream_splice(is, fd, ae->size, cb, cb_ctx); - if (r != ae->size) ret = r < 0 ? r : -ENOSPC; - close(fd); - } else { - r = linkat(atfd, link_target ?: ae->link_target, atfd, fn, 0); - if (r < 0) ret = -errno; - } - break; - case S_IFLNK: - r = symlinkat(link_target ?: ae->link_target, atfd, fn); - if (r < 0) ret = -errno; - atflags |= AT_SYMLINK_NOFOLLOW; - break; - case S_IFBLK: - case S_IFCHR: - case S_IFIFO: - r = mknodat(atfd, fn, ae->mode, ae->device); - if (r < 0) ret = -errno; - break; - } - if (ret) { - apk_error("Failed to create %s: %s", ae->name, strerror(-ret)); - return ret; - } - - r = fchownat(atfd, fn, ae->uid, ae->gid, atflags); - if (r < 0) { - apk_error("Failed to set ownership on %s: %s", - fn, strerror(errno)); - if (!ret) ret = -errno; - } - - /* chown resets suid bit so we need set it again */ - if (ae->mode & 07000) { - r = fchmodat(atfd, fn, ae->mode & 07777, atflags); - if (r < 0) { - apk_error("Failed to set file permissions " - "on %s: %s", - fn, strerror(errno)); - if (!ret) ret = -errno; - } - } - - /* extract xattrs */ - if (!S_ISLNK(ae->mode) && ae->xattrs && ae->xattrs->num) { - r = 0; - fd = openat(atfd, fn, O_RDWR); - if (fd >= 0) { - foreach_array_item(xattr, ae->xattrs) { - if (fsetxattr(fd, xattr->name, xattr->value.ptr, xattr->value.len, 0) < 0) { - r = -errno; - if (r != -ENOTSUP) break; - } - } - close(fd); - } else { - r = -errno; - } - if (r) { - if (r != -ENOTSUP) - apk_error("Failed to set xattrs on %s: %s", - fn, strerror(-r)); - if (!ret) ret = r; - } - } - - if (!S_ISLNK(ae->mode)) { - /* preserve modification time */ - struct timespec times[2]; - - times[0].tv_sec = times[1].tv_sec = ae->mtime; - times[0].tv_nsec = times[1].tv_nsec = 0; - r = utimensat(atfd, fn, times, atflags); - if (r < 0) { - apk_error("Failed to preserve modification time on %s: %s", - fn, strerror(errno)); - if (!ret || ret == -ENOTSUP) ret = -errno; - } - } - - return ret; -} diff --git a/src/gunzip.c b/src/gunzip.c deleted file mode 100644 index 96138b0..0000000 --- a/src/gunzip.c +++ /dev/null @@ -1,252 +0,0 @@ -/* gunzip.c - Alpine Package Keeper (APK) - * - * Copyright (C) 2008-2011 Timo Teräs - * 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 version 2 as published - * by the Free Software Foundation. See http://www.gnu.org/ for details. - */ - -#include -#include -#include -#include -#include -#include - -#include "apk_defines.h" -#include "apk_io.h" - -struct apk_gzip_istream { - struct apk_istream is; - struct apk_istream *zis; - z_stream zs; - - apk_multipart_cb cb; - void *cbctx; - void *cbprev; - apk_blob_t cbarg; -}; - -static void gzi_get_meta(struct apk_istream *is, struct apk_file_meta *meta) -{ - struct apk_gzip_istream *gis = container_of(is, struct apk_gzip_istream, is); - apk_istream_get_meta(gis->zis, meta); -} - -static int gzi_boundary_change(struct apk_gzip_istream *gis) -{ - int r; - - r = gis->cb(gis->cbctx, gis->is.err ? APK_MPART_END : APK_MPART_BOUNDARY, gis->cbarg); - if (r > 0) r = -ECANCELED; - if (r != 0) gis->is.err = r; - return r; -} - -static ssize_t gzi_read(struct apk_istream *is, void *ptr, size_t size) -{ - struct apk_gzip_istream *gis = container_of(is, struct apk_gzip_istream, is); - int r; - - gis->zs.avail_out = size; - gis->zs.next_out = ptr; - - while (gis->zs.avail_out != 0 && gis->is.err == 0) { - if (!APK_BLOB_IS_NULL(gis->cbarg)) { - if (gzi_boundary_change(gis)) - goto ret; - gis->cbarg = APK_BLOB_NULL; - } - if (gis->zs.avail_in == 0) { - apk_blob_t blob; - - if (gis->cb != NULL && gis->cbprev != NULL && - gis->cbprev != gis->zs.next_in) { - gis->cb(gis->cbctx, APK_MPART_DATA, - APK_BLOB_PTR_LEN(gis->cbprev, - (void *)gis->zs.next_in - gis->cbprev)); - } - blob = apk_istream_get_all(gis->zis); - gis->cbprev = blob.ptr; - gis->zs.avail_in = blob.len; - gis->zs.next_in = (void *) gis->cbprev; - if (blob.len < 0) { - gis->is.err = blob.len; - goto ret; - } else if (gis->zs.avail_in == 0) { - gis->is.err = 1; - gis->cbarg = APK_BLOB_NULL; - gzi_boundary_change(gis); - goto ret; - } - } - - r = inflate(&gis->zs, Z_NO_FLUSH); - switch (r) { - case Z_STREAM_END: - /* Digest the inflated bytes */ - if (gis->zis->err && gis->zs.avail_in == 0) - gis->is.err = gis->zis->err; - if (gis->cb != NULL) { - gis->cbarg = APK_BLOB_PTR_LEN(gis->cbprev, (void *) gis->zs.next_in - gis->cbprev); - gis->cbprev = gis->zs.next_in; - } - /* If we hit end of the bitstream (not end - * of just this gzip), we need to do the - * callback here, as we won't be called again. - * For boundaries it should be postponed to not - * be called until next gzip read is started. */ - if (gis->is.err) { - gzi_boundary_change(gis); - goto ret; - } - inflateEnd(&gis->zs); - if (inflateInit2(&gis->zs, 15+32) != Z_OK) - return -ENOMEM; - if (gis->cb) goto ret; - break; - case Z_OK: - break; - default: - gis->is.err = -EIO; - break; - } - } - -ret: - return size - gis->zs.avail_out; -} - -static void gzi_close(struct apk_istream *is) -{ - struct apk_gzip_istream *gis = container_of(is, struct apk_gzip_istream, is); - - inflateEnd(&gis->zs); - apk_istream_close(gis->zis); - free(gis); -} - -static const struct apk_istream_ops gunzip_istream_ops = { - .get_meta = gzi_get_meta, - .read = gzi_read, - .close = gzi_close, -}; - -struct apk_istream *apk_istream_gunzip_mpart(struct apk_istream *is, apk_multipart_cb cb, void *ctx) -{ - struct apk_gzip_istream *gis; - - if (IS_ERR_OR_NULL(is)) return ERR_CAST(is); - - gis = malloc(sizeof(*gis) + apk_io_bufsize); - if (!gis) goto err; - - *gis = (struct apk_gzip_istream) { - .is.ops = &gunzip_istream_ops, - .is.buf = (uint8_t*)(gis + 1), - .is.buf_size = apk_io_bufsize, - .zis = is, - .cb = cb, - .cbctx = ctx, - }; - - if (inflateInit2(&gis->zs, 15+32) != Z_OK) { - free(gis); - goto err; - } - - return &gis->is; -err: - apk_istream_close(is); - return ERR_PTR(-ENOMEM); -} - -struct apk_gzip_ostream { - struct apk_ostream os; - struct apk_ostream *output; - z_stream zs; -}; - -static ssize_t gzo_write(struct apk_ostream *os, const void *ptr, size_t size) -{ - struct apk_gzip_ostream *gos = container_of(os, struct apk_gzip_ostream, os); - unsigned char buffer[1024]; - ssize_t have, r; - - gos->zs.avail_in = size; - gos->zs.next_in = (void *) ptr; - while (gos->zs.avail_in) { - gos->zs.avail_out = sizeof(buffer); - gos->zs.next_out = buffer; - r = deflate(&gos->zs, Z_NO_FLUSH); - if (r == Z_STREAM_ERROR) - return -EIO; - have = sizeof(buffer) - gos->zs.avail_out; - if (have != 0) { - r = apk_ostream_write(gos->output, buffer, have); - if (r != have) - return -EIO; - } - } - - return size; -} - -static int gzo_close(struct apk_ostream *os) -{ - struct apk_gzip_ostream *gos = container_of(os, struct apk_gzip_ostream, os); - unsigned char buffer[1024]; - size_t have; - int r, rc = 0; - - do { - gos->zs.avail_out = sizeof(buffer); - gos->zs.next_out = buffer; - r = deflate(&gos->zs, Z_FINISH); - have = sizeof(buffer) - gos->zs.avail_out; - if (apk_ostream_write(gos->output, buffer, have) != have) - rc = -EIO; - } while (r == Z_OK); - r = apk_ostream_close(gos->output); - if (r != 0) - rc = r; - - deflateEnd(&gos->zs); - free(gos); - - return rc; -} - -static const struct apk_ostream_ops gzip_ostream_ops = { - .write = gzo_write, - .close = gzo_close, -}; - -struct apk_ostream *apk_ostream_gzip(struct apk_ostream *output) -{ - struct apk_gzip_ostream *gos; - - if (IS_ERR_OR_NULL(output)) return ERR_CAST(output); - - gos = malloc(sizeof(struct apk_gzip_ostream)); - if (gos == NULL) goto err; - - *gos = (struct apk_gzip_ostream) { - .os.ops = &gzip_ostream_ops, - .output = output, - }; - - if (deflateInit2(&gos->zs, 9, Z_DEFLATED, 15 | 16, 8, - Z_DEFAULT_STRATEGY) != Z_OK) { - free(gos); - goto err; - } - - return &gos->os; -err: - apk_ostream_close(output); - return ERR_PTR(-ENOMEM); -} - diff --git a/src/io_archive.c b/src/io_archive.c new file mode 100644 index 0000000..22145ab --- /dev/null +++ b/src/io_archive.c @@ -0,0 +1,444 @@ +/* io_archive.c - Alpine Package Keeper (APK) + * + * Copyright (C) 2005-2008 Natanael Copa + * Copyright (C) 2008-2011 Timo Teräs + * 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 version 2 as published + * by the Free Software Foundation. See http://www.gnu.org/ for details. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "apk_defines.h" +#include "apk_print.h" +#include "apk_archive.h" +#include "apk_openssl.h" + +struct tar_header { + /* ustar header, Posix 1003.1 */ + char name[100]; /* 0-99 */ + char mode[8]; /* 100-107 */ + char uid[8]; /* 108-115 */ + char gid[8]; /* 116-123 */ + char size[12]; /* 124-135 */ + char mtime[12]; /* 136-147 */ + char chksum[8]; /* 148-155 */ + char typeflag; /* 156-156 */ + char linkname[100]; /* 157-256 */ + char magic[8]; /* 257-264 */ + char uname[32]; /* 265-296 */ + char gname[32]; /* 297-328 */ + char devmajor[8]; /* 329-336 */ + char devminor[8]; /* 337-344 */ + char prefix[155]; /* 345-499 */ + char padding[12]; /* 500-511 */ +}; + +#define GET_OCTAL(s) get_octal(s, sizeof(s)) +#define PUT_OCTAL(s,v) put_octal(s, sizeof(s), v) + +static unsigned int get_octal(char *s, size_t l) +{ + apk_blob_t b = APK_BLOB_PTR_LEN(s, l); + return apk_blob_pull_uint(&b, 8); +} + +static void put_octal(char *s, size_t l, size_t value) +{ + char *ptr = &s[l - 1]; + + *(ptr--) = '\0'; + while (value != 0 && ptr >= s) { + *(ptr--) = '0' + (value % 8); + value /= 8; + } + while (ptr >= s) + *(ptr--) = '0'; +} + +static int blob_realloc(apk_blob_t *b, size_t newsize) +{ + char *tmp; + if (b->len >= newsize) return 0; + tmp = realloc(b->ptr, newsize); + if (!tmp) return -ENOMEM; + b->ptr = tmp; + b->len = newsize; + return 0; +} + +static void handle_extended_header(struct apk_file_info *fi, apk_blob_t hdr) +{ + apk_blob_t name, value; + + while (1) { + char *start = hdr.ptr; + unsigned int len = apk_blob_pull_uint(&hdr, 10); + apk_blob_pull_char(&hdr, ' '); + if (!apk_blob_split(hdr, APK_BLOB_STR("="), &name, &hdr)) break; + if (len < hdr.ptr - start + 1) break; + len -= hdr.ptr - start + 1; + if (hdr.len < len) break; + value = APK_BLOB_PTR_LEN(hdr.ptr, len); + hdr = APK_BLOB_PTR_LEN(hdr.ptr+len, hdr.len-len); + apk_blob_pull_char(&hdr, '\n'); + if (APK_BLOB_IS_NULL(hdr)) break; + value.ptr[value.len] = 0; + + if (apk_blob_compare(name, APK_BLOB_STR("path")) == 0) { + fi->name = value.ptr; + } else if (apk_blob_compare(name, APK_BLOB_STR("linkpath")) == 0) { + fi->link_target = value.ptr; + } else if (apk_blob_pull_blob_match(&name, APK_BLOB_STR("SCHILY.xattr."))) { + name.ptr[name.len] = 0; + *apk_xattr_array_add(&fi->xattrs) = (struct apk_xattr) { + .name = name.ptr, + .value = value, + }; + } else if (apk_blob_pull_blob_match(&name, APK_BLOB_STR("APK-TOOLS.checksum."))) { + int type = APK_CHECKSUM_NONE; + if (apk_blob_compare(name, APK_BLOB_STR("SHA1")) == 0) + type = APK_CHECKSUM_SHA1; + else if (apk_blob_compare(name, APK_BLOB_STR("MD5")) == 0) + type = APK_CHECKSUM_MD5; + if (type > fi->csum.type) { + fi->csum.type = type; + apk_blob_pull_hexdump(&value, APK_BLOB_CSUM(fi->csum)); + if (APK_BLOB_IS_NULL(value)) fi->csum.type = APK_CHECKSUM_NONE; + } + } + } +} + +int apk_tar_parse(struct apk_istream *is, apk_archive_entry_parser parser, + void *ctx, struct apk_id_cache *idc) +{ + struct apk_file_info entry; + struct apk_segment_istream segment; + struct tar_header buf; + int end = 0, r; + size_t toskip, paxlen = 0; + apk_blob_t pax = APK_BLOB_NULL, longname = APK_BLOB_NULL; + char filename[sizeof buf.name + sizeof buf.prefix + 2]; + + if (IS_ERR_OR_NULL(is)) return PTR_ERR(is) ?: -EINVAL; + + memset(&entry, 0, sizeof(entry)); + entry.name = buf.name; + while ((r = apk_istream_read(is, &buf, 512)) == 512) { + if (buf.name[0] == '\0') { + if (end) break; + end++; + continue; + } + + entry = (struct apk_file_info){ + .size = GET_OCTAL(buf.size), + .uid = apk_resolve_uid(idc, buf.uname, GET_OCTAL(buf.uid)), + .gid = apk_resolve_gid(idc, buf.gname, GET_OCTAL(buf.gid)), + .mode = GET_OCTAL(buf.mode) & 07777, + .mtime = GET_OCTAL(buf.mtime), + .name = entry.name, + .uname = buf.uname, + .gname = buf.gname, + .device = makedev(GET_OCTAL(buf.devmajor), + GET_OCTAL(buf.devminor)), + .xattrs = entry.xattrs, + }; + if (buf.prefix[0] && buf.typeflag != 'x' && buf.typeflag != 'g') { + snprintf(filename, sizeof filename, "%.*s/%.*s", + (int) sizeof buf.prefix, buf.prefix, + (int) sizeof buf.name, buf.name); + entry.name = filename; + } + buf.mode[0] = 0; /* to nul terminate 100-byte buf.name */ + buf.magic[0] = 0; /* to nul terminate 100-byte buf.linkname */ + apk_xattr_array_resize(&entry.xattrs, 0); + + if (entry.size >= SSIZE_MAX-512) goto err; + + if (paxlen) { + handle_extended_header(&entry, APK_BLOB_PTR_LEN(pax.ptr, paxlen)); + apk_fileinfo_hash_xattr(&entry); + } + + toskip = (entry.size + 511) & -512; + switch (buf.typeflag) { + case 'L': /* GNU long name extension */ + if ((r = blob_realloc(&longname, entry.size+1)) != 0 || + (r = apk_istream_read(is, longname.ptr, entry.size)) != entry.size) + goto err; + entry.name = longname.ptr; + entry.name[entry.size] = 0; + toskip -= entry.size; + break; + case 'K': /* GNU long link target extension - ignored */ + break; + case '0': + case '7': /* regular file */ + entry.mode |= S_IFREG; + break; + case '1': /* hard link */ + entry.mode |= S_IFREG; + if (!entry.link_target) entry.link_target = buf.linkname; + break; + case '2': /* symbolic link */ + entry.mode |= S_IFLNK; + if (!entry.link_target) entry.link_target = buf.linkname; + break; + case '3': /* char device */ + entry.mode |= S_IFCHR; + break; + case '4': /* block device */ + entry.mode |= S_IFBLK; + break; + case '5': /* directory */ + entry.mode |= S_IFDIR; + break; + case '6': /* fifo */ + entry.mode |= S_IFIFO; + break; + case 'g': /* global pax header */ + break; + case 'x': /* file specific pax header */ + paxlen = entry.size; + if ((r = blob_realloc(&pax, (paxlen + 511) & -512)) != 0 || + (r = apk_istream_read(is, pax.ptr, paxlen)) != paxlen) + goto err; + toskip -= entry.size; + break; + default: + break; + } + + if (strnlen(entry.name, PATH_MAX) >= PATH_MAX-10 || + (entry.link_target && strnlen(entry.link_target, PATH_MAX) >= PATH_MAX-10)) { + r = -ENAMETOOLONG; + goto err; + } + + if (entry.mode & S_IFMT) { + apk_istream_segment(&segment, is, entry.size, entry.mtime); + r = parser(ctx, &entry, &segment.is); + if (r != 0) goto err; + apk_istream_close(&segment.is); + + entry.name = buf.name; + toskip -= entry.size; + paxlen = 0; + } + + if (toskip && (r = apk_istream_read(is, NULL, toskip)) != toskip) + goto err; + } + + /* Read remaining end-of-archive records, to ensure we read all of + * the file. The underlying istream is likely doing checksumming. */ + if (r == 512) { + while ((r = apk_istream_read(is, &buf, 512)) == 512) { + if (buf.name[0] != 0) break; + } + } + if (r == 0) goto ok; +err: + /* Check that there was no partial (or non-zero) record */ + if (r >= 0) r = -EBADMSG; +ok: + free(pax.ptr); + free(longname.ptr); + apk_fileinfo_free(&entry); + apk_istream_close(is); + return r; +} + +int apk_tar_write_entry(struct apk_ostream *os, const struct apk_file_info *ae, + const char *data) +{ + struct tar_header buf; + + memset(&buf, 0, sizeof(buf)); + if (ae != NULL) { + const unsigned char *src; + int chksum, i; + + if (S_ISREG(ae->mode)) + buf.typeflag = '0'; + else + return -1; + + if (ae->name != NULL) + strlcpy(buf.name, ae->name, sizeof buf.name); + + strlcpy(buf.uname, ae->uname ?: "root", sizeof buf.uname); + strlcpy(buf.gname, ae->gname ?: "root", sizeof buf.gname); + + PUT_OCTAL(buf.size, ae->size); + PUT_OCTAL(buf.uid, ae->uid); + PUT_OCTAL(buf.gid, ae->gid); + PUT_OCTAL(buf.mode, ae->mode & 07777); + PUT_OCTAL(buf.mtime, ae->mtime ?: time(NULL)); + + /* Checksum */ + strcpy(buf.magic, "ustar "); + memset(buf.chksum, ' ', sizeof(buf.chksum)); + src = (const unsigned char *) &buf; + for (i = chksum = 0; i < sizeof(buf); i++) + chksum += src[i]; + put_octal(buf.chksum, sizeof(buf.chksum)-1, chksum); + } + + if (apk_ostream_write(os, &buf, sizeof(buf)) != sizeof(buf)) + return -1; + + if (ae == NULL) { + /* End-of-archive is two empty headers */ + if (apk_ostream_write(os, &buf, sizeof(buf)) != sizeof(buf)) + return -1; + } else if (data != NULL) { + if (apk_ostream_write(os, data, ae->size) != ae->size) + return -1; + if (apk_tar_write_padding(os, ae) != 0) + return -1; + } + + return 0; +} + +int apk_tar_write_padding(struct apk_ostream *os, const struct apk_file_info *ae) +{ + static char padding[512]; + int pad; + + pad = 512 - (ae->size & 511); + if (pad != 512 && + apk_ostream_write(os, padding, pad) != pad) + return -1; + + return 0; +} + +int apk_archive_entry_extract(int atfd, const struct apk_file_info *ae, + const char *extract_name, const char *link_target, + struct apk_istream *is, + apk_progress_cb cb, void *cb_ctx) +{ + struct apk_xattr *xattr; + const char *fn = extract_name ?: ae->name; + int fd, r = -1, atflags = 0, ret = 0; + + if (unlinkat(atfd, fn, 0) != 0 && errno != ENOENT) return -errno; + + switch (ae->mode & S_IFMT) { + case S_IFDIR: + r = mkdirat(atfd, fn, ae->mode & 07777); + if (r < 0 && errno != EEXIST) + ret = -errno; + break; + case S_IFREG: + if (ae->link_target == NULL) { + int flags = O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC | O_EXCL; + + fd = openat(atfd, fn, flags, ae->mode & 07777); + if (fd < 0) { + ret = -errno; + break; + } + r = apk_istream_splice(is, fd, ae->size, cb, cb_ctx); + if (r != ae->size) ret = r < 0 ? r : -ENOSPC; + close(fd); + } else { + r = linkat(atfd, link_target ?: ae->link_target, atfd, fn, 0); + if (r < 0) ret = -errno; + } + break; + case S_IFLNK: + r = symlinkat(link_target ?: ae->link_target, atfd, fn); + if (r < 0) ret = -errno; + atflags |= AT_SYMLINK_NOFOLLOW; + break; + case S_IFBLK: + case S_IFCHR: + case S_IFIFO: + r = mknodat(atfd, fn, ae->mode, ae->device); + if (r < 0) ret = -errno; + break; + } + if (ret) { + apk_error("Failed to create %s: %s", ae->name, strerror(-ret)); + return ret; + } + + r = fchownat(atfd, fn, ae->uid, ae->gid, atflags); + if (r < 0) { + apk_error("Failed to set ownership on %s: %s", + fn, strerror(errno)); + if (!ret) ret = -errno; + } + + /* chown resets suid bit so we need set it again */ + if (ae->mode & 07000) { + r = fchmodat(atfd, fn, ae->mode & 07777, atflags); + if (r < 0) { + apk_error("Failed to set file permissions " + "on %s: %s", + fn, strerror(errno)); + if (!ret) ret = -errno; + } + } + + /* extract xattrs */ + if (!S_ISLNK(ae->mode) && ae->xattrs && ae->xattrs->num) { + r = 0; + fd = openat(atfd, fn, O_RDWR); + if (fd >= 0) { + foreach_array_item(xattr, ae->xattrs) { + if (fsetxattr(fd, xattr->name, xattr->value.ptr, xattr->value.len, 0) < 0) { + r = -errno; + if (r != -ENOTSUP) break; + } + } + close(fd); + } else { + r = -errno; + } + if (r) { + if (r != -ENOTSUP) + apk_error("Failed to set xattrs on %s: %s", + fn, strerror(-r)); + if (!ret) ret = r; + } + } + + if (!S_ISLNK(ae->mode)) { + /* preserve modification time */ + struct timespec times[2]; + + times[0].tv_sec = times[1].tv_sec = ae->mtime; + times[0].tv_nsec = times[1].tv_nsec = 0; + r = utimensat(atfd, fn, times, atflags); + if (r < 0) { + apk_error("Failed to preserve modification time on %s: %s", + fn, strerror(errno)); + if (!ret || ret == -ENOTSUP) ret = -errno; + } + } + + return ret; +} diff --git a/src/io_gunzip.c b/src/io_gunzip.c new file mode 100644 index 0000000..b7fa9f4 --- /dev/null +++ b/src/io_gunzip.c @@ -0,0 +1,252 @@ +/* io_gunzip.c - Alpine Package Keeper (APK) + * + * Copyright (C) 2008-2011 Timo Teräs + * 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 version 2 as published + * by the Free Software Foundation. See http://www.gnu.org/ for details. + */ + +#include +#include +#include +#include +#include +#include + +#include "apk_defines.h" +#include "apk_io.h" + +struct apk_gzip_istream { + struct apk_istream is; + struct apk_istream *zis; + z_stream zs; + + apk_multipart_cb cb; + void *cbctx; + void *cbprev; + apk_blob_t cbarg; +}; + +static void gzi_get_meta(struct apk_istream *is, struct apk_file_meta *meta) +{ + struct apk_gzip_istream *gis = container_of(is, struct apk_gzip_istream, is); + apk_istream_get_meta(gis->zis, meta); +} + +static int gzi_boundary_change(struct apk_gzip_istream *gis) +{ + int r; + + r = gis->cb(gis->cbctx, gis->is.err ? APK_MPART_END : APK_MPART_BOUNDARY, gis->cbarg); + if (r > 0) r = -ECANCELED; + if (r != 0) gis->is.err = r; + return r; +} + +static ssize_t gzi_read(struct apk_istream *is, void *ptr, size_t size) +{ + struct apk_gzip_istream *gis = container_of(is, struct apk_gzip_istream, is); + int r; + + gis->zs.avail_out = size; + gis->zs.next_out = ptr; + + while (gis->zs.avail_out != 0 && gis->is.err == 0) { + if (!APK_BLOB_IS_NULL(gis->cbarg)) { + if (gzi_boundary_change(gis)) + goto ret; + gis->cbarg = APK_BLOB_NULL; + } + if (gis->zs.avail_in == 0) { + apk_blob_t blob; + + if (gis->cb != NULL && gis->cbprev != NULL && + gis->cbprev != gis->zs.next_in) { + gis->cb(gis->cbctx, APK_MPART_DATA, + APK_BLOB_PTR_LEN(gis->cbprev, + (void *)gis->zs.next_in - gis->cbprev)); + } + blob = apk_istream_get_all(gis->zis); + gis->cbprev = blob.ptr; + gis->zs.avail_in = blob.len; + gis->zs.next_in = (void *) gis->cbprev; + if (blob.len < 0) { + gis->is.err = blob.len; + goto ret; + } else if (gis->zs.avail_in == 0) { + gis->is.err = 1; + gis->cbarg = APK_BLOB_NULL; + gzi_boundary_change(gis); + goto ret; + } + } + + r = inflate(&gis->zs, Z_NO_FLUSH); + switch (r) { + case Z_STREAM_END: + /* Digest the inflated bytes */ + if (gis->zis->err && gis->zs.avail_in == 0) + gis->is.err = gis->zis->err; + if (gis->cb != NULL) { + gis->cbarg = APK_BLOB_PTR_LEN(gis->cbprev, (void *) gis->zs.next_in - gis->cbprev); + gis->cbprev = gis->zs.next_in; + } + /* If we hit end of the bitstream (not end + * of just this gzip), we need to do the + * callback here, as we won't be called again. + * For boundaries it should be postponed to not + * be called until next gzip read is started. */ + if (gis->is.err) { + gzi_boundary_change(gis); + goto ret; + } + inflateEnd(&gis->zs); + if (inflateInit2(&gis->zs, 15+32) != Z_OK) + return -ENOMEM; + if (gis->cb) goto ret; + break; + case Z_OK: + break; + default: + gis->is.err = -EIO; + break; + } + } + +ret: + return size - gis->zs.avail_out; +} + +static void gzi_close(struct apk_istream *is) +{ + struct apk_gzip_istream *gis = container_of(is, struct apk_gzip_istream, is); + + inflateEnd(&gis->zs); + apk_istream_close(gis->zis); + free(gis); +} + +static const struct apk_istream_ops gunzip_istream_ops = { + .get_meta = gzi_get_meta, + .read = gzi_read, + .close = gzi_close, +}; + +struct apk_istream *apk_istream_gunzip_mpart(struct apk_istream *is, apk_multipart_cb cb, void *ctx) +{ + struct apk_gzip_istream *gis; + + if (IS_ERR_OR_NULL(is)) return ERR_CAST(is); + + gis = malloc(sizeof(*gis) + apk_io_bufsize); + if (!gis) goto err; + + *gis = (struct apk_gzip_istream) { + .is.ops = &gunzip_istream_ops, + .is.buf = (uint8_t*)(gis + 1), + .is.buf_size = apk_io_bufsize, + .zis = is, + .cb = cb, + .cbctx = ctx, + }; + + if (inflateInit2(&gis->zs, 15+32) != Z_OK) { + free(gis); + goto err; + } + + return &gis->is; +err: + apk_istream_close(is); + return ERR_PTR(-ENOMEM); +} + +struct apk_gzip_ostream { + struct apk_ostream os; + struct apk_ostream *output; + z_stream zs; +}; + +static ssize_t gzo_write(struct apk_ostream *os, const void *ptr, size_t size) +{ + struct apk_gzip_ostream *gos = container_of(os, struct apk_gzip_ostream, os); + unsigned char buffer[1024]; + ssize_t have, r; + + gos->zs.avail_in = size; + gos->zs.next_in = (void *) ptr; + while (gos->zs.avail_in) { + gos->zs.avail_out = sizeof(buffer); + gos->zs.next_out = buffer; + r = deflate(&gos->zs, Z_NO_FLUSH); + if (r == Z_STREAM_ERROR) + return -EIO; + have = sizeof(buffer) - gos->zs.avail_out; + if (have != 0) { + r = apk_ostream_write(gos->output, buffer, have); + if (r != have) + return -EIO; + } + } + + return size; +} + +static int gzo_close(struct apk_ostream *os) +{ + struct apk_gzip_ostream *gos = container_of(os, struct apk_gzip_ostream, os); + unsigned char buffer[1024]; + size_t have; + int r, rc = 0; + + do { + gos->zs.avail_out = sizeof(buffer); + gos->zs.next_out = buffer; + r = deflate(&gos->zs, Z_FINISH); + have = sizeof(buffer) - gos->zs.avail_out; + if (apk_ostream_write(gos->output, buffer, have) != have) + rc = -EIO; + } while (r == Z_OK); + r = apk_ostream_close(gos->output); + if (r != 0) + rc = r; + + deflateEnd(&gos->zs); + free(gos); + + return rc; +} + +static const struct apk_ostream_ops gzip_ostream_ops = { + .write = gzo_write, + .close = gzo_close, +}; + +struct apk_ostream *apk_ostream_gzip(struct apk_ostream *output) +{ + struct apk_gzip_ostream *gos; + + if (IS_ERR_OR_NULL(output)) return ERR_CAST(output); + + gos = malloc(sizeof(struct apk_gzip_ostream)); + if (gos == NULL) goto err; + + *gos = (struct apk_gzip_ostream) { + .os.ops = &gzip_ostream_ops, + .output = output, + }; + + if (deflateInit2(&gos->zs, 9, Z_DEFLATED, 15 | 16, 8, + Z_DEFAULT_STRATEGY) != Z_OK) { + free(gos); + goto err; + } + + return &gos->os; +err: + apk_ostream_close(output); + return ERR_PTR(-ENOMEM); +} + diff --git a/src/io_url.c b/src/io_url.c new file mode 100644 index 0000000..cd3f26c --- /dev/null +++ b/src/io_url.c @@ -0,0 +1,156 @@ +/* io_url.c - Alpine Package Keeper (APK) + * + * Copyright (C) 2005-2008 Natanael Copa + * Copyright (C) 2008-2011 Timo Teräs + * 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 version 2 as published + * by the Free Software Foundation. See http://www.gnu.org/ for details. + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include "apk_io.h" + +const char *apk_url_local_file(const char *url) +{ + if (strncmp(url, "file:", 5) == 0) + return &url[5]; + + if (strncmp(url, "http:", 5) != 0 && + strncmp(url, "https:", 6) != 0 && + strncmp(url, "ftp:", 4) != 0) + return url; + + return NULL; +} + +struct apk_fetch_istream { + struct apk_istream is; + fetchIO *fetchIO; + struct url_stat urlstat; +}; + +static int fetch_maperror(int ec) +{ + static const signed short map[] = { + [FETCH_ABORT] = -ECONNABORTED, + [FETCH_AUTH] = -EACCES, + [FETCH_DOWN] = -ECONNREFUSED, + [FETCH_EXISTS] = -EEXIST, + [FETCH_FULL] = -ENOSPC, + /* [FETCH_INFO] = , */ + [FETCH_MEMORY] = -ENOMEM, + [FETCH_MOVED] = -ENOENT, + [FETCH_NETWORK] = -ENETUNREACH, + /* [FETCH_OK] = , */ + [FETCH_PROTO] = -EPROTO, + [FETCH_RESOLV] = -ENXIO, + [FETCH_SERVER] = -EREMOTEIO, + [FETCH_TEMP] = -EAGAIN, + [FETCH_TIMEOUT] = -ETIMEDOUT, + [FETCH_UNAVAIL] = -ENOENT, + [FETCH_UNKNOWN] = -EIO, + [FETCH_URL] = -EAPKBADURL, + [FETCH_UNCHANGED] = -EALREADY, + }; + + if (ec < 0 || ec >= ARRAY_SIZE(map) || !map[ec]) return -EIO; + return map[ec]; +} + +static void fetch_get_meta(struct apk_istream *is, struct apk_file_meta *meta) +{ + struct apk_fetch_istream *fis = container_of(is, struct apk_fetch_istream, is); + + *meta = (struct apk_file_meta) { + .atime = fis->urlstat.atime, + .mtime = fis->urlstat.mtime, + }; +} + +static ssize_t fetch_read(struct apk_istream *is, void *ptr, size_t size) +{ + struct apk_fetch_istream *fis = container_of(is, struct apk_fetch_istream, is); + ssize_t r; + + r = fetchIO_read(fis->fetchIO, ptr, size); + if (r < 0) return -EIO; + return r; +} + +static void fetch_close(struct apk_istream *is) +{ + struct apk_fetch_istream *fis = container_of(is, struct apk_fetch_istream, is); + + fetchIO_close(fis->fetchIO); + free(fis); +} + +static const struct apk_istream_ops fetch_istream_ops = { + .get_meta = fetch_get_meta, + .read = fetch_read, + .close = fetch_close, +}; + +static struct apk_istream *apk_istream_fetch(const char *url, time_t since) +{ + struct apk_fetch_istream *fis = NULL; + struct url *u; + fetchIO *io = NULL; + int rc = -EIO; + + u = fetchParseURL(url); + if (!u) { + rc = -EAPKBADURL; + goto err; + } + fis = malloc(sizeof *fis + apk_io_bufsize); + if (!fis) { + rc = -ENOMEM; + goto err; + } + + u->last_modified = since; + io = fetchXGet(u, &fis->urlstat, (apk_force & APK_FORCE_REFRESH) ? "Ci" : "i"); + if (!io) { + rc = fetch_maperror(fetchLastErrCode); + goto err; + } + + *fis = (struct apk_fetch_istream) { + .is.ops = &fetch_istream_ops, + .is.buf = (uint8_t*)(fis+1), + .is.buf_size = apk_io_bufsize, + .fetchIO = io, + .urlstat = fis->urlstat, + }; + fetchFreeURL(u); + + return &fis->is; +err: + if (u) fetchFreeURL(u); + if (io) fetchIO_close(io); + if (fis) free(fis); + return ERR_PTR(rc); +} + +struct apk_istream *apk_istream_from_fd_url_if_modified(int atfd, const char *url, time_t since) +{ + if (apk_url_local_file(url) != NULL) + return apk_istream_from_file(atfd, apk_url_local_file(url)); + return apk_istream_fetch(url, since); +} + +struct apk_istream *apk_istream_from_url_gz(const char *file) +{ + return apk_istream_gunzip(apk_istream_from_url(file)); +} diff --git a/src/url.c b/src/url.c deleted file mode 100644 index a7e5a96..0000000 --- a/src/url.c +++ /dev/null @@ -1,156 +0,0 @@ -/* url.c - Alpine Package Keeper (APK) - * - * Copyright (C) 2005-2008 Natanael Copa - * Copyright (C) 2008-2011 Timo Teräs - * 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 version 2 as published - * by the Free Software Foundation. See http://www.gnu.org/ for details. - */ - -#include -#include -#include -#include -#include -#include - -#include - -#include "apk_io.h" - -const char *apk_url_local_file(const char *url) -{ - if (strncmp(url, "file:", 5) == 0) - return &url[5]; - - if (strncmp(url, "http:", 5) != 0 && - strncmp(url, "https:", 6) != 0 && - strncmp(url, "ftp:", 4) != 0) - return url; - - return NULL; -} - -struct apk_fetch_istream { - struct apk_istream is; - fetchIO *fetchIO; - struct url_stat urlstat; -}; - -static int fetch_maperror(int ec) -{ - static const signed short map[] = { - [FETCH_ABORT] = -ECONNABORTED, - [FETCH_AUTH] = -EACCES, - [FETCH_DOWN] = -ECONNREFUSED, - [FETCH_EXISTS] = -EEXIST, - [FETCH_FULL] = -ENOSPC, - /* [FETCH_INFO] = , */ - [FETCH_MEMORY] = -ENOMEM, - [FETCH_MOVED] = -ENOENT, - [FETCH_NETWORK] = -ENETUNREACH, - /* [FETCH_OK] = , */ - [FETCH_PROTO] = -EPROTO, - [FETCH_RESOLV] = -ENXIO, - [FETCH_SERVER] = -EREMOTEIO, - [FETCH_TEMP] = -EAGAIN, - [FETCH_TIMEOUT] = -ETIMEDOUT, - [FETCH_UNAVAIL] = -ENOENT, - [FETCH_UNKNOWN] = -EIO, - [FETCH_URL] = -EAPKBADURL, - [FETCH_UNCHANGED] = -EALREADY, - }; - - if (ec < 0 || ec >= ARRAY_SIZE(map) || !map[ec]) return -EIO; - return map[ec]; -} - -static void fetch_get_meta(struct apk_istream *is, struct apk_file_meta *meta) -{ - struct apk_fetch_istream *fis = container_of(is, struct apk_fetch_istream, is); - - *meta = (struct apk_file_meta) { - .atime = fis->urlstat.atime, - .mtime = fis->urlstat.mtime, - }; -} - -static ssize_t fetch_read(struct apk_istream *is, void *ptr, size_t size) -{ - struct apk_fetch_istream *fis = container_of(is, struct apk_fetch_istream, is); - ssize_t r; - - r = fetchIO_read(fis->fetchIO, ptr, size); - if (r < 0) return -EIO; - return r; -} - -static void fetch_close(struct apk_istream *is) -{ - struct apk_fetch_istream *fis = container_of(is, struct apk_fetch_istream, is); - - fetchIO_close(fis->fetchIO); - free(fis); -} - -static const struct apk_istream_ops fetch_istream_ops = { - .get_meta = fetch_get_meta, - .read = fetch_read, - .close = fetch_close, -}; - -static struct apk_istream *apk_istream_fetch(const char *url, time_t since) -{ - struct apk_fetch_istream *fis = NULL; - struct url *u; - fetchIO *io = NULL; - int rc = -EIO; - - u = fetchParseURL(url); - if (!u) { - rc = -EAPKBADURL; - goto err; - } - fis = malloc(sizeof *fis + apk_io_bufsize); - if (!fis) { - rc = -ENOMEM; - goto err; - } - - u->last_modified = since; - io = fetchXGet(u, &fis->urlstat, (apk_force & APK_FORCE_REFRESH) ? "Ci" : "i"); - if (!io) { - rc = fetch_maperror(fetchLastErrCode); - goto err; - } - - *fis = (struct apk_fetch_istream) { - .is.ops = &fetch_istream_ops, - .is.buf = (uint8_t*)(fis+1), - .is.buf_size = apk_io_bufsize, - .fetchIO = io, - .urlstat = fis->urlstat, - }; - fetchFreeURL(u); - - return &fis->is; -err: - if (u) fetchFreeURL(u); - if (io) fetchIO_close(io); - if (fis) free(fis); - return ERR_PTR(rc); -} - -struct apk_istream *apk_istream_from_fd_url_if_modified(int atfd, const char *url, time_t since) -{ - if (apk_url_local_file(url) != NULL) - return apk_istream_from_file(atfd, apk_url_local_file(url)); - return apk_istream_fetch(url, since); -} - -struct apk_istream *apk_istream_from_url_gz(const char *file) -{ - return apk_istream_gunzip(apk_istream_from_url(file)); -} -- cgit v1.2.3-70-g09d2