On Wed, Jul 30, 2025 at 12:04:38PM +0200, Felix Huettner wrote: > 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> > ---
looks like a random other failure Recheck-request: github-robot > 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