"Fossies" - the Fresh Open Source Software Archive

Member "knot-2.9.2/src/knot/journal/journal_metadata.c" (12 Dec 2019, 11651 Bytes) of package /linux/misc/dns/knot-2.9.2.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "journal_metadata.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 2.9.1_vs_2.9.2.

    1 /*  Copyright (C) 2019 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
    2 
    3     This program is free software: you can redistribute it and/or modify
    4     it under the terms of the GNU General Public License as published by
    5     the Free Software Foundation, either version 3 of the License, or
    6     (at your option) any later version.
    7 
    8     This program is distributed in the hope that it will be useful,
    9     but WITHOUT ANY WARRANTY; without even the implied warranty of
   10     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   11     GNU General Public License for more details.
   12 
   13     You should have received a copy of the GNU General Public License
   14     along with this program.  If not, see <https://www.gnu.org/licenses/>.
   15  */
   16 
   17 #include "knot/journal/journal_metadata.h"
   18 
   19 #include "libknot/endian.h"
   20 #include "libknot/error.h"
   21 
   22 static void fix_endian(void *data, size_t data_size, bool in)
   23 {
   24     union {
   25         uint8_t  u8;
   26         uint16_t u16;
   27         uint32_t u32;
   28         uint64_t u64;
   29     } before, after;
   30 
   31     memcpy(&before, data, data_size);
   32     switch (data_size) {
   33     case sizeof(uint8_t):
   34         return;
   35     case sizeof(uint16_t):
   36         after.u16 = in ? be16toh(before.u16) : htobe16(before.u16);
   37         break;
   38     case sizeof(uint32_t):
   39         after.u32 = in ? be32toh(before.u32) : htobe32(before.u32);
   40         break;
   41     case sizeof(uint64_t):
   42         after.u64 = in ? be64toh(before.u64) : htobe64(before.u64);
   43         break;
   44     default:
   45         assert(0);
   46     }
   47     memcpy(data, &after, data_size);
   48 }
   49 
   50 static MDB_val metadata_key(const knot_dname_t *zone, const char *metadata)
   51 {
   52     if (zone == NULL) {
   53         return knot_lmdb_make_key("IS", (uint32_t)0, metadata);
   54     } else {
   55         return knot_lmdb_make_key("NIS", zone, (uint32_t)0, metadata);
   56     }
   57 }
   58 
   59 static bool del_metadata(knot_lmdb_txn_t *txn, const knot_dname_t *zone, const char *metadata)
   60 {
   61     MDB_val key = metadata_key(zone, metadata);
   62     if (key.mv_data != NULL) {
   63         knot_lmdb_del_prefix(txn, &key);
   64         free(key.mv_data);
   65     }
   66     return (key.mv_data != NULL);
   67 }
   68 
   69 static bool get_metadata(knot_lmdb_txn_t *txn, const knot_dname_t *zone, const char *metadata)
   70 {
   71     MDB_val key = metadata_key(zone, metadata);
   72     bool ret = knot_lmdb_find(txn, &key, KNOT_LMDB_EXACT); // not FORCE
   73     free(key.mv_data);
   74     return ret;
   75 }
   76 
   77 static bool get_metadata_numeric(knot_lmdb_txn_t *txn, const knot_dname_t *zone,
   78                                  const char *metadata, void *result, size_t result_size)
   79 {
   80     if (get_metadata(txn, zone, metadata)) {
   81         if (txn->cur_val.mv_size == result_size) {
   82             memcpy(result, txn->cur_val.mv_data, result_size);
   83             fix_endian(result, result_size, true);
   84             return true;
   85         } else {
   86             txn->ret = KNOT_EMALF;
   87         }
   88     }
   89     return false;
   90 }
   91 
   92 bool get_metadata32(knot_lmdb_txn_t *txn, const knot_dname_t *zone,
   93                     const char *metadata, uint32_t *result)
   94 {
   95     return get_metadata_numeric(txn, zone, metadata, result, sizeof(*result));
   96 }
   97 
   98 bool get_metadata64(knot_lmdb_txn_t *txn, const knot_dname_t *zone,
   99                     const char *metadata, uint64_t *result)
  100 {
  101     return get_metadata_numeric(txn, zone, metadata, result, sizeof(*result));
  102 }
  103 
  104 void set_metadata(knot_lmdb_txn_t *txn, const knot_dname_t *zone, const char *metadata,
  105                   const void *valp, size_t val_size, bool numeric)
  106 {
  107     MDB_val key = metadata_key(zone, metadata);
  108     MDB_val val = { val_size, NULL };
  109     if (knot_lmdb_insert(txn, &key, &val)) {
  110         memcpy(val.mv_data, valp, val_size);
  111         if (numeric) {
  112             fix_endian(val.mv_data, val_size, false);
  113         }
  114     }
  115     free(key.mv_data);
  116 }
  117 
  118 void update_last_inserter(knot_lmdb_txn_t *txn, const knot_dname_t *new_inserter)
  119 {
  120     uint64_t occupied_now = knot_lmdb_usage(txn), occupied_last = 0, lis_occupied = 0;
  121     (void)get_metadata64(txn, NULL, "last_total_occupied", &occupied_last);
  122     knot_dname_t *last_inserter = get_metadata(txn, NULL, "last_inserter_zone") ?
  123                                   knot_dname_copy(txn->cur_val.mv_data, NULL) : NULL;
  124     if (occupied_now == occupied_last || last_inserter == NULL) {
  125         goto update_inserter;
  126     }
  127     (void)get_metadata64(txn, last_inserter, "occupied", &lis_occupied);
  128     if (lis_occupied + occupied_now > occupied_last) {
  129         lis_occupied += occupied_now;
  130         lis_occupied -= occupied_last;
  131     } else {
  132         lis_occupied = 0;
  133     }
  134     set_metadata(txn, last_inserter, "occupied", &lis_occupied, sizeof(lis_occupied), true);
  135 
  136 update_inserter:
  137     if (new_inserter == NULL) {
  138         del_metadata(txn, NULL, "last_inserter_zone");
  139     } else if (last_inserter == NULL || !knot_dname_is_equal(last_inserter, new_inserter)) {
  140         set_metadata(txn, NULL, "last_inserter_zone", new_inserter, knot_dname_size(new_inserter), false);
  141     }
  142     free(last_inserter);
  143     set_metadata(txn, NULL, "last_total_occupied", &occupied_now, sizeof(occupied_now), true);
  144 }
  145 
  146 uint64_t journal_get_occupied(knot_lmdb_txn_t *txn, const knot_dname_t *zone)
  147 {
  148     uint64_t res = 0;
  149     get_metadata64(txn, zone, "occupied", &res);
  150     return res;
  151 }
  152 
  153 static int first_digit(char * of)
  154 {
  155     unsigned maj, min;
  156     return sscanf(of, "%u.%u", &maj, &min) == 2 ? maj : -1;
  157 }
  158 
  159 void journal_load_metadata(knot_lmdb_txn_t *txn, const knot_dname_t *zone, journal_metadata_t *md)
  160 {
  161     memset(md, 0, sizeof(*md));
  162     if (get_metadata(txn, NULL, "version")) {
  163         switch (first_digit(txn->cur_val.mv_data)) {
  164         case 3:
  165             // TODO warning about downgrade
  166             // FALLTHROUGH
  167         case 1:
  168             // still supported
  169             // FALLTHROUGH
  170         case 2:
  171             // normal operation
  172             break;
  173         case 0:
  174             // failed to read version
  175             txn->ret = KNOT_ENOENT;
  176             return;
  177         default:
  178             txn->ret = KNOT_ENOTSUP;
  179             return;
  180         }
  181     }
  182     md->_new_zone = !get_metadata32(txn, zone, "flags", &md->flags);
  183     (void)get_metadata32(txn, zone, "first_serial",    &md->first_serial);
  184     (void)get_metadata32(txn, zone, "last_serial_to",  &md->serial_to);
  185     (void)get_metadata32(txn, zone, "merged_serial",   &md->merged_serial);
  186     (void)get_metadata32(txn, zone, "changeset_count", &md->changeset_count);
  187     if (!get_metadata32(txn, zone, "flushed_upto", &md->flushed_upto)) {
  188         // importing from version 1.0
  189         if ((md->flags & JOURNAL_LAST_FLUSHED_VALID)) {
  190             uint32_t last_flushed = 0;
  191             if (!get_metadata32(txn, zone, "last_flushed", &last_flushed) ||
  192                 !journal_serial_to(txn, false, last_flushed, zone, &md->flushed_upto)) {
  193                 txn->ret = KNOT_EMALF;
  194             } else {
  195                 md->flags &= ~JOURNAL_LAST_FLUSHED_VALID;
  196             }
  197         } else {
  198             md->flushed_upto = md->first_serial;
  199         }
  200     }
  201 
  202 }
  203 
  204 void journal_store_metadata(knot_lmdb_txn_t *txn, const knot_dname_t *zone, const journal_metadata_t *md)
  205 {
  206     set_metadata(txn, zone, "first_serial",    &md->first_serial,    sizeof(md->first_serial),    true);
  207     set_metadata(txn, zone, "last_serial_to",  &md->serial_to,       sizeof(md->serial_to),       true);
  208     set_metadata(txn, zone, "flushed_upto",    &md->flushed_upto,    sizeof(md->flushed_upto),    true);
  209     set_metadata(txn, zone, "merged_serial",   &md->merged_serial,   sizeof(md->merged_serial),   true);
  210     set_metadata(txn, zone, "changeset_count", &md->changeset_count, sizeof(md->changeset_count), true);
  211     set_metadata(txn, zone, "flags",           &md->flags,           sizeof(md->flags),           true);
  212     set_metadata(txn, NULL, "version", "2.0", 4, false);
  213     if (md->_new_zone) {
  214         uint64_t journal_count = 0;
  215         (void)get_metadata64(txn, NULL, "journal_count", &journal_count);
  216         ++journal_count;
  217         set_metadata(txn, NULL, "journal_count", &journal_count, sizeof(journal_count), true);
  218     }
  219 }
  220 
  221 void journal_metadata_after_delete(journal_metadata_t *md, uint32_t deleted_upto,
  222                                    size_t deleted_count)
  223 {
  224     if (deleted_count == 0) {
  225         return;
  226     }
  227     assert((md->flags & JOURNAL_SERIAL_TO_VALID));
  228     if (deleted_upto == md->serial_to) {
  229         assert(md->flushed_upto == md->serial_to);
  230         assert(md->changeset_count == deleted_count);
  231         md->flags &= ~JOURNAL_SERIAL_TO_VALID;
  232     }
  233     md->first_serial = deleted_upto;
  234     md->changeset_count -= deleted_count;
  235 }
  236 
  237 void journal_metadata_after_merge(journal_metadata_t *md, bool merged_zij, uint32_t merged_serial,
  238                                   uint32_t merged_serial_to, uint32_t original_serial_to)
  239 {
  240     md->flushed_upto = merged_serial_to;
  241     if ((md->flags & JOURNAL_MERGED_SERIAL_VALID)) {
  242         assert(!merged_zij);
  243         assert(merged_serial == md->merged_serial);
  244     } else if (!merged_zij) {
  245         md->merged_serial = merged_serial;
  246         md->flags |= JOURNAL_MERGED_SERIAL_VALID;
  247         assert(merged_serial == md->first_serial);
  248         journal_metadata_after_delete(md, original_serial_to, 1); // the merged changeset writes itself instead of first one
  249     }
  250 }
  251 
  252 void journal_metadata_after_insert(journal_metadata_t *md, uint32_t serial, uint32_t serial_to)
  253 {
  254     if (md->first_serial == md->serial_to) { // no changesets yet
  255         md->first_serial = serial;
  256         md->flushed_upto = serial;
  257     }
  258     md->serial_to = serial_to;
  259     md->flags |= JOURNAL_SERIAL_TO_VALID;
  260     md->changeset_count++;
  261 }
  262 
  263 void journal_metadata_after_extra(journal_metadata_t *md, uint32_t serial, uint32_t serial_to)
  264 {
  265     assert(!(md->flags & JOURNAL_MERGED_SERIAL_VALID));
  266     md->merged_serial = serial;
  267     md->flushed_upto = serial_to;
  268     md->flags |= (JOURNAL_MERGED_SERIAL_VALID | JOURNAL_LAST_FLUSHED_VALID);
  269 }
  270 
  271 int journal_scrape_with_md(zone_journal_t j)
  272 {
  273     if (!journal_is_existing(j)) {
  274         return KNOT_EOK;
  275     }
  276     knot_lmdb_txn_t txn = { 0 };
  277     knot_lmdb_begin(j.db, &txn, true);
  278 
  279     update_last_inserter(&txn, NULL);
  280     MDB_val prefix = { knot_dname_size(j.zone), (void *)j.zone };
  281     knot_lmdb_del_prefix(&txn, &prefix);
  282 
  283     knot_lmdb_commit(&txn);
  284     return txn.ret;
  285 }
  286 
  287 int journal_set_flushed(zone_journal_t j)
  288 {
  289     knot_lmdb_txn_t txn = { 0 };
  290     journal_metadata_t md = { 0 };
  291     knot_lmdb_begin(j.db, &txn, true);
  292     journal_load_metadata(&txn, j.zone, &md);
  293 
  294     md.flushed_upto = md.serial_to;
  295 
  296     journal_store_metadata(&txn, j.zone, &md);
  297     knot_lmdb_commit(&txn);
  298     return txn.ret;
  299 }
  300 
  301 int journal_info(zone_journal_t j, bool *exists, uint32_t *first_serial, bool *has_zij,
  302                  uint32_t *serial_to, bool *has_merged, uint32_t *merged_serial,
  303                  uint64_t *occupied, uint64_t *occupied_total)
  304 {
  305     if (!knot_lmdb_exists(j.db)) {
  306         *exists = false;
  307         return KNOT_EOK;
  308     }
  309     int ret = knot_lmdb_open(j.db);
  310     if (ret != KNOT_EOK) {
  311         return ret;
  312     }
  313     knot_lmdb_txn_t txn = { 0 };
  314     journal_metadata_t md = { 0 };
  315     knot_lmdb_begin(j.db, &txn, false);
  316     journal_load_metadata(&txn, j.zone, &md);
  317     *exists = (md.flags & JOURNAL_SERIAL_TO_VALID);
  318     if (first_serial != NULL) {
  319         *first_serial = md.first_serial;
  320     }
  321     if (has_zij != NULL) {
  322         *has_zij = journal_contains(&txn, true, 0, j.zone);
  323     }
  324     if (serial_to != NULL) {
  325         *serial_to = md.serial_to;
  326     }
  327     if (has_merged != NULL) {
  328         *has_merged = (md.flags & JOURNAL_MERGED_SERIAL_VALID);
  329     }
  330     if (merged_serial != NULL) {
  331         *merged_serial = md.merged_serial;
  332     }
  333     if (occupied != NULL) {
  334         *occupied = 0;
  335         get_metadata64(&txn, j.zone, "occupied", occupied);
  336     }
  337     if (occupied_total != NULL) {
  338         *occupied_total = knot_lmdb_usage(&txn);
  339     }
  340     knot_lmdb_abort(&txn);
  341     return txn.ret;
  342 }
  343 
  344 int journals_walk(knot_lmdb_db_t *db, journals_walk_cb_t cb, void *ctx)
  345 {
  346     if (!knot_lmdb_exists(db)) {
  347         return KNOT_EOK;
  348     }
  349     int ret = knot_lmdb_open(db);
  350     if (ret != KNOT_EOK) {
  351         return ret;
  352     }
  353     knot_lmdb_txn_t txn = { 0 };
  354     knot_lmdb_begin(db, &txn, false);
  355     knot_dname_storage_t search_data = { 0 };
  356     MDB_val search = { 1, search_data };
  357     while (knot_lmdb_find(&txn, &search, KNOT_LMDB_GEQ)) {
  358         knot_dname_t *found = txn.cur_key.mv_data;
  359         uint32_t unused_flags;
  360         if (get_metadata32(&txn, found, "flags", &unused_flags)) {
  361             // matched journal DB key appears to be a zone name
  362             txn.ret = cb(found, ctx);
  363         }
  364 
  365         // update searched key to next after found zone
  366         search.mv_size = knot_dname_size(found);
  367         memcpy(search.mv_data, found, search.mv_size);
  368         ((uint8_t *)search.mv_data)[search.mv_size - 1]++;
  369     }
  370     knot_lmdb_abort(&txn);
  371     return txn.ret;
  372 }