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.

1463 lines
37 KiB

  1. /**********************************************************************/
  2. /** Microsoft Windows NT **/
  3. /** Copyright(c) Microsoft Corp., 1998 **/
  4. /**********************************************************************/
  5. /*
  6. fileopen.cxx
  7. This module contains the functions for creating and closing
  8. cached file information.
  9. FILE HISTORY:
  10. MCourage 09-Dec-1997 Created
  11. */
  12. #include <tsunami.hxx>
  13. #include "filecach.hxx"
  14. #include "filehash.hxx"
  15. #include "tsunamip.hxx"
  16. #include "tlcach.h"
  17. #include <mbstring.h>
  18. #include <atq.h>
  19. #include <uspud.h>
  20. #include "dbgutil.h"
  21. extern CFileCacheStats * g_pFileCacheStats;
  22. extern BOOL DisableTsunamiCaching;
  23. //
  24. // globals
  25. //
  26. LARGE_INTEGER g_liFileCacheByteThreshold;
  27. DWORDLONG g_dwMemCacheSize;
  28. BOOL g_bEnableSequentialRead;
  29. //
  30. // Private functions
  31. //
  32. LPTS_OPEN_FILE_INFO TspOpenFile( LPCSTR pszName, HANDLE hUser, DWORD dwOptions );
  33. LPTS_OPEN_FILE_INFO TspOpenCachedFile( LPCSTR pszName, HANDLE hUser, DWORD dwOptions );
  34. BOOL TspOpenCachedFileHelper( LPCSTR pszName, HANDLE hUser, TS_OPEN_FILE_INFO * pOpenFile, DWORD dwOptions );
  35. DWORD TspMakeWidePath( LPCSTR pszName, DWORD cchWideName, BOOL bNoParse, LPWSTR pwszWideName );
  36. VOID TspOplockNotification( PVOID pvContext, DWORD Status );
  37. //
  38. // flags to set on CreateFile
  39. //
  40. DWORD TsCreateFileShareMode = (FILE_SHARE_READ |
  41. FILE_SHARE_WRITE |
  42. FILE_SHARE_DELETE);
  43. DWORD TsCreateFileFlags = (FILE_FLAG_OVERLAPPED |
  44. FILE_FLAG_BACKUP_SEMANTICS |
  45. FILE_DIRECTORY_FILE );
  46. const SECURITY_INFORMATION TsSecurityInfo
  47. = OWNER_SECURITY_INFORMATION
  48. | GROUP_SECURITY_INFORMATION
  49. | DACL_SECURITY_INFORMATION;
  50. //
  51. // macros
  52. //
  53. #define LockUriInfo() ( EnterCriticalSection( &g_csUriInfo ) )
  54. #define UnlockUriInfo() ( LeaveCriticalSection( &g_csUriInfo ) )
  55. LPTS_OPEN_FILE_INFO
  56. TsCreateFile(
  57. IN const TSVC_CACHE &TSvcCache,
  58. IN LPCSTR pszName,
  59. IN HANDLE OpeningUser,
  60. IN DWORD dwOptions
  61. )
  62. /*++
  63. Routine Description:
  64. Opens a TS_OPEN_FILE_INFO. When the requested file is no longer
  65. needed, the client should close it with TsCloseHandle.
  66. Arguments:
  67. TSvcCache - An initialized TSVC_CACHE structure. (Ignored)
  68. lpszName - The name of the file
  69. OpeningUser - HANDLE for the user attempting the open
  70. dwOptions - Valid options are:
  71. TS_CACHING_DESIRED
  72. TS_NO_ACCESS_CHECK
  73. TS_DONT_CACHE_ACCESS_TOKEN
  74. TS_NOT_IMPERSONATED
  75. TS_USE_WIN32_CANON
  76. TS_FORBID_SHORT_NAMES
  77. Return Values:
  78. On success TsCreateFile returns a pointer to a TS_OPEN_FILE_INFO
  79. for the requested file. NULL is returned on failure. More error
  80. information can be obtained from GetLastError();
  81. --*/
  82. {
  83. TS_OPEN_FILE_INFO * pOpenFile = NULL;
  84. CHAR achUpName[MAX_PATH+1];
  85. BOOL bCacheToken = !(dwOptions & TS_DONT_CACHE_ACCESS_TOKEN);
  86. if (DisableTsunamiCaching) {
  87. dwOptions &= ~TS_CACHING_DESIRED;
  88. }
  89. //
  90. // This assert doesn't work on workstation, but it would
  91. // be nice.
  92. //
  93. // DBG_ASSERT( (~TsValidCreateFileOptions & dwOptions) == 0 );
  94. //
  95. dwOptions &= TsValidCreateFileOptions;
  96. //
  97. // Make sure the path is upper case
  98. //
  99. IISstrncpy(achUpName, pszName, MAX_PATH);
  100. achUpName[MAX_PATH] = 0;
  101. _mbsupr(reinterpret_cast<PUCHAR>(achUpName));
  102. //
  103. // Disallow short file names as they break metabase equivalency
  104. // We do the expensive check anytime there's a tilde - number
  105. // sequence. If we find somebody used a short name we return file not found.
  106. // Note we revert if not on UNC since most systems have traverse checking
  107. // turned off.
  108. //
  109. if ( (dwOptions & TS_FORBID_SHORT_NAMES) &&
  110. strchr( pszName, '~' ))
  111. {
  112. DWORD err;
  113. BOOL fShort;
  114. //
  115. // CheckIfShortFileName() should be called unimpersonated
  116. //
  117. err = CheckIfShortFileName( (UCHAR *) pszName, OpeningUser, &fShort );
  118. if ( !err && fShort )
  119. {
  120. DBGPRINTF(( DBG_CONTEXT,
  121. "Short filename being rejected \"%s\"\n",
  122. pszName ));
  123. err = ERROR_FILE_NOT_FOUND;
  124. }
  125. if ( err )
  126. {
  127. SetLastError( err );
  128. return NULL;
  129. }
  130. }
  131. //
  132. // Try to get the file
  133. //
  134. if ( dwOptions & TS_CACHING_DESIRED ) {
  135. //
  136. // Look in the cache first
  137. //
  138. if ( CheckoutFile(achUpName, FCF_FOR_IO, &pOpenFile) ) {
  139. //
  140. // Make sure we can really use this handle
  141. //
  142. if ( ( !(dwOptions & TS_NO_ACCESS_CHECK) ) &&
  143. ( !pOpenFile->AccessCheck(OpeningUser, bCacheToken) ) ) {
  144. CheckinFile(pOpenFile, FCF_FOR_IO);
  145. //
  146. // Return Access Denied
  147. //
  148. pOpenFile = NULL;
  149. SetLastError(ERROR_ACCESS_DENIED);
  150. }
  151. } else if ( GetLastError() == ERROR_FILE_NOT_FOUND ) {
  152. //
  153. // The file was not in the cache. Try to put it in.
  154. //
  155. pOpenFile = TspOpenCachedFile(achUpName, OpeningUser, dwOptions);
  156. }
  157. } else {
  158. //
  159. // We're not using the cache. Just open the file.
  160. //
  161. pOpenFile = TspOpenFile(achUpName, OpeningUser, dwOptions);
  162. }
  163. return pOpenFile;
  164. }
  165. LPTS_OPEN_FILE_INFO
  166. TsCreateFileFromURI(
  167. IN const TSVC_CACHE &TSvcCache,
  168. IN PW3_URI_INFO pURIInfo,
  169. IN HANDLE OpeningUser,
  170. IN DWORD dwOptions,
  171. IN DWORD *dwError
  172. )
  173. /*++
  174. Routine Description:
  175. Opens the TS_OPEN_FILE_INFO associated with the W3_URI_INFO argument. When
  176. the client is done with the file it should be closed with TsCloseURIFile.
  177. As a side effect, this routine will set the pOpenFileInfo, and
  178. dwFileOpenError in the pURIInfo structure.
  179. Arguments:
  180. TSvcCache - An initialized TSVC_CACHE structure.
  181. pURIInfo - Pointer to the W3_URI_INFO whose associated file should be opened
  182. OpeningUser - HANDLE for the user attempting the open
  183. dwOptions - Valid options are:
  184. TS_CACHING_DESIRED
  185. TS_NO_ACCESS_CHECK
  186. TS_DONT_CACHE_ACCESS_TOKEN
  187. TS_NOT_IMPERSONATED
  188. TS_USE_WIN32_CANON
  189. TS_FORBID_SHORT_NAMES
  190. dwError - On failure this DWORD will contain an error code
  191. Return Values:
  192. On success a pointer to the TS_OPEN_FILE_INFO is returned. On failure
  193. TsCreateFileFromURI returns NULL, and dwError is set to an error code.
  194. --*/
  195. {
  196. TS_OPEN_FILE_INFO * pOpenFile = NULL;
  197. DWORD err;
  198. BOOL bCacheToken = !(dwOptions & TS_DONT_CACHE_ACCESS_TOKEN);
  199. BOOL bInfoValid;
  200. //
  201. // This assert doesn't work on workstation, but it would
  202. // be nice.
  203. //
  204. // DBG_ASSERT( (~TsValidCreateFileOptions & dwOptions) == 0 );
  205. //
  206. dwOptions &= TsValidCreateFileOptions;
  207. if (DisableTsunamiCaching) {
  208. pOpenFile = TsCreateFile(TSvcCache,
  209. pURIInfo->pszName,
  210. OpeningUser,
  211. dwOptions);
  212. *dwError = GetLastError();
  213. return pOpenFile;
  214. }
  215. //
  216. // It seems that callers expect caching, but don't request it
  217. //
  218. dwOptions |= TS_CACHING_DESIRED;
  219. //
  220. // Assume no error
  221. //
  222. err = ERROR_SUCCESS;
  223. //
  224. // Try to get the file
  225. //
  226. bInfoValid = FALSE;
  227. LockUriInfo();
  228. pOpenFile = pURIInfo->pOpenFileInfo;
  229. if ( pOpenFile && CheckoutFileEntry(pOpenFile, FCF_FOR_IO) ) {
  230. bInfoValid = TRUE;
  231. }
  232. UnlockUriInfo();
  233. if ( bInfoValid ) {
  234. //
  235. // We've got a cached handle; make sure we're allowed to see it
  236. //
  237. if ( !pOpenFile->AccessCheck(OpeningUser, bCacheToken) ) {
  238. CheckinFile(pOpenFile, FCF_FOR_IO);
  239. //
  240. // Return Access Denied
  241. //
  242. pOpenFile = NULL;
  243. err = ERROR_ACCESS_DENIED;
  244. }
  245. } else {
  246. //
  247. // We've got either an invalid handle or no cached handle
  248. // at all.
  249. //
  250. if (pOpenFile) {
  251. //
  252. // The cached file handle is invalid
  253. //
  254. TS_OPEN_FILE_INFO * pOldFile;
  255. LockUriInfo();
  256. pOldFile = pURIInfo->pOpenFileInfo;
  257. pURIInfo->pOpenFileInfo = NULL;
  258. UnlockUriInfo();
  259. //
  260. // We've got an invalid entry checked out.
  261. // It's been checked out twice, once for I/O (above)
  262. // and once when it was first cached.
  263. //
  264. CheckinFile(pOpenFile, FCF_FOR_IO);
  265. //
  266. // Now we handle the reference that was cached in the
  267. // URI info structure. If someone else already removed
  268. // the reference, then pOldFile will be NULL, or a new
  269. // value.
  270. //
  271. if (pOldFile == pOpenFile) {
  272. //
  273. // We've got the reference all to ourselves so
  274. // we can delete it.
  275. //
  276. CheckinFile(pOpenFile, 0);
  277. } else if (pOldFile) {
  278. //
  279. // Someone else took care of the reference,
  280. // and cached a new one. It would be nice
  281. // if we could use it, but the file might have
  282. // already been flushed. If we go back to the
  283. // top, there's a chance that we'll loop forever.
  284. // Instead, we'll just get rid of this new
  285. // reference and pretend it was never there.
  286. //
  287. CheckinFile(pOldFile, 0);
  288. }
  289. }
  290. //
  291. // No file is cached. Try to open it.
  292. //
  293. pOpenFile = TsCreateFile(TSvcCache,
  294. pURIInfo->pszName,
  295. OpeningUser,
  296. dwOptions);
  297. //
  298. // Save the result.
  299. //
  300. if ( pOpenFile ) {
  301. if (dwOptions & TS_CACHING_DESIRED) {
  302. //
  303. // To save a copy of the info we need to reference it
  304. //
  305. if ( CheckoutFileEntry(pOpenFile, 0) ) {
  306. TS_OPEN_FILE_INFO * pOldFile;
  307. LockUriInfo();
  308. pOldFile = pURIInfo->pOpenFileInfo;
  309. pURIInfo->pOpenFileInfo = pOpenFile;
  310. UnlockUriInfo();
  311. //
  312. // Someone else might have written a value
  313. // into the URI info before we got to it, but
  314. // we'll show them. Our value stays while
  315. // theirs gets put back.
  316. //
  317. if (pOldFile) {
  318. CheckinFile(pOldFile, 0);
  319. }
  320. } else {
  321. //
  322. // It's been flushed, so we don't want to save it.
  323. //
  324. CheckinFile(pOpenFile, 0);
  325. }
  326. }
  327. } else {
  328. //
  329. // We couldn't open a file, so just record the error.
  330. //
  331. err = GetLastError();
  332. }
  333. }
  334. if ( err != ERROR_SUCCESS ) {
  335. *dwError = err;
  336. SetLastError(err);
  337. }
  338. return pOpenFile;
  339. }
  340. BOOL
  341. TsCloseHandle(
  342. IN const TSVC_CACHE &TSvcCache,
  343. IN LPTS_OPEN_FILE_INFO pOpenFile
  344. )
  345. /*++
  346. Routine Description:
  347. Closes a TS_OPEN_FILE_INFO opened with TsCreateFile.
  348. Arguments:
  349. TSvcCache - An initialized TSVC_CACHE structure.
  350. pOpenFile - Pointer to the TS_OPEN_FILE_INFO to be closed
  351. Return Values:
  352. TRUE on success
  353. --*/
  354. {
  355. if ( pOpenFile->IsCached() ) {
  356. CheckinFile(pOpenFile, FCF_FOR_IO);
  357. } else {
  358. //
  359. // This is an uncached file. We can just get rid of it.
  360. //
  361. pOpenFile->CloseHandle();
  362. delete pOpenFile;
  363. }
  364. return TRUE;
  365. }
  366. BOOL
  367. TsCloseURIFile(
  368. IN LPTS_OPEN_FILE_INFO pOpenFile
  369. )
  370. /*++
  371. Routine Description:
  372. Closes a TS_OPEN_FILE_INFO opened with TsCreateFileFromURI.
  373. Arguments:
  374. pOpenFile - Pointer to the TS_OPEN_FILE_INFO to be closed
  375. Return Values:
  376. TRUE on success
  377. --*/
  378. {
  379. if ( pOpenFile ) {
  380. if ( pOpenFile->IsCached() ) {
  381. CheckinFile(pOpenFile, FCF_FOR_IO);
  382. } else {
  383. //
  384. // This is an uncached file. We can just get rid of it.
  385. //
  386. pOpenFile->CloseHandle();
  387. delete pOpenFile;
  388. }
  389. }
  390. return TRUE;
  391. }
  392. BOOL
  393. TsDerefURIFile(
  394. IN LPTS_OPEN_FILE_INFO pOpenFile
  395. )
  396. /*++
  397. Routine Description:
  398. When a W3_URI_INFO gets cleaned up, it needs to get rid
  399. of it's reference to the file info by calling TsDerefURIFile.
  400. Arguments:
  401. pOpenFile - Pointer to the TS_OPEN_FILE_INFO to be derefed.
  402. Return Values:
  403. TRUE on success
  404. --*/
  405. {
  406. CheckinFile(pOpenFile, 0);
  407. return TRUE;
  408. }
  409. LPTS_OPEN_FILE_INFO
  410. TspOpenFile(
  411. LPCSTR pszName,
  412. HANDLE hUser,
  413. DWORD dwOptions
  414. )
  415. /*++
  416. Routine Description:
  417. Opens a non-cached TS_OPEN_FILE_INFO for TsCreateFile.
  418. Arguments:
  419. pszName - The name of the file
  420. hUser - HANDLE for the user attempting the open
  421. dwOptions - Valid options are:
  422. TS_CACHING_DESIRED
  423. TS_NO_ACCESS_CHECK
  424. TS_DONT_CACHE_ACCESS_TOKEN (Ignored for non-cached case)
  425. TS_NOT_IMPERSONATED
  426. TS_USE_WIN32_CANON
  427. Return Values:
  428. On success returns a pointer to a TS_OPEN_FILE_INFO
  429. for the requested file. NULL is returned on failure.
  430. More error information can be gotten
  431. from GetLastError(), which will return one of:
  432. ERROR_INSUFFICIENT_BUFFER
  433. ERROR_INVALID_NAME
  434. ERROR_ACCESS_DENIED
  435. ERROR_FILE_NOT_FOUND
  436. ERROR_PATH_NOT_FOUND
  437. ERROR_NOT_ENOUGH_MEMORY
  438. --*/
  439. {
  440. TS_OPEN_FILE_INFO * pOpenFile;
  441. HANDLE hFile;
  442. WCHAR awchPath[MAX_PATH+8+1];
  443. DWORD cchPath;
  444. SECURITY_ATTRIBUTES sa;
  445. BOOL bImpersonated;
  446. BOOL bNoParse;
  447. DWORD dwError = NO_ERROR;
  448. PSECURITY_DESCRIPTOR pSecDesc;
  449. DWORD dwSecDescSize;
  450. DWORD dwSecDescSizeReq;
  451. BOOL fSecDescAllocated;
  452. bImpersonated = FALSE;
  453. sa.nLength = sizeof(sa);
  454. sa.lpSecurityDescriptor = NULL;
  455. sa.bInheritHandle = FALSE;
  456. //
  457. // Allocate a file info object
  458. //
  459. pOpenFile = new TS_OPEN_FILE_INFO();
  460. if (!pOpenFile || !pOpenFile->SetFileName(pszName)) {
  461. //
  462. // couldn't allocate an object
  463. //
  464. dwError = ERROR_NOT_ENOUGH_MEMORY;
  465. goto err;
  466. }
  467. //
  468. // Convert path to unicode
  469. //
  470. bNoParse = ! ( dwOptions & TS_USE_WIN32_CANON );
  471. cchPath = TspMakeWidePath(pszName, MAX_PATH+8, bNoParse, awchPath);
  472. if (!cchPath) {
  473. //
  474. // Path couldn't be converted
  475. //
  476. dwError = GetLastError();
  477. goto err;
  478. }
  479. // avoid the infamous ::$DATA bug
  480. if ( wcsstr( awchPath, L"::" ) )
  481. {
  482. dwError = ERROR_FILE_NOT_FOUND;
  483. goto err;
  484. }
  485. //
  486. // We may need to impersonate some other user to open the file
  487. //
  488. if ( (dwOptions & TS_NOT_IMPERSONATED) &&
  489. !(dwOptions & TS_NO_ACCESS_CHECK) )
  490. {
  491. if ( !::ImpersonateLoggedOnUser( hUser ) )
  492. {
  493. DBGPRINTF(( DBG_CONTEXT,
  494. "ImpersonateLoggedOnUser[%d] failed with %d\n",
  495. hUser, GetLastError()));
  496. //
  497. // Return access denied
  498. //
  499. dwError = ERROR_ACCESS_DENIED;
  500. goto err;
  501. }
  502. bImpersonated = TRUE;
  503. }
  504. DWORD dwFileAttributes;
  505. if ((awchPath[cchPath-1] == L'.' || awchPath[cchPath-1] == L'\\') &&
  506. ((DWORD(-1) != (dwFileAttributes = GetFileAttributesW(awchPath))) &&
  507. (!(dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)))) {
  508. SetLastError(ERROR_FILE_NOT_FOUND);
  509. hFile = INVALID_HANDLE_VALUE;
  510. } else {
  511. //
  512. // Open the file
  513. //
  514. hFile = CreateFileW(
  515. awchPath,
  516. GENERIC_READ,
  517. TsCreateFileShareMode,
  518. &sa,
  519. OPEN_EXISTING,
  520. TsCreateFileFlags,
  521. NULL);
  522. }
  523. if ( hFile == INVALID_HANDLE_VALUE ) {
  524. dwError = GetLastError();
  525. goto err;
  526. }
  527. //
  528. // If our buffer wasn't big enough we have to get a new one.
  529. //
  530. pSecDesc = pOpenFile->QuerySecDesc();
  531. dwSecDescSize = SECURITY_DESC_DEFAULT_SIZE;
  532. fSecDescAllocated = FALSE;
  533. while(1) {
  534. //
  535. // get the security descriptor
  536. //
  537. if (GetKernelObjectSecurity(hFile,
  538. OWNER_SECURITY_INFORMATION
  539. | GROUP_SECURITY_INFORMATION
  540. | DACL_SECURITY_INFORMATION,
  541. pSecDesc,
  542. dwSecDescSize,
  543. &dwSecDescSizeReq)) {
  544. break;
  545. }
  546. else {
  547. if (fSecDescAllocated)
  548. {
  549. FREE(pSecDesc);
  550. }
  551. pSecDesc = NULL;
  552. if (ERROR_INSUFFICIENT_BUFFER != GetLastError()) {
  553. //
  554. // We got some error other than not enough buffer.
  555. // Fail the whole request.
  556. //
  557. dwError = GetLastError();
  558. ::CloseHandle(hFile);
  559. hFile = INVALID_HANDLE_VALUE;
  560. goto err;
  561. }
  562. }
  563. dwSecDescSize = ((dwSecDescSizeReq + SECURITY_DESC_GRANULARITY - 1)
  564. / SECURITY_DESC_GRANULARITY) * SECURITY_DESC_GRANULARITY;
  565. pSecDesc = ALLOC(dwSecDescSize);
  566. if (!pSecDesc) {
  567. dwError = ERROR_NOT_ENOUGH_MEMORY;
  568. ::CloseHandle(hFile);
  569. hFile = INVALID_HANDLE_VALUE;
  570. goto err;
  571. }
  572. fSecDescAllocated = TRUE;
  573. }
  574. //
  575. // Store the file information
  576. //
  577. pOpenFile->SetFileInfo(
  578. hFile,
  579. hUser,
  580. pSecDesc,
  581. dwSecDescSize
  582. );
  583. if ( bImpersonated ) {
  584. ::RevertToSelf();
  585. }
  586. pOpenFile->TraceCheckpointEx(TS_MAGIC_CREATE_NC, 0, 0);
  587. return pOpenFile;
  588. err:
  589. if ( bImpersonated ) {
  590. ::RevertToSelf();
  591. }
  592. if (pOpenFile) {
  593. delete pOpenFile;
  594. }
  595. SetLastError(dwError);
  596. return NULL;
  597. }
  598. LPTS_OPEN_FILE_INFO
  599. TspOpenCachedFile(
  600. LPCSTR pszName,
  601. HANDLE hUser,
  602. DWORD dwOptions
  603. )
  604. /*++
  605. Routine Description:
  606. Helper function for TsCreateFile
  607. Opens a TS_OPEN_FILE_INFO and puts it in the file cache.
  608. We want to make sure that each file only gets opened once,
  609. so first we put an uninitialized file entry in the cache.
  610. If the open succeeds, we tell the cache that the file is
  611. ready for use. Otherwise we decache it.
  612. Arguments:
  613. pszName - The name of the file
  614. OpeningUser - HANDLE for the user attempting the open
  615. dwOptions - Valid options are:
  616. TS_CACHING_DESIRED
  617. TS_NO_ACCESS_CHECK
  618. TS_DONT_CACHE_ACCESS_TOKEN
  619. TS_NOT_IMPERSONATED
  620. TS_USE_WIN32_CANON
  621. Return Values:
  622. On success returns a pointer to a TS_OPEN_FILE_INFO
  623. for the requested file. NULL is returned on failure.
  624. More error information can be gotten
  625. from GetLastError(), which will return one of:
  626. ERROR_INSUFFICIENT_BUFFER
  627. ERROR_INVALID_NAME
  628. ERROR_ACCESS_DENIED
  629. ERROR_FILE_NOT_FOUND
  630. ERROR_PATH_NOT_FOUND
  631. ERROR_NOT_ENOUGH_MEMORY
  632. --*/
  633. {
  634. TS_OPEN_FILE_INFO *pOpenFile;
  635. DWORD dwResult;
  636. DWORD dwError = NO_ERROR;
  637. DBG_ASSERT( !DisableTsunamiCaching );
  638. //
  639. // Allocate a file info object
  640. //
  641. pOpenFile = new TS_OPEN_FILE_INFO();
  642. if (!pOpenFile || !pOpenFile->SetFileName(pszName)) {
  643. //
  644. // couldn't allocate an object
  645. //
  646. dwError = ERROR_NOT_ENOUGH_MEMORY;
  647. goto err;
  648. }
  649. Retry:
  650. //
  651. // Add a cache entry to prevent others from opening the file simultaneously
  652. //
  653. dwResult = CacheFile(pOpenFile, FCF_UNINITIALIZED | FCF_FOR_IO);
  654. if ( dwResult == TS_ERROR_OUT_OF_MEMORY ) {
  655. dwError = ERROR_NOT_ENOUGH_MEMORY;
  656. goto err;
  657. }
  658. if (dwResult == TS_ERROR_SUCCESS) {
  659. //
  660. // Open the file
  661. //
  662. if ( TspOpenCachedFileHelper(pszName, hUser, pOpenFile, dwOptions) ) {
  663. //
  664. // Tell the cache it worked
  665. //
  666. NotifyInitializedFile(pOpenFile);
  667. } else {
  668. //
  669. // need to remove the entry in the cache
  670. // since it's not valid.
  671. //
  672. // First we need an extra reference which we'll use to tell
  673. // the cache that the file is initialized. Then we'll
  674. // decache the file, and finally mark it initialized.
  675. //
  676. // The file can't be marked initialized before the decache,
  677. // because then it might look like a successful open.
  678. // We need the extra reference because calling decache
  679. // gets rid of our original reference.
  680. //
  681. pOpenFile->TraceCheckpointEx(TS_MAGIC_OPLOCK_FAIL, 0, 0);
  682. CheckoutFileEntry(pOpenFile, 0);
  683. DecacheFile(pOpenFile, FCF_FOR_IO);
  684. NotifyInitializedFile(pOpenFile);
  685. CheckinFile(pOpenFile, 0);
  686. pOpenFile = NULL;
  687. dwError = GetLastError();
  688. goto err;
  689. }
  690. } else {
  691. TS_OPEN_FILE_INFO * pExisting = NULL;
  692. DBG_ASSERT ( dwResult == TS_ERROR_ALREADY_CACHED );
  693. //
  694. // Someone beat us to it. Try to check it out.
  695. //
  696. if ( !CheckoutFile(pszName, FCF_FOR_IO, &pExisting) ) {
  697. //
  698. // It's been removed from the cache since our
  699. // attempt to add it. Try again
  700. //
  701. goto Retry;
  702. }
  703. delete pOpenFile;
  704. pOpenFile = pExisting;
  705. }
  706. return pOpenFile;
  707. err:
  708. if (pOpenFile) {
  709. delete pOpenFile;
  710. }
  711. SetLastError(dwError);
  712. return NULL;
  713. }
  714. BOOL
  715. TspOpenCachedFileHelper(
  716. LPCSTR pszName,
  717. HANDLE hUser,
  718. TS_OPEN_FILE_INFO * pOpenFile,
  719. DWORD dwOptions
  720. )
  721. /*++
  722. Routine Description:
  723. This function actually attempts to open the file with AtqCreateFileW.
  724. When we enable oplocks, ATQ will need a reference to the object.
  725. Therefore AtqCreateFileW must somehow be modified to tell me whether
  726. it has a reference or not. If ATQ has a reference, we tell the cache
  727. so here.
  728. Arguments:
  729. pszName - The name of the file
  730. OpeningUser - HANDLE for the user attempting the open
  731. pOpenFile - the file info object to update on success
  732. dwOptions - Valid options are:
  733. TS_CACHING_DESIRED (should always be set here)
  734. TS_NO_ACCESS_CHECK
  735. TS_DONT_CACHE_ACCESS_TOKEN
  736. TS_NOT_IMPERSONATED
  737. TS_USE_WIN32_CANON
  738. Return Values:
  739. TRUE if the file was opened, and file information was set.
  740. FALSE otherwise. More error information can be gotten
  741. from GetLastError(), which will return one of:
  742. ERROR_INSUFFICIENT_BUFFER
  743. ERROR_INVALID_NAME
  744. ERROR_ACCESS_DENIED
  745. ERROR_FILE_NOT_FOUND
  746. ERROR_PATH_NOT_FOUND
  747. --*/
  748. {
  749. HANDLE hFile;
  750. WCHAR awchPath[MAX_PATH+8+1];
  751. DWORD cchPath;
  752. DWORD dwError = NO_ERROR;
  753. BOOL bSuccess;
  754. BOOL bDirectory;
  755. BOOL bSmallFile;
  756. BOOL bImpersonated = FALSE;
  757. BOOL bNoParse;
  758. SECURITY_ATTRIBUTES sa;
  759. BOOL bCacheSecDesc;
  760. PSECURITY_DESCRIPTOR pSecDesc;
  761. DWORD dwSecDescSize;
  762. DWORD dwSecDescSizeReq;
  763. SPUD_FILE_INFORMATION FileInfo;
  764. DWORD cbFileSize = 0;
  765. DWORD cbBytesRequired;
  766. PBYTE pBuffer = NULL;
  767. HANDLE hCopyFile = INVALID_HANDLE_VALUE;
  768. DBG_ASSERT(dwOptions & TS_CACHING_DESIRED);
  769. bSuccess = FALSE;
  770. dwSecDescSizeReq = 0;
  771. //
  772. // Assume it's not cacheable
  773. //
  774. bSmallFile = FALSE;
  775. bDirectory = FALSE;
  776. //
  777. // set up security info
  778. //
  779. sa.nLength = sizeof(sa);
  780. sa.lpSecurityDescriptor = NULL;
  781. sa.bInheritHandle = FALSE;
  782. //
  783. // Allocate space for security descriptor
  784. //
  785. bCacheSecDesc = TRUE;
  786. //
  787. // The open file info contains a buffer
  788. // of the default size.
  789. //
  790. pSecDesc = pOpenFile->QuerySecDesc();
  791. dwSecDescSize = SECURITY_DESC_DEFAULT_SIZE;
  792. //
  793. // Convert path to unicode
  794. //
  795. bNoParse = ! ( dwOptions & TS_USE_WIN32_CANON );
  796. cchPath = TspMakeWidePath(pszName, MAX_PATH+8, bNoParse, awchPath);
  797. if (!cchPath) {
  798. //
  799. // Path couldn't be converted
  800. //
  801. pSecDesc = NULL; // Don't free the default buffer.
  802. dwError = GetLastError();
  803. goto err;
  804. }
  805. // avoid the infamous ::$DATA bug
  806. if ( wcsstr( awchPath, L"::" ) )
  807. {
  808. pSecDesc = NULL;
  809. dwError = ERROR_FILE_NOT_FOUND;
  810. goto err;
  811. }
  812. //
  813. // We may need to impersonate some other user to open the file
  814. //
  815. if ( (dwOptions & TS_NOT_IMPERSONATED) &&
  816. !(dwOptions & TS_NO_ACCESS_CHECK) )
  817. {
  818. if ( !::ImpersonateLoggedOnUser( hUser ) )
  819. {
  820. DBGPRINTF(( DBG_CONTEXT,
  821. "ImpersonateLoggedOnUser[%d] failed with %d\n",
  822. hUser, GetLastError()));
  823. //
  824. // Return access denied
  825. //
  826. pSecDesc = NULL;
  827. dwError = ERROR_ACCESS_DENIED;
  828. goto err;
  829. }
  830. bImpersonated = TRUE;
  831. }
  832. //
  833. // Open the file
  834. //
  835. hFile = AtqCreateFileW(
  836. awchPath, // lpFileName
  837. TsCreateFileShareMode, // dwShareMode
  838. &sa, // lpSecurityAttributes
  839. TsCreateFileFlags, // dwFlagsAndAttributes
  840. TsSecurityInfo, // si
  841. pSecDesc, // sd
  842. dwSecDescSize, // Length
  843. &dwSecDescSizeReq, // LengthNeeded
  844. &FileInfo); // pFileInfo
  845. if ( hFile == INVALID_HANDLE_VALUE ) {
  846. bSuccess = FALSE;
  847. pSecDesc = NULL; // Don't free the default buffer.
  848. dwError = GetLastError();
  849. goto err;
  850. }
  851. // if the name ends with ('\' or '.')
  852. // and it is NOT a directory, than fail 404
  853. if ((awchPath[cchPath-1] == L'.' || awchPath[cchPath-1] == L'\\') && // suspect file name
  854. !FileInfo.StandardInformation.Directory) {
  855. pSecDesc = NULL;
  856. dwError = ERROR_FILE_NOT_FOUND;
  857. ::CloseHandle(hFile);
  858. hFile = INVALID_HANDLE_VALUE;
  859. goto err;
  860. }
  861. //
  862. // If our buffer wasn't big enough we have to get a new one.
  863. //
  864. if (dwSecDescSizeReq > dwSecDescSize) {
  865. //
  866. // Code below shouldn't try to free our original buffer
  867. //
  868. pSecDesc = NULL;
  869. //
  870. // Keep getting the security descriptor until we have enough space.
  871. //
  872. while (dwSecDescSizeReq > dwSecDescSize) {
  873. DBGPRINTF(( DBG_CONTEXT,
  874. "Allocating a new Security Descriptor buffer! %d > %d\n",
  875. dwSecDescSizeReq,
  876. dwSecDescSize ));
  877. dwSecDescSize = ((dwSecDescSizeReq + SECURITY_DESC_GRANULARITY - 1)
  878. / SECURITY_DESC_GRANULARITY) * SECURITY_DESC_GRANULARITY;
  879. if (pSecDesc) {
  880. FREE(pSecDesc);
  881. }
  882. pSecDesc = ALLOC(dwSecDescSize);
  883. if (!pSecDesc) {
  884. dwError = ERROR_NOT_ENOUGH_MEMORY;
  885. goto err;
  886. }
  887. //
  888. // get a new security descriptor
  889. //
  890. if (!GetKernelObjectSecurity(hFile,
  891. OWNER_SECURITY_INFORMATION
  892. | GROUP_SECURITY_INFORMATION
  893. | DACL_SECURITY_INFORMATION,
  894. pSecDesc,
  895. dwSecDescSize,
  896. &dwSecDescSizeReq)
  897. && (ERROR_INSUFFICIENT_BUFFER != GetLastError()) ) {
  898. //
  899. // We got some error other than not enough buffer.
  900. // Fail the whole request.
  901. //
  902. dwError = GetLastError();
  903. ::CloseHandle(hFile);
  904. hFile = INVALID_HANDLE_VALUE;
  905. goto err;
  906. }
  907. }
  908. }
  909. //
  910. // Don't need to be impersonated anymore
  911. //
  912. if ( bImpersonated ) {
  913. ::RevertToSelf();
  914. bImpersonated = FALSE;
  915. }
  916. bDirectory = FileInfo.StandardInformation.Directory;
  917. //
  918. // We don't need to hold the handle open for directories.
  919. //
  920. if (bDirectory) {
  921. ::CloseHandle(hFile);
  922. hFile = INVALID_HANDLE_VALUE;
  923. }
  924. //
  925. // If we're using a non cached token we don't want to save it
  926. //
  927. if (dwOptions & TS_DONT_CACHE_ACCESS_TOKEN) {
  928. hUser = INVALID_HANDLE_VALUE;
  929. }
  930. //
  931. // try to read the file into the memory cache
  932. //
  933. if ((FileInfo.StandardInformation.EndOfFile.QuadPart < g_liFileCacheByteThreshold.QuadPart)
  934. && (hFile != INVALID_HANDLE_VALUE)) {
  935. bSmallFile = TRUE;
  936. cbFileSize = FileInfo.StandardInformation.EndOfFile.LowPart;
  937. } else {
  938. bSmallFile = FALSE;
  939. }
  940. if (bSmallFile) {
  941. dwError = ReadFileIntoMemoryCache(
  942. hFile,
  943. cbFileSize,
  944. &cbBytesRequired,
  945. (PVOID *) &pBuffer
  946. );
  947. if (dwError == ERROR_SUCCESS) {
  948. //
  949. // it worked. dump the file handle
  950. //
  951. ::CloseHandle(hFile);
  952. hFile = INVALID_HANDLE_VALUE;
  953. } else {
  954. //
  955. // we failed to make it into
  956. // the cache, so don't cache
  957. // this entry
  958. //
  959. bSmallFile = FALSE;
  960. }
  961. }
  962. //
  963. // Add file information to pOpenFile
  964. //
  965. pOpenFile->SetFileInfo(
  966. pBuffer, // buffer contains contents of file
  967. hCopyFile, // handle to a copy of the file
  968. hFile, // handle to the file
  969. hUser, // user token, if we're supposed to cache it
  970. pSecDesc, // security descriptor
  971. dwSecDescSize, // size of security descriptor
  972. &FileInfo // file information struct
  973. );
  974. bSuccess = TRUE;
  975. //
  976. // fall through to do cleanup
  977. //
  978. err:
  979. if (!bDirectory && !bSmallFile) {
  980. //
  981. // This is a regular file that is too large
  982. // for the cache, so don't cache it.
  983. //
  984. IF_DEBUG( OPLOCKS ) {
  985. DBGPRINTF(( DBG_CONTEXT,
  986. "Couldn't cache %8p \"%s\"\n",
  987. pOpenFile, pOpenFile->GetKey()->m_pszFileName ));
  988. }
  989. CheckoutFileEntry(pOpenFile, 0);
  990. DecacheFile(pOpenFile, 0);
  991. }
  992. if ( bImpersonated ) {
  993. ::RevertToSelf();
  994. }
  995. if ( !bSuccess ) {
  996. if ( pSecDesc ) {
  997. FREE(pSecDesc);
  998. }
  999. SetLastError(dwError);
  1000. }
  1001. return bSuccess;
  1002. }
  1003. DWORD
  1004. TspMakeWidePath(
  1005. IN LPCSTR pszName,
  1006. IN DWORD cchWideName,
  1007. IN BOOL bNoParse,
  1008. OUT LPWSTR pwszWideName
  1009. )
  1010. /*++
  1011. Routine Description:
  1012. This routine converts a path name from the local code page and
  1013. converts it to wide characters. In addition it adds a prefix
  1014. to the string, which is "\\?\UNC\" for a UNC path, and "\\?\" for
  1015. other paths. This prefix tells Windows not to parse the path.
  1016. Arguments:
  1017. IN pszName - The path to be converted
  1018. IN cchWideName - The size of the pwszWideName buffer
  1019. OUT pwszWideName - The converted path
  1020. Return Values:
  1021. On success we return the number of wide characters in the
  1022. converted path + the prefix.
  1023. On failure we return zero. More error information will be
  1024. returned by GetLastError(). The error code will be one of:
  1025. ERROR_INSUFFICIENT_BUFFER
  1026. ERROR_INVALID_NAME
  1027. --*/
  1028. {
  1029. DWORD cbPrefix=0;
  1030. LPCSTR pszPath;
  1031. DWORD cch=0;
  1032. if ( bNoParse ) {
  1033. if ( (pszName[0] == '\\') && (pszName[1] == '\\') )
  1034. {
  1035. CopyMemory(
  1036. pwszWideName,
  1037. L"\\\\?\\UNC\\",
  1038. (sizeof("\\\\?\\UNC\\")-1) * sizeof(WCHAR)
  1039. );
  1040. cbPrefix = sizeof("\\\\?\\UNC\\")-1;
  1041. pszPath = pszName + sizeof( "\\\\" ) -1;
  1042. }
  1043. else
  1044. {
  1045. CopyMemory(
  1046. pwszWideName,
  1047. L"\\\\?\\",
  1048. (sizeof("\\\\?\\")-1) * sizeof(WCHAR)
  1049. );
  1050. cbPrefix = sizeof("\\\\?\\")-1;
  1051. pszPath = pszName;
  1052. }
  1053. } else {
  1054. //
  1055. // We're not adding a prefix
  1056. //
  1057. cbPrefix = 0;
  1058. pszPath = pszName;
  1059. }
  1060. cch = MultiByteToWideChar( CP_ACP,
  1061. MB_PRECOMPOSED | MB_ERR_INVALID_CHARS,
  1062. pszPath,
  1063. -1,
  1064. pwszWideName + cbPrefix,
  1065. cchWideName - cbPrefix );
  1066. if ( !cch && (GetLastError() != ERROR_INSUFFICIENT_BUFFER) ) {
  1067. SetLastError(ERROR_INVALID_NAME);
  1068. return cch; // that is, return 0
  1069. }
  1070. return cch+cbPrefix-1; // return the string length without the ZERO
  1071. }
  1072. VOID
  1073. TspOplockNotification(
  1074. PVOID pvContext,
  1075. DWORD Status
  1076. )
  1077. /*++
  1078. Routine Description:
  1079. When an oplock break occurs, this function gets called. If the
  1080. break occured because we closed the file, we just check it in
  1081. to the cache. Otherwise we flush the file so it will be closed
  1082. as soon as possible.
  1083. Arguments:
  1084. pvContext - Pointer to the TS_OPEN_FILE_INFO
  1085. Status - Tells why the break occured.
  1086. Return Values:
  1087. None.
  1088. --*/
  1089. {
  1090. TS_OPEN_FILE_INFO * pOpenFile = static_cast<TS_OPEN_FILE_INFO*>(pvContext);
  1091. CHECK_FILE_STATE( pOpenFile );
  1092. DBG_ASSERT( (Status == SPUD_OPLOCK_BREAK_OPEN)
  1093. || (Status == SPUD_OPLOCK_BREAK_CLOSE) );
  1094. //
  1095. // Someone else opened the file, or we decided to close it.
  1096. // Flush it from the cache.
  1097. //
  1098. IF_DEBUG( OPLOCKS ) {
  1099. DBGPRINTF(( DBG_CONTEXT,
  1100. "Oplock break %d on %p \"%s\"\n",
  1101. Status, pOpenFile, pOpenFile->GetKey()->m_pszFileName ));
  1102. }
  1103. if (Status == SPUD_OPLOCK_BREAK_OPEN) {
  1104. //
  1105. // Someone tried to open the file.
  1106. //
  1107. pOpenFile->TraceCheckpointEx(TS_MAGIC_OPLOCK_2, (PVOID) (ULONG_PTR) pOpenFile->IsFlushed(), 0);
  1108. g_pFileCacheStats->IncOplockBreaks();
  1109. } else {
  1110. //
  1111. // We closed the file, or someone opened it "aggresively"
  1112. // (eg CREATE_ALWAYS). Unfortunately there is no way to tell
  1113. // the difference between these cases.
  1114. //
  1115. pOpenFile->TraceCheckpointEx(TS_MAGIC_OPLOCK_0, (PVOID) (ULONG_PTR) pOpenFile->IsFlushed(), 0);
  1116. g_pFileCacheStats->IncOplockBreaksToNone();
  1117. }
  1118. DecacheFile(pOpenFile, 0);
  1119. }
  1120. HANDLE
  1121. TS_OPEN_FILE_INFO::QueryFileHandle(
  1122. VOID
  1123. )
  1124. /*++
  1125. Routine Description:
  1126. Returns a handle to the file, which we may have to open.
  1127. Arguments:
  1128. None.
  1129. Return Values:
  1130. INVALID_HANDLE_VALUE on failure
  1131. --*/
  1132. {
  1133. HANDLE hFile = INVALID_HANDLE_VALUE;
  1134. HANDLE hOldFile;
  1135. Lock();
  1136. if (m_hFile == INVALID_HANDLE_VALUE) {
  1137. WCHAR awchPath[MAX_PATH+8+1];
  1138. DWORD cchPath;
  1139. //
  1140. // Convert path to unicode
  1141. //
  1142. cchPath = TspMakeWidePath(m_FileKey.m_pszFileName, MAX_PATH+8, FALSE, awchPath);
  1143. if (cchPath) {
  1144. DWORD dwFileAttributes;
  1145. // avoid the infamous ::$DATA bug
  1146. if ( wcsstr( awchPath, L"::" ) )
  1147. {
  1148. SetLastError(ERROR_FILE_NOT_FOUND);
  1149. } else if ((awchPath[cchPath-1] == L'.' || awchPath[cchPath-1] == L'\\') &&
  1150. ((DWORD(-1) != (dwFileAttributes = GetFileAttributesW(awchPath))) &&
  1151. (!(dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)))) {
  1152. SetLastError(ERROR_FILE_NOT_FOUND);
  1153. } else {
  1154. SECURITY_ATTRIBUTES sa;
  1155. sa.nLength = sizeof(sa);
  1156. sa.lpSecurityDescriptor = NULL;
  1157. sa.bInheritHandle = FALSE;
  1158. //
  1159. // Open the file
  1160. //
  1161. hFile = CreateFileW(
  1162. awchPath,
  1163. GENERIC_READ,
  1164. TsCreateFileShareMode,
  1165. &sa,
  1166. OPEN_EXISTING,
  1167. TsCreateFileFlags,
  1168. NULL);
  1169. }
  1170. }
  1171. hOldFile = InterlockedExchangePointer(&m_hFile, hFile);
  1172. if (hOldFile != INVALID_HANDLE_VALUE) {
  1173. ::CloseHandle(hOldFile);
  1174. }
  1175. Unlock();
  1176. //
  1177. // Decache this file
  1178. //
  1179. DecacheFile(this, FCF_NO_DEREF);
  1180. }
  1181. else
  1182. {
  1183. Unlock();
  1184. }
  1185. return m_hFile;
  1186. }
  1187. //
  1188. // fileopen.cxx
  1189. //