windows-nt/Source/XPSP1/NT/admin/admt/workobj/sdresolv.cpp
2020-09-26 16:20:57 +08:00

869 lines
29 KiB
C++

//#pragma title( "SDResolve.cpp - SDResolve: A Domain Migration Utility" )
/*
Copyright (c) 1995-1998, Mission Critical Software, Inc. All rights reserved.
===============================================================================
Module - sdresolve.cpp
System - SDResolve
Author - Christy Boles
Created - 97/07/11
Description - Routines to iterate through files, shares, and printers
when processing security on a machine.
Updates -
===============================================================================
*/
#include "stdafx.h"
#include <stdlib.h>
#include <stdio.h>
#include <iostream.h>
#include <fstream.h>
#include <assert.h>
#include "Common.hpp"
#include "ErrDct.hpp"
#include "UString.hpp"
#include "sd.hpp"
#include "sidcache.hpp"
#include "enumvols.hpp"
#include "SecObj.hpp"
#include "ealen.hpp"
#include "BkupRstr.hpp"
#include "TxtSid.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
bool enforce;
extern TErrorDct err;
extern bool silent;
extern bool IsMachineName(const LPWSTR name);
extern bool IsShareName(const LPWSTR name);
extern bool ContainsWildcard( WCHAR const * name);
#define MAX_BUFFER_LENGTH 10000
#define PRINT_BUFFER_SIZE 2000
//******************************************************************************************************
// Main routine for SDResolve
// Iterates files and directories to be resolved
void
IteratePath(
WCHAR * path, // in -path to start iterating from
SecurityTranslatorArgs * args, // in -translation settings
TSDResolveStats * stats, // in -stats (to display pathnames & pass to ResolveSD)
TSecurableObject * LC, // in -last container
TSecurableObject * LL, // in -last file
bool haswc // in -indicates whether path contains a wc character
)
{
HANDLE hFind;
WIN32_FIND_DATA findEntry;
BOOL b;
TFileSD * currSD;
bool changeLastCont;
bool changeLastLeaf;
WCHAR * appendPath = NULL;
WCHAR safepath[LEN_Path + 10];
TFileSD * LastContain = (TFileSD*) LC;
TFileSD * LastLeaf = (TFileSD*) LL;
WCHAR localPath[LEN_Path];
// this is the first (for this) dir
safecopy(safepath,path);
safecopy(localPath,path);
// Check to see if path is longer than MAX_PATH
// if so, add \\?\ to the beginning of it to
// turn off path parsing
if ( UStrLen(path) >= MAX_PATH && path[2] != L'?' )
{
WCHAR temp[LEN_Path];
if ( (path[0] == L'\\') && (path[1] == L'\\') ) // UNC name
{
UStrCpy(temp,L"\\\\?\\UNC\\");
}
else
{
UStrCpy(temp,L"\\\\?\\");
}
UStrCpy(temp + UStrLen(temp),path);
safecopy(localPath,temp);
}
appendPath = localPath + UStrLen(localPath);
if ( *(appendPath-1) == L'\\' ) // if there's already a backslash on the end of the path, don't add another one
appendPath--;
if ( ! haswc )
UStrCpy(appendPath, "\\*.*");
if ( args->LogVerbose() )
err.DbgMsgWrite(0,L"Starting IteratePath: %ls",path);
for ( b = ((hFind = FindFirstFile(localPath, &findEntry)) != INVALID_HANDLE_VALUE)
; b ; b = FindNextFile(hFind, &findEntry) )
{
if ( ! haswc)
appendPath[1] = '\0'; // restore path -- remove \*.* append
if ( ! UStrCmp((LPWSTR)findEntry.cFileName,L".") || ! UStrCmp((LPWSTR)findEntry.cFileName,L"..") )
continue; // ignore names '.' and '..'
if ( ! haswc )
UStrCpy(appendPath+1, findEntry.cFileName);
else
{
for ( WCHAR * ch = appendPath-1; ch >= path && *ch != L'\\' ; ch-- )
;
UStrCpy(ch+1,findEntry.cFileName);
}
if ( ((TAccountCache *)args->Cache())->IsCancelled() )
{
break;
}
currSD = new TFileSD(localPath);
stats->DisplayPath(localPath);
if ( !currSD || !currSD->HasSecurity() )
{
//err.MsgWrite(0,"Error: Couldn't get the SD");
}
else
{
if ( findEntry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) // if dir
{
// resolve this container & iterate next container
changeLastCont = currSD->ResolveSD(args,stats,directory,LastContain);
if ( changeLastCont )
{
if ( LastContain && LastContain != LC )
{
delete LastContain;
}
LastContain = currSD;
}
else
{
delete currSD;
}
IteratePath(localPath,args,stats,LastContain,LastLeaf,false);
}
else
{
// iterate this file with last
changeLastLeaf = currSD->ResolveSD(args,stats,file,LastLeaf);
if ( changeLastLeaf )
{
if ( LastLeaf && LastLeaf != LL )
{
delete LastLeaf;
}
LastLeaf = currSD;
}
else
{
delete currSD;
}
}
}
}
if ( LastContain && LastContain != LC )
{
delete LastContain;
}
if ( LastLeaf && LastLeaf != LL )
{
delete LastLeaf;
}
appendPath[0] = '\0';
DWORD rc = GetLastError();
if ( args->LogVerbose() )
err.DbgMsgWrite(0,L"Closing IteratePath %S",safepath);
FindClose(hFind);
switch ( rc )
{
case ERROR_NO_MORE_FILES:
case 0:
break;
default:
err.SysMsgWrite(ErrE, rc, DCT_MSG_FIND_FILE_FAILED_SD, path, rc);
}
return;
}
DWORD
ResolvePrinter(
PRINTER_INFO_4 * pPrinter, // in - printer information
SecurityTranslatorArgs * args, // in - translation settings
TSDResolveStats * stats // in - stats
)
{
DWORD rc = 0;
// DWORD needed = 0;
TPrintSD sd(pPrinter->pPrinterName);
if ( sd.GetSecurity() )
{
sd.ResolveSD(args,stats,printer,NULL);
}
return rc;
}
int
ServerResolvePrinters(
WCHAR const * server, // in -translate the printers on this server
SecurityTranslatorArgs * args, // in -translation settings
TSDResolveStats * stats // in -stats
)
{
DWORD rc = 0;
PRINTER_INFO_4 * pInfo = NULL;
BYTE * buffer = new BYTE[PRINT_BUFFER_SIZE];
DWORD cbNeeded = PRINT_BUFFER_SIZE;
DWORD nReturned = 0;
if (!buffer)
return ERROR_NOT_ENOUGH_MEMORY;
if (! EnumPrinters(PRINTER_ENUM_LOCAL,NULL,4,buffer,PRINT_BUFFER_SIZE,&cbNeeded,&nReturned) )
{
rc = GetLastError();
if ( rc == ERROR_INSUFFICIENT_BUFFER )
{
// try again with a bigger buffer size
delete buffer;
buffer = new BYTE[cbNeeded];
if (!buffer)
return ERROR_NOT_ENOUGH_MEMORY;
if (! EnumPrinters(PRINTER_ENUM_LOCAL,NULL,4,buffer,cbNeeded,&cbNeeded,&nReturned) )
{
rc = GetLastError();
}
}
}
if ( ! rc )
{
pInfo = (PRINTER_INFO_4 *)buffer;
for ( DWORD i = 0 ; i < nReturned ; i++ )
{
ResolvePrinter(&(pInfo[i]),args,stats);
}
}
else
{
err.SysMsgWrite(ErrE,rc,DCT_MSG_ERROR_ENUMERATING_LOCAL_PRINTERS_D,rc);
}
delete buffer;
return rc;
}
int
ServerResolveShares(
WCHAR const * server, // in -enumerate and translate the shares on this server
SecurityTranslatorArgs * args, // in -translation settings
TSDResolveStats * stats // in -stats (to display pathnames & pass to ResolveSD)
)
{
DWORD rc = 0;
DWORD numRead = 0;
DWORD totalEntries = 0;
DWORD resumeHandle = 0;
SHARE_INFO_0 * bufPtr = NULL;
WCHAR serverName[LEN_Computer];
WCHAR fullPath[LEN_Path];
WCHAR * pServerName = serverName;
DWORD ttlRead = 0;
if ( server )
{
safecopy(serverName,server);
}
else
{
pServerName = NULL;
}
do
{
rc = NetShareEnum(pServerName,0,(LPBYTE *)&bufPtr,MAX_BUFFER_LENGTH,&numRead,&totalEntries,&resumeHandle);
if ( ! rc || rc == ERROR_MORE_DATA )
{
for ( UINT i = 0 ; i < numRead ; i++ )
{
// Process the SD
if ( pServerName )
{
swprintf(fullPath,L"%s\\%s",pServerName,bufPtr[i].shi0_netname);
}
else
{
swprintf(fullPath,L"%s",bufPtr[i].shi0_netname);
}
TShareSD tSD(fullPath);
if ( tSD.HasSecurity() )
{
stats->DisplayPath(fullPath,TRUE);
tSD.ResolveSD(args,stats,share,NULL);
}
}
ttlRead += numRead;
resumeHandle = ttlRead;
NetApiBufferFree(bufPtr);
}
} while ( rc == ERROR_MORE_DATA && numRead < totalEntries );
if ( rc && rc != ERROR_MORE_DATA )
err.SysMsgWrite(ErrE,rc,DCT_MSG_SHARE_ENUM_FAILED_SD,server,rc);
return rc;
}
void
ResolveFilePath(
SecurityTranslatorArgs * args, // in - translation options
TSDResolveStats * Stats, // in - class to display stats
WCHAR * path, // in - path name
bool validAlone, // in - whether this object exists (false for share names and volume roots)
bool containsWC, // in - true if path contains wildcard
bool iscontainer // in - whether the starting path is a container
)
{
TFileSD * pSD;
if ( args->LogVerbose() )
err.MsgWrite(0,DCT_MSG_PROCESSING_S,path);
Stats->DisplayPath(path);
if ( validAlone && ! containsWC )
{
pSD = new TFileSD(path);
if (!pSD)
return;
if ( pSD->HasSecurity() )
pSD->ResolveSD(args,
Stats,
iscontainer?directory:file,
NULL);
delete pSD;
}
if ( iscontainer || containsWC )
{
IteratePath(path,
args,
Stats,
NULL,
NULL,
containsWC);
}
if ( args->Cache()->IsCancelled() )
{
err.MsgWrite(0,DCT_MSG_OPERATION_ABORTED);
}
}
void WriteOptions(SecurityTranslatorArgs * args)
{
//* WCHAR cmd[1000] = L"SecurityTranslation ";;
WCHAR cmd[1000];
WCHAR arg[300];
UStrCpy(cmd, GET_STRING(IDS_STOptions_Start));
if ( args->NoChange() )
{
//* UStrCpy(cmd +UStrLen(cmd), L"WriteChanges:No ");
UStrCpy(cmd +UStrLen(cmd), GET_STRING(IDS_STOptions_WriteChng));
}
if ( args->TranslateFiles() )
{
//* UStrCpy(cmd +UStrLen(cmd), L"Files:Yes ");
UStrCpy(cmd +UStrLen(cmd), GET_STRING(IDS_STOptions_Files));
}
if ( args->TranslateShares() )
{
//* UStrCpy(cmd + UStrLen(cmd),L"Shares:Yes ");
UStrCpy(cmd + UStrLen(cmd),GET_STRING(IDS_STOptions_Shares));
}
if ( args->TranslateLocalGroups() )
{
//* UStrCpy(cmd + UStrLen(cmd),L"LGroups:Yes ");
UStrCpy(cmd + UStrLen(cmd),GET_STRING(IDS_STOptions_LocalGroup));
}
if ( args->TranslateUserRights() )
{
// UStrCpy(cmd + UStrLen(cmd),L"UserRights:Yes ");
UStrCpy(cmd + UStrLen(cmd),GET_STRING(IDS_STOptions_URights));
}
if ( args->TranslatePrinters() )
{
// UStrCpy(cmd + UStrLen(cmd),L"UserRights:Yes ");
UStrCpy(cmd + UStrLen(cmd),GET_STRING(IDS_STOptions_Printers));
}
if ( args->TranslateUserProfiles() )
{
//* UStrCpy(cmd + UStrLen(cmd),L"Profiles:Yes ");
UStrCpy(cmd + UStrLen(cmd),GET_STRING(IDS_STOptions_Profiles));
}
if ( args->TranslateRecycler() )
{
//* UStrCpy(cmd + UStrLen(cmd),L"RecycleBin:Yes ");
UStrCpy(cmd + UStrLen(cmd),GET_STRING(IDS_STOptions_RBin));
}
if ( *args->LogFile() )
{
//* wsprintf(arg,L"LogFile:%S ",args->LogFile());
wsprintf(arg,GET_STRING(IDS_STOptions_LogName),args->LogFile());
UStrCpy(cmd +UStrLen(cmd), arg);
}
if ( args->TranslationMode() == ADD_SECURITY )
{
//* UStrCpy(cmd +UStrLen(cmd), L"TranslationMode:Add ");
UStrCpy(cmd +UStrLen(cmd), GET_STRING(IDS_STOptions_AddMode));
}
else if ( args->TranslationMode() == REMOVE_SECURITY )
{
//* UStrCpy(cmd +UStrLen(cmd), L"TranslationMode:Remove ");
UStrCpy(cmd +UStrLen(cmd), GET_STRING(IDS_STOptions_RemoveMode));
}
else
{
//* UStrCpy(cmd + UStrLen(cmd),L"TranslationMode:Replace ");
UStrCpy(cmd + UStrLen(cmd),GET_STRING(IDS_STOptions_ReplaceMode));
}
wsprintf(arg,L"%s %s ",args->Source(), args->Target());
UStrCpy(cmd +UStrLen(cmd), arg);
err.MsgWrite(0,DCT_MSG_GENERIC_S,&*cmd);
}
void
TranslateRecycler(
SecurityTranslatorArgs * args, // in - translation options
TSDResolveStats * Stats, // in - class to display stats
WCHAR * path // in - drive name
)
{
err.MsgWrite(0,DCT_MSG_PROCESSING_RECYCLER_S,path);
WCHAR folder[LEN_Path];
WCHAR const * recycler = L"RECYCLER";
WCHAR strSid[200];
WCHAR srcPath[LEN_Path];
WCHAR tgtPath[LEN_Path];
DWORD lenStrSid = DIM(strSid);
_wfinddata_t fData;
// long hRecycler;
LONG_PTR hRecycler;
PSID pSidSrc = NULL, pSidTgt = NULL;
TRidNode * pNode;
DWORD rc = 0;
swprintf(folder,L"%s\\%s\\*",path,recycler);
long mode = args->TranslationMode();
// Windows 2000 checks the SD for the recycle bin when the recycle bin is opened. If the SD does not match the
// default template (permissions for user, admin, and system), Windows displays a message that the recycle bin is corrupt.
// This change may also be in NT 4 SP 7. To avoid causing this corrupt recycle bin message, we will always translate the
// recycle bins in replace mode. We will not change if we are doing a remove.
if (args->TranslationMode() != REMOVE_SECURITY)
args->SetTranslationMode(REPLACE_SECURITY);
// use _wfind to look for hidden files in the folder
for ( hRecycler = _wfindfirst(folder,&fData) ; hRecycler != -1 && ( rc == 0 ); rc = (DWORD)_wfindnext(hRecycler,&fData) )
{
pSidSrc = SidFromString(fData.name);
if ( pSidSrc )
{
err.MsgWrite(0,DCT_MSG_PROCESSING_RECYCLE_FOLDER_S,fData.name);
pNode = (TRidNode*)args->Cache()->Lookup(pSidSrc);
if ( pNode && pNode != (TRidNode*)-1 )
{
pSidTgt = args->Cache()->GetTgtSid(pNode);
// get the target directory name
GetTextualSid(pSidTgt,strSid,&lenStrSid);
if ( args->LogVerbose() )
err.DbgMsgWrite(0,L"Target sid is: %ls",strSid);
if ( ! args->NoChange() && args->TranslationMode() != REMOVE_SECURITY )
{
// rename the directory
swprintf(srcPath,L"%s\\%s\\%s",path,recycler,fData.name);
swprintf(tgtPath,L"%s\\%s\\%s",path,recycler,strSid);
if ( ! MoveFile(srcPath,tgtPath) )
{
rc = GetLastError();
if ( (rc == ERROR_ALREADY_EXISTS) && (args->TranslationMode() == REPLACE_SECURITY) )
{
// the target recycle bin already exists
// attempt to rename it with a suffix, so we can rename the new bin to the SID
WCHAR tmpPath[LEN_Path];
long ndx = 0;
do
{
swprintf(tmpPath,L"%ls%ls%ld",tgtPath,GET_STRING(IDS_RenamedRecyclerSuffix),ndx);
if (! MoveFile(tgtPath,tmpPath) )
{
rc = GetLastError();
ndx++;
}
else
{
rc = 0;
err.MsgWrite(0,DCT_MSG_RECYCLER_RENAMED_SS,tgtPath,tmpPath);
}
} while ( rc == ERROR_ALREADY_EXISTS );
if ( ! rc )
{
// we have moved the pre-existing target recycler out of the way
// now retry the rename
if (! MoveFile(srcPath,tgtPath) )
{
err.SysMsgWrite(ErrE,rc,DCT_MSG_RECYCLER_RENAME_FAILED_SD,pNode->GetAcctName(),rc);
}
else
{
err.MsgWrite(0,DCT_MSG_RECYCLER_RENAMED_SS,srcPath,tgtPath);
// run security translation on the new folder
ResolveFilePath(args,Stats,tgtPath,TRUE,FALSE,TRUE);
}
}
else
{
err.SysMsgWrite(ErrE,rc,DCT_MSG_RECYCLER_RENAME_FAILED_SD,pNode->GetAcctName(),rc);
}
}
else
{
err.SysMsgWrite(ErrE,rc,DCT_MSG_RECYCLER_RENAME_FAILED_SD,pNode->GetAcctName(),rc);
}
}
else
{
err.MsgWrite(0,DCT_MSG_RECYCLER_RENAMED_SS,srcPath,tgtPath);
// run security translation on the new folder
ResolveFilePath(args,Stats,tgtPath,TRUE,FALSE,TRUE);
}
}
FreeSid(pSidTgt);
}
FreeSid(pSidSrc);
}
}
// set the translation mode back to its original value
args->SetTranslationMode(mode);
}
// if the specified node is a normal share, this attempts to convert it to a path
// using the administrative shares
void
BuildAdminPathForShare(
TPathNode * tnode,
WCHAR * adminShare
)
{
// if all else fails, return the same name as specified in the node
UStrCpy(adminShare,tnode->GetPathName());
SHARE_INFO_502 * shInfo = NULL;
DWORD rc = 0;
WCHAR shareName[LEN_Path];
WCHAR * slash = NULL;
UStrCpy(shareName,tnode->GetPathName() + UStrLen(tnode->GetServerName()) +1);
slash = wcschr(shareName,L'\\');
if ( slash )
*slash = 0;
rc = NetShareGetInfo(tnode->GetServerName(),shareName,502,(LPBYTE*)&shInfo);
if ( ! rc )
{
if ( *shInfo->shi502_path )
{
// build the administrative path name for the share
UStrCpy(adminShare,tnode->GetServerName());
UStrCpy(adminShare + UStrLen(adminShare),L"\\");
UStrCpy(adminShare + UStrLen(adminShare),shInfo->shi502_path);
WCHAR * colon = wcschr(adminShare,L':');
if ( colon )
{
*colon = L'$';
UStrCpy(adminShare + UStrLen(adminShare),L"\\");
UStrCpy(adminShare + UStrLen(adminShare),slash+1);
}
else
{
// something went wrong -- revert to the given path
UStrCpy(adminShare,tnode->GetPathName());
}
}
NetApiBufferFree(shInfo);
}
}
// Main routine for resolving file and directory SD's.
int
ResolveAll(
SecurityTranslatorArgs * args, // in- translation settings
TSDResolveStats * Stats // in- counts of examined, changed objects, etc.
)
{
WCHAR * warg;
WCHAR * machine;
UINT errmode;
int retcode = 0;
TPathNode * tnode;
errmode = SetErrorMode(SEM_FAILCRITICALERRORS);
if ( ! retcode )
{
WriteOptions(args);
Stats->InitDisplay(args->NoChange());
err.MsgWrite(0,DCT_MSG_FST_STARTING);
// Process Files and Directories
if (! args->IsLocalSystem() )
{
TNodeListEnum tenum;
for (tnode = (TPathNode *)tenum.OpenFirst((TNodeList *)args->PathList()) ; tnode ; tnode = (TPathNode *)tenum.Next() )
{
DWORD rc;
BOOL needToGetBR = FALSE;
// BOOL abort = FALSE;
// BOOL firstTime = TRUE;
warg = tnode->GetPathName();
machine = GetMachineName(warg);
needToGetBR = ( args->TranslateFiles() );
if ( *tnode->GetServerName() && ! args->IsLocalSystem() )
{
warg = tnode->GetPathName();
err.MsgWrite(0,DCT_MSG_PROCESSING_S,warg);
if ( args->TranslateFiles() )
{
if ( needToGetBR )
{
GetBkupRstrPriv(tnode->GetServerName());
}
if ( IsMachineName(warg) )
{
// need to process each drive on this machine
TVolumeEnum vEnum;
rc = vEnum.Open(warg,VERIFY_PERSISTENT_ACLS,args->LogVerbose());
if ( rc )
{
err.SysMsgWrite(ErrE,rc,DCT_MSG_ERROR_ACCESSING_DRIVES_SD,warg,rc);
}
else
{
while ( warg = vEnum.Next() )
{
ResolveFilePath(args,
Stats,
warg,
false, // not valid alone
false, // no wildcard
true ); // container
}
warg = machine;
}
vEnum.Close();
}
else
{
WCHAR adminShare[LEN_Path];
// Verify that the volume is NTFS
rc = tnode->VerifyPersistentAcls();
switch ( rc )
{
case ERROR_SUCCESS:
// Process the path
// if it's a share name, process the root of the share
if( IsShareName(tnode->GetPathName()) )
{
WCHAR sharePath[LEN_Path];
swprintf(sharePath,L"%s\\.",tnode->GetPathName());
TFileSD sd(sharePath);
if ( sd.HasSecurity() )
{
sd.ResolveSD(args,
Stats,
directory,
NULL);
}
}
// if this is a normal share, convert it to an administrative share
// path, so that we can take advantage of backup/restore privileges
BuildAdminPathForShare(tnode,adminShare);
ResolveFilePath(args,
Stats,
adminShare,
!IsShareName(tnode->GetPathName()),
ContainsWildcard(tnode->GetPathName()),
tnode->IsContainer() || IsShareName(tnode->GetPathName()));
break;
case ERROR_NO_SECURITY_ON_OBJECT:
err.MsgWrite(ErrW,DCT_MSG_SKIPPING_FAT_VOLUME_S,warg);
break;
default:
err.SysMsgWrite(ErrE,rc,DCT_MSG_SKIPPING_PATH_SD,warg,rc );
break;
}
}
}
// Process the shares for this machine
if ( args->TranslateShares() )
{
if ( IsMachineName(warg) )
{
err.MsgWrite(0,DCT_MSG_PROCESSING_SHARES_S,tnode->GetServerName());
ServerResolveShares(tnode->GetServerName(),args,Stats);
}
else if ( IsShareName(warg) )
{
TShareSD sd(warg);
if ( sd.HasSecurity() )
{
if ( args->LogVerbose() )
{
err.MsgWrite(0,DCT_MSG_PROCESSING_SHARE_S,warg);
}
sd.ResolveSD(args,
Stats,
share,
NULL);
}
}
}
}
else
{
// this is a local path
// Verify that the volume is NTFS
DWORD rc2;
GetBkupRstrPriv((WCHAR*)NULL);
rc2 = tnode->VerifyPersistentAcls();
switch ( rc2 )
{
case ERROR_SUCCESS:
// Process the path
if ( args->TranslateFiles() )
{
ResolveFilePath(args,
Stats,
tnode->GetPathName(),
true, // isValidAlone
ContainsWildcard(tnode->GetPathName()),
tnode->IsContainer() );
}
break;
case ERROR_NO_SECURITY_ON_OBJECT:
err.MsgWrite(ErrW,DCT_MSG_SKIPPING_FAT_VOLUME_S,warg);
break;
default:
err.SysMsgWrite(ErrE,rc2,DCT_MSG_SKIPPING_PATH_SD,warg,rc2 );
break;
}
}
if ( machine )
{
delete machine;
machine = NULL;
}
}
tenum.Close();
}
else
{
// Translate the entire machine
err.MsgWrite(0,DCT_MSG_LOCAL_TRANSLATION);
if ( args->TranslateFiles() || args->TranslateRecycler() )
{
GetBkupRstrPriv((WCHAR const*)NULL);
// need to process each drive on this machine
TVolumeEnum vEnum;
vEnum.SetLocalMode(TRUE);
DWORD rc2 = vEnum.Open(NULL,VERIFY_PERSISTENT_ACLS,args->LogVerbose());
if ( rc2 )
{
err.SysMsgWrite(ErrE,rc2,DCT_MSG_ERROR_ACCESSING_LOCAL_DRIVES_D,rc2);
}
else
{
while ( warg = vEnum.Next() )
{
err.MsgWrite(0,DCT_MSG_PROCESSING_S,warg);
if ( args->TranslateFiles() )
{
ResolveFilePath(args,
Stats,
warg,
false, // not valid alone
false, // no wildcard
true ); // container
}
if ( args->TranslateRecycler() )
{
TranslateRecycler(args,Stats,warg);
}
}
warg = NULL;
}
vEnum.Close();
}
if ( args->TranslateShares() )
{
err.MsgWrite(0,DCT_MSG_PROCESSING_LOCAL_SHARES,NULL);
ServerResolveShares(NULL,args,Stats);
}
if ( args->TranslatePrinters() )
{
err.MsgWrite(0,DCT_MSG_PROCESSING_LOCAL_PRINTERS,NULL);
ServerResolvePrinters(NULL,args,Stats);
}
}
Stats->DisplayPath(L"");
} // end if ( ! retcode)
SetErrorMode(errmode);
return retcode;
}