LCOV - code coverage report
Current view: top level - ovsdb - log.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 136 161 84.5 %
Date: 2016-09-14 01:02:56 Functions: 9 9 100.0 %
Branches: 70 98 71.4 %

           Branch data     Line data    Source code
       1                 :            : /* Copyright (c) 2009, 2010, 2011, 2012, 2013 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 "log.h"
      19                 :            : 
      20                 :            : #include <errno.h>
      21                 :            : #include <fcntl.h>
      22                 :            : #include <stdlib.h>
      23                 :            : #include <string.h>
      24                 :            : #include <sys/stat.h>
      25                 :            : #include <unistd.h>
      26                 :            : 
      27                 :            : #include "openvswitch/json.h"
      28                 :            : #include "lockfile.h"
      29                 :            : #include "ovsdb.h"
      30                 :            : #include "ovsdb-error.h"
      31                 :            : #include "sha1.h"
      32                 :            : #include "socket-util.h"
      33                 :            : #include "transaction.h"
      34                 :            : #include "util.h"
      35                 :            : 
      36                 :            : enum ovsdb_log_mode {
      37                 :            :     OVSDB_LOG_READ,
      38                 :            :     OVSDB_LOG_WRITE
      39                 :            : };
      40                 :            : 
      41                 :            : struct ovsdb_log {
      42                 :            :     off_t prev_offset;
      43                 :            :     off_t offset;
      44                 :            :     char *name;
      45                 :            :     struct lockfile *lockfile;
      46                 :            :     FILE *stream;
      47                 :            :     struct ovsdb_error *read_error;
      48                 :            :     bool write_error;
      49                 :            :     enum ovsdb_log_mode mode;
      50                 :            : };
      51                 :            : 
      52                 :            : /* Attempts to open 'name' with the specified 'open_mode'.  On success, stores
      53                 :            :  * the new log into '*filep' and returns NULL; otherwise returns NULL and
      54                 :            :  * stores NULL into '*filep'.
      55                 :            :  *
      56                 :            :  * Whether the file will be locked using lockfile_lock() depends on 'locking':
      57                 :            :  * use true to lock it, false not to lock it, or -1 to lock it only if
      58                 :            :  * 'open_mode' is a mode that allows writing.
      59                 :            :  */
      60                 :            : struct ovsdb_error *
      61                 :       2812 : ovsdb_log_open(const char *name, enum ovsdb_log_open_mode open_mode,
      62                 :            :                int locking, struct ovsdb_log **filep)
      63                 :            : {
      64                 :            :     struct lockfile *lockfile;
      65                 :            :     struct ovsdb_error *error;
      66                 :            :     struct ovsdb_log *file;
      67                 :            :     struct stat s;
      68                 :            :     FILE *stream;
      69                 :            :     int flags;
      70                 :            :     int fd;
      71                 :            : 
      72                 :       2812 :     *filep = NULL;
      73                 :            : 
      74 [ +  + ][ +  - ]:       2812 :     ovs_assert(locking == -1 || locking == false || locking == true);
         [ -  + ][ #  # ]
      75         [ +  + ]:       2812 :     if (locking < 0) {
      76                 :       2808 :         locking = open_mode != OVSDB_LOG_READ_ONLY;
      77                 :            :     }
      78         [ +  + ]:       2812 :     if (locking) {
      79                 :       2790 :         int retval = lockfile_lock(name, &lockfile);
      80         [ -  + ]:       2790 :         if (retval) {
      81                 :          0 :             error = ovsdb_io_error(retval, "%s: failed to lock lockfile",
      82                 :            :                                    name);
      83                 :       2790 :             goto error;
      84                 :            :         }
      85                 :            :     } else {
      86                 :         22 :         lockfile = NULL;
      87                 :            :     }
      88                 :            : 
      89         [ +  + ]:       2812 :     if (open_mode == OVSDB_LOG_READ_ONLY) {
      90                 :         18 :         flags = O_RDONLY;
      91         [ +  + ]:       2794 :     } else if (open_mode == OVSDB_LOG_READ_WRITE) {
      92                 :       1521 :         flags = O_RDWR;
      93         [ +  - ]:       1273 :     } else if (open_mode == OVSDB_LOG_CREATE) {
      94                 :            : #ifndef _WIN32
      95 [ +  + ][ +  - ]:       1273 :         if (stat(name, &s) == -1 && errno == ENOENT
      96 [ +  + ][ +  - ]:       1272 :             && lstat(name, &s) == 0 && S_ISLNK(s.st_mode)) {
      97                 :            :             /* 'name' is a dangling symlink.  We want to create the file that
      98                 :            :              * the symlink points to, but POSIX says that open() with O_EXCL
      99                 :            :              * must fail with EEXIST if the named file is a symlink.  So, we
     100                 :            :              * have to leave off O_EXCL and accept the race. */
     101                 :          2 :             flags = O_RDWR | O_CREAT;
     102                 :            :         } else {
     103                 :       1273 :             flags = O_RDWR | O_CREAT | O_EXCL;
     104                 :            :         }
     105                 :            : #else
     106                 :            :         flags = O_RDWR | O_CREAT | O_EXCL;
     107                 :            : #endif
     108                 :            :     } else {
     109                 :          0 :         OVS_NOT_REACHED();
     110                 :            :     }
     111                 :            : #ifdef _WIN32
     112                 :            :     flags = flags | O_BINARY;
     113                 :            : #endif
     114                 :       2812 :     fd = open(name, flags, 0666);
     115         [ +  + ]:       2812 :     if (fd < 0) {
     116         [ +  + ]:          2 :         const char *op = open_mode == OVSDB_LOG_CREATE ? "create" : "open";
     117                 :          2 :         error = ovsdb_io_error(errno, "%s: %s failed", name, op);
     118                 :          2 :         goto error_unlock;
     119                 :            :     }
     120                 :            : 
     121 [ +  - ][ +  + ]:       2810 :     if (!fstat(fd, &s) && s.st_size == 0) {
     122                 :            :         /* It's (probably) a new file so fsync() its parent directory to ensure
     123                 :            :          * that its directory entry is committed to disk. */
     124                 :       1273 :         fsync_parent_dir(name);
     125                 :            :     }
     126                 :            : 
     127         [ +  + ]:       2810 :     stream = fdopen(fd, open_mode == OVSDB_LOG_READ_ONLY ? "rb" : "w+b");
     128         [ -  + ]:       2810 :     if (!stream) {
     129                 :          0 :         error = ovsdb_io_error(errno, "%s: fdopen failed", name);
     130                 :          0 :         goto error_close;
     131                 :            :     }
     132                 :            : 
     133                 :       2810 :     file = xmalloc(sizeof *file);
     134                 :       2810 :     file->name = xstrdup(name);
     135                 :       2810 :     file->lockfile = lockfile;
     136                 :       2810 :     file->stream = stream;
     137                 :       2810 :     file->prev_offset = 0;
     138                 :       2810 :     file->offset = 0;
     139                 :       2810 :     file->read_error = NULL;
     140                 :       2810 :     file->write_error = false;
     141                 :       2810 :     file->mode = OVSDB_LOG_READ;
     142                 :       2810 :     *filep = file;
     143                 :       2810 :     return NULL;
     144                 :            : 
     145                 :            : error_close:
     146                 :          0 :     close(fd);
     147                 :            : error_unlock:
     148                 :          2 :     lockfile_unlock(lockfile);
     149                 :            : error:
     150                 :       2812 :     return error;
     151                 :            : }
     152                 :            : 
     153                 :            : void
     154                 :       2811 : ovsdb_log_close(struct ovsdb_log *file)
     155                 :            : {
     156         [ +  + ]:       2811 :     if (file) {
     157                 :       2808 :         free(file->name);
     158                 :       2808 :         fclose(file->stream);
     159                 :       2808 :         lockfile_unlock(file->lockfile);
     160                 :       2808 :         ovsdb_error_destroy(file->read_error);
     161                 :       2808 :         free(file);
     162                 :            :     }
     163                 :       2811 : }
     164                 :            : 
     165                 :            : static const char magic[] = "OVSDB JSON ";
     166                 :            : 
     167                 :            : static bool
     168                 :       2186 : parse_header(char *header, unsigned long int *length,
     169                 :            :              uint8_t sha1[SHA1_DIGEST_SIZE])
     170                 :            : {
     171                 :            :     char *p;
     172                 :            : 
     173                 :            :     /* 'header' must consist of a magic string... */
     174         [ +  + ]:       2186 :     if (strncmp(header, magic, strlen(magic))) {
     175                 :          3 :         return false;
     176                 :            :     }
     177                 :            : 
     178                 :            :     /* ...followed by a length in bytes... */
     179                 :       2183 :     *length = strtoul(header + strlen(magic), &p, 10);
     180 [ +  - ][ +  - ]:       2183 :     if (!*length || *length == ULONG_MAX || *p != ' ') {
                 [ -  + ]
     181                 :          0 :         return false;
     182                 :            :     }
     183                 :       2183 :     p++;
     184                 :            : 
     185                 :            :     /* ...followed by a SHA-1 hash... */
     186         [ -  + ]:       2183 :     if (!sha1_from_hex(sha1, p)) {
     187                 :          0 :         return false;
     188                 :            :     }
     189                 :       2183 :     p += SHA1_HEX_DIGEST_LEN;
     190                 :            : 
     191                 :            :     /* ...and ended by a new-line. */
     192         [ -  + ]:       2183 :     if (*p != '\n') {
     193                 :          0 :         return false;
     194                 :            :     }
     195                 :            : 
     196                 :       2186 :     return true;
     197                 :            : }
     198                 :            : 
     199                 :            : static struct ovsdb_error *
     200                 :       2183 : parse_body(struct ovsdb_log *file, off_t offset, unsigned long int length,
     201                 :            :            uint8_t sha1[SHA1_DIGEST_SIZE], struct json **jsonp)
     202                 :            : {
     203                 :            :     struct json_parser *parser;
     204                 :            :     struct sha1_ctx ctx;
     205                 :            : 
     206                 :       2183 :     sha1_init(&ctx);
     207                 :       2183 :     parser = json_parser_create(JSPF_TRAILER);
     208                 :            : 
     209         [ +  + ]:       5084 :     while (length > 0) {
     210                 :            :         char input[BUFSIZ];
     211                 :            :         int chunk;
     212                 :            : 
     213                 :       2902 :         chunk = MIN(length, sizeof input);
     214         [ +  + ]:       2902 :         if (fread(input, 1, chunk, file->stream) != chunk) {
     215                 :          1 :             json_parser_abort(parser);
     216         [ -  + ]:          1 :             return ovsdb_io_error(ferror(file->stream) ? errno : EOF,
     217                 :            :                                   "%s: error reading %lu bytes "
     218                 :            :                                   "starting at offset %lld", file->name,
     219                 :            :                                   length, (long long int) offset);
     220                 :            :         }
     221                 :       2901 :         sha1_update(&ctx, input, chunk);
     222                 :       2901 :         json_parser_feed(parser, input, chunk);
     223                 :       2901 :         length -= chunk;
     224                 :            :     }
     225                 :            : 
     226                 :       2182 :     sha1_final(&ctx, sha1);
     227                 :       2182 :     *jsonp = json_parser_finish(parser);
     228                 :       2183 :     return NULL;
     229                 :            : }
     230                 :            : 
     231                 :            : struct ovsdb_error *
     232                 :       3710 : ovsdb_log_read(struct ovsdb_log *file, struct json **jsonp)
     233                 :            : {
     234                 :            :     uint8_t expected_sha1[SHA1_DIGEST_SIZE];
     235                 :            :     uint8_t actual_sha1[SHA1_DIGEST_SIZE];
     236                 :            :     struct ovsdb_error *error;
     237                 :            :     off_t data_offset;
     238                 :            :     unsigned long data_length;
     239                 :            :     struct json *json;
     240                 :            :     char header[128];
     241                 :            : 
     242                 :       3710 :     *jsonp = json = NULL;
     243                 :            : 
     244         [ -  + ]:       3710 :     if (file->read_error) {
     245                 :          0 :         return ovsdb_error_clone(file->read_error);
     246         [ -  + ]:       3710 :     } else if (file->mode == OVSDB_LOG_WRITE) {
     247                 :          0 :         return OVSDB_BUG("reading file in write mode");
     248                 :            :     }
     249                 :            : 
     250         [ +  + ]:       3710 :     if (!fgets(header, sizeof header, file->stream)) {
     251         [ +  - ]:       1524 :         if (feof(file->stream)) {
     252                 :       1524 :             error = NULL;
     253                 :            :         } else {
     254                 :          0 :             error = ovsdb_io_error(errno, "%s: read failed", file->name);
     255                 :            :         }
     256                 :       1524 :         goto error;
     257                 :            :     }
     258                 :            : 
     259         [ +  + ]:       2186 :     if (!parse_header(header, &data_length, expected_sha1)) {
     260                 :          3 :         error = ovsdb_syntax_error(NULL, NULL, "%s: parse error at offset "
     261                 :            :                                    "%lld in header line \"%.*s\"",
     262                 :          3 :                                    file->name, (long long int) file->offset,
     263                 :          3 :                                    (int) strcspn(header, "\n"), header);
     264                 :          3 :         goto error;
     265                 :            :     }
     266                 :            : 
     267                 :       2183 :     data_offset = file->offset + strlen(header);
     268                 :       2183 :     error = parse_body(file, data_offset, data_length, actual_sha1, &json);
     269         [ +  + ]:       2183 :     if (error) {
     270                 :          1 :         goto error;
     271                 :            :     }
     272                 :            : 
     273         [ +  + ]:       2182 :     if (memcmp(expected_sha1, actual_sha1, SHA1_DIGEST_SIZE)) {
     274                 :          1 :         error = ovsdb_syntax_error(NULL, NULL, "%s: %lu bytes starting at "
     275                 :            :                                    "offset %lld have SHA-1 hash "SHA1_FMT" "
     276                 :            :                                    "but should have hash "SHA1_FMT,
     277                 :            :                                    file->name, data_length,
     278                 :            :                                    (long long int) data_offset,
     279                 :         20 :                                    SHA1_ARGS(actual_sha1),
     280                 :         20 :                                    SHA1_ARGS(expected_sha1));
     281                 :          1 :         goto error;
     282                 :            :     }
     283                 :            : 
     284         [ +  + ]:       2181 :     if (json->type == JSON_STRING) {
     285                 :          1 :         error = ovsdb_syntax_error(NULL, NULL, "%s: %lu bytes starting at "
     286                 :            :                                    "offset %lld are not valid JSON (%s)",
     287                 :            :                                    file->name, data_length,
     288                 :            :                                    (long long int) data_offset,
     289                 :          1 :                                    json->u.string);
     290                 :          1 :         goto error;
     291                 :            :     }
     292                 :            : 
     293                 :       2180 :     file->prev_offset = file->offset;
     294                 :       2180 :     file->offset = data_offset + data_length;
     295                 :       2180 :     *jsonp = json;
     296                 :       2180 :     return NULL;
     297                 :            : 
     298                 :            : error:
     299                 :       1530 :     file->read_error = ovsdb_error_clone(error);
     300                 :       1530 :     json_destroy(json);
     301                 :       3710 :     return error;
     302                 :            : }
     303                 :            : 
     304                 :            : /* Causes the log record read by the previous call to ovsdb_log_read() to be
     305                 :            :  * effectively discarded.  The next call to ovsdb_log_write() will overwrite
     306                 :            :  * that previously read record.
     307                 :            :  *
     308                 :            :  * Calling this function more than once has no additional effect.
     309                 :            :  *
     310                 :            :  * This function is useful when ovsdb_log_read() successfully reads a record
     311                 :            :  * but that record does not make sense at a higher level (e.g. it specifies an
     312                 :            :  * invalid transaction). */
     313                 :            : void
     314                 :          1 : ovsdb_log_unread(struct ovsdb_log *file)
     315                 :            : {
     316         [ -  + ]:          1 :     ovs_assert(file->mode == OVSDB_LOG_READ);
     317                 :          1 :     file->offset = file->prev_offset;
     318                 :          1 : }
     319                 :            : 
     320                 :            : struct ovsdb_error *
     321                 :      11704 : ovsdb_log_write(struct ovsdb_log *file, struct json *json)
     322                 :            : {
     323                 :            :     uint8_t sha1[SHA1_DIGEST_SIZE];
     324                 :            :     struct ovsdb_error *error;
     325                 :            :     char *json_string;
     326                 :            :     char header[128];
     327                 :            :     size_t length;
     328                 :            : 
     329                 :      11704 :     json_string = NULL;
     330                 :            : 
     331 [ +  + ][ -  + ]:      11704 :     if (file->mode == OVSDB_LOG_READ || file->write_error) {
     332                 :       2614 :         file->mode = OVSDB_LOG_WRITE;
     333                 :       2614 :         file->write_error = false;
     334         [ -  + ]:       2614 :         if (fseeko(file->stream, file->offset, SEEK_SET)) {
     335                 :          0 :             error = ovsdb_io_error(errno, "%s: cannot seek to offset %lld",
     336                 :          0 :                                    file->name, (long long int) file->offset);
     337                 :          0 :             goto error;
     338                 :            :         }
     339         [ -  + ]:       2614 :         if (ftruncate(fileno(file->stream), file->offset)) {
     340                 :          0 :             error = ovsdb_io_error(errno, "%s: cannot truncate to length %lld",
     341                 :          0 :                                    file->name, (long long int) file->offset);
     342                 :          0 :             goto error;
     343                 :            :         }
     344                 :            :     }
     345                 :            : 
     346 [ +  + ][ -  + ]:      11704 :     if (json->type != JSON_OBJECT && json->type != JSON_ARRAY) {
     347                 :          0 :         error = OVSDB_BUG("bad JSON type");
     348                 :          0 :         goto error;
     349                 :            :     }
     350                 :            : 
     351                 :            :     /* Compose content.  Add a new-line (replacing the null terminator) to make
     352                 :            :      * the file easier to read, even though it has no semantic value.  */
     353                 :      11704 :     json_string = json_to_string(json, 0);
     354                 :      11704 :     length = strlen(json_string) + 1;
     355                 :      11704 :     json_string[length - 1] = '\n';
     356                 :            : 
     357                 :            :     /* Compose header. */
     358                 :      11704 :     sha1_bytes(json_string, length, sha1);
     359                 :     234080 :     snprintf(header, sizeof header, "%s%"PRIuSIZE" "SHA1_FMT"\n",
     360                 :     234080 :              magic, length, SHA1_ARGS(sha1));
     361                 :            : 
     362                 :            :     /* Write. */
     363         [ +  - ]:      11704 :     if (fwrite(header, strlen(header), 1, file->stream) != 1
     364         [ +  - ]:      11704 :         || fwrite(json_string, length, 1, file->stream) != 1
     365         [ -  + ]:      11704 :         || fflush(file->stream))
     366                 :            :     {
     367                 :          0 :         error = ovsdb_io_error(errno, "%s: write failed", file->name);
     368                 :            : 
     369                 :            :         /* Remove any partially written data, ignoring errors since there is
     370                 :            :          * nothing further we can do. */
     371                 :          0 :         ignore(ftruncate(fileno(file->stream), file->offset));
     372                 :            : 
     373                 :          0 :         goto error;
     374                 :            :     }
     375                 :            : 
     376                 :      11704 :     file->offset += strlen(header) + length;
     377                 :      11704 :     free(json_string);
     378                 :      11704 :     return NULL;
     379                 :            : 
     380                 :            : error:
     381                 :          0 :     file->write_error = true;
     382                 :          0 :     free(json_string);
     383                 :      11704 :     return error;
     384                 :            : }
     385                 :            : 
     386                 :            : struct ovsdb_error *
     387                 :       1268 : ovsdb_log_commit(struct ovsdb_log *file)
     388                 :            : {
     389         [ -  + ]:       1268 :     if (fsync(fileno(file->stream))) {
     390                 :          0 :         return ovsdb_io_error(errno, "%s: fsync failed", file->name);
     391                 :            :     }
     392                 :       1268 :     return NULL;
     393                 :            : }
     394                 :            : 
     395                 :            : /* Returns the current offset into the file backing 'log', in bytes.  This
     396                 :            :  * reflects the number of bytes that have been read or written in the file.  If
     397                 :            :  * the whole file has been read, this is the file size. */
     398                 :            : off_t
     399                 :      10686 : ovsdb_log_get_offset(const struct ovsdb_log *log)
     400                 :            : {
     401                 :      10686 :     return log->offset;
     402                 :            : }

Generated by: LCOV version 1.12