/* * Called here to do a special operation on a variable * op_ptr points to the special operation code (not EOS terminated) * arg_ptr points to argument to special op code * val_ptr points to the value string * out_ptr points to string to be returned */ static var_rc_t operate_var(var_t *var, void *my_ctx, const char *op_ptr, int op_len, const char *arg_ptr, int arg_len, const char *val_ptr, int val_len, char **out_ptr, int *out_len, int *out_size) { COUNTERRES *counter; POOL_MEM buf(PM_NAME); var_rc_t status = VAR_ERR_UNDEFINED_OPERATION; Dmsg0(100, "Enter operate_var\n"); if (!val_ptr) { *out_size = 0; return status; } if (op_len == 3 && bstrncmp(op_ptr, "inc", 3)) { buf.check_size(val_len + 1); pm_memcpy(buf, arg_ptr, val_len); (buf.c_str())[val_len] = 0; Dmsg1(100, "Arg=%s\n", buf.c_str()); pm_memcpy(buf, val_ptr, val_len); (buf.c_str())[val_len] = 0; Dmsg1(100, "Val=%s\n", buf.c_str()); LockRes(); for (counter = NULL; (counter = (COUNTERRES *)GetNextRes(R_COUNTER, (RES *)counter)); ) { if (bstrcmp(counter->name(), buf.c_str())) { Dmsg2(100, "counter=%s val=%s\n", counter->name(), buf.c_str()); break; } } UnlockRes(); return status; } *out_size = 0; return status; }
/** * Save OSX specific resource forks and finder info. */ static inline bool save_rsrc_and_finder(b_save_ctx &bsctx) { char flags[FOPTS_BYTES]; int rsrc_stream; BSOCK *sd = bsctx.jcr->store_bsock; bool retval = false; if (bsctx.ff_pkt->hfsinfo.rsrclength > 0) { if (bopen_rsrc(&bsctx.ff_pkt->bfd, bsctx.ff_pkt->fname, O_RDONLY | O_BINARY, 0) < 0) { bsctx.ff_pkt->ff_errno = errno; berrno be; Jmsg(bsctx.jcr, M_NOTSAVED, -1, _(" Cannot open resource fork for \"%s\": ERR=%s.\n"), bsctx.ff_pkt->fname, be.bstrerror()); bsctx.jcr->JobErrors++; if (is_bopen(&bsctx.ff_pkt->bfd)) { bclose(&bsctx.ff_pkt->bfd); } } else { int status; memcpy(flags, bsctx.ff_pkt->flags, sizeof(flags)); clear_bit(FO_COMPRESS, bsctx.ff_pkt->flags); clear_bit(FO_SPARSE, bsctx.ff_pkt->flags); clear_bit(FO_OFFSETS, bsctx.ff_pkt->flags); rsrc_stream = bit_is_set(FO_ENCRYPT, flags) ? STREAM_ENCRYPTED_MACOS_FORK_DATA : STREAM_MACOS_FORK_DATA; status = send_data(bsctx.jcr, rsrc_stream, bsctx.ff_pkt, bsctx.digest, bsctx.signing_digest); memcpy(bsctx.ff_pkt->flags, flags, sizeof(flags)); bclose(&bsctx.ff_pkt->bfd); if (!status) { goto bail_out; } } } Dmsg1(300, "Saving Finder Info for \"%s\"\n", bsctx.ff_pkt->fname); sd->fsend("%ld %d 0", bsctx.jcr->JobFiles, STREAM_HFSPLUS_ATTRIBUTES); Dmsg1(300, "filed>stored:header %s", sd->msg); pm_memcpy(sd->msg, bsctx.ff_pkt->hfsinfo.fndrinfo, 32); sd->msglen = 32; if (bsctx.digest) { crypto_digest_update(bsctx.digest, (uint8_t *)sd->msg, sd->msglen); } if (bsctx.signing_digest) { crypto_digest_update(bsctx.signing_digest, (uint8_t *)sd->msg, sd->msglen); } sd->send(); sd->signal(BNET_EOD); retval = true; bail_out: return retval; }
/* * Called here from "core" expand code to look up a variable */ static var_rc_t lookup_var(var_t *ctx, void *my_ctx, const char *var_ptr, int var_len, int var_inc, int var_index, const char **val_ptr, int *val_len, int *val_size) { POOL_MEM buf(PM_NAME); char *val, *p, *v; var_rc_t status; int count; /* * Note, if val_size > 0 and val_ptr!=NULL, the core code will free() it */ if ((status = lookup_built_in_var(ctx, my_ctx, var_ptr, var_len, var_index, val_ptr, val_len, val_size)) == VAR_OK) { return VAR_OK; } if ((status = lookup_counter_var(ctx, my_ctx, var_ptr, var_len, var_inc, var_index, val_ptr, val_len, val_size)) == VAR_OK) { return VAR_OK; } /* * Look in environment */ buf.check_size(var_len + 1); pm_memcpy(buf, var_ptr, var_len); (buf.c_str())[var_len] = 0; Dmsg1(100, "Var=%s\n", buf.c_str()); if ((val = getenv(buf.c_str())) == NULL) { return VAR_ERR_UNDEFINED_VARIABLE; } /* * He wants to index the "array" */ count = 1; /* * Find the size of the "array" each element is separated by a | */ for (p = val; *p; p++) { if (*p == '|') { count++; } } Dmsg3(100, "For %s, reqest index=%d have=%d\n", buf.c_str(), var_index, count); /* * -1 => return size of array */ if (var_index == -1) { int len; if (count == 1) { /* if not array */ len = strlen(val); /* return length of string */ } else { len = count; /* else return # array items */ } *val_len = Mmsg(buf, "%d", len); *val_ptr = bstrdup(buf.c_str()); *val_size = 0; /* don't try to free val_ptr */ return VAR_OK; } if (var_index < -1 || var_index > --count) { // return VAR_ERR_SUBMATCH_OUT_OF_RANGE; return VAR_ERR_UNDEFINED_VARIABLE; } /* * Now find the particular item (var_index) he wants */ count = 0; for (p = val; *p; ) { if (*p == '|') { if (count < var_index) { val = ++p; count++; continue; } break; } p++; } buf.check_size(p - val); Dmsg2(100, "val=%s len=%d\n", val, p - val); /* * Make a copy of item, and pass it back */ v = (char *)malloc(p-val+1); memcpy(v, val, p-val); v[p-val] = 0; *val_ptr = v; *val_len = p-val; *val_size = p-val+1; Dmsg1(100, "v=%s\n", v); return VAR_OK; }
/* * Search counter variables */ static var_rc_t lookup_counter_var(var_t *ctx, void *my_ctx, const char *var_ptr, int var_len, int var_inc, int var_index, const char **val_ptr, int *val_len, int *val_size) { COUNTERRES *counter; POOL_MEM buf(PM_NAME); var_rc_t status = VAR_ERR_UNDEFINED_VARIABLE; buf.check_size(var_len + 1); pm_memcpy(buf, var_ptr, var_len); (buf.c_str())[var_len] = 0; LockRes(); for (counter = NULL; (counter = (COUNTERRES *)GetNextRes(R_COUNTER, (RES *)counter)); ) { if (bstrcmp(counter->name(), buf.c_str())) { Dmsg2(100, "Counter=%s val=%d\n", buf.c_str(), counter->CurrentValue); /* * -1 => return size of array */ if (var_index == -1) { Mmsg(buf, "%d", counter->CurrentValue); *val_len = Mmsg(buf, "%d", strlen(buf.c_str())); *val_ptr = bstrdup(buf.c_str()); *val_size = 0; /* don't try to free val_ptr */ return VAR_OK; } else { Mmsg(buf, "%d", counter->CurrentValue); *val_ptr = bstrdup(buf.c_str()); *val_len = strlen(buf.c_str()); *val_size = *val_len + 1; } if (var_inc) { /* increment the variable? */ if (counter->CurrentValue == counter->MaxValue) { counter->CurrentValue = counter->MinValue; } else { counter->CurrentValue++; } if (counter->Catalog) { /* update catalog if need be */ COUNTER_DBR cr; JCR *jcr = (JCR *)my_ctx; memset(&cr, 0, sizeof(cr)); bstrncpy(cr.Counter, counter->name(), sizeof(cr.Counter)); cr.MinValue = counter->MinValue; cr.MaxValue = counter->MaxValue; cr.CurrentValue = counter->CurrentValue; Dmsg1(100, "New value=%d\n", cr.CurrentValue); if (counter->WrapCounter) { bstrncpy(cr.WrapCounter, counter->WrapCounter->name(), sizeof(cr.WrapCounter)); } else { cr.WrapCounter[0] = 0; } if (!db_update_counter_record(jcr, jcr->db, &cr)) { Jmsg(jcr, M_ERROR, 0, _("Count not update counter %s: ERR=%s\n"), counter->name(), db_strerror(jcr->db)); } } } status = VAR_OK; break; } } UnlockRes(); return status; }
/* * Called here by find() for each file included. * This is a callback. The original is find_files() above. * * Send the file and its data to the Storage daemon. * * Returns: 1 if OK * 0 if error * -1 to ignore file/directory (not used here) */ int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level) { bool do_read = false; int stat, data_stream; int rtnstat = 0; DIGEST *digest = NULL; DIGEST *signing_digest = NULL; int digest_stream = STREAM_NONE; SIGNATURE *sig = NULL; bool has_file_data = false; // TODO landonf: Allow the user to specify the digest algorithm #ifdef HAVE_SHA2 crypto_digest_t signing_algorithm = CRYPTO_DIGEST_SHA256; #else crypto_digest_t signing_algorithm = CRYPTO_DIGEST_SHA1; #endif BSOCK *sd = jcr->store_bsock; if (job_canceled(jcr)) { return 0; } jcr->num_files_examined++; /* bump total file count */ switch (ff_pkt->type) { case FT_LNKSAVED: /* Hard linked, file already saved */ Dmsg2(130, "FT_LNKSAVED hard link: %s => %s\n", ff_pkt->fname, ff_pkt->link); break; case FT_REGE: Dmsg1(130, "FT_REGE saving: %s\n", ff_pkt->fname); has_file_data = true; break; case FT_REG: Dmsg1(130, "FT_REG saving: %s\n", ff_pkt->fname); has_file_data = true; break; case FT_LNK: Dmsg2(130, "FT_LNK saving: %s -> %s\n", ff_pkt->fname, ff_pkt->link); break; case FT_DIRBEGIN: jcr->num_files_examined--; /* correct file count */ return 1; /* not used */ case FT_NORECURSE: Jmsg(jcr, M_INFO, 1, _(" Recursion turned off. Will not descend from %s into %s\n"), ff_pkt->top_fname, ff_pkt->fname); ff_pkt->type = FT_DIREND; /* Backup only the directory entry */ break; case FT_NOFSCHG: /* Suppress message for /dev filesystems */ if (!is_in_fileset(ff_pkt)) { Jmsg(jcr, M_INFO, 1, _(" %s is a different filesystem. Will not descend from %s into %s\n"), ff_pkt->fname, ff_pkt->top_fname, ff_pkt->fname); } ff_pkt->type = FT_DIREND; /* Backup only the directory entry */ break; case FT_INVALIDFS: Jmsg(jcr, M_INFO, 1, _(" Disallowed filesystem. Will not descend from %s into %s\n"), ff_pkt->top_fname, ff_pkt->fname); ff_pkt->type = FT_DIREND; /* Backup only the directory entry */ break; case FT_INVALIDDT: Jmsg(jcr, M_INFO, 1, _(" Disallowed drive type. Will not descend into %s\n"), ff_pkt->fname); break; case FT_REPARSE: case FT_DIREND: Dmsg1(130, "FT_DIREND: %s\n", ff_pkt->link); break; case FT_SPEC: Dmsg1(130, "FT_SPEC saving: %s\n", ff_pkt->fname); if (S_ISSOCK(ff_pkt->statp.st_mode)) { Jmsg(jcr, M_SKIPPED, 1, _(" Socket file skipped: %s\n"), ff_pkt->fname); return 1; } break; case FT_RAW: Dmsg1(130, "FT_RAW saving: %s\n", ff_pkt->fname); has_file_data = true; break; case FT_FIFO: Dmsg1(130, "FT_FIFO saving: %s\n", ff_pkt->fname); break; case FT_NOACCESS: { berrno be; Jmsg(jcr, M_NOTSAVED, 0, _(" Could not access \"%s\": ERR=%s\n"), ff_pkt->fname, be.bstrerror(ff_pkt->ff_errno)); jcr->JobErrors++; return 1; } case FT_NOFOLLOW: { berrno be; Jmsg(jcr, M_NOTSAVED, 0, _(" Could not follow link \"%s\": ERR=%s\n"), ff_pkt->fname, be.bstrerror(ff_pkt->ff_errno)); jcr->JobErrors++; return 1; } case FT_NOSTAT: { berrno be; Jmsg(jcr, M_NOTSAVED, 0, _(" Could not stat \"%s\": ERR=%s\n"), ff_pkt->fname, be.bstrerror(ff_pkt->ff_errno)); jcr->JobErrors++; return 1; } case FT_DIRNOCHG: case FT_NOCHG: Jmsg(jcr, M_SKIPPED, 1, _(" Unchanged file skipped: %s\n"), ff_pkt->fname); return 1; case FT_ISARCH: Jmsg(jcr, M_NOTSAVED, 0, _(" Archive file not saved: %s\n"), ff_pkt->fname); return 1; case FT_NOOPEN: { berrno be; Jmsg(jcr, M_NOTSAVED, 0, _(" Could not open directory \"%s\": ERR=%s\n"), ff_pkt->fname, be.bstrerror(ff_pkt->ff_errno)); jcr->JobErrors++; return 1; } default: Jmsg(jcr, M_NOTSAVED, 0, _(" Unknown file type %d; not saved: %s\n"), ff_pkt->type, ff_pkt->fname); jcr->JobErrors++; return 1; } Dmsg1(130, "bfiled: sending %s to stored\n", ff_pkt->fname); /* Digests and encryption are only useful if there's file data */ if (has_file_data) { /* * Setup for digest handling. If this fails, the digest will be set to NULL * and not used. Note, the digest (file hash) can be any one of the four * algorithms below. * * The signing digest is a single algorithm depending on * whether or not we have SHA2. * ****FIXME**** the signing algoritm should really be * determined a different way!!!!!! What happens if * sha2 was available during backup but not restore? */ if (ff_pkt->flags & FO_MD5) { digest = crypto_digest_new(jcr, CRYPTO_DIGEST_MD5); digest_stream = STREAM_MD5_DIGEST; } else if (ff_pkt->flags & FO_SHA1) { digest = crypto_digest_new(jcr, CRYPTO_DIGEST_SHA1); digest_stream = STREAM_SHA1_DIGEST; } else if (ff_pkt->flags & FO_SHA256) { digest = crypto_digest_new(jcr, CRYPTO_DIGEST_SHA256); digest_stream = STREAM_SHA256_DIGEST; } else if (ff_pkt->flags & FO_SHA512) { digest = crypto_digest_new(jcr, CRYPTO_DIGEST_SHA512); digest_stream = STREAM_SHA512_DIGEST; } /* Did digest initialization fail? */ if (digest_stream != STREAM_NONE && digest == NULL) { Jmsg(jcr, M_WARNING, 0, _("%s digest initialization failed\n"), stream_to_ascii(digest_stream)); } /* * Set up signature digest handling. If this fails, the signature digest will be set to * NULL and not used. */ // TODO landonf: We should really only calculate the digest once, for both verification and signing. if (jcr->crypto.pki_sign) { signing_digest = crypto_digest_new(jcr, signing_algorithm); /* Full-stop if a failure occurred initializing the signature digest */ if (signing_digest == NULL) { Jmsg(jcr, M_NOTSAVED, 0, _("%s signature digest initialization failed\n"), stream_to_ascii(signing_algorithm)); jcr->JobErrors++; goto good_rtn; } } /* Enable encryption */ if (jcr->crypto.pki_encrypt) { ff_pkt->flags |= FO_ENCRYPT; } } /* Initialize the file descriptor we use for data and other streams. */ binit(&ff_pkt->bfd); if (ff_pkt->flags & FO_PORTABLE) { set_portable_backup(&ff_pkt->bfd); /* disable Win32 BackupRead() */ } if (ff_pkt->cmd_plugin) { if (!set_cmd_plugin(&ff_pkt->bfd, jcr)) { goto bail_out; } send_plugin_name(jcr, sd, true); /* signal start of plugin data */ } /* Send attributes -- must be done after binit() */ if (!encode_and_send_attributes(jcr, ff_pkt, data_stream)) { goto bail_out; } /* Set up the encryption context and send the session data to the SD */ if (has_file_data && jcr->crypto.pki_encrypt) { if (!crypto_session_send(jcr, sd)) { goto bail_out; } } /* * Open any file with data that we intend to save, then save it. * * Note, if is_win32_backup, we must open the Directory so that * the BackupRead will save its permissions and ownership streams. */ if (ff_pkt->type != FT_LNKSAVED && S_ISREG(ff_pkt->statp.st_mode)) { #ifdef HAVE_WIN32 do_read = !is_portable_backup(&ff_pkt->bfd) || ff_pkt->statp.st_size > 0; #else do_read = ff_pkt->statp.st_size > 0; #endif } else if (ff_pkt->type == FT_RAW || ff_pkt->type == FT_FIFO || ff_pkt->type == FT_REPARSE || (!is_portable_backup(&ff_pkt->bfd) && ff_pkt->type == FT_DIREND)) { do_read = true; } if (ff_pkt->cmd_plugin) { do_read = true; } Dmsg1(400, "do_read=%d\n", do_read); if (do_read) { btimer_t *tid; if (ff_pkt->type == FT_FIFO) { tid = start_thread_timer(jcr, pthread_self(), 60); } else { tid = NULL; } int noatime = ff_pkt->flags & FO_NOATIME ? O_NOATIME : 0; ff_pkt->bfd.reparse_point = ff_pkt->type == FT_REPARSE; if (bopen(&ff_pkt->bfd, ff_pkt->fname, O_RDONLY | O_BINARY | noatime, 0) < 0) { ff_pkt->ff_errno = errno; berrno be; Jmsg(jcr, M_NOTSAVED, 0, _(" Cannot open \"%s\": ERR=%s.\n"), ff_pkt->fname, be.bstrerror()); jcr->JobErrors++; if (tid) { stop_thread_timer(tid); tid = NULL; } goto good_rtn; } if (tid) { stop_thread_timer(tid); tid = NULL; } stat = send_data(jcr, data_stream, ff_pkt, digest, signing_digest); if (ff_pkt->flags & FO_CHKCHANGES) { has_file_changed(jcr, ff_pkt); } bclose(&ff_pkt->bfd); if (!stat) { goto bail_out; } } #ifdef HAVE_DARWIN_OS /* Regular files can have resource forks and Finder Info */ if (ff_pkt->type != FT_LNKSAVED && (S_ISREG(ff_pkt->statp.st_mode) && ff_pkt->flags & FO_HFSPLUS)) { if (ff_pkt->hfsinfo.rsrclength > 0) { int flags; int rsrc_stream; if (!bopen_rsrc(&ff_pkt->bfd, ff_pkt->fname, O_RDONLY | O_BINARY, 0) < 0) { ff_pkt->ff_errno = errno; berrno be; Jmsg(jcr, M_NOTSAVED, -1, _(" Cannot open resource fork for \"%s\": ERR=%s.\n"), ff_pkt->fname, be.bstrerror()); jcr->JobErrors++; if (is_bopen(&ff_pkt->bfd)) { bclose(&ff_pkt->bfd); } goto good_rtn; } flags = ff_pkt->flags; ff_pkt->flags &= ~(FO_GZIP|FO_SPARSE); if (flags & FO_ENCRYPT) { rsrc_stream = STREAM_ENCRYPTED_MACOS_FORK_DATA; } else { rsrc_stream = STREAM_MACOS_FORK_DATA; } stat = send_data(jcr, rsrc_stream, ff_pkt, digest, signing_digest); ff_pkt->flags = flags; bclose(&ff_pkt->bfd); if (!stat) { goto bail_out; } } Dmsg1(300, "Saving Finder Info for \"%s\"\n", ff_pkt->fname); sd->fsend("%ld %d 0", jcr->JobFiles, STREAM_HFSPLUS_ATTRIBUTES); Dmsg1(300, "bfiled>stored:header %s\n", sd->msg); pm_memcpy(sd->msg, ff_pkt->hfsinfo.fndrinfo, 32); sd->msglen = 32; if (digest) { crypto_digest_update(digest, (uint8_t *)sd->msg, sd->msglen); } if (signing_digest) { crypto_digest_update(signing_digest, (uint8_t *)sd->msg, sd->msglen); } sd->send(); sd->signal(BNET_EOD); } #endif /* * Save ACLs for anything not being a symlink and not being a plugin. */ if (!ff_pkt->cmd_plugin) { if (ff_pkt->flags & FO_ACL && ff_pkt->type != FT_LNK) { if (!build_acl_streams(jcr, ff_pkt)) goto bail_out; } } /* * Save Extended Attributes for all files not being a plugin. */ if (!ff_pkt->cmd_plugin) { if (ff_pkt->flags & FO_XATTR) { if (!build_xattr_streams(jcr, ff_pkt)) goto bail_out; } } /* Terminate the signing digest and send it to the Storage daemon */ if (signing_digest) { uint32_t size = 0; if ((sig = crypto_sign_new(jcr)) == NULL) { Jmsg(jcr, M_FATAL, 0, _("Failed to allocate memory for crypto signature.\n")); goto bail_out; } if (!crypto_sign_add_signer(sig, signing_digest, jcr->crypto.pki_keypair)) { Jmsg(jcr, M_FATAL, 0, _("An error occurred while signing the stream.\n")); goto bail_out; } /* Get signature size */ if (!crypto_sign_encode(sig, NULL, &size)) { Jmsg(jcr, M_FATAL, 0, _("An error occurred while signing the stream.\n")); goto bail_out; } /* Grow the bsock buffer to fit our message if necessary */ if (sizeof_pool_memory(sd->msg) < (int32_t)size) { sd->msg = realloc_pool_memory(sd->msg, size); } /* Send our header */ sd->fsend("%ld %ld 0", jcr->JobFiles, STREAM_SIGNED_DIGEST); Dmsg1(300, "bfiled>stored:header %s\n", sd->msg); /* Encode signature data */ if (!crypto_sign_encode(sig, (uint8_t *)sd->msg, &size)) { Jmsg(jcr, M_FATAL, 0, _("An error occurred while signing the stream.\n")); goto bail_out; } sd->msglen = size; sd->send(); sd->signal(BNET_EOD); /* end of checksum */ } /* Terminate any digest and send it to Storage daemon */ if (digest) { uint32_t size; sd->fsend("%ld %d 0", jcr->JobFiles, digest_stream); Dmsg1(300, "bfiled>stored:header %s\n", sd->msg); size = CRYPTO_DIGEST_MAX_SIZE; /* Grow the bsock buffer to fit our message if necessary */ if (sizeof_pool_memory(sd->msg) < (int32_t)size) { sd->msg = realloc_pool_memory(sd->msg, size); } if (!crypto_digest_finalize(digest, (uint8_t *)sd->msg, &size)) { Jmsg(jcr, M_FATAL, 0, _("An error occurred finalizing signing the stream.\n")); goto bail_out; } sd->msglen = size; sd->send(); sd->signal(BNET_EOD); /* end of checksum */ } if (ff_pkt->cmd_plugin) { send_plugin_name(jcr, sd, false); /* signal end of plugin data */ } good_rtn: rtnstat = 1; /* good return */ bail_out: if (digest) { crypto_digest_free(digest); } if (signing_digest) { crypto_digest_free(signing_digest); } if (sig) { crypto_sign_free(sig); } return rtnstat; }