GCC Code Coverage Report
Directory: cvmfs/ Exec Total Coverage
File: cvmfs/xattr.cc Lines: 146 152 96.1 %
Date: 2019-02-03 02:48:13 Branches: 75 84 89.3 %

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 "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
}