Changeset: 848010420450 for MonetDB URL: https://dev.monetdb.org/hg/MonetDB?cmd=changeset;node=848010420450 Modified Files: tools/merovingian/daemon/snapshot.c Branch: hot-snapshot Log Message:
Fsync appropriately when restoring a snapshot diffs (176 lines): diff --git a/tools/merovingian/daemon/snapshot.c b/tools/merovingian/daemon/snapshot.c --- a/tools/merovingian/daemon/snapshot.c +++ b/tools/merovingian/daemon/snapshot.c @@ -14,6 +14,7 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <sys/fcntl.h> #include <sys/stat.h> #include <sys/types.h> #include <time.h> @@ -24,6 +25,12 @@ #include "stream.h" #include "utils/database.h" +struct dir_helper { + char *dir; + int fd; +}; + +static err prepare_directory(struct dir_helper *state, char *prefix_dir, char *dir); static bool parse_snapshot_name(const char *filename, char **dbname, time_t *timestamp); static err validate_location(const char *path); static err unpack_tarstream(stream *tarstream, char *destdir, int skipcomponents); @@ -408,6 +415,106 @@ bailout: return e; } +/* Create the directory 'filename' is in, if necessary, and take care + * of fsync'ing it first when called with a different `dir`. Wrap up if + * filename is set to NULL. + */ +static err +prepare_directory(struct dir_helper *state, char *prefix_dir, char *filename) +{ + char *e = NO_ERR; + size_t file_len; + size_t prefix_len; + int ret; + int fd; + char *dir_buf = NULL; + char *dir = NULL; + + if (filename != NULL) { + dir_buf = strdup(filename); + dir = dirname(dir_buf); + } + + // If we are already tracking a directory and this one is the same, + // there's no need to do anything. + if (dir != NULL && state->dir != NULL && strcmp(state->dir, dir) == 0) { + free(dir_buf); + return NULL; + } + + // Before switching to the new directory, first fsync the old one, if any. + if (state->dir != NULL) { + if (fsync(state->fd) < 0) { + e = newErr("fsync %s: %s", state->dir, strerror(errno)); + goto bailout; + } + if (close(state->fd) < 0) { + e = newErr("close %s: %s", state->dir, strerror(errno)); + state->fd = 0; + goto bailout; + } + state->fd = -1; + free(state->dir); + state->dir = NULL; + } + + if (dir == NULL) { + // no new directory to set up, we just had to sync the current one + return NO_ERR; + } + + // Error checking on the new file. + file_len = strlen(filename); + prefix_len = strlen(prefix_dir); + if (prefix_len > file_len || strncmp(filename, prefix_dir, prefix_len) != 0) { + e = newErr("File '%s' is not inside prefix '%s'", filename, prefix_dir); + goto bailout; + } + + // Create any missing directories beyond the prefix. + for (char *p = dir + prefix_len + 1; *p != '\0'; p++) { + if (*p == '/') { + *p = '\0'; + ret = mkdir(dir, 0755); + if (ret < 0 && errno != EEXIST) { + e = newErr("Could not create directory %s: %s", dir, strerror(errno)); + *p = '/'; + goto bailout; + } + *p = '/'; + } + } + // The final part of the directory is not covered by the loop above + ret = mkdir(dir, 0755); + if (ret < 0 && errno != EEXIST) { + e = newErr("Could not create directory %s: %s", dir, strerror(errno)); + goto bailout; + } + + // Open the directory so we can fsync it later + fd = open(dir, O_RDONLY); + if (fd < 0) { + e = newErr("couldn't open directory %s: %s", dir, strerror(errno)); + goto bailout; + } + + state->fd = fd; + state->dir = strdup(dir); + + free(dir_buf); + return NULL; +bailout: + free(dir_buf); + if (state->fd > 0) { + close(state->fd); + state->fd = -1; + } + free(state->dir); + state->dir = NULL; + return e; +} + + #define TAR_BLOCK_SIZE (512) /* Read a full block from the stream. @@ -488,6 +595,7 @@ unpack_tarstream(stream *tarstream, char char *whole_filename = NULL; char *destfile = NULL; FILE *outfile = NULL; + struct dir_helper dirhelper = {NULL}; while (read_tar_block(tarstream, block, &e) >= 0) { if (e != NO_ERR) @@ -513,18 +621,8 @@ unpack_tarstream(stream *tarstream, char destfile = realloc(destfile, strlen(destdir) + 1 + strlen(useful_part) + 1); sprintf(destfile, "%s/%s", destdir, useful_part); - // Create any missing directories - for (char *p = destfile + strlen(destdir) + 1; *p != '\0'; p++) { - if (*p == '/') { - *p = '\0'; - int ret = mkdir(destfile, 0755); - if (ret < 0 && errno != EEXIST) { - e = newErr("Could not create directory %s: %s", destfile, strerror(errno)); - goto bailout; - } - *p = '/'; - } - } + // Create any missing directories and take care of fsync'ing them + prepare_directory(&dirhelper, destdir, destfile); // Copy the data outfile = fopen(destfile, "w"); @@ -550,9 +648,15 @@ unpack_tarstream(stream *tarstream, char } size -= TAR_BLOCK_SIZE; } + + if (fsync(fileno(outfile)) < 0) { + e = newErr("fsync %s: %s", destfile, strerror(errno)); + goto bailout; + } fclose(outfile); outfile = NULL; } + prepare_directory(&dirhelper, NULL, NULL); bailout: if (outfile != NULL) _______________________________________________ checkin-list mailing list checkin-list@monetdb.org https://www.monetdb.org/mailman/listinfo/checkin-list