1 /*
2    Unix SMB/CIFS implementation.
3    Samba system utilities
4    Copyright (C) Andrew Tridgell 1992-1998
5    Copyright (C) Jeremy Allison  1998-2005
6    Copyright (C) Timur Bakeyev        2005
7    Copyright (C) Bjoern Jacke    2006-2007
8 
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 2 of the License, or
12    (at your option) any later version.
13 
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18 
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 
23    sys_copyxattr modified from LGPL2.1 libattr copyright
24    Copyright (C) 2001-2002 Silicon Graphics, Inc.  All Rights Reserved.
25    Copyright (C) 2001 Andreas Gruenbacher.
26 
27    Samba 3.0.28, modified for netatalk.
28 */
29 
30 #ifdef HAVE_CONFIG_H
31 #include "config.h"
32 #endif
33 
34 #include <stdlib.h>
35 #include <string.h>
36 #include <sys/types.h>
37 #include <errno.h>
38 
39 #if HAVE_ATTR_XATTR_H
40 #include <attr/xattr.h>
41 #elif HAVE_SYS_XATTR_H
42 #include <sys/xattr.h>
43 #endif
44 
45 #ifdef HAVE_SYS_EA_H
46 #include <sys/ea.h>
47 #endif
48 
49 #ifdef HAVE_ATTROPEN
50 
51 #include <dirent.h>
52 #endif
53 
54 #ifdef HAVE_SYS_EXTATTR_H
55 #include <sys/extattr.h>
56 #endif
57 
58 #include <atalk/adouble.h>
59 #include <atalk/util.h>
60 #include <atalk/logger.h>
61 #include <atalk/ea.h>
62 #include <atalk/compat.h>
63 #include <atalk/errchk.h>
64 
65 /******** Solaris EA helper function prototypes ********/
66 #ifdef HAVE_ATTROPEN
67 #define SOLARIS_ATTRMODE S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH
68 static int solaris_write_xattr(int attrfd, const char *value, size_t size);
69 static ssize_t solaris_read_xattr(int attrfd, void *value, size_t size);
70 static ssize_t solaris_list_xattr(int attrdirfd, char *list, size_t size);
71 static int solaris_unlinkat(int attrdirfd, const char *name);
72 static int solaris_attropen(const char *path, const char *attrpath, int oflag, mode_t mode);
73 static int solaris_attropenat(int filedes, const char *path, const char *attrpath, int oflag, mode_t mode);
74 static int solaris_openat(int fildes, const char *path, int oflag, mode_t mode);
75 #endif
76 
77 /**************************************************************************
78  Wrappers for extented attribute calls. Based on the Linux package with
79  support for IRIX and (Net|Free)BSD also. Expand as other systems have them.
80 ****************************************************************************/
81 static char attr_name[256 +5] = "user.";
82 
prefix(const char * uname)83 static const char *prefix(const char *uname)
84 {
85 #if defined(HAVE_ATTROPEN)
86 	return uname;
87 #else
88 	strlcpy(attr_name +5, uname, 256);
89 	return attr_name;
90 #endif
91 }
92 
sys_getxattrfd(int fd,const char * uname,int oflag,...)93 int sys_getxattrfd(int fd, const char *uname, int oflag, ...)
94 {
95 #if defined HAVE_ATTROPEN
96     int eafd;
97     va_list args;
98     mode_t mode = 0;
99 
100     if (oflag & O_CREAT) {
101         va_start(args, oflag);
102         mode = va_arg(args, mode_t);
103         va_end(args);
104     }
105 
106     if (oflag & O_CREAT)
107         eafd = solaris_openat(fd, uname, oflag | O_XATTR, mode);
108     else
109         eafd = solaris_openat(fd, uname, oflag | O_XATTR, mode);
110 
111     return eafd;
112 #else
113     errno = ENOSYS;
114     return -1;
115 #endif
116 }
117 
sys_getxattr(const char * path,const char * uname,void * value,size_t size)118 ssize_t sys_getxattr (const char *path, const char *uname, void *value, size_t size)
119 {
120 	const char *name = prefix(uname);
121 
122 #if defined(HAVE_GETXATTR)
123 #ifndef XATTR_ADD_OPT
124 	return getxattr(path, name, value, size);
125 #else
126 	int options = 0;
127 	return getxattr(path, name, value, size, 0, options);
128 #endif
129 #elif defined(HAVE_GETEA)
130 	return getea(path, name, value, size);
131 #elif defined(HAVE_EXTATTR_GET_FILE)
132 	ssize_t retval;
133 	/*
134 	 * The BSD implementation has a nasty habit of silently truncating
135 	 * the returned value to the size of the buffer, so we have to check
136 	 * that the buffer is large enough to fit the returned value.
137 	 */
138 	if((retval = extattr_get_file(path, EXTATTR_NAMESPACE_USER, uname, NULL, 0)) >= 0) {
139         if (size == 0)
140             /* size == 0 means only return size */
141             return retval;
142 		if (retval > size) {
143 			errno = ERANGE;
144 			return -1;
145 		}
146 		if ((retval = extattr_get_file(path, EXTATTR_NAMESPACE_USER, uname, value, size)) >= 0)
147 			return retval;
148 	}
149 
150 	LOG(log_maxdebug, logtype_default, "sys_getxattr: extattr_get_file() failed with: %s\n", strerror(errno));
151 	return -1;
152 #elif defined(HAVE_ATTR_GET)
153 	int retval, flags = 0;
154 	int valuelength = (int)size;
155 	char *attrname = strchr(name,'.') + 1;
156 
157 	if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
158 
159 	retval = attr_get(path, attrname, (char *)value, &valuelength, flags);
160 
161 	return retval ? retval : valuelength;
162 #elif defined(HAVE_ATTROPEN)
163 	ssize_t ret = -1;
164 	int attrfd = solaris_attropen(path, name, O_RDONLY, 0);
165 	if (attrfd >= 0) {
166 		ret = solaris_read_xattr(attrfd, value, size);
167 		close(attrfd);
168 	}
169 	return ret;
170 #else
171 	errno = ENOSYS;
172 	return -1;
173 #endif
174 }
175 
sys_fgetxattr(int filedes,const char * uname,void * value,size_t size)176 ssize_t sys_fgetxattr (int filedes, const char *uname, void *value, size_t size)
177 {
178     const char *name = prefix(uname);
179 
180 #if defined(HAVE_FGETXATTR)
181 #ifndef XATTR_ADD_OPT
182     return fgetxattr(filedes, name, value, size);
183 #else
184     int options = 0;
185     return fgetxattr(filedes, name, value, size, 0, options);
186 #endif
187 #elif defined(HAVE_FGETEA)
188     return fgetea(filedes, name, value, size);
189 #elif defined(HAVE_EXTATTR_GET_FD)
190     char *s;
191     ssize_t retval;
192     int attrnamespace = (strncmp(name, "system", 6) == 0) ?
193         EXTATTR_NAMESPACE_SYSTEM : EXTATTR_NAMESPACE_USER;
194     const char *attrname = ((s=strchr(name, '.')) == NULL) ? name : s + 1;
195 
196     if((retval=extattr_get_fd(filedes, attrnamespace, attrname, NULL, 0)) >= 0) {
197         if (size == 0) {
198             /* size == 0 means only return size */
199             return retval;
200         }
201         if(retval > size) {
202             errno = ERANGE;
203             return -1;
204         }
205         if((retval=extattr_get_fd(filedes, attrnamespace, attrname, value, size)) >= 0)
206             return retval;
207     }
208 
209     LOG(log_debug, logtype_default, "sys_fgetxattr: extattr_get_fd(): %s", strerror(errno));
210     return -1;
211 #elif defined(HAVE_ATTR_GETF)
212     int retval, flags = 0;
213     int valuelength = (int)size;
214     char *attrname = strchr(name,'.') + 1;
215 
216     if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
217 
218     retval = attr_getf(filedes, attrname, (char *)value, &valuelength, flags);
219 
220     return retval ? retval : valuelength;
221 #elif defined(HAVE_ATTROPEN)
222     ssize_t ret = -1;
223     int attrfd = solaris_openat(filedes, name, O_RDONLY|O_XATTR, 0);
224     if (attrfd >= 0) {
225         ret = solaris_read_xattr(attrfd, value, size);
226         close(attrfd);
227     }
228     return ret;
229 #else
230     errno = ENOSYS;
231     return -1;
232 #endif
233 }
234 
sys_lgetxattr(const char * path,const char * uname,void * value,size_t size)235 ssize_t sys_lgetxattr (const char *path, const char *uname, void *value, size_t size)
236 {
237 	const char *name = prefix(uname);
238 
239 #if defined(HAVE_LGETXATTR)
240 	return lgetxattr(path, name, value, size);
241 #elif defined(HAVE_GETXATTR) && defined(XATTR_ADD_OPT)
242 	int options = XATTR_NOFOLLOW;
243 	return getxattr(path, name, value, size, 0, options);
244 #elif defined(HAVE_LGETEA)
245 	return lgetea(path, name, value, size);
246 #elif defined(HAVE_EXTATTR_GET_LINK)
247 	ssize_t retval;
248 
249 	retval = extattr_get_link(path, EXTATTR_NAMESPACE_USER, uname, NULL, 0);
250     if (retval == -1) {
251         LOG(log_maxdebug, logtype_default, "extattr_get_link(): %s",
252             strerror(errno));
253         return -1;
254     }
255     if (size == 0)
256         /* Only interested in size of xattr */
257         return retval;
258     if (retval > size) {
259         errno = ERANGE;
260         return -1;
261     }
262     return extattr_get_link(path, EXTATTR_NAMESPACE_USER, uname, value, size);
263 
264 #elif defined(HAVE_ATTR_GET)
265 	int retval, flags = ATTR_DONTFOLLOW;
266 	int valuelength = (int)size;
267 	char *attrname = strchr(name,'.') + 1;
268 
269 	if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
270 
271 	retval = attr_get(path, attrname, (char *)value, &valuelength, flags);
272 
273 	return retval ? retval : valuelength;
274 #elif defined(HAVE_ATTROPEN)
275 	ssize_t ret = -1;
276 	int attrfd = solaris_attropen(path, name, O_RDONLY | O_NOFOLLOW, 0);
277 	if (attrfd >= 0) {
278 		ret = solaris_read_xattr(attrfd, value, size);
279 		close(attrfd);
280 	}
281 	return ret;
282 #else
283 	errno = ENOSYS;
284 	return -1;
285 #endif
286 }
287 
288 #if defined(HAVE_EXTATTR_LIST_FILE)
289 
290 #define EXTATTR_PREFIX(s)	(s), (sizeof((s))-1)
291 
292 static struct {
293         int space;
294 	const char *name;
295 	size_t len;
296 }
297 extattr[] = {
298 	{ EXTATTR_NAMESPACE_SYSTEM, EXTATTR_PREFIX("") },
299         { EXTATTR_NAMESPACE_USER, EXTATTR_PREFIX("") },
300 };
301 
302 typedef union {
303 	const char *path;
304 	int filedes;
305 } extattr_arg;
306 
bsd_attr_list(int type,extattr_arg arg,char * list,size_t size)307 static ssize_t bsd_attr_list (int type, extattr_arg arg, char *list, size_t size)
308 {
309 	ssize_t list_size;
310 	int i, len;
311 
312     switch(type) {
313 #if defined(HAVE_EXTATTR_LIST_FILE)
314     case 0:
315         list_size = extattr_list_file(arg.path, EXTATTR_NAMESPACE_USER, list, size);
316         break;
317 #endif
318 #if defined(HAVE_EXTATTR_LIST_LINK)
319     case 1:
320         list_size = extattr_list_link(arg.path, EXTATTR_NAMESPACE_USER, list, size);
321         break;
322 #endif
323 #if defined(HAVE_EXTATTR_LIST_FD)
324     case 2:
325         list_size = extattr_list_fd(arg.filedes, EXTATTR_NAMESPACE_USER, list, size);
326         break;
327 #endif
328     default:
329         errno = ENOSYS;
330         return -1;
331     }
332 
333     /* Some error happend. Errno should be set by the previous call */
334     if(list_size < 0)
335         return -1;
336 
337     /* No attributes */
338     if(list_size == 0)
339         return 0;
340 
341     /* XXX: Call with an empty buffer may be used to calculate
342        necessary buffer size. Unfortunately, we can't say, how
343        many attributes were returned, so here is the potential
344        problem with the emulation.
345     */
346     if(list == NULL)
347         return list_size;
348 
349     /* Buffer is too small to fit the results */
350     if(list_size > size) {
351         errno = ERANGE;
352         return -1;
353     }
354 
355     /* Convert from pascal strings to C strings */
356     len = (unsigned char)list[0];
357     memmove(list, list + 1, list_size - 1);
358 
359     for(i = len; i < list_size; ) {
360         LOG(log_maxdebug, logtype_afpd, "len: %d, i: %d", len, i);
361 
362         len = (unsigned char)list[i];
363         list[i] = '\0';
364         i += len + 1;
365     }
366 
367 	return list_size;
368 }
369 
370 #endif
371 
372 #if defined(HAVE_ATTR_LIST) && defined(HAVE_SYS_ATTRIBUTES_H)
373 static char attr_buffer[ATTR_MAX_VALUELEN];
374 
irix_attr_list(const char * path,int filedes,char * list,size_t size,int flags)375 static ssize_t irix_attr_list(const char *path, int filedes, char *list, size_t size, int flags)
376 {
377 	int retval = 0, index;
378 	attrlist_cursor_t *cursor = 0;
379 	int total_size = 0;
380 	attrlist_t * al = (attrlist_t *)attr_buffer;
381 	attrlist_ent_t *ae;
382 	size_t ent_size, left = size;
383 	char *bp = list;
384 
385 	while (True) {
386 	    if (filedes)
387 		retval = attr_listf(filedes, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
388 	    else
389 		retval = attr_list(path, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
390 	    if (retval) break;
391 	    for (index = 0; index < al->al_count; index++) {
392 		ae = ATTR_ENTRY(attr_buffer, index);
393 		ent_size = strlen(ae->a_name) + sizeof("user.");
394 		if (left >= ent_size) {
395 		    strncpy(bp, "user.", sizeof("user."));
396 		    strncat(bp, ae->a_name, ent_size - sizeof("user."));
397 		    bp += ent_size;
398 		    left -= ent_size;
399 		} else if (size) {
400 		    errno = ERANGE;
401 		    retval = -1;
402 		    break;
403 		}
404 		total_size += ent_size;
405 	    }
406 	    if (al->al_more == 0) break;
407 	}
408 	if (retval == 0) {
409 	    flags |= ATTR_ROOT;
410 	    cursor = 0;
411 	    while (True) {
412 		if (filedes)
413 		    retval = attr_listf(filedes, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
414 		else
415 		    retval = attr_list(path, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
416 		if (retval) break;
417 		for (index = 0; index < al->al_count; index++) {
418 		    ae = ATTR_ENTRY(attr_buffer, index);
419 		    ent_size = strlen(ae->a_name) + sizeof("system.");
420 		    if (left >= ent_size) {
421 			strncpy(bp, "system.", sizeof("system."));
422 			strncat(bp, ae->a_name, ent_size - sizeof("system."));
423 			bp += ent_size;
424 			left -= ent_size;
425 		    } else if (size) {
426 			errno = ERANGE;
427 			retval = -1;
428 			break;
429 		    }
430 		    total_size += ent_size;
431 		}
432 		if (al->al_more == 0) break;
433 	    }
434 	}
435 	return (ssize_t)(retval ? retval : total_size);
436 }
437 
438 #endif
439 
440 #if defined(HAVE_LISTXATTR)
remove_user(ssize_t ret,char * list,size_t size)441 static ssize_t remove_user(ssize_t ret, char *list, size_t size)
442 {
443 	size_t len;
444 	char *ptr;
445 	char *ptr1;
446 	ssize_t ptrsize;
447 
448 	if (ret <= 0 || size == 0)
449 		return ret;
450 	ptrsize = ret;
451 	ptr = ptr1 = list;
452 	while (ptrsize > 0) {
453 		len = strlen(ptr1) +1;
454 		ptrsize -= len;
455 		if (strncmp(ptr1, "user.",5)) {
456 			ptr1 += len;
457 			continue;
458 		}
459 		memmove(ptr, ptr1 +5, len -5);
460 		ptr += len -5;
461 		ptr1 += len;
462 	}
463 	return ptr -list;
464 }
465 #endif
466 
sys_listxattr(const char * path,char * list,size_t size)467 ssize_t sys_listxattr (const char *path, char *list, size_t size)
468 {
469 #if defined(HAVE_LISTXATTR)
470 	ssize_t ret;
471 
472 #ifndef XATTR_ADD_OPT
473 	ret = listxattr(path, list, size);
474 #else
475 	int options = 0;
476 	ret = listxattr(path, list, size, options);
477 #endif
478 	return remove_user(ret, list, size);
479 
480 #elif defined(HAVE_LISTEA)
481 	return listea(path, list, size);
482 #elif defined(HAVE_EXTATTR_LIST_FILE)
483 	extattr_arg arg;
484 	arg.path = path;
485 	return bsd_attr_list(0, arg, list, size);
486 #elif defined(HAVE_ATTR_LIST) && defined(HAVE_SYS_ATTRIBUTES_H)
487 	return irix_attr_list(path, 0, list, size, 0);
488 #elif defined(HAVE_ATTROPEN)
489 	ssize_t ret = -1;
490 	int attrdirfd = solaris_attropen(path, ".", O_RDONLY, 0);
491 	if (attrdirfd >= 0) {
492 		ret = solaris_list_xattr(attrdirfd, list, size);
493 		close(attrdirfd);
494 	}
495 	return ret;
496 #else
497 	errno = ENOSYS;
498 	return -1;
499 #endif
500 }
501 
sys_flistxattr(int filedes,const char * path,char * list,size_t size)502 ssize_t sys_flistxattr (int filedes, const char *path, char *list, size_t size)
503 {
504 #if defined(HAVE_LISTXATTR)
505 	ssize_t ret;
506 
507 #ifndef XATTR_ADD_OPT
508 	ret = listxattr(path, list, size);
509 #else
510 	int options = 0;
511 	ret = listxattr(path, list, size, options);
512 #endif
513 	return remove_user(ret, list, size);
514 
515 #elif defined(HAVE_LISTEA)
516 	return listea(path, list, size);
517 #elif defined(HAVE_EXTATTR_LIST_FILE)
518 	extattr_arg arg;
519 	arg.path = path;
520 	return bsd_attr_list(0, arg, list, size);
521 #elif defined(HAVE_ATTR_LIST) && defined(HAVE_SYS_ATTRIBUTES_H)
522 	return irix_attr_list(path, 0, list, size, 0);
523 #elif defined(HAVE_ATTROPEN)
524 	ssize_t ret = -1;
525 	int attrdirfd = solaris_attropenat(filedes, path, ".", O_RDONLY, 0);
526 	if (attrdirfd >= 0) {
527 		ret = solaris_list_xattr(attrdirfd, list, size);
528 		close(attrdirfd);
529 	}
530 	return ret;
531 #else
532 	errno = ENOSYS;
533 	return -1;
534 #endif
535 }
536 
sys_llistxattr(const char * path,char * list,size_t size)537 ssize_t sys_llistxattr (const char *path, char *list, size_t size)
538 {
539 #if defined(HAVE_LLISTXATTR)
540 	ssize_t ret;
541 
542 	ret = llistxattr(path, list, size);
543 	return remove_user(ret, list, size);
544 #elif defined(HAVE_LISTXATTR) && defined(XATTR_ADD_OPT)
545 	ssize_t ret;
546 	int options = XATTR_NOFOLLOW;
547 
548 	ret = listxattr(path, list, size, options);
549 	return remove_user(ret, list, size);
550 
551 #elif defined(HAVE_LLISTEA)
552 	return llistea(path, list, size);
553 #elif defined(HAVE_EXTATTR_LIST_LINK)
554 	extattr_arg arg;
555 	arg.path = path;
556 	return bsd_attr_list(1, arg, list, size);
557 #elif defined(HAVE_ATTR_LIST) && defined(HAVE_SYS_ATTRIBUTES_H)
558 	return irix_attr_list(path, 0, list, size, ATTR_DONTFOLLOW);
559 #elif defined(HAVE_ATTROPEN)
560 	ssize_t ret = -1;
561 	int attrdirfd = solaris_attropen(path, ".", O_RDONLY | O_NOFOLLOW, 0);
562 	if (attrdirfd >= 0) {
563 		ret = solaris_list_xattr(attrdirfd, list, size);
564 		close(attrdirfd);
565 	}
566 	return ret;
567 #else
568 	errno = ENOSYS;
569 	return -1;
570 #endif
571 }
572 
sys_removexattr(const char * path,const char * uname)573 int sys_removexattr (const char *path, const char *uname)
574 {
575 	const char *name = prefix(uname);
576 #if defined(HAVE_REMOVEXATTR)
577 #ifndef XATTR_ADD_OPT
578 	return removexattr(path, name);
579 #else
580 	int options = 0;
581 	return removexattr(path, name, options);
582 #endif
583 #elif defined(HAVE_REMOVEEA)
584 	return removeea(path, name);
585 #elif defined(HAVE_EXTATTR_DELETE_FILE)
586 	return extattr_delete_file(path, EXTATTR_NAMESPACE_USER, uname);
587 #elif defined(HAVE_ATTR_REMOVE)
588 	int flags = 0;
589 	char *attrname = strchr(name,'.') + 1;
590 
591 	if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
592 
593 	return attr_remove(path, attrname, flags);
594 #elif defined(HAVE_ATTROPEN)
595 	int ret = -1;
596 	int attrdirfd = solaris_attropen(path, ".", O_RDONLY, 0);
597 	if (attrdirfd >= 0) {
598 		ret = solaris_unlinkat(attrdirfd, name);
599 		close(attrdirfd);
600 	}
601 	return ret;
602 #else
603 	errno = ENOSYS;
604 	return -1;
605 #endif
606 }
607 
sys_fremovexattr(int filedes,const char * path,const char * uname)608 int sys_fremovexattr (int filedes, const char *path, const char *uname)
609 {
610 	const char *name = prefix(uname);
611 #if defined(HAVE_REMOVEXATTR)
612 #ifndef XATTR_ADD_OPT
613 	return removexattr(path, name);
614 #else
615 	int options = 0;
616 	return removexattr(path, name, options);
617 #endif
618 #elif defined(HAVE_REMOVEEA)
619 	return removeea(path, name);
620 #elif defined(HAVE_EXTATTR_DELETE_FILE)
621 	return extattr_delete_file(path, EXTATTR_NAMESPACE_USER, uname);
622 #elif defined(HAVE_ATTR_REMOVE)
623 	int flags = 0;
624 	char *attrname = strchr(name,'.') + 1;
625 
626 	if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
627 
628 	return attr_remove(path, attrname, flags);
629 #elif defined(HAVE_ATTROPEN)
630 	int ret = -1;
631 	int attrdirfd = solaris_attropenat(filedes, path, ".", O_RDONLY, 0);
632 	if (attrdirfd >= 0) {
633 		ret = solaris_unlinkat(attrdirfd, name);
634 		close(attrdirfd);
635 	}
636 	return ret;
637 #else
638 	errno = ENOSYS;
639 	return -1;
640 #endif
641 }
642 
sys_lremovexattr(const char * path,const char * uname)643 int sys_lremovexattr (const char *path, const char *uname)
644 {
645 	const char *name = prefix(uname);
646 #if defined(HAVE_LREMOVEXATTR)
647 	return lremovexattr(path, name);
648 #elif defined(HAVE_REMOVEXATTR) && defined(XATTR_ADD_OPT)
649 	int options = XATTR_NOFOLLOW;
650 	return removexattr(path, name, options);
651 #elif defined(HAVE_LREMOVEEA)
652 	return lremoveea(path, name);
653 #elif defined(HAVE_EXTATTR_DELETE_LINK)
654 	return extattr_delete_link(path, EXTATTR_NAMESPACE_USER, uname);
655 #elif defined(HAVE_ATTR_REMOVE)
656 	int flags = ATTR_DONTFOLLOW;
657 	char *attrname = strchr(name,'.') + 1;
658 
659 	if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
660 
661 	return attr_remove(path, attrname, flags);
662 #elif defined(HAVE_ATTROPEN)
663 	int ret = -1;
664 	int attrdirfd = solaris_attropen(path, ".", O_RDONLY | O_NOFOLLOW, 0);
665 	if (attrdirfd >= 0) {
666 		ret = solaris_unlinkat(attrdirfd, name);
667 		close(attrdirfd);
668 	}
669 	return ret;
670 #else
671 	errno = ENOSYS;
672 	return -1;
673 #endif
674 }
675 
sys_setxattr(const char * path,const char * uname,const void * value,size_t size,int flags)676 int sys_setxattr (const char *path, const char *uname, const void *value, size_t size, int flags)
677 {
678 	const char *name = prefix(uname);
679 #if defined(HAVE_SETXATTR)
680 #ifndef XATTR_ADD_OPT
681 	return setxattr(path, name, value, size, flags);
682 #else
683 	int options = 0;
684 	return setxattr(path, name, value, size, 0, options);
685 #endif
686 #elif defined(HAVE_SETEA)
687 	return setea(path, name, value, size, flags);
688 #elif defined(HAVE_EXTATTR_SET_FILE)
689 	int retval = 0;
690 	if (flags) {
691 		/* Check attribute existence */
692 		retval = extattr_get_file(path, EXTATTR_NAMESPACE_USER, uname, 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_file(path, EXTATTR_NAMESPACE_USER, uname, value, size);
710 	return (retval < 0) ? -1 : 0;
711 #elif defined(HAVE_ATTR_SET)
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_set(path, attrname, (const char *)value, size, myflags);
720 #elif defined(HAVE_ATTROPEN)
721 	int ret = -1;
722 	int myflags = O_RDWR;
723 	int attrfd;
724 	if (flags & XATTR_CREATE) myflags |= O_EXCL;
725 	if (!(flags & XATTR_REPLACE)) myflags |= O_CREAT;
726 	attrfd = solaris_attropen(path, name, myflags, (mode_t) SOLARIS_ATTRMODE);
727 	if (attrfd >= 0) {
728 		ret = solaris_write_xattr(attrfd, value, size);
729 		close(attrfd);
730 	}
731 	return ret;
732 #else
733 	errno = ENOSYS;
734 	return -1;
735 #endif
736 }
737 
sys_fsetxattr(int filedes,const char * uname,const void * value,size_t size,int flags)738 int sys_fsetxattr (int filedes, const char *uname, const void *value, size_t size, int flags)
739 {
740     const char *name = prefix(uname);
741 
742 #if defined(HAVE_FSETXATTR)
743 #ifndef XATTR_ADD_OPT
744     return fsetxattr(filedes, name, value, size, flags);
745 #else
746     int options = 0;
747     return fsetxattr(filedes, name, value, size, 0, options);
748 #endif
749 #elif defined(HAVE_FSETEA)
750     return fsetea(filedes, name, value, size, flags);
751 #elif defined(HAVE_EXTATTR_SET_FD)
752     char *s;
753     int retval = 0;
754     int attrnamespace = (strncmp(name, "system", 6) == 0) ?
755         EXTATTR_NAMESPACE_SYSTEM : EXTATTR_NAMESPACE_USER;
756     const char *attrname = ((s=strchr(name, '.')) == NULL) ? name : s + 1;
757     if (flags) {
758         /* Check attribute existence */
759         retval = extattr_get_fd(filedes, attrnamespace, attrname, NULL, 0);
760         if (retval < 0) {
761             /* REPLACE attribute, that doesn't exist */
762             if (flags & XATTR_REPLACE && errno == ENOATTR) {
763                 errno = ENOATTR;
764                 return -1;
765             }
766             /* Ignore other errors */
767         }
768         else {
769             if (flags & XATTR_CREATE) {
770                 errno = EEXIST;
771                 return -1;
772             }
773         }
774     }
775     retval = extattr_set_fd(filedes, attrnamespace, attrname, value, size);
776     return (retval < 0) ? -1 : 0;
777 #elif defined(HAVE_ATTR_SETF)
778     int myflags = 0;
779     char *attrname = strchr(name,'.') + 1;
780 
781     if (strncmp(name, "system", 6) == 0) myflags |= ATTR_ROOT;
782     if (flags & XATTR_CREATE) myflags |= ATTR_CREATE;
783     if (flags & XATTR_REPLACE) myflags |= ATTR_REPLACE;
784 
785     return attr_setf(filedes, attrname, (const char *)value, size, myflags);
786 #elif defined(HAVE_ATTROPEN)
787     int ret = -1;
788     int myflags = O_RDWR | O_XATTR;
789     int attrfd;
790     if (flags & XATTR_CREATE) myflags |= O_EXCL;
791     if (!(flags & XATTR_REPLACE)) myflags |= O_CREAT;
792     attrfd = solaris_openat(filedes, name, myflags, (mode_t) SOLARIS_ATTRMODE);
793     if (attrfd >= 0) {
794         ret = solaris_write_xattr(attrfd, value, size);
795         close(attrfd);
796     }
797     return ret;
798 #else
799     errno = ENOSYS;
800     return -1;
801 #endif
802 }
803 
sys_lsetxattr(const char * path,const char * uname,const void * value,size_t size,int flags)804 int sys_lsetxattr (const char *path, const char *uname, const void *value, size_t size, int flags)
805 {
806 	const char *name = prefix(uname);
807 #if defined(HAVE_LSETXATTR)
808 	return lsetxattr(path, name, value, size, flags);
809 #elif defined(HAVE_SETXATTR) && defined(XATTR_ADD_OPT)
810 	int options = XATTR_NOFOLLOW;
811 	return setxattr(path, name, value, size, 0, options);
812 #elif defined(LSETEA)
813 	return lsetea(path, name, value, size, flags);
814 #elif defined(HAVE_EXTATTR_SET_LINK)
815 	int retval = 0;
816 	if (flags) {
817 		/* Check attribute existence */
818 		retval = extattr_get_link(path, EXTATTR_NAMESPACE_USER, uname, NULL, 0);
819 		if (retval < 0) {
820 			/* REPLACE attribute, that doesn't exist */
821 			if (flags & XATTR_REPLACE && errno == ENOATTR) {
822 				errno = ENOATTR;
823 				return -1;
824 			}
825 			/* Ignore other errors */
826 		}
827 		else {
828 			/* CREATE attribute, that already exists */
829 			if (flags & XATTR_CREATE) {
830 				errno = EEXIST;
831 				return -1;
832 			}
833 		}
834 	}
835 
836 	retval = extattr_set_link(path, EXTATTR_NAMESPACE_USER, uname, value, size);
837 	return (retval < 0) ? -1 : 0;
838 #elif defined(HAVE_ATTR_SET)
839 	int myflags = ATTR_DONTFOLLOW;
840 	char *attrname = strchr(name,'.') + 1;
841 
842 	if (strncmp(name, "system", 6) == 0) myflags |= ATTR_ROOT;
843 	if (flags & XATTR_CREATE) myflags |= ATTR_CREATE;
844 	if (flags & XATTR_REPLACE) myflags |= ATTR_REPLACE;
845 
846 	return attr_set(path, attrname, (const char *)value, size, myflags);
847 #elif defined(HAVE_ATTROPEN)
848 	int ret = -1;
849 	int myflags = O_RDWR | O_NOFOLLOW;
850 	int attrfd;
851 	if (flags & XATTR_CREATE) myflags |= O_EXCL;
852 	if (!(flags & XATTR_REPLACE)) myflags |= O_CREAT;
853 	attrfd = solaris_attropen(path, name, myflags, (mode_t) SOLARIS_ATTRMODE);
854 	if (attrfd >= 0) {
855 		ret = solaris_write_xattr(attrfd, value, size);
856 		close(attrfd);
857 	}
858 	return ret;
859 #else
860 	errno = ENOSYS;
861 	return -1;
862 #endif
863 }
864 
865 /**************************************************************************
866  helper functions for Solaris' EA support
867 ****************************************************************************/
868 #ifdef HAVE_ATTROPEN
solaris_read_xattr(int attrfd,void * value,size_t size)869 static ssize_t solaris_read_xattr(int attrfd, void *value, size_t size)
870 {
871 	struct stat sbuf;
872 
873 	if (fstat(attrfd, &sbuf) == -1) {
874 		return -1;
875 	}
876 
877 	/* This is to return the current size of the named extended attribute */
878 	if (size == 0) {
879 		return sbuf.st_size;
880 	}
881 
882 	/* check size and read xattr */
883 	if (sbuf.st_size > size) {
884 		return -1;
885 	}
886 
887 	return read(attrfd, value, sbuf.st_size);
888 }
889 
solaris_list_xattr(int attrdirfd,char * list,size_t size)890 static ssize_t solaris_list_xattr(int attrdirfd, char *list, size_t size)
891 {
892 	ssize_t len = 0;
893 	DIR *dirp;
894 	struct dirent *de;
895 	int newfd = dup(attrdirfd);
896 	/* CAUTION: The originating file descriptor should not be
897 	            used again following the call to fdopendir().
898 	            For that reason we dup() the file descriptor
899 		    here to make things more clear. */
900 	dirp = fdopendir(newfd);
901 
902 	while ((de = readdir(dirp))) {
903 		size_t listlen;
904 		if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
905 		{
906 			/* we don't want "." and ".." here: */
907 			LOG(log_maxdebug, logtype_default, "skipped EA %s\n",de->d_name);
908 			continue;
909 		}
910 
911 		listlen = strlen(de->d_name);
912 		if (size == 0) {
913 			/* return the current size of the list of extended attribute names*/
914 			len += listlen + 1;
915 		} else {
916 			/* check size and copy entry + nul into list. */
917 			if ((len + listlen + 1) > size) {
918 				errno = ERANGE;
919 				len = -1;
920 				break;
921 			} else {
922 				strcpy(list + len, de->d_name);
923 				len += listlen;
924 				list[len] = '\0';
925 				++len;
926 			}
927 		}
928 	}
929 
930 	if (closedir(dirp) == -1) {
931 		LOG(log_error, logtype_default, "closedir dirp: %s",strerror(errno));
932 		return -1;
933 	}
934 	return len;
935 }
936 
solaris_unlinkat(int attrdirfd,const char * name)937 static int solaris_unlinkat(int attrdirfd, const char *name)
938 {
939 	if (unlinkat(attrdirfd, name, 0) == -1) {
940 		return -1;
941 	}
942 	return 0;
943 }
944 
solaris_attropen(const char * path,const char * attrpath,int oflag,mode_t mode)945 static int solaris_attropen(const char *path, const char *attrpath, int oflag, mode_t mode)
946 {
947     EC_INIT;
948 	int filedes = -1, eafd = -1;
949 
950     if ((filedes = open(path, O_RDONLY | (oflag & O_NOFOLLOW), mode)) == -1) {
951         switch (errno) {
952         case ENOENT:
953         case EEXIST:
954         case OPEN_NOFOLLOW_ERRNO:
955             EC_FAIL;
956         default:
957             LOG(log_debug, logtype_default, "open(\"%s\"): %s", fullpathname(path), strerror(errno));
958             EC_FAIL;
959         }
960 	}
961 
962 	if ((eafd = openat(filedes, attrpath, oflag | O_XATTR, mode)) == -1) {
963         switch (errno) {
964         case ENOENT:
965         case EEXIST:
966             EC_FAIL;
967         default:
968             LOG(log_debug, logtype_default, "openat(\"%s\"): %s", fullpathname(path), strerror(errno));
969             EC_FAIL;
970         }
971 	}
972 
973 EC_CLEANUP:
974     if (filedes != -1)
975         close(filedes);
976     if (ret != 0) {
977         if (eafd != -1)
978             close(eafd);
979         eafd = -1;
980     }
981     return eafd;
982 }
983 
solaris_attropenat(int filedes,const char * path,const char * attrpath,int oflag,mode_t mode)984 static int solaris_attropenat(int filedes, const char *path, const char *attrpath, int oflag, mode_t mode)
985 {
986     EC_INIT;
987 	int eafd = -1;
988 
989 	if ((eafd = openat(filedes, attrpath, oflag | O_XATTR, mode)) == -1) {
990         switch (errno) {
991         case ENOENT:
992         case EEXIST:
993             EC_FAIL;
994         default:
995             LOG(log_debug, logtype_default, "openat(\"%s\"): %s", fullpathname(path), strerror(errno));
996             EC_FAIL;
997         }
998 	}
999 
1000 EC_CLEANUP:
1001     if (ret != 0) {
1002         if (eafd != -1)
1003             close(eafd);
1004         eafd = -1;
1005     }
1006     return eafd;
1007 }
1008 
1009 
solaris_openat(int fildes,const char * path,int oflag,mode_t mode)1010 static int solaris_openat(int fildes, const char *path, int oflag, mode_t mode)
1011 {
1012 	int filedes;
1013 
1014 	if ((filedes = openat(fildes, path, oflag, mode)) == -1) {
1015         switch (errno) {
1016         case ENOENT:
1017         case EEXIST:
1018         case EACCES:
1019             break;
1020         default:
1021             LOG(log_debug, logtype_default, "openat(\"%s\"): %s",
1022                 path, strerror(errno));
1023             errno = ENOATTR;
1024             break;
1025         }
1026 	}
1027 	return filedes;
1028 }
1029 
solaris_write_xattr(int attrfd,const char * value,size_t size)1030 static int solaris_write_xattr(int attrfd, const char *value, size_t size)
1031 {
1032 	if ((ftruncate(attrfd, 0) == 0) && (write(attrfd, value, size) == size)) {
1033 		return 0;
1034 	} else {
1035 		LOG(log_error, logtype_default, "solaris_write_xattr: %s",
1036             strerror(errno));
1037 		return -1;
1038 	}
1039 }
1040 
1041 #endif /*HAVE_ATTROPEN*/
1042 
1043