windows-nt/Source/XPSP1/NT/base/fs/ntfs/fu/quota.c
2020-09-26 16:20:57 +08:00

790 lines
19 KiB
C

/*++
Copyright (c) 2000 Microsoft Corporation
Module Name:
quota.c
Abstract:
This file contains code for commands that affect quotas.
Author:
Wesley Witt [wesw] 1-March-2000
Revision History:
--*/
#include <precomp.h>
#define BUFFER_SIZE 1024
#define IO_FILE_QUOTA_THRESHOLD 0x40040024L
#define IO_FILE_QUOTA_LIMIT 0x40040025L
#define SID_MAX_LENGTH (FIELD_OFFSET(SID, SubAuthority) + sizeof(ULONG) * SID_MAX_SUB_AUTHORITIES)
INT
QuotaHelp(
IN INT argc,
IN PWSTR argv[]
)
{
DisplayMsg( MSG_USAGE_QUOTA );
return EXIT_CODE_SUCCESS;
}
HANDLE
QuotaOpenVolume(
IN PWSTR Drive
)
{
NTSTATUS Status;
HANDLE hFile;
OBJECT_ATTRIBUTES ObjectAttributes;
IO_STATUS_BLOCK IoStatus;
WCHAR Buffer[MAX_PATH];
UNICODE_STRING FileName;
swprintf(
Buffer,
L"\\DosDevices\\%s\\$Extend\\$Quota:$Q:$INDEX_ALLOCATION",
Drive
);
RtlInitUnicodeString( &FileName, Buffer );
InitializeObjectAttributes(
&ObjectAttributes,
&FileName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL
);
Status = NtOpenFile(
&hFile,
FILE_READ_DATA | FILE_WRITE_DATA | SYNCHRONIZE,
&ObjectAttributes,
&IoStatus,
FILE_SHARE_READ | FILE_SHARE_WRITE,
FILE_SYNCHRONOUS_IO_ALERT | FILE_OPEN_FOR_BACKUP_INTENT
);
if (!NT_SUCCESS( Status )) {
DisplayErrorMsg( RtlNtStatusToDosError( Status ), Drive );
return INVALID_HANDLE_VALUE;
}
return hFile;
}
INT
QuotaControl(
IN PWSTR Drive,
IN ULONG QuotaControlBits
)
{
HANDLE Handle = INVALID_HANDLE_VALUE;
NTSTATUS Status;
IO_STATUS_BLOCK IoStatus;
FILE_FS_CONTROL_INFORMATION ControlInfo;
INT ExitCode = EXIT_CODE_SUCCESS;
do {
if (!IsVolumeLocalNTFS( Drive[0] )) {
DisplayMsg( MSG_NTFS_REQUIRED );
ExitCode = EXIT_CODE_FAILURE;
break;
}
Handle = QuotaOpenVolume( Drive );
if (Handle == INVALID_HANDLE_VALUE) {
ExitCode = EXIT_CODE_FAILURE;
break;
}
//
// get the current state of quota tracking on this volume
//
Status = NtQueryVolumeInformationFile(
Handle,
&IoStatus,
&ControlInfo,
sizeof( ControlInfo ),
FileFsControlInformation
);
if (!NT_SUCCESS( Status )) {
DisplayErrorMsg( RtlNtStatusToDosError( Status ));
ExitCode = EXIT_CODE_FAILURE;
break;
}
//
// Set in the new control bits
//
ControlInfo.FileSystemControlFlags &= ~FILE_VC_QUOTA_MASK;
ControlInfo.FileSystemControlFlags |= QuotaControlBits;
//
// Set the control info back onto the volume
//
Status = NtSetVolumeInformationFile(
Handle,
&IoStatus,
&ControlInfo,
sizeof( ControlInfo ),
FileFsControlInformation
);
if (!NT_SUCCESS( Status )) {
DisplayErrorMsg( RtlNtStatusToDosError( Status ));
ExitCode = EXIT_CODE_FAILURE;
break;
}
} while (FALSE);
if (Handle != INVALID_HANDLE_VALUE) {
CloseHandle( Handle );
}
return ExitCode;
}
INT
DisableQuota(
IN INT argc,
IN PWSTR argv[]
)
/*++
Routine Description:
Disables quotas on a specified volume
Arguments:
argc - The argument count, should be 1
argv - Array of strings, the first one must be a drive
Return Value:
None
--*/
{
if (argc != 1) {
DisplayMsg( MSG_USAGE_QUOTA_DISABLE );
if (argc != 0) {
return EXIT_CODE_FAILURE;
} else {
return EXIT_CODE_SUCCESS;
}
}
return QuotaControl( argv[0], FILE_VC_QUOTA_NONE );
}
INT
TrackQuota(
IN INT argc,
IN PWSTR argv[]
)
/*++
Routine Description:
Tracks quotas on a specified volume
Arguments:
argc - The argument count, should be 1
argv - Array of strings, the first one must be a drive
Return Value:
None
--*/
{
if (argc != 1) {
DisplayMsg( MSG_USAGE_QUOTA_TRACK );
if (argc != 0) {
return EXIT_CODE_FAILURE;
} else {
return EXIT_CODE_SUCCESS;
}
}
return QuotaControl( argv[0], FILE_VC_QUOTA_TRACK );
}
INT
EnforceQuota(
IN INT argc,
IN PWSTR argv[]
)
/*++
Routine Description:
Enforces quotas on a specified volume
Arguments:
argc - The argument count, should be 1
argv - Array of strings, the first one must be a drive
Return Value:
None
--*/
{
if (argc != 1) {
DisplayMsg( MSG_USAGE_QUOTA_ENFORCE );
if (argc != 0) {
return EXIT_CODE_FAILURE;
} else {
return EXIT_CODE_SUCCESS;
}
}
return QuotaControl( argv[0], FILE_VC_QUOTA_ENFORCE );
}
INT
GetDiskQuotaViolation(
IN INT argc,
IN PWSTR argv[]
)
/*++
Routine Description:
This is the routine for querying the disk quota violation.
Arguments:
argc - The argument count.
argv - Array of strings of the form:
' fscutl getdskqv '
Return Value:
None
--*/
{
HANDLE handle = NULL;
EVENTLOGRECORD *pevlr, *ptr;
PWCHAR EventString;
DWORD dwRead;
DWORD dwNeeded;
BOOLEAN flag = TRUE;
BOOL Status;
PSID psid;
SID_NAME_USE Use;
WCHAR Name[MAX_PATH];
DWORD dwNameSize;
WCHAR Domain[MAX_PATH];
DWORD dwDomainSize;
WCHAR Log[2][16] = { L"System", L"Application" };
WORD index;
INT ExitCode = EXIT_CODE_SUCCESS;
try {
if (argc != 0) {
DisplayMsg( MSG_USAGE_QUOTA_VIOLATIONS );
if (argc != 0) {
ExitCode = EXIT_CODE_FAILURE;
}
leave;
}
for ( index = 0 ; index < 2 ; index++ ) {
handle = OpenEventLog( NULL, Log[index] );
if ( handle == NULL ) {
DisplayMsg( MSG_COULD_NOT_OPEN_EVENTLOG );
DisplayError();
ExitCode = EXIT_CODE_FAILURE;
leave;
}
ptr = pevlr = (EVENTLOGRECORD *) malloc (BUFFER_SIZE);
DisplayMsg( MSG_SEARCHING_EVENTLOG, Log[index] );
while(ReadEventLog(
handle,
EVENTLOG_FORWARDS_READ | EVENTLOG_SEQUENTIAL_READ,
0,
pevlr,
BUFFER_SIZE,
&dwRead,
&dwNeeded))
{
while(dwRead > 0) {
if (pevlr->EventID == IO_FILE_QUOTA_THRESHOLD || pevlr->EventID == IO_FILE_QUOTA_LIMIT) {
flag = FALSE;
switch(pevlr->EventID) {
case IO_FILE_QUOTA_THRESHOLD :
DisplayMsg( MSG_QUOTA_THREASHOLD );
break;
case IO_FILE_QUOTA_LIMIT:
DisplayMsg( MSG_QUOTA_LIMIT );
break;
}
DisplayMsg( MSG_EVENT_ID, pevlr->EventID );
switch(pevlr->EventType) {
case EVENTLOG_ERROR_TYPE:
DisplayMsg( MSG_EVENT_TYPE_ERROR );
break;
case EVENTLOG_WARNING_TYPE:
DisplayMsg( MSG_EVENT_TYPE_WARNING );
break;
case EVENTLOG_INFORMATION_TYPE:
DisplayMsg( MSG_EVENT_TYPE_INFORMATION );
break;
case EVENTLOG_AUDIT_SUCCESS:
DisplayMsg( MSG_EVENT_TYPE_AUDIT_SUCCESS );
break;
case EVENTLOG_AUDIT_FAILURE:
DisplayMsg( MSG_EVENT_TYPE_AUDIT_FAILURE );
break;
}
DisplayMsg( MSG_EVENT_CATEGORY, pevlr->EventCategory );
DisplayMsg( MSG_EVENT_SOURCE, (LPWSTR)((LPBYTE) pevlr + sizeof(EVENTLOGRECORD)) );
if (pevlr->UserSidOffset > 0) {
psid = (PSID) ( (PBYTE)pevlr + pevlr->UserSidOffset );
dwNameSize = MAX_PATH;
dwDomainSize = MAX_PATH;
Status = LookupAccountSid(
NULL,
psid,
Name,
&dwNameSize,
Domain,
&dwDomainSize,
&Use
);
if (Status) {
DisplayMsg( MSG_USERNAME, Domain, Name );
}
}
if (pevlr->NumStrings == 2) {
EventString = (PWCHAR) ((PBYTE)pevlr + pevlr->StringOffset);
EventString += wcslen( EventString ) + 1;
DisplayMsg( MSG_EVENT_DATA, EventString );
}
}
dwRead -= pevlr->Length;
pevlr = (EVENTLOGRECORD *)((PBYTE) pevlr + pevlr->Length);
}
pevlr = ptr;
}
CloseEventLog(handle);
handle = NULL;
}
if (flag) {
DisplayMsg( MSG_GOOD_QUOTA );
}
} finally {
if (handle != NULL) {
CloseHandle( handle );
}
}
return ExitCode;
}
PWSTR
FileTimeToString(
PLARGE_INTEGER FileTime
)
{
ULONG ElapsedSeconds;
if (RtlTimeToSecondsSince1970( FileTime, &ElapsedSeconds )) {
PWSTR TimeString = _wctime( (time_t*)&ElapsedSeconds );
if (TimeString) {
TimeString[wcslen(TimeString)-1] = 0;
}
return TimeString;
}
return L"";
}
BOOL
GetUserSid(
PWSTR UserName,
PFILE_QUOTA_INFORMATION *QuotaInfoPtr,
PFILE_GET_QUOTA_INFORMATION *SidListPtr
)
{
WCHAR Domain[128];
ULONG DomainLength;
SID_NAME_USE SidNameUse;
*QuotaInfoPtr = (PFILE_QUOTA_INFORMATION) ((PCHAR) *QuotaInfoPtr + (*QuotaInfoPtr)->NextEntryOffset);
*SidListPtr = (PFILE_GET_QUOTA_INFORMATION) ((PCHAR) *SidListPtr + (*SidListPtr)->NextEntryOffset);
(*QuotaInfoPtr)->SidLength = SID_MAX_LENGTH;
DomainLength = sizeof(Domain);
if (!LookupAccountName(
NULL,
UserName,
&(*QuotaInfoPtr)->Sid,
&(*QuotaInfoPtr)->SidLength,
Domain,
&DomainLength,
&SidNameUse))
{
DomainLength = GetLastError();
return FALSE;
}
//
// Initialize the values to something resonable.
//
(*QuotaInfoPtr)->QuotaThreshold.QuadPart = ~0;
(*QuotaInfoPtr)->QuotaLimit.QuadPart = ~0;
(*QuotaInfoPtr)->SidLength = RtlLengthSid( &(*QuotaInfoPtr)->Sid);
(*QuotaInfoPtr)->NextEntryOffset =
FIELD_OFFSET( FILE_QUOTA_INFORMATION, Sid ) +
QuadAlign((*QuotaInfoPtr)->SidLength);
memcpy( &(*SidListPtr)->Sid, &(*QuotaInfoPtr)->Sid, (*QuotaInfoPtr)->SidLength);
(*SidListPtr)->SidLength = (*QuotaInfoPtr)->SidLength;
(*SidListPtr)->NextEntryOffset =
FIELD_OFFSET( FILE_GET_QUOTA_INFORMATION, Sid ) +
QuadAlign((*SidListPtr)->SidLength);
return TRUE;
}
BOOL
DumpVolumeQuotaInfo(
IN HANDLE hFile,
IN BOOL DisplayAlways
)
{
NTSTATUS Status;
IO_STATUS_BLOCK IoStatus;
FILE_FS_CONTROL_INFORMATION TempControlInfo = {0};
ULONG StatusId;
ULONG LoggingId;
ULONG ValueId;
Status = NtQueryVolumeInformationFile(
hFile,
&IoStatus,
&TempControlInfo,
sizeof(FILE_FS_CONTROL_INFORMATION),
FileFsControlInformation
);
//
// If quotas aren't enforced or tracked, then quotas must be disabled.
//
if ((TempControlInfo.FileSystemControlFlags & FILE_VC_QUOTA_MASK) == FILE_VC_QUOTA_NONE) {
StatusId = MSG_QUOTA_STATUS_DISABLED;
} else if ((TempControlInfo.FileSystemControlFlags & FILE_VC_QUOTA_MASK) == FILE_VC_QUOTA_TRACK) {
StatusId = MSG_QUOTA_STATUS_TRACKING;
} else {
ASSERT( TempControlInfo.FileSystemControlFlags & FILE_VC_QUOTA_ENFORCE );
StatusId = MSG_QUOTA_STATUS_ENFORCE;
}
if (!DisplayAlways) {
return StatusId != MSG_QUOTA_STATUS_DISABLED;
}
switch (TempControlInfo.FileSystemControlFlags & (FILE_VC_LOG_QUOTA_LIMIT | FILE_VC_LOG_QUOTA_THRESHOLD)) {
case FILE_VC_LOG_QUOTA_LIMIT:
LoggingId = MSG_QUOTA_LOGGING_LIMITS;
break;
case FILE_VC_LOG_QUOTA_THRESHOLD:
LoggingId = MSG_QUOTA_LOGGING_THRESH;
break;
case FILE_VC_LOG_QUOTA_LIMIT | FILE_VC_LOG_QUOTA_THRESHOLD:
LoggingId = MSG_QUOTA_LOGGING_BOTH;
break;
case 0:
LoggingId = MSG_QUOTA_LOGGING_EVENTS;
break;
}
if (TempControlInfo.FileSystemControlFlags & FILE_VC_QUOTA_MASK) {
if (TempControlInfo.FileSystemControlFlags & FILE_VC_QUOTAS_INCOMPLETE) {
ValueId = MSG_QUOTA_VALUES_INCOMPLETE;
} else {
ValueId = MSG_QUOTA_VALUES_GOOD;
}
} else {
ValueId = MSG_QUOTA_VALUES_GOOD;
}
DisplayMsg( MSG_USAGE_QUOTA_VOLUME_INFO, TempControlInfo.FileSystemControlFlags );
DisplayMsg( StatusId );
DisplayMsg( LoggingId );
DisplayMsg( ValueId );
DisplayMsg( MSG_USAGE_QUOTA_LIMITS,
QuadToPaddedHexText( TempControlInfo.DefaultQuotaThreshold.QuadPart ),
QuadToPaddedHexText( TempControlInfo.DefaultQuotaLimit.QuadPart )
);
return TRUE;
}
VOID
DumpQuota (
IN PFILE_QUOTA_INFORMATION FileQuotaInfo,
IN PWSTR ServerName
)
{
NTSTATUS Status;
SID_NAME_USE SidNameUse;
ULONG AccountLength, DomainLength;
WCHAR AccountName[128];
WCHAR DomainName[128];
UNICODE_STRING String;
AccountLength = sizeof(AccountName) - 1;
DomainLength = sizeof(DomainName) - 1;
if (FileQuotaInfo->SidLength == 0) {
DisplayMsg( MSG_QUOTA_DUMP_DEFAULT );
} else if (LookupAccountSid(
ServerName,
&FileQuotaInfo->Sid,
AccountName,
&AccountLength,
DomainName,
&DomainLength,
&SidNameUse))
{
PWSTR String;
AccountName[AccountLength] = 0;
DomainName[DomainLength] = 0;
DisplayMsg( MSG_QUOTA_SID_USER+SidNameUse-1, DomainName, AccountName );
} else {
DisplayError( );
}
DisplayMsg(
MSG_QUOTA_DUMP_INFO,
FileTimeToString(&FileQuotaInfo->ChangeTime),
QuadToDecimalText( FileQuotaInfo->QuotaUsed.QuadPart ),
QuadToDecimalText( FileQuotaInfo->QuotaThreshold.QuadPart ),
QuadToDecimalText( FileQuotaInfo->QuotaLimit.QuadPart )
);
}
INT
QueryDiskQuota(
IN INT argc,
IN PWSTR argv[]
)
{
NTSTATUS Status;
HANDLE hFile;
IO_STATUS_BLOCK IoStatus;
FILE_QUOTA_INFORMATION QuotaInfo[STRUCT_COUNT(10, FILE_QUOTA_INFORMATION, 4)];
INT ExitCode = EXIT_CODE_SUCCESS;
if (argc != 1) {
DisplayMsg( MSG_USAGE_QUOTA_QUERY, argv[1] );
if (argc != 0) {
return EXIT_CODE_FAILURE;
} else {
return EXIT_CODE_SUCCESS;
}
}
if (!IsVolumeLocalNTFS( argv[0][0] )) {
DisplayMsg( MSG_NTFS_REQUIRED );
return EXIT_CODE_FAILURE;
}
hFile = QuotaOpenVolume( argv[0] );
if (hFile == INVALID_HANDLE_VALUE) {
return EXIT_CODE_FAILURE;
}
if (!DumpVolumeQuotaInfo( hFile, FALSE )) {
DisplayMsg( MSG_USAGE_QUOTA_REQUIRED, argv[0] );
CloseHandle( hFile );
return EXIT_CODE_FAILURE;
}
DumpVolumeQuotaInfo( hFile, TRUE );
while (TRUE) {
Status = NtQueryQuotaInformationFile(
hFile, // File Handle
&IoStatus, // IO status block
QuotaInfo, // Buffer
sizeof(QuotaInfo), // Length
TRUE, // Return Single Entry
NULL, // SidList
0, // SidListLength
NULL, // StartSid
FALSE // RestartScan
);
if (!NT_SUCCESS( Status )) {
if (Status != STATUS_NO_MORE_ENTRIES) {
DisplayErrorMsg( RtlNtStatusToDosError( Status ));
ExitCode = EXIT_CODE_FAILURE;
}
break;
}
DumpQuota( QuotaInfo, NULL );
}
CloseHandle( hFile );
return ExitCode;
}
INT
ChangeDiskQuota(
IN INT argc,
IN PWSTR argv[]
)
{
NTSTATUS Status;
HANDLE hFile;
IO_STATUS_BLOCK IoStatus;
FILE_QUOTA_INFORMATION QuotaInfo[STRUCT_COUNT(10, FILE_QUOTA_INFORMATION, 4)];
FILE_GET_QUOTA_INFORMATION SidList[STRUCT_COUNT(10, FILE_GET_QUOTA_INFORMATION, 4)];
PFILE_QUOTA_INFORMATION QuotaInfoPtr;
PFILE_GET_QUOTA_INFORMATION SidListPtr;
ULONG BufferSize;
ULONG SidListLength;
PWSTR EndPtr;
if (argc != 4) {
DisplayMsg( MSG_USAGE_QUOTA_MODIFY );
if (argc != 0) {
return EXIT_CODE_FAILURE;
} else {
return EXIT_CODE_SUCCESS;
}
}
if (!IsVolumeLocalNTFS( argv[0][0] )) {
DisplayMsg( MSG_NTFS_REQUIRED );
return EXIT_CODE_FAILURE;
}
RtlZeroMemory(&QuotaInfo, sizeof(QuotaInfo));
QuotaInfoPtr = QuotaInfo;
RtlZeroMemory(&SidList, sizeof(SidList));
SidListPtr = SidList;
if (!GetUserSid( argv[3], &QuotaInfoPtr, &SidListPtr )) {
DisplayError();
return EXIT_CODE_FAILURE;
}
QuotaInfoPtr->QuotaThreshold.QuadPart = My_wcstoui64( argv[1], &EndPtr, 0 );
if (UnsignedI64NumberCheck( QuotaInfoPtr->QuotaThreshold.QuadPart, EndPtr )) {
DisplayMsg( MSG_USAGE_QUOTA_MODIFY );
return EXIT_CODE_FAILURE;
}
QuotaInfoPtr->QuotaLimit.QuadPart = My_wcstoui64( argv[2], &EndPtr, 0 );
if (UnsignedI64NumberCheck( QuotaInfoPtr->QuotaLimit.QuadPart, EndPtr )) {
DisplayMsg( MSG_USAGE_QUOTA_MODIFY );
return EXIT_CODE_FAILURE;
}
//
// Terminate the list.
//
BufferSize = (ULONG)((PCHAR) QuotaInfoPtr - (PCHAR) QuotaInfo + QuotaInfoPtr->NextEntryOffset);
QuotaInfoPtr->NextEntryOffset = 0;
SidListLength = (ULONG)((PCHAR) SidListPtr - (PCHAR) SidList + SidListPtr->NextEntryOffset);
SidListPtr->NextEntryOffset = 0;
SidListPtr = SidList;
//
// Change it
//
hFile = QuotaOpenVolume( argv[0] );
if (hFile == INVALID_HANDLE_VALUE) {
DisplayError();
return EXIT_CODE_FAILURE;
}
Status = NtSetQuotaInformationFile(
hFile,
&IoStatus,
&QuotaInfo,
sizeof(QuotaInfo)
);
if (!NT_SUCCESS( Status )) {
DisplayErrorMsg( RtlNtStatusToDosError( Status ));
CloseHandle( hFile );
return EXIT_CODE_FAILURE;
}
CloseHandle( hFile );
return EXIT_CODE_SUCCESS;
}