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 "platform.h" |
15 |
|
|
#include "smalloc.h" |
16 |
|
|
#include "util/pointer.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 |
|
16 |
XattrList *XattrList::CreateFromFile(const std::string &path) { |
31 |
|
|
// Parse the \0 separated list of extended attribute keys |
32 |
|
|
char *list; |
33 |
|
16 |
ssize_t sz_list = platform_llistxattr(path.c_str(), NULL, 0); |
34 |
✓✓✗✓
|
16 |
if ((sz_list < 0) || (sz_list > 64*1024)) { |
35 |
|
4 |
return NULL; |
36 |
✗✓ |
12 |
} else if (sz_list == 0) { |
37 |
|
|
// No extended attributes |
38 |
|
|
return new XattrList(); |
39 |
|
|
} |
40 |
|
12 |
list = reinterpret_cast<char *>(alloca(sz_list)); |
41 |
|
12 |
sz_list = platform_llistxattr(path.c_str(), list, sz_list); |
42 |
✗✓ |
12 |
if (sz_list < 0) { |
43 |
|
|
return NULL; |
44 |
✗✓ |
12 |
} 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 |
|
12 |
vector<string> keys = SplitString(string(list, sz_list), '\0'); |
50 |
|
|
|
51 |
|
|
// Retrieve extended attribute values |
52 |
|
12 |
XattrList *result = new XattrList(); |
53 |
|
|
char value[256]; |
54 |
✓✓ |
56 |
for (unsigned i = 0; i < keys.size(); ++i) { |
55 |
✓✓ |
44 |
if (keys[i].empty()) |
56 |
|
12 |
continue; |
57 |
|
|
ssize_t sz_value = |
58 |
|
32 |
platform_lgetxattr(path.c_str(), keys[i].c_str(), value, 256); |
59 |
✓✓ |
32 |
if (sz_value < 0) |
60 |
|
4 |
continue; |
61 |
|
28 |
result->Set(keys[i], string(value, sz_value)); |
62 |
|
|
} |
63 |
|
12 |
return result; |
64 |
|
|
} |
65 |
|
|
|
66 |
|
|
|
67 |
|
40 |
XattrList *XattrList::Deserialize( |
68 |
|
|
const unsigned char *inbuf, |
69 |
|
|
const unsigned size) |
70 |
|
|
{ |
71 |
✓✓ |
40 |
if (inbuf == NULL) |
72 |
|
4 |
return new XattrList(); |
73 |
|
|
|
74 |
|
36 |
UniquePtr<XattrList> result(new XattrList()); |
75 |
✓✓ |
36 |
if (size < sizeof(XattrHeader)) |
76 |
|
4 |
return NULL; |
77 |
|
32 |
XattrHeader header; |
78 |
|
32 |
memcpy(&header, inbuf, sizeof(header)); |
79 |
✓✓ |
32 |
if (header.version != kVersion) |
80 |
|
4 |
return NULL; |
81 |
|
28 |
unsigned pos = sizeof(header); |
82 |
✓✓ |
56 |
for (unsigned i = 0; i < header.num_xattrs; ++i) { |
83 |
|
44 |
XattrEntry entry; |
84 |
|
44 |
unsigned size_preamble = sizeof(entry.len_key) + sizeof(entry.len_value); |
85 |
✓✓ |
44 |
if (size - pos < size_preamble) |
86 |
|
4 |
return NULL; |
87 |
|
40 |
memcpy(&entry, inbuf + pos, size_preamble); |
88 |
✓✓ |
40 |
if (size - pos < entry.GetSize()) |
89 |
|
4 |
return NULL; |
90 |
✓✓ |
36 |
if (entry.GetSize() == size_preamble) |
91 |
|
8 |
return NULL; |
92 |
|
28 |
pos += size_preamble; |
93 |
|
28 |
memcpy(entry.data, inbuf + pos, entry.GetSize() - size_preamble); |
94 |
|
28 |
pos += entry.GetSize() - size_preamble; |
95 |
|
28 |
bool retval = result->Set(entry.GetKey(), entry.GetValue()); |
96 |
✗✓ |
28 |
if (!retval) |
97 |
|
|
return NULL; |
98 |
|
|
} |
99 |
|
12 |
return result.Release(); |
100 |
|
|
} |
101 |
|
|
|
102 |
|
|
|
103 |
|
48 |
bool XattrList::Has(const string &key) const { |
104 |
|
48 |
return xattrs_.find(key) != xattrs_.end(); |
105 |
|
|
} |
106 |
|
|
|
107 |
|
|
|
108 |
|
1084 |
bool XattrList::Get(const string &key, string *value) const { |
109 |
✗✓ |
1084 |
assert(value); |
110 |
|
1084 |
map<string, string>::const_iterator iter = xattrs_.find(key); |
111 |
✓✓ |
1084 |
if (iter != xattrs_.end()) { |
112 |
|
1076 |
*value = iter->second; |
113 |
|
1076 |
return true; |
114 |
|
|
} |
115 |
|
8 |
return false; |
116 |
|
|
} |
117 |
|
|
|
118 |
|
|
|
119 |
|
52 |
vector<string> XattrList::ListKeys() const { |
120 |
|
52 |
vector<string> result; |
121 |
✓✓ |
1232 |
for (map<string, string>::const_iterator i = xattrs_.begin(), |
122 |
|
52 |
iEnd = xattrs_.end(); i != iEnd; ++i) |
123 |
|
|
{ |
124 |
|
1128 |
result.push_back(i->first); |
125 |
|
|
} |
126 |
|
52 |
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 |
|
16 |
string XattrList::ListKeysPosix(const string &merge_with) const { |
138 |
|
16 |
string result; |
139 |
✓✓ |
16 |
if (!merge_with.empty()) { |
140 |
|
8 |
vector<string> merge_list = SplitString(merge_with, '\0'); |
141 |
✓✓ |
40 |
for (unsigned i = 0; i < merge_list.size(); ++i) { |
142 |
✓✓ |
32 |
if (merge_list[i].empty()) |
143 |
|
8 |
continue; |
144 |
✓✓ |
24 |
if (xattrs_.find(merge_list[i]) == xattrs_.end()) { |
145 |
|
20 |
result += merge_list[i]; |
146 |
|
20 |
result.push_back('\0'); |
147 |
|
|
} |
148 |
|
|
} |
149 |
|
|
} |
150 |
✓✓ |
56 |
for (map<string, string>::const_iterator i = xattrs_.begin(), |
151 |
|
16 |
iEnd = xattrs_.end(); i != iEnd; ++i) |
152 |
|
|
{ |
153 |
|
24 |
result += i->first; |
154 |
|
24 |
result.push_back('\0'); |
155 |
|
|
} |
156 |
|
16 |
return result; |
157 |
|
|
} |
158 |
|
|
|
159 |
|
|
|
160 |
|
2296 |
bool XattrList::Set(const string &key, const string &value) { |
161 |
✗✓ |
2296 |
if (key.empty()) |
162 |
|
|
return false; |
163 |
✓✓ |
2296 |
if (key.length() > 256) |
164 |
|
4 |
return false; |
165 |
✓✓ |
2292 |
if (key.find('\0') != string::npos) |
166 |
|
4 |
return false; |
167 |
✓✓ |
2288 |
if (value.length() > 256) |
168 |
|
4 |
return false; |
169 |
|
|
|
170 |
|
2284 |
map<string, string>::iterator iter = xattrs_.find(key); |
171 |
✓✓ |
2284 |
if (iter != xattrs_.end()) { |
172 |
|
4 |
iter->second = value; |
173 |
|
|
} else { |
174 |
✓✓ |
2280 |
if (xattrs_.size() >= 256) |
175 |
|
4 |
return false; |
176 |
|
2276 |
xattrs_[key] = value; |
177 |
|
|
} |
178 |
|
2280 |
return true; |
179 |
|
|
} |
180 |
|
|
|
181 |
|
|
|
182 |
|
20 |
bool XattrList::Remove(const string &key) { |
183 |
|
20 |
map<string, string>::iterator iter = xattrs_.find(key); |
184 |
✓✓ |
20 |
if (iter != xattrs_.end()) { |
185 |
|
16 |
xattrs_.erase(iter); |
186 |
|
16 |
return true; |
187 |
|
|
} |
188 |
|
4 |
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 |
|
20 |
void XattrList::Serialize( |
197 |
|
|
unsigned char **outbuf, |
198 |
|
|
unsigned *size, |
199 |
|
|
const std::vector<std::string> *blacklist) const |
200 |
|
|
{ |
201 |
✓✓ |
20 |
if (xattrs_.empty()) { |
202 |
|
4 |
*size = 0; |
203 |
|
4 |
*outbuf = NULL; |
204 |
|
4 |
return; |
205 |
|
|
} |
206 |
|
|
|
207 |
|
16 |
XattrHeader header(xattrs_.size()); |
208 |
|
16 |
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 |
|
16 |
smalloc(header.num_xattrs * sizeof(XattrEntry))); |
213 |
|
16 |
unsigned ientries = 0; |
214 |
✓✓ |
80 |
for (map<string, string>::const_iterator it_att = xattrs_.begin(), |
215 |
|
16 |
it_att_end = xattrs_.end(); it_att != it_att_end; ++it_att) |
216 |
|
|
{ |
217 |
|
|
// Only serialize non-blacklist items |
218 |
✓✓ |
48 |
if (blacklist != NULL) { |
219 |
|
24 |
bool skip = false; |
220 |
✓✓ |
48 |
for (unsigned i_bl = 0; i_bl < blacklist->size(); ++i_bl) { |
221 |
✓✓ |
44 |
if (HasPrefix(it_att->first, (*blacklist)[i_bl], |
222 |
|
|
true /* ignore_case */)) |
223 |
|
|
{ |
224 |
|
20 |
skip = true; |
225 |
|
20 |
break; |
226 |
|
|
} |
227 |
|
|
} |
228 |
✓✓ |
24 |
if (skip) continue; |
229 |
|
|
} |
230 |
|
|
/*entries[ientries] =*/ |
231 |
✓✗ |
28 |
new (entries + ientries) XattrEntry(it_att->first, it_att->second); |
232 |
|
28 |
packed_size += entries[ientries].GetSize(); |
233 |
|
28 |
ientries++; |
234 |
|
|
} |
235 |
|
|
|
236 |
|
|
// We might have skipped all attributes |
237 |
✓✓ |
16 |
if (ientries == 0) { |
238 |
|
4 |
free(entries); |
239 |
|
4 |
*size = 0; |
240 |
|
4 |
*outbuf = NULL; |
241 |
|
4 |
return; |
242 |
|
|
} |
243 |
|
|
|
244 |
|
|
// Copy data into buffer |
245 |
|
12 |
header.num_xattrs = ientries; |
246 |
|
12 |
*size = packed_size; |
247 |
|
12 |
*outbuf = reinterpret_cast<unsigned char *>(smalloc(packed_size)); |
248 |
|
12 |
memcpy(*outbuf, &header, sizeof(header)); |
249 |
|
12 |
unsigned pos = sizeof(header); |
250 |
✓✓ |
40 |
for (unsigned i = 0; i < header.num_xattrs; ++i) { |
251 |
|
28 |
memcpy(*outbuf + pos, &entries[i], entries[i].GetSize()); |
252 |
|
28 |
pos += entries[i].GetSize(); |
253 |
|
|
} |
254 |
|
|
|
255 |
|
12 |
free(entries); |
256 |
|
|
} |
257 |
|
|
|
258 |
|
|
|
259 |
|
|
//------------------------------------------------------------------------------ |
260 |
|
|
|
261 |
|
|
|
262 |
|
28 |
string XattrList::XattrEntry::GetKey() const { |
263 |
✗✓ |
28 |
if (len_key == 0) |
264 |
|
|
return ""; |
265 |
|
28 |
return string(data, len_key); |
266 |
|
|
} |
267 |
|
|
|
268 |
|
|
|
269 |
|
216 |
uint16_t XattrList::XattrEntry::GetSize() const { |
270 |
|
|
return sizeof(len_key) + sizeof(len_value) + |
271 |
|
216 |
uint16_t(len_key) + uint16_t(len_value); |
272 |
|
|
} |
273 |
|
|
|
274 |
|
|
|
275 |
|
28 |
string XattrList::XattrEntry::GetValue() const { |
276 |
✓✓ |
28 |
if (len_value == 0) |
277 |
|
8 |
return ""; |
278 |
|
20 |
return string(&data[len_key], len_value); |
279 |
|
|
} |
280 |
|
|
|
281 |
|
|
|
282 |
|
28 |
XattrList::XattrEntry::XattrEntry(const string &key, const string &value) |
283 |
|
|
: len_key(key.size()) |
284 |
|
28 |
, len_value(value.size()) |
285 |
|
|
{ |
286 |
|
28 |
memcpy(data, key.data(), len_key); |
287 |
|
28 |
memcpy(data+len_key, value.data(), len_value); |
288 |
|
28 |
} |