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.
28 $Id: sys_ea.c,v 1.6 2009-12-04 10:26:10 franklahm Exp $
38 #include <sys/types.h>
42 #include <attr/xattr.h>
43 #elif HAVE_SYS_XATTR_H
44 #include <sys/xattr.h>
56 #ifdef HAVE_SYS_EXTATTR_H
57 #include <sys/extattr.h>
60 #include <atalk/adouble.h>
61 #include <atalk/util.h>
62 #include <atalk/logger.h>
66 #define ENOATTR ENODATA
69 /******** Solaris EA helper function prototypes ********/
71 #define SOLARIS_ATTRMODE S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP
72 static int solaris_write_xattr(int attrfd, const char *value, size_t size);
73 static ssize_t solaris_read_xattr(int attrfd, void *value, size_t size);
74 static ssize_t solaris_list_xattr(int attrdirfd, char *list, size_t size);
75 static int solaris_unlinkat(int attrdirfd, const char *name);
76 static int solaris_attropen(const char *path, const char *attrpath, int oflag, mode_t mode);
77 static int solaris_openat(int fildes, const char *path, int oflag, mode_t mode);
80 /**************************************************************************
81 Wrappers for extented attribute calls. Based on the Linux package with
82 support for IRIX and (Net|Free)BSD also. Expand as other systems have them.
83 ****************************************************************************/
84 static char attr_name[256 +5] = "user.";
86 static const char *prefix(const char *uname)
88 #if defined(HAVE_ATTROPEN)
91 strlcpy(attr_name +5, uname, 256);
96 ssize_t sys_getxattr (const char *path, const char *uname, void *value, size_t size)
98 const char *name = prefix(uname);
100 #if defined(HAVE_GETXATTR)
101 #ifndef XATTR_ADD_OPT
102 return getxattr(path, name, value, size);
105 return getxattr(path, name, value, size, 0, options);
107 #elif defined(HAVE_GETEA)
108 return getea(path, name, value, size);
109 #elif defined(HAVE_EXTATTR_GET_FILE)
112 int attrnamespace = (strncmp(name, "system", 6) == 0) ?
113 EXTATTR_NAMESPACE_SYSTEM : EXTATTR_NAMESPACE_USER;
114 const char *attrname = ((s=strchr(name, '.')) == NULL) ? name : s + 1;
116 * The BSD implementation has a nasty habit of silently truncating
117 * the returned value to the size of the buffer, so we have to check
118 * that the buffer is large enough to fit the returned value.
120 if((retval=extattr_get_file(path, attrnamespace, attrname, NULL, 0)) >= 0) {
125 if((retval=extattr_get_file(path, attrnamespace, attrname, value, size)) >= 0)
129 LOG(log_maxdebug, logtype_default, "sys_getxattr: extattr_get_file() failed with: %s\n", strerror(errno));
131 #elif defined(HAVE_ATTR_GET)
132 int retval, flags = 0;
133 int valuelength = (int)size;
134 char *attrname = strchr(name,'.') + 1;
136 if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
138 retval = attr_get(path, attrname, (char *)value, &valuelength, flags);
140 return retval ? retval : valuelength;
141 #elif defined(HAVE_ATTROPEN)
143 int attrfd = solaris_attropen(path, name, O_RDONLY, 0);
145 ret = solaris_read_xattr(attrfd, value, size);
155 ssize_t sys_lgetxattr (const char *path, const char *uname, void *value, size_t size)
157 const char *name = prefix(uname);
159 #if defined(HAVE_LGETXATTR)
160 return lgetxattr(path, name, value, size);
161 #elif defined(HAVE_GETXATTR) && defined(XATTR_ADD_OPT)
162 int options = XATTR_NOFOLLOW;
163 return getxattr(path, name, value, size, 0, options);
164 #elif defined(HAVE_LGETEA)
165 return lgetea(path, name, value, size);
166 #elif defined(HAVE_EXTATTR_GET_LINK)
169 int attrnamespace = (strncmp(name, "system", 6) == 0) ?
170 EXTATTR_NAMESPACE_SYSTEM : EXTATTR_NAMESPACE_USER;
171 const char *attrname = ((s=strchr(name, '.')) == NULL) ? name : s + 1;
173 if((retval=extattr_get_link(path, attrnamespace, attrname, NULL, 0)) >= 0) {
178 if((retval=extattr_get_link(path, attrnamespace, attrname, value, size)) >= 0)
182 LOG(log_maxdebug, logtype_default, "sys_lgetxattr: extattr_get_link() failed with: %s\n", strerror(errno));
184 #elif defined(HAVE_ATTR_GET)
185 int retval, flags = ATTR_DONTFOLLOW;
186 int valuelength = (int)size;
187 char *attrname = strchr(name,'.') + 1;
189 if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
191 retval = attr_get(path, attrname, (char *)value, &valuelength, flags);
193 return retval ? retval : valuelength;
194 #elif defined(HAVE_ATTROPEN)
196 int attrfd = solaris_attropen(path, name, O_RDONLY|AT_SYMLINK_NOFOLLOW, 0);
198 ret = solaris_read_xattr(attrfd, value, size);
208 #if defined(HAVE_EXTATTR_LIST_FILE)
210 #define EXTATTR_PREFIX(s) (s), (sizeof((s))-1)
218 { EXTATTR_NAMESPACE_SYSTEM, EXTATTR_PREFIX("system.") },
219 { EXTATTR_NAMESPACE_USER, EXTATTR_PREFIX("user.") },
227 static ssize_t bsd_attr_list (int type, extattr_arg arg, char *list, size_t size)
229 ssize_t list_size, total_size = 0;
232 /* Iterate through extattr(2) namespaces */
233 for(t = 0; t < (sizeof(extattr)/sizeof(extattr[0])); t++) {
235 #if defined(HAVE_EXTATTR_LIST_FILE)
237 list_size = extattr_list_file(arg.path, extattr[t].space, list, size);
240 #if defined(HAVE_EXTATTR_LIST_LINK)
242 list_size = extattr_list_link(arg.path, extattr[t].space, list, size);
245 #if defined(HAVE_EXTATTR_LIST_FD)
247 list_size = extattr_list_fd(arg.filedes, extattr[t].space, list, size);
254 /* Some error happend. Errno should be set by the previous call */
260 /* XXX: Call with an empty buffer may be used to calculate
261 necessary buffer size. Unfortunately, we can't say, how
262 many attributes were returned, so here is the potential
263 problem with the emulation.
266 /* Take the worse case of one char attribute names -
267 two bytes per name plus one more for sanity.
269 total_size += list_size + (list_size/2 + 1)*extattr[t].len;
272 /* Count necessary offset to fit namespace prefixes */
274 for(i = 0; i < list_size; i += list[i] + 1)
275 len += extattr[t].len;
277 total_size += list_size + len;
278 /* Buffer is too small to fit the results */
279 if(total_size > size) {
283 /* Shift results back, so we can prepend prefixes */
284 buf = memmove(list + len, list, list_size);
286 for(i = 0; i < list_size; i += len + 1) {
288 strncpy(list, extattr[t].name, extattr[t].len + 1);
289 list += extattr[t].len;
290 strncpy(list, buf + i + 1, len);
301 #if defined(HAVE_ATTR_LIST) && defined(HAVE_SYS_ATTRIBUTES_H)
302 static char attr_buffer[ATTR_MAX_VALUELEN];
304 static ssize_t irix_attr_list(const char *path, int filedes, char *list, size_t size, int flags)
306 int retval = 0, index;
307 attrlist_cursor_t *cursor = 0;
309 attrlist_t * al = (attrlist_t *)attr_buffer;
311 size_t ent_size, left = size;
316 retval = attr_listf(filedes, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
318 retval = attr_list(path, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
320 for (index = 0; index < al->al_count; index++) {
321 ae = ATTR_ENTRY(attr_buffer, index);
322 ent_size = strlen(ae->a_name) + sizeof("user.");
323 if (left >= ent_size) {
324 strncpy(bp, "user.", sizeof("user."));
325 strncat(bp, ae->a_name, ent_size - sizeof("user."));
333 total_size += ent_size;
335 if (al->al_more == 0) break;
342 retval = attr_listf(filedes, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
344 retval = attr_list(path, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
346 for (index = 0; index < al->al_count; index++) {
347 ae = ATTR_ENTRY(attr_buffer, index);
348 ent_size = strlen(ae->a_name) + sizeof("system.");
349 if (left >= ent_size) {
350 strncpy(bp, "system.", sizeof("system."));
351 strncat(bp, ae->a_name, ent_size - sizeof("system."));
359 total_size += ent_size;
361 if (al->al_more == 0) break;
364 return (ssize_t)(retval ? retval : total_size);
369 #if defined(HAVE_LISTXATTR)
370 static ssize_t remove_user(ssize_t ret, char *list, size_t size)
377 if (ret <= 0 || size == 0)
381 while (ptrsize > 0) {
382 len = strlen(ptr1) +1;
384 if (strncmp(ptr1, "user.",5)) {
388 memmove(ptr, ptr1 +5, len -5);
396 ssize_t sys_listxattr (const char *path, char *list, size_t size)
398 #if defined(HAVE_LISTXATTR)
401 #ifndef XATTR_ADD_OPT
402 ret = listxattr(path, list, size);
405 ret = listxattr(path, list, size, options);
407 return remove_user(ret, list, size);
409 #elif defined(HAVE_LISTEA)
410 return listea(path, list, size);
411 #elif defined(HAVE_EXTATTR_LIST_FILE)
414 return bsd_attr_list(0, arg, list, size);
415 #elif defined(HAVE_ATTR_LIST) && defined(HAVE_SYS_ATTRIBUTES_H)
416 return irix_attr_list(path, 0, list, size, 0);
417 #elif defined(HAVE_ATTROPEN)
419 int attrdirfd = solaris_attropen(path, ".", O_RDONLY, 0);
420 if (attrdirfd >= 0) {
421 ret = solaris_list_xattr(attrdirfd, list, size);
431 ssize_t sys_llistxattr (const char *path, char *list, size_t size)
433 #if defined(HAVE_LLISTXATTR)
436 ret = llistxattr(path, list, size);
437 return remove_user(ret, list, size);
438 #elif defined(HAVE_LISTXATTR) && defined(XATTR_ADD_OPT)
440 int options = XATTR_NOFOLLOW;
442 ret = listxattr(path, list, size, options);
443 return remove_user(ret, list, size);
445 #elif defined(HAVE_LLISTEA)
446 return llistea(path, list, size);
447 #elif defined(HAVE_EXTATTR_LIST_LINK)
450 return bsd_attr_list(1, arg, list, size);
451 #elif defined(HAVE_ATTR_LIST) && defined(HAVE_SYS_ATTRIBUTES_H)
452 return irix_attr_list(path, 0, list, size, ATTR_DONTFOLLOW);
453 #elif defined(HAVE_ATTROPEN)
455 int attrdirfd = solaris_attropen(path, ".", O_RDONLY|AT_SYMLINK_NOFOLLOW, 0);
456 if (attrdirfd >= 0) {
457 ret = solaris_list_xattr(attrdirfd, list, size);
467 int sys_removexattr (const char *path, const char *uname)
469 const char *name = prefix(uname);
470 #if defined(HAVE_REMOVEXATTR)
471 #ifndef XATTR_ADD_OPT
472 return removexattr(path, name);
475 return removexattr(path, name, options);
477 #elif defined(HAVE_REMOVEEA)
478 return removeea(path, name);
479 #elif defined(HAVE_EXTATTR_DELETE_FILE)
481 int attrnamespace = (strncmp(name, "system", 6) == 0) ?
482 EXTATTR_NAMESPACE_SYSTEM : EXTATTR_NAMESPACE_USER;
483 const char *attrname = ((s=strchr(name, '.')) == NULL) ? name : s + 1;
485 return extattr_delete_file(path, attrnamespace, attrname);
486 #elif defined(HAVE_ATTR_REMOVE)
488 char *attrname = strchr(name,'.') + 1;
490 if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
492 return attr_remove(path, attrname, flags);
493 #elif defined(HAVE_ATTROPEN)
495 int attrdirfd = solaris_attropen(path, ".", O_RDONLY, 0);
496 if (attrdirfd >= 0) {
497 ret = solaris_unlinkat(attrdirfd, name);
507 int sys_lremovexattr (const char *path, const char *uname)
509 const char *name = prefix(uname);
510 #if defined(HAVE_LREMOVEXATTR)
511 return lremovexattr(path, name);
512 #elif defined(HAVE_REMOVEXATTR) && defined(XATTR_ADD_OPT)
513 int options = XATTR_NOFOLLOW;
514 return removexattr(path, name, options);
515 #elif defined(HAVE_LREMOVEEA)
516 return lremoveea(path, name);
517 #elif defined(HAVE_EXTATTR_DELETE_LINK)
519 int attrnamespace = (strncmp(name, "system", 6) == 0) ?
520 EXTATTR_NAMESPACE_SYSTEM : EXTATTR_NAMESPACE_USER;
521 const char *attrname = ((s=strchr(name, '.')) == NULL) ? name : s + 1;
523 return extattr_delete_link(path, attrnamespace, attrname);
524 #elif defined(HAVE_ATTR_REMOVE)
525 int flags = ATTR_DONTFOLLOW;
526 char *attrname = strchr(name,'.') + 1;
528 if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
530 return attr_remove(path, attrname, flags);
531 #elif defined(HAVE_ATTROPEN)
533 int attrdirfd = solaris_attropen(path, ".", O_RDONLY|AT_SYMLINK_NOFOLLOW, 0);
534 if (attrdirfd >= 0) {
535 ret = solaris_unlinkat(attrdirfd, name);
545 int sys_setxattr (const char *path, const char *uname, const void *value, size_t size, int flags)
547 const char *name = prefix(uname);
548 #if defined(HAVE_SETXATTR)
549 #ifndef XATTR_ADD_OPT
550 return setxattr(path, name, value, size, flags);
553 return setxattr(path, name, value, size, 0, options);
555 #elif defined(HAVE_SETEA)
556 return setea(path, name, value, size, flags);
557 #elif defined(HAVE_EXTATTR_SET_FILE)
560 int attrnamespace = (strncmp(name, "system", 6) == 0) ?
561 EXTATTR_NAMESPACE_SYSTEM : EXTATTR_NAMESPACE_USER;
562 const char *attrname = ((s=strchr(name, '.')) == NULL) ? name : s + 1;
564 /* Check attribute existence */
565 retval = extattr_get_file(path, attrnamespace, attrname, NULL, 0);
567 /* REPLACE attribute, that doesn't exist */
568 if (flags & XATTR_REPLACE && errno == ENOATTR) {
572 /* Ignore other errors */
575 /* CREATE attribute, that already exists */
576 if (flags & XATTR_CREATE) {
582 retval = extattr_set_file(path, attrnamespace, attrname, value, size);
583 return (retval < 0) ? -1 : 0;
584 #elif defined(HAVE_ATTR_SET)
586 char *attrname = strchr(name,'.') + 1;
588 if (strncmp(name, "system", 6) == 0) myflags |= ATTR_ROOT;
589 if (flags & XATTR_CREATE) myflags |= ATTR_CREATE;
590 if (flags & XATTR_REPLACE) myflags |= ATTR_REPLACE;
592 return attr_set(path, attrname, (const char *)value, size, myflags);
593 #elif defined(HAVE_ATTROPEN)
595 int myflags = O_RDWR;
597 if (flags & XATTR_CREATE) myflags |= O_EXCL;
598 if (!(flags & XATTR_REPLACE)) myflags |= O_CREAT;
599 attrfd = solaris_attropen(path, name, myflags, (mode_t) SOLARIS_ATTRMODE);
601 ret = solaris_write_xattr(attrfd, value, size);
611 int sys_lsetxattr (const char *path, const char *uname, const void *value, size_t size, int flags)
613 const char *name = prefix(uname);
614 #if defined(HAVE_LSETXATTR)
615 return lsetxattr(path, name, value, size, flags);
616 #elif defined(HAVE_SETXATTR) && defined(XATTR_ADD_OPT)
617 int options = XATTR_NOFOLLOW;
618 return setxattr(path, name, value, size, 0, options);
619 #elif defined(LSETEA)
620 return lsetea(path, name, value, size, flags);
621 #elif defined(HAVE_EXTATTR_SET_LINK)
624 int attrnamespace = (strncmp(name, "system", 6) == 0) ?
625 EXTATTR_NAMESPACE_SYSTEM : EXTATTR_NAMESPACE_USER;
626 const char *attrname = ((s=strchr(name, '.')) == NULL) ? name : s + 1;
628 /* Check attribute existence */
629 retval = extattr_get_link(path, attrnamespace, attrname, NULL, 0);
631 /* REPLACE attribute, that doesn't exist */
632 if (flags & XATTR_REPLACE && errno == ENOATTR) {
636 /* Ignore other errors */
639 /* CREATE attribute, that already exists */
640 if (flags & XATTR_CREATE) {
647 retval = extattr_set_link(path, attrnamespace, attrname, value, size);
648 return (retval < 0) ? -1 : 0;
649 #elif defined(HAVE_ATTR_SET)
650 int myflags = ATTR_DONTFOLLOW;
651 char *attrname = strchr(name,'.') + 1;
653 if (strncmp(name, "system", 6) == 0) myflags |= ATTR_ROOT;
654 if (flags & XATTR_CREATE) myflags |= ATTR_CREATE;
655 if (flags & XATTR_REPLACE) myflags |= ATTR_REPLACE;
657 return attr_set(path, attrname, (const char *)value, size, myflags);
658 #elif defined(HAVE_ATTROPEN)
660 int myflags = O_RDWR | AT_SYMLINK_NOFOLLOW;
662 if (flags & XATTR_CREATE) myflags |= O_EXCL;
663 if (!(flags & XATTR_REPLACE)) myflags |= O_CREAT;
664 attrfd = solaris_attropen(path, name, myflags, (mode_t) SOLARIS_ATTRMODE);
666 ret = solaris_write_xattr(attrfd, value, size);
676 /**************************************************************************
677 helper functions for Solaris' EA support
678 ****************************************************************************/
680 static ssize_t solaris_read_xattr(int attrfd, void *value, size_t size)
684 if (fstat(attrfd, &sbuf) == -1) {
688 /* This is to return the current size of the named extended attribute */
693 /* check size and read xattr */
694 if (sbuf.st_size > size) {
698 return read(attrfd, value, sbuf.st_size);
701 static ssize_t solaris_list_xattr(int attrdirfd, char *list, size_t size)
706 int newfd = dup(attrdirfd);
707 /* CAUTION: The originating file descriptor should not be
708 used again following the call to fdopendir().
709 For that reason we dup() the file descriptor
710 here to make things more clear. */
711 dirp = fdopendir(newfd);
713 while ((de = readdir(dirp))) {
715 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..") ||
716 !strcmp(de->d_name, "SUNWattr_ro") || !strcmp(de->d_name, "SUNWattr_rw"))
718 /* we don't want "." and ".." here: */
719 LOG(log_maxdebug, logtype_default, "skipped EA %s\n",de->d_name);
723 listlen = strlen(de->d_name);
725 /* return the current size of the list of extended attribute names*/
728 /* check size and copy entry + nul into list. */
729 if ((len + listlen + 1) > size) {
734 strcpy(list + len, de->d_name);
742 if (closedir(dirp) == -1) {
743 LOG(log_debug, logtype_default, "closedir dirp failed: %s\n",strerror(errno));
749 static int solaris_unlinkat(int attrdirfd, const char *name)
751 if (unlinkat(attrdirfd, name, 0) == -1) {
757 static int solaris_attropen(const char *path, const char *attrpath, int oflag, mode_t mode)
759 int filedes = attropen(path, attrpath, oflag, mode);
761 LOG(log_maxdebug, logtype_default, "attropen FAILED: path: %s, name: %s, errno: %s\n",path,attrpath,strerror(errno));
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));
776 static int solaris_write_xattr(int attrfd, const char *value, size_t size)
778 if ((ftruncate(attrfd, 0) == 0) && (write(attrfd, value, size) == size)) {
781 LOG(log_maxdebug, logtype_default, "solaris_write_xattr FAILED!\n");
786 #endif /*HAVE_ATTROPEN*/