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.

794 lines
19 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. Module Name:
  4. lfn.c
  5. Abstract:
  6. Author:
  7. Ted Miller (tedm) Apr 1996
  8. --*/
  9. #include "lfn.h"
  10. #pragma hdrstop
  11. //
  12. // Define result codes.
  13. //
  14. #define SUCCESS 0
  15. #define FAILURE 1
  16. //
  17. // Define routine type for callbacks from EnumerateDrives()
  18. //
  19. typedef
  20. BOOLEAN
  21. (*PDRIVEENUM_ROUTINE) (
  22. IN PCWSTR DriveRootPath
  23. );
  24. BOOLEAN
  25. EnumerateDrives(
  26. IN PDRIVEENUM_ROUTINE Callback
  27. );
  28. BOOLEAN
  29. RestoreLfns(
  30. IN PCWSTR DriveRootPath
  31. );
  32. BOOLEAN
  33. Message(
  34. IN ULONG MessageId,
  35. IN ULONG DotCount,
  36. ...
  37. );
  38. BOOLEAN
  39. RenameToLfn(
  40. IN PCWSTR Directory,
  41. IN PCWSTR ExistingFilename,
  42. IN PCWSTR NewFilename
  43. );
  44. VOID
  45. DeleteRenameFile(
  46. IN PCWSTR DriveRootPath
  47. );
  48. VOID
  49. RemoveFromBootExecute(
  50. IN PCWSTR Cmd
  51. );
  52. int
  53. __cdecl
  54. main(
  55. IN int argc,
  56. IN char *argv[]
  57. )
  58. /*++
  59. Routine Description:
  60. Entry point for this program.
  61. The $$RENAME.TXT at the root of each drive is read and
  62. rename operations contained therein are performed.
  63. Arguments:
  64. Ignored.
  65. Return Value:
  66. None.
  67. --*/
  68. {
  69. int i;
  70. i = EnumerateDrives(RestoreLfns) ? SUCCESS : FAILURE;
  71. RemoveFromBootExecute(L"autolfn");
  72. return(i);
  73. }
  74. BOOLEAN
  75. EnumerateDrives(
  76. IN PDRIVEENUM_ROUTINE Callback
  77. )
  78. {
  79. UNICODE_STRING UnicodeString;
  80. OBJECT_ATTRIBUTES ObjectAttributes;
  81. HANDLE DosDevicesDir;
  82. CHAR DirInfoBuffer[512];
  83. CHAR LinkTargetBuffer[512];
  84. UNICODE_STRING LinkTarget;
  85. UNICODE_STRING DesiredPrefix;
  86. UNICODE_STRING DesiredPrefix2;
  87. POBJECT_DIRECTORY_INFORMATION DirInfo;
  88. ULONG Context,Length;
  89. UNICODE_STRING LinkTypeName;
  90. NTSTATUS Status;
  91. HANDLE Handle;
  92. BOOLEAN b;
  93. //
  94. // Open \DosDevices
  95. //
  96. RtlInitUnicodeString(&UnicodeString,L"\\DosDevices");
  97. InitializeObjectAttributes(
  98. &ObjectAttributes,
  99. &UnicodeString,
  100. OBJ_CASE_INSENSITIVE | OBJ_OPENIF | OBJ_PERMANENT,
  101. NULL,
  102. NULL
  103. );
  104. Status = NtOpenDirectoryObject(&DosDevicesDir,DIRECTORY_QUERY,&ObjectAttributes);
  105. if(!NT_SUCCESS(Status)) {
  106. return(FALSE);
  107. }
  108. LinkTarget.Buffer = (PVOID)LinkTargetBuffer;
  109. RtlInitUnicodeString(&LinkTypeName,L"SymbolicLink");
  110. RtlInitUnicodeString(&DesiredPrefix,L"\\Device\\Harddisk");
  111. RtlInitUnicodeString(&DesiredPrefix2,L"\\Device\\Volume");
  112. DirInfo = (POBJECT_DIRECTORY_INFORMATION)DirInfoBuffer;
  113. b = TRUE;
  114. //
  115. // Query first object in \DosDevices directory
  116. //
  117. Status = NtQueryDirectoryObject(
  118. DosDevicesDir,
  119. DirInfo,
  120. sizeof(DirInfoBuffer),
  121. TRUE,
  122. TRUE,
  123. &Context,
  124. &Length
  125. );
  126. while(NT_SUCCESS(Status)) {
  127. //
  128. // Make sure item is a symbolic link
  129. //
  130. if(RtlEqualUnicodeString(&LinkTypeName,&DirInfo->TypeName,TRUE)) {
  131. InitializeObjectAttributes(
  132. &ObjectAttributes,
  133. &DirInfo->Name,
  134. OBJ_CASE_INSENSITIVE,
  135. DosDevicesDir,
  136. NULL
  137. );
  138. Status = NtOpenSymbolicLinkObject(&Handle,SYMBOLIC_LINK_ALL_ACCESS,&ObjectAttributes);
  139. if(NT_SUCCESS(Status)) {
  140. LinkTarget.Length = 0;
  141. LinkTarget.MaximumLength = sizeof(LinkTargetBuffer);
  142. Status = NtQuerySymbolicLinkObject(Handle,&LinkTarget,NULL);
  143. NtClose(Handle);
  144. if(NT_SUCCESS(Status)
  145. && (RtlPrefixUnicodeString(&DesiredPrefix,&LinkTarget,TRUE) ||
  146. RtlPrefixUnicodeString(&DesiredPrefix2,&LinkTarget,TRUE))) {
  147. //
  148. // OK, this is a symbolic link to a hard drive.
  149. // Make sure it's 0-terminated and call the callback
  150. // the the name.
  151. //
  152. LinkTarget.Buffer[LinkTarget.Length/sizeof(WCHAR)] = 0;
  153. if(!Callback(LinkTarget.Buffer)) {
  154. b = FALSE;
  155. }
  156. }
  157. }
  158. }
  159. //
  160. // Query next object in \DosDevices directory
  161. //
  162. Status = NtQueryDirectoryObject(
  163. DosDevicesDir,
  164. DirInfo,
  165. sizeof(DirInfoBuffer),
  166. TRUE,
  167. FALSE,
  168. &Context,
  169. &Length
  170. );
  171. }
  172. NtClose(DosDevicesDir);
  173. return(b);
  174. }
  175. BOOLEAN
  176. RestoreLfns(
  177. IN PCWSTR DriveRootPath
  178. )
  179. {
  180. PMYTEXTFILE TextFile;
  181. BOOLEAN b;
  182. WCHAR Directory[NTMAXPATH];
  183. WCHAR Line[3*NTMAXPATH];
  184. ULONG d;
  185. PWCHAR p,n;
  186. PWSTR Sfn,Lfn;
  187. b = FALSE;
  188. //
  189. // Load the rename file list.
  190. //
  191. if(TextFile = LoadRenameFile(DriveRootPath)) {
  192. //
  193. // Process each directory that is in the rename list file.
  194. //
  195. for(d=0; d<TextFile->SectionCount; d++) {
  196. //
  197. // Form the directory path.
  198. //
  199. wcscpy(Directory,DriveRootPath);
  200. ConcatenatePaths(Directory,TextFile->Sections[d].Name,NTMAXPATH);
  201. //
  202. // Process each line in the section.
  203. //
  204. p = TextFile->Sections[d].Data;
  205. while(GetLineInSection(p,Line,sizeof(Line)/sizeof(WCHAR),&n)) {
  206. if(ParseLine(Line,&Sfn,&Lfn)) {
  207. RenameToLfn(Directory,Sfn,Lfn);
  208. }
  209. p = n;
  210. }
  211. }
  212. UnloadRenameFile(&TextFile);
  213. //
  214. // Upon success, delete the rename file.
  215. //
  216. DeleteRenameFile(DriveRootPath);
  217. b = TRUE;
  218. }
  219. return(b);
  220. }
  221. BOOLEAN
  222. Message(
  223. IN ULONG MessageId,
  224. IN ULONG DotCount,
  225. ...
  226. )
  227. /*++
  228. Routine Description:
  229. Format and display a message, which is retreived from
  230. the image's message resources.
  231. Arguments:
  232. MessageId - Supplies the message id of the message resource.
  233. DotCount - Supplies number of trailing dots to be appended to
  234. the message text prior to display. If this value is non-0,
  235. then the message shouldn't have a trailing cr/lf!
  236. Additional arguments specify message-specific inserts.
  237. Return Value:
  238. Boolean value indicating whether the message was displayed.
  239. --*/
  240. {
  241. PVOID ImageBase;
  242. NTSTATUS Status;
  243. PMESSAGE_RESOURCE_ENTRY MessageEntry;
  244. ANSI_STRING AnsiString;
  245. UNICODE_STRING UnicodeString;
  246. va_list arglist;
  247. WCHAR Buffer[1024];
  248. ULONG u;
  249. //
  250. // Get our image base address
  251. //
  252. ImageBase = NtCurrentPeb()->ImageBaseAddress;
  253. if(!ImageBase) {
  254. return(FALSE);
  255. }
  256. //
  257. // Find the message.
  258. // For DBCS codepages we will use English resources instead of
  259. // default resource because we can only display ASCII characters onto
  260. // blue Screen via HalDisplayString()
  261. //
  262. Status = RtlFindMessage(
  263. ImageBase,
  264. 11,
  265. NLS_MB_CODE_PAGE_TAG ? MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US) : 0,
  266. MessageId,
  267. &MessageEntry
  268. );
  269. if(!NT_SUCCESS(Status)) {
  270. return(FALSE);
  271. }
  272. //
  273. // If the message is not unicode, convert to unicode.
  274. // Let the conversion routine allocate the buffer.
  275. //
  276. if(!(MessageEntry->Flags & MESSAGE_RESOURCE_UNICODE)) {
  277. RtlInitAnsiString(&AnsiString,MessageEntry->Text);
  278. Status = RtlAnsiStringToUnicodeString(&UnicodeString,&AnsiString,TRUE);
  279. if(!NT_SUCCESS(Status)) {
  280. return(FALSE);
  281. }
  282. } else {
  283. //
  284. // Message is already unicode. Make a copy.
  285. //
  286. if(!RtlCreateUnicodeString(&UnicodeString,(PWSTR)MessageEntry->Text)) {
  287. return(FALSE);
  288. }
  289. }
  290. //
  291. // Format the message.
  292. //
  293. va_start(arglist,DotCount);
  294. Status = RtlFormatMessage(
  295. UnicodeString.Buffer,
  296. 0, // max width
  297. FALSE, // don't ignore inserts
  298. FALSE, // args are not ansi
  299. FALSE, // args are not an array
  300. &arglist,
  301. Buffer,
  302. sizeof(Buffer)/sizeof(Buffer[0]),
  303. NULL
  304. );
  305. va_end(arglist);
  306. //
  307. // We don't need the message source any more. Free it.
  308. //
  309. RtlFreeUnicodeString(&UnicodeString);
  310. //
  311. // Add dots and cr.
  312. //
  313. for(u=0; u<DotCount; u++) {
  314. wcscat(Buffer,L".");
  315. }
  316. wcscat(Buffer,L"\r");
  317. //
  318. // Print out the message
  319. //
  320. RtlInitUnicodeString(&UnicodeString,Buffer);
  321. Status = NtDisplayString(&UnicodeString);
  322. return(NT_SUCCESS(Status));
  323. }
  324. VOID
  325. ConcatenatePaths(
  326. IN OUT PWSTR Target,
  327. IN PCWSTR Path,
  328. IN ULONG TargetBufferSize
  329. )
  330. /*++
  331. Routine Description:
  332. Concatenate 2 paths, ensuring that one, and only one,
  333. path separator character is introduced at the junction point.
  334. Arguments:
  335. Target - supplies first part of path. Path is appended to this.
  336. Path - supplies path to be concatenated to Target.
  337. TargetBufferSize - supplies the size of the Target buffer,
  338. in characters.
  339. // ISSUE-2002/03/06-robertko This function returns VOID. Should probably be changed to reflect the
  340. // comment below since the funtion truncates to prevent buffer overflow.
  341. Return Value:
  342. TRUE if the full path fit in Target buffer. Otherwise the path
  343. will have been truncated.
  344. --*/
  345. {
  346. ULONG TargetLength,PathLength;
  347. BOOL TrailingBackslash,LeadingBackslash;
  348. ULONG EndingLength;
  349. ULONG n;
  350. TargetLength = wcslen(Target);
  351. PathLength = wcslen(Path);
  352. //
  353. // See whether the target has a trailing backslash.
  354. //
  355. if(TargetLength && (Target[TargetLength-1] == L'\\')) {
  356. TrailingBackslash = TRUE;
  357. TargetLength--;
  358. } else {
  359. TrailingBackslash = FALSE;
  360. }
  361. //
  362. // See whether the path has a leading backshash.
  363. //
  364. if(Path[0] == L'\\') {
  365. LeadingBackslash = TRUE;
  366. PathLength--;
  367. } else {
  368. LeadingBackslash = FALSE;
  369. }
  370. //
  371. // Calculate the ending length, which is equal to the sum of
  372. // the length of the two strings modulo leading/trailing
  373. // backslashes, plus one path separator, plus a nul.
  374. //
  375. EndingLength = TargetLength + PathLength + 2;
  376. if(!LeadingBackslash && (TargetLength < TargetBufferSize)) {
  377. Target[TargetLength++] = L'\\';
  378. }
  379. if(TargetBufferSize > TargetLength) {
  380. n = wcslen(Path);
  381. if(n > TargetBufferSize-TargetLength) {
  382. n = TargetBufferSize-TargetLength;
  383. }
  384. RtlCopyMemory(Target+TargetLength,Path,n*sizeof(WCHAR));
  385. Target[TargetLength+n] = 0;
  386. }
  387. //
  388. // Make sure the buffer is nul terminated in all cases.
  389. //
  390. if(TargetBufferSize) {
  391. Target[TargetBufferSize-1] = 0;
  392. }
  393. }
  394. BOOLEAN
  395. RenameToLfn(
  396. IN PCWSTR Directory,
  397. IN PCWSTR ExistingFilename,
  398. IN PCWSTR NewFilename
  399. )
  400. {
  401. WCHAR Buffer[2*NTMAXPATH] = {0};
  402. UNICODE_STRING UnicodeString;
  403. IO_STATUS_BLOCK IoStatusBlock;
  404. OBJECT_ATTRIBUTES ObjectAttributes;
  405. NTSTATUS Status;
  406. HANDLE Handle;
  407. PFILE_RENAME_INFORMATION RenameInfo;
  408. //
  409. // Open the existing file for delete access.
  410. //
  411. wcsncpy(Buffer,Directory,sizeof(Buffer)/sizeof(Buffer[0]) - 1);
  412. ConcatenatePaths(Buffer,ExistingFilename,sizeof(Buffer)/sizeof(WCHAR));
  413. INIT_OBJA(&ObjectAttributes,&UnicodeString,Buffer);
  414. Status = NtOpenFile(
  415. &Handle,
  416. DELETE | SYNCHRONIZE,
  417. &ObjectAttributes,
  418. &IoStatusBlock,
  419. FILE_SHARE_READ | FILE_SHARE_WRITE,
  420. FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT
  421. );
  422. if(!NT_SUCCESS(Status)) {
  423. KdPrint(("LFN: Unable to open %ws for renaming (%x)\n",Buffer,Status));
  424. return(FALSE);
  425. }
  426. //
  427. // Rename to temporary intermediate file. This allows the filesystem to
  428. // generate a short filename later, that doesn't collide with the name
  429. // currently in use.
  430. //
  431. RenameInfo = (PFILE_RENAME_INFORMATION)Buffer;
  432. wcscpy(RenameInfo->FileName,Directory);
  433. ConcatenatePaths(RenameInfo->FileName,L"$$!!$$!!.~~~",NTMAXPATH);
  434. RenameInfo->ReplaceIfExists = TRUE;
  435. RenameInfo->RootDirectory = NULL;
  436. RenameInfo->FileNameLength = wcslen(RenameInfo->FileName)*sizeof(WCHAR);
  437. Status = NtSetInformationFile(
  438. Handle,
  439. &IoStatusBlock,
  440. RenameInfo,
  441. sizeof(FILE_RENAME_INFORMATION) + RenameInfo->FileNameLength,
  442. FileRenameInformation
  443. );
  444. if(!NT_SUCCESS(Status)) {
  445. KdPrint(("LFN: Rename of %ws to intermediate temp filename failed (%x)\n",ExistingFilename,Status));
  446. NtClose(Handle);
  447. return(FALSE);
  448. }
  449. //
  450. // Rename to actual target file.
  451. //
  452. wcscpy(RenameInfo->FileName,Directory);
  453. ConcatenatePaths(RenameInfo->FileName,NewFilename,NTMAXPATH);
  454. RenameInfo->ReplaceIfExists = FALSE;
  455. RenameInfo->RootDirectory = NULL;
  456. RenameInfo->FileNameLength = wcslen(RenameInfo->FileName)*sizeof(WCHAR);
  457. Status = NtSetInformationFile(
  458. Handle,
  459. &IoStatusBlock,
  460. RenameInfo,
  461. sizeof(FILE_RENAME_INFORMATION) + RenameInfo->FileNameLength,
  462. FileRenameInformation
  463. );
  464. if(!NT_SUCCESS(Status)) {
  465. KdPrint(("LFN: Rename of file to %ws failed (%x)\n",NewFilename,Status));
  466. NtClose(Handle);
  467. return(FALSE);
  468. }
  469. NtClose(Handle);
  470. return(TRUE);
  471. }
  472. VOID
  473. DeleteRenameFile(
  474. IN PCWSTR DriveRootPath
  475. )
  476. {
  477. WCHAR Filename[NTMAXPATH] = {0};
  478. UNICODE_STRING UnicodeString;
  479. OBJECT_ATTRIBUTES ObjectAttributes;
  480. IO_STATUS_BLOCK IoStatusBlock;
  481. HANDLE Handle;
  482. NTSTATUS Status;
  483. FILE_DISPOSITION_INFORMATION DispositionInfo;
  484. wcsncpy(Filename,DriveRootPath,sizeof(Filename)/sizeof(Filename[0]) - 1);
  485. ConcatenatePaths(Filename,WINNT_OEM_LFNLIST_W,NTMAXPATH);
  486. INIT_OBJA(&ObjectAttributes,&UnicodeString,Filename);
  487. Status = NtOpenFile(
  488. &Handle,
  489. DELETE,
  490. &ObjectAttributes,
  491. &IoStatusBlock,
  492. FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
  493. FILE_NON_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT
  494. );
  495. if(NT_SUCCESS(Status)) {
  496. DispositionInfo.DeleteFile = TRUE;
  497. Status = NtSetInformationFile(
  498. Handle,
  499. &IoStatusBlock,
  500. &DispositionInfo,
  501. sizeof(DispositionInfo),
  502. FileDispositionInformation
  503. );
  504. if(!NT_SUCCESS(Status)) {
  505. KdPrint(("LFN: Unable to delete %ws (%x)\n",Filename,Status));
  506. }
  507. NtClose(Handle);
  508. } else {
  509. KdPrint(("LFN: Unable to open %ws for delete (%x)\n",Filename,Status));
  510. return;
  511. }
  512. }
  513. VOID
  514. RemoveFromBootExecute(
  515. IN PCWSTR Cmd
  516. )
  517. {
  518. OBJECT_ATTRIBUTES ObjectAttributes;
  519. UNICODE_STRING UnicodeString;
  520. NTSTATUS Status;
  521. HANDLE hKey;
  522. PKEY_VALUE_PARTIAL_INFORMATION ValueInfo;
  523. ULONG Length;
  524. PWCHAR NewValue;
  525. PWSTR SourceString,TargetString;
  526. //
  527. // Open the registry key we want.
  528. // [\registry\machine\system\CurrentControlSet\control\Session Manager]
  529. //
  530. INIT_OBJA(
  531. &ObjectAttributes,
  532. &UnicodeString,
  533. L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Session Manager"
  534. );
  535. Status = NtOpenKey(
  536. &hKey,
  537. KEY_QUERY_VALUE | KEY_SET_VALUE,
  538. &ObjectAttributes
  539. );
  540. if(!NT_SUCCESS(Status)) {
  541. KdPrint(("LFN: Unable to open session manager key (%x)\n",Status));
  542. goto c0;
  543. }
  544. //
  545. // Query the current value of the BootExecute value.
  546. //
  547. RtlInitUnicodeString(&UnicodeString,L"BootExecute");
  548. Status = NtQueryValueKey(
  549. hKey,
  550. &UnicodeString,
  551. KeyValuePartialInformation,
  552. NULL,
  553. 0,
  554. &Length
  555. );
  556. if(Status != STATUS_BUFFER_TOO_SMALL) {
  557. KdPrint(("LFN: Unable to query BootExecute value size (%x)\n",Status));
  558. goto c1;
  559. }
  560. ValueInfo = RtlAllocateHeap(RtlProcessHeap(),0,Length);
  561. if(!ValueInfo) {
  562. KdPrint(("LFN: Unable to allocate %u bytes of memory\n",Length));
  563. goto c1;
  564. }
  565. Status = NtQueryValueKey(
  566. hKey,
  567. &UnicodeString,
  568. KeyValuePartialInformation,
  569. ValueInfo,
  570. Length,
  571. &Length
  572. );
  573. if(!NT_SUCCESS(Status)) {
  574. KdPrint(("LFN: Unable to allocate %u bytes of memory\n",Length));
  575. goto c2;
  576. }
  577. //
  578. // Check data type.
  579. //
  580. if(ValueInfo->Type != REG_MULTI_SZ) {
  581. KdPrint(("LFN: BootExecute is wrong data type (%u)\n",ValueInfo->Type));
  582. }
  583. //
  584. // Allocate space for a new multi_sz we will build up.
  585. //
  586. NewValue = RtlAllocateHeap(RtlProcessHeap(),0,ValueInfo->DataLength);
  587. if(!NewValue) {
  588. KdPrint(("LFN: Unable to allocate %u bytes of memory\n",ValueInfo->DataLength));
  589. goto c2;
  590. }
  591. //
  592. // Process each string in the multi_sz. Copy to the new value
  593. // we're building up; filter out any strings containing the given Cmd.
  594. //
  595. for(SourceString=(PWSTR)ValueInfo->Data,TargetString=NewValue;
  596. *SourceString;
  597. SourceString+=Length) {
  598. Length = wcslen(SourceString)+1;
  599. wcscpy(TargetString,SourceString);
  600. _wcslwr(TargetString);
  601. if(!wcsstr(TargetString,Cmd)) {
  602. //
  603. // Don't want to filter this string out of the multi_sz
  604. // we're building up. Recopy from source to preserve case
  605. // and advance the target string pointer.
  606. //
  607. wcscpy(TargetString,SourceString);
  608. TargetString += Length;
  609. }
  610. }
  611. //
  612. // Extra nul-terminator for multi_sz termination.
  613. //
  614. *TargetString++ = 0;
  615. //
  616. // Write the new value out into the registry.
  617. //
  618. Status = NtSetValueKey(
  619. hKey,
  620. &UnicodeString,
  621. 0,
  622. REG_MULTI_SZ,
  623. NewValue,
  624. (ULONG)((TargetString-NewValue)*sizeof(WCHAR))
  625. );
  626. if(!NT_SUCCESS(Status)) {
  627. KdPrint(("LFN: Unable to set BootExecute value (%x)\n",Status));
  628. }
  629. RtlFreeHeap(RtlProcessHeap(),0,NewValue);
  630. c2:
  631. RtlFreeHeap(RtlProcessHeap(),0,ValueInfo);
  632. c1:
  633. NtClose(hKey);
  634. c0:
  635. return;
  636. }