]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/win32/filed/vss_generic.cpp
kes Update AUTHORs file
[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       DWORD dwRet = ::FormatMessageW(
371                         FORMAT_MESSAGE_ALLOCATE_BUFFER 
372                         | FORMAT_MESSAGE_FROM_SYSTEM 
373                         | FORMAT_MESSAGE_IGNORE_INSERTS,
374                         NULL, hrReturned, 
375                         MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 
376                         (LPWSTR)&pwszBuffer, 0, NULL);
377
378       if (dwRet != 0) {         
379          LocalFree(pwszBuffer);         
380       }      
381       errno = b_errno_win32;
382    }
383 #endif
384
385    return FALSE;
386 }
387
388 BOOL VSSClientGeneric::CreateSnapshots(char* szDriveLetters)
389 {
390    /* szDriveLetters contains all drive letters in uppercase */
391    /* if a drive can not being added, it's converted to lowercase in szDriveLetters */
392    /* http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vss/base/ivssbackupcomponents_startsnapshotset.asp */
393    
394    if (!m_pVssObject || m_bBackupIsInitialized) {
395       errno = ENOSYS;
396       return FALSE;  
397    }
398
399    m_uidCurrentSnapshotSet = GUID_NULL;
400
401    IVssBackupComponents *pVss = (IVssBackupComponents*)m_pVssObject;
402
403    /* startSnapshotSet */
404
405    pVss->StartSnapshotSet(&m_uidCurrentSnapshotSet);
406
407    /* AddToSnapshotSet */
408
409    wchar_t szDrive[3];
410    szDrive[1] = ':';
411    szDrive[2] = 0;
412
413    wstring volume;
414
415    CComPtr<IVssAsync>  pAsync1;
416    CComPtr<IVssAsync>  pAsync2;   
417    VSS_ID pid;
418
419    for (size_t i=0; i < strlen (szDriveLetters); i++) {
420       szDrive[0] = szDriveLetters[i];
421       volume = GetUniqueVolumeNameForPath(szDrive);
422       // store uniquevolumname
423       if (SUCCEEDED(pVss->AddToSnapshotSet((LPWSTR)volume.c_str(), GUID_NULL, &pid))) {
424          wcsncpy (m_wszUniqueVolumeName[szDriveLetters[i]-'A'], (LPWSTR) volume.c_str(), MAX_PATH);
425       } else {            
426          szDriveLetters[i] = tolower (szDriveLetters[i]);               
427       }
428    }
429
430    /* PrepareForBackup */
431    if (FAILED(pVss->PrepareForBackup(&pAsync1.p))) {      
432       errno = b_errno_win32;
433       return FALSE;   
434    }
435    
436    // Waits for the async operation to finish and checks the result
437    WaitAndCheckForAsyncOperation(pAsync1.p);
438
439    /* get latest info about writer status */
440    if (!CheckWriterStatus()) {
441       errno = b_errno_win32;
442       return FALSE;
443    }
444
445    /* DoSnapShotSet */   
446    if (FAILED(pVss->DoSnapshotSet(&pAsync2.p))) {      
447       errno = b_errno_win32;
448       return FALSE;   
449    }
450
451    // Waits for the async operation to finish and checks the result
452    WaitAndCheckForAsyncOperation(pAsync2.p); 
453    
454    /* query snapshot info */   
455    QuerySnapshotSet(m_uidCurrentSnapshotSet);
456
457    SetVSSPathConvert(VSSPathConvert, VSSPathConvertW);
458
459    m_bBackupIsInitialized = true;
460
461    return TRUE;
462 }
463
464 BOOL VSSClientGeneric::CloseBackup()
465 {
466    BOOL bRet = FALSE;
467    if (!m_pVssObject)
468       errno = ENOSYS;
469    else {
470       IVssBackupComponents* pVss = (IVssBackupComponents*) m_pVssObject;
471       CComPtr<IVssAsync>  pAsync;
472
473       SetVSSPathConvert(NULL, NULL);
474
475       m_bBackupIsInitialized = false;
476
477       if (SUCCEEDED(pVss->BackupComplete(&pAsync.p))) {
478          // Waits for the async operation to finish and checks the result
479          WaitAndCheckForAsyncOperation(pAsync.p);
480          bRet = TRUE;     
481       } else {
482          errno = b_errno_win32;
483          pVss->AbortBackup();
484       }
485
486       /* get latest info about writer status */
487       CheckWriterStatus();
488
489       if (m_uidCurrentSnapshotSet != GUID_NULL) {
490          VSS_ID idNonDeletedSnapshotID = GUID_NULL;
491          LONG lSnapshots;
492
493          pVss->DeleteSnapshots(
494             m_uidCurrentSnapshotSet, 
495             VSS_OBJECT_SNAPSHOT_SET,
496             FALSE,
497             &lSnapshots,
498             &idNonDeletedSnapshotID);
499
500          m_uidCurrentSnapshotSet = GUID_NULL;
501       }
502
503       pVss->Release();
504       m_pVssObject = NULL;
505    }
506
507    // Call CoUninitialize if the CoInitialize was performed sucesfully
508    if (m_bCoInitializeCalled) {
509       CoUninitialize();
510       m_bCoInitializeCalled = false;
511    }
512
513    return bRet;
514 }
515
516 // Query all the shadow copies in the given set
517 void VSSClientGeneric::QuerySnapshotSet(GUID snapshotSetID)
518 {   
519    if (!(p_CreateVssBackupComponents && p_VssFreeSnapshotProperties)) {
520       errno = ENOSYS;
521       return;
522    }
523
524    memset (m_szShadowCopyName,0,sizeof (m_szShadowCopyName));
525    
526    if (snapshotSetID == GUID_NULL || m_pVssObject == NULL) {
527       errno = ENOSYS;
528       return;
529    }
530
531    IVssBackupComponents* pVss = (IVssBackupComponents*) m_pVssObject;
532                
533    // Get list all shadow copies. 
534    CComPtr<IVssEnumObject> pIEnumSnapshots;
535    HRESULT hr = pVss->Query( GUID_NULL, 
536          VSS_OBJECT_NONE, 
537          VSS_OBJECT_SNAPSHOT, 
538          (IVssEnumObject**)(&pIEnumSnapshots) );    
539
540    // If there are no shadow copies, just return
541    if (FAILED(hr)) {
542       errno = b_errno_win32;
543       return;   
544    }
545
546    // Enumerate all shadow copies. 
547    VSS_OBJECT_PROP Prop;
548    VSS_SNAPSHOT_PROP& Snap = Prop.Obj.Snap;
549    
550    while (true) {
551       // Get the next element
552       ULONG ulFetched;
553       hr = (pIEnumSnapshots.p)->Next( 1, &Prop, &ulFetched );
554
555       // We reached the end of list
556       if (ulFetched == 0)
557          break;
558
559       // Print the shadow copy (if not filtered out)
560       if (Snap.m_SnapshotSetId == snapshotSetID)  {
561          for (int ch='A'-'A';ch<='Z'-'A';ch++) {
562             if (wcscmp(Snap.m_pwszOriginalVolumeName, m_wszUniqueVolumeName[ch]) == 0) {       
563                wcsncpy(m_szShadowCopyName[ch],Snap.m_pwszSnapshotDeviceObject, MAX_PATH-1);               
564                break;
565             }
566          }
567       }
568       p_VssFreeSnapshotProperties(&Snap);
569    }
570    errno = 0;
571 }
572
573 // Check the status for all selected writers
574 BOOL VSSClientGeneric::CheckWriterStatus()
575 {
576     /* 
577     http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vss/base/ivssbackupcomponents_startsnapshotset.asp
578     */
579     IVssBackupComponents* pVss = (IVssBackupComponents*) m_pVssObject;
580     DestroyWriterInfo();
581
582     // Gather writer status to detect potential errors
583     CComPtr<IVssAsync>  pAsync;
584     
585     HRESULT hr = pVss->GatherWriterStatus(&pAsync.p);
586     if (FAILED(hr)) {
587        errno = b_errno_win32;
588        return FALSE;
589     } 
590
591     // Waits for the async operation to finish and checks the result
592     WaitAndCheckForAsyncOperation(pAsync.p);
593       
594     unsigned cWriters = 0;
595
596     hr = pVss->GetWriterStatusCount(&cWriters);
597     if (FAILED(hr)) {
598        errno = b_errno_win32;
599        return FALSE;
600     }
601
602     int nState;
603     
604     // Enumerate each writer
605     for (unsigned iWriter = 0; iWriter < cWriters; iWriter++) {
606         VSS_ID idInstance = GUID_NULL;
607         VSS_ID idWriter= GUID_NULL;
608         VSS_WRITER_STATE eWriterStatus = VSS_WS_UNKNOWN;
609         CComBSTR bstrWriterName;
610         HRESULT hrWriterFailure = S_OK;
611
612         // Get writer status
613         hr = pVss->GetWriterStatus(iWriter,
614                              &idInstance,
615                              &idWriter,
616                              &bstrWriterName,
617                              &eWriterStatus,
618                              &hrWriterFailure);
619         if (FAILED(hr)) {
620             /* unknown */            
621             nState = 0;
622         }
623         else {            
624             switch(eWriterStatus) {
625             case VSS_WS_FAILED_AT_IDENTIFY:
626             case VSS_WS_FAILED_AT_PREPARE_BACKUP:
627             case VSS_WS_FAILED_AT_PREPARE_SNAPSHOT:
628             case VSS_WS_FAILED_AT_FREEZE:
629             case VSS_WS_FAILED_AT_THAW:
630             case VSS_WS_FAILED_AT_POST_SNAPSHOT:
631             case VSS_WS_FAILED_AT_BACKUP_COMPLETE:
632             case VSS_WS_FAILED_AT_PRE_RESTORE:
633             case VSS_WS_FAILED_AT_POST_RESTORE:
634     #if  defined(B_VSS_W2K3) || defined(B_VSS_VISTA)
635             case VSS_WS_FAILED_AT_BACKUPSHUTDOWN:
636     #endif
637                 /* failed */                
638                 nState = -1;
639                 break;
640
641             default:
642                 /* ok */
643                 nState = 1;
644             }
645         }
646         /* store text info */
647         char str[1000];
648         char szBuf[200];        
649         bstrncpy(str, "\"", sizeof(str));
650         wchar_2_UTF8(szBuf, bstrWriterName.p, sizeof(szBuf));
651         bstrncat(str, szBuf, sizeof(str));
652         bstrncat(str, "\", State: 0x", sizeof(str));
653         itoa(eWriterStatus, szBuf, sizeof(szBuf));
654         bstrncat(str, szBuf, sizeof(str));
655         bstrncat(str, " (", sizeof(str));
656         wchar_2_UTF8(szBuf, GetStringFromWriterStatus(eWriterStatus), sizeof(szBuf));
657         bstrncat(str, szBuf, sizeof(str));
658         bstrncat(str, ")", sizeof(str));
659
660         AppendWriterInfo(nState, (const char *)str);     
661     }
662
663     hr = pVss->FreeWriterStatus();
664
665     if (FAILED(hr)) {
666         errno = b_errno_win32;
667         return FALSE;
668     } 
669
670     errno = 0;
671     return TRUE;
672 }
673
674 #endif /* WIN32_VSS */