From 4b616fc38ecee1d077083de76cd0ce6fa527d19c Mon Sep 17 00:00:00 2001 From: Marco van Wieringen Date: Fri, 6 Jan 2012 20:42:22 +0100 Subject: [PATCH] Fix bug #1807 Put the restore of xattr and acl streams on a restore stack which gets popped after the actual file properties are restored. As directories are never extracted those are excluded from the delayed restore stack and have their streams processed when we receive them. This way we don't clobber the security info due to executing chown and chmod calls on the restored file. We now first restore the whole file and properties and then all of its acls and extended attributes. We only exclude the Solaris extended attribute streams as we could have multiple of those with the maximum size and that would use to much internal memory. For all other platforms we only have one xattr stream with a maximum of 1 Mb so that doesn't put to much stress on the filed. Solaris extensible attributes are delayed restored. Restoring the Solaris native extended attributes early should be no problem as they are restored into a seperate namingspace and have file like content. Security specific info is stored in extensible attributes and those are delayed restored. ACL streams are all so small that it makes no sense to exclude them and we really want to always extract acl streams after doing a chown and chmod because otherwise we might reset the acl being restored earlier. --- bacula/src/filed/restore.c | 331 +++++++++++++++++++++++++++++++------ bacula/src/filed/restore.h | 13 +- 2 files changed, 295 insertions(+), 49 deletions(-) diff --git a/bacula/src/filed/restore.c b/bacula/src/filed/restore.c index a8b9486d15..1abc791e0a 100644 --- a/bacula/src/filed/restore.c +++ b/bacula/src/filed/restore.c @@ -1,7 +1,7 @@ /* Bacula® - The Network Backup Solution - Copyright (C) 2000-2011 Free Software Foundation Europe e.V. + Copyright (C) 2000-2012 Free Software Foundation Europe e.V. The main author of Bacula is Kern Sibbald, with contributions from many others, a complete list can be found in the file AUTHORS. @@ -88,13 +88,11 @@ const bool have_lzo = true; const bool have_lzo = false; #endif - static void deallocate_cipher(r_ctx &rctx); static void deallocate_fork_cipher(r_ctx &rctx); static void free_signature(r_ctx &rctx); static void free_session(r_ctx &rctx); -static void close_previous_stream(r_ctx &rctx); - +static bool close_previous_stream(JCR *jcr, r_ctx &rctx); static bool verify_signature(JCR *jcr, r_ctx &rctx); int32_t extract_data(JCR *jcr, BFILE *bfd, POOLMEM *buf, int32_t buflen, uint64_t *addr, int flags, int32_t stream, RESTORE_CIPHER_CTX *cipher_ctx); @@ -122,7 +120,7 @@ static int bclose_chksize(JCR *jcr, BFILE *bfd, boffset_t osize) } #ifdef HAVE_DARWIN_OS -bool restore_finderinfo(JCR *jcr, POOLMEM *buf, int32_t buflen) +static bool restore_finderinfo(JCR *jcr, POOLMEM *buf, int32_t buflen) { struct attrlist attrList; @@ -145,12 +143,212 @@ bool restore_finderinfo(JCR *jcr, POOLMEM *buf, int32_t buflen) return true; } #else -bool restore_finderinfo(JCR *jcr, POOLMEM *buf, int32_t buflen) +static bool restore_finderinfo(JCR *jcr, POOLMEM *buf, int32_t buflen) { return true; } #endif +/* + * Cleanup of delayed restore stack with streams for later + * processing. + */ +static inline void drop_delayed_restore_streams(r_ctx &rctx, bool reuse) +{ + RESTORE_DATA_STREAM *rds; + + if (!rctx.delayed_streams || + rctx.delayed_streams->empty()) { + return; + } + + foreach_alist(rds, rctx.delayed_streams) { + free(rds->content); + } + + rctx.delayed_streams->destroy(); + if (reuse) { + rctx.delayed_streams->init(10, owned_by_alist); + } +} + +/* + * Push a data stream onto the delayed restore stack for + * later processing. + */ +static inline void push_delayed_restore_stream(r_ctx &rctx, BSOCK *sd) +{ + RESTORE_DATA_STREAM *rds; + + if (!rctx.delayed_streams) { + rctx.delayed_streams = New(alist(10, owned_by_alist)); + } + + rds = (RESTORE_DATA_STREAM *)malloc(sizeof(RESTORE_DATA_STREAM)); + rds->stream = rctx.stream; + rds->content = (char *)malloc(sd->msglen); + memcpy(rds->content, sd->msg, sd->msglen); + rds->content_length = sd->msglen; + + rctx.delayed_streams->append(rds); +} + +/* + * Perform a restore of an ACL using the stream received. + * This can either be a delayed restore or direct restore. + */ +static inline bool do_restore_acl(JCR *jcr, + int stream) +{ + switch (parse_acl_streams(jcr, stream)) { + case bacl_exit_fatal: + return false; + case bacl_exit_error: + /* + * Non-fatal errors, count them and when the number is under ACL_REPORT_ERR_MAX_PER_JOB + * print the error message set by the lower level routine in jcr->errmsg. + */ + if (jcr->acl_data->nr_errors < ACL_REPORT_ERR_MAX_PER_JOB) { + Jmsg(jcr, M_WARNING, 0, "%s", jcr->errmsg); + } + jcr->acl_data->nr_errors++; + break; + case bacl_exit_ok: + break; + } + return true; +} + +/* + * Perform a restore of an XATTR using the stream received. + * This can either be a delayed restore or direct restore. + */ +static inline bool do_restore_xattr(JCR *jcr, + int stream) +{ + switch (parse_xattr_streams(jcr, stream)) { + case bxattr_exit_fatal: + return false; + case bxattr_exit_error: + /* + * Non-fatal errors, count them and when the number is under XATTR_REPORT_ERR_MAX_PER_JOB + * print the error message set by the lower level routine in jcr->errmsg. + */ + if (jcr->xattr_data->nr_errors < XATTR_REPORT_ERR_MAX_PER_JOB) { + Jmsg(jcr, M_WARNING, 0, "%s", jcr->errmsg); + } + jcr->xattr_data->nr_errors++; + break; + case bxattr_exit_ok: + break; + } + return true; +} + +/* + * Restore any data streams that are restored after the file + * is fully restored and has its attributes restored. Things + * like acls and xattr are restored after we set the file + * attributes otherwise we might clear some security flags + * by setting the attributes. + */ +static inline bool pop_delayed_data_streams(JCR *jcr, r_ctx &rctx) +{ + RESTORE_DATA_STREAM *rds; + + /* + * See if there is anything todo. + */ + if (!rctx.delayed_streams || + rctx.delayed_streams->empty()) { + return true; + } + + /* + * Only process known delayed data streams here. + * If you start using more delayed data streams + * be sure to add them in this loop and add the + * proper calls here. + * + * Currently we support delayed data stream + * processing for the following type of streams: + * - *_ACL_* + * - *_XATTR_* + */ + foreach_alist(rds, rctx.delayed_streams) { + switch (rds->stream) { + case STREAM_UNIX_ACCESS_ACL: + case STREAM_UNIX_DEFAULT_ACL: + case STREAM_ACL_AIX_TEXT: + case STREAM_ACL_DARWIN_ACCESS_ACL: + case STREAM_ACL_FREEBSD_DEFAULT_ACL: + case STREAM_ACL_FREEBSD_ACCESS_ACL: + case STREAM_ACL_HPUX_ACL_ENTRY: + case STREAM_ACL_IRIX_DEFAULT_ACL: + case STREAM_ACL_IRIX_ACCESS_ACL: + case STREAM_ACL_LINUX_DEFAULT_ACL: + case STREAM_ACL_LINUX_ACCESS_ACL: + case STREAM_ACL_TRU64_DEFAULT_ACL: + case STREAM_ACL_TRU64_DEFAULT_DIR_ACL: + case STREAM_ACL_TRU64_ACCESS_ACL: + case STREAM_ACL_SOLARIS_ACLENT: + case STREAM_ACL_SOLARIS_ACE: + case STREAM_ACL_AFS_TEXT: + case STREAM_ACL_AIX_AIXC: + case STREAM_ACL_AIX_NFS4: + case STREAM_ACL_FREEBSD_NFS4_ACL: + pm_memcpy(jcr->acl_data->content, rds->content, rds->content_length); + jcr->acl_data->content_length = rds->content_length; + if (!do_restore_acl(jcr, rds->stream)) { + goto bail_out; + } + free(rds->content); + break; + case STREAM_XATTR_IRIX: + case STREAM_XATTR_TRU64: + case STREAM_XATTR_AIX: + case STREAM_XATTR_OPENBSD: + case STREAM_XATTR_SOLARIS_SYS: + case STREAM_XATTR_DARWIN: + case STREAM_XATTR_FREEBSD: + case STREAM_XATTR_LINUX: + case STREAM_XATTR_NETBSD: + pm_memcpy(jcr->xattr_data->content, rds->content, rds->content_length); + jcr->xattr_data->content_length = rds->content_length; + if (!do_restore_xattr(jcr, rds->stream)) { + goto bail_out; + } + free(rds->content); + break; + default: + Jmsg(jcr, M_WARNING, 0, _("Unknown stream=%d ignored. This shouldn't happen!\n"), + rds->stream); + break; + } + } + + /* + * We processed the stack so we can destroy it. + */ + rctx.delayed_streams->destroy(); + + /* + * (Re)Initialize the stack for a new use. + */ + rctx.delayed_streams->init(10, owned_by_alist); + + return true; + +bail_out: + + /* + * Destroy the content of the stack and (re)initialize it for a new use. + */ + drop_delayed_restore_streams(rctx, true); + + return false; +} + /* * Restore the requested files. */ @@ -328,7 +526,9 @@ void do_restore(JCR *jcr) /* * if any previous stream open, close it */ - close_previous_stream(rctx); + if (!close_previous_stream(jcr, rctx)) { + goto bail_out; + } /* * TODO: manage deleted files @@ -695,27 +895,24 @@ void do_restore(JCR *jcr) * b) and it is not a directory (they are never "extracted") * c) or the file name is empty */ - if ((!rctx.extract && jcr->last_type != FT_DIREND) || (*jcr->last_fname == 0)) { + if ((!rctx.extract && + jcr->last_type != FT_DIREND) || + (*jcr->last_fname == 0)) { break; } if (have_acl) { - pm_memcpy(jcr->acl_data->content, sd->msg, sd->msglen); - jcr->acl_data->content_length = sd->msglen; - switch (parse_acl_streams(jcr, rctx.stream)) { - case bacl_exit_fatal: - goto bail_out; - case bacl_exit_error: - /* - * Non-fatal errors, count them and when the number is under ACL_REPORT_ERR_MAX_PER_JOB - * print the error message set by the lower level routine in jcr->errmsg. - */ - if (jcr->acl_data->nr_errors < ACL_REPORT_ERR_MAX_PER_JOB) { - Jmsg(jcr, M_WARNING, 0, "%s", jcr->errmsg); + /* + * For anything that is not a directory we delay + * the restore of acls till a later stage. + */ + if (jcr->last_type != FT_DIREND) { + push_delayed_restore_stream(rctx, sd); + } else { + pm_memcpy(jcr->acl_data->content, sd->msg, sd->msglen); + jcr->acl_data->content_length = sd->msglen; + if (!do_restore_acl(jcr, rctx.stream)) { + goto bail_out; } - jcr->acl_data->nr_errors++; - break; - case bacl_exit_ok: - break; } } else { non_support_acl++; @@ -727,7 +924,6 @@ void do_restore(JCR *jcr) case STREAM_XATTR_AIX: case STREAM_XATTR_OPENBSD: case STREAM_XATTR_SOLARIS_SYS: - case STREAM_XATTR_SOLARIS: case STREAM_XATTR_DARWIN: case STREAM_XATTR_FREEBSD: case STREAM_XATTR_LINUX: @@ -738,27 +934,47 @@ void do_restore(JCR *jcr) * b) and it is not a directory (they are never "extracted") * c) or the file name is empty */ - if ((!rctx.extract && jcr->last_type != FT_DIREND) || (*jcr->last_fname == 0)) { + if ((!rctx.extract && + jcr->last_type != FT_DIREND) || + (*jcr->last_fname == 0)) { + break; + } + if (have_xattr) { + /* + * For anything that is not a directory we delay + * the restore of xattr till a later stage. + */ + if (jcr->last_type != FT_DIREND) { + push_delayed_restore_stream(rctx, sd); + } else { + pm_memcpy(jcr->xattr_data->content, sd->msg, sd->msglen); + jcr->xattr_data->content_length = sd->msglen; + if (!do_restore_xattr(jcr, rctx.stream)) { + goto bail_out; + } + } + } else { + non_support_xattr++; + } + break; + + case STREAM_XATTR_SOLARIS: + /* + * Do not restore Extended Attributes when + * a) The current file is not extracted + * b) and it is not a directory (they are never "extracted") + * c) or the file name is empty + */ + if ((!rctx.extract && + jcr->last_type != FT_DIREND) || + (*jcr->last_fname == 0)) { break; } if (have_xattr) { pm_memcpy(jcr->xattr_data->content, sd->msg, sd->msglen); jcr->xattr_data->content_length = sd->msglen; - switch (parse_xattr_streams(jcr, rctx.stream)) { - case bxattr_exit_fatal: + if (!do_restore_xattr(jcr, rctx.stream)) { goto bail_out; - case bxattr_exit_error: - /* - * Non-fatal errors, count them and when the number is under XATTR_REPORT_ERR_MAX_PER_JOB - * print the error message set by the lower level routine in jcr->errmsg. - */ - if (jcr->xattr_data->nr_errors < XATTR_REPORT_ERR_MAX_PER_JOB) { - Jmsg(jcr, M_WARNING, 0, "%s", jcr->errmsg); - } - jcr->xattr_data->nr_errors++; - break; - case bxattr_exit_ok: - break; } } else { non_support_xattr++; @@ -797,7 +1013,9 @@ void do_restore(JCR *jcr) break; case STREAM_PLUGIN_NAME: - close_previous_stream(rctx); + if (!close_previous_stream(jcr, rctx)) { + goto bail_out; + } Dmsg1(50, "restore stream_plugin_name=%s\n", sd->msg); plugin_name_stream(jcr, sd->msg); break; @@ -806,7 +1024,9 @@ void do_restore(JCR *jcr) break; /* these are sent by Director */ default: - close_previous_stream(rctx); + if (!close_previous_stream(jcr, rctx)) { + goto bail_out; + } Jmsg(jcr, M_WARNING, 0, _("Unknown stream=%d ignored. This shouldn't happen!\n"), rctx.stream); Dmsg2(0, "Unknown stream=%d data=%s\n", rctx.stream, sd->msg); @@ -822,7 +1042,9 @@ void do_restore(JCR *jcr) bclose_chksize(jcr, &rctx.forkbfd, rctx.fork_size); } - close_previous_stream(rctx); + if (!close_previous_stream(jcr, rctx)) { + goto bail_out; + } jcr->setJobStatus(JS_Terminated); goto ok_out; @@ -916,6 +1138,14 @@ ok_out: jcr->xattr_data = NULL; } + /* + * Free the delayed stream stack list. + */ + if (rctx.delayed_streams) { + drop_delayed_restore_streams(rctx, false); + delete rctx.delayed_streams; + } + bclose(&rctx.forkbfd); bclose(&rctx.bfd); free_attr(rctx.attr); @@ -1112,7 +1342,9 @@ bool sparse_data(JCR *jcr, BFILE *bfd, uint64_t *addr, char **data, uint32_t *le bool decompress_data(JCR *jcr, int32_t stream, char **data, uint32_t *length) { +#if defined(HAVE_LZO) || defined(HAVE_LIBZ) char ec1[50]; /* Buffer printing huge values */ +#endif Dmsg1(200, "Stream found in decompress_data(): %d\n", stream); if(stream == STREAM_COMPRESSED_DATA || stream == STREAM_SPARSE_COMPRESSED_DATA || stream == STREAM_WIN32_COMPRESSED_DATA @@ -1377,11 +1609,10 @@ bail_out: return -1; } - /* * If extracting, close any previous stream */ -static void close_previous_stream(r_ctx &rctx) +static bool close_previous_stream(JCR *jcr, r_ctx &rctx) { /* * If extracting, it was from previous stream, so @@ -1406,6 +1637,13 @@ static void close_previous_stream(r_ctx &rctx) } rctx.extract = false; + /* + * Now perform the delayed restore of some specific data streams. + */ + if (!pop_delayed_data_streams(jcr, rctx)) { + return false; + } + /* * Verify the cryptographic signature, if any */ @@ -1424,8 +1662,9 @@ static void close_previous_stream(r_ctx &rctx) Dmsg0(000, "=== logic error !open\n"); bclose(&rctx.bfd); } -} + return true; +} /* * In the context of jcr, flush any remaining data from the cipher context, diff --git a/bacula/src/filed/restore.h b/bacula/src/filed/restore.h index bb4cfac92d..6a522688e6 100644 --- a/bacula/src/filed/restore.h +++ b/bacula/src/filed/restore.h @@ -29,13 +29,19 @@ #ifndef __RESTORE_H #define __RESTORE_H +struct RESTORE_DATA_STREAM { + int32_t stream; /* stream less new bits */ + char *content; /* stream data */ + uint32_t content_length; /* stream length */ +}; + struct RESTORE_CIPHER_CTX { CIPHER_CONTEXT *cipher; uint32_t block_size; - POOLMEM *buf; /* Pointer to descryption buffer */ - int32_t buf_len; /* Count of bytes currently in buf */ - int32_t packet_len; /* Total bytes in packet */ + POOLMEM *buf; /* Pointer to descryption buffer */ + int32_t buf_len; /* Count of bytes currently in buf */ + int32_t packet_len; /* Total bytes in packet */ }; struct r_ctx { @@ -55,6 +61,7 @@ struct r_ctx { int32_t type; /* file type FT_ */ ATTR *attr; /* Pointer to attributes */ bool extract; /* set when extracting */ + alist *delayed_streams; /* streams that should be restored as last */ SIGNATURE *sig; /* Cryptographic signature (if any) for file */ CRYPTO_SESSION *cs; /* Cryptographic session data (if any) for file */ -- 2.39.5