This will be used in later commits of this series to support multiple files for a single log.
Signed-off-by: Felix Huettner <felix.huettner@stackit.cloud> --- ovsdb/log.c | 566 ++++++++++++++++++++++++++---------------- tests/ovsdb-log.at | 20 +- tests/ovsdb-server.at | 4 +- 3 files changed, 364 insertions(+), 226 deletions(-) diff --git a/ovsdb/log.c b/ovsdb/log.c index 11d52f950..ac1ea0d58 100644 --- a/ovsdb/log.c +++ b/ovsdb/log.c @@ -74,19 +74,29 @@ enum ovsdb_log_state { OVSDB_LOG_BROKEN, /* Disk on fire, see 'error' for details. */ }; -struct ovsdb_log { - enum ovsdb_log_state state; - struct ovsdb_error *error; - off_t prev_offset; - off_t offset; +struct ovsdb_log_file { + off_t prev_offset; /* While reading the offset before the latest + * read call. */ + off_t offset; /* The current offset in the file. */ char *name; /* Absolute name of file. */ - char *display_name; /* For use in log messages, etc. */ - char *magic; struct lockfile *lockfile; FILE *stream; off_t base; struct afsync *afsync; + bool read_fully; +}; + +struct ovsdb_log { + enum ovsdb_log_state state; + struct ovsdb_error *error; + + enum ovsdb_log_open_mode open_mode; + bool may_lock; + char *display_name; /* For use in log messages, etc. */ + char *magic; + + struct ovsdb_log_file *curr; }; /* Whether the OS supports renaming open files. @@ -107,28 +117,12 @@ static bool is_magic_ok(const char *needle, const char *haystack); static struct afsync *afsync_create(int fd, uint64_t initial_ticket); static uint64_t afsync_destroy(struct afsync *); -/* Attempts to open 'name' with the specified 'open_mode'. On success, stores - * the new log into '*filep' and returns NULL; otherwise returns NULL and - * stores NULL into '*filep'. - * - * 'magic' is a short text string put at the beginning of every record and used - * to distinguish one kind of log file from another. For a conventional OVSDB - * log file, use the OVSDB_MAGIC macro. To accept more than one magic string, - * separate them with "|", e.g. "MAGIC 1|MAGIC 2". - * - * Whether the file will be locked using lockfile_lock() depends on 'may_lock': - * use true to lock if 'open_mode' allows writing, false not to never lock it. - * - * A log consists of a series of records. After opening or creating a log with - * this function, the client may use ovsdb_log_read() to read any existing - * records, one by one. The client may also use ovsdb_log_write() to write new - * records (if some records have not yet been read at this point, then the - * first write truncates them). - */ -struct ovsdb_error * -ovsdb_log_open(const char *name, const char *magic, - enum ovsdb_log_open_mode open_mode, - bool may_lock, struct ovsdb_log **filep) +static struct ovsdb_error * +ovsdb_log_file_open(const char *name, + const char *magic, + enum ovsdb_log_open_mode open_mode, + bool may_lock, struct ovsdb_log_file **filep, + char **out_actual_magic) { struct lockfile *lockfile; struct ovsdb_error *error; @@ -137,27 +131,6 @@ ovsdb_log_open(const char *name, const char *magic, int flags; int fd; - /* If we can create a new file, we need to know what kind of magic to - * use, so there must be only one kind. */ - if (open_mode == OVSDB_LOG_CREATE_EXCL || open_mode == OVSDB_LOG_CREATE) { - ovs_assert(!strchr(magic, '|')); - } - - *filep = NULL; - - /* Get the absolute name of the file because we might need to access it by - * name again later after the process has changed directory (e.g. because - * daemonize() chdirs to "/"). - * - * We save the user-provided name of the file for use in log messages, to - * reduce user confusion. */ - char *abs_name = abs_file_name(NULL, name); - if (!abs_name) { - error = ovsdb_io_error(0, "could not determine current " - "working directory"); - goto error; - } - if (may_lock && open_mode != OVSDB_LOG_READ_ONLY) { int retval = lockfile_lock(name, &lockfile); if (retval) { @@ -265,19 +238,18 @@ ovsdb_log_open(const char *name, const char *magic, goto error_fclose; } - struct ovsdb_log *file = xmalloc(sizeof *file); - file->state = OVSDB_LOG_READ; - file->error = NULL; - file->name = abs_name; - file->display_name = xstrdup(name); - file->magic = xstrdup(actual_magic); - file->lockfile = lockfile; - file->stream = stream; - file->prev_offset = 0; - file->offset = 0; - file->base = 0; - file->afsync = NULL; - *filep = file; + if (out_actual_magic) { + *out_actual_magic = xstrdup(actual_magic); + } + + *filep = xmalloc(sizeof **filep); + (*filep)->name = xstrdup(name); + (*filep)->lockfile = lockfile; + (*filep)->stream = stream; + (*filep)->prev_offset = 0; + (*filep)->offset = 0; + (*filep)->base = 0; + (*filep)->afsync = NULL; return NULL; error_fclose: @@ -285,10 +257,79 @@ error_fclose: error_unlock: lockfile_unlock(lockfile); error: - free(abs_name); return error; } +/* Attempts to open 'name' with the specified 'open_mode'. On success, stores + * the new log into '*filep' and returns NULL; otherwise returns NULL and + * stores NULL into '*filep'. + * + * 'magic' is a short text string put at the beginning of every record and used + * to distinguish one kind of log file from another. For a conventional OVSDB + * log file, use the OVSDB_MAGIC macro. To accept more than one magic string, + * separate them with "|", e.g. "MAGIC 1|MAGIC 2". + * + * Whether the file will be locked using lockfile_lock() depends on 'may_lock': + * use true to lock if 'open_mode' allows writing, false not to never lock it. + * + * A log consists of a series of records. After opening or creating a log with + * this function, the client may use ovsdb_log_read() to read any existing + * records, one by one. The client may also use ovsdb_log_write() to write new + * records (if some records have not yet been read at this point, then the + * first write truncates them). + */ +struct ovsdb_error * +ovsdb_log_open(const char *name, const char *magic, + enum ovsdb_log_open_mode open_mode, + bool may_lock, struct ovsdb_log **filep) +{ + struct ovsdb_log_file *file; + struct ovsdb_error *error; + char *actual_magic; + + /* If we can create a new file, we need to know what kind of magic to + * use, so there must be only one kind. */ + if (open_mode == OVSDB_LOG_CREATE_EXCL || + open_mode == OVSDB_LOG_CREATE) { + ovs_assert(!strchr(magic, '|')); + } + + *filep = NULL; + + /* Get the absolute name of the file because we might need to access it by + * name again later after the process has changed directory (e.g. because + * daemonize() chdirs to "/"). + * + * We save the user-provided name of the file for use in log messages, to + * reduce user confusion. */ + char *abs_name = abs_file_name(NULL, name); + if (!abs_name) { + error = ovsdb_io_error(0, "could not determine current " + "working directory"); + return error; + } + + error = ovsdb_log_file_open(abs_name, magic, open_mode, may_lock, + &file, &actual_magic); + free(abs_name); + if (error) { + return error; + } + + + struct ovsdb_log *log = xzalloc(sizeof *log); + log->state = OVSDB_LOG_READ; + log->error = NULL; + log->display_name = xstrdup(name); + log->magic = actual_magic; + log->open_mode = open_mode; + log->may_lock = may_lock; + + log->curr = file; + *filep = log; + return NULL; +} + /* Returns true if 'needle' is one of the |-delimited words in 'haystack'. */ static bool is_magic_ok(const char *needle, const char *haystack) @@ -311,19 +352,28 @@ is_magic_ok(const char *needle, const char *haystack) } } +static void +ovsdb_log_file_close(struct ovsdb_log_file *file) +{ + if (file) { + afsync_destroy(file->afsync); + free(file->name); + if (file->stream) { + fclose(file->stream); + } + lockfile_unlock(file->lockfile); + free(file); + } +} + void ovsdb_log_close(struct ovsdb_log *file) { if (file) { ovsdb_error_destroy(file->error); - afsync_destroy(file->afsync); - free(file->name); free(file->display_name); free(file->magic); - if (file->stream) { - fclose(file->stream); - } - lockfile_unlock(file->lockfile); + ovsdb_log_file_close(file->curr); free(file); } } @@ -397,13 +447,13 @@ parse_body(struct ovsdb_log *file, off_t offset, unsigned long int length, int chunk; chunk = MIN(length, sizeof input); - if (fread(input, 1, chunk, file->stream) != chunk) { + if (fread(input, 1, chunk, file->curr->stream) != chunk) { sha1_final(&ctx, sha1); json_parser_abort(parser); - return ovsdb_io_error(ferror(file->stream) ? errno : EOF, + return ovsdb_io_error(ferror(file->curr->stream) ? errno : EOF, "%s: error reading %lu bytes " "starting at offset %lld", - file->display_name, length, + file->curr->name, length, (long long int) offset); } sha1_update(&ctx, input, chunk); @@ -426,8 +476,8 @@ parse_body(struct ovsdb_log *file, off_t offset, unsigned long int length, * * If the read reaches end of file, returns NULL and stores NULL in * '*jsonp'. */ -struct ovsdb_error * -ovsdb_log_read(struct ovsdb_log *file, struct json **jsonp) +static struct ovsdb_error * +ovsdb_log_read_(struct ovsdb_log *file, struct json **jsonp) { *jsonp = NULL; switch (file->state) { @@ -452,22 +502,22 @@ ovsdb_log_read(struct ovsdb_log *file, struct json **jsonp) json = NULL; - if (!fgets(header, sizeof header, file->stream)) { - if (feof(file->stream)) { + if (!fgets(header, sizeof header, file->curr->stream)) { + if (feof(file->curr->stream)) { return NULL; } - error = ovsdb_io_error(errno, "%s: read failed", file->display_name); + error = ovsdb_io_error(errno, "%s: read failed", file->curr->name); goto error; } - off_t data_offset = file->offset + strlen(header); + off_t data_offset = file->curr->offset + strlen(header); const char *magic; if (!parse_header(header, &magic, &data_length, expected_sha1) || strcmp(magic, file->magic)) { error = ovsdb_syntax_error(NULL, NULL, "%s: parse error at offset " "%lld in header line \"%.*s\"", - file->display_name, - (long long int) file->offset, + file->curr->name, + (long long int) file->curr->offset, (int) strcspn(header, "\n"), header); goto error; } @@ -481,7 +531,7 @@ ovsdb_log_read(struct ovsdb_log *file, struct json **jsonp) error = ovsdb_syntax_error(NULL, NULL, "%s: %lu bytes starting at " "offset %lld have SHA-1 hash "SHA1_FMT" " "but should have hash "SHA1_FMT, - file->display_name, data_length, + file->curr->name, data_length, (long long int) data_offset, SHA1_ARGS(actual_sha1), SHA1_ARGS(expected_sha1)); @@ -491,7 +541,7 @@ ovsdb_log_read(struct ovsdb_log *file, struct json **jsonp) if (json->type == JSON_STRING) { error = ovsdb_syntax_error(NULL, NULL, "%s: %lu bytes starting at " "offset %lld are not valid JSON (%s)", - file->display_name, data_length, + file->curr->name, data_length, (long long int) data_offset, json_string(json)); goto error; @@ -499,22 +549,14 @@ ovsdb_log_read(struct ovsdb_log *file, struct json **jsonp) if (json->type != JSON_OBJECT) { error = ovsdb_syntax_error(NULL, NULL, "%s: %lu bytes starting at " "offset %lld are not a JSON object", - file->display_name, data_length, + file->curr->name, data_length, (long long int) data_offset); goto error; } - struct json *data = shash_find_and_delete(json_object(json), - OVSDB_LOG_DATA_KEY); - if (data) { - *jsonp = data; - json_destroy(json); - } else { - *jsonp = json; - } - - file->prev_offset = file->offset; - file->offset = data_offset + data_length; + file->curr->prev_offset = file->curr->offset; + file->curr->offset = data_offset + data_length; + *jsonp = json; return NULL; error: @@ -524,6 +566,53 @@ error: return error; } +/* Attempts to read a log record from 'file'. + * + * If successful, returns NULL and stores in '*jsonp' the JSON object that the + * record contains. The caller owns the data and must eventually free it (with + * json_destroy()). + * + * If a read error occurs, returns the error and stores NULL in '*jsonp'. + * + * If the read reaches end of file, returns NULL and stores NULL in + * '*jsonp'. */ +struct ovsdb_error * +ovsdb_log_read(struct ovsdb_log *log, struct json **jsonp) +{ + ovs_assert(log->curr); + *jsonp = NULL; + while (!*jsonp) { + struct ovsdb_error *error = ovsdb_log_read_(log, jsonp); + if (error) { + return error; + } + if (!*jsonp) { + /* EOF */ + break; + } + + struct json *metadata = shash_find_data(json_object(*jsonp), + OVSDB_LOG_META_KEY); + struct json *data = shash_find_and_delete(json_object(*jsonp), + OVSDB_LOG_DATA_KEY); + + if (!metadata && !data) { + /* Record that does not yet have the "logdata" key. */ + break; + } + + if (data) { + json_destroy(*jsonp); + *jsonp = data; + break; + } else { + OVS_NOT_REACHED(); + } + } + + return NULL; +} + /* Causes the log record read by the previous call to ovsdb_log_read() to be * effectively discarded. The next call to ovsdb_log_write() will overwrite * that previously read record. @@ -537,25 +626,31 @@ void ovsdb_log_unread(struct ovsdb_log *file) { ovs_assert(file->state == OVSDB_LOG_READ); - file->offset = file->prev_offset; + file->curr->offset = file->curr->prev_offset; +} + +static struct ovsdb_error * +ovsdb_log_file_truncate(struct ovsdb_log_file *file) +{ + struct ovsdb_error *error = NULL; + if (fseeko(file->stream, file->offset, SEEK_SET)) { + error = ovsdb_io_error(errno, "%s: cannot seek to offset %lld", + file->name, + (long long int) file->offset); + } else if (ftruncate(fileno(file->stream), file->offset)) { + error = ovsdb_io_error(errno, "%s: cannot truncate to length %lld", + file->name, + (long long int) file->offset); + } + + return error; } static struct ovsdb_error * ovsdb_log_truncate(struct ovsdb_log *file) { file->state = OVSDB_LOG_WRITE; - - struct ovsdb_error *error = NULL; - if (fseeko(file->stream, file->offset, SEEK_SET)) { - error = ovsdb_io_error(errno, "%s: cannot seek to offset %lld", - file->display_name, - (long long int) file->offset); - } else if (ftruncate(fileno(file->stream), file->offset)) { - error = ovsdb_io_error(errno, "%s: cannot truncate to length %lld", - file->display_name, - (long long int) file->offset); - } - return error; + return ovsdb_log_file_truncate(file->curr); } /* Removes all the data from the log by moving current offset to zero and @@ -565,7 +660,7 @@ struct ovsdb_error * OVS_WARN_UNUSED_RESULT ovsdb_log_reset(struct ovsdb_log *file) { ovsdb_error_destroy(file->error); - file->offset = file->prev_offset = 0; + file->curr->offset = file->curr->prev_offset = 0; file->error = ovsdb_log_truncate(file); if (file->error) { file->state = OVSDB_LOG_WRITE_ERROR; @@ -602,35 +697,12 @@ ovsdb_log_compose_record(const struct json *json, } static struct ovsdb_error * -ovsdb_log_write_(struct ovsdb_log *file, const struct json *json) +ovsdb_log_write_file(struct ovsdb_log *log, struct ovsdb_log_file *file, + const struct json *json) { - switch (file->state) { - case OVSDB_LOG_WRITE: - break; - - case OVSDB_LOG_READ: - case OVSDB_LOG_READ_ERROR: - case OVSDB_LOG_WRITE_ERROR: - ovsdb_error_destroy(file->error); - file->error = ovsdb_log_truncate(file); - if (file->error) { - file->state = OVSDB_LOG_WRITE_ERROR; - return ovsdb_error_clone(file->error); - } - file->state = OVSDB_LOG_WRITE; - break; - - case OVSDB_LOG_BROKEN: - return ovsdb_error_clone(file->error); - } - - if (json->type != JSON_OBJECT && json->type != JSON_ARRAY) { - return OVSDB_BUG("bad JSON type"); - } - struct ds header = DS_EMPTY_INITIALIZER; struct ds data = DS_EMPTY_INITIALIZER; - ovsdb_log_compose_record(json, file->magic, &header, &data); + ovsdb_log_compose_record(json, log->magic, &header, &data); size_t total_length = header.length + data.length; /* Write. */ @@ -650,16 +722,55 @@ ovsdb_log_write_(struct ovsdb_log *file, const struct json *json) * nothing further we can do. */ ignore(ftruncate(fileno(file->stream), file->offset)); - file->error = ovsdb_io_error(error, "%s: write failed", - file->display_name); - file->state = OVSDB_LOG_WRITE_ERROR; - return ovsdb_error_clone(file->error); + log->error = ovsdb_io_error(error, "%s: write failed", + file->name); + log->state = OVSDB_LOG_WRITE_ERROR; + return ovsdb_error_clone(log->error); } file->offset += total_length; return NULL; } +static struct ovsdb_error * +ovsdb_log_write_(struct ovsdb_log *log, const struct json *json) +{ + + if (log->open_mode == OVSDB_LOG_READ_ONLY) { + return ovsdb_error(NULL, "can not write to a readonly log"); + } + + switch (log->state) { + case OVSDB_LOG_WRITE: + break; + + case OVSDB_LOG_READ: + case OVSDB_LOG_READ_ERROR: + case OVSDB_LOG_WRITE_ERROR: + ovsdb_error_destroy(log->error); + log->error = ovsdb_log_truncate(log); + if (log->error) { + log->state = OVSDB_LOG_WRITE_ERROR; + return ovsdb_error_clone(log->error); + } + log->state = OVSDB_LOG_WRITE; + break; + + case OVSDB_LOG_BROKEN: + return ovsdb_error_clone(log->error); + } + + if (!json) { + return NULL; + } + + if (json->type != JSON_OBJECT && json->type != JSON_ARRAY) { + return OVSDB_BUG("bad JSON type"); + } + + return ovsdb_log_write_file(log, log->curr, json); +} + /* Writes log record 'json' to 'file'. Returns NULL if successful or an error * (which the caller must eventually destroy) on failure. * @@ -672,6 +783,7 @@ ovsdb_log_write_(struct ovsdb_log *file, const struct json *json) struct ovsdb_error * ovsdb_log_write(struct ovsdb_log *file, const struct json *json) { + ovs_assert(file->curr); struct json *outer = json_object_create(); json_object_put(outer, OVSDB_LOG_DATA_KEY, (struct json *) json); struct ovsdb_error *error = ovsdb_log_write_(file, outer); @@ -688,10 +800,8 @@ ovsdb_log_write_and_free(struct ovsdb_log *log, struct json *json) return error; } -/* Attempts to commit 'file' to disk. Waits for the commit to succeed or fail. - * Returns NULL if successful, otherwise the error that occurred. */ -struct ovsdb_error * -ovsdb_log_commit_block(struct ovsdb_log *file) +static struct ovsdb_error * +ovsdb_log_file_commit_block(struct ovsdb_log_file *file) { #if (_POSIX_C_SOURCE >= 199309L || _XOPEN_SOURCE >= 500) /* we do not check metadata - mtime, atime, anywhere, so we @@ -703,18 +813,27 @@ ovsdb_log_commit_block(struct ovsdb_log *file) #else if (file->stream && fsync(fileno(file->stream))) { #endif - return ovsdb_io_error(errno, "%s: fsync failed", file->display_name); + return ovsdb_io_error(errno, "%s: fsync failed", file->name); } return NULL; } +/* Attempts to commit 'file' to disk. Waits for the commit to succeed or fail. + * Returns NULL if successful, otherwise the error that occurred. */ +struct ovsdb_error * +ovsdb_log_commit_block(struct ovsdb_log *file) +{ + return ovsdb_log_file_commit_block(file->curr); +} + /* Sets the current position in 'log' as the "base", that is, the initial size * of the log that ovsdb_log_grew_lots() uses to determine whether the log has * grown enough to make compacting worthwhile. */ void ovsdb_log_mark_base(struct ovsdb_log *log) { - log->base = log->offset; + /* We only need to care about curr, since the others will never grow. */ + log->curr->base = log->curr->offset; } /* Returns true if 'log' has grown enough above the base that it's worthwhile @@ -722,7 +841,9 @@ ovsdb_log_mark_base(struct ovsdb_log *log) bool ovsdb_log_grew_lots(const struct ovsdb_log *log) { - return log->offset > 10 * 1024 * 1024 && log->offset / 2 > log->base; + /* We only need to care about curr, since the others will never grow. */ + return log->curr->offset > 10 * 1024 * 1024 && + log->curr->offset / 2 > log->curr->base; } /* Attempts to atomically replace the contents of 'log', on disk, by the 'n' @@ -753,35 +874,6 @@ ovsdb_log_replace(struct ovsdb_log *log, struct json **entries, size_t n) return ovsdb_log_replace_commit(log, new); } -struct ovsdb_error * OVS_WARN_UNUSED_RESULT -ovsdb_log_replace_start(struct ovsdb_log *old, - struct ovsdb_log **newp) -{ - /* If old->name is a symlink, then we want the new file to be in the same - * directory as the symlink's referent. */ - char *deref_name = follow_symlinks(old->name); - char *tmp_name = xasprintf("%s.tmp", deref_name); - free(deref_name); - - struct ovsdb_error *error; - - ovs_assert(old->lockfile); - - /* Remove temporary file. (It might not exist.) */ - if (unlink(tmp_name) < 0 && errno != ENOENT) { - error = ovsdb_io_error(errno, "failed to remove %s", tmp_name); - free(tmp_name); - *newp = NULL; - return error; - } - - /* Create temporary file. */ - error = ovsdb_log_open(tmp_name, old->magic, OVSDB_LOG_CREATE_EXCL, - false, newp); - free(tmp_name); - return error; -} - /* Rename 'old' to 'new', replacing 'new' if it exists. Returns NULL if * successful, otherwise an ovsdb_error that the caller must destroy. */ static struct ovsdb_error * OVS_WARN_UNUSED_RESULT @@ -802,6 +894,35 @@ ovsdb_rename(const char *old, const char *new) : NULL); } +struct ovsdb_error * OVS_WARN_UNUSED_RESULT +ovsdb_log_replace_start(struct ovsdb_log *old, + struct ovsdb_log **newp) +{ + ovs_assert(old->curr->lockfile); + + /* If old->name is a symlink, then we want the new file to be in the same + * directory as the symlink's referent. */ + char *deref_name = follow_symlinks(old->curr->name); + char *tmp_name = xasprintf("%s.tmp", deref_name); + free(deref_name); + + struct ovsdb_error *error; + + /* Remove temporary file. (It might not exist.) */ + if (unlink(tmp_name) < 0 && errno != ENOENT) { + error = ovsdb_io_error(errno, "failed to remove %s", tmp_name); + free(tmp_name); + *newp = NULL; + return error; + } + + /* Create temporary file. */ + error = ovsdb_log_open(tmp_name, old->magic, OVSDB_LOG_CREATE_EXCL, + false, newp); + free(tmp_name); + return error; +} + struct ovsdb_error * OVS_WARN_UNUSED_RESULT ovsdb_log_replace_commit(struct ovsdb_log *old, struct ovsdb_log *new) { @@ -830,40 +951,39 @@ ovsdb_log_replace_commit(struct ovsdb_log *old, struct ovsdb_log *new) * to test both strategies on Unix-like systems, and to make the code * easier to read. */ if (!rename_open_files) { - fclose(old->stream); - old->stream = NULL; + fclose(old->curr->stream); + old->curr->stream = NULL; - fclose(new->stream); - new->stream = NULL; + fclose(new->curr->stream); + new->curr->stream = NULL; } /* Rename 'old' to 'new'. We dereference the old name because, if it is a * symlink, we want to replace the referent of the symlink instead of the * symlink itself. */ - char *deref_name = follow_symlinks(old->name); - error = ovsdb_rename(new->name, deref_name); + char *deref_name = follow_symlinks(old->curr->name); + error = ovsdb_rename(new->curr->name, deref_name); free(deref_name); - if (error) { ovsdb_log_replace_abort(new); return error; } if (rename_open_files) { - fsync_parent_dir(old->name); - fclose(old->stream); - old->stream = new->stream; - new->stream = NULL; + fsync_parent_dir(old->curr->name); + old->curr->stream = new->curr->stream; + new->curr->stream = NULL; } else { - old->stream = fopen(old->name, "r+b"); - if (!old->stream) { + old->curr->stream = fopen(old->curr->name, "r+b"); + if (!old->curr->stream) { old->error = ovsdb_io_error(errno, "%s: could not reopen log", - old->name); + old->curr->name); old->state = OVSDB_LOG_BROKEN; return ovsdb_error_clone(old->error); } - if (fseek(old->stream, new->offset, SEEK_SET)) { - old->error = ovsdb_io_error(errno, "%s: seek failed", old->name); + if (fseek(old->curr->stream, new->curr->offset, SEEK_SET)) { + old->error = ovsdb_io_error(errno, "%s: seek failed", + old->curr->name); old->state = OVSDB_LOG_BROKEN; return ovsdb_error_clone(old->error); } @@ -877,17 +997,18 @@ ovsdb_log_replace_commit(struct ovsdb_log *old, struct ovsdb_log *new) ovsdb_error_destroy(old->error); old->error = NULL; /* prev_offset only matters for OVSDB_LOG_READ. */ - if (old->afsync) { - uint64_t ticket = afsync_destroy(old->afsync); - old->afsync = afsync_create(fileno(old->stream), ticket + 1); + if (old->curr->afsync) { + uint64_t ticket = afsync_destroy(old->curr->afsync); + old->curr->afsync = afsync_create(fileno(old->curr->stream), + ticket + 1); } - old->offset = new->offset; + old->curr->offset = new->curr->offset; + old->curr->base = new->curr->base; /* Keep old->name. */ free(old->magic); old->magic = new->magic; new->magic = NULL; /* Keep old->lockfile. */ - old->base = new->base; /* Free 'new'. */ ovsdb_log_close(new); @@ -901,7 +1022,8 @@ ovsdb_log_replace_abort(struct ovsdb_log *new) if (new) { /* Unlink the new file, but only after we close it (because Windows * does not allow removing an open file). */ - char *name = xstrdup(new->name); + char *name = new->curr->name; + new->curr->name = NULL; ovsdb_log_close(new); unlink(name); free(name); @@ -989,7 +1111,7 @@ afsync_destroy(struct afsync *afsync) } static struct afsync * -ovsdb_log_get_afsync(struct ovsdb_log *log) +ovsdb_log_get_afsync(struct ovsdb_log_file *log) { if (!log->afsync) { log->afsync = afsync_create(log->stream ? fileno(log->stream) : -1, 0); @@ -997,11 +1119,8 @@ ovsdb_log_get_afsync(struct ovsdb_log *log) return log->afsync; } -/* Starts committing 'log' to disk. Returns a ticket that can be passed to - * ovsdb_log_commit_wait() or compared against the return value of - * ovsdb_log_commit_progress() later. */ -uint64_t -ovsdb_log_commit_start(struct ovsdb_log *log) +static uint64_t +ovsdb_log_file_commit_start(struct ovsdb_log_file *log) { struct afsync *afsync = ovsdb_log_get_afsync(log); @@ -1013,12 +1132,17 @@ ovsdb_log_commit_start(struct ovsdb_log *log) return orig + 1; } -/* Returns a ticket value that represents the current progress of commits to - * 'log'. Suppose that some call to ovsdb_log_commit_start() returns X and any - * call ovsdb_log_commit_progress() returns Y, for the same 'log'. Then commit - * X is complete if and only if X <= Y. */ +/* Starts committing 'log' to disk. Returns a ticket that can be passed to + * ovsdb_log_commit_wait() or compared against the return value of + * ovsdb_log_commit_progress() later. */ uint64_t -ovsdb_log_commit_progress(struct ovsdb_log *log) +ovsdb_log_commit_start(struct ovsdb_log *log) +{ + return ovsdb_log_file_commit_start(log->curr); +} + +static uint64_t +ovsdb_log_file_commit_progress(struct ovsdb_log_file *log) { struct afsync *afsync = ovsdb_log_get_afsync(log); uint64_t cur; @@ -1026,17 +1150,33 @@ ovsdb_log_commit_progress(struct ovsdb_log *log) return cur; } -/* Causes poll_block() to wake up if and when ovsdb_log_commit_progress(log) - * would return at least 'goal'. */ -void -ovsdb_log_commit_wait(struct ovsdb_log *log, uint64_t goal) +/* Returns a ticket value that represents the current progress of commits to + * 'log'. Suppose that some call to ovsdb_log_commit_start() returns X and any + * call ovsdb_log_commit_progress() returns Y, for the same 'log'. Then commit + * X is complete if and only if X <= Y. */ +uint64_t +ovsdb_log_commit_progress(struct ovsdb_log *log) +{ + return ovsdb_log_file_commit_progress(log->curr); +} + +static void +ovsdb_log_file_commit_wait(struct ovsdb_log_file *log, uint64_t goal) { struct afsync *afsync = ovsdb_log_get_afsync(log); uint64_t complete = seq_read(afsync->complete); - uint64_t cur = ovsdb_log_commit_progress(log); + uint64_t cur = ovsdb_log_file_commit_progress(log); if (cur < goal) { seq_wait(afsync->complete, complete); } else { poll_immediate_wake(); } } + +/* Causes poll_block() to wake up if and when ovsdb_log_commit_progress(log) + * would return at least 'goal'. */ +void +ovsdb_log_commit_wait(struct ovsdb_log *log, uint64_t goal) +{ + ovsdb_log_file_commit_wait(log->curr, goal); +} diff --git a/tests/ovsdb-log.at b/tests/ovsdb-log.at index d00a64cb2..a73990a15 100644 --- a/tests/ovsdb-log.at +++ b/tests/ovsdb-log.at @@ -46,9 +46,8 @@ AT_CHECK( file: read: {"x":1} ]], [ignore]) AT_CHECK( - [test-ovsdb log-io file create-excl read], [1], - [], [test-ovsdb: I/O error: file: create failed (File exists) -]) + [test-ovsdb log-io file create-excl read 2>&1 | grep -q "create failed"], [0], + [], []) AT_CHECK( [test-ovsdb log-io file create read], [0], [file: open successful @@ -173,9 +172,8 @@ file: read: {"x":2} file: read: end of log ]], [ignore]) AT_CHECK( - [test-ovsdb log-io file read-only], [1], [], - [test-ovsdb: ovsdb error: file: cannot identify file type -]) + [test-ovsdb log-io file read-only 2>&1 | grep -q "cannot identify file type"], [0], [], + []) AT_CHECK([test -f .file.~lock~]) AT_CLEANUP @@ -247,7 +245,7 @@ file: write:{"x":2} successful ]], [ignore]) AT_CHECK([echo 'xxx' >> file]) AT_CHECK( - [test-ovsdb log-io file read-only read read read read], [0], + [test-ovsdb log-io file read-only read read read read | sed -e 's|error: .*file[[.0-9]]*:|error: file:|'], [0], [[file: open successful file: read: {"x":0} file: read: {"x":1} @@ -269,7 +267,7 @@ file: write:{"x":2} successful ]], [ignore]) AT_CHECK([echo 'xxx' >> file]) AT_CHECK( - [[test-ovsdb log-io file read/write read read read read 'write:{"x":3}']], [0], + [[test-ovsdb log-io file read/write read read read read 'write:{"x":3}' | sed -e 's|error: .*file[.0-9]*:|error: file:|']], [0], [[file: open successful file: read: {"x":0} file: read: {"x":1} @@ -304,7 +302,7 @@ AT_CHECK([mv file.tmp file]) AT_CHECK([[grep -c '{"x":3}' file]], [0], [1 ]) AT_CHECK( - [[test-ovsdb log-io file read/write read read read 'write:{"longer data":0}']], [0], + [[test-ovsdb log-io file read/write read read read 'write:{"longer data":0}' | sed -e 's|error: .*file[.0-9]*:|error: file:|']], [0], [[file: open successful file: read: {"x":0} file: read: {"x":1} @@ -337,7 +335,7 @@ AT_CHECK([mv file.tmp file]) AT_CHECK([[grep -c '^{"logdata":2}$' file]], [0], [1 ]) AT_CHECK( - [[test-ovsdb log-io file read/write read read read 'write:{"longer data":0}']], [0], + [[test-ovsdb log-io file read/write read read read 'write:{"longer data":0}' | sed -e 's|error: .*file[.0-9]*:|error: file:|']], [0], [[file: open successful file: read: {"x":0} file: read: {"x":1} @@ -367,7 +365,7 @@ file: write:{"x":2} successful ]], [ignore]) AT_CHECK([[printf '%s\n%s\n' 'OVSDB JSON 5 d910b02871075d3156ec8675dfc95b7d5d640aa6' 'null' >> file]]) AT_CHECK( - [[test-ovsdb log-io file read/write read read read read 'write:{"replacement data":0}']], [0], + [[test-ovsdb log-io file read/write read read read read 'write:{"replacement data":0}' | sed -e 's|error: .*file[.0-9]*:|error: file:|']], [0], [[file: open successful file: read: {"x":0} file: read: {"x":1} diff --git a/tests/ovsdb-server.at b/tests/ovsdb-server.at index bf9d25fa2..ef27d298e 100644 --- a/tests/ovsdb-server.at +++ b/tests/ovsdb-server.at @@ -83,7 +83,7 @@ AT_DATA([txnfile], [[ovsdb-client transact unix:socket \ "row": {"number": 1, "name": "one"}}]' ]]) AT_CHECK([ovsdb-server --remote=punix:socket db --run="sh txnfile"], [0], [stdout], [stderr]) -AT_CHECK([grep 'syntax error: db: parse error.* in header line "xxx"' stderr], +AT_CHECK([grep 'syntax error: .*db.*: parse error.* in header line "xxx"' stderr], [0], [ignore]) cat stdout >> output dnl Run a final transaction to verify that both transactions succeeeded. @@ -263,7 +263,7 @@ fi # Add a non-existing database. AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/add-db db3], 2, [], [stderr]) -AT_CHECK([sed 's/(.*)/(...)/' stderr], [0], +AT_CHECK([sed -e 's/(.*)/(...)/' -e 's|error: .*db3:|error: db3:|' stderr], [0], [I/O error: db3: open failed (...) ovs-appctl: ovsdb-server: server returned an error ]) -- 2.43.0 _______________________________________________ dev mailing list d...@openvswitch.org https://mail.openvswitch.org/mailman/listinfo/ovs-dev