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.
 
 
 
 
 
 

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;
}