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_GETXATTR)
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_GETEA)
62 return getea(path
, name
, value
, size
);
63 #elif defined(HAVE_EXTATTR_GET_FILE)
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_ATTR_GET)
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_FGETXATTR)
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_FGETEA)
136 return fgetea(filedes
, name
, value
, size
);
137 #elif defined(HAVE_EXTATTR_GET_FD)
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_ATTR_GETF)
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_EXTATTR_LIST_FILE)
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 */
222 #if defined(HAVE_EXTATTR_LIST_FILE)
224 list_size
= extattr_list_file(arg
.path
, extattr
[t
].space
, list
, size
);
227 #if defined(HAVE_EXTATTR_LIST_LINK)
229 list_size
= extattr_list_link(arg
.path
, extattr
[t
].space
, list
, size
);
232 #if defined(HAVE_EXTATTR_LIST_FD)
234 list_size
= extattr_list_fd(arg
.filedes
, extattr
[t
].space
, list
, size
);
241 /* Some error happend. Errno should be set by the previous call */
247 /* XXX: Call with an empty buffer may be used to calculate
248 necessary buffer size. Unfortunately, we can't say, how
249 many attributes were returned, so here is the potential
250 problem with the emulation.
253 /* Take the worse case of one char attribute names -
254 two bytes per name plus one more for sanity.
256 total_size
+= list_size
+ (list_size
/2 + 1)*extattr
[t
].len
;
259 /* Count necessary offset to fit namespace prefixes */
261 for(i
= 0; i
< list_size
; i
+= list
[i
] + 1)
262 len
+= extattr
[t
].len
;
264 total_size
+= list_size
+ len
;
265 /* Buffer is too small to fit the results */
266 if(total_size
> size
) {
270 /* Shift results back, so we can prepend prefixes */
271 buf
= (char *)memmove(list
+ len
, list
, list_size
);
273 for(i
= 0; i
< list_size
; i
+= len
+ 1) {
275 strncpy(list
, extattr
[t
].name
, extattr
[t
].len
+ 1);
276 list
+= extattr
[t
].len
;
277 strncpy(list
, buf
+ i
+ 1, len
);
288 #if defined(HAVE_ATTR_LIST) && (defined(HAVE_SYS_ATTRIBUTES_H) || defined(HAVE_ATTR_ATTRIBUTES_H))
289 static char attr_buffer
[ATTR_MAX_VALUELEN
];
291 static ssize_t
irix_attr_list(const char *path
, int filedes
, char *list
, size_t size
, int flags
)
293 int retval
= 0, index
;
294 attrlist_cursor_t
*cursor
= 0;
296 attrlist_t
* al
= (attrlist_t
*)attr_buffer
;
298 size_t ent_size
, left
= size
;
303 retval
= attr_listf(filedes
, attr_buffer
, ATTR_MAX_VALUELEN
, flags
, cursor
);
305 retval
= attr_list(path
, attr_buffer
, ATTR_MAX_VALUELEN
, flags
, cursor
);
307 for (index
= 0; index
< al
->al_count
; index
++) {
308 ae
= ATTR_ENTRY(attr_buffer
, index
);
309 ent_size
= strlen(ae
->a_name
) + sizeof("user.");
310 if (left
>= ent_size
) {
311 strncpy(bp
, "user.", sizeof("user."));
312 strncat(bp
, ae
->a_name
, ent_size
- sizeof("user."));
320 total_size
+= ent_size
;
322 if (al
->al_more
== 0) break;
329 retval
= attr_listf(filedes
, attr_buffer
, ATTR_MAX_VALUELEN
, flags
, cursor
);
331 retval
= attr_list(path
, attr_buffer
, ATTR_MAX_VALUELEN
, flags
, cursor
);
333 for (index
= 0; index
< al
->al_count
; index
++) {
334 ae
= ATTR_ENTRY(attr_buffer
, index
);
335 ent_size
= strlen(ae
->a_name
) + sizeof("system.");
336 if (left
>= ent_size
) {
337 strncpy(bp
, "system.", sizeof("system."));
338 strncat(bp
, ae
->a_name
, ent_size
- sizeof("system."));
346 total_size
+= ent_size
;
348 if (al
->al_more
== 0) break;
351 return (ssize_t
)(retval
? retval
: total_size
);
356 ssize_t
rep_listxattr (const char *path
, char *list
, size_t size
)
358 #if defined(HAVE_LISTXATTR)
359 #ifndef XATTR_ADDITIONAL_OPTIONS
360 return listxattr(path
, list
, size
);
362 /* So that we do not recursivly call this function */
365 return listxattr(path
, list
, size
, options
);
367 #elif defined(HAVE_LISTEA)
368 return listea(path
, list
, size
);
369 #elif defined(HAVE_EXTATTR_LIST_FILE)
372 return bsd_attr_list(0, arg
, list
, size
);
373 #elif defined(HAVE_ATTR_LIST) && defined(HAVE_SYS_ATTRIBUTES_H)
374 return irix_attr_list(path
, 0, list
, size
, 0);
375 #elif defined(HAVE_ATTROPEN)
377 int attrdirfd
= solaris_attropen(path
, ".", O_RDONLY
, 0);
378 if (attrdirfd
>= 0) {
379 ret
= solaris_list_xattr(attrdirfd
, list
, size
);
389 ssize_t
rep_flistxattr (int filedes
, char *list
, size_t size
)
391 #if defined(HAVE_FLISTXATTR)
392 #ifndef XATTR_ADDITIONAL_OPTIONS
393 return flistxattr(filedes
, list
, size
);
395 /* So that we do not recursivly call this function */
398 return flistxattr(filedes
, list
, size
, options
);
400 #elif defined(HAVE_FLISTEA)
401 return flistea(filedes
, list
, size
);
402 #elif defined(HAVE_EXTATTR_LIST_FD)
404 arg
.filedes
= filedes
;
405 return bsd_attr_list(2, arg
, list
, size
);
406 #elif defined(HAVE_ATTR_LISTF)
407 return irix_attr_list(NULL
, filedes
, list
, size
, 0);
408 #elif defined(HAVE_ATTROPEN)
410 int attrdirfd
= solaris_openat(filedes
, ".", O_RDONLY
|O_XATTR
, 0);
411 if (attrdirfd
>= 0) {
412 ret
= solaris_list_xattr(attrdirfd
, list
, size
);
422 int rep_removexattr (const char *path
, const char *name
)
424 #if defined(HAVE_REMOVEXATTR)
425 #ifndef XATTR_ADDITIONAL_OPTIONS
426 return removexattr(path
, name
);
428 /* So that we do not recursivly call this function */
431 return removexattr(path
, name
, options
);
433 #elif defined(HAVE_REMOVEEA)
434 return removeea(path
, name
);
435 #elif defined(HAVE_EXTATTR_DELETE_FILE)
437 const char *attrname
;
439 if (strncmp(name
, "system.", 7) == 0) {
440 attrnamespace
= EXTATTR_NAMESPACE_SYSTEM
;
442 } else if (strncmp(name
, "user.", 5) == 0) {
443 attrnamespace
= EXTATTR_NAMESPACE_USER
;
450 return extattr_delete_file(path
, attrnamespace
, attrname
);
451 #elif defined(HAVE_ATTR_REMOVE)
453 char *attrname
= strchr(name
,'.') + 1;
455 if (strncmp(name
, "system", 6) == 0) flags
|= ATTR_ROOT
;
457 return attr_remove(path
, attrname
, flags
);
458 #elif defined(HAVE_ATTROPEN)
460 int attrdirfd
= solaris_attropen(path
, ".", O_RDONLY
, 0);
461 if (attrdirfd
>= 0) {
462 ret
= solaris_unlinkat(attrdirfd
, name
);
472 int rep_fremovexattr (int filedes
, const char *name
)
474 #if defined(HAVE_FREMOVEXATTR)
475 #ifndef XATTR_ADDITIONAL_OPTIONS
476 return fremovexattr(filedes
, name
);
478 /* So that we do not recursivly call this function */
481 return fremovexattr(filedes
, name
, options
);
483 #elif defined(HAVE_FREMOVEEA)
484 return fremoveea(filedes
, name
);
485 #elif defined(HAVE_EXTATTR_DELETE_FD)
487 const char *attrname
;
489 if (strncmp(name
, "system.", 7) == 0) {
490 attrnamespace
= EXTATTR_NAMESPACE_SYSTEM
;
492 } else if (strncmp(name
, "user.", 5) == 0) {
493 attrnamespace
= EXTATTR_NAMESPACE_USER
;
500 return extattr_delete_fd(filedes
, attrnamespace
, attrname
);
501 #elif defined(HAVE_ATTR_REMOVEF)
503 char *attrname
= strchr(name
,'.') + 1;
505 if (strncmp(name
, "system", 6) == 0) flags
|= ATTR_ROOT
;
507 return attr_removef(filedes
, attrname
, flags
);
508 #elif defined(HAVE_ATTROPEN)
510 int attrdirfd
= solaris_openat(filedes
, ".", O_RDONLY
|O_XATTR
, 0);
511 if (attrdirfd
>= 0) {
512 ret
= solaris_unlinkat(attrdirfd
, name
);
522 int rep_setxattr (const char *path
, const char *name
, const void *value
, size_t size
, int flags
)
524 #if defined(HAVE_SETXATTR)
525 #ifndef XATTR_ADDITIONAL_OPTIONS
526 return setxattr(path
, name
, value
, size
, flags
);
528 /* So that we do not recursivly call this function */
531 return setxattr(path
, name
, value
, size
, 0, options
);
533 #elif defined(HAVE_SETEA)
534 return setea(path
, name
, value
, size
, flags
);
535 #elif defined(HAVE_EXTATTR_SET_FILE)
538 const char *attrname
;
540 if (strncmp(name
, "system.", 7) == 0) {
541 attrnamespace
= EXTATTR_NAMESPACE_SYSTEM
;
543 } else if (strncmp(name
, "user.", 5) == 0) {
544 attrnamespace
= EXTATTR_NAMESPACE_USER
;
552 /* Check attribute existence */
553 retval
= extattr_get_file(path
, attrnamespace
, attrname
, NULL
, 0);
555 /* REPLACE attribute, that doesn't exist */
556 if (flags
& XATTR_REPLACE
&& errno
== ENOATTR
) {
560 /* Ignore other errors */
563 /* CREATE attribute, that already exists */
564 if (flags
& XATTR_CREATE
) {
570 retval
= extattr_set_file(path
, attrnamespace
, attrname
, value
, size
);
571 return (retval
< 0) ? -1 : 0;
572 #elif defined(HAVE_ATTR_SET)
574 char *attrname
= strchr(name
,'.') + 1;
576 if (strncmp(name
, "system", 6) == 0) myflags
|= ATTR_ROOT
;
577 if (flags
& XATTR_CREATE
) myflags
|= ATTR_CREATE
;
578 if (flags
& XATTR_REPLACE
) myflags
|= ATTR_REPLACE
;
580 return attr_set(path
, attrname
, (const char *)value
, size
, myflags
);
581 #elif defined(HAVE_ATTROPEN)
583 int myflags
= O_RDWR
;
585 if (flags
& XATTR_CREATE
) myflags
|= O_EXCL
;
586 if (!(flags
& XATTR_REPLACE
)) myflags
|= O_CREAT
;
587 attrfd
= solaris_attropen(path
, name
, myflags
, (mode_t
) SOLARIS_ATTRMODE
);
589 ret
= solaris_write_xattr(attrfd
, value
, size
);
599 int rep_fsetxattr (int filedes
, const char *name
, const void *value
, size_t size
, int flags
)
601 #if defined(HAVE_FSETXATTR)
602 #ifndef XATTR_ADDITIONAL_OPTIONS
603 return fsetxattr(filedes
, name
, value
, size
, flags
);
605 /* So that we do not recursivly call this function */
608 return fsetxattr(filedes
, name
, value
, size
, 0, options
);
610 #elif defined(HAVE_FSETEA)
611 return fsetea(filedes
, name
, value
, size
, flags
);
612 #elif defined(HAVE_EXTATTR_SET_FD)
615 const char *attrname
;
617 if (strncmp(name
, "system.", 7) == 0) {
618 attrnamespace
= EXTATTR_NAMESPACE_SYSTEM
;
620 } else if (strncmp(name
, "user.", 5) == 0) {
621 attrnamespace
= EXTATTR_NAMESPACE_USER
;
629 /* Check attribute existence */
630 retval
= extattr_get_fd(filedes
, attrnamespace
, attrname
, NULL
, 0);
632 /* REPLACE attribute, that doesn't exist */
633 if (flags
& XATTR_REPLACE
&& errno
== ENOATTR
) {
637 /* Ignore other errors */
640 /* CREATE attribute, that already exists */
641 if (flags
& XATTR_CREATE
) {
647 retval
= extattr_set_fd(filedes
, attrnamespace
, attrname
, value
, size
);
648 return (retval
< 0) ? -1 : 0;
649 #elif defined(HAVE_ATTR_SETF)
651 char *attrname
= strchr(name
,'.') + 1;
653 if (strncmp(name
, "system", 6) == 0) myflags
|= ATTR_ROOT
;
654 if (flags
& XATTR_CREATE
) myflags
|= ATTR_CREATE
;
655 if (flags
& XATTR_REPLACE
) myflags
|= ATTR_REPLACE
;
657 return attr_setf(filedes
, attrname
, (const char *)value
, size
, myflags
);
658 #elif defined(HAVE_ATTROPEN)
660 int myflags
= O_RDWR
| O_XATTR
;
662 if (flags
& XATTR_CREATE
) myflags
|= O_EXCL
;
663 if (!(flags
& XATTR_REPLACE
)) myflags
|= O_CREAT
;
664 attrfd
= solaris_openat(filedes
, name
, myflags
, (mode_t
) SOLARIS_ATTRMODE
);
666 ret
= solaris_write_xattr(attrfd
, value
, size
);
676 /**************************************************************************
677 helper functions for Solaris' EA support
678 ****************************************************************************/
680 static ssize_t
solaris_read_xattr(int attrfd
, void *value
, size_t size
)
684 if (fstat(attrfd
, &sbuf
) == -1) {
689 /* This is to return the current size of the named extended attribute */
694 /* check size and read xattr */
695 if (sbuf
.st_size
> size
) {
700 return read(attrfd
, value
, sbuf
.st_size
);
703 static ssize_t
solaris_list_xattr(int attrdirfd
, char *list
, size_t size
)
708 int newfd
= dup(attrdirfd
);
709 /* CAUTION: The originating file descriptor should not be
710 used again following the call to fdopendir().
711 For that reason we dup() the file descriptor
712 here to make things more clear. */
713 dirp
= fdopendir(newfd
);
715 while ((de
= readdir(dirp
))) {
716 size_t listlen
= strlen(de
->d_name
) + 1;
717 if (!strcmp(de
->d_name
, ".") || !strcmp(de
->d_name
, "..")) {
718 /* we don't want "." and ".." here: */
723 /* return the current size of the list of extended attribute names*/
726 /* check size and copy entrieѕ + nul into list. */
727 if ((len
+ listlen
) > size
) {
732 strlcpy(list
+ len
, de
->d_name
, listlen
);
738 if (closedir(dirp
) == -1) {
744 static int solaris_unlinkat(int attrdirfd
, const char *name
)
746 if (unlinkat(attrdirfd
, name
, 0) == -1) {
747 if (errno
== ENOENT
) {
755 static int solaris_attropen(const char *path
, const char *attrpath
, int oflag
, mode_t mode
)
757 int filedes
= attropen(path
, attrpath
, oflag
, mode
);
759 if (errno
== EINVAL
) {
768 static int solaris_openat(int fildes
, const char *path
, int oflag
, mode_t mode
)
770 int filedes
= openat(fildes
, path
, oflag
, mode
);
772 if (errno
== EINVAL
) {
781 static int solaris_write_xattr(int attrfd
, const char *value
, size_t size
)
783 if ((ftruncate(attrfd
, 0) == 0) && (write(attrfd
, value
, size
) == size
)) {
789 #endif /*HAVE_ATTROPEN*/