diff options
Diffstat (limited to 'store')
| -rw-r--r-- | store/store-log.c | 24 | ||||
| -rw-r--r-- | store/test-log.c | 30 |
2 files changed, 38 insertions, 16 deletions
diff --git a/store/store-log.c b/store/store-log.c index 6138a84..f73d2bc 100644 --- a/store/store-log.c +++ b/store/store-log.c @@ -478,7 +478,7 @@ static bool image_name(char out[64], const char *name, const char *prefix) { return true; } -PureResult pure_image_write(const char *directory, const char *name, const PureSpan *parts, size_t count) { +PureResult pure_image_write(const char *directory, const char *name, bool (*next)(void *, PureSpan *chunk), void *context) { char final[64], temporary[96]; if (!image_name(final, name, "")) return PURE_INVALID; static uint64_t sequence; @@ -488,19 +488,17 @@ PureResult pure_image_write(const char *directory, const char *name, const PureS if (dir < 0) return PURE_STORE_ERROR; int fd = openat(dir, temporary, O_WRONLY | O_CREAT | O_EXCL | O_NOFOLLOW | O_CLOEXEC, 0600); if (fd < 0) { close(dir); return PURE_STORE_ERROR; } - uint64_t length = 0; - for (size_t i = 0; i < count; ++i) length += parts[i].size; unsigned char head[16]; memcpy(head, image_magic, 8); - put64(head + 8, length); + put64(head + 8, 0); /* its length, once known */ Digest digest = {0}; - bool ok = write_all(fd, head, sizeof(head), 0); + bool ok = write_all(fd, head, sizeof(head), 0), produced = true; uint64_t at = sizeof(head), started = 0; - for (size_t i = 0; ok && i < count; ++i) - for (size_t done = 0; ok && done < parts[i].size;) { - size_t take = parts[i].size - done < STEP ? parts[i].size - done : STEP; - digest_add(&digest, parts[i].data + done, take); - ok = write_all(fd, parts[i].data + done, take, at); + for (PureSpan chunk; ok && (produced = next(context, &chunk)) && chunk.size;) + for (size_t done = 0; ok && done < chunk.size;) { + size_t take = chunk.size - done < STEP ? chunk.size - done : STEP; + digest_add(&digest, chunk.data + done, take); + ok = write_all(fd, chunk.data + done, take, at); done += take; at += take; #if defined(__linux__) @@ -517,13 +515,15 @@ PureResult pure_image_write(const char *directory, const char *name, const PureS } unsigned char tail[8]; put64(tail, digest_end(&digest)); - ok = ok && write_all(fd, tail, sizeof(tail), at) && !fault(PURE_IMAGE_BEFORE_RENAME) && sync_data(fd); + put64(head + 8, at - sizeof(head)); + ok = ok && produced && write_all(fd, tail, sizeof(tail), at) && write_all(fd, head, sizeof(head), 0) && + !fault(PURE_IMAGE_BEFORE_RENAME) && sync_data(fd); if (close(fd)) ok = false; ok = ok && !renameat(dir, temporary, dir, final); if (!ok) unlinkat(dir, temporary, 0); ok = ok && sync_whole(dir); close(dir); - return ok ? PURE_OK : PURE_STORE_ERROR; + return ok ? PURE_OK : produced ? PURE_STORE_ERROR : PURE_ALLOCATION; } PureResult pure_image_read(const char *directory, const char *name, unsigned char **bytes, size_t *size) { diff --git a/store/test-log.c b/store/test-log.c index d4382e9..451f354 100644 --- a/store/test-log.c +++ b/store/test-log.c @@ -191,6 +191,25 @@ static void logs(void) { retracted, ended, most_files); } +typedef struct { const PureSpan *parts; size_t count, at; } Parts; + +static bool next_part(void *context, PureSpan *chunk) { + Parts *p = context; + *chunk = p->at < p->count ? p->parts[p->at++] : (PureSpan){0}; + if (chunk->data && !chunk->size) return next_part(context, chunk); /* an empty part is not the end */ + return true; +} + +static PureResult write_parts(const char *path, const char *name, const PureSpan *parts, size_t count) { + Parts p = {parts, count, 0}; + return pure_image_write(path, name, next_part, &p); +} + +static bool refuse(void *context, PureSpan *chunk) { + (void)context, (void)chunk; + return false; +} + static void images(void) { char *path = new_directory(); unsigned char *bytes; @@ -200,18 +219,18 @@ static void images(void) { CHECK(big); for (size_t i = 0; i < (3u << 20); ++i) big[i] = (unsigned char)(i * 131 + (i >> 11)); PureSpan parts[3] = {{(const unsigned char *)"head", 4}, {big, 3u << 20}, {(const unsigned char *)"", 0}}; - CHECK(pure_image_write(path, "tasks", parts, 3) == PURE_OK); + CHECK(write_parts(path, "tasks", parts, 3) == PURE_OK); CHECK(pure_image_read(path, "tasks", &bytes, &size) == PURE_OK && size == 4 + (3u << 20)); CHECK(!memcmp(bytes, "head", 4) && !memcmp(bytes + 4, big, 3u << 20)); free(bytes); /* A failure before the rename leaves the image as it was, and no temporary file. */ fail_stage = PURE_IMAGE_BEFORE_RENAME; PureSpan other = {(const unsigned char *)"other", 5}; - CHECK(pure_image_write(path, "tasks", &other, 1) == PURE_STORE_ERROR); + CHECK(write_parts(path, "tasks", &other, 1) == PURE_STORE_ERROR); fail_stage = -1; CHECK(pure_image_read(path, "tasks", &bytes, &size) == PURE_OK && size == 4 + (3u << 20)); free(bytes); - CHECK(pure_image_write(path, "tasks", &other, 1) == PURE_OK); + CHECK(write_parts(path, "tasks", &other, 1) == PURE_OK); CHECK(pure_image_read(path, "tasks", &bytes, &size) == PURE_OK && size == 5 && !memcmp(bytes, "other", 5)); free(bytes); /* Damaged: refused. */ @@ -221,7 +240,10 @@ static void images(void) { CHECK(fd >= 0 && pwrite(fd, "X", 1, 17) == 1); close(fd); CHECK(pure_image_read(path, "tasks", &bytes, &size) == PURE_STORE_ERROR); - CHECK(pure_image_write(path, "bad/name", &other, 1) == PURE_INVALID); + CHECK(write_parts(path, "bad/name", &other, 1) == PURE_INVALID); + /* A producer that fails replaces nothing. */ + CHECK(pure_image_write(path, "tasks", refuse, NULL) == PURE_ALLOCATION); + CHECK(pure_image_read(path, "tasks", &bytes, &size) == PURE_STORE_ERROR); /* still the damaged one */ CHECK(pure_image_remove(path, "tasks") == PURE_OK && pure_image_read(path, "tasks", &bytes, &size) == PURE_NOT_FOUND); free(big); remove_directory(path); |
