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.

2238 lines
58 KiB

  1. /**********************************************************************/
  2. /** Microsoft Windows NT **/
  3. /** Copyright(c) Microsoft Corp., 1994 **/
  4. /**********************************************************************/
  5. /*
  6. doput.cxx
  7. This module contains the code for the PUT verb
  8. FILE HISTORY:
  9. Henrysa 08-May-1996 Created from doget.cxx
  10. */
  11. #include "w3p.hxx"
  12. #include <stdlib.h>
  13. //
  14. // Private constants.
  15. //
  16. //
  17. // Private globals.
  18. //
  19. CHAR cPutDeleteBody[] = "<body><h1>%s was %s successfully.</h1></body>";
  20. DWORD dwPutNumCPU;
  21. DWORD dwPutBlockedCount;
  22. //
  23. // Private prototypes.
  24. //
  25. BOOL
  26. W95MoveFileEx(
  27. IN LPCSTR lpExistingFile,
  28. IN LPCSTR lpNewFile
  29. );
  30. class FILENAME_LOCK;
  31. typedef class FILENAME_LOCK *PFILENAME_LOCK;
  32. typedef struct _FN_LOCK_TABLE_ENTRY
  33. {
  34. LIST_ENTRY ListHead;
  35. CRITICAL_SECTION csCritSec;
  36. } FN_LOCK_TABLE_ENTRY, *PFN_LOCK_TABLE_ENTRY;
  37. class FILENAME_LOCK
  38. {
  39. public:
  40. FILENAME_LOCK(STR *pstrFileName,
  41. PFN_LOCK_TABLE_ENTRY pTableEntry) :
  42. hEvent(NULL),
  43. dwWaitCount(0)
  44. {
  45. if (!strFileName.Copy(*pstrFileName))
  46. {
  47. return;
  48. }
  49. hEvent = IIS_CREATE_EVENT(
  50. "FILENAME_LOCK::hEvent",
  51. this,
  52. FALSE,
  53. FALSE
  54. );
  55. if (hEvent == NULL)
  56. {
  57. return;
  58. }
  59. // It's important that we not insert this into the table until
  60. // we know if the event was created OK, because the validity
  61. // check only checks for the event handle, and this object
  62. // will be freed without being pulled from the table if
  63. // the validity check fails.
  64. pLockTableEntry = pTableEntry;
  65. InsertHeadList(&pTableEntry->ListHead, &ListEntry);
  66. }
  67. ~FILENAME_LOCK(VOID)
  68. {
  69. DBG_ASSERT(dwWaitCount == 0);
  70. if (hEvent != NULL)
  71. {
  72. CloseHandle(hEvent);
  73. }
  74. }
  75. BOOL IsValid(VOID)
  76. { return hEvent != NULL; }
  77. DWORD Acquire(VOID);
  78. VOID Release(VOID);
  79. PLIST_ENTRY Next(VOID)
  80. {
  81. return ListEntry.Flink;
  82. }
  83. BOOL IsEqual(STR *Name)
  84. {
  85. if (Name->QueryCB() == strFileName.QueryCB() &&
  86. _strnicmp(Name->QueryStr(), strFileName.QueryStr(), Name->QueryCB() ) == 0)
  87. {
  88. return TRUE;
  89. }
  90. return FALSE;
  91. }
  92. LIST_ENTRY ListEntry;
  93. private:
  94. HANDLE hEvent;
  95. STR strFileName;
  96. PFN_LOCK_TABLE_ENTRY pLockTableEntry;
  97. DWORD dwWaitCount;
  98. };
  99. #define MAX_FN_LOCK_BUCKETS 67
  100. FN_LOCK_TABLE_ENTRY FNLockTable[MAX_FN_LOCK_BUCKETS];
  101. //
  102. // Public functions.
  103. //
  104. BOOL
  105. HandledByApp(
  106. CHAR *pszURL,
  107. PW3_METADATA pMD,
  108. enum HTTP_VERB Verb,
  109. CHAR *pszVerb
  110. )
  111. /*++
  112. Routine Description:
  113. Determine if a particular URL/Verb combo might be handled by a serer side
  114. app, such as an ISAPI or CGI app.
  115. Arguments:
  116. pszURL - URL to be checked.
  117. pMD - Metadata for the URL
  118. Verb - Verb to be checked.
  119. pszVerb - String form of Verb.
  120. Returns:
  121. TRUE if we think it might be handled by an app, FALSE otherwise.
  122. --*/
  123. {
  124. CHAR *pchExt;
  125. STR strDummy;
  126. GATEWAY_TYPE Type;
  127. DWORD cchExt;
  128. BOOL fImageInURL;
  129. BOOL fVerbExcluded;
  130. DWORD dwScriptMapFlags;
  131. PVOID pExtMapInfo;
  132. pchExt = pszURL;
  133. while (*pchExt)
  134. {
  135. pchExt = strchr( pchExt + 1, '.' );
  136. pExtMapInfo = NULL;
  137. if ( !pMD->LookupExtMap( pchExt,
  138. FALSE,
  139. &strDummy,
  140. &Type,
  141. &cchExt,
  142. &fImageInURL,
  143. &fVerbExcluded,
  144. &dwScriptMapFlags,
  145. pszVerb,
  146. Verb,
  147. &pExtMapInfo
  148. ))
  149. {
  150. return FALSE;
  151. }
  152. if ( pchExt == NULL ||
  153. (Type != GATEWAY_UNKNOWN && !fVerbExcluded))
  154. {
  155. break;
  156. }
  157. }
  158. return !(Type == GATEWAY_UNKNOWN || Type == GATEWAY_NONE);
  159. }
  160. DWORD
  161. HTTP_REQUEST::BuildAllowHeader(
  162. CHAR *pszURL,
  163. CHAR *pszTail
  164. )
  165. /*++
  166. Routine Description:
  167. Build an Allow: header appropriate to the URL.
  168. Arguments:
  169. pszURL - URL to be checked.
  170. pszTail - Place to build the allow header.
  171. Returns:
  172. Number of bytes appended to incoming pszTail.
  173. --*/
  174. {
  175. CHAR *pszOld = pszTail;
  176. BOOL bExecAllowed;
  177. if (_pMetaData == NULL)
  178. {
  179. return 0;
  180. }
  181. APPEND_STRING(pszTail, "Allow: OPTIONS, TRACE");
  182. bExecAllowed = (IS_ACCESS_ALLOWED(EXECUTE) || _fAnyParams ||
  183. _pMetaData->QueryAnyExtAllowedOnReadDir() );
  184. if ( (bExecAllowed && HandledByApp(_strURL.QueryStr(),
  185. _pMetaData,
  186. HTV_GET,
  187. "GET") )
  188. || IS_ACCESS_ALLOWED(READ)
  189. )
  190. {
  191. APPEND_STRING(pszTail, ", GET");
  192. }
  193. if ( (bExecAllowed && HandledByApp(_strURL.QueryStr(),
  194. _pMetaData,
  195. HTV_HEAD,
  196. "HEAD") )
  197. || IS_ACCESS_ALLOWED(READ)
  198. )
  199. {
  200. APPEND_STRING(pszTail, ", HEAD");
  201. }
  202. if ( (bExecAllowed && HandledByApp(_strURL.QueryStr(),
  203. _pMetaData,
  204. HTV_PUT,
  205. "PUT") )
  206. || IS_ACCESS_ALLOWED(WRITE)
  207. )
  208. {
  209. APPEND_STRING(pszTail, ", PUT");
  210. }
  211. if ( (bExecAllowed && HandledByApp(_strURL.QueryStr(),
  212. _pMetaData,
  213. HTV_DELETE,
  214. "DELETE") )
  215. || IS_ACCESS_ALLOWED(WRITE)
  216. )
  217. {
  218. APPEND_STRING(pszTail, ", DELETE");
  219. }
  220. if ( bExecAllowed && HandledByApp(_strURL.QueryStr(),
  221. _pMetaData,
  222. HTV_POST,
  223. "POST")
  224. )
  225. {
  226. APPEND_STRING(pszTail, ", POST");
  227. }
  228. APPEND_STRING(pszTail, "\r\n");
  229. return DIFF(pszTail - pszOld);
  230. }
  231. VOID
  232. FILENAME_LOCK::Release(
  233. VOID
  234. )
  235. /*++
  236. Routine Description:
  237. Release a filename lock. If there's noone waiting on the lock, then we're
  238. done with it, and we can go ahead and delete it. Otherwise signal the
  239. waiter so he can continue.
  240. Arguments:
  241. None
  242. Return Value:
  243. None
  244. --*/
  245. {
  246. EnterCriticalSection(&pLockTableEntry->csCritSec);
  247. if (dwWaitCount == 0)
  248. {
  249. // Nobody's waiting. Pull this guy from the list and delete him.
  250. RemoveEntryList(&ListEntry);
  251. LeaveCriticalSection(&pLockTableEntry->csCritSec);
  252. delete this;
  253. }
  254. else
  255. {
  256. DWORD dwRet;
  257. // Someone's waiting, so set the event to let then go.
  258. LeaveCriticalSection(&pLockTableEntry->csCritSec);
  259. dwRet = SetEvent(hEvent);
  260. DBG_ASSERT(dwRet != 0);
  261. }
  262. }
  263. DWORD
  264. FILENAME_LOCK::Acquire(
  265. VOID
  266. )
  267. /*++
  268. Routine Description:
  269. Acquire a filename lock. This routine is called if a filename lock is
  270. already held by another thread. Increment the wait count, and then
  271. block on an event. The holder of the lock will signal the event when
  272. they're done.
  273. This routine is called with the lock table bucket critical section
  274. held, and will free it before returning.
  275. Arguments:
  276. None
  277. Return Value:
  278. None
  279. --*/
  280. {
  281. DWORD dwRet;
  282. DWORD dwMaxThreads;
  283. DWORD dwAlreadyBlocked;
  284. // First, make sure we can block. We can block if no more than
  285. // 3/4ths of the total number of available threads are already
  286. // blocked in us. Note that we don't do this at all under Win95.
  287. if (g_fIsWindows95)
  288. {
  289. LeaveCriticalSection(&pLockTableEntry->csCritSec);
  290. return WAIT_TIMEOUT;
  291. }
  292. dwMaxThreads = (DWORD)AtqGetInfo(AtqMaxPoolThreads) * dwPutNumCPU;
  293. dwAlreadyBlocked = InterlockedIncrement((LPLONG)&dwPutBlockedCount) - 1;
  294. if (dwAlreadyBlocked < ((dwMaxThreads * 3) / 4))
  295. {
  296. // It's OK to block this guy.
  297. // Increment the wait count so the holder knows someone is
  298. // waiting.
  299. dwWaitCount++;
  300. LeaveCriticalSection(&pLockTableEntry->csCritSec);
  301. // Block on the event until we're signalled or time out.
  302. dwRet = WaitForSingleObject(hEvent, g_dwPutEventTimeout * 1000);
  303. // We're done waiting. Enter the critical section and decrement
  304. // the wait count. We can't do this with interlocked instructions
  305. // because because other parts of this package use the critical
  306. // section to serialize with the wait count and other actions
  307. // atomically.
  308. EnterCriticalSection(&pLockTableEntry->csCritSec);
  309. dwWaitCount--;
  310. }
  311. else
  312. {
  313. dwRet = WAIT_TIMEOUT;
  314. }
  315. InterlockedDecrement((LPLONG)&dwPutBlockedCount);
  316. LeaveCriticalSection(&pLockTableEntry->csCritSec);
  317. return dwRet;
  318. }
  319. //
  320. // Private functions.
  321. //
  322. PFILENAME_LOCK
  323. AcquireFileNameLock(
  324. STR *pstrFileName
  325. )
  326. /*++
  327. Routine Description:
  328. Acquire a file name lock after looking it up in the table. We hash the file
  329. name, search the appropriate bucket for it, and acquire it. If we don't
  330. find one we create a new one.
  331. Arguments:
  332. strFileName - Name of the file name we're "locking".
  333. Return Value:
  334. Pointer to the file name lock we return.
  335. --*/
  336. {
  337. DWORD dwHash;
  338. CHAR *pchTemp;
  339. PLIST_ENTRY pCurrentEntry;
  340. PFILENAME_LOCK pFNLock;
  341. // First, hash the file name.
  342. dwHash = 0;
  343. pchTemp = pstrFileName->QueryStr();
  344. while (*pchTemp != '\0')
  345. {
  346. dwHash += (DWORD) *pchTemp;
  347. dwHash = _rotr(dwHash, 1);
  348. pchTemp++;
  349. }
  350. dwHash = ( (dwHash & 0xff) +
  351. ((dwHash >> 8) & 0xff) +
  352. ((dwHash >> 16) & 0xff) +
  353. ((dwHash >> 24) & 0xff) ) % MAX_FN_LOCK_BUCKETS;
  354. // We've hashed it, now take the critical section for this bucket.
  355. EnterCriticalSection(&FNLockTable[dwHash].csCritSec);
  356. // Walk down the bucket, looking for an existing lock structure.
  357. pCurrentEntry = FNLockTable[dwHash].ListHead.Flink;
  358. while (pCurrentEntry != &FNLockTable[dwHash].ListHead)
  359. {
  360. pFNLock = CONTAINING_RECORD(pCurrentEntry, FILENAME_LOCK, ListEntry);
  361. // See if this one matches.
  362. if (pFNLock->IsEqual(pstrFileName))
  363. {
  364. DWORD dwRet;
  365. // It is, so try to acquire this one. The acquire routine will free
  366. // the critical section for us.
  367. dwRet = pFNLock->Acquire();
  368. // Here we're done waiting. If we succeeded, return a pointer to
  369. // the lock, otherwise return NULL.
  370. if (dwRet == WAIT_OBJECT_0)
  371. {
  372. return pFNLock;
  373. }
  374. else
  375. {
  376. SetLastError(ERROR_INVALID_PARAMETER);
  377. return NULL;
  378. }
  379. }
  380. // Wasn't this one, try the next one.
  381. pCurrentEntry = pFNLock->Next();
  382. }
  383. // If we get here there is no filename lock currently existing, so create
  384. // a new one.
  385. pFNLock = new FILENAME_LOCK(pstrFileName, &FNLockTable[dwHash]);
  386. // Don't need the crit sec anymore, so let it go.
  387. LeaveCriticalSection(&FNLockTable[dwHash].csCritSec);
  388. if (pFNLock == NULL)
  389. {
  390. // Couldn't get it.
  391. return NULL;
  392. }
  393. // Make sure the initialization worked.
  394. if (!pFNLock->IsValid())
  395. {
  396. // It didn't.
  397. delete pFNLock;
  398. return NULL;
  399. }
  400. // Otherwise, we're cool, and we own the newly created lock, so we're done.
  401. return pFNLock;
  402. }
  403. BOOL
  404. HTTP_REQUEST::BuildPutDeleteHeader(
  405. enum WRITE_TYPE Action
  406. )
  407. /*++
  408. Routine Description:
  409. This is a utility routine to build a response header and body appropriate
  410. for a put or delete response.
  411. Arguments:
  412. Action - The action we just took.
  413. Return Value:
  414. None
  415. --*/
  416. {
  417. BOOL fFinished;
  418. CHAR *pszTail;
  419. CHAR *pszResp;
  420. DWORD cbRespBytes;
  421. DWORD cbRespBytesNeeded;
  422. STACK_STR(strEscapedURL, 256);
  423. strEscapedURL.Copy(_strURL);
  424. strEscapedURL.Escape();
  425. if (Action == WTYPE_CREATE)
  426. {
  427. CHAR *pszHostName;
  428. CHAR *pszProtocol;
  429. DWORD cbProtocolLength;
  430. DWORD cbHostNameLength;
  431. BOOL bNeedPort;
  432. if (!BuildHttpHeader(&fFinished, "201 Created", NULL))
  433. {
  434. return FALSE;
  435. }
  436. pszResp = (CHAR *)QueryRespBufPtr();
  437. cbRespBytes = strlen(pszResp);
  438. pszHostName = QueryHostAddr();
  439. cbHostNameLength = strlen(pszHostName);
  440. // Now we need to add the Location header, which means fully
  441. // qualifying the URL. First figure out how many bytes we might
  442. // need, then build the Location header.
  443. if (IsSecurePort())
  444. {
  445. pszProtocol = "https://";
  446. cbProtocolLength = sizeof("https://") - 1;
  447. bNeedPort = (INT) QueryClientConn()->QueryPort() != HTTP_SSL_PORT;
  448. }
  449. else
  450. {
  451. pszProtocol = "http://";
  452. cbProtocolLength = sizeof("http://") - 1;
  453. bNeedPort = (INT) QueryClientConn()->QueryPort() != 80;
  454. }
  455. cbRespBytesNeeded = cbRespBytes +
  456. (sizeof("Location: ")) - 1 +
  457. cbProtocolLength +
  458. cbHostNameLength +
  459. (bNeedPort ? 6 : 0) + // For :port, if needed
  460. strEscapedURL.QueryCB() +
  461. sizeof("\r\n") - 1;
  462. if (!QueryRespBuf()->Resize(cbRespBytesNeeded))
  463. {
  464. return FALSE;
  465. }
  466. pszResp = (CHAR *)QueryRespBufPtr();
  467. pszTail = pszResp + cbRespBytes;
  468. APPEND_STRING(pszTail, "Location: ");
  469. memcpy(pszTail, pszProtocol, cbProtocolLength);
  470. pszTail += cbProtocolLength;
  471. memcpy(pszTail, pszHostName, cbHostNameLength);
  472. pszTail += cbHostNameLength;
  473. // Append :port if we need to.
  474. if (bNeedPort)
  475. {
  476. APPEND_STRING(pszTail, ":");
  477. pszTail += sprintf(pszTail, "%u",
  478. (UINT)(QueryClientConn()->QueryPort()));
  479. }
  480. memcpy(pszTail, strEscapedURL.QueryStr(), strEscapedURL.QueryCB());
  481. pszTail += strEscapedURL.QueryCB();
  482. APPEND_STRING(pszTail, "\r\n");
  483. cbRespBytes = cbRespBytesNeeded;
  484. }
  485. else
  486. {
  487. if (!BuildBaseResponseHeader(QueryRespBuf(),
  488. &fFinished,
  489. NULL,
  490. 0))
  491. {
  492. return FALSE;
  493. }
  494. pszResp = (CHAR *)QueryRespBufPtr();
  495. cbRespBytes = strlen(pszResp);
  496. }
  497. cbRespBytesNeeded = cbRespBytes +
  498. sizeof("Content-Type: text/html\r\n") - 1 +
  499. sizeof("Content-Length: ") - 1 +
  500. 5 + // For content length digits
  501. MAX_ALLOW_SIZE +
  502. strEscapedURL.QueryCB() +
  503. sizeof("\r\n\r\n") - 1 +
  504. 7 + // "written"/"deleted"/"created"
  505. sizeof(cPutDeleteBody);
  506. if (!QueryRespBuf()->Resize(cbRespBytesNeeded))
  507. {
  508. return FALSE;
  509. }
  510. pszResp = (CHAR *)QueryRespBufPtr();
  511. pszTail = pszResp + cbRespBytes;
  512. APPEND_STRING(pszTail, "Content-Type: text/html\r\n");
  513. APPEND_STRING(pszTail, "Content-Length: ");
  514. pszTail += sprintf(pszTail, "%u\r\n", sizeof(cPutDeleteBody) - 1 +
  515. strEscapedURL.QueryCB() +
  516. sizeof("deleted") - 1 -
  517. (sizeof("%s") - 1) -
  518. (sizeof("%s") - 1));
  519. if (Action != WTYPE_DELETE)
  520. {
  521. pszTail += BuildAllowHeader(_strURL.QueryStr(), pszTail);
  522. }
  523. APPEND_STRING(pszTail, "\r\n");
  524. sprintf(pszTail, cPutDeleteBody, strEscapedURL.QueryStr(),
  525. (Action == WTYPE_CREATE ? "created" :
  526. (Action == WTYPE_WRITE ? "written" : "deleted")));
  527. return TRUE;
  528. }
  529. VOID
  530. HTTP_REQUEST::CleanupTempFile(
  531. VOID )
  532. /*++
  533. Routine Description:
  534. Utility routine to close the temp file handle and delete the file.
  535. Arguments:
  536. None
  537. Return Value:
  538. None
  539. --*/
  540. {
  541. // Impersonate the user who opened the file. If the temp file handle
  542. // is still valid, close it and delete the file.
  543. if ( ImpersonateUser( ) )
  544. {
  545. if ( _hTempFileHandle != INVALID_HANDLE_VALUE )
  546. {
  547. ::CloseHandle( _hTempFileHandle );
  548. ::DeleteFile( _strTempFileName.QueryStr( ) );
  549. _hTempFileHandle = INVALID_HANDLE_VALUE;
  550. }
  551. RevertUser( );
  552. }
  553. }
  554. VOID
  555. HTTP_REQUEST::VerifyMimeType(
  556. VOID
  557. )
  558. /*++
  559. Routine Description:
  560. Make sure that the mime type specified for the current file
  561. matches that specified by the client. If it doesn't, create
  562. a custom mime mapping for this URL.
  563. Arguments:
  564. None.
  565. Return Value:
  566. --*/
  567. {
  568. STACK_STR(strMimeType, 80);
  569. CHAR *pszClientMimeType;
  570. BOOL bHaveMapping;
  571. // See if the client specified a mime type. If he didn't,
  572. // we're done.
  573. //
  574. pszClientMimeType = (CHAR * ) _HeaderList.FastMapQueryValue(HM_CTY);
  575. if (pszClientMimeType == NULL)
  576. {
  577. return; // No mime type, system default is OK.
  578. }
  579. //
  580. // Client specified a mime type. See if it matches what we already
  581. // have.
  582. //
  583. bHaveMapping = SelectMimeMapping( &strMimeType,
  584. _strPhysicalPath.QueryStr(),
  585. _pMetaData);
  586. if (!bHaveMapping || _stricmp(strMimeType.QueryStr(), pszClientMimeType))
  587. {
  588. //
  589. // Either the mime type lookup failed, or what we have doesn't match what
  590. // the client specified. In either case create a custom mime map entry
  591. // for the specified URL. The custom mime map we create is a default
  592. // one, since it's on a specific file.
  593. //
  594. MB mb( (IMDCOM*) g_pInetSvc->QueryMDObject() );
  595. STACK_STR(strFullMDPath, 128);
  596. STACK_STR(strCustomMime, 80);
  597. if (!strCustomMime.Copy("*") ||
  598. !strCustomMime.Append(",", sizeof(",") - 1) ||
  599. !strCustomMime.Append(pszClientMimeType) )
  600. {
  601. return;
  602. }
  603. // Append the extra NULL.
  604. if (!strCustomMime.Resize(strCustomMime.QueryCCH() + 2))
  605. {
  606. return;
  607. }
  608. if (!strCustomMime.SetLen(strCustomMime.QueryCB() + 1))
  609. {
  610. return;
  611. }
  612. //
  613. // Construct the full path to the URL, and try and open it.
  614. //
  615. if (!strFullMDPath.Copy(QueryW3Instance()->QueryMDVRPath(),
  616. QueryW3Instance()->QueryMDVRPathLen() - 1) ||
  617. !strFullMDPath.Append(_strURL))
  618. {
  619. //
  620. // Couldn't construct the path.
  621. //
  622. return;
  623. }
  624. // Now try and open it.
  625. if ( !mb.Open( strFullMDPath.QueryStr(),
  626. METADATA_PERMISSION_READ | METADATA_PERMISSION_WRITE )
  627. )
  628. {
  629. // See what the error was.
  630. if (GetLastError() != ERROR_PATH_NOT_FOUND)
  631. {
  632. // Some error other than not exist, so quit.
  633. return;
  634. }
  635. // The path doesn't exist, so add it.
  636. if (!mb.Open(QueryW3Instance()->QueryMDVRPath(),
  637. METADATA_PERMISSION_READ | METADATA_PERMISSION_WRITE)
  638. )
  639. {
  640. return;
  641. }
  642. if (!mb.AddObject(_strURL.QueryStr()) &&
  643. GetLastError() != ERROR_ALREADY_EXISTS
  644. )
  645. {
  646. return;
  647. }
  648. // We added it. Close the handle so the next guy can get in.
  649. DBG_REQUIRE(mb.Close());
  650. //
  651. // Now try again to open the full path.
  652. //
  653. if ( !mb.Open( strFullMDPath.QueryStr(),
  654. METADATA_PERMISSION_READ | METADATA_PERMISSION_WRITE )
  655. )
  656. {
  657. return;
  658. }
  659. }
  660. mb.SetMultiSZ( "",
  661. MD_MIME_MAP,
  662. IIS_MD_UT_FILE,
  663. strCustomMime.QueryStr());
  664. }
  665. }
  666. BOOL
  667. HTTP_REQUEST::DoPut(
  668. BOOL * pfFinished
  669. )
  670. /*++
  671. Routine Description:
  672. Handle a PUT request ( cf HTTP 1.1 spec )
  673. Check the URL being put, read the entity body and write it to disk, etc.
  674. Arguments:
  675. pfFinished - Set to TRUE if no further processings is needed for this
  676. request
  677. Return Value:
  678. TRUE if success, else FALSE
  679. --*/
  680. {
  681. HANDLE hFile;
  682. SECURITY_ATTRIBUTES sa;
  683. TCHAR szTempFileName[MAX_PATH/sizeof(TCHAR)];
  684. DWORD err;
  685. BOOL fDone = FALSE;
  686. BOOL fFileSuccess;
  687. DWORD cbFileBytesWritten;
  688. DWORD dwFileCreateError;
  689. BOOL fReturn;
  690. BOOL bPrecondWorked;
  691. CHAR *pszContentType;
  692. switch (_putstate)
  693. {
  694. case PSTATE_START:
  695. //
  696. // Make sure the virtual root allows write access
  697. //
  698. if ( !IS_ACCESS_ALLOWED(WRITE) )
  699. {
  700. SetState( HTR_DONE, HT_FORBIDDEN, ERROR_ACCESS_DENIED );
  701. Disconnect( HT_FORBIDDEN,
  702. IDS_WRITE_ACCESS_DENIED,
  703. FALSE,
  704. pfFinished );
  705. return TRUE;
  706. }
  707. //
  708. // Check what we're trying to PUT, make sure we have permission.
  709. //
  710. if ( !VrootAccessCheck( _pMetaData, FILE_GENERIC_WRITE ) )
  711. {
  712. SetDeniedFlags( SF_DENIED_RESOURCE );
  713. return FALSE;
  714. }
  715. // We don't support Range requests on PUTs.
  716. if (
  717. QueryHeaderList()->FindValueInChunks("Content-Range:",
  718. sizeof("Content-Range:") - 1)
  719. != NULL
  720. )
  721. {
  722. SetState( HTR_DONE, HT_NOT_SUPPORTED, ERROR_NOT_SUPPORTED );
  723. Disconnect( HT_NOT_SUPPORTED, IDS_PUT_RANGE_UNSUPPORTED, FALSE, pfFinished );
  724. return TRUE;
  725. }
  726. // Also don't support PUT to the '*' URL.
  727. if (*_strURL.QueryStr() == '*')
  728. {
  729. // Don't allow GETs on the server URL.
  730. SetState( HTR_DONE, HT_BAD_REQUEST, ERROR_INVALID_PARAMETER );
  731. Disconnect( HT_BAD_REQUEST,
  732. NULL,
  733. FALSE,
  734. pfFinished );
  735. return TRUE;
  736. }
  737. //
  738. // Make sure we support this content type. We don't understand how to
  739. // handle composite MIME types, so fail if it's a message/ content type.
  740. //
  741. pszContentType = (CHAR * ) _HeaderList.FastMapQueryValue(HM_CTY);
  742. if (pszContentType != NULL)
  743. {
  744. if (!_strnicmp(pszContentType,
  745. "message/",
  746. sizeof("message/") - 1)
  747. )
  748. {
  749. SetState( HTR_DONE, HT_NOT_SUPPORTED, ERROR_NOT_SUPPORTED );
  750. Disconnect( HT_NOT_SUPPORTED, IDS_UNSUPPORTED_CONTENT_TYPE, FALSE, pfFinished );
  751. return TRUE;
  752. }
  753. }
  754. // If we don't have a content length or chunked transfer encoding,
  755. // fail.
  756. if (!_fHaveContentLength && !IsChunked())
  757. {
  758. SetState( HTR_DONE, HT_LENGTH_REQUIRED, ERROR_NOT_SUPPORTED );
  759. Disconnect( HT_LENGTH_REQUIRED, IDS_CANNOT_DETERMINE_LENGTH, FALSE, pfFinished );
  760. return TRUE;
  761. }
  762. // Now get the filename lock.
  763. _pFileNameLock = AcquireFileNameLock(&_strPhysicalPath);
  764. if (_pFileNameLock == NULL)
  765. {
  766. // Couldn't get it, return 'resource busy'.
  767. //make sure we log the event
  768. SetState ( HTR_DONE, HT_SVC_UNAVAILABLE, ERROR_SHARING_VIOLATION );
  769. Disconnect( HT_SVC_UNAVAILABLE, IDS_PUT_CONTENTION, FALSE, pfFinished );
  770. return TRUE;
  771. }
  772. if ( !ImpersonateUser( ) )
  773. {
  774. _pFileNameLock->Release();
  775. _pFileNameLock = NULL;
  776. return (FALSE);
  777. }
  778. sa.nLength = sizeof(sa);
  779. sa.lpSecurityDescriptor = NULL;
  780. sa.bInheritHandle = FALSE;
  781. hFile = ::CreateFile( _strPhysicalPath.QueryStr(),
  782. GENERIC_READ | GENERIC_WRITE,
  783. g_fIsWindows95 ?
  784. (FILE_SHARE_READ | FILE_SHARE_WRITE) :
  785. (FILE_SHARE_READ | FILE_SHARE_WRITE |
  786. FILE_SHARE_DELETE),
  787. &sa,
  788. OPEN_ALWAYS,
  789. FILE_ATTRIBUTE_NORMAL,
  790. NULL);
  791. if ( ( hFile != INVALID_HANDLE_VALUE ) &&
  792. ( GetFileType( hFile ) != FILE_TYPE_DISK ) )
  793. {
  794. DBG_REQUIRE( ::CloseHandle( hFile ) );
  795. hFile = INVALID_HANDLE_VALUE;
  796. SetLastError( ERROR_ACCESS_DENIED );
  797. }
  798. dwFileCreateError = ::GetLastError();
  799. if ( hFile == INVALID_HANDLE_VALUE)
  800. {
  801. // Some sort of error opening the file.
  802. RevertUser();
  803. _pFileNameLock->Release();
  804. _pFileNameLock = NULL;
  805. if ( dwFileCreateError == ERROR_FILE_NOT_FOUND ||
  806. dwFileCreateError == ERROR_PATH_NOT_FOUND)
  807. {
  808. // The path or filename itself is bad, fail the request.
  809. SetState( HTR_DONE, HT_NOT_FOUND, GetLastError() );
  810. Disconnect( HT_NOT_FOUND, NO_ERROR, FALSE, pfFinished );
  811. return TRUE;
  812. }
  813. if ( dwFileCreateError == ERROR_INVALID_NAME )
  814. {
  815. // An invalid name.
  816. SetState( HTR_DONE, HT_BAD_REQUEST, GetLastError() );
  817. Disconnect( HT_BAD_REQUEST, NO_ERROR, FALSE, pfFinished );
  818. return TRUE;
  819. }
  820. else
  821. {
  822. // A 'bad' error has occured, so return FALSE.
  823. if ( dwFileCreateError == ERROR_ACCESS_DENIED )
  824. {
  825. SetDeniedFlags( SF_DENIED_RESOURCE );
  826. }
  827. }
  828. return FALSE;
  829. }
  830. else
  831. {
  832. // The create worked. If this isn't a directory and it's not
  833. // hidden or readonly we're OK. If we just created it now, delete
  834. // it so we don't have to remember to if the put fails later.
  835. BY_HANDLE_FILE_INFORMATION FileInfo;
  836. // Get file information and check for a directory or hidden file.
  837. fReturn = GetFileInformationByHandle(
  838. hFile,
  839. &FileInfo
  840. );
  841. // See if this file is accessible. If not, fail.
  842. if ( fReturn)
  843. {
  844. // We got the information.
  845. if ( FileInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
  846. {
  847. DBG_REQUIRE( ::CloseHandle( hFile ));
  848. if ( dwFileCreateError != ERROR_ALREADY_EXISTS)
  849. {
  850. ::DeleteFile( _strPhysicalPath.QueryStr() );
  851. }
  852. // Don't allow puts to directory.
  853. RevertUser();
  854. _pFileNameLock->Release();
  855. _pFileNameLock = NULL;
  856. SetState( HTR_DONE, HT_BAD_REQUEST, GetLastError() );
  857. Disconnect( HT_BAD_REQUEST, NO_ERROR, FALSE, pfFinished );
  858. return TRUE;
  859. }
  860. if ( FileInfo.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
  861. {
  862. // Pretend we can't see hidden files.
  863. DBG_REQUIRE( ::CloseHandle( hFile ));
  864. if ( dwFileCreateError != ERROR_ALREADY_EXISTS)
  865. {
  866. ::DeleteFile( _strPhysicalPath.QueryStr() );
  867. }
  868. RevertUser();
  869. _pFileNameLock->Release();
  870. _pFileNameLock = NULL;
  871. SetState( HTR_DONE, HT_NOT_FOUND, GetLastError() );
  872. Disconnect( HT_NOT_FOUND, NO_ERROR, FALSE, pfFinished );
  873. return TRUE;
  874. }
  875. } else
  876. {
  877. // Some sort of fatal error.
  878. DBG_REQUIRE( ::CloseHandle( hFile ));
  879. if ( dwFileCreateError != ERROR_ALREADY_EXISTS)
  880. {
  881. ::DeleteFile( _strPhysicalPath.QueryStr() );
  882. }
  883. RevertUser();
  884. _pFileNameLock->Release();
  885. _pFileNameLock = NULL;
  886. return FALSE;
  887. }
  888. }
  889. // Check the preconditions on this file, if there are any. It's
  890. // possible that it would be better to do this at the very end,
  891. // right before we rename the file, but for not we do it here.
  892. bPrecondWorked = FALSE;
  893. _bFileExisted = (dwFileCreateError == ERROR_ALREADY_EXISTS);
  894. if (_fIfModifier)
  895. {
  896. // Have a modifier. See if it succeeds. If it does, CheckPreconditions
  897. // will do a synchronous send of the appropriate response header.
  898. bPrecondWorked = CheckPreconditions(hFile,
  899. _bFileExisted,
  900. pfFinished,
  901. &fReturn
  902. );
  903. }
  904. // Close the handle now, since we know longer need it.
  905. DBG_REQUIRE( ::CloseHandle( hFile ));
  906. if (!_bFileExisted)
  907. {
  908. // File didn't originally exist, so delete the one we just created.
  909. ::DeleteFile( _strPhysicalPath.QueryStr() );
  910. }
  911. if (bPrecondWorked)
  912. {
  913. _pFileNameLock->Release();
  914. _pFileNameLock = NULL;
  915. if (!fReturn)
  916. {
  917. return fReturn;
  918. }
  919. // We had a precondition work. If we're read the entire entity
  920. // body, we're done. Otherwise we need to keep reading and
  921. // discarding the data. CheckPreconditions has already done a synchronous
  922. // send of the appropriate headers.
  923. if (QueryClientContentLength() <= QueryTotalEntityBodyCB())
  924. {
  925. // All done. The call to CheckPreconditions() above should
  926. // have set our state to done already.
  927. DBG_ASSERT(QueryState() == HTR_DONE);
  928. *pfFinished = TRUE;
  929. return TRUE;
  930. }
  931. // Otherwise we need to start reading and discarding. We start the
  932. // first read here. If that pends, we just return, leaving our put
  933. // state set to discard-read so that we'll do the appropriate
  934. // things when the read completes. If the read succeeds now,
  935. // we set our put state to discard-chunk and call ourselves
  936. // recursively to let the discard-chunk substate handler do the
  937. // right things.
  938. DBG_ASSERT(!*pfFinished);
  939. SetState( HTR_DOVERB, _dwLogHttpResponse, NO_ERROR );
  940. _putstate = PSTATE_DISCARD_READ;
  941. if ( !ReadEntityBody( &fDone, TRUE,
  942. QueryMetaData()->QueryPutReadSize() ))
  943. {
  944. return FALSE;
  945. }
  946. if (!fDone)
  947. {
  948. return TRUE;
  949. }
  950. _putstate = PSTATE_DISCARD_CHUNK;
  951. return DoPut(pfFinished);
  952. }
  953. // At this point we've verified the request. We need to generate a
  954. // temporary file name and if this is a 1.1 or greater client send
  955. // back a 100 Continue response.
  956. err = ::GetTempFileName(
  957. g_pszW3TempDirName,
  958. W3_TEMP_PREFIX,
  959. 0,
  960. szTempFileName
  961. );
  962. if ( err == 0 || !_strTempFileName.Copy( szTempFileName ) )
  963. {
  964. //
  965. // Couldn't create or copy a temporary file name.
  966. //
  967. RevertUser();
  968. _pFileNameLock->Release();
  969. _pFileNameLock = NULL;
  970. return FALSE;
  971. }
  972. // Now open the temp file. We specify OPEN_EXISTING because we
  973. // want to fail if someone's deleted the temp file name before we
  974. // opened it. If that's happened we have no real guarantee that the
  975. // file is still unique. We don't specify any sharing as we don't
  976. // want anyone else to write into while we are.
  977. _hTempFileHandle = ::CreateFile( szTempFileName,
  978. GENERIC_READ | GENERIC_WRITE,
  979. g_fIsWindows95 ? 0 : FILE_SHARE_DELETE,
  980. &sa,
  981. OPEN_EXISTING,
  982. FILE_FLAG_SEQUENTIAL_SCAN,
  983. NULL);
  984. RevertUser();
  985. if (_hTempFileHandle == INVALID_HANDLE_VALUE)
  986. {
  987. // Uh-oh. Couldn't open the temp file we just created. This is bad,
  988. // return FALSE to fail the request rudely.
  989. DBGPRINTF((DBG_CONTEXT,"CreateFile[%s] failed with %d\n",
  990. szTempFileName, GetLastError()));
  991. _pFileNameLock->Release();
  992. _pFileNameLock = NULL;
  993. return FALSE;
  994. }
  995. // Now everything's good. If this is a 1.1 client or greater send
  996. // back the 100 Continue response, otherwise skip that.
  997. if ( IsAtLeastOneOne() )
  998. {
  999. if ( !SendHeader( "100 Continue", "\r\n", IO_FLAG_SYNC, pfFinished,
  1000. HTTPH_NO_CONNECTION) )
  1001. {
  1002. // An error on the header send. Abort this request.
  1003. CleanupTempFile( );
  1004. _pFileNameLock->Release();
  1005. _pFileNameLock = NULL;
  1006. return FALSE;
  1007. }
  1008. if ( *pfFinished )
  1009. {
  1010. CleanupTempFile();
  1011. }
  1012. }
  1013. // Set out put state to reading, and begin reading the entity body.
  1014. _putstate = PSTATE_READING;
  1015. if ( !ReadEntityBody( &fDone, TRUE, QueryMetaData()->QueryPutReadSize() ))
  1016. {
  1017. // Had some sort of problem reading the entity body. This is
  1018. // fatal.
  1019. CleanupTempFile( );
  1020. _pFileNameLock->Release();
  1021. _pFileNameLock = NULL;
  1022. return FALSE;
  1023. }
  1024. if ( !fDone )
  1025. {
  1026. return TRUE;
  1027. }
  1028. DBG_ASSERT((QueryEntityBodyCB() >= QueryMetaData()->QueryPutReadSize())
  1029. || (QueryEntityBodyCB() == QueryClientContentLength()));
  1030. // Fall through to the PSTATE_READING handler.
  1031. case PSTATE_READING:
  1032. // When we get here, we've completed a read. If this isn't the
  1033. // fall through case call ReadEntityBody again to find out if
  1034. // we're done with the current chunk.
  1035. if ( !fDone )
  1036. {
  1037. if ( !ReadEntityBody( &fDone, FALSE,
  1038. QueryMetaData()->QueryPutReadSize() ))
  1039. {
  1040. // Had some sort of problem reading the entity body. This is
  1041. // fatal.
  1042. CleanupTempFile( );
  1043. _pFileNameLock->Release();
  1044. _pFileNameLock = NULL;
  1045. return FALSE;
  1046. }
  1047. if ( !fDone )
  1048. {
  1049. return TRUE;
  1050. }
  1051. }
  1052. // Right here we know we've read the current chunk, so we want
  1053. // to set our state to writing and write this to the temp file.
  1054. do
  1055. {
  1056. DWORD dwAmountToWrite;
  1057. // Since there could be extra slop in the buffer, we need to
  1058. // figure out how much to actually write. TotalEntityBodyCB
  1059. // is the total number of bytes we've read into the buffer
  1060. // for this request, and EntityBodyCB is the number of bytes
  1061. // in the buffer for this chunk. Therefore TEBCB - EBCB is the
  1062. // amount we've already read and written to the file. We want to
  1063. // write the minimum of the ContentLength - this amount and
  1064. // EBCB.
  1065. dwAmountToWrite = min ( QueryClientContentLength() -
  1066. (QueryTotalEntityBodyCB() -
  1067. QueryEntityBodyCB()),
  1068. QueryEntityBodyCB() );
  1069. //
  1070. // Check to see if the amount we're to write is 0, and skip if it
  1071. // is. This can happen in the chunked case where all we got on
  1072. // the last read is the 0 terminator.
  1073. //
  1074. if (dwAmountToWrite != 0)
  1075. {
  1076. fFileSuccess = ::WriteFile( _hTempFileHandle,
  1077. QueryEntityBody(),
  1078. dwAmountToWrite,
  1079. &cbFileBytesWritten,
  1080. NULL
  1081. );
  1082. if ( !fFileSuccess || cbFileBytesWritten != dwAmountToWrite )
  1083. {
  1084. // Some sort of problem with the write. Abort the request.
  1085. CleanupTempFile( );
  1086. _pFileNameLock->Release();
  1087. _pFileNameLock = NULL;
  1088. return FALSE;
  1089. }
  1090. }
  1091. _cbEntityBody = 0;
  1092. _cbBytesWritten = 0;
  1093. // Now, if we haven't read all of the entity body try to read some more.
  1094. if ( QueryClientContentLength() > QueryTotalEntityBodyCB() )
  1095. {
  1096. if ( !ReadEntityBody( &fDone, TRUE,
  1097. QueryMetaData()->QueryPutReadSize() ))
  1098. {
  1099. // Had some sort of problem reading the entity body. This is
  1100. // fatal.
  1101. CleanupTempFile( );
  1102. _pFileNameLock->Release();
  1103. _pFileNameLock = NULL;
  1104. return FALSE;
  1105. }
  1106. } else
  1107. {
  1108. // We've read and written all of the entity body. Rename the
  1109. // temp file now, close the temp file handle (causing a delete),
  1110. // flush the atq cache and we're done.
  1111. if ( !ImpersonateUser( ) )
  1112. {
  1113. CleanupTempFile( );
  1114. _pFileNameLock->Release();
  1115. _pFileNameLock = NULL;
  1116. return (FALSE);
  1117. }
  1118. //
  1119. // If the file we're PUTing already exists, rename it to
  1120. // another name before we move the other one over.
  1121. if ( _bFileExisted )
  1122. {
  1123. err = ::GetTempFileName(
  1124. g_pszW3TempDirName,
  1125. W3_TEMP_PREFIX,
  1126. 0,
  1127. szTempFileName
  1128. );
  1129. if ( err == 0 )
  1130. {
  1131. //
  1132. // Couldn't create a temporary file name.
  1133. //
  1134. RevertUser();
  1135. CleanupTempFile();
  1136. _pFileNameLock->Release();
  1137. _pFileNameLock = NULL;
  1138. return FALSE;
  1139. }
  1140. // Now do the actual rename. GetTempFileName() will
  1141. // have created the file.
  1142. if ( g_fIsWindows95 ) {
  1143. fFileSuccess = ::W95MoveFileEx( _strPhysicalPath.QueryStr(),
  1144. szTempFileName
  1145. );
  1146. } else {
  1147. fFileSuccess = ::MoveFileEx(_strPhysicalPath.QueryStr(),
  1148. szTempFileName,
  1149. MOVEFILE_REPLACE_EXISTING |
  1150. MOVEFILE_COPY_ALLOWED
  1151. );
  1152. }
  1153. if ( !fFileSuccess )
  1154. {
  1155. // Couldn't move the file!
  1156. RevertUser();
  1157. CleanupTempFile();
  1158. _pFileNameLock->Release();
  1159. _pFileNameLock = NULL;
  1160. return FALSE;
  1161. }
  1162. }
  1163. ::CloseHandle( _hTempFileHandle );
  1164. _hTempFileHandle = INVALID_HANDLE_VALUE;
  1165. // Now rename the temp file to the file we're PUTing. The file
  1166. // shouldn't already exist (since we'd have renamed it if it
  1167. // did), so this should fail if it does.
  1168. if ( g_fIsWindows95 ) {
  1169. fFileSuccess = ::W95MoveFileEx(_strTempFileName.QueryStr(),
  1170. _strPhysicalPath.QueryStr()
  1171. );
  1172. } else {
  1173. fFileSuccess = ::MoveFileEx(_strTempFileName.QueryStr(),
  1174. _strPhysicalPath.QueryStr(),
  1175. MOVEFILE_COPY_ALLOWED
  1176. );
  1177. }
  1178. _pFileNameLock->Release();
  1179. _pFileNameLock = NULL;
  1180. if ( fFileSuccess ) {
  1181. BOOL fHandled;
  1182. DWORD cbDummy;
  1183. // The rename worked. Cleanup the renamed copy of the
  1184. // original file if it exists, and flush the cache.
  1185. if (_bFileExisted)
  1186. {
  1187. ::DeleteFile( szTempFileName );
  1188. }
  1189. RevertUser( );
  1190. _cFilesReceived++;
  1191. // Flush the atq cache here.
  1192. TsFlushURL(QueryW3Instance()->GetTsvcCache(),
  1193. _strURL.QueryStr(),
  1194. _strURL.QueryCB(),
  1195. RESERVED_DEMUX_URI_INFO);
  1196. //
  1197. // Now see if the mime type we currently have
  1198. // matches the mimetype the client requested. If
  1199. // it does, or the client requested one, we're
  1200. // good. Otherwise we need to create a custom
  1201. // mimetype entry for this file.
  1202. //
  1203. VerifyMimeType();
  1204. // Build and send the response.
  1205. if ( !BuildPutDeleteHeader( _bFileExisted ?
  1206. WTYPE_WRITE : WTYPE_CREATE ))
  1207. {
  1208. return FALSE;
  1209. }
  1210. SetState( HTR_DONE, _bFileExisted ? HT_OK : HT_CREATED,
  1211. NO_ERROR );
  1212. return SendHeader( QueryRespBufPtr(),
  1213. (DWORD) -1,
  1214. IO_FLAG_ASYNC,
  1215. pfFinished );
  1216. } else
  1217. {
  1218. // The rename failed. There's no easy way to tell why.
  1219. // Leave a backup copy around, and log it so that an admin
  1220. // can restore it.
  1221. const CHAR *pszFileName[3];
  1222. err = GetLastError();
  1223. pszFileName[0] = _strRawURL.QueryStr();
  1224. pszFileName[1] = _strPhysicalPath.QueryStr();
  1225. if (_bFileExisted)
  1226. {
  1227. pszFileName[2] = szTempFileName;
  1228. g_pInetSvc->LogEvent( W3_EVENT_CANNOT_PUT_RENAME,
  1229. 3,
  1230. pszFileName,
  1231. 0 );
  1232. }
  1233. else
  1234. {
  1235. g_pInetSvc->LogEvent( W3_EVENT_CANNOT_PUT,
  1236. 2,
  1237. pszFileName,
  1238. 0 );
  1239. }
  1240. IF_DEBUG(ERROR) {
  1241. DBGPRINTF((DBG_CONTEXT,
  1242. "MoveFileEx[%s to %s] failed with %d\n",
  1243. _strTempFileName.QueryStr(),
  1244. _strPhysicalPath.QueryStr(),
  1245. err));
  1246. }
  1247. ::DeleteFile( _strTempFileName.QueryStr( ));
  1248. RevertUser( );
  1249. //
  1250. // Rename failed somehow.
  1251. //
  1252. SetLastError(err);
  1253. return FALSE;
  1254. }
  1255. }
  1256. } while ( fDone );
  1257. return TRUE;
  1258. break;
  1259. case PSTATE_DISCARD_READ:
  1260. // Just had a read complete. See if we've read all of the data for
  1261. // this chunk. If not, wait until the next read completes. Otherwise
  1262. // see if we're done reading and discarding.
  1263. if ( !ReadEntityBody( &fDone, FALSE,
  1264. QueryMetaData()->QueryPutReadSize() ))
  1265. {
  1266. return FALSE;
  1267. }
  1268. if (!fDone)
  1269. {
  1270. // Another read pending, so wait for it to complete.
  1271. return TRUE;
  1272. }
  1273. // Otherwise we're read one chunk. Fall through to the discard chunk
  1274. // code, to see if we're done overall and possibly get another
  1275. // read going.
  1276. case PSTATE_DISCARD_CHUNK:
  1277. do {
  1278. if ( QueryClientContentLength() <= QueryTotalEntityBodyCB() )
  1279. {
  1280. // We're read all of the data, so we're done.
  1281. SetState( HTR_DONE, _dwLogHttpResponse, NO_ERROR );
  1282. *pfFinished = TRUE;
  1283. return TRUE;
  1284. }
  1285. // Haven't read it all yet, get another read going.
  1286. _putstate = PSTATE_DISCARD_READ;
  1287. _cbEntityBody = 0;
  1288. _cbBytesWritten = 0;
  1289. DBG_ASSERT(QueryClientContentLength() > QueryTotalEntityBodyCB());
  1290. if ( !ReadEntityBody( &fDone, TRUE,
  1291. QueryMetaData()->QueryPutReadSize() ))
  1292. {
  1293. return FALSE;
  1294. }
  1295. } while ( fDone );
  1296. return TRUE;
  1297. default:
  1298. break;
  1299. }
  1300. return FALSE;
  1301. }
  1302. BOOL
  1303. HTTP_REQUEST::DoDelete(
  1304. BOOL * pfFinished
  1305. )
  1306. /*++
  1307. Routine Description:
  1308. Handle a DELETE request ( cf HTTP 1.1 spec )
  1309. Check the URL being deleted, make sure we have permission, and
  1310. delete it.
  1311. In order to be safe we need to be sure we can both delete the file and
  1312. send a response header. To make this work we'll open the file with delete
  1313. access. If that works we'll try and build and send the response header, and
  1314. iff that works we'll actually try to delete the file.
  1315. Arguments:
  1316. None
  1317. Return Value:
  1318. TRUE if success, else
  1319. FALSE
  1320. --*/
  1321. {
  1322. HANDLE hFile;
  1323. SECURITY_ATTRIBUTES sa;
  1324. DWORD err;
  1325. STR ResponseStr;
  1326. BOOL fDone;
  1327. BOOL fReturn;
  1328. BOOL fDeleted = FALSE;
  1329. //
  1330. // Make sure the virtual root allows write access
  1331. //
  1332. if ( !IS_ACCESS_ALLOWED(WRITE) )
  1333. {
  1334. SetState( HTR_DONE, HT_FORBIDDEN, ERROR_ACCESS_DENIED );
  1335. Disconnect( HT_FORBIDDEN, IDS_WRITE_ACCESS_DENIED, FALSE, pfFinished );
  1336. return TRUE;
  1337. }
  1338. // Check that we're not trying to DELETE the '*' URL.
  1339. if (*_strURL.QueryStr() == '*')
  1340. {
  1341. // Don't allow GETs on the server URL.
  1342. SetState( HTR_DONE, HT_BAD_REQUEST, ERROR_INVALID_PARAMETER );
  1343. Disconnect( HT_BAD_REQUEST,
  1344. NULL,
  1345. FALSE,
  1346. pfFinished );
  1347. return TRUE;
  1348. }
  1349. // Now get the filename lock.
  1350. _pFileNameLock = AcquireFileNameLock(&_strPhysicalPath);
  1351. if (_pFileNameLock == NULL)
  1352. {
  1353. // Couldn't get it, return 'resource busy'.
  1354. return FALSE;
  1355. }
  1356. if ( !ImpersonateUser( ) )
  1357. {
  1358. _pFileNameLock->Release();
  1359. _pFileNameLock = NULL;
  1360. return (FALSE);
  1361. }
  1362. if ( TsDeleteOnClose(_pURIInfo, QueryImpersonationHandle(), &fDeleted ) ) {
  1363. if ( !fDeleted ) {
  1364. // A 'bad' error has occured, so return FALSE.
  1365. RevertUser();
  1366. SetDeniedFlags( SF_DENIED_RESOURCE );
  1367. _pFileNameLock->Release();
  1368. _pFileNameLock = NULL;
  1369. return FALSE;
  1370. } else {
  1371. // Try to build and send the HTTP response header.
  1372. if (!BuildPutDeleteHeader(WTYPE_DELETE)) {
  1373. RevertUser();
  1374. _pFileNameLock->Release();
  1375. _pFileNameLock = NULL;
  1376. return FALSE;
  1377. }
  1378. }
  1379. }
  1380. if (!fDeleted) {
  1381. sa.nLength = sizeof(sa);
  1382. sa.lpSecurityDescriptor = NULL;
  1383. sa.bInheritHandle = FALSE;
  1384. hFile = ::CreateFile( _strPhysicalPath.QueryStr(),
  1385. GENERIC_READ | GENERIC_WRITE,
  1386. g_fIsWindows95 ?
  1387. (FILE_SHARE_READ | FILE_SHARE_WRITE) :
  1388. (FILE_SHARE_READ | FILE_SHARE_WRITE |
  1389. FILE_SHARE_DELETE),
  1390. &sa,
  1391. OPEN_EXISTING,
  1392. FILE_ATTRIBUTE_NORMAL,
  1393. NULL);
  1394. if ( hFile == INVALID_HANDLE_VALUE)
  1395. {
  1396. // Some sort of error opening the file.
  1397. err = ::GetLastError();
  1398. RevertUser();
  1399. _pFileNameLock->Release();
  1400. _pFileNameLock = NULL;
  1401. if ( err == ERROR_FILE_NOT_FOUND ||
  1402. err == ERROR_PATH_NOT_FOUND)
  1403. {
  1404. // The path or filename itself is bad, fail the request.
  1405. SetState( HTR_DONE, HT_NOT_FOUND, GetLastError() );
  1406. Disconnect( HT_NOT_FOUND, NO_ERROR, FALSE, pfFinished );
  1407. return TRUE;
  1408. }
  1409. if ( err == ERROR_INVALID_NAME )
  1410. {
  1411. // An invalid name.
  1412. SetState( HTR_DONE, HT_BAD_REQUEST, GetLastError() );
  1413. Disconnect( HT_BAD_REQUEST, NO_ERROR, FALSE, pfFinished );
  1414. return TRUE;
  1415. }
  1416. else
  1417. {
  1418. // A 'bad' error has occured, so return FALSE.
  1419. if ( err == ERROR_ACCESS_DENIED )
  1420. {
  1421. SetDeniedFlags( SF_DENIED_RESOURCE );
  1422. }
  1423. }
  1424. return FALSE;
  1425. }
  1426. else
  1427. {
  1428. // We should be able to delete it, so just close the handle.
  1429. if (_fIfModifier)
  1430. {
  1431. fDone = CheckPreconditions(hFile,
  1432. TRUE,
  1433. pfFinished,
  1434. &fReturn
  1435. );
  1436. }
  1437. else
  1438. {
  1439. fDone = FALSE;
  1440. }
  1441. ::CloseHandle(hFile);
  1442. if (fDone)
  1443. {
  1444. RevertUser();
  1445. _pFileNameLock->Release();
  1446. _pFileNameLock = NULL;
  1447. *pfFinished = TRUE;
  1448. return fReturn;
  1449. }
  1450. }
  1451. TsFlushURL(QueryW3Instance()->GetTsvcCache(),
  1452. _strURL.QueryStr(),
  1453. _strURL.QueryCB(),
  1454. RESERVED_DEMUX_URI_INFO);
  1455. // Try to build and send the HTTP response header.
  1456. if (!BuildPutDeleteHeader(WTYPE_DELETE))
  1457. {
  1458. RevertUser();
  1459. _pFileNameLock->Release();
  1460. _pFileNameLock = NULL;
  1461. return FALSE;
  1462. }
  1463. // We built the successful response, now delete the actual file.
  1464. fDeleted = ::DeleteFile( _strPhysicalPath.QueryStr() );
  1465. }
  1466. RevertUser();
  1467. _pFileNameLock->Release();
  1468. _pFileNameLock = NULL;
  1469. if ( !fDeleted) {
  1470. return FALSE;
  1471. }
  1472. SetState( HTR_DONE, HT_OK, NO_ERROR );
  1473. if ( !SendHeader( QueryRespBufPtr(),
  1474. (DWORD) -1,
  1475. IO_FLAG_ASYNC,
  1476. pfFinished ))
  1477. {
  1478. // Presumably if the WriteFile fails something serious has gone wrong,
  1479. // and the connection is about to die.
  1480. return FALSE;
  1481. }
  1482. // Here we should go ahead and purge this from the Tsunami cache
  1483. return TRUE;
  1484. }
  1485. BOOL
  1486. W95MoveFileEx(
  1487. IN LPCSTR lpExistingFile,
  1488. IN LPCSTR lpNewFile
  1489. )
  1490. {
  1491. BOOL fRet;
  1492. fRet = ::CopyFile( lpExistingFile, lpNewFile, FALSE );
  1493. if ( fRet ) {
  1494. ::DeleteFile(lpExistingFile);
  1495. } else {
  1496. IF_DEBUG(ERROR) {
  1497. DBGPRINTF((DBG_CONTEXT,
  1498. "Error %d in CopyFile[%s to %s]\n",
  1499. GetLastError(), lpExistingFile, lpNewFile));
  1500. }
  1501. }
  1502. return(fRet);
  1503. } // W95MoveFileEx
  1504. VOID
  1505. HTTP_REQUEST::CleanupWriteState(
  1506. VOID
  1507. )
  1508. /*++
  1509. Routine Description:
  1510. Cleanup any of the write methods (PUT, DELETE) state when a request is
  1511. completed.
  1512. Arguments:
  1513. None
  1514. Return Value:
  1515. None.
  1516. --*/
  1517. {
  1518. if (_pFileNameLock != NULL)
  1519. {
  1520. _pFileNameLock->Release();
  1521. _pFileNameLock = NULL;
  1522. }
  1523. if (_hTempFileHandle != INVALID_HANDLE_VALUE)
  1524. {
  1525. CleanupTempFile();
  1526. }
  1527. }
  1528. DWORD
  1529. InitializeWriteState(
  1530. VOID
  1531. )
  1532. /*++
  1533. Routine Description:
  1534. Initialize the global state we need in order to do write methods such as
  1535. PUT and DELETE.
  1536. Arguments:
  1537. None
  1538. Return Value:
  1539. NO_ERROR on success, or error code if we fail.
  1540. --*/
  1541. {
  1542. DWORD i;
  1543. for (i = 0; i < MAX_FN_LOCK_BUCKETS; i++)
  1544. {
  1545. InitializeListHead(&FNLockTable[i].ListHead);
  1546. INITIALIZE_CRITICAL_SECTION(&FNLockTable[i].csCritSec);
  1547. }
  1548. if ( !InetIsNtServer( IISGetPlatformType() ) )
  1549. {
  1550. dwPutNumCPU = 1;
  1551. } else
  1552. {
  1553. SYSTEM_INFO si;
  1554. //
  1555. // get the count of CPUs for Thread Tuning.
  1556. //
  1557. GetSystemInfo( &si );
  1558. dwPutNumCPU = si.dwNumberOfProcessors;
  1559. }
  1560. dwPutBlockedCount = 0;
  1561. return NO_ERROR;
  1562. }
  1563. VOID
  1564. TerminateWriteState(
  1565. VOID
  1566. )
  1567. /*++
  1568. Routine Description:
  1569. Terminate the global state we need in order to do write methods such as
  1570. PUT and DELETE.
  1571. Arguments:
  1572. None
  1573. Return Value:
  1574. None
  1575. --*/
  1576. {
  1577. DWORD i;
  1578. for (i = 0; i < MAX_FN_LOCK_BUCKETS; i++)
  1579. {
  1580. DeleteCriticalSection(&FNLockTable[i].csCritSec);
  1581. }
  1582. }
  1583. BOOL
  1584. HTTP_REQUEST::DoOptions(
  1585. BOOL * pfFinished
  1586. )
  1587. /*++
  1588. Routine Description:
  1589. Handle an OPTIONS request. If this is a request for the '*' URL, we'll
  1590. send back information about the global methods we support. If this is
  1591. for a particular URL we'll send back information about what's allowable
  1592. on that URL.
  1593. Arguments:
  1594. pfFinished - Set to TRUE if no further processings is needed for this
  1595. request
  1596. Return Value:
  1597. TRUE if success, else FALSE
  1598. --*/
  1599. {
  1600. CHAR *pszResp;
  1601. CHAR *pszTail;
  1602. DWORD cbRespBytes;
  1603. DWORD cbRespBytesNeeded;
  1604. BOOL fDav = ((W3_IIS_SERVICE *) QueryW3Instance()->m_Service)->FDavDll();
  1605. // Build the basic response header first.
  1606. if (!BuildBaseResponseHeader(QueryRespBuf(),
  1607. pfFinished,
  1608. NULL,
  1609. 0))
  1610. {
  1611. return FALSE;
  1612. }
  1613. pszResp = (CHAR *)QueryRespBufPtr();
  1614. cbRespBytes = strlen(pszResp);
  1615. cbRespBytesNeeded = cbRespBytes +
  1616. sizeof("Public: OPTIONS, TRACE, GET, HEAD, POST, PUT, DELETE\r\n") - 1 +
  1617. sizeof("Content-Length: 0\r\n\r\n");
  1618. if (!QueryRespBuf()->Resize(cbRespBytesNeeded))
  1619. {
  1620. return FALSE;
  1621. }
  1622. pszResp = (CHAR *)QueryRespBufPtr();
  1623. pszTail = pszResp + cbRespBytes;
  1624. APPEND_STRING(pszTail, "Public: OPTIONS, TRACE, GET, HEAD, POST, PUT, DELETE\r\n");
  1625. if (*_strURL.QueryStr() != '*')
  1626. {
  1627. // We have an actual URL. Figure out what methods are applicable to it.
  1628. cbRespBytes = DIFF(pszTail - pszResp);
  1629. cbRespBytesNeeded = cbRespBytes +
  1630. MAX_ALLOW_SIZE;
  1631. if (!QueryRespBuf()->Resize(cbRespBytesNeeded))
  1632. {
  1633. return FALSE;
  1634. }
  1635. pszResp = (CHAR *)QueryRespBufPtr();
  1636. pszTail = pszResp + cbRespBytes;
  1637. pszTail += BuildAllowHeader(_strURL.QueryStr(), pszTail);
  1638. }
  1639. APPEND_STRING(pszTail, "Content-Length: 0\r\n\r\n");
  1640. SetState( HTR_DONE, HT_OK, NO_ERROR );
  1641. if ( !SendHeader( QueryRespBufPtr(),
  1642. (DWORD) -1,
  1643. IO_FLAG_ASYNC,
  1644. pfFinished ))
  1645. {
  1646. return FALSE;
  1647. }
  1648. return TRUE;
  1649. }