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