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 recursivly 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 recursivly 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;
215 /* Iterate through extattr(2) namespaces */
216 for(t
= 0; t
< ARRAY_SIZE(extattr
); t
++) {
217 if (t
!= EXTATTR_NAMESPACE_USER
&& geteuid() != 0) {
218 /* ignore all but user namespace when we are not root, see bug 10247 */
223 list_size
= extattr_list_file(arg
.path
, extattr
[t
].space
, list
, size
);
226 list_size
= extattr_list_link(arg
.path
, extattr
[t
].space
, list
, size
);
229 list_size
= extattr_list_fd(arg
.filedes
, extattr
[t
].space
, list
, size
);
235 /* Some error happend. Errno should be set by the previous call */
241 /* XXX: Call with an empty buffer may be used to calculate
242 necessary buffer size. Unfortunately, we can't say, how
243 many attributes were returned, so here is the potential
244 problem with the emulation.
247 /* Take the worse case of one char attribute names -
248 two bytes per name plus one more for sanity.
250 total_size
+= list_size
+ (list_size
/2 + 1)*extattr
[t
].len
;
253 /* Count necessary offset to fit namespace prefixes */
255 for(i
= 0; i
< list_size
; i
+= list
[i
] + 1)
256 len
+= extattr
[t
].len
;
258 total_size
+= list_size
+ len
;
259 /* Buffer is too small to fit the results */
260 if(total_size
> size
) {
264 /* Shift results back, so we can prepend prefixes */
265 buf
= (char *)memmove(list
+ len
, list
, list_size
);
267 for(i
= 0; i
< list_size
; i
+= len
+ 1) {
269 strncpy(list
, extattr
[t
].name
, extattr
[t
].len
+ 1);
270 list
+= extattr
[t
].len
;
271 strncpy(list
, buf
+ i
+ 1, len
);
282 #if defined(HAVE_XATTR_ATTR) && (defined(HAVE_SYS_ATTRIBUTES_H) || defined(HAVE_ATTR_ATTRIBUTES_H))
283 static char attr_buffer
[ATTR_MAX_VALUELEN
];
285 static ssize_t
irix_attr_list(const char *path
, int filedes
, char *list
, size_t size
, int flags
)
287 int retval
= 0, index
;
288 attrlist_cursor_t
*cursor
= 0;
290 attrlist_t
* al
= (attrlist_t
*)attr_buffer
;
292 size_t ent_size
, left
= size
;
297 retval
= attr_listf(filedes
, attr_buffer
, ATTR_MAX_VALUELEN
, flags
, cursor
);
299 retval
= attr_list(path
, attr_buffer
, ATTR_MAX_VALUELEN
, flags
, cursor
);
301 for (index
= 0; index
< al
->al_count
; index
++) {
302 ae
= ATTR_ENTRY(attr_buffer
, index
);
303 ent_size
= strlen(ae
->a_name
) + sizeof("user.");
304 if (left
>= ent_size
) {
305 strncpy(bp
, "user.", sizeof("user."));
306 strncat(bp
, ae
->a_name
, ent_size
- sizeof("user."));
314 total_size
+= ent_size
;
316 if (al
->al_more
== 0) break;
323 retval
= attr_listf(filedes
, attr_buffer
, ATTR_MAX_VALUELEN
, flags
, cursor
);
325 retval
= attr_list(path
, attr_buffer
, ATTR_MAX_VALUELEN
, flags
, cursor
);
327 for (index
= 0; index
< al
->al_count
; index
++) {
328 ae
= ATTR_ENTRY(attr_buffer
, index
);
329 ent_size
= strlen(ae
->a_name
) + sizeof("system.");
330 if (left
>= ent_size
) {
331 strncpy(bp
, "system.", sizeof("system."));
332 strncat(bp
, ae
->a_name
, ent_size
- sizeof("system."));
340 total_size
+= ent_size
;
342 if (al
->al_more
== 0) break;
345 return (ssize_t
)(retval
? retval
: total_size
);
350 ssize_t
rep_listxattr (const char *path
, char *list
, size_t size
)
352 #if defined(HAVE_XATTR_XATTR)
353 #ifndef XATTR_ADDITIONAL_OPTIONS
354 return listxattr(path
, list
, size
);
356 /* So that we do not recursivly call this function */
359 return listxattr(path
, list
, size
, options
);
361 #elif defined(HAVE_XATTR_EA)
362 return listea(path
, list
, size
);
363 #elif defined(HAVE_XATTR_EXTATTR)
366 return bsd_attr_list(0, arg
, list
, size
);
367 #elif defined(HAVE_XATTR_ATTR) && defined(HAVE_SYS_ATTRIBUTES_H)
368 return irix_attr_list(path
, 0, list
, size
, 0);
369 #elif defined(HAVE_ATTROPEN)
371 int attrdirfd
= solaris_attropen(path
, ".", O_RDONLY
, 0);
372 if (attrdirfd
>= 0) {
373 ret
= solaris_list_xattr(attrdirfd
, list
, size
);
383 ssize_t
rep_flistxattr (int filedes
, char *list
, size_t size
)
385 #if defined(HAVE_XATTR_XATTR)
386 #ifndef XATTR_ADDITIONAL_OPTIONS
387 return flistxattr(filedes
, list
, size
);
389 /* So that we do not recursivly call this function */
392 return flistxattr(filedes
, list
, size
, options
);
394 #elif defined(HAVE_XATTR_EA)
395 return flistea(filedes
, list
, size
);
396 #elif defined(HAVE_XATTR_EXTATTR)
398 arg
.filedes
= filedes
;
399 return bsd_attr_list(2, arg
, list
, size
);
400 #elif defined(HAVE_XATTR_ATTR)
401 return irix_attr_list(NULL
, filedes
, list
, size
, 0);
402 #elif defined(HAVE_ATTROPEN)
404 int attrdirfd
= solaris_openat(filedes
, ".", O_RDONLY
|O_XATTR
, 0);
405 if (attrdirfd
>= 0) {
406 ret
= solaris_list_xattr(attrdirfd
, list
, size
);
416 int rep_removexattr (const char *path
, const char *name
)
418 #if defined(HAVE_XATTR_XATTR)
419 #ifndef XATTR_ADDITIONAL_OPTIONS
420 return removexattr(path
, name
);
422 /* So that we do not recursivly call this function */
425 return removexattr(path
, name
, options
);
427 #elif defined(HAVE_XATTR_EA)
428 return removeea(path
, name
);
429 #elif defined(HAVE_XATTR_EXTATTR)
431 const char *attrname
;
433 if (strncmp(name
, "system.", 7) == 0) {
434 attrnamespace
= EXTATTR_NAMESPACE_SYSTEM
;
436 } else if (strncmp(name
, "user.", 5) == 0) {
437 attrnamespace
= EXTATTR_NAMESPACE_USER
;
444 return extattr_delete_file(path
, attrnamespace
, attrname
);
445 #elif defined(HAVE_XATTR_ATTR)
447 char *attrname
= strchr(name
,'.') + 1;
449 if (strncmp(name
, "system", 6) == 0) flags
|= ATTR_ROOT
;
451 return attr_remove(path
, attrname
, flags
);
452 #elif defined(HAVE_ATTROPEN)
454 int attrdirfd
= solaris_attropen(path
, ".", O_RDONLY
, 0);
455 if (attrdirfd
>= 0) {
456 ret
= solaris_unlinkat(attrdirfd
, name
);
466 int rep_fremovexattr (int filedes
, const char *name
)
468 #if defined(HAVE_XATTR_XATTR)
469 #ifndef XATTR_ADDITIONAL_OPTIONS
470 return fremovexattr(filedes
, name
);
472 /* So that we do not recursivly call this function */
475 return fremovexattr(filedes
, name
, options
);
477 #elif defined(HAVE_XATTR_EA)
478 return fremoveea(filedes
, name
);
479 #elif defined(HAVE_XATTR_EXTATTR)
481 const char *attrname
;
483 if (strncmp(name
, "system.", 7) == 0) {
484 attrnamespace
= EXTATTR_NAMESPACE_SYSTEM
;
486 } else if (strncmp(name
, "user.", 5) == 0) {
487 attrnamespace
= EXTATTR_NAMESPACE_USER
;
494 return extattr_delete_fd(filedes
, attrnamespace
, attrname
);
495 #elif defined(HAVE_XATTR_ATTR)
497 char *attrname
= strchr(name
,'.') + 1;
499 if (strncmp(name
, "system", 6) == 0) flags
|= ATTR_ROOT
;
501 return attr_removef(filedes
, attrname
, flags
);
502 #elif defined(HAVE_ATTROPEN)
504 int attrdirfd
= solaris_openat(filedes
, ".", O_RDONLY
|O_XATTR
, 0);
505 if (attrdirfd
>= 0) {
506 ret
= solaris_unlinkat(attrdirfd
, name
);
516 int rep_setxattr (const char *path
, const char *name
, const void *value
, size_t size
, int flags
)
518 #if defined(HAVE_XATTR_XATTR)
519 #ifndef XATTR_ADDITIONAL_OPTIONS
520 return setxattr(path
, name
, value
, size
, flags
);
522 /* So that we do not recursivly call this function */
525 return setxattr(path
, name
, value
, size
, 0, options
);
527 #elif defined(HAVE_XATTR_EA)
528 return setea(path
, name
, value
, size
, flags
);
529 #elif defined(HAVE_XATTR_EXTATTR)
532 const char *attrname
;
534 if (strncmp(name
, "system.", 7) == 0) {
535 attrnamespace
= EXTATTR_NAMESPACE_SYSTEM
;
537 } else if (strncmp(name
, "user.", 5) == 0) {
538 attrnamespace
= EXTATTR_NAMESPACE_USER
;
546 /* Check attribute existence */
547 retval
= extattr_get_file(path
, attrnamespace
, attrname
, NULL
, 0);
549 /* REPLACE attribute, that doesn't exist */
550 if (flags
& XATTR_REPLACE
&& errno
== ENOATTR
) {
554 /* Ignore other errors */
557 /* CREATE attribute, that already exists */
558 if (flags
& XATTR_CREATE
) {
564 retval
= extattr_set_file(path
, attrnamespace
, attrname
, value
, size
);
565 return (retval
< 0) ? -1 : 0;
566 #elif defined(HAVE_XATTR_ATTR)
568 char *attrname
= strchr(name
,'.') + 1;
570 if (strncmp(name
, "system", 6) == 0) myflags
|= ATTR_ROOT
;
571 if (flags
& XATTR_CREATE
) myflags
|= ATTR_CREATE
;
572 if (flags
& XATTR_REPLACE
) myflags
|= ATTR_REPLACE
;
574 return attr_set(path
, attrname
, (const char *)value
, size
, myflags
);
575 #elif defined(HAVE_ATTROPEN)
577 int myflags
= O_RDWR
;
579 if (flags
& XATTR_CREATE
) myflags
|= O_EXCL
;
580 if (!(flags
& XATTR_REPLACE
)) myflags
|= O_CREAT
;
581 attrfd
= solaris_attropen(path
, name
, myflags
, (mode_t
) SOLARIS_ATTRMODE
);
583 ret
= solaris_write_xattr(attrfd
, value
, size
);
593 int rep_fsetxattr (int filedes
, const char *name
, const void *value
, size_t size
, int flags
)
595 #if defined(HAVE_XATTR_XATTR)
596 #ifndef XATTR_ADDITIONAL_OPTIONS
597 return fsetxattr(filedes
, name
, value
, size
, flags
);
599 /* So that we do not recursivly call this function */
602 return fsetxattr(filedes
, name
, value
, size
, 0, options
);
604 #elif defined(HAVE_XATTR_EA)
605 return fsetea(filedes
, name
, value
, size
, flags
);
606 #elif defined(HAVE_XATTR_EXTATTR)
609 const char *attrname
;
611 if (strncmp(name
, "system.", 7) == 0) {
612 attrnamespace
= EXTATTR_NAMESPACE_SYSTEM
;
614 } else if (strncmp(name
, "user.", 5) == 0) {
615 attrnamespace
= EXTATTR_NAMESPACE_USER
;
623 /* Check attribute existence */
624 retval
= extattr_get_fd(filedes
, attrnamespace
, attrname
, NULL
, 0);
626 /* REPLACE attribute, that doesn't exist */
627 if (flags
& XATTR_REPLACE
&& errno
== ENOATTR
) {
631 /* Ignore other errors */
634 /* CREATE attribute, that already exists */
635 if (flags
& XATTR_CREATE
) {
641 retval
= extattr_set_fd(filedes
, attrnamespace
, attrname
, value
, size
);
642 return (retval
< 0) ? -1 : 0;
643 #elif defined(HAVE_XATTR_ATTR)
645 char *attrname
= strchr(name
,'.') + 1;
647 if (strncmp(name
, "system", 6) == 0) myflags
|= ATTR_ROOT
;
648 if (flags
& XATTR_CREATE
) myflags
|= ATTR_CREATE
;
649 if (flags
& XATTR_REPLACE
) myflags
|= ATTR_REPLACE
;
651 return attr_setf(filedes
, attrname
, (const char *)value
, size
, myflags
);
652 #elif defined(HAVE_ATTROPEN)
654 int myflags
= O_RDWR
| O_XATTR
;
656 if (flags
& XATTR_CREATE
) myflags
|= O_EXCL
;
657 if (!(flags
& XATTR_REPLACE
)) myflags
|= O_CREAT
;
658 attrfd
= solaris_openat(filedes
, name
, myflags
, (mode_t
) SOLARIS_ATTRMODE
);
660 ret
= solaris_write_xattr(attrfd
, value
, size
);
670 /**************************************************************************
671 helper functions for Solaris' EA support
672 ****************************************************************************/
674 static ssize_t
solaris_read_xattr(int attrfd
, void *value
, size_t size
)
678 if (fstat(attrfd
, &sbuf
) == -1) {
683 /* This is to return the current size of the named extended attribute */
688 /* check size and read xattr */
689 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
))) {
710 size_t listlen
= strlen(de
->d_name
) + 1;
711 if (!strcmp(de
->d_name
, ".") || !strcmp(de
->d_name
, "..")) {
712 /* we don't want "." and ".." here: */
717 /* return the current size of the list of extended attribute names*/
720 /* check size and copy entrieѕ + nul into list. */
721 if ((len
+ listlen
) > size
) {
726 strlcpy(list
+ len
, de
->d_name
, listlen
);
732 if (closedir(dirp
) == -1) {
738 static int solaris_unlinkat(int attrdirfd
, const char *name
)
740 if (unlinkat(attrdirfd
, name
, 0) == -1) {
741 if (errno
== ENOENT
) {
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 if (errno
== EINVAL
) {
762 static int solaris_openat(int fildes
, const char *path
, int oflag
, mode_t mode
)
764 int filedes
= openat(fildes
, path
, oflag
, mode
);
766 if (errno
== EINVAL
) {
775 static int solaris_write_xattr(int attrfd
, const char *value
, size_t size
)
777 if ((ftruncate(attrfd
, 0) == 0) && (write(attrfd
, value
, size
) == size
)) {
783 #endif /*HAVE_ATTROPEN*/