s4:kdc: Implement KDC plugin hardware authentication policy
[samba.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 extended 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 recursively 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 recursively 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, len;
214         size_t t;
215         char *buf;
216         /* Iterate through extattr(2) namespaces */
217         for(t = 0; t < ARRAY_SIZE(extattr); t++) {
218                 if (t != EXTATTR_NAMESPACE_USER && geteuid() != 0) {
219                         /* ignore all but user namespace when we are not root, see bug 10247 */
220                         continue;
221                 }
222                 switch(type) {
223                         case 0:
224                                 list_size = extattr_list_file(arg.path, extattr[t].space, list, size);
225                                 break;
226                         case 1:
227                                 list_size = extattr_list_link(arg.path, extattr[t].space, list, size);
228                                 break;
229                         case 2:
230                                 list_size = extattr_list_fd(arg.filedes, extattr[t].space, list, size);
231                                 break;
232                         default:
233                                 errno = ENOSYS;
234                                 return -1;
235                 }
236                 /* Some error happened. Errno should be set by the previous call */
237                 if(list_size < 0)
238                         return -1;
239                 /* No attributes */
240                 if(list_size == 0)
241                         continue;
242                 /* XXX: Call with an empty buffer may be used to calculate
243                    necessary buffer size. Unfortunately, we can't say, how
244                    many attributes were returned, so here is the potential
245                    problem with the emulation.
246                 */
247                 if(list == NULL) {
248                         /* Take the worse case of one char attribute names -
249                            two bytes per name plus one more for sanity.
250                         */
251                         total_size += list_size + (list_size/2 + 1)*extattr[t].len;
252                         continue;
253                 }
254                 /* Count necessary offset to fit namespace prefixes */
255                 len = 0;
256                 for(i = 0; i < list_size; i += list[i] + 1)
257                         len += extattr[t].len;
258
259                 total_size += list_size + len;
260                 /* Buffer is too small to fit the results */
261                 if(total_size > size) {
262                         errno = ERANGE;
263                         return -1;
264                 }
265                 /* Shift results back, so we can prepend prefixes */
266                 buf = (char *)memmove(list + len, list, list_size);
267
268                 for(i = 0; i < list_size; i += len + 1) {
269                         len = buf[i];
270
271                         /*
272                          * If for some reason we receive a truncated
273                          * return from call to list xattrs the pascal
274                          * string lengths will not be changed and
275                          * therefore we must check that we're not
276                          * reading garbage data or off end of array
277                          */
278                         if (len + i >= list_size) {
279                                 errno = ERANGE;
280                                 return -1;
281                         }
282                         strncpy(list, extattr[t].name, extattr[t].len + 1);
283                         list += extattr[t].len;
284                         strncpy(list, buf + i + 1, len);
285                         list[len] = '\0';
286                         list += len + 1;
287                 }
288                 size -= total_size;
289         }
290         return total_size;
291 }
292
293 #endif
294
295 #if defined(HAVE_XATTR_ATTR) && (defined(HAVE_SYS_ATTRIBUTES_H) || defined(HAVE_ATTR_ATTRIBUTES_H))
296 static char attr_buffer[ATTR_MAX_VALUELEN];
297
298 static ssize_t irix_attr_list(const char *path, int filedes, char *list, size_t size, int flags)
299 {
300         int retval = 0, index;
301         attrlist_cursor_t *cursor = 0;
302         int total_size = 0;
303         attrlist_t * al = (attrlist_t *)attr_buffer;
304         attrlist_ent_t *ae;
305         size_t ent_size, left = size;
306         char *bp = list;
307
308         while (true) {
309             if (filedes)
310                 retval = attr_listf(filedes, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
311             else
312                 retval = attr_list(path, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
313             if (retval) break;
314             for (index = 0; index < al->al_count; index++) {
315                 ae = ATTR_ENTRY(attr_buffer, index);
316                 ent_size = strlen(ae->a_name) + sizeof("user.");
317                 if (left >= ent_size) {
318                     strncpy(bp, "user.", sizeof("user."));
319                     strncat(bp, ae->a_name, ent_size - sizeof("user."));
320                     bp += ent_size;
321                     left -= ent_size;
322                 } else if (size) {
323                     errno = ERANGE;
324                     retval = -1;
325                     break;
326                 }
327                 total_size += ent_size;
328             }
329             if (al->al_more == 0) break;
330         }
331         if (retval == 0) {
332             flags |= ATTR_ROOT;
333             cursor = 0;
334             while (true) {
335                 if (filedes)
336                     retval = attr_listf(filedes, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
337                 else
338                     retval = attr_list(path, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
339                 if (retval) break;
340                 for (index = 0; index < al->al_count; index++) {
341                     ae = ATTR_ENTRY(attr_buffer, index);
342                     ent_size = strlen(ae->a_name) + sizeof("system.");
343                     if (left >= ent_size) {
344                         strncpy(bp, "system.", sizeof("system."));
345                         strncat(bp, ae->a_name, ent_size - sizeof("system."));
346                         bp += ent_size;
347                         left -= ent_size;
348                     } else if (size) {
349                         errno = ERANGE;
350                         retval = -1;
351                         break;
352                     }
353                     total_size += ent_size;
354                 }
355                 if (al->al_more == 0) break;
356             }
357         }
358         return (ssize_t)(retval ? retval : total_size);
359 }
360
361 #endif
362
363 ssize_t rep_listxattr (const char *path, char *list, size_t size)
364 {
365 #if defined(HAVE_XATTR_XATTR)
366 #ifndef XATTR_ADDITIONAL_OPTIONS
367         return listxattr(path, list, size);
368 #else
369 /* So that we do not recursively call this function */
370 #undef listxattr
371         int options = 0;
372         return listxattr(path, list, size, options);
373 #endif
374 #elif defined(HAVE_XATTR_EA)
375         return listea(path, list, size);
376 #elif defined(HAVE_XATTR_EXTATTR)
377         extattr_arg arg;
378         arg.path = path;
379         return bsd_attr_list(0, arg, list, size);
380 #elif defined(HAVE_XATTR_ATTR) && defined(HAVE_SYS_ATTRIBUTES_H)
381         return irix_attr_list(path, 0, list, size, 0);
382 #elif defined(HAVE_ATTROPEN)
383         ssize_t ret = -1;
384         int attrdirfd = solaris_attropen(path, ".", O_RDONLY, 0);
385         if (attrdirfd >= 0) {
386                 ret = solaris_list_xattr(attrdirfd, list, size);
387                 close(attrdirfd);
388         }
389         return ret;
390 #else
391         errno = ENOSYS;
392         return -1;
393 #endif
394 }
395
396 ssize_t rep_flistxattr (int filedes, char *list, size_t size)
397 {
398 #if defined(HAVE_XATTR_XATTR)
399 #ifndef XATTR_ADDITIONAL_OPTIONS
400         return flistxattr(filedes, list, size);
401 #else
402 /* So that we do not recursively call this function */
403 #undef flistxattr
404         int options = 0;
405         return flistxattr(filedes, list, size, options);
406 #endif
407 #elif defined(HAVE_XATTR_EA)
408         return flistea(filedes, list, size);
409 #elif defined(HAVE_XATTR_EXTATTR)
410         extattr_arg arg;
411         arg.filedes = filedes;
412         return bsd_attr_list(2, arg, list, size);
413 #elif defined(HAVE_XATTR_ATTR)
414         return irix_attr_list(NULL, filedes, list, size, 0);
415 #elif defined(HAVE_ATTROPEN)
416         ssize_t ret = -1;
417         int attrdirfd = solaris_openat(filedes, ".", O_RDONLY|O_XATTR, 0);
418         if (attrdirfd >= 0) {
419                 ret = solaris_list_xattr(attrdirfd, list, size);
420                 close(attrdirfd);
421         }
422         return ret;
423 #else
424         errno = ENOSYS;
425         return -1;
426 #endif
427 }
428
429 int rep_removexattr (const char *path, const char *name)
430 {
431 #if defined(HAVE_XATTR_XATTR)
432 #ifndef XATTR_ADDITIONAL_OPTIONS
433         return removexattr(path, name);
434 #else
435 /* So that we do not recursively call this function */
436 #undef removexattr
437         int options = 0;
438         return removexattr(path, name, options);
439 #endif
440 #elif defined(HAVE_XATTR_EA)
441         return removeea(path, name);
442 #elif defined(HAVE_XATTR_EXTATTR)
443         int attrnamespace;
444         const char *attrname;
445
446         if (strncmp(name, "system.", 7) == 0) {
447                 attrnamespace = EXTATTR_NAMESPACE_SYSTEM;
448                 attrname = name + 7;
449         } else if (strncmp(name, "user.", 5) == 0) {
450                 attrnamespace = EXTATTR_NAMESPACE_USER;
451                 attrname = name + 5;
452         } else {
453                 errno = EINVAL;
454                 return -1;
455         }
456
457         return extattr_delete_file(path, attrnamespace, attrname);
458 #elif defined(HAVE_XATTR_ATTR)
459         int flags = 0;
460         char *attrname = strchr(name,'.') + 1;
461
462         if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
463
464         return attr_remove(path, attrname, flags);
465 #elif defined(HAVE_ATTROPEN)
466         int ret = -1;
467         int attrdirfd = solaris_attropen(path, ".", O_RDONLY, 0);
468         if (attrdirfd >= 0) {
469                 ret = solaris_unlinkat(attrdirfd, name);
470                 close(attrdirfd);
471         }
472         return ret;
473 #else
474         errno = ENOSYS;
475         return -1;
476 #endif
477 }
478
479 int rep_fremovexattr (int filedes, const char *name)
480 {
481 #if defined(HAVE_XATTR_XATTR)
482 #ifndef XATTR_ADDITIONAL_OPTIONS
483         return fremovexattr(filedes, name);
484 #else
485 /* So that we do not recursively call this function */
486 #undef fremovexattr
487         int options = 0;
488         return fremovexattr(filedes, name, options);
489 #endif
490 #elif defined(HAVE_XATTR_EA)
491         return fremoveea(filedes, name);
492 #elif defined(HAVE_XATTR_EXTATTR)
493         int attrnamespace;
494         const char *attrname;
495
496         if (strncmp(name, "system.", 7) == 0) {
497                 attrnamespace = EXTATTR_NAMESPACE_SYSTEM;
498                 attrname = name + 7;
499         } else if (strncmp(name, "user.", 5) == 0) {
500                 attrnamespace = EXTATTR_NAMESPACE_USER;
501                 attrname = name + 5;
502         } else {
503                 errno = EINVAL;
504                 return -1;
505         }
506
507         return extattr_delete_fd(filedes, attrnamespace, attrname);
508 #elif defined(HAVE_XATTR_ATTR)
509         int flags = 0;
510         char *attrname = strchr(name,'.') + 1;
511
512         if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
513
514         return attr_removef(filedes, attrname, flags);
515 #elif defined(HAVE_ATTROPEN)
516         int ret = -1;
517         int attrdirfd = solaris_openat(filedes, ".", O_RDONLY|O_XATTR, 0);
518         if (attrdirfd >= 0) {
519                 ret = solaris_unlinkat(attrdirfd, name);
520                 close(attrdirfd);
521         }
522         return ret;
523 #else
524         errno = ENOSYS;
525         return -1;
526 #endif
527 }
528
529 int rep_setxattr (const char *path, const char *name, const void *value, size_t size, int flags)
530 {
531         int retval = -1;
532 #if defined(HAVE_XATTR_XATTR)
533 #ifndef XATTR_ADDITIONAL_OPTIONS
534         retval = setxattr(path, name, value, size, flags);
535         if (retval < 0) {
536                 if (errno == ENOSPC || errno == E2BIG) {
537                         errno = ENAMETOOLONG;
538                 }
539         }
540         return retval;
541 #else
542 /* So that we do not recursively call this function */
543 #undef setxattr
544         retval = setxattr(path, name, value, size, 0, flags);
545         if (retval < 0) {
546                 if (errno == E2BIG) {
547                         errno = ENAMETOOLONG;
548                 }
549         }
550         return retval;
551 #endif
552 #elif defined(HAVE_XATTR_EA)
553         if (flags) {
554                 retval = getea(path, name, NULL, 0);
555                 if (retval < 0) {
556                         if (flags & XATTR_REPLACE && errno == ENOATTR) {
557                                 return -1;
558                         }
559                 } else {
560                         if (flags & XATTR_CREATE) {
561                                 errno = EEXIST;
562                                 return -1;
563                         }
564                 }
565         }
566         retval = setea(path, name, value, size, 0);
567         return retval;
568 #elif defined(HAVE_XATTR_EXTATTR)
569         int attrnamespace;
570         const char *attrname;
571
572         if (strncmp(name, "system.", 7) == 0) {
573                 attrnamespace = EXTATTR_NAMESPACE_SYSTEM;
574                 attrname = name + 7;
575         } else if (strncmp(name, "user.", 5) == 0) {
576                 attrnamespace = EXTATTR_NAMESPACE_USER;
577                 attrname = name + 5;
578         } else {
579                 errno = EINVAL;
580                 return -1;
581         }
582
583         if (flags) {
584                 /* Check attribute existence */
585                 retval = extattr_get_file(path, attrnamespace, attrname, NULL, 0);
586                 if (retval < 0) {
587                         /* REPLACE attribute, that doesn't exist */
588                         if (flags & XATTR_REPLACE && errno == ENOATTR) {
589                                 errno = ENOATTR;
590                                 return -1;
591                         }
592                         /* Ignore other errors */
593                 }
594                 else {
595                         /* CREATE attribute, that already exists */
596                         if (flags & XATTR_CREATE) {
597                                 errno = EEXIST;
598                                 return -1;
599                         }
600                 }
601         }
602         retval = extattr_set_file(path, attrnamespace, attrname, value, size);
603         return (retval < 0) ? -1 : 0;
604 #elif defined(HAVE_XATTR_ATTR)
605         int myflags = 0;
606         char *attrname = strchr(name,'.') + 1;
607
608         if (strncmp(name, "system", 6) == 0) myflags |= ATTR_ROOT;
609         if (flags & XATTR_CREATE) myflags |= ATTR_CREATE;
610         if (flags & XATTR_REPLACE) myflags |= ATTR_REPLACE;
611
612         retval = attr_set(path, attrname, (const char *)value, size, myflags);
613         if (retval < 0) {
614                 if (errno == E2BIG) {
615                         errno = ENAMETOOLONG;
616                 }
617         }
618         return retval;
619 #elif defined(HAVE_ATTROPEN)
620         int myflags = O_RDWR;
621         int attrfd;
622         if (flags & XATTR_CREATE) myflags |= O_EXCL;
623         if (!(flags & XATTR_REPLACE)) myflags |= O_CREAT;
624         attrfd = solaris_attropen(path, name, myflags, (mode_t) SOLARIS_ATTRMODE);
625         if (attrfd >= 0) {
626                 retval = solaris_write_xattr(attrfd, value, size);
627                 close(attrfd);
628         }
629         return retval;
630 #else
631         errno = ENOSYS;
632         return -1;
633 #endif
634 }
635
636 int rep_fsetxattr (int filedes, const char *name, const void *value, size_t size, int flags)
637 {
638         int retval = -1;
639 #if defined(HAVE_XATTR_XATTR)
640 #ifndef XATTR_ADDITIONAL_OPTIONS
641         retval = fsetxattr(filedes, name, value, size, flags);
642         if (retval < 0) {
643                 if (errno == ENOSPC) {
644                         errno = ENAMETOOLONG;
645                 }
646         }
647         return retval;
648 #else
649 /* So that we do not recursively call this function */
650 #undef fsetxattr
651         retval = fsetxattr(filedes, name, value, size, 0, flags);
652         if (retval < 0) {
653                 if (errno == E2BIG) {
654                         errno = ENAMETOOLONG;
655                 }
656         }
657         return retval;
658 #endif
659 #elif defined(HAVE_XATTR_EA)
660         if (flags) {
661                 retval = fgetea(filedes, name, NULL, 0);
662                 if (retval < 0) {
663                         if (flags & XATTR_REPLACE && errno == ENOATTR) {
664                                 return -1;
665                         }
666                 } else {
667                         if (flags & XATTR_CREATE) {
668                                 errno = EEXIST;
669                                 return -1;
670                         }
671                 }
672         }
673         retval = fsetea(filedes, name, value, size, 0);
674         return retval;
675 #elif defined(HAVE_XATTR_EXTATTR)
676         int attrnamespace;
677         const char *attrname;
678
679         if (strncmp(name, "system.", 7) == 0) {
680                 attrnamespace = EXTATTR_NAMESPACE_SYSTEM;
681                 attrname = name + 7;
682         } else if (strncmp(name, "user.", 5) == 0) {
683                 attrnamespace = EXTATTR_NAMESPACE_USER;
684                 attrname = name + 5;
685         } else {
686                 errno = EINVAL;
687                 return -1;
688         }
689
690         if (flags) {
691                 /* Check attribute existence */
692                 retval = extattr_get_fd(filedes, attrnamespace, attrname, NULL, 0);
693                 if (retval < 0) {
694                         /* REPLACE attribute, that doesn't exist */
695                         if (flags & XATTR_REPLACE && errno == ENOATTR) {
696                                 errno = ENOATTR;
697                                 return -1;
698                         }
699                         /* Ignore other errors */
700                 }
701                 else {
702                         /* CREATE attribute, that already exists */
703                         if (flags & XATTR_CREATE) {
704                                 errno = EEXIST;
705                                 return -1;
706                         }
707                 }
708         }
709         retval = extattr_set_fd(filedes, attrnamespace, attrname, value, size);
710         return (retval < 0) ? -1 : 0;
711 #elif defined(HAVE_XATTR_ATTR)
712         int myflags = 0;
713         char *attrname = strchr(name,'.') + 1;
714
715         if (strncmp(name, "system", 6) == 0) myflags |= ATTR_ROOT;
716         if (flags & XATTR_CREATE) myflags |= ATTR_CREATE;
717         if (flags & XATTR_REPLACE) myflags |= ATTR_REPLACE;
718
719         return attr_setf(filedes, attrname, (const char *)value, size, myflags);
720 #elif defined(HAVE_ATTROPEN)
721         int myflags = O_RDWR | O_XATTR;
722         int attrfd;
723         if (flags & XATTR_CREATE) myflags |= O_EXCL;
724         if (!(flags & XATTR_REPLACE)) myflags |= O_CREAT;
725         attrfd = solaris_openat(filedes, name, myflags, (mode_t) SOLARIS_ATTRMODE);
726         if (attrfd >= 0) {
727                 retval = solaris_write_xattr(attrfd, value, size);
728                 close(attrfd);
729         }
730         return retval;
731 #else
732         errno = ENOSYS;
733         return -1;
734 #endif
735 }
736
737 /**************************************************************************
738  helper functions for Solaris' EA support
739 ****************************************************************************/
740 #ifdef HAVE_ATTROPEN
741 static ssize_t solaris_read_xattr(int attrfd, void *value, size_t size)
742 {
743         struct stat sbuf;
744
745         if (fstat(attrfd, &sbuf) == -1) {
746                 errno = ENOATTR;
747                 return -1;
748         }
749
750         /* This is to return the current size of the named extended attribute */
751         if (size == 0) {
752                 return sbuf.st_size;
753         }
754
755         /* check size and read xattr */
756         if (sbuf.st_size > size) {
757                 errno = ERANGE;
758                 return -1;
759         }
760
761         return read(attrfd, value, sbuf.st_size);
762 }
763
764 static ssize_t solaris_list_xattr(int attrdirfd, char *list, size_t size)
765 {
766         ssize_t len = 0;
767         DIR *dirp;
768         struct dirent *de;
769         int newfd = dup(attrdirfd);
770         /* CAUTION: The originating file descriptor should not be
771                     used again following the call to fdopendir().
772                     For that reason we dup() the file descriptor
773                     here to make things more clear. */
774         dirp = fdopendir(newfd);
775
776         while ((de = readdir(dirp))) {
777                 size_t listlen = strlen(de->d_name) + 1;
778                 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) {
779                         /* we don't want "." and ".." here: */
780                         continue;
781                 }
782
783                 if (size == 0) {
784                         /* return the current size of the list of extended attribute names*/
785                         len += listlen;
786                 } else {
787                         /* check size and copy entrieŃ• + nul into list. */
788                         if ((len + listlen) > size) {
789                                 errno = ERANGE;
790                                 len = -1;
791                                 break;
792                         } else {
793                                 strlcpy(list + len, de->d_name, listlen);
794                                 len += listlen;
795                         }
796                 }
797         }
798
799         if (closedir(dirp) == -1) {
800                 return -1;
801         }
802         return len;
803 }
804
805 static int solaris_unlinkat(int attrdirfd, const char *name)
806 {
807         if (unlinkat(attrdirfd, name, 0) == -1) {
808                 if (errno == ENOENT) {
809                         errno = ENOATTR;
810                 }
811                 return -1;
812         }
813         return 0;
814 }
815
816 static int solaris_attropen(const char *path, const char *attrpath, int oflag, mode_t mode)
817 {
818         int filedes = attropen(path, attrpath, 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_openat(int fildes, const char *path, int oflag, mode_t mode)
830 {
831         int filedes = openat(fildes, path, oflag, mode);
832         if (filedes == -1) {
833                 if (errno == EINVAL) {
834                         errno = ENOTSUP;
835                 } else {
836                         errno = ENOATTR;
837                 }
838         }
839         return filedes;
840 }
841
842 static int solaris_write_xattr(int attrfd, const char *value, size_t size)
843 {
844         if ((ftruncate(attrfd, 0) == 0) && (write(attrfd, value, size) == size)) {
845                 return 0;
846         } else {
847                 return -1;
848         }
849 }
850 #endif /*HAVE_ATTROPEN*/
851
852