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.
28 $Id: sys_ea.c,v 1.3 2009-11-18 11:30:03 didg Exp $
38 #include <sys/types.h>
42 #include <attr/xattr.h>
43 #elif HAVE_SYS_XATTR_H
44 #include <sys/xattr.h>
56 #ifdef HAVE_SYS_EXTATTR_H
57 #include <sys/extattr.h>
60 #include <atalk/adouble.h>
61 #include <atalk/util.h>
62 #include <atalk/logger.h>
65 /******** Solaris EA helper function prototypes ********/
67 #define SOLARIS_ATTRMODE S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP
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);
74 static int solaris_copy_xattr(const char *src, const char *dst );
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.";
83 static const char *prefix(const char *uname)
85 #if defined(HAVE_ATTROPEN)
88 strlcpy(attr_name +5, uname, 256);
93 ssize_t sys_getxattr (const char *path, const char *uname, void *value, size_t size)
95 const char *name = prefix(uname);
97 #if defined(HAVE_GETXATTR)
99 return getxattr(path, name, value, size);
102 return getxattr(path, name, value, size, 0, options);
104 #elif defined(HAVE_GETEA)
105 return getea(path, name, value, size);
106 #elif defined(HAVE_EXTATTR_GET_FILE)
109 int attrnamespace = (strncmp(name, "system", 6) == 0) ?
110 EXTATTR_NAMESPACE_SYSTEM : EXTATTR_NAMESPACE_USER;
111 const char *attrname = ((s=strchr_m(name, '.')) == NULL) ? name : s + 1;
113 * The BSD implementation has a nasty habit of silently truncating
114 * the returned value to the size of the buffer, so we have to check
115 * that the buffer is large enough to fit the returned value.
117 if((retval=extattr_get_file(path, attrnamespace, attrname, NULL, 0)) >= 0) {
122 if((retval=extattr_get_file(path, attrnamespace, attrname, value, size)) >= 0)
126 LOG(log_maxdebug, logtype_default, "sys_getxattr: extattr_get_file() failed with: %s\n", strerror(errno));
128 #elif defined(HAVE_ATTR_GET)
129 int retval, flags = 0;
130 int valuelength = (int)size;
131 char *attrname = strchr(name,'.') + 1;
133 if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
135 retval = attr_get(path, attrname, (char *)value, &valuelength, flags);
137 return retval ? retval : valuelength;
138 #elif defined(HAVE_ATTROPEN)
140 int attrfd = solaris_attropen(path, name, O_RDONLY, 0);
142 ret = solaris_read_xattr(attrfd, value, size);
152 ssize_t sys_lgetxattr (const char *path, const char *uname, void *value, size_t size)
154 const char *name = prefix(uname);
156 #if defined(HAVE_LGETXATTR)
157 return lgetxattr(path, name, value, size);
158 #elif defined(HAVE_GETXATTR) && defined(XATTR_ADD_OPT)
159 int options = XATTR_NOFOLLOW;
160 return getxattr(path, name, value, size, 0, options);
161 #elif defined(HAVE_LGETEA)
162 return lgetea(path, name, value, size);
163 #elif defined(HAVE_EXTATTR_GET_LINK)
166 int attrnamespace = (strncmp(name, "system", 6) == 0) ?
167 EXTATTR_NAMESPACE_SYSTEM : EXTATTR_NAMESPACE_USER;
168 const char *attrname = ((s=strchr_m(name, '.')) == NULL) ? name : s + 1;
170 if((retval=extattr_get_link(path, attrnamespace, attrname, NULL, 0)) >= 0) {
175 if((retval=extattr_get_link(path, attrnamespace, attrname, value, size)) >= 0)
179 LOG(log_maxdebug, logtype_default, "sys_lgetxattr: extattr_get_link() failed with: %s\n", strerror(errno));
181 #elif defined(HAVE_ATTR_GET)
182 int retval, flags = ATTR_DONTFOLLOW;
183 int valuelength = (int)size;
184 char *attrname = strchr(name,'.') + 1;
186 if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
188 retval = attr_get(path, attrname, (char *)value, &valuelength, flags);
190 return retval ? retval : valuelength;
191 #elif defined(HAVE_ATTROPEN)
193 int attrfd = solaris_attropen(path, name, O_RDONLY|AT_SYMLINK_NOFOLLOW, 0);
195 ret = solaris_read_xattr(attrfd, value, size);
205 #if defined(HAVE_EXTATTR_LIST_FILE)
207 #define EXTATTR_PREFIX(s) (s), (sizeof((s))-1)
215 { EXTATTR_NAMESPACE_SYSTEM, EXTATTR_PREFIX("system.") },
216 { EXTATTR_NAMESPACE_USER, EXTATTR_PREFIX("user.") },
224 static ssize_t bsd_attr_list (int type, extattr_arg arg, char *list, size_t size)
226 ssize_t list_size, total_size = 0;
229 /* Iterate through extattr(2) namespaces */
230 for(t = 0; t < (sizeof(extattr)/sizeof(extattr[0])); t++) {
232 #if defined(HAVE_EXTATTR_LIST_FILE)
234 list_size = extattr_list_file(arg.path, extattr[t].space, list, size);
237 #if defined(HAVE_EXTATTR_LIST_LINK)
239 list_size = extattr_list_link(arg.path, extattr[t].space, list, size);
242 #if defined(HAVE_EXTATTR_LIST_FD)
244 list_size = extattr_list_fd(arg.filedes, extattr[t].space, list, size);
251 /* Some error happend. Errno should be set by the previous call */
257 /* XXX: Call with an empty buffer may be used to calculate
258 necessary buffer size. Unfortunately, we can't say, how
259 many attributes were returned, so here is the potential
260 problem with the emulation.
263 /* Take the worse case of one char attribute names -
264 two bytes per name plus one more for sanity.
266 total_size += list_size + (list_size/2 + 1)*extattr[t].len;
269 /* Count necessary offset to fit namespace prefixes */
271 for(i = 0; i < list_size; i += list[i] + 1)
272 len += extattr[t].len;
274 total_size += list_size + len;
275 /* Buffer is too small to fit the results */
276 if(total_size > size) {
280 /* Shift results back, so we can prepend prefixes */
281 buf = memmove(list + len, list, list_size);
283 for(i = 0; i < list_size; i += len + 1) {
285 strncpy(list, extattr[t].name, extattr[t].len + 1);
286 list += extattr[t].len;
287 strncpy(list, buf + i + 1, len);
298 #if defined(HAVE_ATTR_LIST) && defined(HAVE_SYS_ATTRIBUTES_H)
299 static char attr_buffer[ATTR_MAX_VALUELEN];
301 static ssize_t irix_attr_list(const char *path, int filedes, char *list, size_t size, int flags)
303 int retval = 0, index;
304 attrlist_cursor_t *cursor = 0;
306 attrlist_t * al = (attrlist_t *)attr_buffer;
308 size_t ent_size, left = size;
313 retval = attr_listf(filedes, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
315 retval = attr_list(path, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
317 for (index = 0; index < al->al_count; index++) {
318 ae = ATTR_ENTRY(attr_buffer, index);
319 ent_size = strlen(ae->a_name) + sizeof("user.");
320 if (left >= ent_size) {
321 strncpy(bp, "user.", sizeof("user."));
322 strncat(bp, ae->a_name, ent_size - sizeof("user."));
330 total_size += ent_size;
332 if (al->al_more == 0) break;
339 retval = attr_listf(filedes, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
341 retval = attr_list(path, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
343 for (index = 0; index < al->al_count; index++) {
344 ae = ATTR_ENTRY(attr_buffer, index);
345 ent_size = strlen(ae->a_name) + sizeof("system.");
346 if (left >= ent_size) {
347 strncpy(bp, "system.", sizeof("system."));
348 strncat(bp, ae->a_name, ent_size - sizeof("system."));
356 total_size += ent_size;
358 if (al->al_more == 0) break;
361 return (ssize_t)(retval ? retval : total_size);
366 #if defined(HAVE_LISTXATTR)
367 static ssize_t remove_user(ssize_t ret, char *list, size_t size)
374 if (ret <= 0 || size == 0)
378 while (ptrsize > 0) {
379 len = strlen(ptr1) +1;
381 if (strncmp(ptr1, "user.",5)) {
385 memmove(ptr, ptr1 +5, len -5);
393 ssize_t sys_listxattr (const char *path, char *list, size_t size)
395 #if defined(HAVE_LISTXATTR)
398 #ifndef XATTR_ADD_OPT
399 ret = listxattr(path, list, size);
402 ret = listxattr(path, list, size, options);
404 return remove_user(ret, list, size);
406 #elif defined(HAVE_LISTEA)
407 return listea(path, list, size);
408 #elif defined(HAVE_EXTATTR_LIST_FILE)
411 return bsd_attr_list(0, arg, list, size);
412 #elif defined(HAVE_ATTR_LIST) && defined(HAVE_SYS_ATTRIBUTES_H)
413 return irix_attr_list(path, 0, list, size, 0);
414 #elif defined(HAVE_ATTROPEN)
416 int attrdirfd = solaris_attropen(path, ".", O_RDONLY, 0);
417 if (attrdirfd >= 0) {
418 ret = solaris_list_xattr(attrdirfd, list, size);
428 ssize_t sys_llistxattr (const char *path, char *list, size_t size)
430 #if defined(HAVE_LLISTXATTR)
433 ret = llistxattr(path, list, size);
434 return remove_user(ret, list, size);
435 #elif defined(HAVE_LISTXATTR) && defined(XATTR_ADD_OPT)
437 int options = XATTR_NOFOLLOW;
439 ret = listxattr(path, list, size, options);
440 return remove_user(ret, list, size);
442 #elif defined(HAVE_LLISTEA)
443 return llistea(path, list, size);
444 #elif defined(HAVE_EXTATTR_LIST_LINK)
447 return bsd_attr_list(1, arg, list, size);
448 #elif defined(HAVE_ATTR_LIST) && defined(HAVE_SYS_ATTRIBUTES_H)
449 return irix_attr_list(path, 0, list, size, ATTR_DONTFOLLOW);
450 #elif defined(HAVE_ATTROPEN)
452 int attrdirfd = solaris_attropen(path, ".", O_RDONLY|AT_SYMLINK_NOFOLLOW, 0);
453 if (attrdirfd >= 0) {
454 ret = solaris_list_xattr(attrdirfd, list, size);
464 int sys_removexattr (const char *path, const char *uname)
466 const char *name = prefix(uname);
467 #if defined(HAVE_REMOVEXATTR)
468 #ifndef XATTR_ADD_OPT
469 return removexattr(path, name);
472 return removexattr(path, name, options);
474 #elif defined(HAVE_REMOVEEA)
475 return removeea(path, name);
476 #elif defined(HAVE_EXTATTR_DELETE_FILE)
478 int attrnamespace = (strncmp(name, "system", 6) == 0) ?
479 EXTATTR_NAMESPACE_SYSTEM : EXTATTR_NAMESPACE_USER;
480 const char *attrname = ((s=strchr_m(name, '.')) == NULL) ? name : s + 1;
482 return extattr_delete_file(path, attrnamespace, attrname);
483 #elif defined(HAVE_ATTR_REMOVE)
485 char *attrname = strchr(name,'.') + 1;
487 if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
489 return attr_remove(path, attrname, flags);
490 #elif defined(HAVE_ATTROPEN)
492 int attrdirfd = solaris_attropen(path, ".", O_RDONLY, 0);
493 if (attrdirfd >= 0) {
494 ret = solaris_unlinkat(attrdirfd, name);
504 int sys_lremovexattr (const char *path, const char *uname)
506 const char *name = prefix(uname);
507 #if defined(HAVE_LREMOVEXATTR)
508 return lremovexattr(path, name);
509 #elif defined(HAVE_REMOVEXATTR) && defined(XATTR_ADD_OPT)
510 int options = XATTR_NOFOLLOW;
511 return removexattr(path, name, options);
512 #elif defined(HAVE_LREMOVEEA)
513 return lremoveea(path, name);
514 #elif defined(HAVE_EXTATTR_DELETE_LINK)
516 int attrnamespace = (strncmp(name, "system", 6) == 0) ?
517 EXTATTR_NAMESPACE_SYSTEM : EXTATTR_NAMESPACE_USER;
518 const char *attrname = ((s=strchr_m(name, '.')) == NULL) ? name : s + 1;
520 return extattr_delete_link(path, attrnamespace, attrname);
521 #elif defined(HAVE_ATTR_REMOVE)
522 int flags = ATTR_DONTFOLLOW;
523 char *attrname = strchr(name,'.') + 1;
525 if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
527 return attr_remove(path, attrname, flags);
528 #elif defined(HAVE_ATTROPEN)
530 int attrdirfd = solaris_attropen(path, ".", O_RDONLY|AT_SYMLINK_NOFOLLOW, 0);
531 if (attrdirfd >= 0) {
532 ret = solaris_unlinkat(attrdirfd, name);
542 int sys_setxattr (const char *path, const char *uname, const void *value, size_t size, int flags)
544 const char *name = prefix(uname);
545 #if defined(HAVE_SETXATTR)
546 #ifndef XATTR_ADD_OPT
547 return setxattr(path, name, value, size, flags);
550 return setxattr(path, name, value, size, 0, options);
552 #elif defined(HAVE_SETEA)
553 return setea(path, name, value, size, flags);
554 #elif defined(HAVE_EXTATTR_SET_FILE)
557 int attrnamespace = (strncmp(name, "system", 6) == 0) ?
558 EXTATTR_NAMESPACE_SYSTEM : EXTATTR_NAMESPACE_USER;
559 const char *attrname = ((s=strchr_m(name, '.')) == NULL) ? name : s + 1;
561 /* Check attribute existence */
562 retval = extattr_get_file(path, attrnamespace, attrname, NULL, 0);
564 /* REPLACE attribute, that doesn't exist */
565 if (flags & XATTR_REPLACE && errno == ENOATTR) {
569 /* Ignore other errors */
572 /* CREATE attribute, that already exists */
573 if (flags & XATTR_CREATE) {
579 retval = extattr_set_file(path, attrnamespace, attrname, value, size);
580 return (retval < 0) ? -1 : 0;
581 #elif defined(HAVE_ATTR_SET)
583 char *attrname = strchr(name,'.') + 1;
585 if (strncmp(name, "system", 6) == 0) myflags |= ATTR_ROOT;
586 if (flags & XATTR_CREATE) myflags |= ATTR_CREATE;
587 if (flags & XATTR_REPLACE) myflags |= ATTR_REPLACE;
589 return attr_set(path, attrname, (const char *)value, size, myflags);
590 #elif defined(HAVE_ATTROPEN)
592 int myflags = O_RDWR;
594 if (flags & XATTR_CREATE) myflags |= O_EXCL;
595 if (!(flags & XATTR_REPLACE)) myflags |= O_CREAT;
596 attrfd = solaris_attropen(path, name, myflags, (mode_t) SOLARIS_ATTRMODE);
598 ret = solaris_write_xattr(attrfd, value, size);
608 int sys_lsetxattr (const char *path, const char *uname, const void *value, size_t size, int flags)
610 const char *name = prefix(uname);
611 #if defined(HAVE_LSETXATTR)
612 return lsetxattr(path, name, value, size, flags);
613 #elif defined(HAVE_SETXATTR) && defined(XATTR_ADD_OPT)
614 int options = XATTR_NOFOLLOW;
615 return setxattr(path, name, value, size, 0, options);
616 #elif defined(LSETEA)
617 return lsetea(path, name, value, size, flags);
618 #elif defined(HAVE_EXTATTR_SET_LINK)
621 int attrnamespace = (strncmp(name, "system", 6) == 0) ?
622 EXTATTR_NAMESPACE_SYSTEM : EXTATTR_NAMESPACE_USER;
623 const char *attrname = ((s=strchr_m(name, '.')) == NULL) ? name : s + 1;
625 /* Check attribute existence */
626 retval = extattr_get_link(path, attrnamespace, attrname, NULL, 0);
628 /* REPLACE attribute, that doesn't exist */
629 if (flags & XATTR_REPLACE && errno == ENOATTR) {
633 /* Ignore other errors */
636 /* CREATE attribute, that already exists */
637 if (flags & XATTR_CREATE) {
644 retval = extattr_set_link(path, attrnamespace, attrname, value, size);
645 return (retval < 0) ? -1 : 0;
646 #elif defined(HAVE_ATTR_SET)
647 int myflags = ATTR_DONTFOLLOW;
648 char *attrname = strchr(name,'.') + 1;
650 if (strncmp(name, "system", 6) == 0) myflags |= ATTR_ROOT;
651 if (flags & XATTR_CREATE) myflags |= ATTR_CREATE;
652 if (flags & XATTR_REPLACE) myflags |= ATTR_REPLACE;
654 return attr_set(path, attrname, (const char *)value, size, myflags);
655 #elif defined(HAVE_ATTROPEN)
657 int myflags = O_RDWR | AT_SYMLINK_NOFOLLOW;
659 if (flags & XATTR_CREATE) myflags |= O_EXCL;
660 if (!(flags & XATTR_REPLACE)) myflags |= O_CREAT;
661 attrfd = solaris_attropen(path, name, myflags, (mode_t) SOLARIS_ATTRMODE);
663 ret = solaris_write_xattr(attrfd, value, size);
673 /* copy EA, from LGPL2.1 libattr attr_copy_file.c
674 should use fgetxattr? We don't have them but they are in Samba.
675 Or add a sys_copyfxattr? Currently it's only call by afp_copyfile so we can open
676 the both files for reading and get a fd.
678 Or don't use sys_xxx and copy all attributes.
681 int sys_copyxattr(const char *src_path, const char *dst_path)
683 #if defined(HAVE_LISTXATTR) && defined(HAVE_GETXATTR) && defined(HAVE_SETXATTR)
686 char *names = NULL, *end_names, *name, *value = NULL;
687 unsigned int setxattr_ENOTSUP = 0;
689 size = sys_listxattr(src_path, NULL, 0);
691 if (errno != ENOSYS && errno != ENOTSUP) {
696 names = malloc(size+1);
701 size = sys_listxattr(src_path, names, size);
707 end_names = names + size;
710 for (name = names; name != end_names; name = strchr(name, '\0') + 1) {
713 /* check if this attribute shall be preserved */
717 size = sys_getxattr (src_path, name, NULL, 0);
722 value = realloc(old_value = value, size);
723 if (size != 0 && value == NULL) {
727 size = sys_getxattr(src_path, name, value, size);
732 if (sys_setxattr(dst_path, name, value, size, 0) != 0) {
733 if (errno == ENOTSUP)
736 if (errno == ENOSYS) {
738 /* no hope of getting any further */
746 if (setxattr_ENOTSUP) {
754 #elif defined(HAVE_ATTROPEN)
755 /* FIXME same for solaris */
756 return solaris_copy_xattr(src_path, dst_path );
763 /**************************************************************************
764 helper functions for Solaris' EA support
765 ****************************************************************************/
767 static ssize_t solaris_read_xattr(int attrfd, void *value, size_t size)
771 if (fstat(attrfd, &sbuf) == -1) {
775 /* This is to return the current size of the named extended attribute */
780 /* check size and read xattr */
781 if (sbuf.st_size > size) {
785 return read(attrfd, value, sbuf.st_size);
788 static ssize_t solaris_list_xattr(int attrdirfd, char *list, size_t size)
794 int newfd = dup(attrdirfd);
795 /* CAUTION: The originating file descriptor should not be
796 used again following the call to fdopendir().
797 For that reason we dup() the file descriptor
798 here to make things more clear. */
799 dirp = fdopendir(newfd);
801 while ((de = readdir(dirp))) {
803 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..") ||
804 !strcmp(de->d_name, "SUNWattr_ro") || !strcmp(de->d_name, "SUNWattr_rw"))
806 /* we don't want "." and ".." here: */
807 LOG(log_maxdebug, logtype_default, "skipped EA %s\n",de->d_name);
811 listlen = strlen(de->d_name);
813 /* return the current size of the list of extended attribute names*/
816 /* check size and copy entry + nul into list. */
817 if ((len + listlen + 1) > size) {
822 strcpy(list + len, de->d_name);
830 if (closedir(dirp) == -1) {
831 LOG(log_debug, logtype_default, "closedir dirp failed: %s\n",strerror(errno));
837 static int solaris_unlinkat(int attrdirfd, const char *name)
839 if (unlinkat(attrdirfd, name, 0) == -1) {
845 static int solaris_attropen(const char *path, const char *attrpath, int oflag, mode_t mode)
847 int filedes = attropen(path, attrpath, oflag, mode);
849 LOG(log_maxdebug, logtype_default, "attropen FAILED: path: %s, name: %s, errno: %s\n",path,attrpath,strerror(errno));
854 static int solaris_openat(int fildes, const char *path, int oflag, mode_t mode)
856 int filedes = openat(fildes, path, oflag, mode);
858 LOG(log_maxdebug, logtype_default, "openat FAILED: fd: %s, path: %s, errno: %s\n",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!\n");
873 static int solaris_copy_xattr(const char *src, const char *dst )
879 #endif /*HAVE_ATTROPEN*/