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>
64 #define ENOATTR ENODATA
67 /******** Solaris EA helper function prototypes ********/
69 #define SOLARIS_ATTRMODE S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP
70 static int solaris_write_xattr(int attrfd, const char *value, size_t size);
71 static ssize_t solaris_read_xattr(int attrfd, void *value, size_t size);
72 static ssize_t solaris_list_xattr(int attrdirfd, char *list, size_t size);
73 static int solaris_unlinkat(int attrdirfd, const char *name);
74 static int solaris_attropen(const char *path, const char *attrpath, int oflag, mode_t mode);
75 static int solaris_openat(int fildes, const char *path, int oflag, mode_t mode);
78 /**************************************************************************
79 Wrappers for extented attribute calls. Based on the Linux package with
80 support for IRIX and (Net|Free)BSD also. Expand as other systems have them.
81 ****************************************************************************/
82 static char attr_name[256 +5] = "user.";
84 static const char *prefix(const char *uname)
86 #if defined(HAVE_ATTROPEN)
89 strlcpy(attr_name +5, uname, 256);
94 ssize_t sys_getxattr (const char *path, const char *uname, void *value, size_t size)
96 const char *name = prefix(uname);
98 #if defined(HAVE_GETXATTR)
100 return getxattr(path, name, value, size);
103 return getxattr(path, name, value, size, 0, options);
105 #elif defined(HAVE_GETEA)
106 return getea(path, name, value, size);
107 #elif defined(HAVE_EXTATTR_GET_FILE)
110 * The BSD implementation has a nasty habit of silently truncating
111 * the returned value to the size of the buffer, so we have to check
112 * that the buffer is large enough to fit the returned value.
114 if((retval = extattr_get_file(path, EXTATTR_NAMESPACE_USER, uname, NULL, 0)) >= 0) {
116 /* size == 0 means only return size */
122 if ((retval = extattr_get_file(path, EXTATTR_NAMESPACE_USER, uname, 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_fgetxattr (int filedes, const char *name, void *value, size_t size)
154 #if defined(HAVE_FGETXATTR)
155 #ifndef XATTR_ADD_OPT
156 return fgetxattr(filedes, name, value, size);
159 return fgetxattr(filedes, name, value, size, 0, options);
161 #elif defined(HAVE_FGETEA)
162 return fgetea(filedes, name, value, size);
163 #elif defined(HAVE_EXTATTR_GET_FD)
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_fd(filedes, attrnamespace, attrname, NULL, 0)) >= 0) {
175 if((retval=extattr_get_fd(filedes, attrnamespace, attrname, value, size)) >= 0)
179 DEBUG(10,("sys_fgetxattr: extattr_get_fd() failed with: %s\n", strerror(errno)));
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 *name, const void *value, size_t size, int flags)
635 #if defined(HAVE_FSETXATTR)
636 #ifndef XATTR_ADD_OPT
637 return fsetxattr(filedes, name, value, size, flags);
640 return fsetxattr(filedes, name, value, size, 0, options);
642 #elif defined(HAVE_FSETEA)
643 return fsetea(filedes, name, value, size, flags);
644 #elif defined(HAVE_EXTATTR_SET_FD)
647 int attrnamespace = (strncmp(name, "system", 6) == 0) ?
648 EXTATTR_NAMESPACE_SYSTEM : EXTATTR_NAMESPACE_USER;
649 const char *attrname = ((s=strchr_m(name, '.')) == NULL) ? name : s + 1;
651 /* Check attribute existence */
652 retval = extattr_get_fd(filedes, attrnamespace, attrname, NULL, 0);
654 /* REPLACE attribute, that doesn't exist */
655 if (flags & XATTR_REPLACE && errno == ENOATTR) {
659 /* Ignore other errors */
662 /* CREATE attribute, that already exists */
663 if (flags & XATTR_CREATE) {
669 retval = extattr_set_fd(filedes, attrnamespace, attrname, value, size);
670 return (retval < 0) ? -1 : 0;
671 #elif defined(HAVE_ATTR_SETF)
673 char *attrname = strchr(name,'.') + 1;
675 if (strncmp(name, "system", 6) == 0) myflags |= ATTR_ROOT;
676 if (flags & XATTR_CREATE) myflags |= ATTR_CREATE;
677 if (flags & XATTR_REPLACE) myflags |= ATTR_REPLACE;
679 return attr_setf(filedes, attrname, (const char *)value, size, myflags);
680 #elif defined(HAVE_ATTROPEN)
682 int myflags = O_RDWR | O_XATTR;
684 if (flags & XATTR_CREATE) myflags |= O_EXCL;
685 if (!(flags & XATTR_REPLACE)) myflags |= O_CREAT;
686 attrfd = solaris_openat(filedes, name, myflags, (mode_t) SOLARIS_ATTRMODE);
688 ret = solaris_write_xattr(attrfd, value, size);
698 int sys_lsetxattr (const char *path, const char *uname, const void *value, size_t size, int flags)
700 const char *name = prefix(uname);
701 #if defined(HAVE_LSETXATTR)
702 return lsetxattr(path, name, value, size, flags);
703 #elif defined(HAVE_SETXATTR) && defined(XATTR_ADD_OPT)
704 int options = XATTR_NOFOLLOW;
705 return setxattr(path, name, value, size, 0, options);
706 #elif defined(LSETEA)
707 return lsetea(path, name, value, size, flags);
708 #elif defined(HAVE_EXTATTR_SET_LINK)
711 /* Check attribute existence */
712 retval = extattr_get_link(path, EXTATTR_NAMESPACE_USER, uname, NULL, 0);
714 /* REPLACE attribute, that doesn't exist */
715 if (flags & XATTR_REPLACE && errno == ENOATTR) {
719 /* Ignore other errors */
722 /* CREATE attribute, that already exists */
723 if (flags & XATTR_CREATE) {
730 retval = extattr_set_link(path, EXTATTR_NAMESPACE_USER, uname, value, size);
731 return (retval < 0) ? -1 : 0;
732 #elif defined(HAVE_ATTR_SET)
733 int myflags = ATTR_DONTFOLLOW;
734 char *attrname = strchr(name,'.') + 1;
736 if (strncmp(name, "system", 6) == 0) myflags |= ATTR_ROOT;
737 if (flags & XATTR_CREATE) myflags |= ATTR_CREATE;
738 if (flags & XATTR_REPLACE) myflags |= ATTR_REPLACE;
740 return attr_set(path, attrname, (const char *)value, size, myflags);
741 #elif defined(HAVE_ATTROPEN)
743 int myflags = O_RDWR | AT_SYMLINK_NOFOLLOW;
745 if (flags & XATTR_CREATE) myflags |= O_EXCL;
746 if (!(flags & XATTR_REPLACE)) myflags |= O_CREAT;
747 attrfd = solaris_attropen(path, name, myflags, (mode_t) SOLARIS_ATTRMODE);
749 ret = solaris_write_xattr(attrfd, value, size);
759 /**************************************************************************
760 helper functions for Solaris' EA support
761 ****************************************************************************/
763 static ssize_t solaris_read_xattr(int attrfd, void *value, size_t size)
767 if (fstat(attrfd, &sbuf) == -1) {
771 /* This is to return the current size of the named extended attribute */
776 /* check size and read xattr */
777 if (sbuf.st_size > size) {
781 return read(attrfd, value, sbuf.st_size);
784 static ssize_t solaris_list_xattr(int attrdirfd, char *list, size_t size)
789 int newfd = dup(attrdirfd);
790 /* CAUTION: The originating file descriptor should not be
791 used again following the call to fdopendir().
792 For that reason we dup() the file descriptor
793 here to make things more clear. */
794 dirp = fdopendir(newfd);
796 while ((de = readdir(dirp))) {
798 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..") ||
799 !strcmp(de->d_name, "SUNWattr_ro") || !strcmp(de->d_name, "SUNWattr_rw"))
801 /* we don't want "." and ".." here: */
802 LOG(log_maxdebug, logtype_default, "skipped EA %s\n",de->d_name);
806 listlen = strlen(de->d_name);
808 /* return the current size of the list of extended attribute names*/
811 /* check size and copy entry + nul into list. */
812 if ((len + listlen + 1) > size) {
817 strcpy(list + len, de->d_name);
825 if (closedir(dirp) == -1) {
826 LOG(log_debug, logtype_default, "closedir dirp failed: %s\n",strerror(errno));
832 static int solaris_unlinkat(int attrdirfd, const char *name)
834 if (unlinkat(attrdirfd, name, 0) == -1) {
840 static int solaris_attropen(const char *path, const char *attrpath, int oflag, mode_t mode)
842 int filedes = attropen(path, attrpath, oflag, mode);
844 LOG(log_maxdebug, logtype_default, "attropen FAILED: path: %s, name: %s, errno: %s\n",path,attrpath,strerror(errno));
850 static int solaris_openat(int fildes, const char *path, int oflag, mode_t mode)
852 int filedes = openat(fildes, path, oflag, mode);
854 LOG(log_maxdebug, logtype_default, "openat FAILED: fd: %s, path: %s, errno: %s\n",filedes,path,strerror(errno));
859 static int solaris_write_xattr(int attrfd, const char *value, size_t size)
861 if ((ftruncate(attrfd, 0) == 0) && (write(attrfd, value, size) == size)) {
864 LOG(log_maxdebug, logtype_default, "solaris_write_xattr FAILED!\n");
869 #endif /*HAVE_ATTROPEN*/