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 Samba 3.0.28, modified for netatalk.
24 $Id: ad_ea.c,v 1.4 2009-11-13 13:03:29 didg Exp $
34 #define HAVE_ATTR_XATTR_H 1
35 #define HAVE_FGETXATTR 1
36 #define HAVE_FLISTXATTR 1
37 #define HAVE_FREMOVEXATTR 1
38 #define HAVE_FSETXATTR 1
39 #define HAVE_GETXATTR 1
40 #define HAVE_LGETXATTR 1
41 #define HAVE_LISTXATTR 1
42 #define HAVE_LLISTXATTR 1
43 #define HAVE_LREMOVEXATTR 1
44 #define HAVE_LSETXATTR 1
45 #define HAVE_REMOVEXATTR 1
46 #define HAVE_SETXATTR 1
47 #define HAVE_SYS_XATTR_H 1
50 #include <sys/types.h>
53 #include <attr/xattr.h>
54 #elif HAVE_SYS_XATTR_H
55 #include <sys/xattr.h>
62 #ifdef HAVE_SYS_EXTATTR_H
63 #include <sys/extattr.h>
66 #include <atalk/adouble.h>
67 #include <atalk/util.h>
68 #include <atalk/logger.h>
70 /******** Solaris EA helper function prototypes ********/
72 #define SOLARIS_ATTRMODE S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP
73 static int solaris_write_xattr(int attrfd, const char *value, size_t size);
74 static ssize_t solaris_read_xattr(int attrfd, void *value, size_t size);
75 static ssize_t solaris_list_xattr(int attrdirfd, char *list, size_t size);
76 static int solaris_unlinkat(int attrdirfd, const char *name);
77 static int solaris_attropen(const char *path, const char *attrpath, int oflag, mode_t mode);
78 static int solaris_openat(int fildes, const char *path, int oflag, mode_t mode);
81 /**************************************************************************
82 Wrappers for extented attribute calls. Based on the Linux package with
83 support for IRIX and (Net|Free)BSD also. Expand as other systems have them.
84 ****************************************************************************/
85 static char attr_name[256 +5] = "user.";
87 static const char *prefix(const char *uname)
89 #if defined(HAVE_ATTROPEN)
92 strlcpy(attr_name +5, uname, 256);
97 ssize_t sys_getxattr (const char *path, const char *uname, void *value, size_t size)
99 const char *name = prefix(uname);
101 #if defined(HAVE_GETXATTR)
102 #ifndef XATTR_ADD_OPT
103 return getxattr(path, name, value, size);
106 return getxattr(path, name, value, size, 0, options);
108 #elif defined(HAVE_GETEA)
109 return getea(path, name, value, size);
110 #elif defined(HAVE_EXTATTR_GET_FILE)
113 int attrnamespace = (strncmp(name, "system", 6) == 0) ?
114 EXTATTR_NAMESPACE_SYSTEM : EXTATTR_NAMESPACE_USER;
115 const char *attrname = ((s=strchr_m(name, '.')) == NULL) ? name : s + 1;
117 * The BSD implementation has a nasty habit of silently truncating
118 * the returned value to the size of the buffer, so we have to check
119 * that the buffer is large enough to fit the returned value.
121 if((retval=extattr_get_file(path, attrnamespace, attrname, NULL, 0)) >= 0) {
126 if((retval=extattr_get_file(path, attrnamespace, attrname, value, size)) >= 0)
130 LOG(log_maxdebug, logtype_default, "sys_getxattr: extattr_get_file() failed with: %s\n", strerror(errno));
132 #elif defined(HAVE_ATTR_GET)
133 int retval, flags = 0;
134 int valuelength = (int)size;
135 char *attrname = strchr(name,'.') + 1;
137 if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
139 retval = attr_get(path, attrname, (char *)value, &valuelength, flags);
141 return retval ? retval : valuelength;
142 #elif defined(HAVE_ATTROPEN)
144 int attrfd = solaris_attropen(path, name, O_RDONLY, 0);
146 ret = solaris_read_xattr(attrfd, value, size);
156 ssize_t sys_lgetxattr (const char *path, const char *uname, void *value, size_t size)
158 const char *name = prefix(uname);
160 #if defined(HAVE_LGETXATTR)
161 return lgetxattr(path, name, value, size);
162 #elif defined(HAVE_GETXATTR) && defined(XATTR_ADD_OPT)
163 int options = XATTR_NOFOLLOW;
164 return getxattr(path, name, value, size, 0, options);
165 #elif defined(HAVE_LGETEA)
166 return lgetea(path, name, value, size);
167 #elif defined(HAVE_EXTATTR_GET_LINK)
170 int attrnamespace = (strncmp(name, "system", 6) == 0) ?
171 EXTATTR_NAMESPACE_SYSTEM : EXTATTR_NAMESPACE_USER;
172 const char *attrname = ((s=strchr_m(name, '.')) == NULL) ? name : s + 1;
174 if((retval=extattr_get_link(path, attrnamespace, attrname, NULL, 0)) >= 0) {
179 if((retval=extattr_get_link(path, attrnamespace, attrname, value, size)) >= 0)
183 LOG(log_maxdebug, logtype_default, "sys_lgetxattr: extattr_get_link() failed with: %s\n", strerror(errno));
185 #elif defined(HAVE_ATTR_GET)
186 int retval, flags = ATTR_DONTFOLLOW;
187 int valuelength = (int)size;
188 char *attrname = strchr(name,'.') + 1;
190 if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
192 retval = attr_get(path, attrname, (char *)value, &valuelength, flags);
194 return retval ? retval : valuelength;
195 #elif defined(HAVE_ATTROPEN)
197 int attrfd = solaris_attropen(path, name, O_RDONLY|AT_SYMLINK_NOFOLLOW, 0);
199 ret = solaris_read_xattr(attrfd, value, size);
209 #if defined(HAVE_EXTATTR_LIST_FILE)
211 #define EXTATTR_PREFIX(s) (s), (sizeof((s))-1)
219 { EXTATTR_NAMESPACE_SYSTEM, EXTATTR_PREFIX("system.") },
220 { EXTATTR_NAMESPACE_USER, EXTATTR_PREFIX("user.") },
228 static ssize_t bsd_attr_list (int type, extattr_arg arg, char *list, size_t size)
230 ssize_t list_size, total_size = 0;
233 /* Iterate through extattr(2) namespaces */
234 for(t = 0; t < (sizeof(extattr)/sizeof(extattr[0])); t++) {
236 #if defined(HAVE_EXTATTR_LIST_FILE)
238 list_size = extattr_list_file(arg.path, extattr[t].space, list, size);
241 #if defined(HAVE_EXTATTR_LIST_LINK)
243 list_size = extattr_list_link(arg.path, extattr[t].space, list, size);
246 #if defined(HAVE_EXTATTR_LIST_FD)
248 list_size = extattr_list_fd(arg.filedes, extattr[t].space, list, size);
255 /* Some error happend. Errno should be set by the previous call */
261 /* XXX: Call with an empty buffer may be used to calculate
262 necessary buffer size. Unfortunately, we can't say, how
263 many attributes were returned, so here is the potential
264 problem with the emulation.
267 /* Take the worse case of one char attribute names -
268 two bytes per name plus one more for sanity.
270 total_size += list_size + (list_size/2 + 1)*extattr[t].len;
273 /* Count necessary offset to fit namespace prefixes */
275 for(i = 0; i < list_size; i += list[i] + 1)
276 len += extattr[t].len;
278 total_size += list_size + len;
279 /* Buffer is too small to fit the results */
280 if(total_size > size) {
284 /* Shift results back, so we can prepend prefixes */
285 buf = memmove(list + len, list, list_size);
287 for(i = 0; i < list_size; i += len + 1) {
289 strncpy(list, extattr[t].name, extattr[t].len + 1);
290 list += extattr[t].len;
291 strncpy(list, buf + i + 1, len);
302 #if defined(HAVE_ATTR_LIST) && defined(HAVE_SYS_ATTRIBUTES_H)
303 static char attr_buffer[ATTR_MAX_VALUELEN];
305 static ssize_t irix_attr_list(const char *path, int filedes, char *list, size_t size, int flags)
307 int retval = 0, index;
308 attrlist_cursor_t *cursor = 0;
310 attrlist_t * al = (attrlist_t *)attr_buffer;
312 size_t ent_size, left = size;
317 retval = attr_listf(filedes, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
319 retval = attr_list(path, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
321 for (index = 0; index < al->al_count; index++) {
322 ae = ATTR_ENTRY(attr_buffer, index);
323 ent_size = strlen(ae->a_name) + sizeof("user.");
324 if (left >= ent_size) {
325 strncpy(bp, "user.", sizeof("user."));
326 strncat(bp, ae->a_name, ent_size - sizeof("user."));
334 total_size += ent_size;
336 if (al->al_more == 0) break;
343 retval = attr_listf(filedes, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
345 retval = attr_list(path, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
347 for (index = 0; index < al->al_count; index++) {
348 ae = ATTR_ENTRY(attr_buffer, index);
349 ent_size = strlen(ae->a_name) + sizeof("system.");
350 if (left >= ent_size) {
351 strncpy(bp, "system.", sizeof("system."));
352 strncat(bp, ae->a_name, ent_size - sizeof("system."));
360 total_size += ent_size;
362 if (al->al_more == 0) break;
365 return (ssize_t)(retval ? retval : total_size);
370 #if defined(HAVE_LISTXATTR)
371 static ssize_t remove_user(ssize_t ret, char *list, size_t size)
378 if (ret <= 0 || size == 0)
382 while (ptrsize > 0) {
383 len = strlen(ptr1) +1;
385 if (strncmp(ptr1, "user.",5)) {
389 memmove(ptr, ptr1 +5, len -5);
397 ssize_t sys_listxattr (const char *path, char *list, size_t size)
399 #if defined(HAVE_LISTXATTR)
402 #ifndef XATTR_ADD_OPT
403 ret = listxattr(path, list, size);
406 ret = listxattr(path, list, size, options);
408 return remove_user(ret, list, size);
410 #elif defined(HAVE_LISTEA)
411 return listea(path, list, size);
412 #elif defined(HAVE_EXTATTR_LIST_FILE)
415 return bsd_attr_list(0, arg, list, size);
416 #elif defined(HAVE_ATTR_LIST) && defined(HAVE_SYS_ATTRIBUTES_H)
417 return irix_attr_list(path, 0, list, size, 0);
418 #elif defined(HAVE_ATTROPEN)
420 int attrdirfd = solaris_attropen(path, ".", O_RDONLY, 0);
421 if (attrdirfd >= 0) {
422 ret = solaris_list_xattr(attrdirfd, list, size);
432 ssize_t sys_llistxattr (const char *path, char *list, size_t size)
434 #if defined(HAVE_LLISTXATTR)
437 ret = llistxattr(path, list, size);
438 return remove_user(ret, list, size);
439 #elif defined(HAVE_LISTXATTR) && defined(XATTR_ADD_OPT)
441 int options = XATTR_NOFOLLOW;
443 ret = listxattr(path, list, size, options);
444 return remove_user(ret, list, size);
446 #elif defined(HAVE_LLISTEA)
447 return llistea(path, list, size);
448 #elif defined(HAVE_EXTATTR_LIST_LINK)
451 return bsd_attr_list(1, arg, list, size);
452 #elif defined(HAVE_ATTR_LIST) && defined(HAVE_SYS_ATTRIBUTES_H)
453 return irix_attr_list(path, 0, list, size, ATTR_DONTFOLLOW);
454 #elif defined(HAVE_ATTROPEN)
456 int attrdirfd = solaris_attropen(path, ".", O_RDONLY|AT_SYMLINK_NOFOLLOW, 0);
457 if (attrdirfd >= 0) {
458 ret = solaris_list_xattr(attrdirfd, list, size);
468 int sys_removexattr (const char *path, const char *uname)
470 const char *name = prefix(uname);
471 #if defined(HAVE_REMOVEXATTR)
472 #ifndef XATTR_ADD_OPT
473 return removexattr(path, name);
476 return removexattr(path, name, options);
478 #elif defined(HAVE_REMOVEEA)
479 return removeea(path, name);
480 #elif defined(HAVE_EXTATTR_DELETE_FILE)
482 int attrnamespace = (strncmp(name, "system", 6) == 0) ?
483 EXTATTR_NAMESPACE_SYSTEM : EXTATTR_NAMESPACE_USER;
484 const char *attrname = ((s=strchr_m(name, '.')) == NULL) ? name : s + 1;
486 return extattr_delete_file(path, attrnamespace, attrname);
487 #elif defined(HAVE_ATTR_REMOVE)
489 char *attrname = strchr(name,'.') + 1;
491 if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
493 return attr_remove(path, attrname, flags);
494 #elif defined(HAVE_ATTROPEN)
496 int attrdirfd = solaris_attropen(path, ".", O_RDONLY, 0);
497 if (attrdirfd >= 0) {
498 ret = solaris_unlinkat(attrdirfd, name);
508 int sys_lremovexattr (const char *path, const char *uname)
510 const char *name = prefix(uname);
511 #if defined(HAVE_LREMOVEXATTR)
512 return lremovexattr(path, name);
513 #elif defined(HAVE_REMOVEXATTR) && defined(XATTR_ADD_OPT)
514 int options = XATTR_NOFOLLOW;
515 return removexattr(path, name, options);
516 #elif defined(HAVE_LREMOVEEA)
517 return lremoveea(path, name);
518 #elif defined(HAVE_EXTATTR_DELETE_LINK)
520 int attrnamespace = (strncmp(name, "system", 6) == 0) ?
521 EXTATTR_NAMESPACE_SYSTEM : EXTATTR_NAMESPACE_USER;
522 const char *attrname = ((s=strchr_m(name, '.')) == NULL) ? name : s + 1;
524 return extattr_delete_link(path, attrnamespace, attrname);
525 #elif defined(HAVE_ATTR_REMOVE)
526 int flags = ATTR_DONTFOLLOW;
527 char *attrname = strchr(name,'.') + 1;
529 if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
531 return attr_remove(path, attrname, flags);
532 #elif defined(HAVE_ATTROPEN)
534 int attrdirfd = solaris_attropen(path, ".", O_RDONLY|AT_SYMLINK_NOFOLLOW, 0);
535 if (attrdirfd >= 0) {
536 ret = solaris_unlinkat(attrdirfd, name);
546 #if !defined(HAVE_SETXATTR)
547 #define XATTR_CREATE 0x1 /* set value, fail if attr already exists */
548 #define XATTR_REPLACE 0x2 /* set value, fail if attr does not exist */
551 int sys_setxattr (const char *path, const char *uname, const void *value, size_t size, int flags)
553 const char *name = prefix(uname);
554 #if defined(HAVE_SETXATTR)
555 #ifndef XATTR_ADD_OPT
556 return setxattr(path, name, value, size, flags);
559 return setxattr(path, name, value, size, 0, options);
561 #elif defined(HAVE_SETEA)
562 return setea(path, name, value, size, flags);
563 #elif defined(HAVE_EXTATTR_SET_FILE)
566 int attrnamespace = (strncmp(name, "system", 6) == 0) ?
567 EXTATTR_NAMESPACE_SYSTEM : EXTATTR_NAMESPACE_USER;
568 const char *attrname = ((s=strchr_m(name, '.')) == NULL) ? name : s + 1;
570 /* Check attribute existence */
571 retval = extattr_get_file(path, attrnamespace, attrname, NULL, 0);
573 /* REPLACE attribute, that doesn't exist */
574 if (flags & XATTR_REPLACE && errno == ENOATTR) {
578 /* Ignore other errors */
581 /* CREATE attribute, that already exists */
582 if (flags & XATTR_CREATE) {
588 retval = extattr_set_file(path, attrnamespace, attrname, value, size);
589 return (retval < 0) ? -1 : 0;
590 #elif defined(HAVE_ATTR_SET)
592 char *attrname = strchr(name,'.') + 1;
594 if (strncmp(name, "system", 6) == 0) myflags |= ATTR_ROOT;
595 if (flags & XATTR_CREATE) myflags |= ATTR_CREATE;
596 if (flags & XATTR_REPLACE) myflags |= ATTR_REPLACE;
598 return attr_set(path, attrname, (const char *)value, size, myflags);
599 #elif defined(HAVE_ATTROPEN)
601 int myflags = O_RDWR;
603 if (flags & XATTR_CREATE) myflags |= O_EXCL;
604 if (!(flags & XATTR_REPLACE)) myflags |= O_CREAT;
605 attrfd = solaris_attropen(path, name, myflags, (mode_t) SOLARIS_ATTRMODE);
607 ret = solaris_write_xattr(attrfd, value, size);
617 int sys_lsetxattr (const char *path, const char *uname, const void *value, size_t size, int flags)
619 const char *name = prefix(uname);
620 #if defined(HAVE_LSETXATTR)
621 return lsetxattr(path, name, value, size, flags);
622 #elif defined(HAVE_SETXATTR) && defined(XATTR_ADD_OPT)
623 int options = XATTR_NOFOLLOW;
624 return setxattr(path, name, value, size, 0, options);
625 #elif defined(LSETEA)
626 return lsetea(path, name, value, size, flags);
627 #elif defined(HAVE_EXTATTR_SET_LINK)
630 int attrnamespace = (strncmp(name, "system", 6) == 0) ?
631 EXTATTR_NAMESPACE_SYSTEM : EXTATTR_NAMESPACE_USER;
632 const char *attrname = ((s=strchr_m(name, '.')) == NULL) ? name : s + 1;
634 /* Check attribute existence */
635 retval = extattr_get_link(path, attrnamespace, attrname, NULL, 0);
637 /* REPLACE attribute, that doesn't exist */
638 if (flags & XATTR_REPLACE && errno == ENOATTR) {
642 /* Ignore other errors */
645 /* CREATE attribute, that already exists */
646 if (flags & XATTR_CREATE) {
653 retval = extattr_set_link(path, attrnamespace, attrname, value, size);
654 return (retval < 0) ? -1 : 0;
655 #elif defined(HAVE_ATTR_SET)
656 int myflags = ATTR_DONTFOLLOW;
657 char *attrname = strchr(name,'.') + 1;
659 if (strncmp(name, "system", 6) == 0) myflags |= ATTR_ROOT;
660 if (flags & XATTR_CREATE) myflags |= ATTR_CREATE;
661 if (flags & XATTR_REPLACE) myflags |= ATTR_REPLACE;
663 return attr_set(path, attrname, (const char *)value, size, myflags);
664 #elif defined(HAVE_ATTROPEN)
666 int myflags = O_RDWR | AT_SYMLINK_NOFOLLOW;
668 if (flags & XATTR_CREATE) myflags |= O_EXCL;
669 if (!(flags & XATTR_REPLACE)) myflags |= O_CREAT;
670 attrfd = solaris_attropen(path, name, myflags, (mode_t) SOLARIS_ATTRMODE);
672 ret = solaris_write_xattr(attrfd, value, size);
682 /**************************************************************************
683 helper functions for Solaris' EA support
684 ****************************************************************************/
686 static ssize_t solaris_read_xattr(int attrfd, void *value, size_t size)
690 if (fstat(attrfd, &sbuf) == -1) {
695 /* This is to return the current size of the named extended attribute */
700 /* check size and read xattr */
701 if (sbuf.st_size > size) {
706 return read(attrfd, value, sbuf.st_size);
709 static ssize_t solaris_list_xattr(int attrdirfd, char *list, size_t size)
715 int newfd = dup(attrdirfd);
716 /* CAUTION: The originating file descriptor should not be
717 used again following the call to fdopendir().
718 For that reason we dup() the file descriptor
719 here to make things more clear. */
720 dirp = fdopendir(newfd);
722 while ((de = readdir(dirp))) {
724 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..") ||
725 !strcmp(dp->d_name, "SUNWattr_ro") || !strcmp(dp->d_name, "SUNWattr_rw"))
727 /* we don't want "." and ".." here: */
728 LOG(log_maxdebug, logtype_default, "skipped EA %s\n",de->d_name);
732 listlen = strlen(de->d_name);
734 /* return the current size of the list of extended attribute names*/
737 /* check size and copy entry + nul into list. */
738 if ((len + listlen + 1) > size) {
743 strcpy(list + len, de->d_name);
751 if (closedir(dirp) == -1) {
752 LOG(log_debug, logtype_default, "closedir dirp failed: %s\n",strerror(errno));
758 static int solaris_unlinkat(int attrdirfd, const char *name)
760 if (unlinkat(attrdirfd, name, 0) == -1) {
761 if (errno == ENOENT) {
769 static int solaris_attropen(const char *path, const char *attrpath, int oflag, mode_t mode)
771 int filedes = attropen(path, attrpath, oflag, mode);
773 LOG(log_maxdebug, logtype_default, "attropen FAILED: path: %s, name: %s, errno: %s\n",path,attrpath,strerror(errno));
774 if (errno == EINVAL) {
783 static int solaris_openat(int fildes, const char *path, int oflag, mode_t mode)
785 int filedes = openat(fildes, path, oflag, mode);
787 LOG(log_maxdebug, logtype_default, "openat FAILED: fd: %s, path: %s, errno: %s\n",filedes,path,strerror(errno));
788 if (errno == EINVAL) {
797 static int solaris_write_xattr(int attrfd, const char *value, size_t size)
799 if ((ftruncate(attrfd, 0) == 0) && (write(attrfd, value, size) == size)) {
802 LOG(log_maxdebug, logtype_default, "solaris_write_xattr FAILED!\n");
806 #endif /*HAVE_ATTROPEN*/