]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/win32/filed/vss_generic.cpp
3ab65eec5556cf08ea6733cf402b972343cc8569
[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    }
332    else
333    {
334       WCHAR *xml;
335       HRESULT hr;
336       int fd;
337       struct stat stat;
338
339       /* obviously this is just temporary - the xml should come from somewhere like the catalog */
340       fd = open("C:\\james.xml", O_RDONLY);
341       Dmsg1(0, "fd = %d\n", fd);
342       fstat(fd, &stat);
343       Dmsg1(0, "size = %d\n", stat.st_size);
344       xml = new WCHAR[stat.st_size / sizeof(WCHAR) + 1];
345       read(fd, xml, stat.st_size);
346       close(fd);
347       xml[stat.st_size / sizeof(WCHAR)] = 0;
348
349       // 1. InitializeForRestore
350       hr = ((IVssBackupComponents*) m_pVssObject)->InitializeForRestore(xml);
351       if (FAILED(hr)) {
352          Dmsg1(0, "VSSClientGeneric::Initialize: IVssBackupComponents->InitializeForRestore returned 0x%08X\n", hr);
353          errno = b_errno_win32;
354          return false;
355       }
356       VssInitCallback(m_jcr, VSS_INIT_RESTORE_AFTER_INIT);
357
358       // 2. GatherWriterMetaData
359       hr = ((IVssBackupComponents*) m_pVssObject)->GatherWriterMetadata(&pAsync1.p);
360       if (FAILED(hr)) {
361          Dmsg1(0, "VSSClientGeneric::Initialize: IVssBackupComponents->GatherWriterMetadata returned 0x%08X\n", hr);
362          errno = b_errno_win32;
363          return false;
364       }
365       WaitAndCheckForAsyncOperation(pAsync1.p);
366       VssInitCallback(m_jcr, VSS_INIT_RESTORE_AFTER_GATHER);
367
368       // 3. PreRestore
369       hr = ((IVssBackupComponents*) m_pVssObject)->PreRestore(&pAsync1.p);
370       if (FAILED(hr)) {
371          Dmsg1(0, "VSSClientGeneric::Initialize: IVssBackupComponents->PreRestore returned 0x%08X\n", hr);
372          errno = b_errno_win32;
373          return false;
374       }
375       WaitAndCheckForAsyncOperation(pAsync1.p);
376       /* get latest info about writer status */
377       if (!CheckWriterStatus()) {
378          Dmsg0(0, "VSSClientGeneric::InitializePostPlugin: Failed to CheckWriterstatus\n");
379          errno = b_errno_win32;
380          return false;
381       }
382    }
383
384    // We are during restore now?
385    m_bDuringRestore = bDuringRestore;
386
387    // Keep the context
388    m_dwContext = dwContext;
389
390    return true;
391 }
392
393 bool VSSClientGeneric::WaitAndCheckForAsyncOperation(IVssAsync* pAsync)
394 {
395    // Wait until the async operation finishes
396    // unfortunately we can't use a timeout here yet.
397    // the interface would allow it on W2k3,
398    // but it is not implemented yet....
399
400    HRESULT hr;
401
402    // Check the result of the asynchronous operation
403    HRESULT hrReturned = S_OK;
404
405    int timeout = 600; // 10 minutes....
406
407    int queryErrors = 0;
408    do {
409       if (hrReturned != S_OK) 
410          Sleep(1000);
411    
412       hrReturned = S_OK;
413       hr = pAsync->QueryStatus(&hrReturned, NULL);
414    
415       if (FAILED(hr)) 
416          queryErrors++;
417    } while ((timeout-- > 0) && (hrReturned == VSS_S_ASYNC_PENDING));
418
419    if (hrReturned == VSS_S_ASYNC_FINISHED)
420       return true;
421
422    
423 #ifdef xDEBUG 
424    // Check if the async operation succeeded...
425    if(hrReturned != VSS_S_ASYNC_FINISHED) {   
426       wchar_t *pwszBuffer = NULL;
427       /* I don't see the usefulness of the following -- KES */
428       FormatMessageW(
429          FORMAT_MESSAGE_ALLOCATE_BUFFER 
430          | FORMAT_MESSAGE_FROM_SYSTEM 
431          | FORMAT_MESSAGE_IGNORE_INSERTS,
432          NULL, hrReturned, 
433          MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 
434          (LPWSTR)&pwszBuffer, 0, NULL);
435
436       LocalFree(pwszBuffer);         
437       errno = b_errno_win32;
438    }
439 #endif
440
441    return false;
442 }
443
444 bool VSSClientGeneric::CreateSnapshots(char* szDriveLetters)
445 {
446    /* szDriveLetters contains all drive letters in uppercase */
447    /* if a drive can not being added, it's converted to lowercase in szDriveLetters */
448    /* http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vss/base/ivssbackupcomponents_startsnapshotset.asp */
449    
450    if (!m_pVssObject || m_bBackupIsInitialized) {
451       errno = ENOSYS;
452       return false;  
453    }
454
455    m_uidCurrentSnapshotSet = GUID_NULL;
456
457    IVssBackupComponents *pVss = (IVssBackupComponents*)m_pVssObject;
458
459    /* startSnapshotSet */
460
461    pVss->StartSnapshotSet(&m_uidCurrentSnapshotSet);
462
463    /* AddToSnapshotSet */
464
465    wchar_t szDrive[3];
466    szDrive[1] = ':';
467    szDrive[2] = 0;
468
469    wstring volume;
470
471    CComPtr<IVssAsync>  pAsync1;
472    CComPtr<IVssAsync>  pAsync2;   
473    VSS_ID pid;
474
475    for (size_t i=0; i < strlen (szDriveLetters); i++) {
476       szDrive[0] = szDriveLetters[i];
477       volume = GetUniqueVolumeNameForPath(szDrive);
478       // store uniquevolumname
479       if (SUCCEEDED(pVss->AddToSnapshotSet((LPWSTR)volume.c_str(), GUID_NULL, &pid))) {
480          wcsncpy (m_wszUniqueVolumeName[szDriveLetters[i]-'A'], (LPWSTR) volume.c_str(), MAX_PATH);
481       } else {            
482          szDriveLetters[i] = tolower (szDriveLetters[i]);               
483       }
484    }
485
486    /* PrepareForBackup */
487    if (FAILED(pVss->PrepareForBackup(&pAsync1.p))) {      
488       errno = b_errno_win32;
489       return false;   
490    }
491    
492    // Waits for the async operation to finish and checks the result
493    WaitAndCheckForAsyncOperation(pAsync1.p);
494
495    /* get latest info about writer status */
496    if (!CheckWriterStatus()) {
497       errno = b_errno_win32;
498       return false;
499    }
500
501    /* DoSnapShotSet */   
502    if (FAILED(pVss->DoSnapshotSet(&pAsync2.p))) {      
503       errno = b_errno_win32;
504       return false;   
505    }
506
507    // Waits for the async operation to finish and checks the result
508    WaitAndCheckForAsyncOperation(pAsync2.p); 
509    
510    /* query snapshot info */   
511    QuerySnapshotSet(m_uidCurrentSnapshotSet);
512
513    SetVSSPathConvert(VSSPathConvert, VSSPathConvertW);
514
515    m_bBackupIsInitialized = true;
516
517    return true;
518 }
519
520 bool VSSClientGeneric::CloseBackup()
521 {
522    bool bRet = false;
523    if (!m_pVssObject)
524       errno = ENOSYS;
525    else {
526       IVssBackupComponents* pVss = (IVssBackupComponents*) m_pVssObject;
527       CComPtr<IVssAsync>  pAsync;
528
529       SetVSSPathConvert(NULL, NULL);
530
531       m_bBackupIsInitialized = false;
532
533 {
534    HRESULT hr;
535    BSTR xml;
536    int fd;
537
538    hr = pVss->SaveAsXML(&xml);
539    fd = open("C:\\james.xml", O_CREAT | O_WRONLY | O_TRUNC, 0777);
540    write(fd, xml, wcslen(xml) * sizeof(WCHAR));
541    close(fd);
542 }
543       if (SUCCEEDED(pVss->BackupComplete(&pAsync.p))) {
544          // Waits for the async operation to finish and checks the result
545          WaitAndCheckForAsyncOperation(pAsync.p);
546          bRet = true;     
547       } else {
548          errno = b_errno_win32;
549          pVss->AbortBackup();
550       }
551
552       /* get latest info about writer status */
553       CheckWriterStatus();
554
555       if (m_uidCurrentSnapshotSet != GUID_NULL) {
556          VSS_ID idNonDeletedSnapshotID = GUID_NULL;
557          LONG lSnapshots;
558
559          pVss->DeleteSnapshots(
560             m_uidCurrentSnapshotSet, 
561             VSS_OBJECT_SNAPSHOT_SET,
562             false,
563             &lSnapshots,
564             &idNonDeletedSnapshotID);
565
566          m_uidCurrentSnapshotSet = GUID_NULL;
567       }
568
569       pVss->Release();
570       m_pVssObject = NULL;
571    }
572
573    // Call CoUninitialize if the CoInitialize was performed sucesfully
574    if (m_bCoInitializeCalled) {
575       CoUninitialize();
576       m_bCoInitializeCalled = false;
577    }
578
579    return bRet;
580 }
581
582 bool VSSClientGeneric::CloseRestore()
583 {
584    HRESULT hr;
585    IVssBackupComponents* pVss = (IVssBackupComponents*) m_pVssObject;
586    CComPtr<IVssAsync> pAsync;
587
588    if (!pVss)
589    {
590       errno = ENOSYS;
591       return false;
592    }
593    if (SUCCEEDED(hr = pVss->PostRestore(&pAsync.p))) {
594       // Waits for the async operation to finish and checks the result
595       WaitAndCheckForAsyncOperation(pAsync.p);
596       /* get latest info about writer status */
597       if (!CheckWriterStatus()) {
598          errno = b_errno_win32;
599          return false;
600       }
601    } else {
602       errno = b_errno_win32;
603       return false;
604    }
605    return true;
606 }
607
608 // Query all the shadow copies in the given set
609 void VSSClientGeneric::QuerySnapshotSet(GUID snapshotSetID)
610 {   
611    if (!(p_CreateVssBackupComponents && p_VssFreeSnapshotProperties)) {
612       errno = ENOSYS;
613       return;
614    }
615
616    memset (m_szShadowCopyName,0,sizeof (m_szShadowCopyName));
617    
618    if (snapshotSetID == GUID_NULL || m_pVssObject == NULL) {
619       errno = ENOSYS;
620       return;
621    }
622
623    IVssBackupComponents* pVss = (IVssBackupComponents*) m_pVssObject;
624                
625    // Get list all shadow copies. 
626    CComPtr<IVssEnumObject> pIEnumSnapshots;
627    HRESULT hr = pVss->Query( GUID_NULL, 
628          VSS_OBJECT_NONE, 
629          VSS_OBJECT_SNAPSHOT, 
630          (IVssEnumObject**)(&pIEnumSnapshots) );    
631
632    // If there are no shadow copies, just return
633    if (FAILED(hr)) {
634       errno = b_errno_win32;
635       return;   
636    }
637
638    // Enumerate all shadow copies. 
639    VSS_OBJECT_PROP Prop;
640    VSS_SNAPSHOT_PROP& Snap = Prop.Obj.Snap;
641    
642    while (true) {
643       // Get the next element
644       ULONG ulFetched;
645       hr = (pIEnumSnapshots.p)->Next( 1, &Prop, &ulFetched );
646
647       // We reached the end of list
648       if (ulFetched == 0)
649          break;
650
651       // Print the shadow copy (if not filtered out)
652       if (Snap.m_SnapshotSetId == snapshotSetID)  {
653          for (int ch='A'-'A';ch<='Z'-'A';ch++) {
654             if (wcscmp(Snap.m_pwszOriginalVolumeName, m_wszUniqueVolumeName[ch]) == 0) {       
655                wcsncpy(m_szShadowCopyName[ch],Snap.m_pwszSnapshotDeviceObject, MAX_PATH-1);               
656                break;
657             }
658          }
659       }
660       p_VssFreeSnapshotProperties(&Snap);
661    }
662    errno = 0;
663 }
664
665 // Check the status for all selected writers
666 bool VSSClientGeneric::CheckWriterStatus()
667 {
668     /* 
669     http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vss/base/ivssbackupcomponents_startsnapshotset.asp
670     */
671     IVssBackupComponents* pVss = (IVssBackupComponents*) m_pVssObject;
672     DestroyWriterInfo();
673
674     // Gather writer status to detect potential errors
675     CComPtr<IVssAsync>  pAsync;
676     
677     HRESULT hr = pVss->GatherWriterStatus(&pAsync.p);
678     if (FAILED(hr)) {
679        errno = b_errno_win32;
680        return false;
681     } 
682
683     // Waits for the async operation to finish and checks the result
684     WaitAndCheckForAsyncOperation(pAsync.p);
685       
686     unsigned cWriters = 0;
687
688     hr = pVss->GetWriterStatusCount(&cWriters);
689     if (FAILED(hr)) {
690        errno = b_errno_win32;
691        return false;
692     }
693
694     int nState;
695     
696     // Enumerate each writer
697     for (unsigned iWriter = 0; iWriter < cWriters; iWriter++) {
698         VSS_ID idInstance = GUID_NULL;
699         VSS_ID idWriter= GUID_NULL;
700         VSS_WRITER_STATE eWriterStatus = VSS_WS_UNKNOWN;
701         CComBSTR bstrWriterName;
702         HRESULT hrWriterFailure = S_OK;
703
704         // Get writer status
705         hr = pVss->GetWriterStatus(iWriter,
706                              &idInstance,
707                              &idWriter,
708                              &bstrWriterName,
709                              &eWriterStatus,
710                              &hrWriterFailure);
711         if (FAILED(hr)) {
712             /* unknown */            
713             nState = 0;
714         }
715         else {            
716             switch(eWriterStatus) {
717             case VSS_WS_FAILED_AT_IDENTIFY:
718             case VSS_WS_FAILED_AT_PREPARE_BACKUP:
719             case VSS_WS_FAILED_AT_PREPARE_SNAPSHOT:
720             case VSS_WS_FAILED_AT_FREEZE:
721             case VSS_WS_FAILED_AT_THAW:
722             case VSS_WS_FAILED_AT_POST_SNAPSHOT:
723             case VSS_WS_FAILED_AT_BACKUP_COMPLETE:
724             case VSS_WS_FAILED_AT_PRE_RESTORE:
725             case VSS_WS_FAILED_AT_POST_RESTORE:
726     #if  defined(B_VSS_W2K3) || defined(B_VSS_VISTA)
727             case VSS_WS_FAILED_AT_BACKUPSHUTDOWN:
728     #endif
729                 /* failed */                
730                 nState = -1;
731                 break;
732
733             default:
734                 /* ok */
735                 nState = 1;
736             }
737         }
738         /* store text info */
739         char str[1000];
740         char szBuf[200];        
741         bstrncpy(str, "\"", sizeof(str));
742         wchar_2_UTF8(szBuf, bstrWriterName.p, sizeof(szBuf));
743         bstrncat(str, szBuf, sizeof(str));
744         bstrncat(str, "\", State: 0x", sizeof(str));
745         itoa(eWriterStatus, szBuf, sizeof(szBuf));
746         bstrncat(str, szBuf, sizeof(str));
747         bstrncat(str, " (", sizeof(str));
748         wchar_2_UTF8(szBuf, GetStringFromWriterStatus(eWriterStatus), sizeof(szBuf));
749         bstrncat(str, szBuf, sizeof(str));
750         bstrncat(str, ")", sizeof(str));
751
752         AppendWriterInfo(nState, (const char *)str);     
753     }
754
755     hr = pVss->FreeWriterStatus();
756
757     if (FAILED(hr)) {
758         errno = b_errno_win32;
759         return false;
760     } 
761
762     errno = 0;
763     return true;
764 }
765
766 #endif /* WIN32_VSS */