[pacman-dev] [PATCH v2] libalpm: fix download rates becoming negative

morganamilo morganamilo at archlinux.org
Mon Apr 19 20:19:12 UTC 2021


When a download fails on one mirror a new download is started on the
next mirror. This causes the amount downloaded to reset, confusing the
rate math and making it display a negative rate.

This is further complicated by the fact that a download may be resumed
from where it is or started over.

To account for this we alert the frontend that the download was
restarted. Pacman then starts the progress bar over.

diff --git a/lib/libalpm/alpm.h b/lib/libalpm/alpm.h
index 833df829..e9cad015 100644
--- a/lib/libalpm/alpm.h
+++ b/lib/libalpm/alpm.h
@@ -1169,6 +1169,8 @@ typedef enum _alpm_download_event_type_t {
 	ALPM_DOWNLOAD_INIT,
 	/** A download made progress */
 	ALPM_DOWNLOAD_PROGRESS,
+	/** Download will be retried */
+	ALPM_DOWNLOAD_RETRY,
 	/** A download completed */
 	ALPM_DOWNLOAD_COMPLETED
 } alpm_download_event_type_t;
@@ -1187,6 +1189,11 @@ typedef struct _alpm_download_event_progress_t {
 	off_t total;
 } alpm_download_event_progress_t;
 
+/** Context struct for when a download retries. */
+typedef struct _alpm_download_event_retry_t {
+	/** If the download will resume or start over */
+	int resume;
+} alpm_download_event_retry_t;
 
 /** Context struct for when a download completes. */
 typedef struct _alpm_download_event_completed_t {
diff --git a/lib/libalpm/dload.c b/lib/libalpm/dload.c
index a4c42f8d..98ea5b6f 100644
--- a/lib/libalpm/dload.c
+++ b/lib/libalpm/dload.c
@@ -431,7 +431,11 @@ static int curl_retry_next_server(CURLM *curlm, CURL *curl, struct dload_payload
 	MALLOC(payload->fileurl, len, RET_ERR(handle, ALPM_ERR_MEMORY, -1));
 	snprintf(payload->fileurl, len, "%s/%s", server, payload->filepath);
 
-	if(payload->unlink_on_fail) {
+	if(payload->allow_resume) {
+		/* reset progress for next server */
+		curl_easy_setopt(curl, CURLOPT_RESUME_FROM_LARGE, payload->prevprogress);
+		payload->initial_size = payload->prevprogress;
+	} else {
 		/* we keep the file for a new retry but remove its data if any */
 		fflush(payload->localf);
 		if(ftruncate(fileno(payload->localf), 0)) {
@@ -440,6 +444,12 @@ static int curl_retry_next_server(CURLM *curlm, CURL *curl, struct dload_payload
 		fseek(payload->localf, 0, SEEK_SET);
 	}
 
+	if(handle->dlcb && !payload->signature) {
+		alpm_download_event_retry_t cb_data;
+		cb_data.resume = payload->allow_resume;
+		handle->dlcb(payload->remote_name, ALPM_DOWNLOAD_RETRY, &cb_data);
+	}
+
 	/* Set curl with the new URL */
 	curl_easy_setopt(curl, CURLOPT_URL, payload->fileurl);
 
@@ -487,7 +497,6 @@ static int curl_check_finished_download(CURLM *curlm, CURLMsg *msg,
 			_alpm_log(handle, ALPM_LOG_DEBUG, "%s: response code %ld\n",
 					payload->remote_name, payload->respcode);
 			if(payload->respcode >= 400) {
-				payload->unlink_on_fail = 1;
 				if(!payload->errors_ok) {
 					handle->pm_errno = ALPM_ERR_RETRIEVE;
 					/* non-translated message is same as libcurl */
@@ -502,6 +511,7 @@ static int curl_check_finished_download(CURLM *curlm, CURLMsg *msg,
 					(*active_downloads_num)++;
 					return 2;
 				} else {
+					payload->unlink_on_fail = 1;
 					goto cleanup;
 				}
 			}
@@ -519,7 +529,6 @@ static int curl_check_finished_download(CURLM *curlm, CURLMsg *msg,
 			}
 			goto cleanup;
 		case CURLE_COULDNT_RESOLVE_HOST:
-			payload->unlink_on_fail = 1;
 			handle->pm_errno = ALPM_ERR_SERVER_BAD_URL;
 			_alpm_log(handle, ALPM_LOG_ERROR,
 					_("failed retrieving file '%s' from %s : %s\n"),
@@ -529,13 +538,10 @@ static int curl_check_finished_download(CURLM *curlm, CURLMsg *msg,
 				(*active_downloads_num)++;
 				return 2;
 			} else {
+				payload->unlink_on_fail = 1;
 				goto cleanup;
 			}
 		default:
-			/* delete zero length downloads */
-			if(fstat(fileno(payload->localf), &st) == 0 && st.st_size == 0) {
-				payload->unlink_on_fail = 1;
-			}
 			if(!payload->errors_ok) {
 				handle->pm_errno = ALPM_ERR_LIBCURL;
 				_alpm_log(handle, ALPM_LOG_ERROR,
@@ -551,6 +557,10 @@ static int curl_check_finished_download(CURLM *curlm, CURLMsg *msg,
 				(*active_downloads_num)++;
 				return 2;
 			} else {
+				/* delete zero length downloads */
+				if(fstat(fileno(payload->localf), &st) == 0 && st.st_size == 0) {
+					payload->unlink_on_fail = 1;
+				}
 				goto cleanup;
 			}
 	}
diff --git a/src/pacman/callback.c b/src/pacman/callback.c
index a28a79a9..19bcfe58 100644
--- a/src/pacman/callback.c
+++ b/src/pacman/callback.c
@@ -992,6 +992,34 @@ static void dload_progress_event(const char *filename, alpm_download_event_progr
 	fflush(stdout);
 }
 
+/* download retried */
+static void dload_retry_event(const char *filename, alpm_download_event_retry_t *data) {
+	if(!dload_progressbar_enabled()) {
+		return;
+	}
+
+	int index;
+	struct pacman_progress_bar *bar;
+	bool ok = find_bar_for_filename(filename, &index, &bar);
+	assert(ok);
+
+	if(!data->resume) {
+		if(total_enabled) {
+			/* TODO if !resume then we delete the file. this may make the new total larger */
+			totalbar->xfered -= bar->xfered;
+		}
+	}
+
+	bar->xfered = 0;
+	bar->total_size = 0;
+	bar->init_time = get_time_ms();
+	bar->sync_time = 0;
+	bar->sync_xfered = 0;
+	bar->rate = 0.0;
+	bar->eta = 0.0;
+}
+
+
 /* download completed */
 static void dload_complete_event(const char *filename, alpm_download_event_completed_t *data)
 {
@@ -1075,6 +1103,8 @@ void cb_download(const char *filename, alpm_download_event_type_t event, void *d
 		dload_init_event(filename, data);
 	} else if(event == ALPM_DOWNLOAD_PROGRESS) {
 		dload_progress_event(filename, data);
+	} else if(event == ALPM_DOWNLOAD_RETRY) {
+		dload_retry_event(filename, data);
 	} else if(event == ALPM_DOWNLOAD_COMPLETED) {
 		dload_complete_event(filename, data);
 	} else {
-- 
2.31.1


More information about the pacman-dev mailing list