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.

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