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