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
10 ** NOTE! The following LGPL license applies to the replace
11 ** library. This does NOT imply that all of Samba is released
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.
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.
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/>.
28 #define UID_WRAPPER_NOT_REPLACE
30 #include "system/filesys.h"
31 #include "system/dir.h"
33 /******** Solaris EA helper function prototypes ********/
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);
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 ****************************************************************************/
49 ssize_t rep_getxattr (const char *path, const char *name, void *value, size_t size)
51 #if defined(HAVE_XATTR_XATTR)
52 #ifndef XATTR_ADDITIONAL_OPTIONS
53 return getxattr(path, name, value, size);
56 /* So that we do not recursivly call this function */
59 return getxattr(path, name, value, size, 0, options);
61 #elif defined(HAVE_XATTR_EA)
62 return getea(path, name, value, size);
63 #elif defined(HAVE_XATTR_EXTATTR)
68 if (strncmp(name, "system.", 7) == 0) {
69 attrnamespace = EXTATTR_NAMESPACE_SYSTEM;
71 } else if (strncmp(name, "user.", 5) == 0) {
72 attrnamespace = EXTATTR_NAMESPACE_USER;
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.
84 if((retval=extattr_get_file(path, attrnamespace, attrname, NULL, 0)) >= 0) {
87 } else if (retval > size) {
91 if((retval=extattr_get_file(path, attrnamespace, attrname, value, size)) >= 0)
96 #elif defined(HAVE_XATTR_ATTR)
97 int retval, flags = 0;
98 int valuelength = (int)size;
99 char *attrname = strchr(name,'.') + 1;
101 if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
103 retval = attr_get(path, attrname, (char *)value, &valuelength, flags);
104 if (size == 0 && retval == -1 && errno == E2BIG) {
108 return retval ? retval : valuelength;
109 #elif defined(HAVE_ATTROPEN)
111 int attrfd = solaris_attropen(path, name, O_RDONLY, 0);
113 ret = solaris_read_xattr(attrfd, value, size);
123 ssize_t rep_fgetxattr (int filedes, const char *name, void *value, size_t size)
125 #if defined(HAVE_XATTR_XATTR)
126 #ifndef XATTR_ADDITIONAL_OPTIONS
127 return fgetxattr(filedes, name, value, size);
130 /* So that we do not recursivly call this function */
133 return fgetxattr(filedes, name, value, size, 0, options);
135 #elif defined(HAVE_XATTR_EA)
136 return fgetea(filedes, name, value, size);
137 #elif defined(HAVE_XATTR_EXTATTR)
140 const char *attrname;
142 if (strncmp(name, "system.", 7) == 0) {
143 attrnamespace = EXTATTR_NAMESPACE_SYSTEM;
145 } else if (strncmp(name, "user.", 5) == 0) {
146 attrnamespace = EXTATTR_NAMESPACE_USER;
153 if((retval=extattr_get_fd(filedes, attrnamespace, attrname, NULL, 0)) >= 0) {
156 } else if (retval > size) {
160 if((retval=extattr_get_fd(filedes, attrnamespace, attrname, value, size)) >= 0)
165 #elif defined(HAVE_XATTR_ATTR)
166 int retval, flags = 0;
167 int valuelength = (int)size;
168 char *attrname = strchr(name,'.') + 1;
170 if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
172 retval = attr_getf(filedes, attrname, (char *)value, &valuelength, flags);
173 if (size == 0 && retval == -1 && errno == E2BIG) {
176 return retval ? retval : valuelength;
177 #elif defined(HAVE_ATTROPEN)
179 int attrfd = solaris_openat(filedes, name, O_RDONLY|O_XATTR, 0);
181 ret = solaris_read_xattr(attrfd, value, size);
191 #if defined(HAVE_XATTR_EXTATTR)
193 #define EXTATTR_PREFIX(s) (s), (sizeof((s))-1)
201 { EXTATTR_NAMESPACE_SYSTEM, EXTATTR_PREFIX("system.") },
202 { EXTATTR_NAMESPACE_USER, EXTATTR_PREFIX("user.") },
210 static ssize_t bsd_attr_list (int type, extattr_arg arg, char *list, size_t size)
212 ssize_t list_size, total_size = 0;
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 */
223 list_size = extattr_list_file(arg.path, extattr[t].space, list, size);
226 list_size = extattr_list_link(arg.path, extattr[t].space, list, size);
229 list_size = extattr_list_fd(arg.filedes, extattr[t].space, list, size);
235 /* Some error happend. Errno should be set by the previous call */
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.
247 /* Take the worse case of one char attribute names -
248 two bytes per name plus one more for sanity.
250 total_size += list_size + (list_size/2 + 1)*extattr[t].len;
253 /* Count necessary offset to fit namespace prefixes */
255 for(i = 0; i < list_size; i += list[i] + 1)
256 len += extattr[t].len;
258 total_size += list_size + len;
259 /* Buffer is too small to fit the results */
260 if(total_size > size) {
264 /* Shift results back, so we can prepend prefixes */
265 buf = (char *)memmove(list + len, list, list_size);
267 for(i = 0; i < list_size; i += len + 1) {
269 strncpy(list, extattr[t].name, extattr[t].len + 1);
270 list += extattr[t].len;
271 strncpy(list, buf + i + 1, len);
282 #if defined(HAVE_XATTR_ATTR) && (defined(HAVE_SYS_ATTRIBUTES_H) || defined(HAVE_ATTR_ATTRIBUTES_H))
283 static char attr_buffer[ATTR_MAX_VALUELEN];
285 static ssize_t irix_attr_list(const char *path, int filedes, char *list, size_t size, int flags)
287 int retval = 0, index;
288 attrlist_cursor_t *cursor = 0;
290 attrlist_t * al = (attrlist_t *)attr_buffer;
292 size_t ent_size, left = size;
297 retval = attr_listf(filedes, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
299 retval = attr_list(path, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
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."));
314 total_size += ent_size;
316 if (al->al_more == 0) break;
323 retval = attr_listf(filedes, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
325 retval = attr_list(path, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
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."));
340 total_size += ent_size;
342 if (al->al_more == 0) break;
345 return (ssize_t)(retval ? retval : total_size);
350 ssize_t rep_listxattr (const char *path, char *list, size_t size)
352 #if defined(HAVE_XATTR_XATTR)
353 #ifndef XATTR_ADDITIONAL_OPTIONS
354 return listxattr(path, list, size);
356 /* So that we do not recursivly call this function */
359 return listxattr(path, list, size, options);
361 #elif defined(HAVE_XATTR_EA)
362 return listea(path, list, size);
363 #elif defined(HAVE_XATTR_EXTATTR)
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)
371 int attrdirfd = solaris_attropen(path, ".", O_RDONLY, 0);
372 if (attrdirfd >= 0) {
373 ret = solaris_list_xattr(attrdirfd, list, size);
383 ssize_t rep_flistxattr (int filedes, char *list, size_t size)
385 #if defined(HAVE_XATTR_XATTR)
386 #ifndef XATTR_ADDITIONAL_OPTIONS
387 return flistxattr(filedes, list, size);
389 /* So that we do not recursivly call this function */
392 return flistxattr(filedes, list, size, options);
394 #elif defined(HAVE_XATTR_EA)
395 return flistea(filedes, list, size);
396 #elif defined(HAVE_XATTR_EXTATTR)
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)
404 int attrdirfd = solaris_openat(filedes, ".", O_RDONLY|O_XATTR, 0);
405 if (attrdirfd >= 0) {
406 ret = solaris_list_xattr(attrdirfd, list, size);
416 int rep_removexattr (const char *path, const char *name)
418 #if defined(HAVE_XATTR_XATTR)
419 #ifndef XATTR_ADDITIONAL_OPTIONS
420 return removexattr(path, name);
422 /* So that we do not recursivly call this function */
425 return removexattr(path, name, options);
427 #elif defined(HAVE_XATTR_EA)
428 return removeea(path, name);
429 #elif defined(HAVE_XATTR_EXTATTR)
431 const char *attrname;
433 if (strncmp(name, "system.", 7) == 0) {
434 attrnamespace = EXTATTR_NAMESPACE_SYSTEM;
436 } else if (strncmp(name, "user.", 5) == 0) {
437 attrnamespace = EXTATTR_NAMESPACE_USER;
444 return extattr_delete_file(path, attrnamespace, attrname);
445 #elif defined(HAVE_XATTR_ATTR)
447 char *attrname = strchr(name,'.') + 1;
449 if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
451 return attr_remove(path, attrname, flags);
452 #elif defined(HAVE_ATTROPEN)
454 int attrdirfd = solaris_attropen(path, ".", O_RDONLY, 0);
455 if (attrdirfd >= 0) {
456 ret = solaris_unlinkat(attrdirfd, name);
466 int rep_fremovexattr (int filedes, const char *name)
468 #if defined(HAVE_XATTR_XATTR)
469 #ifndef XATTR_ADDITIONAL_OPTIONS
470 return fremovexattr(filedes, name);
472 /* So that we do not recursivly call this function */
475 return fremovexattr(filedes, name, options);
477 #elif defined(HAVE_XATTR_EA)
478 return fremoveea(filedes, name);
479 #elif defined(HAVE_XATTR_EXTATTR)
481 const char *attrname;
483 if (strncmp(name, "system.", 7) == 0) {
484 attrnamespace = EXTATTR_NAMESPACE_SYSTEM;
486 } else if (strncmp(name, "user.", 5) == 0) {
487 attrnamespace = EXTATTR_NAMESPACE_USER;
494 return extattr_delete_fd(filedes, attrnamespace, attrname);
495 #elif defined(HAVE_XATTR_ATTR)
497 char *attrname = strchr(name,'.') + 1;
499 if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
501 return attr_removef(filedes, attrname, flags);
502 #elif defined(HAVE_ATTROPEN)
504 int attrdirfd = solaris_openat(filedes, ".", O_RDONLY|O_XATTR, 0);
505 if (attrdirfd >= 0) {
506 ret = solaris_unlinkat(attrdirfd, name);
516 int rep_setxattr (const char *path, const char *name, const void *value, size_t size, int flags)
519 #if defined(HAVE_XATTR_XATTR)
520 #ifndef XATTR_ADDITIONAL_OPTIONS
521 retval = setxattr(path, name, value, size, flags);
523 if (errno == ENOSPC || errno == E2BIG) {
524 errno = ENAMETOOLONG;
529 /* So that we do not recursivly call this function */
531 retval = setxattr(path, name, value, size, 0, flags);
533 if (errno == E2BIG) {
534 errno = ENAMETOOLONG;
539 #elif defined(HAVE_XATTR_EA)
541 retval = getea(path, name, NULL, 0);
543 if (flags & XATTR_REPLACE && errno == ENOATTR) {
547 if (flags & XATTR_CREATE) {
553 retval = setea(path, name, value, size, 0);
555 #elif defined(HAVE_XATTR_EXTATTR)
557 const char *attrname;
559 if (strncmp(name, "system.", 7) == 0) {
560 attrnamespace = EXTATTR_NAMESPACE_SYSTEM;
562 } else if (strncmp(name, "user.", 5) == 0) {
563 attrnamespace = EXTATTR_NAMESPACE_USER;
571 /* Check attribute existence */
572 retval = extattr_get_file(path, attrnamespace, attrname, NULL, 0);
574 /* REPLACE attribute, that doesn't exist */
575 if (flags & XATTR_REPLACE && errno == ENOATTR) {
579 /* Ignore other errors */
582 /* CREATE attribute, that already exists */
583 if (flags & XATTR_CREATE) {
589 retval = extattr_set_file(path, attrnamespace, attrname, value, size);
590 return (retval < 0) ? -1 : 0;
591 #elif defined(HAVE_XATTR_ATTR)
593 char *attrname = strchr(name,'.') + 1;
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;
599 retval = attr_set(path, attrname, (const char *)value, size, myflags);
601 if (errno == E2BIG) {
602 errno = ENAMETOOLONG;
606 #elif defined(HAVE_ATTROPEN)
607 int myflags = O_RDWR;
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);
613 retval = solaris_write_xattr(attrfd, value, size);
623 int rep_fsetxattr (int filedes, const char *name, const void *value, size_t size, int flags)
626 #if defined(HAVE_XATTR_XATTR)
627 #ifndef XATTR_ADDITIONAL_OPTIONS
628 retval = fsetxattr(filedes, name, value, size, flags);
630 if (errno == ENOSPC) {
631 errno = ENAMETOOLONG;
636 /* So that we do not recursivly call this function */
638 retval = fsetxattr(filedes, name, value, size, 0, flags);
640 if (errno == E2BIG) {
641 errno = ENAMETOOLONG;
646 #elif defined(HAVE_XATTR_EA)
648 retval = fgetea(filedes, name, NULL, 0);
650 if (flags & XATTR_REPLACE && errno == ENOATTR) {
654 if (flags & XATTR_CREATE) {
660 retval = fsetea(filedes, name, value, size, 0);
662 #elif defined(HAVE_XATTR_EXTATTR)
664 const char *attrname;
666 if (strncmp(name, "system.", 7) == 0) {
667 attrnamespace = EXTATTR_NAMESPACE_SYSTEM;
669 } else if (strncmp(name, "user.", 5) == 0) {
670 attrnamespace = EXTATTR_NAMESPACE_USER;
678 /* Check attribute existence */
679 retval = extattr_get_fd(filedes, attrnamespace, attrname, NULL, 0);
681 /* REPLACE attribute, that doesn't exist */
682 if (flags & XATTR_REPLACE && errno == ENOATTR) {
686 /* Ignore other errors */
689 /* CREATE attribute, that already exists */
690 if (flags & XATTR_CREATE) {
696 retval = extattr_set_fd(filedes, attrnamespace, attrname, value, size);
697 return (retval < 0) ? -1 : 0;
698 #elif defined(HAVE_XATTR_ATTR)
700 char *attrname = strchr(name,'.') + 1;
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;
706 return attr_setf(filedes, attrname, (const char *)value, size, myflags);
707 #elif defined(HAVE_ATTROPEN)
708 int myflags = O_RDWR | O_XATTR;
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);
714 retval = solaris_write_xattr(attrfd, value, size);
724 /**************************************************************************
725 helper functions for Solaris' EA support
726 ****************************************************************************/
728 static ssize_t solaris_read_xattr(int attrfd, void *value, size_t size)
732 if (fstat(attrfd, &sbuf) == -1) {
737 /* This is to return the current size of the named extended attribute */
742 /* check size and read xattr */
743 if (sbuf.st_size > size) {
748 return read(attrfd, value, sbuf.st_size);
751 static ssize_t solaris_list_xattr(int attrdirfd, char *list, size_t size)
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);
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: */
771 /* return the current size of the list of extended attribute names*/
774 /* check size and copy entrieѕ + nul into list. */
775 if ((len + listlen) > size) {
780 strlcpy(list + len, de->d_name, listlen);
786 if (closedir(dirp) == -1) {
792 static int solaris_unlinkat(int attrdirfd, const char *name)
794 if (unlinkat(attrdirfd, name, 0) == -1) {
795 if (errno == ENOENT) {
803 static int solaris_attropen(const char *path, const char *attrpath, int oflag, mode_t mode)
805 int filedes = attropen(path, attrpath, oflag, mode);
807 if (errno == EINVAL) {
816 static int solaris_openat(int fildes, const char *path, int oflag, mode_t mode)
818 int filedes = openat(fildes, path, oflag, mode);
820 if (errno == EINVAL) {
829 static int solaris_write_xattr(int attrfd, const char *value, size_t size)
831 if ((ftruncate(attrfd, 0) == 0) && (write(attrfd, value, size) == size)) {
837 #endif /*HAVE_ATTROPEN*/