mirror of
				https://gitlab.archlinux.org/pacman/pacman.git
				synced 2025-11-04 01:14:41 +01:00 
			
		
		
		
	Compare commits
	
		
			14 Commits
		
	
	
		
			release/6.
			...
			allan/priv
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					56c3b396df | ||
| 
						 | 
					4041870a43 | ||
| 
						 | 
					cd585e94db | ||
| 
						 | 
					3c9d5f8192 | ||
| 
						 | 
					79783695e0 | ||
| 
						 | 
					7953c45e16 | ||
| 
						 | 
					044c13d825 | ||
| 
						 | 
					8f4f7a2524 | ||
| 
						 | 
					6cbdc74f31 | ||
| 
						 | 
					95b7c5a613 | ||
| 
						 | 
					f7ec77fa74 | ||
| 
						 | 
					76b57a16f0 | ||
| 
						 | 
					29a4a22c70 | ||
| 
						 | 
					e421e01326 | 
@@ -207,6 +207,10 @@ Options
 | 
			
		||||
	positive integer. If this config option is not set then only one download
 | 
			
		||||
	stream is used (i.e. downloads happen sequentially).
 | 
			
		||||
 | 
			
		||||
*DownloadUser =* username::
 | 
			
		||||
	Specifies the user to switch to for downloading files. If this config
 | 
			
		||||
	option is not set then the downloads are done as the user running pacman.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Repository Sections
 | 
			
		||||
-------------------
 | 
			
		||||
 
 | 
			
		||||
@@ -1870,6 +1870,28 @@ int alpm_option_set_gpgdir(alpm_handle_t *handle, const char *gpgdir);
 | 
			
		||||
/** @} */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/** @name Accessors for use sandboxuser
 | 
			
		||||
 *
 | 
			
		||||
 *  This controls the user that libalpm will use for sensitive operations like
 | 
			
		||||
 *  downloading files.
 | 
			
		||||
 * @{
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/** Returns the user to switch to for sensitive operations.
 | 
			
		||||
 * @return the user name
 | 
			
		||||
 */
 | 
			
		||||
const char *alpm_option_get_sandboxuser(alpm_handle_t *handle);
 | 
			
		||||
 | 
			
		||||
/** Sets the user to switch to for sensitive operations.
 | 
			
		||||
 * @param handle the context handle
 | 
			
		||||
 * @param sandboxuser the user to set
 | 
			
		||||
 */
 | 
			
		||||
int alpm_option_set_sandboxuser(alpm_handle_t *handle, const char *sandboxuser);
 | 
			
		||||
 | 
			
		||||
/* End of sandboxuser accessors */
 | 
			
		||||
/** @} */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/** @name Accessors for use syslog
 | 
			
		||||
 *
 | 
			
		||||
 * This controls whether libalpm will also use the syslog. Even if this option
 | 
			
		||||
@@ -2929,6 +2951,12 @@ const char *alpm_version(void);
 | 
			
		||||
 * */
 | 
			
		||||
int alpm_capabilities(void);
 | 
			
		||||
 | 
			
		||||
/** Drop privileges by switching to a different user.
 | 
			
		||||
 * @param sandboxuser the user to switch to
 | 
			
		||||
 * @return 0 on success, -1 on failure
 | 
			
		||||
 */
 | 
			
		||||
int alpm_sandbox_setup_child(const char *sandboxuser);
 | 
			
		||||
 | 
			
		||||
/* End of libalpm_misc */
 | 
			
		||||
/** @} */
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -189,6 +189,18 @@ int SYMEXPORT alpm_db_update(alpm_handle_t *handle, alpm_list_t *dbs, int force)
 | 
			
		||||
		MALLOC(payload->filepath, len,
 | 
			
		||||
			FREE(payload); GOTO_ERR(handle, ALPM_ERR_MEMORY, cleanup));
 | 
			
		||||
		snprintf(payload->filepath, len, "%s%s", db->treename, dbext);
 | 
			
		||||
 | 
			
		||||
		STRDUP(payload->remote_name, payload->filepath,
 | 
			
		||||
			_alpm_dload_payload_reset(payload); FREE(payload);
 | 
			
		||||
			GOTO_ERR(handle, ALPM_ERR_MEMORY, cleanup));
 | 
			
		||||
		payload->destfile_name = _alpm_get_fullpath(syncpath, payload->remote_name, "");
 | 
			
		||||
		payload->tempfile_name = _alpm_get_fullpath(syncpath, payload->remote_name, ".part");
 | 
			
		||||
		if(!payload->destfile_name || !payload->tempfile_name) {
 | 
			
		||||
			_alpm_dload_payload_reset(payload);
 | 
			
		||||
			FREE(payload);
 | 
			
		||||
			GOTO_ERR(handle, ALPM_ERR_MEMORY, cleanup);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		payload->handle = handle;
 | 
			
		||||
		payload->force = dbforce;
 | 
			
		||||
		payload->unlink_on_fail = 1;
 | 
			
		||||
 
 | 
			
		||||
@@ -28,7 +28,9 @@
 | 
			
		||||
#include <sys/time.h>
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#include <sys/stat.h>
 | 
			
		||||
#include <sys/wait.h>
 | 
			
		||||
#include <signal.h>
 | 
			
		||||
#include <pwd.h>
 | 
			
		||||
 | 
			
		||||
#ifdef HAVE_NETINET_IN_H
 | 
			
		||||
#include <netinet/in.h> /* IPPROTO_TCP */
 | 
			
		||||
@@ -48,6 +50,145 @@
 | 
			
		||||
#include "log.h"
 | 
			
		||||
#include "util.h"
 | 
			
		||||
#include "handle.h"
 | 
			
		||||
#include "sandbox.h"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static const char *get_filename(const char *url)
 | 
			
		||||
{
 | 
			
		||||
	char *filename = strrchr(url, '/');
 | 
			
		||||
	if(filename != NULL) {
 | 
			
		||||
		return filename + 1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* no slash found, it's a filename */
 | 
			
		||||
	return url;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* prefix to avoid possible future clash with getumask(3) */
 | 
			
		||||
static mode_t _getumask(void)
 | 
			
		||||
{
 | 
			
		||||
	mode_t mask = umask(0);
 | 
			
		||||
	umask(mask);
 | 
			
		||||
	return mask;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int touch_and_own_file(const char *filename, const char *user)
 | 
			
		||||
{
 | 
			
		||||
	int fd;
 | 
			
		||||
	struct passwd const *pw = NULL;
 | 
			
		||||
 | 
			
		||||
	ASSERT(filename != NULL, return -1);
 | 
			
		||||
	ASSERT(user != NULL, return -1);
 | 
			
		||||
 | 
			
		||||
	ASSERT((fd = open(filename, O_CREAT | O_WRONLY, 0644)) != -1, return -1);
 | 
			
		||||
	close(fd);
 | 
			
		||||
 | 
			
		||||
	ASSERT((pw = getpwnam(user)) != NULL, return -1);
 | 
			
		||||
	ASSERT(chown(filename, pw->pw_uid, pw->pw_gid) != -1, return -1);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int intialize_download_file(struct dload_payload *p)
 | 
			
		||||
{
 | 
			
		||||
	int ret = 0;
 | 
			
		||||
 | 
			
		||||
	ret = touch_and_own_file(p->tempfile_name, p->handle->sandboxuser);
 | 
			
		||||
 | 
			
		||||
	if(ret == 0 && p->download_signature) {
 | 
			
		||||
		int len = strlen(p->destfile_name) + strlen(".sig") + strlen(".part") + 1;
 | 
			
		||||
		char * sig;
 | 
			
		||||
		MALLOC(sig, len, FREE(sig); RET_ERR(p->handle, ALPM_ERR_MEMORY, -1));
 | 
			
		||||
		snprintf(sig, len, "%s.sig.part", p->destfile_name);
 | 
			
		||||
		ret = touch_and_own_file(sig, p->handle->sandboxuser);
 | 
			
		||||
		free(sig);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int unlink_or_move(const char *file, const char* dest) {
 | 
			
		||||
	struct stat st;
 | 
			
		||||
 | 
			
		||||
	ASSERT(stat(file, &st) == 0, return -1);
 | 
			
		||||
 | 
			
		||||
	if(st.st_size == 0) {
 | 
			
		||||
		unlink(file);
 | 
			
		||||
	} else {
 | 
			
		||||
		ASSERT(chown(file, 0, 0) != -1, return -1);
 | 
			
		||||
		if(rename(file, dest)) {
 | 
			
		||||
				return -1;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int finalize_download_file(struct dload_payload *p)
 | 
			
		||||
{
 | 
			
		||||
	int ret = 0;
 | 
			
		||||
 | 
			
		||||
	ret = unlink_or_move(p->tempfile_name, p->destfile_name);
 | 
			
		||||
 | 
			
		||||
	if(ret == -1) {
 | 
			
		||||
		_alpm_log(p->handle, ALPM_LOG_ERROR, _("could not rename %s to %s (%s)\n"),
 | 
			
		||||
			p->tempfile_name, p->destfile_name, strerror(errno));
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if(p->download_signature) {
 | 
			
		||||
		char *sig_temp, *sig_dest;
 | 
			
		||||
		int len = strlen(p->destfile_name) + strlen(".sig") + 1;
 | 
			
		||||
		MALLOC(sig_dest, len, RET_ERR(p->handle, ALPM_ERR_MEMORY, -1));
 | 
			
		||||
		snprintf(sig_dest, len, "%s.sig", p->destfile_name);
 | 
			
		||||
		len = len + 5;
 | 
			
		||||
		MALLOC(sig_temp, len, FREE(sig_dest); RET_ERR(p->handle, ALPM_ERR_MEMORY, -1));
 | 
			
		||||
		snprintf(sig_temp, len, "%s.sig.part", p->destfile_name);
 | 
			
		||||
 | 
			
		||||
		ret = unlink_or_move(sig_temp, sig_dest);
 | 
			
		||||
		if(ret == -1) {
 | 
			
		||||
			_alpm_log(p->handle, ALPM_LOG_ERROR, _("could not rename %s to %s (%s)\n"),
 | 
			
		||||
				sig_temp, sig_dest, strerror(errno));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		free(sig_dest);
 | 
			
		||||
		free(sig_temp);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static FILE *create_tempfile(struct dload_payload *payload, const char *localpath)
 | 
			
		||||
{
 | 
			
		||||
	int fd;
 | 
			
		||||
	FILE *fp;
 | 
			
		||||
	char *randpath;
 | 
			
		||||
	size_t len;
 | 
			
		||||
 | 
			
		||||
	/* create a random filename, which is opened with O_EXCL */
 | 
			
		||||
	len = strlen(localpath) + 14 + 1;
 | 
			
		||||
	MALLOC(randpath, len, RET_ERR(payload->handle, ALPM_ERR_MEMORY, NULL));
 | 
			
		||||
	snprintf(randpath, len, "%salpmtmp.XXXXXX", localpath);
 | 
			
		||||
	if((fd = mkstemp(randpath)) == -1 ||
 | 
			
		||||
			fchmod(fd, ~(_getumask()) & 0666) ||
 | 
			
		||||
			!(fp = fdopen(fd, payload->tempfile_openmode))) {
 | 
			
		||||
		unlink(randpath);
 | 
			
		||||
		close(fd);
 | 
			
		||||
		_alpm_log(payload->handle, ALPM_LOG_ERROR,
 | 
			
		||||
				_("failed to create temporary file for download\n"));
 | 
			
		||||
		free(randpath);
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
	/* fp now points to our alpmtmp.XXXXXX */
 | 
			
		||||
	free(payload->tempfile_name);
 | 
			
		||||
	payload->tempfile_name = randpath;
 | 
			
		||||
	free(payload->remote_name);
 | 
			
		||||
	STRDUP(payload->remote_name, strrchr(randpath, '/') + 1,
 | 
			
		||||
			fclose(fp); RET_ERR(payload->handle, ALPM_ERR_MEMORY, NULL));
 | 
			
		||||
 | 
			
		||||
	return fp;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#ifdef HAVE_LIBCURL
 | 
			
		||||
 | 
			
		||||
@@ -55,7 +196,7 @@
 | 
			
		||||
#define HOSTNAME_SIZE 256
 | 
			
		||||
 | 
			
		||||
static int curl_add_payload(alpm_handle_t *handle, CURLM *curlm,
 | 
			
		||||
	struct dload_payload *payload, const char *localpath);
 | 
			
		||||
	struct dload_payload *payload);
 | 
			
		||||
static int curl_gethost(const char *url, char *buffer, size_t buf_len);
 | 
			
		||||
 | 
			
		||||
/* number of "soft" errors required to blacklist a server, set to 0 to disable
 | 
			
		||||
@@ -173,29 +314,6 @@ static const char *payload_next_server(struct dload_payload *payload)
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const char *get_filename(const char *url)
 | 
			
		||||
{
 | 
			
		||||
	char *filename = strrchr(url, '/');
 | 
			
		||||
	if(filename != NULL) {
 | 
			
		||||
		return filename + 1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* no slash found, it's a filename */
 | 
			
		||||
	return url;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static char *get_fullpath(const char *path, const char *filename,
 | 
			
		||||
		const char *suffix)
 | 
			
		||||
{
 | 
			
		||||
	char *filepath;
 | 
			
		||||
	/* len = localpath len + filename len + suffix len + null */
 | 
			
		||||
	size_t len = strlen(path) + strlen(filename) + strlen(suffix) + 1;
 | 
			
		||||
	MALLOC(filepath, len, return NULL);
 | 
			
		||||
	snprintf(filepath, len, "%s%s%s", path, filename, suffix);
 | 
			
		||||
 | 
			
		||||
	return filepath;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
enum {
 | 
			
		||||
	ABORT_OVER_MAXFILESIZE = 1,
 | 
			
		||||
};
 | 
			
		||||
@@ -306,45 +424,12 @@ static int utimes_long(const char *path, long seconds)
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* prefix to avoid possible future clash with getumask(3) */
 | 
			
		||||
static mode_t _getumask(void)
 | 
			
		||||
{
 | 
			
		||||
	mode_t mask = umask(0);
 | 
			
		||||
	umask(mask);
 | 
			
		||||
	return mask;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static size_t dload_parseheader_cb(void *ptr, size_t size, size_t nmemb, void *user)
 | 
			
		||||
{
 | 
			
		||||
	size_t realsize = size * nmemb;
 | 
			
		||||
	const char *fptr, *endptr = NULL;
 | 
			
		||||
	const char * const cd_header = "Content-Disposition:";
 | 
			
		||||
	const char * const fn_key = "filename=";
 | 
			
		||||
	struct dload_payload *payload = (struct dload_payload *)user;
 | 
			
		||||
	long respcode;
 | 
			
		||||
 | 
			
		||||
	if(_alpm_raw_ncmp(cd_header, ptr, strlen(cd_header)) == 0) {
 | 
			
		||||
		if((fptr = strstr(ptr, fn_key))) {
 | 
			
		||||
			fptr += strlen(fn_key);
 | 
			
		||||
 | 
			
		||||
			/* find the end of the field, which is either a semi-colon, or the end of
 | 
			
		||||
			 * the data. As per curl_easy_setopt(3), we cannot count on headers being
 | 
			
		||||
			 * null terminated, so we look for the closing \r\n */
 | 
			
		||||
			endptr = fptr + strcspn(fptr, ";\r\n") - 1;
 | 
			
		||||
 | 
			
		||||
			/* remove quotes */
 | 
			
		||||
			if(*fptr == '"' && *endptr == '"') {
 | 
			
		||||
				fptr++;
 | 
			
		||||
				endptr--;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			/* avoid information leakage with badly formed headers */
 | 
			
		||||
			if(endptr > fptr) {
 | 
			
		||||
				STRNDUP(payload->content_disp_name, fptr, endptr - fptr + 1,
 | 
			
		||||
						RET_ERR(payload->handle, ALPM_ERR_MEMORY, realsize));
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	(void) ptr;
 | 
			
		||||
 | 
			
		||||
	curl_easy_getinfo(payload->curl, CURLINFO_RESPONSE_CODE, &respcode);
 | 
			
		||||
	if(payload->respcode != respcode) {
 | 
			
		||||
@@ -418,37 +503,6 @@ static void curl_set_handle_opts(CURL *curl, struct dload_payload *payload)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static FILE *create_tempfile(struct dload_payload *payload, const char *localpath)
 | 
			
		||||
{
 | 
			
		||||
	int fd;
 | 
			
		||||
	FILE *fp;
 | 
			
		||||
	char *randpath;
 | 
			
		||||
	size_t len;
 | 
			
		||||
 | 
			
		||||
	/* create a random filename, which is opened with O_EXCL */
 | 
			
		||||
	len = strlen(localpath) + 14 + 1;
 | 
			
		||||
	MALLOC(randpath, len, RET_ERR(payload->handle, ALPM_ERR_MEMORY, NULL));
 | 
			
		||||
	snprintf(randpath, len, "%salpmtmp.XXXXXX", localpath);
 | 
			
		||||
	if((fd = mkstemp(randpath)) == -1 ||
 | 
			
		||||
			fchmod(fd, ~(_getumask()) & 0666) ||
 | 
			
		||||
			!(fp = fdopen(fd, payload->tempfile_openmode))) {
 | 
			
		||||
		unlink(randpath);
 | 
			
		||||
		close(fd);
 | 
			
		||||
		_alpm_log(payload->handle, ALPM_LOG_ERROR,
 | 
			
		||||
				_("failed to create temporary file for download\n"));
 | 
			
		||||
		free(randpath);
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
	/* fp now points to our alpmtmp.XXXXXX */
 | 
			
		||||
	free(payload->tempfile_name);
 | 
			
		||||
	payload->tempfile_name = randpath;
 | 
			
		||||
	free(payload->remote_name);
 | 
			
		||||
	STRDUP(payload->remote_name, strrchr(randpath, '/') + 1,
 | 
			
		||||
			fclose(fp); RET_ERR(payload->handle, ALPM_ERR_MEMORY, NULL));
 | 
			
		||||
 | 
			
		||||
	return fp;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Return 0 if retry was successful, -1 otherwise */
 | 
			
		||||
static int curl_retry_next_server(CURLM *curlm, CURL *curl, struct dload_payload *payload)
 | 
			
		||||
{
 | 
			
		||||
@@ -512,7 +566,7 @@ static int curl_retry_next_server(CURLM *curlm, CURL *curl, struct dload_payload
 | 
			
		||||
 * Returns -2 if an error happened for an optional file
 | 
			
		||||
 */
 | 
			
		||||
static int curl_check_finished_download(alpm_handle_t *handle, CURLM *curlm, CURLMsg *msg,
 | 
			
		||||
		const char *localpath, int *active_downloads_num)
 | 
			
		||||
		int *active_downloads_num)
 | 
			
		||||
{
 | 
			
		||||
	struct dload_payload *payload = NULL;
 | 
			
		||||
	CURL *curl = msg->easy_handle;
 | 
			
		||||
@@ -626,31 +680,6 @@ static int curl_check_finished_download(alpm_handle_t *handle, CURLM *curlm, CUR
 | 
			
		||||
	curl_easy_getinfo(curl, CURLINFO_CONDITION_UNMET, &timecond);
 | 
			
		||||
	curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &effective_url);
 | 
			
		||||
 | 
			
		||||
	if(payload->trust_remote_name) {
 | 
			
		||||
		if(payload->content_disp_name) {
 | 
			
		||||
			/* content-disposition header has a better name for our file */
 | 
			
		||||
			free(payload->destfile_name);
 | 
			
		||||
			payload->destfile_name = get_fullpath(localpath,
 | 
			
		||||
				get_filename(payload->content_disp_name), "");
 | 
			
		||||
		} else {
 | 
			
		||||
			const char *effective_filename = strrchr(effective_url, '/');
 | 
			
		||||
 | 
			
		||||
			if(effective_filename && strlen(effective_filename) > 2) {
 | 
			
		||||
				effective_filename++;
 | 
			
		||||
 | 
			
		||||
				/* if destfile was never set, we wrote to a tempfile. even if destfile is
 | 
			
		||||
				 * set, we may have followed some redirects and the effective url may
 | 
			
		||||
				 * have a better suggestion as to what to name our file. in either case,
 | 
			
		||||
				 * refactor destfile to this newly derived name. */
 | 
			
		||||
				if(!payload->destfile_name || strcmp(effective_filename,
 | 
			
		||||
							strrchr(payload->destfile_name, '/') + 1) != 0) {
 | 
			
		||||
					free(payload->destfile_name);
 | 
			
		||||
					payload->destfile_name = get_fullpath(localpath, effective_filename, "");
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Let's check if client requested downloading accompanion *.sig file */
 | 
			
		||||
	if(!payload->signature && payload->download_signature && curlerr == CURLE_OK && payload->respcode < 400) {
 | 
			
		||||
		struct dload_payload *sig = NULL;
 | 
			
		||||
@@ -682,22 +711,23 @@ static int curl_check_finished_download(alpm_handle_t *handle, CURLM *curlm, CUR
 | 
			
		||||
		MALLOC(sig->fileurl, len, FREE(sig); GOTO_ERR(handle, ALPM_ERR_MEMORY, cleanup));
 | 
			
		||||
		snprintf(sig->fileurl, len, "%s.sig", url);
 | 
			
		||||
 | 
			
		||||
		if(payload->trust_remote_name) {
 | 
			
		||||
			/* In this case server might provide a new name for the main payload.
 | 
			
		||||
			 * Choose *.sig filename based on this new name.
 | 
			
		||||
			 */
 | 
			
		||||
			const char *final_file = get_filename(realname);
 | 
			
		||||
			int remote_name_len = strlen(final_file) + 5;
 | 
			
		||||
			MALLOC(sig->remote_name, remote_name_len, FREE(sig->fileurl); FREE(sig); GOTO_ERR(handle, ALPM_ERR_MEMORY, cleanup));
 | 
			
		||||
			snprintf(sig->remote_name, remote_name_len, "%s.sig", final_file);
 | 
			
		||||
		}
 | 
			
		||||
		int remote_name_len = strlen(payload->remote_name) + 5;
 | 
			
		||||
		MALLOC(sig->remote_name, remote_name_len, _alpm_dload_payload_reset(sig);
 | 
			
		||||
			FREE(sig); GOTO_ERR(handle, ALPM_ERR_MEMORY, cleanup));
 | 
			
		||||
		snprintf(sig->remote_name, remote_name_len, "%s.sig", payload->remote_name);
 | 
			
		||||
 | 
			
		||||
		/* force the filename to be realname + ".sig" */
 | 
			
		||||
		int destfile_name_len = strlen(realname) + 5;
 | 
			
		||||
		MALLOC(sig->destfile_name, destfile_name_len, FREE(sig->remote_name);
 | 
			
		||||
				FREE(sig->fileurl); FREE(sig); GOTO_ERR(handle, ALPM_ERR_MEMORY, cleanup));
 | 
			
		||||
		MALLOC(sig->destfile_name, destfile_name_len, _alpm_dload_payload_reset(sig);
 | 
			
		||||
				FREE(sig); GOTO_ERR(handle, ALPM_ERR_MEMORY, cleanup));
 | 
			
		||||
		snprintf(sig->destfile_name, destfile_name_len, "%s.sig", realname);
 | 
			
		||||
 | 
			
		||||
		int tempfile_name_len = strlen(realname) + 10;
 | 
			
		||||
		MALLOC(sig->tempfile_name, tempfile_name_len, _alpm_dload_payload_reset(sig);
 | 
			
		||||
				FREE(sig); GOTO_ERR(handle, ALPM_ERR_MEMORY, cleanup));
 | 
			
		||||
		snprintf(sig->tempfile_name, tempfile_name_len, "%s.sig.part", realname);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		sig->signature = 1;
 | 
			
		||||
		sig->handle = handle;
 | 
			
		||||
		sig->force = payload->force;
 | 
			
		||||
@@ -706,7 +736,7 @@ static int curl_check_finished_download(alpm_handle_t *handle, CURLM *curlm, CUR
 | 
			
		||||
		/* set hard upper limit of 16KiB */
 | 
			
		||||
		sig->max_size = 16 * 1024;
 | 
			
		||||
 | 
			
		||||
		curl_add_payload(handle, curlm, sig, localpath);
 | 
			
		||||
		curl_add_payload(handle, curlm, sig);
 | 
			
		||||
		(*active_downloads_num)++;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -745,7 +775,7 @@ cleanup:
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if(ret == 0) {
 | 
			
		||||
		if(payload->destfile_name) {
 | 
			
		||||
		if(payload->destfile_name && handle->sandboxuser == NULL) {
 | 
			
		||||
			if(rename(payload->tempfile_name, payload->destfile_name)) {
 | 
			
		||||
				_alpm_log(handle, ALPM_LOG_ERROR, _("could not rename %s to %s (%s)\n"),
 | 
			
		||||
						payload->tempfile_name, payload->destfile_name, strerror(errno));
 | 
			
		||||
@@ -789,7 +819,7 @@ cleanup:
 | 
			
		||||
 * Returns -1 if am error happened while starting a new download
 | 
			
		||||
 */
 | 
			
		||||
static int curl_add_payload(alpm_handle_t *handle, CURLM *curlm,
 | 
			
		||||
		struct dload_payload *payload, const char *localpath)
 | 
			
		||||
		struct dload_payload *payload)
 | 
			
		||||
{
 | 
			
		||||
	size_t len;
 | 
			
		||||
	CURL *curl = NULL;
 | 
			
		||||
@@ -815,35 +845,11 @@ static int curl_add_payload(alpm_handle_t *handle, CURLM *curlm,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	payload->tempfile_openmode = "wb";
 | 
			
		||||
	if(!payload->remote_name) {
 | 
			
		||||
		STRDUP(payload->remote_name, get_filename(payload->fileurl),
 | 
			
		||||
			GOTO_ERR(handle, ALPM_ERR_MEMORY, cleanup));
 | 
			
		||||
	}
 | 
			
		||||
	if(curl_gethost(payload->fileurl, hostname, sizeof(hostname)) != 0) {
 | 
			
		||||
		_alpm_log(handle, ALPM_LOG_ERROR, _("url '%s' is invalid\n"), payload->fileurl);
 | 
			
		||||
		GOTO_ERR(handle, ALPM_ERR_SERVER_BAD_URL, cleanup);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if(!payload->random_partfile && payload->remote_name && strlen(payload->remote_name) > 0) {
 | 
			
		||||
		if(!payload->destfile_name) {
 | 
			
		||||
			payload->destfile_name = get_fullpath(localpath, payload->remote_name, "");
 | 
			
		||||
		}
 | 
			
		||||
		payload->tempfile_name = get_fullpath(localpath, payload->remote_name, ".part");
 | 
			
		||||
		if(!payload->destfile_name || !payload->tempfile_name) {
 | 
			
		||||
			goto cleanup;
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		/* We want a random filename or the URL does not contain a filename, so download to a
 | 
			
		||||
		 * temporary location. We can not support resuming this kind of download; any partial
 | 
			
		||||
		 * transfers will be destroyed */
 | 
			
		||||
		payload->unlink_on_fail = 1;
 | 
			
		||||
 | 
			
		||||
		payload->localf = create_tempfile(payload, localpath);
 | 
			
		||||
		if(payload->localf == NULL) {
 | 
			
		||||
			goto cleanup;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	curl_set_handle_opts(curl, payload);
 | 
			
		||||
 | 
			
		||||
	if(payload->max_size == payload->initial_size && payload->max_size != 0) {
 | 
			
		||||
@@ -901,8 +907,7 @@ static int compare_dload_payload_sizes(const void *left_ptr, const void *right_p
 | 
			
		||||
 * Returns 1 if no files were downloaded and all errors were non-fatal
 | 
			
		||||
 */
 | 
			
		||||
static int curl_download_internal(alpm_handle_t *handle,
 | 
			
		||||
		alpm_list_t *payloads /* struct dload_payload */,
 | 
			
		||||
		const char *localpath)
 | 
			
		||||
		alpm_list_t *payloads /* struct dload_payload */)
 | 
			
		||||
{
 | 
			
		||||
	int active_downloads_num = 0;
 | 
			
		||||
	int err = 0;
 | 
			
		||||
@@ -920,7 +925,7 @@ static int curl_download_internal(alpm_handle_t *handle,
 | 
			
		||||
		for(; active_downloads_num < max_streams && payloads; active_downloads_num++) {
 | 
			
		||||
			struct dload_payload *payload = payloads->data;
 | 
			
		||||
 | 
			
		||||
			if(curl_add_payload(handle, curlm, payload, localpath) == 0) {
 | 
			
		||||
			if(curl_add_payload(handle, curlm, payload) == 0) {
 | 
			
		||||
				payloads = payloads->next;
 | 
			
		||||
			} else {
 | 
			
		||||
				/* The payload failed to start. Do not start any new downloads.
 | 
			
		||||
@@ -951,7 +956,7 @@ static int curl_download_internal(alpm_handle_t *handle,
 | 
			
		||||
			}
 | 
			
		||||
			if(msg->msg == CURLMSG_DONE) {
 | 
			
		||||
				int ret = curl_check_finished_download(handle, curlm, msg,
 | 
			
		||||
						localpath, &active_downloads_num);
 | 
			
		||||
						&active_downloads_num);
 | 
			
		||||
				if(ret == -1) {
 | 
			
		||||
					/* if current payload failed to download then stop adding new payloads but wait for the
 | 
			
		||||
					 * current ones
 | 
			
		||||
@@ -967,9 +972,160 @@ static int curl_download_internal(alpm_handle_t *handle,
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	int ret = err ? -1 : updated ? 0 : 1;
 | 
			
		||||
	_alpm_log(handle, ALPM_LOG_DEBUG, "curl_download_internal return code is %d\n", ret);
 | 
			
		||||
	return ret;
 | 
			
		||||
	_alpm_log(handle, ALPM_LOG_DEBUG, "curl_download_internal return code is %d\n", err);
 | 
			
		||||
	return err ? -1 : updated ? 0 : 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Download the requested files by launching a process inside a sandbox.
 | 
			
		||||
 * Returns -1 if an error happened for a required file
 | 
			
		||||
 * Returns 0 if a payload was actually downloaded
 | 
			
		||||
 * Returns 1 if no files were downloaded and all errors were non-fatal
 | 
			
		||||
 */
 | 
			
		||||
static int curl_download_internal_sandboxed(alpm_handle_t *handle,
 | 
			
		||||
		alpm_list_t *payloads /* struct dload_payload */,
 | 
			
		||||
		const char *localpath)
 | 
			
		||||
{
 | 
			
		||||
	int pid, err = 0, ret = -1, callbacks_fd[2];
 | 
			
		||||
	sigset_t oldblock;
 | 
			
		||||
	struct sigaction sa_ign, oldint, oldquit;
 | 
			
		||||
	_alpm_sandbox_callback_context callbacks_ctx;
 | 
			
		||||
 | 
			
		||||
	sigemptyset(&sa_ign.sa_mask);
 | 
			
		||||
	sa_ign.sa_handler = SIG_IGN;
 | 
			
		||||
	sa_ign.sa_flags=0;
 | 
			
		||||
 | 
			
		||||
	if(pipe(callbacks_fd) != 0) {
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	sigaction(SIGINT, &sa_ign, &oldint);
 | 
			
		||||
	sigaction(SIGQUIT, &sa_ign, &oldquit);
 | 
			
		||||
	sigaddset(&sa_ign.sa_mask, SIGCHLD);
 | 
			
		||||
	sigprocmask(SIG_BLOCK, &sa_ign.sa_mask, &oldblock);
 | 
			
		||||
 | 
			
		||||
	pid = fork();
 | 
			
		||||
	if(pid == -1) {
 | 
			
		||||
		/* fork failed, make sure errno is preserved after cleanup */
 | 
			
		||||
		err = errno;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* child */
 | 
			
		||||
	if(pid == 0) {
 | 
			
		||||
		close(callbacks_fd[0]);
 | 
			
		||||
		fcntl(callbacks_fd[1], F_SETFD, FD_CLOEXEC);
 | 
			
		||||
		callbacks_ctx.callback_pipe = callbacks_fd[1];
 | 
			
		||||
		alpm_option_set_logcb(handle, _alpm_sandbox_cb_log, &callbacks_ctx);
 | 
			
		||||
		alpm_option_set_dlcb(handle, _alpm_sandbox_cb_dl, &callbacks_ctx);
 | 
			
		||||
		alpm_option_set_fetchcb(handle, NULL, NULL);
 | 
			
		||||
		alpm_option_set_eventcb(handle, NULL, NULL);
 | 
			
		||||
		alpm_option_set_questioncb(handle, NULL, NULL);
 | 
			
		||||
		alpm_option_set_progresscb(handle, NULL, NULL);
 | 
			
		||||
 | 
			
		||||
		/* restore default signal handling in the child */
 | 
			
		||||
		_alpm_reset_signals();
 | 
			
		||||
 | 
			
		||||
		/* cwd to the download directory */
 | 
			
		||||
		ret = chdir(localpath);
 | 
			
		||||
		if(ret != 0) {
 | 
			
		||||
			handle->pm_errno = errno;
 | 
			
		||||
			_alpm_log(handle, ALPM_LOG_ERROR, _("could not chdir to download directory %s\n"), localpath);
 | 
			
		||||
			ret = -1;
 | 
			
		||||
		} else {
 | 
			
		||||
			ret = alpm_sandbox_setup_child(handle->sandboxuser);
 | 
			
		||||
			if (ret != 0) {
 | 
			
		||||
				_alpm_log(handle, ALPM_LOG_ERROR, _("switching to sandbox user '%s' failed!\n"), handle->sandboxuser);
 | 
			
		||||
				_Exit(ret | 128);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			ret = curl_download_internal(handle, payloads);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/* pass the result back to the parent */
 | 
			
		||||
		if(ret == 0) {
 | 
			
		||||
			/* a payload was actually downloaded */
 | 
			
		||||
			_Exit(0);
 | 
			
		||||
		}
 | 
			
		||||
		else if(ret == 1) {
 | 
			
		||||
			/* no files were downloaded and all errors were non-fatal */
 | 
			
		||||
			_Exit(handle->pm_errno);
 | 
			
		||||
		}
 | 
			
		||||
		else {
 | 
			
		||||
			/* an error happened for a required file */
 | 
			
		||||
			_Exit(handle->pm_errno | 128);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* parent */
 | 
			
		||||
	close(callbacks_fd[1]);
 | 
			
		||||
 | 
			
		||||
	if(pid != -1)  {
 | 
			
		||||
		bool had_error = false;
 | 
			
		||||
		while(true) {
 | 
			
		||||
			_alpm_sandbox_callback_t callback_type;
 | 
			
		||||
			ssize_t got = read(callbacks_fd[0], &callback_type, sizeof(callback_type));
 | 
			
		||||
			if(got < 0 || (size_t)got != sizeof(callback_type)) {
 | 
			
		||||
				had_error = true;
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if(callback_type == ALPM_SANDBOX_CB_DOWNLOAD) {
 | 
			
		||||
				if(!_alpm_sandbox_process_cb_download(handle, callbacks_fd[0])) {
 | 
			
		||||
					had_error = true;
 | 
			
		||||
					break;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			else if(callback_type == ALPM_SANDBOX_CB_LOG) {
 | 
			
		||||
				if(!_alpm_sandbox_process_cb_log(handle, callbacks_fd[0])) {
 | 
			
		||||
					had_error = true;
 | 
			
		||||
					break;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		if(had_error) {
 | 
			
		||||
			kill(pid, SIGTERM);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		int wret;
 | 
			
		||||
		while((wret = waitpid(pid, &ret, 0)) == -1 && errno == EINTR);
 | 
			
		||||
		if(wret > 0) {
 | 
			
		||||
			if(!WIFEXITED(ret)) {
 | 
			
		||||
				/* the child did not terminate normally */
 | 
			
		||||
				ret = -1;
 | 
			
		||||
			}
 | 
			
		||||
			else {
 | 
			
		||||
				ret = WEXITSTATUS(ret);
 | 
			
		||||
				if(ret != 0) {
 | 
			
		||||
					if(ret & 128) {
 | 
			
		||||
						/* an error happened for a required file, or unexpected exit status */
 | 
			
		||||
						handle->pm_errno = ret & ~128;
 | 
			
		||||
						ret = -1;
 | 
			
		||||
					}
 | 
			
		||||
					else {
 | 
			
		||||
						handle->pm_errno = ret;
 | 
			
		||||
						ret = 1;
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		else {
 | 
			
		||||
			/* waitpid failed */
 | 
			
		||||
			err = errno;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	close(callbacks_fd[0]);
 | 
			
		||||
 | 
			
		||||
	sigaction(SIGINT, &oldint, NULL);
 | 
			
		||||
	sigaction(SIGQUIT, &oldquit, NULL);
 | 
			
		||||
	sigprocmask(SIG_SETMASK, &oldblock, NULL);
 | 
			
		||||
 | 
			
		||||
	if(err) {
 | 
			
		||||
		errno = err;
 | 
			
		||||
		ret = -1;
 | 
			
		||||
	}
 | 
			
		||||
  return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
@@ -1001,7 +1157,26 @@ int _alpm_download(alpm_handle_t *handle,
 | 
			
		||||
{
 | 
			
		||||
	if(handle->fetchcb == NULL) {
 | 
			
		||||
#ifdef HAVE_LIBCURL
 | 
			
		||||
		return curl_download_internal(handle, payloads, localpath);
 | 
			
		||||
		if(handle->sandboxuser) {
 | 
			
		||||
			int ret = 0;
 | 
			
		||||
			alpm_list_t *i;
 | 
			
		||||
 | 
			
		||||
			for(i = payloads; i; i = i->next) {
 | 
			
		||||
				struct dload_payload *p = i->data;
 | 
			
		||||
				intialize_download_file(p);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			ret = curl_download_internal_sandboxed(handle, payloads, localpath);
 | 
			
		||||
 | 
			
		||||
			for(i = payloads; i; i = i->next) {
 | 
			
		||||
				struct dload_payload *p = i->data;
 | 
			
		||||
				finalize_download_file(p);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return ret;
 | 
			
		||||
		} else {
 | 
			
		||||
			return curl_download_internal(handle, payloads);
 | 
			
		||||
		}
 | 
			
		||||
#else
 | 
			
		||||
		RET_ERR(handle, ALPM_ERR_EXTERNAL_DOWNLOAD, -1);
 | 
			
		||||
#endif
 | 
			
		||||
@@ -1080,16 +1255,34 @@ int SYMEXPORT alpm_fetch_pkgurl(alpm_handle_t *handle, const alpm_list_t *urls,
 | 
			
		||||
			CALLOC(payload, 1, sizeof(*payload), GOTO_ERR(handle, ALPM_ERR_MEMORY, err));
 | 
			
		||||
			STRDUP(payload->fileurl, url, FREE(payload); GOTO_ERR(handle, ALPM_ERR_MEMORY, err));
 | 
			
		||||
 | 
			
		||||
			STRDUP(payload->remote_name, get_filename(payload->fileurl),
 | 
			
		||||
				GOTO_ERR(handle, ALPM_ERR_MEMORY, err));
 | 
			
		||||
 | 
			
		||||
			c = strrchr(url, '/');
 | 
			
		||||
			if(strstr(c, ".pkg")) {
 | 
			
		||||
			if(payload->remote_name && strlen(payload->remote_name) > 0 && strstr(c, ".pkg")) {
 | 
			
		||||
				/* we probably have a usable package filename to download to */
 | 
			
		||||
				payload->destfile_name = _alpm_get_fullpath(cachedir, payload->remote_name, "");
 | 
			
		||||
				payload->tempfile_name = _alpm_get_fullpath(cachedir, payload->remote_name, ".part");
 | 
			
		||||
				payload->allow_resume = 1;
 | 
			
		||||
 | 
			
		||||
				if(!payload->destfile_name || !payload->tempfile_name) {
 | 
			
		||||
					goto err;
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
			} else {
 | 
			
		||||
				payload->random_partfile = 1;
 | 
			
		||||
				/* The URL does not contain a filename, so download to a temporary location.
 | 
			
		||||
				 * We can not support resuming this kind of download; any partial transfers
 | 
			
		||||
				 * will be destroyed */
 | 
			
		||||
				payload->unlink_on_fail = 1;
 | 
			
		||||
 | 
			
		||||
				payload->tempfile_openmode = "wb";
 | 
			
		||||
				payload->localf = create_tempfile(payload, cachedir);
 | 
			
		||||
				if(payload->localf == NULL) {
 | 
			
		||||
					goto err;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			payload->handle = handle;
 | 
			
		||||
			payload->trust_remote_name = 1;
 | 
			
		||||
			payload->download_signature = (handle->siglevel & ALPM_SIG_PACKAGE);
 | 
			
		||||
			payload->signature_optional = (handle->siglevel & ALPM_SIG_PACKAGE_OPTIONAL);
 | 
			
		||||
			payloads = alpm_list_add(payloads, payload);
 | 
			
		||||
@@ -1150,7 +1343,6 @@ void _alpm_dload_payload_reset(struct dload_payload *payload)
 | 
			
		||||
	FREE(payload->remote_name);
 | 
			
		||||
	FREE(payload->tempfile_name);
 | 
			
		||||
	FREE(payload->destfile_name);
 | 
			
		||||
	FREE(payload->content_disp_name);
 | 
			
		||||
	FREE(payload->fileurl);
 | 
			
		||||
	FREE(payload->filepath);
 | 
			
		||||
	*payload = (struct dload_payload){0};
 | 
			
		||||
 
 | 
			
		||||
@@ -26,10 +26,12 @@
 | 
			
		||||
struct dload_payload {
 | 
			
		||||
	alpm_handle_t *handle;
 | 
			
		||||
	const char *tempfile_openmode;
 | 
			
		||||
	/* name of the remote file */
 | 
			
		||||
	char *remote_name;
 | 
			
		||||
	/* temporary file name, to which the payload is downloaded */
 | 
			
		||||
	char *tempfile_name;
 | 
			
		||||
	/* name to which the downloaded file will be renamed */
 | 
			
		||||
	char *destfile_name;
 | 
			
		||||
	char *content_disp_name;
 | 
			
		||||
	/* client has to provide either
 | 
			
		||||
	 *  1) fileurl - full URL to the file
 | 
			
		||||
	 *  2) pair of (servers, filepath), in this case ALPM iterates over the
 | 
			
		||||
@@ -45,19 +47,17 @@ struct dload_payload {
 | 
			
		||||
	off_t prevprogress;
 | 
			
		||||
	int force;
 | 
			
		||||
	int allow_resume;
 | 
			
		||||
	int random_partfile;
 | 
			
		||||
	int errors_ok;
 | 
			
		||||
	int unlink_on_fail;
 | 
			
		||||
	int trust_remote_name;
 | 
			
		||||
	int download_signature; /* specifies if an accompanion *.sig file need to be downloaded*/
 | 
			
		||||
	int signature_optional; /* *.sig file is optional */
 | 
			
		||||
#ifdef HAVE_LIBCURL
 | 
			
		||||
	CURL *curl;
 | 
			
		||||
	char error_buffer[CURL_ERROR_SIZE];
 | 
			
		||||
	FILE *localf; /* temp download file */
 | 
			
		||||
	int signature; /* specifies if this payload is for a signature file */
 | 
			
		||||
	int request_errors_ok; /* per-request errors-ok */
 | 
			
		||||
#endif
 | 
			
		||||
	FILE *localf; /* temp download file */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void _alpm_dload_payload_reset(struct dload_payload *payload);
 | 
			
		||||
 
 | 
			
		||||
@@ -101,6 +101,7 @@ void _alpm_handle_free(alpm_handle_t *handle)
 | 
			
		||||
	FREE(handle->lockfile);
 | 
			
		||||
	FREELIST(handle->architectures);
 | 
			
		||||
	FREE(handle->gpgdir);
 | 
			
		||||
	FREE(handle->sandboxuser);
 | 
			
		||||
	FREELIST(handle->noupgrade);
 | 
			
		||||
	FREELIST(handle->noextract);
 | 
			
		||||
	FREELIST(handle->ignorepkg);
 | 
			
		||||
@@ -292,6 +293,12 @@ const char SYMEXPORT *alpm_option_get_gpgdir(alpm_handle_t *handle)
 | 
			
		||||
	return handle->gpgdir;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char SYMEXPORT *alpm_option_get_sandboxuser(alpm_handle_t *handle)
 | 
			
		||||
{
 | 
			
		||||
	CHECK_HANDLE(handle, return NULL);
 | 
			
		||||
	return handle->sandboxuser;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int SYMEXPORT alpm_option_get_usesyslog(alpm_handle_t *handle)
 | 
			
		||||
{
 | 
			
		||||
	CHECK_HANDLE(handle, return -1);
 | 
			
		||||
@@ -595,6 +602,19 @@ int SYMEXPORT alpm_option_set_gpgdir(alpm_handle_t *handle, const char *gpgdir)
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int SYMEXPORT alpm_option_set_sandboxuser(alpm_handle_t *handle, const char *sandboxuser)
 | 
			
		||||
{
 | 
			
		||||
	CHECK_HANDLE(handle, return -1);
 | 
			
		||||
	if(handle->sandboxuser) {
 | 
			
		||||
		FREE(handle->sandboxuser);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	STRDUP(handle->sandboxuser, sandboxuser, RET_ERR(handle, ALPM_ERR_MEMORY, -1));
 | 
			
		||||
 | 
			
		||||
	_alpm_log(handle, ALPM_LOG_DEBUG, "option 'sandboxuser' = %s\n", handle->sandboxuser);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int SYMEXPORT alpm_option_set_usesyslog(alpm_handle_t *handle, int usesyslog)
 | 
			
		||||
{
 | 
			
		||||
	CHECK_HANDLE(handle, return -1);
 | 
			
		||||
 
 | 
			
		||||
@@ -91,6 +91,7 @@ struct _alpm_handle_t {
 | 
			
		||||
	char *logfile;           /* Name of the log file */
 | 
			
		||||
	char *lockfile;          /* Name of the lock file */
 | 
			
		||||
	char *gpgdir;            /* Directory where GnuPG files are stored */
 | 
			
		||||
	char *sandboxuser;       /* User to switch to for sensitive operations */
 | 
			
		||||
	alpm_list_t *cachedirs;  /* Paths to pacman cache directories */
 | 
			
		||||
	alpm_list_t *hookdirs;   /* Paths to hook directories */
 | 
			
		||||
	alpm_list_t *overwrite_files; /* Paths that may be overwritten */
 | 
			
		||||
 
 | 
			
		||||
@@ -24,6 +24,7 @@ libalpm_sources = files('''
 | 
			
		||||
  pkghash.h pkghash.c
 | 
			
		||||
  rawstr.c
 | 
			
		||||
  remove.h remove.c
 | 
			
		||||
  sandbox.h sandbox.c
 | 
			
		||||
  signing.c signing.h
 | 
			
		||||
  sync.h sync.c
 | 
			
		||||
  trans.h trans.c
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										224
									
								
								lib/libalpm/sandbox.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										224
									
								
								lib/libalpm/sandbox.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,224 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  sandbox.c
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (c) 2021-2022 Pacman Development Team <pacman-dev@lists.archlinux.org>
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 *  it under the terms of the GNU General Public License as published by
 | 
			
		||||
 *  the Free Software Foundation; either version 2 of the License, or
 | 
			
		||||
 *  (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is distributed in the hope that it will be useful,
 | 
			
		||||
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 *  GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 *  You should have received a copy of the GNU General Public License
 | 
			
		||||
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <grp.h>
 | 
			
		||||
#include <pwd.h>
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
 | 
			
		||||
#include "alpm.h"
 | 
			
		||||
#include "log.h"
 | 
			
		||||
#include "sandbox.h"
 | 
			
		||||
#include "util.h"
 | 
			
		||||
 | 
			
		||||
int SYMEXPORT alpm_sandbox_setup_child(const char* sandboxuser)
 | 
			
		||||
{
 | 
			
		||||
	struct passwd const *pw = NULL;
 | 
			
		||||
 | 
			
		||||
	ASSERT(sandboxuser != NULL, return EINVAL);
 | 
			
		||||
	ASSERT(getuid() == 0, return EPERM);
 | 
			
		||||
	ASSERT((pw = getpwnam(sandboxuser)), return errno);
 | 
			
		||||
	ASSERT(setgid(pw->pw_gid) == 0, return errno);
 | 
			
		||||
	ASSERT(setgroups(0, NULL) == 0, return errno);
 | 
			
		||||
	ASSERT(setuid(pw->pw_uid) == 0, return errno);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int should_retry(int errnum)
 | 
			
		||||
{
 | 
			
		||||
	return errnum == EINTR;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int read_from_pipe(int fd, void *buf, size_t count)
 | 
			
		||||
{
 | 
			
		||||
	size_t nread = 0;
 | 
			
		||||
 | 
			
		||||
	ASSERT(count > 0, return -1);
 | 
			
		||||
 | 
			
		||||
	while(nread < count) {
 | 
			
		||||
		ssize_t r = read(fd, (char *)buf + nread, count-nread);
 | 
			
		||||
		if(r < 0) {
 | 
			
		||||
			if(!should_retry(errno)) {
 | 
			
		||||
				return -1;
 | 
			
		||||
			}
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
		if(r == 0) {
 | 
			
		||||
			/* we hit EOF unexpectedly - bail */
 | 
			
		||||
			return -1;
 | 
			
		||||
		}
 | 
			
		||||
		nread += r;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int write_to_pipe(int fd, const void *buf, size_t count)
 | 
			
		||||
{
 | 
			
		||||
	size_t nwrite = 0;
 | 
			
		||||
 | 
			
		||||
	ASSERT(count > 0, return -1);
 | 
			
		||||
 | 
			
		||||
	while(nwrite < count) {
 | 
			
		||||
		ssize_t r = write(fd, (char *)buf + nwrite, count-nwrite);
 | 
			
		||||
		if(r < 0) {
 | 
			
		||||
			if(!should_retry(errno)) {
 | 
			
		||||
				return -1;
 | 
			
		||||
			}
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
		nwrite += r;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void _alpm_sandbox_cb_log(void *ctx, alpm_loglevel_t level, const char *fmt, va_list args)
 | 
			
		||||
{
 | 
			
		||||
	_alpm_sandbox_callback_t type = ALPM_SANDBOX_CB_LOG;
 | 
			
		||||
	_alpm_sandbox_callback_context *context = ctx;
 | 
			
		||||
	char *string = NULL;
 | 
			
		||||
	int string_size = 0;
 | 
			
		||||
 | 
			
		||||
	if(!context || context->callback_pipe == -1) {
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* compute the required size, as allowed by POSIX.1-2001 and C99 */
 | 
			
		||||
	/* first we need to copy the va_list as it will be consumed by the first call */
 | 
			
		||||
	va_list copy;
 | 
			
		||||
	va_copy(copy, args);
 | 
			
		||||
	string_size = vsnprintf(NULL, 0, fmt, copy);
 | 
			
		||||
	if(string_size <= 0) {
 | 
			
		||||
		va_end(copy);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	MALLOC(string, string_size + 1, return);
 | 
			
		||||
	string_size = vsnprintf(string, string_size + 1, fmt, args);
 | 
			
		||||
	if(string_size > 0) {
 | 
			
		||||
		write_to_pipe(context->callback_pipe, &type, sizeof(type));
 | 
			
		||||
		write_to_pipe(context->callback_pipe, &level, sizeof(level));
 | 
			
		||||
		write_to_pipe(context->callback_pipe, &string_size, sizeof(string_size));
 | 
			
		||||
		write_to_pipe(context->callback_pipe, string, string_size);
 | 
			
		||||
	}
 | 
			
		||||
	va_end(copy);
 | 
			
		||||
	FREE(string);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void _alpm_sandbox_cb_dl(void *ctx, const char *filename, alpm_download_event_type_t event, void *data)
 | 
			
		||||
{
 | 
			
		||||
	_alpm_sandbox_callback_t type = ALPM_SANDBOX_CB_DOWNLOAD;
 | 
			
		||||
	_alpm_sandbox_callback_context *context = ctx;
 | 
			
		||||
	size_t filename_len;
 | 
			
		||||
 | 
			
		||||
	if(!context || context->callback_pipe == -1) {
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ASSERT(filename != NULL, return);
 | 
			
		||||
	ASSERT(event == ALPM_DOWNLOAD_INIT || event == ALPM_DOWNLOAD_PROGRESS || event == ALPM_DOWNLOAD_RETRY || event == ALPM_DOWNLOAD_COMPLETED, return);
 | 
			
		||||
 | 
			
		||||
	filename_len = strlen(filename);
 | 
			
		||||
 | 
			
		||||
	write_to_pipe(context->callback_pipe, &type, sizeof(type));
 | 
			
		||||
	write_to_pipe(context->callback_pipe, &event, sizeof(event));
 | 
			
		||||
	switch(event) {
 | 
			
		||||
		case ALPM_DOWNLOAD_INIT:
 | 
			
		||||
			write_to_pipe(context->callback_pipe, data, sizeof(alpm_download_event_init_t));
 | 
			
		||||
			break;
 | 
			
		||||
		case ALPM_DOWNLOAD_PROGRESS:
 | 
			
		||||
			write_to_pipe(context->callback_pipe, data, sizeof(alpm_download_event_progress_t));
 | 
			
		||||
			break;
 | 
			
		||||
		case ALPM_DOWNLOAD_RETRY:
 | 
			
		||||
			write_to_pipe(context->callback_pipe, data, sizeof(alpm_download_event_retry_t));
 | 
			
		||||
			break;
 | 
			
		||||
		case ALPM_DOWNLOAD_COMPLETED:
 | 
			
		||||
			write_to_pipe(context->callback_pipe, data, sizeof(alpm_download_event_completed_t));
 | 
			
		||||
			break;
 | 
			
		||||
	}
 | 
			
		||||
	write_to_pipe(context->callback_pipe, &filename_len, sizeof(filename_len));
 | 
			
		||||
	write_to_pipe(context->callback_pipe, filename, filename_len);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
bool _alpm_sandbox_process_cb_log(alpm_handle_t *handle, int callback_pipe) {
 | 
			
		||||
	alpm_loglevel_t level;
 | 
			
		||||
	char *string = NULL;
 | 
			
		||||
	int string_size = 0;
 | 
			
		||||
 | 
			
		||||
	ASSERT(read_from_pipe(callback_pipe, &level, sizeof(level)) != -1, return false);
 | 
			
		||||
	ASSERT(read_from_pipe(callback_pipe, &string_size, sizeof(string_size)) != -1, return false);
 | 
			
		||||
 | 
			
		||||
	MALLOC(string, string_size + 1, return false);
 | 
			
		||||
 | 
			
		||||
	ASSERT(read_from_pipe(callback_pipe, string, string_size) != -1, FREE(string); return false);
 | 
			
		||||
	string[string_size] = '\0';
 | 
			
		||||
 | 
			
		||||
	_alpm_log(handle, level, "%s", string);
 | 
			
		||||
	FREE(string);
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool _alpm_sandbox_process_cb_download(alpm_handle_t *handle, int callback_pipe) {
 | 
			
		||||
	alpm_download_event_type_t type;
 | 
			
		||||
	char *filename = NULL;
 | 
			
		||||
	size_t filename_size, cb_data_size;
 | 
			
		||||
	union {
 | 
			
		||||
		alpm_download_event_init_t init;
 | 
			
		||||
		alpm_download_event_progress_t progress;
 | 
			
		||||
		alpm_download_event_retry_t retry;
 | 
			
		||||
		alpm_download_event_completed_t completed;
 | 
			
		||||
	} cb_data;
 | 
			
		||||
 | 
			
		||||
	ASSERT(read_from_pipe(callback_pipe, &type, sizeof(type)) != -1, return false);
 | 
			
		||||
 | 
			
		||||
	switch (type) {
 | 
			
		||||
		case ALPM_DOWNLOAD_INIT:
 | 
			
		||||
			cb_data_size = sizeof(alpm_download_event_init_t);
 | 
			
		||||
			ASSERT(read_from_pipe(callback_pipe, &cb_data.init, cb_data_size) != -1, return false);
 | 
			
		||||
			break;
 | 
			
		||||
		case ALPM_DOWNLOAD_PROGRESS:
 | 
			
		||||
			cb_data_size = sizeof(alpm_download_event_progress_t);
 | 
			
		||||
			ASSERT(read_from_pipe(callback_pipe, &cb_data.progress, cb_data_size) != -1, return false);
 | 
			
		||||
			break;
 | 
			
		||||
		case ALPM_DOWNLOAD_RETRY:
 | 
			
		||||
			cb_data_size = sizeof(alpm_download_event_retry_t);
 | 
			
		||||
			ASSERT(read_from_pipe(callback_pipe, &cb_data.retry, cb_data_size) != -1, return false);
 | 
			
		||||
			break;
 | 
			
		||||
		case ALPM_DOWNLOAD_COMPLETED:
 | 
			
		||||
			cb_data_size = sizeof(alpm_download_event_completed_t);
 | 
			
		||||
			ASSERT(read_from_pipe(callback_pipe, &cb_data.completed, cb_data_size) != -1, return false);
 | 
			
		||||
			break;
 | 
			
		||||
		default:
 | 
			
		||||
			return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ASSERT(read_from_pipe(callback_pipe, &filename_size, sizeof(filename_size)) != -1, return false);;
 | 
			
		||||
 | 
			
		||||
	MALLOC(filename, filename_size + 1, return false);
 | 
			
		||||
 | 
			
		||||
	ASSERT(read_from_pipe(callback_pipe, filename, filename_size) != -1, FREE(filename); return false);
 | 
			
		||||
	filename[filename_size] = '\0';
 | 
			
		||||
 | 
			
		||||
	handle->dlcb(handle->dlcb_ctx, filename, type, &cb_data);
 | 
			
		||||
	FREE(filename);
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										51
									
								
								lib/libalpm/sandbox.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								lib/libalpm/sandbox.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,51 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  sandbox.h
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (c) 2021-2022 Pacman Development Team <pacman-dev@lists.archlinux.org>
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 *  it under the terms of the GNU General Public License as published by
 | 
			
		||||
 *  the Free Software Foundation; either version 2 of the License, or
 | 
			
		||||
 *  (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is distributed in the hope that it will be useful,
 | 
			
		||||
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 *  GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 *  You should have received a copy of the GNU General Public License
 | 
			
		||||
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef ALPM_SANDBOX_H
 | 
			
		||||
#define ALPM_SANDBOX_H
 | 
			
		||||
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* The type of callbacks that can happen during a sandboxed operation */
 | 
			
		||||
typedef enum {
 | 
			
		||||
	ALPM_SANDBOX_CB_LOG,
 | 
			
		||||
	ALPM_SANDBOX_CB_DOWNLOAD
 | 
			
		||||
} _alpm_sandbox_callback_t;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
	int callback_pipe;
 | 
			
		||||
} _alpm_sandbox_callback_context;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* Sandbox callbacks */
 | 
			
		||||
 | 
			
		||||
__attribute__((format(printf, 3, 0)))
 | 
			
		||||
void _alpm_sandbox_cb_log(void *ctx, alpm_loglevel_t level, const char *fmt, va_list args);
 | 
			
		||||
 | 
			
		||||
void _alpm_sandbox_cb_dl(void *ctx, const char *filename, alpm_download_event_type_t event, void *data);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* Functions to capture sandbox callbacks and convert them to alpm callbacks */
 | 
			
		||||
 | 
			
		||||
bool _alpm_sandbox_process_cb_log(alpm_handle_t *handle, int callback_pipe);
 | 
			
		||||
bool _alpm_sandbox_process_cb_download(alpm_handle_t *handle, int callback_pipe);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#endif /* ALPM_SANDBOX_H */
 | 
			
		||||
@@ -767,6 +767,7 @@ static int find_dl_candidates(alpm_handle_t *handle, alpm_list_t **files)
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static int download_files(alpm_handle_t *handle)
 | 
			
		||||
{
 | 
			
		||||
	const char *cachedir;
 | 
			
		||||
@@ -825,8 +826,15 @@ static int download_files(alpm_handle_t *handle)
 | 
			
		||||
			CALLOC(payload, 1, sizeof(*payload), GOTO_ERR(handle, ALPM_ERR_MEMORY, finish));
 | 
			
		||||
			STRDUP(payload->remote_name, pkg->filename, FREE(payload); GOTO_ERR(handle, ALPM_ERR_MEMORY, finish));
 | 
			
		||||
			STRDUP(payload->filepath, pkg->filename,
 | 
			
		||||
				FREE(payload->remote_name); FREE(payload);
 | 
			
		||||
				_alpm_dload_payload_reset(payload); FREE(payload);
 | 
			
		||||
				GOTO_ERR(handle, ALPM_ERR_MEMORY, finish));
 | 
			
		||||
			payload->destfile_name = _alpm_get_fullpath(cachedir, payload->remote_name, "");
 | 
			
		||||
			payload->tempfile_name = _alpm_get_fullpath(cachedir, payload->remote_name, ".part");
 | 
			
		||||
			if(!payload->destfile_name || !payload->tempfile_name) {
 | 
			
		||||
				_alpm_dload_payload_reset(payload);
 | 
			
		||||
				FREE(payload);
 | 
			
		||||
				GOTO_ERR(handle, ALPM_ERR_MEMORY, finish);
 | 
			
		||||
			}
 | 
			
		||||
			payload->max_size = pkg->size;
 | 
			
		||||
			payload->cache_servers = pkg->origin_data.db->cache_servers;
 | 
			
		||||
			payload->servers = pkg->origin_data.db->servers;
 | 
			
		||||
 
 | 
			
		||||
@@ -197,6 +197,23 @@ cleanup:
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** Combines a directory, filename and suffix to provide full path of a file
 | 
			
		||||
 * @param path directory path
 | 
			
		||||
 * @param filename file name
 | 
			
		||||
 * @param suffix suffix
 | 
			
		||||
 * @return file path
 | 
			
		||||
*/
 | 
			
		||||
char *_alpm_get_fullpath(const char *path, const char *filename, const char *suffix)
 | 
			
		||||
{
 | 
			
		||||
	char *filepath;
 | 
			
		||||
	/* len = localpath len + filename len + suffix len + null */
 | 
			
		||||
	size_t len = strlen(path) + strlen(filename) + strlen(suffix) + 1;
 | 
			
		||||
	MALLOC(filepath, len, return NULL);
 | 
			
		||||
	snprintf(filepath, len, "%s%s%s", path, filename, suffix);
 | 
			
		||||
 | 
			
		||||
	return filepath;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** Trim trailing newlines from a string (if any exist).
 | 
			
		||||
 * @param str a single line of text
 | 
			
		||||
 * @param len size of str, if known, else 0
 | 
			
		||||
@@ -555,7 +572,7 @@ static int _alpm_chroot_read_from_child(alpm_handle_t *handle, int fd,
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void _alpm_reset_signals(void)
 | 
			
		||||
void _alpm_reset_signals(void)
 | 
			
		||||
{
 | 
			
		||||
	/* reset POSIX defined signals (see signal.h) */
 | 
			
		||||
	/* there are likely more but there is no easy way
 | 
			
		||||
 
 | 
			
		||||
@@ -116,6 +116,7 @@ struct archive_read_buffer {
 | 
			
		||||
int _alpm_makepath(const char *path);
 | 
			
		||||
int _alpm_makepath_mode(const char *path, mode_t mode);
 | 
			
		||||
int _alpm_copyfile(const char *src, const char *dest);
 | 
			
		||||
char *_alpm_get_fullpath(const char *path, const char *filename, const char *suffix);
 | 
			
		||||
size_t _alpm_strip_newline(char *str, size_t len);
 | 
			
		||||
 | 
			
		||||
int _alpm_open_archive(alpm_handle_t *handle, const char *path,
 | 
			
		||||
@@ -129,6 +130,7 @@ ssize_t _alpm_files_in_directory(alpm_handle_t *handle, const char *path, int fu
 | 
			
		||||
 | 
			
		||||
typedef ssize_t (*_alpm_cb_io)(void *buf, ssize_t len, void *ctx);
 | 
			
		||||
 | 
			
		||||
void _alpm_reset_signals(void);
 | 
			
		||||
int _alpm_run_chroot(alpm_handle_t *handle, const char *cmd, char *const argv[],
 | 
			
		||||
		_alpm_cb_io in_cb, void *in_ctx);
 | 
			
		||||
int _alpm_ldconfig(alpm_handle_t *handle);
 | 
			
		||||
 
 | 
			
		||||
@@ -57,7 +57,7 @@ static alpm_list_t *output = NULL;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
struct pacman_progress_bar {
 | 
			
		||||
	const char *filename;
 | 
			
		||||
	char *filename;
 | 
			
		||||
	off_t xfered; /* Current amount of transferred data */
 | 
			
		||||
	off_t total_size;
 | 
			
		||||
	size_t downloaded;
 | 
			
		||||
@@ -745,7 +745,8 @@ static void init_total_progressbar(void)
 | 
			
		||||
{
 | 
			
		||||
	totalbar = calloc(1, sizeof(struct pacman_progress_bar));
 | 
			
		||||
	assert(totalbar);
 | 
			
		||||
	totalbar->filename = _("Total");
 | 
			
		||||
	totalbar->filename = strdup(_("Total"));
 | 
			
		||||
	assert(totalbar->filename);
 | 
			
		||||
	totalbar->init_time = get_time_ms();
 | 
			
		||||
	totalbar->total_size = list_total;
 | 
			
		||||
	totalbar->howmany = list_total_pkgs;
 | 
			
		||||
@@ -882,7 +883,8 @@ static void dload_init_event(const char *filename, alpm_download_event_init_t *d
 | 
			
		||||
 | 
			
		||||
	struct pacman_progress_bar *bar = calloc(1, sizeof(struct pacman_progress_bar));
 | 
			
		||||
	assert(bar);
 | 
			
		||||
	bar->filename = filename;
 | 
			
		||||
	bar->filename = strdup(filename);
 | 
			
		||||
	assert(bar->filename);
 | 
			
		||||
	bar->init_time = get_time_ms();
 | 
			
		||||
	bar->rate = 0.0;
 | 
			
		||||
	multibar_ui.active_downloads = alpm_list_add(multibar_ui.active_downloads, bar);
 | 
			
		||||
@@ -1087,6 +1089,7 @@ static void dload_complete_event(const char *filename, alpm_download_event_compl
 | 
			
		||||
			multibar_ui.active_downloads = alpm_list_remove_item(
 | 
			
		||||
				multibar_ui.active_downloads, head);
 | 
			
		||||
			free(head);
 | 
			
		||||
			free(j->filename);
 | 
			
		||||
			free(j);
 | 
			
		||||
		} else {
 | 
			
		||||
			break;
 | 
			
		||||
 
 | 
			
		||||
@@ -155,6 +155,7 @@ int config_free(config_t *oldconfig)
 | 
			
		||||
	free(oldconfig->dbpath);
 | 
			
		||||
	free(oldconfig->logfile);
 | 
			
		||||
	free(oldconfig->gpgdir);
 | 
			
		||||
	free(oldconfig->sandboxuser);
 | 
			
		||||
	FREELIST(oldconfig->hookdirs);
 | 
			
		||||
	FREELIST(oldconfig->cachedirs);
 | 
			
		||||
	free(oldconfig->xfercommand);
 | 
			
		||||
@@ -669,6 +670,11 @@ static int _parse_options(const char *key, char *value,
 | 
			
		||||
				config->logfile = strdup(value);
 | 
			
		||||
				pm_printf(ALPM_LOG_DEBUG, "config: logfile: %s\n", value);
 | 
			
		||||
			}
 | 
			
		||||
		} else if(strcmp(key, "DownloadUser") == 0) {
 | 
			
		||||
			if(!config->sandboxuser) {
 | 
			
		||||
				config->sandboxuser = strdup(value);
 | 
			
		||||
				pm_printf(ALPM_LOG_DEBUG, "config: sandboxuser: %s\n", value);
 | 
			
		||||
			}
 | 
			
		||||
		} else if(strcmp(key, "XferCommand") == 0) {
 | 
			
		||||
			char **c;
 | 
			
		||||
			if((config->xfercommand_argv = wordsplit(value)) == NULL) {
 | 
			
		||||
@@ -921,6 +927,7 @@ static int setup_libalpm(void)
 | 
			
		||||
	alpm_option_set_architectures(handle, config->architectures);
 | 
			
		||||
	alpm_option_set_checkspace(handle, config->checkspace);
 | 
			
		||||
	alpm_option_set_usesyslog(handle, config->usesyslog);
 | 
			
		||||
	alpm_option_set_sandboxuser(handle, config->sandboxuser);
 | 
			
		||||
 | 
			
		||||
	alpm_option_set_ignorepkgs(handle, config->ignorepkg);
 | 
			
		||||
	alpm_option_set_ignoregroups(handle, config->ignoregrp);
 | 
			
		||||
 
 | 
			
		||||
@@ -68,6 +68,7 @@ typedef struct __config_t {
 | 
			
		||||
	char *logfile;
 | 
			
		||||
	char *gpgdir;
 | 
			
		||||
	char *sysroot;
 | 
			
		||||
	char *sandboxuser;
 | 
			
		||||
	alpm_list_t *hookdirs;
 | 
			
		||||
	alpm_list_t *cachedirs;
 | 
			
		||||
	alpm_list_t *architectures;
 | 
			
		||||
 
 | 
			
		||||
@@ -252,6 +252,7 @@ static void dump_config(void)
 | 
			
		||||
	show_list_str("HookDir", config->hookdirs);
 | 
			
		||||
	show_str("GPGDir", config->gpgdir);
 | 
			
		||||
	show_str("LogFile", config->logfile);
 | 
			
		||||
	show_str("DownloadUser", config->sandboxuser);
 | 
			
		||||
 | 
			
		||||
	show_list_str("HoldPkg", config->holdpkg);
 | 
			
		||||
	show_list_str("IgnorePkg", config->ignorepkg);
 | 
			
		||||
@@ -352,6 +353,8 @@ static int list_directives(void)
 | 
			
		||||
			show_str("GPGDir", config->gpgdir);
 | 
			
		||||
		} else if(strcasecmp(i->data, "LogFile") == 0) {
 | 
			
		||||
			show_str("LogFile", config->logfile);
 | 
			
		||||
		} else if(strcasecmp(i->data, "DownloadUser") == 0) {
 | 
			
		||||
			show_str("DownloadUser", config->sandboxuser);
 | 
			
		||||
 | 
			
		||||
		} else if(strcasecmp(i->data, "HoldPkg") == 0) {
 | 
			
		||||
			show_list_str("HoldPkg", config->holdpkg);
 | 
			
		||||
 
 | 
			
		||||
@@ -155,6 +155,8 @@ pacman_tests = [
 | 
			
		||||
  'tests/replace103.py',
 | 
			
		||||
  'tests/replace104.py',
 | 
			
		||||
  'tests/replace110.py',
 | 
			
		||||
  'tests/sandbox-download-upgrade.py',
 | 
			
		||||
  'tests/sandbox-download-basic.py',
 | 
			
		||||
  'tests/scriptlet001.py',
 | 
			
		||||
  'tests/scriptlet002.py',
 | 
			
		||||
  'tests/scriptlet-signal-handling.py',
 | 
			
		||||
@@ -334,7 +336,6 @@ pacman_tests = [
 | 
			
		||||
  'tests/xfercommand001.py',
 | 
			
		||||
  'tests/upgrade-download-404.py',
 | 
			
		||||
  'tests/upgrade-download-pkg-and-sig-with-filename.py',
 | 
			
		||||
  'tests/upgrade-download-pkg-and-sig-without-filename.py',
 | 
			
		||||
  'tests/upgrade-download-with-xfercommand.py',
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
@@ -347,7 +348,6 @@ xfail_tests = {
 | 
			
		||||
  'tests/sync403.py': true,
 | 
			
		||||
  'tests/sync406.py': true,
 | 
			
		||||
  'tests/upgrade078.py': true,
 | 
			
		||||
  'tests/upgrade-download-with-xfercommand.py': true,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
foreach input : pacman_tests
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										11
									
								
								test/pacman/tests/database-refresh-optional-siglevel.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								test/pacman/tests/database-refresh-optional-siglevel.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
			
		||||
self.description = "refresh databases with Optional siglevel"
 | 
			
		||||
self.require_capability("curl")
 | 
			
		||||
 | 
			
		||||
p1 = pmpkg('pkg1', '1.0-1')
 | 
			
		||||
self.addpkg2db('sync', p1)
 | 
			
		||||
 | 
			
		||||
self.db['sync'].option['SigLevel'] = ["Optional"]
 | 
			
		||||
 | 
			
		||||
self.args = '-Syy'
 | 
			
		||||
 | 
			
		||||
self.addrule("PACMAN_RETCODE=0")
 | 
			
		||||
							
								
								
									
										20
									
								
								test/pacman/tests/sandbox-download-basic.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								test/pacman/tests/sandbox-download-basic.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,20 @@
 | 
			
		||||
self.description = "--sync with DownloadUser set"
 | 
			
		||||
self.require_capability("curl")
 | 
			
		||||
 | 
			
		||||
p1 = pmpkg('pkg1', '1.0-1')
 | 
			
		||||
self.addpkg2db('sync', p1)
 | 
			
		||||
 | 
			
		||||
url = self.add_simple_http_server({
 | 
			
		||||
    '/{}'.format(p1.filename()): p1.makepkg_bytes(),
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
self.option['DownloadUser'] = ['root']
 | 
			
		||||
self.db['sync'].option['Server'] = [ url ]
 | 
			
		||||
self.db['sync'].syncdir = False
 | 
			
		||||
self.cachepkgs = False
 | 
			
		||||
 | 
			
		||||
self.args = '-S pkg1'
 | 
			
		||||
 | 
			
		||||
self.addrule("PACMAN_RETCODE=0")
 | 
			
		||||
self.addrule("PKG_EXIST=pkg1")
 | 
			
		||||
self.addrule("CACHE_EXISTS=pkg1|1.0-1")
 | 
			
		||||
							
								
								
									
										17
									
								
								test/pacman/tests/sandbox-download-upgrade.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								test/pacman/tests/sandbox-download-upgrade.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
			
		||||
self.description = "--upgrade with DownloadUser set"
 | 
			
		||||
self.require_capability("curl")
 | 
			
		||||
 | 
			
		||||
self.option['DownloadUser'] = ['root']
 | 
			
		||||
 | 
			
		||||
p1 = pmpkg('pkg1', '1.0-1')
 | 
			
		||||
self.addpkg(p1)
 | 
			
		||||
 | 
			
		||||
url = self.add_simple_http_server({
 | 
			
		||||
    '/{}'.format(p1.filename()): p1.makepkg_bytes(),
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
self.args = '-U {url}/{}'.format(p1.filename(), url=url)
 | 
			
		||||
 | 
			
		||||
self.addrule("PACMAN_RETCODE=0")
 | 
			
		||||
self.addrule("PKG_EXIST=pkg1")
 | 
			
		||||
self.addrule("CACHE_EXISTS=pkg1|1.0-1")
 | 
			
		||||
@@ -10,7 +10,7 @@ url = self.add_simple_http_server({
 | 
			
		||||
        'body': 'simple.sig',
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    # content-disposition filename
 | 
			
		||||
    # content-disposition filename is now ignored
 | 
			
		||||
    '/cd.pkg': {
 | 
			
		||||
        'headers': { 'Content-Disposition': 'attachment; filename="cd-alt.pkg"' },
 | 
			
		||||
        'body': 'cd'
 | 
			
		||||
@@ -64,19 +64,26 @@ self.addrule('!PACMAN_RETCODE=0')
 | 
			
		||||
self.addrule('CACHE_FCONTENTS=simple.pkg|simple')
 | 
			
		||||
self.addrule('CACHE_FCONTENTS=simple.pkg.sig|simple.sig')
 | 
			
		||||
 | 
			
		||||
self.addrule('!CACHE_FEXISTS=cd.pkg')
 | 
			
		||||
self.addrule('!CACHE_FEXISTS=cd.pkg.sig')
 | 
			
		||||
self.addrule('CACHE_FCONTENTS=cd-alt.pkg|cd')
 | 
			
		||||
self.addrule('CACHE_FCONTENTS=cd-alt.pkg.sig|cd.sig')
 | 
			
		||||
self.addrule('!CACHE_FEXISTS=cd-alt.pkg')
 | 
			
		||||
self.addrule('!CACHE_FEXISTS=cd-alt.pkg.sig')
 | 
			
		||||
self.addrule('CACHE_FCONTENTS=cd.pkg|cd')
 | 
			
		||||
self.addrule('CACHE_FCONTENTS=cd.pkg.sig|cd.sig')
 | 
			
		||||
 | 
			
		||||
self.addrule('!CACHE_FEXISTS=redir.pkg')
 | 
			
		||||
self.addrule('CACHE_FCONTENTS=redir-dest.pkg|redir-dest')
 | 
			
		||||
self.addrule('CACHE_FCONTENTS=redir-dest.pkg.sig|redir-dest.sig')
 | 
			
		||||
self.addrule('!CACHE_FEXISTS=redir-dest.pkg')
 | 
			
		||||
self.addrule('CACHE_FCONTENTS=redir.pkg|redir-dest')
 | 
			
		||||
self.addrule('CACHE_FCONTENTS=redir.pkg.sig|redir-dest.sig')
 | 
			
		||||
 | 
			
		||||
self.addrule('!CACHE_FEXISTS=cd-redir.pkg')
 | 
			
		||||
self.addrule('!CACHE_FEXISTS=cd-redir-dest.pkg')
 | 
			
		||||
self.addrule('CACHE_FCONTENTS=cd-redir-dest-alt.pkg|cd-redir-dest')
 | 
			
		||||
self.addrule('CACHE_FCONTENTS=cd-redir-dest-alt.pkg.sig|cd-redir-dest.sig')
 | 
			
		||||
self.addrule('CACHE_FCONTENTS=redir-cdn.pkg|redir-dest')
 | 
			
		||||
self.addrule('CACHE_FCONTENTS=redir-cdn.pkg.sig|redir-dest.sig')
 | 
			
		||||
 | 
			
		||||
self.addrule('CACHE_FCONTENTS=cdn-alt.pkg|cdn-alt')
 | 
			
		||||
self.addrule('CACHE_FCONTENTS=cdn-alt.pkg.sig|cdn-alt.sig')
 | 
			
		||||
self.addrule('!CACHE_FEXISTS=cd-redir-dest-alt.pkg')
 | 
			
		||||
self.addrule('!CACHE_FEXISTS=cd-redir-dest-alt.pkg')
 | 
			
		||||
self.addrule('CACHE_FCONTENTS=cd-redir.pkg|cd-redir-dest')
 | 
			
		||||
self.addrule('CACHE_FCONTENTS=cd-redir.pkg.sig|cd-redir-dest.sig')
 | 
			
		||||
 | 
			
		||||
self.addrule('!CACHE_FEXISTS=cdn-3')
 | 
			
		||||
self.addrule('!CACHE_FEXISTS=cdn-4')
 | 
			
		||||
self.addrule('!CACHE_FEXISTS=cdn-alt.pkg')
 | 
			
		||||
self.addrule('!CACHE_FEXISTS=cdn-alt.pkg.sig')
 | 
			
		||||
self.addrule('CACHE_FCONTENTS=cd-redir-cdn.pkg|cdn-alt')
 | 
			
		||||
self.addrule('CACHE_FCONTENTS=cd-redir-cdn.pkg.sig|cdn-alt.sig')
 | 
			
		||||
 
 | 
			
		||||
@@ -1,63 +0,0 @@
 | 
			
		||||
self.description = 'download remote packages with -U without a URL filename'
 | 
			
		||||
self.require_capability("gpg")
 | 
			
		||||
self.require_capability("curl")
 | 
			
		||||
 | 
			
		||||
url = self.add_simple_http_server({
 | 
			
		||||
    # simple
 | 
			
		||||
    '/simple.pkg/': 'simple',
 | 
			
		||||
    '/simple.pkg/.sig': 'simple.sig',
 | 
			
		||||
 | 
			
		||||
    # content-disposition filename
 | 
			
		||||
    '/cd.pkg/': {
 | 
			
		||||
        'headers': { 'Content-Disposition': 'attachment; filename="cd-alt.pkg"' },
 | 
			
		||||
        'body': 'cd'
 | 
			
		||||
    },
 | 
			
		||||
    '/cd.pkg/.sig': {
 | 
			
		||||
        'headers': { 'Content-Disposition': 'attachment; filename="cd-alt-bad.pkg.sig"' },
 | 
			
		||||
        'body': 'cd.sig'
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    # redirect
 | 
			
		||||
    '/redir.pkg/': { 'code': 303, 'headers': { 'Location': '/redir-dest.pkg' } },
 | 
			
		||||
    '/redir-dest.pkg': 'redir-dest',
 | 
			
		||||
    '/redir-dest.pkg.sig': 'redir-dest.sig',
 | 
			
		||||
 | 
			
		||||
    # content-disposition and redirect
 | 
			
		||||
    '/cd-redir.pkg/': { 'code': 303, 'headers': { 'Location': '/cd-redir-dest.pkg' } },
 | 
			
		||||
    '/cd-redir-dest.pkg': {
 | 
			
		||||
        'headers': { 'Content-Disposition': 'attachment; filename="cd-redir-dest-alt.pkg"' },
 | 
			
		||||
        'body': 'cd-redir-dest'
 | 
			
		||||
    },
 | 
			
		||||
    '/cd-redir-dest.pkg.sig': 'cd-redir-dest.sig',
 | 
			
		||||
 | 
			
		||||
    # TODO: absolutely terrible hack to prevent pacman from attempting to
 | 
			
		||||
    # validate packages, which causes failure under --valgrind thanks to
 | 
			
		||||
    # a memory leak in gpgme that is too general for inclusion in valgrind.supp
 | 
			
		||||
    '/404': { 'code': 404 },
 | 
			
		||||
 | 
			
		||||
    '': 'fallback',
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
self.args = '-Uw {url}/simple.pkg/ {url}/cd.pkg/ {url}/redir.pkg/ {url}/cd-redir.pkg/ {url}/404'.format(url=url)
 | 
			
		||||
 | 
			
		||||
# packages/sigs are not valid, error is expected
 | 
			
		||||
self.addrule('!PACMAN_RETCODE=0')
 | 
			
		||||
 | 
			
		||||
# TODO: use a predictable file name
 | 
			
		||||
#self.addrule('CACHE_FCONTENTS=simple.pkg|simple')
 | 
			
		||||
#self.addrule('CACHE_FCONTENTS=simple.pkg.sig|simple.sig')
 | 
			
		||||
 | 
			
		||||
self.addrule('!CACHE_FEXISTS=cd.pkg')
 | 
			
		||||
self.addrule('CACHE_FCONTENTS=cd-alt.pkg|cd')
 | 
			
		||||
self.addrule('CACHE_FCONTENTS=cd-alt.pkg.sig|cd.sig')
 | 
			
		||||
 | 
			
		||||
self.addrule('!CACHE_FEXISTS=redir.pkg')
 | 
			
		||||
self.addrule('CACHE_FCONTENTS=redir-dest.pkg|redir-dest')
 | 
			
		||||
self.addrule('CACHE_FCONTENTS=redir-dest.pkg.sig|redir-dest.sig')
 | 
			
		||||
 | 
			
		||||
self.addrule('!CACHE_FEXISTS=cd-redir.pkg')
 | 
			
		||||
self.addrule('!CACHE_FEXISTS=cd-redir-dest.pkg')
 | 
			
		||||
self.addrule('CACHE_FCONTENTS=cd-redir-dest-alt.pkg|cd-redir-dest')
 | 
			
		||||
self.addrule('CACHE_FCONTENTS=cd-redir-dest-alt.pkg.sig|cd-redir-dest.sig')
 | 
			
		||||
 | 
			
		||||
self.addrule('!CACHE_FEXISTS=.sig')
 | 
			
		||||
@@ -20,7 +20,3 @@ self.addrule("PKG_EXIST=pkg1")
 | 
			
		||||
self.addrule("PKG_EXIST=pkg2")
 | 
			
		||||
self.addrule("CACHE_EXISTS=pkg1|1.0-1")
 | 
			
		||||
self.addrule("CACHE_EXISTS=pkg2|2.0-2")
 | 
			
		||||
 | 
			
		||||
# --upgrade fails hard with XferCommand because the fetch callback has no way
 | 
			
		||||
# to return the file path to alpm
 | 
			
		||||
self.expectfailure = True
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user