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>
63 /******** Solaris EA helper function prototypes ********/
65 #define SOLARIS_ATTRMODE S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP
66 static int solaris_write_xattr(int attrfd, const char *value, size_t size);
67 static ssize_t solaris_read_xattr(int attrfd, void *value, size_t size);
68 static ssize_t solaris_list_xattr(int attrdirfd, char *list, size_t size);
69 static int solaris_unlinkat(int attrdirfd, const char *name);
70 static int solaris_attropen(const char *path, const char *attrpath, int oflag, mode_t mode);
71 static int solaris_openat(int fildes, const char *path, int oflag, mode_t mode);
74 /**************************************************************************
75 Wrappers for extented attribute calls. Based on the Linux package with
76 support for IRIX and (Net|Free)BSD also. Expand as other systems have them.
77 ****************************************************************************/
78 static char attr_name[256 +5] = "user.";
80 static const char *prefix(const char *uname)
82 #if defined(HAVE_ATTROPEN)
85 strlcpy(attr_name +5, uname, 256);
90 ssize_t sys_getxattr (const char *path, const char *uname, void *value, size_t size)
92 const char *name = prefix(uname);
94 #if defined(HAVE_GETXATTR)
96 return getxattr(path, name, value, size);
99 return getxattr(path, name, value, size, 0, options);
101 #elif defined(HAVE_GETEA)
102 return getea(path, name, value, size);
103 #elif defined(HAVE_EXTATTR_GET_FILE)
106 * The BSD implementation has a nasty habit of silently truncating
107 * the returned value to the size of the buffer, so we have to check
108 * that the buffer is large enough to fit the returned value.
110 if((retval = extattr_get_file(path, EXTATTR_NAMESPACE_USER, uname, NULL, 0)) >= 0) {
112 /* size == 0 means only return size */
118 if ((retval = extattr_get_file(path, EXTATTR_NAMESPACE_USER, uname, value, size)) >= 0)
122 LOG(log_maxdebug, logtype_default, "sys_getxattr: extattr_get_file() failed with: %s\n", strerror(errno));
124 #elif defined(HAVE_ATTR_GET)
125 int retval, flags = 0;
126 int valuelength = (int)size;
127 char *attrname = strchr(name,'.') + 1;
129 if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
131 retval = attr_get(path, attrname, (char *)value, &valuelength, flags);
133 return retval ? retval : valuelength;
134 #elif defined(HAVE_ATTROPEN)
136 int attrfd = solaris_attropen(path, name, O_RDONLY, 0);
138 ret = solaris_read_xattr(attrfd, value, size);
148 ssize_t sys_fgetxattr (int filedes, const char *uname, void *value, size_t size)
150 const char *name = prefix(uname);
152 #if defined(HAVE_FGETXATTR)
153 #ifndef XATTR_ADD_OPT
154 return fgetxattr(filedes, name, value, size);
157 return fgetxattr(filedes, name, value, size, 0, options);
159 #elif defined(HAVE_FGETEA)
160 return fgetea(filedes, name, value, size);
161 #elif defined(HAVE_EXTATTR_GET_FD)
164 int attrnamespace = (strncmp(name, "system", 6) == 0) ?
165 EXTATTR_NAMESPACE_SYSTEM : EXTATTR_NAMESPACE_USER;
166 const char *attrname = ((s=strchr_m(name, '.')) == NULL) ? name : s + 1;
168 if((retval=extattr_get_fd(filedes, attrnamespace, attrname, NULL, 0)) >= 0) {
173 if((retval=extattr_get_fd(filedes, attrnamespace, attrname, value, size)) >= 0)
177 LOG(log_debug, logtype_default, "sys_fgetxattr: extattr_get_fd(): %s",
180 #elif defined(HAVE_ATTR_GETF)
181 int retval, flags = 0;
182 int valuelength = (int)size;
183 char *attrname = strchr(name,'.') + 1;
185 if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
187 retval = attr_getf(filedes, attrname, (char *)value, &valuelength, flags);
189 return retval ? retval : valuelength;
190 #elif defined(HAVE_ATTROPEN)
192 int attrfd = solaris_openat(filedes, name, O_RDONLY|O_XATTR, 0);
194 ret = solaris_read_xattr(attrfd, value, size);
204 ssize_t sys_lgetxattr (const char *path, const char *uname, void *value, size_t size)
206 const char *name = prefix(uname);
208 #if defined(HAVE_LGETXATTR)
209 return lgetxattr(path, name, value, size);
210 #elif defined(HAVE_GETXATTR) && defined(XATTR_ADD_OPT)
211 int options = XATTR_NOFOLLOW;
212 return getxattr(path, name, value, size, 0, options);
213 #elif defined(HAVE_LGETEA)
214 return lgetea(path, name, value, size);
215 #elif defined(HAVE_EXTATTR_GET_LINK)
217 if((retval=extattr_get_link(path, EXTATTR_NAMESPACE_USER, uname, NULL, 0)) >= 0) {
222 if((retval=extattr_get_link(path, EXTATTR_NAMESPACE_USER, uname, value, size)) >= 0)
226 LOG(log_maxdebug, logtype_default, "sys_lgetxattr: extattr_get_link() failed with: %s\n", strerror(errno));
228 #elif defined(HAVE_ATTR_GET)
229 int retval, flags = ATTR_DONTFOLLOW;
230 int valuelength = (int)size;
231 char *attrname = strchr(name,'.') + 1;
233 if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
235 retval = attr_get(path, attrname, (char *)value, &valuelength, flags);
237 return retval ? retval : valuelength;
238 #elif defined(HAVE_ATTROPEN)
240 int attrfd = solaris_attropen(path, name, O_RDONLY|AT_SYMLINK_NOFOLLOW, 0);
242 ret = solaris_read_xattr(attrfd, value, size);
252 #if defined(HAVE_EXTATTR_LIST_FILE)
254 #define EXTATTR_PREFIX(s) (s), (sizeof((s))-1)
262 { EXTATTR_NAMESPACE_SYSTEM, EXTATTR_PREFIX("") },
263 { EXTATTR_NAMESPACE_USER, EXTATTR_PREFIX("") },
271 static ssize_t bsd_attr_list (int type, extattr_arg arg, char *list, size_t size)
277 #if defined(HAVE_EXTATTR_LIST_FILE)
279 list_size = extattr_list_file(arg.path, EXTATTR_NAMESPACE_USER, list, size);
282 #if defined(HAVE_EXTATTR_LIST_LINK)
284 list_size = extattr_list_link(arg.path, EXTATTR_NAMESPACE_USER, list, size);
287 #if defined(HAVE_EXTATTR_LIST_FD)
289 list_size = extattr_list_fd(arg.filedes, EXTATTR_NAMESPACE_USER, list, size);
297 /* Some error happend. Errno should be set by the previous call */
305 /* XXX: Call with an empty buffer may be used to calculate
306 necessary buffer size. Unfortunately, we can't say, how
307 many attributes were returned, so here is the potential
308 problem with the emulation.
313 /* Buffer is too small to fit the results */
314 if(list_size > size) {
319 /* Convert from pascal strings to C strings */
321 memmove(list, list + 1, list_size);
323 for(i = len; i < list_size; ) {
324 LOG(log_maxdebug, logtype_afpd, "len: %d, i: %d", len, i);
336 #if defined(HAVE_ATTR_LIST) && defined(HAVE_SYS_ATTRIBUTES_H)
337 static char attr_buffer[ATTR_MAX_VALUELEN];
339 static ssize_t irix_attr_list(const char *path, int filedes, char *list, size_t size, int flags)
341 int retval = 0, index;
342 attrlist_cursor_t *cursor = 0;
344 attrlist_t * al = (attrlist_t *)attr_buffer;
346 size_t ent_size, left = size;
351 retval = attr_listf(filedes, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
353 retval = attr_list(path, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
355 for (index = 0; index < al->al_count; index++) {
356 ae = ATTR_ENTRY(attr_buffer, index);
357 ent_size = strlen(ae->a_name) + sizeof("user.");
358 if (left >= ent_size) {
359 strncpy(bp, "user.", sizeof("user."));
360 strncat(bp, ae->a_name, ent_size - sizeof("user."));
368 total_size += ent_size;
370 if (al->al_more == 0) break;
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("system.");
384 if (left >= ent_size) {
385 strncpy(bp, "system.", sizeof("system."));
386 strncat(bp, ae->a_name, ent_size - sizeof("system."));
394 total_size += ent_size;
396 if (al->al_more == 0) break;
399 return (ssize_t)(retval ? retval : total_size);
404 #if defined(HAVE_LISTXATTR)
405 static ssize_t remove_user(ssize_t ret, char *list, size_t size)
412 if (ret <= 0 || size == 0)
416 while (ptrsize > 0) {
417 len = strlen(ptr1) +1;
419 if (strncmp(ptr1, "user.",5)) {
423 memmove(ptr, ptr1 +5, len -5);
431 ssize_t sys_listxattr (const char *path, char *list, size_t size)
433 #if defined(HAVE_LISTXATTR)
436 #ifndef XATTR_ADD_OPT
437 ret = listxattr(path, list, size);
440 ret = listxattr(path, list, size, options);
442 return remove_user(ret, list, size);
444 #elif defined(HAVE_LISTEA)
445 return listea(path, list, size);
446 #elif defined(HAVE_EXTATTR_LIST_FILE)
449 return bsd_attr_list(0, arg, list, size);
450 #elif defined(HAVE_ATTR_LIST) && defined(HAVE_SYS_ATTRIBUTES_H)
451 return irix_attr_list(path, 0, list, size, 0);
452 #elif defined(HAVE_ATTROPEN)
454 int attrdirfd = solaris_attropen(path, ".", O_RDONLY, 0);
455 if (attrdirfd >= 0) {
456 ret = solaris_list_xattr(attrdirfd, list, size);
466 ssize_t sys_llistxattr (const char *path, char *list, size_t size)
468 #if defined(HAVE_LLISTXATTR)
471 ret = llistxattr(path, list, size);
472 return remove_user(ret, list, size);
473 #elif defined(HAVE_LISTXATTR) && defined(XATTR_ADD_OPT)
475 int options = XATTR_NOFOLLOW;
477 ret = listxattr(path, list, size, options);
478 return remove_user(ret, list, size);
480 #elif defined(HAVE_LLISTEA)
481 return llistea(path, list, size);
482 #elif defined(HAVE_EXTATTR_LIST_LINK)
485 return bsd_attr_list(1, arg, list, size);
486 #elif defined(HAVE_ATTR_LIST) && defined(HAVE_SYS_ATTRIBUTES_H)
487 return irix_attr_list(path, 0, list, size, ATTR_DONTFOLLOW);
488 #elif defined(HAVE_ATTROPEN)
490 int attrdirfd = solaris_attropen(path, ".", O_RDONLY|AT_SYMLINK_NOFOLLOW, 0);
491 if (attrdirfd >= 0) {
492 ret = solaris_list_xattr(attrdirfd, list, size);
502 int sys_removexattr (const char *path, const char *uname)
504 const char *name = prefix(uname);
505 #if defined(HAVE_REMOVEXATTR)
506 #ifndef XATTR_ADD_OPT
507 return removexattr(path, name);
510 return removexattr(path, name, options);
512 #elif defined(HAVE_REMOVEEA)
513 return removeea(path, name);
514 #elif defined(HAVE_EXTATTR_DELETE_FILE)
515 return extattr_delete_file(path, EXTATTR_NAMESPACE_USER, uname);
516 #elif defined(HAVE_ATTR_REMOVE)
518 char *attrname = strchr(name,'.') + 1;
520 if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
522 return attr_remove(path, attrname, flags);
523 #elif defined(HAVE_ATTROPEN)
525 int attrdirfd = solaris_attropen(path, ".", O_RDONLY, 0);
526 if (attrdirfd >= 0) {
527 ret = solaris_unlinkat(attrdirfd, name);
537 int sys_lremovexattr (const char *path, const char *uname)
539 const char *name = prefix(uname);
540 #if defined(HAVE_LREMOVEXATTR)
541 return lremovexattr(path, name);
542 #elif defined(HAVE_REMOVEXATTR) && defined(XATTR_ADD_OPT)
543 int options = XATTR_NOFOLLOW;
544 return removexattr(path, name, options);
545 #elif defined(HAVE_LREMOVEEA)
546 return lremoveea(path, name);
547 #elif defined(HAVE_EXTATTR_DELETE_LINK)
548 return extattr_delete_link(path, EXTATTR_NAMESPACE_USER, uname);
549 #elif defined(HAVE_ATTR_REMOVE)
550 int flags = ATTR_DONTFOLLOW;
551 char *attrname = strchr(name,'.') + 1;
553 if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
555 return attr_remove(path, attrname, flags);
556 #elif defined(HAVE_ATTROPEN)
558 int attrdirfd = solaris_attropen(path, ".", O_RDONLY|AT_SYMLINK_NOFOLLOW, 0);
559 if (attrdirfd >= 0) {
560 ret = solaris_unlinkat(attrdirfd, name);
570 int sys_setxattr (const char *path, const char *uname, const void *value, size_t size, int flags)
572 const char *name = prefix(uname);
573 #if defined(HAVE_SETXATTR)
574 #ifndef XATTR_ADD_OPT
575 return setxattr(path, name, value, size, flags);
578 return setxattr(path, name, value, size, 0, options);
580 #elif defined(HAVE_SETEA)
581 return setea(path, name, value, size, flags);
582 #elif defined(HAVE_EXTATTR_SET_FILE)
585 /* Check attribute existence */
586 retval = extattr_get_file(path, EXTATTR_NAMESPACE_USER, uname, NULL, 0);
588 /* REPLACE attribute, that doesn't exist */
589 if (flags & XATTR_REPLACE && errno == ENOATTR) {
593 /* Ignore other errors */
596 /* CREATE attribute, that already exists */
597 if (flags & XATTR_CREATE) {
603 retval = extattr_set_file(path, EXTATTR_NAMESPACE_USER, uname, value, size);
604 return (retval < 0) ? -1 : 0;
605 #elif defined(HAVE_ATTR_SET)
607 char *attrname = strchr(name,'.') + 1;
609 if (strncmp(name, "system", 6) == 0) myflags |= ATTR_ROOT;
610 if (flags & XATTR_CREATE) myflags |= ATTR_CREATE;
611 if (flags & XATTR_REPLACE) myflags |= ATTR_REPLACE;
613 return attr_set(path, attrname, (const char *)value, size, myflags);
614 #elif defined(HAVE_ATTROPEN)
616 int myflags = O_RDWR;
618 if (flags & XATTR_CREATE) myflags |= O_EXCL;
619 if (!(flags & XATTR_REPLACE)) myflags |= O_CREAT;
620 attrfd = solaris_attropen(path, name, myflags, (mode_t) SOLARIS_ATTRMODE);
622 ret = solaris_write_xattr(attrfd, value, size);
632 int sys_fsetxattr (int filedes, const char *uname, const void *value, size_t size, int flags)
634 const char *name = prefix(uname);
636 #if defined(HAVE_FSETXATTR)
637 #ifndef XATTR_ADD_OPT
638 return fsetxattr(filedes, name, value, size, flags);
641 return fsetxattr(filedes, name, value, size, 0, options);
643 #elif defined(HAVE_FSETEA)
644 return fsetea(filedes, name, value, size, flags);
645 #elif defined(HAVE_EXTATTR_SET_FD)
648 int attrnamespace = (strncmp(name, "system", 6) == 0) ?
649 EXTATTR_NAMESPACE_SYSTEM : EXTATTR_NAMESPACE_USER;
650 const char *attrname = ((s=strchr_m(name, '.')) == NULL) ? name : s + 1;
652 /* Check attribute existence */
653 retval = extattr_get_fd(filedes, attrnamespace, attrname, NULL, 0);
655 /* REPLACE attribute, that doesn't exist */
656 if (flags & XATTR_REPLACE && errno == ENOATTR) {
660 /* Ignore other errors */
663 log_error, logtype_default /* CREATE attribute, that already exists */
664 if (flags & XATTR_CREATE) {
670 retval = extattr_set_fd(filedes, attrnamespace, attrname, value, size);
671 return (retval < 0) ? -1 : 0;
672 #elif defined(HAVE_ATTR_SETF)
674 char *attrname = strchr(name,'.') + 1;
676 if (strncmp(name, "system", 6) == 0) myflags |= ATTR_ROOT;
677 if (flags & XATTR_CREATE) myflags |= ATTR_CREATE;
678 if (flags & XATTR_REPLACE) myflags |= ATTR_REPLACE;
680 return attr_setf(filedes, attrname, (const char *)value, size, myflags);
681 #elif defined(HAVE_ATTROPEN)
683 int myflags = O_RDWR | O_XATTR;
685 if (flags & XATTR_CREATE) myflags |= O_EXCL;
686 if (!(flags & XATTR_REPLACE)) myflags |= O_CREAT;
687 attrfd = solaris_openat(filedes, name, myflags, (mode_t) SOLARIS_ATTRMODE);
689 ret = solaris_write_xattr(attrfd, value, size);
699 int sys_lsetxattr (const char *path, const char *uname, const void *value, size_t size, int flags)
701 const char *name = prefix(uname);
702 #if defined(HAVE_LSETXATTR)
703 return lsetxattr(path, name, value, size, flags);
704 #elif defined(HAVE_SETXATTR) && defined(XATTR_ADD_OPT)
705 int options = XATTR_NOFOLLOW;
706 return setxattr(path, name, value, size, 0, options);
707 #elif defined(LSETEA)
708 return lsetea(path, name, value, size, flags);
709 #elif defined(HAVE_EXTATTR_SET_LINK)
712 /* Check attribute existence */
713 retval = extattr_get_link(path, EXTATTR_NAMESPACE_USER, uname, NULL, 0);
715 /* REPLACE attribute, that doesn't exist */
716 if (flags & XATTR_REPLACE && errno == ENOATTR) {
720 /* Ignore other errors */
723 /* CREATE attribute, that already exists */
724 if (flags & XATTR_CREATE) {
731 retval = extattr_set_link(path, EXTATTR_NAMESPACE_USER, uname, value, size);
732 return (retval < 0) ? -1 : 0;
733 #elif defined(HAVE_ATTR_SET)
734 int myflags = ATTR_DONTFOLLOW;
735 char *attrname = strchr(name,'.') + 1;
737 if (strncmp(name, "system", 6) == 0) myflags |= ATTR_ROOT;
738 if (flags & XATTR_CREATE) myflags |= ATTR_CREATE;
739 if (flags & XATTR_REPLACE) myflags |= ATTR_REPLACE;
741 return attr_set(path, attrname, (const char *)value, size, myflags);
742 #elif defined(HAVE_ATTROPEN)
744 int myflags = O_RDWR | AT_SYMLINK_NOFOLLOW;
746 if (flags & XATTR_CREATE) myflags |= O_EXCL;
747 if (!(flags & XATTR_REPLACE)) myflags |= O_CREAT;
748 attrfd = solaris_attropen(path, name, myflags, (mode_t) SOLARIS_ATTRMODE);
750 ret = solaris_write_xattr(attrfd, value, size);
760 /**************************************************************************
761 helper functions for Solaris' EA support
762 ****************************************************************************/
764 static ssize_t solaris_read_xattr(int attrfd, void *value, size_t size)
768 if (fstat(attrfd, &sbuf) == -1) {
772 /* This is to return the current size of the named extended attribute */
777 /* check size and read xattr */
778 if (sbuf.st_size > size) {
782 return read(attrfd, value, sbuf.st_size);
785 static ssize_t solaris_list_xattr(int attrdirfd, char *list, size_t size)
790 int newfd = dup(attrdirfd);
791 /* CAUTION: The originating file descriptor should not be
792 used again following the call to fdopendir().
793 For that reason we dup() the file descriptor
794 here to make things more clear. */
795 dirp = fdopendir(newfd);
797 while ((de = readdir(dirp))) {
799 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..") ||
800 !strcmp(de->d_name, "SUNWattr_ro") || !strcmp(de->d_name, "SUNWattr_rw"))
802 /* we don't want "." and ".." here: */
803 LOG(log_maxdebug, logtype_default, "skipped EA %s\n",de->d_name);
807 listlen = strlen(de->d_name);
809 /* return the current size of the list of extended attribute names*/
812 /* check size and copy entry + nul into list. */
813 if ((len + listlen + 1) > size) {
818 strcpy(list + len, de->d_name);
826 if (closedir(dirp) == -1) {
827 LOG(log_debug, logtype_default, "closedir dirp failed: %s\n",strerror(errno));
833 static int solaris_unlinkat(int attrdirfd, const char *name)
835 if (unlinkat(attrdirfd, name, 0) == -1) {
841 static int solaris_attropen(const char *path, const char *attrpath, int oflag, mode_t mode)
843 int filedes = attropen(path, attrpath, oflag, mode);
845 LOG(log_maxdebug, logtype_default, "attropen FAILED: path: %s, name: %s, errno: %s",
846 path, attrpath, strerror(errno));
852 static int solaris_openat(int fildes, const char *path, int oflag, mode_t mode)
854 int filedes = openat(fildes, path, oflag, mode);
856 LOG(log_maxdebug, logtype_default, "openat FAILED: fd: %d, path: %s, errno: %s",
857 filedes, path, strerror(errno));
862 static int solaris_write_xattr(int attrfd, const char *value, size_t size)
864 if ((ftruncate(attrfd, 0) == 0) && (write(attrfd, value, size) == size)) {
867 LOG(log_maxdebug, logtype_default, "solaris_write_xattr FAILED!");
872 #endif /*HAVE_ATTROPEN*/