//#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 #include "stdafx.h" #include #include #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 <:>) 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 }