]> arthur.barton.de Git - netatalk.git/blob - libatalk/vfs/extattr.c
Merge master
[netatalk.git] / libatalk / vfs / extattr.c
1 /* 
2    Unix SMB/CIFS implementation.
3    Samba system utilities
4    Copyright (C) Andrew Tridgell 1992-1998
5    Copyright (C) Jeremy Allison  1998-2005
6    Copyright (C) Timur Bakeyev        2005
7    Copyright (C) Bjoern Jacke    2006-2007
8    
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 2 of the License, or
12    (at your option) any later version.
13    
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18    
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22    
23    sys_copyxattr modified from LGPL2.1 libattr copyright
24    Copyright (C) 2001-2002 Silicon Graphics, Inc.  All Rights Reserved.
25    Copyright (C) 2001 Andreas Gruenbacher.
26       
27    Samba 3.0.28, modified for netatalk.
28 */
29
30 #ifdef HAVE_CONFIG_H
31 #include "config.h"
32 #endif
33
34 #include <stdlib.h>
35 #include <string.h>
36 #include <sys/types.h>
37 #include <errno.h>
38
39 #if HAVE_ATTR_XATTR_H
40 #include <attr/xattr.h>
41 #elif HAVE_SYS_XATTR_H
42 #include <sys/xattr.h>
43 #endif
44
45 #ifdef HAVE_SYS_EA_H
46 #include <sys/ea.h>
47 #endif
48
49 #ifdef HAVE_ATTROPEN
50
51 #include <dirent.h>
52 #endif
53
54 #ifdef HAVE_SYS_EXTATTR_H
55 #include <sys/extattr.h>
56 #endif
57
58 #include <atalk/adouble.h>
59 #include <atalk/util.h>
60 #include <atalk/logger.h>
61 #include <atalk/ea.h>
62 #include <atalk/compat.h>
63
64 /******** Solaris EA helper function prototypes ********/
65 #ifdef HAVE_ATTROPEN
66 #define SOLARIS_ATTRMODE S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP
67 static int solaris_write_xattr(int attrfd, const char *value, size_t size);
68 static ssize_t solaris_read_xattr(int attrfd, void *value, size_t size);
69 static ssize_t solaris_list_xattr(int attrdirfd, char *list, size_t size);
70 static int solaris_unlinkat(int attrdirfd, const char *name);
71 static int solaris_attropen(const char *path, const char *attrpath, int oflag, mode_t mode);
72 static int solaris_openat(int fildes, const char *path, int oflag, mode_t mode);
73 #endif
74
75 /**************************************************************************
76  Wrappers for extented attribute calls. Based on the Linux package with
77  support for IRIX and (Net|Free)BSD also. Expand as other systems have them.
78 ****************************************************************************/
79 static char attr_name[256 +5] = "user.";
80
81 static const char *prefix(const char *uname)
82 {
83 #if defined(HAVE_ATTROPEN)
84         return uname;
85 #else
86         strlcpy(attr_name +5, uname, 256);
87         return attr_name;
88 #endif
89 }
90
91 ssize_t sys_getxattr (const char *path, const char *uname, void *value, size_t size)
92 {
93         const char *name = prefix(uname);
94
95 #if defined(HAVE_GETXATTR)
96 #ifndef XATTR_ADD_OPT
97         return getxattr(path, name, value, size);
98 #else
99         int options = 0;
100         return getxattr(path, name, value, size, 0, options);
101 #endif
102 #elif defined(HAVE_GETEA)
103         return getea(path, name, value, size);
104 #elif defined(HAVE_EXTATTR_GET_FILE)
105         ssize_t retval;
106         /*
107          * The BSD implementation has a nasty habit of silently truncating
108          * the returned value to the size of the buffer, so we have to check
109          * that the buffer is large enough to fit the returned value.
110          */
111         if((retval = extattr_get_file(path, EXTATTR_NAMESPACE_USER, uname, NULL, 0)) >= 0) {
112         if (size == 0)
113             /* size == 0 means only return size */
114             return retval;
115                 if (retval > size) {
116                         errno = ERANGE;
117                         return -1;
118                 }
119                 if ((retval = extattr_get_file(path, EXTATTR_NAMESPACE_USER, uname, value, size)) >= 0)
120                         return retval;
121         }
122
123         LOG(log_maxdebug, logtype_default, "sys_getxattr: extattr_get_file() failed with: %s\n", strerror(errno));
124         return -1;
125 #elif defined(HAVE_ATTR_GET)
126         int retval, flags = 0;
127         int valuelength = (int)size;
128         char *attrname = strchr(name,'.') + 1;
129         
130         if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
131
132         retval = attr_get(path, attrname, (char *)value, &valuelength, flags);
133
134         return retval ? retval : valuelength;
135 #elif defined(HAVE_ATTROPEN)
136         ssize_t ret = -1;
137         int attrfd = solaris_attropen(path, name, O_RDONLY, 0);
138         if (attrfd >= 0) {
139                 ret = solaris_read_xattr(attrfd, value, size);
140                 close(attrfd);
141         }
142         return ret;
143 #else
144         errno = ENOSYS;
145         return -1;
146 #endif
147 }
148
149 ssize_t sys_fgetxattr (int filedes, const char *uname, void *value, size_t size)
150 {
151     const char *name = prefix(uname);
152
153 #if defined(HAVE_FGETXATTR)
154 #ifndef XATTR_ADD_OPT
155     return fgetxattr(filedes, name, value, size);
156 #else
157     int options = 0;
158     return fgetxattr(filedes, name, value, size, 0, options);
159 #endif
160 #elif defined(HAVE_FGETEA)
161     return fgetea(filedes, name, value, size);
162 #elif defined(HAVE_EXTATTR_GET_FD)
163     char *s;
164     ssize_t retval;
165     int attrnamespace = (strncmp(name, "system", 6) == 0) ? 
166         EXTATTR_NAMESPACE_SYSTEM : EXTATTR_NAMESPACE_USER;
167     const char *attrname = ((s=strchr_m(name, '.')) == NULL) ? name : s + 1;
168
169     if((retval=extattr_get_fd(filedes, attrnamespace, attrname, NULL, 0)) >= 0) {
170         if(retval > size) {
171             errno = ERANGE;
172             return -1;
173         }
174         if((retval=extattr_get_fd(filedes, attrnamespace, attrname, value, size)) >= 0)
175             return retval;
176     }
177
178     LOG(log_debug, logtype_default, "sys_fgetxattr: extattr_get_fd(): %s",
179         strerror(errno)));
180     return -1;
181 #elif defined(HAVE_ATTR_GETF)
182     int retval, flags = 0;
183     int valuelength = (int)size;
184     char *attrname = strchr(name,'.') + 1;
185
186     if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
187
188     retval = attr_getf(filedes, attrname, (char *)value, &valuelength, flags);
189
190     return retval ? retval : valuelength;
191 #elif defined(HAVE_ATTROPEN)
192     ssize_t ret = -1;
193     int attrfd = solaris_openat(filedes, name, O_RDONLY|O_XATTR, 0);
194     if (attrfd >= 0) {
195         ret = solaris_read_xattr(attrfd, value, size);
196         close(attrfd);
197     }
198     return ret;
199 #else
200     errno = ENOSYS;
201     return -1;
202 #endif
203 }
204
205 ssize_t sys_lgetxattr (const char *path, const char *uname, void *value, size_t size)
206 {
207         const char *name = prefix(uname);
208
209 #if defined(HAVE_LGETXATTR)
210         return lgetxattr(path, name, value, size);
211 #elif defined(HAVE_GETXATTR) && defined(XATTR_ADD_OPT)
212         int options = XATTR_NOFOLLOW;
213         return getxattr(path, name, value, size, 0, options);
214 #elif defined(HAVE_LGETEA)
215         return lgetea(path, name, value, size);
216 #elif defined(HAVE_EXTATTR_GET_LINK)
217         ssize_t retval;
218         if((retval=extattr_get_link(path, EXTATTR_NAMESPACE_USER, uname, NULL, 0)) >= 0) {
219                 if(retval > size) {
220                         errno = ERANGE;
221                         return -1;
222                 }
223                 if((retval=extattr_get_link(path, EXTATTR_NAMESPACE_USER, uname, value, size)) >= 0)
224                         return retval;
225         }
226         
227         LOG(log_maxdebug, logtype_default, "sys_lgetxattr: extattr_get_link() failed with: %s\n", strerror(errno));
228         return -1;
229 #elif defined(HAVE_ATTR_GET)
230         int retval, flags = ATTR_DONTFOLLOW;
231         int valuelength = (int)size;
232         char *attrname = strchr(name,'.') + 1;
233         
234         if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
235
236         retval = attr_get(path, attrname, (char *)value, &valuelength, flags);
237
238         return retval ? retval : valuelength;
239 #elif defined(HAVE_ATTROPEN)
240         ssize_t ret = -1;
241         int attrfd = solaris_attropen(path, name, O_RDONLY|AT_SYMLINK_NOFOLLOW, 0);
242         if (attrfd >= 0) {
243                 ret = solaris_read_xattr(attrfd, value, size);
244                 close(attrfd);
245         }
246         return ret;
247 #else
248         errno = ENOSYS;
249         return -1;
250 #endif
251 }
252
253 #if defined(HAVE_EXTATTR_LIST_FILE)
254
255 #define EXTATTR_PREFIX(s)       (s), (sizeof((s))-1)
256
257 static struct {
258         int space;
259         const char *name;
260         size_t len;
261
262 extattr[] = {
263         { EXTATTR_NAMESPACE_SYSTEM, EXTATTR_PREFIX("") },
264         { EXTATTR_NAMESPACE_USER, EXTATTR_PREFIX("") },
265 };
266
267 typedef union {
268         const char *path;
269         int filedes;
270 } extattr_arg;
271
272 static ssize_t bsd_attr_list (int type, extattr_arg arg, char *list, size_t size)
273 {
274         ssize_t list_size;
275         int i, len;
276
277     switch(type) {
278 #if defined(HAVE_EXTATTR_LIST_FILE)
279     case 0:
280         list_size = extattr_list_file(arg.path, EXTATTR_NAMESPACE_USER, list, size);
281         break;
282 #endif
283 #if defined(HAVE_EXTATTR_LIST_LINK)
284     case 1:
285         list_size = extattr_list_link(arg.path, EXTATTR_NAMESPACE_USER, list, size);
286         break;
287 #endif
288 #if defined(HAVE_EXTATTR_LIST_FD)
289     case 2:
290         list_size = extattr_list_fd(arg.filedes, EXTATTR_NAMESPACE_USER, list, size);
291         break;
292 #endif
293     default:
294         errno = ENOSYS;
295         return -1;
296     }
297
298     /* Some error happend. Errno should be set by the previous call */
299     if(list_size < 0)
300         return -1;
301
302     /* No attributes */
303     if(list_size == 0)
304         return 0;
305
306     /* XXX: Call with an empty buffer may be used to calculate
307        necessary buffer size. Unfortunately, we can't say, how
308        many attributes were returned, so here is the potential
309        problem with the emulation.
310     */
311     if(list == NULL)
312         return list_size;
313
314     /* Buffer is too small to fit the results */
315     if(list_size > size) {
316         errno = ERANGE;
317         return -1;
318     }
319
320     /* Convert from pascal strings to C strings */
321     len = list[0];
322     memmove(list, list + 1, list_size);
323
324     for(i = len; i < list_size; ) {
325         LOG(log_maxdebug, logtype_afpd, "len: %d, i: %d", len, i);
326
327         len = list[i];
328         list[i] = '\0';
329         i += len + 1;
330     }
331
332         return list_size;
333 }
334
335 #endif
336
337 #if defined(HAVE_ATTR_LIST) && defined(HAVE_SYS_ATTRIBUTES_H)
338 static char attr_buffer[ATTR_MAX_VALUELEN];
339
340 static ssize_t irix_attr_list(const char *path, int filedes, char *list, size_t size, int flags)
341 {
342         int retval = 0, index;
343         attrlist_cursor_t *cursor = 0;
344         int total_size = 0;
345         attrlist_t * al = (attrlist_t *)attr_buffer;
346         attrlist_ent_t *ae;
347         size_t ent_size, left = size;
348         char *bp = list;
349
350         while (True) {
351             if (filedes)
352                 retval = attr_listf(filedes, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
353             else
354                 retval = attr_list(path, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
355             if (retval) break;
356             for (index = 0; index < al->al_count; index++) {
357                 ae = ATTR_ENTRY(attr_buffer, index);
358                 ent_size = strlen(ae->a_name) + sizeof("user.");
359                 if (left >= ent_size) {
360                     strncpy(bp, "user.", sizeof("user."));
361                     strncat(bp, ae->a_name, ent_size - sizeof("user."));
362                     bp += ent_size;
363                     left -= ent_size;
364                 } else if (size) {
365                     errno = ERANGE;
366                     retval = -1;
367                     break;
368                 }
369                 total_size += ent_size;
370             }
371             if (al->al_more == 0) break;
372         }
373         if (retval == 0) {
374             flags |= ATTR_ROOT;
375             cursor = 0;
376             while (True) {
377                 if (filedes)
378                     retval = attr_listf(filedes, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
379                 else
380                     retval = attr_list(path, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
381                 if (retval) break;
382                 for (index = 0; index < al->al_count; index++) {
383                     ae = ATTR_ENTRY(attr_buffer, index);
384                     ent_size = strlen(ae->a_name) + sizeof("system.");
385                     if (left >= ent_size) {
386                         strncpy(bp, "system.", sizeof("system."));
387                         strncat(bp, ae->a_name, ent_size - sizeof("system."));
388                         bp += ent_size;
389                         left -= ent_size;
390                     } else if (size) {
391                         errno = ERANGE;
392                         retval = -1;
393                         break;
394                     }
395                     total_size += ent_size;
396                 }
397                 if (al->al_more == 0) break;
398             }
399         }
400         return (ssize_t)(retval ? retval : total_size);
401 }
402
403 #endif
404
405 #if defined(HAVE_LISTXATTR)
406 static ssize_t remove_user(ssize_t ret, char *list, size_t size)
407 {
408         size_t len;
409         char *ptr;
410         char *ptr1;
411         ssize_t ptrsize;
412         
413         if (ret <= 0 || size == 0)
414                 return ret;
415         ptrsize = ret;
416         ptr = ptr1 = list;
417         while (ptrsize > 0) {
418                 len = strlen(ptr1) +1;
419                 ptrsize -= len;
420                 if (strncmp(ptr1, "user.",5)) {
421                         ptr1 += len;
422                         continue;
423                 }
424                 memmove(ptr, ptr1 +5, len -5);
425                 ptr += len -5;
426                 ptr1 += len;
427         }
428         return ptr -list;
429 }
430 #endif
431
432 ssize_t sys_listxattr (const char *path, char *list, size_t size)
433 {
434 #if defined(HAVE_LISTXATTR)
435         ssize_t ret;
436
437 #ifndef XATTR_ADD_OPT
438         ret = listxattr(path, list, size);
439 #else
440         int options = 0;
441         ret = listxattr(path, list, size, options);
442 #endif
443         return remove_user(ret, list, size);
444
445 #elif defined(HAVE_LISTEA)
446         return listea(path, list, size);
447 #elif defined(HAVE_EXTATTR_LIST_FILE)
448         extattr_arg arg;
449         arg.path = path;
450         return bsd_attr_list(0, arg, list, size);
451 #elif defined(HAVE_ATTR_LIST) && defined(HAVE_SYS_ATTRIBUTES_H)
452         return irix_attr_list(path, 0, list, size, 0);
453 #elif defined(HAVE_ATTROPEN)
454         ssize_t ret = -1;
455         int attrdirfd = solaris_attropen(path, ".", O_RDONLY, 0);
456         if (attrdirfd >= 0) {
457                 ret = solaris_list_xattr(attrdirfd, list, size);
458                 close(attrdirfd);
459         }
460         return ret;
461 #else
462         errno = ENOSYS;
463         return -1;
464 #endif
465 }
466
467 ssize_t sys_llistxattr (const char *path, char *list, size_t size)
468 {
469 #if defined(HAVE_LLISTXATTR)
470         ssize_t ret;
471
472         ret = llistxattr(path, list, size);
473         return remove_user(ret, list, size);
474 #elif defined(HAVE_LISTXATTR) && defined(XATTR_ADD_OPT)
475         ssize_t ret;
476         int options = XATTR_NOFOLLOW;
477
478         ret = listxattr(path, list, size, options);
479         return remove_user(ret, list, size);
480
481 #elif defined(HAVE_LLISTEA)
482         return llistea(path, list, size);
483 #elif defined(HAVE_EXTATTR_LIST_LINK)
484         extattr_arg arg;
485         arg.path = path;
486         return bsd_attr_list(1, arg, list, size);
487 #elif defined(HAVE_ATTR_LIST) && defined(HAVE_SYS_ATTRIBUTES_H)
488         return irix_attr_list(path, 0, list, size, ATTR_DONTFOLLOW);
489 #elif defined(HAVE_ATTROPEN)
490         ssize_t ret = -1;
491         int attrdirfd = solaris_attropen(path, ".", O_RDONLY|AT_SYMLINK_NOFOLLOW, 0);
492         if (attrdirfd >= 0) {
493                 ret = solaris_list_xattr(attrdirfd, list, size);
494                 close(attrdirfd);
495         }
496         return ret;
497 #else
498         errno = ENOSYS;
499         return -1;
500 #endif
501 }
502
503 int sys_removexattr (const char *path, const char *uname)
504 {
505         const char *name = prefix(uname);
506 #if defined(HAVE_REMOVEXATTR)
507 #ifndef XATTR_ADD_OPT
508         return removexattr(path, name);
509 #else
510         int options = 0;
511         return removexattr(path, name, options);
512 #endif
513 #elif defined(HAVE_REMOVEEA)
514         return removeea(path, name);
515 #elif defined(HAVE_EXTATTR_DELETE_FILE)
516         return extattr_delete_file(path, EXTATTR_NAMESPACE_USER, uname);
517 #elif defined(HAVE_ATTR_REMOVE)
518         int flags = 0;
519         char *attrname = strchr(name,'.') + 1;
520         
521         if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
522
523         return attr_remove(path, attrname, flags);
524 #elif defined(HAVE_ATTROPEN)
525         int ret = -1;
526         int attrdirfd = solaris_attropen(path, ".", O_RDONLY, 0);
527         if (attrdirfd >= 0) {
528                 ret = solaris_unlinkat(attrdirfd, name);
529                 close(attrdirfd);
530         }
531         return ret;
532 #else
533         errno = ENOSYS;
534         return -1;
535 #endif
536 }
537
538 int sys_lremovexattr (const char *path, const char *uname)
539 {
540         const char *name = prefix(uname);
541 #if defined(HAVE_LREMOVEXATTR)
542         return lremovexattr(path, name);
543 #elif defined(HAVE_REMOVEXATTR) && defined(XATTR_ADD_OPT)
544         int options = XATTR_NOFOLLOW;
545         return removexattr(path, name, options);
546 #elif defined(HAVE_LREMOVEEA)
547         return lremoveea(path, name);
548 #elif defined(HAVE_EXTATTR_DELETE_LINK)
549         return extattr_delete_link(path, EXTATTR_NAMESPACE_USER, uname);
550 #elif defined(HAVE_ATTR_REMOVE)
551         int flags = ATTR_DONTFOLLOW;
552         char *attrname = strchr(name,'.') + 1;
553         
554         if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
555
556         return attr_remove(path, attrname, flags);
557 #elif defined(HAVE_ATTROPEN)
558         int ret = -1;
559         int attrdirfd = solaris_attropen(path, ".", O_RDONLY|AT_SYMLINK_NOFOLLOW, 0);
560         if (attrdirfd >= 0) {
561                 ret = solaris_unlinkat(attrdirfd, name);
562                 close(attrdirfd);
563         }
564         return ret;
565 #else
566         errno = ENOSYS;
567         return -1;
568 #endif
569 }
570
571 int sys_setxattr (const char *path, const char *uname, const void *value, size_t size, int flags)
572 {
573         const char *name = prefix(uname);
574 #if defined(HAVE_SETXATTR)
575 #ifndef XATTR_ADD_OPT
576         return setxattr(path, name, value, size, flags);
577 #else
578         int options = 0;
579         return setxattr(path, name, value, size, 0, options);
580 #endif
581 #elif defined(HAVE_SETEA)
582         return setea(path, name, value, size, flags);
583 #elif defined(HAVE_EXTATTR_SET_FILE)
584         int retval = 0;
585         if (flags) {
586                 /* Check attribute existence */
587                 retval = extattr_get_file(path, EXTATTR_NAMESPACE_USER, uname, NULL, 0);
588                 if (retval < 0) {
589                         /* REPLACE attribute, that doesn't exist */
590                         if (flags & XATTR_REPLACE && errno == ENOATTR) {
591                                 errno = ENOATTR;
592                                 return -1;
593                         }
594                         /* Ignore other errors */
595                 }
596                 else {
597                         /* CREATE attribute, that already exists */
598                         if (flags & XATTR_CREATE) {
599                                 errno = EEXIST;
600                                 return -1;
601                         }
602                 }
603         }
604         retval = extattr_set_file(path, EXTATTR_NAMESPACE_USER, uname, value, size);
605         return (retval < 0) ? -1 : 0;
606 #elif defined(HAVE_ATTR_SET)
607         int myflags = 0;
608         char *attrname = strchr(name,'.') + 1;
609         
610         if (strncmp(name, "system", 6) == 0) myflags |= ATTR_ROOT;
611         if (flags & XATTR_CREATE) myflags |= ATTR_CREATE;
612         if (flags & XATTR_REPLACE) myflags |= ATTR_REPLACE;
613
614         return attr_set(path, attrname, (const char *)value, size, myflags);
615 #elif defined(HAVE_ATTROPEN)
616         int ret = -1;
617         int myflags = O_RDWR;
618         int attrfd;
619         if (flags & XATTR_CREATE) myflags |= O_EXCL;
620         if (!(flags & XATTR_REPLACE)) myflags |= O_CREAT;
621         attrfd = solaris_attropen(path, name, myflags, (mode_t) SOLARIS_ATTRMODE);
622         if (attrfd >= 0) {
623                 ret = solaris_write_xattr(attrfd, value, size);
624                 close(attrfd);
625         }
626         return ret;
627 #else
628         errno = ENOSYS;
629         return -1;
630 #endif
631 }
632
633 int sys_fsetxattr (int filedes, const char *uname, const void *value, size_t size, int flags)
634 {
635     const char *name = prefix(uname);
636
637 #if defined(HAVE_FSETXATTR)
638 #ifndef XATTR_ADD_OPT
639     return fsetxattr(filedes, name, value, size, flags);
640 #else
641     int options = 0;
642     return fsetxattr(filedes, name, value, size, 0, options);
643 #endif
644 #elif defined(HAVE_FSETEA)
645     return fsetea(filedes, name, value, size, flags);
646 #elif defined(HAVE_EXTATTR_SET_FD)
647     char *s;
648     int retval = 0;
649     int attrnamespace = (strncmp(name, "system", 6) == 0) ? 
650         EXTATTR_NAMESPACE_SYSTEM : EXTATTR_NAMESPACE_USER;
651     const char *attrname = ((s=strchr_m(name, '.')) == NULL) ? name : s + 1;
652     if (flags) {
653         /* Check attribute existence */
654         retval = extattr_get_fd(filedes, attrnamespace, attrname, NULL, 0);
655         if (retval < 0) {
656             /* REPLACE attribute, that doesn't exist */
657             if (flags & XATTR_REPLACE && errno == ENOATTR) {
658                 errno = ENOATTR;
659                 return -1;
660             }
661             /* Ignore other errors */
662         }
663         else {
664             log_error, logtype_default            /* CREATE attribute, that already exists */
665             if (flags & XATTR_CREATE) {
666                 errno = EEXIST;
667                 return -1;
668             }
669         }
670     }
671     retval = extattr_set_fd(filedes, attrnamespace, attrname, value, size);
672     return (retval < 0) ? -1 : 0;
673 #elif defined(HAVE_ATTR_SETF)
674     int myflags = 0;
675     char *attrname = strchr(name,'.') + 1;
676
677     if (strncmp(name, "system", 6) == 0) myflags |= ATTR_ROOT;
678     if (flags & XATTR_CREATE) myflags |= ATTR_CREATE;
679     if (flags & XATTR_REPLACE) myflags |= ATTR_REPLACE;
680
681     return attr_setf(filedes, attrname, (const char *)value, size, myflags);
682 #elif defined(HAVE_ATTROPEN)
683     int ret = -1;
684     int myflags = O_RDWR | O_XATTR;
685     int attrfd;
686     if (flags & XATTR_CREATE) myflags |= O_EXCL;
687     if (!(flags & XATTR_REPLACE)) myflags |= O_CREAT;
688     attrfd = solaris_openat(filedes, name, myflags, (mode_t) SOLARIS_ATTRMODE);
689     if (attrfd >= 0) {
690         ret = solaris_write_xattr(attrfd, value, size);
691         close(attrfd);
692     }
693     return ret;
694 #else
695     errno = ENOSYS;
696     return -1;
697 #endif
698 }
699
700 int sys_lsetxattr (const char *path, const char *uname, const void *value, size_t size, int flags)
701 {
702         const char *name = prefix(uname);
703 #if defined(HAVE_LSETXATTR)
704         return lsetxattr(path, name, value, size, flags);
705 #elif defined(HAVE_SETXATTR) && defined(XATTR_ADD_OPT)
706         int options = XATTR_NOFOLLOW;
707         return setxattr(path, name, value, size, 0, options);
708 #elif defined(LSETEA)
709         return lsetea(path, name, value, size, flags);
710 #elif defined(HAVE_EXTATTR_SET_LINK)
711         int retval = 0;
712         if (flags) {
713                 /* Check attribute existence */
714                 retval = extattr_get_link(path, EXTATTR_NAMESPACE_USER, uname, NULL, 0);
715                 if (retval < 0) {
716                         /* REPLACE attribute, that doesn't exist */
717                         if (flags & XATTR_REPLACE && errno == ENOATTR) {
718                                 errno = ENOATTR;
719                                 return -1;
720                         }
721                         /* Ignore other errors */
722                 }
723                 else {
724                         /* CREATE attribute, that already exists */
725                         if (flags & XATTR_CREATE) {
726                                 errno = EEXIST;
727                                 return -1;
728                         }
729                 }
730         }
731
732         retval = extattr_set_link(path, EXTATTR_NAMESPACE_USER, uname, value, size);
733         return (retval < 0) ? -1 : 0;
734 #elif defined(HAVE_ATTR_SET)
735         int myflags = ATTR_DONTFOLLOW;
736         char *attrname = strchr(name,'.') + 1;
737         
738         if (strncmp(name, "system", 6) == 0) myflags |= ATTR_ROOT;
739         if (flags & XATTR_CREATE) myflags |= ATTR_CREATE;
740         if (flags & XATTR_REPLACE) myflags |= ATTR_REPLACE;
741
742         return attr_set(path, attrname, (const char *)value, size, myflags);
743 #elif defined(HAVE_ATTROPEN)
744         int ret = -1;
745         int myflags = O_RDWR | AT_SYMLINK_NOFOLLOW;
746         int attrfd;
747         if (flags & XATTR_CREATE) myflags |= O_EXCL;
748         if (!(flags & XATTR_REPLACE)) myflags |= O_CREAT;
749         attrfd = solaris_attropen(path, name, myflags, (mode_t) SOLARIS_ATTRMODE);
750         if (attrfd >= 0) {
751                 ret = solaris_write_xattr(attrfd, value, size);
752                 close(attrfd);
753         }
754         return ret;
755 #else
756         errno = ENOSYS;
757         return -1;
758 #endif
759 }
760
761 /**************************************************************************
762  helper functions for Solaris' EA support
763 ****************************************************************************/
764 #ifdef HAVE_ATTROPEN
765 static ssize_t solaris_read_xattr(int attrfd, void *value, size_t size)
766 {
767         struct stat sbuf;
768
769         if (fstat(attrfd, &sbuf) == -1) {
770                 return -1;
771         }
772
773         /* This is to return the current size of the named extended attribute */
774         if (size == 0) {
775                 return sbuf.st_size;
776         }
777
778         /* check size and read xattr */
779         if (sbuf.st_size > size) {
780                 return -1;
781         }
782
783         return read(attrfd, value, sbuf.st_size);
784 }
785
786 static ssize_t solaris_list_xattr(int attrdirfd, char *list, size_t size)
787 {
788         ssize_t len = 0;
789         DIR *dirp;
790         struct dirent *de;
791         int newfd = dup(attrdirfd);
792         /* CAUTION: The originating file descriptor should not be
793                     used again following the call to fdopendir().
794                     For that reason we dup() the file descriptor
795                     here to make things more clear. */
796         dirp = fdopendir(newfd);
797
798         while ((de = readdir(dirp))) {
799                 size_t listlen;
800                 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..") ||
801                      !strcmp(de->d_name, "SUNWattr_ro") || !strcmp(de->d_name, "SUNWattr_rw")) 
802                 {
803                         /* we don't want "." and ".." here: */
804                         LOG(log_maxdebug, logtype_default, "skipped EA %s\n",de->d_name);
805                         continue;
806                 }
807
808                 listlen = strlen(de->d_name);
809                 if (size == 0) {
810                         /* return the current size of the list of extended attribute names*/
811                         len += listlen + 1;
812                 } else {
813                         /* check size and copy entry + nul into list. */
814                         if ((len + listlen + 1) > size) {
815                                 errno = ERANGE;
816                                 len = -1;
817                                 break;
818                         } else {
819                                 strcpy(list + len, de->d_name);
820                                 len += listlen;
821                                 list[len] = '\0';
822                                 ++len;
823                         }
824                 }
825         }
826
827         if (closedir(dirp) == -1) {
828                 LOG(log_error, logtype_default, "closedir dirp: %s",strerror(errno));
829                 return -1;
830         }
831         return len;
832 }
833
834 static int solaris_unlinkat(int attrdirfd, const char *name)
835 {
836         if (unlinkat(attrdirfd, name, 0) == -1) {
837                 return -1;
838         }
839         return 0;
840 }
841
842 static int solaris_attropen(const char *path, const char *attrpath, int oflag, mode_t mode)
843 {
844         int filedes = attropen(path, attrpath, oflag, mode);
845         if (filedes == -1) {
846         if (errno != ENOENT)
847             LOG(log_error, logtype_default, "attropen(\"%s\", ea:'%s'): %s",
848                 path, attrpath, strerror(errno));
849         errno = ENOATTR;
850         }
851         return filedes;
852 }
853
854 static int solaris_openat(int fildes, const char *path, int oflag, mode_t mode)
855 {
856         int filedes = openat(fildes, path, oflag, mode);
857         if (filedes == -1) {
858                 LOG(log_error, logtype_default, "openat(\"%s\"): %s",
859             path, strerror(errno));
860         }
861         return filedes;
862 }
863
864 static int solaris_write_xattr(int attrfd, const char *value, size_t size)
865 {
866         if ((ftruncate(attrfd, 0) == 0) && (write(attrfd, value, size) == size)) {
867                 return 0;
868         } else {
869                 LOG(log_error, logtype_default, "solaris_write_xattr: %s",
870             strerror(errno));
871                 return -1;
872         }
873 }
874
875 #endif /*HAVE_ATTROPEN*/
876