2 Unix SMB/CIFS implementation.
3 replacement routines for xattr implementations
4 Copyright (C) Jeremy Allison 1998-2005
5 Copyright (C) Timur Bakeyev 2005
6 Copyright (C) Bjoern Jacke 2006-2007
7 Copyright (C) Herb Lewis 2003
8 Copyright (C) Andrew Bartlett 2012
10 ** NOTE! The following LGPL license applies to the replace
11 ** library. This does NOT imply that all of Samba is released
14 This library is free software; you can redistribute it and/or
15 modify it under the terms of the GNU Lesser General Public
16 License as published by the Free Software Foundation; either
17 version 3 of the License, or (at your option) any later version.
19 This library is distributed in the hope that it will be useful,
20 but WITHOUT ANY WARRANTY; without even the implied warranty of
21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 Lesser General Public License for more details.
24 You should have received a copy of the GNU Lesser General Public
25 License along with this library; if not, see <http://www.gnu.org/licenses/>.
28 #define UID_WRAPPER_NOT_REPLACE
30 #include "system/filesys.h"
31 #include "system/dir.h"
33 /******** Solaris EA helper function prototypes ********/
35 #define SOLARIS_ATTRMODE S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP
36 static int solaris_write_xattr(int attrfd
, const char *value
, size_t size
);
37 static ssize_t
solaris_read_xattr(int attrfd
, void *value
, size_t size
);
38 static ssize_t
solaris_list_xattr(int attrdirfd
, char *list
, size_t size
);
39 static int solaris_unlinkat(int attrdirfd
, const char *name
);
40 static int solaris_attropen(const char *path
, const char *attrpath
, int oflag
, mode_t mode
);
41 static int solaris_openat(int fildes
, const char *path
, int oflag
, mode_t mode
);
44 /**************************************************************************
45 Wrappers for extented attribute calls. Based on the Linux package with
46 support for IRIX and (Net|Free)BSD also. Expand as other systems have them.
47 ****************************************************************************/
49 ssize_t
rep_getxattr (const char *path
, const char *name
, void *value
, size_t size
)
51 #if defined(HAVE_XATTR_XATTR)
52 #ifndef XATTR_ADDITIONAL_OPTIONS
53 return getxattr(path
, name
, value
, size
);
56 /* So that we do not recursively call this function */
59 return getxattr(path
, name
, value
, size
, 0, options
);
61 #elif defined(HAVE_XATTR_EA)
62 return getea(path
, name
, value
, size
);
63 #elif defined(HAVE_XATTR_EXTATTR)
68 if (strncmp(name
, "system.", 7) == 0) {
69 attrnamespace
= EXTATTR_NAMESPACE_SYSTEM
;
71 } else if (strncmp(name
, "user.", 5) == 0) {
72 attrnamespace
= EXTATTR_NAMESPACE_USER
;
80 * The BSD implementation has a nasty habit of silently truncating
81 * the returned value to the size of the buffer, so we have to check
82 * that the buffer is large enough to fit the returned value.
84 if((retval
=extattr_get_file(path
, attrnamespace
, attrname
, NULL
, 0)) >= 0) {
87 } else if (retval
> size
) {
91 if((retval
=extattr_get_file(path
, attrnamespace
, attrname
, value
, size
)) >= 0)
96 #elif defined(HAVE_XATTR_ATTR)
97 int retval
, flags
= 0;
98 int valuelength
= (int)size
;
99 char *attrname
= strchr(name
,'.') + 1;
101 if (strncmp(name
, "system", 6) == 0) flags
|= ATTR_ROOT
;
103 retval
= attr_get(path
, attrname
, (char *)value
, &valuelength
, flags
);
104 if (size
== 0 && retval
== -1 && errno
== E2BIG
) {
108 return retval
? retval
: valuelength
;
109 #elif defined(HAVE_ATTROPEN)
111 int attrfd
= solaris_attropen(path
, name
, O_RDONLY
, 0);
113 ret
= solaris_read_xattr(attrfd
, value
, size
);
123 ssize_t
rep_fgetxattr (int filedes
, const char *name
, void *value
, size_t size
)
125 #if defined(HAVE_XATTR_XATTR)
126 #ifndef XATTR_ADDITIONAL_OPTIONS
127 return fgetxattr(filedes
, name
, value
, size
);
130 /* So that we do not recursively call this function */
133 return fgetxattr(filedes
, name
, value
, size
, 0, options
);
135 #elif defined(HAVE_XATTR_EA)
136 return fgetea(filedes
, name
, value
, size
);
137 #elif defined(HAVE_XATTR_EXTATTR)
140 const char *attrname
;
142 if (strncmp(name
, "system.", 7) == 0) {
143 attrnamespace
= EXTATTR_NAMESPACE_SYSTEM
;
145 } else if (strncmp(name
, "user.", 5) == 0) {
146 attrnamespace
= EXTATTR_NAMESPACE_USER
;
153 if((retval
=extattr_get_fd(filedes
, attrnamespace
, attrname
, NULL
, 0)) >= 0) {
156 } else if (retval
> size
) {
160 if((retval
=extattr_get_fd(filedes
, attrnamespace
, attrname
, value
, size
)) >= 0)
165 #elif defined(HAVE_XATTR_ATTR)
166 int retval
, flags
= 0;
167 int valuelength
= (int)size
;
168 char *attrname
= strchr(name
,'.') + 1;
170 if (strncmp(name
, "system", 6) == 0) flags
|= ATTR_ROOT
;
172 retval
= attr_getf(filedes
, attrname
, (char *)value
, &valuelength
, flags
);
173 if (size
== 0 && retval
== -1 && errno
== E2BIG
) {
176 return retval
? retval
: valuelength
;
177 #elif defined(HAVE_ATTROPEN)
179 int attrfd
= solaris_openat(filedes
, name
, O_RDONLY
|O_XATTR
, 0);
181 ret
= solaris_read_xattr(attrfd
, value
, size
);
191 #if defined(HAVE_XATTR_EXTATTR)
193 #define EXTATTR_PREFIX(s) (s), (sizeof((s))-1)
201 { EXTATTR_NAMESPACE_SYSTEM
, EXTATTR_PREFIX("system.") },
202 { EXTATTR_NAMESPACE_USER
, EXTATTR_PREFIX("user.") },
210 static ssize_t
bsd_attr_list (int type
, extattr_arg arg
, char *list
, size_t size
)
212 ssize_t list_size
, total_size
= 0;
216 /* Iterate through extattr(2) namespaces */
217 for(t
= 0; t
< ARRAY_SIZE(extattr
); t
++) {
218 if (t
!= EXTATTR_NAMESPACE_USER
&& geteuid() != 0) {
219 /* ignore all but user namespace when we are not root, see bug 10247 */
224 list_size
= extattr_list_file(arg
.path
, extattr
[t
].space
, list
, size
);
227 list_size
= extattr_list_link(arg
.path
, extattr
[t
].space
, list
, size
);
230 list_size
= extattr_list_fd(arg
.filedes
, extattr
[t
].space
, list
, size
);
236 /* Some error happend. Errno should be set by the previous call */
242 /* XXX: Call with an empty buffer may be used to calculate
243 necessary buffer size. Unfortunately, we can't say, how
244 many attributes were returned, so here is the potential
245 problem with the emulation.
248 /* Take the worse case of one char attribute names -
249 two bytes per name plus one more for sanity.
251 total_size
+= list_size
+ (list_size
/2 + 1)*extattr
[t
].len
;
254 /* Count necessary offset to fit namespace prefixes */
256 for(i
= 0; i
< list_size
; i
+= list
[i
] + 1)
257 len
+= extattr
[t
].len
;
259 total_size
+= list_size
+ len
;
260 /* Buffer is too small to fit the results */
261 if(total_size
> size
) {
265 /* Shift results back, so we can prepend prefixes */
266 buf
= (char *)memmove(list
+ len
, list
, list_size
);
268 for(i
= 0; i
< list_size
; i
+= len
+ 1) {
272 * If for some reason we receive a truncated
273 * return from call to list xattrs the pascal
274 * string lengths will not be changed and
275 * therefore we must check that we're not
276 * reading garbage data or off end of array
278 if (len
+ i
>= list_size
) {
282 strncpy(list
, extattr
[t
].name
, extattr
[t
].len
+ 1);
283 list
+= extattr
[t
].len
;
284 strncpy(list
, buf
+ i
+ 1, len
);
295 #if defined(HAVE_XATTR_ATTR) && (defined(HAVE_SYS_ATTRIBUTES_H) || defined(HAVE_ATTR_ATTRIBUTES_H))
296 static char attr_buffer
[ATTR_MAX_VALUELEN
];
298 static ssize_t
irix_attr_list(const char *path
, int filedes
, char *list
, size_t size
, int flags
)
300 int retval
= 0, index
;
301 attrlist_cursor_t
*cursor
= 0;
303 attrlist_t
* al
= (attrlist_t
*)attr_buffer
;
305 size_t ent_size
, left
= size
;
310 retval
= attr_listf(filedes
, attr_buffer
, ATTR_MAX_VALUELEN
, flags
, cursor
);
312 retval
= attr_list(path
, attr_buffer
, ATTR_MAX_VALUELEN
, flags
, cursor
);
314 for (index
= 0; index
< al
->al_count
; index
++) {
315 ae
= ATTR_ENTRY(attr_buffer
, index
);
316 ent_size
= strlen(ae
->a_name
) + sizeof("user.");
317 if (left
>= ent_size
) {
318 strncpy(bp
, "user.", sizeof("user."));
319 strncat(bp
, ae
->a_name
, ent_size
- sizeof("user."));
327 total_size
+= ent_size
;
329 if (al
->al_more
== 0) break;
336 retval
= attr_listf(filedes
, attr_buffer
, ATTR_MAX_VALUELEN
, flags
, cursor
);
338 retval
= attr_list(path
, attr_buffer
, ATTR_MAX_VALUELEN
, flags
, cursor
);
340 for (index
= 0; index
< al
->al_count
; index
++) {
341 ae
= ATTR_ENTRY(attr_buffer
, index
);
342 ent_size
= strlen(ae
->a_name
) + sizeof("system.");
343 if (left
>= ent_size
) {
344 strncpy(bp
, "system.", sizeof("system."));
345 strncat(bp
, ae
->a_name
, ent_size
- sizeof("system."));
353 total_size
+= ent_size
;
355 if (al
->al_more
== 0) break;
358 return (ssize_t
)(retval
? retval
: total_size
);
363 ssize_t
rep_listxattr (const char *path
, char *list
, size_t size
)
365 #if defined(HAVE_XATTR_XATTR)
366 #ifndef XATTR_ADDITIONAL_OPTIONS
367 return listxattr(path
, list
, size
);
369 /* So that we do not recursively call this function */
372 return listxattr(path
, list
, size
, options
);
374 #elif defined(HAVE_XATTR_EA)
375 return listea(path
, list
, size
);
376 #elif defined(HAVE_XATTR_EXTATTR)
379 return bsd_attr_list(0, arg
, list
, size
);
380 #elif defined(HAVE_XATTR_ATTR) && defined(HAVE_SYS_ATTRIBUTES_H)
381 return irix_attr_list(path
, 0, list
, size
, 0);
382 #elif defined(HAVE_ATTROPEN)
384 int attrdirfd
= solaris_attropen(path
, ".", O_RDONLY
, 0);
385 if (attrdirfd
>= 0) {
386 ret
= solaris_list_xattr(attrdirfd
, list
, size
);
396 ssize_t
rep_flistxattr (int filedes
, char *list
, size_t size
)
398 #if defined(HAVE_XATTR_XATTR)
399 #ifndef XATTR_ADDITIONAL_OPTIONS
400 return flistxattr(filedes
, list
, size
);
402 /* So that we do not recursively call this function */
405 return flistxattr(filedes
, list
, size
, options
);
407 #elif defined(HAVE_XATTR_EA)
408 return flistea(filedes
, list
, size
);
409 #elif defined(HAVE_XATTR_EXTATTR)
411 arg
.filedes
= filedes
;
412 return bsd_attr_list(2, arg
, list
, size
);
413 #elif defined(HAVE_XATTR_ATTR)
414 return irix_attr_list(NULL
, filedes
, list
, size
, 0);
415 #elif defined(HAVE_ATTROPEN)
417 int attrdirfd
= solaris_openat(filedes
, ".", O_RDONLY
|O_XATTR
, 0);
418 if (attrdirfd
>= 0) {
419 ret
= solaris_list_xattr(attrdirfd
, list
, size
);
429 int rep_removexattr (const char *path
, const char *name
)
431 #if defined(HAVE_XATTR_XATTR)
432 #ifndef XATTR_ADDITIONAL_OPTIONS
433 return removexattr(path
, name
);
435 /* So that we do not recursively call this function */
438 return removexattr(path
, name
, options
);
440 #elif defined(HAVE_XATTR_EA)
441 return removeea(path
, name
);
442 #elif defined(HAVE_XATTR_EXTATTR)
444 const char *attrname
;
446 if (strncmp(name
, "system.", 7) == 0) {
447 attrnamespace
= EXTATTR_NAMESPACE_SYSTEM
;
449 } else if (strncmp(name
, "user.", 5) == 0) {
450 attrnamespace
= EXTATTR_NAMESPACE_USER
;
457 return extattr_delete_file(path
, attrnamespace
, attrname
);
458 #elif defined(HAVE_XATTR_ATTR)
460 char *attrname
= strchr(name
,'.') + 1;
462 if (strncmp(name
, "system", 6) == 0) flags
|= ATTR_ROOT
;
464 return attr_remove(path
, attrname
, flags
);
465 #elif defined(HAVE_ATTROPEN)
467 int attrdirfd
= solaris_attropen(path
, ".", O_RDONLY
, 0);
468 if (attrdirfd
>= 0) {
469 ret
= solaris_unlinkat(attrdirfd
, name
);
479 int rep_fremovexattr (int filedes
, const char *name
)
481 #if defined(HAVE_XATTR_XATTR)
482 #ifndef XATTR_ADDITIONAL_OPTIONS
483 return fremovexattr(filedes
, name
);
485 /* So that we do not recursively call this function */
488 return fremovexattr(filedes
, name
, options
);
490 #elif defined(HAVE_XATTR_EA)
491 return fremoveea(filedes
, name
);
492 #elif defined(HAVE_XATTR_EXTATTR)
494 const char *attrname
;
496 if (strncmp(name
, "system.", 7) == 0) {
497 attrnamespace
= EXTATTR_NAMESPACE_SYSTEM
;
499 } else if (strncmp(name
, "user.", 5) == 0) {
500 attrnamespace
= EXTATTR_NAMESPACE_USER
;
507 return extattr_delete_fd(filedes
, attrnamespace
, attrname
);
508 #elif defined(HAVE_XATTR_ATTR)
510 char *attrname
= strchr(name
,'.') + 1;
512 if (strncmp(name
, "system", 6) == 0) flags
|= ATTR_ROOT
;
514 return attr_removef(filedes
, attrname
, flags
);
515 #elif defined(HAVE_ATTROPEN)
517 int attrdirfd
= solaris_openat(filedes
, ".", O_RDONLY
|O_XATTR
, 0);
518 if (attrdirfd
>= 0) {
519 ret
= solaris_unlinkat(attrdirfd
, name
);
529 int rep_setxattr (const char *path
, const char *name
, const void *value
, size_t size
, int flags
)
532 #if defined(HAVE_XATTR_XATTR)
533 #ifndef XATTR_ADDITIONAL_OPTIONS
534 retval
= setxattr(path
, name
, value
, size
, flags
);
536 if (errno
== ENOSPC
|| errno
== E2BIG
) {
537 errno
= ENAMETOOLONG
;
542 /* So that we do not recursively call this function */
544 retval
= setxattr(path
, name
, value
, size
, 0, flags
);
546 if (errno
== E2BIG
) {
547 errno
= ENAMETOOLONG
;
552 #elif defined(HAVE_XATTR_EA)
554 retval
= getea(path
, name
, NULL
, 0);
556 if (flags
& XATTR_REPLACE
&& errno
== ENOATTR
) {
560 if (flags
& XATTR_CREATE
) {
566 retval
= setea(path
, name
, value
, size
, 0);
568 #elif defined(HAVE_XATTR_EXTATTR)
570 const char *attrname
;
572 if (strncmp(name
, "system.", 7) == 0) {
573 attrnamespace
= EXTATTR_NAMESPACE_SYSTEM
;
575 } else if (strncmp(name
, "user.", 5) == 0) {
576 attrnamespace
= EXTATTR_NAMESPACE_USER
;
584 /* Check attribute existence */
585 retval
= extattr_get_file(path
, attrnamespace
, attrname
, NULL
, 0);
587 /* REPLACE attribute, that doesn't exist */
588 if (flags
& XATTR_REPLACE
&& errno
== ENOATTR
) {
592 /* Ignore other errors */
595 /* CREATE attribute, that already exists */
596 if (flags
& XATTR_CREATE
) {
602 retval
= extattr_set_file(path
, attrnamespace
, attrname
, value
, size
);
603 return (retval
< 0) ? -1 : 0;
604 #elif defined(HAVE_XATTR_ATTR)
606 char *attrname
= strchr(name
,'.') + 1;
608 if (strncmp(name
, "system", 6) == 0) myflags
|= ATTR_ROOT
;
609 if (flags
& XATTR_CREATE
) myflags
|= ATTR_CREATE
;
610 if (flags
& XATTR_REPLACE
) myflags
|= ATTR_REPLACE
;
612 retval
= attr_set(path
, attrname
, (const char *)value
, size
, myflags
);
614 if (errno
== E2BIG
) {
615 errno
= ENAMETOOLONG
;
619 #elif defined(HAVE_ATTROPEN)
620 int myflags
= O_RDWR
;
622 if (flags
& XATTR_CREATE
) myflags
|= O_EXCL
;
623 if (!(flags
& XATTR_REPLACE
)) myflags
|= O_CREAT
;
624 attrfd
= solaris_attropen(path
, name
, myflags
, (mode_t
) SOLARIS_ATTRMODE
);
626 retval
= solaris_write_xattr(attrfd
, value
, size
);
636 int rep_fsetxattr (int filedes
, const char *name
, const void *value
, size_t size
, int flags
)
639 #if defined(HAVE_XATTR_XATTR)
640 #ifndef XATTR_ADDITIONAL_OPTIONS
641 retval
= fsetxattr(filedes
, name
, value
, size
, flags
);
643 if (errno
== ENOSPC
) {
644 errno
= ENAMETOOLONG
;
649 /* So that we do not recursively call this function */
651 retval
= fsetxattr(filedes
, name
, value
, size
, 0, flags
);
653 if (errno
== E2BIG
) {
654 errno
= ENAMETOOLONG
;
659 #elif defined(HAVE_XATTR_EA)
661 retval
= fgetea(filedes
, name
, NULL
, 0);
663 if (flags
& XATTR_REPLACE
&& errno
== ENOATTR
) {
667 if (flags
& XATTR_CREATE
) {
673 retval
= fsetea(filedes
, name
, value
, size
, 0);
675 #elif defined(HAVE_XATTR_EXTATTR)
677 const char *attrname
;
679 if (strncmp(name
, "system.", 7) == 0) {
680 attrnamespace
= EXTATTR_NAMESPACE_SYSTEM
;
682 } else if (strncmp(name
, "user.", 5) == 0) {
683 attrnamespace
= EXTATTR_NAMESPACE_USER
;
691 /* Check attribute existence */
692 retval
= extattr_get_fd(filedes
, attrnamespace
, attrname
, NULL
, 0);
694 /* REPLACE attribute, that doesn't exist */
695 if (flags
& XATTR_REPLACE
&& errno
== ENOATTR
) {
699 /* Ignore other errors */
702 /* CREATE attribute, that already exists */
703 if (flags
& XATTR_CREATE
) {
709 retval
= extattr_set_fd(filedes
, attrnamespace
, attrname
, value
, size
);
710 return (retval
< 0) ? -1 : 0;
711 #elif defined(HAVE_XATTR_ATTR)
713 char *attrname
= strchr(name
,'.') + 1;
715 if (strncmp(name
, "system", 6) == 0) myflags
|= ATTR_ROOT
;
716 if (flags
& XATTR_CREATE
) myflags
|= ATTR_CREATE
;
717 if (flags
& XATTR_REPLACE
) myflags
|= ATTR_REPLACE
;
719 return attr_setf(filedes
, attrname
, (const char *)value
, size
, myflags
);
720 #elif defined(HAVE_ATTROPEN)
721 int myflags
= O_RDWR
| O_XATTR
;
723 if (flags
& XATTR_CREATE
) myflags
|= O_EXCL
;
724 if (!(flags
& XATTR_REPLACE
)) myflags
|= O_CREAT
;
725 attrfd
= solaris_openat(filedes
, name
, myflags
, (mode_t
) SOLARIS_ATTRMODE
);
727 retval
= solaris_write_xattr(attrfd
, value
, size
);
737 /**************************************************************************
738 helper functions for Solaris' EA support
739 ****************************************************************************/
741 static ssize_t
solaris_read_xattr(int attrfd
, void *value
, size_t size
)
745 if (fstat(attrfd
, &sbuf
) == -1) {
750 /* This is to return the current size of the named extended attribute */
755 /* check size and read xattr */
756 if (sbuf
.st_size
> size
) {
761 return read(attrfd
, value
, sbuf
.st_size
);
764 static ssize_t
solaris_list_xattr(int attrdirfd
, char *list
, size_t size
)
769 int newfd
= dup(attrdirfd
);
770 /* CAUTION: The originating file descriptor should not be
771 used again following the call to fdopendir().
772 For that reason we dup() the file descriptor
773 here to make things more clear. */
774 dirp
= fdopendir(newfd
);
776 while ((de
= readdir(dirp
))) {
777 size_t listlen
= strlen(de
->d_name
) + 1;
778 if (!strcmp(de
->d_name
, ".") || !strcmp(de
->d_name
, "..")) {
779 /* we don't want "." and ".." here: */
784 /* return the current size of the list of extended attribute names*/
787 /* check size and copy entrieѕ + nul into list. */
788 if ((len
+ listlen
) > size
) {
793 strlcpy(list
+ len
, de
->d_name
, listlen
);
799 if (closedir(dirp
) == -1) {
805 static int solaris_unlinkat(int attrdirfd
, const char *name
)
807 if (unlinkat(attrdirfd
, name
, 0) == -1) {
808 if (errno
== ENOENT
) {
816 static int solaris_attropen(const char *path
, const char *attrpath
, int oflag
, mode_t mode
)
818 int filedes
= attropen(path
, attrpath
, oflag
, mode
);
820 if (errno
== EINVAL
) {
829 static int solaris_openat(int fildes
, const char *path
, int oflag
, mode_t mode
)
831 int filedes
= openat(fildes
, path
, oflag
, mode
);
833 if (errno
== EINVAL
) {
842 static int solaris_write_xattr(int attrfd
, const char *value
, size_t size
)
844 if ((ftruncate(attrfd
, 0) == 0) && (write(attrfd
, value
, size
) == size
)) {
850 #endif /*HAVE_ATTROPEN*/