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.
1401 lines
38 KiB
1401 lines
38 KiB
/*++
|
|
|
|
Copyright (c) 1998 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
cmds1.c
|
|
|
|
Abstract:
|
|
|
|
This module implements miscellaneous commands.
|
|
|
|
Author:
|
|
|
|
Wesley Witt (wesw) 21-Oct-1998
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "cmdcons.h"
|
|
#pragma hdrstop
|
|
|
|
BOOLEAN AllowWildCards;
|
|
|
|
NTSTATUS
|
|
RcSetFileAttributes(
|
|
LPCWSTR lpFileName,
|
|
DWORD dwFileAttributes
|
|
);
|
|
|
|
NTSTATUS
|
|
RcSetFileCompression(
|
|
LPCWSTR szFileName,
|
|
BOOLEAN bCompress
|
|
);
|
|
|
|
NTSTATUS
|
|
RcGetFileAttributes(
|
|
LPCWSTR lpFileName,
|
|
PULONG FileAttributes
|
|
);
|
|
|
|
BOOLEAN
|
|
pRcCmdEnumDelFiles(
|
|
IN LPCWSTR Directory,
|
|
IN PFILE_BOTH_DIR_INFORMATION FileInfo,
|
|
OUT NTSTATUS *Status,
|
|
IN PWCHAR DosDirectorySpec
|
|
);
|
|
|
|
|
|
|
|
ULONG
|
|
RcCmdType(
|
|
IN PTOKENIZED_LINE TokenizedLine
|
|
)
|
|
{
|
|
LPCWSTR Arg;
|
|
HANDLE FileHandle;
|
|
HANDLE SectionHandle;
|
|
PVOID ViewBase;
|
|
ULONG FileSize;
|
|
ULONG rc;
|
|
ULONG cbText;
|
|
WCHAR *pText;
|
|
NTSTATUS Status;
|
|
|
|
|
|
if (RcCmdParseHelp( TokenizedLine, MSG_TYPE_HELP )) {
|
|
return 1;
|
|
}
|
|
|
|
//
|
|
// There should be a token for TYPE and one for the arg.
|
|
//
|
|
ASSERT(TokenizedLine->TokenCount == 2);
|
|
|
|
//
|
|
// Get the argument and convert it into a full NT pathname.
|
|
//
|
|
Arg = TokenizedLine->Tokens->Next->String;
|
|
if (!RcFormFullPath(Arg,_CmdConsBlock->TemporaryBuffer,FALSE)) {
|
|
RcMessageOut(MSG_INVALID_PATH);
|
|
return 1;
|
|
}
|
|
|
|
if (!RcIsPathNameAllowed(_CmdConsBlock->TemporaryBuffer,TRUE,FALSE)) {
|
|
RcMessageOut(MSG_ACCESS_DENIED);
|
|
return 1;
|
|
}
|
|
|
|
//
|
|
// Get the argument and convert it into a full NT pathname.
|
|
//
|
|
if (!RcFormFullPath(Arg,_CmdConsBlock->TemporaryBuffer,TRUE)) {
|
|
RcMessageOut(MSG_INVALID_PATH);
|
|
return 1;
|
|
}
|
|
|
|
//
|
|
// Map in the entire file.
|
|
//
|
|
FileHandle = NULL;
|
|
Status = SpOpenAndMapFile(
|
|
_CmdConsBlock->TemporaryBuffer,
|
|
&FileHandle,
|
|
&SectionHandle,
|
|
&ViewBase,
|
|
&FileSize,
|
|
FALSE
|
|
);
|
|
|
|
if( !NT_SUCCESS(Status) ) {
|
|
RcNtError(Status,MSG_CANT_OPEN_FILE);
|
|
return 1;
|
|
}
|
|
|
|
//
|
|
// See if we think the file is Unicode. We think it's Unicode
|
|
// if it's even length and starts with the Unicode text marker.
|
|
//
|
|
pText = ViewBase;
|
|
cbText = FileSize;
|
|
|
|
try {
|
|
if( (cbText >= sizeof(WCHAR)) && (*pText == 0xfeff) && !(cbText & 1) ) {
|
|
//
|
|
// Assume it's already unicode.
|
|
//
|
|
pText = SpMemAlloc(cbText);
|
|
RtlCopyMemory(pText,(WCHAR *)ViewBase+1,cbText-sizeof(WCHAR));
|
|
pText[cbText/sizeof(WCHAR)] = 0;
|
|
|
|
} else {
|
|
//
|
|
// It's not Unicode. Convert it from ANSI to Unicode.
|
|
//
|
|
// Allocate a buffer large enough to hold the maximum
|
|
// unicode text. This max size occurs when
|
|
// every character is single-byte, and this size is
|
|
// equal to exactly double the size of the single-byte text.
|
|
//
|
|
pText = SpMemAlloc((cbText+1)*sizeof(WCHAR));
|
|
RtlZeroMemory(pText,(cbText+1)*sizeof(WCHAR));
|
|
|
|
Status = RtlMultiByteToUnicodeN(
|
|
pText, // output: newly allocated buffer
|
|
cbText * sizeof(WCHAR), // max size of output
|
|
&cbText, // receives # bytes in unicode text
|
|
ViewBase, // input: ANSI text (mapped file)
|
|
cbText // size of input
|
|
);
|
|
}
|
|
}except(IN_PAGE_ERROR) {
|
|
Status = STATUS_IN_PAGE_ERROR;
|
|
}
|
|
|
|
if( NT_SUCCESS(Status) ) {
|
|
pRcEnableMoreMode();
|
|
RcTextOut(pText);
|
|
pRcDisableMoreMode();
|
|
} else {
|
|
RcNtError(Status,MSG_CANT_READ_FILE);
|
|
}
|
|
|
|
if( pText != ViewBase ) {
|
|
SpMemFree(pText);
|
|
}
|
|
SpUnmapFile(SectionHandle,ViewBase);
|
|
ZwClose(FileHandle);
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
ULONG
|
|
RcCmdDelete(
|
|
IN PTOKENIZED_LINE TokenizedLine
|
|
)
|
|
{
|
|
WCHAR *Final;
|
|
BOOLEAN Confirm = FALSE;
|
|
OBJECT_ATTRIBUTES Obja;
|
|
UNICODE_STRING UnicodeString;
|
|
NTSTATUS Status;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
HANDLE Handle;
|
|
PWSTR DelSpec = NULL;
|
|
PWSTR DosDelSpec = NULL;
|
|
WCHAR Text[2];
|
|
PWSTR YesNo = NULL;
|
|
ULONG rc;
|
|
|
|
|
|
if (RcCmdParseHelp( TokenizedLine, MSG_DELETE_HELP )) {
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// Fetch the spec for the file to be deleted and convert it
|
|
// into a fully-qualified NT-style path.
|
|
//
|
|
if (!RcFormFullPath(TokenizedLine->Tokens->Next->String,_CmdConsBlock->TemporaryBuffer,TRUE)) {
|
|
RcMessageOut(MSG_INVALID_PATH);
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// Leave room for appending * if necessary.
|
|
//
|
|
DelSpec = SpMemAlloc((wcslen(_CmdConsBlock->TemporaryBuffer)+3)*sizeof(WCHAR));
|
|
wcscpy(DelSpec,_CmdConsBlock->TemporaryBuffer);
|
|
|
|
//
|
|
// Do the same thing, except now we want the DOS-style name.
|
|
// This is used for printing in case of errors.
|
|
//
|
|
if (!RcFormFullPath(TokenizedLine->Tokens->Next->String,_CmdConsBlock->TemporaryBuffer,FALSE)) {
|
|
RcMessageOut(MSG_INVALID_PATH);
|
|
goto exit;
|
|
}
|
|
|
|
DosDelSpec = SpMemAlloc((wcslen(_CmdConsBlock->TemporaryBuffer)+3)*sizeof(WCHAR));
|
|
wcscpy(DosDelSpec,_CmdConsBlock->TemporaryBuffer);
|
|
|
|
//
|
|
// see if the user is authorized to delete this file
|
|
//
|
|
if (!RcIsPathNameAllowed(_CmdConsBlock->TemporaryBuffer,TRUE,FALSE)) {
|
|
RcMessageOut(MSG_ACCESS_DENIED);
|
|
goto exit;
|
|
}
|
|
|
|
if (RcDoesPathHaveWildCards(_CmdConsBlock->TemporaryBuffer)) {
|
|
Confirm = TRUE;
|
|
if (!AllowWildCards) {
|
|
RcMessageOut(MSG_DEL_WILDCARD_NOT_SUPPORTED);
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check to see whether the target specifies a directory.
|
|
// If so, add the * so we don't need to special-case
|
|
// the confirmation message later.
|
|
//
|
|
INIT_OBJA(&Obja,&UnicodeString,DelSpec);
|
|
|
|
Status = ZwOpenFile(
|
|
&Handle,
|
|
FILE_READ_ATTRIBUTES,
|
|
&Obja,
|
|
&IoStatusBlock,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
FILE_DIRECTORY_FILE
|
|
);
|
|
if( NT_SUCCESS(Status) ) {
|
|
ZwClose(Handle);
|
|
SpConcatenatePaths(DelSpec,L"*");
|
|
SpConcatenatePaths(DosDelSpec,L"*");
|
|
Confirm = TRUE;
|
|
}
|
|
|
|
//
|
|
// Fetch yes/no text
|
|
//
|
|
YesNo = SpRetreiveMessageText(ImageBase,MSG_YESNO,NULL,0);
|
|
if (!YesNo) {
|
|
Confirm = FALSE;
|
|
}
|
|
|
|
if (!InBatchMode) {
|
|
while( Confirm ) {
|
|
RcMessageOut(MSG_CONFIRM_DELETE,DosDelSpec);
|
|
if( RcLineIn(Text,2) ) {
|
|
if( (Text[0] == YesNo[0]) || (Text[0] == YesNo[1]) ) {
|
|
//
|
|
// Wants to do it.
|
|
//
|
|
Confirm = FALSE;
|
|
} else {
|
|
if( (Text[0] == YesNo[2]) || (Text[0] == YesNo[3]) ) {
|
|
//
|
|
// Doesn't want to do it.
|
|
//
|
|
goto exit;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Trim back the DOS-style path so it's a path to the directory
|
|
// containing the file or files to be deleted.
|
|
//
|
|
*wcsrchr(DosDelSpec,L'\\') = 0;
|
|
|
|
// Perform deletion via callback.
|
|
//
|
|
Status = RcEnumerateFiles(TokenizedLine->Tokens->Next->String,
|
|
DelSpec,
|
|
pRcCmdEnumDelFiles,
|
|
DosDelSpec);
|
|
|
|
if( !NT_SUCCESS(Status) ) {
|
|
RcNtError(Status,MSG_FILE_ENUM_ERROR);
|
|
}
|
|
|
|
exit:
|
|
|
|
if (DelSpec) {
|
|
SpMemFree(DelSpec);
|
|
}
|
|
if (DosDelSpec) {
|
|
SpMemFree(DosDelSpec);
|
|
}
|
|
if (YesNo) {
|
|
SpMemFree(YesNo);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
pRcCmdEnumDelFiles(
|
|
IN LPCWSTR Directory,
|
|
IN PFILE_BOTH_DIR_INFORMATION FileInfo,
|
|
OUT NTSTATUS *Status,
|
|
IN PWCHAR DosDirectorySpec
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
HANDLE Handle;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
UNICODE_STRING UnicodeString;
|
|
OBJECT_ATTRIBUTES Obja;
|
|
WCHAR *p;
|
|
FILE_DISPOSITION_INFORMATION Disposition;
|
|
unsigned u;
|
|
|
|
*Status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// Skip directories
|
|
//
|
|
if( FileInfo->FileAttributes & FILE_ATTRIBUTE_DIRECTORY ) {
|
|
return(TRUE);
|
|
}
|
|
|
|
//
|
|
// Form fully qualified NT path of the file to be deleted.
|
|
//
|
|
u = ((wcslen(Directory)+2)*sizeof(WCHAR)) + FileInfo->FileNameLength;
|
|
p = SpMemAlloc(u);
|
|
wcscpy(p,Directory);
|
|
SpConcatenatePaths(p,FileInfo->FileName);
|
|
|
|
INIT_OBJA(&Obja,&UnicodeString,p);
|
|
|
|
status = ZwOpenFile(
|
|
&Handle,
|
|
(ACCESS_MASK)DELETE,
|
|
&Obja,
|
|
&IoStatusBlock,
|
|
FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
FILE_NON_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT
|
|
);
|
|
|
|
if( !NT_SUCCESS(status) ) {
|
|
RcTextOut(DosDirectorySpec);
|
|
RcTextOut(L"\\");
|
|
RcTextOut(FileInfo->FileName);
|
|
RcTextOut(L"\r\n");
|
|
RcNtError(status,MSG_DELETE_ERROR);
|
|
SpMemFree(p);
|
|
return(TRUE);
|
|
}
|
|
|
|
#undef DeleteFile
|
|
Disposition.DeleteFile = TRUE;
|
|
|
|
status = ZwSetInformationFile(
|
|
Handle,
|
|
&IoStatusBlock,
|
|
&Disposition,
|
|
sizeof(FILE_DISPOSITION_INFORMATION),
|
|
FileDispositionInformation
|
|
);
|
|
|
|
ZwClose(Handle);
|
|
|
|
if( !NT_SUCCESS(status) ) {
|
|
RcTextOut(DosDirectorySpec);
|
|
RcTextOut(L"\\");
|
|
RcTextOut(FileInfo->FileName);
|
|
RcTextOut(L"\r\n");
|
|
RcNtError(status,MSG_DELETE_ERROR);
|
|
}
|
|
|
|
SpMemFree(p);
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
ULONG
|
|
RcCmdRename(
|
|
IN PTOKENIZED_LINE TokenizedLine
|
|
)
|
|
{
|
|
WCHAR *Arg;
|
|
WCHAR *p,*q;
|
|
NTSTATUS Status;
|
|
ULONG rc;
|
|
|
|
|
|
//
|
|
// check for help
|
|
//
|
|
if (RcCmdParseHelp( TokenizedLine, MSG_RENAME_HELP )) {
|
|
return 1;
|
|
}
|
|
|
|
//
|
|
// There should be a token for RENAME and one each for the source and
|
|
// target names.
|
|
//
|
|
if (TokenizedLine->TokenCount != 3) {
|
|
RcMessageOut(MSG_SYNTAX_ERROR);
|
|
return 1;
|
|
}
|
|
|
|
//
|
|
// use the console's temporary buffer
|
|
//
|
|
p = _CmdConsBlock->TemporaryBuffer;
|
|
|
|
//
|
|
// process the SOURCE filename
|
|
//
|
|
Arg = TokenizedLine->Tokens->Next->String;
|
|
|
|
//
|
|
// Convert the SOURCE filname into a DOS path so we
|
|
// can verify if the path is allowed by our security restrictions.
|
|
//
|
|
if (!RcFormFullPath(Arg,p,FALSE)) {
|
|
RcMessageOut(MSG_INVALID_PATH);
|
|
return 1;
|
|
}
|
|
|
|
if (!RcIsPathNameAllowed(p,TRUE,FALSE)) {
|
|
RcMessageOut(MSG_ACCESS_DENIED);
|
|
return 1;
|
|
}
|
|
|
|
//
|
|
// Convert the SOURCE filename into a fully qualified
|
|
// NT-style path name.
|
|
//
|
|
if (!RcFormFullPath(Arg,p,TRUE)) {
|
|
RcMessageOut(MSG_INVALID_PATH);
|
|
return 1;
|
|
}
|
|
|
|
//
|
|
// using the same buffer for the TARGET name
|
|
//
|
|
q = p + wcslen(p) + 1;
|
|
|
|
//
|
|
// get the TARGET file name
|
|
//
|
|
Arg = TokenizedLine->Tokens->Next->Next->String;
|
|
|
|
//
|
|
// Verify that the TARGET filename does not contain
|
|
// any path seperator characters or drive specifier
|
|
// characters.
|
|
//
|
|
if( wcschr(Arg,L'\\') ) {
|
|
RcMessageOut(MSG_SYNTAX_ERROR);
|
|
return 1;
|
|
}
|
|
if( RcIsAlpha(Arg[0]) && (Arg[1] == L':') ) {
|
|
RcMessageOut(MSG_SYNTAX_ERROR);
|
|
return 1;
|
|
}
|
|
|
|
//
|
|
// Convert the DESTINATION filename into a DOS path so we
|
|
// can verify if the path is allowed by our security restrictions.
|
|
//
|
|
if (!RcFormFullPath(Arg,q,FALSE)) {
|
|
RcMessageOut(MSG_INVALID_PATH);
|
|
return 1;
|
|
}
|
|
|
|
if (!RcIsPathNameAllowed(q,TRUE,FALSE)) {
|
|
RcMessageOut(MSG_ACCESS_DENIED);
|
|
return 1;
|
|
}
|
|
|
|
//
|
|
// Convert the SOURCE filename into a fully qualified
|
|
// NT-style path name.
|
|
//
|
|
if (!RcFormFullPath(Arg,q,TRUE)) {
|
|
RcMessageOut(MSG_INVALID_PATH);
|
|
return 1;
|
|
}
|
|
|
|
//
|
|
// OK, looks like a plain filename specification.
|
|
// Glom it onto the end of the relevent part of the
|
|
// source specification so we have 2 fully qualified names.
|
|
//
|
|
// wcscpy(q,p);
|
|
// wcscpy(wcsrchr(q,L'\\')+1,Arg);
|
|
|
|
//
|
|
// Call worker routine to actually do the rename.
|
|
//
|
|
Status = SpRenameFile(p,q,TRUE);
|
|
|
|
if( !NT_SUCCESS(Status) ) {
|
|
RcNtError(Status,MSG_RENAME_ERROR, Arg);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
ULONG
|
|
RcCmdMkdir(
|
|
IN PTOKENIZED_LINE TokenizedLine
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
UNICODE_STRING UnicodeString;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
HANDLE Handle;
|
|
OBJECT_ATTRIBUTES Obja;
|
|
ULONG rc;
|
|
|
|
|
|
if (RcCmdParseHelp( TokenizedLine, MSG_MAKEDIR_HELP )) {
|
|
return 1;
|
|
}
|
|
|
|
//
|
|
// There should be a token for MKDIR and one for the target.
|
|
//
|
|
ASSERT(TokenizedLine->TokenCount == 2);
|
|
|
|
//
|
|
// Convert the given arg into a fully qualified NT path specification.
|
|
//
|
|
if (!RcFormFullPath(TokenizedLine->Tokens->Next->String,_CmdConsBlock->TemporaryBuffer,FALSE)) {
|
|
RcMessageOut(MSG_INVALID_PATH);
|
|
return 1;
|
|
}
|
|
|
|
if (!RcIsPathNameAllowed(_CmdConsBlock->TemporaryBuffer,TRUE,TRUE)) {
|
|
RcMessageOut(MSG_ACCESS_DENIED);
|
|
return 1;
|
|
}
|
|
|
|
//
|
|
// Convert the given arg into a fully qualified NT path specification.
|
|
//
|
|
if (!RcFormFullPath(TokenizedLine->Tokens->Next->String,_CmdConsBlock->TemporaryBuffer,TRUE)) {
|
|
RcMessageOut(MSG_INVALID_PATH);
|
|
return 1;
|
|
}
|
|
|
|
//
|
|
// Create the directory.
|
|
//
|
|
INIT_OBJA(&Obja,&UnicodeString,_CmdConsBlock->TemporaryBuffer);
|
|
|
|
Status = ZwCreateFile(
|
|
&Handle,
|
|
FILE_LIST_DIRECTORY | SYNCHRONIZE,
|
|
&Obja,
|
|
&IoStatusBlock,
|
|
NULL,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
FILE_CREATE,
|
|
FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT,
|
|
NULL,
|
|
0
|
|
);
|
|
|
|
if( NT_SUCCESS(Status) ) {
|
|
ZwClose(Handle);
|
|
} else {
|
|
RcNtError(Status,MSG_CREATE_DIR_FAILED,TokenizedLine->Tokens->Next->String);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
ULONG
|
|
RcCmdRmdir(
|
|
IN PTOKENIZED_LINE TokenizedLine
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
HANDLE Handle;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
UNICODE_STRING UnicodeString;
|
|
OBJECT_ATTRIBUTES Obja;
|
|
FILE_DISPOSITION_INFORMATION Disposition;
|
|
ULONG rc;
|
|
|
|
|
|
if (RcCmdParseHelp( TokenizedLine, MSG_REMOVEDIR_HELP )) {
|
|
return 1;
|
|
}
|
|
|
|
//
|
|
// There should be a token for RMDIR and one for the target.
|
|
//
|
|
ASSERT(TokenizedLine->TokenCount == 2);
|
|
|
|
//
|
|
// Convert the given arg into a fully qualified NT path specification.
|
|
//
|
|
if (!RcFormFullPath(TokenizedLine->Tokens->Next->String,_CmdConsBlock->TemporaryBuffer,FALSE)) {
|
|
RcMessageOut(MSG_INVALID_PATH);
|
|
return 1;
|
|
}
|
|
|
|
if (!RcIsPathNameAllowed(_CmdConsBlock->TemporaryBuffer,TRUE,TRUE)) {
|
|
RcMessageOut(MSG_ACCESS_DENIED);
|
|
return 1;
|
|
}
|
|
|
|
//
|
|
// Convert the given arg into a fully qualified NT path specification.
|
|
//
|
|
if (!RcFormFullPath(TokenizedLine->Tokens->Next->String,_CmdConsBlock->TemporaryBuffer,TRUE)) {
|
|
RcMessageOut(MSG_INVALID_PATH);
|
|
return 1;
|
|
}
|
|
|
|
INIT_OBJA(&Obja,&UnicodeString,_CmdConsBlock->TemporaryBuffer);
|
|
|
|
Status = ZwOpenFile(
|
|
&Handle,
|
|
DELETE | SYNCHRONIZE,
|
|
&Obja,
|
|
&IoStatusBlock,
|
|
FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT
|
|
);
|
|
|
|
if( !NT_SUCCESS(Status) ) {
|
|
RcNtError(Status,MSG_RMDIR_ERROR);
|
|
return 1;
|
|
}
|
|
|
|
Disposition.DeleteFile = TRUE;
|
|
|
|
Status = ZwSetInformationFile(
|
|
Handle,
|
|
&IoStatusBlock,
|
|
&Disposition,
|
|
sizeof(FILE_DISPOSITION_INFORMATION),
|
|
FileDispositionInformation
|
|
);
|
|
|
|
ZwClose(Handle);
|
|
|
|
if( !NT_SUCCESS(Status) ) {
|
|
RcNtError(Status,MSG_RMDIR_ERROR);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
ULONG
|
|
RcCmdSetFlags(
|
|
IN PTOKENIZED_LINE TokenizedLine
|
|
)
|
|
{
|
|
if (RcCmdParseHelp( TokenizedLine, MSG_SETCMD_HELP )) {
|
|
return 1;
|
|
}
|
|
|
|
if (TokenizedLine->TokenCount == 1) {
|
|
RcTextOut( L"\r\n" );
|
|
RcMessageOut(MSG_SET_ALLOW_WILDCARDS,AllowWildCards?L"TRUE":L"FALSE");
|
|
RcMessageOut(MSG_SET_ALLOW_ALLPATHS,AllowAllPaths?L"TRUE":L"FALSE");
|
|
RcMessageOut(MSG_SET_ALLOW_REMOVABLE_MEDIA,AllowRemovableMedia?L"TRUE":L"FALSE");
|
|
RcMessageOut(MSG_SET_NO_COPY_PROMPT,NoCopyPrompt?L"TRUE":L"FALSE");
|
|
RcTextOut( L"\r\n" );
|
|
return 1;
|
|
}
|
|
|
|
//
|
|
// should have the priviledge to use the SET command
|
|
//
|
|
if (TokenizedLine->TokenCount != 4) {
|
|
RcMessageOut(MSG_SYNTAX_ERROR);
|
|
return 1;
|
|
}
|
|
|
|
if (RcGetSETCommandStatus() != TRUE) {
|
|
RcMessageOut(MSG_SETCMD_DISABLED);
|
|
return 1;
|
|
}
|
|
|
|
if (_wcsicmp(TokenizedLine->Tokens->Next->String,L"allowallpaths")==0) {
|
|
if (_wcsicmp(TokenizedLine->Tokens->Next->Next->Next->String,L"true")==0) {
|
|
AllowAllPaths = TRUE;
|
|
} else {
|
|
AllowAllPaths = FALSE;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
if (_wcsicmp(TokenizedLine->Tokens->Next->String,L"allowwildcards")==0) {
|
|
if (_wcsicmp(TokenizedLine->Tokens->Next->Next->Next->String,L"true")==0) {
|
|
AllowWildCards = TRUE;
|
|
} else {
|
|
AllowWildCards = FALSE;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
if (_wcsicmp(TokenizedLine->Tokens->Next->String,L"allowremovablemedia")==0) {
|
|
if (_wcsicmp(TokenizedLine->Tokens->Next->Next->Next->String,L"true")==0) {
|
|
AllowRemovableMedia = TRUE;
|
|
} else {
|
|
AllowRemovableMedia = FALSE;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
if (_wcsicmp(TokenizedLine->Tokens->Next->String,L"nocopyprompt")==0) {
|
|
if (_wcsicmp(TokenizedLine->Tokens->Next->Next->Next->String,L"true")==0) {
|
|
NoCopyPrompt = TRUE;
|
|
} else {
|
|
NoCopyPrompt = FALSE;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
RcMessageOut(MSG_SYNTAX_ERROR);
|
|
return 1;
|
|
}
|
|
|
|
ULONG
|
|
RcCmdAttrib(
|
|
IN PTOKENIZED_LINE TokenizedLine
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
PWCHAR AttributeString;
|
|
ULONG OldAttributes;
|
|
ULONG NewAttributes;
|
|
BOOLEAN SetAttribute;
|
|
BOOLEAN bShowHelp = TRUE;
|
|
BOOLEAN bChangeCompression = FALSE;
|
|
|
|
// "attrib -h <filename>" should clear the hidden attribute
|
|
// and not show the help
|
|
if (TokenizedLine->TokenCount > 2){
|
|
PWCHAR szSecondParam = TokenizedLine->Tokens->Next->String;
|
|
|
|
bShowHelp = !wcscmp( szSecondParam, L"/?" );
|
|
}
|
|
|
|
if (bShowHelp && RcCmdParseHelp( TokenizedLine, MSG_ATTRIB_HELP )) {
|
|
return 1;
|
|
}
|
|
|
|
if (TokenizedLine->TokenCount != 3) {
|
|
RcMessageOut(MSG_SYNTAX_ERROR);
|
|
return 1;
|
|
}
|
|
|
|
//
|
|
// Fetch the spec for the file to be attribed and convert it
|
|
// into a fully-qualified NT-style path.
|
|
//
|
|
if (!RcFormFullPath(TokenizedLine->Tokens->Next->Next->String,_CmdConsBlock->TemporaryBuffer,FALSE)) {
|
|
RcMessageOut(MSG_INVALID_PATH);
|
|
return 1;
|
|
}
|
|
|
|
//
|
|
// see if the user is authorized to change this file
|
|
//
|
|
if (!RcIsPathNameAllowed(_CmdConsBlock->TemporaryBuffer,TRUE,FALSE)) {
|
|
RcMessageOut(MSG_ACCESS_DENIED);
|
|
return 1;
|
|
}
|
|
|
|
if (!RcFormFullPath(TokenizedLine->Tokens->Next->Next->String,_CmdConsBlock->TemporaryBuffer,TRUE)) {
|
|
RcMessageOut(MSG_INVALID_PATH);
|
|
return 1;
|
|
}
|
|
|
|
Status = RcGetFileAttributes( _CmdConsBlock->TemporaryBuffer, &OldAttributes );
|
|
|
|
if( !NT_SUCCESS(Status) ) {
|
|
RcNtError(Status,MSG_CANT_OPEN_FILE);
|
|
return 1;
|
|
}
|
|
|
|
NewAttributes = OldAttributes;
|
|
|
|
for(AttributeString = TokenizedLine->Tokens->Next->String; *AttributeString; AttributeString++){
|
|
if(*AttributeString == L'+'){
|
|
SetAttribute = TRUE;
|
|
AttributeString++;
|
|
} else if(*AttributeString == L'-'){
|
|
SetAttribute = FALSE;
|
|
AttributeString++;
|
|
} else {
|
|
// attribute change should start with "+" or "-"
|
|
if (AttributeString == TokenizedLine->Tokens->Next->String) {
|
|
RcMessageOut(MSG_SYNTAX_ERROR);
|
|
return 1;
|
|
}
|
|
|
|
// use the old state for setting or resetting (for +rsh
|
|
}
|
|
|
|
switch(*AttributeString){
|
|
case L'h':
|
|
case L'H':
|
|
if (SetAttribute)
|
|
NewAttributes |= FILE_ATTRIBUTE_HIDDEN;
|
|
else
|
|
NewAttributes &= ~FILE_ATTRIBUTE_HIDDEN;
|
|
|
|
break;
|
|
|
|
case L's':
|
|
case L'S':
|
|
if (SetAttribute)
|
|
NewAttributes |= FILE_ATTRIBUTE_SYSTEM;
|
|
else
|
|
NewAttributes &= ~FILE_ATTRIBUTE_SYSTEM;
|
|
|
|
break;
|
|
|
|
case L'r':
|
|
case L'R':
|
|
if (SetAttribute)
|
|
NewAttributes |= FILE_ATTRIBUTE_READONLY;
|
|
else
|
|
NewAttributes &= ~FILE_ATTRIBUTE_READONLY;
|
|
|
|
break;
|
|
|
|
case L'a':
|
|
case L'A':
|
|
if (SetAttribute)
|
|
NewAttributes |= FILE_ATTRIBUTE_ARCHIVE;
|
|
else
|
|
NewAttributes &= ~FILE_ATTRIBUTE_ARCHIVE;
|
|
|
|
break;
|
|
|
|
case L'c':
|
|
case L'C':
|
|
bChangeCompression = TRUE;
|
|
|
|
if (SetAttribute)
|
|
NewAttributes |= FILE_ATTRIBUTE_COMPRESSED;
|
|
else
|
|
NewAttributes &= ~FILE_ATTRIBUTE_COMPRESSED;
|
|
|
|
break;
|
|
|
|
default:
|
|
RcMessageOut(MSG_SYNTAX_ERROR);
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
if (SetAttribute) {
|
|
FileAttributes |= Attribute;
|
|
} else {
|
|
FileAttributes &= ~Attribute;
|
|
}
|
|
*/
|
|
}
|
|
|
|
Status = RcSetFileAttributes( _CmdConsBlock->TemporaryBuffer, NewAttributes );
|
|
|
|
if( !NT_SUCCESS(Status) ) {
|
|
RcNtError(Status,MSG_CANT_OPEN_FILE);
|
|
} else {
|
|
if (bChangeCompression) {
|
|
BOOLEAN bCompress = (NewAttributes & FILE_ATTRIBUTE_COMPRESSED) ?
|
|
TRUE : FALSE;
|
|
|
|
Status = RcSetFileCompression(_CmdConsBlock->TemporaryBuffer, bCompress);
|
|
|
|
if ( !NT_SUCCESS(Status) )
|
|
RcNtError(Status, MSG_ATTRIB_CANNOT_CHANGE_COMPRESSION);
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
NTSTATUS
|
|
RcSetFileCompression(
|
|
LPCWSTR szFileName,
|
|
BOOLEAN bCompress
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
OBJECT_ATTRIBUTES Obja;
|
|
HANDLE Handle;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
FILE_BASIC_INFORMATION BasicInfo;
|
|
UNICODE_STRING FileName;
|
|
USHORT uCompressionType;
|
|
|
|
|
|
INIT_OBJA(&Obja,&FileName,szFileName);
|
|
|
|
//
|
|
// Open the file inhibiting the reparse behavior.
|
|
//
|
|
|
|
Status = ZwOpenFile(
|
|
&Handle,
|
|
(ACCESS_MASK)FILE_WRITE_ATTRIBUTES | SYNCHRONIZE,
|
|
&Obja,
|
|
&IoStatusBlock,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
|
FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT
|
|
);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
//
|
|
// set & reset the compression bit also
|
|
//
|
|
uCompressionType = bCompress ?
|
|
COMPRESSION_FORMAT_DEFAULT : COMPRESSION_FORMAT_NONE;
|
|
|
|
Status = ZwFsControlFile(
|
|
Handle, // file handle
|
|
NULL, // event handle
|
|
NULL, // APC rountine pointer
|
|
NULL, // APC context
|
|
&IoStatusBlock, // IO status block
|
|
FSCTL_SET_COMPRESSION, // IOCTL code
|
|
&uCompressionType, // input buffer
|
|
sizeof(uCompressionType), // input buffer length
|
|
NULL, // output buffer pointer
|
|
0); // output buffer length
|
|
|
|
DbgPrint( "ZwDeviceIoControlFile() status : %X\r\n", Status);
|
|
|
|
ZwClose(Handle);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
RcSetFileAttributes(
|
|
LPCWSTR lpFileName,
|
|
DWORD dwFileAttributes
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The attributes of a file can be set using SetFileAttributes.
|
|
|
|
Arguments:
|
|
|
|
lpFileName - Supplies the file name of the file whose attributes are to
|
|
be set.
|
|
|
|
dwFileAttributes - Specifies the file attributes to be set for the
|
|
file. Any combination of flags is acceptable except that all
|
|
other flags override the normal file attribute,
|
|
FILE_ATTRIBUTE_NORMAL.
|
|
|
|
FileAttributes Flags:
|
|
|
|
FILE_ATTRIBUTE_NORMAL - A normal file should be created.
|
|
|
|
FILE_ATTRIBUTE_READONLY - A read-only file should be created.
|
|
|
|
FILE_ATTRIBUTE_HIDDEN - A hidden file should be created.
|
|
|
|
FILE_ATTRIBUTE_SYSTEM - A system file should be created.
|
|
|
|
FILE_ATTRIBUTE_ARCHIVE - The file should be marked so that it
|
|
will be archived.
|
|
|
|
Return Value:
|
|
|
|
NTStatus of last NT call
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
OBJECT_ATTRIBUTES Obja;
|
|
HANDLE Handle;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
FILE_BASIC_INFORMATION BasicInfo;
|
|
UNICODE_STRING FileName;
|
|
USHORT uCompressionType;
|
|
|
|
|
|
INIT_OBJA(&Obja,&FileName,lpFileName);
|
|
|
|
//
|
|
// Open the file ihibiting the reparse behavior.
|
|
//
|
|
|
|
Status = ZwOpenFile(
|
|
&Handle,
|
|
(ACCESS_MASK)FILE_WRITE_ATTRIBUTES | SYNCHRONIZE,
|
|
&Obja,
|
|
&IoStatusBlock,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
|
FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT | FILE_OPEN_REPARSE_POINT
|
|
);
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
//
|
|
// Back level file systems may not support reparse points.
|
|
// We infer this is the case when the Status is STATUS_INVALID_PARAMETER.
|
|
//
|
|
|
|
if ( Status == STATUS_INVALID_PARAMETER ) {
|
|
//
|
|
// Open the file without inhibiting the reparse behavior.
|
|
//
|
|
|
|
Status = ZwOpenFile(
|
|
&Handle,
|
|
(ACCESS_MASK)FILE_WRITE_ATTRIBUTES | SYNCHRONIZE,
|
|
&Obja,
|
|
&IoStatusBlock,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
|
FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT
|
|
);
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
return Status;
|
|
}
|
|
|
|
}
|
|
else {
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Set the basic attributes
|
|
//
|
|
ZeroMemory(&BasicInfo,sizeof(BasicInfo));
|
|
BasicInfo.FileAttributes = (dwFileAttributes & FILE_ATTRIBUTE_VALID_FLAGS) | FILE_ATTRIBUTE_NORMAL;
|
|
|
|
Status = ZwSetInformationFile(
|
|
Handle,
|
|
&IoStatusBlock,
|
|
&BasicInfo,
|
|
sizeof(BasicInfo),
|
|
FileBasicInformation
|
|
);
|
|
|
|
ZwClose(Handle);
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
RcGetFileAttributes(
|
|
LPCWSTR lpFileName,
|
|
PULONG FileAttributes
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
lpFileName - Supplies the file name of the file whose attributes are to
|
|
be set.
|
|
|
|
Return Value:
|
|
|
|
Not -1 - Returns the attributes of the specified file. Valid
|
|
returned attributes are:
|
|
|
|
FILE_ATTRIBUTE_NORMAL - The file is a normal file.
|
|
|
|
FILE_ATTRIBUTE_READONLY - The file is marked read-only.
|
|
|
|
FILE_ATTRIBUTE_HIDDEN - The file is marked as hidden.
|
|
|
|
FILE_ATTRIBUTE_SYSTEM - The file is marked as a system file.
|
|
|
|
FILE_ATTRIBUTE_ARCHIVE - The file is marked for archive.
|
|
|
|
FILE_ATTRIBUTE_DIRECTORY - The file is marked as a directory.
|
|
|
|
FILE_ATTRIBUTE_REPARSE_POINT - The file is marked as a reparse point.
|
|
|
|
FILE_ATTRIBUTE_VOLUME_LABEL - The file is marked as a volume lable.
|
|
|
|
0xffffffff - The operation failed. Extended error status is available
|
|
using GetLastError.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
OBJECT_ATTRIBUTES Obja;
|
|
UNICODE_STRING FileName;
|
|
FILE_BASIC_INFORMATION BasicInfo;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
HANDLE Handle;
|
|
|
|
INIT_OBJA(&Obja,&FileName,lpFileName);
|
|
|
|
//
|
|
// Open the file inhibiting the reparse behavior.
|
|
//
|
|
|
|
Status = ZwOpenFile(
|
|
&Handle,
|
|
(ACCESS_MASK)FILE_READ_ATTRIBUTES | SYNCHRONIZE,
|
|
&Obja,
|
|
&IoStatusBlock,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
|
FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT | FILE_OPEN_REPARSE_POINT
|
|
);
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
//
|
|
// Back level file systems may not support reparse points.
|
|
// We infer this is the case when the Status is STATUS_INVALID_PARAMETER.
|
|
//
|
|
|
|
if ( Status == STATUS_INVALID_PARAMETER ) {
|
|
//
|
|
// Open the file without inhibiting the reparse behavior.
|
|
//
|
|
|
|
Status = ZwOpenFile(
|
|
&Handle,
|
|
(ACCESS_MASK)FILE_READ_ATTRIBUTES | SYNCHRONIZE,
|
|
&Obja,
|
|
&IoStatusBlock,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
|
FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT
|
|
);
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
return Status;
|
|
}
|
|
}
|
|
else {
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Query the file
|
|
//
|
|
|
|
Status = ZwQueryInformationFile(
|
|
Handle,
|
|
&IoStatusBlock,
|
|
(PVOID) &BasicInfo,
|
|
sizeof(BasicInfo),
|
|
FileBasicInformation
|
|
);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
*FileAttributes = BasicInfo.FileAttributes;
|
|
}
|
|
|
|
ZwClose( Handle );
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
|
|
ULONG
|
|
RcCmdNet(
|
|
IN PTOKENIZED_LINE TokenizedLine
|
|
)
|
|
{
|
|
WCHAR *Share;
|
|
WCHAR *User;
|
|
WCHAR *pwch;
|
|
WCHAR PasswordBuffer[64];
|
|
WCHAR Drive[3];
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
|
|
|
|
|
|
//
|
|
// check for help
|
|
//
|
|
if (RcCmdParseHelp( TokenizedLine, MSG_NET_USE_HELP )) {
|
|
return 1;
|
|
}
|
|
|
|
//
|
|
// There should be a token for NET and USE and one each for the server\share, and possible
|
|
// tokens for the /u:domainname\username and password.
|
|
//
|
|
if ((TokenizedLine->TokenCount < 3) || (TokenizedLine->TokenCount > 5)) {
|
|
RcMessageOut(MSG_SYNTAX_ERROR);
|
|
return 1;
|
|
}
|
|
|
|
//
|
|
// The only NET command supported is USE, so verify that the second token is that.
|
|
//
|
|
if (_wcsicmp(TokenizedLine->Tokens->Next->String, L"USE")){
|
|
RcMessageOut(MSG_SYNTAX_ERROR);
|
|
return 1;
|
|
}
|
|
|
|
//
|
|
// Get the first parameter to NET USE
|
|
//
|
|
Share = TokenizedLine->Tokens->Next->Next->String;
|
|
|
|
if (*Share == L'\\') { // attempt at making a connection
|
|
|
|
//
|
|
// Verify the share name parameter
|
|
//
|
|
if (*(Share+1) != L'\\') {
|
|
RcMessageOut(MSG_SYNTAX_ERROR);
|
|
return 1;
|
|
}
|
|
|
|
|
|
//
|
|
// get the user logon context
|
|
//
|
|
if (TokenizedLine->TokenCount > 3) {
|
|
|
|
//
|
|
// The command has the context in it, so get it.
|
|
//
|
|
User = TokenizedLine->Tokens->Next->Next->Next->String;
|
|
|
|
if (*User != L'/') {
|
|
RcMessageOut(MSG_SYNTAX_ERROR);
|
|
return 1;
|
|
}
|
|
|
|
User++;
|
|
pwch = User;
|
|
while ((*pwch != UNICODE_NULL) && (*pwch != L':')) {
|
|
pwch++;
|
|
}
|
|
|
|
if (*pwch != L':') {
|
|
RcMessageOut(MSG_SYNTAX_ERROR);
|
|
return 1;
|
|
}
|
|
|
|
*pwch = UNICODE_NULL;
|
|
pwch++;
|
|
|
|
if (_wcsicmp(User, L"USER") && _wcsicmp(User, L"U")) {
|
|
RcMessageOut(MSG_SYNTAX_ERROR);
|
|
return 1;
|
|
}
|
|
|
|
User = pwch;
|
|
|
|
//
|
|
// Get the password
|
|
//
|
|
if (TokenizedLine->TokenCount == 4) {
|
|
|
|
RcMessageOut( MSG_NET_USE_PROMPT_PASSWORD );
|
|
RtlZeroMemory( PasswordBuffer, sizeof(PasswordBuffer) );
|
|
RcPasswordIn( PasswordBuffer, 60 );
|
|
|
|
} else {
|
|
|
|
if (wcslen(TokenizedLine->Tokens->Next->Next->Next->Next->String) > 60) {
|
|
RcMessageOut(MSG_SYNTAX_ERROR);
|
|
return 1;
|
|
}
|
|
|
|
wcscpy(PasswordBuffer, TokenizedLine->Tokens->Next->Next->Next->Next->String);
|
|
|
|
if ((PasswordBuffer[0] == L'*') && (PasswordBuffer[1] == UNICODE_NULL)) {
|
|
|
|
RcMessageOut( MSG_NET_USE_PROMPT_PASSWORD );
|
|
RtlZeroMemory( PasswordBuffer, sizeof(PasswordBuffer) );
|
|
RcPasswordIn( PasswordBuffer, 60 );
|
|
|
|
} else if (PasswordBuffer[0] == L'"') {
|
|
|
|
pwch = &(PasswordBuffer[1]);
|
|
|
|
while (*pwch != UNICODE_NULL) {
|
|
pwch++;
|
|
}
|
|
|
|
pwch--;
|
|
|
|
if ((*pwch == L'"') && (pwch != &(PasswordBuffer[1]))) {
|
|
*pwch = UNICODE_NULL;
|
|
}
|
|
|
|
RtlMoveMemory(PasswordBuffer, &(PasswordBuffer[1]), (PtrToUlong(pwch) - PtrToUlong(PasswordBuffer)) + sizeof(WCHAR));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// If we allow holding a current context, then we would use that here, but we currently
|
|
// don't, so spew a syntax error message.
|
|
//
|
|
RcMessageOut(MSG_SYNTAX_ERROR);
|
|
return 1;
|
|
|
|
}
|
|
|
|
//
|
|
// Call worker routine to make the connection
|
|
//
|
|
Status = RcDoNetUse(Share, User, PasswordBuffer, Drive);
|
|
RtlSecureZeroMemory(PasswordBuffer, sizeof(PasswordBuffer));
|
|
|
|
if( !NT_SUCCESS(Status) ) {
|
|
RcNtError(Status, MSG_NET_USE_ERROR);
|
|
} else {
|
|
RcMessageOut(MSG_NET_USE_DRIVE_LETTER, Share, Drive);
|
|
}
|
|
|
|
} else { // attempt to disconnect
|
|
|
|
//
|
|
// Verify drive letter parameter
|
|
//
|
|
if ((*(Share+1) != L':') || (*(Share + 2) != UNICODE_NULL)) {
|
|
RcMessageOut(MSG_SYNTAX_ERROR);
|
|
return 1;
|
|
}
|
|
|
|
//
|
|
// Verify /d parameter
|
|
//
|
|
User = TokenizedLine->Tokens->Next->Next->Next->String;
|
|
|
|
if ((*User != L'/') || ((*(User + 1) != L'd') && (*(User + 1) != L'D'))) {
|
|
RcMessageOut(MSG_SYNTAX_ERROR);
|
|
return 1;
|
|
}
|
|
|
|
//
|
|
// Call worker routine to actually do the disconnect.
|
|
//
|
|
Status = RcNetUnuse(Share);
|
|
|
|
if( !NT_SUCCESS(Status) ) {
|
|
RcNtError(Status, MSG_NET_USE_ERROR);
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
|