1357 lines
36 KiB
C++
1357 lines
36 KiB
C++
/**********************************************************************/
|
||
/** Microsoft Windows NT **/
|
||
/** Copyright(c) Microsoft Corp., 1993 **/
|
||
/**********************************************************************/
|
||
|
||
/*
|
||
virtual.cxx
|
||
|
||
This module contains the virtual I/O package.
|
||
|
||
Under Win32, the "current directory" is an attribute of a process,
|
||
not a thread. This causes some grief for the FTPD service, since
|
||
it is impersonating users on the server side. The users must
|
||
"think" they can change current directory at will. We'll provide
|
||
this behaviour in this package.
|
||
|
||
Functions exported by this module:
|
||
|
||
VirtualCreateFile
|
||
VirtualCreateUniqueFile
|
||
Virtual_fopen
|
||
|
||
VirtualDeleteFile
|
||
VirtualRenameFile
|
||
VirtualChDir
|
||
VirtualRmDir
|
||
VirtualMkDir
|
||
|
||
|
||
FILE HISTORY:
|
||
KeithMo 09-Mar-1993 Created.
|
||
|
||
MuraliK 28-Mar-1995 Enabled FILE_FLAG_OVERLAPPED in OpenFile()
|
||
MuraliK 28-Apr-1995 modified to use new canonicalization
|
||
11-May-1995 made parameters to be const unless otherwise
|
||
required.
|
||
12-May-1995 eliminated the old log file access
|
||
|
||
*/
|
||
|
||
|
||
#include "ftpdp.hxx"
|
||
|
||
|
||
//
|
||
// Private prototypes.
|
||
//
|
||
|
||
VOID
|
||
VirtualpSanitizePath(
|
||
CHAR * pszPath
|
||
);
|
||
|
||
|
||
|
||
//
|
||
// Public functions.
|
||
//
|
||
|
||
|
||
|
||
/*******************************************************************
|
||
|
||
NAME: VirtualCreateFile
|
||
|
||
SYNOPSIS: Creates a new (or overwrites an existing) file.
|
||
Also handles moving the file pointer and truncating the
|
||
file in the case of a REST command sequence.
|
||
|
||
ENTRY: pUserData - The user initiating the request.
|
||
|
||
phFile - Will receive the file handle. Will be
|
||
INVALID_HANDLE_VALUE if an error occurs.
|
||
|
||
pszFile - The name of the new file.
|
||
|
||
fAppend - If TRUE, and pszFile already exists, then
|
||
append to the existing file. Otherwise, create
|
||
a new file. Note that FALSE will ALWAYS create
|
||
a new file, potentially overwriting an existing
|
||
file.
|
||
|
||
RETURNS: APIERR - NO_ERROR if successful, otherwise a Win32
|
||
error code.
|
||
|
||
HISTORY:
|
||
KeithMo 09-Mar-1993 Created.
|
||
MuraliK 28-Apr-1995 modified to use new canonicalization
|
||
|
||
********************************************************************/
|
||
APIERR
|
||
VirtualCreateFile(
|
||
USER_DATA * pUserData,
|
||
HANDLE * phFile,
|
||
LPSTR pszFile,
|
||
BOOL fAppend
|
||
)
|
||
{
|
||
HANDLE hFile = INVALID_HANDLE_VALUE;
|
||
APIERR err;
|
||
CHAR szCanonPath[MAX_PATH+1];
|
||
DWORD cbSize = sizeof(szCanonPath);
|
||
LARGE_INTEGER liOffset, liZero;
|
||
CHAR szVirtualPath[MAX_PATH+1];
|
||
DWORD ccbVirtualPath = sizeof(szVirtualPath);
|
||
|
||
DBG_ASSERT( pUserData != NULL );
|
||
DBG_ASSERT( phFile != NULL );
|
||
DBG_ASSERT( pszFile != NULL );
|
||
|
||
liZero.QuadPart = 0;
|
||
liOffset.QuadPart = pUserData->QueryCurrentOffset();
|
||
|
||
// We'll want to do pretty much the same thing whether we're
|
||
// actually appending or just starting at an offset due to a REST
|
||
// command, so combine them here.
|
||
|
||
fAppend = fAppend || (liOffset.QuadPart != 0);
|
||
|
||
err = pUserData->VirtualCanonicalize(szCanonPath,
|
||
&cbSize,
|
||
pszFile,
|
||
AccessTypeCreate,
|
||
NULL,
|
||
szVirtualPath,
|
||
&ccbVirtualPath);
|
||
|
||
if( err == NO_ERROR ) {
|
||
|
||
IF_DEBUG( VIRTUAL_IO )
|
||
{
|
||
DBGPRINTF(( DBG_CONTEXT,
|
||
"creating %s\n", szCanonPath ));
|
||
}
|
||
|
||
// store the virtual file name for logging
|
||
pUserData->SetVirtualFileName( szVirtualPath );
|
||
|
||
if ( pUserData->ImpersonateUser()) {
|
||
|
||
WCHAR awchPath[MAX_PATH+8+1];
|
||
|
||
if (TsMakeWidePath( szCanonPath, awchPath, MAX_PATH+8+1)) {
|
||
|
||
hFile = CreateFileW( awchPath,
|
||
GENERIC_WRITE,
|
||
FILE_SHARE_READ,
|
||
NULL,
|
||
fAppend ? OPEN_ALWAYS : CREATE_ALWAYS,
|
||
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
|
||
NULL );
|
||
}
|
||
|
||
//
|
||
// Disallow usage of short names
|
||
//
|
||
|
||
pUserData->RevertToSelf();
|
||
|
||
if ( hFile != INVALID_HANDLE_VALUE )
|
||
{
|
||
if ( GetFileType( hFile ) != FILE_TYPE_DISK )
|
||
{
|
||
DBG_REQUIRE( CloseHandle( hFile ) );
|
||
SetLastError( ERROR_ACCESS_DENIED );
|
||
hFile = INVALID_HANDLE_VALUE;
|
||
}
|
||
else if ( strchr( szCanonPath, '~' )) {
|
||
|
||
BOOL fShort;
|
||
DWORD err;
|
||
|
||
err = CheckIfShortFileName( (UCHAR *) szCanonPath,
|
||
pUserData->QueryImpersonationToken(),
|
||
&fShort );
|
||
|
||
if ( !err && fShort ) {
|
||
|
||
err = ERROR_FILE_NOT_FOUND;
|
||
}
|
||
|
||
if ( err ) {
|
||
|
||
DBG_REQUIRE( CloseHandle( hFile ));
|
||
hFile = INVALID_HANDLE_VALUE;
|
||
SetLastError( err );
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if( hFile == INVALID_HANDLE_VALUE ) {
|
||
|
||
err = GetLastError();
|
||
}
|
||
|
||
if( fAppend && ( err == NO_ERROR ) ) {
|
||
|
||
if (liOffset.QuadPart == 0) {
|
||
// This is a real append, not a restart sequence.
|
||
// set Offset to the file size
|
||
if( !SetFilePointerEx( hFile,
|
||
liZero,
|
||
&liOffset,
|
||
FILE_END ) ) {
|
||
|
||
err = GetLastError();
|
||
}
|
||
} else {
|
||
|
||
// We're in part of a restart sequence. Set the file pointer
|
||
// to the offset, and truncate the file there.
|
||
|
||
if ( !SetFilePointerEx( hFile,
|
||
liOffset,
|
||
NULL,
|
||
FILE_BEGIN)
|
||
||
|
||
!SetEndOfFile( hFile) ) {
|
||
|
||
err = GetLastError();
|
||
}
|
||
|
||
}
|
||
|
||
if (err != NO_ERROR ) {
|
||
|
||
CloseHandle( hFile );
|
||
hFile = INVALID_HANDLE_VALUE;
|
||
|
||
}
|
||
}
|
||
}
|
||
|
||
if ( err != NO_ERROR) {
|
||
|
||
IF_DEBUG( VIRTUAL_IO )
|
||
{
|
||
DBGPRINTF(( DBG_CONTEXT,
|
||
"cannot create %s, error %lu\n",
|
||
szCanonPath,
|
||
err ));
|
||
}
|
||
}
|
||
|
||
if ( err == NO_ERROR) {
|
||
|
||
pUserData->SetCurrentOffset( liOffset.QuadPart );
|
||
}
|
||
|
||
*phFile = hFile;
|
||
|
||
return err;
|
||
|
||
} // VirtualCreateFile
|
||
|
||
|
||
/*******************************************************************
|
||
|
||
NAME: VirtualCreateUniqueFile
|
||
|
||
SYNOPSIS: Creates a new unique (temporary) file in the current
|
||
virtual directory.
|
||
|
||
ENTRY: pUserData - The user initiating the request.
|
||
|
||
phFile - Will receive the file handle. Will be
|
||
INVALID_HANDLE_VALUE if an error occurs.
|
||
|
||
pszTmpFile - Will receive the name of the temporary
|
||
file. This buffer MUST be at least MAX_PATH
|
||
characters long.
|
||
|
||
RETURNS: APIERR - NO_ERROR if successful, otherwise a Win32
|
||
error code.
|
||
|
||
HISTORY:
|
||
KeithMo 16-Mar-1993 Created.
|
||
MuraliK 28-Apr-1995 modified to use new canonicalization
|
||
|
||
********************************************************************/
|
||
APIERR
|
||
VirtualCreateUniqueFile(
|
||
USER_DATA * pUserData,
|
||
HANDLE * phFile,
|
||
LPSTR pszTmpFile
|
||
)
|
||
{
|
||
HANDLE hFile = INVALID_HANDLE_VALUE;
|
||
APIERR err = NO_ERROR;
|
||
CHAR szCanon[MAX_PATH+1];
|
||
DWORD cbSize = sizeof(szCanon);
|
||
CHAR szVirtualPath[MAX_PATH+1];
|
||
DWORD ccbVirtualPath = sizeof(szVirtualPath);
|
||
|
||
DBG_ASSERT( pUserData != NULL );
|
||
DBG_ASSERT( phFile != NULL );
|
||
DBG_ASSERT( pszTmpFile != NULL );
|
||
|
||
//
|
||
// Obtain the virtual to real path conversion.
|
||
//
|
||
|
||
err = pUserData->VirtualCanonicalize(szCanon,
|
||
&cbSize,
|
||
"", // current directory
|
||
AccessTypeCreate,
|
||
NULL,
|
||
szVirtualPath,
|
||
&ccbVirtualPath);
|
||
|
||
if( err == NO_ERROR ) {
|
||
|
||
IF_DEBUG( VIRTUAL_IO ) {
|
||
|
||
DBGPRINTF(( DBG_CONTEXT,
|
||
"creating unique file %s\n", pszTmpFile ));
|
||
}
|
||
|
||
// store the virtual file name for logging
|
||
pUserData->SetVirtualFileName( szVirtualPath );
|
||
|
||
if ( pUserData->ImpersonateUser()) {
|
||
|
||
if ( GetTempFileName(szCanon,
|
||
"FTPD",
|
||
0, pszTmpFile ) != 0
|
||
) {
|
||
|
||
WCHAR awchPath[MAX_PATH+8+1];
|
||
|
||
if (TsMakeWidePath( pszTmpFile, awchPath, MAX_PATH+8+1)) {
|
||
|
||
hFile = CreateFileW( awchPath,
|
||
GENERIC_WRITE,
|
||
FILE_SHARE_READ,
|
||
NULL,
|
||
CREATE_ALWAYS,
|
||
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
|
||
NULL );
|
||
}
|
||
}
|
||
|
||
pUserData->RevertToSelf();
|
||
}
|
||
|
||
if( hFile == INVALID_HANDLE_VALUE ) {
|
||
|
||
err = GetLastError();
|
||
}
|
||
}
|
||
|
||
if( err != NO_ERROR )
|
||
{
|
||
IF_DEBUG( VIRTUAL_IO )
|
||
{
|
||
DBGPRINTF(( DBG_CONTEXT,
|
||
"cannot create unique file, error %lu\n",
|
||
err ));
|
||
}
|
||
}
|
||
|
||
if ( err == NO_ERROR) {
|
||
|
||
pUserData->SetCurrentOffset( 0 );
|
||
}
|
||
|
||
*phFile = hFile;
|
||
|
||
return err;
|
||
|
||
} // VirtualCreateUniqueFile()
|
||
|
||
|
||
|
||
/*******************************************************************
|
||
|
||
NAME: Virtual_fopen
|
||
|
||
SYNOPSIS: Opens an file stream.
|
||
|
||
ENTRY: pUserData - The user initiating the request.
|
||
|
||
pszFile - The name of the file to open.
|
||
|
||
pszMode - The type of access required.
|
||
|
||
RETURNS: FILE * - The open file stream, NULL if file cannot
|
||
be opened.
|
||
|
||
NOTES: Since this is only used for accessing the ~FTPSVC~.CKM
|
||
annotation files, we don't log file accesses here.
|
||
|
||
HISTORY:
|
||
KeithMo 07-May-1993 Created.
|
||
MuraliK 28-Apr-1995 modified to use new canonicalization
|
||
|
||
********************************************************************/
|
||
FILE *
|
||
Virtual_fopen(
|
||
USER_DATA * pUserData,
|
||
LPSTR pszFile,
|
||
LPSTR pszMode
|
||
)
|
||
{
|
||
FILE * pfile = NULL;
|
||
APIERR err;
|
||
CHAR szCanonPath[MAX_PATH+1];
|
||
DWORD cbSize = sizeof(szCanonPath);
|
||
|
||
DBG_ASSERT( pUserData != NULL );
|
||
DBG_ASSERT( pszFile != NULL );
|
||
DBG_ASSERT( pszMode != NULL );
|
||
|
||
err = pUserData->VirtualCanonicalize(szCanonPath,
|
||
&cbSize,
|
||
pszFile,
|
||
*pszMode == 'r'
|
||
? AccessTypeRead
|
||
: AccessTypeWrite );
|
||
|
||
if( err == NO_ERROR )
|
||
{
|
||
IF_DEBUG( VIRTUAL_IO )
|
||
{
|
||
DBGPRINTF(( DBG_CONTEXT,
|
||
"opening %s\n", szCanonPath ));
|
||
}
|
||
|
||
if ( pUserData->ImpersonateUser()) {
|
||
|
||
pfile = fopen( szCanonPath, pszMode );
|
||
|
||
pUserData->RevertToSelf();
|
||
}
|
||
|
||
if( pfile == NULL )
|
||
{
|
||
err = ERROR_FILE_NOT_FOUND; // best guess
|
||
}
|
||
}
|
||
|
||
IF_DEBUG( VIRTUAL_IO )
|
||
{
|
||
if( err != NO_ERROR )
|
||
{
|
||
DBGPRINTF(( DBG_CONTEXT,
|
||
"cannot open %s, error %lu\n",
|
||
pszFile,
|
||
err ));
|
||
}
|
||
}
|
||
|
||
return pfile;
|
||
|
||
} // Virtual_fopen
|
||
|
||
|
||
|
||
|
||
/*******************************************************************
|
||
|
||
NAME: VirtualDeleteFile
|
||
|
||
SYNOPSIS: Deletes an existing file.
|
||
|
||
ENTRY: pUserData - The user initiating the request.
|
||
|
||
pszFile - The name of the file.
|
||
|
||
RETURNS: APIERR - NO_ERROR if successful, otherwise a Win32
|
||
error code.
|
||
|
||
HISTORY:
|
||
KeithMo 09-Mar-1993 Created.
|
||
|
||
********************************************************************/
|
||
APIERR
|
||
VirtualDeleteFile(
|
||
USER_DATA * pUserData,
|
||
LPSTR pszFile
|
||
)
|
||
{
|
||
APIERR err;
|
||
CHAR szCanonPath[MAX_PATH+1];
|
||
DWORD cbSize = sizeof(szCanonPath);
|
||
DWORD dwAccessMask = 0;
|
||
|
||
DBG_ASSERT( pUserData != NULL );
|
||
|
||
//
|
||
// We'll canonicalize the path, asking for *read* access. If
|
||
// the path canonicalizes correctly, we'll then try to open the
|
||
// file to ensure it exists. Only then will we check for delete
|
||
// access to the path. This mumbo-jumbo is necessary to get the
|
||
// proper error codes if someone trys to delete a nonexistent
|
||
// file on a read-only volume.
|
||
//
|
||
|
||
err = pUserData->VirtualCanonicalize(szCanonPath,
|
||
&cbSize,
|
||
pszFile,
|
||
AccessTypeRead,
|
||
&dwAccessMask);
|
||
|
||
if( err == NO_ERROR )
|
||
{
|
||
HANDLE hFile = INVALID_HANDLE_VALUE;
|
||
|
||
if ( pUserData->ImpersonateUser()) {
|
||
|
||
WCHAR awchPath[MAX_PATH+8+1];
|
||
|
||
if (TsMakeWidePath( szCanonPath, awchPath, MAX_PATH+8+1)) {
|
||
|
||
hFile = CreateFileW( awchPath,
|
||
GENERIC_READ,
|
||
FILE_SHARE_READ,
|
||
NULL,
|
||
OPEN_EXISTING,
|
||
FILE_ATTRIBUTE_NORMAL,
|
||
NULL );
|
||
}
|
||
|
||
pUserData->RevertToSelf();
|
||
}
|
||
|
||
if( hFile == INVALID_HANDLE_VALUE )
|
||
{
|
||
err = GetLastError();
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// The file DOES exist. Close the handle, then check
|
||
// to ensure we really have delete access.
|
||
//
|
||
|
||
CloseHandle( hFile );
|
||
|
||
if( !PathAccessCheck( AccessTypeDelete,
|
||
dwAccessMask,
|
||
TEST_UF(pUserData, READ_ACCESS),
|
||
TEST_UF(pUserData, WRITE_ACCESS)
|
||
)
|
||
) {
|
||
|
||
err = ERROR_ACCESS_DENIED;
|
||
}
|
||
}
|
||
}
|
||
|
||
if( err == NO_ERROR )
|
||
{
|
||
IF_DEBUG( VIRTUAL_IO )
|
||
{
|
||
DBGPRINTF(( DBG_CONTEXT,
|
||
"deleting %s\n", szCanonPath ));
|
||
}
|
||
|
||
if ( pUserData->ImpersonateUser()) {
|
||
|
||
if( !DeleteFile( szCanonPath ) ) {
|
||
|
||
err = GetLastError();
|
||
|
||
IF_DEBUG( VIRTUAL_IO ) {
|
||
|
||
DBGPRINTF(( DBG_CONTEXT,
|
||
"cannot delete %s, error %lu\n",
|
||
szCanonPath,
|
||
err ));
|
||
}
|
||
}
|
||
|
||
pUserData->RevertToSelf();
|
||
} else {
|
||
|
||
err = GetLastError();
|
||
}
|
||
}
|
||
else
|
||
{
|
||
IF_DEBUG( VIRTUAL_IO )
|
||
{
|
||
DBGPRINTF(( DBG_CONTEXT,
|
||
"cannot canonicalize %s - %s, error %lu\n",
|
||
pUserData->QueryCurrentDirectory().QueryStr(),
|
||
pszFile,
|
||
err ));
|
||
}
|
||
}
|
||
|
||
return err;
|
||
|
||
} // VirtualDeleteFile()
|
||
|
||
|
||
|
||
/*******************************************************************
|
||
|
||
NAME: VirtualRenameFile
|
||
|
||
SYNOPSIS: Renames an existing file or directory.
|
||
|
||
ENTRY: pUserData - The user initiating the request.
|
||
|
||
pszExisting - The name of an existing file or directory.
|
||
|
||
pszNew - The new name for the file or directory.
|
||
|
||
RETURNS: APIERR - NO_ERROR if successful, otherwise a Win32
|
||
error code.
|
||
|
||
HISTORY:
|
||
KeithMo 10-Mar-1993 Created.
|
||
|
||
********************************************************************/
|
||
APIERR
|
||
VirtualRenameFile(
|
||
USER_DATA * pUserData,
|
||
LPSTR pszExisting,
|
||
LPSTR pszNew
|
||
)
|
||
{
|
||
APIERR err;
|
||
CHAR szCanonExisting[MAX_PATH+1];
|
||
DWORD cbSize = sizeof(szCanonExisting);
|
||
|
||
DBG_ASSERT( pUserData != NULL );
|
||
|
||
err = pUserData->VirtualCanonicalize(szCanonExisting,
|
||
&cbSize,
|
||
pszExisting,
|
||
AccessTypeDelete );
|
||
|
||
if( err == NO_ERROR ) {
|
||
|
||
CHAR szCanonNew[MAX_PATH+1];
|
||
cbSize = sizeof(szCanonNew);
|
||
|
||
err = pUserData->VirtualCanonicalize(szCanonNew,
|
||
&cbSize,
|
||
pszNew,
|
||
AccessTypeCreate );
|
||
|
||
if( err == NO_ERROR ) {
|
||
|
||
IF_DEBUG( VIRTUAL_IO ) {
|
||
|
||
DBGPRINTF(( DBG_CONTEXT,
|
||
"renaming %s to %s\n",
|
||
szCanonExisting,
|
||
szCanonNew ));
|
||
}
|
||
|
||
if ( pUserData->ImpersonateUser()) {
|
||
|
||
if( !MoveFileEx( szCanonExisting,
|
||
szCanonNew,
|
||
pUserData->QueryInstance()->AllowReplaceOnRename()
|
||
? MOVEFILE_REPLACE_EXISTING
|
||
: 0 )
|
||
){
|
||
|
||
err = GetLastError();
|
||
|
||
IF_DEBUG( VIRTUAL_IO ) {
|
||
|
||
DBGPRINTF(( DBG_CONTEXT,
|
||
"cannot rename %s to %s, error %lu\n",
|
||
szCanonExisting,
|
||
szCanonNew,
|
||
err ));
|
||
}
|
||
}
|
||
|
||
pUserData->RevertToSelf();
|
||
|
||
} else {
|
||
err = GetLastError();
|
||
}
|
||
|
||
} else {
|
||
|
||
IF_DEBUG( VIRTUAL_IO ) {
|
||
|
||
DBGPRINTF(( DBG_CONTEXT,
|
||
"cannot canonicalize %s - %s, error %lu\n",
|
||
pUserData->QueryCurrentDirectory().QueryStr(),
|
||
pszExisting,
|
||
err ));
|
||
}
|
||
}
|
||
} else {
|
||
|
||
IF_DEBUG( VIRTUAL_IO ) {
|
||
|
||
DBGPRINTF(( DBG_CONTEXT,
|
||
"cannot canonicalize %s - %s, error %lu\n",
|
||
pUserData->QueryCurrentDirectory().QueryStr(),
|
||
pszExisting,
|
||
err ));
|
||
}
|
||
}
|
||
|
||
return err;
|
||
|
||
} // VirtualRenameFile()
|
||
|
||
|
||
|
||
|
||
APIERR
|
||
VirtualChDir(
|
||
USER_DATA * pUserData,
|
||
LPSTR pszDir
|
||
)
|
||
/*++
|
||
This function sets the current directory to newly specified directory.
|
||
|
||
Arguments:
|
||
pUserData -- the user initiating the request
|
||
pszDir -- pointer to null-terminated buffer containing the
|
||
new directory name.
|
||
|
||
Returns:
|
||
APIERR -- NO_ERROR if successful, otherwise a Win32 error code.
|
||
|
||
History:
|
||
MuraliK 28-Apr-1995 Modified to use symbolic roots.
|
||
--*/
|
||
{
|
||
CHAR rgchVirtual[MAX_PATH+1];
|
||
DWORD cbVirtSize;
|
||
APIERR err = NO_ERROR;
|
||
DWORD cbSize;
|
||
CHAR szCanonDir[MAX_PATH+1];
|
||
|
||
DBG_ASSERT( pUserData != NULL );
|
||
|
||
if (pszDir == NULL || *pszDir == '\0') {
|
||
|
||
//
|
||
// Nothing new specified.
|
||
//
|
||
|
||
return ( NO_ERROR);
|
||
}
|
||
|
||
//
|
||
// Canonicalize the new path.
|
||
//
|
||
|
||
cbSize = sizeof(szCanonDir);
|
||
cbVirtSize = sizeof(rgchVirtual);
|
||
err = pUserData->VirtualCanonicalize(szCanonDir,
|
||
&cbSize,
|
||
pszDir,
|
||
AccessTypeRead,
|
||
NULL,
|
||
rgchVirtual,
|
||
&cbVirtSize);
|
||
|
||
if ( err == ERROR_ACCESS_DENIED) {
|
||
|
||
//
|
||
// this maybe a write only virtual root directory.
|
||
// Let us try again to find we have Write access atleast
|
||
//
|
||
|
||
cbSize = sizeof(szCanonDir);
|
||
cbVirtSize = sizeof(rgchVirtual);
|
||
err = pUserData->VirtualCanonicalize(szCanonDir,
|
||
&cbSize,
|
||
pszDir,
|
||
AccessTypeWrite,
|
||
NULL,
|
||
rgchVirtual,
|
||
&cbVirtSize);
|
||
}
|
||
|
||
if( err != NO_ERROR )
|
||
{
|
||
|
||
IF_DEBUG( VIRTUAL_IO )
|
||
{
|
||
DBGPRINTF(( DBG_CONTEXT,
|
||
"cannot canonicalize %s - %s, error %lu\n",
|
||
pUserData->QueryCurrentDirectory().QueryStr(),
|
||
pszDir,
|
||
err ));
|
||
}
|
||
|
||
return err;
|
||
}
|
||
|
||
//
|
||
// Try to open the directory and get a handle for the same.
|
||
//
|
||
|
||
// This is possibly a new place to change directory to.
|
||
|
||
if ( pUserData->ImpersonateUser()) {
|
||
|
||
HANDLE CurrentDirHandle = INVALID_HANDLE_VALUE;
|
||
|
||
err = OpenPathForAccess( &CurrentDirHandle,
|
||
szCanonDir,
|
||
GENERIC_READ,
|
||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||
pUserData->QueryImpersonationToken()
|
||
);
|
||
|
||
if( err == ERROR_ACCESS_DENIED ) {
|
||
err = OpenPathForAccess( &CurrentDirHandle,
|
||
szCanonDir,
|
||
GENERIC_WRITE,
|
||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||
pUserData->QueryImpersonationToken()
|
||
);
|
||
}
|
||
|
||
if( err == NO_ERROR ) {
|
||
|
||
BY_HANDLE_FILE_INFORMATION fileInfo;
|
||
BOOL fRet;
|
||
|
||
fRet = GetFileInformationByHandle( CurrentDirHandle,
|
||
&fileInfo);
|
||
|
||
if ( !fRet) {
|
||
|
||
err = GetLastError();
|
||
|
||
// Error in getting the file information.
|
||
// close handle and return.
|
||
|
||
CloseHandle( CurrentDirHandle);
|
||
|
||
} else {
|
||
|
||
if ( (fileInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
||
!= FILE_ATTRIBUTE_DIRECTORY) {
|
||
|
||
//
|
||
// this file is not a directory.
|
||
// Do not change directory. But return error.
|
||
//
|
||
|
||
err = ERROR_DIRECTORY;
|
||
CloseHandle( CurrentDirHandle);
|
||
} else {
|
||
|
||
//
|
||
// Directory successfully opened. Save the handle
|
||
// in the per-user data. This handle is maintained to
|
||
// prevent accidental deletion of the directory.
|
||
//
|
||
|
||
if( pUserData->CurrentDirHandle != INVALID_HANDLE_VALUE ) {
|
||
|
||
IF_DEBUG( VIRTUAL_IO ) {
|
||
|
||
DBGPRINTF(( DBG_CONTEXT,
|
||
"closing dir handle %08lX for %s\n",
|
||
pUserData->CurrentDirHandle,
|
||
pUserData->QueryCurrentDirectory().QueryStr()));
|
||
}
|
||
|
||
CloseHandle( pUserData->CurrentDirHandle );
|
||
}
|
||
|
||
pUserData->CurrentDirHandle = CurrentDirHandle;
|
||
|
||
IF_DEBUG( VIRTUAL_IO ) {
|
||
|
||
DBGPRINTF(( DBG_CONTEXT,
|
||
"opened directory %s, handle = %08lX\n",
|
||
szCanonDir,
|
||
CurrentDirHandle ));
|
||
}
|
||
|
||
// update the current directory
|
||
pUserData->SetCurrentDirectory( rgchVirtual );
|
||
|
||
}
|
||
}
|
||
}
|
||
|
||
pUserData->RevertToSelf();
|
||
|
||
} else {
|
||
|
||
// Impersonation failed
|
||
err = GetLastError();
|
||
}
|
||
|
||
IF_DEBUG( VIRTUAL_IO ) {
|
||
|
||
DBGPRINTF(( DBG_CONTEXT,
|
||
"chdir to %s returns error = %d\n", szCanonDir, err ));
|
||
}
|
||
|
||
return ( err);
|
||
|
||
} // VirtualChDir()
|
||
|
||
|
||
|
||
/*******************************************************************
|
||
|
||
NAME: VirtualRmDir
|
||
|
||
SYNOPSIS: Removes an existing directory.
|
||
|
||
ENTRY: pUserData - The user initiating the request.
|
||
|
||
pszDir - The name of the directory to remove.
|
||
|
||
RETURNS: APIERR - NO_ERROR if successful, otherwise a Win32
|
||
error code.
|
||
|
||
HISTORY:
|
||
KeithMo 09-Mar-1993 Created.
|
||
|
||
********************************************************************/
|
||
APIERR
|
||
VirtualRmDir(
|
||
USER_DATA * pUserData,
|
||
LPSTR pszDir
|
||
)
|
||
{
|
||
APIERR err;
|
||
CHAR szCanonDir[MAX_PATH+1];
|
||
DWORD cbSize = sizeof(szCanonDir);
|
||
|
||
err = pUserData->VirtualCanonicalize(szCanonDir,
|
||
&cbSize,
|
||
pszDir,
|
||
AccessTypeDelete );
|
||
|
||
if( err == NO_ERROR )
|
||
{
|
||
IF_DEBUG( VIRTUAL_IO )
|
||
{
|
||
DBGPRINTF(( DBG_CONTEXT,
|
||
"rmdir %s\n", szCanonDir ));
|
||
}
|
||
|
||
if ( pUserData->ImpersonateUser()) {
|
||
|
||
if( !RemoveDirectory( szCanonDir ) ) {
|
||
|
||
err = GetLastError();
|
||
|
||
IF_DEBUG( VIRTUAL_IO ) {
|
||
|
||
DBGPRINTF(( DBG_CONTEXT,
|
||
"cannot rmdir %s, error %lu\n",
|
||
szCanonDir,
|
||
err ));
|
||
}
|
||
}
|
||
pUserData->RevertToSelf();
|
||
} else {
|
||
err = GetLastError();
|
||
}
|
||
} else {
|
||
|
||
IF_DEBUG( VIRTUAL_IO )
|
||
{
|
||
DBGPRINTF(( DBG_CONTEXT,
|
||
"cannot canonicalize %s - %s, error %lu\n",
|
||
pUserData->QueryCurrentDirectory().QueryStr(),
|
||
pszDir,
|
||
err ));
|
||
}
|
||
}
|
||
|
||
return err;
|
||
|
||
} // VirtualRmDir
|
||
|
||
|
||
|
||
/*******************************************************************
|
||
|
||
NAME: VirtualMkDir
|
||
|
||
SYNOPSIS: Creates a new directory.
|
||
|
||
ENTRY: pUserData - The user initiating the request.
|
||
|
||
pszDir - The name of the directory to create.
|
||
|
||
RETURNS: APIERR - NO_ERROR if successful, otherwise a Win32
|
||
error code.
|
||
|
||
HISTORY:
|
||
KeithMo 09-Mar-1993 Created.
|
||
|
||
********************************************************************/
|
||
APIERR
|
||
VirtualMkDir(
|
||
USER_DATA * pUserData,
|
||
LPSTR pszDir
|
||
)
|
||
{
|
||
APIERR err;
|
||
CHAR szCanonDir[MAX_PATH+1];
|
||
DWORD cbSize = sizeof(szCanonDir);
|
||
|
||
DBG_ASSERT( pUserData != NULL );
|
||
|
||
err = pUserData->VirtualCanonicalize(szCanonDir,
|
||
&cbSize,
|
||
pszDir,
|
||
AccessTypeCreate );
|
||
|
||
if( err == NO_ERROR )
|
||
{
|
||
IF_DEBUG( VIRTUAL_IO )
|
||
{
|
||
DBGPRINTF(( DBG_CONTEXT,
|
||
"mkdir %s\n", szCanonDir ));
|
||
}
|
||
|
||
if ( pUserData->ImpersonateUser()) {
|
||
|
||
if( !CreateDirectory( szCanonDir, NULL ) ) {
|
||
|
||
err = GetLastError();
|
||
|
||
IF_DEBUG( VIRTUAL_IO )
|
||
{
|
||
DBGPRINTF(( DBG_CONTEXT,
|
||
"cannot mkdir %s, error %lu\n",
|
||
szCanonDir,
|
||
err ));
|
||
}
|
||
}
|
||
pUserData->RevertToSelf();
|
||
} else {
|
||
err = GetLastError();
|
||
}
|
||
} else {
|
||
|
||
IF_DEBUG( VIRTUAL_IO ) {
|
||
|
||
DBGPRINTF(( DBG_CONTEXT,
|
||
"cannot canonicalize %s - %s, error %lu\n",
|
||
pUserData->QueryCurrentDirectory().QueryStr(),
|
||
pszDir,
|
||
err ));
|
||
}
|
||
}
|
||
|
||
return err;
|
||
|
||
} // VirtualMkDir
|
||
|
||
|
||
|
||
|
||
//
|
||
// Private constants.
|
||
//
|
||
|
||
#define ACTION_NOTHING 0x00000000
|
||
#define ACTION_EMIT_CH 0x00010000
|
||
#define ACTION_EMIT_DOT_CH 0x00020000
|
||
#define ACTION_EMIT_DOT_DOT_CH 0x00030000
|
||
#define ACTION_BACKUP 0x00040000
|
||
#define ACTION_MASK 0xFFFF0000
|
||
|
||
|
||
//
|
||
// Private globals.
|
||
//
|
||
|
||
INT p_StateTable[4][4] =
|
||
{
|
||
{ // state 0
|
||
1 | ACTION_EMIT_CH, // "\"
|
||
0 | ACTION_EMIT_CH, // "."
|
||
4 | ACTION_EMIT_CH, // EOS
|
||
0 | ACTION_EMIT_CH // other
|
||
},
|
||
|
||
{ // state 1
|
||
1 | ACTION_NOTHING, // "\"
|
||
2 | ACTION_NOTHING, // "."
|
||
4 | ACTION_EMIT_CH, // EOS
|
||
0 | ACTION_EMIT_CH // other
|
||
},
|
||
|
||
{ // state 2
|
||
1 | ACTION_NOTHING, // "\"
|
||
3 | ACTION_NOTHING, // "."
|
||
4 | ACTION_EMIT_CH, // EOS
|
||
0 | ACTION_EMIT_DOT_CH // other
|
||
},
|
||
|
||
{ // state 3
|
||
1 | ACTION_BACKUP, // "\"
|
||
0 | ACTION_EMIT_DOT_DOT_CH, // "."
|
||
4 | ACTION_BACKUP, // EOS
|
||
0 | ACTION_EMIT_DOT_DOT_CH // other
|
||
}
|
||
};
|
||
|
||
|
||
|
||
/*******************************************************************
|
||
|
||
NAME: VirtualpSanitizePath
|
||
|
||
SYNOPSIS: Sanitizes a path by removing bogus path elements.
|
||
|
||
As expected, "/./" entries are simply removed, and
|
||
"/../" entries are removed along with the previous
|
||
path element.
|
||
|
||
To maintain compatibility with URL path semantics
|
||
additional transformations are required. All backward
|
||
slashes "\\" are converted to forward slashes. Any
|
||
repeated forward slashes (such as "///") are mapped to
|
||
single backslashes. Also, any trailing path elements
|
||
consisting solely of dots "/....." are removed.
|
||
|
||
Thus, the path "/foo\./bar/../tar\....\......" is
|
||
mapped to "/foo/tar".
|
||
|
||
A state table (see the p_StateTable global at the
|
||
beginning of this file) is used to perform most of
|
||
the transformations. The table's rows are indexed
|
||
by current state, and the columns are indexed by
|
||
the current character's "class" (either slash, dot,
|
||
NULL, or other). Each entry in the table consists
|
||
of the new state tagged with an action to perform.
|
||
See the ACTION_* constants for the valid action
|
||
codes.
|
||
|
||
After the FSA is finished with the path, we make one
|
||
additional pass through it to remove any trailing
|
||
backslash, and to remove any trailing path elements
|
||
consisting solely of dots.
|
||
|
||
ENTRY: pszPath - The path to sanitize.
|
||
|
||
HISTORY:
|
||
KeithMo 07-Sep-1994 Created.
|
||
MuraliK 28-Apr-1995 Adopted this for symbolic paths
|
||
|
||
********************************************************************/
|
||
VOID
|
||
VirtualpSanitizePath(
|
||
CHAR * pszPath
|
||
)
|
||
{
|
||
CHAR * pszSrc;
|
||
CHAR * pszDest;
|
||
CHAR * pszHead;
|
||
CHAR ch;
|
||
INT State;
|
||
INT Class;
|
||
BOOL fDBCS = FALSE;
|
||
|
||
//
|
||
// Ensure we got a valid symbolic path (something starting "/"
|
||
//
|
||
|
||
DBG_ASSERT( pszPath != NULL );
|
||
// DBG_ASSERT( pszPath[0] == '/');
|
||
|
||
//
|
||
// Start our scan at the first "/.
|
||
//
|
||
|
||
pszHead = pszSrc = pszDest = pszPath;
|
||
|
||
//
|
||
// State 0 is the initial state.
|
||
//
|
||
|
||
State = 0;
|
||
|
||
//
|
||
// Loop until we enter state 4 (the final, accepting state).
|
||
//
|
||
|
||
while( State != 4 )
|
||
{
|
||
//
|
||
// Grab the next character from the path and compute its
|
||
// character class. While we're at it, map any forward
|
||
// slashes to backward slashes.
|
||
//
|
||
|
||
ch = *pszSrc++;
|
||
|
||
switch( ch )
|
||
{
|
||
case '\\' :
|
||
//
|
||
// fDBCS is always false for non-DBCS system
|
||
//
|
||
|
||
if ( fDBCS )
|
||
{
|
||
Class = 3;
|
||
break;
|
||
}
|
||
ch = '/'; // convert it to symbolic URL path separator char.
|
||
/* fall through */
|
||
|
||
case '/' :
|
||
Class = 0;
|
||
break;
|
||
|
||
case '.' :
|
||
Class = 1;
|
||
break;
|
||
|
||
case '\0' :
|
||
Class = 2;
|
||
break;
|
||
|
||
default :
|
||
Class = 3;
|
||
break;
|
||
}
|
||
|
||
//
|
||
// Advance to the next state.
|
||
//
|
||
|
||
State = p_StateTable[State][Class];
|
||
|
||
//
|
||
// Perform the action associated with the state.
|
||
//
|
||
|
||
switch( State & ACTION_MASK )
|
||
{
|
||
case ACTION_EMIT_DOT_DOT_CH :
|
||
*pszDest++ = '.';
|
||
/* fall through */
|
||
|
||
case ACTION_EMIT_DOT_CH :
|
||
*pszDest++ = '.';
|
||
/* fall through */
|
||
|
||
case ACTION_EMIT_CH :
|
||
*pszDest++ = ch;
|
||
/* fall through */
|
||
|
||
case ACTION_NOTHING :
|
||
break;
|
||
|
||
case ACTION_BACKUP :
|
||
if( pszDest > ( pszHead + 1 ) )
|
||
{
|
||
pszDest--;
|
||
DBG_ASSERT( *pszDest == '/' );
|
||
|
||
*pszDest = '\0';
|
||
pszDest = strrchr( pszPath, '/') + 1;
|
||
}
|
||
|
||
*pszDest = '\0';
|
||
break;
|
||
|
||
default :
|
||
DBG_ASSERT( !"Invalid action code in state table!" );
|
||
State = 4;
|
||
*pszDest++ = '\0';
|
||
break;
|
||
}
|
||
|
||
State &= ~ACTION_MASK;
|
||
if ( !fDBCS )
|
||
{
|
||
if ( IsDBCSLeadByte( ch ) )
|
||
{
|
||
fDBCS = TRUE;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
fDBCS = FALSE;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Remove any trailing slash.
|
||
//
|
||
|
||
pszDest -= 2;
|
||
|
||
if( ( strlen( pszPath ) > 3 ) && ( *pszDest == '/' ) )
|
||
{
|
||
*pszDest = '\0';
|
||
}
|
||
|
||
//
|
||
// If the final path elements consists solely of dots and spaces, remove them.
|
||
//
|
||
|
||
while( strlen( pszPath ) > 3 )
|
||
{
|
||
pszDest = strrchr( pszPath, '/');
|
||
DBG_ASSERT( pszDest != NULL );
|
||
|
||
pszHead = pszDest;
|
||
pszDest++;
|
||
|
||
while( ch = *pszDest++ )
|
||
{
|
||
if( (ch != '.') && (ch != ' ') )
|
||
{
|
||
break;
|
||
}
|
||
}
|
||
|
||
if( ch == '\0' )
|
||
{
|
||
//
|
||
// this is probably dead code left over from
|
||
// when we used physical paths.
|
||
//
|
||
if( pszHead == ( pszPath + 2 ) )
|
||
{
|
||
pszHead++;
|
||
}
|
||
// end of dead code
|
||
|
||
//
|
||
// Don't remove the first '/'
|
||
//
|
||
if ( pszHead == pszPath )
|
||
{
|
||
pszHead++;
|
||
}
|
||
|
||
*pszHead = '\0';
|
||
}
|
||
else
|
||
{
|
||
break;
|
||
}
|
||
}
|
||
|
||
} // VirtualpSanitizePath
|
||
|