]> git.proxmox.com Git - ceph.git/blob - ceph/src/os/filestore/chain_xattr.cc
update sources to v12.1.0
[ceph.git] / 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 }