Verified Commit ed17313f authored by Karel Koci's avatar Karel Koci 🤘

download: add possibility to download to temporally file

This uses standard function mkstemp to generate random file name,
anything else is same as in case of download_file.

This also changes behaviour of download_file so that it truncates file.
It makes much more sense because otherwise we would append data. If we
ever need append data then it would make sense to have dedicated option
for it.
parent 29d7a781
......@@ -237,30 +237,10 @@ static size_t download_write_callback(char *ptr, size_t size, size_t nmemb, void
return rsize;
}
static struct download_i *new_instance(struct downloader *downloader,
const char *url, const char *output_path, const struct download_opts *opts,
enum download_output_type type) {
static struct download_i *init_instance(struct download_i *inst,
struct downloader *downloader, const char *url,
const struct download_opts *opts, enum download_output_type type) {
// TODO TRACE configured options
struct download_i *inst = malloc(sizeof *inst);
switch (type) {
case DOWN_OUT_T_FILE:
inst->out.file = malloc(sizeof *inst->out.file);
// Note: For some reason umask seems to be sometime changed. So we set here our own explicitly.
inst->out.file->fd = open(output_path, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
if (inst->out.file->fd == -1) {
ERROR("(%s) Opening output file \"%s\" failed: %s", url, output_path, strerror(errno));
free(inst->out.file);
free(inst);
return NULL;
}
inst->out.file->fpath = strdup(output_path);
break;
case DOWN_OUT_T_BUFFER:
inst->out.buff = malloc(sizeof *inst->out.buff);
inst->out.buff->size = 0;
inst->out.buff->data = NULL;
break;
}
inst->done = false;
inst->success = false;
inst->retries = opts->retries;
......@@ -308,14 +288,46 @@ static struct download_i *new_instance(struct downloader *downloader,
struct download_i *download_file(struct downloader *downloader, const char *url,
const char *output_path, const struct download_opts *opts) {
struct download_i *inst = malloc(sizeof *inst);
inst->out.file = malloc(sizeof *inst->out.file);
// Note: For some reason umask seems to be sometime changed. So we set here our own explicitly.
inst->out.file->fd = open(output_path, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
if (inst->out.file->fd == -1) {
ERROR("(%s) Opening output file \"%s\" failed: %s", url, output_path, strerror(errno));
free(inst->out.file);
free(inst);
return NULL;
}
inst->out.file->fpath = strdup(output_path);
TRACE("Downloder: url %s to file %s", url, output_path);
return new_instance(downloader, url, output_path, opts, DOWN_OUT_T_FILE);
return init_instance(inst, downloader, url, opts, DOWN_OUT_T_FILE);
}
struct download_i *download_temp_file(struct downloader *downloader,
const char *url, char *output_template,
const struct download_opts *opts) {
struct download_i *inst = malloc(sizeof *inst);
inst->out.file = malloc(sizeof *inst->out.file);
inst->out.file->fd = mkstemp(output_template);
if (inst->out.file->fd == -1) {
ERROR("(%s) Opening temporally output file \"%s\" failed: %s", url, output_template, strerror(errno));
free(inst->out.file);
free(inst);
return NULL;
}
inst->out.file->fpath = strdup(output_template);
TRACE("Downloder: url %s to temporally file %s", url, output_template);
return init_instance(inst, downloader, url, opts, DOWN_OUT_T_FILE);
}
struct download_i *download_data(struct downloader *downloader, const char *url,
const struct download_opts *opts) {
TRACE("Downloder: url %s", url);
return new_instance(downloader, url, NULL, opts, DOWN_OUT_T_BUFFER);
struct download_i *inst = malloc(sizeof *inst);
inst->out.buff = malloc(sizeof *inst->out.buff);
inst->out.buff->size = 0;
inst->out.buff->data = NULL;
TRACE("Downloder: url %s to buffer", url);
return init_instance(inst, downloader, url, opts, DOWN_OUT_T_BUFFER);
}
void download_i_free(struct download_i *inst) {
......
......@@ -110,7 +110,19 @@ void download_opts_def(struct download_opts *opts) __attribute__((nonnull));
// Returns download instance
struct download_i *download_file(struct downloader *downloader, const char *url,
const char *output_path, const struct download_opts *opts)
__attribute__((nonnull(1, 2, 3, 4)));
__attribute__((nonnull(1, 2, 3, 4)));
// Register given URL to be downloaded to temporally file. Output file path is
// generated using mkstemp function.
// url: URL data are downloaded from
// output_template: Template for path where data are going to be stored (written
// to). Passed string has to end with XXXXXX and is modified to contain used
// path.
// opts: Download options
// Returns download instance
struct download_i *download_temp_file(struct downloader *downloader,
const char *url, char *output_template, const struct download_opts *opts)
__attribute__((nonnull(1, 2, 3, 4)));
// Register given URL to be downloaded to internal buffer.
// url: URL data are downloaded from
......
......@@ -31,7 +31,7 @@
START_TEST(downloader_empty) {
struct downloader *d = downloader_new(1);
ck_assert(downloader_run(d) == NULL);
ck_assert_ptr_null(downloader_run(d));
downloader_free(d);
}
END_TEST
......@@ -39,13 +39,13 @@ END_TEST
// Test simple download from http with redirect to https and Let's encrypt certificate
START_TEST(simple_download) {
struct downloader *d = downloader_new(1);
ck_assert(downloader_run(d) == NULL);
ck_assert_ptr_null(downloader_run(d));
struct download_opts opts;
download_opts_def(&opts);
struct download_i *inst = download_data(d, HTTP_SMALL, &opts);
ck_assert(downloader_run(d) == NULL);
ck_assert_ptr_null(downloader_run(d));
ck_assert_uint_eq(SMALL_SIZE, inst->out.buff->size);
ck_assert_str_eq(SMALL_CONTENT, (char *)inst->out.buff->data);
......@@ -57,7 +57,7 @@ END_TEST
// Test download to file. Otherwise it's same test as in case of simple_download.
START_TEST(file_download) {
struct downloader *d = downloader_new(1);
ck_assert(downloader_run(d) == NULL);
ck_assert_ptr_null(downloader_run(d));
struct download_opts opts;
download_opts_def(&opts);
......@@ -66,9 +66,9 @@ START_TEST(file_download) {
tmpdir = "/tmp";
char *file = aprintf("%s/updater-download.txt", tmpdir);
download_file(d, HTTP_SMALL, file, &opts);
ck_assert_ptr_nonnull(download_file(d, HTTP_SMALL, file, &opts));
ck_assert(downloader_run(d) == NULL);
ck_assert_ptr_null(downloader_run(d));
char *str = readfile(file);
ck_assert(str);
......@@ -82,12 +82,63 @@ START_TEST(file_download) {
}
END_TEST
// Test download to temporally file. We download different data to different
// files to test that having same template we end up with two different files.
START_TEST(temp_file_download) {
struct downloader *d = downloader_new(2);
ck_assert_ptr_null(downloader_run(d));
struct download_opts opts;
download_opts_def(&opts);
char *tmpdir = getenv("TMPDIR");
if (!tmpdir)
tmpdir = "/tmp";
char *file1 = aprintf("%s/updater-download-temp-XXXXXX", tmpdir);
char *file2 = aprintf("%s/updater-download-temp-XXXXXX", tmpdir);
ck_assert_str_eq(file1, file2); // Templates are same
ck_assert_ptr_nonnull(download_temp_file(d, HTTP_SMALL, file1, &opts));
ck_assert_ptr_nonnull(download_temp_file(d, HTTP_BIG, file2, &opts));
printf("1: %s 2: %s\n", file1, file2);
ck_assert_str_ne(file1, file2); // Paths are not same
ck_assert_ptr_null(downloader_run(d));
char *str = readfile(file1);
ck_assert(str);
ck_assert_uint_eq(SMALL_SIZE, strlen(str));
ck_assert_str_eq(SMALL_CONTENT, str);
free(str);
const char *s_dir = getenv("S");
if (!s_dir)
s_dir = ".";
char *lorem_ipsum_file = aprintf("%s/tests/data/lorem_ipsum.txt", s_dir);
char *big_content = readfile(lorem_ipsum_file);
size_t big_size = strlen(big_content);
str = readfile(file2);
ck_assert(str);
ck_assert_uint_eq(big_size, strlen(str));
ck_assert_str_eq(big_content, str);
free(str);
free(big_content);
unlink(file1);
unlink(file2);
downloader_free(d);
}
END_TEST
// Test that we can have multiple downloads and that all are downloaded
// Half of them are small file and half of them are bigger ones
// This test requires min. 20MB of memory.
START_TEST(multiple_downloads) {
struct downloader *d = downloader_new(4);
ck_assert(downloader_run(d) == NULL);
ck_assert_ptr_null(downloader_run(d));
struct download_opts opts;
download_opts_def(&opts);
......@@ -100,7 +151,7 @@ START_TEST(multiple_downloads) {
insts[i] = download_data(d, HTTP_BIG, &opts);
}
ck_assert(downloader_run(d) == NULL);
ck_assert_ptr_null(downloader_run(d));
const char *s_dir = getenv("S");
if (!s_dir)
......@@ -127,7 +178,7 @@ END_TEST
// Test failure if we access non-existent url
START_TEST(invalid) {
struct downloader *d = downloader_new(1);
ck_assert(downloader_run(d) == NULL);
ck_assert_ptr_null(downloader_run(d));
struct download_opts opts;
download_opts_def(&opts);
......@@ -142,7 +193,7 @@ END_TEST
// Test that even if one of download fail that all other will be downloaded
START_TEST(invalid_continue) {
struct downloader *d = downloader_new(4);
ck_assert(downloader_run(d) == NULL);
ck_assert_ptr_null(downloader_run(d));
struct download_opts opts;
download_opts_def(&opts);
......@@ -153,7 +204,7 @@ START_TEST(invalid_continue) {
struct download_i *fail_inst = download_data(d, HTTP_URL "/invalid", &opts);
ck_assert_ptr_eq(downloader_run(d), fail_inst);
ck_assert(downloader_run(d) == NULL);
ck_assert_ptr_null(downloader_run(d));
for (size_t i = 0; i < cnt; i++) {
ck_assert_uint_eq(SMALL_SIZE, insts[i]->out.buff->size);
......@@ -167,7 +218,7 @@ END_TEST
// Test certification pinning
START_TEST(cert_pinning) {
struct downloader *d = downloader_new(1);
ck_assert(downloader_run(d) == NULL);
ck_assert_ptr_null(downloader_run(d));
struct download_opts opts;
download_opts_def(&opts);
......@@ -179,7 +230,7 @@ START_TEST(cert_pinning) {
struct download_i *inst = download_data(d, HTTP_SMALL, &opts);
ck_assert(downloader_run(d) == NULL);
ck_assert_ptr_null(downloader_run(d));
ck_assert_uint_eq(SMALL_SIZE, inst->out.buff->size);
ck_assert_str_eq(SMALL_CONTENT, (char *)inst->out.buff->data);
......@@ -191,7 +242,7 @@ END_TEST
// Test failure if we try invalid certificate
START_TEST(cert_invalid) {
struct downloader *d = downloader_new(1);
ck_assert(downloader_run(d) == NULL);
ck_assert_ptr_null(downloader_run(d));
struct download_opts opts;
download_opts_def(&opts);
......@@ -219,6 +270,7 @@ Suite *gen_test_suite(void) {
tcase_add_test(down, simple_download);
tcase_add_test(down, multiple_downloads);
tcase_add_test(down, file_download);
tcase_add_test(down, temp_file_download);
tcase_add_test(down, invalid);
tcase_add_test(down, invalid_continue);
tcase_add_test(down, cert_pinning);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment