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) {
78 if((retval
=extattr_get_file(path
, attrnamespace
, attrname
, value
, size
)) >= 0)
83 #elif defined(HAVE_ATTR_GET)
84 int retval
, flags
= 0;
85 int valuelength
= (int)size
;
86 char *attrname
= strchr(name
,'.') + 1;
88 if (strncmp(name
, "system", 6) == 0) flags
|= ATTR_ROOT
;
90 retval
= attr_get(path
, attrname
, (char *)value
, &valuelength
, flags
);
92 return retval
? retval
: valuelength
;
93 #elif defined(HAVE_ATTROPEN)
95 int attrfd
= solaris_attropen(path
, name
, O_RDONLY
, 0);
97 ret
= solaris_read_xattr(attrfd
, value
, size
);
107 ssize_t
rep_fgetxattr (int filedes
, const char *name
, void *value
, size_t size
)
109 #if defined(HAVE_FGETXATTR)
110 #ifndef XATTR_ADDITIONAL_OPTIONS
111 return fgetxattr(filedes
, name
, value
, size
);
114 /* So that we do not recursivly call this function */
117 return fgetxattr(filedes
, name
, value
, size
, 0, options
);
119 #elif defined(HAVE_FGETEA)
120 return fgetea(filedes
, name
, value
, size
);
121 #elif defined(HAVE_EXTATTR_GET_FD)
124 int attrnamespace
= (strncmp(name
, "system", 6) == 0) ?
125 EXTATTR_NAMESPACE_SYSTEM
: EXTATTR_NAMESPACE_USER
;
126 const char *attrname
= ((s
=strchr(name
, '.')) == NULL
) ? name
: s
+ 1;
128 if((retval
=extattr_get_fd(filedes
, attrnamespace
, attrname
, NULL
, 0)) >= 0) {
133 if((retval
=extattr_get_fd(filedes
, attrnamespace
, attrname
, value
, size
)) >= 0)
138 #elif defined(HAVE_ATTR_GETF)
139 int retval
, flags
= 0;
140 int valuelength
= (int)size
;
141 char *attrname
= strchr(name
,'.') + 1;
143 if (strncmp(name
, "system", 6) == 0) flags
|= ATTR_ROOT
;
145 retval
= attr_getf(filedes
, attrname
, (char *)value
, &valuelength
, flags
);
147 return retval
? retval
: valuelength
;
148 #elif defined(HAVE_ATTROPEN)
150 int attrfd
= solaris_openat(filedes
, name
, O_RDONLY
|O_XATTR
, 0);
152 ret
= solaris_read_xattr(attrfd
, value
, size
);
162 #if defined(HAVE_EXTATTR_LIST_FILE)
164 #define EXTATTR_PREFIX(s) (s), (sizeof((s))-1)
172 { EXTATTR_NAMESPACE_SYSTEM
, EXTATTR_PREFIX("system.") },
173 { EXTATTR_NAMESPACE_USER
, EXTATTR_PREFIX("user.") },
181 static ssize_t
bsd_attr_list (int type
, extattr_arg arg
, char *list
, size_t size
)
183 ssize_t list_size
, total_size
= 0;
186 /* Iterate through extattr(2) namespaces */
187 for(t
= 0; t
< ARRAY_SIZE(extattr
); t
++) {
189 #if defined(HAVE_EXTATTR_LIST_FILE)
191 list_size
= extattr_list_file(arg
.path
, extattr
[t
].space
, list
, size
);
194 #if defined(HAVE_EXTATTR_LIST_LINK)
196 list_size
= extattr_list_link(arg
.path
, extattr
[t
].space
, list
, size
);
199 #if defined(HAVE_EXTATTR_LIST_FD)
201 list_size
= extattr_list_fd(arg
.filedes
, extattr
[t
].space
, list
, size
);
208 /* Some error happend. Errno should be set by the previous call */
214 /* XXX: Call with an empty buffer may be used to calculate
215 necessary buffer size. Unfortunately, we can't say, how
216 many attributes were returned, so here is the potential
217 problem with the emulation.
220 /* Take the worse case of one char attribute names -
221 two bytes per name plus one more for sanity.
223 total_size
+= list_size
+ (list_size
/2 + 1)*extattr
[t
].len
;
226 /* Count necessary offset to fit namespace prefixes */
228 for(i
= 0; i
< list_size
; i
+= list
[i
] + 1)
229 len
+= extattr
[t
].len
;
231 total_size
+= list_size
+ len
;
232 /* Buffer is too small to fit the results */
233 if(total_size
> size
) {
237 /* Shift results back, so we can prepend prefixes */
238 buf
= (char *)memmove(list
+ len
, list
, list_size
);
240 for(i
= 0; i
< list_size
; i
+= len
+ 1) {
242 strncpy(list
, extattr
[t
].name
, extattr
[t
].len
+ 1);
243 list
+= extattr
[t
].len
;
244 strncpy(list
, buf
+ i
+ 1, len
);
255 #if defined(HAVE_ATTR_LIST) && (defined(HAVE_SYS_ATTRIBUTES_H) || defined(HAVE_ATTR_ATTRIBUTES_H))
256 static char attr_buffer
[ATTR_MAX_VALUELEN
];
258 static ssize_t
irix_attr_list(const char *path
, int filedes
, char *list
, size_t size
, int flags
)
260 int retval
= 0, index
;
261 attrlist_cursor_t
*cursor
= 0;
263 attrlist_t
* al
= (attrlist_t
*)attr_buffer
;
265 size_t ent_size
, left
= size
;
270 retval
= attr_listf(filedes
, attr_buffer
, ATTR_MAX_VALUELEN
, flags
, cursor
);
272 retval
= attr_list(path
, attr_buffer
, ATTR_MAX_VALUELEN
, flags
, cursor
);
274 for (index
= 0; index
< al
->al_count
; index
++) {
275 ae
= ATTR_ENTRY(attr_buffer
, index
);
276 ent_size
= strlen(ae
->a_name
) + sizeof("user.");
277 if (left
>= ent_size
) {
278 strncpy(bp
, "user.", sizeof("user."));
279 strncat(bp
, ae
->a_name
, ent_size
- sizeof("user."));
287 total_size
+= ent_size
;
289 if (al
->al_more
== 0) break;
296 retval
= attr_listf(filedes
, attr_buffer
, ATTR_MAX_VALUELEN
, flags
, cursor
);
298 retval
= attr_list(path
, attr_buffer
, ATTR_MAX_VALUELEN
, flags
, cursor
);
300 for (index
= 0; index
< al
->al_count
; index
++) {
301 ae
= ATTR_ENTRY(attr_buffer
, index
);
302 ent_size
= strlen(ae
->a_name
) + sizeof("system.");
303 if (left
>= ent_size
) {
304 strncpy(bp
, "system.", sizeof("system."));
305 strncat(bp
, ae
->a_name
, ent_size
- sizeof("system."));
313 total_size
+= ent_size
;
315 if (al
->al_more
== 0) break;
318 return (ssize_t
)(retval
? retval
: total_size
);
323 ssize_t
rep_listxattr (const char *path
, char *list
, size_t size
)
325 #if defined(HAVE_LISTXATTR)
326 #ifndef XATTR_ADDITIONAL_OPTIONS
327 return listxattr(path
, list
, size
);
329 /* So that we do not recursivly call this function */
332 return listxattr(path
, list
, size
, options
);
334 #elif defined(HAVE_LISTEA)
335 return listea(path
, list
, size
);
336 #elif defined(HAVE_EXTATTR_LIST_FILE)
339 return bsd_attr_list(0, arg
, list
, size
);
340 #elif defined(HAVE_ATTR_LIST) && defined(HAVE_SYS_ATTRIBUTES_H)
341 return irix_attr_list(path
, 0, list
, size
, 0);
342 #elif defined(HAVE_ATTROPEN)
344 int attrdirfd
= solaris_attropen(path
, ".", O_RDONLY
, 0);
345 if (attrdirfd
>= 0) {
346 ret
= solaris_list_xattr(attrdirfd
, list
, size
);
356 ssize_t
rep_flistxattr (int filedes
, char *list
, size_t size
)
358 #if defined(HAVE_FLISTXATTR)
359 #ifndef XATTR_ADDITIONAL_OPTIONS
360 return flistxattr(filedes
, list
, size
);
362 /* So that we do not recursivly call this function */
365 return flistxattr(filedes
, list
, size
, options
);
367 #elif defined(HAVE_FLISTEA)
368 return flistea(filedes
, list
, size
);
369 #elif defined(HAVE_EXTATTR_LIST_FD)
371 arg
.filedes
= filedes
;
372 return bsd_attr_list(2, arg
, list
, size
);
373 #elif defined(HAVE_ATTR_LISTF)
374 return irix_attr_list(NULL
, filedes
, list
, size
, 0);
375 #elif defined(HAVE_ATTROPEN)
377 int attrdirfd
= solaris_openat(filedes
, ".", O_RDONLY
|O_XATTR
, 0);
378 if (attrdirfd
>= 0) {
379 ret
= solaris_list_xattr(attrdirfd
, list
, size
);
389 int rep_removexattr (const char *path
, const char *name
)
391 #if defined(HAVE_REMOVEXATTR)
392 #ifndef XATTR_ADDITIONAL_OPTIONS
393 return removexattr(path
, name
);
395 /* So that we do not recursivly call this function */
398 return removexattr(path
, name
, options
);
400 #elif defined(HAVE_REMOVEEA)
401 return removeea(path
, name
);
402 #elif defined(HAVE_EXTATTR_DELETE_FILE)
404 int attrnamespace
= (strncmp(name
, "system", 6) == 0) ?
405 EXTATTR_NAMESPACE_SYSTEM
: EXTATTR_NAMESPACE_USER
;
406 const char *attrname
= ((s
=strchr(name
, '.')) == NULL
) ? name
: s
+ 1;
408 return extattr_delete_file(path
, attrnamespace
, attrname
);
409 #elif defined(HAVE_ATTR_REMOVE)
411 char *attrname
= strchr(name
,'.') + 1;
413 if (strncmp(name
, "system", 6) == 0) flags
|= ATTR_ROOT
;
415 return attr_remove(path
, attrname
, flags
);
416 #elif defined(HAVE_ATTROPEN)
418 int attrdirfd
= solaris_attropen(path
, ".", O_RDONLY
, 0);
419 if (attrdirfd
>= 0) {
420 ret
= solaris_unlinkat(attrdirfd
, name
);
430 int rep_fremovexattr (int filedes
, const char *name
)
432 #if defined(HAVE_FREMOVEXATTR)
433 #ifndef XATTR_ADDITIONAL_OPTIONS
434 return fremovexattr(filedes
, name
);
436 /* So that we do not recursivly call this function */
439 return fremovexattr(filedes
, name
, options
);
441 #elif defined(HAVE_FREMOVEEA)
442 return fremoveea(filedes
, name
);
443 #elif defined(HAVE_EXTATTR_DELETE_FD)
445 int attrnamespace
= (strncmp(name
, "system", 6) == 0) ?
446 EXTATTR_NAMESPACE_SYSTEM
: EXTATTR_NAMESPACE_USER
;
447 const char *attrname
= ((s
=strchr(name
, '.')) == NULL
) ? name
: s
+ 1;
449 return extattr_delete_fd(filedes
, attrnamespace
, attrname
);
450 #elif defined(HAVE_ATTR_REMOVEF)
452 char *attrname
= strchr(name
,'.') + 1;
454 if (strncmp(name
, "system", 6) == 0) flags
|= ATTR_ROOT
;
456 return attr_removef(filedes
, attrname
, flags
);
457 #elif defined(HAVE_ATTROPEN)
459 int attrdirfd
= solaris_openat(filedes
, ".", O_RDONLY
|O_XATTR
, 0);
460 if (attrdirfd
>= 0) {
461 ret
= solaris_unlinkat(attrdirfd
, name
);
471 int rep_setxattr (const char *path
, const char *name
, const void *value
, size_t size
, int flags
)
473 #if defined(HAVE_SETXATTR)
474 #ifndef XATTR_ADDITIONAL_OPTIONS
475 return setxattr(path
, name
, value
, size
, flags
);
477 /* So that we do not recursivly call this function */
480 return setxattr(path
, name
, value
, size
, 0, options
);
482 #elif defined(HAVE_SETEA)
483 return setea(path
, name
, value
, size
, flags
);
484 #elif defined(HAVE_EXTATTR_SET_FILE)
487 int attrnamespace
= (strncmp(name
, "system", 6) == 0) ?
488 EXTATTR_NAMESPACE_SYSTEM
: EXTATTR_NAMESPACE_USER
;
489 const char *attrname
= ((s
=strchr(name
, '.')) == NULL
) ? name
: s
+ 1;
491 /* Check attribute existence */
492 retval
= extattr_get_file(path
, attrnamespace
, attrname
, NULL
, 0);
494 /* REPLACE attribute, that doesn't exist */
495 if (flags
& XATTR_REPLACE
&& errno
== ENOATTR
) {
499 /* Ignore other errors */
502 /* CREATE attribute, that already exists */
503 if (flags
& XATTR_CREATE
) {
509 retval
= extattr_set_file(path
, attrnamespace
, attrname
, value
, size
);
510 return (retval
< 0) ? -1 : 0;
511 #elif defined(HAVE_ATTR_SET)
513 char *attrname
= strchr(name
,'.') + 1;
515 if (strncmp(name
, "system", 6) == 0) myflags
|= ATTR_ROOT
;
516 if (flags
& XATTR_CREATE
) myflags
|= ATTR_CREATE
;
517 if (flags
& XATTR_REPLACE
) myflags
|= ATTR_REPLACE
;
519 return attr_set(path
, attrname
, (const char *)value
, size
, myflags
);
520 #elif defined(HAVE_ATTROPEN)
522 int myflags
= O_RDWR
;
524 if (flags
& XATTR_CREATE
) myflags
|= O_EXCL
;
525 if (!(flags
& XATTR_REPLACE
)) myflags
|= O_CREAT
;
526 attrfd
= solaris_attropen(path
, name
, myflags
, (mode_t
) SOLARIS_ATTRMODE
);
528 ret
= solaris_write_xattr(attrfd
, value
, size
);
538 int rep_fsetxattr (int filedes
, const char *name
, const void *value
, size_t size
, int flags
)
540 #if defined(HAVE_FSETXATTR)
541 #ifndef XATTR_ADDITIONAL_OPTIONS
542 return fsetxattr(filedes
, name
, value
, size
, flags
);
544 /* So that we do not recursivly call this function */
547 return fsetxattr(filedes
, name
, value
, size
, 0, options
);
549 #elif defined(HAVE_FSETEA)
550 return fsetea(filedes
, name
, value
, size
, flags
);
551 #elif defined(HAVE_EXTATTR_SET_FD)
554 int attrnamespace
= (strncmp(name
, "system", 6) == 0) ?
555 EXTATTR_NAMESPACE_SYSTEM
: EXTATTR_NAMESPACE_USER
;
556 const char *attrname
= ((s
=strchr(name
, '.')) == NULL
) ? name
: s
+ 1;
558 /* Check attribute existence */
559 retval
= extattr_get_fd(filedes
, attrnamespace
, attrname
, NULL
, 0);
561 /* REPLACE attribute, that doesn't exist */
562 if (flags
& XATTR_REPLACE
&& errno
== ENOATTR
) {
566 /* Ignore other errors */
569 /* CREATE attribute, that already exists */
570 if (flags
& XATTR_CREATE
) {
576 retval
= extattr_set_fd(filedes
, attrnamespace
, attrname
, value
, size
);
577 return (retval
< 0) ? -1 : 0;
578 #elif defined(HAVE_ATTR_SETF)
580 char *attrname
= strchr(name
,'.') + 1;
582 if (strncmp(name
, "system", 6) == 0) myflags
|= ATTR_ROOT
;
583 if (flags
& XATTR_CREATE
) myflags
|= ATTR_CREATE
;
584 if (flags
& XATTR_REPLACE
) myflags
|= ATTR_REPLACE
;
586 return attr_setf(filedes
, attrname
, (const char *)value
, size
, myflags
);
587 #elif defined(HAVE_ATTROPEN)
589 int myflags
= O_RDWR
| O_XATTR
;
591 if (flags
& XATTR_CREATE
) myflags
|= O_EXCL
;
592 if (!(flags
& XATTR_REPLACE
)) myflags
|= O_CREAT
;
593 attrfd
= solaris_openat(filedes
, name
, myflags
, (mode_t
) SOLARIS_ATTRMODE
);
595 ret
= solaris_write_xattr(attrfd
, value
, size
);
605 /**************************************************************************
606 helper functions for Solaris' EA support
607 ****************************************************************************/
609 static ssize_t
solaris_read_xattr(int attrfd
, void *value
, size_t size
)
613 if (fstat(attrfd
, &sbuf
) == -1) {
618 /* This is to return the current size of the named extended attribute */
623 /* check size and read xattr */
624 if (sbuf
.st_size
> size
) {
629 return read(attrfd
, value
, sbuf
.st_size
);
632 static ssize_t
solaris_list_xattr(int attrdirfd
, char *list
, size_t size
)
637 int newfd
= dup(attrdirfd
);
638 /* CAUTION: The originating file descriptor should not be
639 used again following the call to fdopendir().
640 For that reason we dup() the file descriptor
641 here to make things more clear. */
642 dirp
= fdopendir(newfd
);
644 while ((de
= readdir(dirp
))) {
645 size_t listlen
= strlen(de
->d_name
) + 1;
646 if (!strcmp(de
->d_name
, ".") || !strcmp(de
->d_name
, "..")) {
647 /* we don't want "." and ".." here: */
652 /* return the current size of the list of extended attribute names*/
655 /* check size and copy entrieѕ + nul into list. */
656 if ((len
+ listlen
) > size
) {
661 strlcpy(list
+ len
, de
->d_name
, listlen
);
667 if (closedir(dirp
) == -1) {
673 static int solaris_unlinkat(int attrdirfd
, const char *name
)
675 if (unlinkat(attrdirfd
, name
, 0) == -1) {
676 if (errno
== ENOENT
) {
684 static int solaris_attropen(const char *path
, const char *attrpath
, int oflag
, mode_t mode
)
686 int filedes
= attropen(path
, attrpath
, oflag
, mode
);
688 if (errno
== EINVAL
) {
697 static int solaris_openat(int fildes
, const char *path
, int oflag
, mode_t mode
)
699 int filedes
= openat(fildes
, path
, oflag
, mode
);
701 if (errno
== EINVAL
) {
710 static int solaris_write_xattr(int attrfd
, const char *value
, size_t size
)
712 if ((ftruncate(attrfd
, 0) == 0) && (write(attrfd
, value
, size
) == size
)) {
718 #endif /*HAVE_ATTROPEN*/