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