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.

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