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.

4639 lines
129 KiB

  1. /*++
  2. Copyright (c) 1989-2000 Microsoft Corporation
  3. Module Name:
  4. FileInfo.c
  5. Abstract:
  6. This module implements the File Information routines for Fat called by
  7. the dispatch driver.
  8. // @@BEGIN_DDKSPLIT
  9. Author:
  10. Gary Kimura [GaryKi] 22-Oct-1990
  11. Revision History:
  12. // @@END_DDKSPLIT
  13. --*/
  14. #include "FatProcs.h"
  15. //
  16. // The Bug check file id for this module
  17. //
  18. #define BugCheckFileId (FAT_BUG_CHECK_FILEINFO)
  19. //
  20. // The local debug trace level
  21. //
  22. #define Dbg (DEBUG_TRACE_FILEINFO)
  23. VOID
  24. FatQueryBasicInfo (
  25. IN PIRP_CONTEXT IrpContext,
  26. IN PFCB Fcb,
  27. IN PFILE_OBJECT FileObject,
  28. IN OUT PFILE_BASIC_INFORMATION Buffer,
  29. IN OUT PLONG Length
  30. );
  31. VOID
  32. FatQueryStandardInfo (
  33. IN PIRP_CONTEXT IrpContext,
  34. IN PFCB Fcb,
  35. IN OUT PFILE_STANDARD_INFORMATION Buffer,
  36. IN OUT PLONG Length
  37. );
  38. VOID
  39. FatQueryInternalInfo (
  40. IN PIRP_CONTEXT IrpContext,
  41. IN PFCB Fcb,
  42. IN OUT PFILE_INTERNAL_INFORMATION Buffer,
  43. IN OUT PLONG Length
  44. );
  45. VOID
  46. FatQueryEaInfo (
  47. IN PIRP_CONTEXT IrpContext,
  48. IN PFCB Fcb,
  49. IN OUT PFILE_EA_INFORMATION Buffer,
  50. IN OUT PLONG Length
  51. );
  52. VOID
  53. FatQueryPositionInfo (
  54. IN PIRP_CONTEXT IrpContext,
  55. IN PFILE_OBJECT FileObject,
  56. IN OUT PFILE_POSITION_INFORMATION Buffer,
  57. IN OUT PLONG Length
  58. );
  59. VOID
  60. FatQueryNameInfo (
  61. IN PIRP_CONTEXT IrpContext,
  62. IN PFCB Fcb,
  63. IN PCCB Ccb,
  64. IN OUT PFILE_NAME_INFORMATION Buffer,
  65. IN OUT PLONG Length
  66. );
  67. VOID
  68. FatQueryShortNameInfo (
  69. IN PIRP_CONTEXT IrpContext,
  70. IN PFCB Fcb,
  71. IN OUT PFILE_NAME_INFORMATION Buffer,
  72. IN OUT PLONG Length
  73. );
  74. VOID
  75. FatQueryNetworkInfo (
  76. IN PIRP_CONTEXT IrpContext,
  77. IN PFCB Fcb,
  78. IN PFILE_OBJECT FileObject,
  79. IN OUT PFILE_NETWORK_OPEN_INFORMATION Buffer,
  80. IN OUT PLONG Length
  81. );
  82. NTSTATUS
  83. FatSetBasicInfo (
  84. IN PIRP_CONTEXT IrpContext,
  85. IN PIRP Irp,
  86. IN PFCB Fcb,
  87. IN PCCB Ccb
  88. );
  89. NTSTATUS
  90. FatSetDispositionInfo (
  91. IN PIRP_CONTEXT IrpContext,
  92. IN PIRP Irp,
  93. IN PFILE_OBJECT FileObject,
  94. IN PFCB Fcb
  95. );
  96. NTSTATUS
  97. FatSetRenameInfo (
  98. IN PIRP_CONTEXT IrpContext,
  99. IN PIRP Irp,
  100. IN PVCB Vcb,
  101. IN PFCB Fcb,
  102. IN PCCB Ccb
  103. );
  104. NTSTATUS
  105. FatSetPositionInfo (
  106. IN PIRP_CONTEXT IrpContext,
  107. IN PIRP Irp,
  108. IN PFILE_OBJECT FileObject
  109. );
  110. NTSTATUS
  111. FatSetAllocationInfo (
  112. IN PIRP_CONTEXT IrpContext,
  113. IN PIRP Irp,
  114. IN PFCB Fcb,
  115. IN PFILE_OBJECT FileObject
  116. );
  117. NTSTATUS
  118. FatSetEndOfFileInfo (
  119. IN PIRP_CONTEXT IrpContext,
  120. IN PIRP Irp,
  121. IN PFILE_OBJECT FileObject,
  122. IN PVCB Vcb,
  123. IN PFCB Fcb
  124. );
  125. VOID
  126. FatDeleteFile (
  127. IN PIRP_CONTEXT IrpContext,
  128. IN PDCB TargetDcb,
  129. IN ULONG LfnOffset,
  130. IN ULONG DirentOffset,
  131. IN PDIRENT Dirent,
  132. IN PUNICODE_STRING Lfn
  133. );
  134. VOID
  135. FatRenameEAs (
  136. IN PIRP_CONTEXT IrpContext,
  137. IN PFCB Fcb,
  138. IN USHORT ExtendedAttributes,
  139. IN POEM_STRING OldOemName
  140. );
  141. #ifdef ALLOC_PRAGMA
  142. #pragma alloc_text(PAGE, FatCommonQueryInformation)
  143. #pragma alloc_text(PAGE, FatCommonSetInformation)
  144. #pragma alloc_text(PAGE, FatFsdQueryInformation)
  145. #pragma alloc_text(PAGE, FatFsdSetInformation)
  146. #pragma alloc_text(PAGE, FatQueryBasicInfo)
  147. #pragma alloc_text(PAGE, FatQueryEaInfo)
  148. #pragma alloc_text(PAGE, FatQueryInternalInfo)
  149. #pragma alloc_text(PAGE, FatQueryNameInfo)
  150. #pragma alloc_text(PAGE, FatQueryNetworkInfo)
  151. #pragma alloc_text(PAGE, FatQueryShortNameInfo)
  152. #pragma alloc_text(PAGE, FatQueryPositionInfo)
  153. #pragma alloc_text(PAGE, FatQueryStandardInfo)
  154. #pragma alloc_text(PAGE, FatSetAllocationInfo)
  155. #pragma alloc_text(PAGE, FatSetBasicInfo)
  156. #pragma alloc_text(PAGE, FatSetDispositionInfo)
  157. #pragma alloc_text(PAGE, FatSetEndOfFileInfo)
  158. #pragma alloc_text(PAGE, FatSetPositionInfo)
  159. #pragma alloc_text(PAGE, FatSetRenameInfo)
  160. #pragma alloc_text(PAGE, FatDeleteFile)
  161. #pragma alloc_text(PAGE, FatRenameEAs)
  162. #endif
  163. NTSTATUS
  164. FatFsdQueryInformation (
  165. IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject,
  166. IN PIRP Irp
  167. )
  168. /*++
  169. Routine Description:
  170. This routine implements the Fsd part of the NtQueryInformationFile API
  171. call.
  172. Arguments:
  173. VolumeDeviceObject - Supplies the volume device object where the file
  174. being queried exists.
  175. Irp - Supplies the Irp being processed.
  176. Return Value:
  177. NTSTATUS - The FSD status for the Irp.
  178. --*/
  179. {
  180. NTSTATUS Status;
  181. PIRP_CONTEXT IrpContext = NULL;
  182. BOOLEAN TopLevel;
  183. DebugTrace(+1, Dbg, "FatFsdQueryInformation\n", 0);
  184. //
  185. // Call the common query routine, with blocking allowed if synchronous
  186. //
  187. FsRtlEnterFileSystem();
  188. TopLevel = FatIsIrpTopLevel( Irp );
  189. try {
  190. IrpContext = FatCreateIrpContext( Irp, CanFsdWait( Irp ) );
  191. Status = FatCommonQueryInformation( IrpContext, Irp );
  192. } except(FatExceptionFilter( IrpContext, GetExceptionInformation() )) {
  193. //
  194. // We had some trouble trying to perform the requested
  195. // operation, so we'll abort the I/O request with
  196. // the error status that we get back from the
  197. // execption code
  198. //
  199. Status = FatProcessException( IrpContext, Irp, GetExceptionCode() );
  200. }
  201. if (TopLevel) { IoSetTopLevelIrp( NULL ); }
  202. FsRtlExitFileSystem();
  203. //
  204. // And return to our caller
  205. //
  206. DebugTrace(-1, Dbg, "FatFsdQueryInformation -> %08lx\n", Status);
  207. UNREFERENCED_PARAMETER( VolumeDeviceObject );
  208. return Status;
  209. }
  210. NTSTATUS
  211. FatFsdSetInformation (
  212. IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject,
  213. IN PIRP Irp
  214. )
  215. /*++
  216. Routine Description:
  217. This routine implements the FSD part of the NtSetInformationFile API
  218. call.
  219. Arguments:
  220. VolumeDeviceObject - Supplies the volume device object where the file
  221. being set exists.
  222. Irp - Supplies the Irp being processed.
  223. Return Value:
  224. NTSTATUS - The FSD status for the Irp.
  225. --*/
  226. {
  227. NTSTATUS Status;
  228. PIRP_CONTEXT IrpContext = NULL;
  229. BOOLEAN TopLevel;
  230. DebugTrace(+1, Dbg, "FatFsdSetInformation\n", 0);
  231. //
  232. // Call the common set routine, with blocking allowed if synchronous
  233. //
  234. FsRtlEnterFileSystem();
  235. TopLevel = FatIsIrpTopLevel( Irp );
  236. try {
  237. IrpContext = FatCreateIrpContext( Irp, CanFsdWait( Irp ) );
  238. Status = FatCommonSetInformation( IrpContext, Irp );
  239. } except(FatExceptionFilter( IrpContext, GetExceptionInformation() )) {
  240. //
  241. // We had some trouble trying to perform the requested
  242. // operation, so we'll abort the I/O request with
  243. // the error status that we get back from the
  244. // execption code
  245. //
  246. Status = FatProcessException( IrpContext, Irp, GetExceptionCode() );
  247. }
  248. if (TopLevel) { IoSetTopLevelIrp( NULL ); }
  249. FsRtlExitFileSystem();
  250. //
  251. // And return to our caller
  252. //
  253. DebugTrace(-1, Dbg, "FatFsdSetInformation -> %08lx\n", Status);
  254. UNREFERENCED_PARAMETER( VolumeDeviceObject );
  255. return Status;
  256. }
  257. NTSTATUS
  258. FatCommonQueryInformation (
  259. IN PIRP_CONTEXT IrpContext,
  260. IN PIRP Irp
  261. )
  262. /*++
  263. Routine Description:
  264. This is the common routine for querying file information called by both
  265. the fsd and fsp threads.
  266. Arguments:
  267. Irp - Supplies the Irp being processed
  268. Return Value:
  269. NTSTATUS - The return status for the operation
  270. --*/
  271. {
  272. NTSTATUS Status;
  273. PIO_STACK_LOCATION IrpSp;
  274. PFILE_OBJECT FileObject;
  275. LONG Length;
  276. FILE_INFORMATION_CLASS FileInformationClass;
  277. PVOID Buffer;
  278. TYPE_OF_OPEN TypeOfOpen;
  279. PVCB Vcb;
  280. PFCB Fcb;
  281. PCCB Ccb;
  282. BOOLEAN FcbAcquired = FALSE;
  283. BOOLEAN VcbAcquired = FALSE;
  284. PFILE_ALL_INFORMATION AllInfo;
  285. //
  286. // Get the current stack location
  287. //
  288. IrpSp = IoGetCurrentIrpStackLocation( Irp );
  289. FileObject = IrpSp->FileObject;
  290. DebugTrace(+1, Dbg, "FatCommonQueryInformation...\n", 0);
  291. DebugTrace( 0, Dbg, "Irp = %08lx\n", Irp);
  292. DebugTrace( 0, Dbg, "->Length = %08lx\n", IrpSp->Parameters.QueryFile.Length);
  293. DebugTrace( 0, Dbg, "->FileInformationClass = %08lx\n", IrpSp->Parameters.QueryFile.FileInformationClass);
  294. DebugTrace( 0, Dbg, "->Buffer = %08lx\n", Irp->AssociatedIrp.SystemBuffer);
  295. //
  296. // Reference our input parameters to make things easier
  297. //
  298. Length = (LONG)IrpSp->Parameters.QueryFile.Length;
  299. FileInformationClass = IrpSp->Parameters.QueryFile.FileInformationClass;
  300. Buffer = Irp->AssociatedIrp.SystemBuffer;
  301. //
  302. // Decode the file object
  303. //
  304. TypeOfOpen = FatDecodeFileObject( FileObject, &Vcb, &Fcb, &Ccb );
  305. Status = STATUS_SUCCESS;
  306. try {
  307. //
  308. // Case on the type of open we're dealing with
  309. //
  310. switch (TypeOfOpen) {
  311. case UserVolumeOpen:
  312. //
  313. // We cannot query the user volume open.
  314. //
  315. Status = STATUS_INVALID_PARAMETER;
  316. break;
  317. case UserFileOpen:
  318. case UserDirectoryOpen:
  319. case DirectoryFile:
  320. //
  321. // NameInfo requires synchronization with deletion in order to perform
  322. // the full filename query. A lighter-weight way to do this would be per
  323. // directory as the full name is built up and since the multiple Fcb
  324. // lockorder is bottom up, this is conceivable. At this time, though,
  325. // this change is safer.
  326. //
  327. if (FileInformationClass == FileNameInformation ||
  328. FileInformationClass == FileAllInformation) {
  329. if (!FatAcquireExclusiveVcb( IrpContext, Vcb )) {
  330. DebugTrace(0, Dbg, "Cannot acquire Vcb\n", 0);
  331. Status = FatFsdPostRequest( IrpContext, Irp );
  332. IrpContext = NULL;
  333. Irp = NULL;
  334. try_return( Status );
  335. }
  336. VcbAcquired = TRUE;
  337. }
  338. //
  339. // Acquire shared access to the fcb, except for a paging file
  340. // in order to avoid deadlocks with Mm.
  341. //
  342. if (!FlagOn( Fcb->FcbState, FCB_STATE_PAGING_FILE )) {
  343. if (!FatAcquireSharedFcb( IrpContext, Fcb )) {
  344. DebugTrace(0, Dbg, "Cannot acquire Fcb\n", 0);
  345. Status = FatFsdPostRequest( IrpContext, Irp );
  346. IrpContext = NULL;
  347. Irp = NULL;
  348. try_return( Status );
  349. }
  350. FcbAcquired = TRUE;
  351. }
  352. //
  353. // Make sure the Fcb is in a usable condition. This
  354. // will raise an error condition if the fcb is unusable
  355. //
  356. FatVerifyFcb( IrpContext, Fcb );
  357. //
  358. // Based on the information class we'll do different
  359. // actions. Each of hte procedures that we're calling fills
  360. // up the output buffer, if possible. They will raise the
  361. // status STATUS_BUFFER_OVERFLOW for an insufficient buffer.
  362. // This is considered a somewhat unusual case and is handled
  363. // more cleanly with the exception mechanism rather than
  364. // testing a return status value for each call.
  365. //
  366. switch (FileInformationClass) {
  367. case FileAllInformation:
  368. //
  369. // For the all information class we'll typecast a local
  370. // pointer to the output buffer and then call the
  371. // individual routines to fill in the buffer.
  372. //
  373. AllInfo = Buffer;
  374. Length -= (sizeof(FILE_ACCESS_INFORMATION)
  375. + sizeof(FILE_MODE_INFORMATION)
  376. + sizeof(FILE_ALIGNMENT_INFORMATION));
  377. FatQueryBasicInfo( IrpContext, Fcb, FileObject, &AllInfo->BasicInformation, &Length );
  378. FatQueryStandardInfo( IrpContext, Fcb, &AllInfo->StandardInformation, &Length );
  379. FatQueryInternalInfo( IrpContext, Fcb, &AllInfo->InternalInformation, &Length );
  380. FatQueryEaInfo( IrpContext, Fcb, &AllInfo->EaInformation, &Length );
  381. FatQueryPositionInfo( IrpContext, FileObject, &AllInfo->PositionInformation, &Length );
  382. FatQueryNameInfo( IrpContext, Fcb, Ccb, &AllInfo->NameInformation, &Length );
  383. break;
  384. case FileBasicInformation:
  385. FatQueryBasicInfo( IrpContext, Fcb, FileObject, Buffer, &Length );
  386. break;
  387. case FileStandardInformation:
  388. FatQueryStandardInfo( IrpContext, Fcb, Buffer, &Length );
  389. break;
  390. case FileInternalInformation:
  391. FatQueryInternalInfo( IrpContext, Fcb, Buffer, &Length );
  392. break;
  393. case FileEaInformation:
  394. FatQueryEaInfo( IrpContext, Fcb, Buffer, &Length );
  395. break;
  396. case FilePositionInformation:
  397. FatQueryPositionInfo( IrpContext, FileObject, Buffer, &Length );
  398. break;
  399. case FileNameInformation:
  400. FatQueryNameInfo( IrpContext, Fcb, Ccb, Buffer, &Length );
  401. break;
  402. case FileAlternateNameInformation:
  403. FatQueryShortNameInfo( IrpContext, Fcb, Buffer, &Length );
  404. break;
  405. case FileNetworkOpenInformation:
  406. FatQueryNetworkInfo( IrpContext, Fcb, FileObject, Buffer, &Length );
  407. break;
  408. default:
  409. Status = STATUS_INVALID_PARAMETER;
  410. break;
  411. }
  412. break;
  413. default:
  414. KdPrintEx((DPFLTR_FASTFAT_ID,
  415. DPFLTR_INFO_LEVEL,
  416. "FATQueryFile, Illegal TypeOfOpen = %08lx\n",
  417. TypeOfOpen));
  418. Status = STATUS_INVALID_PARAMETER;
  419. break;
  420. }
  421. //
  422. // If we overflowed the buffer, set the length to 0 and change the
  423. // status to STATUS_BUFFER_OVERFLOW.
  424. //
  425. if ( Length < 0 ) {
  426. Status = STATUS_BUFFER_OVERFLOW;
  427. Length = 0;
  428. }
  429. //
  430. // Set the information field to the number of bytes actually filled in
  431. // and then complete the request
  432. //
  433. Irp->IoStatus.Information = IrpSp->Parameters.QueryFile.Length - Length;
  434. try_exit: NOTHING;
  435. } finally {
  436. DebugUnwind( FatCommonQueryInformation );
  437. if (FcbAcquired) { FatReleaseFcb( IrpContext, Fcb ); }
  438. if (VcbAcquired) { FatReleaseVcb( IrpContext, Vcb ); }
  439. if (!AbnormalTermination()) {
  440. FatCompleteRequest( IrpContext, Irp, Status );
  441. }
  442. DebugTrace(-1, Dbg, "FatCommonQueryInformation -> %08lx\n", Status);
  443. }
  444. return Status;
  445. }
  446. NTSTATUS
  447. FatCommonSetInformation (
  448. IN PIRP_CONTEXT IrpContext,
  449. IN PIRP Irp
  450. )
  451. /*++
  452. Routine Description:
  453. This is the common routine for setting file information called by both
  454. the fsd and fsp threads.
  455. Arguments:
  456. Irp - Supplies the Irp being processed
  457. Return Value:
  458. NTSTATUS - The return status for the operation
  459. --*/
  460. {
  461. NTSTATUS Status;
  462. PIO_STACK_LOCATION IrpSp;
  463. PFILE_OBJECT FileObject;
  464. FILE_INFORMATION_CLASS FileInformationClass;
  465. TYPE_OF_OPEN TypeOfOpen;
  466. PVCB Vcb;
  467. PFCB Fcb;
  468. PCCB Ccb;
  469. BOOLEAN VcbAcquired = FALSE;
  470. BOOLEAN FcbAcquired = FALSE;
  471. //
  472. // Get the current stack location
  473. //
  474. IrpSp = IoGetCurrentIrpStackLocation( Irp );
  475. DebugTrace(+1, Dbg, "FatCommonSetInformation...\n", 0);
  476. DebugTrace( 0, Dbg, "Irp = %08lx\n", Irp);
  477. DebugTrace( 0, Dbg, "->Length = %08lx\n", IrpSp->Parameters.SetFile.Length);
  478. DebugTrace( 0, Dbg, "->FileInformationClass = %08lx\n", IrpSp->Parameters.SetFile.FileInformationClass);
  479. DebugTrace( 0, Dbg, "->FileObject = %08lx\n", IrpSp->Parameters.SetFile.FileObject);
  480. DebugTrace( 0, Dbg, "->ReplaceIfExists = %08lx\n", IrpSp->Parameters.SetFile.ReplaceIfExists);
  481. DebugTrace( 0, Dbg, "->Buffer = %08lx\n", Irp->AssociatedIrp.SystemBuffer);
  482. //
  483. // Reference our input parameters to make things easier
  484. //
  485. FileInformationClass = IrpSp->Parameters.SetFile.FileInformationClass;
  486. FileObject = IrpSp->FileObject;
  487. //
  488. // Decode the file object
  489. //
  490. TypeOfOpen = FatDecodeFileObject( FileObject, &Vcb, &Fcb, &Ccb );
  491. try {
  492. //
  493. // Case on the type of open we're dealing with
  494. //
  495. switch (TypeOfOpen) {
  496. case UserVolumeOpen:
  497. //
  498. // We cannot query the user volume open.
  499. //
  500. try_return( Status = STATUS_INVALID_PARAMETER );
  501. break;
  502. case UserFileOpen:
  503. if (!FlagOn( Fcb->FcbState, FCB_STATE_PAGING_FILE ) &&
  504. ((FileInformationClass == FileEndOfFileInformation) ||
  505. (FileInformationClass == FileAllocationInformation))) {
  506. //
  507. // We check whether we can proceed
  508. // based on the state of the file oplocks.
  509. //
  510. Status = FsRtlCheckOplock( &Fcb->Specific.Fcb.Oplock,
  511. Irp,
  512. IrpContext,
  513. NULL,
  514. NULL );
  515. if (Status != STATUS_SUCCESS) {
  516. try_return( Status );
  517. }
  518. //
  519. // Set the flag indicating if Fast I/O is possible
  520. //
  521. Fcb->Header.IsFastIoPossible = FatIsFastIoPossible( Fcb );
  522. }
  523. break;
  524. case UserDirectoryOpen:
  525. break;
  526. default:
  527. try_return( Status = STATUS_INVALID_PARAMETER );
  528. }
  529. //
  530. // We can only do a set on a nonroot dcb, so we do the test
  531. // and then fall through to the user file open code.
  532. //
  533. if (NodeType(Fcb) == FAT_NTC_ROOT_DCB) {
  534. if (FileInformationClass == FileDispositionInformation) {
  535. try_return( Status = STATUS_CANNOT_DELETE );
  536. }
  537. try_return( Status = STATUS_INVALID_PARAMETER );
  538. }
  539. //
  540. // In the following two cases, we cannot have creates occuring
  541. // while we are here, so acquire the volume exclusive.
  542. //
  543. if ((FileInformationClass == FileDispositionInformation) ||
  544. (FileInformationClass == FileRenameInformation)) {
  545. if (!FatAcquireExclusiveVcb( IrpContext, Vcb )) {
  546. DebugTrace(0, Dbg, "Cannot acquire Vcb\n", 0);
  547. Status = FatFsdPostRequest( IrpContext, Irp );
  548. Irp = NULL;
  549. IrpContext = NULL;
  550. try_return( Status );
  551. }
  552. VcbAcquired = TRUE;
  553. }
  554. //
  555. // We need to look here to check whether the oplock state
  556. // will allow us to continue. We may have to loop to prevent
  557. // an oplock being granted between the time we check the oplock
  558. // and obtain the Fcb.
  559. //
  560. //
  561. // Acquire exclusive access to the Fcb, We use exclusive
  562. // because it is probable that one of the subroutines
  563. // that we call will need to monkey with file allocation,
  564. // create/delete extra fcbs. So we're willing to pay the
  565. // cost of exclusive Fcb access.
  566. //
  567. // Note that we do not acquire the resource for paging file
  568. // operations in order to avoid deadlock with Mm.
  569. //
  570. if (!FlagOn( Fcb->FcbState, FCB_STATE_PAGING_FILE )) {
  571. if (!FatAcquireExclusiveFcb( IrpContext, Fcb )) {
  572. DebugTrace(0, Dbg, "Cannot acquire Fcb\n", 0);
  573. Status = FatFsdPostRequest( IrpContext, Irp );
  574. Irp = NULL;
  575. IrpContext = NULL;
  576. try_return( Status );
  577. }
  578. FcbAcquired = TRUE;
  579. }
  580. Status = STATUS_SUCCESS;
  581. //
  582. // Make sure the Fcb is in a usable condition. This
  583. // will raise an error condition if the fcb is unusable
  584. //
  585. FatVerifyFcb( IrpContext, Fcb );
  586. //
  587. // Based on the information class we'll do different
  588. // actions. Each of the procedures that we're calling will either
  589. // complete the request of send the request off to the fsp
  590. // to do the work.
  591. //
  592. switch (FileInformationClass) {
  593. case FileBasicInformation:
  594. Status = FatSetBasicInfo( IrpContext, Irp, Fcb, Ccb );
  595. break;
  596. case FileDispositionInformation:
  597. //
  598. // If this is on deferred flush media, we have to be able to wait.
  599. //
  600. if ( FlagOn(Vcb->VcbState, VCB_STATE_FLAG_DEFERRED_FLUSH) &&
  601. !FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) ) {
  602. Status = FatFsdPostRequest( IrpContext, Irp );
  603. Irp = NULL;
  604. IrpContext = NULL;
  605. } else {
  606. Status = FatSetDispositionInfo( IrpContext, Irp, FileObject, Fcb );
  607. }
  608. break;
  609. case FileRenameInformation:
  610. //
  611. // We proceed with this operation only if we can wait
  612. //
  613. if (!FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT)) {
  614. Status = FatFsdPostRequest( IrpContext, Irp );
  615. Irp = NULL;
  616. IrpContext = NULL;
  617. } else {
  618. Status = FatSetRenameInfo( IrpContext, Irp, Vcb, Fcb, Ccb );
  619. //
  620. // If STATUS_PENDING is returned it means the oplock
  621. // package has the Irp. Don't complete the request here.
  622. //
  623. if (Status == STATUS_PENDING) {
  624. Irp = NULL;
  625. IrpContext = NULL;
  626. }
  627. }
  628. break;
  629. case FilePositionInformation:
  630. Status = FatSetPositionInfo( IrpContext, Irp, FileObject );
  631. break;
  632. case FileLinkInformation:
  633. Status = STATUS_INVALID_DEVICE_REQUEST;
  634. break;
  635. case FileAllocationInformation:
  636. Status = FatSetAllocationInfo( IrpContext, Irp, Fcb, FileObject );
  637. break;
  638. case FileEndOfFileInformation:
  639. Status = FatSetEndOfFileInfo( IrpContext, Irp, FileObject, Vcb, Fcb );
  640. break;
  641. default:
  642. Status = STATUS_INVALID_PARAMETER;
  643. break;
  644. }
  645. if ( IrpContext != NULL ) {
  646. FatUnpinRepinnedBcbs( IrpContext );
  647. }
  648. try_exit: NOTHING;
  649. } finally {
  650. DebugUnwind( FatCommonSetInformation );
  651. if (FcbAcquired) { FatReleaseFcb( IrpContext, Fcb ); }
  652. if (VcbAcquired) { FatReleaseVcb( IrpContext, Vcb ); }
  653. if (!AbnormalTermination()) {
  654. FatCompleteRequest( IrpContext, Irp, Status );
  655. }
  656. DebugTrace(-1, Dbg, "FatCommonSetInformation -> %08lx\n", Status);
  657. }
  658. return Status;
  659. }
  660. //
  661. // Internal Support Routine
  662. //
  663. VOID
  664. FatQueryBasicInfo (
  665. IN PIRP_CONTEXT IrpContext,
  666. IN PFCB Fcb,
  667. IN PFILE_OBJECT FileObject,
  668. IN OUT PFILE_BASIC_INFORMATION Buffer,
  669. IN OUT PLONG Length
  670. )
  671. /*++
  672. Description:
  673. This routine performs the query basic information function for fat.
  674. Arguments:
  675. Fcb - Supplies the Fcb being queried, it has been verified
  676. FileObject - Supplies the flag bit that indicates the file was modified.
  677. Buffer - Supplies a pointer to the buffer where the information is to
  678. be returned
  679. Length - Supplies the length of the buffer in bytes, and receives the
  680. remaining bytes free in the buffer upon return.
  681. Return Value:
  682. None
  683. --*/
  684. {
  685. DebugTrace(+1, Dbg, "FatQueryBasicInfo...\n", 0);
  686. //
  687. // Zero out the output buffer, and set it to indicate that
  688. // the query is a normal file. Later we might overwrite the
  689. // attribute.
  690. //
  691. RtlZeroMemory( Buffer, sizeof(FILE_BASIC_INFORMATION) );
  692. //
  693. // Extract the data and fill in the non zero fields of the output
  694. // buffer
  695. //
  696. if (Fcb->Header.NodeTypeCode == FAT_NTC_ROOT_DCB) {
  697. //
  698. // We have to munge a lie on the fly. Every time we have to
  699. // use 1/1/80 we need to convert to GMT since the TZ may have
  700. // changed on us.
  701. //
  702. ExLocalTimeToSystemTime( &FatJanOne1980,
  703. &Buffer->LastWriteTime );
  704. Buffer->CreationTime = Buffer->LastAccessTime = Buffer->LastWriteTime;
  705. } else {
  706. Buffer->LastWriteTime = Fcb->LastWriteTime;
  707. Buffer->CreationTime = Fcb->CreationTime;
  708. Buffer->LastAccessTime = Fcb->LastAccessTime;
  709. }
  710. Buffer->FileAttributes = Fcb->DirentFatFlags;
  711. //
  712. // If the temporary flag is set, then set it in the buffer.
  713. //
  714. if (FlagOn( Fcb->FcbState, FCB_STATE_TEMPORARY )) {
  715. SetFlag( Buffer->FileAttributes, FILE_ATTRIBUTE_TEMPORARY );
  716. }
  717. //
  718. // If no attributes were set, set the normal bit.
  719. //
  720. if (Buffer->FileAttributes == 0) {
  721. Buffer->FileAttributes = FILE_ATTRIBUTE_NORMAL;
  722. }
  723. //
  724. // Update the length and status output variables
  725. //
  726. *Length -= sizeof( FILE_BASIC_INFORMATION );
  727. DebugTrace( 0, Dbg, "*Length = %08lx\n", *Length);
  728. DebugTrace(-1, Dbg, "FatQueryBasicInfo -> VOID\n", 0);
  729. return;
  730. }
  731. //
  732. // Internal Support Routine
  733. //
  734. VOID
  735. FatQueryStandardInfo (
  736. IN PIRP_CONTEXT IrpContext,
  737. IN PFCB Fcb,
  738. IN OUT PFILE_STANDARD_INFORMATION Buffer,
  739. IN OUT PLONG Length
  740. )
  741. /*++
  742. Routine Description:
  743. This routine performs the query standard information function for fat.
  744. Arguments:
  745. Fcb - Supplies the Fcb being queried, it has been verified
  746. Buffer - Supplies a pointer to the buffer where the information is to
  747. be returned
  748. Length - Supplies the length of the buffer in bytes, and receives the
  749. remaining bytes free in the buffer upon return.
  750. Return Value:
  751. None
  752. --*/
  753. {
  754. DebugTrace(+1, Dbg, "FatQueryStandardInfo...\n", 0);
  755. //
  756. // Zero out the output buffer, and fill in the number of links
  757. // and the delete pending flag.
  758. //
  759. RtlZeroMemory( Buffer, sizeof(FILE_STANDARD_INFORMATION) );
  760. Buffer->NumberOfLinks = 1;
  761. Buffer->DeletePending = BooleanFlagOn( Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE );
  762. //
  763. // Case on whether this is a file or a directory, and extract
  764. // the information and fill in the fcb/dcb specific parts
  765. // of the output buffer
  766. //
  767. if (NodeType(Fcb) == FAT_NTC_FCB) {
  768. if (Fcb->Header.AllocationSize.QuadPart == FCB_LOOKUP_ALLOCATIONSIZE_HINT) {
  769. FatLookupFileAllocationSize( IrpContext, Fcb );
  770. }
  771. Buffer->AllocationSize = Fcb->Header.AllocationSize;
  772. Buffer->EndOfFile = Fcb->Header.FileSize;
  773. Buffer->Directory = FALSE;
  774. } else {
  775. Buffer->Directory = TRUE;
  776. }
  777. //
  778. // Update the length and status output variables
  779. //
  780. *Length -= sizeof( FILE_STANDARD_INFORMATION );
  781. DebugTrace( 0, Dbg, "*Length = %08lx\n", *Length);
  782. DebugTrace(-1, Dbg, "FatQueryStandardInfo -> VOID\n", 0);
  783. return;
  784. }
  785. //
  786. // Internal Support Routine
  787. //
  788. VOID
  789. FatQueryInternalInfo (
  790. IN PIRP_CONTEXT IrpContext,
  791. IN PFCB Fcb,
  792. IN OUT PFILE_INTERNAL_INFORMATION Buffer,
  793. IN OUT PLONG Length
  794. )
  795. /*++
  796. Routine Description:
  797. This routine performs the query internal information function for fat.
  798. Arguments:
  799. Fcb - Supplies the Fcb being queried, it has been verified
  800. Buffer - Supplies a pointer to the buffer where the information is to
  801. be returned
  802. Length - Supplies the length of the buffer in bytes, and receives the
  803. remaining bytes free in the buffer upon return.
  804. Return Value:
  805. None
  806. --*/
  807. {
  808. DebugTrace(+1, Dbg, "FatQueryInternalInfo...\n", 0);
  809. try {
  810. Buffer->IndexNumber.QuadPart = FatGenerateFileIdFromFcb( Fcb );
  811. //
  812. // Update the length and status output variables
  813. //
  814. *Length -= sizeof( FILE_INTERNAL_INFORMATION );
  815. } finally {
  816. DebugUnwind( FatQueryInternalInfo );
  817. DebugTrace( 0, Dbg, "*Length = %08lx\n", *Length);
  818. DebugTrace(-1, Dbg, "FatQueryInternalInfo -> VOID\n", 0);
  819. }
  820. return;
  821. }
  822. //
  823. // Internal Support Routine
  824. //
  825. VOID
  826. FatQueryEaInfo (
  827. IN PIRP_CONTEXT IrpContext,
  828. IN PFCB Fcb,
  829. IN OUT PFILE_EA_INFORMATION Buffer,
  830. IN OUT PLONG Length
  831. )
  832. /*++
  833. Routine Description:
  834. This routine performs the query Ea information function for fat.
  835. Arguments:
  836. Fcb - Supplies the Fcb being queried, it has been verified
  837. Buffer - Supplies a pointer to the buffer where the information is to
  838. be returned
  839. Length - Supplies the length of the buffer in bytes, and receives the
  840. remaining bytes free in the buffer upon return.
  841. Return Value:
  842. None
  843. --*/
  844. {
  845. PBCB Bcb;
  846. DebugTrace(+1, Dbg, "FatQueryEaInfo...\n", 0);
  847. Bcb = NULL;
  848. try {
  849. //
  850. // Zero out the output buffer
  851. //
  852. RtlZeroMemory( Buffer, sizeof(FILE_EA_INFORMATION) );
  853. //
  854. // The Root dcb does not have any EAs so don't look for any. Fat32
  855. // doesn't have any, either.
  856. //
  857. if ( NodeType( Fcb ) != FAT_NTC_ROOT_DCB &&
  858. !FatIsFat32( Fcb->Vcb )) {
  859. PDIRENT Dirent;
  860. //
  861. // Try to get the dirent for this file.
  862. //
  863. FatGetDirentFromFcbOrDcb( IrpContext,
  864. Fcb,
  865. &Dirent,
  866. &Bcb );
  867. if (Dirent != NULL) {
  868. //
  869. // Get a the size needed to store the full eas for the file.
  870. //
  871. FatGetEaLength( IrpContext,
  872. Fcb->Vcb,
  873. Dirent,
  874. &Buffer->EaSize );
  875. }
  876. }
  877. //
  878. // Update the length and status output variables
  879. //
  880. *Length -= sizeof( FILE_EA_INFORMATION );
  881. } finally {
  882. DebugUnwind( FatQueryEaInfo );
  883. //
  884. // Unpin the dirent if pinned.
  885. //
  886. FatUnpinBcb( IrpContext, Bcb );
  887. DebugTrace( 0, Dbg, "*Length = %08lx\n", *Length);
  888. DebugTrace(-1, Dbg, "FatQueryEaInfo -> VOID\n", 0);
  889. }
  890. return;
  891. }
  892. //
  893. // Internal Support Routine
  894. //
  895. VOID
  896. FatQueryPositionInfo (
  897. IN PIRP_CONTEXT IrpContext,
  898. IN PFILE_OBJECT FileObject,
  899. IN OUT PFILE_POSITION_INFORMATION Buffer,
  900. IN OUT PLONG Length
  901. )
  902. /*++
  903. Routine Description:
  904. This routine performs the query position information function for fat.
  905. Arguments:
  906. FileObject - Supplies the File object being queried
  907. Buffer - Supplies a pointer to the buffer where the information is to
  908. be returned
  909. Length - Supplies the length of the buffer in bytes, and receives the
  910. remaining bytes free in the buffer upon return.
  911. Return Value:
  912. None
  913. --*/
  914. {
  915. DebugTrace(+1, Dbg, "FatQueryPositionInfo...\n", 0);
  916. //
  917. // Get the current position found in the file object.
  918. //
  919. Buffer->CurrentByteOffset = FileObject->CurrentByteOffset;
  920. //
  921. // Update the length and status output variables
  922. //
  923. *Length -= sizeof( FILE_POSITION_INFORMATION );
  924. DebugTrace( 0, Dbg, "*Length = %08lx\n", *Length);
  925. DebugTrace(-1, Dbg, "FatQueryPositionInfo -> VOID\n", 0);
  926. UNREFERENCED_PARAMETER( IrpContext );
  927. return;
  928. }
  929. //
  930. // Internal Support Routine
  931. //
  932. VOID
  933. FatQueryNameInfo (
  934. IN PIRP_CONTEXT IrpContext,
  935. IN PFCB Fcb,
  936. IN PCCB Ccb,
  937. IN OUT PFILE_NAME_INFORMATION Buffer,
  938. IN OUT PLONG Length
  939. )
  940. /*++
  941. Routine Description:
  942. This routine performs the query name information function for fat.
  943. Arguments:
  944. Fcb - Supplies the Fcb being queried, it has been verified
  945. Ccb - Supplies the Ccb for the context of the user open
  946. Buffer - Supplies a pointer to the buffer where the information is to
  947. be returned
  948. Length - Supplies the length of the buffer in bytes, and receives the
  949. remaining bytes free in the buffer upon return.
  950. Return Value:
  951. None
  952. --*/
  953. {
  954. ULONG BytesToCopy;
  955. LONG TrimLength;
  956. BOOLEAN Overflow = FALSE;
  957. DebugTrace(+1, Dbg, "FatQueryNameInfo...\n", 0);
  958. //
  959. // Convert the name to UNICODE
  960. //
  961. *Length -= FIELD_OFFSET(FILE_NAME_INFORMATION, FileName[0]);
  962. //
  963. // Use the full filename to build the path up. If we wanted to be
  964. // slick in the future, we'd just build the path directly into the
  965. // return buffer and avoid constructing the full filename, but since
  966. // the full filename winds up being required so often lets not
  967. // over optimize this case yet.
  968. //
  969. if (Fcb->FullFileName.Buffer == NULL) {
  970. FatSetFullFileNameInFcb( IrpContext, Fcb );
  971. }
  972. //
  973. // Here is where it gets a smidge tricky. FinalNameLength is the length
  974. // of the LFN element if it exists, and since the long name is always used
  975. // to build FullFileName, we have two cases:
  976. //
  977. // 1) short name: use FinalNameLength to tear off the path from FullFileName
  978. // and append the UNICODE converted short name.
  979. // 2) long name: just use FullFileName
  980. //
  981. // We bias to the name the user thinks they opened by. This winds
  982. // up fixing some oddball tunneling cases where intermediate filters
  983. // translate operations like delete into renames - this lets them
  984. // do the operation in the context of the name the user was using.
  985. //
  986. // It also matches what NTFS does, and so we have the definition of
  987. // correct behavior.
  988. //
  989. //
  990. //
  991. // Assume there is no long name and we are just going to use
  992. // FullFileName.
  993. //
  994. TrimLength = 0;
  995. //
  996. // If a LongName exists and the original open was by the short name
  997. // then set TrimLength to point to the place where the short name goes.
  998. //
  999. //
  1000. // Note: The Ccb can be NULL. The lazy writer calls to get the name of
  1001. // a DirectoryOpen FILE_OBJECT that it wants to display in the lost
  1002. // delayed write popup. Handle this case by just using the FileFullName.
  1003. //
  1004. if (Fcb->LongName.Unicode.Name.Unicode.Buffer != NULL) {
  1005. if ((Ccb != NULL) && FlagOn(Ccb->Flags, CCB_FLAG_OPENED_BY_SHORTNAME)) {
  1006. TrimLength = Fcb->FinalNameLength;
  1007. }
  1008. }
  1009. if (*Length < Fcb->FullFileName.Length - TrimLength) {
  1010. BytesToCopy = *Length;
  1011. Overflow = TRUE;
  1012. } else {
  1013. BytesToCopy = Fcb->FullFileName.Length - TrimLength;
  1014. *Length -= BytesToCopy;
  1015. }
  1016. RtlCopyMemory( &Buffer->FileName[0],
  1017. Fcb->FullFileName.Buffer,
  1018. BytesToCopy );
  1019. //
  1020. // Note that this is just the amount of name we've copied so far. It'll
  1021. // either be all of it (long) or the path element including the \ (short).
  1022. //
  1023. Buffer->FileNameLength = Fcb->FullFileName.Length - TrimLength;
  1024. //
  1025. // If we trimmed off the name element, this is the short name case. Pick
  1026. // up the UNICODE conversion and append it.
  1027. //
  1028. if (TrimLength != 0) {
  1029. UNICODE_STRING ShortName;
  1030. WCHAR ShortNameBuffer[12];
  1031. NTSTATUS Status;
  1032. //
  1033. // Convert the short name to UNICODE and figure out how much
  1034. // of it can fit. Again, we always bump the returned length
  1035. // to indicate how much is available even if we can't return it.
  1036. //
  1037. ShortName.Length = 0;
  1038. ShortName.MaximumLength = sizeof(ShortNameBuffer);
  1039. ShortName.Buffer = ShortNameBuffer;
  1040. Status = RtlOemStringToCountedUnicodeString( &ShortName,
  1041. &Fcb->ShortName.Name.Oem,
  1042. FALSE );
  1043. ASSERT( Status == STATUS_SUCCESS );
  1044. if (!Overflow) {
  1045. if (*Length < ShortName.Length) {
  1046. BytesToCopy = *Length;
  1047. Overflow = TRUE;
  1048. } else {
  1049. BytesToCopy = ShortName.Length;
  1050. *Length -= BytesToCopy;
  1051. }
  1052. RtlCopyMemory( (PUCHAR)&Buffer->FileName[0] + Buffer->FileNameLength,
  1053. ShortName.Buffer,
  1054. BytesToCopy );
  1055. }
  1056. Buffer->FileNameLength += ShortName.Length;
  1057. }
  1058. if (Overflow) {
  1059. *Length = -1;
  1060. }
  1061. //
  1062. // Return to caller
  1063. //
  1064. DebugTrace( 0, Dbg, "*Length = %08lx\n", *Length);
  1065. DebugTrace(-1, Dbg, "FatQueryNameInfo -> VOID\n", 0);
  1066. UNREFERENCED_PARAMETER( IrpContext );
  1067. return;
  1068. }
  1069. //
  1070. // Internal Support Routine
  1071. //
  1072. VOID
  1073. FatQueryShortNameInfo (
  1074. IN PIRP_CONTEXT IrpContext,
  1075. IN PFCB Fcb,
  1076. IN OUT PFILE_NAME_INFORMATION Buffer,
  1077. IN OUT PLONG Length
  1078. )
  1079. /*++
  1080. Routine Description:
  1081. This routine queries the short name of the file.
  1082. Arguments:
  1083. Fcb - Supplies the Fcb being queried, it has been verified
  1084. Buffer - Supplies a pointer to the buffer where the information is to
  1085. be returned
  1086. Length - Supplies the length of the buffer in bytes, and receives the
  1087. remaining bytes free in the buffer upon return.
  1088. Return Value:
  1089. None
  1090. --*/
  1091. {
  1092. NTSTATUS Status;
  1093. ULONG BytesToCopy;
  1094. WCHAR ShortNameBuffer[12];
  1095. UNICODE_STRING ShortName;
  1096. DebugTrace(+1, Dbg, "FatQueryNameInfo...\n", 0);
  1097. //
  1098. // Convert the name to UNICODE
  1099. //
  1100. ShortName.Length = 0;
  1101. ShortName.MaximumLength = sizeof(ShortNameBuffer);
  1102. ShortName.Buffer = ShortNameBuffer;
  1103. *Length -= FIELD_OFFSET(FILE_NAME_INFORMATION, FileName[0]);
  1104. Status = RtlOemStringToCountedUnicodeString( &ShortName,
  1105. &Fcb->ShortName.Name.Oem,
  1106. FALSE );
  1107. ASSERT( Status == STATUS_SUCCESS );
  1108. //
  1109. // If we overflow, set *Length to -1 as a flag.
  1110. //
  1111. if (*Length < ShortName.Length) {
  1112. BytesToCopy = *Length;
  1113. *Length = -1;
  1114. } else {
  1115. BytesToCopy = ShortName.Length;
  1116. *Length -= ShortName.Length;
  1117. }
  1118. RtlCopyMemory( &Buffer->FileName[0],
  1119. &ShortName.Buffer[0],
  1120. BytesToCopy );
  1121. Buffer->FileNameLength = ShortName.Length;
  1122. //
  1123. // Return to caller
  1124. //
  1125. DebugTrace( 0, Dbg, "*Length = %08lx\n", *Length);
  1126. DebugTrace(-1, Dbg, "FatQueryNameInfo -> VOID\n", 0);
  1127. UNREFERENCED_PARAMETER( IrpContext );
  1128. return;
  1129. }
  1130. //
  1131. // Internal Support Routine
  1132. //
  1133. VOID
  1134. FatQueryNetworkInfo (
  1135. IN PIRP_CONTEXT IrpContext,
  1136. IN PFCB Fcb,
  1137. IN PFILE_OBJECT FileObject,
  1138. IN OUT PFILE_NETWORK_OPEN_INFORMATION Buffer,
  1139. IN OUT PLONG Length
  1140. )
  1141. /*++
  1142. Description:
  1143. This routine performs the query network open information function for fat.
  1144. Arguments:
  1145. Fcb - Supplies the Fcb being queried, it has been verified
  1146. FileObject - Supplies the flag bit that indicates the file was modified.
  1147. Buffer - Supplies a pointer to the buffer where the information is to
  1148. be returned
  1149. Length - Supplies the length of the buffer in bytes, and receives the
  1150. remaining bytes free in the buffer upon return.
  1151. Return Value:
  1152. None
  1153. --*/
  1154. {
  1155. DebugTrace(+1, Dbg, "FatQueryNetworkInfo...\n", 0);
  1156. //
  1157. // Zero out the output buffer, and set it to indicate that
  1158. // the query is a normal file. Later we might overwrite the
  1159. // attribute.
  1160. //
  1161. RtlZeroMemory( Buffer, sizeof(FILE_NETWORK_OPEN_INFORMATION) );
  1162. //
  1163. // Extract the data and fill in the non zero fields of the output
  1164. // buffer
  1165. //
  1166. if (Fcb->Header.NodeTypeCode == FAT_NTC_ROOT_DCB) {
  1167. //
  1168. // We have to munge a lie on the fly. Every time we have to
  1169. // use 1/1/80 we need to convert to GMT since the TZ may have
  1170. // changed on us.
  1171. //
  1172. ExLocalTimeToSystemTime( &FatJanOne1980,
  1173. &Buffer->LastWriteTime );
  1174. Buffer->CreationTime = Buffer->LastAccessTime = Buffer->LastWriteTime;
  1175. } else {
  1176. Buffer->LastWriteTime.QuadPart = Fcb->LastWriteTime.QuadPart;
  1177. Buffer->CreationTime.QuadPart = Fcb->CreationTime.QuadPart;
  1178. Buffer->LastAccessTime.QuadPart = Fcb->LastAccessTime.QuadPart;
  1179. }
  1180. Buffer->FileAttributes = Fcb->DirentFatFlags;
  1181. //
  1182. // If the temporary flag is set, then set it in the buffer.
  1183. //
  1184. if (FlagOn( Fcb->FcbState, FCB_STATE_TEMPORARY )) {
  1185. SetFlag( Buffer->FileAttributes, FILE_ATTRIBUTE_TEMPORARY );
  1186. }
  1187. //
  1188. // If no attributes were set, set the normal bit.
  1189. //
  1190. if (Buffer->FileAttributes == 0) {
  1191. Buffer->FileAttributes = FILE_ATTRIBUTE_NORMAL;
  1192. }
  1193. //
  1194. // Case on whether this is a file or a directory, and extract
  1195. // the information and fill in the fcb/dcb specific parts
  1196. // of the output buffer
  1197. //
  1198. if (NodeType(Fcb) == FAT_NTC_FCB) {
  1199. if (Fcb->Header.AllocationSize.QuadPart == FCB_LOOKUP_ALLOCATIONSIZE_HINT) {
  1200. FatLookupFileAllocationSize( IrpContext, Fcb );
  1201. }
  1202. Buffer->AllocationSize.QuadPart = Fcb->Header.AllocationSize.QuadPart;
  1203. Buffer->EndOfFile.QuadPart = Fcb->Header.FileSize.QuadPart;
  1204. }
  1205. //
  1206. // Update the length and status output variables
  1207. //
  1208. *Length -= sizeof( FILE_NETWORK_OPEN_INFORMATION );
  1209. DebugTrace( 0, Dbg, "*Length = %08lx\n", *Length);
  1210. DebugTrace(-1, Dbg, "FatQueryNetworkInfo -> VOID\n", 0);
  1211. return;
  1212. }
  1213. //
  1214. // Internal Support routine
  1215. //
  1216. NTSTATUS
  1217. FatSetBasicInfo (
  1218. IN PIRP_CONTEXT IrpContext,
  1219. IN PIRP Irp,
  1220. IN PFCB Fcb,
  1221. IN PCCB Ccb
  1222. )
  1223. /*++
  1224. Routine Description:
  1225. This routine performs the set basic information for fat. It either
  1226. completes the request or enqueues it off to the fsp.
  1227. Arguments:
  1228. Irp - Supplies the irp being processed
  1229. Fcb - Supplies the Fcb or Dcb being processed, already known not to
  1230. be the root dcb
  1231. Ccb - Supplies the flag bit that control updating the last modify
  1232. time on cleanup.
  1233. Return Value:
  1234. NTSTATUS - The result of this operation if it completes without
  1235. an exception.
  1236. --*/
  1237. {
  1238. NTSTATUS Status;
  1239. PFILE_BASIC_INFORMATION Buffer;
  1240. PDIRENT Dirent;
  1241. PBCB DirentBcb;
  1242. FAT_TIME_STAMP CreationTime;
  1243. UCHAR CreationMSec;
  1244. FAT_TIME_STAMP LastWriteTime;
  1245. FAT_TIME_STAMP LastAccessTime;
  1246. FAT_DATE LastAccessDate;
  1247. UCHAR Attributes;
  1248. BOOLEAN ModifyCreation = FALSE;
  1249. BOOLEAN ModifyLastWrite = FALSE;
  1250. BOOLEAN ModifyLastAccess = FALSE;
  1251. LARGE_INTEGER LargeCreationTime;
  1252. LARGE_INTEGER LargeLastWriteTime;
  1253. LARGE_INTEGER LargeLastAccessTime;
  1254. ULONG NotifyFilter = 0;
  1255. DebugTrace(+1, Dbg, "FatSetBasicInfo...\n", 0);
  1256. Buffer = Irp->AssociatedIrp.SystemBuffer;
  1257. //
  1258. // If the user is specifying -1 for a field, that means
  1259. // we should leave that field unchanged, even if we might
  1260. // have otherwise set it ourselves. We'll set the Ccb flag
  1261. // saying that the user set the field so that we
  1262. // don't do our default updating.
  1263. //
  1264. // We set the field to 0 then so we know not to actually
  1265. // set the field to the user-specified (and in this case,
  1266. // illegal) value.
  1267. //
  1268. if (Buffer->LastWriteTime.QuadPart == -1) {
  1269. SetFlag( Ccb->Flags, CCB_FLAG_USER_SET_LAST_WRITE );
  1270. Buffer->LastWriteTime.QuadPart = 0;
  1271. }
  1272. if (Buffer->LastAccessTime.QuadPart == -1) {
  1273. SetFlag( Ccb->Flags, CCB_FLAG_USER_SET_LAST_ACCESS );
  1274. Buffer->LastAccessTime.QuadPart = 0;
  1275. }
  1276. if (Buffer->CreationTime.QuadPart == -1) {
  1277. SetFlag( Ccb->Flags, CCB_FLAG_USER_SET_CREATION );
  1278. Buffer->CreationTime.QuadPart = 0;
  1279. }
  1280. DirentBcb = NULL;
  1281. Status = STATUS_SUCCESS;
  1282. try {
  1283. LARGE_INTEGER FatLocalDecThirtyOne1979;
  1284. LARGE_INTEGER FatLocalJanOne1980;
  1285. ExLocalTimeToSystemTime( &FatDecThirtyOne1979,
  1286. &FatLocalDecThirtyOne1979 );
  1287. ExLocalTimeToSystemTime( &FatJanOne1980,
  1288. &FatLocalJanOne1980 );
  1289. //
  1290. // Get a pointer to the dirent
  1291. //
  1292. ASSERT( Fcb->FcbCondition == FcbGood );
  1293. FatGetDirentFromFcbOrDcb( IrpContext,
  1294. Fcb,
  1295. &Dirent,
  1296. &DirentBcb );
  1297. ASSERT( Dirent && DirentBcb );
  1298. //
  1299. // Check if the user specified a non-zero creation time
  1300. //
  1301. if (FatData.ChicagoMode && (Buffer->CreationTime.QuadPart != 0)) {
  1302. LargeCreationTime = Buffer->CreationTime;
  1303. //
  1304. // Convert the Nt time to a Fat time
  1305. //
  1306. if ( !FatNtTimeToFatTime( IrpContext,
  1307. &LargeCreationTime,
  1308. FALSE,
  1309. &CreationTime,
  1310. &CreationMSec )) {
  1311. //
  1312. // Special case the value 12/31/79 and treat this as 1/1/80.
  1313. // This '79 value can happen because of time zone issues.
  1314. //
  1315. if ((LargeCreationTime.QuadPart >= FatLocalDecThirtyOne1979.QuadPart) &&
  1316. (LargeCreationTime.QuadPart < FatLocalJanOne1980.QuadPart)) {
  1317. CreationTime = FatTimeJanOne1980;
  1318. LargeCreationTime = FatLocalJanOne1980;
  1319. } else {
  1320. DebugTrace(0, Dbg, "Invalid CreationTime\n", 0);
  1321. try_return( Status = STATUS_INVALID_PARAMETER );
  1322. }
  1323. //
  1324. // Don't worry about CreationMSec
  1325. //
  1326. CreationMSec = 0;
  1327. }
  1328. ModifyCreation = TRUE;
  1329. }
  1330. //
  1331. // Check if the user specified a non-zero last access time
  1332. //
  1333. if (FatData.ChicagoMode && (Buffer->LastAccessTime.QuadPart != 0)) {
  1334. LargeLastAccessTime = Buffer->LastAccessTime;
  1335. //
  1336. // Convert the Nt time to a Fat time
  1337. //
  1338. if ( !FatNtTimeToFatTime( IrpContext,
  1339. &LargeLastAccessTime,
  1340. TRUE,
  1341. &LastAccessTime,
  1342. NULL )) {
  1343. //
  1344. // Special case the value 12/31/79 and treat this as 1/1/80.
  1345. // This '79 value can happen because of time zone issues.
  1346. //
  1347. if ((LargeLastAccessTime.QuadPart >= FatLocalDecThirtyOne1979.QuadPart) &&
  1348. (LargeLastAccessTime.QuadPart < FatLocalJanOne1980.QuadPart)) {
  1349. LastAccessTime = FatTimeJanOne1980;
  1350. LargeLastAccessTime = FatLocalJanOne1980;
  1351. } else {
  1352. DebugTrace(0, Dbg, "Invalid LastAccessTime\n", 0);
  1353. try_return( Status = STATUS_INVALID_PARAMETER );
  1354. }
  1355. }
  1356. LastAccessDate = LastAccessTime.Date;
  1357. ModifyLastAccess = TRUE;
  1358. }
  1359. //
  1360. // Check if the user specified a non-zero last write time
  1361. //
  1362. if (Buffer->LastWriteTime.QuadPart != 0) {
  1363. //
  1364. // First do a quick check here if the this time is the same
  1365. // time as LastAccessTime.
  1366. //
  1367. if (ModifyLastAccess &&
  1368. (Buffer->LastWriteTime.QuadPart == Buffer->LastAccessTime.QuadPart)) {
  1369. ModifyLastWrite = TRUE;
  1370. LastWriteTime = LastAccessTime;
  1371. LargeLastWriteTime = LargeLastAccessTime;
  1372. } else {
  1373. LargeLastWriteTime = Buffer->LastWriteTime;
  1374. //
  1375. // Convert the Nt time to a Fat time
  1376. //
  1377. if ( !FatNtTimeToFatTime( IrpContext,
  1378. &LargeLastWriteTime,
  1379. TRUE,
  1380. &LastWriteTime,
  1381. NULL )) {
  1382. //
  1383. // Special case the value 12/31/79 and treat this as 1/1/80.
  1384. // This '79 value can happen because of time zone issues.
  1385. //
  1386. if ((LargeLastWriteTime.QuadPart >= FatLocalDecThirtyOne1979.QuadPart) &&
  1387. (LargeLastWriteTime.QuadPart < FatLocalJanOne1980.QuadPart)) {
  1388. LastWriteTime = FatTimeJanOne1980;
  1389. LargeLastWriteTime = FatLocalJanOne1980;
  1390. } else {
  1391. DebugTrace(0, Dbg, "Invalid LastWriteTime\n", 0);
  1392. try_return( Status = STATUS_INVALID_PARAMETER );
  1393. }
  1394. }
  1395. ModifyLastWrite = TRUE;
  1396. }
  1397. }
  1398. //
  1399. // Check if the user specified a non zero file attributes byte
  1400. //
  1401. if (Buffer->FileAttributes != 0) {
  1402. //
  1403. // Only permit the attributes that FAT understands. The rest are silently
  1404. // dropped on the floor.
  1405. //
  1406. Attributes = (UCHAR)(Buffer->FileAttributes & (FILE_ATTRIBUTE_READONLY |
  1407. FILE_ATTRIBUTE_HIDDEN |
  1408. FILE_ATTRIBUTE_SYSTEM |
  1409. FILE_ATTRIBUTE_DIRECTORY |
  1410. FILE_ATTRIBUTE_ARCHIVE));
  1411. //
  1412. // Make sure that for a file the directory bit is not set
  1413. // and that for a directory the bit is set.
  1414. //
  1415. if (NodeType(Fcb) == FAT_NTC_FCB) {
  1416. if (FlagOn(Buffer->FileAttributes, FILE_ATTRIBUTE_DIRECTORY)) {
  1417. DebugTrace(0, Dbg, "Attempt to set dir attribute on file\n", 0);
  1418. try_return( Status = STATUS_INVALID_PARAMETER );
  1419. }
  1420. } else {
  1421. Attributes |= FAT_DIRENT_ATTR_DIRECTORY;
  1422. }
  1423. //
  1424. // Mark the FcbState temporary flag correctly.
  1425. //
  1426. if (FlagOn(Buffer->FileAttributes, FILE_ATTRIBUTE_TEMPORARY)) {
  1427. //
  1428. // Don't allow the temporary bit to be set on directories.
  1429. //
  1430. if (NodeType(Fcb) == FAT_NTC_DCB) {
  1431. DebugTrace(0, Dbg, "No temporary directories\n", 0);
  1432. try_return( Status = STATUS_INVALID_PARAMETER );
  1433. }
  1434. SetFlag( Fcb->FcbState, FCB_STATE_TEMPORARY );
  1435. SetFlag( IoGetCurrentIrpStackLocation(Irp)->FileObject->Flags,
  1436. FO_TEMPORARY_FILE );
  1437. } else {
  1438. ClearFlag( Fcb->FcbState, FCB_STATE_TEMPORARY );
  1439. ClearFlag( IoGetCurrentIrpStackLocation(Irp)->FileObject->Flags,
  1440. FO_TEMPORARY_FILE );
  1441. }
  1442. //
  1443. // Set the new attributes byte, and mark the bcb dirty
  1444. //
  1445. Fcb->DirentFatFlags = Attributes;
  1446. Dirent->Attributes = Attributes;
  1447. NotifyFilter |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
  1448. }
  1449. if ( ModifyCreation ) {
  1450. //
  1451. // Set the new last write time in the dirent, and mark
  1452. // the bcb dirty
  1453. //
  1454. Fcb->CreationTime = LargeCreationTime;
  1455. Dirent->CreationTime = CreationTime;
  1456. Dirent->CreationMSec = CreationMSec;
  1457. NotifyFilter |= FILE_NOTIFY_CHANGE_CREATION;
  1458. //
  1459. // Now we have to round the time in the Fcb up to the
  1460. // nearest tem msec.
  1461. //
  1462. Fcb->CreationTime.QuadPart =
  1463. ((Fcb->CreationTime.QuadPart + AlmostTenMSec) /
  1464. TenMSec) * TenMSec;
  1465. //
  1466. // Now because the user just set the creation time we
  1467. // better not set the creation time on close
  1468. //
  1469. SetFlag( Ccb->Flags, CCB_FLAG_USER_SET_CREATION );
  1470. }
  1471. if ( ModifyLastAccess ) {
  1472. //
  1473. // Set the new last write time in the dirent, and mark
  1474. // the bcb dirty
  1475. //
  1476. Fcb->LastAccessTime = LargeLastAccessTime;
  1477. Dirent->LastAccessDate = LastAccessDate;
  1478. NotifyFilter |= FILE_NOTIFY_CHANGE_LAST_ACCESS;
  1479. //
  1480. // Now we have to truncate the time in the Fcb down to the
  1481. // current day. This has to be in LocalTime though, so first
  1482. // convert to local, trunacate, then set back to GMT.
  1483. //
  1484. ExSystemTimeToLocalTime( &Fcb->LastAccessTime,
  1485. &Fcb->LastAccessTime );
  1486. Fcb->LastAccessTime.QuadPart =
  1487. (Fcb->LastAccessTime.QuadPart /
  1488. FatOneDay.QuadPart) * FatOneDay.QuadPart;
  1489. ExLocalTimeToSystemTime( &Fcb->LastAccessTime,
  1490. &Fcb->LastAccessTime );
  1491. //
  1492. // Now because the user just set the last access time we
  1493. // better not set the last access time on close
  1494. //
  1495. SetFlag( Ccb->Flags, CCB_FLAG_USER_SET_LAST_ACCESS );
  1496. }
  1497. if ( ModifyLastWrite ) {
  1498. //
  1499. // Set the new last write time in the dirent, and mark
  1500. // the bcb dirty
  1501. //
  1502. Fcb->LastWriteTime = LargeLastWriteTime;
  1503. Dirent->LastWriteTime = LastWriteTime;
  1504. NotifyFilter |= FILE_NOTIFY_CHANGE_LAST_WRITE;
  1505. //
  1506. // Now we have to round the time in the Fcb up to the
  1507. // nearest two seconds.
  1508. //
  1509. Fcb->LastWriteTime.QuadPart =
  1510. ((Fcb->LastWriteTime.QuadPart + AlmostTwoSeconds) /
  1511. TwoSeconds) * TwoSeconds;
  1512. //
  1513. // Now because the user just set the last write time we
  1514. // better not set the last write time on close
  1515. //
  1516. SetFlag( Ccb->Flags, CCB_FLAG_USER_SET_LAST_WRITE );
  1517. }
  1518. //
  1519. // If we modified any of the values, we report this to the notify
  1520. // package.
  1521. //
  1522. // We also take this opportunity to set the current file size and
  1523. // first cluster in the Dirent in order to support a server hack.
  1524. //
  1525. if (NotifyFilter != 0) {
  1526. if (NodeType(Fcb) == FAT_NTC_FCB) {
  1527. Dirent->FileSize = Fcb->Header.FileSize.LowPart;
  1528. Dirent->FirstClusterOfFile = (USHORT)Fcb->FirstClusterOfFile;
  1529. if (FatIsFat32(Fcb->Vcb)) {
  1530. Dirent->FirstClusterOfFileHi =
  1531. (USHORT)(Fcb->FirstClusterOfFile >> 16);
  1532. }
  1533. }
  1534. FatNotifyReportChange( IrpContext,
  1535. Fcb->Vcb,
  1536. Fcb,
  1537. NotifyFilter,
  1538. FILE_ACTION_MODIFIED );
  1539. FatSetDirtyBcb( IrpContext, DirentBcb, Fcb->Vcb, TRUE );
  1540. }
  1541. try_exit: NOTHING;
  1542. } finally {
  1543. DebugUnwind( FatSetBasicInfo );
  1544. FatUnpinBcb( IrpContext, DirentBcb );
  1545. DebugTrace(-1, Dbg, "FatSetBasicInfo -> %08lx\n", Status);
  1546. }
  1547. return Status;
  1548. }
  1549. //
  1550. // Internal Support Routine
  1551. //
  1552. NTSTATUS
  1553. FatSetDispositionInfo (
  1554. IN PIRP_CONTEXT IrpContext,
  1555. IN PIRP Irp,
  1556. IN PFILE_OBJECT FileObject,
  1557. IN PFCB Fcb
  1558. )
  1559. /*++
  1560. Routine Description:
  1561. This routine performs the set disposition information for fat. It either
  1562. completes the request or enqueues it off to the fsp.
  1563. Arguments:
  1564. Irp - Supplies the irp being processed
  1565. FileObject - Supplies the file object being processed
  1566. Fcb - Supplies the Fcb or Dcb being processed, already known not to
  1567. be the root dcb
  1568. Return Value:
  1569. NTSTATUS - The result of this operation if it completes without
  1570. an exception.
  1571. --*/
  1572. {
  1573. PFILE_DISPOSITION_INFORMATION Buffer;
  1574. PBCB Bcb;
  1575. PDIRENT Dirent;
  1576. DebugTrace(+1, Dbg, "FatSetDispositionInfo...\n", 0);
  1577. Buffer = Irp->AssociatedIrp.SystemBuffer;
  1578. //
  1579. // Check if the user wants to delete the file or not delete
  1580. // the file
  1581. //
  1582. if (Buffer->DeleteFile) {
  1583. //
  1584. // Check if the file is marked read only
  1585. //
  1586. if (FlagOn(Fcb->DirentFatFlags, FAT_DIRENT_ATTR_READ_ONLY)) {
  1587. DebugTrace(-1, Dbg, "Cannot delete readonly file\n", 0);
  1588. return STATUS_CANNOT_DELETE;
  1589. }
  1590. //
  1591. // Make sure there is no process mapping this file as an image.
  1592. //
  1593. if (!MmFlushImageSection( &Fcb->NonPaged->SectionObjectPointers,
  1594. MmFlushForDelete )) {
  1595. DebugTrace(-1, Dbg, "Cannot delete user mapped image\n", 0);
  1596. return STATUS_CANNOT_DELETE;
  1597. }
  1598. //
  1599. // Check if this is a dcb and if so then only allow
  1600. // the request if the directory is empty.
  1601. //
  1602. if (NodeType(Fcb) == FAT_NTC_ROOT_DCB) {
  1603. DebugTrace(-1, Dbg, "Cannot delete root Directory\n", 0);
  1604. return STATUS_CANNOT_DELETE;
  1605. }
  1606. if (NodeType(Fcb) == FAT_NTC_DCB) {
  1607. DebugTrace(-1, Dbg, "User wants to delete a directory\n", 0);
  1608. //
  1609. // Check if the directory is empty
  1610. //
  1611. if ( !FatIsDirectoryEmpty(IrpContext, Fcb) ) {
  1612. DebugTrace(-1, Dbg, "Directory is not empty\n", 0);
  1613. return STATUS_DIRECTORY_NOT_EMPTY;
  1614. }
  1615. }
  1616. //
  1617. // If this is a floppy, touch the volume so to verify that it
  1618. // is not write protected.
  1619. //
  1620. if ( FlagOn(Fcb->Vcb->Vpb->RealDevice->Characteristics, FILE_FLOPPY_DISKETTE)) {
  1621. PVCB Vcb;
  1622. PBCB LocalBcb = NULL;
  1623. UCHAR *LocalBuffer;
  1624. UCHAR TmpChar;
  1625. ULONG BytesToMap;
  1626. IO_STATUS_BLOCK Iosb;
  1627. Vcb = Fcb->Vcb;
  1628. BytesToMap = Vcb->AllocationSupport.FatIndexBitSize == 12 ?
  1629. FatReservedBytes(&Vcb->Bpb) +
  1630. FatBytesPerFat(&Vcb->Bpb):PAGE_SIZE;
  1631. FatReadVolumeFile( IrpContext,
  1632. Vcb,
  1633. 0,
  1634. BytesToMap,
  1635. &LocalBcb,
  1636. (PVOID *)&LocalBuffer );
  1637. try {
  1638. if (!CcPinMappedData( Vcb->VirtualVolumeFile,
  1639. &FatLargeZero,
  1640. BytesToMap,
  1641. BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT),
  1642. &LocalBcb )) {
  1643. //
  1644. // Could not pin the data without waiting (cache miss).
  1645. //
  1646. FatRaiseStatus( IrpContext, STATUS_CANT_WAIT );
  1647. }
  1648. //
  1649. // Make Mm, myself, and Cc think the byte is dirty, and then
  1650. // force a writethrough.
  1651. //
  1652. LocalBuffer += FatReservedBytes(&Vcb->Bpb);
  1653. TmpChar = LocalBuffer[0];
  1654. LocalBuffer[0] = TmpChar;
  1655. FatAddMcbEntry( Vcb, &Vcb->DirtyFatMcb,
  1656. FatReservedBytes( &Vcb->Bpb ),
  1657. FatReservedBytes( &Vcb->Bpb ),
  1658. Vcb->Bpb.BytesPerSector );
  1659. } finally {
  1660. if (AbnormalTermination() && (LocalBcb != NULL)) {
  1661. FatUnpinBcb( IrpContext, LocalBcb );
  1662. }
  1663. }
  1664. CcRepinBcb( LocalBcb );
  1665. CcSetDirtyPinnedData( LocalBcb, NULL );
  1666. CcUnpinData( LocalBcb );
  1667. DbgDoit( ASSERT( IrpContext->PinCount ));
  1668. DbgDoit( IrpContext->PinCount -= 1 );
  1669. CcUnpinRepinnedBcb( LocalBcb, TRUE, &Iosb );
  1670. //
  1671. // If this was not successful, raise the status.
  1672. //
  1673. if ( !NT_SUCCESS(Iosb.Status) ) {
  1674. FatNormalizeAndRaiseStatus( IrpContext, Iosb.Status );
  1675. }
  1676. } else {
  1677. //
  1678. // Just set a Bcb dirty here. The above code was only there to
  1679. // detect a write protected floppy, while the below code works
  1680. // for any write protected media and only takes a hit when the
  1681. // volume in clean.
  1682. //
  1683. FatGetDirentFromFcbOrDcb( IrpContext,
  1684. Fcb,
  1685. &Dirent,
  1686. &Bcb );
  1687. //
  1688. // This has to work for the usual reasons (we verified the Fcb within
  1689. // volume synch).
  1690. //
  1691. ASSERT( Bcb != NULL );
  1692. try {
  1693. FatSetDirtyBcb( IrpContext, Bcb, Fcb->Vcb, TRUE );
  1694. } finally {
  1695. FatUnpinBcb( IrpContext, Bcb );
  1696. }
  1697. }
  1698. //
  1699. // At this point either we have a file or an empty directory
  1700. // so we know the delete can proceed.
  1701. //
  1702. SetFlag( Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE );
  1703. FileObject->DeletePending = TRUE;
  1704. //
  1705. // If this is a directory then report this delete pending to
  1706. // the dir notify package.
  1707. //
  1708. if (NodeType(Fcb) == FAT_NTC_DCB) {
  1709. FsRtlNotifyFullChangeDirectory( Fcb->Vcb->NotifySync,
  1710. &Fcb->Vcb->DirNotifyList,
  1711. FileObject->FsContext,
  1712. NULL,
  1713. FALSE,
  1714. FALSE,
  1715. 0,
  1716. NULL,
  1717. NULL,
  1718. NULL );
  1719. }
  1720. } else {
  1721. //
  1722. // The user doesn't want to delete the file so clear
  1723. // the delete on close bit
  1724. //
  1725. DebugTrace(0, Dbg, "User want to not delete file\n", 0);
  1726. ClearFlag( Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE );
  1727. FileObject->DeletePending = FALSE;
  1728. }
  1729. DebugTrace(-1, Dbg, "FatSetDispositionInfo -> STATUS_SUCCESS\n", 0);
  1730. return STATUS_SUCCESS;
  1731. }
  1732. //
  1733. // Internal Support Routine
  1734. //
  1735. NTSTATUS
  1736. FatSetRenameInfo (
  1737. IN PIRP_CONTEXT IrpContext,
  1738. IN PIRP Irp,
  1739. IN PVCB Vcb,
  1740. IN PFCB Fcb,
  1741. IN PCCB Ccb
  1742. )
  1743. /*++
  1744. Routine Description:
  1745. This routine performs the set name information for fat. It either
  1746. completes the request or enqueues it off to the fsp.
  1747. Arguments:
  1748. Irp - Supplies the irp being processed
  1749. Vcb - Supplies the Vcb being processed
  1750. Fcb - Supplies the Fcb or Dcb being processed, already known not to
  1751. be the root dcb
  1752. Ccb - Supplies the Ccb corresponding to the handle opening the source
  1753. file
  1754. Return Value:
  1755. NTSTATUS - The result of this operation if it completes without
  1756. an exception.
  1757. --*/
  1758. {
  1759. BOOLEAN AllLowerComponent;
  1760. BOOLEAN AllLowerExtension;
  1761. BOOLEAN CaseOnlyRename;
  1762. BOOLEAN ContinueWithRename;
  1763. BOOLEAN CreateLfn;
  1764. BOOLEAN DeleteSourceDirent;
  1765. BOOLEAN DeleteTarget;
  1766. BOOLEAN NewDirentFromPool;
  1767. BOOLEAN RenamedAcrossDirectories;
  1768. BOOLEAN ReplaceIfExists;
  1769. CCB LocalCcb;
  1770. PCCB SourceCcb;
  1771. DIRENT SourceDirent;
  1772. NTSTATUS Status;
  1773. OEM_STRING OldOemName;
  1774. OEM_STRING NewOemName;
  1775. UCHAR OemNameBuffer[24*2];
  1776. PBCB DotDotBcb;
  1777. PBCB NewDirentBcb;
  1778. PBCB OldDirentBcb;
  1779. PBCB SecondPageBcb;
  1780. PBCB TargetDirentBcb;
  1781. PDCB TargetDcb;
  1782. PDCB OldParentDcb;
  1783. PDIRENT DotDotDirent;
  1784. PDIRENT FirstPageDirent;
  1785. PDIRENT NewDirent;
  1786. PDIRENT OldDirent;
  1787. PDIRENT SecondPageDirent;
  1788. PDIRENT ShortDirent;
  1789. PDIRENT TargetDirent;
  1790. PFCB TempFcb;
  1791. PFILE_OBJECT TargetFileObject;
  1792. PFILE_OBJECT FileObject;
  1793. PIO_STACK_LOCATION IrpSp;
  1794. PLIST_ENTRY Links;
  1795. ULONG BytesInFirstPage;
  1796. ULONG DirentsInFirstPage;
  1797. ULONG DirentsRequired;
  1798. ULONG NewOffset;
  1799. ULONG NotifyAction;
  1800. ULONG SecondPageOffset;
  1801. ULONG ShortDirentOffset;
  1802. ULONG TargetDirentOffset;
  1803. ULONG TargetLfnOffset;
  1804. UNICODE_STRING NewName;
  1805. UNICODE_STRING NewUpcasedName;
  1806. UNICODE_STRING OldName;
  1807. UNICODE_STRING OldUpcasedName;
  1808. UNICODE_STRING TargetLfn;
  1809. PWCHAR UnicodeBuffer;
  1810. UNICODE_STRING UniTunneledShortName;
  1811. WCHAR UniTunneledShortNameBuffer[12];
  1812. UNICODE_STRING UniTunneledLongName;
  1813. WCHAR UniTunneledLongNameBuffer[26];
  1814. LARGE_INTEGER TunneledCreationTime;
  1815. ULONG TunneledDataSize;
  1816. BOOLEAN HaveTunneledInformation;
  1817. BOOLEAN UsingTunneledLfn = FALSE;
  1818. BOOLEAN InvalidateFcbOnRaise = FALSE;
  1819. DebugTrace(+1, Dbg, "FatSetRenameInfo...\n", 0);
  1820. //
  1821. // P H A S E 0: Initialize some variables.
  1822. //
  1823. CaseOnlyRename = FALSE;
  1824. ContinueWithRename = FALSE;
  1825. DeleteSourceDirent = FALSE;
  1826. DeleteTarget = FALSE;
  1827. NewDirentFromPool = FALSE;
  1828. RenamedAcrossDirectories = FALSE;
  1829. DotDotBcb = NULL;
  1830. NewDirentBcb = NULL;
  1831. OldDirentBcb = NULL;
  1832. SecondPageBcb = NULL;
  1833. TargetDirentBcb = NULL;
  1834. NewOemName.Length = 0;
  1835. NewOemName.MaximumLength = 24;
  1836. NewOemName.Buffer = &OemNameBuffer[0];
  1837. OldOemName.Length = 0;
  1838. OldOemName.MaximumLength = 24;
  1839. OldOemName.Buffer = &OemNameBuffer[24];
  1840. UnicodeBuffer = FsRtlAllocatePoolWithTag( PagedPool,
  1841. 4 * MAX_LFN_CHARACTERS * sizeof(WCHAR),
  1842. TAG_FILENAME_BUFFER );
  1843. NewUpcasedName.Length = 0;
  1844. NewUpcasedName.MaximumLength = MAX_LFN_CHARACTERS * sizeof(WCHAR);
  1845. NewUpcasedName.Buffer = &UnicodeBuffer[0];
  1846. OldName.Length = 0;
  1847. OldName.MaximumLength = MAX_LFN_CHARACTERS * sizeof(WCHAR);
  1848. OldName.Buffer = &UnicodeBuffer[MAX_LFN_CHARACTERS];
  1849. OldUpcasedName.Length = 0;
  1850. OldUpcasedName.MaximumLength = MAX_LFN_CHARACTERS * sizeof(WCHAR);
  1851. OldUpcasedName.Buffer = &UnicodeBuffer[MAX_LFN_CHARACTERS * 2];
  1852. TargetLfn.Length = 0;
  1853. TargetLfn.MaximumLength = MAX_LFN_CHARACTERS * sizeof(WCHAR);
  1854. TargetLfn.Buffer = &UnicodeBuffer[MAX_LFN_CHARACTERS * 3];
  1855. UniTunneledShortName.Length = 0;
  1856. UniTunneledShortName.MaximumLength = sizeof(UniTunneledShortNameBuffer);
  1857. UniTunneledShortName.Buffer = &UniTunneledShortNameBuffer[0];
  1858. UniTunneledLongName.Length = 0;
  1859. UniTunneledLongName.MaximumLength = sizeof(UniTunneledLongNameBuffer);
  1860. UniTunneledLongName.Buffer = &UniTunneledLongNameBuffer[0];
  1861. //
  1862. // Remember the name in case we have to modify the name
  1863. // value in the ea.
  1864. //
  1865. RtlCopyMemory( OldOemName.Buffer,
  1866. Fcb->ShortName.Name.Oem.Buffer,
  1867. OldOemName.Length );
  1868. //
  1869. // Get the current stack location
  1870. //
  1871. IrpSp = IoGetCurrentIrpStackLocation( Irp );
  1872. //
  1873. // Extract information from the Irp to make our life easier
  1874. //
  1875. FileObject = IrpSp->FileObject;
  1876. SourceCcb = FileObject->FsContext2;
  1877. TargetFileObject = IrpSp->Parameters.SetFile.FileObject;
  1878. ReplaceIfExists = IrpSp->Parameters.SetFile.ReplaceIfExists;
  1879. RtlZeroMemory( &LocalCcb, sizeof(CCB) );
  1880. //
  1881. // P H A S E 1:
  1882. //
  1883. // Test if rename is legal. Only small side-effects are not undone.
  1884. //
  1885. try {
  1886. //
  1887. // Can't rename the root directory
  1888. //
  1889. if ( NodeType(Fcb) == FAT_NTC_ROOT_DCB ) {
  1890. try_return( Status = STATUS_INVALID_PARAMETER );
  1891. }
  1892. //
  1893. // Check that we were not given a dcb with open handles beneath
  1894. // it. If there are only UncleanCount == 0 Fcbs beneath us, then
  1895. // remove them from the prefix table, and they will just close
  1896. // and go away naturally.
  1897. //
  1898. if (NodeType(Fcb) == FAT_NTC_DCB) {
  1899. PFCB BatchOplockFcb;
  1900. ULONG BatchOplockCount;
  1901. //
  1902. // Loop until there are no batch oplocks in the subtree below
  1903. // this directory.
  1904. //
  1905. while (TRUE) {
  1906. BatchOplockFcb = NULL;
  1907. BatchOplockCount = 0;
  1908. //
  1909. // First look for any UncleanCount != 0 Fcbs, and fail if we
  1910. // find any.
  1911. //
  1912. for ( TempFcb = FatGetNextFcbBottomUp(IrpContext, NULL, Fcb);
  1913. TempFcb != Fcb;
  1914. TempFcb = FatGetNextFcbBottomUp(IrpContext, TempFcb, Fcb) ) {
  1915. if ( TempFcb->UncleanCount != 0 ) {
  1916. //
  1917. // If there is a batch oplock on this file then
  1918. // increment our count and remember the Fcb if
  1919. // this is the first.
  1920. //
  1921. if ( (NodeType(TempFcb) == FAT_NTC_FCB) &&
  1922. FsRtlCurrentBatchOplock( &TempFcb->Specific.Fcb.Oplock ) ) {
  1923. BatchOplockCount += 1;
  1924. if ( BatchOplockFcb == NULL ) {
  1925. BatchOplockFcb = TempFcb;
  1926. }
  1927. } else {
  1928. try_return( Status = STATUS_ACCESS_DENIED );
  1929. }
  1930. }
  1931. }
  1932. //
  1933. // If this is not the first pass for rename and the number
  1934. // of batch oplocks has not decreased then give up.
  1935. //
  1936. if ( BatchOplockFcb != NULL ) {
  1937. if ( (Irp->IoStatus.Information != 0) &&
  1938. (BatchOplockCount >= Irp->IoStatus.Information) ) {
  1939. try_return( Status = STATUS_ACCESS_DENIED );
  1940. }
  1941. //
  1942. // Try to break this batch oplock.
  1943. //
  1944. Irp->IoStatus.Information = BatchOplockCount;
  1945. Status = FsRtlCheckOplock( &BatchOplockFcb->Specific.Fcb.Oplock,
  1946. Irp,
  1947. IrpContext,
  1948. FatOplockComplete,
  1949. NULL );
  1950. //
  1951. // If the oplock was already broken then look for more
  1952. // batch oplocks.
  1953. //
  1954. if (Status == STATUS_SUCCESS) {
  1955. continue;
  1956. }
  1957. //
  1958. // Otherwise the oplock package will post or complete the
  1959. // request.
  1960. //
  1961. try_return( Status = STATUS_PENDING );
  1962. }
  1963. break;
  1964. }
  1965. //
  1966. // Now try to get as many of these file object, and thus Fcbs
  1967. // to go away as possible, flushing first, of course.
  1968. //
  1969. FatPurgeReferencedFileObjects( IrpContext, Fcb, TRUE );
  1970. //
  1971. // OK, so there are no UncleanCount != 0, Fcbs. Infact, there
  1972. // shouldn't really be any Fcbs left at all, except obstinate
  1973. // ones from user mapped sections ....oh well, he shouldn't have
  1974. // closed his handle if he wanted the file to stick around. So
  1975. // remove any Fcbs beneath us from the splay table and mark them
  1976. // DELETE_ON_CLOSE so that any future operations will fail.
  1977. //
  1978. for ( TempFcb = FatGetNextFcbBottomUp(IrpContext, NULL, Fcb);
  1979. TempFcb != Fcb;
  1980. TempFcb = FatGetNextFcbBottomUp(IrpContext, TempFcb, Fcb) ) {
  1981. FatRemoveNames( IrpContext, TempFcb );
  1982. SetFlag( TempFcb->FcbState, FCB_STATE_DELETE_ON_CLOSE );
  1983. }
  1984. }
  1985. //
  1986. // Check if this is a simple rename or a fully-qualified rename
  1987. // In both cases we need to figure out what the TargetDcb, and
  1988. // NewName are.
  1989. //
  1990. if (TargetFileObject == NULL) {
  1991. //
  1992. // In the case of a simple rename the target dcb is the
  1993. // same as the source file's parent dcb, and the new file name
  1994. // is taken from the system buffer
  1995. //
  1996. PFILE_RENAME_INFORMATION Buffer;
  1997. Buffer = Irp->AssociatedIrp.SystemBuffer;
  1998. TargetDcb = Fcb->ParentDcb;
  1999. NewName.Length = (USHORT) Buffer->FileNameLength;
  2000. NewName.Buffer = (PWSTR) &Buffer->FileName;
  2001. //
  2002. // Make sure the name is of legal length.
  2003. //
  2004. if (NewName.Length >= 255*sizeof(WCHAR)) {
  2005. try_return( Status = STATUS_OBJECT_NAME_INVALID );
  2006. }
  2007. } else {
  2008. //
  2009. // For a fully-qualified rename the target dcb is taken from
  2010. // the target file object, which must be on the same vcb as
  2011. // the source.
  2012. //
  2013. PVCB TargetVcb;
  2014. PCCB TargetCcb;
  2015. if ((FatDecodeFileObject( TargetFileObject,
  2016. &TargetVcb,
  2017. &TargetDcb,
  2018. &TargetCcb ) != UserDirectoryOpen) ||
  2019. (TargetVcb != Vcb)) {
  2020. try_return( Status = STATUS_INVALID_PARAMETER );
  2021. }
  2022. //
  2023. // This name is by definition legal.
  2024. //
  2025. NewName = *((PUNICODE_STRING)&TargetFileObject->FileName);
  2026. }
  2027. //
  2028. // We will need an upcased version of the unicode name and the
  2029. // old name as well.
  2030. //
  2031. Status = RtlUpcaseUnicodeString( &NewUpcasedName, &NewName, FALSE );
  2032. if (!NT_SUCCESS(Status)) {
  2033. try_return( Status );
  2034. }
  2035. FatGetUnicodeNameFromFcb( IrpContext, Fcb, &OldName );
  2036. Status = RtlUpcaseUnicodeString( &OldUpcasedName, &OldName, FALSE );
  2037. if (!NT_SUCCESS(Status)) {
  2038. try_return(Status);
  2039. }
  2040. //
  2041. // Check if the current name and new name are equal, and the
  2042. // DCBs are equal. If they are then our work is already done.
  2043. //
  2044. if (TargetDcb == Fcb->ParentDcb) {
  2045. //
  2046. // OK, now if we found something then check if it was an exact
  2047. // match or just a case match. If it was an exact match, then
  2048. // we can bail here.
  2049. //
  2050. if (FsRtlAreNamesEqual( &NewName,
  2051. &OldName,
  2052. FALSE,
  2053. NULL )) {
  2054. try_return( Status = STATUS_SUCCESS );
  2055. }
  2056. //
  2057. // Check now for a case only rename.
  2058. //
  2059. if (FsRtlAreNamesEqual( &NewUpcasedName,
  2060. &OldUpcasedName,
  2061. FALSE,
  2062. NULL )) {
  2063. CaseOnlyRename = TRUE;
  2064. }
  2065. } else {
  2066. RenamedAcrossDirectories = TRUE;
  2067. }
  2068. //
  2069. // Upcase the name and convert it to the Oem code page.
  2070. //
  2071. // If the new UNICODE name is already more than 12 characters,
  2072. // then we know the Oem name will not be valid
  2073. //
  2074. if (NewName.Length <= 12*sizeof(WCHAR)) {
  2075. FatUnicodeToUpcaseOem( IrpContext, &NewOemName, &NewName );
  2076. //
  2077. // If the name is not valid 8.3, zero the length.
  2078. //
  2079. if (FatSpaceInName( IrpContext, &NewName ) ||
  2080. !FatIsNameShortOemValid( IrpContext, NewOemName, FALSE, FALSE, FALSE)) {
  2081. NewOemName.Length = 0;
  2082. }
  2083. } else {
  2084. NewOemName.Length = 0;
  2085. }
  2086. //
  2087. // Look in the tunnel cache for names and timestamps to restore
  2088. //
  2089. TunneledDataSize = sizeof(LARGE_INTEGER);
  2090. HaveTunneledInformation = FsRtlFindInTunnelCache( &Vcb->Tunnel,
  2091. FatDirectoryKey(TargetDcb),
  2092. &NewName,
  2093. &UniTunneledShortName,
  2094. &UniTunneledLongName,
  2095. &TunneledDataSize,
  2096. &TunneledCreationTime );
  2097. ASSERT(TunneledDataSize == sizeof(LARGE_INTEGER));
  2098. //
  2099. // Now we need to determine how many dirents this new name will
  2100. // require.
  2101. //
  2102. if ((NewOemName.Length == 0) ||
  2103. (FatEvaluateNameCase( IrpContext,
  2104. &NewName,
  2105. &AllLowerComponent,
  2106. &AllLowerExtension,
  2107. &CreateLfn ),
  2108. CreateLfn)) {
  2109. DirentsRequired = FAT_LFN_DIRENTS_NEEDED(&NewName) + 1;
  2110. } else {
  2111. //
  2112. // The user-given name is a short name, but we might still have
  2113. // a tunneled long name we want to use. See if we can.
  2114. //
  2115. if (UniTunneledLongName.Length &&
  2116. !FatLfnDirentExists(IrpContext, TargetDcb, &UniTunneledLongName, &TargetLfn)) {
  2117. UsingTunneledLfn = CreateLfn = TRUE;
  2118. DirentsRequired = FAT_LFN_DIRENTS_NEEDED(&UniTunneledLongName) + 1;
  2119. } else {
  2120. //
  2121. // This really is a simple dirent. Note that the two AllLower BOOLEANs
  2122. // are correctly set now.
  2123. //
  2124. DirentsRequired = 1;
  2125. }
  2126. }
  2127. //
  2128. // Do some extra checks here if we are not in Chicago mode.
  2129. //
  2130. if (!FatData.ChicagoMode) {
  2131. //
  2132. // If the name was not 8.3 valid, fail the rename.
  2133. //
  2134. if (NewOemName.Length == 0) {
  2135. try_return( Status = STATUS_OBJECT_NAME_INVALID );
  2136. }
  2137. //
  2138. // Don't use the magic bits.
  2139. //
  2140. AllLowerComponent = FALSE;
  2141. AllLowerExtension = FALSE;
  2142. CreateLfn = FALSE;
  2143. UsingTunneledLfn = FALSE;
  2144. }
  2145. if (!CaseOnlyRename) {
  2146. //
  2147. // Check if the new name already exists, wait is known to be
  2148. // true.
  2149. //
  2150. if (NewOemName.Length != 0) {
  2151. FatStringTo8dot3( IrpContext,
  2152. NewOemName,
  2153. &LocalCcb.OemQueryTemplate.Constant );
  2154. } else {
  2155. SetFlag( LocalCcb.Flags, CCB_FLAG_SKIP_SHORT_NAME_COMPARE );
  2156. }
  2157. LocalCcb.UnicodeQueryTemplate = NewUpcasedName;
  2158. LocalCcb.ContainsWildCards = FALSE;
  2159. FatLocateDirent( IrpContext,
  2160. TargetDcb,
  2161. &LocalCcb,
  2162. 0,
  2163. &TargetDirent,
  2164. &TargetDirentBcb,
  2165. &TargetDirentOffset,
  2166. NULL,
  2167. &TargetLfn);
  2168. if (TargetDirent != NULL) {
  2169. //
  2170. // The name already exists, check if the user wants
  2171. // to overwrite the name, and has access to do the overwrite
  2172. // We cannot overwrite a directory.
  2173. //
  2174. if ((!ReplaceIfExists) ||
  2175. (FlagOn(TargetDirent->Attributes, FAT_DIRENT_ATTR_DIRECTORY)) ||
  2176. (FlagOn(TargetDirent->Attributes, FAT_DIRENT_ATTR_READ_ONLY))) {
  2177. try_return( Status = STATUS_OBJECT_NAME_COLLISION );
  2178. }
  2179. //
  2180. // Check that the file has no open user handles, if it does
  2181. // then we will deny access. We do the check by searching
  2182. // down the list of fcbs opened under our parent Dcb, and making
  2183. // sure none of the maching Fcbs have a non-zero unclean count or
  2184. // outstanding image sections.
  2185. //
  2186. for (Links = TargetDcb->Specific.Dcb.ParentDcbQueue.Flink;
  2187. Links != &TargetDcb->Specific.Dcb.ParentDcbQueue; ) {
  2188. TempFcb = CONTAINING_RECORD( Links, FCB, ParentDcbLinks );
  2189. //
  2190. // Advance now. The image section flush may cause the final
  2191. // close, which will recursively happen underneath of us here.
  2192. // It would be unfortunate if we looked through free memory.
  2193. //
  2194. Links = Links->Flink;
  2195. if ((TempFcb->DirentOffsetWithinDirectory == TargetDirentOffset) &&
  2196. ((TempFcb->UncleanCount != 0) ||
  2197. !MmFlushImageSection( &TempFcb->NonPaged->SectionObjectPointers,
  2198. MmFlushForDelete))) {
  2199. //
  2200. // If there are batch oplocks on this file then break the
  2201. // oplocks before failing the rename.
  2202. //
  2203. Status = STATUS_ACCESS_DENIED;
  2204. if ((NodeType(TempFcb) == FAT_NTC_FCB) &&
  2205. FsRtlCurrentBatchOplock( &TempFcb->Specific.Fcb.Oplock )) {
  2206. //
  2207. // Do all of our cleanup now since the IrpContext
  2208. // could go away when this request is posted.
  2209. //
  2210. FatUnpinBcb( IrpContext, TargetDirentBcb );
  2211. Status = FsRtlCheckOplock( &TempFcb->Specific.Fcb.Oplock,
  2212. Irp,
  2213. IrpContext,
  2214. FatOplockComplete,
  2215. NULL );
  2216. if (Status != STATUS_PENDING) {
  2217. Status = STATUS_ACCESS_DENIED;
  2218. }
  2219. }
  2220. try_return( NOTHING );
  2221. }
  2222. }
  2223. //
  2224. // OK, this target is toast. Remember the Lfn offset.
  2225. //
  2226. TargetLfnOffset = TargetDirentOffset -
  2227. FAT_LFN_DIRENTS_NEEDED(&TargetLfn) *
  2228. sizeof(DIRENT);
  2229. DeleteTarget = TRUE;
  2230. }
  2231. }
  2232. //
  2233. // If we will need more dirents than we have, allocate them now.
  2234. //
  2235. if ((TargetDcb != Fcb->ParentDcb) ||
  2236. (DirentsRequired !=
  2237. (Fcb->DirentOffsetWithinDirectory -
  2238. Fcb->LfnOffsetWithinDirectory) / sizeof(DIRENT) + 1)) {
  2239. //
  2240. // Get some new allocation
  2241. //
  2242. NewOffset = FatCreateNewDirent( IrpContext,
  2243. TargetDcb,
  2244. DirentsRequired );
  2245. DeleteSourceDirent = TRUE;
  2246. } else {
  2247. NewOffset = Fcb->LfnOffsetWithinDirectory;
  2248. }
  2249. ContinueWithRename = TRUE;
  2250. try_exit: NOTHING;
  2251. } finally {
  2252. if (!ContinueWithRename) {
  2253. //
  2254. // Undo everything from above.
  2255. //
  2256. ExFreePool( UnicodeBuffer );
  2257. FatUnpinBcb( IrpContext, TargetDirentBcb );
  2258. }
  2259. }
  2260. //
  2261. // Now, if we are already done, return here.
  2262. //
  2263. if (!ContinueWithRename) {
  2264. return Status;
  2265. }
  2266. //
  2267. // P H A S E 2: Actually perform the rename.
  2268. //
  2269. try {
  2270. //
  2271. // Report the fact that we are going to remove this entry.
  2272. // If we renamed within the same directory and the new name for the
  2273. // file did not previously exist, we report this as a rename old
  2274. // name. Otherwise this is a removed file.
  2275. //
  2276. if (!RenamedAcrossDirectories && !DeleteTarget) {
  2277. NotifyAction = FILE_ACTION_RENAMED_OLD_NAME;
  2278. } else {
  2279. NotifyAction = FILE_ACTION_REMOVED;
  2280. }
  2281. FatNotifyReportChange( IrpContext,
  2282. Vcb,
  2283. Fcb,
  2284. ((NodeType( Fcb ) == FAT_NTC_FCB)
  2285. ? FILE_NOTIFY_CHANGE_FILE_NAME
  2286. : FILE_NOTIFY_CHANGE_DIR_NAME ),
  2287. NotifyAction );
  2288. //
  2289. // Capture a copy of the source dirent.
  2290. //
  2291. FatGetDirentFromFcbOrDcb( IrpContext, Fcb, &OldDirent, &OldDirentBcb );
  2292. SourceDirent = *OldDirent;
  2293. try {
  2294. //
  2295. // Tunnel the source Fcb - the names are disappearing regardless of
  2296. // whether the dirent allocation physically changed
  2297. //
  2298. FatTunnelFcbOrDcb( Fcb, SourceCcb );
  2299. //
  2300. // From here until very nearly the end of the operation, if we raise there
  2301. // is no reasonable way to suppose we'd be able to undo the damage. Not
  2302. // being a transactional filesystem, FAT is at the mercy of a lot of things
  2303. // (as the astute reader has no doubt realized by now).
  2304. //
  2305. InvalidateFcbOnRaise = TRUE;
  2306. //
  2307. // Delete our current dirent(s) if we got a new one.
  2308. //
  2309. if (DeleteSourceDirent) {
  2310. FatDeleteDirent( IrpContext, Fcb, NULL, FALSE );
  2311. }
  2312. //
  2313. // Delete a target conflict if we were meant to.
  2314. //
  2315. if (DeleteTarget) {
  2316. FatDeleteFile( IrpContext,
  2317. TargetDcb,
  2318. TargetLfnOffset,
  2319. TargetDirentOffset,
  2320. TargetDirent,
  2321. &TargetLfn );
  2322. }
  2323. //
  2324. // We need to evaluate any short names required. If there were any
  2325. // conflicts in existing short names, they would have been deleted above.
  2326. //
  2327. // It isn't neccesary to worry about the UsingTunneledLfn case. Since we
  2328. // actually already know whether CreateLfn will be set either NewName is
  2329. // an Lfn and !UsingTunneledLfn is implied or NewName is a short name and
  2330. // we can handle that externally.
  2331. //
  2332. FatSelectNames( IrpContext,
  2333. TargetDcb,
  2334. &NewOemName,
  2335. &NewName,
  2336. &NewOemName,
  2337. (HaveTunneledInformation ? &UniTunneledShortName : NULL),
  2338. &AllLowerComponent,
  2339. &AllLowerExtension,
  2340. &CreateLfn );
  2341. if (!CreateLfn && UsingTunneledLfn) {
  2342. CreateLfn = TRUE;
  2343. NewName = UniTunneledLongName;
  2344. //
  2345. // Short names are always upcase if an LFN exists
  2346. //
  2347. AllLowerComponent = FALSE;
  2348. AllLowerExtension = FALSE;
  2349. }
  2350. //
  2351. // OK, now setup the new dirent(s) for the new name.
  2352. //
  2353. FatPrepareWriteDirectoryFile( IrpContext,
  2354. TargetDcb,
  2355. NewOffset,
  2356. sizeof(DIRENT),
  2357. &NewDirentBcb,
  2358. &NewDirent,
  2359. FALSE,
  2360. TRUE,
  2361. &Status );
  2362. ASSERT( NT_SUCCESS( Status ) );
  2363. //
  2364. // Deal with the special case of an LFN + Dirent structure crossing
  2365. // a page boundry.
  2366. //
  2367. if ((NewOffset / PAGE_SIZE) !=
  2368. ((NewOffset + (DirentsRequired - 1) * sizeof(DIRENT)) / PAGE_SIZE)) {
  2369. SecondPageOffset = (NewOffset & ~(PAGE_SIZE - 1)) + PAGE_SIZE;
  2370. BytesInFirstPage = SecondPageOffset - NewOffset;
  2371. DirentsInFirstPage = BytesInFirstPage / sizeof(DIRENT);
  2372. FatPrepareWriteDirectoryFile( IrpContext,
  2373. TargetDcb,
  2374. SecondPageOffset,
  2375. sizeof(DIRENT),
  2376. &SecondPageBcb,
  2377. &SecondPageDirent,
  2378. FALSE,
  2379. TRUE,
  2380. &Status );
  2381. ASSERT( NT_SUCCESS( Status ) );
  2382. FirstPageDirent = NewDirent;
  2383. NewDirent = FsRtlAllocatePoolWithTag( PagedPool,
  2384. DirentsRequired * sizeof(DIRENT),
  2385. TAG_DIRENT );
  2386. NewDirentFromPool = TRUE;
  2387. }
  2388. //
  2389. // Bump up Dirent and DirentOffset
  2390. //
  2391. ShortDirent = NewDirent + DirentsRequired - 1;
  2392. ShortDirentOffset = NewOffset + (DirentsRequired - 1) * sizeof(DIRENT);
  2393. //
  2394. // Fill in the fields of the dirent.
  2395. //
  2396. *ShortDirent = SourceDirent;
  2397. FatConstructDirent( IrpContext,
  2398. ShortDirent,
  2399. &NewOemName,
  2400. AllLowerComponent,
  2401. AllLowerExtension,
  2402. CreateLfn ? &NewName : NULL,
  2403. SourceDirent.Attributes,
  2404. FALSE,
  2405. (HaveTunneledInformation ? &TunneledCreationTime : NULL) );
  2406. if (HaveTunneledInformation) {
  2407. //
  2408. // Need to go in and fix the timestamps in the FCB. Note that we can't use
  2409. // the TunneledCreationTime since the conversions may have failed.
  2410. //
  2411. Fcb->CreationTime = FatFatTimeToNtTime(IrpContext, ShortDirent->CreationTime, ShortDirent->CreationMSec);
  2412. Fcb->LastWriteTime = FatFatTimeToNtTime(IrpContext, ShortDirent->LastWriteTime, 0);
  2413. Fcb->LastAccessTime = FatFatDateToNtTime(IrpContext, ShortDirent->LastAccessDate);
  2414. }
  2415. //
  2416. // If the dirent crossed pages, split the contents of the
  2417. // temporary pool between the two pages.
  2418. //
  2419. if (NewDirentFromPool) {
  2420. RtlCopyMemory( FirstPageDirent, NewDirent, BytesInFirstPage );
  2421. RtlCopyMemory( SecondPageDirent,
  2422. NewDirent + DirentsInFirstPage,
  2423. DirentsRequired*sizeof(DIRENT) - BytesInFirstPage );
  2424. ShortDirent = SecondPageDirent +
  2425. (DirentsRequired - DirentsInFirstPage) - 1;
  2426. }
  2427. } finally {
  2428. //
  2429. // Remove the entry from the splay table, and then remove the
  2430. // full file name and exact case lfn. It is important that we
  2431. // always remove the name from the prefix table regardless of
  2432. // other errors.
  2433. //
  2434. FatRemoveNames( IrpContext, Fcb );
  2435. if (Fcb->FullFileName.Buffer != NULL) {
  2436. ExFreePool( Fcb->FullFileName.Buffer );
  2437. Fcb->FullFileName.Buffer = NULL;
  2438. }
  2439. if (Fcb->ExactCaseLongName.Buffer) {
  2440. ExFreePool( Fcb->ExactCaseLongName.Buffer );
  2441. Fcb->ExactCaseLongName.Buffer = NULL;
  2442. }
  2443. }
  2444. //
  2445. // Now we need to update the location of the file's directory
  2446. // offset and move the fcb from its current parent dcb to
  2447. // the target dcb.
  2448. //
  2449. Fcb->LfnOffsetWithinDirectory = NewOffset;
  2450. Fcb->DirentOffsetWithinDirectory = ShortDirentOffset;
  2451. RemoveEntryList( &Fcb->ParentDcbLinks );
  2452. //
  2453. // There is a deep reason we put files on the tail, others on the head,
  2454. // which is to allow us to easily enumerate all child directories before
  2455. // child files. This is important to let us maintain whole-volume lockorder
  2456. // via BottomUp enumeration.
  2457. //
  2458. if (NodeType(Fcb) == FAT_NTC_FCB) {
  2459. InsertTailList( &TargetDcb->Specific.Dcb.ParentDcbQueue,
  2460. &Fcb->ParentDcbLinks );
  2461. } else {
  2462. InsertHeadList( &TargetDcb->Specific.Dcb.ParentDcbQueue,
  2463. &Fcb->ParentDcbLinks );
  2464. }
  2465. OldParentDcb = Fcb->ParentDcb;
  2466. Fcb->ParentDcb = TargetDcb;
  2467. //
  2468. // If we renamed across directories, some cleanup is now in order.
  2469. //
  2470. if (RenamedAcrossDirectories) {
  2471. //
  2472. // See if we need to uninitialize the cachemap for the source directory.
  2473. // Do this now in case we get unlucky and raise trying to finalize the
  2474. // operation.
  2475. //
  2476. if (IsListEmpty(&OldParentDcb->Specific.Dcb.ParentDcbQueue) &&
  2477. (OldParentDcb->OpenCount == 0) &&
  2478. (OldParentDcb->Specific.Dcb.DirectoryFile != NULL)) {
  2479. PFILE_OBJECT DirectoryFileObject;
  2480. ASSERT( NodeType(OldParentDcb) == FAT_NTC_DCB );
  2481. DirectoryFileObject = OldParentDcb->Specific.Dcb.DirectoryFile;
  2482. DebugTrace(0, Dbg, "Uninitialize our parent Stream Cache Map\n", 0);
  2483. CcUninitializeCacheMap( DirectoryFileObject, NULL, NULL );
  2484. OldParentDcb->Specific.Dcb.DirectoryFile = NULL;
  2485. ObDereferenceObject( DirectoryFileObject );
  2486. }
  2487. //
  2488. // If we move a directory across directories, we have to change
  2489. // the cluster number in its .. entry
  2490. //
  2491. if (NodeType(Fcb) == FAT_NTC_DCB) {
  2492. FatPrepareWriteDirectoryFile( IrpContext,
  2493. Fcb,
  2494. sizeof(DIRENT),
  2495. sizeof(DIRENT),
  2496. &DotDotBcb,
  2497. &DotDotDirent,
  2498. FALSE,
  2499. TRUE,
  2500. &Status );
  2501. ASSERT( NT_SUCCESS( Status ) );
  2502. DotDotDirent->FirstClusterOfFile = (USHORT)
  2503. ( NodeType(TargetDcb) == FAT_NTC_ROOT_DCB ?
  2504. 0 : TargetDcb->FirstClusterOfFile);
  2505. if (FatIsFat32( Vcb )) {
  2506. DotDotDirent->FirstClusterOfFileHi = (USHORT)
  2507. ( NodeType( TargetDcb ) == FAT_NTC_ROOT_DCB ?
  2508. 0 : (TargetDcb->FirstClusterOfFile >> 16));
  2509. }
  2510. }
  2511. }
  2512. //
  2513. // Now we need to setup the splay table and the name within
  2514. // the fcb. Free the old short name at this point.
  2515. //
  2516. ExFreePool( Fcb->ShortName.Name.Oem.Buffer );
  2517. Fcb->ShortName.Name.Oem.Buffer = NULL;
  2518. FatConstructNamesInFcb( IrpContext,
  2519. Fcb,
  2520. ShortDirent,
  2521. CreateLfn ? &NewName : NULL );
  2522. FatSetFullNameInFcb( IrpContext, Fcb, &NewName );
  2523. //
  2524. // The rest of the actions taken are not related to correctness of
  2525. // the in-memory structures, so we shouldn't toast the Fcb if we
  2526. // raise from here to the end.
  2527. //
  2528. InvalidateFcbOnRaise = FALSE;
  2529. //
  2530. // If a file, set the file as modified so that the archive bit
  2531. // is set. We prevent this from adjusting the write time by
  2532. // indicating the user flag in the ccb.
  2533. //
  2534. if (Fcb->Header.NodeTypeCode == FAT_NTC_FCB) {
  2535. SetFlag( FileObject->Flags, FO_FILE_MODIFIED );
  2536. SetFlag( Ccb->Flags, CCB_FLAG_USER_SET_LAST_WRITE );
  2537. }
  2538. //
  2539. // We have three cases to report.
  2540. //
  2541. // 1. If we overwrote an existing file, we report this as
  2542. // a modified file.
  2543. //
  2544. // 2. If we renamed to a new directory, then we added a file.
  2545. //
  2546. // 3. If we renamed in the same directory, then we report the
  2547. // the renamednewname.
  2548. //
  2549. if (DeleteTarget) {
  2550. FatNotifyReportChange( IrpContext,
  2551. Vcb,
  2552. Fcb,
  2553. FILE_NOTIFY_CHANGE_ATTRIBUTES
  2554. | FILE_NOTIFY_CHANGE_SIZE
  2555. | FILE_NOTIFY_CHANGE_LAST_WRITE
  2556. | FILE_NOTIFY_CHANGE_LAST_ACCESS
  2557. | FILE_NOTIFY_CHANGE_CREATION
  2558. | FILE_NOTIFY_CHANGE_EA,
  2559. FILE_ACTION_MODIFIED );
  2560. } else if (RenamedAcrossDirectories) {
  2561. FatNotifyReportChange( IrpContext,
  2562. Vcb,
  2563. Fcb,
  2564. ((NodeType( Fcb ) == FAT_NTC_FCB)
  2565. ? FILE_NOTIFY_CHANGE_FILE_NAME
  2566. : FILE_NOTIFY_CHANGE_DIR_NAME ),
  2567. FILE_ACTION_ADDED );
  2568. } else {
  2569. FatNotifyReportChange( IrpContext,
  2570. Vcb,
  2571. Fcb,
  2572. ((NodeType( Fcb ) == FAT_NTC_FCB)
  2573. ? FILE_NOTIFY_CHANGE_FILE_NAME
  2574. : FILE_NOTIFY_CHANGE_DIR_NAME ),
  2575. FILE_ACTION_RENAMED_NEW_NAME );
  2576. }
  2577. //
  2578. // We need to update the file name in the dirent. This value
  2579. // is never used elsewhere, so we don't concern ourselves
  2580. // with any error we may encounter. We let chkdsk fix the
  2581. // disk at some later time.
  2582. //
  2583. if (!FatIsFat32(Vcb) &&
  2584. ShortDirent->ExtendedAttributes != 0) {
  2585. FatRenameEAs( IrpContext,
  2586. Fcb,
  2587. ShortDirent->ExtendedAttributes,
  2588. &OldOemName );
  2589. }
  2590. //
  2591. // Set our final status
  2592. //
  2593. Status = STATUS_SUCCESS;
  2594. } finally {
  2595. DebugUnwind( FatSetRenameInfo );
  2596. ExFreePool( UnicodeBuffer );
  2597. if (UniTunneledLongName.Buffer != UniTunneledLongNameBuffer) {
  2598. //
  2599. // Free pool if the buffer was grown on tunneling lookup
  2600. //
  2601. ExFreePool(UniTunneledLongName.Buffer);
  2602. }
  2603. FatUnpinBcb( IrpContext, OldDirentBcb );
  2604. FatUnpinBcb( IrpContext, TargetDirentBcb );
  2605. FatUnpinBcb( IrpContext, NewDirentBcb );
  2606. FatUnpinBcb( IrpContext, SecondPageBcb );
  2607. FatUnpinBcb( IrpContext, DotDotBcb );
  2608. //
  2609. // If this was an abnormal termination, then we are in trouble.
  2610. // Should the operation have been in a sensitive state there is
  2611. // nothing we can do but invalidate the Fcb.
  2612. //
  2613. if (AbnormalTermination() && InvalidateFcbOnRaise) {
  2614. Fcb->FcbCondition = FcbBad;
  2615. }
  2616. DebugTrace(-1, Dbg, "FatSetRenameInfo -> %08lx\n", Status);
  2617. }
  2618. return Status;
  2619. }
  2620. //
  2621. // Internal Support Routine
  2622. //
  2623. NTSTATUS
  2624. FatSetPositionInfo (
  2625. IN PIRP_CONTEXT IrpContext,
  2626. IN PIRP Irp,
  2627. IN PFILE_OBJECT FileObject
  2628. )
  2629. /*++
  2630. Routine Description:
  2631. This routine performs the set position information for fat. It either
  2632. completes the request or enqueues it off to the fsp.
  2633. Arguments:
  2634. Irp - Supplies the irp being processed
  2635. FileObject - Supplies the file object being processed
  2636. Return Value:
  2637. NTSTATUS - The result of this operation if it completes without
  2638. an exception.
  2639. --*/
  2640. {
  2641. PFILE_POSITION_INFORMATION Buffer;
  2642. DebugTrace(+1, Dbg, "FatSetPositionInfo...\n", 0);
  2643. Buffer = Irp->AssociatedIrp.SystemBuffer;
  2644. //
  2645. // Check if the file does not use intermediate buffering. If it
  2646. // does not use intermediate buffering then the new position we're
  2647. // supplied must be aligned properly for the device
  2648. //
  2649. if (FlagOn( FileObject->Flags, FO_NO_INTERMEDIATE_BUFFERING )) {
  2650. PDEVICE_OBJECT DeviceObject;
  2651. DeviceObject = IoGetCurrentIrpStackLocation( Irp )->DeviceObject;
  2652. if ((Buffer->CurrentByteOffset.LowPart & DeviceObject->AlignmentRequirement) != 0) {
  2653. DebugTrace(0, Dbg, "Cannot set position due to aligment conflict\n", 0);
  2654. DebugTrace(-1, Dbg, "FatSetPositionInfo -> %08lx\n", STATUS_INVALID_PARAMETER);
  2655. return STATUS_INVALID_PARAMETER;
  2656. }
  2657. }
  2658. //
  2659. // The input parameter is fine so set the current byte offset and
  2660. // complete the request
  2661. //
  2662. DebugTrace(0, Dbg, "Set the new position to %08lx\n", Buffer->CurrentByteOffset);
  2663. FileObject->CurrentByteOffset = Buffer->CurrentByteOffset;
  2664. DebugTrace(-1, Dbg, "FatSetPositionInfo -> %08lx\n", STATUS_SUCCESS);
  2665. UNREFERENCED_PARAMETER( IrpContext );
  2666. return STATUS_SUCCESS;
  2667. }
  2668. //
  2669. // Internal Support Routine
  2670. //
  2671. NTSTATUS
  2672. FatSetAllocationInfo (
  2673. IN PIRP_CONTEXT IrpContext,
  2674. IN PIRP Irp,
  2675. IN PFCB Fcb,
  2676. IN PFILE_OBJECT FileObject
  2677. )
  2678. /*++
  2679. Routine Description:
  2680. This routine performs the set Allocation information for fat. It either
  2681. completes the request or enqueues it off to the fsp.
  2682. Arguments:
  2683. Irp - Supplies the irp being processed
  2684. Fcb - Supplies the Fcb or Dcb being processed, already known not to
  2685. be the root dcb
  2686. FileObject - Supplies the FileObject being processed, already known not to
  2687. be the root dcb
  2688. Return Value:
  2689. NTSTATUS - The result of this operation if it completes without
  2690. an exception.
  2691. --*/
  2692. {
  2693. NTSTATUS Status = STATUS_SUCCESS;
  2694. PFILE_ALLOCATION_INFORMATION Buffer;
  2695. ULONG NewAllocationSize;
  2696. BOOLEAN FileSizeTruncated = FALSE;
  2697. BOOLEAN CacheMapInitialized = FALSE;
  2698. BOOLEAN ResourceAcquired = FALSE;
  2699. ULONG OriginalFileSize;
  2700. ULONG OriginalValidDataLength;
  2701. ULONG OriginalValidDataToDisk;
  2702. Buffer = Irp->AssociatedIrp.SystemBuffer;
  2703. NewAllocationSize = Buffer->AllocationSize.LowPart;
  2704. DebugTrace(+1, Dbg, "FatSetAllocationInfo.. to %08lx\n", NewAllocationSize);
  2705. //
  2706. // Allocation is only allowed on a file and not a directory
  2707. //
  2708. if (NodeType(Fcb) == FAT_NTC_DCB) {
  2709. DebugTrace(-1, Dbg, "Cannot change allocation of a directory\n", 0);
  2710. return STATUS_INVALID_DEVICE_REQUEST;
  2711. }
  2712. //
  2713. // Check that the new file allocation is legal
  2714. //
  2715. if (!FatIsIoRangeValid( Fcb->Vcb, Buffer->AllocationSize, 0 )) {
  2716. DebugTrace(-1, Dbg, "Illegal allocation size\n", 0);
  2717. return STATUS_DISK_FULL;
  2718. }
  2719. //
  2720. // If we haven't yet looked up the correct AllocationSize, do so.
  2721. //
  2722. if (Fcb->Header.AllocationSize.QuadPart == FCB_LOOKUP_ALLOCATIONSIZE_HINT) {
  2723. FatLookupFileAllocationSize( IrpContext, Fcb );
  2724. }
  2725. //
  2726. // This is kinda gross, but if the file is not cached, but there is
  2727. // a data section, we have to cache the file to avoid a bunch of
  2728. // extra work.
  2729. //
  2730. if ((FileObject->SectionObjectPointer->DataSectionObject != NULL) &&
  2731. (FileObject->SectionObjectPointer->SharedCacheMap == NULL) &&
  2732. !FlagOn(Irp->Flags, IRP_PAGING_IO)) {
  2733. ASSERT( !FlagOn( FileObject->Flags, FO_CLEANUP_COMPLETE ) );
  2734. //
  2735. // Now initialize the cache map.
  2736. //
  2737. CcInitializeCacheMap( FileObject,
  2738. (PCC_FILE_SIZES)&Fcb->Header.AllocationSize,
  2739. FALSE,
  2740. &FatData.CacheManagerCallbacks,
  2741. Fcb );
  2742. CacheMapInitialized = TRUE;
  2743. }
  2744. //
  2745. // Now mark the fact that the file needs to be truncated on close
  2746. //
  2747. Fcb->FcbState |= FCB_STATE_TRUNCATE_ON_CLOSE;
  2748. //
  2749. // Now mark that the time on the dirent needs to be updated on close.
  2750. //
  2751. SetFlag( FileObject->Flags, FO_FILE_MODIFIED );
  2752. try {
  2753. //
  2754. // Increase or decrease the allocation size.
  2755. //
  2756. if (NewAllocationSize > Fcb->Header.AllocationSize.LowPart) {
  2757. FatAddFileAllocation( IrpContext, Fcb, FileObject, NewAllocationSize);
  2758. } else {
  2759. //
  2760. // Check here if we will be decreasing file size and synchonize with
  2761. // paging IO.
  2762. //
  2763. if ( Fcb->Header.FileSize.LowPart > NewAllocationSize ) {
  2764. //
  2765. // Before we actually truncate, check to see if the purge
  2766. // is going to fail.
  2767. //
  2768. if (!MmCanFileBeTruncated( FileObject->SectionObjectPointer,
  2769. &Buffer->AllocationSize )) {
  2770. try_return( Status = STATUS_USER_MAPPED_FILE );
  2771. }
  2772. FileSizeTruncated = TRUE;
  2773. OriginalFileSize = Fcb->Header.FileSize.LowPart;
  2774. OriginalValidDataLength = Fcb->Header.ValidDataLength.LowPart;
  2775. OriginalValidDataToDisk = Fcb->ValidDataToDisk;
  2776. (VOID)ExAcquireResourceExclusiveLite( Fcb->Header.PagingIoResource, TRUE );
  2777. ResourceAcquired = TRUE;
  2778. Fcb->Header.FileSize.LowPart = NewAllocationSize;
  2779. //
  2780. // If we reduced the file size to less than the ValidDataLength,
  2781. // adjust the VDL. Likewise ValidDataToDisk.
  2782. //
  2783. if (Fcb->Header.ValidDataLength.LowPart > Fcb->Header.FileSize.LowPart) {
  2784. Fcb->Header.ValidDataLength.LowPart = Fcb->Header.FileSize.LowPart;
  2785. }
  2786. if (Fcb->ValidDataToDisk > Fcb->Header.FileSize.LowPart) {
  2787. Fcb->ValidDataToDisk = Fcb->Header.FileSize.LowPart;
  2788. }
  2789. }
  2790. //
  2791. // Now that File Size is down, actually do the truncate.
  2792. //
  2793. FatTruncateFileAllocation( IrpContext, Fcb, NewAllocationSize);
  2794. //
  2795. // Now check if we needed to decrease the file size accordingly.
  2796. //
  2797. if ( FileSizeTruncated ) {
  2798. //
  2799. // Tell the cache manager we reduced the file size.
  2800. // The call is unconditional, because MM always wants to know.
  2801. //
  2802. #if DBG
  2803. try {
  2804. #endif
  2805. CcSetFileSizes( FileObject, (PCC_FILE_SIZES)&Fcb->Header.AllocationSize );
  2806. #if DBG
  2807. } except(FatBugCheckExceptionFilter( GetExceptionInformation() )) {
  2808. NOTHING;
  2809. }
  2810. #endif
  2811. ASSERT( FileObject->DeleteAccess || FileObject->WriteAccess );
  2812. //
  2813. // There is no going back from this. If we run into problems updating
  2814. // the dirent we will have to live with the consequences. Not sending
  2815. // the notifies is likewise pretty benign compared to failing the entire
  2816. // operation and trying to back out everything, which could fail for the
  2817. // same reasons.
  2818. //
  2819. // If you want a transacted filesystem, use NTFS ...
  2820. //
  2821. FileSizeTruncated = FALSE;
  2822. FatSetFileSizeInDirent( IrpContext, Fcb, NULL );
  2823. //
  2824. // Report that we just reduced the file size.
  2825. //
  2826. FatNotifyReportChange( IrpContext,
  2827. Fcb->Vcb,
  2828. Fcb,
  2829. FILE_NOTIFY_CHANGE_SIZE,
  2830. FILE_ACTION_MODIFIED );
  2831. }
  2832. }
  2833. try_exit: NOTHING;
  2834. } finally {
  2835. if ( AbnormalTermination() && FileSizeTruncated ) {
  2836. Fcb->Header.FileSize.LowPart = OriginalFileSize;
  2837. Fcb->Header.ValidDataLength.LowPart = OriginalValidDataLength;
  2838. Fcb->ValidDataToDisk = OriginalValidDataToDisk;
  2839. //
  2840. // Make sure Cc knows the right filesize.
  2841. //
  2842. if (FileObject->SectionObjectPointer->SharedCacheMap != NULL) {
  2843. *CcGetFileSizePointer(FileObject) = Fcb->Header.FileSize;
  2844. }
  2845. ASSERT( Fcb->Header.FileSize.LowPart <= Fcb->Header.AllocationSize.LowPart );
  2846. }
  2847. if (CacheMapInitialized) {
  2848. CcUninitializeCacheMap( FileObject, NULL, NULL );
  2849. }
  2850. if (ResourceAcquired) {
  2851. ExReleaseResourceLite( Fcb->Header.PagingIoResource );
  2852. }
  2853. }
  2854. DebugTrace(-1, Dbg, "FatSetAllocationInfo -> %08lx\n", STATUS_SUCCESS);
  2855. return Status;
  2856. }
  2857. //
  2858. // Internal Support Routine
  2859. //
  2860. NTSTATUS
  2861. FatSetEndOfFileInfo (
  2862. IN PIRP_CONTEXT IrpContext,
  2863. IN PIRP Irp,
  2864. IN PFILE_OBJECT FileObject,
  2865. IN PVCB Vcb,
  2866. IN PFCB Fcb
  2867. )
  2868. /*++
  2869. Routine Description:
  2870. This routine performs the set End of File information for fat. It either
  2871. completes the request or enqueues it off to the fsp.
  2872. Arguments:
  2873. Irp - Supplies the irp being processed
  2874. FileObject - Supplies the file object being processed
  2875. Vcb - Supplies the Vcb being processed
  2876. Fcb - Supplies the Fcb or Dcb being processed, already known not to
  2877. be the root dcb
  2878. Return Value:
  2879. NTSTATUS - The result of this operation if it completes without
  2880. an exception.
  2881. --*/
  2882. {
  2883. NTSTATUS Status;
  2884. PFILE_END_OF_FILE_INFORMATION Buffer;
  2885. ULONG NewFileSize;
  2886. ULONG InitialFileSize;
  2887. ULONG InitialValidDataLength;
  2888. ULONG InitialValidDataToDisk;
  2889. BOOLEAN CacheMapInitialized = FALSE;
  2890. BOOLEAN UnwindFileSizes = FALSE;
  2891. BOOLEAN ResourceAcquired = FALSE;
  2892. DebugTrace(+1, Dbg, "FatSetEndOfFileInfo...\n", 0);
  2893. Buffer = Irp->AssociatedIrp.SystemBuffer;
  2894. try {
  2895. //
  2896. // File Size changes are only allowed on a file and not a directory
  2897. //
  2898. if (NodeType(Fcb) != FAT_NTC_FCB) {
  2899. DebugTrace(0, Dbg, "Cannot change size of a directory\n", 0);
  2900. try_return( Status = STATUS_INVALID_DEVICE_REQUEST );
  2901. }
  2902. //
  2903. // Check that the new file size is legal
  2904. //
  2905. if (!FatIsIoRangeValid( Fcb->Vcb, Buffer->EndOfFile, 0 )) {
  2906. DebugTrace(0, Dbg, "Illegal allocation size\n", 0);
  2907. try_return( Status = STATUS_DISK_FULL );
  2908. }
  2909. NewFileSize = Buffer->EndOfFile.LowPart;
  2910. //
  2911. // If we haven't yet looked up the correct AllocationSize, do so.
  2912. //
  2913. if (Fcb->Header.AllocationSize.QuadPart == FCB_LOOKUP_ALLOCATIONSIZE_HINT) {
  2914. FatLookupFileAllocationSize( IrpContext, Fcb );
  2915. }
  2916. //
  2917. // This is kinda gross, but if the file is not cached, but there is
  2918. // a data section, we have to cache the file to avoid a bunch of
  2919. // extra work.
  2920. //
  2921. if ((FileObject->SectionObjectPointer->DataSectionObject != NULL) &&
  2922. (FileObject->SectionObjectPointer->SharedCacheMap == NULL) &&
  2923. !FlagOn(Irp->Flags, IRP_PAGING_IO)) {
  2924. if (FlagOn( FileObject->Flags, FO_CLEANUP_COMPLETE )) {
  2925. //
  2926. // This IRP has raced (and lost) with a close (=>cleanup)
  2927. // on the same fileobject. We don't want to reinitialise the
  2928. // cachemap here now because we'll leak it (unless we do so &
  2929. // then tear it down again here, which is too much of a change at
  2930. // this stage). So we'll just say the file is closed - which
  2931. // is arguably the right thing to do anyway, since a caller
  2932. // racing operations in this way is broken. The only stumbling
  2933. // block is possibly filters - do they operate on cleaned
  2934. // up fileobjects?
  2935. //
  2936. FatRaiseStatus( IrpContext, STATUS_FILE_CLOSED);
  2937. }
  2938. //
  2939. // Now initialize the cache map.
  2940. //
  2941. CcInitializeCacheMap( FileObject,
  2942. (PCC_FILE_SIZES)&Fcb->Header.AllocationSize,
  2943. FALSE,
  2944. &FatData.CacheManagerCallbacks,
  2945. Fcb );
  2946. CacheMapInitialized = TRUE;
  2947. }
  2948. //
  2949. // Do a special case here for the lazy write of file sizes.
  2950. //
  2951. if (IoGetCurrentIrpStackLocation(Irp)->Parameters.SetFile.AdvanceOnly) {
  2952. //
  2953. // Only attempt this if the file hasn't been "deleted on close" and
  2954. // this is a good FCB.
  2955. //
  2956. if (!IsFileDeleted( IrpContext, Fcb ) && (Fcb->FcbCondition == FcbGood)) {
  2957. PDIRENT Dirent;
  2958. PBCB DirentBcb;
  2959. //
  2960. // Never have the dirent filesize larger than the fcb filesize
  2961. //
  2962. if (NewFileSize >= Fcb->Header.FileSize.LowPart) {
  2963. NewFileSize = Fcb->Header.FileSize.LowPart;
  2964. }
  2965. //
  2966. // Make sure we don't set anything higher than the alloc size.
  2967. //
  2968. ASSERT( NewFileSize <= Fcb->Header.AllocationSize.LowPart );
  2969. //
  2970. // Only advance the file size, never reduce it with this call
  2971. //
  2972. FatGetDirentFromFcbOrDcb( IrpContext,
  2973. Fcb,
  2974. &Dirent,
  2975. &DirentBcb );
  2976. ASSERT( Dirent && DirentBcb );
  2977. try {
  2978. if ( NewFileSize > Dirent->FileSize ) {
  2979. Dirent->FileSize = NewFileSize;
  2980. FatSetDirtyBcb( IrpContext, DirentBcb, Fcb->Vcb, TRUE );
  2981. //
  2982. // Report that we just changed the file size.
  2983. //
  2984. FatNotifyReportChange( IrpContext,
  2985. Vcb,
  2986. Fcb,
  2987. FILE_NOTIFY_CHANGE_SIZE,
  2988. FILE_ACTION_MODIFIED );
  2989. }
  2990. } finally {
  2991. FatUnpinBcb( IrpContext, DirentBcb );
  2992. }
  2993. } else {
  2994. DebugTrace(0, Dbg, "Cannot set size on deleted file.\n", 0);
  2995. }
  2996. try_return( Status = STATUS_SUCCESS );
  2997. }
  2998. //
  2999. // Check if the new file size is greater than the current
  3000. // allocation size. If it is then we need to increase the
  3001. // allocation size.
  3002. //
  3003. if ( NewFileSize > Fcb->Header.AllocationSize.LowPart ) {
  3004. //
  3005. // Change the file allocation
  3006. //
  3007. FatAddFileAllocation( IrpContext, Fcb, FileObject, NewFileSize );
  3008. }
  3009. //
  3010. // At this point we have enough allocation for the file.
  3011. // So check if we are really changing the file size
  3012. //
  3013. if (Fcb->Header.FileSize.LowPart != NewFileSize) {
  3014. if ( NewFileSize < Fcb->Header.FileSize.LowPart ) {
  3015. //
  3016. // Before we actually truncate, check to see if the purge
  3017. // is going to fail.
  3018. //
  3019. if (!MmCanFileBeTruncated( FileObject->SectionObjectPointer,
  3020. &Buffer->EndOfFile )) {
  3021. try_return( Status = STATUS_USER_MAPPED_FILE );
  3022. }
  3023. //
  3024. // This call is unconditional, because MM always wants to know.
  3025. // Also serialize here with paging io since we are truncating
  3026. // the file size.
  3027. //
  3028. ResourceAcquired =
  3029. ExAcquireResourceExclusiveLite( Fcb->Header.PagingIoResource, TRUE );
  3030. }
  3031. //
  3032. // Set the new file size
  3033. //
  3034. InitialFileSize = Fcb->Header.FileSize.LowPart;
  3035. InitialValidDataLength = Fcb->Header.ValidDataLength.LowPart;
  3036. InitialValidDataToDisk = Fcb->ValidDataToDisk;
  3037. UnwindFileSizes = TRUE;
  3038. Fcb->Header.FileSize.LowPart = NewFileSize;
  3039. //
  3040. // If we reduced the file size to less than the ValidDataLength,
  3041. // adjust the VDL. Likewise ValidDataToDisk.
  3042. //
  3043. if (Fcb->Header.ValidDataLength.LowPart > NewFileSize) {
  3044. Fcb->Header.ValidDataLength.LowPart = NewFileSize;
  3045. }
  3046. if (Fcb->ValidDataToDisk > NewFileSize) {
  3047. Fcb->ValidDataToDisk = NewFileSize;
  3048. }
  3049. DebugTrace(0, Dbg, "New file size is 0x%08lx.\n", NewFileSize);
  3050. //
  3051. // We must now update the cache mapping (benign if not cached).
  3052. //
  3053. CcSetFileSizes( FileObject,
  3054. (PCC_FILE_SIZES)&Fcb->Header.AllocationSize );
  3055. FatSetFileSizeInDirent( IrpContext, Fcb, NULL );
  3056. //
  3057. // Report that we just changed the file size.
  3058. //
  3059. FatNotifyReportChange( IrpContext,
  3060. Vcb,
  3061. Fcb,
  3062. FILE_NOTIFY_CHANGE_SIZE,
  3063. FILE_ACTION_MODIFIED );
  3064. //
  3065. // Mark the fact that the file will need to checked for
  3066. // truncation on cleanup.
  3067. //
  3068. SetFlag( Fcb->FcbState, FCB_STATE_TRUNCATE_ON_CLOSE );
  3069. }
  3070. //
  3071. // Set this handle as having modified the file
  3072. //
  3073. FileObject->Flags |= FO_FILE_MODIFIED;
  3074. //
  3075. // Set our return status to success
  3076. //
  3077. Status = STATUS_SUCCESS;
  3078. try_exit: NOTHING;
  3079. FatUnpinRepinnedBcbs( IrpContext );
  3080. } finally {
  3081. DebugUnwind( FatSetEndOfFileInfo );
  3082. if (AbnormalTermination() && UnwindFileSizes) {
  3083. Fcb->Header.FileSize.LowPart = InitialFileSize;
  3084. Fcb->Header.ValidDataLength.LowPart = InitialValidDataLength;
  3085. Fcb->ValidDataToDisk = InitialValidDataToDisk;
  3086. if (FileObject->SectionObjectPointer->SharedCacheMap != NULL) {
  3087. *CcGetFileSizePointer(FileObject) = Fcb->Header.FileSize;
  3088. }
  3089. }
  3090. if (CacheMapInitialized) {
  3091. CcUninitializeCacheMap( FileObject, NULL, NULL );
  3092. }
  3093. if ( ResourceAcquired ) {
  3094. ExReleaseResourceLite( Fcb->Header.PagingIoResource );
  3095. }
  3096. DebugTrace(-1, Dbg, "FatSetEndOfFileInfo -> %08lx\n", Status);
  3097. }
  3098. return Status;
  3099. }
  3100. //
  3101. // Internal Support Routine
  3102. //
  3103. VOID
  3104. FatDeleteFile (
  3105. IN PIRP_CONTEXT IrpContext,
  3106. IN PDCB TargetDcb,
  3107. IN ULONG LfnOffset,
  3108. IN ULONG DirentOffset,
  3109. IN PDIRENT Dirent,
  3110. IN PUNICODE_STRING Lfn
  3111. )
  3112. {
  3113. PFCB Fcb;
  3114. PLIST_ENTRY Links;
  3115. //
  3116. // We can do the replace by removing the other Fcb(s) from
  3117. // the prefix table.
  3118. //
  3119. for (Links = TargetDcb->Specific.Dcb.ParentDcbQueue.Flink;
  3120. Links != &TargetDcb->Specific.Dcb.ParentDcbQueue;
  3121. Links = Links->Flink) {
  3122. Fcb = CONTAINING_RECORD( Links, FCB, ParentDcbLinks );
  3123. if (FlagOn(Fcb->FcbState, FCB_STATE_NAMES_IN_SPLAY_TREE) &&
  3124. (Fcb->DirentOffsetWithinDirectory == DirentOffset)) {
  3125. ASSERT( NodeType(Fcb) == FAT_NTC_FCB );
  3126. ASSERT( Fcb->LfnOffsetWithinDirectory == LfnOffset );
  3127. if ( Fcb->UncleanCount != 0 ) {
  3128. FatBugCheck(0,0,0);
  3129. } else {
  3130. PERESOURCE Resource;
  3131. //
  3132. // Make this fcb "appear" deleted, synchronizing with
  3133. // paging IO.
  3134. //
  3135. FatRemoveNames( IrpContext, Fcb );
  3136. Resource = Fcb->Header.PagingIoResource;
  3137. (VOID)ExAcquireResourceExclusiveLite( Resource, TRUE );
  3138. SetFlag(Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE);
  3139. Fcb->ValidDataToDisk = 0;
  3140. Fcb->Header.FileSize.QuadPart =
  3141. Fcb->Header.ValidDataLength.QuadPart = 0;
  3142. Fcb->FirstClusterOfFile = 0;
  3143. ExReleaseResourceLite( Resource );
  3144. }
  3145. }
  3146. }
  3147. //
  3148. // The file is not currently opened so we can delete the file
  3149. // that is being overwritten. To do the operation we dummy
  3150. // up an fcb, truncate allocation, delete the fcb, and delete
  3151. // the dirent.
  3152. //
  3153. Fcb = FatCreateFcb( IrpContext,
  3154. TargetDcb->Vcb,
  3155. TargetDcb,
  3156. LfnOffset,
  3157. DirentOffset,
  3158. Dirent,
  3159. Lfn,
  3160. FALSE,
  3161. FALSE );
  3162. Fcb->Header.FileSize.LowPart = 0;
  3163. try {
  3164. FatTruncateFileAllocation( IrpContext, Fcb, 0 );
  3165. FatDeleteDirent( IrpContext, Fcb, NULL, TRUE );
  3166. } finally {
  3167. FatDeleteFcb( IrpContext, Fcb );
  3168. }
  3169. }
  3170. //
  3171. // Internal Support Routine
  3172. //
  3173. VOID
  3174. FatRenameEAs (
  3175. IN PIRP_CONTEXT IrpContext,
  3176. IN PFCB Fcb,
  3177. IN USHORT ExtendedAttributes,
  3178. IN POEM_STRING OldOemName
  3179. )
  3180. {
  3181. BOOLEAN LockedEaFcb = FALSE;
  3182. PBCB EaBcb = NULL;
  3183. PDIRENT EaDirent;
  3184. EA_RANGE EaSetRange;
  3185. PEA_SET_HEADER EaSetHeader;
  3186. PVCB Vcb;
  3187. RtlZeroMemory( &EaSetRange, sizeof( EA_RANGE ));
  3188. Vcb = Fcb->Vcb;
  3189. try {
  3190. //
  3191. // Use a try-except to catch any errors.
  3192. //
  3193. try {
  3194. //
  3195. // Try to get the Ea file object. Return FALSE on failure.
  3196. //
  3197. FatGetEaFile( IrpContext,
  3198. Vcb,
  3199. &EaDirent,
  3200. &EaBcb,
  3201. FALSE,
  3202. FALSE );
  3203. LockedEaFcb = TRUE;
  3204. //
  3205. // If we didn't get the file because it doesn't exist, then the
  3206. // disk is corrupted. We do nothing here.
  3207. //
  3208. if (Vcb->VirtualEaFile != NULL) {
  3209. //
  3210. // Try to pin down the Ea set header for the index in the
  3211. // dirent. If the operation doesn't complete, return FALSE
  3212. // from this routine.
  3213. //
  3214. FatReadEaSet( IrpContext,
  3215. Vcb,
  3216. ExtendedAttributes,
  3217. OldOemName,
  3218. FALSE,
  3219. &EaSetRange );
  3220. EaSetHeader = (PEA_SET_HEADER) EaSetRange.Data;
  3221. //
  3222. // We now have the Ea set header for this file. We simply
  3223. // overwrite the owning file name.
  3224. //
  3225. RtlZeroMemory( EaSetHeader->OwnerFileName, 14 );
  3226. RtlCopyMemory( EaSetHeader->OwnerFileName,
  3227. Fcb->ShortName.Name.Oem.Buffer,
  3228. Fcb->ShortName.Name.Oem.Length );
  3229. FatMarkEaRangeDirty( IrpContext, Vcb->VirtualEaFile, &EaSetRange );
  3230. FatUnpinEaRange( IrpContext, &EaSetRange );
  3231. CcFlushCache( Vcb->VirtualEaFile->SectionObjectPointer, NULL, 0, NULL );
  3232. }
  3233. } except(FatExceptionFilter( IrpContext, GetExceptionInformation() )) {
  3234. //
  3235. // We catch all exceptions that Fat catches, but don't do
  3236. // anything with them.
  3237. //
  3238. }
  3239. } finally {
  3240. //
  3241. // Unpin the EaDirent and the EaSetHeader if pinned.
  3242. //
  3243. FatUnpinBcb( IrpContext, EaBcb );
  3244. FatUnpinEaRange( IrpContext, &EaSetRange );
  3245. //
  3246. // Release the Fcb for the Ea file if locked.
  3247. //
  3248. if (LockedEaFcb) {
  3249. FatReleaseFcb( IrpContext, Vcb->EaFcb );
  3250. }
  3251. }
  3252. return;
  3253. }