]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/win32/filed/vss_generic.cpp
moved fdplugins directory to under bin in the winbacula installer
[bacula/bacula] / bacula / src / win32 / filed / vss_generic.cpp
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2005-2008 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 Kern Sibbald.
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
122
123 #include "vss.h"
124
125 /*  
126  *
127  * some helper functions 
128  *
129  *
130  */
131
132 // Append a backslash to the current string 
133 inline wstring AppendBackslash(wstring str)
134 {
135     if (str.length() == 0)
136         return wstring(L"\\");
137     if (str[str.length() - 1] == L'\\')
138         return str;
139     return str.append(L"\\");
140 }
141
142 // Get the unique volume name for the given path
143 inline wstring GetUniqueVolumeNameForPath(wstring path)
144 {
145     if (path.length() <= 0) {
146        return L"";
147     }
148
149     // Add the backslash termination, if needed
150     path = AppendBackslash(path);
151
152     // Get the root path of the volume
153     wchar_t volumeRootPath[MAX_PATH];
154     wchar_t volumeName[MAX_PATH];
155     wchar_t volumeUniqueName[MAX_PATH];
156
157     if (!p_GetVolumePathNameW || !p_GetVolumePathNameW((LPCWSTR)path.c_str(), volumeRootPath, MAX_PATH))
158       return L"";
159     
160     // Get the volume name alias (might be different from the unique volume name in rare cases)
161     if (!p_GetVolumeNameForVolumeMountPointW || !p_GetVolumeNameForVolumeMountPointW(volumeRootPath, volumeName, MAX_PATH))
162        return L"";
163     
164     // Get the unique volume name    
165     if (!p_GetVolumeNameForVolumeMountPointW(volumeName, volumeUniqueName, MAX_PATH))
166        return L"";
167     
168     return volumeUniqueName;
169 }
170
171
172 // Helper macro for quick treatment of case statements for error codes
173 #define GEN_MERGE(A, B) A##B
174 #define GEN_MAKE_W(A) GEN_MERGE(L, A)
175
176 #define CHECK_CASE_FOR_CONSTANT(value)                      \
177     case value: return (GEN_MAKE_W(#value));
178
179
180 // Convert a writer status into a string
181 inline const wchar_t* GetStringFromWriterStatus(VSS_WRITER_STATE eWriterStatus)
182 {
183     switch (eWriterStatus) {
184     CHECK_CASE_FOR_CONSTANT(VSS_WS_STABLE);
185     CHECK_CASE_FOR_CONSTANT(VSS_WS_WAITING_FOR_FREEZE);
186     CHECK_CASE_FOR_CONSTANT(VSS_WS_WAITING_FOR_THAW);
187     CHECK_CASE_FOR_CONSTANT(VSS_WS_WAITING_FOR_POST_SNAPSHOT);
188     CHECK_CASE_FOR_CONSTANT(VSS_WS_WAITING_FOR_BACKUP_COMPLETE);
189     CHECK_CASE_FOR_CONSTANT(VSS_WS_FAILED_AT_IDENTIFY);
190     CHECK_CASE_FOR_CONSTANT(VSS_WS_FAILED_AT_PREPARE_BACKUP);
191     CHECK_CASE_FOR_CONSTANT(VSS_WS_FAILED_AT_PREPARE_SNAPSHOT);
192     CHECK_CASE_FOR_CONSTANT(VSS_WS_FAILED_AT_FREEZE);
193     CHECK_CASE_FOR_CONSTANT(VSS_WS_FAILED_AT_THAW);
194     CHECK_CASE_FOR_CONSTANT(VSS_WS_FAILED_AT_POST_SNAPSHOT);
195     CHECK_CASE_FOR_CONSTANT(VSS_WS_FAILED_AT_BACKUP_COMPLETE);
196     CHECK_CASE_FOR_CONSTANT(VSS_WS_FAILED_AT_PRE_RESTORE);
197     CHECK_CASE_FOR_CONSTANT(VSS_WS_FAILED_AT_POST_RESTORE);
198     
199     default:
200         return L"Error or Undefined";
201     }
202 }
203
204 // Constructor
205
206 /* 32 bit entrypoint name */
207 #define VSSVBACK_ENTRY "?CreateVssBackupComponents@@YGJPAPAVIVssBackupComponents@@@Z"
208 /* 64 bit entrypoint name */
209 #define VSSVBACK64_ENTRY "?CreateVssBackupComponents@@YAJPEAPEAVIVssBackupComponents@@@Z"
210
211
212 VSSClientGeneric::VSSClientGeneric()
213 {
214    m_hLib = LoadLibraryA("VSSAPI.DLL");
215    if (m_hLib) {      
216       p_CreateVssBackupComponents = (t_CreateVssBackupComponents)
217          GetProcAddress(m_hLib, VSSVBACK_ENTRY);
218       /* If we don't find it try the 64 bit entry point */
219       if (!p_CreateVssBackupComponents) {
220          p_CreateVssBackupComponents = (t_CreateVssBackupComponents)
221            GetProcAddress(m_hLib, VSSVBACK64_ENTRY);
222       }
223       p_VssFreeSnapshotProperties = (t_VssFreeSnapshotProperties)
224           GetProcAddress(m_hLib, "VssFreeSnapshotProperties");      
225    } 
226 }
227
228
229
230 // Destructor
231 VSSClientGeneric::~VSSClientGeneric()
232 {
233    if (m_hLib)
234       FreeLibrary(m_hLib);
235 }
236
237 // Initialize the COM infrastructure and the internal pointers
238 BOOL VSSClientGeneric::Initialize(DWORD dwContext, BOOL bDuringRestore)
239 {
240    if (!(p_CreateVssBackupComponents && p_VssFreeSnapshotProperties)) {
241       Dmsg2(0, "VSSClientGeneric::Initialize: p_CreateVssBackupComponents = 0x%08X, p_VssFreeSnapshotProperties = 0x%08X\n", p_CreateVssBackupComponents, p_VssFreeSnapshotProperties);
242       errno = ENOSYS;
243       return FALSE;
244    }
245
246    HRESULT hr;
247    // Initialize COM
248    if (!m_bCoInitializeCalled) {
249       hr = CoInitialize(NULL);
250       if (FAILED(hr)) {
251          Dmsg1(0, "VSSClientGeneric::Initialize: CoInitialize returned 0x%08X\n", hr);
252          errno = b_errno_win32;
253          return FALSE;
254       }
255       m_bCoInitializeCalled = true;
256    }
257
258    // Initialize COM security
259    if (!m_bCoInitializeSecurityCalled) {
260       hr =
261          CoInitializeSecurity(
262          NULL,                           //  Allow *all* VSS writers to communicate back!
263          -1,                             //  Default COM authentication service
264          NULL,                           //  Default COM authorization service
265          NULL,                           //  reserved parameter
266          RPC_C_AUTHN_LEVEL_PKT_PRIVACY,  //  Strongest COM authentication level
267          RPC_C_IMP_LEVEL_IDENTIFY,       //  Minimal impersonation abilities 
268          NULL,                           //  Default COM authentication settings
269          EOAC_NONE,                      //  No special options
270          NULL                            //  Reserved parameter
271          );
272
273       if (FAILED(hr)) {
274          Dmsg1(0, "VSSClientGeneric::Initialize: CoInitializeSecurity returned 0x%08X\n", hr);
275          errno = b_errno_win32;
276          return FALSE;
277       }
278       m_bCoInitializeSecurityCalled = true;
279    }
280
281    // Release the IVssBackupComponents interface 
282    if (m_pVssObject) {
283       m_pVssObject->Release();
284       m_pVssObject = NULL;
285    }
286
287    // Create the internal backup components object
288    hr = p_CreateVssBackupComponents((IVssBackupComponents**) &m_pVssObject);
289    if (FAILED(hr)) {
290       Dmsg1(0, "VSSClientGeneric::Initialize: CreateVssBackupComponents returned 0x%08X\n", hr);
291       errno = b_errno_win32;
292       return FALSE;
293    }
294
295 #if   defined(B_VSS_W2K3) || defined(B_VSS_VISTA)
296    if (dwContext != VSS_CTX_BACKUP) {
297       hr = ((IVssBackupComponents*) m_pVssObject)->SetContext(dwContext);
298       if (FAILED(hr)) {
299          Dmsg1(0, "VSSClientGeneric::Initialize: IVssBackupComponents->SetContext returned 0x%08X\n", hr);
300          errno = b_errno_win32;
301          return FALSE;
302       }
303    }
304 #endif
305
306    if (!bDuringRestore) {
307       // 1. InitializeForBackup
308       hr = ((IVssBackupComponents*) m_pVssObject)->InitializeForBackup();
309       if (FAILED(hr)) {
310          Dmsg1(0, "VSSClientGeneric::Initialize: IVssBackupComponents->InitializeForBackup returned 0x%08X\n", hr);
311          errno = b_errno_win32; 
312          return FALSE;
313       }
314  
315       // 2. SetBackupState
316       hr = ((IVssBackupComponents*) m_pVssObject)->SetBackupState(true, true, VSS_BT_FULL, false);
317       if (FAILED(hr)) {
318          Dmsg1(0, "VSSClientGeneric::Initialize: IVssBackupComponents->SetBackupState returned 0x%08X\n", hr);
319          errno = b_errno_win32;
320          return FALSE;
321       }
322
323       CComPtr<IVssAsync>  pAsync1;
324       // 3. GatherWriterMetaData
325       hr = ((IVssBackupComponents*) m_pVssObject)->GatherWriterMetadata(&pAsync1.p);
326       if (FAILED(hr)) {
327          Dmsg1(0, "VSSClientGeneric::Initialize: IVssBackupComponents->GatherWriterMetadata returned 0x%08X\n", hr);
328          errno = b_errno_win32;
329          return FALSE;
330       }
331       // Waits for the async operation to finish and checks the result
332       WaitAndCheckForAsyncOperation(pAsync1.p);
333    }
334
335    // We are during restore now?
336    m_bDuringRestore = bDuringRestore;
337
338    // Keep the context
339    m_dwContext = dwContext;
340
341    return TRUE;
342 }
343
344
345 BOOL VSSClientGeneric::WaitAndCheckForAsyncOperation(IVssAsync* pAsync)
346 {
347    // Wait until the async operation finishes
348    // unfortunately we can't use a timeout here yet.
349    // the interface would allow it on W2k3,
350    // but it is not implemented yet....
351
352    HRESULT hr;
353
354    // Check the result of the asynchronous operation
355    HRESULT hrReturned = S_OK;
356
357    int timeout = 600; // 10 minutes....
358
359    int queryErrors = 0;
360    do {
361       if (hrReturned != S_OK) 
362          Sleep(1000);
363    
364       hrReturned = S_OK;
365       hr = pAsync->QueryStatus(&hrReturned, NULL);
366    
367       if (FAILED(hr)) 
368          queryErrors++;
369    } while ((timeout-- > 0) && (hrReturned == VSS_S_ASYNC_PENDING));
370
371    if (hrReturned == VSS_S_ASYNC_FINISHED)
372       return TRUE;
373
374    
375 #ifdef xDEBUG 
376    // Check if the async operation succeeded...
377    if(hrReturned != VSS_S_ASYNC_FINISHED) {   
378       wchar_t *pwszBuffer = NULL;
379       /* I don't see the usefulness of the following -- KES */
380       FormatMessageW(
381          FORMAT_MESSAGE_ALLOCATE_BUFFER 
382          | FORMAT_MESSAGE_FROM_SYSTEM 
383          | FORMAT_MESSAGE_IGNORE_INSERTS,
384          NULL, hrReturned, 
385          MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 
386          (LPWSTR)&pwszBuffer, 0, NULL);
387
388       LocalFree(pwszBuffer);         
389       errno = b_errno_win32;
390    }
391 #endif
392
393    return FALSE;
394 }
395
396 BOOL VSSClientGeneric::CreateSnapshots(char* szDriveLetters)
397 {
398    /* szDriveLetters contains all drive letters in uppercase */
399    /* if a drive can not being added, it's converted to lowercase in szDriveLetters */
400    /* http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vss/base/ivssbackupcomponents_startsnapshotset.asp */
401    
402    if (!m_pVssObject || m_bBackupIsInitialized) {
403       errno = ENOSYS;
404       return FALSE;  
405    }
406
407    m_uidCurrentSnapshotSet = GUID_NULL;
408
409    IVssBackupComponents *pVss = (IVssBackupComponents*)m_pVssObject;
410
411    /* startSnapshotSet */
412
413    pVss->StartSnapshotSet(&m_uidCurrentSnapshotSet);
414
415    /* AddToSnapshotSet */
416
417    wchar_t szDrive[3];
418    szDrive[1] = ':';
419    szDrive[2] = 0;
420
421    wstring volume;
422
423    CComPtr<IVssAsync>  pAsync1;
424    CComPtr<IVssAsync>  pAsync2;   
425    VSS_ID pid;
426
427    for (size_t i=0; i < strlen (szDriveLetters); i++) {
428       szDrive[0] = szDriveLetters[i];
429       volume = GetUniqueVolumeNameForPath(szDrive);
430       // store uniquevolumname
431       if (SUCCEEDED(pVss->AddToSnapshotSet((LPWSTR)volume.c_str(), GUID_NULL, &pid))) {
432          wcsncpy (m_wszUniqueVolumeName[szDriveLetters[i]-'A'], (LPWSTR) volume.c_str(), MAX_PATH);
433       } else {            
434          szDriveLetters[i] = tolower (szDriveLetters[i]);               
435       }
436    }
437
438    /* PrepareForBackup */
439    if (FAILED(pVss->PrepareForBackup(&pAsync1.p))) {      
440       errno = b_errno_win32;
441       return FALSE;   
442    }
443    
444    // Waits for the async operation to finish and checks the result
445    WaitAndCheckForAsyncOperation(pAsync1.p);
446
447    /* get latest info about writer status */
448    if (!CheckWriterStatus()) {
449       errno = b_errno_win32;
450       return FALSE;
451    }
452
453    /* DoSnapShotSet */   
454    if (FAILED(pVss->DoSnapshotSet(&pAsync2.p))) {      
455       errno = b_errno_win32;
456       return FALSE;   
457    }
458
459    // Waits for the async operation to finish and checks the result
460    WaitAndCheckForAsyncOperation(pAsync2.p); 
461    
462    /* query snapshot info */   
463    QuerySnapshotSet(m_uidCurrentSnapshotSet);
464
465    SetVSSPathConvert(VSSPathConvert, VSSPathConvertW);
466
467    m_bBackupIsInitialized = true;
468
469    return TRUE;
470 }
471
472 BOOL VSSClientGeneric::CloseBackup()
473 {
474    BOOL bRet = FALSE;
475    if (!m_pVssObject)
476       errno = ENOSYS;
477    else {
478       IVssBackupComponents* pVss = (IVssBackupComponents*) m_pVssObject;
479       CComPtr<IVssAsync>  pAsync;
480
481       SetVSSPathConvert(NULL, NULL);
482
483       m_bBackupIsInitialized = false;
484
485       if (SUCCEEDED(pVss->BackupComplete(&pAsync.p))) {
486          // Waits for the async operation to finish and checks the result
487          WaitAndCheckForAsyncOperation(pAsync.p);
488          bRet = TRUE;     
489       } else {
490          errno = b_errno_win32;
491          pVss->AbortBackup();
492       }
493
494       /* get latest info about writer status */
495       CheckWriterStatus();
496
497       if (m_uidCurrentSnapshotSet != GUID_NULL) {
498          VSS_ID idNonDeletedSnapshotID = GUID_NULL;
499          LONG lSnapshots;
500
501          pVss->DeleteSnapshots(
502             m_uidCurrentSnapshotSet, 
503             VSS_OBJECT_SNAPSHOT_SET,
504             FALSE,
505             &lSnapshots,
506             &idNonDeletedSnapshotID);
507
508          m_uidCurrentSnapshotSet = GUID_NULL;
509       }
510
511       pVss->Release();
512       m_pVssObject = NULL;
513    }
514
515    // Call CoUninitialize if the CoInitialize was performed sucesfully
516    if (m_bCoInitializeCalled) {
517       CoUninitialize();
518       m_bCoInitializeCalled = false;
519    }
520
521    return bRet;
522 }
523
524 // Query all the shadow copies in the given set
525 void VSSClientGeneric::QuerySnapshotSet(GUID snapshotSetID)
526 {   
527    if (!(p_CreateVssBackupComponents && p_VssFreeSnapshotProperties)) {
528       errno = ENOSYS;
529       return;
530    }
531
532    memset (m_szShadowCopyName,0,sizeof (m_szShadowCopyName));
533    
534    if (snapshotSetID == GUID_NULL || m_pVssObject == NULL) {
535       errno = ENOSYS;
536       return;
537    }
538
539    IVssBackupComponents* pVss = (IVssBackupComponents*) m_pVssObject;
540                
541    // Get list all shadow copies. 
542    CComPtr<IVssEnumObject> pIEnumSnapshots;
543    HRESULT hr = pVss->Query( GUID_NULL, 
544          VSS_OBJECT_NONE, 
545          VSS_OBJECT_SNAPSHOT, 
546          (IVssEnumObject**)(&pIEnumSnapshots) );    
547
548    // If there are no shadow copies, just return
549    if (FAILED(hr)) {
550       errno = b_errno_win32;
551       return;   
552    }
553
554    // Enumerate all shadow copies. 
555    VSS_OBJECT_PROP Prop;
556    VSS_SNAPSHOT_PROP& Snap = Prop.Obj.Snap;
557    
558    while (true) {
559       // Get the next element
560       ULONG ulFetched;
561       hr = (pIEnumSnapshots.p)->Next( 1, &Prop, &ulFetched );
562
563       // We reached the end of list
564       if (ulFetched == 0)
565          break;
566
567       // Print the shadow copy (if not filtered out)
568       if (Snap.m_SnapshotSetId == snapshotSetID)  {
569          for (int ch='A'-'A';ch<='Z'-'A';ch++) {
570             if (wcscmp(Snap.m_pwszOriginalVolumeName, m_wszUniqueVolumeName[ch]) == 0) {       
571                wcsncpy(m_szShadowCopyName[ch],Snap.m_pwszSnapshotDeviceObject, MAX_PATH-1);               
572                break;
573             }
574          }
575       }
576       p_VssFreeSnapshotProperties(&Snap);
577    }
578    errno = 0;
579 }
580
581 // Check the status for all selected writers
582 BOOL VSSClientGeneric::CheckWriterStatus()
583 {
584     /* 
585     http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vss/base/ivssbackupcomponents_startsnapshotset.asp
586     */
587     IVssBackupComponents* pVss = (IVssBackupComponents*) m_pVssObject;
588     DestroyWriterInfo();
589
590     // Gather writer status to detect potential errors
591     CComPtr<IVssAsync>  pAsync;
592     
593     HRESULT hr = pVss->GatherWriterStatus(&pAsync.p);
594     if (FAILED(hr)) {
595        errno = b_errno_win32;
596        return FALSE;
597     } 
598
599     // Waits for the async operation to finish and checks the result
600     WaitAndCheckForAsyncOperation(pAsync.p);
601       
602     unsigned cWriters = 0;
603
604     hr = pVss->GetWriterStatusCount(&cWriters);
605     if (FAILED(hr)) {
606        errno = b_errno_win32;
607        return FALSE;
608     }
609
610     int nState;
611     
612     // Enumerate each writer
613     for (unsigned iWriter = 0; iWriter < cWriters; iWriter++) {
614         VSS_ID idInstance = GUID_NULL;
615         VSS_ID idWriter= GUID_NULL;
616         VSS_WRITER_STATE eWriterStatus = VSS_WS_UNKNOWN;
617         CComBSTR bstrWriterName;
618         HRESULT hrWriterFailure = S_OK;
619
620         // Get writer status
621         hr = pVss->GetWriterStatus(iWriter,
622                              &idInstance,
623                              &idWriter,
624                              &bstrWriterName,
625                              &eWriterStatus,
626                              &hrWriterFailure);
627         if (FAILED(hr)) {
628             /* unknown */            
629             nState = 0;
630         }
631         else {            
632             switch(eWriterStatus) {
633             case VSS_WS_FAILED_AT_IDENTIFY:
634             case VSS_WS_FAILED_AT_PREPARE_BACKUP:
635             case VSS_WS_FAILED_AT_PREPARE_SNAPSHOT:
636             case VSS_WS_FAILED_AT_FREEZE:
637             case VSS_WS_FAILED_AT_THAW:
638             case VSS_WS_FAILED_AT_POST_SNAPSHOT:
639             case VSS_WS_FAILED_AT_BACKUP_COMPLETE:
640             case VSS_WS_FAILED_AT_PRE_RESTORE:
641             case VSS_WS_FAILED_AT_POST_RESTORE:
642     #if  defined(B_VSS_W2K3) || defined(B_VSS_VISTA)
643             case VSS_WS_FAILED_AT_BACKUPSHUTDOWN:
644     #endif
645                 /* failed */                
646                 nState = -1;
647                 break;
648
649             default:
650                 /* ok */
651                 nState = 1;
652             }
653         }
654         /* store text info */
655         char str[1000];
656         char szBuf[200];        
657         bstrncpy(str, "\"", sizeof(str));
658         wchar_2_UTF8(szBuf, bstrWriterName.p, sizeof(szBuf));
659         bstrncat(str, szBuf, sizeof(str));
660         bstrncat(str, "\", State: 0x", sizeof(str));
661         itoa(eWriterStatus, szBuf, sizeof(szBuf));
662         bstrncat(str, szBuf, sizeof(str));
663         bstrncat(str, " (", sizeof(str));
664         wchar_2_UTF8(szBuf, GetStringFromWriterStatus(eWriterStatus), sizeof(szBuf));
665         bstrncat(str, szBuf, sizeof(str));
666         bstrncat(str, ")", sizeof(str));
667
668         AppendWriterInfo(nState, (const char *)str);     
669     }
670
671     hr = pVss->FreeWriterStatus();
672
673     if (FAILED(hr)) {
674         errno = b_errno_win32;
675         return FALSE;
676     } 
677
678     errno = 0;
679     return TRUE;
680 }
681
682 #endif /* WIN32_VSS */