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.

1731 lines
47 KiB

  1. /*++
  2. Copyright (c) 2001 Microsoft Corporation
  3. Module Name:
  4. srvsnap.c
  5. Abstract:
  6. This module contains routines for supporting the SnapShot feature
  7. that interfaces CIFS with SnapShots
  8. Author:
  9. David Kruse (dkruse) 22-March-2001
  10. Revision History:
  11. --*/
  12. #include "precomp.h"
  13. #include <initguid.h>
  14. #include <mountmgr.h>
  15. #include <strsafe.h>
  16. #include "srvsupp.h"
  17. #include "ntddsnap.h"
  18. #include "stdarg.h"
  19. #include "stdio.h"
  20. #include "srvsnap.tmh"
  21. #pragma hdrstop
  22. #define MAX_SNAPSHOTS_PER_SHARE 500
  23. // Function Declaractions
  24. NTSTATUS
  25. StartIoAndWait (
  26. IN PIRP Irp,
  27. IN PDEVICE_OBJECT DeviceObject,
  28. IN PKEVENT Event,
  29. IN PIO_STATUS_BLOCK IoStatusBlock
  30. );
  31. PIRP
  32. BuildCoreOfSyncIoRequest (
  33. IN HANDLE FileHandle,
  34. IN PFILE_OBJECT FileObject OPTIONAL,
  35. IN PKEVENT Event,
  36. IN PIO_STATUS_BLOCK IoStatusBlock,
  37. IN OUT PDEVICE_OBJECT *DeviceObject
  38. );
  39. NTSTATUS
  40. SrvSnapIssueIoctl(
  41. IN HANDLE hFile,
  42. IN DWORD IOCtl,
  43. IN OUT PVOID pBuffer,
  44. IN OUT LPDWORD lpdwBufSize
  45. );
  46. NTSTATUS
  47. SrvSnapGetNamesForVolume(
  48. IN HANDLE hFile,
  49. OUT PVOLSNAP_NAMES* Names
  50. );
  51. NTSTATUS
  52. SrvSnapGetEpicForVolume(
  53. IN HANDLE hFile,
  54. OUT PLONG Epic
  55. );
  56. NTSTATUS
  57. SrvSnapFillConfigInfo(
  58. IN PSHARE_SNAPSHOT SnapShare,
  59. IN PUNICODE_STRING SnapShotPath
  60. );
  61. BOOLEAN
  62. SrvParseMultiSZ(
  63. IN OUT PUNICODE_STRING mszString
  64. );
  65. NTSTATUS
  66. SrvSnapInsertSnapIntoShare(
  67. IN PSHARE Share,
  68. IN PSHARE_SNAPSHOT SnapShot
  69. );
  70. NTSTATUS
  71. SrvSnapAddShare(
  72. IN PSHARE Share,
  73. IN PUNICODE_STRING SnapPath
  74. );
  75. NTSTATUS
  76. SrvSnapRemoveShare(
  77. IN PSHARE_SNAPSHOT SnapShare
  78. );
  79. NTSTATUS
  80. SrvSnapCheckForAndCreateSnapShare(
  81. IN PSHARE Share,
  82. IN PUNICODE_STRING SnapShotName
  83. );
  84. NTSTATUS
  85. SrvSnapRefreshSnapShotsForShare(
  86. IN PSHARE Share
  87. );
  88. NTSTATUS
  89. SrvSnapEnumerateSnapShots(
  90. IN PWORK_CONTEXT WorkContext
  91. );
  92. NTSTATUS
  93. SrvSnapGetRootHandle(
  94. IN PWORK_CONTEXT WorkContext,
  95. OUT HANDLE* RootHandle
  96. );
  97. NTSTATUS
  98. SrvSnapGetNameString(
  99. IN PWORK_CONTEXT WorkContext,
  100. OUT PUNICODE_STRING* ShareName,
  101. OUT PBOOLEAN AllocatedShareName
  102. );
  103. BOOLEAN
  104. ExtractNumber(
  105. IN PWSTR psz,
  106. IN ULONG Count,
  107. OUT PLONG value
  108. );
  109. BOOLEAN
  110. SrvSnapParseToken(
  111. IN PWSTR Source,
  112. IN ULONG SourceSizeInBytes,
  113. OUT PLARGE_INTEGER TimeStamp
  114. );
  115. NTSTATUS
  116. SrvSnapCheckAppInfoForTimeWarp(
  117. IN PUNICODE_STRING SnapShotHandle
  118. );
  119. #ifdef ALLOC_PRAGMA
  120. #pragma alloc_text( PAGE, SrvSnapIssueIoctl )
  121. #pragma alloc_text( PAGE, SrvSnapGetNamesForVolume )
  122. #pragma alloc_text( PAGE, SrvSnapGetEpicForVolume )
  123. #pragma alloc_text( PAGE, SrvSnapFillConfigInfo )
  124. #pragma alloc_text( PAGE, SrvParseMultiSZ )
  125. #pragma alloc_text( PAGE, SrvSnapInsertSnapIntoShare )
  126. #pragma alloc_text( PAGE, SrvSnapAddShare )
  127. #pragma alloc_text( PAGE, SrvSnapRemoveShare )
  128. #pragma alloc_text( PAGE, SrvSnapCheckForAndCreateSnapShare )
  129. #pragma alloc_text( PAGE, SrvSnapRefreshSnapShotsForShare )
  130. #pragma alloc_text( PAGE, SrvSnapEnumerateSnapShots )
  131. #pragma alloc_text( PAGE, SrvSnapGetRootHandle )
  132. #pragma alloc_text( PAGE, SrvSnapGetNameString )
  133. #pragma alloc_text( PAGE, ExtractNumber )
  134. #pragma alloc_text( PAGE, SrvSnapParseToken )
  135. #pragma alloc_text( PAGE, SrvSnapCheckAppInfoForTimeWarp )
  136. #pragma alloc_text( PAGE, SrvSnapEnumerateSnapShotsAsDirInfo )
  137. #endif
  138. //
  139. // Helper Functions
  140. //
  141. NTSTATUS
  142. SrvSnapIssueIoctl(
  143. IN HANDLE hFile,
  144. IN DWORD IOCtl,
  145. IN OUT PVOID pBuffer,
  146. IN OUT LPDWORD lpdwBufSize
  147. )
  148. /*++
  149. Routine Description:
  150. This function takes a volume handle and attempts to enumerate all the
  151. SnapShots on that volume into the given buffer
  152. Arguments:
  153. hFile - The handle to the volume
  154. IOCtl - the IoControl to issue
  155. pBuffer - A pointer to the output buffer
  156. lpdwBufSize - Pointer to the size of passed in buffer. Set to the
  157. required size if STATUS_BUFFER_OVERFLOW is returned
  158. Return Value:
  159. NTSTATUS - Expected return codes are STATUS_SUCCESS or STATUS_BUFFER_OVERFLOW.
  160. Any other response is an unexpected error code and the request should be
  161. failed
  162. --*/
  163. {
  164. PIRP Irp = NULL;
  165. NTSTATUS Status;
  166. IO_STATUS_BLOCK IoStatus;
  167. KEVENT CompletionEvent;
  168. PDEVICE_OBJECT DeviceObject = NULL;
  169. PIO_STACK_LOCATION IrpSp;
  170. PAGED_CODE();
  171. // Initialize the variables
  172. KeInitializeEvent( &CompletionEvent, SynchronizationEvent, FALSE );
  173. // Create the IRP
  174. Irp = BuildCoreOfSyncIoRequest(
  175. hFile,
  176. NULL,
  177. &CompletionEvent,
  178. &IoStatus,
  179. &DeviceObject );
  180. if( !Irp )
  181. {
  182. return STATUS_INSUFFICIENT_RESOURCES;
  183. }
  184. // Initialize the other IRP fields
  185. Irp->Flags |= (LONG)IRP_BUFFERED_IO;
  186. Irp->AssociatedIrp.SystemBuffer = pBuffer;
  187. IrpSp = IoGetNextIrpStackLocation( Irp );
  188. IrpSp->MajorFunction = IRP_MJ_DEVICE_CONTROL;
  189. IrpSp->MinorFunction = 0;
  190. IrpSp->Parameters.DeviceIoControl.OutputBufferLength = *lpdwBufSize;
  191. IrpSp->Parameters.DeviceIoControl.InputBufferLength = 0;
  192. IrpSp->Parameters.DeviceIoControl.IoControlCode = IOCtl;
  193. IrpSp->Parameters.DeviceIoControl.Type3InputBuffer = NULL;
  194. // Issue the IO
  195. Status = StartIoAndWait( Irp, DeviceObject, &CompletionEvent, &IoStatus );
  196. // If this was a buffer overflow, update the value
  197. if( Status == STATUS_BUFFER_OVERFLOW )
  198. {
  199. *lpdwBufSize = (DWORD)(IoStatus.Information);
  200. }
  201. return Status;
  202. }
  203. NTSTATUS
  204. SrvSnapGetNamesForVolume(
  205. IN HANDLE hFile,
  206. OUT PVOLSNAP_NAMES* Names
  207. )
  208. /*++
  209. Routine Description:
  210. This function takes a volume handle and returns the list of SnapShots for that
  211. volume. If an error occurs, or no SnapShots exist, the returned pointer is
  212. NULL. NOTE: The caller is responsible for freeing the returned pointer via
  213. DEALLOCATE_NONPAGED_POOL
  214. Arguments:
  215. hFile - The handle to the volume
  216. Names - A pointer to the pointer where we will store the volume name list
  217. Return Value:
  218. NTSTATUS - Expected return code is STATUS_SUCCESS . Any other response is an
  219. unexpected error code and the request should be failed
  220. --*/
  221. {
  222. NTSTATUS Status;
  223. VOLSNAP_NAMES VolNamesBase;
  224. PVOLSNAP_NAMES pNames = NULL;
  225. DWORD dwSize = sizeof(VOLSNAP_NAMES);
  226. PAGED_CODE();
  227. // Initialize the values
  228. *Names = NULL;
  229. // Find out how big it should be
  230. Status = SrvSnapIssueIoctl( hFile, IOCTL_VOLSNAP_QUERY_NAMES_OF_SNAPSHOTS, &VolNamesBase, &dwSize );
  231. if( Status != STATUS_BUFFER_OVERFLOW )
  232. {
  233. return Status;
  234. }
  235. // Allocate the correct size block
  236. dwSize = VolNamesBase.MultiSzLength + sizeof(VOLSNAP_NAMES);
  237. pNames = (PVOLSNAP_NAMES)ALLOCATE_NONPAGED_POOL( dwSize, BlockTypeSnapShot );
  238. if( !pNames )
  239. {
  240. return STATUS_INSUFFICIENT_RESOURCES;
  241. }
  242. // Build up the IRP to be used for the query
  243. Status = SrvSnapIssueIoctl( hFile, IOCTL_VOLSNAP_QUERY_NAMES_OF_SNAPSHOTS, pNames, &dwSize );
  244. // Save the output if we're successful, or else deallocate
  245. if( !NT_SUCCESS(Status) )
  246. {
  247. if( pNames )
  248. {
  249. DEALLOCATE_NONPAGED_POOL( pNames );
  250. pNames = NULL;
  251. *Names = NULL;
  252. }
  253. }
  254. else
  255. {
  256. ASSERT(pNames);
  257. *Names = pNames;
  258. }
  259. return Status;
  260. }
  261. NTSTATUS
  262. SrvSnapGetEpicForVolume(
  263. IN HANDLE hFile,
  264. OUT PLONG Epic
  265. )
  266. /*++
  267. Routine Description:
  268. This function takes a volume handle and returns the epic number for the volume that
  269. hosts the share
  270. Arguments:
  271. hFile - The handle to the volume
  272. Epic - Pointer to LONG that will receive the data
  273. Return Value:
  274. NTSTATUS - Expected return code is STATUS_SUCCESS . Any other response is an
  275. unexpected error code and the request should be failed
  276. --*/
  277. {
  278. NTSTATUS Status;
  279. VOLSNAP_EPIC VolEpic;
  280. DWORD dwSize = sizeof(VOLSNAP_EPIC);
  281. PAGED_CODE();
  282. // Build up the IRP to be used for the query
  283. Status = SrvSnapIssueIoctl( hFile, IOCTL_VOLSNAP_QUERY_EPIC, &VolEpic, &dwSize );
  284. if( NT_SUCCESS(Status) )
  285. {
  286. *Epic = VolEpic.EpicNumber;
  287. }
  288. else
  289. {
  290. *Epic = -1;
  291. }
  292. return Status;
  293. }
  294. NTSTATUS
  295. SrvSnapFillConfigInfo(
  296. IN PSHARE_SNAPSHOT SnapShare,
  297. IN PUNICODE_STRING SnapShotPath
  298. )
  299. /*++
  300. Routine Description:
  301. This function takes a SnapShare with existing names filled in and
  302. queries the extra config info (the timestamp, the epic) for that SnapShot
  303. Arguments:
  304. SnapShare - Pointer to the SnapShot share to be filled in
  305. Return Value:
  306. NTSTATUS - Expected return code is STATUS_SUCCESS . Any other response is an
  307. unexpected error code and the request should be failed
  308. --*/
  309. {
  310. HANDLE hVolume;
  311. DWORD dwSize = sizeof(VOLSNAP_CONFIG_INFO);
  312. NTSTATUS Status;
  313. OBJECT_ATTRIBUTES objectAttributes;
  314. VOLSNAP_CONFIG_INFO ConfigInfo;
  315. IO_STATUS_BLOCK IoStatus;
  316. PAGED_CODE();
  317. // Now open the SnapShot handle
  318. InitializeObjectAttributes(
  319. &objectAttributes,
  320. SnapShotPath,
  321. OBJ_CASE_INSENSITIVE,
  322. NULL,
  323. NULL
  324. );
  325. Status = NtOpenFile(
  326. &hVolume,
  327. 0,
  328. &objectAttributes,
  329. &IoStatus,
  330. FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
  331. 0
  332. );
  333. if( !NT_SUCCESS(Status) )
  334. {
  335. return Status;
  336. }
  337. // Get the timestamp
  338. Status = SrvSnapIssueIoctl( hVolume, IOCTL_VOLSNAP_QUERY_CONFIG_INFO, &ConfigInfo, &dwSize );
  339. if( NT_SUCCESS( Status ) )
  340. {
  341. // Fill in the info
  342. SnapShare->Timestamp = ConfigInfo.SnapshotCreationTime;
  343. }
  344. NtClose( hVolume );
  345. return Status;
  346. }
  347. BOOLEAN
  348. SrvParseMultiSZ(
  349. IN OUT PUNICODE_STRING mszString
  350. )
  351. /*++
  352. Routine Description:
  353. This function takes a UNICODE_STRING that points to a MultiSZ value, and
  354. parses it with each iteration (similar to strtok( psz, "\0" )). For every
  355. iteration this returns TRUE, the string will point to the next SZ in the
  356. MultiSZ
  357. Arguments:
  358. mszString - Pointer to the MultiSZ string. For the first iteration, set the
  359. MaximumLength to the length of the MultiSZ, and the Length to 0. For all
  360. other iterations, simply pass in the string from the previous iteration
  361. Return Value:
  362. BOOLEAN - If TRUE, this is a valid SZ. If FALSE, all have been parsed
  363. --*/
  364. {
  365. USHORT Count;
  366. PAGED_CODE();
  367. ASSERT( mszString->Length <= mszString->MaximumLength );
  368. if( mszString->Length > 0 )
  369. {
  370. // Move the pointer past the string and the NULL
  371. mszString->Buffer += (mszString->Length/2)+1;
  372. mszString->MaximumLength -= (mszString->Length+2);
  373. }
  374. for( Count=0; Count<mszString->MaximumLength; Count++ )
  375. {
  376. if( mszString->Buffer[Count] == (WCHAR)'\0' )
  377. {
  378. mszString->Length = Count*2;
  379. if( Count > 0 )
  380. return TRUE;
  381. else
  382. return FALSE;
  383. }
  384. }
  385. // The starting data was bad!
  386. ASSERT(FALSE);
  387. return FALSE;
  388. }
  389. NTSTATUS
  390. SrvSnapInsertSnapIntoShare(
  391. IN PSHARE Share,
  392. IN PSHARE_SNAPSHOT SnapShot
  393. )
  394. {
  395. PLIST_ENTRY ListEntry = Share->SnapShots.Flink;
  396. PAGED_CODE();
  397. // Walk the SnapShot share list and see if this is a duplicate
  398. while( ListEntry != &Share->SnapShots )
  399. {
  400. PSHARE_SNAPSHOT snapShare = CONTAINING_RECORD( ListEntry, SHARE_SNAPSHOT, SnapShotList );
  401. if( RtlEqualUnicodeString( &SnapShot->SnapShotName, &snapShare->SnapShotName, TRUE ) )
  402. {
  403. return STATUS_DUPLICATE_NAME;
  404. }
  405. ListEntry = ListEntry->Flink;
  406. }
  407. InsertTailList( &Share->SnapShots, &SnapShot->SnapShotList );
  408. return STATUS_SUCCESS;
  409. }
  410. NTSTATUS
  411. SrvSnapAddShare(
  412. IN PSHARE Share,
  413. IN PUNICODE_STRING SnapPath
  414. )
  415. /*++
  416. Routine Description:
  417. This function allocates a SnapShot Share that is being added to the system and
  418. initializes it
  419. Arguments:
  420. Share - Pointer to the parent share
  421. SnapPath - UNICODE_STRING name of the SnapShot (\\Device\\HardDiskSnap1)
  422. NOTE: Caller must have the SnapShotLock acquired
  423. Return Value:
  424. NTSTATUS - Returns STATUS_SUCCESS or STATUS_INSUFFICIENT_RESOURCES
  425. --*/
  426. {
  427. #define SHARE_DEVICE_HEADER 6
  428. NTSTATUS Status;
  429. PSHARE_SNAPSHOT snapShare = NULL;
  430. OBJECT_ATTRIBUTES objectAttributes;
  431. IO_STATUS_BLOCK IoStatus;
  432. ULONG AllocSize;
  433. TIME_FIELDS rtlTime;
  434. WCHAR FieldSeperator = L'\\';
  435. PAGED_CODE();
  436. IF_DEBUG( SNAPSHOT) KdPrint(( "SrvSnapAddShare %p %wZ\n", Share, SnapPath ));
  437. // Calculate the size to allocate
  438. // sizeof(SNAP_STRUCT) + (Length of SnapShotName) + (Max Length of SnapShot Path)
  439. AllocSize = sizeof(SHARE_SNAPSHOT) + SNAPSHOT_NAME_LENGTH + (SnapPath->Length + Share->RelativePath.Length + 2);
  440. snapShare = ALLOCATE_HEAP( AllocSize, BlockTypeSnapShot );
  441. if( !snapShare )
  442. {
  443. return STATUS_INSUFFICIENT_RESOURCES;
  444. }
  445. // Initialize the SnapShot-share structure
  446. RtlZeroMemory( snapShare, sizeof(SHARE_SNAPSHOT) );
  447. snapShare->SnapShotName.MaximumLength = SNAPSHOT_NAME_LENGTH;
  448. snapShare->SnapShotName.Length = 0;
  449. snapShare->SnapShotName.Buffer = (PWCHAR)(snapShare+1);
  450. // This is only valid on shares who's NT Path is \??\X:\ where X is a logical drive
  451. // Don't allow any others
  452. if( Share->NtPathName.Length < (SHARE_DEVICE_HEADER+1)*sizeof(WCHAR) )
  453. {
  454. Status = STATUS_INVALID_PARAMETER;
  455. goto Cleanup;
  456. }
  457. // Build the new SnapShot-relative path
  458. snapShare->SnapShotPath.MaximumLength = SnapPath->Length + Share->RelativePath.Length + 2;
  459. snapShare->SnapShotPath.Length = 0;
  460. snapShare->SnapShotPath.Buffer = snapShare->SnapShotName.Buffer + (snapShare->SnapShotName.MaximumLength/sizeof(WCHAR));
  461. // Build the path by starting with the device name
  462. RtlCopyUnicodeString( &snapShare->SnapShotPath, SnapPath );
  463. // Append the middle whack
  464. RtlCopyMemory( snapShare->SnapShotPath.Buffer + (snapShare->SnapShotPath.Length/sizeof(WCHAR)), &FieldSeperator, sizeof(WCHAR) );
  465. snapShare->SnapShotPath.Length += sizeof(WCHAR);
  466. // Now append the Relative Path name
  467. RtlCopyMemory( snapShare->SnapShotPath.Buffer + (snapShare->SnapShotPath.Length/sizeof(WCHAR)), Share->RelativePath.Buffer, Share->RelativePath.Length );
  468. snapShare->SnapShotPath.Length += Share->RelativePath.Length;
  469. //DbgPrint( "%wZ => %wZ\n", &Share->NtPathName, &snapShare->SnapShotPath );
  470. //IF_DEBUG( SNAPSHOT ) KdPrint(( "%wZ -> %wZ\n", &Share->NtPathName, &snapShare->SnapShotPath ));
  471. // Now open the relative handle
  472. InitializeObjectAttributes(
  473. &objectAttributes,
  474. &snapShare->SnapShotPath,
  475. OBJ_CASE_INSENSITIVE,
  476. NULL,
  477. NULL
  478. );
  479. Status = NtOpenFile(
  480. &snapShare->SnapShotRootDirectoryHandle,
  481. FILE_TRAVERSE,
  482. &objectAttributes,
  483. &IoStatus,
  484. FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
  485. 0
  486. );
  487. if( !NT_SUCCESS(Status) )
  488. {
  489. goto Cleanup;
  490. }
  491. // Make sure its a timewarp snapshot
  492. Status = SrvSnapCheckAppInfoForTimeWarp( SnapPath );
  493. if( !NT_SUCCESS(Status) )
  494. {
  495. NtClose( snapShare->SnapShotRootDirectoryHandle );
  496. goto Cleanup;
  497. }
  498. // Fill in the configuration information
  499. Status = SrvSnapFillConfigInfo( snapShare, SnapPath );
  500. if( !NT_SUCCESS(Status) )
  501. {
  502. NtClose( snapShare->SnapShotRootDirectoryHandle );
  503. goto Cleanup;
  504. }
  505. // Generate the SnapShot name
  506. snapShare->SnapShotName.Length = SNAPSHOT_NAME_LENGTH-sizeof(WCHAR);
  507. RtlTimeToTimeFields( &snapShare->Timestamp, &rtlTime );
  508. rtlTime.Milliseconds = 0;
  509. rtlTime.Weekday = 0;
  510. if( !SUCCEEDED( StringCbPrintf( snapShare->SnapShotName.Buffer, SNAPSHOT_NAME_LENGTH, SNAPSHOT_NAME_FORMAT, rtlTime.Year, rtlTime.Month, rtlTime.Day, rtlTime.Hour, rtlTime.Minute, rtlTime.Second ) ) )
  511. {
  512. Status = STATUS_INTERNAL_ERROR;
  513. NtClose( snapShare->SnapShotRootDirectoryHandle );
  514. goto Cleanup;
  515. }
  516. RtlTimeFieldsToTime( &rtlTime, &snapShare->Timestamp );
  517. ASSERT( wcslen( snapShare->SnapShotName.Buffer )*sizeof(WCHAR) == snapShare->SnapShotName.Length );
  518. // Insert it into the list, and return Success
  519. Status = SrvSnapInsertSnapIntoShare( Share, snapShare );
  520. if( !NT_SUCCESS(Status) ) {
  521. NtClose( snapShare->SnapShotRootDirectoryHandle );
  522. goto Cleanup;
  523. }
  524. //IF_DEBUG( SNAPSHOT ) KdPrint(( "%wZ Handle=%p\n", &snapShare->SnapShotPath, snapShare->SnapShotRootDirectoryHandle ));
  525. Cleanup:
  526. // Cleanup
  527. if( !NT_SUCCESS(Status) )
  528. {
  529. if( snapShare ) FREE_HEAP( snapShare );
  530. }
  531. return Status;
  532. }
  533. NTSTATUS
  534. SrvSnapRemoveShare(
  535. IN PSHARE_SNAPSHOT SnapShare
  536. )
  537. /*++
  538. Routine Description:
  539. This function deallocates a SnapShot Share after it has been removed from
  540. the underlying disk
  541. NOTE: Caller must have the SnapShotLock acquired
  542. Arguments:
  543. SnapShare - Pointer to the SnapShot Share to remove. It will be removed and
  544. deallocated.
  545. Return Value:
  546. NTSTATUS - Returns STATUS_SUCCESS
  547. --*/
  548. {
  549. PAGED_CODE();
  550. IF_DEBUG( SNAPSHOT ) KdPrint(( "SrvSnapRemoveShare %p %wZ\n", SnapShare, &SnapShare->SnapShotName ));
  551. if( SnapShare->SnapShotRootDirectoryHandle )
  552. {
  553. NtClose( SnapShare->SnapShotRootDirectoryHandle );
  554. SnapShare->SnapShotRootDirectoryHandle = NULL;
  555. }
  556. RemoveEntryList( &SnapShare->SnapShotList );
  557. FREE_HEAP( SnapShare );
  558. return STATUS_SUCCESS;
  559. }
  560. NTSTATUS
  561. SrvSnapCheckForAndCreateSnapShare(
  562. IN PSHARE Share,
  563. IN PUNICODE_STRING SnapShotName
  564. )
  565. /*++
  566. Routine Description:
  567. This function checks to see if a given SnapShot share exists on the given
  568. share, and if it does not it creates one. If it does, it removes the NOT_FOUND
  569. flag to signify this share still exists
  570. NOTE: Caller must have the SnapShotLock acquired
  571. Arguments:
  572. Share - The parent share of the SnapShot
  573. SnapShotName - The UNICODE_STRING name of the SnapShot (\\Device\\HardDiskSnap1)
  574. Return Value:
  575. NTSTATUS - Returns STATUS_SUCCESS or an unexpected error
  576. --*/
  577. {
  578. PLIST_ENTRY snapList;
  579. UNICODE_STRING SnapPartialName;
  580. PAGED_CODE();
  581. //IF_DEBUG( SNAPSHOT ) KdPrint(( "Share %x, Name %wZ\n", Share, SnapShotName ));
  582. snapList = Share->SnapShots.Flink;
  583. SnapPartialName.Length = SnapShotName->Length;
  584. while( snapList != &Share->SnapShots )
  585. {
  586. PSHARE_SNAPSHOT snapShare = CONTAINING_RECORD( snapList, SHARE_SNAPSHOT, SnapShotList );
  587. snapList = snapList->Flink;
  588. // Strip the trailing \ off the name
  589. SnapPartialName.Buffer = snapShare->SnapShotPath.Buffer;
  590. if( (snapShare->SnapShotPath.Length >= SnapPartialName.Length) &&
  591. RtlEqualUnicodeString( SnapShotName, &SnapPartialName, TRUE ) &&
  592. ( (snapShare->SnapShotPath.Length == SnapShotName->Length) ||
  593. (snapShare->SnapShotPath.Buffer[ SnapShotName->Length/sizeof(WCHAR) ] == L'\\') ) )
  594. {
  595. if( NT_SUCCESS( SrvSnapCheckAppInfoForTimeWarp( &SnapPartialName ) ) )
  596. {
  597. ClearFlag( snapShare->Flags, SRV_SNAP_SHARE_NOT_FOUND );
  598. }
  599. return STATUS_SUCCESS;
  600. }
  601. }
  602. return SrvSnapAddShare( Share, SnapShotName );
  603. }
  604. NTSTATUS
  605. SrvSnapSetVolumeHandle(
  606. IN PSHARE Share
  607. )
  608. {
  609. PFILE_OBJECT ShareFileObject;
  610. NTSTATUS status;
  611. KEVENT event;
  612. PMOUNTDEV_NAME name;
  613. UCHAR buffer[512];
  614. PIRP irp;
  615. IO_STATUS_BLOCK ioStatus;
  616. UNICODE_STRING VolumeName;
  617. OBJECT_ATTRIBUTES objectAttributes;
  618. if( Share->ShareVolumeHandle != NULL )
  619. {
  620. return STATUS_SUCCESS;
  621. }
  622. // We have a handle to the volume, get the FILE_OBJECT
  623. status = ObReferenceObjectByHandle( Share->RootDirectoryHandle, SYNCHRONIZE|FILE_TRAVERSE, NULL, KernelMode, &ShareFileObject, NULL );
  624. if( !NT_SUCCESS(status) )
  625. {
  626. return status;
  627. }
  628. try {
  629. KeInitializeEvent(&event, NotificationEvent, FALSE);
  630. name = (PMOUNTDEV_NAME) buffer;
  631. irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTDEV_QUERY_DEVICE_NAME,
  632. ShareFileObject->DeviceObject, NULL, 0, name,
  633. 512, FALSE, &event, &ioStatus);
  634. if (!irp) {
  635. status = STATUS_INSUFFICIENT_RESOURCES;
  636. leave;
  637. }
  638. status = IoCallDriver(ShareFileObject->DeviceObject, irp);
  639. if (status == STATUS_PENDING) {
  640. KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
  641. status = ioStatus.Status;
  642. }
  643. if (!NT_SUCCESS(status)) {
  644. leave;
  645. }
  646. VolumeName.Length = VolumeName.MaximumLength = name->NameLength;
  647. VolumeName.Buffer = name->Name;
  648. /*
  649. DbgPrint( "Share %p\n", Share );
  650. DbgPrint( "Share Nt Path %wZ\n", &Share->NtPathName );
  651. DbgPrint( "Share Dos Path %wZ\n", &Share->DosPathName );
  652. DbgPrint( "Share File Path %wZ\n", &ShareFileObject->FileName );
  653. DbgPrint( "Share Volume Path %wZ\n", &VolumeName );
  654. */
  655. // Determine the volume relative path
  656. // The relative path is equal to the path relative to the volume that the share location resides on.
  657. // We can determine it by looking at the file name for the Root Directory FILE_OBJECT and removing 1 character
  658. // representing the trailing \, and then compining this with the NtPathName (Which has no trailing \)
  659. Share->RelativePath.Length = (ShareFileObject->FileName.Length-2);
  660. ASSERT( Share->RelativePath.Length < Share->NtPathName.Length );
  661. Share->RelativePath.Buffer = Share->NtPathName.Buffer + ((Share->NtPathName.Length - Share->RelativePath.Length)/sizeof(WCHAR));
  662. //DbgPrint( "Share Relative Path %wZ\n", &Share->RelativePath );
  663. SrvInitializeObjectAttributes_U(
  664. &objectAttributes,
  665. &VolumeName,
  666. OBJ_CASE_INSENSITIVE,
  667. NULL,
  668. NULL
  669. );
  670. status = NtOpenFile(
  671. &Share->ShareVolumeHandle,
  672. 0,
  673. &objectAttributes,
  674. &ioStatus,
  675. FILE_SHARE_READ | FILE_SHARE_WRITE,
  676. 0
  677. );
  678. if( !NT_SUCCESS(status) )
  679. {
  680. Share->ShareVolumeHandle = NULL;
  681. }
  682. else
  683. {
  684. //DbgPrint( "Share Volume Handle %p\n", Share->ShareVolumeHandle );
  685. }
  686. }
  687. finally {
  688. ObDereferenceObject( ShareFileObject );
  689. }
  690. return status;
  691. }
  692. NTSTATUS
  693. SrvSnapRefreshSnapShotsForShare(
  694. IN PSHARE Share
  695. )
  696. /*++
  697. Routine Description:
  698. This function takes a share and refreshes the SnapShot views on the share
  699. so that only currently existing SnapShots are listed
  700. Arguments:
  701. Share - The share we are examining
  702. Return Value:
  703. NTSTATUS - STATUS_SUCCESS if all went well, otherwise the appropriate error
  704. code (and the request should be failed)
  705. --*/
  706. {
  707. NTSTATUS Status = STATUS_UNSUCCESSFUL;
  708. OBJECT_HANDLE_INFORMATION HandleInfo;
  709. PVOLSNAP_NAMES pNames = NULL;
  710. UNICODE_STRING VolumeName;
  711. UNICODE_STRING RootVolume;
  712. OBJECT_ATTRIBUTES objectAttributes;
  713. IO_STATUS_BLOCK iosb;
  714. PLIST_ENTRY shareList;
  715. ULONG NumberOfSnapshots = 0;
  716. LONG Epic;
  717. PAGED_CODE();
  718. // Validate we can do SnapShots
  719. if( Share->Removable )
  720. {
  721. Status = STATUS_NOT_SUPPORTED;
  722. goto Cleanup;
  723. }
  724. SrvSnapSetVolumeHandle( Share );
  725. if( Share->ShareVolumeHandle == NULL )
  726. {
  727. Status = STATUS_INTERNAL_ERROR;
  728. goto Cleanup;
  729. }
  730. // Check the EPIC number, so we can exit quickly if possible
  731. ACQUIRE_LOCK_SHARED( Share->SnapShotLock );
  732. Status = SrvSnapGetEpicForVolume( Share->ShareVolumeHandle, &Epic );
  733. if( NT_SUCCESS(Status) &&
  734. Epic == Share->SnapShotEpic )
  735. {
  736. RELEASE_LOCK( Share->SnapShotLock );
  737. //DbgPrint( "Fast refresh with Epic\n" );
  738. return STATUS_SUCCESS;
  739. }
  740. RELEASE_LOCK( Share->SnapShotLock );
  741. //DbgPrint( "Slow refresh (Epic mismatch)\n" );
  742. ACQUIRE_LOCK( Share->SnapShotLock );
  743. // Check the EPIC number again. There is a chance that another thread
  744. // grabbed the resource exclusively before us and has already done the update.
  745. // If so, we can exit. If not, we use this to set the new Epic number
  746. Status = SrvSnapGetEpicForVolume( Share->ShareVolumeHandle, &Epic );
  747. if( !NT_SUCCESS(Status) )
  748. {
  749. // Set Epic to -1, so we will always update
  750. Epic = -1;
  751. }
  752. else if( Epic == Share->SnapShotEpic )
  753. {
  754. RELEASE_LOCK( Share->SnapShotLock );
  755. //DbgPrint( "Fast refresh (exclusive) with Epic\n" );
  756. return STATUS_SUCCESS;
  757. }
  758. // Get the NamesArray for the volume
  759. Status = SrvSnapGetNamesForVolume( Share->ShareVolumeHandle, &pNames );
  760. if( !NT_SUCCESS(Status) )
  761. {
  762. RELEASE_LOCK( Share->SnapShotLock );
  763. goto Cleanup;
  764. }
  765. else if( !pNames )
  766. {
  767. // No SnapShots were found, so delete any that exist
  768. shareList = Share->SnapShots.Flink;
  769. while( shareList != &Share->SnapShots )
  770. {
  771. PSHARE_SNAPSHOT snapShare = CONTAINING_RECORD( shareList, SHARE_SNAPSHOT, SnapShotList );
  772. shareList = shareList->Flink;
  773. SrvSnapRemoveShare( snapShare );
  774. }
  775. // Try and update the Epic. Ignore failure, as it simply makes us query again
  776. Share->SnapShotEpic = Epic;
  777. RELEASE_LOCK( Share->SnapShotLock );
  778. return STATUS_SUCCESS;
  779. }
  780. // We only allow MAX_SNAPSHOTS_PER_SHARE snapshots on a volume, or enough
  781. // to fill a buffer of length MAX_USHORT. (MAX_SNAPSHOTS_PER_SHARE is always the
  782. // limiting factor, the MAX_USHORT is for safety of parsing to make sure we don't
  783. // crash if this ever changes)
  784. VolumeName.MaximumLength = (USHORT)(MIN(pNames->MultiSzLength,0xFFFF));
  785. VolumeName.Length = 0;
  786. VolumeName.Buffer = pNames->Names;
  787. // Mark all the snap-shares as "not-found"
  788. shareList = Share->SnapShots.Flink;
  789. while( shareList != &Share->SnapShots )
  790. {
  791. PSHARE_SNAPSHOT snapShare = CONTAINING_RECORD( shareList, SHARE_SNAPSHOT, SnapShotList );
  792. snapShare->Flags |= SRV_SNAP_SHARE_NOT_FOUND;
  793. shareList = shareList->Flink;
  794. }
  795. // Walk the name list and create a SnapShot for any volumes we don't currently have
  796. while( (NumberOfSnapshots < MAX_SNAPSHOTS_PER_SHARE) && SrvParseMultiSZ( &VolumeName ) )
  797. {
  798. Status = SrvSnapCheckForAndCreateSnapShare( Share, &VolumeName );
  799. if( !NT_SUCCESS(Status) )
  800. {
  801. IF_DEBUG( SNAPSHOT ) KdPrint(( "Failed to Add share %wZ (%x). Continuing..\n", &VolumeName, Status ));
  802. Status = STATUS_SUCCESS;
  803. }
  804. NumberOfSnapshots++;
  805. }
  806. // Any shares that are still marked as "not-found" are no longer availibe,
  807. // so we need to remove them
  808. shareList = Share->SnapShots.Flink;
  809. while( shareList != &Share->SnapShots )
  810. {
  811. PSHARE_SNAPSHOT snapShare = CONTAINING_RECORD( shareList, SHARE_SNAPSHOT, SnapShotList );
  812. shareList = shareList->Flink;
  813. if( snapShare->Flags & SRV_SNAP_SHARE_NOT_FOUND )
  814. {
  815. SrvSnapRemoveShare( snapShare );
  816. }
  817. }
  818. // Update the Epic.
  819. Share->SnapShotEpic = Epic;
  820. RELEASE_LOCK( Share->SnapShotLock );
  821. //DbgPrint( "Refresh complete\n" );
  822. Cleanup:
  823. // Release the memory associated with the enumeration
  824. if( pNames )
  825. {
  826. DEALLOCATE_NONPAGED_POOL( pNames );
  827. pNames = NULL;
  828. }
  829. return Status;
  830. }
  831. NTSTATUS
  832. SrvSnapEnumerateSnapShots(
  833. IN PWORK_CONTEXT WorkContext
  834. )
  835. /*++
  836. Routine Description:
  837. This function handles a transaction that wants to enumerate the availible
  838. SnapShots for a given share
  839. Arguments:
  840. WorkContext - The context for the transaction
  841. Return Value:
  842. NTSTATUS - STATUS_SUCCESS and STATUS_BUFFER_OVERFLOW are expected, and should
  843. be returned with data. Any other status code should be returned without data
  844. --*/
  845. {
  846. NTSTATUS Status;
  847. ULONG SnapShotCount;
  848. PLIST_ENTRY listEntry;
  849. PSHARE Share = WorkContext->TreeConnect->Share;
  850. PTRANSACTION transaction = WorkContext->Parameters.Transaction;
  851. PSRV_SNAPSHOT_ARRAY SnapShotArray = (PSRV_SNAPSHOT_ARRAY)transaction->OutData;
  852. PAGED_CODE();
  853. ASSERT(WorkContext->TreeConnect);
  854. // Check the buffer
  855. if( transaction->MaxDataCount < sizeof(SRV_SNAPSHOT_ARRAY) )
  856. {
  857. return STATUS_INVALID_PARAMETER;
  858. }
  859. // Refresh the SnapShot share list
  860. if( transaction->MaxDataCount == sizeof(SRV_SNAPSHOT_ARRAY) )
  861. {
  862. Status = SrvSnapRefreshSnapShotsForShare( Share );
  863. if( !NT_SUCCESS(Status) )
  864. {
  865. return Status;
  866. }
  867. }
  868. // Lock the share
  869. ACQUIRE_LOCK_SHARED( Share->SnapShotLock );
  870. // Check the buffer size
  871. SnapShotCount = 0;
  872. listEntry = Share->SnapShots.Blink;
  873. while( listEntry != &(Share->SnapShots) )
  874. {
  875. SnapShotCount++;
  876. listEntry = listEntry->Blink;
  877. }
  878. // Set the value and check if we will overflow
  879. SnapShotArray->NumberOfSnapShots = SnapShotCount;
  880. SnapShotArray->SnapShotArraySize = SNAPSHOT_NAME_LENGTH*SnapShotArray->NumberOfSnapShots+sizeof(WCHAR);
  881. if( (SnapShotCount == 0) || (transaction->MaxDataCount < SnapShotArray->SnapShotArraySize) )
  882. {
  883. // The buffer is not big enough. Return the required size
  884. SnapShotArray->NumberOfSnapShotsReturned = 0;
  885. transaction->DataCount = sizeof(SRV_SNAPSHOT_ARRAY);
  886. Status = STATUS_SUCCESS;
  887. }
  888. else
  889. {
  890. // The buffer is big enough. Fill it in and return it
  891. PBYTE nameLocation = (PBYTE)SnapShotArray->SnapShotMultiSZ;
  892. SnapShotCount = 0;
  893. listEntry = Share->SnapShots.Blink;
  894. RtlZeroMemory( SnapShotArray->SnapShotMultiSZ, SnapShotArray->SnapShotArraySize );
  895. while( listEntry != &(Share->SnapShots) )
  896. {
  897. PSHARE_SNAPSHOT SnapShot = CONTAINING_RECORD( listEntry, SHARE_SNAPSHOT, SnapShotList );
  898. RtlCopyMemory( nameLocation, SnapShot->SnapShotName.Buffer, SNAPSHOT_NAME_LENGTH );
  899. nameLocation += SNAPSHOT_NAME_LENGTH;
  900. SnapShotCount++;
  901. listEntry = listEntry->Blink;
  902. }
  903. SnapShotArray->NumberOfSnapShotsReturned = SnapShotArray->NumberOfSnapShots;
  904. transaction->DataCount = sizeof(SRV_SNAPSHOT_ARRAY)+SnapShotArray->SnapShotArraySize;
  905. Status = STATUS_SUCCESS;
  906. }
  907. // Release the lock
  908. RELEASE_LOCK( Share->SnapShotLock );
  909. return Status;
  910. }
  911. NTSTATUS
  912. SrvSnapGetRootHandle(
  913. IN PWORK_CONTEXT WorkContext,
  914. OUT HANDLE* RootHandle
  915. )
  916. /*++
  917. Routine Description:
  918. This function retrieves the correct Root Handle for an operation given the
  919. parsed SnapShot timestamp on the WORK_CONTEXT
  920. Arguments:
  921. WorkContext - The context for the transaction
  922. RootHandle - Where to store the resulting handle
  923. Return Value:
  924. NTSTATUS - STATUS_SUCCESS or STATUS_NOT_FOUND if the SnapShot is not found
  925. --*/
  926. {
  927. NTSTATUS Status = STATUS_OBJECT_NAME_NOT_FOUND;
  928. PSHARE Share;
  929. PLIST_ENTRY listEntry;
  930. PSHARE_SNAPSHOT SnapShare;
  931. PAGED_CODE();
  932. ASSERT( WorkContext );
  933. ASSERT( WorkContext->TreeConnect );
  934. ASSERT( WorkContext->TreeConnect->Share );
  935. Share = WorkContext->TreeConnect->Share;
  936. if( WorkContext->SnapShotTime.QuadPart != 0 )
  937. {
  938. //IF_DEBUG( SNAPSHOT ) KdPrint(( "Looking for %x%x\n", WorkContext->SnapShotTime.HighPart, WorkContext->SnapShotTime.LowPart ));
  939. // Acquire the shared lock
  940. ACQUIRE_LOCK_SHARED( Share->SnapShotLock );
  941. // Walk the list and look for the entry
  942. listEntry = Share->SnapShots.Flink;
  943. while( listEntry != &Share->SnapShots )
  944. {
  945. SnapShare = CONTAINING_RECORD( listEntry, SHARE_SNAPSHOT, SnapShotList );
  946. if( SnapShare->Timestamp.QuadPart == WorkContext->SnapShotTime.QuadPart )
  947. {
  948. //IF_DEBUG( SNAPSHOT ) KdPrint((" Found %wZ\n", &SnapShare->SnapShotName ));
  949. *RootHandle = SnapShare->SnapShotRootDirectoryHandle;
  950. Status = STATUS_SUCCESS;
  951. break;
  952. }
  953. listEntry = listEntry->Flink;
  954. }
  955. RELEASE_LOCK( Share->SnapShotLock );
  956. }
  957. else
  958. {
  959. *RootHandle = Share->RootDirectoryHandle;
  960. Status = STATUS_SUCCESS;
  961. }
  962. return Status;
  963. }
  964. NTSTATUS
  965. SrvSnapGetNameString(
  966. IN PWORK_CONTEXT WorkContext,
  967. OUT PUNICODE_STRING* ShareName,
  968. OUT PBOOLEAN AllocatedShareName
  969. )
  970. /*++
  971. Routine Description:
  972. This function retrieves the correct Root Handle for an operation given the
  973. parsed SnapShot timestamp on the WORK_CONTEXT
  974. Arguments:
  975. WorkContext - The context for the transaction
  976. ShareName - The Name of the share
  977. AllocatedShareName - Whether the ShareName should be freed after use (via FREE_HEAP)
  978. Return Value:
  979. PUNICODE_STRING - pointer to existing string or NULL
  980. BUGBUG = Have to take a reference or copy at this point!
  981. --*/
  982. {
  983. NTSTATUS Status = STATUS_OBJECT_NAME_NOT_FOUND;
  984. PSHARE Share;
  985. PLIST_ENTRY listEntry;
  986. PSHARE_SNAPSHOT SnapShare;
  987. PUNICODE_STRING SnapShareName;
  988. PAGED_CODE();
  989. ASSERT( WorkContext );
  990. ASSERT( WorkContext->TreeConnect );
  991. ASSERT( WorkContext->TreeConnect->Share );
  992. Share = WorkContext->TreeConnect->Share;
  993. if( WorkContext->SnapShotTime.QuadPart != 0 )
  994. {
  995. //IF_DEBUG( SNAPSHOT ) KdPrint(( "Looking for %x%x\n", WorkContext->SnapShotTime.HighPart, WorkContext->SnapShotTime.LowPart ));
  996. // Acquire the shared lock
  997. ACQUIRE_LOCK_SHARED( Share->SnapShotLock );
  998. // Walk the list and look for the entry
  999. listEntry = Share->SnapShots.Flink;
  1000. while( listEntry != &Share->SnapShots )
  1001. {
  1002. SnapShare = CONTAINING_RECORD( listEntry, SHARE_SNAPSHOT, SnapShotList );
  1003. if( SnapShare->Timestamp.QuadPart == WorkContext->SnapShotTime.QuadPart )
  1004. {
  1005. SnapShareName = ALLOCATE_HEAP( sizeof(UNICODE_STRING)+SnapShare->SnapShotPath.Length, BlockTypeSnapShot );
  1006. if( !SnapShareName )
  1007. {
  1008. Status = STATUS_INSUFFICIENT_RESOURCES;
  1009. *AllocatedShareName = FALSE;
  1010. }
  1011. else
  1012. {
  1013. SnapShareName->Length = 0;
  1014. SnapShareName->MaximumLength = SnapShare->SnapShotPath.Length;
  1015. SnapShareName->Buffer = (PWCHAR)(SnapShareName+1);
  1016. RtlCopyUnicodeString( SnapShareName, &SnapShare->SnapShotPath );
  1017. *AllocatedShareName = TRUE;
  1018. *ShareName = SnapShareName;
  1019. Status = STATUS_SUCCESS;
  1020. }
  1021. RELEASE_LOCK( Share->SnapShotLock );
  1022. return Status;
  1023. }
  1024. listEntry = listEntry->Flink;
  1025. }
  1026. RELEASE_LOCK( Share->SnapShotLock );
  1027. return Status;
  1028. }
  1029. else
  1030. {
  1031. *ShareName = &WorkContext->TreeConnect->Share->NtPathName;
  1032. *AllocatedShareName = FALSE;
  1033. return STATUS_SUCCESS;
  1034. }
  1035. }
  1036. BOOLEAN
  1037. ExtractNumber(
  1038. IN PWSTR psz,
  1039. IN ULONG Count,
  1040. OUT PLONG value
  1041. )
  1042. /*++
  1043. Routine Description:
  1044. This function takes a string of characters and parses out a <Count> length decimal
  1045. number. If it returns TRUE, value has been set and the string was parsed correctly.
  1046. FALSE indicates an error in parsing.
  1047. Arguments:
  1048. psz - String pointer
  1049. Count - Number of characters to pull off
  1050. value - pointer to output parameter where value is stored
  1051. Return Value:
  1052. BOOLEAN - See description
  1053. --*/
  1054. {
  1055. PAGED_CODE();
  1056. *value = 0;
  1057. while( Count )
  1058. {
  1059. if( (*psz == L'\0') ||
  1060. IS_UNICODE_PATH_SEPARATOR( *psz ) )
  1061. {
  1062. //IF_DEBUG( SNAPSHOT ) KdPrint(( "Path Seperator found %d\n", Count ));
  1063. return FALSE;
  1064. }
  1065. if( (*psz < L'0') || (*psz > L'9') )
  1066. {
  1067. //IF_DEBUG( SNAPSHOT ) KdPrint(( "Non-digit found %x\n", *psz ));
  1068. return FALSE;
  1069. }
  1070. *value = (*value)*10+(*psz-L'0');
  1071. Count--;
  1072. psz++;
  1073. }
  1074. return TRUE;
  1075. }
  1076. BOOLEAN
  1077. SrvSnapParseToken(
  1078. IN PWSTR Source,
  1079. IN ULONG SourceSizeInBytes,
  1080. OUT PLARGE_INTEGER TimeStamp
  1081. )
  1082. /*++
  1083. Routine Description:
  1084. This function parses a null-terminated UNICODE file path name string to see if the
  1085. current token is a Snap-designator
  1086. Arguments:
  1087. Source - Pointer to the string
  1088. SourceSizeInBytes - size of the source string in bytes
  1089. TimeStamp - If this is a SnapShot, this is set to the time value designated by the string
  1090. Return Value:
  1091. BOOLEAN - indicates whether this is a SnapShot token
  1092. --*/
  1093. {
  1094. PWSTR psz = Source;
  1095. UNICODE_STRING NameString;
  1096. ULONG Count = 0;
  1097. #define SNAPSHOT_HEADER L"@GMT-"
  1098. PWSTR header = SNAPSHOT_HEADER;
  1099. TIME_FIELDS rtlTime;
  1100. LONG value;
  1101. PAGED_CODE();
  1102. if( SourceSizeInBytes < (SNAPSHOT_NAME_LENGTH-sizeof(WCHAR)) )
  1103. {
  1104. return FALSE;
  1105. }
  1106. // Check the SNAP. header
  1107. for( Count=0; Count<wcslen(SNAPSHOT_HEADER); Count++,psz++ )
  1108. {
  1109. if( (toupper(*psz) != header[Count]) ||
  1110. (*psz == L'\0') ||
  1111. IS_UNICODE_PATH_SEPARATOR( *psz ) )
  1112. {
  1113. //IF_DEBUG( SNAPSHOT ) KdPrint(("Count %d (%x != %x)\n", Count, *psz, header[Count] ));
  1114. goto NoMatch;
  1115. }
  1116. }
  1117. // Prepare to parse
  1118. RtlZeroMemory( &rtlTime, sizeof(TIME_FIELDS) );
  1119. // Extract the Year
  1120. if( !ExtractNumber( psz, 4, &value ) )
  1121. goto NoMatch;
  1122. if( psz[4] != L'.' )
  1123. goto NoMatch;
  1124. rtlTime.Year = (CSHORT)value;
  1125. psz += 5;
  1126. // Extract the Month
  1127. if( !ExtractNumber( psz, 2, &value ) )
  1128. goto NoMatch;
  1129. if( psz[2] != L'.' )
  1130. goto NoMatch;
  1131. rtlTime.Month = (CSHORT)value;
  1132. psz += 3;
  1133. // Extract the Day
  1134. if( !ExtractNumber( psz, 2, &value ) )
  1135. goto NoMatch;
  1136. if( psz[2] != L'-' )
  1137. goto NoMatch;
  1138. rtlTime.Day = (CSHORT)value;
  1139. psz += 3;
  1140. // Extract the Hour
  1141. if( !ExtractNumber( psz, 2, &value ) )
  1142. goto NoMatch;
  1143. if( psz[2] != L'.' )
  1144. goto NoMatch;
  1145. rtlTime.Hour = (CSHORT)value;
  1146. psz += 3;
  1147. // Extract the Minutes
  1148. if( !ExtractNumber( psz, 2, &value ) )
  1149. goto NoMatch;
  1150. if( psz[2] != L'.' )
  1151. goto NoMatch;
  1152. rtlTime.Minute = (CSHORT)value;
  1153. psz += 3;
  1154. // Extract the Seconds
  1155. if( !ExtractNumber( psz, 2, &value ) )
  1156. goto NoMatch;
  1157. if( !IS_UNICODE_PATH_SEPARATOR( psz[2] ) &&
  1158. (psz[2] != L'\0') )
  1159. goto NoMatch;
  1160. rtlTime.Second = (CSHORT)value;
  1161. psz += 3;
  1162. RtlTimeFieldsToTime( &rtlTime, TimeStamp );
  1163. return TRUE;
  1164. NoMatch:
  1165. return FALSE;
  1166. }
  1167. NTSTATUS
  1168. SrvSnapCheckAppInfoForTimeWarp(
  1169. IN PUNICODE_STRING SnapShotPath
  1170. )
  1171. /*++
  1172. Routine Description:
  1173. This function determines whether the specified snapshot should be allowed as a snapshot share
  1174. Arguments:
  1175. SnapShotHandle - Handle to the snapshot volume
  1176. Return Value:
  1177. NTSTATUS (either STATUS_SUCCESS, or STATUS_INVALID_DEVICE_REQUEST)
  1178. --*/
  1179. {
  1180. HANDLE hVolume;
  1181. DWORD dwSize = sizeof(VOLSNAP_APPLICATION_INFO);
  1182. NTSTATUS Status;
  1183. OBJECT_ATTRIBUTES objectAttributes;
  1184. VOLSNAP_APPLICATION_INFO AppInfo;
  1185. PVOLSNAP_APPLICATION_INFO pAppInfo;
  1186. IO_STATUS_BLOCK IoStatus;
  1187. PAGED_CODE();
  1188. // Now open the SnapShot handle
  1189. dwSize = sizeof(VOLSNAP_APPLICATION_INFO);
  1190. InitializeObjectAttributes(
  1191. &objectAttributes,
  1192. SnapShotPath,
  1193. OBJ_CASE_INSENSITIVE,
  1194. NULL,
  1195. NULL
  1196. );
  1197. Status = NtOpenFile(
  1198. &hVolume,
  1199. 0,
  1200. &objectAttributes,
  1201. &IoStatus,
  1202. FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
  1203. 0
  1204. );
  1205. if( !NT_SUCCESS(Status) )
  1206. {
  1207. return Status;
  1208. }
  1209. try {
  1210. // Find out how big it should be
  1211. Status = SrvSnapIssueIoctl( hVolume, IOCTL_VOLSNAP_QUERY_APPLICATION_INFO, &AppInfo, &dwSize );
  1212. if( Status != STATUS_BUFFER_OVERFLOW )
  1213. {
  1214. Status = STATUS_INVALID_DEVICE_REQUEST;
  1215. leave;
  1216. }
  1217. // Allocate the correct size block
  1218. dwSize = sizeof(VOLSNAP_APPLICATION_INFO)+AppInfo.InformationLength;
  1219. pAppInfo = (PVOLSNAP_APPLICATION_INFO)ALLOCATE_NONPAGED_POOL( dwSize, BlockTypeSnapShot );
  1220. if( !pAppInfo )
  1221. {
  1222. Status = STATUS_INSUFFICIENT_RESOURCES;
  1223. leave;
  1224. }
  1225. // Build up the IRP to be used for the query
  1226. Status = SrvSnapIssueIoctl( hVolume, IOCTL_VOLSNAP_QUERY_APPLICATION_INFO, pAppInfo, &dwSize );
  1227. // Check whether the AppInfo contains the GUID we are interested in
  1228. if( !NT_SUCCESS(Status) )
  1229. {
  1230. Status = STATUS_INVALID_DEVICE_REQUEST;
  1231. }
  1232. else
  1233. {
  1234. if( pAppInfo->InformationLength > sizeof(GUID) )
  1235. {
  1236. if( RtlEqualMemory( pAppInfo->Information, &VOLSNAP_APPINFO_GUID_CLIENT_ACCESSIBLE, sizeof(GUID) ) )
  1237. {
  1238. Status = STATUS_SUCCESS;
  1239. }
  1240. else
  1241. {
  1242. Status = STATUS_INVALID_DEVICE_REQUEST;
  1243. }
  1244. }
  1245. }
  1246. // Return the result
  1247. DEALLOCATE_NONPAGED_POOL(pAppInfo);
  1248. pAppInfo = NULL;
  1249. }
  1250. finally {
  1251. // Close the handle to the volume
  1252. NtClose( hVolume );
  1253. }
  1254. return Status;
  1255. }
  1256. NTSTATUS
  1257. SrvSnapEnumerateSnapShotsAsDirInfo(
  1258. IN PWORK_CONTEXT WorkContext,
  1259. IN PVOID Buffer,
  1260. IN ULONG BufferLength,
  1261. IN PUNICODE_STRING FileResumeName,
  1262. IN BOOLEAN SingleEntries,
  1263. IN OUT PSRV_DIRECTORY_INFORMATION DirectoryInformation
  1264. )
  1265. {
  1266. NTSTATUS Status;
  1267. ULONG SnapShotCount;
  1268. ULONG Current = 0;
  1269. ULONG Count = 0;
  1270. PLIST_ENTRY listEntry;
  1271. PSHARE Share = WorkContext->TreeConnect->Share;
  1272. ULONG Remaining = BufferLength - FIELD_OFFSET( SRV_DIRECTORY_INFORMATION, Buffer );
  1273. PBYTE pCurrent = Buffer;
  1274. PFILE_BOTH_DIR_INFORMATION fileBoth = (PFILE_BOTH_DIR_INFORMATION)pCurrent;
  1275. PFILE_BOTH_DIR_INFORMATION currentEntry = (PFILE_BOTH_DIR_INFORMATION)DirectoryInformation->DirectoryHandle;
  1276. UNICODE_STRING lastName;
  1277. PAGED_CODE();
  1278. if( currentEntry && !FileResumeName )
  1279. {
  1280. lastName.MaximumLength = lastName.Length = (USHORT)currentEntry->FileNameLength;
  1281. lastName.Buffer = currentEntry->FileName;
  1282. FileResumeName = &lastName;
  1283. }
  1284. // Lock the share
  1285. ACQUIRE_LOCK_SHARED( Share->SnapShotLock );
  1286. // Check the buffer size
  1287. SnapShotCount = 0;
  1288. listEntry = Share->SnapShots.Blink;
  1289. while( listEntry != &(Share->SnapShots) )
  1290. {
  1291. SnapShotCount++;
  1292. listEntry = listEntry->Blink;
  1293. }
  1294. // Start filling the buffer. First, let's find the starting entry
  1295. listEntry = Share->SnapShots.Blink;
  1296. if( FileResumeName != NULL )
  1297. {
  1298. while( listEntry != &(Share->SnapShots) )
  1299. {
  1300. PSHARE_SNAPSHOT SnapShot = CONTAINING_RECORD( listEntry, SHARE_SNAPSHOT, SnapShotList );
  1301. listEntry = listEntry->Blink;
  1302. if( RtlEqualUnicodeString( &SnapShot->SnapShotName, FileResumeName, TRUE ) )
  1303. {
  1304. break;
  1305. }
  1306. Current++;
  1307. }
  1308. if( listEntry == &Share->SnapShots )
  1309. {
  1310. Status = STATUS_NO_MORE_FILES;
  1311. goto return_with_lock;
  1312. }
  1313. }
  1314. fileBoth->NextEntryOffset = 0;
  1315. // Now lets start filling the buffer
  1316. while( listEntry != &(Share->SnapShots) )
  1317. {
  1318. PSHARE_SNAPSHOT SnapShot = CONTAINING_RECORD( listEntry, SHARE_SNAPSHOT, SnapShotList );
  1319. ULONG requiredSize = ALIGN_UP(sizeof(FILE_BOTH_DIR_INFORMATION)+SnapShot->SnapShotName.Length, LARGE_INTEGER);
  1320. WCHAR ShortName[12];
  1321. HRESULT result;
  1322. // Check if we can fit this entry
  1323. if( requiredSize > Remaining )
  1324. {
  1325. break;
  1326. }
  1327. else
  1328. {
  1329. listEntry = listEntry->Blink;
  1330. pCurrent += fileBoth->NextEntryOffset;
  1331. fileBoth = (PFILE_BOTH_DIR_INFORMATION)pCurrent;
  1332. }
  1333. RtlZeroMemory( fileBoth, sizeof(FILE_BOTH_DIR_INFORMATION) );
  1334. fileBoth->AllocationSize.QuadPart = 0;
  1335. fileBoth->ChangeTime = fileBoth->CreationTime = fileBoth->LastAccessTime = fileBoth->LastWriteTime = SnapShot->Timestamp;
  1336. fileBoth->EaSize = 0;
  1337. fileBoth->EndOfFile.QuadPart = 0;
  1338. fileBoth->FileAttributes = FILE_ATTRIBUTE_DIRECTORY;// | FILE_ATTRIBUTE_HIDDEN;
  1339. fileBoth->FileIndex = 0;
  1340. fileBoth->FileNameLength = SnapShot->SnapShotName.Length;
  1341. RtlZeroMemory( ShortName, 12*sizeof(WCHAR) );
  1342. result = StringCbPrintf( ShortName, 12*sizeof(WCHAR), L"@GMT~%03d", Current );
  1343. ASSERT( SUCCEEDED(result) ); // Since # of snapshots is limited, current will never exceed 3 digits
  1344. RtlCopyMemory( fileBoth->ShortName, ShortName, 12*sizeof(WCHAR) );
  1345. fileBoth->ShortNameLength = wcslen(ShortName)*sizeof(WCHAR);
  1346. RtlCopyMemory( fileBoth->FileName, SnapShot->SnapShotName.Buffer, fileBoth->FileNameLength );
  1347. fileBoth->NextEntryOffset = ALIGN_UP( requiredSize, LARGE_INTEGER );
  1348. DirectoryInformation->DirectoryHandle = (HANDLE)fileBoth;
  1349. Remaining -= fileBoth->NextEntryOffset;
  1350. Current++;
  1351. Count++;
  1352. }
  1353. if( fileBoth->NextEntryOffset != 0 )
  1354. {
  1355. fileBoth->NextEntryOffset = 0;
  1356. }
  1357. if( listEntry == &(Share->SnapShots) &&
  1358. (Count == 0) )
  1359. {
  1360. Status = STATUS_NO_MORE_FILES;
  1361. }
  1362. else if( Count == 0 )
  1363. {
  1364. Status = STATUS_BUFFER_TOO_SMALL;
  1365. }
  1366. else
  1367. {
  1368. Status = STATUS_SUCCESS;
  1369. }
  1370. // Release the lock
  1371. return_with_lock:
  1372. RELEASE_LOCK( Share->SnapShotLock );
  1373. return Status;
  1374. }