GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/xattr.cc
Date: 2024-04-21 02:33:16
Exec Total Coverage
Lines: 150 156 96.2%
Branches: 105 151 69.5%

Line Branch Exec Source
1 /**
2 * This file is part of the CernVM File System.
3 */
4
5 #include "cvmfs_config.h"
6 #include "xattr.h"
7
8 #include <alloca.h>
9 #include <sys/xattr.h>
10
11 #include <cassert>
12 #include <cstring>
13
14 #include "util/platform.h"
15 #include "util/pointer.h"
16 #include "util/smalloc.h"
17 #include "util/string.h"
18
19 using namespace std; // NOLINT
20
21 const uint8_t XattrList::kVersion = 1;
22
23 /**
24 * Converts all the extended attributes of path into a XattrList. Attributes
25 * that violate the XattrList restrictions are ignored. If path does not exist
26 * or on I/O errors, NULL is returned. The list of extended attributes is not
27 * supposed to change during the runtime of this method. The list of values
28 * must not exceed 64kB.
29 */
30 4 XattrList *XattrList::CreateFromFile(const std::string &path) {
31 // Parse the \0 separated list of extended attribute keys
32 char *list;
33 4 ssize_t sz_list = platform_llistxattr(path.c_str(), NULL, 0);
34
3/4
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
4 if ((sz_list < 0) || (sz_list > 64*1024)) {
35 1 return NULL;
36
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 } else if (sz_list == 0) {
37 // No extended attributes
38 return new XattrList();
39 }
40 3 list = reinterpret_cast<char *>(alloca(sz_list));
41 3 sz_list = platform_llistxattr(path.c_str(), list, sz_list);
42
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (sz_list < 0) {
43 return NULL;
44
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 } else if (sz_list == 0) {
45 // Can only happen if the list was removed since the previous call to
46 // llistxattr
47 return new XattrList();
48 }
49
2/4
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 3 times.
✗ Branch 6 not taken.
6 vector<string> keys = SplitString(string(list, sz_list), '\0');
50
51 // Retrieve extended attribute values
52
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 XattrList *result = new XattrList();
53 char value[256];
54
2/2
✓ Branch 1 taken 14 times.
✓ Branch 2 taken 3 times.
17 for (unsigned i = 0; i < keys.size(); ++i) {
55
2/2
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 11 times.
14 if (keys[i].empty())
56 3 continue;
57 ssize_t sz_value =
58 11 platform_lgetxattr(path.c_str(), keys[i].c_str(), value, 256);
59
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 10 times.
11 if (sz_value < 0)
60 1 continue;
61
2/4
✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 10 times.
✗ Branch 7 not taken.
10 result->Set(keys[i], string(value, sz_value));
62 }
63 3 return result;
64 3 }
65
66
67 10 XattrList *XattrList::Deserialize(
68 const unsigned char *inbuf,
69 const unsigned size)
70 {
71
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 9 times.
10 if (inbuf == NULL)
72
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 return new XattrList();
73
74
2/4
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 9 times.
✗ Branch 6 not taken.
9 UniquePtr<XattrList> result(new XattrList());
75
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 8 times.
9 if (size < sizeof(XattrHeader))
76 1 return NULL;
77 8 XattrHeader header;
78 8 memcpy(&header, inbuf, sizeof(header));
79
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 7 times.
8 if (header.version != kVersion)
80 1 return NULL;
81 7 unsigned pos = sizeof(header);
82
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 3 times.
14 for (unsigned i = 0; i < header.num_xattrs; ++i) {
83 11 XattrEntry entry;
84 11 unsigned size_preamble = sizeof(entry.len_key) + sizeof(entry.len_value);
85
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 10 times.
11 if (size - pos < size_preamble)
86 4 return NULL;
87 10 memcpy(&entry, inbuf + pos, size_preamble);
88
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 9 times.
10 if (size - pos < entry.GetSize())
89 1 return NULL;
90
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 7 times.
9 if (entry.GetSize() == size_preamble)
91 2 return NULL;
92 7 pos += size_preamble;
93 7 memcpy(entry.data, inbuf + pos, entry.GetSize() - size_preamble);
94 7 pos += entry.GetSize() - size_preamble;
95
3/6
✓ Branch 2 taken 7 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 7 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 7 times.
✗ Branch 9 not taken.
7 bool retval = result->Set(entry.GetKey(), entry.GetValue());
96
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
7 if (!retval)
97 return NULL;
98 }
99 3 return result.Release();
100 9 }
101
102
103 12 bool XattrList::Has(const string &key) const {
104
1/2
✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
12 return xattrs_.find(key) != xattrs_.end();
105 }
106
107
108 271 bool XattrList::Get(const string &key, string *value) const {
109
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 271 times.
271 assert(value);
110
1/2
✓ Branch 1 taken 271 times.
✗ Branch 2 not taken.
271 map<string, string>::const_iterator iter = xattrs_.find(key);
111
2/2
✓ Branch 2 taken 269 times.
✓ Branch 3 taken 2 times.
271 if (iter != xattrs_.end()) {
112
1/2
✓ Branch 2 taken 269 times.
✗ Branch 3 not taken.
269 *value = iter->second;
113 269 return true;
114 }
115 2 return false;
116 }
117
118
119 13 vector<string> XattrList::ListKeys() const {
120 13 vector<string> result;
121 26 for (map<string, string>::const_iterator i = xattrs_.begin(),
122
2/2
✓ Branch 3 taken 285 times.
✓ Branch 4 taken 13 times.
311 iEnd = xattrs_.end(); i != iEnd; ++i)
123 {
124
1/2
✓ Branch 2 taken 285 times.
✗ Branch 3 not taken.
285 result.push_back(i->first);
125 }
126 13 return result;
127 }
128
129
130 /**
131 * The format of extended attribute lists in the (l)listxattr call is an array
132 * of all the keys concatenated and separated by null characters. If merge_with
133 * is not empty, the final list will be have the keys from the XattrList and the
134 * keys from merge_with without duplicates. The merge_with list is supposed to
135 * be in POSIX format.
136 */
137 4 string XattrList::ListKeysPosix(const string &merge_with) const {
138 4 string result;
139
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 2 times.
4 if (!merge_with.empty()) {
140
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 vector<string> merge_list = SplitString(merge_with, '\0');
141
2/2
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 2 times.
10 for (unsigned i = 0; i < merge_list.size(); ++i) {
142
2/2
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 6 times.
8 if (merge_list[i].empty())
143 2 continue;
144
3/5
✓ Branch 3 taken 6 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 5 times.
✓ Branch 7 taken 1 times.
6 if (xattrs_.find(merge_list[i]) == xattrs_.end()) {
145
1/2
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
5 result += merge_list[i];
146
1/2
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
5 result.push_back('\0');
147 }
148 }
149 2 }
150 8 for (map<string, string>::const_iterator i = xattrs_.begin(),
151
2/2
✓ Branch 3 taken 6 times.
✓ Branch 4 taken 4 times.
14 iEnd = xattrs_.end(); i != iEnd; ++i)
152 {
153
1/2
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
6 result += i->first;
154
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 result.push_back('\0');
155 }
156 4 return result;
157 }
158
159
160 577 bool XattrList::Set(const string &key, const string &value) {
161
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 577 times.
577 if (key.empty())
162 return false;
163
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 576 times.
577 if (key.length() > 256)
164 1 return false;
165
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 575 times.
576 if (key.find('\0') != string::npos)
166 1 return false;
167
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 574 times.
575 if (value.length() > 256)
168 1 return false;
169
170
1/2
✓ Branch 1 taken 574 times.
✗ Branch 2 not taken.
574 map<string, string>::iterator iter = xattrs_.find(key);
171
2/2
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 573 times.
574 if (iter != xattrs_.end()) {
172
1/2
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 iter->second = value;
173 } else {
174
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 572 times.
573 if (xattrs_.size() >= 256)
175 1 return false;
176
2/4
✓ Branch 1 taken 572 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 572 times.
✗ Branch 5 not taken.
572 xattrs_[key] = value;
177 }
178 573 return true;
179 }
180
181
182 5 bool XattrList::Remove(const string &key) {
183
1/2
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
5 map<string, string>::iterator iter = xattrs_.find(key);
184
2/2
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 1 times.
5 if (iter != xattrs_.end()) {
185
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 xattrs_.erase(iter);
186 4 return true;
187 }
188 1 return false;
189 }
190
191
192 /**
193 * If the list of attributes is empty, Serialize returns NULL. Deserialize
194 * can deal with NULL pointers.
195 */
196 5 void XattrList::Serialize(
197 unsigned char **outbuf,
198 unsigned *size,
199 const std::vector<std::string> *blacklist) const
200 {
201
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 4 times.
5 if (xattrs_.empty()) {
202 1 *size = 0;
203 1 *outbuf = NULL;
204 2 return;
205 }
206
207 4 XattrHeader header(xattrs_.size());
208 4 uint32_t packed_size = sizeof(header);
209
210 // Determine size of the buffer (allocate space for max num of attributes)
211 XattrEntry *entries = reinterpret_cast<XattrEntry *>(
212 4 smalloc(header.num_xattrs * sizeof(XattrEntry)));
213 4 unsigned ientries = 0;
214 8 for (map<string, string>::const_iterator it_att = xattrs_.begin(),
215
2/2
✓ Branch 3 taken 12 times.
✓ Branch 4 taken 4 times.
20 it_att_end = xattrs_.end(); it_att != it_att_end; ++it_att)
216 {
217 // Only serialize non-blacklist items
218
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6 times.
12 if (blacklist != NULL) {
219 6 bool skip = false;
220
2/2
✓ Branch 1 taken 11 times.
✓ Branch 2 taken 1 times.
12 for (unsigned i_bl = 0; i_bl < blacklist->size(); ++i_bl) {
221
3/4
✓ Branch 3 taken 11 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 5 times.
✓ Branch 6 taken 6 times.
11 if (HasPrefix(it_att->first, (*blacklist)[i_bl],
222 true /* ignore_case */))
223 {
224 5 skip = true;
225 5 break;
226 }
227 }
228
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 1 times.
6 if (skip) continue;
229 }
230 /*entries[ientries] =*/
231 7 new (entries + ientries) XattrEntry(it_att->first, it_att->second);
232 7 packed_size += entries[ientries].GetSize();
233 7 ientries++;
234 }
235
236 // We might have skipped all attributes
237
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 3 times.
4 if (ientries == 0) {
238 1 free(entries);
239 1 *size = 0;
240 1 *outbuf = NULL;
241 1 return;
242 }
243
244 // Copy data into buffer
245 3 header.num_xattrs = ientries;
246 3 *size = packed_size;
247 3 *outbuf = reinterpret_cast<unsigned char *>(smalloc(packed_size));
248 3 memcpy(*outbuf, &header, sizeof(header));
249 3 unsigned pos = sizeof(header);
250
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 3 times.
10 for (unsigned i = 0; i < header.num_xattrs; ++i) {
251 7 memcpy(*outbuf + pos, &entries[i], entries[i].GetSize());
252 7 pos += entries[i].GetSize();
253 }
254
255 3 free(entries);
256 }
257
258
259 //------------------------------------------------------------------------------
260
261
262 7 string XattrList::XattrEntry::GetKey() const {
263
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
7 if (len_key == 0)
264 return "";
265
1/2
✓ Branch 2 taken 7 times.
✗ Branch 3 not taken.
7 return string(data, len_key);
266 }
267
268
269 54 uint16_t XattrList::XattrEntry::GetSize() const {
270 return sizeof(len_key) + sizeof(len_value) +
271 54 uint16_t(len_key) + uint16_t(len_value);
272 }
273
274
275 7 string XattrList::XattrEntry::GetValue() const {
276
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 5 times.
7 if (len_value == 0)
277
1/2
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 return "";
278
1/2
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
5 return string(&data[len_key], len_value);
279 }
280
281
282 7 XattrList::XattrEntry::XattrEntry(const string &key, const string &value)
283 7 : len_key(key.size())
284 7 , len_value(value.size())
285 {
286 7 memcpy(data, key.data(), len_key);
287 7 memcpy(data+len_key, value.data(), len_value);
288 7 }
289