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