2 Unix SMB/CIFS implementation.
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
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.
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.
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.
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.
27 Samba 3.0.28, modified for netatalk.
36 #include <sys/types.h>
40 #include <attr/xattr.h>
41 #elif HAVE_SYS_XATTR_H
42 #include <sys/xattr.h>
54 #ifdef HAVE_SYS_EXTATTR_H
55 #include <sys/extattr.h>
58 #include <atalk/adouble.h>
59 #include <atalk/util.h>
60 #include <atalk/logger.h>
62 #include <atalk/compat.h>
63 #include <atalk/errchk.h>
65 /******** Solaris EA helper function prototypes ********/
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_openat(int fildes, const char *path, int oflag, mode_t mode);
76 /**************************************************************************
77 Wrappers for extented attribute calls. Based on the Linux package with
78 support for IRIX and (Net|Free)BSD also. Expand as other systems have them.
79 ****************************************************************************/
80 static char attr_name[256 +5] = "user.";
82 static const char *prefix(const char *uname)
84 #if defined(HAVE_ATTROPEN)
87 strlcpy(attr_name +5, uname, 256);
92 int sys_getxattrfd(int fd, const char *uname, int oflag, ...)
94 #if defined HAVE_ATTROPEN
99 if (oflag & O_CREAT) {
100 va_start(args, oflag);
101 mode = va_arg(args, mode_t);
106 eafd = solaris_openat(fd, uname, oflag | O_XATTR, mode);
108 eafd = solaris_openat(fd, uname, oflag | O_XATTR, mode);
117 ssize_t sys_getxattr (const char *path, const char *uname, void *value, size_t size)
119 const char *name = prefix(uname);
121 #if defined(HAVE_GETXATTR)
122 #ifndef XATTR_ADD_OPT
123 return getxattr(path, name, value, size);
126 return getxattr(path, name, value, size, 0, options);
128 #elif defined(HAVE_GETEA)
129 return getea(path, name, value, size);
130 #elif defined(HAVE_EXTATTR_GET_FILE)
133 * The BSD implementation has a nasty habit of silently truncating
134 * the returned value to the size of the buffer, so we have to check
135 * that the buffer is large enough to fit the returned value.
137 if((retval = extattr_get_file(path, EXTATTR_NAMESPACE_USER, uname, NULL, 0)) >= 0) {
139 /* size == 0 means only return size */
145 if ((retval = extattr_get_file(path, EXTATTR_NAMESPACE_USER, uname, value, size)) >= 0)
149 LOG(log_maxdebug, logtype_default, "sys_getxattr: extattr_get_file() failed with: %s\n", strerror(errno));
151 #elif defined(HAVE_ATTR_GET)
152 int retval, flags = 0;
153 int valuelength = (int)size;
154 char *attrname = strchr(name,'.') + 1;
156 if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
158 retval = attr_get(path, attrname, (char *)value, &valuelength, flags);
160 return retval ? retval : valuelength;
161 #elif defined(HAVE_ATTROPEN)
163 int attrfd = solaris_attropen(path, name, O_RDONLY, 0);
165 ret = solaris_read_xattr(attrfd, value, size);
175 ssize_t sys_fgetxattr (int filedes, const char *uname, void *value, size_t size)
177 const char *name = prefix(uname);
179 #if defined(HAVE_FGETXATTR)
180 #ifndef XATTR_ADD_OPT
181 return fgetxattr(filedes, name, value, size);
184 return fgetxattr(filedes, name, value, size, 0, options);
186 #elif defined(HAVE_FGETEA)
187 return fgetea(filedes, name, value, size);
188 #elif defined(HAVE_EXTATTR_GET_FD)
191 int attrnamespace = (strncmp(name, "system", 6) == 0) ?
192 EXTATTR_NAMESPACE_SYSTEM : EXTATTR_NAMESPACE_USER;
193 const char *attrname = ((s=strchr(name, '.')) == NULL) ? name : s + 1;
195 if((retval=extattr_get_fd(filedes, attrnamespace, attrname, NULL, 0)) >= 0) {
200 if((retval=extattr_get_fd(filedes, attrnamespace, attrname, value, size)) >= 0)
204 LOG(log_debug, logtype_default, "sys_fgetxattr: extattr_get_fd(): %s", strerror(errno));
206 #elif defined(HAVE_ATTR_GETF)
207 int retval, flags = 0;
208 int valuelength = (int)size;
209 char *attrname = strchr(name,'.') + 1;
211 if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
213 retval = attr_getf(filedes, attrname, (char *)value, &valuelength, flags);
215 return retval ? retval : valuelength;
216 #elif defined(HAVE_ATTROPEN)
218 int attrfd = solaris_openat(filedes, name, O_RDONLY|O_XATTR, 0);
220 ret = solaris_read_xattr(attrfd, value, size);
230 ssize_t sys_lgetxattr (const char *path, const char *uname, void *value, size_t size)
232 const char *name = prefix(uname);
234 #if defined(HAVE_LGETXATTR)
235 return lgetxattr(path, name, value, size);
236 #elif defined(HAVE_GETXATTR) && defined(XATTR_ADD_OPT)
237 int options = XATTR_NOFOLLOW;
238 return getxattr(path, name, value, size, 0, options);
239 #elif defined(HAVE_LGETEA)
240 return lgetea(path, name, value, size);
241 #elif defined(HAVE_EXTATTR_GET_LINK)
243 if((retval=extattr_get_link(path, EXTATTR_NAMESPACE_USER, uname, NULL, 0)) >= 0) {
248 if((retval=extattr_get_link(path, EXTATTR_NAMESPACE_USER, uname, value, size)) >= 0)
252 LOG(log_maxdebug, logtype_default, "sys_lgetxattr: extattr_get_link() failed with: %s\n", strerror(errno));
254 #elif defined(HAVE_ATTR_GET)
255 int retval, flags = ATTR_DONTFOLLOW;
256 int valuelength = (int)size;
257 char *attrname = strchr(name,'.') + 1;
259 if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
261 retval = attr_get(path, attrname, (char *)value, &valuelength, flags);
263 return retval ? retval : valuelength;
264 #elif defined(HAVE_ATTROPEN)
266 int attrfd = solaris_attropen(path, name, O_RDONLY | O_NOFOLLOW, 0);
268 ret = solaris_read_xattr(attrfd, value, size);
278 #if defined(HAVE_EXTATTR_LIST_FILE)
280 #define EXTATTR_PREFIX(s) (s), (sizeof((s))-1)
288 { EXTATTR_NAMESPACE_SYSTEM, EXTATTR_PREFIX("") },
289 { EXTATTR_NAMESPACE_USER, EXTATTR_PREFIX("") },
297 static ssize_t bsd_attr_list (int type, extattr_arg arg, char *list, size_t size)
303 #if defined(HAVE_EXTATTR_LIST_FILE)
305 list_size = extattr_list_file(arg.path, EXTATTR_NAMESPACE_USER, list, size);
308 #if defined(HAVE_EXTATTR_LIST_LINK)
310 list_size = extattr_list_link(arg.path, EXTATTR_NAMESPACE_USER, list, size);
313 #if defined(HAVE_EXTATTR_LIST_FD)
315 list_size = extattr_list_fd(arg.filedes, EXTATTR_NAMESPACE_USER, list, size);
323 /* Some error happend. Errno should be set by the previous call */
331 /* XXX: Call with an empty buffer may be used to calculate
332 necessary buffer size. Unfortunately, we can't say, how
333 many attributes were returned, so here is the potential
334 problem with the emulation.
339 /* Buffer is too small to fit the results */
340 if(list_size > size) {
345 /* Convert from pascal strings to C strings */
347 memmove(list, list + 1, list_size);
349 for(i = len; i < list_size; ) {
350 LOG(log_maxdebug, logtype_afpd, "len: %d, i: %d", len, i);
362 #if defined(HAVE_ATTR_LIST) && defined(HAVE_SYS_ATTRIBUTES_H)
363 static char attr_buffer[ATTR_MAX_VALUELEN];
365 static ssize_t irix_attr_list(const char *path, int filedes, char *list, size_t size, int flags)
367 int retval = 0, index;
368 attrlist_cursor_t *cursor = 0;
370 attrlist_t * al = (attrlist_t *)attr_buffer;
372 size_t ent_size, left = size;
377 retval = attr_listf(filedes, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
379 retval = attr_list(path, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
381 for (index = 0; index < al->al_count; index++) {
382 ae = ATTR_ENTRY(attr_buffer, index);
383 ent_size = strlen(ae->a_name) + sizeof("user.");
384 if (left >= ent_size) {
385 strncpy(bp, "user.", sizeof("user."));
386 strncat(bp, ae->a_name, ent_size - sizeof("user."));
394 total_size += ent_size;
396 if (al->al_more == 0) break;
403 retval = attr_listf(filedes, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
405 retval = attr_list(path, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
407 for (index = 0; index < al->al_count; index++) {
408 ae = ATTR_ENTRY(attr_buffer, index);
409 ent_size = strlen(ae->a_name) + sizeof("system.");
410 if (left >= ent_size) {
411 strncpy(bp, "system.", sizeof("system."));
412 strncat(bp, ae->a_name, ent_size - sizeof("system."));
420 total_size += ent_size;
422 if (al->al_more == 0) break;
425 return (ssize_t)(retval ? retval : total_size);
430 #if defined(HAVE_LISTXATTR)
431 static ssize_t remove_user(ssize_t ret, char *list, size_t size)
438 if (ret <= 0 || size == 0)
442 while (ptrsize > 0) {
443 len = strlen(ptr1) +1;
445 if (strncmp(ptr1, "user.",5)) {
449 memmove(ptr, ptr1 +5, len -5);
457 ssize_t sys_listxattr (const char *path, char *list, size_t size)
459 #if defined(HAVE_LISTXATTR)
462 #ifndef XATTR_ADD_OPT
463 ret = listxattr(path, list, size);
466 ret = listxattr(path, list, size, options);
468 return remove_user(ret, list, size);
470 #elif defined(HAVE_LISTEA)
471 return listea(path, list, size);
472 #elif defined(HAVE_EXTATTR_LIST_FILE)
475 return bsd_attr_list(0, arg, list, size);
476 #elif defined(HAVE_ATTR_LIST) && defined(HAVE_SYS_ATTRIBUTES_H)
477 return irix_attr_list(path, 0, list, size, 0);
478 #elif defined(HAVE_ATTROPEN)
480 int attrdirfd = solaris_attropen(path, ".", O_RDONLY, 0);
481 if (attrdirfd >= 0) {
482 ret = solaris_list_xattr(attrdirfd, list, size);
492 ssize_t sys_llistxattr (const char *path, char *list, size_t size)
494 #if defined(HAVE_LLISTXATTR)
497 ret = llistxattr(path, list, size);
498 return remove_user(ret, list, size);
499 #elif defined(HAVE_LISTXATTR) && defined(XATTR_ADD_OPT)
501 int options = XATTR_NOFOLLOW;
503 ret = listxattr(path, list, size, options);
504 return remove_user(ret, list, size);
506 #elif defined(HAVE_LLISTEA)
507 return llistea(path, list, size);
508 #elif defined(HAVE_EXTATTR_LIST_LINK)
511 return bsd_attr_list(1, arg, list, size);
512 #elif defined(HAVE_ATTR_LIST) && defined(HAVE_SYS_ATTRIBUTES_H)
513 return irix_attr_list(path, 0, list, size, ATTR_DONTFOLLOW);
514 #elif defined(HAVE_ATTROPEN)
516 int attrdirfd = solaris_attropen(path, ".", O_RDONLY | O_NOFOLLOW, 0);
517 if (attrdirfd >= 0) {
518 ret = solaris_list_xattr(attrdirfd, list, size);
528 int sys_removexattr (const char *path, const char *uname)
530 const char *name = prefix(uname);
531 #if defined(HAVE_REMOVEXATTR)
532 #ifndef XATTR_ADD_OPT
533 return removexattr(path, name);
536 return removexattr(path, name, options);
538 #elif defined(HAVE_REMOVEEA)
539 return removeea(path, name);
540 #elif defined(HAVE_EXTATTR_DELETE_FILE)
541 return extattr_delete_file(path, EXTATTR_NAMESPACE_USER, uname);
542 #elif defined(HAVE_ATTR_REMOVE)
544 char *attrname = strchr(name,'.') + 1;
546 if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
548 return attr_remove(path, attrname, flags);
549 #elif defined(HAVE_ATTROPEN)
551 int attrdirfd = solaris_attropen(path, ".", O_RDONLY, 0);
552 if (attrdirfd >= 0) {
553 ret = solaris_unlinkat(attrdirfd, name);
563 int sys_lremovexattr (const char *path, const char *uname)
565 const char *name = prefix(uname);
566 #if defined(HAVE_LREMOVEXATTR)
567 return lremovexattr(path, name);
568 #elif defined(HAVE_REMOVEXATTR) && defined(XATTR_ADD_OPT)
569 int options = XATTR_NOFOLLOW;
570 return removexattr(path, name, options);
571 #elif defined(HAVE_LREMOVEEA)
572 return lremoveea(path, name);
573 #elif defined(HAVE_EXTATTR_DELETE_LINK)
574 return extattr_delete_link(path, EXTATTR_NAMESPACE_USER, uname);
575 #elif defined(HAVE_ATTR_REMOVE)
576 int flags = ATTR_DONTFOLLOW;
577 char *attrname = strchr(name,'.') + 1;
579 if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
581 return attr_remove(path, attrname, flags);
582 #elif defined(HAVE_ATTROPEN)
584 int attrdirfd = solaris_attropen(path, ".", O_RDONLY | O_NOFOLLOW, 0);
585 if (attrdirfd >= 0) {
586 ret = solaris_unlinkat(attrdirfd, name);
596 int sys_setxattr (const char *path, const char *uname, const void *value, size_t size, int flags)
598 const char *name = prefix(uname);
599 #if defined(HAVE_SETXATTR)
600 #ifndef XATTR_ADD_OPT
601 return setxattr(path, name, value, size, flags);
604 return setxattr(path, name, value, size, 0, options);
606 #elif defined(HAVE_SETEA)
607 return setea(path, name, value, size, flags);
608 #elif defined(HAVE_EXTATTR_SET_FILE)
611 /* Check attribute existence */
612 retval = extattr_get_file(path, EXTATTR_NAMESPACE_USER, uname, NULL, 0);
614 /* REPLACE attribute, that doesn't exist */
615 if (flags & XATTR_REPLACE && errno == ENOATTR) {
619 /* Ignore other errors */
622 /* CREATE attribute, that already exists */
623 if (flags & XATTR_CREATE) {
629 retval = extattr_set_file(path, EXTATTR_NAMESPACE_USER, uname, value, size);
630 return (retval < 0) ? -1 : 0;
631 #elif defined(HAVE_ATTR_SET)
633 char *attrname = strchr(name,'.') + 1;
635 if (strncmp(name, "system", 6) == 0) myflags |= ATTR_ROOT;
636 if (flags & XATTR_CREATE) myflags |= ATTR_CREATE;
637 if (flags & XATTR_REPLACE) myflags |= ATTR_REPLACE;
639 return attr_set(path, attrname, (const char *)value, size, myflags);
640 #elif defined(HAVE_ATTROPEN)
642 int myflags = O_RDWR;
644 if (flags & XATTR_CREATE) myflags |= O_EXCL;
645 if (!(flags & XATTR_REPLACE)) myflags |= O_CREAT;
646 attrfd = solaris_attropen(path, name, myflags, (mode_t) SOLARIS_ATTRMODE);
648 ret = solaris_write_xattr(attrfd, value, size);
658 int sys_fsetxattr (int filedes, const char *uname, const void *value, size_t size, int flags)
660 const char *name = prefix(uname);
662 #if defined(HAVE_FSETXATTR)
663 #ifndef XATTR_ADD_OPT
664 return fsetxattr(filedes, name, value, size, flags);
667 return fsetxattr(filedes, name, value, size, 0, options);
669 #elif defined(HAVE_FSETEA)
670 return fsetea(filedes, name, value, size, flags);
671 #elif defined(HAVE_EXTATTR_SET_FD)
674 int attrnamespace = (strncmp(name, "system", 6) == 0) ?
675 EXTATTR_NAMESPACE_SYSTEM : EXTATTR_NAMESPACE_USER;
676 const char *attrname = ((s=strchr(name, '.')) == NULL) ? name : s + 1;
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 if (flags & XATTR_CREATE) {
695 retval = extattr_set_fd(filedes, attrnamespace, attrname, value, size);
696 return (retval < 0) ? -1 : 0;
697 #elif defined(HAVE_ATTR_SETF)
699 char *attrname = strchr(name,'.') + 1;
701 if (strncmp(name, "system", 6) == 0) myflags |= ATTR_ROOT;
702 if (flags & XATTR_CREATE) myflags |= ATTR_CREATE;
703 if (flags & XATTR_REPLACE) myflags |= ATTR_REPLACE;
705 return attr_setf(filedes, attrname, (const char *)value, size, myflags);
706 #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 ret = solaris_write_xattr(attrfd, value, size);
724 int sys_lsetxattr (const char *path, const char *uname, const void *value, size_t size, int flags)
726 const char *name = prefix(uname);
727 #if defined(HAVE_LSETXATTR)
728 return lsetxattr(path, name, value, size, flags);
729 #elif defined(HAVE_SETXATTR) && defined(XATTR_ADD_OPT)
730 int options = XATTR_NOFOLLOW;
731 return setxattr(path, name, value, size, 0, options);
732 #elif defined(LSETEA)
733 return lsetea(path, name, value, size, flags);
734 #elif defined(HAVE_EXTATTR_SET_LINK)
737 /* Check attribute existence */
738 retval = extattr_get_link(path, EXTATTR_NAMESPACE_USER, uname, NULL, 0);
740 /* REPLACE attribute, that doesn't exist */
741 if (flags & XATTR_REPLACE && errno == ENOATTR) {
745 /* Ignore other errors */
748 /* CREATE attribute, that already exists */
749 if (flags & XATTR_CREATE) {
756 retval = extattr_set_link(path, EXTATTR_NAMESPACE_USER, uname, value, size);
757 return (retval < 0) ? -1 : 0;
758 #elif defined(HAVE_ATTR_SET)
759 int myflags = ATTR_DONTFOLLOW;
760 char *attrname = strchr(name,'.') + 1;
762 if (strncmp(name, "system", 6) == 0) myflags |= ATTR_ROOT;
763 if (flags & XATTR_CREATE) myflags |= ATTR_CREATE;
764 if (flags & XATTR_REPLACE) myflags |= ATTR_REPLACE;
766 return attr_set(path, attrname, (const char *)value, size, myflags);
767 #elif defined(HAVE_ATTROPEN)
769 int myflags = O_RDWR | O_NOFOLLOW;
771 if (flags & XATTR_CREATE) myflags |= O_EXCL;
772 if (!(flags & XATTR_REPLACE)) myflags |= O_CREAT;
773 attrfd = solaris_attropen(path, name, myflags, (mode_t) SOLARIS_ATTRMODE);
775 ret = solaris_write_xattr(attrfd, value, size);
785 /**************************************************************************
786 helper functions for Solaris' EA support
787 ****************************************************************************/
789 static ssize_t solaris_read_xattr(int attrfd, void *value, size_t size)
793 if (fstat(attrfd, &sbuf) == -1) {
797 /* This is to return the current size of the named extended attribute */
802 /* check size and read xattr */
803 if (sbuf.st_size > size) {
807 return read(attrfd, value, sbuf.st_size);
810 static ssize_t solaris_list_xattr(int attrdirfd, char *list, size_t size)
815 int newfd = dup(attrdirfd);
816 /* CAUTION: The originating file descriptor should not be
817 used again following the call to fdopendir().
818 For that reason we dup() the file descriptor
819 here to make things more clear. */
820 dirp = fdopendir(newfd);
822 while ((de = readdir(dirp))) {
824 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..") ||
825 !strcmp(de->d_name, "SUNWattr_ro") || !strcmp(de->d_name, "SUNWattr_rw"))
827 /* we don't want "." and ".." here: */
828 LOG(log_maxdebug, logtype_default, "skipped EA %s\n",de->d_name);
832 listlen = strlen(de->d_name);
834 /* return the current size of the list of extended attribute names*/
837 /* check size and copy entry + nul into list. */
838 if ((len + listlen + 1) > size) {
843 strcpy(list + len, de->d_name);
851 if (closedir(dirp) == -1) {
852 LOG(log_error, logtype_default, "closedir dirp: %s",strerror(errno));
858 static int solaris_unlinkat(int attrdirfd, const char *name)
860 if (unlinkat(attrdirfd, name, 0) == -1) {
866 static int solaris_attropen(const char *path, const char *attrpath, int oflag, mode_t mode)
869 int filedes = -1, eafd = -1;
871 if ((filedes = open(path, O_RDONLY | (oflag & O_NOFOLLOW), mode)) == -1) {
875 case OPEN_NOFOLLOW_ERRNO:
878 LOG(log_debug, logtype_default, "open(\"%s\"): %s", fullpathname(path), strerror(errno));
883 if ((eafd = openat(filedes, attrpath, oflag | O_XATTR, mode)) == -1) {
889 LOG(log_debug, logtype_default, "openat(\"%s\"): %s", fullpathname(path), strerror(errno));
905 static int solaris_openat(int fildes, const char *path, int oflag, mode_t mode)
909 if ((filedes = openat(fildes, path, oflag, mode)) == -1) {
916 LOG(log_debug, logtype_default, "openat(\"%s\"): %s",
917 path, strerror(errno));
925 static int solaris_write_xattr(int attrfd, const char *value, size_t size)
927 if ((ftruncate(attrfd, 0) == 0) && (write(attrfd, value, size) == size)) {
930 LOG(log_error, logtype_default, "solaris_write_xattr: %s",
936 #endif /*HAVE_ATTROPEN*/