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