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.

2920 lines
82 KiB

  1. /*++
  2. Copyright (c) 1989-2000 Microsoft Corporation
  3. Module Name:
  4. Create.c
  5. Abstract:
  6. This module implements the File Create routine for Cdfs called by the
  7. Fsd/Fsp dispatch routines.
  8. // @@BEGIN_DDKSPLIT
  9. Author:
  10. Brian Andrew [BrianAn] 01-July-1995
  11. Revision History:
  12. // @@END_DDKSPLIT
  13. --*/
  14. #include "CdProcs.h"
  15. //
  16. // The Bug check file id for this module
  17. //
  18. #define BugCheckFileId (CDFS_BUG_CHECK_CREATE)
  19. //
  20. // Local support routines
  21. //
  22. NTSTATUS
  23. CdNormalizeFileNames (
  24. IN PIRP_CONTEXT IrpContext,
  25. IN PVCB Vcb,
  26. IN BOOLEAN OpenByFileId,
  27. IN BOOLEAN IgnoreCase,
  28. IN TYPE_OF_OPEN RelatedTypeOfOpen,
  29. IN PCCB RelatedCcb OPTIONAL,
  30. IN PUNICODE_STRING RelatedFileName OPTIONAL,
  31. IN OUT PUNICODE_STRING FileName,
  32. IN OUT PCD_NAME RemainingName
  33. );
  34. NTSTATUS
  35. CdOpenByFileId (
  36. IN PIRP_CONTEXT IrpContext,
  37. IN PIO_STACK_LOCATION IrpSp,
  38. IN PVCB Vcb,
  39. IN OUT PFCB *CurrentFcb
  40. );
  41. NTSTATUS
  42. CdOpenExistingFcb (
  43. IN PIRP_CONTEXT IrpContext,
  44. IN PIO_STACK_LOCATION IrpSp,
  45. IN OUT PFCB *CurrentFcb,
  46. IN TYPE_OF_OPEN TypeOfOpen,
  47. IN BOOLEAN IgnoreCase,
  48. IN PCCB RelatedCcb OPTIONAL
  49. );
  50. NTSTATUS
  51. CdOpenDirectoryFromPathEntry (
  52. IN PIRP_CONTEXT IrpContext,
  53. IN PIO_STACK_LOCATION IrpSp,
  54. IN PVCB Vcb,
  55. IN OUT PFCB *CurrentFcb,
  56. IN PCD_NAME DirName,
  57. IN BOOLEAN IgnoreCase,
  58. IN BOOLEAN ShortNameMatch,
  59. IN PPATH_ENTRY PathEntry,
  60. IN BOOLEAN PerformUserOpen,
  61. IN PCCB RelatedCcb OPTIONAL
  62. );
  63. NTSTATUS
  64. CdOpenFileFromFileContext (
  65. IN PIRP_CONTEXT IrpContext,
  66. IN PIO_STACK_LOCATION IrpSp,
  67. IN PVCB Vcb,
  68. IN OUT PFCB *CurrentFcb,
  69. IN PCD_NAME FileName,
  70. IN BOOLEAN IgnoreCase,
  71. IN BOOLEAN ShortNameMatch,
  72. IN PFILE_ENUM_CONTEXT FileContext,
  73. IN PCCB RelatedCcb OPTIONAL
  74. );
  75. NTSTATUS
  76. CdCompleteFcbOpen (
  77. IN PIRP_CONTEXT IrpContext,
  78. PIO_STACK_LOCATION IrpSp,
  79. IN PVCB Vcb,
  80. IN OUT PFCB *CurrentFcb,
  81. IN TYPE_OF_OPEN TypeOfOpen,
  82. IN ULONG UserCcbFlags,
  83. IN ACCESS_MASK DesiredAccess
  84. );
  85. #ifdef ALLOC_PRAGMA
  86. #pragma alloc_text(PAGE, CdCommonCreate)
  87. #pragma alloc_text(PAGE, CdCompleteFcbOpen)
  88. #pragma alloc_text(PAGE, CdNormalizeFileNames)
  89. #pragma alloc_text(PAGE, CdOpenByFileId)
  90. #pragma alloc_text(PAGE, CdOpenDirectoryFromPathEntry)
  91. #pragma alloc_text(PAGE, CdOpenExistingFcb)
  92. #pragma alloc_text(PAGE, CdOpenFileFromFileContext)
  93. #endif
  94. NTSTATUS
  95. CdCommonCreate (
  96. IN PIRP_CONTEXT IrpContext,
  97. IN PIRP Irp
  98. )
  99. /*++
  100. Routine Description:
  101. This is the common routine for opening a file called by both the
  102. Fsp and Fsd threads.
  103. The file can be opened either by name or by file Id either with or without
  104. a relative name. The file name field in the file object passed to this routine
  105. contains either a unicode string or a 64 bit value which is the file Id.
  106. If this is not a Joliet disk then we will convert the unicode name to
  107. an Oem string in this routine. If there is a related file object with
  108. a name then we will already have converted that name to Oem.
  109. We will store the full name for the file in the file object on a successful
  110. open. We will allocate a larger buffer if necessary and combine the
  111. related and file object names. The only exception is the relative open
  112. when the related file object is for an OpenByFileId file. If we need to
  113. allocate a buffer for a case insensitive name then we allocate it at
  114. the tail of the buffer we will store into the file object. The upcased
  115. portion will begin immediately after the name defined by the FileName
  116. in the file object.
  117. Once we have the full name in the file object we don't want to split the
  118. name in the event of a retry. We use a flag in the IrpContext to indicate
  119. that the name has been split.
  120. Arguments:
  121. Irp - Supplies the Irp to process
  122. Return Value:
  123. NTSTATUS - This is the status from this open operation.
  124. --*/
  125. {
  126. NTSTATUS Status = STATUS_SUCCESS;
  127. PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp );
  128. PFILE_OBJECT FileObject;
  129. COMPOUND_PATH_ENTRY CompoundPathEntry;
  130. BOOLEAN CleanupCompoundPathEntry = FALSE;
  131. FILE_ENUM_CONTEXT FileContext;
  132. BOOLEAN CleanupFileContext = FALSE;
  133. BOOLEAN FoundEntry;
  134. PVCB Vcb;
  135. BOOLEAN OpenByFileId;
  136. BOOLEAN IgnoreCase;
  137. ULONG CreateDisposition;
  138. BOOLEAN ShortNameMatch;
  139. ULONG ShortNameDirentOffset;
  140. BOOLEAN VolumeOpen = FALSE;
  141. //
  142. // We will be acquiring and releasing file Fcb's as we move down the
  143. // directory tree during opens. At any time we need to know the deepest
  144. // point we have traversed down in the tree in case we need to cleanup
  145. // any structures created here.
  146. //
  147. // CurrentFcb - represents this point. If non-null it means we have
  148. // acquired it and need to release it in finally clause.
  149. //
  150. // NextFcb - represents the NextFcb to walk to but haven't acquired yet.
  151. //
  152. TYPE_OF_OPEN RelatedTypeOfOpen = UnopenedFileObject;
  153. PFILE_OBJECT RelatedFileObject;
  154. PCCB RelatedCcb = NULL;
  155. PFCB NextFcb;
  156. PFCB CurrentFcb = NULL;
  157. //
  158. // During the open we need to combine the related file object name
  159. // with the remaining name. We also may need to upcase the file name
  160. // in order to do a case-insensitive name comparison. We also need
  161. // to restore the name in the file object in the event that we retry
  162. // the request. We use the following string variables to manage the
  163. // name. We will can put these strings into either Unicode or Ansi
  164. // form.
  165. //
  166. // FileName - Pointer to name as currently stored in the file
  167. // object. We store the full name into the file object early in
  168. // the open operation.
  169. //
  170. // RelatedFileName - Pointer to the name in the related file object.
  171. //
  172. // RemainingName - String containing remaining name to parse.
  173. //
  174. // MatchingName - Address of name structure in FileContext which matched.
  175. // We need this to know whether we matched the long or short name.
  176. //
  177. PUNICODE_STRING FileName;
  178. PUNICODE_STRING RelatedFileName = NULL;
  179. CD_NAME RemainingName;
  180. CD_NAME FinalName;
  181. PCD_NAME MatchingName;
  182. PAGED_CODE();
  183. //
  184. // If we were called with our file system device object instead of a
  185. // volume device object, just complete this request with STATUS_SUCCESS.
  186. //
  187. if (IrpContext->Vcb == NULL) {
  188. CdCompleteRequest( IrpContext, Irp, STATUS_SUCCESS );
  189. return STATUS_SUCCESS;
  190. }
  191. //
  192. // Get create parameters from the Irp.
  193. //
  194. OpenByFileId = BooleanFlagOn( IrpSp->Parameters.Create.Options, FILE_OPEN_BY_FILE_ID );
  195. IgnoreCase = !BooleanFlagOn( IrpSp->Flags, SL_CASE_SENSITIVE );
  196. CreateDisposition = (IrpSp->Parameters.Create.Options >> 24) & 0x000000ff;
  197. //
  198. // Do some preliminary checks to make sure the operation is supported.
  199. // We fail in the following cases immediately.
  200. //
  201. // - Open a paging file.
  202. // - Open a target directory.
  203. // - Open a file with Eas.
  204. // - Create a file.
  205. //
  206. if (FlagOn( IrpSp->Flags, SL_OPEN_PAGING_FILE | SL_OPEN_TARGET_DIRECTORY) ||
  207. (IrpSp->Parameters.Create.EaLength != 0) ||
  208. (CreateDisposition == FILE_CREATE)) {
  209. CdCompleteRequest( IrpContext, Irp, STATUS_ACCESS_DENIED );
  210. return STATUS_ACCESS_DENIED;
  211. }
  212. //
  213. // Copy the Vcb to a local. Assume the starting directory is the root.
  214. //
  215. Vcb = IrpContext->Vcb;
  216. NextFcb = Vcb->RootIndexFcb;
  217. //
  218. // Reference our input parameters to make things easier
  219. //
  220. FileObject = IrpSp->FileObject;
  221. RelatedFileObject = NULL;
  222. FileName = &FileObject->FileName;
  223. //
  224. // Set up the file object's Vpb pointer in case anything happens.
  225. // This will allow us to get a reasonable pop-up.
  226. //
  227. if ((FileObject->RelatedFileObject != NULL) && !OpenByFileId) {
  228. RelatedFileObject = FileObject->RelatedFileObject;
  229. FileObject->Vpb = RelatedFileObject->Vpb;
  230. RelatedTypeOfOpen = CdDecodeFileObject( IrpContext, RelatedFileObject, &NextFcb, &RelatedCcb );
  231. //
  232. // Fail the request if this is not a user file object.
  233. //
  234. if (RelatedTypeOfOpen < UserVolumeOpen) {
  235. CdCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
  236. return STATUS_INVALID_PARAMETER;
  237. }
  238. //
  239. // Remember the name in the related file object.
  240. //
  241. RelatedFileName = &RelatedFileObject->FileName;
  242. }
  243. //
  244. // If we haven't initialized the names then make sure the strings are valid.
  245. // If this an OpenByFileId then verify the file id buffer.
  246. //
  247. // After this routine returns we know that the full name is in the
  248. // FileName buffer and the buffer will hold the upcased portion
  249. // of the name yet to parse immediately after the full name in the
  250. // buffer. Any trailing backslash has been removed and the flag
  251. // in the IrpContext will indicate whether we removed the
  252. // backslash.
  253. //
  254. Status = CdNormalizeFileNames( IrpContext,
  255. Vcb,
  256. OpenByFileId,
  257. IgnoreCase,
  258. RelatedTypeOfOpen,
  259. RelatedCcb,
  260. RelatedFileName,
  261. FileName,
  262. &RemainingName );
  263. //
  264. // Return the error code if not successful.
  265. //
  266. if (!NT_SUCCESS( Status )) {
  267. CdCompleteRequest( IrpContext, Irp, Status );
  268. return Status;
  269. }
  270. //
  271. // We want to acquire the Vcb. Exclusively for a volume open, shared otherwise.
  272. // The file name is empty for a volume open.
  273. //
  274. if ((FileName->Length == 0) &&
  275. (RelatedTypeOfOpen <= UserVolumeOpen) &&
  276. !OpenByFileId) {
  277. VolumeOpen = TRUE;
  278. CdAcquireVcbExclusive( IrpContext, Vcb, FALSE );
  279. } else {
  280. CdAcquireVcbShared( IrpContext, Vcb, FALSE );
  281. }
  282. //
  283. // Use a try-finally to facilitate cleanup.
  284. //
  285. try {
  286. //
  287. // Verify that the Vcb is not in an unusable condition. This routine
  288. // will raise if not usable.
  289. //
  290. CdVerifyVcb( IrpContext, Vcb );
  291. //
  292. // If the Vcb is locked then we cannot open another file
  293. //
  294. if (FlagOn( Vcb->VcbState, VCB_STATE_LOCKED )) {
  295. try_return( Status = STATUS_ACCESS_DENIED );
  296. }
  297. //
  298. // If we are opening this file by FileId then process this immediately
  299. // and exit.
  300. //
  301. if (OpenByFileId) {
  302. //
  303. // We only allow Dasd opens of audio disks. Fail this request at
  304. // this point.
  305. //
  306. if (FlagOn( Vcb->VcbState, VCB_STATE_AUDIO_DISK )) {
  307. try_return( Status = STATUS_INVALID_DEVICE_REQUEST );
  308. }
  309. //
  310. // The only create disposition we allow is OPEN.
  311. //
  312. if ((CreateDisposition != FILE_OPEN) &&
  313. (CreateDisposition != FILE_OPEN_IF)) {
  314. try_return( Status = STATUS_ACCESS_DENIED );
  315. }
  316. //
  317. // Make sure we can wait for this request.
  318. //
  319. if (!FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT )) {
  320. CdRaiseStatus( IrpContext, STATUS_CANT_WAIT );
  321. }
  322. try_return( Status = CdOpenByFileId( IrpContext,
  323. IrpSp,
  324. Vcb,
  325. &CurrentFcb ));
  326. }
  327. //
  328. // If we are opening this volume Dasd then process this immediately
  329. // and exit.
  330. //
  331. if (VolumeOpen) {
  332. //
  333. // The only create disposition we allow is OPEN.
  334. //
  335. if ((CreateDisposition != FILE_OPEN) &&
  336. (CreateDisposition != FILE_OPEN_IF)) {
  337. try_return( Status = STATUS_ACCESS_DENIED );
  338. }
  339. //
  340. // If they wanted to open a directory, surprise.
  341. //
  342. if (FlagOn( IrpSp->Parameters.Create.Options, FILE_DIRECTORY_FILE )) {
  343. try_return( Status = STATUS_NOT_A_DIRECTORY );
  344. }
  345. //
  346. // Acquire the Fcb first.
  347. //
  348. CurrentFcb = Vcb->VolumeDasdFcb;
  349. CdAcquireFcbExclusive( IrpContext, CurrentFcb, FALSE );
  350. try_return( Status = CdOpenExistingFcb( IrpContext,
  351. IrpSp,
  352. &CurrentFcb,
  353. UserVolumeOpen,
  354. FALSE,
  355. NULL ));
  356. }
  357. //
  358. // At this point CurrentFcb points to the deepest Fcb for this open
  359. // in the tree. Let's acquire this Fcb to keep it from being deleted
  360. // beneath us.
  361. //
  362. CdAcquireFcbExclusive( IrpContext, NextFcb, FALSE );
  363. CurrentFcb = NextFcb;
  364. //
  365. // Do a prefix search if there is more of the name to parse.
  366. //
  367. if (RemainingName.FileName.Length != 0) {
  368. //
  369. // Do the prefix search to find the longest matching name.
  370. //
  371. CdFindPrefix( IrpContext,
  372. &CurrentFcb,
  373. &RemainingName.FileName,
  374. IgnoreCase );
  375. }
  376. //
  377. // If the remaining name length is zero then we have found our
  378. // target.
  379. //
  380. if (RemainingName.FileName.Length == 0) {
  381. //
  382. // If this is a file so verify the user didn't want to open
  383. // a directory.
  384. //
  385. if (SafeNodeType( CurrentFcb ) == CDFS_NTC_FCB_DATA) {
  386. if (FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_TRAIL_BACKSLASH ) ||
  387. FlagOn( IrpSp->Parameters.Create.Options, FILE_DIRECTORY_FILE )) {
  388. try_return( Status = STATUS_NOT_A_DIRECTORY );
  389. }
  390. //
  391. // The only create disposition we allow is OPEN.
  392. //
  393. if ((CreateDisposition != FILE_OPEN) &&
  394. (CreateDisposition != FILE_OPEN_IF)) {
  395. try_return( Status = STATUS_ACCESS_DENIED );
  396. }
  397. try_return( Status = CdOpenExistingFcb( IrpContext,
  398. IrpSp,
  399. &CurrentFcb,
  400. UserFileOpen,
  401. IgnoreCase,
  402. RelatedCcb ));
  403. //
  404. // This is a directory. Verify the user didn't want to open
  405. // as a file.
  406. //
  407. } else if (FlagOn( IrpSp->Parameters.Create.Options, FILE_NON_DIRECTORY_FILE )) {
  408. try_return( Status = STATUS_FILE_IS_A_DIRECTORY );
  409. //
  410. // Open the file as a directory.
  411. //
  412. } else {
  413. //
  414. // The only create disposition we allow is OPEN.
  415. //
  416. if ((CreateDisposition != FILE_OPEN) &&
  417. (CreateDisposition != FILE_OPEN_IF)) {
  418. try_return( Status = STATUS_ACCESS_DENIED );
  419. }
  420. try_return( Status = CdOpenExistingFcb( IrpContext,
  421. IrpSp,
  422. &CurrentFcb,
  423. UserDirectoryOpen,
  424. IgnoreCase,
  425. RelatedCcb ));
  426. }
  427. }
  428. //
  429. // We have more work to do. We have a starting Fcb which we own shared.
  430. // We also have the remaining name to parse. Walk through the name
  431. // component by component looking for the full name.
  432. //
  433. //
  434. // Our starting Fcb better be a directory.
  435. //
  436. if (!FlagOn( CurrentFcb->FileAttributes, FILE_ATTRIBUTE_DIRECTORY )) {
  437. try_return( Status = STATUS_OBJECT_PATH_NOT_FOUND );
  438. }
  439. //
  440. // If we can't wait then post this request.
  441. //
  442. if (!FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT )) {
  443. CdRaiseStatus( IrpContext, STATUS_CANT_WAIT );
  444. }
  445. //
  446. // Make sure the final name has no version string.
  447. //
  448. FinalName.VersionString.Length = 0;
  449. while (TRUE) {
  450. ShortNameMatch = FALSE;
  451. //
  452. // Split off the next component from the name.
  453. //
  454. CdDissectName( IrpContext,
  455. &RemainingName.FileName,
  456. &FinalName.FileName );
  457. //
  458. // Go ahead and look this entry up in the path table.
  459. //
  460. CdInitializeCompoundPathEntry( IrpContext, &CompoundPathEntry );
  461. CleanupCompoundPathEntry = TRUE;
  462. FoundEntry = CdFindPathEntry( IrpContext,
  463. CurrentFcb,
  464. &FinalName,
  465. IgnoreCase,
  466. &CompoundPathEntry );
  467. //
  468. // If we didn't find the entry then check if the current name
  469. // is a possible short name.
  470. //
  471. if (!FoundEntry) {
  472. ShortNameDirentOffset = CdShortNameDirentOffset( IrpContext, &FinalName.FileName );
  473. //
  474. // If there is an embedded short name offset then look for the
  475. // matching long name in the directory.
  476. //
  477. if (ShortNameDirentOffset != MAXULONG) {
  478. if (CleanupFileContext) {
  479. CdCleanupFileContext( IrpContext, &FileContext );
  480. }
  481. CdInitializeFileContext( IrpContext, &FileContext );
  482. CleanupFileContext = TRUE;
  483. FoundEntry = CdFindFileByShortName( IrpContext,
  484. CurrentFcb,
  485. &FinalName,
  486. IgnoreCase,
  487. ShortNameDirentOffset,
  488. &FileContext );
  489. //
  490. // If we found an entry and it is a directory then look
  491. // this up in the path table.
  492. //
  493. if (FoundEntry) {
  494. ShortNameMatch = TRUE;
  495. if (FlagOn( FileContext.InitialDirent->Dirent.DirentFlags,
  496. CD_ATTRIBUTE_DIRECTORY )) {
  497. CdCleanupCompoundPathEntry( IrpContext, &CompoundPathEntry );
  498. CdInitializeCompoundPathEntry( IrpContext, &CompoundPathEntry );
  499. FoundEntry = CdFindPathEntry( IrpContext,
  500. CurrentFcb,
  501. &FileContext.InitialDirent->Dirent.CdCaseFileName,
  502. IgnoreCase,
  503. &CompoundPathEntry );
  504. //
  505. // We better find this entry.
  506. //
  507. if (!FoundEntry) {
  508. CdRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR );
  509. }
  510. //
  511. // Upcase the name with the short name if case
  512. // insensitive.
  513. //
  514. if (IgnoreCase) {
  515. CdUpcaseName( IrpContext, &FinalName, &FinalName );
  516. }
  517. //
  518. // We found a matching file. If we are at the last
  519. // entry then break out of the loop and open the
  520. // file below. Otherwise we return an error.
  521. //
  522. } else if (RemainingName.FileName.Length == 0) {
  523. //
  524. // Break out of the loop. We will process the dirent
  525. // below.
  526. //
  527. MatchingName = &FileContext.ShortName;
  528. break;
  529. } else {
  530. try_return( Status = STATUS_OBJECT_PATH_NOT_FOUND );
  531. }
  532. }
  533. }
  534. //
  535. // We didn't find the name in either the path table or as
  536. // a short name in a directory. If the remaining name
  537. // length is zero then break out of the loop to search
  538. // the directory.
  539. //
  540. if (!FoundEntry) {
  541. if (RemainingName.FileName.Length == 0) {
  542. break;
  543. //
  544. // Otherwise this path could not be cracked.
  545. //
  546. } else {
  547. try_return( Status = STATUS_OBJECT_PATH_NOT_FOUND );
  548. }
  549. }
  550. }
  551. //
  552. // If this is an ignore case open then copy the exact case
  553. // in the file object name. If it was a short name match then
  554. // the name must be upcase already.
  555. //
  556. if (IgnoreCase && !ShortNameMatch) {
  557. RtlCopyMemory( FinalName.FileName.Buffer,
  558. CompoundPathEntry.PathEntry.CdDirName.FileName.Buffer,
  559. CompoundPathEntry.PathEntry.CdDirName.FileName.Length );
  560. }
  561. //
  562. // If we have found the last component then open this as a directory
  563. // and return to our caller.
  564. //
  565. if (RemainingName.FileName.Length == 0) {
  566. if (FlagOn( IrpSp->Parameters.Create.Options, FILE_NON_DIRECTORY_FILE )) {
  567. try_return( Status = STATUS_FILE_IS_A_DIRECTORY );
  568. }
  569. //
  570. // The only create disposition we allow is OPEN.
  571. //
  572. if ((CreateDisposition != FILE_OPEN) &&
  573. (CreateDisposition != FILE_OPEN_IF)) {
  574. try_return( Status = STATUS_ACCESS_DENIED );
  575. }
  576. try_return( Status = CdOpenDirectoryFromPathEntry( IrpContext,
  577. IrpSp,
  578. Vcb,
  579. &CurrentFcb,
  580. &FinalName,
  581. IgnoreCase,
  582. ShortNameMatch,
  583. &CompoundPathEntry.PathEntry,
  584. TRUE,
  585. RelatedCcb ));
  586. }
  587. //
  588. // Otherwise open an Fcb for this intermediate index Fcb.
  589. //
  590. CdOpenDirectoryFromPathEntry( IrpContext,
  591. IrpSp,
  592. Vcb,
  593. &CurrentFcb,
  594. &FinalName,
  595. IgnoreCase,
  596. ShortNameMatch,
  597. &CompoundPathEntry.PathEntry,
  598. FALSE,
  599. NULL );
  600. CdCleanupCompoundPathEntry( IrpContext, &CompoundPathEntry );
  601. CleanupCompoundPathEntry = FALSE;
  602. }
  603. //
  604. // We need to scan the current directory for a matching file name
  605. // if we don't already have one.
  606. //
  607. if (!FoundEntry) {
  608. if (CleanupFileContext) {
  609. CdCleanupFileContext( IrpContext, &FileContext );
  610. }
  611. CdInitializeFileContext( IrpContext, &FileContext );
  612. CleanupFileContext = TRUE;
  613. //
  614. // Split our search name into separate components.
  615. //
  616. CdConvertNameToCdName( IrpContext, &FinalName );
  617. FoundEntry = CdFindFile( IrpContext,
  618. CurrentFcb,
  619. &FinalName,
  620. IgnoreCase,
  621. &FileContext,
  622. &MatchingName );
  623. }
  624. //
  625. // If we didn't find a match then check if the name is invalid to
  626. // determine which error code to return.
  627. //
  628. if (!FoundEntry) {
  629. if ((CreateDisposition == FILE_OPEN) ||
  630. (CreateDisposition == FILE_OVERWRITE)) {
  631. try_return( Status = STATUS_OBJECT_NAME_NOT_FOUND );
  632. }
  633. //
  634. // Any other operation return STATUS_ACCESS_DENIED.
  635. //
  636. try_return( Status = STATUS_ACCESS_DENIED );
  637. }
  638. //
  639. // If this is a directory then the disk is corrupt because it wasn't
  640. // in the Path Table.
  641. //
  642. if (FlagOn( FileContext.InitialDirent->Dirent.Flags, CD_ATTRIBUTE_DIRECTORY )) {
  643. CdRaiseStatus( IrpContext, STATUS_DISK_CORRUPT_ERROR );
  644. }
  645. //
  646. // Make sure our opener didn't want a directory.
  647. //
  648. if (FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_TRAIL_BACKSLASH ) ||
  649. FlagOn( IrpSp->Parameters.Create.Options, FILE_DIRECTORY_FILE )) {
  650. try_return( Status = STATUS_NOT_A_DIRECTORY );
  651. }
  652. //
  653. // The only create disposition we allow is OPEN.
  654. //
  655. if ((CreateDisposition != FILE_OPEN) &&
  656. (CreateDisposition != FILE_OPEN_IF)) {
  657. try_return( Status = STATUS_ACCESS_DENIED );
  658. }
  659. //
  660. // If this is an ignore case open then copy the exact case
  661. // in the file object name. Any version portion should
  662. // already be upcased.
  663. //
  664. if (IgnoreCase) {
  665. RtlCopyMemory( FinalName.FileName.Buffer,
  666. MatchingName->FileName.Buffer,
  667. MatchingName->FileName.Length );
  668. }
  669. //
  670. // Open the file using the file context. We already have the
  671. // first and last dirents.
  672. //
  673. try_return( Status = CdOpenFileFromFileContext( IrpContext,
  674. IrpSp,
  675. Vcb,
  676. &CurrentFcb,
  677. &FinalName,
  678. IgnoreCase,
  679. (BOOLEAN) (MatchingName == &FileContext.ShortName),
  680. &FileContext,
  681. RelatedCcb ));
  682. try_exit: NOTHING;
  683. } finally {
  684. //
  685. // Cleanup the PathEntry if initialized.
  686. //
  687. if (CleanupCompoundPathEntry) {
  688. CdCleanupCompoundPathEntry( IrpContext, &CompoundPathEntry );
  689. }
  690. //
  691. // Cleanup the FileContext if initialized.
  692. //
  693. if (CleanupFileContext) {
  694. CdCleanupFileContext( IrpContext, &FileContext );
  695. }
  696. //
  697. // The result of this open could be success, pending or some error
  698. // condition.
  699. //
  700. if (AbnormalTermination()) {
  701. //
  702. // In the error path we start by calling our teardown routine if we
  703. // have a CurrentFcb.
  704. //
  705. if (CurrentFcb != NULL) {
  706. BOOLEAN RemovedFcb;
  707. CdTeardownStructures( IrpContext, CurrentFcb, &RemovedFcb );
  708. if (RemovedFcb) {
  709. CurrentFcb = NULL;
  710. }
  711. }
  712. //
  713. // No need to complete the request.
  714. //
  715. IrpContext = NULL;
  716. Irp = NULL;
  717. //
  718. // If we posted this request through the oplock package we need
  719. // to show that there is no reason to complete the request.
  720. //
  721. } else if (Status == STATUS_PENDING) {
  722. IrpContext = NULL;
  723. Irp = NULL;
  724. }
  725. //
  726. // Release the Current Fcb if still acquired.
  727. //
  728. if (CurrentFcb != NULL) {
  729. CdReleaseFcb( IrpContext, CurrentFcb );
  730. }
  731. //
  732. // Release the Vcb.
  733. //
  734. CdReleaseVcb( IrpContext, Vcb );
  735. //
  736. // Call our completion routine. It will handle the case where either
  737. // the Irp and/or IrpContext are gone.
  738. //
  739. CdCompleteRequest( IrpContext, Irp, Status );
  740. }
  741. return Status;
  742. }
  743. //
  744. // Local support routine
  745. //
  746. NTSTATUS
  747. CdNormalizeFileNames (
  748. IN PIRP_CONTEXT IrpContext,
  749. IN PVCB Vcb,
  750. IN BOOLEAN OpenByFileId,
  751. IN BOOLEAN IgnoreCase,
  752. IN TYPE_OF_OPEN RelatedTypeOfOpen,
  753. IN PCCB RelatedCcb OPTIONAL,
  754. IN PUNICODE_STRING RelatedFileName OPTIONAL,
  755. IN OUT PUNICODE_STRING FileName,
  756. IN OUT PCD_NAME RemainingName
  757. )
  758. /*++
  759. Routine Description:
  760. This routine is called to store the full name and upcased name into the
  761. filename buffer. We only upcase the portion yet to parse. We also
  762. check for a trailing backslash and lead-in double backslashes. This
  763. routine also verifies the mode of the related open against the name
  764. currently in the filename.
  765. Arguments:
  766. Vcb - Vcb for this volume.
  767. OpenByFileId - Indicates if the filename should be a 64 bit FileId.
  768. IgnoreCase - Indicates if this open is a case-insensitive operation.
  769. RelatedTypeOfOpen - Indicates the type of the related file object.
  770. RelatedCcb - Ccb for the related open. Ignored if no relative open.
  771. RelatedFileName - FileName buffer for related open. Ignored if no
  772. relative open.
  773. FileName - FileName to update in this routine. The name should
  774. either be a 64-bit FileId or a Unicode string.
  775. RemainingName - Name with the remaining portion of the name. This
  776. will begin after the related name and any separator. For a
  777. non-relative open we also step over the initial separator.
  778. Return Value:
  779. NTSTATUS - STATUS_SUCCESS if the names are OK, appropriate error code
  780. otherwise.
  781. --*/
  782. {
  783. ULONG RemainingNameLength;
  784. ULONG RelatedNameLength = 0;
  785. ULONG SeparatorLength = 0;
  786. ULONG BufferLength;
  787. UNICODE_STRING NewFileName;
  788. PAGED_CODE();
  789. //
  790. // If this is the first pass then we need to build the full name and
  791. // check for name compatibility.
  792. //
  793. if (!FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_FULL_NAME )) {
  794. //
  795. // Deal with the regular file name case first.
  796. //
  797. if (!OpenByFileId) {
  798. //
  799. // This is here because the Win32 layer can't avoid sending me double
  800. // beginning backslashes.
  801. //
  802. if ((FileName->Length > sizeof( WCHAR )) &&
  803. (FileName->Buffer[1] == L'\\') &&
  804. (FileName->Buffer[0] == L'\\')) {
  805. //
  806. // If there are still two beginning backslashes, the name is bogus.
  807. //
  808. if ((FileName->Length > 2 * sizeof( WCHAR )) &&
  809. (FileName->Buffer[2] == L'\\')) {
  810. return STATUS_OBJECT_NAME_INVALID;
  811. }
  812. //
  813. // Slide the name down in the buffer.
  814. //
  815. FileName->Length -= sizeof( WCHAR );
  816. RtlMoveMemory( FileName->Buffer,
  817. FileName->Buffer + 1,
  818. FileName->Length );
  819. }
  820. //
  821. // Check for a trailing backslash. Don't strip off if only character
  822. // in the full name or for relative opens where this is illegal.
  823. //
  824. if (((FileName->Length > sizeof( WCHAR)) ||
  825. ((FileName->Length == sizeof( WCHAR )) && (RelatedTypeOfOpen == UserDirectoryOpen))) &&
  826. (FileName->Buffer[ (FileName->Length/2) - 1 ] == L'\\')) {
  827. SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_TRAIL_BACKSLASH );
  828. FileName->Length -= sizeof( WCHAR );
  829. }
  830. //
  831. // Remember the length we need for this portion of the name.
  832. //
  833. RemainingNameLength = FileName->Length;
  834. //
  835. // If this is a related file object then we verify the compatibility
  836. // of the name in the file object with the relative file object.
  837. //
  838. if (RelatedTypeOfOpen != UnopenedFileObject) {
  839. //
  840. // If the filename length was zero then it must be legal.
  841. // If there are characters then check with the related
  842. // type of open.
  843. //
  844. if (FileName->Length != 0) {
  845. //
  846. // The name length must always be zero for a volume open.
  847. //
  848. if (RelatedTypeOfOpen <= UserVolumeOpen) {
  849. return STATUS_INVALID_PARAMETER;
  850. //
  851. // The remaining name cannot begin with a backslash.
  852. //
  853. } else if (FileName->Buffer[0] == L'\\' ) {
  854. return STATUS_INVALID_PARAMETER;
  855. //
  856. // If the related file is a user file then there
  857. // is no file with this path.
  858. //
  859. } else if (RelatedTypeOfOpen == UserFileOpen) {
  860. return STATUS_OBJECT_PATH_NOT_FOUND;
  861. }
  862. }
  863. //
  864. // Remember the length of the related name when building
  865. // the full name. We leave the RelatedNameLength and
  866. // SeparatorLength at zero if the relative file is opened
  867. // by Id.
  868. //
  869. if (!FlagOn( RelatedCcb->Flags, CCB_FLAG_OPEN_BY_ID )) {
  870. //
  871. // Add a separator if the name length is non-zero
  872. // unless the relative Fcb is at the root.
  873. //
  874. if ((FileName->Length != 0) &&
  875. (RelatedCcb->Fcb != Vcb->RootIndexFcb)) {
  876. SeparatorLength = sizeof( WCHAR );
  877. }
  878. RelatedNameLength = RelatedFileName->Length;
  879. }
  880. //
  881. // The full name is already in the filename. It must either
  882. // be length 0 or begin with a backslash.
  883. //
  884. } else if (FileName->Length != 0) {
  885. if (FileName->Buffer[0] != L'\\') {
  886. return STATUS_INVALID_PARAMETER;
  887. }
  888. //
  889. // We will want to trim the leading backslash from the
  890. // remaining name we return.
  891. //
  892. RemainingNameLength -= sizeof( WCHAR );
  893. SeparatorLength = sizeof( WCHAR );
  894. }
  895. //
  896. // Now see if the buffer is large enough to hold the full name.
  897. //
  898. BufferLength = RelatedNameLength + SeparatorLength + RemainingNameLength;
  899. //
  900. // Check for an overflow of the maximum filename size.
  901. //
  902. if (BufferLength > MAXUSHORT) {
  903. return STATUS_INVALID_PARAMETER;
  904. }
  905. //
  906. // Now see if we need to allocate a new buffer.
  907. //
  908. if (FileName->MaximumLength < BufferLength) {
  909. NewFileName.Buffer = FsRtlAllocatePoolWithTag( CdPagedPool,
  910. BufferLength,
  911. TAG_FILE_NAME );
  912. NewFileName.MaximumLength = (USHORT) BufferLength;
  913. } else {
  914. NewFileName.Buffer = FileName->Buffer;
  915. NewFileName.MaximumLength = FileName->MaximumLength;
  916. }
  917. //
  918. // If there is a related name then we need to slide the remaining bytes up and
  919. // insert the related name. Otherwise the name is in the correct position
  920. // already.
  921. //
  922. if (RelatedNameLength != 0) {
  923. //
  924. // Store the remaining name in its correct position.
  925. //
  926. if (RemainingNameLength != 0) {
  927. RtlMoveMemory( Add2Ptr( NewFileName.Buffer, RelatedNameLength + SeparatorLength, PVOID ),
  928. FileName->Buffer,
  929. RemainingNameLength );
  930. }
  931. RtlCopyMemory( NewFileName.Buffer,
  932. RelatedFileName->Buffer,
  933. RelatedNameLength );
  934. //
  935. // Add the separator if needed.
  936. //
  937. if (SeparatorLength != 0) {
  938. *(Add2Ptr( NewFileName.Buffer, RelatedNameLength, PWCHAR )) = L'\\';
  939. }
  940. //
  941. // Update the filename value we got from the user.
  942. //
  943. if (NewFileName.Buffer != FileName->Buffer) {
  944. if (FileName->Buffer != NULL) {
  945. ExFreePool( FileName->Buffer );
  946. }
  947. FileName->Buffer = NewFileName.Buffer;
  948. FileName->MaximumLength = NewFileName.MaximumLength;
  949. }
  950. //
  951. // Copy the name length to the user's filename.
  952. //
  953. FileName->Length = (USHORT) (RelatedNameLength + SeparatorLength + RemainingNameLength);
  954. }
  955. //
  956. // Now update the remaining name to parse.
  957. //
  958. RemainingName->FileName.MaximumLength =
  959. RemainingName->FileName.Length = (USHORT) RemainingNameLength;
  960. RemainingName->VersionString.Length = 0;
  961. RemainingName->FileName.Buffer = Add2Ptr( FileName->Buffer,
  962. RelatedNameLength + SeparatorLength,
  963. PWCHAR );
  964. //
  965. // Upcase the name if necessary.
  966. //
  967. if (IgnoreCase && (RemainingNameLength != 0)) {
  968. CdUpcaseName( IrpContext,
  969. RemainingName,
  970. RemainingName );
  971. }
  972. //
  973. // Do a quick check to make sure there are no wildcards.
  974. //
  975. if (FsRtlDoesNameContainWildCards( &RemainingName->FileName )) {
  976. return STATUS_OBJECT_NAME_INVALID;
  977. }
  978. //
  979. // For the open by file Id case we verify the name really contains
  980. // a 64 bit value.
  981. //
  982. } else {
  983. //
  984. // Check for validity of the buffer.
  985. //
  986. if (FileName->Length != sizeof( FILE_ID )) {
  987. return STATUS_INVALID_PARAMETER;
  988. }
  989. }
  990. SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_FULL_NAME );
  991. //
  992. // If we are in the retry path then the full name is already in the
  993. // file object name. If this is a case-sensitive operation then
  994. // we need to upcase the name from the end of any related file name already stored
  995. // there.
  996. //
  997. } else {
  998. //
  999. // Assume there is no relative name.
  1000. //
  1001. RemainingName->FileName = *FileName;
  1002. RemainingName->VersionString.Length = 0;
  1003. //
  1004. // Nothing to do if the name length is zero.
  1005. //
  1006. if (RemainingName->FileName.Length != 0) {
  1007. //
  1008. // If there is a relative name then we need to walk past it.
  1009. //
  1010. if (RelatedTypeOfOpen != UnopenedFileObject) {
  1011. //
  1012. // Nothing to walk past if the RelatedCcb is opened by FileId.
  1013. //
  1014. if (!FlagOn( RelatedCcb->Flags, CCB_FLAG_OPEN_BY_ID )) {
  1015. //
  1016. // Related file name is a proper prefix of the full name.
  1017. // We step over the related name and if we are then
  1018. // pointing at a separator character we step over that.
  1019. //
  1020. RemainingName->FileName.Buffer = Add2Ptr( RemainingName->FileName.Buffer,
  1021. RelatedFileName->Length,
  1022. PWCHAR );
  1023. RemainingName->FileName.Length -= RelatedFileName->Length;
  1024. }
  1025. }
  1026. //
  1027. // If we are pointing at a separator character then step past that.
  1028. //
  1029. if (RemainingName->FileName.Length != 0) {
  1030. if (*(RemainingName->FileName.Buffer) == L'\\') {
  1031. RemainingName->FileName.Buffer = Add2Ptr( RemainingName->FileName.Buffer,
  1032. sizeof( WCHAR ),
  1033. PWCHAR );
  1034. RemainingName->FileName.Length -= sizeof( WCHAR );
  1035. }
  1036. }
  1037. }
  1038. //
  1039. // Upcase the name if necessary.
  1040. //
  1041. if (IgnoreCase && (RemainingName->FileName.Length != 0)) {
  1042. CdUpcaseName( IrpContext,
  1043. RemainingName,
  1044. RemainingName );
  1045. }
  1046. }
  1047. return STATUS_SUCCESS;
  1048. }
  1049. //
  1050. // Local support routine
  1051. //
  1052. NTSTATUS
  1053. CdOpenByFileId (
  1054. IN PIRP_CONTEXT IrpContext,
  1055. IN PIO_STACK_LOCATION IrpSp,
  1056. IN PVCB Vcb,
  1057. IN OUT PFCB *CurrentFcb
  1058. )
  1059. /*++
  1060. Routine Description:
  1061. This routine is called to open a file by the FileId. The file Id is in
  1062. the FileObject name buffer and has been verified to be 64 bits.
  1063. We extract the Id number and then check to see whether we are opening a
  1064. file or directory and compare that with the create options. If this
  1065. generates no error then optimistically look up the Fcb in the Fcb Table.
  1066. If we don't find the Fcb then we need to carefully verify there is a file
  1067. at this offset. First check whether the Parent Fcb is in the table. If
  1068. not then lookup the parent at the path table offset given by file ID.
  1069. If found then build the Fcb from this entry and store the new Fcb in the
  1070. tree.
  1071. We know have the parent Fcb. Do a directory scan to find the dirent at
  1072. the given offset in this stream. This must point to the first entry
  1073. of a valid file.
  1074. Finally we call our worker routine to complete the open on this Fcb.
  1075. Arguments:
  1076. IrpSp - Stack location within the create Irp.
  1077. Vcb - Vcb for this volume.
  1078. CurrentFcb - Address to store the Fcb for this open. We only store the
  1079. CurrentFcb here when we have acquired it so our caller knows to
  1080. free or deallocate it.
  1081. Return Value:
  1082. NTSTATUS - Status indicating the result of the operation.
  1083. --*/
  1084. {
  1085. NTSTATUS Status = STATUS_ACCESS_DENIED;
  1086. BOOLEAN UnlockVcb = FALSE;
  1087. BOOLEAN Found;
  1088. ULONG StreamOffset;
  1089. NODE_TYPE_CODE NodeTypeCode;
  1090. TYPE_OF_OPEN TypeOfOpen;
  1091. FILE_ENUM_CONTEXT FileContext;
  1092. BOOLEAN CleanupFileContext = FALSE;
  1093. COMPOUND_PATH_ENTRY CompoundPathEntry;
  1094. BOOLEAN CleanupCompoundPathEntry = FALSE;
  1095. FILE_ID FileId;
  1096. FILE_ID ParentFileId;
  1097. PFCB NextFcb;
  1098. PAGED_CODE();
  1099. //
  1100. // Extract the FileId from the FileObject.
  1101. //
  1102. RtlCopyMemory( &FileId, IrpSp->FileObject->FileName.Buffer, sizeof( FILE_ID ));
  1103. //
  1104. // Use a try-finally to facilitate cleanup.
  1105. //
  1106. try {
  1107. //
  1108. // Go ahead and figure out the TypeOfOpen and NodeType. We can
  1109. // get these from the input FileId.
  1110. //
  1111. if (CdFidIsDirectory( FileId )) {
  1112. TypeOfOpen = UserDirectoryOpen;
  1113. NodeTypeCode = CDFS_NTC_FCB_INDEX;
  1114. //
  1115. // If the offset isn't zero then the file Id is bad.
  1116. //
  1117. if (CdQueryFidDirentOffset( FileId ) != 0) {
  1118. try_return( Status = STATUS_INVALID_PARAMETER );
  1119. }
  1120. } else {
  1121. TypeOfOpen = UserFileOpen;
  1122. NodeTypeCode = CDFS_NTC_FCB_DATA;
  1123. }
  1124. //
  1125. // Acquire the Vcb and check if there is already an Fcb.
  1126. // If not we will need to carefully verify the Fcb.
  1127. // We will post the request if we don't find the Fcb and this
  1128. // request can't wait.
  1129. //
  1130. CdLockVcb( IrpContext, Vcb );
  1131. UnlockVcb = TRUE;
  1132. NextFcb = CdLookupFcbTable( IrpContext, Vcb, FileId );
  1133. if (NextFcb == NULL) {
  1134. //
  1135. // Get the path table offset from the file id.
  1136. //
  1137. StreamOffset = CdQueryFidPathTableOffset( FileId );
  1138. //
  1139. // Build the parent FileId for this and try looking it
  1140. // up in the PathTable.
  1141. //
  1142. CdSetFidDirentOffset( ParentFileId, 0 );
  1143. CdSetFidPathTableOffset( ParentFileId, StreamOffset );
  1144. CdFidSetDirectory( ParentFileId );
  1145. NextFcb = CdLookupFcbTable( IrpContext, Vcb, ParentFileId );
  1146. //
  1147. // If not present then walk through the PathTable to this point.
  1148. //
  1149. if (NextFcb == NULL) {
  1150. CdUnlockVcb( IrpContext, Vcb );
  1151. UnlockVcb = FALSE;
  1152. //
  1153. // Check that the path table offset lies within the path
  1154. // table.
  1155. //
  1156. if (StreamOffset > Vcb->PathTableFcb->FileSize.LowPart) {
  1157. try_return( Status = STATUS_INVALID_PARAMETER );
  1158. }
  1159. CdInitializeCompoundPathEntry( IrpContext, &CompoundPathEntry );
  1160. CleanupCompoundPathEntry = TRUE;
  1161. //
  1162. // Start at the first entry in the PathTable.
  1163. //
  1164. CdLookupPathEntry( IrpContext,
  1165. Vcb->PathTableFcb->StreamOffset,
  1166. 1,
  1167. TRUE,
  1168. &CompoundPathEntry );
  1169. //
  1170. // Continue looking until we have passed our target offset.
  1171. //
  1172. while (TRUE) {
  1173. //
  1174. // Move to the next entry.
  1175. //
  1176. Found = CdLookupNextPathEntry( IrpContext,
  1177. &CompoundPathEntry.PathContext,
  1178. &CompoundPathEntry.PathEntry );
  1179. //
  1180. // If we didn't find the entry or are beyond it then the
  1181. // input Id is invalid.
  1182. //
  1183. if (!Found ||
  1184. (CompoundPathEntry.PathEntry.PathTableOffset > StreamOffset)) {
  1185. try_return( Status = STATUS_INVALID_PARAMETER );
  1186. }
  1187. }
  1188. //
  1189. // If the FileId specified a directory then we have found
  1190. // the entry. Make sure our caller wanted to open a directory.
  1191. //
  1192. if ((TypeOfOpen == UserDirectoryOpen) &&
  1193. FlagOn( IrpSp->Parameters.Create.Options, FILE_NON_DIRECTORY_FILE )) {
  1194. try_return( Status = STATUS_FILE_IS_A_DIRECTORY );
  1195. }
  1196. //
  1197. // Lock the Vcb and create the Fcb if necessary.
  1198. //
  1199. CdLockVcb( IrpContext, Vcb );
  1200. UnlockVcb = TRUE;
  1201. NextFcb = CdCreateFcb( IrpContext, ParentFileId, NodeTypeCode, &Found );
  1202. //
  1203. // It's possible that someone got in here ahead of us.
  1204. //
  1205. if (!Found) {
  1206. CdInitializeFcbFromPathEntry( IrpContext,
  1207. NextFcb,
  1208. NULL,
  1209. &CompoundPathEntry.PathEntry );
  1210. }
  1211. //
  1212. // If the user wanted to open a directory then we have found
  1213. // it. Store this Fcb into the CurrentFcb and skip the
  1214. // directory scan.
  1215. //
  1216. if (TypeOfOpen == UserDirectoryOpen) {
  1217. *CurrentFcb = NextFcb;
  1218. NextFcb = NULL;
  1219. }
  1220. }
  1221. //
  1222. // Perform the directory scan if we don't already have our target.
  1223. //
  1224. if (NextFcb != NULL) {
  1225. //
  1226. // Acquire the parent. We currently own the Vcb lock so
  1227. // do this without waiting first.
  1228. //
  1229. if (!CdAcquireFcbExclusive( IrpContext,
  1230. NextFcb,
  1231. TRUE )) {
  1232. NextFcb->FcbReference += 1;
  1233. CdUnlockVcb( IrpContext, Vcb );
  1234. CdAcquireFcbExclusive( IrpContext, NextFcb, FALSE );
  1235. CdLockVcb( IrpContext, Vcb );
  1236. NextFcb->FcbReference -= 1;
  1237. CdUnlockVcb( IrpContext, Vcb );
  1238. } else {
  1239. CdUnlockVcb( IrpContext, Vcb );
  1240. }
  1241. UnlockVcb = FALSE;
  1242. //
  1243. // Set up the CurrentFcb pointers. We know there was
  1244. // no previous parent in this case.
  1245. //
  1246. *CurrentFcb = NextFcb;
  1247. //
  1248. // Calculate the offset in the stream.
  1249. //
  1250. StreamOffset = CdQueryFidDirentOffset( FileId );
  1251. //
  1252. // Create the stream file if it doesn't exist. This will update
  1253. // the Fcb with the size from the self entry.
  1254. //
  1255. if (NextFcb->FileObject == NULL) {
  1256. CdCreateInternalStream( IrpContext, Vcb, NextFcb );
  1257. }
  1258. //
  1259. // If our offset is beyond the end of the directory then the
  1260. // FileId is invalid.
  1261. //
  1262. if (StreamOffset > NextFcb->FileSize.LowPart) {
  1263. try_return( Status = STATUS_INVALID_PARAMETER );
  1264. }
  1265. //
  1266. // Otherwise position ourselves at the self entry and walk
  1267. // through dirent by dirent until this location is found.
  1268. //
  1269. CdInitializeFileContext( IrpContext, &FileContext );
  1270. CdLookupInitialFileDirent( IrpContext,
  1271. NextFcb,
  1272. &FileContext,
  1273. NextFcb->StreamOffset );
  1274. CleanupFileContext = TRUE;
  1275. while (TRUE) {
  1276. //
  1277. // Move to the first entry of the next file.
  1278. //
  1279. Found = CdLookupNextInitialFileDirent( IrpContext,
  1280. NextFcb,
  1281. &FileContext );
  1282. //
  1283. // If we didn't find the entry or are beyond it then the
  1284. // input Id is invalid.
  1285. //
  1286. if (!Found ||
  1287. (FileContext.InitialDirent->Dirent.DirentOffset > StreamOffset)) {
  1288. try_return( Status = STATUS_INVALID_PARAMETER );
  1289. }
  1290. }
  1291. //
  1292. // This better not be a directory. Directory FileIds must
  1293. // refer to the self entry for directories.
  1294. //
  1295. if (FlagOn( FileContext.InitialDirent->Dirent.DirentFlags,
  1296. CD_ATTRIBUTE_DIRECTORY )) {
  1297. try_return( Status = STATUS_INVALID_PARAMETER );
  1298. }
  1299. //
  1300. // Check that our caller wanted to open a file.
  1301. //
  1302. if (FlagOn( IrpSp->Parameters.Create.Options, FILE_DIRECTORY_FILE )) {
  1303. try_return( Status = STATUS_NOT_A_DIRECTORY );
  1304. }
  1305. //
  1306. // Otherwise we want to collect all of the dirents for this file
  1307. // and create an Fcb with this.
  1308. //
  1309. CdLookupLastFileDirent( IrpContext, NextFcb, &FileContext );
  1310. CdLockVcb( IrpContext, Vcb );
  1311. UnlockVcb = TRUE;
  1312. NextFcb = CdCreateFcb( IrpContext, FileId, NodeTypeCode, &Found );
  1313. //
  1314. // It's possible that someone has since created this Fcb since we
  1315. // first checked. If so then can simply use this. Otherwise
  1316. // we need to initialize a new Fcb and attach it to our parent
  1317. // and insert it into the Fcb Table.
  1318. //
  1319. if (!Found) {
  1320. CdInitializeFcbFromFileContext( IrpContext,
  1321. NextFcb,
  1322. *CurrentFcb,
  1323. &FileContext );
  1324. }
  1325. }
  1326. //
  1327. // We have the Fcb. Check that the type of the file is compatible with
  1328. // the desired type of file to open.
  1329. //
  1330. } else {
  1331. if (FlagOn( NextFcb->FileAttributes, FILE_ATTRIBUTE_DIRECTORY )) {
  1332. if (FlagOn( IrpSp->Parameters.Create.Options, FILE_NON_DIRECTORY_FILE )) {
  1333. try_return( Status = STATUS_FILE_IS_A_DIRECTORY );
  1334. }
  1335. } else if (FlagOn( IrpSp->Parameters.Create.Options, FILE_DIRECTORY_FILE )) {
  1336. try_return( Status = STATUS_NOT_A_DIRECTORY );
  1337. }
  1338. }
  1339. //
  1340. // If we have a the previous Fcb and have inserted the next Fcb into
  1341. // the Fcb Table. It is safe to release the current Fcb if present
  1342. // since it is referenced through the child Fcb.
  1343. //
  1344. if (*CurrentFcb != NULL) {
  1345. CdReleaseFcb( IrpContext, *CurrentFcb );
  1346. }
  1347. //
  1348. // We now know the Fcb and currently hold the Vcb lock.
  1349. // Try to acquire this Fcb without waiting. Otherwise we
  1350. // need to reference it, drop the Vcb, acquire the Fcb and
  1351. // then dereference the Fcb.
  1352. //
  1353. if (!CdAcquireFcbExclusive( IrpContext, NextFcb, TRUE )) {
  1354. NextFcb->FcbReference += 1;
  1355. CdUnlockVcb( IrpContext, Vcb );
  1356. CdAcquireFcbExclusive( IrpContext, NextFcb, FALSE );
  1357. CdLockVcb( IrpContext, Vcb );
  1358. NextFcb->FcbReference -= 1;
  1359. CdUnlockVcb( IrpContext, Vcb );
  1360. } else {
  1361. CdUnlockVcb( IrpContext, Vcb );
  1362. }
  1363. UnlockVcb = FALSE;
  1364. //
  1365. // Move to this Fcb.
  1366. //
  1367. *CurrentFcb = NextFcb;
  1368. //
  1369. // Check the requested access on this Fcb.
  1370. //
  1371. if (!CdIllegalFcbAccess( IrpContext,
  1372. TypeOfOpen,
  1373. IrpSp->Parameters.Create.SecurityContext->DesiredAccess )) {
  1374. //
  1375. // Call our worker routine to complete the open.
  1376. //
  1377. Status = CdCompleteFcbOpen( IrpContext,
  1378. IrpSp,
  1379. Vcb,
  1380. CurrentFcb,
  1381. TypeOfOpen,
  1382. CCB_FLAG_OPEN_BY_ID,
  1383. IrpSp->Parameters.Create.SecurityContext->DesiredAccess );
  1384. }
  1385. try_exit: NOTHING;
  1386. } finally {
  1387. if (UnlockVcb) {
  1388. CdUnlockVcb( IrpContext, Vcb );
  1389. }
  1390. if (CleanupFileContext) {
  1391. CdCleanupFileContext( IrpContext, &FileContext );
  1392. }
  1393. if (CleanupCompoundPathEntry) {
  1394. CdCleanupCompoundPathEntry( IrpContext, &CompoundPathEntry );
  1395. }
  1396. }
  1397. return Status;
  1398. }
  1399. //
  1400. // Local support routine
  1401. //
  1402. NTSTATUS
  1403. CdOpenExistingFcb (
  1404. IN PIRP_CONTEXT IrpContext,
  1405. IN PIO_STACK_LOCATION IrpSp,
  1406. IN OUT PFCB *CurrentFcb,
  1407. IN TYPE_OF_OPEN TypeOfOpen,
  1408. IN BOOLEAN IgnoreCase,
  1409. IN PCCB RelatedCcb OPTIONAL
  1410. )
  1411. /*++
  1412. Routine Description:
  1413. This routine is called to open an Fcb which is already in the Fcb table.
  1414. We will verify the access to the file and then call our worker routine
  1415. to perform the final operations.
  1416. Arguments:
  1417. IrpSp - Pointer to the stack location for this open.
  1418. CurrentFcb - Address of Fcb to open. We will clear this if the Fcb
  1419. is released here.
  1420. TypeOfOpen - Indicates whether we are opening a file, directory or volume.
  1421. IgnoreCase - Indicates if this open is case-insensitive.
  1422. RelatedCcb - Ccb for related file object if relative open. We use
  1423. this when setting the Ccb flags for this open. It will tell
  1424. us whether the name currently in the file object is relative or
  1425. absolute.
  1426. Return Value:
  1427. NTSTATUS - Status indicating the result of the operation.
  1428. --*/
  1429. {
  1430. ULONG CcbFlags = 0;
  1431. NTSTATUS Status = STATUS_ACCESS_DENIED;
  1432. PAGED_CODE();
  1433. //
  1434. // Check that the desired access is legal.
  1435. //
  1436. if (!CdIllegalFcbAccess( IrpContext,
  1437. TypeOfOpen,
  1438. IrpSp->Parameters.Create.SecurityContext->DesiredAccess )) {
  1439. //
  1440. // Set the Ignore case.
  1441. //
  1442. if (IgnoreCase) {
  1443. SetFlag( CcbFlags, CCB_FLAG_IGNORE_CASE );
  1444. }
  1445. //
  1446. // Check the related Ccb to see if this was an OpenByFileId and
  1447. // whether there was a version.
  1448. //
  1449. if (ARGUMENT_PRESENT( RelatedCcb )) {
  1450. SetFlag( CcbFlags, FlagOn( RelatedCcb->Flags, CCB_FLAG_OPEN_WITH_VERSION ));
  1451. if (FlagOn( RelatedCcb->Flags, CCB_FLAG_OPEN_BY_ID | CCB_FLAG_OPEN_RELATIVE_BY_ID )) {
  1452. SetFlag( CcbFlags, CCB_FLAG_OPEN_RELATIVE_BY_ID );
  1453. }
  1454. }
  1455. //
  1456. // Call our worker routine to complete the open.
  1457. //
  1458. Status = CdCompleteFcbOpen( IrpContext,
  1459. IrpSp,
  1460. (*CurrentFcb)->Vcb,
  1461. CurrentFcb,
  1462. TypeOfOpen,
  1463. CcbFlags,
  1464. IrpSp->Parameters.Create.SecurityContext->DesiredAccess );
  1465. }
  1466. return Status;
  1467. }
  1468. //
  1469. // Local support routine
  1470. //
  1471. NTSTATUS
  1472. CdOpenDirectoryFromPathEntry (
  1473. IN PIRP_CONTEXT IrpContext,
  1474. IN PIO_STACK_LOCATION IrpSp,
  1475. IN PVCB Vcb,
  1476. IN OUT PFCB *CurrentFcb,
  1477. IN PCD_NAME DirName,
  1478. IN BOOLEAN IgnoreCase,
  1479. IN BOOLEAN ShortNameMatch,
  1480. IN PPATH_ENTRY PathEntry,
  1481. IN BOOLEAN PerformUserOpen,
  1482. IN PCCB RelatedCcb OPTIONAL
  1483. )
  1484. /*++
  1485. Routine Description:
  1486. This routine is called to open a directory where the directory was found
  1487. in the path table. This routine is called in the case where this is the
  1488. file to open for the user and where this is an intermediate node in the
  1489. full path to open.
  1490. We first check that the desired access is legal for a directory. Then we
  1491. construct the FileId for this and do a check to see if it is the Fcb
  1492. Table. It is always possible that either it was created since or simply
  1493. wasn't in the prefix table at the time of the prefix table search.
  1494. Initialize the Fcb and store into the FcbTable if not present.
  1495. Next we will add this to the prefix table of our parent if needed.
  1496. Once we know that the new Fcb has been initialized then we move our pointer
  1497. in the tree down to this position.
  1498. This routine does not own the Vcb lock on entry. We must be sure to release
  1499. it on exit.
  1500. Arguments:
  1501. IrpSp - Stack location for this request.
  1502. Vcb - Vcb for this volume.
  1503. CurrentFcb - On input this is the parent of the Fcb to open. On output we
  1504. store the Fcb for the file being opened.
  1505. DirName - This is always the exact name used to reach this file.
  1506. IgnoreCase - Indicates the type of case match for the open.
  1507. ShortNameMatch - Indicates if we are opening via the short name.
  1508. PathEntry - Path entry for the entry found.
  1509. PerformUserOpen - TRUE if we are to open this for a user, FALSE otherwise.
  1510. RelatedCcb - RelatedCcb for relative file object used to make this open.
  1511. Return Value:
  1512. NTSTATUS - Status indicating the result of the operation.
  1513. --*/
  1514. {
  1515. ULONG CcbFlags = 0;
  1516. FILE_ID FileId;
  1517. BOOLEAN UnlockVcb = FALSE;
  1518. BOOLEAN FcbExisted;
  1519. PFCB NextFcb;
  1520. PFCB ParentFcb = NULL;
  1521. NTSTATUS Status;
  1522. PAGED_CODE();
  1523. //
  1524. // Check for illegal access to this file.
  1525. //
  1526. if (PerformUserOpen &&
  1527. CdIllegalFcbAccess( IrpContext,
  1528. UserDirectoryOpen,
  1529. IrpSp->Parameters.Create.SecurityContext->DesiredAccess )) {
  1530. return STATUS_ACCESS_DENIED;
  1531. }
  1532. //
  1533. // Use a try-finally to facilitate cleanup.
  1534. //
  1535. try {
  1536. //
  1537. // Check the related Ccb to see if this was an OpenByFileId.
  1538. //
  1539. if (ARGUMENT_PRESENT( RelatedCcb ) &&
  1540. FlagOn( RelatedCcb->Flags, CCB_FLAG_OPEN_BY_ID | CCB_FLAG_OPEN_RELATIVE_BY_ID )) {
  1541. CcbFlags = CCB_FLAG_OPEN_RELATIVE_BY_ID;
  1542. }
  1543. if (IgnoreCase) {
  1544. SetFlag( CcbFlags, CCB_FLAG_IGNORE_CASE );
  1545. }
  1546. //
  1547. // Build the file Id for this file.
  1548. //
  1549. FileId.QuadPart = 0;
  1550. CdSetFidPathTableOffset( FileId, PathEntry->PathTableOffset );
  1551. CdFidSetDirectory( FileId );
  1552. //
  1553. // Lock the Vcb so we can examine the Fcb Table.
  1554. //
  1555. CdLockVcb( IrpContext, Vcb );
  1556. UnlockVcb = TRUE;
  1557. //
  1558. // Get the Fcb for this directory.
  1559. //
  1560. NextFcb = CdCreateFcb( IrpContext, FileId, CDFS_NTC_FCB_INDEX, &FcbExisted );
  1561. //
  1562. // If the Fcb was created here then initialize from the values in the
  1563. // path table entry.
  1564. //
  1565. if (!FcbExisted) {
  1566. CdInitializeFcbFromPathEntry( IrpContext, NextFcb, *CurrentFcb, PathEntry );
  1567. }
  1568. //
  1569. // Now try to acquire the new Fcb without waiting. We will reference
  1570. // the Fcb and retry with wait if unsuccessful.
  1571. //
  1572. if (!CdAcquireFcbExclusive( IrpContext, NextFcb, TRUE )) {
  1573. NextFcb->FcbReference += 1;
  1574. CdUnlockVcb( IrpContext, Vcb );
  1575. CdReleaseFcb( IrpContext, *CurrentFcb );
  1576. CdAcquireFcbExclusive( IrpContext, NextFcb, FALSE );
  1577. CdAcquireFcbExclusive( IrpContext, *CurrentFcb, FALSE );
  1578. CdLockVcb( IrpContext, Vcb );
  1579. NextFcb->FcbReference -= 1;
  1580. CdUnlockVcb( IrpContext, Vcb );
  1581. } else {
  1582. //
  1583. // Unlock the Vcb and move down to this new Fcb. Remember that we still
  1584. // own the parent however.
  1585. //
  1586. CdUnlockVcb( IrpContext, Vcb );
  1587. }
  1588. UnlockVcb = FALSE;
  1589. ParentFcb = *CurrentFcb;
  1590. *CurrentFcb = NextFcb;
  1591. //
  1592. // Store this name into the prefix table for the parent.
  1593. //
  1594. if (ShortNameMatch) {
  1595. //
  1596. // Make sure the exact case is always in the tree.
  1597. //
  1598. CdInsertPrefix( IrpContext,
  1599. NextFcb,
  1600. DirName,
  1601. FALSE,
  1602. TRUE,
  1603. ParentFcb );
  1604. if (IgnoreCase) {
  1605. CdInsertPrefix( IrpContext,
  1606. NextFcb,
  1607. DirName,
  1608. TRUE,
  1609. TRUE,
  1610. ParentFcb );
  1611. }
  1612. } else {
  1613. //
  1614. // Make sure the exact case is always in the tree.
  1615. //
  1616. CdInsertPrefix( IrpContext,
  1617. NextFcb,
  1618. &PathEntry->CdDirName,
  1619. FALSE,
  1620. FALSE,
  1621. ParentFcb );
  1622. if (IgnoreCase) {
  1623. CdInsertPrefix( IrpContext,
  1624. NextFcb,
  1625. &PathEntry->CdCaseDirName,
  1626. TRUE,
  1627. FALSE,
  1628. ParentFcb );
  1629. }
  1630. }
  1631. //
  1632. // Release the parent Fcb at this point.
  1633. //
  1634. CdReleaseFcb( IrpContext, ParentFcb );
  1635. ParentFcb = NULL;
  1636. //
  1637. // Call our worker routine to complete the open.
  1638. //
  1639. if (PerformUserOpen) {
  1640. Status = CdCompleteFcbOpen( IrpContext,
  1641. IrpSp,
  1642. Vcb,
  1643. CurrentFcb,
  1644. UserDirectoryOpen,
  1645. CcbFlags,
  1646. IrpSp->Parameters.Create.SecurityContext->DesiredAccess );
  1647. }
  1648. } finally {
  1649. //
  1650. // Unlock the Vcb if held.
  1651. //
  1652. if (UnlockVcb) {
  1653. CdUnlockVcb( IrpContext, Vcb );
  1654. }
  1655. //
  1656. // Release the parent if held.
  1657. //
  1658. if (ParentFcb != NULL) {
  1659. CdReleaseFcb( IrpContext, ParentFcb );
  1660. }
  1661. }
  1662. return Status;
  1663. }
  1664. //
  1665. // Local support routine
  1666. //
  1667. NTSTATUS
  1668. CdOpenFileFromFileContext (
  1669. IN PIRP_CONTEXT IrpContext,
  1670. IN PIO_STACK_LOCATION IrpSp,
  1671. IN PVCB Vcb,
  1672. IN OUT PFCB *CurrentFcb,
  1673. IN PCD_NAME FileName,
  1674. IN BOOLEAN IgnoreCase,
  1675. IN BOOLEAN ShortNameMatch,
  1676. IN PFILE_ENUM_CONTEXT FileContext,
  1677. IN PCCB RelatedCcb OPTIONAL
  1678. )
  1679. /*++
  1680. Routine Description:
  1681. This routine is called to open a file where the file was found in a directory scan.
  1682. This should only be for a file in the case since we will find the directories in the
  1683. path table.
  1684. We first check that the desired access is legal for this file. Then we
  1685. construct the FileId for this and do a check to see if it is the Fcb
  1686. Table. It is always possible that either it was created since or simply
  1687. wasn't in the prefix table at the time of the prefix table search.
  1688. Initialize the Fcb and store into the FcbTable if not present.
  1689. Next we will add this to the prefix table of our parent if needed.
  1690. Once we know that the new Fcb has been initialized then we move our pointer
  1691. in the tree down to this position.
  1692. This routine does not own the Vcb lock on entry. We must be sure to release
  1693. it on exit.
  1694. Arguments:
  1695. IrpSp - Stack location for this request.
  1696. Vcb - Vcb for the current volume.
  1697. CurrentFcb - On input this is the parent of the Fcb to open. On output we
  1698. store the Fcb for the file being opened.
  1699. FileName - This is always the exact name used to reach this file.
  1700. IgnoreCase - Indicates the type of case of CaseName above.
  1701. ShortNameMatch - Indicates if we are opening via the short name.
  1702. FileContext - This is the context used to find the file.
  1703. RelatedCcb - RelatedCcb for relative file object used to make this open.
  1704. Return Value:
  1705. NTSTATUS - Status indicating the result of the operation.
  1706. --*/
  1707. {
  1708. ULONG CcbFlags = 0;
  1709. FILE_ID FileId;
  1710. BOOLEAN UnlockVcb = FALSE;
  1711. BOOLEAN FcbExisted;
  1712. PFCB NextFcb;
  1713. PFCB ParentFcb = NULL;
  1714. NTSTATUS Status;
  1715. PAGED_CODE();
  1716. //
  1717. // Check for illegal access to this file.
  1718. //
  1719. if (CdIllegalFcbAccess( IrpContext,
  1720. UserFileOpen,
  1721. IrpSp->Parameters.Create.SecurityContext->DesiredAccess )) {
  1722. return STATUS_ACCESS_DENIED;
  1723. }
  1724. //
  1725. // Use a try-finally to facilitate cleanup.
  1726. //
  1727. try {
  1728. //
  1729. // Check if a version number was used to open this file.
  1730. //
  1731. if (FileName->VersionString.Length != 0) {
  1732. SetFlag( CcbFlags, CCB_FLAG_OPEN_WITH_VERSION );
  1733. }
  1734. //
  1735. // Check the related Ccb to see if this was an OpenByFileId.
  1736. //
  1737. if (ARGUMENT_PRESENT( RelatedCcb ) &&
  1738. FlagOn( RelatedCcb->Flags, CCB_FLAG_OPEN_BY_ID | CCB_FLAG_OPEN_RELATIVE_BY_ID )) {
  1739. SetFlag( CcbFlags, CCB_FLAG_OPEN_RELATIVE_BY_ID );
  1740. }
  1741. if (IgnoreCase) {
  1742. SetFlag( CcbFlags, CCB_FLAG_IGNORE_CASE );
  1743. }
  1744. //
  1745. // Build the file Id for this file. We can use the path table offset from the
  1746. // parent and the directory offset from the dirent.
  1747. //
  1748. CdSetFidPathTableOffset( FileId, CdQueryFidPathTableOffset( (*CurrentFcb)->FileId ));
  1749. CdSetFidDirentOffset( FileId, FileContext->InitialDirent->Dirent.DirentOffset );
  1750. //
  1751. // Lock the Vcb so we can examine the Fcb Table.
  1752. //
  1753. CdLockVcb( IrpContext, Vcb );
  1754. UnlockVcb = TRUE;
  1755. //
  1756. // Get the Fcb for this file.
  1757. //
  1758. NextFcb = CdCreateFcb( IrpContext, FileId, CDFS_NTC_FCB_DATA, &FcbExisted );
  1759. //
  1760. // If the Fcb was created here then initialize from the values in the
  1761. // dirent.
  1762. //
  1763. if (!FcbExisted) {
  1764. CdInitializeFcbFromFileContext( IrpContext,
  1765. NextFcb,
  1766. *CurrentFcb,
  1767. FileContext );
  1768. }
  1769. //
  1770. // Now try to acquire the new Fcb without waiting. We will reference
  1771. // the Fcb and retry with wait if unsuccessful.
  1772. //
  1773. if (!CdAcquireFcbExclusive( IrpContext, NextFcb, TRUE )) {
  1774. NextFcb->FcbReference += 1;
  1775. CdUnlockVcb( IrpContext, Vcb );
  1776. CdReleaseFcb( IrpContext, *CurrentFcb );
  1777. CdAcquireFcbExclusive( IrpContext, NextFcb, FALSE );
  1778. CdAcquireFcbExclusive( IrpContext, *CurrentFcb, FALSE );
  1779. CdLockVcb( IrpContext, Vcb );
  1780. NextFcb->FcbReference -= 1;
  1781. CdUnlockVcb( IrpContext, Vcb );
  1782. } else {
  1783. //
  1784. // Unlock the Vcb and move down to this new Fcb. Remember that we still
  1785. // own the parent however.
  1786. //
  1787. CdUnlockVcb( IrpContext, Vcb );
  1788. }
  1789. UnlockVcb = FALSE;
  1790. ParentFcb = *CurrentFcb;
  1791. *CurrentFcb = NextFcb;
  1792. //
  1793. // Store this name into the prefix table for the parent.
  1794. //
  1795. if (ShortNameMatch) {
  1796. //
  1797. // Make sure the exact case is always in the tree.
  1798. //
  1799. CdInsertPrefix( IrpContext,
  1800. NextFcb,
  1801. FileName,
  1802. FALSE,
  1803. TRUE,
  1804. ParentFcb );
  1805. if (IgnoreCase) {
  1806. CdInsertPrefix( IrpContext,
  1807. NextFcb,
  1808. FileName,
  1809. TRUE,
  1810. TRUE,
  1811. ParentFcb );
  1812. }
  1813. //
  1814. // Insert this into the prefix table if we found this without
  1815. // using a version string.
  1816. //
  1817. } else if (FileName->VersionString.Length == 0) {
  1818. //
  1819. // Make sure the exact case is always in the tree.
  1820. //
  1821. CdInsertPrefix( IrpContext,
  1822. NextFcb,
  1823. &FileContext->InitialDirent->Dirent.CdFileName,
  1824. FALSE,
  1825. FALSE,
  1826. ParentFcb );
  1827. if (IgnoreCase) {
  1828. CdInsertPrefix( IrpContext,
  1829. NextFcb,
  1830. &FileContext->InitialDirent->Dirent.CdCaseFileName,
  1831. TRUE,
  1832. FALSE,
  1833. ParentFcb );
  1834. }
  1835. }
  1836. //
  1837. // Release the parent Fcb at this point.
  1838. //
  1839. CdReleaseFcb( IrpContext, ParentFcb );
  1840. ParentFcb = NULL;
  1841. //
  1842. // Call our worker routine to complete the open.
  1843. //
  1844. Status = CdCompleteFcbOpen( IrpContext,
  1845. IrpSp,
  1846. Vcb,
  1847. CurrentFcb,
  1848. UserFileOpen,
  1849. CcbFlags,
  1850. IrpSp->Parameters.Create.SecurityContext->DesiredAccess );
  1851. } finally {
  1852. //
  1853. // Unlock the Vcb if held.
  1854. //
  1855. if (UnlockVcb) {
  1856. CdUnlockVcb( IrpContext, Vcb );
  1857. }
  1858. //
  1859. // Release the parent if held.
  1860. //
  1861. if (ParentFcb != NULL) {
  1862. CdReleaseFcb( IrpContext, ParentFcb );
  1863. }
  1864. }
  1865. return Status;
  1866. }
  1867. //
  1868. // Local support routine
  1869. //
  1870. NTSTATUS
  1871. CdCompleteFcbOpen (
  1872. IN PIRP_CONTEXT IrpContext,
  1873. PIO_STACK_LOCATION IrpSp,
  1874. IN PVCB Vcb,
  1875. IN OUT PFCB *CurrentFcb,
  1876. IN TYPE_OF_OPEN TypeOfOpen,
  1877. IN ULONG UserCcbFlags,
  1878. IN ACCESS_MASK DesiredAccess
  1879. )
  1880. /*++
  1881. Routine Description:
  1882. This is the worker routine which takes an existing Fcb and completes
  1883. the open. We will do any necessary oplock checks and sharing checks.
  1884. Finally we will create the Ccb and update the file object and any
  1885. file object flags.
  1886. Arguments:
  1887. IrpSp - Stack location for the current request.
  1888. Vcb - Vcb for the current volume.
  1889. CurrentFcb - Address of pointer to Fcb to open. We clear this field if
  1890. we release the resource for this file.
  1891. TypeOfOpen - Type of open for this request.
  1892. UserCcbFlags - Flags to OR into the Ccb flags.
  1893. DesiredAccess - Desired access for this open.
  1894. Return Value:
  1895. NTSTATUS - STATUS_SUCCESS if we complete this request, STATUS_PENDING if
  1896. the oplock package takes the Irp or SHARING_VIOLATION if there is a
  1897. sharing check conflict.
  1898. --*/
  1899. {
  1900. NTSTATUS Status;
  1901. NTSTATUS OplockStatus = STATUS_SUCCESS;
  1902. ULONG Information = FILE_OPENED;
  1903. BOOLEAN LockVolume = FALSE;
  1904. PFCB Fcb = *CurrentFcb;
  1905. PCCB Ccb;
  1906. PAGED_CODE();
  1907. //
  1908. // Expand maximum allowed to something sensible for share access checking
  1909. //
  1910. if (MAXIMUM_ALLOWED == DesiredAccess) {
  1911. DesiredAccess = FILE_ALL_ACCESS & ~((TypeOfOpen != UserVolumeOpen ?
  1912. (FILE_WRITE_ATTRIBUTES |
  1913. FILE_WRITE_DATA |
  1914. FILE_WRITE_EA |
  1915. FILE_ADD_FILE |
  1916. FILE_ADD_SUBDIRECTORY |
  1917. FILE_APPEND_DATA) : 0) |
  1918. FILE_DELETE_CHILD |
  1919. DELETE |
  1920. WRITE_DAC );
  1921. }
  1922. //
  1923. // If this a volume open and the user wants to lock the volume then
  1924. // purge and lock the volume.
  1925. //
  1926. if ((TypeOfOpen <= UserVolumeOpen) &&
  1927. !FlagOn( IrpSp->Parameters.Create.ShareAccess, FILE_SHARE_READ )) {
  1928. //
  1929. // If there are open handles then fail this immediately.
  1930. //
  1931. if (Vcb->VcbCleanup != 0) {
  1932. return STATUS_SHARING_VIOLATION;
  1933. }
  1934. //
  1935. // If we can't wait then force this to be posted.
  1936. //
  1937. if (!FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT )) {
  1938. CdRaiseStatus( IrpContext, STATUS_CANT_WAIT );
  1939. }
  1940. LockVolume = TRUE;
  1941. //
  1942. // Purge the volume and make sure all of the user references
  1943. // are gone.
  1944. //
  1945. Status = CdPurgeVolume( IrpContext, Vcb, FALSE );
  1946. if (Status != STATUS_SUCCESS) {
  1947. return Status;
  1948. }
  1949. //
  1950. // Now force all of the delayed close operations to go away.
  1951. //
  1952. CdFspClose( Vcb );
  1953. if (Vcb->VcbUserReference > CDFS_RESIDUAL_USER_REFERENCE) {
  1954. return STATUS_SHARING_VIOLATION;
  1955. }
  1956. }
  1957. //
  1958. // If the Fcb already existed then we need to check the oplocks and
  1959. // the share access.
  1960. //
  1961. if (Fcb->FcbCleanup != 0) {
  1962. //
  1963. // If this is a user file open then check whether there are any
  1964. // batch oplock.
  1965. //
  1966. if (TypeOfOpen == UserFileOpen) {
  1967. //
  1968. // Store the address of the Fcb for a possible teardown into
  1969. // the IrpContext. We will release this in the call to
  1970. // prepost the Irp.
  1971. //
  1972. IrpContext->TeardownFcb = CurrentFcb;
  1973. if (FsRtlCurrentBatchOplock( &Fcb->Oplock )) {
  1974. //
  1975. // We remember if a batch oplock break is underway for the
  1976. // case where the sharing check fails.
  1977. //
  1978. Information = FILE_OPBATCH_BREAK_UNDERWAY;
  1979. OplockStatus = FsRtlCheckOplock( &Fcb->Oplock,
  1980. IrpContext->Irp,
  1981. IrpContext,
  1982. CdOplockComplete,
  1983. CdPrePostIrp );
  1984. if (OplockStatus == STATUS_PENDING) {
  1985. return STATUS_PENDING;
  1986. }
  1987. }
  1988. //
  1989. // Check the share access before breaking any exclusive oplocks.
  1990. //
  1991. Status = IoCheckShareAccess( DesiredAccess,
  1992. IrpSp->Parameters.Create.ShareAccess,
  1993. IrpSp->FileObject,
  1994. &Fcb->ShareAccess,
  1995. FALSE );
  1996. if (!NT_SUCCESS( Status )) {
  1997. return Status;
  1998. }
  1999. //
  2000. // Now check that we can continue based on the oplock state of the
  2001. // file.
  2002. //
  2003. OplockStatus = FsRtlCheckOplock( &Fcb->Oplock,
  2004. IrpContext->Irp,
  2005. IrpContext,
  2006. CdOplockComplete,
  2007. CdPrePostIrp );
  2008. if (OplockStatus == STATUS_PENDING) {
  2009. return STATUS_PENDING;
  2010. }
  2011. IrpContext->TeardownFcb = NULL;
  2012. //
  2013. // Otherwise just do the sharing check.
  2014. //
  2015. } else {
  2016. Status = IoCheckShareAccess( DesiredAccess,
  2017. IrpSp->Parameters.Create.ShareAccess,
  2018. IrpSp->FileObject,
  2019. &Fcb->ShareAccess,
  2020. FALSE );
  2021. if (!NT_SUCCESS( Status )) {
  2022. return Status;
  2023. }
  2024. }
  2025. }
  2026. //
  2027. // Create the Ccb now.
  2028. //
  2029. Ccb = CdCreateCcb( IrpContext, Fcb, UserCcbFlags );
  2030. //
  2031. // Update the share access.
  2032. //
  2033. if (Fcb->FcbCleanup == 0) {
  2034. IoSetShareAccess( DesiredAccess,
  2035. IrpSp->Parameters.Create.ShareAccess,
  2036. IrpSp->FileObject,
  2037. &Fcb->ShareAccess );
  2038. } else {
  2039. IoUpdateShareAccess( IrpSp->FileObject, &Fcb->ShareAccess );
  2040. }
  2041. //
  2042. // Set the file object type.
  2043. //
  2044. CdSetFileObject( IrpContext, IrpSp->FileObject, TypeOfOpen, Fcb, Ccb );
  2045. //
  2046. // Set the appropriate cache flags for a user file object.
  2047. //
  2048. if (TypeOfOpen == UserFileOpen) {
  2049. if (FlagOn( IrpSp->Parameters.Create.Options, FILE_NO_INTERMEDIATE_BUFFERING )) {
  2050. SetFlag( IrpSp->FileObject->Flags, FO_NO_INTERMEDIATE_BUFFERING );
  2051. } else {
  2052. SetFlag( IrpSp->FileObject->Flags, FO_CACHE_SUPPORTED );
  2053. }
  2054. }
  2055. //
  2056. // Update the open and cleanup counts. Check the fast io state here.
  2057. //
  2058. CdLockVcb( IrpContext, Vcb );
  2059. CdIncrementCleanupCounts( IrpContext, Fcb );
  2060. CdIncrementReferenceCounts( IrpContext, Fcb, 1, 1 );
  2061. if (LockVolume) {
  2062. Vcb->VolumeLockFileObject = IrpSp->FileObject;
  2063. SetFlag( Vcb->VcbState, VCB_STATE_LOCKED );
  2064. }
  2065. CdUnlockVcb( IrpContext, Vcb );
  2066. CdLockFcb( IrpContext, Fcb );
  2067. if (TypeOfOpen == UserFileOpen) {
  2068. Fcb->IsFastIoPossible = CdIsFastIoPossible( Fcb );
  2069. } else {
  2070. Fcb->IsFastIoPossible = FastIoIsNotPossible;
  2071. }
  2072. CdUnlockFcb( IrpContext, Fcb );
  2073. //
  2074. // Show that we opened the file.
  2075. //
  2076. IrpContext->Irp->IoStatus.Information = Information;
  2077. //
  2078. // Point to the section object pointer in the non-paged Fcb.
  2079. //
  2080. IrpSp->FileObject->SectionObjectPointer = &Fcb->FcbNonpaged->SegmentObject;
  2081. return OplockStatus;
  2082. }