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