Directory: | cvmfs/ |
---|---|
File: | cvmfs/xattr.cc |
Date: | 2025-02-09 02:34:19 |
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 | |||
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 11 times.
✓ Branch 2 taken 3 times.
|
14 | for (unsigned i = 0; i < keys.size(); ++i) { |
55 |
2/2✓ Branch 2 taken 3 times.
✓ Branch 3 taken 8 times.
|
11 | if (keys[i].empty()) |
56 | 3 | continue; | |
57 | ssize_t sz_value = | ||
58 | 8 | platform_lgetxattr(path.c_str(), keys[i].c_str(), value, 256); | |
59 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 7 times.
|
8 | if (sz_value < 0) |
60 | 1 | continue; | |
61 |
2/4✓ Branch 2 taken 7 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 7 times.
✗ Branch 7 not taken.
|
7 | 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 282 times.
✓ Branch 4 taken 13 times.
|
308 | iEnd = xattrs_.end(); i != iEnd; ++i) |
123 | { | ||
124 |
1/2✓ Branch 2 taken 282 times.
✗ Branch 3 not taken.
|
282 | 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 | 574 | bool XattrList::Set(const string &key, const string &value) { | |
161 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 574 times.
|
574 | if (key.empty()) |
162 | ✗ | return false; | |
163 |
2/2✓ Branch 1 taken 1 times.
✓ Branch 2 taken 573 times.
|
574 | if (key.length() > 256) |
164 | 1 | return false; | |
165 |
2/2✓ Branch 1 taken 1 times.
✓ Branch 2 taken 572 times.
|
573 | if (key.find('\0') != string::npos) |
166 | 1 | return false; | |
167 |
2/2✓ Branch 1 taken 1 times.
✓ Branch 2 taken 571 times.
|
572 | if (value.length() > 256) |
168 | 1 | return false; | |
169 | |||
170 |
1/2✓ Branch 1 taken 571 times.
✗ Branch 2 not taken.
|
571 | map<string, string>::iterator iter = xattrs_.find(key); |
171 |
2/2✓ Branch 2 taken 1 times.
✓ Branch 3 taken 570 times.
|
571 | 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 569 times.
|
570 | if (xattrs_.size() >= 256) |
175 | 1 | return false; | |
176 |
2/4✓ Branch 1 taken 569 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 569 times.
✗ Branch 5 not taken.
|
569 | xattrs_[key] = value; |
177 | } | ||
178 | 570 | 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 |