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: sys_ea.c,v 1.1 2009-11-17 12:33:30 franklahm Exp $
33 #include <sys/types.h>
37 #include <attr/xattr.h>
38 #elif HAVE_SYS_XATTR_H
39 #include <sys/xattr.h>
51 #ifdef HAVE_SYS_EXTATTR_H
52 #include <sys/extattr.h>
55 #include <atalk/adouble.h>
56 #include <atalk/util.h>
57 #include <atalk/logger.h>
60 /******** Solaris EA helper function prototypes ********/
62 #define SOLARIS_ATTRMODE S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP
63 static int solaris_write_xattr(int attrfd, const char *value, size_t size);
64 static ssize_t solaris_read_xattr(int attrfd, void *value, size_t size);
65 static ssize_t solaris_list_xattr(int attrdirfd, char *list, size_t size);
66 static int solaris_unlinkat(int attrdirfd, const char *name);
67 static int solaris_attropen(const char *path, const char *attrpath, int oflag, mode_t mode);
68 static int solaris_openat(int fildes, const char *path, int oflag, mode_t mode);
71 /**************************************************************************
72 Wrappers for extented attribute calls. Based on the Linux package with
73 support for IRIX and (Net|Free)BSD also. Expand as other systems have them.
74 ****************************************************************************/
75 static char attr_name[256 +5] = "user.";
77 static const char *prefix(const char *uname)
79 #if defined(HAVE_ATTROPEN)
82 strlcpy(attr_name +5, uname, 256);
87 ssize_t sys_getxattr (const char *path, const char *uname, void *value, size_t size)
89 const char *name = prefix(uname);
91 #if defined(HAVE_GETXATTR)
93 return getxattr(path, name, value, size);
96 return getxattr(path, name, value, size, 0, options);
98 #elif defined(HAVE_GETEA)
99 return getea(path, name, value, size);
100 #elif defined(HAVE_EXTATTR_GET_FILE)
103 int attrnamespace = (strncmp(name, "system", 6) == 0) ?
104 EXTATTR_NAMESPACE_SYSTEM : EXTATTR_NAMESPACE_USER;
105 const char *attrname = ((s=strchr_m(name, '.')) == NULL) ? name : s + 1;
107 * The BSD implementation has a nasty habit of silently truncating
108 * the returned value to the size of the buffer, so we have to check
109 * that the buffer is large enough to fit the returned value.
111 if((retval=extattr_get_file(path, attrnamespace, attrname, NULL, 0)) >= 0) {
116 if((retval=extattr_get_file(path, attrnamespace, attrname, value, size)) >= 0)
120 LOG(log_maxdebug, logtype_default, "sys_getxattr: extattr_get_file() failed with: %s\n", strerror(errno));
122 #elif defined(HAVE_ATTR_GET)
123 int retval, flags = 0;
124 int valuelength = (int)size;
125 char *attrname = strchr(name,'.') + 1;
127 if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
129 retval = attr_get(path, attrname, (char *)value, &valuelength, flags);
131 return retval ? retval : valuelength;
132 #elif defined(HAVE_ATTROPEN)
134 int attrfd = solaris_attropen(path, name, O_RDONLY, 0);
136 ret = solaris_read_xattr(attrfd, value, size);
146 ssize_t sys_lgetxattr (const char *path, const char *uname, void *value, size_t size)
148 const char *name = prefix(uname);
150 #if defined(HAVE_LGETXATTR)
151 return lgetxattr(path, name, value, size);
152 #elif defined(HAVE_GETXATTR) && defined(XATTR_ADD_OPT)
153 int options = XATTR_NOFOLLOW;
154 return getxattr(path, name, value, size, 0, options);
155 #elif defined(HAVE_LGETEA)
156 return lgetea(path, name, value, size);
157 #elif defined(HAVE_EXTATTR_GET_LINK)
160 int attrnamespace = (strncmp(name, "system", 6) == 0) ?
161 EXTATTR_NAMESPACE_SYSTEM : EXTATTR_NAMESPACE_USER;
162 const char *attrname = ((s=strchr_m(name, '.')) == NULL) ? name : s + 1;
164 if((retval=extattr_get_link(path, attrnamespace, attrname, NULL, 0)) >= 0) {
169 if((retval=extattr_get_link(path, attrnamespace, attrname, value, size)) >= 0)
173 LOG(log_maxdebug, logtype_default, "sys_lgetxattr: extattr_get_link() failed with: %s\n", strerror(errno));
175 #elif defined(HAVE_ATTR_GET)
176 int retval, flags = ATTR_DONTFOLLOW;
177 int valuelength = (int)size;
178 char *attrname = strchr(name,'.') + 1;
180 if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
182 retval = attr_get(path, attrname, (char *)value, &valuelength, flags);
184 return retval ? retval : valuelength;
185 #elif defined(HAVE_ATTROPEN)
187 int attrfd = solaris_attropen(path, name, O_RDONLY|AT_SYMLINK_NOFOLLOW, 0);
189 ret = solaris_read_xattr(attrfd, value, size);
199 #if defined(HAVE_EXTATTR_LIST_FILE)
201 #define EXTATTR_PREFIX(s) (s), (sizeof((s))-1)
209 { EXTATTR_NAMESPACE_SYSTEM, EXTATTR_PREFIX("system.") },
210 { EXTATTR_NAMESPACE_USER, EXTATTR_PREFIX("user.") },
218 static ssize_t bsd_attr_list (int type, extattr_arg arg, char *list, size_t size)
220 ssize_t list_size, total_size = 0;
223 /* Iterate through extattr(2) namespaces */
224 for(t = 0; t < (sizeof(extattr)/sizeof(extattr[0])); t++) {
226 #if defined(HAVE_EXTATTR_LIST_FILE)
228 list_size = extattr_list_file(arg.path, extattr[t].space, list, size);
231 #if defined(HAVE_EXTATTR_LIST_LINK)
233 list_size = extattr_list_link(arg.path, extattr[t].space, list, size);
236 #if defined(HAVE_EXTATTR_LIST_FD)
238 list_size = extattr_list_fd(arg.filedes, extattr[t].space, list, size);
245 /* Some error happend. Errno should be set by the previous call */
251 /* XXX: Call with an empty buffer may be used to calculate
252 necessary buffer size. Unfortunately, we can't say, how
253 many attributes were returned, so here is the potential
254 problem with the emulation.
257 /* Take the worse case of one char attribute names -
258 two bytes per name plus one more for sanity.
260 total_size += list_size + (list_size/2 + 1)*extattr[t].len;
263 /* Count necessary offset to fit namespace prefixes */
265 for(i = 0; i < list_size; i += list[i] + 1)
266 len += extattr[t].len;
268 total_size += list_size + len;
269 /* Buffer is too small to fit the results */
270 if(total_size > size) {
274 /* Shift results back, so we can prepend prefixes */
275 buf = memmove(list + len, list, list_size);
277 for(i = 0; i < list_size; i += len + 1) {
279 strncpy(list, extattr[t].name, extattr[t].len + 1);
280 list += extattr[t].len;
281 strncpy(list, buf + i + 1, len);
292 #if defined(HAVE_ATTR_LIST) && defined(HAVE_SYS_ATTRIBUTES_H)
293 static char attr_buffer[ATTR_MAX_VALUELEN];
295 static ssize_t irix_attr_list(const char *path, int filedes, char *list, size_t size, int flags)
297 int retval = 0, index;
298 attrlist_cursor_t *cursor = 0;
300 attrlist_t * al = (attrlist_t *)attr_buffer;
302 size_t ent_size, left = size;
307 retval = attr_listf(filedes, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
309 retval = attr_list(path, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
311 for (index = 0; index < al->al_count; index++) {
312 ae = ATTR_ENTRY(attr_buffer, index);
313 ent_size = strlen(ae->a_name) + sizeof("user.");
314 if (left >= ent_size) {
315 strncpy(bp, "user.", sizeof("user."));
316 strncat(bp, ae->a_name, ent_size - sizeof("user."));
324 total_size += ent_size;
326 if (al->al_more == 0) break;
333 retval = attr_listf(filedes, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
335 retval = attr_list(path, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
337 for (index = 0; index < al->al_count; index++) {
338 ae = ATTR_ENTRY(attr_buffer, index);
339 ent_size = strlen(ae->a_name) + sizeof("system.");
340 if (left >= ent_size) {
341 strncpy(bp, "system.", sizeof("system."));
342 strncat(bp, ae->a_name, ent_size - sizeof("system."));
350 total_size += ent_size;
352 if (al->al_more == 0) break;
355 return (ssize_t)(retval ? retval : total_size);
360 #if defined(HAVE_LISTXATTR)
361 static ssize_t remove_user(ssize_t ret, char *list, size_t size)
368 if (ret <= 0 || size == 0)
372 while (ptrsize > 0) {
373 len = strlen(ptr1) +1;
375 if (strncmp(ptr1, "user.",5)) {
379 memmove(ptr, ptr1 +5, len -5);
387 ssize_t sys_listxattr (const char *path, char *list, size_t size)
389 #if defined(HAVE_LISTXATTR)
392 #ifndef XATTR_ADD_OPT
393 ret = listxattr(path, list, size);
396 ret = listxattr(path, list, size, options);
398 return remove_user(ret, list, size);
400 #elif defined(HAVE_LISTEA)
401 return listea(path, list, size);
402 #elif defined(HAVE_EXTATTR_LIST_FILE)
405 return bsd_attr_list(0, arg, list, size);
406 #elif defined(HAVE_ATTR_LIST) && defined(HAVE_SYS_ATTRIBUTES_H)
407 return irix_attr_list(path, 0, list, size, 0);
408 #elif defined(HAVE_ATTROPEN)
410 int attrdirfd = solaris_attropen(path, ".", O_RDONLY, 0);
411 if (attrdirfd >= 0) {
412 ret = solaris_list_xattr(attrdirfd, list, size);
422 ssize_t sys_llistxattr (const char *path, char *list, size_t size)
424 #if defined(HAVE_LLISTXATTR)
427 ret = llistxattr(path, list, size);
428 return remove_user(ret, list, size);
429 #elif defined(HAVE_LISTXATTR) && defined(XATTR_ADD_OPT)
431 int options = XATTR_NOFOLLOW;
433 ret = listxattr(path, list, size, options);
434 return remove_user(ret, list, size);
436 #elif defined(HAVE_LLISTEA)
437 return llistea(path, list, size);
438 #elif defined(HAVE_EXTATTR_LIST_LINK)
441 return bsd_attr_list(1, arg, list, size);
442 #elif defined(HAVE_ATTR_LIST) && defined(HAVE_SYS_ATTRIBUTES_H)
443 return irix_attr_list(path, 0, list, size, ATTR_DONTFOLLOW);
444 #elif defined(HAVE_ATTROPEN)
446 int attrdirfd = solaris_attropen(path, ".", O_RDONLY|AT_SYMLINK_NOFOLLOW, 0);
447 if (attrdirfd >= 0) {
448 ret = solaris_list_xattr(attrdirfd, list, size);
458 int sys_removexattr (const char *path, const char *uname)
460 const char *name = prefix(uname);
461 #if defined(HAVE_REMOVEXATTR)
462 #ifndef XATTR_ADD_OPT
463 return removexattr(path, name);
466 return removexattr(path, name, options);
468 #elif defined(HAVE_REMOVEEA)
469 return removeea(path, name);
470 #elif defined(HAVE_EXTATTR_DELETE_FILE)
472 int attrnamespace = (strncmp(name, "system", 6) == 0) ?
473 EXTATTR_NAMESPACE_SYSTEM : EXTATTR_NAMESPACE_USER;
474 const char *attrname = ((s=strchr_m(name, '.')) == NULL) ? name : s + 1;
476 return extattr_delete_file(path, attrnamespace, attrname);
477 #elif defined(HAVE_ATTR_REMOVE)
479 char *attrname = strchr(name,'.') + 1;
481 if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
483 return attr_remove(path, attrname, flags);
484 #elif defined(HAVE_ATTROPEN)
486 int attrdirfd = solaris_attropen(path, ".", O_RDONLY, 0);
487 if (attrdirfd >= 0) {
488 ret = solaris_unlinkat(attrdirfd, name);
498 int sys_lremovexattr (const char *path, const char *uname)
500 const char *name = prefix(uname);
501 #if defined(HAVE_LREMOVEXATTR)
502 return lremovexattr(path, name);
503 #elif defined(HAVE_REMOVEXATTR) && defined(XATTR_ADD_OPT)
504 int options = XATTR_NOFOLLOW;
505 return removexattr(path, name, options);
506 #elif defined(HAVE_LREMOVEEA)
507 return lremoveea(path, name);
508 #elif defined(HAVE_EXTATTR_DELETE_LINK)
510 int attrnamespace = (strncmp(name, "system", 6) == 0) ?
511 EXTATTR_NAMESPACE_SYSTEM : EXTATTR_NAMESPACE_USER;
512 const char *attrname = ((s=strchr_m(name, '.')) == NULL) ? name : s + 1;
514 return extattr_delete_link(path, attrnamespace, attrname);
515 #elif defined(HAVE_ATTR_REMOVE)
516 int flags = ATTR_DONTFOLLOW;
517 char *attrname = strchr(name,'.') + 1;
519 if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
521 return attr_remove(path, attrname, flags);
522 #elif defined(HAVE_ATTROPEN)
524 int attrdirfd = solaris_attropen(path, ".", O_RDONLY|AT_SYMLINK_NOFOLLOW, 0);
525 if (attrdirfd >= 0) {
526 ret = solaris_unlinkat(attrdirfd, name);
536 int sys_setxattr (const char *path, const char *uname, const void *value, size_t size, int flags)
538 const char *name = prefix(uname);
539 #if defined(HAVE_SETXATTR)
540 #ifndef XATTR_ADD_OPT
541 return setxattr(path, name, value, size, flags);
544 return setxattr(path, name, value, size, 0, options);
546 #elif defined(HAVE_SETEA)
547 return setea(path, name, value, size, flags);
548 #elif defined(HAVE_EXTATTR_SET_FILE)
551 int attrnamespace = (strncmp(name, "system", 6) == 0) ?
552 EXTATTR_NAMESPACE_SYSTEM : EXTATTR_NAMESPACE_USER;
553 const char *attrname = ((s=strchr_m(name, '.')) == NULL) ? name : s + 1;
555 /* Check attribute existence */
556 retval = extattr_get_file(path, attrnamespace, attrname, NULL, 0);
558 /* REPLACE attribute, that doesn't exist */
559 if (flags & XATTR_REPLACE && errno == ENOATTR) {
563 /* Ignore other errors */
566 /* CREATE attribute, that already exists */
567 if (flags & XATTR_CREATE) {
573 retval = extattr_set_file(path, attrnamespace, attrname, value, size);
574 return (retval < 0) ? -1 : 0;
575 #elif defined(HAVE_ATTR_SET)
577 char *attrname = strchr(name,'.') + 1;
579 if (strncmp(name, "system", 6) == 0) myflags |= ATTR_ROOT;
580 if (flags & XATTR_CREATE) myflags |= ATTR_CREATE;
581 if (flags & XATTR_REPLACE) myflags |= ATTR_REPLACE;
583 return attr_set(path, attrname, (const char *)value, size, myflags);
584 #elif defined(HAVE_ATTROPEN)
586 int myflags = O_RDWR;
588 if (flags & XATTR_CREATE) myflags |= O_EXCL;
589 if (!(flags & XATTR_REPLACE)) myflags |= O_CREAT;
590 attrfd = solaris_attropen(path, name, myflags, (mode_t) SOLARIS_ATTRMODE);
592 ret = solaris_write_xattr(attrfd, value, size);
602 int sys_lsetxattr (const char *path, const char *uname, const void *value, size_t size, int flags)
604 const char *name = prefix(uname);
605 #if defined(HAVE_LSETXATTR)
606 return lsetxattr(path, name, value, size, flags);
607 #elif defined(HAVE_SETXATTR) && defined(XATTR_ADD_OPT)
608 int options = XATTR_NOFOLLOW;
609 return setxattr(path, name, value, size, 0, options);
610 #elif defined(LSETEA)
611 return lsetea(path, name, value, size, flags);
612 #elif defined(HAVE_EXTATTR_SET_LINK)
615 int attrnamespace = (strncmp(name, "system", 6) == 0) ?
616 EXTATTR_NAMESPACE_SYSTEM : EXTATTR_NAMESPACE_USER;
617 const char *attrname = ((s=strchr_m(name, '.')) == NULL) ? name : s + 1;
619 /* Check attribute existence */
620 retval = extattr_get_link(path, attrnamespace, attrname, NULL, 0);
622 /* REPLACE attribute, that doesn't exist */
623 if (flags & XATTR_REPLACE && errno == ENOATTR) {
627 /* Ignore other errors */
630 /* CREATE attribute, that already exists */
631 if (flags & XATTR_CREATE) {
638 retval = extattr_set_link(path, attrnamespace, attrname, value, size);
639 return (retval < 0) ? -1 : 0;
640 #elif defined(HAVE_ATTR_SET)
641 int myflags = ATTR_DONTFOLLOW;
642 char *attrname = strchr(name,'.') + 1;
644 if (strncmp(name, "system", 6) == 0) myflags |= ATTR_ROOT;
645 if (flags & XATTR_CREATE) myflags |= ATTR_CREATE;
646 if (flags & XATTR_REPLACE) myflags |= ATTR_REPLACE;
648 return attr_set(path, attrname, (const char *)value, size, myflags);
649 #elif defined(HAVE_ATTROPEN)
651 int myflags = O_RDWR | AT_SYMLINK_NOFOLLOW;
653 if (flags & XATTR_CREATE) myflags |= O_EXCL;
654 if (!(flags & XATTR_REPLACE)) myflags |= O_CREAT;
655 attrfd = solaris_attropen(path, name, myflags, (mode_t) SOLARIS_ATTRMODE);
657 ret = solaris_write_xattr(attrfd, value, size);
667 /**************************************************************************
668 helper functions for Solaris' EA support
669 ****************************************************************************/
671 static ssize_t solaris_read_xattr(int attrfd, void *value, size_t size)
675 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) {
689 return read(attrfd, value, sbuf.st_size);
692 static ssize_t solaris_list_xattr(int attrdirfd, char *list, size_t size)
698 int newfd = dup(attrdirfd);
699 /* CAUTION: The originating file descriptor should not be
700 used again following the call to fdopendir().
701 For that reason we dup() the file descriptor
702 here to make things more clear. */
703 dirp = fdopendir(newfd);
705 while ((de = readdir(dirp))) {
707 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..") ||
708 !strcmp(de->d_name, "SUNWattr_ro") || !strcmp(de->d_name, "SUNWattr_rw"))
710 /* we don't want "." and ".." here: */
711 LOG(log_maxdebug, logtype_default, "skipped EA %s\n",de->d_name);
715 listlen = strlen(de->d_name);
717 /* return the current size of the list of extended attribute names*/
720 /* check size and copy entry + nul into list. */
721 if ((len + listlen + 1) > size) {
726 strcpy(list + len, de->d_name);
734 if (closedir(dirp) == -1) {
735 LOG(log_debug, logtype_default, "closedir dirp failed: %s\n",strerror(errno));
741 static int solaris_unlinkat(int attrdirfd, const char *name)
743 if (unlinkat(attrdirfd, name, 0) == -1) {
749 static int solaris_attropen(const char *path, const char *attrpath, int oflag, mode_t mode)
751 int filedes = attropen(path, attrpath, oflag, mode);
753 LOG(log_maxdebug, logtype_default, "attropen FAILED: path: %s, name: %s, errno: %s\n",path,attrpath,strerror(errno));
758 static int solaris_openat(int fildes, const char *path, int oflag, mode_t mode)
760 int filedes = openat(fildes, path, oflag, mode);
762 LOG(log_maxdebug, logtype_default, "openat FAILED: fd: %s, path: %s, errno: %s\n",filedes,path,strerror(errno));
767 static int solaris_write_xattr(int attrfd, const char *value, size_t size)
769 if ((ftruncate(attrfd, 0) == 0) && (write(attrfd, value, size) == size)) {
772 LOG(log_maxdebug, logtype_default, "solaris_write_xattr FAILED!\n");
776 #endif /*HAVE_ATTROPEN*/