Leaked source code of windows server 2003
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

  1. /*++
  2. Copyright (c) 2000 Microsoft Corporation
  3. Module Name:
  4. quota.c
  5. Abstract:
  6. This file contains code for commands that affect quotas.
  7. Author:
  8. Wesley Witt [wesw] 1-March-2000
  9. Revision History:
  10. --*/
  11. #include <precomp.h>
  12. #define BUFFER_SIZE 1024
  13. #define IO_FILE_QUOTA_THRESHOLD 0x40040024L
  14. #define IO_FILE_QUOTA_LIMIT 0x40040025L
  15. #define SID_MAX_LENGTH (FIELD_OFFSET(SID, SubAuthority) + sizeof(ULONG) * SID_MAX_SUB_AUTHORITIES)
  16. INT
  17. QuotaHelp(
  18. IN INT argc,
  19. IN PWSTR argv[]
  20. )
  21. {
  22. DisplayMsg( MSG_USAGE_QUOTA );
  23. return EXIT_CODE_SUCCESS;
  24. }
  25. HANDLE
  26. QuotaOpenVolume(
  27. IN PWSTR Drive
  28. )
  29. {
  30. NTSTATUS Status;
  31. HANDLE hFile;
  32. OBJECT_ATTRIBUTES ObjectAttributes;
  33. IO_STATUS_BLOCK IoStatus;
  34. WCHAR Buffer[MAX_PATH];
  35. UNICODE_STRING FileName;
  36. if (_snwprintf( Buffer,
  37. sizeof( Buffer ) / sizeof( Buffer[0] ) - 1, /* leave space for null */
  38. L"\\DosDevices\\%s\\$Extend\\$Quota:$Q:$INDEX_ALLOCATION",
  39. Drive ) < 0) {
  40. DisplayMsg( MSG_DRIVE_TOO_LONG );
  41. return INVALID_HANDLE_VALUE;
  42. }
  43. RtlInitUnicodeString( &FileName, Buffer );
  44. InitializeObjectAttributes(
  45. &ObjectAttributes,
  46. &FileName,
  47. OBJ_CASE_INSENSITIVE,
  48. NULL,
  49. NULL
  50. );
  51. Status = NtOpenFile(
  52. &hFile,
  53. FILE_READ_DATA | FILE_WRITE_DATA | SYNCHRONIZE,
  54. &ObjectAttributes,
  55. &IoStatus,
  56. FILE_SHARE_READ | FILE_SHARE_WRITE,
  57. FILE_SYNCHRONOUS_IO_ALERT | FILE_OPEN_FOR_BACKUP_INTENT
  58. );
  59. if (!NT_SUCCESS( Status )) {
  60. DisplayErrorMsg( RtlNtStatusToDosError( Status ), Drive );
  61. return INVALID_HANDLE_VALUE;
  62. }
  63. return hFile;
  64. }
  65. INT
  66. QuotaControl(
  67. IN PWSTR Drive,
  68. IN ULONG QuotaControlBits
  69. )
  70. {
  71. HANDLE Handle = INVALID_HANDLE_VALUE;
  72. NTSTATUS Status;
  73. IO_STATUS_BLOCK IoStatus;
  74. FILE_FS_CONTROL_INFORMATION ControlInfo;
  75. INT ExitCode = EXIT_CODE_SUCCESS;
  76. do {
  77. if (!IsVolumeLocalNTFS( Drive[0] )) {
  78. DisplayMsg( MSG_NTFS_REQUIRED );
  79. ExitCode = EXIT_CODE_FAILURE;
  80. break;
  81. }
  82. Handle = QuotaOpenVolume( Drive );
  83. if (Handle == INVALID_HANDLE_VALUE) {
  84. ExitCode = EXIT_CODE_FAILURE;
  85. break;
  86. }
  87. //
  88. // get the current state of quota tracking on this volume
  89. //
  90. Status = NtQueryVolumeInformationFile(
  91. Handle,
  92. &IoStatus,
  93. &ControlInfo,
  94. sizeof( ControlInfo ),
  95. FileFsControlInformation
  96. );
  97. if (!NT_SUCCESS( Status )) {
  98. DisplayErrorMsg( RtlNtStatusToDosError( Status ));
  99. ExitCode = EXIT_CODE_FAILURE;
  100. break;
  101. }
  102. //
  103. // Set in the new control bits
  104. //
  105. ControlInfo.FileSystemControlFlags &= ~FILE_VC_QUOTA_MASK;
  106. ControlInfo.FileSystemControlFlags |= QuotaControlBits;
  107. //
  108. // Set the control info back onto the volume
  109. //
  110. Status = NtSetVolumeInformationFile(
  111. Handle,
  112. &IoStatus,
  113. &ControlInfo,
  114. sizeof( ControlInfo ),
  115. FileFsControlInformation
  116. );
  117. if (!NT_SUCCESS( Status )) {
  118. DisplayErrorMsg( RtlNtStatusToDosError( Status ));
  119. ExitCode = EXIT_CODE_FAILURE;
  120. break;
  121. }
  122. } while (FALSE);
  123. if (Handle != INVALID_HANDLE_VALUE) {
  124. CloseHandle( Handle );
  125. }
  126. return ExitCode;
  127. }
  128. INT
  129. DisableQuota(
  130. IN INT argc,
  131. IN PWSTR argv[]
  132. )
  133. /*++
  134. Routine Description:
  135. Disables quotas on a specified volume
  136. Arguments:
  137. argc - The argument count, should be 1
  138. argv - Array of strings, the first one must be a drive
  139. Return Value:
  140. None
  141. --*/
  142. {
  143. if (argc != 1) {
  144. DisplayMsg( MSG_USAGE_QUOTA_DISABLE );
  145. if (argc != 0) {
  146. return EXIT_CODE_FAILURE;
  147. } else {
  148. return EXIT_CODE_SUCCESS;
  149. }
  150. }
  151. return QuotaControl( argv[0], FILE_VC_QUOTA_NONE );
  152. }
  153. INT
  154. TrackQuota(
  155. IN INT argc,
  156. IN PWSTR argv[]
  157. )
  158. /*++
  159. Routine Description:
  160. Tracks quotas on a specified volume
  161. Arguments:
  162. argc - The argument count, should be 1
  163. argv - Array of strings, the first one must be a drive
  164. Return Value:
  165. None
  166. --*/
  167. {
  168. if (argc != 1) {
  169. DisplayMsg( MSG_USAGE_QUOTA_TRACK );
  170. if (argc != 0) {
  171. return EXIT_CODE_FAILURE;
  172. } else {
  173. return EXIT_CODE_SUCCESS;
  174. }
  175. }
  176. return QuotaControl( argv[0], FILE_VC_QUOTA_TRACK );
  177. }
  178. INT
  179. EnforceQuota(
  180. IN INT argc,
  181. IN PWSTR argv[]
  182. )
  183. /*++
  184. Routine Description:
  185. Enforces quotas on a specified volume
  186. Arguments:
  187. argc - The argument count, should be 1
  188. argv - Array of strings, the first one must be a drive
  189. Return Value:
  190. None
  191. --*/
  192. {
  193. if (argc != 1) {
  194. DisplayMsg( MSG_USAGE_QUOTA_ENFORCE );
  195. if (argc != 0) {
  196. return EXIT_CODE_FAILURE;
  197. } else {
  198. return EXIT_CODE_SUCCESS;
  199. }
  200. }
  201. return QuotaControl( argv[0], FILE_VC_QUOTA_ENFORCE );
  202. }
  203. INT
  204. GetDiskQuotaViolation(
  205. IN INT argc,
  206. IN PWSTR argv[]
  207. )
  208. /*++
  209. Routine Description:
  210. This is the routine for querying the disk quota violation.
  211. Arguments:
  212. argc - The argument count.
  213. argv - Array of strings of the form:
  214. ' fscutl getdskqv '
  215. Return Value:
  216. None
  217. --*/
  218. {
  219. HANDLE handle = NULL;
  220. EVENTLOGRECORD *pevlr, *ptr;
  221. PWCHAR EventString;
  222. DWORD dwRead;
  223. DWORD dwNeeded;
  224. BOOLEAN flag = TRUE;
  225. BOOL Status;
  226. PSID psid;
  227. SID_NAME_USE Use;
  228. WCHAR Name[MAX_PATH];
  229. DWORD dwNameSize;
  230. WCHAR Domain[MAX_PATH];
  231. DWORD dwDomainSize;
  232. WCHAR Log[2][16] = { L"System", L"Application" };
  233. WORD index;
  234. INT ExitCode = EXIT_CODE_SUCCESS;
  235. try {
  236. if (argc != 0) {
  237. DisplayMsg( MSG_USAGE_QUOTA_VIOLATIONS );
  238. if (argc != 0) {
  239. ExitCode = EXIT_CODE_FAILURE;
  240. }
  241. leave;
  242. }
  243. for ( index = 0 ; index < 2 ; index++ ) {
  244. handle = OpenEventLog( NULL, Log[index] );
  245. if ( handle == NULL ) {
  246. DisplayMsg( MSG_COULD_NOT_OPEN_EVENTLOG );
  247. DisplayError();
  248. ExitCode = EXIT_CODE_FAILURE;
  249. leave;
  250. }
  251. ptr = pevlr = (EVENTLOGRECORD *) malloc (BUFFER_SIZE);
  252. if (ptr == NULL) {
  253. DisplayErrorMsg( ERROR_NOT_ENOUGH_MEMORY );
  254. ExitCode = EXIT_CODE_FAILURE;
  255. leave;
  256. }
  257. DisplayMsg( MSG_SEARCHING_EVENTLOG, Log[index] );
  258. while(ReadEventLog(
  259. handle,
  260. EVENTLOG_FORWARDS_READ | EVENTLOG_SEQUENTIAL_READ,
  261. 0,
  262. pevlr,
  263. BUFFER_SIZE,
  264. &dwRead,
  265. &dwNeeded))
  266. {
  267. while(dwRead > 0) {
  268. if (pevlr->EventID == IO_FILE_QUOTA_THRESHOLD || pevlr->EventID == IO_FILE_QUOTA_LIMIT) {
  269. flag = FALSE;
  270. switch(pevlr->EventID) {
  271. case IO_FILE_QUOTA_THRESHOLD :
  272. DisplayMsg( MSG_QUOTA_THREASHOLD );
  273. break;
  274. case IO_FILE_QUOTA_LIMIT:
  275. DisplayMsg( MSG_QUOTA_LIMIT );
  276. break;
  277. }
  278. DisplayMsg( MSG_EVENT_ID, pevlr->EventID );
  279. switch(pevlr->EventType) {
  280. case EVENTLOG_ERROR_TYPE:
  281. DisplayMsg( MSG_EVENT_TYPE_ERROR );
  282. break;
  283. case EVENTLOG_WARNING_TYPE:
  284. DisplayMsg( MSG_EVENT_TYPE_WARNING );
  285. break;
  286. case EVENTLOG_INFORMATION_TYPE:
  287. DisplayMsg( MSG_EVENT_TYPE_INFORMATION );
  288. break;
  289. case EVENTLOG_AUDIT_SUCCESS:
  290. DisplayMsg( MSG_EVENT_TYPE_AUDIT_SUCCESS );
  291. break;
  292. case EVENTLOG_AUDIT_FAILURE:
  293. DisplayMsg( MSG_EVENT_TYPE_AUDIT_FAILURE );
  294. break;
  295. }
  296. DisplayMsg( MSG_EVENT_CATEGORY, pevlr->EventCategory );
  297. DisplayMsg( MSG_EVENT_SOURCE, (LPWSTR)((LPBYTE) pevlr + sizeof(EVENTLOGRECORD)) );
  298. if (pevlr->UserSidOffset > 0) {
  299. psid = (PSID) ( (PBYTE)pevlr + pevlr->UserSidOffset );
  300. dwNameSize = MAX_PATH;
  301. dwDomainSize = MAX_PATH;
  302. Status = LookupAccountSid(
  303. NULL,
  304. psid,
  305. Name,
  306. &dwNameSize,
  307. Domain,
  308. &dwDomainSize,
  309. &Use
  310. );
  311. if (Status) {
  312. DisplayMsg( MSG_USERNAME, Domain, Name );
  313. }
  314. }
  315. if (pevlr->NumStrings == 2) {
  316. EventString = (PWCHAR) ((PBYTE)pevlr + pevlr->StringOffset);
  317. EventString += wcslen( EventString ) + 1;
  318. DisplayMsg( MSG_EVENT_DATA, EventString );
  319. }
  320. }
  321. dwRead -= pevlr->Length;
  322. pevlr = (EVENTLOGRECORD *)((PBYTE) pevlr + pevlr->Length);
  323. }
  324. pevlr = ptr;
  325. }
  326. CloseEventLog(handle);
  327. handle = NULL;
  328. }
  329. if (flag) {
  330. DisplayMsg( MSG_GOOD_QUOTA );
  331. }
  332. } finally {
  333. if (handle != NULL) {
  334. CloseHandle( handle );
  335. }
  336. }
  337. return ExitCode;
  338. }
  339. PWSTR
  340. FileTimeToString(
  341. PLARGE_INTEGER FileTime
  342. )
  343. {
  344. ULONG ElapsedSeconds;
  345. if (RtlTimeToSecondsSince1970( FileTime, &ElapsedSeconds )) {
  346. PWSTR TimeString = _wctime( (time_t*)&ElapsedSeconds );
  347. if (TimeString) {
  348. TimeString[wcslen(TimeString)-1] = 0;
  349. }
  350. return TimeString;
  351. }
  352. return L"";
  353. }
  354. BOOL
  355. GetUserSid(
  356. PWSTR UserName,
  357. PFILE_QUOTA_INFORMATION *QuotaInfoPtr,
  358. PFILE_GET_QUOTA_INFORMATION *SidListPtr
  359. )
  360. {
  361. WCHAR Domain[128];
  362. ULONG DomainLength;
  363. SID_NAME_USE SidNameUse;
  364. (*QuotaInfoPtr)->SidLength = SID_MAX_LENGTH;
  365. DomainLength = sizeof(Domain);
  366. if (!LookupAccountName(
  367. NULL,
  368. UserName,
  369. &(*QuotaInfoPtr)->Sid,
  370. &(*QuotaInfoPtr)->SidLength,
  371. Domain,
  372. &DomainLength,
  373. &SidNameUse))
  374. {
  375. DomainLength = GetLastError();
  376. return FALSE;
  377. }
  378. //
  379. // Initialize the values to something resonable.
  380. //
  381. (*QuotaInfoPtr)->QuotaThreshold.QuadPart = ~0;
  382. (*QuotaInfoPtr)->QuotaLimit.QuadPart = ~0;
  383. (*QuotaInfoPtr)->SidLength = RtlLengthSid( &(*QuotaInfoPtr)->Sid);
  384. (*QuotaInfoPtr)->NextEntryOffset =
  385. FIELD_OFFSET( FILE_QUOTA_INFORMATION, Sid ) +
  386. QuadAlign((*QuotaInfoPtr)->SidLength);
  387. memcpy( &(*SidListPtr)->Sid, &(*QuotaInfoPtr)->Sid, (*QuotaInfoPtr)->SidLength);
  388. (*SidListPtr)->SidLength = (*QuotaInfoPtr)->SidLength;
  389. (*SidListPtr)->NextEntryOffset =
  390. FIELD_OFFSET( FILE_GET_QUOTA_INFORMATION, Sid ) +
  391. QuadAlign((*SidListPtr)->SidLength);
  392. return TRUE;
  393. }
  394. BOOL
  395. DumpVolumeQuotaInfo(
  396. IN HANDLE hFile,
  397. IN BOOL DisplayAlways
  398. )
  399. {
  400. NTSTATUS Status;
  401. IO_STATUS_BLOCK IoStatus;
  402. FILE_FS_CONTROL_INFORMATION TempControlInfo = {0};
  403. ULONG StatusId;
  404. ULONG LoggingId;
  405. ULONG ValueId;
  406. Status = NtQueryVolumeInformationFile(
  407. hFile,
  408. &IoStatus,
  409. &TempControlInfo,
  410. sizeof(FILE_FS_CONTROL_INFORMATION),
  411. FileFsControlInformation
  412. );
  413. //
  414. // If quotas aren't enforced or tracked, then quotas must be disabled.
  415. //
  416. if ((TempControlInfo.FileSystemControlFlags & FILE_VC_QUOTA_MASK) == FILE_VC_QUOTA_NONE) {
  417. StatusId = MSG_QUOTA_STATUS_DISABLED;
  418. } else if ((TempControlInfo.FileSystemControlFlags & FILE_VC_QUOTA_MASK) == FILE_VC_QUOTA_TRACK) {
  419. StatusId = MSG_QUOTA_STATUS_TRACKING;
  420. } else {
  421. ASSERT( TempControlInfo.FileSystemControlFlags & FILE_VC_QUOTA_ENFORCE );
  422. StatusId = MSG_QUOTA_STATUS_ENFORCE;
  423. }
  424. if (!DisplayAlways) {
  425. return StatusId != MSG_QUOTA_STATUS_DISABLED;
  426. }
  427. switch (TempControlInfo.FileSystemControlFlags & (FILE_VC_LOG_QUOTA_LIMIT | FILE_VC_LOG_QUOTA_THRESHOLD)) {
  428. case FILE_VC_LOG_QUOTA_LIMIT:
  429. LoggingId = MSG_QUOTA_LOGGING_LIMITS;
  430. break;
  431. case FILE_VC_LOG_QUOTA_THRESHOLD:
  432. LoggingId = MSG_QUOTA_LOGGING_THRESH;
  433. break;
  434. case FILE_VC_LOG_QUOTA_LIMIT | FILE_VC_LOG_QUOTA_THRESHOLD:
  435. LoggingId = MSG_QUOTA_LOGGING_BOTH;
  436. break;
  437. case 0:
  438. LoggingId = MSG_QUOTA_LOGGING_EVENTS;
  439. break;
  440. }
  441. if (TempControlInfo.FileSystemControlFlags & FILE_VC_QUOTA_MASK) {
  442. if (TempControlInfo.FileSystemControlFlags & FILE_VC_QUOTAS_INCOMPLETE) {
  443. ValueId = MSG_QUOTA_VALUES_INCOMPLETE;
  444. } else {
  445. ValueId = MSG_QUOTA_VALUES_GOOD;
  446. }
  447. } else {
  448. ValueId = MSG_QUOTA_VALUES_GOOD;
  449. }
  450. DisplayMsg( MSG_USAGE_QUOTA_VOLUME_INFO, TempControlInfo.FileSystemControlFlags );
  451. DisplayMsg( StatusId );
  452. DisplayMsg( LoggingId );
  453. DisplayMsg( ValueId );
  454. DisplayMsg( MSG_USAGE_QUOTA_LIMITS,
  455. QuadToPaddedHexText( TempControlInfo.DefaultQuotaThreshold.QuadPart ),
  456. QuadToPaddedHexText( TempControlInfo.DefaultQuotaLimit.QuadPart )
  457. );
  458. return TRUE;
  459. }
  460. VOID
  461. DumpQuota (
  462. IN PFILE_QUOTA_INFORMATION FileQuotaInfo,
  463. IN PWSTR ServerName
  464. )
  465. {
  466. NTSTATUS Status;
  467. SID_NAME_USE SidNameUse;
  468. ULONG AccountLength, DomainLength;
  469. WCHAR AccountName[128];
  470. WCHAR DomainName[128];
  471. AccountLength = sizeof(AccountName) - 1;
  472. DomainLength = sizeof(DomainName) - 1;
  473. if (FileQuotaInfo->SidLength == 0) {
  474. DisplayMsg( MSG_QUOTA_DUMP_DEFAULT );
  475. } else if (LookupAccountSid(
  476. ServerName,
  477. &FileQuotaInfo->Sid,
  478. AccountName,
  479. &AccountLength,
  480. DomainName,
  481. &DomainLength,
  482. &SidNameUse))
  483. {
  484. AccountName[AccountLength] = 0;
  485. DomainName[DomainLength] = 0;
  486. switch (SidNameUse) {
  487. case SidTypeUser:
  488. DisplayMsg( MSG_QUOTA_SID_USER, DomainName, AccountName );
  489. break;
  490. case SidTypeGroup:
  491. DisplayMsg( MSG_QUOTA_SID_GROUP, DomainName, AccountName );
  492. break;
  493. case SidTypeDomain:
  494. DisplayMsg( MSG_QUOTA_SID_DOMAIN, DomainName, AccountName );
  495. break;
  496. case SidTypeAlias:
  497. DisplayMsg( MSG_QUOTA_SID_ALIAS, DomainName, AccountName );
  498. break;
  499. case SidTypeWellKnownGroup:
  500. DisplayMsg( MSG_QUOTA_SID_WELLKNOWNGROUP, DomainName, AccountName );
  501. break;
  502. case SidTypeDeletedAccount:
  503. DisplayMsg( MSG_QUOTA_SID_DELETEDACCOUNT, DomainName, AccountName );
  504. break;
  505. case SidTypeInvalid:
  506. DisplayMsg( MSG_QUOTA_SID_INVALID, DomainName, AccountName );
  507. break;
  508. case SidTypeUnknown:
  509. default:
  510. DisplayMsg( MSG_QUOTA_SID_USER, DomainName, AccountName );
  511. break;
  512. }
  513. } else {
  514. DisplayError( );
  515. }
  516. DisplayMsg(
  517. MSG_QUOTA_DUMP_INFO,
  518. FileTimeToString(&FileQuotaInfo->ChangeTime),
  519. QuadToDecimalText( FileQuotaInfo->QuotaUsed.QuadPart ),
  520. QuadToDecimalText( FileQuotaInfo->QuotaThreshold.QuadPart ),
  521. QuadToDecimalText( FileQuotaInfo->QuotaLimit.QuadPart )
  522. );
  523. }
  524. INT
  525. QueryDiskQuota(
  526. IN INT argc,
  527. IN PWSTR argv[]
  528. )
  529. {
  530. NTSTATUS Status;
  531. HANDLE hFile;
  532. IO_STATUS_BLOCK IoStatus;
  533. PFILE_QUOTA_INFORMATION QuotaInfo;
  534. INT ExitCode = EXIT_CODE_SUCCESS;
  535. if (argc != 1) {
  536. DisplayMsg( MSG_USAGE_QUOTA_QUERY, argv[1] );
  537. if (argc != 0) {
  538. return EXIT_CODE_FAILURE;
  539. } else {
  540. return EXIT_CODE_SUCCESS;
  541. }
  542. }
  543. if (!IsVolumeLocalNTFS( argv[0][0] )) {
  544. DisplayMsg( MSG_NTFS_REQUIRED );
  545. return EXIT_CODE_FAILURE;
  546. }
  547. hFile = QuotaOpenVolume( argv[0] );
  548. if (hFile == INVALID_HANDLE_VALUE) {
  549. return EXIT_CODE_FAILURE;
  550. }
  551. if (!DumpVolumeQuotaInfo( hFile, FALSE )) {
  552. DisplayMsg( MSG_USAGE_QUOTA_REQUIRED, argv[0] );
  553. CloseHandle( hFile );
  554. return EXIT_CODE_FAILURE;
  555. }
  556. DumpVolumeQuotaInfo( hFile, TRUE );
  557. QuotaInfo = malloc( sizeof( FILE_QUOTA_INFORMATION ) + SID_MAX_LENGTH );
  558. if (QuotaInfo == NULL) {
  559. DisplayErrorMsg( ERROR_NOT_ENOUGH_MEMORY );
  560. CloseHandle( hFile );
  561. return EXIT_CODE_FAILURE;
  562. }
  563. while (TRUE) {
  564. Status = NtQueryQuotaInformationFile(
  565. hFile, // File Handle
  566. &IoStatus, // IO status block
  567. QuotaInfo, // Buffer
  568. sizeof( FILE_QUOTA_INFORMATION ) + SID_MAX_LENGTH, // Length
  569. TRUE, // Return Single Entry
  570. NULL, // SidList
  571. 0, // SidListLength
  572. NULL, // StartSid
  573. FALSE // RestartScan
  574. );
  575. if (!NT_SUCCESS( Status )) {
  576. if (Status != STATUS_NO_MORE_ENTRIES) {
  577. DisplayErrorMsg( RtlNtStatusToDosError( Status ));
  578. ExitCode = EXIT_CODE_FAILURE;
  579. }
  580. break;
  581. }
  582. DumpQuota( QuotaInfo, NULL );
  583. }
  584. CloseHandle( hFile );
  585. free( QuotaInfo );
  586. return ExitCode;
  587. }
  588. INT
  589. ChangeDiskQuota(
  590. IN INT argc,
  591. IN PWSTR argv[]
  592. )
  593. {
  594. NTSTATUS Status;
  595. HANDLE hFile = INVALID_HANDLE_VALUE;
  596. IO_STATUS_BLOCK IoStatus;
  597. PFILE_QUOTA_INFORMATION QuotaInfo;
  598. PFILE_GET_QUOTA_INFORMATION SidList;
  599. PFILE_QUOTA_INFORMATION QuotaInfoPtr;
  600. PFILE_GET_QUOTA_INFORMATION SidListPtr;
  601. ULONG BufferSize;
  602. ULONG SidListLength;
  603. PWSTR EndPtr;
  604. INT retval = EXIT_CODE_SUCCESS;
  605. if (argc != 4) {
  606. DisplayMsg( MSG_USAGE_QUOTA_MODIFY );
  607. if (argc != 0) {
  608. return EXIT_CODE_FAILURE;
  609. } else {
  610. return EXIT_CODE_SUCCESS;
  611. }
  612. }
  613. if (!IsVolumeLocalNTFS( argv[0][0] )) {
  614. DisplayMsg( MSG_NTFS_REQUIRED );
  615. return EXIT_CODE_FAILURE;
  616. }
  617. QuotaInfo = malloc( sizeof( FILE_QUOTA_INFORMATION ) + SID_MAX_LENGTH );
  618. SidList = malloc( sizeof( FILE_GET_QUOTA_INFORMATION ) + SID_MAX_LENGTH );
  619. try {
  620. if (QuotaInfo == NULL || SidList == NULL) {
  621. DisplayErrorMsg( ERROR_NOT_ENOUGH_MEMORY );
  622. retval = EXIT_CODE_FAILURE;
  623. leave;
  624. }
  625. RtlZeroMemory(QuotaInfo, sizeof( FILE_QUOTA_INFORMATION ) + SID_MAX_LENGTH);
  626. QuotaInfoPtr = QuotaInfo;
  627. RtlZeroMemory(SidList, sizeof( FILE_GET_QUOTA_INFORMATION ) + SID_MAX_LENGTH);
  628. SidListPtr = SidList;
  629. if (!GetUserSid( argv[3], &QuotaInfoPtr, &SidListPtr )) {
  630. DisplayError();
  631. retval = EXIT_CODE_FAILURE;
  632. leave;
  633. }
  634. QuotaInfoPtr->QuotaThreshold.QuadPart = My_wcstoui64( argv[1], &EndPtr, 0 );
  635. if (UnsignedI64NumberCheck( QuotaInfoPtr->QuotaThreshold.QuadPart, EndPtr )) {
  636. DisplayMsg( MSG_USAGE_QUOTA_MODIFY );
  637. retval = EXIT_CODE_FAILURE;
  638. leave;
  639. }
  640. QuotaInfoPtr->QuotaLimit.QuadPart = My_wcstoui64( argv[2], &EndPtr, 0 );
  641. if (UnsignedI64NumberCheck( QuotaInfoPtr->QuotaLimit.QuadPart, EndPtr )) {
  642. DisplayMsg( MSG_USAGE_QUOTA_MODIFY );
  643. retval = EXIT_CODE_FAILURE;
  644. leave;
  645. }
  646. //
  647. // Build the complete quota info data structure
  648. //
  649. QuotaInfoPtr->NextEntryOffset = 0;
  650. // QuotaInfoPtr->SidLength set in GetUserSid
  651. // QuotaInfoPtr->ChangeTime is ignored
  652. // QuotaInfoPtr->QuotaUsed is ignored
  653. // QuotaInfoPtr->QuotaThreshold is set above
  654. // QuotaInfoPtr->QuotaLimit is set above
  655. // QuotaInfoPtr->Sid is set in GetUserSid
  656. //
  657. // Terminate the list.
  658. //
  659. BufferSize = FIELD_OFFSET( FILE_QUOTA_INFORMATION, Sid ) + QuotaInfoPtr->SidLength;
  660. //
  661. // Change it
  662. //
  663. hFile = QuotaOpenVolume( argv[0] );
  664. if (hFile == INVALID_HANDLE_VALUE) {
  665. DisplayError();
  666. retval = EXIT_CODE_FAILURE;
  667. leave;
  668. }
  669. Status = NtSetQuotaInformationFile(
  670. hFile,
  671. &IoStatus,
  672. QuotaInfo,
  673. BufferSize
  674. );
  675. if (!NT_SUCCESS( Status )) {
  676. DisplayErrorMsg( RtlNtStatusToDosError( Status ));
  677. retval = EXIT_CODE_FAILURE;
  678. leave;
  679. }
  680. retval = EXIT_CODE_SUCCESS;
  681. } finally {
  682. if (hFile != INVALID_HANDLE_VALUE) {
  683. CloseHandle( hFile );
  684. }
  685. free( QuotaInfo );
  686. free( SidList );
  687. }
  688. return retval;
  689. }