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