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

804 lines
23 KiB
C++

//+-------------------------------------------------------------------------
//
// Microsoft Windows
//
// Copyright (C) Microsoft Corporation, 1997 - 2001
//
// File: admin.cpp
//
// Authors;
// Jeff Saathoff (jeffreys)
//
// Notes;
// Support for Administratively pinned folders
//--------------------------------------------------------------------------
#include "pch.h"
#pragma hdrstop
#include "strings.h"
#include "registry.h"
DWORD WINAPI _PinAdminFoldersThread(LPVOID);
//*************************************************************
//
// ApplyAdminFolderPolicy
//
// Purpose: Pin the admin folder list
//
// Parameters: none
//
// Return: none
//
// Notes:
//
//*************************************************************
void
ApplyAdminFolderPolicy(void)
{
BOOL bNoNet = FALSE;
CSCIsServerOffline(NULL, &bNoNet);
if (!bNoNet)
{
SHCreateThread(_PinAdminFoldersThread, NULL, CTF_COINIT | CTF_FREELIBANDEXIT, NULL);
}
}
//
// Does a particular path exist in the DPA of path strings?
//
BOOL
ExistsAPF(
HDPA hdpa,
LPCTSTR pszPath
)
{
const int cItems = DPA_GetPtrCount(hdpa);
for (int i = 0; i < cItems; i++)
{
LPCTSTR pszItem = (LPCTSTR)DPA_GetPtr(hdpa, i);
if (pszItem && (0 == lstrcmpi(pszItem, pszPath)))
return TRUE;
}
return FALSE;
}
BOOL
ReadAPFFromRegistry(HDPA hdpaFiles)
{
const HKEY rghkeyRoots[] = { HKEY_LOCAL_MACHINE, HKEY_CURRENT_USER };
for (int i = 0; i < ARRAYSIZE(rghkeyRoots); i++)
{
HKEY hKey;
// Read in the Administratively pinned folder list.
if (ERROR_SUCCESS == RegOpenKeyEx(rghkeyRoots[i], c_szRegKeyAPF, 0, KEY_QUERY_VALUE, &hKey))
{
TCHAR szName[MAX_PATH];
DWORD dwIndex = 0, dwSize = ARRAYSIZE(szName);
while (ERROR_SUCCESS == _RegEnumValueExp(hKey, dwIndex, szName, &dwSize, NULL, NULL, NULL, NULL))
{
if (!ExistsAPF(hdpaFiles, szName))
{
LPTSTR pszDup;
if (LocalAllocString(&pszDup, szName))
{
if (-1 == DPA_AppendPtr(hdpaFiles, pszDup))
{
LocalFreeString(&pszDup);
}
}
}
dwSize = ARRAYSIZE(szName);
dwIndex++;
}
RegCloseKey(hKey);
}
}
return TRUE;
}
BOOL
BuildFRList(HDPA hdpaFiles)
{
HKEY hKey;
if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_CURRENT_USER, TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders"), 0, KEY_QUERY_VALUE, &hKey))
{
TCHAR szName[MAX_PATH];
DWORD cchName = ARRAYSIZE(szName);
TCHAR szValue[MAX_PATH];
DWORD cbValue = sizeof(szValue);
DWORD dwIndex = 0;
while (ERROR_SUCCESS == RegEnumValue(hKey,
dwIndex,
szName,
&cchName,
NULL,
NULL,
(LPBYTE)szValue,
&cbValue))
{
LPTSTR pszUNC = NULL;
GetRemotePath(szValue, &pszUNC);
if (pszUNC)
{
if (-1 == DPA_AppendPtr(hdpaFiles, pszUNC))
{
LocalFreeString(&pszUNC);
}
}
cchName = ARRAYSIZE(szName);
cbValue = sizeof(szValue);
dwIndex++;
}
RegCloseKey(hKey);
}
return TRUE;
}
BOOL
ReconcileAPF(HDPA hdpaPin, HDPA hdpaUnpin)
{
HKEY hKey;
int cItems;
int i;
//
// First, try to convert everything to UNC
//
cItems = DPA_GetPtrCount(hdpaPin);
for (i = 0; i < cItems; i++)
{
LPTSTR pszItem = (LPTSTR)DPA_GetPtr(hdpaPin, i);
if (!PathIsUNC(pszItem))
{
LPTSTR pszUNC = NULL;
GetRemotePath(pszItem, &pszUNC);
if (pszUNC)
{
DPA_SetPtr(hdpaPin, i, pszUNC);
LocalFree(pszItem);
}
}
}
// Read in the previous Administratively pinned folder list for this user.
if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_CURRENT_USER, c_szRegKeyAPFResult, 0, KEY_QUERY_VALUE, &hKey))
{
TCHAR szName[MAX_PATH];
DWORD dwIndex = 0, dwSize = ARRAYSIZE(szName);
while (ERROR_SUCCESS == _RegEnumValueExp(hKey, dwIndex, szName, &dwSize, NULL, NULL, NULL, NULL))
{
if (!ExistsAPF(hdpaPin, szName))
{
LPTSTR pszDup = NULL;
// This one is not in the new list, save it in the Unpin list
if (LocalAllocString(&pszDup, szName))
{
if (-1 == DPA_AppendPtr(hdpaUnpin, pszDup))
{
LocalFreeString(&pszDup);
}
}
}
dwSize = ARRAYSIZE(szName);
dwIndex++;
}
RegCloseKey(hKey);
}
// Save out the new admin pin list for this user
if (ERROR_SUCCESS == RegCreateKeyEx(HKEY_CURRENT_USER,
c_szRegKeyAPFResult,
0,
NULL,
REG_OPTION_NON_VOLATILE,
KEY_SET_VALUE,
NULL,
&hKey,
NULL))
{
// Add reg entries from the Pin list
cItems = DPA_GetPtrCount(hdpaPin);
for (i = 0; i < cItems; i++)
{
DWORD dwValue = 0;
RegSetValueEx(hKey, (LPCTSTR)DPA_GetPtr(hdpaPin, i), 0, REG_DWORD, (LPBYTE)&dwValue, sizeof(dwValue));
}
// Remove reg entries from the Unpin list
cItems = DPA_GetPtrCount(hdpaUnpin);
for (i = 0; i < cItems; i++)
{
RegDeleteValue(hKey, (LPCTSTR)DPA_GetPtr(hdpaUnpin, i));
}
RegCloseKey(hKey);
}
return TRUE;
}
DWORD WINAPI
_AdminFillCallback(LPCTSTR /*pszName*/,
DWORD /*dwStatus*/,
DWORD /*dwHintFlags*/,
DWORD /*dwPinCount*/,
LPWIN32_FIND_DATA /*pFind32*/,
DWORD /*dwReason*/,
DWORD /*dwParam1*/,
DWORD /*dwParam2*/,
DWORD_PTR /*dwContext*/)
{
if (WAIT_OBJECT_0 == WaitForSingleObject(g_heventTerminate, 0))
return CSCPROC_RETURN_ABORT;
return CSCPROC_RETURN_CONTINUE;
}
void
_DoAdminPin(LPCTSTR pszItem, LPWIN32_FIND_DATA pFind32)
{
DWORD dwHintFlags = 0;
TraceEnter(TRACE_ADMINPIN, "_DoAdminPin");
if (!pszItem || !*pszItem)
TraceLeaveVoid();
TraceAssert(PathIsUNC(pszItem));
// This may fail, for example if the file is not in the cache
CSCQueryFileStatus(pszItem, NULL, NULL, &dwHintFlags);
// Is the admin flag already turned on?
if (!(dwHintFlags & FLAG_CSC_HINT_PIN_ADMIN))
{
//
// Pin the item
//
if (CSCPinFile(pszItem,
dwHintFlags | FLAG_CSC_HINT_PIN_ADMIN,
NULL,
NULL,
&dwHintFlags))
{
ShellChangeNotify(pszItem, pFind32, FALSE);
}
}
//
// Make sure files are filled.
//
// Yes, this takes longer, and isn't necessary if you stay logged
// on, since the CSC agent fills everything in the background.
//
// However, JDP's are using this with laptop pools, and for
// people who logon just to get the latest stuff, then immediately
// disconnect their laptop and hit the road. They need to have
// everything filled right away.
//
if (!pFind32 || !(pFind32->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
{
CSCFillSparseFiles(pszItem, FALSE, _AdminFillCallback, 0);
}
Trace((TEXT("AdminPin %s"), pszItem));
TraceLeaveVoid();
}
void
_PinLinkTarget(LPCTSTR pszLink)
{
LPTSTR pszTarget = NULL;
TraceEnter(TRACE_ADMINPIN, "_PinLinkTarget");
TraceAssert(pszLink);
// 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(pszLink, &pszTarget, NULL);
if (pszTarget)
{
WIN32_FIND_DATA fd = {0};
LPCTSTR pszT = PathFindFileName(pszTarget);
fd.dwFileAttributes = 0;
lstrcpyn(fd.cFileName, pszT ? pszT : pszTarget, ARRAYSIZE(fd.cFileName));
// Pin the target
_DoAdminPin(pszTarget, &fd);
LocalFree(pszTarget);
}
TraceLeaveVoid();
}
// export this from shell32.dll
BOOL PathIsShortcut(LPCTSTR pszItem, DWORD dwAttributes)
{
BOOL bIsShortcut = FALSE;
SHFILEINFO sfi;
sfi.dwAttributes = SFGAO_LINK;
if (SHGetFileInfo(pszItem, dwAttributes, &sfi, sizeof(sfi), SHGFI_ATTRIBUTES | SHGFI_ATTR_SPECIFIED | SHGFI_USEFILEATTRIBUTES))
{
bIsShortcut = (sfi.dwAttributes & SFGAO_LINK);
}
return bIsShortcut;
}
DWORD WINAPI
_PinAdminFolderCallback(LPCTSTR pszItem,
ENUM_REASON eReason,
LPWIN32_FIND_DATA pFind32,
LPARAM /*lpContext*/)
{
TraceEnter(TRACE_ADMINPIN, "_PinAdminFolderCallback");
TraceAssert(pszItem);
if (WAIT_OBJECT_0 == WaitForSingleObject(g_heventTerminate, 0))
TraceLeaveValue(CSCPROC_RETURN_ABORT);
if (!pszItem || !*pszItem)
TraceLeaveValue(CSCPROC_RETURN_SKIP);
if (eReason == ENUM_REASON_FILE || eReason == ENUM_REASON_FOLDER_BEGIN)
{
// If it's a link, pin the target
if (PathIsShortcut(pszItem, pFind32 ? pFind32->dwFileAttributes : 0))
_PinLinkTarget(pszItem);
// Pin the item
if (PathIsUNC(pszItem))
_DoAdminPin(pszItem, pFind32);
}
TraceLeaveValue(CSCPROC_RETURN_CONTINUE);
}
void
_UnpinLinkTarget(LPCTSTR pszLink)
{
LPTSTR pszTarget = NULL;
TraceEnter(TRACE_ADMINPIN, "_UnpinLinkTarget");
TraceAssert(pszLink);
// We only want to unpin a link target if it's a file (not a directory).
// GetLinkTarget does this check and only returns files.
GetLinkTarget(pszLink, &pszTarget, NULL);
if (pszTarget)
{
DWORD dwStatus = 0;
DWORD dwPinCount = 0;
DWORD dwHintFlags = 0;
if (CSCQueryFileStatus(pszTarget, &dwStatus, &dwPinCount, &dwHintFlags)
&& (dwHintFlags & FLAG_CSC_HINT_PIN_ADMIN))
{
// Unpin the target
CSCUnpinFile(pszTarget,
FLAG_CSC_HINT_PIN_ADMIN,
&dwStatus,
&dwPinCount,
&dwHintFlags);
if (0 == dwPinCount && 0 == dwHintFlags
&& !(dwStatus & FLAG_CSCUI_COPY_STATUS_LOCALLY_DIRTY))
{
WIN32_FIND_DATA fd = {0};
LPCTSTR pszT = PathFindFileName(pszTarget);
fd.dwFileAttributes = 0;
lstrcpyn(fd.cFileName, pszT ? pszT : pszTarget, ARRAYSIZE(fd.cFileName));
CscDelete(pszTarget);
ShellChangeNotify(pszTarget, &fd, FALSE);
}
}
LocalFree(pszTarget);
}
TraceLeaveVoid();
}
DWORD WINAPI
_UnpinAdminFolderCallback(LPCTSTR pszItem,
ENUM_REASON eReason,
DWORD dwStatus,
DWORD dwHintFlags,
DWORD dwPinCount,
LPWIN32_FIND_DATA pFind32,
LPARAM /*dwContext*/)
{
BOOL bDeleteItem = FALSE;
TraceEnter(TRACE_ADMINPIN, "_UnpinAdminFolderCallback");
if (WAIT_OBJECT_0 == WaitForSingleObject(g_heventTerminate, 0))
TraceLeaveValue(CSCPROC_RETURN_ABORT);
if (!pszItem || !*pszItem)
TraceLeaveValue(CSCPROC_RETURN_SKIP);
TraceAssert(PathIsUNC(pszItem));
if (eReason == ENUM_REASON_FILE)
{
if (PathIsShortcut(pszItem, pFind32 ? pFind32->dwFileAttributes : 0))
{
_UnpinLinkTarget(pszItem);
}
}
if ((eReason == ENUM_REASON_FILE || eReason == ENUM_REASON_FOLDER_BEGIN)
&& (dwHintFlags & FLAG_CSC_HINT_PIN_ADMIN))
{
// Unpin the item
CSCUnpinFile(pszItem,
FLAG_CSC_HINT_PIN_ADMIN,
&dwStatus,
&dwPinCount,
&dwHintFlags);
//
// If it's a file, delete it below on this pass
//
bDeleteItem = (ENUM_REASON_FILE == eReason);
Trace((TEXT("AdminUnpin %s"), pszItem));
}
else if (ENUM_REASON_FOLDER_END == eReason)
{
//
// Delete any unused folders in the post-order part of the traversal.
//
// Note that dwPinCount and dwHintFlags are always 0 in the
// post-order part of the traversal, so fetch them here.
//
bDeleteItem = CSCQueryFileStatus(pszItem, &dwStatus, &dwPinCount, &dwHintFlags);
}
//
// Delete items that are no longer pinned and have no offline changes
//
if (bDeleteItem
&& 0 == dwPinCount && 0 == dwHintFlags
&& !(dwStatus & FLAG_CSCUI_COPY_STATUS_LOCALLY_DIRTY))
{
CscDelete(pszItem);
ShellChangeNotify(pszItem, pFind32, FALSE);
}
TraceLeaveValue(CSCPROC_RETURN_CONTINUE);
}
//
// Determines if a path is a "special" file pinned by the folder
// redirection code.
//
BOOL
_IsSpecialRedirectedFile(
LPCTSTR pszPath,
HDPA hdpaFRList
)
{
TraceAssert(NULL != pszPath);
TraceAssert(!IsBadStringPtr(pszPath, MAX_PATH));
if (hdpaFRList)
{
const int cchPath = lstrlen(pszPath);
int i;
for (i = 0; i < DPA_GetPtrCount(hdpaFRList); i++)
{
LPCTSTR pszThis = (LPCTSTR)DPA_GetPtr(hdpaFRList, i);
int cchThis = lstrlen(pszThis);
if (cchPath >= cchThis)
{
//
// Path being examined is the same length or longer than
// current path from the table. Possible match.
//
if (0 == StrCmpNI(pszPath, pszThis, cchThis))
{
//
// Path being examined is either the same as,
// or a child of, the current path from the table.
//
if (TEXT('\0') == *(pszPath + cchThis))
{
//
// Path is same as this path from the table.
//
return TRUE;
}
else if (0 == lstrcmpi(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;
}
DWORD WINAPI
_ResetPinCountsCallback(LPCTSTR pszItem,
ENUM_REASON eReason,
DWORD dwStatus,
DWORD dwHintFlags,
DWORD dwPinCount,
LPWIN32_FIND_DATA /*pFind32*/,
LPARAM dwContext)
{
TraceEnter(TRACE_ADMINPIN, "_ResetPinCountsCallback");
if (WAIT_OBJECT_0 == WaitForSingleObject(g_heventTerminate, 0))
TraceLeaveValue(CSCPROC_RETURN_ABORT);
if (!pszItem || !*pszItem)
TraceLeaveValue(CSCPROC_RETURN_SKIP);
TraceAssert(PathIsUNC(pszItem));
if (eReason == ENUM_REASON_FILE || eReason == ENUM_REASON_FOLDER_BEGIN)
{
DWORD dwCurrentPinCount = dwPinCount;
DWORD dwDesiredPinCount = _IsSpecialRedirectedFile(pszItem, (HDPA)dwContext) ? 1 : 0;
while (dwCurrentPinCount-- > dwDesiredPinCount)
{
CSCUnpinFile(pszItem,
FLAG_CSC_HINT_COMMAND_ALTER_PIN_COUNT,
&dwStatus,
&dwPinCount,
&dwHintFlags);
}
}
TraceLeaveValue(CSCPROC_RETURN_CONTINUE);
}
int CALLBACK _LocalFreeCallback(LPVOID p, LPVOID)
{
// OK to pass NULL to LocalFree
LocalFree(p);
return 1;
}
DWORD WINAPI
_PinAdminFoldersThread(LPVOID)
{
TraceEnter(TRACE_ADMINPIN, "_PinAdminFoldersThread");
TraceAssert(IsCSCEnabled());
HANDLE rghSyncObj[] = { g_heventTerminate,
g_hmutexAdminPin };
UINT wmAdminPin = RegisterWindowMessage(c_szAPFMessage);
//
// Wait until we either own the "admin pin" mutex OR the
// "terminate" event is set.
//
TraceMsg("Waiting for 'admin-pin' mutex or 'terminate' event...");
DWORD dwWait = WaitForMultipleObjects(ARRAYSIZE(rghSyncObj),
rghSyncObj,
FALSE,
INFINITE);
if (1 == (dwWait - WAIT_OBJECT_0))
{
HKEY hkCSC = NULL;
FILETIME ft = {0};
RegCreateKeyEx(HKEY_CURRENT_USER,
c_szCSCKey,
0,
NULL,
REG_OPTION_NON_VOLATILE,
KEY_QUERY_VALUE | KEY_SET_VALUE,
NULL,
&hkCSC,
NULL);
if (hkCSC)
{
GetSystemTimeAsFileTime(&ft);
RegSetValueEx(hkCSC, c_szAPFStart, 0, REG_BINARY, (LPBYTE)&ft, sizeof(ft));
RegDeleteValue(hkCSC, c_szAPFEnd);
}
if (wmAdminPin)
SendNotifyMessage(HWND_BROADCAST, wmAdminPin, 0, 0);
TraceMsg("Thread now owns 'admin-pin' mutex.");
//
// We own the "admin pin" mutex. OK to perform admin pin.
//
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_IDLE);
//
// Get the Admin Folders list from the registry
//
HDPA hdpaFiles = DPA_Create(10);
HDPA hdpaUnpin = DPA_Create(4);
if (NULL != hdpaFiles && NULL != hdpaUnpin)
{
DWORD dwResult = CSCPROC_RETURN_CONTINUE;
int cFiles;
int i;
//
// NTRAID#NTBUG9-376185-2001/04/24-jeffreys
// NTRAID#NTBUG9-379736-2001/04/24-jeffreys
//
// Unless directed by policy, pin all redirected special folders.
//
if (!CConfig::GetSingleton().NoAdminPinSpecialFolders())
{
BuildFRList(hdpaFiles);
}
ReadAPFFromRegistry(hdpaFiles);
ReconcileAPF(hdpaFiles, hdpaUnpin);
//
// Iterate through the unpin list and unpin the items
//
//
cFiles = DPA_GetPtrCount(hdpaUnpin);
for (i = 0; i < cFiles; i++)
{
LPTSTR pszItem = (LPTSTR)DPA_GetPtr(hdpaUnpin, i);
DWORD dwStatus = 0;
DWORD dwPinCount = 0;
DWORD dwHintFlags = 0;
// If this fails, then it's not cached and there's nothing to do
if (CSCPROC_RETURN_CONTINUE == dwResult &&
CSCQueryFileStatus(pszItem, &dwStatus, &dwPinCount, &dwHintFlags))
{
// Unpin this item
dwResult = _UnpinAdminFolderCallback(pszItem, ENUM_REASON_FILE, dwStatus, dwHintFlags, dwPinCount, NULL, 0);
if (CSCPROC_RETURN_CONTINUE == dwResult
&& PathIsUNC(pszItem))
{
// Unpin everything under this folder (if it's a folder)
dwResult = _CSCEnumDatabase(pszItem, TRUE, _UnpinAdminFolderCallback, 0);
// Delete this item if it's no longer used (won't cause any
// harm if it's not a folder).
_UnpinAdminFolderCallback(pszItem, ENUM_REASON_FOLDER_END, 0, 0, 0, NULL, 0);
}
}
if (CSCPROC_RETURN_ABORT == dwResult)
{
// We failed to clean this one up completely, so remember it for next time
SHSetValue(HKEY_CURRENT_USER, c_szRegKeyAPFResult, pszItem, REG_DWORD, &dwResult, sizeof(dwResult));
}
}
//
// Iterate through the list and pin the items
//
cFiles = DPA_GetPtrCount(hdpaFiles);
for (i = 0; i < cFiles && CSCPROC_RETURN_CONTINUE == dwResult; i++)
{
LPTSTR pszItem = (LPTSTR)DPA_GetPtr(hdpaFiles, i);
// Pin this item
dwResult = _PinAdminFolderCallback(pszItem, ENUM_REASON_FILE, NULL, 0);
// Pin everything under this folder (if it's a folder)
if (CSCPROC_RETURN_CONTINUE == dwResult
&& PathIsUNC(pszItem))
{
dwResult = _Win32EnumFolder(pszItem, TRUE, _PinAdminFolderCallback, 0);
}
}
}
if (NULL != hdpaFiles)
{
DPA_DestroyCallback(hdpaFiles, _LocalFreeCallback, 0);
}
if (NULL != hdpaUnpin)
{
DPA_DestroyCallback(hdpaUnpin, _LocalFreeCallback, 0);
}
//
// Reduce pin counts on everything since we don't use them anymore.
// This is a one time (per user) cleanup.
//
DWORD dwCleanupDone = 0;
DWORD dwSize = sizeof(dwCleanupDone);
if (hkCSC)
{
RegQueryValueEx(hkCSC, c_szPinCountsReset, 0, NULL, (LPBYTE)&dwCleanupDone, &dwSize);
}
if (0 == dwCleanupDone)
{
HDPA hdpaFRList = DPA_Create(4);
if (hdpaFRList)
{
BuildFRList(hdpaFRList);
}
TraceMsg("Doing pin count cleanup.");
if (CSCPROC_RETURN_ABORT != _CSCEnumDatabase(NULL, TRUE, _ResetPinCountsCallback, (LPARAM)hdpaFRList)
&& hkCSC)
{
dwCleanupDone = 1;
RegSetValueEx(hkCSC, c_szPinCountsReset, 0, REG_DWORD, (LPBYTE)&dwCleanupDone, sizeof(dwCleanupDone));
}
if (hdpaFRList)
{
DPA_DestroyCallback(hdpaFRList, _LocalFreeCallback, 0);
}
}
if (hkCSC)
{
GetSystemTimeAsFileTime(&ft);
RegSetValueEx(hkCSC, c_szAPFEnd, 0, REG_BINARY, (LPBYTE)&ft, sizeof(ft));
RegCloseKey(hkCSC);
}
if (wmAdminPin)
SendNotifyMessage(HWND_BROADCAST, wmAdminPin, 1, 0);
TraceMsg("Thread releasing 'admin-pin' mutex.");
ReleaseMutex(g_hmutexAdminPin);
}
TraceMsg("_PinAdminFoldersThread exiting");
TraceLeaveValue(0);
}