[metadata] Fix leaks when handling a few attributes (#16675)
[mono-project.git] / support / sys-xattr.c
blob4b7f55e086bab15e15b865329fde696fea9d95de
1 /*
2 * Wrapper functions for <sys/xattr.h> (or <attr/xattr.h>) and <sys/extattr.h>
4 * Authors:
5 * Daniel Drake (dsd@gentoo.org)
6 * Jonathan Pryor (jonpryor@vt.edu)
8 * Copyright (C) 2005 Daniel Drake
9 * Copyright (C) 2006 Jonathan Pryor
12 #include <config.h>
14 //If we're compiling to API level < 16 this won't be available
15 #if defined (HOST_ANDROID) && __ANDROID_API__ < 16
16 #define ANDROID_NO_XATTR
17 #endif
19 #if (defined(HAVE_SYS_XATTR_H) || defined(HAVE_ATTR_ATTR_H) || defined(HAVE_SYS_EXTATTR_H)) && !defined (ANDROID_NO_XATTR)
21 #include <sys/types.h>
24 * Where available, we prefer to use the libc implementation of the xattr
25 * syscalls. However, we also support using libattr for this on systems where
26 * libc does not provide this (e.g. glibc-2.2 and older)
27 * (configure-time magic is used to select which library to link to)
29 #ifdef HAVE_SYS_XATTR_H
30 // libc
31 #include <sys/xattr.h>
32 #define EA_UNIX
33 #elif HAVE_ATTR_ATTR_H
34 // libattr
35 #include <attr/xattr.h>
36 #define EA_UNIX
37 #endif /* HAVE_SYS_XATTR_H */
39 #ifdef HAVE_SYS_EXTATTR_H
40 #include <sys/extattr.h>
41 #include <sys/uio.h>
42 #define EA_BSD
43 #endif
45 #include <unistd.h>
46 #include <fcntl.h>
47 #include <errno.h>
48 #include <string.h>
49 #include <stdlib.h>
51 #include "map.h"
52 #include "mph.h"
55 * Linux provides extended attributes through the <sys/xattr.h> API.
56 * Any file or link can have attributes assigned to it (provided that they are
57 * supported by the backing filesystem). Each attribute has to be placed in a
58 * namespace, of which "user" is the most common. Namespaces are specified as
59 * a prefix to the attribute name, proceeded by a '.' (e.g. user.myattribute)
61 * FreeBSD provides extended attributes through the <sys/extattr.h> API.
62 * Behaviour is very similar to Linux EA's, but the namespace is specified
63 * through an enum-style parameter rather than as a prefix to an attribute
64 * name. There are also differences in the behaviour of the "list attributes"
65 * system calls.
67 * This file merges the two implementations into a single API for use by the
68 * Mono.Unix.Syscall.*xattr methods. No matter which OS you are on, things
69 * should "just work" the same as anywhere else.
71 * The API provided here leans more towards the Linux implementation. Attribute
72 * namespaces are provided as prefixes to the attribute name (followed by '.').
73 * There is no limit to the namespaces accepted by the Linux side of this
74 * implementation, but you are obviously limited to the ones available to you
75 * on the system.
76 * FreeBSD namespaces have to be converted from the textual prefix into their
77 * relevant number so that they can be used in the FreeBSD system calls.
78 * This means that the only namespaces available are the ones known by in this
79 * file (see bsd_extattr_namespaces). However, you can also specify the
80 * numericalnamespace index yourself, by using an attribute name such as
81 * "5.myattr".
82 * (this will obviously fail on Linux, your code will no longer be 'portable')
84 * Linux {,l,f}setxattr calls have a flags parameter which allow you to control
85 * what should happen if an attribute with the same name does (or doesn't)
86 * already exist. The 'flags' parameter is available here, but because FreeBSD
87 * does not support this kind of refinement, it will fail on FreeBSD if you
88 * specify anything other than XATTR_AUTO (XATTR_AUTO will create the attribute
89 * if it doesn't already exist, and overwrite the existing attribute if it
90 * already set).
92 * For usage and behaviour information, see the monodoc documentation on the
93 * Mono.Unix.Syscall class.
96 G_BEGIN_DECLS
99 // HELPER FUNCTIONS
102 #ifdef EA_BSD
104 struct BsdNamespaceInfo {
105 const char *name;
106 int value;
109 static struct BsdNamespaceInfo bsd_extattr_namespaces[] = {
110 {"user" , EXTATTR_NAMESPACE_USER},
111 {"system" , EXTATTR_NAMESPACE_SYSTEM}
114 static int bsd_check_flags (gint32 flags)
116 // BSD doesn't support flags, but always provides the same behaviour as
117 // XATTR_AUTO. So we enforce that here.
118 if (flags != Mono_Posix_XattrFlags_XATTR_AUTO) {
119 errno = EINVAL;
120 return -1;
122 return 0;
125 // On FreeBSD, we need to convert "user.blah" into namespace 1 and attribute
126 // name "blah", or maybe "6.blah" into namespace 6 attribute "blah"
127 static int
128 bsd_handle_nsprefix (const char *name, char **_name, int *namespace)
130 int i;
131 gchar **components = g_strsplit (name, ".", 2);
133 // Find namespace number from textual representation
134 for (i = 0; i < G_N_ELEMENTS(bsd_extattr_namespaces); i++)
135 if (strcmp (bsd_extattr_namespaces[i].name, components[0]) == 0) {
136 *namespace = bsd_extattr_namespaces[i].value;
137 break;
140 if (*namespace == 0) {
141 // Perhaps they specified the namespace number themselves..?
142 char *endptr;
143 *namespace = (int) strtol (components[0], &endptr, 10);
144 if (*endptr != '\0')
145 return -1;
148 *_name = g_strdup (components[1]);
149 g_strfreev (components);
150 return 0;
153 static void
154 init_attrlists (char *attrlists[])
156 memset (attrlists, 0, G_N_ELEMENTS(bsd_extattr_namespaces) * sizeof(char*));
159 static void
160 free_attrlists (char *attrlists[])
162 int i;
163 for (i = 0; i < G_N_ELEMENTS(bsd_extattr_namespaces); i++)
164 g_free (attrlists[i]);
167 // Counts the number of attributes in the result of a
168 // extattr_list_*() call. Note that the format of the data
169 // is: \3one\3two\6eleven where the leading charaters represent the length
170 // of the following attribute. (the description in the man-page is wrong)
171 static unsigned int
172 count_num_attrs (char *attrs, size_t size)
174 size_t i = 0;
175 unsigned int num_attrs = 0;
177 if (!attrs || !size)
178 return 0;
180 while (i < size) {
181 num_attrs++;
182 i += attrs[i] + 1;
185 return num_attrs;
188 // Convert a BSD-style list buffer (see the description for count_num_attrs)
189 // into a Linux-style NULL-terminated list including namespace prefix.
190 static char
191 *bsd_convert_list (const char *nsprefix, const char *src, size_t size, char *dest)
193 size_t i = 0;
194 if (src == NULL || dest == NULL || size == 0)
195 return NULL;
197 while (i < size) {
198 // Read length
199 int attr_len = (int) src[i];
200 int prefix_len = strlen (nsprefix);
202 // Add namespace prefix
203 strncpy (dest, nsprefix, prefix_len);
204 dest[prefix_len] = '.';
205 dest += prefix_len + 1;
207 // Copy attribute
208 memcpy(dest, src + ++i, attr_len);
210 // NULL-terminate
211 i += attr_len;
212 dest[attr_len] = '\0';
213 dest += attr_len + 1;
216 return dest;
219 // Combine all the lists of attributes that we know about into a single
220 // Linux-style buffer
221 static ssize_t
222 bsd_combine_lists (char *attrlists[], char *dest, size_t dest_size_needed, size_t dest_size)
224 int i;
225 if (!dest)
226 return dest_size_needed;
228 if (dest_size < dest_size_needed) {
229 errno = ERANGE;
230 return -1;
233 for (i = 0; i < G_N_ELEMENTS(bsd_extattr_namespaces); i++)
234 if (attrlists[i])
235 dest = bsd_convert_list (bsd_extattr_namespaces[i].name, attrlists[i], strlen (attrlists[i]), dest);
237 return dest_size_needed;
240 static mph_ssize_t
241 bsd_listxattr (const char *path, void *list, mph_size_t size)
243 size_t full_size = 0;
244 int i;
245 char *attrlists[G_N_ELEMENTS(bsd_extattr_namespaces)];
247 init_attrlists (attrlists);
248 for (i = 0; i < G_N_ELEMENTS(bsd_extattr_namespaces); i++) {
249 size_t buf_size;
250 int num_attrs;
252 buf_size = (size_t) extattr_list_file (path, i + 1, NULL, 0);
253 if (buf_size == -1)
254 continue;
256 attrlists[i] = g_malloc0 (buf_size + 1);
257 buf_size = (size_t) extattr_list_file (path, i + 1, attrlists[i], buf_size);
258 if (buf_size == -1)
259 continue;
261 num_attrs = count_num_attrs(attrlists[i], buf_size);
262 full_size += buf_size + (num_attrs * (strlen (bsd_extattr_namespaces[i].name) + 1));
265 full_size = bsd_combine_lists (attrlists, (char *) list, full_size, size);
266 free_attrlists (attrlists);
267 return full_size;
270 static mph_ssize_t
271 bsd_llistxattr (const char *path, void *list, mph_size_t size)
273 size_t full_size = 0;
274 int i;
275 char *attrlists[G_N_ELEMENTS(bsd_extattr_namespaces)];
277 init_attrlists (attrlists);
278 for (i = 0; i < G_N_ELEMENTS(bsd_extattr_namespaces); i++) {
279 size_t buf_size;
280 int num_attrs;
282 buf_size = (size_t) extattr_list_link (path, i + 1, NULL, 0);
283 if (buf_size == -1)
284 continue;
286 attrlists[i] = g_malloc0 (buf_size + 1);
287 buf_size = (size_t) extattr_list_link (path, i + 1, attrlists[i], buf_size);
288 if (buf_size == -1)
289 continue;
291 num_attrs = count_num_attrs(attrlists[i], buf_size);
292 full_size += buf_size + (num_attrs * (strlen (bsd_extattr_namespaces[i].name) + 1));
295 full_size = bsd_combine_lists (attrlists, (char *) list, full_size, size);
296 free_attrlists (attrlists);
297 return full_size;
300 static mph_ssize_t
301 bsd_flistxattr (int fd, void *list, mph_size_t size)
303 size_t full_size = 0;
304 int i;
305 char *attrlists[G_N_ELEMENTS(bsd_extattr_namespaces)];
307 init_attrlists (attrlists);
308 for (i = 0; i < G_N_ELEMENTS(bsd_extattr_namespaces); i++) {
309 size_t buf_size;
310 int num_attrs;
312 buf_size = (size_t) extattr_list_fd (fd, i + 1, NULL, 0);
313 if (buf_size == -1)
314 continue;
316 attrlists[i] = g_malloc0 (buf_size + 1);
317 buf_size = (size_t) extattr_list_fd (fd, i + 1, attrlists[i], buf_size);
318 if (buf_size == -1)
319 continue;
321 num_attrs = count_num_attrs(attrlists[i], buf_size);
322 full_size += buf_size + (num_attrs * (strlen (bsd_extattr_namespaces[i].name) + 1));
325 full_size = bsd_combine_lists (attrlists, (char *) list, full_size, size);
326 free_attrlists (attrlists);
327 return full_size;
330 #endif /* EA_BSD */
333 // THE PROVIDED API
336 gint32
337 Mono_Posix_Syscall_setxattr (const char *path, const char *name, unsigned char *value, mph_size_t size, gint32 flags)
339 gint32 ret;
340 mph_return_if_size_t_overflow (size);
342 #ifdef EA_UNIX
344 int _flags;
345 if (Mono_Posix_FromXattrFlags (flags, &_flags) == -1)
346 return -1;
347 #if __APPLE__
348 ret = setxattr (path, name, value, (size_t) size, 0, _flags);
349 #else /* __APPLE__ */
350 ret = setxattr (path, name, value, (size_t) size, _flags);
351 #endif /* __APPLE__ */
353 #else /* EA_UNIX */
355 char *_name;
356 int namespace;
357 if (bsd_check_flags (flags) == -1)
358 return -1;
359 if (bsd_handle_nsprefix (name, &_name, &namespace) == -1)
360 return -1;
361 ret = extattr_set_file (path, namespace, _name, value, (size_t) size);
362 g_free (_name);
364 #endif /* EA_UNIX */
366 return ret;
369 #if !__APPLE__
370 gint32
371 Mono_Posix_Syscall_lsetxattr (const char *path, const char *name, unsigned char *value, mph_size_t size, gint32 flags)
373 gint32 ret;
374 mph_return_if_size_t_overflow (size);
376 #ifdef EA_UNIX
378 int _flags;
379 if (Mono_Posix_FromXattrFlags (flags, &_flags) == -1)
380 return -1;
381 ret = lsetxattr (path, name, value, size, _flags);
383 #else /* EA_UNIX */
385 char *_name;
386 int namespace;
387 if (bsd_check_flags (flags) == -1)
388 return -1;
389 if (bsd_handle_nsprefix (name, &_name, &namespace) == -1)
390 return -1;
391 ret = extattr_set_link (path, namespace, _name, value, (size_t) size);
392 g_free (_name);
394 #endif /* EA_UNIX */
396 return ret;
398 #endif /* !__APPLE__ */
400 gint32
401 Mono_Posix_Syscall_fsetxattr (int fd, const char *name, unsigned char *value, mph_size_t size, gint32 flags)
403 gint32 ret;
404 mph_return_if_size_t_overflow (size);
406 #ifdef EA_UNIX
408 int _flags;
409 if (Mono_Posix_FromXattrFlags (flags, &_flags) == -1)
410 return -1;
411 #if __APPLE__
412 ret = fsetxattr (fd, name, value, (size_t) size, 0, _flags);
413 #else /* __APPLE__ */
414 ret = fsetxattr (fd, name, value, (size_t) size, _flags);
415 #endif /* __APPLE__ */
417 #else /* EA_UNIX */
419 char *_name;
420 int namespace;
421 if (bsd_check_flags (flags) == -1)
422 return -1;
423 if (bsd_handle_nsprefix (name, &_name, &namespace) == -1)
424 return -1;
425 ret = extattr_set_fd (fd, namespace, _name, value, (size_t) size);
426 g_free (_name);
428 #endif /* EA_UNIX */
430 return ret;
433 mph_ssize_t
434 Mono_Posix_Syscall_getxattr (const char *path, const char *name, unsigned char *value, mph_size_t size)
436 mph_ssize_t ret;
437 mph_return_if_size_t_overflow (size);
439 #ifdef EA_UNIX
440 #if __APPLE__
441 ret = getxattr (path, name, value, (size_t) size, 0, 0);
442 #else /* __APPLE__ */
443 ret = getxattr (path, name, value, (size_t) size);
444 #endif /* __APPLE__ */
445 #else /* EA_UNIX */
447 char *_name;
448 int namespace;
449 if (bsd_handle_nsprefix (name, &_name, &namespace) == -1)
450 return -1;
451 ret = extattr_get_file (path, namespace, _name, value, (size_t) size);
452 g_free (_name);
454 #endif /* EA_UNIX */
456 return ret;
459 #if !__APPLE__
460 mph_ssize_t
461 Mono_Posix_Syscall_lgetxattr (const char *path, const char *name, unsigned char *value, mph_size_t size)
463 mph_ssize_t ret;
464 mph_return_if_size_t_overflow (size);
466 #ifdef EA_UNIX
467 ret = lgetxattr (path, name, value, (size_t) size);
468 #else /* EA_UNIX */
470 char *_name;
471 int namespace;
472 if (bsd_handle_nsprefix (name, &_name, &namespace) == -1)
473 return -1;
474 ret = extattr_get_link (path, namespace, _name, value, (size_t) size);
475 g_free (_name);
477 #endif /* EA_UNIX */
479 return ret;
481 #endif /* !__APPLE__ */
483 mph_ssize_t
484 Mono_Posix_Syscall_fgetxattr (int fd, const char *name, unsigned char *value, mph_size_t size)
486 mph_ssize_t ret;
487 mph_return_if_size_t_overflow (size);
489 #ifdef EA_UNIX
490 #if __APPLE__
491 ret = fgetxattr (fd, name, value, (size_t) size, 0, 0);
492 #else /* __APPLE__ */
493 ret = fgetxattr (fd, name, value, (size_t) size);
494 #endif /* __APPLE__ */
495 #else /* EA_UNIX */
497 char *_name;
498 int namespace;
499 if (bsd_handle_nsprefix (name, &_name, &namespace) == -1)
500 return -1;
501 ret = extattr_get_fd (fd, namespace, _name, value, (size_t) size);
502 g_free (_name);
504 #endif /* EA_UNIX */
506 return ret;
509 mph_ssize_t
510 Mono_Posix_Syscall_listxattr (const char *path, unsigned char *list, mph_size_t size)
512 mph_return_if_size_t_overflow (size);
514 #ifdef EA_UNIX
515 #if __APPLE__
516 return listxattr (path, (char*) list, (size_t) size, 0);
517 #else /* __APPLE__ */
518 return listxattr (path, (char*) list, (size_t) size);
519 #endif /* __APPLE__ */
520 #else /* EA_UNIX */
521 return bsd_listxattr (path, list, size);
522 #endif /* EA_UNIX */
525 #if !__APPLE__
526 mph_ssize_t
527 Mono_Posix_Syscall_llistxattr (const char *path, unsigned char *list, mph_size_t size)
529 mph_return_if_size_t_overflow (size);
531 #ifdef EA_UNIX
532 return llistxattr (path, (char*) list, (size_t) size);
533 #else /* EA_UNIX */
534 return bsd_llistxattr (path, list, size);
535 #endif /* EA_UNIX */
537 #endif /* !__APPLE__ */
539 mph_ssize_t
540 Mono_Posix_Syscall_flistxattr (int fd, unsigned char *list, mph_size_t size)
542 mph_return_if_size_t_overflow (size);
544 #ifdef EA_UNIX
545 #if __APPLE__
546 return flistxattr (fd, (char*) list, (size_t) size, 0);
547 #else /* __APPLE__ */
548 return flistxattr (fd, (char*) list, (size_t) size);
549 #endif /* __APPLE__ */
550 #else /* EA_UNIX */
551 return bsd_flistxattr (fd, list, size);
552 #endif /* EA_UNIX */
555 gint32
556 Mono_Posix_Syscall_removexattr (const char *path, const char *name)
558 gint32 ret;
560 #ifdef EA_UNIX
561 #if __APPLE__
562 ret = removexattr (path, name, 0);
563 #else /* __APPLE__ */
564 ret = removexattr (path, name);
565 #endif /* __APPLE__ */
566 #else /* EA_UNIX */
568 char *_name;
569 int namespace;
570 if (bsd_handle_nsprefix (name, &_name, &namespace) == -1)
571 return -1;
572 ret = extattr_delete_file (path, namespace, _name);
573 g_free (_name);
575 #endif /* EA_UNIX */
577 return ret;
580 #if !__APPLE__
581 gint32
582 Mono_Posix_Syscall_lremovexattr (const char *path, const char *name)
584 gint32 ret;
586 #ifdef EA_UNIX
587 ret = lremovexattr (path, name);
588 #else /* EA_UNIX */
590 char *_name;
591 int namespace;
592 if (bsd_handle_nsprefix (name, &_name, &namespace) == -1)
593 return -1;
594 ret = extattr_delete_link (path, namespace, _name);
595 g_free (_name);
597 #endif /* EA_UNIX */
599 return ret;
601 #endif /* !__APPLE__ */
603 gint32
604 Mono_Posix_Syscall_fremovexattr (int fd, const char *name)
606 gint32 ret;
608 #ifdef EA_UNIX
609 #if __APPLE__
610 ret = fremovexattr (fd, name, 0);
611 #else /* __APPLE__ */
612 ret = fremovexattr (fd, name);
613 #endif /* __APPLE__ */
614 #else /* EA_UNIX */
616 char *_name;
617 int namespace;
618 if (bsd_handle_nsprefix (name, &_name, &namespace) == -1)
619 return -1;
620 ret = extattr_delete_fd (fd, namespace, _name);
621 g_free (_name);
623 #endif /* EA_UNIX */
625 return ret;
628 G_END_DECLS
630 #endif /* HAVE_SYS_XATTR_H || HAVE_ATTR_ATTR_H || HAVE_SYS_EXTATTR_H */
633 * vim: noexpandtab