示例#1
0
文件: add.c 项目: culot/pkgng
int
exec_add(int argc, char **argv)
{
    struct pkgdb *db = NULL;
    struct sbuf *failedpkgs = sbuf_new_auto();
    char path[MAXPATHLEN + 1];
    char *file;
    int retcode = EPKG_OK;
    int i;
    int failedpkgcount = 0;
    struct pkg *p = NULL;

    if (argc < 2) {
        usage_add();
        return (EX_USAGE);
    }

    if (geteuid() != 0) {
        warnx("adding packages can only be done as root");
        return (EX_NOPERM);
    }

    if (pkgdb_open(&db, PKGDB_DEFAULT) != EPKG_OK) {
        return (EX_IOERR);
    }

    for (i = 1; i < argc; i++) {
        if (is_url(argv[i]) == EPKG_OK) {
            snprintf(path, sizeof(path), "./%s", basename(argv[i]));
            if ((retcode = pkg_fetch_file(argv[i], path)) != EPKG_OK)
                break;

            file = path;
        } else
            file = argv[i];

        pkg_open(&p, file, NULL);

        if ((retcode = pkg_add(db, file, 0)) != EPKG_OK) {
            sbuf_cat(failedpkgs, argv[i]);
            if (i != argc - 1)
                sbuf_printf(failedpkgs, ", ");
            failedpkgcount++;
        }

    }

    pkgdb_close(db);

    if(failedpkgcount > 0) {
        sbuf_finish(failedpkgs);
        printf("Failed to install the following %d package(s): %s.\n", failedpkgcount, sbuf_data(failedpkgs));
    }
    sbuf_delete(failedpkgs);

    return (retcode == EPKG_OK ? EX_OK : EX_SOFTWARE);
}
示例#2
0
int
pkg_repo_fetch(struct pkg *pkg)
{
	char dest[MAXPATHLEN + 1];
	char url[MAXPATHLEN + 1];
	int fetched = 0;
	char cksum[SHA256_DIGEST_LENGTH * 2 +1];
	char *path = NULL;
	const char *packagesite = NULL;
	const char *cachedir = NULL;
	bool multirepos_enabled = false;
	int retcode = EPKG_OK;
	const char *repopath, *repourl, *sum, *name, *version;

	assert((pkg->type & PKG_REMOTE) == PKG_REMOTE);

	if (pkg_config_string(PKG_CONFIG_CACHEDIR, &cachedir) != EPKG_OK)
		return (EPKG_FATAL);

	pkg_get(pkg, PKG_REPOPATH, &repopath, PKG_REPOURL, &repourl,
	    PKG_CKSUM, &sum, PKG_NAME, &name, PKG_VERSION, &version);

	snprintf(dest, sizeof(dest), "%s/%s", cachedir, repopath);

	/* 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 */
	if ((path = dirname(dest)) == 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.
	 */
	pkg_config_bool(PKG_CONFIG_MULTIREPOS, &multirepos_enabled);

	if (multirepos_enabled) {
		packagesite = repourl;
	} else {
		pkg_config_string(PKG_CONFIG_REPO, &packagesite);
	}

	if (packagesite == NULL || packagesite[0] == '\0') {
		pkg_emit_error("PACKAGESITE is not defined");
		retcode = 1;
		goto cleanup;
	}

	if (packagesite[strlen(packagesite) - 1] == '/')
		snprintf(url, sizeof(url), "%s%s", packagesite, repopath);
	else
		snprintf(url, sizeof(url), "%s/%s", packagesite, repopath);

	retcode = pkg_fetch_file(url, dest, 0);
	fetched = 1;

	if (retcode != EPKG_OK)
		goto cleanup;

	checksum:
	retcode = sha256_file(dest, cksum);
	if (retcode == EPKG_OK)
		if (strcmp(cksum, sum)) {
			if (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_fetch(pkg));
			}
		}

	cleanup:
	if (retcode != EPKG_OK)
		unlink(dest);

	return (retcode);
}
示例#3
0
文件: pkg_repo.c 项目: dnaeon/pkgng
int
pkg_repo_fetch(struct pkg *pkg)
{
	char dest[MAXPATHLEN + 1];
	char url[MAXPATHLEN + 1];
	int fetched = 0;
	char cksum[SHA256_DIGEST_LENGTH * 2 +1];
	char *path = NULL;
	const char *packagesite = NULL;
	int retcode = EPKG_OK;

	assert((pkg->type & PKG_REMOTE) == PKG_REMOTE);

	if ((packagesite = pkg_config("PACKAGESITE")) == NULL) {
		pkg_emit_error("pkg_repo_fetch(): %s", "PACKAGESITE is not defined");
		return (EPKG_FATAL);
	}

	snprintf(dest, sizeof(dest), "%s/%s", pkg_config("PKG_CACHEDIR"),
			 pkg_get(pkg, PKG_REPOPATH));

	/* 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 */
	if ((path = dirname(dest)) == NULL) {
		pkg_emit_errno("dirname", dest);
		retcode = EPKG_FATAL;
		goto cleanup;
	}

	if ((retcode = mkdirs(path)) != 0)
		goto cleanup;

	if (packagesite[strlen(packagesite) - 1] == '/')
		snprintf(url, sizeof(url), "%s%s", packagesite,
				pkg_get(pkg, PKG_REPOPATH));
	else
		snprintf(url, sizeof(url), "%s/%s", packagesite,
				pkg_get(pkg, PKG_REPOPATH));

	retcode = pkg_fetch_file(url, dest);
	fetched = 1;

	if (retcode != EPKG_OK)
		goto cleanup;

	checksum:
	retcode = sha256_file(dest, cksum);
	if (retcode == EPKG_OK)
		if (strcmp(cksum, pkg_get(pkg, PKG_CKSUM))) {
			if (fetched == 1) {
				pkg_emit_error("%s-%s failed checksum from repository",
						pkg_get(pkg, PKG_NAME), pkg_get(pkg, PKG_VERSION));
				retcode = EPKG_FATAL;
			} else {
				pkg_emit_error("cached package %s-%s: checksum mismatch, fetching from remote",
						pkg_get(pkg, PKG_NAME), pkg_get(pkg, PKG_VERSION));
				unlink(dest);
				return (pkg_repo_fetch(pkg));
			}
		}

	cleanup:
	if (retcode != EPKG_OK)
		unlink(dest);

	return (retcode);
}
示例#4
0
文件: fetch.c 项目: Moscarda/pkg
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);
}
示例#5
0
文件: add.c 项目: jillest/pkg
int
exec_add(int argc, char **argv)
{
	struct pkgdb *db = NULL;
	struct sbuf *failedpkgs = NULL;
	char path[MAXPATHLEN];
	char *file;
	int retcode;
	int ch;
	int i;
	int failedpkgcount = 0;
	pkg_flags f = PKG_FLAG_NONE;
	struct pkg_manifest_key *keys = NULL;
	const char *location = NULL;

	/* options descriptor */
	struct option longopts[] = {
		{ "no-scripts",          no_argument,            NULL,           'I' },
		{ "automatic",           no_argument,            NULL,           'A' },
		{ "force",               no_argument,            NULL,           'f' },
		{ "accept-missing",      no_argument,            NULL,           'M' },
		{ "quiet",               no_argument,            NULL,           'q' },
		{ "relocate",            required_argument,      NULL,            1  },
		{ NULL,                  0,                      NULL,            0  }
	};

	while ((ch = getopt_long(argc, argv, "IAfqM", longopts, NULL)) != -1) {
		switch (ch) {
		case 'I':
			f |= PKG_ADD_NOSCRIPT;
			break;
		case 'A':
			f |= PKG_ADD_AUTOMATIC;
			break;
		case 'f':
			f |= PKG_ADD_FORCE;
			force = true;
			break;
		case 'M':
			f |= PKG_ADD_FORCE_MISSING;
			break;
		case 'q':
			quiet = true;
			break;
		case 1:
			location = optarg;
			break;
		default:
			usage_add();
			return (EX_USAGE);
		}
	}
	argc -= optind;
	argv += optind;

	if (argc < 1) {
		usage_add();
		return (EX_USAGE);
	}

	retcode = pkgdb_access(PKGDB_MODE_READ  |
			       PKGDB_MODE_WRITE |
			       PKGDB_MODE_CREATE,
			       PKGDB_DB_LOCAL);
	if (retcode == EPKG_ENOACCESS) {
		warnx("Insufficient privileges to add packages");
		return (EX_NOPERM);
	} else if (retcode != EPKG_OK)
		return (EX_IOERR);

	if (pkgdb_open(&db, PKGDB_DEFAULT) != EPKG_OK)
		return (EX_IOERR);

	if (pkgdb_obtain_lock(db, PKGDB_LOCK_EXCLUSIVE) != EPKG_OK) {
		pkgdb_close(db);
		warnx("Cannot get an exclusive lock on a database, it is locked by another process");
		return (EX_TEMPFAIL);
	}

	failedpkgs = sbuf_new_auto();
	pkg_manifest_keys_new(&keys);
	for (i = 0; i < argc; i++) {
		if (is_url(argv[i]) == EPKG_OK) {
			snprintf(path, sizeof(path), "%s/%s.XXXXX",
			    getenv("TMPDIR") != NULL ? getenv("TMPDIR") : "/tmp", basename(argv[i]));
			if ((retcode = pkg_fetch_file(NULL, argv[i], path, 0)) != EPKG_OK)
				break;

			file = path;
		} else {
			file = argv[i];

			/* Special case: treat a filename of "-" as
			   meaning 'read from stdin.'  It doesn't make
			   sense to have a filename of "-" more than
			   once per command line, but we aren't
			   testing for that at the moment */

			if (strcmp(file, "-") != 0 && access(file, F_OK) != 0) {
				warn("%s", file);
				if (errno == ENOENT)
					warnx("Was 'pkg install %s' meant?", file);
				sbuf_cat(failedpkgs, argv[i]);
				if (i != argc - 1)
					sbuf_printf(failedpkgs, ", ");
				failedpkgcount++;
				continue;
			}

		}

		if ((retcode = pkg_add(db, file, f, keys, location)) != EPKG_OK) {
			sbuf_cat(failedpkgs, argv[i]);
			if (i != argc - 1)
				sbuf_printf(failedpkgs, ", ");
			failedpkgcount++;
		}

		if (is_url(argv[i]) == EPKG_OK)
			unlink(file);

	}
	pkg_manifest_keys_free(keys);
	pkgdb_release_lock(db, PKGDB_LOCK_EXCLUSIVE);
	pkgdb_close(db);
	
	if(failedpkgcount > 0) {
		sbuf_finish(failedpkgs);
		printf("\nFailed to install the following %d package(s): %s\n", failedpkgcount, sbuf_data(failedpkgs));
		retcode = EPKG_FATAL;
	}
	sbuf_delete(failedpkgs);

	if (messages != NULL) {
		sbuf_finish(messages);
		printf("%s", sbuf_data(messages));
	}

	return (retcode == EPKG_OK ? EX_OK : EX_SOFTWARE);
}
示例#6
0
文件: update.c 项目: culot/pkgng
static int
update_from_remote_repo(const char *name, const char *url)
{
	struct archive *a = NULL;
	struct archive_entry *ae = NULL;
	char repofile[MAXPATHLEN];
	char repofile_unchecked[MAXPATHLEN];
	char tmp[21];
	const char *dbdir = NULL;
	unsigned char *sig = NULL;
	int siglen = 0;
	int rc = EPKG_OK;

	(void)strlcpy(tmp, "/tmp/repo.txz.XXXXXX", sizeof(tmp));
	if (mktemp(tmp) == NULL) {
		warnx("Could not create temporary file %s, aborting update.\n", tmp);
		return (EPKG_FATAL);
	}

	if (pkg_config_string(PKG_CONFIG_DBDIR, &dbdir) != EPKG_OK) {
		warnx("Cant get dbdir config entry");
		return (EPKG_FATAL);
	}

	if (pkg_fetch_file(url, tmp) != EPKG_OK) {
		/*
		 * No need to unlink(tmp) here as it is already
		 * done in pkg_fetch_file() in case fetch failed.
		 */
		return (EPKG_FATAL);
	}

	a = archive_read_new();
	archive_read_support_compression_all(a);
	archive_read_support_format_tar(a);

	archive_read_open_filename(a, tmp, 4096);

	while (archive_read_next_header(a, &ae) == ARCHIVE_OK) {
		if (strcmp(archive_entry_pathname(ae), "repo.sqlite") == 0) {
			snprintf(repofile, sizeof(repofile), "%s/%s.sqlite", dbdir, name);
			snprintf(repofile_unchecked, sizeof(repofile_unchecked), "%s.unchecked", repofile);
			archive_entry_set_pathname(ae, repofile_unchecked);
			archive_read_extract(a, ae, EXTRACT_ARCHIVE_FLAGS);
		}
		if (strcmp(archive_entry_pathname(ae), "signature") == 0) {
			siglen = archive_entry_size(ae);
			sig = malloc(siglen);
			archive_read_data(a, sig, siglen);
		}
	}

	if (sig != NULL) {
		if (pkg_repo_verify(repofile_unchecked, sig, siglen - 1) != EPKG_OK) {
			warnx("Invalid signature, removing repository.\n");
			unlink(repofile_unchecked);
			free(sig);
			rc = EPKG_FATAL;
			goto cleanup;
		}
	}

	rename(repofile_unchecked, repofile);

cleanup:
	if (a != NULL)
		archive_read_finish(a);

	(void)unlink(tmp);

	return (rc);
}
示例#7
0
文件: add.c 项目: DarknessWarrior/pkg
int
exec_add(int argc, char **argv)
{
	struct pkgdb *db = NULL;
	struct sbuf *failedpkgs = NULL;
	char path[MAXPATHLEN + 1];
	char *file;
	int retcode;
	int ch;
	int i;
	int failedpkgcount = 0;
	pkg_flags f = PKG_FLAG_NONE;
	struct pkg_manifest_key *keys = NULL;

	while ((ch = getopt(argc, argv, "IAfq")) != -1) {
		switch (ch) {
		case 'I':
			f |= PKG_ADD_NOSCRIPT;
			break;
		case 'A':
			f |= PKG_FLAG_AUTOMATIC;
			break;
		case 'f':
			f |= PKG_FLAG_FORCE;
			break;
		case 'q':
			quiet = true;
			break;
		default:
			usage_add();
			return (EX_USAGE);
		}
	}
	argc -= optind;
	argv += optind;

	if (argc < 1) {
		usage_add();
		return (EX_USAGE);
	}

	retcode = pkgdb_access(PKGDB_MODE_READ  |
			       PKGDB_MODE_WRITE |
			       PKGDB_MODE_CREATE,
			       PKGDB_DB_LOCAL);
	if (retcode == EPKG_ENOACCESS) {
		warnx("Insufficient privilege to add packages");
		return (EX_NOPERM);
	} else if (retcode != EPKG_OK)
		return (EX_IOERR);

	if (pkgdb_open(&db, PKGDB_DEFAULT) != EPKG_OK)
		return (EX_IOERR);

	failedpkgs = sbuf_new_auto();
	pkg_manifest_keys_new(&keys);
	for (i = 0; i < argc; i++) {
		if (is_url(argv[i]) == EPKG_OK) {
			snprintf(path, sizeof(path), "./%s", basename(argv[i]));
			if ((retcode = pkg_fetch_file(NULL, argv[i], path, 0)) != EPKG_OK)
				break;

			file = path;
		} else {
			file = argv[i];
			if (access(file, F_OK) != 0) {
				warn("%s",file);
				if (errno == ENOENT)
					warnx("Did you mean 'pkg install %s'?", file);
				sbuf_cat(failedpkgs, argv[i]);
				if (i != argc - 1)
					sbuf_printf(failedpkgs, ", ");
				failedpkgcount++;
				continue;
			}

		}

		if ((retcode = pkg_add(db, file, f, keys)) != EPKG_OK) {
			sbuf_cat(failedpkgs, argv[i]);
			if (i != argc - 1)
				sbuf_printf(failedpkgs, ", ");
			failedpkgcount++;
		}

	}
	pkg_manifest_keys_free(keys);

	pkgdb_close(db);
	
	if(failedpkgcount > 0) {
		sbuf_finish(failedpkgs);
		printf("\nFailed to install the following %d package(s): %s\n", failedpkgcount, sbuf_data(failedpkgs));
		retcode = EPKG_FATAL;
	}
	sbuf_delete(failedpkgs);

	if (messages != NULL) {
		sbuf_finish(messages);
		printf("%s", sbuf_data(messages));
	}

	return (retcode == EPKG_OK ? EX_OK : EX_SOFTWARE);
}
示例#8
0
文件: fetch.c 项目: vianaweb/pkg
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);
}