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