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.5 2009-11-13 13:31:13 didg Exp $
34 #include <sys/types.h>
37 #include <attr/xattr.h>
38 #elif HAVE_SYS_XATTR_H
39 #include <sys/xattr.h>
46 #ifdef HAVE_SYS_EXTATTR_H
47 #include <sys/extattr.h>
50 #include <atalk/adouble.h>
51 #include <atalk/util.h>
52 #include <atalk/logger.h>
54 /******** Solaris EA helper function prototypes ********/
56 #define SOLARIS_ATTRMODE S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP
57 static int solaris_write_xattr(int attrfd, const char *value, size_t size);
58 static ssize_t solaris_read_xattr(int attrfd, void *value, size_t size);
59 static ssize_t solaris_list_xattr(int attrdirfd, char *list, size_t size);
60 static int solaris_unlinkat(int attrdirfd, const char *name);
61 static int solaris_attropen(const char *path, const char *attrpath, int oflag, mode_t mode);
62 static int solaris_openat(int fildes, const char *path, int oflag, mode_t mode);
65 /**************************************************************************
66 Wrappers for extented attribute calls. Based on the Linux package with
67 support for IRIX and (Net|Free)BSD also. Expand as other systems have them.
68 ****************************************************************************/
69 static char attr_name[256 +5] = "user.";
71 static const char *prefix(const char *uname)
73 #if defined(HAVE_ATTROPEN)
76 strlcpy(attr_name +5, uname, 256);
81 ssize_t sys_getxattr (const char *path, const char *uname, void *value, size_t size)
83 const char *name = prefix(uname);
85 #if defined(HAVE_GETXATTR)
87 return getxattr(path, name, value, size);
90 return getxattr(path, name, value, size, 0, options);
92 #elif defined(HAVE_GETEA)
93 return getea(path, name, value, size);
94 #elif defined(HAVE_EXTATTR_GET_FILE)
97 int attrnamespace = (strncmp(name, "system", 6) == 0) ?
98 EXTATTR_NAMESPACE_SYSTEM : EXTATTR_NAMESPACE_USER;
99 const char *attrname = ((s=strchr_m(name, '.')) == NULL) ? name : s + 1;
101 * The BSD implementation has a nasty habit of silently truncating
102 * the returned value to the size of the buffer, so we have to check
103 * that the buffer is large enough to fit the returned value.
105 if((retval=extattr_get_file(path, attrnamespace, attrname, NULL, 0)) >= 0) {
110 if((retval=extattr_get_file(path, attrnamespace, attrname, value, size)) >= 0)
114 LOG(log_maxdebug, logtype_default, "sys_getxattr: extattr_get_file() failed with: %s\n", strerror(errno));
116 #elif defined(HAVE_ATTR_GET)
117 int retval, flags = 0;
118 int valuelength = (int)size;
119 char *attrname = strchr(name,'.') + 1;
121 if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
123 retval = attr_get(path, attrname, (char *)value, &valuelength, flags);
125 return retval ? retval : valuelength;
126 #elif defined(HAVE_ATTROPEN)
128 int attrfd = solaris_attropen(path, name, O_RDONLY, 0);
130 ret = solaris_read_xattr(attrfd, value, size);
140 ssize_t sys_lgetxattr (const char *path, const char *uname, void *value, size_t size)
142 const char *name = prefix(uname);
144 #if defined(HAVE_LGETXATTR)
145 return lgetxattr(path, name, value, size);
146 #elif defined(HAVE_GETXATTR) && defined(XATTR_ADD_OPT)
147 int options = XATTR_NOFOLLOW;
148 return getxattr(path, name, value, size, 0, options);
149 #elif defined(HAVE_LGETEA)
150 return lgetea(path, name, value, size);
151 #elif defined(HAVE_EXTATTR_GET_LINK)
154 int attrnamespace = (strncmp(name, "system", 6) == 0) ?
155 EXTATTR_NAMESPACE_SYSTEM : EXTATTR_NAMESPACE_USER;
156 const char *attrname = ((s=strchr_m(name, '.')) == NULL) ? name : s + 1;
158 if((retval=extattr_get_link(path, attrnamespace, attrname, NULL, 0)) >= 0) {
163 if((retval=extattr_get_link(path, attrnamespace, attrname, value, size)) >= 0)
167 LOG(log_maxdebug, logtype_default, "sys_lgetxattr: extattr_get_link() failed with: %s\n", strerror(errno));
169 #elif defined(HAVE_ATTR_GET)
170 int retval, flags = ATTR_DONTFOLLOW;
171 int valuelength = (int)size;
172 char *attrname = strchr(name,'.') + 1;
174 if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
176 retval = attr_get(path, attrname, (char *)value, &valuelength, flags);
178 return retval ? retval : valuelength;
179 #elif defined(HAVE_ATTROPEN)
181 int attrfd = solaris_attropen(path, name, O_RDONLY|AT_SYMLINK_NOFOLLOW, 0);
183 ret = solaris_read_xattr(attrfd, value, size);
193 #if defined(HAVE_EXTATTR_LIST_FILE)
195 #define EXTATTR_PREFIX(s) (s), (sizeof((s))-1)
203 { EXTATTR_NAMESPACE_SYSTEM, EXTATTR_PREFIX("system.") },
204 { EXTATTR_NAMESPACE_USER, EXTATTR_PREFIX("user.") },
212 static ssize_t bsd_attr_list (int type, extattr_arg arg, char *list, size_t size)
214 ssize_t list_size, total_size = 0;
217 /* Iterate through extattr(2) namespaces */
218 for(t = 0; t < (sizeof(extattr)/sizeof(extattr[0])); t++) {
220 #if defined(HAVE_EXTATTR_LIST_FILE)
222 list_size = extattr_list_file(arg.path, extattr[t].space, list, size);
225 #if defined(HAVE_EXTATTR_LIST_LINK)
227 list_size = extattr_list_link(arg.path, extattr[t].space, list, size);
230 #if defined(HAVE_EXTATTR_LIST_FD)
232 list_size = extattr_list_fd(arg.filedes, extattr[t].space, list, size);
239 /* Some error happend. Errno should be set by the previous call */
245 /* XXX: Call with an empty buffer may be used to calculate
246 necessary buffer size. Unfortunately, we can't say, how
247 many attributes were returned, so here is the potential
248 problem with the emulation.
251 /* Take the worse case of one char attribute names -
252 two bytes per name plus one more for sanity.
254 total_size += list_size + (list_size/2 + 1)*extattr[t].len;
257 /* Count necessary offset to fit namespace prefixes */
259 for(i = 0; i < list_size; i += list[i] + 1)
260 len += extattr[t].len;
262 total_size += list_size + len;
263 /* Buffer is too small to fit the results */
264 if(total_size > size) {
268 /* Shift results back, so we can prepend prefixes */
269 buf = memmove(list + len, list, list_size);
271 for(i = 0; i < list_size; i += len + 1) {
273 strncpy(list, extattr[t].name, extattr[t].len + 1);
274 list += extattr[t].len;
275 strncpy(list, buf + i + 1, len);
286 #if defined(HAVE_ATTR_LIST) && defined(HAVE_SYS_ATTRIBUTES_H)
287 static char attr_buffer[ATTR_MAX_VALUELEN];
289 static ssize_t irix_attr_list(const char *path, int filedes, char *list, size_t size, int flags)
291 int retval = 0, index;
292 attrlist_cursor_t *cursor = 0;
294 attrlist_t * al = (attrlist_t *)attr_buffer;
296 size_t ent_size, left = size;
301 retval = attr_listf(filedes, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
303 retval = attr_list(path, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
305 for (index = 0; index < al->al_count; index++) {
306 ae = ATTR_ENTRY(attr_buffer, index);
307 ent_size = strlen(ae->a_name) + sizeof("user.");
308 if (left >= ent_size) {
309 strncpy(bp, "user.", sizeof("user."));
310 strncat(bp, ae->a_name, ent_size - sizeof("user."));
318 total_size += ent_size;
320 if (al->al_more == 0) break;
327 retval = attr_listf(filedes, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
329 retval = attr_list(path, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
331 for (index = 0; index < al->al_count; index++) {
332 ae = ATTR_ENTRY(attr_buffer, index);
333 ent_size = strlen(ae->a_name) + sizeof("system.");
334 if (left >= ent_size) {
335 strncpy(bp, "system.", sizeof("system."));
336 strncat(bp, ae->a_name, ent_size - sizeof("system."));
344 total_size += ent_size;
346 if (al->al_more == 0) break;
349 return (ssize_t)(retval ? retval : total_size);
354 #if defined(HAVE_LISTXATTR)
355 static ssize_t remove_user(ssize_t ret, char *list, size_t size)
362 if (ret <= 0 || size == 0)
366 while (ptrsize > 0) {
367 len = strlen(ptr1) +1;
369 if (strncmp(ptr1, "user.",5)) {
373 memmove(ptr, ptr1 +5, len -5);
381 ssize_t sys_listxattr (const char *path, char *list, size_t size)
383 #if defined(HAVE_LISTXATTR)
386 #ifndef XATTR_ADD_OPT
387 ret = listxattr(path, list, size);
390 ret = listxattr(path, list, size, options);
392 return remove_user(ret, list, size);
394 #elif defined(HAVE_LISTEA)
395 return listea(path, list, size);
396 #elif defined(HAVE_EXTATTR_LIST_FILE)
399 return bsd_attr_list(0, arg, list, size);
400 #elif defined(HAVE_ATTR_LIST) && defined(HAVE_SYS_ATTRIBUTES_H)
401 return irix_attr_list(path, 0, list, size, 0);
402 #elif defined(HAVE_ATTROPEN)
404 int attrdirfd = solaris_attropen(path, ".", O_RDONLY, 0);
405 if (attrdirfd >= 0) {
406 ret = solaris_list_xattr(attrdirfd, list, size);
416 ssize_t sys_llistxattr (const char *path, char *list, size_t size)
418 #if defined(HAVE_LLISTXATTR)
421 ret = llistxattr(path, list, size);
422 return remove_user(ret, list, size);
423 #elif defined(HAVE_LISTXATTR) && defined(XATTR_ADD_OPT)
425 int options = XATTR_NOFOLLOW;
427 ret = listxattr(path, list, size, options);
428 return remove_user(ret, list, size);
430 #elif defined(HAVE_LLISTEA)
431 return llistea(path, list, size);
432 #elif defined(HAVE_EXTATTR_LIST_LINK)
435 return bsd_attr_list(1, arg, list, size);
436 #elif defined(HAVE_ATTR_LIST) && defined(HAVE_SYS_ATTRIBUTES_H)
437 return irix_attr_list(path, 0, list, size, ATTR_DONTFOLLOW);
438 #elif defined(HAVE_ATTROPEN)
440 int attrdirfd = solaris_attropen(path, ".", O_RDONLY|AT_SYMLINK_NOFOLLOW, 0);
441 if (attrdirfd >= 0) {
442 ret = solaris_list_xattr(attrdirfd, list, size);
452 int sys_removexattr (const char *path, const char *uname)
454 const char *name = prefix(uname);
455 #if defined(HAVE_REMOVEXATTR)
456 #ifndef XATTR_ADD_OPT
457 return removexattr(path, name);
460 return removexattr(path, name, options);
462 #elif defined(HAVE_REMOVEEA)
463 return removeea(path, name);
464 #elif defined(HAVE_EXTATTR_DELETE_FILE)
466 int attrnamespace = (strncmp(name, "system", 6) == 0) ?
467 EXTATTR_NAMESPACE_SYSTEM : EXTATTR_NAMESPACE_USER;
468 const char *attrname = ((s=strchr_m(name, '.')) == NULL) ? name : s + 1;
470 return extattr_delete_file(path, attrnamespace, attrname);
471 #elif defined(HAVE_ATTR_REMOVE)
473 char *attrname = strchr(name,'.') + 1;
475 if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
477 return attr_remove(path, attrname, flags);
478 #elif defined(HAVE_ATTROPEN)
480 int attrdirfd = solaris_attropen(path, ".", O_RDONLY, 0);
481 if (attrdirfd >= 0) {
482 ret = solaris_unlinkat(attrdirfd, name);
492 int sys_lremovexattr (const char *path, const char *uname)
494 const char *name = prefix(uname);
495 #if defined(HAVE_LREMOVEXATTR)
496 return lremovexattr(path, name);
497 #elif defined(HAVE_REMOVEXATTR) && defined(XATTR_ADD_OPT)
498 int options = XATTR_NOFOLLOW;
499 return removexattr(path, name, options);
500 #elif defined(HAVE_LREMOVEEA)
501 return lremoveea(path, name);
502 #elif defined(HAVE_EXTATTR_DELETE_LINK)
504 int attrnamespace = (strncmp(name, "system", 6) == 0) ?
505 EXTATTR_NAMESPACE_SYSTEM : EXTATTR_NAMESPACE_USER;
506 const char *attrname = ((s=strchr_m(name, '.')) == NULL) ? name : s + 1;
508 return extattr_delete_link(path, attrnamespace, attrname);
509 #elif defined(HAVE_ATTR_REMOVE)
510 int flags = ATTR_DONTFOLLOW;
511 char *attrname = strchr(name,'.') + 1;
513 if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
515 return attr_remove(path, attrname, flags);
516 #elif defined(HAVE_ATTROPEN)
518 int attrdirfd = solaris_attropen(path, ".", O_RDONLY|AT_SYMLINK_NOFOLLOW, 0);
519 if (attrdirfd >= 0) {
520 ret = solaris_unlinkat(attrdirfd, name);
530 #if !defined(HAVE_SETXATTR)
531 #define XATTR_CREATE 0x1 /* set value, fail if attr already exists */
532 #define XATTR_REPLACE 0x2 /* set value, fail if attr does not exist */
535 int sys_setxattr (const char *path, const char *uname, const void *value, size_t size, int flags)
537 const char *name = prefix(uname);
538 #if defined(HAVE_SETXATTR)
539 #ifndef XATTR_ADD_OPT
540 return setxattr(path, name, value, size, flags);
543 return setxattr(path, name, value, size, 0, options);
545 #elif defined(HAVE_SETEA)
546 return setea(path, name, value, size, flags);
547 #elif defined(HAVE_EXTATTR_SET_FILE)
550 int attrnamespace = (strncmp(name, "system", 6) == 0) ?
551 EXTATTR_NAMESPACE_SYSTEM : EXTATTR_NAMESPACE_USER;
552 const char *attrname = ((s=strchr_m(name, '.')) == NULL) ? name : s + 1;
554 /* Check attribute existence */
555 retval = extattr_get_file(path, attrnamespace, attrname, NULL, 0);
557 /* REPLACE attribute, that doesn't exist */
558 if (flags & XATTR_REPLACE && errno == ENOATTR) {
562 /* Ignore other errors */
565 /* CREATE attribute, that already exists */
566 if (flags & XATTR_CREATE) {
572 retval = extattr_set_file(path, attrnamespace, attrname, value, size);
573 return (retval < 0) ? -1 : 0;
574 #elif defined(HAVE_ATTR_SET)
576 char *attrname = strchr(name,'.') + 1;
578 if (strncmp(name, "system", 6) == 0) myflags |= ATTR_ROOT;
579 if (flags & XATTR_CREATE) myflags |= ATTR_CREATE;
580 if (flags & XATTR_REPLACE) myflags |= ATTR_REPLACE;
582 return attr_set(path, attrname, (const char *)value, size, myflags);
583 #elif defined(HAVE_ATTROPEN)
585 int myflags = O_RDWR;
587 if (flags & XATTR_CREATE) myflags |= O_EXCL;
588 if (!(flags & XATTR_REPLACE)) myflags |= O_CREAT;
589 attrfd = solaris_attropen(path, name, myflags, (mode_t) SOLARIS_ATTRMODE);
591 ret = solaris_write_xattr(attrfd, value, size);
601 int sys_lsetxattr (const char *path, const char *uname, const void *value, size_t size, int flags)
603 const char *name = prefix(uname);
604 #if defined(HAVE_LSETXATTR)
605 return lsetxattr(path, name, value, size, flags);
606 #elif defined(HAVE_SETXATTR) && defined(XATTR_ADD_OPT)
607 int options = XATTR_NOFOLLOW;
608 return setxattr(path, name, value, size, 0, options);
609 #elif defined(LSETEA)
610 return lsetea(path, name, value, size, flags);
611 #elif defined(HAVE_EXTATTR_SET_LINK)
614 int attrnamespace = (strncmp(name, "system", 6) == 0) ?
615 EXTATTR_NAMESPACE_SYSTEM : EXTATTR_NAMESPACE_USER;
616 const char *attrname = ((s=strchr_m(name, '.')) == NULL) ? name : s + 1;
618 /* Check attribute existence */
619 retval = extattr_get_link(path, attrnamespace, attrname, NULL, 0);
621 /* REPLACE attribute, that doesn't exist */
622 if (flags & XATTR_REPLACE && errno == ENOATTR) {
626 /* Ignore other errors */
629 /* CREATE attribute, that already exists */
630 if (flags & XATTR_CREATE) {
637 retval = extattr_set_link(path, attrnamespace, attrname, value, size);
638 return (retval < 0) ? -1 : 0;
639 #elif defined(HAVE_ATTR_SET)
640 int myflags = ATTR_DONTFOLLOW;
641 char *attrname = strchr(name,'.') + 1;
643 if (strncmp(name, "system", 6) == 0) myflags |= ATTR_ROOT;
644 if (flags & XATTR_CREATE) myflags |= ATTR_CREATE;
645 if (flags & XATTR_REPLACE) myflags |= ATTR_REPLACE;
647 return attr_set(path, attrname, (const char *)value, size, myflags);
648 #elif defined(HAVE_ATTROPEN)
650 int myflags = O_RDWR | AT_SYMLINK_NOFOLLOW;
652 if (flags & XATTR_CREATE) myflags |= O_EXCL;
653 if (!(flags & XATTR_REPLACE)) myflags |= O_CREAT;
654 attrfd = solaris_attropen(path, name, myflags, (mode_t) SOLARIS_ATTRMODE);
656 ret = solaris_write_xattr(attrfd, value, size);
666 /**************************************************************************
667 helper functions for Solaris' EA support
668 ****************************************************************************/
670 static ssize_t solaris_read_xattr(int attrfd, void *value, size_t size)
674 if (fstat(attrfd, &sbuf) == -1) {
679 /* This is to return the current size of the named extended attribute */
684 /* check size and read xattr */
685 if (sbuf.st_size > size) {
690 return read(attrfd, value, sbuf.st_size);
693 static ssize_t solaris_list_xattr(int attrdirfd, char *list, size_t size)
699 int newfd = dup(attrdirfd);
700 /* CAUTION: The originating file descriptor should not be
701 used again following the call to fdopendir().
702 For that reason we dup() the file descriptor
703 here to make things more clear. */
704 dirp = fdopendir(newfd);
706 while ((de = readdir(dirp))) {
708 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..") ||
709 !strcmp(dp->d_name, "SUNWattr_ro") || !strcmp(dp->d_name, "SUNWattr_rw"))
711 /* we don't want "." and ".." here: */
712 LOG(log_maxdebug, logtype_default, "skipped EA %s\n",de->d_name);
716 listlen = strlen(de->d_name);
718 /* return the current size of the list of extended attribute names*/
721 /* check size and copy entry + nul into list. */
722 if ((len + listlen + 1) > size) {
727 strcpy(list + len, de->d_name);
735 if (closedir(dirp) == -1) {
736 LOG(log_debug, logtype_default, "closedir dirp failed: %s\n",strerror(errno));
742 static int solaris_unlinkat(int attrdirfd, const char *name)
744 if (unlinkat(attrdirfd, name, 0) == -1) {
745 if (errno == ENOENT) {
753 static int solaris_attropen(const char *path, const char *attrpath, int oflag, mode_t mode)
755 int filedes = attropen(path, attrpath, oflag, mode);
757 LOG(log_maxdebug, logtype_default, "attropen FAILED: path: %s, name: %s, errno: %s\n",path,attrpath,strerror(errno));
758 if (errno == EINVAL) {
767 static int solaris_openat(int fildes, const char *path, int oflag, mode_t mode)
769 int filedes = openat(fildes, path, oflag, mode);
771 LOG(log_maxdebug, logtype_default, "openat FAILED: fd: %s, path: %s, errno: %s\n",filedes,path,strerror(errno));
772 if (errno == EINVAL) {
781 static int solaris_write_xattr(int attrfd, const char *value, size_t size)
783 if ((ftruncate(attrfd, 0) == 0) && (write(attrfd, value, size) == size)) {
786 LOG(log_maxdebug, logtype_default, "solaris_write_xattr FAILED!\n");
790 #endif /*HAVE_ATTROPEN*/