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.

1301 lines
45 KiB

  1. /*++
  2. Copyright (c) 1999 Microsoft Corporation
  3. Module Name:
  4. querydir.c
  5. Abstract:
  6. This module implements the user mode DAV miniredir routine(s) pertaining to
  7. the QueryDirectory call.
  8. Author:
  9. Rohan Kumar [RohanK] 20-Sept-1999
  10. Revision History:
  11. --*/
  12. #include "pch.h"
  13. #pragma hdrstop
  14. #include "ntumrefl.h"
  15. #include "usrmddav.h"
  16. #include "global.h"
  17. #include "nodefac.h"
  18. #include "UniUtf.h"
  19. #define MSN_SPACE_FAKE_DELTA 52428800 // 50 MB
  20. ULONG
  21. DavFsQueryDirectory(
  22. PDAV_USERMODE_WORKITEM DavWorkItem
  23. )
  24. /*++
  25. Routine Description:
  26. This routine handles QueryDirectory requests for the DAV Mini-Redir that
  27. get reflected from the kernel.
  28. Arguments:
  29. DavWorkItem - The buffer that contains the request parameters and options.
  30. Return Value:
  31. The return status for the operation
  32. --*/
  33. {
  34. ULONG WStatus = ERROR_SUCCESS;
  35. PDAV_USERMODE_QUERYDIR_REQUEST QueryDirRequest;
  36. PWCHAR ServerName = NULL, DirectoryPath = NULL, CanName = NULL;
  37. ULONG_PTR CallBackContext = (ULONG_PTR)0;
  38. BOOL EnCriSec = FALSE, ReturnVal, CallBackContextInitialized = FALSE;
  39. ULONG ServerID;
  40. PPER_USER_ENTRY PerUserEntry = NULL;
  41. PHASH_SERVER_ENTRY ServerHashEntry = NULL;
  42. HINTERNET DavConnHandle, DavOpenHandle;
  43. BOOL didImpersonate = FALSE;
  44. PUMRX_USERMODE_WORKITEM_HEADER UserWorkItem = NULL;
  45. BOOL BStatus = FALSE;
  46. //
  47. // Get the request and response buffer pointers from the DavWorkItem.
  48. //
  49. QueryDirRequest = &(DavWorkItem->QueryDirRequest);
  50. //
  51. // Check to see if we have already created the DavFileAttributes list. If
  52. // we have, we are already done and just need to return.
  53. //
  54. if (QueryDirRequest->AlreadyDone) {
  55. DavPrint((DEBUG_MISC,
  56. "DavFsQueryDirectory: DavFileAttributes already created.\n"));
  57. WStatus = ERROR_SUCCESS;
  58. goto EXIT_THE_FUNCTION;
  59. }
  60. //
  61. // The first character is a '\' which has to be stripped.
  62. //
  63. ServerName = &(QueryDirRequest->ServerName[1]);
  64. if (!ServerName) {
  65. DavPrint((DEBUG_ERRORS, "DavFsQueryDirectory: ServerName is NULL.\n"));
  66. WStatus = ERROR_INVALID_PARAMETER; // STATUS_INVALID_PARAMETER;
  67. goto EXIT_THE_FUNCTION;
  68. }
  69. DavPrint((DEBUG_MISC,
  70. "DavFsQueryDirectory: ServerName = %ws.\n", ServerName));
  71. ServerID = QueryDirRequest->ServerID;
  72. DavPrint((DEBUG_MISC, "DavFsQueryDirectory: ServerID = %d.\n", ServerID));
  73. //
  74. // The first character is a '\' which has to be stripped.
  75. //
  76. DirectoryPath = &(QueryDirRequest->PathName[1]);
  77. if (!DirectoryPath) {
  78. DavPrint((DEBUG_ERRORS, "DavFsQueryDirectory: DirectoryPath is NULL.\n"));
  79. WStatus = ERROR_INVALID_PARAMETER; // STATUS_INVALID_PARAMETER;
  80. goto EXIT_THE_FUNCTION;
  81. }
  82. DavPrint((DEBUG_MISC,
  83. "DavFsQueryDirectory: DirectoryPath = %ws.\n", DirectoryPath));
  84. //
  85. // The DirectoryPath can contain \ characters. Replace them by / characters.
  86. //
  87. CanName = DirectoryPath;
  88. while (*CanName) {
  89. if (*CanName == L'\\') {
  90. *CanName = L'/';
  91. }
  92. CanName++;
  93. }
  94. //
  95. // If we have a dummy share name in the DirectoryPath, we need to remove it
  96. // right now before we contact the server.
  97. //
  98. DavRemoveDummyShareFromFileName(DirectoryPath);
  99. //
  100. // If there are no wild cards, we set the depth of the DAV request to 0,
  101. // otherwise, we set the depth to 1.
  102. //
  103. DavWorkItem->AsyncQueryDirectoryCall.NoWildCards = QueryDirRequest->NoWildCards;
  104. DavPrint((DEBUG_MISC,
  105. "DavFsQueryDirectory: NoWildCards = %d.\n", QueryDirRequest->NoWildCards));
  106. DavPrint((DEBUG_MISC,
  107. "DavFsQueryDirectory: LogonId.LowPart = %08lx.\n",
  108. QueryDirRequest->LogonID.LowPart));
  109. DavPrint((DEBUG_MISC,
  110. "DavFsQueryDirectory: LogonId.HighPart = %08lx.\n",
  111. QueryDirRequest->LogonID.HighPart));
  112. UserWorkItem = (PUMRX_USERMODE_WORKITEM_HEADER)DavWorkItem;
  113. //
  114. // If we are using WinInet synchronously, then we need to impersonate the
  115. // clients context now.
  116. //
  117. WStatus = UMReflectorImpersonate(UserWorkItem, DavWorkItem->ImpersonationHandle);
  118. if (WStatus != ERROR_SUCCESS) {
  119. DavPrint((DEBUG_ERRORS,
  120. "DavFsQueryDirectory/UMReflectorImpersonate. Error Val = %d\n",
  121. WStatus));
  122. goto EXIT_THE_FUNCTION;
  123. }
  124. didImpersonate = TRUE;
  125. //
  126. // Allocate memory for the INTERNET_ASYNC_RESULT structure.
  127. //
  128. DavWorkItem->AsyncResult = LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT,
  129. sizeof(INTERNET_ASYNC_RESULT));
  130. if (DavWorkItem->AsyncResult == NULL) {
  131. WStatus = GetLastError();
  132. DavPrint((DEBUG_ERRORS,
  133. "DavFsQueryDirectory/LocalAlloc. Error Val = %d\n",
  134. WStatus));
  135. goto EXIT_THE_FUNCTION;
  136. }
  137. //
  138. // A User Entry for this user must have been created during the create call
  139. // earlier. The user entry contains the handle used to send an HttpOpen
  140. // request.
  141. //
  142. EnterCriticalSection( &(HashServerEntryTableLock) );
  143. EnCriSec = TRUE;
  144. ReturnVal = DavDoesUserEntryExist(ServerName,
  145. ServerID,
  146. &(QueryDirRequest->LogonID),
  147. &PerUserEntry,
  148. &ServerHashEntry);
  149. //
  150. // If the following request in the kernel get cancelled even before the
  151. // corresponding usermode thread gets a chance to execute this code, then
  152. // it possible that the VNetRoot (hence the PerUserEntry) and SrvCall get
  153. // finalized before the thread that is handling the create comes here. This
  154. // could happen if this request was the only one for this share and the
  155. // server as well. This is why we need to check if the ServerHashEntry and
  156. // the PerUserEntry are valid before proceeding.
  157. //
  158. if (ReturnVal == FALSE || ServerHashEntry == NULL || PerUserEntry == NULL) {
  159. WStatus = ERROR_CANCELLED;
  160. DavPrint((DEBUG_ERRORS, "DavFsQueryDirectory: (ServerHashEntry == NULL || PerUserEntry == NULL)\n"));
  161. goto EXIT_THE_FUNCTION;
  162. }
  163. DavWorkItem->AsyncQueryDirectoryCall.ServerHashEntry = ServerHashEntry;
  164. DavWorkItem->AsyncQueryDirectoryCall.PerUserEntry = PerUserEntry;
  165. DavPrint((DEBUG_MISC,
  166. "DavFsQueryDirectory: PerUserEntry = %08lx.\n",
  167. PerUserEntry));
  168. //
  169. // Add a reference to the user entry.
  170. //
  171. PerUserEntry->UserEntryRefCount++;
  172. //
  173. // Since a create had succeeded earlier, the entry must be good.
  174. //
  175. ASSERT(PerUserEntry->UserEntryState == UserEntryInitialized);
  176. ASSERT(PerUserEntry->DavConnHandle != NULL);
  177. DavConnHandle = PerUserEntry->DavConnHandle;
  178. //
  179. // And yes, we obviously have to leave the critical section
  180. // before returning.
  181. //
  182. LeaveCriticalSection( &(HashServerEntryTableLock) );
  183. EnCriSec = FALSE;
  184. //
  185. // We now call the HttpOpenRequest function and return.
  186. //
  187. DavWorkItem->DavOperation = DAV_CALLBACK_HTTP_OPEN;
  188. DavWorkItem->DavMinorOperation = DavMinorReadData;
  189. DavWorkItem->AsyncQueryDirectoryCall.DataBuff = NULL;
  190. DavWorkItem->AsyncQueryDirectoryCall.didRead = NULL;
  191. DavWorkItem->AsyncQueryDirectoryCall.Context1 = NULL;
  192. DavWorkItem->AsyncQueryDirectoryCall.Context2 = NULL;
  193. // convert the unicode directory path to UTF-8 URL format
  194. // space and other white characters will remain untouched - these should
  195. // be taken care of by wininet calls
  196. BStatus = DavHttpOpenRequestW(DavConnHandle,
  197. L"PROPFIND",
  198. DirectoryPath,
  199. L"HTTP/1.1",
  200. NULL,
  201. NULL,
  202. INTERNET_FLAG_KEEP_CONNECTION |
  203. INTERNET_FLAG_NO_COOKIES,
  204. CallBackContext,
  205. L"DavFsQueryDirectory",
  206. &DavOpenHandle);
  207. if(BStatus == FALSE) {
  208. WStatus = GetLastError();
  209. goto EXIT_THE_FUNCTION;
  210. }
  211. if (DavOpenHandle == NULL) {
  212. WStatus = GetLastError();
  213. if (WStatus != ERROR_IO_PENDING) {
  214. DavPrint((DEBUG_ERRORS,
  215. "DavFsQueryDirectory/HttpOpenRequest. Error Val = %d\n",
  216. WStatus));
  217. }
  218. goto EXIT_THE_FUNCTION;
  219. }
  220. //
  221. // Cache the DavOpenHandle in the DavWorkItem.
  222. //
  223. DavWorkItem->AsyncQueryDirectoryCall.DavOpenHandle = DavOpenHandle;
  224. WStatus = DavAsyncCommonStates(DavWorkItem, FALSE);
  225. if (WStatus != ERROR_SUCCESS && WStatus != ERROR_IO_PENDING) {
  226. DavPrint((DEBUG_ERRORS,
  227. "DavFsQueryDirectory/DavAsyncCommonStates. Error Val = %08lx\n",
  228. WStatus));
  229. }
  230. EXIT_THE_FUNCTION:
  231. if (EnCriSec) {
  232. LeaveCriticalSection( &(HashServerEntryTableLock) );
  233. EnCriSec = FALSE;
  234. }
  235. //
  236. // If we are using WinInet synchronously, then we should never get back
  237. // ERROR_IO_PENDING from WinInet.
  238. //
  239. ASSERT(WStatus != ERROR_IO_PENDING);
  240. //
  241. // If this thread impersonated a user, we need to revert back.
  242. //
  243. if (didImpersonate) {
  244. RevertToSelf();
  245. }
  246. //
  247. // Set the return status of the operation. This is used by the kernel
  248. // mode routines to figure out the completion status of the user mode
  249. // request. This is done here because the async completion routine that is
  250. // called immediately afterwards needs the status set.
  251. //
  252. if (WStatus != ERROR_SUCCESS) {
  253. DavWorkItem->Status = DavMapErrorToNtStatus(WStatus);
  254. } else {
  255. DavWorkItem->Status = STATUS_SUCCESS;
  256. }
  257. DavAsyncQueryDirectoryCompletion(DavWorkItem);
  258. return WStatus;
  259. }
  260. DWORD
  261. DavAsyncQueryDirectory(
  262. PDAV_USERMODE_WORKITEM DavWorkItem,
  263. BOOLEAN CalledByCallBackThread
  264. )
  265. /*++
  266. Routine Description:
  267. This is the callback routine for the query directory operation.
  268. Arguments:
  269. DavWorkItem - The DAV_USERMODE_WORKITEM value.
  270. CalledByCallbackThread - TRUE, if this function was called by the thread
  271. which picks of the DavWorkItem from the Callback
  272. function. This happens when an Async WinInet call
  273. returns ERROR_IO_PENDING and completes later.
  274. Return Value:
  275. ERROR_SUCCESS or the appropriate error value.
  276. --*/
  277. {
  278. ULONG WStatus = ERROR_SUCCESS;
  279. ULONG NumOfFileEntries = 0;
  280. PUMRX_USERMODE_WORKITEM_HEADER UserWorkItem = NULL;
  281. BOOL didImpersonate = FALSE, ReturnVal, readDone = FALSE;
  282. HINTERNET DavOpenHandle = NULL;
  283. DWORD didRead, DataBuffBytes, TotalDataBytesRead = 0;
  284. PCHAR DataBuff = NULL;
  285. LPDWORD NumRead = NULL;
  286. PDAV_FILE_ATTRIBUTES DavFileAttributes = NULL;
  287. PVOID Ctx1 = NULL, Ctx2 = NULL;
  288. PDAV_USERMODE_QUERYDIR_RESPONSE QueryDirResponse = NULL;
  289. PDAV_FILE_ATTRIBUTES DFA1 = NULL, DFA2 = NULL, TempDFA = NULL;
  290. BOOL fFreeDFAs = TRUE;
  291. PDAV_FILE_ATTRIBUTES parentDFA = NULL;
  292. UserWorkItem = (PUMRX_USERMODE_WORKITEM_HEADER)DavWorkItem;
  293. ASSERT(CalledByCallBackThread == FALSE);
  294. switch (DavWorkItem->DavOperation) {
  295. case DAV_CALLBACK_HTTP_END: {
  296. DavOpenHandle = DavWorkItem->AsyncQueryDirectoryCall.DavOpenHandle;
  297. //
  298. // If the file for which the PROPFIND was done does not exist, then
  299. // we need to fail right away.
  300. //
  301. WStatus = DavQueryAndParseResponse(DavOpenHandle);
  302. if (WStatus != ERROR_SUCCESS) {
  303. //
  304. // The file/directory for which the PROPFIND was done, does not
  305. // exist.
  306. //
  307. if (WStatus != ERROR_FILE_NOT_FOUND) {
  308. DavPrint((DEBUG_ERRORS,
  309. "DavAsyncQueryDirectory/DavQueryAndParseResponse. "
  310. "WStatus = %d\n", WStatus));
  311. }
  312. WStatus = ERROR_FILE_NOT_FOUND; // STATUS_OBJECT_NAME_NOT_FOUND;
  313. goto EXIT_THE_FUNCTION;
  314. }
  315. //
  316. // The file exists. The next thing we do is read the properties
  317. // of the file (or files in the directory).
  318. //
  319. DavWorkItem->DavOperation = DAV_CALLBACK_HTTP_READ;
  320. }
  321. //
  322. // Lack of break is intentional.
  323. //
  324. case DAV_CALLBACK_HTTP_READ: {
  325. DavOpenHandle = DavWorkItem->AsyncQueryDirectoryCall.DavOpenHandle;
  326. if (DavWorkItem->AsyncQueryDirectoryCall.DataBuff == NULL) {
  327. //
  328. // Need to allocate memory for the read buffer.
  329. //
  330. DataBuffBytes = NUM_OF_BYTES_TO_READ;
  331. DataBuff = LocalAlloc (LMEM_FIXED | LMEM_ZEROINIT, DataBuffBytes);
  332. if (DataBuff == NULL) {
  333. WStatus = GetLastError();
  334. DavPrint((DEBUG_ERRORS,
  335. "DavAsyncQueryDirectory/LocalAlloc. Error Val = %d\n",
  336. WStatus));
  337. goto EXIT_THE_FUNCTION;
  338. }
  339. DavWorkItem->AsyncQueryDirectoryCall.DataBuff = DataBuff;
  340. }
  341. if (DavWorkItem->AsyncQueryDirectoryCall.didRead == NULL) {
  342. //
  343. // Allocate memory for the DWORD that stores the number of bytes
  344. // read.
  345. //
  346. NumRead = LocalAlloc (LMEM_FIXED | LMEM_ZEROINIT, sizeof(DWORD));
  347. if (NumRead == NULL) {
  348. WStatus = GetLastError();
  349. DavPrint((DEBUG_ERRORS,
  350. "DavAsyncQueryDirectory/LocalAlloc. Error Val = %d\n",
  351. WStatus));
  352. goto EXIT_THE_FUNCTION;
  353. }
  354. DavWorkItem->AsyncQueryDirectoryCall.didRead = NumRead;
  355. }
  356. DavWorkItem->DavOperation = DAV_CALLBACK_HTTP_READ;
  357. NumRead = DavWorkItem->AsyncQueryDirectoryCall.didRead;
  358. DataBuff = DavWorkItem->AsyncQueryDirectoryCall.DataBuff;
  359. Ctx1 = DavWorkItem->AsyncQueryDirectoryCall.Context1;
  360. Ctx2 = DavWorkItem->AsyncQueryDirectoryCall.Context2;
  361. do {
  362. switch (DavWorkItem->DavMinorOperation) {
  363. case DavMinorReadData:
  364. DavWorkItem->DavMinorOperation = DavMinorPushData;
  365. ReturnVal = InternetReadFile(DavOpenHandle,
  366. (LPVOID)DataBuff,
  367. NUM_OF_BYTES_TO_READ,
  368. NumRead);
  369. if (!ReturnVal) {
  370. WStatus = GetLastError();
  371. if (WStatus != ERROR_IO_PENDING) {
  372. DavCloseContext(Ctx1, Ctx2);
  373. DavPrint((DEBUG_ERRORS,
  374. "DavAsyncQueryDirectory/InternetReadFile. "
  375. "Error Val = %d\n", WStatus));
  376. }
  377. DavPrint((DEBUG_MISC,
  378. "DavAsyncQueryDirectory/InternetReadFile. "
  379. "ERROR_IO_PENDING.\n"));
  380. goto EXIT_THE_FUNCTION;
  381. }
  382. //
  383. // We reject files whose attributes are greater than a
  384. // certain size (DavFileAttributesLimitInBytes). This
  385. // is a parameter that can be set in the registry. This
  386. // is done to avoid attacks by rogue servers. For PROPFIND
  387. // with depth 1, we add a multiple of 10.
  388. //
  389. TotalDataBytesRead += *NumRead;
  390. if (DavWorkItem->AsyncQueryDirectoryCall.NoWildCards) {
  391. if (TotalDataBytesRead > DavFileAttributesLimitInBytes) {
  392. WStatus = ERROR_BAD_NET_RESP;
  393. DavPrint((DEBUG_ERRORS, "DavAsyncQueryDirectory. FileAttributesSize > %d\n", DavFileAttributesLimitInBytes));
  394. goto EXIT_THE_FUNCTION;
  395. }
  396. } else {
  397. if (TotalDataBytesRead > (10 * DavFileAttributesLimitInBytes)) {
  398. WStatus = ERROR_BAD_NET_RESP;
  399. DavPrint((DEBUG_ERRORS, "DavAsyncQueryDirectory. FileAttributesSize > %d\n", (10 * DavFileAttributesLimitInBytes)));
  400. goto EXIT_THE_FUNCTION;
  401. }
  402. }
  403. //
  404. // Lack of break is intentional.
  405. //
  406. case DavMinorPushData:
  407. DavWorkItem->DavMinorOperation = DavMinorReadData;
  408. didRead = *NumRead;
  409. readDone = (didRead == 0) ? TRUE : FALSE;
  410. WStatus = DavPushData(DataBuff, &Ctx1, &Ctx2, didRead, readDone);
  411. if (WStatus != ERROR_SUCCESS) {
  412. DavPrint((DEBUG_ERRORS,
  413. "DavAsyncQueryDirectory/DavPushData."
  414. " Error Val = %d\n", WStatus));
  415. goto EXIT_THE_FUNCTION;
  416. }
  417. if (DavWorkItem->AsyncQueryDirectoryCall.Context1 == NULL) {
  418. DavWorkItem->AsyncQueryDirectoryCall.Context1 = Ctx1;
  419. }
  420. if (DavWorkItem->AsyncQueryDirectoryCall.Context2 == NULL) {
  421. DavWorkItem->AsyncQueryDirectoryCall.Context2 = Ctx2;
  422. }
  423. break;
  424. default:
  425. WStatus = ERROR_INVALID_PARAMETER;
  426. DavPrint((DEBUG_ERRORS,
  427. "DavAsyncQueryDirectory. Invalid DavMinorOperation ="
  428. " %d.\n", DavWorkItem->DavMinorOperation));
  429. goto EXIT_THE_FUNCTION;
  430. break;
  431. }
  432. if (readDone) {
  433. break;
  434. }
  435. } while ( TRUE );
  436. //
  437. // We now need to parse the data.
  438. //
  439. DavFileAttributes = LocalAlloc( LMEM_FIXED | LMEM_ZEROINIT,
  440. sizeof(DAV_FILE_ATTRIBUTES) );
  441. if (DavFileAttributes == NULL) {
  442. WStatus = GetLastError();
  443. DavCloseContext(Ctx1, Ctx2);
  444. DavPrint((DEBUG_ERRORS,
  445. "DavAsyncQueryDirectory/LocalAlloc. "
  446. "Error Val = %d\n", WStatus));
  447. goto EXIT_THE_FUNCTION;
  448. }
  449. InitializeListHead( &(DavFileAttributes->NextEntry) );
  450. WStatus = DavParseDataEx(DavFileAttributes, Ctx1, Ctx2, &NumOfFileEntries, &parentDFA);
  451. if (WStatus != ERROR_SUCCESS) {
  452. DavFinalizeFileAttributesList(DavFileAttributes, TRUE);
  453. DavFileAttributes = NULL;
  454. DavPrint((DEBUG_ERRORS,
  455. "DavAsyncQueryDirectory/DavParseDataEx. "
  456. "Error Val = %d\n", WStatus));
  457. goto EXIT_THE_FUNCTION;
  458. }
  459. QueryDirResponse = &(DavWorkItem->QueryDirResponse);
  460. //
  461. // If we queried the server for a file which did not exist, it may
  462. // return 200 OK with no files in the XML response.
  463. //
  464. if (DavWorkItem->AsyncQueryDirectoryCall.NoWildCards) {
  465. if (NumOfFileEntries != 1) {
  466. PLIST_ENTRY listEntry = &(DavFileAttributes->NextEntry);
  467. PDAV_FILE_ATTRIBUTES DavFA = NULL;
  468. DavPrint((DEBUG_MISC,
  469. "DavAsyncQueryDirectory. NumOfFileEntries = %d\n",
  470. NumOfFileEntries));
  471. do {
  472. DavFA = CONTAINING_RECORD(listEntry, DAV_FILE_ATTRIBUTES, NextEntry);
  473. DavPrint((DEBUG_MISC,
  474. "DavAsyncQueryDirectory. FileName = %ws\n",
  475. DavFA->FileName));
  476. listEntry = listEntry->Flink;
  477. } while ( listEntry != &(DavFileAttributes->NextEntry) );
  478. ASSERT(NumOfFileEntries == 0);
  479. DavCloseContext(Ctx1, Ctx2);
  480. DavFinalizeFileAttributesList(DavFileAttributes, TRUE);
  481. DavFileAttributes = NULL;
  482. WStatus = ERROR_FILE_NOT_FOUND; // STATUS_OBJECT_NAME_NOT_FOUND;
  483. goto EXIT_THE_FUNCTION;
  484. }
  485. } else {
  486. //
  487. // This Query is done for a Directory or for a collection of files
  488. // (ex. dir Z:\ab*).
  489. //
  490. // In the DFA list (DavFileAttributes) returned by DavParseDataEx(...),
  491. // we want to have DFA of the "directory being queried" at the head
  492. // of the list.
  493. // List (DavFileAttributes) returned by DavParseDataEx(...) may not
  494. // necessarily have this TRUE.
  495. // Since DavFileAttributes is a cyclic linked list (all entries are allocated
  496. // and are to be freed by this function), we will set DavFileAttributes to
  497. // point to DFA pointed by parentDFA (points to DFA of "directory being
  498. // queried").
  499. //
  500. // Note: DavFileAttributes->FileIndex which is set in an increasing order
  501. // starting from 0 in DavParseDataEx(...), may no longer remain in this valid
  502. // order after re-pointing of DavFileAttributes pointer. We will set them
  503. // in valid order again here.
  504. //
  505. if (parentDFA != NULL && parentDFA != DavFileAttributes) {
  506. PLIST_ENTRY listEntry = NULL;
  507. PDAV_FILE_ATTRIBUTES TempDFA = NULL;
  508. ULONG Count = DavFileAttributes->FileIndex;
  509. DavPrint((DEBUG_DEBUG, "DavAsyncQueryDirectory. CollectionDFA=0x%x",
  510. parentDFA));
  511. DavFileAttributes = parentDFA;
  512. //
  513. // We start the Count with first value DavParseDataEx (value of Head
  514. // entry in the List) is setting in DavFileAttributes List.
  515. //
  516. listEntry = DavFileAttributes->NextEntry.Flink;
  517. //
  518. // Set the file indices.
  519. //
  520. DavFileAttributes->FileIndex = Count;
  521. Count++;
  522. while ( listEntry != &(DavFileAttributes->NextEntry) ) {
  523. TempDFA = CONTAINING_RECORD(listEntry, DAV_FILE_ATTRIBUTES, NextEntry);
  524. listEntry = listEntry->Flink;
  525. TempDFA->FileIndex = Count;
  526. Count++;
  527. }
  528. }
  529. }
  530. //
  531. // If this was a query for all the files under the directory, then we
  532. // need to add the files . (current directory) and .. (parent directory)
  533. // since these are not returned by the server.
  534. //
  535. if ( !(DavWorkItem->AsyncQueryDirectoryCall.NoWildCards) ) {
  536. PLIST_ENTRY listEntry = NULL;
  537. PLIST_ENTRY TempEntry = NULL;
  538. ULONG Count = 0;
  539. //
  540. // We first create the two entires and copy the file names in them.
  541. //
  542. DFA1 = LocalAlloc(LPTR, sizeof(DAV_FILE_ATTRIBUTES));
  543. if (DFA1 == NULL) {
  544. WStatus = GetLastError();
  545. DavPrint((DEBUG_ERRORS,
  546. "DavAsyncQueryDirectory/LocalAlloc. "
  547. "Error Val = %d\n", WStatus));
  548. DavCloseContext(Ctx1, Ctx2);
  549. DavFinalizeFileAttributesList(DavFileAttributes, TRUE);
  550. DavFileAttributes = NULL;
  551. goto EXIT_THE_FUNCTION;
  552. }
  553. InitializeListHead( &(DFA1->NextEntry) );
  554. //
  555. // Since the file name is ".", the amount of memory required to hold
  556. // this name is 2 * sizeof(WCHAR). The extra 1 is for the final L'\0'.
  557. //
  558. DFA1->FileName = LocalAlloc(LPTR, (2 * sizeof(WCHAR)));
  559. if (DFA1->FileName == NULL) {
  560. WStatus = GetLastError();
  561. DavPrint((DEBUG_ERRORS,
  562. "DavAsyncQueryDirectory/LocalAlloc. "
  563. "Error Val = %d\n", WStatus));
  564. DavCloseContext(Ctx1, Ctx2);
  565. DavFinalizeFileAttributesList(DavFileAttributes, TRUE);
  566. DavFileAttributes = NULL;
  567. goto EXIT_THE_FUNCTION;
  568. }
  569. wcscpy(DFA1->FileName, L".");
  570. DFA1->FileNameLength = 1;
  571. DFA2 = LocalAlloc(LPTR, sizeof(DAV_FILE_ATTRIBUTES));
  572. if (DFA2 == NULL) {
  573. WStatus = GetLastError();
  574. DavPrint((DEBUG_ERRORS,
  575. "DavAsyncQueryDirectory/LocalAlloc. "
  576. "Error Val = %d\n", WStatus));
  577. DavCloseContext(Ctx1, Ctx2);
  578. DavFinalizeFileAttributesList(DavFileAttributes, TRUE);
  579. DavFileAttributes = NULL;
  580. goto EXIT_THE_FUNCTION;
  581. }
  582. InitializeListHead( &(DFA2->NextEntry) );
  583. //
  584. // Since the file name is "..", the amount of memory required to hold
  585. // this name is 3 * sizeof(WCHAR). The extra 1 is for the final L'\0'.
  586. //
  587. DFA2->FileName = LocalAlloc(LPTR, (3 * sizeof(WCHAR)));
  588. if (DFA2->FileName == NULL) {
  589. WStatus = GetLastError();
  590. DavPrint((DEBUG_ERRORS,
  591. "DavAsyncQueryDirectory/LocalAlloc. "
  592. "Error Val = %d\n", WStatus));
  593. DavCloseContext(Ctx1, Ctx2);
  594. DavFinalizeFileAttributesList(DavFileAttributes, TRUE);
  595. DavFileAttributes = NULL;
  596. goto EXIT_THE_FUNCTION;
  597. }
  598. wcscpy(DFA2->FileName, L"..");
  599. DFA2->FileNameLength = 2;
  600. //
  601. // Both these are collections ofcourse.
  602. //
  603. DFA1->isCollection = DFA2->isCollection = TRUE;
  604. //
  605. // We set the following time values of the new entries to the value
  606. // of the first entry in the DavFileAttributes list which is the
  607. // directory being enumerated.
  608. //
  609. DFA1->CreationTime.HighPart = DFA2->CreationTime.HighPart = DavFileAttributes->CreationTime.HighPart;
  610. DFA1->CreationTime.LowPart = DFA2->CreationTime.LowPart = DavFileAttributes->CreationTime.LowPart;
  611. DFA1->DavCreationTime.HighPart = DFA2->DavCreationTime.HighPart = DavFileAttributes->DavCreationTime.HighPart;
  612. DFA1->DavCreationTime.LowPart = DFA2->DavCreationTime.LowPart = DavFileAttributes->DavCreationTime.LowPart;
  613. DFA1->LastModifiedTime.HighPart = DFA2->LastModifiedTime.HighPart = DavFileAttributes->LastModifiedTime.HighPart;
  614. DFA1->LastModifiedTime.LowPart = DFA2->LastModifiedTime.LowPart = DavFileAttributes->LastModifiedTime.LowPart;
  615. DFA1->DavLastModifiedTime.HighPart = DFA2->DavLastModifiedTime.HighPart = DavFileAttributes->DavLastModifiedTime.HighPart;
  616. DFA1->DavLastModifiedTime.LowPart = DFA2->DavLastModifiedTime.LowPart = DavFileAttributes->DavLastModifiedTime.LowPart;
  617. DFA1->LastAccessTime.HighPart = DFA2->LastAccessTime.HighPart = DavFileAttributes->LastAccessTime.HighPart;
  618. DFA1->LastAccessTime.LowPart = DFA2->LastAccessTime.LowPart = DavFileAttributes->LastAccessTime.LowPart;
  619. //
  620. // We need to add these two after the first entry. This is because
  621. // the first entry is always ignored when dealing with WildCard
  622. // queries in the kernel. This is done because the first entry is
  623. // the directory being enumerated and we don't need to show that.
  624. // So, if we had 1->2->3->....->n->1 (cyclic list), we need to insert
  625. // DFA1 and DFA2 in the following manner.
  626. // 1->DFA1->DFA2->2->3->......->n->1 (cyclic list)
  627. // ^
  628. // |
  629. // TempEntry
  630. // where DFA1 = L"." and DFA2 = L".."
  631. // We do this insertion below.
  632. //
  633. TempEntry = DavFileAttributes->NextEntry.Flink;
  634. InsertTailList(TempEntry, &(DFA1->NextEntry));
  635. InsertTailList(TempEntry, &(DFA2->NextEntry));
  636. TempEntry = NULL;
  637. fFreeDFAs = FALSE;
  638. //
  639. // We need to increment the number of file entries by 2 to take into
  640. // account the two new entries we added above.
  641. //
  642. NumOfFileEntries += 2;
  643. listEntry = DavFileAttributes->NextEntry.Flink;
  644. //
  645. // We start the Count with first value DavParseDataEx (value of Head
  646. // entry in the List) is setting in DavFileAttributes List.
  647. //
  648. Count = DavFileAttributes->FileIndex;
  649. //
  650. // Set the file indices.
  651. //
  652. DavFileAttributes->FileIndex = Count;
  653. Count++;
  654. while ( listEntry != &(DavFileAttributes->NextEntry) ) {
  655. TempDFA = CONTAINING_RECORD(listEntry, DAV_FILE_ATTRIBUTES, NextEntry);
  656. listEntry = listEntry->Flink;
  657. TempDFA->FileIndex = Count;
  658. Count++;
  659. }
  660. DavPrint((DEBUG_MISC,
  661. "DavAsyncQueryDirectory: NumOfFileEntries = %d, Count = %d\n",
  662. NumOfFileEntries, Count));
  663. }
  664. //
  665. // Set the response to be sent down to the kernel. We send the pointer
  666. // to the head of the list that was allocated during parsing.
  667. //
  668. QueryDirResponse->DavFileAttributes = DavFileAttributes;
  669. QueryDirResponse->NumOfFileEntries = NumOfFileEntries;
  670. DavCloseContext(Ctx1, Ctx2);
  671. DavPrint((DEBUG_MISC,
  672. "DavAsyncQueryDirectory: DavFileAttributes = %08lx.\n",
  673. DavFileAttributes));
  674. }
  675. break;
  676. default:
  677. WStatus = ERROR_INVALID_PARAMETER;
  678. DavPrint((DEBUG_ERRORS,
  679. "DavAsyncQueryDirectory: Invalid DavOperation = %d.\n",
  680. DavWorkItem->DavOperation));
  681. break;
  682. }
  683. EXIT_THE_FUNCTION:
  684. if(fFreeDFAs == TRUE) {
  685. if(DFA1 != NULL) {
  686. DavFinalizeFileAttributesList(DFA1, TRUE);
  687. DFA1 = NULL;
  688. }
  689. if(DFA2 != NULL) {
  690. DavFinalizeFileAttributesList(DFA2, TRUE);
  691. DFA2 = NULL;
  692. }
  693. fFreeDFAs = FALSE;
  694. }
  695. //
  696. // If we did impersonate, we need to revert back.
  697. //
  698. if (didImpersonate) {
  699. ULONG RStatus;
  700. RStatus = UMReflectorRevert(UserWorkItem);
  701. if (RStatus != ERROR_SUCCESS) {
  702. DavPrint((DEBUG_ERRORS,
  703. "DavAsyncQueryDirectory/UMReflectorRevert. Error Val"
  704. " = %d\n", RStatus));
  705. }
  706. }
  707. return WStatus;
  708. }
  709. VOID
  710. DavAsyncQueryDirectoryCompletion(
  711. PDAV_USERMODE_WORKITEM DavWorkItem
  712. )
  713. /*++
  714. Routine Description:
  715. This routine handles the QueryDirectory completion. It basically frees up
  716. the resources allocated during the QueryDirectory operation.
  717. Arguments:
  718. DavWorkItem - The DAV_USERMODE_WORKITEM value.
  719. Return Value:
  720. none.
  721. --*/
  722. {
  723. if (DavWorkItem->AsyncQueryDirectoryCall.DavOpenHandle != NULL) {
  724. BOOL ReturnVal;
  725. ULONG FreeStatus;
  726. HINTERNET DavOpenHandle = DavWorkItem->AsyncQueryDirectoryCall.DavOpenHandle;
  727. ReturnVal = InternetCloseHandle( DavOpenHandle );
  728. if (!ReturnVal) {
  729. FreeStatus = GetLastError();
  730. DavPrint((DEBUG_ERRORS,
  731. "DavAsyncQueryDirectoryCompletion/InternetCloseHandle. "
  732. "Error Val = %d\n", FreeStatus));
  733. }
  734. }
  735. if (DavWorkItem->AsyncQueryDirectoryCall.DataBuff != NULL) {
  736. HLOCAL FreeHandle;
  737. ULONG FreeStatus;
  738. FreeHandle = LocalFree((HLOCAL)DavWorkItem->AsyncQueryDirectoryCall.DataBuff);
  739. if (FreeHandle != NULL) {
  740. FreeStatus = GetLastError();
  741. DavPrint((DEBUG_ERRORS,
  742. "DavAsyncQueryDirectoryCompletion/LocalFree. Error Val = %d\n",
  743. FreeStatus));
  744. }
  745. }
  746. if (DavWorkItem->AsyncQueryDirectoryCall.didRead != NULL) {
  747. HLOCAL FreeHandle;
  748. ULONG FreeStatus;
  749. FreeHandle = LocalFree((HLOCAL)DavWorkItem->AsyncQueryDirectoryCall.didRead);
  750. if (FreeHandle != NULL) {
  751. FreeStatus = GetLastError();
  752. DavPrint((DEBUG_ERRORS,
  753. "DavAsyncQueryDirectoryCompletion/LocalFree. Error Val = %d\n",
  754. FreeStatus));
  755. }
  756. }
  757. if (DavWorkItem->AsyncResult != NULL) {
  758. HLOCAL FreeHandle;
  759. ULONG FreeStatus;
  760. FreeHandle = LocalFree((HLOCAL)DavWorkItem->AsyncResult);
  761. if (FreeHandle != NULL) {
  762. FreeStatus = GetLastError();
  763. DavPrint((DEBUG_ERRORS,
  764. "DavAsyncQueryDirectoryCompletion/LocalFree. Error Val ="
  765. " %d\n", FreeStatus));
  766. }
  767. }
  768. //
  769. // The callback context should not be finalized if we are returning
  770. // ERROR_IO_PENDING.
  771. //
  772. DavFsFinalizeTheDavCallBackContext(DavWorkItem);
  773. //
  774. // We are done with the per user entry, so finalize it.
  775. //
  776. if (DavWorkItem->AsyncQueryDirectoryCall.PerUserEntry) {
  777. DavFinalizePerUserEntry( &(DavWorkItem->AsyncQueryDirectoryCall.PerUserEntry) );
  778. }
  779. return;
  780. }
  781. ULONG
  782. DavFsQueryVolumeInformation(
  783. PDAV_USERMODE_WORKITEM DavWorkItem
  784. )
  785. /*++
  786. Routine Description:
  787. This routine handles QueryVolumeInformationRequest requests for the DAV Mini-Redir that
  788. get reflected from the kernel.
  789. Arguments:
  790. DavWorkItem - The buffer that contains the request parameters and options.
  791. Return Value:
  792. The return status for the operation
  793. --*/
  794. {
  795. ULONG WStatus = ERROR_SUCCESS;
  796. PDAV_USERMODE_QUERYVOLUMEINFORMATION_REQUEST QueryVolumeInformationRequest;
  797. PWCHAR ServerName = NULL, ShareName = NULL;
  798. BOOL EnCriSec = FALSE, ReturnVal, CallBackContextInitialized = FALSE;
  799. ULONG ServerID;
  800. PPER_USER_ENTRY PerUserEntry = NULL;
  801. PHASH_SERVER_ENTRY ServerHashEntry = NULL;
  802. HINTERNET DavConnHandle, DavOpenHandle;
  803. BOOL didImpersonate = FALSE;
  804. PUMRX_USERMODE_WORKITEM_HEADER UserWorkItem = NULL;
  805. BOOL BStatus = FALSE;
  806. UserWorkItem = (PUMRX_USERMODE_WORKITEM_HEADER)DavWorkItem;
  807. //
  808. // Get the request buffer from the DavWorkItem.
  809. //
  810. QueryVolumeInformationRequest = &(DavWorkItem->QueryVolumeInformationRequest);
  811. //
  812. // The first character is a '\' which has to be stripped.
  813. //
  814. ServerName = &(QueryVolumeInformationRequest->ServerName[1]);
  815. if (!ServerName) {
  816. DavPrint((DEBUG_ERRORS, "DavFsQueryVolumeInformation: ServerName is NULL.\n"));
  817. WStatus = ERROR_INVALID_PARAMETER; // STATUS_INVALID_PARAMETER;
  818. goto EXIT_THE_FUNCTION;
  819. }
  820. DavPrint((DEBUG_MISC, "DavFsQueryVolumeInformation: ServerName = %ws.\n", ServerName));
  821. ServerID = QueryVolumeInformationRequest->ServerID;
  822. DavPrint((DEBUG_MISC, "DavFsQueryVolumeInformation: ServerID = %d.\n", ServerID));
  823. //
  824. // The first character is a '\' which has to be stripped.
  825. //
  826. ShareName = &(QueryVolumeInformationRequest->ShareName[1]);
  827. if (!ServerName) {
  828. DavPrint((DEBUG_ERRORS, "DavFsQueryVolumeInformation: ShareName is NULL.\n"));
  829. WStatus = ERROR_INVALID_PARAMETER; // STATUS_INVALID_PARAMETER;
  830. goto EXIT_THE_FUNCTION;
  831. }
  832. DavPrint((DEBUG_MISC, "DavFsQueryVolumeInformation: ShareName = %ws.\n", ShareName));
  833. //
  834. // If ShareName is a dummy share, we need to remove it right now before we
  835. // contact the server.
  836. //
  837. DavRemoveDummyShareFromFileName(ShareName);
  838. DavPrint((DEBUG_MISC,
  839. "DavFsQueryVolumeInformation: LogonId.LowPart = %d, LogonId.HighPart = %d\n",
  840. QueryVolumeInformationRequest->LogonID.LowPart,
  841. QueryVolumeInformationRequest->LogonID.HighPart));
  842. WStatus = UMReflectorImpersonate(UserWorkItem, DavWorkItem->ImpersonationHandle);
  843. if (WStatus != ERROR_SUCCESS) {
  844. DavPrint((DEBUG_ERRORS,
  845. "DavFsCreateVNetRoot/UMReflectorImpersonate. Error Val = %d\n",
  846. WStatus));
  847. goto EXIT_THE_FUNCTION;
  848. }
  849. didImpersonate = TRUE;
  850. //
  851. // Allocate memory for the INTERNET_ASYNC_RESULT structure.
  852. //
  853. DavWorkItem->AsyncResult = LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT,
  854. sizeof(INTERNET_ASYNC_RESULT));
  855. if (DavWorkItem->AsyncResult == NULL) {
  856. WStatus = GetLastError();
  857. DavPrint((DEBUG_ERRORS,
  858. "DavFsQueryVolumeInformation/LocalAlloc. Error Val = %d\n",
  859. WStatus));
  860. goto EXIT_THE_FUNCTION;
  861. }
  862. //
  863. // A User Entry for this user must have been created during the create call
  864. // earlier. The user entry contains the handle used to send an HttpOpen
  865. // request.
  866. //
  867. EnterCriticalSection( &(HashServerEntryTableLock) );
  868. EnCriSec = TRUE;
  869. ReturnVal = DavDoesUserEntryExist(ServerName,
  870. ServerID,
  871. &(QueryVolumeInformationRequest->LogonID),
  872. &PerUserEntry,
  873. &ServerHashEntry);
  874. //
  875. // If the following request in the kernel get cancelled even before the
  876. // corresponding usermode thread gets a chance to execute this code, then
  877. // it possible that the VNetRoot (hence the PerUserEntry) and SrvCall get
  878. // finalized before the thread that is handling the create comes here. This
  879. // could happen if this request was the only one for this share and the
  880. // server as well. This is why we need to check if the ServerHashEntry and
  881. // the PerUserEntry are valid before proceeding.
  882. //
  883. if (ReturnVal == FALSE || ServerHashEntry == NULL || PerUserEntry == NULL) {
  884. WStatus = ERROR_CANCELLED;
  885. DavPrint((DEBUG_ERRORS, "DavFsQueryVolumeInformation: (ServerHashEntry == NULL || PerUserEntry == NULL)\n"));
  886. goto EXIT_THE_FUNCTION;
  887. }
  888. DavWorkItem->AsyncQueryVolumeInformation.ServerHashEntry = ServerHashEntry;
  889. DavWorkItem->AsyncQueryVolumeInformation.PerUserEntry = PerUserEntry;
  890. DavPrint((DEBUG_MISC,
  891. "DavFsQueryVolumeInformation: PerUserEntry = %08lx.\n",
  892. PerUserEntry));
  893. //
  894. // Add a reference to the user entry.
  895. //
  896. PerUserEntry->UserEntryRefCount++;
  897. //
  898. // Since a create had succeeded earlier, the entry must be good.
  899. //
  900. ASSERT(PerUserEntry->UserEntryState == UserEntryInitialized);
  901. ASSERT(PerUserEntry->DavConnHandle != NULL);
  902. //
  903. // And yes, we obviously have to leave the critical section
  904. // before returning.
  905. //
  906. LeaveCriticalSection( &(HashServerEntryTableLock) );
  907. EnCriSec = FALSE;
  908. //
  909. // We now call the HttpOpenRequest function and return.
  910. //
  911. DavWorkItem->DavOperation = DAV_CALLBACK_INTERNET_CONNECT;
  912. DavWorkItem->DavMinorOperation = DavMinorReadData;
  913. DavWorkItem->AsyncQueryVolumeInformation.PerUserEntry = PerUserEntry;
  914. WStatus = DavAsyncCommonStates(DavWorkItem, FALSE);
  915. EXIT_THE_FUNCTION:
  916. if (EnCriSec) {
  917. LeaveCriticalSection( &(HashServerEntryTableLock) );
  918. EnCriSec = FALSE;
  919. }
  920. //
  921. // If we are using WinInet synchronously, then we should never get back
  922. // ERROR_IO_PENDING from WinInet.
  923. //
  924. ASSERT(WStatus != ERROR_IO_PENDING);
  925. //
  926. // If this thread impersonated a user, we need to revert back.
  927. //
  928. if (didImpersonate) {
  929. RevertToSelf();
  930. }
  931. //
  932. // Set the return status of the operation. This is used by the kernel
  933. // mode routines to figure out the completion status of the user mode
  934. // request. This is done here because the async completion routine that is
  935. // called immediately afterwards needs the status set.
  936. //
  937. if (WStatus != ERROR_SUCCESS) {
  938. DavWorkItem->Status = DavMapErrorToNtStatus(WStatus);
  939. } else {
  940. DavWorkItem->Status = STATUS_SUCCESS;
  941. }
  942. DavAsyncQueryVolumeInformationCompletion(DavWorkItem);
  943. return WStatus;
  944. }
  945. DWORD
  946. DavAsyncQueryVolumeInformation(
  947. PDAV_USERMODE_WORKITEM DavWorkItem,
  948. BOOLEAN CalledByCallBackThread
  949. )
  950. /*++
  951. Routine Description:
  952. This is the callback routine for the query directory operation.
  953. Arguments:
  954. DavWorkItem - The DAV_USERMODE_WORKITEM value.
  955. CalledByCallbackThread - TRUE, if this function was called by the thread
  956. which picks of the DavWorkItem from the Callback
  957. function. This happens when an Async WinInet call
  958. returns ERROR_IO_PENDING and completes later.
  959. Return Value:
  960. ERROR_SUCCESS or the appropriate error value.
  961. --*/
  962. {
  963. ULONG WStatus = ERROR_SUCCESS;
  964. DAV_FILE_ATTRIBUTES DavFileAttributes;
  965. WStatus = DavParseXmlResponse(DavWorkItem->AsyncQueryVolumeInformation.DavOpenHandle, &DavFileAttributes, NULL);
  966. if (WStatus == ERROR_SUCCESS)
  967. {
  968. DavWorkItem->QueryVolumeInformationResponse.TotalSpace =
  969. DavFileAttributes.TotalSpace;
  970. DavWorkItem->QueryVolumeInformationResponse.AvailableSpace =
  971. DavFileAttributes.AvailableSpace;
  972. if (!*(LONGLONG *)&(DavWorkItem->QueryVolumeInformationResponse.TotalSpace))
  973. {
  974. *(LONGLONG *)&(DavWorkItem->QueryVolumeInformationResponse.TotalSpace) =
  975. *(LONGLONG *)&(DavWorkItem->QueryVolumeInformationResponse.AvailableSpace)+MSN_SPACE_FAKE_DELTA;
  976. }
  977. DavFinalizeFileAttributesList(&DavFileAttributes, FALSE);
  978. }
  979. return WStatus;
  980. }
  981. VOID
  982. DavAsyncQueryVolumeInformationCompletion(
  983. PDAV_USERMODE_WORKITEM DavWorkItem
  984. )
  985. /*++
  986. Routine Description:
  987. This routine handles the QueryVolumeInformation completion. It basically frees up
  988. the resources allocated during the QueryVolumeInformation operation.
  989. Arguments:
  990. DavWorkItem - The DAV_USERMODE_WORKITEM value.
  991. Return Value:
  992. none.
  993. --*/
  994. {
  995. if (DavWorkItem->AsyncQueryVolumeInformation.DavOpenHandle != NULL) {
  996. BOOL ReturnVal;
  997. ULONG FreeStatus;
  998. ReturnVal = InternetCloseHandle(DavWorkItem->AsyncQueryVolumeInformation.DavOpenHandle);
  999. if (!ReturnVal) {
  1000. FreeStatus = GetLastError();
  1001. DavPrint((DEBUG_ERRORS,
  1002. "DavAsyncQueryVolumeInformationCompletion/InternetCloseHandle. "
  1003. "Error Val = %d\n", FreeStatus));
  1004. }
  1005. }
  1006. if (DavWorkItem->AsyncResult != NULL) {
  1007. HLOCAL FreeHandle;
  1008. ULONG FreeStatus;
  1009. FreeHandle = LocalFree((HLOCAL)DavWorkItem->AsyncResult);
  1010. if (FreeHandle != NULL) {
  1011. FreeStatus = GetLastError();
  1012. DavPrint((DEBUG_ERRORS,
  1013. "DavAsyncQueryVolumeInformationCompletion/LocalFree. Error Val ="
  1014. " %d\n", FreeStatus));
  1015. }
  1016. }
  1017. //
  1018. // The callback context should not be finalized if we are returning
  1019. // ERROR_IO_PENDING.
  1020. //
  1021. DavFsFinalizeTheDavCallBackContext(DavWorkItem);
  1022. //
  1023. // We are done with the per user entry, so finalize it.
  1024. //
  1025. if (DavWorkItem->AsyncQueryVolumeInformation.PerUserEntry) {
  1026. DavFinalizePerUserEntry( &(DavWorkItem->AsyncQueryVolumeInformation.PerUserEntry) );
  1027. }
  1028. return;
  1029. }