root/lib/common/digest.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. dump_xml_for_digest
  2. calculate_xml_digest_v1
  3. calculate_xml_digest_v2
  4. calculate_on_disk_digest
  5. calculate_operation_digest
  6. calculate_xml_versioned_digest
  7. crm_digest_verify

   1 /*
   2  * Copyright (C) 2015 Andrew Beekhof <andrew@beekhof.net>
   3  *
   4  * This library is free software; you can redistribute it and/or
   5  * modify it under the terms of the GNU Lesser General Public
   6  * License as published by the Free Software Foundation; either
   7  * version 2.1 of the License, or (at your option) any later version.
   8  *
   9  * This library is distributed in the hope that it will be useful,
  10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  12  * Lesser General Public License for more details.
  13  *
  14  * You should have received a copy of the GNU Lesser General Public
  15  * License along with this library; if not, write to the Free Software
  16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  17  */
  18 
  19 #include <crm_internal.h>
  20 
  21 #include <stdio.h>
  22 #include <unistd.h>
  23 #include <string.h>
  24 #include <stdlib.h>
  25 
  26 #include <crm/crm.h>
  27 #include <crm/msg_xml.h>
  28 #include <crm/common/xml.h>
  29 
  30 #define BEST_EFFORT_STATUS 0
  31 
  32 /*!
  33  * \brief Dump XML in a format used with v1 digests
  34  *
  35  * \param[in] an_xml_node Root of XML to dump
  36  *
  37  * \return Newly allocated buffer containing dumped XML
  38  */
  39 static char *
  40 dump_xml_for_digest(xmlNode * an_xml_node)
     /* [previous][next][first][last][top][bottom][index][help] */
  41 {
  42     char *buffer = NULL;
  43     int offset = 0, max = 0;
  44 
  45     /* for compatibility with the old result which is used for v1 digests */
  46     crm_buffer_add_char(&buffer, &offset, &max, ' ');
  47     crm_xml_dump(an_xml_node, 0, &buffer, &offset, &max, 0);
  48     crm_buffer_add_char(&buffer, &offset, &max, '\n');
  49 
  50     return buffer;
  51 }
  52 
  53 /*!
  54  * \brief Calculate and return v1 digest of XML tree
  55  *
  56  * \param[in] input Root of XML to digest
  57  * \param[in] sort Whether to sort the XML before calculating digest
  58  * \param[in] ignored Not used
  59  *
  60  * \return Newly allocated string containing digest
  61  * \note Example return value: "c048eae664dba840e1d2060f00299e9d"
  62  */
  63 static char *
  64 calculate_xml_digest_v1(xmlNode * input, gboolean sort, gboolean ignored)
     /* [previous][next][first][last][top][bottom][index][help] */
  65 {
  66     char *digest = NULL;
  67     char *buffer = NULL;
  68     xmlNode *copy = NULL;
  69 
  70     if (sort) {
  71         crm_trace("Sorting xml...");
  72         copy = sorted_xml(input, NULL, TRUE);
  73         crm_trace("Done");
  74         input = copy;
  75     }
  76 
  77     buffer = dump_xml_for_digest(input);
  78     CRM_CHECK(buffer != NULL && strlen(buffer) > 0, free_xml(copy);
  79               free(buffer);
  80               return NULL);
  81 
  82     digest = crm_md5sum(buffer);
  83     crm_log_xml_trace(input, "digest:source");
  84 
  85     free(buffer);
  86     free_xml(copy);
  87     return digest;
  88 }
  89 
  90 /*!
  91  * \brief Calculate and return v2 digest of XML tree
  92  *
  93  * \param[in] source Root of XML to digest
  94  * \param[in] do_filter Whether to filter certain XML attributes
  95  *
  96  * \return Newly allocated string containing digest
  97  */
  98 static char *
  99 calculate_xml_digest_v2(xmlNode * source, gboolean do_filter)
     /* [previous][next][first][last][top][bottom][index][help] */
 100 {
 101     char *digest = NULL;
 102     char *buffer = NULL;
 103     int offset, max;
 104 
 105     static struct qb_log_callsite *digest_cs = NULL;
 106 
 107     crm_trace("Begin digest %s", do_filter?"filtered":"");
 108     if (do_filter && BEST_EFFORT_STATUS) {
 109         /* Exclude the status calculation from the digest
 110          *
 111          * This doesn't mean it won't be sync'd, we just won't be paranoid
 112          * about it being an _exact_ copy
 113          *
 114          * We don't need it to be exact, since we throw it away and regenerate
 115          * from our peers whenever a new DC is elected anyway
 116          *
 117          * Importantly, this reduces the amount of XML to copy+export as
 118          * well as the amount of data for MD5 needs to operate on
 119          */
 120 
 121     } else {
 122         crm_xml_dump(source, do_filter ? xml_log_option_filtered : 0, &buffer, &offset, &max, 0);
 123     }
 124 
 125     CRM_ASSERT(buffer != NULL);
 126     digest = crm_md5sum(buffer);
 127 
 128     if (digest_cs == NULL) {
 129         digest_cs = qb_log_callsite_get(__func__, __FILE__, "cib-digest", LOG_TRACE, __LINE__,
 130                                         crm_trace_nonlog);
 131     }
 132     if (digest_cs && digest_cs->targets) {
 133         char *trace_file = crm_concat("/tmp/digest", digest, '-');
 134 
 135         crm_trace("Saving %s.%s.%s to %s",
 136                   crm_element_value(source, XML_ATTR_GENERATION_ADMIN),
 137                   crm_element_value(source, XML_ATTR_GENERATION),
 138                   crm_element_value(source, XML_ATTR_NUMUPDATES), trace_file);
 139         save_xml_to_file(source, "digest input", trace_file);
 140         free(trace_file);
 141     }
 142 
 143     free(buffer);
 144     crm_trace("End digest");
 145     return digest;
 146 }
 147 
 148 /*!
 149  * \brief Calculate and return digest of XML tree, suitable for storing on disk
 150  *
 151  * \param[in] input Root of XML to digest
 152  *
 153  * \return Newly allocated string containing digest
 154  */
 155 char *
 156 calculate_on_disk_digest(xmlNode * input)
     /* [previous][next][first][last][top][bottom][index][help] */
 157 {
 158     /* Always use the v1 format for on-disk digests
 159      * a) it's a compatibility nightmare
 160      * b) we only use this once at startup, all other
 161      *    invocations are in a separate child process
 162      */
 163     return calculate_xml_digest_v1(input, FALSE, FALSE);
 164 }
 165 
 166 /*!
 167  * \brief Calculate and return digest of XML operation
 168  *
 169  * \param[in] input Root of XML to digest
 170  * \param[in] version Not used
 171  *
 172  * \return Newly allocated string containing digest
 173  */
 174 char *
 175 calculate_operation_digest(xmlNode *input, const char *version)
     /* [previous][next][first][last][top][bottom][index][help] */
 176 {
 177     /* We still need the sorting for operation digests */
 178     return calculate_xml_digest_v1(input, TRUE, FALSE);
 179 }
 180 
 181 /*!
 182  * \brief Calculate and return digest of XML tree
 183  *
 184  * \param[in] input Root of XML to digest
 185  * \param[in] sort Whether to sort XML before calculating digest
 186  * \param[in] do_filter Whether to filter certain XML attributes
 187  * \param[in] version CRM feature set version (used to select v1/v2 digest)
 188  *
 189  * \return Newly allocated string containing digest
 190  */
 191 char *
 192 calculate_xml_versioned_digest(xmlNode * input, gboolean sort, gboolean do_filter,
     /* [previous][next][first][last][top][bottom][index][help] */
 193                                const char *version)
 194 {
 195     /*
 196      * The sorting associated with v1 digest creation accounted for 23% of
 197      * the CIB's CPU usage on the server. v2 drops this.
 198      *
 199      * The filtering accounts for an additional 2.5% and we may want to
 200      * remove it in future.
 201      *
 202      * v2 also uses the xmlBuffer contents directly to avoid additional copying
 203      */
 204     if (version == NULL || compare_version("3.0.5", version) > 0) {
 205         crm_trace("Using v1 digest algorithm for %s", crm_str(version));
 206         return calculate_xml_digest_v1(input, sort, do_filter);
 207     }
 208     crm_trace("Using v2 digest algorithm for %s", crm_str(version));
 209     return calculate_xml_digest_v2(input, do_filter);
 210 }
 211 
 212 /*!
 213  * \internal
 214  * \brief Return whether calculated digest of XML tree matches expected digest
 215  *
 216  * \param[in] input Root of XML to digest
 217  * \param[in] expected Expected digest in on-disk format
 218  *
 219  * \return TRUE if digests match, FALSE otherwise or on error
 220  */
 221 gboolean
 222 crm_digest_verify(xmlNode *input, const char *expected)
     /* [previous][next][first][last][top][bottom][index][help] */
 223 {
 224     char *calculated = NULL;
 225     gboolean passed;
 226 
 227     if (input != NULL) {
 228         calculated = calculate_on_disk_digest(input);
 229         if (calculated == NULL) {
 230             crm_perror(LOG_ERR, "Could not calculate digest for comparison");
 231             return FALSE;
 232         }
 233     }
 234     passed = safe_str_eq(expected, calculated);
 235     if (passed) {
 236         crm_trace("Digest comparison passed: %s", calculated);
 237     } else {
 238         crm_err("Digest comparison failed: expected %s, calculated %s",
 239                 expected, calculated);
 240     }
 241     free(calculated);
 242     return passed;
 243 }

/* [previous][next][first][last][top][bottom][index][help] */