Commit dee6ffa4 authored by Timo Teräs's avatar Timo Teräs

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.
parent bd9835a2
......@@ -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);
......
......@@ -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)
......
......@@ -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)
......
......@@ -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) {
......
......@@ -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)
......
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