Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2581 lines
91 KiB

/*++
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;
}