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