825 lines
21 KiB
C
825 lines
21 KiB
C
|
/*++
|
||
|
|
||
|
Copyright (c) Microsoft Corporation. All rights reserved.
|
||
|
|
||
|
Module Name:
|
||
|
|
||
|
IoFileUtil.c
|
||
|
|
||
|
Abstract:
|
||
|
|
||
|
This module implements various file utility functions for the Io subsystem.
|
||
|
|
||
|
Author:
|
||
|
|
||
|
Adrian J. Oney - April 4, 2000
|
||
|
|
||
|
Revision History:
|
||
|
|
||
|
--*/
|
||
|
|
||
|
#include "pnpmgrp.h"
|
||
|
#include "IopFileUtil.h"
|
||
|
#pragma hdrstop
|
||
|
|
||
|
#ifdef ALLOC_PRAGMA
|
||
|
#pragma alloc_text(INIT, IopFileUtilWalkDirectoryTreeTopDown)
|
||
|
#pragma alloc_text(INIT, IopFileUtilWalkDirectoryTreeBottomUp)
|
||
|
#pragma alloc_text(INIT, IopFileUtilWalkDirectoryTreeHelper)
|
||
|
#pragma alloc_text(INIT, IopFileUtilClearAttributes)
|
||
|
#pragma alloc_text(INIT, IopFileUtilRename)
|
||
|
#endif
|
||
|
|
||
|
#define POOLTAG_FILEUTIL ('uFoI')
|
||
|
|
||
|
NTSTATUS
|
||
|
IopFileUtilWalkDirectoryTreeTopDown(
|
||
|
IN PUNICODE_STRING Directory,
|
||
|
IN ULONG Flags,
|
||
|
IN DIRWALK_CALLBACK CallbackFunction,
|
||
|
IN PVOID Context
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This funcion walks a directory tree *top down*, passing each entry to the
|
||
|
callback with the below restrictions. Note that the root directory itself
|
||
|
is not included in the callback!
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
Directory - Supplies the NT Path to the directory to walk. The directory
|
||
|
should *not* have a slash '\\'.
|
||
|
|
||
|
Flags - Specifies constraints on how the directory tree should be walked:
|
||
|
|
||
|
DIRWALK_INCLUDE_FILES - Files should be included in the dump.
|
||
|
|
||
|
DIRWALK_INCLUDE_DIRECTORIES - Directories should be included in the
|
||
|
dump.
|
||
|
|
||
|
DIRWALK_CULL_DOTPATHS - "." and ".." should *not* be included
|
||
|
in the list of directories passed to
|
||
|
the callback function.
|
||
|
|
||
|
DIRWALK_TRAVERSE - Each subdirectory should be traversed
|
||
|
in turn.
|
||
|
|
||
|
DIRWALK_TRAVERSE_MOUNTPOINTS - Set if mountpoints/symlinks should
|
||
|
be traversed as well.
|
||
|
|
||
|
CallbackFunction - Pointer to a function to call for each entry in the
|
||
|
directory/subtree.
|
||
|
|
||
|
Context - Context to pass to the callback function.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
NTSTATUS - status of the operation.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
PDIRWALK_ENTRY pDirEntry;
|
||
|
PLIST_ENTRY pListEntry;
|
||
|
NTSTATUS status;
|
||
|
UCHAR buffer[1024];
|
||
|
LIST_ENTRY dirListHead;
|
||
|
|
||
|
InitializeListHead(&dirListHead);
|
||
|
|
||
|
//
|
||
|
// Walk the first directory.
|
||
|
//
|
||
|
status = IopFileUtilWalkDirectoryTreeHelper(
|
||
|
Directory,
|
||
|
Flags,
|
||
|
CallbackFunction,
|
||
|
Context,
|
||
|
buffer,
|
||
|
sizeof(buffer),
|
||
|
&dirListHead
|
||
|
);
|
||
|
|
||
|
//
|
||
|
// Each directory that WalkDirectory finds gets added to the list.
|
||
|
// process the list until we have no more directories.
|
||
|
//
|
||
|
while((!IsListEmpty(&dirListHead)) && NT_SUCCESS(status)) {
|
||
|
|
||
|
pListEntry = RemoveHeadList(&dirListHead);
|
||
|
|
||
|
pDirEntry = (PDIRWALK_ENTRY) CONTAINING_RECORD(pListEntry, DIRWALK_ENTRY, Link);
|
||
|
|
||
|
status = IopFileUtilWalkDirectoryTreeHelper(
|
||
|
&pDirEntry->Directory,
|
||
|
Flags,
|
||
|
CallbackFunction,
|
||
|
Context,
|
||
|
buffer,
|
||
|
sizeof(buffer),
|
||
|
&dirListHead
|
||
|
);
|
||
|
|
||
|
ExFreePool(pDirEntry);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// If we failed we need to empty out our directory list.
|
||
|
//
|
||
|
if (!NT_SUCCESS(status)) {
|
||
|
|
||
|
while (!IsListEmpty(&dirListHead)) {
|
||
|
|
||
|
pListEntry = RemoveHeadList(&dirListHead);
|
||
|
|
||
|
pDirEntry = (PDIRWALK_ENTRY) CONTAINING_RECORD(pListEntry, DIRWALK_ENTRY, Link);
|
||
|
|
||
|
ExFreePool(pDirEntry);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
|
||
|
NTSTATUS
|
||
|
IopFileUtilWalkDirectoryTreeBottomUp(
|
||
|
IN PUNICODE_STRING Directory,
|
||
|
IN ULONG Flags,
|
||
|
IN DIRWALK_CALLBACK CallbackFunction,
|
||
|
IN PVOID Context
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This funcion walks a directory tree *bottom up*, passing each entry to the
|
||
|
callback with the below restrictions. Note that the root directory itself
|
||
|
is not included in the callback!
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
Directory - Supplies the NT Path to the directory to walk. The directory
|
||
|
should *not* have a slash trailing '\\'.
|
||
|
|
||
|
Flags - Specifies constraints on how the directory tree should be walked:
|
||
|
|
||
|
DIRWALK_INCLUDE_FILES - Files should be included in the dump.
|
||
|
|
||
|
DIRWALK_INCLUDE_DIRECTORIES - Directories should be included in the
|
||
|
dump.
|
||
|
|
||
|
DIRWALK_CULL_DOTPATHS - "." and ".." should *not* be included
|
||
|
in the list of directories passed to
|
||
|
the callback function.
|
||
|
|
||
|
DIRWALK_TRAVERSE - Each subdirectory should be traversed
|
||
|
in turn.
|
||
|
|
||
|
DIRWALK_TRAVERSE_MOUNTPOINTS - Set if mountpoints/symlinks should
|
||
|
be traversed as well.
|
||
|
|
||
|
CallbackFunction - Pointer to a function to call for each entry in the
|
||
|
directory/subtree.
|
||
|
|
||
|
Context - Context to pass to the callback function.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
NTSTATUS - status of the operation.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
PDIRWALK_ENTRY pDirEntry;
|
||
|
PLIST_ENTRY pListEntry;
|
||
|
NTSTATUS status;
|
||
|
UCHAR buffer[1024];
|
||
|
LIST_ENTRY dirListHead, dirNothingHead;
|
||
|
|
||
|
InitializeListHead(&dirListHead);
|
||
|
InitializeListHead(&dirNothingHead);
|
||
|
|
||
|
//
|
||
|
// Create an entry for the root directory.
|
||
|
//
|
||
|
pDirEntry = (PDIRWALK_ENTRY) ExAllocatePoolWithTag(
|
||
|
PagedPool,
|
||
|
sizeof(DIRWALK_ENTRY) + Directory->Length - sizeof(WCHAR),
|
||
|
POOLTAG_FILEUTIL
|
||
|
);
|
||
|
|
||
|
if (pDirEntry == NULL) {
|
||
|
|
||
|
return STATUS_INSUFFICIENT_RESOURCES;
|
||
|
}
|
||
|
|
||
|
pDirEntry->Directory.Length = 0;
|
||
|
pDirEntry->Directory.MaximumLength = Directory->Length;
|
||
|
pDirEntry->Directory.Buffer = &pDirEntry->Name[0];
|
||
|
RtlCopyUnicodeString(&pDirEntry->Directory, Directory);
|
||
|
|
||
|
InsertHeadList(&dirListHead, &pDirEntry->Link);
|
||
|
|
||
|
//
|
||
|
// Collect the directory trees. When we are done we will walk the list in
|
||
|
// reverse.
|
||
|
//
|
||
|
status = STATUS_SUCCESS;
|
||
|
if (Flags & DIRWALK_TRAVERSE) {
|
||
|
|
||
|
for(pListEntry = &dirListHead;
|
||
|
pListEntry->Flink != &dirListHead;
|
||
|
pListEntry = pListEntry->Flink) {
|
||
|
|
||
|
pDirEntry = (PDIRWALK_ENTRY) CONTAINING_RECORD(pListEntry, DIRWALK_ENTRY, Link);
|
||
|
|
||
|
status = IopFileUtilWalkDirectoryTreeHelper(
|
||
|
&pDirEntry->Directory,
|
||
|
DIRWALK_TRAVERSE,
|
||
|
NULL,
|
||
|
NULL,
|
||
|
buffer,
|
||
|
sizeof(buffer),
|
||
|
&dirListHead
|
||
|
);
|
||
|
|
||
|
if (!NT_SUCCESS(status)) {
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Each directory that WalkDirectory finds gets added to the list.
|
||
|
// process the list until we have no more directories.
|
||
|
//
|
||
|
while((!IsListEmpty(&dirListHead)) && NT_SUCCESS(status)) {
|
||
|
|
||
|
pListEntry = RemoveTailList(&dirListHead);
|
||
|
|
||
|
pDirEntry = (PDIRWALK_ENTRY) CONTAINING_RECORD(pListEntry, DIRWALK_ENTRY, Link);
|
||
|
|
||
|
status = IopFileUtilWalkDirectoryTreeHelper(
|
||
|
&pDirEntry->Directory,
|
||
|
Flags & ~DIRWALK_TRAVERSE,
|
||
|
CallbackFunction,
|
||
|
Context,
|
||
|
buffer,
|
||
|
sizeof(buffer),
|
||
|
&dirNothingHead
|
||
|
);
|
||
|
|
||
|
ExFreePool(pDirEntry);
|
||
|
|
||
|
ASSERT(IsListEmpty(&dirNothingHead));
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Now do any final cleanup.
|
||
|
//
|
||
|
if (!NT_SUCCESS(status)) {
|
||
|
|
||
|
while (!IsListEmpty(&dirListHead)) {
|
||
|
|
||
|
pListEntry = RemoveHeadList(&dirListHead);
|
||
|
|
||
|
pDirEntry = (PDIRWALK_ENTRY) CONTAINING_RECORD(pListEntry, DIRWALK_ENTRY, Link);
|
||
|
|
||
|
ExFreePool(pDirEntry);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
|
||
|
NTSTATUS
|
||
|
IopFileUtilWalkDirectoryTreeHelper(
|
||
|
IN PUNICODE_STRING Directory,
|
||
|
IN ULONG Flags,
|
||
|
IN DIRWALK_CALLBACK CallbackFunction,
|
||
|
IN PVOID Context,
|
||
|
IN PUCHAR Buffer,
|
||
|
IN ULONG BufferSize,
|
||
|
IN OUT PLIST_ENTRY DirList
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This is a helper function for the IopFileUtilWalkDirectoryTree* functions.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
Directory - Supplies the NT Path to the directory to walk. The directory
|
||
|
should *not* have a slash trailing '\\'.
|
||
|
|
||
|
Flags - Specifies constraints on how the directory tree should be walked:
|
||
|
|
||
|
DIRWALK_INCLUDE_FILES - Files should be included in the dump.
|
||
|
|
||
|
DIRWALK_INCLUDE_DIRECTORIES - Directories should be included in the
|
||
|
dump.
|
||
|
|
||
|
DIRWALK_CULL_DOTPATHS - "." and ".." should *not* be included
|
||
|
in the list of directories passed to
|
||
|
the callback function.
|
||
|
|
||
|
DIRWALK_TRAVERSE - Each subdirectory should be traversed
|
||
|
in turn.
|
||
|
|
||
|
DIRWALK_TRAVERSE_MOUNTPOINTS - Set if mountpoints/symlinks should
|
||
|
be traversed as well.
|
||
|
|
||
|
DirList - Recieves list of new directories to scan after completion of this
|
||
|
directory. Each entry is a member of the DIRECTORY_ENTRY
|
||
|
structure.
|
||
|
|
||
|
CallbackFunction - Pointer to a function to call for each entry in the
|
||
|
directory/subtree.
|
||
|
|
||
|
Context - Context to pass to the callback function.
|
||
|
|
||
|
Buffer - A scratch buffer to use.
|
||
|
|
||
|
BufferSize - The length of Buffer. Must be greater than sizeof(WCHAR).
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
NTSTATUS - status of the operation.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
NTSTATUS status;
|
||
|
HANDLE fileHandle;
|
||
|
OBJECT_ATTRIBUTES objectAttributes;
|
||
|
IO_STATUS_BLOCK ioStatus;
|
||
|
BOOLEAN bRestartScan, bIsDotPath;
|
||
|
WCHAR savedChar;
|
||
|
PFILE_BOTH_DIR_INFORMATION pFileInfo;
|
||
|
UNICODE_STRING entryName;
|
||
|
USHORT newNameLength;
|
||
|
PDIRWALK_ENTRY pDirEntry;
|
||
|
ULONG OpenFlags;
|
||
|
|
||
|
//
|
||
|
// Setup initial values
|
||
|
//
|
||
|
bRestartScan = TRUE;
|
||
|
|
||
|
//
|
||
|
// Open the file for list directory access
|
||
|
//
|
||
|
if (Flags & DIRWALK_TRAVERSE_MOUNTPOINTS) {
|
||
|
|
||
|
OpenFlags = FILE_DIRECTORY_FILE |
|
||
|
FILE_OPEN_FOR_BACKUP_INTENT;
|
||
|
|
||
|
} else {
|
||
|
|
||
|
OpenFlags = FILE_OPEN_REPARSE_POINT |
|
||
|
FILE_DIRECTORY_FILE |
|
||
|
FILE_OPEN_FOR_BACKUP_INTENT;
|
||
|
}
|
||
|
|
||
|
InitializeObjectAttributes(
|
||
|
&objectAttributes,
|
||
|
Directory,
|
||
|
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
|
||
|
NULL,
|
||
|
NULL
|
||
|
);
|
||
|
|
||
|
status = ZwOpenFile(
|
||
|
&fileHandle,
|
||
|
FILE_LIST_DIRECTORY | SYNCHRONIZE,
|
||
|
&objectAttributes,
|
||
|
&ioStatus,
|
||
|
FILE_SHARE_READ,
|
||
|
OpenFlags
|
||
|
);
|
||
|
|
||
|
if (!NT_SUCCESS(status)) {
|
||
|
|
||
|
goto cleanup;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Do the directory loop
|
||
|
//
|
||
|
while(1) {
|
||
|
|
||
|
//
|
||
|
// We subtract off a WCHAR so that we can append a terminating null as
|
||
|
// needed.
|
||
|
//
|
||
|
ASSERT(BufferSize > sizeof(WCHAR));
|
||
|
|
||
|
status = ZwQueryDirectoryFile(
|
||
|
fileHandle,
|
||
|
(HANDLE)NULL,
|
||
|
(PIO_APC_ROUTINE)NULL,
|
||
|
(PVOID)NULL,
|
||
|
&ioStatus,
|
||
|
Buffer,
|
||
|
BufferSize - sizeof(WCHAR),
|
||
|
FileBothDirectoryInformation,
|
||
|
FALSE,
|
||
|
(PUNICODE_STRING)NULL,
|
||
|
bRestartScan
|
||
|
);
|
||
|
|
||
|
if (!NT_SUCCESS(status)) {
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// We may come back here. Make sure the file scan doesn't start back
|
||
|
// over.
|
||
|
//
|
||
|
bRestartScan = FALSE;
|
||
|
|
||
|
//
|
||
|
// Wait for the event to complete if neccessary.
|
||
|
//
|
||
|
if (status == STATUS_PENDING) {
|
||
|
|
||
|
ZwWaitForSingleObject(fileHandle, TRUE, NULL);
|
||
|
status = ioStatus.Status;
|
||
|
|
||
|
//
|
||
|
// Check the Irp for success
|
||
|
//
|
||
|
if (!NT_SUCCESS(status)) {
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Walk each returned record. Note that we won't be here if there are
|
||
|
// no records, as ioStatus will have contains STATUS_NO_MORE_FILES.
|
||
|
//
|
||
|
pFileInfo = (PFILE_BOTH_DIR_INFORMATION) Buffer;
|
||
|
|
||
|
while(1) {
|
||
|
|
||
|
//
|
||
|
// Temporarily terminate the file. We allocated an extra WCHAR to
|
||
|
// make sure we could safely do this.
|
||
|
//
|
||
|
savedChar = pFileInfo->FileName[pFileInfo->FileNameLength/sizeof(WCHAR)];
|
||
|
pFileInfo->FileName[pFileInfo->FileNameLength/sizeof(WCHAR)] = 0;
|
||
|
|
||
|
//
|
||
|
// Build a full unicode path for the file along with a directory
|
||
|
// entry at the same time.
|
||
|
//
|
||
|
RtlInitUnicodeString(&entryName, pFileInfo->FileName);
|
||
|
|
||
|
newNameLength =
|
||
|
(Directory->Length + entryName.Length + sizeof(WCHAR));
|
||
|
|
||
|
pDirEntry = (PDIRWALK_ENTRY) ExAllocatePoolWithTag(
|
||
|
PagedPool,
|
||
|
sizeof(DIRWALK_ENTRY) + newNameLength - sizeof(WCHAR),
|
||
|
POOLTAG_FILEUTIL
|
||
|
);
|
||
|
|
||
|
if (pDirEntry == NULL) {
|
||
|
|
||
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
pDirEntry->Directory.Length = 0;
|
||
|
pDirEntry->Directory.MaximumLength = newNameLength;
|
||
|
pDirEntry->Directory.Buffer = &pDirEntry->Name[0];
|
||
|
RtlCopyUnicodeString(&pDirEntry->Directory, Directory);
|
||
|
RtlAppendUnicodeToString(&pDirEntry->Directory, L"\\");
|
||
|
RtlAppendUnicodeStringToString(&pDirEntry->Directory, &entryName);
|
||
|
|
||
|
if (pFileInfo->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||
|
|
||
|
//
|
||
|
// Check for . and ..
|
||
|
//
|
||
|
if ((!_wcsicmp(pFileInfo->FileName, L".")) ||
|
||
|
(!_wcsicmp(pFileInfo->FileName, L".."))) {
|
||
|
bIsDotPath = TRUE;
|
||
|
}
|
||
|
else {
|
||
|
bIsDotPath = FALSE;
|
||
|
}
|
||
|
|
||
|
if ((Flags & DIRWALK_INCLUDE_DIRECTORIES) &&
|
||
|
((!(Flags & DIRWALK_CULL_DOTPATHS)) || (!bIsDotPath))) {
|
||
|
|
||
|
status = CallbackFunction(
|
||
|
&pDirEntry->Directory,
|
||
|
&entryName,
|
||
|
pFileInfo->FileAttributes,
|
||
|
Context
|
||
|
);
|
||
|
}
|
||
|
|
||
|
if ((!bIsDotPath) && (Flags & DIRWALK_TRAVERSE)) {
|
||
|
|
||
|
InsertTailList(DirList, &pDirEntry->Link);
|
||
|
|
||
|
} else {
|
||
|
|
||
|
ExFreePool(pDirEntry);
|
||
|
}
|
||
|
|
||
|
} else {
|
||
|
|
||
|
if (Flags & DIRWALK_INCLUDE_FILES) {
|
||
|
|
||
|
status = CallbackFunction(
|
||
|
&pDirEntry->Directory,
|
||
|
&entryName,
|
||
|
pFileInfo->FileAttributes,
|
||
|
Context
|
||
|
);
|
||
|
}
|
||
|
|
||
|
ExFreePool(pDirEntry);
|
||
|
}
|
||
|
|
||
|
if (!NT_SUCCESS(status)) {
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Put back the character we wrote down. It might have been part of
|
||
|
// the next entry.
|
||
|
//
|
||
|
pFileInfo->FileName[pFileInfo->FileNameLength/sizeof(WCHAR)] = savedChar;
|
||
|
|
||
|
//
|
||
|
// Check if there is another record, if there isn't then we
|
||
|
// simply get out of this loop
|
||
|
//
|
||
|
if (pFileInfo->NextEntryOffset == 0) {
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// There is another record so advance FileInfo to the next
|
||
|
// record
|
||
|
//
|
||
|
pFileInfo = (PFILE_BOTH_DIR_INFORMATION)
|
||
|
(((PUCHAR) pFileInfo) + pFileInfo->NextEntryOffset);
|
||
|
}
|
||
|
|
||
|
if (!NT_SUCCESS(status)) {
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ZwClose( fileHandle );
|
||
|
|
||
|
if (status == STATUS_NO_MORE_FILES) {
|
||
|
|
||
|
status = STATUS_SUCCESS;
|
||
|
}
|
||
|
|
||
|
cleanup:
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
|
||
|
NTSTATUS
|
||
|
IopFileUtilClearAttributes(
|
||
|
IN PUNICODE_STRING FullPathName,
|
||
|
IN ULONG FileAttributes
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This function clears the passed in attributes off the specified file.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
FullPathName - Full path name of the identified file.
|
||
|
|
||
|
FileAttributes - Attributes to clear.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
NTSTATUS.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
HANDLE fileHandle;
|
||
|
OBJECT_ATTRIBUTES objectAttributes;
|
||
|
IO_STATUS_BLOCK ioStatus;
|
||
|
FILE_BASIC_INFORMATION fileBasicInformation;
|
||
|
ULONG newAttributes;
|
||
|
NTSTATUS status;
|
||
|
|
||
|
//
|
||
|
// First we open the file.
|
||
|
//
|
||
|
InitializeObjectAttributes(
|
||
|
&objectAttributes,
|
||
|
FullPathName,
|
||
|
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
|
||
|
NULL,
|
||
|
NULL
|
||
|
);
|
||
|
|
||
|
status = ZwOpenFile(
|
||
|
&fileHandle,
|
||
|
FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES | SYNCHRONIZE,
|
||
|
&objectAttributes,
|
||
|
&ioStatus,
|
||
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||
|
FILE_OPEN_REPARSE_POINT | FILE_SYNCHRONOUS_IO_NONALERT |
|
||
|
FILE_OPEN_FOR_BACKUP_INTENT | FILE_WRITE_THROUGH
|
||
|
);
|
||
|
|
||
|
if (!NT_SUCCESS(status)) {
|
||
|
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Then we get the file attributes.
|
||
|
//
|
||
|
status = ZwQueryInformationFile(
|
||
|
fileHandle,
|
||
|
&ioStatus,
|
||
|
&fileBasicInformation,
|
||
|
sizeof(fileBasicInformation),
|
||
|
FileBasicInformation
|
||
|
);
|
||
|
|
||
|
if (!NT_SUCCESS(status)) {
|
||
|
|
||
|
ZwClose(fileHandle);
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Anything to do?
|
||
|
//
|
||
|
if (fileBasicInformation.FileAttributes & FileAttributes) {
|
||
|
|
||
|
//
|
||
|
// Clear the specified bits.
|
||
|
//
|
||
|
newAttributes = fileBasicInformation.FileAttributes;
|
||
|
newAttributes &= ~FileAttributes;
|
||
|
if (newAttributes == 0) {
|
||
|
|
||
|
newAttributes = FILE_ATTRIBUTE_NORMAL;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Zero fields that shouldn't be touched.
|
||
|
//
|
||
|
RtlZeroMemory(
|
||
|
&fileBasicInformation,
|
||
|
sizeof(FILE_BASIC_INFORMATION)
|
||
|
);
|
||
|
|
||
|
fileBasicInformation.FileAttributes = newAttributes;
|
||
|
|
||
|
//
|
||
|
// Commit the changes.
|
||
|
//
|
||
|
status = ZwSetInformationFile(
|
||
|
fileHandle,
|
||
|
&ioStatus,
|
||
|
&fileBasicInformation,
|
||
|
sizeof(fileBasicInformation),
|
||
|
FileBasicInformation
|
||
|
);
|
||
|
}
|
||
|
|
||
|
ZwClose(fileHandle);
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
|
||
|
NTSTATUS
|
||
|
IopFileUtilRename(
|
||
|
IN PUNICODE_STRING SourcePathName,
|
||
|
IN PUNICODE_STRING DestinationPathName,
|
||
|
IN BOOLEAN ReplaceIfPresent
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This function renames or moves a file or directory.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
SourcePathName - Full path name of the file or directory to rename.
|
||
|
|
||
|
DestinationPathName - Future full path name of the file or directory.
|
||
|
|
||
|
ReplaceIfPresent - If true, NewPathName is deleted if already present.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
NTSTATUS.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
HANDLE fileHandle;
|
||
|
OBJECT_ATTRIBUTES objectAttributes;
|
||
|
IO_STATUS_BLOCK ioStatus;
|
||
|
PFILE_RENAME_INFORMATION pNewName;
|
||
|
NTSTATUS status;
|
||
|
|
||
|
pNewName = ExAllocatePoolWithTag(
|
||
|
PagedPool,
|
||
|
sizeof(FILE_RENAME_INFORMATION) + DestinationPathName->Length,
|
||
|
POOLTAG_FILEUTIL
|
||
|
);
|
||
|
|
||
|
if (pNewName == NULL) {
|
||
|
|
||
|
return STATUS_INSUFFICIENT_RESOURCES;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// If we may be replacing the file, we first need to clear the read only
|
||
|
// attributes.
|
||
|
//
|
||
|
if (ReplaceIfPresent) {
|
||
|
|
||
|
//
|
||
|
// Errors are ignored as the file may not exist.
|
||
|
//
|
||
|
IopFileUtilClearAttributes(
|
||
|
DestinationPathName,
|
||
|
( FILE_ATTRIBUTE_READONLY |
|
||
|
FILE_ATTRIBUTE_HIDDEN |
|
||
|
FILE_ATTRIBUTE_SYSTEM )
|
||
|
);
|
||
|
}
|
||
|
|
||
|
InitializeObjectAttributes(
|
||
|
&objectAttributes,
|
||
|
SourcePathName,
|
||
|
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
|
||
|
NULL,
|
||
|
NULL
|
||
|
);
|
||
|
|
||
|
status = ZwOpenFile(
|
||
|
&fileHandle,
|
||
|
FILE_READ_ATTRIBUTES | DELETE | SYNCHRONIZE,
|
||
|
&objectAttributes,
|
||
|
&ioStatus,
|
||
|
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
||
|
FILE_OPEN_REPARSE_POINT | FILE_SYNCHRONOUS_IO_NONALERT |
|
||
|
FILE_OPEN_FOR_BACKUP_INTENT | FILE_WRITE_THROUGH
|
||
|
);
|
||
|
|
||
|
if (!NT_SUCCESS(status)) {
|
||
|
|
||
|
ExFreePool(pNewName);
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Change \\SystemRoot\LastGood\Blah... to \\SystemRoot\Blah...
|
||
|
//
|
||
|
RtlCopyMemory(
|
||
|
pNewName->FileName,
|
||
|
DestinationPathName->Buffer,
|
||
|
DestinationPathName->Length
|
||
|
);
|
||
|
|
||
|
pNewName->ReplaceIfExists = ReplaceIfPresent;
|
||
|
pNewName->RootDirectory = NULL;
|
||
|
pNewName->FileNameLength = DestinationPathName->Length;
|
||
|
|
||
|
status = ZwSetInformationFile(
|
||
|
fileHandle,
|
||
|
&ioStatus,
|
||
|
pNewName,
|
||
|
pNewName->FileNameLength + sizeof(FILE_RENAME_INFORMATION),
|
||
|
FileRenameInformation
|
||
|
);
|
||
|
|
||
|
ExFreePool(pNewName);
|
||
|
ZwClose(fileHandle);
|
||
|
|
||
|
return status;
|
||
|
}
|
||
|
|