From dee6ffa492c2efee982dcd0b4724213317eceb37 Mon Sep 17 00:00:00 2001 From: Timo Teras Date: Wed, 12 Aug 2009 11:05:09 +0300 Subject: io: better error handling when writing stuff out also have the output stream support writing to temporary file and do renameat/unlinkat on close depending on if all writes succeeded or not. --- src/apk_io.h | 6 +++--- src/database.c | 62 +++++++++++++++++++++++++++++++--------------------- src/gunzip.c | 13 +++++++---- src/index.c | 2 +- src/io.c | 68 +++++++++++++++++++++++++++++++++++++++++++++++----------- 5 files changed, 106 insertions(+), 45 deletions(-) diff --git a/src/apk_io.h b/src/apk_io.h index 53b91b3..68f9925 100644 --- a/src/apk_io.h +++ b/src/apk_io.h @@ -46,7 +46,7 @@ struct apk_bstream { struct apk_ostream { ssize_t (*write)(void *stream, const void *buf, size_t size); - void (*close)(void *stream); + int (*close)(void *stream); }; #define APK_MPART_DATA 1 /* data processed so far */ @@ -81,8 +81,8 @@ struct apk_bstream *apk_bstream_from_url(const char *url); struct apk_bstream *apk_bstream_tee(struct apk_bstream *from, int atfd, const char *to); struct apk_ostream *apk_ostream_to_fd(int fd); -struct apk_ostream *apk_ostream_to_file(int atfd, const char *file, mode_t mode); -struct apk_ostream *apk_ostream_to_file_gz(int atfd, const char *file, mode_t mode); +struct apk_ostream *apk_ostream_to_file(int atfd, const char *file, const char *tmpfile, mode_t mode); +struct apk_ostream *apk_ostream_to_file_gz(int atfd, const char *file, const char *tmpfile, mode_t mode); size_t apk_ostream_write_string(struct apk_ostream *ostream, const char *string); apk_blob_t apk_blob_from_istream(struct apk_istream *istream, size_t size); diff --git a/src/database.c b/src/database.c index cafa47f..7b0900c 100644 --- a/src/database.c +++ b/src/database.c @@ -814,7 +814,10 @@ static int apk_db_index_write_nr_cache(struct apk_database *db) /* Write list of installed non-repository packages to * cached index file */ - os = apk_ostream_to_file(db->cache_fd, "installed.new", 0644); + os = apk_ostream_to_file(db->cache_fd, + "installed", + "installed.new", + 0644); if (os == NULL) return -1; @@ -826,11 +829,9 @@ static int apk_db_index_write_nr_cache(struct apk_database *db) if (r != 0) return r; } - - os->close(os); - if (renameat(db->cache_fd, "installed.new", - db->cache_fd, "installed") < 0) - return -errno; + r = os->close(os); + if (r < 0) + return r; return ctx.count; } @@ -1020,6 +1021,7 @@ struct write_ctx { int apk_db_write_config(struct apk_database *db) { struct apk_ostream *os; + int r; if (db->root == NULL) return 0; @@ -1029,34 +1031,40 @@ int apk_db_write_config(struct apk_database *db) return -1; } - os = apk_ostream_to_file(db->root_fd, "var/lib/apk/world.new", 0644); + os = apk_ostream_to_file(db->root_fd, + "var/lib/apk/world", + "var/lib/apk/world.new", + 0644); if (os == NULL) return -1; + apk_deps_write(db->world, os); os->write(os, "\n", 1); - os->close(os); - if (renameat(db->root_fd, "var/lib/apk/world.new", - db->root_fd, "var/lib/apk/world") < 0) - return -errno; + r = os->close(os); + if (r < 0) + return r; - os = apk_ostream_to_file(db->root_fd, "var/lib/apk/installed.new", 0644); + os = apk_ostream_to_file(db->root_fd, + "var/lib/apk/installed", + "var/lib/apk/installed.new", + 0644); if (os == NULL) return -1; apk_db_write_fdb(db, os); - os->close(os); - - if (renameat(db->root_fd, "var/lib/apk/installed.new", - db->root_fd, "var/lib/apk/installed") < 0) - return -errno; + r = os->close(os); + if (r < 0) + return r; - os = apk_ostream_to_file(db->root_fd, "var/lib/apk/scripts.tar.new", 0644); + os = apk_ostream_to_file(db->root_fd, + "var/lib/apk/scripts.tar", + "var/lib/apk/scripts.tar.new", + 0644); if (os == NULL) return -1; apk_db_scriptdb_write(db, os); - os->close(os); - if (renameat(db->root_fd, "var/lib/apk/scripts.tar.new", - db->root_fd, "var/lib/apk/scripts.tar") < 0) - return -errno; + r = os->close(os); + if (r < 0) + return r; unlinkat(db->root_fd, "var/lib/apk/scripts", 0); apk_db_index_write_nr_cache(db); @@ -1796,6 +1804,13 @@ static int apk_db_unpack_pkg(struct apk_database *db, if (ctx.replaces) free(ctx.replaces); + if (need_copy) { + if (r == 0) + renameat(db->cachetmp_fd, file, db->cache_fd, file); + else + unlinkat(db->cachetmp_fd, file, 0); + } + if (r != 0) { apk_error("%s-%s: %s", newpkg->name->name, newpkg->version, @@ -1808,9 +1823,6 @@ static int apk_db_unpack_pkg(struct apk_database *db, apk_db_migrate_files(db, newpkg); - if (need_copy) - renameat(db->cachetmp_fd, file, db->cache_fd, file); - return 0; err: if (!reinstall) diff --git a/src/gunzip.c b/src/gunzip.c index e5144a0..dd8d248 100644 --- a/src/gunzip.c +++ b/src/gunzip.c @@ -190,24 +190,29 @@ static ssize_t gzo_write(void *stream, const void *ptr, size_t size) return size; } -static void gzo_close(void *stream) +static int gzo_close(void *stream) { struct apk_gzip_ostream *gos = (struct apk_gzip_ostream *) stream; unsigned char buffer[1024]; size_t have; - int r; + int r, rc = 0; do { gos->zs.avail_out = sizeof(buffer); gos->zs.next_out = buffer; r = deflate(&gos->zs, Z_FINISH); have = sizeof(buffer) - gos->zs.avail_out; - gos->output->write(gos->output, buffer, have); + if (gos->output->write(gos->output, buffer, have) != have) + rc = -EIO; } while (r == Z_OK); - gos->output->close(gos->output); + r = gos->output->close(gos->output); + if (r != 0) + rc = r; deflateEnd(&gos->zs); free(stream); + + return rc; } struct apk_ostream *apk_ostream_gzip(struct apk_ostream *output) diff --git a/src/index.c b/src/index.c index fbf1bcb..d485afd 100644 --- a/src/index.c +++ b/src/index.c @@ -169,7 +169,7 @@ static int index_main(void *ctx, struct apk_database *db, int argc, char **argv) } if (ictx->output != NULL) - os = apk_ostream_to_file(AT_FDCWD, ictx->output, 0644); + os = apk_ostream_to_file(AT_FDCWD, ictx->output, NULL, 0644); else os = apk_ostream_to_fd(STDOUT_FILENO); if (ictx->method == APK_SIGN_GENERATE) { diff --git a/src/io.c b/src/io.c index 1b25355..40590a2 100644 --- a/src/io.c +++ b/src/io.c @@ -65,8 +65,10 @@ struct apk_istream *apk_istream_from_fd(int fd) return NULL; fis = malloc(sizeof(struct apk_fd_istream)); - if (fis == NULL) + if (fis == NULL) { + close(fd); return NULL; + } *fis = (struct apk_fd_istream) { .is.read = fdi_read, @@ -527,7 +529,11 @@ struct apk_istream *apk_istream_from_file_gz(int atfd, const char *file) struct apk_fd_ostream { struct apk_ostream os; - int fd; + int fd, rc; + + const char *file, *tmpfile; + int atfd; + size_t bytes; char buffer[1024]; }; @@ -555,8 +561,10 @@ static ssize_t fdo_flush(struct apk_fd_ostream *fos) if (fos->bytes == 0) return 0; - if ((r = safe_write(fos->fd, fos->buffer, fos->bytes)) != fos->bytes) + if ((r = safe_write(fos->fd, fos->buffer, fos->bytes)) != fos->bytes) { + fos->rc = r < 0 ? r : -EIO; return r; + } fos->bytes = 0; return 0; @@ -572,8 +580,12 @@ static ssize_t fdo_write(void *stream, const void *ptr, size_t size) r = fdo_flush(fos); if (r != 0) return r; - if (size >= sizeof(fos->buffer) / 2) - return safe_write(fos->fd, ptr, size); + if (size >= sizeof(fos->buffer) / 2) { + r = safe_write(fos->fd, ptr, size); + if (r != size) + fos->rc = r < 0 ? r : -EIO; + return r; + } } memcpy(&fos->buffer[fos->bytes], ptr, size); @@ -582,14 +594,28 @@ static ssize_t fdo_write(void *stream, const void *ptr, size_t size) return size; } -static void fdo_close(void *stream) +static int fdo_close(void *stream) { struct apk_fd_ostream *fos = container_of(stream, struct apk_fd_ostream, os); + int rc = fos->rc; fdo_flush(fos); - close(fos->fd); + if (fos->fd > STDERR_FILENO && + close(fos->fd) < 0) + rc = -errno; + + if (fos->tmpfile != NULL) { + if (rc == 0) + renameat(fos->atfd, fos->tmpfile, + fos->atfd, fos->file); + else + unlinkat(fos->atfd, fos->tmpfile, 0); + } + free(fos); + + return rc; } struct apk_ostream *apk_ostream_to_fd(int fd) @@ -600,8 +626,10 @@ struct apk_ostream *apk_ostream_to_fd(int fd) return NULL; fos = malloc(sizeof(struct apk_fd_ostream)); - if (fos == NULL) + if (fos == NULL) { + close(fd); return NULL; + } *fos = (struct apk_fd_ostream) { .os.write = fdo_write, @@ -612,17 +640,32 @@ struct apk_ostream *apk_ostream_to_fd(int fd) return &fos->os; } -struct apk_ostream *apk_ostream_to_file(int atfd, const char *file, mode_t mode) +struct apk_ostream *apk_ostream_to_file(int atfd, + const char *file, + const char *tmpfile, + mode_t mode) { + struct apk_ostream *os; int fd; - fd = openat(atfd, file, O_CREAT | O_RDWR | O_TRUNC, mode); + fd = openat(atfd, tmpfile ?: file, O_CREAT | O_RDWR | O_TRUNC, mode); if (fd < 0) return NULL; fcntl(fd, F_SETFD, FD_CLOEXEC); - return apk_ostream_to_fd(fd); + os = apk_ostream_to_fd(fd); + if (os == NULL) + return NULL; + + if (tmpfile != NULL) { + struct apk_fd_ostream *fos = + container_of(os, struct apk_fd_ostream, os); + fos->file = file; + fos->tmpfile = tmpfile; + fos->atfd = atfd; + } + return os; } struct apk_counter_ostream { @@ -639,12 +682,13 @@ static ssize_t co_write(void *stream, const void *ptr, size_t size) return size; } -static void co_close(void *stream) +static int co_close(void *stream) { struct apk_counter_ostream *cos = container_of(stream, struct apk_counter_ostream, os); free(cos); + return 0; } struct apk_ostream *apk_ostream_counter(off_t *counter) -- cgit v1.2.3