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