1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
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"
12 #if defined(__linux__)
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
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.
28 void get_raw_xattr_name(const char *name, int i, char *raw_name, int raw_len)
34 case '@': /* escape it */
36 assert (pos < raw_len - 1);
43 assert(pos < raw_len - 1);
54 int r = snprintf(raw_name, raw_len - pos, "@%d", i);
55 assert(r < raw_len - pos);
59 static int translate_raw_name(const char *raw_name, char *name, int name_len, bool *is_first)
66 case '@': /* escape it */
70 if (*raw_name != '@') {
81 assert(pos < name_len);
93 static int getxattr_len(const char *fn, const char *name)
96 char raw_name[CHAIN_XATTR_MAX_NAME_LEN * 2 + 16];
100 get_raw_xattr_name(name, i, raw_name, sizeof(raw_name));
101 r = sys_getxattr(fn, raw_name, 0, 0);
108 } while (r == CHAIN_XATTR_MAX_BLOCK_LEN ||
109 r == CHAIN_XATTR_SHORT_BLOCK_LEN);
114 int chain_getxattr(const char *fn, const char *name, void *val, size_t size)
117 char raw_name[CHAIN_XATTR_MAX_NAME_LEN * 2 + 16];
123 return getxattr_len(fn, name);
127 get_raw_xattr_name(name, i, raw_name, sizeof(raw_name));
129 r = sys_getxattr(fn, raw_name, (char *)val + pos, chunk_size);
130 if (i && r == -ENODATA) {
145 } while (size && (r == CHAIN_XATTR_MAX_BLOCK_LEN ||
146 r == CHAIN_XATTR_SHORT_BLOCK_LEN));
150 /* is there another chunk? that can happen if the last read size span over
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
164 int chain_getxattr_buf(const char *fn, const char *name, bufferptr *bp)
166 size_t size = 1024; // Initial
169 int r = chain_getxattr(
189 assert(0 == "unreachable");
193 static int chain_fgetxattr_len(int fd, const char *name)
195 int i = 0, total = 0;
196 char raw_name[CHAIN_XATTR_MAX_NAME_LEN * 2 + 16];
200 get_raw_xattr_name(name, i, raw_name, sizeof(raw_name));
201 r = sys_fgetxattr(fd, raw_name, 0, 0);
208 } while (r == CHAIN_XATTR_MAX_BLOCK_LEN ||
209 r == CHAIN_XATTR_SHORT_BLOCK_LEN);
214 int chain_fgetxattr(int fd, const char *name, void *val, size_t size)
217 char raw_name[CHAIN_XATTR_MAX_NAME_LEN * 2 + 16];
223 return chain_fgetxattr_len(fd, name);
227 get_raw_xattr_name(name, i, raw_name, sizeof(raw_name));
229 r = sys_fgetxattr(fd, raw_name, (char *)val + pos, chunk_size);
230 if (i && r == -ENODATA) {
245 } while (size && (r == CHAIN_XATTR_MAX_BLOCK_LEN ||
246 r == CHAIN_XATTR_SHORT_BLOCK_LEN));
250 /* is there another chunk? that can happen if the last read size span over
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
267 int get_xattr_block_size(size_t size)
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;
278 int chain_removexattr(const char *fn, const char *name)
281 char raw_name[CHAIN_XATTR_MAX_NAME_LEN * 2 + 16];
285 get_raw_xattr_name(name, i, raw_name, sizeof(raw_name));
286 r = sys_removexattr(fn, raw_name);
295 int chain_fremovexattr(int fd, const char *name)
298 char raw_name[CHAIN_XATTR_MAX_NAME_LEN * 2 + 16];
302 get_raw_xattr_name(name, i, raw_name, sizeof(raw_name));
303 r = sys_fremovexattr(fd, raw_name);
315 int chain_listxattr(const char *fn, char *names, size_t len) {
319 return sys_listxattr(fn, names, len) * 2;
321 r = sys_listxattr(fn, 0, 0);
325 size_t total_len = r * 2; // should be enough
326 char *full_buf = (char *)malloc(total_len);
330 r = sys_listxattr(fn, full_buf, total_len);
337 const char *end = full_buf + r;
339 char *dest_end = names + len;
342 char name[CHAIN_XATTR_MAX_NAME_LEN * 2 + 16];
343 int attr_len = strlen(p);
345 int name_len = translate_raw_name(p, name, sizeof(name), &is_first);
347 if (dest + name_len > dest_end) {
352 dest += name_len + 1;
363 int chain_flistxattr(int fd, char *names, size_t len) {
371 return sys_flistxattr(fd, names, len) * 2;
373 r = sys_flistxattr(fd, 0, 0);
377 size_t total_len = r * 2; // should be enough
378 char *full_buf = (char *)malloc(total_len);
382 r = sys_flistxattr(fd, full_buf, total_len);
389 dest_end = names + len;
392 char name[CHAIN_XATTR_MAX_NAME_LEN * 2 + 16];
393 int attr_len = strlen(p);
395 int name_len = translate_raw_name(p, name, sizeof(name), &is_first);
397 if (dest + name_len > dest_end) {
402 dest += name_len + 1;