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