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|S_IROTH|S_IWOTH
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 int sys_getxattrfd(int fd, const char *uname, int oflag, ...)
93 #if defined HAVE_ATTROPEN
98 if (oflag & O_CREAT) {
99 va_start(args, oflag);
100 mode = va_arg(args, mode_t);
105 eafd = solaris_openat(fd, uname, oflag | O_XATTR, mode);
107 eafd = solaris_openat(fd, uname, oflag | O_XATTR, mode);
116 ssize_t sys_getxattr (const char *path, const char *uname, void *value, size_t size)
118 const char *name = prefix(uname);
120 #if defined(HAVE_GETXATTR)
121 #ifndef XATTR_ADD_OPT
122 return getxattr(path, name, value, size);
125 return getxattr(path, name, value, size, 0, options);
127 #elif defined(HAVE_GETEA)
128 return getea(path, name, value, size);
129 #elif defined(HAVE_EXTATTR_GET_FILE)
132 * The BSD implementation has a nasty habit of silently truncating
133 * the returned value to the size of the buffer, so we have to check
134 * that the buffer is large enough to fit the returned value.
136 if((retval = extattr_get_file(path, EXTATTR_NAMESPACE_USER, uname, NULL, 0)) >= 0) {
138 /* size == 0 means only return size */
144 if ((retval = extattr_get_file(path, EXTATTR_NAMESPACE_USER, uname, value, size)) >= 0)
148 LOG(log_maxdebug, logtype_default, "sys_getxattr: extattr_get_file() failed with: %s\n", strerror(errno));
150 #elif defined(HAVE_ATTR_GET)
151 int retval, flags = 0;
152 int valuelength = (int)size;
153 char *attrname = strchr(name,'.') + 1;
155 if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
157 retval = attr_get(path, attrname, (char *)value, &valuelength, flags);
159 return retval ? retval : valuelength;
160 #elif defined(HAVE_ATTROPEN)
162 int attrfd = solaris_attropen(path, name, O_RDONLY, 0);
164 ret = solaris_read_xattr(attrfd, value, size);
174 ssize_t sys_fgetxattr (int filedes, const char *uname, void *value, size_t size)
176 const char *name = prefix(uname);
178 #if defined(HAVE_FGETXATTR)
179 #ifndef XATTR_ADD_OPT
180 return fgetxattr(filedes, name, value, size);
183 return fgetxattr(filedes, name, value, size, 0, options);
185 #elif defined(HAVE_FGETEA)
186 return fgetea(filedes, name, value, size);
187 #elif defined(HAVE_EXTATTR_GET_FD)
190 int attrnamespace = (strncmp(name, "system", 6) == 0) ?
191 EXTATTR_NAMESPACE_SYSTEM : EXTATTR_NAMESPACE_USER;
192 const char *attrname = ((s=strchr_m(name, '.')) == NULL) ? name : s + 1;
194 if((retval=extattr_get_fd(filedes, attrnamespace, attrname, NULL, 0)) >= 0) {
199 if((retval=extattr_get_fd(filedes, attrnamespace, attrname, value, size)) >= 0)
203 LOG(log_debug, logtype_default, "sys_fgetxattr: extattr_get_fd(): %s",
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)
243 if((retval=extattr_get_link(path, EXTATTR_NAMESPACE_USER, uname, NULL, 0)) >= 0) {
248 if((retval=extattr_get_link(path, EXTATTR_NAMESPACE_USER, uname, value, size)) >= 0)
252 LOG(log_maxdebug, logtype_default, "sys_lgetxattr: extattr_get_link() failed with: %s\n", strerror(errno));
254 #elif defined(HAVE_ATTR_GET)
255 int retval, flags = ATTR_DONTFOLLOW;
256 int valuelength = (int)size;
257 char *attrname = strchr(name,'.') + 1;
259 if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
261 retval = attr_get(path, attrname, (char *)value, &valuelength, flags);
263 return retval ? retval : valuelength;
264 #elif defined(HAVE_ATTROPEN)
266 int attrfd = solaris_attropen(path, name, O_RDONLY|AT_SYMLINK_NOFOLLOW, 0);
268 ret = solaris_read_xattr(attrfd, value, size);
278 #if defined(HAVE_EXTATTR_LIST_FILE)
280 #define EXTATTR_PREFIX(s) (s), (sizeof((s))-1)
288 { EXTATTR_NAMESPACE_SYSTEM, EXTATTR_PREFIX("") },
289 { EXTATTR_NAMESPACE_USER, EXTATTR_PREFIX("") },
297 static ssize_t bsd_attr_list (int type, extattr_arg arg, char *list, size_t size)
303 #if defined(HAVE_EXTATTR_LIST_FILE)
305 list_size = extattr_list_file(arg.path, EXTATTR_NAMESPACE_USER, list, size);
308 #if defined(HAVE_EXTATTR_LIST_LINK)
310 list_size = extattr_list_link(arg.path, EXTATTR_NAMESPACE_USER, list, size);
313 #if defined(HAVE_EXTATTR_LIST_FD)
315 list_size = extattr_list_fd(arg.filedes, EXTATTR_NAMESPACE_USER, list, size);
323 /* Some error happend. Errno should be set by the previous call */
331 /* XXX: Call with an empty buffer may be used to calculate
332 necessary buffer size. Unfortunately, we can't say, how
333 many attributes were returned, so here is the potential
334 problem with the emulation.
339 /* Buffer is too small to fit the results */
340 if(list_size > size) {
345 /* Convert from pascal strings to C strings */
347 memmove(list, list + 1, list_size);
349 for(i = len; i < list_size; ) {
350 LOG(log_maxdebug, logtype_afpd, "len: %d, i: %d", len, i);
362 #if defined(HAVE_ATTR_LIST) && defined(HAVE_SYS_ATTRIBUTES_H)
363 static char attr_buffer[ATTR_MAX_VALUELEN];
365 static ssize_t irix_attr_list(const char *path, int filedes, char *list, size_t size, int flags)
367 int retval = 0, index;
368 attrlist_cursor_t *cursor = 0;
370 attrlist_t * al = (attrlist_t *)attr_buffer;
372 size_t ent_size, left = size;
377 retval = attr_listf(filedes, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
379 retval = attr_list(path, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
381 for (index = 0; index < al->al_count; index++) {
382 ae = ATTR_ENTRY(attr_buffer, index);
383 ent_size = strlen(ae->a_name) + sizeof("user.");
384 if (left >= ent_size) {
385 strncpy(bp, "user.", sizeof("user."));
386 strncat(bp, ae->a_name, ent_size - sizeof("user."));
394 total_size += ent_size;
396 if (al->al_more == 0) break;
403 retval = attr_listf(filedes, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
405 retval = attr_list(path, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
407 for (index = 0; index < al->al_count; index++) {
408 ae = ATTR_ENTRY(attr_buffer, index);
409 ent_size = strlen(ae->a_name) + sizeof("system.");
410 if (left >= ent_size) {
411 strncpy(bp, "system.", sizeof("system."));
412 strncat(bp, ae->a_name, ent_size - sizeof("system."));
420 total_size += ent_size;
422 if (al->al_more == 0) break;
425 return (ssize_t)(retval ? retval : total_size);
430 #if defined(HAVE_LISTXATTR)
431 static ssize_t remove_user(ssize_t ret, char *list, size_t size)
438 if (ret <= 0 || size == 0)
442 while (ptrsize > 0) {
443 len = strlen(ptr1) +1;
445 if (strncmp(ptr1, "user.",5)) {
449 memmove(ptr, ptr1 +5, len -5);
457 ssize_t sys_listxattr (const char *path, char *list, size_t size)
459 #if defined(HAVE_LISTXATTR)
462 #ifndef XATTR_ADD_OPT
463 ret = listxattr(path, list, size);
466 ret = listxattr(path, list, size, options);
468 return remove_user(ret, list, size);
470 #elif defined(HAVE_LISTEA)
471 return listea(path, list, size);
472 #elif defined(HAVE_EXTATTR_LIST_FILE)
475 return bsd_attr_list(0, arg, list, size);
476 #elif defined(HAVE_ATTR_LIST) && defined(HAVE_SYS_ATTRIBUTES_H)
477 return irix_attr_list(path, 0, list, size, 0);
478 #elif defined(HAVE_ATTROPEN)
480 int attrdirfd = solaris_attropen(path, ".", O_RDONLY, 0);
481 if (attrdirfd >= 0) {
482 ret = solaris_list_xattr(attrdirfd, list, size);
492 ssize_t sys_llistxattr (const char *path, char *list, size_t size)
494 #if defined(HAVE_LLISTXATTR)
497 ret = llistxattr(path, list, size);
498 return remove_user(ret, list, size);
499 #elif defined(HAVE_LISTXATTR) && defined(XATTR_ADD_OPT)
501 int options = XATTR_NOFOLLOW;
503 ret = listxattr(path, list, size, options);
504 return remove_user(ret, list, size);
506 #elif defined(HAVE_LLISTEA)
507 return llistea(path, list, size);
508 #elif defined(HAVE_EXTATTR_LIST_LINK)
511 return bsd_attr_list(1, arg, list, size);
512 #elif defined(HAVE_ATTR_LIST) && defined(HAVE_SYS_ATTRIBUTES_H)
513 return irix_attr_list(path, 0, list, size, ATTR_DONTFOLLOW);
514 #elif defined(HAVE_ATTROPEN)
516 int attrdirfd = solaris_attropen(path, ".", O_RDONLY|AT_SYMLINK_NOFOLLOW, 0);
517 if (attrdirfd >= 0) {
518 ret = solaris_list_xattr(attrdirfd, list, size);
528 int sys_removexattr (const char *path, const char *uname)
530 const char *name = prefix(uname);
531 #if defined(HAVE_REMOVEXATTR)
532 #ifndef XATTR_ADD_OPT
533 return removexattr(path, name);
536 return removexattr(path, name, options);
538 #elif defined(HAVE_REMOVEEA)
539 return removeea(path, name);
540 #elif defined(HAVE_EXTATTR_DELETE_FILE)
541 return extattr_delete_file(path, EXTATTR_NAMESPACE_USER, uname);
542 #elif defined(HAVE_ATTR_REMOVE)
544 char *attrname = strchr(name,'.') + 1;
546 if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
548 return attr_remove(path, attrname, flags);
549 #elif defined(HAVE_ATTROPEN)
551 int attrdirfd = solaris_attropen(path, ".", O_RDONLY, 0);
552 if (attrdirfd >= 0) {
553 ret = solaris_unlinkat(attrdirfd, name);
563 int sys_lremovexattr (const char *path, const char *uname)
565 const char *name = prefix(uname);
566 #if defined(HAVE_LREMOVEXATTR)
567 return lremovexattr(path, name);
568 #elif defined(HAVE_REMOVEXATTR) && defined(XATTR_ADD_OPT)
569 int options = XATTR_NOFOLLOW;
570 return removexattr(path, name, options);
571 #elif defined(HAVE_LREMOVEEA)
572 return lremoveea(path, name);
573 #elif defined(HAVE_EXTATTR_DELETE_LINK)
574 return extattr_delete_link(path, EXTATTR_NAMESPACE_USER, uname);
575 #elif defined(HAVE_ATTR_REMOVE)
576 int flags = ATTR_DONTFOLLOW;
577 char *attrname = strchr(name,'.') + 1;
579 if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
581 return attr_remove(path, attrname, flags);
582 #elif defined(HAVE_ATTROPEN)
584 int attrdirfd = solaris_attropen(path, ".", O_RDONLY|AT_SYMLINK_NOFOLLOW, 0);
585 if (attrdirfd >= 0) {
586 ret = solaris_unlinkat(attrdirfd, name);
596 int sys_setxattr (const char *path, const char *uname, const void *value, size_t size, int flags)
598 const char *name = prefix(uname);
599 #if defined(HAVE_SETXATTR)
600 #ifndef XATTR_ADD_OPT
601 return setxattr(path, name, value, size, flags);
604 return setxattr(path, name, value, size, 0, options);
606 #elif defined(HAVE_SETEA)
607 return setea(path, name, value, size, flags);
608 #elif defined(HAVE_EXTATTR_SET_FILE)
611 /* Check attribute existence */
612 retval = extattr_get_file(path, EXTATTR_NAMESPACE_USER, uname, NULL, 0);
614 /* REPLACE attribute, that doesn't exist */
615 if (flags & XATTR_REPLACE && errno == ENOATTR) {
619 /* Ignore other errors */
622 /* CREATE attribute, that already exists */
623 if (flags & XATTR_CREATE) {
629 retval = extattr_set_file(path, EXTATTR_NAMESPACE_USER, uname, value, size);
630 return (retval < 0) ? -1 : 0;
631 #elif defined(HAVE_ATTR_SET)
633 char *attrname = strchr(name,'.') + 1;
635 if (strncmp(name, "system", 6) == 0) myflags |= ATTR_ROOT;
636 if (flags & XATTR_CREATE) myflags |= ATTR_CREATE;
637 if (flags & XATTR_REPLACE) myflags |= ATTR_REPLACE;
639 return attr_set(path, attrname, (const char *)value, size, myflags);
640 #elif defined(HAVE_ATTROPEN)
642 int myflags = O_RDWR;
644 if (flags & XATTR_CREATE) myflags |= O_EXCL;
645 if (!(flags & XATTR_REPLACE)) myflags |= O_CREAT;
646 attrfd = solaris_attropen(path, name, myflags, (mode_t) SOLARIS_ATTRMODE);
648 ret = solaris_write_xattr(attrfd, value, size);
658 int sys_fsetxattr (int filedes, const char *uname, const void *value, size_t size, int flags)
660 const char *name = prefix(uname);
662 #if defined(HAVE_FSETXATTR)
663 #ifndef XATTR_ADD_OPT
664 return fsetxattr(filedes, name, value, size, flags);
667 return fsetxattr(filedes, name, value, size, 0, options);
669 #elif defined(HAVE_FSETEA)
670 return fsetea(filedes, name, value, size, flags);
671 #elif defined(HAVE_EXTATTR_SET_FD)
674 int attrnamespace = (strncmp(name, "system", 6) == 0) ?
675 EXTATTR_NAMESPACE_SYSTEM : EXTATTR_NAMESPACE_USER;
676 const char *attrname = ((s=strchr_m(name, '.')) == NULL) ? name : s + 1;
678 /* Check attribute existence */
679 retval = extattr_get_fd(filedes, attrnamespace, attrname, NULL, 0);
681 /* REPLACE attribute, that doesn't exist */
682 if (flags & XATTR_REPLACE && errno == ENOATTR) {
686 /* Ignore other errors */
689 log_error, logtype_default /* CREATE attribute, that already exists */
690 if (flags & XATTR_CREATE) {
696 retval = extattr_set_fd(filedes, attrnamespace, attrname, value, size);
697 return (retval < 0) ? -1 : 0;
698 #elif defined(HAVE_ATTR_SETF)
700 char *attrname = strchr(name,'.') + 1;
702 if (strncmp(name, "system", 6) == 0) myflags |= ATTR_ROOT;
703 if (flags & XATTR_CREATE) myflags |= ATTR_CREATE;
704 if (flags & XATTR_REPLACE) myflags |= ATTR_REPLACE;
706 return attr_setf(filedes, attrname, (const char *)value, size, myflags);
707 #elif defined(HAVE_ATTROPEN)
709 int myflags = O_RDWR | O_XATTR;
711 if (flags & XATTR_CREATE) myflags |= O_EXCL;
712 if (!(flags & XATTR_REPLACE)) myflags |= O_CREAT;
713 attrfd = solaris_openat(filedes, name, myflags, (mode_t) SOLARIS_ATTRMODE);
715 ret = solaris_write_xattr(attrfd, value, size);
725 int sys_lsetxattr (const char *path, const char *uname, const void *value, size_t size, int flags)
727 const char *name = prefix(uname);
728 #if defined(HAVE_LSETXATTR)
729 return lsetxattr(path, name, value, size, flags);
730 #elif defined(HAVE_SETXATTR) && defined(XATTR_ADD_OPT)
731 int options = XATTR_NOFOLLOW;
732 return setxattr(path, name, value, size, 0, options);
733 #elif defined(LSETEA)
734 return lsetea(path, name, value, size, flags);
735 #elif defined(HAVE_EXTATTR_SET_LINK)
738 /* Check attribute existence */
739 retval = extattr_get_link(path, EXTATTR_NAMESPACE_USER, uname, NULL, 0);
741 /* REPLACE attribute, that doesn't exist */
742 if (flags & XATTR_REPLACE && errno == ENOATTR) {
746 /* Ignore other errors */
749 /* CREATE attribute, that already exists */
750 if (flags & XATTR_CREATE) {
757 retval = extattr_set_link(path, EXTATTR_NAMESPACE_USER, uname, value, size);
758 return (retval < 0) ? -1 : 0;
759 #elif defined(HAVE_ATTR_SET)
760 int myflags = ATTR_DONTFOLLOW;
761 char *attrname = strchr(name,'.') + 1;
763 if (strncmp(name, "system", 6) == 0) myflags |= ATTR_ROOT;
764 if (flags & XATTR_CREATE) myflags |= ATTR_CREATE;
765 if (flags & XATTR_REPLACE) myflags |= ATTR_REPLACE;
767 return attr_set(path, attrname, (const char *)value, size, myflags);
768 #elif defined(HAVE_ATTROPEN)
770 int myflags = O_RDWR | AT_SYMLINK_NOFOLLOW;
772 if (flags & XATTR_CREATE) myflags |= O_EXCL;
773 if (!(flags & XATTR_REPLACE)) myflags |= O_CREAT;
774 attrfd = solaris_attropen(path, name, myflags, (mode_t) SOLARIS_ATTRMODE);
776 ret = solaris_write_xattr(attrfd, value, size);
786 /**************************************************************************
787 helper functions for Solaris' EA support
788 ****************************************************************************/
790 static ssize_t solaris_read_xattr(int attrfd, void *value, size_t size)
794 if (fstat(attrfd, &sbuf) == -1) {
798 /* This is to return the current size of the named extended attribute */
803 /* check size and read xattr */
804 if (sbuf.st_size > size) {
808 return read(attrfd, value, sbuf.st_size);
811 static ssize_t solaris_list_xattr(int attrdirfd, char *list, size_t size)
816 int newfd = dup(attrdirfd);
817 /* CAUTION: The originating file descriptor should not be
818 used again following the call to fdopendir().
819 For that reason we dup() the file descriptor
820 here to make things more clear. */
821 dirp = fdopendir(newfd);
823 while ((de = readdir(dirp))) {
825 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..") ||
826 !strcmp(de->d_name, "SUNWattr_ro") || !strcmp(de->d_name, "SUNWattr_rw"))
828 /* we don't want "." and ".." here: */
829 LOG(log_maxdebug, logtype_default, "skipped EA %s\n",de->d_name);
833 listlen = strlen(de->d_name);
835 /* return the current size of the list of extended attribute names*/
838 /* check size and copy entry + nul into list. */
839 if ((len + listlen + 1) > size) {
844 strcpy(list + len, de->d_name);
852 if (closedir(dirp) == -1) {
853 LOG(log_error, logtype_default, "closedir dirp: %s",strerror(errno));
859 static int solaris_unlinkat(int attrdirfd, const char *name)
861 if (unlinkat(attrdirfd, name, 0) == -1) {
867 static int solaris_attropen(const char *path, const char *attrpath, int oflag, mode_t mode)
871 if ((filedes = attropen(path, attrpath, oflag, mode)) == -1) {
877 LOG(log_error, logtype_default, "attropen(\"%s\", ea:'%s'): %s",
878 path, attrpath, strerror(errno));
886 static int solaris_openat(int fildes, const char *path, int oflag, mode_t mode)
890 if ((filedes = openat(fildes, path, oflag, mode)) == -1) {
896 LOG(log_error, logtype_default, "openat(\"%s\"): %s",
897 path, strerror(errno));
905 static int solaris_write_xattr(int attrfd, const char *value, size_t size)
907 if ((ftruncate(attrfd, 0) == 0) && (write(attrfd, value, size) == size)) {
910 LOG(log_error, logtype_default, "solaris_write_xattr: %s",
916 #endif /*HAVE_ATTROPEN*/