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.

3003 lines
91 KiB

  1. /*++
  2. Copyright (c) 1998-1999 Microsoft Corporation
  3. Module Name:
  4. filenames.c
  5. Abstract:
  6. this is the code that handles the file lists (exclusion/inclusion).
  7. Author:
  8. Neal Christiansen (nealch) 03-Jan-2001
  9. Revision History:
  10. --*/
  11. #include "precomp.h"
  12. //
  13. // Local prototypes
  14. //
  15. NTSTATUS
  16. SrpExpandShortNames(
  17. IN PSR_DEVICE_EXTENSION pExtension,
  18. IN OUT PSRP_NAME_CONTROL pNameCtrl,
  19. IN BOOLEAN expandFileNameComponenet
  20. );
  21. NTSTATUS
  22. SrpExpandPathOfFileName(
  23. IN PSR_DEVICE_EXTENSION pExtension,
  24. IN OUT PSRP_NAME_CONTROL pNameCtrl,
  25. OUT PBOOLEAN pReasonableErrorForUnOpenedName
  26. );
  27. //
  28. // linker commands
  29. //
  30. #ifdef ALLOC_PRAGMA
  31. #pragma alloc_text( PAGE, SrpGetFileName )
  32. #pragma alloc_text( PAGE, SrpGetFileNameFromFileObject )
  33. #pragma alloc_text( PAGE, SrpGetFileNameOpenById )
  34. #pragma alloc_text( PAGE, SrpExpandShortNames )
  35. #pragma alloc_text( PAGE, SrpExpandPathOfFileName )
  36. #pragma alloc_text( PAGE, SrpRemoveStreamName )
  37. #pragma alloc_text( PAGE, SrpExpandDestPath )
  38. #pragma alloc_text( PAGE, SrpInitNameControl )
  39. #pragma alloc_text( PAGE, SrpCleanupNameControl )
  40. #pragma alloc_text( PAGE, SrpReallocNameControl )
  41. #pragma alloc_text( PAGE, SrpExpandFileName )
  42. #pragma alloc_text( PAGE, SrIsFileEligible )
  43. #pragma alloc_text( PAGE, SrFileNameContainsStream )
  44. #pragma alloc_text( PAGE, SrFileAlreadyExists )
  45. #pragma alloc_text( PAGE, SrIsFileStream )
  46. #endif // ALLOC_PRAGMA
  47. #if DBG
  48. /***************************************************************************++
  49. Routine Description:
  50. This presently scans the filename looking for two backslashes in a row.
  51. If so
  52. Arguments:
  53. Return Value:
  54. --***************************************************************************/
  55. VOID
  56. VALIDATE_FILENAME(
  57. IN PUNICODE_STRING pName
  58. )
  59. {
  60. ULONG i;
  61. ULONG len;
  62. if (pName && (pName->Length > 0))
  63. {
  64. len = (pName->Length/sizeof(WCHAR))-1;
  65. //
  66. // Setup to scan the name
  67. //
  68. for (i=0;
  69. i < len;
  70. i++ )
  71. {
  72. //
  73. // See if there are adjacent backslashes
  74. //
  75. if (pName->Buffer[i] == L'\\' &&
  76. pName->Buffer[i+1] == L'\\')
  77. {
  78. if (FlagOn(global->DebugControl,
  79. SR_DEBUG_VERBOSE_ERRORS|SR_DEBUG_BREAK_ON_ERROR))
  80. {
  81. KdPrint(("sr!VALIDATE_FILENAME: Detected adjacent backslashes in \"%wZ\"\n",
  82. pName));
  83. }
  84. if (FlagOn(global->DebugControl,SR_DEBUG_BREAK_ON_ERROR))
  85. {
  86. DbgBreakPoint();
  87. }
  88. }
  89. }
  90. }
  91. }
  92. #endif
  93. /***************************************************************************++
  94. Routine Description:
  95. This routine will try and get the name of the given file object. This
  96. will allocate a buffer if it needs to. Because of deadlock issues we
  97. do not call ObQueryNameString. Instead we ask the file system for the
  98. name by generating our own IRPs.
  99. Arguments:
  100. pExtension - the extension for the device this file is on
  101. pFileObject - the fileObject for the file we want the name of.
  102. pNameControl - a structure used to manage the name of the file
  103. Return Value:
  104. status of the operation
  105. --***************************************************************************/
  106. NTSTATUS
  107. SrpGetFileName (
  108. IN PSR_DEVICE_EXTENSION pExtension,
  109. IN PFILE_OBJECT pFileObject,
  110. IN OUT PSRP_NAME_CONTROL pNameCtrl
  111. )
  112. {
  113. PFILE_NAME_INFORMATION nameInfo;
  114. NTSTATUS status;
  115. ULONG volNameLength = (ULONG)pExtension->pNtVolumeName->Length;
  116. ULONG returnLength;
  117. ULONG fullNameLength;
  118. PAGED_CODE();
  119. ASSERT(IS_VALID_FILE_OBJECT( pFileObject ) && (pFileObject->Vpb != NULL));
  120. ASSERT(pNameCtrl->AllocatedBuffer == NULL);
  121. //
  122. // Use the small buffer in the structure (that will handle most cases)
  123. // for the name. Then get the name
  124. //
  125. nameInfo = (PFILE_NAME_INFORMATION)pNameCtrl->SmallBuffer;
  126. status = SrQueryInformationFile( pExtension->pTargetDevice,
  127. pFileObject,
  128. nameInfo,
  129. pNameCtrl->BufferSize - sizeof(WCHAR),
  130. FileNameInformation,
  131. &returnLength );
  132. //
  133. // If the buffer was too small, get a bigger one.
  134. //
  135. if (status == STATUS_BUFFER_OVERFLOW)
  136. {
  137. //
  138. // The buffer was too small, allocate one big enough (including volume
  139. // name and terminating NULL)
  140. //
  141. status = SrpReallocNameControl( pNameCtrl,
  142. nameInfo->FileNameLength +
  143. volNameLength +
  144. SHORT_NAME_EXPANSION_SPACE +
  145. sizeof(WCHAR),
  146. NULL );
  147. if (!NT_SUCCESS( status ))
  148. {
  149. return status;
  150. }
  151. //
  152. // Set the allocated buffer and get the name again
  153. //
  154. nameInfo = (PFILE_NAME_INFORMATION)pNameCtrl->AllocatedBuffer;
  155. status = SrQueryInformationFile( pExtension->pTargetDevice,
  156. pFileObject,
  157. nameInfo,
  158. pNameCtrl->BufferSize - sizeof(WCHAR),
  159. FileNameInformation,
  160. &returnLength );
  161. }
  162. //
  163. // Handle QueryInformation errors
  164. //
  165. if (!NT_SUCCESS( status ))
  166. {
  167. return status;
  168. }
  169. //
  170. // We now have the filename. Calucalte how much space the full name
  171. // (including device name) will be. Include space for a terminating
  172. // NULL. See if there is room in the current buffer.
  173. //
  174. fullNameLength = nameInfo->FileNameLength + volNameLength;
  175. status = SrpNameCtrlBufferCheck( pNameCtrl, fullNameLength);
  176. if (!NT_SUCCESS( status ))
  177. {
  178. return status;
  179. }
  180. //
  181. // Slide the file name down to make room for the device name. Account
  182. // for the header in the FILE_NAME_INFORMATION structure
  183. //
  184. RtlMoveMemory( &pNameCtrl->Name.Buffer[volNameLength/sizeof(WCHAR) ],
  185. nameInfo->FileName,
  186. nameInfo->FileNameLength );
  187. RtlCopyMemory( pNameCtrl->Name.Buffer,
  188. pExtension->pNtVolumeName->Buffer,
  189. volNameLength );
  190. pNameCtrl->Name.Length = (USHORT)fullNameLength;
  191. return STATUS_SUCCESS;
  192. }
  193. /***************************************************************************++
  194. Routine Description:
  195. Gets the file name and volume name for a file that is not yet opened.
  196. This is necessary in the MJ_CREATE where work is done prior to the file
  197. being opened by the fsd.
  198. Arguments:
  199. Return Value:
  200. NTSTATUS - completion code.
  201. --***************************************************************************/
  202. NTSTATUS
  203. SrpGetFileNameFromFileObject (
  204. IN PSR_DEVICE_EXTENSION pExtension,
  205. IN PFILE_OBJECT pFileObject,
  206. IN OUT PSRP_NAME_CONTROL pNameCtrl,
  207. OUT PBOOLEAN pReasonableErrorForUnOpenedName
  208. )
  209. {
  210. NTSTATUS status;
  211. ULONG fullNameLength;
  212. PAGED_CODE();
  213. ASSERT(IS_VALID_FILE_OBJECT(pFileObject));
  214. ASSERT(pExtension->pNtVolumeName != NULL);
  215. ASSERT(pExtension->pNtVolumeName->Length > 0);
  216. ASSERT(pNameCtrl->AllocatedBuffer == NULL);
  217. //
  218. // first see if this is a relative open
  219. //
  220. if (pFileObject->RelatedFileObject != NULL)
  221. {
  222. //
  223. // get the full name of the related file object
  224. //
  225. status = SrpGetFileName( pExtension,
  226. pFileObject->RelatedFileObject,
  227. pNameCtrl );
  228. if (!NT_SUCCESS_NO_DBGBREAK(status))
  229. {
  230. goto Cleanup;
  231. }
  232. ASSERT(pNameCtrl->Name.Length > 0);
  233. //
  234. // make sure the buffer is still large enough. Note that we account
  235. // for a trailing null to be added as well as the poosible addition
  236. // of a separator character.
  237. //
  238. fullNameLength = pNameCtrl->Name.Length +
  239. sizeof(WCHAR) + // could be a seperator
  240. pFileObject->FileName.Length;
  241. status = SrpNameCtrlBufferCheck( pNameCtrl, fullNameLength );
  242. if (!NT_SUCCESS( status ))
  243. {
  244. return status;
  245. }
  246. //
  247. // put on the slash seperator if it is missing
  248. //
  249. if ((pFileObject->FileName.Length > 0) &&
  250. (pFileObject->FileName.Buffer[0] != L'\\') &&
  251. (pFileObject->FileName.Buffer[0] != L':') &&
  252. (pNameCtrl->Name.Buffer[(pNameCtrl->Name.Length/sizeof(WCHAR))-1] != L'\\'))
  253. {
  254. pNameCtrl->Name.Buffer[pNameCtrl->Name.Length/sizeof(WCHAR)] = L'\\';
  255. pNameCtrl->Name.Length += sizeof(WCHAR);
  256. }
  257. //
  258. // now append the file's name part
  259. //
  260. status = RtlAppendUnicodeStringToString( &pNameCtrl->Name,
  261. &pFileObject->FileName );
  262. ASSERT(STATUS_SUCCESS == status);
  263. }
  264. else
  265. {
  266. //
  267. // this is a full path open off of the volume
  268. //
  269. //
  270. // Make sure the buffer is large enough. Note that we account
  271. // for a trailing null to be added.
  272. //
  273. fullNameLength = pExtension->pNtVolumeName->Length + pFileObject->FileName.Length;
  274. status = SrpNameCtrlBufferCheck( pNameCtrl, fullNameLength );
  275. if (!NT_SUCCESS( status ))
  276. {
  277. return status;
  278. }
  279. //
  280. // set the volume name
  281. //
  282. RtlCopyUnicodeString( &pNameCtrl->Name,
  283. pExtension->pNtVolumeName );
  284. //
  285. // now append the file's name part (it already has the prefix '\\')
  286. //
  287. status = RtlAppendUnicodeStringToString( &pNameCtrl->Name,
  288. &pFileObject->FileName );
  289. ASSERT(STATUS_SUCCESS == status);
  290. }
  291. //
  292. // The main reson we come through this path is because we are in pre-
  293. // create and we got the name from the file object. We need to expand
  294. // the path to remove any mount points from it.
  295. //
  296. status = SrpExpandPathOfFileName( pExtension,
  297. pNameCtrl,
  298. pReasonableErrorForUnOpenedName );
  299. Cleanup:
  300. #if DBG
  301. if ((STATUS_MOUNT_POINT_NOT_RESOLVED == status) ||
  302. (STATUS_OBJECT_PATH_NOT_FOUND == status) ||
  303. (STATUS_OBJECT_NAME_NOT_FOUND == status) ||
  304. (STATUS_OBJECT_NAME_INVALID == status) ||
  305. (STATUS_REPARSE_POINT_NOT_RESOLVED == status) ||
  306. (STATUS_NOT_SAME_DEVICE == status))
  307. {
  308. return status;
  309. }
  310. #endif
  311. RETURN(status);
  312. }
  313. /***************************************************************************++
  314. Routine Description:
  315. Gets the file name if we have a file object that has not be fully
  316. opened yet and it has been opened by ID. In this case, the file must
  317. already exist. We will try to open the file by id to get a fully
  318. initialized file object, then use that file object to query the file name.
  319. Arguments:
  320. Return Value:
  321. NTSTATUS - completion code.
  322. --***************************************************************************/
  323. NTSTATUS
  324. SrpGetFileNameOpenById (
  325. IN PSR_DEVICE_EXTENSION pExtension,
  326. IN PFILE_OBJECT pFileObject,
  327. IN OUT PSRP_NAME_CONTROL pNameCtrl,
  328. OUT PBOOLEAN pReasonableErrorForUnOpenedName
  329. )
  330. {
  331. OBJECT_ATTRIBUTES ObjectAttributes;
  332. IO_STATUS_BLOCK IoStatusBlock;
  333. NTSTATUS Status;
  334. HANDLE FileHandle = NULL;
  335. PFILE_OBJECT pReopenedFileObject = NULL;
  336. SRP_NAME_CONTROL FileNameCtrl;
  337. ULONG FileNameLength;
  338. PAGED_CODE();
  339. *pReasonableErrorForUnOpenedName = FALSE;
  340. SrpInitNameControl( &FileNameCtrl );
  341. //
  342. // Make sure that we have a valid file name in the file object.
  343. //
  344. if (pFileObject->FileName.Length == 0)
  345. {
  346. Status = STATUS_OBJECT_NAME_INVALID;
  347. *pReasonableErrorForUnOpenedName = TRUE;
  348. goto SrpGetFileNameOpenById_Exit;
  349. }
  350. //
  351. // Build up our name so that it is in the format of:
  352. // \Device\HarddiskVolume1\[id in binary format]
  353. // We have the device name in our device extension and we have
  354. // the binary format of the file id in pFileObject->FileName.
  355. //
  356. FileNameLength = pExtension->pNtVolumeName->Length +
  357. sizeof( L'\\' ) +
  358. pFileObject->FileName.Length;
  359. Status = SrpNameCtrlBufferCheck( &FileNameCtrl,
  360. FileNameLength );
  361. if (!NT_SUCCESS( Status ))
  362. {
  363. goto SrpGetFileNameOpenById_Exit;
  364. }
  365. RtlCopyUnicodeString( &(FileNameCtrl.Name), pExtension->pNtVolumeName );
  366. //
  367. // Check to see if we need to add a '\' separator.
  368. //
  369. if (pFileObject->FileName.Buffer[0] != L'\\')
  370. {
  371. RtlAppendUnicodeToString( &(FileNameCtrl.Name), L"\\" );
  372. }
  373. RtlAppendUnicodeStringToString( &(FileNameCtrl.Name), &(pFileObject->FileName) );
  374. InitializeObjectAttributes( &ObjectAttributes,
  375. &(FileNameCtrl.Name),
  376. OBJ_KERNEL_HANDLE,
  377. NULL,
  378. NULL );
  379. Status = SrIoCreateFile( &FileHandle,
  380. GENERIC_READ,
  381. &ObjectAttributes,
  382. &IoStatusBlock,
  383. NULL,
  384. 0,
  385. FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
  386. FILE_OPEN,
  387. FILE_OPEN_BY_FILE_ID,
  388. NULL,
  389. 0,
  390. IO_IGNORE_SHARE_ACCESS_CHECK,
  391. pExtension->pTargetDevice );
  392. if (!NT_SUCCESS_NO_DBGBREAK( Status ))
  393. {
  394. *pReasonableErrorForUnOpenedName = TRUE;
  395. goto SrpGetFileNameOpenById_Exit;
  396. }
  397. Status = ObReferenceObjectByHandle( FileHandle,
  398. 0,
  399. *IoFileObjectType,
  400. KernelMode,
  401. (PVOID *) &pReopenedFileObject,
  402. NULL );
  403. if (!NT_SUCCESS( Status ))
  404. {
  405. goto SrpGetFileNameOpenById_Exit;
  406. }
  407. Status = SrpGetFileName( pExtension,
  408. pReopenedFileObject,
  409. pNameCtrl );
  410. CHECK_STATUS( Status );
  411. SrpGetFileNameOpenById_Exit:
  412. SrpCleanupNameControl( &FileNameCtrl );
  413. if (pReopenedFileObject != NULL)
  414. {
  415. ObDereferenceObject( pReopenedFileObject );
  416. }
  417. if (FileHandle != NULL)
  418. {
  419. ZwClose( FileHandle );
  420. }
  421. RETURN ( Status );
  422. }
  423. /***************************************************************************++
  424. Routine Description:
  425. This will scan for short file names in the filename buffer and expand
  426. them inplace. if we need to we will reallocate the name buffer to
  427. grow it.
  428. Arguments:
  429. Return Value:
  430. NTSTATUS - completion code.
  431. --***************************************************************************/
  432. NTSTATUS
  433. SrpExpandShortNames (
  434. IN PSR_DEVICE_EXTENSION pExtension,
  435. IN OUT PSRP_NAME_CONTROL pNameCtrl,
  436. IN BOOLEAN expandFileNameComponent
  437. )
  438. {
  439. NTSTATUS status = STATUS_SUCCESS;
  440. ULONG idx;
  441. ULONG start;
  442. ULONG end;
  443. ULONG nameLen;
  444. LONG shortNameLen;
  445. LONG copyLen;
  446. LONG delta;
  447. HANDLE directoryHandle = NULL;
  448. PFILE_NAMES_INFORMATION pFileEntry = NULL;
  449. OBJECT_ATTRIBUTES objectAttributes;
  450. IO_STATUS_BLOCK ioStatusBlock;
  451. UNICODE_STRING shortFileName;
  452. UNICODE_STRING expandedFileName;
  453. USHORT newFileNameLength;
  454. USHORT savedFileNameLength;
  455. PAGED_CODE();
  456. ASSERT( IS_VALID_SR_STREAM_STRING( &pNameCtrl->Name, pNameCtrl->StreamNameLength) );
  457. VALIDATE_FILENAME( &pNameCtrl->Name );
  458. nameLen = pNameCtrl->Name.Length / sizeof(WCHAR);
  459. //
  460. // scan the entire string
  461. //
  462. for (idx = 0; idx < nameLen; idx++)
  463. {
  464. //
  465. //
  466. // in this example the pointers are like this:
  467. //
  468. // \Device\HarddiskDmVolumes\PhysicalDmVolumes\
  469. // BlockVolume3\Progra~1\office.exe
  470. // ^ ^
  471. // | |
  472. // start end
  473. //
  474. // pStart always points to the last seen '\\' .
  475. //
  476. //
  477. // is this a potential start of a path part?
  478. //
  479. if (pNameCtrl->Name.Buffer[idx] == L'\\')
  480. {
  481. //
  482. // yes, save current position
  483. //
  484. start = idx;
  485. }
  486. //
  487. // does this current path part contain a short version (~)
  488. //
  489. else if (pNameCtrl->Name.Buffer[idx] == L'~')
  490. {
  491. //
  492. // we need to expand this part.
  493. //
  494. //
  495. // find the end (a ending slash or end of string)
  496. //
  497. while ((idx < nameLen) && (pNameCtrl->Name.Buffer[idx] != L'\\'))
  498. {
  499. idx++;
  500. }
  501. //
  502. // If we are looking at the filename component (we hit the end
  503. // of the string) and we are not to expand this component, quit
  504. // now
  505. //
  506. if ((idx >= nameLen) && !expandFileNameComponent)
  507. {
  508. break;
  509. }
  510. //
  511. // at this point idx points either to the NULL or to the
  512. // subsequent '\\', so we will use this as our
  513. //
  514. end = idx;
  515. ASSERT(pNameCtrl->Name.Buffer[start] == L'\\');
  516. ASSERT((end >= nameLen) || (pNameCtrl->Name.Buffer[end] == L'\\'));
  517. //
  518. // Get the length of the file component that we think might be a
  519. // short name. Only try and get the expanded name if the
  520. // component length is <= an 8.3 name length.
  521. //
  522. shortNameLen = end - start - 1;
  523. //
  524. // shortNameLen counts the number of characters, not bytes, in the
  525. // name, therefore we compare this against SR_SHORT_NAME_CHARS, not
  526. // SR_SHORT_NAME_CHARS * sizeof(WCHAR)
  527. //
  528. if (shortNameLen > SR_SHORT_NAME_CHARS)
  529. {
  530. //
  531. // This name is too long to be a short name. Make end the
  532. // next start and keep looping to look at the next path
  533. // component.
  534. //
  535. start = end;
  536. }
  537. else
  538. {
  539. //
  540. // We have a potential shortname here.
  541. //
  542. // Change the file name length to only include the parent
  543. // directory of the current name component (including
  544. // the trailing slash).
  545. //
  546. savedFileNameLength = pNameCtrl->Name.Length;
  547. pNameCtrl->Name.Length = (USHORT)(start+1)*sizeof(WCHAR);
  548. //
  549. // now open the parent directory
  550. //
  551. ASSERT(directoryHandle == NULL);
  552. InitializeObjectAttributes( &objectAttributes,
  553. &pNameCtrl->Name,
  554. OBJ_KERNEL_HANDLE,
  555. NULL,
  556. NULL );
  557. status = SrIoCreateFile(
  558. &directoryHandle,
  559. FILE_LIST_DIRECTORY | SYNCHRONIZE,
  560. &objectAttributes,
  561. &ioStatusBlock,
  562. NULL, // AllocationSize
  563. FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_NORMAL,
  564. FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, // ShareAccess
  565. FILE_OPEN, // OPEN_EXISTING
  566. FILE_DIRECTORY_FILE
  567. | FILE_OPEN_FOR_BACKUP_INTENT
  568. | FILE_SYNCHRONOUS_IO_NONALERT, //create options
  569. NULL,
  570. 0, // EaLength
  571. IO_IGNORE_SHARE_ACCESS_CHECK,
  572. pExtension->pTargetDevice );
  573. //
  574. // Now that we have the directory open, restore the original
  575. // saved file name length.
  576. //
  577. pNameCtrl->Name.Length = savedFileNameLength;
  578. //
  579. // Handle an open error
  580. //
  581. #if DBG
  582. if (STATUS_MOUNT_POINT_NOT_RESOLVED == status)
  583. {
  584. //
  585. // This is directory is through a mount point, so we don't
  586. // care about it here. We will wait and take care of it
  587. // when the name has been resolved to the correct volume.
  588. //
  589. goto Cleanup;
  590. }
  591. else
  592. #endif
  593. if (!NT_SUCCESS(status))
  594. {
  595. goto Cleanup;
  596. }
  597. //
  598. // Allocate a buffer to receive the filename if we don't
  599. // already have one.
  600. //
  601. if (pFileEntry == NULL)
  602. {
  603. pFileEntry = ExAllocatePoolWithTag(
  604. PagedPool,
  605. SR_FILE_ENTRY_LENGTH,
  606. SR_FILE_ENTRY_TAG );
  607. if (pFileEntry == NULL)
  608. {
  609. status = STATUS_INSUFFICIENT_RESOURCES;
  610. goto Cleanup;
  611. }
  612. }
  613. //
  614. // now set just the file part for the query
  615. //
  616. shortFileName.Buffer = &pNameCtrl->Name.Buffer[start+1];
  617. shortFileName.Length = (USHORT)shortNameLen * sizeof(WCHAR);
  618. shortFileName.MaximumLength = shortFileName.Length;
  619. //
  620. // query the file entry to get the long name
  621. //
  622. pFileEntry->FileNameLength = 0;
  623. status = ZwQueryDirectoryFile( directoryHandle,
  624. NULL,
  625. NULL,
  626. NULL,
  627. &ioStatusBlock,
  628. pFileEntry,
  629. SR_FILE_ENTRY_LENGTH,
  630. FileNamesInformation,
  631. TRUE, // ReturnSingleEntry
  632. &shortFileName,
  633. TRUE ); // RestartScan
  634. //
  635. // it's possible that this file doesn't exist yet. new
  636. // creates with a literal '~' in the name. or creates
  637. // with non-existant directories in the path with ~'s in
  638. // them
  639. //
  640. if (status == STATUS_NO_SUCH_FILE)
  641. {
  642. status = STATUS_SUCCESS;
  643. //
  644. // exit the 'for' loop
  645. //
  646. break;
  647. }
  648. else if (status == STATUS_UNMAPPABLE_CHARACTER)
  649. {
  650. //
  651. // this appears to be ok. fat returns this if there
  652. // are funny characters in the name, but still gives us
  653. // the full name back.
  654. //
  655. status = STATUS_SUCCESS;
  656. }
  657. else if (!NT_SUCCESS(status))
  658. {
  659. goto Cleanup;
  660. }
  661. ASSERT(pFileEntry->FileNameLength > 0);
  662. //
  663. // did it expand?
  664. //
  665. expandedFileName.Buffer = pFileEntry->FileName;
  666. expandedFileName.Length = (USHORT)pFileEntry->FileNameLength;
  667. expandedFileName.MaximumLength = (USHORT)pFileEntry->FileNameLength;
  668. if (RtlCompareUnicodeString(&expandedFileName,&shortFileName,TRUE) != 0)
  669. {
  670. SrTrace(EXPAND_SHORT_NAMES, ("sr!SrpExpandShortNames: expanded \"%wZ\" to \"%wZ\"\n", &shortFileName, &expandedFileName));
  671. //
  672. // Is there room in the current filename buffer for the
  673. // expanded file name
  674. //
  675. newFileNameLength = ((pNameCtrl->Name.Length - shortFileName.Length) +
  676. expandedFileName.Length);
  677. status = SrpNameCtrlBufferCheck( pNameCtrl,
  678. (ULONG)(newFileNameLength +
  679. pNameCtrl->StreamNameLength));
  680. if (!NT_SUCCESS( status )) {
  681. goto Cleanup;
  682. }
  683. //
  684. // shuffle things around to make room for the expanded name
  685. //
  686. delta = ((LONG)expandedFileName.Length - (LONG)shortFileName.Length)/
  687. (LONG)sizeof(WCHAR);
  688. //
  689. // Open a hole for the name
  690. //
  691. if (delta != 0)
  692. {
  693. copyLen = (pNameCtrl->Name.Length +
  694. pNameCtrl->StreamNameLength) -
  695. (end * sizeof(WCHAR));
  696. ASSERT(copyLen >= 0);
  697. if (copyLen > 0)
  698. {
  699. RtlMoveMemory( &pNameCtrl->Name.Buffer[end + delta],
  700. &pNameCtrl->Name.Buffer[end],
  701. copyLen );
  702. }
  703. }
  704. //
  705. // now copy over the expanded name
  706. //
  707. RtlCopyMemory(&pNameCtrl->Name.Buffer[start + 1],
  708. pFileEntry->FileName,
  709. pFileEntry->FileNameLength );
  710. //
  711. // and update our current index and lengths.
  712. //
  713. idx += delta;
  714. pNameCtrl->Name.Length = (USHORT)newFileNameLength;
  715. nameLen = newFileNameLength / sizeof(WCHAR);
  716. //
  717. // always NULL terminate
  718. //
  719. //pNameCtrl->Name.Buffer[pNameCtrl->Name.Length/sizeof(WCHAR)] = UNICODE_NULL;
  720. }
  721. //
  722. // close the directory handle.
  723. //
  724. ZwClose( directoryHandle );
  725. directoryHandle = NULL;
  726. //
  727. // We may have just expanded a name component. Make sure
  728. // that we still have valid name and stream name components.
  729. //
  730. ASSERT( IS_VALID_SR_STREAM_STRING( &pNameCtrl->Name, pNameCtrl->StreamNameLength) );
  731. //
  732. // skip start ahead to the next spot (the next slash or NULL)
  733. //
  734. start = idx;
  735. end = -1;
  736. }
  737. }
  738. }
  739. //
  740. // Cleanup state and return
  741. //
  742. Cleanup:
  743. if (NULL != pFileEntry)
  744. {
  745. ExFreePool(pFileEntry);
  746. NULLPTR(pFileEntry);
  747. }
  748. if (NULL != directoryHandle)
  749. {
  750. ZwClose(directoryHandle);
  751. NULLPTR(directoryHandle);
  752. }
  753. #if DBG
  754. if (STATUS_MOUNT_POINT_NOT_RESOLVED == status) {
  755. return status;
  756. }
  757. #endif
  758. VALIDATE_FILENAME( &pNameCtrl->Name );
  759. RETURN(status);
  760. }
  761. /***************************************************************************++
  762. Routine Description:
  763. This routine will take the given full path filename, extract the PATH
  764. portion, get its value and substitue it back into the original name
  765. if it is different. We do this to handle volume mount points as well
  766. as normallizing the name to a common format. NOTE: this does NOT
  767. expand short names, but it will normalize the volume name to the
  768. \Device\HarddiskVolume1 format.
  769. We do this by:
  770. open the parent to get the real path to the parent. we can't just
  771. open the target as it might not exist yet. we are sure the parent
  772. of the target always exists. if it doesn't, the rename will fail,
  773. and we are ok.
  774. Arguments:
  775. pExtension - the SR device extension for the volume we are working on
  776. pNameCtrl - the name control structure that contains the name we are to
  777. expand.
  778. pReasonableErrorForUnOpenedName - this will be set to TRUE if we hit
  779. a reasonable error (e.g., something that leads us to believe the
  780. original operation will also fail) during our work to expand the
  781. path.
  782. Return Value:
  783. NTSTATUS - STATUS_SUCCESS if we were able to expand the path without
  784. error, or the appropriate error code otherwise. If we did hit a
  785. reasonable error during this process, we will return that error
  786. here.
  787. --***************************************************************************/
  788. NTSTATUS
  789. SrpExpandPathOfFileName (
  790. IN PSR_DEVICE_EXTENSION pExtension,
  791. IN OUT PSRP_NAME_CONTROL pNameCtrl,
  792. OUT PBOOLEAN pReasonableErrorForUnOpenedName
  793. )
  794. {
  795. NTSTATUS status;
  796. OBJECT_ATTRIBUTES ObjectAttributes;
  797. IO_STATUS_BLOCK IoStatusBlock;
  798. SRP_NAME_CONTROL localNameCtrl;
  799. ULONG TokenLength = 0;
  800. PWSTR pToken;
  801. PWCHAR pOrigBuffer;
  802. HANDLE FileHandle = NULL;
  803. PFILE_OBJECT pFileObject = NULL;
  804. PAGED_CODE();
  805. //
  806. // Initialize state
  807. //
  808. SrpInitNameControl( &localNameCtrl );
  809. //
  810. // Throughout this function, if this name contains a stream component,
  811. // we want to manipulate the name as if the stream was part of the
  812. // file name. So add this amount to the pNameCtrl->Name.Length for
  813. // now. At the end of this routine, we will decrement
  814. // pNameCtrl->Name.Length appropriately.
  815. //
  816. ASSERT( IS_VALID_SR_STREAM_STRING( &pNameCtrl->Name, pNameCtrl->StreamNameLength) );
  817. pNameCtrl->Name.Length += pNameCtrl->StreamNameLength;
  818. ASSERT( pNameCtrl->Name.Length <= pNameCtrl->Name.MaximumLength );
  819. //
  820. // find the filename part in the full path
  821. //
  822. status = SrFindCharReverse( pNameCtrl->Name.Buffer,
  823. pNameCtrl->Name.Length,
  824. L'\\',
  825. &pToken,
  826. &TokenLength );
  827. if (!NT_SUCCESS(status))
  828. {
  829. goto Cleanup;
  830. }
  831. //
  832. // The token pointer points to the last '\' on the original file and the
  833. // length includes that '\'. Adjust the token pointer and length to not
  834. // include it. Note that it is possible for a directory name to get
  835. // down this path if the user tries to open a directory for overwrite or
  836. // supercede. This open will fail, but we will try to lookup the name
  837. // anyway.
  838. //
  839. ASSERT(*pToken == L'\\');
  840. ASSERT(TokenLength >= sizeof(WCHAR));
  841. pToken++;
  842. TokenLength -= sizeof(WCHAR);
  843. //
  844. // Take the filename part out of the original name.
  845. // NOTE: This does not take the '\' out of the name. If we do and this
  846. // is the root directory of the volume, the ZwCreateFile will
  847. // result in a volume open instead of an open on the root directory
  848. // of the volume.
  849. // NOTE: We specifically are sending this command to the TOP
  850. // of the filter stack chain because we want to resolve
  851. // the mount points. We need the name with all mount points
  852. // resolved so that we log the correct name in the change.log.
  853. //
  854. ASSERT(pNameCtrl->Name.Length > TokenLength);
  855. pNameCtrl->Name.Length -= (USHORT)TokenLength;
  856. InitializeObjectAttributes( &ObjectAttributes,
  857. &pNameCtrl->Name,
  858. OBJ_KERNEL_HANDLE,
  859. NULL,
  860. NULL );
  861. status = SrIoCreateFile( &FileHandle,
  862. SYNCHRONIZE,
  863. &ObjectAttributes,
  864. &IoStatusBlock,
  865. NULL,
  866. FILE_ATTRIBUTE_NORMAL,
  867. FILE_SHARE_READ | FILE_SHARE_WRITE,
  868. FILE_OPEN, // OPEN_EXISTING
  869. FILE_SYNCHRONOUS_IO_NONALERT
  870. | FILE_OPEN_FOR_BACKUP_INTENT,
  871. NULL,
  872. 0, // EaLength
  873. IO_IGNORE_SHARE_ACCESS_CHECK,
  874. NULL ); // go to TOP of filter stack
  875. //
  876. // not there? that's ok, the rename will fail then.
  877. //
  878. if (!NT_SUCCESS_NO_DBGBREAK(status))
  879. {
  880. //
  881. // It is resonable for pre-create to get an error here
  882. //
  883. *pReasonableErrorForUnOpenedName = TRUE;
  884. goto Cleanup;
  885. }
  886. //
  887. // reference the file object
  888. //
  889. status = ObReferenceObjectByHandle( FileHandle,
  890. 0,
  891. *IoFileObjectType,
  892. KernelMode,
  893. (PVOID *) &pFileObject,
  894. NULL );
  895. if (!NT_SUCCESS(status))
  896. {
  897. goto Cleanup;
  898. }
  899. //
  900. // Now we need to make sure that we are still on the same volume. The
  901. // above open will have resolved any mount points and that could have
  902. // taken us to another volume in the system.
  903. //
  904. if (IoGetRelatedDeviceObject( pFileObject ) !=
  905. IoGetAttachedDevice( pExtension->pDeviceObject )) {
  906. status = STATUS_NOT_SAME_DEVICE;
  907. *pReasonableErrorForUnOpenedName = TRUE;
  908. goto Cleanup;
  909. }
  910. //
  911. // Get the name of the parent directory, this will handle
  912. // resolving mount locations
  913. //
  914. status = SrpGetFileName( pExtension,
  915. pFileObject,
  916. &localNameCtrl );
  917. if (!NT_SUCCESS(status))
  918. {
  919. goto Cleanup;
  920. }
  921. //
  922. // Make sure there is a slash on the end of the new string (since our
  923. // source string always has a slash) before we see if they are the same
  924. //
  925. ASSERT(localNameCtrl.Name.Length > 0);
  926. if (localNameCtrl.Name.Buffer[(localNameCtrl.Name.Length/sizeof(WCHAR))-1] != L'\\')
  927. {
  928. status = SrpNameCtrlBufferCheck( &localNameCtrl,
  929. localNameCtrl.Name.Length+sizeof(WCHAR) );
  930. if (!NT_SUCCESS( status ))
  931. {
  932. goto Cleanup;
  933. }
  934. localNameCtrl.Name.Buffer[(localNameCtrl.Name.Length/sizeof(WCHAR))] = L'\\';
  935. localNameCtrl.Name.Length += sizeof(WCHAR);
  936. }
  937. //
  938. // See if the directory name is different. If not just return now
  939. //
  940. if (RtlCompareUnicodeString( &pNameCtrl->Name,
  941. &localNameCtrl.Name,
  942. TRUE ) == 0)
  943. {
  944. goto Cleanup;
  945. }
  946. //
  947. // Worst case the new name is longer so make sure our buffer is big
  948. // enough. If the new buffer is smaller then we know we won't need
  949. // to allocate more name.
  950. //
  951. status = SrpNameCtrlBufferCheckKeepOldBuffer( pNameCtrl,
  952. localNameCtrl.Name.Length + TokenLength,
  953. &pOrigBuffer );
  954. if (!NT_SUCCESS( status ))
  955. {
  956. goto Cleanup;
  957. }
  958. //
  959. // The name did change. Shuffle the file name left or right based on
  960. // the new estimated length of the path. Note that we need to do the
  961. // move first because pToken is a pointer into this string where the
  962. // filename used to be.
  963. //
  964. RtlMoveMemory( &pNameCtrl->Name.Buffer[localNameCtrl.Name.Length/sizeof(WCHAR)],
  965. pToken,
  966. TokenLength );
  967. //
  968. // We may have had to allocate a new buffer for this new name. If there
  969. // happened to be an old allocated buffer (the system is deisgned so this
  970. // should never happen) then we had the SrNameCtrlBufferCheckKeepOldBuffer
  971. // macro return us the old buffer because pToken still pointed into it.
  972. // We now need to free that buffer.
  973. //
  974. if (pOrigBuffer)
  975. {
  976. ExFreePool(pOrigBuffer);
  977. NULLPTR(pToken);
  978. }
  979. //
  980. // Copy the path portion of the name and set the length
  981. //
  982. RtlCopyMemory( pNameCtrl->Name.Buffer,
  983. localNameCtrl.Name.Buffer,
  984. localNameCtrl.Name.Length );
  985. pNameCtrl->Name.Length = localNameCtrl.Name.Length /*+
  986. (USHORT)TokenLength*/; // token length is now added in cleanup
  987. //
  988. // Handle cleanup
  989. //
  990. Cleanup:
  991. //
  992. // Always restore the name length back to its original size -- adjust
  993. // for TokenLength and StreamNameLength;
  994. //
  995. pNameCtrl->Name.Length += (USHORT)TokenLength;
  996. pNameCtrl->Name.Length -= pNameCtrl->StreamNameLength;
  997. if (pFileObject != NULL)
  998. {
  999. ObDereferenceObject(pFileObject);
  1000. NULLPTR(pFileObject);
  1001. }
  1002. if (FileHandle != NULL)
  1003. {
  1004. ZwClose(FileHandle);
  1005. NULLPTR(FileHandle);
  1006. }
  1007. SrpCleanupNameControl( &localNameCtrl );
  1008. #if DBG
  1009. if ((STATUS_MOUNT_POINT_NOT_RESOLVED == status) ||
  1010. (STATUS_OBJECT_PATH_NOT_FOUND == status) ||
  1011. (STATUS_OBJECT_NAME_NOT_FOUND == status) ||
  1012. (STATUS_OBJECT_NAME_INVALID == status) ||
  1013. (STATUS_REPARSE_POINT_NOT_RESOLVED == status) ||
  1014. (STATUS_NOT_SAME_DEVICE == status))
  1015. {
  1016. return status;
  1017. }
  1018. #endif
  1019. RETURN(status);
  1020. }
  1021. VOID
  1022. SrpRemoveExtraDataFromStream (
  1023. PUNICODE_STRING pStreamComponent,
  1024. PUSHORT AmountToRemove
  1025. )
  1026. {
  1027. UNICODE_STRING dataName;
  1028. UNICODE_STRING endOfName;
  1029. ASSERT( pStreamComponent != NULL );
  1030. ASSERT( AmountToRemove != NULL );
  1031. *AmountToRemove = 0;
  1032. //
  1033. // Determine if we have an extra ":$DATA" to remove from the stream name.
  1034. //
  1035. RtlInitUnicodeString( &dataName, L":$DATA" );
  1036. if (pStreamComponent->Length >= dataName.Length)
  1037. {
  1038. endOfName.Buffer = &(pStreamComponent->Buffer[
  1039. (pStreamComponent->Length - dataName.Length) /
  1040. sizeof(WCHAR) ]);
  1041. endOfName.Length = dataName.Length;
  1042. endOfName.MaximumLength = dataName.Length;
  1043. //
  1044. // If the end of the stream name matches ":$DATA" strip it off
  1045. //
  1046. if (RtlEqualUnicodeString( &dataName, &endOfName, TRUE))
  1047. {
  1048. USHORT endOfStream;
  1049. *AmountToRemove += dataName.Length;
  1050. //
  1051. // We may still have one trailing ':' since
  1052. // filename.txt::$DATA is a valid way to open the default
  1053. // stream of a file.
  1054. //
  1055. if ((pStreamComponent->Length + dataName.Length) > 0)
  1056. {
  1057. endOfStream = ((pStreamComponent->Length - dataName.Length)/sizeof(WCHAR))-1;
  1058. if (pStreamComponent->Buffer[endOfStream] == L':')
  1059. {
  1060. *AmountToRemove += sizeof(L':');
  1061. }
  1062. }
  1063. }
  1064. }
  1065. }
  1066. /***************************************************************************++
  1067. Routine Description:
  1068. This will look to see if the given file name has a stream name on it.
  1069. If so it will remove it from the string.
  1070. Arguments:
  1071. pNameControl - a structure used to manage the name of the file
  1072. Return Value:
  1073. None
  1074. --***************************************************************************/
  1075. VOID
  1076. SrpRemoveStreamName (
  1077. IN OUT PSRP_NAME_CONTROL pNameCtrl
  1078. )
  1079. {
  1080. INT i;
  1081. INT countOfColons = 0;
  1082. PAGED_CODE();
  1083. //
  1084. // search for a potential stream name to strip.
  1085. //
  1086. ASSERT(pNameCtrl->Name.Length > 0);
  1087. for ( i = (pNameCtrl->Name.Length / sizeof(WCHAR)) - 1;
  1088. i >= 0;
  1089. i -= 1 )
  1090. {
  1091. if (pNameCtrl->Name.Buffer[i] == L'\\')
  1092. {
  1093. //
  1094. // hit the end of the file part. stop looking
  1095. //
  1096. break;
  1097. }
  1098. else if (pNameCtrl->Name.Buffer[i] == L':')
  1099. {
  1100. USHORT delta;
  1101. //
  1102. // Track the number of colons we see so that we know
  1103. // what we need to try to strip at the end.
  1104. //
  1105. countOfColons ++;
  1106. //
  1107. // strip the stream name (save how much we stripped)
  1108. //
  1109. delta = pNameCtrl->Name.Length - (USHORT)(i * sizeof(WCHAR));
  1110. pNameCtrl->StreamNameLength += delta;
  1111. pNameCtrl->Name.Length -= delta;
  1112. }
  1113. }
  1114. if (countOfColons == 2)
  1115. {
  1116. UNICODE_STRING streamName;
  1117. USHORT amountToRemove = 0;
  1118. //
  1119. // We have an extra ":$DATA" to remove from the stream name.
  1120. //
  1121. streamName.Length = streamName.MaximumLength = pNameCtrl->StreamNameLength;
  1122. streamName.Buffer = pNameCtrl->Name.Buffer + (pNameCtrl->Name.Length/sizeof(WCHAR));
  1123. SrpRemoveExtraDataFromStream( &streamName,
  1124. &amountToRemove );
  1125. pNameCtrl->StreamNameLength -= amountToRemove;
  1126. }
  1127. }
  1128. /***************************************************************************++
  1129. Routine Description:
  1130. This routine will construct a full nt path name for the target of a
  1131. rename or link operation. The name will be completely normalized for SR's
  1132. lookup and logging purposes.
  1133. Arguments:
  1134. pExtension - SR's device extension for the volume on which this file resides.
  1135. RootDirectory - Handle to the root directory for which this rename/link is
  1136. relative
  1137. pFileName - If this is rename\link that is relative to the original file,
  1138. this is the target name.
  1139. FileNameLength - The length in bytes of pFileName.
  1140. pOriginalFileContext - The file context for the file that is being renamed
  1141. or linked to.
  1142. pOriginalFileObject - The file object that is being renamed or linked to.
  1143. ppNewName - The normalized name that this function generates. The caller
  1144. is responsible for freeing the memory allocated.
  1145. pReasonableErrorForUnOpenedName - Set to TRUE if we hit an error during
  1146. the name normalization path that is reasonable since this operation
  1147. has not yet been validated by the file system.
  1148. Return Value:
  1149. NTSTATUS - Completion status.
  1150. --***************************************************************************/
  1151. NTSTATUS
  1152. SrpExpandDestPath (
  1153. IN PSR_DEVICE_EXTENSION pExtension,
  1154. IN HANDLE RootDirectory,
  1155. IN ULONG FileNameLength,
  1156. IN PWSTR pFileName,
  1157. IN PSR_STREAM_CONTEXT pOriginalFileContext,
  1158. IN PFILE_OBJECT pOriginalFileObject,
  1159. OUT PUNICODE_STRING *ppNewName,
  1160. OUT PUSHORT pNewNameStreamLength,
  1161. OUT PBOOLEAN pReasonableErrorForUnOpenedName
  1162. )
  1163. {
  1164. NTSTATUS status;
  1165. UNICODE_STRING NewName;
  1166. ULONG TokenLength;
  1167. PWSTR pToken;
  1168. PFILE_OBJECT pDirectory = NULL;
  1169. SRP_NAME_CONTROL newNameCtrl;
  1170. ULONG fullNameLength;
  1171. UNICODE_STRING OrigName;
  1172. SRP_NAME_CONTROL originalNameCtrl;
  1173. BOOLEAN freeOriginalNameCtrl = FALSE;
  1174. PAGED_CODE();
  1175. ASSERT(IS_VALID_SR_DEVICE_EXTENSION(pExtension));
  1176. ASSERT(pFileName != NULL);
  1177. ASSERT(pOriginalFileContext != NULL);
  1178. ASSERT(ppNewName != NULL);
  1179. ASSERT(pNewNameStreamLength != NULL);
  1180. //
  1181. // Initialize state
  1182. //
  1183. *ppNewName = NULL;
  1184. *pNewNameStreamLength = 0;
  1185. SrpInitNameControl( &newNameCtrl );
  1186. //
  1187. // fill in the new name to a UNICODE_STRING
  1188. //
  1189. NewName.Length = (USHORT)FileNameLength;
  1190. NewName.MaximumLength = (USHORT)FileNameLength;
  1191. NewName.Buffer = pFileName;
  1192. //
  1193. // construct a fully qualified name we can use to open the parent
  1194. // dir
  1195. //
  1196. //
  1197. // is this a directory relative op?
  1198. //
  1199. if (RootDirectory != NULL)
  1200. {
  1201. //
  1202. // reference the directory file object
  1203. //
  1204. status = ObReferenceObjectByHandle( RootDirectory,
  1205. 0,
  1206. *IoFileObjectType,
  1207. KernelMode,
  1208. (PVOID *) &pDirectory,
  1209. NULL );
  1210. if (!NT_SUCCESS(status))
  1211. {
  1212. goto Cleanup;
  1213. }
  1214. //
  1215. // get path name for the directory
  1216. //
  1217. status = SrpGetFileName( pExtension,
  1218. pDirectory,
  1219. &newNameCtrl );
  1220. if (!NT_SUCCESS(status))
  1221. {
  1222. goto Cleanup;
  1223. }
  1224. fullNameLength = newNameCtrl.Name.Length +
  1225. NewName.Length +
  1226. sizeof(WCHAR); //space for seperator
  1227. status = SrpNameCtrlBufferCheck( &newNameCtrl, fullNameLength );
  1228. if (!NT_SUCCESS( status )) {
  1229. goto Cleanup;
  1230. }
  1231. //
  1232. // Slap on the relative part now
  1233. //
  1234. // We may need to add a slash separator if it is missing.
  1235. //
  1236. if ((NewName.Buffer[0] != L'\\') &&
  1237. (NewName.Buffer[0] != L':') &&
  1238. (newNameCtrl.Name.Buffer[(newNameCtrl.Name.Length/sizeof(WCHAR))-1] != L'\\'))
  1239. {
  1240. newNameCtrl.Name.Buffer[newNameCtrl.Name.Length/sizeof(WCHAR)] = L'\\';
  1241. newNameCtrl.Name.Length += sizeof(WCHAR);
  1242. }
  1243. RtlAppendUnicodeStringToString( &newNameCtrl.Name,
  1244. &NewName );
  1245. //
  1246. // done with the object now
  1247. //
  1248. ObDereferenceObject(pDirectory);
  1249. pDirectory = NULL;
  1250. }
  1251. //
  1252. // is this a same directory rename\link creation ?
  1253. //
  1254. else if (NewName.Buffer[0] != L'\\')
  1255. {
  1256. PUNICODE_STRING pOriginalName;
  1257. ASSERT(RootDirectory == NULL);
  1258. if (!FlagOn( pOriginalFileContext->Flags, CTXFL_IsInteresting ))
  1259. {
  1260. //
  1261. // We don't have a name for this file, so generate the fully
  1262. // qualified name.
  1263. //
  1264. SrpInitNameControl( &originalNameCtrl );
  1265. freeOriginalNameCtrl = TRUE;
  1266. status = SrpGetFileName( pExtension,
  1267. pOriginalFileObject,
  1268. &originalNameCtrl );
  1269. if (!NT_SUCCESS( status )) {
  1270. goto Cleanup;
  1271. }
  1272. pOriginalName = &(originalNameCtrl.Name);
  1273. }
  1274. else
  1275. {
  1276. //
  1277. // This file is interesting, so we have a name in the context.
  1278. //
  1279. pOriginalName = &(pOriginalFileContext->FileName);
  1280. }
  1281. //
  1282. // We are doing either a same directory rename/link or renaming a
  1283. // stream of this file. We can figure out which by looking for a ':'
  1284. // as the first character in the NewName. In either case,
  1285. // NewName should have no '\'s in it.
  1286. status = SrFindCharReverse( NewName.Buffer,
  1287. NewName.Length,
  1288. L'\\',
  1289. &pToken,
  1290. &TokenLength );
  1291. if (status != STATUS_OBJECT_NAME_NOT_FOUND)
  1292. {
  1293. *pReasonableErrorForUnOpenedName = TRUE;
  1294. status = STATUS_OBJECT_NAME_INVALID;
  1295. goto Cleanup;
  1296. }
  1297. if (NewName.Buffer[0] == ':')
  1298. {
  1299. USHORT CurrentFileNameLength;
  1300. USHORT AmountToRemove = 0;
  1301. //
  1302. // We are renaming a stream on this file. This is the easy
  1303. // case because we can build up the new name without any more
  1304. // parsing of the original name.
  1305. //
  1306. //
  1307. // NewName currently contains the new stream name component. It
  1308. // may have the extra $DATA at the end of the stream name
  1309. // and we want to strip that part off.
  1310. //
  1311. SrpRemoveExtraDataFromStream( &NewName,
  1312. &AmountToRemove );
  1313. NewName.Length -= AmountToRemove;
  1314. //
  1315. // Calculate the full length of the name with stream and upgrade
  1316. // our buffer if we need to.
  1317. //
  1318. fullNameLength = pOriginalName->Length + NewName.Length;
  1319. status = SrpNameCtrlBufferCheck( &newNameCtrl, fullNameLength );
  1320. if (!NT_SUCCESS( status ))
  1321. {
  1322. goto Cleanup;
  1323. }
  1324. //
  1325. // insert the orignal file name into the string
  1326. //
  1327. RtlCopyUnicodeString( &newNameCtrl.Name,
  1328. pOriginalName );
  1329. //
  1330. // Append the stream name component on, but remember the current
  1331. // length of the file name, since will will restore that after
  1332. // the append operation to maintain our file name format.
  1333. //
  1334. CurrentFileNameLength = newNameCtrl.Name.Length;
  1335. RtlAppendUnicodeStringToString( &newNameCtrl.Name,
  1336. &NewName );
  1337. newNameCtrl.Name.Length = CurrentFileNameLength;
  1338. newNameCtrl.StreamNameLength = NewName.Length;
  1339. }
  1340. else
  1341. {
  1342. //
  1343. // get the length of the filename portion of the source
  1344. // path
  1345. //
  1346. status = SrFindCharReverse( pOriginalName->Buffer,
  1347. pOriginalName->Length,
  1348. L'\\',
  1349. &pToken,
  1350. &TokenLength );
  1351. if (!NT_SUCCESS(status))
  1352. {
  1353. goto Cleanup;
  1354. }
  1355. //
  1356. // Leave the prefix character ('\') on the path
  1357. //
  1358. TokenLength -= sizeof(WCHAR);
  1359. ASSERT(pOriginalName->Length > TokenLength);
  1360. OrigName.Length = (USHORT) (pOriginalName->Length - TokenLength);
  1361. OrigName.MaximumLength = OrigName.Length;
  1362. OrigName.Buffer = pOriginalName->Buffer;
  1363. //
  1364. // Calculate the full length of the name and upgrade our
  1365. // buffer if we need to.
  1366. //
  1367. fullNameLength = OrigName.Length + NewName.Length;
  1368. status = SrpNameCtrlBufferCheck( &newNameCtrl, fullNameLength );
  1369. if (!NT_SUCCESS( status ))
  1370. {
  1371. goto Cleanup;
  1372. }
  1373. //
  1374. // insert the orignal file name into the string
  1375. //
  1376. RtlCopyUnicodeString( &newNameCtrl.Name,
  1377. &OrigName );
  1378. //
  1379. // Append the new name on
  1380. //
  1381. RtlAppendUnicodeStringToString( &newNameCtrl.Name,
  1382. &NewName );
  1383. }
  1384. }
  1385. else
  1386. {
  1387. ASSERT(NewName.Buffer[0] == L'\\');
  1388. //
  1389. // it's already fully quailifed, simply allocate a buffer and
  1390. // copy it in so we can post process expand mount points and
  1391. // shortnames
  1392. //
  1393. status = SrpNameCtrlBufferCheck( &newNameCtrl, NewName.Length );
  1394. if (!NT_SUCCESS( status ))
  1395. {
  1396. goto Cleanup;
  1397. }
  1398. //
  1399. // Copy the name into the buffer
  1400. //
  1401. RtlCopyUnicodeString( &newNameCtrl.Name,
  1402. &NewName );
  1403. }
  1404. //
  1405. // NULL terminate the name
  1406. //
  1407. ASSERT(newNameCtrl.Name.Length > 0);
  1408. //
  1409. // Since this may be a raw path name from the user, try and expand
  1410. // the path so that we will eliminate the mount points. After this
  1411. // call, the name will be normalized to
  1412. // \Device\HarddiskVolume1\[fullpath]
  1413. //
  1414. status = SrpExpandPathOfFileName( pExtension,
  1415. &newNameCtrl,
  1416. pReasonableErrorForUnOpenedName );
  1417. if (!NT_SUCCESS_NO_DBGBREAK(status))
  1418. {
  1419. goto Cleanup;
  1420. }
  1421. //
  1422. // Now expand any shortnames in the path
  1423. //
  1424. status = SrpExpandShortNames( pExtension,
  1425. &newNameCtrl,
  1426. FALSE );
  1427. if (!NT_SUCCESS_NO_DBGBREAK(status))
  1428. {
  1429. goto Cleanup;
  1430. }
  1431. //
  1432. // Allocate a string buffer to return and copy the name to it
  1433. //
  1434. *ppNewName = ExAllocatePoolWithTag( PagedPool,
  1435. sizeof( UNICODE_STRING ) +
  1436. newNameCtrl.Name.Length +
  1437. newNameCtrl.StreamNameLength,
  1438. SR_FILENAME_BUFFER_TAG );
  1439. if (NULL == *ppNewName)
  1440. {
  1441. status = STATUS_INSUFFICIENT_RESOURCES;
  1442. goto Cleanup;
  1443. }
  1444. (*ppNewName)->MaximumLength = newNameCtrl.Name.Length + newNameCtrl.StreamNameLength;
  1445. (*ppNewName)->Buffer = (PWCHAR)((PWCHAR)((*ppNewName) + 1));
  1446. //
  1447. // Since we need to copy the steam information also, do the copy
  1448. // ourselves here instead of relying on the Unicode function.
  1449. //
  1450. RtlCopyMemory( (*ppNewName)->Buffer,
  1451. newNameCtrl.Name.Buffer,
  1452. (*ppNewName)->MaximumLength );
  1453. (*ppNewName)->Length = newNameCtrl.Name.Length;
  1454. *pNewNameStreamLength = newNameCtrl.StreamNameLength;
  1455. //
  1456. // Handle cleanup of state
  1457. //
  1458. Cleanup:
  1459. if (pDirectory != NULL)
  1460. {
  1461. ObDereferenceObject(pDirectory);
  1462. NULLPTR(pDirectory);
  1463. }
  1464. SrpCleanupNameControl( &newNameCtrl );
  1465. if (freeOriginalNameCtrl)
  1466. {
  1467. SrpCleanupNameControl( &originalNameCtrl );
  1468. }
  1469. #if DBG
  1470. if ((STATUS_MOUNT_POINT_NOT_RESOLVED == status) ||
  1471. (STATUS_OBJECT_PATH_NOT_FOUND == status) ||
  1472. (STATUS_OBJECT_NAME_NOT_FOUND == status) ||
  1473. (STATUS_OBJECT_NAME_INVALID == status) ||
  1474. (STATUS_REPARSE_POINT_NOT_RESOLVED == status) ||
  1475. (STATUS_NOT_SAME_DEVICE == status))
  1476. {
  1477. return status;
  1478. }
  1479. #endif
  1480. RETURN(status);
  1481. }
  1482. /***************************************************************************++
  1483. Routine Description:
  1484. This will initialize the name control structure
  1485. Arguments:
  1486. Return Value:
  1487. None
  1488. --***************************************************************************/
  1489. VOID
  1490. SrpInitNameControl (
  1491. IN PSRP_NAME_CONTROL pNameCtrl
  1492. )
  1493. {
  1494. PAGED_CODE();
  1495. pNameCtrl->AllocatedBuffer = NULL;
  1496. pNameCtrl->StreamNameLength = 0;
  1497. pNameCtrl->BufferSize = sizeof(pNameCtrl->SmallBuffer);
  1498. RtlInitEmptyUnicodeString( &pNameCtrl->Name,
  1499. (PWCHAR)pNameCtrl->SmallBuffer,
  1500. pNameCtrl->BufferSize );
  1501. //pNameCtrl->Name.Buffer[0] = UNICODE_NULL;
  1502. }
  1503. /***************************************************************************++
  1504. Routine Description:
  1505. This will cleanup the name control structure
  1506. Arguments:
  1507. Return Value:
  1508. None
  1509. --***************************************************************************/
  1510. VOID
  1511. SrpCleanupNameControl (
  1512. IN PSRP_NAME_CONTROL pNameCtrl
  1513. )
  1514. {
  1515. PAGED_CODE();
  1516. if (NULL != pNameCtrl->AllocatedBuffer)
  1517. {
  1518. ExFreePool( pNameCtrl->AllocatedBuffer );
  1519. pNameCtrl->AllocatedBuffer = NULL;
  1520. }
  1521. }
  1522. /***************************************************************************++
  1523. Routine Description:
  1524. This routine will allocate a new larger name buffer and put it into the
  1525. NameControl structure. If there is already an allocated buffer it will
  1526. be freed. It will also copy any name information from the old buffer
  1527. into the new buffer.
  1528. Arguments:
  1529. pNameCtrl - the name control we need a bigger buffer for
  1530. newSize - size of the new buffer
  1531. retOrignalBuffer - if defined, receives the buffer that we were
  1532. going to free. if NULL was returned no buffer
  1533. needed to be freed.
  1534. WARNING: if this parameter is defined and a
  1535. non-null value is returned then the
  1536. call MUST free this memory else the
  1537. memory will be lost.
  1538. Return Value:
  1539. None
  1540. --***************************************************************************/
  1541. NTSTATUS
  1542. SrpReallocNameControl (
  1543. IN PSRP_NAME_CONTROL pNameCtrl,
  1544. ULONG newSize,
  1545. PWCHAR *retOriginalBuffer OPTIONAL
  1546. )
  1547. {
  1548. PCHAR newBuffer;
  1549. PAGED_CODE();
  1550. ASSERT(newSize > pNameCtrl->BufferSize);
  1551. //
  1552. // Flag no buffer to return yet
  1553. //
  1554. if (retOriginalBuffer)
  1555. {
  1556. *retOriginalBuffer = NULL;
  1557. }
  1558. //
  1559. // Allocate the new buffer
  1560. //
  1561. newBuffer = ExAllocatePoolWithTag( PagedPool,
  1562. newSize,
  1563. SR_FILENAME_BUFFER_TAG );
  1564. if (NULL == newBuffer)
  1565. {
  1566. return STATUS_INSUFFICIENT_RESOURCES;
  1567. }
  1568. SrTrace( CONTEXT_LOG, ("Sr!SrpReallocNameControl: Realloc: (%p) oldSz=%05x newSz=%05x \"%.*S\"\n",
  1569. pNameCtrl,
  1570. pNameCtrl->BufferSize,
  1571. newSize,
  1572. (pNameCtrl->Name.Length+
  1573. pNameCtrl->StreamNameLength)/sizeof(WCHAR),
  1574. pNameCtrl->Name.Buffer));
  1575. //
  1576. // Copy data from old buffer if there is any, including any stream
  1577. // name component.
  1578. //
  1579. if ((pNameCtrl->Name.Length + pNameCtrl->StreamNameLength) > 0)
  1580. {
  1581. ASSERT(newSize > (USHORT)(pNameCtrl->Name.Length + pNameCtrl->StreamNameLength));
  1582. RtlCopyMemory( newBuffer,
  1583. pNameCtrl->Name.Buffer,
  1584. (pNameCtrl->Name.Length + pNameCtrl->StreamNameLength) );
  1585. }
  1586. //
  1587. // If we had an old buffer free it if the caller doesn't want
  1588. // it passed back to him. This is done because there are
  1589. // cases where the caller has a pointer into the old buffer so
  1590. // it can't be freed yet. The caller must free this memory.
  1591. //
  1592. if (NULL != pNameCtrl->AllocatedBuffer)
  1593. {
  1594. if (retOriginalBuffer)
  1595. {
  1596. *retOriginalBuffer = (PWCHAR)pNameCtrl->AllocatedBuffer;
  1597. }
  1598. else
  1599. {
  1600. ExFreePool(pNameCtrl->AllocatedBuffer);
  1601. }
  1602. }
  1603. //
  1604. // Set the new buffer into the name control
  1605. //
  1606. pNameCtrl->AllocatedBuffer = newBuffer;
  1607. pNameCtrl->BufferSize = newSize;
  1608. pNameCtrl->Name.Buffer = (PWCHAR)newBuffer;
  1609. pNameCtrl->Name.MaximumLength = (USHORT)newSize;
  1610. return STATUS_SUCCESS;
  1611. }
  1612. /***************************************************************************++
  1613. Routine Description:
  1614. This routine does the following things:
  1615. - Get the FULL path name of the given file object
  1616. - Will expand any short names in the path to long names
  1617. - Will remove any stream names.
  1618. Arguments:
  1619. Return Value:
  1620. NTSTATUS - Completion status.
  1621. --***************************************************************************/
  1622. NTSTATUS
  1623. SrpExpandFileName (
  1624. IN PSR_DEVICE_EXTENSION pExtension,
  1625. IN PFILE_OBJECT pFileObject,
  1626. IN ULONG EventFlags,
  1627. IN OUT PSRP_NAME_CONTROL pNameCtrl,
  1628. OUT PBOOLEAN pReasonableErrorForUnOpenedName
  1629. )
  1630. {
  1631. NTSTATUS status;
  1632. PAGED_CODE();
  1633. //
  1634. // If we are in pre-create, use the name in the FILE_OBJECT. Also if
  1635. // there is no related file object and the name starts with a slash,
  1636. // just use the name in the FILE_OBJECT.
  1637. //
  1638. if (FlagOn( EventFlags, SrEventOpenById ))
  1639. {
  1640. status = SrpGetFileNameOpenById( pExtension,
  1641. pFileObject,
  1642. pNameCtrl,
  1643. pReasonableErrorForUnOpenedName);
  1644. }
  1645. else if (FlagOn( EventFlags, SrEventInPreCreate ))
  1646. {
  1647. status = SrpGetFileNameFromFileObject( pExtension,
  1648. pFileObject,
  1649. pNameCtrl,
  1650. pReasonableErrorForUnOpenedName );
  1651. }
  1652. else
  1653. {
  1654. //
  1655. // Ask the file system for the name
  1656. //
  1657. status = SrpGetFileName( pExtension,
  1658. pFileObject,
  1659. pNameCtrl );
  1660. }
  1661. if (!NT_SUCCESS_NO_DBGBREAK( status ))
  1662. {
  1663. return status;
  1664. }
  1665. //
  1666. // Remove the stream name from the file name (if defined)
  1667. //
  1668. SrpRemoveStreamName( pNameCtrl );
  1669. //
  1670. // Expand any short names in the filename
  1671. //
  1672. status = SrpExpandShortNames( pExtension,
  1673. pNameCtrl,
  1674. TRUE );
  1675. RETURN(status);
  1676. }
  1677. /***************************************************************************++
  1678. Routine Description:
  1679. This will see if we care about this file. During this process it looks
  1680. up the full file name and returns it.
  1681. Arguments:
  1682. pExtension - the extension for the device this file is on
  1683. pFileObject - the fileobject being handled
  1684. IsDirectory - TRUE if this is a directory, else FALSE
  1685. EventFlags - The flags portion of the current event. This is used to
  1686. determine if we are in the pre-create path or if this file is being
  1687. opened by file id.
  1688. pNameControl - a structure used to manage the name of the file
  1689. pIsInteresting - returns if this file should be monitored
  1690. pReasonableErrorForUnOpenedName -
  1691. Return Value:
  1692. NTSTATUS - Completion status
  1693. --***************************************************************************/
  1694. NTSTATUS
  1695. SrIsFileEligible (
  1696. IN PSR_DEVICE_EXTENSION pExtension,
  1697. IN PFILE_OBJECT pFileObject,
  1698. IN BOOLEAN IsDirectory,
  1699. IN SR_EVENT_TYPE EventFlags,
  1700. IN OUT PSRP_NAME_CONTROL pNameCtrl,
  1701. OUT PBOOLEAN pIsInteresting,
  1702. OUT PBOOLEAN pReasonableErrorForUnOpenedName
  1703. )
  1704. {
  1705. NTSTATUS status;
  1706. PAGED_CODE();
  1707. ASSERT(IS_VALID_SR_DEVICE_EXTENSION(pExtension));
  1708. ASSERT(IS_VALID_FILE_OBJECT(pFileObject));
  1709. //
  1710. // Assume the file is NOT interesting
  1711. //
  1712. *pIsInteresting = FALSE;
  1713. //
  1714. // is anyone monitoring this system at all?
  1715. //
  1716. if (!SR_LOGGING_ENABLED(pExtension))
  1717. {
  1718. return SR_STATUS_VOLUME_DISABLED;
  1719. }
  1720. //
  1721. // have we loaded the file config information
  1722. //
  1723. if (!_globals.BlobInfoLoaded)
  1724. {
  1725. status = SrReadBlobInfo();
  1726. if (!NT_SUCCESS_NO_DBGBREAK( status ))
  1727. {
  1728. ASSERT(!_globals.BlobInfoLoaded);
  1729. //
  1730. // We couldn't load the lookup blob for some reason, but
  1731. // we have already handled the error so mark that this is a
  1732. // resonable error, so just let the error propogate out.
  1733. //
  1734. *pReasonableErrorForUnOpenedName = TRUE;
  1735. return status;
  1736. }
  1737. ASSERT(_globals.BlobInfoLoaded);
  1738. }
  1739. //
  1740. // Always query the name
  1741. //
  1742. status = SrpExpandFileName( pExtension,
  1743. pFileObject,
  1744. EventFlags,
  1745. pNameCtrl,
  1746. pReasonableErrorForUnOpenedName );
  1747. if (!NT_SUCCESS_NO_DBGBREAK( status ))
  1748. {
  1749. return status;
  1750. }
  1751. //
  1752. // Check to see if this file name is longer than SR_MAX_PATH. If so,
  1753. // this file is not interesting.
  1754. //
  1755. if (!IS_FILENAME_VALID_LENGTH( pExtension,
  1756. &(pNameCtrl->Name),
  1757. pNameCtrl->StreamNameLength ))
  1758. {
  1759. *pIsInteresting = FALSE;
  1760. return STATUS_SUCCESS;
  1761. }
  1762. //
  1763. // is this is a file, check the extension for a match
  1764. //
  1765. if (!IsDirectory)
  1766. {
  1767. //
  1768. // does this extension match? do this first as we can do this
  1769. // pretty quick like
  1770. //
  1771. status = SrIsExtInteresting( &pNameCtrl->Name,
  1772. pIsInteresting );
  1773. if (!NT_SUCCESS( status ))
  1774. {
  1775. return status;
  1776. }
  1777. //
  1778. // Is this not interesting
  1779. //
  1780. if (!*pIsInteresting)
  1781. {
  1782. return status;
  1783. }
  1784. //
  1785. // Check to see if this file has a stream component. If so,
  1786. // we need to check to see if this is a named stream on a
  1787. // file or directory. We are only interested in streams on
  1788. // files.
  1789. //
  1790. if (pNameCtrl->StreamNameLength > 0)
  1791. {
  1792. status = SrIsFileStream( pExtension,
  1793. pNameCtrl,
  1794. pIsInteresting,
  1795. pReasonableErrorForUnOpenedName );
  1796. if (!NT_SUCCESS_NO_DBGBREAK( status ) || !*pIsInteresting)
  1797. {
  1798. return status;
  1799. }
  1800. }
  1801. }
  1802. //
  1803. // see if this is a file that we should monitor?
  1804. //
  1805. status = SrIsPathInteresting( &pNameCtrl->Name,
  1806. pExtension->pNtVolumeName,
  1807. IsDirectory,
  1808. pIsInteresting );
  1809. RETURN(status);
  1810. }
  1811. /***************************************************************************++
  1812. Routine Description:
  1813. This routine does a quick scan of the file object's name to see if it
  1814. contains the stream name delimiter ':'.
  1815. Note: This routine assumes that the name in the file object is valid,
  1816. therefore this routine should only be called from SrCreate.
  1817. Note2: We only need to rely on the name components in the
  1818. pFileObject->FileName field because for our purposes this is sufficient.
  1819. If this filed doesn't contain the ':' delimiter, we are either not
  1820. opening a stream or we are doing a self-relative open of a stream
  1821. Arguments:
  1822. pExtension - the SR extension the current volume
  1823. pFileObject - the current fileobject to be opened
  1824. pFileContext - if provided, we will get the name from the context
  1825. Return Value:
  1826. Returns TRUE if the file name contains a steam delimiter or FALSE otherwise.
  1827. --***************************************************************************/
  1828. BOOLEAN
  1829. SrFileNameContainsStream (
  1830. IN PSR_DEVICE_EXTENSION pExtension,
  1831. IN PFILE_OBJECT pFileObject,
  1832. IN PSR_STREAM_CONTEXT pFileContext OPTIONAL
  1833. )
  1834. {
  1835. PUNICODE_STRING pFileName;
  1836. NTSTATUS status;
  1837. PWCHAR pToken;
  1838. ULONG tokenLength;
  1839. ASSERT( IS_VALID_SR_DEVICE_EXTENSION( pExtension ) );
  1840. ASSERT( IS_VALID_FILE_OBJECT( pFileObject ) );
  1841. //
  1842. // If we've already cached the attributes of this volume, do a quick
  1843. // check to see if this FS supports named streams. If not, we don't need
  1844. // to do any more work here.
  1845. //
  1846. if (pExtension->CachedFsAttributes &&
  1847. !FlagOn( pExtension->FsAttributes, FILE_NAMED_STREAMS ))
  1848. {
  1849. return FALSE;
  1850. }
  1851. if (pFileContext != NULL)
  1852. {
  1853. //
  1854. // If we've got a pFileContext, it has all the stream information
  1855. // in it already, so just use that.
  1856. //
  1857. if (pFileContext->StreamNameLength == 0)
  1858. {
  1859. return FALSE;
  1860. }
  1861. else
  1862. {
  1863. return TRUE;
  1864. }
  1865. }
  1866. pFileName = &(pFileObject->FileName);
  1867. status = SrFindCharReverse( pFileName->Buffer,
  1868. pFileName->Length,
  1869. L':',
  1870. &pToken,
  1871. &tokenLength );
  1872. if (status == STATUS_OBJECT_NAME_NOT_FOUND)
  1873. {
  1874. //
  1875. // We didn't find a ':', therefore this doen't have a stream component
  1876. // in the name.
  1877. //
  1878. return FALSE;
  1879. }
  1880. else if (status == STATUS_SUCCESS)
  1881. {
  1882. //
  1883. // We found a ':', so there is a stream component in this name.
  1884. //
  1885. return TRUE;
  1886. }
  1887. else
  1888. {
  1889. //
  1890. // We should never reach this path.
  1891. //
  1892. ASSERT( FALSE );
  1893. }
  1894. return FALSE;
  1895. }
  1896. /***************************************************************************++
  1897. Routine Description:
  1898. This routine opens the file-only component of the file name (ignoring
  1899. any stream component) to see if the unnamed data stream for this file
  1900. already exists.
  1901. Note: This routine assumes that the name in the file object is valid,
  1902. therefore this routine should only be called from SrCreate.
  1903. Arguments:
  1904. pExtension - the SR extension the current volume
  1905. pFileObject - the current fileobject to be opened
  1906. pFileContext - if provided, we will get the name from the context
  1907. Return Value:
  1908. Returns TRUE if the file name contains a steam delimiter or FALSE otherwise.
  1909. --***************************************************************************/
  1910. BOOLEAN
  1911. SrFileAlreadyExists (
  1912. IN PSR_DEVICE_EXTENSION pExtension,
  1913. IN PFILE_OBJECT pFileObject,
  1914. IN PSR_STREAM_CONTEXT pFileContext OPTIONAL
  1915. )
  1916. {
  1917. SRP_NAME_CONTROL nameCtrl;
  1918. BOOLEAN cleanupNameCtrl = FALSE;
  1919. BOOLEAN reasonableError;
  1920. NTSTATUS status;
  1921. BOOLEAN unnamedStreamExists = FALSE;
  1922. OBJECT_ATTRIBUTES objectAttributes;
  1923. HANDLE fileHandle = NULL;
  1924. IO_STATUS_BLOCK ioStatus;
  1925. PUNICODE_STRING pFileName;
  1926. if (pFileContext == NULL)
  1927. {
  1928. SrpInitNameControl( &nameCtrl );
  1929. cleanupNameCtrl = TRUE;
  1930. status = SrpGetFileNameFromFileObject( pExtension,
  1931. pFileObject,
  1932. &nameCtrl,
  1933. &reasonableError );
  1934. if (!NT_SUCCESS_NO_DBGBREAK( status ))
  1935. {
  1936. goto SrFileAlreadyHasUnnamedStream_Exit;
  1937. }
  1938. //
  1939. // Remove the stream name from the file name (if defined)
  1940. //
  1941. SrpRemoveStreamName( &nameCtrl );
  1942. pFileName = &(nameCtrl.Name);
  1943. //
  1944. // The stream component just resolved down to the default data stream,
  1945. // go exit now without doing the open.
  1946. //
  1947. if (nameCtrl.StreamNameLength == 0)
  1948. {
  1949. goto SrFileAlreadyHasUnnamedStream_Exit;
  1950. }
  1951. }
  1952. else
  1953. {
  1954. pFileName = &(pFileContext->FileName);
  1955. //
  1956. // This name should have a stream component, that's the reason we are
  1957. // in this path.
  1958. //
  1959. ASSERT( pFileContext->StreamNameLength > 0 );
  1960. }
  1961. InitializeObjectAttributes( &objectAttributes,
  1962. pFileName,
  1963. OBJ_KERNEL_HANDLE,
  1964. NULL,
  1965. NULL );
  1966. status = SrIoCreateFile( &fileHandle,
  1967. FILE_READ_ATTRIBUTES,
  1968. &objectAttributes,
  1969. &ioStatus,
  1970. NULL,
  1971. FILE_ATTRIBUTE_NORMAL,
  1972. FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
  1973. FILE_OPEN,
  1974. 0,
  1975. NULL,
  1976. 0,
  1977. IO_IGNORE_SHARE_ACCESS_CHECK,
  1978. pExtension->pTargetDevice );
  1979. if (status == STATUS_OBJECT_NAME_NOT_FOUND)
  1980. {
  1981. //
  1982. // The unnamed data stream doesn't exist, so the creation of this
  1983. // stream is also going to created the unnamed data stream on this
  1984. // file.
  1985. //
  1986. unnamedStreamExists = FALSE;
  1987. }
  1988. else if (status == STATUS_SUCCESS)
  1989. {
  1990. //
  1991. // The unnamed data stream does exist, so the creation of this
  1992. // stream is just going to create a new stream on this file.
  1993. //
  1994. unnamedStreamExists = TRUE;
  1995. }
  1996. else if (status == STATUS_DELETE_PENDING)
  1997. {
  1998. //
  1999. // This file already exists but is about to be deleted.
  2000. //
  2001. unnamedStreamExists = TRUE;
  2002. }
  2003. else
  2004. {
  2005. CHECK_STATUS( status );
  2006. }
  2007. SrFileAlreadyHasUnnamedStream_Exit:
  2008. if (fileHandle != NULL)
  2009. {
  2010. ZwClose( fileHandle );
  2011. }
  2012. if (cleanupNameCtrl)
  2013. {
  2014. SrpCleanupNameControl( &nameCtrl );
  2015. }
  2016. return unnamedStreamExists;
  2017. }
  2018. /***************************************************************************++
  2019. Routine Description:
  2020. This routine determines if a name containing a stream component is a
  2021. named stream on a directory or on a file. To this this, this routine opens
  2022. the current file name ignoring any stream component in the name.
  2023. Arguments:
  2024. pExtension - the SR extension the current volume
  2025. pNameCtrl - the SRP_NAME_CTRL structure that has the complete name.
  2026. pIsFileStream - set to TRUE if this is a stream on a file, or FALSE if
  2027. this is a stream on a directory.
  2028. pReasonableErrorForUnOpenedName - set to TRUE if we hit an error trying
  2029. to do this open.
  2030. Return Value:
  2031. Returns STATUS_SUCCESS if we were able to determine if the parent to the
  2032. stream is a file or directory, or the error we hit in the open path
  2033. otherwise.
  2034. --***************************************************************************/
  2035. NTSTATUS
  2036. SrIsFileStream (
  2037. PSR_DEVICE_EXTENSION pExtension,
  2038. PSRP_NAME_CONTROL pNameCtrl,
  2039. PBOOLEAN pIsFileStream,
  2040. PBOOLEAN pReasonableErrorForUnOpenedName
  2041. )
  2042. {
  2043. OBJECT_ATTRIBUTES objectAttributes;
  2044. HANDLE fileHandle = NULL;
  2045. NTSTATUS status;
  2046. IO_STATUS_BLOCK ioStatus;
  2047. ASSERT( pIsFileStream != NULL );
  2048. ASSERT( pReasonableErrorForUnOpenedName != NULL );
  2049. *pReasonableErrorForUnOpenedName = FALSE;
  2050. InitializeObjectAttributes( &objectAttributes,
  2051. &(pNameCtrl->Name),
  2052. OBJ_KERNEL_HANDLE,
  2053. NULL,
  2054. NULL );
  2055. status = SrIoCreateFile( &fileHandle,
  2056. FILE_READ_ATTRIBUTES,
  2057. &objectAttributes,
  2058. &ioStatus,
  2059. NULL,
  2060. FILE_ATTRIBUTE_NORMAL,
  2061. FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
  2062. FILE_OPEN,
  2063. FILE_NON_DIRECTORY_FILE,
  2064. NULL,
  2065. 0,
  2066. IO_IGNORE_SHARE_ACCESS_CHECK,
  2067. pExtension->pTargetDevice );
  2068. if (status == STATUS_FILE_IS_A_DIRECTORY)
  2069. {
  2070. status = STATUS_SUCCESS;
  2071. *pIsFileStream = FALSE;
  2072. }
  2073. else if (status == STATUS_OBJECT_NAME_NOT_FOUND)
  2074. {
  2075. //
  2076. // We must be creating a new file with this stream operation,
  2077. // therefore the parent of this stream must be a file and not
  2078. // a directory.
  2079. //
  2080. status = STATUS_SUCCESS;
  2081. *pIsFileStream = TRUE;
  2082. }
  2083. else if (!NT_SUCCESS_NO_DBGBREAK( status ))
  2084. {
  2085. *pReasonableErrorForUnOpenedName = TRUE;
  2086. }
  2087. else
  2088. {
  2089. *pIsFileStream = TRUE;
  2090. }
  2091. if (fileHandle)
  2092. {
  2093. ZwClose( fileHandle );
  2094. }
  2095. RETURN( status );
  2096. }
  2097. /***************************************************************************++
  2098. Routine Description:
  2099. This routine checks to see if the long name for this file was tunneled. If
  2100. so, the user could have opened the file by its short name, but it will have
  2101. a long name associated with it. For correctness, we need to log operations
  2102. on this file via the long name.
  2103. Arguments:
  2104. pExtension - The SR extension the current volume
  2105. ppFileContext - This reference parameter passes in the current file context
  2106. for this file and may get replaced with a new file context if we need
  2107. to replace the name in this context. If this is the case, this
  2108. routine will properly cleanup the context passed in and the caller is
  2109. responsible for cleaning up the context passed out.
  2110. Return Value:
  2111. Returns STATUS_SUCCESS if the check for tunneling was successful and the
  2112. ppFileContext was updated as needed. If there was some error, the
  2113. appropriate error status is returned. Just like when we create our
  2114. original contexts, if an error occurs while doing this work, we must
  2115. generate a volume error and go into pass through mode. This routine will
  2116. generation the volume error and the caller should just get out of this
  2117. IO path.
  2118. --***************************************************************************/
  2119. NTSTATUS
  2120. SrCheckForNameTunneling (
  2121. IN PSR_DEVICE_EXTENSION pExtension,
  2122. IN OUT PSR_STREAM_CONTEXT *ppFileContext
  2123. )
  2124. {
  2125. NTSTATUS status;
  2126. PWCHAR pFileNameComponentBuffer = NULL;
  2127. ULONG FileNameComponentLength = 0;
  2128. PWCHAR pTildaPosition = NULL;
  2129. ULONG TildaPositionLength;
  2130. HANDLE parentDirectory = NULL;
  2131. PSR_STREAM_CONTEXT pOrigCtx;
  2132. ASSERT( ppFileContext != NULL );
  2133. pOrigCtx = *ppFileContext;
  2134. ASSERT( pOrigCtx != NULL);
  2135. //
  2136. // First, see if this file is interesting and if this pFileObject
  2137. // represents a file. Name tunneling is not done on directory names.
  2138. //
  2139. if (FlagOn( pOrigCtx->Flags, CTXFL_IsDirectory ) ||
  2140. !FlagOn( pOrigCtx->Flags, CTXFL_IsInteresting ))
  2141. {
  2142. status = STATUS_SUCCESS;
  2143. goto SrCheckForNameTunneling_Exit;
  2144. }
  2145. //
  2146. // Find the file name component of name we have in pOrigCtx.
  2147. //
  2148. status = SrFindCharReverse( pOrigCtx->FileName.Buffer,
  2149. pOrigCtx->FileName.Length,
  2150. L'\\',
  2151. &pFileNameComponentBuffer,
  2152. &FileNameComponentLength );
  2153. if (!NT_SUCCESS( status ))
  2154. {
  2155. goto SrCheckForNameTunneling_Exit;
  2156. }
  2157. ASSERT( FileNameComponentLength > sizeof( L'\\' ) );
  2158. ASSERT( pFileNameComponentBuffer[0] == L'\\' );
  2159. //
  2160. // Move past the leading '\' of the file name since we want to keep that
  2161. // with the parent directory name.
  2162. //
  2163. pFileNameComponentBuffer ++;
  2164. FileNameComponentLength -= sizeof( WCHAR );
  2165. //
  2166. // We've got the file name component. Now see if it is a candidate for
  2167. // tunneling of the long name. It will be a candidate if:
  2168. // * The name is (SR_SHORT_NAME_CHARS) or less.
  2169. // * The name contains a '~'.
  2170. //
  2171. if (FileNameComponentLength > ((SR_SHORT_NAME_CHARS) * sizeof (WCHAR)))
  2172. {
  2173. //
  2174. // This name is too long to be a short name. We're done.
  2175. //
  2176. goto SrCheckForNameTunneling_Exit;
  2177. }
  2178. status = SrFindCharReverse( pFileNameComponentBuffer,
  2179. FileNameComponentLength,
  2180. L'~',
  2181. &pTildaPosition,
  2182. &TildaPositionLength );
  2183. if (status == STATUS_OBJECT_NAME_NOT_FOUND)
  2184. {
  2185. //
  2186. // This name doesn't have a '~' therefore, it cannot be a short
  2187. // name.
  2188. //
  2189. status = STATUS_SUCCESS;
  2190. goto SrCheckForNameTunneling_Exit;
  2191. }
  2192. else
  2193. {
  2194. OBJECT_ATTRIBUTES objectAttributes;
  2195. UNICODE_STRING fileNameComponent;
  2196. UNICODE_STRING parentDirectoryName;
  2197. UNICODE_STRING fsFileName;
  2198. IO_STATUS_BLOCK ioStatus;
  2199. PFILE_BOTH_DIR_INFORMATION pFileBothDirInfo;
  2200. # define FILE_BOTH_DIR_SIZE (sizeof( FILE_BOTH_DIR_INFORMATION ) + (256 * sizeof( WCHAR )))
  2201. PCHAR pFileBothDirBuffer [FILE_BOTH_DIR_SIZE];
  2202. pFileBothDirInfo = (PFILE_BOTH_DIR_INFORMATION) pFileBothDirBuffer;
  2203. //
  2204. // This name contains a '~', therefore we need to open the parent directory
  2205. // and query for FileBothNamesInformation for this file to get the
  2206. // possibly tunneled long name.
  2207. //
  2208. parentDirectoryName.Length =
  2209. parentDirectoryName.MaximumLength =
  2210. (pOrigCtx->FileName.Length - (USHORT)FileNameComponentLength);
  2211. parentDirectoryName.Buffer = pOrigCtx->FileName.Buffer;
  2212. InitializeObjectAttributes( &objectAttributes,
  2213. &parentDirectoryName,
  2214. OBJ_KERNEL_HANDLE,
  2215. NULL,
  2216. NULL );
  2217. status = SrIoCreateFile(
  2218. &parentDirectory,
  2219. FILE_LIST_DIRECTORY | SYNCHRONIZE,
  2220. &objectAttributes,
  2221. &ioStatus,
  2222. NULL, // AllocationSize
  2223. FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_NORMAL,
  2224. FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, // ShareAccess
  2225. FILE_OPEN, // OPEN_EXISTING
  2226. FILE_DIRECTORY_FILE
  2227. | FILE_OPEN_FOR_BACKUP_INTENT
  2228. | FILE_SYNCHRONOUS_IO_NONALERT, //create options
  2229. NULL,
  2230. 0, // EaLength
  2231. IO_IGNORE_SHARE_ACCESS_CHECK,
  2232. pExtension->pTargetDevice );
  2233. if (!NT_SUCCESS( status ))
  2234. {
  2235. goto SrCheckForNameTunneling_Exit;
  2236. }
  2237. //
  2238. // Build a unicode string with for the file name component.
  2239. //
  2240. fileNameComponent.Buffer = pFileNameComponentBuffer;
  2241. fileNameComponent.Length =
  2242. fileNameComponent.MaximumLength = (USHORT)FileNameComponentLength;
  2243. status = ZwQueryDirectoryFile( parentDirectory,
  2244. NULL,
  2245. NULL,
  2246. NULL,
  2247. &ioStatus,
  2248. pFileBothDirInfo,
  2249. FILE_BOTH_DIR_SIZE,
  2250. FileBothDirectoryInformation,
  2251. TRUE,
  2252. &fileNameComponent,
  2253. TRUE );
  2254. if (!NT_SUCCESS( status ))
  2255. {
  2256. goto SrCheckForNameTunneling_Exit;
  2257. }
  2258. fsFileName.Buffer = &(pFileBothDirInfo->FileName[0]);
  2259. fsFileName.Length =
  2260. fsFileName.MaximumLength =
  2261. (USHORT)pFileBothDirInfo->FileNameLength;
  2262. if (RtlCompareUnicodeString( &fsFileName, &fileNameComponent, TRUE ) != 0)
  2263. {
  2264. PSR_STREAM_CONTEXT ctx;
  2265. ULONG contextSize;
  2266. ULONG fileNameLength;
  2267. //
  2268. // Name tunneling did occur. Now we need to create a new context
  2269. // with the updated name for this file.
  2270. //
  2271. fileNameLength = parentDirectoryName.Length + sizeof( L'\\' ) +
  2272. fsFileName.Length + pOrigCtx->StreamNameLength;
  2273. contextSize = fileNameLength + sizeof( SR_STREAM_CONTEXT );
  2274. ctx = ExAllocatePoolWithTag( PagedPool,
  2275. contextSize,
  2276. SR_STREAM_CONTEXT_TAG );
  2277. if (!ctx)
  2278. {
  2279. status = STATUS_INSUFFICIENT_RESOURCES;
  2280. goto SrCheckForNameTunneling_Exit;
  2281. }
  2282. #if DBG
  2283. INC_STATS(TotalContextCreated);
  2284. INC_STATS(TotalContextIsEligible);
  2285. #endif
  2286. //
  2287. // Initialize the context structure from the components we've
  2288. // got. We can copy over most everything but the full name from
  2289. // the pOrigCtx. We also need to initialize the filename
  2290. // correctly when this copy is through.
  2291. //
  2292. RtlCopyMemory( ctx,
  2293. pOrigCtx,
  2294. (sizeof(SR_STREAM_CONTEXT) + parentDirectoryName.Length) );
  2295. RtlInitEmptyUnicodeString( &ctx->FileName,
  2296. (PWCHAR)(ctx + 1),
  2297. fileNameLength );
  2298. ctx->FileName.MaximumLength = (USHORT)fileNameLength;
  2299. ctx->FileName.Length = parentDirectoryName.Length;
  2300. //
  2301. // Append trailing slash if one is not already there
  2302. // NOTE: About fix for bug 374479
  2303. // I believe the append below is unnecessary because the
  2304. // code above this guarentees that the path always has
  2305. // a trailing slash. But because this fix is occuring so
  2306. // late in the release I decided to simply add a check to
  2307. // see if we should add the slash. If so we will add it.
  2308. // I believe the following 6 lines of code can be deleted
  2309. // in a future version of SR.
  2310. //
  2311. ASSERT(ctx->FileName.Length > 0);
  2312. if (ctx->FileName.Buffer[(ctx->FileName.Length/sizeof(WCHAR))-1] != L'\\')
  2313. {
  2314. RtlAppendUnicodeToString( &ctx->FileName, L"\\" );
  2315. }
  2316. //
  2317. // Append file name
  2318. //
  2319. RtlAppendUnicodeStringToString( &ctx->FileName, &fsFileName );
  2320. if (pOrigCtx->StreamNameLength > 0)
  2321. {
  2322. //
  2323. // This file has a stream name component so copy that over now.
  2324. // The ctx->StreamNameLength should already be correctly set.
  2325. //
  2326. ASSERT( ctx->StreamNameLength == pOrigCtx->StreamNameLength );
  2327. RtlCopyMemory( &(ctx->FileName.Buffer[ctx->FileName.Length/sizeof( WCHAR )]),
  2328. &(pOrigCtx->FileName.Buffer[pOrigCtx->FileName.Length/sizeof( WCHAR )]),
  2329. pOrigCtx->StreamNameLength );
  2330. }
  2331. //
  2332. // Now we are done with the original file context and we want
  2333. // to return our new one.
  2334. //
  2335. status = STATUS_SUCCESS;
  2336. SrReleaseContext( pOrigCtx );
  2337. *ppFileContext = ctx;
  2338. VALIDATE_FILENAME( &ctx->FileName );
  2339. }
  2340. }
  2341. SrCheckForNameTunneling_Exit:
  2342. //
  2343. // If we have an error, we need to generate a volume error here.
  2344. //
  2345. if (CHECK_FOR_VOLUME_ERROR( status ))
  2346. {
  2347. //
  2348. // Trigger the failure notification to the service
  2349. //
  2350. NTSTATUS tempStatus = SrNotifyVolumeError( pExtension,
  2351. &(pOrigCtx->FileName),
  2352. status,
  2353. SrEventFileCreate );
  2354. CHECK_STATUS(tempStatus);
  2355. }
  2356. if ( parentDirectory != NULL )
  2357. {
  2358. ZwClose( parentDirectory );
  2359. }
  2360. RETURN( status );
  2361. }