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