///////////////////////////////////////////////////////////////////////// // Copyright © 2006 Microsoft Corporation. All rights reserved. // // This file may contain preliminary information or inaccuracies, // and may not correctly represent any associated Microsoft // Product as commercially released. All Materials are provided entirely // “AS IS.” To the extent permitted by law, MICROSOFT MAKES NO // WARRANTY OF ANY KIND, DISCLAIMS ALL EXPRESS, IMPLIED AND STATUTORY // WARRANTIES, AND ASSUMES NO LIABILITY TO YOU FOR ANY DAMAGES OF // ANY TYPE IN CONNECTION WITH THESE MATERIALS OR ANY INTELLECTUAL PROPERTY IN THEM. // // Main header #include "stdafx.h" // Expose the given shadow copy set as a mount point or drive letter void VssClient::ExposeSnapshotLocally(VSS_ID snapshotID, wstring path) { FunctionTracer ft(DBG_INFO); ft.WriteLine(L"- Exposing shadow copy " WSTR_GUID_FMT L" under the path '%s'", GUID_PRINTF_ARG(snapshotID), path.c_str()); // Make sure that the expose operation is valid for this snapshot. // Get the shadow copy properties VSS_SNAPSHOT_PROP Snap; HRESULT hr = m_pVssObject->GetSnapshotProperties(snapshotID, &Snap); if (hr == VSS_E_OBJECT_NOT_FOUND) { ft.WriteLine(L"\nERROR: there is no snapshot with the given ID"); throw(E_INVALIDARG); } // Automatically call VssFreeSnapshotProperties on this structure at the end of scope CAutoSnapPointer snapAutoCleanup(&Snap); // Client Accessible snapshots are not exposable if (Snap.m_lSnapshotAttributes & VSS_VOLSNAP_ATTR_CLIENT_ACCESSIBLE) { ft.WriteLine(L"\nERROR: the snapshot ID identifies a Client Accessible snapshot which cannot be exposed"); throw(E_INVALIDARG); } // Client Accessible snapshots are not exposable if ((Snap.m_pwszExposedName != NULL) || (Snap.m_pwszExposedPath != NULL)) { ft.WriteLine(L"\nERROR: Client-accessible (SFSF) snapshots cannot be exposed."); throw(E_INVALIDARG); } // // Check if the path parameter is valid // // If this looks like a drive letter then make sure that the drive letter is not in use if ((path.length() == 2) && path[1] == L':') { ft.WriteLine(L"- Checking if '%s' is a valid drive letter ...", path.c_str()); wstring device(MAX_PATH, L'\0'); if (0 != QueryDosDeviceW( path.c_str(), WString2Buffer(device), (DWORD)device.length())) { ft.WriteLine(L"\nERROR: the second parameter to -el [%s] is a drive letter already in use!", path.c_str()); throw(E_INVALIDARG); } } else { // Append a backslash if (path[path.length() - 1] != L'\\') path += L'\\'; ft.WriteLine(L"- Checking if '%s' is a valid empty directory ...", path.c_str()); // Make sure this is a directory DWORD dwAttributes = GetFileAttributes(path.c_str()); if ((dwAttributes == INVALID_FILE_ATTRIBUTES) || ((dwAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0)) { ft.WriteLine(L"\nERROR: the second parameter to -el [%s] is not a valid directory!", path.c_str()); throw(E_INVALIDARG); } // Make sure that this directory is empty WIN32_FIND_DATA FindFileData; wstring pattern = path + L'*'; HANDLE hFind = FindFirstFile( pattern.c_str(), &FindFileData); if (hFind == INVALID_HANDLE_VALUE) CHECK_WIN32_ERROR(GetLastError(), L"FindFirstFile"); // Automatically calls FindClose at the end of scope CAutoSearchHandle autoHandle(hFind); // Enumerate all the files/subdirectories while (true) { wstring fileName = FindFileData.cFileName; if ((fileName != wstring(L".")) && (fileName != wstring(L".."))) { ft.WriteLine(L"\nERROR: the second parameter to -el [%s] is not an empty directory!", path.c_str()); throw(E_INVALIDARG); } if (!FindNextFile(hFind, &FindFileData)) { if (GetLastError() == ERROR_NO_MORE_FILES) break; CHECK_WIN32_ERROR(GetLastError(), L"FindNextFile"); } } } // Expose locally the shadow copy set LPWSTR pwszExposed = NULL; CHECK_COM(m_pVssObject->ExposeSnapshot(snapshotID, NULL, VSS_VOLSNAP_ATTR_EXPOSED_LOCALLY, (VSS_PWSZ)path.c_str(), &pwszExposed)); // Automatically call CoTaskMemFree on this pointer at the end of scope CAutoComPointer ptrAutoCleanup(pwszExposed); ft.WriteLine(L"- Shadow copy exposed as '%s'", pwszExposed); } // Expose the given shadow copy set as a share void VssClient::ExposeSnapshotRemotely(VSS_ID snapshotID, wstring shareName, wstring pathFromRoot) { FunctionTracer ft(DBG_INFO); ft.WriteLine(L"- Exposing shadow copy " WSTR_GUID_FMT L" under the share '%s' (path from root: '%s')", GUID_PRINTF_ARG(snapshotID), shareName.c_str(), pathFromRoot.c_str()); // Make sure that the expose operation is valid for this snapshot. // Get the shadow copy properties VSS_SNAPSHOT_PROP Snap; HRESULT hr = m_pVssObject->GetSnapshotProperties(snapshotID, &Snap); if (hr == VSS_E_OBJECT_NOT_FOUND) { ft.WriteLine(L"\nERROR: there is no snapshot with the given ID"); throw(E_INVALIDARG); } // Automatically call VssFreeSnapshotProperties on this structure at the end of scope CAutoSnapPointer snapAutoCleanup(&Snap); // Client Accessible snapshots are not exposable if (Snap.m_lSnapshotAttributes & VSS_VOLSNAP_ATTR_CLIENT_ACCESSIBLE) { ft.WriteLine(L"\nERROR: the snapshot ID identifies a Client Accessible snapshot which cannot be exposed"); throw(E_INVALIDARG); } // Client Accessible snapshots are not exposable if ((Snap.m_pwszExposedName != NULL) || (Snap.m_pwszExposedPath != NULL)) { ft.WriteLine(L"\nERROR: Client-accessible (SFSF) snapshots cannot be exposed."); throw(E_INVALIDARG); } // Note: a true reqestor should also check here if // - the remote share name is valid (i.e. unused) // - the path from root is valid // The path from root represents where to put the share on the snapshot volume. // If the share is on root, then use a NULL argument LPWSTR pwszPathFromRoot = NULL; if (pathFromRoot.length() != 0) pwszPathFromRoot = (VSS_PWSZ)pathFromRoot.c_str(); // Expose locally the shadow copy set LPWSTR pwszExposed = NULL; CHECK_COM(m_pVssObject->ExposeSnapshot(snapshotID, pwszPathFromRoot, VSS_VOLSNAP_ATTR_EXPOSED_REMOTELY, (VSS_PWSZ)shareName.c_str(), &pwszExposed)); // Automatically call CoTaskMemFree on this pointer at the end of scope CAutoComPointer ptrAutoCleanup(pwszExposed); ft.WriteLine(L"- Shadow copy exposed as '%s'", pwszExposed); }