Source code of Windows XP (NT5)
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.
 
 
 
 
 
 

2966 lines
89 KiB

//*************************************************************
//
// Copyright (c) Microsoft Corporation 1998
// All rights reserved
//
// filedb.cxx
//
//*************************************************************
#include "fdeploy.hxx"
#define SAVED_SETTINGS_FILE L"{25537BA6-77A8-11D2-9B6C-0000F8080861}.ini"
HRESULT RsopSidsFromToken(PRSOPTOKEN pRsopToken,
PTOKEN_GROUPS* ppGroups);
FOLDERINFO gUserShellFolders[] =
{
{CSIDL_APPDATA, 17, L"Application Data\\", L"AppData"},
// {CSIDL_COOKIES, 8, L"Cookies\\", L"Cookies"},
{CSIDL_DESKTOPDIRECTORY, 8, L"Desktop\\", L"Desktop"},
{CSIDL_FAVORITES, 10, L"Favorites\\", L"Favorites"},
// {CSIDL_HISTORY, 8, L"History\\", L"History"},
// {0, 15, L"Local Settings\\", NULL}, Has no reg key, no CSIDL
{CSIDL_MYPICTURES, 25, L"My Documents\\My Pictures\\", L"My Pictures"},
{CSIDL_PERSONAL, 13, L"My Documents\\", L"Personal"},
{CSIDL_NETHOOD, 8, L"NetHood\\", L"NetHood"},
{CSIDL_PRINTHOOD, 10, L"PrintHood\\", L"PrintHood"},
// {CSIDL_RECENT, 7, L"Recent\\", L"Recent"},
{CSIDL_SENDTO, 7, L"SendTo\\", L"SendTo"},
{CSIDL_STARTUP, 28, L"Start Menu\\Programs\\Startup\\", L"Startup"},
{CSIDL_PROGRAMS, 20, L"Start Menu\\Programs\\", L"Programs"},
{CSIDL_STARTMENU, 11, L"Start Menu\\", L"Start Menu"},
{CSIDL_TEMPLATES, 10, L"Templates\\", L"Templates"},
// {CSIDL_INTERNET_CACHE, 25, L"Temporary Internet Files\\", L"Cache"},
{0, 0, NULL, NULL }
};
FOLDERINFO gMachineShellFolders[] =
{
{CSIDL_COMMON_APPDATA, 17, L"Application Data\\", L"Common AppData"},
{CSIDL_COMMON_DESKTOPDIRECTORY, 8, L"Desktop\\", L"Common Desktop"},
// {0, 10, L"Documents\\", L"Common Documents\\"}, No shell support
{CSIDL_COMMON_STARTUP, 28, L"Start Menu\\Programs\\Startup\\", L"Common Startup"},
{CSIDL_COMMON_PROGRAMS, 20, L"Start Menu\\Programs\\", L"Common Programs"},
{CSIDL_COMMON_STARTMENU, 11, L"Start Menu\\", L"Common Start Menu"},
{0, 0, NULL, NULL }
};
static DWORD gSchema = 1;
CFileDB::CFileDB()
{
_hUserToken = 0;
_hkRoot = 0;
_pEnvBlock = 0;
_pGroups = 0;
_pwszProfilePath = 0;
_pwszGPTPath = 0;
_GPTPathLen = 0;
_pwszIniFilePath = 0;
_IniFileLen = 0;
_pwszGPOName = 0;
_pwszGPOUniqueName = 0;
_pwszGPOSOMPath = 0;
_pRsopContext = 0;
}
CFileDB::~CFileDB()
{
if ( _pEnvBlock )
DestroyEnvironmentBlock( _pEnvBlock );
if (_pwszProfilePath)
delete _pwszProfilePath;
if (_pwszGPTPath)
delete _pwszGPTPath;
if (_pwszIniFilePath)
delete _pwszIniFilePath;
//
// Note that _pGroups must be freed with LocalFree --
// we are able to free it with delete because we
// have redefined delete to be LocalFree
//
if (_pGroups)
delete (BYTE*) _pGroups;
// _pwszGPOName is not allocated
// _pwszGPOUniqueName is not allocated
// _pwszGPODSPath is not allocated
}
DWORD CFileDB::Initialize (
HANDLE hUserToken,
HKEY hkRoot,
CRsopContext* pRsopContext)
{
BOOL bStatus;
DWORD Status = ERROR_SUCCESS;
ULONG Size;
int CSidl;
WCHAR * pwszSlash;
HRESULT hr;
HANDLE hFind;
WIN32_FIND_DATA FindData;
//set the token
_hUserToken = hUserToken;
//set the root key
_hkRoot = hkRoot;
// set the rsop logging context
_pRsopContext = pRsopContext;
//create an environment block for the user. we need this for expanding
//variables.
if (! _pEnvBlock)
{
if (!CreateEnvironmentBlock ( &_pEnvBlock, _hUserToken, FALSE))
{
Status = GetLastError();
goto InitializeEnd;
}
}
//get the list of group to which the user belongs
_pGroups = 0;
Size = 0;
//
// We may only use the Nt security subsystem api below
// to retrieve groups when we are not in planning mode
//
for (; ! _pRsopContext->IsPlanningModeEnabled() ;)
{
Status = NtQueryInformationToken(
_hUserToken,
TokenGroups,
_pGroups,
Size,
&Size );
if ( STATUS_BUFFER_TOO_SMALL == Status )
{
_pGroups = (PTOKEN_GROUPS) new BYTE [ Size ];
if ( ! _pGroups )
break;
continue;
}
if ( Status != STATUS_SUCCESS )
{
if (_pGroups)
delete [] ((BYTE*) _pGroups);
_pGroups = 0;
}
break;
}
//
// In planning mode, we get our security groups from
// the policy engine's simulated token, not from a real token
//
if ( _pRsopContext->IsPlanningModeEnabled() )
{
DWORD cbSize;
PRSOP_TARGET pRsopTarget;
HRESULT hr;
pRsopTarget = _pRsopContext->_pRsopTarget;
//
// The call below uses RSoP's planning mode "simulated"
// security subsystem to retrieve the sids from the simulated
// token. The function allocates memory in _pGroups that
// must be freed with LocalFree.
//
hr = RsopSidsFromToken(pRsopTarget->pRsopToken, &_pGroups);
Status = HRESULT_CODE(hr);
}
if (ERROR_SUCCESS != Status)
goto InitializeEnd;
//
// Retrieve the local path -- note that we do not need this in planning mode
//
if ( ! _pRsopContext->IsPlanningModeEnabled() )
{
//get the path to our directory under Local Settings.
CSidl = CSIDL_LOCAL_APPDATA | CSIDL_FLAG_CREATE;
hr = SHGetFolderPath( NULL, CSidl, _hUserToken, 0, _pwszLocalPath );
if ( hr != S_OK )
{
//try to get the last error.
if (FACILITY_WIN32 == HRESULT_FACILITY (hr))
{
Status = HRESULT_CODE(hr);
}
else
{
Status = GetLastError();
if (ERROR_SUCCESS == Status)
{
//an error had occurred but nobody called SetLastError
//should not be mistaken as a success.
Status = (DWORD) hr;
}
}
DebugMsg((DM_WARNING, IDS_NO_LOCALAPPDATA, Status));
goto InitializeEnd;
}
pwszSlash = _pwszLocalPath + wcslen( _pwszLocalPath );
wcscat( _pwszLocalPath, L"\\Microsoft\\Windows\\File Deployment" );
Status = ERROR_SUCCESS;
//now create directories as necessary
// Quick check to see if we have necessary local dirs.
hFind = FindFirstFile( _pwszLocalPath, &FindData );
if ( INVALID_HANDLE_VALUE == hFind )
{
do
{
pwszSlash = wcschr( &pwszSlash[1], L'\\' );
if ( pwszSlash )
*pwszSlash = 0;
bStatus = CreateDirectory( _pwszLocalPath, NULL );
if ( ! bStatus && (GetLastError() != ERROR_ALREADY_EXISTS) )
{
Status = GetLastError();
break;
}
if ( pwszSlash )
*pwszSlash = L'\\';
} while ( pwszSlash );
}
else
{
FindClose( hFind );
}
}
InitializeEnd:
return Status;
}
DWORD
CFileDB::Process(
PGROUP_POLICY_OBJECT pGPO,
BOOL bRemove
)
{
WCHAR * pwszGPTIniFilePath;
DWORD Length;
DWORD Status;
DWORD ProcessStatus;
BOOL bStatus;
BOOL bPolicyApplied;
HANDLE hFind;
WIN32_FIND_DATA FindData;
if ( bRemove && _pRsopContext->IsPlanningModeEnabled() )
{
return ERROR_INVALID_PARAMETER;
}
bPolicyApplied = FALSE;
Status = ERROR_SUCCESS;
//first initialize the variables that vary with policies
_bRemove = bRemove;
if ( ! _bRemove )
{
Length = wcslen(pGPO->lpFileSysPath) + wcslen(GPT_SUBDIR) + 1;
if (Length > _GPTPathLen)
{
//we need more memory than has been allocated.
//so get that before proceeding
if (_pwszGPTPath)
delete _pwszGPTPath;
_GPTPathLen = 0; //make sure that this always reflects the correct value
_pwszGPTPath = new WCHAR [Length];
if ( ! _pwszGPTPath )
{
Status = ERROR_OUTOFMEMORY;
goto ProcessEnd;
}
_GPTPathLen = Length; //make sure that this always reflects the correct value
}
wcscpy( _pwszGPTPath, pGPO->lpFileSysPath );
wcscat( _pwszGPTPath, GPT_SUBDIR );
Length += wcslen( INIFILE_NAME );
pwszGPTIniFilePath = (WCHAR *) alloca( Length * sizeof(WCHAR) );
if ( ! pwszGPTIniFilePath )
{
Status = ERROR_OUTOFMEMORY;
goto ProcessEnd;
}
wcscpy( pwszGPTIniFilePath, _pwszGPTPath );
wcscat( pwszGPTIniFilePath, INIFILE_NAME );
//
// Do a quick check to see if we have any file deployment
// for this policy.
//
hFind = FindFirstFile( pwszGPTIniFilePath, &FindData );
if ( INVALID_HANDLE_VALUE == hFind )
{
Status = GetLastError();
goto ProcessEnd;
}
else
{
bPolicyApplied = TRUE;
DebugMsg((DM_VERBOSE, IDS_HASADD_POLICY, pGPO->lpDisplayName));
FindClose( hFind );
}
}
Status = ERROR_SUCCESS;
if ( _pRsopContext->IsPlanningModeEnabled() )
{
Length = wcslen( pwszGPTIniFilePath ) + 1;
}
else
{
Length = wcslen( _pwszLocalPath ) + wcslen( pGPO->szGPOName ) + 6;
}
if (Length > _IniFileLen)
{
//we need more memory than has been allocated
if (_pwszIniFilePath)
delete _pwszIniFilePath;
_IniFileLen = 0; //make sure that this always reflects the current value
_pwszIniFilePath = new WCHAR[Length];
if ( ! _pwszIniFilePath )
{
Status = ERROR_OUTOFMEMORY;
goto ProcessEnd;
}
_IniFileLen = Length; //make sure that this always reflects the current value
}
if ( _pRsopContext->IsPlanningModeEnabled() )
{
wcscpy( _pwszIniFilePath, pwszGPTIniFilePath );
}
else
{
wcscpy( _pwszIniFilePath, _pwszLocalPath );
wcscat( _pwszIniFilePath, L"\\" );
wcscat( _pwszIniFilePath, pGPO->szGPOName );
wcscat( _pwszIniFilePath, L".ini" );
}
if ( _bRemove )
{
hFind = FindFirstFile( _pwszIniFilePath, &FindData );
if ( INVALID_HANDLE_VALUE == hFind )
{
//this error should be ignored since there is nothing we can do.
//the policy has been deleted and the local settings are missing
//so we have no choice but to treat these as if the settings were
//to orphan the folder upon policy removal.
goto ProcessEnd;
}
else
{
bPolicyApplied = TRUE;
DebugMsg((DM_VERBOSE, IDS_HASREMOVE_POLICY, pGPO->lpDisplayName));
FindClose( hFind );
}
}
else if ( ! _pRsopContext->IsPlanningModeEnabled() )
{
bStatus = CopyFile( pwszGPTIniFilePath, _pwszIniFilePath, FALSE );
if ( ! bStatus )
Status = GetLastError();
}
if ( Status != ERROR_SUCCESS )
goto ProcessEnd;
_pwszGPOName = (WCHAR *) pGPO->lpDisplayName;
_pwszGPOUniqueName = (WCHAR *) pGPO->szGPOName;
_pwszGPOSOMPath = ( WCHAR *) pGPO->lpLink;
_pwszGPODSPath = ( WCHAR *) pGPO->lpDSPath;
ProcessStatus = ProcessRedirects();
if ( (ProcessStatus != ERROR_SUCCESS) && (ERROR_SUCCESS == Status) )
Status = ProcessStatus;
ProcessEnd:
if ( Status != ERROR_SUCCESS )
{
gpEvents->Report (
EVENT_FDEPLOY_POLICYPROCESS_FAIL,
2,
pGPO->lpDisplayName,
StatusToString (Status)
);
}
else if ( bPolicyApplied )
{
DebugMsg((DM_VERBOSE, IDS_PROCESS_GATHER_OK, pGPO->lpDisplayName));
}
return Status;
}
DWORD
CFileDB::ProcessRedirects(void)
{
WCHAR * pwszString = 0;
WCHAR * pwszSectionStrings = 0;
WCHAR * pwszRedirection = 0;
WCHAR wszFolderName[80];
UNICODE_STRING String;
DWORD Flags;
DWORD RedirectStatus;
DWORD Status;
BOOL bStatus;
REDIRECTABLE index;
DWORD RedirStatus;
CRedirectInfo riPolicy [(int) EndRedirectable]; //the redirection info. for this policy
DWORD i;
//first load the localized folder names
for (i = 0, Status = ERROR_SUCCESS; i < (DWORD)EndRedirectable; i++)
{
Status = riPolicy[i].LoadLocalizedNames();
if (ERROR_SUCCESS != Status)
return Status; //bail out if the resource names cannot be loaded.
}
pwszSectionStrings = 0;
bStatus = ReadIniSection( L"FolderStatus", &pwszSectionStrings );
if ( ! bStatus )
{
Status = ERROR_OUTOFMEMORY;
goto ProcessRedirectsEnd;
}
Status = ERROR_SUCCESS;
for ( pwszString = pwszSectionStrings;
*pwszString != 0;
pwszString += lstrlen(pwszString) + 1 )
{
//
// The syntax for each line is :
// foldername=FLAGS
//
//
// First extract the foldername.
//
pwszRedirection = wcschr( pwszString, L'=' );
*pwszRedirection = 0;
wcscpy( wszFolderName, pwszString );
*pwszRedirection++ = L'=';
//
// Now grab the hex FLAGS.
//
String.Buffer = pwszRedirection;
pwszRedirection = wcschr( pwszRedirection, L' ' );
if ( pwszRedirection )
*pwszRedirection = 0;
String.Length = wcslen( String.Buffer ) * sizeof(WCHAR);
String.MaximumLength = String.Length + sizeof(WCHAR);
RtlUnicodeStringToInteger( &String, 16, &Flags );
//just gather the information here.
//actual redirections are performed in ProcessGPO after all the policies
//have been processed.
if (EndRedirectable ==
(index = CRedirectInfo::GetFolderIndex (wszFolderName)))
{
//redirection of this folder is not supported
DebugMsg ((DM_VERBOSE, IDS_REDIR_NOTSUPPORTED, wszFolderName));
}
else
{
//if this is a policy that has been removed and it was decided to
//orphan the contents, we don't even look at it
if (!_bRemove || (Flags & (REDIR_RELOCATEONREMOVE | REDIR_FOLLOW_PARENT)))
{
//if there is a problem in gathering redirection info. for a folder
//quit immediately, or we might end up computing an incorrect
//resultant policy
if (ERROR_SUCCESS != (Status = riPolicy [(int) index].GatherRedirectionInfo (this, Flags, _bRemove)))
goto ProcessRedirectsEnd;
}
}
}
Status = ERROR_SUCCESS;
//now update the data stored in the descendant objects
//this is required because if the descendants follow the parent
//then the settings need to be obtained from the parent
//note that we do not call UpdateDescendant for MyPics here, but in fdeploy.cxx
//for details on why we do this, look at comments in operator= in redir.cxx
riPolicy[(int) Programs].UpdateDescendant();
riPolicy[(int) Startup].UpdateDescendant(); //this call must be made after Programs has been updated
//merge info into the global redirection store
for (i = 0; i < (DWORD) EndRedirectable; i++)
{
if (_bRemove)
gDeletedPolicyResultant[i] = riPolicy[i];
else
gAddedPolicyResultant[i] = riPolicy[i];
}
ProcessRedirectsEnd:
delete pwszSectionStrings;
if ( ERROR_SUCCESS != Status )
{
DebugMsg((DM_VERBOSE, IDS_PROCESSREDIRECTS, Status));
}
else
{
if ( ! _bRemove && _pRsopContext->IsRsopEnabled() )
{
(void) AddRedirectionPolicies(
this,
riPolicy);
}
}
return Status;
}
BOOL
CFileDB::ReadIniSection(
WCHAR * pwszSectionName,
WCHAR ** ppwszStrings,
DWORD * pcchLen
)
{
DWORD Length;
DWORD ReturnLength;
*ppwszStrings = 0;
Length = 256;
for (;;)
{
delete *ppwszStrings;
*ppwszStrings = new WCHAR[Length];
if ( ! *ppwszStrings )
return FALSE;
ReturnLength = GetPrivateProfileSection(
pwszSectionName,
*ppwszStrings,
Length,
_pwszIniFilePath );
if ( ReturnLength != (Length - 2) )
{
if (pcchLen)
{
*pcchLen = ReturnLength;
}
return TRUE;
}
Length *= 2;
}
}
DWORD
CFileDB::GetLocalFilePath(
WCHAR * pwszFolderPath,
WCHAR * wszFullPath
)
{
int CSidl;
DWORD Status;
HRESULT hr;
WCHAR * pwszFolderName;
CSidl = CSIDL_FLAG_MASK; //use a value that is not a valid CSIDL value for any folder.
for (DWORD n = 0; gUserShellFolders[n].FolderName; n++)
{
if (0 == _wcsicmp (pwszFolderPath, gUserShellFolders[n].FolderName))
{
pwszFolderName = gUserShellFolders[n].FolderName;
CSidl = gUserShellFolders[n].CSidl;
break;
}
}
if ( CSIDL_FLAG_MASK != CSidl )
{
hr = SHGetFolderPath( 0, CSidl | CSIDL_FLAG_DONT_VERIFY,
_hUserToken, 0, wszFullPath );
Status = GetWin32ErrFromHResult (hr);
if ( ERROR_SUCCESS != Status )
{
DebugMsg((DM_WARNING, IDS_FOLDERPATH_FAIL, pwszFolderName, Status));
return Status;
}
}
else
return ERROR_INVALID_NAME;
return ERROR_SUCCESS;
}
DWORD
CFileDB::GetPathFromFolderName(
WCHAR * pwszFolderName,
WCHAR * wszFullPath
)
{
int CSidl;
DWORD Status;
HRESULT hr;
CSidl = CSIDL_FLAG_MASK; //use a csidl value that is not valid for any folder
for (DWORD n = 0; gUserShellFolders[n].FolderName; n++)
{
//we subtract 1 from the length because one of the paths is \ terminated
//and the other is not.
if ( _wcsnicmp( pwszFolderName, gUserShellFolders[n].FolderName, gUserShellFolders[n].FolderNameLength - 1 ) == 0 )
{
CSidl = gUserShellFolders[n].CSidl;
break;
}
}
if ( CSIDL_FLAG_MASK != CSidl )
{
hr = SHGetFolderPath( 0, CSidl | CSIDL_FLAG_DONT_VERIFY,
_hUserToken, 0, wszFullPath );
if ( S_OK != hr )
{
DebugMsg((DM_WARNING, IDS_FOLDERPATH_FAIL, pwszFolderName, hr));
return (DWORD) hr;
}
}
else
return ERROR_INVALID_NAME;
return ERROR_SUCCESS;
}
DWORD
CFileDB::CopyGPTFile(
WCHAR * pwszLocalPath,
WCHAR * pwszGPTPath
)
{
DWORD FileAttr;
DWORD Status;
BOOL bStatus;
if ( L'\\' == pwszLocalPath[wcslen(pwszLocalPath)-1] )
{
bStatus = CreateDirectory( pwszLocalPath, NULL );
//
// Note, we leave attributes & security as is if the dir
// already exits.
//
if ( ! bStatus && (ERROR_ALREADY_EXISTS == GetLastError()) )
return ERROR_SUCCESS;
}
else
{
FileAttr = GetFileAttributes( pwszLocalPath );
if ( 0xFFFFFFFF == FileAttr )
return GetLastError();
SetFileAttributes( pwszLocalPath, FileAttr & ~FILE_ATTRIBUTE_READONLY );
bStatus = CopyFile(
pwszGPTPath,
pwszLocalPath,
FALSE );
//
// By default, we set the read only attribute on deployed files. We
// combine this with any existing file attributes on the GPT file.
//
if ( bStatus )
{
FileAttr = GetFileAttributes( pwszGPTPath );
bStatus = (FileAttr != 0xFFFFFFFF);
}
else
{
SetFileAttributes( pwszLocalPath, FileAttr );
}
if ( bStatus )
{
FileAttr |= FILE_ATTRIBUTE_READONLY;
bStatus = SetFileAttributes( pwszLocalPath, FileAttr );
}
}
if ( ! bStatus )
return GetLastError();
return ERROR_SUCCESS;
}
const FOLDERINFO*
CFileDB::FolderInfoFromFolderName(
WCHAR * pwszFolderName
)
{
//
// This method returns the index into global array.
//
if ( _hUserToken )
{
for ( DWORD n = 0; gUserShellFolders[n].FolderName; n++ )
{
if ( _wcsnicmp( pwszFolderName, gUserShellFolders[n].FolderName, gUserShellFolders[n].FolderNameLength - 1 ) == 0 )
return &gUserShellFolders[n];
}
}
else
{
for ( DWORD n = 0; gMachineShellFolders[n].FolderName; n++ )
{
if ( _wcsnicmp( pwszFolderName, gMachineShellFolders[n].FolderName, gMachineShellFolders[n].FolderNameLength - 1 ) == 0 )
return &gMachineShellFolders[n];
}
}
return NULL;
}
int
CFileDB::RegValueCSIDLFromFolderName(
WCHAR * pwszFolderName
)
{
const FOLDERINFO *pFI;
pFI = FolderInfoFromFolderName(pwszFolderName);
if (pFI != NULL)
return pFI->CSidl;
else
return -1; // invalid CSIDL
}
WCHAR *
CFileDB::RegValueNameFromFolderName(
WCHAR * pwszFolderName
)
{
//
// This is used by folder redirection logic. In this case the folder
// name is not '\' terminated, so we subtract one from the folder
// name length.
//
const FOLDERINFO *pFI;
pFI = FolderInfoFromFolderName(pwszFolderName);
if (pFI != NULL)
return pFI->RegValue;
else
return NULL;
}
const WCHAR *
CFileDB::GetLocalStoragePath ()
{
return (LPCWSTR) _pwszLocalPath;
}
DWORD
CFileDB::CopyFileTree(
WCHAR * pwszExistingPath,
WCHAR * pwszNewPath,
WCHAR * pwszIgnoredSubdir,
SHARESTATUS StatusFrom,
SHARESTATUS StatusTo,
BOOL bAllowRdrTimeout,
CCopyFailData * pCopyFailure
)
{
HANDLE hFind;
WIN32_FIND_DATA FindData;
WIN32_FILE_ATTRIBUTE_DATA SourceAttr;
WIN32_FILE_ATTRIBUTE_DATA DestAttr;
WCHAR * wszSource = NULL;
WCHAR * pwszSourceEnd = 0;
WCHAR * wszDest = NULL;
WCHAR * pwszDestEnd = 0;
WCHAR * pwszTempFilename = 0;
DWORD FileAttributes;
DWORD Status;
BOOL bStatus;
BOOL bReuseTempName = FALSE;
int lenSource;
int lenDest;
DWORD StatusCSCDel = ERROR_SUCCESS;
DWORD dwAttr = INVALID_FILE_ATTRIBUTES;
if (! pwszExistingPath || ! pwszNewPath)
return ERROR_PATH_NOT_FOUND;
lenSource = wcslen (pwszExistingPath);
lenDest = wcslen (pwszNewPath);
if (! lenSource || ! lenDest)
return ERROR_PATH_NOT_FOUND;
wszSource = (WCHAR *) alloca (sizeof (WCHAR) * (lenSource + MAX_PATH + 2));
if (NULL == wszSource)
return ERROR_OUTOFMEMORY;
lstrcpy( wszSource, pwszExistingPath );
pwszSourceEnd = wszSource + lenSource;
if (L'\\' != pwszSourceEnd[-1])
*pwszSourceEnd++ = L'\\';
pwszSourceEnd[0] = L'*';
pwszSourceEnd[1] = 0;
wszDest = (WCHAR *) alloca (sizeof (WCHAR) * (lenDest + MAX_PATH + 2));
if (NULL == wszDest)
return ERROR_OUTOFMEMORY;
lstrcpy( wszDest, pwszNewPath );
pwszDestEnd = wszDest + lenDest;
if (L'\\' != pwszDestEnd[-1])
*pwszDestEnd++ = L'\\';
*pwszDestEnd = 0;
hFind = FindFirstFile( wszSource, &FindData );
if ( INVALID_HANDLE_VALUE == hFind )
return ERROR_SUCCESS;
Status = ERROR_SUCCESS;
do
{
lstrcpy( pwszSourceEnd, FindData.cFileName );
lstrcpy( pwszDestEnd, FindData.cFileName );
if ( FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
{
if ( lstrcmp( FindData.cFileName, L"." ) == 0 ||
lstrcmp( FindData.cFileName, L".." ) == 0 ||
(pwszIgnoredSubdir && lstrcmpi( FindData.cFileName, pwszIgnoredSubdir ) == 0) )
continue;
Status = FullDirCopyW (wszSource, wszDest, FALSE);
if ( ERROR_SUCCESS == Status )
{
if (ERROR_SUCCESS == StatusCSCDel)
{
Status = CopyFileTree( wszSource, wszDest, NULL, StatusFrom, StatusTo, bAllowRdrTimeout, pCopyFailure );
}
else
{
//no point delaying CSCDeletes anymore since we have already failed once.
Status = CopyFileTree (wszSource, wszDest, NULL, StatusFrom, StatusTo, FALSE, pCopyFailure);
}
//copy over the pin info. too
if (ERROR_SUCCESS == Status)
MergePinInfo (wszSource, wszDest, StatusFrom, StatusTo);
}
else
{
pCopyFailure->RegisterFailure (wszSource, wszDest);
DebugMsg((DM_VERBOSE, IDS_DIRCREATE_FAIL, wszDest, Status));
}
}
else
{
Status = ERROR_SUCCESS;
// First check if it is necessary to copy the file over.
bStatus = GetFileAttributesEx( wszSource, GetFileExInfoStandard, &SourceAttr );
if ( bStatus )
{
bStatus = GetFileAttributesEx( wszDest, GetFileExInfoStandard, &DestAttr );
if (bStatus)
{
if (CompareFileTime( &SourceAttr.ftLastWriteTime, &DestAttr.ftLastWriteTime ) <= 0)
{
// The destination is newer or at least as old as the source.
// There is no need to copy. However, we should delete
// the locally cached copy of the file if any since the
// destination is newer.
if (ERROR_SUCCESS == StatusCSCDel)
StatusCSCDel = DeleteCSCFile ( wszSource, bAllowRdrTimeout);
else
DeleteCSCFile ( wszSource, FALSE);
continue;
}
}
else
{
Status = GetLastError();
if (ERROR_PATH_NOT_FOUND == Status ||
ERROR_FILE_NOT_FOUND == Status)
{
// The destination was not found. So we must proceed with the copy.
bStatus = TRUE;
Status = ERROR_SUCCESS;
}
}
}
else
{
// We failed to get the attributes of the source file.
Status = GetLastError();
}
if (ERROR_SUCCESS == Status)
{
//
// If we are here, we need to copy the file over.
// In order to avoid loss of data, we must first copy the file
// over to a temporary file at the destination and then rename
// the file at the destination. This is because if the network
// connection gets dropped during the filecopy operation, then
// we are left with an incomplete file at the destination.
// If we use the real name on the destination directly, then
// the file will be skipped at the next redirection attempt
// because of our last writer wins algorithm. As a result, when
// the redirection succeeds subsequently, we end up with a loss
// of user data. Copying to a temp name and then renaming the
// file prevents this problem from happening because the rename
// operation is atomic.
//
// First check if we need to generate a new temporary filename
// Note: We try to minimize the number of calls to GetTempFilename
// because it can be a very expensive call as it can result in
// multiple CreateFile calls over the network which can be
// especially slow for EFS shares.
//
bReuseTempName = FALSE;
if (NULL != pwszTempFilename && L'\0' != *pwszTempFilename)
{
dwAttr = GetFileAttributes (pwszTempFilename);
if (INVALID_FILE_ATTRIBUTES == dwAttr)
{
Status = GetLastError();
if (ERROR_PATH_NOT_FOUND == Status ||
ERROR_FILE_NOT_FOUND == Status)
{
Status = ERROR_SUCCESS;
bReuseTempName = TRUE;
}
}
}
if (ERROR_SUCCESS == Status && FALSE == bReuseTempName)
{
// We need to generate a new temporary filename.
if (NULL == pwszTempFilename)
{
pwszTempFilename = new WCHAR [MAX_PATH + 1];
if (NULL == pwszTempFilename)
Status = ERROR_OUTOFMEMORY;
}
if (ERROR_SUCCESS == Status)
{
*pwszTempFilename = 0;
*pwszDestEnd = 0;
bStatus = GetTempFileName(wszDest, TEXT("frd"), 0, pwszTempFilename);
*pwszDestEnd = FindData.cFileName[0];
if (!bStatus)
{
Status = GetLastError();
}
}
}
if (ERROR_SUCCESS == Status)
{
// Now we have a temp. filename and we are ready to copy.
Status = FullFileCopyW (wszSource, pwszTempFilename, FALSE);
if (ERROR_SUCCESS == Status)
{
// Now we rename the file at the destination in one atomic
// step. Note however that if the destination file exists
// and has readonly/hidden/system attributes, the MoveFileEx
// API will fail with ERROR_ACCESS_DENIED. So we slap on normal
// attributes on the file before doing the Move. If we fail,
// we restore the attributes.
dwAttr = GetFileAttributes (wszDest);
// Change attributes only if we managed to figure out the
// actual attributes.
if (INVALID_FILE_ATTRIBUTES != dwAttr)
SetFileAttributes (wszDest, FILE_ATTRIBUTE_NORMAL);
if (!MoveFileEx(pwszTempFilename, wszDest, MOVEFILE_REPLACE_EXISTING))
{
Status = GetLastError();
// Restore the attributes of the destination file.
// Provided we changed those in the first place.
if (INVALID_FILE_ATTRIBUTES != dwAttr)
SetFileAttributes (wszDest, dwAttr);
// Also try to delete the temp file because we might still
// be able to do it. But ignore any failures. We are just
// trying to be nice by removing turds.
DeleteFile(pwszTempFilename);
}
}
}
}
if ( Status != ERROR_SUCCESS )
{
pCopyFailure->RegisterFailure (wszSource, wszDest);
switch (Status)
{
case ERROR_INVALID_SECURITY_DESCR:
DebugMsg((DM_VERBOSE, IDS_SETSECURITY_FAIL, wszSource, wszDest));
break;
default:
DebugMsg((DM_VERBOSE, IDS_FILECOPY_FAIL, wszSource, wszDest, Status));
break;
}
}
}
if ( Status != ERROR_SUCCESS )
break;
} while ( FindNextFile( hFind, &FindData ) );
// Some final cleanup before we return.
FindClose( hFind );
if (pwszTempFilename)
{
delete [] pwszTempFilename;
pwszTempFilename = NULL;
}
return Status;
}
DWORD
CFileDB::DeleteFileTree(
WCHAR * pwszPath,
WCHAR * pwszIgnoredSubdir
)
{
HANDLE hFind;
WIN32_FIND_DATA FindData;
WCHAR * wszSource = NULL;
WCHAR * pwszSourceEnd = 0;
DWORD Status;
BOOL bStatus;
int len;
if (!pwszPath)
return ERROR_PATH_NOT_FOUND;
len = wcslen (pwszPath);
if (!len)
return ERROR_PATH_NOT_FOUND;
wszSource = (WCHAR *) alloca (sizeof (WCHAR) * (len + MAX_PATH + 2));
if (NULL == wszSource)
return ERROR_OUTOFMEMORY;
lstrcpy( wszSource, pwszPath );
pwszSourceEnd = wszSource + lstrlen( wszSource );
if (L'\\' != pwszSourceEnd[-1])
*pwszSourceEnd++ = L'\\';
pwszSourceEnd[0] = L'*';
pwszSourceEnd[1] = 0;
hFind = FindFirstFile( wszSource, &FindData );
if ( INVALID_HANDLE_VALUE == hFind )
return ERROR_SUCCESS;
Status = ERROR_SUCCESS;
for (;;)
{
lstrcpy( pwszSourceEnd, FindData.cFileName );
if ( FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
{
if ( lstrcmp( FindData.cFileName, L"." ) != 0 &&
lstrcmp( FindData.cFileName, L".." ) != 0 &&
(! pwszIgnoredSubdir || lstrcmpi( FindData.cFileName, pwszIgnoredSubdir ) != 0) )
{
SetFileAttributes( wszSource, FILE_ATTRIBUTE_NORMAL );
Status = DeleteFileTree( wszSource, NULL);
if ( ERROR_SUCCESS == Status )
{
if ( ! RemoveDirectory( wszSource ) )
{
Status = GetLastError();
DebugMsg((DM_VERBOSE, IDS_DIRDEL_FAIL, wszSource, Status));
}
}
}
}
else
{
SetFileAttributes( wszSource, FILE_ATTRIBUTE_NORMAL );
bStatus = DeleteFile( wszSource );
if ( ! bStatus )
{
Status = GetLastError();
DebugMsg((DM_VERBOSE, IDS_FILEDEL_FAIL, wszSource, Status));
}
}
if ( Status != ERROR_SUCCESS )
break;
bStatus = FindNextFile( hFind, &FindData );
if ( ! bStatus )
{
Status = GetLastError();
if ( ERROR_NO_MORE_FILES == Status )
Status = ERROR_SUCCESS;
break;
}
}
FindClose( hFind );
return Status;
}
//note: the bCheckOwner flag: if set to true, then if the directory in question
// already exists, the function Fails with an ERROR_INVALID_OWNER if the
// the owner of the existing directory is not the user.
// bSourceValid indicates if there is a valid path in pwszSource
DWORD
CFileDB::CreateRedirectedFolderPath(
const WCHAR * pwszSource,
const WCHAR * pwszDest,
BOOL bSourceValid,
BOOL bCheckOwner,
BOOL bMoveContents
)
{
WCHAR * pwszSlash = 0;
WCHAR * pwszPath = NULL;
DWORD Status = ERROR_SUCCESS;
int len;
WCHAR * pwszSuccess = NULL;
DWORD dwAttributes;
//first make a local copy of the destination path to work with.
//while copying, we actually convert it into an absolute path so
//that we eliminate any problems with weird paths like
//\\server\share\..\hello\..\there
len = wcslen (pwszDest) + 1;
pwszPath = (WCHAR*) alloca (len * sizeof(WCHAR));
if (!pwszPath)
return ERROR_OUTOFMEMORY;
pwszSuccess = _wfullpath (pwszPath, pwszDest, len);
if (!pwszSuccess)
return ERROR_BAD_PATHNAME; //actually _wfullpath rarely fails
//
// Will only accept drive based or UNC based paths.
//
// A redirect path of just <drive>:\ or \\server\share will be accepted
// even though this would be a strange choice for redirection.
//
// IMPORTANT: also see notes at the beginning of the function
if ( L':' == pwszPath[1] && L'\\' == pwszPath[2] )
{
pwszSlash = &pwszPath[2];
}
else if ( L'\\' == pwszPath[0] && L'\\' == pwszPath[1] )
{
pwszSlash = wcschr( &pwszPath[2], L'\\' );
if ( pwszSlash )
{
//watch out for '\' terminated paths
if (L'\0' == pwszSlash[1])
{
pwszSlash = 0;
}
else //it is at least of the form \\server\share
{
pwszSlash = wcschr( &pwszSlash[1], L'\\' );
//if it is of the form \\server\share, then we allow this path
//based depending on the ownership checks if any
if (!pwszSlash)
return bCheckOwner ? IsUserOwner(pwszPath) : ERROR_SUCCESS;
//note: we do not have to watch out for the '\' terminated
//paths here (e.g. \\server\share\) because that will be
//taken care of below : in -> if (!pwszSlash[1])
}
}
}
else
{
pwszSlash = 0;
}
if ( ! pwszSlash )
return ERROR_BAD_PATHNAME;
//if it is the root directory of a drive or root of a UNC share
//we succeed based on ownership checks, if any...
//but before that, we also need to make sure that the specified path
//exists or we may end up redirecting to a non-existent location.
if ( !pwszSlash[1])
{
if (0xFFFFFFFF != (dwAttributes = GetFileAttributes(pwszPath)))
{
//it exists
if (! (FILE_ATTRIBUTE_DIRECTORY & dwAttributes))
{
return ERROR_DIRECTORY;
}
//it exists and is a directory
return bCheckOwner ? IsUserOwner (pwszPath) : ERROR_SUCCESS;
}
else
{
return GetLastError();
}
}
//if we are here, it is not the root of a drive or a share.
//so we might have to do create the destination.
//First do a quick check to see if the path exists already. this
//is not only an optimization, but is also necessary for cases
//where an admin. may want to lock down access to certain folders
//by pre-creating the folders and putting highly resitrictive ACLs on them
//in that case, CreateDirectory (later in the code) would fail with
//ACCESS_DENIED rather than ERROR_ALREADY_EXISTS and the redirection code
//will bail out even though it is not necessary to.
if (0xFFFFFFFF != (dwAttributes = GetFileAttributes(pwszPath)))
{
//it exists
if (! (FILE_ATTRIBUTE_DIRECTORY & dwAttributes))
{
return ERROR_DIRECTORY;
}
//it exists and is a directory
return bCheckOwner ? IsUserOwner (pwszPath) : ERROR_SUCCESS;
}
//the destination has not been pre-created, so we need to do that
//ourselves
do
{
pwszSlash = wcschr( &pwszSlash[1], L'\\' );
// Watch out for '\' terminated paths.
if ( pwszSlash && (L'\0' == pwszSlash[1]) )
pwszSlash = 0;
if ( pwszSlash )
{
*pwszSlash = 0;
CreateDirectory( pwszPath, NULL );
*pwszSlash = L'\\';
//ignore all errors in the intermediate folders not just
//ERROR_ALREADY_EXISTS because of folders like
//\\server\share\dir1\dir2\%username% where the user may not
//have write access in dir1 but might have so in dir2
//if the path is invalid we will either discover it at the last
//dir in the chain or while trying to redirect to the destination
//retaining the code here just in case...
//
/*if ( ! bStatus && (GetLastError() != ERROR_ALREADY_EXISTS) )
return GetLastError();*/
}
else
{
//
// Last dir in the chain. We set security on the last dir in
// the path to only allow the user & system access if
// the directory did not already exist.
//
if (bCheckOwner)
{
Status = CreateFolderWithUserFileSecurity( pwszPath );
}
else
{
Status = ERROR_SUCCESS;
if (!CreateDirectory( pwszPath, NULL ))
Status = GetLastError();
}
if ( ERROR_SUCCESS == Status )
{
//the extension created the directory, so try to set the user as
//the owner explicitly because if a member of the local administrators
//group creates a directory/file, the Administrators group becomes
//the owner by default. This can cause problems with quota accounting
//and also if the settings on the redirection policy are changed
//at a later date. However, since it is not necessary that the
//we will succeed in setting the owner here, we ignore any failures
SetUserAsOwner (pwszPath);
//
// We want to skip the DACL if we want to apply exclusive ACLs
// i.e., bCheckOwner is true. Otherwise, we should just copy
// over all the metadata.
//
if (bSourceValid && bMoveContents)
FullDirCopyW (pwszSource, pwszPath, bCheckOwner);
return ERROR_SUCCESS;
}
else if ( ERROR_ALREADY_EXISTS != Status)
{
return Status;
}
else
{
//the directory already exists
//start anti-spoofing agent
//do the ownership check only on the last dir in chain and only
//if the flags in the ini file tell you to.
if (bCheckOwner &&
(ERROR_SUCCESS != (Status = IsUserOwner(pwszPath))))
{
DebugMsg ((DM_VERBOSE, IDS_ACL_MISMATCH, pwszPath, Status));
return Status;
}
else
return ERROR_SUCCESS;
}
}
} while ( pwszSlash );
return ERROR_SUCCESS;
}
DWORD
CFileDB::SetUserAsOwner(
WCHAR * pwszPath
)
{
SECURITY_DESCRIPTOR SecDesc;
PSID pSidUser = 0;
PTOKEN_USER pTokenUser = 0;
DWORD Size = 0;
DWORD Status = ERROR_SUCCESS;
BOOL bStatus;
if ( ! _hUserToken )
return ERROR_SUCCESS;
for (;;)
{
Status = NtQueryInformationToken(
_hUserToken,
TokenUser,
pTokenUser,
Size,
&Size );
if ( STATUS_BUFFER_TOO_SMALL == Status )
{
pTokenUser = (PTOKEN_USER) alloca( Size );
if ( ! pTokenUser )
return ERROR_OUTOFMEMORY;
continue;
}
break;
}
if ( Status != ERROR_SUCCESS )
return Status;
Size = RtlLengthSid( pTokenUser->User.Sid );
pSidUser = (PSID) alloca( Size );
if ( pSidUser )
Status = RtlCopySid( Size, pSidUser, pTokenUser->User.Sid );
else
Status = ERROR_OUTOFMEMORY;
if ( Status != ERROR_SUCCESS )
return Status;
bStatus = InitializeSecurityDescriptor( &SecDesc, SECURITY_DESCRIPTOR_REVISION );
if ( bStatus )
bStatus = SetSecurityDescriptorOwner (&SecDesc, pSidUser, 0);
if (bStatus)
bStatus = SetFileSecurity( pwszPath, OWNER_SECURITY_INFORMATION, &SecDesc);
if ( ! bStatus )
Status = GetLastError();
return Status;
}
DWORD
CFileDB::CreateFolderWithUserFileSecurity(
WCHAR * pwszPath
)
{
SECURITY_DESCRIPTOR SecDesc;
SID_IDENTIFIER_AUTHORITY AuthorityNT = SECURITY_NT_AUTHORITY;
SID_IDENTIFIER_AUTHORITY AuthWorld = SECURITY_WORLD_SID_AUTHORITY;
PSID pSidUser = 0;
PSID pSidSystem = 0;
PACL pAcl = 0;
ACE_HEADER * pAceHeader;
PTOKEN_USER pTokenUser = 0;
PSID pSid = 0;
DWORD AclSize;
DWORD AceIndex;
DWORD Size;
DWORD Status;
BOOL bStatus;
if ( ! _hUserToken )
return ERROR_SUCCESS;
pSidSystem = 0;
pTokenUser = 0;
Size = 0;
for (;;)
{
Status = NtQueryInformationToken(
_hUserToken,
TokenUser,
pTokenUser,
Size,
&Size );
if ( STATUS_BUFFER_TOO_SMALL == Status )
{
pTokenUser = (PTOKEN_USER) alloca( Size );
if ( ! pTokenUser )
return ERROR_OUTOFMEMORY;
continue;
}
break;
}
if ( Status != ERROR_SUCCESS )
return Status;
Size = RtlLengthSid( pTokenUser->User.Sid );
pSidUser = (PSID) alloca( Size );
if ( pSidUser )
Status = RtlCopySid( Size, pSidUser, pTokenUser->User.Sid );
else
Status = ERROR_OUTOFMEMORY;
if ( Status != ERROR_SUCCESS )
return Status;
bStatus = AllocateAndInitializeSid( &AuthorityNT, 1, SECURITY_LOCAL_SYSTEM_RID, 0, 0, 0, 0, 0, 0, 0, &pSidSystem);
if ( ! bStatus )
{
Status = GetLastError();
goto SetUserFileSecurityEnd;
}
//
// Allocate space for the ACL
//
AclSize = (GetLengthSid(pSidUser)) +
(GetLengthSid(pSidSystem)) +
sizeof(ACL) + (2 * (sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD)));
pAcl = (PACL) alloca( AclSize );
if ( pAcl )
{
bStatus = InitializeAcl( pAcl, AclSize, ACL_REVISION );
if ( ! bStatus )
Status = GetLastError();
}
else
{
Status = ERROR_OUTOFMEMORY;
}
if ( Status != ERROR_SUCCESS )
goto SetUserFileSecurityEnd;
//
// Add Aces for User, System, and Admin. Non-inheritable ACEs first
//
AceIndex = 0;
bStatus = AddAccessAllowedAce(pAcl, ACL_REVISION, FILE_ALL_ACCESS, pSidUser);
if ( bStatus )
{
bStatus = GetAce(pAcl, AceIndex, (void **) &pAceHeader);
if ( bStatus )
pAceHeader->AceFlags |= (OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE);
}
if ( bStatus )
{
AceIndex++;
bStatus = AddAccessAllowedAce(pAcl, ACL_REVISION, FILE_ALL_ACCESS, pSidSystem);
if ( bStatus )
{
bStatus = GetAce(pAcl, AceIndex, (void **) &pAceHeader);
if ( bStatus )
pAceHeader->AceFlags |= (OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE);
}
}
if ( ! bStatus )
{
Status = GetLastError();
goto SetUserFileSecurityEnd;
}
bStatus = InitializeSecurityDescriptor( &SecDesc, SECURITY_DESCRIPTOR_REVISION );
if (bStatus)
SetSecurityDescriptorControl (&SecDesc, SE_DACL_PROTECTED,
SE_DACL_PROTECTED);
//SE_DACL_PROTECTED is supported by NTFS5 but not by NTFS4, therefore
//we ignore any failures in SetSecurityDesciptorControl
if ( bStatus )
bStatus = SetSecurityDescriptorDacl( &SecDesc, TRUE, pAcl, FALSE );
//set the owner explicitly. This is required because if the user is an
//admin, then when the directory is created, the owner is set to the group
//administrators, rather than the user
if ( bStatus )
{
bStatus = SetSecurityDescriptorOwner (&SecDesc, pSidUser, 0);
}
if ( bStatus )
{
SECURITY_ATTRIBUTES sa;
sa.bInheritHandle = FALSE;
sa.lpSecurityDescriptor = &SecDesc;
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
bStatus = CreateDirectory(pwszPath, &sa);
}
if ( ! bStatus )
{
Status = GetLastError();
}
SetUserFileSecurityEnd:
// The user Sid was allocated on the stack, so there is no need to Free it.
if (pSidSystem)
FreeSid( pSidSystem );
return Status;
}
//+--------------------------------------------------------------------------
//
// Member: CFileDB::IsUserOwner
//
// Synopsis: given a path. this function determines if the user is the
// the owner of the file/folder
//
// Arguments: [in] pwszPath
//
// Returns: ERROR_SUCCESS if user is owner
// otherwise an error code
//
// History: 10/6/1998 RahulTh created
//
// Notes:
//
//---------------------------------------------------------------------------
DWORD CFileDB::IsUserOwner (const WCHAR * pwszPath)
{
BOOL bStatus;
DWORD Status;
BOOL bIsMember;
DWORD dwLengthNeeded;
PSECURITY_DESCRIPTOR pSecDesc = 0;
PSID pOwnerSid = 0;
BOOL bDaclDefaulted;
//first check if we are on FAT. if we are on FAT, then we simply let
//succeed since FAT cannot have any ACLs anyway
Status = IsOnNTFS (pwszPath);
if (ERROR_NO_SECURITY_ON_OBJECT == Status)
{
Status = ERROR_SUCCESS; //we are on FAT
goto UserOwnerEnd;
}
else if (ERROR_SUCCESS != Status)
goto UserOwnerEnd; //there was some other error
//else we are on NTFS and ready to rumble!
//get the owner sid from the folder.
for (dwLengthNeeded = 0;;)
{
bStatus = GetFileSecurity (pwszPath, OWNER_SECURITY_INFORMATION,
pSecDesc, dwLengthNeeded,
&dwLengthNeeded);
if (bStatus)
break; //we have the security descriptor. we are free to leave.
//GetFileSecurity failed if we are here
Status = GetLastError();
if (ERROR_INSUFFICIENT_BUFFER != Status)
goto UserOwnerEnd;
//GetFileSecurity failed due to insufficient memory if we are here.
pSecDesc = NULL;
pSecDesc = (PSECURITY_DESCRIPTOR) alloca (dwLengthNeeded);
if (NULL == pSecDesc)
{
Status = ERROR_OUTOFMEMORY;
goto UserOwnerEnd;
}
}
//now get the owner sid
bStatus = GetSecurityDescriptorOwner (
pSecDesc,
&pOwnerSid,
&bDaclDefaulted);
if (!bStatus)
{
Status = GetLastError();
}
else
{
if (!pOwnerSid)
{
Status = ERROR_INVALID_OWNER;
}
else
{
bStatus = CheckTokenMembership (_hUserToken, pOwnerSid, &bIsMember);
if (!bStatus)
Status = GetLastError();
else
{
if (bIsMember)
Status = ERROR_SUCCESS;
else
Status = ERROR_INVALID_OWNER;
}
}
}
UserOwnerEnd:
return Status;
}
//+--------------------------------------------------------------------------
//
// Member: CFileDB::GetEnvBlock
//
// Synopsis: gets a pointer to the user's environment block
//
// Arguments: none.
//
// Returns: pointer to the user's environment block.
// NULL if it is not created yet
//
// History: 9/20/1999 RahulTh created
//
// Notes:
//
//---------------------------------------------------------------------------
PVOID CFileDB::GetEnvBlock (void)
{
return _pEnvBlock;
}
//+--------------------------------------------------------------------------
//
// Member: CFileDB::GetRsopContext
//
// Synopsis: gets a pointer to the rsop logging context
//
// Arguments: none.
//
// Returns: pointer to the rsop logging context -- never null
//
//
// History: 12/8/1999 adamed created
//
// Notes:
//
//---------------------------------------------------------------------------
CRsopContext*
CFileDB::GetRsopContext()
{
return _pRsopContext;
}
//member functions and data for class CSavedSettings
//initialize the class's static variables.
int CSavedSettings::m_idConstructor = 0;
CFileDB * CSavedSettings::m_pFileDB = NULL;
WCHAR * CSavedSettings::m_szSavedSettingsPath = NULL;
//+--------------------------------------------------------------------------
//
// Member: CSavedSettings::ResetStaticMembers
//
// Synopsis: resets the static members 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 CSavedSettings::ResetStaticMembers(void)
{
//
// No need to delete it. This usually points to a local variable in
// ProcessGroupPolicyInternal. So the actual object gets deleted when
// after each processing. We just need to make sure that it is reflected
// here.
//
m_pFileDB = NULL;
if (m_szSavedSettingsPath)
{
delete [] m_szSavedSettingsPath;
m_szSavedSettingsPath = NULL;
}
// No need to do anything about m_idConstructor
}
//+--------------------------------------------------------------------------
//
// Member: CSavedSettings
//
// Synopsis: default constructor for the class.
//
// Arguments:
//
// Returns:
//
// History: 11/18/1998 RahulTh created
//
// Notes:
//
//---------------------------------------------------------------------------
CSavedSettings::CSavedSettings ()
{
m_rID = (REDIRECTABLE) m_idConstructor;
m_idConstructor = (m_idConstructor + 1) % ((int) EndRedirectable);
m_szLastRedirectedPath = NULL;
m_szCurrentPath = NULL;
m_szLastUserName = NULL;
m_szLastHomedir = NULL;
m_bIsHomedirRedir = FALSE;
m_bHomedirChanged = FALSE;
m_dwFlags = REDIR_DONT_CARE; //this is always a safe default
m_psid = 0;
m_bValidGPO = FALSE;
m_bUserNameChanged = FALSE;
m_szGPOName[0] = L'\0';
}
//+--------------------------------------------------------------------------
//
// Member: CSavedSettings::ResetMembers
//
// Synopsis: Resets the member variables.
//
// Arguments: none.
//
// Returns: nothing.
//
// History: 12/17/2000 RahulTh created
//
// Notes: see ResetStaticMembers.
//
//---------------------------------------------------------------------------
void CSavedSettings::ResetMembers(void)
{
if (m_szLastRedirectedPath)
{
delete [] m_szLastRedirectedPath;
m_szLastRedirectedPath = NULL;
}
if (m_szCurrentPath)
{
delete [] m_szCurrentPath;
m_szCurrentPath = NULL;
}
if (m_szLastUserName)
{
delete [] m_szLastUserName;
m_szLastUserName = NULL;
}
if (m_szLastHomedir)
{
delete [] m_szLastHomedir;
m_szLastHomedir = NULL;
}
if (m_psid)
{
RtlFreeSid (m_psid);
m_psid = NULL;
}
m_bHomedirChanged = FALSE;
m_dwFlags = REDIR_DONT_CARE; //this is always a safe default
m_bValidGPO = FALSE;
m_bUserNameChanged = FALSE;
m_szGPOName[0] = L'\0';
// No need to do anything about m_rID.
}
//+--------------------------------------------------------------------------
//
// Member: ~CSavedSettings
//
// Synopsis: default destructor
//
// Arguments:
//
// Returns:
//
// History: 11/18/1998 RahulTh created
//
// Notes:
//
//---------------------------------------------------------------------------
CSavedSettings::~CSavedSettings ()
{
//free static members if necessary
if (m_szSavedSettingsPath)
{
delete [] m_szSavedSettingsPath;
m_szSavedSettingsPath = NULL; //this is necessary so that the
//destructor of the next object does
//not try to free it again
}
//
// Free other memory allocated for members of this object
// and reset them.
//
ResetMembers();
}
//+--------------------------------------------------------------------------
//
// Member: Load
//
// Synopsis: loads the saved settings into the object for its corresponding
// folder
//
// Arguments: none
//
// Returns: ERROR_SUCCESS if successful. otherwise an error code
//
// History: 11/18/1998 RahulTh created
//
// Notes:
//
//---------------------------------------------------------------------------
DWORD CSavedSettings::Load (CFileDB * pFileDB)
{
DWORD Status;
BOOL bStatus;
DWORD len;
//first set the FileDB object if it has not already been set
if (!m_pFileDB)
m_pFileDB = pFileDB;
ASSERT (m_pFileDB);
//get the name of the file where the last settings have been saved
//if we haven't already done so.
if (!m_szSavedSettingsPath)
{
len = wcslen (pFileDB->_pwszLocalPath) +
wcslen (SAVED_SETTINGS_FILE) + 2;
m_szSavedSettingsPath = (WCHAR *) new WCHAR [len];
if (!m_szSavedSettingsPath)
{
Status = ERROR_OUTOFMEMORY;
goto LoadEnd;
}
wcscpy (m_szSavedSettingsPath, pFileDB->_pwszLocalPath);
wcscat (m_szSavedSettingsPath, L"\\");
wcscat (m_szSavedSettingsPath, SAVED_SETTINGS_FILE);
}
//do a quick check to see if the file exists
if (0xFFFFFFFF == GetFileAttributes(m_szSavedSettingsPath))
Status = LoadDefaultLocal ();
else
Status = LoadFromIniFile ();
LoadEnd:
return Status;
}
//+--------------------------------------------------------------------------
//
// Member: GetCurrentPath
//
// Synopsis: gets the current path of the folder.
//
// Arguments: none
//
// Returns: ERROR_SUCCESS if successful
//
// History: 11/18/1998 RahulTh created
//
// Notes:
//
//---------------------------------------------------------------------------
DWORD CSavedSettings::GetCurrentPath (void)
{
DWORD Status = ERROR_SUCCESS;
WCHAR * pwszValueName = 0;
DWORD Size;
WCHAR * pwszProcessedPath = NULL;
if (m_szCurrentPath)
{
delete [] m_szCurrentPath;
m_szCurrentPath = NULL;
}
m_szCurrentPath = new WCHAR [MAX_PATH];
if (!m_szCurrentPath)
{
Status = ERROR_OUTOFMEMORY;
goto GetCurrentPathEnd;
}
m_szCurrentPath[0] = L'\0';
Status = m_pFileDB->GetPathFromFolderName (
g_szRelativePathNames[(int)m_rID],
m_szCurrentPath
);
if (((DWORD) S_OK != Status) && //if SHGetFolderPath failed, use the local userprofile path
ERROR_INVALID_NAME != Status) //the only error from GetPathFromFolderName that is not generated by SHGetFolderPath
{
Status = ERROR_SUCCESS;
wcscpy (m_szCurrentPath, L"%USERPROFILE%\\");
wcscat (m_szCurrentPath, g_szRelativePathNames[(int)m_rID]);
}
else
{
// expand the homedir path if applicable.
if (IsHomedirPath (m_rID, m_szCurrentPath, TRUE))
{
Status = ExpandHomeDir (m_rID, m_szCurrentPath, TRUE, &pwszProcessedPath);
delete [] m_szCurrentPath;
m_szCurrentPath = NULL;
if (ERROR_SUCCESS == Status)
{
m_szCurrentPath = pwszProcessedPath;
pwszProcessedPath = NULL;
}
}
}
if (m_szCurrentPath)
RemoveEndingSlash( m_szCurrentPath );
GetCurrentPathEnd:
return Status;
}
//+--------------------------------------------------------------------------
//
// Member: ResetLastUserName
//
// Synopsis: sets the last user name to the current username
//
// Arguments: none.
//
// Returns: ERROR_SUCCESS : if there is no error.
// an error code otherwise.
//
// History: 9/15/1999 RahulTh created
//
// Notes: the most likely cause of failure for this function -- if at all
// it happens, will be an out of memory condition.
//
//---------------------------------------------------------------------------
DWORD CSavedSettings::ResetLastUserName (void)
{
if (m_szLastUserName)
{
delete [] m_szLastUserName;
m_szLastUserName = NULL;
}
m_szLastUserName = new WCHAR [wcslen(gwszUserName) + 1];
if (!m_szLastUserName)
return ERROR_OUTOFMEMORY;
wcscpy (m_szLastUserName, gwszUserName);
m_bUserNameChanged = FALSE;
return ERROR_SUCCESS;
}
//+--------------------------------------------------------------------------
//
// Member: LoadDefaultLocal
//
// Synopsis: loads the default local userprofile path and default
// flags into the object
//
// Arguments: none
//
// Returns: ERROR_SUCCESS if successful an error code otherwise
//
// History: 11/18/1998 RahulTh created
//
// Notes: the security group is set to Everyone as you can never
// cease being a member of the group
//
// this function is usually invoked when one can't find
// the ini file that contains the last saved settings or
// if the ini file does not contain the corresponding section
//
//---------------------------------------------------------------------------
DWORD CSavedSettings::LoadDefaultLocal (void)
{
DWORD Status = ERROR_SUCCESS;
DWORD len;
// The default case cannot be homedir redirection.
// Just make sure that our bool has been properly set.
m_bIsHomedirRedir = FALSE;
//set the default flags
m_dwFlags = REDIR_DONT_CARE;
//to be on the safe side -- set the default values for all members.
m_bValidGPO = FALSE;
m_szGPOName[0] = L'\0';
//set the last username to be the same as the current username
//since we load the defaults only when we do not have any data about the
//last logon (i.e., no per user per machine FR cache
Status = ResetLastUserName ();
if (ERROR_SUCCESS != Status)
goto LoadDefaultsEnd;
//allocate the sid
//to be on the safe side, free the sid if it has already been allocated
if (m_psid)
{
RtlFreeSid (m_psid);
m_psid = NULL;
}
Status = AllocateAndInitSidFromString (L"S-1-1-0", &m_psid);
if (ERROR_SUCCESS != Status)
{
m_psid = 0;
goto LoadDefaultsEnd;
}
//get the last redirected path
//again, to be on the safe side, free any used memory first
if (m_szLastRedirectedPath)
{
delete [] m_szLastRedirectedPath;
m_szLastRedirectedPath = NULL;
}
len = wcslen (L"%USERPROFILE%\\") +
wcslen (g_szRelativePathNames[(int)m_rID]) + 1;
m_szLastRedirectedPath = new WCHAR [len];
if (!m_szLastRedirectedPath)
{
Status = ERROR_OUTOFMEMORY;
goto LoadDefaultsEnd;
}
wcscpy (m_szLastRedirectedPath, L"%USERPROFILE%\\");
wcscat (m_szLastRedirectedPath, g_szRelativePathNames[(int)m_rID]);
//get the current path
Status = GetCurrentPath();
LoadDefaultsEnd:
return Status;
}
//+--------------------------------------------------------------------------
//
// Member: LoadFromIniFile
//
// Synopsis: this function loads redirection info. from the ini file
// that contains the last saved settings.
// file.
//
// Arguments: none
//
// Returns: ERROR_SUCCESS if everything is successful. an error code otherwise
//
// History: 11/18/1998 RahulTh created
//
// Notes: if this function cannot find the corresponding section in the
// ini file, it loads the default local path
//
//---------------------------------------------------------------------------
DWORD CSavedSettings::LoadFromIniFile (void)
{
DWORD Status = ERROR_SUCCESS;
WCHAR pwszDefault[] = L"*";
WCHAR * pwszReturnedString = NULL;
WCHAR * pwszProcessedPath = NULL;
DWORD retVal;
DWORD Size;
UNICODE_STRING StringW;
GUID GPOGuid;
//
// If this object contains the MyDocs settings, get the last value
// of homedir
//
if (MyDocs == m_rID || MyPics == m_rID)
{
Status = SafeGetPrivateProfileStringW (
g_szDisplayNames [(int) m_rID],
L"Homedir",
pwszDefault,
&m_szLastHomedir,
&Size,
m_szSavedSettingsPath);
if (ERROR_SUCCESS != Status)
goto LoadIniEnd;
if (L'*' == *m_szLastHomedir)
{
// The value of homedir at the last redirection was not found.
delete [] m_szLastHomedir;
m_szLastHomedir = NULL;
}
}
//first try to get the user name when the user had last logged on.
Status = SafeGetPrivateProfileStringW (
g_szDisplayNames [(int) m_rID],
L"Username",
pwszDefault,
&m_szLastUserName,
&Size,
m_szSavedSettingsPath);
if (ERROR_SUCCESS != Status)
goto LoadIniEnd;
if (L'*' == *m_szLastUserName)
{
//the username field was not found in the ini file. must be an older
//cache. since we do not have the information, we just use the defaults,
//i.e. set the current user name as the last user name
Status = ResetLastUserName();
if (ERROR_SUCCESS != Status)
goto LoadIniEnd;
}
else
{
//we found a user name in the local cache.
//update the member variable which indicates whether the user name
//has changed since the last logon.
if (0 == _wcsicmp (m_szLastUserName, gwszUserName))
m_bUserNameChanged = FALSE;
else
m_bUserNameChanged = TRUE;
}
//then try to get the path
Status = SafeGetPrivateProfileStringW (
g_szDisplayNames [(int) m_rID],
L"Path",
pwszDefault,
&m_szLastRedirectedPath,
&Size,
m_szSavedSettingsPath);
if (ERROR_SUCCESS != Status)
goto LoadIniEnd;
if (L'*' == *m_szLastRedirectedPath) //we could not find the required data.
{
//so we go with the defaults
Status = LoadDefaultLocal();
goto LoadIniEnd;
}
else if (IsHomedirPath (m_rID, m_szLastRedirectedPath, TRUE))
{
Status = ExpandHomeDir (m_rID,
m_szLastRedirectedPath,
TRUE,
&pwszProcessedPath,
m_szLastHomedir
);
delete [] m_szLastRedirectedPath;
if (ERROR_SUCCESS != Status)
{
m_szLastRedirectedPath = NULL;
goto LoadIniEnd;
}
else
{
m_bIsHomedirRedir = TRUE;
m_szLastRedirectedPath = pwszProcessedPath;
pwszProcessedPath = NULL;
}
}
//next try to get the security group
Status = SafeGetPrivateProfileStringW (
g_szDisplayNames[(int)m_rID],
L"Group",
pwszDefault,
&pwszReturnedString,
&Size,
m_szSavedSettingsPath
);
if (ERROR_SUCCESS != Status)
goto LoadIniEnd;
if (L'*' == *pwszReturnedString)
{
//data was missing, so go with the defaults
Status = LoadDefaultLocal();
goto LoadIniEnd;
}
if (m_psid)
{
RtlFreeSid (m_psid);
m_psid = NULL;
}
Status = AllocateAndInitSidFromString (pwszReturnedString, &m_psid);
if (ERROR_SUCCESS != Status)
{
m_psid = 0;
goto LoadIniEnd;
}
//now get the flags
Status = SafeGetPrivateProfileStringW (
g_szDisplayNames[(int)m_rID],
L"Flags",
pwszDefault,
&pwszReturnedString,
&Size,
m_szSavedSettingsPath
);
if (ERROR_SUCCESS != Status)
goto LoadIniEnd;
if (L'*' == *pwszReturnedString)
{
Status = LoadDefaultLocal();
goto LoadIniEnd;
}
//now grab the hex flags
StringW.Buffer = pwszReturnedString;
StringW.Length = wcslen (pwszReturnedString) * sizeof (WCHAR);
StringW.MaximumLength = StringW.Length + sizeof(WCHAR);
RtlUnicodeStringToInteger( &StringW, 16, &m_dwFlags );
//now get the unique name of the GPO (if any) that was used to redirect the folder
Status = SafeGetPrivateProfileStringW (
g_szDisplayNames[(int) m_rID],
L"GPO",
pwszDefault,
&pwszReturnedString,
&Size,
m_szSavedSettingsPath
);
if (ERROR_SUCCESS != Status)
goto LoadIniEnd;
StringW.Length = sizeof (WCHAR) * wcslen (pwszReturnedString);
StringW.MaximumLength = (USHORT)(sizeof(WCHAR) * Size);
StringW.Buffer = pwszReturnedString;
if ((L'*' == *pwszReturnedString) || //there is no valid GPO info., or
(sizeof(m_szGPOName) <= wcslen(pwszReturnedString)) || //the file has been corrupted. the length of a valid unique name cannot exceed the size of m_szGPO, or
(STATUS_INVALID_PARAMETER == RtlGUIDFromString (&StringW, &GPOGuid)) //it is not a valid GUID
)
{
//there is no valid GPO info.
m_bValidGPO = FALSE;
m_szGPOName[0] = L'\0';
}
else
{
m_bValidGPO = TRUE;
wcscpy (m_szGPOName, pwszReturnedString);
}
//get the current path.
Status = GetCurrentPath ();
LoadIniEnd:
if (pwszReturnedString)
delete [] pwszReturnedString;
if (pwszProcessedPath)
delete [] pwszProcessedPath;
return Status;
}
//+--------------------------------------------------------------------------
//
// Member: NeedsProcessing
//
// Synopsis: once the saved settings and the registry values have been
// loaded, this function determines if we need to look at all the
// policies. Note that this assumes that the GPO_NOCHANGES flag
// has already been set by the policy engine
//
// Arguments: none
//
// Returns: TRUE / FALSE
//
// History: 11/18/1998 RahulTh created
//
// Notes:
//
//---------------------------------------------------------------------------
BOOL CSavedSettings::NeedsProcessing (void)
{
BOOL bStatus;
DWORD i;
DWORD Status;
PTOKEN_GROUPS pGroups;
WCHAR wszExpandedPath [TARGETPATHLIMIT + 1];
UNICODE_STRING Path;
UNICODE_STRING ExpandedPath;
const WCHAR * pwszCurrentHomedir = NULL;
WCHAR wszProcessedSource [TARGETPATHLIMIT + 1];
WCHAR wszProcessedDest [TARGETPATHLIMIT + 1];
int len;
//if policy didn't care about the location of the folder at last logon
//then even if the last redirected path is not the same as the current
//path, it should not mean that that we need to reprocess the policy
//when the GPO_NOCHANGES flag has been provided
if (m_dwFlags & REDIR_DONT_CARE)
return FALSE;
//if we are here, policy specified the location of the folder at the
//the last logon. make sure that the user name has not changed since
//last logon. If so, we need to process the policies again and move
//the user's folders accordingly.
if (m_bUserNameChanged)
return TRUE;
//
// If we are here, the username had not changed, but if the homedir
// changed, that can affect the path.
// Note: Here, we cannot use the IsHomedirPath function to determine
// if this is a homedir redirection since we would have already expanded
// the path in LoadFromIniFile. Therefore, we must use m_bIsHomedirRedir
//
if (m_bIsHomedirRedir)
{
//
// Note: GetHomeDir is an expensive call since it can result
// in a call to the DS to get the user's home directory. So we try to
// be as lazy as possible about executing it.
//
pwszCurrentHomedir = gUserInfo.GetHomeDir (Status);
if (ERROR_SUCCESS != Status ||
! pwszCurrentHomedir ||
! m_szLastHomedir ||
0 != lstrcmpi (m_szLastHomedir, pwszCurrentHomedir))
{
m_bHomedirChanged = TRUE;
return TRUE;
}
}
//check if the last redirected path and current path are identical
if (0 != _wcsicmp(m_szLastRedirectedPath, m_szCurrentPath))
{
//the paths are different. we need to do processing
//even if the policy engine thinks otherwise
//but sometimes we may have an expanded path in m_szCurrentPath
//e.g. if some User Shell Folder values are missing. So expand
//the last redirected path and compare it with the current path
Path.Length = (wcslen (m_szLastRedirectedPath) + 1) * sizeof (WCHAR);
Path.MaximumLength = sizeof (m_szLastRedirectedPath);
Path.Buffer = m_szLastRedirectedPath;
ExpandedPath.Length = 0;
ExpandedPath.MaximumLength = sizeof (wszExpandedPath);
ExpandedPath.Buffer = wszExpandedPath;
Status = RtlExpandEnvironmentStrings_U (
m_pFileDB->_pEnvBlock,
&Path,
&ExpandedPath,
NULL
);
if (ERROR_SUCCESS != Status)
return TRUE; //that's our best bet in case of failure
//
// Now process the paths so that they do not contain any redundant
// slashes etc.
//
if (NULL == _wfullpath (wszProcessedSource, m_szCurrentPath, MAX_PATH))
{
return TRUE;
}
else
{
//
// Eliminate any trailing slashes. Note: after going through
// _wfullpath, there can be at the most one trailing slash
//
len = lstrlen (wszProcessedSource);
if (L'\\' == wszProcessedSource[len-1])
wszProcessedSource[len - 1] = L'\0';
}
if (NULL == _wfullpath (wszProcessedDest, wszExpandedPath, MAX_PATH))
{
return TRUE;
}
else
{
//
// Eliminate any trailing slashes. Note: after going through
// _wfullpath, there can be at the most one trailing slash
//
len = lstrlen (wszProcessedDest);
if (L'\\' == wszProcessedDest[len-1])
wszProcessedDest[len - 1] = L'\0';
}
// Now that we have the nice compact paths, we compare them.
if (0 != _wcsicmp (wszProcessedSource, wszProcessedDest))
return TRUE;
}
return FALSE;
}
//+--------------------------------------------------------------------------
//
// Member: Save
//
// Synopsis: saves settings back to the local settings
//
// Arguments: [in] pwszPath : the path to which the folder was redirected
// [in] dwFlags : the flags that were used for redirection
// [in] pSid : the Sid of the group that was used for
// redirection. If this is NULL, then we
// default to the Sid for everyone : S-1-1-0
//
// Returns: ERROR_SUCCESS if everything was successful or an error code.
//
// History: 11/20/1998 RahulTh created
//
// Notes:
//
//---------------------------------------------------------------------------
DWORD CSavedSettings::Save (const WCHAR * pwszPath, DWORD dwFlags, PSID pSid, const WCHAR * pszGPOName)
{
WCHAR * pwszSection = NULL;
UNICODE_STRING StringW;
WCHAR pwszFlags [ MAX_PATH ];
UINT len;
int homedirInfoLen = 0;
WCHAR * pszCurr;
WCHAR pwszSid [ MAX_PATH ];
DWORD Status;
BOOL bStatus;
ULONG ulUserNameInfoLen;
const WCHAR * wszHomedir = NULL;
BOOL bSaveHomedir = FALSE;
//
// First determine if the homedir value needs to be saved.
// We need to do this first so that we can add the necessary value to the
// buffer.
if (IsHomedirPath (m_rID, pwszPath, TRUE) ||
IsHomedirPolicyPath (m_rID, pwszPath, TRUE))
{
wszHomedir = gUserInfo.GetHomeDir (Status);
if (ERROR_SUCCESS != Status ||
! wszHomedir ||
L'\0' == *wszHomedir)
{
Status = (ERROR_SUCCESS == Status) ? ERROR_BAD_PATHNAME : Status;
goto SaveSettings_End;
}
// If we are here, then we need to save the homedir value
bSaveHomedir = TRUE;
homedirInfoLen = wcslen (wszHomedir) + 9; // 8 chars for Homedir=
// + 1 for the terminating NULL
}
//caclulate the # of characters required to store UserName=<username>
//including the terminating NULL character.
ulUserNameInfoLen = wcslen (gwszUserName) + 10; //9 chars for Username=
//+ 1 terminating NULL
//don't need to calculate the exact length, this will be more than enough
pwszSection = new WCHAR[sizeof(WCHAR) * ((len = wcslen(pwszPath)) +
homedirInfoLen +
2 * MAX_PATH +
ulUserNameInfoLen +
50)];
if (NULL == pwszSection)
{
Status = ERROR_OUTOFMEMORY;
goto SaveSettings_End;
}
pwszFlags[0] = L'\0';
StringW.Length = 0;
StringW.MaximumLength = sizeof (pwszFlags);
StringW.Buffer = pwszFlags;
RtlIntegerToUnicodeString (dwFlags, 16, &StringW);
wcscpy (pwszSection, L"Username=");
wcscat (pwszSection, gwszUserName);
pszCurr = pwszSection + ulUserNameInfoLen;
// Save the homedir value only if the redirection destination is the homedir.
if (bSaveHomedir)
{
wcscpy (pszCurr, L"Homedir=");
wcscat (pszCurr, wszHomedir);
pszCurr += homedirInfoLen;
}
wcscpy (pszCurr, L"Path=");
if (IsHomedirPolicyPath (m_rID, pwszPath, TRUE))
{
wcscat (pszCurr, &pwszPath[2]);
pszCurr += 6 + (len - 2);
}
else
{
wcscat (pszCurr, pwszPath);
pszCurr += 6 + len;
}
wcscpy (pszCurr, L"Flags=");
wcscat (pszCurr, pwszFlags);
pszCurr += (7 + StringW.Length/sizeof(WCHAR));
wcscpy (pszCurr, L"Group=");
//now we set the sid to everyone (that is the safest) if no sid has been
//specified or if we are unable to convert the supplied sid into a string.
Status = ERROR_INVALID_SID; //just some error code
if (pSid)
{
pwszSid [0] = L'\0';
StringW.Length = 0;
StringW.MaximumLength = sizeof (pwszSid);
StringW.Buffer = pwszSid;
Status = RtlConvertSidToUnicodeString (&StringW, pSid, FALSE);
}
if (ERROR_SUCCESS != Status)
wcscpy (pwszSid, L"S-1-1-0"); //use the sid for everyone if we can't find anything else
wcscat (pszCurr, pwszSid);
//add an extra terminating NULL
pszCurr += (7 + wcslen (pwszSid));
//add the GPO if there is a valid one.
if (pszGPOName)
{
wcscpy (pszCurr, L"GPO=");
wcscat (pszCurr, pszGPOName);
pszCurr += (5 + wcslen (pszGPOName));
}
//add an extra null character at the end.
*pszCurr = L'\0';
//before writing to the ini file, we must pre-create it in Unicode,
//otherwise, the WritePrivate* APIs will write the file in ANSI, which
//will break folder redirection in international/ML builds.
PrecreateUnicodeIniFile (m_szSavedSettingsPath);
Status = ERROR_SUCCESS;
//now we can go ahead and save the section
//first empty the section
bStatus = WritePrivateProfileSection (
g_szDisplayNames[(int) m_rID],
NULL,
m_szSavedSettingsPath
);
//now write the actual section
if (bStatus)
bStatus = WritePrivateProfileSection (
g_szDisplayNames[(int) m_rID],
pwszSection,
m_szSavedSettingsPath
);
if (!bStatus)
Status = GetLastError();
SaveSettings_End:
if (pwszSection)
delete [] pwszSection;
return Status;
}
//+--------------------------------------------------------------------------
//
// Member: CSavedSettings::HandleUserNameChange
//
// Synopsis: this function handles any changes in the user name that
// have occurred since the last logon. in case the username has
// changed since the last logon, this function renames any
// folders redirected earlier using the %username% variable so
// that the path that the redirected folder points to is continues
// to be valid.
//
// Arguments: [in] pFileDB : point to the CFileDB object.
//
// Returns: ERROR_SUCCESS if everything worked properly
// an error code otherwise.
//
// History: 9/20/1999 RahulTh created
//
// Notes: a failed rename is not considered a failure. in this case,
// an event is logged and the code ensures that redirection
// is not attempted if there is a simultaneous change in the
// redirection policies.
//
// in this way a failed rename for one folder does not hold up
// the redirection of other folders which might be independent of
// this particular folder.
//---------------------------------------------------------------------------
DWORD CSavedSettings::HandleUserNameChange (CFileDB * pFileDB,
CRedirectInfo * pRedir)
{
BOOL bStatus;
DWORD Status = ERROR_SUCCESS;
DWORD RedirStatus = ERROR_SUCCESS;
WCHAR wszLastPath [TARGETPATHLIMIT];
WCHAR wszExpandedSource [TARGETPATHLIMIT];
WCHAR wszExpandedDest [TARGETPATHLIMIT];
WCHAR wszRenamePart [TARGETPATHLIMIT];
WCHAR wszExpandedRenameSource [TARGETPATHLIMIT];
WCHAR wszExpandedRenameDest [TARGETPATHLIMIT];
WCHAR * wszTemp = NULL;
WCHAR * wszEnd = NULL;
SHARESTATUS SourceStatus;
SHARESTATUS DestStatus;
BOOL bRenamePerformed = FALSE;
if ((! m_bUserNameChanged) ||
pRedir->WasRedirectionAttempted() ||
(REDIR_DONT_CARE & m_dwFlags))
{
goto HandleUPNChangeEnd;//nothing to do if the username has not changed
//since the last logon or if the rename has
//already been attempted. Even if the attempted
//redirection had failed, it is not fatal.
//similarly, if policy didn't set the location
//of the folder last time, we don't care.
}
if (Programs == m_rID || //since programs and startup always follow the
Startup == m_rID //Start Menu, rename of Start Menu handles these
//folders automatically
)
{
goto HandleUPNChangeEnd;
}
//show additional status messages if the verbose status is on.
DisplayStatusMessage (IDS_REDIR_CALLBACK);
DebugMsg ((DM_VERBOSE, IDS_UPN_CHANGE, pRedir->GetLocalizedName(), m_szLastUserName, gwszUserName));
//okay, so there is a change in the user name and policy did care about the
//the location. check if last path contained the username. if not, we have
//nothing more to do.
if (TARGETPATHLIMIT <= wcslen (m_szLastRedirectedPath))
{
gpEvents->Report (
EVENT_FDEPLOY_DESTPATH_TOO_LONG,
3,
pRedir->GetLocalizedName(),
m_szLastRedirectedPath,
NumberToString ( TARGETPATHLIMIT )
);
pRedir->PreventRedirection (STATUS_BUFFER_TOO_SMALL);
goto HandleUPNChangeEnd;
}
wcscpy (wszLastPath, m_szLastRedirectedPath);
_wcslwr (wszLastPath);
wszTemp = wcsstr (wszLastPath, L"%username%");
if (NULL == wszTemp)
goto HandleUPNChangeEnd; //there is no %username% string, we are
//done. no rename is required.
//get the part that needs to be renamed.
wcscpy (wszRenamePart, wszLastPath);
wszEnd = wcschr (wszRenamePart + (wszTemp - wszLastPath), L'\\');
if (wszEnd)
*wszEnd = L'\0';
//get expanded versions of the paths -- using the new username and the old
//username
wszTemp = wszLastPath;
RedirStatus = ExpandPathSpecial (pFileDB, wszLastPath, m_szLastUserName, wszExpandedSource);
if (ERROR_SUCCESS == RedirStatus)
{
RedirStatus = ExpandPathSpecial (pFileDB, wszLastPath, gwszUserName, wszExpandedDest);
}
if (ERROR_SUCCESS == RedirStatus)
{
wszTemp = wszRenamePart;
RedirStatus = ExpandPathSpecial (pFileDB, wszRenamePart, m_szLastUserName, wszExpandedRenameSource);
}
if (ERROR_SUCCESS == RedirStatus)
{
RedirStatus = ExpandPathSpecial (pFileDB, wszRenamePart, gwszUserName, wszExpandedRenameDest);
}
if (STATUS_BUFFER_TOO_SMALL == RedirStatus)
{
gpEvents->Report (
EVENT_FDEPLOY_DESTPATH_TOO_LONG,
3,
pRedir->GetLocalizedName(),
wszTemp,
NumberToString ( TARGETPATHLIMIT )
);
pRedir->PreventRedirection (STATUS_BUFFER_TOO_SMALL);
goto HandleUPNChangeEnd;
}
else if (ERROR_SUCCESS != RedirStatus)
{
gpEvents->Report (
EVENT_FDEPLOY_FOLDER_EXPAND_FAIL,
2,
pRedir->GetLocalizedName(),
StatusToString ( RedirStatus )
);
pRedir->PreventRedirection (RedirStatus);
goto HandleUPNChangeEnd;
}
//get the online/offline status of the shares.
SourceStatus = GetCSCStatus (wszExpandedRenameSource);
DestStatus = GetCSCStatus (wszExpandedRenameDest);
if (ShareOffline == Status || ShareOffline == DestStatus)
{
gpEvents->Report (
EVENT_FDEPLOY_FOLDER_OFFLINE,
3,
pRedir->GetLocalizedName(),
wszExpandedSource,
wszExpandedDest
);
pRedir->PreventRedirection (ERROR_CSCSHARE_OFFLINE);
goto HandleUPNChangeEnd;
}
//we are finally ready to rename. first make sure that the source exists.
//sometimes an earlier rename operation might have already renamed this
//folder
RedirStatus = ERROR_SUCCESS;
if (0xFFFFFFFF == GetFileAttributes(wszExpandedRenameSource))
{
RedirStatus = GetLastError();
}
if (ERROR_FILE_NOT_FOUND != RedirStatus)
{
bStatus = MoveFile (wszExpandedRenameSource, wszExpandedRenameDest);
if (!bStatus)
{
RedirStatus = GetLastError();
gpEvents->Report (
EVENT_FDEPLOY_REDIRECT_FAIL,
4,
pRedir->GetLocalizedName(),
StatusToString (RedirStatus),
wszExpandedSource,
wszExpandedDest
);
pRedir->PreventRedirection (RedirStatus);
goto HandleUPNChangeEnd;
}
}
//the rename was successful. now rename the CSC cache.
if (ShareOnline == SourceStatus)
{
MoveDirInCSC (wszExpandedSource, wszExpandedDest, NULL, SourceStatus, DestStatus, TRUE, TRUE);
DeleteCSCShareIfEmpty (wszExpandedSource, SourceStatus);
}
bRenamePerformed = TRUE;
HandleUPNChangeEnd:
if (m_bUserNameChanged && ERROR_SUCCESS == pRedir->GetRedirStatus())
{
UpdateUserNameInCache();
if (bRenamePerformed)
{
gpEvents->Report(
EVENT_FDEPLOY_FOLDER_REDIRECT,
3,
pRedir->GetLocalizedName(),
wszExpandedSource,
wszExpandedDest);
}
}
return Status;
}
//+--------------------------------------------------------------------------
//
// Member: CSavedSettings::UpdateUserNameInCache
//
// Synopsis: updates the user name in the cache with the new username
//
// Arguments: none.
//
// Returns: ERROR_SUCCESS : if successful.
// an error code otherwise.
//
// History: 9/20/1999 RahulTh created
//
// Notes:
//
//---------------------------------------------------------------------------
DWORD CSavedSettings::UpdateUserNameInCache (void)
{
BOOL bStatus;
bStatus = WritePrivateProfileString (
g_szDisplayNames[(int) m_rID],
L"UserName",
gwszUserName,
m_szSavedSettingsPath
);
if (!bStatus)
return GetLastError();
return ERROR_SUCCESS;
}