This source file includes following definitions.
- read_stdin
 
- decompress_file
 
- pcmk__xml_read
 
- pcmk__xml_parse
 
- dump_xml_element
 
- dump_xml_text
 
- dump_xml_cdata
 
- dump_xml_comment
 
- xml_element_type_text
 
- pcmk__xml_string
 
- write_compressed_stream
 
- write_xml_stream
 
- pcmk__xml_write_fd
 
- pcmk__xml_write_file
 
- pcmk__xml2fd
 
- save_xml_to_file
 
- filename2xml
 
- stdin2xml
 
- string2xml
 
- dump_xml_formatted
 
- dump_xml_formatted_with_text
 
- dump_xml_unformatted
 
- write_xml_fd
 
- write_xml_file
 
   1 
   2 
   3 
   4 
   5 
   6 
   7 
   8 
   9 
  10 #include <crm_internal.h>
  11 
  12 #include <stdio.h>
  13 #include <stdlib.h>
  14 #include <string.h>
  15 #include <sys/types.h>
  16 
  17 #include <bzlib.h>
  18 #include <libxml/parser.h>
  19 #include <libxml/tree.h>
  20 #include <libxml/xmlIO.h>               
  21 
  22 #include <crm/crm.h>
  23 #include <crm/common/xml.h>
  24 #include <crm/common/xml_io.h>
  25 #include "crmcommon_private.h"
  26 
  27 
  28 
  29 
  30 
  31 
  32 #define PCMK__XML_PARSE_OPTS_WITHOUT_RECOVER    (XML_PARSE_NOBLANKS)
  33 #define PCMK__XML_PARSE_OPTS_WITH_RECOVER       (XML_PARSE_NOBLANKS \
  34                                                  |XML_PARSE_RECOVER)
  35 
  36 
  37 
  38 
  39 
  40 
  41 
  42 
  43 
  44 
  45 static char *
  46 read_stdin(void)
     
  47 {
  48     char *buf = NULL;
  49     size_t length = 0;
  50 
  51     do {
  52         buf = pcmk__realloc(buf, length + PCMK__BUFFER_SIZE + 1);
  53         length += fread(buf + length, 1, PCMK__BUFFER_SIZE, stdin);
  54     } while ((feof(stdin) == 0) && (ferror(stdin) == 0));
  55 
  56     if (ferror(stdin) != 0) {
  57         crm_err("Error reading input from stdin");
  58         free(buf);
  59         buf = NULL;
  60     } else {
  61         buf[length] = '\0';
  62     }
  63     clearerr(stdin);
  64     return buf;
  65 }
  66 
  67 
  68 
  69 
  70 
  71 
  72 
  73 
  74 
  75 
  76 
  77 
  78 static char *
  79 decompress_file(const char *filename)
     
  80 {
  81     char *buffer = NULL;
  82     int rc = pcmk_rc_ok;
  83     size_t length = 0;
  84     BZFILE *bz_file = NULL;
  85     FILE *input = fopen(filename, "r");
  86 
  87     if (input == NULL) {
  88         crm_perror(LOG_ERR, "Could not open %s for reading", filename);
  89         return NULL;
  90     }
  91 
  92     bz_file = BZ2_bzReadOpen(&rc, input, 0, 0, NULL, 0);
  93     rc = pcmk__bzlib2rc(rc);
  94     if (rc != pcmk_rc_ok) {
  95         crm_err("Could not prepare to read compressed %s: %s "
  96                 CRM_XS " rc=%d", filename, pcmk_rc_str(rc), rc);
  97         goto done;
  98     }
  99 
 100     
 101     
 102     do {
 103         int read_len = 0;
 104 
 105         buffer = pcmk__realloc(buffer, length + PCMK__BUFFER_SIZE + 1);
 106         read_len = BZ2_bzRead(&rc, bz_file, buffer + length, PCMK__BUFFER_SIZE);
 107 
 108         if ((rc == BZ_OK) || (rc == BZ_STREAM_END)) {
 109             crm_trace("Read %ld bytes from file: %d", (long) read_len, rc);
 110             length += read_len;
 111         }
 112     } while (rc == BZ_OK);
 113 
 114     rc = pcmk__bzlib2rc(rc);
 115     if (rc != pcmk_rc_ok) {
 116         rc = pcmk__bzlib2rc(rc);
 117         crm_err("Could not read compressed %s: %s " CRM_XS " rc=%d",
 118                 filename, pcmk_rc_str(rc), rc);
 119         free(buffer);
 120         buffer = NULL;
 121     } else {
 122         buffer[length] = '\0';
 123     }
 124 
 125 done:
 126     BZ2_bzReadClose(&rc, bz_file);
 127     fclose(input);
 128     return buffer;
 129 }
 130 
 131 
 132 
 133 
 134 
 135 
 136 
 137 
 138 
 139 
 140 
 141 #define parse_xml_recover(result, fn, ...) do {                             \
 142         *result = fn(__VA_ARGS__, PCMK__XML_PARSE_OPTS_WITHOUT_RECOVER);    \
 143         if (*result == NULL) {                                              \
 144             *result = fn(__VA_ARGS__, PCMK__XML_PARSE_OPTS_WITH_RECOVER);   \
 145                                                                             \
 146             if (*result != NULL) {                                          \
 147                 crm_warn("Successfully recovered from XML errors "          \
 148                          "(note: a future release will treat this as a "    \
 149                          "fatal failure)");                                 \
 150             }                                                               \
 151         }                                                                   \
 152     } while (0);
 153 
 154 
 155 
 156 
 157 
 158 
 159 
 160 
 161 
 162 
 163 
 164 
 165 xmlNode *
 166 pcmk__xml_read(const char *filename)
     
 167 {
 168     bool use_stdin = pcmk__str_eq(filename, "-", pcmk__str_null_matches);
 169     xmlNode *xml = NULL;
 170     xmlDoc *output = NULL;
 171     xmlParserCtxt *ctxt = NULL;
 172     const xmlError *last_error = NULL;
 173 
 174     
 175     ctxt = xmlNewParserCtxt();
 176     CRM_CHECK(ctxt != NULL, return NULL);
 177 
 178     xmlCtxtResetLastError(ctxt);
 179     xmlSetGenericErrorFunc(ctxt, pcmk__log_xmllib_err);
 180 
 181     if (use_stdin) {
 182         
 183 
 184 
 185 
 186 
 187 
 188         char *input = read_stdin();
 189 
 190         if (input != NULL) {
 191             parse_xml_recover(&output, xmlCtxtReadDoc, ctxt, (pcmkXmlStr) input,
 192                               NULL, NULL);
 193             free(input);
 194         }
 195 
 196     } else if (pcmk__ends_with_ext(filename, ".bz2")) {
 197         char *input = decompress_file(filename);
 198 
 199         if (input != NULL) {
 200             parse_xml_recover(&output, xmlCtxtReadDoc, ctxt, (pcmkXmlStr) input,
 201                               NULL, NULL);
 202             free(input);
 203         }
 204 
 205     } else {
 206         parse_xml_recover(&output, xmlCtxtReadFile, ctxt, filename, NULL);
 207     }
 208 
 209     if (output != NULL) {
 210         xml = xmlDocGetRootElement(output);
 211         if (xml != NULL) {
 212             
 213 
 214 
 215 
 216 
 217 
 218             pcmk__strip_xml_text(xml);
 219         }
 220     }
 221 
 222     
 223     last_error = xmlCtxtGetLastError(ctxt);
 224     if (last_error != NULL) {
 225         if (xml != NULL) {
 226             crm_log_xml_info(xml, "Partial");
 227         }
 228     }
 229 
 230     xmlFreeParserCtxt(ctxt);
 231     return xml;
 232 }
 233 
 234 
 235 
 236 
 237 
 238 
 239 
 240 
 241 
 242 
 243 xmlNode *
 244 pcmk__xml_parse(const char *input)
     
 245 {
 246     xmlNode *xml = NULL;
 247     xmlDoc *output = NULL;
 248     xmlParserCtxt *ctxt = NULL;
 249     const xmlError *last_error = NULL;
 250 
 251     if (input == NULL) {
 252         return NULL;
 253     }
 254 
 255     ctxt = xmlNewParserCtxt();
 256     if (ctxt == NULL) {
 257         return NULL;
 258     }
 259 
 260     xmlCtxtResetLastError(ctxt);
 261     xmlSetGenericErrorFunc(ctxt, pcmk__log_xmllib_err);
 262 
 263     parse_xml_recover(&output, xmlCtxtReadDoc, ctxt, (pcmkXmlStr) input, NULL,
 264                       NULL);
 265 
 266     if (output != NULL) {
 267         xml = xmlDocGetRootElement(output);
 268     }
 269 
 270     
 271     last_error = xmlCtxtGetLastError(ctxt);
 272     if (last_error != NULL) {
 273         if (xml != NULL) {
 274             crm_log_xml_info(xml, "Partial");
 275         }
 276     }
 277 
 278     xmlFreeParserCtxt(ctxt);
 279     return xml;
 280 }
 281 
 282 
 283 
 284 
 285 
 286 
 287 
 288 
 289 
 290 
 291 static void
 292 dump_xml_element(const xmlNode *data, uint32_t options, GString *buffer,
     
 293                  int depth)
 294 {
 295     bool pretty = pcmk_is_set(options, pcmk__xml_fmt_pretty);
 296     bool filtered = pcmk_is_set(options, pcmk__xml_fmt_filtered);
 297     int spaces = pretty? (2 * depth) : 0;
 298 
 299     for (int lpc = 0; lpc < spaces; lpc++) {
 300         g_string_append_c(buffer, ' ');
 301     }
 302 
 303     pcmk__g_strcat(buffer, "<", data->name, NULL);
 304 
 305     for (const xmlAttr *attr = pcmk__xe_first_attr(data); attr != NULL;
 306          attr = attr->next) {
 307 
 308         if (!filtered || !pcmk__xa_filterable((const char *) (attr->name))) {
 309             pcmk__dump_xml_attr(attr, buffer);
 310         }
 311     }
 312 
 313     if (data->children == NULL) {
 314         g_string_append(buffer, "/>");
 315 
 316     } else {
 317         g_string_append_c(buffer, '>');
 318     }
 319 
 320     if (pretty) {
 321         g_string_append_c(buffer, '\n');
 322     }
 323 
 324     if (data->children) {
 325         for (const xmlNode *child = data->children; child != NULL;
 326              child = child->next) {
 327             pcmk__xml_string(child, options, buffer, depth + 1);
 328         }
 329 
 330         for (int lpc = 0; lpc < spaces; lpc++) {
 331             g_string_append_c(buffer, ' ');
 332         }
 333 
 334         pcmk__g_strcat(buffer, "</", data->name, ">", NULL);
 335 
 336         if (pretty) {
 337             g_string_append_c(buffer, '\n');
 338         }
 339     }
 340 }
 341 
 342 
 343 
 344 
 345 
 346 
 347 
 348 
 349 
 350 
 351 static void
 352 dump_xml_text(const xmlNode *data, uint32_t options, GString *buffer,
     
 353               int depth)
 354 {
 355     bool pretty = pcmk_is_set(options, pcmk__xml_fmt_pretty);
 356     int spaces = pretty? (2 * depth) : 0;
 357     const char *content = (const char *) data->content;
 358     gchar *content_esc = NULL;
 359 
 360     if (pcmk__xml_needs_escape(content, pcmk__xml_escape_text)) {
 361         content_esc = pcmk__xml_escape(content, pcmk__xml_escape_text);
 362         content = content_esc;
 363     }
 364 
 365     for (int lpc = 0; lpc < spaces; lpc++) {
 366         g_string_append_c(buffer, ' ');
 367     }
 368 
 369     g_string_append(buffer, content);
 370 
 371     if (pretty) {
 372         g_string_append_c(buffer, '\n');
 373     }
 374     g_free(content_esc);
 375 }
 376 
 377 
 378 
 379 
 380 
 381 
 382 
 383 
 384 
 385 
 386 static void
 387 dump_xml_cdata(const xmlNode *data, uint32_t options, GString *buffer,
     
 388                int depth)
 389 {
 390     bool pretty = pcmk_is_set(options, pcmk__xml_fmt_pretty);
 391     int spaces = pretty? (2 * depth) : 0;
 392 
 393     for (int lpc = 0; lpc < spaces; lpc++) {
 394         g_string_append_c(buffer, ' ');
 395     }
 396 
 397     pcmk__g_strcat(buffer, "<![CDATA[", (const char *) data->content, "]]>",
 398                    NULL);
 399 
 400     if (pretty) {
 401         g_string_append_c(buffer, '\n');
 402     }
 403 }
 404 
 405 
 406 
 407 
 408 
 409 
 410 
 411 
 412 
 413 
 414 static void
 415 dump_xml_comment(const xmlNode *data, uint32_t options, GString *buffer,
     
 416                  int depth)
 417 {
 418     bool pretty = pcmk_is_set(options, pcmk__xml_fmt_pretty);
 419     int spaces = pretty? (2 * depth) : 0;
 420 
 421     for (int lpc = 0; lpc < spaces; lpc++) {
 422         g_string_append_c(buffer, ' ');
 423     }
 424 
 425     pcmk__g_strcat(buffer, "<!--", (const char *) data->content, "-->", NULL);
 426 
 427     if (pretty) {
 428         g_string_append_c(buffer, '\n');
 429     }
 430 }
 431 
 432 
 433 
 434 
 435 
 436 
 437 
 438 
 439 
 440 static const char *
 441 xml_element_type_text(xmlElementType type)
     
 442 {
 443     static const char *const element_type_names[] = {
 444         [XML_ELEMENT_NODE]       = "element",
 445         [XML_ATTRIBUTE_NODE]     = "attribute",
 446         [XML_TEXT_NODE]          = "text",
 447         [XML_CDATA_SECTION_NODE] = "CDATA section",
 448         [XML_ENTITY_REF_NODE]    = "entity reference",
 449         [XML_ENTITY_NODE]        = "entity",
 450         [XML_PI_NODE]            = "PI",
 451         [XML_COMMENT_NODE]       = "comment",
 452         [XML_DOCUMENT_NODE]      = "document",
 453         [XML_DOCUMENT_TYPE_NODE] = "document type",
 454         [XML_DOCUMENT_FRAG_NODE] = "document fragment",
 455         [XML_NOTATION_NODE]      = "notation",
 456         [XML_HTML_DOCUMENT_NODE] = "HTML document",
 457         [XML_DTD_NODE]           = "DTD",
 458         [XML_ELEMENT_DECL]       = "element declaration",
 459         [XML_ATTRIBUTE_DECL]     = "attribute declaration",
 460         [XML_ENTITY_DECL]        = "entity declaration",
 461         [XML_NAMESPACE_DECL]     = "namespace declaration",
 462         [XML_XINCLUDE_START]     = "XInclude start",
 463         [XML_XINCLUDE_END]       = "XInclude end",
 464     };
 465 
 466     if ((type < 0) || (type >= PCMK__NELEM(element_type_names))) {
 467         return "unrecognized type";
 468     }
 469     return element_type_names[type];
 470 }
 471 
 472 
 473 
 474 
 475 
 476 
 477 
 478 
 479 
 480 
 481 
 482 
 483 
 484 
 485 
 486 
 487 void
 488 pcmk__xml_string(const xmlNode *data, uint32_t options, GString *buffer,
     
 489                  int depth)
 490 {
 491     if (data == NULL) {
 492         crm_trace("Nothing to dump");
 493         return;
 494     }
 495 
 496     CRM_ASSERT(buffer != NULL);
 497     CRM_CHECK(depth >= 0, depth = 0);
 498 
 499     switch(data->type) {
 500         case XML_ELEMENT_NODE:
 501             
 502             dump_xml_element(data, options, buffer, depth);
 503             break;
 504         case XML_TEXT_NODE:
 505             if (pcmk_is_set(options, pcmk__xml_fmt_text)) {
 506                 dump_xml_text(data, options, buffer, depth);
 507             }
 508             break;
 509         case XML_COMMENT_NODE:
 510             dump_xml_comment(data, options, buffer, depth);
 511             break;
 512         case XML_CDATA_SECTION_NODE:
 513             dump_xml_cdata(data, options, buffer, depth);
 514             break;
 515         default:
 516             crm_warn("Cannot convert XML %s node to text " CRM_XS " type=%d",
 517                      xml_element_type_text(data->type), data->type);
 518             break;
 519     }
 520 }
 521 
 522 
 523 
 524 
 525 
 526 
 527 
 528 
 529 
 530 
 531 
 532 
 533 static int
 534 write_compressed_stream(char *text, const char *filename, FILE *stream,
     
 535                         unsigned int *bytes_out)
 536 {
 537     unsigned int bytes_in = 0;
 538     int rc = pcmk_rc_ok;
 539 
 540     
 541     BZFILE *bz_file = BZ2_bzWriteOpen(&rc, stream, 5, 0, 0);
 542 
 543     rc = pcmk__bzlib2rc(rc);
 544     if (rc != pcmk_rc_ok) {
 545         crm_warn("Not compressing %s: could not prepare file stream: %s "
 546                  CRM_XS " rc=%d",
 547                  filename, pcmk_rc_str(rc), rc);
 548         goto done;
 549     }
 550 
 551     BZ2_bzWrite(&rc, bz_file, text, strlen(text));
 552     rc = pcmk__bzlib2rc(rc);
 553     if (rc != pcmk_rc_ok) {
 554         crm_warn("Not compressing %s: could not compress data: %s "
 555                  CRM_XS " rc=%d errno=%d",
 556                  filename, pcmk_rc_str(rc), rc, errno);
 557         goto done;
 558     }
 559 
 560     BZ2_bzWriteClose(&rc, bz_file, 0, &bytes_in, bytes_out);
 561     bz_file = NULL;
 562     rc = pcmk__bzlib2rc(rc);
 563     if (rc != pcmk_rc_ok) {
 564         crm_warn("Not compressing %s: could not write compressed data: %s "
 565                  CRM_XS " rc=%d errno=%d",
 566                  filename, pcmk_rc_str(rc), rc, errno);
 567         goto done;
 568     }
 569 
 570     crm_trace("Compressed XML for %s from %u bytes to %u",
 571               filename, bytes_in, *bytes_out);
 572 
 573 done:
 574     if (bz_file != NULL) {
 575         BZ2_bzWriteClose(&rc, bz_file, 0, NULL, NULL);
 576     }
 577     return rc;
 578 }
 579 
 580 
 581 
 582 
 583 
 584 
 585 
 586 
 587 
 588 
 589 
 590 
 591 
 592 
 593 static int
 594 write_xml_stream(const xmlNode *xml, const char *filename, FILE *stream,
     
 595                  bool compress, unsigned int *nbytes)
 596 {
 597     
 598     GString *buffer = g_string_sized_new(1024);
 599     unsigned int bytes_out = 0;
 600     int rc = pcmk_rc_ok;
 601 
 602     pcmk__xml_string(xml, pcmk__xml_fmt_pretty, buffer, 0);
 603     CRM_CHECK(!pcmk__str_empty(buffer->str),
 604               crm_log_xml_info(xml, "dump-failed");
 605               rc = pcmk_rc_error;
 606               goto done);
 607 
 608     crm_log_xml_trace(xml, "writing");
 609 
 610     if (compress
 611         && (write_compressed_stream(buffer->str, filename, stream,
 612                                     &bytes_out) == pcmk_rc_ok)) {
 613         goto done;
 614     }
 615 
 616     rc = fprintf(stream, "%s", buffer->str);
 617     if (rc < 0) {
 618         rc = EIO;
 619         crm_perror(LOG_ERR, "writing %s", filename);
 620         goto done;
 621     }
 622     bytes_out = (unsigned int) rc;
 623     rc = pcmk_rc_ok;
 624 
 625 done:
 626     if (fflush(stream) != 0) {
 627         rc = errno;
 628         crm_perror(LOG_ERR, "flushing %s", filename);
 629     }
 630 
 631     
 632     if ((fsync(fileno(stream)) < 0) && (errno != EROFS) && (errno != EINVAL)) {
 633         rc = errno;
 634         crm_perror(LOG_ERR, "synchronizing %s", filename);
 635     }
 636 
 637     fclose(stream);
 638     crm_trace("Saved %u bytes to %s as XML", bytes_out, filename);
 639 
 640     if (nbytes != NULL) {
 641         *nbytes = bytes_out;
 642     }
 643     g_string_free(buffer, TRUE);
 644     return rc;
 645 }
 646 
 647 
 648 
 649 
 650 
 651 
 652 
 653 
 654 
 655 
 656 
 657 
 658 
 659 int
 660 pcmk__xml_write_fd(const xmlNode *xml, const char *filename, int fd,
     
 661                    bool compress, unsigned int *nbytes)
 662 {
 663     
 664     FILE *stream = NULL;
 665 
 666     CRM_CHECK((xml != NULL) && (fd > 0), return EINVAL);
 667     stream = fdopen(fd, "w");
 668     if (stream == NULL) {
 669         return errno;
 670     }
 671 
 672     return write_xml_stream(xml, pcmk__s(filename, "unnamed file"), stream,
 673                             compress, nbytes);
 674 }
 675 
 676 
 677 
 678 
 679 
 680 
 681 
 682 
 683 
 684 
 685 
 686 
 687 int
 688 pcmk__xml_write_file(const xmlNode *xml, const char *filename, bool compress,
     
 689                      unsigned int *nbytes)
 690 {
 691     
 692     FILE *stream = NULL;
 693 
 694     CRM_CHECK((xml != NULL) && (filename != NULL), return EINVAL);
 695     stream = fopen(filename, "w");
 696     if (stream == NULL) {
 697         return errno;
 698     }
 699 
 700     return write_xml_stream(xml, filename, stream, compress, nbytes);
 701 }
 702 
 703 
 704 
 705 
 706 
 707 
 708 
 709 
 710 
 711 
 712 int
 713 pcmk__xml2fd(int fd, xmlNode *cur)
     
 714 {
 715     bool success;
 716 
 717     xmlOutputBuffer *fd_out = xmlOutputBufferCreateFd(fd, NULL);
 718     pcmk__mem_assert(fd_out);
 719     xmlNodeDumpOutput(fd_out, cur->doc, cur, 0, pcmk__xml_fmt_pretty, NULL);
 720 
 721     success = xmlOutputBufferWrite(fd_out, sizeof("\n") - 1, "\n") != -1;
 722 
 723     success = xmlOutputBufferClose(fd_out) != -1 && success;
 724 
 725     if (!success) {
 726         return EIO;
 727     }
 728 
 729     fsync(fd);
 730     return pcmk_rc_ok;
 731 }
 732 
 733 void
 734 save_xml_to_file(const xmlNode *xml, const char *desc, const char *filename)
     
 735 {
 736     char *f = NULL;
 737 
 738     if (filename == NULL) {
 739         char *uuid = crm_generate_uuid();
 740 
 741         f = crm_strdup_printf("%s/%s", pcmk__get_tmpdir(), uuid);
 742         filename = f;
 743         free(uuid);
 744     }
 745 
 746     crm_info("Saving %s to %s", desc, filename);
 747     pcmk__xml_write_file(xml, filename, false, NULL);
 748     free(f);
 749 }
 750 
 751 
 752 
 753 
 754 
 755 #include <crm/common/xml_io_compat.h>
 756 
 757 xmlNode *
 758 filename2xml(const char *filename)
     
 759 {
 760     return pcmk__xml_read(filename);
 761 }
 762 
 763 xmlNode *
 764 stdin2xml(void)
     
 765 {
 766     return pcmk__xml_read(NULL);
 767 }
 768 
 769 xmlNode *
 770 string2xml(const char *input)
     
 771 {
 772     return pcmk__xml_parse(input);
 773 }
 774 
 775 char *
 776 dump_xml_formatted(const xmlNode *xml)
     
 777 {
 778     char *str = NULL;
 779     GString *buffer = g_string_sized_new(1024);
 780 
 781     pcmk__xml_string(xml, pcmk__xml_fmt_pretty, buffer, 0);
 782 
 783     str = pcmk__str_copy(buffer->str);
 784     g_string_free(buffer, TRUE);
 785     return str;
 786 }
 787 
 788 char *
 789 dump_xml_formatted_with_text(const xmlNode *xml)
     
 790 {
 791     char *str = NULL;
 792     GString *buffer = g_string_sized_new(1024);
 793 
 794     pcmk__xml_string(xml, pcmk__xml_fmt_pretty|pcmk__xml_fmt_text, buffer, 0);
 795 
 796     str = pcmk__str_copy(buffer->str);
 797     g_string_free(buffer, TRUE);
 798     return str;
 799 }
 800 
 801 char *
 802 dump_xml_unformatted(const xmlNode *xml)
     
 803 {
 804     char *str = NULL;
 805     GString *buffer = g_string_sized_new(1024);
 806 
 807     pcmk__xml_string(xml, 0, buffer, 0);
 808 
 809     str = pcmk__str_copy(buffer->str);
 810     g_string_free(buffer, TRUE);
 811     return str;
 812 }
 813 
 814 int
 815 write_xml_fd(const xmlNode *xml, const char *filename, int fd,
     
 816              gboolean compress)
 817 {
 818     unsigned int nbytes = 0;
 819     int rc = pcmk__xml_write_fd(xml, filename, fd, compress, &nbytes);
 820 
 821     if (rc != pcmk_rc_ok) {
 822         return pcmk_rc2legacy(rc);
 823     }
 824     return (int) nbytes;
 825 }
 826 
 827 int
 828 write_xml_file(const xmlNode *xml, const char *filename, gboolean compress)
     
 829 {
 830     unsigned int nbytes = 0;
 831     int rc = pcmk__xml_write_file(xml, filename, compress, &nbytes);
 832 
 833     if (rc != pcmk_rc_ok) {
 834         return pcmk_rc2legacy(rc);
 835     }
 836     return (int) nbytes;
 837 }
 838 
 839 
 840