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.5 2009-11-23 19:04:15 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>
65 /******** Solaris EA helper function prototypes ********/
67 #define SOLARIS_ATTRMODE S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP
68 static int solaris_write_xattr(int attrfd, const char *value, size_t size);
69 static ssize_t solaris_read_xattr(int attrfd, void *value, size_t size);
70 static ssize_t solaris_list_xattr(int attrdirfd, char *list, size_t size);
71 static int solaris_unlinkat(int attrdirfd, const char *name);
72 static int solaris_attropen(const char *path, const char *attrpath, int oflag, mode_t mode);
73 static int solaris_openat(int fildes, const char *path, int oflag, mode_t mode);
76 /**************************************************************************
77 Wrappers for extented attribute calls. Based on the Linux package with
78 support for IRIX and (Net|Free)BSD also. Expand as other systems have them.
79 ****************************************************************************/
80 static char attr_name[256 +5] = "user.";
82 static const char *prefix(const char *uname)
84 #if defined(HAVE_ATTROPEN)
87 strlcpy(attr_name +5, uname, 256);
92 ssize_t sys_getxattr (const char *path, const char *uname, void *value, size_t size)
94 const char *name = prefix(uname);
96 #if defined(HAVE_GETXATTR)
98 return getxattr(path, name, value, size);
101 return getxattr(path, name, value, size, 0, options);
103 #elif defined(HAVE_GETEA)
104 return getea(path, name, value, size);
105 #elif defined(HAVE_EXTATTR_GET_FILE)
108 int attrnamespace = (strncmp(name, "system", 6) == 0) ?
109 EXTATTR_NAMESPACE_SYSTEM : EXTATTR_NAMESPACE_USER;
110 const char *attrname = ((s=strchr(name, '.')) == NULL) ? name : s + 1;
112 * The BSD implementation has a nasty habit of silently truncating
113 * the returned value to the size of the buffer, so we have to check
114 * that the buffer is large enough to fit the returned value.
116 if((retval=extattr_get_file(path, attrnamespace, attrname, NULL, 0)) >= 0) {
121 if((retval=extattr_get_file(path, attrnamespace, attrname, value, size)) >= 0)
125 LOG(log_maxdebug, logtype_default, "sys_getxattr: extattr_get_file() failed with: %s\n", strerror(errno));
127 #elif defined(HAVE_ATTR_GET)
128 int retval, flags = 0;
129 int valuelength = (int)size;
130 char *attrname = strchr(name,'.') + 1;
132 if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
134 retval = attr_get(path, attrname, (char *)value, &valuelength, flags);
136 return retval ? retval : valuelength;
137 #elif defined(HAVE_ATTROPEN)
139 int attrfd = solaris_attropen(path, name, O_RDONLY, 0);
141 ret = solaris_read_xattr(attrfd, value, size);
151 ssize_t sys_lgetxattr (const char *path, const char *uname, void *value, size_t size)
153 const char *name = prefix(uname);
155 #if defined(HAVE_LGETXATTR)
156 return lgetxattr(path, name, value, size);
157 #elif defined(HAVE_GETXATTR) && defined(XATTR_ADD_OPT)
158 int options = XATTR_NOFOLLOW;
159 return getxattr(path, name, value, size, 0, options);
160 #elif defined(HAVE_LGETEA)
161 return lgetea(path, name, value, size);
162 #elif defined(HAVE_EXTATTR_GET_LINK)
165 int attrnamespace = (strncmp(name, "system", 6) == 0) ?
166 EXTATTR_NAMESPACE_SYSTEM : EXTATTR_NAMESPACE_USER;
167 const char *attrname = ((s=strchr(name, '.')) == NULL) ? name : s + 1;
169 if((retval=extattr_get_link(path, attrnamespace, attrname, NULL, 0)) >= 0) {
174 if((retval=extattr_get_link(path, attrnamespace, attrname, value, size)) >= 0)
178 LOG(log_maxdebug, logtype_default, "sys_lgetxattr: extattr_get_link() failed with: %s\n", strerror(errno));
180 #elif defined(HAVE_ATTR_GET)
181 int retval, flags = ATTR_DONTFOLLOW;
182 int valuelength = (int)size;
183 char *attrname = strchr(name,'.') + 1;
185 if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
187 retval = attr_get(path, attrname, (char *)value, &valuelength, flags);
189 return retval ? retval : valuelength;
190 #elif defined(HAVE_ATTROPEN)
192 int attrfd = solaris_attropen(path, name, O_RDONLY|AT_SYMLINK_NOFOLLOW, 0);
194 ret = solaris_read_xattr(attrfd, value, size);
204 #if defined(HAVE_EXTATTR_LIST_FILE)
206 #define EXTATTR_PREFIX(s) (s), (sizeof((s))-1)
214 { EXTATTR_NAMESPACE_SYSTEM, EXTATTR_PREFIX("system.") },
215 { EXTATTR_NAMESPACE_USER, EXTATTR_PREFIX("user.") },
223 static ssize_t bsd_attr_list (int type, extattr_arg arg, char *list, size_t size)
225 ssize_t list_size, total_size = 0;
228 /* Iterate through extattr(2) namespaces */
229 for(t = 0; t < (sizeof(extattr)/sizeof(extattr[0])); t++) {
231 #if defined(HAVE_EXTATTR_LIST_FILE)
233 list_size = extattr_list_file(arg.path, extattr[t].space, list, size);
236 #if defined(HAVE_EXTATTR_LIST_LINK)
238 list_size = extattr_list_link(arg.path, extattr[t].space, list, size);
241 #if defined(HAVE_EXTATTR_LIST_FD)
243 list_size = extattr_list_fd(arg.filedes, extattr[t].space, list, size);
250 /* Some error happend. Errno should be set by the previous call */
256 /* XXX: Call with an empty buffer may be used to calculate
257 necessary buffer size. Unfortunately, we can't say, how
258 many attributes were returned, so here is the potential
259 problem with the emulation.
262 /* Take the worse case of one char attribute names -
263 two bytes per name plus one more for sanity.
265 total_size += list_size + (list_size/2 + 1)*extattr[t].len;
268 /* Count necessary offset to fit namespace prefixes */
270 for(i = 0; i < list_size; i += list[i] + 1)
271 len += extattr[t].len;
273 total_size += list_size + len;
274 /* Buffer is too small to fit the results */
275 if(total_size > size) {
279 /* Shift results back, so we can prepend prefixes */
280 buf = memmove(list + len, list, list_size);
282 for(i = 0; i < list_size; i += len + 1) {
284 strncpy(list, extattr[t].name, extattr[t].len + 1);
285 list += extattr[t].len;
286 strncpy(list, buf + i + 1, len);
297 #if defined(HAVE_ATTR_LIST) && defined(HAVE_SYS_ATTRIBUTES_H)
298 static char attr_buffer[ATTR_MAX_VALUELEN];
300 static ssize_t irix_attr_list(const char *path, int filedes, char *list, size_t size, int flags)
302 int retval = 0, index;
303 attrlist_cursor_t *cursor = 0;
305 attrlist_t * al = (attrlist_t *)attr_buffer;
307 size_t ent_size, left = size;
312 retval = attr_listf(filedes, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
314 retval = attr_list(path, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
316 for (index = 0; index < al->al_count; index++) {
317 ae = ATTR_ENTRY(attr_buffer, index);
318 ent_size = strlen(ae->a_name) + sizeof("user.");
319 if (left >= ent_size) {
320 strncpy(bp, "user.", sizeof("user."));
321 strncat(bp, ae->a_name, ent_size - sizeof("user."));
329 total_size += ent_size;
331 if (al->al_more == 0) break;
338 retval = attr_listf(filedes, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
340 retval = attr_list(path, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
342 for (index = 0; index < al->al_count; index++) {
343 ae = ATTR_ENTRY(attr_buffer, index);
344 ent_size = strlen(ae->a_name) + sizeof("system.");
345 if (left >= ent_size) {
346 strncpy(bp, "system.", sizeof("system."));
347 strncat(bp, ae->a_name, ent_size - sizeof("system."));
355 total_size += ent_size;
357 if (al->al_more == 0) break;
360 return (ssize_t)(retval ? retval : total_size);
365 #if defined(HAVE_LISTXATTR)
366 static ssize_t remove_user(ssize_t ret, char *list, size_t size)
373 if (ret <= 0 || size == 0)
377 while (ptrsize > 0) {
378 len = strlen(ptr1) +1;
380 if (strncmp(ptr1, "user.",5)) {
384 memmove(ptr, ptr1 +5, len -5);
392 ssize_t sys_listxattr (const char *path, char *list, size_t size)
394 #if defined(HAVE_LISTXATTR)
397 #ifndef XATTR_ADD_OPT
398 ret = listxattr(path, list, size);
401 ret = listxattr(path, list, size, options);
403 return remove_user(ret, list, size);
405 #elif defined(HAVE_LISTEA)
406 return listea(path, list, size);
407 #elif defined(HAVE_EXTATTR_LIST_FILE)
410 return bsd_attr_list(0, arg, list, size);
411 #elif defined(HAVE_ATTR_LIST) && defined(HAVE_SYS_ATTRIBUTES_H)
412 return irix_attr_list(path, 0, list, size, 0);
413 #elif defined(HAVE_ATTROPEN)
415 int attrdirfd = solaris_attropen(path, ".", O_RDONLY, 0);
416 if (attrdirfd >= 0) {
417 ret = solaris_list_xattr(attrdirfd, list, size);
427 ssize_t sys_llistxattr (const char *path, char *list, size_t size)
429 #if defined(HAVE_LLISTXATTR)
432 ret = llistxattr(path, list, size);
433 return remove_user(ret, list, size);
434 #elif defined(HAVE_LISTXATTR) && defined(XATTR_ADD_OPT)
436 int options = XATTR_NOFOLLOW;
438 ret = listxattr(path, list, size, options);
439 return remove_user(ret, list, size);
441 #elif defined(HAVE_LLISTEA)
442 return llistea(path, list, size);
443 #elif defined(HAVE_EXTATTR_LIST_LINK)
446 return bsd_attr_list(1, arg, list, size);
447 #elif defined(HAVE_ATTR_LIST) && defined(HAVE_SYS_ATTRIBUTES_H)
448 return irix_attr_list(path, 0, list, size, ATTR_DONTFOLLOW);
449 #elif defined(HAVE_ATTROPEN)
451 int attrdirfd = solaris_attropen(path, ".", O_RDONLY|AT_SYMLINK_NOFOLLOW, 0);
452 if (attrdirfd >= 0) {
453 ret = solaris_list_xattr(attrdirfd, list, size);
463 int sys_removexattr (const char *path, const char *uname)
465 const char *name = prefix(uname);
466 #if defined(HAVE_REMOVEXATTR)
467 #ifndef XATTR_ADD_OPT
468 return removexattr(path, name);
471 return removexattr(path, name, options);
473 #elif defined(HAVE_REMOVEEA)
474 return removeea(path, name);
475 #elif defined(HAVE_EXTATTR_DELETE_FILE)
477 int attrnamespace = (strncmp(name, "system", 6) == 0) ?
478 EXTATTR_NAMESPACE_SYSTEM : EXTATTR_NAMESPACE_USER;
479 const char *attrname = ((s=strchr(name, '.')) == NULL) ? name : s + 1;
481 return extattr_delete_file(path, attrnamespace, attrname);
482 #elif defined(HAVE_ATTR_REMOVE)
484 char *attrname = strchr(name,'.') + 1;
486 if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
488 return attr_remove(path, attrname, flags);
489 #elif defined(HAVE_ATTROPEN)
491 int attrdirfd = solaris_attropen(path, ".", O_RDONLY, 0);
492 if (attrdirfd >= 0) {
493 ret = solaris_unlinkat(attrdirfd, name);
503 int sys_lremovexattr (const char *path, const char *uname)
505 const char *name = prefix(uname);
506 #if defined(HAVE_LREMOVEXATTR)
507 return lremovexattr(path, name);
508 #elif defined(HAVE_REMOVEXATTR) && defined(XATTR_ADD_OPT)
509 int options = XATTR_NOFOLLOW;
510 return removexattr(path, name, options);
511 #elif defined(HAVE_LREMOVEEA)
512 return lremoveea(path, name);
513 #elif defined(HAVE_EXTATTR_DELETE_LINK)
515 int attrnamespace = (strncmp(name, "system", 6) == 0) ?
516 EXTATTR_NAMESPACE_SYSTEM : EXTATTR_NAMESPACE_USER;
517 const char *attrname = ((s=strchr(name, '.')) == NULL) ? name : s + 1;
519 return extattr_delete_link(path, attrnamespace, attrname);
520 #elif defined(HAVE_ATTR_REMOVE)
521 int flags = ATTR_DONTFOLLOW;
522 char *attrname = strchr(name,'.') + 1;
524 if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
526 return attr_remove(path, attrname, flags);
527 #elif defined(HAVE_ATTROPEN)
529 int attrdirfd = solaris_attropen(path, ".", O_RDONLY|AT_SYMLINK_NOFOLLOW, 0);
530 if (attrdirfd >= 0) {
531 ret = solaris_unlinkat(attrdirfd, name);
541 int sys_setxattr (const char *path, const char *uname, const void *value, size_t size, int flags)
543 const char *name = prefix(uname);
544 #if defined(HAVE_SETXATTR)
545 #ifndef XATTR_ADD_OPT
546 return setxattr(path, name, value, size, flags);
549 return setxattr(path, name, value, size, 0, options);
551 #elif defined(HAVE_SETEA)
552 return setea(path, name, value, size, flags);
553 #elif defined(HAVE_EXTATTR_SET_FILE)
556 int attrnamespace = (strncmp(name, "system", 6) == 0) ?
557 EXTATTR_NAMESPACE_SYSTEM : EXTATTR_NAMESPACE_USER;
558 const char *attrname = ((s=strchr(name, '.')) == NULL) ? name : s + 1;
560 /* Check attribute existence */
561 retval = extattr_get_file(path, attrnamespace, attrname, NULL, 0);
563 /* REPLACE attribute, that doesn't exist */
564 if (flags & XATTR_REPLACE && errno == ENOATTR) {
568 /* Ignore other errors */
571 /* CREATE attribute, that already exists */
572 if (flags & XATTR_CREATE) {
578 retval = extattr_set_file(path, attrnamespace, attrname, value, size);
579 return (retval < 0) ? -1 : 0;
580 #elif defined(HAVE_ATTR_SET)
582 char *attrname = strchr(name,'.') + 1;
584 if (strncmp(name, "system", 6) == 0) myflags |= ATTR_ROOT;
585 if (flags & XATTR_CREATE) myflags |= ATTR_CREATE;
586 if (flags & XATTR_REPLACE) myflags |= ATTR_REPLACE;
588 return attr_set(path, attrname, (const char *)value, size, myflags);
589 #elif defined(HAVE_ATTROPEN)
591 int myflags = O_RDWR;
593 if (flags & XATTR_CREATE) myflags |= O_EXCL;
594 if (!(flags & XATTR_REPLACE)) myflags |= O_CREAT;
595 attrfd = solaris_attropen(path, name, myflags, (mode_t) SOLARIS_ATTRMODE);
597 ret = solaris_write_xattr(attrfd, value, size);
607 int sys_lsetxattr (const char *path, const char *uname, const void *value, size_t size, int flags)
609 const char *name = prefix(uname);
610 #if defined(HAVE_LSETXATTR)
611 return lsetxattr(path, name, value, size, flags);
612 #elif defined(HAVE_SETXATTR) && defined(XATTR_ADD_OPT)
613 int options = XATTR_NOFOLLOW;
614 return setxattr(path, name, value, size, 0, options);
615 #elif defined(LSETEA)
616 return lsetea(path, name, value, size, flags);
617 #elif defined(HAVE_EXTATTR_SET_LINK)
620 int attrnamespace = (strncmp(name, "system", 6) == 0) ?
621 EXTATTR_NAMESPACE_SYSTEM : EXTATTR_NAMESPACE_USER;
622 const char *attrname = ((s=strchr(name, '.')) == NULL) ? name : s + 1;
624 /* Check attribute existence */
625 retval = extattr_get_link(path, attrnamespace, attrname, NULL, 0);
627 /* REPLACE attribute, that doesn't exist */
628 if (flags & XATTR_REPLACE && errno == ENOATTR) {
632 /* Ignore other errors */
635 /* CREATE attribute, that already exists */
636 if (flags & XATTR_CREATE) {
643 retval = extattr_set_link(path, attrnamespace, attrname, value, size);
644 return (retval < 0) ? -1 : 0;
645 #elif defined(HAVE_ATTR_SET)
646 int myflags = ATTR_DONTFOLLOW;
647 char *attrname = strchr(name,'.') + 1;
649 if (strncmp(name, "system", 6) == 0) myflags |= ATTR_ROOT;
650 if (flags & XATTR_CREATE) myflags |= ATTR_CREATE;
651 if (flags & XATTR_REPLACE) myflags |= ATTR_REPLACE;
653 return attr_set(path, attrname, (const char *)value, size, myflags);
654 #elif defined(HAVE_ATTROPEN)
656 int myflags = O_RDWR | AT_SYMLINK_NOFOLLOW;
658 if (flags & XATTR_CREATE) myflags |= O_EXCL;
659 if (!(flags & XATTR_REPLACE)) myflags |= O_CREAT;
660 attrfd = solaris_attropen(path, name, myflags, (mode_t) SOLARIS_ATTRMODE);
662 ret = solaris_write_xattr(attrfd, value, size);
672 /**************************************************************************
673 helper functions for Solaris' EA support
674 ****************************************************************************/
676 static ssize_t solaris_read_xattr(int attrfd, void *value, size_t size)
680 if (fstat(attrfd, &sbuf) == -1) {
684 /* This is to return the current size of the named extended attribute */
689 /* check size and read xattr */
690 if (sbuf.st_size > size) {
694 return read(attrfd, value, sbuf.st_size);
697 static ssize_t solaris_list_xattr(int attrdirfd, char *list, size_t size)
702 int newfd = dup(attrdirfd);
703 /* CAUTION: The originating file descriptor should not be
704 used again following the call to fdopendir().
705 For that reason we dup() the file descriptor
706 here to make things more clear. */
707 dirp = fdopendir(newfd);
709 while ((de = readdir(dirp))) {
711 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..") ||
712 !strcmp(de->d_name, "SUNWattr_ro") || !strcmp(de->d_name, "SUNWattr_rw"))
714 /* we don't want "." and ".." here: */
715 LOG(log_maxdebug, logtype_default, "skipped EA %s\n",de->d_name);
719 listlen = strlen(de->d_name);
721 /* return the current size of the list of extended attribute names*/
724 /* check size and copy entry + nul into list. */
725 if ((len + listlen + 1) > size) {
730 strcpy(list + len, de->d_name);
738 if (closedir(dirp) == -1) {
739 LOG(log_debug, logtype_default, "closedir dirp failed: %s\n",strerror(errno));
745 static int solaris_unlinkat(int attrdirfd, const char *name)
747 if (unlinkat(attrdirfd, name, 0) == -1) {
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));
762 static int solaris_openat(int fildes, const char *path, int oflag, mode_t mode)
764 int filedes = openat(fildes, path, oflag, mode);
766 LOG(log_maxdebug, logtype_default, "openat FAILED: fd: %s, path: %s, errno: %s\n",filedes,path,strerror(errno));
771 static int solaris_write_xattr(int attrfd, const char *value, size_t size)
773 if ((ftruncate(attrfd, 0) == 0) && (write(attrfd, value, size) == size)) {
776 LOG(log_maxdebug, logtype_default, "solaris_write_xattr FAILED!\n");
781 #endif /*HAVE_ATTROPEN*/