327 lines
10 KiB
C++
327 lines
10 KiB
C++
|
/*++
|
||
|
|
||
|
Copyright (c) 2001 Microsoft Corporation
|
||
|
|
||
|
Module Name:
|
||
|
|
||
|
DfsPath.cpp
|
||
|
|
||
|
Abstract:
|
||
|
This is the implementation file for Dfs Shell path handling modules for the Dfs Shell
|
||
|
Extension object.
|
||
|
|
||
|
Author:
|
||
|
|
||
|
Environment:
|
||
|
|
||
|
NT only.
|
||
|
--*/
|
||
|
|
||
|
#include <nt.h>
|
||
|
#include <ntrtl.h>
|
||
|
#include <ntioapi.h>
|
||
|
#include <nturtl.h>
|
||
|
#include <windef.h>
|
||
|
#include <winbase.h>
|
||
|
#include <lmerr.h>
|
||
|
#include <lmcons.h>
|
||
|
#include <lmdfs.h>
|
||
|
#include <lmapibuf.h>
|
||
|
#include <tchar.h>
|
||
|
#include "DfsPath.h"
|
||
|
|
||
|
|
||
|
//--------------------------------------------------------------------------------------------
|
||
|
//
|
||
|
// caller of this function must call free() on *o_ppszRemotePath
|
||
|
//
|
||
|
HRESULT GetRemotePath(
|
||
|
LPCTSTR i_pszPath,
|
||
|
PTSTR *o_ppszRemotePath
|
||
|
)
|
||
|
{
|
||
|
if (!i_pszPath || !*i_pszPath || !o_ppszRemotePath)
|
||
|
return E_INVALIDARG;
|
||
|
|
||
|
if (*o_ppszRemotePath)
|
||
|
free(*o_ppszRemotePath); // to prevent memory leak
|
||
|
|
||
|
UNICODE_STRING unicodePath;
|
||
|
RtlInitUnicodeString(&unicodePath, i_pszPath);
|
||
|
|
||
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
||
|
InitializeObjectAttributes(&ObjectAttributes,
|
||
|
&unicodePath,
|
||
|
OBJ_CASE_INSENSITIVE,
|
||
|
NULL,
|
||
|
NULL);
|
||
|
|
||
|
HANDLE hFile = NULL;
|
||
|
IO_STATUS_BLOCK ioStatusBlock;
|
||
|
NTSTATUS ntStatus = NtOpenFile(&hFile,
|
||
|
SYNCHRONIZE,
|
||
|
&ObjectAttributes,
|
||
|
&ioStatusBlock,
|
||
|
FILE_SHARE_READ,
|
||
|
FILE_DIRECTORY_FILE);
|
||
|
|
||
|
if (!NT_SUCCESS(ntStatus))
|
||
|
return HRESULT_FROM_WIN32(ntStatus);
|
||
|
|
||
|
TCHAR buffer[MAX_PATH + sizeof(FILE_NAME_INFORMATION) + 1] = {0};
|
||
|
PFILE_NAME_INFORMATION pFileNameInfo = (PFILE_NAME_INFORMATION)buffer;
|
||
|
ntStatus = NtQueryInformationFile(hFile,
|
||
|
&ioStatusBlock,
|
||
|
pFileNameInfo,
|
||
|
sizeof(buffer) - sizeof(TCHAR), // leave room for the ending '\0'
|
||
|
FileNameInformation);
|
||
|
|
||
|
NtClose(hFile);
|
||
|
|
||
|
if (!NT_SUCCESS(ntStatus))
|
||
|
{
|
||
|
return HRESULT_FROM_WIN32(ntStatus);
|
||
|
}
|
||
|
|
||
|
UINT uiRequiredLength = (pFileNameInfo->FileNameLength / sizeof(TCHAR)) + 2; // +1 for prefix '\\', the other for the ending NULL
|
||
|
*o_ppszRemotePath = (PTSTR)calloc(uiRequiredLength, sizeof(TCHAR));
|
||
|
if (!*o_ppszRemotePath)
|
||
|
return E_OUTOFMEMORY;
|
||
|
|
||
|
// prepend a "\" as the Api puts only 1 "\" as in \dfsserver\dfsroot
|
||
|
(*o_ppszRemotePath)[0] = _T('\\');
|
||
|
|
||
|
RtlCopyMemory((BYTE*)&((*o_ppszRemotePath)[1]),
|
||
|
pFileNameInfo->FileName,
|
||
|
pFileNameInfo->FileNameLength);
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
bool IsPathWithDriveLetter(LPCTSTR pszPath)
|
||
|
{
|
||
|
if (!pszPath || lstrlen(pszPath) < 3)
|
||
|
return false;
|
||
|
|
||
|
if (*(pszPath+1) == _T(':') &&
|
||
|
*(pszPath+2) == _T('\\') &&
|
||
|
(*pszPath >= _T('a') && *pszPath <= _T('z') ||
|
||
|
*pszPath >= _T('A') && *pszPath <= _T('Z')))
|
||
|
return true;
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
bool
|
||
|
IsDfsPath
|
||
|
(
|
||
|
LPTSTR i_lpszDirPath,
|
||
|
LPTSTR* o_plpszEntryPath,
|
||
|
LPDFS_ALTERNATES** o_pppDfsAlternates
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Checks if the give directory path is a Dfs Path.
|
||
|
If it is then it returns the largest Dfs entry path that matches
|
||
|
this directory.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
i_lpszDirPath - The directory path.
|
||
|
|
||
|
o_plpszEntryPath - The largest Dfs entrypath is returned here.
|
||
|
if the dir path is not Dfs path then this is NULL.
|
||
|
|
||
|
o_pppDfsAlternates - If the path is a dfs path, then an array of pointers to the possible alternate
|
||
|
paths are returned here.
|
||
|
|
||
|
Return value:
|
||
|
|
||
|
true if the path is determined to be a Dfs Path
|
||
|
false if otherwise.
|
||
|
--*/
|
||
|
{
|
||
|
if (!i_lpszDirPath || !*i_lpszDirPath || !o_pppDfsAlternates || !o_plpszEntryPath)
|
||
|
{
|
||
|
return(false);
|
||
|
}
|
||
|
|
||
|
*o_pppDfsAlternates = NULL;
|
||
|
*o_plpszEntryPath = NULL;
|
||
|
|
||
|
PTSTR lpszSharePath = NULL; // this variable will hold the path in UNC format, and with an ending whack.
|
||
|
|
||
|
// Is the dir path of type d:\* or \\server\share\*?
|
||
|
if (_T('\\') == i_lpszDirPath[0])
|
||
|
{
|
||
|
// This is a UNC path.
|
||
|
|
||
|
lpszSharePath = (PTSTR)calloc(lstrlen(i_lpszDirPath) + 2, sizeof(TCHAR)); // + 2 to hold the ending whack and '\0'
|
||
|
if (!lpszSharePath)
|
||
|
return false; // out of memory
|
||
|
|
||
|
_tcscpy(lpszSharePath, i_lpszDirPath);
|
||
|
|
||
|
// will append the whack later if necessary
|
||
|
}
|
||
|
else if (!IsPathWithDriveLetter(i_lpszDirPath))
|
||
|
{
|
||
|
return false; // unknown path format
|
||
|
} else
|
||
|
{
|
||
|
// This is a drive based path.
|
||
|
TCHAR lpszDirPath[] = _T("\\??\\C:\\");
|
||
|
PTSTR lpszDrive = lpszDirPath + 4;
|
||
|
|
||
|
// Copy the drive letter,
|
||
|
*lpszDrive = *i_lpszDirPath;
|
||
|
|
||
|
// See if it is a remote drive. If not return false.
|
||
|
if (DRIVE_REMOTE != GetDriveType(lpszDrive))
|
||
|
return false;
|
||
|
|
||
|
PTSTR pszRemotePath = NULL;
|
||
|
if (FAILED(GetRemotePath(lpszDirPath, &pszRemotePath)))
|
||
|
return false;
|
||
|
|
||
|
lpszSharePath = (PTSTR)calloc(lstrlen(pszRemotePath) + lstrlen(i_lpszDirPath), sizeof(TCHAR));
|
||
|
// since we won't copy over the 2-drive-chars c:, this is long enough to hold the ending whack and '\0'
|
||
|
if (!lpszSharePath)
|
||
|
{
|
||
|
free(pszRemotePath);
|
||
|
return false; // out of memory
|
||
|
}
|
||
|
|
||
|
_tcscpy(lpszSharePath, pszRemotePath);
|
||
|
|
||
|
if (pszRemotePath[lstrlen(pszRemotePath) - 1] == _T('\\'))
|
||
|
_tcscat(lpszSharePath, i_lpszDirPath + 3);
|
||
|
else
|
||
|
_tcscat(lpszSharePath, i_lpszDirPath + 2);
|
||
|
|
||
|
// will append the whack later if necessary
|
||
|
|
||
|
free(pszRemotePath);
|
||
|
}
|
||
|
|
||
|
// Start from the end of the path taking a component at
|
||
|
// a time and see if it is a Dfs entry Path.
|
||
|
|
||
|
// now append the whack if necessary
|
||
|
// point lpszTemp at the ending whack
|
||
|
LPTSTR lpszTemp = lpszSharePath + _tcslen(lpszSharePath) - 1;
|
||
|
if (_T('\\') != *lpszTemp)
|
||
|
*(++lpszTemp) = _T('\\');
|
||
|
|
||
|
bool bIsDfsPath = false;
|
||
|
DWORD dwStatus = NERR_Success;
|
||
|
DFS_INFO_3* pDfsInfo3 = NULL;
|
||
|
|
||
|
while (lpszTemp > (lpszSharePath + 2))
|
||
|
{
|
||
|
*lpszTemp = _T('\0');
|
||
|
|
||
|
pDfsInfo3 = NULL;
|
||
|
dwStatus = NetDfsGetClientInfo(
|
||
|
lpszSharePath, // dir path as entrypath
|
||
|
NULL, // No server name required
|
||
|
NULL, // No share required
|
||
|
3L, // level 1
|
||
|
(LPBYTE *)&pDfsInfo3 // out buffer.
|
||
|
);
|
||
|
|
||
|
*lpszTemp = _T('\\'); // Restore Component.
|
||
|
|
||
|
if (NERR_Success == dwStatus)
|
||
|
{ // This component is a Dfs path.
|
||
|
// Copy entry path.
|
||
|
*o_plpszEntryPath = new TCHAR [_tcslen(lpszSharePath) + 1];
|
||
|
if (!*o_plpszEntryPath)
|
||
|
{
|
||
|
NetApiBufferFree(pDfsInfo3);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
_tcscpy(*o_plpszEntryPath, lpszSharePath);
|
||
|
|
||
|
// Allocate null terminated array for alternate pointers.
|
||
|
*o_pppDfsAlternates = new LPDFS_ALTERNATES[pDfsInfo3->NumberOfStorages + 1];
|
||
|
|
||
|
if (!*o_pppDfsAlternates)
|
||
|
{
|
||
|
NetApiBufferFree(pDfsInfo3);
|
||
|
|
||
|
delete[] *o_plpszEntryPath;
|
||
|
*o_plpszEntryPath = NULL;
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
(*o_pppDfsAlternates)[pDfsInfo3->NumberOfStorages] = NULL;
|
||
|
|
||
|
// Allocate space for each alternate.
|
||
|
DWORD i = 0;
|
||
|
for (i = 0; i < pDfsInfo3->NumberOfStorages; i++)
|
||
|
{
|
||
|
(*o_pppDfsAlternates)[i] = new DFS_ALTERNATES;
|
||
|
if (NULL == (*o_pppDfsAlternates)[i])
|
||
|
{
|
||
|
for(int j = i-1; j >= 0; j--)
|
||
|
delete (*o_pppDfsAlternates)[j];
|
||
|
|
||
|
delete[] *o_pppDfsAlternates;
|
||
|
*o_pppDfsAlternates = NULL;
|
||
|
|
||
|
delete[] *o_plpszEntryPath;
|
||
|
*o_plpszEntryPath = NULL;
|
||
|
|
||
|
NetApiBufferFree(pDfsInfo3);
|
||
|
|
||
|
free(lpszSharePath);
|
||
|
|
||
|
return(false);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Copy alternate paths.
|
||
|
for (i = 0; i < pDfsInfo3->NumberOfStorages; i++)
|
||
|
{
|
||
|
(*o_pppDfsAlternates)[i]->bstrServer = (pDfsInfo3->Storage[i]).ServerName;
|
||
|
(*o_pppDfsAlternates)[i]->bstrShare = (pDfsInfo3->Storage[i]).ShareName;
|
||
|
(*o_pppDfsAlternates)[i]->bstrAlternatePath = _T("\\\\");
|
||
|
(*o_pppDfsAlternates)[i]->bstrAlternatePath += (pDfsInfo3->Storage[i]).ServerName;
|
||
|
(*o_pppDfsAlternates)[i]->bstrAlternatePath += _T("\\");
|
||
|
(*o_pppDfsAlternates)[i]->bstrAlternatePath += (pDfsInfo3->Storage[i]).ShareName;
|
||
|
(*o_pppDfsAlternates)[i]->bstrAlternatePath += lpszTemp;
|
||
|
|
||
|
// remove the ending whack
|
||
|
(*o_pppDfsAlternates)[i]->bstrAlternatePath[_tcslen((*o_pppDfsAlternates)[i]->bstrAlternatePath) - 1] = _T('\0');
|
||
|
|
||
|
// Set replica state.
|
||
|
if ((pDfsInfo3->Storage[i]).State & DFS_STORAGE_STATE_ACTIVE)
|
||
|
{
|
||
|
(*o_pppDfsAlternates)[i]->ReplicaState = SHL_DFS_REPLICA_STATE_ACTIVE_UNKNOWN;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bIsDfsPath = true;
|
||
|
NetApiBufferFree(pDfsInfo3);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
// move lpszTemp backforward to the next whack
|
||
|
lpszTemp--;
|
||
|
while (_T('\\') != *lpszTemp && lpszTemp > (lpszSharePath + 2))
|
||
|
{
|
||
|
lpszTemp--;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (lpszSharePath)
|
||
|
free(lpszSharePath);
|
||
|
|
||
|
return(bIsDfsPath);
|
||
|
}
|
||
|
//----------------------------------------------------------------------------------
|