From a790891428b200e272338608275e2da8daa99153 Mon Sep 17 00:00:00 2001 From: Marco van Wieringen Date: Wed, 7 Oct 2009 09:12:15 +0200 Subject: [PATCH] Implement the extattr xBSD interface Moved some code around to be able to reuse the encoding and decoding for both the extattr and xattr interface Use an alist for storing the xattr list so we don't have to count the number of xattr first --- bacula/src/filed/xattr.c | 775 ++++++++++++++++++++++++++++----------- 1 file changed, 568 insertions(+), 207 deletions(-) diff --git a/bacula/src/filed/xattr.c b/bacula/src/filed/xattr.c index bb8f0d70ac..7e187339fe 100644 --- a/bacula/src/filed/xattr.c +++ b/bacula/src/filed/xattr.c @@ -114,69 +114,26 @@ static bxattr_exit_code send_xattr_stream(JCR *jcr, int stream) } /* - * This is a supported OS, See what kind of interface we should use. - * Start with the generic interface used by most OS-es. + * First some generic functions for OSes that use the same xattr encoding scheme. */ #if defined(HAVE_DARWIN_OS) || \ defined(HAVE_LINUX_OS) || \ - defined(HAVE_NETBSD_OS) - -#ifdef HAVE_SYS_XATTR_H -#include -#endif - -/* - * Define the supported XATTR streams for this OS - */ -#if defined(HAVE_DARWIN_OS) -static int os_default_xattr_streams[1] = { STREAM_XATTR_DARWIN }; -static const char *xattr_acl_skiplist[2] = { "com.apple.system.Security", NULL }; -static const char *xattr_skiplist[3] = { "com.apple.system.extendedsecurity", "com.apple.ResourceFork", NULL }; -#elif defined(HAVE_LINUX_OS) -static int os_default_xattr_streams[1] = { STREAM_XATTR_LINUX }; -static const char *xattr_acl_skiplist[2] = { "system.posix_acl_access", NULL }; -static const char *xattr_skiplist[1] = { NULL }; -#endif - -/* - * OSX doesn't have llistxattr, lgetxattr and lsetxattr but has - * listxattr, getxattr and setxattr with an extra options argument - * which mimics the l variants of the functions when we specify - * XATTR_NOFOLLOW as the options value. - */ -#if defined(HAVE_DARWIN_OS) - #define llistxattr(path, list, size) listxattr((path), (list), (size), XATTR_NOFOLLOW) - #define lgetxattr(path, name, value, size) getxattr((path), (name), (value), (size), 0, XATTR_NOFOLLOW) - #define lsetxattr(path, name, value, size, flags) setxattr((path), (name), (value), (size), (flags), XATTR_NOFOLLOW) -#else - /* - * Fallback to the non l-functions when those are not available. - */ - #if defined(HAVE_GETXATTR) && !defined(HAVE_LGETXATTR) - #define lgetxattr getxattr - #endif - #if defined(HAVE_SETXATTR) && !defined(HAVE_LSETXATTR) - #define lsetxattr setxattr - #endif - #if defined(HAVE_LISTXATTR) && !defined(HAVE_LLISTXATTR) - #define llistxattr listxattr - #endif -#endif + defined(HAVE_NETBSD_OS) || \ + defined(HAVE_FREEBSD_OS) || \ + defined(HAVE_OPENBSD_OS) -static void xattr_drop_internal_table(xattr_t *xattr_value_list) +static void xattr_drop_internal_table(alist *xattr_value_list) { xattr_t *current_xattr; /* * Walk the list of xattrs and free allocated memory on traversing. */ - for (current_xattr = xattr_value_list; - current_xattr != (xattr_t *)NULL; - current_xattr++) { + foreach_alist(current_xattr, xattr_value_list) { /* * See if we can shortcut. */ - if (current_xattr->magic != XATTR_MAGIC) + if (current_xattr == NULL || current_xattr->magic != XATTR_MAGIC) break; free(current_xattr->name); @@ -184,11 +141,6 @@ static void xattr_drop_internal_table(xattr_t *xattr_value_list) if (current_xattr->value_length > 0) free(current_xattr->value); } - - /* - * Free the array of control structs. - */ - free(xattr_value_list); } /* @@ -205,7 +157,7 @@ static void xattr_drop_internal_table(xattr_t *xattr_value_list) * This is repeated 1 or more times. * */ -static uint32_t serialize_xattr_stream(JCR *jcr, uint32_t expected_serialize_len, xattr_t *xattr_value_list) +static uint32_t serialize_xattr_stream(JCR *jcr, uint32_t expected_serialize_len, alist *xattr_value_list) { xattr_t *current_xattr; ser_declare; @@ -220,11 +172,11 @@ static uint32_t serialize_xattr_stream(JCR *jcr, uint32_t expected_serialize_len /* * Walk the list of xattrs and serialize the data. */ - for (current_xattr = xattr_value_list; current_xattr != (xattr_t *)NULL; current_xattr++) { + foreach_alist(current_xattr, xattr_value_list) { /* * See if we can shortcut. */ - if (current_xattr->magic != XATTR_MAGIC) + if (current_xattr == NULL || current_xattr->magic != XATTR_MAGIC) break; ser_uint32(current_xattr->magic); @@ -237,18 +189,157 @@ static uint32_t serialize_xattr_stream(JCR *jcr, uint32_t expected_serialize_len ser_end(jcr->xattr_data->content, expected_serialize_len + 10); jcr->xattr_data->content_length = ser_length(jcr->xattr_data->content); + return jcr->xattr_data->content_length; } +/* + * Forward declaration for restore function. + */ +static bxattr_exit_code restore_xattr_on_file(JCR *jcr, xattr_t *xattr); + +static bxattr_exit_code unserialize_xattr_stream(JCR *jcr) +{ + unser_declare; + xattr_t current_xattr; + bxattr_exit_code retval = bxattr_exit_ok; + + /* + * Parse the stream and call restore_xattr_on_file for each extended attribute. + * + * Start unserializing the data. We keep on looping while we have not + * unserialized all bytes in the stream. + */ + unser_begin(jcr->xattr_data->content, jcr->xattr_data->content_length); + while (unser_length(jcr->xattr_data->content) < jcr->xattr_data->content_length) { + /* + * First make sure the magic is present. This way we can easily catch corruption. + * Any missing MAGIC is fatal we do NOT try to continue. + */ + unser_uint32(current_xattr.magic); + if (current_xattr.magic != XATTR_MAGIC) { + Mmsg1(jcr->errmsg, _("Illegal xattr stream, no XATTR_MAGIC on file \"%s\"\n"), + jcr->last_fname); + Dmsg1(100, "Illegal xattr stream, no XATTR_MAGIC on file \"%s\"\n", + jcr->last_fname); + return bxattr_exit_error; + } + + /* + * Decode the valuepair. First decode the length of the name. + */ + unser_uint32(current_xattr.name_length); + + /* + * Allocate room for the name and decode its content. + */ + current_xattr.name = (char *)malloc(current_xattr.name_length + 1); + unser_bytes(current_xattr.name, current_xattr.name_length); + + /* + * The xattr_name needs to be null terminated for lsetxattr. + */ + current_xattr.name[current_xattr.name_length] = '\0'; + + /* + * Decode the value length. + */ + unser_uint32(current_xattr.value_length); + + /* + * Allocate room for the value and decode its content. + */ + current_xattr.value = (char *)malloc(current_xattr.value_length); + unser_bytes(current_xattr.value, current_xattr.value_length); + + /* + * Try to set the extended attribute on the file. + * If we fail to set this attribute we flag the error but its not fatal, + * we try to restore the other extended attributes too. + */ + if (restore_xattr_on_file(jcr, ¤t_xattr) != bxattr_exit_ok) { + retval = bxattr_exit_error; + } + + /* + * Free the temporary buffers. + */ + free(current_xattr.name); + free(current_xattr.value); + } + + unser_end(jcr->xattr_data->content, jcr->xattr_data->content_length); + return retval; +} +#endif + +/* + * This is a supported OS, See what kind of interface we should use. + * Start with the generic interface used by most OS-es. + */ +#if defined(HAVE_DARWIN_OS) || \ + defined(HAVE_LINUX_OS) + +#if (!defined(HAVE_LISTXATTR) && !defined(HAVE_LLISTXATTR)) || \ + (!defined(HAVE_GETXATTR) && !defined(HAVE_LGETXATTR)) || \ + (!defined(HAVE_SETXATTR) && !defined(HAVE_LSETXATTR)) +#error "Missing either full support for the LXATTR or XATTR functions." +#endif + +#ifdef HAVE_SYS_XATTR_H +#include +#else +#error "Missing sys/xattr.h header file" +#endif + +/* + * Define the supported XATTR streams for this OS + */ +#if defined(HAVE_DARWIN_OS) +static int os_default_xattr_streams[1] = { STREAM_XATTR_DARWIN }; +static const char *xattr_acl_skiplist[2] = { "com.apple.system.Security", NULL }; +static const char *xattr_skiplist[3] = { "com.apple.system.extendedsecurity", "com.apple.ResourceFork", NULL }; +#elif defined(HAVE_LINUX_OS) +static int os_default_xattr_streams[1] = { STREAM_XATTR_LINUX }; +static const char *xattr_acl_skiplist[2] = { "system.posix_acl_access", NULL }; +static const char *xattr_skiplist[1] = { NULL }; +#endif + +/* + * OSX doesn't have llistxattr, lgetxattr and lsetxattr but has + * listxattr, getxattr and setxattr with an extra options argument + * which mimics the l variants of the functions when we specify + * XATTR_NOFOLLOW as the options value. + */ +#if defined(HAVE_DARWIN_OS) + #define llistxattr(path, list, size) listxattr((path), (list), (size), XATTR_NOFOLLOW) + #define lgetxattr(path, name, value, size) getxattr((path), (name), (value), (size), 0, XATTR_NOFOLLOW) + #define lsetxattr(path, name, value, size, flags) setxattr((path), (name), (value), (size), (flags), XATTR_NOFOLLOW) +#else + /* + * Fallback to the non l-functions when those are not available. + */ + #if defined(HAVE_GETXATTR) && !defined(HAVE_LGETXATTR) + #define lgetxattr getxattr + #endif + #if defined(HAVE_SETXATTR) && !defined(HAVE_LSETXATTR) + #define lsetxattr setxattr + #endif + #if defined(HAVE_LISTXATTR) && !defined(HAVE_LLISTXATTR) + #define llistxattr listxattr + #endif +#endif + static bxattr_exit_code generic_xattr_build_streams(JCR *jcr, FF_PKT *ff_pkt) { bool skip_xattr; + char *xattr_list, *bp; int cnt, xattr_count = 0; int32_t xattr_list_len, xattr_value_len; uint32_t expected_serialize_len = 0; - char *xattr_list, *bp; - xattr_t *xattr_value_list = NULL, *current_xattr; + xattr_t *current_xattr; + alist *xattr_value_list = NULL; bxattr_exit_code retval = bxattr_exit_error; berrno be; @@ -296,62 +387,12 @@ static bxattr_exit_code generic_xattr_build_streams(JCR *jcr, FF_PKT *ff_pkt) } xattr_list[xattr_list_len] = '\0'; - /* - * Count the number of extended attributes on a file. - */ - bp = xattr_list; - while ((bp - xattr_list) + 1 < xattr_list_len) { - skip_xattr = false; - - /* - * On some OSes you also get the acls in the extented attribute list. - * So we check if we are already backing up acls and if we do we - * don't store the extended attribute with the same info. - */ - if (ff_pkt->flags & FO_ACL) { - for (cnt = 0; xattr_acl_skiplist[cnt] != NULL; cnt++) { - if (!strcmp(bp, xattr_acl_skiplist[cnt])) { - skip_xattr = true; - break; - } - } - } - - /* - * On some OSes we want to skip certain xattrs which are in the xattr_skiplist array. - */ - if (!skip_xattr) { - for (cnt = 0; xattr_skiplist[cnt] != NULL; cnt++) { - if (!strcmp(bp, xattr_skiplist[cnt])) { - skip_xattr = true; - break; - } - } - } - - if (!skip_xattr) { - xattr_count++; - } - bp = strchr(bp, '\0') + 1; - } - - if (xattr_count == 0) { - retval = bxattr_exit_ok; - goto bail_out; - } - - /* - * Allocate enough room to hold all extended attributes. - * After allocating the storage make sure its empty by zeroing it. - */ - xattr_value_list = (xattr_t *)malloc(xattr_count * sizeof(xattr_t)); - memset((caddr_t)xattr_value_list, 0, xattr_count * sizeof(xattr_t)); + xattr_value_list = New(alist(10, not_owned_by_alist)); /* * Walk the list of extended attributes names and retrieve the data. * We already count the bytes needed for serializing the stream later on. */ - current_xattr = xattr_value_list; bp = xattr_list; while ((bp - xattr_list) + 1 < xattr_list_len) { skip_xattr = false; @@ -390,6 +431,7 @@ static bxattr_exit_code generic_xattr_build_streams(JCR *jcr, FF_PKT *ff_pkt) /* * Each xattr valuepair starts with a magic so we can parse it easier. */ + current_xattr = (xattr_t *)malloc(sizeof(xattr_t)); current_xattr->magic = XATTR_MAGIC; expected_serialize_len += sizeof(current_xattr->magic); @@ -410,12 +452,16 @@ static bxattr_exit_code generic_xattr_build_streams(JCR *jcr, FF_PKT *ff_pkt) switch (errno) { case ENOENT: retval = bxattr_exit_ok; + free(current_xattr->name); + free(current_xattr); goto bail_out; default: Mmsg2(jcr->errmsg, _("lgetxattr error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror()); Dmsg2(100, "lgetxattr error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror()); + free(current_xattr->name); + free(current_xattr); goto bail_out; } } @@ -431,12 +477,18 @@ static bxattr_exit_code generic_xattr_build_streams(JCR *jcr, FF_PKT *ff_pkt) switch (errno) { case ENOENT: retval = bxattr_exit_ok; + free(current_xattr->value); + free(current_xattr->name); + free(current_xattr); goto bail_out; default: Mmsg2(jcr->errmsg, _("lgetxattr error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror()); Dmsg2(100, "lgetxattr error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror()); + free(current_xattr->value); + free(current_xattr->name); + free(current_xattr); goto bail_out; } } @@ -453,166 +505,468 @@ static bxattr_exit_code generic_xattr_build_streams(JCR *jcr, FF_PKT *ff_pkt) if (expected_serialize_len >= MAX_XATTR_STREAM) { Mmsg2(jcr->errmsg, _("Xattr stream on file \"%s\" exceeds maximum size of %d bytes\n"), jcr->last_fname, MAX_XATTR_STREAM); + free(current_xattr->value); + free(current_xattr->name); + free(current_xattr); goto bail_out; } - - /* - * Next attribute. - */ - current_xattr++; - bp = strchr(bp, '\0') + 1; - } - /* - * Serialize the datastream. - */ - if (serialize_xattr_stream(jcr, expected_serialize_len, xattr_value_list) < expected_serialize_len) { - Mmsg1(jcr->errmsg, _("Failed to serialize extended attributes on file \"%s\"\n"), - jcr->last_fname); - Dmsg1(100, "Failed to serialize extended attributes on file \"%s\"\n", - jcr->last_fname); - goto bail_out; + xattr_value_list->append(current_xattr); + xattr_count++; + bp = strchr(bp, '\0') + 1; } - xattr_drop_internal_table(xattr_value_list); free(xattr_list); + xattr_list = (char *)NULL; /* - * Send the datastream to the SD. + * If we found any xattr send them to the SD. */ - return send_xattr_stream(jcr, os_default_xattr_streams[0]); + if (xattr_count > 0) { + /* + * Serialize the datastream. + */ + if (serialize_xattr_stream(jcr, expected_serialize_len, xattr_value_list) < expected_serialize_len) { + Mmsg1(jcr->errmsg, _("Failed to serialize extended attributes on file \"%s\"\n"), + jcr->last_fname); + Dmsg1(100, "Failed to serialize extended attributes on file \"%s\"\n", + jcr->last_fname); + goto bail_out; + } + + xattr_drop_internal_table(xattr_value_list); + delete xattr_value_list; + xattr_value_list = NULL; + + /* + * Send the datastream to the SD. + */ + return send_xattr_stream(jcr, os_default_xattr_streams[0]); + } else { + xattr_drop_internal_table(xattr_value_list); + delete xattr_value_list; + xattr_value_list = NULL; + + return bxattr_exit_ok; + } bail_out: + if (xattr_list) { + free(xattr_list); + } if (xattr_value_list) { xattr_drop_internal_table(xattr_value_list); + delete xattr_value_list; + xattr_value_list = NULL; } - free(xattr_list); return retval; } +/* + * This function gets called by the unserialize_xattr_stream function for the OS specific + * code to restore an extended attribute on a file. + */ +static bxattr_exit_code restore_xattr_on_file(JCR *jcr, xattr_t *xattr) +{ + berrno be; + + if (lsetxattr(jcr->last_fname, xattr->name, xattr->value, xattr->value_length, 0) != 0) { + switch (errno) { + case ENOENT: + break; + default: + Mmsg2(jcr->errmsg, _("lsetxattr error on file \"%s\": ERR=%s\n"), + jcr->last_fname, be.bstrerror()); + Dmsg2(100, "lsetxattr error file=%s ERR=%s\n", + jcr->last_fname, be.bstrerror()); + return bxattr_exit_error; + break; + } + } + + return bxattr_exit_ok; +} + static bxattr_exit_code generic_xattr_parse_streams(JCR *jcr, int stream) { - unser_declare; - xattr_t current_xattr; - bxattr_exit_code retval = bxattr_exit_ok; + return unserialize_xattr_stream(jcr); +} + +/* + * For all these os-es setup the build and parse function pointer to the generic functions. + */ +static bxattr_exit_code (*os_build_xattr_streams)(JCR *jcr, FF_PKT *ff_pkt) = generic_xattr_build_streams; +static bxattr_exit_code (*os_parse_xattr_streams)(JCR *jcr, int stream) = generic_xattr_parse_streams; + +#elif defined(HAVE_FREEBSD_OS) || \ + defined(HAVE_NETBSD_OS) || \ + defined(HAVE_OPENBSD_OS) + +#if !defined(HAVE_EXTATTR_GET_LINK) || \ + !defined(HAVE_EXTATTR_SET_LINK) || \ + !defined(HAVE_EXTATTR_LIST_LINK) || \ + !defined(HAVE_EXTATTR_NAMESPACE_TO_STRING) || \ + !defined(HAVE_EXTATTR_STRING_TO_NAMESPACE) +#error "Missing full support for the extattr functions." +#endif + +#ifdef HAVE_SYS_EXTATTR_H +#include +#else +#error "Missing sys/extattr.h header file" +#endif + +#ifdef HAVE_LIBUTIL_H +#include +#endif + +#if defined(HAVE_FREEBSD_OS) +static int os_default_xattr_streams[1] = { STREAM_XATTR_FREEBSD }; +static int os_default_xattr_namespaces[2] = { EXTATTR_NAMESPACE_USER, EXTATTR_NAMESPACE_SYSTEM }; +static const char *xattr_acl_skiplist[1] = { NULL }; +static const char *xattr_skiplist[1] = { NULL }; +#elif defined(HAVE_NETBSD_OS) +static int os_default_xattr_streams[1] = { STREAM_XATTR_NETBSD }; +static int os_default_xattr_namespaces[2] = { EXTATTR_NAMESPACE_USER, EXTATTR_NAMESPACE_SYSTEM }; +static const char *xattr_acl_skiplist[1] = { NULL }; +static const char *xattr_skiplist[1] = { NULL }; +#elif defined(HAVE_OPENBSD_OS) +static int os_default_xattr_streams[1] = { STREAM_XATTR_OPENBSD }; +static int os_default_xattr_namespaces[2] = { EXTATTR_NAMESPACE_USER, EXTATTR_NAMESPACE_SYSTEM }; +static const char *xattr_acl_skiplist[1] = { NULL }; +static const char *xattr_skiplist[1] = { NULL }; +#endif + +static bxattr_exit_code bsd_build_xattr_streams(JCR *jcr, FF_PKT *ff_pkt) +{ + bool skip_xattr; + char *xattr_list; + int cnt, index, xattr_count = 0; + int32_t xattr_list_len, + xattr_value_len; + uint32_t expected_serialize_len = 0; + unsigned int namespace_index; + int attrnamespace; + char *current_attrnamespace, current_attrname[BUFSIZ], current_attrtuple[BUFSIZ]; + xattr_t *current_xattr; + alist *xattr_value_list = NULL; + bxattr_exit_code retval = bxattr_exit_error; berrno be; + xattr_value_list = New(alist(10, not_owned_by_alist)); + /* - * Parse the stream and perform the setxattr calls on the file. - * - * Start unserializing the data. We keep on looping while we have not - * unserialized all bytes in the stream. + * Loop over all available xattr namespaces. */ - unser_begin(jcr->xattr_data->content, jcr->xattr_data->content_length); - while (unser_length(jcr->xattr_data->content) < jcr->xattr_data->content_length) { + for (namespace_index = 0; namespace_index < sizeof(os_default_xattr_namespaces) / sizeof(int); namespace_index++) { + attrnamespace = os_default_xattr_namespaces[namespace_index]; + /* - * First make sure the magic is present. This way we can easily catch corruption. - * Any missing MAGIC is fatal we do NOT try to continue. + * First get the length of the available list with extended attributes. */ - unser_uint32(current_xattr.magic); - if (current_xattr.magic != XATTR_MAGIC) { - Mmsg1(jcr->errmsg, _("Illegal xattr stream, no XATTR_MAGIC on file \"%s\"\n"), - jcr->last_fname); - Dmsg1(100, "Illegal xattr stream, no XATTR_MAGIC on file \"%s\"\n", - jcr->last_fname); - return bxattr_exit_error; + xattr_list_len = extattr_list_link(jcr->last_fname, attrnamespace, NULL, 0); + if (xattr_list_len < 0) { + switch (errno) { + case ENOENT: + retval = bxattr_exit_ok; + goto bail_out; + default: + Mmsg2(jcr->errmsg, _("extattr_list_link error on file \"%s\": ERR=%s\n"), + jcr->last_fname, be.bstrerror()); + Dmsg2(100, "extattr_list_link error file=%s ERR=%s\n", + jcr->last_fname, be.bstrerror()); + goto bail_out; + } + } else if (xattr_list_len == 0) { + continue; } /* - * Decode the valuepair. First decode the length of the name. + * Allocate room for the extented attribute list. */ - unser_uint32(current_xattr.name_length); - - /* - * Allocate room for the name and decode its content. - */ - current_xattr.name = (char *)malloc(current_xattr.name_length + 1); - unser_bytes(current_xattr.name, current_xattr.name_length); + xattr_list = (char *)malloc(xattr_list_len + 1); + memset((caddr_t)xattr_list, 0, xattr_list_len + 1); /* - * The xattr_name needs to be null terminated for lsetxattr. + * Get the actual list of extended attributes names for a file. */ - current_xattr.name[current_xattr.name_length] = '\0'; + xattr_list_len = extattr_list_link(jcr->last_fname, attrnamespace, xattr_list, xattr_list_len); + if (xattr_list_len < 0) { + switch (errno) { + case ENOENT: + retval = bxattr_exit_ok; + goto bail_out; + default: + Mmsg2(jcr->errmsg, _("extattr_list_link error on file \"%s\": ERR=%s\n"), + jcr->last_fname, be.bstrerror()); + Dmsg2(100, "extattr_list_link error file=%s ERR=%s\n", + jcr->last_fname, be.bstrerror()); + goto bail_out; + } + } + xattr_list[xattr_list_len] = '\0'; /* - * Decode the value length. + * Walk the list of extended attributes names and retrieve the data. + * We already count the bytes needed for serializing the stream later on. */ - unser_uint32(current_xattr.value_length); + for (index = 0; index < xattr_list_len; index += xattr_list[index] + 1) { + skip_xattr = false; + bsnprintf(current_attrname, sizeof(current_attrname), "%*.*s", + xattr_list[index], xattr_list[index], xattr_list + (index + 1)); + + /* + * First make a xattr tuple of the current namespace and the name of the xattr. + * e.g. something like user. or system. + */ + if (extattr_namespace_to_string(attrnamespace, ¤t_attrnamespace) != 0) { + Mmsg2(jcr->errmsg, _("Failed to convert %d into namespace on file \"%s\"\n"), + attrnamespace, jcr->last_fname); + Dmsg2(100, "Failed to convert %d into namespace on file \"%s\"\n", + attrnamespace, jcr->last_fname); + goto bail_out; + } + + /* + * print the current name into the buffer as its not null terminated we need to + * use the length encoded in the string for copying only the needed bytes. + */ + bsnprintf(current_attrtuple, sizeof(current_attrtuple), "%s.%s", current_attrnamespace, current_attrname); + + /* + * On some OSes you also get the acls in the extented attribute list. + * So we check if we are already backing up acls and if we do we + * don't store the extended attribute with the same info. + */ + if (ff_pkt->flags & FO_ACL) { + for (cnt = 0; xattr_acl_skiplist[cnt] != NULL; cnt++) { + if (!strcmp(current_attrtuple, xattr_acl_skiplist[cnt])) { + skip_xattr = true; + break; + } + } + } + + /* + * On some OSes we want to skip certain xattrs which are in the xattr_skiplist array. + */ + if (skip_xattr) { + for (cnt = 0; xattr_skiplist[cnt] != NULL; cnt++) { + if (!strcmp(current_attrtuple, xattr_skiplist[cnt])) { + skip_xattr = true; + break; + } + } + } + + if (skip_xattr) { + continue; + } + + /* + * Each xattr valuepair starts with a magic so we can parse it easier. + */ + current_xattr = (xattr_t *)malloc(sizeof(xattr_t)); + current_xattr->magic = XATTR_MAGIC; + expected_serialize_len += sizeof(current_xattr->magic); + + /* + * Allocate space for storing the name. + */ + current_xattr->name_length = strlen(current_attrtuple); + current_xattr->name = (char *)malloc(current_xattr->name_length); + memcpy((caddr_t)current_xattr->name, (caddr_t)current_attrtuple, current_xattr->name_length); + + expected_serialize_len += sizeof(current_xattr->name_length) + current_xattr->name_length; + + /* + * First see how long the value is for the extended attribute. + */ + xattr_value_len = extattr_get_link(jcr->last_fname, attrnamespace, current_attrname, NULL, 0); + if (xattr_value_len < 0) { + switch (errno) { + case ENOENT: + retval = bxattr_exit_ok; + free(current_xattr->name); + free(current_xattr); + goto bail_out; + default: + Mmsg2(jcr->errmsg, _("extattr_get_link error on file \"%s\": ERR=%s\n"), + jcr->last_fname, be.bstrerror()); + Dmsg2(100, "extattr_get_link error file=%s ERR=%s\n", + jcr->last_fname, be.bstrerror()); + free(current_xattr->name); + free(current_xattr); + goto bail_out; + } + } + + /* + * Allocate space for storing the value. + */ + current_xattr->value = (char *)malloc(xattr_value_len); + memset((caddr_t)current_xattr->value, 0, xattr_value_len); + + xattr_value_len = extattr_get_link(jcr->last_fname, attrnamespace, current_attrname, current_xattr->value, xattr_value_len); + if (xattr_value_len < 0) { + switch (errno) { + case ENOENT: + retval = bxattr_exit_ok; + free(current_xattr->value); + free(current_xattr->name); + free(current_xattr); + goto bail_out; + default: + Mmsg2(jcr->errmsg, _("extattr_get_link error on file \"%s\": ERR=%s\n"), + jcr->last_fname, be.bstrerror()); + Dmsg2(100, "extattr_get_link error file=%s ERR=%s\n", + jcr->last_fname, be.bstrerror()); + free(current_xattr->value); + free(current_xattr->name); + free(current_xattr); + goto bail_out; + } + } + + /* + * Store the actual length of the value. + */ + current_xattr->value_length = xattr_value_len; + expected_serialize_len += sizeof(current_xattr->value_length) + current_xattr->value_length; + + /* + * Protect ourself against things getting out of hand. + */ + if (expected_serialize_len >= MAX_XATTR_STREAM) { + Mmsg2(jcr->errmsg, _("Xattr stream on file \"%s\" exceeds maximum size of %d bytes\n"), + jcr->last_fname, MAX_XATTR_STREAM); + free(current_xattr->value); + free(current_xattr->name); + free(current_xattr); + goto bail_out; + } + + xattr_value_list->append(current_xattr); + xattr_count++; + } /* - * Allocate room for the value and decode its content. + * We are done with this xattr list. */ - current_xattr.value = (char *)malloc(current_xattr.value_length); - unser_bytes(current_xattr.value, current_xattr.value_length); + free(xattr_list); + xattr_list = (char *)NULL; + } + /* + * If we found any xattr send them to the SD. + */ + if (xattr_count > 0) { /* - * Try to set the extended attribute on the file. - * If we fail to set this attribute we flag the error but its not fatal, - * we try to restore the other extended attributes too. + * Serialize the datastream. */ - if (lsetxattr(jcr->last_fname, current_xattr.name, current_xattr.value, - current_xattr.value_length, 0) != 0) { - switch (errno) { - case ENOENT: - break; - default: - Mmsg2(jcr->errmsg, _("lsetxattr error on file \"%s\": ERR=%s\n"), - jcr->last_fname, be.bstrerror()); - Dmsg2(100, "lsetxattr error file=%s ERR=%s\n", - jcr->last_fname, be.bstrerror()); - retval = bxattr_exit_error; - break; - } + if (serialize_xattr_stream(jcr, expected_serialize_len, xattr_value_list) < expected_serialize_len) { + Mmsg1(jcr->errmsg, _("Failed to serialize extended attributes on file \"%s\"\n"), + jcr->last_fname); + Dmsg1(100, "Failed to serialize extended attributes on file \"%s\"\n", + jcr->last_fname); + goto bail_out; } + xattr_drop_internal_table(xattr_value_list); + delete xattr_value_list; + xattr_value_list = NULL; + /* - * Free the temporary buffers. + * Send the datastream to the SD. */ - free(current_xattr.name); - free(current_xattr.value); + return send_xattr_stream(jcr, os_default_xattr_streams[0]); + } else { + xattr_drop_internal_table(xattr_value_list); + delete xattr_value_list; + xattr_value_list = NULL; + + return bxattr_exit_ok; } - unser_end(jcr->xattr_data->content, jcr->xattr_data->content_length); +bail_out: + if (xattr_list) { + free(xattr_list); + } + if (xattr_value_list) { + xattr_drop_internal_table(xattr_value_list); + delete xattr_value_list; + xattr_value_list = NULL; + } return retval; } /* - * For all these os-es setup the build and parse function pointer to the generic functions. + * This function gets called by the unserialize_xattr_stream function for the OS specific + * code to restore an extended attribute on a file. */ -static bxattr_exit_code (*os_build_xattr_streams)(JCR *jcr, FF_PKT *ff_pkt) = generic_xattr_build_streams; -static bxattr_exit_code (*os_parse_xattr_streams)(JCR *jcr, int stream) = generic_xattr_parse_streams; - -#elif defined(HAVE_FREEBSD_OS) || \ - defined(HAVE_NETBSD_OS) || \ - defined(HAVE_OPENBSD_OS) +static bxattr_exit_code restore_xattr_on_file(JCR *jcr, xattr_t *xattr) +{ + berrno be; + int current_attrnamespace, cnt; + char *attrnamespace, *attrname; -#ifdef HAVE_SYS_EXTATTR_H -#include -#endif + /* + * Try splitting the xattr_name into a namespace and name part. + * The splitting character is a . + */ + attrnamespace = xattr->name; + if ((attrname = strchr(attrnamespace, '.')) == (char *)NULL) { + Mmsg2(jcr->errmsg, _("Failed to split %s into namespace and name part on file \"%s\"\n"), + xattr->name, jcr->last_fname); + Dmsg2(100, "Failed to split %s into namespace and name part on file \"%s\"\n", + xattr->name, jcr->last_fname); + return bxattr_exit_error; + } + *attrname++ = '\0'; -static int os_default_xattr_streams[1] = { STREAM_XATTR_FREEBSD }; -static const char *xattr_acl_skiplist[1] = { NULL }; -static const char *xattr_skiplist[1] = { NULL }; + /* + * Make sure the attrnamespace makes sense. + */ + if (extattr_string_to_namespace(attrnamespace, ¤t_attrnamespace) != 0) { + Mmsg2(jcr->errmsg, _("Failed to convert %s into namespace on file \"%s\"\n"), + attrnamespace, jcr->last_fname); + Dmsg2(100, "Failed to convert %s into namespace on file \"%s\"\n", + attrnamespace, jcr->last_fname); + return bxattr_exit_error; + } -static bxattr_exit_code freebsd_build_xattr_streams(JCR *jcr, FF_PKT *ff_pkt) -{ - bxattr_exit_code retval = bxattr_exit_ok; + /* + * Try restoring the extended attribute. + */ + cnt = extattr_set_link(jcr->last_fname, current_attrnamespace, + attrname, xattr->value, xattr->value_length); + if (cnt < 0 || cnt != xattr->value_length) { + switch (errno) { + case ENOENT: + break; + default: + Mmsg2(jcr->errmsg, _("extattr_set_link error on file \"%s\": ERR=%s\n"), + jcr->last_fname, be.bstrerror()); + Dmsg2(100, "extattr_set_link error file=%s ERR=%s\n", + jcr->last_fname, be.bstrerror()); + return bxattr_exit_error; + break; + } + } - return retval; + return bxattr_exit_ok; } -static bxattr_exit_code freebsd_parse_xattr_streams(JCR *jcr, int stream) +static bxattr_exit_code bsd_parse_xattr_streams(JCR *jcr, int stream) { - bxattr_exit_code retval; - - return retval; + return unserialize_xattr_stream(jcr); } /* * Function pointers to the build and parse function to use for these xattrs. */ -static bxattr_exit_code (*os_build_xattr_streams)(JCR *jcr, FF_PKT *ff_pkt) = freebsd_build_xattr_streams; -static bxattr_exit_code (*os_parse_xattr_streams)(JCR *jcr, int stream) = freebsd_parse_xattr_streams; +static bxattr_exit_code (*os_build_xattr_streams)(JCR *jcr, FF_PKT *ff_pkt) = bsd_build_xattr_streams; +static bxattr_exit_code (*os_parse_xattr_streams)(JCR *jcr, int stream) = bsd_parse_xattr_streams; #elif defined(HAVE_SUN_OS) /* @@ -710,6 +1064,13 @@ static bxattr_exit_code (*os_parse_xattr_streams)(JCR *jcr, int stream) = freebs #include #endif +#if !defined(HAVE_OPENAT) || + !defined(HAVE_UNKINKAT) || + !defined(HAVE_FCHOWNAT) || + !defined(HAVE_FUTIMESAT) +#error "Unable to compile code because of missing openat, unlinkat, fchownat or futimesat function" +#endif + /* * Define the supported XATTR streams for this OS */ -- 2.39.5