xbox-kernel/private/ntos/xapi/k32/filemisc.c
2020-09-30 17:17:25 +02:00

945 lines
24 KiB
C

/*++
Copyright (c) 1990 Microsoft Corporation
Module Name:
filemisc.c
Abstract:
Misc file operations for Win32
Author:
Mark Lucovsky (markl) 26-Sep-1990
Revision History:
--*/
#include <basedll.h>
#include "..\..\fatx\fat.h"
BOOL
APIENTRY
SetFileAttributes(
PCOSTR lpFileName,
DWORD dwFileAttributes
)
/*++
Routine Description:
The attributes of a file can be set using SetFileAttributes.
This API provides the same functionality as DOS (int 21h, function
43H with AL=1), and provides a subset of OS/2's DosSetFileInfo.
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:
TRUE - The operation was successful.
FALSE/NULL - The operation failed. Extended error status is available
using GetLastError.
--*/
{
NTSTATUS Status;
OBJECT_ATTRIBUTES Obja;
HANDLE Handle;
OBJECT_STRING FileName;
IO_STATUS_BLOCK IoStatusBlock;
FILE_BASIC_INFORMATION BasicInfo;
RtlInitObjectString(&FileName, lpFileName);
InitializeObjectAttributes(
&Obja,
&FileName,
OBJ_CASE_INSENSITIVE,
ObDosDevicesDirectory(),
NULL
);
//
// Open the file.
//
Status = NtOpenFile(
&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) ) {
XapiSetLastNTError(Status);
return FALSE;
}
//
// Set the attributes
//
RtlZeroMemory(&BasicInfo,sizeof(BasicInfo));
BasicInfo.FileAttributes = (dwFileAttributes & FILE_ATTRIBUTE_VALID_SET_FLAGS) | FILE_ATTRIBUTE_NORMAL;
Status = NtSetInformationFile(
Handle,
&IoStatusBlock,
&BasicInfo,
sizeof(BasicInfo),
FileBasicInformation
);
NtClose(Handle);
if ( NT_SUCCESS(Status) ) {
return TRUE;
}
else {
XapiSetLastNTError(Status);
return FALSE;
}
}
DWORD
APIENTRY
GetFileAttributes(
PCOSTR lpFileName
)
/*++
Routine Description:
The attributes of a file can be obtained using GetFileAttributes.
This API provides the same functionality as DOS (int 21h, function
43H with AL=0), and provides a subset of OS/2's DosQueryFileInfo.
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_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;
OBJECT_STRING FileName;
FILE_NETWORK_OPEN_INFORMATION NetworkInfo;
RtlInitObjectString(&FileName, lpFileName);
InitializeObjectAttributes(
&Obja,
&FileName,
OBJ_CASE_INSENSITIVE,
ObDosDevicesDirectory(),
NULL
);
//
// Query the information about the file using the path-based NT service.
//
Status = NtQueryFullAttributesFile( &Obja, &NetworkInfo );
if ( NT_SUCCESS(Status) ) {
return NetworkInfo.FileAttributes;
}
else {
XapiSetLastNTError(Status);
return (DWORD)-1;
}
}
BOOL
APIENTRY
GetFileAttributesEx(
PCOSTR lpFileName,
GET_FILEEX_INFO_LEVELS fInfoLevelId,
LPVOID lpFileInformation
)
/*++
Routine Description:
The main attributes of a file can be obtained using GetFileAttributesEx.
Arguments:
lpFileName - Supplies the file name of the file whose attributes are to
be set.
fInfoLevelId - Supplies the info level indicating the information to be
returned about the file.
lpFileInformation - Supplies a buffer to receive the specified information
about the file.
Return Value:
TRUE - The operation was successful.
FALSE/NULL - The operation failed. Extended error status is available
using GetLastError.
--*/
{
NTSTATUS Status;
OBJECT_ATTRIBUTES Obja;
OBJECT_STRING FileName;
FILE_NETWORK_OPEN_INFORMATION NetworkInfo;
LPWIN32_FILE_ATTRIBUTE_DATA AttributeData;
//
// Check the parameters. Note that for now there is only one info level,
// so there's no special code here to determine what to do.
//
RIP_ON_NOT_TRUE("GetFileAttributesEx()",
(fInfoLevelId < GetFileExMaxInfoLevel && fInfoLevelId >= GetFileExInfoStandard));
RtlInitObjectString(&FileName, lpFileName);
InitializeObjectAttributes(
&Obja,
&FileName,
OBJ_CASE_INSENSITIVE,
ObDosDevicesDirectory(),
NULL
);
//
// Query the information about the file using the path-based NT service.
//
Status = NtQueryFullAttributesFile( &Obja, &NetworkInfo );
if ( NT_SUCCESS(Status) ) {
AttributeData = (LPWIN32_FILE_ATTRIBUTE_DATA)lpFileInformation;
AttributeData->dwFileAttributes = NetworkInfo.FileAttributes;
AttributeData->ftCreationTime = *(PFILETIME)&NetworkInfo.CreationTime;
AttributeData->ftLastAccessTime = *(PFILETIME)&NetworkInfo.LastAccessTime;
AttributeData->ftLastWriteTime = *(PFILETIME)&NetworkInfo.LastWriteTime;
AttributeData->nFileSizeHigh = NetworkInfo.EndOfFile.HighPart;
AttributeData->nFileSizeLow = (DWORD)NetworkInfo.EndOfFile.LowPart;
return TRUE;
}
else {
XapiSetLastNTError(Status);
return FALSE;
}
}
BOOL
APIENTRY
MoveFile(
PCOSTR lpExistingFileName,
PCOSTR lpNewFileName
)
{
NTSTATUS Status;
OBJECT_ATTRIBUTES Obja;
HANDLE Handle;
OBJECT_STRING FileName;
IO_STATUS_BLOCK IoStatusBlock;
FILE_RENAME_INFORMATION RenameInfo;
RtlInitObjectString(&FileName, lpExistingFileName);
InitializeObjectAttributes(
&Obja,
&FileName,
OBJ_CASE_INSENSITIVE,
ObDosDevicesDirectory(),
NULL
);
//
// Open the existing file for delete access.
//
Status = NtOpenFile( &Handle,
DELETE | 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) ) {
XapiSetLastNTError(Status);
return FALSE;
}
//
// Rename the file.
//
RenameInfo.ReplaceIfExists = FALSE;
RenameInfo.RootDirectory = ObDosDevicesDirectory();
RtlInitObjectString(&RenameInfo.FileName, lpNewFileName);
Status = NtSetInformationFile(
Handle,
&IoStatusBlock,
&RenameInfo,
sizeof(RenameInfo),
FileRenameInformation
);
NtClose(Handle);
if ( NT_SUCCESS(Status) ) {
return TRUE;
}
else {
XapiSetLastNTError(Status);
return FALSE;
}
}
BOOL
APIENTRY
MoveFileEx(
PCOSTR lpExistingFileName,
PCOSTR lpNewFileName,
DWORD dwFlags
)
{
return MoveFileWithProgress( lpExistingFileName,
lpNewFileName,
(LPPROGRESS_ROUTINE)NULL,
NULL,
dwFlags );
}
BOOL
APIENTRY
MoveFileWithProgress(
PCOSTR lpExistingFileName,
PCOSTR lpNewFileName,
LPPROGRESS_ROUTINE lpProgressRoutine OPTIONAL,
LPVOID lpData OPTIONAL,
DWORD dwFlags
)
/*++
Routine Description:
An existing file can be renamed using MoveFileWithProgressW.
Arguments:
lpExistingFileName - Supplies the name of an existing file that is to be
renamed.
lpNewFileName - Supplies the new name for the existing file. The new
name must reside in the same file system/drive as the existing
file and must not already exist.
lpProgressRoutine - Supplies a callback routine that is notified.
lpData - Supplies context data passed to the progress routine.
dwFlags - Supplies optional flag bits to control the behavior of the
rename. The following bits are currently defined:
MOVEFILE_REPLACE_EXISTING - if the new file name exists, replace
it by renaming the old file name on top of the new file name.
MOVEFILE_COPY_ALLOWED - if the new file name is on a different
volume than the old file name, and causes the rename operation
to fail, then setting this flag allows the MoveFileEx API
call to simulate the rename with a call to CopyFile followed
by a call to DeleteFile to the delete the old file if the
CopyFile was successful.
MOVEFILE_WRITE_THROUGH - perform the rename operation in such a
way that the file has actually been moved on the disk before
the API returns to the caller. Note that this flag causes a
flush at the end of a copy operation (if one were allowed and
necessary), and has no effect if the rename operation is
delayed until the next reboot.
Return Value:
TRUE - The operation was successful.
FALSE/NULL - The operation failed. Extended error status is available
using GetLastError.
--*/
{
NTSTATUS Status;
OBJECT_ATTRIBUTES Obja;
HANDLE Handle;
OBJECT_STRING FileName;
IO_STATUS_BLOCK IoStatusBlock;
FILE_RENAME_INFORMATION RenameInfo;
BOOL b;
RtlInitObjectString(&FileName, lpExistingFileName);
InitializeObjectAttributes(
&Obja,
&FileName,
OBJ_CASE_INSENSITIVE,
ObDosDevicesDirectory(),
NULL
);
//
// Open the existing file for delete access.
//
Status = NtOpenFile( &Handle,
DELETE | 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) ) {
XapiSetLastNTError(Status);
return FALSE;
}
//
// Rename the file.
//
RenameInfo.ReplaceIfExists = ((dwFlags & MOVEFILE_REPLACE_EXISTING) != 0);
RenameInfo.RootDirectory = ObDosDevicesDirectory();
RtlInitObjectString(&RenameInfo.FileName, lpNewFileName);
Status = NtSetInformationFile(
Handle,
&IoStatusBlock,
&RenameInfo,
sizeof(RenameInfo),
FileRenameInformation
);
NtClose(Handle);
if ( NT_SUCCESS(Status) ) {
return TRUE;
}
else {
if ((Status == STATUS_NOT_SAME_DEVICE) &&
((dwFlags & MOVEFILE_COPY_ALLOWED) != 0)) {
b = CopyFileEx(lpExistingFileName,
lpNewFileName,
lpProgressRoutine,
lpData,
NULL,
(RenameInfo.ReplaceIfExists ? 0 : COPY_FILE_FAIL_IF_EXISTS) |
COPY_FILE_OPEN_SOURCE_FOR_WRITE
);
//
// the copy worked... Delete the source of the rename
// if it fails, try a set attributes and then a delete
//
if (b && !DeleteFile( lpExistingFileName ) ) {
//
// If the delete fails, we will return true, but possibly
// leave the source dangling
//
SetFileAttributes(lpExistingFileName,FILE_ATTRIBUTE_NORMAL);
DeleteFile( lpExistingFileName );
}
return b;
}
XapiSetLastNTError(Status);
return FALSE;
}
}
BOOL
APIENTRY
DeleteFile(
PCOSTR lpFileName
)
/*++
Routine Description:
An existing file can be deleted using DeleteFile.
This API provides the same functionality as DOS (int 21h, function 41H)
and OS/2's DosDelete.
Arguments:
lpFileName - Supplies the file name of the file to be deleted.
Return Value:
TRUE - The operation was successful.
FALSE/NULL - The operation failed. Extended error status is available
using GetLastError.
--*/
{
NTSTATUS Status;
OBJECT_ATTRIBUTES Obja;
HANDLE Handle;
OBJECT_STRING FileName;
IO_STATUS_BLOCK IoStatusBlock;
FILE_DISPOSITION_INFORMATION Disposition;
RtlInitObjectString(&FileName, lpFileName);
InitializeObjectAttributes(
&Obja,
&FileName,
OBJ_CASE_INSENSITIVE,
ObDosDevicesDirectory(),
NULL
);
//
// Open the file for delete access.
//
Status = NtOpenFile(
&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) ) {
XapiSetLastNTError(Status);
return FALSE;
}
//
// Delete the file
//
#undef DeleteFile
Disposition.DeleteFile = TRUE;
Status = NtSetInformationFile(
Handle,
&IoStatusBlock,
&Disposition,
sizeof(Disposition),
FileDispositionInformation
);
NtClose(Handle);
if ( NT_SUCCESS(Status) ) {
return TRUE;
}
else {
XapiSetLastNTError(Status);
return FALSE;
}
}
BOOL
WINAPI
XSetFileCacheSize(
SIZE_T dwCacheSize
)
/*++
Routine Description:
This routine changes the number of bytes allocated to the file system cache.
Arguments:
dwCacheSize - Supplies the number of bytes that should be allocated to the
file system cache. The number of bytes is rounded up to a multiple of
the page size.
Return Value:
Status of operation.
TRUE - The operation was successful.
FALSE/NULL - The operation failed. Extended error status is available
using GetLastError.
--*/
{
NTSTATUS status;
PFN_COUNT NumberOfCachePages;
//
// Carefully compute the number of cache pages if the cache size is near
// MAXULONG.
//
NumberOfCachePages = dwCacheSize >> PAGE_SHIFT;
if (BYTE_OFFSET(dwCacheSize) != 0) {
NumberOfCachePages++;
}
status = FscSetCacheSize(NumberOfCachePages);
if (NT_SUCCESS(status)) {
return TRUE;
} else {
XapiSetLastNTError(status);
return FALSE;
}
}
SIZE_T
WINAPI
XGetFileCacheSize(
VOID
)
/*++
Routine Description:
This routine returns the number of bytes allocated to the file system cache.
Arguments:
None.
Return Value:
Number of bytes currently allocated to the file system cache.
--*/
{
return FscGetCacheSize() << PAGE_SHIFT;
}
NTSTATUS
XapiGetDirectoryDisplayBlocks(
HANDLE DirHandle,
DWORD *TotalBlocks,
PFILE_DIRECTORY_INFORMATION DirectoryInfo
)
/*++
Routine Description:
Helper function for XDisplayBlocksFromPath. Does the recursion of directories.
--*/
{
NTSTATUS status;
IO_STATUS_BLOCK ioStatusBlock;
BOOLEAN fFindFirst = TRUE;
DWORD fileCount = 0;
HANDLE subdirHandle;
OBJECT_STRING subdirName;
OBJECT_ATTRIBUTES objectAttributes;
DWORD directorySize;
do
{
status = NtQueryDirectoryFile(
DirHandle,
NULL,
NULL,
NULL,
&ioStatusBlock,
DirectoryInfo,
sizeof(FILE_DIRECTORY_INFORMATION)+sizeof(OCHAR)*254,
FileDirectoryInformation,
NULL,
fFindFirst
);
fFindFirst = FALSE;
//
// If we succesfully found a file or directory then we have work to do
//
if (NT_SUCCESS(status))
{
//
// The fileCount and fileNameSizes will go into
// computing the blocks required by this directory.
//
fileCount++;
//
// If this is a directory we will need to recurse it.
//
if(FILE_ATTRIBUTE_DIRECTORY&DirectoryInfo->FileAttributes)
{
//
// Null terminate filename
//
DirectoryInfo->FileName[DirectoryInfo->FileNameLength/sizeof(OCHAR)] = OBJECT_NULL;
//
// Create an OBJECT_STRING for the relative path of the directory
//
RtlInitObjectString(&subdirName, DirectoryInfo->FileName);
//
// Initialize Obja with Handle of the parent and the relative path.
//
InitializeObjectAttributes(
&objectAttributes,
&subdirName,
OBJ_CASE_INSENSITIVE,
DirHandle,
NULL
);
//
// Open the directory
//
status = NtOpenFile(
&subdirHandle,
FILE_LIST_DIRECTORY | SYNCHRONIZE,
&objectAttributes,
&ioStatusBlock,
FILE_SHARE_READ | FILE_SHARE_WRITE,
FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT
);
if(NT_SUCCESS(status))
{
status = XapiGetDirectoryDisplayBlocks(subdirHandle, TotalBlocks, DirectoryInfo);
NtClose(subdirHandle);
}
} else
{
if(DirectoryInfo->EndOfFile.HighPart)
{
*TotalBlocks = MAX_DISPLAY_BLOCKS;
break;
} else
{
DWORD fileBytes, fileBlocks;
fileBytes = DirectoryInfo->EndOfFile.LowPart;
fileBlocks = (fileBytes/0x4000) + ((fileBytes%0x4000) ? 1 : 0);
if(fileBlocks >= MAX_DISPLAY_BLOCKS)
{
fileBlocks = MAX_DISPLAY_BLOCKS;
} else
{
*TotalBlocks += fileBlocks;
}
}
}
if(*TotalBlocks >= MAX_DISPLAY_BLOCKS)
{
*TotalBlocks = MAX_DISPLAY_BLOCKS;
break;
}
}
} while(NT_SUCCESS(status));
//
// How much space the directory itself occupies.
// CAVEAT: directory entries don't shrink so
// this calculation could underestimate the size
// if many files\or subdirectoires have been deleted.
//
directorySize = fileCount*sizeof(DIRENT);
*TotalBlocks += (directorySize/0x4000);
*TotalBlocks += (directorySize%0x4000) ? 1 : 0;
if(*TotalBlocks >= MAX_DISPLAY_BLOCKS)
{
*TotalBlocks = MAX_DISPLAY_BLOCKS;
}
//
// Don't fail if we just found an empty directory
// or got to the end of it.
//
if(
(status == STATUS_NO_MORE_FILES) ||
(status == STATUS_NO_SUCH_FILE)
){
status = STATUS_SUCCESS;
}
return status;
}
DWORD
WINAPI
XGetDisplayBlocks(
PCOSTR lpPathName
)
/*++
Routine Description:
Given a path to a file or directory, recursively computes the number
of display blocks (bytes on disk\16kbytes) taken up by that file or
directory and all of its subdirectories and files.
Arguments:
On Success:
Return Value:
On Success: Number of Display Blocks used by the path. If the total is greater than 50,000,
then MAX_DISPLAY_BLOCKS is returned.
On Failure: 0, call GetLastError() for extended error information.
--*/
{
OBJECT_STRING objectName;
OBJECT_ATTRIBUTES objectAttributes;
FILE_NETWORK_OPEN_INFORMATION networkInfo;
HANDLE dirHandle;
IO_STATUS_BLOCK ioStatusBlock;
NTSTATUS status;
DWORD totalBlocks;
//
// Open a handle to the path.
//
RtlInitObjectString(&objectName, lpPathName);
InitializeObjectAttributes(&objectAttributes,
&objectName,
OBJ_CASE_INSENSITIVE,
ObDosDevicesDirectory(),
NULL
);
status = NtQueryFullAttributesFile(&objectAttributes, &networkInfo);
if(NT_SUCCESS(status))
{
if(networkInfo.FileAttributes&FILE_ATTRIBUTE_DIRECTORY)
{
totalBlocks = 0;
status = NtOpenFile(
&dirHandle,
FILE_LIST_DIRECTORY | SYNCHRONIZE,
&objectAttributes,
&ioStatusBlock,
FILE_SHARE_READ | FILE_SHARE_WRITE,
FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT
);
if(NT_SUCCESS(status))
{
struct {FILE_DIRECTORY_INFORMATION DirInfo;OCHAR Names[255];} buffer;
PFILE_DIRECTORY_INFORMATION directoryInfo = &buffer.DirInfo;
status = XapiGetDirectoryDisplayBlocks(dirHandle, &totalBlocks, directoryInfo);
NtClose(dirHandle);
}
} else
{
if(networkInfo.EndOfFile.HighPart)
{
totalBlocks = MAX_DISPLAY_BLOCKS;
} else
{
DWORD fileBytes;
fileBytes = networkInfo.EndOfFile.LowPart;
totalBlocks = (fileBytes/0x4000) + ((fileBytes%0x4000) ? 1 : 0);
if(totalBlocks > MAX_DISPLAY_BLOCKS) totalBlocks = MAX_DISPLAY_BLOCKS;
}
}
}
//
// Set an error on failure.
//
if(!NT_SUCCESS(status))
{
XapiSetLastNTError(status);
return 0;
}
return totalBlocks;
}