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.

1401 lines
38 KiB

  1. /*++
  2. Copyright (c) 1998 Microsoft Corporation
  3. Module Name:
  4. cmds1.c
  5. Abstract:
  6. This module implements miscellaneous commands.
  7. Author:
  8. Wesley Witt (wesw) 21-Oct-1998
  9. Revision History:
  10. --*/
  11. #include "cmdcons.h"
  12. #pragma hdrstop
  13. BOOLEAN AllowWildCards;
  14. NTSTATUS
  15. RcSetFileAttributes(
  16. LPCWSTR lpFileName,
  17. DWORD dwFileAttributes
  18. );
  19. NTSTATUS
  20. RcSetFileCompression(
  21. LPCWSTR szFileName,
  22. BOOLEAN bCompress
  23. );
  24. NTSTATUS
  25. RcGetFileAttributes(
  26. LPCWSTR lpFileName,
  27. PULONG FileAttributes
  28. );
  29. BOOLEAN
  30. pRcCmdEnumDelFiles(
  31. IN LPCWSTR Directory,
  32. IN PFILE_BOTH_DIR_INFORMATION FileInfo,
  33. OUT NTSTATUS *Status,
  34. IN PWCHAR DosDirectorySpec
  35. );
  36. ULONG
  37. RcCmdType(
  38. IN PTOKENIZED_LINE TokenizedLine
  39. )
  40. {
  41. LPCWSTR Arg;
  42. HANDLE FileHandle;
  43. HANDLE SectionHandle;
  44. PVOID ViewBase;
  45. ULONG FileSize;
  46. ULONG rc;
  47. ULONG cbText;
  48. WCHAR *pText;
  49. NTSTATUS Status;
  50. if (RcCmdParseHelp( TokenizedLine, MSG_TYPE_HELP )) {
  51. return 1;
  52. }
  53. //
  54. // There should be a token for TYPE and one for the arg.
  55. //
  56. ASSERT(TokenizedLine->TokenCount == 2);
  57. //
  58. // Get the argument and convert it into a full NT pathname.
  59. //
  60. Arg = TokenizedLine->Tokens->Next->String;
  61. if (!RcFormFullPath(Arg,_CmdConsBlock->TemporaryBuffer,FALSE)) {
  62. RcMessageOut(MSG_INVALID_PATH);
  63. return 1;
  64. }
  65. if (!RcIsPathNameAllowed(_CmdConsBlock->TemporaryBuffer,TRUE,FALSE)) {
  66. RcMessageOut(MSG_ACCESS_DENIED);
  67. return 1;
  68. }
  69. //
  70. // Get the argument and convert it into a full NT pathname.
  71. //
  72. if (!RcFormFullPath(Arg,_CmdConsBlock->TemporaryBuffer,TRUE)) {
  73. RcMessageOut(MSG_INVALID_PATH);
  74. return 1;
  75. }
  76. //
  77. // Map in the entire file.
  78. //
  79. FileHandle = NULL;
  80. Status = SpOpenAndMapFile(
  81. _CmdConsBlock->TemporaryBuffer,
  82. &FileHandle,
  83. &SectionHandle,
  84. &ViewBase,
  85. &FileSize,
  86. FALSE
  87. );
  88. if( !NT_SUCCESS(Status) ) {
  89. RcNtError(Status,MSG_CANT_OPEN_FILE);
  90. return 1;
  91. }
  92. //
  93. // See if we think the file is Unicode. We think it's Unicode
  94. // if it's even length and starts with the Unicode text marker.
  95. //
  96. pText = ViewBase;
  97. cbText = FileSize;
  98. try {
  99. if( (cbText >= sizeof(WCHAR)) && (*pText == 0xfeff) && !(cbText & 1) ) {
  100. //
  101. // Assume it's already unicode.
  102. //
  103. pText = SpMemAlloc(cbText);
  104. RtlCopyMemory(pText,(WCHAR *)ViewBase+1,cbText-sizeof(WCHAR));
  105. pText[cbText/sizeof(WCHAR)] = 0;
  106. } else {
  107. //
  108. // It's not Unicode. Convert it from ANSI to Unicode.
  109. //
  110. // Allocate a buffer large enough to hold the maximum
  111. // unicode text. This max size occurs when
  112. // every character is single-byte, and this size is
  113. // equal to exactly double the size of the single-byte text.
  114. //
  115. pText = SpMemAlloc((cbText+1)*sizeof(WCHAR));
  116. RtlZeroMemory(pText,(cbText+1)*sizeof(WCHAR));
  117. Status = RtlMultiByteToUnicodeN(
  118. pText, // output: newly allocated buffer
  119. cbText * sizeof(WCHAR), // max size of output
  120. &cbText, // receives # bytes in unicode text
  121. ViewBase, // input: ANSI text (mapped file)
  122. cbText // size of input
  123. );
  124. }
  125. }except(IN_PAGE_ERROR) {
  126. Status = STATUS_IN_PAGE_ERROR;
  127. }
  128. if( NT_SUCCESS(Status) ) {
  129. pRcEnableMoreMode();
  130. RcTextOut(pText);
  131. pRcDisableMoreMode();
  132. } else {
  133. RcNtError(Status,MSG_CANT_READ_FILE);
  134. }
  135. if( pText != ViewBase ) {
  136. SpMemFree(pText);
  137. }
  138. SpUnmapFile(SectionHandle,ViewBase);
  139. ZwClose(FileHandle);
  140. return 1;
  141. }
  142. ULONG
  143. RcCmdDelete(
  144. IN PTOKENIZED_LINE TokenizedLine
  145. )
  146. {
  147. WCHAR *Final;
  148. BOOLEAN Confirm = FALSE;
  149. OBJECT_ATTRIBUTES Obja;
  150. UNICODE_STRING UnicodeString;
  151. NTSTATUS Status;
  152. IO_STATUS_BLOCK IoStatusBlock;
  153. HANDLE Handle;
  154. PWSTR DelSpec = NULL;
  155. PWSTR DosDelSpec = NULL;
  156. WCHAR Text[2];
  157. PWSTR YesNo = NULL;
  158. ULONG rc;
  159. if (RcCmdParseHelp( TokenizedLine, MSG_DELETE_HELP )) {
  160. goto exit;
  161. }
  162. //
  163. // Fetch the spec for the file to be deleted and convert it
  164. // into a fully-qualified NT-style path.
  165. //
  166. if (!RcFormFullPath(TokenizedLine->Tokens->Next->String,_CmdConsBlock->TemporaryBuffer,TRUE)) {
  167. RcMessageOut(MSG_INVALID_PATH);
  168. goto exit;
  169. }
  170. //
  171. // Leave room for appending * if necessary.
  172. //
  173. DelSpec = SpMemAlloc((wcslen(_CmdConsBlock->TemporaryBuffer)+3)*sizeof(WCHAR));
  174. wcscpy(DelSpec,_CmdConsBlock->TemporaryBuffer);
  175. //
  176. // Do the same thing, except now we want the DOS-style name.
  177. // This is used for printing in case of errors.
  178. //
  179. if (!RcFormFullPath(TokenizedLine->Tokens->Next->String,_CmdConsBlock->TemporaryBuffer,FALSE)) {
  180. RcMessageOut(MSG_INVALID_PATH);
  181. goto exit;
  182. }
  183. DosDelSpec = SpMemAlloc((wcslen(_CmdConsBlock->TemporaryBuffer)+3)*sizeof(WCHAR));
  184. wcscpy(DosDelSpec,_CmdConsBlock->TemporaryBuffer);
  185. //
  186. // see if the user is authorized to delete this file
  187. //
  188. if (!RcIsPathNameAllowed(_CmdConsBlock->TemporaryBuffer,TRUE,FALSE)) {
  189. RcMessageOut(MSG_ACCESS_DENIED);
  190. goto exit;
  191. }
  192. if (RcDoesPathHaveWildCards(_CmdConsBlock->TemporaryBuffer)) {
  193. Confirm = TRUE;
  194. if (!AllowWildCards) {
  195. RcMessageOut(MSG_DEL_WILDCARD_NOT_SUPPORTED);
  196. goto exit;
  197. }
  198. }
  199. //
  200. // Check to see whether the target specifies a directory.
  201. // If so, add the * so we don't need to special-case
  202. // the confirmation message later.
  203. //
  204. INIT_OBJA(&Obja,&UnicodeString,DelSpec);
  205. Status = ZwOpenFile(
  206. &Handle,
  207. FILE_READ_ATTRIBUTES,
  208. &Obja,
  209. &IoStatusBlock,
  210. FILE_SHARE_READ | FILE_SHARE_WRITE,
  211. FILE_DIRECTORY_FILE
  212. );
  213. if( NT_SUCCESS(Status) ) {
  214. ZwClose(Handle);
  215. SpConcatenatePaths(DelSpec,L"*");
  216. SpConcatenatePaths(DosDelSpec,L"*");
  217. Confirm = TRUE;
  218. }
  219. //
  220. // Fetch yes/no text
  221. //
  222. YesNo = SpRetreiveMessageText(ImageBase,MSG_YESNO,NULL,0);
  223. if (!YesNo) {
  224. Confirm = FALSE;
  225. }
  226. if (!InBatchMode) {
  227. while( Confirm ) {
  228. RcMessageOut(MSG_CONFIRM_DELETE,DosDelSpec);
  229. if( RcLineIn(Text,2) ) {
  230. if( (Text[0] == YesNo[0]) || (Text[0] == YesNo[1]) ) {
  231. //
  232. // Wants to do it.
  233. //
  234. Confirm = FALSE;
  235. } else {
  236. if( (Text[0] == YesNo[2]) || (Text[0] == YesNo[3]) ) {
  237. //
  238. // Doesn't want to do it.
  239. //
  240. goto exit;
  241. }
  242. }
  243. }
  244. }
  245. }
  246. //
  247. // Trim back the DOS-style path so it's a path to the directory
  248. // containing the file or files to be deleted.
  249. //
  250. *wcsrchr(DosDelSpec,L'\\') = 0;
  251. // Perform deletion via callback.
  252. //
  253. Status = RcEnumerateFiles(TokenizedLine->Tokens->Next->String,
  254. DelSpec,
  255. pRcCmdEnumDelFiles,
  256. DosDelSpec);
  257. if( !NT_SUCCESS(Status) ) {
  258. RcNtError(Status,MSG_FILE_ENUM_ERROR);
  259. }
  260. exit:
  261. if (DelSpec) {
  262. SpMemFree(DelSpec);
  263. }
  264. if (DosDelSpec) {
  265. SpMemFree(DosDelSpec);
  266. }
  267. if (YesNo) {
  268. SpMemFree(YesNo);
  269. }
  270. return 1;
  271. }
  272. BOOLEAN
  273. pRcCmdEnumDelFiles(
  274. IN LPCWSTR Directory,
  275. IN PFILE_BOTH_DIR_INFORMATION FileInfo,
  276. OUT NTSTATUS *Status,
  277. IN PWCHAR DosDirectorySpec
  278. )
  279. {
  280. NTSTATUS status;
  281. HANDLE Handle;
  282. IO_STATUS_BLOCK IoStatusBlock;
  283. UNICODE_STRING UnicodeString;
  284. OBJECT_ATTRIBUTES Obja;
  285. WCHAR *p;
  286. FILE_DISPOSITION_INFORMATION Disposition;
  287. unsigned u;
  288. *Status = STATUS_SUCCESS;
  289. //
  290. // Skip directories
  291. //
  292. if( FileInfo->FileAttributes & FILE_ATTRIBUTE_DIRECTORY ) {
  293. return(TRUE);
  294. }
  295. //
  296. // Form fully qualified NT path of the file to be deleted.
  297. //
  298. u = ((wcslen(Directory)+2)*sizeof(WCHAR)) + FileInfo->FileNameLength;
  299. p = SpMemAlloc(u);
  300. wcscpy(p,Directory);
  301. SpConcatenatePaths(p,FileInfo->FileName);
  302. INIT_OBJA(&Obja,&UnicodeString,p);
  303. status = ZwOpenFile(
  304. &Handle,
  305. (ACCESS_MASK)DELETE,
  306. &Obja,
  307. &IoStatusBlock,
  308. FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
  309. FILE_NON_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT
  310. );
  311. if( !NT_SUCCESS(status) ) {
  312. RcTextOut(DosDirectorySpec);
  313. RcTextOut(L"\\");
  314. RcTextOut(FileInfo->FileName);
  315. RcTextOut(L"\r\n");
  316. RcNtError(status,MSG_DELETE_ERROR);
  317. SpMemFree(p);
  318. return(TRUE);
  319. }
  320. #undef DeleteFile
  321. Disposition.DeleteFile = TRUE;
  322. status = ZwSetInformationFile(
  323. Handle,
  324. &IoStatusBlock,
  325. &Disposition,
  326. sizeof(FILE_DISPOSITION_INFORMATION),
  327. FileDispositionInformation
  328. );
  329. ZwClose(Handle);
  330. if( !NT_SUCCESS(status) ) {
  331. RcTextOut(DosDirectorySpec);
  332. RcTextOut(L"\\");
  333. RcTextOut(FileInfo->FileName);
  334. RcTextOut(L"\r\n");
  335. RcNtError(status,MSG_DELETE_ERROR);
  336. }
  337. SpMemFree(p);
  338. return(TRUE);
  339. }
  340. ULONG
  341. RcCmdRename(
  342. IN PTOKENIZED_LINE TokenizedLine
  343. )
  344. {
  345. WCHAR *Arg;
  346. WCHAR *p,*q;
  347. NTSTATUS Status;
  348. ULONG rc;
  349. //
  350. // check for help
  351. //
  352. if (RcCmdParseHelp( TokenizedLine, MSG_RENAME_HELP )) {
  353. return 1;
  354. }
  355. //
  356. // There should be a token for RENAME and one each for the source and
  357. // target names.
  358. //
  359. if (TokenizedLine->TokenCount != 3) {
  360. RcMessageOut(MSG_SYNTAX_ERROR);
  361. return 1;
  362. }
  363. //
  364. // use the console's temporary buffer
  365. //
  366. p = _CmdConsBlock->TemporaryBuffer;
  367. //
  368. // process the SOURCE filename
  369. //
  370. Arg = TokenizedLine->Tokens->Next->String;
  371. //
  372. // Convert the SOURCE filname into a DOS path so we
  373. // can verify if the path is allowed by our security restrictions.
  374. //
  375. if (!RcFormFullPath(Arg,p,FALSE)) {
  376. RcMessageOut(MSG_INVALID_PATH);
  377. return 1;
  378. }
  379. if (!RcIsPathNameAllowed(p,TRUE,FALSE)) {
  380. RcMessageOut(MSG_ACCESS_DENIED);
  381. return 1;
  382. }
  383. //
  384. // Convert the SOURCE filename into a fully qualified
  385. // NT-style path name.
  386. //
  387. if (!RcFormFullPath(Arg,p,TRUE)) {
  388. RcMessageOut(MSG_INVALID_PATH);
  389. return 1;
  390. }
  391. //
  392. // using the same buffer for the TARGET name
  393. //
  394. q = p + wcslen(p) + 1;
  395. //
  396. // get the TARGET file name
  397. //
  398. Arg = TokenizedLine->Tokens->Next->Next->String;
  399. //
  400. // Verify that the TARGET filename does not contain
  401. // any path seperator characters or drive specifier
  402. // characters.
  403. //
  404. if( wcschr(Arg,L'\\') ) {
  405. RcMessageOut(MSG_SYNTAX_ERROR);
  406. return 1;
  407. }
  408. if( RcIsAlpha(Arg[0]) && (Arg[1] == L':') ) {
  409. RcMessageOut(MSG_SYNTAX_ERROR);
  410. return 1;
  411. }
  412. //
  413. // Convert the DESTINATION filename into a DOS path so we
  414. // can verify if the path is allowed by our security restrictions.
  415. //
  416. if (!RcFormFullPath(Arg,q,FALSE)) {
  417. RcMessageOut(MSG_INVALID_PATH);
  418. return 1;
  419. }
  420. if (!RcIsPathNameAllowed(q,TRUE,FALSE)) {
  421. RcMessageOut(MSG_ACCESS_DENIED);
  422. return 1;
  423. }
  424. //
  425. // Convert the SOURCE filename into a fully qualified
  426. // NT-style path name.
  427. //
  428. if (!RcFormFullPath(Arg,q,TRUE)) {
  429. RcMessageOut(MSG_INVALID_PATH);
  430. return 1;
  431. }
  432. //
  433. // OK, looks like a plain filename specification.
  434. // Glom it onto the end of the relevent part of the
  435. // source specification so we have 2 fully qualified names.
  436. //
  437. // wcscpy(q,p);
  438. // wcscpy(wcsrchr(q,L'\\')+1,Arg);
  439. //
  440. // Call worker routine to actually do the rename.
  441. //
  442. Status = SpRenameFile(p,q,TRUE);
  443. if( !NT_SUCCESS(Status) ) {
  444. RcNtError(Status,MSG_RENAME_ERROR, Arg);
  445. }
  446. return 1;
  447. }
  448. ULONG
  449. RcCmdMkdir(
  450. IN PTOKENIZED_LINE TokenizedLine
  451. )
  452. {
  453. NTSTATUS Status;
  454. UNICODE_STRING UnicodeString;
  455. IO_STATUS_BLOCK IoStatusBlock;
  456. HANDLE Handle;
  457. OBJECT_ATTRIBUTES Obja;
  458. ULONG rc;
  459. if (RcCmdParseHelp( TokenizedLine, MSG_MAKEDIR_HELP )) {
  460. return 1;
  461. }
  462. //
  463. // There should be a token for MKDIR and one for the target.
  464. //
  465. ASSERT(TokenizedLine->TokenCount == 2);
  466. //
  467. // Convert the given arg into a fully qualified NT path specification.
  468. //
  469. if (!RcFormFullPath(TokenizedLine->Tokens->Next->String,_CmdConsBlock->TemporaryBuffer,FALSE)) {
  470. RcMessageOut(MSG_INVALID_PATH);
  471. return 1;
  472. }
  473. if (!RcIsPathNameAllowed(_CmdConsBlock->TemporaryBuffer,TRUE,TRUE)) {
  474. RcMessageOut(MSG_ACCESS_DENIED);
  475. return 1;
  476. }
  477. //
  478. // Convert the given arg into a fully qualified NT path specification.
  479. //
  480. if (!RcFormFullPath(TokenizedLine->Tokens->Next->String,_CmdConsBlock->TemporaryBuffer,TRUE)) {
  481. RcMessageOut(MSG_INVALID_PATH);
  482. return 1;
  483. }
  484. //
  485. // Create the directory.
  486. //
  487. INIT_OBJA(&Obja,&UnicodeString,_CmdConsBlock->TemporaryBuffer);
  488. Status = ZwCreateFile(
  489. &Handle,
  490. FILE_LIST_DIRECTORY | SYNCHRONIZE,
  491. &Obja,
  492. &IoStatusBlock,
  493. NULL,
  494. FILE_ATTRIBUTE_NORMAL,
  495. FILE_SHARE_READ | FILE_SHARE_WRITE,
  496. FILE_CREATE,
  497. FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT,
  498. NULL,
  499. 0
  500. );
  501. if( NT_SUCCESS(Status) ) {
  502. ZwClose(Handle);
  503. } else {
  504. RcNtError(Status,MSG_CREATE_DIR_FAILED,TokenizedLine->Tokens->Next->String);
  505. }
  506. return 1;
  507. }
  508. ULONG
  509. RcCmdRmdir(
  510. IN PTOKENIZED_LINE TokenizedLine
  511. )
  512. {
  513. NTSTATUS Status;
  514. HANDLE Handle;
  515. IO_STATUS_BLOCK IoStatusBlock;
  516. UNICODE_STRING UnicodeString;
  517. OBJECT_ATTRIBUTES Obja;
  518. FILE_DISPOSITION_INFORMATION Disposition;
  519. ULONG rc;
  520. if (RcCmdParseHelp( TokenizedLine, MSG_REMOVEDIR_HELP )) {
  521. return 1;
  522. }
  523. //
  524. // There should be a token for RMDIR and one for the target.
  525. //
  526. ASSERT(TokenizedLine->TokenCount == 2);
  527. //
  528. // Convert the given arg into a fully qualified NT path specification.
  529. //
  530. if (!RcFormFullPath(TokenizedLine->Tokens->Next->String,_CmdConsBlock->TemporaryBuffer,FALSE)) {
  531. RcMessageOut(MSG_INVALID_PATH);
  532. return 1;
  533. }
  534. if (!RcIsPathNameAllowed(_CmdConsBlock->TemporaryBuffer,TRUE,TRUE)) {
  535. RcMessageOut(MSG_ACCESS_DENIED);
  536. return 1;
  537. }
  538. //
  539. // Convert the given arg into a fully qualified NT path specification.
  540. //
  541. if (!RcFormFullPath(TokenizedLine->Tokens->Next->String,_CmdConsBlock->TemporaryBuffer,TRUE)) {
  542. RcMessageOut(MSG_INVALID_PATH);
  543. return 1;
  544. }
  545. INIT_OBJA(&Obja,&UnicodeString,_CmdConsBlock->TemporaryBuffer);
  546. Status = ZwOpenFile(
  547. &Handle,
  548. DELETE | SYNCHRONIZE,
  549. &Obja,
  550. &IoStatusBlock,
  551. FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
  552. FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT
  553. );
  554. if( !NT_SUCCESS(Status) ) {
  555. RcNtError(Status,MSG_RMDIR_ERROR);
  556. return 1;
  557. }
  558. Disposition.DeleteFile = TRUE;
  559. Status = ZwSetInformationFile(
  560. Handle,
  561. &IoStatusBlock,
  562. &Disposition,
  563. sizeof(FILE_DISPOSITION_INFORMATION),
  564. FileDispositionInformation
  565. );
  566. ZwClose(Handle);
  567. if( !NT_SUCCESS(Status) ) {
  568. RcNtError(Status,MSG_RMDIR_ERROR);
  569. }
  570. return 1;
  571. }
  572. ULONG
  573. RcCmdSetFlags(
  574. IN PTOKENIZED_LINE TokenizedLine
  575. )
  576. {
  577. if (RcCmdParseHelp( TokenizedLine, MSG_SETCMD_HELP )) {
  578. return 1;
  579. }
  580. if (TokenizedLine->TokenCount == 1) {
  581. RcTextOut( L"\r\n" );
  582. RcMessageOut(MSG_SET_ALLOW_WILDCARDS,AllowWildCards?L"TRUE":L"FALSE");
  583. RcMessageOut(MSG_SET_ALLOW_ALLPATHS,AllowAllPaths?L"TRUE":L"FALSE");
  584. RcMessageOut(MSG_SET_ALLOW_REMOVABLE_MEDIA,AllowRemovableMedia?L"TRUE":L"FALSE");
  585. RcMessageOut(MSG_SET_NO_COPY_PROMPT,NoCopyPrompt?L"TRUE":L"FALSE");
  586. RcTextOut( L"\r\n" );
  587. return 1;
  588. }
  589. //
  590. // should have the priviledge to use the SET command
  591. //
  592. if (TokenizedLine->TokenCount != 4) {
  593. RcMessageOut(MSG_SYNTAX_ERROR);
  594. return 1;
  595. }
  596. if (RcGetSETCommandStatus() != TRUE) {
  597. RcMessageOut(MSG_SETCMD_DISABLED);
  598. return 1;
  599. }
  600. if (_wcsicmp(TokenizedLine->Tokens->Next->String,L"allowallpaths")==0) {
  601. if (_wcsicmp(TokenizedLine->Tokens->Next->Next->Next->String,L"true")==0) {
  602. AllowAllPaths = TRUE;
  603. } else {
  604. AllowAllPaths = FALSE;
  605. }
  606. return 1;
  607. }
  608. if (_wcsicmp(TokenizedLine->Tokens->Next->String,L"allowwildcards")==0) {
  609. if (_wcsicmp(TokenizedLine->Tokens->Next->Next->Next->String,L"true")==0) {
  610. AllowWildCards = TRUE;
  611. } else {
  612. AllowWildCards = FALSE;
  613. }
  614. return 1;
  615. }
  616. if (_wcsicmp(TokenizedLine->Tokens->Next->String,L"allowremovablemedia")==0) {
  617. if (_wcsicmp(TokenizedLine->Tokens->Next->Next->Next->String,L"true")==0) {
  618. AllowRemovableMedia = TRUE;
  619. } else {
  620. AllowRemovableMedia = FALSE;
  621. }
  622. return 1;
  623. }
  624. if (_wcsicmp(TokenizedLine->Tokens->Next->String,L"nocopyprompt")==0) {
  625. if (_wcsicmp(TokenizedLine->Tokens->Next->Next->Next->String,L"true")==0) {
  626. NoCopyPrompt = TRUE;
  627. } else {
  628. NoCopyPrompt = FALSE;
  629. }
  630. return 1;
  631. }
  632. RcMessageOut(MSG_SYNTAX_ERROR);
  633. return 1;
  634. }
  635. ULONG
  636. RcCmdAttrib(
  637. IN PTOKENIZED_LINE TokenizedLine
  638. )
  639. {
  640. NTSTATUS Status;
  641. PWCHAR AttributeString;
  642. ULONG OldAttributes;
  643. ULONG NewAttributes;
  644. BOOLEAN SetAttribute;
  645. BOOLEAN bShowHelp = TRUE;
  646. BOOLEAN bChangeCompression = FALSE;
  647. // "attrib -h <filename>" should clear the hidden attribute
  648. // and not show the help
  649. if (TokenizedLine->TokenCount > 2){
  650. PWCHAR szSecondParam = TokenizedLine->Tokens->Next->String;
  651. bShowHelp = !wcscmp( szSecondParam, L"/?" );
  652. }
  653. if (bShowHelp && RcCmdParseHelp( TokenizedLine, MSG_ATTRIB_HELP )) {
  654. return 1;
  655. }
  656. if (TokenizedLine->TokenCount != 3) {
  657. RcMessageOut(MSG_SYNTAX_ERROR);
  658. return 1;
  659. }
  660. //
  661. // Fetch the spec for the file to be attribed and convert it
  662. // into a fully-qualified NT-style path.
  663. //
  664. if (!RcFormFullPath(TokenizedLine->Tokens->Next->Next->String,_CmdConsBlock->TemporaryBuffer,FALSE)) {
  665. RcMessageOut(MSG_INVALID_PATH);
  666. return 1;
  667. }
  668. //
  669. // see if the user is authorized to change this file
  670. //
  671. if (!RcIsPathNameAllowed(_CmdConsBlock->TemporaryBuffer,TRUE,FALSE)) {
  672. RcMessageOut(MSG_ACCESS_DENIED);
  673. return 1;
  674. }
  675. if (!RcFormFullPath(TokenizedLine->Tokens->Next->Next->String,_CmdConsBlock->TemporaryBuffer,TRUE)) {
  676. RcMessageOut(MSG_INVALID_PATH);
  677. return 1;
  678. }
  679. Status = RcGetFileAttributes( _CmdConsBlock->TemporaryBuffer, &OldAttributes );
  680. if( !NT_SUCCESS(Status) ) {
  681. RcNtError(Status,MSG_CANT_OPEN_FILE);
  682. return 1;
  683. }
  684. NewAttributes = OldAttributes;
  685. for(AttributeString = TokenizedLine->Tokens->Next->String; *AttributeString; AttributeString++){
  686. if(*AttributeString == L'+'){
  687. SetAttribute = TRUE;
  688. AttributeString++;
  689. } else if(*AttributeString == L'-'){
  690. SetAttribute = FALSE;
  691. AttributeString++;
  692. } else {
  693. // attribute change should start with "+" or "-"
  694. if (AttributeString == TokenizedLine->Tokens->Next->String) {
  695. RcMessageOut(MSG_SYNTAX_ERROR);
  696. return 1;
  697. }
  698. // use the old state for setting or resetting (for +rsh
  699. }
  700. switch(*AttributeString){
  701. case L'h':
  702. case L'H':
  703. if (SetAttribute)
  704. NewAttributes |= FILE_ATTRIBUTE_HIDDEN;
  705. else
  706. NewAttributes &= ~FILE_ATTRIBUTE_HIDDEN;
  707. break;
  708. case L's':
  709. case L'S':
  710. if (SetAttribute)
  711. NewAttributes |= FILE_ATTRIBUTE_SYSTEM;
  712. else
  713. NewAttributes &= ~FILE_ATTRIBUTE_SYSTEM;
  714. break;
  715. case L'r':
  716. case L'R':
  717. if (SetAttribute)
  718. NewAttributes |= FILE_ATTRIBUTE_READONLY;
  719. else
  720. NewAttributes &= ~FILE_ATTRIBUTE_READONLY;
  721. break;
  722. case L'a':
  723. case L'A':
  724. if (SetAttribute)
  725. NewAttributes |= FILE_ATTRIBUTE_ARCHIVE;
  726. else
  727. NewAttributes &= ~FILE_ATTRIBUTE_ARCHIVE;
  728. break;
  729. case L'c':
  730. case L'C':
  731. bChangeCompression = TRUE;
  732. if (SetAttribute)
  733. NewAttributes |= FILE_ATTRIBUTE_COMPRESSED;
  734. else
  735. NewAttributes &= ~FILE_ATTRIBUTE_COMPRESSED;
  736. break;
  737. default:
  738. RcMessageOut(MSG_SYNTAX_ERROR);
  739. return 1;
  740. }
  741. /*
  742. if (SetAttribute) {
  743. FileAttributes |= Attribute;
  744. } else {
  745. FileAttributes &= ~Attribute;
  746. }
  747. */
  748. }
  749. Status = RcSetFileAttributes( _CmdConsBlock->TemporaryBuffer, NewAttributes );
  750. if( !NT_SUCCESS(Status) ) {
  751. RcNtError(Status,MSG_CANT_OPEN_FILE);
  752. } else {
  753. if (bChangeCompression) {
  754. BOOLEAN bCompress = (NewAttributes & FILE_ATTRIBUTE_COMPRESSED) ?
  755. TRUE : FALSE;
  756. Status = RcSetFileCompression(_CmdConsBlock->TemporaryBuffer, bCompress);
  757. if ( !NT_SUCCESS(Status) )
  758. RcNtError(Status, MSG_ATTRIB_CANNOT_CHANGE_COMPRESSION);
  759. }
  760. }
  761. return 1;
  762. }
  763. NTSTATUS
  764. RcSetFileCompression(
  765. LPCWSTR szFileName,
  766. BOOLEAN bCompress
  767. )
  768. {
  769. NTSTATUS Status;
  770. OBJECT_ATTRIBUTES Obja;
  771. HANDLE Handle;
  772. IO_STATUS_BLOCK IoStatusBlock;
  773. FILE_BASIC_INFORMATION BasicInfo;
  774. UNICODE_STRING FileName;
  775. USHORT uCompressionType;
  776. INIT_OBJA(&Obja,&FileName,szFileName);
  777. //
  778. // Open the file inhibiting the reparse behavior.
  779. //
  780. Status = ZwOpenFile(
  781. &Handle,
  782. (ACCESS_MASK)FILE_WRITE_ATTRIBUTES | SYNCHRONIZE,
  783. &Obja,
  784. &IoStatusBlock,
  785. FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
  786. FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT
  787. );
  788. if (NT_SUCCESS(Status)) {
  789. //
  790. // set & reset the compression bit also
  791. //
  792. uCompressionType = bCompress ?
  793. COMPRESSION_FORMAT_DEFAULT : COMPRESSION_FORMAT_NONE;
  794. Status = ZwFsControlFile(
  795. Handle, // file handle
  796. NULL, // event handle
  797. NULL, // APC rountine pointer
  798. NULL, // APC context
  799. &IoStatusBlock, // IO status block
  800. FSCTL_SET_COMPRESSION, // IOCTL code
  801. &uCompressionType, // input buffer
  802. sizeof(uCompressionType), // input buffer length
  803. NULL, // output buffer pointer
  804. 0); // output buffer length
  805. DbgPrint( "ZwDeviceIoControlFile() status : %X\r\n", Status);
  806. ZwClose(Handle);
  807. }
  808. return Status;
  809. }
  810. NTSTATUS
  811. RcSetFileAttributes(
  812. LPCWSTR lpFileName,
  813. DWORD dwFileAttributes
  814. )
  815. /*++
  816. Routine Description:
  817. The attributes of a file can be set using SetFileAttributes.
  818. Arguments:
  819. lpFileName - Supplies the file name of the file whose attributes are to
  820. be set.
  821. dwFileAttributes - Specifies the file attributes to be set for the
  822. file. Any combination of flags is acceptable except that all
  823. other flags override the normal file attribute,
  824. FILE_ATTRIBUTE_NORMAL.
  825. FileAttributes Flags:
  826. FILE_ATTRIBUTE_NORMAL - A normal file should be created.
  827. FILE_ATTRIBUTE_READONLY - A read-only file should be created.
  828. FILE_ATTRIBUTE_HIDDEN - A hidden file should be created.
  829. FILE_ATTRIBUTE_SYSTEM - A system file should be created.
  830. FILE_ATTRIBUTE_ARCHIVE - The file should be marked so that it
  831. will be archived.
  832. Return Value:
  833. NTStatus of last NT call
  834. --*/
  835. {
  836. NTSTATUS Status;
  837. OBJECT_ATTRIBUTES Obja;
  838. HANDLE Handle;
  839. IO_STATUS_BLOCK IoStatusBlock;
  840. FILE_BASIC_INFORMATION BasicInfo;
  841. UNICODE_STRING FileName;
  842. USHORT uCompressionType;
  843. INIT_OBJA(&Obja,&FileName,lpFileName);
  844. //
  845. // Open the file ihibiting the reparse behavior.
  846. //
  847. Status = ZwOpenFile(
  848. &Handle,
  849. (ACCESS_MASK)FILE_WRITE_ATTRIBUTES | SYNCHRONIZE,
  850. &Obja,
  851. &IoStatusBlock,
  852. FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
  853. FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT | FILE_OPEN_REPARSE_POINT
  854. );
  855. if ( !NT_SUCCESS(Status) ) {
  856. //
  857. // Back level file systems may not support reparse points.
  858. // We infer this is the case when the Status is STATUS_INVALID_PARAMETER.
  859. //
  860. if ( Status == STATUS_INVALID_PARAMETER ) {
  861. //
  862. // Open the file without inhibiting the reparse behavior.
  863. //
  864. Status = ZwOpenFile(
  865. &Handle,
  866. (ACCESS_MASK)FILE_WRITE_ATTRIBUTES | SYNCHRONIZE,
  867. &Obja,
  868. &IoStatusBlock,
  869. FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
  870. FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT
  871. );
  872. if ( !NT_SUCCESS(Status) ) {
  873. return Status;
  874. }
  875. }
  876. else {
  877. return Status;
  878. }
  879. }
  880. //
  881. // Set the basic attributes
  882. //
  883. ZeroMemory(&BasicInfo,sizeof(BasicInfo));
  884. BasicInfo.FileAttributes = (dwFileAttributes & FILE_ATTRIBUTE_VALID_FLAGS) | FILE_ATTRIBUTE_NORMAL;
  885. Status = ZwSetInformationFile(
  886. Handle,
  887. &IoStatusBlock,
  888. &BasicInfo,
  889. sizeof(BasicInfo),
  890. FileBasicInformation
  891. );
  892. ZwClose(Handle);
  893. return Status;
  894. }
  895. NTSTATUS
  896. RcGetFileAttributes(
  897. LPCWSTR lpFileName,
  898. PULONG FileAttributes
  899. )
  900. /*++
  901. Routine Description:
  902. Arguments:
  903. lpFileName - Supplies the file name of the file whose attributes are to
  904. be set.
  905. Return Value:
  906. Not -1 - Returns the attributes of the specified file. Valid
  907. returned attributes are:
  908. FILE_ATTRIBUTE_NORMAL - The file is a normal file.
  909. FILE_ATTRIBUTE_READONLY - The file is marked read-only.
  910. FILE_ATTRIBUTE_HIDDEN - The file is marked as hidden.
  911. FILE_ATTRIBUTE_SYSTEM - The file is marked as a system file.
  912. FILE_ATTRIBUTE_ARCHIVE - The file is marked for archive.
  913. FILE_ATTRIBUTE_DIRECTORY - The file is marked as a directory.
  914. FILE_ATTRIBUTE_REPARSE_POINT - The file is marked as a reparse point.
  915. FILE_ATTRIBUTE_VOLUME_LABEL - The file is marked as a volume lable.
  916. 0xffffffff - The operation failed. Extended error status is available
  917. using GetLastError.
  918. --*/
  919. {
  920. NTSTATUS Status;
  921. OBJECT_ATTRIBUTES Obja;
  922. UNICODE_STRING FileName;
  923. FILE_BASIC_INFORMATION BasicInfo;
  924. IO_STATUS_BLOCK IoStatusBlock;
  925. HANDLE Handle;
  926. INIT_OBJA(&Obja,&FileName,lpFileName);
  927. //
  928. // Open the file inhibiting the reparse behavior.
  929. //
  930. Status = ZwOpenFile(
  931. &Handle,
  932. (ACCESS_MASK)FILE_READ_ATTRIBUTES | SYNCHRONIZE,
  933. &Obja,
  934. &IoStatusBlock,
  935. FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
  936. FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT | FILE_OPEN_REPARSE_POINT
  937. );
  938. if ( !NT_SUCCESS(Status) ) {
  939. //
  940. // Back level file systems may not support reparse points.
  941. // We infer this is the case when the Status is STATUS_INVALID_PARAMETER.
  942. //
  943. if ( Status == STATUS_INVALID_PARAMETER ) {
  944. //
  945. // Open the file without inhibiting the reparse behavior.
  946. //
  947. Status = ZwOpenFile(
  948. &Handle,
  949. (ACCESS_MASK)FILE_READ_ATTRIBUTES | SYNCHRONIZE,
  950. &Obja,
  951. &IoStatusBlock,
  952. FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
  953. FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT
  954. );
  955. if ( !NT_SUCCESS(Status) ) {
  956. return Status;
  957. }
  958. }
  959. else {
  960. return Status;
  961. }
  962. }
  963. //
  964. // Query the file
  965. //
  966. Status = ZwQueryInformationFile(
  967. Handle,
  968. &IoStatusBlock,
  969. (PVOID) &BasicInfo,
  970. sizeof(BasicInfo),
  971. FileBasicInformation
  972. );
  973. if (NT_SUCCESS(Status)) {
  974. *FileAttributes = BasicInfo.FileAttributes;
  975. }
  976. ZwClose( Handle );
  977. return Status;
  978. }
  979. ULONG
  980. RcCmdNet(
  981. IN PTOKENIZED_LINE TokenizedLine
  982. )
  983. {
  984. WCHAR *Share;
  985. WCHAR *User;
  986. WCHAR *pwch;
  987. WCHAR PasswordBuffer[64];
  988. WCHAR Drive[3];
  989. NTSTATUS Status = STATUS_SUCCESS;
  990. IO_STATUS_BLOCK IoStatusBlock;
  991. OBJECT_ATTRIBUTES ObjectAttributes;
  992. //
  993. // check for help
  994. //
  995. if (RcCmdParseHelp( TokenizedLine, MSG_NET_USE_HELP )) {
  996. return 1;
  997. }
  998. //
  999. // There should be a token for NET and USE and one each for the server\share, and possible
  1000. // tokens for the /u:domainname\username and password.
  1001. //
  1002. if ((TokenizedLine->TokenCount < 3) || (TokenizedLine->TokenCount > 5)) {
  1003. RcMessageOut(MSG_SYNTAX_ERROR);
  1004. return 1;
  1005. }
  1006. //
  1007. // The only NET command supported is USE, so verify that the second token is that.
  1008. //
  1009. if (_wcsicmp(TokenizedLine->Tokens->Next->String, L"USE")){
  1010. RcMessageOut(MSG_SYNTAX_ERROR);
  1011. return 1;
  1012. }
  1013. //
  1014. // Get the first parameter to NET USE
  1015. //
  1016. Share = TokenizedLine->Tokens->Next->Next->String;
  1017. if (*Share == L'\\') { // attempt at making a connection
  1018. //
  1019. // Verify the share name parameter
  1020. //
  1021. if (*(Share+1) != L'\\') {
  1022. RcMessageOut(MSG_SYNTAX_ERROR);
  1023. return 1;
  1024. }
  1025. //
  1026. // get the user logon context
  1027. //
  1028. if (TokenizedLine->TokenCount > 3) {
  1029. //
  1030. // The command has the context in it, so get it.
  1031. //
  1032. User = TokenizedLine->Tokens->Next->Next->Next->String;
  1033. if (*User != L'/') {
  1034. RcMessageOut(MSG_SYNTAX_ERROR);
  1035. return 1;
  1036. }
  1037. User++;
  1038. pwch = User;
  1039. while ((*pwch != UNICODE_NULL) && (*pwch != L':')) {
  1040. pwch++;
  1041. }
  1042. if (*pwch != L':') {
  1043. RcMessageOut(MSG_SYNTAX_ERROR);
  1044. return 1;
  1045. }
  1046. *pwch = UNICODE_NULL;
  1047. pwch++;
  1048. if (_wcsicmp(User, L"USER") && _wcsicmp(User, L"U")) {
  1049. RcMessageOut(MSG_SYNTAX_ERROR);
  1050. return 1;
  1051. }
  1052. User = pwch;
  1053. //
  1054. // Get the password
  1055. //
  1056. if (TokenizedLine->TokenCount == 4) {
  1057. RcMessageOut( MSG_NET_USE_PROMPT_PASSWORD );
  1058. RtlZeroMemory( PasswordBuffer, sizeof(PasswordBuffer) );
  1059. RcPasswordIn( PasswordBuffer, 60 );
  1060. } else {
  1061. if (wcslen(TokenizedLine->Tokens->Next->Next->Next->Next->String) > 60) {
  1062. RcMessageOut(MSG_SYNTAX_ERROR);
  1063. return 1;
  1064. }
  1065. wcscpy(PasswordBuffer, TokenizedLine->Tokens->Next->Next->Next->Next->String);
  1066. if ((PasswordBuffer[0] == L'*') && (PasswordBuffer[1] == UNICODE_NULL)) {
  1067. RcMessageOut( MSG_NET_USE_PROMPT_PASSWORD );
  1068. RtlZeroMemory( PasswordBuffer, sizeof(PasswordBuffer) );
  1069. RcPasswordIn( PasswordBuffer, 60 );
  1070. } else if (PasswordBuffer[0] == L'"') {
  1071. pwch = &(PasswordBuffer[1]);
  1072. while (*pwch != UNICODE_NULL) {
  1073. pwch++;
  1074. }
  1075. pwch--;
  1076. if ((*pwch == L'"') && (pwch != &(PasswordBuffer[1]))) {
  1077. *pwch = UNICODE_NULL;
  1078. }
  1079. RtlMoveMemory(PasswordBuffer, &(PasswordBuffer[1]), (PtrToUlong(pwch) - PtrToUlong(PasswordBuffer)) + sizeof(WCHAR));
  1080. }
  1081. }
  1082. } else {
  1083. //
  1084. // If we allow holding a current context, then we would use that here, but we currently
  1085. // don't, so spew a syntax error message.
  1086. //
  1087. RcMessageOut(MSG_SYNTAX_ERROR);
  1088. return 1;
  1089. }
  1090. //
  1091. // Call worker routine to make the connection
  1092. //
  1093. Status = RcDoNetUse(Share, User, PasswordBuffer, Drive);
  1094. RtlSecureZeroMemory(PasswordBuffer, sizeof(PasswordBuffer));
  1095. if( !NT_SUCCESS(Status) ) {
  1096. RcNtError(Status, MSG_NET_USE_ERROR);
  1097. } else {
  1098. RcMessageOut(MSG_NET_USE_DRIVE_LETTER, Share, Drive);
  1099. }
  1100. } else { // attempt to disconnect
  1101. //
  1102. // Verify drive letter parameter
  1103. //
  1104. if ((*(Share+1) != L':') || (*(Share + 2) != UNICODE_NULL)) {
  1105. RcMessageOut(MSG_SYNTAX_ERROR);
  1106. return 1;
  1107. }
  1108. //
  1109. // Verify /d parameter
  1110. //
  1111. User = TokenizedLine->Tokens->Next->Next->Next->String;
  1112. if ((*User != L'/') || ((*(User + 1) != L'd') && (*(User + 1) != L'D'))) {
  1113. RcMessageOut(MSG_SYNTAX_ERROR);
  1114. return 1;
  1115. }
  1116. //
  1117. // Call worker routine to actually do the disconnect.
  1118. //
  1119. Status = RcNetUnuse(Share);
  1120. if( !NT_SUCCESS(Status) ) {
  1121. RcNtError(Status, MSG_NET_USE_ERROR);
  1122. }
  1123. }
  1124. return 1;
  1125. }