Download to a temporary directory owned by the Download user
Signed-off-by: Remi Gacogne <rgacogne@archlinux.org> Signed-off-by: Allan McRae <allan@archlinux.org>
This commit is contained in:
parent
11c8eca9a6
commit
e1a7b83e8e
7 changed files with 194 additions and 27 deletions
|
@ -208,10 +208,8 @@ Options
|
|||
stream is used (i.e. downloads happen sequentially).
|
||||
|
||||
*DownloadUser =* username::
|
||||
Specifies the user to switch to for downloading files. That user should exist
|
||||
on the system and have the permissions to write to the files located in
|
||||
`DBPath/sync` and `CacheDir`. If this config option is not set then the
|
||||
downloads are done as the user running pacman.
|
||||
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
|
||||
|
|
|
@ -138,6 +138,7 @@ valid:
|
|||
|
||||
int SYMEXPORT alpm_db_update(alpm_handle_t *handle, alpm_list_t *dbs, int force) {
|
||||
char *syncpath;
|
||||
char *temporary_syncpath;
|
||||
const char *dbext = handle->dbext;
|
||||
alpm_list_t *i;
|
||||
int ret = -1;
|
||||
|
@ -152,6 +153,8 @@ int SYMEXPORT alpm_db_update(alpm_handle_t *handle, alpm_list_t *dbs, int force)
|
|||
|
||||
syncpath = get_sync_dir(handle);
|
||||
ASSERT(syncpath != NULL, return -1);
|
||||
temporary_syncpath = _alpm_temporary_download_dir_setup(syncpath, handle->sandboxuser);
|
||||
ASSERT(temporary_syncpath != NULL, FREE(syncpath); return -1);
|
||||
|
||||
/* make sure we have a sane umask */
|
||||
oldmask = umask(0022);
|
||||
|
@ -193,8 +196,8 @@ int SYMEXPORT alpm_db_update(alpm_handle_t *handle, alpm_list_t *dbs, int force)
|
|||
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");
|
||||
payload->destfile_name = _alpm_get_fullpath(temporary_syncpath, payload->remote_name, "");
|
||||
payload->tempfile_name = _alpm_get_fullpath(temporary_syncpath, payload->remote_name, ".part");
|
||||
if(!payload->destfile_name || !payload->tempfile_name) {
|
||||
_alpm_dload_payload_reset(payload);
|
||||
FREE(payload);
|
||||
|
@ -217,7 +220,7 @@ int SYMEXPORT alpm_db_update(alpm_handle_t *handle, alpm_list_t *dbs, int force)
|
|||
|
||||
event.type = ALPM_EVENT_DB_RETRIEVE_START;
|
||||
EVENT(handle, &event);
|
||||
ret = _alpm_download(handle, payloads, syncpath);
|
||||
ret = _alpm_download(handle, payloads, syncpath, temporary_syncpath);
|
||||
if(ret < 0) {
|
||||
event.type = ALPM_EVENT_DB_RETRIEVE_FAILED;
|
||||
EVENT(handle, &event);
|
||||
|
@ -265,7 +268,9 @@ cleanup:
|
|||
alpm_list_free_inner(payloads, (alpm_list_fn_free)_alpm_dload_payload_reset);
|
||||
FREELIST(payloads);
|
||||
}
|
||||
free(syncpath);
|
||||
_alpm_remove_temporary_download_dir(temporary_syncpath);
|
||||
FREE(temporary_syncpath);
|
||||
FREE(syncpath);
|
||||
umask(oldmask);
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -30,6 +30,8 @@
|
|||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
#include <signal.h>
|
||||
#include <dirent.h>
|
||||
#include <pwd.h>
|
||||
|
||||
#ifdef HAVE_NETINET_IN_H
|
||||
#include <netinet/in.h> /* IPPROTO_TCP */
|
||||
|
@ -71,6 +73,20 @@ static mode_t _getumask(void)
|
|||
return mask;
|
||||
}
|
||||
|
||||
static int finalize_download_file(const char *filename)
|
||||
{
|
||||
struct stat st;
|
||||
ASSERT(filename != NULL, return -1);
|
||||
ASSERT(stat(filename, &st) == 0, return -1);
|
||||
if(st.st_size == 0) {
|
||||
unlink(filename);
|
||||
return 1;
|
||||
}
|
||||
ASSERT(chown(filename, 0, 0) != -1, return -1);
|
||||
ASSERT(chmod(filename, ~(_getumask()) & 0666) != -1, return -1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static FILE *create_tempfile(struct dload_payload *payload, const char *localpath)
|
||||
{
|
||||
int fd;
|
||||
|
@ -1061,20 +1077,72 @@ static int payload_download_fetchcb(struct dload_payload *payload,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int move_file(const char *filepath, const char *directory)
|
||||
{
|
||||
ASSERT(filepath != NULL, return -1);
|
||||
ASSERT(directory != NULL, return -1);
|
||||
int ret = finalize_download_file(filepath);
|
||||
if(ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
const char *filename = mbasename(filepath);
|
||||
char *dest = _alpm_get_fullpath(directory, filename, "");
|
||||
if(rename(filepath, dest)) {
|
||||
FREE(dest);
|
||||
return -1;
|
||||
}
|
||||
FREE(dest);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int finalize_download_locations(alpm_list_t *payloads, const char *localpath)
|
||||
{
|
||||
ASSERT(payloads != NULL, return -1);
|
||||
ASSERT(localpath != NULL, return -1);
|
||||
alpm_list_t *p;
|
||||
int returnvalue = 0;
|
||||
for(p = payloads; p; p = p->next) {
|
||||
struct dload_payload *payload = p->data;
|
||||
if(payload->tempfile_name) {
|
||||
move_file(payload->tempfile_name, localpath);
|
||||
}
|
||||
if(payload->destfile_name) {
|
||||
int ret = move_file(payload->destfile_name, localpath);
|
||||
|
||||
if(ret == -1) {
|
||||
returnvalue = -1;
|
||||
}
|
||||
|
||||
if (payload->download_signature) {
|
||||
const char sig_suffix[] = ".sig";
|
||||
char *sig_filename = NULL;
|
||||
size_t sig_filename_len = strlen(payload->destfile_name) + sizeof(sig_suffix);
|
||||
MALLOC(sig_filename, sig_filename_len, continue);
|
||||
snprintf(sig_filename, sig_filename_len, "%s%s", payload->destfile_name, sig_suffix);
|
||||
move_file(sig_filename, localpath);
|
||||
FREE(sig_filename);
|
||||
}
|
||||
}
|
||||
}
|
||||
return returnvalue;
|
||||
}
|
||||
|
||||
/* 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
|
||||
*/
|
||||
int _alpm_download(alpm_handle_t *handle,
|
||||
alpm_list_t *payloads /* struct dload_payload */,
|
||||
const char *localpath)
|
||||
const char *localpath,
|
||||
const char *temporary_localpath)
|
||||
{
|
||||
int ret;
|
||||
if(handle->fetchcb == NULL) {
|
||||
#ifdef HAVE_LIBCURL
|
||||
if(handle->sandboxuser) {
|
||||
return curl_download_internal_sandboxed(handle, payloads, localpath);
|
||||
ret = curl_download_internal_sandboxed(handle, payloads, temporary_localpath);
|
||||
} else {
|
||||
return curl_download_internal(handle, payloads);
|
||||
ret = curl_download_internal(handle, payloads);
|
||||
}
|
||||
#else
|
||||
RET_ERR(handle, ALPM_ERR_EXTERNAL_DOWNLOAD, -1);
|
||||
|
@ -1085,10 +1153,10 @@ int _alpm_download(alpm_handle_t *handle,
|
|||
for(p = payloads; p; p = p->next) {
|
||||
struct dload_payload *payload = p->data;
|
||||
alpm_list_t *s;
|
||||
int ret = -1;
|
||||
ret = -1;
|
||||
|
||||
if(payload->fileurl) {
|
||||
ret = handle->fetchcb(handle->fetchcb_ctx, payload->fileurl, localpath, payload->force);
|
||||
ret = handle->fetchcb(handle->fetchcb_ctx, payload->fileurl, temporary_localpath, payload->force);
|
||||
if (ret != -1 && payload->download_signature) {
|
||||
/* Download signature if requested */
|
||||
char *sig_fileurl;
|
||||
|
@ -1098,7 +1166,7 @@ int _alpm_download(alpm_handle_t *handle,
|
|||
MALLOC(sig_fileurl, sig_len, RET_ERR(handle, ALPM_ERR_MEMORY, -1));
|
||||
snprintf(sig_fileurl, sig_len, "%s.sig", payload->fileurl);
|
||||
|
||||
retsig = handle->fetchcb(handle->fetchcb_ctx, sig_fileurl, localpath, payload->force);
|
||||
retsig = handle->fetchcb(handle->fetchcb_ctx, sig_fileurl, temporary_localpath, payload->force);
|
||||
free(sig_fileurl);
|
||||
|
||||
if(!payload->signature_optional) {
|
||||
|
@ -1107,13 +1175,13 @@ int _alpm_download(alpm_handle_t *handle,
|
|||
}
|
||||
} else {
|
||||
for(s = payload->cache_servers; s; s = s->next) {
|
||||
ret = payload_download_fetchcb(payload, s->data, localpath);
|
||||
ret = payload_download_fetchcb(payload, s->data, temporary_localpath);
|
||||
if (ret != -1) {
|
||||
goto download_signature;
|
||||
}
|
||||
}
|
||||
for(s = payload->servers; s; s = s->next) {
|
||||
ret = payload_download_fetchcb(payload, s->data, localpath);
|
||||
ret = payload_download_fetchcb(payload, s->data, temporary_localpath);
|
||||
if (ret != -1) {
|
||||
goto download_signature;
|
||||
}
|
||||
|
@ -1129,7 +1197,7 @@ download_signature:
|
|||
MALLOC(sig_fileurl, sig_len, RET_ERR(handle, ALPM_ERR_MEMORY, -1));
|
||||
snprintf(sig_fileurl, sig_len, "%s/%s.sig", (const char *)(s->data), payload->filepath);
|
||||
|
||||
retsig = handle->fetchcb(handle->fetchcb_ctx, sig_fileurl, localpath, payload->force);
|
||||
retsig = handle->fetchcb(handle->fetchcb_ctx, sig_fileurl, temporary_localpath, payload->force);
|
||||
free(sig_fileurl);
|
||||
|
||||
if(!payload->signature_optional) {
|
||||
|
@ -1144,8 +1212,13 @@ download_signature:
|
|||
updated = 1;
|
||||
}
|
||||
}
|
||||
return updated ? 0 : 1;
|
||||
ret = updated ? 0 : 1;
|
||||
}
|
||||
|
||||
if (finalize_download_locations(payloads, localpath) != 0) {
|
||||
return -1;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static char *filecache_find_url(alpm_handle_t *handle, const char *url)
|
||||
|
@ -1168,6 +1241,7 @@ int SYMEXPORT alpm_fetch_pkgurl(alpm_handle_t *handle, const alpm_list_t *urls,
|
|||
alpm_list_t **fetched)
|
||||
{
|
||||
const char *cachedir;
|
||||
char *temporary_cachedir = NULL;
|
||||
alpm_list_t *payloads = NULL;
|
||||
const alpm_list_t *i;
|
||||
alpm_event_t event = {0};
|
||||
|
@ -1177,6 +1251,8 @@ int SYMEXPORT alpm_fetch_pkgurl(alpm_handle_t *handle, const alpm_list_t *urls,
|
|||
|
||||
/* find a valid cache dir to download to */
|
||||
cachedir = _alpm_filecache_setup(handle);
|
||||
temporary_cachedir = _alpm_temporary_download_dir_setup(cachedir, handle->sandboxuser);
|
||||
ASSERT(temporary_cachedir != NULL, return -1);
|
||||
|
||||
for(i = urls; i; i = i->next) {
|
||||
char *url = i->data;
|
||||
|
@ -1200,8 +1276,8 @@ int SYMEXPORT alpm_fetch_pkgurl(alpm_handle_t *handle, const alpm_list_t *urls,
|
|||
c = strrchr(url, '/');
|
||||
if(c != NULL && strstr(c, ".pkg") && payload->remote_name && strlen(payload->remote_name) > 0) {
|
||||
/* 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->destfile_name = _alpm_get_fullpath(temporary_cachedir, payload->remote_name, "");
|
||||
payload->tempfile_name = _alpm_get_fullpath(temporary_cachedir, payload->remote_name, ".part");
|
||||
payload->allow_resume = 1;
|
||||
|
||||
if(!payload->destfile_name || !payload->tempfile_name) {
|
||||
|
@ -1215,7 +1291,7 @@ int SYMEXPORT alpm_fetch_pkgurl(alpm_handle_t *handle, const alpm_list_t *urls,
|
|||
payload->unlink_on_fail = 1;
|
||||
|
||||
payload->tempfile_openmode = "wb";
|
||||
payload->localf = create_tempfile(payload, cachedir);
|
||||
payload->localf = create_tempfile(payload, temporary_cachedir);
|
||||
if(payload->localf == NULL) {
|
||||
goto err;
|
||||
}
|
||||
|
@ -1232,7 +1308,7 @@ int SYMEXPORT alpm_fetch_pkgurl(alpm_handle_t *handle, const alpm_list_t *urls,
|
|||
event.type = ALPM_EVENT_PKG_RETRIEVE_START;
|
||||
event.pkg_retrieve.num = alpm_list_count(payloads);
|
||||
EVENT(handle, &event);
|
||||
if(_alpm_download(handle, payloads, cachedir) == -1) {
|
||||
if(_alpm_download(handle, payloads, cachedir, temporary_cachedir) == -1) {
|
||||
_alpm_log(handle, ALPM_LOG_WARNING, _("failed to retrieve some files\n"));
|
||||
event.type = ALPM_EVENT_PKG_RETRIEVE_FAILED;
|
||||
EVENT(handle, &event);
|
||||
|
@ -1265,10 +1341,14 @@ int SYMEXPORT alpm_fetch_pkgurl(alpm_handle_t *handle, const alpm_list_t *urls,
|
|||
FREELIST(payloads);
|
||||
}
|
||||
|
||||
_alpm_remove_temporary_download_dir(temporary_cachedir);
|
||||
FREE(temporary_cachedir);
|
||||
return 0;
|
||||
|
||||
err:
|
||||
alpm_list_free_inner(payloads, (alpm_list_fn_free)_alpm_dload_payload_reset);
|
||||
_alpm_remove_temporary_download_dir(temporary_cachedir);
|
||||
FREE(temporary_cachedir);
|
||||
FREELIST(payloads);
|
||||
FREELIST(*fetched);
|
||||
|
||||
|
|
|
@ -64,6 +64,7 @@ void _alpm_dload_payload_reset(struct dload_payload *payload);
|
|||
|
||||
int _alpm_download(alpm_handle_t *handle,
|
||||
alpm_list_t *payloads /* struct dload_payload */,
|
||||
const char *localpath);
|
||||
const char *localpath,
|
||||
const char *temporary_localpath);
|
||||
|
||||
#endif /* ALPM_DLOAD_H */
|
||||
|
|
|
@ -773,12 +773,18 @@ static int find_dl_candidates(alpm_handle_t *handle, alpm_list_t **files)
|
|||
static int download_files(alpm_handle_t *handle)
|
||||
{
|
||||
const char *cachedir;
|
||||
char * temporary_cachedir = NULL;
|
||||
alpm_list_t *i, *files = NULL;
|
||||
int ret = 0;
|
||||
alpm_event_t event = {0};
|
||||
alpm_list_t *payloads = NULL;
|
||||
|
||||
cachedir = _alpm_filecache_setup(handle);
|
||||
temporary_cachedir = _alpm_temporary_download_dir_setup(cachedir, handle->sandboxuser);
|
||||
if(temporary_cachedir == NULL) {
|
||||
ret = -1;
|
||||
goto finish;
|
||||
}
|
||||
handle->trans->state = STATE_DOWNLOADING;
|
||||
|
||||
ret = find_dl_candidates(handle, &files);
|
||||
|
@ -802,7 +808,7 @@ static int download_files(alpm_handle_t *handle)
|
|||
file_sizes[idx] = pkg->size;
|
||||
}
|
||||
|
||||
ret = _alpm_check_downloadspace(handle, cachedir, num_files, file_sizes);
|
||||
ret = _alpm_check_downloadspace(handle, temporary_cachedir, num_files, file_sizes);
|
||||
free(file_sizes);
|
||||
|
||||
if(ret != 0) {
|
||||
|
@ -830,8 +836,8 @@ static int download_files(alpm_handle_t *handle)
|
|||
STRDUP(payload->filepath, pkg->filename,
|
||||
_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");
|
||||
payload->destfile_name = _alpm_get_fullpath(temporary_cachedir, payload->remote_name, "");
|
||||
payload->tempfile_name = _alpm_get_fullpath(temporary_cachedir, payload->remote_name, ".part");
|
||||
if(!payload->destfile_name || !payload->tempfile_name) {
|
||||
_alpm_dload_payload_reset(payload);
|
||||
FREE(payload);
|
||||
|
@ -848,7 +854,7 @@ static int download_files(alpm_handle_t *handle)
|
|||
payloads = alpm_list_add(payloads, payload);
|
||||
}
|
||||
|
||||
ret = _alpm_download(handle, payloads, cachedir);
|
||||
ret = _alpm_download(handle, payloads, cachedir, temporary_cachedir);
|
||||
if(ret == -1) {
|
||||
event.type = ALPM_EVENT_PKG_RETRIEVE_FAILED;
|
||||
EVENT(handle, &event);
|
||||
|
@ -874,6 +880,8 @@ finish:
|
|||
pkg->infolevel &= ~INFRQ_DSIZE;
|
||||
pkg->download_size = 0;
|
||||
}
|
||||
_alpm_remove_temporary_download_dir(temporary_cachedir);
|
||||
FREE(temporary_cachedir);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -31,9 +31,11 @@
|
|||
#include <limits.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <fcntl.h>
|
||||
#include <fnmatch.h>
|
||||
#include <poll.h>
|
||||
#include <pwd.h>
|
||||
#include <signal.h>
|
||||
|
||||
/* libarchive */
|
||||
|
@ -946,6 +948,76 @@ const char *_alpm_filecache_setup(alpm_handle_t *handle)
|
|||
return cachedir;
|
||||
}
|
||||
|
||||
/** Create a temporary directory under the supplied directory.
|
||||
* The new directory is writable by the download user, and will be
|
||||
* removed after the download operation has completed.
|
||||
* @param dir existing sync or cache directory
|
||||
* @param user download user name
|
||||
* @return pointer to a sub-directory writable by the download user inside the existing directory.
|
||||
*/
|
||||
char *_alpm_temporary_download_dir_setup(const char *dir, const char *user)
|
||||
{
|
||||
struct passwd const *pw = NULL;
|
||||
|
||||
ASSERT(dir != NULL, return NULL);
|
||||
if(user != NULL) {
|
||||
ASSERT((pw = getpwnam(user)) != NULL, return NULL);
|
||||
}
|
||||
|
||||
const char template[] = "download-XXXXXX";
|
||||
size_t newdirlen = strlen(dir) + sizeof(template) + 1;
|
||||
char *newdir = NULL;
|
||||
MALLOC(newdir, newdirlen, return NULL);
|
||||
snprintf(newdir, newdirlen - 1, "%s%s", dir, template);
|
||||
newdir = mkdtemp(newdir);
|
||||
if(newdir == NULL) {
|
||||
free(newdir);
|
||||
return NULL;
|
||||
}
|
||||
if(pw != NULL) {
|
||||
if(chown(newdir, pw->pw_uid, pw->pw_gid) == -1) {
|
||||
free(newdir);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
newdir[newdirlen-2] = '/';
|
||||
newdir[newdirlen-1] = 0;
|
||||
return newdir;
|
||||
}
|
||||
|
||||
/** Remove a temporary directory.
|
||||
* The temporary download directory is removed after deleting any
|
||||
* leftover files.
|
||||
* @param dir directory to be removed
|
||||
*/
|
||||
void _alpm_remove_temporary_download_dir(const char *dir)
|
||||
{
|
||||
ASSERT(dir != NULL, return);
|
||||
size_t dirlen = strlen(dir);
|
||||
struct dirent *dp = NULL;
|
||||
DIR *dirp = opendir(dir);
|
||||
if(!dirp) {
|
||||
return;
|
||||
}
|
||||
for(dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
|
||||
if(strcmp(dp->d_name, "..") != 0 && strcmp(dp->d_name, ".") != 0) {
|
||||
char name[PATH_MAX];
|
||||
if(dirlen + strlen(dp->d_name) + 2 > PATH_MAX) {
|
||||
/* file path is too long to remove, hmm. */
|
||||
continue;
|
||||
} else {
|
||||
sprintf(name, "%s/%s", dir, dp->d_name);
|
||||
if(unlink(name)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
closedir(dirp);
|
||||
rmdir(dir);
|
||||
}
|
||||
|
||||
|
||||
#if defined HAVE_LIBSSL || defined HAVE_LIBNETTLE
|
||||
/** Compute the MD5 message digest of a file.
|
||||
* @param path file path of file to compute MD5 digest of
|
||||
|
|
|
@ -139,6 +139,9 @@ char *_alpm_filecache_find(alpm_handle_t *handle, const char *filename);
|
|||
/* Checks whether a file exists in cache */
|
||||
int _alpm_filecache_exists(alpm_handle_t *handle, const char *filename);
|
||||
const char *_alpm_filecache_setup(alpm_handle_t *handle);
|
||||
char *_alpm_temporary_download_dir_setup(const char *dir, const char *user);
|
||||
void _alpm_remove_temporary_download_dir(const char *dir);
|
||||
|
||||
/* Unlike many uses of alpm_pkgvalidation_t, _alpm_test_checksum expects
|
||||
* an enum value rather than a bitfield. */
|
||||
int _alpm_test_checksum(const char *filepath, const char *expected, alpm_pkgvalidation_t type);
|
||||
|
|
Loading…
Add table
Reference in a new issue