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.

862 lines
24 KiB

  1. #include <nt.h>
  2. #include <ntrtl.h>
  3. #include <nturtl.h>
  4. #include <windows.h>
  5. #include <srvfsctl.h> // FSCTL_SRV_ENUMERATE_SNAPSHOTS
  6. #include <lm.h>
  7. #include <lmdfs.h> // NetDfsGetClientInfo
  8. #include <shlwapi.h> // PathIsUNC
  9. #include "timewarp.h"
  10. typedef struct _SRV_SNAPSHOT_ARRAY
  11. {
  12. ULONG NumberOfSnapshots; // The number of snapshots for the volume
  13. ULONG NumberOfSnapshotsReturned; // The number of snapshots we can fit into this buffer
  14. ULONG SnapshotArraySize; // The size (in bytes) needed for the array
  15. WCHAR SnapShotMultiSZ[1]; // The multiSZ array of snapshot names
  16. } SRV_SNAPSHOT_ARRAY, *PSRV_SNAPSHOT_ARRAY;
  17. DWORD
  18. OpenFileForSnapshot(
  19. IN LPCWSTR lpszFilePath,
  20. OUT HANDLE* pHandle
  21. )
  22. /*++
  23. Routine Description:
  24. This routine opens a file with the access needed to query its snapshot information
  25. Arguments:
  26. lpszFilePath - network path to the file
  27. pHandle - Upon return, the handle to the opened file
  28. Return Value:
  29. Win32 Error
  30. Notes:
  31. None
  32. --*/
  33. {
  34. NTSTATUS Status;
  35. UNICODE_STRING uPathName;
  36. OBJECT_ATTRIBUTES objectAttributes;
  37. IO_STATUS_BLOCK ioStatusBlock;
  38. *pHandle = CreateFile( lpszFilePath, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL );
  39. if( *pHandle == INVALID_HANDLE_VALUE )
  40. {
  41. return GetLastError();
  42. }
  43. else
  44. {
  45. return ERROR_SUCCESS;
  46. }
  47. }
  48. NTSTATUS
  49. IssueSnapshotControl(
  50. IN HANDLE hFile,
  51. IN PVOID pData,
  52. IN ULONG outDataSize
  53. )
  54. /*++
  55. Routine Description:
  56. This routine issues the snapshot enumeration FSCTL against the provided handle
  57. Arguments:
  58. hFile - The handle to the file in question
  59. pData - A pointer to the output buffer
  60. outDataSize - The size of the given output buffer
  61. Return Value:
  62. NTSTATUS
  63. Notes:
  64. None
  65. --*/
  66. {
  67. NTSTATUS Status;
  68. HANDLE hEvent;
  69. IO_STATUS_BLOCK ioStatusBlock;
  70. PSRV_SNAPSHOT_ARRAY pArray;
  71. RtlZeroMemory( pData, outDataSize );
  72. // Create an event to synchronize with the driver
  73. Status = NtCreateEvent(
  74. &hEvent,
  75. FILE_ALL_ACCESS,
  76. NULL,
  77. NotificationEvent,
  78. FALSE
  79. );
  80. if( NT_SUCCESS(Status) )
  81. {
  82. Status = NtFsControlFile(
  83. hFile,
  84. hEvent,
  85. NULL,
  86. NULL,
  87. &ioStatusBlock,
  88. FSCTL_SRV_ENUMERATE_SNAPSHOTS,
  89. NULL,
  90. 0,
  91. pData,
  92. outDataSize);
  93. if( Status == STATUS_PENDING )
  94. {
  95. NtWaitForSingleObject( hEvent, FALSE, NULL );
  96. Status = ioStatusBlock.Status;
  97. }
  98. NtClose( hEvent );
  99. }
  100. // Check the return value
  101. if( NT_SUCCESS(Status) )
  102. {
  103. pArray = (PSRV_SNAPSHOT_ARRAY)pData;
  104. if( pArray->NumberOfSnapshots != pArray->NumberOfSnapshotsReturned )
  105. {
  106. Status = STATUS_BUFFER_OVERFLOW;
  107. }
  108. }
  109. return Status;
  110. }
  111. DWORD
  112. QuerySnapshotNames(
  113. IN HANDLE hFile,
  114. OUT LPWSTR* ppszSnapshotNameArray,
  115. OUT LPDWORD pdwNumberOfSnapshots
  116. )
  117. /*++
  118. Routine Description:
  119. This routine takes a handle to a file and returns a MultiSZ list
  120. of the snapshots availible on the volume the handle resides.
  121. Arguments:
  122. hFile - Handle to the file in question
  123. ppszSnapshotNameArray - Upon return, an allocated MultiSZ array of names
  124. pdwNumberOfSnapshots - the number of snapshots in the above array
  125. Return Value:
  126. Win32 Error
  127. Notes:
  128. The returned list of snapshots is complete for the volume. It is not
  129. guaranteed that every returned entry will actually have the file existing
  130. in that snapshot. The caller should check that themselves.
  131. --*/
  132. {
  133. NTSTATUS Status;
  134. SRV_SNAPSHOT_ARRAY sArray;
  135. PSRV_SNAPSHOT_ARRAY psAllocatedArray = NULL;
  136. LPWSTR pszNameArray = NULL;
  137. // Query the size needed for the snapshots
  138. Status = IssueSnapshotControl( hFile, &sArray, sizeof(SRV_SNAPSHOT_ARRAY) );
  139. if( NT_SUCCESS(Status) || (Status == STATUS_BUFFER_OVERFLOW) )
  140. {
  141. ULONG AllocSize = sizeof(SRV_SNAPSHOT_ARRAY)+sArray.SnapshotArraySize;
  142. if( sArray.NumberOfSnapshots == 0 )
  143. {
  144. *pdwNumberOfSnapshots = 0;
  145. *ppszSnapshotNameArray = NULL;
  146. }
  147. else
  148. {
  149. // Allocate the array to the necessary size
  150. psAllocatedArray = (PSRV_SNAPSHOT_ARRAY)LocalAlloc( LPTR, AllocSize );
  151. if( psAllocatedArray )
  152. {
  153. // Call again with the proper size array
  154. Status = IssueSnapshotControl( hFile, psAllocatedArray, AllocSize );
  155. if( NT_SUCCESS(Status) )
  156. {
  157. // Allocate the string needed
  158. pszNameArray = (LPWSTR)LocalAlloc( LPTR, psAllocatedArray->SnapshotArraySize );
  159. if( pszNameArray )
  160. {
  161. // Copy the string and succeed
  162. RtlCopyMemory( pszNameArray, psAllocatedArray->SnapShotMultiSZ, psAllocatedArray->SnapshotArraySize );
  163. *ppszSnapshotNameArray = pszNameArray;
  164. *pdwNumberOfSnapshots = psAllocatedArray->NumberOfSnapshots;
  165. Status = STATUS_SUCCESS;
  166. }
  167. else
  168. {
  169. Status = STATUS_INSUFFICIENT_RESOURCES;
  170. }
  171. }
  172. else if( Status == STATUS_BUFFER_OVERFLOW )
  173. {
  174. Status = STATUS_RETRY;
  175. }
  176. LocalFree( psAllocatedArray );
  177. }
  178. else
  179. {
  180. Status = STATUS_INSUFFICIENT_RESOURCES;
  181. }
  182. }
  183. }
  184. return RtlNtStatusToDosError(Status);
  185. }
  186. LPCWSTR
  187. FindVolumePathSplit(
  188. IN LPCWSTR lpszPath
  189. )
  190. {
  191. LPCWSTR pszTail = NULL;
  192. WCHAR szVolumeName[MAX_PATH];
  193. if (GetVolumePathNameW(lpszPath, szVolumeName, MAX_PATH))
  194. {
  195. ULONG cchVolumeName = lstrlenW(szVolumeName);
  196. ASSERT(cchVolumeName > 0);
  197. ASSERT(szVolumeName[cchVolumeName-1] == L'\\');
  198. pszTail = lpszPath + (cchVolumeName - 1);
  199. ASSERT(pszTail <= lpszPath + lstrlenW(lpszPath));
  200. ASSERT(*pszTail == L'\\' || *pszTail == L'\0');
  201. }
  202. return pszTail;
  203. }
  204. LPCWSTR
  205. FindDfsUncSplit(
  206. IN LPCWSTR lpszPath
  207. )
  208. {
  209. LPCWSTR pszTail = NULL;
  210. PDFS_INFO_1 pDI1;
  211. DWORD dwErr;
  212. ASSERT(PathIsUNCW(lpszPath));
  213. // Check for DFS
  214. dwErr = NetDfsGetClientInfo((LPWSTR)lpszPath, NULL, NULL, 1, (LPBYTE*)&pDI1);
  215. if (NERR_Success == dwErr)
  216. {
  217. // Note that EntryPath has only a single leading backslash, hence +1
  218. pszTail = lpszPath + lstrlenW(pDI1->EntryPath) + 1;
  219. ASSERT(pszTail <= lpszPath + lstrlenW(lpszPath));
  220. ASSERT(*pszTail == L'\\' || *pszTail == L'\0');
  221. NetApiBufferFree(pDI1);
  222. }
  223. return pszTail;
  224. }
  225. LPCWSTR
  226. FindDfsPathSplit(
  227. IN LPCWSTR lpszPath
  228. )
  229. {
  230. LPCWSTR pszTail = NULL;
  231. DWORD cbBuffer;
  232. DWORD dwErr;
  233. REMOTE_NAME_INFOW *pUncInfo;
  234. if (PathIsUNCW(lpszPath))
  235. return FindDfsUncSplit(lpszPath);
  236. ASSERT(PathIsNetworkPathW(lpszPath));
  237. // Get the UNC path.
  238. //
  239. // Note that WNetGetUniversalName returns ERROR_INVALID_PARAMETER if you
  240. // specify NULL for the buffer and 0 length (asking for size).
  241. cbBuffer = sizeof(REMOTE_NAME_INFOW) + MAX_PATH*sizeof(WCHAR); // initial guess
  242. pUncInfo = (REMOTE_NAME_INFOW*)LocalAlloc(LPTR, cbBuffer);
  243. if (pUncInfo)
  244. {
  245. dwErr = WNetGetUniversalNameW(lpszPath, REMOTE_NAME_INFO_LEVEL, pUncInfo, &cbBuffer);
  246. if (ERROR_MORE_DATA == dwErr)
  247. {
  248. // Alloc a new buffer and try again
  249. LocalFree(pUncInfo);
  250. pUncInfo = (REMOTE_NAME_INFOW*)LocalAlloc(LPTR, cbBuffer);
  251. if (pUncInfo)
  252. {
  253. dwErr = WNetGetUniversalNameW(lpszPath, REMOTE_NAME_INFO_LEVEL, pUncInfo, &cbBuffer);
  254. }
  255. }
  256. if (ERROR_SUCCESS == dwErr)
  257. {
  258. // Find the tail
  259. LPCWSTR pszUncTail = FindDfsUncSplit(pUncInfo->lpUniversalName);
  260. if (pszUncTail)
  261. {
  262. UINT cchJunction;
  263. UINT cchConnectionName;
  264. // It's a DFS path, so we'll at least return a
  265. // pointer to the drive root.
  266. ASSERT(lpszPath[0] != L'\0' && lpszPath[1] == L':');
  267. pszTail = lpszPath + 2; // skip "X:"
  268. // If the DFS junction is deeper than the drive mapping,
  269. // move the pointer to after the junction point.
  270. cchJunction = (UINT)(pszUncTail - pUncInfo->lpUniversalName);
  271. cchConnectionName = lstrlenW(pUncInfo->lpConnectionName);
  272. if (cchJunction > cchConnectionName)
  273. {
  274. pszTail += cchJunction - cchConnectionName;
  275. }
  276. ASSERT(pszTail <= lpszPath + lstrlenW(lpszPath));
  277. ASSERT(*pszTail == L'\\' || *pszTail == L'\0');
  278. }
  279. }
  280. LocalFree(pUncInfo);
  281. }
  282. return pszTail;
  283. }
  284. LPCWSTR
  285. FindSnapshotPathSplit(
  286. IN LPCWSTR lpszPath
  287. )
  288. /*++
  289. Routine Description:
  290. This routine looks at a path and determines where the snapshot token will be
  291. inserted
  292. Arguments:
  293. lpszPath - The path we're examining
  294. Return Value:
  295. LPWSTR (pointer to the insertion point within lpszPath)
  296. Notes:
  297. None
  298. --*/
  299. {
  300. LPCWSTR pszVolumeTail;
  301. LPCWSTR pszDfsTail;
  302. // FindVolumePathSplit accounts for mounted volumes, but not DFS.
  303. // FindDfsPathSplit accounts for DFS, but not mounted volumes.
  304. // Try both methods and pick the larger of the 2, if both succeed.
  305. pszVolumeTail = FindVolumePathSplit(lpszPath);
  306. pszDfsTail = FindDfsPathSplit(lpszPath);
  307. // Note that this comparison automatically handles the cases
  308. // where either or both pointers are NULL.
  309. if (pszDfsTail > pszVolumeTail)
  310. {
  311. pszVolumeTail = pszDfsTail;
  312. }
  313. return pszVolumeTail;
  314. }
  315. #define PREFIX_DRIVE L"\\\\?\\"
  316. #define PREFIX_UNC L"\\\\?\\UNC\\"
  317. #define MAX_PREFIX_LENGTH 8 // strlen(PREFIX_UNC)
  318. DWORD
  319. BuildSnapshotPathArray(
  320. IN ULONG lNumberOfSnapshots,
  321. IN LPWSTR pszSnapshotNameMultiSZ,
  322. IN LPCWSTR lpszPath,
  323. IN ULONG lFlags,
  324. OUT LPWSTR* lplpszPathArray,
  325. OUT LPDWORD lpdwPathCount
  326. )
  327. /*++
  328. Routine Description:
  329. This routine has the fun task of assembling an array of paths based on the
  330. snapshot name array, the path to the file, and the flags the user passed in.
  331. Arguments:
  332. lNumberOfSnapshots - The number of snapshots in the array
  333. pszSnapshotNameMultiSZ - A multi-SZ list of the snapshot names
  334. lpszPath - The path to the file
  335. lFlags - The query flags to determine what the user desires to be returned
  336. lplpszPathArray - Upon return, the allocated array of path names
  337. lpdwPathCount - Upon return, the number of paths in the array
  338. Return Value:
  339. Win32 Error
  340. Notes:
  341. None
  342. --*/
  343. {
  344. DWORD dwError;
  345. LPWSTR pPathMultiSZ;
  346. ULONG lPathSize;
  347. LPCWSTR pPathSplit;
  348. LPWSTR pPathCopy;
  349. LPWSTR pPathCopyStart;
  350. ULONG lPathFront, lPathBack;
  351. LPWSTR pSnapName = pszSnapshotNameMultiSZ;
  352. ULONG iCount;
  353. WIN32_FILE_ATTRIBUTE_DATA w32Attributes;
  354. FILETIME fModifiedTime;
  355. FILETIME fOriginalTime;
  356. // If the user only wants files that are different, we use the ModifiedTime field
  357. // to keep track of the last modified time so we can remove duplicates. This field
  358. // is initialized to the current last-modified time of the file. Since the snapshot
  359. // name array is passed back in newest-to-oldest format, we can simply compare the current
  360. // iteration to the previous one to determine if the file changed in the current snapshot
  361. // as we build the list
  362. if( lFlags & QUERY_SNAPSHOT_DIFFERENT )
  363. {
  364. fModifiedTime.dwHighDateTime = fModifiedTime.dwLowDateTime = 0;
  365. if( !GetFileAttributesEx( lpszPath, GetFileExInfoStandard, &w32Attributes ) )
  366. {
  367. return GetLastError();
  368. }
  369. fOriginalTime.dwHighDateTime = w32Attributes.ftLastWriteTime.dwHighDateTime;
  370. fOriginalTime.dwLowDateTime = w32Attributes.ftLastWriteTime.dwLowDateTime;
  371. }
  372. // Allocate the buffer to the maximum size we will need
  373. lPathSize = ((MAX_PREFIX_LENGTH+lstrlenW(lpszPath)+1+SNAPSHOT_NAME_LENGTH+1)*sizeof(WCHAR))*lNumberOfSnapshots + 2*sizeof(WCHAR);
  374. pPathMultiSZ = LocalAlloc( LPTR, lPathSize );
  375. if( pPathMultiSZ )
  376. {
  377. // For the path, we need to determine where the snapshot token will be inserted. It will be
  378. // placed as far left in the name as possible, at the volume junction point.
  379. pPathSplit = FindSnapshotPathSplit( lpszPath );
  380. if( pPathSplit )
  381. {
  382. // Because we are inserting an extra segment into the path, it is
  383. // easy to start with a valid path that is less than MAX_PATH, but
  384. // end up with something greater than MAX_PATH. Therefore, we add
  385. // the "\\?\" or "\\?\UNC\" prefix to override the maximum length.
  386. LPCWSTR pPrefix = PREFIX_DRIVE;
  387. ULONG lPrefix;
  388. if (PathIsUNCW(lpszPath))
  389. {
  390. pPrefix = PREFIX_UNC;
  391. lpszPath += 2; // skip backslashes
  392. }
  393. lPrefix = lstrlenW(pPrefix);
  394. pPathCopy = pPathMultiSZ;
  395. lPathBack = lstrlenW( pPathSplit );
  396. lPathFront = lstrlenW( lpszPath ) - lPathBack;
  397. // We now iterate through the snapshots and create the paths
  398. for( iCount=0; iCount<lNumberOfSnapshots; iCount++ )
  399. {
  400. BOOL bAcceptThisEntry = FALSE;
  401. pPathCopyStart = pPathCopy;
  402. // Copy the prefix
  403. RtlCopyMemory( pPathCopy, pPrefix, lPrefix*sizeof(WCHAR) );
  404. pPathCopy += lPrefix;
  405. // Copy the front portion of the path
  406. RtlCopyMemory( pPathCopy, lpszPath, lPathFront*sizeof(WCHAR) );
  407. pPathCopy += lPathFront;
  408. // Copy the seperator
  409. *pPathCopy++ = L'\\';
  410. // Copy the Snapshot name
  411. if (lstrlenW(pSnapName) < SNAPSHOT_NAME_LENGTH)
  412. {
  413. LocalFree( pPathMultiSZ );
  414. return ERROR_INVALID_PARAMETER;
  415. }
  416. RtlCopyMemory( pPathCopy, pSnapName, SNAPSHOT_NAME_LENGTH*sizeof(WCHAR) );
  417. pPathCopy += SNAPSHOT_NAME_LENGTH;
  418. // Copy the tail
  419. RtlCopyMemory( pPathCopy, pPathSplit, lPathBack*sizeof(WCHAR) );
  420. pPathCopy += lPathBack;
  421. // Copy the NULL
  422. *pPathCopy++ = L'\0';
  423. // A path is only included in the return result if it matches the users criteria
  424. if( lFlags & (QUERY_SNAPSHOT_EXISTING|QUERY_SNAPSHOT_DIFFERENT) )
  425. {
  426. // If they just want Existing, we query the attributes to confirm that the file exists
  427. if( GetFileAttributesEx( pPathCopyStart, GetFileExInfoStandard, &w32Attributes ) )
  428. {
  429. // If they want Different, we check the lastModifiedTime against the last iteration to
  430. // determine acceptance
  431. if( lFlags & QUERY_SNAPSHOT_DIFFERENT )
  432. {
  433. if( ((w32Attributes.ftLastWriteTime.dwHighDateTime != fModifiedTime.dwHighDateTime) ||
  434. (w32Attributes.ftLastWriteTime.dwLowDateTime != fModifiedTime.dwLowDateTime)) &&
  435. ((w32Attributes.ftLastWriteTime.dwHighDateTime != fOriginalTime.dwHighDateTime) ||
  436. (w32Attributes.ftLastWriteTime.dwLowDateTime != fOriginalTime.dwLowDateTime))
  437. )
  438. {
  439. // When we accept this entry, we update the LastModifiedTime for the next iteration
  440. fModifiedTime.dwLowDateTime = w32Attributes.ftLastWriteTime.dwLowDateTime;
  441. fModifiedTime.dwHighDateTime = w32Attributes.ftLastWriteTime.dwHighDateTime;
  442. bAcceptThisEntry = TRUE;
  443. }
  444. }
  445. else
  446. {
  447. bAcceptThisEntry = TRUE;
  448. }
  449. }
  450. }
  451. else
  452. {
  453. bAcceptThisEntry = TRUE;
  454. }
  455. if (!bAcceptThisEntry)
  456. {
  457. // Skip this entry and remove its reference
  458. pPathCopy = pPathCopyStart;
  459. lNumberOfSnapshots--;
  460. iCount--;
  461. }
  462. // Move the Snapshot Name forward
  463. pSnapName += (SNAPSHOT_NAME_LENGTH + 1);
  464. }
  465. // Append the final NULL
  466. *pPathCopy = L'\0';
  467. *lplpszPathArray = pPathMultiSZ;
  468. *lpdwPathCount = lNumberOfSnapshots;
  469. dwError = ERROR_SUCCESS;
  470. }
  471. else
  472. {
  473. // The name was invalid, return the fact
  474. dwError = ERROR_INVALID_PARAMETER;
  475. }
  476. // If we're failing, free the buffer
  477. if( ERROR_SUCCESS != dwError )
  478. {
  479. LocalFree( pPathMultiSZ );
  480. }
  481. }
  482. else
  483. {
  484. // Our allocation failed. Return the error
  485. dwError = ERROR_OUTOFMEMORY;
  486. }
  487. return dwError;
  488. }
  489. DWORD
  490. QuerySnapshotsForPath(
  491. IN LPCWSTR lpszFilePath,
  492. IN DWORD dwQueryFlags,
  493. OUT LPWSTR* ppszPathMultiSZ,
  494. OUT LPDWORD iNumberOfPaths )
  495. /*++
  496. Routine Description:
  497. This function takes a path and returns an array of snapshot-paths to the file.
  498. (These are the paths to be passed to Win32 functions to obtain handles to the
  499. previous versions of the file) This will be the only public export of the
  500. TimeWarp API
  501. Arguments:
  502. lpszFilePath - The UNICODE path to the file or directory
  503. dwQueryFlags - See Notes below
  504. ppszPathMultiSZ - Upon successful return, the allocated array of the path
  505. iNumberOfPaths - Upon successful return, the number of paths returned
  506. Return Value:
  507. Windows Error code
  508. Notes:
  509. - The user is responsible for freeing the returned buffer with LocalFree
  510. - The possible flags are:
  511. Return only the path names where the file exists
  512. #define QUERY_SNAPSHOT_EXISTING 0x1
  513. Return the minimum set of paths to the different versions of the
  514. files. (Does LastModifiedTime checking)
  515. #define QUERY_SNAPSHOT_DIFFERENT 0x2
  516. --*/
  517. {
  518. HANDLE hFile;
  519. DWORD dwError;
  520. LPWSTR pSnapshotNameArray = NULL;
  521. DWORD dwNumberOfSnapshots = 0;
  522. LPWSTR pSnapshotPathArray = NULL;
  523. DWORD dwSnapshotPathSize = 0;
  524. DWORD dwFinalSnapshoutCount = 0;
  525. ASSERT( lpszFilePath );
  526. dwError = OpenFileForSnapshot( lpszFilePath, &hFile );
  527. if( dwError == ERROR_SUCCESS )
  528. {
  529. // Get the array of names
  530. dwError = QuerySnapshotNames( hFile, &pSnapshotNameArray, &dwNumberOfSnapshots );
  531. if( dwError == ERROR_SUCCESS )
  532. {
  533. // Calculate the necessary string size
  534. if (dwNumberOfSnapshots > 0)
  535. {
  536. dwError = BuildSnapshotPathArray( dwNumberOfSnapshots, pSnapshotNameArray, lpszFilePath, dwQueryFlags, &pSnapshotPathArray, &dwFinalSnapshoutCount );
  537. }
  538. else
  539. {
  540. dwError = ERROR_NOT_FOUND;
  541. }
  542. if( dwError == ERROR_SUCCESS )
  543. {
  544. *ppszPathMultiSZ = pSnapshotPathArray;
  545. *iNumberOfPaths = dwFinalSnapshoutCount;
  546. }
  547. // Release the name array buffer
  548. LocalFree( pSnapshotNameArray );
  549. pSnapshotNameArray = NULL;
  550. }
  551. CloseHandle( hFile );
  552. }
  553. return dwError;
  554. }
  555. BOOLEAN
  556. ExtractNumber(
  557. IN PCWSTR psz,
  558. IN ULONG Count,
  559. OUT CSHORT* value
  560. )
  561. /*++
  562. Routine Description:
  563. This function takes a string of characters and parses out a <Count> length decimal
  564. number. If it returns TRUE, value has been set and the string was parsed correctly.
  565. FALSE indicates an error in parsing.
  566. Arguments:
  567. psz - String pointer
  568. Count - Number of characters to pull off
  569. value - pointer to output parameter where value is stored
  570. Return Value:
  571. BOOLEAN - See description
  572. --*/
  573. {
  574. *value = 0;
  575. while( Count )
  576. {
  577. if( (*psz == L'\0') ||
  578. (*psz == L'\\') )
  579. {
  580. return FALSE;
  581. }
  582. if( (*psz < '0') || (*psz > '9') )
  583. {
  584. return FALSE;
  585. }
  586. *value = (*value)*10+(*psz-L'0');
  587. Count--;
  588. psz++;
  589. }
  590. return TRUE;
  591. }
  592. DWORD
  593. GetSnapshotTimeFromPath(
  594. IN LPCWSTR lpszFilePath,
  595. IN OUT FILETIME* pUTCTime
  596. )
  597. {
  598. PCWSTR pszPath = lpszFilePath;
  599. TIME_FIELDS LocalTimeFields;
  600. CSHORT lValue;
  601. // Find the token
  602. pszPath = wcsstr( lpszFilePath, SNAPSHOT_MARKER );
  603. if( !pszPath )
  604. {
  605. return ERROR_INVALID_PARAMETER;
  606. }
  607. // Skip the GMT header
  608. pszPath += 5;
  609. // Pull the Year
  610. if( !ExtractNumber( pszPath, 4, &lValue ) )
  611. {
  612. return ERROR_INVALID_PARAMETER;
  613. }
  614. LocalTimeFields.Year = lValue;
  615. pszPath += 4;
  616. // Skip the seperator
  617. if( *pszPath != L'.' )
  618. {
  619. return ERROR_INVALID_PARAMETER;
  620. }
  621. pszPath++;
  622. // Pull the Month
  623. if( !ExtractNumber( pszPath, 2, &lValue ) )
  624. {
  625. return ERROR_INVALID_PARAMETER;
  626. }
  627. LocalTimeFields.Month = lValue;
  628. pszPath += 2;
  629. // Skip the seperator
  630. if( *pszPath != L'.' )
  631. {
  632. return ERROR_INVALID_PARAMETER;
  633. }
  634. pszPath++;
  635. // Pull the Day
  636. if( !ExtractNumber( pszPath, 2, &lValue ) )
  637. {
  638. return ERROR_INVALID_PARAMETER;
  639. }
  640. LocalTimeFields.Day = lValue;
  641. pszPath += 2;
  642. // Skip the seperator
  643. if( *pszPath != L'-' )
  644. {
  645. return ERROR_INVALID_PARAMETER;
  646. }
  647. pszPath++;
  648. // Pull the Hour
  649. if( !ExtractNumber( pszPath, 2, &lValue ) )
  650. {
  651. return ERROR_INVALID_PARAMETER;
  652. }
  653. LocalTimeFields.Hour = lValue;
  654. pszPath += 2;
  655. // Skip the seperator
  656. if( *pszPath != L'.' )
  657. {
  658. return ERROR_INVALID_PARAMETER;
  659. }
  660. pszPath++;
  661. // Pull the Minute
  662. if( !ExtractNumber( pszPath, 2, &lValue ) )
  663. {
  664. return ERROR_INVALID_PARAMETER;
  665. }
  666. LocalTimeFields.Minute = lValue;
  667. pszPath += 2;
  668. // Skip the seperator
  669. if( *pszPath != L'.' )
  670. {
  671. return ERROR_INVALID_PARAMETER;
  672. }
  673. pszPath++;
  674. // Pull the Seconds
  675. if( !ExtractNumber( pszPath, 2, &lValue ) )
  676. {
  677. return ERROR_INVALID_PARAMETER;
  678. }
  679. LocalTimeFields.Second = lValue;
  680. pszPath += 2;
  681. // Make sure the seperator is there
  682. if( (*pszPath != L'\\') && (*pszPath != L'\0') )
  683. {
  684. return ERROR_INVALID_PARAMETER;
  685. }
  686. LocalTimeFields.Milliseconds = 0;
  687. LocalTimeFields.Weekday = 0;
  688. RtlTimeFieldsToTime( &LocalTimeFields, (LARGE_INTEGER*)pUTCTime );
  689. return ERROR_SUCCESS;
  690. }