Fix some bugs when testing opensds ansible
[stor4nfv.git] / src / ceph / src / os / filestore / chain_xattr.cc
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3
4 #include "chain_xattr.h"
5 #include <errno.h>           // for ERANGE, ENODATA, ENOMEM
6 #include <stdio.h>           // for size_t, snprintf
7 #include <stdlib.h>          // for free, malloc
8 #include <string.h>          // for strcpy, strlen
9 #include "include/assert.h"  // for assert
10 #include "include/buffer.h"
11
12 #if defined(__linux__)
13 #include <linux/fs.h>
14 #endif
15
16 /*
17  * chaining xattrs
18  *
19  * In order to support xattrs that are larger than the xattr size limit that some file systems
20  * impose, we use multiple xattrs to store the value of a single xattr. The xattrs keys
21  * are set as follows:
22  * The first xattr in the chain, has a key that holds the original xattr name, with any '@' char
23  * being esacped ("@@").
24  * The chained keys will have the first xattr's key (with the escaping), and a suffix: "@<id>"
25  * where <id> marks the num of xattr in the chain.
26  */
27
28 void get_raw_xattr_name(const char *name, int i, char *raw_name, int raw_len)
29 {
30   int pos = 0;
31
32   while (*name) {
33     switch (*name) {
34     case '@': /* escape it */
35       pos += 2;
36       assert (pos < raw_len - 1);
37       *raw_name = '@';
38       raw_name++;
39       *raw_name = '@';
40       break;
41     default:
42       pos++;
43       assert(pos < raw_len - 1);
44       *raw_name = *name;
45       break;
46     }
47     name++;
48     raw_name++;
49   }
50
51   if (!i) {
52     *raw_name = '\0';
53   } else {
54     int r = snprintf(raw_name, raw_len - pos, "@%d", i);
55     assert(r < raw_len - pos);
56   }
57 }
58
59 static int translate_raw_name(const char *raw_name, char *name, int name_len, bool *is_first)
60 {
61   int pos = 0;
62
63   *is_first = true;
64   while (*raw_name) {
65     switch (*raw_name) {
66     case '@': /* escape it */
67       raw_name++;
68       if (!*raw_name)
69         break;
70       if (*raw_name != '@') {
71         *is_first = false;
72         goto done;
73       }
74
75     /* fall through */
76     default:
77       *name = *raw_name;
78       break;
79     }
80     pos++;
81     assert(pos < name_len);
82     name++;
83     raw_name++;
84   }
85 done:
86   *name = '\0';
87   return pos;
88 }
89
90
91 // setxattr
92
93 static int getxattr_len(const char *fn, const char *name)
94 {
95   int i = 0, total = 0;
96   char raw_name[CHAIN_XATTR_MAX_NAME_LEN * 2 + 16];
97   int r;
98
99   do {
100     get_raw_xattr_name(name, i, raw_name, sizeof(raw_name));
101     r = sys_getxattr(fn, raw_name, 0, 0);
102     if (!i && r < 0)
103       return r;
104     if (r < 0)
105       break;
106     total += r;
107     i++;
108   } while (r == CHAIN_XATTR_MAX_BLOCK_LEN ||
109            r == CHAIN_XATTR_SHORT_BLOCK_LEN);
110
111   return total;
112 }
113
114 int chain_getxattr(const char *fn, const char *name, void *val, size_t size)
115 {
116   int i = 0, pos = 0;
117   char raw_name[CHAIN_XATTR_MAX_NAME_LEN * 2 + 16];
118   int ret = 0;
119   int r;
120   size_t chunk_size;
121
122   if (!size)
123     return getxattr_len(fn, name);
124
125   do {
126     chunk_size = size;
127     get_raw_xattr_name(name, i, raw_name, sizeof(raw_name));
128
129     r = sys_getxattr(fn, raw_name, (char *)val + pos, chunk_size);
130     if (i && r == -ENODATA) {
131       ret = pos;
132       break;
133     }
134     if (r < 0) {
135       ret = r;
136       break;
137     }
138
139     if (r > 0) {
140       pos += r;
141       size -= r;
142     }
143
144     i++;
145   } while (size && (r == CHAIN_XATTR_MAX_BLOCK_LEN ||
146                     r == CHAIN_XATTR_SHORT_BLOCK_LEN));
147
148   if (r >= 0) {
149     ret = pos;
150     /* is there another chunk? that can happen if the last read size span over
151        exactly one block */
152     if (chunk_size == CHAIN_XATTR_MAX_BLOCK_LEN ||
153         chunk_size == CHAIN_XATTR_SHORT_BLOCK_LEN) {
154       get_raw_xattr_name(name, i, raw_name, sizeof(raw_name));
155       r = sys_getxattr(fn, raw_name, 0, 0);
156       if (r > 0) { // there's another chunk.. the original buffer was too small
157         ret = -ERANGE;
158       }
159     }
160   }
161   return ret;
162 }
163
164 int chain_getxattr_buf(const char *fn, const char *name, bufferptr *bp)
165 {
166   size_t size = 1024; // Initial
167   while (1) {
168     bufferptr buf(size);
169     int r = chain_getxattr(
170       fn,
171       name,
172       buf.c_str(),
173       size);
174     if (r > 0) {
175       buf.set_length(r);
176       if (bp)
177         bp->swap(buf);
178       return r;
179     } else if (r == 0) {
180       return 0;
181     } else {
182       if (r == -ERANGE) {
183         size *= 2;
184       } else {
185         return r;
186       }
187     }
188   }
189   assert(0 == "unreachable");
190   return 0;
191 }
192
193 static int chain_fgetxattr_len(int fd, const char *name)
194 {
195   int i = 0, total = 0;
196   char raw_name[CHAIN_XATTR_MAX_NAME_LEN * 2 + 16];
197   int r;
198
199   do {
200     get_raw_xattr_name(name, i, raw_name, sizeof(raw_name));
201     r = sys_fgetxattr(fd, raw_name, 0, 0);
202     if (!i && r < 0)
203       return r;
204     if (r < 0)
205       break;
206     total += r;
207     i++;
208   } while (r == CHAIN_XATTR_MAX_BLOCK_LEN ||
209            r == CHAIN_XATTR_SHORT_BLOCK_LEN);
210
211   return total;
212 }
213
214 int chain_fgetxattr(int fd, const char *name, void *val, size_t size)
215 {
216   int i = 0, pos = 0;
217   char raw_name[CHAIN_XATTR_MAX_NAME_LEN * 2 + 16];
218   int ret = 0;
219   int r;
220   size_t chunk_size;
221
222   if (!size)
223     return chain_fgetxattr_len(fd, name);
224
225   do {
226     chunk_size = size;
227     get_raw_xattr_name(name, i, raw_name, sizeof(raw_name));
228
229     r = sys_fgetxattr(fd, raw_name, (char *)val + pos, chunk_size);
230     if (i && r == -ENODATA) {
231       ret = pos;
232       break;
233     }
234     if (r < 0) {
235       ret = r;
236       break;
237     }
238
239     if (r > 0) {
240       pos += r;
241       size -= r;
242     }
243
244     i++;
245   } while (size && (r == CHAIN_XATTR_MAX_BLOCK_LEN ||
246                     r == CHAIN_XATTR_SHORT_BLOCK_LEN));
247
248   if (r >= 0) {
249     ret = pos;
250     /* is there another chunk? that can happen if the last read size span over
251        exactly one block */
252     if (chunk_size == CHAIN_XATTR_MAX_BLOCK_LEN ||
253         chunk_size == CHAIN_XATTR_SHORT_BLOCK_LEN) {
254       get_raw_xattr_name(name, i, raw_name, sizeof(raw_name));
255       r = sys_fgetxattr(fd, raw_name, 0, 0);
256       if (r > 0) { // there's another chunk.. the original buffer was too small
257         ret = -ERANGE;
258       }
259     }
260   }
261   return ret;
262 }
263
264
265 // setxattr
266
267 int get_xattr_block_size(size_t size)
268 {
269   if (size <= CHAIN_XATTR_SHORT_LEN_THRESHOLD)
270     // this may fit in the inode; stripe over short attrs so that XFS
271     // won't kick it out.
272     return CHAIN_XATTR_SHORT_BLOCK_LEN;
273   return CHAIN_XATTR_MAX_BLOCK_LEN;
274 }
275
276 // removexattr
277
278 int chain_removexattr(const char *fn, const char *name)
279 {
280   int i = 0;
281   char raw_name[CHAIN_XATTR_MAX_NAME_LEN * 2 + 16];
282   int r;
283
284   do {
285     get_raw_xattr_name(name, i, raw_name, sizeof(raw_name));
286     r = sys_removexattr(fn, raw_name);
287     if (!i && r < 0) {
288       return r;
289     }
290     i++;
291   } while (r >= 0);
292   return 0;
293 }
294
295 int chain_fremovexattr(int fd, const char *name)
296 {
297   int i = 0;
298   char raw_name[CHAIN_XATTR_MAX_NAME_LEN * 2 + 16];
299   int r;
300
301   do {
302     get_raw_xattr_name(name, i, raw_name, sizeof(raw_name));
303     r = sys_fremovexattr(fd, raw_name);
304     if (!i && r < 0) {
305       return r;
306     }
307     i++;
308   } while (r >= 0);
309   return 0;
310 }
311
312
313 // listxattr
314
315 int chain_listxattr(const char *fn, char *names, size_t len) {
316   int r;
317
318   if (!len)
319     return sys_listxattr(fn, names, len) * 2;
320
321   r = sys_listxattr(fn, 0, 0);
322   if (r < 0)
323     return r;
324
325   size_t total_len = r * 2; // should be enough
326   char *full_buf = (char *)malloc(total_len);
327   if (!full_buf)
328     return -ENOMEM;
329
330   r = sys_listxattr(fn, full_buf, total_len);
331   if (r < 0) {
332     free(full_buf);
333     return r;
334   }
335
336   char *p = full_buf;
337   const char *end = full_buf + r;
338   char *dest = names;
339   char *dest_end = names + len;
340
341   while (p < end) {
342     char name[CHAIN_XATTR_MAX_NAME_LEN * 2 + 16];
343     int attr_len = strlen(p);
344     bool is_first;
345     int name_len = translate_raw_name(p, name, sizeof(name), &is_first);
346     if (is_first)  {
347       if (dest + name_len > dest_end) {
348         r = -ERANGE;
349         goto done;
350       }
351       strcpy(dest, name);
352       dest += name_len + 1;
353     }
354     p += attr_len + 1;
355   }
356   r = dest - names;
357
358 done:
359   free(full_buf);
360   return r;
361 }
362
363 int chain_flistxattr(int fd, char *names, size_t len) {
364   int r;
365   char *p;
366   const char * end;
367   char *dest;
368   char *dest_end;
369
370   if (!len)
371     return sys_flistxattr(fd, names, len) * 2;
372
373   r = sys_flistxattr(fd, 0, 0);
374   if (r < 0)
375     return r;
376
377   size_t total_len = r * 2; // should be enough
378   char *full_buf = (char *)malloc(total_len);
379   if (!full_buf)
380     return -ENOMEM;
381
382   r = sys_flistxattr(fd, full_buf, total_len);
383   if (r < 0)
384     goto done;
385
386   p = full_buf;
387   end = full_buf + r;
388   dest = names;
389   dest_end = names + len;
390
391   while (p < end) {
392     char name[CHAIN_XATTR_MAX_NAME_LEN * 2 + 16];
393     int attr_len = strlen(p);
394     bool is_first;
395     int name_len = translate_raw_name(p, name, sizeof(name), &is_first);
396     if (is_first)  {
397       if (dest + name_len > dest_end) {
398         r = -ERANGE;
399         goto done;
400       }
401       strcpy(dest, name);
402       dest += name_len + 1;
403     }
404     p += attr_len + 1;
405   }
406   r = dest - names;
407
408 done:
409   free(full_buf);
410   return r;
411 }