]> arthur.barton.de Git - netatalk.git/blob - libatalk/vfs/sys_ea.c
copy native EA in afp_copyfile, doesn't work if copy between two volumes with differe...
[netatalk.git] / libatalk / vfs / sys_ea.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    $Id: sys_ea.c,v 1.2 2009-11-18 11:14:59 didg Exp $
29    
30 */
31
32 #ifdef HAVE_CONFIG_H
33 #include "config.h"
34 #endif
35
36 #include <stdlib.h>
37 #include <string.h>
38 #include <sys/types.h>
39 #include <errno.h>
40
41 #if HAVE_ATTR_XATTR_H
42 #include <attr/xattr.h>
43 #elif HAVE_SYS_XATTR_H
44 #include <sys/xattr.h>
45 #endif
46
47 #ifdef HAVE_SYS_EA_H
48 #include <sys/ea.h>
49 #endif
50
51 #ifdef HAVE_ATTROPEN
52
53 #include <dirent.h>
54 #endif
55
56 #ifdef HAVE_SYS_EXTATTR_H
57 #include <sys/extattr.h>
58 #endif
59
60 #include <atalk/adouble.h>
61 #include <atalk/util.h>
62 #include <atalk/logger.h>
63 #include <atalk/ea.h>
64
65 /******** Solaris EA helper function prototypes ********/
66 #ifdef HAVE_ATTROPEN
67 #define SOLARIS_ATTRMODE S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP
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 ssize_t sys_getxattr (const char *path, const char *uname, void *value, size_t size)
93 {
94         const char *name = prefix(uname);
95
96 #if defined(HAVE_GETXATTR)
97 #ifndef XATTR_ADD_OPT
98         return getxattr(path, name, value, size);
99 #else
100         int options = 0;
101         return getxattr(path, name, value, size, 0, options);
102 #endif
103 #elif defined(HAVE_GETEA)
104         return getea(path, name, value, size);
105 #elif defined(HAVE_EXTATTR_GET_FILE)
106         char *s;
107         ssize_t retval;
108         int attrnamespace = (strncmp(name, "system", 6) == 0) ? 
109                 EXTATTR_NAMESPACE_SYSTEM : EXTATTR_NAMESPACE_USER;
110         const char *attrname = ((s=strchr_m(name, '.')) == NULL) ? name : s + 1;
111         /*
112          * The BSD implementation has a nasty habit of silently truncating
113          * the returned value to the size of the buffer, so we have to check
114          * that the buffer is large enough to fit the returned value.
115          */
116         if((retval=extattr_get_file(path, attrnamespace, attrname, NULL, 0)) >= 0) {
117                 if(retval > size) {
118                         errno = ERANGE;
119                         return -1;
120                 }
121                 if((retval=extattr_get_file(path, attrnamespace, attrname, value, size)) >= 0)
122                         return retval;
123         }
124
125         LOG(log_maxdebug, logtype_default, "sys_getxattr: extattr_get_file() failed with: %s\n", strerror(errno));
126         return -1;
127 #elif defined(HAVE_ATTR_GET)
128         int retval, flags = 0;
129         int valuelength = (int)size;
130         char *attrname = strchr(name,'.') + 1;
131         
132         if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
133
134         retval = attr_get(path, attrname, (char *)value, &valuelength, flags);
135
136         return retval ? retval : valuelength;
137 #elif defined(HAVE_ATTROPEN)
138         ssize_t ret = -1;
139         int attrfd = solaris_attropen(path, name, O_RDONLY, 0);
140         if (attrfd >= 0) {
141                 ret = solaris_read_xattr(attrfd, value, size);
142                 close(attrfd);
143         }
144         return ret;
145 #else
146         errno = ENOSYS;
147         return -1;
148 #endif
149 }
150
151 ssize_t sys_lgetxattr (const char *path, const char *uname, void *value, size_t size)
152 {
153         const char *name = prefix(uname);
154
155 #if defined(HAVE_LGETXATTR)
156         return lgetxattr(path, name, value, size);
157 #elif defined(HAVE_GETXATTR) && defined(XATTR_ADD_OPT)
158         int options = XATTR_NOFOLLOW;
159         return getxattr(path, name, value, size, 0, options);
160 #elif defined(HAVE_LGETEA)
161         return lgetea(path, name, value, size);
162 #elif defined(HAVE_EXTATTR_GET_LINK)
163         char *s;
164         ssize_t retval;
165         int attrnamespace = (strncmp(name, "system", 6) == 0) ? 
166                 EXTATTR_NAMESPACE_SYSTEM : EXTATTR_NAMESPACE_USER;
167         const char *attrname = ((s=strchr_m(name, '.')) == NULL) ? name : s + 1;
168
169         if((retval=extattr_get_link(path, attrnamespace, attrname, NULL, 0)) >= 0) {
170                 if(retval > size) {
171                         errno = ERANGE;
172                         return -1;
173                 }
174                 if((retval=extattr_get_link(path, attrnamespace, attrname, value, size)) >= 0)
175                         return retval;
176         }
177         
178         LOG(log_maxdebug, logtype_default, "sys_lgetxattr: extattr_get_link() failed with: %s\n", strerror(errno));
179         return -1;
180 #elif defined(HAVE_ATTR_GET)
181         int retval, flags = ATTR_DONTFOLLOW;
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_get(path, 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_attropen(path, name, O_RDONLY|AT_SYMLINK_NOFOLLOW, 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 #if defined(HAVE_EXTATTR_LIST_FILE)
205
206 #define EXTATTR_PREFIX(s)       (s), (sizeof((s))-1)
207
208 static struct {
209         int space;
210         const char *name;
211         size_t len;
212
213 extattr[] = {
214         { EXTATTR_NAMESPACE_SYSTEM, EXTATTR_PREFIX("system.") },
215         { EXTATTR_NAMESPACE_USER, EXTATTR_PREFIX("user.") },
216 };
217
218 typedef union {
219         const char *path;
220         int filedes;
221 } extattr_arg;
222
223 static ssize_t bsd_attr_list (int type, extattr_arg arg, char *list, size_t size)
224 {
225         ssize_t list_size, total_size = 0;
226         int i, t, len;
227         char *buf;
228         /* Iterate through extattr(2) namespaces */
229         for(t = 0; t < (sizeof(extattr)/sizeof(extattr[0])); t++) {
230                 switch(type) {
231 #if defined(HAVE_EXTATTR_LIST_FILE)
232                         case 0:
233                                 list_size = extattr_list_file(arg.path, extattr[t].space, list, size);
234                                 break;
235 #endif
236 #if defined(HAVE_EXTATTR_LIST_LINK)
237                         case 1:
238                                 list_size = extattr_list_link(arg.path, extattr[t].space, list, size);
239                                 break;
240 #endif
241 #if defined(HAVE_EXTATTR_LIST_FD)
242                         case 2:
243                                 list_size = extattr_list_fd(arg.filedes, extattr[t].space, list, size);
244                                 break;
245 #endif
246                         default:
247                                 errno = ENOSYS;
248                                 return -1;
249                 }
250                 /* Some error happend. Errno should be set by the previous call */
251                 if(list_size < 0)
252                         return -1;
253                 /* No attributes */
254                 if(list_size == 0)
255                         continue;
256                 /* XXX: Call with an empty buffer may be used to calculate
257                    necessary buffer size. Unfortunately, we can't say, how
258                    many attributes were returned, so here is the potential
259                    problem with the emulation.
260                 */
261                 if(list == NULL) {
262                         /* Take the worse case of one char attribute names - 
263                            two bytes per name plus one more for sanity.
264                         */
265                         total_size += list_size + (list_size/2 + 1)*extattr[t].len;
266                         continue;
267                 }
268                 /* Count necessary offset to fit namespace prefixes */
269                 len = 0;
270                 for(i = 0; i < list_size; i += list[i] + 1)
271                         len += extattr[t].len;
272
273                 total_size += list_size + len;
274                 /* Buffer is too small to fit the results */
275                 if(total_size > size) {
276                         errno = ERANGE;
277                         return -1;
278                 }
279                 /* Shift results back, so we can prepend prefixes */
280                 buf = memmove(list + len, list, list_size);
281
282                 for(i = 0; i < list_size; i += len + 1) {
283                         len = buf[i];
284                         strncpy(list, extattr[t].name, extattr[t].len + 1);
285                         list += extattr[t].len;
286                         strncpy(list, buf + i + 1, len);
287                         list[len] = '\0';
288                         list += len + 1;
289                 }
290                 size -= total_size;
291         }
292         return total_size;
293 }
294
295 #endif
296
297 #if defined(HAVE_ATTR_LIST) && defined(HAVE_SYS_ATTRIBUTES_H)
298 static char attr_buffer[ATTR_MAX_VALUELEN];
299
300 static ssize_t irix_attr_list(const char *path, int filedes, char *list, size_t size, int flags)
301 {
302         int retval = 0, index;
303         attrlist_cursor_t *cursor = 0;
304         int total_size = 0;
305         attrlist_t * al = (attrlist_t *)attr_buffer;
306         attrlist_ent_t *ae;
307         size_t ent_size, left = size;
308         char *bp = list;
309
310         while (True) {
311             if (filedes)
312                 retval = attr_listf(filedes, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
313             else
314                 retval = attr_list(path, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
315             if (retval) break;
316             for (index = 0; index < al->al_count; index++) {
317                 ae = ATTR_ENTRY(attr_buffer, index);
318                 ent_size = strlen(ae->a_name) + sizeof("user.");
319                 if (left >= ent_size) {
320                     strncpy(bp, "user.", sizeof("user."));
321                     strncat(bp, ae->a_name, ent_size - sizeof("user."));
322                     bp += ent_size;
323                     left -= ent_size;
324                 } else if (size) {
325                     errno = ERANGE;
326                     retval = -1;
327                     break;
328                 }
329                 total_size += ent_size;
330             }
331             if (al->al_more == 0) break;
332         }
333         if (retval == 0) {
334             flags |= ATTR_ROOT;
335             cursor = 0;
336             while (True) {
337                 if (filedes)
338                     retval = attr_listf(filedes, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
339                 else
340                     retval = attr_list(path, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
341                 if (retval) break;
342                 for (index = 0; index < al->al_count; index++) {
343                     ae = ATTR_ENTRY(attr_buffer, index);
344                     ent_size = strlen(ae->a_name) + sizeof("system.");
345                     if (left >= ent_size) {
346                         strncpy(bp, "system.", sizeof("system."));
347                         strncat(bp, ae->a_name, ent_size - sizeof("system."));
348                         bp += ent_size;
349                         left -= ent_size;
350                     } else if (size) {
351                         errno = ERANGE;
352                         retval = -1;
353                         break;
354                     }
355                     total_size += ent_size;
356                 }
357                 if (al->al_more == 0) break;
358             }
359         }
360         return (ssize_t)(retval ? retval : total_size);
361 }
362
363 #endif
364
365 #if defined(HAVE_LISTXATTR)
366 static ssize_t remove_user(ssize_t ret, char *list, size_t size)
367 {
368         size_t len;
369         char *ptr;
370         char *ptr1;
371         ssize_t ptrsize;
372         
373         if (ret <= 0 || size == 0)
374                 return ret;
375         ptrsize = ret;
376         ptr = ptr1 = list;
377         while (ptrsize > 0) {
378                 len = strlen(ptr1) +1;
379                 ptrsize -= len;
380                 if (strncmp(ptr1, "user.",5)) {
381                         ptr1 += len;
382                         continue;
383                 }
384                 memmove(ptr, ptr1 +5, len -5);
385                 ptr += len -5;
386                 ptr1 += len;
387         }
388         return ptr -list;
389 }
390 #endif
391
392 ssize_t sys_listxattr (const char *path, char *list, size_t size)
393 {
394 #if defined(HAVE_LISTXATTR)
395         ssize_t ret;
396
397 #ifndef XATTR_ADD_OPT
398         ret = listxattr(path, list, size);
399 #else
400         int options = 0;
401         ret = listxattr(path, list, size, options);
402 #endif
403         return remove_user(ret, list, size);
404
405 #elif defined(HAVE_LISTEA)
406         return listea(path, list, size);
407 #elif defined(HAVE_EXTATTR_LIST_FILE)
408         extattr_arg arg;
409         arg.path = path;
410         return bsd_attr_list(0, arg, list, size);
411 #elif defined(HAVE_ATTR_LIST) && defined(HAVE_SYS_ATTRIBUTES_H)
412         return irix_attr_list(path, 0, list, size, 0);
413 #elif defined(HAVE_ATTROPEN)
414         ssize_t ret = -1;
415         int attrdirfd = solaris_attropen(path, ".", O_RDONLY, 0);
416         if (attrdirfd >= 0) {
417                 ret = solaris_list_xattr(attrdirfd, list, size);
418                 close(attrdirfd);
419         }
420         return ret;
421 #else
422         errno = ENOSYS;
423         return -1;
424 #endif
425 }
426
427 ssize_t sys_llistxattr (const char *path, char *list, size_t size)
428 {
429 #if defined(HAVE_LLISTXATTR)
430         ssize_t ret;
431
432         ret = llistxattr(path, list, size);
433         return remove_user(ret, list, size);
434 #elif defined(HAVE_LISTXATTR) && defined(XATTR_ADD_OPT)
435         ssize_t ret;
436         int options = XATTR_NOFOLLOW;
437
438         ret = listxattr(path, list, size, options);
439         return remove_user(ret, list, size);
440
441 #elif defined(HAVE_LLISTEA)
442         return llistea(path, list, size);
443 #elif defined(HAVE_EXTATTR_LIST_LINK)
444         extattr_arg arg;
445         arg.path = path;
446         return bsd_attr_list(1, arg, list, size);
447 #elif defined(HAVE_ATTR_LIST) && defined(HAVE_SYS_ATTRIBUTES_H)
448         return irix_attr_list(path, 0, list, size, ATTR_DONTFOLLOW);
449 #elif defined(HAVE_ATTROPEN)
450         ssize_t ret = -1;
451         int attrdirfd = solaris_attropen(path, ".", O_RDONLY|AT_SYMLINK_NOFOLLOW, 0);
452         if (attrdirfd >= 0) {
453                 ret = solaris_list_xattr(attrdirfd, list, size);
454                 close(attrdirfd);
455         }
456         return ret;
457 #else
458         errno = ENOSYS;
459         return -1;
460 #endif
461 }
462
463 int sys_removexattr (const char *path, const char *uname)
464 {
465         const char *name = prefix(uname);
466 #if defined(HAVE_REMOVEXATTR)
467 #ifndef XATTR_ADD_OPT
468         return removexattr(path, name);
469 #else
470         int options = 0;
471         return removexattr(path, name, options);
472 #endif
473 #elif defined(HAVE_REMOVEEA)
474         return removeea(path, name);
475 #elif defined(HAVE_EXTATTR_DELETE_FILE)
476         char *s;
477         int attrnamespace = (strncmp(name, "system", 6) == 0) ? 
478                 EXTATTR_NAMESPACE_SYSTEM : EXTATTR_NAMESPACE_USER;
479         const char *attrname = ((s=strchr_m(name, '.')) == NULL) ? name : s + 1;
480
481         return extattr_delete_file(path, attrnamespace, attrname);
482 #elif defined(HAVE_ATTR_REMOVE)
483         int flags = 0;
484         char *attrname = strchr(name,'.') + 1;
485         
486         if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
487
488         return attr_remove(path, attrname, flags);
489 #elif defined(HAVE_ATTROPEN)
490         int ret = -1;
491         int attrdirfd = solaris_attropen(path, ".", O_RDONLY, 0);
492         if (attrdirfd >= 0) {
493                 ret = solaris_unlinkat(attrdirfd, name);
494                 close(attrdirfd);
495         }
496         return ret;
497 #else
498         errno = ENOSYS;
499         return -1;
500 #endif
501 }
502
503 int sys_lremovexattr (const char *path, const char *uname)
504 {
505         const char *name = prefix(uname);
506 #if defined(HAVE_LREMOVEXATTR)
507         return lremovexattr(path, name);
508 #elif defined(HAVE_REMOVEXATTR) && defined(XATTR_ADD_OPT)
509         int options = XATTR_NOFOLLOW;
510         return removexattr(path, name, options);
511 #elif defined(HAVE_LREMOVEEA)
512         return lremoveea(path, name);
513 #elif defined(HAVE_EXTATTR_DELETE_LINK)
514         char *s;
515         int attrnamespace = (strncmp(name, "system", 6) == 0) ? 
516                 EXTATTR_NAMESPACE_SYSTEM : EXTATTR_NAMESPACE_USER;
517         const char *attrname = ((s=strchr_m(name, '.')) == NULL) ? name : s + 1;
518
519         return extattr_delete_link(path, attrnamespace, attrname);
520 #elif defined(HAVE_ATTR_REMOVE)
521         int flags = ATTR_DONTFOLLOW;
522         char *attrname = strchr(name,'.') + 1;
523         
524         if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
525
526         return attr_remove(path, attrname, flags);
527 #elif defined(HAVE_ATTROPEN)
528         int ret = -1;
529         int attrdirfd = solaris_attropen(path, ".", O_RDONLY|AT_SYMLINK_NOFOLLOW, 0);
530         if (attrdirfd >= 0) {
531                 ret = solaris_unlinkat(attrdirfd, name);
532                 close(attrdirfd);
533         }
534         return ret;
535 #else
536         errno = ENOSYS;
537         return -1;
538 #endif
539 }
540
541 int sys_setxattr (const char *path, const char *uname, const void *value, size_t size, int flags)
542 {
543         const char *name = prefix(uname);
544 #if defined(HAVE_SETXATTR)
545 #ifndef XATTR_ADD_OPT
546         return setxattr(path, name, value, size, flags);
547 #else
548         int options = 0;
549         return setxattr(path, name, value, size, 0, options);
550 #endif
551 #elif defined(HAVE_SETEA)
552         return setea(path, name, value, size, flags);
553 #elif defined(HAVE_EXTATTR_SET_FILE)
554         char *s;
555         int retval = 0;
556         int attrnamespace = (strncmp(name, "system", 6) == 0) ? 
557                 EXTATTR_NAMESPACE_SYSTEM : EXTATTR_NAMESPACE_USER;
558         const char *attrname = ((s=strchr_m(name, '.')) == NULL) ? name : s + 1;
559         if (flags) {
560                 /* Check attribute existence */
561                 retval = extattr_get_file(path, attrnamespace, attrname, NULL, 0);
562                 if (retval < 0) {
563                         /* REPLACE attribute, that doesn't exist */
564                         if (flags & XATTR_REPLACE && errno == ENOATTR) {
565                                 errno = ENOATTR;
566                                 return -1;
567                         }
568                         /* Ignore other errors */
569                 }
570                 else {
571                         /* CREATE attribute, that already exists */
572                         if (flags & XATTR_CREATE) {
573                                 errno = EEXIST;
574                                 return -1;
575                         }
576                 }
577         }
578         retval = extattr_set_file(path, attrnamespace, attrname, value, size);
579         return (retval < 0) ? -1 : 0;
580 #elif defined(HAVE_ATTR_SET)
581         int myflags = 0;
582         char *attrname = strchr(name,'.') + 1;
583         
584         if (strncmp(name, "system", 6) == 0) myflags |= ATTR_ROOT;
585         if (flags & XATTR_CREATE) myflags |= ATTR_CREATE;
586         if (flags & XATTR_REPLACE) myflags |= ATTR_REPLACE;
587
588         return attr_set(path, attrname, (const char *)value, size, myflags);
589 #elif defined(HAVE_ATTROPEN)
590         int ret = -1;
591         int myflags = O_RDWR;
592         int attrfd;
593         if (flags & XATTR_CREATE) myflags |= O_EXCL;
594         if (!(flags & XATTR_REPLACE)) myflags |= O_CREAT;
595         attrfd = solaris_attropen(path, name, myflags, (mode_t) SOLARIS_ATTRMODE);
596         if (attrfd >= 0) {
597                 ret = solaris_write_xattr(attrfd, value, size);
598                 close(attrfd);
599         }
600         return ret;
601 #else
602         errno = ENOSYS;
603         return -1;
604 #endif
605 }
606
607 int sys_lsetxattr (const char *path, const char *uname, const void *value, size_t size, int flags)
608 {
609         const char *name = prefix(uname);
610 #if defined(HAVE_LSETXATTR)
611         return lsetxattr(path, name, value, size, flags);
612 #elif defined(HAVE_SETXATTR) && defined(XATTR_ADD_OPT)
613         int options = XATTR_NOFOLLOW;
614         return setxattr(path, name, value, size, 0, options);
615 #elif defined(LSETEA)
616         return lsetea(path, name, value, size, flags);
617 #elif defined(HAVE_EXTATTR_SET_LINK)
618         char *s;
619         int retval = 0;
620         int attrnamespace = (strncmp(name, "system", 6) == 0) ? 
621                 EXTATTR_NAMESPACE_SYSTEM : EXTATTR_NAMESPACE_USER;
622         const char *attrname = ((s=strchr_m(name, '.')) == NULL) ? name : s + 1;
623         if (flags) {
624                 /* Check attribute existence */
625                 retval = extattr_get_link(path, attrnamespace, attrname, NULL, 0);
626                 if (retval < 0) {
627                         /* REPLACE attribute, that doesn't exist */
628                         if (flags & XATTR_REPLACE && errno == ENOATTR) {
629                                 errno = ENOATTR;
630                                 return -1;
631                         }
632                         /* Ignore other errors */
633                 }
634                 else {
635                         /* CREATE attribute, that already exists */
636                         if (flags & XATTR_CREATE) {
637                                 errno = EEXIST;
638                                 return -1;
639                         }
640                 }
641         }
642
643         retval = extattr_set_link(path, attrnamespace, attrname, value, size);
644         return (retval < 0) ? -1 : 0;
645 #elif defined(HAVE_ATTR_SET)
646         int myflags = ATTR_DONTFOLLOW;
647         char *attrname = strchr(name,'.') + 1;
648         
649         if (strncmp(name, "system", 6) == 0) myflags |= ATTR_ROOT;
650         if (flags & XATTR_CREATE) myflags |= ATTR_CREATE;
651         if (flags & XATTR_REPLACE) myflags |= ATTR_REPLACE;
652
653         return attr_set(path, attrname, (const char *)value, size, myflags);
654 #elif defined(HAVE_ATTROPEN)
655         int ret = -1;
656         int myflags = O_RDWR | AT_SYMLINK_NOFOLLOW;
657         int attrfd;
658         if (flags & XATTR_CREATE) myflags |= O_EXCL;
659         if (!(flags & XATTR_REPLACE)) myflags |= O_CREAT;
660         attrfd = solaris_attropen(path, name, myflags, (mode_t) SOLARIS_ATTRMODE);
661         if (attrfd >= 0) {
662                 ret = solaris_write_xattr(attrfd, value, size);
663                 close(attrfd);
664         }
665         return ret;
666 #else
667         errno = ENOSYS;
668         return -1;
669 #endif
670 }
671
672 /* copy EA, from LGPL2.1 libattr attr_copy_file.c
673    should use fgetxattr? We don't have them but they are in Samba.
674    Or add a sys_copyfxattr? Currently it's only call by afp_copyfile so we can open
675    the both files for reading and get a fd.
676
677    Or don't use sys_xxx and copy all attributes.
678
679 */
680 int sys_copyxattr(const char *src_path, const char *dst_path)
681 {
682 #if defined(HAVE_LISTXATTR) && defined(HAVE_GETXATTR) && defined(HAVE_SETXATTR)
683         int ret = 0;
684         ssize_t size;
685         char *names = NULL, *end_names, *name, *value = NULL;
686         unsigned int setxattr_ENOTSUP = 0;
687
688         size = sys_listxattr(src_path, NULL, 0);
689         if (size < 0) {
690                 if (errno != ENOSYS && errno != ENOTSUP) {
691                         ret = -1;
692                 }
693                 goto getout;
694         }
695         names = malloc(size+1);
696         if (names == NULL) {
697                 ret = -1;
698                 goto getout;
699         }
700         size = sys_listxattr(src_path, names, size);
701         if (size < 0) {
702                 ret = -1;
703                 goto getout;
704         } else {
705                 names[size] = '\0';
706                 end_names = names + size;
707         }
708
709         for (name = names; name != end_names; name = strchr(name, '\0') + 1) {
710                 void *old_value;
711
712                 /* check if this attribute shall be preserved */
713                 if (!*name)
714                         continue;
715
716                 size = sys_getxattr (src_path, name, NULL, 0);
717                 if (size < 0) {
718                         ret = -1;
719                         continue;
720                 }
721                 value = realloc(old_value = value, size);
722                 if (size != 0 && value == NULL) {
723                         free(old_value);
724                         ret = -1;
725                 }
726                 size = sys_getxattr(src_path, name, value, size);
727                 if (size < 0) {
728                         ret = -1;
729                         continue;
730                 }
731                 if (sys_setxattr(dst_path, name, value, size, 0) != 0) {
732                         if (errno == ENOTSUP)
733                                 setxattr_ENOTSUP++;
734                         else {
735                                 if (errno == ENOSYS) {
736                                         ret = -1;
737                                         /* no hope of getting any further */
738                                         break;
739                                 } else {
740                                         ret = -1;
741                                 }
742                         }
743                 }
744         }
745         if (setxattr_ENOTSUP) {
746                 errno = ENOTSUP;
747                 ret = -1;
748         }
749 getout:
750         free(value);
751         free(names);
752         return ret;
753 #else
754         /* FIXME same for solaris */
755         return 0;
756 #endif
757 }
758
759 /**************************************************************************
760  helper functions for Solaris' EA support
761 ****************************************************************************/
762 #ifdef HAVE_ATTROPEN
763 static ssize_t solaris_read_xattr(int attrfd, void *value, size_t size)
764 {
765         struct stat sbuf;
766
767         if (fstat(attrfd, &sbuf) == -1) {
768                 return -1;
769         }
770
771         /* This is to return the current size of the named extended attribute */
772         if (size == 0) {
773                 return sbuf.st_size;
774         }
775
776         /* check size and read xattr */
777         if (sbuf.st_size > size) {
778                 return -1;
779         }
780
781         return read(attrfd, value, sbuf.st_size);
782 }
783
784 static ssize_t solaris_list_xattr(int attrdirfd, char *list, size_t size)
785 {
786         ssize_t len = 0;
787         int stop = 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\n",path,attrpath,strerror(errno));
846         }
847         return filedes;
848 }
849
850 static int solaris_openat(int fildes, const char *path, int oflag, mode_t mode)
851 {
852         int filedes = openat(fildes, path, oflag, mode);
853         if (filedes == -1) {
854                 LOG(log_maxdebug, logtype_default, "openat FAILED: fd: %s, path: %s, errno: %s\n",filedes,path,strerror(errno));
855         }
856         return filedes;
857 }
858
859 static int solaris_write_xattr(int attrfd, const char *value, size_t size)
860 {
861         if ((ftruncate(attrfd, 0) == 0) && (write(attrfd, value, size) == size)) {
862                 return 0;
863         } else {
864                 LOG(log_maxdebug, logtype_default, "solaris_write_xattr FAILED!\n");
865                 return -1;
866         }
867 }
868 #endif /*HAVE_ATTROPEN*/
869