import-job.c revision 56ebfaf1ca185a93ffb372b6e1a1fa3a957d93cd
/*-*- 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 "import-job.h"
if (!j)
return NULL;
safe_close(j->disk_fd);
if (j->compressed == IMPORT_JOB_XZ)
else if (j->compressed == IMPORT_JOB_GZIP)
inflateEnd(&j->gzip);
free(j);
return NULL;
}
assert(j);
if (j->state == IMPORT_JOB_DONE ||
j->state == IMPORT_JOB_FAILED)
return;
if (ret == 0)
j->state = IMPORT_JOB_DONE;
else {
j->state = IMPORT_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.");
r = 0;
goto finish;
} else if (status >= 300) {
r = -EIO;
goto finish;
} else if (status < 200) {
r = -EIO;
goto finish;
}
if (j->state != IMPORT_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->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 */
r = -errno;
goto finish;
}
if (j->etag)
if (j->url)
if (j->mtime != 0) {
}
}
r = 0;
import_job_finish(j, r);
}
ssize_t n;
assert(j);
assert(p);
log_error("File too large, overflow");
return -EOVERFLOW;
}
log_error("File overly large, refusing");
return -EFBIG;
}
if (j->disk_fd >= 0) {
if (j->allow_sparse)
else
if (n < 0) {
return -errno;
}
log_error("Short write");
return -EIO;
}
} else {
return log_oom();
j->payload_size += sz;
}
j->written_uncompressed += sz;
return 0;
}
int r;
assert(j);
assert(p);
log_error("File too large, overflow");
return -EOVERFLOW;
}
log_error("File overly large, refusing.");
return -EFBIG;
}
log_error("Content length incorrect.");
return -EFBIG;
}
switch (j->compressed) {
case IMPORT_JOB_UNCOMPRESSED:
r = import_job_write_uncompressed(j, p, sz);
if (r < 0)
return r;
break;
case IMPORT_JOB_XZ:
log_error("Decompression error.");
return -EIO;
}
if (r < 0)
return r;
}
break;
case IMPORT_JOB_GZIP:
if (r != Z_OK && r != Z_STREAM_END) {
log_error("Decompression error.");
return -EIO;
}
if (r < 0)
return r;
}
break;
default:
assert_not_reached("Unknown compression");
}
j->written_compressed += sz;
return 0;
}
static int import_job_open_disk(ImportJob *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;
}
}
return 0;
}
static int import_job_detect_compression(ImportJob *j) {
static const uint8_t xz_signature[] = {
0xfd, '7', 'z', 'X', 'Z', 0x00
};
static const uint8_t gzip_signature[] = {
0x1f, 0x8b
};
int r;
assert(j);
return 0;
j->compressed = IMPORT_JOB_XZ;
j->compressed = IMPORT_JOB_GZIP;
else
if (j->compressed == IMPORT_JOB_XZ) {
log_error("Failed to initialize XZ decoder.");
return -EIO;
}
}
if (j->compressed == IMPORT_JOB_GZIP) {
if (r != Z_OK) {
log_error("Failed to initialize gzip decoder.");
return -EIO;
}
}
r = import_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->state = IMPORT_JOB_RUNNING;
if (r < 0)
return r;
return 0;
}
static size_t import_job_write_callback(void *contents, size_t size, size_t nmemb, void *userdata) {
int r;
assert(j);
switch (j->state) {
case IMPORT_JOB_ANALYZING:
/* Let's first check what it actually is */
r = log_oom();
goto fail;
}
j->payload_size += sz;
r = import_job_detect_compression(j);
if (r < 0)
goto fail;
break;
case IMPORT_JOB_RUNNING:
if (r < 0)
goto fail;
break;
case IMPORT_JOB_DONE:
case IMPORT_JOB_FAILED:
r = -ESTALE;
goto fail;
default:
assert_not_reached("Impossible state.");
}
return sz;
fail:
import_job_finish(j, r);
return 0;
}
static size_t import_job_header_callback(void *contents, size_t size, size_t nmemb, void *userdata) {
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.");
import_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;
}
return sz;
fail:
import_job_finish(j, r);
return 0;
}
static int import_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];
done = n - j->start_usec;
log_info("Got %u%% of %s. %s left.", percent, j->url, format_timespan(buf, sizeof(buf), left, USEC_PER_SEC));
} else
j->progress_percent = percent;
j->last_status_usec = n;
}
return 0;
}
if (!j)
return -ENOMEM;
j->state = IMPORT_JOB_INIT;
j->disk_fd = -1;
if (!j->url)
return -ENOMEM;
*ret = j;
j = NULL;
return 0;
}
int import_job_begin(ImportJob *j) {
int r;
assert(j);
if (j->state != IMPORT_JOB_INIT)
return -EBUSY;
if (r < 0)
return r;
if (!strv_isempty(j->old_etags)) {
if (!cc)
return -ENOMEM;
if (!hdr)
return -ENOMEM;
if (!j->request_header)
return -ENOMEM;
return -EIO;
}
return -EIO;
return -EIO;
return -EIO;
return -EIO;
return -EIO;
return -EIO;
return -EIO;
if (r < 0)
return r;
j->state = IMPORT_JOB_ANALYZING;
return 0;
}