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

Reply via email to