#include "brian.h" typedef struct _ASYNC_NOTIFY { USHORT FileIndex; BOOLEAN UseEvent; PIO_APC_ROUTINE ApcRoutine; PVOID ApcContext; PUSHORT BufferIndexPtr; USHORT BufferIndex; ULONG Length; ULONG CompletionFilter; BOOLEAN WatchTree; BOOLEAN DisplayParms; BOOLEAN VerboseResults; USHORT AsyncIndex; } ASYNC_NOTIFY, *PASYNC_NOTIFY; #define USE_EVENT_DEFAULT TRUE #define APC_ROUTINE_DEFAULT NULL #define APC_CONTEXT_DEFAULT NULL #define LENGTH_DEFAULT 0 #define FILTER_DEFAULT FILE_NOTIFY_CHANGE_FILE_NAME #define WATCH_TREE_DEFAULT FALSE #define DISPLAY_PARMS_DEFAULT FALSE #define VERBOSE_RESULTS_DEFAULT FALSE VOID FullNotify( IN OUT PASYNC_NOTIFY AsyncNotify ); VOID InputNotifyChange( IN PCHAR ParamBuffer ) { ULONG FileIndex; BOOLEAN UseEvent; PIO_APC_ROUTINE ApcRoutine; PVOID ApcContext; PUSHORT BufferIndexPtr; USHORT BufferIndex; ULONG Length; ULONG CompletionFilter; BOOLEAN WatchTree; BOOLEAN DisplayParms; BOOLEAN VerboseResults; USHORT AsyncIndex; BOOLEAN ParamReceived; BOOLEAN LastInput; // // Set the defaults. // UseEvent = USE_EVENT_DEFAULT; ApcRoutine = APC_ROUTINE_DEFAULT; ApcContext = APC_CONTEXT_DEFAULT; BufferIndexPtr = NULL; BufferIndex = 0; Length = LENGTH_DEFAULT; CompletionFilter = FILTER_DEFAULT; WatchTree = WATCH_TREE_DEFAULT; DisplayParms = DISPLAY_PARMS_DEFAULT; VerboseResults = VERBOSE_RESULTS_DEFAULT; AsyncIndex = 0; ParamReceived = FALSE; LastInput = TRUE; // // While there is more input, analyze the parameter and update the // query flags. // while (TRUE) { ULONG DummyCount; ULONG TempIndex; // // Swallow leading white spaces. // ParamBuffer = SwallowWhite( ParamBuffer, &DummyCount ); if (*ParamBuffer) { // // If the next parameter is legal then check the paramter value. // Update the parameter value. // if ((*ParamBuffer == '-' || *ParamBuffer == '/') && (ParamBuffer++, *ParamBuffer != '\0')) { BOOLEAN SwitchBool; // // Switch on the next character. // switch (*ParamBuffer) { // // Update the file handle index. // case 'i' : case 'I' : // // Move to the next character, as long as there // are no white spaces continue analyzing letters. // On the first bad letter, skip to the next // parameter. // ParamBuffer++; FileIndex = AsciiToInteger( ParamBuffer ); ParamBuffer = SwallowNonWhite( ParamBuffer, &DummyCount ); ParamReceived = TRUE; break; // // Check whether we should use an event to signal // completion. // case 'e' : case 'E' : // // Legal values for use event are T/t or F/f. // ParamBuffer++; if (*ParamBuffer == 'T' || *ParamBuffer == 't') { UseEvent = TRUE; ParamBuffer++; } else if (*ParamBuffer == 'F' || *ParamBuffer == 'f') { UseEvent = FALSE; ParamBuffer++; } break; // // Check whether we should watch the tree. // case 'w' : case 'W' : // // Legal values for use event are T/t or F/f. // ParamBuffer++; if (*ParamBuffer == 'T' || *ParamBuffer == 't') { WatchTree = TRUE; ParamBuffer++; } else if (*ParamBuffer == 'F' || *ParamBuffer == 'f') { WatchTree = FALSE; ParamBuffer++; } break; // // Update the buffer index. // case 'b' : case 'B' : // // Move to the next character, as long as there // are no white spaces continue analyzing letters. // On the first bad letter, skip to the next // parameter. // ParamBuffer++; TempIndex = AsciiToInteger( ParamBuffer ); BufferIndex = (USHORT) TempIndex; BufferIndexPtr = &BufferIndex; Length = Buffers[BufferIndex].Length; ParamBuffer = SwallowNonWhite( ParamBuffer, &DummyCount ); break; // // Update the byte count. // case 'l' : case 'L' : // // Move to the next character, as long as there // are no white spaces continue analyzing letters. // On the first bad letter, skip to the next // parameter. // ParamBuffer++; Length = AsciiToInteger( ParamBuffer ); ParamBuffer = SwallowNonWhite( ParamBuffer, &DummyCount ); break; // // Update the completion filter. // case 'f' : case 'F' : // // Move to the next character, as long as there // are no white spaces continue analyzing letters. // On the first bad letter, skip to the next // parameter. // ParamBuffer++; SwitchBool = TRUE; while (*ParamBuffer && *ParamBuffer != ' ' && *ParamBuffer != '\t') { // // Perform switch on character. // switch (*ParamBuffer) { case 'a' : case 'A' : CompletionFilter |= FILE_NOTIFY_CHANGE_FILE_NAME; break; case 'b' : case 'B' : CompletionFilter |= FILE_NOTIFY_CHANGE_DIR_NAME; break; case 'c' : case 'C' : CompletionFilter |= FILE_NOTIFY_CHANGE_ATTRIBUTES; break; case 'd' : case 'D' : CompletionFilter |= FILE_NOTIFY_CHANGE_SIZE; break; case 'e' : case 'E' : CompletionFilter |= FILE_NOTIFY_CHANGE_LAST_WRITE; break; case 'f' : case 'F' : CompletionFilter |= FILE_NOTIFY_CHANGE_LAST_ACCESS; break; case 'g' : case 'G' : CompletionFilter |= FILE_NOTIFY_CHANGE_CREATION; break; case 'h' : case 'H' : CompletionFilter |= FILE_NOTIFY_CHANGE_EA; break; case 'i' : case 'I' : CompletionFilter |= FILE_NOTIFY_CHANGE_SECURITY; break; case 'j' : case 'J' : CompletionFilter |= FILE_NOTIFY_CHANGE_STREAM_NAME; break; case 'k' : case 'K' : CompletionFilter |= FILE_NOTIFY_CHANGE_STREAM_SIZE; break; case 'l' : case 'L' : CompletionFilter |= FILE_NOTIFY_CHANGE_STREAM_WRITE; break; case 'y' : case 'Y' : CompletionFilter = FILE_NOTIFY_VALID_MASK; break; case 'z' : case 'Z' : CompletionFilter = 0; break; default : ParamBuffer = SwallowNonWhite( ParamBuffer, &DummyCount ); SwitchBool = FALSE; } if (!SwitchBool) { break; } ParamBuffer++; } break; case 'v' : case 'V' : // // Legal values for params are T/t or F/f. // ParamBuffer++; if( *ParamBuffer == 'T' || *ParamBuffer == 't' ) { VerboseResults = TRUE; ParamBuffer++; } else if( *ParamBuffer == 'F' || *ParamBuffer == 'f' ) { VerboseResults = FALSE; ParamBuffer++; } break; case 'y' : case 'Y' : // // Set the display parms flag and jump over this // character. // DisplayParms = TRUE; ParamBuffer = SwallowNonWhite( ParamBuffer, &DummyCount ); break; case 'z' : case 'Z' : // // Set flag for more input and jump over this char. // LastInput = FALSE; ParamBuffer = SwallowNonWhite( ParamBuffer, &DummyCount ); break; default : // // Swallow to the next white space and continue the // loop. // ParamBuffer = SwallowNonWhite( ParamBuffer, &DummyCount ); } } // // Else the text is invalid, skip the entire block. // // // // Else if there is no input then exit. // } else if( LastInput ) { break; // // Else try to read another line for open parameters. // } else { } } // // If no parameters were received then display the syntax message. // if (!ParamReceived) { printf( "\n Usage: ncd [options]* -i [options]*\n" ); printf( "\n Options:" ); printf( "\n -i File index" ); printf( "\n -b Buffer to store results" ); printf( "\n -l Stated length of buffer" ); printf( "\n -f Completion filter" ); printf( "\n -w[t|f] Watch directory tree" ); printf( "\n -e[t|f] Use event on completion" ); printf( "\n -v[t|f] Verbose results" ); printf( "\n -y Display parameters to query" ); printf( "\n -z Additional input line" ); printf( "\n\n" ); // // Else call our notify routine. // } else { NTSTATUS Status; SIZE_T RegionSize; ULONG TempIndex; PASYNC_NOTIFY AsyncNotify; HANDLE ThreadHandle; ULONG ThreadId; RegionSize = sizeof( ASYNC_NOTIFY ); Status = AllocateBuffer( 0, &RegionSize, &TempIndex ); AsyncIndex = (USHORT) TempIndex; if (!NT_SUCCESS( Status )) { printf("\n\tInputRead: Unable to allocate async structure" ); } else { AsyncNotify = (PASYNC_NOTIFY) Buffers[AsyncIndex].Buffer; AsyncNotify->FileIndex = (USHORT) FileIndex; AsyncNotify->UseEvent = UseEvent; AsyncNotify->ApcRoutine = ApcRoutine; AsyncNotify->ApcContext = ApcContext; AsyncNotify->BufferIndex = BufferIndex; AsyncNotify->BufferIndexPtr = BufferIndexPtr ? &AsyncNotify->BufferIndex : BufferIndexPtr; AsyncNotify->Length = Length; AsyncNotify->CompletionFilter = CompletionFilter; AsyncNotify->WatchTree = WatchTree; AsyncNotify->DisplayParms = DisplayParms; AsyncNotify->VerboseResults = VerboseResults; AsyncNotify->AsyncIndex = AsyncIndex; if (!SynchronousCmds) { ThreadHandle = CreateThread( NULL, 0, FullNotify, AsyncNotify, 0, &ThreadId ); if (ThreadHandle == 0) { printf( "\nInputNotify: Spawning thread fails -> %d\n", GetLastError() ); return; } } else { FullNotify( AsyncNotify ); } } } return; } VOID FullNotify( IN OUT PASYNC_NOTIFY AsyncNotify ) { NTSTATUS Status; IO_STATUS_BLOCK Iosb; HANDLE ThisEvent; USHORT ThisEventIndex; USHORT ThisBufferIndex; BOOLEAN UnwindNotifyBuffer = FALSE; BOOLEAN UnwindEvent = FALSE; Status = STATUS_SUCCESS; if (AsyncNotify->DisplayParms) { bprint "\nNotify Parameters" ); bprint "\n File Handle Index -> %d", AsyncNotify->FileIndex ); bprint "\n Buffer Index Ptr -> %08lx", AsyncNotify->BufferIndexPtr ); if (AsyncNotify->BufferIndexPtr) { bprint "\n BufferIndex value -> %04x", AsyncNotify->BufferIndex ); } bprint "\n Length -> %08lx", AsyncNotify->Length ); bprint "\n CompletionFilter -> %08lx", AsyncNotify->CompletionFilter ); bprint "\n WatchTree -> %d", AsyncNotify->WatchTree ); bprint "\n UseEvent -> %d", AsyncNotify->UseEvent ); bprint "\n ApcRoutine -> %08lx", AsyncNotify->ApcRoutine ); bprint "\n ApcContext -> %08lx", AsyncNotify->ApcContext ); bprint "\n\n" ); } try { SIZE_T ThisLength; // // If we need a buffer, allocate it now. // if (AsyncNotify->BufferIndexPtr == NULL ) { ULONG TempIndex; ThisLength = 4096; Status = AllocateBuffer( 0L, &ThisLength, &TempIndex ); ThisBufferIndex = (USHORT) TempIndex; if (!NT_SUCCESS( Status )) { bprint "\n\tFullNotify: Unable to allocate a notify buffer" ); try_return( Status ); } bprint "\n\tFullNotify: Reading into buffer -> %04x\n", ThisBufferIndex ); bprint "\n" ); UnwindNotifyBuffer = TRUE; AsyncNotify->Length = (ULONG) ThisLength; } else { ThisBufferIndex = AsyncNotify->BufferIndex; } // // Check that the buffer index is valid. // if (ThisBufferIndex >= MAX_BUFFERS) { bprint "\n\tFullNotify: The read buffer index is invalid" ); try_return( Status = STATUS_INVALID_HANDLE ); } // // Check that the file index is valid. // if (AsyncNotify->FileIndex >= MAX_HANDLES) { bprint "\n\tFullNotify: The file index is invalid" ); try_return( Status = STATUS_INVALID_HANDLE ); } // // If we need an event, allocate and set it now. // if (AsyncNotify->UseEvent == TRUE) { Status = ObtainEvent( &ThisEventIndex ); if (!NT_SUCCESS( Status )) { bprint "\n\tFullNotify: Unable to allocate an event" ); try_return( Status ); } UnwindEvent = TRUE; ThisEvent = Events[ThisEventIndex].Handle; } else { ThisEvent = 0; } // // Call the read routine. // Status = NtNotifyChangeDirectoryFile( Handles[AsyncNotify->FileIndex].Handle, ThisEvent, AsyncNotify->ApcRoutine, AsyncNotify->ApcContext, &Iosb, Buffers[ThisBufferIndex].Buffer, AsyncNotify->Length, AsyncNotify->CompletionFilter, AsyncNotify->WatchTree ); if (AsyncNotify->VerboseResults) { bprint "\nNotifyChangeDir: Status -> %08lx\n", Status ); if (AsyncNotify->UseEvent && NT_SUCCESS( Status )) { if ((Status = NtWaitForSingleObject( ThisEvent, FALSE, NULL )) != STATUS_SUCCESS) { bprint "\n\tNotifyChangeDir: Wait for event failed -> %08lx\n", Status ); try_return( Status ); } } if (NT_SUCCESS( Status )) { bprint "\nNotifyChangeDir: Iosb.Information -> %08lx", Iosb.Information ); bprint "\nNotifyChangeDir: Iosb.Status -> %08lx", Iosb.Status ); } bprint "\n" ); } try_return( Status ); try_exit: NOTHING; } finally { if (UnwindEvent) { FreeEvent( ThisEventIndex ); } DeallocateBuffer( AsyncNotify->AsyncIndex ); } NtTerminateThread( 0, STATUS_SUCCESS ); }