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;