Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1265 lines
32 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 "srvsupp.h"
  14. #include "ntddsnap.h"
  15. #include "stdarg.h"
  16. #include "stdio.h"
  17. #include "srvsnap.tmh"
  18. #pragma hdrstop
  19. // Function Declaractions
  20. NTSTATUS
  21. StartIoAndWait (
  22. IN PIRP Irp,
  23. IN PDEVICE_OBJECT DeviceObject,
  24. IN PKEVENT Event,
  25. IN PIO_STATUS_BLOCK IoStatusBlock
  26. );
  27. PIRP
  28. BuildCoreOfSyncIoRequest (
  29. IN HANDLE FileHandle,
  30. IN PFILE_OBJECT FileObject OPTIONAL,
  31. IN PKEVENT Event,
  32. IN PIO_STATUS_BLOCK IoStatusBlock,
  33. IN OUT PDEVICE_OBJECT *DeviceObject
  34. );
  35. NTSTATUS
  36. SrvSnapGetNames(
  37. IN HANDLE hFile,
  38. IN OUT PVOID pBuffer,
  39. IN OUT LPDWORD lpdwBufSize
  40. );
  41. NTSTATUS
  42. SrvSnapGetNamesForVolume(
  43. IN HANDLE hFile,
  44. OUT PVOLSNAP_NAMES* Names
  45. );
  46. NTSTATUS
  47. SrvSnapFillConfigInfo(
  48. IN PSHARE_SNAPSHOT SnapShare,
  49. IN PUNICODE_STRING SnapShotPath
  50. );
  51. BOOLEAN
  52. SrvParseMultiSZ(
  53. IN OUT PUNICODE_STRING mszString
  54. );
  55. NTSTATUS
  56. SrvSnapInsertSnapIntoShare(
  57. IN PSHARE Share,
  58. IN PSHARE_SNAPSHOT SnapShot
  59. );
  60. NTSTATUS
  61. SrvSnapAddShare(
  62. IN PSHARE Share,
  63. IN PUNICODE_STRING SnapPath
  64. );
  65. NTSTATUS
  66. SrvSnapRemoveShare(
  67. IN PSHARE_SNAPSHOT SnapShare
  68. );
  69. NTSTATUS
  70. SrvSnapCheckForAndCreateSnapShare(
  71. IN PSHARE Share,
  72. IN PUNICODE_STRING SnapShotName
  73. );
  74. NTSTATUS
  75. SrvSnapRefreshSnapShotsForShare(
  76. IN PSHARE Share
  77. );
  78. NTSTATUS
  79. SrvSnapEnumerateSnapShots(
  80. IN PWORK_CONTEXT WorkContext
  81. );
  82. NTSTATUS
  83. SrvSnapGetRootHandle(
  84. IN PWORK_CONTEXT WorkContext,
  85. OUT HANDLE* RootHandle
  86. );
  87. NTSTATUS
  88. SrvSnapGetNameString(
  89. IN PWORK_CONTEXT WorkContext,
  90. OUT PUNICODE_STRING* ShareName,
  91. OUT PBOOLEAN AllocatedShareName
  92. );
  93. BOOLEAN
  94. ExtractNumber(
  95. IN PWSTR psz,
  96. IN ULONG Count,
  97. OUT PLONG value
  98. );
  99. BOOLEAN
  100. SrvSnapParseToken(
  101. IN PWSTR Source,
  102. OUT PLARGE_INTEGER TimeStamp
  103. );
  104. #ifdef ALLOC_PRAGMA
  105. #pragma alloc_text( PAGE, SrvSnapGetNames )
  106. #pragma alloc_text( PAGE, SrvSnapGetNamesForVolume )
  107. #pragma alloc_text( PAGE, SrvSnapFillConfigInfo )
  108. #pragma alloc_text( PAGE, SrvParseMultiSZ )
  109. #pragma alloc_text( PAGE, SrvSnapInsertSnapIntoShare )
  110. #pragma alloc_text( PAGE, SrvSnapAddShare )
  111. #pragma alloc_text( PAGE, SrvSnapRemoveShare )
  112. #pragma alloc_text( PAGE, SrvSnapCheckForAndCreateSnapShare )
  113. #pragma alloc_text( PAGE, SrvSnapRefreshSnapShotsForShare )
  114. #pragma alloc_text( PAGE, SrvSnapEnumerateSnapShots )
  115. #pragma alloc_text( PAGE, SrvSnapGetRootHandle )
  116. #pragma alloc_text( PAGE, SrvSnapGetNameString )
  117. #pragma alloc_text( PAGE, ExtractNumber )
  118. #pragma alloc_text( PAGE, SrvSnapParseToken )
  119. #endif
  120. //
  121. // Helper Functions
  122. //
  123. NTSTATUS
  124. SrvSnapGetNames(
  125. IN HANDLE hFile,
  126. IN OUT PVOID pBuffer,
  127. IN OUT LPDWORD lpdwBufSize
  128. )
  129. /*++
  130. Routine Description:
  131. This function takes a volume handle and attempts to enumerate all the
  132. SnapShots on that volume into the given buffer
  133. Arguments:
  134. hFile - The handle to the volume
  135. pBuffer - A pointer to the output buffer
  136. lpdwBufSize - Pointer to the size of passed in buffer. Set to the
  137. required size if STATUS_BUFFER_OVERFLOW is returned
  138. Return Value:
  139. NTSTATUS - Expected return codes are STATUS_SUCCESS or STATUS_BUFFER_OVERFLOW.
  140. Any other response is an unexpected error code and the request should be
  141. failed
  142. --*/
  143. {
  144. PIRP Irp = NULL;
  145. NTSTATUS Status;
  146. IO_STATUS_BLOCK IoStatus;
  147. KEVENT CompletionEvent;
  148. PDEVICE_OBJECT DeviceObject = NULL;
  149. PIO_STACK_LOCATION IrpSp;
  150. ASSERT( (*lpdwBufSize) > sizeof(VOLSNAP_NAMES) );
  151. // Initialize the variables
  152. KeInitializeEvent( &CompletionEvent, SynchronizationEvent, FALSE );
  153. // Create the IRP
  154. Irp = BuildCoreOfSyncIoRequest(
  155. hFile,
  156. NULL,
  157. &CompletionEvent,
  158. &IoStatus,
  159. &DeviceObject );
  160. if( !Irp )
  161. {
  162. return STATUS_INSUFFICIENT_RESOURCES;
  163. }
  164. // Initialize the other IRP fields
  165. Irp->Flags = (LONG)IRP_BUFFERED_IO;
  166. Irp->AssociatedIrp.SystemBuffer = pBuffer;
  167. IrpSp = IoGetNextIrpStackLocation( Irp );
  168. IrpSp->MajorFunction = IRP_MJ_DEVICE_CONTROL;
  169. IrpSp->MinorFunction = 0;
  170. IrpSp->Parameters.DeviceIoControl.OutputBufferLength = *lpdwBufSize;
  171. IrpSp->Parameters.DeviceIoControl.InputBufferLength = 0;
  172. IrpSp->Parameters.DeviceIoControl.IoControlCode = IOCTL_VOLSNAP_QUERY_NAMES_OF_SNAPSHOTS;
  173. IrpSp->Parameters.DeviceIoControl.Type3InputBuffer = NULL;
  174. // Issue the IO
  175. Status = StartIoAndWait( Irp, DeviceObject, &CompletionEvent, &IoStatus );
  176. // If this was a buffer overflow, update the value
  177. if( (Status == STATUS_BUFFER_OVERFLOW) &&
  178. (*lpdwBufSize >= sizeof(VOLSNAP_NAMES)) )
  179. {
  180. *lpdwBufSize = ((PVOLSNAP_NAMES)pBuffer)->MultiSzLength + sizeof(VOLSNAP_NAMES);
  181. }
  182. return Status;
  183. }
  184. NTSTATUS
  185. SrvSnapGetNamesForVolume(
  186. IN HANDLE hFile,
  187. OUT PVOLSNAP_NAMES* Names
  188. )
  189. /*++
  190. Routine Description:
  191. This function takes a volume handle and returns the list of SnapShots for that
  192. volume. If an error occurs, or no SnapShots exist, the returned pointer is
  193. NULL. NOTE: The caller is responsible for freeing the returned pointer via
  194. DEALLOCATE_NONPAGED_POOL
  195. Arguments:
  196. hFile - The handle to the volume
  197. Names - A pointer to the pointer where we will store the volume name list
  198. Return Value:
  199. NTSTATUS - Expected return code is STATUS_SUCCESS . Any other response is an
  200. unexpected error code and the request should be failed
  201. --*/
  202. {
  203. NTSTATUS Status;
  204. VOLSNAP_NAMES VolNamesBase;
  205. PVOLSNAP_NAMES pNames = NULL;
  206. DWORD dwSize = sizeof(VOLSNAP_NAMES);
  207. // Initialize the values
  208. *Names = NULL;
  209. // Find out how big it should be
  210. Status = SrvSnapGetNames( hFile, &VolNamesBase, &dwSize );
  211. if( Status != STATUS_BUFFER_OVERFLOW )
  212. {
  213. return Status;
  214. }
  215. // Allocate the correct size block
  216. pNames = (PVOLSNAP_NAMES)ALLOCATE_NONPAGED_POOL( dwSize, BlockTypeSnapShot );
  217. if( !pNames )
  218. {
  219. return STATUS_INSUFFICIENT_RESOURCES;
  220. }
  221. // Build up the IRP to be used for the query
  222. Status = SrvSnapGetNames( hFile, pNames, &dwSize );
  223. // Save the output if we're successful, or else deallocate
  224. if( !NT_SUCCESS(Status) )
  225. {
  226. if( pNames )
  227. {
  228. DEALLOCATE_NONPAGED_POOL( pNames );
  229. pNames = NULL;
  230. *Names = NULL;
  231. }
  232. }
  233. else
  234. {
  235. ASSERT(pNames);
  236. *Names = pNames;
  237. }
  238. return Status;
  239. }
  240. NTSTATUS
  241. SrvSnapFillConfigInfo(
  242. IN PSHARE_SNAPSHOT SnapShare,
  243. IN PUNICODE_STRING SnapShotPath
  244. )
  245. /*++
  246. Routine Description:
  247. This function takes a SnapShare with existing names filled in and
  248. queries the extra config info (the timestamp) for that SnapShot
  249. Arguments:
  250. SnapShare - Pointer to the SnapShot share to be filled in
  251. Return Value:
  252. NTSTATUS - Expected return code is STATUS_SUCCESS . Any other response is an
  253. unexpected error code and the request should be failed
  254. --*/
  255. {
  256. HANDLE hVolume;
  257. DWORD dwBytes;
  258. NTSTATUS Status;
  259. PIRP Irp = NULL;
  260. OBJECT_ATTRIBUTES objectAttributes;
  261. VOLSNAP_CONFIG_INFO ConfigInfo;
  262. IO_STATUS_BLOCK IoStatus;
  263. KEVENT CompletionEvent;
  264. PDEVICE_OBJECT DeviceObject;
  265. PIO_STACK_LOCATION IrpSp;
  266. // Now open the SnapShot handle
  267. KeInitializeEvent( &CompletionEvent, SynchronizationEvent, FALSE );
  268. InitializeObjectAttributes(
  269. &objectAttributes,
  270. SnapShotPath,
  271. OBJ_CASE_INSENSITIVE,
  272. NULL,
  273. NULL
  274. );
  275. Status = NtOpenFile(
  276. &hVolume,
  277. FILE_TRAVERSE|FILE_GENERIC_READ,
  278. &objectAttributes,
  279. &IoStatus,
  280. FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
  281. 0
  282. );
  283. if( !NT_SUCCESS(Status) )
  284. {
  285. return Status;
  286. }
  287. // Create the IRP
  288. Irp = BuildCoreOfSyncIoRequest(
  289. hVolume,
  290. NULL,
  291. &CompletionEvent,
  292. &IoStatus,
  293. &DeviceObject );
  294. if( !Irp )
  295. {
  296. return STATUS_INSUFFICIENT_RESOURCES;
  297. }
  298. // Initialize the other IRP fields
  299. Irp->Flags = (LONG)IRP_BUFFERED_IO;
  300. Irp->AssociatedIrp.SystemBuffer = &ConfigInfo;
  301. IrpSp = IoGetNextIrpStackLocation( Irp );
  302. IrpSp->MajorFunction = IRP_MJ_DEVICE_CONTROL;
  303. IrpSp->MinorFunction = 0;
  304. IrpSp->Parameters.DeviceIoControl.OutputBufferLength = sizeof(VOLSNAP_CONFIG_INFO);
  305. IrpSp->Parameters.DeviceIoControl.InputBufferLength = 0;
  306. IrpSp->Parameters.DeviceIoControl.IoControlCode = IOCTL_VOLSNAP_QUERY_CONFIG_INFO;
  307. IrpSp->Parameters.DeviceIoControl.Type3InputBuffer = NULL;
  308. // Issue the IO
  309. Status = StartIoAndWait( Irp, DeviceObject, &CompletionEvent, &IoStatus );
  310. if( !NT_SUCCESS( Status ) )
  311. {
  312. NtClose( hVolume );
  313. return Status;
  314. }
  315. // Fill in the info and return success
  316. SnapShare->Timestamp = ConfigInfo.SnapshotCreationTime;
  317. NtClose( hVolume );
  318. return STATUS_SUCCESS;
  319. }
  320. BOOLEAN
  321. SrvParseMultiSZ(
  322. IN OUT PUNICODE_STRING mszString
  323. )
  324. /*++
  325. Routine Description:
  326. This function takes a UNICODE_STRING that points to a MultiSZ value, and
  327. parses it with each iteration (similar to strtok( psz, "\0" )). For every
  328. iteration this returns TRUE, the string will point to the next SZ in the
  329. MultiSZ
  330. Arguments:
  331. mszString - Pointer to the MultiSZ string. For the first iteration, set the
  332. MaximumLength to the length of the MultiSZ, and the Length to 0. For all
  333. other iterations, simply pass in the string from the previous iteration
  334. Return Value:
  335. BOOLEAN - If TRUE, this is a valid SZ. If FALSE, all have been parsed
  336. --*/
  337. {
  338. USHORT Count;
  339. ASSERT( mszString->Length <= mszString->MaximumLength );
  340. if( mszString->Length > 0 )
  341. {
  342. // Move the pointer past the string and the NULL
  343. mszString->Buffer += (mszString->Length/2)+1;
  344. mszString->MaximumLength -= (mszString->Length+2);
  345. }
  346. for( Count=0; Count<mszString->MaximumLength; Count++ )
  347. {
  348. if( mszString->Buffer[Count] == (WCHAR)'\0' )
  349. {
  350. mszString->Length = Count*2;
  351. if( Count > 0 )
  352. return TRUE;
  353. else
  354. return FALSE;
  355. }
  356. }
  357. // The starting data was bad!
  358. ASSERT(FALSE);
  359. return FALSE;
  360. }
  361. NTSTATUS
  362. SrvSnapInsertSnapIntoShare(
  363. IN PSHARE Share,
  364. IN PSHARE_SNAPSHOT SnapShot
  365. )
  366. {
  367. PLIST_ENTRY ListEntry = Share->SnapShots.Flink;
  368. // Walk the SnapShot share list and see if this is a duplicate
  369. while( ListEntry != &Share->SnapShots )
  370. {
  371. PSHARE_SNAPSHOT snapShare = CONTAINING_RECORD( ListEntry, SHARE_SNAPSHOT, SnapShotList );
  372. if( RtlEqualUnicodeString( &SnapShot->SnapShotName, &snapShare->SnapShotName, TRUE ) )
  373. {
  374. return STATUS_DUPLICATE_NAME;
  375. }
  376. ListEntry = ListEntry->Flink;
  377. }
  378. InsertTailList( &Share->SnapShots, &SnapShot->SnapShotList );
  379. return STATUS_SUCCESS;
  380. }
  381. NTSTATUS
  382. SrvSnapAddShare(
  383. IN PSHARE Share,
  384. IN PUNICODE_STRING SnapPath
  385. )
  386. /*++
  387. Routine Description:
  388. This function allocates a SnapShot Share that is being added to the system and
  389. initializes it
  390. Arguments:
  391. Share - Pointer to the parent share
  392. SnapPath - UNICODE_STRING name of the SnapShot (\\Device\\HardDiskSnap1)
  393. NOTE: Caller must have the SnapShotLock acquired
  394. Return Value:
  395. NTSTATUS - Returns STATUS_SUCCESS or STATUS_INSUFFICIENT_RESOURCES
  396. --*/
  397. {
  398. #define SHARE_DEVICE_HEADER 6
  399. NTSTATUS Status;
  400. PSHARE_SNAPSHOT snapShare = NULL;
  401. OBJECT_ATTRIBUTES objectAttributes;
  402. IO_STATUS_BLOCK IoStatus;
  403. ULONG AllocSize;
  404. TIME_FIELDS rtlTime;
  405. IF_DEBUG( SNAPSHOT) KdPrint(( "SrvSnapAddShare %p %wZ\n", Share, SnapPath ));
  406. // Calculate the size to allocate
  407. // sizeof(SNAP_STRUCT) + (Length of SnapShotName) + (Max Length of SnapShot Path)
  408. AllocSize = sizeof(SHARE_SNAPSHOT) + SNAPSHOT_NAME_LENGTH + (SnapPath->Length + Share->NtPathName.Length);
  409. snapShare = ALLOCATE_HEAP( AllocSize, BlockTypeSnapShot );
  410. if( !snapShare )
  411. {
  412. return STATUS_INSUFFICIENT_RESOURCES;
  413. }
  414. // Initialize the SnapShot-share structure
  415. RtlZeroMemory( snapShare, sizeof(SHARE_SNAPSHOT) );
  416. snapShare->SnapShotName.MaximumLength = SNAPSHOT_NAME_LENGTH;
  417. snapShare->SnapShotName.Length = 0;
  418. snapShare->SnapShotName.Buffer = (PWCHAR)(snapShare+1);
  419. // This is only valid on shares who's NT Path is \??\X:\ where X is a logical drive
  420. // Don't allow any others
  421. if( Share->NtPathName.Length < (SHARE_DEVICE_HEADER+1)*sizeof(WCHAR) )
  422. {
  423. Status = STATUS_INVALID_PARAMETER;
  424. goto Cleanup;
  425. }
  426. // Build the new SnapShot-relative path
  427. snapShare->SnapShotPath.MaximumLength = SnapPath->Length + Share->NtPathName.Length;
  428. snapShare->SnapShotPath.Length = 0;
  429. snapShare->SnapShotPath.Buffer = snapShare->SnapShotName.Buffer + (snapShare->SnapShotName.MaximumLength/sizeof(WCHAR));
  430. // Create it by using the SnapShot device and the Share NtPath
  431. RtlCopyUnicodeString( &snapShare->SnapShotPath, SnapPath );
  432. RtlCopyMemory( snapShare->SnapShotPath.Buffer + (snapShare->SnapShotPath.Length/2), Share->NtPathName.Buffer + SHARE_DEVICE_HEADER, Share->NtPathName.Length-SHARE_DEVICE_HEADER*sizeof(WCHAR) );
  433. snapShare->SnapShotPath.Length += (Share->NtPathName.Length-SHARE_DEVICE_HEADER*sizeof(WCHAR));
  434. //IF_DEBUG( SNAPSHOT ) KdPrint(( "%wZ -> %wZ\n", &Share->NtPathName, &snapShare->SnapShotPath ));
  435. // Now open the relative handle
  436. InitializeObjectAttributes(
  437. &objectAttributes,
  438. &snapShare->SnapShotPath,
  439. OBJ_CASE_INSENSITIVE,
  440. NULL,
  441. NULL
  442. );
  443. Status = NtOpenFile(
  444. &snapShare->SnapShotRootDirectoryHandle,
  445. FILE_TRAVERSE,
  446. &objectAttributes,
  447. &IoStatus,
  448. FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
  449. 0
  450. );
  451. if( !NT_SUCCESS(Status) )
  452. {
  453. goto Cleanup;
  454. }
  455. // Fill in the configuration information
  456. Status = SrvSnapFillConfigInfo( snapShare, SnapPath );
  457. if( !NT_SUCCESS(Status) )
  458. {
  459. NtClose( snapShare->SnapShotRootDirectoryHandle );
  460. goto Cleanup;
  461. }
  462. // Generate the SnapShot name
  463. snapShare->SnapShotName.Length = SNAPSHOT_NAME_LENGTH-sizeof(WCHAR);
  464. RtlTimeToTimeFields( &snapShare->Timestamp, &rtlTime );
  465. rtlTime.Milliseconds = 0;
  466. rtlTime.Weekday = 0;
  467. _snwprintf( snapShare->SnapShotName.Buffer, SNAPSHOT_NAME_LENGTH, SNAPSHOT_NAME_FORMAT, rtlTime.Year, rtlTime.Month, rtlTime.Day, rtlTime.Hour, rtlTime.Minute, rtlTime.Second );
  468. RtlTimeFieldsToTime( &rtlTime, &snapShare->Timestamp );
  469. ASSERT( wcslen( snapShare->SnapShotName.Buffer ) == snapShare->SnapShotName.Length );
  470. // Insert it into the list, and return Success
  471. Status = SrvSnapInsertSnapIntoShare( Share, snapShare );
  472. if( !NT_SUCCESS(Status) ) {
  473. NtClose( snapShare->SnapShotRootDirectoryHandle );
  474. goto Cleanup;
  475. }
  476. //IF_DEBUG( SNAPSHOT ) KdPrint(( "%wZ Handle=%p\n", &snapShare->SnapShotPath, snapShare->SnapShotRootDirectoryHandle ));
  477. Cleanup:
  478. // Cleanup
  479. if( !NT_SUCCESS(Status) )
  480. {
  481. if( snapShare ) FREE_HEAP( snapShare );
  482. }
  483. return Status;
  484. }
  485. NTSTATUS
  486. SrvSnapRemoveShare(
  487. IN PSHARE_SNAPSHOT SnapShare
  488. )
  489. /*++
  490. Routine Description:
  491. This function deallocates a SnapShot Share after it has been removed from
  492. the underlying disk
  493. NOTE: Caller must have the SnapShotLock acquired
  494. Arguments:
  495. SnapShare - Pointer to the SnapShot Share to remove. It will be removed and
  496. deallocated.
  497. Return Value:
  498. NTSTATUS - Returns STATUS_SUCCESS
  499. --*/
  500. {
  501. IF_DEBUG( SNAPSHOT ) KdPrint(( "SrvSnapRemoveShare %p %wZ\n", SnapShare, &SnapShare->SnapShotName ));
  502. if( SnapShare->SnapShotRootDirectoryHandle )
  503. {
  504. NtClose( SnapShare->SnapShotRootDirectoryHandle );
  505. SnapShare->SnapShotRootDirectoryHandle = NULL;
  506. }
  507. RemoveEntryList( &SnapShare->SnapShotList );
  508. FREE_HEAP( SnapShare );
  509. return STATUS_SUCCESS;
  510. }
  511. NTSTATUS
  512. SrvSnapCheckForAndCreateSnapShare(
  513. IN PSHARE Share,
  514. IN PUNICODE_STRING SnapShotName
  515. )
  516. /*++
  517. Routine Description:
  518. This function checks to see if a given SnapShot share exists on the given
  519. share, and if it does not it creates one. If it does, it removes the NOT_FOUND
  520. flag to signify this share still exists
  521. NOTE: Caller must have the SnapShotLock acquired
  522. Arguments:
  523. Share - The parent share of the SnapShot
  524. SnapShotName - The UNICODE_STRING name of the SnapShot (\\Device\\HardDiskSnap1)
  525. Return Value:
  526. NTSTATUS - Returns STATUS_SUCCESS or an unexpected error
  527. --*/
  528. {
  529. PLIST_ENTRY snapList;
  530. UNICODE_STRING SnapPartialName;
  531. //IF_DEBUG( SNAPSHOT ) KdPrint(( "Share %x, Name %wZ\n", Share, SnapShotName ));
  532. snapList = Share->SnapShots.Flink;
  533. SnapPartialName.Length = SnapPartialName.MaximumLength = SnapShotName->Length;
  534. while( snapList != &Share->SnapShots )
  535. {
  536. PSHARE_SNAPSHOT snapShare = CONTAINING_RECORD( snapList, SHARE_SNAPSHOT, SnapShotList );
  537. snapList = snapList->Flink;
  538. if( snapShare->SnapShotPath.Length < SnapPartialName.Length )
  539. {
  540. return STATUS_INTERNAL_ERROR;
  541. }
  542. SnapPartialName.Buffer = snapShare->SnapShotPath.Buffer;
  543. if( RtlEqualUnicodeString( SnapShotName, &SnapPartialName, TRUE ) )
  544. {
  545. ClearFlag( snapShare->Flags, SRV_SNAP_SHARE_NOT_FOUND );
  546. return STATUS_SUCCESS;
  547. }
  548. }
  549. return SrvSnapAddShare( Share, SnapShotName );
  550. }
  551. NTSTATUS
  552. SrvSnapRefreshSnapShotsForShare(
  553. IN PSHARE Share
  554. )
  555. /*++
  556. Routine Description:
  557. This function takes a share and refreshes the SnapShot views on the share
  558. so that only currently existing SnapShots are listed
  559. Arguments:
  560. Share - The share we are examining
  561. Return Value:
  562. NTSTATUS - STATUS_SUCCESS if all went well, otherwise the appropriate error
  563. code (and the request should be failed)
  564. --*/
  565. {
  566. NTSTATUS Status = STATUS_UNSUCCESSFUL;
  567. OBJECT_HANDLE_INFORMATION HandleInfo;
  568. PVOLSNAP_NAMES pNames = NULL;
  569. UNICODE_STRING VolumeName;
  570. UNICODE_STRING RootVolume;
  571. HANDLE VolumeHandle;
  572. OBJECT_ATTRIBUTES objectAttributes;
  573. IO_STATUS_BLOCK iosb;
  574. PLIST_ENTRY shareList;
  575. // Validate we can do SnapShots
  576. if( Share->Removable )
  577. {
  578. Status = STATUS_NOT_SUPPORTED;
  579. goto Cleanup;
  580. }
  581. // Get a handle to the root volume
  582. RootVolume.MaximumLength = Share->NtPathName.Length;
  583. RootVolume.Buffer = Share->NtPathName.Buffer;
  584. RootVolume.Length = 12;
  585. if( (Share->NtPathName.Length < 12) ||
  586. (RootVolume.Buffer[5] != L':') )
  587. {
  588. IF_DEBUG( SNAPSHOT ) KdPrint(( "Bad NtPathName on Share (%wZ)\n", &Share->NtPathName ));
  589. Status = STATUS_INVALID_PARAMETER;
  590. goto Cleanup;
  591. }
  592. InitializeObjectAttributes(
  593. &objectAttributes,
  594. &RootVolume,
  595. OBJ_CASE_INSENSITIVE,
  596. NULL,
  597. NULL
  598. );
  599. Status = NtOpenFile(
  600. &VolumeHandle,
  601. FILE_TRAVERSE,
  602. &objectAttributes,
  603. &iosb,
  604. FILE_SHARE_READ|FILE_SHARE_WRITE,
  605. 0
  606. );
  607. if( !NT_SUCCESS(Status) )
  608. {
  609. goto Cleanup;
  610. }
  611. // Get the NamesArray for the volume
  612. Status = SrvSnapGetNamesForVolume( VolumeHandle, &pNames );
  613. NtClose( VolumeHandle );
  614. if( !NT_SUCCESS(Status) )
  615. {
  616. goto Cleanup;
  617. }
  618. else if( !pNames )
  619. {
  620. ACQUIRE_LOCK( Share->SnapShotLock );
  621. // No SnapShots were found, so delete any that exist
  622. shareList = Share->SnapShots.Flink;
  623. while( shareList != &Share->SnapShots )
  624. {
  625. PSHARE_SNAPSHOT snapShare = CONTAINING_RECORD( shareList, SHARE_SNAPSHOT, SnapShotList );
  626. shareList = shareList->Flink;
  627. SrvSnapRemoveShare( snapShare );
  628. }
  629. RELEASE_LOCK( Share->SnapShotLock );
  630. return STATUS_SUCCESS;
  631. }
  632. if( pNames->MultiSzLength > 0xFFFF )
  633. {
  634. Status = STATUS_BUFFER_OVERFLOW;
  635. goto Cleanup;
  636. }
  637. VolumeName.MaximumLength = (USHORT)pNames->MultiSzLength;
  638. VolumeName.Length = 0;
  639. VolumeName.Buffer = pNames->Names;
  640. ACQUIRE_LOCK( Share->SnapShotLock );
  641. // Mark all the snap-shares as "not-found"
  642. shareList = Share->SnapShots.Flink;
  643. while( shareList != &Share->SnapShots )
  644. {
  645. PSHARE_SNAPSHOT snapShare = CONTAINING_RECORD( shareList, SHARE_SNAPSHOT, SnapShotList );
  646. snapShare->Flags |= SRV_SNAP_SHARE_NOT_FOUND;
  647. shareList = shareList->Flink;
  648. }
  649. // Walk the name list and create a SnapShot for any volumes we don't currently have
  650. while( SrvParseMultiSZ( &VolumeName ) )
  651. {
  652. Status = SrvSnapCheckForAndCreateSnapShare( Share, &VolumeName );
  653. if( !NT_SUCCESS(Status) )
  654. {
  655. IF_DEBUG( SNAPSHOT ) KdPrint(( "Failed to Add share %wZ (%x). Continuing..\n", &VolumeName, Status ));
  656. Status = STATUS_SUCCESS;
  657. }
  658. }
  659. // Any shares that are still marked as "not-found" are no longer availibe,
  660. // so we need to remove them
  661. shareList = Share->SnapShots.Flink;
  662. while( shareList != &Share->SnapShots )
  663. {
  664. PSHARE_SNAPSHOT snapShare = CONTAINING_RECORD( shareList, SHARE_SNAPSHOT, SnapShotList );
  665. shareList = shareList->Flink;
  666. if( snapShare->Flags & SRV_SNAP_SHARE_NOT_FOUND )
  667. {
  668. SrvSnapRemoveShare( snapShare );
  669. }
  670. }
  671. RELEASE_LOCK( Share->SnapShotLock );
  672. Cleanup:
  673. // Release the memory associated with the enumeration
  674. if( pNames )
  675. {
  676. DEALLOCATE_NONPAGED_POOL( pNames );
  677. pNames = NULL;
  678. }
  679. return Status;
  680. }
  681. NTSTATUS
  682. SrvSnapEnumerateSnapShots(
  683. IN PWORK_CONTEXT WorkContext
  684. )
  685. /*++
  686. Routine Description:
  687. This function handles a transaction that wants to enumerate the availible
  688. SnapShots for a given share
  689. Arguments:
  690. WorkContext - The context for the transaction
  691. Return Value:
  692. NTSTATUS - STATUS_SUCCESS and STATUS_BUFFER_OVERFLOW are expected, and should
  693. be returned with data. Any other status code should be returned without data
  694. --*/
  695. {
  696. NTSTATUS Status;
  697. ULONG SnapShotCount;
  698. PLIST_ENTRY listEntry;
  699. PSHARE Share = WorkContext->TreeConnect->Share;
  700. PTRANSACTION transaction = WorkContext->Parameters.Transaction;
  701. PSRV_SNAPSHOT_ARRAY SnapShotArray = (PSRV_SNAPSHOT_ARRAY)transaction->OutData;
  702. ASSERT(WorkContext->TreeConnect);
  703. // Check the buffer
  704. if( transaction->MaxDataCount < sizeof(SRV_SNAPSHOT_ARRAY) )
  705. {
  706. return STATUS_INVALID_PARAMETER;
  707. }
  708. // Refresh the SnapShot share list
  709. Status = SrvSnapRefreshSnapShotsForShare( Share );
  710. if( !NT_SUCCESS(Status) )
  711. {
  712. return Status;
  713. }
  714. // Lock the share
  715. ACQUIRE_LOCK_SHARED( Share->SnapShotLock );
  716. // Check the buffer size
  717. SnapShotCount = 0;
  718. listEntry = Share->SnapShots.Blink;
  719. while( listEntry != &(Share->SnapShots) )
  720. {
  721. SnapShotCount++;
  722. listEntry = listEntry->Blink;
  723. }
  724. // Set the value and check if we will overflow
  725. SnapShotArray->NumberOfSnapShots = SnapShotCount;
  726. SnapShotArray->SnapShotArraySize = SNAPSHOT_NAME_LENGTH*SnapShotArray->NumberOfSnapShots+sizeof(WCHAR);
  727. if( (SnapShotCount == 0) || (transaction->MaxDataCount < SnapShotArray->SnapShotArraySize) )
  728. {
  729. // The buffer is not big enough. Return the required size
  730. SnapShotArray->NumberOfSnapShotsReturned = 0;
  731. transaction->DataCount = sizeof(SRV_SNAPSHOT_ARRAY);
  732. Status = STATUS_SUCCESS;
  733. }
  734. else
  735. {
  736. // The buffer is big enough. Fill it in and return it
  737. PBYTE nameLocation = (PBYTE)SnapShotArray->SnapShotMultiSZ;
  738. SnapShotCount = 0;
  739. listEntry = Share->SnapShots.Blink;
  740. RtlZeroMemory( SnapShotArray->SnapShotMultiSZ, SnapShotArray->SnapShotArraySize );
  741. while( listEntry != &(Share->SnapShots) )
  742. {
  743. PSHARE_SNAPSHOT SnapShot = CONTAINING_RECORD( listEntry, SHARE_SNAPSHOT, SnapShotList );
  744. RtlCopyMemory( nameLocation, SnapShot->SnapShotName.Buffer, SNAPSHOT_NAME_LENGTH );
  745. nameLocation += SNAPSHOT_NAME_LENGTH;
  746. SnapShotCount++;
  747. listEntry = listEntry->Blink;
  748. }
  749. SnapShotArray->NumberOfSnapShotsReturned = SnapShotArray->NumberOfSnapShots;
  750. transaction->DataCount = sizeof(SRV_SNAPSHOT_ARRAY)+SnapShotArray->SnapShotArraySize;
  751. Status = STATUS_SUCCESS;
  752. }
  753. // Release the lock
  754. RELEASE_LOCK( Share->SnapShotLock );
  755. return Status;
  756. }
  757. NTSTATUS
  758. SrvSnapGetRootHandle(
  759. IN PWORK_CONTEXT WorkContext,
  760. OUT HANDLE* RootHandle
  761. )
  762. /*++
  763. Routine Description:
  764. This function retrieves the correct Root Handle for an operation given the
  765. parsed SnapShot timestamp on the WORK_CONTEXT
  766. Arguments:
  767. WorkContext - The context for the transaction
  768. RootHandle - Where to store the resulting handle
  769. Return Value:
  770. NTSTATUS - STATUS_SUCCESS or STATUS_NOT_FOUND if the SnapShot is not found
  771. --*/
  772. {
  773. NTSTATUS Status = STATUS_OBJECT_NAME_NOT_FOUND;
  774. PSHARE Share;
  775. PLIST_ENTRY listEntry;
  776. PSHARE_SNAPSHOT SnapShare;
  777. ASSERT( WorkContext );
  778. ASSERT( WorkContext->TreeConnect );
  779. ASSERT( WorkContext->TreeConnect->Share );
  780. Share = WorkContext->TreeConnect->Share;
  781. if( WorkContext->SnapShotTime.QuadPart != 0 )
  782. {
  783. //IF_DEBUG( SNAPSHOT ) KdPrint(( "Looking for %x%x\n", WorkContext->SnapShotTime.HighPart, WorkContext->SnapShotTime.LowPart ));
  784. // Acquire the shared lock
  785. ACQUIRE_LOCK_SHARED( Share->SnapShotLock );
  786. // Walk the list and look for the entry
  787. listEntry = Share->SnapShots.Flink;
  788. while( listEntry != &Share->SnapShots )
  789. {
  790. SnapShare = CONTAINING_RECORD( listEntry, SHARE_SNAPSHOT, SnapShotList );
  791. if( SnapShare->Timestamp.QuadPart == WorkContext->SnapShotTime.QuadPart )
  792. {
  793. //IF_DEBUG( SNAPSHOT ) KdPrint((" Found %wZ\n", &SnapShare->SnapShotName ));
  794. *RootHandle = SnapShare->SnapShotRootDirectoryHandle;
  795. Status = STATUS_SUCCESS;
  796. break;
  797. }
  798. listEntry = listEntry->Flink;
  799. }
  800. RELEASE_LOCK( Share->SnapShotLock );
  801. }
  802. else
  803. {
  804. *RootHandle = Share->RootDirectoryHandle;
  805. Status = STATUS_SUCCESS;
  806. }
  807. return Status;
  808. }
  809. NTSTATUS
  810. SrvSnapGetNameString(
  811. IN PWORK_CONTEXT WorkContext,
  812. OUT PUNICODE_STRING* ShareName,
  813. OUT PBOOLEAN AllocatedShareName
  814. )
  815. /*++
  816. Routine Description:
  817. This function retrieves the correct Root Handle for an operation given the
  818. parsed SnapShot timestamp on the WORK_CONTEXT
  819. Arguments:
  820. WorkContext - The context for the transaction
  821. ShareName - The Name of the share
  822. AllocatedShareName - Whether the ShareName should be freed after use (via FREE_HEAP)
  823. Return Value:
  824. PUNICODE_STRING - pointer to existing string or NULL
  825. BUGBUG = Have to take a reference or copy at this point!
  826. --*/
  827. {
  828. NTSTATUS Status = STATUS_OBJECT_NAME_NOT_FOUND;
  829. PSHARE Share;
  830. PLIST_ENTRY listEntry;
  831. PSHARE_SNAPSHOT SnapShare;
  832. PUNICODE_STRING SnapShareName;
  833. ASSERT( WorkContext );
  834. ASSERT( WorkContext->TreeConnect );
  835. ASSERT( WorkContext->TreeConnect->Share );
  836. Share = WorkContext->TreeConnect->Share;
  837. if( WorkContext->SnapShotTime.QuadPart != 0 )
  838. {
  839. //IF_DEBUG( SNAPSHOT ) KdPrint(( "Looking for %x%x\n", WorkContext->SnapShotTime.HighPart, WorkContext->SnapShotTime.LowPart ));
  840. // Acquire the shared lock
  841. ACQUIRE_LOCK_SHARED( Share->SnapShotLock );
  842. // Walk the list and look for the entry
  843. listEntry = Share->SnapShots.Flink;
  844. while( listEntry != &Share->SnapShots )
  845. {
  846. SnapShare = CONTAINING_RECORD( listEntry, SHARE_SNAPSHOT, SnapShotList );
  847. if( SnapShare->Timestamp.QuadPart == WorkContext->SnapShotTime.QuadPart )
  848. {
  849. SnapShareName = ALLOCATE_HEAP( sizeof(UNICODE_STRING)+SnapShare->SnapShotPath.Length, BlockTypeSnapShot );
  850. if( !SnapShareName )
  851. {
  852. Status = STATUS_INSUFFICIENT_RESOURCES;
  853. *AllocatedShareName = FALSE;
  854. }
  855. else
  856. {
  857. SnapShareName->Length = 0;
  858. SnapShareName->MaximumLength = SnapShare->SnapShotPath.Length;
  859. SnapShareName->Buffer = (PWCHAR)(SnapShareName+1);
  860. RtlCopyUnicodeString( SnapShareName, &SnapShare->SnapShotPath );
  861. *AllocatedShareName = TRUE;
  862. *ShareName = SnapShareName;
  863. Status = STATUS_SUCCESS;
  864. }
  865. RELEASE_LOCK( Share->SnapShotLock );
  866. return Status;
  867. }
  868. listEntry = listEntry->Flink;
  869. }
  870. RELEASE_LOCK( Share->SnapShotLock );
  871. return Status;
  872. }
  873. else
  874. {
  875. *ShareName = &WorkContext->TreeConnect->Share->NtPathName;
  876. *AllocatedShareName = FALSE;
  877. return STATUS_SUCCESS;
  878. }
  879. }
  880. BOOLEAN
  881. ExtractNumber(
  882. IN PWSTR psz,
  883. IN ULONG Count,
  884. OUT PLONG value
  885. )
  886. /*++
  887. Routine Description:
  888. This function takes a string of characters and parses out a <Count> length decimal
  889. number. If it returns TRUE, value has been set and the string was parsed correctly.
  890. FALSE indicates an error in parsing.
  891. Arguments:
  892. psz - String pointer
  893. Count - Number of characters to pull off
  894. value - pointer to output parameter where value is stored
  895. Return Value:
  896. BOOLEAN - See description
  897. --*/
  898. {
  899. *value = 0;
  900. while( Count )
  901. {
  902. if( (*psz == L'\0') ||
  903. IS_UNICODE_PATH_SEPARATOR( *psz ) )
  904. {
  905. //IF_DEBUG( SNAPSHOT ) KdPrint(( "Path Seperator found %d\n", Count ));
  906. return FALSE;
  907. }
  908. if( (*psz < '0') || (*psz > '9') )
  909. {
  910. //IF_DEBUG( SNAPSHOT ) KdPrint(( "Non-digit found %x\n", *psz ));
  911. return FALSE;
  912. }
  913. *value = (*value)*10+(*psz-L'0');
  914. Count--;
  915. psz++;
  916. }
  917. return TRUE;
  918. }
  919. BOOLEAN
  920. SrvSnapParseToken(
  921. IN PWSTR Source,
  922. OUT PLARGE_INTEGER TimeStamp
  923. )
  924. /*++
  925. Routine Description:
  926. This function parses a null-terminated UNICODE file path name string to see if the
  927. current token is a Snap-designator
  928. Arguments:
  929. Source - Pointer to the string
  930. TimeStamp - If this is a SnapShot, this is set to the time value designated by the string
  931. Return Value:
  932. PUNICODE_STRING - pointer to existing string or NULL
  933. --*/
  934. {
  935. PWSTR psz = Source;
  936. UNICODE_STRING NameString;
  937. ULONG Count = 0;
  938. #define SNAPSHOT_HEADER L"@GMT-"
  939. PWSTR header = SNAPSHOT_HEADER;
  940. TIME_FIELDS rtlTime;
  941. LONG value;
  942. // Check the SNAP. header
  943. for( Count=0; Count<wcslen(SNAPSHOT_HEADER); Count++,psz++ )
  944. {
  945. if( (toupper(*psz) != header[Count]) ||
  946. (*psz == L'\0') ||
  947. IS_UNICODE_PATH_SEPARATOR( *psz ) )
  948. {
  949. //IF_DEBUG( SNAPSHOT ) KdPrint(("Count %d (%x != %x)\n", Count, *psz, header[Count] ));
  950. goto NoMatch;
  951. }
  952. }
  953. // Prepare to parse
  954. RtlZeroMemory( &rtlTime, sizeof(TIME_FIELDS) );
  955. // Extract the Year
  956. if( !ExtractNumber( psz, 4, &value ) )
  957. goto NoMatch;
  958. if( psz[4] != L'.' )
  959. goto NoMatch;
  960. rtlTime.Year = (CSHORT)value;
  961. psz += 5;
  962. // Extract the Month
  963. if( !ExtractNumber( psz, 2, &value ) )
  964. goto NoMatch;
  965. if( psz[2] != L'.' )
  966. goto NoMatch;
  967. rtlTime.Month = (CSHORT)value;
  968. psz += 3;
  969. // Extract the Day
  970. if( !ExtractNumber( psz, 2, &value ) )
  971. goto NoMatch;
  972. if( psz[2] != L'-' )
  973. goto NoMatch;
  974. rtlTime.Day = (CSHORT)value;
  975. psz += 3;
  976. // Extract the Hour
  977. if( !ExtractNumber( psz, 2, &value ) )
  978. goto NoMatch;
  979. if( psz[2] != L'.' )
  980. goto NoMatch;
  981. rtlTime.Hour = (CSHORT)value;
  982. psz += 3;
  983. // Extract the Minutes
  984. if( !ExtractNumber( psz, 2, &value ) )
  985. goto NoMatch;
  986. if( psz[2] != L'.' )
  987. goto NoMatch;
  988. rtlTime.Minute = (CSHORT)value;
  989. psz += 3;
  990. // Extract the Seconds
  991. if( !ExtractNumber( psz, 2, &value ) )
  992. goto NoMatch;
  993. if( !IS_UNICODE_PATH_SEPARATOR( psz[2] ) &&
  994. (psz[2] != L'\0') )
  995. goto NoMatch;
  996. rtlTime.Second = (CSHORT)value;
  997. psz += 3;
  998. RtlTimeFieldsToTime( &rtlTime, TimeStamp );
  999. return TRUE;
  1000. NoMatch:
  1001. return FALSE;
  1002. }