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/>.
29 #include "system/filesys.h"
30 #include "system/dir.h"
32 /******** Solaris EA helper function prototypes ********/
34 #define SOLARIS_ATTRMODE S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP
35 static int solaris_write_xattr(int attrfd
, const char *value
, size_t size
);
36 static ssize_t
solaris_read_xattr(int attrfd
, void *value
, size_t size
);
37 static ssize_t
solaris_list_xattr(int attrdirfd
, char *list
, size_t size
);
38 static int solaris_unlinkat(int attrdirfd
, const char *name
);
39 static int solaris_attropen(const char *path
, const char *attrpath
, int oflag
, mode_t mode
);
40 static int solaris_openat(int fildes
, const char *path
, int oflag
, mode_t mode
);
43 /**************************************************************************
44 Wrappers for extented attribute calls. Based on the Linux package with
45 support for IRIX and (Net|Free)BSD also. Expand as other systems have them.
46 ****************************************************************************/
48 ssize_t
rep_getxattr (const char *path
, const char *name
, void *value
, size_t size
)
50 #if defined(HAVE_GETXATTR)
51 #ifndef XATTR_ADDITIONAL_OPTIONS
52 return getxattr(path
, name
, value
, size
);
55 /* So that we do not recursivly call this function */
58 return getxattr(path
, name
, value
, size
, 0, options
);
60 #elif defined(HAVE_GETEA)
61 return getea(path
, name
, value
, size
);
62 #elif defined(HAVE_EXTATTR_GET_FILE)
65 int attrnamespace
= (strncmp(name
, "system", 6) == 0) ?
66 EXTATTR_NAMESPACE_SYSTEM
: EXTATTR_NAMESPACE_USER
;
67 const char *attrname
= ((s
=strchr(name
, '.')) == NULL
) ? name
: s
+ 1;
69 * The BSD implementation has a nasty habit of silently truncating
70 * the returned value to the size of the buffer, so we have to check
71 * that the buffer is large enough to fit the returned value.
73 if((retval
=extattr_get_file(path
, attrnamespace
, attrname
, NULL
, 0)) >= 0) {
76 } else if (retval
> size
) {
80 if((retval
=extattr_get_file(path
, attrnamespace
, attrname
, value
, size
)) >= 0)
85 #elif defined(HAVE_ATTR_GET)
86 int retval
, flags
= 0;
87 int valuelength
= (int)size
;
88 char *attrname
= strchr(name
,'.') + 1;
90 if (strncmp(name
, "system", 6) == 0) flags
|= ATTR_ROOT
;
92 retval
= attr_get(path
, attrname
, (char *)value
, &valuelength
, flags
);
93 if (size
== 0 && retval
== -1 && errno
== E2BIG
) {
97 return retval
? retval
: valuelength
;
98 #elif defined(HAVE_ATTROPEN)
100 int attrfd
= solaris_attropen(path
, name
, O_RDONLY
, 0);
102 ret
= solaris_read_xattr(attrfd
, value
, size
);
112 ssize_t
rep_fgetxattr (int filedes
, const char *name
, void *value
, size_t size
)
114 #if defined(HAVE_FGETXATTR)
115 #ifndef XATTR_ADDITIONAL_OPTIONS
116 return fgetxattr(filedes
, name
, value
, size
);
119 /* So that we do not recursivly call this function */
122 return fgetxattr(filedes
, name
, value
, size
, 0, options
);
124 #elif defined(HAVE_FGETEA)
125 return fgetea(filedes
, name
, value
, size
);
126 #elif defined(HAVE_EXTATTR_GET_FD)
129 int attrnamespace
= (strncmp(name
, "system", 6) == 0) ?
130 EXTATTR_NAMESPACE_SYSTEM
: EXTATTR_NAMESPACE_USER
;
131 const char *attrname
= ((s
=strchr(name
, '.')) == NULL
) ? name
: s
+ 1;
133 if((retval
=extattr_get_fd(filedes
, attrnamespace
, attrname
, NULL
, 0)) >= 0) {
136 } else if (retval
> size
) {
140 if((retval
=extattr_get_fd(filedes
, attrnamespace
, attrname
, value
, size
)) >= 0)
145 #elif defined(HAVE_ATTR_GETF)
146 int retval
, flags
= 0;
147 int valuelength
= (int)size
;
148 char *attrname
= strchr(name
,'.') + 1;
150 if (strncmp(name
, "system", 6) == 0) flags
|= ATTR_ROOT
;
152 retval
= attr_getf(filedes
, attrname
, (char *)value
, &valuelength
, flags
);
153 if (size
== 0 && retval
== -1 && errno
== E2BIG
) {
156 return retval
? retval
: valuelength
;
157 #elif defined(HAVE_ATTROPEN)
159 int attrfd
= solaris_openat(filedes
, name
, O_RDONLY
|O_XATTR
, 0);
161 ret
= solaris_read_xattr(attrfd
, value
, size
);
171 #if defined(HAVE_EXTATTR_LIST_FILE)
173 #define EXTATTR_PREFIX(s) (s), (sizeof((s))-1)
181 { EXTATTR_NAMESPACE_SYSTEM
, EXTATTR_PREFIX("system.") },
182 { EXTATTR_NAMESPACE_USER
, EXTATTR_PREFIX("user.") },
190 static ssize_t
bsd_attr_list (int type
, extattr_arg arg
, char *list
, size_t size
)
192 ssize_t list_size
, total_size
= 0;
195 /* Iterate through extattr(2) namespaces */
196 for(t
= 0; t
< ARRAY_SIZE(extattr
); t
++) {
197 if (t
!= EXTATTR_NAMESPACE_USER
&& geteuid() != 0) {
198 /* ignore all but user namespace when we are not root, see bug 10247 */
202 #if defined(HAVE_EXTATTR_LIST_FILE)
204 list_size
= extattr_list_file(arg
.path
, extattr
[t
].space
, list
, size
);
207 #if defined(HAVE_EXTATTR_LIST_LINK)
209 list_size
= extattr_list_link(arg
.path
, extattr
[t
].space
, list
, size
);
212 #if defined(HAVE_EXTATTR_LIST_FD)
214 list_size
= extattr_list_fd(arg
.filedes
, extattr
[t
].space
, list
, size
);
221 /* Some error happend. Errno should be set by the previous call */
227 /* XXX: Call with an empty buffer may be used to calculate
228 necessary buffer size. Unfortunately, we can't say, how
229 many attributes were returned, so here is the potential
230 problem with the emulation.
233 /* Take the worse case of one char attribute names -
234 two bytes per name plus one more for sanity.
236 total_size
+= list_size
+ (list_size
/2 + 1)*extattr
[t
].len
;
239 /* Count necessary offset to fit namespace prefixes */
241 for(i
= 0; i
< list_size
; i
+= list
[i
] + 1)
242 len
+= extattr
[t
].len
;
244 total_size
+= list_size
+ len
;
245 /* Buffer is too small to fit the results */
246 if(total_size
> size
) {
250 /* Shift results back, so we can prepend prefixes */
251 buf
= (char *)memmove(list
+ len
, list
, list_size
);
253 for(i
= 0; i
< list_size
; i
+= len
+ 1) {
255 strncpy(list
, extattr
[t
].name
, extattr
[t
].len
+ 1);
256 list
+= extattr
[t
].len
;
257 strncpy(list
, buf
+ i
+ 1, len
);
268 #if defined(HAVE_ATTR_LIST) && (defined(HAVE_SYS_ATTRIBUTES_H) || defined(HAVE_ATTR_ATTRIBUTES_H))
269 static char attr_buffer
[ATTR_MAX_VALUELEN
];
271 static ssize_t
irix_attr_list(const char *path
, int filedes
, char *list
, size_t size
, int flags
)
273 int retval
= 0, index
;
274 attrlist_cursor_t
*cursor
= 0;
276 attrlist_t
* al
= (attrlist_t
*)attr_buffer
;
278 size_t ent_size
, left
= size
;
283 retval
= attr_listf(filedes
, attr_buffer
, ATTR_MAX_VALUELEN
, flags
, cursor
);
285 retval
= attr_list(path
, attr_buffer
, ATTR_MAX_VALUELEN
, flags
, cursor
);
287 for (index
= 0; index
< al
->al_count
; index
++) {
288 ae
= ATTR_ENTRY(attr_buffer
, index
);
289 ent_size
= strlen(ae
->a_name
) + sizeof("user.");
290 if (left
>= ent_size
) {
291 strncpy(bp
, "user.", sizeof("user."));
292 strncat(bp
, ae
->a_name
, ent_size
- sizeof("user."));
300 total_size
+= ent_size
;
302 if (al
->al_more
== 0) break;
309 retval
= attr_listf(filedes
, attr_buffer
, ATTR_MAX_VALUELEN
, flags
, cursor
);
311 retval
= attr_list(path
, attr_buffer
, ATTR_MAX_VALUELEN
, flags
, cursor
);
313 for (index
= 0; index
< al
->al_count
; index
++) {
314 ae
= ATTR_ENTRY(attr_buffer
, index
);
315 ent_size
= strlen(ae
->a_name
) + sizeof("system.");
316 if (left
>= ent_size
) {
317 strncpy(bp
, "system.", sizeof("system."));
318 strncat(bp
, ae
->a_name
, ent_size
- sizeof("system."));
326 total_size
+= ent_size
;
328 if (al
->al_more
== 0) break;
331 return (ssize_t
)(retval
? retval
: total_size
);
336 ssize_t
rep_listxattr (const char *path
, char *list
, size_t size
)
338 #if defined(HAVE_LISTXATTR)
339 #ifndef XATTR_ADDITIONAL_OPTIONS
340 return listxattr(path
, list
, size
);
342 /* So that we do not recursivly call this function */
345 return listxattr(path
, list
, size
, options
);
347 #elif defined(HAVE_LISTEA)
348 return listea(path
, list
, size
);
349 #elif defined(HAVE_EXTATTR_LIST_FILE)
352 return bsd_attr_list(0, arg
, list
, size
);
353 #elif defined(HAVE_ATTR_LIST) && defined(HAVE_SYS_ATTRIBUTES_H)
354 return irix_attr_list(path
, 0, list
, size
, 0);
355 #elif defined(HAVE_ATTROPEN)
357 int attrdirfd
= solaris_attropen(path
, ".", O_RDONLY
, 0);
358 if (attrdirfd
>= 0) {
359 ret
= solaris_list_xattr(attrdirfd
, list
, size
);
369 ssize_t
rep_flistxattr (int filedes
, char *list
, size_t size
)
371 #if defined(HAVE_FLISTXATTR)
372 #ifndef XATTR_ADDITIONAL_OPTIONS
373 return flistxattr(filedes
, list
, size
);
375 /* So that we do not recursivly call this function */
378 return flistxattr(filedes
, list
, size
, options
);
380 #elif defined(HAVE_FLISTEA)
381 return flistea(filedes
, list
, size
);
382 #elif defined(HAVE_EXTATTR_LIST_FD)
384 arg
.filedes
= filedes
;
385 return bsd_attr_list(2, arg
, list
, size
);
386 #elif defined(HAVE_ATTR_LISTF)
387 return irix_attr_list(NULL
, filedes
, list
, size
, 0);
388 #elif defined(HAVE_ATTROPEN)
390 int attrdirfd
= solaris_openat(filedes
, ".", O_RDONLY
|O_XATTR
, 0);
391 if (attrdirfd
>= 0) {
392 ret
= solaris_list_xattr(attrdirfd
, list
, size
);
402 int rep_removexattr (const char *path
, const char *name
)
404 #if defined(HAVE_REMOVEXATTR)
405 #ifndef XATTR_ADDITIONAL_OPTIONS
406 return removexattr(path
, name
);
408 /* So that we do not recursivly call this function */
411 return removexattr(path
, name
, options
);
413 #elif defined(HAVE_REMOVEEA)
414 return removeea(path
, name
);
415 #elif defined(HAVE_EXTATTR_DELETE_FILE)
417 int attrnamespace
= (strncmp(name
, "system", 6) == 0) ?
418 EXTATTR_NAMESPACE_SYSTEM
: EXTATTR_NAMESPACE_USER
;
419 const char *attrname
= ((s
=strchr(name
, '.')) == NULL
) ? name
: s
+ 1;
421 return extattr_delete_file(path
, attrnamespace
, attrname
);
422 #elif defined(HAVE_ATTR_REMOVE)
424 char *attrname
= strchr(name
,'.') + 1;
426 if (strncmp(name
, "system", 6) == 0) flags
|= ATTR_ROOT
;
428 return attr_remove(path
, attrname
, flags
);
429 #elif defined(HAVE_ATTROPEN)
431 int attrdirfd
= solaris_attropen(path
, ".", O_RDONLY
, 0);
432 if (attrdirfd
>= 0) {
433 ret
= solaris_unlinkat(attrdirfd
, name
);
443 int rep_fremovexattr (int filedes
, const char *name
)
445 #if defined(HAVE_FREMOVEXATTR)
446 #ifndef XATTR_ADDITIONAL_OPTIONS
447 return fremovexattr(filedes
, name
);
449 /* So that we do not recursivly call this function */
452 return fremovexattr(filedes
, name
, options
);
454 #elif defined(HAVE_FREMOVEEA)
455 return fremoveea(filedes
, name
);
456 #elif defined(HAVE_EXTATTR_DELETE_FD)
458 int attrnamespace
= (strncmp(name
, "system", 6) == 0) ?
459 EXTATTR_NAMESPACE_SYSTEM
: EXTATTR_NAMESPACE_USER
;
460 const char *attrname
= ((s
=strchr(name
, '.')) == NULL
) ? name
: s
+ 1;
462 return extattr_delete_fd(filedes
, attrnamespace
, attrname
);
463 #elif defined(HAVE_ATTR_REMOVEF)
465 char *attrname
= strchr(name
,'.') + 1;
467 if (strncmp(name
, "system", 6) == 0) flags
|= ATTR_ROOT
;
469 return attr_removef(filedes
, attrname
, flags
);
470 #elif defined(HAVE_ATTROPEN)
472 int attrdirfd
= solaris_openat(filedes
, ".", O_RDONLY
|O_XATTR
, 0);
473 if (attrdirfd
>= 0) {
474 ret
= solaris_unlinkat(attrdirfd
, name
);
484 int rep_setxattr (const char *path
, const char *name
, const void *value
, size_t size
, int flags
)
486 #if defined(HAVE_SETXATTR)
487 #ifndef XATTR_ADDITIONAL_OPTIONS
488 return setxattr(path
, name
, value
, size
, flags
);
490 /* So that we do not recursivly call this function */
493 return setxattr(path
, name
, value
, size
, 0, options
);
495 #elif defined(HAVE_SETEA)
496 return setea(path
, name
, value
, size
, flags
);
497 #elif defined(HAVE_EXTATTR_SET_FILE)
500 int attrnamespace
= (strncmp(name
, "system", 6) == 0) ?
501 EXTATTR_NAMESPACE_SYSTEM
: EXTATTR_NAMESPACE_USER
;
502 const char *attrname
= ((s
=strchr(name
, '.')) == NULL
) ? name
: s
+ 1;
504 /* Check attribute existence */
505 retval
= extattr_get_file(path
, attrnamespace
, attrname
, NULL
, 0);
507 /* REPLACE attribute, that doesn't exist */
508 if (flags
& XATTR_REPLACE
&& errno
== ENOATTR
) {
512 /* Ignore other errors */
515 /* CREATE attribute, that already exists */
516 if (flags
& XATTR_CREATE
) {
522 retval
= extattr_set_file(path
, attrnamespace
, attrname
, value
, size
);
523 return (retval
< 0) ? -1 : 0;
524 #elif defined(HAVE_ATTR_SET)
526 char *attrname
= strchr(name
,'.') + 1;
528 if (strncmp(name
, "system", 6) == 0) myflags
|= ATTR_ROOT
;
529 if (flags
& XATTR_CREATE
) myflags
|= ATTR_CREATE
;
530 if (flags
& XATTR_REPLACE
) myflags
|= ATTR_REPLACE
;
532 return attr_set(path
, attrname
, (const char *)value
, size
, myflags
);
533 #elif defined(HAVE_ATTROPEN)
535 int myflags
= O_RDWR
;
537 if (flags
& XATTR_CREATE
) myflags
|= O_EXCL
;
538 if (!(flags
& XATTR_REPLACE
)) myflags
|= O_CREAT
;
539 attrfd
= solaris_attropen(path
, name
, myflags
, (mode_t
) SOLARIS_ATTRMODE
);
541 ret
= solaris_write_xattr(attrfd
, value
, size
);
551 int rep_fsetxattr (int filedes
, const char *name
, const void *value
, size_t size
, int flags
)
553 #if defined(HAVE_FSETXATTR)
554 #ifndef XATTR_ADDITIONAL_OPTIONS
555 return fsetxattr(filedes
, name
, value
, size
, flags
);
557 /* So that we do not recursivly call this function */
560 return fsetxattr(filedes
, name
, value
, size
, 0, options
);
562 #elif defined(HAVE_FSETEA)
563 return fsetea(filedes
, name
, value
, size
, flags
);
564 #elif defined(HAVE_EXTATTR_SET_FD)
567 int attrnamespace
= (strncmp(name
, "system", 6) == 0) ?
568 EXTATTR_NAMESPACE_SYSTEM
: EXTATTR_NAMESPACE_USER
;
569 const char *attrname
= ((s
=strchr(name
, '.')) == NULL
) ? name
: s
+ 1;
571 /* Check attribute existence */
572 retval
= extattr_get_fd(filedes
, attrnamespace
, attrname
, NULL
, 0);
574 /* REPLACE attribute, that doesn't exist */
575 if (flags
& XATTR_REPLACE
&& errno
== ENOATTR
) {
579 /* Ignore other errors */
582 /* CREATE attribute, that already exists */
583 if (flags
& XATTR_CREATE
) {
589 retval
= extattr_set_fd(filedes
, attrnamespace
, attrname
, value
, size
);
590 return (retval
< 0) ? -1 : 0;
591 #elif defined(HAVE_ATTR_SETF)
593 char *attrname
= strchr(name
,'.') + 1;
595 if (strncmp(name
, "system", 6) == 0) myflags
|= ATTR_ROOT
;
596 if (flags
& XATTR_CREATE
) myflags
|= ATTR_CREATE
;
597 if (flags
& XATTR_REPLACE
) myflags
|= ATTR_REPLACE
;
599 return attr_setf(filedes
, attrname
, (const char *)value
, size
, myflags
);
600 #elif defined(HAVE_ATTROPEN)
602 int myflags
= O_RDWR
| O_XATTR
;
604 if (flags
& XATTR_CREATE
) myflags
|= O_EXCL
;
605 if (!(flags
& XATTR_REPLACE
)) myflags
|= O_CREAT
;
606 attrfd
= solaris_openat(filedes
, name
, myflags
, (mode_t
) SOLARIS_ATTRMODE
);
608 ret
= solaris_write_xattr(attrfd
, value
, size
);
618 /**************************************************************************
619 helper functions for Solaris' EA support
620 ****************************************************************************/
622 static ssize_t
solaris_read_xattr(int attrfd
, void *value
, size_t size
)
626 if (fstat(attrfd
, &sbuf
) == -1) {
631 /* This is to return the current size of the named extended attribute */
636 /* check size and read xattr */
637 if (sbuf
.st_size
> size
) {
642 return read(attrfd
, value
, sbuf
.st_size
);
645 static ssize_t
solaris_list_xattr(int attrdirfd
, char *list
, size_t size
)
650 int newfd
= dup(attrdirfd
);
651 /* CAUTION: The originating file descriptor should not be
652 used again following the call to fdopendir().
653 For that reason we dup() the file descriptor
654 here to make things more clear. */
655 dirp
= fdopendir(newfd
);
657 while ((de
= readdir(dirp
))) {
658 size_t listlen
= strlen(de
->d_name
) + 1;
659 if (!strcmp(de
->d_name
, ".") || !strcmp(de
->d_name
, "..")) {
660 /* we don't want "." and ".." here: */
665 /* return the current size of the list of extended attribute names*/
668 /* check size and copy entrieѕ + nul into list. */
669 if ((len
+ listlen
) > size
) {
674 strlcpy(list
+ len
, de
->d_name
, listlen
);
680 if (closedir(dirp
) == -1) {
686 static int solaris_unlinkat(int attrdirfd
, const char *name
)
688 if (unlinkat(attrdirfd
, name
, 0) == -1) {
689 if (errno
== ENOENT
) {
697 static int solaris_attropen(const char *path
, const char *attrpath
, int oflag
, mode_t mode
)
699 int filedes
= attropen(path
, attrpath
, oflag
, mode
);
701 if (errno
== EINVAL
) {
710 static int solaris_openat(int fildes
, const char *path
, int oflag
, mode_t mode
)
712 int filedes
= openat(fildes
, path
, oflag
, mode
);
714 if (errno
== EINVAL
) {
723 static int solaris_write_xattr(int attrfd
, const char *value
, size_t size
)
725 if ((ftruncate(attrfd
, 0) == 0) && (write(attrfd
, value
, size
) == size
)) {
731 #endif /*HAVE_ATTROPEN*/