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
++) {
198 #if defined(HAVE_EXTATTR_LIST_FILE)
200 list_size
= extattr_list_file(arg
.path
, extattr
[t
].space
, list
, size
);
203 #if defined(HAVE_EXTATTR_LIST_LINK)
205 list_size
= extattr_list_link(arg
.path
, extattr
[t
].space
, list
, size
);
208 #if defined(HAVE_EXTATTR_LIST_FD)
210 list_size
= extattr_list_fd(arg
.filedes
, extattr
[t
].space
, list
, size
);
217 /* Some error happend. Errno should be set by the previous call */
223 /* XXX: Call with an empty buffer may be used to calculate
224 necessary buffer size. Unfortunately, we can't say, how
225 many attributes were returned, so here is the potential
226 problem with the emulation.
229 /* Take the worse case of one char attribute names -
230 two bytes per name plus one more for sanity.
232 total_size
+= list_size
+ (list_size
/2 + 1)*extattr
[t
].len
;
235 /* Count necessary offset to fit namespace prefixes */
237 for(i
= 0; i
< list_size
; i
+= list
[i
] + 1)
238 len
+= extattr
[t
].len
;
240 total_size
+= list_size
+ len
;
241 /* Buffer is too small to fit the results */
242 if(total_size
> size
) {
246 /* Shift results back, so we can prepend prefixes */
247 buf
= (char *)memmove(list
+ len
, list
, list_size
);
249 for(i
= 0; i
< list_size
; i
+= len
+ 1) {
251 strncpy(list
, extattr
[t
].name
, extattr
[t
].len
+ 1);
252 list
+= extattr
[t
].len
;
253 strncpy(list
, buf
+ i
+ 1, len
);
264 #if defined(HAVE_ATTR_LIST) && (defined(HAVE_SYS_ATTRIBUTES_H) || defined(HAVE_ATTR_ATTRIBUTES_H))
265 static char attr_buffer
[ATTR_MAX_VALUELEN
];
267 static ssize_t
irix_attr_list(const char *path
, int filedes
, char *list
, size_t size
, int flags
)
269 int retval
= 0, index
;
270 attrlist_cursor_t
*cursor
= 0;
272 attrlist_t
* al
= (attrlist_t
*)attr_buffer
;
274 size_t ent_size
, left
= size
;
279 retval
= attr_listf(filedes
, attr_buffer
, ATTR_MAX_VALUELEN
, flags
, cursor
);
281 retval
= attr_list(path
, attr_buffer
, ATTR_MAX_VALUELEN
, flags
, cursor
);
283 for (index
= 0; index
< al
->al_count
; index
++) {
284 ae
= ATTR_ENTRY(attr_buffer
, index
);
285 ent_size
= strlen(ae
->a_name
) + sizeof("user.");
286 if (left
>= ent_size
) {
287 strncpy(bp
, "user.", sizeof("user."));
288 strncat(bp
, ae
->a_name
, ent_size
- sizeof("user."));
296 total_size
+= ent_size
;
298 if (al
->al_more
== 0) break;
305 retval
= attr_listf(filedes
, attr_buffer
, ATTR_MAX_VALUELEN
, flags
, cursor
);
307 retval
= attr_list(path
, attr_buffer
, ATTR_MAX_VALUELEN
, flags
, cursor
);
309 for (index
= 0; index
< al
->al_count
; index
++) {
310 ae
= ATTR_ENTRY(attr_buffer
, index
);
311 ent_size
= strlen(ae
->a_name
) + sizeof("system.");
312 if (left
>= ent_size
) {
313 strncpy(bp
, "system.", sizeof("system."));
314 strncat(bp
, ae
->a_name
, ent_size
- sizeof("system."));
322 total_size
+= ent_size
;
324 if (al
->al_more
== 0) break;
327 return (ssize_t
)(retval
? retval
: total_size
);
332 ssize_t
rep_listxattr (const char *path
, char *list
, size_t size
)
334 #if defined(HAVE_LISTXATTR)
335 #ifndef XATTR_ADDITIONAL_OPTIONS
336 return listxattr(path
, list
, size
);
338 /* So that we do not recursivly call this function */
341 return listxattr(path
, list
, size
, options
);
343 #elif defined(HAVE_LISTEA)
344 return listea(path
, list
, size
);
345 #elif defined(HAVE_EXTATTR_LIST_FILE)
348 return bsd_attr_list(0, arg
, list
, size
);
349 #elif defined(HAVE_ATTR_LIST) && defined(HAVE_SYS_ATTRIBUTES_H)
350 return irix_attr_list(path
, 0, list
, size
, 0);
351 #elif defined(HAVE_ATTROPEN)
353 int attrdirfd
= solaris_attropen(path
, ".", O_RDONLY
, 0);
354 if (attrdirfd
>= 0) {
355 ret
= solaris_list_xattr(attrdirfd
, list
, size
);
365 ssize_t
rep_flistxattr (int filedes
, char *list
, size_t size
)
367 #if defined(HAVE_FLISTXATTR)
368 #ifndef XATTR_ADDITIONAL_OPTIONS
369 return flistxattr(filedes
, list
, size
);
371 /* So that we do not recursivly call this function */
374 return flistxattr(filedes
, list
, size
, options
);
376 #elif defined(HAVE_FLISTEA)
377 return flistea(filedes
, list
, size
);
378 #elif defined(HAVE_EXTATTR_LIST_FD)
380 arg
.filedes
= filedes
;
381 return bsd_attr_list(2, arg
, list
, size
);
382 #elif defined(HAVE_ATTR_LISTF)
383 return irix_attr_list(NULL
, filedes
, list
, size
, 0);
384 #elif defined(HAVE_ATTROPEN)
386 int attrdirfd
= solaris_openat(filedes
, ".", O_RDONLY
|O_XATTR
, 0);
387 if (attrdirfd
>= 0) {
388 ret
= solaris_list_xattr(attrdirfd
, list
, size
);
398 int rep_removexattr (const char *path
, const char *name
)
400 #if defined(HAVE_REMOVEXATTR)
401 #ifndef XATTR_ADDITIONAL_OPTIONS
402 return removexattr(path
, name
);
404 /* So that we do not recursivly call this function */
407 return removexattr(path
, name
, options
);
409 #elif defined(HAVE_REMOVEEA)
410 return removeea(path
, name
);
411 #elif defined(HAVE_EXTATTR_DELETE_FILE)
413 int attrnamespace
= (strncmp(name
, "system", 6) == 0) ?
414 EXTATTR_NAMESPACE_SYSTEM
: EXTATTR_NAMESPACE_USER
;
415 const char *attrname
= ((s
=strchr(name
, '.')) == NULL
) ? name
: s
+ 1;
417 return extattr_delete_file(path
, attrnamespace
, attrname
);
418 #elif defined(HAVE_ATTR_REMOVE)
420 char *attrname
= strchr(name
,'.') + 1;
422 if (strncmp(name
, "system", 6) == 0) flags
|= ATTR_ROOT
;
424 return attr_remove(path
, attrname
, flags
);
425 #elif defined(HAVE_ATTROPEN)
427 int attrdirfd
= solaris_attropen(path
, ".", O_RDONLY
, 0);
428 if (attrdirfd
>= 0) {
429 ret
= solaris_unlinkat(attrdirfd
, name
);
439 int rep_fremovexattr (int filedes
, const char *name
)
441 #if defined(HAVE_FREMOVEXATTR)
442 #ifndef XATTR_ADDITIONAL_OPTIONS
443 return fremovexattr(filedes
, name
);
445 /* So that we do not recursivly call this function */
448 return fremovexattr(filedes
, name
, options
);
450 #elif defined(HAVE_FREMOVEEA)
451 return fremoveea(filedes
, name
);
452 #elif defined(HAVE_EXTATTR_DELETE_FD)
454 int attrnamespace
= (strncmp(name
, "system", 6) == 0) ?
455 EXTATTR_NAMESPACE_SYSTEM
: EXTATTR_NAMESPACE_USER
;
456 const char *attrname
= ((s
=strchr(name
, '.')) == NULL
) ? name
: s
+ 1;
458 return extattr_delete_fd(filedes
, attrnamespace
, attrname
);
459 #elif defined(HAVE_ATTR_REMOVEF)
461 char *attrname
= strchr(name
,'.') + 1;
463 if (strncmp(name
, "system", 6) == 0) flags
|= ATTR_ROOT
;
465 return attr_removef(filedes
, attrname
, flags
);
466 #elif defined(HAVE_ATTROPEN)
468 int attrdirfd
= solaris_openat(filedes
, ".", O_RDONLY
|O_XATTR
, 0);
469 if (attrdirfd
>= 0) {
470 ret
= solaris_unlinkat(attrdirfd
, name
);
480 int rep_setxattr (const char *path
, const char *name
, const void *value
, size_t size
, int flags
)
482 #if defined(HAVE_SETXATTR)
483 #ifndef XATTR_ADDITIONAL_OPTIONS
484 return setxattr(path
, name
, value
, size
, flags
);
486 /* So that we do not recursivly call this function */
489 return setxattr(path
, name
, value
, size
, 0, options
);
491 #elif defined(HAVE_SETEA)
492 return setea(path
, name
, value
, size
, flags
);
493 #elif defined(HAVE_EXTATTR_SET_FILE)
496 int attrnamespace
= (strncmp(name
, "system", 6) == 0) ?
497 EXTATTR_NAMESPACE_SYSTEM
: EXTATTR_NAMESPACE_USER
;
498 const char *attrname
= ((s
=strchr(name
, '.')) == NULL
) ? name
: s
+ 1;
500 /* Check attribute existence */
501 retval
= extattr_get_file(path
, attrnamespace
, attrname
, NULL
, 0);
503 /* REPLACE attribute, that doesn't exist */
504 if (flags
& XATTR_REPLACE
&& errno
== ENOATTR
) {
508 /* Ignore other errors */
511 /* CREATE attribute, that already exists */
512 if (flags
& XATTR_CREATE
) {
518 retval
= extattr_set_file(path
, attrnamespace
, attrname
, value
, size
);
519 return (retval
< 0) ? -1 : 0;
520 #elif defined(HAVE_ATTR_SET)
522 char *attrname
= strchr(name
,'.') + 1;
524 if (strncmp(name
, "system", 6) == 0) myflags
|= ATTR_ROOT
;
525 if (flags
& XATTR_CREATE
) myflags
|= ATTR_CREATE
;
526 if (flags
& XATTR_REPLACE
) myflags
|= ATTR_REPLACE
;
528 return attr_set(path
, attrname
, (const char *)value
, size
, myflags
);
529 #elif defined(HAVE_ATTROPEN)
531 int myflags
= O_RDWR
;
533 if (flags
& XATTR_CREATE
) myflags
|= O_EXCL
;
534 if (!(flags
& XATTR_REPLACE
)) myflags
|= O_CREAT
;
535 attrfd
= solaris_attropen(path
, name
, myflags
, (mode_t
) SOLARIS_ATTRMODE
);
537 ret
= solaris_write_xattr(attrfd
, value
, size
);
547 int rep_fsetxattr (int filedes
, const char *name
, const void *value
, size_t size
, int flags
)
549 #if defined(HAVE_FSETXATTR)
550 #ifndef XATTR_ADDITIONAL_OPTIONS
551 return fsetxattr(filedes
, name
, value
, size
, flags
);
553 /* So that we do not recursivly call this function */
556 return fsetxattr(filedes
, name
, value
, size
, 0, options
);
558 #elif defined(HAVE_FSETEA)
559 return fsetea(filedes
, name
, value
, size
, flags
);
560 #elif defined(HAVE_EXTATTR_SET_FD)
563 int attrnamespace
= (strncmp(name
, "system", 6) == 0) ?
564 EXTATTR_NAMESPACE_SYSTEM
: EXTATTR_NAMESPACE_USER
;
565 const char *attrname
= ((s
=strchr(name
, '.')) == NULL
) ? name
: s
+ 1;
567 /* Check attribute existence */
568 retval
= extattr_get_fd(filedes
, attrnamespace
, attrname
, NULL
, 0);
570 /* REPLACE attribute, that doesn't exist */
571 if (flags
& XATTR_REPLACE
&& errno
== ENOATTR
) {
575 /* Ignore other errors */
578 /* CREATE attribute, that already exists */
579 if (flags
& XATTR_CREATE
) {
585 retval
= extattr_set_fd(filedes
, attrnamespace
, attrname
, value
, size
);
586 return (retval
< 0) ? -1 : 0;
587 #elif defined(HAVE_ATTR_SETF)
589 char *attrname
= strchr(name
,'.') + 1;
591 if (strncmp(name
, "system", 6) == 0) myflags
|= ATTR_ROOT
;
592 if (flags
& XATTR_CREATE
) myflags
|= ATTR_CREATE
;
593 if (flags
& XATTR_REPLACE
) myflags
|= ATTR_REPLACE
;
595 return attr_setf(filedes
, attrname
, (const char *)value
, size
, myflags
);
596 #elif defined(HAVE_ATTROPEN)
598 int myflags
= O_RDWR
| O_XATTR
;
600 if (flags
& XATTR_CREATE
) myflags
|= O_EXCL
;
601 if (!(flags
& XATTR_REPLACE
)) myflags
|= O_CREAT
;
602 attrfd
= solaris_openat(filedes
, name
, myflags
, (mode_t
) SOLARIS_ATTRMODE
);
604 ret
= solaris_write_xattr(attrfd
, value
, size
);
614 /**************************************************************************
615 helper functions for Solaris' EA support
616 ****************************************************************************/
618 static ssize_t
solaris_read_xattr(int attrfd
, void *value
, size_t size
)
622 if (fstat(attrfd
, &sbuf
) == -1) {
627 /* This is to return the current size of the named extended attribute */
632 /* check size and read xattr */
633 if (sbuf
.st_size
> size
) {
638 return read(attrfd
, value
, sbuf
.st_size
);
641 static ssize_t
solaris_list_xattr(int attrdirfd
, char *list
, size_t size
)
646 int newfd
= dup(attrdirfd
);
647 /* CAUTION: The originating file descriptor should not be
648 used again following the call to fdopendir().
649 For that reason we dup() the file descriptor
650 here to make things more clear. */
651 dirp
= fdopendir(newfd
);
653 while ((de
= readdir(dirp
))) {
654 size_t listlen
= strlen(de
->d_name
) + 1;
655 if (!strcmp(de
->d_name
, ".") || !strcmp(de
->d_name
, "..")) {
656 /* we don't want "." and ".." here: */
661 /* return the current size of the list of extended attribute names*/
664 /* check size and copy entrieѕ + nul into list. */
665 if ((len
+ listlen
) > size
) {
670 strlcpy(list
+ len
, de
->d_name
, listlen
);
676 if (closedir(dirp
) == -1) {
682 static int solaris_unlinkat(int attrdirfd
, const char *name
)
684 if (unlinkat(attrdirfd
, name
, 0) == -1) {
685 if (errno
== ENOENT
) {
693 static int solaris_attropen(const char *path
, const char *attrpath
, int oflag
, mode_t mode
)
695 int filedes
= attropen(path
, attrpath
, oflag
, mode
);
697 if (errno
== EINVAL
) {
706 static int solaris_openat(int fildes
, const char *path
, int oflag
, mode_t mode
)
708 int filedes
= openat(fildes
, path
, oflag
, mode
);
710 if (errno
== EINVAL
) {
719 static int solaris_write_xattr(int attrfd
, const char *value
, size_t size
)
721 if ((ftruncate(attrfd
, 0) == 0) && (write(attrfd
, value
, size
) == size
)) {
727 #endif /*HAVE_ATTROPEN*/