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)
66 int attrnamespace
= (strncmp(name
, "system", 6) == 0) ?
67 EXTATTR_NAMESPACE_SYSTEM
: EXTATTR_NAMESPACE_USER
;
68 const char *attrname
= ((s
=strchr(name
, '.')) == NULL
) ? name
: s
+ 1;
70 * The BSD implementation has a nasty habit of silently truncating
71 * the returned value to the size of the buffer, so we have to check
72 * that the buffer is large enough to fit the returned value.
74 if((retval
=extattr_get_file(path
, attrnamespace
, attrname
, NULL
, 0)) >= 0) {
77 } else if (retval
> size
) {
81 if((retval
=extattr_get_file(path
, attrnamespace
, attrname
, value
, size
)) >= 0)
86 #elif defined(HAVE_ATTR_GET)
87 int retval
, flags
= 0;
88 int valuelength
= (int)size
;
89 char *attrname
= strchr(name
,'.') + 1;
91 if (strncmp(name
, "system", 6) == 0) flags
|= ATTR_ROOT
;
93 retval
= attr_get(path
, attrname
, (char *)value
, &valuelength
, flags
);
94 if (size
== 0 && retval
== -1 && errno
== E2BIG
) {
98 return retval
? retval
: valuelength
;
99 #elif defined(HAVE_ATTROPEN)
101 int attrfd
= solaris_attropen(path
, name
, O_RDONLY
, 0);
103 ret
= solaris_read_xattr(attrfd
, value
, size
);
113 ssize_t
rep_fgetxattr (int filedes
, const char *name
, void *value
, size_t size
)
115 #if defined(HAVE_FGETXATTR)
116 #ifndef XATTR_ADDITIONAL_OPTIONS
117 return fgetxattr(filedes
, name
, value
, size
);
120 /* So that we do not recursivly call this function */
123 return fgetxattr(filedes
, name
, value
, size
, 0, options
);
125 #elif defined(HAVE_FGETEA)
126 return fgetea(filedes
, name
, value
, size
);
127 #elif defined(HAVE_EXTATTR_GET_FD)
130 int attrnamespace
= (strncmp(name
, "system", 6) == 0) ?
131 EXTATTR_NAMESPACE_SYSTEM
: EXTATTR_NAMESPACE_USER
;
132 const char *attrname
= ((s
=strchr(name
, '.')) == NULL
) ? name
: s
+ 1;
134 if((retval
=extattr_get_fd(filedes
, attrnamespace
, attrname
, NULL
, 0)) >= 0) {
137 } else if (retval
> size
) {
141 if((retval
=extattr_get_fd(filedes
, attrnamespace
, attrname
, value
, size
)) >= 0)
146 #elif defined(HAVE_ATTR_GETF)
147 int retval
, flags
= 0;
148 int valuelength
= (int)size
;
149 char *attrname
= strchr(name
,'.') + 1;
151 if (strncmp(name
, "system", 6) == 0) flags
|= ATTR_ROOT
;
153 retval
= attr_getf(filedes
, attrname
, (char *)value
, &valuelength
, flags
);
154 if (size
== 0 && retval
== -1 && errno
== E2BIG
) {
157 return retval
? retval
: valuelength
;
158 #elif defined(HAVE_ATTROPEN)
160 int attrfd
= solaris_openat(filedes
, name
, O_RDONLY
|O_XATTR
, 0);
162 ret
= solaris_read_xattr(attrfd
, value
, size
);
172 #if defined(HAVE_EXTATTR_LIST_FILE)
174 #define EXTATTR_PREFIX(s) (s), (sizeof((s))-1)
182 { EXTATTR_NAMESPACE_SYSTEM
, EXTATTR_PREFIX("system.") },
183 { EXTATTR_NAMESPACE_USER
, EXTATTR_PREFIX("user.") },
191 static ssize_t
bsd_attr_list (int type
, extattr_arg arg
, char *list
, size_t size
)
193 ssize_t list_size
, total_size
= 0;
196 /* Iterate through extattr(2) namespaces */
197 for(t
= 0; t
< ARRAY_SIZE(extattr
); t
++) {
198 if (t
!= EXTATTR_NAMESPACE_USER
&& geteuid() != 0) {
199 /* ignore all but user namespace when we are not root, see bug 10247 */
203 #if defined(HAVE_EXTATTR_LIST_FILE)
205 list_size
= extattr_list_file(arg
.path
, extattr
[t
].space
, list
, size
);
208 #if defined(HAVE_EXTATTR_LIST_LINK)
210 list_size
= extattr_list_link(arg
.path
, extattr
[t
].space
, list
, size
);
213 #if defined(HAVE_EXTATTR_LIST_FD)
215 list_size
= extattr_list_fd(arg
.filedes
, extattr
[t
].space
, list
, size
);
222 /* Some error happend. Errno should be set by the previous call */
228 /* XXX: Call with an empty buffer may be used to calculate
229 necessary buffer size. Unfortunately, we can't say, how
230 many attributes were returned, so here is the potential
231 problem with the emulation.
234 /* Take the worse case of one char attribute names -
235 two bytes per name plus one more for sanity.
237 total_size
+= list_size
+ (list_size
/2 + 1)*extattr
[t
].len
;
240 /* Count necessary offset to fit namespace prefixes */
242 for(i
= 0; i
< list_size
; i
+= list
[i
] + 1)
243 len
+= extattr
[t
].len
;
245 total_size
+= list_size
+ len
;
246 /* Buffer is too small to fit the results */
247 if(total_size
> size
) {
251 /* Shift results back, so we can prepend prefixes */
252 buf
= (char *)memmove(list
+ len
, list
, list_size
);
254 for(i
= 0; i
< list_size
; i
+= len
+ 1) {
256 strncpy(list
, extattr
[t
].name
, extattr
[t
].len
+ 1);
257 list
+= extattr
[t
].len
;
258 strncpy(list
, buf
+ i
+ 1, len
);
269 #if defined(HAVE_ATTR_LIST) && (defined(HAVE_SYS_ATTRIBUTES_H) || defined(HAVE_ATTR_ATTRIBUTES_H))
270 static char attr_buffer
[ATTR_MAX_VALUELEN
];
272 static ssize_t
irix_attr_list(const char *path
, int filedes
, char *list
, size_t size
, int flags
)
274 int retval
= 0, index
;
275 attrlist_cursor_t
*cursor
= 0;
277 attrlist_t
* al
= (attrlist_t
*)attr_buffer
;
279 size_t ent_size
, left
= size
;
284 retval
= attr_listf(filedes
, attr_buffer
, ATTR_MAX_VALUELEN
, flags
, cursor
);
286 retval
= attr_list(path
, attr_buffer
, ATTR_MAX_VALUELEN
, flags
, cursor
);
288 for (index
= 0; index
< al
->al_count
; index
++) {
289 ae
= ATTR_ENTRY(attr_buffer
, index
);
290 ent_size
= strlen(ae
->a_name
) + sizeof("user.");
291 if (left
>= ent_size
) {
292 strncpy(bp
, "user.", sizeof("user."));
293 strncat(bp
, ae
->a_name
, ent_size
- sizeof("user."));
301 total_size
+= ent_size
;
303 if (al
->al_more
== 0) break;
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("system.");
317 if (left
>= ent_size
) {
318 strncpy(bp
, "system.", sizeof("system."));
319 strncat(bp
, ae
->a_name
, ent_size
- sizeof("system."));
327 total_size
+= ent_size
;
329 if (al
->al_more
== 0) break;
332 return (ssize_t
)(retval
? retval
: total_size
);
337 ssize_t
rep_listxattr (const char *path
, char *list
, size_t size
)
339 #if defined(HAVE_LISTXATTR)
340 #ifndef XATTR_ADDITIONAL_OPTIONS
341 return listxattr(path
, list
, size
);
343 /* So that we do not recursivly call this function */
346 return listxattr(path
, list
, size
, options
);
348 #elif defined(HAVE_LISTEA)
349 return listea(path
, list
, size
);
350 #elif defined(HAVE_EXTATTR_LIST_FILE)
353 return bsd_attr_list(0, arg
, list
, size
);
354 #elif defined(HAVE_ATTR_LIST) && defined(HAVE_SYS_ATTRIBUTES_H)
355 return irix_attr_list(path
, 0, list
, size
, 0);
356 #elif defined(HAVE_ATTROPEN)
358 int attrdirfd
= solaris_attropen(path
, ".", O_RDONLY
, 0);
359 if (attrdirfd
>= 0) {
360 ret
= solaris_list_xattr(attrdirfd
, list
, size
);
370 ssize_t
rep_flistxattr (int filedes
, char *list
, size_t size
)
372 #if defined(HAVE_FLISTXATTR)
373 #ifndef XATTR_ADDITIONAL_OPTIONS
374 return flistxattr(filedes
, list
, size
);
376 /* So that we do not recursivly call this function */
379 return flistxattr(filedes
, list
, size
, options
);
381 #elif defined(HAVE_FLISTEA)
382 return flistea(filedes
, list
, size
);
383 #elif defined(HAVE_EXTATTR_LIST_FD)
385 arg
.filedes
= filedes
;
386 return bsd_attr_list(2, arg
, list
, size
);
387 #elif defined(HAVE_ATTR_LISTF)
388 return irix_attr_list(NULL
, filedes
, list
, size
, 0);
389 #elif defined(HAVE_ATTROPEN)
391 int attrdirfd
= solaris_openat(filedes
, ".", O_RDONLY
|O_XATTR
, 0);
392 if (attrdirfd
>= 0) {
393 ret
= solaris_list_xattr(attrdirfd
, list
, size
);
403 int rep_removexattr (const char *path
, const char *name
)
405 #if defined(HAVE_REMOVEXATTR)
406 #ifndef XATTR_ADDITIONAL_OPTIONS
407 return removexattr(path
, name
);
409 /* So that we do not recursivly call this function */
412 return removexattr(path
, name
, options
);
414 #elif defined(HAVE_REMOVEEA)
415 return removeea(path
, name
);
416 #elif defined(HAVE_EXTATTR_DELETE_FILE)
418 int attrnamespace
= (strncmp(name
, "system", 6) == 0) ?
419 EXTATTR_NAMESPACE_SYSTEM
: EXTATTR_NAMESPACE_USER
;
420 const char *attrname
= ((s
=strchr(name
, '.')) == NULL
) ? name
: s
+ 1;
422 return extattr_delete_file(path
, attrnamespace
, attrname
);
423 #elif defined(HAVE_ATTR_REMOVE)
425 char *attrname
= strchr(name
,'.') + 1;
427 if (strncmp(name
, "system", 6) == 0) flags
|= ATTR_ROOT
;
429 return attr_remove(path
, attrname
, flags
);
430 #elif defined(HAVE_ATTROPEN)
432 int attrdirfd
= solaris_attropen(path
, ".", O_RDONLY
, 0);
433 if (attrdirfd
>= 0) {
434 ret
= solaris_unlinkat(attrdirfd
, name
);
444 int rep_fremovexattr (int filedes
, const char *name
)
446 #if defined(HAVE_FREMOVEXATTR)
447 #ifndef XATTR_ADDITIONAL_OPTIONS
448 return fremovexattr(filedes
, name
);
450 /* So that we do not recursivly call this function */
453 return fremovexattr(filedes
, name
, options
);
455 #elif defined(HAVE_FREMOVEEA)
456 return fremoveea(filedes
, name
);
457 #elif defined(HAVE_EXTATTR_DELETE_FD)
459 int attrnamespace
= (strncmp(name
, "system", 6) == 0) ?
460 EXTATTR_NAMESPACE_SYSTEM
: EXTATTR_NAMESPACE_USER
;
461 const char *attrname
= ((s
=strchr(name
, '.')) == NULL
) ? name
: s
+ 1;
463 return extattr_delete_fd(filedes
, attrnamespace
, attrname
);
464 #elif defined(HAVE_ATTR_REMOVEF)
466 char *attrname
= strchr(name
,'.') + 1;
468 if (strncmp(name
, "system", 6) == 0) flags
|= ATTR_ROOT
;
470 return attr_removef(filedes
, attrname
, flags
);
471 #elif defined(HAVE_ATTROPEN)
473 int attrdirfd
= solaris_openat(filedes
, ".", O_RDONLY
|O_XATTR
, 0);
474 if (attrdirfd
>= 0) {
475 ret
= solaris_unlinkat(attrdirfd
, name
);
485 int rep_setxattr (const char *path
, const char *name
, const void *value
, size_t size
, int flags
)
487 #if defined(HAVE_SETXATTR)
488 #ifndef XATTR_ADDITIONAL_OPTIONS
489 return setxattr(path
, name
, value
, size
, flags
);
491 /* So that we do not recursivly call this function */
494 return setxattr(path
, name
, value
, size
, 0, options
);
496 #elif defined(HAVE_SETEA)
497 return setea(path
, name
, value
, size
, flags
);
498 #elif defined(HAVE_EXTATTR_SET_FILE)
501 int attrnamespace
= (strncmp(name
, "system", 6) == 0) ?
502 EXTATTR_NAMESPACE_SYSTEM
: EXTATTR_NAMESPACE_USER
;
503 const char *attrname
= ((s
=strchr(name
, '.')) == NULL
) ? name
: s
+ 1;
505 /* Check attribute existence */
506 retval
= extattr_get_file(path
, attrnamespace
, attrname
, NULL
, 0);
508 /* REPLACE attribute, that doesn't exist */
509 if (flags
& XATTR_REPLACE
&& errno
== ENOATTR
) {
513 /* Ignore other errors */
516 /* CREATE attribute, that already exists */
517 if (flags
& XATTR_CREATE
) {
523 retval
= extattr_set_file(path
, attrnamespace
, attrname
, value
, size
);
524 return (retval
< 0) ? -1 : 0;
525 #elif defined(HAVE_ATTR_SET)
527 char *attrname
= strchr(name
,'.') + 1;
529 if (strncmp(name
, "system", 6) == 0) myflags
|= ATTR_ROOT
;
530 if (flags
& XATTR_CREATE
) myflags
|= ATTR_CREATE
;
531 if (flags
& XATTR_REPLACE
) myflags
|= ATTR_REPLACE
;
533 return attr_set(path
, attrname
, (const char *)value
, size
, myflags
);
534 #elif defined(HAVE_ATTROPEN)
536 int myflags
= O_RDWR
;
538 if (flags
& XATTR_CREATE
) myflags
|= O_EXCL
;
539 if (!(flags
& XATTR_REPLACE
)) myflags
|= O_CREAT
;
540 attrfd
= solaris_attropen(path
, name
, myflags
, (mode_t
) SOLARIS_ATTRMODE
);
542 ret
= solaris_write_xattr(attrfd
, value
, size
);
552 int rep_fsetxattr (int filedes
, const char *name
, const void *value
, size_t size
, int flags
)
554 #if defined(HAVE_FSETXATTR)
555 #ifndef XATTR_ADDITIONAL_OPTIONS
556 return fsetxattr(filedes
, name
, value
, size
, flags
);
558 /* So that we do not recursivly call this function */
561 return fsetxattr(filedes
, name
, value
, size
, 0, options
);
563 #elif defined(HAVE_FSETEA)
564 return fsetea(filedes
, name
, value
, size
, flags
);
565 #elif defined(HAVE_EXTATTR_SET_FD)
568 int attrnamespace
= (strncmp(name
, "system", 6) == 0) ?
569 EXTATTR_NAMESPACE_SYSTEM
: EXTATTR_NAMESPACE_USER
;
570 const char *attrname
= ((s
=strchr(name
, '.')) == NULL
) ? name
: s
+ 1;
572 /* Check attribute existence */
573 retval
= extattr_get_fd(filedes
, attrnamespace
, attrname
, NULL
, 0);
575 /* REPLACE attribute, that doesn't exist */
576 if (flags
& XATTR_REPLACE
&& errno
== ENOATTR
) {
580 /* Ignore other errors */
583 /* CREATE attribute, that already exists */
584 if (flags
& XATTR_CREATE
) {
590 retval
= extattr_set_fd(filedes
, attrnamespace
, attrname
, value
, size
);
591 return (retval
< 0) ? -1 : 0;
592 #elif defined(HAVE_ATTR_SETF)
594 char *attrname
= strchr(name
,'.') + 1;
596 if (strncmp(name
, "system", 6) == 0) myflags
|= ATTR_ROOT
;
597 if (flags
& XATTR_CREATE
) myflags
|= ATTR_CREATE
;
598 if (flags
& XATTR_REPLACE
) myflags
|= ATTR_REPLACE
;
600 return attr_setf(filedes
, attrname
, (const char *)value
, size
, myflags
);
601 #elif defined(HAVE_ATTROPEN)
603 int myflags
= O_RDWR
| O_XATTR
;
605 if (flags
& XATTR_CREATE
) myflags
|= O_EXCL
;
606 if (!(flags
& XATTR_REPLACE
)) myflags
|= O_CREAT
;
607 attrfd
= solaris_openat(filedes
, name
, myflags
, (mode_t
) SOLARIS_ATTRMODE
);
609 ret
= solaris_write_xattr(attrfd
, value
, size
);
619 /**************************************************************************
620 helper functions for Solaris' EA support
621 ****************************************************************************/
623 static ssize_t
solaris_read_xattr(int attrfd
, void *value
, size_t size
)
627 if (fstat(attrfd
, &sbuf
) == -1) {
632 /* This is to return the current size of the named extended attribute */
637 /* check size and read xattr */
638 if (sbuf
.st_size
> size
) {
643 return read(attrfd
, value
, sbuf
.st_size
);
646 static ssize_t
solaris_list_xattr(int attrdirfd
, char *list
, size_t size
)
651 int newfd
= dup(attrdirfd
);
652 /* CAUTION: The originating file descriptor should not be
653 used again following the call to fdopendir().
654 For that reason we dup() the file descriptor
655 here to make things more clear. */
656 dirp
= fdopendir(newfd
);
658 while ((de
= readdir(dirp
))) {
659 size_t listlen
= strlen(de
->d_name
) + 1;
660 if (!strcmp(de
->d_name
, ".") || !strcmp(de
->d_name
, "..")) {
661 /* we don't want "." and ".." here: */
666 /* return the current size of the list of extended attribute names*/
669 /* check size and copy entrieѕ + nul into list. */
670 if ((len
+ listlen
) > size
) {
675 strlcpy(list
+ len
, de
->d_name
, listlen
);
681 if (closedir(dirp
) == -1) {
687 static int solaris_unlinkat(int attrdirfd
, const char *name
)
689 if (unlinkat(attrdirfd
, name
, 0) == -1) {
690 if (errno
== ENOENT
) {
698 static int solaris_attropen(const char *path
, const char *attrpath
, int oflag
, mode_t mode
)
700 int filedes
= attropen(path
, attrpath
, oflag
, mode
);
702 if (errno
== EINVAL
) {
711 static int solaris_openat(int fildes
, const char *path
, int oflag
, mode_t mode
)
713 int filedes
= openat(fildes
, path
, oflag
, mode
);
715 if (errno
== EINVAL
) {
724 static int solaris_write_xattr(int attrfd
, const char *value
, size_t size
)
726 if ((ftruncate(attrfd
, 0) == 0) && (write(attrfd
, value
, size
) == size
)) {
732 #endif /*HAVE_ATTROPEN*/