int verify_checksum(int fd, const char *file, const unsigned char *chksum, Id chksumtype) { char buf[1024]; const unsigned char *sum; Chksum *h; int l; h = solv_chksum_create(chksumtype); if (!h) { printf("%s: unknown checksum type\n", file); return 0; } while ((l = read(fd, buf, sizeof(buf))) > 0) solv_chksum_add(h, buf, l); lseek(fd, 0, SEEK_SET); l = 0; sum = solv_chksum_get(h, &l); if (memcmp(sum, chksum, l)) { printf("%s: checksum mismatch\n", file); solv_chksum_free(h, 0); return 0; } solv_chksum_free(h, 0); return 1; }
/* calls rewind(fp) before returning */ int checksum_fp(unsigned char *out, FILE *fp) { /* based on calc_checksum_fp in libsolv's solv.c */ char buf[4096]; void *h = solv_chksum_create(CHKSUM_TYPE); int l; rewind(fp); solv_chksum_add(h, CHKSUM_IDENT, strlen(CHKSUM_IDENT)); while ((l = fread(buf, 1, sizeof(buf), fp)) > 0) solv_chksum_add(h, buf, l); rewind(fp); solv_chksum_free(h, out); return 0; }
/* does not move the fp position */ int checksum_stat(unsigned char *out, FILE *fp) { assert(fp); struct stat stat; if (fstat(fileno(fp), &stat)) return 1; /* based on calc_checksum_stat in libsolv's solv.c */ void *h = solv_chksum_create(CHKSUM_TYPE); solv_chksum_add(h, CHKSUM_IDENT, strlen(CHKSUM_IDENT)); solv_chksum_add(h, &stat.st_dev, sizeof(stat.st_dev)); solv_chksum_add(h, &stat.st_ino, sizeof(stat.st_ino)); solv_chksum_add(h, &stat.st_size, sizeof(stat.st_size)); solv_chksum_add(h, &stat.st_mtime, sizeof(stat.st_mtime)); solv_chksum_free(h, out); return 0; }
static int skip_bytes(FILE *fp, size_t skip, Chksum *chk) { unsigned char buf[4096]; while (skip) { size_t bite = skip > sizeof(buf) ? sizeof(buf) : skip; if (fread(buf, bite, 1, fp) != 1) return 0; if (chk) solv_chksum_add(chk, buf, bite); skip -= bite; } return 1; }
Id repo_add_deb(Repo *repo, const char *deb, int flags) { Pool *pool = repo->pool; Repodata *data; unsigned char buf[4096], *bp; int l, l2, vlen, clen, ctarlen; unsigned char *ctgz; unsigned char pkgid[16]; unsigned char *ctar; int gotpkgid; FILE *fp; Solvable *s; struct stat stb; data = repo_add_repodata(repo, flags); if ((fp = fopen(flags & REPO_USE_ROOTDIR ? pool_prepend_rootdir_tmp(pool, deb) : deb, "r")) == 0) { pool_error(pool, -1, "%s: %s", deb, strerror(errno)); return 0; } if (fstat(fileno(fp), &stb)) { pool_error(pool, -1, "fstat: %s", strerror(errno)); fclose(fp); return 0; } l = fread(buf, 1, sizeof(buf), fp); if (l < 8 + 60 || strncmp((char *)buf, "!<arch>\ndebian-binary ", 8 + 16) != 0) { pool_error(pool, -1, "%s: not a deb package", deb); fclose(fp); return 0; } vlen = atoi((char *)buf + 8 + 48); if (vlen < 0 || vlen > l) { pool_error(pool, -1, "%s: not a deb package", deb); fclose(fp); return 0; } vlen += vlen & 1; if (l < 8 + 60 + vlen + 60) { pool_error(pool, -1, "%s: unhandled deb package", deb); fclose(fp); return 0; } if (strncmp((char *)buf + 8 + 60 + vlen, "control.tar.gz ", 16) != 0) { pool_error(pool, -1, "%s: control.tar.gz is not second entry", deb); fclose(fp); return 0; } clen = atoi((char *)buf + 8 + 60 + vlen + 48); if (clen <= 0 || clen >= 0x100000) { pool_error(pool, -1, "%s: control.tar.gz has illegal size", deb); fclose(fp); return 0; } ctgz = solv_calloc(1, clen + 4); bp = buf + 8 + 60 + vlen + 60; l -= 8 + 60 + vlen + 60; if (l > clen) l = clen; if (l) memcpy(ctgz, bp, l); if (l < clen) { if (fread(ctgz + l, clen - l, 1, fp) != 1) { pool_error(pool, -1, "%s: unexpected EOF", deb); solv_free(ctgz); fclose(fp); return 0; } } fclose(fp); gotpkgid = 0; if (flags & DEBS_ADD_WITH_PKGID) { Chksum *chk = solv_chksum_create(REPOKEY_TYPE_MD5); solv_chksum_add(chk, ctgz, clen); solv_chksum_free(chk, pkgid); gotpkgid = 1; } if (ctgz[0] != 0x1f || ctgz[1] != 0x8b) { pool_error(pool, -1, "%s: control.tar.gz is not gzipped", deb); solv_free(ctgz); return 0; } if (ctgz[2] != 8 || (ctgz[3] & 0xe0) != 0) { pool_error(pool, -1, "%s: control.tar.gz is compressed in a strange way", deb); solv_free(ctgz); return 0; } bp = ctgz + 4; bp += 6; /* skip time, xflags and OS code */ if (ctgz[3] & 0x04) { /* skip extra field */ l = bp[0] | bp[1] << 8; bp += l + 2; if (bp >= ctgz + clen) { pool_error(pool, -1, "%s: control.tar.gz is corrupt", deb); solv_free(ctgz); return 0; } } if (ctgz[3] & 0x08) /* orig filename */ while (*bp) bp++; if (ctgz[3] & 0x10) /* file comment */ while (*bp) bp++; if (ctgz[3] & 0x02) /* header crc */ bp += 2; if (bp >= ctgz + clen) { pool_error(pool, -1, "%s: control.tar.gz is corrupt", deb); solv_free(ctgz); return 0; } ctar = decompress(bp, ctgz + clen - bp, &ctarlen); solv_free(ctgz); if (!ctar) { pool_error(pool, -1, "%s: control.tar.gz is corrupt", deb); return 0; } bp = ctar; l = ctarlen; while (l > 512) { int j; l2 = 0; for (j = 124; j < 124 + 12; j++) if (bp[j] >= '0' && bp[j] <= '7') l2 = l2 * 8 + (bp[j] - '0'); if (!strcmp((char *)bp, "./control") || !strcmp((char *)bp, "control")) break; l2 = 512 + ((l2 + 511) & ~511); l -= l2; bp += l2; } if (l <= 512 || l - 512 - l2 <= 0 || l2 <= 0) { pool_error(pool, -1, "%s: control.tar.gz contains no control file", deb); free(ctar); return 0; } memmove(ctar, bp + 512, l2); ctar = solv_realloc(ctar, l2 + 1); ctar[l2] = 0; s = pool_id2solvable(pool, repo_add_solvable(repo)); control2solvable(s, data, (char *)ctar); if (!(flags & REPO_NO_LOCATION)) repodata_set_location(data, s - pool->solvables, 0, 0, deb); if (S_ISREG(stb.st_mode)) repodata_set_num(data, s - pool->solvables, SOLVABLE_DOWNLOADSIZE, (unsigned long long)stb.st_size); if (gotpkgid) repodata_set_bin_checksum(data, s - pool->solvables, SOLVABLE_PKGID, REPOKEY_TYPE_MD5, pkgid); solv_free(ctar); if (!(flags & REPO_NO_INTERNALIZE)) repodata_internalize(data); return s - pool->solvables; }
struct solv_zchunk * solv_zchunk_open(FILE *fp, unsigned int streamid) { struct solv_zchunk *zck; unsigned char *p; unsigned int hdr_size; /* preface + index + signatures */ unsigned int lead_size; unsigned int preface_size; unsigned int index_size; zck = solv_calloc(1, sizeof(*zck)); /* read and parse the lead, read the complete header */ zck->hdr = solv_calloc(15, 1); zck->hdr_end = zck->hdr + 15; if (fread(zck->hdr, 15, 1, fp) != 1 || memcmp(zck->hdr, "\000ZCK1", 5) != 0) return open_error(zck); p = zck->hdr + 5; if ((p = getchksum(p, zck->hdr_end, &zck->hdr_chk_type, &zck->hdr_chk_len, &zck->hdr_chk_id)) == 0) return open_error(zck); if ((p = getuint(p, zck->hdr_end, &hdr_size)) == 0 || hdr_size > MAX_HDR_SIZE) return open_error(zck); lead_size = p - zck->hdr + zck->hdr_chk_len; zck->hdr = solv_realloc(zck->hdr, lead_size + hdr_size); zck->hdr_end = zck->hdr + lead_size + hdr_size; if (fread(zck->hdr + 15, lead_size + hdr_size - 15, 1, fp) != 1) return open_error(zck); /* verify header checksum to guard against corrupt files */ if (zck->hdr_chk_id) { Chksum *chk = solv_chksum_create(zck->hdr_chk_id); if (!chk) return open_error(zck); solv_chksum_add(chk, zck->hdr, lead_size - zck->hdr_chk_len); solv_chksum_add(chk, zck->hdr + lead_size, hdr_size); if (memcmp(solv_chksum_get(chk, 0), zck->hdr + (lead_size - zck->hdr_chk_len), zck->hdr_chk_len) != 0) { solv_chksum_free(chk, 0); return open_error(zck); } solv_chksum_free(chk, 0); } /* parse preface: data chksum, flags, compression */ p = zck->hdr + lead_size; if (p + zck->hdr_chk_len > zck->hdr_end) return open_error(zck); zck->data_chk_ptr = p; p += zck->hdr_chk_len; #ifdef VERIFY_DATA_CHKSUM if (zck->hdr_chk_id && (zck->data_chk = solv_chksum_create(zck->hdr_chk_id)) == 0) return open_error(zck); #endif if ((p = getuint(p, zck->hdr_end, &zck->flags)) == 0) return open_error(zck); if ((zck->flags & ~(3)) != 0) return open_error(zck); if ((p = getuint(p, zck->hdr_end, &zck->comp)) == 0 || (zck->comp != 0 && zck->comp != 2)) return open_error(zck); /* only uncompressed + zstd supported */ /* skip all optional elements if present */ if ((zck->flags & 2) != 0) { unsigned int nopt, lopt; if ((p = getuint(p, zck->hdr_end, &nopt)) == 0) return open_error(zck); for (; nopt != 0; nopt--) { if ((p = getuint(p, zck->hdr_end, &lopt)) == 0) return open_error(zck); if ((p = getuint(p, zck->hdr_end, &lopt)) == 0) return open_error(zck); if (p + lopt > zck->hdr_end) return open_error(zck); p += lopt; } } preface_size = p - (zck->hdr + lead_size); /* parse index: index size, index chksum type, num chunks, chunk data */ if ((p = getuint(p, zck->hdr_end, &index_size)) == 0) return open_error(zck); if (hdr_size < preface_size + index_size) return open_error(zck); if ((p = getchksum(p, zck->hdr_end, &zck->chunk_chk_type, &zck->chunk_chk_len, &zck->chunk_chk_id)) == 0) return open_error(zck); if ((p = getuint(p, zck->hdr_end, &zck->nchunks)) == 0 || zck->nchunks > MAX_CHUNK_CNT) return open_error(zck); /* setup decompressor */ if (zck->comp == 2) { if ((zck->dctx = ZSTD_createDCtx()) == 0) return open_error(zck); } zck->fp = fp; zck->chunks = p; zck->streamid = streamid; if (streamid == 0) { zck->nchunks = zck->nchunks ? 1 : 0; /* limit to dict chunk */ return zck; } /* setup dictionary */ if (!nextchunk(zck, 0)) { zck->fp = 0; return open_error(zck); } if (zck->comp == 2 && zck->buf_avail) { if ((zck->ddict = ZSTD_createDDict(zck->buf, zck->buf_avail)) == 0) { zck->fp = 0; return open_error(zck); } } zck->buf = solv_free(zck->buf); zck->buf_used = 0; zck->buf_avail = 0; /* ready to read the rest of the chunks */ return zck; }
static int nextchunk(struct solv_zchunk *zck, unsigned int streamid) { unsigned char *p = zck->chunks; unsigned char *chunk_chk_ptr; unsigned int sid, chunk_len, uncompressed_len; unsigned char *cbuf; /* free old buffer */ zck->buf = solv_free(zck->buf); zck->buf_avail = 0; zck->buf_used = 0; for (;;) { if (zck->nchunks == 0) { zck->chunks = p; return 1; /* EOF reached */ } if (p >= zck->hdr_end) return 0; sid = streamid ? 1 : 0; /* check if this is the correct stream */ if ((zck->flags & 1) != 0 && (p = getuint(p, zck->hdr_end, &sid)) == 0) return 0; chunk_chk_ptr = p; /* remember for verification */ p += zck->chunk_chk_len; if (p >= zck->hdr_end) return 0; if ((p = getuint(p, zck->hdr_end, &chunk_len)) == 0) return 0; if ((p = getuint(p, zck->hdr_end, &uncompressed_len)) == 0) return 0; zck->nchunks--; if (sid == streamid) break; /* skip the chunk, but the dict chunk must come first */ if (streamid == 0 || skip_bytes(zck->fp, chunk_len, zck->data_chk) == 0) return 0; } zck->chunks = p; /* ok, read the compressed chunk */ if (!chunk_len) return uncompressed_len ? 0 : 1; cbuf = solv_malloc(chunk_len); if (fread(cbuf, chunk_len, 1, zck->fp) != 1) { solv_free(cbuf); return 0; } if (zck->data_chk) solv_chksum_add(zck->data_chk, cbuf, chunk_len); /* verify the chunk checksum */ if (zck->chunk_chk_id) { Chksum *chk = solv_chksum_create(zck->chunk_chk_id); if (!chk) { solv_free(cbuf); return 0; } solv_chksum_add(chk, cbuf, chunk_len); if (memcmp(solv_chksum_get(chk, 0), chunk_chk_ptr, zck->chunk_chk_len) != 0) { solv_chksum_free(chk, 0); solv_free(cbuf); return 0; } solv_chksum_free(chk, 0); } /* uncompress */ if (zck->comp == 0) { /* not compressed */ if (chunk_len != uncompressed_len) { solv_free(cbuf); return 0; } zck->buf = cbuf; zck->buf_avail = uncompressed_len; return 1; } if (zck->comp == 2) { /* zstd compressed */ size_t r; zck->buf = solv_malloc(uncompressed_len + 1); /* +1 so we can detect too large frames */ if (zck->ddict) r = ZSTD_decompress_usingDDict(zck->dctx, zck->buf, uncompressed_len + 1, cbuf, chunk_len, zck->ddict); else r = ZSTD_decompressDCtx(zck->dctx, zck->buf, uncompressed_len + 1, cbuf, chunk_len); solv_free(cbuf); if (r != uncompressed_len) return 0; zck->buf_avail = uncompressed_len; return 1; } solv_free(cbuf); return 0; }
Id repo_add_arch_pkg(Repo *repo, const char *fn, int flags) { Pool *pool = repo->pool; Repodata *data; FILE *fp; struct tarhead th; char line[4096]; int ignoreline; Solvable *s; int l, fd; struct stat stb; void *pkgidhandle = 0; data = repo_add_repodata(repo, flags); if ((fd = open(flags & REPO_USE_ROOTDIR ? pool_prepend_rootdir_tmp(pool, fn) : fn, O_RDONLY, 0)) < 0) { pool_error(pool, -1, "%s: %s", fn, strerror(errno)); return 0; } if (fstat(fd, &stb)) { pool_error(pool, -1, "%s: fstat: %s", fn, strerror(errno)); close(fd); return 0; } if (!(fp = solv_xfopen_fd(fn, fd, "r"))) { pool_error(pool, -1, "%s: fdopen failed", fn); close(fd); return 0; } s = 0; inittarhead(&th, fp); while (gettarhead(&th) > 0) { if (th.type != 1 || strcmp(th.path, ".PKGINFO") != 0) { skipentry(&th); continue; } ignoreline = 0; s = pool_id2solvable(pool, repo_add_solvable(repo)); if (flags & ARCH_ADD_WITH_PKGID) pkgidhandle = solv_chksum_create(REPOKEY_TYPE_MD5); while (getsentry(&th, line, sizeof(line))) { l = strlen(line); if (l == 0) continue; if (pkgidhandle) solv_chksum_add(pkgidhandle, line, l); if (line[l - 1] != '\n') { ignoreline = 1; continue; } if (ignoreline) { ignoreline = 0; continue; } line[--l] = 0; if (l == 0 || line[0] == '#') continue; if (!strncmp(line, "pkgname = ", 10)) s->name = pool_str2id(pool, line + 10, 1); else if (!strncmp(line, "pkgver = ", 9)) s->evr = pool_str2id(pool, line + 9, 1); else if (!strncmp(line, "pkgdesc = ", 10)) { repodata_set_str(data, s - pool->solvables, SOLVABLE_SUMMARY, line + 10); repodata_set_str(data, s - pool->solvables, SOLVABLE_DESCRIPTION, line + 10); } else if (!strncmp(line, "url = ", 6)) repodata_set_str(data, s - pool->solvables, SOLVABLE_URL, line + 6); else if (!strncmp(line, "builddate = ", 12)) repodata_set_num(data, s - pool->solvables, SOLVABLE_BUILDTIME, strtoull(line + 12, 0, 10)); else if (!strncmp(line, "packager = ", 11)) repodata_set_poolstr(data, s - pool->solvables, SOLVABLE_PACKAGER, line + 11); else if (!strncmp(line, "size = ", 7)) repodata_set_num(data, s - pool->solvables, SOLVABLE_INSTALLSIZE, strtoull(line + 7, 0, 10)); else if (!strncmp(line, "arch = ", 7)) s->arch = pool_str2id(pool, line + 7, 1); else if (!strncmp(line, "license = ", 10)) repodata_set_poolstr(data, s - pool->solvables, SOLVABLE_LICENSE, line + 10); else if (!strncmp(line, "replaces = ", 11)) s->obsoletes = adddep(repo, s->obsoletes, line + 11); else if (!strncmp(line, "group = ", 8)) repodata_set_poolstr(data, s - pool->solvables, SOLVABLE_GROUP, line + 8); else if (!strncmp(line, "depend = ", 9)) s->requires = adddep(repo, s->requires, line + 9); else if (!strncmp(line, "optdepend = ", 12)) { char *p = strchr(line, ':'); if (p) *p = 0; s->suggests = adddep(repo, s->suggests, line + 12); } else if (!strncmp(line, "conflict = ", 11)) s->conflicts = adddep(repo, s->conflicts, line + 11); else if (!strncmp(line, "provides = ", 11)) s->provides = adddep(repo, s->provides, line + 11); } break; } freetarhead(&th); fclose(fp); if (!s) { pool_error(pool, -1, "%s: not an arch package", fn); if (pkgidhandle) solv_chksum_free(pkgidhandle, 0); return 0; } if (s && !s->name) { pool_error(pool, -1, "%s: package has no name", fn); repo_free_solvable(repo, s - pool->solvables, 1); s = 0; } if (s) { if (!s->arch) s->arch = ARCH_ANY; if (!s->evr) s->evr = ID_EMPTY; s->provides = repo_addid_dep(repo, s->provides, pool_rel2id(pool, s->name, s->evr, REL_EQ, 1), 0); if (!(flags & REPO_NO_LOCATION)) repodata_set_location(data, s - pool->solvables, 0, 0, fn); if (S_ISREG(stb.st_mode)) repodata_set_num(data, s - pool->solvables, SOLVABLE_DOWNLOADSIZE, (unsigned long long)stb.st_size); if (pkgidhandle) { unsigned char pkgid[16]; solv_chksum_free(pkgidhandle, pkgid); repodata_set_bin_checksum(data, s - pool->solvables, SOLVABLE_PKGID, REPOKEY_TYPE_MD5, pkgid); pkgidhandle = 0; } } if (pkgidhandle) solv_chksum_free(pkgidhandle, 0); if (!(flags & REPO_NO_INTERNALIZE)) repodata_internalize(data); return s ? s - pool->solvables : 0; }