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.
36 #include <sys/types.h>
40 #include <attr/xattr.h>
41 #elif HAVE_SYS_XATTR_H
42 #include <sys/xattr.h>
54 #ifdef HAVE_SYS_EXTATTR_H
55 #include <sys/extattr.h>
58 #include <atalk/adouble.h>
59 #include <atalk/util.h>
60 #include <atalk/logger.h>
64 #define ENOATTR ENODATA
67 /******** Solaris EA helper function prototypes ********/
69 #define SOLARIS_ATTRMODE S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP
70 static int solaris_write_xattr(int attrfd, const char *value, size_t size);
71 static ssize_t solaris_read_xattr(int attrfd, void *value, size_t size);
72 static ssize_t solaris_list_xattr(int attrdirfd, char *list, size_t size);
73 static int solaris_unlinkat(int attrdirfd, const char *name);
74 static int solaris_attropen(const char *path, const char *attrpath, int oflag, mode_t mode);
75 static int solaris_openat(int fildes, const char *path, int oflag, mode_t mode);
78 /**************************************************************************
79 Wrappers for extented attribute calls. Based on the Linux package with
80 support for IRIX and (Net|Free)BSD also. Expand as other systems have them.
81 ****************************************************************************/
82 static char attr_name[256 +5] = "user.";
84 static const char *prefix(const char *uname)
86 #if defined(HAVE_ATTROPEN)
89 strlcpy(attr_name +5, uname, 256);
94 ssize_t sys_getxattr (const char *path, const char *uname, void *value, size_t size)
96 const char *name = prefix(uname);
98 #if defined(HAVE_GETXATTR)
100 return getxattr(path, name, value, size);
103 return getxattr(path, name, value, size, 0, options);
105 #elif defined(HAVE_GETEA)
106 return getea(path, name, value, size);
107 #elif defined(HAVE_EXTATTR_GET_FILE)
110 * The BSD implementation has a nasty habit of silently truncating
111 * the returned value to the size of the buffer, so we have to check
112 * that the buffer is large enough to fit the returned value.
114 if((retval = extattr_get_file(path, EXTATTR_NAMESPACE_USER, uname, NULL, 0)) >= 0) {
116 /* size == 0 means only return size */
122 if ((retval = extattr_get_file(path, EXTATTR_NAMESPACE_USER, uname, value, size)) >= 0)
126 LOG(log_maxdebug, logtype_default, "sys_getxattr: extattr_get_file() failed with: %s\n", strerror(errno));
128 #elif defined(HAVE_ATTR_GET)
129 int retval, flags = 0;
130 int valuelength = (int)size;
131 char *attrname = strchr(name,'.') + 1;
133 if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
135 retval = attr_get(path, attrname, (char *)value, &valuelength, flags);
137 return retval ? retval : valuelength;
138 #elif defined(HAVE_ATTROPEN)
140 int attrfd = solaris_attropen(path, name, O_RDONLY, 0);
142 ret = solaris_read_xattr(attrfd, value, size);
152 ssize_t sys_lgetxattr (const char *path, const char *uname, void *value, size_t size)
154 const char *name = prefix(uname);
156 #if defined(HAVE_LGETXATTR)
157 return lgetxattr(path, name, value, size);
158 #elif defined(HAVE_GETXATTR) && defined(XATTR_ADD_OPT)
159 int options = XATTR_NOFOLLOW;
160 return getxattr(path, name, value, size, 0, options);
161 #elif defined(HAVE_LGETEA)
162 return lgetea(path, name, value, size);
163 #elif defined(HAVE_EXTATTR_GET_LINK)
165 if((retval=extattr_get_link(path, EXTATTR_NAMESPACE_USER, uname, NULL, 0)) >= 0) {
170 if((retval=extattr_get_link(path, EXTATTR_NAMESPACE_USER, uname, value, size)) >= 0)
174 LOG(log_maxdebug, logtype_default, "sys_lgetxattr: extattr_get_link() failed with: %s\n", strerror(errno));
176 #elif defined(HAVE_ATTR_GET)
177 int retval, flags = ATTR_DONTFOLLOW;
178 int valuelength = (int)size;
179 char *attrname = strchr(name,'.') + 1;
181 if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
183 retval = attr_get(path, attrname, (char *)value, &valuelength, flags);
185 return retval ? retval : valuelength;
186 #elif defined(HAVE_ATTROPEN)
188 int attrfd = solaris_attropen(path, name, O_RDONLY|AT_SYMLINK_NOFOLLOW, 0);
190 ret = solaris_read_xattr(attrfd, value, size);
200 #if defined(HAVE_EXTATTR_LIST_FILE)
202 #define EXTATTR_PREFIX(s) (s), (sizeof((s))-1)
210 { EXTATTR_NAMESPACE_SYSTEM, EXTATTR_PREFIX("") },
211 { EXTATTR_NAMESPACE_USER, EXTATTR_PREFIX("") },
219 static ssize_t bsd_attr_list (int type, extattr_arg arg, char *list, size_t size)
225 #if defined(HAVE_EXTATTR_LIST_FILE)
227 list_size = extattr_list_file(arg.path, EXTATTR_NAMESPACE_USER, list, size);
230 #if defined(HAVE_EXTATTR_LIST_LINK)
232 list_size = extattr_list_link(arg.path, EXTATTR_NAMESPACE_USER, list, size);
235 #if defined(HAVE_EXTATTR_LIST_FD)
237 list_size = extattr_list_fd(arg.filedes, EXTATTR_NAMESPACE_USER, list, size);
245 /* Some error happend. Errno should be set by the previous call */
253 /* XXX: Call with an empty buffer may be used to calculate
254 necessary buffer size. Unfortunately, we can't say, how
255 many attributes were returned, so here is the potential
256 problem with the emulation.
261 /* Buffer is too small to fit the results */
262 if(list_size > size) {
267 /* Convert from pascal strings to C strings */
269 memmove(list, list + 1, list_size);
271 for(i = len; i < list_size; ) {
272 LOG(log_maxdebug, logtype_afpd, "len: %d, i: %d", len, i);
284 #if defined(HAVE_ATTR_LIST) && defined(HAVE_SYS_ATTRIBUTES_H)
285 static char attr_buffer[ATTR_MAX_VALUELEN];
287 static ssize_t irix_attr_list(const char *path, int filedes, char *list, size_t size, int flags)
289 int retval = 0, index;
290 attrlist_cursor_t *cursor = 0;
292 attrlist_t * al = (attrlist_t *)attr_buffer;
294 size_t ent_size, left = size;
299 retval = attr_listf(filedes, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
301 retval = attr_list(path, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
303 for (index = 0; index < al->al_count; index++) {
304 ae = ATTR_ENTRY(attr_buffer, index);
305 ent_size = strlen(ae->a_name) + sizeof("user.");
306 if (left >= ent_size) {
307 strncpy(bp, "user.", sizeof("user."));
308 strncat(bp, ae->a_name, ent_size - sizeof("user."));
316 total_size += ent_size;
318 if (al->al_more == 0) break;
325 retval = attr_listf(filedes, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
327 retval = attr_list(path, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
329 for (index = 0; index < al->al_count; index++) {
330 ae = ATTR_ENTRY(attr_buffer, index);
331 ent_size = strlen(ae->a_name) + sizeof("system.");
332 if (left >= ent_size) {
333 strncpy(bp, "system.", sizeof("system."));
334 strncat(bp, ae->a_name, ent_size - sizeof("system."));
342 total_size += ent_size;
344 if (al->al_more == 0) break;
347 return (ssize_t)(retval ? retval : total_size);
352 #if defined(HAVE_LISTXATTR)
353 static ssize_t remove_user(ssize_t ret, char *list, size_t size)
360 if (ret <= 0 || size == 0)
364 while (ptrsize > 0) {
365 len = strlen(ptr1) +1;
367 if (strncmp(ptr1, "user.",5)) {
371 memmove(ptr, ptr1 +5, len -5);
379 ssize_t sys_listxattr (const char *path, char *list, size_t size)
381 #if defined(HAVE_LISTXATTR)
384 #ifndef XATTR_ADD_OPT
385 ret = listxattr(path, list, size);
388 ret = listxattr(path, list, size, options);
390 return remove_user(ret, list, size);
392 #elif defined(HAVE_LISTEA)
393 return listea(path, list, size);
394 #elif defined(HAVE_EXTATTR_LIST_FILE)
397 return bsd_attr_list(0, arg, list, size);
398 #elif defined(HAVE_ATTR_LIST) && defined(HAVE_SYS_ATTRIBUTES_H)
399 return irix_attr_list(path, 0, list, size, 0);
400 #elif defined(HAVE_ATTROPEN)
402 int attrdirfd = solaris_attropen(path, ".", O_RDONLY, 0);
403 if (attrdirfd >= 0) {
404 ret = solaris_list_xattr(attrdirfd, list, size);
414 ssize_t sys_llistxattr (const char *path, char *list, size_t size)
416 #if defined(HAVE_LLISTXATTR)
419 ret = llistxattr(path, list, size);
420 return remove_user(ret, list, size);
421 #elif defined(HAVE_LISTXATTR) && defined(XATTR_ADD_OPT)
423 int options = XATTR_NOFOLLOW;
425 ret = listxattr(path, list, size, options);
426 return remove_user(ret, list, size);
428 #elif defined(HAVE_LLISTEA)
429 return llistea(path, list, size);
430 #elif defined(HAVE_EXTATTR_LIST_LINK)
433 return bsd_attr_list(1, arg, list, size);
434 #elif defined(HAVE_ATTR_LIST) && defined(HAVE_SYS_ATTRIBUTES_H)
435 return irix_attr_list(path, 0, list, size, ATTR_DONTFOLLOW);
436 #elif defined(HAVE_ATTROPEN)
438 int attrdirfd = solaris_attropen(path, ".", O_RDONLY|AT_SYMLINK_NOFOLLOW, 0);
439 if (attrdirfd >= 0) {
440 ret = solaris_list_xattr(attrdirfd, list, size);
450 int sys_removexattr (const char *path, const char *uname)
452 const char *name = prefix(uname);
453 #if defined(HAVE_REMOVEXATTR)
454 #ifndef XATTR_ADD_OPT
455 return removexattr(path, name);
458 return removexattr(path, name, options);
460 #elif defined(HAVE_REMOVEEA)
461 return removeea(path, name);
462 #elif defined(HAVE_EXTATTR_DELETE_FILE)
463 return extattr_delete_file(path, EXTATTR_NAMESPACE_USER, uname);
464 #elif defined(HAVE_ATTR_REMOVE)
466 char *attrname = strchr(name,'.') + 1;
468 if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
470 return attr_remove(path, attrname, flags);
471 #elif defined(HAVE_ATTROPEN)
473 int attrdirfd = solaris_attropen(path, ".", O_RDONLY, 0);
474 if (attrdirfd >= 0) {
475 ret = solaris_unlinkat(attrdirfd, name);
485 int sys_lremovexattr (const char *path, const char *uname)
487 const char *name = prefix(uname);
488 #if defined(HAVE_LREMOVEXATTR)
489 return lremovexattr(path, name);
490 #elif defined(HAVE_REMOVEXATTR) && defined(XATTR_ADD_OPT)
491 int options = XATTR_NOFOLLOW;
492 return removexattr(path, name, options);
493 #elif defined(HAVE_LREMOVEEA)
494 return lremoveea(path, name);
495 #elif defined(HAVE_EXTATTR_DELETE_LINK)
496 return extattr_delete_link(path, EXTATTR_NAMESPACE_USER, uname);
497 #elif defined(HAVE_ATTR_REMOVE)
498 int flags = ATTR_DONTFOLLOW;
499 char *attrname = strchr(name,'.') + 1;
501 if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
503 return attr_remove(path, attrname, flags);
504 #elif defined(HAVE_ATTROPEN)
506 int attrdirfd = solaris_attropen(path, ".", O_RDONLY|AT_SYMLINK_NOFOLLOW, 0);
507 if (attrdirfd >= 0) {
508 ret = solaris_unlinkat(attrdirfd, name);
518 int sys_setxattr (const char *path, const char *uname, const void *value, size_t size, int flags)
520 const char *name = prefix(uname);
521 #if defined(HAVE_SETXATTR)
522 #ifndef XATTR_ADD_OPT
523 return setxattr(path, name, value, size, flags);
526 return setxattr(path, name, value, size, 0, options);
528 #elif defined(HAVE_SETEA)
529 return setea(path, name, value, size, flags);
530 #elif defined(HAVE_EXTATTR_SET_FILE)
533 /* Check attribute existence */
534 retval = extattr_get_file(path, EXTATTR_NAMESPACE_USER, uname, NULL, 0);
536 /* REPLACE attribute, that doesn't exist */
537 if (flags & XATTR_REPLACE && errno == ENOATTR) {
541 /* Ignore other errors */
544 /* CREATE attribute, that already exists */
545 if (flags & XATTR_CREATE) {
551 retval = extattr_set_file(path, EXTATTR_NAMESPACE_USER, uname, value, size);
552 return (retval < 0) ? -1 : 0;
553 #elif defined(HAVE_ATTR_SET)
555 char *attrname = strchr(name,'.') + 1;
557 if (strncmp(name, "system", 6) == 0) myflags |= ATTR_ROOT;
558 if (flags & XATTR_CREATE) myflags |= ATTR_CREATE;
559 if (flags & XATTR_REPLACE) myflags |= ATTR_REPLACE;
561 return attr_set(path, attrname, (const char *)value, size, myflags);
562 #elif defined(HAVE_ATTROPEN)
564 int myflags = O_RDWR;
566 if (flags & XATTR_CREATE) myflags |= O_EXCL;
567 if (!(flags & XATTR_REPLACE)) myflags |= O_CREAT;
568 attrfd = solaris_attropen(path, name, myflags, (mode_t) SOLARIS_ATTRMODE);
570 ret = solaris_write_xattr(attrfd, value, size);
580 int sys_lsetxattr (const char *path, const char *uname, const void *value, size_t size, int flags)
582 const char *name = prefix(uname);
583 #if defined(HAVE_LSETXATTR)
584 return lsetxattr(path, name, value, size, flags);
585 #elif defined(HAVE_SETXATTR) && defined(XATTR_ADD_OPT)
586 int options = XATTR_NOFOLLOW;
587 return setxattr(path, name, value, size, 0, options);
588 #elif defined(LSETEA)
589 return lsetea(path, name, value, size, flags);
590 #elif defined(HAVE_EXTATTR_SET_LINK)
593 /* Check attribute existence */
594 retval = extattr_get_link(path, EXTATTR_NAMESPACE_USER, uname, NULL, 0);
596 /* REPLACE attribute, that doesn't exist */
597 if (flags & XATTR_REPLACE && errno == ENOATTR) {
601 /* Ignore other errors */
604 /* CREATE attribute, that already exists */
605 if (flags & XATTR_CREATE) {
612 retval = extattr_set_link(path, EXTATTR_NAMESPACE_USER, uname, value, size);
613 return (retval < 0) ? -1 : 0;
614 #elif defined(HAVE_ATTR_SET)
615 int myflags = ATTR_DONTFOLLOW;
616 char *attrname = strchr(name,'.') + 1;
618 if (strncmp(name, "system", 6) == 0) myflags |= ATTR_ROOT;
619 if (flags & XATTR_CREATE) myflags |= ATTR_CREATE;
620 if (flags & XATTR_REPLACE) myflags |= ATTR_REPLACE;
622 return attr_set(path, attrname, (const char *)value, size, myflags);
623 #elif defined(HAVE_ATTROPEN)
625 int myflags = O_RDWR | AT_SYMLINK_NOFOLLOW;
627 if (flags & XATTR_CREATE) myflags |= O_EXCL;
628 if (!(flags & XATTR_REPLACE)) myflags |= O_CREAT;
629 attrfd = solaris_attropen(path, name, myflags, (mode_t) SOLARIS_ATTRMODE);
631 ret = solaris_write_xattr(attrfd, value, size);
641 /**************************************************************************
642 helper functions for Solaris' EA support
643 ****************************************************************************/
645 static ssize_t solaris_read_xattr(int attrfd, void *value, size_t size)
649 if (fstat(attrfd, &sbuf) == -1) {
653 /* This is to return the current size of the named extended attribute */
658 /* check size and read xattr */
659 if (sbuf.st_size > size) {
663 return read(attrfd, value, sbuf.st_size);
666 static ssize_t solaris_list_xattr(int attrdirfd, char *list, size_t size)
671 int newfd = dup(attrdirfd);
672 /* CAUTION: The originating file descriptor should not be
673 used again following the call to fdopendir().
674 For that reason we dup() the file descriptor
675 here to make things more clear. */
676 dirp = fdopendir(newfd);
678 while ((de = readdir(dirp))) {
680 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..") ||
681 !strcmp(de->d_name, "SUNWattr_ro") || !strcmp(de->d_name, "SUNWattr_rw"))
683 /* we don't want "." and ".." here: */
684 LOG(log_maxdebug, logtype_default, "skipped EA %s\n",de->d_name);
688 listlen = strlen(de->d_name);
690 /* return the current size of the list of extended attribute names*/
693 /* check size and copy entry + nul into list. */
694 if ((len + listlen + 1) > size) {
699 strcpy(list + len, de->d_name);
707 if (closedir(dirp) == -1) {
708 LOG(log_debug, logtype_default, "closedir dirp failed: %s\n",strerror(errno));
714 static int solaris_unlinkat(int attrdirfd, const char *name)
716 if (unlinkat(attrdirfd, name, 0) == -1) {
722 static int solaris_attropen(const char *path, const char *attrpath, int oflag, mode_t mode)
724 int filedes = attropen(path, attrpath, oflag, mode);
726 LOG(log_maxdebug, logtype_default, "attropen FAILED: path: %s, name: %s, errno: %s\n",path,attrpath,strerror(errno));
732 static int solaris_openat(int fildes, const char *path, int oflag, mode_t mode)
734 int filedes = openat(fildes, path, oflag, mode);
736 LOG(log_maxdebug, logtype_default, "openat FAILED: fd: %s, path: %s, errno: %s\n",filedes,path,strerror(errno));
741 static int solaris_write_xattr(int attrfd, const char *value, size_t size)
743 if ((ftruncate(attrfd, 0) == 0) && (write(attrfd, value, size) == size)) {
746 LOG(log_maxdebug, logtype_default, "solaris_write_xattr FAILED!\n");
751 #endif /*HAVE_ATTROPEN*/