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)
244 retval = extattr_get_link(path, EXTATTR_NAMESPACE_USER, uname, NULL, 0);
246 LOG(log_maxdebug, logtype_default, "extattr_get_link(): %s",
251 /* Only interested in size of xattr */
257 return extattr_get_link(path, EXTATTR_NAMESPACE_USER, uname, value, size);
259 #elif defined(HAVE_ATTR_GET)
260 int retval, flags = ATTR_DONTFOLLOW;
261 int valuelength = (int)size;
262 char *attrname = strchr(name,'.') + 1;
264 if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
266 retval = attr_get(path, attrname, (char *)value, &valuelength, flags);
268 return retval ? retval : valuelength;
269 #elif defined(HAVE_ATTROPEN)
271 int attrfd = solaris_attropen(path, name, O_RDONLY | O_NOFOLLOW, 0);
273 ret = solaris_read_xattr(attrfd, value, size);
283 #if defined(HAVE_EXTATTR_LIST_FILE)
285 #define EXTATTR_PREFIX(s) (s), (sizeof((s))-1)
293 { EXTATTR_NAMESPACE_SYSTEM, EXTATTR_PREFIX("") },
294 { EXTATTR_NAMESPACE_USER, EXTATTR_PREFIX("") },
302 static ssize_t bsd_attr_list (int type, extattr_arg arg, char *list, size_t size)
308 #if defined(HAVE_EXTATTR_LIST_FILE)
310 list_size = extattr_list_file(arg.path, EXTATTR_NAMESPACE_USER, list, size);
313 #if defined(HAVE_EXTATTR_LIST_LINK)
315 list_size = extattr_list_link(arg.path, EXTATTR_NAMESPACE_USER, list, size);
318 #if defined(HAVE_EXTATTR_LIST_FD)
320 list_size = extattr_list_fd(arg.filedes, EXTATTR_NAMESPACE_USER, list, size);
328 /* Some error happend. Errno should be set by the previous call */
336 /* XXX: Call with an empty buffer may be used to calculate
337 necessary buffer size. Unfortunately, we can't say, how
338 many attributes were returned, so here is the potential
339 problem with the emulation.
344 /* Buffer is too small to fit the results */
345 if(list_size > size) {
350 /* Convert from pascal strings to C strings */
352 memmove(list, list + 1, list_size);
354 for(i = len; i < list_size; ) {
355 LOG(log_maxdebug, logtype_afpd, "len: %d, i: %d", len, i);
367 #if defined(HAVE_ATTR_LIST) && defined(HAVE_SYS_ATTRIBUTES_H)
368 static char attr_buffer[ATTR_MAX_VALUELEN];
370 static ssize_t irix_attr_list(const char *path, int filedes, char *list, size_t size, int flags)
372 int retval = 0, index;
373 attrlist_cursor_t *cursor = 0;
375 attrlist_t * al = (attrlist_t *)attr_buffer;
377 size_t ent_size, left = size;
382 retval = attr_listf(filedes, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
384 retval = attr_list(path, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
386 for (index = 0; index < al->al_count; index++) {
387 ae = ATTR_ENTRY(attr_buffer, index);
388 ent_size = strlen(ae->a_name) + sizeof("user.");
389 if (left >= ent_size) {
390 strncpy(bp, "user.", sizeof("user."));
391 strncat(bp, ae->a_name, ent_size - sizeof("user."));
399 total_size += ent_size;
401 if (al->al_more == 0) break;
408 retval = attr_listf(filedes, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
410 retval = attr_list(path, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
412 for (index = 0; index < al->al_count; index++) {
413 ae = ATTR_ENTRY(attr_buffer, index);
414 ent_size = strlen(ae->a_name) + sizeof("system.");
415 if (left >= ent_size) {
416 strncpy(bp, "system.", sizeof("system."));
417 strncat(bp, ae->a_name, ent_size - sizeof("system."));
425 total_size += ent_size;
427 if (al->al_more == 0) break;
430 return (ssize_t)(retval ? retval : total_size);
435 #if defined(HAVE_LISTXATTR)
436 static ssize_t remove_user(ssize_t ret, char *list, size_t size)
443 if (ret <= 0 || size == 0)
447 while (ptrsize > 0) {
448 len = strlen(ptr1) +1;
450 if (strncmp(ptr1, "user.",5)) {
454 memmove(ptr, ptr1 +5, len -5);
462 ssize_t sys_listxattr (const char *path, char *list, size_t size)
464 #if defined(HAVE_LISTXATTR)
467 #ifndef XATTR_ADD_OPT
468 ret = listxattr(path, list, size);
471 ret = listxattr(path, list, size, options);
473 return remove_user(ret, list, size);
475 #elif defined(HAVE_LISTEA)
476 return listea(path, list, size);
477 #elif defined(HAVE_EXTATTR_LIST_FILE)
480 return bsd_attr_list(0, arg, list, size);
481 #elif defined(HAVE_ATTR_LIST) && defined(HAVE_SYS_ATTRIBUTES_H)
482 return irix_attr_list(path, 0, list, size, 0);
483 #elif defined(HAVE_ATTROPEN)
485 int attrdirfd = solaris_attropen(path, ".", O_RDONLY, 0);
486 if (attrdirfd >= 0) {
487 ret = solaris_list_xattr(attrdirfd, list, size);
497 ssize_t sys_llistxattr (const char *path, char *list, size_t size)
499 #if defined(HAVE_LLISTXATTR)
502 ret = llistxattr(path, list, size);
503 return remove_user(ret, list, size);
504 #elif defined(HAVE_LISTXATTR) && defined(XATTR_ADD_OPT)
506 int options = XATTR_NOFOLLOW;
508 ret = listxattr(path, list, size, options);
509 return remove_user(ret, list, size);
511 #elif defined(HAVE_LLISTEA)
512 return llistea(path, list, size);
513 #elif defined(HAVE_EXTATTR_LIST_LINK)
516 return bsd_attr_list(1, arg, list, size);
517 #elif defined(HAVE_ATTR_LIST) && defined(HAVE_SYS_ATTRIBUTES_H)
518 return irix_attr_list(path, 0, list, size, ATTR_DONTFOLLOW);
519 #elif defined(HAVE_ATTROPEN)
521 int attrdirfd = solaris_attropen(path, ".", O_RDONLY | O_NOFOLLOW, 0);
522 if (attrdirfd >= 0) {
523 ret = solaris_list_xattr(attrdirfd, list, size);
533 int sys_removexattr (const char *path, const char *uname)
535 const char *name = prefix(uname);
536 #if defined(HAVE_REMOVEXATTR)
537 #ifndef XATTR_ADD_OPT
538 return removexattr(path, name);
541 return removexattr(path, name, options);
543 #elif defined(HAVE_REMOVEEA)
544 return removeea(path, name);
545 #elif defined(HAVE_EXTATTR_DELETE_FILE)
546 return extattr_delete_file(path, EXTATTR_NAMESPACE_USER, uname);
547 #elif defined(HAVE_ATTR_REMOVE)
549 char *attrname = strchr(name,'.') + 1;
551 if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
553 return attr_remove(path, attrname, flags);
554 #elif defined(HAVE_ATTROPEN)
556 int attrdirfd = solaris_attropen(path, ".", O_RDONLY, 0);
557 if (attrdirfd >= 0) {
558 ret = solaris_unlinkat(attrdirfd, name);
568 int sys_lremovexattr (const char *path, const char *uname)
570 const char *name = prefix(uname);
571 #if defined(HAVE_LREMOVEXATTR)
572 return lremovexattr(path, name);
573 #elif defined(HAVE_REMOVEXATTR) && defined(XATTR_ADD_OPT)
574 int options = XATTR_NOFOLLOW;
575 return removexattr(path, name, options);
576 #elif defined(HAVE_LREMOVEEA)
577 return lremoveea(path, name);
578 #elif defined(HAVE_EXTATTR_DELETE_LINK)
579 return extattr_delete_link(path, EXTATTR_NAMESPACE_USER, uname);
580 #elif defined(HAVE_ATTR_REMOVE)
581 int flags = ATTR_DONTFOLLOW;
582 char *attrname = strchr(name,'.') + 1;
584 if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
586 return attr_remove(path, attrname, flags);
587 #elif defined(HAVE_ATTROPEN)
589 int attrdirfd = solaris_attropen(path, ".", O_RDONLY | O_NOFOLLOW, 0);
590 if (attrdirfd >= 0) {
591 ret = solaris_unlinkat(attrdirfd, name);
601 int sys_setxattr (const char *path, const char *uname, const void *value, size_t size, int flags)
603 const char *name = prefix(uname);
604 #if defined(HAVE_SETXATTR)
605 #ifndef XATTR_ADD_OPT
606 return setxattr(path, name, value, size, flags);
609 return setxattr(path, name, value, size, 0, options);
611 #elif defined(HAVE_SETEA)
612 return setea(path, name, value, size, flags);
613 #elif defined(HAVE_EXTATTR_SET_FILE)
616 /* Check attribute existence */
617 retval = extattr_get_file(path, EXTATTR_NAMESPACE_USER, uname, NULL, 0);
619 /* REPLACE attribute, that doesn't exist */
620 if (flags & XATTR_REPLACE && errno == ENOATTR) {
624 /* Ignore other errors */
627 /* CREATE attribute, that already exists */
628 if (flags & XATTR_CREATE) {
634 retval = extattr_set_file(path, EXTATTR_NAMESPACE_USER, uname, value, size);
635 return (retval < 0) ? -1 : 0;
636 #elif defined(HAVE_ATTR_SET)
638 char *attrname = strchr(name,'.') + 1;
640 if (strncmp(name, "system", 6) == 0) myflags |= ATTR_ROOT;
641 if (flags & XATTR_CREATE) myflags |= ATTR_CREATE;
642 if (flags & XATTR_REPLACE) myflags |= ATTR_REPLACE;
644 return attr_set(path, attrname, (const char *)value, size, myflags);
645 #elif defined(HAVE_ATTROPEN)
647 int myflags = O_RDWR;
649 if (flags & XATTR_CREATE) myflags |= O_EXCL;
650 if (!(flags & XATTR_REPLACE)) myflags |= O_CREAT;
651 attrfd = solaris_attropen(path, name, myflags, (mode_t) SOLARIS_ATTRMODE);
653 ret = solaris_write_xattr(attrfd, value, size);
663 int sys_fsetxattr (int filedes, const char *uname, const void *value, size_t size, int flags)
665 const char *name = prefix(uname);
667 #if defined(HAVE_FSETXATTR)
668 #ifndef XATTR_ADD_OPT
669 return fsetxattr(filedes, name, value, size, flags);
672 return fsetxattr(filedes, name, value, size, 0, options);
674 #elif defined(HAVE_FSETEA)
675 return fsetea(filedes, name, value, size, flags);
676 #elif defined(HAVE_EXTATTR_SET_FD)
679 int attrnamespace = (strncmp(name, "system", 6) == 0) ?
680 EXTATTR_NAMESPACE_SYSTEM : EXTATTR_NAMESPACE_USER;
681 const char *attrname = ((s=strchr(name, '.')) == NULL) ? name : s + 1;
683 /* Check attribute existence */
684 retval = extattr_get_fd(filedes, attrnamespace, attrname, NULL, 0);
686 /* REPLACE attribute, that doesn't exist */
687 if (flags & XATTR_REPLACE && errno == ENOATTR) {
691 /* Ignore other errors */
694 if (flags & XATTR_CREATE) {
700 retval = extattr_set_fd(filedes, attrnamespace, attrname, value, size);
701 return (retval < 0) ? -1 : 0;
702 #elif defined(HAVE_ATTR_SETF)
704 char *attrname = strchr(name,'.') + 1;
706 if (strncmp(name, "system", 6) == 0) myflags |= ATTR_ROOT;
707 if (flags & XATTR_CREATE) myflags |= ATTR_CREATE;
708 if (flags & XATTR_REPLACE) myflags |= ATTR_REPLACE;
710 return attr_setf(filedes, attrname, (const char *)value, size, myflags);
711 #elif defined(HAVE_ATTROPEN)
713 int myflags = O_RDWR | O_XATTR;
715 if (flags & XATTR_CREATE) myflags |= O_EXCL;
716 if (!(flags & XATTR_REPLACE)) myflags |= O_CREAT;
717 attrfd = solaris_openat(filedes, name, myflags, (mode_t) SOLARIS_ATTRMODE);
719 ret = solaris_write_xattr(attrfd, value, size);
729 int sys_lsetxattr (const char *path, const char *uname, const void *value, size_t size, int flags)
731 const char *name = prefix(uname);
732 #if defined(HAVE_LSETXATTR)
733 return lsetxattr(path, name, value, size, flags);
734 #elif defined(HAVE_SETXATTR) && defined(XATTR_ADD_OPT)
735 int options = XATTR_NOFOLLOW;
736 return setxattr(path, name, value, size, 0, options);
737 #elif defined(LSETEA)
738 return lsetea(path, name, value, size, flags);
739 #elif defined(HAVE_EXTATTR_SET_LINK)
742 /* Check attribute existence */
743 retval = extattr_get_link(path, EXTATTR_NAMESPACE_USER, uname, NULL, 0);
745 /* REPLACE attribute, that doesn't exist */
746 if (flags & XATTR_REPLACE && errno == ENOATTR) {
750 /* Ignore other errors */
753 /* CREATE attribute, that already exists */
754 if (flags & XATTR_CREATE) {
761 retval = extattr_set_link(path, EXTATTR_NAMESPACE_USER, uname, value, size);
762 return (retval < 0) ? -1 : 0;
763 #elif defined(HAVE_ATTR_SET)
764 int myflags = ATTR_DONTFOLLOW;
765 char *attrname = strchr(name,'.') + 1;
767 if (strncmp(name, "system", 6) == 0) myflags |= ATTR_ROOT;
768 if (flags & XATTR_CREATE) myflags |= ATTR_CREATE;
769 if (flags & XATTR_REPLACE) myflags |= ATTR_REPLACE;
771 return attr_set(path, attrname, (const char *)value, size, myflags);
772 #elif defined(HAVE_ATTROPEN)
774 int myflags = O_RDWR | O_NOFOLLOW;
776 if (flags & XATTR_CREATE) myflags |= O_EXCL;
777 if (!(flags & XATTR_REPLACE)) myflags |= O_CREAT;
778 attrfd = solaris_attropen(path, name, myflags, (mode_t) SOLARIS_ATTRMODE);
780 ret = solaris_write_xattr(attrfd, value, size);
790 /**************************************************************************
791 helper functions for Solaris' EA support
792 ****************************************************************************/
794 static ssize_t solaris_read_xattr(int attrfd, void *value, size_t size)
798 if (fstat(attrfd, &sbuf) == -1) {
802 /* This is to return the current size of the named extended attribute */
807 /* check size and read xattr */
808 if (sbuf.st_size > size) {
812 return read(attrfd, value, sbuf.st_size);
815 static ssize_t solaris_list_xattr(int attrdirfd, char *list, size_t size)
820 int newfd = dup(attrdirfd);
821 /* CAUTION: The originating file descriptor should not be
822 used again following the call to fdopendir().
823 For that reason we dup() the file descriptor
824 here to make things more clear. */
825 dirp = fdopendir(newfd);
827 while ((de = readdir(dirp))) {
829 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..") ||
830 !strcmp(de->d_name, "SUNWattr_ro") || !strcmp(de->d_name, "SUNWattr_rw"))
832 /* we don't want "." and ".." here: */
833 LOG(log_maxdebug, logtype_default, "skipped EA %s\n",de->d_name);
837 listlen = strlen(de->d_name);
839 /* return the current size of the list of extended attribute names*/
842 /* check size and copy entry + nul into list. */
843 if ((len + listlen + 1) > size) {
848 strcpy(list + len, de->d_name);
856 if (closedir(dirp) == -1) {
857 LOG(log_error, logtype_default, "closedir dirp: %s",strerror(errno));
863 static int solaris_unlinkat(int attrdirfd, const char *name)
865 if (unlinkat(attrdirfd, name, 0) == -1) {
871 static int solaris_attropen(const char *path, const char *attrpath, int oflag, mode_t mode)
874 int filedes = -1, eafd = -1;
876 if ((filedes = open(path, O_RDONLY | (oflag & O_NOFOLLOW), mode)) == -1) {
880 case OPEN_NOFOLLOW_ERRNO:
883 LOG(log_debug, logtype_default, "open(\"%s\"): %s", fullpathname(path), strerror(errno));
888 if ((eafd = openat(filedes, attrpath, oflag | O_XATTR, mode)) == -1) {
894 LOG(log_debug, logtype_default, "openat(\"%s\"): %s", fullpathname(path), strerror(errno));
910 static int solaris_openat(int fildes, const char *path, int oflag, mode_t mode)
914 if ((filedes = openat(fildes, path, oflag, mode)) == -1) {
921 LOG(log_debug, logtype_default, "openat(\"%s\"): %s",
922 path, strerror(errno));
930 static int solaris_write_xattr(int attrfd, const char *value, size_t size)
932 if ((ftruncate(attrfd, 0) == 0) && (write(attrfd, value, size) == size)) {
935 LOG(log_error, logtype_default, "solaris_write_xattr: %s",
941 #endif /*HAVE_ATTROPEN*/