467 lines
10 KiB
C++
467 lines
10 KiB
C++
|
/*
|
||
|
* 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"
|
||
|
|
||
|
/*
|
||
|
* 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;
|
||
|
DWORD dwErr;
|
||
|
|
||
|
if (_tcslen(pszDirectory) > (MAX_PATH)) {
|
||
|
return(ERROR_BAD_PATHNAME);
|
||
|
}
|
||
|
|
||
|
if (ExpandEnvironmentStrings(pszDirectory, Buffer, MAX_PATH) > MAX_PATH) {
|
||
|
return(ERROR_BAD_PATHNAME);
|
||
|
}
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Create this portion of the path.
|
||
|
//
|
||
|
|
||
|
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);
|
||
|
|
||
|
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
|
||
|
)
|
||
|
{
|
||
|
_tcscpy(gszDatabaseDirectory, pszDatabaseDir);
|
||
|
}
|
||
|
|