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_m(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",
207 #elif defined(HAVE_ATTR_GETF)
208 int retval, flags = 0;
209 int valuelength = (int)size;
210 char *attrname = strchr(name,'.') + 1;
212 if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
214 retval = attr_getf(filedes, attrname, (char *)value, &valuelength, flags);
216 return retval ? retval : valuelength;
217 #elif defined(HAVE_ATTROPEN)
219 int attrfd = solaris_openat(filedes, name, O_RDONLY|O_XATTR, 0);
221 ret = solaris_read_xattr(attrfd, value, size);
231 ssize_t sys_lgetxattr (const char *path, const char *uname, void *value, size_t size)
233 const char *name = prefix(uname);
235 #if defined(HAVE_LGETXATTR)
236 return lgetxattr(path, name, value, size);
237 #elif defined(HAVE_GETXATTR) && defined(XATTR_ADD_OPT)
238 int options = XATTR_NOFOLLOW;
239 return getxattr(path, name, value, size, 0, options);
240 #elif defined(HAVE_LGETEA)
241 return lgetea(path, name, value, size);
242 #elif defined(HAVE_EXTATTR_GET_LINK)
244 if((retval=extattr_get_link(path, EXTATTR_NAMESPACE_USER, uname, NULL, 0)) >= 0) {
249 if((retval=extattr_get_link(path, EXTATTR_NAMESPACE_USER, uname, value, size)) >= 0)
253 LOG(log_maxdebug, logtype_default, "sys_lgetxattr: extattr_get_link() failed with: %s\n", strerror(errno));
255 #elif defined(HAVE_ATTR_GET)
256 int retval, flags = ATTR_DONTFOLLOW;
257 int valuelength = (int)size;
258 char *attrname = strchr(name,'.') + 1;
260 if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
262 retval = attr_get(path, attrname, (char *)value, &valuelength, flags);
264 return retval ? retval : valuelength;
265 #elif defined(HAVE_ATTROPEN)
267 int attrfd = solaris_attropen(path, name, O_RDONLY | O_NOFOLLOW, 0);
269 ret = solaris_read_xattr(attrfd, value, size);
279 #if defined(HAVE_EXTATTR_LIST_FILE)
281 #define EXTATTR_PREFIX(s) (s), (sizeof((s))-1)
289 { EXTATTR_NAMESPACE_SYSTEM, EXTATTR_PREFIX("") },
290 { EXTATTR_NAMESPACE_USER, EXTATTR_PREFIX("") },
298 static ssize_t bsd_attr_list (int type, extattr_arg arg, char *list, size_t size)
304 #if defined(HAVE_EXTATTR_LIST_FILE)
306 list_size = extattr_list_file(arg.path, EXTATTR_NAMESPACE_USER, list, size);
309 #if defined(HAVE_EXTATTR_LIST_LINK)
311 list_size = extattr_list_link(arg.path, EXTATTR_NAMESPACE_USER, list, size);
314 #if defined(HAVE_EXTATTR_LIST_FD)
316 list_size = extattr_list_fd(arg.filedes, EXTATTR_NAMESPACE_USER, list, size);
324 /* Some error happend. Errno should be set by the previous call */
332 /* XXX: Call with an empty buffer may be used to calculate
333 necessary buffer size. Unfortunately, we can't say, how
334 many attributes were returned, so here is the potential
335 problem with the emulation.
340 /* Buffer is too small to fit the results */
341 if(list_size > size) {
346 /* Convert from pascal strings to C strings */
348 memmove(list, list + 1, list_size);
350 for(i = len; i < list_size; ) {
351 LOG(log_maxdebug, logtype_afpd, "len: %d, i: %d", len, i);
363 #if defined(HAVE_ATTR_LIST) && defined(HAVE_SYS_ATTRIBUTES_H)
364 static char attr_buffer[ATTR_MAX_VALUELEN];
366 static ssize_t irix_attr_list(const char *path, int filedes, char *list, size_t size, int flags)
368 int retval = 0, index;
369 attrlist_cursor_t *cursor = 0;
371 attrlist_t * al = (attrlist_t *)attr_buffer;
373 size_t ent_size, left = size;
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("user.");
385 if (left >= ent_size) {
386 strncpy(bp, "user.", sizeof("user."));
387 strncat(bp, ae->a_name, ent_size - sizeof("user."));
395 total_size += ent_size;
397 if (al->al_more == 0) break;
404 retval = attr_listf(filedes, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
406 retval = attr_list(path, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
408 for (index = 0; index < al->al_count; index++) {
409 ae = ATTR_ENTRY(attr_buffer, index);
410 ent_size = strlen(ae->a_name) + sizeof("system.");
411 if (left >= ent_size) {
412 strncpy(bp, "system.", sizeof("system."));
413 strncat(bp, ae->a_name, ent_size - sizeof("system."));
421 total_size += ent_size;
423 if (al->al_more == 0) break;
426 return (ssize_t)(retval ? retval : total_size);
431 #if defined(HAVE_LISTXATTR)
432 static ssize_t remove_user(ssize_t ret, char *list, size_t size)
439 if (ret <= 0 || size == 0)
443 while (ptrsize > 0) {
444 len = strlen(ptr1) +1;
446 if (strncmp(ptr1, "user.",5)) {
450 memmove(ptr, ptr1 +5, len -5);
458 ssize_t sys_listxattr (const char *path, char *list, size_t size)
460 #if defined(HAVE_LISTXATTR)
463 #ifndef XATTR_ADD_OPT
464 ret = listxattr(path, list, size);
467 ret = listxattr(path, list, size, options);
469 return remove_user(ret, list, size);
471 #elif defined(HAVE_LISTEA)
472 return listea(path, list, size);
473 #elif defined(HAVE_EXTATTR_LIST_FILE)
476 return bsd_attr_list(0, arg, list, size);
477 #elif defined(HAVE_ATTR_LIST) && defined(HAVE_SYS_ATTRIBUTES_H)
478 return irix_attr_list(path, 0, list, size, 0);
479 #elif defined(HAVE_ATTROPEN)
481 int attrdirfd = solaris_attropen(path, ".", O_RDONLY, 0);
482 if (attrdirfd >= 0) {
483 ret = solaris_list_xattr(attrdirfd, list, size);
493 ssize_t sys_llistxattr (const char *path, char *list, size_t size)
495 #if defined(HAVE_LLISTXATTR)
498 ret = llistxattr(path, list, size);
499 return remove_user(ret, list, size);
500 #elif defined(HAVE_LISTXATTR) && defined(XATTR_ADD_OPT)
502 int options = XATTR_NOFOLLOW;
504 ret = listxattr(path, list, size, options);
505 return remove_user(ret, list, size);
507 #elif defined(HAVE_LLISTEA)
508 return llistea(path, list, size);
509 #elif defined(HAVE_EXTATTR_LIST_LINK)
512 return bsd_attr_list(1, arg, list, size);
513 #elif defined(HAVE_ATTR_LIST) && defined(HAVE_SYS_ATTRIBUTES_H)
514 return irix_attr_list(path, 0, list, size, ATTR_DONTFOLLOW);
515 #elif defined(HAVE_ATTROPEN)
517 int attrdirfd = solaris_attropen(path, ".", O_RDONLY | O_NOFOLLOW, 0);
518 if (attrdirfd >= 0) {
519 ret = solaris_list_xattr(attrdirfd, list, size);
529 int sys_removexattr (const char *path, const char *uname)
531 const char *name = prefix(uname);
532 #if defined(HAVE_REMOVEXATTR)
533 #ifndef XATTR_ADD_OPT
534 return removexattr(path, name);
537 return removexattr(path, name, options);
539 #elif defined(HAVE_REMOVEEA)
540 return removeea(path, name);
541 #elif defined(HAVE_EXTATTR_DELETE_FILE)
542 return extattr_delete_file(path, EXTATTR_NAMESPACE_USER, uname);
543 #elif defined(HAVE_ATTR_REMOVE)
545 char *attrname = strchr(name,'.') + 1;
547 if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
549 return attr_remove(path, attrname, flags);
550 #elif defined(HAVE_ATTROPEN)
552 int attrdirfd = solaris_attropen(path, ".", O_RDONLY, 0);
553 if (attrdirfd >= 0) {
554 ret = solaris_unlinkat(attrdirfd, name);
564 int sys_lremovexattr (const char *path, const char *uname)
566 const char *name = prefix(uname);
567 #if defined(HAVE_LREMOVEXATTR)
568 return lremovexattr(path, name);
569 #elif defined(HAVE_REMOVEXATTR) && defined(XATTR_ADD_OPT)
570 int options = XATTR_NOFOLLOW;
571 return removexattr(path, name, options);
572 #elif defined(HAVE_LREMOVEEA)
573 return lremoveea(path, name);
574 #elif defined(HAVE_EXTATTR_DELETE_LINK)
575 return extattr_delete_link(path, EXTATTR_NAMESPACE_USER, uname);
576 #elif defined(HAVE_ATTR_REMOVE)
577 int flags = ATTR_DONTFOLLOW;
578 char *attrname = strchr(name,'.') + 1;
580 if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
582 return attr_remove(path, attrname, flags);
583 #elif defined(HAVE_ATTROPEN)
585 int attrdirfd = solaris_attropen(path, ".", O_RDONLY | O_NOFOLLOW, 0);
586 if (attrdirfd >= 0) {
587 ret = solaris_unlinkat(attrdirfd, name);
597 int sys_setxattr (const char *path, const char *uname, const void *value, size_t size, int flags)
599 const char *name = prefix(uname);
600 #if defined(HAVE_SETXATTR)
601 #ifndef XATTR_ADD_OPT
602 return setxattr(path, name, value, size, flags);
605 return setxattr(path, name, value, size, 0, options);
607 #elif defined(HAVE_SETEA)
608 return setea(path, name, value, size, flags);
609 #elif defined(HAVE_EXTATTR_SET_FILE)
612 /* Check attribute existence */
613 retval = extattr_get_file(path, EXTATTR_NAMESPACE_USER, uname, NULL, 0);
615 /* REPLACE attribute, that doesn't exist */
616 if (flags & XATTR_REPLACE && errno == ENOATTR) {
620 /* Ignore other errors */
623 /* CREATE attribute, that already exists */
624 if (flags & XATTR_CREATE) {
630 retval = extattr_set_file(path, EXTATTR_NAMESPACE_USER, uname, value, size);
631 return (retval < 0) ? -1 : 0;
632 #elif defined(HAVE_ATTR_SET)
634 char *attrname = strchr(name,'.') + 1;
636 if (strncmp(name, "system", 6) == 0) myflags |= ATTR_ROOT;
637 if (flags & XATTR_CREATE) myflags |= ATTR_CREATE;
638 if (flags & XATTR_REPLACE) myflags |= ATTR_REPLACE;
640 return attr_set(path, attrname, (const char *)value, size, myflags);
641 #elif defined(HAVE_ATTROPEN)
643 int myflags = O_RDWR;
645 if (flags & XATTR_CREATE) myflags |= O_EXCL;
646 if (!(flags & XATTR_REPLACE)) myflags |= O_CREAT;
647 attrfd = solaris_attropen(path, name, myflags, (mode_t) SOLARIS_ATTRMODE);
649 ret = solaris_write_xattr(attrfd, value, size);
659 int sys_fsetxattr (int filedes, const char *uname, const void *value, size_t size, int flags)
661 const char *name = prefix(uname);
663 #if defined(HAVE_FSETXATTR)
664 #ifndef XATTR_ADD_OPT
665 return fsetxattr(filedes, name, value, size, flags);
668 return fsetxattr(filedes, name, value, size, 0, options);
670 #elif defined(HAVE_FSETEA)
671 return fsetea(filedes, name, value, size, flags);
672 #elif defined(HAVE_EXTATTR_SET_FD)
675 int attrnamespace = (strncmp(name, "system", 6) == 0) ?
676 EXTATTR_NAMESPACE_SYSTEM : EXTATTR_NAMESPACE_USER;
677 const char *attrname = ((s=strchr_m(name, '.')) == NULL) ? name : s + 1;
679 /* Check attribute existence */
680 retval = extattr_get_fd(filedes, attrnamespace, attrname, NULL, 0);
682 /* REPLACE attribute, that doesn't exist */
683 if (flags & XATTR_REPLACE && errno == ENOATTR) {
687 /* Ignore other errors */
690 log_error, logtype_default /* CREATE attribute, that already exists */
691 if (flags & XATTR_CREATE) {
697 retval = extattr_set_fd(filedes, attrnamespace, attrname, value, size);
698 return (retval < 0) ? -1 : 0;
699 #elif defined(HAVE_ATTR_SETF)
701 char *attrname = strchr(name,'.') + 1;
703 if (strncmp(name, "system", 6) == 0) myflags |= ATTR_ROOT;
704 if (flags & XATTR_CREATE) myflags |= ATTR_CREATE;
705 if (flags & XATTR_REPLACE) myflags |= ATTR_REPLACE;
707 return attr_setf(filedes, attrname, (const char *)value, size, myflags);
708 #elif defined(HAVE_ATTROPEN)
710 int myflags = O_RDWR | O_XATTR;
712 if (flags & XATTR_CREATE) myflags |= O_EXCL;
713 if (!(flags & XATTR_REPLACE)) myflags |= O_CREAT;
714 attrfd = solaris_openat(filedes, name, myflags, (mode_t) SOLARIS_ATTRMODE);
716 ret = solaris_write_xattr(attrfd, value, size);
726 int sys_lsetxattr (const char *path, const char *uname, const void *value, size_t size, int flags)
728 const char *name = prefix(uname);
729 #if defined(HAVE_LSETXATTR)
730 return lsetxattr(path, name, value, size, flags);
731 #elif defined(HAVE_SETXATTR) && defined(XATTR_ADD_OPT)
732 int options = XATTR_NOFOLLOW;
733 return setxattr(path, name, value, size, 0, options);
734 #elif defined(LSETEA)
735 return lsetea(path, name, value, size, flags);
736 #elif defined(HAVE_EXTATTR_SET_LINK)
739 /* Check attribute existence */
740 retval = extattr_get_link(path, EXTATTR_NAMESPACE_USER, uname, NULL, 0);
742 /* REPLACE attribute, that doesn't exist */
743 if (flags & XATTR_REPLACE && errno == ENOATTR) {
747 /* Ignore other errors */
750 /* CREATE attribute, that already exists */
751 if (flags & XATTR_CREATE) {
758 retval = extattr_set_link(path, EXTATTR_NAMESPACE_USER, uname, value, size);
759 return (retval < 0) ? -1 : 0;
760 #elif defined(HAVE_ATTR_SET)
761 int myflags = ATTR_DONTFOLLOW;
762 char *attrname = strchr(name,'.') + 1;
764 if (strncmp(name, "system", 6) == 0) myflags |= ATTR_ROOT;
765 if (flags & XATTR_CREATE) myflags |= ATTR_CREATE;
766 if (flags & XATTR_REPLACE) myflags |= ATTR_REPLACE;
768 return attr_set(path, attrname, (const char *)value, size, myflags);
769 #elif defined(HAVE_ATTROPEN)
771 int myflags = O_RDWR | O_NOFOLLOW;
773 if (flags & XATTR_CREATE) myflags |= O_EXCL;
774 if (!(flags & XATTR_REPLACE)) myflags |= O_CREAT;
775 attrfd = solaris_attropen(path, name, myflags, (mode_t) SOLARIS_ATTRMODE);
777 ret = solaris_write_xattr(attrfd, value, size);
787 /**************************************************************************
788 helper functions for Solaris' EA support
789 ****************************************************************************/
791 static ssize_t solaris_read_xattr(int attrfd, void *value, size_t size)
795 if (fstat(attrfd, &sbuf) == -1) {
799 /* This is to return the current size of the named extended attribute */
804 /* check size and read xattr */
805 if (sbuf.st_size > size) {
809 return read(attrfd, value, sbuf.st_size);
812 static ssize_t solaris_list_xattr(int attrdirfd, char *list, size_t size)
817 int newfd = dup(attrdirfd);
818 /* CAUTION: The originating file descriptor should not be
819 used again following the call to fdopendir().
820 For that reason we dup() the file descriptor
821 here to make things more clear. */
822 dirp = fdopendir(newfd);
824 while ((de = readdir(dirp))) {
826 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..") ||
827 !strcmp(de->d_name, "SUNWattr_ro") || !strcmp(de->d_name, "SUNWattr_rw"))
829 /* we don't want "." and ".." here: */
830 LOG(log_maxdebug, logtype_default, "skipped EA %s\n",de->d_name);
834 listlen = strlen(de->d_name);
836 /* return the current size of the list of extended attribute names*/
839 /* check size and copy entry + nul into list. */
840 if ((len + listlen + 1) > size) {
845 strcpy(list + len, de->d_name);
853 if (closedir(dirp) == -1) {
854 LOG(log_error, logtype_default, "closedir dirp: %s",strerror(errno));
860 static int solaris_unlinkat(int attrdirfd, const char *name)
862 if (unlinkat(attrdirfd, name, 0) == -1) {
868 static int solaris_attropen(const char *path, const char *attrpath, int oflag, mode_t mode)
871 int filedes = -1, eafd = -1;
873 if ((filedes = open(path, O_RDONLY | (oflag & O_NOFOLLOW), mode)) == -1) {
877 case OPEN_NOFOLLOW_ERRNO:
880 LOG(log_debug, logtype_default, "open(\"%s\"): %s", fullpathname(path), strerror(errno));
886 if ((eafd = openat(filedes, attrpath, oflag | O_XATTR, mode)) == -1) {
892 LOG(log_debug, logtype_default, "openat(\"%s\"): %s", fullpathname(path), strerror(errno));
909 static int solaris_openat(int fildes, const char *path, int oflag, mode_t mode)
913 if ((filedes = openat(fildes, path, oflag, mode)) == -1) {
919 LOG(log_debug, logtype_default, "openat(\"%s\"): %s",
920 path, strerror(errno));
928 static int solaris_write_xattr(int attrfd, const char *value, size_t size)
930 if ((ftruncate(attrfd, 0) == 0) && (write(attrfd, value, size) == size)) {
933 LOG(log_error, logtype_default, "solaris_write_xattr: %s",
939 #endif /*HAVE_ATTROPEN*/