d07dd2cc1e352111a3e1c1273dacb5cf8ca814c6
[gd/samba-autobuild/.git] / lib / replace / xattr.c
1 /* 
2    Unix SMB/CIFS implementation.
3    replacement routines for xattr implementations
4    Copyright (C) Jeremy Allison  1998-2005
5    Copyright (C) Timur Bakeyev        2005
6    Copyright (C) Bjoern Jacke    2006-2007
7    Copyright (C) Herb Lewis           2003
8    Copyright (C) Andrew Bartlett      2012
9
10      ** NOTE! The following LGPL license applies to the replace
11      ** library. This does NOT imply that all of Samba is released
12      ** under the LGPL
13    
14    This library is free software; you can redistribute it and/or
15    modify it under the terms of the GNU Lesser General Public
16    License as published by the Free Software Foundation; either
17    version 3 of the License, or (at your option) any later version.
18
19    This library is distributed in the hope that it will be useful,
20    but WITHOUT ANY WARRANTY; without even the implied warranty of
21    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
22    Lesser General Public License for more details.
23
24    You should have received a copy of the GNU Lesser General Public
25    License along with this library; if not, see <http://www.gnu.org/licenses/>.
26 */
27
28 #define UID_WRAPPER_NOT_REPLACE
29 #include "replace.h"
30 #include "system/filesys.h"
31 #include "system/dir.h"
32
33 /******** Solaris EA helper function prototypes ********/
34 #ifdef HAVE_ATTROPEN
35 #define SOLARIS_ATTRMODE S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP
36 static int solaris_write_xattr(int attrfd, const char *value, size_t size);
37 static ssize_t solaris_read_xattr(int attrfd, void *value, size_t size);
38 static ssize_t solaris_list_xattr(int attrdirfd, char *list, size_t size);
39 static int solaris_unlinkat(int attrdirfd, const char *name);
40 static int solaris_attropen(const char *path, const char *attrpath, int oflag, mode_t mode);
41 static int solaris_openat(int fildes, const char *path, int oflag, mode_t mode);
42 #endif
43
44 /**************************************************************************
45  Wrappers for extented attribute calls. Based on the Linux package with
46  support for IRIX and (Net|Free)BSD also. Expand as other systems have them.
47 ****************************************************************************/
48
49 ssize_t rep_getxattr (const char *path, const char *name, void *value, size_t size)
50 {
51 #if defined(HAVE_XATTR_XATTR)
52 #ifndef XATTR_ADDITIONAL_OPTIONS
53         return getxattr(path, name, value, size);
54 #else
55
56 /* So that we do not recursivly call this function */
57 #undef getxattr
58         int options = 0;
59         return getxattr(path, name, value, size, 0, options);
60 #endif
61 #elif defined(HAVE_XATTR_EA)
62         return getea(path, name, value, size);
63 #elif defined(HAVE_XATTR_EXTATTR)
64         ssize_t retval;
65         int attrnamespace;
66         const char *attrname;
67
68         if (strncmp(name, "system.", 7) == 0) {
69                 attrnamespace = EXTATTR_NAMESPACE_SYSTEM;
70                 attrname = name + 7;
71         } else if (strncmp(name, "user.", 5) == 0) {
72                 attrnamespace = EXTATTR_NAMESPACE_USER;
73                 attrname = name + 5;
74         } else {
75                 errno = EINVAL;
76                 return -1;
77         }
78
79         /*
80          * The BSD implementation has a nasty habit of silently truncating
81          * the returned value to the size of the buffer, so we have to check
82          * that the buffer is large enough to fit the returned value.
83          */
84         if((retval=extattr_get_file(path, attrnamespace, attrname, NULL, 0)) >= 0) {
85                 if (size == 0) {
86                         return retval;
87                 } else if (retval > size) {
88                         errno = ERANGE;
89                         return -1;
90                 }
91                 if((retval=extattr_get_file(path, attrnamespace, attrname, value, size)) >= 0)
92                         return retval;
93         }
94
95         return -1;
96 #elif defined(HAVE_XATTR_ATTR)
97         int retval, flags = 0;
98         int valuelength = (int)size;
99         char *attrname = strchr(name,'.') + 1;
100
101         if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
102
103         retval = attr_get(path, attrname, (char *)value, &valuelength, flags);
104         if (size == 0 && retval == -1 && errno == E2BIG) {
105                 return valuelength;
106         }
107
108         return retval ? retval : valuelength;
109 #elif defined(HAVE_ATTROPEN)
110         ssize_t ret = -1;
111         int attrfd = solaris_attropen(path, name, O_RDONLY, 0);
112         if (attrfd >= 0) {
113                 ret = solaris_read_xattr(attrfd, value, size);
114                 close(attrfd);
115         }
116         return ret;
117 #else
118         errno = ENOSYS;
119         return -1;
120 #endif
121 }
122
123 ssize_t rep_fgetxattr (int filedes, const char *name, void *value, size_t size)
124 {
125 #if defined(HAVE_XATTR_XATTR)
126 #ifndef XATTR_ADDITIONAL_OPTIONS
127         return fgetxattr(filedes, name, value, size);
128 #else
129
130 /* So that we do not recursivly call this function */
131 #undef fgetxattr
132         int options = 0;
133         return fgetxattr(filedes, name, value, size, 0, options);
134 #endif
135 #elif defined(HAVE_XATTR_EA)
136         return fgetea(filedes, name, value, size);
137 #elif defined(HAVE_XATTR_EXTATTR)
138         ssize_t retval;
139         int attrnamespace;
140         const char *attrname;
141
142         if (strncmp(name, "system.", 7) == 0) {
143                 attrnamespace = EXTATTR_NAMESPACE_SYSTEM;
144                 attrname = name + 7;
145         } else if (strncmp(name, "user.", 5) == 0) {
146                 attrnamespace = EXTATTR_NAMESPACE_USER;
147                 attrname = name + 5;
148         } else {
149                 errno = EINVAL;
150                 return -1;
151         }
152
153         if((retval=extattr_get_fd(filedes, attrnamespace, attrname, NULL, 0)) >= 0) {
154                 if (size == 0) {
155                         return retval;
156                 } else if (retval > size) {
157                         errno = ERANGE;
158                         return -1;
159                 }
160                 if((retval=extattr_get_fd(filedes, attrnamespace, attrname, value, size)) >= 0)
161                         return retval;
162         }
163
164         return -1;
165 #elif defined(HAVE_XATTR_ATTR)
166         int retval, flags = 0;
167         int valuelength = (int)size;
168         char *attrname = strchr(name,'.') + 1;
169
170         if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
171
172         retval = attr_getf(filedes, attrname, (char *)value, &valuelength, flags);
173         if (size == 0 && retval == -1 && errno == E2BIG) {
174                 return valuelength;
175         }
176         return retval ? retval : valuelength;
177 #elif defined(HAVE_ATTROPEN)
178         ssize_t ret = -1;
179         int attrfd = solaris_openat(filedes, name, O_RDONLY|O_XATTR, 0);
180         if (attrfd >= 0) {
181                 ret = solaris_read_xattr(attrfd, value, size);
182                 close(attrfd);
183         }
184         return ret;
185 #else
186         errno = ENOSYS;
187         return -1;
188 #endif
189 }
190
191 #if defined(HAVE_XATTR_EXTATTR)
192
193 #define EXTATTR_PREFIX(s)       (s), (sizeof((s))-1)
194
195 static struct {
196         int space;
197         const char *name;
198         size_t len;
199
200 extattr[] = {
201         { EXTATTR_NAMESPACE_SYSTEM, EXTATTR_PREFIX("system.") },
202         { EXTATTR_NAMESPACE_USER, EXTATTR_PREFIX("user.") },
203 };
204
205 typedef union {
206         const char *path;
207         int filedes;
208 } extattr_arg;
209
210 static ssize_t bsd_attr_list (int type, extattr_arg arg, char *list, size_t size)
211 {
212         ssize_t list_size, total_size = 0;
213         int i, t, len;
214         char *buf;
215         /* Iterate through extattr(2) namespaces */
216         for(t = 0; t < ARRAY_SIZE(extattr); t++) {
217                 if (t != EXTATTR_NAMESPACE_USER && geteuid() != 0) {
218                         /* ignore all but user namespace when we are not root, see bug 10247 */
219                         continue;
220                 }
221                 switch(type) {
222                         case 0:
223                                 list_size = extattr_list_file(arg.path, extattr[t].space, list, size);
224                                 break;
225                         case 1:
226                                 list_size = extattr_list_link(arg.path, extattr[t].space, list, size);
227                                 break;
228                         case 2:
229                                 list_size = extattr_list_fd(arg.filedes, extattr[t].space, list, size);
230                                 break;
231                         default:
232                                 errno = ENOSYS;
233                                 return -1;
234                 }
235                 /* Some error happend. Errno should be set by the previous call */
236                 if(list_size < 0)
237                         return -1;
238                 /* No attributes */
239                 if(list_size == 0)
240                         continue;
241                 /* XXX: Call with an empty buffer may be used to calculate
242                    necessary buffer size. Unfortunately, we can't say, how
243                    many attributes were returned, so here is the potential
244                    problem with the emulation.
245                 */
246                 if(list == NULL) {
247                         /* Take the worse case of one char attribute names - 
248                            two bytes per name plus one more for sanity.
249                         */
250                         total_size += list_size + (list_size/2 + 1)*extattr[t].len;
251                         continue;
252                 }
253                 /* Count necessary offset to fit namespace prefixes */
254                 len = 0;
255                 for(i = 0; i < list_size; i += list[i] + 1)
256                         len += extattr[t].len;
257
258                 total_size += list_size + len;
259                 /* Buffer is too small to fit the results */
260                 if(total_size > size) {
261                         errno = ERANGE;
262                         return -1;
263                 }
264                 /* Shift results back, so we can prepend prefixes */
265                 buf = (char *)memmove(list + len, list, list_size);
266
267                 for(i = 0; i < list_size; i += len + 1) {
268                         len = buf[i];
269                         strncpy(list, extattr[t].name, extattr[t].len + 1);
270                         list += extattr[t].len;
271                         strncpy(list, buf + i + 1, len);
272                         list[len] = '\0';
273                         list += len + 1;
274                 }
275                 size -= total_size;
276         }
277         return total_size;
278 }
279
280 #endif
281
282 #if defined(HAVE_XATTR_ATTR) && (defined(HAVE_SYS_ATTRIBUTES_H) || defined(HAVE_ATTR_ATTRIBUTES_H))
283 static char attr_buffer[ATTR_MAX_VALUELEN];
284
285 static ssize_t irix_attr_list(const char *path, int filedes, char *list, size_t size, int flags)
286 {
287         int retval = 0, index;
288         attrlist_cursor_t *cursor = 0;
289         int total_size = 0;
290         attrlist_t * al = (attrlist_t *)attr_buffer;
291         attrlist_ent_t *ae;
292         size_t ent_size, left = size;
293         char *bp = list;
294
295         while (true) {
296             if (filedes)
297                 retval = attr_listf(filedes, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
298             else
299                 retval = attr_list(path, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
300             if (retval) break;
301             for (index = 0; index < al->al_count; index++) {
302                 ae = ATTR_ENTRY(attr_buffer, index);
303                 ent_size = strlen(ae->a_name) + sizeof("user.");
304                 if (left >= ent_size) {
305                     strncpy(bp, "user.", sizeof("user."));
306                     strncat(bp, ae->a_name, ent_size - sizeof("user."));
307                     bp += ent_size;
308                     left -= ent_size;
309                 } else if (size) {
310                     errno = ERANGE;
311                     retval = -1;
312                     break;
313                 }
314                 total_size += ent_size;
315             }
316             if (al->al_more == 0) break;
317         }
318         if (retval == 0) {
319             flags |= ATTR_ROOT;
320             cursor = 0;
321             while (true) {
322                 if (filedes)
323                     retval = attr_listf(filedes, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
324                 else
325                     retval = attr_list(path, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
326                 if (retval) break;
327                 for (index = 0; index < al->al_count; index++) {
328                     ae = ATTR_ENTRY(attr_buffer, index);
329                     ent_size = strlen(ae->a_name) + sizeof("system.");
330                     if (left >= ent_size) {
331                         strncpy(bp, "system.", sizeof("system."));
332                         strncat(bp, ae->a_name, ent_size - sizeof("system."));
333                         bp += ent_size;
334                         left -= ent_size;
335                     } else if (size) {
336                         errno = ERANGE;
337                         retval = -1;
338                         break;
339                     }
340                     total_size += ent_size;
341                 }
342                 if (al->al_more == 0) break;
343             }
344         }
345         return (ssize_t)(retval ? retval : total_size);
346 }
347
348 #endif
349
350 ssize_t rep_listxattr (const char *path, char *list, size_t size)
351 {
352 #if defined(HAVE_XATTR_XATTR)
353 #ifndef XATTR_ADDITIONAL_OPTIONS
354         return listxattr(path, list, size);
355 #else
356 /* So that we do not recursivly call this function */
357 #undef listxattr
358         int options = 0;
359         return listxattr(path, list, size, options);
360 #endif
361 #elif defined(HAVE_XATTR_EA)
362         return listea(path, list, size);
363 #elif defined(HAVE_XATTR_EXTATTR)
364         extattr_arg arg;
365         arg.path = path;
366         return bsd_attr_list(0, arg, list, size);
367 #elif defined(HAVE_XATTR_ATTR) && defined(HAVE_SYS_ATTRIBUTES_H)
368         return irix_attr_list(path, 0, list, size, 0);
369 #elif defined(HAVE_ATTROPEN)
370         ssize_t ret = -1;
371         int attrdirfd = solaris_attropen(path, ".", O_RDONLY, 0);
372         if (attrdirfd >= 0) {
373                 ret = solaris_list_xattr(attrdirfd, list, size);
374                 close(attrdirfd);
375         }
376         return ret;
377 #else
378         errno = ENOSYS;
379         return -1;
380 #endif
381 }
382
383 ssize_t rep_flistxattr (int filedes, char *list, size_t size)
384 {
385 #if defined(HAVE_XATTR_XATTR)
386 #ifndef XATTR_ADDITIONAL_OPTIONS
387         return flistxattr(filedes, list, size);
388 #else
389 /* So that we do not recursivly call this function */
390 #undef flistxattr
391         int options = 0;
392         return flistxattr(filedes, list, size, options);
393 #endif
394 #elif defined(HAVE_XATTR_EA)
395         return flistea(filedes, list, size);
396 #elif defined(HAVE_XATTR_EXTATTR)
397         extattr_arg arg;
398         arg.filedes = filedes;
399         return bsd_attr_list(2, arg, list, size);
400 #elif defined(HAVE_XATTR_ATTR)
401         return irix_attr_list(NULL, filedes, list, size, 0);
402 #elif defined(HAVE_ATTROPEN)
403         ssize_t ret = -1;
404         int attrdirfd = solaris_openat(filedes, ".", O_RDONLY|O_XATTR, 0);
405         if (attrdirfd >= 0) {
406                 ret = solaris_list_xattr(attrdirfd, list, size);
407                 close(attrdirfd);
408         }
409         return ret;
410 #else
411         errno = ENOSYS;
412         return -1;
413 #endif
414 }
415
416 int rep_removexattr (const char *path, const char *name)
417 {
418 #if defined(HAVE_XATTR_XATTR)
419 #ifndef XATTR_ADDITIONAL_OPTIONS
420         return removexattr(path, name);
421 #else
422 /* So that we do not recursivly call this function */
423 #undef removexattr
424         int options = 0;
425         return removexattr(path, name, options);
426 #endif
427 #elif defined(HAVE_XATTR_EA)
428         return removeea(path, name);
429 #elif defined(HAVE_XATTR_EXTATTR)
430         int attrnamespace;
431         const char *attrname;
432
433         if (strncmp(name, "system.", 7) == 0) {
434                 attrnamespace = EXTATTR_NAMESPACE_SYSTEM;
435                 attrname = name + 7;
436         } else if (strncmp(name, "user.", 5) == 0) {
437                 attrnamespace = EXTATTR_NAMESPACE_USER;
438                 attrname = name + 5;
439         } else {
440                 errno = EINVAL;
441                 return -1;
442         }
443
444         return extattr_delete_file(path, attrnamespace, attrname);
445 #elif defined(HAVE_XATTR_ATTR)
446         int flags = 0;
447         char *attrname = strchr(name,'.') + 1;
448
449         if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
450
451         return attr_remove(path, attrname, flags);
452 #elif defined(HAVE_ATTROPEN)
453         int ret = -1;
454         int attrdirfd = solaris_attropen(path, ".", O_RDONLY, 0);
455         if (attrdirfd >= 0) {
456                 ret = solaris_unlinkat(attrdirfd, name);
457                 close(attrdirfd);
458         }
459         return ret;
460 #else
461         errno = ENOSYS;
462         return -1;
463 #endif
464 }
465
466 int rep_fremovexattr (int filedes, const char *name)
467 {
468 #if defined(HAVE_XATTR_XATTR)
469 #ifndef XATTR_ADDITIONAL_OPTIONS
470         return fremovexattr(filedes, name);
471 #else
472 /* So that we do not recursivly call this function */
473 #undef fremovexattr
474         int options = 0;
475         return fremovexattr(filedes, name, options);
476 #endif
477 #elif defined(HAVE_XATTR_EA)
478         return fremoveea(filedes, name);
479 #elif defined(HAVE_XATTR_EXTATTR)
480         int attrnamespace;
481         const char *attrname;
482
483         if (strncmp(name, "system.", 7) == 0) {
484                 attrnamespace = EXTATTR_NAMESPACE_SYSTEM;
485                 attrname = name + 7;
486         } else if (strncmp(name, "user.", 5) == 0) {
487                 attrnamespace = EXTATTR_NAMESPACE_USER;
488                 attrname = name + 5;
489         } else {
490                 errno = EINVAL;
491                 return -1;
492         }
493
494         return extattr_delete_fd(filedes, attrnamespace, attrname);
495 #elif defined(HAVE_XATTR_ATTR)
496         int flags = 0;
497         char *attrname = strchr(name,'.') + 1;
498
499         if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
500
501         return attr_removef(filedes, attrname, flags);
502 #elif defined(HAVE_ATTROPEN)
503         int ret = -1;
504         int attrdirfd = solaris_openat(filedes, ".", O_RDONLY|O_XATTR, 0);
505         if (attrdirfd >= 0) {
506                 ret = solaris_unlinkat(attrdirfd, name);
507                 close(attrdirfd);
508         }
509         return ret;
510 #else
511         errno = ENOSYS;
512         return -1;
513 #endif
514 }
515
516 int rep_setxattr (const char *path, const char *name, const void *value, size_t size, int flags)
517 {
518         int retval = -1;
519 #if defined(HAVE_XATTR_XATTR)
520 #ifndef XATTR_ADDITIONAL_OPTIONS
521         retval = setxattr(path, name, value, size, flags);
522         if (retval < 0) {
523                 if (errno == ENOSPC || errno == E2BIG) {
524                         errno = ENAMETOOLONG;
525                 }
526         }
527         return retval;
528 #else
529 /* So that we do not recursivly call this function */
530 #undef setxattr
531         retval = setxattr(path, name, value, size, 0, flags);
532         if (retval < 0) {
533                 if (errno == E2BIG) {
534                         errno = ENAMETOOLONG;
535                 }
536         }
537         return retval;
538 #endif
539 #elif defined(HAVE_XATTR_EA)
540         if (flags) {
541                 retval = getea(path, name, NULL, 0);
542                 if (retval < 0) {
543                         if (flags & XATTR_REPLACE && errno == ENOATTR) {
544                                 return -1;
545                         }
546                 } else {
547                         if (flags & XATTR_CREATE) {
548                                 errno = EEXIST;
549                                 return -1;
550                         }
551                 }
552         }
553         retval = setea(path, name, value, size, 0);
554         return retval;
555 #elif defined(HAVE_XATTR_EXTATTR)
556         int attrnamespace;
557         const char *attrname;
558
559         if (strncmp(name, "system.", 7) == 0) {
560                 attrnamespace = EXTATTR_NAMESPACE_SYSTEM;
561                 attrname = name + 7;
562         } else if (strncmp(name, "user.", 5) == 0) {
563                 attrnamespace = EXTATTR_NAMESPACE_USER;
564                 attrname = name + 5;
565         } else {
566                 errno = EINVAL;
567                 return -1;
568         }
569
570         if (flags) {
571                 /* Check attribute existence */
572                 retval = extattr_get_file(path, attrnamespace, attrname, NULL, 0);
573                 if (retval < 0) {
574                         /* REPLACE attribute, that doesn't exist */
575                         if (flags & XATTR_REPLACE && errno == ENOATTR) {
576                                 errno = ENOATTR;
577                                 return -1;
578                         }
579                         /* Ignore other errors */
580                 }
581                 else {
582                         /* CREATE attribute, that already exists */
583                         if (flags & XATTR_CREATE) {
584                                 errno = EEXIST;
585                                 return -1;
586                         }
587                 }
588         }
589         retval = extattr_set_file(path, attrnamespace, attrname, value, size);
590         return (retval < 0) ? -1 : 0;
591 #elif defined(HAVE_XATTR_ATTR)
592         int myflags = 0;
593         char *attrname = strchr(name,'.') + 1;
594
595         if (strncmp(name, "system", 6) == 0) myflags |= ATTR_ROOT;
596         if (flags & XATTR_CREATE) myflags |= ATTR_CREATE;
597         if (flags & XATTR_REPLACE) myflags |= ATTR_REPLACE;
598
599         retval = attr_set(path, attrname, (const char *)value, size, myflags);
600         if (retval < 0) {
601                 if (errno == E2BIG) {
602                         errno = ENAMETOOLONG;
603                 }
604         }
605         return retval;
606 #elif defined(HAVE_ATTROPEN)
607         int myflags = O_RDWR;
608         int attrfd;
609         if (flags & XATTR_CREATE) myflags |= O_EXCL;
610         if (!(flags & XATTR_REPLACE)) myflags |= O_CREAT;
611         attrfd = solaris_attropen(path, name, myflags, (mode_t) SOLARIS_ATTRMODE);
612         if (attrfd >= 0) {
613                 retval = solaris_write_xattr(attrfd, value, size);
614                 close(attrfd);
615         }
616         return retval;
617 #else
618         errno = ENOSYS;
619         return -1;
620 #endif
621 }
622
623 int rep_fsetxattr (int filedes, const char *name, const void *value, size_t size, int flags)
624 {
625         int retval = -1;
626 #if defined(HAVE_XATTR_XATTR)
627 #ifndef XATTR_ADDITIONAL_OPTIONS
628         retval = fsetxattr(filedes, name, value, size, flags);
629         if (retval < 0) {
630                 if (errno == ENOSPC) {
631                         errno = ENAMETOOLONG;
632                 }
633         }
634         return retval;
635 #else
636 /* So that we do not recursivly call this function */
637 #undef fsetxattr
638         retval = fsetxattr(filedes, name, value, size, 0, flags);
639         if (retval < 0) {
640                 if (errno == E2BIG) {
641                         errno = ENAMETOOLONG;
642                 }
643         }
644         return retval;
645 #endif
646 #elif defined(HAVE_XATTR_EA)
647         if (flags) {
648                 retval = fgetea(filedes, name, NULL, 0);
649                 if (retval < 0) {
650                         if (flags & XATTR_REPLACE && errno == ENOATTR) {
651                                 return -1;
652                         }
653                 } else {
654                         if (flags & XATTR_CREATE) {
655                                 errno = EEXIST;
656                                 return -1;
657                         }
658                 }
659         }
660         retval = fsetea(filedes, name, value, size, 0);
661         return retval;
662 #elif defined(HAVE_XATTR_EXTATTR)
663         int attrnamespace;
664         const char *attrname;
665
666         if (strncmp(name, "system.", 7) == 0) {
667                 attrnamespace = EXTATTR_NAMESPACE_SYSTEM;
668                 attrname = name + 7;
669         } else if (strncmp(name, "user.", 5) == 0) {
670                 attrnamespace = EXTATTR_NAMESPACE_USER;
671                 attrname = name + 5;
672         } else {
673                 errno = EINVAL;
674                 return -1;
675         }
676
677         if (flags) {
678                 /* Check attribute existence */
679                 retval = extattr_get_fd(filedes, attrnamespace, attrname, NULL, 0);
680                 if (retval < 0) {
681                         /* REPLACE attribute, that doesn't exist */
682                         if (flags & XATTR_REPLACE && errno == ENOATTR) {
683                                 errno = ENOATTR;
684                                 return -1;
685                         }
686                         /* Ignore other errors */
687                 }
688                 else {
689                         /* CREATE attribute, that already exists */
690                         if (flags & XATTR_CREATE) {
691                                 errno = EEXIST;
692                                 return -1;
693                         }
694                 }
695         }
696         retval = extattr_set_fd(filedes, attrnamespace, attrname, value, size);
697         return (retval < 0) ? -1 : 0;
698 #elif defined(HAVE_XATTR_ATTR)
699         int myflags = 0;
700         char *attrname = strchr(name,'.') + 1;
701
702         if (strncmp(name, "system", 6) == 0) myflags |= ATTR_ROOT;
703         if (flags & XATTR_CREATE) myflags |= ATTR_CREATE;
704         if (flags & XATTR_REPLACE) myflags |= ATTR_REPLACE;
705
706         return attr_setf(filedes, attrname, (const char *)value, size, myflags);
707 #elif defined(HAVE_ATTROPEN)
708         int myflags = O_RDWR | O_XATTR;
709         int attrfd;
710         if (flags & XATTR_CREATE) myflags |= O_EXCL;
711         if (!(flags & XATTR_REPLACE)) myflags |= O_CREAT;
712         attrfd = solaris_openat(filedes, name, myflags, (mode_t) SOLARIS_ATTRMODE);
713         if (attrfd >= 0) {
714                 retval = solaris_write_xattr(attrfd, value, size);
715                 close(attrfd);
716         }
717         return retval;
718 #else
719         errno = ENOSYS;
720         return -1;
721 #endif
722 }
723
724 /**************************************************************************
725  helper functions for Solaris' EA support
726 ****************************************************************************/
727 #ifdef HAVE_ATTROPEN
728 static ssize_t solaris_read_xattr(int attrfd, void *value, size_t size)
729 {
730         struct stat sbuf;
731
732         if (fstat(attrfd, &sbuf) == -1) {
733                 errno = ENOATTR;
734                 return -1;
735         }
736
737         /* This is to return the current size of the named extended attribute */
738         if (size == 0) {
739                 return sbuf.st_size;
740         }
741
742         /* check size and read xattr */
743         if (sbuf.st_size > size) {
744                 errno = ERANGE;
745                 return -1;
746         }
747
748         return read(attrfd, value, sbuf.st_size);
749 }
750
751 static ssize_t solaris_list_xattr(int attrdirfd, char *list, size_t size)
752 {
753         ssize_t len = 0;
754         DIR *dirp;
755         struct dirent *de;
756         int newfd = dup(attrdirfd);
757         /* CAUTION: The originating file descriptor should not be
758                     used again following the call to fdopendir().
759                     For that reason we dup() the file descriptor
760                     here to make things more clear. */
761         dirp = fdopendir(newfd);
762
763         while ((de = readdir(dirp))) {
764                 size_t listlen = strlen(de->d_name) + 1;
765                 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) {
766                         /* we don't want "." and ".." here: */
767                         continue;
768                 }
769
770                 if (size == 0) {
771                         /* return the current size of the list of extended attribute names*/
772                         len += listlen;
773                 } else {
774                         /* check size and copy entrieŃ• + nul into list. */
775                         if ((len + listlen) > size) {
776                                 errno = ERANGE;
777                                 len = -1;
778                                 break;
779                         } else {
780                                 strlcpy(list + len, de->d_name, listlen);
781                                 len += listlen;
782                         }
783                 }
784         }
785
786         if (closedir(dirp) == -1) {
787                 return -1;
788         }
789         return len;
790 }
791
792 static int solaris_unlinkat(int attrdirfd, const char *name)
793 {
794         if (unlinkat(attrdirfd, name, 0) == -1) {
795                 if (errno == ENOENT) {
796                         errno = ENOATTR;
797                 }
798                 return -1;
799         }
800         return 0;
801 }
802
803 static int solaris_attropen(const char *path, const char *attrpath, int oflag, mode_t mode)
804 {
805         int filedes = attropen(path, attrpath, oflag, mode);
806         if (filedes == -1) {
807                 if (errno == EINVAL) {
808                         errno = ENOTSUP;
809                 } else {
810                         errno = ENOATTR;
811                 }
812         }
813         return filedes;
814 }
815
816 static int solaris_openat(int fildes, const char *path, int oflag, mode_t mode)
817 {
818         int filedes = openat(fildes, path, oflag, mode);
819         if (filedes == -1) {
820                 if (errno == EINVAL) {
821                         errno = ENOTSUP;
822                 } else {
823                         errno = ENOATTR;
824                 }
825         }
826         return filedes;
827 }
828
829 static int solaris_write_xattr(int attrfd, const char *value, size_t size)
830 {
831         if ((ftruncate(attrfd, 0) == 0) && (write(attrfd, value, size) == size)) {
832                 return 0;
833         } else {
834                 return -1;
835         }
836 }
837 #endif /*HAVE_ATTROPEN*/
838
839