Coverage Report

Created: 2026-04-10 06:58

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/pacemaker/lib/common/xml_attr.c
Line
Count
Source
1
/*
2
 * Copyright 2004-2025 the Pacemaker project contributors
3
 *
4
 * The version control history for this file may have further details.
5
 *
6
 * This source code is licensed under the GNU Lesser General Public License
7
 * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
8
 */
9
10
#include <crm_internal.h>
11
12
#include <stdbool.h>
13
#include <stdio.h>
14
#include <sys/types.h>
15
#include <unistd.h>
16
#include <time.h>
17
#include <string.h>
18
#include <stdlib.h>
19
#include <stdarg.h>
20
#include <bzlib.h>
21
22
#include <libxml/parser.h>
23
#include <libxml/tree.h>
24
#include <libxml/xmlIO.h>  /* xmlAllocOutputBuffer */
25
26
#include <crm/crm.h>
27
#include <crm/common/xml.h>
28
#include "crmcommon_private.h"
29
30
/*!
31
 * \internal
32
 * \brief Remove an XML attribute from its parent and free it
33
 *
34
 * \param[in,out] attr   XML attribute to remove
35
 * \param[in]     force  If \c true, remove the attribute immediately, ignoring
36
 *                       ACLs and change tracking
37
 *
38
 * \return Standard Pacemaker return code (\c EPERM if ACLs prevent removal, or
39
 *         or \c pcmk_rc_ok otherwise)
40
 *
41
 * \note If the attribute has no parent element, this function does not free it.
42
 *       This mimics \c xmlRemoveProp().
43
 */
44
int
45
pcmk__xa_remove(xmlAttr *attr, bool force)
46
0
{
47
0
    xmlNode *element = NULL;
48
49
0
    if ((attr == NULL) || (attr->parent == NULL)) {
50
0
        return pcmk_rc_ok;
51
0
    }
52
53
0
    element = attr->parent;
54
55
0
    if (!force && !pcmk__check_acl(element, NULL, pcmk__xf_acl_write)) {
56
        // ACLs apply to element, not to particular attributes
57
0
        pcmk__trace("ACLs prevent removal of attributes from %s element",
58
0
                    element->name);
59
0
        return EPERM;
60
0
    }
61
62
0
    if (!force && (element != NULL)
63
0
        && pcmk__xml_doc_all_flags_set(element->doc, pcmk__xf_tracking)) {
64
65
        // Leave in place (marked for removal) until after diff is calculated
66
0
        pcmk__xml_set_parent_flags(element, pcmk__xf_dirty);
67
0
        pcmk__set_xml_flags((xml_node_private_t *) attr->_private,
68
0
                            pcmk__xf_deleted);
69
0
    } else {
70
0
        pcmk__xml_free_private_data((xmlNode *) attr);
71
0
        xmlRemoveProp(attr);
72
0
    }
73
0
    return pcmk_rc_ok;
74
0
}
75
76
void
77
pcmk__mark_xml_attr_dirty(xmlAttr *a) 
78
0
{
79
0
    xmlNode *parent = a->parent;
80
0
    xml_node_private_t *nodepriv = a->_private;
81
82
0
    pcmk__set_xml_flags(nodepriv, pcmk__xf_dirty|pcmk__xf_modified);
83
0
    pcmk__clear_xml_flags(nodepriv, pcmk__xf_deleted);
84
0
    pcmk__mark_xml_node_dirty(parent);
85
0
}
86
87
// This also clears attribute's flags if not marked as deleted
88
bool
89
pcmk__marked_as_deleted(xmlAttrPtr a, void *user_data)
90
0
{
91
0
    xml_node_private_t *nodepriv = a->_private;
92
93
0
    if (pcmk__is_set(nodepriv->flags, pcmk__xf_deleted)) {
94
0
        return true;
95
0
    }
96
0
    nodepriv->flags = pcmk__xf_none;
97
0
    return false;
98
0
}
99
100
/*!
101
 * \internal
102
 * \brief Append an XML attribute to a buffer
103
 *
104
 * \param[in]     attr     Attribute to append
105
 * \param[in,out] buffer   Where to append the content (must not be \p NULL)
106
 */
107
void
108
pcmk__dump_xml_attr(const xmlAttr *attr, GString *buffer)
109
0
{
110
0
    const char *name = NULL;
111
0
    const char *value = NULL;
112
0
    gchar *value_esc = NULL;
113
0
    xml_node_private_t *nodepriv = NULL;
114
115
0
    if (attr == NULL || attr->children == NULL) {
116
0
        return;
117
0
    }
118
119
0
    nodepriv = attr->_private;
120
0
    if ((nodepriv != NULL) && pcmk__is_set(nodepriv->flags, pcmk__xf_deleted)) {
121
0
        return;
122
0
    }
123
124
0
    name = (const char *) attr->name;
125
0
    value = (const char *) attr->children->content;
126
0
    if (value == NULL) {
127
        /* Don't print anything for unset attribute. Any null-indicator value,
128
         * including the empty string, could also be a real value that needs to
129
         * be treated differently from "unset".
130
         */
131
0
        return;
132
0
    }
133
134
0
    if (pcmk__xml_needs_escape(value, pcmk__xml_escape_attr)) {
135
0
        value_esc = pcmk__xml_escape(value, pcmk__xml_escape_attr);
136
0
        value = value_esc;
137
0
    }
138
139
    pcmk__g_strcat(buffer, " ", name, "=\"", value, "\"", NULL);
140
0
    g_free(value_esc);
141
0
}