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.

1175 lines
31 KiB

  1. /*++
  2. Copyright (c) 1988-1999 Microsoft Corporation
  3. Module Name:
  4. del.c
  5. Abstract:
  6. Process the DEL/ERASE command
  7. --*/
  8. #include "cmd.h"
  9. #define Wild(spec) ((spec)->flags & (CI_NAMEWILD))
  10. VOID ResetCtrlC();
  11. extern unsigned msglen;
  12. extern TCHAR Fmt11[], Fmt19[], Fmt17[];
  13. extern TCHAR CurDrvDir[] ;
  14. extern TCHAR SwitChar;
  15. extern unsigned DosErr ;
  16. extern BOOL CtrlCSeen;
  17. extern ULONG DCount ;
  18. STATUS DelPatterns (PDRP );
  19. PTCHAR GetWildPattern( ULONG, PPATDSC );
  20. STATUS
  21. ParseRmDirParms (
  22. IN PTCHAR pszCmdLine,
  23. OUT PDRP pdrp
  24. )
  25. /*++
  26. Routine Description:
  27. Parse the command line translating the tokens into values
  28. placed in the parameter structure. The values are or'd into
  29. the parameter structure since this routine is called repeatedly
  30. to build up values (once for the environment variable DIRCMD
  31. and once for the actual command line).
  32. Arguments:
  33. pszCmdLine - pointer to command line user typed
  34. Return Value:
  35. pdrp - parameter data structure
  36. Return: TRUE - if valid command line.
  37. FALSE - if not.
  38. --*/
  39. {
  40. PTCHAR pszTok;
  41. TCHAR szT[10] ;
  42. USHORT irgchTok;
  43. BOOLEAN fToggle;
  44. PPATDSC ppatdscCur;
  45. int tlen;
  46. //
  47. // Tokensize the command line (special delimeters are tokens)
  48. //
  49. szT[0] = SwitChar ;
  50. szT[1] = NULLC ;
  51. pszTok = TokStr(pszCmdLine, szT, TS_SDTOKENS) ;
  52. ppatdscCur = &(pdrp->patdscFirst);
  53. //
  54. // If there was a pattern put in place from the environment.
  55. // just add any new patterns on. So move to the end of the
  56. // current list.
  57. //
  58. if (pdrp->cpatdsc) {
  59. while (ppatdscCur->ppatdscNext) {
  60. ppatdscCur = ppatdscCur->ppatdscNext;
  61. }
  62. }
  63. //
  64. // At this state pszTok will be a series of zero terminated strings.
  65. // "/o foo" wil be /0o0foo0
  66. //
  67. for ( irgchTok = 0; *pszTok ; pszTok += tlen+1, irgchTok = 0) {
  68. tlen = mystrlen(pszTok);
  69. DEBUG((ICGRP, DILVL, "PRIVSW: pszTok = %ws", pszTok)) ;
  70. //
  71. // fToggle control wither to turn off a switch that was set
  72. // in the DIRCMD environment variable.
  73. //
  74. fToggle = FALSE;
  75. if (pszTok[irgchTok] == (TCHAR)SwitChar) {
  76. if (pszTok[irgchTok + 2] == MINUS) {
  77. //
  78. // disable the previously enabled the switch
  79. //
  80. fToggle = TRUE;
  81. irgchTok++;
  82. }
  83. switch (_totupper(pszTok[irgchTok + 2])) {
  84. case QUIETCH:
  85. fToggle ? (pdrp->rgfSwitches ^= QUIETSWITCH) : (pdrp->rgfSwitches |= QUIETSWITCH);
  86. if (pszTok[irgchTok + 3]) {
  87. PutStdErr(MSG_PARAMETER_FORMAT_NOT_CORRECT, ONEARG,
  88. (UINT_PTR)(&(pszTok[irgchTok + 2])) );
  89. return( FAILURE );
  90. }
  91. break;
  92. case TEXT('S'):
  93. fToggle ? (pdrp->rgfSwitches ^= RECURSESWITCH) : (pdrp->rgfSwitches |= RECURSESWITCH);
  94. if (pszTok[irgchTok + 3]) {
  95. PutStdErr(MSG_PARAMETER_FORMAT_NOT_CORRECT, ONEARG,
  96. (UINT_PTR)(&(pszTok[irgchTok + 2])) );
  97. return( FAILURE );
  98. }
  99. break;
  100. case QMARK:
  101. BeginHelpPause();
  102. PutStdOut(MSG_HELP_DIR, NOARGS);
  103. EndHelpPause();
  104. return( FAILURE );
  105. break;
  106. default:
  107. szT[0] = SwitChar;
  108. szT[1] = pszTok[2];
  109. szT[2] = NULLC;
  110. PutStdErr(MSG_INVALID_SWITCH,
  111. ONEARG,
  112. (UINT_PTR)(&(pszTok[irgchTok + 2])) );
  113. return( FAILURE );
  114. } // switch
  115. //
  116. // TokStr parses /N as /0N0 so we need to move over the
  117. // switchar in or to move past the actual switch value
  118. // in for loop.
  119. //
  120. pszTok += 2;
  121. } else {
  122. mystrcpy( pszTok, StripQuotes( pszTok ) );
  123. //
  124. // If there already is a list the extend it else put info
  125. // directly into structure.
  126. //
  127. if (pdrp->cpatdsc) {
  128. ppatdscCur->ppatdscNext = (PPATDSC)gmkstr(sizeof(PATDSC));
  129. ppatdscCur = ppatdscCur->ppatdscNext;
  130. ppatdscCur->ppatdscNext = NULL;
  131. }
  132. pdrp->cpatdsc++;
  133. ppatdscCur->pszPattern = (PTCHAR)gmkstr(_tcslen(pszTok)*sizeof(TCHAR) + sizeof(TCHAR));
  134. mystrcpy(ppatdscCur->pszPattern, pszTok);
  135. ppatdscCur->fIsFat = TRUE;
  136. }
  137. } // for
  138. return( SUCCESS );
  139. }
  140. STATUS
  141. ParseDelParms (
  142. IN PTCHAR pszCmdLine,
  143. OUT PDRP pdrp
  144. )
  145. /*++
  146. Routine Description:
  147. Parse the command line translating the tokens into values
  148. placed in the parameter structure. The values are or'd into
  149. the parameter structure since this routine is called repeatedly
  150. to build up values (once for the environment variable DIRCMD
  151. and once for the actual command line).
  152. Arguments:
  153. pszCmdLine - pointer to command line user typed
  154. Return Value:
  155. pdrp - parameter data structure
  156. Return: TRUE - if valid command line.
  157. FALSE - if not.
  158. --*/
  159. {
  160. PTCHAR pszTok;
  161. TCHAR szT[10] ;
  162. USHORT irgchTok;
  163. BOOLEAN fToggle;
  164. PPATDSC ppatdscCur;
  165. //
  166. // Tokensize the command line (special delimeters are tokens)
  167. //
  168. szT[0] = SwitChar ;
  169. szT[1] = NULLC ;
  170. pszTok = TokStr(pszCmdLine, szT, TS_SDTOKENS) ;
  171. ppatdscCur = &(pdrp->patdscFirst);
  172. //
  173. // If there was a pattern put in place from the environment.
  174. // just add any new patterns on. So move to the end of the
  175. // current list.
  176. //
  177. if (pdrp->cpatdsc) {
  178. while (ppatdscCur->ppatdscNext) {
  179. ppatdscCur = ppatdscCur->ppatdscNext;
  180. }
  181. }
  182. //
  183. // At this state pszTok will be a series of zero terminated strings.
  184. // "/o foo" wil be /0o0foo0
  185. //
  186. for ( irgchTok = 0; *pszTok ; pszTok += mystrlen(pszTok)+1, irgchTok = 0) {
  187. DEBUG((ICGRP, DILVL, "PRIVSW: pszTok = %ws", (ULONG_PTR)pszTok)) ;
  188. //
  189. // fToggle control wither to turn off a switch that was set
  190. // in the DIRCMD environment variable.
  191. //
  192. fToggle = FALSE;
  193. if (pszTok[irgchTok] == (TCHAR)SwitChar) {
  194. if (pszTok[irgchTok + 2] == MINUS) {
  195. //
  196. // disable the previously enabled the switch
  197. //
  198. fToggle = TRUE;
  199. irgchTok++;
  200. }
  201. switch (_totupper(pszTok[irgchTok + 2])) {
  202. case TEXT('P'):
  203. fToggle ? (pdrp->rgfSwitches ^= PROMPTUSERSWITCH) : (pdrp->rgfSwitches |= PROMPTUSERSWITCH);
  204. if (pszTok[irgchTok + 3]) {
  205. PutStdErr(MSG_PARAMETER_FORMAT_NOT_CORRECT, ONEARG,
  206. (UINT_PTR)(&(pszTok[irgchTok + 2])) );
  207. return( FAILURE );
  208. }
  209. break;
  210. case TEXT('S'):
  211. fToggle ? (pdrp->rgfSwitches ^= RECURSESWITCH) : (pdrp->rgfSwitches |= RECURSESWITCH);
  212. if (pszTok[irgchTok + 3]) {
  213. PutStdErr(MSG_PARAMETER_FORMAT_NOT_CORRECT, ONEARG,
  214. (UINT_PTR)(&(pszTok[irgchTok + 2])) );
  215. return( FAILURE );
  216. }
  217. break;
  218. case TEXT('F'):
  219. fToggle ? (pdrp->rgfSwitches ^= FORCEDELSWITCH) : (pdrp->rgfSwitches |= FORCEDELSWITCH);
  220. if (pszTok[irgchTok + 3]) {
  221. PutStdErr(MSG_PARAMETER_FORMAT_NOT_CORRECT, ONEARG,
  222. (UINT_PTR)(&(pszTok[irgchTok + 2])) );
  223. return( FAILURE );
  224. }
  225. break;
  226. case QMARK:
  227. BeginHelpPause();
  228. PutStdOut(MSG_HELP_DEL_ERASE, NOARGS);
  229. EndHelpPause();
  230. return( FAILURE );
  231. break;
  232. case QUIETCH:
  233. fToggle ? (pdrp->rgfSwitches ^= QUIETSWITCH) : (pdrp->rgfSwitches |= QUIETSWITCH);
  234. if (pszTok[irgchTok + 3]) {
  235. PutStdErr(MSG_PARAMETER_FORMAT_NOT_CORRECT, ONEARG,
  236. (UINT_PTR)(&(pszTok[irgchTok + 2])) );
  237. return( FAILURE );
  238. }
  239. break;
  240. case TEXT('A'):
  241. if (fToggle) {
  242. if ( _tcslen( &(pszTok[irgchTok + 2]) ) > 1) {
  243. PutStdErr(MSG_PARAMETER_FORMAT_NOT_CORRECT, ONEARG,
  244. (UINT_PTR)(&(pszTok[irgchTok + 2])) );
  245. return( FAILURE );
  246. }
  247. pdrp->rgfAttribs = FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN;
  248. pdrp->rgfAttribsOnOff = 0;
  249. break;
  250. }
  251. if (SetAttribs(&(pszTok[irgchTok + 3]), pdrp) ) {
  252. return( FAILURE );
  253. }
  254. if (pdrp->rgfAttribsOnOff & FILE_ATTRIBUTE_READONLY) {
  255. pdrp->rgfSwitches |= FORCEDELSWITCH;
  256. }
  257. break;
  258. default:
  259. szT[0] = SwitChar;
  260. szT[1] = pszTok[2];
  261. szT[2] = NULLC;
  262. PutStdErr(MSG_INVALID_SWITCH,
  263. ONEARG,
  264. (UINT_PTR)(&(pszTok[irgchTok + 2])) );
  265. return( FAILURE );
  266. } // switch
  267. //
  268. // TokStr parses /N as /0N0 so we need to move over the
  269. // switchar in or to move past the actual switch value
  270. // in for loop.
  271. //
  272. pszTok += 2;
  273. } else {
  274. //
  275. // If there already is a list then extend it else put info
  276. // directly into structure.
  277. //
  278. if (pdrp->cpatdsc) {
  279. ppatdscCur->ppatdscNext = (PPATDSC)gmkstr(sizeof(PATDSC));
  280. ppatdscCur = ppatdscCur->ppatdscNext;
  281. ppatdscCur->ppatdscNext = NULL;
  282. }
  283. pdrp->cpatdsc++;
  284. ppatdscCur->pszPattern = (PTCHAR)gmkstr(_tcslen(pszTok)*sizeof(TCHAR) + sizeof(TCHAR));
  285. mystrcpy(ppatdscCur->pszPattern, StripQuotes(pszTok));
  286. ppatdscCur->fIsFat = TRUE;
  287. }
  288. } // for
  289. return( SUCCESS );
  290. }
  291. int
  292. DelWork (
  293. TCHAR *pszCmdLine
  294. ) {
  295. //
  296. // drp - structure holding current set of parameters. It is initialized
  297. // in ParseDelParms function. It is also modified later when
  298. // parameters are examined to determine if some turn others on.
  299. //
  300. DRP drpCur = {0, 0, 0, 0,
  301. {{0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}},
  302. 0, 0, NULL, 0, 0, 0, 0} ;
  303. //
  304. // szCurDrv - Hold current drive letter
  305. //
  306. TCHAR szCurDrv[MAX_PATH + 2];
  307. //
  308. // OldDCount - Holds the level number of the heap. It is used to
  309. // free entries off the stack that might not have been
  310. // freed due to error processing (ctrl-c etc.)
  311. ULONG OldDCount;
  312. STATUS rc;
  313. OldDCount = DCount;
  314. //
  315. // Setup defaults
  316. //
  317. //
  318. // Display everything but system and hidden files
  319. // rgfAttribs set the attribute bits to that are of interest and
  320. // rgfAttribsOnOff says wither the attributs should be present
  321. // or not (i.e. On or Off)
  322. //
  323. drpCur.rgfAttribs = FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN;
  324. drpCur.rgfAttribsOnOff = 0;
  325. //
  326. // Number of patterns present. A pattern is a string that may have
  327. // wild cards. It is used to match against files present in the directory
  328. // 0 patterns will show all files (i.e. mapped to *.*)
  329. //
  330. drpCur.cpatdsc = 0;
  331. //
  332. // default time is LAST_WRITE_TIME.
  333. //
  334. drpCur.dwTimeType = LAST_WRITE_TIME;
  335. //
  336. //
  337. //
  338. if (ParseDelParms(pszCmdLine, &drpCur) == FAILURE) {
  339. return( FAILURE );
  340. }
  341. //
  342. // Must have some pattern on command line
  343. //
  344. //
  345. GetDir((PTCHAR)szCurDrv, GD_DEFAULT);
  346. if (drpCur.cpatdsc == 0) {
  347. PutStdErr(MSG_BAD_SYNTAX, NOARGS);
  348. return(FAILURE);
  349. }
  350. //
  351. // Print out this particular pattern. If the recursion switch
  352. // is set then this will descend down the tree.
  353. //
  354. drpCur.rgfSwitches |= DELPROCESSEARLY;
  355. rc = DelPatterns( &drpCur );
  356. mystrcpy(CurDrvDir, szCurDrv);
  357. //
  358. // Free unneeded memory
  359. //
  360. FreeStack( OldDCount );
  361. return( (int)rc );
  362. }
  363. STATUS
  364. NewEraseFile (
  365. IN PFS CurrentFS,
  366. IN PFF CurrentFF,
  367. IN PSCREEN pscr,
  368. IN PVOID Data
  369. )
  370. {
  371. TCHAR szFile[MAX_PATH + 2];
  372. STATUS rc;
  373. PTCHAR LastComponent;
  374. PTCHAR pszPattern;
  375. TCHAR szFilePrompt[MAX_PATH + 2];
  376. int incr;
  377. PDRP pdrp = (PDRP) Data;
  378. PWIN32_FIND_DATA pdata = &CurrentFF->data;
  379. USHORT obAlternate = CurrentFF->obAlternate;
  380. BOOLEAN fPrompt = FALSE;
  381. BOOLEAN fQuiet = FALSE;
  382. if (pdrp->rgfSwitches & PROMPTUSERSWITCH) {
  383. fPrompt = TRUE;
  384. }
  385. if (pdrp->rgfSwitches & QUIETSWITCH) {
  386. fQuiet = TRUE;
  387. }
  388. //
  389. // Global delete prompt
  390. //
  391. if (
  392. // Not prompting on each file
  393. !fPrompt &&
  394. // Global prompt not issued yet
  395. !CurrentFS->fDelPrompted &&
  396. // not suppressing global prompt
  397. !fQuiet &&
  398. // global pattern in delete
  399. (pszPattern = GetWildPattern( CurrentFS->cpatdsc, CurrentFS->ppatdsc ))) {
  400. //
  401. // Form complete path for prompt
  402. //
  403. if (AppendPath( szFile, MAX_PATH + 2, CurrentFS->pszDir, pszPattern ) != SUCCESS) {
  404. PutStdErr(MSG_PATH_TOO_LONG, TWOARGS, CurrentFS->pszDir, pszPattern );
  405. return( FAILURE );
  406. }
  407. //
  408. // Prompt the user and see if we can continue
  409. //
  410. CurrentFS->fDelPrompted = TRUE;
  411. if (PromptUser( szFile, MSG_ARE_YOU_SURE, MSG_NOYES_RESPONSE_DATA ) != 1) {
  412. return( FAILURE );
  413. }
  414. }
  415. //
  416. // Directories succeed here
  417. //
  418. if ((pdata->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) {
  419. return SUCCESS;
  420. }
  421. //
  422. // Form a short name for the delete operation. This gives us the best
  423. // chance to delete a really long path.
  424. //
  425. if (AppendPath( szFile, MAX_PATH + 2, CurrentFS->pszDir, pdata->cFileName + obAlternate) != SUCCESS) {
  426. PutStdErr(MSG_PATH_TOO_LONG, TWOARGS, CurrentFS->pszDir, pdata->cFileName + obAlternate );
  427. return( FAILURE );
  428. }
  429. //
  430. // Form a long name for the prompt. If it's too long, then use the short name for prompt
  431. //
  432. if (AppendPath( szFilePrompt, MAX_PATH + 2, CurrentFS->pszDir, pdata->cFileName ) != SUCCESS) {
  433. _tcscpy( szFilePrompt, szFile );
  434. }
  435. //
  436. // Prompt the user for this one file
  437. //
  438. if (fPrompt && PromptUser( szFilePrompt, MSG_CMD_DELETE, MSG_NOYES_RESPONSE_DATA ) != 1) {
  439. if (CtrlCSeen) {
  440. return( FAILURE );
  441. }
  442. return( SUCCESS );
  443. }
  444. //
  445. // If we are forcibly deleteing everything and this is a read-only file
  446. // then turn off R/O.
  447. //
  448. if ((pdrp->rgfSwitches & FORCEDELSWITCH) != 0 &&
  449. pdata->dwFileAttributes & FILE_ATTRIBUTE_READONLY) {
  450. if (!SetFileAttributes( szFile, pdata->dwFileAttributes & ~FILE_ATTRIBUTE_READONLY )) {
  451. PutStdErr( GetLastError(), NOARGS );
  452. return( FAILURE );
  453. }
  454. }
  455. //
  456. // Delete the file. Display error if we got a failure
  457. //
  458. if (!DeleteFile( szFile )) {
  459. rc = GetLastError( );
  460. } else {
  461. rc = SUCCESS;
  462. }
  463. if (rc != SUCCESS) {
  464. if (rc == ERROR_REQUEST_ABORTED) {
  465. return FAILURE;
  466. }
  467. cmd_printf( Fmt17, szFilePrompt );
  468. PutStdErr( rc, NOARGS );
  469. } else {
  470. pdrp->FileCount++;
  471. if (fEnableExtensions && (pdrp->rgfSwitches & RECURSESWITCH)) {
  472. PutStdOut(MSG_FILE_DELETED, ONEARG, szFilePrompt);
  473. }
  474. }
  475. return SUCCESS;
  476. }
  477. STATUS
  478. DelPatterns (
  479. IN PDRP pdpr
  480. )
  481. {
  482. PPATDSC ppatdscCur;
  483. PPATDSC ppatdscX;
  484. PFS pfsFirst;
  485. PFS pfsCur;
  486. ULONG i;
  487. STATUS rc;
  488. ULONG cffTotal = 0;
  489. TCHAR szSearchPath[MAX_PATH+2];
  490. DosErr = 0;
  491. if (BuildFSFromPatterns(pdpr, TRUE, FALSE, &pfsFirst ) == FAILURE) {
  492. return( FAILURE );
  493. }
  494. for( pfsCur = pfsFirst; pfsCur; pfsCur = pfsCur->pfsNext) {
  495. rc = WalkTree( pfsCur,
  496. NULL,
  497. pdpr->rgfAttribs,
  498. pdpr->rgfAttribsOnOff,
  499. (BOOLEAN)(pdpr->rgfSwitches & RECURSESWITCH),
  500. pdpr,
  501. NULL, // Error
  502. NULL, // Pre
  503. NewEraseFile, // Scan
  504. NULL ); // Post
  505. //
  506. // If we had a general failure, just return
  507. //
  508. if (rc == FAILURE) {
  509. return rc;
  510. }
  511. //
  512. // If we had a failure that we don't understand, print the error and
  513. // exit
  514. //
  515. if (rc != SUCCESS && rc != ERROR_FILE_NOT_FOUND) {
  516. PutStdErr( rc, NOARGS );
  517. return rc;
  518. }
  519. //
  520. // If we got FILE_NOT_FOUND then build the full name and
  521. // display the file not-found message
  522. //
  523. if (rc == ERROR_FILE_NOT_FOUND) {
  524. rc = AppendPath( szSearchPath,
  525. sizeof( szSearchPath ) / sizeof( TCHAR ),
  526. pfsCur->pszDir,
  527. pfsCur->ppatdsc->pszPattern );
  528. if (rc == SUCCESS) {
  529. PutStdErr( MSG_NOT_FOUND, ONEARG, szSearchPath );
  530. }
  531. //
  532. // We leave the status code alone here. If we succeeded in
  533. // generating the message, then the del command "succeeded"
  534. // in that the named file is not there.
  535. //
  536. }
  537. //
  538. // Have walked down and back up the tree, but in the case of
  539. // deleting directories we have not deleted the top most directory
  540. // do so now.
  541. //
  542. FreeStr(pfsCur->pszDir);
  543. for(i = 1, ppatdscCur = pfsCur->ppatdsc;
  544. i <= pfsCur->cpatdsc;
  545. i++, ppatdscCur = ppatdscX) {
  546. ppatdscX = ppatdscCur->ppatdscNext;
  547. FreeStr(ppatdscCur->pszPattern);
  548. FreeStr(ppatdscCur->pszDir);
  549. FreeStr((PTCHAR)ppatdscCur);
  550. }
  551. }
  552. return(rc);
  553. }
  554. STATUS
  555. RemoveDirectoryForce(
  556. PTCHAR pszDirectory
  557. )
  558. /*++
  559. Routine Description:
  560. Removes a directory, even if it is read-only.
  561. Arguments:
  562. pszDirectory - Supplies the name of the directory to delete.
  563. Return Value:
  564. SUCCESS - Success.
  565. other - Windows error code.
  566. --*/
  567. {
  568. STATUS Status = SUCCESS;
  569. BOOL Ok;
  570. DWORD Attr;
  571. TCHAR szRootPath[ 4 ];
  572. TCHAR *pFilePart;
  573. if (GetFullPathName(pszDirectory, 4, szRootPath, &pFilePart) == 3 &&
  574. szRootPath[1] == COLON &&
  575. szRootPath[2] == BSLASH
  576. ) {
  577. //
  578. // Don't waste time trying to delete the root directory.
  579. //
  580. return SUCCESS;
  581. }
  582. if ( !RemoveDirectory( pszDirectory ) ) {
  583. Status = (STATUS)GetLastError();
  584. if ( Status == ERROR_ACCESS_DENIED ) {
  585. Attr = GetFileAttributes( pszDirectory );
  586. if ( Attr != 0xFFFFFFFF &&
  587. Attr & FILE_ATTRIBUTE_READONLY ) {
  588. Attr &= ~FILE_ATTRIBUTE_READONLY;
  589. if ( SetFileAttributes( pszDirectory, Attr ) ) {
  590. if ( RemoveDirectory( pszDirectory ) ) {
  591. Status = SUCCESS;
  592. } else {
  593. Status = GetLastError();
  594. }
  595. }
  596. }
  597. }
  598. }
  599. return Status;
  600. }
  601. STATUS
  602. RmDirSlashS(
  603. IN PTCHAR pszDirectory,
  604. OUT PBOOL AllEntriesDeleted
  605. )
  606. /*++
  607. Routine Description:
  608. This routine deletes the given directory including all
  609. of its files and subdirectories.
  610. Arguments:
  611. pszDirectory - Supplies the name of the directory to delete.
  612. AllEntriesDeleted - Returns whether or not all files were deleted.
  613. Return Value:
  614. SUCCESS - Success.
  615. other - Windows error code.
  616. --*/
  617. {
  618. HANDLE find_handle;
  619. DWORD attr;
  620. STATUS s;
  621. BOOL all_deleted;
  622. int dir_len, new_len;
  623. TCHAR *new_str;
  624. WIN32_FIND_DATA find_data;
  625. TCHAR pszFileBuffer[MAX_PATH];
  626. *AllEntriesDeleted = TRUE;
  627. dir_len = _tcslen(pszDirectory);
  628. if (dir_len == 0) {
  629. return ERROR_BAD_PATHNAME;
  630. }
  631. //
  632. // If this path is so long that we can't append \* to it then
  633. // it can't have any subdirectories.
  634. //
  635. if (dir_len + 3 > MAX_PATH) {
  636. return RemoveDirectoryForce(pszDirectory);
  637. }
  638. // Compute the findfirst pattern for enumerating the files
  639. // in the given directory.
  640. _tcscpy(pszFileBuffer, pszDirectory);
  641. if (dir_len && pszDirectory[dir_len - 1] != COLON &&
  642. pszDirectory[dir_len - 1] != BSLASH) {
  643. _tcscat(pszFileBuffer, TEXT("\\"));
  644. dir_len++;
  645. }
  646. _tcscat(pszFileBuffer, TEXT("*"));
  647. // Initiate findfirst loop.
  648. find_handle = FindFirstFile(pszFileBuffer, &find_data);
  649. if (find_handle == INVALID_HANDLE_VALUE) {
  650. return RemoveDirectoryForce(pszDirectory);
  651. }
  652. do {
  653. // Check for control-C.
  654. if (CtrlCSeen) {
  655. break;
  656. }
  657. //
  658. // Replace previous file name with new one, checking against MAX_PATH
  659. // Using the short name where possible lets us go deeper before we hit
  660. // the MAX_PATH limit.
  661. //
  662. new_len = _tcslen(new_str = find_data.cAlternateFileName);
  663. if (!new_len)
  664. new_len = _tcslen(new_str = find_data.cFileName);
  665. if (dir_len + new_len >= MAX_PATH) {
  666. *AllEntriesDeleted = FALSE;
  667. PutStdErr( MSG_PATH_TOO_LONG, 2, pszDirectory, new_str );
  668. break;
  669. }
  670. _tcscpy(&pszFileBuffer[dir_len], new_str);
  671. attr = find_data.dwFileAttributes;
  672. //
  673. // If the entry is a directory and not a reparse directory descend into it
  674. //
  675. if (IsDirectory( attr ) && !IsReparse( attr )) {
  676. if (!_tcscmp(find_data.cFileName, TEXT(".")) ||
  677. !_tcscmp(find_data.cFileName, TEXT(".."))) {
  678. continue;
  679. }
  680. s = RmDirSlashS(pszFileBuffer, &all_deleted);
  681. // Don't report error if control-C
  682. if (CtrlCSeen) {
  683. break;
  684. }
  685. if (s != ESUCCESS) {
  686. *AllEntriesDeleted = FALSE;
  687. if (s != ERROR_DIR_NOT_EMPTY || all_deleted) {
  688. PutStdErr(MSG_FILE_NAME_PRECEEDING_ERROR, 1, pszFileBuffer);
  689. PutStdErr(GetLastError(), NOARGS);
  690. }
  691. }
  692. } else {
  693. if (attr&FILE_ATTRIBUTE_READONLY) {
  694. SetFileAttributes(pszFileBuffer,
  695. attr&(~FILE_ATTRIBUTE_READONLY));
  696. }
  697. //
  698. // Two ways of removal:
  699. // if reparse and dir call RemoveDirectoryForce
  700. // else call DeleteFile
  701. //
  702. // If either call fails
  703. //
  704. if ( (IsDirectory( attr ) && IsReparse( attr ) && RemoveDirectoryForce( pszFileBuffer ) != SUCCESS) ||
  705. (!IsDirectory( attr ) && !DeleteFile( pszFileBuffer ))) {
  706. s = GetLastError();
  707. if ( s == ERROR_REQUEST_ABORTED )
  708. break;
  709. //
  710. // Win32 always reports file names that are too long as
  711. // ERROR_PATH_NOT_FOUND. So, when we get this error back,
  712. // we test to see if the path was too long
  713. // and attempt to map the error.
  714. //
  715. if (s == ERROR_PATH_NOT_FOUND && _tcsnicmp( pszFileBuffer, TEXT( "\\\\?\\"), 4) ) {
  716. int Length = GetFullPathName( pszFileBuffer, 0, NULL, NULL );
  717. if (Length > MAX_PATH) {
  718. SetLastError( ERROR_BUFFER_OVERFLOW );
  719. }
  720. }
  721. if (_tcslen(find_data.cAlternateFileName)) {
  722. pszFileBuffer[dir_len] = 0;
  723. if (dir_len + _tcslen(find_data.cFileName) >= MAX_PATH) {
  724. _tcscat(pszFileBuffer, find_data.cAlternateFileName);
  725. PutStdErr(MSG_FILE_NAME_PRECEEDING_ERROR, 1, pszFileBuffer);
  726. }
  727. else {
  728. _tcscat(pszFileBuffer, find_data.cFileName);
  729. PutStdErr(MSG_FILE_NAME_PRECEEDING_ERROR, 1, pszFileBuffer);
  730. pszFileBuffer[dir_len] = 0;
  731. _tcscat(pszFileBuffer, find_data.cAlternateFileName);
  732. }
  733. } else {
  734. PutStdErr(MSG_FILE_NAME_PRECEEDING_ERROR, 1, pszFileBuffer);
  735. }
  736. PutStdErr(GetLastError(), NOARGS);
  737. SetFileAttributes(pszFileBuffer, attr);
  738. *AllEntriesDeleted = FALSE;
  739. }
  740. }
  741. } while (FindNextFile( find_handle, &find_data ));
  742. FindClose(find_handle);
  743. // If control-C was hit then don't bother trying to remove the
  744. // directory.
  745. if (CtrlCSeen) {
  746. return SUCCESS;
  747. }
  748. return RemoveDirectoryForce(pszDirectory);
  749. }
  750. int
  751. RdWork (
  752. TCHAR *pszCmdLine
  753. ) {
  754. //
  755. // drp - structure holding current set of parameters. It is initialized
  756. // in ParseDelParms function. It is also modified later when
  757. // parameters are examined to determine if some turn others on.
  758. //
  759. DRP drpCur = {0, 0, 0, 0,
  760. {{0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}},
  761. 0, 0, NULL, 0, 0, 0, 0} ;
  762. //
  763. // szCurDrv - Hold current drive letter
  764. //
  765. TCHAR szCurDrv[MAX_PATH + 2];
  766. //
  767. // OldDCount - Holds the level number of the heap. It is used to
  768. // free entries off the stack that might not have been
  769. // freed due to error processing (ctrl-c etc.)
  770. ULONG OldDCount;
  771. PPATDSC ppatdscCur;
  772. ULONG cpatdsc;
  773. STATUS rc, s;
  774. BOOL all_deleted;
  775. rc = SUCCESS;
  776. OldDCount = DCount;
  777. //
  778. // Setup defaults
  779. //
  780. //
  781. // Display everything but system and hidden files
  782. // rgfAttribs set the attribute bits to that are of interest and
  783. // rgfAttribsOnOff says wither the attributs should be present
  784. // or not (i.e. On or Off)
  785. //
  786. drpCur.rgfAttribs = FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN;
  787. drpCur.rgfAttribsOnOff = 0;
  788. //
  789. // Number of patterns present. A pattern is a string that may have
  790. // wild cards. It is used to match against files present in the directory
  791. // 0 patterns will show all files (i.e. mapped to *.*)
  792. //
  793. drpCur.cpatdsc = 0;
  794. //
  795. // default time is LAST_WRITE_TIME.
  796. //
  797. drpCur.dwTimeType = LAST_WRITE_TIME;
  798. //
  799. //
  800. //
  801. if (ParseRmDirParms(pszCmdLine, &drpCur) == FAILURE) {
  802. return( FAILURE );
  803. }
  804. GetDir((PTCHAR)szCurDrv, GD_DEFAULT);
  805. //
  806. // If no patterns on the command line then syntax error out
  807. //
  808. if (drpCur.cpatdsc == 0) {
  809. PutStdErr(MSG_BAD_SYNTAX, NOARGS);
  810. return( FAILURE );
  811. }
  812. for (ppatdscCur = &(drpCur.patdscFirst),cpatdsc = drpCur.cpatdsc;
  813. cpatdsc;
  814. ppatdscCur = ppatdscCur->ppatdscNext, cpatdsc--) {
  815. if (mystrlen( ppatdscCur->pszPattern ) == 0) {
  816. PutStdErr( MSG_BAD_SYNTAX, NOARGS );
  817. return rc = FAILURE;
  818. }
  819. }
  820. for (ppatdscCur = &(drpCur.patdscFirst),cpatdsc = drpCur.cpatdsc;
  821. cpatdsc;
  822. ppatdscCur = ppatdscCur->ppatdscNext, cpatdsc--) {
  823. if (drpCur.rgfSwitches & RECURSESWITCH) {
  824. if (!(drpCur.rgfSwitches & QUIETSWITCH) &&
  825. PromptUser(ppatdscCur->pszPattern, MSG_ARE_YOU_SURE, MSG_NOYES_RESPONSE_DATA) != 1
  826. ) {
  827. rc = FAILURE;
  828. } else {
  829. s = RmDirSlashS(ppatdscCur->pszPattern, &all_deleted);
  830. if (s != SUCCESS && (s != ERROR_DIR_NOT_EMPTY || all_deleted)) {
  831. PutStdErr(rc = s, NOARGS);
  832. }
  833. }
  834. } else {
  835. if (!RemoveDirectory(ppatdscCur->pszPattern)) {
  836. PutStdErr(rc = GetLastError(), NOARGS);
  837. }
  838. }
  839. }
  840. mystrcpy(CurDrvDir, szCurDrv);
  841. //
  842. // Free unneeded memory
  843. //
  844. FreeStack( OldDCount );
  845. #ifdef _CRTHEAP_
  846. //
  847. // Force the crt to release heap we may have taken on recursion
  848. //
  849. if (drpCur.rgfSwitches & RECURSESWITCH) {
  850. _heapmin();
  851. }
  852. #endif
  853. return( (int)rc );
  854. }
  855. PTCHAR
  856. GetWildPattern(
  857. IN ULONG cpatdsc,
  858. IN PPATDSC ppatdsc
  859. )
  860. /*
  861. return pointer to a pattern if it contains only a wild card
  862. */
  863. {
  864. ULONG i;
  865. PTCHAR pszT;
  866. for(i = 1; i <= cpatdsc; i++, ppatdsc = ppatdsc->ppatdscNext) {
  867. pszT = ppatdsc->pszPattern;
  868. if (!_tcscmp(pszT, TEXT("*")) ||
  869. !_tcscmp(pszT, TEXT("*.*")) ||
  870. !_tcscmp(pszT, TEXT("????????.???")) ) {
  871. return( pszT );
  872. }
  873. }
  874. return( NULL );
  875. }