windows-nt/Source/XPSP1/NT/shell/ext/cscui/pin/cscpin.cpp
2020-09-26 16:20:57 +08:00

1181 lines
32 KiB
C++

//+-------------------------------------------------------------------------
//
// Microsoft Windows
//
// Copyright (C) Microsoft Corporation, 2000
//
// File: cscpin.cpp
//
//--------------------------------------------------------------------------
#include "pch.h"
#pragma hdrstop
#include <stdio.h>
#include "cscpin.h"
#include "console.h"
#include "error.h"
#include "exitcode.h"
#include "listfile.h"
#include "print.h"
#include "strings.h"
//-----------------------------------------------------------------------------
// CCscPinItem
//
// This class represents a single item being pinned or unpinned.
// It contains all of the knowledge of how to pin and unpin a file. The
// CCscPin class coordinates the pinning and unpinning of the entire set
// of files.
//-----------------------------------------------------------------------------
class CCscPinItem
{
public:
CCscPinItem(LPCWSTR pszFile,
const WIN32_FIND_DATAW *pfd,
const CPrint& pr);
DWORD Pin(DWORD *pdwCscResult = NULL);
DWORD Unpin(DWORD *pdwCscResult = NULL);
DWORD DeleteIfUnused(void);
private:
WCHAR m_szFile[MAX_PATH];
SHFILEINFOW m_sfi;
WIN32_FIND_DATAW m_fd;
BOOL m_bIsValidUnc; // Is m_szFile a valid UNC?
BOOL m_bIsValidFindData; // Is m_fd valid?
const CPrint& m_pr; // For console/log output.
bool _Skip(void) const;
DWORD _PinFile(LPCWSTR pszFile, WIN32_FIND_DATAW *pfd, DWORD *pdwCscResult);
DWORD _PinOrUnpinLinkTarget(LPCWSTR pszFile, BOOL bPin, DWORD *pdwCscResult);
DWORD _PinLinkTarget(LPCWSTR pszFile, DWORD *pdwCscResult)
{ return _PinOrUnpinLinkTarget(pszFile, TRUE, pdwCscResult); }
DWORD _UnpinLinkTarget(LPCWSTR pszFile, DWORD *pdwCscResult)
{ return _PinOrUnpinLinkTarget(pszFile, FALSE, pdwCscResult); }
DWORD _UnpinFile(LPCWSTR pszFile, WIN32_FIND_DATAW *pfd, DWORD *pdwCscResult);
DWORD _GetDesiredPinCount(LPCWSTR pszFile);
void _DecrementPinCountForFile(LPCWSTR pszFile, DWORD dwCurrentPinCount);
BOOL _IsSpecialRedirectedFile(LPCWSTR pszFile);
WIN32_FIND_DATAW *_FindDataPtrOrNull(void)
{ return m_bIsValidFindData ? &m_fd : NULL; }
//
// Prevent copy.
//
CCscPinItem(const CCscPinItem& rhs); // not implemented.
CCscPinItem& operator = (const CCscPinItem& rhs); // not implemented.
};
CCscPinItem::CCscPinItem(
LPCWSTR pszFile,
const WIN32_FIND_DATAW *pfd, // Optional. May be NULL.
const CPrint& pr
) : m_bIsValidUnc(FALSE),
m_bIsValidFindData(FALSE),
m_pr(pr)
{
TraceAssert(NULL != pszFile);
TraceAssert(NULL == pfd || !IsBadReadPtr(pfd, sizeof(*pfd)));
lstrcpynW(m_szFile, pszFile, ARRAYSIZE(m_szFile));
if (NULL != pfd)
{
m_fd = *pfd;
m_bIsValidFindData = TRUE;
}
ZeroMemory(&m_sfi, sizeof(m_sfi));
m_sfi.dwAttributes = SFGAO_FILESYSTEM | SFGAO_LINK;
if (PathIsUNCW(m_szFile) &&
SHGetFileInfoW(m_szFile, 0, &m_sfi, sizeof(m_sfi), SHGFI_ATTRIBUTES | SHGFI_ATTR_SPECIFIED))
{
m_bIsValidUnc = true;
}
}
//
// Pins the item's file. If the item is a link, the link target
// is also pinned.
// Returns one of the CSCPROC_RETURN_XXXXX codes.
// Optionally returns the result of CSCPinFile.
//
DWORD
CCscPinItem::Pin(
DWORD *pdwCscResult // Optional. Default is NULL.
)
{
TraceEnter(TRACE_ADMINPIN, "CCscPinItem::Pin");
DWORD dwCscResult = ERROR_SUCCESS;
DWORD dwResult = CSCPROC_RETURN_SKIP;
if (!_Skip())
{
if (SFGAO_LINK & m_sfi.dwAttributes)
{
//
// Ignore result from pinning the link target.
//
DWORD dwCscResultIgnored;
_PinLinkTarget(m_szFile, &dwCscResultIgnored);
}
dwResult = _PinFile(m_szFile, _FindDataPtrOrNull(), &dwCscResult);
}
if (NULL != pdwCscResult)
{
*pdwCscResult = dwCscResult;
}
TraceLeaveValue(dwResult);
}
//
// Unpins the item's file.
// Returns one of the CSCPROC_RETURN_XXXXX codes.
// Optionally returns the result of CSCUnpinFile.
//
DWORD
CCscPinItem::Unpin(
DWORD *pdwCscResult // Optional. Default is NULL.
)
{
TraceEnter(TRACE_ADMINPIN, "CCscPinItem::Unpin");
DWORD dwCscResult = ERROR_SUCCESS;
DWORD dwResult = CSCPROC_RETURN_SKIP;
if (!_Skip())
{
if (SFGAO_LINK & m_sfi.dwAttributes)
{
//
// Ignore result from unpinning the link target.
//
DWORD dwCscResultIgnored;
_UnpinLinkTarget(m_szFile, &dwCscResultIgnored);
}
dwResult = _UnpinFile(m_szFile, _FindDataPtrOrNull(), &dwCscResult);
}
if (NULL != pdwCscResult)
{
*pdwCscResult = dwCscResult;
}
TraceLeaveResult(dwResult);
}
//
// Delete an item if it is no longer used.
//
DWORD
CCscPinItem::DeleteIfUnused(
void
)
{
TraceEnter(TRACE_ADMINPIN, "CCscPin::DeleteIfUnused");
DWORD dwStatus = 0;
DWORD dwPinCount = 0;
DWORD dwHintFlags = 0;
DWORD dwResult = ERROR_SUCCESS;
if (CSCQueryFileStatusW(m_szFile, &dwStatus, &dwPinCount, &dwHintFlags) &&
0 == dwPinCount &&
0 == dwHintFlags &&
!(dwStatus & FLAG_CSCUI_COPY_STATUS_LOCALLY_DIRTY))
{
dwResult = CscDelete(m_szFile);
if (ERROR_SUCCESS == dwResult)
{
m_pr.PrintVerbose(L"Deleted \"%s\" from cache.\n", m_szFile);
ShellChangeNotify(m_szFile, _FindDataPtrOrNull(), FALSE);
}
else
{
if (ERROR_DIR_NOT_EMPTY == dwResult)
{
dwResult = ERROR_SUCCESS;
}
if (ERROR_SUCCESS != dwResult)
{
m_pr.PrintAlways(L"Error deleting \"%s\" from cache. %s\n",
m_szFile, CWinError(dwResult).Text());
}
}
}
TraceLeaveValue(dwResult);
}
//
// Internal function for pinning a file. This is a common
// function called by both Pin() and _PinOrUnpinLinkTarget().
//
DWORD
CCscPinItem::_PinFile(
LPCWSTR pszFile, // UNC path of file to pin.
WIN32_FIND_DATAW *pfd, // Optional. May be NULL.
DWORD *pdwCscResult // Result of CSCPinFile.
)
{
TraceEnter(TRACE_ADMINPIN, "CCscPinItem::_PinFile");
TraceAssert(NULL != pszFile);
TraceAssert(!IsBadStringPtr(pszFile, MAX_PATH));
TraceAssert(NULL != pdwCscResult);
*pdwCscResult = ERROR_SUCCESS;
//
// Collect cache information for the item.
// This may fail, for example if the file is not in the cache
//
DWORD dwPinCount = 0;
DWORD dwHintFlags = 0;
CSCQueryFileStatusW(pszFile, NULL, &dwPinCount, &dwHintFlags);
//
// Is the admin flag already turned on?
//
const BOOL bNewItem = !(dwHintFlags & FLAG_CSC_HINT_PIN_ADMIN);
if (bNewItem)
{
//
// Turn on the admin flag
//
dwHintFlags |= FLAG_CSC_HINT_PIN_ADMIN;
if (CSCPinFileW(pszFile,
dwHintFlags,
NULL,
&dwPinCount,
&dwHintFlags))
{
m_pr.PrintVerbose(L"Pin \"%s\"\n", pszFile);
ShellChangeNotify(pszFile, pfd, FALSE);
}
else
{
const DWORD dwErr = GetLastError();
if (ERROR_INVALID_NAME == dwErr)
{
//
// This is the error we get from CSC when trying to
// pin a file in the exclusion list. Display a unique
// error message for this particular situation.
//
m_pr.PrintAlways(L"Pinning file \"%s\" is not allowed.\n", pszFile);
}
else
{
m_pr.PrintAlways(L"Error pinning \"%s\". %s\n",
pszFile,
CWinError(dwErr).Text());
}
*pdwCscResult = dwErr;
}
}
else
{
m_pr.PrintVerbose(L"\"%s\" already pinned.\n", pszFile);
}
TraceLeaveValue(CSCPROC_RETURN_CONTINUE);
}
//
//.Get the target of a link and pin it.
//
DWORD
CCscPinItem::_PinOrUnpinLinkTarget(
LPCWSTR pszFile, // UNC of link file.
BOOL bPin,
DWORD *pdwCscResult // Result of CSCPinFile on target.
)
{
TraceEnter(TRACE_ADMINPIN, "CCscPinItem::_PinOrUnpinLinkTarget");
TraceAssert(NULL != pszFile);
TraceAssert(!IsBadStringPtr(pszFile, MAX_PATH));
TraceAssert(NULL != pdwCscResult);
*pdwCscResult = ERROR_SUCCESS;
DWORD dwResult = CSCPROC_RETURN_CONTINUE;
LPWSTR pszTarget = NULL;
//
// We only want to pin a link target if it's a file (not a directory).
// GetLinkTarget does this check and only returns files.
//
GetLinkTarget(pszFile, NULL, &pszTarget);
if (NULL != pszTarget)
{
WIN32_FIND_DATAW fd = {0};
LPCWSTR pszT = PathFindFileName(pszTarget);
fd.dwFileAttributes = 0;
lstrcpynW(fd.cFileName, pszT ? pszT : pszTarget, ARRAYSIZE(fd.cFileName));
//
// Pin the target
//
if (bPin)
{
dwResult = _PinFile(pszTarget, &fd, pdwCscResult);
}
else
{
dwResult = _UnpinFile(pszTarget, &fd, pdwCscResult);
}
LocalFree(pszTarget);
}
TraceLeaveValue(dwResult);
}
DWORD
CCscPinItem::_UnpinFile(
LPCWSTR pszFile, // UNC of file to unpin.
WIN32_FIND_DATAW *pfd, // Optional. May be NULL.
DWORD *pdwCscResult // Result of CSCUnpinFile
)
{
TraceEnter(TRACE_ADMINPIN, "CCscPinItem::_UnpinFile");
TraceAssert(NULL != pszFile);
TraceAssert(!IsBadStringPtr(pszFile, MAX_PATH));
TraceAssert(NULL != pdwCscResult);
*pdwCscResult = ERROR_SUCCESS;
//
// Collect cache information for the item.
// This may fail, for example if the file is not in the cache
//
DWORD dwPinCount = 0;
DWORD dwHintFlags = 0;
DWORD dwStatus = 0;
CSCQueryFileStatusW(pszFile, &dwStatus, &dwPinCount, &dwHintFlags);
if (dwHintFlags & FLAG_CSC_HINT_PIN_ADMIN)
{
DWORD dwStatus = 0;
DWORD dwHintFlags = 0;
//
// Decrement pin count. Amount decremented depends on the file.
// Win2000 deployment code increments the pin count of some special
// folders as well as for the desktop.ini file in those special
// folders. In those cases, we want to leave the pin count at
// 1. For all other files, the pin count can drop to zero.
//
_DecrementPinCountForFile(pszFile, dwPinCount);
//
// Clear system-pin flag (aka admin-pin flag).
//
dwHintFlags |= FLAG_CSC_HINT_PIN_ADMIN;
if (CSCUnpinFileW(pszFile,
dwHintFlags,
&dwStatus,
&dwPinCount,
&dwHintFlags))
{
m_pr.PrintVerbose(L"Unpin \"%s\"\n", pszFile);
if (FLAG_CSC_COPY_STATUS_IS_FILE & dwStatus)
{
//
// Delete a file here. Directories are deleted
// on the backside of the post-order traversal
// in CscPin::_FolderCallback.
//
DeleteIfUnused();
}
ShellChangeNotify(pszFile, pfd, FALSE);
}
else
{
*pdwCscResult = GetLastError();
m_pr.PrintAlways(L"Error unpinning \"%s\". %s\n",
pszFile,
CWinError(*pdwCscResult).Text());
}
}
TraceLeaveValue(CSCPROC_RETURN_CONTINUE);
}
//
// As part of the unpin operation, we decrement the pin count
// to either 0 or 1. Folder redirection (contact RahulTh) increments
// the pin count of redirected special folders and the desktop.ini file
// within those folders. In those cases, we want to leave the
// pin count at 1 so that we don't upset the behavior of redirected
// folders. For all other files we drop the pin count to 0.
//
void
CCscPinItem::_DecrementPinCountForFile(
LPCWSTR pszFile,
DWORD dwCurrentPinCount
)
{
DWORD dwStatus = 0;
DWORD dwPinCount = 0;
DWORD dwHintFlags = 0;
const DWORD dwDesiredPinCount = _GetDesiredPinCount(pszFile);
while(dwCurrentPinCount-- > dwDesiredPinCount)
{
dwHintFlags = FLAG_CSC_HINT_COMMAND_ALTER_PIN_COUNT;
CSCUnpinFileW(pszFile,
dwHintFlags,
&dwStatus,
&dwPinCount,
&dwHintFlags);
}
}
//
// This function returns the desired pin count (0 or 1) for a
// given file. Returns 1 for any redirected special folders
// and the desktop.ini file within those folders. Returns 0
// for all other files.
//
DWORD
CCscPinItem::_GetDesiredPinCount(
LPCWSTR pszFile
)
{
TraceAssert(NULL != pszFile);
TraceAssert(!IsBadStringPtr(pszFile, MAX_PATH));
DWORD dwPinCount = 0; // Default for most files.
if (_IsSpecialRedirectedFile(pszFile))
{
dwPinCount = 1;
}
return dwPinCount;
}
//
// Determines if a path is a "special" file pinned by the folder
// redirection code.
//
BOOL
CCscPinItem::_IsSpecialRedirectedFile(
LPCWSTR pszPath
)
{
TraceAssert(NULL != pszPath);
TraceAssert(!IsBadStringPtr(pszPath, MAX_PATH));
//
// This list of special folder IDs provided by RahulTh (08/30/00).
// These are the paths that may be pinned by folder redirection.
//
static struct
{
int csidl;
WCHAR szPath[MAX_PATH];
int cchPath;
} rgFolderPaths[] = {
{ CSIDL_PERSONAL, 0, 0 },
{ CSIDL_MYPICTURES, 0, 0 },
{ CSIDL_DESKTOPDIRECTORY, 0, 0 },
{ CSIDL_STARTMENU, 0, 0 },
{ CSIDL_PROGRAMS, 0, 0 },
{ CSIDL_STARTUP, 0, 0 },
{ CSIDL_APPDATA, 0, 0 }
};
int i;
if (L'\0' == rgFolderPaths[0].szPath[0])
{
//
// Initialize the special folder path data.
// One-time only initialization.
//
for (i = 0; i < ARRAYSIZE(rgFolderPaths); i++)
{
if (!SHGetSpecialFolderPath(NULL,
rgFolderPaths[i].szPath,
rgFolderPaths[i].csidl | CSIDL_FLAG_DONT_VERIFY,
FALSE))
{
m_pr.PrintAlways(L"Error getting path for shell special folder %d. %s\n",
rgFolderPaths[i].csidl,
CWinError(GetLastError()).Text());
}
else
{
//
// Calculate and cache the length.
//
rgFolderPaths[i].cchPath = lstrlen(rgFolderPaths[i].szPath);
}
}
}
const int cchPath = lstrlen(pszPath);
for (i = 0; i < ARRAYSIZE(rgFolderPaths); i++)
{
int cchThis = rgFolderPaths[i].cchPath;
LPCWSTR pszThis = rgFolderPaths[i].szPath;
if (cchPath >= cchThis)
{
//
// Path being examined is the same length or longer than
// current path from the table. Possible match.
//
if (0 == StrCmpNIW(pszPath, pszThis, cchThis))
{
//
// Path being examined is either the same as,
// or a child of, the current path from the table.
//
if (L'\0' == *(pszPath + cchThis))
{
//
// Path is same as this path from the table.
//
return TRUE;
}
else if (0 == lstrcmpiW(pszPath + cchThis + 1, L"desktop.ini"))
{
//
// Path is for a desktop.ini file that exists in the
// root of one of our special folders.
//
return TRUE;
}
}
}
}
return FALSE;
}
//
// Determines if the item should be skipped or not.
//
bool
CCscPinItem::_Skip(
void
) const
{
return !m_bIsValidUnc || (0 == (SFGAO_FILESYSTEM & m_sfi.dwAttributes));
}
//-----------------------------------------------------------------------------
// CCscPin
//-----------------------------------------------------------------------------
CCscPin::CCscPin(
const CSCPIN_INFO& info
) : m_bUseListFile(info.bUseListFile),
m_bPin(info.bPin),
m_bPinDefaultSet(info.bPinDefaultSet),
m_bBreakDetected(FALSE),
m_cFilesPinned(0),
m_cCscErrors(0),
m_pr(info.bVerbose, info.pszLogFile)
{
TraceAssert(NULL != info.pszFile);
lstrcpynW(m_szFile, info.pszFile, ARRAYSIZE(m_szFile));
}
CCscPin::~CCscPin(
void
)
{
}
//
// The only public method on the CCscPin object.
// Just create an object and tell it to Run.
//
HRESULT
CCscPin::Run(
void
)
{
HRESULT hr = E_FAIL;
m_cCscErrors = 0;
m_cFilesPinned = 0;
if (!IsCSCEnabled())
{
m_pr.PrintAlways(L"Offline Files is not enabled.\n");
SetExitCode(CSCPIN_EXIT_CSC_NOT_ENABLED);
}
else if (_IsAdminPinPolicyActive())
{
m_pr.PrintAlways(L"The Offline Files 'admin-pin' policy is active.\n");
SetExitCode(CSCPIN_EXIT_POLICY_ACTIVE);
}
else
{
if (m_bUseListFile)
{
//
// Process files listed in m_szFile.
//
hr = _ProcessPathsInFile(m_szFile);
}
else
{
//
// Process the one file provided on the cmd line.
// Do a quick existence check first.
//
if (DWORD(-1) != GetFileAttributesW(m_szFile))
{
hr = _ProcessThisPath(m_szFile, m_bPin);
}
else
{
m_pr.PrintAlways(L"File \"%s\" not found.\n", m_szFile);
hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
SetExitCode(CSCPIN_EXIT_FILE_NOT_FOUND);
}
}
//
// Flush all change notifications.
//
ShellChangeNotify(NULL, TRUE);
if (0 < m_cFilesPinned && !_DetectConsoleBreak())
{
//
// If we pinned some files, fill all sparse
// files in the cache.
//
_FillSparseFiles();
}
if (0 < m_cCscErrors)
{
SetExitCode(CSCPIN_EXIT_CSC_ERRORS);
}
}
return hr;
}
//
// Callback parameter block passed to _FolderCallback.
//
struct
CSCPIN_FOLDER_CBK_PARAMS
{
CCscPin *pCscPin; // Reference to the CCscPin object.
BOOL bPin; // TRUE == Pin files, FALSE == Unpin.
};
//
// Callback used for enumerating the filesystem. This function
// is called for each file processed.
//
DWORD WINAPI
CCscPin::_FolderCallback(
LPCWSTR pszItem,
ENUM_REASON eReason,
WIN32_FIND_DATAW *pFind32,
LPARAM pContext // Ptr to CSCPIN_FOLDER_CBK_PARAMS.
)
{
TraceEnter(TRACE_ADMINPIN, "CCscPin::_PinFolderCallback");
TraceAssert(NULL != pszItem);
TraceAssert(NULL != pContext);
CSCPIN_FOLDER_CBK_PARAMS *pfcp = (CSCPIN_FOLDER_CBK_PARAMS *)pContext;
CCscPin *pThis = pfcp->pCscPin;
DWORD dwResult = CSCPROC_RETURN_CONTINUE;
if (pThis->_DetectConsoleBreak())
{
TraceLeaveValue(CSCPROC_RETURN_ABORT);
}
if (!pszItem || !*pszItem)
{
TraceLeaveValue(CSCPROC_RETURN_SKIP);
}
if (ENUM_REASON_FILE == eReason || ENUM_REASON_FOLDER_BEGIN == eReason)
{
TraceAssert(NULL != pFind32);
CCscPinItem item(pszItem, pFind32, pThis->m_pr);
DWORD dwCscResult = ERROR_SUCCESS;
if (pfcp->bPin)
{
dwResult = item.Pin(&dwCscResult);
if (ERROR_SUCCESS == dwCscResult)
{
pThis->m_cFilesPinned++;
}
}
else
{
dwResult = item.Unpin(&dwCscResult);
}
if (ERROR_SUCCESS != dwCscResult)
{
pThis->m_cCscErrors++;
}
}
else if (ENUM_REASON_FOLDER_END == eReason && !pfcp->bPin)
{
//
// This code is executed for each folder item after all children
// have been visited in the post-order traversal of the
// CSC filesystem. We use it to remove any empty folder entries
// from the cache.
//
CCscPinItem item(pszItem, pFind32, pThis->m_pr);
item.DeleteIfUnused();
}
TraceLeaveValue(dwResult);
}
//
// Pin or unpin one path string. If it's a folder, all it's children
// are also pinned or unpinned according to the bPin argument.
//
HRESULT
CCscPin::_ProcessThisPath(
LPCWSTR pszFile,
BOOL bPin
)
{
TraceEnter(TRACE_ADMINPIN, "CCscPin::_ProcessThisPath");
TraceAssert(NULL != pszFile);
TraceAssert(!IsBadStringPtr(pszFile, MAX_PATH));
LPCWSTR pszPath = pszFile;
LPWSTR pszUncPath = NULL;
if (!PathIsUNC(pszPath))
{
GetRemotePath(pszPath, &pszUncPath);
pszPath = (LPCWSTR)pszUncPath;
}
if (NULL != pszPath)
{
CSCPIN_FOLDER_CBK_PARAMS CbkParams = { this, bPin };
//
// Process this item
//
DWORD dwResult = _FolderCallback(pszPath, ENUM_REASON_FILE, NULL, (LPARAM)&CbkParams);
//
// Process everything under this folder (if it's a folder)
//
//
// ISSUE-2000/08/28-BrianAu Should we provide the capability to
// limit recursive pinning and unpinning? Maybe in the future
// but not now.
//
if (CSCPROC_RETURN_CONTINUE == dwResult && PathIsUNC(pszPath))
{
_Win32EnumFolder(pszPath, TRUE, _FolderCallback, (LPARAM)&CbkParams);
}
//
// Finally, once we're all done, delete the top level item if it's
// unused.
//
CCscPinItem item(pszPath, NULL, m_pr);
item.DeleteIfUnused();
}
LocalFreeString(&pszUncPath);
TraceLeaveResult(S_OK);
}
//
// Reads paths in the [Pin], [Unpin] and [Default] sections of an INI file.
// For each, call the _ProcessThisPath function.
//
HRESULT
CCscPin::_ProcessPathsInFile(
LPCWSTR pszFile
)
{
TraceEnter(TRACE_ADMINPIN, "CCscPin::_ProcessPathsInFile");
TraceAssert(NULL != pszFile);
TraceAssert(!IsBadStringPtr(pszFile, MAX_PATH));
HRESULT hr = S_OK;
//
// Need a full path name. Otherwise, the PrivateProfile APIs
// used by the CListFile object will assume the file is in
// one of the "system" directories.
//
WCHAR szFile[MAX_PATH];
LPWSTR pszNamePart;
if (0 == GetFullPathNameW(pszFile,
ARRAYSIZE(szFile),
szFile,
&pszNamePart))
{
const DWORD dwErr = GetLastError();
hr = HRESULT_FROM_WIN32(dwErr);
SetExitCode(CSCPIN_EXIT_LISTFILE_NO_OPEN);
m_pr.PrintAlways(L"Error expanding path \"%s\". %s\n",
pszFile,
CWinError(hr).Text());
}
else
{
//
// Before we go any further, verify the file really exists.
//
if (DWORD(-1) == GetFileAttributesW(szFile))
{
hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
SetExitCode(CSCPIN_EXIT_LISTFILE_NO_OPEN);
m_pr.PrintAlways(L"Error opening input file \"%s\". %s\n",
szFile, CWinError(hr).Text());
}
else
{
//
// Read and process the information in the file.
// Note that the listfile object MUST remain alive while the
// iterator is being used.
//
CListFile listfile(szFile);
CDblNulStrIter iter;
typedef HRESULT (CListFile::*PFN)(CDblNulStrIter *);
//
// This table describes the sections read from the listfile,
// the order they are read in and if the files read should
// be 'pinned' or 'unpinned'.
//
static const struct
{
PFN pfn; // Function called to read file contents.
BOOL bQuery; // Query input file for these items?
BOOL bPin; // Action to perform on contents read.
} rgReadFuncs[] = {
{ &CListFile::GetFilesToUnpin, TRUE, FALSE }, // Reads [Unpin] section.
{ &CListFile::GetFilesToPin, TRUE, TRUE }, // Reads [Pin] section.
{ &CListFile::GetFilesDefault, m_bPinDefaultSet, m_bPin }, // Reads [Default] section.
};
for (int i = 0; i < ARRAYSIZE(rgReadFuncs) && !_DetectConsoleBreak(); i++)
{
if (rgReadFuncs[i].bQuery)
{
PFN pfn = rgReadFuncs[i].pfn;
BOOL bPin = rgReadFuncs[i].bPin;
//
// Read the info from the listfile using the appropriate
// function. The returned iterator will iterate over all
// of the files read.
//
hr = (listfile.*pfn)(&iter);
if (SUCCEEDED(hr))
{
//
// Process the entries.
//
LPCWSTR pszPath;
while(iter.Next(&pszPath))
{
//
// Paths in the listfile can contain embedded environment
// strings.
//
TCHAR szPathExpanded[MAX_PATH];
if (0 == ExpandEnvironmentStrings(pszPath, szPathExpanded, ARRAYSIZE(szPathExpanded)))
{
m_pr.PrintAlways(L"Error expanding \"%s\". %s\n",
pszPath,
CWinError(GetLastError()));
lstrcpynW(szPathExpanded, pszPath, ARRAYSIZE(szPathExpanded));
}
hr = _ProcessThisPath(szPathExpanded, bPin);
}
}
}
}
}
}
return hr;
}
//
// Enumerates each share in the cache and attempts to fill all sparse
// files in that share.
//
HRESULT
CCscPin::_FillSparseFiles(
void
)
{
HRESULT hr = S_OK;
m_pr.PrintAlways(L"Copying pinned files into cache...\n");
DWORD dwStatus;
DWORD dwPinCount;
DWORD dwHintFlags;
WIN32_FIND_DATA fd;
FILETIME ft;
CCscFindHandle hFind;
hFind = CacheFindFirst(NULL, &fd, &dwStatus, &dwPinCount, &dwHintFlags, &ft);
if (hFind.IsValid())
{
do
{
const BOOL bFullSync = FALSE;
CSCFillSparseFilesW(fd.cFileName,
bFullSync,
_FillSparseFilesCallback,
(DWORD_PTR)this);
}
while(CacheFindNext(hFind, &fd, &dwStatus, &dwPinCount, &dwHintFlags, &ft));
}
return hr;
}
//
// Called by CSC for each file processed by CSCFillSparseFiles.
//
DWORD WINAPI
CCscPin::_FillSparseFilesCallback(
LPCWSTR pszName,
DWORD dwStatus,
DWORD dwHintFlags,
DWORD dwPinCount,
WIN32_FIND_DATAW *pfd,
DWORD dwReason,
DWORD dwParam1,
DWORD dwParam2,
DWORD_PTR dwContext
)
{
TraceAssert(NULL != dwContext);
CCscPin *pThis = (CCscPin *)dwContext;
DWORD dwResult = CSCPROC_RETURN_CONTINUE;
if (pThis->_DetectConsoleBreak())
{
dwResult = CSCPROC_RETURN_ABORT;
}
else
{
switch(dwReason)
{
case CSCPROC_REASON_BEGIN:
pThis->m_pr.PrintVerbose(L"Filling file \"%s\"\n", pszName);
break;
case CSCPROC_REASON_END:
dwParam2 = pThis->_TranslateFillResult(dwParam2, dwStatus, &dwResult);
if (ERROR_SUCCESS != dwParam2)
{
pThis->m_cCscErrors++;
pThis->m_pr.PrintAlways(L"Error filling \"%s\" %s\n",
pszName,
CWinError(dwParam2).Text());
}
break;
default:
break;
}
}
TraceLeaveValue(dwResult);
}
//
// Translates the error code and status provided by CSC from CSCFillSparseFiles
// into the correct error code and CSCPROC_RETURN_XXXXXX value. Some errors
// require translation before presentation to the user.
//
DWORD
CCscPin::_TranslateFillResult(
DWORD dwError,
DWORD dwStatus,
DWORD *pdwCscAction
)
{
DWORD dwResult = dwError;
DWORD dwAction = CSCPROC_RETURN_CONTINUE;
if (ERROR_SUCCESS != dwError)
{
if (3000 <= dwError && dwError <= 3200)
{
//
// Special internal CSC error codes.
//
dwResult = ERROR_SUCCESS;
}
else
{
switch(dwError)
{
case ERROR_OPERATION_ABORTED:
dwResult = ERROR_SUCCESS;
dwAction = CSCPROC_RETURN_ABORT;
break;
case ERROR_GEN_FAILURE:
if (FLAG_CSC_COPY_STATUS_FILE_IN_USE & dwStatus)
{
dwResult = ERROR_OPEN_FILES;
}
break;
case ERROR_DISK_FULL:
dwAction = CSCPROC_RETURN_ABORT;
break;
default:
break;
}
}
}
if (NULL != pdwCscAction)
{
*pdwCscAction = dwAction;
}
return dwResult;
}
//
// Determine if the admin-pin policy is active on the current
// computer.
//
BOOL
CCscPin::_IsAdminPinPolicyActive(
void
)
{
const HKEY rghkeyRoots[] = { HKEY_LOCAL_MACHINE, HKEY_CURRENT_USER };
BOOL bIsActive = FALSE;
for (int i = 0; !bIsActive && i < ARRAYSIZE(rghkeyRoots); i++)
{
HKEY hkey;
if (ERROR_SUCCESS == RegOpenKey(rghkeyRoots[i], c_szRegKeyAPF, &hkey))
{
WCHAR szName[MAX_PATH];
DWORD cchName = ARRAYSIZE(szName);
if (ERROR_SUCCESS == RegEnumValue(hkey,
0,
szName,
&cchName,
NULL,
NULL,
NULL,
NULL))
{
bIsActive = TRUE;
}
RegCloseKey(hkey);
}
}
return bIsActive;
}
//
// Determine if one of the following system events has occured.
//
// 1. User pressed Ctrl-C.
// 2. User pressed Ctrl-Break.
// 3. Console window was closed.
// 4. User logged off.
//
// If one of these events has occured, an output message is generated
// and TRUE is returned.
// Otherwise, FALSE is returned.
// Note that the output message is generated only once.
//
BOOL
CCscPin::_DetectConsoleBreak(
void
)
{
if (!m_bBreakDetected)
{
DWORD dwCtrlEvent;
m_bBreakDetected = ConsoleHasCtrlEventOccured(&dwCtrlEvent);
if (m_bBreakDetected)
{
m_pr.PrintAlways(L"Program aborted. ");
switch(dwCtrlEvent)
{
case CTRL_C_EVENT:
m_pr.PrintAlways(L"User pressed Ctrl-C\n");
break;
case CTRL_BREAK_EVENT:
m_pr.PrintAlways(L"User pressed Ctrl-Break\n");
break;
case CTRL_CLOSE_EVENT:
m_pr.PrintAlways(L"Application forceably closed.\n");
break;
case CTRL_LOGOFF_EVENT:
m_pr.PrintAlways(L"User logged off.\n");
break;
default:
m_pr.PrintAlways(L"Unknown console break event %d\n", dwCtrlEvent);
break;
}
}
}
return m_bBreakDetected;
}