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.

5579 lines
152 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. Module Name:
  4. file.c
  5. Abstract:
  6. General file-related functions.
  7. Author:
  8. Mike Condra 16-Aug-1996
  9. Revision History:
  10. calinn 23-Sep-1998 Additional options to file enum
  11. jimschm 05-Feb-1998 File enumeration code
  12. jimschm 30-Sep-1997 WriteFileString routines
  13. --*/
  14. #include "pch.h"
  15. #include "migutilp.h"
  16. BOOL
  17. IsPathLengthOkA (
  18. IN PCSTR FileSpec
  19. )
  20. /*++
  21. Routine Description:
  22. IsPathLengthOkA checks the inbound string to make sure it fits within
  23. MAX_MBCHAR_PATH. This includes the nul terminator.
  24. Arguments:
  25. FileSpec - Specifies the C string to test for appropriate length
  26. Return Value:
  27. TRUE if FileSpec fits completely within MAX_MBCHAR_PATH
  28. FALSE otherwise
  29. --*/
  30. {
  31. PCSTR maxEnd;
  32. if (!FileSpec) {
  33. return FALSE;
  34. }
  35. maxEnd = FileSpec + MAX_MBCHAR_PATH - 1;
  36. while (*FileSpec) {
  37. if (FileSpec == maxEnd) {
  38. return FALSE;
  39. }
  40. FileSpec++;
  41. }
  42. return TRUE;
  43. }
  44. BOOL
  45. IsPathLengthOkW (
  46. IN PCWSTR FileSpec
  47. )
  48. /*++
  49. Routine Description:
  50. IsPathLengthOkW checks the inbound string to make sure it fits within
  51. MAX_WCHAR_PATH. This includes the nul terminator.
  52. Arguments:
  53. FileSpec - Specifies the C string to test for appropriate length
  54. Return Value:
  55. TRUE if FileSpec fits completely within MAX_MBCHAR_PATH
  56. FALSE otherwise
  57. --*/
  58. {
  59. PCWSTR maxEnd;
  60. if (!FileSpec) {
  61. return FALSE;
  62. }
  63. maxEnd = FileSpec + MAX_WCHAR_PATH - 1;
  64. while (*FileSpec) {
  65. if (FileSpec == maxEnd) {
  66. return FALSE;
  67. }
  68. FileSpec++;
  69. }
  70. return TRUE;
  71. }
  72. BOOL
  73. IsPathOnFixedDriveA (
  74. IN PCSTR FileSpec OPTIONAL
  75. )
  76. /*++
  77. Routine Description:
  78. IsPathOnFixedDriveA checks the first three characters of the specified file
  79. path. If the first three characters are in the form of C:\, and the first
  80. letter refers to a local fixed disk, then the path is on a fixed
  81. drive.
  82. This function does not validate the rest of the path.
  83. CAUTION: This function has an optimization to cache the last drive letter
  84. test, and the optimization is not designed to be thread-safe.
  85. Arguments:
  86. FileSpec - Specifies the file string to test
  87. Return Value:
  88. TRUE if FileSpec starts with a valid local fixed drive specification
  89. FALSE otherwise
  90. --*/
  91. {
  92. static CHAR root[] = "?:\\";
  93. UINT type;
  94. static BOOL lastResult;
  95. if (!FileSpec) {
  96. return FALSE;
  97. }
  98. if (!FileSpec[0]) {
  99. return FALSE;
  100. }
  101. if (FileSpec[1] != ':' || FileSpec[2] != '\\') {
  102. return FALSE;
  103. }
  104. if (root[0] == FileSpec[0]) {
  105. return lastResult;
  106. }
  107. root[0] = FileSpec[0];
  108. type = GetDriveTypeA (root);
  109. if (type != DRIVE_FIXED && type != DRIVE_REMOTE) {
  110. DEBUGMSGA_IF ((
  111. type != DRIVE_REMOVABLE && type != DRIVE_NO_ROOT_DIR,
  112. DBG_VERBOSE,
  113. "Path %s is on unexpected drive type %u",
  114. FileSpec,
  115. type
  116. ));
  117. lastResult = FALSE;
  118. } else {
  119. lastResult = TRUE;
  120. }
  121. return lastResult;
  122. }
  123. BOOL
  124. IsPathOnFixedDriveW (
  125. IN PCWSTR FileSpec OPTIONAL
  126. )
  127. /*++
  128. Routine Description:
  129. IsPathOnFixedDriveW checks the first three characters of the specified file
  130. path. If the first three characters are in the form of C:\, and the first
  131. letter refers to a local fixed disk, then the path is on a fixed
  132. drive.
  133. This function does not validate the rest of the path.
  134. This function is intended for use only on Windows NT. To test for a drive
  135. letter on Win9x, use IsPathOnFixedDriveA.
  136. This function supports NT's extended path syntax, \\?\drive:\path,
  137. as in \\?\c:\foo.
  138. CAUTION: This function has an optimization to cache the last drive letter
  139. test, and the optimization is not designed to be thread-safe.
  140. Arguments:
  141. FileSpec - Specifies the file string to test
  142. Return Value:
  143. TRUE if FileSpec starts with a valid local fixed drive specification
  144. FALSE otherwise
  145. --*/
  146. {
  147. static WCHAR root[] = L"?:\\";
  148. UINT type;
  149. static BOOL lastResult;
  150. PCWSTR p;
  151. if (!FileSpec) {
  152. return FALSE;
  153. }
  154. p = FileSpec;
  155. if (p[0] == L'\\' && p[1] == L'\\' && p[2] == L'?' && p[3] == L'\\') {
  156. p += 4;
  157. }
  158. MYASSERT (ISNT());
  159. if (!p[0]) {
  160. return FALSE;
  161. }
  162. if (p[1] != L':' || p[2] != L'\\') {
  163. return FALSE;
  164. }
  165. if (root[0] == p[0]) {
  166. return lastResult;
  167. }
  168. root[0] = p[0];
  169. type = GetDriveTypeW (root);
  170. if (type != DRIVE_FIXED && type != DRIVE_REMOTE) {
  171. DEBUGMSGW_IF ((
  172. type != DRIVE_REMOVABLE && type != DRIVE_NO_ROOT_DIR,
  173. DBG_VERBOSE,
  174. "Path %s is on unexpected drive type %u",
  175. FileSpec,
  176. type
  177. ));
  178. lastResult = FALSE;
  179. } else {
  180. lastResult = TRUE;
  181. }
  182. return lastResult;
  183. }
  184. BOOL
  185. CopyFileSpecToLongA (
  186. IN PCSTR FullFileSpecIn,
  187. OUT PSTR OutPath // MAX_MBCHAR_PATH buffer
  188. )
  189. /*++
  190. Routine Description:
  191. CopyFileSpecToLongA takes a file specification, either in short or long
  192. format, and copies the long format into the caller's destination
  193. buffer.
  194. This routine generally assumes the caller has restricted the path length to
  195. fit in a buffer of MAX_PATH.
  196. CAUTION:
  197. - If the initial file spec won't fit within MAX_PATH, then it will be copied
  198. into the destination, but will also be truncated.
  199. - If the long format of FullFileSpec won't fit within MAX_PATH, then
  200. FullFileSpecIn is copied into the destination unchanged.
  201. - MAX_PATH is actually smaller than MAX_MBCHAR_PATH. This function assumes
  202. the destination buffer is only MAX_PATH chars.
  203. Arguments:
  204. FullFileSpecIn - Specifies the inbound file spec.
  205. OutPath - Receives the long path, original path or truncated original path.
  206. The function attempts to fit long path in, then falls back to
  207. original path, and then falls back to truncated original path.
  208. Return Value:
  209. TRUE if the long path was transferred into the destination without any
  210. issues. In this case, multiple backslashes are converted to one (e.g.,
  211. c:\\foo becomes c:\foo).
  212. FALSE if the original path was transferred into the destination; the
  213. original path might get truncated
  214. --*/
  215. {
  216. CHAR fullFileSpec[MAX_MBCHAR_PATH];
  217. WIN32_FIND_DATAA findData;
  218. HANDLE findHandle;
  219. PSTR end;
  220. PSTR currentIn;
  221. PSTR currentOut;
  222. PSTR outEnd;
  223. PSTR maxOutPath = OutPath + MAX_PATH - 1;
  224. BOOL result = FALSE;
  225. UINT oldMode;
  226. BOOL forceCopy = FALSE;
  227. oldMode = SetErrorMode (SEM_FAILCRITICALERRORS);
  228. __try {
  229. //
  230. // Limit source length for temp copy below
  231. //
  232. if (!IsPathLengthOkA (FullFileSpecIn)) {
  233. DEBUGMSGA ((DBG_ERROR, "Inbound file spec is too long: %s", FullFileSpecIn));
  234. __leave;
  235. }
  236. //
  237. // If path is on removable media, don't touch the disk
  238. //
  239. if (!IsPathOnFixedDriveA (FullFileSpecIn)) {
  240. forceCopy = TRUE;
  241. __leave;
  242. }
  243. //
  244. // Make a copy of the file spec so we can truncate it at the wacks
  245. //
  246. StackStringCopyA (fullFileSpec, FullFileSpecIn);
  247. //
  248. // Begin building the path
  249. //
  250. OutPath[0] = fullFileSpec[0];
  251. OutPath[1] = fullFileSpec[1];
  252. OutPath[2] = fullFileSpec[2];
  253. OutPath[3] = 0;
  254. MYASSERT (OutPath[0] && OutPath[1] && OutPath[2]);
  255. //
  256. // IsPathOnFixedDrive makes the following addition of 3 OK
  257. //
  258. currentIn = fullFileSpec + 3;
  259. currentOut = OutPath + 3;
  260. //
  261. // Loop for each segment of the path
  262. //
  263. for (;;) {
  264. end = _mbschr (currentIn, '\\');
  265. if (end == (currentIn + 1)) {
  266. //
  267. // Treat multiple backslashes as one
  268. //
  269. currentIn++;
  270. continue;
  271. }
  272. if (end) {
  273. *end = 0;
  274. }
  275. findHandle = FindFirstFileA (fullFileSpec, &findData);
  276. if (findHandle != INVALID_HANDLE_VALUE) {
  277. FindClose (findHandle);
  278. //
  279. // Copy long file name obtained from FindFirstFile
  280. //
  281. outEnd = currentOut + TcharCountA (findData.cFileName);
  282. if (outEnd > maxOutPath) {
  283. #ifdef DEBUG
  284. *currentOut = 0;
  285. DEBUGMSGA ((
  286. DBG_WARNING,
  287. "Path %s too long to append to %s",
  288. findData.cFileName,
  289. OutPath
  290. ));
  291. #endif
  292. __leave;
  293. }
  294. //
  295. // StringCopy is not bound because length is restricted above
  296. //
  297. StringCopyA (currentOut, findData.cFileName);
  298. currentOut = outEnd;
  299. } else {
  300. //
  301. // Copy the rest of the path since it doesn't exist
  302. //
  303. if (end) {
  304. *end = '\\';
  305. }
  306. outEnd = currentOut + TcharCountA (currentIn);
  307. if (outEnd > maxOutPath) {
  308. #ifdef DEBUG
  309. DEBUGMSGA ((
  310. DBG_WARNING,
  311. "Path %s too long to append to %s",
  312. currentIn,
  313. OutPath
  314. ));
  315. #endif
  316. __leave;
  317. }
  318. //
  319. // StringCopy is not bound because length is restricted above
  320. //
  321. StringCopyA (currentOut, currentIn);
  322. break; // done with path
  323. }
  324. if (!end) {
  325. MYASSERT (*currentOut == 0);
  326. break; // done with path
  327. }
  328. //
  329. // Ensure wack fits in destination buffer
  330. //
  331. if (currentOut + 1 > maxOutPath) {
  332. DEBUGMSGW ((
  333. DBG_WARNING,
  334. "Append wack exceeds MAX_PATH for %s",
  335. OutPath
  336. ));
  337. __leave;
  338. }
  339. //
  340. // Add wack and advance pointers
  341. //
  342. *currentOut++ = '\\';
  343. *currentOut = 0;
  344. *end = '\\'; // restore cut point
  345. currentIn = end + 1;
  346. }
  347. result = TRUE;
  348. }
  349. __finally {
  350. SetErrorMode (oldMode);
  351. }
  352. if (!result) {
  353. StringCopyTcharCountA (OutPath, FullFileSpecIn, MAX_PATH);
  354. }
  355. MYASSERT (IsPathLengthOkA (OutPath));
  356. return result || forceCopy;
  357. }
  358. BOOL
  359. CopyFileSpecToLongW (
  360. IN PCWSTR FullFileSpecIn,
  361. OUT PWSTR OutPath // MAX_WCHAR_PATH buffer
  362. )
  363. /*++
  364. Routine Description:
  365. CopyFileSpecToLongW takes a file specification, either in short or long
  366. format, and copies the long format into the caller's destination
  367. buffer.
  368. This routine generally assumes the caller has restricted the path length to
  369. fit in a buffer of MAX_PATH.
  370. CAUTION:
  371. - If the initial file spec won't fit within MAX_PATH, then it will be copied
  372. into the destination, but will also be truncated.
  373. - If the long format of FullFileSpec won't fit within MAX_PATH, then
  374. FullFileSpecIn is copied into the destination unchanged.
  375. - MAX_PATH is equal to MAX_WCHAR_PATH. This function assumes the destination
  376. buffer is only MAX_PATH wchars.
  377. Arguments:
  378. FullFileSpecIn - Specifies the inbound file spec.
  379. OutPath - Receives the long path, original path or truncated original path.
  380. The function attempts to fit long path in, then falls back to
  381. original path, and then falls back to truncated original path.
  382. Return Value:
  383. TRUE if the long path was transferred into the destination without any
  384. issues. In this case, multiple backslashes are converted to one (e.g.,
  385. c:\\foo becomes c:\foo).
  386. FALSE if the original path was transferred into the destination; the
  387. original path might get truncated
  388. --*/
  389. {
  390. WCHAR fullFileSpec[MAX_WCHAR_PATH];
  391. WIN32_FIND_DATAW findData;
  392. HANDLE findHandle;
  393. PWSTR end;
  394. PWSTR currentIn;
  395. PWSTR currentOut;
  396. PWSTR outEnd;
  397. PWSTR maxOutPath = OutPath + MAX_PATH - 1;
  398. BOOL result = FALSE;
  399. UINT oldMode;
  400. BOOL forceCopy = FALSE;
  401. MYASSERT (ISNT());
  402. oldMode = SetErrorMode (SEM_FAILCRITICALERRORS);
  403. __try {
  404. //
  405. // Limit source length for temp copy below
  406. //
  407. if (!IsPathLengthOkW (FullFileSpecIn)) {
  408. DEBUGMSGW ((DBG_ERROR, "Inbound file spec is too long: %s", FullFileSpecIn));
  409. __leave;
  410. }
  411. //
  412. // If path is on removable media, don't touch the disk
  413. //
  414. if (!IsPathOnFixedDriveW (FullFileSpecIn)) {
  415. forceCopy = TRUE;
  416. __leave;
  417. }
  418. //
  419. // Make a copy of the file spec so we can truncate it at the wacks
  420. //
  421. StackStringCopyW (fullFileSpec, FullFileSpecIn);
  422. //
  423. // Begin building the path
  424. //
  425. OutPath[0] = fullFileSpec[0];
  426. OutPath[1] = fullFileSpec[1];
  427. OutPath[2] = fullFileSpec[2];
  428. OutPath[3] = 0;
  429. MYASSERT (OutPath[0] && OutPath[1] && OutPath[2]);
  430. //
  431. // IsPathOnFixedDrive makes the following addition of 3 OK
  432. //
  433. currentIn = fullFileSpec + 3;
  434. currentOut = OutPath + 3;
  435. //
  436. // Loop for each segment of the path
  437. //
  438. for (;;) {
  439. end = wcschr (currentIn, L'\\');
  440. if (end == (currentIn + 1)) {
  441. //
  442. // Treat multiple backslashes as one
  443. //
  444. currentIn++;
  445. continue;
  446. }
  447. if (end) {
  448. *end = 0;
  449. }
  450. findHandle = FindFirstFileW (fullFileSpec, &findData);
  451. if (findHandle != INVALID_HANDLE_VALUE) {
  452. FindClose (findHandle);
  453. //
  454. // Copy long file name obtained from FindFirstFile
  455. //
  456. outEnd = currentOut + TcharCountW (findData.cFileName);
  457. if (outEnd > maxOutPath) {
  458. DEBUGMSGW ((
  459. DBG_WARNING,
  460. "Path %s too long to append to %s",
  461. findData.cFileName,
  462. OutPath
  463. ));
  464. __leave;
  465. }
  466. //
  467. // StringCopy is not bound because length is restricted above
  468. //
  469. StringCopyW (currentOut, findData.cFileName);
  470. currentOut = outEnd;
  471. } else {
  472. //
  473. // Copy the rest of the path since it doesn't exist
  474. //
  475. if (end) {
  476. *end = L'\\';
  477. }
  478. outEnd = currentOut + TcharCountW (currentIn);
  479. if (outEnd > maxOutPath) {
  480. DEBUGMSGW ((
  481. DBG_WARNING,
  482. "Path %s too long to append to %s",
  483. currentIn,
  484. OutPath
  485. ));
  486. __leave;
  487. }
  488. //
  489. // StringCopy is not bound because length is restricted above
  490. //
  491. StringCopyW (currentOut, currentIn);
  492. break; // done with path
  493. }
  494. if (!end) {
  495. MYASSERT (*currentOut == 0);
  496. break; // done with path
  497. }
  498. //
  499. // Ensure wack fits in destination buffer
  500. //
  501. if (currentOut + 1 > maxOutPath) {
  502. DEBUGMSGW ((
  503. DBG_WARNING,
  504. "Append wack exceeds MAX_PATH for %s",
  505. OutPath
  506. ));
  507. __leave;
  508. }
  509. //
  510. // Add wack and advance pointers
  511. //
  512. *currentOut++ = L'\\';
  513. *currentOut = 0;
  514. *end = L'\\'; // restore cut point
  515. currentIn = end + 1;
  516. }
  517. result = TRUE;
  518. }
  519. __finally {
  520. SetErrorMode (oldMode);
  521. }
  522. if (!result) {
  523. StringCopyTcharCountW (OutPath, FullFileSpecIn, MAX_PATH);
  524. }
  525. MYASSERT (IsPathLengthOkW (OutPath));
  526. return result || forceCopy;
  527. }
  528. BOOL
  529. DoesFileExistExA(
  530. IN PCSTR FileName,
  531. OUT PWIN32_FIND_DATAA FindData OPTIONAL
  532. )
  533. /*++
  534. Routine Description:
  535. Determine if a file exists and is accessible.
  536. Errormode is set (and then restored) so the user will not see
  537. any pop-ups.
  538. Arguments:
  539. FileName - supplies full path of file to check for existance.
  540. FindData - if specified, receives find data for the file.
  541. Return Value:
  542. TRUE if the file exists and is accessible.
  543. FALSE if not. GetLastError() returns extended error info.
  544. --*/
  545. {
  546. WIN32_FIND_DATAA ourFindData;
  547. HANDLE FindHandle;
  548. UINT OldMode;
  549. DWORD Error;
  550. if (!FindData) {
  551. //
  552. // We know that this can fail for other reasons, but for our purposes,
  553. // we want to know if the file is there. If we can't access it, we'll
  554. // just assume it is not there.
  555. //
  556. // Technically, the caller can look at GetLastError() results to make
  557. // a distinction. (Nobody does this.)
  558. //
  559. return GetFileAttributesA (FileName) != 0xffffffff;
  560. }
  561. OldMode = SetErrorMode(SEM_FAILCRITICALERRORS);
  562. FindHandle = FindFirstFileA(FileName, &ourFindData);
  563. if (FindHandle == INVALID_HANDLE_VALUE) {
  564. Error = GetLastError();
  565. } else {
  566. FindClose(FindHandle);
  567. *FindData = ourFindData;
  568. Error = NO_ERROR;
  569. }
  570. SetErrorMode(OldMode);
  571. SetLastError(Error);
  572. return (Error == NO_ERROR);
  573. }
  574. BOOL
  575. DoesFileExistExW (
  576. IN PCWSTR FileName,
  577. OUT PWIN32_FIND_DATAW FindData OPTIONAL
  578. )
  579. /*++
  580. Routine Description:
  581. Determine if a file exists and is accessible.
  582. Errormode is set (and then restored) so the user will not see
  583. any pop-ups.
  584. Arguments:
  585. FileName - supplies full path of file to check for existance.
  586. FindData - if specified, receives find data for the file.
  587. Return Value:
  588. TRUE if the file exists and is accessible.
  589. FALSE if not. GetLastError() returns extended error info.
  590. --*/
  591. {
  592. WIN32_FIND_DATAW ourFindData;
  593. HANDLE FindHandle;
  594. UINT OldMode;
  595. DWORD Error = NO_ERROR;
  596. UINT length;
  597. BOOL result = FALSE;
  598. PCWSTR longFileName = NULL;
  599. __try {
  600. if (FileName[0] != TEXT('\\')) {
  601. length = TcharCountW (FileName);
  602. if (length >= MAX_PATH) {
  603. longFileName = JoinPathsW (L"\\\\?", FileName);
  604. MYASSERT (longFileName);
  605. FileName = longFileName;
  606. }
  607. }
  608. if (!FindData) {
  609. //
  610. // We know that this can fail for other reasons, but for our
  611. // purposes, we want to know if the file is there. If we can't
  612. // access it, we'll just assume it is not there.
  613. //
  614. // Technically, the caller can look at GetLastError() results to
  615. // make a distinction. (Nobody does this.)
  616. //
  617. result = (GetLongPathAttributesW (FileName) != 0xffffffff);
  618. __leave;
  619. }
  620. OldMode = SetErrorMode(SEM_FAILCRITICALERRORS);
  621. FindHandle = FindFirstFileW(FileName,&ourFindData);
  622. if (FindHandle == INVALID_HANDLE_VALUE) {
  623. Error = GetLastError();
  624. } else {
  625. FindClose(FindHandle);
  626. *FindData = ourFindData;
  627. }
  628. SetErrorMode(OldMode);
  629. SetLastError(Error);
  630. result = (Error == NO_ERROR);
  631. }
  632. __finally {
  633. if (longFileName) {
  634. FreePathStringW (longFileName);
  635. }
  636. }
  637. return result;
  638. }
  639. DWORD
  640. MakeSurePathExistsA(
  641. IN PCSTR FullFileSpec,
  642. IN BOOL PathOnly
  643. )
  644. /*++
  645. Routine Description:
  646. MakeSurePathExistsA creates the subdirectories necessary to hold FileSpec.
  647. It attempts to create each level of the subdirectory.
  648. CAUTION: This routine does not set ACLs. Instead, it relies on
  649. parent inheritance. It is intended for Win9x use.
  650. Arguments:
  651. FullFileSpec - Specifies the path, which might be a local drive or a UNC
  652. path.
  653. PathOnly - Specifies TRUE if FullFileSpec represents a subdirectory path, or
  654. FALSE if FullFileSpec represents a subdirectory/file path.
  655. Return Value:
  656. The Win32 result code, normally ERROR_SUCCESS.
  657. --*/
  658. {
  659. CHAR Buffer[MAX_MBCHAR_PATH];
  660. PCHAR p,q;
  661. BOOL Done;
  662. BOOL isUnc;
  663. DWORD d;
  664. WIN32_FIND_DATAA FindData;
  665. isUnc = (FullFileSpec[0] == '\\') && (FullFileSpec[1] == '\\');
  666. //
  667. // Locate and strip off the final component
  668. //
  669. _mbssafecpy(Buffer,FullFileSpec,sizeof(Buffer));
  670. p = _mbsrchr(Buffer, '\\');
  671. if (p) {
  672. if (!PathOnly) {
  673. *p = 0;
  674. }
  675. //
  676. // If it's a drive root, nothing to do.
  677. //
  678. if(Buffer[0] && (Buffer[1] == ':') && !Buffer[2]) {
  679. return(NO_ERROR);
  680. }
  681. } else {
  682. //
  683. // Just a relative filename, nothing to do.
  684. //
  685. return(NO_ERROR);
  686. }
  687. //
  688. // If it already exists do nothing.
  689. //
  690. if (DoesFileExistExA (Buffer,&FindData)) {
  691. return NO_ERROR;
  692. }
  693. p = Buffer;
  694. //
  695. // Compensate for drive spec.
  696. //
  697. if(p[0] && (p[1] == ':')) {
  698. p += 2;
  699. }
  700. //
  701. // Compensate for UNC paths.
  702. //
  703. if (isUnc) {
  704. //
  705. // Skip the initial wack wack before machine name.
  706. //
  707. p += 2;
  708. //
  709. // Skip to the share.
  710. //
  711. p = _mbschr(p, '\\');
  712. if (p==NULL) {
  713. return ERROR_BAD_PATHNAME;
  714. }
  715. //
  716. // Skip past the share.
  717. //
  718. p = _mbschr(p, '\\');
  719. if (p==NULL) {
  720. return ERROR_BAD_PATHNAME;
  721. }
  722. }
  723. Done = FALSE;
  724. do {
  725. //
  726. // Skip path sep char.
  727. //
  728. while(*p == '\\') {
  729. p++;
  730. }
  731. //
  732. // Locate next path sep char or terminating nul.
  733. //
  734. if(q = _mbschr(p, '\\')) {
  735. *q = 0;
  736. } else {
  737. q = GetEndOfStringA(p);
  738. Done = TRUE;
  739. }
  740. //
  741. // Create this portion of the path.
  742. //
  743. if(!CreateDirectoryA(Buffer,NULL)) {
  744. d = GetLastError();
  745. if(d != ERROR_ALREADY_EXISTS) {
  746. return(d);
  747. }
  748. }
  749. if(!Done) {
  750. *q = '\\';
  751. p = q+1;
  752. }
  753. } while(!Done);
  754. return(NO_ERROR);
  755. }
  756. #if 0 // UNUSED FUNCTION
  757. VOID
  758. DestPathCopyW (
  759. OUT PWSTR DestPath,
  760. IN PCWSTR SrcPath
  761. )
  762. {
  763. PCWSTR p;
  764. PWSTR q;
  765. PCWSTR end;
  766. PCWSTR maxStart;
  767. UINT len;
  768. UINT count;
  769. len = TcharCountW (SrcPath);
  770. if (len < MAX_PATH) {
  771. StringCopyW (DestPath, SrcPath);
  772. return;
  773. }
  774. //
  775. // Path is too long -- try to truncate it
  776. //
  777. wsprintfW (DestPath, L"%c:\\Long", SrcPath[0]);
  778. CreateDirectoryW (DestPath, NULL);
  779. p = SrcPath;
  780. end = SrcPath + len;
  781. maxStart = end - 220;
  782. while (p < end) {
  783. if (*p == '\\') {
  784. if (p >= maxStart) {
  785. break;
  786. }
  787. }
  788. p++;
  789. }
  790. if (p == end) {
  791. p = maxStart;
  792. }
  793. MYASSERT (TcharCountW (p) <= 220);
  794. StringCopyW (AppendWackW (DestPath), p);
  795. q = (PWSTR) GetEndOfStringW (DestPath);
  796. //
  797. // Verify there is no collision
  798. //
  799. for (count = 1 ; count < 1000000 ; count++) {
  800. if (GetFileAttributesW (DestPath) == INVALID_ATTRIBUTES) {
  801. break;
  802. }
  803. wsprintfW (q, L" (%u)", count);
  804. }
  805. }
  806. #endif
  807. DWORD
  808. MakeSurePathExistsW(
  809. IN LPCWSTR FullFileSpec,
  810. IN BOOL PathOnly
  811. )
  812. /*++
  813. Routine Description:
  814. MakeSurePathExistsW creates the subdirectories necessary to hold FileSpec.
  815. It attempts to create each level of the subdirectory.
  816. This function will not create subdirectories that exceed MAX_PATH, unless
  817. the path is decordated with the \\?\ prefix.
  818. CAUTION: This routine does not set ACLs. Instead, it relies on
  819. parent inheritance. It is intended for Win9x use.
  820. Arguments:
  821. FullFileSpec - Specifies the path, which might be a local drive or a UNC
  822. path.
  823. PathOnly - Specifies TRUE if FullFileSpec represents a subdirectory path, or
  824. FALSE if FullFileSpec represents a subdirectory/file path.
  825. Return Value:
  826. The Win32 result code, normally ERROR_SUCCESS.
  827. --*/
  828. {
  829. PWSTR buffer;
  830. WCHAR *p, *q;
  831. BOOL Done;
  832. DWORD d;
  833. WIN32_FIND_DATAW FindData;
  834. DWORD result = NO_ERROR;
  835. if (FullFileSpec[0] != L'\\') {
  836. if (TcharCountW (FullFileSpec) >= MAX_PATH) {
  837. //
  838. // Verify the path portion is too large. Note that a string without a wack
  839. // does work here.
  840. //
  841. if (PathOnly || ((wcsrchr (FullFileSpec, L'\\') - FullFileSpec) >= MAX_PATH)) {
  842. LOGW ((LOG_ERROR, "Can't create path %s because it is too long", FullFileSpec));
  843. return ERROR_FILENAME_EXCED_RANGE;
  844. }
  845. }
  846. }
  847. //
  848. // Locate and strip off the final component
  849. //
  850. buffer = DuplicatePathStringW (FullFileSpec, 0);
  851. __try {
  852. p = wcsrchr(buffer, L'\\');
  853. if (p) {
  854. if (!PathOnly) {
  855. *p = 0;
  856. }
  857. } else {
  858. //
  859. // Just a relative filename, nothing to do.
  860. //
  861. __leave;
  862. }
  863. //
  864. // If it already exists do nothing.
  865. //
  866. if (DoesFileExistExW (buffer, &FindData)) {
  867. result = ((FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? NO_ERROR : ERROR_DIRECTORY);
  868. __leave;
  869. }
  870. p = buffer;
  871. //
  872. // Compensate for drive spec.
  873. //
  874. if (p[0] == L'\\' && p[1] == L'\\' && p[2] == L'?' && p[3] == L'\\') {
  875. p += 4;
  876. }
  877. if (p[0] && (p[1] == L':')) {
  878. p += 2;
  879. }
  880. if ((p[0] == 0) || (p[0] == L'\\' && p[1] == 0)) {
  881. //
  882. // Root -- leave
  883. //
  884. __leave;
  885. }
  886. Done = FALSE;
  887. do {
  888. //
  889. // Skip path sep char.
  890. //
  891. while(*p == L'\\') {
  892. p++;
  893. }
  894. //
  895. // Locate next path sep char or terminating nul.
  896. //
  897. q = wcschr(p, L'\\');
  898. if(q) {
  899. *q = 0;
  900. } else {
  901. q = GetEndOfStringW (p);
  902. Done = TRUE;
  903. }
  904. //
  905. // Create this portion of the path.
  906. //
  907. if(!CreateDirectoryW(buffer,NULL)) {
  908. d = GetLastError();
  909. if(d != ERROR_ALREADY_EXISTS) {
  910. result = d;
  911. __leave;
  912. }
  913. }
  914. if(!Done) {
  915. *q = L'\\';
  916. p = q+1;
  917. }
  918. } while(!Done);
  919. }
  920. __finally {
  921. FreePathStringW (buffer);
  922. }
  923. return result;
  924. }
  925. BOOL
  926. WriteFileStringA (
  927. IN HANDLE File,
  928. IN PCSTR String
  929. )
  930. /*++
  931. Routine Description:
  932. Writes a DBCS string to the specified file.
  933. Arguments:
  934. File - Specifies the file handle that was opened with write access.
  935. String - Specifies the nul-terminated string to write to the file.
  936. Return Value:
  937. TRUE if successful, FALSE if an error occurred. Call GetLastError
  938. for error condition.
  939. --*/
  940. {
  941. DWORD DontCare;
  942. return WriteFile (File, String, ByteCountA (String), &DontCare, NULL);
  943. }
  944. BOOL
  945. WriteFileStringW (
  946. IN HANDLE File,
  947. IN PCWSTR String
  948. )
  949. /*++
  950. Routine Description:
  951. Converts a UNICODE string to DBCS, then Writes it to the specified file.
  952. Arguments:
  953. File - Specifies the file handle that was opened with write access.
  954. String - Specifies the UNICODE nul-terminated string to convert and
  955. write to the file.
  956. Return Value:
  957. TRUE if successful, FALSE if an error occurred. Call GetLastError for
  958. error condition.
  959. --*/
  960. {
  961. DWORD DontCare;
  962. PCSTR AnsiVersion;
  963. BOOL b;
  964. AnsiVersion = ConvertWtoA (String);
  965. if (!AnsiVersion) {
  966. return FALSE;
  967. }
  968. b = WriteFile (File, AnsiVersion, ByteCountA (AnsiVersion), &DontCare, NULL);
  969. FreeConvertedStr (AnsiVersion);
  970. return b;
  971. }
  972. BOOL
  973. pFindShortNameA (
  974. IN PCSTR WhatToFind,
  975. OUT PSTR Buffer,
  976. IN OUT INT *BufferSizeInBytes
  977. )
  978. /*++
  979. Routine Description:
  980. pFindShortNameA is a helper function for OurGetLongPathName. It
  981. obtains the short file name, if it exists, using FindFirstFile.
  982. Arguments:
  983. WhatToFind - Specifies the short or long name of a file to locate
  984. Buffer - Receives the matching file name. This buffer must be MAX_PATH or
  985. larger (actually, it must be sized identical to Win32's
  986. WIN32_FIND_DATAA cFileName member, which is MAX_PATH).
  987. BufferSizeInBytes - Specifies the size of Buffer (the bytes remaining),
  988. receives the number of bytes (excluding the terminating
  989. nul) written to Buffer. This is for optimizing.
  990. Return Value:
  991. TRUE if the file was found and Buffer was updated, or FALSE if the
  992. file was not found and Buffer was not updated.
  993. --*/
  994. {
  995. WIN32_FIND_DATAA fd;
  996. HANDLE hFind;
  997. hFind = FindFirstFileA (WhatToFind, &fd);
  998. if (hFind == INVALID_HANDLE_VALUE) {
  999. return FALSE;
  1000. }
  1001. FindClose (hFind);
  1002. //
  1003. // CAUTION: This code is returning TRUE when it cannot append the next
  1004. // segment to Buffer. However, BufferSizeInBytes is reduced, so the caller
  1005. // will not be able to add anything else.
  1006. //
  1007. *BufferSizeInBytes -= ByteCountA (fd.cFileName);
  1008. if (*BufferSizeInBytes >= sizeof (CHAR)) {
  1009. StringCopyTcharCountA (Buffer, fd.cFileName, ARRAYSIZE(fd.cFileName));
  1010. }
  1011. return TRUE;
  1012. }
  1013. BOOL
  1014. pFindShortNameW (
  1015. IN PCWSTR WhatToFind,
  1016. OUT PWSTR Buffer,
  1017. IN OUT INT *BufferSizeInBytes
  1018. )
  1019. /*++
  1020. Routine Description:
  1021. pFindShortNameW is a helper function for OurGetLongPathName. It obtains the
  1022. short file name, if it exists, using FindFirstFile. This version (the wide
  1023. character version) calls FindFirstFileW on NT, and FindFirstFileA on 9x.
  1024. Arguments:
  1025. WhatToFind - Specifies the short or long name of a file to locate
  1026. Buffer - Receives the matching file name. This buffer must be MAX_PATH or
  1027. larger (actually, it must be sized identical to Win32's
  1028. WIN32_FIND_DATAA cFileName member, which is MAX_PATH).
  1029. BufferSizeInBytes - Specifies the size of Buffer (the bytes remaining),
  1030. receives the number of bytes (excluding the terminating
  1031. nul) written to Buffer. This is for optimizing.
  1032. Return Value:
  1033. TRUE if the file was found and Buffer was updated, or FALSE if the
  1034. file was not found and Buffer was not updated.
  1035. --*/
  1036. {
  1037. WIN32_FIND_DATAA fdA;
  1038. WIN32_FIND_DATAW fdW;
  1039. PCWSTR UnicodeVersion;
  1040. PCSTR AnsiVersion;
  1041. HANDLE hFind;
  1042. if (ISNT ()) {
  1043. hFind = FindFirstFileW (WhatToFind, &fdW);
  1044. if (hFind == INVALID_HANDLE_VALUE) {
  1045. return FALSE;
  1046. }
  1047. FindClose (hFind);
  1048. } else {
  1049. AnsiVersion = ConvertWtoA (WhatToFind);
  1050. MYASSERT (AnsiVersion);
  1051. hFind = FindFirstFileA (AnsiVersion, &fdA);
  1052. FreeConvertedStr (AnsiVersion);
  1053. if (hFind == INVALID_HANDLE_VALUE) {
  1054. return FALSE;
  1055. }
  1056. FindClose (hFind);
  1057. //
  1058. // Transfer ANSI to UNICODE.
  1059. //
  1060. // BUGBUG -- this is highly unnecessary, as fdW is local and is
  1061. // eventually discarded.
  1062. //
  1063. fdW.dwFileAttributes = fdA.dwFileAttributes;
  1064. fdW.ftCreationTime = fdA.ftCreationTime;
  1065. fdW.ftLastAccessTime = fdA.ftLastAccessTime;
  1066. fdW.ftLastWriteTime = fdA.ftLastWriteTime;
  1067. fdW.nFileSizeHigh = fdA.nFileSizeHigh;
  1068. fdW.nFileSizeLow = fdA.nFileSizeLow;
  1069. fdW.dwReserved0 = fdA.dwReserved0;
  1070. fdW.dwReserved1 = fdA.dwReserved1;
  1071. UnicodeVersion = ConvertAtoW (fdA.cFileName);
  1072. MYASSERT (UnicodeVersion);
  1073. StringCopyTcharCountW (fdW.cFileName, UnicodeVersion, ARRAYSIZE(fdW.cFileName));
  1074. FreeConvertedStr (UnicodeVersion);
  1075. UnicodeVersion = ConvertAtoW (fdA.cAlternateFileName);
  1076. MYASSERT (UnicodeVersion);
  1077. StringCopyTcharCountW (fdW.cAlternateFileName, UnicodeVersion, ARRAYSIZE(fdW.cAlternateFileName));
  1078. FreeConvertedStr (UnicodeVersion);
  1079. }
  1080. //
  1081. // CAUTION: This code is returning TRUE when it cannot append the next
  1082. // segment to Buffer. However, BufferSizeInBytes is reduced, so the caller
  1083. // will not be able to add anything else.
  1084. //
  1085. *BufferSizeInBytes -= ByteCountW (fdW.cFileName);
  1086. if (*BufferSizeInBytes >= sizeof (WCHAR)) {
  1087. StringCopyTcharCountW (Buffer, fdW.cFileName, ARRAYSIZE(fdW.cFileName));
  1088. }
  1089. return TRUE;
  1090. }
  1091. BOOL
  1092. OurGetLongPathNameA (
  1093. IN PCSTR ShortPath,
  1094. OUT PSTR Buffer,
  1095. IN INT BufferSizeInBytes
  1096. )
  1097. /*++
  1098. Routine Description:
  1099. OurGetLongPathNameA locates the long file name for the specified short file.
  1100. It first computes the full path if a path is not explicitly provided, and
  1101. then uses FindFirstFileA to get the long file name. NOTE: This is exactly
  1102. what the Win32 function GetLongPathName does, but unfortunately the Win32
  1103. API is not available on Win95.
  1104. CAUTION: If the buffer is not big enough to hold the whole path, the path
  1105. will be truncated.
  1106. Arguments:
  1107. ShortPath - Specifies the file name or full file path to locate
  1108. Buffer - Receives the full file path. This buffer must be big enough to
  1109. handle the maximum file name size.
  1110. BufferSizeInBytes - Specifies the size of Buffer, in bytes (not TCHARs).
  1111. Since this is the A version, bytes happens to equal
  1112. sizeof (TCHAR).
  1113. Return Value:
  1114. TRUE if the file is found and Buffer contains the long name, or FALSE
  1115. if the file is not found and Buffer is not modified.
  1116. --*/
  1117. {
  1118. CHAR FullPath[MAX_MBCHAR_PATH];
  1119. PCSTR SanitizedPath;
  1120. PSTR FilePart;
  1121. PSTR BufferEnd;
  1122. PSTR p, p2;
  1123. MBCHAR c;
  1124. BOOL result = TRUE;
  1125. // BUGBUG: This has been reviewed to be true for XP SP1, but should be
  1126. // enforced by returning an error if it is not the case
  1127. MYASSERT (BufferSizeInBytes >= MAX_MBCHAR_PATH);
  1128. if (ShortPath[0] == 0) {
  1129. return FALSE;
  1130. }
  1131. __try {
  1132. //
  1133. // Clean up the path so it is just a path spec, not filled with .. or
  1134. // other combinations
  1135. //
  1136. SanitizedPath = SanitizePathA (ShortPath);
  1137. if (!SanitizedPath) {
  1138. SanitizedPath = DuplicatePathStringA (ShortPath, 0);
  1139. }
  1140. if (!_mbschr (SanitizedPath, '\\')) {
  1141. //
  1142. // If only a file name is specified, use the path to find the file
  1143. //
  1144. if (!SearchPathA (NULL, SanitizedPath, NULL, ARRAYSIZE(FullPath), FullPath, &FilePart)) {
  1145. result = FALSE;
  1146. __leave;
  1147. }
  1148. } else {
  1149. //
  1150. // Use the OS to sanitize the path even further
  1151. //
  1152. GetFullPathNameA (SanitizedPath, ARRAYSIZE(FullPath), FullPath, &FilePart);
  1153. }
  1154. //
  1155. // Convert short paths to long paths
  1156. //
  1157. p = FullPath;
  1158. if (!IsPathOnFixedDriveA (FullPath)) {
  1159. //
  1160. // Not a local path, just return what we have. It might get truncated.
  1161. //
  1162. _mbssafecpy (Buffer, FullPath, BufferSizeInBytes);
  1163. __leave;
  1164. }
  1165. //
  1166. // We know that the first three chars are something like c:\, so we
  1167. // can advance by 3
  1168. //
  1169. MYASSERT (FullPath[0] && FullPath[1] && FullPath[2]);
  1170. p += 3;
  1171. //
  1172. // We've already asserted that the caller passed in a MAX_PATH buffer
  1173. //
  1174. MYASSERT (BufferSizeInBytes > 3 * sizeof (CHAR));
  1175. //
  1176. // Copy drive letter to buffer
  1177. //
  1178. StringCopyABA (Buffer, FullPath, p);
  1179. BufferEnd = GetEndOfStringA (Buffer);
  1180. BufferSizeInBytes -= (UINT) (UINT_PTR) (p - FullPath);
  1181. //
  1182. // Convert each portion of the path
  1183. //
  1184. do {
  1185. //
  1186. // Locate end of this file or dir
  1187. //
  1188. // BUGBUG: Other functions take into account multiple wack
  1189. // combinations, like c:\\\foo is really c:\foo. Is this a
  1190. // problem?
  1191. //
  1192. p2 = _mbschr (p, '\\');
  1193. if (!p2) {
  1194. p = GetEndOfStringA (p);
  1195. } else {
  1196. p = p2;
  1197. }
  1198. //
  1199. // Cut the path and look up file
  1200. //
  1201. c = *p;
  1202. *p = 0;
  1203. if (!pFindShortNameA (FullPath, BufferEnd, &BufferSizeInBytes)) {
  1204. DEBUGMSG ((DBG_VERBOSE, "OurGetLongPathNameA: %s does not exist", FullPath));
  1205. result = FALSE;
  1206. __leave;
  1207. }
  1208. *p = (CHAR)c; // restore the cut point
  1209. //
  1210. // Move on to next part of path
  1211. //
  1212. if (*p) {
  1213. p = _mbsinc (p);
  1214. if (BufferSizeInBytes >= sizeof (CHAR) * 2) {
  1215. BufferEnd = _mbsappend (BufferEnd, "\\");
  1216. BufferSizeInBytes -= sizeof (CHAR);
  1217. }
  1218. // BUGBUG -- let's break if we're out of buffer space!
  1219. }
  1220. //
  1221. // CAUTION: result is assumed TRUE until proven otherwise
  1222. //
  1223. } while (*p);
  1224. }
  1225. __finally {
  1226. FreePathStringA (SanitizedPath);
  1227. }
  1228. return result;
  1229. }
  1230. DWORD
  1231. OurGetShortPathNameW (
  1232. IN PCWSTR LongPath,
  1233. OUT PWSTR ShortPath,
  1234. IN DWORD CharSize
  1235. )
  1236. /*++
  1237. Routine Description:
  1238. OurGetShortPathNameW copies an 8.3 filename for the given LongPath, if the
  1239. file exists on the system.
  1240. If this function is called on Win9x, then the long path is converted to
  1241. ANSI, and GetShortPathNameA is called. Additionally, if the path points to a
  1242. non-local disk, then the true short path is not obtained, because it can
  1243. either take an unexpectedly long time, or it can cause other side effects
  1244. like spinning the floppy or CD drive.
  1245. If this function is called on NT, then the request is passed directly to the
  1246. Win32 version -- GetShortPathNameW.
  1247. Arguments:
  1248. LongPath - Specifies the long path to examine
  1249. ShortPath - Receives the short path on success
  1250. CharSize - Specifies the number of TCHARs (wchars in this case) that
  1251. ShortPath can hold
  1252. Return Value:
  1253. The number of TCHARs (wchars in this case) copied to ShortPath, excluding
  1254. the nul terminator, or zero if an error occurred. GetLastError provides the
  1255. error code.
  1256. CAUTION: This function fills in ShortPath on failure on Win9x, but does not
  1257. fill it in on NT.
  1258. --*/
  1259. {
  1260. PCSTR LongPathA;
  1261. PSTR ShortPathA;
  1262. PCWSTR ShortPathW;
  1263. DWORD result;
  1264. if (ISNT()) {
  1265. return GetShortPathNameW (LongPath, ShortPath, CharSize);
  1266. } else {
  1267. LongPathA = ConvertWtoA (LongPath);
  1268. MYASSERT (LongPathA);
  1269. if (!IsPathOnFixedDriveA (LongPathA)) {
  1270. StringCopyTcharCountW (ShortPath, LongPath, CharSize);
  1271. FreeConvertedStr (LongPathA);
  1272. return TcharCountW (ShortPath);
  1273. }
  1274. ShortPathA = AllocPathStringA (CharSize);
  1275. result = GetShortPathNameA (LongPathA, ShortPathA, CharSize);
  1276. if (result) {
  1277. ShortPathW = ConvertAtoW (ShortPathA);
  1278. MYASSERT (ShortPathW);
  1279. StringCopyTcharCountW (ShortPath, ShortPathW, CharSize);
  1280. FreeConvertedStr (ShortPathW);
  1281. } else {
  1282. // BUGBUG -- this is not consistent behavior
  1283. StringCopyTcharCountW (ShortPath, LongPath, CharSize);
  1284. }
  1285. FreePathStringA (ShortPathA);
  1286. FreeConvertedStr (LongPathA);
  1287. return result;
  1288. }
  1289. }
  1290. DWORD
  1291. OurGetFullPathNameW (
  1292. IN PCWSTR FileName,
  1293. IN DWORD CharSize,
  1294. OUT PWSTR FullPath,
  1295. OUT PWSTR *FilePtr
  1296. )
  1297. /*++
  1298. Routine Description:
  1299. OurGetFullPathNameW is a wrapper to GetFullPathName. Depending on the OS, it
  1300. is directed to GetFullPathNameW (NT) or GetFullPathNameA (9x).
  1301. CAUTION: The failure code for the 9x case is lost, but currently nobody
  1302. cares about this.
  1303. Arguments:
  1304. FileName - Specifies the file name to get the full path name of (see Win32
  1305. API for details on what GetFullPathName does)
  1306. CharSize - Specifies the number of TCHARs (wchars in this case) that
  1307. FullPath points to.
  1308. FullPath - Receives the full path specification of FileName
  1309. FilePtr - Receives a pointer to the file within FullPath. CAUTION: You
  1310. would think this is optional, but it is NOT.
  1311. Return Value:
  1312. The number of TCHARs (wchars in this case) written to FullPath, or zero if
  1313. an error occurred. On NT, GetLastError() will provide the status code. On
  1314. 9x, GetLastError() might provide the status code, but it could be eaten by
  1315. the ANSI/UNICODE conversion routines. BUGBUG -- maybe this should be fixed.
  1316. --*/
  1317. {
  1318. PCSTR FileNameA;
  1319. PSTR FullPathA;
  1320. PSTR FilePtrA;
  1321. PCWSTR FullPathW;
  1322. DWORD result;
  1323. DWORD err;
  1324. if (ISNT()) {
  1325. return GetFullPathNameW (FileName, CharSize, FullPath, FilePtr);
  1326. } else {
  1327. FileNameA = ConvertWtoA (FileName);
  1328. MYASSERT (FileNameA);
  1329. FullPathA = AllocPathStringA (CharSize);
  1330. MYASSERT (FullPathA);
  1331. MYASSERT (*FullPathA == 0); // this is important!
  1332. result = GetFullPathNameA (FileNameA, CharSize, FullPathA, &FilePtrA);
  1333. FullPathW = ConvertAtoW (FullPathA);
  1334. MYASSERT (FullPathW);
  1335. StringCopyTcharCountW (FullPath, FullPathW, CharSize);
  1336. err = GetLastError (); // BUGBUG -- unused assignment
  1337. MYASSERT (FilePtr); // non-optional argument
  1338. *FilePtr = (PWSTR)GetFileNameFromPathW (FullPath);
  1339. FreeConvertedStr (FullPathW);
  1340. FreePathStringA (FullPathA);
  1341. FreeConvertedStr (FileNameA);
  1342. return result;
  1343. }
  1344. }
  1345. BOOL
  1346. OurGetLongPathNameW (
  1347. IN PCWSTR ShortPath,
  1348. OUT PWSTR Buffer,
  1349. IN INT BufferSizeInChars
  1350. )
  1351. /*++
  1352. Routine Description:
  1353. OurGetLongPathNameW locates the long file name for the specified short file.
  1354. It first computes the full path if a path is not explicitly provided, and
  1355. then uses FindFirstFileA to get the long file name. NOTE: This is exactly
  1356. what the Win32 function GetLongPathName does, but unfortunately the Win32
  1357. API is not available on Win95.
  1358. CAUTIONS: If the buffer is not big enough to hold the whole path, the path
  1359. will be truncated.
  1360. If this version (the W version) is called on Win9x, and a full
  1361. path specification is not provided, the function will fail.
  1362. Path sanitization is not done in this version, but it is done in
  1363. the A version.
  1364. Arguments:
  1365. ShortPath - Specifies the file name or full file path to locate
  1366. Buffer - Receives the full file path. This buffer must be big enough to
  1367. handle the maximum file name size.
  1368. BufferSizeInBytes - Specifies the size of Buffer, in bytes (not TCHARs).
  1369. Since this is the A version, bytes happens to equal
  1370. sizeof (TCHAR).
  1371. Return Value:
  1372. TRUE if the file is found and Buffer contains the long name, or FALSE
  1373. if the file is not found and Buffer is not modified.
  1374. --*/
  1375. {
  1376. WCHAR FullPath[MAX_WCHAR_PATH];
  1377. PWSTR FilePart;
  1378. PWSTR BufferEnd;
  1379. PWSTR p, p2;
  1380. WCHAR c;
  1381. INT BufferSizeInBytes;
  1382. // BUGBUG: This has been reviewed to be true for XP SP1, but should be
  1383. // enforced by returning an error if it is not the case
  1384. MYASSERT (BufferSizeInChars >= MAX_WCHAR_PATH);
  1385. if (ShortPath[0] == 0) {
  1386. return FALSE;
  1387. }
  1388. BufferSizeInBytes = BufferSizeInChars * sizeof (WCHAR);
  1389. //
  1390. // CAUTION: In the A version, we sanitize the path (e.g., convert
  1391. // c:\foo\..\bar to c:\bar), but we don't do this for the W version,
  1392. // because it isn't necessary given the current use of this function.
  1393. //
  1394. //
  1395. // Resolve the ShortPath into a full path
  1396. //
  1397. if (!wcschr (ShortPath, L'\\')) {
  1398. if (!SearchPathW (NULL, ShortPath, NULL, MAX_WCHAR_PATH, FullPath, &FilePart)) {
  1399. return FALSE;
  1400. }
  1401. } else {
  1402. if (OurGetFullPathNameW (ShortPath, MAX_WCHAR_PATH, FullPath, &FilePart) == 0) {
  1403. return FALSE;
  1404. }
  1405. }
  1406. //
  1407. // Convert short paths to long paths
  1408. //
  1409. p = FullPath;
  1410. if (!IsPathOnFixedDriveW (FullPath)) {
  1411. StringCopyTcharCountW (Buffer, FullPath, BufferSizeInChars);
  1412. return TRUE;
  1413. }
  1414. //
  1415. // We know that the first three chars are something like c:\, so we
  1416. // can advance by 3
  1417. //
  1418. MYASSERT (FullPath[0] && FullPath[1] && FullPath[2]);
  1419. p += 3;
  1420. //
  1421. // Copy drive letter to buffer
  1422. //
  1423. StringCopyABW (Buffer, FullPath, p);
  1424. BufferEnd = GetEndOfStringW (Buffer);
  1425. BufferSizeInBytes -= sizeof (WCHAR) * 3;
  1426. //
  1427. // Convert each portion of the path
  1428. //
  1429. do {
  1430. //
  1431. // Locate end of this file or dir
  1432. //
  1433. // BUGBUG: Other functions take into account multiple wack
  1434. // combinations, like c:\\\foo is really c:\foo. Is this a
  1435. // problem?
  1436. //
  1437. p2 = wcschr (p, L'\\');
  1438. if (!p2) {
  1439. p = GetEndOfStringW (p);
  1440. } else {
  1441. p = p2;
  1442. }
  1443. //
  1444. // Cut the path and look up file
  1445. //
  1446. c = *p;
  1447. *p = 0;
  1448. if (!pFindShortNameW (FullPath, BufferEnd, &BufferSizeInBytes)) {
  1449. DEBUGMSG ((DBG_VERBOSE, "OurGetLongPathNameW: %ls does not exist", FullPath));
  1450. return FALSE;
  1451. }
  1452. *p = c; // restore cut point
  1453. //
  1454. // Move on to next part of path
  1455. //
  1456. if (*p) {
  1457. p++;
  1458. if (BufferSizeInBytes >= sizeof (WCHAR) * 2) {
  1459. BufferEnd = _wcsappend (BufferEnd, L"\\");
  1460. BufferSizeInBytes -= sizeof (WCHAR);
  1461. }
  1462. // BUGBUG -- let's break if we're out of buffer space!
  1463. }
  1464. } while (*p);
  1465. return TRUE;
  1466. }
  1467. #ifdef DEBUG
  1468. UINT g_FileEnumResourcesInUse;
  1469. #endif
  1470. VOID
  1471. pTrackedFindClose (
  1472. HANDLE FindHandle
  1473. )
  1474. {
  1475. #ifdef DEBUG
  1476. g_FileEnumResourcesInUse--;
  1477. #endif
  1478. FindClose (FindHandle);
  1479. }
  1480. BOOL
  1481. EnumFirstFileInTreeExA (
  1482. OUT PTREE_ENUMA EnumPtr,
  1483. IN PCSTR RootPath,
  1484. IN PCSTR FilePattern, OPTIONAL
  1485. IN BOOL EnumDirsFirst,
  1486. IN BOOL EnumDepthFirst,
  1487. IN INT MaxLevel
  1488. )
  1489. /*++
  1490. Routine Description:
  1491. EnumFirstFileInTreeA begins an enumeration of a directory tree. The
  1492. caller supplies an uninitialized enum structure, a directory path to
  1493. enumerate, and an optional file pattern. On return, the caller
  1494. receives all files and directories that match the pattern.
  1495. If a file pattern is supplied, directories that do not match the
  1496. file pattern are enumerated anyway.
  1497. Arguments:
  1498. EnumPtr - Receives the enumerated file or directory
  1499. RootPath - Specifies the full path of the directory to enumerate
  1500. FilePattern - Specifies a pattern of files to limit the search to
  1501. EnumDirsFirst - Specifies TRUE if the directories should be enumerated
  1502. before the files, or FALSE if the directories should
  1503. be enumerated after the files.
  1504. Return Value:
  1505. TRUE if a file or directory was enumerated, or FALSE if enumeration is complete
  1506. or an error occurred. (Use GetLastError to determine the result.)
  1507. --*/
  1508. {
  1509. ZeroMemory (EnumPtr, sizeof (TREE_ENUMA));
  1510. EnumPtr->State = TREE_ENUM_INIT;
  1511. _mbssafecpy (EnumPtr->RootPath, RootPath, sizeof (EnumPtr->RootPath));
  1512. if (FilePattern) {
  1513. _mbssafecpy (EnumPtr->FilePattern, FilePattern, sizeof (EnumPtr->FilePattern));
  1514. } else {
  1515. //
  1516. // Important: some drivers on Win9x don't think * is *.*
  1517. //
  1518. StringCopyA (EnumPtr->FilePattern, "*.*");
  1519. }
  1520. EnumPtr->EnumDirsFirst = EnumDirsFirst;
  1521. EnumPtr->EnumDepthFirst = EnumDepthFirst;
  1522. EnumPtr->Level = 1;
  1523. EnumPtr->MaxLevel = MaxLevel;
  1524. return EnumNextFileInTreeA (EnumPtr);
  1525. }
  1526. BOOL
  1527. EnumFirstFileInTreeExW (
  1528. OUT PTREE_ENUMW EnumPtr,
  1529. IN PCWSTR RootPath,
  1530. IN PCWSTR FilePattern, OPTIONAL
  1531. IN BOOL EnumDirsFirst,
  1532. IN BOOL EnumDepthFirst,
  1533. IN INT MaxLevel
  1534. )
  1535. /*++
  1536. Routine Description:
  1537. EnumFirstFileInTreeW begins an enumeration of a directory tree. The
  1538. caller supplies an uninitialized enum structure, a directory path to
  1539. enumerate, and an optional file pattern. On return, the caller
  1540. receives all files and directories that match the pattern.
  1541. If a file pattern is supplied, directories that do not match the
  1542. file pattern are enumerated anyway.
  1543. Arguments:
  1544. EnumPtr - Receives the enumerated file or directory
  1545. RootPath - Specifies the full path of the directory to enumerate
  1546. FilePattern - Specifies a pattern of files to limit the search to
  1547. EnumDirsFirst - Specifies TRUE if the directories should be enumerated
  1548. before the files, or FALSE if the directories should
  1549. be enumerated after the files.
  1550. Return Value:
  1551. TRUE if a file or directory was enumerated, or FALSE if enumeration is complete
  1552. or an error occurred. (Use GetLastError to determine the result.)
  1553. --*/
  1554. {
  1555. ZeroMemory (EnumPtr, sizeof (TREE_ENUMW));
  1556. EnumPtr->State = TREE_ENUM_INIT;
  1557. _wcssafecpy (EnumPtr->RootPath, RootPath, sizeof (EnumPtr->RootPath));
  1558. if (FilePattern) {
  1559. _wcssafecpy (EnumPtr->FilePattern, FilePattern, sizeof (EnumPtr->FilePattern));
  1560. } else {
  1561. //
  1562. // Important: some drivers on Win9x don't think * is *.*
  1563. //
  1564. StringCopyW (EnumPtr->FilePattern, L"*.*");
  1565. }
  1566. EnumPtr->EnumDirsFirst = EnumDirsFirst;
  1567. EnumPtr->EnumDepthFirst = EnumDepthFirst;
  1568. EnumPtr->Level = 1;
  1569. EnumPtr->MaxLevel = MaxLevel;
  1570. return EnumNextFileInTreeW (EnumPtr);
  1571. }
  1572. BOOL
  1573. EnumNextFileInTreeA (
  1574. IN OUT PTREE_ENUMA EnumPtr
  1575. )
  1576. /*++
  1577. Routine Description:
  1578. EnumNextFileInTree continues an enumeration of a directory tree,
  1579. returning the files that match the pattern specified in EnumFirstFileInTree,
  1580. and also returning all directories.
  1581. Arguments:
  1582. EnumPtr - Specifies the enumeration in progress, receives the enumerated file
  1583. or directory
  1584. Return Value:
  1585. TRUE if a file or directory was enumerated, or FALSE if enumeration is complete
  1586. or an error occurred. (Use GetLastError to determine the result.)
  1587. --*/
  1588. {
  1589. PSTR p;
  1590. for (;;) {
  1591. switch (EnumPtr->State) {
  1592. case TREE_ENUM_INIT:
  1593. //
  1594. // Get rid of wack at the end of root path, if it exists
  1595. //
  1596. p = GetEndOfStringA (EnumPtr->RootPath);
  1597. p = our_mbsdec (EnumPtr->RootPath, p);
  1598. if (!p) {
  1599. DEBUGMSGA ((DBG_ERROR, "Path spec %s is incomplete", EnumPtr->RootPath));
  1600. EnumPtr->State = TREE_ENUM_FAILED;
  1601. break;
  1602. }
  1603. if (_mbsnextc (p) == '\\') {
  1604. *p = 0;
  1605. }
  1606. //
  1607. // Initialize enumeration structure
  1608. //
  1609. EnumPtr->FilePatternSize = SizeOfStringA (EnumPtr->FilePattern);
  1610. MYASSERT (sizeof (EnumPtr->FileBuffer) == sizeof (EnumPtr->RootPath));
  1611. StringCopyA (EnumPtr->FileBuffer, EnumPtr->RootPath);
  1612. EnumPtr->EndOfFileBuffer = GetEndOfStringA (EnumPtr->FileBuffer);
  1613. MYASSERT (sizeof (EnumPtr->Pattern) == sizeof (EnumPtr->RootPath));
  1614. StringCopyA (EnumPtr->Pattern, EnumPtr->RootPath);
  1615. EnumPtr->EndOfPattern = GetEndOfStringA (EnumPtr->Pattern);
  1616. EnumPtr->FullPath = EnumPtr->FileBuffer;
  1617. EnumPtr->RootPathSize = ByteCountA (EnumPtr->RootPath);
  1618. //
  1619. // Allocate first find data sturct
  1620. //
  1621. EnumPtr->Current = (PFIND_DATAA) GrowBuffer (
  1622. &EnumPtr->FindDataArray,
  1623. sizeof (FIND_DATAA)
  1624. );
  1625. if (!EnumPtr->Current) {
  1626. EnumPtr->State = TREE_ENUM_FAILED;
  1627. break;
  1628. }
  1629. #ifdef DEBUG
  1630. g_FileEnumResourcesInUse++; // account for grow buffer
  1631. #endif
  1632. EnumPtr->State = TREE_ENUM_BEGIN;
  1633. break;
  1634. case TREE_ENUM_BEGIN:
  1635. //
  1636. // Initialize current find data struct
  1637. //
  1638. EnumPtr->Current->SavedEndOfFileBuffer = EnumPtr->EndOfFileBuffer;
  1639. EnumPtr->Current->SavedEndOfPattern = EnumPtr->EndOfPattern;
  1640. //
  1641. // Limit the length of the pattern string
  1642. //
  1643. MYASSERT (ARRAYSIZE(EnumPtr->FileBuffer) == MAX_MBCHAR_PATH);
  1644. //
  1645. // This math below does account for the added wack between the
  1646. // pattern base already in EnumPtr->Pattern and the new pattern
  1647. // in EnumPtr->FilePatternSize. The way it is included is by
  1648. // using >= instead of ==, and an assumption that sizeof(CHAR) == 1.
  1649. // EnumPtr->FilePatternSize includes the nul terminator.
  1650. //
  1651. MYASSERT (sizeof (CHAR) == 1); // the math is totally dependent on this
  1652. if ((EnumPtr->EndOfPattern - EnumPtr->Pattern) +
  1653. EnumPtr->FilePatternSize >= MAX_MBCHAR_PATH
  1654. ) {
  1655. LOGA ((LOG_ERROR, "Path %s\\%s is too long", EnumPtr->Pattern, EnumPtr->FilePattern));
  1656. EnumPtr->State = TREE_ENUM_POP;
  1657. break;
  1658. }
  1659. //
  1660. // Enumerate the files or directories
  1661. //
  1662. if (EnumPtr->EnumDirsFirst) {
  1663. EnumPtr->State = TREE_ENUM_DIRS_BEGIN;
  1664. } else {
  1665. EnumPtr->State = TREE_ENUM_FILES_BEGIN;
  1666. }
  1667. break;
  1668. case TREE_ENUM_FILES_BEGIN:
  1669. //
  1670. // Begin enumeration of files
  1671. //
  1672. // This assert is valid because of length check in TREE_ENUM_BEGIN
  1673. MYASSERT ((TcharCountA (EnumPtr->Pattern) + 1 +
  1674. TcharCountA (EnumPtr->FilePattern)) < ARRAYSIZE(EnumPtr->Pattern)
  1675. );
  1676. StringCopyA (EnumPtr->EndOfPattern, "\\");
  1677. StringCopyA (EnumPtr->EndOfPattern + 1, EnumPtr->FilePattern);
  1678. EnumPtr->Current->FindHandle = FindFirstFileA (
  1679. EnumPtr->Pattern,
  1680. &EnumPtr->Current->FindData
  1681. );
  1682. if (EnumPtr->Current->FindHandle == INVALID_HANDLE_VALUE) {
  1683. if (EnumPtr->EnumDirsFirst) {
  1684. EnumPtr->State = TREE_ENUM_POP;
  1685. } else {
  1686. EnumPtr->State = TREE_ENUM_DIRS_BEGIN;
  1687. }
  1688. } else {
  1689. #ifdef DEBUG
  1690. g_FileEnumResourcesInUse++; // account for creation of find handle
  1691. #endif
  1692. //
  1693. // Skip directories
  1694. //
  1695. if (EnumPtr->Current->FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
  1696. EnumPtr->State = TREE_ENUM_FILES_NEXT;
  1697. } else {
  1698. EnumPtr->State = TREE_ENUM_RETURN_ITEM;
  1699. }
  1700. }
  1701. break;
  1702. case TREE_ENUM_RETURN_ITEM:
  1703. //
  1704. // Update pointers to current item
  1705. //
  1706. EnumPtr->FindData = &EnumPtr->Current->FindData;
  1707. EnumPtr->Name = EnumPtr->FindData->cFileName;
  1708. EnumPtr->Directory = (EnumPtr->FindData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
  1709. //
  1710. // Limit the length of the resulting full path. The nul is
  1711. // accounted for in SizeOfStringA, and the additional wack is
  1712. // accounted for by using >= instead of ==.
  1713. //
  1714. MYASSERT (ARRAYSIZE(EnumPtr->FileBuffer) == MAX_MBCHAR_PATH);
  1715. MYASSERT (sizeof (CHAR) == 1); // the math is totally dependent on this
  1716. if ((EnumPtr->EndOfFileBuffer - EnumPtr->FileBuffer) +
  1717. SizeOfStringA (EnumPtr->Name) >= MAX_MBCHAR_PATH
  1718. ) {
  1719. LOGA ((LOG_ERROR, "Path %s\\%s is too long", EnumPtr->FileBuffer, EnumPtr->Name));
  1720. if (EnumPtr->Directory) {
  1721. EnumPtr->State = TREE_ENUM_DIRS_NEXT;
  1722. } else {
  1723. EnumPtr->State = TREE_ENUM_FILES_NEXT;
  1724. }
  1725. break;
  1726. }
  1727. //
  1728. // Generate the full path
  1729. //
  1730. StringCopyA (EnumPtr->EndOfFileBuffer, "\\");
  1731. StringCopyA (EnumPtr->EndOfFileBuffer + 1, EnumPtr->Name);
  1732. if (EnumPtr->Directory) {
  1733. if ((EnumPtr->MaxLevel == FILE_ENUM_ALL_LEVELS) ||
  1734. (EnumPtr->Level < EnumPtr->MaxLevel)
  1735. ) {
  1736. if (EnumPtr->EnumDepthFirst) {
  1737. EnumPtr->State = TREE_ENUM_DIRS_NEXT;
  1738. }
  1739. else {
  1740. EnumPtr->State = TREE_ENUM_PUSH;
  1741. }
  1742. }
  1743. else {
  1744. EnumPtr->State = TREE_ENUM_DIRS_NEXT;
  1745. }
  1746. } else {
  1747. EnumPtr->State = TREE_ENUM_FILES_NEXT;
  1748. }
  1749. EnumPtr->SubPath = (PCSTR) ((PBYTE) EnumPtr->FileBuffer + EnumPtr->RootPathSize);
  1750. if (*EnumPtr->SubPath) {
  1751. EnumPtr->SubPath++; // advance beyond wack
  1752. }
  1753. return TRUE;
  1754. case TREE_ENUM_FILES_NEXT:
  1755. if (FindNextFileA (EnumPtr->Current->FindHandle, &EnumPtr->Current->FindData)) {
  1756. //
  1757. // Return files only
  1758. //
  1759. if (!(EnumPtr->Current->FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
  1760. EnumPtr->State = TREE_ENUM_RETURN_ITEM;
  1761. }
  1762. } else {
  1763. if (!EnumPtr->EnumDirsFirst) {
  1764. pTrackedFindClose (EnumPtr->Current->FindHandle);
  1765. EnumPtr->State = TREE_ENUM_DIRS_BEGIN;
  1766. } else {
  1767. EnumPtr->State = TREE_ENUM_POP;
  1768. }
  1769. }
  1770. break;
  1771. case TREE_ENUM_DIRS_FILTER:
  1772. if (!(EnumPtr->Current->FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
  1773. EnumPtr->State = TREE_ENUM_DIRS_NEXT;
  1774. } else if (StringMatchA (EnumPtr->Current->FindData.cFileName, ".") ||
  1775. StringMatchA (EnumPtr->Current->FindData.cFileName, "..")
  1776. ) {
  1777. EnumPtr->State = TREE_ENUM_DIRS_NEXT;
  1778. } else {
  1779. if (EnumPtr->EnumDepthFirst) {
  1780. EnumPtr->State = TREE_ENUM_PUSH;
  1781. }
  1782. else {
  1783. EnumPtr->State = TREE_ENUM_RETURN_ITEM;
  1784. }
  1785. }
  1786. break;
  1787. case TREE_ENUM_DIRS_BEGIN:
  1788. //
  1789. // Begin enumeration of directories
  1790. //
  1791. if ((EnumPtr->EndOfPattern - EnumPtr->Pattern) + 4 >= MAX_MBCHAR_PATH) {
  1792. LOGA ((LOG_ERROR, "Path %s\\*.* is too long", EnumPtr->Pattern));
  1793. EnumPtr->State = TREE_ENUM_POP;
  1794. break;
  1795. }
  1796. StringCopyA (EnumPtr->EndOfPattern, "\\*.*");
  1797. EnumPtr->Current->FindHandle = FindFirstFileA (
  1798. EnumPtr->Pattern,
  1799. &EnumPtr->Current->FindData
  1800. );
  1801. if (EnumPtr->Current->FindHandle == INVALID_HANDLE_VALUE) {
  1802. EnumPtr->State = TREE_ENUM_POP;
  1803. } else {
  1804. #ifdef DEBUG
  1805. g_FileEnumResourcesInUse++; // account for creation of find handle
  1806. #endif
  1807. EnumPtr->State = TREE_ENUM_DIRS_FILTER;
  1808. }
  1809. break;
  1810. case TREE_ENUM_DIRS_NEXT:
  1811. if (FindNextFileA (EnumPtr->Current->FindHandle, &EnumPtr->Current->FindData)) {
  1812. //
  1813. // Return directories only, then recurse into directory
  1814. //
  1815. EnumPtr->State = TREE_ENUM_DIRS_FILTER;
  1816. } else {
  1817. //
  1818. // Directory enumeration complete.
  1819. //
  1820. if (EnumPtr->EnumDirsFirst) {
  1821. pTrackedFindClose (EnumPtr->Current->FindHandle);
  1822. EnumPtr->State = TREE_ENUM_FILES_BEGIN;
  1823. } else {
  1824. EnumPtr->State = TREE_ENUM_POP;
  1825. }
  1826. }
  1827. break;
  1828. case TREE_ENUM_PUSH:
  1829. //
  1830. // Limit the length of the resulting full path. The nul is
  1831. // accounted for in SizeOfStringA, and the additional wack is
  1832. // accounted for by using >= instead of ==.
  1833. //
  1834. MYASSERT (ARRAYSIZE(EnumPtr->FileBuffer) == MAX_MBCHAR_PATH);
  1835. MYASSERT (sizeof (CHAR) == 1); // the math is totally dependent on this
  1836. if ((EnumPtr->EndOfFileBuffer - EnumPtr->FileBuffer) +
  1837. SizeOfStringA (EnumPtr->Current->FindData.cFileName) >= MAX_MBCHAR_PATH
  1838. ) {
  1839. LOGA ((LOG_ERROR, "Path %s\\%s is too long", EnumPtr->FileBuffer, EnumPtr->Current->FindData.cFileName));
  1840. EnumPtr->State = TREE_ENUM_DIRS_NEXT;
  1841. break;
  1842. }
  1843. if ((EnumPtr->EndOfPattern - EnumPtr->Pattern) +
  1844. SizeOfStringA (EnumPtr->Current->FindData.cFileName) >= MAX_MBCHAR_PATH
  1845. ) {
  1846. LOGA ((LOG_ERROR, "Path %s\\%s is too long", EnumPtr->Pattern, EnumPtr->Current->FindData.cFileName));
  1847. EnumPtr->State = TREE_ENUM_DIRS_NEXT;
  1848. break;
  1849. }
  1850. //
  1851. // Tack on directory name to strings and recalcuate end pointers
  1852. //
  1853. StringCopyA (EnumPtr->EndOfPattern + 1, EnumPtr->Current->FindData.cFileName);
  1854. StringCopyA (EnumPtr->EndOfFileBuffer, "\\");
  1855. StringCopyA (EnumPtr->EndOfFileBuffer + 1, EnumPtr->Current->FindData.cFileName);
  1856. EnumPtr->EndOfPattern = GetEndOfStringA (EnumPtr->EndOfPattern);
  1857. EnumPtr->EndOfFileBuffer = GetEndOfStringA (EnumPtr->EndOfFileBuffer);
  1858. //
  1859. // Allocate another find data struct
  1860. //
  1861. EnumPtr->Current = (PFIND_DATAA) GrowBuffer (
  1862. &EnumPtr->FindDataArray,
  1863. sizeof (FIND_DATAA)
  1864. );
  1865. if (!EnumPtr->Current) {
  1866. EnumPtr->State = TREE_ENUM_FAILED;
  1867. break;
  1868. }
  1869. EnumPtr->Level++;
  1870. EnumPtr->State = TREE_ENUM_BEGIN;
  1871. break;
  1872. case TREE_ENUM_POP:
  1873. //
  1874. // Free the current resources
  1875. //
  1876. pTrackedFindClose (EnumPtr->Current->FindHandle);
  1877. EnumPtr->Level--;
  1878. //
  1879. // Get the previous find data struct
  1880. //
  1881. MYASSERT (EnumPtr->FindDataArray.End >= sizeof (FIND_DATAA));
  1882. EnumPtr->FindDataArray.End -= sizeof (FIND_DATAA);
  1883. if (!EnumPtr->FindDataArray.End) {
  1884. EnumPtr->State = TREE_ENUM_DONE;
  1885. break;
  1886. }
  1887. EnumPtr->Current = (PFIND_DATAA) (EnumPtr->FindDataArray.Buf +
  1888. (EnumPtr->FindDataArray.End - sizeof (FIND_DATAA)));
  1889. //
  1890. // Restore the settings of the parent directory
  1891. //
  1892. EnumPtr->EndOfPattern = EnumPtr->Current->SavedEndOfPattern;
  1893. EnumPtr->EndOfFileBuffer = EnumPtr->Current->SavedEndOfFileBuffer;
  1894. if (EnumPtr->EnumDepthFirst) {
  1895. EnumPtr->State = TREE_ENUM_RETURN_ITEM;
  1896. }
  1897. else {
  1898. EnumPtr->State = TREE_ENUM_DIRS_NEXT;
  1899. }
  1900. break;
  1901. case TREE_ENUM_DONE:
  1902. AbortEnumFileInTreeA (EnumPtr);
  1903. SetLastError (ERROR_SUCCESS);
  1904. return FALSE;
  1905. case TREE_ENUM_FAILED:
  1906. PushError();
  1907. AbortEnumFileInTreeA (EnumPtr);
  1908. PopError();
  1909. return FALSE;
  1910. case TREE_ENUM_CLEANED_UP:
  1911. return FALSE;
  1912. }
  1913. }
  1914. }
  1915. BOOL
  1916. EnumNextFileInTreeW (
  1917. IN OUT PTREE_ENUMW EnumPtr
  1918. )
  1919. {
  1920. PWSTR p;
  1921. for (;;) {
  1922. switch (EnumPtr->State) {
  1923. case TREE_ENUM_INIT:
  1924. //
  1925. // Get rid of wack at the end of root path, if it exists
  1926. //
  1927. p = GetEndOfStringW (EnumPtr->RootPath);
  1928. p = _wcsdec2 (EnumPtr->RootPath, p);
  1929. if (!p) {
  1930. DEBUGMSG ((DBG_ERROR, "Path spec %ls is incomplete", EnumPtr->RootPath));
  1931. EnumPtr->State = TREE_ENUM_FAILED;
  1932. break;
  1933. }
  1934. if (*p == L'\\') {
  1935. *p = 0;
  1936. }
  1937. //
  1938. // Initialize enumeration structure
  1939. //
  1940. EnumPtr->FilePatternSize = SizeOfStringW (EnumPtr->FilePattern);
  1941. MYASSERT (sizeof (EnumPtr->FileBuffer) == sizeof (EnumPtr->RootPath));
  1942. StringCopyW (EnumPtr->FileBuffer, EnumPtr->RootPath);
  1943. EnumPtr->EndOfFileBuffer = GetEndOfStringW (EnumPtr->FileBuffer);
  1944. MYASSERT (sizeof (EnumPtr->Pattern) == sizeof (EnumPtr->RootPath));
  1945. StringCopyW (EnumPtr->Pattern, EnumPtr->RootPath);
  1946. EnumPtr->EndOfPattern = GetEndOfStringW (EnumPtr->Pattern);
  1947. EnumPtr->FullPath = EnumPtr->FileBuffer;
  1948. EnumPtr->RootPathSize = ByteCountW (EnumPtr->RootPath);
  1949. //
  1950. // Allocate first find data sturct
  1951. //
  1952. EnumPtr->Current = (PFIND_DATAW) GrowBuffer (
  1953. &EnumPtr->FindDataArray,
  1954. sizeof (FIND_DATAW)
  1955. );
  1956. if (!EnumPtr->Current) {
  1957. EnumPtr->State = TREE_ENUM_FAILED;
  1958. break;
  1959. }
  1960. #ifdef DEBUG
  1961. g_FileEnumResourcesInUse++; // account for grow buffer
  1962. #endif
  1963. EnumPtr->State = TREE_ENUM_BEGIN;
  1964. break;
  1965. case TREE_ENUM_BEGIN:
  1966. //
  1967. // Initialize current find data struct
  1968. //
  1969. EnumPtr->Current->SavedEndOfFileBuffer = EnumPtr->EndOfFileBuffer;
  1970. EnumPtr->Current->SavedEndOfPattern = EnumPtr->EndOfPattern;
  1971. //
  1972. // Limit the length of the pattern string. Calculate:
  1973. //
  1974. // pattern root + wack + file pattern + nul.
  1975. //
  1976. MYASSERT (ARRAYSIZE(EnumPtr->FileBuffer) == (MAX_PATH * 2));
  1977. if (((PBYTE) EnumPtr->EndOfPattern - (PBYTE) EnumPtr->Pattern) + sizeof (WCHAR) +
  1978. EnumPtr->FilePatternSize > (MAX_PATH * 2 * sizeof (WCHAR))
  1979. ) {
  1980. LOGW ((LOG_ERROR, "Path %s\\%s is too long", EnumPtr->Pattern, EnumPtr->FilePattern));
  1981. EnumPtr->State = TREE_ENUM_POP;
  1982. break;
  1983. }
  1984. //
  1985. // Enumerate the files or directories
  1986. //
  1987. if (EnumPtr->EnumDirsFirst) {
  1988. EnumPtr->State = TREE_ENUM_DIRS_BEGIN;
  1989. } else {
  1990. EnumPtr->State = TREE_ENUM_FILES_BEGIN;
  1991. }
  1992. break;
  1993. case TREE_ENUM_FILES_BEGIN:
  1994. //
  1995. // Begin enumeration of files
  1996. //
  1997. // This assert is valid because of length check in TREE_ENUM_BEGIN
  1998. MYASSERT ((TcharCountW (EnumPtr->Pattern) + 1 +
  1999. TcharCountW (EnumPtr->FilePattern)) < ARRAYSIZE(EnumPtr->Pattern)
  2000. );
  2001. StringCopyW (EnumPtr->EndOfPattern, L"\\");
  2002. StringCopyW (EnumPtr->EndOfPattern + 1, EnumPtr->FilePattern);
  2003. EnumPtr->Current->FindHandle = FindFirstFileW (
  2004. EnumPtr->Pattern,
  2005. &EnumPtr->Current->FindData
  2006. );
  2007. if (EnumPtr->Current->FindHandle == INVALID_HANDLE_VALUE) {
  2008. if (EnumPtr->EnumDirsFirst) {
  2009. EnumPtr->State = TREE_ENUM_POP;
  2010. } else {
  2011. EnumPtr->State = TREE_ENUM_DIRS_BEGIN;
  2012. }
  2013. } else {
  2014. #ifdef DEBUG
  2015. g_FileEnumResourcesInUse++; // account for creation of find handle
  2016. #endif
  2017. //
  2018. // Skip directories
  2019. //
  2020. if (EnumPtr->Current->FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
  2021. EnumPtr->State = TREE_ENUM_FILES_NEXT;
  2022. } else {
  2023. EnumPtr->State = TREE_ENUM_RETURN_ITEM;
  2024. }
  2025. }
  2026. break;
  2027. case TREE_ENUM_RETURN_ITEM:
  2028. //
  2029. // Update pointers to current item
  2030. //
  2031. EnumPtr->FindData = &EnumPtr->Current->FindData;
  2032. EnumPtr->Name = EnumPtr->FindData->cFileName;
  2033. EnumPtr->Directory = (EnumPtr->FindData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
  2034. //
  2035. // Limit the length of the resulting full path. Math is:
  2036. //
  2037. // file root + wack + file name + nul.
  2038. //
  2039. if (((PBYTE) EnumPtr->EndOfFileBuffer - (PBYTE) EnumPtr->FileBuffer) + sizeof (WCHAR) +
  2040. SizeOfStringW (EnumPtr->Name) >= (MAX_PATH * 2 * sizeof (WCHAR))
  2041. ) {
  2042. LOGW ((LOG_ERROR, "Path %s\\%s is too long", EnumPtr->FileBuffer, EnumPtr->Name));
  2043. if (EnumPtr->Directory) {
  2044. EnumPtr->State = TREE_ENUM_DIRS_NEXT;
  2045. } else {
  2046. EnumPtr->State = TREE_ENUM_FILES_NEXT;
  2047. }
  2048. break;
  2049. }
  2050. //
  2051. // Generate the full path
  2052. //
  2053. StringCopyW (EnumPtr->EndOfFileBuffer, L"\\");
  2054. StringCopyW (EnumPtr->EndOfFileBuffer + 1, EnumPtr->Name);
  2055. if (EnumPtr->Directory) {
  2056. if ((EnumPtr->MaxLevel == FILE_ENUM_ALL_LEVELS) ||
  2057. (EnumPtr->Level < EnumPtr->MaxLevel)
  2058. ) {
  2059. if (EnumPtr->EnumDepthFirst) {
  2060. EnumPtr->State = TREE_ENUM_DIRS_NEXT;
  2061. }
  2062. else {
  2063. EnumPtr->State = TREE_ENUM_PUSH;
  2064. }
  2065. }
  2066. else {
  2067. EnumPtr->State = TREE_ENUM_DIRS_NEXT;
  2068. }
  2069. } else {
  2070. EnumPtr->State = TREE_ENUM_FILES_NEXT;
  2071. }
  2072. EnumPtr->SubPath = (PCWSTR) ((PBYTE) EnumPtr->FileBuffer + EnumPtr->RootPathSize);
  2073. if (*EnumPtr->SubPath) {
  2074. EnumPtr->SubPath++; // advance beyond wack
  2075. }
  2076. return TRUE;
  2077. case TREE_ENUM_FILES_NEXT:
  2078. if (FindNextFileW (EnumPtr->Current->FindHandle, &EnumPtr->Current->FindData)) {
  2079. //
  2080. // Return files only
  2081. //
  2082. if (!(EnumPtr->Current->FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
  2083. EnumPtr->State = TREE_ENUM_RETURN_ITEM;
  2084. }
  2085. } else {
  2086. if (!EnumPtr->EnumDirsFirst) {
  2087. pTrackedFindClose (EnumPtr->Current->FindHandle);
  2088. EnumPtr->State = TREE_ENUM_DIRS_BEGIN;
  2089. } else {
  2090. EnumPtr->State = TREE_ENUM_POP;
  2091. }
  2092. }
  2093. break;
  2094. case TREE_ENUM_DIRS_FILTER:
  2095. if (!(EnumPtr->Current->FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
  2096. EnumPtr->State = TREE_ENUM_DIRS_NEXT;
  2097. } else if (StringMatchW (EnumPtr->Current->FindData.cFileName, L".") ||
  2098. StringMatchW (EnumPtr->Current->FindData.cFileName, L"..")
  2099. ) {
  2100. EnumPtr->State = TREE_ENUM_DIRS_NEXT;
  2101. } else {
  2102. if (EnumPtr->EnumDepthFirst) {
  2103. EnumPtr->State = TREE_ENUM_PUSH;
  2104. }
  2105. else {
  2106. EnumPtr->State = TREE_ENUM_RETURN_ITEM;
  2107. }
  2108. }
  2109. break;
  2110. case TREE_ENUM_DIRS_BEGIN:
  2111. //
  2112. // Begin enumeration of directories
  2113. //
  2114. if ((EnumPtr->EndOfPattern - EnumPtr->Pattern) + 4 >= (MAX_PATH * 2)) {
  2115. LOGW ((LOG_ERROR, "Path %s\\*.* is too long", EnumPtr->Pattern));
  2116. EnumPtr->State = TREE_ENUM_POP;
  2117. break;
  2118. }
  2119. StringCopyW (EnumPtr->EndOfPattern, L"\\*.*");
  2120. EnumPtr->Current->FindHandle = FindFirstFileW (
  2121. EnumPtr->Pattern,
  2122. &EnumPtr->Current->FindData
  2123. );
  2124. if (EnumPtr->Current->FindHandle == INVALID_HANDLE_VALUE) {
  2125. EnumPtr->State = TREE_ENUM_POP;
  2126. } else {
  2127. #ifdef DEBUG
  2128. g_FileEnumResourcesInUse++; // account for creation of find handle
  2129. #endif
  2130. EnumPtr->State = TREE_ENUM_DIRS_FILTER;
  2131. }
  2132. break;
  2133. case TREE_ENUM_DIRS_NEXT:
  2134. if (FindNextFileW (EnumPtr->Current->FindHandle, &EnumPtr->Current->FindData)) {
  2135. //
  2136. // Return directories only, then recurse into directory
  2137. //
  2138. EnumPtr->State = TREE_ENUM_DIRS_FILTER;
  2139. } else {
  2140. //
  2141. // Directory enumeration complete.
  2142. //
  2143. if (EnumPtr->EnumDirsFirst) {
  2144. pTrackedFindClose (EnumPtr->Current->FindHandle);
  2145. EnumPtr->State = TREE_ENUM_FILES_BEGIN;
  2146. } else {
  2147. EnumPtr->State = TREE_ENUM_POP;
  2148. }
  2149. }
  2150. break;
  2151. case TREE_ENUM_PUSH:
  2152. //
  2153. // Limit the length of the resulting full path. Math is:
  2154. //
  2155. // file root + wack + file name + nul.
  2156. //
  2157. if (((PBYTE) EnumPtr->EndOfFileBuffer - (PBYTE) EnumPtr->FileBuffer) + sizeof (WCHAR) +
  2158. SizeOfStringW (EnumPtr->Current->FindData.cFileName) >= (MAX_PATH * 2 * sizeof (WCHAR))
  2159. ) {
  2160. LOGW ((LOG_ERROR, "Path %s\\%s is too long", EnumPtr->FileBuffer, EnumPtr->Current->FindData.cFileName));
  2161. EnumPtr->State = TREE_ENUM_DIRS_NEXT;
  2162. break;
  2163. }
  2164. if ((EnumPtr->EndOfPattern - EnumPtr->Pattern) + 2 +
  2165. TcharCountW (EnumPtr->Current->FindData.cFileName) >= (MAX_PATH * 2)
  2166. ) {
  2167. LOGW ((LOG_ERROR, "Path %s\\%s is too long", EnumPtr->Pattern, EnumPtr->Current->FindData.cFileName));
  2168. EnumPtr->State = TREE_ENUM_DIRS_NEXT;
  2169. break;
  2170. }
  2171. //
  2172. // Tack on directory name to strings and recalcuate end pointers
  2173. //
  2174. StringCopyW (EnumPtr->EndOfPattern + 1, EnumPtr->Current->FindData.cFileName);
  2175. StringCopyW (EnumPtr->EndOfFileBuffer, L"\\");
  2176. StringCopyW (EnumPtr->EndOfFileBuffer + 1, EnumPtr->Current->FindData.cFileName);
  2177. EnumPtr->EndOfPattern = GetEndOfStringW (EnumPtr->EndOfPattern);
  2178. EnumPtr->EndOfFileBuffer = GetEndOfStringW (EnumPtr->EndOfFileBuffer);
  2179. //
  2180. // Allocate another find data struct
  2181. //
  2182. EnumPtr->Current = (PFIND_DATAW) GrowBuffer (
  2183. &EnumPtr->FindDataArray,
  2184. sizeof (FIND_DATAW)
  2185. );
  2186. if (!EnumPtr->Current) {
  2187. EnumPtr->State = TREE_ENUM_FAILED;
  2188. break;
  2189. }
  2190. EnumPtr->Level++;
  2191. EnumPtr->State = TREE_ENUM_BEGIN;
  2192. break;
  2193. case TREE_ENUM_POP:
  2194. //
  2195. // Free the current resources
  2196. //
  2197. pTrackedFindClose (EnumPtr->Current->FindHandle);
  2198. EnumPtr->Level--;
  2199. //
  2200. // Get the previous find data struct
  2201. //
  2202. MYASSERT (EnumPtr->FindDataArray.End >= sizeof (FIND_DATAW));
  2203. EnumPtr->FindDataArray.End -= sizeof (FIND_DATAW);
  2204. if (!EnumPtr->FindDataArray.End) {
  2205. EnumPtr->State = TREE_ENUM_DONE;
  2206. break;
  2207. }
  2208. EnumPtr->Current = (PFIND_DATAW) (EnumPtr->FindDataArray.Buf +
  2209. (EnumPtr->FindDataArray.End - sizeof (FIND_DATAW)));
  2210. //
  2211. // Restore the settings of the parent directory
  2212. //
  2213. EnumPtr->EndOfPattern = EnumPtr->Current->SavedEndOfPattern;
  2214. EnumPtr->EndOfFileBuffer = EnumPtr->Current->SavedEndOfFileBuffer;
  2215. if (EnumPtr->EnumDepthFirst) {
  2216. EnumPtr->State = TREE_ENUM_RETURN_ITEM;
  2217. }
  2218. else {
  2219. EnumPtr->State = TREE_ENUM_DIRS_NEXT;
  2220. }
  2221. break;
  2222. case TREE_ENUM_DONE:
  2223. AbortEnumFileInTreeW (EnumPtr);
  2224. SetLastError (ERROR_SUCCESS);
  2225. return FALSE;
  2226. case TREE_ENUM_FAILED:
  2227. PushError();
  2228. AbortEnumFileInTreeW (EnumPtr);
  2229. PopError();
  2230. return FALSE;
  2231. case TREE_ENUM_CLEANED_UP:
  2232. return FALSE;
  2233. }
  2234. }
  2235. }
  2236. VOID
  2237. AbortEnumFileInTreeA (
  2238. IN OUT PTREE_ENUMA EnumPtr
  2239. )
  2240. /*++
  2241. Routine Description:
  2242. AbortEnumFileInTreeA cleans up all resources used by an enumeration started
  2243. by EnumFirstFileInTree. This routine must be called if file enumeration
  2244. will not be completed by calling EnumNextFileInTree until it returns FALSE.
  2245. If EnumNextFileInTree returns FALSE, it is unnecessary (but harmless) to
  2246. call this function.
  2247. Arguments:
  2248. EnumPtr - Specifies the enumeration in progress, receives the enumerated file
  2249. or directory
  2250. Return Value:
  2251. none
  2252. --*/
  2253. {
  2254. UINT Pos;
  2255. PGROWBUFFER g;
  2256. PFIND_DATAA Current;
  2257. if (EnumPtr->State == TREE_ENUM_CLEANED_UP) {
  2258. return;
  2259. }
  2260. //
  2261. // Close any currently open handles
  2262. //
  2263. g = &EnumPtr->FindDataArray;
  2264. for (Pos = 0 ; Pos < g->End ; Pos += sizeof (FIND_DATAA)) {
  2265. Current = (PFIND_DATAA) (g->Buf + Pos);
  2266. pTrackedFindClose (Current->FindHandle);
  2267. }
  2268. FreeGrowBuffer (&EnumPtr->FindDataArray);
  2269. #ifdef DEBUG
  2270. g_FileEnumResourcesInUse--;
  2271. #endif
  2272. EnumPtr->State = TREE_ENUM_CLEANED_UP;
  2273. }
  2274. VOID
  2275. AbortEnumFileInTreeW (
  2276. IN OUT PTREE_ENUMW EnumPtr
  2277. )
  2278. /*++
  2279. Routine Description:
  2280. AbortEnumFileInTreeW cleans up all resources used by an enumeration started
  2281. by EnumFirstFileInTree. This routine must be called if file enumeration
  2282. will not be completed by calling EnumNextFileInTree until it returns FALSE.
  2283. If EnumNextFileInTree returns FALSE, it is unnecessary (but harmless) to
  2284. call this function.
  2285. Arguments:
  2286. EnumPtr - Specifies the enumeration in progress, receives the enumerated file
  2287. or directory
  2288. Return Value:
  2289. none
  2290. --*/
  2291. {
  2292. UINT Pos;
  2293. PGROWBUFFER g;
  2294. PFIND_DATAW Current;
  2295. if (EnumPtr->State == TREE_ENUM_CLEANED_UP) {
  2296. return;
  2297. }
  2298. //
  2299. // Close any currently open handles
  2300. //
  2301. g = &EnumPtr->FindDataArray;
  2302. for (Pos = 0 ; Pos < g->End ; Pos += sizeof (FIND_DATAW)) {
  2303. Current = (PFIND_DATAW) (g->Buf + Pos);
  2304. pTrackedFindClose (Current->FindHandle);
  2305. }
  2306. FreeGrowBuffer (&EnumPtr->FindDataArray);
  2307. #ifdef DEBUG
  2308. g_FileEnumResourcesInUse--;
  2309. #endif
  2310. EnumPtr->State = TREE_ENUM_CLEANED_UP;
  2311. }
  2312. VOID
  2313. AbortEnumCurrentDirA (
  2314. IN OUT PTREE_ENUMA EnumPtr
  2315. )
  2316. /*++
  2317. Routine Description:
  2318. AbortEnumCurrentDirA discontinues enumeration of the current subdirectory,
  2319. continuing enumeration at its parent.
  2320. Arguments:
  2321. EnumPtr - Specifies the enumeration in progress, receives updated state
  2322. machine position.
  2323. Return Value:
  2324. None.
  2325. --*/
  2326. {
  2327. if (EnumPtr->State == TREE_ENUM_PUSH) {
  2328. EnumPtr->State = TREE_ENUM_DIRS_NEXT;
  2329. }
  2330. }
  2331. VOID
  2332. AbortEnumCurrentDirW (
  2333. IN OUT PTREE_ENUMW EnumPtr
  2334. )
  2335. /*++
  2336. Routine Description:
  2337. AbortEnumCurrentDirW discontinues enumeration of the current subdirectory,
  2338. continuing enumeration at its parent.
  2339. Arguments:
  2340. EnumPtr - Specifies the enumeration in progress, receives updated state
  2341. machine position.
  2342. Return Value:
  2343. None.
  2344. --*/
  2345. {
  2346. if (EnumPtr->State == TREE_ENUM_PUSH) {
  2347. EnumPtr->State = TREE_ENUM_DIRS_NEXT;
  2348. }
  2349. }
  2350. BOOL
  2351. EnumFirstFileA (
  2352. OUT PFILE_ENUMA EnumPtr,
  2353. IN PCSTR RootPath,
  2354. IN PCSTR FilePattern OPTIONAL
  2355. )
  2356. /*++
  2357. Routine Description:
  2358. EnumFirstFileA enumerates file names/subdirectory names in a specified
  2359. subdirectory. It does not enumerate the contents of subdirectories.
  2360. The output is limited to paths that fit in MAX_PATH.
  2361. Arguments:
  2362. EnumPtr - Receives the enumeration output
  2363. RootPath - Specifies the path to enumerate
  2364. FilePattern - Specifies the pattern of files to enumerate within RootPath
  2365. Return Value:
  2366. TRUE if a file or subdirectory was found, FALSE otherwise.
  2367. NOTE: There is no need to call AbortFileEnumA on return of FALSE.
  2368. --*/
  2369. {
  2370. UINT patternTchars;
  2371. ZeroMemory (EnumPtr, sizeof (FILE_ENUMA));
  2372. EnumPtr->FileName = EnumPtr->fd.cFileName;
  2373. EnumPtr->FullPath = EnumPtr->RootPath;
  2374. if (!RootPath) {
  2375. MYASSERT (FALSE);
  2376. return FALSE;
  2377. }
  2378. if (FilePattern) {
  2379. patternTchars = TcharCountA (FilePattern) + 1;
  2380. } else {
  2381. patternTchars = 4; // number of tchars in \*.*
  2382. }
  2383. patternTchars += TcharCountA (RootPath);
  2384. if (patternTchars >= ARRAYSIZE (EnumPtr->RootPath)) {
  2385. LOGA ((LOG_ERROR, "Enumeration path is too long: %s\\%s", RootPath, FilePattern ? FilePattern : "*.*"));
  2386. return FALSE;
  2387. }
  2388. StringCopyA (EnumPtr->RootPath, RootPath);
  2389. EnumPtr->EndOfRoot = AppendWackA (EnumPtr->RootPath);
  2390. StringCopyA (EnumPtr->EndOfRoot, FilePattern ? FilePattern : "*.*");
  2391. EnumPtr->Handle = FindFirstFileA (EnumPtr->RootPath, &EnumPtr->fd);
  2392. if (EnumPtr->Handle != INVALID_HANDLE_VALUE) {
  2393. if (StringMatchA (EnumPtr->FileName, ".") ||
  2394. StringMatchA (EnumPtr->FileName, "..")
  2395. ) {
  2396. return EnumNextFileA (EnumPtr);
  2397. }
  2398. patternTchars = (UINT) (UINT_PTR) (EnumPtr->EndOfRoot - EnumPtr->RootPath);
  2399. patternTchars += TcharCountA (EnumPtr->FileName);
  2400. if (patternTchars >= ARRAYSIZE (EnumPtr->RootPath)) {
  2401. LOGA ((LOG_ERROR, "Enumeration item is too long: %s\\%s", EnumPtr->RootPath, EnumPtr->FileName));
  2402. return EnumNextFileA (EnumPtr);
  2403. }
  2404. StringCopyA (EnumPtr->EndOfRoot, EnumPtr->FileName);
  2405. EnumPtr->Directory = EnumPtr->fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
  2406. return TRUE;
  2407. }
  2408. return FALSE;
  2409. }
  2410. BOOL
  2411. EnumFirstFileW (
  2412. OUT PFILE_ENUMW EnumPtr,
  2413. IN PCWSTR RootPath,
  2414. IN PCWSTR FilePattern OPTIONAL
  2415. )
  2416. /*++
  2417. Routine Description:
  2418. EnumFirstFileW enumerates file names/subdirectory names in a specified
  2419. subdirectory. It does not enumerate the contents of subdirectories.
  2420. The output is limited to paths that fit in MAX_PATH.
  2421. Arguments:
  2422. EnumPtr - Receives the enumeration output
  2423. RootPath - Specifies the path to enumerate
  2424. FilePattern - Specifies the pattern of files to enumerate within RootPath
  2425. Return Value:
  2426. TRUE if a file or subdirectory was found, FALSE otherwise.
  2427. NOTE: There is no need to call AbortFileEnumW on return of FALSE.
  2428. --*/
  2429. {
  2430. UINT patternTchars;
  2431. ZeroMemory (EnumPtr, sizeof (FILE_ENUMW));
  2432. EnumPtr->FileName = EnumPtr->fd.cFileName;
  2433. EnumPtr->FullPath = EnumPtr->RootPath;
  2434. if (!RootPath) {
  2435. MYASSERT (FALSE);
  2436. return FALSE;
  2437. }
  2438. if (FilePattern) {
  2439. patternTchars = TcharCountW (FilePattern) + 1;
  2440. } else {
  2441. patternTchars = 4; // number of tchars in \*.*
  2442. }
  2443. patternTchars += TcharCountW (RootPath);
  2444. if (patternTchars >= ARRAYSIZE (EnumPtr->RootPath)) {
  2445. LOGW ((LOG_ERROR, "Enumeration path is too long: %s\\%s", RootPath, FilePattern ? FilePattern : L"*.*"));
  2446. return FALSE;
  2447. }
  2448. StringCopyW (EnumPtr->RootPath, RootPath);
  2449. EnumPtr->EndOfRoot = AppendWackW (EnumPtr->RootPath);
  2450. StringCopyW (EnumPtr->EndOfRoot, FilePattern ? FilePattern : L"*.*");
  2451. EnumPtr->Handle = FindFirstFileW (EnumPtr->RootPath, &EnumPtr->fd);
  2452. if (EnumPtr->Handle != INVALID_HANDLE_VALUE) {
  2453. if (StringMatchW (EnumPtr->FileName, L".") ||
  2454. StringMatchW (EnumPtr->FileName, L"..")
  2455. ) {
  2456. return EnumNextFileW (EnumPtr);
  2457. }
  2458. patternTchars = (UINT) (UINT_PTR) (EnumPtr->EndOfRoot - EnumPtr->RootPath);
  2459. patternTchars += TcharCountW (EnumPtr->FileName);
  2460. if (patternTchars >= ARRAYSIZE (EnumPtr->RootPath)) {
  2461. LOGW ((LOG_ERROR, "Enumeration item is too long: %s\\%s", EnumPtr->RootPath, EnumPtr->FileName));
  2462. return EnumNextFileW (EnumPtr);
  2463. }
  2464. StringCopyW (EnumPtr->EndOfRoot, EnumPtr->FileName);
  2465. EnumPtr->Directory = EnumPtr->fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
  2466. return TRUE;
  2467. }
  2468. return FALSE;
  2469. }
  2470. BOOL
  2471. EnumNextFileA (
  2472. IN OUT PFILE_ENUMA EnumPtr
  2473. )
  2474. /*++
  2475. Routine Description:
  2476. EnumNextFileA continues enumeration of file names/subdirectory names in a
  2477. specified subdirectory. It does not enumerate the contents of
  2478. subdirectories.
  2479. The output is limited to paths that fit in MAX_PATH.
  2480. Arguments:
  2481. EnumPtr - Specifies the previous enumeration state, receives the enumeration
  2482. output
  2483. Return Value:
  2484. TRUE if a file or subdirectory was found, FALSE otherwise.
  2485. NOTE: There is no need to call AbortFileEnumA on return of FALSE.
  2486. --*/
  2487. {
  2488. UINT patternTchars;
  2489. do {
  2490. if (!FindNextFileA (EnumPtr->Handle, &EnumPtr->fd)) {
  2491. AbortFileEnumA (EnumPtr);
  2492. return FALSE;
  2493. }
  2494. patternTchars = (UINT) (UINT_PTR) (EnumPtr->EndOfRoot - EnumPtr->RootPath);
  2495. patternTchars += TcharCountA (EnumPtr->FileName);
  2496. if (patternTchars >= ARRAYSIZE (EnumPtr->RootPath)) {
  2497. LOGA ((LOG_ERROR, "Enumeration path is too long: %s\\%s", EnumPtr->RootPath, EnumPtr->FileName));
  2498. continue;
  2499. }
  2500. } while (StringMatchA (EnumPtr->FileName, ".") ||
  2501. StringMatchA (EnumPtr->FileName, "..")
  2502. );
  2503. StringCopyA (EnumPtr->EndOfRoot, EnumPtr->FileName);
  2504. EnumPtr->Directory = EnumPtr->fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
  2505. return TRUE;
  2506. }
  2507. BOOL
  2508. EnumNextFileW (
  2509. IN OUT PFILE_ENUMW EnumPtr
  2510. )
  2511. /*++
  2512. Routine Description:
  2513. EnumNextFileW continues enumeration of file names/subdirectory names in a
  2514. specified subdirectory. It does not enumerate the contents of
  2515. subdirectories.
  2516. The output is limited to paths that fit in MAX_PATH.
  2517. Arguments:
  2518. EnumPtr - Specifies the previous enumeration state, receives the enumeration
  2519. output
  2520. Return Value:
  2521. TRUE if a file or subdirectory was found, FALSE otherwise.
  2522. NOTE: There is no need to call AbortFileEnumW on return of FALSE.
  2523. --*/
  2524. {
  2525. UINT patternTchars;
  2526. do {
  2527. if (!FindNextFileW (EnumPtr->Handle, &EnumPtr->fd)) {
  2528. AbortFileEnumW (EnumPtr);
  2529. return FALSE;
  2530. }
  2531. patternTchars = (UINT) (UINT_PTR) (EnumPtr->EndOfRoot - EnumPtr->RootPath);
  2532. patternTchars += TcharCountW (EnumPtr->FileName);
  2533. if (patternTchars >= ARRAYSIZE (EnumPtr->RootPath)) {
  2534. LOGW ((LOG_ERROR, "Enumeration path is too long: %s\\%s", EnumPtr->RootPath, EnumPtr->FileName));
  2535. continue;
  2536. }
  2537. } while (StringMatchW (EnumPtr->FileName, L".") ||
  2538. StringMatchW (EnumPtr->FileName, L"..")
  2539. );
  2540. if (!FindNextFileW (EnumPtr->Handle, &EnumPtr->fd)) {
  2541. AbortFileEnumW (EnumPtr);
  2542. return FALSE;
  2543. }
  2544. StringCopyW (EnumPtr->EndOfRoot, EnumPtr->FileName);
  2545. EnumPtr->Directory = EnumPtr->fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
  2546. return TRUE;
  2547. }
  2548. VOID
  2549. AbortFileEnumA (
  2550. IN OUT PFILE_ENUMA EnumPtr
  2551. )
  2552. /*++
  2553. Routine Description:
  2554. AbortFileEnumA stops an incomplete enumeration and cleans up its resources.
  2555. This function is intended for the case in which some but not all of the
  2556. matches are enumerated. In other words, enumerations of all items do not
  2557. need to be aborted.
  2558. Arguments:
  2559. EnumPtr - Specifies the previous enumeration state, receives a zeroed struct
  2560. Return Value:
  2561. None.
  2562. --*/
  2563. {
  2564. if (EnumPtr->Handle && EnumPtr->Handle != INVALID_HANDLE_VALUE) {
  2565. FindClose (EnumPtr->Handle);
  2566. ZeroMemory (EnumPtr, sizeof (FILE_ENUMA));
  2567. }
  2568. }
  2569. VOID
  2570. AbortFileEnumW (
  2571. IN OUT PFILE_ENUMW EnumPtr
  2572. )
  2573. /*++
  2574. Routine Description:
  2575. AbortFileEnumW stops an incomplete enumeration and cleans up its resources.
  2576. This function is intended for the case in which some but not all of the
  2577. matches are enumerated. In other words, enumerations of all items do not
  2578. need to be aborted.
  2579. Arguments:
  2580. EnumPtr - Specifies the previous enumeration state, receives a zeroed struct
  2581. Return Value:
  2582. None.
  2583. --*/
  2584. {
  2585. if (EnumPtr->Handle && EnumPtr->Handle != INVALID_HANDLE_VALUE) {
  2586. FindClose (EnumPtr->Handle);
  2587. ZeroMemory (EnumPtr, sizeof (FILE_ENUMW));
  2588. }
  2589. }
  2590. PVOID
  2591. MapFileIntoMemoryExA (
  2592. IN PCSTR FileName,
  2593. OUT PHANDLE FileHandle,
  2594. OUT PHANDLE MapHandle,
  2595. IN BOOL WriteAccess
  2596. )
  2597. /*++
  2598. Routine Description:
  2599. MapFileIntoMemoryA maps a file into memory. It does that by opening the
  2600. file, creating a mapping object and mapping opened file into created mapping
  2601. object. It returns the address where the file is mapped and also sets
  2602. FileHandle and MapHandle variables to be used in order to unmap the file
  2603. when work is done.
  2604. Arguments:
  2605. FileName - Specifies the name of the file to be mapped into memory
  2606. FileHandle - Receives the file handle on success
  2607. MapHandle - Receives the map handle on success
  2608. WriteAccess - Specifies TRUE to create a read/write mapping, or FALSE to
  2609. create a read-only mapping.
  2610. Return Value:
  2611. NULL if function fails, a valid memory address if successful.
  2612. Call UnmapFile to release all allocated resources, even if the return value
  2613. is NULL.
  2614. --*/
  2615. {
  2616. PVOID fileImage = NULL;
  2617. //
  2618. // verify function parameters
  2619. //
  2620. if ((FileHandle == NULL) || (MapHandle == NULL)) {
  2621. return NULL;
  2622. }
  2623. //
  2624. // Try to open the file
  2625. //
  2626. *FileHandle = CreateFileA (
  2627. FileName,
  2628. WriteAccess?GENERIC_READ|GENERIC_WRITE:GENERIC_READ,
  2629. FILE_SHARE_READ,
  2630. NULL,
  2631. OPEN_EXISTING,
  2632. FILE_ATTRIBUTE_NORMAL,
  2633. NULL
  2634. );
  2635. if (*FileHandle == INVALID_HANDLE_VALUE) {
  2636. return NULL;
  2637. }
  2638. //
  2639. // now try to create a mapping object
  2640. //
  2641. *MapHandle = CreateFileMappingA (
  2642. *FileHandle,
  2643. NULL,
  2644. WriteAccess?PAGE_READWRITE:PAGE_READONLY,
  2645. 0,
  2646. 0,
  2647. NULL
  2648. );
  2649. if (*MapHandle == NULL) {
  2650. return NULL;
  2651. }
  2652. //
  2653. // map view of file
  2654. //
  2655. fileImage = MapViewOfFile (*MapHandle, WriteAccess?FILE_MAP_WRITE:FILE_MAP_READ, 0, 0, 0);
  2656. return fileImage;
  2657. }
  2658. PVOID
  2659. MapFileIntoMemoryExW (
  2660. IN PCWSTR FileName,
  2661. OUT PHANDLE FileHandle,
  2662. OUT PHANDLE MapHandle,
  2663. IN BOOL WriteAccess
  2664. )
  2665. /*++
  2666. Routine Description:
  2667. MapFileIntoMemoryW maps a file into memory. It does that by opening the
  2668. file, creating a mapping object and mapping opened file into created mapping
  2669. object. It returns the address where the file is mapped and also sets
  2670. FileHandle and MapHandle variables to be used in order to unmap the file
  2671. when work is done.
  2672. Arguments:
  2673. FileName - Specifies the name of the file to be mapped into memory
  2674. FileHandle - Receives the file handle on success
  2675. MapHandle - Receives the map handle on success
  2676. WriteAccess - Specifies TRUE to create a read/write mapping, or FALSE to
  2677. create a read-only mapping.
  2678. Return Value:
  2679. NULL if function fails, a valid memory address if successful.
  2680. Call UnmapFile to release all allocated resources, even if the return value
  2681. is NULL.
  2682. --*/
  2683. {
  2684. PVOID fileImage = NULL;
  2685. //
  2686. // verify function parameters
  2687. //
  2688. if ((FileHandle == NULL) || (MapHandle == NULL)) {
  2689. return NULL;
  2690. }
  2691. //
  2692. // Try to open the file, read-only
  2693. //
  2694. *FileHandle = CreateFileW (
  2695. FileName,
  2696. WriteAccess?GENERIC_READ|GENERIC_WRITE:GENERIC_READ,
  2697. FILE_SHARE_READ,
  2698. NULL,
  2699. OPEN_EXISTING,
  2700. FILE_ATTRIBUTE_NORMAL,
  2701. NULL
  2702. );
  2703. if (*FileHandle == INVALID_HANDLE_VALUE) {
  2704. return NULL;
  2705. }
  2706. //
  2707. // now try to create a mapping object
  2708. //
  2709. *MapHandle = CreateFileMappingW (
  2710. *FileHandle,
  2711. NULL,
  2712. WriteAccess ? PAGE_READWRITE : PAGE_READONLY,
  2713. 0,
  2714. 0,
  2715. NULL
  2716. );
  2717. if (*MapHandle == NULL) {
  2718. return NULL;
  2719. }
  2720. //
  2721. // map view of file
  2722. //
  2723. fileImage = MapViewOfFile (*MapHandle, WriteAccess?FILE_MAP_WRITE:FILE_MAP_READ, 0, 0, 0);
  2724. return fileImage;
  2725. }
  2726. BOOL
  2727. UnmapFile (
  2728. IN PVOID FileImage, OPTIONAL
  2729. IN HANDLE MapHandle, OPTIONAL
  2730. IN HANDLE FileHandle OPTIONAL
  2731. )
  2732. /*++
  2733. Routine Description:
  2734. UnmapFile is used to release all resources allocated by MapFileIntoMemory.
  2735. Arguments:
  2736. FileImage - Specifies the image of the mapped file as returned by
  2737. MapFileIntoMemoryExA/W
  2738. MapHandle - Specifies the handle of the mapping object as returned by
  2739. MapFileIntoMemoryExA/W
  2740. FileHandle - Specifies the handle of the file as returned by
  2741. MapFileIntoMemoryExA/W
  2742. Return Value:
  2743. TRUE if successful, FALSE if not
  2744. --*/
  2745. {
  2746. BOOL result = TRUE;
  2747. //
  2748. // if FileImage is a valid pointer then try to unmap file
  2749. //
  2750. if (FileImage != NULL) {
  2751. if (UnmapViewOfFile (FileImage) == 0) {
  2752. result = FALSE;
  2753. }
  2754. }
  2755. //
  2756. // if mapping object is valid then try to delete it
  2757. //
  2758. if (MapHandle != NULL) {
  2759. if (CloseHandle (MapHandle) == 0) {
  2760. result = FALSE;
  2761. }
  2762. }
  2763. //
  2764. // if file handle is valid then try to close the file
  2765. //
  2766. if (FileHandle && FileHandle != INVALID_HANDLE_VALUE) {
  2767. if (CloseHandle (FileHandle) == 0) {
  2768. result = FALSE;
  2769. }
  2770. }
  2771. return result;
  2772. }
  2773. BOOL
  2774. RemoveCompleteDirectoryA (
  2775. IN PCSTR Dir
  2776. )
  2777. /*++
  2778. Routine Description:
  2779. RemoveCompleteDirectoryA enumerates the file system and obliterates all
  2780. files and subdirectories within the specified path. It resets file
  2781. attributes to normal prior to deleting. It does not change ACLs however.
  2782. Any files that cannot be deleted (e.g., ACLs are different) are left on the
  2783. system unchanged.
  2784. This function is limited to MAX_PATH.
  2785. Arguments:
  2786. Dir - Specifies the directory to remove.
  2787. Return Value:
  2788. TRUE on complete removal of the directory, FALSE if at least one
  2789. subdirectory still remains. GetLastError() returns the error code of the
  2790. first failure encountered.
  2791. --*/
  2792. {
  2793. TREE_ENUMA e;
  2794. BOOL b = TRUE;
  2795. CHAR CurDir[MAX_MBCHAR_PATH];
  2796. CHAR NewDir[MAX_MBCHAR_PATH];
  2797. LONG rc = ERROR_SUCCESS;
  2798. DWORD Attribs;
  2799. //
  2800. // Validate
  2801. //
  2802. if (!IsPathLengthOkA (Dir)) {
  2803. LOGA ((LOG_ERROR, "Can't remove very long dir: %s", Dir));
  2804. return FALSE;
  2805. }
  2806. //
  2807. // Capture attributes and check for existence
  2808. //
  2809. Attribs = GetFileAttributesA (Dir);
  2810. if (Attribs == INVALID_ATTRIBUTES) {
  2811. return TRUE;
  2812. }
  2813. //
  2814. // If it's a file, delete it
  2815. //
  2816. if (!(Attribs & FILE_ATTRIBUTE_DIRECTORY)) {
  2817. SetFileAttributesA (Dir, FILE_ATTRIBUTE_NORMAL);
  2818. return DeleteFileA (Dir);
  2819. }
  2820. //
  2821. // Set the current directory to the specified path, so the current dir is
  2822. // not keeping us from removing the dir. Then get the current directory in
  2823. // NewDir (to sanitize it).
  2824. //
  2825. GetCurrentDirectoryA (ARRAYSIZE(CurDir), CurDir);
  2826. SetCurrentDirectoryA (Dir);
  2827. GetCurrentDirectoryA (ARRAYSIZE(NewDir), NewDir);
  2828. //
  2829. // Enumerate the file system and delete all the files. Record failures
  2830. // along the way in the log. Keep the first error code.
  2831. //
  2832. if (EnumFirstFileInTreeA (&e, NewDir, NULL, FALSE)) {
  2833. do {
  2834. if (!e.Directory) {
  2835. SetFileAttributesA (e.FullPath, FILE_ATTRIBUTE_NORMAL);
  2836. if (!DeleteFileA (e.FullPath)) {
  2837. DEBUGMSGA ((DBG_ERROR, "Can't delete %s", e.FullPath));
  2838. if (b) {
  2839. b = FALSE;
  2840. rc = GetLastError();
  2841. }
  2842. }
  2843. }
  2844. } while (EnumNextFileInTreeA (&e));
  2845. }
  2846. //
  2847. // Enumerate the file system again (dirs first this time) and delete the
  2848. // dirs. Record failures along the way. Keep the first error code.
  2849. //
  2850. if (EnumFirstFileInTreeExA (&e, NewDir, NULL, TRUE, TRUE, FILE_ENUM_ALL_LEVELS)) {
  2851. do {
  2852. if (e.Directory) {
  2853. SetFileAttributesA (e.FullPath, FILE_ATTRIBUTE_NORMAL);
  2854. if (!RemoveDirectoryA (e.FullPath)) {
  2855. DEBUGMSGA ((DBG_ERROR, "Can't remove %s", e.FullPath));
  2856. if (b) {
  2857. b = FALSE;
  2858. rc = GetLastError();
  2859. }
  2860. }
  2861. }
  2862. } while (EnumNextFileInTreeA (&e));
  2863. }
  2864. if (b) {
  2865. //
  2866. // Try to remove the directory itself
  2867. //
  2868. SetFileAttributesA (NewDir, FILE_ATTRIBUTE_NORMAL);
  2869. SetCurrentDirectoryA ("..");
  2870. b = RemoveDirectoryA (NewDir);
  2871. }
  2872. if (!b && rc == ERROR_SUCCESS) {
  2873. //
  2874. // Capture the error
  2875. //
  2876. rc = GetLastError();
  2877. MYASSERT (rc != ERROR_SUCCESS);
  2878. }
  2879. SetCurrentDirectoryA (CurDir);
  2880. SetLastError (rc);
  2881. return b;
  2882. }
  2883. BOOL
  2884. RemoveCompleteDirectoryW (
  2885. IN PCWSTR Dir
  2886. )
  2887. /*++
  2888. Routine Description:
  2889. RemoveCompleteDirectoryW enumerates the file system and obliterates all
  2890. files and subdirectories within the specified path. It resets file
  2891. attributes to normal prior to deleting. It does not change ACLs however.
  2892. Any files that cannot be deleted (e.g., ACLs are different) are left on the
  2893. system unchanged.
  2894. This function is limited to MAX_PATH * 2.
  2895. Arguments:
  2896. Dir - Specifies the directory to remove.
  2897. Return Value:
  2898. TRUE on complete removal of the directory, FALSE if at least one
  2899. subdirectory still remains. GetLastError() returns the error code of the
  2900. first failure encountered.
  2901. --*/
  2902. {
  2903. TREE_ENUMW e;
  2904. BOOL b = TRUE;
  2905. WCHAR CurDir[MAX_PATH * 2];
  2906. WCHAR NewDir[MAX_PATH * 2];
  2907. LONG rc = ERROR_SUCCESS;
  2908. DWORD Attribs;
  2909. //
  2910. // Capture attributes and check for existence
  2911. //
  2912. Attribs = GetLongPathAttributesW (Dir);
  2913. if (Attribs == INVALID_ATTRIBUTES) {
  2914. return TRUE;
  2915. }
  2916. //
  2917. // If path is a file, delete the file
  2918. //
  2919. if (!(Attribs & FILE_ATTRIBUTE_DIRECTORY)) {
  2920. SetLongPathAttributesW (Dir, FILE_ATTRIBUTE_NORMAL);
  2921. return DeleteLongPathW (Dir);
  2922. }
  2923. //
  2924. // Move the current directory outside of the path, to keep us from failing
  2925. // the delete because our own current dir is in the path. Fetch the
  2926. // sanitized path.
  2927. //
  2928. // BUGBUG - Does this behave properly with extended (e.g., \\?\) paths?
  2929. GetCurrentDirectoryW (ARRAYSIZE(CurDir), CurDir);
  2930. SetCurrentDirectoryW (Dir);
  2931. GetCurrentDirectoryW (ARRAYSIZE(NewDir), NewDir);
  2932. //
  2933. // Enumerate the file system again (dirs first this time) and delete the
  2934. // dirs. Record failures along the way. Keep the first error code.
  2935. //
  2936. // CAUTION: Enum is limited to MAX_PATH * 2
  2937. //
  2938. MYASSERT (ARRAYSIZE(e.FileBuffer) >= MAX_PATH * 2);
  2939. if (EnumFirstFileInTreeW (&e, NewDir, NULL, FALSE)) {
  2940. do {
  2941. if (!e.Directory) {
  2942. SetLongPathAttributesW (e.FullPath, FILE_ATTRIBUTE_NORMAL);
  2943. if (!DeleteLongPathW (e.FullPath)) {
  2944. DEBUGMSGW ((DBG_ERROR, "Can't delete %s", e.FullPath));
  2945. if (b) {
  2946. b = FALSE;
  2947. rc = GetLastError();
  2948. }
  2949. }
  2950. }
  2951. } while (EnumNextFileInTreeW (&e));
  2952. }
  2953. //
  2954. // Enumerate the file system again (dirs first this time) and delete the
  2955. // dirs. Record failures along the way. Keep the first error code.
  2956. //
  2957. if (EnumFirstFileInTreeExW (&e, NewDir, NULL, TRUE, TRUE, FILE_ENUM_ALL_LEVELS)) {
  2958. do {
  2959. if (e.Directory) {
  2960. SetLongPathAttributesW (e.FullPath, FILE_ATTRIBUTE_NORMAL);
  2961. if (!RemoveDirectoryW (e.FullPath)) {
  2962. DEBUGMSGW ((DBG_ERROR, "Can't remove %s", e.FullPath));
  2963. if (b) {
  2964. b = FALSE;
  2965. rc = GetLastError();
  2966. }
  2967. }
  2968. }
  2969. } while (EnumNextFileInTreeW (&e));
  2970. }
  2971. if (b) {
  2972. //
  2973. // Try to remove the directory itself
  2974. //
  2975. SetLongPathAttributesW (NewDir, FILE_ATTRIBUTE_NORMAL);
  2976. SetCurrentDirectoryW (L"..");
  2977. b = RemoveDirectoryW (NewDir);
  2978. }
  2979. if (!b && rc == ERROR_SUCCESS) {
  2980. //
  2981. // Capture the error
  2982. //
  2983. rc = GetLastError();
  2984. }
  2985. SetCurrentDirectoryW (CurDir);
  2986. SetLastError (rc);
  2987. return b;
  2988. }
  2989. PCMDLINEA
  2990. ParseCmdLineA (
  2991. IN PCSTR CmdLine,
  2992. IN OUT PGROWBUFFER Buffer
  2993. )
  2994. {
  2995. GROWBUFFER SpacePtrs = GROWBUF_INIT;
  2996. PCSTR p;
  2997. PSTR q;
  2998. INT Count;
  2999. INT i;
  3000. INT j;
  3001. PSTR *Array;
  3002. PCSTR Start;
  3003. CHAR OldChar = 0;
  3004. GROWBUFFER StringBuf = GROWBUF_INIT;
  3005. PBYTE CopyBuf;
  3006. PCMDLINEA CmdLineTable;
  3007. PCMDLINEARGA CmdLineArg;
  3008. UINT_PTR Base;
  3009. CHAR Path[MAX_MBCHAR_PATH];
  3010. CHAR UnquotedPath[MAX_MBCHAR_PATH];
  3011. CHAR FixedFileName[MAX_MBCHAR_PATH];
  3012. PCSTR FullPath = NULL;
  3013. DWORD Attribs = INVALID_ATTRIBUTES;
  3014. PSTR CmdLineCopy;
  3015. BOOL Quoted;
  3016. UINT OriginalArgOffset = 0;
  3017. UINT CleanedUpArgOffset = 0;
  3018. BOOL GoodFileFound = FALSE;
  3019. PSTR DontCare;
  3020. CHAR FirstArgPath[MAX_MBCHAR_PATH];
  3021. PSTR EndOfFirstArg;
  3022. BOOL QuoteMode = FALSE;
  3023. PSTR End;
  3024. CmdLineCopy = DuplicateTextA (CmdLine);
  3025. //
  3026. // Build an array of places to break the string
  3027. //
  3028. for (p = CmdLineCopy ; *p ; p = _mbsinc (p)) {
  3029. if (_mbsnextc (p) == '\"') {
  3030. QuoteMode = !QuoteMode;
  3031. } else if (!QuoteMode && (_mbsnextc (p) == ' ' || _mbsnextc (p) == '=')) {
  3032. //
  3033. // Remove excess spaces
  3034. //
  3035. q = (PSTR) p + 1;
  3036. while (_mbsnextc (q) == ' ') {
  3037. q++;
  3038. }
  3039. if (q > p + 1) {
  3040. MoveMemory ((PBYTE) p + sizeof (CHAR), q, SizeOfStringA (q));
  3041. }
  3042. GrowBufAppendUintPtr (&SpacePtrs, (UINT_PTR) p);
  3043. }
  3044. }
  3045. //
  3046. // Prepare the CMDLINE struct
  3047. //
  3048. CmdLineTable = (PCMDLINEA) GrowBuffer (Buffer, sizeof (CMDLINEA));
  3049. MYASSERT (CmdLineTable);
  3050. //
  3051. // NOTE: We store string offsets, then at the end resolve them
  3052. // to pointers later.
  3053. //
  3054. CmdLineTable->CmdLine = (PCSTR) (UINT_PTR) StringBuf.End;
  3055. MultiSzAppendA (&StringBuf, CmdLine);
  3056. CmdLineTable->ArgCount = 0;
  3057. //
  3058. // Now test every combination, emulating CreateProcess
  3059. //
  3060. Count = SpacePtrs.End / sizeof (UINT_PTR);
  3061. Array = (PSTR *) SpacePtrs.Buf;
  3062. i = -1;
  3063. EndOfFirstArg = NULL;
  3064. while (i < Count) {
  3065. GoodFileFound = FALSE;
  3066. Quoted = FALSE;
  3067. if (i >= 0) {
  3068. Start = Array[i] + 1;
  3069. } else {
  3070. Start = CmdLineCopy;
  3071. }
  3072. //
  3073. // Check for a full path at Start
  3074. //
  3075. if (_mbsnextc (Start) != '/') {
  3076. for (j = i + 1 ; j <= Count && !GoodFileFound ; j++) {
  3077. if (j < Count) {
  3078. OldChar = *Array[j];
  3079. *Array[j] = 0;
  3080. }
  3081. FullPath = Start;
  3082. //
  3083. // Remove quotes; continue in the loop if it has no terminating quotes
  3084. //
  3085. Quoted = FALSE;
  3086. if (_mbsnextc (Start) == '\"') {
  3087. StringCopyByteCountA (UnquotedPath, Start + 1, sizeof (UnquotedPath));
  3088. q = _mbschr (UnquotedPath, '\"');
  3089. if (q) {
  3090. *q = 0;
  3091. FullPath = UnquotedPath;
  3092. Quoted = TRUE;
  3093. } else {
  3094. FullPath = NULL;
  3095. }
  3096. }
  3097. if (FullPath && *FullPath) {
  3098. //
  3099. // Look in file system for the path
  3100. //
  3101. Attribs = GetFileAttributesA (FullPath);
  3102. if (Attribs == INVALID_ATTRIBUTES && EndOfFirstArg) {
  3103. //
  3104. // Try prefixing the path with the first arg's path.
  3105. //
  3106. StringCopyByteCountA (
  3107. EndOfFirstArg,
  3108. FullPath,
  3109. sizeof (FirstArgPath) - ((PBYTE) EndOfFirstArg - (PBYTE) FirstArgPath)
  3110. );
  3111. FullPath = FirstArgPath;
  3112. Attribs = GetFileAttributesA (FullPath);
  3113. }
  3114. if (Attribs == INVALID_ATTRIBUTES && i < 0) {
  3115. //
  3116. // Try appending .exe, then testing again. This
  3117. // emulates what CreateProcess does.
  3118. //
  3119. StringCopyByteCountA (
  3120. FixedFileName,
  3121. FullPath,
  3122. sizeof (FixedFileName) - sizeof (".exe") // includes nul in subtraction
  3123. );
  3124. //
  3125. // Back up one to make sure we don't generate foo..exe
  3126. //
  3127. q = GetEndOfStringA (FixedFileName);
  3128. q = _mbsdec (FixedFileName, q);
  3129. MYASSERT (q); // we know FullPath != ""
  3130. if (_mbsnextc (q) != '.') {
  3131. q = _mbsinc (q);
  3132. }
  3133. StringCopyA (q, ".exe");
  3134. FullPath = FixedFileName;
  3135. Attribs = GetFileAttributesA (FullPath);
  3136. }
  3137. if (Attribs != INVALID_ATTRIBUTES) {
  3138. //
  3139. // Full file path found. Test its file status, then
  3140. // move on if there are no important operations on it.
  3141. //
  3142. OriginalArgOffset = StringBuf.End;
  3143. MultiSzAppendA (&StringBuf, Start);
  3144. if (!StringMatchA (Start, FullPath)) {
  3145. CleanedUpArgOffset = StringBuf.End;
  3146. MultiSzAppendA (&StringBuf, FullPath);
  3147. } else {
  3148. CleanedUpArgOffset = OriginalArgOffset;
  3149. }
  3150. i = j;
  3151. GoodFileFound = TRUE;
  3152. }
  3153. }
  3154. if (j < Count) {
  3155. *Array[j] = OldChar;
  3156. }
  3157. }
  3158. if (!GoodFileFound) {
  3159. //
  3160. // If a wack is in the path, then we could have a relative path, an arg, or
  3161. // a full path to a non-existent file.
  3162. //
  3163. if (_mbschr (Start, '\\')) {
  3164. #ifdef DEBUG
  3165. j = i + 1;
  3166. if (j < Count) {
  3167. OldChar = *Array[j];
  3168. *Array[j] = 0;
  3169. }
  3170. DEBUGMSGA ((
  3171. DBG_VERBOSE,
  3172. "%s is a non-existent path spec, a relative path, or an arg",
  3173. Start
  3174. ));
  3175. if (j < Count) {
  3176. *Array[j] = OldChar;
  3177. }
  3178. #endif
  3179. } else {
  3180. //
  3181. // The string at Start did not contain a full path; try using
  3182. // SearchPath.
  3183. //
  3184. for (j = i + 1 ; j <= Count && !GoodFileFound ; j++) {
  3185. if (j < Count) {
  3186. OldChar = *Array[j];
  3187. *Array[j] = 0;
  3188. }
  3189. FullPath = Start;
  3190. //
  3191. // Remove quotes; continue in the loop if it has no terminating quotes
  3192. //
  3193. Quoted = FALSE;
  3194. if (_mbsnextc (Start) == '\"') {
  3195. StringCopyByteCountA (UnquotedPath, Start + 1, sizeof (UnquotedPath));
  3196. q = _mbschr (UnquotedPath, '\"');
  3197. if (q) {
  3198. *q = 0;
  3199. FullPath = UnquotedPath;
  3200. Quoted = TRUE;
  3201. } else {
  3202. FullPath = NULL;
  3203. }
  3204. }
  3205. if (FullPath && *FullPath) {
  3206. if (SearchPathA (
  3207. NULL,
  3208. FullPath,
  3209. NULL,
  3210. sizeof (Path) / sizeof (Path[0]),
  3211. Path,
  3212. &DontCare
  3213. )) {
  3214. FullPath = Path;
  3215. } else if (i < 0) {
  3216. //
  3217. // Try appending .exe and searching the path again
  3218. //
  3219. StringCopyByteCountA (
  3220. FixedFileName,
  3221. FullPath,
  3222. sizeof (FixedFileName) - sizeof (".exe") // includes nul in subtraction
  3223. );
  3224. //
  3225. // Back up one and check for dot, to prevent
  3226. // "foo..exe" when input is "foo."
  3227. //
  3228. q = GetEndOfStringA (FixedFileName);
  3229. q = _mbsdec (FixedFileName, q);
  3230. MYASSERT (q);
  3231. if (_mbsnextc (q) != '.') {
  3232. q = _mbsinc (q);
  3233. }
  3234. StringCopyA (q, ".exe");
  3235. if (SearchPathA (
  3236. NULL,
  3237. FixedFileName,
  3238. NULL,
  3239. sizeof (Path) / sizeof (Path[0]),
  3240. Path,
  3241. &DontCare
  3242. )) {
  3243. FullPath = Path;
  3244. } else {
  3245. FullPath = NULL;
  3246. }
  3247. } else {
  3248. FullPath = NULL;
  3249. }
  3250. }
  3251. if (FullPath && *FullPath) {
  3252. Attribs = GetFileAttributesA (FullPath);
  3253. MYASSERT (Attribs != INVALID_ATTRIBUTES);
  3254. OriginalArgOffset = StringBuf.End;
  3255. MultiSzAppendA (&StringBuf, Start);
  3256. if (!StringMatchA (Start, FullPath)) {
  3257. CleanedUpArgOffset = StringBuf.End;
  3258. MultiSzAppendA (&StringBuf, FullPath);
  3259. } else {
  3260. CleanedUpArgOffset = OriginalArgOffset;
  3261. }
  3262. i = j;
  3263. GoodFileFound = TRUE;
  3264. }
  3265. if (j < Count) {
  3266. *Array[j] = OldChar;
  3267. }
  3268. }
  3269. }
  3270. }
  3271. }
  3272. CmdLineTable->ArgCount += 1;
  3273. CmdLineArg = (PCMDLINEARGA) GrowBuffer (Buffer, sizeof (CMDLINEARGA));
  3274. MYASSERT (CmdLineArg);
  3275. if (GoodFileFound) {
  3276. //
  3277. // We have a good full file spec in FullPath, its attributes
  3278. // are in Attribs, and it has been moved to the space beyond
  3279. // the path. We now add a table entry.
  3280. //
  3281. CmdLineArg->OriginalArg = (PCSTR) (UINT_PTR) OriginalArgOffset;
  3282. CmdLineArg->CleanedUpArg = (PCSTR) (UINT_PTR) CleanedUpArgOffset;
  3283. CmdLineArg->Attributes = Attribs;
  3284. CmdLineArg->Quoted = Quoted;
  3285. if (!EndOfFirstArg) {
  3286. StringCopyByteCountA (
  3287. FirstArgPath,
  3288. (PCSTR) (StringBuf.Buf + (UINT_PTR) CmdLineArg->CleanedUpArg),
  3289. sizeof (FirstArgPath) - sizeof (CHAR) // account for AppendWack
  3290. );
  3291. q = (PSTR) GetFileNameFromPathA (FirstArgPath);
  3292. if (q) {
  3293. q = _mbsdec (FirstArgPath, q);
  3294. if (q) {
  3295. *q = 0;
  3296. }
  3297. }
  3298. EndOfFirstArg = AppendWackA (FirstArgPath);
  3299. }
  3300. } else {
  3301. //
  3302. // We do not have a good file spec; we must have a non-file
  3303. // argument. Put it in the table, and advance to the next
  3304. // arg.
  3305. //
  3306. j = i + 1;
  3307. if (j <= Count) {
  3308. if (j < Count) {
  3309. OldChar = *Array[j];
  3310. *Array[j] = 0;
  3311. }
  3312. CmdLineArg->OriginalArg = (PCSTR) (UINT_PTR) StringBuf.End;
  3313. MultiSzAppendA (&StringBuf, Start);
  3314. Quoted = FALSE;
  3315. if (_mbschr (Start, '\"')) {
  3316. p = Start;
  3317. q = UnquotedPath;
  3318. End = (PSTR) ((PBYTE) UnquotedPath + sizeof (UnquotedPath) - sizeof (CHAR));
  3319. while (*p && q < End) {
  3320. if (IsLeadByte (p)) {
  3321. *q++ = *p++;
  3322. *q++ = *p++;
  3323. } else {
  3324. if (*p == '\"') {
  3325. p++;
  3326. } else {
  3327. *q++ = *p++;
  3328. }
  3329. }
  3330. }
  3331. *q = 0;
  3332. CmdLineArg->CleanedUpArg = (PCSTR) (UINT_PTR) StringBuf.End;
  3333. MultiSzAppendA (&StringBuf, UnquotedPath);
  3334. Quoted = TRUE;
  3335. } else {
  3336. CmdLineArg->CleanedUpArg = CmdLineArg->OriginalArg;
  3337. }
  3338. CmdLineArg->Attributes = INVALID_ATTRIBUTES;
  3339. CmdLineArg->Quoted = Quoted;
  3340. if (j < Count) {
  3341. *Array[j] = OldChar;
  3342. }
  3343. i = j;
  3344. }
  3345. }
  3346. }
  3347. //
  3348. // We now have a command line table; transfer StringBuf to Buffer, then
  3349. // convert all offsets into pointers.
  3350. //
  3351. MYASSERT (StringBuf.End);
  3352. CopyBuf = GrowBuffer (Buffer, StringBuf.End);
  3353. MYASSERT (CopyBuf);
  3354. Base = (UINT_PTR) CopyBuf;
  3355. CopyMemory (CopyBuf, StringBuf.Buf, StringBuf.End);
  3356. CmdLineTable->CmdLine = (PCSTR) ((PBYTE) CmdLineTable->CmdLine + Base);
  3357. CmdLineArg = &CmdLineTable->Args[0];
  3358. for (i = 0 ; i < (INT) CmdLineTable->ArgCount ; i++) {
  3359. CmdLineArg->OriginalArg = (PCSTR) ((PBYTE) CmdLineArg->OriginalArg + Base);
  3360. CmdLineArg->CleanedUpArg = (PCSTR) ((PBYTE) CmdLineArg->CleanedUpArg + Base);
  3361. CmdLineArg++;
  3362. }
  3363. FreeGrowBuffer (&StringBuf);
  3364. FreeGrowBuffer (&SpacePtrs);
  3365. return (PCMDLINEA) Buffer->Buf;
  3366. }
  3367. PCMDLINEW
  3368. ParseCmdLineW (
  3369. IN PCWSTR CmdLine,
  3370. IN OUT PGROWBUFFER Buffer
  3371. )
  3372. {
  3373. GROWBUFFER SpacePtrs = GROWBUF_INIT;
  3374. PCWSTR p;
  3375. PWSTR q;
  3376. INT Count;
  3377. INT i;
  3378. INT j;
  3379. PWSTR *Array;
  3380. PCWSTR Start;
  3381. WCHAR OldChar = 0;
  3382. GROWBUFFER StringBuf = GROWBUF_INIT;
  3383. PBYTE CopyBuf;
  3384. PCMDLINEW CmdLineTable;
  3385. PCMDLINEARGW CmdLineArg;
  3386. UINT_PTR Base;
  3387. WCHAR Path[MAX_WCHAR_PATH];
  3388. WCHAR UnquotedPath[MAX_WCHAR_PATH];
  3389. WCHAR FixedFileName[MAX_WCHAR_PATH];
  3390. PCWSTR FullPath = NULL;
  3391. DWORD Attribs = INVALID_ATTRIBUTES;
  3392. PWSTR CmdLineCopy;
  3393. BOOL Quoted;
  3394. UINT OriginalArgOffset = 0;
  3395. UINT CleanedUpArgOffset = 0;
  3396. BOOL GoodFileFound = FALSE;
  3397. PWSTR DontCare;
  3398. WCHAR FirstArgPath[MAX_MBCHAR_PATH];
  3399. PWSTR EndOfFirstArg;
  3400. BOOL QuoteMode = FALSE;
  3401. PWSTR End;
  3402. CmdLineCopy = DuplicateTextW (CmdLine);
  3403. //
  3404. // Build an array of places to break the string
  3405. //
  3406. for (p = CmdLineCopy ; *p ; p++) {
  3407. if (*p == L'\"') {
  3408. QuoteMode = !QuoteMode;
  3409. } else if (!QuoteMode && (*p == L' ' || *p == L'=')) {
  3410. //
  3411. // Remove excess spaces
  3412. //
  3413. q = (PWSTR) p + 1;
  3414. while (*q == L' ') {
  3415. q++;
  3416. }
  3417. if (q > p + 1) {
  3418. MoveMemory ((PBYTE) p + sizeof (WCHAR), q, SizeOfStringW (q));
  3419. }
  3420. GrowBufAppendUintPtr (&SpacePtrs, (UINT_PTR) p);
  3421. }
  3422. }
  3423. //
  3424. // Prepare the CMDLINE struct
  3425. //
  3426. CmdLineTable = (PCMDLINEW) GrowBuffer (Buffer, sizeof (CMDLINEW));
  3427. MYASSERT (CmdLineTable);
  3428. //
  3429. // NOTE: We store string offsets, then at the end resolve them
  3430. // to pointers later.
  3431. //
  3432. CmdLineTable->CmdLine = (PCWSTR) (UINT_PTR) StringBuf.End;
  3433. MultiSzAppendW (&StringBuf, CmdLine);
  3434. CmdLineTable->ArgCount = 0;
  3435. //
  3436. // Now test every combination, emulating CreateProcess
  3437. //
  3438. Count = SpacePtrs.End / sizeof (UINT_PTR);
  3439. Array = (PWSTR *) SpacePtrs.Buf;
  3440. i = -1;
  3441. EndOfFirstArg = NULL;
  3442. while (i < Count) {
  3443. GoodFileFound = FALSE;
  3444. Quoted = FALSE;
  3445. if (i >= 0) {
  3446. Start = Array[i] + 1;
  3447. } else {
  3448. Start = CmdLineCopy;
  3449. }
  3450. //
  3451. // Check for a full path at Start
  3452. //
  3453. if (*Start != L'/') {
  3454. for (j = i + 1 ; j <= Count && !GoodFileFound ; j++) {
  3455. if (j < Count) {
  3456. OldChar = *Array[j];
  3457. *Array[j] = 0;
  3458. }
  3459. FullPath = Start;
  3460. //
  3461. // Remove quotes; continue in the loop if it has no terminating quotes
  3462. //
  3463. Quoted = FALSE;
  3464. if (*Start == L'\"') {
  3465. StringCopyByteCountW (UnquotedPath, Start + 1, sizeof (UnquotedPath));
  3466. q = wcschr (UnquotedPath, L'\"');
  3467. if (q) {
  3468. *q = 0;
  3469. FullPath = UnquotedPath;
  3470. Quoted = TRUE;
  3471. } else {
  3472. FullPath = NULL;
  3473. }
  3474. }
  3475. if (FullPath && *FullPath) {
  3476. //
  3477. // Look in file system for the path
  3478. //
  3479. Attribs = GetLongPathAttributesW (FullPath);
  3480. if (Attribs == INVALID_ATTRIBUTES && EndOfFirstArg) {
  3481. //
  3482. // Try prefixing the path with the first arg's path.
  3483. //
  3484. StringCopyByteCountW (
  3485. EndOfFirstArg,
  3486. FullPath,
  3487. sizeof (FirstArgPath) - ((PBYTE) EndOfFirstArg - (PBYTE) FirstArgPath)
  3488. );
  3489. FullPath = FirstArgPath;
  3490. Attribs = GetLongPathAttributesW (FullPath);
  3491. }
  3492. if (Attribs == INVALID_ATTRIBUTES && i < 0) {
  3493. //
  3494. // Try appending .exe, then testing again. This
  3495. // emulates what CreateProcess does.
  3496. //
  3497. StringCopyByteCountW (
  3498. FixedFileName,
  3499. FullPath,
  3500. sizeof (FixedFileName) - sizeof (L".exe") // includes nul in subtraction
  3501. );
  3502. //
  3503. // Back up one and overwrite any trailing dot
  3504. //
  3505. q = GetEndOfStringW (FixedFileName);
  3506. q--;
  3507. MYASSERT (q >= FixedFileName);
  3508. if (*q != L'.') {
  3509. q++;
  3510. }
  3511. StringCopyW (q, L".exe");
  3512. FullPath = FixedFileName;
  3513. Attribs = GetLongPathAttributesW (FullPath);
  3514. }
  3515. if (Attribs != INVALID_ATTRIBUTES) {
  3516. //
  3517. // Full file path found. Test its file status, then
  3518. // move on if there are no important operations on it.
  3519. //
  3520. OriginalArgOffset = StringBuf.End;
  3521. MultiSzAppendW (&StringBuf, Start);
  3522. if (!StringMatchW (Start, FullPath)) {
  3523. CleanedUpArgOffset = StringBuf.End;
  3524. MultiSzAppendW (&StringBuf, FullPath);
  3525. } else {
  3526. CleanedUpArgOffset = OriginalArgOffset;
  3527. }
  3528. i = j;
  3529. GoodFileFound = TRUE;
  3530. }
  3531. }
  3532. if (j < Count) {
  3533. *Array[j] = OldChar;
  3534. }
  3535. }
  3536. if (!GoodFileFound) {
  3537. //
  3538. // If a wack is in the path, then we could have a relative path, an arg, or
  3539. // a full path to a non-existent file.
  3540. //
  3541. if (wcschr (Start, L'\\')) {
  3542. #ifdef DEBUG
  3543. j = i + 1;
  3544. if (j < Count) {
  3545. OldChar = *Array[j];
  3546. *Array[j] = 0;
  3547. }
  3548. DEBUGMSGW ((
  3549. DBG_VERBOSE,
  3550. "%s is a non-existent path spec, a relative path, or an arg",
  3551. Start
  3552. ));
  3553. if (j < Count) {
  3554. *Array[j] = OldChar;
  3555. }
  3556. #endif
  3557. } else {
  3558. //
  3559. // The string at Start did not contain a full path; try using
  3560. // SearchPath.
  3561. //
  3562. for (j = i + 1 ; j <= Count && !GoodFileFound ; j++) {
  3563. if (j < Count) {
  3564. OldChar = *Array[j];
  3565. *Array[j] = 0;
  3566. }
  3567. FullPath = Start;
  3568. //
  3569. // Remove quotes; continue in the loop if it has no terminating quotes
  3570. //
  3571. Quoted = FALSE;
  3572. if (*Start == L'\"') {
  3573. StringCopyByteCountW (UnquotedPath, Start + 1, sizeof (UnquotedPath));
  3574. q = wcschr (UnquotedPath, L'\"');
  3575. if (q) {
  3576. *q = 0;
  3577. FullPath = UnquotedPath;
  3578. Quoted = TRUE;
  3579. } else {
  3580. FullPath = NULL;
  3581. }
  3582. }
  3583. if (FullPath && *FullPath) {
  3584. if (SearchPathW (
  3585. NULL,
  3586. FullPath,
  3587. NULL,
  3588. sizeof (Path) / sizeof (Path[0]),
  3589. Path,
  3590. &DontCare
  3591. )) {
  3592. FullPath = Path;
  3593. } else if (i < 0) {
  3594. //
  3595. // Try appending .exe and searching the path again
  3596. //
  3597. StringCopyByteCountW (
  3598. FixedFileName,
  3599. FullPath,
  3600. sizeof (FixedFileName) - sizeof (L".exe") // includes nul in subtraction
  3601. );
  3602. //
  3603. // Back up one and overwrite any trailing dot
  3604. //
  3605. q = GetEndOfStringW (FixedFileName);
  3606. q--;
  3607. MYASSERT (q >= FixedFileName);
  3608. if (*q != L'.') {
  3609. q++;
  3610. }
  3611. StringCopyW (q, L".exe");
  3612. if (SearchPathW (
  3613. NULL,
  3614. FixedFileName,
  3615. NULL,
  3616. sizeof (Path) / sizeof (Path[0]),
  3617. Path,
  3618. &DontCare
  3619. )) {
  3620. FullPath = Path;
  3621. } else {
  3622. FullPath = NULL;
  3623. }
  3624. } else {
  3625. FullPath = NULL;
  3626. }
  3627. }
  3628. if (FullPath && *FullPath) {
  3629. Attribs = GetLongPathAttributesW (FullPath);
  3630. MYASSERT (Attribs != INVALID_ATTRIBUTES);
  3631. OriginalArgOffset = StringBuf.End;
  3632. MultiSzAppendW (&StringBuf, Start);
  3633. if (!StringMatchW (Start, FullPath)) {
  3634. CleanedUpArgOffset = StringBuf.End;
  3635. MultiSzAppendW (&StringBuf, FullPath);
  3636. } else {
  3637. CleanedUpArgOffset = OriginalArgOffset;
  3638. }
  3639. i = j;
  3640. GoodFileFound = TRUE;
  3641. }
  3642. if (j < Count) {
  3643. *Array[j] = OldChar;
  3644. }
  3645. }
  3646. }
  3647. }
  3648. }
  3649. CmdLineTable->ArgCount += 1;
  3650. CmdLineArg = (PCMDLINEARGW) GrowBuffer (Buffer, sizeof (CMDLINEARGW));
  3651. MYASSERT (CmdLineArg);
  3652. if (GoodFileFound) {
  3653. //
  3654. // We have a good full file spec in FullPath, its attributes
  3655. // are in Attribs, and i has been moved to the space beyond
  3656. // the path. We now add a table entry.
  3657. //
  3658. CmdLineArg->OriginalArg = (PCWSTR) (UINT_PTR) OriginalArgOffset;
  3659. CmdLineArg->CleanedUpArg = (PCWSTR) (UINT_PTR) CleanedUpArgOffset;
  3660. CmdLineArg->Attributes = Attribs;
  3661. CmdLineArg->Quoted = Quoted;
  3662. if (!EndOfFirstArg) {
  3663. StringCopyByteCountW (
  3664. FirstArgPath,
  3665. (PCWSTR) (StringBuf.Buf + (UINT_PTR) CmdLineArg->CleanedUpArg),
  3666. sizeof (FirstArgPath) - sizeof (WCHAR) // account for AppendWack
  3667. );
  3668. q = (PWSTR) GetFileNameFromPathW (FirstArgPath);
  3669. if (q) {
  3670. q--;
  3671. if (q >= FirstArgPath) {
  3672. *q = 0;
  3673. }
  3674. }
  3675. EndOfFirstArg = AppendWackW (FirstArgPath);
  3676. }
  3677. } else {
  3678. //
  3679. // We do not have a good file spec; we must have a non-file
  3680. // argument. Put it in the table, and advance to the next
  3681. // arg.
  3682. //
  3683. j = i + 1;
  3684. if (j <= Count) {
  3685. if (j < Count) {
  3686. OldChar = *Array[j];
  3687. *Array[j] = 0;
  3688. }
  3689. CmdLineArg->OriginalArg = (PCWSTR) (UINT_PTR) StringBuf.End;
  3690. MultiSzAppendW (&StringBuf, Start);
  3691. Quoted = FALSE;
  3692. if (wcschr (Start, '\"')) {
  3693. p = Start;
  3694. q = UnquotedPath;
  3695. End = (PWSTR) ((PBYTE) UnquotedPath + sizeof (UnquotedPath) - sizeof (WCHAR));
  3696. while (*p && q < End) {
  3697. if (*p == L'\"') {
  3698. p++;
  3699. } else {
  3700. *q++ = *p++;
  3701. }
  3702. }
  3703. *q = 0;
  3704. CmdLineArg->CleanedUpArg = (PCWSTR) (UINT_PTR) StringBuf.End;
  3705. MultiSzAppendW (&StringBuf, UnquotedPath);
  3706. Quoted = TRUE;
  3707. } else {
  3708. CmdLineArg->CleanedUpArg = CmdLineArg->OriginalArg;
  3709. }
  3710. CmdLineArg->Attributes = INVALID_ATTRIBUTES;
  3711. CmdLineArg->Quoted = Quoted;
  3712. if (j < Count) {
  3713. *Array[j] = OldChar;
  3714. }
  3715. i = j;
  3716. }
  3717. }
  3718. }
  3719. //
  3720. // We now have a command line table; transfer StringBuf to Buffer, then
  3721. // convert all offsets into pointers.
  3722. //
  3723. MYASSERT (StringBuf.End);
  3724. CopyBuf = GrowBuffer (Buffer, StringBuf.End);
  3725. MYASSERT (CopyBuf);
  3726. Base = (UINT_PTR) CopyBuf;
  3727. CopyMemory (CopyBuf, StringBuf.Buf, StringBuf.End);
  3728. CmdLineTable->CmdLine = (PCWSTR) ((PBYTE) CmdLineTable->CmdLine + Base);
  3729. CmdLineArg = &CmdLineTable->Args[0];
  3730. for (i = 0 ; i < (INT) CmdLineTable->ArgCount ; i++) {
  3731. CmdLineArg->OriginalArg = (PCWSTR) ((PBYTE) CmdLineArg->OriginalArg + Base);
  3732. CmdLineArg->CleanedUpArg = (PCWSTR) ((PBYTE) CmdLineArg->CleanedUpArg + Base);
  3733. CmdLineArg++;
  3734. }
  3735. FreeGrowBuffer (&StringBuf);
  3736. FreeGrowBuffer (&SpacePtrs);
  3737. return (PCMDLINEW) Buffer->Buf;
  3738. }
  3739. BOOL
  3740. GetFileSizeFromFilePathA(
  3741. IN PCSTR FilePath,
  3742. OUT ULARGE_INTEGER * FileSize
  3743. )
  3744. {
  3745. WIN32_FILE_ATTRIBUTE_DATA fileDataAttributes;
  3746. if(!FilePath || !FileSize){
  3747. MYASSERT(FALSE);
  3748. return FALSE;
  3749. }
  3750. if (!IsPathOnFixedDriveA (FilePath)) {
  3751. FileSize->QuadPart = 0;
  3752. MYASSERT(FALSE);
  3753. return FALSE;
  3754. }
  3755. if(!GetFileAttributesExA(FilePath, GetFileExInfoStandard, &fileDataAttributes) ||
  3756. fileDataAttributes.dwFileAttributes == INVALID_ATTRIBUTES ||
  3757. (fileDataAttributes.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)){
  3758. MYASSERT(FALSE);
  3759. return FALSE;
  3760. }
  3761. FileSize->LowPart = fileDataAttributes.nFileSizeLow;
  3762. FileSize->HighPart = fileDataAttributes.nFileSizeHigh;
  3763. return TRUE;
  3764. }
  3765. BOOL
  3766. GetFileSizeFromFilePathW(
  3767. IN PCWSTR FilePath,
  3768. OUT ULARGE_INTEGER * FileSize
  3769. )
  3770. {
  3771. WIN32_FILE_ATTRIBUTE_DATA fileDataAttributes;
  3772. if(!FilePath || !FileSize){
  3773. MYASSERT(FALSE);
  3774. return FALSE;
  3775. }
  3776. if (!IsPathOnFixedDriveW (FilePath)) {
  3777. FileSize->QuadPart = 0;
  3778. MYASSERT(FALSE);
  3779. return FALSE;
  3780. }
  3781. if(!GetFileAttributesExW(FilePath, GetFileExInfoStandard, &fileDataAttributes) ||
  3782. fileDataAttributes.dwFileAttributes == INVALID_ATTRIBUTES ||
  3783. (fileDataAttributes.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)){
  3784. MYASSERT(FALSE);
  3785. return FALSE;
  3786. }
  3787. FileSize->LowPart = fileDataAttributes.nFileSizeLow;
  3788. FileSize->HighPart = fileDataAttributes.nFileSizeHigh;
  3789. return TRUE;
  3790. }
  3791. VOID
  3792. InitializeDriveLetterStructureA (
  3793. OUT PDRIVELETTERSA DriveLetters
  3794. )
  3795. {
  3796. BYTE bitPosition;
  3797. DWORD maxBitPosition = NUMDRIVELETTERS;
  3798. CHAR rootPath[] = "?:\\";
  3799. BOOL driveExists;
  3800. UINT type;
  3801. //
  3802. // GetLogicalDrives returns a bitmask of all of the drive letters
  3803. // in use on the system. (i.e. bit position 0 is turned on if there is
  3804. // an 'A' drive, 1 is turned on if there is a 'B' drive, etc.
  3805. // This loop will use this bitmask to fill in the global drive
  3806. // letters structure with information about what drive letters
  3807. // are available and what there drive types are.
  3808. //
  3809. for (bitPosition = 0; bitPosition < maxBitPosition; bitPosition++) {
  3810. //
  3811. // Initialize this drive
  3812. //
  3813. DriveLetters->ExistsOnSystem[bitPosition] = FALSE;
  3814. DriveLetters->Type[bitPosition] = 0;
  3815. DriveLetters->IdentifierString[bitPosition][0] = 0;
  3816. rootPath[0] = 'A' + bitPosition;
  3817. DriveLetters->Letter[bitPosition] = rootPath[0];
  3818. //
  3819. // Determine if there is a drive in this spot.
  3820. //
  3821. driveExists = GetLogicalDrives() & (1 << bitPosition);
  3822. if (driveExists) {
  3823. //
  3824. // There is. Now, see if it is one that we care about.
  3825. //
  3826. type = GetDriveTypeA(rootPath);
  3827. if (type == DRIVE_FIXED || type == DRIVE_REMOVABLE || type == DRIVE_CDROM) {
  3828. //
  3829. // This is a drive that we are interested in.
  3830. //
  3831. DriveLetters->ExistsOnSystem[bitPosition] = TRUE;
  3832. DriveLetters->Type[bitPosition] = type;
  3833. //
  3834. // Identifier String is not filled in this function.
  3835. //
  3836. }
  3837. }
  3838. }
  3839. }
  3840. VOID
  3841. InitializeDriveLetterStructureW (
  3842. OUT PDRIVELETTERSW DriveLetters
  3843. )
  3844. {
  3845. BYTE bitPosition;
  3846. DWORD maxBitPosition = NUMDRIVELETTERS;
  3847. WCHAR rootPath[] = L"?:\\";
  3848. BOOL driveExists;
  3849. UINT type;
  3850. //
  3851. // GetLogicalDrives returns a bitmask of all of the drive letters
  3852. // in use on the system. (i.e. bit position 0 is turned on if there is
  3853. // an 'A' drive, 1 is turned on if there is a 'B' drive, etc.
  3854. // This loop will use this bitmask to fill in the global drive
  3855. // letters structure with information about what drive letters
  3856. // are available and what there drive types are.
  3857. //
  3858. for (bitPosition = 0; bitPosition < maxBitPosition; bitPosition++) {
  3859. //
  3860. // Initialize this drive
  3861. //
  3862. DriveLetters->ExistsOnSystem[bitPosition] = FALSE;
  3863. DriveLetters->Type[bitPosition] = 0;
  3864. DriveLetters->IdentifierString[bitPosition][0] = 0;
  3865. rootPath[0] = L'A' + bitPosition;
  3866. DriveLetters->Letter[bitPosition] = rootPath[0];
  3867. //
  3868. // Determine if there is a drive in this spot.
  3869. //
  3870. driveExists = GetLogicalDrives() & (1 << bitPosition);
  3871. if (driveExists) {
  3872. //
  3873. // There is. Now, see if it is one that we care about.
  3874. //
  3875. type = GetDriveTypeW(rootPath);
  3876. if (type == DRIVE_FIXED || type == DRIVE_REMOVABLE || type == DRIVE_CDROM) {
  3877. //
  3878. // This is a drive that we are interested in.
  3879. //
  3880. DriveLetters->ExistsOnSystem[bitPosition] = TRUE;
  3881. DriveLetters->Type[bitPosition] = type;
  3882. //
  3883. // Identifier String is not filled in this function.
  3884. //
  3885. }
  3886. }
  3887. }
  3888. }
  3889. typedef BOOL (WINAPI * GETDISKFREESPACEEXA)(
  3890. PCSTR lpDirectoryName, // directory name
  3891. PULARGE_INTEGER lpFreeBytesAvailable, // bytes available to caller
  3892. PULARGE_INTEGER lpTotalNumberOfBytes, // bytes on disk
  3893. PULARGE_INTEGER lpTotalNumberOfFreeBytes // free bytes on disk
  3894. );
  3895. typedef BOOL (WINAPI * GETDISKFREESPACEEXW)(
  3896. PCWSTR lpDirectoryName, // directory name
  3897. PULARGE_INTEGER lpFreeBytesAvailable, // bytes available to caller
  3898. PULARGE_INTEGER lpTotalNumberOfBytes, // bytes on disk
  3899. PULARGE_INTEGER lpTotalNumberOfFreeBytes // free bytes on disk
  3900. );
  3901. BOOL
  3902. GetDiskFreeSpaceNewA(
  3903. IN PCSTR DriveName,
  3904. OUT DWORD * OutSectorsPerCluster,
  3905. OUT DWORD * OutBytesPerSector,
  3906. OUT ULARGE_INTEGER * OutNumberOfFreeClusters,
  3907. OUT ULARGE_INTEGER * OutTotalNumberOfClusters
  3908. )
  3909. /*++
  3910. Routine Description:
  3911. On Win9x GetDiskFreeSpace never return free/total space more than 2048MB.
  3912. GetDiskFreeSpaceNew use GetDiskFreeSpaceEx to calculate real number of free/total clusters.
  3913. Has same declaration as GetDiskFreeSpaceA.
  3914. Arguments:
  3915. DriveName - supplies directory name
  3916. OutSectorsPerCluster - receive number of sectors per cluster
  3917. OutBytesPerSector - receive number of bytes per sector
  3918. OutNumberOfFreeClusters - receive number of free clusters
  3919. OutTotalNumberOfClusters - receive number of total clusters
  3920. Return Value:
  3921. TRUE if the function succeeds.
  3922. If the function fails, the return value is FALSE. To get extended error information, call GetLastError
  3923. --*/
  3924. {
  3925. ULARGE_INTEGER TotalNumberOfFreeBytes = {0, 0};
  3926. ULARGE_INTEGER TotalNumberOfBytes = {0, 0};
  3927. ULARGE_INTEGER DonotCare;
  3928. HMODULE hKernel32;
  3929. GETDISKFREESPACEEXA pGetDiskFreeSpaceExA;
  3930. ULARGE_INTEGER NumberOfFreeClusters = {0, 0};
  3931. ULARGE_INTEGER TotalNumberOfClusters = {0, 0};
  3932. DWORD SectorsPerCluster;
  3933. DWORD BytesPerSector;
  3934. if(!GetDiskFreeSpaceA(DriveName,
  3935. &SectorsPerCluster,
  3936. &BytesPerSector,
  3937. &NumberOfFreeClusters.LowPart,
  3938. &TotalNumberOfClusters.LowPart)){
  3939. DEBUGMSG((DBG_ERROR,"GetDiskFreeSpaceNewA: GetDiskFreeSpaceA failed on drive %s", DriveName));
  3940. return FALSE;
  3941. }
  3942. hKernel32 = LoadSystemLibraryA("kernel32.dll");
  3943. pGetDiskFreeSpaceExA = (GETDISKFREESPACEEXA)GetProcAddress(hKernel32, "GetDiskFreeSpaceExA");
  3944. if(pGetDiskFreeSpaceExA &&
  3945. pGetDiskFreeSpaceExA(DriveName, &DonotCare, &TotalNumberOfBytes, &TotalNumberOfFreeBytes)){
  3946. NumberOfFreeClusters.QuadPart = TotalNumberOfFreeBytes.QuadPart / (SectorsPerCluster * BytesPerSector);
  3947. TotalNumberOfClusters.QuadPart = TotalNumberOfBytes.QuadPart / (SectorsPerCluster * BytesPerSector);
  3948. }
  3949. else{
  3950. DEBUGMSG((DBG_WARNING,
  3951. pGetDiskFreeSpaceExA?
  3952. "GetDiskFreeSpaceNewA: GetDiskFreeSpaceExA is failed":
  3953. "GetDiskFreeSpaceNewA: GetDiskFreeSpaceExA function is not in kernel32.dll"));
  3954. }
  3955. FreeLibrary(hKernel32);
  3956. if(OutSectorsPerCluster){
  3957. *OutSectorsPerCluster = SectorsPerCluster;
  3958. }
  3959. if(OutBytesPerSector){
  3960. *OutBytesPerSector = BytesPerSector;
  3961. }
  3962. if(OutNumberOfFreeClusters){
  3963. OutNumberOfFreeClusters->QuadPart = NumberOfFreeClusters.QuadPart;
  3964. }
  3965. if(OutTotalNumberOfClusters){
  3966. OutTotalNumberOfClusters->QuadPart = TotalNumberOfClusters.QuadPart;
  3967. }
  3968. DEBUGMSG((DBG_VERBOSE,
  3969. "GetDiskFreeSpaceNewA: \n\t"
  3970. "SectorsPerCluster = %d\n\t"
  3971. "BytesPerSector = %d\n\t"
  3972. "NumberOfFreeClusters = %I64u\n\t"
  3973. "TotalNumberOfClusters = %I64u",
  3974. SectorsPerCluster,
  3975. BytesPerSector,
  3976. NumberOfFreeClusters.QuadPart,
  3977. TotalNumberOfClusters.QuadPart));
  3978. return TRUE;
  3979. }
  3980. BOOL
  3981. GetDiskFreeSpaceNewW(
  3982. IN PCWSTR DriveName,
  3983. OUT DWORD * OutSectorsPerCluster,
  3984. OUT DWORD * OutBytesPerSector,
  3985. OUT ULARGE_INTEGER * OutNumberOfFreeClusters,
  3986. OUT ULARGE_INTEGER * OutTotalNumberOfClusters
  3987. )
  3988. /*++
  3989. Routine Description:
  3990. Correct NumberOfFreeClusters and TotalNumberOfClusters out parameters
  3991. with using GetDiskFreeSpace and GetDiskFreeSpaceEx
  3992. Arguments:
  3993. DriveName - supplies directory name
  3994. OutSectorsPerCluster - receive number of sectors per cluster
  3995. OutBytesPerSector - receive number of bytes per sector
  3996. OutNumberOfFreeClusters - receive number of free clusters
  3997. OutTotalNumberOfClusters - receive number of total clusters
  3998. Return Value:
  3999. TRUE if the function succeeds.
  4000. If the function fails, the return value is FALSE. To get extended error information, call GetLastError
  4001. --*/
  4002. {
  4003. ULARGE_INTEGER TotalNumberOfFreeBytes = {0, 0};
  4004. ULARGE_INTEGER TotalNumberOfBytes = {0, 0};
  4005. ULARGE_INTEGER DonotCare;
  4006. HMODULE hKernel32;
  4007. GETDISKFREESPACEEXW pGetDiskFreeSpaceExW;
  4008. ULARGE_INTEGER NumberOfFreeClusters = {0, 0};
  4009. ULARGE_INTEGER TotalNumberOfClusters = {0, 0};
  4010. DWORD SectorsPerCluster;
  4011. DWORD BytesPerSector;
  4012. if(!GetDiskFreeSpaceW(DriveName,
  4013. &SectorsPerCluster,
  4014. &BytesPerSector,
  4015. &NumberOfFreeClusters.LowPart,
  4016. &TotalNumberOfClusters.LowPart)){
  4017. DEBUGMSG((DBG_ERROR,"GetDiskFreeSpaceNewW: GetDiskFreeSpaceW failed on drive %s", DriveName));
  4018. return FALSE;
  4019. }
  4020. hKernel32 = LoadSystemLibraryA("kernel32.dll");
  4021. pGetDiskFreeSpaceExW = (GETDISKFREESPACEEXW)GetProcAddress(hKernel32, "GetDiskFreeSpaceExW");
  4022. if(pGetDiskFreeSpaceExW &&
  4023. pGetDiskFreeSpaceExW(DriveName, &DonotCare, &TotalNumberOfBytes, &TotalNumberOfFreeBytes)){
  4024. NumberOfFreeClusters.QuadPart = TotalNumberOfFreeBytes.QuadPart / (SectorsPerCluster * BytesPerSector);
  4025. TotalNumberOfClusters.QuadPart = TotalNumberOfBytes.QuadPart / (SectorsPerCluster * BytesPerSector);
  4026. }
  4027. else{
  4028. DEBUGMSG((DBG_WARNING,
  4029. pGetDiskFreeSpaceExW?
  4030. "GetDiskFreeSpaceNewW: GetDiskFreeSpaceExW is failed":
  4031. "GetDiskFreeSpaceNewW: GetDiskFreeSpaceExW function is not in kernel32.dll"));
  4032. }
  4033. FreeLibrary(hKernel32);
  4034. if(OutSectorsPerCluster){
  4035. *OutSectorsPerCluster = SectorsPerCluster;
  4036. }
  4037. if(OutBytesPerSector){
  4038. *OutBytesPerSector = BytesPerSector;
  4039. }
  4040. if(OutNumberOfFreeClusters){
  4041. OutNumberOfFreeClusters->QuadPart = NumberOfFreeClusters.QuadPart;
  4042. }
  4043. if(OutTotalNumberOfClusters){
  4044. OutTotalNumberOfClusters->QuadPart = TotalNumberOfClusters.QuadPart;
  4045. }
  4046. DEBUGMSG((DBG_VERBOSE,
  4047. "GetDiskFreeSpaceNewW: \n\t"
  4048. "SectorsPerCluster = %d\n\t"
  4049. "BytesPerSector = %d\n\t"
  4050. "NumberOfFreeClusters = %I64u\n\t"
  4051. "TotalNumberOfClusters = %I64u",
  4052. SectorsPerCluster,
  4053. BytesPerSector,
  4054. NumberOfFreeClusters.QuadPart,
  4055. TotalNumberOfClusters.QuadPart));
  4056. return TRUE;
  4057. }
  4058. DWORD
  4059. QuietGetFileAttributesA (
  4060. IN PCSTR FilePath
  4061. )
  4062. {
  4063. if (!IsPathOnFixedDriveA (FilePath)) {
  4064. return INVALID_ATTRIBUTES;
  4065. }
  4066. return GetFileAttributesA (FilePath);
  4067. }
  4068. DWORD
  4069. QuietGetFileAttributesW (
  4070. IN PCWSTR FilePath
  4071. )
  4072. {
  4073. MYASSERT (ISNT());
  4074. if (!IsPathOnFixedDriveW (FilePath)) {
  4075. return INVALID_ATTRIBUTES;
  4076. }
  4077. return GetLongPathAttributesW (FilePath);
  4078. }
  4079. DWORD
  4080. MakeSureLongPathExistsW (
  4081. IN PCWSTR Path,
  4082. IN BOOL PathOnly
  4083. )
  4084. {
  4085. PCWSTR tmp;
  4086. DWORD result;
  4087. if (Path[0] == L'\\' || TcharCountW (Path) < MAX_PATH) {
  4088. result = MakeSurePathExistsW (Path, PathOnly);
  4089. } else {
  4090. tmp = JoinPathsW (L"\\\\?", Path);
  4091. result = MakeSurePathExistsW (tmp, PathOnly);
  4092. FreePathStringW (tmp);
  4093. }
  4094. return result;
  4095. }
  4096. DWORD
  4097. SetLongPathAttributesW (
  4098. IN PCWSTR Path,
  4099. IN DWORD Attributes
  4100. )
  4101. {
  4102. PCWSTR tmp;
  4103. DWORD result;
  4104. if (Path[0] == L'\\' || TcharCountW (Path) < MAX_PATH) {
  4105. result = SetFileAttributesW (Path, Attributes);
  4106. } else {
  4107. tmp = JoinPathsW (L"\\\\?", Path);
  4108. result = SetFileAttributesW (tmp, Attributes);
  4109. FreePathStringW (tmp);
  4110. }
  4111. return result;
  4112. }
  4113. DWORD
  4114. GetLongPathAttributesW (
  4115. IN PCWSTR Path
  4116. )
  4117. {
  4118. PCWSTR tmp;
  4119. DWORD result;
  4120. if (Path[0] == L'\\' || TcharCountW (Path) < MAX_PATH) {
  4121. result = GetFileAttributesW (Path);
  4122. } else {
  4123. tmp = JoinPathsW (L"\\\\?", Path);
  4124. result = GetFileAttributesW (tmp);
  4125. FreePathStringW (tmp);
  4126. }
  4127. return result;
  4128. }
  4129. BOOL
  4130. DeleteLongPathW (
  4131. IN PCWSTR Path
  4132. )
  4133. {
  4134. PCWSTR tmp;
  4135. BOOL result;
  4136. if (Path[0] == L'\\' || TcharCountW (Path) < MAX_PATH) {
  4137. result = DeleteFileW (Path);
  4138. } else {
  4139. tmp = JoinPathsW (L"\\\\?", Path);
  4140. result = DeleteFileW (tmp);
  4141. FreePathStringW (tmp);
  4142. }
  4143. return result;
  4144. }
  4145. BOOL
  4146. RemoveLongDirectoryPathW (
  4147. IN PCWSTR Path
  4148. )
  4149. {
  4150. PCWSTR tmp;
  4151. BOOL result;
  4152. if (Path[0] == L'\\' || TcharCountW (Path) < MAX_PATH) {
  4153. result = RemoveDirectoryW (Path);
  4154. } else {
  4155. tmp = JoinPathsW (L"\\\\?", Path);
  4156. result = RemoveDirectoryW (tmp);
  4157. FreePathStringW (tmp);
  4158. }
  4159. return result;
  4160. }