Source code of Windows XP (NT5)
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.

450 lines
14 KiB

  1. /*** UNDEL.C - retrieve deleted files if possible ***************************
  2. *
  3. * Copyright (c) 1987-1990, Microsoft Corporation. All rights reserved.
  4. *
  5. * Purpose:
  6. * The three tools EXP, RM and UNDEL are used to delete files so
  7. * that they can be undeleted. This is done my renaming the file into
  8. * a hidden directory called DELETED.
  9. *
  10. * Notes:
  11. * This tool allows the user to view a list of the files that have been
  12. * 'deleted' from the current directory, and to undelete a file from
  13. * the list.
  14. *
  15. * To view a list of undeleted files: UNDEL
  16. *
  17. * To undelete a file: UNDEL filename [filename ...]
  18. *
  19. * If more than one file by that name has been deleted, a list of the
  20. * deletions, by date, will be presented and the user prompted to
  21. * choose one.
  22. *
  23. * If a file by that name exists currently, it is RM'ed before the
  24. * deleted file is restored.
  25. *
  26. * Revision History:
  27. * 17-Oct-1990 w-barry Temporarily replaced 'rename' with 'rename_NT' until
  28. * DosMove completely implemented on NT.
  29. * 29-Jun-1990 SB Do not do index conversion if file is readonly ...
  30. * 29-Jun-1990 SB print filename only once if one instance is to be undel'ed
  31. * 08-Feb-1990 bw Do index file conversion in dump()
  32. * 07-Feb-1990 bw Third arg to readNewIdxRec
  33. * 08-Jan-1990 SB SLM version upgrading added; Add CopyRightYrs Macro
  34. * 03-Jan-1990 SB define QH_TOPIC_NOT_FOUND
  35. * 28-Dec-1989 SB Add #ifdef BEAUTIFY stuff
  36. * 27-Dec-1989 SB Changes for new index file format
  37. * 15-Dec-1989 SB Include os2.h instead of doscalls.h
  38. * Qh return code 3 means 'Topic not Found'
  39. * 14-Dec-1989 LN Update Copyright to include 1990
  40. * 23-Oct-1989 LN Version no bumped to 1.01
  41. * 02-Oct-1989 LN Changed Version no to 1.00
  42. * 08-Aug-1989 bw Add Version number, update copyright
  43. * 15-May-1989 wb Add /help
  44. * 24-Jan-1989 bw Use C runtime rename() so fastcopy doesn't get dragged in.
  45. * 30-Oct-1987 bw Changed 'DOS5' to 'OS2'
  46. * 06-Apr-1987 bw Add Usage prompt for /<anything>
  47. * 18-Oct-1990 w-barry Removed 'dead' code.
  48. * 28-Nov-1990 w-barry Switched to Win32 API - Replaced DosQueryFSInfo() with
  49. * GetDiskFreeSpace in the Dump() routine.
  50. *
  51. ******************************************************************************/
  52. /* I N C L U D E Files */
  53. #include <sys\types.h>
  54. #include <sys\stat.h>
  55. #include <dos.h>
  56. #include <errno.h>
  57. #include <fcntl.h>
  58. #include <malloc.h>
  59. #include <process.h>
  60. #include <io.h>
  61. #include <time.h>
  62. #include <string.h>
  63. #include <stdio.h>
  64. #include <ctype.h>
  65. #include <math.h>
  66. #include <stdlib.h>
  67. #include <windows.h>
  68. #include <tools.h>
  69. #include <rm.h>
  70. /* D E F I N E s */
  71. #define CopyRightYrs "1987-90"
  72. /* Need 2 steps, first to get correct values in and 2nd to paste them */
  73. /* paste() is hacked to allow LEADING ZEROES */
  74. #define paste(a, b, c) #a ".0" #b ".00" #c
  75. #define VERSION(major, minor, buildno) paste(major, minor, buildno)
  76. #define QH_TOPIC_NOT_FOUND 3
  77. // Forward Function Declarations...
  78. void Usage( void );
  79. void dump( void );
  80. void undel( char * );
  81. flagType getRecord( int, int, char * );
  82. long pfile( char * );
  83. /*** main - Entry point
  84. *
  85. * Usage:
  86. *
  87. * See above
  88. *
  89. *************************************************************************/
  90. __cdecl main(c, v)
  91. int c;
  92. char *v[];
  93. {
  94. register char *p;
  95. ConvertAppToOem( c, v );
  96. SHIFT(c,v);
  97. if (!c)
  98. dump();
  99. else
  100. if (fSwitChr(**v)) {
  101. p = *v;
  102. if (!_strcmpi(++p, "help")) {
  103. int iRetCode = (int) _spawnlp(P_WAIT, "qh.exe", "qh", "/u",
  104. "undel.exe", NULL);
  105. /* for Return Code QH_TOPIC_NOT_FOUND do Usage(),
  106. * and -1 means that the spawn failed
  107. */
  108. if (iRetCode != QH_TOPIC_NOT_FOUND && iRetCode != -1)
  109. exit(0);
  110. }
  111. Usage();
  112. }
  113. while (c) {
  114. undel(*v);
  115. SHIFT(c,v);
  116. }
  117. return(0);
  118. }
  119. /*** pfile - Display the size and date of a file
  120. *
  121. * Purpose:
  122. *
  123. * This is used to generate a list of files. It displays one
  124. * line of list output.
  125. *
  126. * Input:
  127. * p - File to list
  128. *
  129. * Output:
  130. *
  131. * Returns Size of the file, or 0L if it does not exist.
  132. *
  133. *************************************************************************/
  134. long pfile(p)
  135. char *p;
  136. {
  137. struct _stat sbuf;
  138. if (_stat(p, &sbuf)) {
  139. printf("%s %s\n", p, error());
  140. return 0L;
  141. }
  142. else {
  143. char *t = ctime(&sbuf.st_mtime);
  144. // This NULLs the \n in string returned by ctime()
  145. *(t+24) = '\0';
  146. printf("%8ld %s", sbuf.st_size, t);
  147. return sbuf.st_size;
  148. }
  149. }
  150. /*** getRecord - Get one file's worth of into from the index file
  151. *
  152. * Purpose:
  153. *
  154. * A proper DELETED directory has a file called 'index' that holds a
  155. * list of the files that the directory contains. This is necessary
  156. * because the files are named DELETED.XXX. This function reads the
  157. * next file record from 'index'.
  158. *
  159. * Input:
  160. * fh - Handle of index file.
  161. * i - Number of record to _read from index file
  162. * p - Target buffer to place record.
  163. *
  164. * Output:
  165. *
  166. * Returns TRUE if record _read, FALSE otherwise.
  167. *
  168. * Notes:
  169. *
  170. * Assumes NEW format.
  171. *
  172. *************************************************************************/
  173. flagType getRecord(fh, i, p)
  174. int fh, i;
  175. char *p;
  176. {
  177. /* UNDONE: Can read the index file to a table of longs and use
  178. * UNDONE: this. Current stuff is to make it work [SB]
  179. */
  180. /* Seek to the beginning of the index file, past the header */
  181. if (_lseek(fh, (long) RM_RECLEN, SEEK_SET) == -1) {
  182. return FALSE;
  183. }
  184. /* Read (i - 1) records */
  185. if (i < 0)
  186. return TRUE;
  187. for (; i ; i--)
  188. if (!readNewIdxRec(fh, p, MAX_PATH))
  189. return FALSE;
  190. /* Read in the ith record, which is the one we need */
  191. return( (flagType)readNewIdxRec(fh, p, MAX_PATH) );
  192. }
  193. /*** undel - Do all the work for one file.
  194. *
  195. * Purpose:
  196. *
  197. * Undeletes one file.
  198. *
  199. * Input:
  200. * p - Name of file
  201. *
  202. * Output: None
  203. *
  204. *************************************************************************/
  205. void undel(p)
  206. char *p;
  207. {
  208. int fhidx, /* Index file handle */
  209. iEntry, /* Entry no in index file */
  210. iDelCount, /* No of times RMed */
  211. iDelIndex, /* deleted.xxx index value */
  212. i, j;
  213. char *buf, *idx;
  214. char *szLongName;
  215. char rec[RM_RECLEN];
  216. char *dbuf;
  217. buf = malloc(MAX_PATH);
  218. idx = malloc(MAX_PATH);
  219. dbuf = malloc(MAX_PATH);
  220. szLongName = malloc(MAX_PATH);
  221. pname(p);
  222. fileext(p, buf);
  223. upd(p, RM_DIR, idx);
  224. strcpy(szLongName, idx);
  225. strcat(idx, "\\");
  226. strcat(idx, RM_IDX);
  227. if ((fhidx = _open(idx, O_RDWR | O_BINARY)) == -1)
  228. printf("not deleted\n");
  229. else {
  230. convertIdxFile(fhidx, szLongName);
  231. /* scan and count number of instances deleted */
  232. iEntry = -1;
  233. iDelCount = 0;
  234. while (getRecord(fhidx, ++iEntry, szLongName))
  235. if (!_strcmpi(szLongName, buf)) {
  236. /* Save found entry */
  237. i = iEntry;
  238. iDelCount++;
  239. iDelIndex = (_lseek(fhidx, 0L, SEEK_CUR)
  240. - strlen(szLongName)) / RM_RECLEN;
  241. }
  242. /* none found */
  243. if (iDelCount == 0)
  244. printf("not deleted\n");
  245. else {
  246. if (iDelCount == 1)
  247. iEntry = i;
  248. /* More than one deleted */
  249. else {
  250. printf("%s More than one are deleted:\n\n", szLongName);
  251. i = iDelIndex = 0;
  252. iEntry = -1;
  253. printf("No Size Timestamp\n\n");
  254. while (getRecord(fhidx, ++iEntry, szLongName))
  255. if (!_strcmpi(szLongName, buf)) {
  256. iDelIndex = (_lseek(fhidx, 0L, SEEK_CUR)
  257. - strlen(szLongName)) / RM_RECLEN;
  258. sprintf(dbuf, "deleted.%03x", iDelIndex);
  259. upd(idx, dbuf, dbuf);
  260. printf("%2d ", ++i);
  261. pfile(dbuf);
  262. printf("\n");
  263. }
  264. while (TRUE) {
  265. printf("\nEnter number to undelete(1-%d): ", iDelCount);
  266. fgetl(szLongName, 80, stdin);
  267. i = atoi(szLongName)-1;
  268. if (i >= 0 && i < iDelCount)
  269. break;
  270. }
  271. iEntry = -1;
  272. j = 0;
  273. while (getRecord(fhidx, ++iEntry, szLongName))
  274. if (!_strcmpi(szLongName, buf))
  275. if (j++ == i)
  276. break;
  277. iDelIndex = (_lseek(fhidx, 0L, SEEK_CUR)
  278. - strlen(szLongName)) / RM_RECLEN;
  279. }
  280. /* At this stage relevant entry is (iEntry)
  281. * and this corresponds to ('deleted.%03x', iDelIndex)
  282. */
  283. getRecord(fhidx, iEntry, szLongName);
  284. _close(fhidx);
  285. fdelete(p);
  286. printf("%s\t", szLongName);
  287. fflush(stdout);
  288. sprintf(dbuf, "deleted.%03x", iDelIndex);
  289. upd(idx, dbuf, dbuf);
  290. if (rename(dbuf, p))
  291. printf(" rename failed - %s\n", error());
  292. else {
  293. printf("[OK]\n");
  294. if ((fhidx = _open(idx, O_RDWR | O_BINARY)) != -1) {
  295. long lOffPrev, /* Offset of Previous Entry */
  296. lOff; /* Offset of current entry */
  297. getRecord(fhidx, iEntry, szLongName);
  298. lOff = _lseek(fhidx, 0L, SEEK_CUR);
  299. getRecord(fhidx, iEntry - 1, szLongName);
  300. lOffPrev = _lseek(fhidx, 0L, SEEK_CUR);
  301. for (;lOffPrev != lOff; lOffPrev += RM_RECLEN) {
  302. memset((char far *)rec, 0, RM_RECLEN);
  303. writeIdxRec(fhidx, rec);
  304. }
  305. }
  306. }
  307. }
  308. _close(fhidx);
  309. }
  310. }
  311. /*** dump - display info about DELETED directory
  312. *
  313. * Purpose:
  314. *
  315. * Displays info contained in INDEX file
  316. *
  317. * Input: None
  318. *
  319. * Output: None
  320. *
  321. *************************************************************************/
  322. void dump()
  323. {
  324. int fhidx, i;
  325. char *buf = (*tools_alloc)(MAX_PATH);
  326. char *idx = (*tools_alloc)(MAX_PATH);
  327. char *szName = (*tools_alloc)(MAX_PATH);
  328. DWORD cSecsPerClus, cBytesPerSec, cFreeClus, cTotalClus;
  329. int totfiles;
  330. long totbytes, totalloc, bPerA, l;
  331. sprintf(idx, "%s\\%s", RM_DIR, RM_IDX);
  332. sprintf (szName, "%s", RM_DIR);
  333. if( !GetDiskFreeSpace( NULL, &cSecsPerClus, &cBytesPerSec, &cFreeClus, &cTotalClus ) ) {
  334. printf(" file system query failed - %s\n", error());
  335. }
  336. bPerA = cBytesPerSec * cSecsPerClus;
  337. if ((fhidx = _open(idx, O_RDWR | O_BINARY)) != -1) {
  338. convertIdxFile(fhidx, szName);
  339. totalloc = totbytes = 0L;
  340. totfiles = 0;
  341. i = 0;
  342. while (getRecord(fhidx, i++, buf))
  343. if (*buf) {
  344. if (i == 1)
  345. printf("The following have been deleted:\n\n Size Timestamp\t\t Filename\n\n");
  346. #ifdef BEAUTIFY
  347. //Or optionally
  348. printf(" size wdy mmm dd hh:mm:ss yyyy filename\n\n");
  349. #endif
  350. strcpy(szName, buf);
  351. sprintf(buf, "deleted.%03x", (_lseek(fhidx, 0L, SEEK_CUR)
  352. - strlen(buf)) / RM_RECLEN);
  353. upd(idx, buf, buf);
  354. totbytes += (l = pfile(buf));
  355. printf(" %s\n", szName);
  356. l = l + bPerA - 1;
  357. l = l / bPerA;
  358. l = l * bPerA;
  359. totalloc += l;
  360. totfiles++;
  361. }
  362. _close(fhidx);
  363. printf("\n%ld(%ld) bytes in %d deleted files\n", totalloc, totbytes, totfiles);
  364. }
  365. // Maybe the file is readonly
  366. else if (errno == EACCES) {
  367. if ((fhidx = _open(idx, O_RDONLY | O_BINARY)) != -1) {
  368. // Cannot convert Index file for this case
  369. totalloc = totbytes = 0L;
  370. totfiles = 0;
  371. i = 0;
  372. while (getRecord(fhidx, i++, buf))
  373. if (*buf) {
  374. if (i == 1)
  375. printf("The following have been deleted:\n\n Size Timestamp\t\t Filename\n\n");
  376. #ifdef BEAUTIFY
  377. //Or optionally
  378. printf(" size wdy mmm dd hh:mm:ss yyyy filename\n\n");
  379. #endif
  380. strcpy(szName, buf);
  381. sprintf(buf, "deleted.%03x", (_lseek(fhidx, 0L, SEEK_CUR)
  382. - strlen(buf)) / RM_RECLEN);
  383. upd(idx, buf, buf);
  384. totbytes += (l = pfile(buf));
  385. printf(" %s\n", szName);
  386. l = l + bPerA - 1;
  387. l = l / bPerA;
  388. l = l * bPerA;
  389. totalloc += l;
  390. totfiles++;
  391. }
  392. _close(fhidx);
  393. printf("\n%ld(%ld) bytes in %d deleted files\n", totalloc, totbytes, totfiles);
  394. }
  395. }
  396. free(buf);
  397. free(idx);
  398. free(szName);
  399. }
  400. /*** Usage - standard usage function; help user
  401. *
  402. * Purpose:
  403. *
  404. * The usual.
  405. *
  406. *************************************************************************/
  407. void Usage()
  408. {
  409. printf(
  410. "Microsoft File Undelete Utility. Version %s\n"
  411. "Copyright (C) Microsoft Corp %s. All rights reserved.\n\n"
  412. "Usage: UNDEL [/help] [files]\n",
  413. VERSION(rmj, rmm, rup), CopyRightYrs);
  414. exit(1);
  415. }