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