lib/dload: major refactor of signature downloading
There's a lot of related moving parts here:
* iteration through mirrors is moved back to the calling functions. this
allows removal of _alpm_download_single_file and _alpm_download_files
* rename download => _alpm_download. also modified to accept an extra
arg of type pgp_verify_t which is passed through to
curl_download_internal
* move the actual signature download to curl_download_internal. this
allows us to ensure that the signature and the file came from the
same mirror, and only to accept the file if signature verification
passes.
- /* Download and check the signature of the database if needed */
- if(db->pgp_verify != PM_PGP_VERIFY_NEVER) {
- char *sigfile, *sigfilepath;
- int sigret;
-
- len = strlen(dbfile) + 5;
- MALLOC(sigfile, len, RET_ERR(PM_ERR_MEMORY, -1));
- sprintf(sigfile, "%s.sig", dbfile);
-
- /* prevent old signature being used if the following download fails */
- len = strlen(syncpath) + strlen(sigfile) + 1;
- MALLOC(sigfilepath, len, RET_ERR(PM_ERR_MEMORY, -1));
- sprintf(sigfilepath, "%s%s", syncpath, sigfile);
- _alpm_rmrf(sigfilepath);
- free(sigfilepath);
-
- sigret = _alpm_download_single_file(sigfile, db->servers, syncpath, 0);
- free(sigfile);
-
- if(sigret == -1 && db->pgp_verify == PM_PGP_VERIFY_ALWAYS) {
- _alpm_log(PM_LOG_ERROR, _("Failed to download signature for db: %s
"),
- alpm_strerrorlast());
- pm_errno = PM_ERR_SIG_INVALID;
- ret = -1;
- goto cleanup;
- }
-
- sigret = alpm_db_check_pgp_signature(db);
- if((db->pgp_verify == PM_PGP_VERIFY_ALWAYS && sigret != 0) ||
- (db->pgp_verify == PM_PGP_VERIFY_OPTIONAL && sigret == 1)) {
- /* pm_errno was set by the checking code */
- /* TODO: should we just leave the unverified database */
- ret = -1;
- goto cleanup;
- }
- }
-
/* Cache needs to be rebuilt */
_alpm_db_free_pkgcache(db);
cleanup:
- free(dbfile);
free(syncpath);
umask(oldmask);
return ret;
diff --git a/lib/libalpm/dload.c b/lib/libalpm/dload.c
index 948e623..69fc708 100644
--- a/lib/libalpm/dload.c
+++ b/lib/libalpm/dload.c
@@ -155,15 +155,15 @@ static int utimes_long(const char *path, long time)
static int curl_download_internal(const char *url, const char *localpath,
- int force)
+ int force, pgp_verify_t check_sig)
{
- int ret = -1;
+ int ret = -1, sig_ret = -1;
FILE *localf = NULL;
const char *open_mode, *useragent;
char *destfile, *tempfile;
char hostname[256]; /* RFC1123 states applications should support this length */
struct stat st;
- long httpresp, timecond, remote_time;
+ long resp_code, timecond, remote_time;
double remote_size, bytes_dl;
struct sigaction sig_pipe[2], sig_int[2];
struct fileinfo dlfile;
@@ -242,7 +242,7 @@ static int curl_download_internal(const char *url, const char *localpath,
handle->curlerr = curl_easy_perform(handle->curl);
/* retrieve info about the state of the transfer */
- curl_easy_getinfo(handle->curl, CURLINFO_RESPONSE_CODE, &httpresp);
+ curl_easy_getinfo(handle->curl, CURLINFO_RESPONSE_CODE, &resp_code);
curl_easy_getinfo(handle->curl, CURLINFO_FILETIME, &remote_time);
curl_easy_getinfo(handle->curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &remote_size);
curl_easy_getinfo(handle->curl, CURLINFO_SIZE_DOWNLOAD, &bytes_dl);
@@ -279,15 +279,92 @@ static int curl_download_internal(const char *url, const char *localpath,
ret = 0;
+ /* we were successful downloading the file. now we need to fetch a signature
+ * if necessary. we already know that a new file was actually fetched; the
+ * not modified case was handled and we jumped to label cleanup already. */
+ if(check_sig != PM_PGP_VERIFY_NEVER) {
+ FILE *sigf = NULL;
+ char *sig_url, *sig_temp = NULL, *sig_dest = NULL;
+ size_t len;
+ CURLcode sig_curlerr;
+
+ /* if we enter this block, assume failure unless we explicitly state
+ * otherwise */
+ sig_ret = -1;
+
+ len = strlen(url) + 5;
+ CALLOC(sig_url, len, sizeof(char), goto sig_cleanup);
+ snprintf(sig_url, len, "%s.sig", url);
+
+ dlfile.filename = get_filename(sig_url);
+ dlfile.initial_size = 0;
+
+ sig_dest = get_fullpath(localpath, dlfile.filename, "");
+ sig_temp = get_fullpath(localpath, dlfile.filename, ".part");
+
+ curl_easy_setopt(handle->curl, CURLOPT_URL, sig_url);
+ curl_easy_setopt(handle->curl, CURLOPT_PROGRESSDATA, (void *)&dlfile);
+ curl_easy_setopt(handle->curl, CURLOPT_TIMECONDITION, 0L);
+ curl_easy_setopt(handle->curl, CURLOPT_TIMEVALUE, 0L);
+ curl_easy_setopt(handle->curl, CURLOPT_RESUME_FROM, 0L);
+
+ sigf = fopen(sig_temp, "wb");
+ if(sigf == NULL) {
+ goto sig_cleanup;
+ }
+ curl_easy_setopt(handle->curl, CURLOPT_WRITEDATA, sigf);
+
+ /* Progress 0 - initialize */
+ prevprogress = 0;
+
+ /* perform transfer */
+ sig_curlerr = curl_easy_perform(handle->curl);
+
+ /* retrieve info about the state of the transfer */
+ curl_easy_getinfo(handle->curl, CURLINFO_RESPONSE_CODE, &resp_code);
+ curl_easy_getinfo(handle->curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &remote_size);
+ curl_easy_getinfo(handle->curl, CURLINFO_SIZE_DOWNLOAD, &bytes_dl);
+
+ if(sig_curlerr == CURLE_ABORTED_BY_CALLBACK) {
+ goto sig_cleanup;
+ } else if((sig_curlerr == CURLE_REMOTE_FILE_NOT_FOUND
+ || sig_curlerr == CURLE_FTP_COULDNT_RETR_FILE
+ || (sig_curlerr == CURLE_HTTP_RETURNED_ERROR && resp_code == 404))
+ && check_sig != PM_PGP_VERIFY_ALWAYS) {
+ /* an error case that is not fatal */
+ sig_ret = 1;
+ goto sig_cleanup;
+ } else if(sig_curlerr != CURLE_OK) {
+ pm_errno = PM_ERR_LIBCURL;
+ handle->curlerr = sig_curlerr;
+ _alpm_log(PM_LOG_ERROR, _("failed retrieving file '%s' from %s : %s
"),
+ dlfile.filename, hostname, curl_easy_strerror(handle->curlerr));
+ /* unlink both files- they are only good if they both were downloaded */
+ unlink(sig_temp);
+ unlink(tempfile);
+ goto sig_cleanup;
+ }
+
+ sig_ret = 0;
+
+sig_cleanup:
+ if(sigf) {
+ fclose(sigf);
+ }
+ if(sig_ret == 0) {
+ rename(sig_temp, sig_dest);
+ }
+ FREE(sig_temp);
+ FREE(sig_dest);
+ }
+
cleanup:
if(localf != NULL) {
fclose(localf);
utimes_long(tempfile, remote_time);
}
- /* TODO: A signature download will need to return success here as well before
- * we're willing to rotate the new file into place. */
- if(ret == 0) {
+ if(ret == 0 && sig_ret != 0) {
rename(tempfile, destfile);
}
- if (errors) {
+ if(errors) {
_alpm_log(PM_LOG_WARNING, _("failed to retrieve some files from %s
"),
current->treename);
if(pm_errno == 0) {
--
1.7.4.2
04-21-2011, 12:03 AM
Dan McGee
lib/dload: major refactor of signature downloading
On Mon, Mar 28, 2011 at 2:15 PM, Dave Reisner <d@falconindy.com> wrote:
> There's a lot of related moving parts here:
> * iteration through mirrors is moved back to the calling functions. this
> *allows removal of _alpm_download_single_file and _alpm_download_files
> * rename download => _alpm_download. also modified to accept an extra
> *arg of type pgp_verify_t which is passed through to
> *curl_download_internal
> * move the actual signature download to curl_download_internal. this
> *allows us to ensure that the signature and the file came from the
> *same mirror, and only to accept the file if signature verification
> *passes.
>
> Signed-off-by: Dave Reisner <d@falconindy.com>
This one needs a little more testing...note the (now) non-existant
core.db file, and the completely bogus community2.
dmcgee@galway ~/projects/pacman (master)
$ sudo ./src/pacman/pacman -Sy
:: Synchronizing package databases...
testing is up to date
core is up to date
extra is up to date
community-testing is up to date
multilib is up to date
community is up to date
community2 is up to date