pull-job.c revision b6e676ce41508e2aeea22202fc8f234126177f52
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2015 Lennart Poettering
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include "strv.h"
#include "machine-pool.h"
#include "pull-job.h"
if (!j)
return NULL;
safe_close(j->disk_fd);
import_compress_free(&j->compress);
if (j->checksum_context)
free(j);
return NULL;
}
assert(j);
if (j->state == PULL_JOB_DONE ||
j->state == PULL_JOB_FAILED)
return;
if (ret == 0) {
j->state = PULL_JOB_DONE;
j->progress_percent = 100;
} else {
j->state = PULL_JOB_FAILED;
}
if (j->on_finished)
j->on_finished(j);
}
long status;
int r;
return;
return;
r = -EIO;
goto finish;
}
r = -EIO;
goto finish;
} else if (status == 304) {
log_info("Image already downloaded. Skipping download.");
j->etag_exists = true;
r = 0;
goto finish;
} else if (status >= 300) {
r = -EIO;
goto finish;
} else if (status < 200) {
r = -EIO;
goto finish;
}
if (j->state != PULL_JOB_RUNNING) {
log_error("Premature connection termination.");
r = -EIO;
goto finish;
}
j->content_length != j->written_compressed) {
log_error("Download truncated.");
r = -EIO;
goto finish;
}
if (j->checksum_context) {
uint8_t *k;
if (!k) {
log_error("Failed to get checksum.");
r = -EIO;
goto finish;
}
if (!j->checksum) {
r = log_oom();
goto finish;
}
}
if (j->disk_fd >= 0 && j->allow_sparse) {
/* Make sure the file size is right, in case the file was
* sparse and we just seeked for the last part */
goto finish;
}
if (j->etag)
if (j->url)
if (j->mtime != 0) {
}
}
r = 0;
pull_job_finish(j, r);
}
ssize_t n;
assert(j);
assert(p);
if (sz <= 0)
return 0;
log_error("File too large, overflow");
return -EOVERFLOW;
}
log_error("File overly large, refusing");
return -EFBIG;
}
if (j->disk_fd >= 0) {
j->written_since_last_grow = 0;
}
if (j->allow_sparse)
else
if (n < 0)
log_error("Short write");
return -EIO;
}
} else {
return log_oom();
j->payload_size += sz;
}
j->written_uncompressed += sz;
j->written_since_last_grow += sz;
return 0;
}
int r;
assert(j);
assert(p);
if (sz <= 0)
return 0;
log_error("File too large, overflow");
return -EOVERFLOW;
}
log_error("File overly large, refusing.");
return -EFBIG;
}
log_error("Content length incorrect.");
return -EFBIG;
}
if (j->checksum_context)
if (r < 0)
return r;
j->written_compressed += sz;
return 0;
}
static int pull_job_open_disk(PullJob *j) {
int r;
assert(j);
if (j->on_open_disk) {
r = j->on_open_disk(j);
if (r < 0)
return r;
}
if (j->disk_fd >= 0) {
/* Check if we can do sparse files */
j->allow_sparse = true;
else {
j->allow_sparse = false;
}
}
if (j->calc_checksum) {
log_error("Failed to initialize hash context.");
return -EIO;
}
}
return 0;
}
static int pull_job_detect_compression(PullJob *j) {
int r;
assert(j);
if (r < 0)
return log_error_errno(r, "Failed to initialize compressor: %m");
if (r == 0)
return 0;
r = pull_job_open_disk(j);
if (r < 0)
return r;
/* Now, take the payload we read so far, and decompress it */
stub_size = j->payload_size;
j->payload_size = 0;
j->payload_allocated = 0;
j->state = PULL_JOB_RUNNING;
if (r < 0)
return r;
return 0;
}
int r;
assert(j);
switch (j->state) {
case PULL_JOB_ANALYZING:
/* Let's first check what it actually is */
r = log_oom();
goto fail;
}
j->payload_size += sz;
r = pull_job_detect_compression(j);
if (r < 0)
goto fail;
break;
case PULL_JOB_RUNNING:
if (r < 0)
goto fail;
break;
case PULL_JOB_DONE:
case PULL_JOB_FAILED:
r = -ESTALE;
goto fail;
default:
assert_not_reached("Impossible state.");
}
return sz;
fail:
pull_job_finish(j, r);
return 0;
}
char *etag;
int r;
assert(j);
r = -ESTALE;
goto fail;
}
if (r < 0) {
log_oom();
goto fail;
}
if (r > 0) {
log_info("Image already downloaded. Skipping download.");
j->etag_exists = true;
pull_job_finish(j, 0);
return sz;
}
return sz;
}
if (r < 0) {
log_oom();
goto fail;
}
if (r > 0) {
char bytes[FORMAT_BYTES_MAX];
if (j->content_length > j->compressed_max) {
log_error("Content too large.");
r = -EFBIG;
goto fail;
}
}
return sz;
}
if (r < 0) {
log_oom();
goto fail;
}
if (r > 0) {
return sz;
}
if (j->on_header) {
if (r < 0)
goto fail;
}
return sz;
fail:
pull_job_finish(j, r);
return 0;
}
static int pull_job_progress_callback(void *userdata, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) {
unsigned percent;
usec_t n;
assert(j);
if (dltotal <= 0)
return 0;
n = now(CLOCK_MONOTONIC);
if (n > j->last_status_usec + USEC_PER_SEC &&
percent != j->progress_percent &&
char buf[FORMAT_TIMESPAN_MAX];
char y[FORMAT_BYTES_MAX];
done = n - j->start_usec;
log_info("Got %u%% of %s. %s left at %s/s.",
j->url,
} else
j->progress_percent = percent;
j->last_status_usec = n;
if (j->on_progress)
j->on_progress(j);
}
return 0;
}
if (!j)
return -ENOMEM;
j->state = PULL_JOB_INIT;
j->disk_fd = -1;
if (!j->url)
return -ENOMEM;
*ret = j;
j = NULL;
return 0;
}
int pull_job_begin(PullJob *j) {
int r;
assert(j);
if (j->state != PULL_JOB_INIT)
return -EBUSY;
if (j->grow_machine_directory)
if (r < 0)
return r;
if (!strv_isempty(j->old_etags)) {
if (!cc)
return -ENOMEM;
if (!hdr)
return -ENOMEM;
if (!j->request_header) {
if (!j->request_header)
return -ENOMEM;
} else {
struct curl_slist *l;
if (!l)
return -ENOMEM;
j->request_header = l;
}
}
if (j->request_header) {
return -EIO;
}
return -EIO;
return -EIO;
return -EIO;
return -EIO;
return -EIO;
return -EIO;
return -EIO;
if (r < 0)
return r;
j->state = PULL_JOB_ANALYZING;
return 0;
}