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.
 
 
 
 
 
 

880 lines
25 KiB

//#pragma title( "EnumVols.cpp - Volume Enumeration" )
/*
Copyright (c) 1995-1998, Mission Critical Software, Inc. All rights reserved.
===============================================================================
Module - enumvols.hpp
System - SDResolve
Author - Christy Boles
Created - 97/06/27
Description - Classes used to generate a list of pathnames, given a list of paths and/or
machine names.
Updates -
===============================================================================
*/
#include <stdio.h>
#include "stdafx.h"
#include <lm.h>
#include <assert.h>
#include "Common.hpp"
#include "Err.hpp"
#include "ErrDct.hpp"
#include "UString.hpp"
#include "EnumVols.hpp"
#include "BkupRstr.hpp"
#define BUF_ENTRY_LENGTH (3)
extern WCHAR * // ret -machine-name prefix of pathname if pathname is a UNC path, otherwise returns NULL
GetMachineName(
const LPWSTR pathname // in -pathname from which to extract machine name
);
extern TErrorDct err;
extern bool silent;
bool // ret -true if name begins with "\\" has at least 3 total chars, and no other '\'
IsMachineName(
const LPWSTR name // in -possible machine name to check
)
{
assert( name );
WCHAR * c = NULL; // used to traverse the name (will stay NULL if prefix check fails)
if ( name[0] == L'\\' && name[1] == L'\\' ) // check for "\\" prefix
{
for ( c = name + 2 ; *c && *c != L'\\' ; c++ ) // check rest of string
;
}
return ( c && *c != L'\\' ); // <=> prefix check worked && we made it to the end of the string without hitting a '\'
}
bool // ret -true if name is of the form \\machine\share
IsShareName(
const LPWSTR name // in -string to check
)
{
assert( name );
WCHAR * c = NULL; // used to traverse the name (will stay NULL if prefix check fails)
bool skip = true;
if ( name[0] == L'\\' && name[1] == L'\\' ) // check for "\\" prefix
{
for ( c = name + 2 ; *c && (*c != L'\\' || skip) ; c++ ) // check rest of string
{
if ( *c == L'\\' )
skip = false;
}
}
return ( c && *c != L'\\' );
}
bool
IsUNCName(
const LPWSTR name // in - string to check
)
{
return ( name[0] == L'\\' && name[1] == L'\\' && name[2]!=0 );
}
bool
ContainsWildcard(
WCHAR const * string
)
{
bool wc = false;
WCHAR const * curr = string;
if ( string )
{
while ( *curr && ! wc )
{
if ( *curr == L'*'
|| *curr == L'?'
|| *curr == L'#'
)
{
wc = true;
}
curr++;
}
}
return wc;
}
/************************************************************************************
TPathNode Implementation
*************************************************************************************/
TPathNode::TPathNode(
const LPWSTR name // -in path-name for this node
)
{
assert( name ); // name should always be a valid
assert( UStrLen(name) <= MAX_PATH ); // string, shorter than MAX_PATH
safecopy(path,name);
iscontainer = true;
FindServerName();
LookForWCChars();
}
void
TPathNode::Display() const
{
wprintf(L"%s\n",path);
wprintf(L"%s\n",server);
}
void
TPathNode::LookForWCChars()
{
ContainsWC(ContainsWildcard(path));
}
void
TPathNode::FindServerName()
{
WCHAR volRoot[MAX_PATH];
WCHAR tempName[MAX_PATH];
UINT driveType;
DWORD rc = 0;
REMOTE_NAME_INFO info;
DWORD sizeBuffer = (sizeof info);
WCHAR * machine;
if ( IsMachineName(path) )
{
safecopy(server,path);
}
else
{
safecopy(tempName,path);
if ( path[0] != L'\\' || path[1] != L'\\' ) // get the unc name
{
swprintf(volRoot, L"%-3.3s", path);
driveType = GetDriveType(volRoot);
switch ( driveType )
{
case DRIVE_REMOTE:
rc = WNetGetUniversalName(volRoot,
REMOTE_NAME_INFO_LEVEL,
(PVOID)&info,
&sizeBuffer);
switch ( rc )
{
case 0:
safecopy(tempName, info.lpUniversalName);
swprintf(volRoot,L"%s\\%s",tempName,path+3);
safecopy(path,volRoot);
break;
case ERROR_NOT_CONNECTED:
break;
default:
err.SysMsgWrite(ErrE, rc, DCT_MSG_GET_UNIVERSAL_NAME_FAILED_SD,
path, rc);
}
break;
}
}
machine = GetMachineName(path);
if ( machine )
{
safecopy(server,machine);
delete [] machine;
}
else
{
server[0] = 0;
}
}
}
DWORD // ret-0=path exists, ERROR_PATH_NOT_FOUND=path does not exist
TPathNode::VerifyExists()
{
DWORD rc = 0;
WCHAR wname[MAX_PATH];
int len;
HANDLE hFind;
WIN32_FIND_DATAW findEntry;
SERVER_INFO_100 * servInfo = NULL;
SHARE_INFO_0 * shareInfo = NULL;
safecopy(wname,path);
if ( IsMachineName(wname) )
{
rc = NetServerGetInfo(wname,100,(LPBYTE *)&servInfo);
switch ( rc )
{
case NERR_Success:
break;
case ERROR_BAD_NETPATH:
rc = ERROR_PATH_NOT_FOUND;
break;
default:
err.SysMsgWrite(ErrW,rc,DCT_MSG_SERVER_GETINFO_FAILED_SD,wname,rc);
break;
}
if ( servInfo )
{
NetApiBufferFree(servInfo);
}
}
else if ( IsShareName(wname) )
{
int ch;
for ( ch = 2; wname[ch]!= L'\\' && wname[ch] ; ch++ )
;
MCSVERIFY(wname[ch] == L'\\' );
wname[ch] = 0;
rc = NetShareGetInfo(wname,wname+ch+1,0,(LPBYTE *)&shareInfo);
wname[ch] = L'\\';
switch ( rc )
{
case NERR_NetNameNotFound:
rc = ERROR_PATH_NOT_FOUND;
break;
case ERROR_SUCCESS:
NetApiBufferFree(shareInfo);
break;
default:
err.SysMsgWrite(ErrW,rc,DCT_MSG_SHARE_GETINFO_FAILED_SD,wname,rc);
break;
}
}
else
{
iscontainer = false;
if ( wname[len = UStrLen(wname) - 1] == '\\' ) // len is the index of the last character (before NULL)
{
wname[len] = '\0'; // remove trailing backslash
len--;
}
// do a 'find' on this file w/o wildcards, in case it is a file
hFind = FindFirstFileW(wname, &findEntry);
if ( hFind == INVALID_HANDLE_VALUE )
{ // it's not a file, lets see if it's a directory
// do a find with \*.* appended
validalone = false;
UStrCpy(wname + len + 1,"\\*.*",DIM(wname) - len);
hFind = FindFirstFileW(wname,&findEntry);
if ( hFind == INVALID_HANDLE_VALUE )
{
rc = ERROR_PATH_NOT_FOUND;
}
iscontainer = true;
wname[len+1] = 0;
}
else
{
validalone = true;
if ( findEntry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
{
iscontainer = true;
}
FindClose(hFind);
}
}
return rc;
}
DWORD // ret- 0=successful, ERROR_PRIVILEGE_NOT_HELD otherwise
TPathNode::VerifyBackupRestore()
{
DWORD rc = 0;
//get needed privileges and keep them until the agent removes itself
if ( ! GetBkupRstrPriv(server) )
{
rc = ERROR_PRIVILEGE_NOT_HELD;
}
return rc;
}
// GetRootPath finds the root path of a volume. This is needed so we can call
// GetVolumeInformation to find out things like whether this volume supports ACLs
// This is fairly simplistic, and works by counting the backslashes in the path
DWORD // ret- 0 or OS return code
GetRootPath(
WCHAR * rootpath, // out- path to root of volume
WCHAR const * path // in - path within some volume
)
{
DWORD rc = 0;
DWORD i = 0;
DWORD slashcount = 1;
bool unc = false;
WCHAR tempPath[MAX_PATH];
SHARE_INFO_2 * sInfo;
if ( path[0] == L'\\' && path[1] == L'\\' )
{
slashcount = 4;
unc = true;
}
for (i = 0 ; path[i] && slashcount && i < DIM(tempPath)-1; i++ )
{
tempPath[i] = path[i];
if ( tempPath[i] == L'\\' )
{
slashcount--;
}
}
if ( tempPath[i-1] == L'\\' )
{
tempPath[i] = 0;
}
else
{
if (i == DIM(tempPath) - 1)
{
// if i points to the end of the buffer, truncate the last character
i--;
}
tempPath[i] = L'\\' ;
tempPath[i+1] = 0;
i++;
}
// now rootpath contains either D:\ or \\machine\share\ .
if ( unc )
{
// remove the trailing slash from the sharename
if ( tempPath[i] == 0 )
{
i--;
}
if ( tempPath[i] == L'\\' )
{
tempPath[i] = 0;
}
// find the beginning of the share name
while ( ( i > 0 ) && tempPath[i] != L'\\' )
i--;
if ( i < 3 )
{
MCSVERIFY(FALSE);
rc = ERROR_INVALID_PARAMETER;
}
else
{
tempPath[i] = 0;
}
rc = NetShareGetInfo(tempPath,tempPath+i+1,2,(LPBYTE*)&sInfo);
if ( ! rc )
{
swprintf(rootpath,L"%s\\%c$\\",tempPath,sInfo->shi2_path[0]);
NetApiBufferFree(sInfo);
}
}
else
{
UStrCpy(rootpath,tempPath);
}
return rc;
}
DWORD
TPathNode::VerifyPersistentAcls() // ret- 0=Yes, ERROR_NO_SECURITY_ON_OBJECT or OS error code
{
DWORD rc = 0;
DWORD maxcomponentlen; // will be used as args for GetVolumeInformation
DWORD flags;
UINT errmode;
WCHAR rootpath[MAX_PATH];
WCHAR fstype[MAX_PATH];
errmode = SetErrorMode(SEM_FAILCRITICALERRORS); // set this to prevent message box when
// called on removable media drives which are empty
if ( ! IsMachineName(path) )
{
rc = GetRootPath(rootpath,path);
if ( ! rc )
{
if ( !GetVolumeInformation(rootpath,NULL,0,NULL,&maxcomponentlen,&flags,fstype,DIM(fstype)) )
{
rc = GetLastError();
if ( rc != ERROR_NOT_READY )
{
err.SysMsgWrite(ErrW,GetLastError(),DCT_MSG_GET_VOLUME_INFO_FAILED_SD,rootpath,GetLastError());
}
}
else
{
if (!( FS_PERSISTENT_ACLS & flags) )
{
rc = ERROR_NO_SECURITY_ON_OBJECT;
}
}
}
}
SetErrorMode(errmode); // restore error mode to its prior state
return rc;
}
// This function is used when expanding wildcards in server names. It replaces server field with the new name,
// and if the path is a UNC, it changes the server component of the path.
void
TPathNode::SetServerName(
UCHAR const * name // in - new server name
)
{
if ( IsUNCName(path) )
{
WCHAR newpath[MAX_PATH];
int len = UStrLen(server);
swprintf(newpath,L"%S%s",name,path+len);
safecopy(path,newpath);
}
safecopy(server,name);
}
/************************************************************************************
TPathList Implementation
*************************************************************************************/
TPathList::TPathList()
{
numServers = 0;
numPaths = 0;
}
TPathList::~TPathList()
{
TPathNode * node;
for (node = (TPathNode *)Head() ; Count() ; node = (TPathNode *)Head() )
{
Remove(node);
delete node;
}
}
// enumerate the nodes in the list, and display the name of each - used for debugging purposes
void
TPathList::Display() const
{
TPathNode * node;
TNodeListEnum displayenum;
err.DbgMsgWrite(0,L"%ld servers, %ld total paths\n", numServers, numPaths);
for ( node = (TPathNode *)displayenum.OpenFirst(this) ;
node ;
node = (TPathNode *)displayenum.Next()
)
{
node->Display();
}
displayenum.Close();
}
void
TPathList::OpenEnum()
{
tenum.Open(this);
}
// Return the name from the next node in the enumeration
// Returns NULL if no more nodes in the list
// OpenEnum() must be called before calling Next();
WCHAR *
TPathList::Next()
{
TPathNode * pn = (TPathNode *)tenum.Next();
LPWSTR result;
if ( pn )
result = pn->GetPathName();
else
result = NULL;
return result;
}
void
TPathList::CloseEnum()
{
tenum.Close();
}
bool // ret -returns true if path added, false if path too long
TPathList::AddPath(
const LPWSTR path, // in -path to add to list
DWORD verifyFlags // in -indicates which types of verification to perform
)
{
TPathNode * pnode;
bool error = false;
bool messageshown = false;
DWORD rc = 0;
WCHAR fullpath[MAX_PATH];
WCHAR* pFullPathBuffer = NULL;
if ( UStrLen(path) >= MAX_PATH )
{
err.MsgWrite(ErrW,DCT_MSG_PATH_TOO_LONG_SD,path,MAX_PATH);
messageshown = true;
error = true;
return error;
}
pFullPathBuffer = _wfullpath(fullpath,path,DIM(fullpath));
if(!pFullPathBuffer)
{
err.MsgWrite(ErrW,DCT_MSG_GET_FULL_PATH_FAILED, path);
messageshown = true;
error = true;
return error;
}
pnode = new TPathNode(fullpath);
if (!pnode)
return true;
if ( ! ContainsWildcard(pnode->GetServerName()) )
{
if ( verifyFlags & VERIFY_EXISTS )
{
if ( rc = pnode->VerifyExists() )
{
error = true;
}
}
if ( !error && ( verifyFlags & VERIFY_BACKUPRESTORE) )
{
if ( rc = pnode->VerifyBackupRestore() )
{
// WCHAR * server = pnode->GetServerName();
}
}
if ( !error && (verifyFlags & VERIFY_PERSISTENT_ACLS ) )
{
rc = pnode->VerifyPersistentAcls();
if ( rc == ERROR_NO_SECURITY_ON_OBJECT )
{
err.MsgWrite(ErrW,DCT_MSG_NO_ACLS_S,fullpath);
error = true;
messageshown = true;
}
}
}
if ( ! error )
{
AddPathToList(pnode);
numPaths++; // increment count of paths
}
else if ( !messageshown )
{
// need to include an error code here.
if ( ! rc )
{
err.MsgWrite(ErrE,DCT_MSG_PATH_NOT_FOUND_S,fullpath);
}
else
{
err.SysMsgWrite(ErrE,rc,DCT_MSG_CANNOT_READ_PATH_SD,fullpath,rc);
}
delete pnode;
}
else
delete pnode;
return error;
}
void
TPathList::Clear()
{
TNodeListEnum tEnum;
TPathNode * pNode;
TPathNode * pNext;
for ( pNode = (TPathNode *)tEnum.OpenFirst(this) ; pNode ; pNode = pNext )
{
pNext = (TPathNode *)tEnum.Next();
Remove(pNode);
delete pNode;
}
}
void
TPathList::AddPathToList(
TPathNode * pNode // in - path to add to the list
)
{
// set the IsFirstPathFromMachine property
TNodeListEnum tEnum;
TPathNode * currNode;
bool machineFound = false;
WCHAR * myMachine = GetMachineName(pNode->GetPathName());
WCHAR * currMachine;
for ( currNode = (TPathNode *)tEnum.OpenFirst(this)
; currNode && !machineFound
; currNode = (TPathNode *)tEnum.Next() )
{
currMachine = GetMachineName(currNode->GetPathName());
if ( currMachine && myMachine )
{
if ( !UStrICmp(currMachine,myMachine) )
{
machineFound = true;
}
}
else
{
if ( !currMachine && ! myMachine )
{
machineFound = true;
}
}
if ( currMachine )
delete [] currMachine;
}
if ( myMachine )
delete [] myMachine;
tEnum.Close();
pNode->IsFirstPathFromMachine(!machineFound);
InsertBottom((TNode *)pNode);
}
// AddVolsOnMachine generates a list of volumes on the machine mach, checks for the administrative share
// for each volume, and adds NTFS shared volumes to the pathlist
DWORD
TVolumeEnum::Open(
WCHAR const * serv, // in - server to enumerate volumes on
DWORD verifyflgs, // in - flags indicating what to verify about each volume (i.e. NTFS)
BOOL logmsgs // in - flag whether to print diagnostic messages
)
{
NET_API_STATUS res;
if ( isOpen )
Close();
if ( serv )
safecopy(server,serv);
else
server[0] = 0;
resume_handle = 0;
pbuf = NULL;
verbose = logmsgs;
verifyFlags = verifyflgs;
errmode = SetErrorMode(SEM_FAILCRITICALERRORS); // set this to prevent message box when
// called on removable media drives which are empty
if ( ! bLocalOnly )
{
res = NetServerDiskEnum(server,0,&pbuf,MAXSIZE, &numread, &total, &resume_handle);
if (NERR_Success != res )
{
err.SysMsgWrite(ErrW, res, DCT_MSG_DRIVE_ENUM_FAILED_SD,server, res);
isOpen = FALSE;
}
if ( ! res )
{
drivelist = (WCHAR *) pbuf; // NetServerDiskEnum returns an array of
isOpen = true; // WCHAR[3] elements (of the form <DriveLetter><:><NULL>)
curr = 0;
}
}
else
{
//first call to determine the size of the buffer we need for drive strings
DWORD dwSizeNeeded = GetLogicalDriveStrings(0, NULL);
if (dwSizeNeeded != 0)
{
//add 1 character to the needed length and allocate the memory
pbuf = new BYTE[(dwSizeNeeded + 1) * sizeof(TCHAR)];
if (!pbuf)
return ERROR_NOT_ENOUGH_MEMORY;
//now get the drive strings
dwSizeNeeded = GetLogicalDriveStrings(dwSizeNeeded + 1, (WCHAR *)pbuf);
if (dwSizeNeeded != 0) //if success, save the drive string
{
drivelist = (WCHAR*)pbuf;
isOpen = true;
curr = 0;
res = 0;
}
else
{
res = GetLastError();
err.SysMsgWrite(ErrW,res,DCT_MSG_LOCAL_DRIVE_ENUM_FAILED_D,res);
}
}//end if got size needed
else
{
res = GetLastError();
err.SysMsgWrite(ErrW,res,DCT_MSG_LOCAL_DRIVE_ENUM_FAILED_D,res);
}
}
return res;
}
WCHAR *
TVolumeEnum::Next()
{
WCHAR * pValue = NULL;
WCHAR ShareName[MAX_PATH];
WCHAR rootsharename[MAX_PATH]; // this will hold "machinename\C$\"
NET_API_STATUS res;
bool found = false;
assert(isOpen);
while ( ! found )
{
if ( ( !bLocalOnly && curr < BUF_ENTRY_LENGTH * numread )
|| ( bLocalOnly && drivelist[curr] ) )
{
if ( verbose )
err.DbgMsgWrite(0,L"%C\n",drivelist[curr]);
if ( ! bLocalOnly )
{
swprintf(ShareName,L"%c$",drivelist[curr]);
res = NetShareGetInfo(server, ShareName, 1, &shareptr); // is this really necessary?
switch ( res )
{
case NERR_NetNameNotFound:
if ( verbose )
err.DbgMsgWrite(0,L"Not Shared\n");
break;
case NERR_Success:
{
if ( verbose )
err.DbgMsgWrite(0,L"Shared\n");
NetApiBufferFree(shareptr);
shareptr = NULL;
// build the complete share name
DWORD mnamelen = UStrLen(server);
WCHAR append[5] = L"\\C$\\";
append[1] = drivelist[curr]; // change the 'C' to the actual drive letter
UStrCpy(rootsharename, server, mnamelen+1);
UStrCpy(&rootsharename[mnamelen], append, 5);
if ( verbose )
err.DbgMsgWrite(0,L"Share name: %S\n",rootsharename);
}
break;
default:
err.MsgWrite(ErrW,DCT_MSG_ADMIN_SHARES_ERROR_SSD,ShareName,server,res);
break;
}
}
else
{
res = GetDriveType(&drivelist[curr]);
switch ( res )
{
case DRIVE_REMOVABLE:
case DRIVE_FIXED:
res = 0;
break;
case DRIVE_REMOTE:
err.MsgWrite(0,DCT_MSG_SKIPPING_DRIVE_REMOTE_S, &drivelist[curr]);
break;
case DRIVE_CDROM:
err.MsgWrite(0,DCT_MSG_SKIPPING_DRIVE_CDROM_S, &drivelist[curr]);
break;
case DRIVE_RAMDISK:
err.MsgWrite(0,DCT_MSG_SKIPPING_DRIVE_RAMDISK_S, &drivelist[curr]);
break;
case DRIVE_UNKNOWN:
err.MsgWrite(0,DCT_MSG_SKIPPING_DRIVE_UNKNOWN_S, &drivelist[curr]);
break;
case DRIVE_NO_ROOT_DIR:
err.MsgWrite(0,DCT_MSG_SKIPPING_DRIVE_NO_ROOT_S, &drivelist[curr]);
break;
default:
err.MsgWrite(0,DCT_MSG_SKIPPING_DRIVE_SD, &drivelist[curr],res);
break;
}
UStrCpy(rootsharename,&drivelist[curr]);
curr++;
}
if ( ! res )
{
if ( verifyFlags & VERIFY_PERSISTENT_ACLS )
{
TPathNode pnode(rootsharename);
DWORD rc = pnode.VerifyPersistentAcls();
if ( !rc )
{
safecopy(currEntry,rootsharename);
pValue = currEntry;
found = true;
}
else if ( rc == ERROR_NO_SECURITY_ON_OBJECT )
{
err.MsgWrite(0,DCT_MSG_SKIPPING_FAT_VOLUME_S,rootsharename);
}
else
{
err.SysMsgWrite(0,rc,DCT_MSG_SKIPPING_PATH_SD,rootsharename,rc);
}
}
else
{
safecopy(currEntry,rootsharename);
pValue = currEntry;
found = true;
}
}
curr += BUF_ENTRY_LENGTH;
}
else
{
break; // no more drives left
}
}
return pValue;
}
void
TVolumeEnum::Close()
{
if ( pbuf )
{
if (! bLocalOnly )
{
NetApiBufferFree(pbuf);
}
else
{
delete [] pbuf;
}
pbuf = NULL;
}
isOpen = FALSE;
SetErrorMode(errmode); // restore error mode to its prior state
}