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>
64 /******** Solaris EA helper function prototypes ********/
66 #define SOLARIS_ATTRMODE S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP
67 static int solaris_write_xattr(int attrfd, const char *value, size_t size);
68 static ssize_t solaris_read_xattr(int attrfd, void *value, size_t size);
69 static ssize_t solaris_list_xattr(int attrdirfd, char *list, size_t size);
70 static int solaris_unlinkat(int attrdirfd, const char *name);
71 static int solaris_attropen(const char *path, const char *attrpath, int oflag, mode_t mode);
72 static int solaris_openat(int fildes, const char *path, int oflag, mode_t mode);
75 /**************************************************************************
76 Wrappers for extented attribute calls. Based on the Linux package with
77 support for IRIX and (Net|Free)BSD also. Expand as other systems have them.
78 ****************************************************************************/
79 static char attr_name[256 +5] = "user.";
81 static const char *prefix(const char *uname)
83 #if defined(HAVE_ATTROPEN)
86 strlcpy(attr_name +5, uname, 256);
91 ssize_t sys_getxattr (const char *path, const char *uname, void *value, size_t size)
93 const char *name = prefix(uname);
95 #if defined(HAVE_GETXATTR)
97 return getxattr(path, name, value, size);
100 return getxattr(path, name, value, size, 0, options);
102 #elif defined(HAVE_GETEA)
103 return getea(path, name, value, size);
104 #elif defined(HAVE_EXTATTR_GET_FILE)
107 * The BSD implementation has a nasty habit of silently truncating
108 * the returned value to the size of the buffer, so we have to check
109 * that the buffer is large enough to fit the returned value.
111 if((retval = extattr_get_file(path, EXTATTR_NAMESPACE_USER, uname, NULL, 0)) >= 0) {
113 /* size == 0 means only return size */
119 if ((retval = extattr_get_file(path, EXTATTR_NAMESPACE_USER, uname, value, size)) >= 0)
123 LOG(log_maxdebug, logtype_default, "sys_getxattr: extattr_get_file() failed with: %s\n", strerror(errno));
125 #elif defined(HAVE_ATTR_GET)
126 int retval, flags = 0;
127 int valuelength = (int)size;
128 char *attrname = strchr(name,'.') + 1;
130 if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
132 retval = attr_get(path, attrname, (char *)value, &valuelength, flags);
134 return retval ? retval : valuelength;
135 #elif defined(HAVE_ATTROPEN)
137 int attrfd = solaris_attropen(path, name, O_RDONLY, 0);
139 ret = solaris_read_xattr(attrfd, value, size);
149 ssize_t sys_fgetxattr (int filedes, const char *uname, void *value, size_t size)
151 const char *name = prefix(uname);
153 #if defined(HAVE_FGETXATTR)
154 #ifndef XATTR_ADD_OPT
155 return fgetxattr(filedes, name, value, size);
158 return fgetxattr(filedes, name, value, size, 0, options);
160 #elif defined(HAVE_FGETEA)
161 return fgetea(filedes, name, value, size);
162 #elif defined(HAVE_EXTATTR_GET_FD)
165 int attrnamespace = (strncmp(name, "system", 6) == 0) ?
166 EXTATTR_NAMESPACE_SYSTEM : EXTATTR_NAMESPACE_USER;
167 const char *attrname = ((s=strchr_m(name, '.')) == NULL) ? name : s + 1;
169 if((retval=extattr_get_fd(filedes, attrnamespace, attrname, NULL, 0)) >= 0) {
174 if((retval=extattr_get_fd(filedes, attrnamespace, attrname, value, size)) >= 0)
178 LOG(log_debug, logtype_default, "sys_fgetxattr: extattr_get_fd(): %s",
181 #elif defined(HAVE_ATTR_GETF)
182 int retval, flags = 0;
183 int valuelength = (int)size;
184 char *attrname = strchr(name,'.') + 1;
186 if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
188 retval = attr_getf(filedes, attrname, (char *)value, &valuelength, flags);
190 return retval ? retval : valuelength;
191 #elif defined(HAVE_ATTROPEN)
193 int attrfd = solaris_openat(filedes, name, O_RDONLY|O_XATTR, 0);
195 ret = solaris_read_xattr(attrfd, value, size);
205 ssize_t sys_lgetxattr (const char *path, const char *uname, void *value, size_t size)
207 const char *name = prefix(uname);
209 #if defined(HAVE_LGETXATTR)
210 return lgetxattr(path, name, value, size);
211 #elif defined(HAVE_GETXATTR) && defined(XATTR_ADD_OPT)
212 int options = XATTR_NOFOLLOW;
213 return getxattr(path, name, value, size, 0, options);
214 #elif defined(HAVE_LGETEA)
215 return lgetea(path, name, value, size);
216 #elif defined(HAVE_EXTATTR_GET_LINK)
218 if((retval=extattr_get_link(path, EXTATTR_NAMESPACE_USER, uname, NULL, 0)) >= 0) {
223 if((retval=extattr_get_link(path, EXTATTR_NAMESPACE_USER, uname, value, size)) >= 0)
227 LOG(log_maxdebug, logtype_default, "sys_lgetxattr: extattr_get_link() failed with: %s\n", strerror(errno));
229 #elif defined(HAVE_ATTR_GET)
230 int retval, flags = ATTR_DONTFOLLOW;
231 int valuelength = (int)size;
232 char *attrname = strchr(name,'.') + 1;
234 if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
236 retval = attr_get(path, attrname, (char *)value, &valuelength, flags);
238 return retval ? retval : valuelength;
239 #elif defined(HAVE_ATTROPEN)
241 int attrfd = solaris_attropen(path, name, O_RDONLY|AT_SYMLINK_NOFOLLOW, 0);
243 ret = solaris_read_xattr(attrfd, value, size);
253 #if defined(HAVE_EXTATTR_LIST_FILE)
255 #define EXTATTR_PREFIX(s) (s), (sizeof((s))-1)
263 { EXTATTR_NAMESPACE_SYSTEM, EXTATTR_PREFIX("") },
264 { EXTATTR_NAMESPACE_USER, EXTATTR_PREFIX("") },
272 static ssize_t bsd_attr_list (int type, extattr_arg arg, char *list, size_t size)
278 #if defined(HAVE_EXTATTR_LIST_FILE)
280 list_size = extattr_list_file(arg.path, EXTATTR_NAMESPACE_USER, list, size);
283 #if defined(HAVE_EXTATTR_LIST_LINK)
285 list_size = extattr_list_link(arg.path, EXTATTR_NAMESPACE_USER, list, size);
288 #if defined(HAVE_EXTATTR_LIST_FD)
290 list_size = extattr_list_fd(arg.filedes, EXTATTR_NAMESPACE_USER, list, size);
298 /* Some error happend. Errno should be set by the previous call */
306 /* XXX: Call with an empty buffer may be used to calculate
307 necessary buffer size. Unfortunately, we can't say, how
308 many attributes were returned, so here is the potential
309 problem with the emulation.
314 /* Buffer is too small to fit the results */
315 if(list_size > size) {
320 /* Convert from pascal strings to C strings */
322 memmove(list, list + 1, list_size);
324 for(i = len; i < list_size; ) {
325 LOG(log_maxdebug, logtype_afpd, "len: %d, i: %d", len, i);
337 #if defined(HAVE_ATTR_LIST) && defined(HAVE_SYS_ATTRIBUTES_H)
338 static char attr_buffer[ATTR_MAX_VALUELEN];
340 static ssize_t irix_attr_list(const char *path, int filedes, char *list, size_t size, int flags)
342 int retval = 0, index;
343 attrlist_cursor_t *cursor = 0;
345 attrlist_t * al = (attrlist_t *)attr_buffer;
347 size_t ent_size, left = size;
352 retval = attr_listf(filedes, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
354 retval = attr_list(path, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
356 for (index = 0; index < al->al_count; index++) {
357 ae = ATTR_ENTRY(attr_buffer, index);
358 ent_size = strlen(ae->a_name) + sizeof("user.");
359 if (left >= ent_size) {
360 strncpy(bp, "user.", sizeof("user."));
361 strncat(bp, ae->a_name, ent_size - sizeof("user."));
369 total_size += ent_size;
371 if (al->al_more == 0) break;
378 retval = attr_listf(filedes, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
380 retval = attr_list(path, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
382 for (index = 0; index < al->al_count; index++) {
383 ae = ATTR_ENTRY(attr_buffer, index);
384 ent_size = strlen(ae->a_name) + sizeof("system.");
385 if (left >= ent_size) {
386 strncpy(bp, "system.", sizeof("system."));
387 strncat(bp, ae->a_name, ent_size - sizeof("system."));
395 total_size += ent_size;
397 if (al->al_more == 0) break;
400 return (ssize_t)(retval ? retval : total_size);
405 #if defined(HAVE_LISTXATTR)
406 static ssize_t remove_user(ssize_t ret, char *list, size_t size)
413 if (ret <= 0 || size == 0)
417 while (ptrsize > 0) {
418 len = strlen(ptr1) +1;
420 if (strncmp(ptr1, "user.",5)) {
424 memmove(ptr, ptr1 +5, len -5);
432 ssize_t sys_listxattr (const char *path, char *list, size_t size)
434 #if defined(HAVE_LISTXATTR)
437 #ifndef XATTR_ADD_OPT
438 ret = listxattr(path, list, size);
441 ret = listxattr(path, list, size, options);
443 return remove_user(ret, list, size);
445 #elif defined(HAVE_LISTEA)
446 return listea(path, list, size);
447 #elif defined(HAVE_EXTATTR_LIST_FILE)
450 return bsd_attr_list(0, arg, list, size);
451 #elif defined(HAVE_ATTR_LIST) && defined(HAVE_SYS_ATTRIBUTES_H)
452 return irix_attr_list(path, 0, list, size, 0);
453 #elif defined(HAVE_ATTROPEN)
455 int attrdirfd = solaris_attropen(path, ".", O_RDONLY, 0);
456 if (attrdirfd >= 0) {
457 ret = solaris_list_xattr(attrdirfd, list, size);
467 ssize_t sys_llistxattr (const char *path, char *list, size_t size)
469 #if defined(HAVE_LLISTXATTR)
472 ret = llistxattr(path, list, size);
473 return remove_user(ret, list, size);
474 #elif defined(HAVE_LISTXATTR) && defined(XATTR_ADD_OPT)
476 int options = XATTR_NOFOLLOW;
478 ret = listxattr(path, list, size, options);
479 return remove_user(ret, list, size);
481 #elif defined(HAVE_LLISTEA)
482 return llistea(path, list, size);
483 #elif defined(HAVE_EXTATTR_LIST_LINK)
486 return bsd_attr_list(1, arg, list, size);
487 #elif defined(HAVE_ATTR_LIST) && defined(HAVE_SYS_ATTRIBUTES_H)
488 return irix_attr_list(path, 0, list, size, ATTR_DONTFOLLOW);
489 #elif defined(HAVE_ATTROPEN)
491 int attrdirfd = solaris_attropen(path, ".", O_RDONLY|AT_SYMLINK_NOFOLLOW, 0);
492 if (attrdirfd >= 0) {
493 ret = solaris_list_xattr(attrdirfd, list, size);
503 int sys_removexattr (const char *path, const char *uname)
505 const char *name = prefix(uname);
506 #if defined(HAVE_REMOVEXATTR)
507 #ifndef XATTR_ADD_OPT
508 return removexattr(path, name);
511 return removexattr(path, name, options);
513 #elif defined(HAVE_REMOVEEA)
514 return removeea(path, name);
515 #elif defined(HAVE_EXTATTR_DELETE_FILE)
516 return extattr_delete_file(path, EXTATTR_NAMESPACE_USER, uname);
517 #elif defined(HAVE_ATTR_REMOVE)
519 char *attrname = strchr(name,'.') + 1;
521 if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
523 return attr_remove(path, attrname, flags);
524 #elif defined(HAVE_ATTROPEN)
526 int attrdirfd = solaris_attropen(path, ".", O_RDONLY, 0);
527 if (attrdirfd >= 0) {
528 ret = solaris_unlinkat(attrdirfd, name);
538 int sys_lremovexattr (const char *path, const char *uname)
540 const char *name = prefix(uname);
541 #if defined(HAVE_LREMOVEXATTR)
542 return lremovexattr(path, name);
543 #elif defined(HAVE_REMOVEXATTR) && defined(XATTR_ADD_OPT)
544 int options = XATTR_NOFOLLOW;
545 return removexattr(path, name, options);
546 #elif defined(HAVE_LREMOVEEA)
547 return lremoveea(path, name);
548 #elif defined(HAVE_EXTATTR_DELETE_LINK)
549 return extattr_delete_link(path, EXTATTR_NAMESPACE_USER, uname);
550 #elif defined(HAVE_ATTR_REMOVE)
551 int flags = ATTR_DONTFOLLOW;
552 char *attrname = strchr(name,'.') + 1;
554 if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
556 return attr_remove(path, attrname, flags);
557 #elif defined(HAVE_ATTROPEN)
559 int attrdirfd = solaris_attropen(path, ".", O_RDONLY|AT_SYMLINK_NOFOLLOW, 0);
560 if (attrdirfd >= 0) {
561 ret = solaris_unlinkat(attrdirfd, name);
571 int sys_setxattr (const char *path, const char *uname, const void *value, size_t size, int flags)
573 const char *name = prefix(uname);
574 #if defined(HAVE_SETXATTR)
575 #ifndef XATTR_ADD_OPT
576 return setxattr(path, name, value, size, flags);
579 return setxattr(path, name, value, size, 0, options);
581 #elif defined(HAVE_SETEA)
582 return setea(path, name, value, size, flags);
583 #elif defined(HAVE_EXTATTR_SET_FILE)
586 /* Check attribute existence */
587 retval = extattr_get_file(path, EXTATTR_NAMESPACE_USER, uname, NULL, 0);
589 /* REPLACE attribute, that doesn't exist */
590 if (flags & XATTR_REPLACE && errno == ENOATTR) {
594 /* Ignore other errors */
597 /* CREATE attribute, that already exists */
598 if (flags & XATTR_CREATE) {
604 retval = extattr_set_file(path, EXTATTR_NAMESPACE_USER, uname, value, size);
605 return (retval < 0) ? -1 : 0;
606 #elif defined(HAVE_ATTR_SET)
608 char *attrname = strchr(name,'.') + 1;
610 if (strncmp(name, "system", 6) == 0) myflags |= ATTR_ROOT;
611 if (flags & XATTR_CREATE) myflags |= ATTR_CREATE;
612 if (flags & XATTR_REPLACE) myflags |= ATTR_REPLACE;
614 return attr_set(path, attrname, (const char *)value, size, myflags);
615 #elif defined(HAVE_ATTROPEN)
617 int myflags = O_RDWR;
619 if (flags & XATTR_CREATE) myflags |= O_EXCL;
620 if (!(flags & XATTR_REPLACE)) myflags |= O_CREAT;
621 attrfd = solaris_attropen(path, name, myflags, (mode_t) SOLARIS_ATTRMODE);
623 ret = solaris_write_xattr(attrfd, value, size);
633 int sys_fsetxattr (int filedes, const char *uname, const void *value, size_t size, int flags)
635 const char *name = prefix(uname);
637 #if defined(HAVE_FSETXATTR)
638 #ifndef XATTR_ADD_OPT
639 return fsetxattr(filedes, name, value, size, flags);
642 return fsetxattr(filedes, name, value, size, 0, options);
644 #elif defined(HAVE_FSETEA)
645 return fsetea(filedes, name, value, size, flags);
646 #elif defined(HAVE_EXTATTR_SET_FD)
649 int attrnamespace = (strncmp(name, "system", 6) == 0) ?
650 EXTATTR_NAMESPACE_SYSTEM : EXTATTR_NAMESPACE_USER;
651 const char *attrname = ((s=strchr_m(name, '.')) == NULL) ? name : s + 1;
653 /* Check attribute existence */
654 retval = extattr_get_fd(filedes, attrnamespace, attrname, NULL, 0);
656 /* REPLACE attribute, that doesn't exist */
657 if (flags & XATTR_REPLACE && errno == ENOATTR) {
661 /* Ignore other errors */
664 log_error, logtype_default /* CREATE attribute, that already exists */
665 if (flags & XATTR_CREATE) {
671 retval = extattr_set_fd(filedes, attrnamespace, attrname, value, size);
672 return (retval < 0) ? -1 : 0;
673 #elif defined(HAVE_ATTR_SETF)
675 char *attrname = strchr(name,'.') + 1;
677 if (strncmp(name, "system", 6) == 0) myflags |= ATTR_ROOT;
678 if (flags & XATTR_CREATE) myflags |= ATTR_CREATE;
679 if (flags & XATTR_REPLACE) myflags |= ATTR_REPLACE;
681 return attr_setf(filedes, attrname, (const char *)value, size, myflags);
682 #elif defined(HAVE_ATTROPEN)
684 int myflags = O_RDWR | O_XATTR;
686 if (flags & XATTR_CREATE) myflags |= O_EXCL;
687 if (!(flags & XATTR_REPLACE)) myflags |= O_CREAT;
688 attrfd = solaris_openat(filedes, name, myflags, (mode_t) SOLARIS_ATTRMODE);
690 ret = solaris_write_xattr(attrfd, value, size);
700 int sys_lsetxattr (const char *path, const char *uname, const void *value, size_t size, int flags)
702 const char *name = prefix(uname);
703 #if defined(HAVE_LSETXATTR)
704 return lsetxattr(path, name, value, size, flags);
705 #elif defined(HAVE_SETXATTR) && defined(XATTR_ADD_OPT)
706 int options = XATTR_NOFOLLOW;
707 return setxattr(path, name, value, size, 0, options);
708 #elif defined(LSETEA)
709 return lsetea(path, name, value, size, flags);
710 #elif defined(HAVE_EXTATTR_SET_LINK)
713 /* Check attribute existence */
714 retval = extattr_get_link(path, EXTATTR_NAMESPACE_USER, uname, NULL, 0);
716 /* REPLACE attribute, that doesn't exist */
717 if (flags & XATTR_REPLACE && errno == ENOATTR) {
721 /* Ignore other errors */
724 /* CREATE attribute, that already exists */
725 if (flags & XATTR_CREATE) {
732 retval = extattr_set_link(path, EXTATTR_NAMESPACE_USER, uname, value, size);
733 return (retval < 0) ? -1 : 0;
734 #elif defined(HAVE_ATTR_SET)
735 int myflags = ATTR_DONTFOLLOW;
736 char *attrname = strchr(name,'.') + 1;
738 if (strncmp(name, "system", 6) == 0) myflags |= ATTR_ROOT;
739 if (flags & XATTR_CREATE) myflags |= ATTR_CREATE;
740 if (flags & XATTR_REPLACE) myflags |= ATTR_REPLACE;
742 return attr_set(path, attrname, (const char *)value, size, myflags);
743 #elif defined(HAVE_ATTROPEN)
745 int myflags = O_RDWR | AT_SYMLINK_NOFOLLOW;
747 if (flags & XATTR_CREATE) myflags |= O_EXCL;
748 if (!(flags & XATTR_REPLACE)) myflags |= O_CREAT;
749 attrfd = solaris_attropen(path, name, myflags, (mode_t) SOLARIS_ATTRMODE);
751 ret = solaris_write_xattr(attrfd, value, size);
761 /**************************************************************************
762 helper functions for Solaris' EA support
763 ****************************************************************************/
765 static ssize_t solaris_read_xattr(int attrfd, void *value, size_t size)
769 if (fstat(attrfd, &sbuf) == -1) {
773 /* This is to return the current size of the named extended attribute */
778 /* check size and read xattr */
779 if (sbuf.st_size > size) {
783 return read(attrfd, value, sbuf.st_size);
786 static ssize_t solaris_list_xattr(int attrdirfd, char *list, size_t size)
791 int newfd = dup(attrdirfd);
792 /* CAUTION: The originating file descriptor should not be
793 used again following the call to fdopendir().
794 For that reason we dup() the file descriptor
795 here to make things more clear. */
796 dirp = fdopendir(newfd);
798 while ((de = readdir(dirp))) {
800 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..") ||
801 !strcmp(de->d_name, "SUNWattr_ro") || !strcmp(de->d_name, "SUNWattr_rw"))
803 /* we don't want "." and ".." here: */
804 LOG(log_maxdebug, logtype_default, "skipped EA %s\n",de->d_name);
808 listlen = strlen(de->d_name);
810 /* return the current size of the list of extended attribute names*/
813 /* check size and copy entry + nul into list. */
814 if ((len + listlen + 1) > size) {
819 strcpy(list + len, de->d_name);
827 if (closedir(dirp) == -1) {
828 LOG(log_debug, logtype_default, "closedir dirp failed: %s\n",strerror(errno));
834 static int solaris_unlinkat(int attrdirfd, const char *name)
836 if (unlinkat(attrdirfd, name, 0) == -1) {
842 static int solaris_attropen(const char *path, const char *attrpath, int oflag, mode_t mode)
844 int filedes = attropen(path, attrpath, oflag, mode);
846 LOG(log_maxdebug, logtype_default, "attropen FAILED: path: %s, name: %s, errno: %s",
847 path, attrpath, strerror(errno));
853 static int solaris_openat(int fildes, const char *path, int oflag, mode_t mode)
855 int filedes = openat(fildes, path, oflag, mode);
857 LOG(log_maxdebug, logtype_default, "openat FAILED: fd: %d, path: %s, errno: %s",
858 filedes, path, strerror(errno));
863 static int solaris_write_xattr(int attrfd, const char *value, size_t size)
865 if ((ftruncate(attrfd, 0) == 0) && (write(attrfd, value, size) == size)) {
868 LOG(log_maxdebug, logtype_default, "solaris_write_xattr FAILED!");
873 #endif /*HAVE_ATTROPEN*/