|
|
/*++
Microsoft Windows Copyright (C) Microsoft Corporation, 1981 - 1998
Module Name:
redir.cxx
Abstract: This module contains the implementation for the members of the class CRedirInfo which is used to consolidate redirection information from all the policies applied to a particular GPO and then finally do the redirection for the policy with the highest precedence.
Author:
Rahul Thombre (RahulTh) 8/11/1998
Revision History:
8/11/1998 RahulTh Created this module.
--*/
#include "fdeploy.hxx"
//initialize some global variables
WCHAR * g_szRelativePathNames[] = { L"Desktop", L"My Documents", L"My Documents\\My Pictures", L"Start Menu", L"Start Menu\\Programs", L"Start Menu\\Programs\\Startup", L"Application Data" };
WCHAR * g_szDisplayNames[] = { L"Desktop", L"My Documents", L"My Pictures", L"Start Menu", L"Programs", L"Startup", L"Application Data" }; //above: use the same order and elements as in REDIRECTABLE
//global variables.
const int g_lRedirInfoSize = (int) EndRedirectable; //static members of the class
int CRedirectInfo::m_idConstructor = 0;
CRedirectInfo gPolicyResultant [g_lRedirInfoSize]; CRedirectInfo gDeletedPolicyResultant [g_lRedirInfoSize]; CRedirectInfo gAddedPolicyResultant [g_lRedirInfoSize];
//+--------------------------------------------------------------------------
//
// Member: CRedirectInfo::CRedirectInfo
//
// Synopsis: Constructor for the class
//
// Arguments:
//
// Returns:
//
// History: 8/11/1998 RahulTh created
//
// Notes:
//
//---------------------------------------------------------------------------
CRedirectInfo::CRedirectInfo () { m_rID = (REDIRECTABLE)(m_idConstructor); //assign IDs sequentially.
m_idConstructor = (m_idConstructor + 1) % ((int)EndRedirectable); m_pSid = NULL; m_szLocation = NULL; m_cbLocSize = 0; m_szGroupRedirectionData = NULL;
ResetMembers (); }
//+--------------------------------------------------------------------------
//
// Member: CRedirectInfo::~CRedirectInfo
//
// Synopsis: destructor
//
// Arguments:
//
// Returns:
//
// History: 10/6/1998 RahulTh created
//
// Notes:
//
//---------------------------------------------------------------------------
CRedirectInfo::~CRedirectInfo () { FreeAllocatedMem (); }
//+--------------------------------------------------------------------------
//
// Member: CRedirectInfo::FreeAllocatedMem
//
// Synopsis: frees memory allocated for member vars.
//
// Arguments: none.
//
// Returns: nothing.
//
// History: 12/17/2000 RahulTh created
//
// Notes:
//
//---------------------------------------------------------------------------
void CRedirectInfo::FreeAllocatedMem (void) { if (m_szLocation) { delete [] m_szLocation; m_szLocation = NULL; m_cbLocSize = 0; }
if (m_pSid) { delete [] ((BYTE *)m_pSid); m_pSid = NULL; }
if (m_szGroupRedirectionData) { delete [] m_szGroupRedirectionData; m_szGroupRedirectionData = NULL; } }
//+--------------------------------------------------------------------------
//
// Member: CRedirectInfo::ResetMembers
//
// Synopsis: resets the members of the class to their default values.
//
// Arguments: none.
//
// Returns: nothing.
//
// History: 12/17/2000 RahulTh created
//
// Notes: static members of a class, like other global variables are
// initialized only when the dll is loaded. So if this dll
// stays loaded across logons, then the fact that the globals
// have been initialized based on a previous logon can cause
// problems. Therefore, this function is used to reinitialize
// the globals.
//
//---------------------------------------------------------------------------
void CRedirectInfo::ResetMembers(void) { FreeAllocatedMem (); m_iRedirectingGroup = 0; //defaults. are changed later on based on the state of this object.
m_bFollowsParent = FALSE; m_bRedirectionAttempted = FALSE; m_StatusRedir = ERROR_SUCCESS; m_bValidGPO = FALSE; m_szGPOName[0] = L'\0';
(void) StringCbCopy(m_szFolderRelativePath, sizeof(m_szFolderRelativePath), g_szRelativePathNames [(int) m_rID]); (void) StringCbCopy(m_szDisplayName, sizeof(m_szDisplayName), g_szDisplayNames [(int) m_rID]);
m_fDataValid = FALSE; m_cbLocSize = 256; //start with a random amount
m_szLocation = new WCHAR [m_cbLocSize]; if (m_szLocation) { m_szLocation[0] = '\0'; } else { m_cbLocSize = 0; //don't worry right now if memory cannot be allocated here, we will fail later
} //set the parent and child pointers for the special parent/descendants
m_pChild = NULL; //start with defaults
m_pParent = NULL; switch (m_rID) { case MyDocs: m_pChild = this - (int) m_rID + (int) MyPics; m_dwFlags = REDIR_DONT_CARE; break; case MyPics: m_pParent = this - (int) m_rID + (int) MyDocs; m_dwFlags = REDIR_DONT_CARE; break; case StartMenu: m_pChild = this - (int) m_rID + (int) Programs; m_dwFlags = REDIR_DONT_CARE; break; case Programs: m_pParent = this - (int) m_rID + (int) StartMenu; m_pChild = this - (int) m_rID + (int) Startup; m_dwFlags = REDIR_DONT_CARE; break; case Startup: m_pParent = this - (int) m_rID + (int) Programs; m_dwFlags = REDIR_DONT_CARE; break; case Desktop: m_pChild = NULL; m_pParent = NULL; m_dwFlags = REDIR_DONT_CARE; break; case AppData: m_pChild = NULL; m_pParent = NULL; m_dwFlags = REDIR_DONT_CARE; break; }
//as a safety mechanism, load localized folder names if ghDllInstance
//has been set. note: ghDllInstance won't be set for global variables
//since their constructors before DllMain. For such variables, the
//localized names have to be called explicitly from some other function
//which is called after DllMain.
if (ghDllInstance) LoadLocalizedNames();
//
// No need to modify m_rID or m_idConstructor. The construct has already
// taken care of it and touching them might cause undesirable results if
// not done in the proper order. Besides, these don't change across multiple
// sessions.
//
}
//+--------------------------------------------------------------------------
//
// Member: CRedirectInfo::GetFolderIndex
//
// Synopsis: a static member function that, given the name of a special
// folder, returns its id which can be used to locate the
// redirection info. for that particular folder.
//
// Arguments: [in][szFldrName : the name of the folder as stored in fdeploy.ini
//
// Returns: the id of the folder. If the folder is not redirectable, the
// function returns EndRedirectable.
//
// History: 8/11/1998 RahulTh created
//
// Notes:
//
//---------------------------------------------------------------------------
REDIRECTABLE CRedirectInfo::GetFolderIndex (LPCTSTR szFldrName) { int i; for (i = 0; i < (int)EndRedirectable; i++) { if (0 == lstrcmpi (szFldrName, g_szDisplayNames[i])) break; //we have found a match
}
return (REDIRECTABLE)i; //if a match was not found above, i == EndRedirectable
}
//+--------------------------------------------------------------------------
//
// Member: CRedirectInfo::LoadLocalizedNames
//
// Synopsis: loads the localized folder display names and folder relative
// paths from the resources.
//
// Arguments: none.
//
// Returns: ERROR_SUCCESS if the names were successfully loaded.
// an error code describing the cause of the failure otherwise.
//
// History: 5/6/1999 RahulTh created
//
// Notes: we cannot do this in the constructor because ghDllInstance
// is not initialized at that time and therefore LoadString will
// fail. This is because DllMain is called after the constructors.
//
//---------------------------------------------------------------------------
DWORD CRedirectInfo::LoadLocalizedNames (void) { UINT DisplayID = 0; UINT RelpathID = 0;
switch (m_rID) { case MyDocs: DisplayID = RelpathID = IDS_MYDOCS; break; case MyPics: DisplayID = IDS_MYPICS; RelpathID = IDS_MYPICS_REL; break; case StartMenu: DisplayID = RelpathID = IDS_STARTMENU; break; case Programs: DisplayID = IDS_PROGRAMS; RelpathID = IDS_PROGRAMS_REL; break; case Startup: DisplayID = IDS_STARTUP; RelpathID = IDS_STARTUP_REL; break; case Desktop: DisplayID = RelpathID = IDS_DESKTOP; break; case AppData: DisplayID = RelpathID = IDS_APPDATA; break; default: ASSERT("Unknown folder id" && NULL); break; }
//now get the localized name of the folder and the localized relative
//path names (w.r.t. the userprofile directory)
m_szLocDisplayName[0] = m_szLocFolderRelativePath[0] = '\0'; //safety
if (!LoadString (ghDllInstance, DisplayID, m_szLocDisplayName, 80)) return GetLastError();
if (DisplayID == RelpathID) { return HRESULT_CODE(StringCbCopy(m_szLocFolderRelativePath, sizeof(m_szLocFolderRelativePath), m_szLocDisplayName)); //top level folders
} else { if (LoadString (ghDllInstance, RelpathID, m_szLocFolderRelativePath, 80)) //special descendant folders
{ return ERROR_SUCCESS; } else { return GetLastError(); } } }
//+--------------------------------------------------------------------------
//
// Member: CRedirectInfo::GatherRedirectionInfo
//
// Synopsis: this function gathers redirection info. from the ini file
//
// Arguments: [in] pFileDB : pointer to the CFileDB object that called it
// [in] dwFlags : the flags as obtained from the ini file
// [in] bRemove : whether this is a policy that is being removed
//
// Returns: STATUS_SUCCESS if successful. An error code otherwise.
//
// History: 8/11/1998 RahulTh created
//
// Notes:
//
//---------------------------------------------------------------------------
DWORD CRedirectInfo::GatherRedirectionInfo (CFileDB * pFileDB, DWORD dwFlags, BOOL bRemove) { DWORD Status = ERROR_SUCCESS; HRESULT hr = S_OK; DWORD len; BOOL bStatus; WCHAR * pwszSection = 0; BOOL bFoundGroup; WCHAR * pwszString = 0; WCHAR * pwszSid = 0; WCHAR * pwszPath = 0; PSID Sid = NULL;
if (bRemove && (!gSavedSettings[m_rID].m_bValidGPO || (0 != _wcsicmp (pFileDB->_pwszGPOUniqueName, gSavedSettings[m_rID].m_szGPOName)) ) ) { DebugMsg((DM_VERBOSE, IDS_IGNORE_DELETEDGPO, pFileDB->_pwszGPOName, pFileDB->_pwszGPOUniqueName, m_szLocDisplayName)); Status = ERROR_SUCCESS; //set default values on the members just to be on the safe side.
m_fDataValid = FALSE; m_dwFlags = REDIR_DONT_CARE; m_bValidGPO = FALSE; m_szGPOName[0] = L'\0'; goto GatherInfoEnd; }
//the data is valid. This function is only called when something relevant is found in the ini file.
m_fDataValid = TRUE;
//store the flags
m_dwFlags = dwFlags;
//store the GPO's unique name
if (bRemove) { m_bValidGPO = FALSE; //for redirection resulting from a removed GPO, we do not store the GPO name to avoid processing a removal twice
m_szGPOName[0] = L'\0'; } else { m_bValidGPO = TRUE; (void) StringCbCopy(m_szGPOName, sizeof(m_szGPOName), pFileDB->_pwszGPOUniqueName); }
//there is nothing to do if policy is not specified for this folder
if (m_dwFlags & REDIR_DONT_CARE) goto GatherInfoSuccess;
m_bRemove = bRemove;
//also, we can do nothing right now if this is a special descendant folder
//following its parent
if (m_dwFlags & REDIR_FOLLOW_PARENT) goto GatherInfoSuccess;
//a location has been specified by this policy
if ( ! pFileDB->GetRsopContext()->IsPlanningModeEnabled() ) { //we must have a list of groups to which the user belongs
//each user must at least have everyone as one of his groups
if (! pFileDB->_pGroups) goto GatherInfoErr; }
DWORD cchSectionLen;
bStatus = pFileDB->ReadIniSection (m_szDisplayName, &pwszSection, &cchSectionLen);
if (!bStatus) goto GatherInfoErr;
bFoundGroup = FALSE;
//
// For rsop, we need to know the list of security groups
// and redirected paths -- we'll copy this information for
// use by the rsop logging code
//
if (pFileDB->GetRsopContext()->IsRsopEnabled()) { //
// Note that when we do the copy, the size returned
// by ReadIniSection above does not include the null terminator,
// even though thaft character is present, so we must add that character
// to the count ourselves.
//
m_szGroupRedirectionData = new WCHAR[cchSectionLen + 1];
if (m_szGroupRedirectionData) { RtlCopyMemory(m_szGroupRedirectionData, pwszSection, ( cchSectionLen + 1 ) * sizeof(*pwszSection)); } else { pFileDB->GetRsopContext()->DisableRsop( E_OUTOFMEMORY ); } }
if ( ! pFileDB->GetRsopContext()->IsReportingModeEnabled() ) { DWORD iGroup;
iGroup = 0;
for (pwszString = pwszSection; *pwszString; pwszString += lstrlen(pwszString) + 1) { pwszSid = pwszString; pwszPath = wcschr (pwszString, L'='); if (!pwszPath) continue; //skip any invalid entries
//temporarily break up the sid and the path
*pwszPath++ = L'\0';
if (! *pwszPath) { //again an invalid path. restore the = sign and move on
pwszPath[-1] = L'='; //note: we had advanced the pointer above
continue; }
//the entry is valid
bFoundGroup = GroupInList (pwszSid, pFileDB->_pGroups);
if (bFoundGroup) { m_iRedirectingGroup = iGroup;
break; //we have found a group, so break out of the loop
} else { //restore the '=' sign, and try the next group
pwszPath[-1] = L'='; //note: we had advanced the pointer above
} iGroup++; } } else { //
// In reporting mode, we treat every folder as if it applies so
// that we can report on all settings in the GPO
//
bFoundGroup = TRUE; } if (!bFoundGroup) { //no group was found, so treat this as a don't care
m_dwFlags = REDIR_DONT_CARE; } else if ( ! pFileDB->GetRsopContext()->IsReportingModeEnabled() ) { //first store the sid
if (m_pSid) delete [] ((BYTE*) m_pSid); //m_pSid is always allocated from our heap, so never use RtlFreeSid
m_pSid = NULL;
if (!m_bRemove) Status = AllocateAndInitSidFromString (pwszSid, &Sid); else Status = AllocateAndInitSidFromString (L"S-1-1-0", &Sid); //if this is a removed policy, we set the SID to Everyone -- so that when we look at the saved settings at a later time, we shouldn't have to process the policy again (see code for NeedsProcessing)
if (ERROR_SUCCESS != Status) goto GatherInfoEnd;
if (m_pSid) delete [] ((BYTE*) m_pSid); Status = MySidCopy (&m_pSid, Sid); //we want to always allocate memory for this sid from our heap
RtlFreeSid (Sid); //cleanup. must take place before the following check
if (ERROR_SUCCESS != Status) goto GatherInfoEnd;
//we have found a group
DebugMsg ((DM_VERBOSE, IDS_GROUP_MEMBER, pwszSid, pwszPath)); SimplifyPath (pwszPath); //
// Copy the location
// Use X:\ for paths of the form X: to avoid problems during the file
// copy phase.
//
BOOL bAppendSlash = FALSE; len = lstrlen (pwszPath); if (2 == len && L':' == pwszPath[1]) { bAppendSlash = TRUE; len++; // Ensure that the extra backslash at the end is accounted for in length calculations.
} if (m_cbLocSize <= len) { //we need to reallocate memory for the location
if (m_cbLocSize) delete [] m_szLocation;
m_cbLocSize = len + 1; // Add one for the terminating null.
m_szLocation = new TCHAR [m_cbLocSize]; if (!m_szLocation) { m_cbLocSize = 0; goto GatherInfoErr; } } (void) StringCchCopy(m_szLocation, m_cbLocSize, pwszPath); if (bAppendSlash) { (void) StringCchCat(m_szLocation, m_cbLocSize, L"\\"); } }
GatherInfoSuccess: DebugMsg ((DM_VERBOSE, IDS_COLLECT_REDIRINFO, m_szLocDisplayName, m_dwFlags)); Status = STATUS_SUCCESS; goto GatherInfoEnd;
GatherInfoErr: Status = ERROR_OUTOFMEMORY; m_fDataValid = FALSE; m_dwFlags = REDIR_DONT_CARE; //so that these settings will be ignored while merging into global data
DebugMsg ((DM_VERBOSE, IDS_GATHER_FAILURE, Status, m_szLocDisplayName));
GatherInfoEnd: if (pwszSection) delete [] pwszSection; return Status; }
//+--------------------------------------------------------------------------
//
// Member: CRedirectInfo::WasRedirectionAttempted
//
// Synopsis: indicates whether redirection has already been attempted on
// this folder.
//
// Arguments: none.
//
// Returns: a bool indicating the required status
//
// History: 9/20/1999 RahulTh created
//
// Notes:
//
//---------------------------------------------------------------------------
const BOOL CRedirectInfo::WasRedirectionAttempted (void) { return m_bRedirectionAttempted; }
//+--------------------------------------------------------------------------
//
// Member: CRedirectInfo::GetFolderID
//
// Synopsis: returns the index of this folder in the array of
// redirection candidates
//
// Arguments: none.
//
// Returns: an index into the list of redirected folders
//
// History: 11/20/2001 AdamEd created
//
// Notes:
//
//---------------------------------------------------------------------------
REDIRECTABLE CRedirectInfo::GetFolderID(void) { return m_rID; }
//+--------------------------------------------------------------------------
//
// Member: CRedirectInfo::GetFlags
//
// Synopsis: returns the redirection flags for the folder represented by
// this object
//
// Arguments: none
//
// Returns: the value of the flags
//
// History: 8/12/1998 RahulTh created
//
// Notes:
//
//---------------------------------------------------------------------------
const DWORD CRedirectInfo::GetFlags (void) { return m_dwFlags; }
//+--------------------------------------------------------------------------
//
// Member: CRedirectInfo::GetRedirStatus
//
// Synopsis: retrieves the return code of the redirection operation
//
// Arguments: none
//
// Returns: the member m_StatusRedir
//
// History: 5/3/1999 RahulTh created
//
// Notes:
//
//---------------------------------------------------------------------------
const DWORD CRedirectInfo::GetRedirStatus (void) { return m_StatusRedir; }
//+--------------------------------------------------------------------------
//
// Member: CRedirectInfo::GetLocation
//
// Synopsis: returns the redirection location for the folder represented
// by this object
//
// Arguments: none
//
// Returns: a pointer to the buffer containing the path
//
// History: 8/12/1998 RahulTh created
//
// Notes:
//
//---------------------------------------------------------------------------
LPCTSTR CRedirectInfo::GetLocation (void) { return m_szLocation; }
//+--------------------------------------------------------------------------
//
// Member: CRedirectInfo::GetLocalizedName
//
// Synopsis: returns the localized name of the folder
//
// Arguments: none
//
// Returns: a pointer to the buffer containing the localized name
//
// History: 8/12/1998 RahulTh created
//
// Notes:
//
//---------------------------------------------------------------------------
LPCTSTR CRedirectInfo::GetLocalizedName (void) { return m_szLocDisplayName; }
//+--------------------------------------------------------------------------
//
// Member: CRedirectInfo::Redirect
//
// Synopsis: this function performs the actual redirection for the
// folder represented by this object.
//
// Arguments: [in] hUserToken : handle to the user token
// [in] hKeyRoot : handle to HKCU
// [in] pFileDB : pointer to the CFileDB object from which this
// function was called.
//
// Returns: a DWORD indicating the success/failure code in redirection
//
// History: 8/12/1998 RahulTh created
//
// Notes:
// It is very important to record the return code in m_StatusRedir
// before returning from this function. If we don't do that
// we might end up returning the wrong value to the policy engine
// while attempting to redirect special descendant folders which
// can affect the list of policies obtained at a subsequent logon
// session.
//
//---------------------------------------------------------------------------
DWORD CRedirectInfo::Redirect (HANDLE hUserToken, HKEY hKeyRoot, CFileDB* pFileDB) { HRESULT hr = S_OK; SHARESTATUS SourceStatus = NoCSC; SHARESTATUS DestStatus = NoCSC; DWORD PinStatus = ERROR_SUCCESS; WCHAR * wszProcessedPath = NULL;
if (m_bRedirectionAttempted) { goto Redir_CleanupAndQuit; //redirection has already been attempted
//and the success / failure code has been
//recorded in m_StatusRedir. Use the same
//error code as the return value as this
//value may not yet have been recorded in
//ProcessGroupPolicy
}
//we shall now attempt to redirect...
m_bRedirectionAttempted = TRUE;
if (m_dwFlags & REDIR_FOLLOW_PARENT) { //this is possible only if UpdateDescendant ran out of memory
//which means that we could not derive the folder's redirection
//info. No point trying to log an event since we are out of memory
//anyway. we just try to quit as gracefully as we can.
m_StatusRedir = ERROR_OUTOFMEMORY; goto Redir_KillChildAndLeave; }
WCHAR wszExpandedNewPath [TARGETPATHLIMIT]; WCHAR wszExpandedCurrentPath [TARGETPATHLIMIT]; WCHAR wszHackedName [TARGETPATHLIMIT]; WCHAR wszExpandedSavedFolderPath [TARGETPATHLIMIT]; WCHAR wszExpandedNewFolderPath [TARGETPATHLIMIT]; CSavedSettings * pLocalSettings; BOOL bCurrentPathValid; //indicates if we have been successful in obtaining the current path of the folder.
UNICODE_STRING Path; UNICODE_STRING ExpandedPath;
//set some defaults.
m_StatusRedir = ERROR_SUCCESS; bCurrentPathValid = TRUE;
//if the policy cares about the location of the folder, then we must
//expand the path
if (! (m_dwFlags & REDIR_DONT_CARE)) { //show additional status messages if the verbose status is on.
DisplayStatusMessage (IDS_REDIR_CALLBACK);
if (!m_cbLocSize) { m_StatusRedir = ERROR_OUTOFMEMORY; //we had run out of memory so no point trying to log an event
//we just quit as gracefully as we can.
goto Redir_KillChildAndLeave; }
if (m_bRemove) { m_StatusRedir = HRESULT_CODE(StringCchCopy( m_szLocation, m_cbLocSize, L"%USERPROFILE%\\")); if ( m_StatusRedir != ERROR_SUCCESS ) { goto Redir_KillChildAndLeave; } m_StatusRedir = HRESULT_CODE(StringCchCat( m_szLocation, m_cbLocSize, m_szLocFolderRelativePath)); if ( m_StatusRedir != ERROR_SUCCESS ) { goto Redir_KillChildAndLeave; } }
// Get the expanded destination path.
// First expand the homedir component, if applicable.
m_StatusRedir = ExpandHomeDirPolicyPath (m_rID, m_szLocation, m_bFollowsParent, &wszProcessedPath); if (ERROR_SUCCESS == m_StatusRedir) { Path.Length = (wcslen (wszProcessedPath) + 1) * sizeof (WCHAR); Path.MaximumLength = sizeof (wszProcessedPath); Path.Buffer = wszProcessedPath;
ExpandedPath.Length = 0; ExpandedPath.MaximumLength = sizeof (wszExpandedNewFolderPath); ExpandedPath.Buffer = wszExpandedNewFolderPath;
m_StatusRedir = RtlExpandEnvironmentStrings_U ( pFileDB->_pEnvBlock, &Path, &ExpandedPath, NULL );
if (STATUS_BUFFER_TOO_SMALL == m_StatusRedir) { gpEvents->Report ( EVENT_FDEPLOY_DESTPATH_TOO_LONG, 3, m_szLocDisplayName, m_szLocation, NumberToString ( TARGETPATHLIMIT ) );
goto Redir_KillChildAndLeave; } }
if (ERROR_SUCCESS != m_StatusRedir) { DebugMsg ((DM_WARNING, IDS_REDIRECT_EXP_FAIL, m_szLocation, m_StatusRedir)); gpEvents->Report ( EVENT_FDEPLOY_FOLDER_EXPAND_FAIL, 2, m_szLocDisplayName, StatusToString ( m_StatusRedir ) );
goto Redir_KillChildAndLeave; } }
pLocalSettings = & (gSavedSettings[(int) m_rID]);
if (pLocalSettings->m_dwFlags & REDIR_DONT_CARE) { if (m_dwFlags & REDIR_DONT_CARE) { pLocalSettings->Save (pLocalSettings->m_szCurrentPath, m_dwFlags, NULL, NULL); if (MyDocs == m_rID) { m_StatusRedir = RestrictMyDocsRedirection (hUserToken, hKeyRoot, FALSE);
if ( ERROR_SUCCESS != m_StatusRedir ) { goto Redir_KillChildAndLeave; } }
goto Redir_CleanupAndQuit; }
DebugMsg ((DM_VERBOSE, IDS_REDIRECT, m_szLocDisplayName, m_szLocation));
//the policy cares about the location of this doc.
//must get the correct syntax for GetLocalFilePath
m_StatusRedir = HRESULT_CODE(StringCchCopy(wszHackedName, TARGETPATHLIMIT, m_szFolderRelativePath)); if ( m_StatusRedir != ERROR_SUCCESS ) { goto Redir_KillChildAndLeave; } m_StatusRedir = HRESULT_CODE(StringCchCat(wszHackedName, TARGETPATHLIMIT, L"\\")); if ( m_StatusRedir != ERROR_SUCCESS ) { goto Redir_KillChildAndLeave; } m_StatusRedir = pFileDB->GetLocalFilePath (wszHackedName, wszExpandedCurrentPath);
//
// Expand the homedir if necessary. Note: GetLocalFilePath will not
// do it because it calls SHGetFolderPath and HOMESHARE and HOMEPATH
// are not defined at that point.
//
if (ERROR_SUCCESS == m_StatusRedir) { m_StatusRedir = ExpandHomeDir (m_rID, wszExpandedCurrentPath, TRUE, &wszProcessedPath, pLocalSettings->m_bHomedirChanged ? pLocalSettings->m_szLastHomedir : NULL ); if (ERROR_SUCCESS != m_StatusRedir || ! wszProcessedPath || TARGETPATHLIMIT <= lstrlen (wszProcessedPath)) { m_StatusRedir = (ERROR_SUCCESS == m_StatusRedir) ? ERROR_BUFFER_OVERFLOW : m_StatusRedir; } else { (void) StringCchCopy(wszExpandedCurrentPath, TARGETPATHLIMIT, wszProcessedPath); } }
//GetLocalFilePath might fail if SHGetFolderPath fails. SHGetFolderPath
//fails if the registry keys for the folder point to an invalid path and
//there is nothing in the CSC cache for it either. So in this particular
//case, we ignore the failure and treat it as a NO_MOVE. In NO_MOVE, we
//do not require the current path of the folder.
if ((ERROR_SUCCESS != m_StatusRedir) && ERROR_INVALID_NAME != m_StatusRedir) //the only possible error from GetLocalFilePath not generated by SHGetFolderPath
{ m_dwFlags &= ~REDIR_MOVE_CONTENTS; m_StatusRedir = ERROR_SUCCESS; bCurrentPathValid = FALSE; (void) StringCchCopy(wszExpandedCurrentPath, TARGETPATHLIMIT, L"???"); //if bCurrentPathValid is FALSE, this string will only be used in logs
}
if (m_StatusRedir != ERROR_SUCCESS) { DebugMsg ((DM_WARNING, IDS_REDIRECT_NO_LOCAL, m_szLocDisplayName, m_StatusRedir)); gpEvents->Report ( EVENT_FDEPLOY_FOLDER_EXPAND_FAIL, 2, m_szLocDisplayName, StatusToString ( m_StatusRedir ) ); goto Redir_KillChildAndLeave; }
//make sure that it is not already redirected
if (bCurrentPathValid && (0 == _wcsicmp (wszExpandedCurrentPath, wszExpandedNewFolderPath))) { pLocalSettings->Save (m_szLocation, m_dwFlags, m_pSid, m_bValidGPO ? m_szGPOName : NULL); if (MyDocs == m_rID) { m_StatusRedir = RestrictMyDocsRedirection (hUserToken, hKeyRoot, (m_bRemove || (m_dwFlags & REDIR_DONT_CARE))?FALSE:TRUE);
if ( ERROR_SUCCESS != m_StatusRedir ) { goto Redir_KillChildAndLeave; } }
DebugMsg ((DM_VERBOSE, IDS_REDIRECT_INSYNC, m_szLocDisplayName, wszExpandedNewFolderPath)); //however, it is possible that the folders have not been pinned.
//e.g. a roaming user who has already been redirected on one machine
//and now logs on to another machine for the first time.
DestStatus = GetCSCStatus (wszExpandedNewFolderPath); if (ShareOnline == DestStatus) {
PinStatus = PinIfNecessary (wszExpandedNewFolderPath, DestStatus); if ( ERROR_SUCCESS != PinStatus ) DebugMsg((DM_VERBOSE, IDS_CSCPIN_FAIL, m_szLocDisplayName, PinStatus));
CacheDesktopIni (wszExpandedNewFolderPath, DestStatus, PinFile); } m_StatusRedir = ERROR_SUCCESS; goto Redir_CleanupAndQuit; }
if (g_bCSCEnabled) { SourceStatus = bCurrentPathValid ? GetCSCStatus (wszExpandedCurrentPath) : PathLocal; DestStatus = GetCSCStatus (wszExpandedNewFolderPath); if (ShareOffline == DestStatus || ((m_dwFlags & REDIR_MOVE_CONTENTS) && ShareOffline == SourceStatus)) { m_StatusRedir = ERROR_CSCSHARE_OFFLINE; gpEvents->Report ( EVENT_FDEPLOY_FOLDER_OFFLINE, 3, m_szLocDisplayName, wszExpandedCurrentPath, wszExpandedNewFolderPath ); goto Redir_KillChildAndLeave; } }
DebugMsg (( DM_VERBOSE, IDS_REDIRECT_PREVPATH, pLocalSettings->m_szCurrentPath, wszExpandedCurrentPath)); DebugMsg (( DM_VERBOSE, IDS_REDIRECT_NEWPATH, m_szLocation, wszExpandedNewFolderPath));
m_StatusRedir = PerformRedirection ( pFileDB, bCurrentPathValid, wszExpandedCurrentPath, wszExpandedNewFolderPath, SourceStatus, DestStatus, hUserToken );
if (ERROR_SUCCESS == m_StatusRedir) { if (m_bRemove) { if (MyDocs == m_rID) { m_StatusRedir = RestrictMyDocsRedirection (hUserToken, hKeyRoot, FALSE);
if ( ERROR_SUCCESS != m_StatusRedir ) { goto Redir_KillChildAndLeave; } }
pLocalSettings->Save (m_szLocation, REDIR_DONT_CARE, NULL, NULL); } else { if (MyDocs == m_rID) { m_StatusRedir = RestrictMyDocsRedirection (hUserToken, hKeyRoot, (m_dwFlags & REDIR_DONT_CARE) ? FALSE : TRUE);
if ( ERROR_SUCCESS != m_StatusRedir ) { goto Redir_KillChildAndLeave; } }
pLocalSettings->Save (m_szLocation, m_dwFlags, m_pSid, m_bValidGPO ? m_szGPOName : 0); } } else if (m_pChild && m_pChild->m_bFollowsParent) //if this is a parent whose redirection was unsuccessful, do not redirect the child if it is supposed to follow
{ PreventDescendantRedirection (m_StatusRedir); }
goto Redir_CleanupAndQuit; }
//if we are here, it means that the saved settings don't have the
//REDIR_DONT_CARE flag set
if (m_dwFlags & REDIR_DONT_CARE) { //the original settings cared about the location of the folder,
//but now no one cares. So remove any redirection restrictions.
if (MyDocs == m_rID) { m_StatusRedir = RestrictMyDocsRedirection (hUserToken, hKeyRoot, FALSE);
if ( ERROR_SUCCESS != m_StatusRedir ) { goto Redir_KillChildAndLeave; } }
pLocalSettings->Save (pLocalSettings->m_szCurrentPath, m_dwFlags, NULL, NULL); m_StatusRedir = ERROR_SUCCESS; goto Redir_CleanupAndQuit; }
DebugMsg((DM_VERBOSE, IDS_REDIRECT, m_szLocDisplayName, m_szLocation));
// This policy cares about where this folder goes.
//
// So first expand the homedir part if applicable
// use the last value of homedir if the value of homedir has changed.
//
m_StatusRedir = ExpandHomeDir(m_rID, pLocalSettings->m_szLastRedirectedPath, TRUE, &wszProcessedPath, pLocalSettings->m_bHomedirChanged ? pLocalSettings->m_szLastHomedir : NULL);
if (ERROR_SUCCESS == m_StatusRedir) { // Now first expand the saved path, we will use that for redirection
Path.Length = (wcslen (wszProcessedPath) + 1) * sizeof (WCHAR); Path.MaximumLength = Path.Length; Path.Buffer = wszProcessedPath;
ExpandedPath.Length = 0; ExpandedPath.MaximumLength = sizeof (wszExpandedSavedFolderPath); ExpandedPath.Buffer = wszExpandedSavedFolderPath;
m_StatusRedir = RtlExpandEnvironmentStrings_U ( pFileDB->_pEnvBlock, &Path, &ExpandedPath, NULL );
if (STATUS_BUFFER_TOO_SMALL == m_StatusRedir) { gpEvents->Report ( EVENT_FDEPLOY_DESTPATH_TOO_LONG, 3, m_szLocDisplayName, pLocalSettings->m_szLastRedirectedPath, NumberToString ( TARGETPATHLIMIT ) );
goto Redir_KillChildAndLeave; } }
if (ERROR_SUCCESS != m_StatusRedir) { DebugMsg ((DM_WARNING, IDS_REDIRECT_EXP_FAIL, pLocalSettings->m_szLastRedirectedPath, m_StatusRedir)); gpEvents->Report ( EVENT_FDEPLOY_FOLDER_EXPAND_FAIL, 2, m_szLocDisplayName, StatusToString ( m_StatusRedir ) );
goto Redir_KillChildAndLeave; }
//make sure that it is not already redirected to the location.
if (0 == _wcsicmp (wszExpandedSavedFolderPath, wszExpandedNewFolderPath)) { //the cached path and the path specified by the policy is the same
//but someone may have messed with the registry, if that is the case
//we use the path in the registry as the base path
//must get the correct syntax for GetLocalFilePath
m_StatusRedir = HRESULT_CODE(StringCchCopy(wszHackedName, TARGETPATHLIMIT, m_szFolderRelativePath)); if ( m_StatusRedir != ERROR_SUCCESS ) { goto Redir_KillChildAndLeave; } m_StatusRedir = HRESULT_CODE(StringCchCat(wszHackedName, TARGETPATHLIMIT, L"\\")); if ( m_StatusRedir != ERROR_SUCCESS ) { goto Redir_KillChildAndLeave; } m_StatusRedir = pFileDB->GetLocalFilePath (wszHackedName, wszExpandedCurrentPath);
//
// Expand the homedir if necessary. Note: GetLocalFilePath will not
// do it because it calls SHGetFolderPath and HOMESHARE and HOMEPATH
// are not defined at that point.
//
if (ERROR_SUCCESS == m_StatusRedir) { m_StatusRedir = ExpandHomeDir (m_rID, wszExpandedCurrentPath, TRUE, &wszProcessedPath, pLocalSettings->m_bHomedirChanged ? pLocalSettings->m_szLastHomedir : NULL ); if (ERROR_SUCCESS != m_StatusRedir || ! wszProcessedPath || TARGETPATHLIMIT <= lstrlen (wszProcessedPath)) { m_StatusRedir = (ERROR_SUCCESS == m_StatusRedir) ? ERROR_BUFFER_OVERFLOW : m_StatusRedir; } else { (void) StringCchCopy(wszExpandedCurrentPath, TARGETPATHLIMIT, wszProcessedPath); } }
//GetLocalFilePath may fail if SHGetFolderPath fails. This
//would mean that the registry is messed up anyway. Also, move contents
//no longer makes sense, since the path to which the reg. points to
//is invalid or worse, the registry key is missing. also, it would
//imply that there is nothing in the CSC cache
if ((ERROR_SUCCESS != m_StatusRedir) && ERROR_INVALID_NAME != m_StatusRedir) //the only possible error from GetLocalFilePath not generated by SHGetFolderPath
{ m_StatusRedir = ERROR_SUCCESS; bCurrentPathValid = FALSE; m_dwFlags &= ~REDIR_MOVE_CONTENTS; }
if (m_StatusRedir != ERROR_SUCCESS) { DebugMsg ((DM_WARNING, IDS_REDIRECT_NO_LOCAL, m_szLocDisplayName, m_StatusRedir)); gpEvents->Report ( EVENT_FDEPLOY_FOLDER_EXPAND_FAIL, 2, m_szLocDisplayName, StatusToString ( m_StatusRedir ) ); goto Redir_KillChildAndLeave; } //we have found out the path stored in the registry, so compare it with
//the saved path
if (bCurrentPathValid && (0 == _wcsicmp (wszExpandedSavedFolderPath, wszExpandedCurrentPath))) { //all the paths are identical, so we are fine. save and quit
pLocalSettings->Save (m_szLocation, m_dwFlags, m_pSid, m_bValidGPO ? m_szGPOName : 0); if (MyDocs == m_rID) { m_StatusRedir = RestrictMyDocsRedirection (hUserToken, hKeyRoot, (m_bRemove || (m_dwFlags & REDIR_DONT_CARE))?FALSE:TRUE);
if ( ERROR_SUCCESS != m_StatusRedir ) { goto Redir_KillChildAndLeave; } }
DebugMsg ((DM_VERBOSE, IDS_REDIRECT_INSYNC, m_szLocDisplayName, wszExpandedNewFolderPath)); //however, it is possible that the folders have not been pinned.
//e.g. a roaming user who has already been redirected on one machine
//and now logs on to another machine for the first time.
DestStatus = GetCSCStatus (wszExpandedNewFolderPath); if (ShareOnline == DestStatus) {
PinStatus = PinIfNecessary (wszExpandedNewFolderPath, DestStatus); if ( ERROR_SUCCESS != PinStatus ) DebugMsg((DM_VERBOSE, IDS_CSCPIN_FAIL, m_szLocDisplayName, PinStatus));
CacheDesktopIni (wszExpandedNewFolderPath, DestStatus, PinFile); } m_StatusRedir = ERROR_SUCCESS; goto Redir_CleanupAndQuit; } else //somebody has been messing with the registry
{ //use the path in the registry as the source path
//and perform redirection again
if (bCurrentPathValid) (void) StringCchCopy(wszExpandedSavedFolderPath, TARGETPATHLIMIT, wszExpandedCurrentPath); else (void) StringCchCopy(wszExpandedSavedFolderPath, TARGETPATHLIMIT, L"???"); //to be on the safe side, since we don't want to use the saved path as the source for redirection
//if bCurrentPathValid is FALSE, this string will only be used in debug messages and error logs
} }
if (g_bCSCEnabled) { SourceStatus = bCurrentPathValid ? GetCSCStatus (wszExpandedSavedFolderPath) : PathLocal; DestStatus = GetCSCStatus (wszExpandedNewFolderPath); if (ShareOffline == DestStatus || ((m_dwFlags & REDIR_MOVE_CONTENTS) && ShareOffline == SourceStatus)) { m_StatusRedir = ERROR_CSCSHARE_OFFLINE; gpEvents->Report ( EVENT_FDEPLOY_FOLDER_OFFLINE, 3, m_szLocDisplayName, wszExpandedSavedFolderPath, wszExpandedNewFolderPath ); goto Redir_KillChildAndLeave; } }
DebugMsg (( DM_VERBOSE, IDS_REDIRECT_PREVPATH, pLocalSettings->m_szLastRedirectedPath, wszExpandedSavedFolderPath)); DebugMsg (( DM_VERBOSE, IDS_REDIRECT_NEWPATH, m_szLocation, wszExpandedNewFolderPath));
m_StatusRedir = PerformRedirection ( pFileDB, bCurrentPathValid, wszExpandedSavedFolderPath, wszExpandedNewFolderPath, SourceStatus, DestStatus, hUserToken );
if (ERROR_SUCCESS == m_StatusRedir) { if (m_bRemove) { if (MyDocs == m_rID) { m_StatusRedir = RestrictMyDocsRedirection (hUserToken, hKeyRoot, FALSE);
if ( ERROR_SUCCESS != m_StatusRedir ) { goto Redir_KillChildAndLeave; } }
pLocalSettings->Save (m_szLocation, REDIR_DONT_CARE, NULL, NULL); } else { if (MyDocs == m_rID) { m_StatusRedir = RestrictMyDocsRedirection (hUserToken, hKeyRoot, (m_dwFlags & REDIR_DONT_CARE)?FALSE:TRUE);
if ( ERROR_SUCCESS != m_StatusRedir ) { goto Redir_KillChildAndLeave; } }
pLocalSettings->Save (m_szLocation, m_dwFlags, m_pSid, m_bValidGPO ? m_szGPOName : 0); } } else if (m_pChild && m_pChild->m_bFollowsParent) //if redirection was unsuccessful and this folder has children, prevent the redirection of the children if they are supposed to follow
{ PreventDescendantRedirection (m_StatusRedir); }
goto Redir_CleanupAndQuit;
//the following code is executed whenever some fatal error occurs
//and we want to make sure that if this is a dir with a special
//descendant and the descendant is supposed to follow the parent,
//then we don't want to attempt redirection for the child.
Redir_KillChildAndLeave: if (ERROR_SUCCESS != m_StatusRedir && (m_pChild && m_pChild->m_bFollowsParent)) PreventDescendantRedirection (m_StatusRedir);
Redir_CleanupAndQuit: if (wszProcessedPath) delete [] wszProcessedPath;
if ( ERROR_SUCCESS != m_StatusRedir ) { HRESULT hrRsop;
hrRsop = pFileDB->AddPreservedPolicy( (WCHAR*) m_szDisplayName );
if ( FAILED( hrRsop ) ) { pFileDB->GetRsopContext()->DisableRsop( hrRsop ); } }
return m_StatusRedir; } //+--------------------------------------------------------------------------
//
// Member: CRedirectInfo::ShouldSaveExpandedPath
//
// Synopsis: Determines whether we need to store the expanded path in the
// registry or the unexpanded paths. See notes for additional
// details.
//
// Arguments: none.
//
// Returns: TRUE : if the expanded path should be stored.
// FALSE : otherwise.
//
// History: 5/1/2001 RahulTh created
//
// Notes:
// If the destination path is a homedir path, store the expanded path
// in the registry so that if msgina is unable to set the homedir
// variables and uses the local defaults instead, the shell doesn't
// end up thinking that MyDocs is local.
//
// If the destination path is a different kind of path, then store the
// expanded path if it has the %username% variable in it. Because if we
// don't then when a user's UPN changes and the user logs on with the new
// username, then the shell will expand the path using the new username.
// Now, normally this won't be a problem because we would have already
// done the rename operation. However, if for some reason we are
// not successful in that rename operation (say because we are
// invoked in limited foreground or because the server is offline)
// then the rename will not succeed. In this case, the shell will
// end up creating a new empty folder in that location whenever the
// user tries to access it. So the next time the user logs on,
// the new folder is already present and the rename fails with
// ERROR_ALREADY_EXISTS and we just end up pointing to the new
// location. The files stay in the old location. Therefore, we must
// not use the unexpanded path in SHSetFolderPath. However, we must
// store the unexpanded path in our local cache in order to
// successfully detect UPN changes.
//
// We do not want to store the expanded path unconditionally because
// in situations where the user is going back to a local location,
// especially the local userprofile location, we do not want to store
// the expanded path because it is not device independent and will cause
// problems for roaming users (e.g. a certain drive may not be available
// on all the machines so we should keep the path as %userprofile%\...
// rather than something like E:\Documents & Settings\...)
//
//---------------------------------------------------------------------------
BOOL CRedirectInfo::ShouldSaveExpandedPath(void) { if (! m_szLocation || L'\0' == m_szLocation[0]) return FALSE; // Detect the homedir case
if (IsHomedirPolicyPath(m_rID, m_szLocation, TRUE)) return TRUE; //
// Check if the path contains the username variable.
// If it does then we should store the expanded paths. However regardless
// of everything else, if the path begins with %userprofile%, we should
// never store the expanded path because we will never be guaranteed a
// device independent path in that case and will run into all sorts of
// problems.
//
_wcslwr (m_szLocation); // Compare the beginning of the two strings (the -1 is required to prevent comparing the terminating NULL)
if (0 == wcsncmp (m_szLocation, L"%userprofile%", sizeof(L"%userprofile%")/sizeof(WCHAR) - 1)) return FALSE; if (wcsstr (m_szLocation, L"%username%")) return TRUE; //
// If we are here, we did not meet any of the conditions required for
// storing the expanded paths. So we should just store the unexpanded paths
//
return FALSE; }
//+--------------------------------------------------------------------------
//
// Member: CRedirectInfo::PerformRedirection
//
// Synopsis: performs the nitty gritty of redirection including copying
// files, updating the registry, logging events etc.
//
// Arguments: [in] pFileDB : the CFileDB structure that is used throughout
// [in] bSourceValid : if pwszSource contains a valid path
// [in] pwszSource : source path
// [in] pwszDest : Destination path
// [in] StatusFrom : CSC status of the source share
// [in] StatusTo : CSC status of the destination share
//
// Returns: ERROR_SUCCESS if everything was successful. An error code
// otherwise.
//
// History: 11/21/1998 RahulTh created
// 12/13/2000 RahulTh Special cased homedir redirection to
// prevent security checks and store expanded
// paths in the registry (see comments within the function)
//
// Notes: this functions expects both shares to be online when it is
// invoked.
//
//---------------------------------------------------------------------------
DWORD CRedirectInfo::PerformRedirection (CFileDB * pFileDB, BOOL bSourceValid, WCHAR * pwszSource, WCHAR * pwszDest, SHARESTATUS StatusFrom, SHARESTATUS StatusTo, HANDLE hUserToken ) { BOOL bCheckOwner; BOOL bMoveContents; BOOL bIsHomeDirRedirection = FALSE; // Tracks if this is a homedir redirection policy
DWORD Status; int iResultCompare; WCHAR * pwszSkipSubdir; int csidl; BOOL bStatus; DWORD PinStatus; HRESULT hResult = S_OK; CCopyFailData CopyFailure; //
// We need to track if this is a homedir redirection policy because
// of 2 reasons:
// 1) In homedir redirection, security checks are skipped.
// 2) In homedir redirection, the expanded path is stored in the registry. This is
// to prevent problems that might occur if the homedir variables cannot be
// set by msgina and end up pointing to the local userprofile. In this case,
// we do not want the shell to start pointing MyDocs to the local location.
//
bIsHomeDirRedirection = IsHomedirPolicyPath(m_rID, m_szLocation, TRUE); csidl = pFileDB->RegValueCSIDLFromFolderName (m_szFolderRelativePath);
// Skip security check for homedir redirection.
bCheckOwner = ((m_dwFlags & REDIR_SETACLS) && (!bIsHomeDirRedirection)) ? TRUE : FALSE; bMoveContents = m_dwFlags & REDIR_MOVE_CONTENTS ? TRUE : FALSE;
if (bSourceValid) { Status = ComparePaths (pwszSource, pwszDest, &iResultCompare);
if (ERROR_SUCCESS != Status) { //this is very unlikely
gpEvents->Report ( EVENT_FDEPLOY_REDIRECT_FAIL, 4, m_szLocDisplayName, StatusToString (Status), pwszSource, pwszDest ); return Status; }
if (0 == iResultCompare) { DebugMsg ((DM_VERBOSE, IDS_REDIRECT_INSYNC, m_szLocDisplayName, pwszDest)); //however, it is possible that the folders have not been pinned.
//e.g. a roaming user who has already been redirected on one machine
//and now logs on to another machine for the first time.
if (ShareOnline == StatusTo) {
PinStatus = PinIfNecessary (pwszDest, StatusTo); if ( ERROR_SUCCESS != PinStatus ) DebugMsg((DM_VERBOSE, IDS_CSCPIN_FAIL, m_szLocDisplayName, PinStatus));
CacheDesktopIni (pwszDest, StatusTo, PinFile); } return ERROR_SUCCESS; }
//it is okay to redirect to a path that is a descendant of the current
//path if we are not moving contents
if (bMoveContents && (-1 == iResultCompare)) { gpEvents->Report ( EVENT_FDEPLOY_REDIRECT_RECURSE, 4, m_szLocDisplayName, m_szLocation, pwszSource, pwszDest );
return ERROR_REQUEST_ABORTED; } }
Status = pFileDB->CreateRedirectedFolderPath (pwszSource, pwszDest, bSourceValid, bCheckOwner, bMoveContents);
if (ERROR_SUCCESS != Status) { gpEvents->Report ( EVENT_FDEPLOY_FOLDER_CREATE_FAIL, 4, m_szLocDisplayName, StatusToString (Status), m_szLocation, pwszDest ); return Status; }
//now we know that both the source and destinations paths exist
//so make sure that they are not the same path in different formats
//this is an expensive function as it involves creation and deletion of
//multiple files over the network. so we invoke it only if absolutely
//necessary
if (bSourceValid && bMoveContents) { Status = CheckIdenticalSpecial (pwszSource, pwszDest, &iResultCompare);
if (ERROR_SUCCESS != Status) { //this is very unlikely...
gpEvents->Report ( EVENT_FDEPLOY_REDIRECT_FAIL, 4, m_szLocDisplayName, StatusToString (Status), pwszSource, pwszDest ); return Status; }
if (0 == iResultCompare) { DebugMsg ((DM_VERBOSE, IDS_REDIRECT_INSYNC, m_szLocDisplayName, pwszDest));
//
// The paths are the same but in different formats (or perhaps
// through different shares. So at least update the registry to
// point to the new path.
//
hResult = SHSetFolderPath(csidl | CSIDL_FLAG_DONT_UNEXPAND, hUserToken, 0, ShouldSaveExpandedPath() ? pwszDest : m_szLocation); Status = GetWin32ErrFromHResult (hResult);
//
// This basically should never fail. But do we want to try to delete the
// copied files if it does? Or the wacked CSC database?
//
if ( Status != ERROR_SUCCESS ) { gpEvents->Report( EVENT_FDEPLOY_FOLDER_REGSET_FAIL, 2, m_szLocDisplayName, StatusToString( Status ) );
return Status; } else { //we were successul.
//first, rename the local CSC cache.
if (m_pChild) pwszSkipSubdir = m_pChild->m_szLocDisplayName; else pwszSkipSubdir = NULL; MoveDirInCSC (pwszSource, pwszDest, pwszSkipSubdir, StatusFrom, StatusTo, TRUE, TRUE); if (g_bCSCEnabled && ShareOnline == StatusFrom) { DeleteCSCFileTree (pwszSource, pwszSkipSubdir, TRUE); DeleteCSCShareIfEmpty (pwszSource, StatusFrom); }
//Also, it is possible that the folders have not been pinned.
//e.g. a roaming user who has already been redirected on one
//machine and now logs on to another machine for the first time.
if (ShareOnline == StatusTo) {
PinStatus = PinIfNecessary (pwszDest, StatusTo); if ( ERROR_SUCCESS != PinStatus ) DebugMsg((DM_VERBOSE, IDS_CSCPIN_FAIL, m_szLocDisplayName, PinStatus));
CacheDesktopIni (pwszDest, StatusTo, PinFile); }
//report this event and return. there is
//nothing more to be done here.
gpEvents->Report( EVENT_FDEPLOY_FOLDER_REDIRECT, 1, m_szLocDisplayName ); return ERROR_SUCCESS; } } }
if (bSourceValid && bMoveContents) { DebugMsg ((DM_VERBOSE, IDS_REDIRECT_COPYON, m_szLocDisplayName));
//
// Exclude any special descendants when
// doing file copies and deletes. Similarly for Programs and
// Startup
//
if (m_pChild) pwszSkipSubdir = m_pChild->m_szLocDisplayName; else pwszSkipSubdir = NULL;
Status = pFileDB->CopyFileTree( pwszSource, pwszDest, pwszSkipSubdir, StatusFrom, StatusTo, TRUE, &CopyFailure );
//it is necessary to do the following for 2 reasons:
//(a) the user may have dirty files in the cache. these don't get
// moved above.
//(b) the files may have already been moved on the network by the
// the extension when the user logged on from another machine
// therefore, the cache on the second machine never gets updated
//
//we only try our best to move the files here. Errors are ignored.
if (ERROR_SUCCESS == Status) { MoveDirInCSC (pwszSource, pwszDest, pwszSkipSubdir, StatusFrom, StatusTo, FALSE, TRUE); } else { //the copy failed. We might have failed halfway and left the cache
//in an inconsistent state, so rollback all the cached entries
MoveDirInCSC (pwszDest, pwszSource, pwszSkipSubdir, StatusTo, StatusFrom, TRUE, TRUE); }
if ( ERROR_SUCCESS != Status ) { if (! CopyFailure.IsCopyFailure()) { gpEvents->Report( EVENT_FDEPLOY_FOLDER_MOVE_FAIL, 5, m_szLocDisplayName, StatusToString( Status ), m_szLocation, pwszSource, pwszDest ); } else { gpEvents->Report( EVENT_FDEPLOY_FOLDER_COPY_FAIL, 7, m_szLocDisplayName, StatusToString( Status ), m_szLocation, pwszSource, pwszDest, CopyFailure.GetSourceName(), CopyFailure.GetDestName() ); }
return Status; } } else { DebugMsg((DM_VERBOSE, IDS_REDIRECT_COPYOFF, m_szLocDisplayName)); }
//
// Look at the comments for ShouldSaveExpandedPath() for details on why we
// sometimes need to store expanded paths.
//
hResult = SHSetFolderPath(csidl | CSIDL_FLAG_DONT_UNEXPAND, hUserToken, 0, ShouldSaveExpandedPath() ? pwszDest : m_szLocation); Status = GetWin32ErrFromHResult (hResult);
if ( Status != ERROR_SUCCESS ) { gpEvents->Report( EVENT_FDEPLOY_FOLDER_REGSET_FAIL, 2, m_szLocDisplayName, StatusToString( Status ) );
return Status; }
if (!m_bRemove) { //
// hack to work around a shell problem.
//
// Pin the folder so that the shell never gets an error when
// trying to resolve this path. This will prevent it from
// reverting to a temporary local path.
//
// For now just call pin/unpin APIs for any unc style path. Not
// really much value in checking first to see if the share is
// cacheable.
//
if ( bSourceValid && (L'\\' == pwszSource[0]) && (L'\\' == pwszSource[1]) ) { CSCUnpinFile( pwszSource, FLAG_CSC_HINT_COMMAND_ALTER_PIN_COUNT, NULL, NULL, NULL ); CacheDesktopIni (pwszSource, StatusFrom, UnpinFile); }
if ( (L'\\' == pwszDest[0]) && (L'\\' == pwszDest[1]) ) { PinStatus = PinIfNecessary (pwszDest, StatusTo);
if ( ERROR_SUCCESS != PinStatus ) DebugMsg((DM_VERBOSE, IDS_CSCPIN_FAIL, m_szLocDisplayName, PinStatus));
CacheDesktopIni (pwszDest, StatusTo, PinFile); } }
//the contents were moved. now redirect any special children
//this ensures that deletions (if any) in the child folders,
//are performed first. thus deletion of this folder won't fail
//due to existence of its children within it.
//should not check for m_pChild->m_bFollowsParent here as
//the child may currently lie under this folder and if we do
//not perform the redirection of the child here, we might have
//problems deleting this folder even when we should not have any
//problems
if (m_pChild) { Status = m_pChild->Redirect (pFileDB->_hUserToken, pFileDB->_hkRoot, pFileDB);
}
//note : contents of the source should not be deleted if this is a
// policy removal
if ( bSourceValid && bMoveContents) { Status = ERROR_SUCCESS;
//leave the contents on the server if this is a policy removal.
//also leave the contents on the server when moving from
//a network to a local location, so that subsequent redirections
//from other workstations will get all the contents back to local.
if (!m_bRemove && (!IsPathLocal(pwszDest) || IsPathLocal(pwszSource))) { //
// This could fail because of ACLing. We ignore any failures here.
//
DebugMsg((DM_VERBOSE, IDS_REDIRECT_DELETE, m_szLocDisplayName, pwszSource));
Status = pFileDB->DeleteFileTree( pwszSource, pwszSkipSubdir ); if ( ERROR_SUCCESS == Status ) { //DeleteFileTree does not remove the top level node passed to it.
// Delete the top level node only if it is not the user's home
// directory
const WCHAR * pwszHomeDir = NULL; DWORD dwHomeDirStatus = ERROR_SUCCESS; pwszHomeDir = gUserInfo.GetHomeDir(dwHomeDirStatus); if (NULL == pwszHomeDir || 0 != lstrcmpi (pwszHomeDir, pwszSource)) { //clear the attributes before deleting.
SetFileAttributes (pwszSource, FILE_ATTRIBUTE_NORMAL); if ( ! RemoveDirectory( pwszSource ) ) { Status = GetLastError(); DebugMsg((DM_VERBOSE, IDS_DIRDEL_FAIL, pwszSource, Status)); } } }
if ( Status != ERROR_SUCCESS ) DebugMsg((DM_WARNING, IDS_REDIRECT_DEL_FAIL, pwszSource, m_szLocDisplayName, m_szLocation, Status)); }
//but we always clean up the CSC cache irrespective of whether it is a
//policy removal or not because folder redirection should be as transparent
//to the user as possible and it would be annoying for the user to get
//CSC notifications for shares that are no longer used as redirection
//targets.
if (g_bCSCEnabled && ShareOnline == StatusFrom) { DeleteCSCFileTree (pwszSource, pwszSkipSubdir, TRUE); DeleteCSCShareIfEmpty (pwszSource, StatusFrom); }
}
gpEvents->Report( EVENT_FDEPLOY_FOLDER_REDIRECT, 1, m_szLocDisplayName );
return ERROR_SUCCESS; }
//+--------------------------------------------------------------------------
//
// Member: PreventRedirection
//
// Synopsis: this function prevents the redirection code from attempting
// redirection. Also prevents redirection of any of the child
// folders.
//
// Arguments: [in] Status : the error code indicating the cause of failure.
//
// Returns: nothing. It will always succeed.
//
// History: 9/20/1999 RahulTh created
//
// Notes: if the pre-processing step that handles the user name change
// fails for some reason, this function is invoked so that
// any attempt at applying simultaneous policy changes is thwarted
//
//---------------------------------------------------------------------------
void CRedirectInfo::PreventRedirection (DWORD Status) { m_bRedirectionAttempted = TRUE; m_StatusRedir = Status;
if (m_pChild) PreventDescendantRedirection (Status);
return; }
//+--------------------------------------------------------------------------
//
// Member: PreventDescendantRedirection
//
// Synopsis: this function invalidates the data in the children so that
// the client extension will not try to redirect them
// this is necessary to prevent redirection of children if the
// redirection of the parents failed.
//
// Arguments: [in] Status : the error code indicating the cause of failure
//
// Returns: nothing. it will always succeed.
//
// History: 11/21/1998 RahulTh created
//
// Notes:
//
//---------------------------------------------------------------------------
void CRedirectInfo::PreventDescendantRedirection (DWORD Status) { if (! m_pChild) //nothing to do if this is not a parent
return;
m_pChild->m_bRedirectionAttempted = TRUE; m_pChild->m_StatusRedir = Status;
//disable Startup too if start menu failed.
if (StartMenu == m_rID) { m_pChild->m_pChild->m_bRedirectionAttempted = TRUE; m_pChild->m_pChild->m_StatusRedir = Status; }
return; }
//+--------------------------------------------------------------------------
//
// Member: CRedirectInfo::UpdateDescendant
//
// Synopsis: fills up the internal variables of a descendant object
// if it is supposed to follow the parent by using the
// data stored in the parent object
//
// Arguments: none
//
// Returns: nothing
//
// History: 10/6/1998 RahulTh created
//
// Notes:
//
//---------------------------------------------------------------------------
DWORD CRedirectInfo::UpdateDescendant (void) { DWORD len; WCHAR * szLoc = NULL; DWORD Status;
Status = ERROR_SUCCESS;
if (!m_pParent) //this is not a special descendant
goto UpdateEnd;
if (!(m_dwFlags & REDIR_FOLLOW_PARENT)) goto UpdateEnd; //nothing to do. this descendant is not supposed to follow the parent
if (!m_pParent->m_fDataValid || (m_pParent->m_dwFlags & REDIR_DONT_CARE)) { m_fDataValid = m_pParent->m_fDataValid; m_dwFlags = REDIR_DONT_CARE; goto UpdateEnd; }
m_fDataValid = m_pParent->m_fDataValid;
m_bFollowsParent = TRUE;
//if we are here, policy has been specified for the parent
len = lstrlen (m_szLocDisplayName) + lstrlen (m_pParent->m_szLocation) + 2; //one extra for the backslash
if (m_cbLocSize < len) { //we need to allocate memory
szLoc = new WCHAR [len]; if (!szLoc) { //out of memory. cannot derive info. from parent. will have to ignore this GPO
DebugMsg((DM_VERBOSE, IDS_DERIVEINFO_ERROR, m_szLocDisplayName)); m_dwFlags = REDIR_DONT_CARE; m_fDataValid = FALSE; Status = ERROR_OUTOFMEMORY; goto UpdateEnd; } if (m_cbLocSize) delete [] m_szLocation; m_szLocation = szLoc; m_cbLocSize = len; }
if (m_pSid) { delete [] ((BYTE*) m_pSid); m_pSid = NULL; } Status = MySidCopy (&m_pSid, m_pParent->m_pSid); if ( ERROR_SUCCESS != Status ) { goto UpdateEnd; }
//copy the data (based on code above, the buffer is large enough, so no error checking is needed
//first get the settings
m_dwFlags = m_pParent->m_dwFlags & (REDIR_SETACLS | REDIR_MOVE_CONTENTS | REDIR_RELOCATEONREMOVE); (void) StringCchCopy(m_szLocation, m_cbLocSize, m_pParent->m_szLocation); len = lstrlen (m_szLocation); if (len > 0 && L'\\' != m_szLocation[len - 1]) { (void) StringCchCat(m_szLocation, m_cbLocSize, L"\\"); //add a \ only if the parent's path is not
//terminated with a \. Otherwise, we will
//end up with 2 \ in the child's path which
//will land SHGetFolderPath into trouble
//after the redirection is done.
} (void) StringCchCat(m_szLocation, m_cbLocSize, m_szLocDisplayName); //use the localized folder name
m_bRemove = m_pParent->m_bRemove;
UpdateEnd: return Status; }
//+--------------------------------------------------------------------------
//
// Member: CRedirectInfo::operator=
//
// Synopsis: overloaded assignment operator used for merging
//
// Arguments: standard
//
// Returns: standard
//
// History: 10/6/1998 RahulTh created
//
// Notes: DO NOT copy the values of m_bRedirectionAttempted and
// m_StatusRedir in this function.
//
//---------------------------------------------------------------------------
CRedirectInfo& CRedirectInfo::operator= (const CRedirectInfo& ri) { WCHAR * szLoc = 0; DWORD Status; PSID Sid; HRESULT hr = S_OK;
ASSERT (m_rID == ri.m_rID);
if (!ri.m_fDataValid) goto AssignEnd;
if ((ri.m_dwFlags & REDIR_FOLLOW_PARENT) && MyPics == m_rID) { m_fDataValid = ri.m_fDataValid; m_dwFlags = REDIR_FOLLOW_PARENT; m_bRemove = ri.m_bRemove; if (m_bValidGPO = ri.m_bValidGPO) //note:this IS an assignment -- not a comparison
(void) StringCbCopy(m_szGPOName, sizeof(m_szGPOName), ri.m_szGPOName); else m_szGPOName[0] = L'\0';
goto AssignEnd; } else if ((ri.m_dwFlags & (REDIR_DONT_CARE | REDIR_FOLLOW_PARENT))) { //REDIR_FOLLOW_PARENT will be set only if UpdateDescendant ran out of memory
//in any case, we will have to ignore the policy
//note that we have to special case My Pics above because UpdateDescendant
//is called for My Pics after all the policies have been looked at
//thus it has not been called at this point yet.
//the reason we call UpdateDescendant for My Pictures after looking at
//all the policies because it is possible to specify "Follow My Docs"
//in one policy and specify the location of My Docs in another policy
if (!m_fDataValid) { m_fDataValid = ri.m_fDataValid; m_dwFlags = REDIR_DONT_CARE; m_bRemove = ri.m_bRemove; if (m_bValidGPO = ri.m_bValidGPO) //note: this IS an assignment -- not a comparison
(void) StringCbCopy(m_szGPOName, sizeof(m_szGPOName), ri.m_szGPOName); else m_szGPOName[0] = L'\0'; }
goto AssignEnd; //ignore. no policy settings for the GPO being merged.
}
//note: in the following code... before modifying any of the data
//we must make sure that we can get memory for all of the members
//if we fail for even one of them and we have already changed the rest
//we can run into an inconsistent state. Therefore, we first allocate
//all the required memory and then actually proceed with the copy.
Sid = 0; Status = MySidCopy (&Sid, ri.m_pSid); if (ERROR_SUCCESS != Status) { DebugMsg ((DM_VERBOSE, IDS_MERGE_FAILURE, m_szLocDisplayName)); goto AssignEnd; }
if (m_cbLocSize < ri.m_cbLocSize) { szLoc = new WCHAR [ri.m_cbLocSize]; if (!szLoc) { //we could not obtain memory to store the new path.
//we will have to ignore this policy
DebugMsg ((DM_VERBOSE, IDS_MERGE_FAILURE, m_szLocDisplayName)); delete [] ((BYTE*) Sid); //do not do this at the end. The same memory will be used by m_pSid.
goto AssignEnd; } if (m_cbLocSize) delete [] m_szLocation; m_szLocation = szLoc; m_cbLocSize = ri.m_cbLocSize; }
//now we have the required memory, so we won't fail.
//fill in the data.
if (m_pSid) delete [] ((BYTE*) m_pSid); m_pSid = Sid; (void) StringCchCopy(m_szLocation, m_cbLocSize, ri.m_szLocation); m_dwFlags = ri.m_dwFlags & (REDIR_SETACLS | REDIR_MOVE_CONTENTS | REDIR_RELOCATEONREMOVE); m_bRemove = ri.m_bRemove; m_bFollowsParent = ri.m_bFollowsParent; m_fDataValid = ri.m_fDataValid; if (m_bValidGPO = ri.m_bValidGPO) //note: this IS an assignment not a comparison
(void) StringCbCopy(m_szGPOName, sizeof(m_szGPOName), ri.m_szGPOName); else m_szGPOName[0] = L'\0';
AssignEnd: return *this; }
//+--------------------------------------------------------------------------
//
// Member: CRedirectInfo::ComputeEffectivePolicyRemoval
//
// Synopsis: tries to find out if the removal of a user from a group
// has caused a particular policy to be effectively removed
// for a particular user.
//
// Arguments: [pGPOList] : a list of GPOs still in effect for this user.
// if a GPO is effectively removed for this user, it
// has to figure in this list.
// [pFileDB] : pointer to the file DB structure
//
// Returns:
//
// History: 2/18/1999 RahulTh created
//
// Notes: this also detects cases where a user's group membership may
// not have changed but the policy no longer specifies any
// target for this group.
//
//---------------------------------------------------------------------------
DWORD CRedirectInfo::ComputeEffectivePolicyRemoval ( PGROUP_POLICY_OBJECT pDeletedGPOList, PGROUP_POLICY_OBJECT pChangedGPOList, CFileDB * pFileDB) { WCHAR pwszLocalPath[MAX_PATH]; WCHAR * pwszLocalIniFile = NULL; PGROUP_POLICY_OBJECT pGPO; WCHAR * pwszGPTIniFilePath = NULL; DWORD Length = 0; DWORD Status = ERROR_SUCCESS; BOOL bStatus; HANDLE hFind; WIN32_FIND_DATA FindData; WCHAR pwszDefault[] = L"*"; DWORD dwFlags; WCHAR * pwszReturnedString = NULL; DWORD Size = 0; UNICODE_STRING StringW; DWORD i; PTOKEN_GROUPS pGroups; WCHAR pwszSid[MAX_PATH]; //more than enough to store a sid.
BOOL bGPOInChangedList = FALSE;
//if the policy resultant is not set to DONT_CARE, it means that even if a
//policy has been effectively removed due to a group change or removal of a
//group from the advanced settings, some other policy has taken precedence
//and as a result, the policy resultant should remain the way it is.
if (! (m_dwFlags & REDIR_DONT_CARE)) { Status = ERROR_SUCCESS; goto CmpEffPolRem_End; }
//there is no valid GPO stored in the per user per machine cache.
//so we cannot do much.
if (!gSavedSettings[m_rID].m_bValidGPO) { Status = ERROR_SUCCESS; goto CmpEffPolRem_End; }
//if the location of this folder was not specified by policy at last logon
//a group change cannot result in an effective policy removal for this folder.
if (gSavedSettings[m_rID].m_dwFlags & REDIR_DONT_CARE) { Status = ERROR_SUCCESS; goto CmpEffPolRem_End; }
//if we are here, then the folder was last redirected through policy and now
//either policy does not care, or a group change makes it seem so. If it is
//the latter, then we have to compute effective policy removal. Also, it is
//possible that the last policy application was a partial success and
//therefore a GPO that got deleted did not show up in either the changed
//GPO list or the deleted GPO list. We have to take into account that case
//too.
//first check if the GPO is present in the deleted GPO list.
for (pGPO = pDeletedGPOList; pGPO; pGPO = pGPO->pNext) { if (0 == _wcsicmp (gSavedSettings[m_rID].m_szGPOName, pGPO->szGPOName)) break; }
if (!pGPO) { //if the policy isn't in the deleted GPO list, check if it is in the
//changed GPO list. If it isn't then this is a GPO that got deleted but did
//not show up in any of the lists because it never got fully applied.
//if it is, then either this is actually a don't care situation or it
//should be treated as policy removal because there was a group change.
for (pGPO = pChangedGPOList; pGPO; pGPO = pGPO->pNext) { if (0 == _wcsicmp (gSavedSettings[m_rID].m_szGPOName, pGPO->szGPOName)) break; }
if (NULL != pGPO) //it is in the changed GPO list.
{ bGPOInChangedList = TRUE; //get the path to the ini file on the sysvol.
Length = wcslen(pGPO->lpFileSysPath) + wcslen(GPT_SUBDIR) + wcslen (INIFILE_NAME) + 1; __try { pwszGPTIniFilePath = (WCHAR *) alloca( Length * sizeof(WCHAR) ); } __except(GetExceptionCode() == STATUS_STACK_OVERFLOW) { _resetstkoflw(); pwszGPTIniFilePath = NULL; Status = ERROR_OUTOFMEMORY; goto CmpEffPolRem_End; } Status = HRESULT_CODE(StringCchCopy( pwszGPTIniFilePath, Length, pGPO->lpFileSysPath )); if ( Status != ERROR_SUCCESS ) { goto CmpEffPolRem_End; } Status = HRESULT_CODE(StringCchCat( pwszGPTIniFilePath, Length, GPT_SUBDIR )); if ( Status != ERROR_SUCCESS ) { goto CmpEffPolRem_End; } Status = HRESULT_CODE(StringCchCat( pwszGPTIniFilePath, Length, INIFILE_NAME )); if ( Status != ERROR_SUCCESS ) { goto CmpEffPolRem_End; } } }
//get the path to the locally cached copy of the ini file.
Length = wcslen (pFileDB->_pwszLocalPath) + wcslen (gSavedSettings[m_rID].m_szGPOName) + 6; __try { pwszLocalIniFile = (WCHAR *) alloca (Length * sizeof (WCHAR)); } __except(GetExceptionCode() == STATUS_STACK_OVERFLOW) { _resetstkoflw(); pwszLocalIniFile = NULL; Status = ERROR_OUTOFMEMORY; goto CmpEffPolRem_End; } Status = HRESULT_CODE(StringCchCopy( pwszLocalIniFile, Length, pFileDB->_pwszLocalPath )); if ( Status != ERROR_SUCCESS ) { goto CmpEffPolRem_End; } Status = HRESULT_CODE(StringCchCat( pwszLocalIniFile, Length, L"\\" )); if ( Status != ERROR_SUCCESS ) { goto CmpEffPolRem_End; } Status = HRESULT_CODE(StringCchCat( pwszLocalIniFile, Length, gSavedSettings[m_rID].m_szGPOName )); if ( Status != ERROR_SUCCESS ) { goto CmpEffPolRem_End; } Status = HRESULT_CODE(StringCchCat( pwszLocalIniFile, Length, L".ini" )); if ( Status != ERROR_SUCCESS ) { goto CmpEffPolRem_End; }
Status = ERROR_SUCCESS; bStatus = FALSE; if (bGPOInChangedList) { bStatus = CopyFile( pwszLocalIniFile, pwszGPTIniFilePath, FALSE ); } if ( ! bStatus ) // Work off of the locally cached copy.
{ //try to use the cached version if any.
hFind = FindFirstFile( pwszLocalIniFile, &FindData ); if ( INVALID_HANDLE_VALUE != hFind ) { Status = ERROR_SUCCESS; FindClose( hFind ); } else { Status = GetLastError(); } }
//we don't have an ini file to work with, so we can't do much but quit.
if (ERROR_SUCCESS != Status) goto CmpEffPolRem_End;
//now we have an ini file. so read the relevant info. from it.
//first grab the flags
Status = SafeGetPrivateProfileStringW ( L"FolderStatus", m_szDisplayName, pwszDefault, &pwszReturnedString, &Size, pwszLocalIniFile );
if (ERROR_SUCCESS != Status) goto CmpEffPolRem_End;
if (L'*' == *pwszReturnedString) { //there are no settings for this folder. Possibly because
//someone changed the settings on the server.
//Treat it as a don't care.
goto CmpEffPolRem_End; }
//now grab the hex flags
StringW.Buffer = pwszReturnedString; StringW.Length = wcslen (pwszReturnedString) * sizeof (WCHAR); StringW.MaximumLength = StringW.Length + sizeof(WCHAR); RtlUnicodeStringToInteger( &StringW, 16, &dwFlags );
//if this is a special descendant folder and it is supposed to follow
//the parent, we might first have to derive its settings from the
//parent and then proceed.
if (m_pParent && (dwFlags & REDIR_FOLLOW_PARENT)) { //the check for m_pParent is redundant since non-descendant folders
//can never have this flag. but this has been added as a safety mechanism
//against ini file corruption
//we will have to derive the settings from the parent later on.
m_dwFlags = REDIR_FOLLOW_PARENT; m_fDataValid = TRUE; m_bFollowsParent = TRUE; m_bRemove = TRUE; m_bValidGPO = FALSE; //since this is a removal
m_szGPOName[0] = L'\0'; Status = UpdateDescendant(); //derive the settings from the parent
goto CmpEffPolRem_End; }
if ((dwFlags & REDIR_DONT_CARE) || (m_bFollowsParent && (m_dwFlags & REDIR_DONT_CARE))) { //the policy has been changed to Don't Care. so it is not a removal.
//leave everything as is.
goto CmpEffPolRem_End; }
if (!(dwFlags & REDIR_RELOCATEONREMOVE)) { //the choice is to orphan. so let it stay as don't care.
goto CmpEffPolRem_End; } //
// If the GPO that was used for redirection the last time is not in the
// changed GPO list, then this is surely a policy removal.
// otherwise, it can either be a policy removal due to a group change (or
// a group's policy getting removed from the GPO or it can be just that
// the policy was changed to Don't care.
//
if (bGPOInChangedList) { //
// Check if the user is still a member of the group that was used for
// redirection.
//
pGroups = pFileDB->_pGroups; for (i = 0, bStatus = FALSE; i < pGroups->GroupCount && !bStatus; i++ ) { bStatus = RtlEqualSid (gSavedSettings[m_rID].m_psid, pGroups->Groups[i].Sid); }
if (bStatus) //the user still belongs to that group.
{ //so perhaps the policy for this group was removed.
//make sure that this is the case.
Status = ERROR_INVALID_SID; if (gSavedSettings[m_rID].m_psid) { pwszSid [0] = L'\0'; StringW.Length = 0; StringW.MaximumLength = sizeof (pwszSid); StringW.Buffer = pwszSid; Status = RtlConvertSidToUnicodeString (&StringW, gSavedSettings[m_rID].m_psid, FALSE); }
if (ERROR_SUCCESS != Status) goto CmpEffPolRem_End;
Status = SafeGetPrivateProfileStringW ( m_szDisplayName, StringW.Buffer, pwszDefault, &pwszReturnedString, &Size, pwszLocalIniFile );
if (ERROR_SUCCESS != Status) goto CmpEffPolRem_End;
if (0 != _wcsicmp(pwszReturnedString, pwszDefault)) { //
// Policy exists for this folder so leave things the way they are.
// Ideally this is not possible and one should never enter this
// code path
//
goto CmpEffPolRem_End; } } }
//
// If the user is no longer a member of the group, then this is clearly
// a case where the policy is effectively removed because the user was
// removed from a group that was used for redirection.
//
// At any rate, if we are here, then this is a policy removal so make the
// appropriate settings
//
Size = wcslen(L"%USERPROFILE%\\") + wcslen(m_szLocFolderRelativePath) + 1; if (m_cbLocSize && (m_cbLocSize < Size)) { delete [] m_szLocation; m_cbLocSize = 0; m_szLocation = new WCHAR [Size]; if (!m_szLocation) { Status = ERROR_OUTOFMEMORY; goto CmpEffPolRem_End; } m_cbLocSize = Size; } (void) StringCchCopy(m_szLocation, m_cbLocSize, L"%USERPROFILE%\\"); (void) StringCchCat(m_szLocation, m_cbLocSize, m_szLocFolderRelativePath); m_fDataValid = TRUE; m_dwFlags = dwFlags; m_bRemove = TRUE; m_bValidGPO = FALSE; //since this is a removal.
m_szGPOName[0] = '\0'; DebugMsg((DM_VERBOSE, IDS_EFFECTIVE_REMOVE_POLICY, pGPO?pGPO->lpDisplayName:gSavedSettings[m_rID].m_szGPOName, m_szLocDisplayName));
CmpEffPolRem_End: if (pwszReturnedString) delete [] pwszReturnedString; return Status; }
CRedirectInfo::HasPolicy() { BOOL bHasExplicitPolicy; BOOL bHasPolicy;
bHasExplicitPolicy = ! ( m_dwFlags & REDIR_DONT_CARE );
//
// If this folder appears to be explicitly redirected, we
// should be sure that its parent isn't explicitly denied,
// if it is configured to follow the parent
//
if ( ( m_dwFlags & REDIR_FOLLOW_PARENT ) && m_pParent && bHasExplicitPolicy ) { bHasPolicy = m_pParent->HasPolicy(); } else { bHasPolicy = bHasExplicitPolicy; } return bHasPolicy; }
|