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.

1548 lines
40 KiB

  1. /*
  2. * F S L O C K . C P P
  3. *
  4. * Sources file system implementation of DAV-Lock
  5. *
  6. * Copyright 1986-1997 Microsoft Corporation, All Rights Reserved
  7. */
  8. #include "_davfs.h"
  9. #include "_shlkmgr.h"
  10. #include <stdlib.h>
  11. #include <statetok.h>
  12. #include <xlock.h>
  13. // Lock prop support ---------------------------------------------------------
  14. //
  15. // ------------------------------------------------------------------------
  16. //
  17. // DwGetSupportedLockType
  18. //
  19. // Return the supported locktype flags for the resource type.
  20. //$LATER: If/when we have more types than just coll/non-coll, change
  21. //$LATER: the boolean parameter to an enum.
  22. //
  23. DWORD __fastcall DwGetSupportedLockType (RESOURCE_TYPE rt)
  24. {
  25. // DAVFS doesn't support locks on collections.
  26. // On files, DAVFS supports write locks and all lockscope flags.
  27. return (RT_COLLECTION == rt)
  28. ? 0
  29. : GENERIC_WRITE | DAV_LOCKSCOPE_FLAGS;
  30. }
  31. // ------------------------------------------------------------------------
  32. //
  33. // ScSendLockComment
  34. //
  35. // Set lock comment information from the lock object into the
  36. // response.
  37. //
  38. SCODE
  39. ScSendLockComment(LPMETHUTIL pmu,
  40. SNewLockData * pnld,
  41. UINT cchLockToken,
  42. LPCWSTR pwszLockToken)
  43. {
  44. auto_ref_ptr<CXMLEmitter> pemitter;
  45. auto_ref_ptr<CXMLBody> pxb;
  46. SCODE sc = S_OK;
  47. Assert(pmu);
  48. Assert(pnld);
  49. Assert(cchLockToken);
  50. Assert(pwszLockToken);
  51. // Emit the Content-Type: header
  52. //
  53. pmu->SetResponseHeader(gc_szContent_Type, gc_szText_XML);
  54. // Construct the root ('DAV:prop') for the lock response, not chunked
  55. //
  56. pxb.take_ownership (new CXMLBody(pmu, FALSE));
  57. pemitter.take_ownership (new CXMLEmitter(pxb.get()));
  58. sc = pemitter->ScSetRoot (gc_wszProp);
  59. if (FAILED (sc))
  60. {
  61. goto ret;
  62. }
  63. {
  64. CEmitterNode enLockDiscovery;
  65. // Construct the 'DAV:lockdiscovery' node
  66. //
  67. sc = enLockDiscovery.ScConstructNode (*pemitter, pemitter->PxnRoot(), gc_wszLockDiscovery);
  68. if (FAILED (sc))
  69. {
  70. goto ret;
  71. }
  72. // Add the 'DAV:activelock' node for this CLock
  73. //
  74. sc = ScLockDiscoveryFromSNewLockData (pmu,
  75. *pemitter,
  76. enLockDiscovery,
  77. pnld,
  78. pwszLockToken);
  79. if (FAILED (sc))
  80. {
  81. goto ret;
  82. }
  83. }
  84. // Emit the XML body
  85. //
  86. pemitter->Done();
  87. ret:
  88. return sc;
  89. }
  90. // ------------------------------------------------------------------------
  91. // LOCK helper functions
  92. //
  93. // ------------------------------------------------------------------------
  94. //
  95. // HrProcessLockRefresh
  96. //
  97. // pmu -- MethUtil access
  98. // pszLockToken -- header containing the locktoken to refresh
  99. // puiErrorDetail -- error detail string id, passed out on error
  100. // pnld -- pass back the lock attributes
  101. // cchBufferLen -- buffer length for the lock token
  102. // rgwszLockToken -- buffer for the lock token
  103. // pcchLockToken -- pointer that will receive the count of characters written
  104. // for the lock token
  105. //
  106. // NOTE: This function still only can handle refreshing ONE locktoken.
  107. //$REVIEW: Do we need to fix this?
  108. //
  109. HRESULT HrProcessLockRefresh (LPMETHUTIL pmu,
  110. LPCWSTR pwszLockToken,
  111. UINT * puiErrorDetail,
  112. SNewLockData * pnld,
  113. UINT cchBufferLen,
  114. LPWSTR rgwszLockToken,
  115. UINT * pcchLockToken)
  116. {
  117. HRESULT hr = S_OK;
  118. DWORD dwTimeout = 0;
  119. LARGE_INTEGER liLockID;
  120. LPCWSTR pwszPath = pmu->LpwszPathTranslated();
  121. SLockHandleData lhd;
  122. Assert(pmu);
  123. Assert(pwszLockToken);
  124. Assert(puiErrorDetail);
  125. Assert(pnld);
  126. Assert(rgwszLockToken);
  127. Assert(pcchLockToken);
  128. // Get a lock timeout, if they specified one.
  129. //
  130. if (!FGetLockTimeout (pmu, &dwTimeout))
  131. {
  132. DebugTrace ("DavFS: LOCK fails with improper Timeout header\n");
  133. hr = E_DAV_INVALID_HEADER; //HSC_BAD_REQUEST;
  134. *puiErrorDetail = IDS_BR_TIMEOUT_SYNTAX;
  135. goto ret;
  136. }
  137. // Here's the real work.
  138. // Get the lock from the cache. If this object is not in our cache,
  139. // or the lockid doesn't match, don't let them refresh the lock.
  140. //$REVIEW: Should this be two distinct error codes?
  141. //
  142. // Feed the Lock-Token header string into a parser object.
  143. // Then get the lockid from the parser object.
  144. //
  145. {
  146. CParseLockTokenHeader lth(pmu, pwszLockToken);
  147. // If there is more than one token, bad request.
  148. //
  149. if (!lth.FOneToken())
  150. {
  151. hr = HRESULT_FROM_WIN32 (ERROR_BAD_FORMAT); //HSC_BAD_REQUEST;
  152. *puiErrorDetail = IDS_BR_MULTIPLE_LOCKTOKENS;
  153. goto ret;
  154. }
  155. lth.SetPaths (pwszPath, NULL);
  156. // 0 means match all access.
  157. //
  158. hr = lth.HrGetLockIdForPath (pwszPath, 0, &liLockID);
  159. if (FAILED (hr))
  160. {
  161. DavTrace ("DavFS: HrGetLockIdForPath could not find the path.\n");
  162. goto ret;
  163. }
  164. }
  165. // Fetch the lock from the cache. (This call updates the timestamp.)
  166. //
  167. hr = CSharedLockMgr::Instance().HrGetLockData(liLockID,
  168. pmu->HitUser(),
  169. pwszPath,
  170. dwTimeout,
  171. pnld,
  172. &lhd,
  173. cchBufferLen,
  174. rgwszLockToken,
  175. pcchLockToken);
  176. if (FAILED(hr))
  177. {
  178. DavTrace ("DavFS: Refreshing a non-locked resource constitutes an unsatisfiable request.\n");
  179. // If it's an access check failed, leave the return code unchanged.
  180. // If the buffer was not sufficient, leave the return code unchanged.
  181. // Otherwise, give "can't satisfy request" (412 Precondition Failed).
  182. //
  183. if (HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED) != hr &&
  184. HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) != hr)
  185. hr = E_DAV_CANT_SATISFY_LOCK_REQUEST;
  186. *puiErrorDetail = IDS_BR_LOCKTOKEN_INVALID;
  187. goto ret;
  188. }
  189. ret:
  190. return hr;
  191. }
  192. // ========================================================================
  193. //
  194. // CLockRequest
  195. //
  196. // Used by ProcessLockRequest() below to manage possible asynchronous
  197. // processing of a lock request in light of the fact that one cannot
  198. // determine whether a request body is so large that read operations
  199. // on it execute asynchronously.
  200. //
  201. class CLockRequest :
  202. public CMTRefCounted,
  203. private IAsyncIStreamObserver
  204. {
  205. // Reference to the CMethUtil
  206. //
  207. auto_ref_ptr<CMethUtil> m_pmu;
  208. // Cached translated path
  209. //
  210. LPCWSTR m_pwszPath;
  211. // File backing the lock we create
  212. //
  213. auto_ref_handle m_hfile;
  214. // The lock XML node factory
  215. //
  216. auto_ref_ptr<CNFLock> m_pnfl;
  217. // The request body stream
  218. //
  219. auto_ref_ptr<IStream> m_pstmRequest;
  220. // The XML parser used to parse the request body using
  221. // the node factory above.
  222. //
  223. auto_ref_ptr<IXMLParser> m_pxprs;
  224. // Flag set to TRUE if we created the file as a result of creating
  225. // the lock. Used to indicate the status code to return as well
  226. // as to know whether to delete the file on error.
  227. //
  228. BOOL m_fCreatedFile;
  229. // IAsyncIStreamObserver
  230. //
  231. VOID AsyncIOComplete();
  232. // State functions
  233. //
  234. VOID ParseBody();
  235. VOID DoLock();
  236. VOID SendResponse( SCODE sc, UINT uiErrorDetail = 0 );
  237. // NOT IMPLEMENTED
  238. //
  239. CLockRequest& operator= (const CLockRequest&);
  240. CLockRequest (const CLockRequest&);
  241. public:
  242. // CREATORS
  243. //
  244. CLockRequest (CMethUtil * pmu) :
  245. m_pmu(pmu),
  246. m_pwszPath(m_pmu->LpwszPathTranslated()),
  247. m_fCreatedFile(FALSE)
  248. {
  249. }
  250. ~CLockRequest();
  251. // MANIPULATORS
  252. //
  253. VOID Execute();
  254. };
  255. // ------------------------------------------------------------------------
  256. //
  257. // CLockRequest::~CLockRequest
  258. //
  259. CLockRequest::~CLockRequest()
  260. {
  261. // We have cleaned up the old handle in CLockRequest::SendResponse()
  262. // The following path could be executed only in exception stack rewinding
  263. //
  264. if ( m_hfile.get() && m_fCreatedFile )
  265. {
  266. // WARNING: the safe_revert class should only be
  267. // used in very selective situations. It is not
  268. // a "quick way to get around" impersonation.
  269. //
  270. safe_revert sr(m_pmu->HitUser());
  271. m_hfile.clear();
  272. //$REVIEW Note if exception happened after the lock handle is duplicated,
  273. //$REVIEW then we won't be able to delete the file, but this is very
  274. //$REVIEW rare. not sure if we ever want to handle this.
  275. //
  276. DavDeleteFile (m_pwszPath);
  277. DebugTrace ("Dav: deleting partial lock (%ld)\n", GetLastError());
  278. }
  279. }
  280. // ------------------------------------------------------------------------
  281. //
  282. // CLockRequest::Execute
  283. //
  284. VOID
  285. CLockRequest::Execute()
  286. {
  287. //
  288. // First off, tell the pmu that we want to defer the response.
  289. // Even if we send it synchronously (i.e. due to an error in
  290. // this function), we still want to use the same mechanism that
  291. // we would use for async.
  292. //
  293. m_pmu->DeferResponse();
  294. // The client must not submit a depth header with any value
  295. // but 0 or Infinity.
  296. // NOTE: Currently, DAVFS cannot lock collections, so the
  297. // depth header doesn't change anything. So, we don't change
  298. // our processing at all for the Depth: infinity case.
  299. //
  300. //$LATER: If we do want to support locking collections,
  301. //$LATER: need to set DAV_RECURSIVE_LOCK on depth infinity.
  302. //
  303. LONG lDepth = m_pmu->LDepth(DEPTH_ZERO);
  304. if ((DEPTH_ZERO != lDepth) && (DEPTH_INFINITY != lDepth))
  305. {
  306. // If the header is anything besides 0 or infinity, bad request.
  307. //
  308. SendResponse(E_DAV_INVALID_HEADER);
  309. return;
  310. }
  311. // Instantiate the XML parser
  312. //
  313. m_pnfl.take_ownership(new CNFLock);
  314. m_pstmRequest.take_ownership(m_pmu->GetRequestBodyIStream(*this));
  315. SCODE sc = ScNewXMLParser( m_pnfl.get(),
  316. m_pstmRequest.get(),
  317. m_pxprs.load() );
  318. if (FAILED(sc))
  319. {
  320. DebugTrace( "CLockRequest::Execute() - ScNewXMLParser() failed (0x%08lX)\n", sc );
  321. SendResponse(sc);
  322. return;
  323. }
  324. // Parse the body
  325. //
  326. ParseBody();
  327. }
  328. // ------------------------------------------------------------------------
  329. //
  330. // CLockRequest::ParseBody()
  331. //
  332. VOID
  333. CLockRequest::ParseBody()
  334. {
  335. SCODE sc;
  336. Assert( m_pxprs.get() );
  337. Assert( m_pnfl.get() );
  338. Assert( m_pstmRequest.get() );
  339. // Parse XML from the request body stream.
  340. //
  341. // Add a ref for the following async operation.
  342. // Use auto_ref_ptr rather than AddRef() for exception safety.
  343. //
  344. auto_ref_ptr<CLockRequest> pRef(this);
  345. sc = ScParseXML (m_pxprs.get(), m_pnfl.get());
  346. if ( SUCCEEDED(sc) )
  347. {
  348. Assert( S_OK == sc || S_FALSE == sc );
  349. DoLock();
  350. }
  351. else if ( E_PENDING == sc )
  352. {
  353. //
  354. // The operation is pending -- AsyncIOComplete() will take ownership
  355. // ownership of the reference when it is called.
  356. //
  357. pRef.relinquish();
  358. }
  359. else
  360. {
  361. DebugTrace( "CLockRequest::ParseBody() - ScParseXML() failed (0x%08lX)\n", sc );
  362. SendResponse( sc );
  363. return;
  364. }
  365. }
  366. // ------------------------------------------------------------------------
  367. //
  368. // CLockRequest::AsyncIOComplete()
  369. //
  370. // Called on completion of an async operation on our stream to
  371. // resume parsing XML from that stream.
  372. //
  373. VOID
  374. CLockRequest::AsyncIOComplete()
  375. {
  376. // Take ownership of the reference added above in ParseBody()
  377. //
  378. auto_ref_ptr<CLockRequest> pRef;
  379. pRef.take_ownership(this);
  380. // Resume parsing
  381. //
  382. ParseBody();
  383. }
  384. // ------------------------------------------------------------------------
  385. //
  386. // CLockRequest::DoLock()
  387. //
  388. VOID
  389. CLockRequest::DoLock()
  390. {
  391. DWORD dw;
  392. DWORD dwAccess = 0;
  393. DWORD dwLockType;
  394. DWORD dwLockScope;
  395. DWORD dwSharing;
  396. DWORD dwSecondsTimeout;
  397. LPCWSTR pwszURI = m_pmu->LpwszRequestUrl();
  398. SNewLockData nld;
  399. WCHAR rgwszLockToken[MAX_LOCKTOKEN_LENGTH];
  400. UINT cchLockToken = CElems(rgwszLockToken);
  401. SCODE sc = S_OK;
  402. // Pull lock flags out of the xml parser.
  403. // NOTE: I'm doing special stuff here, rather than inside the xml parser.
  404. // Our write locks get read access also -- I'm relying on all methods
  405. // that USE a lock handle to check the metabase flags!!!
  406. //
  407. // Rollback is not supported here.
  408. // If we see this, fail explicitly.
  409. //
  410. dwLockType = m_pnfl->DwGetLockRollback();
  411. if (dwLockType)
  412. {
  413. SendResponse(E_DAV_CANT_SATISFY_LOCK_REQUEST); //HSC_PRECONDITION_FAILED;
  414. return;
  415. }
  416. // If the parser gives us a non-supported locktype (like rollback!)
  417. // tell the user it's not supported.
  418. //
  419. dwLockType = m_pnfl->DwGetLockType();
  420. if (GENERIC_WRITE != dwLockType &&
  421. GENERIC_READ != dwLockType)
  422. {
  423. SendResponse(E_DAV_CANT_SATISFY_LOCK_REQUEST); //HSC_PRECONDITION_FAILED;
  424. return;
  425. }
  426. Assert (GENERIC_WRITE == dwLockType ||
  427. GENERIC_READ == dwLockType);
  428. // Since we KNOW (see above assumption) that our locktype is WRITE,
  429. // we also KNOW that our access should be read+write.
  430. //
  431. dwAccess = GENERIC_READ | GENERIC_WRITE;
  432. #ifdef DBG
  433. // This is needed for BeckyAn to test that our infrastructure still
  434. // handles setting a read-lock. DBG ONLY.
  435. dwAccess = (dwLockType & GENERIC_WRITE)
  436. ? GENERIC_READ | GENERIC_WRITE
  437. : GENERIC_READ;
  438. #endif // DBG
  439. // Get our lockscope from the parser.
  440. //
  441. dwLockScope = m_pnfl->DwGetLockScope();
  442. if (DAV_SHARED_LOCK != dwLockScope &&
  443. DAV_EXCLUSIVE_LOCK != dwLockScope)
  444. {
  445. SendResponse(E_DAV_CANT_SATISFY_LOCK_REQUEST); //HSC_PRECONDITION_FAILED;
  446. return;
  447. }
  448. if (DAV_SHARED_LOCK == dwLockScope)
  449. {
  450. // Shared lock -- turn on all sharing flags.
  451. dwSharing = FILE_SHARE_READ | FILE_SHARE_WRITE;
  452. }
  453. else
  454. {
  455. // Our lock type is write (see above assumption). Set the sharing
  456. // flags correctly.
  457. //$LATER: If we have a different lock type later, fix these flags!
  458. //
  459. dwSharing = FILE_SHARE_READ;
  460. #ifdef DBG
  461. // This is needed for BeckyAn to test that our infrastructure still
  462. // handles setting a read-lock. DBG ONLY.
  463. dwSharing = 0;
  464. if (!(dwLockType & GENERIC_READ))
  465. {
  466. dwSharing |= FILE_SHARE_READ;
  467. }
  468. if (!(dwLockType & GENERIC_WRITE))
  469. {
  470. dwSharing |= FILE_SHARE_WRITE;
  471. }
  472. #endif // DBG
  473. }
  474. Assert(S_OK == sc);
  475. AssertSz (dwAccess, "Strange. Lock requested with NO access (no locktypes?).");
  476. // Check our LOCKTYPE against the metabase access rights.
  477. // NOTE: I'm not checking our ACCESS flags against the metabase
  478. // because our access flags don't come directly from the caller's requested
  479. // access. This check just makes sure that the caller hasn't asked for
  480. // anything he can't have.
  481. // NOTE: I don't listen for metabase changes, so if I get a lock with
  482. // more/less access than the user, I don't/can't change it for a
  483. // metabase update.
  484. // NOTE: This works IF we assiduously check the metabase flags on
  485. // ALL other methds (which we currenly do). If that checking ever
  486. // goes missing, and we grab a lock handle that has more access than
  487. // the caller rightfully is allowed, we have a security hole.
  488. // (So keep checking metabase flags on all methods!)
  489. //
  490. dw = (dwLockType & GENERIC_READ) ? MD_ACCESS_READ : 0;
  491. dw |= (dwLockType & GENERIC_WRITE) ? MD_ACCESS_WRITE : 0;
  492. sc = m_pmu->ScIISAccess (pwszURI, dw);
  493. if (FAILED (sc))
  494. {
  495. DebugTrace( "CLockRequest::DoLock() - IMethUtil::ScIISAccess failed (0x%08lX)\n", sc );
  496. SendResponse(sc);
  497. return;
  498. }
  499. // Check for user-specified timeout header.
  500. // (The timeout header is optional, so it's okay to have no timeout
  501. // header, but syntax errors in the timeout header are NOT okay.)
  502. // If no timeout header is present, dw will come back ZERO.
  503. //
  504. if (!FGetLockTimeout (m_pmu.get(), &dwSecondsTimeout))
  505. {
  506. DebugTrace ("DavFS: LOCK fails with improper Time-Out header\n");
  507. SendResponse(HRESULT_FROM_WIN32 (ERROR_BAD_FORMAT), //HSC_BAD_REQUEST;
  508. IDS_BR_TIMEOUT_SYNTAX);
  509. return;
  510. }
  511. try_open_resource:
  512. // And now lock the resource.
  513. // NOTE: On WRITE operations, if the file doesn't exist, CREATE it here
  514. // (OPEN_ALWAYS, not OPEN_EXISTING) and change the hsc below!
  515. // NOTE: We NEVER allow delete access (no FILE_SHARE_DELETE).
  516. // NOTE: All our reads/writes will be async, so open the file overlapped.
  517. // NOTE: We will be reading/writing the whole file usually, so use SEQUENTIAL_SCAN.
  518. //
  519. if (!m_hfile.FCreate(
  520. DavCreateFile (m_pwszPath,
  521. dwAccess,
  522. dwSharing,
  523. NULL,
  524. (dwAccess & GENERIC_WRITE)
  525. ? OPEN_ALWAYS
  526. : OPEN_EXISTING,
  527. FILE_ATTRIBUTE_NORMAL |
  528. FILE_FLAG_OVERLAPPED |
  529. FILE_FLAG_SEQUENTIAL_SCAN,
  530. NULL)))
  531. {
  532. sc = HRESULT_FROM_WIN32 (GetLastError());
  533. // Special check for NEW-STYLE write locks.
  534. // We are asking for rw access when we get a write lock.
  535. // IF we don't have read access (in the ACLs) for the resource,
  536. // we will fail here with ERROR_ACCESS_DENIED.
  537. // Catch this case and try again with just w access!
  538. //
  539. if (ERROR_ACCESS_DENIED == GetLastError() &&
  540. dwAccess == (GENERIC_READ | GENERIC_WRITE) &&
  541. dwLockType == GENERIC_WRITE)
  542. {
  543. // Try again.
  544. dwAccess = GENERIC_WRITE;
  545. goto try_open_resource;
  546. }
  547. // Special work for 416 Locked responses -- fetch the
  548. // comment & set that as the response body.
  549. // (You'll hit here if someone else already has this file locked!)
  550. //
  551. if (FLockViolation (m_pmu.get(), sc, m_pwszPath, dwLockType))
  552. {
  553. sc = HRESULT_FROM_WIN32 (ERROR_SHARING_VIOLATION); //HSC_LOCKED;
  554. }
  555. DavTrace ("Dav: unable to lock resource on LOCK method\n");
  556. SendResponse(sc);
  557. return;
  558. }
  559. // If we created the file (only for write locks),
  560. // change the default error code to say so.
  561. //
  562. if (dwAccess & GENERIC_WRITE &&
  563. GetLastError() != ERROR_ALREADY_EXISTS)
  564. {
  565. // Emit the location
  566. //
  567. m_pmu->EmitLocation (gc_szLocation, pwszURI, FALSE);
  568. m_fCreatedFile = TRUE;
  569. }
  570. // Ask the shared lock manager to create a new shared lock token
  571. //
  572. nld.m_dwAccess = dwAccess;
  573. nld.m_dwLockType = dwLockType;
  574. nld.m_dwLockScope = dwLockScope;
  575. nld.m_dwSecondsTimeout = dwSecondsTimeout;
  576. nld.m_pwszResourceString = const_cast<LPWSTR>(m_pwszPath);
  577. nld.m_pwszOwnerComment = const_cast<LPWSTR>(m_pnfl->PwszLockOwner());
  578. sc = CSharedLockMgr::Instance().HrGetNewLockData(m_hfile.get(),
  579. m_pmu->HitUser(),
  580. &nld,
  581. cchLockToken,
  582. rgwszLockToken,
  583. &cchLockToken);
  584. if (FAILED(sc))
  585. {
  586. DebugTrace ("DavFS: CLockRequest::DoLock() - CSharedLockMgr::Instance().HrGetNewLockData() failed 0x%08lX\n", sc);
  587. SendResponse(E_ABORT); //HSC_INTERNAL_SERVER_ERROR;
  588. return;
  589. }
  590. // Emit the Lock-Token: header
  591. //
  592. Assert(cchLockToken);
  593. Assert(L'\0' == rgwszLockToken[cchLockToken - 1]);
  594. m_pmu->SetResponseHeader (gc_szLockTokenHeader, rgwszLockToken);
  595. // Generate a valid lock response
  596. //
  597. sc = ScSendLockComment(m_pmu.get(),
  598. &nld,
  599. cchLockToken,
  600. rgwszLockToken);
  601. if (FAILED(sc))
  602. {
  603. DebugTrace ("DavFS: CLockRequest::DoLock() ScSendLockComment () failed 0x%08lX\n", sc);
  604. SendResponse(E_ABORT);
  605. return;
  606. }
  607. Assert(S_OK == sc);
  608. SendResponse(m_fCreatedFile ? W_DAV_CREATED : S_OK);
  609. }
  610. // ------------------------------------------------------------------------
  611. //
  612. // CLockRequest::SendResponse()
  613. //
  614. // Set the response code and send the response.
  615. //
  616. VOID
  617. CLockRequest::SendResponse( SCODE sc, UINT uiErrorDetail )
  618. {
  619. PutTrace( "DAV: TID %3d: 0x%08lX: CLockRequest::SendResponse() called\n", GetCurrentThreadId(), this );
  620. // We must close the file handle before we send any respose back
  621. // to client. Otherwise, if the lcok failed, client may send another
  622. // request immediately and expect the resource is not locked.
  623. //
  624. // Even in the case the lock succeeded, it's still cleaner we release
  625. // the file handle here. Think about the following sequence:
  626. // LOCK f1, UNLOCK f1, PUT f1;
  627. // the last PUT could fail if the first LOCK reqeust hangs a little longer
  628. // after it sends the response.
  629. //
  630. // Keep in mind that if locked succeeded, the handle is already duplicated
  631. // in davcdata.exe. so releasing the file handle here doesn't really 'unlock'
  632. // file. the file is still locked.
  633. //
  634. m_hfile.clear();
  635. if (FAILED(sc) && m_fCreatedFile)
  636. {
  637. // WARNING: the safe_revert class should only be
  638. // used in very selective situations. It is not
  639. // a "quick way to get around" impersonation.
  640. //
  641. safe_revert sr(m_pmu->HitUser());
  642. // If we created the new file, we much delete it. Note that
  643. // DoLock() would never fail after it duplicate the filehandle
  644. // to davcdata, so we should be able to delete the file successfully
  645. //
  646. DavDeleteFile (m_pwszPath);
  647. DebugTrace ("Dav: deleting partial lock (%ld)\n", GetLastError());
  648. // Now that we have cleaned up. reset m_fCreateFile so that we can
  649. // skip the exception-safe code in ~CLockRequest()
  650. //
  651. m_fCreatedFile = FALSE;
  652. }
  653. // Set the response code and go
  654. //
  655. m_pmu->SetResponseCode (HscFromHresult(sc), NULL, uiErrorDetail);
  656. m_pmu->SendCompleteResponse();
  657. }
  658. //
  659. // ProcessLockRequest
  660. //
  661. // pmu -- MethUtil access
  662. //
  663. VOID
  664. ProcessLockRequest (LPMETHUTIL pmu)
  665. {
  666. auto_ref_ptr<CLockRequest> pRequest(new CLockRequest (pmu));
  667. pRequest->Execute();
  668. }
  669. // DAV-Lock Implementation ---------------------------------------------------
  670. //
  671. /*
  672. * DAVLock()
  673. *
  674. * Purpose:
  675. *
  676. * Win32 file system implementation of the DAV LOCK method. The
  677. * LOCK method results in the locking of a resource for a specific
  678. * type of access. The response tells whether the lock was granted
  679. * or not. If the lock was granted, it provides a lockid to be used
  680. * in future methods (including UNLOCK) on the resource.
  681. *
  682. * Parameters:
  683. *
  684. * pmu [in] pointer to the method utility object
  685. *
  686. * Notes:
  687. *
  688. * In the file system implementation, the LOCK method maps directly
  689. * to the Win32 CreateFile() method with special access flags.
  690. */
  691. void
  692. DAVLock (LPMETHUTIL pmu)
  693. {
  694. SCODE sc = S_OK;
  695. UINT uiErrorDetail = 0;
  696. LPCWSTR pwszLockToken;
  697. CResourceInfo cri;
  698. // Do ISAPI application and IIS access bits checking
  699. //
  700. sc = pmu->ScIISCheck (pmu->LpwszRequestUrl());
  701. if (FAILED(sc))
  702. {
  703. // Either the request has been forwarded, or some bad error occurred.
  704. // In either case, quit here and map the error!
  705. //
  706. goto ret;
  707. }
  708. // Process based on resource info
  709. //
  710. sc = cri.ScGetResourceInfo (pmu->LpwszPathTranslated());
  711. if (!FAILED (sc))
  712. {
  713. // Check to see if the resource is a DIRECTORY.
  714. // DAVFS can lock non-existant resources, but can't lock directories.
  715. //
  716. if (cri.FCollection())
  717. {
  718. // The resource is a directory.
  719. //
  720. DavTrace ("Dav: directory resource specified for LOCK\n");
  721. sc = E_DAV_PROTECTED_ENTITY;
  722. uiErrorDetail = IDS_BR_NO_COLL_LOCK;
  723. goto ret;
  724. }
  725. // Ensure the URI and resource match
  726. //
  727. sc = ScCheckForLocationCorrectness (pmu, cri, NO_REDIRECT);
  728. if (FAILED(sc))
  729. {
  730. goto ret;
  731. }
  732. // Check against the "if-xxx" headers
  733. //
  734. sc = ScCheckIfHeaders (pmu, cri.PftLastModified(), FALSE);
  735. }
  736. else
  737. {
  738. sc = ScCheckIfHeaders (pmu, pmu->LpwszPathTranslated(), FALSE);
  739. }
  740. if (FAILED(sc))
  741. {
  742. DebugTrace ("DavFS: If-xxx checking failed.\n");
  743. goto ret;
  744. }
  745. // Check If-State-Match headers.
  746. //
  747. sc = HrCheckStateHeaders (pmu, pmu->LpwszPathTranslated(), FALSE);
  748. if (FAILED(sc))
  749. {
  750. DebugTrace ("DavFS: If-State checking failed.\n");
  751. goto ret;
  752. }
  753. // If they pass in a lock token *AND* a lockinfo header, it's a
  754. // bad request. (Lock upgrading is NOT allowed.)
  755. // Just the lock token (no lockinfo) is a lock refresh request.
  756. //
  757. pwszLockToken = pmu->LpwszGetRequestHeader (gc_szLockToken, TRUE);
  758. if (pwszLockToken)
  759. {
  760. // Lock-Token header present -- REFRESH request.
  761. //
  762. LPCWSTR pwsz;
  763. auto_co_task_mem<WCHAR> a_pwszResourceString;
  764. auto_co_task_mem<WCHAR> a_pwszOwnerComment;
  765. SNewLockData nld;
  766. WCHAR rgwszLockToken[MAX_LOCKTOKEN_LENGTH];
  767. UINT cchLockToken = CElems(rgwszLockToken);
  768. // If we have a content-type, it better be text/xml.
  769. //
  770. pwsz = pmu->LpwszGetRequestHeader (gc_szContent_Type, FALSE);
  771. if (pwsz)
  772. {
  773. // If it's not text/xml....
  774. //
  775. if (_wcsicmp(pwsz, gc_wszText_XML) && _wcsicmp(pwsz, gc_wszApplication_XML))
  776. {
  777. // Invalid request -- has some other kind of request body
  778. //
  779. DebugTrace ("DavFS: Invalid body found on LOCK refresh method.\n");
  780. sc = E_DAV_UNKNOWN_CONTENT;
  781. uiErrorDetail = IDS_BR_LOCK_BODY_TYPE;
  782. goto ret;
  783. }
  784. }
  785. // If we have a content length at all, it had better be zero.
  786. // (Lock refreshes can't have a body!)
  787. //
  788. pwsz = pmu->LpwszGetRequestHeader (gc_szContent_Length, FALSE);
  789. if (pwsz)
  790. {
  791. // If the Content-Length is anything other than zero, bad request.
  792. //
  793. if (_wcsicmp(pwsz, gc_wsz0))
  794. {
  795. // Invalid request -- has some other kind of request body
  796. //
  797. DebugTrace ("DavFS: Invalid body found on LOCK refresh method.\n");
  798. sc = E_DAV_INVALID_HEADER; //HSC_BAD_REQUEST;
  799. uiErrorDetail = IDS_BR_LOCK_BODY_SYNTAX;
  800. goto ret;
  801. }
  802. }
  803. // Process the refresh.
  804. //
  805. sc = HrProcessLockRefresh (pmu,
  806. pwszLockToken,
  807. &uiErrorDetail,
  808. &nld,
  809. cchLockToken,
  810. rgwszLockToken,
  811. &cchLockToken);
  812. if (FAILED(sc))
  813. {
  814. // Make sure we did not get insufficient buffer errors as the
  815. // buffer we passed was sufficient.
  816. //
  817. Assert(HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) != sc);
  818. goto ret;
  819. }
  820. // Take ownership of the memory allocated
  821. //
  822. a_pwszResourceString.take_ownership(nld.m_pwszResourceString);
  823. a_pwszOwnerComment.take_ownership(nld.m_pwszOwnerComment);
  824. // Send back the lock comment.
  825. // Tell the lock to generate XML lockdiscovery prop data
  826. // and emit it to the response body.
  827. //
  828. sc = ScSendLockComment(pmu,
  829. &nld,
  830. cchLockToken,
  831. rgwszLockToken);
  832. if (FAILED(sc))
  833. {
  834. goto ret;
  835. }
  836. }
  837. else
  838. {
  839. // No Lock-Token header present -- LOCK request.
  840. //
  841. // Go get this lock. All error handling and response
  842. // generation is done inside ProcessLockRequest()
  843. // so there's nothing more to do here once we call it.
  844. //
  845. ProcessLockRequest (pmu);
  846. return;
  847. }
  848. ret:
  849. pmu->SetResponseCode (HscFromHresult(sc), NULL, uiErrorDetail, CSEFromHresult(sc));
  850. }
  851. /*
  852. * DAVUnlock()
  853. *
  854. * Purpose:
  855. *
  856. * Win32 file system implementation of the DAV UNLOCK method. The
  857. * UNLOCK method results in the moving of a resource from one location
  858. * to another. The response is used to indicate the success of the
  859. * call.
  860. *
  861. * Parameters:
  862. *
  863. * pmu [in] pointer to the method utility object
  864. *
  865. * Notes:
  866. *
  867. * In the file system implementation, the UNLOCK method maps directly
  868. * to the Win32 CloseHandle() method.
  869. */
  870. void
  871. DAVUnlock (LPMETHUTIL pmu)
  872. {
  873. LPCWSTR pwszPath = pmu->LpwszPathTranslated();
  874. LPCWSTR pwsz;
  875. LARGE_INTEGER liLockID;
  876. UINT uiErrorDetail = 0;
  877. HRESULT hr;
  878. CResourceInfo cri;
  879. // Do ISAPI application and IIS access bits checking
  880. //
  881. hr = pmu->ScIISCheck (pmu->LpwszRequestUrl());
  882. if (FAILED(hr))
  883. {
  884. // Either the request has been forwarded, or some bad error occurred.
  885. // In either case, quit here and map the error!
  886. //
  887. goto ret;
  888. }
  889. // Check what kind of lock is requested.
  890. // (No lock-info header means this request is invalid.)
  891. //
  892. pwsz = pmu->LpwszGetRequestHeader (gc_szLockTokenHeader, FALSE);
  893. if (!pwsz)
  894. {
  895. DebugTrace ("DavFS: UNLOCK fails without Lock-Token.\n");
  896. hr = E_INVALIDARG;
  897. uiErrorDetail = IDS_BR_LOCKTOKEN_MISSING;
  898. goto ret;
  899. }
  900. hr = HrCheckStateHeaders (pmu, // methutil
  901. pwszPath, // path
  902. FALSE); // fGetMeth
  903. if (FAILED(hr))
  904. {
  905. DebugTrace ("DavFS: If-State checking failed.\n");
  906. goto ret;
  907. }
  908. #ifdef NEVER
  909. //$NEVER
  910. // Old code -- the common functions use here have changed to expect
  911. // If: header syntax. We can't use this anymore. It gives errors because
  912. // the Lock-Token header doesn't have parens around the locktokens.
  913. //$NEVER: Remove this after Joel has a chance to test stuff!
  914. //
  915. // Feed the Lock-Token header string into a parser object.
  916. // Then get the lockid from the parser object.
  917. //
  918. {
  919. CParseLockTokenHeader lth(pmu, pwsz);
  920. // If there is more than one token, bad request.
  921. //
  922. if (!lth.FOneToken())
  923. {
  924. DavTrace ("DavFS: More than one token in DAVUnlock.\n");
  925. hr = E_DAV_INVALID_HEADER;
  926. uiErrorDetail = IDS_BR_MULTIPLE_LOCKTOKENS;
  927. goto ret;
  928. }
  929. lth.SetPaths (pwszPath, NULL);
  930. hr = lth.HrGetLockIdForPath (pwszPath, 0, &i64LockId);
  931. if (FAILED(hr))
  932. {
  933. DavTrace ("Dav: Failure in DAVUnlock on davfs.\n");
  934. uiErrorDetail = IDS_BR_LOCKTOKEN_SYNTAX;
  935. goto ret;
  936. }
  937. }
  938. #endif // NEVER
  939. // Call to fetch the lockid from the Lock-Token header.
  940. //
  941. hr = HrLockIdFromString(pmu, pwsz, &liLockID);
  942. if (FAILED(hr))
  943. {
  944. DavTrace ("DavFS: Failed to fetch locktoken in UNLOCK.\n");
  945. // They have a well-formed request, but their locktoken is not right.
  946. // Tell the caller we can't satisfy this (un)lock request. (412 Precondition Failed)
  947. //
  948. hr = E_DAV_CANT_SATISFY_LOCK_REQUEST;
  949. goto ret;
  950. }
  951. // Fetch the lock from the cache. (This call updates the timestamp.)
  952. // Get the lock from the cache. If this object is not in our cache,
  953. // or the lockid doesn't match, don't let them unlock the resource.
  954. //$REVIEW: Should this be two distinct error codes?
  955. //
  956. hr = CSharedLockMgr::Instance().HrCheckLockID(liLockID,
  957. pmu->HitUser(),
  958. pwszPath);
  959. if (FAILED(hr))
  960. {
  961. DavTrace ("DavFS: Unlocking a non-locked resource constitutes an unsatisfiable request.\n");
  962. // If it's an access violation, leave the return code unchanged.
  963. // Otherwise, give "can't satisfy request" (412 Precondition Failed).
  964. //
  965. if (HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED) != hr)
  966. hr = E_DAV_CANT_SATISFY_LOCK_REQUEST;
  967. uiErrorDetail = IDS_BR_LOCKTOKEN_INVALID;
  968. goto ret;
  969. }
  970. // This method is gated by the "if-xxx" headers
  971. //
  972. hr = cri.ScGetResourceInfo (pwszPath);
  973. if (FAILED (hr))
  974. {
  975. goto ret;
  976. }
  977. hr = ScCheckIfHeaders (pmu, cri.PftLastModified(), FALSE);
  978. if (FAILED (hr))
  979. {
  980. goto ret;
  981. }
  982. // Ensure the URI and resource match
  983. //
  984. (void) ScCheckForLocationCorrectness (pmu, cri, NO_REDIRECT);
  985. // Delete the lock from the cache.
  986. //
  987. hr = CSharedLockMgr::Instance().HrDeleteLock(pmu->HitUser(),
  988. liLockID);
  989. if (FAILED(hr))
  990. {
  991. goto ret;
  992. }
  993. ret:
  994. if (!FAILED (hr))
  995. {
  996. hr = W_DAV_NO_CONTENT;
  997. }
  998. // Setup the response
  999. //
  1000. pmu->SetResponseCode (HscFromHresult(hr), NULL, uiErrorDetail, CSEFromHresult(hr));
  1001. }
  1002. // ------------------------------------------------------------------------
  1003. //
  1004. // Utility functions for other FS methods to use when accessing locks.
  1005. //
  1006. // ------------------------------------------------------------------------
  1007. // ------------------------------------------------------------------------
  1008. //
  1009. // FGetLockHandleFromId
  1010. //
  1011. BOOL
  1012. FGetLockHandleFromId (LPMETHUTIL pmu, LARGE_INTEGER liLockID,
  1013. LPCWSTR pwszPath, DWORD dwAccess,
  1014. auto_ref_handle * phandle)
  1015. {
  1016. HRESULT hr = S_OK;
  1017. auto_co_task_mem<WCHAR> a_pwszResourceString;
  1018. auto_co_task_mem<WCHAR> a_pwszOwnerComment;
  1019. SNewLockData nld;
  1020. SLockHandleData lhd;
  1021. HANDLE hTemp = NULL;
  1022. // These are unused. Oplimize the interface not to ask for them later
  1023. //
  1024. WCHAR rgwszLockToken[MAX_LOCKTOKEN_LENGTH];
  1025. UINT cchLockToken = CElems(rgwszLockToken);
  1026. Assert (pmu);
  1027. Assert (pwszPath);
  1028. Assert (!IsBadWritePtr(phandle, sizeof(auto_ref_handle)));
  1029. // Fetch the lock from the cache. (This call updates the timestamp.)
  1030. //
  1031. hr = CSharedLockMgr::Instance().HrGetLockData(liLockID,
  1032. pmu->HitUser(),
  1033. pwszPath,
  1034. 0,
  1035. &nld,
  1036. &lhd,
  1037. cchLockToken,
  1038. rgwszLockToken,
  1039. &cchLockToken);
  1040. if (FAILED(hr))
  1041. {
  1042. DavTrace ("Dav: Failure in FGetLockHandle on davfs.\n");
  1043. return FALSE;
  1044. }
  1045. // Take ownership of the memory allocated
  1046. //
  1047. a_pwszResourceString.take_ownership(nld.m_pwszResourceString);
  1048. a_pwszOwnerComment.take_ownership(nld.m_pwszOwnerComment);
  1049. // Check the access type required.
  1050. // (If the lock is missing any single flag requested, fail.)
  1051. //
  1052. if ( (dwAccess & nld.m_dwAccess) != dwAccess )
  1053. {
  1054. DavTrace ("FGetLockHandleFromId: Access did not match -- bad request.\n");
  1055. return FALSE;
  1056. }
  1057. hr = HrGetUsableHandle(reinterpret_cast<HANDLE>(lhd.h), lhd.dwProcessID, &hTemp);
  1058. if (FAILED(hr))
  1059. {
  1060. DavTrace("HrGetUsableHandle failed with %x \r\n", hr);
  1061. return FALSE;
  1062. }
  1063. if (!phandle->FCreate(hTemp))
  1064. {
  1065. hr = E_OUTOFMEMORY;
  1066. DavTrace("FCreate on autohandler failed \r\n");
  1067. return FALSE;
  1068. }
  1069. // HACK: Rewind the handle here -- until we get a better solution!
  1070. //$LATER: Need a real way to handle multiple access to the same lock handle.
  1071. //
  1072. SetFilePointer ((*phandle).get(), 0, NULL, FILE_BEGIN);
  1073. return TRUE;
  1074. }
  1075. // ------------------------------------------------------------------------
  1076. //
  1077. // FGetLockHandle
  1078. //
  1079. // Main routine for all other methods to get a handle from the cache.
  1080. //
  1081. BOOL
  1082. FGetLockHandle (LPMETHUTIL pmu, LPCWSTR pwszPath,
  1083. DWORD dwAccess, LPCWSTR pwszLockTokenHeader,
  1084. auto_ref_handle * phandle)
  1085. {
  1086. LARGE_INTEGER liLockID;
  1087. HRESULT hr;
  1088. Assert (pmu);
  1089. Assert (pwszPath);
  1090. Assert (pwszLockTokenHeader);
  1091. Assert (!IsBadWritePtr(phandle, sizeof(auto_ref_handle)));
  1092. // Feed the Lock-Token header string into a parser object.
  1093. // And feed in the one path we're interested in.
  1094. // Then get the lockid from the parser object.
  1095. //
  1096. {
  1097. CParseLockTokenHeader lth (pmu, pwszLockTokenHeader);
  1098. lth.SetPaths (pwszPath, NULL);
  1099. hr = lth.HrGetLockIdForPath (pwszPath, dwAccess, &liLockID);
  1100. if (FAILED(hr))
  1101. {
  1102. DavTrace ("Dav: Failure in FGetLockHandle on davfs.\n");
  1103. return FALSE;
  1104. }
  1105. }
  1106. return FGetLockHandleFromId (pmu, liLockID, pwszPath, dwAccess, phandle);
  1107. }
  1108. // ========================================================================
  1109. // Helper functions for locked MOVE and COPY
  1110. //
  1111. // ------------------------------------------------------------------------
  1112. //
  1113. // ScDoOverlappedCopy
  1114. //
  1115. // Takes two file handles that have been opened for overlapped (async)
  1116. // processing, and copies data from the source to the dest.
  1117. // The provided hevt is used in the async read/write operations.
  1118. //
  1119. SCODE
  1120. ScDoOverlappedCopy (HANDLE hfSource, HANDLE hfDest, HANDLE hevtOverlapped)
  1121. {
  1122. SCODE sc = S_OK;
  1123. OVERLAPPED ov;
  1124. BYTE rgbBuffer[1024];
  1125. ULONG cbToWrite;
  1126. ULONG cbActual;
  1127. Assert (hfSource);
  1128. Assert (hfDest);
  1129. Assert (hevtOverlapped);
  1130. ov.hEvent = hevtOverlapped;
  1131. ov.Offset = 0;
  1132. ov.OffsetHigh = 0;
  1133. // Big loop. Read from one file, and write to the other.
  1134. //
  1135. while (1)
  1136. {
  1137. // Read from the source file.
  1138. //
  1139. if (!ReadFromOverlapped (hfSource, rgbBuffer, sizeof(rgbBuffer),
  1140. &cbToWrite, &ov))
  1141. {
  1142. DebugTrace ("Dav: failed to write to file\n");
  1143. sc = HRESULT_FROM_WIN32 (GetLastError());
  1144. goto ret;
  1145. }
  1146. // If no bytes were read (and no error), we're done!
  1147. //
  1148. if (!cbToWrite)
  1149. break;
  1150. // Write the data to the destination file.
  1151. //
  1152. if (!WriteToOverlapped (hfDest,
  1153. rgbBuffer,
  1154. cbToWrite,
  1155. &cbActual,
  1156. &ov))
  1157. {
  1158. DebugTrace ("Dav: failed to write to file\n");
  1159. sc = HRESULT_FROM_WIN32 (GetLastError());
  1160. goto ret;
  1161. }
  1162. // Adjust the starting read position.
  1163. //
  1164. ov.Offset += cbActual;
  1165. }
  1166. // That's it. Set the destination file's size (set EOF) and we're done.
  1167. //
  1168. SetFilePointer (hfDest,
  1169. ov.Offset,
  1170. reinterpret_cast<LONG *>(&ov.OffsetHigh),
  1171. FILE_BEGIN);
  1172. SetEndOfFile (hfDest);
  1173. ret:
  1174. return sc;
  1175. }
  1176. // ------------------------------------------------------------------------
  1177. //
  1178. // ScDoLockedCopy
  1179. //
  1180. // Given the Lock-Token header and the source & destination paths,
  1181. // handle copying from one file to another, with locks in the way.
  1182. //
  1183. // The general flow is this:
  1184. //
  1185. // First check the lock tokens for validity & fetch any valid lock handles.
  1186. // We must have read access on the source and write access on the dest.
  1187. // If any lock token is invalid, or doesn't have the correct access, fail.
  1188. // We need two handles (source & dest) to do the copy, so
  1189. // manually fetch handles that didn't have lock tokens.
  1190. // Once we have both handles, call ScDoOverlappedCopy to copy the file data.
  1191. // Then, copy the DAV property stream from the source to the dest.
  1192. // Any questions?
  1193. //
  1194. // NOTE: This routine should ONLY be called if we already tried to copy
  1195. // the files and we hit a sharing violation.
  1196. //
  1197. //
  1198. //
  1199. SCODE
  1200. ScDoLockedCopy (LPMETHUTIL pmu,
  1201. CParseLockTokenHeader * plth,
  1202. LPCWSTR pwszSrc,
  1203. LPCWSTR pwszDst)
  1204. {
  1205. auto_handle<HANDLE> hfCreated;
  1206. auto_handle<HANDLE> hevt;
  1207. BOOL fSourceLock = FALSE;
  1208. BOOL fDestLock = FALSE;
  1209. LARGE_INTEGER liSource;
  1210. LARGE_INTEGER liDest;
  1211. auto_ref_handle hfLockedSource;
  1212. auto_ref_handle hfLockedDest;
  1213. HANDLE hfSource = INVALID_HANDLE_VALUE;
  1214. HANDLE hfDest = INVALID_HANDLE_VALUE;
  1215. SCODE sc;
  1216. Assert (pmu);
  1217. Assert (plth);
  1218. Assert (pwszSrc);
  1219. Assert (pwszDst);
  1220. // Get any lockids for these paths.
  1221. //
  1222. sc = plth->HrGetLockIdForPath (pwszSrc, GENERIC_READ, &liSource);
  1223. if (SUCCEEDED(sc))
  1224. {
  1225. fSourceLock = TRUE;
  1226. }
  1227. sc = plth->HrGetLockIdForPath (pwszDst, GENERIC_WRITE, &liDest);
  1228. if (SUCCEEDED(sc))
  1229. {
  1230. fDestLock = TRUE;
  1231. }
  1232. // If they didn't even pass in tokens for these paths, quit here.
  1233. // Return & tell them that there's still a sharing violation.
  1234. //
  1235. if (!fSourceLock && !fDestLock)
  1236. {
  1237. DebugTrace ("DwDoLockedCopy -- No locks apply to these paths!");
  1238. return E_DAV_LOCKED;
  1239. }
  1240. if (fSourceLock)
  1241. {
  1242. if (FGetLockHandleFromId (pmu, liSource, pwszSrc, GENERIC_READ,
  1243. &hfLockedSource))
  1244. {
  1245. hfSource = hfLockedSource.get();
  1246. }
  1247. else
  1248. {
  1249. // Clear our flag -- they passed in an invalid/expired token.
  1250. fSourceLock = FALSE;
  1251. }
  1252. }
  1253. if (fDestLock)
  1254. {
  1255. if (FGetLockHandleFromId (pmu, liDest, pwszDst, GENERIC_WRITE,
  1256. &hfLockedDest))
  1257. {
  1258. hfDest = hfLockedDest.get();
  1259. }
  1260. else
  1261. {
  1262. // Clear our flag -- they passed in an invalid/expired token.
  1263. fDestLock = FALSE;
  1264. }
  1265. }
  1266. // Okay, now we either have NO lockhandles (they passed in locktokens
  1267. // but they were all expired) or one handle, or two handles.
  1268. //
  1269. // NO lockhandles (all their locks were expired) -- kick 'em out.
  1270. // And tell 'em there's still a sharing violation to deal with.
  1271. //$REVIEW: Or should we try the copy again???
  1272. if (!fSourceLock && !fDestLock)
  1273. {
  1274. DebugTrace ("DwDoLockedCopy -- No locks apply to these paths!");
  1275. return E_DAV_LOCKED;
  1276. }
  1277. // One handle -- open up the other file manually & shove the data across.
  1278. // Two handles -- shove the data across.
  1279. // If we don't have one of these handles, open the missing one manually.
  1280. //
  1281. if (!fSourceLock)
  1282. {
  1283. // Open up the source file manually.
  1284. //
  1285. hfCreated = DavCreateFile (pwszSrc, // filename
  1286. GENERIC_READ, // dwAccess
  1287. FILE_SHARE_READ | FILE_SHARE_WRITE, // don't clash with OTHER locks
  1288. NULL, // lpSecurityAttributes
  1289. OPEN_ALWAYS, // creation flags
  1290. FILE_ATTRIBUTE_NORMAL | // attributes
  1291. FILE_FLAG_OVERLAPPED |
  1292. FILE_FLAG_SEQUENTIAL_SCAN,
  1293. NULL); // tenplate
  1294. if (INVALID_HANDLE_VALUE == hfCreated.get())
  1295. {
  1296. DebugTrace ("DavFS: DwDoLockedCopy failed to open source file\n");
  1297. sc = HRESULT_FROM_WIN32 (GetLastError());
  1298. goto ret;
  1299. }
  1300. hfSource = hfCreated.get();
  1301. }
  1302. else if (!fDestLock)
  1303. {
  1304. // Open up the destination file manually.
  1305. // This guy is CREATE_NEW becuase we should have already deleted
  1306. // any files that would have conflicted!
  1307. //
  1308. hfCreated = DavCreateFile (pwszDst, // filename
  1309. GENERIC_WRITE, // dwAccess
  1310. 0, //FILE_SHARE_READ | FILE_SHARE_WRITE, // DO clash with OTHER locks -- just like PUT
  1311. NULL, // lpSecurityAttributes
  1312. CREATE_NEW, // creation flags
  1313. FILE_ATTRIBUTE_NORMAL | // attributes
  1314. FILE_FLAG_OVERLAPPED |
  1315. FILE_FLAG_SEQUENTIAL_SCAN,
  1316. NULL); // tenplate
  1317. if (INVALID_HANDLE_VALUE == hfCreated)
  1318. {
  1319. DebugTrace ("DavFS: DwDoLockedCopy failed to open destination file\n");
  1320. sc = HRESULT_FROM_WIN32 (GetLastError());
  1321. goto ret;
  1322. }
  1323. hfDest = hfCreated.get();
  1324. }
  1325. // Now we should have two handles.
  1326. //
  1327. Assert ((hfSource != INVALID_HANDLE_VALUE) && (hfDest != INVALID_HANDLE_VALUE));
  1328. // Setup the overlapped structure so we can read/write to async files.
  1329. //
  1330. hevt = CreateEvent(NULL, TRUE, FALSE, NULL);
  1331. if (!hevt.get())
  1332. {
  1333. DebugTrace ("DavFS: DwDoLockedCopy failed to create event for overlapped read.\n");
  1334. sc = HRESULT_FROM_WIN32 (GetLastError());
  1335. goto ret;
  1336. }
  1337. // Copy the file data.
  1338. //
  1339. sc = ScDoOverlappedCopy (hfSource, hfDest, hevt.get());
  1340. if (FAILED (sc))
  1341. goto ret;
  1342. // Copy over any property data.
  1343. //
  1344. if (FAILED (ScCopyProps (pmu, pwszSrc, pwszDst, FALSE, hfSource, hfDest)))
  1345. sc = E_DAV_LOCKED;
  1346. ret:
  1347. return sc;
  1348. }