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.
855 lines
22 KiB
855 lines
22 KiB
/*++
|
|
|
|
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;
|
|
|
|
if (_snwprintf( Buffer,
|
|
sizeof( Buffer ) / sizeof( Buffer[0] ) - 1, /* leave space for null */
|
|
L"\\DosDevices\\%s\\$Extend\\$Quota:$Q:$INDEX_ALLOCATION",
|
|
Drive ) < 0) {
|
|
DisplayMsg( MSG_DRIVE_TOO_LONG );
|
|
return INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
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);
|
|
if (ptr == NULL) {
|
|
DisplayErrorMsg( ERROR_NOT_ENOUGH_MEMORY );
|
|
ExitCode = EXIT_CODE_FAILURE;
|
|
leave;
|
|
}
|
|
|
|
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)->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];
|
|
|
|
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))
|
|
{
|
|
|
|
AccountName[AccountLength] = 0;
|
|
DomainName[DomainLength] = 0;
|
|
|
|
switch (SidNameUse) {
|
|
case SidTypeUser:
|
|
DisplayMsg( MSG_QUOTA_SID_USER, DomainName, AccountName );
|
|
break;
|
|
case SidTypeGroup:
|
|
DisplayMsg( MSG_QUOTA_SID_GROUP, DomainName, AccountName );
|
|
break;
|
|
case SidTypeDomain:
|
|
DisplayMsg( MSG_QUOTA_SID_DOMAIN, DomainName, AccountName );
|
|
break;
|
|
case SidTypeAlias:
|
|
DisplayMsg( MSG_QUOTA_SID_ALIAS, DomainName, AccountName );
|
|
break;
|
|
case SidTypeWellKnownGroup:
|
|
DisplayMsg( MSG_QUOTA_SID_WELLKNOWNGROUP, DomainName, AccountName );
|
|
break;
|
|
case SidTypeDeletedAccount:
|
|
DisplayMsg( MSG_QUOTA_SID_DELETEDACCOUNT, DomainName, AccountName );
|
|
break;
|
|
case SidTypeInvalid:
|
|
DisplayMsg( MSG_QUOTA_SID_INVALID, DomainName, AccountName );
|
|
break;
|
|
case SidTypeUnknown:
|
|
default:
|
|
DisplayMsg( MSG_QUOTA_SID_USER, DomainName, AccountName );
|
|
break;
|
|
}
|
|
|
|
} 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;
|
|
PFILE_QUOTA_INFORMATION QuotaInfo;
|
|
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 );
|
|
|
|
|
|
QuotaInfo = malloc( sizeof( FILE_QUOTA_INFORMATION ) + SID_MAX_LENGTH );
|
|
if (QuotaInfo == NULL) {
|
|
DisplayErrorMsg( ERROR_NOT_ENOUGH_MEMORY );
|
|
CloseHandle( hFile );
|
|
return EXIT_CODE_FAILURE;
|
|
}
|
|
|
|
while (TRUE) {
|
|
|
|
Status = NtQueryQuotaInformationFile(
|
|
hFile, // File Handle
|
|
&IoStatus, // IO status block
|
|
QuotaInfo, // Buffer
|
|
sizeof( FILE_QUOTA_INFORMATION ) + SID_MAX_LENGTH, // 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 );
|
|
free( QuotaInfo );
|
|
|
|
return ExitCode;
|
|
}
|
|
|
|
|
|
INT
|
|
ChangeDiskQuota(
|
|
IN INT argc,
|
|
IN PWSTR argv[]
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
HANDLE hFile = INVALID_HANDLE_VALUE;
|
|
IO_STATUS_BLOCK IoStatus;
|
|
PFILE_QUOTA_INFORMATION QuotaInfo;
|
|
PFILE_GET_QUOTA_INFORMATION SidList;
|
|
PFILE_QUOTA_INFORMATION QuotaInfoPtr;
|
|
PFILE_GET_QUOTA_INFORMATION SidListPtr;
|
|
ULONG BufferSize;
|
|
ULONG SidListLength;
|
|
PWSTR EndPtr;
|
|
INT retval = EXIT_CODE_SUCCESS;
|
|
|
|
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;
|
|
}
|
|
|
|
QuotaInfo = malloc( sizeof( FILE_QUOTA_INFORMATION ) + SID_MAX_LENGTH );
|
|
SidList = malloc( sizeof( FILE_GET_QUOTA_INFORMATION ) + SID_MAX_LENGTH );
|
|
|
|
try {
|
|
|
|
if (QuotaInfo == NULL || SidList == NULL) {
|
|
DisplayErrorMsg( ERROR_NOT_ENOUGH_MEMORY );
|
|
retval = EXIT_CODE_FAILURE;
|
|
leave;
|
|
}
|
|
|
|
RtlZeroMemory(QuotaInfo, sizeof( FILE_QUOTA_INFORMATION ) + SID_MAX_LENGTH);
|
|
QuotaInfoPtr = QuotaInfo;
|
|
RtlZeroMemory(SidList, sizeof( FILE_GET_QUOTA_INFORMATION ) + SID_MAX_LENGTH);
|
|
SidListPtr = SidList;
|
|
|
|
if (!GetUserSid( argv[3], &QuotaInfoPtr, &SidListPtr )) {
|
|
DisplayError();
|
|
retval = EXIT_CODE_FAILURE;
|
|
leave;
|
|
}
|
|
|
|
QuotaInfoPtr->QuotaThreshold.QuadPart = My_wcstoui64( argv[1], &EndPtr, 0 );
|
|
if (UnsignedI64NumberCheck( QuotaInfoPtr->QuotaThreshold.QuadPart, EndPtr )) {
|
|
|
|
DisplayMsg( MSG_USAGE_QUOTA_MODIFY );
|
|
retval = EXIT_CODE_FAILURE;
|
|
leave;
|
|
}
|
|
|
|
QuotaInfoPtr->QuotaLimit.QuadPart = My_wcstoui64( argv[2], &EndPtr, 0 );
|
|
if (UnsignedI64NumberCheck( QuotaInfoPtr->QuotaLimit.QuadPart, EndPtr )) {
|
|
|
|
DisplayMsg( MSG_USAGE_QUOTA_MODIFY );
|
|
retval = EXIT_CODE_FAILURE;
|
|
leave;
|
|
}
|
|
|
|
//
|
|
// Build the complete quota info data structure
|
|
//
|
|
|
|
QuotaInfoPtr->NextEntryOffset = 0;
|
|
// QuotaInfoPtr->SidLength set in GetUserSid
|
|
// QuotaInfoPtr->ChangeTime is ignored
|
|
// QuotaInfoPtr->QuotaUsed is ignored
|
|
// QuotaInfoPtr->QuotaThreshold is set above
|
|
// QuotaInfoPtr->QuotaLimit is set above
|
|
// QuotaInfoPtr->Sid is set in GetUserSid
|
|
|
|
//
|
|
// Terminate the list.
|
|
//
|
|
|
|
BufferSize = FIELD_OFFSET( FILE_QUOTA_INFORMATION, Sid ) + QuotaInfoPtr->SidLength;
|
|
|
|
//
|
|
// Change it
|
|
//
|
|
|
|
hFile = QuotaOpenVolume( argv[0] );
|
|
if (hFile == INVALID_HANDLE_VALUE) {
|
|
DisplayError();
|
|
retval = EXIT_CODE_FAILURE;
|
|
leave;
|
|
}
|
|
|
|
Status = NtSetQuotaInformationFile(
|
|
hFile,
|
|
&IoStatus,
|
|
QuotaInfo,
|
|
BufferSize
|
|
);
|
|
if (!NT_SUCCESS( Status )) {
|
|
DisplayErrorMsg( RtlNtStatusToDosError( Status ));
|
|
retval = EXIT_CODE_FAILURE;
|
|
leave;
|
|
}
|
|
retval = EXIT_CODE_SUCCESS;
|
|
} finally {
|
|
if (hFile != INVALID_HANDLE_VALUE) {
|
|
CloseHandle( hFile );
|
|
}
|
|
|
|
free( QuotaInfo );
|
|
free( SidList );
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|