]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/win32/filed/vss_generic.cpp
Tweak comments + indenting
[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 #undef uuid
85 #define uuid(x)
86
87 #ifdef B_VSS_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    #define VSSClientGeneric VSSClient2003
98    
99    #include "inc/Win2003/vss.h"
100    #include "inc/Win2003/vswriter.h"
101    #include "inc/Win2003/vsbackup.h"
102 #endif
103
104 #ifdef B_VSS_VISTA
105    #define VSSClientGeneric VSSClientVista
106
107    #include "inc/Win2003/vss.h"
108    #include "inc/Win2003/vswriter.h"
109    #include "inc/Win2003/vsbackup.h"
110 #endif
111    
112 /* In VSSAPI.DLL */
113 typedef HRESULT (STDAPICALLTYPE* t_CreateVssBackupComponents)(OUT IVssBackupComponents **);
114 typedef void (APIENTRY* t_VssFreeSnapshotProperties)(IN VSS_SNAPSHOT_PROP*);
115    
116 static t_CreateVssBackupComponents p_CreateVssBackupComponents = NULL;
117 static t_VssFreeSnapshotProperties p_VssFreeSnapshotProperties = NULL;
118
119
120
121 #include "vss.h"
122
123 /*  
124  *
125  * some helper functions 
126  *
127  *
128  */
129
130 // Append a backslash to the current string 
131 inline wstring AppendBackslash(wstring str)
132 {
133     if (str.length() == 0)
134         return wstring(L"\\");
135     if (str[str.length() - 1] == L'\\')
136         return str;
137     return str.append(L"\\");
138 }
139
140 // Get the unique volume name for the given path
141 inline wstring GetUniqueVolumeNameForPath(wstring path)
142 {
143     if (path.length() <= 0) {
144        return L"";
145     }
146
147     // Add the backslash termination, if needed
148     path = AppendBackslash(path);
149
150     // Get the root path of the volume
151     wchar_t volumeRootPath[MAX_PATH];
152     wchar_t volumeName[MAX_PATH];
153     wchar_t volumeUniqueName[MAX_PATH];
154
155     if (!p_GetVolumePathNameW || !p_GetVolumePathNameW((LPCWSTR)path.c_str(), volumeRootPath, MAX_PATH))
156       return L"";
157     
158     // Get the volume name alias (might be different from the unique volume name in rare cases)
159     if (!p_GetVolumeNameForVolumeMountPointW || !p_GetVolumeNameForVolumeMountPointW(volumeRootPath, volumeName, MAX_PATH))
160        return L"";
161     
162     // Get the unique volume name    
163     if (!p_GetVolumeNameForVolumeMountPointW(volumeName, volumeUniqueName, MAX_PATH))
164        return L"";
165     
166     return volumeUniqueName;
167 }
168
169
170 // Helper macro for quick treatment of case statements for error codes
171 #define GEN_MERGE(A, B) A##B
172 #define GEN_MAKE_W(A) GEN_MERGE(L, A)
173
174 #define CHECK_CASE_FOR_CONSTANT(value)                      \
175     case value: return (GEN_MAKE_W(#value));
176
177
178 // Convert a writer status into a string
179 inline const wchar_t* GetStringFromWriterStatus(VSS_WRITER_STATE eWriterStatus)
180 {
181     switch (eWriterStatus) {
182     CHECK_CASE_FOR_CONSTANT(VSS_WS_STABLE);
183     CHECK_CASE_FOR_CONSTANT(VSS_WS_WAITING_FOR_FREEZE);
184     CHECK_CASE_FOR_CONSTANT(VSS_WS_WAITING_FOR_THAW);
185     CHECK_CASE_FOR_CONSTANT(VSS_WS_WAITING_FOR_POST_SNAPSHOT);
186     CHECK_CASE_FOR_CONSTANT(VSS_WS_WAITING_FOR_BACKUP_COMPLETE);
187     CHECK_CASE_FOR_CONSTANT(VSS_WS_FAILED_AT_IDENTIFY);
188     CHECK_CASE_FOR_CONSTANT(VSS_WS_FAILED_AT_PREPARE_BACKUP);
189     CHECK_CASE_FOR_CONSTANT(VSS_WS_FAILED_AT_PREPARE_SNAPSHOT);
190     CHECK_CASE_FOR_CONSTANT(VSS_WS_FAILED_AT_FREEZE);
191     CHECK_CASE_FOR_CONSTANT(VSS_WS_FAILED_AT_THAW);
192     CHECK_CASE_FOR_CONSTANT(VSS_WS_FAILED_AT_POST_SNAPSHOT);
193     CHECK_CASE_FOR_CONSTANT(VSS_WS_FAILED_AT_BACKUP_COMPLETE);
194     CHECK_CASE_FOR_CONSTANT(VSS_WS_FAILED_AT_PRE_RESTORE);
195     CHECK_CASE_FOR_CONSTANT(VSS_WS_FAILED_AT_POST_RESTORE);
196     
197     default:
198         return L"Error or Undefined";
199     }
200 }
201
202 // Constructor
203
204 #ifdef HAVE_VSS64
205 /* 64 bit entrypoint name */
206 #define VSSVBACK_ENTRY "?CreateVssBackupComponents@@YAJPEAPEAVIVssBackupComponents@@@Z"
207 #else
208 /* 32 bit entrypoint name */
209 #define VSSVBACK_ENTRY "?CreateVssBackupComponents@@YGJPAPAVIVssBackupComponents@@@Z"
210 #endif
211
212 VSSClientGeneric::VSSClientGeneric()
213 {
214    m_hLib = LoadLibraryA("VSSAPI.DLL");
215    if (m_hLib) {      
216       p_CreateVssBackupComponents = (t_CreateVssBackupComponents)
217          GetProcAddress(m_hLib, VSSVBACK_ENTRY);
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, bool (*VssInitCallback)(JCR *, int))
234 {
235    CComPtr<IVssAsync>  pAsync1;
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       // 3. GatherWriterMetaData
323       hr = ((IVssBackupComponents*) m_pVssObject)->GatherWriterMetadata(&pAsync1.p);
324       if (FAILED(hr)) {
325          Dmsg1(0, "VSSClientGeneric::Initialize: IVssBackupComponents->GatherWriterMetadata returned 0x%08X\n", hr);
326          errno = b_errno_win32;
327          return false;
328       }
329       // Waits for the async operation to finish and checks the result
330       WaitAndCheckForAsyncOperation(pAsync1.p);
331    } else {
332  
333    /*
334     * Initialize for restore
335     */
336
337       WCHAR *xml;
338       HRESULT hr;
339       int fd;
340       struct stat stat;
341
342       /* obviously this is just temporary - the xml should come from somewhere like the catalog */
343       fd = open("C:\\james.xml", O_RDONLY);
344       Dmsg1(0, "fd = %d\n", fd);
345       fstat(fd, &stat);
346       Dmsg1(0, "size = %d\n", stat.st_size);
347       xml = new WCHAR[stat.st_size / sizeof(WCHAR) + 1];
348       read(fd, xml, stat.st_size);
349       close(fd);
350       xml[stat.st_size / sizeof(WCHAR)] = 0;
351
352       // 1. InitializeForRestore
353       hr = ((IVssBackupComponents*) m_pVssObject)->InitializeForRestore(xml);
354       if (FAILED(hr)) {
355          Dmsg1(0, "VSSClientGeneric::Initialize: IVssBackupComponents->InitializeForRestore returned 0x%08X\n", hr);
356          errno = b_errno_win32;
357          return false;
358       }
359       VssInitCallback(m_jcr, VSS_INIT_RESTORE_AFTER_INIT);
360
361       // 2. GatherWriterMetaData
362       hr = ((IVssBackupComponents*) m_pVssObject)->GatherWriterMetadata(&pAsync1.p);
363       if (FAILED(hr)) {
364          Dmsg1(0, "VSSClientGeneric::Initialize: IVssBackupComponents->GatherWriterMetadata returned 0x%08X\n", hr);
365          errno = b_errno_win32;
366          return false;
367       }
368       WaitAndCheckForAsyncOperation(pAsync1.p);
369       VssInitCallback(m_jcr, VSS_INIT_RESTORE_AFTER_GATHER);
370
371       // 3. PreRestore
372       hr = ((IVssBackupComponents*) m_pVssObject)->PreRestore(&pAsync1.p);
373       if (FAILED(hr)) {
374          Dmsg1(0, "VSSClientGeneric::Initialize: IVssBackupComponents->PreRestore returned 0x%08X\n", hr);
375          errno = b_errno_win32;
376          return false;
377       }
378       WaitAndCheckForAsyncOperation(pAsync1.p);
379       /* get latest info about writer status */
380       if (!CheckWriterStatus()) {
381          Dmsg0(0, "VSSClientGeneric::InitializePostPlugin: Failed to CheckWriterstatus\n");
382          errno = b_errno_win32;
383          return false;
384       }
385    }
386
387    // We are during restore now?
388    m_bDuringRestore = bDuringRestore;
389
390    // Keep the context
391    m_dwContext = dwContext;
392
393    return true;
394 }
395
396 bool VSSClientGeneric::WaitAndCheckForAsyncOperation(IVssAsync* pAsync)
397 {
398    // Wait until the async operation finishes
399    // unfortunately we can't use a timeout here yet.
400    // the interface would allow it on W2k3,
401    // but it is not implemented yet....
402
403    HRESULT hr;
404
405    // Check the result of the asynchronous operation
406    HRESULT hrReturned = S_OK;
407
408    int timeout = 600; // 10 minutes....
409
410    int queryErrors = 0;
411    do {
412       if (hrReturned != S_OK) 
413          Sleep(1000);
414    
415       hrReturned = S_OK;
416       hr = pAsync->QueryStatus(&hrReturned, NULL);
417    
418       if (FAILED(hr)) 
419          queryErrors++;
420    } while ((timeout-- > 0) && (hrReturned == VSS_S_ASYNC_PENDING));
421
422    if (hrReturned == VSS_S_ASYNC_FINISHED)
423       return true;
424
425    
426 #ifdef xDEBUG 
427    // Check if the async operation succeeded...
428    if(hrReturned != VSS_S_ASYNC_FINISHED) {   
429       wchar_t *pwszBuffer = NULL;
430       /* I don't see the usefulness of the following -- KES */
431       FormatMessageW(
432          FORMAT_MESSAGE_ALLOCATE_BUFFER 
433          | FORMAT_MESSAGE_FROM_SYSTEM 
434          | FORMAT_MESSAGE_IGNORE_INSERTS,
435          NULL, hrReturned, 
436          MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 
437          (LPWSTR)&pwszBuffer, 0, NULL);
438
439       LocalFree(pwszBuffer);         
440       errno = b_errno_win32;
441    }
442 #endif
443
444    return false;
445 }
446
447 bool VSSClientGeneric::CreateSnapshots(char* szDriveLetters)
448 {
449    /* szDriveLetters contains all drive letters in uppercase */
450    /* if a drive can not being added, it's converted to lowercase in szDriveLetters */
451    /* http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vss/base/ivssbackupcomponents_startsnapshotset.asp */
452    
453    if (!m_pVssObject || m_bBackupIsInitialized) {
454       errno = ENOSYS;
455       return false;  
456    }
457
458    m_uidCurrentSnapshotSet = GUID_NULL;
459
460    IVssBackupComponents *pVss = (IVssBackupComponents*)m_pVssObject;
461
462    /* startSnapshotSet */
463
464    pVss->StartSnapshotSet(&m_uidCurrentSnapshotSet);
465
466    /* AddToSnapshotSet */
467
468    wchar_t szDrive[3];
469    szDrive[1] = ':';
470    szDrive[2] = 0;
471
472    wstring volume;
473
474    CComPtr<IVssAsync>  pAsync1;
475    CComPtr<IVssAsync>  pAsync2;   
476    VSS_ID pid;
477
478    for (size_t i=0; i < strlen (szDriveLetters); i++) {
479       szDrive[0] = szDriveLetters[i];
480       volume = GetUniqueVolumeNameForPath(szDrive);
481       // store uniquevolumname
482       if (SUCCEEDED(pVss->AddToSnapshotSet((LPWSTR)volume.c_str(), GUID_NULL, &pid))) {
483          wcsncpy (m_wszUniqueVolumeName[szDriveLetters[i]-'A'], (LPWSTR) volume.c_str(), MAX_PATH);
484       } else {            
485          szDriveLetters[i] = tolower (szDriveLetters[i]);               
486       }
487    }
488
489    /* PrepareForBackup */
490    if (FAILED(pVss->PrepareForBackup(&pAsync1.p))) {      
491       errno = b_errno_win32;
492       return false;   
493    }
494    
495    // Waits for the async operation to finish and checks the result
496    WaitAndCheckForAsyncOperation(pAsync1.p);
497
498    /* get latest info about writer status */
499    if (!CheckWriterStatus()) {
500       errno = b_errno_win32;
501       return false;
502    }
503
504    /* DoSnapShotSet */   
505    if (FAILED(pVss->DoSnapshotSet(&pAsync2.p))) {      
506       errno = b_errno_win32;
507       return false;   
508    }
509
510    // Waits for the async operation to finish and checks the result
511    WaitAndCheckForAsyncOperation(pAsync2.p); 
512    
513    /* query snapshot info */   
514    QuerySnapshotSet(m_uidCurrentSnapshotSet);
515
516    SetVSSPathConvert(VSSPathConvert, VSSPathConvertW);
517
518    m_bBackupIsInitialized = true;
519
520    return true;
521 }
522
523 bool VSSClientGeneric::CloseBackup()
524 {
525    bool bRet = false;
526    if (!m_pVssObject)
527       errno = ENOSYS;
528    else {
529       IVssBackupComponents* pVss = (IVssBackupComponents*) m_pVssObject;
530       CComPtr<IVssAsync>  pAsync;
531
532       SetVSSPathConvert(NULL, NULL);
533
534       m_bBackupIsInitialized = false;
535
536 {
537    HRESULT hr;
538    BSTR xml;
539    int fd;
540
541    hr = pVss->SaveAsXML(&xml);
542    fd = open("C:\\james.xml", O_CREAT | O_WRONLY | O_TRUNC, 0777);
543    write(fd, xml, wcslen(xml) * sizeof(WCHAR));
544    close(fd);
545 }
546       if (SUCCEEDED(pVss->BackupComplete(&pAsync.p))) {
547          // Waits for the async operation to finish and checks the result
548          WaitAndCheckForAsyncOperation(pAsync.p);
549          bRet = true;     
550       } else {
551          errno = b_errno_win32;
552          pVss->AbortBackup();
553       }
554
555       /* get latest info about writer status */
556       CheckWriterStatus();
557
558       if (m_uidCurrentSnapshotSet != GUID_NULL) {
559          VSS_ID idNonDeletedSnapshotID = GUID_NULL;
560          LONG lSnapshots;
561
562          pVss->DeleteSnapshots(
563             m_uidCurrentSnapshotSet, 
564             VSS_OBJECT_SNAPSHOT_SET,
565             false,
566             &lSnapshots,
567             &idNonDeletedSnapshotID);
568
569          m_uidCurrentSnapshotSet = GUID_NULL;
570       }
571
572       pVss->Release();
573       m_pVssObject = NULL;
574    }
575
576    // Call CoUninitialize if the CoInitialize was performed sucesfully
577    if (m_bCoInitializeCalled) {
578       CoUninitialize();
579       m_bCoInitializeCalled = false;
580    }
581
582    return bRet;
583 }
584
585 bool VSSClientGeneric::CloseRestore()
586 {
587    HRESULT hr;
588    IVssBackupComponents* pVss = (IVssBackupComponents*) m_pVssObject;
589    CComPtr<IVssAsync> pAsync;
590
591    if (!pVss)
592    {
593       errno = ENOSYS;
594       return false;
595    }
596    if (SUCCEEDED(hr = pVss->PostRestore(&pAsync.p))) {
597       // Waits for the async operation to finish and checks the result
598       WaitAndCheckForAsyncOperation(pAsync.p);
599       /* get latest info about writer status */
600       if (!CheckWriterStatus()) {
601          errno = b_errno_win32;
602          return false;
603       }
604    } else {
605       errno = b_errno_win32;
606       return false;
607    }
608    return true;
609 }
610
611 // Query all the shadow copies in the given set
612 void VSSClientGeneric::QuerySnapshotSet(GUID snapshotSetID)
613 {   
614    if (!(p_CreateVssBackupComponents && p_VssFreeSnapshotProperties)) {
615       errno = ENOSYS;
616       return;
617    }
618
619    memset (m_szShadowCopyName,0,sizeof (m_szShadowCopyName));
620    
621    if (snapshotSetID == GUID_NULL || m_pVssObject == NULL) {
622       errno = ENOSYS;
623       return;
624    }
625
626    IVssBackupComponents* pVss = (IVssBackupComponents*) m_pVssObject;
627                
628    // Get list all shadow copies. 
629    CComPtr<IVssEnumObject> pIEnumSnapshots;
630    HRESULT hr = pVss->Query( GUID_NULL, 
631          VSS_OBJECT_NONE, 
632          VSS_OBJECT_SNAPSHOT, 
633          (IVssEnumObject**)(&pIEnumSnapshots) );    
634
635    // If there are no shadow copies, just return
636    if (FAILED(hr)) {
637       errno = b_errno_win32;
638       return;   
639    }
640
641    // Enumerate all shadow copies. 
642    VSS_OBJECT_PROP Prop;
643    VSS_SNAPSHOT_PROP& Snap = Prop.Obj.Snap;
644    
645    while (true) {
646       // Get the next element
647       ULONG ulFetched;
648       hr = (pIEnumSnapshots.p)->Next( 1, &Prop, &ulFetched );
649
650       // We reached the end of list
651       if (ulFetched == 0)
652          break;
653
654       // Print the shadow copy (if not filtered out)
655       if (Snap.m_SnapshotSetId == snapshotSetID)  {
656          for (int ch='A'-'A';ch<='Z'-'A';ch++) {
657             if (wcscmp(Snap.m_pwszOriginalVolumeName, m_wszUniqueVolumeName[ch]) == 0) {       
658                wcsncpy(m_szShadowCopyName[ch],Snap.m_pwszSnapshotDeviceObject, MAX_PATH-1);               
659                break;
660             }
661          }
662       }
663       p_VssFreeSnapshotProperties(&Snap);
664    }
665    errno = 0;
666 }
667
668 // Check the status for all selected writers
669 bool VSSClientGeneric::CheckWriterStatus()
670 {
671     /* 
672     http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vss/base/ivssbackupcomponents_startsnapshotset.asp
673     */
674     IVssBackupComponents* pVss = (IVssBackupComponents*) m_pVssObject;
675     DestroyWriterInfo();
676
677     // Gather writer status to detect potential errors
678     CComPtr<IVssAsync>  pAsync;
679     
680     HRESULT hr = pVss->GatherWriterStatus(&pAsync.p);
681     if (FAILED(hr)) {
682        errno = b_errno_win32;
683        return false;
684     } 
685
686     // Waits for the async operation to finish and checks the result
687     WaitAndCheckForAsyncOperation(pAsync.p);
688       
689     unsigned cWriters = 0;
690
691     hr = pVss->GetWriterStatusCount(&cWriters);
692     if (FAILED(hr)) {
693        errno = b_errno_win32;
694        return false;
695     }
696
697     int nState;
698     
699     // Enumerate each writer
700     for (unsigned iWriter = 0; iWriter < cWriters; iWriter++) {
701         VSS_ID idInstance = GUID_NULL;
702         VSS_ID idWriter= GUID_NULL;
703         VSS_WRITER_STATE eWriterStatus = VSS_WS_UNKNOWN;
704         CComBSTR bstrWriterName;
705         HRESULT hrWriterFailure = S_OK;
706
707         // Get writer status
708         hr = pVss->GetWriterStatus(iWriter,
709                              &idInstance,
710                              &idWriter,
711                              &bstrWriterName,
712                              &eWriterStatus,
713                              &hrWriterFailure);
714         if (FAILED(hr)) {
715             /* unknown */            
716             nState = 0;
717         }
718         else {            
719             switch(eWriterStatus) {
720             case VSS_WS_FAILED_AT_IDENTIFY:
721             case VSS_WS_FAILED_AT_PREPARE_BACKUP:
722             case VSS_WS_FAILED_AT_PREPARE_SNAPSHOT:
723             case VSS_WS_FAILED_AT_FREEZE:
724             case VSS_WS_FAILED_AT_THAW:
725             case VSS_WS_FAILED_AT_POST_SNAPSHOT:
726             case VSS_WS_FAILED_AT_BACKUP_COMPLETE:
727             case VSS_WS_FAILED_AT_PRE_RESTORE:
728             case VSS_WS_FAILED_AT_POST_RESTORE:
729     #if  defined(B_VSS_W2K3) || defined(B_VSS_VISTA)
730             case VSS_WS_FAILED_AT_BACKUPSHUTDOWN:
731     #endif
732                 /* failed */                
733                 nState = -1;
734                 break;
735
736             default:
737                 /* ok */
738                 nState = 1;
739             }
740         }
741         /* store text info */
742         char str[1000];
743         char szBuf[200];        
744         bstrncpy(str, "\"", sizeof(str));
745         wchar_2_UTF8(szBuf, bstrWriterName.p, sizeof(szBuf));
746         bstrncat(str, szBuf, sizeof(str));
747         bstrncat(str, "\", State: 0x", sizeof(str));
748         itoa(eWriterStatus, szBuf, sizeof(szBuf));
749         bstrncat(str, szBuf, sizeof(str));
750         bstrncat(str, " (", sizeof(str));
751         wchar_2_UTF8(szBuf, GetStringFromWriterStatus(eWriterStatus), sizeof(szBuf));
752         bstrncat(str, szBuf, sizeof(str));
753         bstrncat(str, ")", sizeof(str));
754
755         AppendWriterInfo(nState, (const char *)str);     
756     }
757
758     hr = pVss->FreeWriterStatus();
759
760     if (FAILED(hr)) {
761         errno = b_errno_win32;
762         return false;
763     } 
764
765     errno = 0;
766     return true;
767 }
768
769 #endif /* WIN32_VSS */