int pkg_repo_binary_mirror(struct pkg_repo *repo, struct pkg *pkg, const char *destdir) { return (pkg_repo_binary_try_fetch(repo, pkg, false, true, destdir)); }
static int pkg_repo_binary_try_fetch(struct pkg_repo *repo, struct pkg *pkg, bool already_tried, bool mirror, const char *destdir) { char dest[MAXPATHLEN]; char url[MAXPATHLEN]; char *dir = NULL; bool fetched = false; struct stat st; char *path = NULL; const char *packagesite = NULL; ssize_t offset = -1; int retcode = EPKG_OK; assert((pkg->type & PKG_REMOTE) == PKG_REMOTE); if (mirror) { const char *cachedir; if (destdir != NULL) cachedir = destdir; else cachedir = pkg_object_string(pkg_config_get("PKG_CACHEDIR")); snprintf(dest, sizeof(dest), "%s/%s", cachedir, pkg->repopath); } else pkg_repo_binary_get_cached_name(repo, pkg, dest, sizeof(dest)); /* If it is already in the local cachedir, dont bother to * download it */ if (stat(dest, &st) == 0) { /* try to resume */ if (pkg->pkgsize > st.st_size) { offset = st.st_size; pkg_debug(1, "Resuming fetch"); } else { goto checksum; } } /* Create the dirs in cachedir */ dir = strdup(dest); if (dir == NULL || (path = dirname(dir)) == NULL) { pkg_emit_errno("dirname", dest); retcode = EPKG_FATAL; goto cleanup; } if ((retcode = mkdirs(path)) != EPKG_OK) goto cleanup; /* * In multi-repos the remote URL is stored in pkg[PKG_REPOURL] * For a single attached database the repository URL should be * defined by URL. */ packagesite = pkg_repo_url(repo); if (packagesite == NULL || packagesite[0] == '\0') { pkg_emit_error("URL is not defined"); retcode = 1; goto cleanup; } if (packagesite[strlen(packagesite) - 1] == '/') pkg_snprintf(url, sizeof(url), "%S%R", packagesite, pkg); else pkg_snprintf(url, sizeof(url), "%S/%R", packagesite, pkg); if (!mirror && strncasecmp(packagesite, "file://", 7) == 0) { free(dir); return (EPKG_OK); } retcode = pkg_fetch_file(repo, url, dest, 0, offset, pkg->pkgsize); fetched = true; if (retcode != EPKG_OK) goto cleanup; checksum: /* checksum calculation is expensive, if size does not match, skip it and assume failed checksum. */ if (stat(dest, &st) == -1 || pkg->pkgsize != st.st_size) { if (already_tried) { pkg_emit_error("cached package %s-%s: " "size mismatch, cannot continue", pkg->name, pkg->version); retcode = EPKG_FATAL; goto cleanup; } unlink(dest); free(dir); pkg_emit_error("cached package %s-%s: " "size mismatch, fetching from remote", pkg->name, pkg->version); return (pkg_repo_binary_try_fetch(repo, pkg, true, mirror, destdir)); } if (pkg_checksum_validate_file(dest, pkg->sum) != 0) { if (already_tried || fetched) { pkg_emit_error("%s-%s failed checksum " "from repository", pkg->name, pkg->version); retcode = EPKG_FATAL; } else { pkg_emit_error("cached package %s-%s: " "checksum mismatch, fetching from remote", pkg->name, pkg->version); unlink(dest); return (pkg_repo_binary_try_fetch(repo, pkg, true, mirror, destdir)); } } cleanup: if (retcode != EPKG_OK) unlink(dest); else if (!mirror && path != NULL) { (void)pkg_repo_binary_create_symlink(pkg, dest, path); } /* allowed even if dir is NULL */ free(dir); return (retcode); }
int pkg_repo_binary_fetch(struct pkg_repo *repo, struct pkg *pkg) { return (pkg_repo_binary_try_fetch(repo, pkg, false, false, NULL)); }
static int pkg_repo_binary_try_fetch(struct pkg_repo *repo, struct pkg *pkg, bool already_tried, bool mirror, const char *destdir) { char dest[MAXPATHLEN]; char url[MAXPATHLEN]; char *dir = NULL; int fetched = 0; char cksum[SHA256_DIGEST_LENGTH * 2 +1]; int64_t pkgsize; struct stat st; char *path = NULL; const char *packagesite = NULL, *repourl; int retcode = EPKG_OK; const char *name, *version, *sum; assert((pkg->type & PKG_REMOTE) == PKG_REMOTE); pkg_get(pkg, PKG_CKSUM, &sum, PKG_NAME, &name, PKG_VERSION, &version, PKG_PKGSIZE, &pkgsize, PKG_REPOPATH, &repourl); if (mirror) { const char *cachedir; if (destdir != NULL) cachedir = destdir; else cachedir = pkg_object_string(pkg_config_get("PKG_CACHEDIR")); snprintf(dest, sizeof(dest), "%s/%s", cachedir, repourl); } else pkg_repo_binary_get_cached_name(repo, pkg, dest, sizeof(dest)); /* If it is already in the local cachedir, dont bother to * download it */ if (access(dest, F_OK) == 0) goto checksum; /* Create the dirs in cachedir */ dir = strdup(dest); if (dir == NULL || (path = dirname(dir)) == NULL) { pkg_emit_errno("dirname", dest); retcode = EPKG_FATAL; goto cleanup; } if ((retcode = mkdirs(path)) != EPKG_OK) goto cleanup; /* * In multi-repos the remote URL is stored in pkg[PKG_REPOURL] * For a single attached database the repository URL should be * defined by PACKAGESITE. */ packagesite = pkg_repo_url(repo); if (packagesite == NULL || packagesite[0] == '\0') { pkg_emit_error("PACKAGESITE is not defined"); retcode = 1; goto cleanup; } if (packagesite[strlen(packagesite) - 1] == '/') pkg_snprintf(url, sizeof(url), "%S%R", packagesite, pkg); else pkg_snprintf(url, sizeof(url), "%S/%R", packagesite, pkg); if (!mirror && strncasecmp(packagesite, "file://", 7) == 0) { pkg_set(pkg, PKG_REPOPATH, url + 7); return (EPKG_OK); } retcode = pkg_fetch_file(repo, url, dest, 0); fetched = 1; if (retcode != EPKG_OK) goto cleanup; checksum: /* checksum calculation is expensive, if size does not match, skip it and assume failed checksum. */ if (stat(dest, &st) == -1 || pkgsize != st.st_size) { if (already_tried) { pkg_emit_error("cached package %s-%s: " "size mismatch, cannot continue", name, version); retcode = EPKG_FATAL; goto cleanup; } unlink(dest); pkg_emit_error("cached package %s-%s: " "size mismatch, fetching from remote", name, version); return (pkg_repo_binary_try_fetch(repo, pkg, true, mirror, destdir)); } retcode = sha256_file(dest, cksum); if (retcode == EPKG_OK) { if (strcmp(cksum, sum)) { if (already_tried || fetched == 1) { pkg_emit_error("%s-%s failed checksum " "from repository", name, version); retcode = EPKG_FATAL; } else { pkg_emit_error("cached package %s-%s: " "checksum mismatch, fetching from remote", name, version); unlink(dest); return (pkg_repo_binary_try_fetch(repo, pkg, true, mirror, destdir)); } } } cleanup: if (retcode != EPKG_OK) unlink(dest); else if (!mirror && path != NULL) { (void)pkg_repo_binary_create_symlink(pkg, dest, path); } /* allowed even if dir is NULL */ free(dir); return (retcode); }