1 /*
2 BAREOS® - Backup Archiving REcovery Open Sourced
3
4 Copyright (C) 2013-2016 Bareos GmbH & Co. KG
5
6 This program is Free Software; you can redistribute it and/or
7 modify it under the terms of version three of the GNU Affero General Public
8 License as published by the Free Software Foundation and included
9 in the file LICENSE.
10
11 This program is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Affero General Public License for more details.
15
16 You should have received a copy of the GNU Affero General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301, USA.
20 */
21 /**
22 * @file
23 * Windows specific functions.
24 */
25
26 #include "include/bareos.h"
27
28 #if defined(HAVE_WIN32)
29
30 #include "include/jcr.h"
31 #include "findlib/find.h"
32 #include "lib/cbuf.h"
33 #include "findlib/drivetype.h"
34 #include "findlib/fstype.h"
35 #include "win32/findlib/win32.h"
36
37 /**
38 * We need to analyze if a fileset contains onefs=no as option, because only then
39 * we need to snapshot submounted vmps
40 */
win32_onefs_is_disabled(findFILESET * fileset)41 bool win32_onefs_is_disabled(findFILESET *fileset)
42 {
43 findIncludeExcludeItem *incexe;
44
45 for (int i = 0; i < fileset->include_list.size(); i++) {
46 incexe = (findIncludeExcludeItem *)fileset->include_list.get(i);
47 /*
48 * Look through all files and check
49 */
50 for (int j = 0; j < incexe->opts_list.size(); j++) {
51 findFOPTS *fo = (findFOPTS *)incexe->opts_list.get(j);
52 if (BitIsSet(FO_MULTIFS, fo->flags)) {
53 return true;
54 }
55 }
56 }
57
58 return false;
59 }
60
61 /**
62 * For VSS we need to know which windows drives are used, because we create a snapshot
63 * of all used drives. This function returns the number of used drives and fills
64 * szDrives with up to 26 (A..Z) drive names.
65 */
get_win32_driveletters(findFILESET * fileset,char * szDrives)66 int get_win32_driveletters(findFILESET *fileset, char *szDrives)
67 {
68 int i;
69 int nCount;
70 char *fname, ch;
71 char drive[4], dt[16];
72 struct stat sb;
73 dlistString *node;
74 findIncludeExcludeItem *incexe;
75
76 /*
77 * szDrives must be at least 27 bytes long
78 * Can be already filled by plugin, so check that all
79 * letters are in upper case. There should be no duplicates.
80 */
81 for (nCount = 0; nCount < 27 && szDrives[nCount]; nCount++) {
82 szDrives[nCount] = toupper(szDrives[nCount]);
83 }
84
85 /*
86 * First check if there are any non fixed drives in the list
87 * filled by the plugin. VSS can only snapshot fixed drives.
88 */
89 for (nCount = 0; nCount < 27 && szDrives[nCount]; nCount++) {
90 Bsnprintf(drive, sizeof(drive), "%c:\\", szDrives[nCount]);
91 if (Drivetype(drive, dt, sizeof(dt))) {
92 if (bstrcmp(dt, "fixed")) {
93 continue;
94 }
95
96 /*
97 * Inline copy the rest of the string over the current
98 * drive letter.
99 */
100 bstrinlinecpy(szDrives + nCount, szDrives + nCount + 1);
101 }
102 }
103
104 if (fileset) {
105 for (i = 0; i < fileset->include_list.size(); i++) {
106 incexe = (findIncludeExcludeItem *)fileset->include_list.get(i);
107
108 /*
109 * Look through all files and check
110 */
111 foreach_dlist(node, &incexe->name_list) {
112 fname = node->c_str();
113
114 /*
115 * See if the entry doesn't have the FILE_ATTRIBUTE_VOLUME_MOUNT_POINT flag set.
116 */
117 if (stat(fname, &sb) == 0) {
118 if (sb.st_rdev & FILE_ATTRIBUTE_VOLUME_MOUNT_POINT) {
119 continue;
120 }
121 }
122
123 /*
124 * fname should match x:/
125 */
126 if (strlen(fname) >= 2 && B_ISALPHA(fname[0]) && fname[1] == ':') {
127 /*
128 * VSS can only snapshot fixed drives.
129 */
130 bstrncpy(drive, fname, sizeof(drive));
131 drive[2] = '\\';
132 drive[3] = '\0';
133
134 /*
135 * Lookup the drive type.
136 */
137 if (Drivetype(drive, dt, sizeof(dt))) {
138 if (bstrcmp(dt, "fixed")) {
139 /*
140 * Always add in uppercase
141 */
142 ch = toupper(fname[0]);
143
144 /*
145 * If not found in string, add drive letter
146 */
147 if (!strchr(szDrives, ch)) {
148 szDrives[nCount] = ch;
149 szDrives[nCount + 1] = 0;
150 nCount++;
151 }
152 }
153 }
154 }
155 }
156 }
157 }
158
159 return nCount;
160 }
161
162 /**
163 * For VSS we need to know which windows virtual mountpoints are used, because we create
164 * a snapshot of all used drives and virtual mountpoints. This function returns the number
165 * of used virtual mountpoints and fills szVmps with a list of all virtual mountpoints.
166 */
get_win32_virtualmountpoints(findFILESET * fileset,dlist ** szVmps)167 int get_win32_virtualmountpoints(findFILESET *fileset, dlist **szVmps)
168 {
169 int i, cnt;
170 char *fname;
171 struct stat sb;
172 dlistString *node;
173 findIncludeExcludeItem *incexe;
174 POOLMEM *devicename;
175
176 cnt = 0;
177 if (fileset) {
178 devicename = GetPoolMemory(PM_FNAME);
179 for (i = 0; i < fileset->include_list.size(); i++) {
180 incexe = (findIncludeExcludeItem *)fileset->include_list.get(i);
181 /*
182 * Look through all files and check
183 */
184 foreach_dlist(node, &incexe->name_list) {
185 fname = node->c_str();
186
187 /*
188 * See if the entry has the FILE_ATTRIBUTE_VOLUME_MOUNT_POINT flag set.
189 */
190 if (stat(fname, &sb) == 0) {
191 if (!(sb.st_rdev & FILE_ATTRIBUTE_VOLUME_MOUNT_POINT)) {
192 continue;
193 }
194 } else {
195 continue;
196 }
197
198 if (win32_get_vmp_devicename(fname, devicename)) {
199 /*
200 * See if we need to allocate a new dlist.
201 */
202 if (!cnt) {
203 if (!*szVmps) {
204 *szVmps = (dlist *)malloc(sizeof(dlist));
205 (*szVmps)->init();
206 }
207 }
208
209 (*szVmps)->append(new_dlistString(devicename));
210 cnt++;
211 }
212 }
213 }
214 FreePoolMemory(devicename);
215 }
216
217 return cnt;
218 }
219
WantedDriveType(const char * drive,findIncludeExcludeItem * incexe)220 static inline bool WantedDriveType(const char *drive, findIncludeExcludeItem *incexe)
221 {
222 int i,j;
223 char dt[16];
224 findFOPTS *fo;
225 bool done = false;
226 bool wanted = true;
227
228 /*
229 * Lookup the drive type.
230 */
231 if (!Drivetype(drive, dt, sizeof(dt))) {
232 return false;
233 }
234
235 /*
236 * We start the loop with done set to false and wanted
237 * to true so when there are no Drivetype selections we
238 * select any Drivetype.
239 */
240 for (i = 0; !done && i < incexe->opts_list.size(); i++) {
241 fo = (findFOPTS *)incexe->opts_list.get(i);
242
243 /*
244 * If there is any Drivetype selection set the default
245 * selection to false.
246 */
247 if (fo->Drivetype.size()) {
248 wanted = false;
249 }
250
251 for (j = 0; !done && j < fo->Drivetype.size(); j++) {
252 if (Bstrcasecmp(dt, (char *)fo->Drivetype.get(j))) {
253 wanted = true;
254 done = true;
255 }
256 }
257 }
258
259 return wanted;
260 }
261
expand_win32_fileset(findFILESET * fileset)262 bool expand_win32_fileset(findFILESET *fileset)
263 {
264 int i;
265 char *bp;
266 dlistString *node;
267 findIncludeExcludeItem *incexe;
268 char drives[MAX_NAME_LENGTH];
269
270 for (i = 0; i < fileset->include_list.size(); i++) {
271 incexe = (findIncludeExcludeItem *)fileset->include_list.get(i);
272 foreach_dlist(node, &incexe->name_list) {
273 Dmsg1(100, "Checking %s\n", node->c_str());
274 if (bstrcmp(node->c_str(), "/")) {
275 /*
276 * Request for auto expansion but no support for it.
277 */
278 if (!p_GetLogicalDriveStringsA) {
279 return false;
280 }
281
282 /*
283 * we want to add all available local drives to our fileset
284 * if we have "/" specified in the fileset. We want to remove
285 * the "/" pattern itself that gets expanded into all
286 * available drives.
287 */
288 incexe->name_list.remove(node);
289 if (p_GetLogicalDriveStringsA(sizeof(drives), drives) != 0) {
290 bp = drives;
291 while (bp && strlen(bp) > 0) {
292 /*
293 * Apply any Drivetype selection to the currently
294 * processed item.
295 */
296 if (WantedDriveType(bp, incexe)) {
297 if (*(bp + 2) == '\\') {
298 *(bp + 2) = '/'; /* 'x:\' -> 'x:/' */
299 }
300 Dmsg1(100, "adding drive %s\n", bp);
301 incexe->name_list.append(new_dlistString(bp));
302 }
303 if ((bp = strchr(bp, '\0'))) {
304 bp++;
305 }
306 }
307 } else {
308 return false;
309 }
310
311 /*
312 * No need to search further in the include list when we have
313 * found what we were looking for.
314 */
315 break;
316 }
317 }
318 }
319 return true;
320 }
321
CountIncludeListFileEntries(FindFilesPacket * ff)322 static inline int CountIncludeListFileEntries(FindFilesPacket *ff)
323 {
324 int cnt = 0;
325 findFILESET *fileset;
326 findIncludeExcludeItem *incexe;
327
328 fileset = ff->fileset;
329 if (fileset) {
330 for (int i = 0; i < fileset->include_list.size(); i++) {
331 incexe = (findIncludeExcludeItem *)fileset->include_list.get(i);
332 cnt += incexe->name_list.size();
333 }
334 }
335
336 return cnt;
337 }
338
339 /**
340 * Automatically exclude all files and paths defined in Registry Key
341 * "SYSTEM\\CurrentControlSet\\Control\\BackupRestore\\FilesNotToBackup"
342 *
343 * Files/directories with wildcard characters are added to an
344 * options block with flags "exclude=yes" as "wild=dir/file".
345 * This options block is created and prepended by us.
346 *
347 */
348
349 #define MAX_VALUE_NAME 16383
350 #define REGISTRY_KEY "SYSTEM\\CurrentControlSet\\Control\\BackupRestore\\FilesNotToBackup"
351
exclude_win32_not_to_backup_registry_entries(JobControlRecord * jcr,FindFilesPacket * ff)352 bool exclude_win32_not_to_backup_registry_entries(JobControlRecord *jcr, FindFilesPacket *ff)
353 {
354 bool retval = false;
355 uint32_t wild_count = 0;
356 DWORD retCode;
357 HKEY hKey;
358
359 /*
360 * If we do not have "File = " directives (e.g. only plugin calls)
361 * we do not create excludes for the NotForBackup RegKey
362 */
363 if (CountIncludeListFileEntries(ff) == 0 ) {
364 Qmsg(jcr, M_INFO, 1, _("Fileset has no \"File=\" directives, ignoring FilesNotToBackup Registry key\n"));
365 return true;
366 }
367
368 /*
369 * If autoexclude is set to no in fileset options somewhere, we do not
370 * automatically exclude files from FilesNotToBackup Registry Key
371 */
372 for (int i = 0; i < ff->fileset->include_list.size(); i++) {
373 findIncludeExcludeItem *incexe = (findIncludeExcludeItem *)ff->fileset->include_list.get(i);
374
375 for (int j = 0; j < incexe->opts_list.size(); j++) {
376 findFOPTS *fo = (findFOPTS *)incexe->opts_list.get(j);
377
378 if (BitIsSet(FO_NO_AUTOEXCL, fo->flags)) {
379 Qmsg(jcr, M_INFO, 1, _("Fileset has autoexclude disabled, ignoring FilesNotToBackup Registry key\n"));
380 return true;
381 }
382 }
383 }
384
385 retCode = RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT(REGISTRY_KEY), 0, KEY_READ, &hKey);
386 if (retCode == ERROR_SUCCESS ) {
387 PoolMem achClass(PM_MESSAGE),
388 achValue(PM_MESSAGE),
389 dwKeyEn(PM_MESSAGE),
390 expandedKey(PM_MESSAGE),
391 destination(PM_MESSAGE);
392 DWORD cchClassName; /* Size of class string */
393 DWORD cSubKeys = 0; /* Number of subkeys */
394 DWORD cbMaxSubKey; /* Longest subkey size */
395 DWORD cchMaxClass; /* Longest class string */
396 DWORD cValues; /* Number of values for key */
397 DWORD cchMaxValue; /* Longest value name */
398 DWORD cbMaxValueData; /* Longest value data */
399 DWORD cbSecurityDescriptor; /* Size of security descriptor */
400 FILETIME ftLastWriteTime; /* Last write time */
401 DWORD cchValue; /* Size of value string */
402 findIncludeExcludeItem *include;
403
404 /*
405 * Make sure the variable are big enough to contain the data.
406 */
407 achClass.check_size(MAX_PATH);
408
409 /*
410 * Get the class name and the value count.
411 */
412 cchClassName = achClass.size();
413 retCode = RegQueryInfoKey(hKey, /* Key handle */
414 achClass.c_str(), /* Buffer for class name */
415 &cchClassName, /* Size of class string */
416 NULL, /* Reserved */
417 &cSubKeys, /* Number of subkeys */
418 &cbMaxSubKey, /* Longest subkey size */
419 &cchMaxClass, /* Longest class string */
420 &cValues, /* Number of values for this key */
421 &cchMaxValue, /* Longest value name */
422 &cbMaxValueData, /* Longest value data */
423 &cbSecurityDescriptor, /* Security descriptor */
424 &ftLastWriteTime); /* Last write time */
425
426 if (cValues) {
427 findFOPTS *fo;
428
429 /*
430 * Prepare include block to do exclusion via wildcards in options
431 */
432 new_preinclude(ff->fileset);
433
434 include = (findIncludeExcludeItem*)ff->fileset->include_list.get(0);
435
436 if (include->opts_list.size() == 0) {
437 /*
438 * Create new options block in include block for the wildcard excludes
439 */
440 Dmsg0(100, "prepending new options block\n");
441 NewOptions(ff, ff->fileset->incexe);
442 } else {
443 Dmsg0(100, "reusing existing options block\n");
444 }
445 fo = (findFOPTS *)include->opts_list.get(0);
446 SetBit(FO_EXCLUDE, fo->flags); /* exclude = yes */
447 SetBit(FO_IGNORECASE, fo->flags); /* ignore case = yes */
448
449 /*
450 * Make sure the variables are big enough to contain the data.
451 */
452 achValue.check_size(MAX_VALUE_NAME);
453 dwKeyEn.check_size(MAX_PATH);
454 expandedKey.check_size(MAX_PATH);
455 destination.check_size(MAX_PATH);
456
457 for (unsigned int i = 0; i < cValues; i++) {
458 PmStrcpy(achValue, "");
459 cchValue = achValue.size();
460 retCode = RegEnumValue(hKey, i, achValue.c_str(), &cchValue, NULL, NULL, NULL, NULL);
461
462 if (retCode == ERROR_SUCCESS ){
463 DWORD dwLen;
464
465 Dmsg2(100 , "(%d) \"%s\" : \n", i + 1, achValue.c_str());
466
467 dwLen = dwKeyEn.size();
468 retCode = RegQueryValueEx(hKey, achValue.c_str(), 0, NULL, (LPBYTE)dwKeyEn.c_str(), &dwLen);
469 if (retCode == ERROR_SUCCESS) {
470 char *lpValue;
471
472 /*
473 * Iterate over each string, expand the %xxx% variables and
474 * process them for addition to exclude block or wildcard options block
475 */
476 for (lpValue = dwKeyEn.c_str();
477 lpValue && *lpValue;
478 lpValue = strchr(lpValue, '\0') + 1) {
479 char *s, *d;
480
481 ExpandEnvironmentStrings(lpValue, expandedKey.c_str(), expandedKey.size());
482 Dmsg1(100, " \"%s\"\n", expandedKey.c_str());
483
484 /*
485 * Never add single character entries. Probably known buggy DRM Entry in Windows XP / 2003
486 */
487 if (strlen(expandedKey.c_str()) <= 1) {
488 Dmsg0(100, TEXT("Single character entry ignored. Probably reading buggy DRM entry on Windows XP/2003 SP 1 and later\n"));
489 } else {
490 PmStrcpy(destination, "");
491
492 /*
493 * Do all post processing.
494 * - Replace '\' by '/'
495 * - Strip any trailing /s patterns.
496 * - Check if wildcards are used.
497 */
498 s = expandedKey.c_str();
499 d = destination.c_str();
500
501 /*
502 * Begin with \ means all drives
503 */
504 if (*s == '\\') {
505 d += PmStrcpy(destination, "[A-Z]:");
506 }
507
508 while (*s) {
509 switch (*s) {
510 case '\\':
511 *d++ = '/';
512 s++;
513 break;
514 case ' ':
515 if (bstrcmp(s, " /s")) {
516 *d = '\0';
517 *s = '\0';
518 continue;
519 }
520 /* FALLTHROUGH */
521 default:
522 *d++ = *s++;
523 break;
524 }
525 }
526 *d = '\0';
527 Dmsg1(100, " -> \"%s\"\n", destination.c_str() );
528 fo->wild.append(bstrdup(destination.c_str()));
529 wild_count++;
530 }
531 }
532 } else {
533 Dmsg0(100, TEXT("RegGetValue failed \n"));
534 }
535 }
536 }
537 }
538
539 Qmsg(jcr, M_INFO, 0, _("Created %d wildcard excludes from FilesNotToBackup Registry key\n"), wild_count);
540 RegCloseKey(hKey);
541 retval = true;
542 } else {
543 Qmsg(jcr, M_ERROR, 0, _("Failed to open FilesNotToBackup Registry Key\n"));
544 }
545
546 return retval;
547 }
548
549 /**
550 * Windows specific code for restoring EFS data.
551 */
552 struct CopyThreadSaveData {
553 uint32_t data_len; /* Length of Data */
554 POOLMEM *data; /* Data */
555 };
556
557 struct CopyThreadContext {
558 BareosWinFilePacket *bfd; /* Filehandle */
559 int nr_save_elements; /* Number of save items in save_data */
560 CopyThreadSaveData *save_data; /* To save data (cached structure build during restore) */
561 CircularBuffer *cb; /* Circular buffer for passing work to copy thread */
562 bool started; /* Copy thread consuming data */
563 bool flushed; /* Copy thread flushed data */
564 pthread_t thread_id; /* Id of copy thread */
565 pthread_mutex_t lock; /* Lock the structure */
566 pthread_cond_t start; /* Start consuming data from the Circular buffer */
567 pthread_cond_t flush; /* Flush data from the Circular buffer */
568 };
569
570 /**
571 * Callback method for WriteEncryptedFileRaw()
572 */
receive_efs_data(PBYTE pbData,PVOID pvCallbackContext,PULONG ulLength)573 static DWORD WINAPI receive_efs_data(PBYTE pbData, PVOID pvCallbackContext, PULONG ulLength)
574 {
575 CopyThreadSaveData *save_data;
576 CopyThreadContext *context = (CopyThreadContext *)pvCallbackContext;
577
578 /*
579 * Dequeue an item from the circular buffer.
580 */
581 save_data = (CopyThreadSaveData *)context->cb->dequeue();
582
583 if (save_data) {
584 if (save_data->data_len > *ulLength) {
585 Dmsg2(100, "Restore of data bigger then allowed EFS buffer %d vs %d\n", save_data->data_len, *ulLength);
586 *ulLength = 0;
587 } else {
588 memcpy(pbData, save_data->data, save_data->data_len);
589 *ulLength = save_data->data_len;
590 }
591 } else {
592 *ulLength = 0;
593 }
594
595 return ERROR_SUCCESS;
596 }
597
598 /**
599 * Copy thread cancel handler.
600 */
CopyCleanupThread(void * data)601 static void CopyCleanupThread(void *data)
602 {
603 CopyThreadContext *context = (CopyThreadContext *)data;
604
605 pthread_mutex_unlock(&context->lock);
606 }
607
608 /**
609 * Actual copy thread that restores EFS data.
610 */
copy_thread(void * data)611 static void *copy_thread(void *data)
612 {
613 CopyThreadContext *context = (CopyThreadContext *)data;
614
615 if (pthread_mutex_lock(&context->lock) != 0) {
616 goto bail_out;
617 }
618
619 /*
620 * When we get canceled make sure we run the cleanup function.
621 */
622 pthread_cleanup_push(CopyCleanupThread, data);
623
624 while (1) {
625 /*
626 * Wait for the moment we are supposed to start.
627 * We are signalled by the restore thread.
628 */
629 pthread_cond_wait(&context->start, &context->lock);
630 context->started = true;
631
632 pthread_mutex_unlock(&context->lock);
633
634 if (p_WriteEncryptedFileRaw((PFE_IMPORT_FUNC)receive_efs_data, context, context->bfd->pvContext)) {
635 goto bail_out;
636 }
637
638 /*
639 * Need to synchronize the main thread and this one so the main thread cannot miss the conditional signal.
640 */
641 if (pthread_mutex_lock(&context->lock) != 0) {
642 goto bail_out;
643 }
644
645 /*
646 * Signal the main thread we flushed the data and the BFD can be closed.
647 */
648 pthread_cond_signal(&context->flush);
649
650 context->started = false;
651 context->flushed = true;
652
653 }
654
655 pthread_cleanup_pop(1);
656
657 bail_out:
658 return NULL;
659 }
660
661 /**
662 * Create a copy thread that restores the EFS data.
663 */
SetupCopyThread(JobControlRecord * jcr,BareosWinFilePacket * bfd)664 static inline bool SetupCopyThread(JobControlRecord *jcr, BareosWinFilePacket *bfd)
665 {
666 int nr_save_elements;
667 CopyThreadContext *new_context;
668
669 new_context = (CopyThreadContext *)malloc(sizeof(CopyThreadContext));
670 new_context->started = false;
671 new_context->flushed = false;
672 new_context->cb = New(CircularBuffer);
673
674 nr_save_elements = new_context->cb->capacity();
675 new_context->save_data = (CopyThreadSaveData *)malloc(nr_save_elements * sizeof(CopyThreadSaveData));
676 memset(new_context->save_data, 0, nr_save_elements * sizeof(CopyThreadSaveData));
677 new_context->nr_save_elements = nr_save_elements;
678
679 if (pthread_mutex_init(&new_context->lock, NULL) != 0) {
680 goto bail_out;
681 }
682
683 if (pthread_cond_init(&new_context->start, NULL) != 0) {
684 pthread_mutex_destroy(&new_context->lock);
685 goto bail_out;
686 }
687
688 if (pthread_create(&new_context->thread_id, NULL, copy_thread, (void *)new_context) != 0) {
689 pthread_cond_destroy(&new_context->start);
690 pthread_mutex_destroy(&new_context->lock);
691 goto bail_out;
692 }
693
694 jcr->cp_thread = new_context;
695 return true;
696
697 bail_out:
698
699 free(new_context->save_data);
700 delete new_context->cb;
701 free(new_context);
702
703 return false;
704 }
705
706 /**
707 * Send data to the copy thread that restores EFS data.
708 */
win32_send_to_copy_thread(JobControlRecord * jcr,BareosWinFilePacket * bfd,char * data,const int32_t length)709 int win32_send_to_copy_thread(JobControlRecord *jcr, BareosWinFilePacket *bfd, char *data, const int32_t length)
710 {
711 CircularBuffer *cb;
712 int slotnr;
713 CopyThreadSaveData *save_data;
714
715 if (!p_WriteEncryptedFileRaw) {
716 Jmsg0(jcr, M_FATAL, 0, _("Encrypted file restore but no EFS support functions\n"));
717 }
718
719 /*
720 * If no copy thread started do it now.
721 */
722 if (!jcr->cp_thread) {
723 if (!SetupCopyThread(jcr, bfd)) {
724 Jmsg0(jcr, M_FATAL, 0, _("Failed to start encrypted data restore copy thread\n"));
725 return -1;
726 }
727 }
728 cb = jcr->cp_thread->cb;
729
730 /*
731 * See if the bfd changed.
732 */
733 if (jcr->cp_thread->bfd != bfd) {
734 jcr->cp_thread->bfd = bfd;
735 }
736
737 /*
738 * Find out which next slot will be used on the Circular Buffer.
739 * The method will block when the circular buffer is full until a slot is available.
740 */
741 slotnr = cb->NextSlot();
742 save_data = &jcr->cp_thread->save_data[slotnr];
743
744 /*
745 * If this is the first time we use this slot we need to allocate some memory.
746 */
747 if (!save_data->data) {
748 save_data->data = GetPoolMemory(PM_BSOCK);
749 }
750 save_data->data = CheckPoolMemorySize(save_data->data, length + 1);
751 memcpy(save_data->data, data, length);
752 save_data->data_len = length;
753
754 cb->enqueue(save_data);
755
756 /*
757 * Signal the copy thread its time to start if it didn't start yet.
758 */
759 if (!jcr->cp_thread->started) {
760 pthread_cond_signal(&jcr->cp_thread->start);
761 }
762
763 return length;
764 }
765
766 /**
767 * Flush the copy thread so we can close the BFD.
768 */
win32_flush_copy_thread(JobControlRecord * jcr)769 void win32_flush_copy_thread(JobControlRecord *jcr)
770 {
771 CopyThreadContext *context = jcr->cp_thread;
772
773 if (pthread_mutex_lock(&context->lock) != 0) {
774 return;
775 }
776
777 /*
778 * In essence the flush should work in one shot but be a bit more conservative.
779 */
780 while (!context->flushed) {
781 /*
782 * Tell the copy thread to flush out all data.
783 */
784 context->cb->flush();
785
786 /*
787 * Wait for the copy thread to say it flushed the data out.
788 */
789 pthread_cond_wait(&context->flush, &context->lock);
790 }
791
792 context->flushed = false;
793
794 pthread_mutex_unlock(&context->lock);
795 }
796
797 /**
798 * Cleanup all data allocated for the copy thread.
799 */
win32_cleanup_copy_thread(JobControlRecord * jcr)800 void win32_cleanup_copy_thread(JobControlRecord *jcr)
801 {
802 int slotnr;
803
804 /*
805 * Stop the copy thread.
806 */
807 if (!pthread_equal(jcr->cp_thread->thread_id, pthread_self())) {
808 pthread_cancel(jcr->cp_thread->thread_id);
809 pthread_join(jcr->cp_thread->thread_id, NULL);
810 }
811
812 /*
813 * Free all data allocated along the way.
814 */
815 for (slotnr = 0; slotnr < jcr->cp_thread->nr_save_elements; slotnr++) {
816 if (jcr->cp_thread->save_data[slotnr].data) {
817 FreePoolMemory(jcr->cp_thread->save_data[slotnr].data);
818 }
819 }
820 free(jcr->cp_thread->save_data);
821 delete jcr->cp_thread->cb;
822 free(jcr->cp_thread);
823 jcr->cp_thread = NULL;
824 }
825 #endif
826