vendredi 21 novembre 2008

Hello world!

Bonjour à tous,

Un p'tit blog sans prétention ou je vais essayer de vous part de ce que je peux découvrir au cours de mon travail. Je suis informaticien, du moins j'essaye, ce domaine étant très vaste et varié. Je suis spécialisé en administration système et réseau, donc pas du tout développeur. Si certains d'entre vous trouve des aneries dans ce que je raconte, n'hésitez pas à me le faire savoir, preuve à l'appui bien sûr ;-).
J'ai une préférence pour les Unix, MacOSX, FreeBSD, OpenBSD et Linux. Cela ne m'empêche pas de suivre les cours Microsoft, car cet éditeur est incontestablement très présent sur le marché, et en recherche constante d'amélioration de ses produits. Certains critiqueront la politique tarifaire, mais passer du temps pour essayer de faire la même chose en moins bien avec des produits libres n'est pas forcément économique non plus. Donc intégriste de tel ou tel technologie, passez votre chemin, mon credo étant d'utiliser l'outil le mieux adapté pour faire mon boulot, le plus efficacement possible, en faisant abstraction de mes préférences. Surtout que ce qui fait que souvent on préfère une technologie à une autre, c'est la méconnaissance (pour ne pas dire incompétence) des produits.

On va commencer par parler des sauvegardes des données et paramètres utilisateurs sous Windows, à partir de XP et ultérieur. Il faut savoir qu'il n'est pas simple de procéder à une sauvegarde de fichiers, car certains d'entre eux sont verrouillés par le système. Pas moyens de les copier comme ça, comme on le ferait sous Unix. Pour y accéder, il faut passer par le Volume Shadow copy Service, gentiment appelé VSS par Microsoft. Quand on veut développer sa propre appli, il faut passer par le VSS SDK qui va vous apporter tout ce qu'il faut y parvenir.
Le but du VSS est de prendre des clichés instantanés, des snapshots, du ou des disques dur. En fait, c'est une image du contenu du disque dur (ou volumes pour être un peu tatillon) accessible en lecture seule.

Pour faire simple, le VSS est composé de trois entités:
  1. Les providers:
  2. Les requesters:
  3. Les writers:
Les providers gèrent les volumes courants et créent les snapshot à la demande. On a de la chance, Microsoft en fourni par défaut qui iront bien pour ce qu'on a à faire.

Les requesters sont typiquement des appli de sauvegarde/restauration, comme ntbackup. Ce sont eux qui vont demander aux provider de fournir un ou plusieurs snapshot afin d'avoir une sauvegarde cohérente. Optionnellement, les requesters coordonnent leurs efforts avec les writers présents sur le système. Si il ne le font pas, il semble qu'on ne peut pas restaurer par le VSS. On se retrouve avec un état dit "crash consistent", c'est à dire le même état que si vous éteignez sauvagement votre machine. C'est donc ce type de code que l'on va s'efforcer d'écrire.

Et enfin, les writers. Un writer doit être implémenter par une appli qui veux s'assurer de fournir un état cohérent sur le snapshot. Typiquement un SGBDR par exemple (SQL server implémente un writer). Si on veut pouvoir restaurer, à ce que j'ai pu comprendre de la documentation (très obscure pour moi) sur msdn, il va falloir en écrire un. Au moment ou j'écris ces lignes, je n'y suis toujours pas parvenu.

Voila le code source.

Tout d'abord, debug.cpp:
#include "stdafx.h"
#include "vss.h"
#include "vswriter.h"
#include "vsbackup.h"
#include
//#include
#include
#include


LPCWSTR GetStringFromFailureType(HRESULT hrStatus)
{
LPCWSTR pwszFailureType = L"";

switch (hrStatus)
{
case VSS_E_WRITERERROR_INCONSISTENTSNAPSHOT: pwszFailureType = L"VSS_E_WRITERERROR_INCONSISTENTSNAPSHOT"; break;
case VSS_E_WRITERERROR_OUTOFRESOURCES: pwszFailureType = L"VSS_E_WRITERERROR_OUTOFRESOURCES"; break;
case VSS_E_WRITERERROR_TIMEOUT: pwszFailureType = L"VSS_E_WRITERERROR_TIMEOUT"; break;
case VSS_E_WRITERERROR_NONRETRYABLE: pwszFailureType = L"VSS_E_WRITERERROR_NONRETRYABLE"; break;
case VSS_E_WRITERERROR_RETRYABLE: pwszFailureType = L"VSS_E_WRITERERROR_RETRYABLE"; break;
case VSS_E_BAD_STATE: pwszFailureType = L"VSS_E_BAD_STATE"; break;
case VSS_E_PROVIDER_ALREADY_REGISTERED: pwszFailureType = L"VSS_E_PROVIDER_ALREADY_REGISTERED"; break;
case VSS_E_PROVIDER_NOT_REGISTERED: pwszFailureType = L"VSS_E_PROVIDER_NOT_REGISTERED"; break;
case VSS_E_PROVIDER_VETO: pwszFailureType = L"VSS_E_PROVIDER_VETO"; break;
case VSS_E_PROVIDER_IN_USE: pwszFailureType = L"VSS_E_PROVIDER_IN_USE"; break;
case VSS_E_OBJECT_NOT_FOUND: pwszFailureType = L"VSS_E_OBJECT_NOT_FOUND"; break;
case VSS_S_ASYNC_PENDING: pwszFailureType = L"VSS_S_ASYNC_PENDING"; break;
case VSS_S_ASYNC_FINISHED: pwszFailureType = L"VSS_S_ASYNC_FINISHED"; break;
case VSS_S_ASYNC_CANCELLED: pwszFailureType = L"VSS_S_ASYNC_CANCELLED"; break;
case VSS_E_VOLUME_NOT_SUPPORTED: pwszFailureType = L"VSS_E_VOLUME_NOT_SUPPORTED"; break;
case VSS_E_VOLUME_NOT_SUPPORTED_BY_PROVIDER: pwszFailureType = L"VSS_E_VOLUME_NOT_SUPPORTED_BY_PROVIDER"; break;
case VSS_E_OBJECT_ALREADY_EXISTS: pwszFailureType = L"VSS_E_OBJECT_ALREADY_EXISTS"; break;
case VSS_E_UNEXPECTED_PROVIDER_ERROR: pwszFailureType = L"VSS_E_UNEXPECTED_PROVIDER_ERROR"; break;
case VSS_E_CORRUPT_XML_DOCUMENT: pwszFailureType = L"VSS_E_CORRUPT_XML_DOCUMENT"; break;
case VSS_E_INVALID_XML_DOCUMENT: pwszFailureType = L"VSS_E_INVALID_XML_DOCUMENT"; break;
case VSS_E_MAXIMUM_NUMBER_OF_VOLUMES_REACHED: pwszFailureType = L"VSS_E_MAXIMUM_NUMBER_OF_VOLUMES_REACHED"; break;
case VSS_E_FLUSH_WRITES_TIMEOUT: pwszFailureType = L"VSS_E_FLUSH_WRITES_TIMEOUT"; break;
case VSS_E_HOLD_WRITES_TIMEOUT: pwszFailureType = L"VSS_E_HOLD_WRITES_TIMEOUT"; break;
case VSS_E_UNEXPECTED_WRITER_ERROR: pwszFailureType = L"VSS_E_UNEXPECTED_WRITER_ERROR"; break;
case VSS_E_SNAPSHOT_SET_IN_PROGRESS: pwszFailureType = L"VSS_E_SNAPSHOT_SET_IN_PROGRESS"; break;
case VSS_E_MAXIMUM_NUMBER_OF_SNAPSHOTS_REACHED: pwszFailureType = L"VSS_E_MAXIMUM_NUMBER_OF_SNAPSHOTS_REACHED"; break;
case VSS_E_WRITER_INFRASTRUCTURE: pwszFailureType = L"VSS_E_WRITER_INFRASTRUCTURE"; break;
case VSS_E_WRITER_NOT_RESPONDING: pwszFailureType = L"VSS_E_WRITER_NOT_RESPONDING"; break;
case VSS_E_WRITER_ALREADY_SUBSCRIBED: pwszFailureType = L"VSS_E_WRITER_ALREADY_SUBSCRIBED"; break;

case NOERROR:
default:
break;
}

return (pwszFailureType);
}


// This function displays the formatted message at the console and throws
// The passed return code will be returned by vsreq.exe
void Error(
IN INT nReturnCode,
IN const WCHAR* pwszMsgFormat,
IN ...
)
{
va_list marker;
va_start( marker, pwszMsgFormat );
vwprintf( pwszMsgFormat, marker );
va_end( marker );

//BS_ASSERT(FALSE);
// throw that return code.
throw(nReturnCode);
}

Ensuite debug.h

#define CHECK_SUCCESS( Call ) \
{ \
hr = Call; \
if (hr != S_OK) \
Error(1, L"\nError in %S(%d): \n\t- Call %S not succeeded. \n" \
L"\t Error code = 0x%08lx. Error description = %s\n", \
__FILE__, __LINE__, #Call, hr, GetStringFromFailureType(hr)); \
}

#define CHECK_NOFAIL( Call ) \
{ \
hr = Call; \
if (FAILED(hr)) \
Error(1, L"\nError in %S(%d): \n\t- Call %S not succeeded. \n" \
L"\t Error code = 0x%08lx. Error description = %s\n", \
__FILE__, __LINE__, #Call, hr, GetStringFromFailureType(hr)); \
}



void Error(INT nReturnCode, const WCHAR* pwszMsgFormat, ...);
LPCWSTR GetStringFromFailureType(HRESULT hrStatus);
LPCWSTR WszFromRestoreTarget(VSS_RESTORE_TARGET rt);
LPCWSTR WszFromFileRestoreStatus(VSS_FILE_RESTORE_STATUS rs);
void PrintPartialFiles(IVssComponent *pComponent);
void PrintDifferencedFiles(IVssComponent* pComponent);
void PrintNewTargets(IVssComponent *pComponent);
void PrintDirectedTargets(IVssComponent *pComponent);
void PrintRestoreSubcomponents(IVssComponent *pComponent);

Il faudra aussi: BasicFunctions.cpp

#include "stdafx.h"

//Copier un fichier, honteusement pompé sur la faq Developpez
int CopyAFile(char const * const source, char const * const destination)
{
FILE* fSrc = NULL;
FILE* fDest = NULL;
char buffer[512];
int NbLu;

if ((fSrc = fopen(source, "rb")) == NULL)
{
int err = errno;
perror("Cannot open file");
printf("Cannot open source file: %s, error=%d\n", source, err);
exit(1);
}

if ((fDest = fopen(destination, "wb")) == NULL)
{
int err = errno;
perror("Cannot open file");
printf("Cannot open destination file: %s, error=%d\n", destination, err);
fclose(fSrc);
exit(1);
}

while ((NbLu=fread(buffer, 1, 512, fSrc)) != 0)
fwrite(buffer, 1, NbLu, fDest);

fclose(fDest);
fclose(fSrc);

return 0;
}

//Convertir un WCHAR en CHAR, honteusement pompé sur la faq Developpez
void wtoc(CHAR* Dest, const WCHAR* Source)
{
int i =0;
while(Source[i] != '\0')
{
Dest[i] = (CHAR)Source[i];
i++;
}
}


Et voici le code principal

#include "stdafx.h"
#include "Vss.h"
#include "VsWriter.h"
#include "VsBackup.h"
#include "atlbase.h"
#include "atlconv.h"
#include "stdlib.h"
#include "stdio.h"
#include
#include
#include
#include
#include
//#include "BasicFunctions.h"
//#include "macros.h"
//#include "Error.h"
#pragma comment (lib, "VssApi.lib")

int _tmain(int argc, _TCHAR* argv[])
{
//Déclaration des fonctions
int CopyAFile(char const * const source, char const * const destination);
void wtoc(CHAR* Dest, const WCHAR* Source);
void BSTRtoASC (BSTR str, char * &strRet);
void ASCtoBSTR (char * str, BSTR * strRet);
int Split(char const * const FileToSplit);

//Initialisation des composants COM
CoInitializeEx(NULL, COINIT_MULTITHREADED);
CoInitializeSecurity
(
NULL,
-1,
NULL,
NULL,
RPC_C_AUTHN_LEVEL_CONNECT,
RPC_C_IMP_LEVEL_IMPERSONATE,
NULL,
EOAC_NONE,
NULL
);
HRESULT hr;
VSS_ID pSnapShotSetId;
VSS_ID pSnapShotId;
CComBSTR pbstrBackupXML = L"";
CComBSTR pbstrWriterXML = L"";
VSS_SNAPSHOT_PROP pSnapShotProperties;
VSS_PWSZ pSnapShotDevice;
BOOL IsVolumeSupported = TRUE;

//Initialisation du SnapShot
CComPtr pBackupComponent;
CHECK_SUCCESS(CreateVssBackupComponents(&pBackupComponent));
CHECK_SUCCESS(pBackupComponent->InitializeForBackup(pbstrBackupXML));
CHECK_SUCCESS(pBackupComponent->SetBackupState(TRUE, TRUE, VSS_BT_FULL, FALSE));
CComPtr pIVssAsync1;
CHECK_SUCCESS(pBackupComponent->GatherWriterMetadata(&pIVssAsync1));
CHECK_SUCCESS(pIVssAsync1->Wait());

//Pour la restauration: Combien de writers présents et lequel utiliser?
UINT cWriters = 0;
UINT iWriters = 0;
UINT TabInit = 0;
CHECK_SUCCESS(pBackupComponent->GetWriterMetadataCount(&cWriters));
printf("\nNombre de Writers presents: %i\n", cWriters);
VSS_ID idWriterInstance;
VSS_ID idWriter;
BSTR pWriterName;
VSS_USAGE_TYPE pUsage;
VSS_SOURCE_TYPE pSource;
CComPtr pExamineWriterMetadata;
for (iWriters = 0; iWriters <>
{
CHECK_SUCCESS(pBackupComponent->GetWriterMetadata(iWriters, &idWriterInstance, &pExamineWriterMetadata));
CHECK_SUCCESS(pExamineWriterMetadata->GetIdentity(&idWriterInstance, &idWriter, &pWriterName, &pUsage, &pSource));
if (((pUsage == 2) || (pUsage == 3)) && (pSource == 3))
{
TabInit ++;
}
}
//Maintenant que l'on sait lesquels, on recommence l'itération pour mettre les id dans un //tableau. ça ne marche pas terrible, ya un truc qui m'a échappé. Pas grave, de toute façon, il //faut que h'écrive mon writer...
printf("Nombre de Writers qui correspondent: %i\n", TabInit);
UINT SizeTab = (TabInit * 2);
VSS_ID TabWriter[sizeof(SizeTab)];
TabInit = 0;
for (iWriters = 0; iWriters <>
{
TabWriter[iWriters] = GUID_NULL;
}
for (iWriters = 0; iWriters <>
{
CHECK_SUCCESS(pBackupComponent->GetWriterMetadata(iWriters, &idWriterInstance, &pExamineWriterMetadata));
CHECK_SUCCESS(pExamineWriterMetadata->GetIdentity(&idWriterInstance, &idWriter, &pWriterName, &pUsage, &pSource));

if (((pUsage == 2) || (pUsage == 3)) && (pSource == 3))
{
printf("\nidWriterInstance: %li\n", idWriterInstance);
printf("idWriter: %li\n", idWriter);
printf("WriterName: %ls\n", pWriterName);
printf("Usage: %i\n", pUsage);
printf("Source: %i\n", pSource);
TabWriter[TabInit] = idWriterInstance;
TabInit ++;
TabWriter[TabInit] = idWriter;
TabInit ++;

}
}
//Pour la restauration\

CHECK_SUCCESS(pBackupComponent->StartSnapshotSet(&pSnapShotSetId));

//Vérification que le volume supporte les SnapShots...
CHECK_SUCCESS(pBackupComponent->IsVolumeSupported(GUID_NULL, L"C:\\", &IsVolumeSupported));
if (IsVolumeSupported == TRUE)
{
printf("\n***Le volume supporte les SnapShots, on continue...***\n\n");
}
else
{
printf("***Le volume ne supporte pas les SnapShot, on arrête...dommage***\n\n");
exit(1);
}

//Pour la restauration:
CHECK_SUCCESS(pBackupComponent->AddComponent(TabWriter[0], TabWriter[1], VSS_CT_FILEGROUP, NULL, L"MyBackup"));
//Pour la restauration\

//SnapShot du volume C:
CHECK_SUCCESS(pBackupComponent->AddToSnapshotSet(L"C:\\", GUID_NULL, &pSnapShotId));
CComPtr pIVssAsync2;
CHECK_SUCCESS(pBackupComponent->DoSnapshotSet(&pIVssAsync2));
CHECK_SUCCESS(pIVssAsync2->Wait());

//On récupère les info du Snapshot pour copier un ficher verrouillé
CHECK_SUCCESS(pBackupComponent->GetSnapshotProperties(pSnapShotId, &pSnapShotProperties));
pSnapShotDevice=pSnapShotProperties.m_pwszSnapshotDeviceObject;
printf("Lecteur virtuel du SnapShot: %ws\n\n", pSnapShotDevice);
WCHAR *FileToSave = L"\\Documents and Settings\\scolyo\\NTUSER.DAT";
printf("Fichier a sauvegarder: %ls\n\n", FileToSave);
wcsncat(pSnapShotDevice, FileToSave, 100);
printf("Chemin complet du fichier a sauvegarder: %ls\n\n", pSnapShotDevice);
WCHAR *DestFile = L"C:\\BETest\\NTUSER.DAT";

CHAR SourceFile[450+1] = "null";
wtoc(SourceFile, pSnapShotDevice);
printf("\nSourceFile: %s\n\n", SourceFile);
CHAR DestinationFile[450+1] = "null";
wtoc(DestinationFile, DestFile);
printf("\nDestinationFile: %s\n\n", DestinationFile);

CopyAFile(SourceFile, DestinationFile);

CHECK_SUCCESS(pBackupComponent->SaveAsXML(&pbstrBackupXML));
printf("String BackupXML: %ls\n\n", pbstrBackupXML);

//On créer le document XML du BackupComponent de la sauvegarde, nécessaire pour la restauration
//Convertissons un bstr en char
char * CharXML = NULL;
BSTRtoASC(pbstrBackupXML, CharXML);
FILE* FileXML = NULL;
FileXML = fopen("C:\\BETest\\BackupComponent.xml", "w");
if (FileXML != NULL)
{
fputs(CharXML, FileXML);
fclose(FileXML);
}
else
{
printf("Cannot create XML file: %s\n\n", pbstrBackupXML);
}

//Pour la restauration
CHECK_SUCCESS(pExamineWriterMetadata->SaveAsXML(&pbstrWriterXML));
printf("String WriterXML %ls\n\n", pbstrWriterXML);
CharXML = NULL;
BSTRtoASC(pbstrWriterXML, CharXML);
FileXML = NULL;
FileXML = fopen("C:\\BETest\\WriterComponent.xml", "w");
if (FileXML != NULL)
{
fputs(CharXML, FileXML);
fclose(FileXML);
}
else
{
printf("Cannot create XML file: %s\n\n", pbstrWriterXML);
}
//Pour la restauration\

//On détruit proprement le snapshot avant de quitter
LONG pDeletedSnapShot;
VSS_ID pNonDeletedSnapShotId;
CHECK_SUCCESS(pBackupComponent->DeleteSnapshots(pSnapShotId, VSS_OBJECT_SNAPSHOT, TRUE, &pDeletedSnapShot, &pNonDeletedSnapShotId));
printf("***SnapShot wiped ***\n");

return 0;
}

Voila, avec ce code, vous êtes capable de copier un fichier verrouiller (modifier les lignes en rouge pour les adapter à votre cas).

Pour plus de détails, consulter les pages du msdn:

http://msdn.microsoft.com/en-us/library/aa384649(VS.85).aspx

Vous pouvez aussi me contacter, si je peux aider, ce sera avec plaisir ;-)