LCOV - code coverage report
Current view: top level - ovsdb - file.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 273 323 84.5 %
Date: 2016-09-14 01:02:56 Functions: 21 21 100.0 %
Branches: 149 210 71.0 %

           Branch data     Line data    Source code
       1                 :            : /* Copyright (c) 2009, 2010, 2011, 2012, 2013, 2016 Nicira, Inc.
       2                 :            :  *
       3                 :            :  * Licensed under the Apache License, Version 2.0 (the "License");
       4                 :            :  * you may not use this file except in compliance with the License.
       5                 :            :  * You may obtain a copy of the License at:
       6                 :            :  *
       7                 :            :  *     http://www.apache.org/licenses/LICENSE-2.0
       8                 :            :  *
       9                 :            :  * Unless required by applicable law or agreed to in writing, software
      10                 :            :  * distributed under the License is distributed on an "AS IS" BASIS,
      11                 :            :  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      12                 :            :  * See the License for the specific language governing permissions and
      13                 :            :  * limitations under the License.
      14                 :            :  */
      15                 :            : 
      16                 :            : #include <config.h>
      17                 :            : 
      18                 :            : #include "file.h"
      19                 :            : 
      20                 :            : #include <errno.h>
      21                 :            : #include <fcntl.h>
      22                 :            : #include <unistd.h>
      23                 :            : 
      24                 :            : #include "bitmap.h"
      25                 :            : #include "column.h"
      26                 :            : #include "log.h"
      27                 :            : #include "openvswitch/json.h"
      28                 :            : #include "lockfile.h"
      29                 :            : #include "ovsdb.h"
      30                 :            : #include "ovsdb-error.h"
      31                 :            : #include "row.h"
      32                 :            : #include "socket-util.h"
      33                 :            : #include "table.h"
      34                 :            : #include "timeval.h"
      35                 :            : #include "transaction.h"
      36                 :            : #include "uuid.h"
      37                 :            : #include "util.h"
      38                 :            : #include "openvswitch/vlog.h"
      39                 :            : 
      40                 :       5622 : VLOG_DEFINE_THIS_MODULE(ovsdb_file);
      41                 :            : 
      42                 :            : /* Minimum number of milliseconds between database compactions. */
      43                 :            : #define COMPACT_MIN_MSEC        (10 * 60 * 1000) /* 10 minutes. */
      44                 :            : 
      45                 :            : /* Minimum number of milliseconds between trying to compact the database if
      46                 :            :  * compacting fails. */
      47                 :            : #define COMPACT_RETRY_MSEC      (60 * 1000)      /* 1 minute. */
      48                 :            : 
      49                 :            : /* A transaction being converted to JSON for writing to a file. */
      50                 :            : struct ovsdb_file_txn {
      51                 :            :     struct json *json;          /* JSON for the whole transaction. */
      52                 :            :     struct json *table_json;    /* JSON for 'table''s transaction. */
      53                 :            :     struct ovsdb_table *table;  /* Table described in 'table_json'.  */
      54                 :            : };
      55                 :            : 
      56                 :            : static void ovsdb_file_txn_init(struct ovsdb_file_txn *);
      57                 :            : static void ovsdb_file_txn_add_row(struct ovsdb_file_txn *,
      58                 :            :                                    const struct ovsdb_row *old,
      59                 :            :                                    const struct ovsdb_row *new,
      60                 :            :                                    const unsigned long int *changed);
      61                 :            : static struct ovsdb_error *ovsdb_file_txn_commit(struct json *,
      62                 :            :                                                  const char *comment,
      63                 :            :                                                  bool durable,
      64                 :            :                                                  struct ovsdb_log *);
      65                 :            : 
      66                 :            : static struct ovsdb_error *ovsdb_file_open__(const char *file_name,
      67                 :            :                                              const struct ovsdb_schema *,
      68                 :            :                                              bool read_only, struct ovsdb **,
      69                 :            :                                              struct ovsdb_file **);
      70                 :            : static struct ovsdb_error *ovsdb_file_txn_from_json(
      71                 :            :     struct ovsdb *, const struct json *, bool converting, struct ovsdb_txn **);
      72                 :            : static struct ovsdb_error *ovsdb_file_create(struct ovsdb *,
      73                 :            :                                              struct ovsdb_log *,
      74                 :            :                                              const char *file_name,
      75                 :            :                                              unsigned int n_transactions,
      76                 :            :                                              off_t snapshot_size,
      77                 :            :                                              struct ovsdb_file **filep);
      78                 :            : 
      79                 :            : /* Opens database 'file_name' and stores a pointer to the new database in
      80                 :            :  * '*dbp'.  If 'read_only' is false, then the database will be locked and
      81                 :            :  * changes to the database will be written to disk.  If 'read_only' is true,
      82                 :            :  * the database will not be locked and changes to the database will persist
      83                 :            :  * only as long as the "struct ovsdb".
      84                 :            :  *
      85                 :            :  * If 'filep' is nonnull and 'read_only' is false, then on success sets
      86                 :            :  * '*filep' to an ovsdb_file that represents the open file.  This ovsdb_file
      87                 :            :  * persists until '*dbp' is destroyed.
      88                 :            :  *
      89                 :            :  * On success, returns NULL.  On failure, returns an ovsdb_error (which the
      90                 :            :  * caller must destroy) and sets '*dbp' and '*filep' to NULL. */
      91                 :            : struct ovsdb_error *
      92                 :       1516 : ovsdb_file_open(const char *file_name, bool read_only,
      93                 :            :                 struct ovsdb **dbp, struct ovsdb_file **filep)
      94                 :            : {
      95                 :       1516 :     return ovsdb_file_open__(file_name, NULL, read_only, dbp, filep);
      96                 :            : }
      97                 :            : 
      98                 :            : /* Opens database 'file_name' with an alternate schema.  The specified 'schema'
      99                 :            :  * is used to interpret the data in 'file_name', ignoring the schema actually
     100                 :            :  * stored in the file.  Data in the file for tables or columns that do not
     101                 :            :  * exist in 'schema' are ignored, but the ovsdb file format must otherwise be
     102                 :            :  * observed, including column constraints.
     103                 :            :  *
     104                 :            :  * This function can be useful for upgrading or downgrading databases to
     105                 :            :  * "almost-compatible" formats.
     106                 :            :  *
     107                 :            :  * The database will not be locked.  Changes to the database will persist only
     108                 :            :  * as long as the "struct ovsdb".
     109                 :            :  *
     110                 :            :  * On success, stores a pointer to the new database in '*dbp' and returns a
     111                 :            :  * null pointer.  On failure, returns an ovsdb_error (which the caller must
     112                 :            :  * destroy) and sets '*dbp' to NULL. */
     113                 :            : struct ovsdb_error *
     114                 :          2 : ovsdb_file_open_as_schema(const char *file_name,
     115                 :            :                           const struct ovsdb_schema *schema,
     116                 :            :                           struct ovsdb **dbp)
     117                 :            : {
     118                 :          2 :     return ovsdb_file_open__(file_name, schema, true, dbp, NULL);
     119                 :            : }
     120                 :            : 
     121                 :            : static struct ovsdb_error *
     122                 :       1522 : ovsdb_file_open_log(const char *file_name, enum ovsdb_log_open_mode open_mode,
     123                 :            :                     struct ovsdb_log **logp, struct ovsdb_schema **schemap)
     124                 :            : {
     125                 :       1522 :     struct ovsdb_schema *schema = NULL;
     126                 :       1522 :     struct ovsdb_log *log = NULL;
     127                 :            :     struct ovsdb_error *error;
     128                 :       1522 :     struct json *json = NULL;
     129                 :            : 
     130 [ +  + ][ -  + ]:       1522 :     ovs_assert(logp || schemap);
     131                 :            : 
     132                 :       1522 :     error = ovsdb_log_open(file_name, open_mode, -1, &log);
     133         [ +  + ]:       1522 :     if (error) {
     134                 :          1 :         goto error;
     135                 :            :     }
     136                 :            : 
     137                 :       1521 :     error = ovsdb_log_read(log, &json);
     138         [ -  + ]:       1521 :     if (error) {
     139                 :          0 :         goto error;
     140         [ -  + ]:       1521 :     } else if (!json) {
     141                 :          0 :         error = ovsdb_io_error(EOF, "%s: database file contains no schema",
     142                 :            :                                file_name);
     143                 :          0 :         goto error;
     144                 :            :     }
     145                 :            : 
     146         [ +  + ]:       1521 :     if (schemap) {
     147                 :       1519 :         error = ovsdb_schema_from_json(json, &schema);
     148         [ -  + ]:       1519 :         if (error) {
     149                 :          0 :             error = ovsdb_wrap_error(error,
     150                 :            :                                      "failed to parse \"%s\" as ovsdb schema",
     151                 :            :                                      file_name);
     152                 :          0 :             goto error;
     153                 :            :         }
     154                 :            :     }
     155                 :       1521 :     json_destroy(json);
     156                 :            : 
     157         [ +  + ]:       1521 :     if (logp) {
     158                 :       1517 :         *logp = log;
     159                 :            :     } else {
     160                 :          4 :         ovsdb_log_close(log);
     161                 :            :     }
     162         [ +  + ]:       1521 :     if (schemap) {
     163                 :       1519 :         *schemap = schema;
     164                 :            :     }
     165                 :       1521 :     return NULL;
     166                 :            : 
     167                 :            : error:
     168                 :          1 :     ovsdb_log_close(log);
     169                 :          1 :     json_destroy(json);
     170         [ +  - ]:          1 :     if (logp) {
     171                 :          1 :         *logp = NULL;
     172                 :            :     }
     173         [ +  - ]:          1 :     if (schemap) {
     174                 :          1 :         *schemap = NULL;
     175                 :            :     }
     176                 :       1522 :     return error;
     177                 :            : }
     178                 :            : 
     179                 :            : static struct ovsdb_error *
     180                 :       1518 : ovsdb_file_open__(const char *file_name,
     181                 :            :                   const struct ovsdb_schema *alternate_schema,
     182                 :            :                   bool read_only, struct ovsdb **dbp,
     183                 :            :                   struct ovsdb_file **filep)
     184                 :            : {
     185                 :            :     enum ovsdb_log_open_mode open_mode;
     186                 :       1518 :     struct ovsdb_schema *schema = NULL;
     187                 :            :     struct ovsdb_error *error;
     188                 :            :     struct ovsdb_log *log;
     189                 :            :     struct json *json;
     190                 :       1518 :     struct ovsdb *db = NULL;
     191                 :            : 
     192                 :            :     /* In read-only mode there is no ovsdb_file so 'filep' must be null. */
     193 [ +  + ][ -  + ]:       1518 :     ovs_assert(!(read_only && filep));
     194                 :            : 
     195         [ +  + ]:       1518 :     open_mode = read_only ? OVSDB_LOG_READ_ONLY : OVSDB_LOG_READ_WRITE;
     196         [ +  + ]:       1518 :     error = ovsdb_file_open_log(file_name, open_mode, &log,
     197                 :            :                                 alternate_schema ? NULL : &schema);
     198         [ +  + ]:       1518 :     if (error) {
     199                 :          1 :         goto error;
     200                 :            :     }
     201                 :            : 
     202         [ +  + ]:       1517 :     db = ovsdb_create(schema ? schema : ovsdb_schema_clone(alternate_schema));
     203                 :            : 
     204                 :            :     /* When a log gets big, we compact it into a new log that initially has
     205                 :            :      * only a single transaction that represents the entire state of the
     206                 :            :      * database.  Thus, we consider the first transaction in the database to be
     207                 :            :      * the snapshot.  We measure its size to later influence the minimum log
     208                 :            :      * size before compacting again.
     209                 :            :      *
     210                 :            :      * The schema precedes the snapshot in the log; we could compensate for its
     211                 :            :      * size, but it's just not that important. */
     212                 :       1517 :     off_t snapshot_size = 0;
     213                 :       1517 :     unsigned int n_transactions = 0;
     214 [ +  + ][ +  + ]:       2133 :     while ((error = ovsdb_log_read(log, &json)) == NULL && json) {
     215                 :            :         struct ovsdb_txn *txn;
     216                 :            : 
     217                 :        617 :         error = ovsdb_file_txn_from_json(db, json, alternate_schema != NULL,
     218                 :            :                                          &txn);
     219                 :        617 :         json_destroy(json);
     220         [ +  + ]:        617 :         if (error) {
     221                 :          1 :             ovsdb_log_unread(log);
     222                 :          1 :             break;
     223                 :            :         }
     224                 :            : 
     225                 :        616 :         n_transactions++;
     226                 :        616 :         error = ovsdb_txn_commit(txn, false);
     227         [ -  + ]:        616 :         if (error) {
     228                 :          0 :             ovsdb_log_unread(log);
     229                 :          0 :             break;
     230                 :            :         }
     231                 :            : 
     232         [ +  + ]:        616 :         if (n_transactions == 1) {
     233                 :        616 :             snapshot_size = ovsdb_log_get_offset(log);
     234                 :            :         }
     235                 :            :     }
     236         [ +  + ]:       1517 :     if (error) {
     237                 :            :         /* Log error but otherwise ignore it.  Probably the database just got
     238                 :            :          * truncated due to power failure etc. and we should use its current
     239                 :            :          * contents. */
     240                 :          2 :         char *msg = ovsdb_error_to_string(error);
     241         [ +  - ]:          2 :         VLOG_ERR("%s", msg);
     242                 :          2 :         free(msg);
     243                 :            : 
     244                 :          2 :         ovsdb_error_destroy(error);
     245                 :            :     }
     246                 :            : 
     247         [ +  + ]:       1517 :     if (!read_only) {
     248                 :            :         struct ovsdb_file *file;
     249                 :            : 
     250                 :       1514 :         error = ovsdb_file_create(db, log, file_name, n_transactions,
     251                 :            :                                   snapshot_size, &file);
     252         [ -  + ]:       1514 :         if (error) {
     253                 :          0 :             goto error;
     254                 :            :         }
     255         [ +  + ]:       1514 :         if (filep) {
     256                 :       1514 :             *filep = file;
     257                 :            :         }
     258                 :            :     } else {
     259                 :          3 :         ovsdb_log_close(log);
     260                 :            :     }
     261                 :            : 
     262                 :       1517 :     *dbp = db;
     263                 :       1517 :     return NULL;
     264                 :            : 
     265                 :            : error:
     266                 :          1 :     *dbp = NULL;
     267         [ +  - ]:          1 :     if (filep) {
     268                 :          1 :         *filep = NULL;
     269                 :            :     }
     270                 :          1 :     ovsdb_destroy(db);
     271                 :          1 :     ovsdb_log_close(log);
     272                 :       1518 :     return error;
     273                 :            : }
     274                 :            : 
     275                 :            : static struct ovsdb_error *
     276                 :        761 : ovsdb_file_update_row_from_json(struct ovsdb_row *row, bool converting,
     277                 :            :                                 const struct json *json)
     278                 :            : {
     279                 :        761 :     struct ovsdb_table_schema *schema = row->table->schema;
     280                 :            :     struct ovsdb_error *error;
     281                 :            :     struct shash_node *node;
     282                 :            : 
     283         [ -  + ]:        761 :     if (json->type != JSON_OBJECT) {
     284                 :          0 :         return ovsdb_syntax_error(json, NULL, "row must be JSON object");
     285                 :            :     }
     286                 :            : 
     287 [ +  + ][ -  + ]:       2067 :     SHASH_FOR_EACH (node, json_object(json)) {
     288                 :       1306 :         const char *column_name = node->name;
     289                 :            :         const struct ovsdb_column *column;
     290                 :            :         struct ovsdb_datum datum;
     291                 :            : 
     292                 :       1306 :         column = ovsdb_table_schema_get_column(schema, column_name);
     293         [ +  + ]:       1306 :         if (!column) {
     294         [ +  - ]:          6 :             if (converting) {
     295                 :          6 :                 continue;
     296                 :            :             }
     297                 :          0 :             return ovsdb_syntax_error(json, "unknown column",
     298                 :            :                                       "No column %s in table %s.",
     299                 :            :                                       column_name, schema->name);
     300                 :            :         }
     301                 :            : 
     302                 :       1300 :         error = ovsdb_datum_from_json(&datum, &column->type, node->data, NULL);
     303         [ -  + ]:       1300 :         if (error) {
     304                 :          0 :             return error;
     305                 :            :         }
     306                 :       1300 :         ovsdb_datum_swap(&row->fields[column->index], &datum);
     307                 :       1300 :         ovsdb_datum_destroy(&datum, &column->type);
     308                 :            :     }
     309                 :            : 
     310                 :        761 :     return NULL;
     311                 :            : }
     312                 :            : 
     313                 :            : static struct ovsdb_error *
     314                 :        944 : ovsdb_file_txn_row_from_json(struct ovsdb_txn *txn, struct ovsdb_table *table,
     315                 :            :                              bool converting,
     316                 :            :                              const struct uuid *row_uuid, struct json *json)
     317                 :            : {
     318                 :        944 :     const struct ovsdb_row *row = ovsdb_table_get_row(table, row_uuid);
     319         [ +  + ]:        944 :     if (json->type == JSON_NULL) {
     320         [ -  + ]:        183 :         if (!row) {
     321                 :          0 :             return ovsdb_syntax_error(NULL, NULL, "transaction deletes "
     322                 :            :                                       "row "UUID_FMT" that does not exist",
     323                 :          0 :                                       UUID_ARGS(row_uuid));
     324                 :            :         }
     325                 :        183 :         ovsdb_txn_row_delete(txn, row);
     326                 :        183 :         return NULL;
     327         [ +  + ]:        761 :     } else if (row) {
     328                 :         32 :         return ovsdb_file_update_row_from_json(ovsdb_txn_row_modify(txn, row),
     329                 :            :                                                converting, json);
     330                 :            :     } else {
     331                 :            :         struct ovsdb_error *error;
     332                 :            :         struct ovsdb_row *new;
     333                 :            : 
     334                 :        729 :         new = ovsdb_row_create(table);
     335                 :        729 :         *ovsdb_row_get_uuid_rw(new) = *row_uuid;
     336                 :        729 :         error = ovsdb_file_update_row_from_json(new, converting, json);
     337         [ -  + ]:        729 :         if (error) {
     338                 :          0 :             ovsdb_row_destroy(new);
     339                 :            :         } else {
     340                 :        729 :             ovsdb_txn_row_insert(txn, new);
     341                 :            :         }
     342                 :        729 :         return error;
     343                 :            :     }
     344                 :            : }
     345                 :            : 
     346                 :            : static struct ovsdb_error *
     347                 :        688 : ovsdb_file_txn_table_from_json(struct ovsdb_txn *txn,
     348                 :            :                                struct ovsdb_table *table,
     349                 :            :                                bool converting, struct json *json)
     350                 :            : {
     351                 :            :     struct shash_node *node;
     352                 :            : 
     353         [ -  + ]:        688 :     if (json->type != JSON_OBJECT) {
     354                 :          0 :         return ovsdb_syntax_error(json, NULL, "object expected");
     355                 :            :     }
     356                 :            : 
     357 [ +  + ][ -  + ]:       1632 :     SHASH_FOR_EACH (node, json->u.object) {
     358                 :        944 :         const char *uuid_string = node->name;
     359                 :        944 :         struct json *txn_row_json = node->data;
     360                 :            :         struct ovsdb_error *error;
     361                 :            :         struct uuid row_uuid;
     362                 :            : 
     363         [ -  + ]:        944 :         if (!uuid_from_string(&row_uuid, uuid_string)) {
     364                 :          0 :             return ovsdb_syntax_error(json, NULL, "\"%s\" is not a valid UUID",
     365                 :            :                                       uuid_string);
     366                 :            :         }
     367                 :            : 
     368                 :        944 :         error = ovsdb_file_txn_row_from_json(txn, table, converting,
     369                 :            :                                              &row_uuid, txn_row_json);
     370         [ -  + ]:        944 :         if (error) {
     371                 :          0 :             return error;
     372                 :            :         }
     373                 :            :     }
     374                 :            : 
     375                 :        688 :     return NULL;
     376                 :            : }
     377                 :            : 
     378                 :            : /* Converts 'json' to an ovsdb_txn for 'db', storing the new transaction in
     379                 :            :  * '*txnp'.  Returns NULL if successful, otherwise an error.
     380                 :            :  *
     381                 :            :  * If 'converting' is true, then unknown table and column names are ignored
     382                 :            :  * (which can ease upgrading and downgrading schemas); otherwise, they are
     383                 :            :  * treated as errors. */
     384                 :            : static struct ovsdb_error *
     385                 :        617 : ovsdb_file_txn_from_json(struct ovsdb *db, const struct json *json,
     386                 :            :                          bool converting, struct ovsdb_txn **txnp)
     387                 :            : {
     388                 :            :     struct ovsdb_error *error;
     389                 :            :     struct shash_node *node;
     390                 :            :     struct ovsdb_txn *txn;
     391                 :            : 
     392                 :        617 :     *txnp = NULL;
     393                 :            : 
     394         [ -  + ]:        617 :     if (json->type != JSON_OBJECT) {
     395                 :          0 :         return ovsdb_syntax_error(json, NULL, "object expected");
     396                 :            :     }
     397                 :            : 
     398                 :        617 :     txn = ovsdb_txn_create(db);
     399 [ +  + ][ -  + ]:       2167 :     SHASH_FOR_EACH (node, json->u.object) {
     400                 :       1551 :         const char *table_name = node->name;
     401                 :       1551 :         struct json *node_json = node->data;
     402                 :            :         struct ovsdb_table *table;
     403                 :            : 
     404                 :       1551 :         table = shash_find_data(&db->tables, table_name);
     405         [ +  + ]:       1551 :         if (!table) {
     406         [ +  + ]:        863 :             if (!strcmp(table_name, "_date")
     407         [ +  - ]:        616 :                 && node_json->type == JSON_INTEGER) {
     408                 :        616 :                 continue;
     409 [ +  + ][ -  + ]:        247 :             } else if (!strcmp(table_name, "_comment") || converting) {
     410                 :        246 :                 continue;
     411                 :            :             }
     412                 :            : 
     413                 :          1 :             error = ovsdb_syntax_error(json, "unknown table",
     414                 :            :                                        "No table named %s.", table_name);
     415                 :          1 :             goto error;
     416                 :            :         }
     417                 :            : 
     418                 :        688 :         error = ovsdb_file_txn_table_from_json(txn, table, converting,
     419                 :            :                                                node_json);
     420         [ -  + ]:        688 :         if (error) {
     421                 :          0 :             goto error;
     422                 :            :         }
     423                 :            :     }
     424                 :        616 :     *txnp = txn;
     425                 :        616 :     return NULL;
     426                 :            : 
     427                 :            : error:
     428                 :          1 :     ovsdb_txn_abort(txn);
     429                 :          1 :     return error;
     430                 :            : }
     431                 :            : 
     432                 :            : static struct ovsdb_error *
     433                 :          4 : ovsdb_file_save_copy__(const char *file_name, int locking,
     434                 :            :                        const char *comment, const struct ovsdb *db,
     435                 :            :                        struct ovsdb_log **logp)
     436                 :            : {
     437                 :            :     const struct shash_node *node;
     438                 :            :     struct ovsdb_file_txn ftxn;
     439                 :            :     struct ovsdb_error *error;
     440                 :            :     struct ovsdb_log *log;
     441                 :            :     struct json *json;
     442                 :            : 
     443                 :          4 :     error = ovsdb_log_open(file_name, OVSDB_LOG_CREATE, locking, &log);
     444         [ -  + ]:          4 :     if (error) {
     445                 :          0 :         return error;
     446                 :            :     }
     447                 :            : 
     448                 :            :     /* Write schema. */
     449                 :          4 :     json = ovsdb_schema_to_json(db->schema);
     450                 :          4 :     error = ovsdb_log_write(log, json);
     451                 :          4 :     json_destroy(json);
     452         [ -  + ]:          4 :     if (error) {
     453                 :          0 :         goto exit;
     454                 :            :     }
     455                 :            : 
     456                 :            :     /* Write data. */
     457                 :          4 :     ovsdb_file_txn_init(&ftxn);
     458 [ +  + ][ -  + ]:          8 :     SHASH_FOR_EACH (node, &db->tables) {
     459                 :          4 :         const struct ovsdb_table *table = node->data;
     460                 :            :         const struct ovsdb_row *row;
     461                 :            : 
     462 [ +  + ][ -  + ]:         28 :         HMAP_FOR_EACH (row, hmap_node, &table->rows) {
     463                 :         24 :             ovsdb_file_txn_add_row(&ftxn, NULL, row, NULL);
     464                 :            :         }
     465                 :            :     }
     466                 :          4 :     error = ovsdb_file_txn_commit(ftxn.json, comment, true, log);
     467                 :            : 
     468                 :            : exit:
     469         [ +  + ]:          4 :     if (logp) {
     470         [ +  - ]:          1 :         if (!error) {
     471                 :          1 :             *logp = log;
     472                 :          1 :             log = NULL;
     473                 :            :         } else {
     474                 :          0 :             *logp = NULL;
     475                 :            :         }
     476                 :            :     }
     477                 :          4 :     ovsdb_log_close(log);
     478         [ -  + ]:          4 :     if (error) {
     479                 :          0 :         remove(file_name);
     480                 :            :     }
     481                 :          4 :     return error;
     482                 :            : }
     483                 :            : 
     484                 :            : /* Saves a snapshot of 'db''s current contents as 'file_name'.  If 'comment' is
     485                 :            :  * nonnull, then it is added along with the data contents and can be viewed
     486                 :            :  * with "ovsdb-tool show-log".
     487                 :            :  *
     488                 :            :  * 'locking' is passed along to ovsdb_log_open() untouched. */
     489                 :            : struct ovsdb_error *
     490                 :          3 : ovsdb_file_save_copy(const char *file_name, int locking,
     491                 :            :                      const char *comment, const struct ovsdb *db)
     492                 :            : {
     493                 :          3 :     return ovsdb_file_save_copy__(file_name, locking, comment, db, NULL);
     494                 :            : }
     495                 :            : 
     496                 :            : /* Opens database 'file_name', reads its schema, and closes it.  On success,
     497                 :            :  * stores the schema into '*schemap' and returns NULL; the caller then owns the
     498                 :            :  * schema.  On failure, returns an ovsdb_error (which the caller must destroy)
     499                 :            :  * and sets '*dbp' to NULL. */
     500                 :            : struct ovsdb_error *
     501                 :          4 : ovsdb_file_read_schema(const char *file_name, struct ovsdb_schema **schemap)
     502                 :            : {
     503         [ -  + ]:          4 :     ovs_assert(schemap != NULL);
     504                 :          4 :     return ovsdb_file_open_log(file_name, OVSDB_LOG_READ_ONLY, NULL, schemap);
     505                 :            : }
     506                 :            : 
     507                 :            : /* Replica implementation. */
     508                 :            : 
     509                 :            : struct ovsdb_file {
     510                 :            :     struct ovsdb_replica replica;
     511                 :            :     struct ovsdb *db;
     512                 :            :     struct ovsdb_log *log;
     513                 :            :     char *file_name;
     514                 :            :     long long int last_compact;
     515                 :            :     long long int next_compact;
     516                 :            :     unsigned int n_transactions;
     517                 :            :     off_t snapshot_size;
     518                 :            : };
     519                 :            : 
     520                 :            : static const struct ovsdb_replica_class ovsdb_file_class;
     521                 :            : 
     522                 :            : static struct ovsdb_error *
     523                 :       1514 : ovsdb_file_create(struct ovsdb *db, struct ovsdb_log *log,
     524                 :            :                   const char *file_name,
     525                 :            :                   unsigned int n_transactions, off_t snapshot_size,
     526                 :            :                   struct ovsdb_file **filep)
     527                 :            : {
     528                 :            :     struct ovsdb_file *file;
     529                 :            :     char *deref_name;
     530                 :            :     char *abs_name;
     531                 :            : 
     532                 :            :     /* Use the absolute name of the file because ovsdb-server opens its
     533                 :            :      * database before daemonize() chdirs to "/". */
     534                 :       1514 :     deref_name = follow_symlinks(file_name);
     535                 :       1514 :     abs_name = abs_file_name(NULL, deref_name);
     536                 :       1514 :     free(deref_name);
     537         [ -  + ]:       1514 :     if (!abs_name) {
     538                 :          0 :         *filep = NULL;
     539                 :          0 :         return ovsdb_io_error(0, "could not determine current "
     540                 :            :                               "working directory");
     541                 :            :     }
     542                 :            : 
     543                 :       1514 :     file = xmalloc(sizeof *file);
     544                 :       1514 :     ovsdb_replica_init(&file->replica, &ovsdb_file_class);
     545                 :       1514 :     file->db = db;
     546                 :       1514 :     file->log = log;
     547                 :       1514 :     file->file_name = abs_name;
     548                 :       1514 :     file->last_compact = time_msec();
     549                 :       1514 :     file->next_compact = file->last_compact + COMPACT_MIN_MSEC;
     550                 :       1514 :     file->snapshot_size = snapshot_size;
     551                 :       1514 :     file->n_transactions = n_transactions;
     552                 :       1514 :     ovsdb_add_replica(db, &file->replica);
     553                 :            : 
     554                 :       1514 :     *filep = file;
     555                 :       1514 :     return NULL;
     556                 :            : }
     557                 :            : 
     558                 :            : static struct ovsdb_file *
     559                 :      12537 : ovsdb_file_cast(struct ovsdb_replica *replica)
     560                 :            : {
     561         [ -  + ]:      12537 :     ovs_assert(replica->class == &ovsdb_file_class);
     562                 :      12537 :     return CONTAINER_OF(replica, struct ovsdb_file, replica);
     563                 :            : }
     564                 :            : 
     565                 :            : static bool
     566                 :      63529 : ovsdb_file_change_cb(const struct ovsdb_row *old,
     567                 :            :                      const struct ovsdb_row *new,
     568                 :            :                      const unsigned long int *changed,
     569                 :            :                      void *ftxn_)
     570                 :            : {
     571                 :      63529 :     struct ovsdb_file_txn *ftxn = ftxn_;
     572                 :      63529 :     ovsdb_file_txn_add_row(ftxn, old, new, changed);
     573                 :      63529 :     return true;
     574                 :            : }
     575                 :            : 
     576                 :            : static struct ovsdb_error *
     577                 :      11025 : ovsdb_file_commit(struct ovsdb_replica *replica,
     578                 :            :                   const struct ovsdb_txn *txn, bool durable)
     579                 :            : {
     580                 :      11025 :     struct ovsdb_file *file = ovsdb_file_cast(replica);
     581                 :            :     struct ovsdb_file_txn ftxn;
     582                 :            :     struct ovsdb_error *error;
     583                 :            : 
     584                 :      11025 :     ovsdb_file_txn_init(&ftxn);
     585                 :      11025 :     ovsdb_txn_for_each_change(txn, ovsdb_file_change_cb, &ftxn);
     586         [ +  + ]:      11025 :     if (!ftxn.json) {
     587                 :            :         /* Nothing to commit. */
     588                 :        618 :         return NULL;
     589                 :            :     }
     590                 :            : 
     591                 :      10407 :     error = ovsdb_file_txn_commit(ftxn.json, ovsdb_txn_get_comment(txn),
     592                 :            :                                   durable, file->log);
     593         [ -  + ]:      10407 :     if (error) {
     594                 :          0 :         return error;
     595                 :            :     }
     596                 :      10407 :     file->n_transactions++;
     597                 :            : 
     598                 :            :     /* If it has been at least COMPACT_MIN_MSEC ms since the last time we
     599                 :            :      * compacted (or at least COMPACT_RETRY_MSEC ms since the last time we
     600                 :            :      * tried), and if there are at least 100 transactions in the database, and
     601                 :            :      * if the database is at least 10 MB, and the database is at least 4x the
     602                 :            :      * size of the previous snapshot, then compact the database. */
     603                 :      10407 :     off_t log_size = ovsdb_log_get_offset(file->log);
     604         [ -  + ]:      10407 :     if (time_msec() >= file->next_compact
     605         [ #  # ]:          0 :         && file->n_transactions >= 100
     606         [ #  # ]:          0 :         && log_size >= 10 * 1024 * 1024
     607         [ #  # ]:          0 :         && log_size / 4 >= file->snapshot_size)
     608                 :            :     {
     609                 :          0 :         error = ovsdb_file_compact(file);
     610         [ #  # ]:          0 :         if (error) {
     611                 :          0 :             char *s = ovsdb_error_to_string(error);
     612                 :          0 :             ovsdb_error_destroy(error);
     613         [ #  # ]:          0 :             VLOG_WARN("%s: compacting database failed (%s), retrying in "
     614                 :            :                       "%d seconds",
     615                 :            :                       file->file_name, s, COMPACT_RETRY_MSEC / 1000);
     616                 :          0 :             free(s);
     617                 :            : 
     618                 :          0 :             file->next_compact = time_msec() + COMPACT_RETRY_MSEC;
     619                 :            :         }
     620                 :            :     }
     621                 :            : 
     622                 :      11025 :     return NULL;
     623                 :            : }
     624                 :            : 
     625                 :            : struct ovsdb_error *
     626                 :          1 : ovsdb_file_compact(struct ovsdb_file *file)
     627                 :            : {
     628                 :          1 :     struct ovsdb_log *new_log = NULL;
     629                 :          1 :     struct lockfile *tmp_lock = NULL;
     630                 :            :     struct ovsdb_error *error;
     631                 :          1 :     char *tmp_name = NULL;
     632                 :          1 :     char *comment = NULL;
     633                 :            :     int retval;
     634                 :            : 
     635                 :          1 :     comment = xasprintf("compacting database online "
     636                 :            :                         "(%.3f seconds old, %u transactions, %llu bytes)",
     637                 :          1 :                         (time_wall_msec() - file->last_compact) / 1000.0,
     638                 :            :                         file->n_transactions,
     639                 :          1 :                         (unsigned long long) ovsdb_log_get_offset(file->log));
     640         [ +  - ]:          1 :     VLOG_INFO("%s: %s", file->file_name, comment);
     641                 :            : 
     642                 :            :     /* Commit the old version, so that we can be assured that we'll eventually
     643                 :            :      * have either the old or the new version. */
     644                 :          1 :     error = ovsdb_log_commit(file->log);
     645         [ -  + ]:          1 :     if (error) {
     646                 :          0 :         goto exit;
     647                 :            :     }
     648                 :            : 
     649                 :            :     /* Lock temporary file. */
     650                 :          1 :     tmp_name = xasprintf("%s.tmp", file->file_name);
     651                 :          1 :     retval = lockfile_lock(tmp_name, &tmp_lock);
     652         [ -  + ]:          1 :     if (retval) {
     653                 :          0 :         error = ovsdb_io_error(retval, "could not get lock on %s", tmp_name);
     654                 :          0 :         goto exit;
     655                 :            :     }
     656                 :            : 
     657                 :            :     /* Remove temporary file.  (It might not exist.) */
     658 [ +  - ][ -  + ]:          1 :     if (unlink(tmp_name) < 0 && errno != ENOENT) {
     659                 :          0 :         error = ovsdb_io_error(errno, "failed to remove %s", tmp_name);
     660                 :          0 :         goto exit;
     661                 :            :     }
     662                 :            : 
     663                 :            :     /* Save a copy. */
     664                 :          1 :     error = ovsdb_file_save_copy__(tmp_name, false, comment, file->db,
     665                 :            :                                    &new_log);
     666         [ -  + ]:          1 :     if (error) {
     667                 :          0 :         goto exit;
     668                 :            :     }
     669                 :            : 
     670                 :            :     /* Replace original by temporary. */
     671         [ -  + ]:          1 :     if (rename(tmp_name, file->file_name)) {
     672                 :          0 :         error = ovsdb_io_error(errno, "failed to rename \"%s\" to \"%s\"",
     673                 :            :                                tmp_name, file->file_name);
     674                 :          0 :         goto exit;
     675                 :            :     }
     676                 :          1 :     fsync_parent_dir(file->file_name);
     677                 :            : 
     678                 :            : exit:
     679         [ +  - ]:          1 :     if (!error) {
     680                 :          1 :         ovsdb_log_close(file->log);
     681                 :          1 :         file->log = new_log;
     682                 :          1 :         file->last_compact = time_msec();
     683                 :          1 :         file->next_compact = file->last_compact + COMPACT_MIN_MSEC;
     684                 :          1 :         file->n_transactions = 1;
     685                 :            :     } else {
     686                 :          0 :         ovsdb_log_close(new_log);
     687         [ #  # ]:          0 :         if (tmp_lock) {
     688                 :          0 :             unlink(tmp_name);
     689                 :            :         }
     690                 :            :     }
     691                 :            : 
     692                 :          1 :     lockfile_unlock(tmp_lock);
     693                 :          1 :     free(tmp_name);
     694                 :          1 :     free(comment);
     695                 :            : 
     696                 :          1 :     return error;
     697                 :            : }
     698                 :            : 
     699                 :            : static void
     700                 :       1512 : ovsdb_file_destroy(struct ovsdb_replica *replica)
     701                 :            : {
     702                 :       1512 :     struct ovsdb_file *file = ovsdb_file_cast(replica);
     703                 :            : 
     704                 :       1512 :     ovsdb_log_close(file->log);
     705                 :       1512 :     free(file->file_name);
     706                 :       1512 :     free(file);
     707                 :       1512 : }
     708                 :            : 
     709                 :            : static const struct ovsdb_replica_class ovsdb_file_class = {
     710                 :            :     ovsdb_file_commit,
     711                 :            :     ovsdb_file_destroy
     712                 :            : };
     713                 :            : 
     714                 :            : static void
     715                 :      11029 : ovsdb_file_txn_init(struct ovsdb_file_txn *ftxn)
     716                 :            : {
     717                 :      11029 :     ftxn->json = NULL;
     718                 :      11029 :     ftxn->table_json = NULL;
     719                 :      11029 :     ftxn->table = NULL;
     720                 :      11029 : }
     721                 :            : 
     722                 :            : static void
     723                 :      63553 : ovsdb_file_txn_add_row(struct ovsdb_file_txn *ftxn,
     724                 :            :                        const struct ovsdb_row *old,
     725                 :            :                        const struct ovsdb_row *new,
     726                 :            :                        const unsigned long int *changed)
     727                 :            : {
     728                 :            :     struct json *row;
     729                 :            : 
     730         [ +  + ]:      63553 :     if (!new) {
     731                 :       3971 :         row = json_null_create();
     732                 :            :     } else {
     733                 :            :         struct shash_node *node;
     734                 :            : 
     735         [ +  + ]:      59582 :         row = old ? NULL : json_object_create();
     736 [ +  + ][ -  + ]:    1342169 :         SHASH_FOR_EACH (node, &new->table->schema->columns) {
     737                 :    1282587 :             const struct ovsdb_column *column = node->data;
     738                 :    1282587 :             const struct ovsdb_type *type = &column->type;
     739                 :    1282587 :             unsigned int idx = column->index;
     740                 :            : 
     741 [ +  + ][ +  + ]:    1282587 :             if (idx != OVSDB_COL_UUID && column->persistent
     742 [ +  + ][ +  + ]:    1050390 :                 && (old
     743                 :     649970 :                     ? bitmap_is_set(changed, idx)
     744                 :     200210 :                     : !ovsdb_datum_is_default(&new->fields[idx], type)))
     745                 :            :             {
     746         [ +  + ]:      71728 :                 if (!row) {
     747                 :      12647 :                     row = json_object_create();
     748                 :            :                 }
     749                 :      71728 :                 json_object_put(row, column->name,
     750                 :            :                                 ovsdb_datum_to_json(&new->fields[idx], type));
     751                 :            :             }
     752                 :            :         }
     753                 :            :     }
     754                 :            : 
     755         [ +  + ]:      63553 :     if (row) {
     756         [ +  + ]:      33670 :         struct ovsdb_table *table = new ? new->table : old->table;
     757                 :            :         char uuid[UUID_LEN + 1];
     758                 :            : 
     759         [ +  + ]:      33670 :         if (table != ftxn->table) {
     760                 :            :             /* Create JSON object for transaction overall. */
     761         [ +  + ]:      19718 :             if (!ftxn->json) {
     762                 :      10411 :                 ftxn->json = json_object_create();
     763                 :            :             }
     764                 :            : 
     765                 :            :             /* Create JSON object for transaction on this table. */
     766                 :      19718 :             ftxn->table_json = json_object_create();
     767                 :      19718 :             ftxn->table = table;
     768                 :      19718 :             json_object_put(ftxn->json, table->schema->name, ftxn->table_json);
     769                 :            :         }
     770                 :            : 
     771                 :            :         /* Add row to transaction for this table. */
     772                 :     101010 :         snprintf(uuid, sizeof uuid,
     773 [ +  + ][ +  + ]:      33670 :                  UUID_FMT, UUID_ARGS(ovsdb_row_get_uuid(new ? new : old)));
         [ +  + ][ +  + ]
         [ +  + ][ +  + ]
     774                 :      33670 :         json_object_put(ftxn->table_json, uuid, row);
     775                 :            :     }
     776                 :      63553 : }
     777                 :            : 
     778                 :            : static struct ovsdb_error *
     779                 :      10411 : ovsdb_file_txn_commit(struct json *json, const char *comment,
     780                 :            :                       bool durable, struct ovsdb_log *log)
     781                 :            : {
     782                 :            :     struct ovsdb_error *error;
     783                 :            : 
     784         [ -  + ]:      10411 :     if (!json) {
     785                 :          0 :         json = json_object_create();
     786                 :            :     }
     787         [ +  + ]:      10411 :     if (comment) {
     788                 :       4125 :         json_object_put_string(json, "_comment", comment);
     789                 :            :     }
     790                 :      10411 :     json_object_put(json, "_date", json_integer_create(time_wall_msec()));
     791                 :            : 
     792                 :      10411 :     error = ovsdb_log_write(log, json);
     793                 :      10411 :     json_destroy(json);
     794         [ -  + ]:      10411 :     if (error) {
     795                 :          0 :         return ovsdb_wrap_error(error, "writing transaction failed");
     796                 :            :     }
     797                 :            : 
     798         [ +  + ]:      10411 :     if (durable) {
     799                 :         10 :         error = ovsdb_log_commit(log);
     800         [ -  + ]:         10 :         if (error) {
     801                 :          0 :             return ovsdb_wrap_error(error, "committing transaction failed");
     802                 :            :         }
     803                 :            :     }
     804                 :            : 
     805                 :      10411 :     return NULL;
     806                 :            : }

Generated by: LCOV version 1.12