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