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

627 lines
22 KiB

  1. //+---------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1995 - 2000.
  5. //
  6. // File: CIOPLOCK.CXX
  7. //
  8. // Contents: Oplock support for filtering documents
  9. //
  10. // Classes: CFilterOplock
  11. //
  12. // History: 03-Jul-95 DwightKr Created.
  13. //
  14. //----------------------------------------------------------------------------
  15. #include <pch.cxx>
  16. #pragma hdrstop
  17. #include <ntopen.hxx>
  18. #include <cioplock.hxx>
  19. const WCHAR * g_aOplockException[] =
  20. {
  21. L"asf",
  22. L"asx",
  23. L"avi",
  24. L"m1v",
  25. L"mov",
  26. L"mp2",
  27. L"mp3",
  28. L"mpa",
  29. L"mpe",
  30. L"mpeg",
  31. L"mpg",
  32. L"mpv2",
  33. L"qt",
  34. L"wma",
  35. L"wmv",
  36. L"wvx",
  37. };
  38. const unsigned g_cOplockException = sizeof g_aOplockException /
  39. sizeof g_aOplockException[0];
  40. //+---------------------------------------------------------------------------
  41. //
  42. // Function: IsOplockException
  43. //
  44. // Synopsis: Checks if the extension on a file makes us want to not take
  45. // the oplock because filtering properties will open the file
  46. // in an incompatible mode.
  47. //
  48. // Arguments: [pwcPath] -- The path of the file to check
  49. //
  50. // History: 1-Feb-01 dlee Created
  51. //
  52. //----------------------------------------------------------------------------
  53. BOOL IsOplockException( WCHAR const * pwcPath )
  54. {
  55. WCHAR const * pwc = wcsrchr( pwcPath, L'.' );
  56. if ( 0 == pwc )
  57. return FALSE;
  58. pwc++;
  59. for ( unsigned i = 0; i < g_cOplockException; i++ )
  60. if ( !wcscmp( pwc, g_aOplockException[i] ) )
  61. return TRUE;
  62. return FALSE;
  63. } //IsOplockException
  64. //+---------------------------------------------------------------------------
  65. //
  66. // Member: CFilterOplock::CFilterOplock
  67. //
  68. // Synopsis: Takes an oplock on the file object specified
  69. //
  70. // Arguments: [wcsFileName] -- name of file to take oplock on
  71. //
  72. // History: 03-Jul-95 DwightKr Created
  73. // 21-Feb-96 DwightKr Add support for OPLocks on NTFS
  74. // directories
  75. //
  76. // Notes: NTFS doesn't support oplocks on directories. Change
  77. // this routine's expectations when directory oplocks
  78. // are supported.
  79. //
  80. //----------------------------------------------------------------------------
  81. CFilterOplock::CFilterOplock( const CFunnyPath & funnyFileName, BOOL fTakeOplock )
  82. : _hFileOplock(INVALID_HANDLE_VALUE),
  83. _hFileNormal(INVALID_HANDLE_VALUE),
  84. _hLockEvent(INVALID_HANDLE_VALUE),
  85. _funnyFileName( funnyFileName ),
  86. _fWriteAccess( TRUE )
  87. {
  88. const BOOL fDrivePath = ( !funnyFileName.IsRemote() &&
  89. funnyFileName.GetActualLength() > 2 );
  90. HANDLE handle = INVALID_HANDLE_VALUE;
  91. NTSTATUS Status;
  92. SHandle xLockHandle; // save handle in a smart pointer
  93. fTakeOplock = fTakeOplock && fDrivePath;
  94. BOOL fAppendBackSlash = FALSE;
  95. // For volume \\?\D:, NtQueryInformationFile fails with the following
  96. // error: 0xC0000010L - STATUS_INVALID_DEVICE_REQUEST. Need to append a \,
  97. // to make it work. We need to append the '\'only in case of volume path.
  98. if ( !funnyFileName.IsRemote() && 2 == funnyFileName.GetActualLength() )
  99. {
  100. Win4Assert( L':' == (funnyFileName.GetActualPath())[1] );
  101. ((CFunnyPath&)funnyFileName).AppendBackSlash(); // override const
  102. fAppendBackSlash = TRUE;
  103. }
  104. //
  105. // Major work-around here. The shell IPropertySetStorage routines open
  106. // these filetypes through wmi which open the file GENERIC_WRITE. This
  107. // will deadlock the filter thread with itself since it's incompatible
  108. // with the oplock.
  109. //
  110. if ( fTakeOplock && IsOplockException( funnyFileName.GetPath() ) )
  111. fTakeOplock = FALSE;
  112. //
  113. // Make this case work (for SPS): \\.\backofficestorage...
  114. // Funyypath treats it as a remote path. Detect this and change
  115. // \\?\UNC\.\backoffice
  116. // into
  117. // \\?\UN\\.\backoffice
  118. // then pass this into Rtl: \\.\backoffice
  119. //
  120. WCHAR *pwcPath = (WCHAR *) funnyFileName.GetPath();
  121. BOOL fCtoBack = FALSE;
  122. if ( pwcPath[8] == L'.' &&
  123. pwcPath[6] == L'C' )
  124. {
  125. pwcPath[6] = L'\\';
  126. pwcPath = pwcPath + 6;
  127. fCtoBack = TRUE;
  128. }
  129. UNICODE_STRING uScope;
  130. if ( !RtlDosPathNameToNtPathName_U( pwcPath,
  131. &uScope,
  132. 0,
  133. 0 ) )
  134. {
  135. ciDebugOut(( DEB_ERROR, "Error converting %ws to Nt path\n", funnyFileName.GetPath() ));
  136. THROW( CException(STATUS_INSUFFICIENT_RESOURCES) );
  137. }
  138. if ( fAppendBackSlash )
  139. {
  140. ((CFunnyPath&)funnyFileName).RemoveBackSlash(); // override const
  141. }
  142. if ( fCtoBack )
  143. pwcPath[6] = L'C';
  144. IO_STATUS_BLOCK IoStatus;
  145. OBJECT_ATTRIBUTES ObjectAttr;
  146. InitializeObjectAttributes( &ObjectAttr, // Structure
  147. &uScope, // Name
  148. OBJ_CASE_INSENSITIVE, // Attributes
  149. 0, // Root
  150. 0 ); // Security
  151. //
  152. // Don't try to take oplocks on UNC shares. Gibraltar doesn't support
  153. // redirected network drives. So, don't worry about redirected network
  154. // drives. Testing if a drive letter is a network drive is not very
  155. // cheap.
  156. //
  157. BOOL fOplockNotSupported = !fTakeOplock;
  158. SHandle xOplockHandle(INVALID_HANDLE_VALUE);
  159. if ( fTakeOplock )
  160. {
  161. ULONG modeAccess = FILE_READ_ATTRIBUTES;
  162. ULONG modeShare = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
  163. ULONG modeCreate = FILE_OPEN;
  164. ULONG modeAttribute = FILE_ATTRIBUTE_NORMAL;
  165. ULONG modeCreateOptions = FILE_RESERVE_OPFILTER;
  166. Status = NtCreateFile( &handle, // Handle
  167. modeAccess, // Access
  168. &ObjectAttr, // Object Attributes
  169. &IoStatus, // I/O Status block
  170. 0, // Allocation Size
  171. modeAttribute, // File Attributes
  172. modeShare, // File Sharing
  173. modeCreate, // Create Disposition
  174. modeCreateOptions, // Create Options
  175. 0, // EA Buffer
  176. 0 ); // EA Buffer Length
  177. if ( NT_SUCCESS(Status) )
  178. Status = IoStatus.Status;
  179. //
  180. // Note: Keep uScope.Buffer around for the other open which occurs
  181. // below.
  182. //
  183. if ( !NT_SUCCESS(Status) )
  184. {
  185. //
  186. // Failed to get oplock. Continue on though; this may have been
  187. // due to a failure to get an oplock on a directory.
  188. //
  189. ciDebugOut(( DEB_IWARN,
  190. "CFilterOplock failed to NtCreateFile(%ws) status = 0x%x\n",
  191. funnyFileName.GetPath(),
  192. Status ));
  193. }
  194. else
  195. {
  196. //
  197. // Oplock open succeeded. Try to actually get the oplock.
  198. //
  199. xOplockHandle.Set(handle); // Save in the smart pointer
  200. //
  201. // Create event (signalled on oplock break)
  202. //
  203. Status = NtCreateEvent( &handle,
  204. EVENT_ALL_ACCESS,
  205. 0,
  206. NotificationEvent,
  207. TRUE );
  208. if (! NT_SUCCESS(Status) )
  209. {
  210. ciDebugOut(( DEB_ERROR, "Error creating oplock event\n" ));
  211. THROW( CException(Status) );
  212. }
  213. xLockHandle.Set( handle );
  214. _IoStatus.Status = STATUS_SUCCESS;
  215. _IoStatus.Information = 0;
  216. Status = NtFsControlFile( xOplockHandle.Get(),
  217. xLockHandle.Get(),
  218. 0,
  219. 0,
  220. &_IoStatus,
  221. FSCTL_REQUEST_FILTER_OPLOCK,
  222. 0,
  223. 0,
  224. 0,
  225. 0 );
  226. if ( !NT_SUCCESS(Status) )
  227. {
  228. if ( STATUS_INVALID_DEVICE_REQUEST == Status )
  229. fOplockNotSupported = TRUE;
  230. ciDebugOut(( DEB_IWARN,
  231. "CFilterOplock failed to NtFsControlFile(%ws) status = 0x%x\n",
  232. funnyFileName.GetPath(),
  233. Status ));
  234. NtClose( xOplockHandle.Acquire() );
  235. NtClose( xLockHandle.Acquire() );
  236. }
  237. }
  238. #if CIDBG == 1
  239. if ( ( ! NT_SUCCESS(Status) ) && fDrivePath )
  240. {
  241. //
  242. // If we failed to take a filter oplock this is okay as long
  243. // as either:
  244. // 1. the file type is a directory and the FS is NTFS.
  245. // 2. the file system does not support filter oplocks.
  246. //
  247. WCHAR wcsDrive[4];
  248. wcsncpy( wcsDrive, funnyFileName.GetActualPath(), 3 );
  249. wcsDrive[3] = L'\0';
  250. Win4Assert( wcsDrive[1] == L':' && wcsDrive[2] == L'\\' );
  251. WCHAR wcsFileSystemName[10];
  252. wcsFileSystemName[0] = 0;
  253. if ( !GetVolumeInformation( wcsDrive,
  254. 0,0,0,0,0,
  255. wcsFileSystemName,
  256. sizeof wcsFileSystemName / sizeof (WCHAR)
  257. ) )
  258. {
  259. THROW( CException() );
  260. }
  261. if ( _wcsicmp(wcsFileSystemName, L"NTFS") == 0 )
  262. {
  263. Win4Assert( !fOplockNotSupported &&
  264. _wcsicmp(wcsFileSystemName, L"NTFS") == 0 );
  265. }
  266. }
  267. #endif // CIDBG == 1
  268. }
  269. //
  270. // Now open the file handle for normal access to the file.
  271. // The oplock handle should only be used for the oplock.
  272. //
  273. InitializeObjectAttributes( &ObjectAttr, // Structure
  274. &uScope, // Name
  275. OBJ_CASE_INSENSITIVE, // Attributes
  276. 0, // Root
  277. 0 ); // Security
  278. ULONG modeAccess = READ_CONTROL | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES;
  279. ULONG modeShare = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
  280. ULONG modeCreate = FILE_OPEN;
  281. ULONG modeAttribute = FILE_ATTRIBUTE_NORMAL;
  282. ULONG modeCreateOptions = 0;
  283. Status = NtCreateFile( &handle, // Handle
  284. modeAccess, // Access
  285. &ObjectAttr, // Object Attributes
  286. &IoStatus, // I/O Status block
  287. 0, // Allocation Size
  288. modeAttribute, // File Attributes
  289. modeShare, // File Sharing
  290. modeCreate, // Create Disposition
  291. modeCreateOptions, // Create Options
  292. 0, // EA Buffer
  293. 0 ); // EA Buffer Length
  294. if ( NT_SUCCESS(Status) )
  295. Status = IoStatus.Status;
  296. if ( STATUS_ACCESS_DENIED == Status )
  297. {
  298. // The open failed. Try again without requesting FILE_WRITE_ATTRIBUTE
  299. // access.
  300. _fWriteAccess = FALSE;
  301. modeAccess &= ~FILE_WRITE_ATTRIBUTES;
  302. Status = NtCreateFile( &handle, // Handle
  303. modeAccess, // Access
  304. &ObjectAttr, // Object Attributes
  305. &IoStatus, // I/O Status block
  306. 0, // Allocation Size
  307. modeAttribute, // File Attributes
  308. modeShare, // File Sharing
  309. modeCreate, // Create Disposition
  310. modeCreateOptions, // Create Options
  311. 0, // EA Buffer
  312. 0 ); // EA Buffer Length
  313. if ( NT_SUCCESS(Status) )
  314. Status = IoStatus.Status;
  315. }
  316. RtlFreeHeap( RtlProcessHeap(), 0, uScope.Buffer );
  317. if ( !NT_SUCCESS( Status ) )
  318. {
  319. ciDebugOut(( DEB_IERROR, "CFilterOplock - Error opening %ws as normal file\n", funnyFileName.GetPath() ));
  320. QUIETTHROW( CException(Status) );
  321. }
  322. SHandle xNormalHandle( handle );
  323. Status = NtQueryInformationFile( xNormalHandle.Get(), // File handle
  324. &IoStatus, // I/O Status
  325. &_BasicInfo, // Buffer
  326. sizeof _BasicInfo, // Buffer size
  327. FileBasicInformation );
  328. if ( NT_SUCCESS(Status) )
  329. Status = IoStatus.Status;
  330. if ( !NT_SUCCESS(Status) )
  331. {
  332. ciDebugOut(( DEB_IERROR, "CFilterOplock - Error 0x%x querying file info for %ws.\n",
  333. Status, funnyFileName.GetPath() ));
  334. QUIETTHROW( CException(Status) );
  335. }
  336. _fDirectory = (_BasicInfo.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
  337. if ( INVALID_HANDLE_VALUE == xOplockHandle.Get() &&
  338. ! ( _fDirectory || fOplockNotSupported ) )
  339. {
  340. ciDebugOut(( DEB_ITRACE,
  341. "CFilterOplock failed to aquire oplock (%ws) %d %d\n",
  342. funnyFileName.GetPath(), _fDirectory, fOplockNotSupported ));
  343. QUIETTHROW( CException(STATUS_OPLOCK_BREAK_IN_PROGRESS) );
  344. }
  345. _hLockEvent = xLockHandle.Acquire();
  346. _hFileOplock = xOplockHandle.Acquire();
  347. _hFileNormal = xNormalHandle.Acquire();
  348. }
  349. //+---------------------------------------------------------------------------
  350. //
  351. // Member: CFilterOplock::~CFilterOplock, public
  352. //
  353. // Synopsis: Destructor
  354. //
  355. // History: 03-Jul-95 DwightKr Created
  356. //
  357. //----------------------------------------------------------------------------
  358. CFilterOplock::~CFilterOplock()
  359. {
  360. if ( INVALID_HANDLE_VALUE != _hFileNormal )
  361. NtClose(_hFileNormal);
  362. //
  363. // Make sure this is the last file handle closed.
  364. //
  365. if ( INVALID_HANDLE_VALUE != _hFileOplock )
  366. NtClose(_hFileOplock);
  367. if ( INVALID_HANDLE_VALUE != _hLockEvent )
  368. {
  369. //
  370. // We MUST wait until the lock event has been completed to
  371. // prevent a race between APC call and the cleanup.
  372. //
  373. NTSTATUS Status = NtWaitForSingleObject( _hLockEvent,
  374. FALSE,
  375. 0 // Infinite
  376. );
  377. NtClose( _hLockEvent );
  378. }
  379. }
  380. //+---------------------------------------------------------------------------
  381. //
  382. // Member: CFilterOplock::IsOplockBroken, public
  383. //
  384. // Synopsis: Tests for a broken oplock
  385. //
  386. // History: 03-Jul-95 DwightKr Created
  387. //
  388. //----------------------------------------------------------------------------
  389. BOOL CFilterOplock::IsOplockBroken() const
  390. {
  391. if ( INVALID_HANDLE_VALUE != _hLockEvent )
  392. {
  393. static LARGE_INTEGER li = {0,0};
  394. NTSTATUS Status = NtWaitForSingleObject( _hLockEvent,
  395. FALSE,
  396. &li );
  397. if ( STATUS_SUCCESS == Status )
  398. {
  399. if ( STATUS_NOT_IMPLEMENTED == _IoStatus.Status )
  400. {
  401. return FALSE;
  402. }
  403. else if ( STATUS_OPLOCK_NOT_GRANTED != _IoStatus.Status )
  404. {
  405. return TRUE;
  406. }
  407. }
  408. }
  409. return FALSE;
  410. }
  411. //+---------------------------------------------------------------------------
  412. //
  413. // Member: CFilterOplock::MaybeSetLastAccessTime, public
  414. //
  415. // Synopsis: Restores the last access time to value on oplock open
  416. //
  417. // Arguments: [ulDelay] -- If file is < [ulDelay] days old, time not written
  418. //
  419. // History: 01-Jul-98 KyleP Created
  420. //
  421. //----------------------------------------------------------------------------
  422. void CFilterOplock::MaybeSetLastAccessTime( ULONG ulDelay )
  423. {
  424. ULONGLONG const OneDay = 24i64 * 60i64 * 60i64 * 10000000i64;
  425. if ( _fWriteAccess && !IsOplockBroken() && !_funnyFileName.IsRemote() )
  426. {
  427. FILETIME ft;
  428. GetSystemTimeAsFileTime( &ft );
  429. ULONGLONG ftLastAccess = (ULONGLONG) _BasicInfo.LastAccessTime.QuadPart;
  430. if ( ( *(ULONGLONG *)&ft - ftLastAccess ) > (OneDay * ulDelay) )
  431. {
  432. do
  433. {
  434. //
  435. // The normal file handle may have been closed and will need reopening...
  436. //
  437. NTSTATUS Status = STATUS_SUCCESS;
  438. if ( INVALID_HANDLE_VALUE == _hFileNormal )
  439. {
  440. Status = CiNtOpenNoThrow( _hFileNormal,
  441. _funnyFileName.GetActualPath(),
  442. READ_CONTROL | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES,
  443. FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
  444. 0 );
  445. }
  446. if ( !NT_SUCCESS(Status) )
  447. {
  448. ciDebugOut(( DEB_WARN,
  449. "CFilterOplock::MaybeSetLastAccessTime -- Error 0x%x re-opening %ws\n",
  450. Status, _funnyFileName.GetActualPath() ));
  451. break;
  452. }
  453. //
  454. // Open volume. Needed to mark USN Journal entry.
  455. //
  456. WCHAR wszVolumePath[] = L"\\\\.\\a:";
  457. wszVolumePath[4] = _funnyFileName.GetActualPath()[0];
  458. HANDLE hVolume = CreateFile( wszVolumePath,
  459. FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES,
  460. FILE_SHARE_READ | FILE_SHARE_WRITE,
  461. NULL,
  462. OPEN_EXISTING,
  463. 0,
  464. NULL );
  465. if ( hVolume == INVALID_HANDLE_VALUE )
  466. {
  467. ciDebugOut(( DEB_ERROR,
  468. "CFilterOplock::MaybeSetLastAccessTime -- Error %u opening volume\n",
  469. GetLastError() ));
  470. break;
  471. }
  472. SWin32Handle xHandleVolume( hVolume );
  473. IO_STATUS_BLOCK IoStatus;
  474. MARK_HANDLE_INFO hiCiIgnore = { USN_SOURCE_AUXILIARY_DATA, hVolume, 0 };
  475. Status = NtFsControlFile( _hFileNormal,
  476. NULL,
  477. NULL,
  478. NULL,
  479. &IoStatus,
  480. FSCTL_MARK_HANDLE,
  481. &hiCiIgnore,
  482. sizeof( hiCiIgnore),
  483. 0,
  484. 0 );
  485. Win4Assert( STATUS_PENDING != Status );
  486. if ( !NT_SUCCESS( Status ) && STATUS_INVALID_DEVICE_REQUEST != Status )
  487. {
  488. ciDebugOut(( DEB_ERROR,
  489. "CFilterOplock::MaybeSetLastAccessTime -- Error 0x%x marking handle\n",
  490. Status ));
  491. break;
  492. }
  493. //
  494. // We only want to update last access time.
  495. //
  496. _BasicInfo.CreationTime.QuadPart = -1;
  497. _BasicInfo.LastWriteTime.QuadPart = -1;
  498. _BasicInfo.ChangeTime.QuadPart = -1;
  499. _BasicInfo.FileAttributes = 0;
  500. Status = NtSetInformationFile( _hFileNormal, // File handle
  501. &IoStatus, // I/O Status
  502. &_BasicInfo, // Buffer
  503. sizeof _BasicInfo, // Buffer size
  504. FileBasicInformation );
  505. Win4Assert( STATUS_PENDING != Status );
  506. if ( !NT_SUCCESS( Status ) )
  507. {
  508. ciDebugOut(( DEB_ERROR,
  509. "CFilterOplock::MaybeSetLastAccessTime -- Error 0x%x resetting last-access time.\n",
  510. Status ));
  511. break;
  512. }
  513. } while( FALSE ); // Polish loop
  514. } // if access time sufficiently stale
  515. } // if oplock broken
  516. }