summaryrefslogtreecommitdiff
path: root/ovsdb/log.c
diff options
context:
space:
mode:
authorBen Pfaff <blp@ovn.org>2017-12-24 16:19:24 -0800
committerBen Pfaff <blp@ovn.org>2017-12-24 16:19:24 -0800
commitfed2775975e650be8f4ae3bf63e4da5d0b21adf2 (patch)
tree8b2e5f2613342f8d6dade4607b8d7629164e9074 /ovsdb/log.c
parent71e4030fba417085022a459bb03cbb5a98fa8b23 (diff)
downloadopenvswitch-fed2775975e650be8f4ae3bf63e4da5d0b21adf2.tar.gz
log: Support multiple magic.
Some OVSDB tools will want to open files that might be standalone or clustered databases, and so it's better if ovsdb_log_open() can accept more than one valid "magic". This commit makes that possible. Signed-off-by: Ben Pfaff <blp@ovn.org> Acked-by: Justin Pettit <jpettit@ovn.org>
Diffstat (limited to 'ovsdb/log.c')
-rw-r--r--ovsdb/log.c155
1 files changed, 111 insertions, 44 deletions
diff --git a/ovsdb/log.c b/ovsdb/log.c
index 65103b61e..a5243ce52 100644
--- a/ovsdb/log.c
+++ b/ovsdb/log.c
@@ -88,13 +88,19 @@ static bool rename_open_files = false;
static bool rename_open_files = true;
#endif
+static bool parse_header(char *header, const char **magicp,
+ unsigned long int *length,
+ uint8_t sha1[SHA1_DIGEST_SIZE]);
+static bool is_magic_ok(const char *needle, const char *haystack);
+
/* 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.
+ * 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 'locking':
* use true to lock it, false not to lock it, or -1 to lock it only if
@@ -107,12 +113,16 @@ ovsdb_log_open(const char *name, const char *magic,
{
struct lockfile *lockfile;
struct ovsdb_error *error;
- struct ovsdb_log *file;
struct stat s;
FILE *stream;
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;
ovs_assert(locking == -1 || locking == false || locking == true);
@@ -181,43 +191,56 @@ ovsdb_log_open(const char *name, const char *magic,
goto error_unlock;
}
- if (!fstat(fd, &s)) {
- if (s.st_size == 0) {
- /* It's (probably) a new file so fsync() its parent directory to
- * ensure that its directory entry is committed to disk. */
- fsync_parent_dir(name);
- } else if (s.st_size >= strlen(magic) && S_ISREG(s.st_mode)) {
- /* Try to read the magic from the first log record. If it's not
- * the magic we expect, this is the wrong kind of file, so reject
- * it immediately. */
- size_t magic_len = strlen(magic);
- char *buf = xzalloc(magic_len + 1);
- bool err = (read(fd, buf, magic_len) == magic_len
- && strcmp(buf, magic));
- free(buf);
- if (err) {
- error = ovsdb_error(NULL, "%s: bad magic (unexpected "
- "kind of file)", name);
- goto error_close;
- }
- if (lseek(fd, 0, SEEK_SET)) {
- error = ovsdb_io_error(errno, "%s: seek failed", name);
- goto error_close;
- }
- }
- }
-
stream = fdopen(fd, open_mode == OVSDB_LOG_READ_ONLY ? "rb" : "w+b");
if (!stream) {
error = ovsdb_io_error(errno, "%s: fdopen failed", name);
- goto error_close;
+ close(fd);
+ goto error_unlock;
+ }
+
+ /* Read the magic from the first log record. */
+ char header[128];
+ const char *actual_magic;
+ if (!fgets(header, sizeof header, stream)) {
+ if (ferror(stream)) {
+ error = ovsdb_io_error(errno, "%s: read error", name);
+ goto error_fclose;
+ }
+
+ /* We need to be able to report what kind of file this is but we can't
+ * if it's empty and we accept more than one. */
+ if (strchr(magic, '|')) {
+ error = ovsdb_error(NULL, "%s: cannot identify file type", name);
+ goto error_fclose;
+ }
+ actual_magic = magic;
+
+ /* It's an empty file and therefore probably a new file, so fsync()
+ * its parent directory to ensure that its directory entry is
+ * committed to disk. */
+ fsync_parent_dir(name);
+ } else {
+ unsigned long int length;
+ uint8_t sha1[SHA1_DIGEST_SIZE];
+ if (!parse_header(header, &actual_magic, &length, sha1)) {
+ error = ovsdb_error(NULL, "%s: unexpected file format", name);
+ goto error_fclose;
+ } else if (!is_magic_ok(actual_magic, magic)) {
+ error = ovsdb_error(NULL, "%s: cannot identify file type", name);
+ goto error_fclose;
+ }
+ }
+
+ if (fseek(stream, 0, SEEK_SET)) {
+ error = ovsdb_io_error(errno, "%s: seek failed", name);
+ goto error_fclose;
}
- file = xmalloc(sizeof *file);
+ struct ovsdb_log *file = xmalloc(sizeof *file);
file->state = OVSDB_LOG_READ;
file->error = NULL;
file->name = xstrdup(name);
- file->magic = xstrdup(magic);
+ file->magic = xstrdup(actual_magic);
file->lockfile = lockfile;
file->stream = stream;
file->prev_offset = 0;
@@ -225,14 +248,36 @@ ovsdb_log_open(const char *name, const char *magic,
*filep = file;
return NULL;
-error_close:
- close(fd);
+error_fclose:
+ fclose(stream);
error_unlock:
lockfile_unlock(lockfile);
error:
return error;
}
+/* Returns true if 'needle' is one of the |-delimited words in 'haystack'. */
+static bool
+is_magic_ok(const char *needle, const char *haystack)
+{
+ /* 'needle' can't be multiple words. */
+ if (strchr(needle, '|')) {
+ return false;
+ }
+
+ size_t n = strlen(needle);
+ for (;;) {
+ if (!strncmp(needle, haystack, n) && strchr("|", haystack[n])) {
+ return true;
+ }
+ haystack = strchr(haystack, '|');
+ if (!haystack) {
+ return false;
+ }
+ haystack++;
+ }
+}
+
void
ovsdb_log_close(struct ovsdb_log *file)
{
@@ -248,20 +293,41 @@ ovsdb_log_close(struct ovsdb_log *file)
}
}
+const char *
+ovsdb_log_get_magic(const struct ovsdb_log *log)
+{
+ return log->magic;
+}
+
+/* Attempts to parse 'header' as a header line for an OVSDB log record (as
+ * described in ovsdb(5)). Stores a pointer to the magic string in '*magicp',
+ * the length in *length, and the parsed sha1 value in sha1[].
+ *
+ * Modifies 'header' and points '*magicp' inside it.
+ *
+ * Returns true if successful, false on failure. */
static bool
-parse_header(const char *magic, char *header, unsigned long int *length,
- uint8_t sha1[SHA1_DIGEST_SIZE])
+parse_header(char *header, const char **magicp,
+ unsigned long int *length, uint8_t sha1[SHA1_DIGEST_SIZE])
{
- char *p;
+ /* 'header' must consist of "OVSDB "... */
+ const char lead[] = "OVSDB ";
+ if (strncmp(lead, header, strlen(lead))) {
+ return false;
+ }
- /* 'header' must consist of a magic string... */
- size_t magic_len = strlen(magic);
- if (strncmp(header, magic, magic_len) || header[magic_len] != ' ') {
+ /* ...followed by a magic string... */
+ char *magic = header + strlen(lead);
+ size_t magic_len = strcspn(magic, " ");
+ if (magic[magic_len] != ' ') {
return false;
}
+ magic[magic_len] = '\0';
+ *magicp = magic;
/* ...followed by a length in bytes... */
- *length = strtoul(header + magic_len + 1, &p, 10);
+ char *p;
+ *length = strtoul(magic + magic_len + 1, &p, 10);
if (!*length || *length == ULONG_MAX || *p != ' ') {
return false;
}
@@ -343,7 +409,6 @@ ovsdb_log_read(struct ovsdb_log *file, struct json **jsonp)
uint8_t expected_sha1[SHA1_DIGEST_SIZE];
uint8_t actual_sha1[SHA1_DIGEST_SIZE];
struct ovsdb_error *error;
- off_t data_offset;
unsigned long data_length;
struct json *json;
char header[128];
@@ -357,8 +422,11 @@ ovsdb_log_read(struct ovsdb_log *file, struct json **jsonp)
error = ovsdb_io_error(errno, "%s: read failed", file->name);
goto error;
}
+ off_t data_offset = file->offset + strlen(header);
- if (!parse_header(file->magic, header, &data_length, expected_sha1)) {
+ 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->name, (long long int) file->offset,
@@ -366,7 +434,6 @@ ovsdb_log_read(struct ovsdb_log *file, struct json **jsonp)
goto error;
}
- data_offset = file->offset + strlen(header);
error = parse_body(file, data_offset, data_length, actual_sha1, &json);
if (error) {
goto error;
@@ -447,7 +514,7 @@ ovsdb_log_compose_record(const struct json *json,
/* Compose header. */
uint8_t sha1[SHA1_DIGEST_SIZE];
sha1_bytes(data->string, data->length, sha1);
- ds_put_format(header, "%s %"PRIuSIZE" "SHA1_FMT"\n",
+ ds_put_format(header, "OVSDB %s %"PRIuSIZE" "SHA1_FMT"\n",
magic, data->length, SHA1_ARGS(sha1));
}