]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/win32/filed/vss_generic.cpp
kes Fix Win32 build.
[bacula/bacula] / bacula / src / win32 / filed / vss_generic.cpp
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2005-2007 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from
7    many others, a complete list can be found in the file AUTHORS.
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version two of the GNU General Public
10    License as published by the Free Software Foundation and included
11    in the file LICENSE.
12
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22
23    Bacula® is a registered trademark of John Walker.
24    The licensor of Bacula is the Free Software Foundation Europe
25    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26    Switzerland, email:ftf@fsfeurope.org.
27 */
28 //                              -*- Mode: C++ -*-
29 // vss.cpp -- Interface to Volume Shadow Copies (VSS)
30 //
31 // Copyright transferred from MATRIX-Computer GmbH to
32 //   Kern Sibbald by express permission.
33 //
34 // Author          : Thorsten Engel
35 // Created On      : Fri May 06 21:44:00 2005
36
37
38 #ifdef WIN32_VSS
39
40 #include "bacula.h"
41
42 #undef setlocale
43
44 // STL includes
45 #include <vector>
46 #include <algorithm>
47 #include <string>
48 #include <sstream>
49 #include <fstream>
50 using namespace std;
51
52 #include "ms_atl.h"
53 #include <objbase.h>
54
55 /* 
56  * Kludges to get Vista code to compile.             
57  *  KES - June 2007
58  */
59 #define __in  IN
60 #define __out OUT
61 #define __RPC_unique_pointer
62 #define __RPC_string
63 #define __RPC__out_ecount_part(x, y)
64 #define __RPC__deref_inout_opt
65 #define __RPC__out
66
67 #if !defined(ENABLE_NLS)
68 #define setlocale(p, d)
69 #endif
70
71 #ifdef HAVE_STRSAFE_H
72 // Used for safe string manipulation
73 #include <strsafe.h>
74 #endif
75
76 BOOL VSSPathConvert(const char *szFilePath, char *szShadowPath, int nBuflen);
77 BOOL VSSPathConvertW(const wchar_t *szFilePath, wchar_t *szShadowPath, int nBuflen);
78
79 #ifdef HAVE_MINGW
80 class IXMLDOMDocument;
81 #endif
82
83 /* Reduce compiler warnings from Windows vss code */
84 #define uuid(x)
85
86 #ifdef B_VSS_XP
87    #pragma message("compile VSS for Windows XP")   
88    #define VSSClientGeneric VSSClientXP
89    
90    #include "inc/WinXP/vss.h"
91    #include "inc/WinXP/vswriter.h"
92    #include "inc/WinXP/vsbackup.h"
93
94 #endif
95
96 #ifdef B_VSS_W2K3
97    #pragma message("compile VSS for Windows 2003")
98    #define VSSClientGeneric VSSClient2003
99    
100    #include "inc/Win2003/vss.h"
101    #include "inc/Win2003/vswriter.h"
102    #include "inc/Win2003/vsbackup.h"
103 #endif
104
105 #ifdef B_VSS_VISTA
106    #pragma message("compile VSS for Vista")
107    #define VSSClientGeneric VSSClientVista
108
109    #include "inc/Win2003/vss.h"
110    #include "inc/Win2003/vswriter.h"
111    #include "inc/Win2003/vsbackup.h"
112 #endif
113    
114    /* In VSSAPI.DLL */
115    typedef HRESULT (STDAPICALLTYPE* t_CreateVssBackupComponents)(OUT IVssBackupComponents **);
116    typedef void (APIENTRY* t_VssFreeSnapshotProperties)(IN VSS_SNAPSHOT_PROP*);
117    
118    static t_CreateVssBackupComponents p_CreateVssBackupComponents = NULL;
119    static t_VssFreeSnapshotProperties p_VssFreeSnapshotProperties = NULL;
120
121    #define VSSVBACK_ENTRY "?CreateVssBackupComponents@@YGJPAPAVIVssBackupComponents@@@Z"
122
123
124 #include "vss.h"
125
126 /*  
127  *
128  * some helper functions 
129  *
130  *
131  */
132
133 // Append a backslash to the current string 
134 inline wstring AppendBackslash(wstring str)
135 {
136     if (str.length() == 0)
137         return wstring(L"\\");
138     if (str[str.length() - 1] == L'\\')
139         return str;
140     return str.append(L"\\");
141 }
142
143 // Get the unique volume name for the given path
144 inline wstring GetUniqueVolumeNameForPath(wstring path)
145 {
146     if (path.length() <= 0) {
147        return L"";
148     }
149
150     // Add the backslash termination, if needed
151     path = AppendBackslash(path);
152
153     // Get the root path of the volume
154     wchar_t volumeRootPath[MAX_PATH];
155     wchar_t volumeName[MAX_PATH];
156     wchar_t volumeUniqueName[MAX_PATH];
157
158     if (!p_GetVolumePathNameW || !p_GetVolumePathNameW((LPCWSTR)path.c_str(), volumeRootPath, MAX_PATH))
159       return L"";
160     
161     // Get the volume name alias (might be different from the unique volume name in rare cases)
162     if (!p_GetVolumeNameForVolumeMountPointW || !p_GetVolumeNameForVolumeMountPointW(volumeRootPath, volumeName, MAX_PATH))
163        return L"";
164     
165     // Get the unique volume name    
166     if (!p_GetVolumeNameForVolumeMountPointW(volumeName, volumeUniqueName, MAX_PATH))
167        return L"";
168     
169     return volumeUniqueName;
170 }
171
172
173 // Helper macro for quick treatment of case statements for error codes
174 #define GEN_MERGE(A, B) A##B
175 #define GEN_MAKE_W(A) GEN_MERGE(L, A)
176
177 #define CHECK_CASE_FOR_CONSTANT(value)                      \
178     case value: return (GEN_MAKE_W(#value));
179
180
181 // Convert a writer status into a string
182 inline const wchar_t* GetStringFromWriterStatus(VSS_WRITER_STATE eWriterStatus)
183 {
184     switch (eWriterStatus) {
185     CHECK_CASE_FOR_CONSTANT(VSS_WS_STABLE);
186     CHECK_CASE_FOR_CONSTANT(VSS_WS_WAITING_FOR_FREEZE);
187     CHECK_CASE_FOR_CONSTANT(VSS_WS_WAITING_FOR_THAW);
188     CHECK_CASE_FOR_CONSTANT(VSS_WS_WAITING_FOR_POST_SNAPSHOT);
189     CHECK_CASE_FOR_CONSTANT(VSS_WS_WAITING_FOR_BACKUP_COMPLETE);
190     CHECK_CASE_FOR_CONSTANT(VSS_WS_FAILED_AT_IDENTIFY);
191     CHECK_CASE_FOR_CONSTANT(VSS_WS_FAILED_AT_PREPARE_BACKUP);
192     CHECK_CASE_FOR_CONSTANT(VSS_WS_FAILED_AT_PREPARE_SNAPSHOT);
193     CHECK_CASE_FOR_CONSTANT(VSS_WS_FAILED_AT_FREEZE);
194     CHECK_CASE_FOR_CONSTANT(VSS_WS_FAILED_AT_THAW);
195     CHECK_CASE_FOR_CONSTANT(VSS_WS_FAILED_AT_POST_SNAPSHOT);
196     CHECK_CASE_FOR_CONSTANT(VSS_WS_FAILED_AT_BACKUP_COMPLETE);
197     CHECK_CASE_FOR_CONSTANT(VSS_WS_FAILED_AT_PRE_RESTORE);
198     CHECK_CASE_FOR_CONSTANT(VSS_WS_FAILED_AT_POST_RESTORE);
199     
200     default:
201         return L"Error or Undefined";
202     }
203 }
204
205 // Constructor
206
207 VSSClientGeneric::VSSClientGeneric()
208 {
209    m_hLib = LoadLibraryA("VSSAPI.DLL");
210    if (m_hLib) {      
211       p_CreateVssBackupComponents = (t_CreateVssBackupComponents)
212          GetProcAddress(m_hLib, VSSVBACK_ENTRY);
213                                  
214       p_VssFreeSnapshotProperties = (t_VssFreeSnapshotProperties)
215           GetProcAddress(m_hLib, "VssFreeSnapshotProperties");      
216    } 
217 }
218
219
220
221 // Destructor
222 VSSClientGeneric::~VSSClientGeneric()
223 {
224    if (m_hLib)
225       FreeLibrary(m_hLib);
226 }
227
228 // Initialize the COM infrastructure and the internal pointers
229 BOOL VSSClientGeneric::Initialize(DWORD dwContext, BOOL bDuringRestore)
230 {
231    if (!(p_CreateVssBackupComponents && p_VssFreeSnapshotProperties)) {
232       Dmsg2(0, "VSSClientGeneric::Initialize: p_CreateVssBackupComponents = 0x%08X, p_VssFreeSnapshotProperties = 0x%08X\n", p_CreateVssBackupComponents, p_VssFreeSnapshotProperties);
233       errno = ENOSYS;
234       return FALSE;
235    }
236
237    HRESULT hr;
238    // Initialize COM
239    if (!m_bCoInitializeCalled) {
240       hr = CoInitialize(NULL);
241       if (FAILED(hr)) {
242          Dmsg1(0, "VSSClientGeneric::Initialize: CoInitialize returned 0x%08X\n", hr);
243          errno = b_errno_win32;
244          return FALSE;
245       }
246       m_bCoInitializeCalled = true;
247    }
248
249    // Initialize COM security
250    if (!m_bCoInitializeSecurityCalled) {
251       hr =
252          CoInitializeSecurity(
253          NULL,                           //  Allow *all* VSS writers to communicate back!
254          -1,                             //  Default COM authentication service
255          NULL,                           //  Default COM authorization service
256          NULL,                           //  reserved parameter
257          RPC_C_AUTHN_LEVEL_PKT_PRIVACY,  //  Strongest COM authentication level
258          RPC_C_IMP_LEVEL_IDENTIFY,       //  Minimal impersonation abilities 
259          NULL,                           //  Default COM authentication settings
260          EOAC_NONE,                      //  No special options
261          NULL                            //  Reserved parameter
262          );
263
264       if (FAILED(hr)) {
265          Dmsg1(0, "VSSClientGeneric::Initialize: CoInitializeSecurity returned 0x%08X\n", hr);
266          errno = b_errno_win32;
267          return FALSE;
268       }
269       m_bCoInitializeSecurityCalled = true;
270    }
271
272    // Release the IVssBackupComponents interface 
273    if (m_pVssObject) {
274       m_pVssObject->Release();
275       m_pVssObject = NULL;
276    }
277
278    // Create the internal backup components object
279    hr = p_CreateVssBackupComponents((IVssBackupComponents**) &m_pVssObject);
280    if (FAILED(hr)) {
281       Dmsg1(0, "VSSClientGeneric::Initialize: CreateVssBackupComponents returned 0x%08X\n", hr);
282       errno = b_errno_win32;
283       return FALSE;
284    }
285
286 #if   defined(B_VSS_W2K3) || defined(B_VSS_VISTA)
287    if (dwContext != VSS_CTX_BACKUP) {
288       hr = ((IVssBackupComponents*) m_pVssObject)->SetContext(dwContext);
289       if (FAILED(hr)) {
290          Dmsg1(0, "VSSClientGeneric::Initialize: IVssBackupComponents->SetContext returned 0x%08X\n", hr);
291          errno = b_errno_win32;
292          return FALSE;
293       }
294    }
295 #endif
296
297    if (!bDuringRestore) {
298       // 1. InitializeForBackup
299       hr = ((IVssBackupComponents*) m_pVssObject)->InitializeForBackup();
300       if (FAILED(hr)) {
301          Dmsg1(0, "VSSClientGeneric::Initialize: IVssBackupComponents->InitializeForBackup returned 0x%08X\n", hr);
302          errno = b_errno_win32; 
303          return FALSE;
304       }
305  
306       // 2. SetBackupState
307       hr = ((IVssBackupComponents*) m_pVssObject)->SetBackupState(true, true, VSS_BT_FULL, false);
308       if (FAILED(hr)) {
309          Dmsg1(0, "VSSClientGeneric::Initialize: IVssBackupComponents->SetBackupState returned 0x%08X\n", hr);
310          errno = b_errno_win32;
311          return FALSE;
312       }
313
314       CComPtr<IVssAsync>  pAsync1;
315       // 3. GatherWriterMetaData
316       hr = ((IVssBackupComponents*) m_pVssObject)->GatherWriterMetadata(&pAsync1.p);
317       if (FAILED(hr)) {
318          Dmsg1(0, "VSSClientGeneric::Initialize: IVssBackupComponents->GatherWriterMetadata returned 0x%08X\n", hr);
319          errno = b_errno_win32;
320          return FALSE;
321       }
322       // Waits for the async operation to finish and checks the result
323       WaitAndCheckForAsyncOperation(pAsync1.p);
324    }
325
326    // We are during restore now?
327    m_bDuringRestore = bDuringRestore;
328
329    // Keep the context
330    m_dwContext = dwContext;
331
332    return TRUE;
333 }
334
335
336 BOOL VSSClientGeneric::WaitAndCheckForAsyncOperation(IVssAsync* pAsync)
337 {
338    // Wait until the async operation finishes
339    // unfortunately we can't use a timeout here yet.
340    // the interface would allow it on W2k3,
341    // but it is not implemented yet....
342
343    HRESULT hr;
344
345    // Check the result of the asynchronous operation
346    HRESULT hrReturned = S_OK;
347
348    int timeout = 600; // 10 minutes....
349
350    int queryErrors = 0;
351    do {
352       if (hrReturned != S_OK) 
353          Sleep(1000);
354    
355       hrReturned = S_OK;
356       hr = pAsync->QueryStatus(&hrReturned, NULL);
357    
358       if (FAILED(hr)) 
359          queryErrors++;
360    } while ((timeout-- > 0) && (hrReturned == VSS_S_ASYNC_PENDING));
361
362    if (hrReturned == VSS_S_ASYNC_FINISHED)
363       return TRUE;
364
365    
366 #ifdef DEBUG 
367    // Check if the async operation succeeded...
368    if(hrReturned != VSS_S_ASYNC_FINISHED) {   
369       wchar_t *pwszBuffer = NULL;
370       /* I don't see the usefulness of the following -- KES */
371       DWORD dwRet = ::FormatMessageW(
372                         FORMAT_MESSAGE_ALLOCATE_BUFFER 
373                         | FORMAT_MESSAGE_FROM_SYSTEM 
374                         | FORMAT_MESSAGE_IGNORE_INSERTS,
375                         NULL, hrReturned, 
376                         MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 
377                         (LPWSTR)&pwszBuffer, 0, NULL);
378
379 //    if (dwRet != 0) {         
380          LocalFree(pwszBuffer);         
381 //    }      
382       errno = b_errno_win32;
383    }
384 #endif
385
386    return FALSE;
387 }
388
389 BOOL VSSClientGeneric::CreateSnapshots(char* szDriveLetters)
390 {
391    /* szDriveLetters contains all drive letters in uppercase */
392    /* if a drive can not being added, it's converted to lowercase in szDriveLetters */
393    /* http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vss/base/ivssbackupcomponents_startsnapshotset.asp */
394    
395    if (!m_pVssObject || m_bBackupIsInitialized) {
396       errno = ENOSYS;
397       return FALSE;  
398    }
399
400    m_uidCurrentSnapshotSet = GUID_NULL;
401
402    IVssBackupComponents *pVss = (IVssBackupComponents*)m_pVssObject;
403
404    /* startSnapshotSet */
405
406    pVss->StartSnapshotSet(&m_uidCurrentSnapshotSet);
407
408    /* AddToSnapshotSet */
409
410    wchar_t szDrive[3];
411    szDrive[1] = ':';
412    szDrive[2] = 0;
413
414    wstring volume;
415
416    CComPtr<IVssAsync>  pAsync1;
417    CComPtr<IVssAsync>  pAsync2;   
418    VSS_ID pid;
419
420    for (size_t i=0; i < strlen (szDriveLetters); i++) {
421       szDrive[0] = szDriveLetters[i];
422       volume = GetUniqueVolumeNameForPath(szDrive);
423       // store uniquevolumname
424       if (SUCCEEDED(pVss->AddToSnapshotSet((LPWSTR)volume.c_str(), GUID_NULL, &pid))) {
425          wcsncpy (m_wszUniqueVolumeName[szDriveLetters[i]-'A'], (LPWSTR) volume.c_str(), MAX_PATH);
426       } else {            
427          szDriveLetters[i] = tolower (szDriveLetters[i]);               
428       }
429    }
430
431    /* PrepareForBackup */
432    if (FAILED(pVss->PrepareForBackup(&pAsync1.p))) {      
433       errno = b_errno_win32;
434       return FALSE;   
435    }
436    
437    // Waits for the async operation to finish and checks the result
438    WaitAndCheckForAsyncOperation(pAsync1.p);
439
440    /* get latest info about writer status */
441    if (!CheckWriterStatus()) {
442       errno = b_errno_win32;
443       return FALSE;
444    }
445
446    /* DoSnapShotSet */   
447    if (FAILED(pVss->DoSnapshotSet(&pAsync2.p))) {      
448       errno = b_errno_win32;
449       return FALSE;   
450    }
451
452    // Waits for the async operation to finish and checks the result
453    WaitAndCheckForAsyncOperation(pAsync2.p); 
454    
455    /* query snapshot info */   
456    QuerySnapshotSet(m_uidCurrentSnapshotSet);
457
458    SetVSSPathConvert(VSSPathConvert, VSSPathConvertW);
459
460    m_bBackupIsInitialized = true;
461
462    return TRUE;
463 }
464
465 BOOL VSSClientGeneric::CloseBackup()
466 {
467    BOOL bRet = FALSE;
468    if (!m_pVssObject)
469       errno = ENOSYS;
470    else {
471       IVssBackupComponents* pVss = (IVssBackupComponents*) m_pVssObject;
472       CComPtr<IVssAsync>  pAsync;
473
474       SetVSSPathConvert(NULL, NULL);
475
476       m_bBackupIsInitialized = false;
477
478       if (SUCCEEDED(pVss->BackupComplete(&pAsync.p))) {
479          // Waits for the async operation to finish and checks the result
480          WaitAndCheckForAsyncOperation(pAsync.p);
481          bRet = TRUE;     
482       } else {
483          errno = b_errno_win32;
484          pVss->AbortBackup();
485       }
486
487       /* get latest info about writer status */
488       CheckWriterStatus();
489
490       if (m_uidCurrentSnapshotSet != GUID_NULL) {
491          VSS_ID idNonDeletedSnapshotID = GUID_NULL;
492          LONG lSnapshots;
493
494          pVss->DeleteSnapshots(
495             m_uidCurrentSnapshotSet, 
496             VSS_OBJECT_SNAPSHOT_SET,
497             FALSE,
498             &lSnapshots,
499             &idNonDeletedSnapshotID);
500
501          m_uidCurrentSnapshotSet = GUID_NULL;
502       }
503
504       pVss->Release();
505       m_pVssObject = NULL;
506    }
507
508    // Call CoUninitialize if the CoInitialize was performed sucesfully
509    if (m_bCoInitializeCalled) {
510       CoUninitialize();
511       m_bCoInitializeCalled = false;
512    }
513
514    return bRet;
515 }
516
517 // Query all the shadow copies in the given set
518 void VSSClientGeneric::QuerySnapshotSet(GUID snapshotSetID)
519 {   
520    if (!(p_CreateVssBackupComponents && p_VssFreeSnapshotProperties)) {
521       errno = ENOSYS;
522       return;
523    }
524
525    memset (m_szShadowCopyName,0,sizeof (m_szShadowCopyName));
526    
527    if (snapshotSetID == GUID_NULL || m_pVssObject == NULL) {
528       errno = ENOSYS;
529       return;
530    }
531
532    IVssBackupComponents* pVss = (IVssBackupComponents*) m_pVssObject;
533                
534    // Get list all shadow copies. 
535    CComPtr<IVssEnumObject> pIEnumSnapshots;
536    HRESULT hr = pVss->Query( GUID_NULL, 
537          VSS_OBJECT_NONE, 
538          VSS_OBJECT_SNAPSHOT, 
539          (IVssEnumObject**)(&pIEnumSnapshots) );    
540
541    // If there are no shadow copies, just return
542    if (FAILED(hr)) {
543       errno = b_errno_win32;
544       return;   
545    }
546
547    // Enumerate all shadow copies. 
548    VSS_OBJECT_PROP Prop;
549    VSS_SNAPSHOT_PROP& Snap = Prop.Obj.Snap;
550    
551    while (true) {
552       // Get the next element
553       ULONG ulFetched;
554       hr = (pIEnumSnapshots.p)->Next( 1, &Prop, &ulFetched );
555
556       // We reached the end of list
557       if (ulFetched == 0)
558          break;
559
560       // Print the shadow copy (if not filtered out)
561       if (Snap.m_SnapshotSetId == snapshotSetID)  {
562          for (int ch='A'-'A';ch<='Z'-'A';ch++) {
563             if (wcscmp(Snap.m_pwszOriginalVolumeName, m_wszUniqueVolumeName[ch]) == 0) {       
564                wcsncpy(m_szShadowCopyName[ch],Snap.m_pwszSnapshotDeviceObject, MAX_PATH-1);               
565                break;
566             }
567          }
568       }
569       p_VssFreeSnapshotProperties(&Snap);
570    }
571    errno = 0;
572 }
573
574 // Check the status for all selected writers
575 BOOL VSSClientGeneric::CheckWriterStatus()
576 {
577     /* 
578     http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vss/base/ivssbackupcomponents_startsnapshotset.asp
579     */
580     IVssBackupComponents* pVss = (IVssBackupComponents*) m_pVssObject;
581     DestroyWriterInfo();
582
583     // Gather writer status to detect potential errors
584     CComPtr<IVssAsync>  pAsync;
585     
586     HRESULT hr = pVss->GatherWriterStatus(&pAsync.p);
587     if (FAILED(hr)) {
588        errno = b_errno_win32;
589        return FALSE;
590     } 
591
592     // Waits for the async operation to finish and checks the result
593     WaitAndCheckForAsyncOperation(pAsync.p);
594       
595     unsigned cWriters = 0;
596
597     hr = pVss->GetWriterStatusCount(&cWriters);
598     if (FAILED(hr)) {
599        errno = b_errno_win32;
600        return FALSE;
601     }
602
603     int nState;
604     
605     // Enumerate each writer
606     for (unsigned iWriter = 0; iWriter < cWriters; iWriter++) {
607         VSS_ID idInstance = GUID_NULL;
608         VSS_ID idWriter= GUID_NULL;
609         VSS_WRITER_STATE eWriterStatus = VSS_WS_UNKNOWN;
610         CComBSTR bstrWriterName;
611         HRESULT hrWriterFailure = S_OK;
612
613         // Get writer status
614         hr = pVss->GetWriterStatus(iWriter,
615                              &idInstance,
616                              &idWriter,
617                              &bstrWriterName,
618                              &eWriterStatus,
619                              &hrWriterFailure);
620         if (FAILED(hr)) {
621             /* unknown */            
622             nState = 0;
623         }
624         else {            
625             switch(eWriterStatus) {
626             case VSS_WS_FAILED_AT_IDENTIFY:
627             case VSS_WS_FAILED_AT_PREPARE_BACKUP:
628             case VSS_WS_FAILED_AT_PREPARE_SNAPSHOT:
629             case VSS_WS_FAILED_AT_FREEZE:
630             case VSS_WS_FAILED_AT_THAW:
631             case VSS_WS_FAILED_AT_POST_SNAPSHOT:
632             case VSS_WS_FAILED_AT_BACKUP_COMPLETE:
633             case VSS_WS_FAILED_AT_PRE_RESTORE:
634             case VSS_WS_FAILED_AT_POST_RESTORE:
635     #if  defined(B_VSS_W2K3) || defined(B_VSS_VISTA)
636             case VSS_WS_FAILED_AT_BACKUPSHUTDOWN:
637     #endif
638                 /* failed */                
639                 nState = -1;
640                 break;
641
642             default:
643                 /* ok */
644                 nState = 1;
645             }
646         }
647         /* store text info */
648         char str[1000];
649         char szBuf[200];        
650         bstrncpy(str, "\"", sizeof(str));
651         wchar_2_UTF8(szBuf, bstrWriterName.p, sizeof(szBuf));
652         bstrncat(str, szBuf, sizeof(str));
653         bstrncat(str, "\", State: 0x", sizeof(str));
654         itoa(eWriterStatus, szBuf, sizeof(szBuf));
655         bstrncat(str, szBuf, sizeof(str));
656         bstrncat(str, " (", sizeof(str));
657         wchar_2_UTF8(szBuf, GetStringFromWriterStatus(eWriterStatus), sizeof(szBuf));
658         bstrncat(str, szBuf, sizeof(str));
659         bstrncat(str, ")", sizeof(str));
660
661         AppendWriterInfo(nState, (const char *)str);     
662     }
663
664     hr = pVss->FreeWriterStatus();
665
666     if (FAILED(hr)) {
667         errno = b_errno_win32;
668         return FALSE;
669     } 
670
671     errno = 0;
672     return TRUE;
673 }
674
675 #endif /* WIN32_VSS */