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.
 
 
 
 
 
 

584 lines
15 KiB

/*
* Copyright (c) 1998 Microsoft Corporation
*
* Module Name:
*
* dir.cpp
*
* Abstract:
*
* This file contains code to recursively create directories.
*
* Author:
*
* Breen Hagan (BreenH) Oct-02-98
*
* Environment:
*
* User Mode
*/
#include "stdafx.h"
#include "logfile.h"
#include <sddl.h>
#include "Aclapi.h"
#include "Accctrl.h"
/*
* Global variables.
*/
TCHAR gszDatabaseDirectory[MAX_PATH + 1] =
_T("%SystemRoot%\\System32\\LServer");
/*
* Helper Functions.
*/
DWORD
CreateDirectoryRecursively(
IN LPCTSTR pszDirectory
)
{
TCHAR Buffer[MAX_PATH + 1];
PTCHAR p,q;
BOOL fDone, br;
DWORD dwErr;
SECURITY_ATTRIBUTES SA;
SECURITY_INFORMATION securityInfo;
PSECURITY_DESCRIPTOR pSD = NULL;
PACL pOldACL = NULL;
ACL_SIZE_INFORMATION asiAclSize;
DWORD dwBufLength=sizeof(asiAclSize);
ACCESS_ALLOWED_ACE *paaAllowedAce;
DWORD dwAcl_i;
if (_tcslen(pszDirectory) > (MAX_PATH)) {
return(ERROR_BAD_PATHNAME);
}
if (ExpandEnvironmentStrings(pszDirectory, Buffer, MAX_PATH) > MAX_PATH) {
return(ERROR_BAD_PATHNAME);
}
//
// This is a string security descriptor. Look up "Security Descriptor
// Definition Language" in MSDN for more details.
//
// This one says:
//
// D: <we are creating a DACL>
// (A; <Allow ACE>
// OICI; <Perform object and container inheritance, i.e., let files and
// directories under this one have these attributes>
// GA <Generic All Access--Full Control>
// ;;;SY) <SYSTEM>
// (A;OICI;GA;;;BA) <same for Builtin Administrators group>
// (A;OICI;GA;;;CO) <same for creator/owner>
// (A;OICI;GRGWGXDTSDCCLC;;;PU) <read access for Power Users>
//
// We'll use it below to create our directory with the right permissions.
TCHAR* pwszSD = _TEXT("D:(A;OICI;GA;;;SY)(A;OICI;GA;;;BA)(A;OICI;GA;;;CO)(A;OICI;GRGWGXDTSDCCLC;;;PU)");
SA.nLength = sizeof(SECURITY_ATTRIBUTES);
SA.bInheritHandle = FALSE;
SA.lpSecurityDescriptor = NULL;
br = ConvertStringSecurityDescriptorToSecurityDescriptor(pwszSD,
SDDL_REVISION_1, &(SA.lpSecurityDescriptor), NULL);
if (br == 0) {
dwErr = GetLastError();
goto cleanup;
}
q = Buffer;
if (q[1] == _T(':')) {
//
// This is a "C:" style path. Put p past the colon and first
// backslash, if it exists.
//
if (q[2] == _T('\\')) {
p = &(q[3]);
} else {
p = &(q[2]);
}
} else if (q[0] == _T('\\')) {
//
// This path begins with a backslash. If the second character is
// also a backslash, this is a UNC path, which is not accepted.
//
if (q[1] == _T('\\')) {
return(ERROR_BAD_PATHNAME);
} else {
p = &(q[1]);
}
} else {
//
// This path is a relative path from the current directory.
//
p = q;
}
q = p;
fDone = FALSE;
do {
//
// Locate the next path sep char. If there is none then
// this is the deepest level of the path.
//
p = _tcschr(q, _T('\\'));
if (p) {
*p = (TCHAR)NULL;
} else {
fDone = TRUE;
}
if(fDone == TRUE)
{
BOOL bInherit = TRUE;
//
// Create this portion of the path.
//
if(CreateDirectory(Buffer,&SA)) {
dwErr = NO_ERROR;
}
else
{
dwErr = GetLastError();
if(dwErr == ERROR_ALREADY_EXISTS) {
dwErr = NO_ERROR;
if( GetNamedSecurityInfoW( (LPWSTR)Buffer,
SE_FILE_OBJECT,
DACL_SECURITY_INFORMATION,
NULL, // psidOwner
NULL, // psidGroup
&pOldACL, // pDacl
NULL, // pSacl
&pSD ) != ERROR_SUCCESS)
{
dwErr = GetLastError();
goto cleanup;
}
if(pOldACL == NULL)
{
goto cleanup;
}
if (GetAclInformation(pOldACL,
(LPVOID)&asiAclSize,
(DWORD)dwBufLength,
(ACL_INFORMATION_CLASS)AclSizeInformation))
{
for (dwAcl_i = 0; dwAcl_i < asiAclSize.AceCount; dwAcl_i++)
{
if(GetAce( pOldACL, dwAcl_i, (LPVOID *)&paaAllowedAce))
{
if(!(paaAllowedAce->Header.AceFlags & INHERITED_ACE))
{
//some permission already exist, we don't need to
//do anything (even if it is a different permission!)
bInherit = FALSE;
break;
}
}
}
}
if (bInherit)
{
// If the database had default security => inheritance from parent only then, set the security.
// No changes in case of custom security.
securityInfo = DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION;
SetFileSecurity(Buffer, securityInfo, SA.lpSecurityDescriptor);
}
}
}
}
else
{
if(CreateDirectory(Buffer, NULL)) {
dwErr = NO_ERROR;
}
else {
dwErr = GetLastError();
if(dwErr == ERROR_ALREADY_EXISTS) {
dwErr = NO_ERROR;
}
}
}
if(dwErr == NO_ERROR) {
//
// Put back the path sep and move to the next component.
//
if (!fDone) {
*p = TEXT('\\');
q = p + sizeof(TCHAR);
}
} else {
fDone = TRUE;
}
} while(!fDone);
cleanup:
LocalFree(SA.lpSecurityDescriptor);
SA.lpSecurityDescriptor = NULL;
return(dwErr);
}
BOOL
ConcatenatePaths(
IN OUT LPTSTR Target,
IN LPCTSTR Path,
IN UINT TargetBufferSize,
OUT LPUINT RequiredSize OPTIONAL
)
{
UINT TargetLength,PathLength;
BOOL TrailingBackslash,LeadingBackslash;
UINT EndingLength;
TargetLength = lstrlen(Target);
PathLength = lstrlen(Path);
//
// See whether the target has a trailing backslash.
//
if(TargetLength && (Target[TargetLength-1] == TEXT('\\'))) {
TrailingBackslash = TRUE;
TargetLength--;
} else {
TrailingBackslash = FALSE;
}
//
// See whether the path has a leading backshash.
//
if(Path[0] == TEXT('\\')) {
LeadingBackslash = TRUE;
PathLength--;
} else {
LeadingBackslash = FALSE;
}
//
// Calculate the ending length, which is equal to the sum of
// the length of the two strings modulo leading/trailing
// backslashes, plus one path separator, plus a nul.
//
EndingLength = TargetLength + PathLength + 2;
if(RequiredSize) {
*RequiredSize = EndingLength;
}
if(!LeadingBackslash && (TargetLength < TargetBufferSize)) {
Target[TargetLength++] = TEXT('\\');
}
if(TargetBufferSize > TargetLength) {
lstrcpyn(Target+TargetLength,Path,TargetBufferSize-TargetLength);
}
//
// Make sure the buffer is nul terminated in all cases.
//
if (TargetBufferSize) {
Target[TargetBufferSize-1] = 0;
}
return(EndingLength <= TargetBufferSize);
}
VOID
Delnode(
IN LPCTSTR Directory
)
{
TCHAR pszDirectory[MAX_PATH + 1];
TCHAR pszPattern[MAX_PATH + 1];
WIN32_FIND_DATA FindData;
HANDLE FindHandle;
LOGMESSAGE(_T("Delnode: Entered"));
//
// Delete each file in the given directory, then remove the directory
// itself. If any directories are encountered along the way recurse to
// delete them as they are encountered.
//
// Start by forming the search pattern, which is <currentdir>\*.
//
ExpandEnvironmentStrings(Directory, pszDirectory, MAX_PATH);
LOGMESSAGE(_T("Delnode: Deleting %s"), pszDirectory);
lstrcpyn(pszPattern, pszDirectory, MAX_PATH);
ConcatenatePaths(pszPattern, _T("*"), MAX_PATH, NULL);
//
// Start the search.
//
FindHandle = FindFirstFile(pszPattern, &FindData);
if(FindHandle != INVALID_HANDLE_VALUE) {
do {
//
// Form the full name of the file or directory we just found.
//
lstrcpyn(pszPattern, pszDirectory, MAX_PATH);
ConcatenatePaths(pszPattern, FindData.cFileName, MAX_PATH, NULL);
//
// Remove read-only atttribute if it's there.
//
if (FindData.dwFileAttributes & FILE_ATTRIBUTE_READONLY) {
SetFileAttributes(pszPattern, FILE_ATTRIBUTE_NORMAL);
}
if (FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
//
// The current match is a directory. Recurse into it unless
// it's . or ...
//
if ((lstrcmp(FindData.cFileName,_T("."))) &&
(lstrcmp(FindData.cFileName,_T("..")))) {
Delnode(pszPattern);
}
} else {
//
// The current match is not a directory -- so delete it.
//
if (!DeleteFile(pszPattern)) {
LOGMESSAGE(_T("Delnode: %s not deleted: %d"), pszPattern,
GetLastError());
}
}
} while(FindNextFile(FindHandle, &FindData));
FindClose(FindHandle);
}
//
// Remove the directory we just emptied out. Ignore errors.
//
RemoveDirectory(pszDirectory);
}
/*
* Exported Functions.
*/
/*
* CheckDatabaseDirectory()
*
* CheckDatabaseDirectory is very hardcore about which paths it will accept.
*
* Good Paths:
* <DriveLetter>:\AbsolutePathToDirectory
*
* Bad Paths:
* Any path that is not like above, AND any path in the form above that
* is not on a fixed disk (e.g. no path to a floppy, CD-ROM, network
* share).
*/
DWORD
CheckDatabaseDirectory(
IN LPCTSTR pszDatabaseDir
)
{
BOOL fBadChars;
BOOL fBadPath;
UINT DriveType;
TCHAR pszExpandedDir[MAX_PATH + 1];
LOGMESSAGE(_T("CheckDatabaseDirectory: Entered"));
LOGMESSAGE(_T("CheckDatabaseDirectory: Checking %s"), pszDatabaseDir);
//
// NULL is not accepted.
//
if (pszDatabaseDir == NULL) {
return(ERROR_INVALID_PARAMETER);
}
//
// A path greater than MAX_PATH will cause problems somewhere. This
// will also catch pathnames with no environment variables that are
// still too long.
//
if (ExpandEnvironmentStrings(pszDatabaseDir, pszExpandedDir, MAX_PATH) >
MAX_PATH) {
LOGMESSAGE(_T("CheckDatabaseDirectory: Path too long"));
return(ERROR_BAD_PATHNAME);
}
//
// A path of less than three characters can't contain "<DriveLetter>:\".
// Also, don't allow anything but a letter, a colon, and a backslash.
//
fBadPath = FALSE;
if (!fBadPath) {
fBadPath = (_tcslen(pszExpandedDir) < 3);
}
if (!fBadPath) {
fBadPath = !(_istalpha(pszExpandedDir[0]));
}
if (!fBadPath) {
fBadPath = (pszExpandedDir[1] != _T(':'));
}
if (!fBadPath) {
fBadPath = (pszExpandedDir[2] != _T('\\'));
}
if (fBadPath) {
LOGMESSAGE(_T("CheckDatabaseDirectory: Not a C:\\ style directory"));
return(ERROR_BAD_PATHNAME);
}
//
// Characters like < > * ? and , won't work. Check for that now.
// Also, check for additional colons after the first C:\....
//
fBadChars = FALSE;
if (!fBadChars) {
fBadChars = (_tcschr(pszExpandedDir, _T('<')) != NULL);
}
if (!fBadChars) {
fBadChars = (_tcschr(pszExpandedDir, _T('>')) != NULL);
}
if (!fBadChars) {
fBadChars = (_tcschr(pszExpandedDir, _T('*')) != NULL);
}
if (!fBadChars) {
fBadChars = (_tcschr(pszExpandedDir, _T('?')) != NULL);
}
if (!fBadChars) {
fBadChars = (_tcschr(&(pszExpandedDir[3]), _T(':')) != NULL);
}
if (fBadChars) {
LOGMESSAGE(_T("CheckDatabaseDirectory: Invalid characters"));
return(ERROR_BAD_PATHNAME);
}
//
// GetDriveType only works for paths in the form "C:\" or
// "C:\ExistingDir". As pszDatabaseDir probably doesn't exist, it can't
// be passed to GetDriveType. Set a NULL character passed the "C:\" to
// pass in only the drive letter.
//
pszExpandedDir[3] = (TCHAR)NULL;
DriveType = GetDriveType(pszExpandedDir);
if (DriveType == DRIVE_FIXED) {
return(NO_ERROR);
} else {
LOGMESSAGE(_T("CheckDatabaseDirectory: Bad DriveType %d"), DriveType);
return(ERROR_BAD_PATHNAME);
}
}
/*
* CreateDatabaseDirectory()
*
* Creates the specified database directory.
*/
DWORD
CreateDatabaseDirectory(
VOID
)
{
return(CreateDirectoryRecursively(gszDatabaseDirectory));
}
/*
* GetDatabaseDirectory()
*
* Returns the current database directory.
*/
LPCTSTR
GetDatabaseDirectory(
VOID
)
{
return(gszDatabaseDirectory);
}
/*
* RemoveDatabaseDirectory()
*
* Removes the entire database directory.
*/
VOID
RemoveDatabaseDirectory(
VOID
)
{
Delnode(gszDatabaseDirectory);
}
/*
* SetDatabaseDirectory()
*
* This function assumes pszDatabaseDir has been verified by a call to
* CheckDatabaseDir(), which verifies not NULL, within MAX_PATH, and on a
* fixed hard drive.
*/
VOID
SetDatabaseDirectory(
IN LPCTSTR pszDatabaseDir
)
{
if(pszDatabaseDir && (_tcslen(pszDatabaseDir) <= MAX_PATH ))
{
_tcscpy(gszDatabaseDirectory, pszDatabaseDir);
}
}