[pacman-dev] Handle .part files that are the size of the correct package

Message ID 20191115134617.86141-1-allan@archlinux.org
State Accepted, archived
Headers show
Series [pacman-dev] Handle .part files that are the size of the correct package | expand

Commit Message

Allan McRae Nov. 15, 2019, 1:46 p.m. UTC
In rare cases, likely due to a well timed Ctrl+C, but possibly due to a
broken mirror, a ".part" file may have size at least that of the correct
package size.

When encountering this issue, currently pacman fails in different ways
depending on where the package falls in the list to download.  If last,
"wrong or NULL argument passed" error is reported, or a "invalid or
corrupt package" issue if not.

Capture these .part files, and remove the extension. This lets pacman
either use the package if valid, or offer to remove it if it fails checksum
or signature verification.

Signed-off-by: Allan McRae <allan@archlinux.org>
---

To test this patch do:

mv /var/cache/pacman/pkg/xz-5.2.4-2-x86_64.pkg.tar.xz{,.part}
pacman -S xz


Having to run _alpm_filecache_find() in find_dl_candidates() on all packages that
have download size of 0 is not given we run already it in compute_download_size().
However, we would require storing it in the alpm_pkg_t struct and I don't think
that overhead was worth it.  However, we then call _alpm_filecache_find() in 
check_validity() and load_package()...  Still probably not worth it!

 lib/libalpm/dload.c |  6 ++++++
 lib/libalpm/sync.c  | 14 ++++++++++++--
 2 files changed, 18 insertions(+), 2 deletions(-)

Comments

Andrew Gregory Nov. 16, 2019, 3:53 a.m. UTC | #1
On 11/15/19 at 11:46pm, Allan McRae wrote:
> In rare cases, likely due to a well timed Ctrl+C, but possibly due to a
> broken mirror, a ".part" file may have size at least that of the correct
> package size.
> 
> When encountering this issue, currently pacman fails in different ways
> depending on where the package falls in the list to download.  If last,
> "wrong or NULL argument passed" error is reported, or a "invalid or
> corrupt package" issue if not.
> 
> Capture these .part files, and remove the extension. This lets pacman
> either use the package if valid, or offer to remove it if it fails checksum
> or signature verification.
> 
> Signed-off-by: Allan McRae <allan@archlinux.org>
> ---
> 
> To test this patch do:
> 
> mv /var/cache/pacman/pkg/xz-5.2.4-2-x86_64.pkg.tar.xz{,.part}
> pacman -S xz
> 
> 
> Having to run _alpm_filecache_find() in find_dl_candidates() on all packages that
> have download size of 0 is not given we run already it in compute_download_size().
> However, we would require storing it in the alpm_pkg_t struct and I don't think
> that overhead was worth it.  However, we then call _alpm_filecache_find() in 
> check_validity() and load_package()...  Still probably not worth it!
> 
>  lib/libalpm/dload.c |  6 ++++++
>  lib/libalpm/sync.c  | 14 ++++++++++++--
>  2 files changed, 18 insertions(+), 2 deletions(-)

ACK.  This does cause one minor oddity: if the .part is the only
package being installed, pacman prints "::Retrieving packages..." but
then doesn't actually download anything.

Patch

diff --git a/lib/libalpm/dload.c b/lib/libalpm/dload.c
index ddcc45f6..40a1d07d 100644
--- a/lib/libalpm/dload.c
+++ b/lib/libalpm/dload.c
@@ -414,6 +414,12 @@  static int curl_download_internal(struct dload_payload *payload,
 
 	curl_set_handle_opts(payload, curl, error_buffer);
 
+	if(payload->max_size == payload->initial_size) {
+		/* .part file is complete */
+		ret = 0;
+		goto cleanup;
+	}
+
 	if(localf == NULL) {
 		localf = fopen(payload->tempfile_name, payload->tempfile_openmode);
 		if(localf == NULL) {
diff --git a/lib/libalpm/sync.c b/lib/libalpm/sync.c
index 70c37890..97a351fe 100644
--- a/lib/libalpm/sync.c
+++ b/lib/libalpm/sync.c
@@ -731,6 +731,8 @@  static int find_dl_candidates(alpm_db_t *repo, alpm_list_t **files)
 		alpm_pkg_t *spkg = i->data;
 
 		if(spkg->origin != ALPM_PKG_FROM_FILE && repo == spkg->origin_data.db) {
+			char *fpath = NULL;
+
 			if(!repo->servers) {
 				handle->pm_errno = ALPM_ERR_SERVER_NONE;
 				_alpm_log(handle, ALPM_LOG_ERROR, "%s: %s\n",
@@ -738,13 +740,21 @@  static int find_dl_candidates(alpm_db_t *repo, alpm_list_t **files)
 				return 1;
 			}
 
-			if(spkg->download_size != 0) {
+			ASSERT(spkg->filename != NULL, RET_ERR(handle, ALPM_ERR_PKG_INVALID_NAME, -1));
+
+			if(spkg->download_size == 0) {
+				/* check for file in cache - allows us to handle complete .part files */
+				fpath = _alpm_filecache_find(handle, spkg->filename);
+			}
+
+			if(spkg->download_size != 0 || !fpath) {
 				struct dload_payload *payload;
-				ASSERT(spkg->filename != NULL, RET_ERR(handle, ALPM_ERR_PKG_INVALID_NAME, -1));
 				payload = build_payload(handle, spkg->filename, spkg->size, repo->servers);
 				ASSERT(payload, return -1);
 				*files = alpm_list_add(*files, payload);
 			}
+
+			FREE(fpath);
 		}
 	}