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.

2367 lines
63 KiB

  1. /*
  2. * F S M V C P Y . C P P
  3. *
  4. * Sources for directory ineration object
  5. *
  6. * Copyright 1986-1997 Microsoft Corporation, All Rights Reserved
  7. */
  8. #include "_davfs.h"
  9. #include "_fsmvcpy.h"
  10. #include "_shlkmgr.h"
  11. // ScAddMultiUrl
  12. // Helper function for XML emitting
  13. //
  14. SCODE
  15. ScAddMultiFromUrl (
  16. /* [in] */ CXMLEmitter& emitter,
  17. /* [in] */ IMethUtil * pmu,
  18. /* [in] */ LPCWSTR pwszUrl,
  19. /* [in] */ ULONG hsc,
  20. /* [in] */ BOOL fCollection,
  21. /* [in] */ BOOL fMove = FALSE)
  22. {
  23. SCODE sc = S_OK;
  24. // NT#403615 -- Rosebud in Office9 or in NT5 re-issues a MOVE if they
  25. // see a 401 inside the 207 Multi-Status response, which can result
  26. // in data loss.
  27. // WORKAROUND: If we are doing a MOVE, and the User-Agent string shows
  28. // that this is Rosebud from Office9 or from NT5, change all 401s to 403s
  29. // to avoid the problem -- Rosebud will not re-issue the MOVE, so the
  30. // data (now sitting in the destination dir) will not be wiped.
  31. // This is the minimal code needed to work around the problem.
  32. //
  33. if (fMove &&
  34. HSC_UNAUTHORIZED == hsc &&
  35. (pmu->FIsOffice9Request() || pmu->FIsRosebudNT5Request()))
  36. {
  37. hsc = HSC_FORBIDDEN;
  38. }
  39. // Supress omitting of "Method Failure" node as it is a "SHOULD NOT"
  40. // item in the DAV drafts.
  41. // It's possible pszUrl passed in as NULL, in these cases, simply skip
  42. // the emitting, do nothing
  43. //
  44. if ((hsc != HSC_METHOD_FAILURE) && pwszUrl)
  45. {
  46. auto_heap_ptr<CHAR> pszUrlEscaped;
  47. CEmitterNode enRes;
  48. UINT cchUrl;
  49. //$REVIEW: This is important, we should not start a xml document
  50. //$REVIEW: unless we have to. Otherwise, we may end up XML body
  51. //$REVIEW: when not necessary.
  52. //$REVIEW: So it's necessary to call ScSetRoot() to make sure
  53. //$REVIEW: that the xml document is intialized bofore continue
  54. //$REVIEW:
  55. //$REVIEW: This model works because in fsmvcpy.cpp, all the calls to
  56. //$REVIEW: XML emitter are through ScAddMultiFromUrl and ScAddMulti
  57. //$REVIEW:
  58. sc = emitter.ScSetRoot (gc_wszMultiResponse);
  59. if (FAILED (sc))
  60. goto ret;
  61. // Construct the response
  62. //
  63. sc = enRes.ScConstructNode (emitter, emitter.PxnRoot(), gc_wszResponse);
  64. if (FAILED (sc))
  65. goto ret;
  66. // Construct the href node
  67. //
  68. {
  69. CEmitterNode en;
  70. sc = enRes.ScAddNode (gc_wszXML__Href, en);
  71. if (FAILED (sc))
  72. goto ret;
  73. // Set the value of the href node. If the url is absolute,
  74. // but not fully qualified, qualify it...
  75. //
  76. if (L'/' == *pwszUrl)
  77. {
  78. LPCSTR psz;
  79. UINT cch;
  80. // Add the prefix
  81. //
  82. cch = pmu->CchUrlPrefix (&psz);
  83. sc = en.Pxn()->ScSetUTF8Value (psz, cch);
  84. if (FAILED (sc))
  85. goto ret;
  86. //$ REVIEW: Does the host name need escaping?
  87. //
  88. // Add the server
  89. //
  90. cch = pmu->CchServerName (&psz);
  91. sc = en.Pxn()->ScSetValue (psz, cch);
  92. if (FAILED (sc))
  93. goto ret;
  94. }
  95. // Make the url wire safe
  96. //
  97. sc = ScWireUrlFromWideLocalUrl (static_cast<UINT>(wcslen(pwszUrl)),
  98. pwszUrl,
  99. pszUrlEscaped);
  100. if (FAILED (sc))
  101. goto ret;
  102. // Add the url value
  103. //
  104. cchUrl = static_cast<UINT>(strlen(pszUrlEscaped.get()));
  105. sc = en.Pxn()->ScSetUTF8Value (pszUrlEscaped.get(), cchUrl);
  106. if (FAILED (sc))
  107. goto ret;
  108. // If this is a collection, and the last char is not a
  109. // trailing slash, add one....
  110. //
  111. if (fCollection && ('/' != pszUrlEscaped.get()[cchUrl-1]))
  112. {
  113. sc = en.Pxn()->ScSetUTF8Value ("/", 1);
  114. if (FAILED (sc))
  115. goto ret;
  116. }
  117. }
  118. // Add the status/error string
  119. //
  120. sc = ScAddStatus (&enRes, hsc);
  121. if (FAILED (sc))
  122. goto ret;
  123. }
  124. ret:
  125. return sc;
  126. }
  127. SCODE
  128. ScAddMulti (
  129. /* [in] */ CXMLEmitter& emitter,
  130. /* [in] */ IMethUtil * pmu,
  131. /* [in] */ LPCWSTR pwszPath,
  132. /* [in] */ LPCWSTR pwszErr,
  133. /* [in] */ ULONG hsc,
  134. /* [in] */ BOOL fCollection,
  135. /* [in] */ CVRoot* pcvrTrans)
  136. {
  137. SCODE sc = S_OK;
  138. // Supress omitting of "Method Failure" node as it is a "SHOULD NOT"
  139. // item in the DAV drafts.
  140. //
  141. if (hsc != HSC_METHOD_FAILURE)
  142. {
  143. CEmitterNode enRes;
  144. //$REVIEW: This is important, we should not start a xml document
  145. //$REVIEW: unless we have to. Otherwise, we may end up XML body
  146. //$REVIEW: when not necessary.
  147. //$REVIEW: So it's necessary to call ScSetRoot() to make sure
  148. //$REVIEW: that the xml document is intialized bofore continue
  149. //$REVIEW:
  150. //$REVIEW: This model works because in fsmvcpy.cpp, all the calls to
  151. //$REVIEW: XML emitter are through ScAddMultiFromUrl and ScAddMulti
  152. //$REVIEW:
  153. sc = emitter.ScSetRoot (gc_wszMultiResponse);
  154. if (FAILED (sc))
  155. goto ret;
  156. sc = enRes.ScConstructNode (emitter, emitter.PxnRoot(), gc_wszResponse);
  157. if (FAILED (sc))
  158. goto ret;
  159. sc = ScAddHref (enRes, pmu, pwszPath, fCollection, pcvrTrans);
  160. if (FAILED (sc))
  161. goto ret;
  162. sc = ScAddStatus (&enRes, hsc);
  163. if (FAILED (sc))
  164. goto ret;
  165. if (pwszErr)
  166. {
  167. sc = ScAddError (&enRes, pwszErr);
  168. if (FAILED (sc))
  169. goto ret;
  170. }
  171. }
  172. ret:
  173. return sc;
  174. }
  175. // class CAccessMetaOp -------------------------------------------------------
  176. //
  177. SCODE __fastcall
  178. CAccessMetaOp::ScOp(LPCWSTR pwszMbPath, UINT cch)
  179. {
  180. SCODE sc;
  181. METADATA_RECORD mdrec;
  182. Assert (MD_ACCESS_PERM == m_dwId);
  183. Assert (DWORD_METADATA == m_dwType);
  184. // Get the value from the metabase and don't inherit
  185. //
  186. DWORD dwAcc = 0;
  187. DWORD cb = sizeof(DWORD);
  188. mdrec.dwMDIdentifier = m_dwId;
  189. mdrec.dwMDAttributes = METADATA_NO_ATTRIBUTES;
  190. mdrec.dwMDUserType = 0;
  191. mdrec.dwMDDataType = m_dwType;
  192. mdrec.dwMDDataLen = cb;
  193. mdrec.pbMDData = reinterpret_cast<LPBYTE>(&dwAcc);
  194. sc = m_mdoh.HrGetMetaData( pwszMbPath,
  195. &mdrec,
  196. &cb );
  197. if (FAILED(sc))
  198. {
  199. MCDTrace ("CAccessMetaOp::ScOp() - CMDObjectHandle::HrGetMetaData() failed 0x%08lX\n", sc);
  200. // We will ignore any NOT_FOUND type errors
  201. //
  202. if ((HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == sc) ||
  203. (MD_ERROR_DATA_NOT_FOUND == sc))
  204. sc = S_OK;
  205. goto ret;
  206. }
  207. // Hey, we got a value, so let's do our quick check..
  208. //
  209. if (m_dwAcc == (dwAcc & m_dwAcc))
  210. {
  211. // We have full required access to this node, so
  212. // we can proceed.
  213. //
  214. Assert (S_OK == sc);
  215. }
  216. else
  217. {
  218. // We do not have access to operate on this item and
  219. // it's inherited children.
  220. //
  221. MCDTrace ("CAccessMetaOp::ScOp() - no access to '%S'\n", pwszMbPath);
  222. m_fAccessBlocked = TRUE;
  223. // We know enough...
  224. //
  225. sc = S_FALSE;
  226. }
  227. ret:
  228. return sc;
  229. }
  230. // class CAuthMetaOp -------------------------------------------------------
  231. //
  232. SCODE __fastcall
  233. CAuthMetaOp::ScOp(LPCWSTR pwszMbPath, UINT cch)
  234. {
  235. SCODE sc;
  236. METADATA_RECORD mdrec;
  237. Assert (MD_AUTHORIZATION == m_dwId);
  238. Assert (DWORD_METADATA == m_dwType);
  239. // Get the value from the metabase and don't inherit
  240. //
  241. DWORD dwAuth = 0;
  242. DWORD cb = sizeof(DWORD);
  243. mdrec.dwMDIdentifier = m_dwId;
  244. mdrec.dwMDAttributes = METADATA_NO_ATTRIBUTES;
  245. mdrec.dwMDUserType = 0;
  246. mdrec.dwMDDataType = m_dwType;
  247. mdrec.dwMDDataLen = cb;
  248. mdrec.pbMDData = reinterpret_cast<LPBYTE>(&dwAuth);
  249. sc = m_mdoh.HrGetMetaData( pwszMbPath,
  250. &mdrec,
  251. &cb );
  252. if (FAILED(sc))
  253. {
  254. MCDTrace ("CAuthMetaOp::ScOp() - CMDObjectHandle::HrGetMetaData() failed 0x%08lX\n", sc);
  255. // We will ignore any NOT_FOUND type errors
  256. //
  257. if ((HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == sc) ||
  258. (MD_ERROR_DATA_NOT_FOUND == sc))
  259. sc = S_OK;
  260. goto ret;
  261. }
  262. // Hey, we got a value, so let's do our quick check..
  263. //
  264. if (m_dwAuth == dwAuth)
  265. {
  266. Assert(S_OK == sc);
  267. }
  268. else
  269. {
  270. // We do not have access to operate on this item and
  271. // it's inherited children.
  272. //
  273. MCDTrace ("CAuthMetaOp::ScOp() - authorization differs, no access to '%S'\n", pwszMbPath);
  274. m_fAccessBlocked = TRUE;
  275. // We know enough...
  276. //
  277. sc = S_FALSE;
  278. }
  279. ret:
  280. return sc;
  281. }
  282. // class CIPRestrictionMetaOp ------------------------------------------------
  283. //
  284. SCODE __fastcall
  285. CIPRestrictionMetaOp::ScOp(LPCWSTR pwszMbPath, UINT cch)
  286. {
  287. SCODE sc;
  288. METADATA_RECORD mdrec;
  289. Assert (MD_IP_SEC == m_dwId);
  290. Assert (BINARY_METADATA == m_dwType);
  291. // Get the value from the metabase and don't inherit
  292. //
  293. DWORD cb = 0;
  294. mdrec.dwMDIdentifier = m_dwId;
  295. mdrec.dwMDAttributes = METADATA_NO_ATTRIBUTES;
  296. mdrec.dwMDUserType = 0;
  297. mdrec.dwMDDataType = m_dwType;
  298. mdrec.dwMDDataLen = cb;
  299. mdrec.pbMDData = NULL;
  300. sc = m_mdoh.HrGetMetaData( pwszMbPath,
  301. &mdrec,
  302. &cb );
  303. if (FAILED(sc) && (0 == cb))
  304. {
  305. MCDTrace ("CIPRestrictionMetaOp::ScOp() - CMDObjectHandle::HrGetMetaData() failed 0x%08lX, but that means success in this path\n", sc);
  306. // We will ignore any NOT_FOUND type errors
  307. //
  308. if ((HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == sc) ||
  309. (MD_ERROR_DATA_NOT_FOUND == sc))
  310. sc = S_OK;
  311. }
  312. else
  313. {
  314. Assert (S_OK == sc ||
  315. HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) == sc);
  316. // Hey, we got a value, and we don't want to check here so
  317. // we are going to be pessimistic about this one....
  318. //
  319. MCDTrace ("CIPRestrictionMetaOp::ScOp() - IPRestriction exists in tree '%S'\n", pwszMbPath);
  320. m_fAccessBlocked = TRUE;
  321. // We know enough...
  322. //
  323. sc = S_FALSE;
  324. }
  325. return sc;
  326. }
  327. // ScCheckMoveCopyDeleteAccess() ---------------------------------------------
  328. //
  329. SCODE __fastcall
  330. ScCheckMoveCopyDeleteAccess (
  331. /* [in] */ IMethUtil* pmu,
  332. /* [in] */ LPCWSTR pwszUrl,
  333. /* [in] */ CVRoot* pcvr,
  334. /* [in] */ BOOL fDirectory,
  335. /* [in] */ BOOL fCheckScriptmaps,
  336. /* [in] */ DWORD dwAccess,
  337. /* [out] */ SCODE* pscItem,
  338. /* [in] */ CXMLEmitter& msr)
  339. {
  340. SCODE sc = S_OK;
  341. // Check with the CMethUtil on whether or not we have access
  342. //
  343. sc = pmu->ScCheckMoveCopyDeleteAccess (pwszUrl,
  344. pcvr,
  345. fDirectory,
  346. fCheckScriptmaps,
  347. dwAccess);
  348. // Pass back the results...
  349. //
  350. *pscItem = sc;
  351. // ... and if the call cannot proceed, then add the item to the
  352. // multi-status response.
  353. //
  354. if (FAILED (sc))
  355. {
  356. // Add to the reponse XML
  357. //
  358. sc = ScAddMultiFromUrl (msr,
  359. pmu,
  360. pwszUrl,
  361. HscFromHresult(sc),
  362. fDirectory);
  363. if (!FAILED (sc))
  364. sc = W_DAV_PARTIAL_SUCCESS;
  365. }
  366. return sc;
  367. }
  368. // Directory deletes ---------------------------------------------------------
  369. //
  370. /*
  371. * ScDeleteDirectory()
  372. *
  373. * Purpose:
  374. *
  375. * Helper function used to iterate through a directory
  376. * and delete all its contents as well as the directory
  377. * itself.
  378. *
  379. * Notes:
  380. *
  381. * BIG FAT NOTE ABOUT LOCKING.
  382. *
  383. * The Lock-Token header may contain locks that we have to use in
  384. * this operation.
  385. * The following code was written with these assumptions:
  386. * o Directory locks are NOT SUPPORTED on davfs.
  387. * o Locks only affect the ability to WRITE to a resource.
  388. * (The only currently supported locktype on davfs is WRITE.)
  389. * o This function may be called from DELETE or other methods.
  390. * (If called from DELETE, we want to DROP the locks listed.)
  391. * Because of these two assumptions, we only check the passed-in
  392. * locktokens when we have a write-error (destination).
  393. *
  394. * Locking uses the final two parameters. plth is a
  395. * pointer to a locktoken header parser object. If we have a plth,
  396. * then we must check it for provided locktokens when we hit a lock
  397. * conflict. If a locktoken is provided, the failed delete operation
  398. * should NOT be reported as an error, but instead skipped here
  399. * (to be handled by the calling routine later in the operation) OR
  400. * the lock should be dropped here and the delete attempted again.
  401. * The fDeleteLocks variable tells whether to drop locks (TRUE),
  402. * or to skip the deleteion of locked items that have locktokens.
  403. *
  404. * Basic logic:
  405. * Try to delete.
  406. * If LOCKING failure (ERROR_SHARING_VIOLATION), check the plth.
  407. * If the plth has a locktoken for this path, check fDeleteLocks.
  408. * If fDeleteLocks == TRUE, drop the lock and try the delte again.
  409. * If fDeleteLocks == FALSE, skip this file and move on.
  410. */
  411. SCODE
  412. ScDeleteDirectory (
  413. /* [in] */ IMethUtil* pmu,
  414. /* [in] */ LPCWSTR pwszUrl,
  415. /* [in] */ LPCWSTR pwszDir,
  416. /* [in] */ BOOL fCheckAccess,
  417. /* [in] */ DWORD dwAcc,
  418. /* [in] */ LONG lDepth,
  419. /* [in] */ CVRoot* pcvrTranslate,
  420. /* [in] */ CXMLEmitter& msr,
  421. /* [out] */ BOOL* pfDeleted,
  422. /* [in] */ CParseLockTokenHeader* plth, // Usually NULL -- no locktokens to worry about
  423. /* [in] */ BOOL fDeleteLocks) // Normally FALSE -- don't drop locks
  424. {
  425. BOOL fOneSkipped = FALSE;
  426. ChainedStringBuffer<WCHAR> sb;
  427. SCODE sc = S_OK;
  428. SCODE scItem = S_OK;
  429. std::list<LPCWSTR, heap_allocator<LPCWSTR> > lst;
  430. CDirIter di(pwszUrl,
  431. pwszDir,
  432. NULL, // no-destination for deletes
  433. NULL, // no-destination for deletes
  434. NULL, // no-destination for deletes
  435. TRUE); // recurse into sub-driectories
  436. Assert (pfDeleted);
  437. *pfDeleted = TRUE;
  438. // A SMALL FAT NOTE ABOUT ACCESS
  439. //
  440. // The caller of this method is required to sniff the tree
  441. // prior to this call. If there is any access block in the
  442. // tree, then each item will be checked for access before the
  443. // operation proceeds.
  444. //
  445. const DWORD dwDirectory = MD_ACCESS_READ | MD_ACCESS_WRITE;
  446. const DWORD dwFile = MD_ACCESS_WRITE;
  447. if (fCheckAccess & (0 == (dwAcc & MD_ACCESS_READ)))
  448. {
  449. DebugTrace ("Dav: MCD: no permissions for deleting\n");
  450. sc = E_DAV_NO_IIS_READ_ACCESS;
  451. *pfDeleted = FALSE;
  452. goto ret;
  453. }
  454. // Push the current path
  455. //
  456. //$ REVIEW: if "depth: infinity,no-root" ever needs to be supported,
  457. // it really is as simple as not pushing the current path.
  458. //
  459. if (DEPTH_INFINITY_NOROOT != lDepth)
  460. {
  461. Assert (DEPTH_INFINITY == lDepth);
  462. lst.push_back(pwszDir);
  463. }
  464. // Iterate through the directories. Deleting files and pushing
  465. // directory names as we go.
  466. //
  467. // We really only want to push into the child directories if the
  468. // operation on the parent succeeds.
  469. //
  470. while (S_OK == di.ScGetNext(!FAILED (scItem)))
  471. {
  472. // Check our access rights and only push down into the directory
  473. // if we have access to delete its contents.
  474. //
  475. // Note that we need read and write access to enum and delete
  476. // a directory, but we need only write access in order to delete
  477. // a file.
  478. //
  479. if (fCheckAccess)
  480. {
  481. sc = ScCheckMoveCopyDeleteAccess (pmu,
  482. di.PwszUri(),
  483. pcvrTranslate,
  484. di.FDirectory(),
  485. FALSE, // don't check scriptmaps
  486. di.FDirectory() ? dwDirectory : dwFile,
  487. &scItem,
  488. msr);
  489. if (FAILED (sc))
  490. goto ret;
  491. // If things were not 100%, don't process this resource
  492. //
  493. if (S_OK != sc)
  494. continue;
  495. }
  496. // Process the file
  497. //
  498. if (di.FDirectory())
  499. {
  500. auto_ref_ptr<CVRoot> pcvr;
  501. if (di.FSpecial())
  502. continue;
  503. // Child virtual root scriptmappings have been
  504. // handled via ScCheckMoveCopyDeleteAccess(),
  505. // and the physical deleting happens after this
  506. // call completes!
  507. //
  508. // So there is no need to do any special processing
  509. // other than to push the directory and move on...
  510. //
  511. lst.push_back (AppendChainedSz (sb, di.PwszSource()));
  512. scItem = S_OK;
  513. }
  514. else
  515. {
  516. // Delete the file
  517. //
  518. // NOTE: We've already checked that we have write access.
  519. // Also keep in mind that the ordering in which the directory
  520. // traversals occur allows us to still key off of scItem to
  521. // determine if we should push down into subdirectories.
  522. //
  523. // This is because the directory entry is processed BEFORE
  524. // any children are processed. So the iteration on the dir
  525. // will reset the scItem with the appropriate scode for access.
  526. //
  527. MCDTrace ("Dav: MCD: deleting '%ws'\n", di.PwszSource());
  528. if (!DavDeleteFile (di.PwszSource()))
  529. {
  530. DWORD dwLastError = GetLastError();
  531. ULONG hsc = HscFromLastError(dwLastError);
  532. DebugTrace ("Dav: MCD: failed to delete file (%d)\n", dwLastError);
  533. // If it's a sharing (lock) violation, AND we have a
  534. // locktoken for this path (lth.GetToken(pwsz))
  535. // skip this path.
  536. //
  537. if ((ERROR_SHARING_VIOLATION == dwLastError) && plth)
  538. {
  539. LARGE_INTEGER liLockID;
  540. // If we have a locktoken for this path, skip it.
  541. //
  542. scItem = plth->HrGetLockIdForPath(di.PwszSource(),
  543. GENERIC_WRITE,
  544. &liLockID);
  545. if (SUCCEEDED (scItem))
  546. {
  547. // Should we try to delete locks?
  548. //
  549. if (!fDeleteLocks)
  550. {
  551. // Don't delete locks. Just skip this item.
  552. // Remember that we skipped it, tho, so we don't
  553. // complain about deleting the parent dir below.
  554. //
  555. fOneSkipped = TRUE;
  556. continue;
  557. }
  558. else
  559. {
  560. // Drop the lock & try again.
  561. //
  562. scItem = CSharedLockMgr::Instance().HrDeleteLock(pmu->HitUser(),
  563. liLockID);
  564. if (SUCCEEDED(scItem))
  565. {
  566. if (DavDeleteFile(di.PwszSource()))
  567. {
  568. // We're done with this item. Move along.
  569. //
  570. continue;
  571. }
  572. // Else, record the error in our XML
  573. //
  574. hsc = HscFromLastError(GetLastError());
  575. }
  576. else
  577. {
  578. hsc = HscFromHresult(scItem);
  579. }
  580. }
  581. }
  582. // else, record the error in our XML.
  583. //
  584. }
  585. // Add to the reponse XML
  586. //
  587. sc = ScAddMultiFromUrl (msr,
  588. pmu,
  589. di.PwszUri(),
  590. hsc,
  591. di.FDirectory());
  592. if (FAILED (sc))
  593. goto ret;
  594. sc = W_DAV_PARTIAL_SUCCESS;
  595. }
  596. }
  597. }
  598. // Now that all the files are deleted, we can start deleting
  599. // the directories.
  600. //
  601. while (!lst.empty())
  602. {
  603. MCDTrace ("Dav: MCD: removing '%ws'\n", lst.back());
  604. // Try to delete the dir. If it doesn't delete, check our
  605. // "skipped because of a lock above" flag before complaining.
  606. //
  607. //$ LATER: Fix this to actually lookup the dir path in the
  608. // lockcache (using "fPathLookup").
  609. //
  610. if (!DavRemoveDirectory (lst.back()) && !fOneSkipped)
  611. {
  612. DWORD dw = GetLastError();
  613. DebugTrace ("Dav: MCD: failed to delete directory: %ld\n", dw);
  614. // Add to the reponse XML
  615. //
  616. sc = ScAddMulti (msr,
  617. pmu,
  618. lst.back(),
  619. NULL,
  620. HscFromLastError(dw),
  621. TRUE, // We know it's a directory
  622. pcvrTranslate);
  623. if (FAILED (sc))
  624. goto ret;
  625. sc = W_DAV_PARTIAL_SUCCESS;
  626. *pfDeleted = FALSE;
  627. }
  628. lst.pop_back();
  629. }
  630. ret:
  631. return sc;
  632. }
  633. SCODE
  634. ScDeleteDirectoryAndChildren (
  635. /* [in] */ IMethUtil* pmu,
  636. /* [in] */ LPCWSTR pwszUrl,
  637. /* [in] */ LPCWSTR pwszPath,
  638. /* [in] */ BOOL fCheckAccess,
  639. /* [in] */ DWORD dwAcc,
  640. /* [in] */ LONG lDepth,
  641. /* [in] */ CXMLEmitter& msr,
  642. /* [in] */ CVRoot* pcvrTranslate,
  643. /* [out] */ BOOL* pfDeleted,
  644. /* [in] */ CParseLockTokenHeader* plth, // Usually NULL -- no locktokens to worry about
  645. /* [in] */ BOOL fDeleteLocks) // Normally FALSE -- don't drop locks
  646. {
  647. BOOL fPartial = FALSE;
  648. SCODE sc = S_OK;
  649. // Delete the main body first
  650. //
  651. MCDTrace ("Dav: MCD: deleting '%ws'\n", pwszPath);
  652. sc = ScDeleteDirectory (pmu,
  653. pwszUrl,
  654. pwszPath,
  655. fCheckAccess,
  656. dwAcc,
  657. lDepth,
  658. pcvrTranslate, // translations are pmu based
  659. msr,
  660. pfDeleted,
  661. plth,
  662. fDeleteLocks);
  663. if (!FAILED (sc))
  664. {
  665. // Enumerate the child vroots and perform the
  666. // deletion of those directories as well
  667. //
  668. ChainedStringBuffer<WCHAR> sb;
  669. CVRList vrl;
  670. // Cleanup the list such that our namespaces are in
  671. // a reasonable order.
  672. //
  673. (void) pmu->ScFindChildVRoots (pwszUrl, sb, vrl);
  674. vrl.sort();
  675. for ( ; !FAILED(sc) && !vrl.empty(); vrl.pop_front())
  676. {
  677. auto_ref_ptr<CVRoot> pcvr;
  678. CResourceInfo cri;
  679. LPCWSTR pwszChildUrl;
  680. LPCWSTR pwszChildPath;
  681. SCODE scItem;
  682. // Remember any partial returns
  683. //
  684. if (W_DAV_PARTIAL_SUCCESS == sc)
  685. fPartial = TRUE;
  686. if (pmu->FGetChildVRoot (vrl.front().m_pwsz, pcvr))
  687. {
  688. // Note, only check access if required
  689. //
  690. Assert (fCheckAccess);
  691. pcvr->CchGetVRoot (&pwszChildUrl);
  692. pcvr->CchGetVRPath (&pwszChildPath);
  693. sc = ScCheckMoveCopyDeleteAccess (pmu,
  694. pwszChildUrl,
  695. pcvr.get(),
  696. TRUE, // Directory
  697. FALSE, // dont check scriptmaps
  698. MD_ACCESS_READ|MD_ACCESS_WRITE,
  699. &scItem,
  700. msr);
  701. if (FAILED (sc))
  702. goto ret;
  703. // If things were not 100%, don't process this resource
  704. //
  705. if (S_OK != sc)
  706. continue;
  707. // Delete the sub-virtual roots files and and continue on
  708. //
  709. sc = ScDeleteDirectory (pmu,
  710. pwszChildUrl,
  711. pwszChildPath,
  712. fCheckAccess,
  713. dwAcc,
  714. DEPTH_INFINITY,
  715. pcvr.get(),
  716. msr,
  717. pfDeleted,
  718. plth,
  719. fDeleteLocks);
  720. if (FAILED (sc))
  721. {
  722. sc = ScAddMultiFromUrl (msr,
  723. pmu,
  724. pwszChildUrl,
  725. HscFromHresult(sc),
  726. TRUE); // We know it's a directory
  727. if (FAILED (sc))
  728. goto ret;
  729. sc = W_DAV_PARTIAL_SUCCESS;
  730. *pfDeleted = FALSE;
  731. }
  732. }
  733. }
  734. }
  735. ret:
  736. return ((S_OK == sc) && fPartial) ? W_DAV_PARTIAL_SUCCESS : sc;
  737. }
  738. // class CContentTypeMetaOp --------------------------------------------------
  739. //
  740. SCODE __fastcall
  741. CContentTypeMetaOp::ScOp(LPCWSTR pwszMbPath, UINT cchSrc)
  742. {
  743. Assert (MD_MIME_MAP == m_dwId);
  744. Assert (MULTISZ_METADATA == m_dwType);
  745. // Ok, we are going to get the data and copy it across
  746. // if there is a destination path.
  747. //
  748. if (NULL != m_pwszDestPath)
  749. {
  750. WCHAR prgchContentType[MAX_PATH];
  751. auto_heap_ptr<WCHAR> pwszContentType;
  752. CMDObjectHandle mdohDest(*m_pecb);
  753. CStackBuffer<WCHAR,128> pwsz;
  754. DWORD cb = sizeof(prgchContentType);
  755. LPBYTE pbValue = reinterpret_cast<LPBYTE>(prgchContentType);
  756. LPWSTR pwszLowest;
  757. METADATA_RECORD mdrec;
  758. SCODE sc = S_OK;
  759. UINT cchBase;
  760. MCDTrace ("CContentTypeMetaOp::ScOp() - content-type: copying for '%S%S'...\n",
  761. m_pwszMetaPath,
  762. pwszMbPath);
  763. mdrec.dwMDIdentifier = m_dwId;
  764. mdrec.dwMDAttributes = METADATA_NO_ATTRIBUTES;
  765. mdrec.dwMDUserType = 0;
  766. mdrec.dwMDDataType = m_dwType;
  767. mdrec.dwMDDataLen = cb;
  768. mdrec.pbMDData = pbValue;
  769. sc = m_mdoh.HrGetMetaData( pwszMbPath,
  770. &mdrec,
  771. &cb );
  772. if (HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) == sc)
  773. {
  774. pwszContentType = static_cast<LPWSTR>(g_heap.Alloc(cb));
  775. pbValue = reinterpret_cast<LPBYTE>(pwszContentType.get());
  776. mdrec.dwMDIdentifier = m_dwId;
  777. mdrec.dwMDAttributes = METADATA_NO_ATTRIBUTES;
  778. mdrec.dwMDUserType = 0;
  779. mdrec.dwMDDataType = m_dwType;
  780. mdrec.dwMDDataLen = cb;
  781. mdrec.pbMDData = pbValue;
  782. sc = m_mdoh.HrGetMetaData( pwszMbPath,
  783. &mdrec,
  784. &cb );
  785. }
  786. if (FAILED(sc))
  787. {
  788. //$ REVIEW: should failure to copy a content-type
  789. // fail the call? So we will ignore any NOT_FOUND
  790. // type errors
  791. //
  792. if ((HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == sc) ||
  793. (MD_ERROR_DATA_NOT_FOUND == sc))
  794. sc = S_OK;
  795. goto ret;
  796. }
  797. // We sucesfully read some metadata. Remember the size.
  798. //
  799. cb = mdrec.dwMDDataLen;
  800. m_mdoh.Close();
  801. // The destination path is the comprised of the stored
  802. // destination path and the tail of the original source
  803. // path.
  804. //
  805. MCDTrace ("CContentTypeMetaOp::ScOp() - content-type: ...to '%S%S'\n",
  806. m_pwszDestPath,
  807. pwszMbPath);
  808. // Construct an metabase path for lowest node construction
  809. //
  810. cchBase = static_cast<UINT>(wcslen(m_pwszDestPath));
  811. if (NULL == pwsz.resize(CbSizeWsz(cchBase + cchSrc)))
  812. {
  813. sc = E_OUTOFMEMORY;
  814. goto ret;
  815. }
  816. // Before we construct anything, make sure that we are not
  817. // doing something stupid and creating two '//' in a row.
  818. //
  819. if ((L'/' == m_pwszDestPath[cchBase - 1]) &&
  820. (L'/' == *pwszMbPath))
  821. {
  822. cchBase -= 1;
  823. }
  824. memcpy (pwsz.get(), m_pwszDestPath, cchBase * sizeof(WCHAR));
  825. memcpy (pwsz.get() + cchBase, pwszMbPath, (cchSrc + 1) * sizeof(WCHAR));
  826. // Release our hold on the metabase. We need to do this
  827. // because it is possible that the common root between
  828. // the two nodes, may be in the same hierarchy.
  829. //
  830. // NOTE: this should only really happen one time per
  831. // move/copy operation. The reason being that the lowest
  832. // node will be established outside of the scope of the
  833. // source on the first call.
  834. //
  835. sc = HrMDOpenLowestNodeMetaObject(pwsz.get(),
  836. METADATA_PERMISSION_READ | METADATA_PERMISSION_WRITE,
  837. &pwszLowest,
  838. &mdohDest);
  839. if (SUCCEEDED(sc))
  840. {
  841. mdrec.dwMDIdentifier = m_dwId;
  842. mdrec.dwMDAttributes = METADATA_INHERIT;
  843. mdrec.dwMDUserType = IIS_MD_UT_FILE;
  844. mdrec.dwMDDataType = m_dwType;
  845. mdrec.dwMDDataLen = cb;
  846. mdrec.pbMDData = pbValue;
  847. (void) mdohDest.HrSetMetaData(pwszLowest, &mdrec);
  848. mdohDest.Close();
  849. }
  850. // Reaquire our hold on the metabase
  851. //
  852. sc = HrMDOpenMetaObject( m_pwszMetaPath,
  853. m_fWrite ? METADATA_PERMISSION_READ | METADATA_PERMISSION_WRITE : METADATA_PERMISSION_READ,
  854. 5000,
  855. &m_mdoh);
  856. if (FAILED (sc))
  857. goto ret;
  858. }
  859. // If this is a move, then delete the source
  860. //
  861. if (m_fDelete)
  862. {
  863. MCDTrace ("Dav: MCD: content-type: deleting from '%S'\n", pwszMbPath);
  864. (void) m_mdoh.HrDeleteMetaData( pwszMbPath,
  865. m_dwId,
  866. m_dwType);
  867. }
  868. ret:
  869. return S_OK;
  870. }
  871. // Directory moves/copies ----------------------------------------------------
  872. //
  873. /*
  874. * ScMoveCopyDirectory()
  875. *
  876. * Purpose:
  877. *
  878. * Helper function used to iterate through a directory
  879. * and copy all its contents to a destination directory.
  880. *
  881. * Notes:
  882. *
  883. * BIG FAT NOTE ABOUT LOCKING.
  884. *
  885. * The Lock-Token header may contain locks that we have to use in
  886. * this operation.
  887. * The following code was written with these assumptions:
  888. * o Directory locks are NOT SUPPORTED on davfs.
  889. * o Locks only affect the ability to WRITE to a resource.
  890. * (The only currently supported locktype on davfs is WRITE.)
  891. * Because of these two assumptions, we only check the passed-in
  892. * locktokens when we have a write-error (destination).
  893. */
  894. SCODE
  895. ScMoveCopyDirectory (
  896. /* [in] */ IMethUtil* pmu,
  897. /* [in] */ LPCWSTR pwszUrl,
  898. /* [in] */ LPCWSTR pwszSrc,
  899. /* [in] */ LPCWSTR pwszUrlDst,
  900. /* [in] */ LPCWSTR pwszDst,
  901. /* [in] */ BOOL fMove,
  902. /* [in] */ DWORD dwReplace,
  903. /* [in] */ BOOL fCheckAccess,
  904. /* [in] */ BOOL fCheckDestinationAccess,
  905. /* [in] */ DWORD dwAcc,
  906. /* [in] */ CVRoot* pcvrTranslateSrc,
  907. /* [in] */ CVRoot* pcvrTranslateDst,
  908. /* [in] */ CXMLEmitter& msr,
  909. /* [in] */ LONG lDepth,
  910. /* [in] */ CParseLockTokenHeader* plth) // Usually NULL -- no locktokens to worry about
  911. {
  912. auto_ref_ptr<CVRoot> pcvrDestination(pcvrTranslateDst);
  913. ChainedStringBuffer<WCHAR> sb;
  914. LPCWSTR pwszDestinationRedirect = NULL;
  915. SCODE sc = S_OK;
  916. SCODE scItem = S_OK;
  917. std::list<LPCWSTR, heap_allocator<LPCWSTR> > lst;
  918. CDirIter di(pwszUrl,
  919. pwszSrc,
  920. pwszUrlDst,
  921. pwszDst,
  922. pcvrTranslateDst,
  923. TRUE); // Traverse into sub-directories
  924. // See if there is a path conflict
  925. //
  926. if (FPathConflict (pwszSrc, pwszDst))
  927. {
  928. DebugTrace ("Dav: source and dest are in conflict\n");
  929. sc = E_DAV_CONFLICTING_PATHS;
  930. goto ret;
  931. }
  932. // Ok, for MOVE requests where there is no blocked
  933. // access along the way we can do the whole thing
  934. // in one big shot.
  935. //
  936. // Otherwise we are going to try and do this piece-wise.
  937. //
  938. //$ REVIEW:
  939. //
  940. // Normally, we would do something like the following
  941. //
  942. // if (!fMove ||
  943. // fCheckAccess ||
  944. // fCheckDestinationAccess ||
  945. // !DavMoveFile (pwszSrc, pwszDst, dwReplace))
  946. //
  947. // However, if the above code is used, IIS holds a lock
  948. // on the moved source directory. This prevents further
  949. // access to that physical path. NtCreateFile() reports
  950. // a status of "DELETE PENDING" on the locked directory
  951. // Win32 API's report "ACCESS DENIED"
  952. //
  953. // If we do the degenerate case of always copying the root
  954. // by hand, it reduces the likely hood of the lock out.
  955. //
  956. // When this code gets checked in, a bug should be filed
  957. // against IIS over this lock issue. If and only if they
  958. // do not fix this, will we fall back to the degenerate
  959. // code.
  960. //
  961. if (!fMove ||
  962. fCheckAccess ||
  963. fCheckDestinationAccess ||
  964. !DavMoveFile (pwszSrc, pwszDst, dwReplace))
  965. //
  966. //$ REVIEW: end
  967. {
  968. // Create the destination directory
  969. //
  970. if (!DavCreateDirectory (pwszDst, NULL))
  971. {
  972. // If we have locks, and the dir is already there, it's okay.
  973. // Otherwise, return an error.
  974. //
  975. //$ LATER: Fix this to actually lookup the dir path in the
  976. // lockcache (using "fPathLookup").
  977. //
  978. if (!plth)
  979. {
  980. DWORD dw = GetLastError();
  981. if ((dw == ERROR_FILE_EXISTS) || (dw == ERROR_ALREADY_EXISTS))
  982. sc = E_DAV_OVERWRITE_REQUIRED;
  983. else
  984. sc = HRESULT_FROM_WIN32(dw);
  985. DebugTrace ("Dav: MCD: failed to create destination\n");
  986. goto ret;
  987. }
  988. }
  989. // Slurp the properties over for the root directory
  990. // We need to copy the properties first,
  991. // Note, Currently, this call must be ahead of FInstantiate,
  992. // as FInstantiate will keep the src dir open, and we won't be able to
  993. // get a IPropertyBagEx with STGM_SHARE_EXCLUSIVE, which is
  994. // required by current nt5 impl. as STGM_SHARE_SHARE_WRITE
  995. // does not work with IPropertyBagEx::Enum.
  996. //
  997. sc = ScCopyProps (pmu, pwszSrc, pwszDst, TRUE);
  998. if (FAILED(sc))
  999. {
  1000. // Do our best to remove the turd directory
  1001. //
  1002. DavRemoveDirectory (pwszDst);
  1003. goto ret;
  1004. }
  1005. // For MOVE's, push the current path
  1006. //
  1007. if (fMove)
  1008. lst.push_back (pwszSrc);
  1009. }
  1010. else // !fMove || fCheckAccess || fCheckDestinationAccess || !MoveFileEx()
  1011. {
  1012. Assert (DEPTH_INFINITY == lDepth);
  1013. // Ok, this is the cool bit. If this succeeded,
  1014. // there there is no more processing required.
  1015. //
  1016. goto ret;
  1017. }
  1018. // If we are not asked to copy internal members,
  1019. // then we are done
  1020. //
  1021. if (DEPTH_INFINITY != lDepth)
  1022. {
  1023. Assert (!fMove);
  1024. goto ret;
  1025. }
  1026. // Iterate through the directories -- copying as we go
  1027. //
  1028. while (S_OK == di.ScGetNext(!FAILED (scItem),
  1029. pwszDestinationRedirect,
  1030. pcvrDestination.get()))
  1031. {
  1032. //$ REVIEW:
  1033. //
  1034. // We have a very nasty case that we need to be
  1035. // able to handle...
  1036. //
  1037. // If a virtual root already exists along the path of
  1038. // the destination, we need to redirect out destination
  1039. // path to the vrpath of that virtual root.
  1040. //
  1041. // Reset the destination redirection
  1042. //
  1043. pwszDestinationRedirect = di.PwszDestination();
  1044. pcvrDestination = di.PvrDestination();
  1045. //
  1046. //$ REVIEW: end
  1047. // Before anything, if this is one of the specials,
  1048. // do nothing...
  1049. //
  1050. if (di.FSpecial())
  1051. continue;
  1052. if (fCheckAccess)
  1053. {
  1054. // Check our access rights and only push down
  1055. // into the directory if and only if we have access.
  1056. //
  1057. sc = ScCheckMoveCopyDeleteAccess (pmu,
  1058. di.PwszUri(),
  1059. pcvrTranslateSrc,
  1060. di.FDirectory(),
  1061. TRUE, // check scriptmaps
  1062. dwAcc,
  1063. &scItem,
  1064. msr);
  1065. if (FAILED (sc))
  1066. goto ret;
  1067. // If things were not 100%, don't process this resource
  1068. //
  1069. if (S_OK != sc)
  1070. continue;
  1071. }
  1072. if (fCheckDestinationAccess)
  1073. {
  1074. //$ REVIEW:
  1075. //
  1076. // We have a very nasty case that we need to be
  1077. // able to handle...
  1078. //
  1079. // If a virtual root already exists along the path of
  1080. // the destination, we need to redirect out destination
  1081. // path to the vrpath of that virtual root.
  1082. //
  1083. // Look for a virtual root matching the destination url,
  1084. // and set the redirect path if need be.
  1085. //
  1086. if (pmu->FFindVRootFromUrl(di.PwszUriDestination(), pcvrDestination))
  1087. {
  1088. MCDTrace ("Dav: MCD: destination url maps to virtual root\n");
  1089. // All access checking, including scriptmap honors are handled
  1090. // by ScCheckMoveCopyDeleteAccess()
  1091. //
  1092. // Redirect the destination
  1093. //
  1094. pcvrDestination->CchGetVRPath (&pwszDestinationRedirect);
  1095. }
  1096. //
  1097. //$ REVIEW: end
  1098. // Same kind of deal -- check our access rights and
  1099. // only push down into the directory if and only if
  1100. // we have access.
  1101. //
  1102. sc = ScCheckMoveCopyDeleteAccess (pmu,
  1103. di.PwszUriDestination(),
  1104. pcvrDestination.get(),
  1105. di.FDirectory(),
  1106. TRUE, // check scriptmap on dest
  1107. MD_ACCESS_WRITE,
  1108. &scItem,
  1109. msr);
  1110. if (FAILED (sc))
  1111. goto ret;
  1112. // If things were not 100%, don't process this resource
  1113. //
  1114. if (S_OK != sc)
  1115. continue;
  1116. }
  1117. MCDTrace ("Dav: MCD: moving/copying '%S' to '%S'\n",
  1118. di.PwszSource(),
  1119. pwszDestinationRedirect);
  1120. // If we are moving, then just try the generic MoveFileW(),
  1121. // and if it fails, then do it piecewise...
  1122. //
  1123. if (!fMove ||
  1124. fCheckAccess ||
  1125. fCheckDestinationAccess ||
  1126. !DavMoveFile (di.PwszSource(),
  1127. pwszDestinationRedirect,
  1128. dwReplace))
  1129. {
  1130. scItem = S_OK;
  1131. // If we found another directory, then iterate on it
  1132. //
  1133. if (di.FDirectory())
  1134. {
  1135. // We need to create the sister directory in
  1136. // the destination directory
  1137. //
  1138. if (DavCreateDirectory (pwszDestinationRedirect, NULL) || plth)
  1139. {
  1140. scItem = ScCopyProps (pmu,
  1141. di.PwszSource(),
  1142. pwszDestinationRedirect,
  1143. TRUE);
  1144. if (FAILED (scItem))
  1145. {
  1146. // Do our best to remove the turd directory
  1147. //
  1148. DavRemoveDirectory (pwszDestinationRedirect);
  1149. }
  1150. // For all MOVEs push the directory
  1151. //
  1152. if (!FAILED (scItem) && fMove)
  1153. {
  1154. lst.push_back (AppendChainedSz(sb, di.PwszSource()));
  1155. }
  1156. }
  1157. else
  1158. {
  1159. DebugTrace ("Dav: MCD: failed to create directory\n");
  1160. scItem = HRESULT_FROM_WIN32(GetLastError());
  1161. }
  1162. if (FAILED (scItem))
  1163. {
  1164. // Add to the reponse XML
  1165. //
  1166. sc = ScAddMultiFromUrl (msr,
  1167. pmu,
  1168. di.PwszUri(),
  1169. HscFromHresult(scItem),
  1170. di.FDirectory());
  1171. if (FAILED (sc))
  1172. goto ret;
  1173. sc = W_DAV_PARTIAL_SUCCESS;
  1174. }
  1175. }
  1176. else
  1177. {
  1178. // Copy the file
  1179. //
  1180. if (!DavCopyFile (di.PwszSource(),
  1181. pwszDestinationRedirect,
  1182. 0 != dwReplace))
  1183. {
  1184. DWORD dw = GetLastError();
  1185. scItem = HRESULT_FROM_WIN32(dw);
  1186. // If it's a sharing (lock) violation, AND we have a
  1187. // locktoken parser (plth) AND it has a locktoken for
  1188. // this path (lth.GetToken(pwsz)), copy it manually.
  1189. //
  1190. if (plth &&
  1191. (ERROR_SHARING_VIOLATION == dw || ERROR_FILE_EXISTS == dw))
  1192. {
  1193. scItem = ScDoLockedCopy (pmu,
  1194. plth,
  1195. di.PwszSource(),
  1196. pwszDestinationRedirect);
  1197. }
  1198. }
  1199. // In the case of a move, handle the potentially
  1200. // locked source.
  1201. //
  1202. if (!FAILED (scItem) && fMove)
  1203. {
  1204. LARGE_INTEGER liLockID;
  1205. // If we have a locktoken for this path, then we really
  1206. // want to try and release the lock for the source and
  1207. // delete the file.
  1208. //
  1209. if (plth)
  1210. {
  1211. // Find the lock...
  1212. //
  1213. scItem = plth->HrGetLockIdForPath (di.PwszSource(),
  1214. GENERIC_WRITE,
  1215. &liLockID);
  1216. if (SUCCEEDED(scItem))
  1217. {
  1218. // ... and drop it on the floor...
  1219. //
  1220. scItem = CSharedLockMgr::Instance().HrDeleteLock(pmu->HitUser(),
  1221. liLockID);
  1222. if (SUCCEEDED(scItem))
  1223. {
  1224. // ... and try to delete the source again.
  1225. //
  1226. if (!DavDeleteFile (di.PwszSource()))
  1227. {
  1228. scItem = HRESULT_FROM_WIN32(GetLastError());
  1229. }
  1230. }
  1231. }
  1232. }
  1233. else
  1234. {
  1235. // ... and try to delete the source again.
  1236. //
  1237. if (!DavDeleteFile (di.PwszSource()))
  1238. {
  1239. scItem = HRESULT_FROM_WIN32(GetLastError());
  1240. }
  1241. }
  1242. }
  1243. if (FAILED (scItem))
  1244. {
  1245. // If the file that failed to be copied was a hidden
  1246. // and/or system file, then it was more than likely a
  1247. // trigger file, but even as such, if the file has the
  1248. // hidden attribute, we don't want to partial report
  1249. // the failure.
  1250. //
  1251. if (!di.FHidden())
  1252. {
  1253. sc = ScAddMultiFromUrl (msr,
  1254. pmu,
  1255. di.PwszUri(),
  1256. HscFromHresult(scItem),
  1257. di.FDirectory(),
  1258. fMove);
  1259. if (FAILED (sc))
  1260. goto ret;
  1261. sc = W_DAV_PARTIAL_SUCCESS;
  1262. }
  1263. }
  1264. }
  1265. }
  1266. else // !fMove || fCheckAccess || fCheckDestinationAccess || !MoveFileEx()
  1267. {
  1268. // Again, this is the cool bit. If we got here, then
  1269. // there is no need to delve down into this particular
  1270. // branch of the tree.
  1271. //
  1272. // To accomplish this, we are going to lie in a gentle
  1273. // way. By setting scItem to a failure condition we are
  1274. // preventing the extra work.
  1275. //
  1276. scItem = HRESULT_FROM_WIN32(ERROR_FILE_EXISTS);
  1277. }
  1278. }
  1279. // Now that all the files are moved or copied, the pushed directories
  1280. // can be removed.
  1281. //
  1282. while (!lst.empty())
  1283. {
  1284. Assert (fMove);
  1285. MCDTrace ("Dav: MCD: removing '%S'\n", lst.back());
  1286. // Try to delete the dir. If it doesn't delete, check our
  1287. // "skipped because of a lock above" flag before complaining.
  1288. //
  1289. //$ LATER: Fix this to actually lookup the dir path in the
  1290. // lockcache (using "fPathLookup").
  1291. //
  1292. if (!DavRemoveDirectory (lst.back()))
  1293. {
  1294. DebugTrace ("Dav: MCD: failed to delete directory\n");
  1295. // Add to the reponse XML
  1296. //
  1297. sc = ScAddMulti (msr,
  1298. pmu,
  1299. lst.back(),
  1300. NULL,
  1301. HscFromLastError(GetLastError()),
  1302. TRUE, // We know it's a directory
  1303. pcvrTranslateSrc);
  1304. if (FAILED (sc))
  1305. goto ret;
  1306. sc = W_DAV_PARTIAL_SUCCESS;
  1307. }
  1308. lst.pop_back();
  1309. }
  1310. ret:
  1311. return sc;
  1312. }
  1313. SCODE
  1314. ScMoveCopyDirectoryAndChildren (
  1315. /* [in] */ IMethUtil* pmu,
  1316. /* [in] */ LPCWSTR pwszUrl,
  1317. /* [in] */ LPCWSTR pwszSrc,
  1318. /* [in] */ LPCWSTR pwszUrlDst,
  1319. /* [in] */ LPCWSTR pwszDst,
  1320. /* [in] */ BOOL fMove,
  1321. /* [in] */ DWORD dwReplace,
  1322. /* [in] */ BOOL fCheckAccess,
  1323. /* [in] */ BOOL fCheckDestinationAccess,
  1324. /* [in] */ CVRoot* pcvrTranslateDestination,
  1325. /* [in] */ DWORD dwAcc,
  1326. /* [in] */ CXMLEmitter& msr,
  1327. /* [in] */ LONG lDepth,
  1328. /* [in] */ CParseLockTokenHeader* plth) // Usually NULL -- no locktokens to worry about
  1329. {
  1330. BOOL fPartial = FALSE;
  1331. SCODE sc = S_OK;
  1332. // Move/Copy the main body first
  1333. //
  1334. MCDTrace ("Dav: copying '%S' to '%S'\n", pwszSrc, pwszDst);
  1335. sc = ScMoveCopyDirectory (pmu,
  1336. pwszUrl,
  1337. pwszSrc,
  1338. pwszUrlDst,
  1339. pwszDst,
  1340. fMove,
  1341. dwReplace,
  1342. fCheckAccess,
  1343. fCheckDestinationAccess,
  1344. dwAcc,
  1345. NULL, // translations are pmu based
  1346. pcvrTranslateDestination,
  1347. msr,
  1348. lDepth,
  1349. plth);
  1350. if (!FAILED (sc) && (lDepth != DEPTH_ZERO))
  1351. {
  1352. Assert (lDepth == DEPTH_INFINITY);
  1353. // Enumerate the child vroots and perform the
  1354. // deletion of those directories as well
  1355. //
  1356. ChainedStringBuffer<WCHAR> sb;
  1357. CVRList vrl;
  1358. UINT cchUrl = static_cast<UINT>(wcslen (pwszUrl));
  1359. UINT cchDstUrl = static_cast<UINT>(wcslen (pwszUrlDst));
  1360. UINT cchDstPath = static_cast<UINT>(wcslen (pwszDst));
  1361. // Cleanup the list such that our namespaces are in
  1362. // a reasonable order.
  1363. //
  1364. (void) pmu->ScFindChildVRoots (pwszUrl, sb, vrl);
  1365. vrl.sort();
  1366. for ( ; !FAILED(sc) && !vrl.empty(); vrl.pop_front())
  1367. {
  1368. auto_ref_ptr<CVRoot> pcvrDst;
  1369. auto_ref_ptr<CVRoot> pcvrSrc;
  1370. CResourceInfo cri;
  1371. CStackBuffer<WCHAR,128> pwszChildDstT;
  1372. LPCWSTR pwszChildDst;
  1373. LPCWSTR pwszChildPath;
  1374. LPCWSTR pwszChildUrl;
  1375. SCODE scItem;
  1376. UINT cchVRoot;
  1377. // Remember any partial returns
  1378. //
  1379. if (W_DAV_PARTIAL_SUCCESS == sc)
  1380. fPartial = TRUE;
  1381. if (pmu->FGetChildVRoot (vrl.front().m_pwsz, pcvrSrc))
  1382. {
  1383. Assert (fCheckAccess);
  1384. cchVRoot = pcvrSrc->CchGetVRoot (&pwszChildUrl);
  1385. sc = ScCheckMoveCopyDeleteAccess (pmu,
  1386. pwszChildUrl,
  1387. pcvrSrc.get(),
  1388. TRUE, // directory
  1389. TRUE, // check scriptmaps
  1390. dwAcc,
  1391. &scItem,
  1392. msr);
  1393. if (FAILED (sc))
  1394. goto ret;
  1395. // If things were not 100%, don't process this resource
  1396. //
  1397. if (S_OK != sc)
  1398. continue;
  1399. // We now have to figure out how we can really do this!
  1400. //
  1401. // The source path and url bits are easy. The destination
  1402. // path, on the other hand, is a pain. It is the original
  1403. // destination root with the delta between the source root
  1404. // and the child's url path. Huh?
  1405. //
  1406. // Ok, here is an example:
  1407. //
  1408. // Source url: /misc
  1409. // Source root: c:\inetpub\wwwroot\misc
  1410. // Dest. root: c:\inetpub\wwwroot\copy
  1411. //
  1412. // Child url: /misc/blah
  1413. //
  1414. // In this example the childs, destination path would need to
  1415. // be:
  1416. //
  1417. // Child dest.: c:\inetpub\wwwroot\copy\blah
  1418. //
  1419. //$ REVIEW:
  1420. //
  1421. // And the real pain here is that the child path could already
  1422. // exist, but not match the namespace path. I am not too sure
  1423. // how to handle that eventuality at this point.
  1424. //
  1425. Assert (cchUrl < cchVRoot);
  1426. //
  1427. // Construct the new destination url
  1428. //
  1429. UINT cchDest = cchVRoot - cchUrl + cchDstUrl + 1;
  1430. CStackBuffer<WCHAR,128> pwszChildUrlDst;
  1431. if (NULL == pwszChildUrlDst.resize(CbSizeWsz(cchDest)))
  1432. {
  1433. sc = E_OUTOFMEMORY;
  1434. goto ret;
  1435. }
  1436. memcpy (pwszChildUrlDst.get(), pwszUrlDst, cchDstUrl * sizeof(WCHAR));
  1437. memcpy (pwszChildUrlDst.get() + cchDstUrl, pwszChildUrl + cchUrl, (1 + cchDest - cchDstUrl) * sizeof(WCHAR));
  1438. if (fCheckDestinationAccess)
  1439. {
  1440. sc = ScCheckMoveCopyDeleteAccess (pmu,
  1441. pwszChildUrlDst.get(),
  1442. pcvrSrc.get(),
  1443. TRUE, // directory
  1444. TRUE, // check scriptmap on dest
  1445. MD_ACCESS_WRITE,
  1446. &scItem,
  1447. msr);
  1448. if (FAILED (sc))
  1449. goto ret;
  1450. // If things were not 100%, don't process this resource
  1451. //
  1452. if (S_OK != sc)
  1453. continue;
  1454. }
  1455. // And now that we have perfomed the forbidden dance... we
  1456. // have to go back and see if the destination url actually
  1457. // refers to a new child virtual root as well.
  1458. //
  1459. if (pmu->FFindVRootFromUrl (pwszChildUrlDst.get(), pcvrDst))
  1460. {
  1461. MCDTrace ("Dav: MCD: destination url maps to virtual root\n");
  1462. // Access checking, as always is handled in ScCheckM/C/DAccess()
  1463. // So all we need to do here is setup the destination path
  1464. //
  1465. pcvrDst->CchGetVRPath (&pwszChildDst);
  1466. }
  1467. else
  1468. {
  1469. // We actually need to construct a physical path from
  1470. // the url and current destination path.
  1471. //
  1472. cchDest = cchDstPath + cchVRoot - cchUrl + 1;
  1473. if (NULL == pwszChildDstT.resize(CbSizeWsz(cchDest)))
  1474. {
  1475. sc = E_OUTOFMEMORY;
  1476. goto ret;
  1477. }
  1478. memcpy (pwszChildDstT.get(), pwszDst, cchDstPath * sizeof(WCHAR));
  1479. memcpy (pwszChildDstT.get() + cchDstPath, pwszChildUrl + cchUrl, (cchVRoot - cchUrl) * sizeof(WCHAR));
  1480. pwszChildDstT[cchDstPath + cchVRoot - cchUrl] = L'\0';
  1481. // We also now need to rip through the trailing part of the
  1482. // path one more time, translating all '/' to '\\' as we go.
  1483. //
  1484. for (WCHAR* pwch = pwszChildDstT.get() + cchDstPath;
  1485. NULL != (pwch = wcschr (pwch, L'/'));
  1486. )
  1487. {
  1488. *pwch++ = L'\\';
  1489. }
  1490. pwszChildDst = pwszChildDstT.get();
  1491. }
  1492. // Well, now we should be able to continue the MOVE/COPY
  1493. //
  1494. pcvrSrc->CchGetVRPath (&pwszChildPath);
  1495. sc = ScMoveCopyDirectory (pmu,
  1496. pwszChildUrl,
  1497. pwszChildPath,
  1498. pwszChildUrlDst.get(),
  1499. pwszChildDst,
  1500. fMove,
  1501. dwReplace,
  1502. fCheckAccess,
  1503. fCheckDestinationAccess,
  1504. dwAcc,
  1505. pcvrSrc.get(),
  1506. pcvrDst.get(),
  1507. msr,
  1508. DEPTH_INFINITY,
  1509. plth);
  1510. if (FAILED (sc))
  1511. {
  1512. sc = ScAddMultiFromUrl (msr,
  1513. pmu,
  1514. pwszChildUrl,
  1515. HscFromHresult(sc),
  1516. TRUE); // We know it's a directory
  1517. if (FAILED (sc))
  1518. goto ret;
  1519. sc = W_DAV_PARTIAL_SUCCESS;
  1520. }
  1521. }
  1522. }
  1523. }
  1524. ret:
  1525. return ((S_OK == sc) && fPartial) ? W_DAV_PARTIAL_SUCCESS : sc;
  1526. }
  1527. // Move/Copy -----------------------------------------------------------------
  1528. //
  1529. void
  1530. MoveCopyResource (LPMETHUTIL pmu, DWORD dwAccRequired, BOOL fDeleteSrc)
  1531. {
  1532. auto_ptr<CParseLockTokenHeader> plth;
  1533. auto_ref_ptr<CXMLBody> pxb;
  1534. auto_ref_ptr<CXMLEmitter> pxml;
  1535. BOOL fCheckDestination = FALSE;
  1536. BOOL fCheckSource = FALSE;
  1537. BOOL fCreateNew = TRUE;
  1538. BOOL fDestinationExists = TRUE; // IMPORTANT: assume exists for location header processing
  1539. CResourceInfo criDst;
  1540. CResourceInfo criSrc;
  1541. CStackBuffer<WCHAR> pwszMBPathDst;
  1542. CStackBuffer<WCHAR> pwszMBPathSrc;
  1543. CVRoot* pcvrDestination;
  1544. DWORD dwAccDest = MD_ACCESS_WRITE;
  1545. DWORD dwReplace = 0;
  1546. LONG lDepth;
  1547. LPCWSTR pwsz;
  1548. LPCWSTR pwszDst = NULL;
  1549. LPCWSTR pwszDstUrl = NULL;
  1550. LPCWSTR pwszSrc = pmu->LpwszPathTranslated();
  1551. LPCWSTR pwszSrcUrl = pmu->LpwszRequestUrl();
  1552. SCODE sc = S_OK;
  1553. SCODE scDest = S_OK;
  1554. UINT cch;
  1555. UINT uiErrorDetail = 0;
  1556. // We don't know if we'll have chunked XML response, defer response anyway
  1557. //
  1558. pmu->DeferResponse();
  1559. // Create an XML doc, NOT chunked
  1560. //
  1561. pxb.take_ownership (new CXMLBody(pmu));
  1562. pxml.take_ownership (new CXMLEmitter(pxb.get()));
  1563. // Must set all headers before XML emitting starts
  1564. //
  1565. pmu->SetResponseHeader (gc_szContent_Type, gc_szText_XML);
  1566. pmu->SetResponseCode (HscFromHresult(W_DAV_PARTIAL_SUCCESS),
  1567. NULL,
  1568. 0,
  1569. CSEFromHresult(W_DAV_PARTIAL_SUCCESS));
  1570. // Do ISAPI application and IIS access bits checking on source
  1571. //
  1572. sc = pmu->ScIISCheck (pmu->LpwszRequestUrl(), dwAccRequired);
  1573. if (FAILED(sc))
  1574. {
  1575. // Either the request has been forwarded, or some bad error occurred.
  1576. // In either case, quit here and map the error!
  1577. //
  1578. MCDTrace ("Dav: Move/Copy: insufficient access\n");
  1579. goto ret;
  1580. }
  1581. // If there's no valid destination header, it's a bad request.
  1582. //
  1583. // NOTE: we are asking for the translated url's virtual root if
  1584. // it exists. The ECB holds the reference for us, so we do not
  1585. // add one or release the one we have!
  1586. //
  1587. sc = pmu->ScGetDestination (&pwszDstUrl, &pwszDst, &cch, &pcvrDestination);
  1588. if (FAILED (sc))
  1589. {
  1590. MCDTrace ("Dav: Move/Copy: no and/or bad destination header\n");
  1591. if (sc != E_DAV_NO_DESTINATION)
  1592. {
  1593. Assert (pwszDstUrl);
  1594. sc = ScAddMultiFromUrl (*pxml,
  1595. pmu,
  1596. pwszDstUrl,
  1597. HscFromHresult(sc),
  1598. FALSE); // do not check for trailing slash
  1599. if (!FAILED (sc))
  1600. sc = W_DAV_PARTIAL_SUCCESS;
  1601. }
  1602. goto ret;
  1603. }
  1604. // Get the file attributes for the passed in URI. If it aint there, then
  1605. // don't do jack!
  1606. //
  1607. sc = criSrc.ScGetResourceInfo (pwszSrc);
  1608. if (FAILED (sc))
  1609. goto ret;
  1610. // Get the metabase for the source and destination for later use
  1611. //
  1612. if ((NULL == pwszMBPathSrc.resize(pmu->CbMDPathW(pwszSrcUrl))) ||
  1613. (NULL == pwszMBPathDst.resize(pmu->CbMDPathW(pwszDstUrl))))
  1614. {
  1615. sc = E_OUTOFMEMORY;
  1616. goto ret;
  1617. }
  1618. pmu->MDPathFromUrlW (pwszSrcUrl, pwszMBPathSrc.get());
  1619. pmu->MDPathFromUrlW (pwszDstUrl, pwszMBPathDst.get());
  1620. // Get the resource info for the destination up front
  1621. //
  1622. sc = criDst.ScGetResourceInfo (pwszDst);
  1623. if (FAILED (sc))
  1624. {
  1625. MCDTrace ("Dav: Move/Copy: destination probably did not exist prior to op\n");
  1626. // The destination may or may not exist. We will just act like
  1627. // it doesn't. However, if we don't have access, then we want to
  1628. // stick the error into a 207 body.
  1629. //
  1630. fDestinationExists = FALSE;
  1631. if ((HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED) == sc))
  1632. {
  1633. sc = ScAddMultiFromUrl (*pxml,
  1634. pmu,
  1635. pwszDstUrl,
  1636. HscFromHresult(sc),
  1637. FALSE); // do not check for trailing slash
  1638. if (!FAILED (sc))
  1639. sc = W_DAV_PARTIAL_SUCCESS;
  1640. goto ret;
  1641. }
  1642. }
  1643. // Again, emit all the headers before XML chunking starts
  1644. //
  1645. if (!fDestinationExists)
  1646. {
  1647. Assert (pxml->PxnRoot() == NULL);
  1648. //$NOTE At this time, we have only the destination URL, the destination
  1649. //$NOTE is not created yet, but we do know whether it will be created
  1650. //$NOTE as a collection by looking at the source
  1651. //
  1652. pmu->EmitLocation (gc_szLocation, pwszDstUrl, criSrc.FCollection());
  1653. }
  1654. //$ SECURITY:
  1655. //
  1656. // Check to see if the destination is really a short
  1657. // filename.
  1658. //
  1659. sc = ScCheckIfShortFileName (pwszDst, pmu->HitUser());
  1660. if (FAILED (sc))
  1661. {
  1662. DebugTrace ("Dav: MCD: destination is short-filename\n");
  1663. sc = ScAddMultiFromUrl (*pxml,
  1664. pmu,
  1665. pwszDstUrl,
  1666. HscFromHresult(sc),
  1667. FALSE); // do not check for trailing slash
  1668. if (!FAILED (sc))
  1669. sc = W_DAV_PARTIAL_SUCCESS;
  1670. goto ret;
  1671. }
  1672. //
  1673. //$ SECURITY: end.
  1674. //$ SECURITY:
  1675. //
  1676. // Check to see if the destination is really the default
  1677. // data stream via alternate file access.
  1678. //
  1679. sc = ScCheckForAltFileStream (pwszDst);
  1680. if (FAILED (sc))
  1681. {
  1682. DebugTrace ("Dav: MCD: destination is possible alternate filestream\n");
  1683. sc = ScAddMultiFromUrl (*pxml,
  1684. pmu,
  1685. pwszDstUrl,
  1686. HscFromHresult(sc),
  1687. FALSE); // do not check for trailing slash
  1688. if (!FAILED (sc))
  1689. sc = W_DAV_PARTIAL_SUCCESS;
  1690. goto ret;
  1691. }
  1692. //
  1693. //$ SECURITY: end.
  1694. // See if we have move/copy access at destination
  1695. //
  1696. if (fDestinationExists && criDst.FCollection())
  1697. dwAccDest |= MD_ACCESS_READ;
  1698. sc = ScCheckMoveCopyDeleteAccess (pmu,
  1699. pwszDstUrl,
  1700. pcvrDestination,
  1701. fDestinationExists
  1702. ? criDst.FCollection()
  1703. : criSrc.FCollection(),
  1704. TRUE, // check scriptmaps on dest.
  1705. dwAccDest,
  1706. &scDest,
  1707. *pxml);
  1708. if (sc != S_OK)
  1709. goto ret;
  1710. // The client must not submit a depth header with any value
  1711. // but Infinity
  1712. //
  1713. lDepth = pmu->LDepth (DEPTH_INFINITY);
  1714. if (DEPTH_INFINITY != lDepth)
  1715. {
  1716. if (fDeleteSrc || (DEPTH_ZERO != lDepth))
  1717. {
  1718. MCDTrace ("Dav: only 'Depth: inifinity' is allowed for MOVE\n"
  1719. "- 'Depth: inifinity' and 'Depth: 0' are allowed for COPY\n");
  1720. sc = E_DAV_INVALID_HEADER;
  1721. goto ret;
  1722. }
  1723. }
  1724. // See if there is a path conflict
  1725. //
  1726. if (FPathConflict (pwszSrc, pwszDst))
  1727. {
  1728. DebugTrace ("Dav: source and dest are in conflict\n");
  1729. sc = E_DAV_CONFLICTING_PATHS;
  1730. goto ret;
  1731. }
  1732. // If we were to check either URI for correctness, the only
  1733. // real result would be to possibly emit a content-location
  1734. // header that would only be invalidated in the case of a
  1735. // successful move
  1736. //
  1737. if (!fDeleteSrc)
  1738. {
  1739. sc = ScCheckForLocationCorrectness (pmu, criSrc, NO_REDIRECT);
  1740. if (FAILED (sc))
  1741. goto ret;
  1742. }
  1743. // This method is gated by If-xxx headers
  1744. //
  1745. sc = ScCheckIfHeaders (pmu, criSrc.PftLastModified(), FALSE);
  1746. if (FAILED (sc))
  1747. goto ret;
  1748. // Check state headers
  1749. //
  1750. sc = HrCheckStateHeaders (pmu, pwszSrc, FALSE);
  1751. if (FAILED (sc))
  1752. {
  1753. DebugTrace ("DavFS: If-State checking failed.\n");
  1754. goto ret;
  1755. }
  1756. // If there are locktokens, feed them to a parser object.
  1757. //
  1758. pwsz = pmu->LpwszGetRequestHeader (gc_szLockToken, TRUE);
  1759. if (pwsz)
  1760. {
  1761. plth = new CParseLockTokenHeader (pmu, pwsz);
  1762. Assert(plth.get());
  1763. plth->SetPaths (pwszSrc, pwszDst);
  1764. }
  1765. // Check for deep access issues
  1766. //
  1767. //$ REVIEW: we wanted to be able to not have to check
  1768. // access at each level. However, because of the semantics of
  1769. // MOVE/COPY, we have to check each source file for scriptmap
  1770. // access. We cannot copy a file that has a scriptmap if the
  1771. // they do not have source access.
  1772. //
  1773. // So we must always check the source of the MOVE/COPY operation.
  1774. //
  1775. fCheckSource = TRUE;
  1776. //
  1777. //$ REVIEW: end.
  1778. //
  1779. // However, we still can try and be optimistic for the destination
  1780. //
  1781. if (NULL == pwszMBPathDst.resize(pmu->CbMDPathW(pwszDstUrl)))
  1782. {
  1783. sc = E_OUTOFMEMORY;
  1784. goto ret;
  1785. }
  1786. pmu->MDPathFromUrlW (pwszDstUrl, pwszMBPathDst.get());
  1787. if (fDestinationExists || (DEPTH_ONE != pmu->LDepth(DEPTH_INFINITY)))
  1788. {
  1789. CAccessMetaOp moAccess(pmu, pwszMBPathDst.get(), dwAccDest);
  1790. CAuthMetaOp moAuth(pmu, pwszMBPathDst.get(), pmu->MetaData().DwAuthorization());
  1791. CIPRestrictionMetaOp moIPRestriction(pmu, pwszMBPathDst.get());
  1792. ChainedStringBuffer<WCHAR> sb;
  1793. CVRList vrl;
  1794. // If we do not have access to COPY/MOVE or
  1795. // DELETE anything in the destination, then
  1796. // we really shouldn't blindly proceed.
  1797. //
  1798. sc = moAccess.ScMetaOp();
  1799. if (FAILED (sc))
  1800. goto ret;
  1801. fCheckDestination |= moAccess.FAccessBlocked();
  1802. if (!fCheckDestination)
  1803. {
  1804. // If we do not have the same authorization anywhere along
  1805. // the destination as we do for the request url, then we
  1806. // really shouldn't blindly proceed.
  1807. //
  1808. sc = moAuth.ScMetaOp();
  1809. if (FAILED (sc))
  1810. goto ret;
  1811. fCheckDestination |= moAuth.FAccessBlocked();
  1812. }
  1813. if (!fCheckDestination)
  1814. {
  1815. // If we do not have the same authorization anywhere along
  1816. // the destination as we do for the request url, then we
  1817. // really shouldn't blindly proceed.
  1818. //
  1819. sc = moIPRestriction.ScMetaOp();
  1820. if (FAILED (sc))
  1821. goto ret;
  1822. fCheckDestination |= moAuth.FAccessBlocked();
  1823. }
  1824. if (!fCheckDestination)
  1825. {
  1826. // If there are any child virtual roots along
  1827. // the destination tree, there is some redirection
  1828. // that may need to happen as well.
  1829. //
  1830. (void) pmu->ScFindChildVRoots (pwszDstUrl, sb, vrl);
  1831. fCheckDestination |= !vrl.empty();
  1832. }
  1833. }
  1834. // Determine if we are destructive or not.
  1835. //
  1836. if (pmu->LOverwrite() & OVERWRITE_YES)
  1837. {
  1838. dwReplace |= MOVEFILE_REPLACE_EXISTING;
  1839. // MoveFileEx does not seem to want to replace existing
  1840. // directories.. It returns E_ACCESS_DENIED so we delete
  1841. // the existing directory ourselves.
  1842. //
  1843. if (fDestinationExists)
  1844. {
  1845. BOOL fDeletedDestination;
  1846. // The destination exists already
  1847. //
  1848. fCreateNew = FALSE;
  1849. // If the destination is a directory, delete it.
  1850. //
  1851. if (criDst.FCollection())
  1852. {
  1853. // Otherwise, go ahead and delete the directory currently at dest.
  1854. //
  1855. sc = ScDeleteDirectoryAndChildren (pmu,
  1856. pwszDstUrl,
  1857. pwszDst,
  1858. fCheckDestination,
  1859. dwAccDest,
  1860. DEPTH_INFINITY,
  1861. *pxml,
  1862. pcvrDestination,
  1863. &fDeletedDestination,
  1864. plth.get(), // DO use locktokens, if any exist.
  1865. FALSE); // Do NOT drop locks. Just skip them.
  1866. if (sc != S_OK)
  1867. {
  1868. DebugTrace("DavFS: MOVE failed to pre-delete destination directory.\n");
  1869. goto ret;
  1870. }
  1871. }
  1872. else
  1873. {
  1874. // If the destination is locked (ERROR_SHARING_VIOLATION),
  1875. // DO NOT catch it here. We'll handle it below....
  1876. //
  1877. if (!DavDeleteFile (pwszDst))
  1878. {
  1879. DWORD dw = GetLastError();
  1880. if (ERROR_ACCESS_DENIED == dw)
  1881. {
  1882. sc = HRESULT_FROM_WIN32(dw);
  1883. goto ret;
  1884. }
  1885. }
  1886. }
  1887. }
  1888. }
  1889. // Do the move/copy. If the operation is either a move, or the source
  1890. // is a collection, then call out to do the diry work.
  1891. //
  1892. MCDTrace ("DavFS: MCD: moving copying '%S' to '%S'\n", pwszSrc, pwszDst);
  1893. if (criSrc.FCollection())
  1894. {
  1895. sc = ScMoveCopyDirectoryAndChildren (pmu,
  1896. pwszSrcUrl,
  1897. pwszSrc,
  1898. pwszDstUrl,
  1899. pwszDst,
  1900. fDeleteSrc,
  1901. dwReplace,
  1902. fCheckSource,
  1903. fCheckDestination,
  1904. pcvrDestination,
  1905. dwAccRequired,
  1906. *pxml,
  1907. lDepth,
  1908. plth.get());
  1909. if (FAILED (sc))
  1910. goto ret;
  1911. }
  1912. else
  1913. {
  1914. // Well this should be the move/copy of a single file
  1915. //
  1916. if (!fDeleteSrc || !DavMoveFile (pwszSrc, pwszDst, dwReplace))
  1917. {
  1918. if (!DavCopyFile (pwszSrc, pwszDst, (0 == dwReplace)))
  1919. {
  1920. DWORD dw = GetLastError();
  1921. DebugTrace ("Dav: failed to copy file\n");
  1922. // If it's a sharing violation (lock-caused error),
  1923. // AND we have a locktoken parser (plth), handle the copy.
  1924. //
  1925. if ((ERROR_SHARING_VIOLATION == dw) && plth.get())
  1926. {
  1927. // Check if any locktokens apply to these file,
  1928. // and try the copy using the locks from the cache.
  1929. //
  1930. sc = ScDoLockedCopy (pmu, plth.get(), pwszSrc, pwszDst);
  1931. }
  1932. else
  1933. {
  1934. if ((dw == ERROR_FILE_EXISTS) ||
  1935. (dw == ERROR_ALREADY_EXISTS))
  1936. {
  1937. sc = E_DAV_OVERWRITE_REQUIRED;
  1938. }
  1939. else
  1940. sc = HRESULT_FROM_WIN32(dw);
  1941. }
  1942. // If the file-manual-move failed, we'll hit here.
  1943. //
  1944. if (FAILED (sc))
  1945. {
  1946. DebugTrace("Dav: MCD: move/copy failed. Looking for lock conflicts.\n");
  1947. // Special work for '423 Locked' responses -- fetch the
  1948. // comment & set that as the response body.
  1949. //
  1950. if (FLockViolation (pmu, sc, pwszSrc,
  1951. GENERIC_READ | GENERIC_WRITE))
  1952. {
  1953. sc = E_DAV_LOCKED;
  1954. goto ret;
  1955. }
  1956. else
  1957. {
  1958. // Test the destination too.
  1959. // However, if the dest is locked, do NOT add
  1960. // the lockinfo as the body -- we have to list the dest
  1961. // URI as the problem, so we need to have a multi-status
  1962. // body, and we put a plain 423 Locked node under there.
  1963. // (NOTE: Yes, this means we can't use FLockViolation.
  1964. // Instead, we have to check "by hand".)
  1965. //
  1966. if (CSharedLockMgr::Instance().FGetLockOnError (
  1967. pmu,
  1968. pwszDst,
  1969. GENERIC_READ | GENERIC_WRITE))
  1970. {
  1971. sc = ScAddMultiFromUrl (*pxml,
  1972. pmu,
  1973. pwszDstUrl,
  1974. HscFromHresult(E_DAV_LOCKED),
  1975. FALSE); // We know it's not a directory
  1976. if (!FAILED (sc))
  1977. sc = W_DAV_PARTIAL_SUCCESS;
  1978. goto ret;
  1979. }
  1980. }
  1981. }
  1982. } // end !DavCopyFile
  1983. if (SUCCEEDED(sc) && fDeleteSrc)
  1984. {
  1985. // Delete the source file by hand.
  1986. // (fDeleteSrc means this is a MOVE, not a COPY.)
  1987. //
  1988. // Move the content-types only if the source is
  1989. // deleted, otherwise treat it as a copy the of
  1990. // the content-type
  1991. //
  1992. if (!DavDeleteFile (pwszSrc))
  1993. {
  1994. DWORD dwLastError = GetLastError();
  1995. DebugTrace ("Dav: failed to delete file (%d)\n", dwLastError);
  1996. // If it's a sharing (lock) violation, AND we have a
  1997. // locktoken for this path (lth.GetToken(pwsz))
  1998. // skip this path.
  1999. //
  2000. if ((ERROR_SHARING_VIOLATION == dwLastError) && plth)
  2001. {
  2002. LARGE_INTEGER liLockID;
  2003. // If we have a locktoken for this path, drop
  2004. // the lock and try to delete the source again.
  2005. //
  2006. if (SUCCEEDED(plth->HrGetLockIdForPath (pwszSrc,
  2007. GENERIC_WRITE,
  2008. &liLockID)))
  2009. {
  2010. // This item is locked in our cache.
  2011. // We are doing a MOVE, so DO delete the lock
  2012. // and try again.
  2013. //
  2014. if (SUCCEEDED(CSharedLockMgr::Instance().HrDeleteLock(pmu->HitUser(),
  2015. liLockID)))
  2016. {
  2017. // Try the delete again, and set/clear our error
  2018. // code for testing below.
  2019. // This error code will control whether we
  2020. // add this error to our XML.
  2021. //
  2022. if (DavDeleteFile(pwszSrc))
  2023. {
  2024. dwLastError = ERROR_SUCCESS;
  2025. }
  2026. else
  2027. {
  2028. dwLastError = GetLastError();
  2029. }
  2030. }
  2031. }
  2032. // else, record the error in our XML.
  2033. //
  2034. }
  2035. if (ERROR_SUCCESS != dwLastError)
  2036. {
  2037. // We could not work around all the errors.
  2038. // Add this failure to the XML.
  2039. //
  2040. sc = ScAddMultiFromUrl (*pxml,
  2041. pmu,
  2042. pwszSrcUrl,
  2043. HscFromLastError(dwLastError),
  2044. FALSE); // We know it's not a directory
  2045. if (FAILED (sc))
  2046. goto ret;
  2047. // It is partial sucess if we are here. And do not fail out
  2048. // yet as we still need to take care of content types.
  2049. //
  2050. sc = W_DAV_PARTIAL_SUCCESS;
  2051. }
  2052. }
  2053. }
  2054. }
  2055. }
  2056. // Now that we're done mucking around in the filesystem,
  2057. // muck around in the metabase.
  2058. // (Delete any destination content-types, then copy/move
  2059. // the source content-types on over.)
  2060. //
  2061. // Delete the content-types for the destination
  2062. //
  2063. {
  2064. Assert (pwszMBPathDst.get());
  2065. CContentTypeMetaOp amoContent(pmu, pwszMBPathDst.get(), NULL, TRUE);
  2066. (void) amoContent.ScMetaOp();
  2067. }
  2068. // Move/copy the content-type
  2069. //
  2070. //$ REVIEW: I am not so sure what can be done when this fails
  2071. //
  2072. {
  2073. Assert (pwszMBPathDst.get());
  2074. // Only delete the source content-types if everything has been 100%
  2075. // successfull up to this point.
  2076. //
  2077. CContentTypeMetaOp amoContent(pmu,
  2078. pwszMBPathSrc.get(),
  2079. pwszMBPathDst.get(),
  2080. (fDeleteSrc && (S_OK == sc)));
  2081. (void) amoContent.ScMetaOp ();
  2082. }
  2083. //
  2084. //$ REVIEW: end.
  2085. ret:
  2086. if (pxml.get() && pxml->PxnRoot())
  2087. {
  2088. pxml->Done();
  2089. // No more header can be sent after XML chunking started
  2090. }
  2091. else
  2092. {
  2093. if (SUCCEEDED (sc))
  2094. sc = fCreateNew ? W_DAV_CREATED : W_DAV_NO_CONTENT;
  2095. pmu->SetResponseCode (HscFromHresult(sc), NULL, uiErrorDetail, CSEFromHresult(sc));
  2096. }
  2097. pmu->SendCompleteResponse();
  2098. }
  2099. /*
  2100. * DAVMove()
  2101. *
  2102. * Purpose:
  2103. *
  2104. * Win32 file system implementation of the DAV MOVE method. The
  2105. * MOVE method results in the moving of a resource from one location
  2106. * to another. The response is used to indicate the success of the
  2107. * call.
  2108. *
  2109. * Parameters:
  2110. *
  2111. * pmu [in] pointer to the method utility object
  2112. *
  2113. * Notes:
  2114. *
  2115. * In the file system implementation, the MOVE method maps directly
  2116. * to the Win32 RenameFile() method.
  2117. */
  2118. void
  2119. DAVMove (LPMETHUTIL pmu)
  2120. {
  2121. MoveCopyResource (pmu,
  2122. MD_ACCESS_READ|MD_ACCESS_WRITE, // src access required
  2123. TRUE); // fDeleteSource
  2124. }
  2125. /*
  2126. * DAVCopy()
  2127. *
  2128. * Purpose:
  2129. *
  2130. * Win32 file system implementation of the DAV COPY method. The
  2131. * COPY method results in the copying of a resource from one location
  2132. * to another. The response is used to indicate the success of the
  2133. * call.
  2134. *
  2135. * Parameters:
  2136. *
  2137. * pmu [in] pointer to the method utility object
  2138. *
  2139. * Notes:
  2140. *
  2141. * In the file system implementation, the COPY method maps directly
  2142. * to the Win32 CopyFile() API for a single file. Directory copies
  2143. * are done via a custom process.
  2144. */
  2145. void
  2146. DAVCopy (LPMETHUTIL pmu)
  2147. {
  2148. MoveCopyResource (pmu,
  2149. MD_ACCESS_READ, // src access required
  2150. FALSE); // fDeleteSource
  2151. }