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.

2407 lines
75 KiB

  1. /*++
  2. Copyright (c) 1998-2000 Microsoft Corporation
  3. Module Name :
  4. device.cpp
  5. Abstract:
  6. Device object handles one redirected device
  7. Revision History:
  8. --*/
  9. #include "precomp.hxx"
  10. #define TRC_FILE "device"
  11. #include "trc.h"
  12. #if DBG
  13. extern UCHAR IrpNames[IRP_MJ_MAXIMUM_FUNCTION + 1][40];
  14. #endif // DBG
  15. DrDevice::DrDevice(SmartPtr<DrSession> &Session, ULONG DeviceType, ULONG DeviceId, PUCHAR PreferredDosName)
  16. {
  17. unsigned len;
  18. BEGIN_FN("DrDevice::DrDevice");
  19. ASSERT(Session != NULL);
  20. ASSERT(PreferredDosName != NULL);
  21. TRC_NRM((TB, "Create Device (%p, session: %p, type: %d, id: %d, dosname: %s",
  22. this, Session, DeviceType, DeviceId, PreferredDosName));
  23. SetClassName("DrDevice");
  24. _Session = Session;
  25. _DeviceType = DeviceType;
  26. _DeviceId = DeviceId;
  27. _DeviceStatus = dsAvailable;
  28. #if DBG
  29. _VNetRootFinalized = FALSE;
  30. _VNetRoot = NULL;
  31. #endif
  32. RtlCopyMemory(_PreferredDosName, PreferredDosName,
  33. PREFERRED_DOS_NAME_SIZE);
  34. //
  35. // Review: We don't want to redirect any device name willy nilly,
  36. // as I think that would be a security issue given a bad client
  37. //
  38. _PreferredDosName[PREFERRED_DOS_NAME_SIZE - 1] = 0;
  39. //
  40. // We don't want colon for end of DosName
  41. //
  42. len = strlen((CHAR*)_PreferredDosName);
  43. if (len && _PreferredDosName[len-1] == ':') {
  44. _PreferredDosName[len-1] = '\0';
  45. }
  46. }
  47. DrDevice::~DrDevice()
  48. {
  49. BEGIN_FN("DrDevice::~DrDevice");
  50. #if DBG
  51. if (_VNetRoot != NULL && _VNetRootFinalized != TRUE) {
  52. ASSERT(FALSE);
  53. }
  54. #endif
  55. TRC_NRM((TB, "Delete Device %p for Session %p", this, _Session));
  56. }
  57. BOOL DrDevice::ShouldCreateDevice()
  58. {
  59. BEGIN_FN("DrDevice::ShouldCreateDevice");
  60. //
  61. // Default is to create the device
  62. //
  63. return TRUE;
  64. }
  65. NTSTATUS DrDevice::Initialize(PRDPDR_DEVICE_ANNOUNCE devAnnounceMsg, ULONG Length)
  66. {
  67. BEGIN_FN("DrDevice::Initialize");
  68. // Can't assume devAnnouceMsg is not NULL, need to check if uses it
  69. // ASSERT(devAnnounceMsg != NULL);
  70. return STATUS_SUCCESS;
  71. }
  72. VOID DrDevice::CreateReferenceString(
  73. IN OUT PUNICODE_STRING refString)
  74. /*++
  75. Routine Description:
  76. Create the device reference string using information about the
  77. client device. This string is in a form suitable for reparse
  78. on IRP_MJ_CREATE and redirection to the minirdr DO.
  79. The form of the resultant reference string is:
  80. \;<DriveLetter>:<sessionid>\clientname\preferredDosName
  81. Arguments:
  82. DosDeviceName - Dos Device Name in UNICODE
  83. refString - UNICODE structure large enough to hold
  84. the entire resultant reference string. This
  85. works out to be DRMAXREFSTRINGLEN bytes.
  86. Return Value:
  87. NONE
  88. --*/
  89. {
  90. NTSTATUS status;
  91. STRING string;
  92. WCHAR numericBuf[RDPDRMAXULONGSTRING+1] = L"\0";
  93. WCHAR ansiBuf[RDPDRMAXDOSNAMELEN+1] = L"\0";
  94. UNICODE_STRING ansiUnc;
  95. UNICODE_STRING numericUnc;
  96. ULONG sessionID = _Session->GetSessionId();
  97. PCSZ preferredDosName = (PCSZ)_PreferredDosName;
  98. BEGIN_FN("DrDevice::CreateReferenceString");
  99. ASSERT(refString != NULL);
  100. // Sanity check the preferred DOS name.
  101. TRC_ASSERT(preferredDosName != NULL, (TB, "Invalid DOS device name."));
  102. // Make sure the reference string buf is big enough.
  103. TRC_ASSERT(refString->MaximumLength >= (RDPDRMAXREFSTRINGLEN * sizeof(WCHAR)),
  104. (TB, "Reference string buffer too small."));
  105. // Zero it out.
  106. refString->Length = 0;
  107. refString->Buffer[0] = L'\0';
  108. // Add a '\;'
  109. RtlAppendUnicodeToString(refString, L"\\;");
  110. // Initialize the ansi conversion buf.
  111. ansiUnc.Length = 0;
  112. ansiUnc.MaximumLength = RDPDRMAXDOSNAMELEN * sizeof(WCHAR);
  113. ansiUnc.Buffer = ansiBuf;
  114. // Add the preferred dos name.
  115. RtlInitAnsiString(&string, preferredDosName);
  116. RtlAnsiStringToUnicodeString(&ansiUnc, &string, FALSE);
  117. RtlAppendUnicodeStringToString(refString, &ansiUnc);
  118. // Add a ':'
  119. RtlAppendUnicodeToString(refString, L":");
  120. // Initialize the numeric buf.
  121. numericUnc.Length = 0;
  122. numericUnc.MaximumLength = RDPDRMAXULONGSTRING * sizeof(WCHAR);
  123. numericUnc.Buffer = numericBuf;
  124. // Add the session ID in base 10.
  125. RtlIntegerToUnicodeString(sessionID, 10, &numericUnc);
  126. RtlAppendUnicodeStringToString(refString, &numericUnc);
  127. // Add the '\'
  128. RtlAppendUnicodeToString(refString, L"\\");
  129. // Add Client name
  130. #if 0
  131. RtlAppendUnicodeToString(refString, _Session->GetClientName());
  132. #endif
  133. RtlAppendUnicodeToString(refString, DRUNCSERVERNAME_U);
  134. // Add a '\'
  135. RtlAppendUnicodeToString(refString, L"\\");
  136. // Add the preferred dos name.
  137. RtlAppendUnicodeStringToString(refString, &ansiUnc);
  138. TRC_NRM((TB, "Reference string = %wZ", refString));
  139. }
  140. NTSTATUS DrDevice::CreateDevicePath(PUNICODE_STRING DevicePath)
  141. /*++
  142. Create NT DeviceName compatible with RDBSS convention
  143. Format is:
  144. \device\rdpdr\;<DriveLetter>:<sessionid>\ClientName\DosDeviceName
  145. --*/
  146. {
  147. NTSTATUS Status;
  148. UNICODE_STRING DevicePathTail;
  149. BEGIN_FN("DrDevice::CreateDevicePath");
  150. ASSERT(DevicePath != NULL);
  151. DevicePath->Length = 0;
  152. Status = RtlAppendUnicodeToString(DevicePath, RDPDR_DEVICE_NAME_U);
  153. if (!NT_ERROR(Status)) {
  154. // Add the reference string to the end:
  155. // Format is: \;<DriveLetter>:<sessionid>\clientName\share
  156. DevicePathTail.Length = 0;
  157. DevicePathTail.MaximumLength = DevicePath->MaximumLength - DevicePath->Length;
  158. DevicePathTail.Buffer = DevicePath->Buffer + (DevicePath->Length / sizeof(WCHAR));
  159. CreateReferenceString(&DevicePathTail);
  160. DevicePath->Length += DevicePathTail.Length;
  161. }
  162. TRC_NRM((TB, "DevicePath=%wZ", DevicePath));
  163. return Status;
  164. }
  165. NTSTATUS DrDevice::CreateDosDevicePath(PUNICODE_STRING DosDevicePath,
  166. PUNICODE_STRING DosDeviceName)
  167. {
  168. NTSTATUS Status;
  169. UNICODE_STRING linkNameTail;
  170. BEGIN_FN("DrDevice::CreateDosDevicePath");
  171. ASSERT(DosDevicePath != NULL);
  172. ASSERT(DosDeviceName != NULL);
  173. //
  174. // Create the "\\Sessions\\<sessionId>\\DosDevices\\<DosDeviceName>" string
  175. //
  176. DosDevicePath->Length = 0;
  177. Status = RtlAppendUnicodeToString(DosDevicePath, L"\\Sessions\\");
  178. if (!NT_ERROR(Status)) {
  179. //
  180. // Append the Session Number
  181. //
  182. linkNameTail.Buffer = (PWSTR)(((PBYTE)DosDevicePath->Buffer) +
  183. DosDevicePath->Length);
  184. linkNameTail.Length = 0;
  185. linkNameTail.MaximumLength = DosDevicePath->MaximumLength -
  186. DosDevicePath->Length;
  187. Status = RtlIntegerToUnicodeString(_Session->GetSessionId(), 10, &linkNameTail);
  188. }
  189. if (!NT_ERROR(Status)) {
  190. DosDevicePath->Length += linkNameTail.Length;
  191. //
  192. // Append DosDevices
  193. //
  194. Status = RtlAppendUnicodeToString(DosDevicePath, L"\\DosDevices\\");
  195. }
  196. if (!NT_ERROR(Status)) {
  197. Status = RtlAppendUnicodeStringToString(DosDevicePath, DosDeviceName);
  198. TRC_NRM((TB, "Created DosDevicePath: %wZ", DosDevicePath));
  199. }
  200. TRC_NRM((TB, "DosDevicePath=%wZ", DosDevicePath));
  201. return Status;
  202. }
  203. NTSTATUS DrDevice::CreateDosSymbolicLink(PUNICODE_STRING DosDeviceName)
  204. {
  205. WCHAR NtDevicePathBuffer[RDPDRMAXNTDEVICENAMEGLEN];
  206. UNICODE_STRING NtDevicePath;
  207. WCHAR DosDevicePathBuffer[MAX_PATH];
  208. UNICODE_STRING DosDevicePath;
  209. NTSTATUS Status;
  210. BEGIN_FN("DrDevice::CreateDosSymbolicLink");
  211. ASSERT(DosDeviceName != NULL);
  212. NtDevicePath.MaximumLength = sizeof(NtDevicePathBuffer);
  213. NtDevicePath.Length = 0;
  214. NtDevicePath.Buffer = &NtDevicePathBuffer[0];
  215. DosDevicePath.MaximumLength = sizeof(DosDevicePathBuffer);
  216. DosDevicePath.Length = 0;
  217. DosDevicePath.Buffer = &DosDevicePathBuffer[0];
  218. //
  219. // Get the NT device path to this dr device
  220. //
  221. Status = CreateDevicePath(&NtDevicePath);
  222. TRC_NRM((TB, "Nt Device path: %wZ", &NtDevicePath));
  223. if (!NT_ERROR(Status)) {
  224. //
  225. // Build the dos device path for this session
  226. //
  227. Status = CreateDosDevicePath(&DosDevicePath, DosDeviceName);
  228. TRC_NRM((TB, "Dos Device path: %wZ", &DosDevicePath));
  229. } else {
  230. TRC_ERR((TB, "Can't create nt device path: 0x%08lx", Status));
  231. return Status;
  232. }
  233. if (!NT_ERROR(Status)) {
  234. //
  235. // Actually create the symbolic link
  236. //
  237. IoDeleteSymbolicLink(&DosDevicePath);
  238. Status = IoCreateSymbolicLink(&DosDevicePath, &NtDevicePath);
  239. if (NT_SUCCESS(Status)) {
  240. TRC_NRM((TB, "Successfully created Symbolic link"));
  241. }
  242. else {
  243. TRC_NRM((TB, "Failed to create Symbolic link %x", Status));
  244. }
  245. } else {
  246. TRC_ERR((TB, "Can't create dos device path: 0x%08lx", Status));
  247. return Status;
  248. }
  249. return Status;
  250. }
  251. NTSTATUS DrDevice::VerifyCreateSecurity(PRX_CONTEXT RxContext, ULONG CurrentSessionId)
  252. {
  253. NTSTATUS Status;
  254. ULONG irpSessionId;
  255. BEGIN_FN("DrDevice::VerifyCreateSecurity");
  256. ASSERT(RxContext != NULL);
  257. ASSERT(RxContext->MajorFunction == IRP_MJ_CREATE);
  258. Status = IoGetRequestorSessionId(RxContext->CurrentIrp, &irpSessionId);
  259. if (NT_SUCCESS(Status)) {
  260. if (irpSessionId == CurrentSessionId) {
  261. TRC_DBG((TB, "Access accepted in DrCreate."));
  262. return STATUS_SUCCESS;
  263. }
  264. //
  265. // If the request is from the console session, it needs to be from a system
  266. // process.
  267. //
  268. else if (irpSessionId == CONSOLE_SESSIONID) {
  269. TRC_NRM((TB, "Create request from console process."));
  270. if (!DrIsSystemProcessRequest(RxContext->CurrentIrp,
  271. RxContext->CurrentIrpSp)) {
  272. TRC_ALT((TB, "Create request not from system process."));
  273. return STATUS_ACCESS_DENIED;
  274. }
  275. else {
  276. TRC_NRM((TB, "Create request from system. Access accepted."));
  277. return STATUS_SUCCESS;
  278. }
  279. }
  280. //
  281. // If not from the console and doesn't match the client entry session
  282. // ID then deny access.
  283. //
  284. else {
  285. TRC_ALT((TB, "Create request from %ld mismatch with session %ld.",
  286. irpSessionId, _Session->GetSessionId()));
  287. return STATUS_ACCESS_DENIED;
  288. }
  289. }
  290. else {
  291. TRC_ERR((TB, "IoGetRequestorSessionId failed with %08X.", Status));
  292. return Status;
  293. }
  294. }
  295. VOID DrDevice::FinishCreate(PRX_CONTEXT RxContext)
  296. {
  297. RxCaptureFcb;
  298. RX_FILE_TYPE StorageType;
  299. BEGIN_FN("DrDevice::FinishCreate");
  300. ULONG Attributes = 0; // in the fcb this is DirentRxFlags;
  301. ULONG NumLinks = 0; // in the fcb this is NumberOfLinks;
  302. LARGE_INTEGER CreationTime; // these fields are the same as for the Fcb
  303. LARGE_INTEGER LastAccessTime;
  304. LARGE_INTEGER LastWriteTime;
  305. LARGE_INTEGER LastChangeTime;
  306. LARGE_INTEGER AllocationSize; // common header fields
  307. LARGE_INTEGER FileSize;
  308. LARGE_INTEGER ValidDataLength;
  309. FCB_INIT_PACKET InitPacket = { &Attributes, &NumLinks, &CreationTime,
  310. &LastAccessTime, &LastWriteTime, &LastChangeTime,
  311. &AllocationSize, &FileSize, &ValidDataLength };
  312. ASSERT(RxContext != NULL);
  313. //
  314. // Pretty sure this is Device specific, but maybe caching the information
  315. // be generic? We might be able to fill in these values from member
  316. // variables
  317. //
  318. CreationTime.QuadPart = 0;
  319. LastAccessTime.QuadPart = 0;
  320. LastWriteTime.QuadPart = 0;
  321. LastChangeTime.QuadPart = 0;
  322. AllocationSize.QuadPart = 0;
  323. FileSize.QuadPart = 0x7FFFFFFF; // These need to be non-zero for reads to occur
  324. ValidDataLength.QuadPart = 0x7FFFFFFF;
  325. StorageType = RxInferFileType(RxContext);
  326. if (StorageType == FileTypeNotYetKnown) {
  327. StorageType = FileTypeFile;
  328. }
  329. RxFinishFcbInitialization(capFcb, (RX_FILE_TYPE)RDBSS_STORAGE_NTC(StorageType), &InitPacket);
  330. }
  331. NTSTATUS DrDevice::Create(IN OUT PRX_CONTEXT RxContext)
  332. /*++
  333. Routine Description:
  334. Opens a file (or device) across the network
  335. Arguments:
  336. RxContext - Context for the operation
  337. Return Value:
  338. Could return status success, cancelled, or pending.
  339. --*/
  340. {
  341. NTSTATUS Status;
  342. RxCaptureFcb;
  343. PMRX_SRV_OPEN SrvOpen = RxContext->pRelevantSrvOpen;
  344. PMRX_SRV_CALL SrvCall = RxContext->Create.pSrvCall;
  345. PMRX_NET_ROOT NetRoot = RxContext->Create.pNetRoot;
  346. SmartPtr<DrSession> Session = _Session;
  347. PRDPDR_IOREQUEST_PACKET pIoPacket;
  348. ULONG cbPacketSize;
  349. PUNICODE_STRING FileName = GET_ALREADY_PREFIXED_NAME(SrvOpen, capFcb);
  350. LARGE_INTEGER TimeOut;
  351. BEGIN_FN("DrDevice::Create");
  352. ASSERT(RxContext != NULL);
  353. ASSERT(RxContext->MajorFunction == IRP_MJ_CREATE);
  354. if (!Session->IsConnected()) {
  355. TRC_ALT((TB, "Tried to open client device while not "
  356. "connected. State: %ld", Session->GetState()));
  357. return STATUS_DEVICE_NOT_CONNECTED;
  358. }
  359. //
  360. // Security check the irp.
  361. //
  362. Status = VerifyCreateSecurity(RxContext, Session->GetSessionId());
  363. if (NT_ERROR(Status)) {
  364. return Status;
  365. }
  366. //
  367. // We already have an exclusive lock on the fcb. Finish the create.
  368. //
  369. if (NT_SUCCESS(Status)) {
  370. //
  371. // JC: Worry about this when do buffering
  372. //
  373. SrvOpen->Flags |= SRVOPEN_FLAG_DONTUSE_WRITE_CACHING;
  374. SrvOpen->Flags |= SRVOPEN_FLAG_DONTUSE_READ_CACHING;
  375. RxContext->pFobx = RxCreateNetFobx(RxContext, RxContext->pRelevantSrvOpen);
  376. if (RxContext->pFobx != NULL) {
  377. // Fobx keeps a reference to the device so it won't go away
  378. AddRef();
  379. RxContext->pFobx->Context = (DrDevice *)this;
  380. Status = STATUS_SUCCESS;
  381. } else {
  382. Status = STATUS_INSUFFICIENT_RESOURCES;
  383. }
  384. }
  385. if (NT_SUCCESS(Status)) {
  386. //
  387. // Get the file name
  388. //
  389. // If the file name only has back slash at the end and rdbss didn't record it
  390. // we need to pass this to the client
  391. //
  392. if (GetDeviceType() == RDPDR_DTYP_FILESYSTEM && FlagOn(RxContext->Create.Flags, RX_CONTEXT_CREATE_FLAG_STRIPPED_TRAILING_BACKSLASH) &&
  393. FileName->Length == 0) {
  394. FileName->Buffer = L"\\";
  395. FileName->Length = FileName->MaximumLength = sizeof(WCHAR);
  396. }
  397. TRC_DBG((TB, "Attempt to open = %wZ", FileName));
  398. //
  399. // Build the create packet and send it to the client
  400. // We add the string null terminator to the filename
  401. //
  402. if (FileName->Length) {
  403. //
  404. // FileName Length does not include string null terminator.
  405. //
  406. cbPacketSize = sizeof(RDPDR_IOREQUEST_PACKET) + FileName->Length + sizeof(WCHAR);
  407. }
  408. else {
  409. cbPacketSize = sizeof(RDPDR_IOREQUEST_PACKET);
  410. }
  411. pIoPacket = (PRDPDR_IOREQUEST_PACKET)new(PagedPool) BYTE[cbPacketSize];
  412. if (pIoPacket != NULL) {
  413. memset(pIoPacket, 0, cbPacketSize);
  414. pIoPacket->Header.Component = RDPDR_CTYP_CORE;
  415. pIoPacket->Header.PacketId = DR_CORE_DEVICE_IOREQUEST;
  416. pIoPacket->IoRequest.DeviceId = _DeviceId;
  417. pIoPacket->IoRequest.MajorFunction = IRP_MJ_CREATE;
  418. pIoPacket->IoRequest.MinorFunction = 0;
  419. pIoPacket->IoRequest.Parameters.Create.DesiredAccess =
  420. RxContext->Create.NtCreateParameters.DesiredAccess;
  421. pIoPacket->IoRequest.Parameters.Create.AllocationSize =
  422. RxContext->Create.NtCreateParameters.AllocationSize;
  423. pIoPacket->IoRequest.Parameters.Create.FileAttributes =
  424. RxContext->Create.NtCreateParameters.FileAttributes;
  425. pIoPacket->IoRequest.Parameters.Create.ShareAccess =
  426. RxContext->Create.NtCreateParameters.ShareAccess;
  427. pIoPacket->IoRequest.Parameters.Create.Disposition =
  428. RxContext->Create.NtCreateParameters.Disposition;
  429. pIoPacket->IoRequest.Parameters.Create.CreateOptions =
  430. RxContext->Create.NtCreateParameters.CreateOptions;
  431. //
  432. // File name path
  433. //
  434. if (FileName->Length) {
  435. pIoPacket->IoRequest.Parameters.Create.PathLength = FileName->Length + sizeof(WCHAR);
  436. RtlCopyMemory(pIoPacket + 1, FileName->Buffer, FileName->Length);
  437. //
  438. // Packet is already zero'd, so no need to null terminate the string
  439. //
  440. } else {
  441. pIoPacket->IoRequest.Parameters.Create.PathLength = 0;
  442. }
  443. TRC_NRM((TB, "Sending Create IoRequest"));
  444. TRC_NRM((TB, " DesiredAccess: %lx",
  445. pIoPacket->IoRequest.Parameters.Create.DesiredAccess));
  446. TRC_NRM((TB, " AllocationSize: %lx",
  447. pIoPacket->IoRequest.Parameters.Create.AllocationSize));
  448. TRC_NRM((TB, " FileAttributes: %lx",
  449. pIoPacket->IoRequest.Parameters.Create.FileAttributes));
  450. TRC_NRM((TB, " ShareAccess: %lx",
  451. pIoPacket->IoRequest.Parameters.Create.ShareAccess));
  452. TRC_NRM((TB, " Disposition: %lx",
  453. pIoPacket->IoRequest.Parameters.Create.Disposition));
  454. TRC_NRM((TB, " CreateOptions: %lx",
  455. pIoPacket->IoRequest.Parameters.Create.CreateOptions));
  456. //
  457. // Always do create synchronously
  458. // 30 seconds in hundreds of nano-seconds, in case client hangs,
  459. // we don't want this create thread to wait infinitely.
  460. //
  461. TimeOut = RtlEnlargedIntegerMultiply( 300000, -1000 );
  462. Status = SendIoRequest(RxContext, pIoPacket, cbPacketSize, TRUE, &TimeOut);
  463. delete pIoPacket;
  464. }
  465. else {
  466. Status = STATUS_INSUFFICIENT_RESOURCES;
  467. }
  468. }
  469. if (NT_SUCCESS(Status)) {
  470. FinishCreate(RxContext);
  471. }
  472. else {
  473. // Release the Device Reference
  474. if (RxContext->pFobx != NULL) {
  475. ((DrDevice *)RxContext->pFobx->Context)->Release();
  476. RxContext->pFobx->Context = NULL;
  477. }
  478. }
  479. return Status;
  480. }
  481. NTSTATUS DrDevice::Flush(IN OUT PRX_CONTEXT RxContext)
  482. {
  483. BEGIN_FN("DrDevice::Flush");
  484. ASSERT(RxContext != NULL);
  485. return STATUS_SUCCESS;
  486. }
  487. NTSTATUS DrDevice::Write(IN OUT PRX_CONTEXT RxContext, IN BOOL LowPrioSend)
  488. {
  489. NTSTATUS Status;
  490. RxCaptureFcb;
  491. RxCaptureFobx;
  492. PMRX_NET_ROOT NetRoot = capFcb->pNetRoot;
  493. PMRX_SRV_CALL SrvCall = NetRoot->pSrvCall;
  494. SmartPtr<DrSession> Session = _Session;
  495. DrFile *pFile = (DrFile *)RxContext->pFobx->Context2;
  496. SmartPtr<DrFile> FileObj = pFile;
  497. PRDPDR_IOREQUEST_PACKET pIoPacket;
  498. ULONG cbPacketSize = sizeof(RDPDR_IOREQUEST_PACKET) +
  499. RxContext->LowIoContext.ParamsFor.ReadWrite.ByteCount;
  500. PVOID pv;
  501. BEGIN_FN("DrDevice::Write");
  502. //
  503. // Make sure it's okay to access the Client at this time
  504. // This is an optimization, we don't need to acquire the spin lock,
  505. // because it is okay if we're not, we'll just catch it later
  506. //
  507. ASSERT(RxContext != NULL);
  508. ASSERT(RxContext->MajorFunction == IRP_MJ_WRITE);
  509. if (!Session->IsConnected()) {
  510. return STATUS_DEVICE_NOT_CONNECTED;
  511. }
  512. if (FileObj == NULL) {
  513. return STATUS_DEVICE_NOT_CONNECTED;
  514. }
  515. //
  516. // Make sure the device is still enabled
  517. //
  518. if (_DeviceStatus != dsAvailable) {
  519. TRC_ALT((TB, "Tried to write to client device which is not "
  520. "available. State: %ld", _DeviceStatus));
  521. return STATUS_DEVICE_NOT_CONNECTED;
  522. }
  523. if (RxContext->LowIoContext.ParamsFor.ReadWrite.ByteCount == 0) {
  524. RxContext->IoStatusBlock.Information = 0;
  525. return STATUS_SUCCESS;
  526. }
  527. pIoPacket = (PRDPDR_IOREQUEST_PACKET)new(PagedPool) BYTE[cbPacketSize];
  528. if (pIoPacket != NULL) {
  529. memset(pIoPacket, 0, cbPacketSize);
  530. pIoPacket->Header.Component = RDPDR_CTYP_CORE;
  531. pIoPacket->Header.PacketId = DR_CORE_DEVICE_IOREQUEST;
  532. pIoPacket->IoRequest.DeviceId = _DeviceId;
  533. pIoPacket->IoRequest.FileId = FileObj->GetFileId();
  534. pIoPacket->IoRequest.MajorFunction = IRP_MJ_WRITE;
  535. pIoPacket->IoRequest.MinorFunction = 0;
  536. pIoPacket->IoRequest.Parameters.Write.Length =
  537. RxContext->LowIoContext.ParamsFor.ReadWrite.ByteCount;
  538. //
  539. // Get the low dword byte offset of where to write
  540. //
  541. pIoPacket->IoRequest.Parameters.Write.OffsetLow =
  542. ((LONG)((LONGLONG)(RxContext->LowIoContext.ParamsFor.ReadWrite.ByteOffset)
  543. & 0xffffffff));
  544. //
  545. // Get the high dword by offset of where to write
  546. //
  547. pIoPacket->IoRequest.Parameters.Write.OffsetHigh =
  548. ((LONG)((LONGLONG)(RxContext->LowIoContext.ParamsFor.ReadWrite.ByteOffset)
  549. >> 32));
  550. TRC_DBG((TB, "ByteOffset to write = %x",
  551. RxContext->LowIoContext.ParamsFor.ReadWrite.ByteOffset));
  552. pv = MmGetSystemAddressForMdlSafe(RxContext->LowIoContext.ParamsFor.ReadWrite.Buffer,
  553. NormalPagePriority);
  554. if (pv != NULL) {
  555. RtlCopyMemory(pIoPacket + 1, pv, // + RxContext->LowIoContext.ParamsFor.ReadWrite.ByteOffset?,
  556. pIoPacket->IoRequest.Parameters.Write.Length);
  557. TRC_DBG((TB, "Write packet length: 0x%lx",
  558. pIoPacket->IoRequest.Parameters.Write.Length));
  559. Status = SendIoRequest(RxContext, pIoPacket, cbPacketSize,
  560. (BOOLEAN)!BooleanFlagOn(RxContext->Flags, RX_CONTEXT_FLAG_ASYNC_OPERATION),
  561. NULL, LowPrioSend);
  562. TRC_NRM((TB, "IoRequestWrite returned to DrWrite: %lx", Status));
  563. }
  564. else {
  565. Status = STATUS_INSUFFICIENT_RESOURCES;
  566. }
  567. delete pIoPacket;
  568. } else {
  569. Status = STATUS_INSUFFICIENT_RESOURCES;
  570. }
  571. return Status;
  572. }
  573. NTSTATUS DrDevice::Read(IN OUT PRX_CONTEXT RxContext)
  574. {
  575. NTSTATUS Status = STATUS_NOT_IMPLEMENTED;
  576. RxCaptureFcb;
  577. RxCaptureFobx;
  578. PMRX_NET_ROOT NetRoot = capFcb->pNetRoot;
  579. PMRX_SRV_CALL SrvCall = NetRoot->pSrvCall;
  580. SmartPtr<DrSession> Session = _Session;
  581. DrFile *pFile = (DrFile *)RxContext->pFobx->Context2;
  582. SmartPtr<DrFile> FileObj = pFile;
  583. RDPDR_IOREQUEST_PACKET IoPacket;
  584. BEGIN_FN("DrDevice::Read");
  585. //
  586. // Make sure it's okay to access the Client at this time
  587. // This is an optimization, we don't need to acquire the spin lock,
  588. // because it is okay if we're not, we'll just catch it later
  589. //
  590. ASSERT(Session != NULL);
  591. ASSERT(RxContext != NULL);
  592. ASSERT(RxContext->MajorFunction == IRP_MJ_READ);
  593. if (!Session->IsConnected()) {
  594. return STATUS_DEVICE_NOT_CONNECTED;
  595. }
  596. if (FileObj == NULL) {
  597. return STATUS_DEVICE_NOT_CONNECTED;
  598. }
  599. //
  600. // Make sure the device is still enabled
  601. //
  602. if (_DeviceStatus != dsAvailable) {
  603. TRC_ALT((TB, "Tried to read from client device which is not "
  604. "available. State: %ld", _DeviceStatus));
  605. return STATUS_DEVICE_NOT_CONNECTED;
  606. }
  607. memset(&IoPacket, 0, sizeof(IoPacket));
  608. IoPacket.Header.Component = RDPDR_CTYP_CORE;
  609. IoPacket.Header.PacketId = DR_CORE_DEVICE_IOREQUEST;
  610. IoPacket.IoRequest.DeviceId = _DeviceId;
  611. IoPacket.IoRequest.FileId = FileObj->GetFileId();
  612. IoPacket.IoRequest.MajorFunction = IRP_MJ_READ;
  613. IoPacket.IoRequest.MinorFunction = 0;
  614. IoPacket.IoRequest.Parameters.Read.Length =
  615. RxContext->LowIoContext.ParamsFor.ReadWrite.ByteCount;
  616. //
  617. // Get low dword of read offset
  618. //
  619. IoPacket.IoRequest.Parameters.Read.OffsetLow =
  620. ((LONG)((LONGLONG)(RxContext->LowIoContext.ParamsFor.ReadWrite.ByteOffset)
  621. & 0xffffffff));
  622. //
  623. // Get high dword of read offset
  624. //
  625. IoPacket.IoRequest.Parameters.Read.OffsetHigh =
  626. ((LONG)((LONGLONG)(RxContext->LowIoContext.ParamsFor.ReadWrite.ByteOffset)
  627. >> 32));
  628. TRC_NRM((TB, "DrRead reading length: %ld, at offset: %x",
  629. IoPacket.IoRequest.Parameters.Read.Length,
  630. RxContext->LowIoContext.ParamsFor.ReadWrite.ByteOffset));
  631. Status = SendIoRequest(RxContext, &IoPacket, sizeof(IoPacket),
  632. (BOOLEAN)!BooleanFlagOn(RxContext->Flags,RX_CONTEXT_FLAG_ASYNC_OPERATION));
  633. TRC_NRM((TB, "IoRequestWrite returned to DrRead: %lx", Status));
  634. return Status;
  635. }
  636. NTSTATUS DrDevice::IoControl(IN OUT PRX_CONTEXT RxContext)
  637. {
  638. NTSTATUS Status = STATUS_SUCCESS;
  639. RxCaptureFcb;
  640. RxCaptureFobx;
  641. PMRX_NET_ROOT NetRoot = capFcb->pNetRoot;
  642. PMRX_SRV_CALL SrvCall = NetRoot->pSrvCall;
  643. SmartPtr<DrSession> Session = _Session;
  644. DrFile *pFile = (DrFile *)RxContext->pFobx->Context2;
  645. SmartPtr<DrFile> FileObj = pFile;
  646. PRDPDR_IOREQUEST_PACKET pIoPacket = NULL;
  647. PLOWIO_CONTEXT LowIoContext = &RxContext->LowIoContext;
  648. ULONG cbPacketSize = sizeof(RDPDR_IOREQUEST_PACKET) +
  649. LowIoContext->ParamsFor.IoCtl.InputBufferLength;
  650. ULONG IoControlCode = LowIoContext->ParamsFor.IoCtl.IoControlCode;
  651. ULONG InputBufferLength = LowIoContext->ParamsFor.IoCtl.InputBufferLength;
  652. ULONG OutputBufferLength = LowIoContext->ParamsFor.IoCtl.OutputBufferLength;
  653. PVOID InputBuffer = LowIoContext->ParamsFor.IoCtl.pInputBuffer;
  654. PVOID OutputBuffer = LowIoContext->ParamsFor.IoCtl.pOutputBuffer;
  655. BEGIN_FN("DrDevice::IoControl");
  656. //
  657. // Make sure it's okay to access the Client at this time
  658. // This is an optimization, we don't need to acquire the spin lock,
  659. // because it is okay if we're not, we'll just catch it later
  660. //
  661. ASSERT(Session != NULL);
  662. ASSERT(RxContext != NULL);
  663. ASSERT(RxContext->MajorFunction == IRP_MJ_DEVICE_CONTROL ||
  664. RxContext->MajorFunction == IRP_MJ_INTERNAL_DEVICE_CONTROL ||
  665. RxContext->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL);
  666. if (COMPARE_VERSION(Session->GetClientVersion().Minor,
  667. Session->GetClientVersion().Major, 4, 1) < 0) {
  668. TRC_ALT((TB, "Failing IoCtl for client that doesn't support it"));
  669. return STATUS_NOT_IMPLEMENTED;
  670. }
  671. if (!Session->IsConnected()) {
  672. return STATUS_DEVICE_NOT_CONNECTED;
  673. }
  674. if (FileObj == NULL) {
  675. return STATUS_DEVICE_NOT_CONNECTED;
  676. }
  677. //
  678. // Make sure the device is still enabled
  679. //
  680. if (_DeviceStatus != dsAvailable) {
  681. TRC_ALT((TB, "Tried to send IoControl to client device which is not "
  682. "available. State: %ld", _DeviceStatus));
  683. return STATUS_DEVICE_NOT_CONNECTED;
  684. }
  685. //
  686. // Validate the buffer
  687. //
  688. __try {
  689. if (RxContext->CurrentIrp->RequestorMode != KernelMode) {
  690. // If the buffering method is METHOD_NEITHER or METHOD_IN_DIRECT
  691. // then we need to probe the input buffer
  692. if ((IoControlCode & 0x1) &&
  693. InputBuffer != NULL && InputBufferLength != 0) {
  694. ProbeForRead(InputBuffer, InputBufferLength, sizeof(UCHAR));
  695. }
  696. // If the buffering method is METHOD_NEITHER or METHOD_OUT_DIRECT
  697. // then we need to probe the output buffer
  698. if ((IoControlCode & 0x2) &&
  699. OutputBuffer != NULL && OutputBufferLength != 0) {
  700. ProbeForWrite(OutputBuffer, OutputBufferLength, sizeof(UCHAR));
  701. }
  702. }
  703. pIoPacket = (PRDPDR_IOREQUEST_PACKET)new(PagedPool) BYTE[cbPacketSize];
  704. if (pIoPacket != NULL) {
  705. memset(pIoPacket, 0, cbPacketSize);
  706. //
  707. // FS Control uses the same path as IO Control.
  708. //
  709. pIoPacket->Header.Component = RDPDR_CTYP_CORE;
  710. pIoPacket->Header.PacketId = DR_CORE_DEVICE_IOREQUEST;
  711. pIoPacket->IoRequest.DeviceId = _DeviceId;
  712. pIoPacket->IoRequest.FileId = FileObj->GetFileId();
  713. pIoPacket->IoRequest.MajorFunction = IRP_MJ_DEVICE_CONTROL;
  714. pIoPacket->IoRequest.MinorFunction =
  715. LowIoContext->ParamsFor.IoCtl.MinorFunction;
  716. pIoPacket->IoRequest.Parameters.DeviceIoControl.OutputBufferLength =
  717. LowIoContext->ParamsFor.IoCtl.OutputBufferLength;
  718. pIoPacket->IoRequest.Parameters.DeviceIoControl.InputBufferLength =
  719. LowIoContext->ParamsFor.IoCtl.InputBufferLength;
  720. pIoPacket->IoRequest.Parameters.DeviceIoControl.IoControlCode =
  721. LowIoContext->ParamsFor.IoCtl.IoControlCode;
  722. if (LowIoContext->ParamsFor.IoCtl.InputBufferLength != 0) {
  723. TRC_NRM((TB, "DrIoControl inputbufferlength: %lx",
  724. LowIoContext->ParamsFor.IoCtl.InputBufferLength));
  725. RtlCopyMemory(pIoPacket + 1, LowIoContext->ParamsFor.IoCtl.pInputBuffer,
  726. LowIoContext->ParamsFor.IoCtl.InputBufferLength);
  727. } else {
  728. TRC_NRM((TB, "DrIoControl with no inputbuffer"));
  729. }
  730. Status = SendIoRequest(RxContext, pIoPacket, cbPacketSize,
  731. (BOOLEAN)!BooleanFlagOn(RxContext->Flags,RX_CONTEXT_FLAG_ASYNC_OPERATION));
  732. TRC_NRM((TB, "IoRequestWrite returned to DrIoControl: %lx", Status));
  733. delete pIoPacket;
  734. } else {
  735. TRC_ERR((TB, "DrIoControl unable to allocate packet: %lx", Status));
  736. Status = STATUS_INSUFFICIENT_RESOURCES;
  737. }
  738. return Status;
  739. }
  740. __except (EXCEPTION_EXECUTE_HANDLER) {
  741. TRC_ERR((TB, "Invalid buffer parameter(s)"));
  742. if (pIoPacket) {
  743. delete pIoPacket;
  744. }
  745. return STATUS_INVALID_PARAMETER;
  746. }
  747. }
  748. NTSTATUS DrDevice::Close(IN OUT PRX_CONTEXT RxContext)
  749. {
  750. NTSTATUS Status;
  751. RxCaptureFcb;
  752. RxCaptureFobx;
  753. PMRX_NET_ROOT NetRoot = capFcb->pNetRoot;
  754. PMRX_SRV_CALL SrvCall = NetRoot->pSrvCall;
  755. SmartPtr<DrSession> Session = _Session;
  756. DrFile *pFile = (DrFile *)RxContext->pFobx->Context2;
  757. SmartPtr<DrFile> FileObj = pFile;
  758. RDPDR_IOREQUEST_PACKET IoPacket;
  759. BEGIN_FN("DrDevice::Close");
  760. //
  761. // Make sure it's okay to access the Client at this time
  762. // This is an optimization, we don't need to acquire the spin lock,
  763. // because it is okay if we're not, we'll just catch it later
  764. //
  765. ASSERT(Session != NULL);
  766. ASSERT(RxContext != NULL);
  767. ASSERT(RxContext->MajorFunction == IRP_MJ_CLOSE);
  768. if (!Session->IsConnected()) {
  769. // Review: Since we're not connected, there shouldn't be any reason
  770. // to say it isn't closed, right?
  771. return STATUS_SUCCESS;
  772. }
  773. if (FileObj == NULL) {
  774. return STATUS_DEVICE_NOT_CONNECTED;
  775. }
  776. if (_DeviceStatus != dsAvailable) {
  777. TRC_ALT((TB, "Tried to close a client device which is not "
  778. "available. State: %ld", _DeviceStatus));
  779. return STATUS_SUCCESS;
  780. }
  781. memset(&IoPacket, 0, sizeof(IoPacket));
  782. IoPacket.Header.Component = RDPDR_CTYP_CORE;
  783. IoPacket.Header.PacketId = DR_CORE_DEVICE_IOREQUEST;
  784. IoPacket.IoRequest.DeviceId = _DeviceId;
  785. IoPacket.IoRequest.FileId = FileObj->GetFileId();
  786. IoPacket.IoRequest.MajorFunction = IRP_MJ_CLOSE;
  787. IoPacket.IoRequest.MinorFunction = 0;
  788. Status = SendIoRequest(RxContext, &IoPacket, sizeof(IoPacket),
  789. (BOOLEAN)!BooleanFlagOn(RxContext->Flags,RX_CONTEXT_FLAG_ASYNC_OPERATION));
  790. return Status;
  791. }
  792. NTSTATUS DrDevice::Cleanup(IN OUT PRX_CONTEXT RxContext)
  793. {
  794. NTSTATUS Status;
  795. RxCaptureFcb;
  796. RxCaptureFobx;
  797. PMRX_NET_ROOT NetRoot = capFcb->pNetRoot;
  798. PMRX_SRV_CALL SrvCall = NetRoot->pSrvCall;
  799. SmartPtr<DrSession> Session = _Session;
  800. DrFile *pFile = (DrFile *)RxContext->pFobx->Context2;
  801. SmartPtr<DrFile> FileObj = pFile;
  802. RDPDR_IOREQUEST_PACKET IoPacket;
  803. BEGIN_FN("DrDevice::Cleanup");
  804. //
  805. // Make sure it's okay to access the Client at this time
  806. // This is an optimization, we don't need to acquire the spin lock,
  807. // because it is okay if we're not, we'll just catch it later
  808. //
  809. ASSERT(RxContext != NULL);
  810. ASSERT(RxContext->MajorFunction == IRP_MJ_CLEANUP);
  811. if (!Session->IsConnected()) {
  812. return STATUS_SUCCESS;
  813. }
  814. if (FileObj == NULL) {
  815. return STATUS_DEVICE_NOT_CONNECTED;
  816. }
  817. //
  818. // Make sure the device is still enabled
  819. //
  820. if (_DeviceStatus != dsAvailable) {
  821. TRC_ALT((TB, "Tried to cleanup a client device which is not "
  822. "available. State: %ld", _DeviceStatus));
  823. return STATUS_SUCCESS;
  824. }
  825. memset(&IoPacket, 0, sizeof(IoPacket));
  826. IoPacket.Header.Component = RDPDR_CTYP_CORE;
  827. IoPacket.Header.PacketId = DR_CORE_DEVICE_IOREQUEST;
  828. IoPacket.IoRequest.DeviceId = _DeviceId;
  829. IoPacket.IoRequest.FileId = FileObj->GetFileId();
  830. IoPacket.IoRequest.MajorFunction = IRP_MJ_CLEANUP;
  831. IoPacket.IoRequest.MinorFunction = 0;
  832. Status = SendIoRequest(RxContext, &IoPacket, sizeof(IoPacket),
  833. (BOOLEAN)!BooleanFlagOn(RxContext->Flags,RX_CONTEXT_FLAG_ASYNC_OPERATION));
  834. return Status;
  835. }
  836. NTSTATUS DrDevice::SendIoRequest(IN OUT PRX_CONTEXT RxContext,
  837. PRDPDR_IOREQUEST_PACKET IoRequest, ULONG Length,
  838. BOOLEAN Synchronous, PLARGE_INTEGER TimeOut, BOOL LowPrioSend)
  839. /*++
  840. Routine Description:
  841. Sends the request to the client, and manages the completion. This IO
  842. can only be completed once, by returning non-STATUS_PENDING or by
  843. calling RxLowIoCompletion.
  844. Arguments:
  845. RxContext - The IoRequest
  846. IoRequest - The IoRequest packet
  847. Length - size of IoRequest packet
  848. Synchronous - duh
  849. LowPrioSend - Packet should be sent to client at low priority.
  850. Return Value:
  851. None
  852. --*/
  853. {
  854. NTSTATUS Status = STATUS_SUCCESS;
  855. USHORT Mid = INVALID_MID;
  856. BOOL ExchangeCreated = FALSE;
  857. DrIoContext *Context = NULL;
  858. SmartPtr<DrExchange> Exchange;
  859. SmartPtr<DrDevice> Device(this);
  860. BEGIN_FN("DrDevice::SendIoRequest");
  861. ASSERT(RxContext != NULL);
  862. ASSERT(IoRequest != NULL);
  863. ASSERT(Length >= sizeof(RDPDR_IOREQUEST_PACKET));
  864. Context = new DrIoContext(RxContext, Device);
  865. if (Context != NULL) {
  866. Status = STATUS_SUCCESS;
  867. } else {
  868. Status = STATUS_INSUFFICIENT_RESOURCES;
  869. }
  870. if (NT_SUCCESS(Status)) {
  871. //
  872. // Set up a mapping so the completion response handler can
  873. // find this context
  874. //
  875. TRC_DBG((TB, "Create the context for this I/O"));
  876. KeClearEvent(&RxContext->SyncEvent);
  877. ExchangeCreated =
  878. _Session->GetExchangeManager().CreateExchange(this, Context, Exchange);
  879. if (ExchangeCreated) {
  880. //
  881. // No need to explicit Refcount for the RxContext
  882. // The place it's been used is the cancel routine.
  883. // Since CreateExchange holds the ref count. we are okay
  884. //
  885. //Exchange->AddRef();
  886. RxContext->MRxContext[MRX_DR_CONTEXT] = (DrExchange *)Exchange;
  887. Status = STATUS_SUCCESS;
  888. } else {
  889. delete Context;
  890. Status = STATUS_INSUFFICIENT_RESOURCES;
  891. }
  892. }
  893. if (NT_SUCCESS(Status)) {
  894. TRC_DBG((TB, "Writing IoRequest to the client channel"));
  895. //
  896. // Mark the IoRequest with the context mapper
  897. //
  898. IoRequest->IoRequest.CompletionId = Exchange->_Mid;
  899. TRC_DBG((TB, "IO packet:"));
  900. TRC_DBG((TB, " Component %c%c",
  901. HIBYTE(IoRequest->Header.Component),
  902. LOBYTE(IoRequest->Header.Component)));
  903. TRC_DBG((TB, " PacketId %c%c",
  904. HIBYTE(IoRequest->Header.PacketId),
  905. LOBYTE(IoRequest->Header.PacketId)));
  906. TRC_DBG((TB, " DeviceId 0x%lx",
  907. IoRequest->IoRequest.DeviceId));
  908. TRC_DBG((TB, " FileId 0x%lx",
  909. IoRequest->IoRequest.FileId));
  910. TRC_DBG((TB, " MajorFunction 0x%lx",
  911. IoRequest->IoRequest.MajorFunction));
  912. TRC_DBG((TB, " MinorFunction 0x%lx",
  913. IoRequest->IoRequest.MinorFunction));
  914. Status = _Session->GetExchangeManager().StartExchange(Exchange, this,
  915. IoRequest, Length, LowPrioSend);
  916. }
  917. if (NT_SUCCESS(Status)) {
  918. TRC_DBG((TB, "Setting cancel routine for Io"));
  919. //
  920. // Set this after sending the IO to the client
  921. // if cancel was requested already, we can just call the
  922. // cancel routine ourselves
  923. //
  924. Status = RxSetMinirdrCancelRoutine(RxContext,
  925. MinirdrCancelRoutine);
  926. if (Status == STATUS_CANCELLED) {
  927. TRC_NRM((TB, "Io was already cancelled"));
  928. MinirdrCancelRoutine(RxContext);
  929. Status = STATUS_SUCCESS;
  930. }
  931. }
  932. if (Synchronous) {
  933. //
  934. // Some failure is going to prevent our completions routine from
  935. // being called. Do that work now.
  936. //
  937. if (!ExchangeCreated) {
  938. //
  939. // If we couldn't even create the exchange, we need to just
  940. // complete the IO as failed
  941. //
  942. CompleteRxContext(RxContext, Status, 0);
  943. }
  944. else {
  945. TRC_DBG((TB, "Waiting for IoResult for synchronous request"));
  946. if (NT_SUCCESS(Status)) {
  947. Status = KeWaitForSingleObject(&RxContext->SyncEvent, UserRequest,
  948. KernelMode, FALSE, TimeOut);
  949. if (Status == STATUS_TIMEOUT) {
  950. RxContext->IoStatusBlock.Status = Status;
  951. TRC_DBG((TB, "Wait timed out"));
  952. MarkTimedOut(Exchange);
  953. }
  954. else {
  955. Status = RxContext->IoStatusBlock.Status;
  956. }
  957. }
  958. else {
  959. //
  960. // If we created the exchange and then got a transport failure
  961. // we'll be disconnected, and the the I/O will be completed
  962. // the same way all outstanding I/O is completed when we are
  963. // disconnected.
  964. //
  965. if (MarkTimedOut(Exchange)) {
  966. CompleteRxContext(RxContext, Status, 0);
  967. }
  968. else {
  969. Status = KeWaitForSingleObject(&RxContext->SyncEvent, UserRequest,
  970. KernelMode, FALSE, NULL);
  971. Status = RxContext->IoStatusBlock.Status;
  972. }
  973. }
  974. }
  975. }
  976. else {
  977. TRC_DBG((TB, "Not waiting for IoResult for asynchronous request"));
  978. //
  979. // Some failure is going to prevent our completions routine from
  980. // being called. Do that work now.
  981. //
  982. if (!ExchangeCreated) {
  983. //
  984. // If we couldn't even create the exchange, we need to just
  985. // complete the IO as failed
  986. //
  987. CompleteRxContext(RxContext, Status, 0);
  988. }
  989. else {
  990. //
  991. // If we created the exchange and then got a transport failure
  992. // we'll be disconnected, and the the I/O will be completed
  993. // the same way all outstanding I/O is completed when we are
  994. // disconnected.
  995. //
  996. }
  997. Status = STATUS_PENDING;
  998. }
  999. return Status;
  1000. }
  1001. VOID DrDevice::CompleteBusyExchange(SmartPtr<DrExchange> &Exchange,
  1002. NTSTATUS Status, ULONG Information)
  1003. /*++
  1004. Routine Description:
  1005. Takes an exchange which is already busy and
  1006. Arguments:
  1007. Mid - Id to find
  1008. ExchangeFound - Pointer to a storage for the pointer to the context
  1009. Return Value:
  1010. drexchBusy - Exchange provided, was marked busy
  1011. drexchCancelled - Exchange provided, was already cancelled
  1012. drexchUnavailable - Exchange not provided, disconnected
  1013. --*/
  1014. {
  1015. DrIoContext *Context;
  1016. PRX_CONTEXT RxContext;
  1017. BEGIN_FN("DrDevice::CompleteBusyExchange");
  1018. DrAcquireMutex();
  1019. Context = (DrIoContext *)Exchange->_Context;
  1020. ASSERT(Context != NULL);
  1021. ASSERT(Context->_Busy);
  1022. RxContext = Context->_RxContext;
  1023. Context->_RxContext = NULL;
  1024. Exchange->_Context = NULL;
  1025. DrReleaseMutex();
  1026. //
  1027. // Note: We've left the Mutex, and the Exchange with no
  1028. // context still exists and can be looked up until we Discard it.
  1029. //
  1030. if (RxContext != NULL) {
  1031. CompleteRxContext(RxContext, Status, Information);
  1032. }
  1033. _Session->GetExchangeManager().Discard(Exchange);
  1034. delete Context;
  1035. }
  1036. VOID DrDevice::DiscardBusyExchange(SmartPtr<DrExchange> &Exchange)
  1037. {
  1038. DrIoContext *Context;
  1039. BEGIN_FN("DrDevice::DiscardBusyExchange");
  1040. DrAcquireMutex();
  1041. Context = (DrIoContext *)Exchange->_Context;
  1042. ASSERT(Context != NULL);
  1043. ASSERT(Context->_Busy);
  1044. ASSERT(Context->_RxContext == NULL);
  1045. Exchange->_Context = NULL;
  1046. DrReleaseMutex();
  1047. //
  1048. // Note: We've left the Mutex, and the Exchange with no
  1049. // context still exists and can be looked up until we Discard it.
  1050. //
  1051. _Session->GetExchangeManager().Discard(Exchange);
  1052. delete Context;
  1053. }
  1054. BOOL DrDevice::MarkBusy(SmartPtr<DrExchange> &Exchange)
  1055. /*++
  1056. Routine Description:
  1057. Marks an Exchange context as busy so it won't be cancelled
  1058. while we're copying in to its buffer
  1059. Arguments:
  1060. Exchange - Context
  1061. Return Value:
  1062. TRUE - if Marked Busy
  1063. FALSE - if Context was gone
  1064. --*/
  1065. {
  1066. NTSTATUS Status;
  1067. BOOL rc;
  1068. DrIoContext *Context = NULL;
  1069. BEGIN_FN("DrDevice::MarkBusy");
  1070. ASSERT(Exchange != NULL);
  1071. DrAcquireMutex();
  1072. Context = (DrIoContext *)Exchange->_Context;
  1073. if (Context != NULL) {
  1074. ASSERT(!Context->_Busy);
  1075. Context->_Busy = TRUE;
  1076. rc = TRUE;
  1077. } else {
  1078. rc = FALSE;
  1079. }
  1080. DrReleaseMutex();
  1081. return rc;
  1082. }
  1083. VOID DrDevice::MarkIdle(SmartPtr<DrExchange> &Exchange)
  1084. /*++
  1085. Routine Description:
  1086. Marks an Exchange context as idle. If it was cancelled
  1087. while we're copying in to its buffer, do the cancel now
  1088. Arguments:
  1089. The busy exchange
  1090. Return Value:
  1091. None
  1092. --*/
  1093. {
  1094. PRX_CONTEXT RxContext = NULL;
  1095. DrIoContext *Context = NULL;
  1096. BEGIN_FN("DrDevice::MarkIdle");
  1097. ASSERT(Exchange != NULL);
  1098. DrAcquireMutex();
  1099. Context = (DrIoContext *)Exchange->_Context;
  1100. TRC_ASSERT(Context != NULL, (TB, "Not allowed to delete context while "
  1101. "it is busy"));
  1102. ASSERT(Context->_Busy);
  1103. Context->_Busy = FALSE;
  1104. if (Context->_Cancelled && Context->_RxContext != NULL) {
  1105. TRC_DBG((TB, "Context was cancelled while busy, "
  1106. "completing"));
  1107. //
  1108. // If we were cancelled while busy, we do the work now,
  1109. // swap out the RxContext safely while in the Mutex and
  1110. // actually cancel it right after. Also set the state to
  1111. // indicate the cancelling work has been done
  1112. //
  1113. RxContext = Context->_RxContext;
  1114. TRC_ASSERT(RxContext != NULL, (TB, "Cancelled RxContext was NULL "
  1115. "going from busy to Idle"));
  1116. Context->_RxContext = NULL;
  1117. RxContext->MRxContext[MRX_DR_CONTEXT] = NULL;
  1118. }
  1119. if (Context->_Disconnected) {
  1120. //
  1121. // If the connection dropped while busy, clear that out
  1122. // in the Mutex for safety, and then delete it outside
  1123. //
  1124. Exchange->_Context = NULL;
  1125. }
  1126. DrReleaseMutex();
  1127. if (RxContext != NULL) {
  1128. //
  1129. // We only remove the RxContext because marking Idle means
  1130. // we expect to come back and look for it again later after we
  1131. // receive more data
  1132. //
  1133. CompleteRxContext(RxContext, STATUS_CANCELLED, 0);
  1134. if (Context->_Disconnected) {
  1135. //
  1136. // We got disconnected while busy, and will get no further
  1137. // notifications from the Exachnge manager. The Context must
  1138. // be deleted now
  1139. //
  1140. delete Context;
  1141. }
  1142. }
  1143. }
  1144. BOOL DrDevice::MarkTimedOut(SmartPtr<DrExchange> &Exchange)
  1145. /*++
  1146. Routine Description:
  1147. Marks an Exchange context as timed out so it won't be processd
  1148. when the client later returns.
  1149. Arguments:
  1150. Exchange - Context
  1151. Return Value:
  1152. TRUE - if Marked TimedOut
  1153. FALSE - if Context was gone
  1154. --*/
  1155. {
  1156. NTSTATUS Status;
  1157. BOOL rc;
  1158. DrIoContext *Context = NULL;
  1159. PRX_CONTEXT RxContext = NULL;
  1160. BEGIN_FN("DrDevice::MarkTimedOut");
  1161. ASSERT(Exchange != NULL);
  1162. DrAcquireMutex();
  1163. Context = (DrIoContext *)Exchange->_Context;
  1164. if (Context != NULL) {
  1165. ASSERT(!Context->_TimedOut);
  1166. Context->_TimedOut = TRUE;
  1167. if (Context->_RxContext != NULL) {
  1168. RxContext = Context->_RxContext;
  1169. Context->_RxContext = NULL;
  1170. RxContext->MRxContext[MRX_DR_CONTEXT] = NULL;
  1171. rc = TRUE;
  1172. }
  1173. else {
  1174. rc = FALSE;
  1175. }
  1176. } else {
  1177. rc = FALSE;
  1178. }
  1179. DrReleaseMutex();
  1180. return rc;
  1181. }
  1182. VOID DrDevice::CompleteRxContext(PRX_CONTEXT RxContext, NTSTATUS Status,
  1183. ULONG Information)
  1184. /*++
  1185. Routine Description:
  1186. Completes the Io from the RDBSS perspective with the supplied information
  1187. Arguments:
  1188. RxContext - IFS kit context
  1189. Status - Completion status
  1190. Information - Completion information
  1191. Return Value:
  1192. None
  1193. --*/
  1194. {
  1195. BEGIN_FN_STATIC("DrDevice::CompleteRxContext");
  1196. ASSERT(RxContext != NULL);
  1197. RxContext->IoStatusBlock.Status = Status;
  1198. RxContext->IoStatusBlock.Information = Information;
  1199. if (((RxContext->LowIoContext.Flags & LOWIO_CONTEXT_FLAG_SYNCCALL) != 0) ||
  1200. (RxContext->MajorFunction == IRP_MJ_CREATE)) {
  1201. TRC_DBG((TB, "Setting event for synchronous Io"));
  1202. KeSetEvent(&RxContext->SyncEvent, 0, FALSE);
  1203. } else {
  1204. TRC_DBG((TB, "Calling RxLowIoCompletion for asynchronous Io"));
  1205. RxLowIoCompletion(RxContext);
  1206. }
  1207. }
  1208. NTSTATUS DrDevice::OnDeviceIoCompletion(
  1209. PRDPDR_IOCOMPLETION_PACKET CompletionPacket, ULONG cbPacket,
  1210. BOOL *DoDefaultRead, SmartPtr<DrExchange> &Exchange)
  1211. /*++
  1212. Routine Description:
  1213. Callback from the Exchange manager to process an Io
  1214. Arguments:
  1215. CompletionPacket - The packet containing the completion
  1216. cbPacket - count of bytes in the packet
  1217. DoDefaultRead - Should be set to TRUE if read isn't explicitly called
  1218. Exchange - Context for the Io
  1219. Return Value:
  1220. NTSTATUS code. An error indicates a protocol error or need to disconnect
  1221. the client
  1222. --*/
  1223. {
  1224. DrIoContext *Context = NULL;
  1225. NTSTATUS Status;
  1226. PRX_CONTEXT RxContext;
  1227. BEGIN_FN("DrDevice::OnDeviceIoCompletion");
  1228. ASSERT(CompletionPacket != NULL);
  1229. ASSERT(DoDefaultRead != NULL);
  1230. if (MarkBusy(Exchange)) {
  1231. Context = (DrIoContext *)Exchange->_Context;
  1232. ASSERT(Context != NULL);
  1233. TRC_NRM((TB, "Client completed %s irp, Completion Status: %lx",
  1234. IrpNames[Context->_MajorFunction],
  1235. CompletionPacket->IoCompletion.IoStatus));
  1236. //
  1237. // If the IRP was timed out, then we just discard this exchange
  1238. //
  1239. if (Context->_TimedOut) {
  1240. TRC_NRM((TB, "Irp was timed out"));
  1241. DiscardBusyExchange(Exchange);
  1242. return STATUS_SUCCESS;
  1243. }
  1244. switch (Context->_MajorFunction) {
  1245. case IRP_MJ_CREATE:
  1246. Status = OnCreateCompletion(CompletionPacket, cbPacket,
  1247. DoDefaultRead, Exchange);
  1248. break;
  1249. case IRP_MJ_WRITE:
  1250. Status = OnWriteCompletion(CompletionPacket, cbPacket,
  1251. DoDefaultRead, Exchange);
  1252. break;
  1253. case IRP_MJ_READ:
  1254. Status = OnReadCompletion(CompletionPacket, cbPacket,
  1255. DoDefaultRead, Exchange);
  1256. break;
  1257. case IRP_MJ_DEVICE_CONTROL:
  1258. case IRP_MJ_FILE_SYSTEM_CONTROL:
  1259. Status = OnDeviceControlCompletion(CompletionPacket, cbPacket,
  1260. DoDefaultRead, Exchange);
  1261. break;
  1262. case IRP_MJ_LOCK_CONTROL:
  1263. Status = OnLocksCompletion(CompletionPacket, cbPacket,
  1264. DoDefaultRead, Exchange);
  1265. break;
  1266. case IRP_MJ_DIRECTORY_CONTROL:
  1267. Status = OnDirectoryControlCompletion(CompletionPacket, cbPacket,
  1268. DoDefaultRead, Exchange);
  1269. break;
  1270. case IRP_MJ_QUERY_VOLUME_INFORMATION:
  1271. Status = OnQueryVolumeInfoCompletion(CompletionPacket, cbPacket,
  1272. DoDefaultRead, Exchange);
  1273. break;
  1274. case IRP_MJ_SET_VOLUME_INFORMATION:
  1275. Status = OnSetVolumeInfoCompletion(CompletionPacket, cbPacket,
  1276. DoDefaultRead, Exchange);
  1277. break;
  1278. case IRP_MJ_QUERY_INFORMATION:
  1279. Status = OnQueryFileInfoCompletion(CompletionPacket, cbPacket,
  1280. DoDefaultRead, Exchange);
  1281. break;
  1282. case IRP_MJ_SET_INFORMATION:
  1283. Status = OnSetFileInfoCompletion(CompletionPacket, cbPacket,
  1284. DoDefaultRead, Exchange);
  1285. break;
  1286. case IRP_MJ_QUERY_SECURITY:
  1287. Status = OnQuerySdInfoCompletion(CompletionPacket, cbPacket,
  1288. DoDefaultRead, Exchange);
  1289. break;
  1290. case IRP_MJ_SET_SECURITY:
  1291. Status = OnSetSdInfoCompletion(CompletionPacket, cbPacket,
  1292. DoDefaultRead, Exchange);
  1293. break;
  1294. case IRP_MJ_CLOSE:
  1295. NotifyClose();
  1296. // no break;
  1297. default:
  1298. RxContext = Context->_RxContext;
  1299. if (RxContext != NULL) {
  1300. TRC_NRM((TB, "Irp: %s, Completion Status: %lx",
  1301. IrpNames[RxContext->MajorFunction],
  1302. RxContext->IoStatusBlock.Status));
  1303. CompleteBusyExchange(Exchange, CompletionPacket->IoCompletion.IoStatus, 0);
  1304. } else {
  1305. TRC_NRM((TB, "Irp was cancelled"));
  1306. DiscardBusyExchange(Exchange);
  1307. }
  1308. Status = STATUS_SUCCESS;
  1309. }
  1310. } else {
  1311. //
  1312. // We could have been disconnected between getting the callback and
  1313. // trying to mark it busy. So the only legitimate way for this to
  1314. // happen is if we were disconnected anyway.
  1315. //
  1316. TRC_ALT((TB, "Found no context in Io notification"));
  1317. *DoDefaultRead = FALSE;
  1318. Status = STATUS_UNSUCCESSFUL;
  1319. }
  1320. return Status;
  1321. }
  1322. NTSTATUS DrDevice::OnCreateCompletion(PRDPDR_IOCOMPLETION_PACKET CompletionPacket, ULONG cbPacket,
  1323. BOOL *DoDefaultRead, SmartPtr<DrExchange> Exchange)
  1324. {
  1325. DrIoContext *Context = (DrIoContext *)Exchange->_Context;
  1326. PRX_CONTEXT RxContext;
  1327. SmartPtr<DrDevice> Device;
  1328. SmartPtr<DrFile> FileObj;
  1329. BEGIN_FN("DrDevice::OnCreateCompletion");
  1330. RxContext = Context->_RxContext;
  1331. if (cbPacket < (ULONG)FIELD_OFFSET(RDPDR_IOCOMPLETION_PACKET,
  1332. IoCompletion.Parameters.Create.Information)) {
  1333. //
  1334. // Bad packet. Bad. We've already claimed the RxContext in the
  1335. // atlas. Complete it as unsuccessful. Then shutdown the channel
  1336. // as this is a Bad Client.
  1337. //
  1338. TRC_ERR((TB, "Detected bad client CreateCompletion packet"));
  1339. if (RxContext != NULL) {
  1340. CompleteBusyExchange(Exchange, STATUS_DEVICE_PROTOCOL_ERROR, 0);
  1341. } else {
  1342. DiscardBusyExchange(Exchange);
  1343. }
  1344. //
  1345. // No point in starting a default read or anything, what with the
  1346. // channel being shut down and all.
  1347. //
  1348. *DoDefaultRead = FALSE;
  1349. return STATUS_DEVICE_PROTOCOL_ERROR;
  1350. }
  1351. if (RxContext != NULL) {
  1352. DrAcquireSpinLock();
  1353. Device = (DrDevice *)RxContext->Create.pVNetRoot->Context;
  1354. DrReleaseSpinLock();
  1355. ASSERT(Device != NULL);
  1356. //
  1357. // We are using a file object to keep track of file open instance
  1358. // and any information stored in the mini-redir for this instance
  1359. //
  1360. FileObj = new(NonPagedPool) DrFile(Device,
  1361. CompletionPacket->IoCompletion.Parameters.Create.FileId);
  1362. if (FileObj) {
  1363. //
  1364. // Explicit reference the file object here
  1365. //
  1366. FileObj->AddRef();
  1367. RxContext->pFobx->Context2 = (VOID *)(FileObj);
  1368. TRC_NRM((TB, "CreateCompletion: status =%d, information=%d",
  1369. CompletionPacket->IoCompletion.IoStatus,
  1370. CompletionPacket->IoCompletion.Parameters.Create.Information));
  1371. if (cbPacket >= sizeof(RDPDR_IOCOMPLETION_PACKET)) {
  1372. RxContext->Create.ReturnedCreateInformation =
  1373. CompletionPacket->IoCompletion.Parameters.Create.Information;
  1374. CompleteBusyExchange(Exchange, CompletionPacket->IoCompletion.IoStatus,
  1375. CompletionPacket->IoCompletion.Parameters.Create.Information);
  1376. }
  1377. else {
  1378. // For printer creat completion packet, the cbPacket is less than
  1379. // sizeof(RDPDR_IOCOMPLETION_PACKET). We don't want to access information beyond its length
  1380. RxContext->Create.ReturnedCreateInformation = 0;
  1381. CompleteBusyExchange(Exchange, CompletionPacket->IoCompletion.IoStatus, 0);
  1382. }
  1383. }
  1384. else {
  1385. CompleteBusyExchange(Exchange, STATUS_INSUFFICIENT_RESOURCES, 0);
  1386. return STATUS_INSUFFICIENT_RESOURCES;
  1387. }
  1388. } else {
  1389. //
  1390. // Was cancelled but Context wasn't cleaned up
  1391. //
  1392. DiscardBusyExchange(Exchange);
  1393. }
  1394. return STATUS_SUCCESS;
  1395. }
  1396. NTSTATUS DrDevice::OnWriteCompletion(PRDPDR_IOCOMPLETION_PACKET CompletionPacket, ULONG cbPacket,
  1397. BOOL *DoDefaultRead, SmartPtr<DrExchange> Exchange)
  1398. {
  1399. PRX_CONTEXT RxContext;
  1400. DrIoContext *Context = (DrIoContext *)Exchange->_Context;
  1401. BEGIN_FN("DrDevice::OnWriteCompletion");
  1402. RxContext = Context->_RxContext;
  1403. if (cbPacket < sizeof(RDPDR_IOCOMPLETION_PACKET)) {
  1404. //
  1405. // Bad packet. Bad. We've already claimed the RxContext in the
  1406. // atlas. Complete it as unsuccessful. Then shutdown the channel
  1407. // as this is a Bad Client.
  1408. //
  1409. TRC_ERR((TB, "Detected bad client WriteCompletion packet"));
  1410. if (RxContext != NULL) {
  1411. CompleteBusyExchange(Exchange, STATUS_DEVICE_PROTOCOL_ERROR, 0);
  1412. } else {
  1413. DiscardBusyExchange(Exchange);
  1414. }
  1415. //
  1416. // No point in starting a default read or anything, what with the
  1417. // channel being shut down and all.
  1418. //
  1419. *DoDefaultRead = FALSE;
  1420. return STATUS_DEVICE_PROTOCOL_ERROR;
  1421. }
  1422. if (RxContext != NULL) {
  1423. ASSERT(RxContext->MajorFunction == IRP_MJ_WRITE);
  1424. TRC_NRM((TB, "Irp: %s, Completion Status: %lx",
  1425. IrpNames[RxContext->MajorFunction],
  1426. RxContext->IoStatusBlock.Status));
  1427. CompleteBusyExchange(Exchange, CompletionPacket->IoCompletion.IoStatus,
  1428. CompletionPacket->IoCompletion.Parameters.Write.Length);
  1429. } else {
  1430. //
  1431. // Was cancelled but Context wasn't cleaned up
  1432. //
  1433. DiscardBusyExchange(Exchange);
  1434. }
  1435. return STATUS_SUCCESS;
  1436. }
  1437. NTSTATUS DrDevice::OnReadCompletion(PRDPDR_IOCOMPLETION_PACKET CompletionPacket, ULONG cbPacket,
  1438. BOOL *DoDefaultRead, SmartPtr<DrExchange> Exchange)
  1439. {
  1440. PRX_CONTEXT RxContext;
  1441. PVOID pData = CompletionPacket->IoCompletion.Parameters.Read.Buffer;
  1442. ULONG cbWantData; // Amount of actual Read data in this packet
  1443. ULONG cbHaveData; // Amount of data available so far
  1444. DrIoContext *Context = (DrIoContext *)Exchange->_Context;
  1445. NTSTATUS Status;
  1446. PVOID pv;
  1447. BEGIN_FN("DrDevice::OnReadCompletion");
  1448. //
  1449. // Even if the IO was cancelled we need to correctly parse
  1450. // this data.
  1451. //
  1452. // Check to make sure this is up to size before accessing
  1453. // further portions of the packet
  1454. //
  1455. RxContext = Context->_RxContext;
  1456. if (cbPacket < (ULONG)FIELD_OFFSET(RDPDR_IOCOMPLETION_PACKET,
  1457. IoCompletion.Parameters.Read.Buffer)) {
  1458. //
  1459. // Bad packet. Bad. We've already claimed the RxContext in the
  1460. // atlas. Complete it as unsuccessful. Then shutdown the channel
  1461. // as this is a Bad Client.
  1462. //
  1463. TRC_ERR((TB, "Detected bad client read packet"));
  1464. if (RxContext != NULL) {
  1465. CompleteBusyExchange(Exchange, STATUS_DEVICE_PROTOCOL_ERROR, 0);
  1466. } else {
  1467. DiscardBusyExchange(Exchange);
  1468. }
  1469. //
  1470. // No point in starting a default read or anything, what with the
  1471. // channel being shut down and all.
  1472. //
  1473. *DoDefaultRead = FALSE;
  1474. return STATUS_DEVICE_PROTOCOL_ERROR;
  1475. }
  1476. //
  1477. // Calculate how much data is available immediately and how much data
  1478. // is coming
  1479. //
  1480. if (NT_SUCCESS(CompletionPacket->IoCompletion.IoStatus)) {
  1481. //
  1482. // Successful IO at the client end
  1483. //
  1484. TRC_DBG((TB, "Successful Read at the client end"));
  1485. TRC_DBG((TB, "Read Length: 0x%d, DataCopied 0x%d",
  1486. CompletionPacket->IoCompletion.Parameters.Read.Length,
  1487. Context->_DataCopied));
  1488. cbWantData = CompletionPacket->IoCompletion.Parameters.Read.Length -
  1489. Context->_DataCopied;
  1490. cbHaveData = cbPacket - (ULONG)FIELD_OFFSET(RDPDR_IOCOMPLETION_PACKET,
  1491. IoCompletion.Parameters.Read.Buffer);
  1492. if (cbHaveData > cbWantData) {
  1493. //
  1494. // Sounds like a bad client to me
  1495. //
  1496. TRC_ERR((TB, "Read returned more data than "
  1497. "advertised cbHaveData 0x%d cbWantData 0x%d",
  1498. cbHaveData, cbWantData));
  1499. if (RxContext != NULL) {
  1500. CompleteBusyExchange(Exchange, STATUS_DEVICE_PROTOCOL_ERROR, 0);
  1501. } else {
  1502. DiscardBusyExchange(Exchange);
  1503. }
  1504. *DoDefaultRead = FALSE;
  1505. return STATUS_DEVICE_PROTOCOL_ERROR;
  1506. }
  1507. if (RxContext != NULL) { // And not drexchCancelled
  1508. TRC_DBG((TB, "Copying data for Read"));
  1509. ASSERT(RxContext != NULL);
  1510. if (cbWantData > RxContext->LowIoContext.ParamsFor.ReadWrite.ByteCount) {
  1511. TRC_ERR((TB, "Read returned more data than "
  1512. "requested"));
  1513. CompleteBusyExchange(Exchange, STATUS_DEVICE_PROTOCOL_ERROR, 0);
  1514. *DoDefaultRead = FALSE;
  1515. return STATUS_DEVICE_PROTOCOL_ERROR;
  1516. }
  1517. //
  1518. // Copy the actual size of the read, and check to see if we have all
  1519. // the data. The information field tells us what to expect.
  1520. //
  1521. RxContext->IoStatusBlock.Information =
  1522. CompletionPacket->IoCompletion.Parameters.Read.Length;
  1523. if (RxContext->IoStatusBlock.Information && cbHaveData) {
  1524. pv = MmGetSystemAddressForMdl(RxContext->LowIoContext.ParamsFor.ReadWrite.Buffer);
  1525. RtlCopyMemory(((BYTE *)pv) + Context->_DataCopied, pData, cbHaveData);
  1526. //
  1527. // Keep track of how much data we've copied in case this is a
  1528. // multi chunk completion
  1529. //
  1530. Context->_DataCopied += cbHaveData;
  1531. }
  1532. }
  1533. if (cbHaveData == cbWantData) {
  1534. //
  1535. // There is exactly as much data as we need to satisfy the read,
  1536. // I like it.
  1537. //
  1538. if (RxContext != NULL) {
  1539. CompleteBusyExchange(Exchange,
  1540. CompletionPacket->IoCompletion.IoStatus,
  1541. CompletionPacket->IoCompletion.Parameters.Read.Length);
  1542. } else {
  1543. DiscardBusyExchange(Exchange);
  1544. }
  1545. //
  1546. // Go with a default channel read now
  1547. //
  1548. *DoDefaultRead = TRUE;
  1549. return STATUS_SUCCESS;
  1550. } else {
  1551. //
  1552. // We don't have all the data yet, release the DrExchange and
  1553. // read more data
  1554. //
  1555. MarkIdle(Exchange);
  1556. _Session->GetExchangeManager().ReadMore(
  1557. (ULONG)FIELD_OFFSET(RDPDR_IOCOMPLETION_PACKET,
  1558. IoCompletion.Parameters.Read.Buffer));
  1559. *DoDefaultRead = FALSE;
  1560. return STATUS_SUCCESS;
  1561. }
  1562. } else {
  1563. //
  1564. // Unsuccessful IO at the client end
  1565. //
  1566. TRC_DBG((TB, "Unsuccessful Read at the client end"));
  1567. if (cbPacket >= FIELD_OFFSET(RDPDR_IOCOMPLETION_PACKET,
  1568. IoCompletion.Parameters.Read.Buffer)) {
  1569. if (RxContext != NULL) {
  1570. CompleteBusyExchange(Exchange,
  1571. CompletionPacket->IoCompletion.IoStatus,
  1572. 0);
  1573. }
  1574. else {
  1575. DiscardBusyExchange(Exchange);
  1576. }
  1577. *DoDefaultRead = TRUE;
  1578. return STATUS_SUCCESS;
  1579. } else {
  1580. TRC_ERR((TB, "Read returned invalid data"));
  1581. if (RxContext != NULL) {
  1582. CompleteBusyExchange(Exchange, STATUS_DEVICE_PROTOCOL_ERROR, 0);
  1583. } else {
  1584. DiscardBusyExchange(Exchange);
  1585. }
  1586. *DoDefaultRead = FALSE;
  1587. return STATUS_DEVICE_PROTOCOL_ERROR;
  1588. }
  1589. }
  1590. }
  1591. NTSTATUS DrDevice::OnDeviceControlCompletion(PRDPDR_IOCOMPLETION_PACKET CompletionPacket, ULONG cbPacket,
  1592. BOOL *DoDefaultRead, SmartPtr<DrExchange> Exchange)
  1593. {
  1594. PRX_CONTEXT RxContext;
  1595. DrIoContext *Context = (DrIoContext *)Exchange->_Context;
  1596. PVOID pData = CompletionPacket->IoCompletion.Parameters.DeviceIoControl.OutputBuffer;
  1597. ULONG cbWantData; // Amount of actual Read data in this packet
  1598. ULONG cbHaveData; // Amount of data available so far
  1599. NTSTATUS Status;
  1600. PVOID pv;
  1601. BEGIN_FN("DrDevice::OnDeviceControlCompletion");
  1602. //
  1603. // Even if the IO was cancelled we need to correctly parse
  1604. // this data.
  1605. //
  1606. // Check to make sure this is up to size before accessing
  1607. // further portions of the packet
  1608. //
  1609. RxContext = Context->_RxContext;
  1610. if (cbPacket < (ULONG)FIELD_OFFSET(RDPDR_IOCOMPLETION_PACKET,
  1611. IoCompletion.Parameters.DeviceIoControl.OutputBuffer)) {
  1612. //
  1613. // Bad packet. Bad. We've already claimed the RxContext in the
  1614. // atlas. Complete it as unsuccessful. Then shutdown the channel
  1615. // as this is a Bad Client.
  1616. //
  1617. TRC_ERR((TB, "Detected bad client DeviceControl packet"));
  1618. if (RxContext != NULL) {
  1619. CompleteBusyExchange(Exchange, STATUS_DEVICE_PROTOCOL_ERROR, 0);
  1620. } else {
  1621. DiscardBusyExchange(Exchange);
  1622. }
  1623. //
  1624. // No point in starting a default read or anything, what with the
  1625. // channel being shut down and all.
  1626. //
  1627. *DoDefaultRead = FALSE;
  1628. return STATUS_DEVICE_PROTOCOL_ERROR;
  1629. }
  1630. //
  1631. // Calculate how much data is available immediately and how much data
  1632. // is coming
  1633. //
  1634. if (NT_SUCCESS(CompletionPacket->IoCompletion.IoStatus)) {
  1635. //
  1636. // Successful IO at the client end
  1637. //
  1638. TRC_DBG((TB, "Successful DeviceControl at the client end"));
  1639. cbWantData = CompletionPacket->IoCompletion.Parameters.DeviceIoControl.OutputBufferLength -
  1640. Context->_DataCopied;
  1641. cbHaveData = cbPacket - (ULONG)FIELD_OFFSET(RDPDR_IOCOMPLETION_PACKET,
  1642. IoCompletion.Parameters.DeviceIoControl.OutputBuffer);
  1643. if (cbHaveData > cbWantData) {
  1644. //
  1645. // Sounds like a bad client to me
  1646. //
  1647. TRC_ERR((TB, "DeviceControl returned more data than "
  1648. "advertised, cbHaveData: %ld cbWantData: %ld", cbHaveData,
  1649. cbWantData));
  1650. if (RxContext != NULL) {
  1651. CompleteBusyExchange(Exchange, STATUS_DEVICE_PROTOCOL_ERROR, 0);
  1652. } else {
  1653. DiscardBusyExchange(Exchange);
  1654. }
  1655. *DoDefaultRead = FALSE;
  1656. return STATUS_DEVICE_PROTOCOL_ERROR;
  1657. }
  1658. if (RxContext != NULL) { // And not drexchCancelled
  1659. TRC_DBG((TB, "Copying data for DeviceControl"));
  1660. ASSERT(RxContext != NULL);
  1661. if (cbWantData > RxContext->LowIoContext.ParamsFor.IoCtl.OutputBufferLength) {
  1662. TRC_ERR((TB, "DeviceControl returned more data than "
  1663. "requested"));
  1664. CompleteBusyExchange(Exchange, STATUS_DEVICE_PROTOCOL_ERROR, 0);
  1665. *DoDefaultRead = FALSE;
  1666. return STATUS_DEVICE_PROTOCOL_ERROR;
  1667. }
  1668. //
  1669. // Copy the actual size of the read, and check to see if we have all
  1670. // the data. The information field tells us what to expect.
  1671. //
  1672. RxContext->IoStatusBlock.Information =
  1673. CompletionPacket->IoCompletion.Parameters.DeviceIoControl.OutputBufferLength;
  1674. __try {
  1675. if (RxContext->IoStatusBlock.Information && cbHaveData) {
  1676. RtlCopyMemory(((BYTE *)RxContext->LowIoContext.ParamsFor.IoCtl.pOutputBuffer) +
  1677. Context->_DataCopied, pData, cbHaveData);
  1678. //
  1679. // Keep track of how much data we've copied in case this is a
  1680. // multi chunk completion
  1681. //
  1682. Context->_DataCopied += cbHaveData;
  1683. }
  1684. }
  1685. __except (EXCEPTION_EXECUTE_HANDLER) {
  1686. TRC_ERR((TB, "Invalid buffer parameter(s)"));
  1687. CompleteBusyExchange(Exchange, STATUS_INVALID_PARAMETER, 0);
  1688. *DoDefaultRead = FALSE;
  1689. // This is the status returned back to HandlePacket, not the status
  1690. // returned back to the caller of IoControl.
  1691. return STATUS_SUCCESS;
  1692. }
  1693. }
  1694. if (cbHaveData == cbWantData) {
  1695. //
  1696. // There is exactly as much data as we need to satisfy the io,
  1697. // I like it.
  1698. //
  1699. TRC_NRM((TB, "DeviceControl, read %d bytes", Context->_DataCopied));
  1700. if (RxContext != NULL) {
  1701. CompleteBusyExchange(Exchange,
  1702. CompletionPacket->IoCompletion.IoStatus,
  1703. CompletionPacket->IoCompletion.Parameters.DeviceIoControl.OutputBufferLength);
  1704. } else {
  1705. DiscardBusyExchange(Exchange);
  1706. }
  1707. //
  1708. // Go with a default channel read now
  1709. //
  1710. *DoDefaultRead = TRUE;
  1711. return STATUS_SUCCESS;
  1712. } else {
  1713. //
  1714. // We don't have all the data yet, release the DrExchange and
  1715. // read more data
  1716. //
  1717. MarkIdle(Exchange);
  1718. _Session->GetExchangeManager().ReadMore(
  1719. (ULONG)FIELD_OFFSET(RDPDR_IOCOMPLETION_PACKET,
  1720. IoCompletion.Parameters.DeviceIoControl.OutputBuffer));
  1721. *DoDefaultRead = FALSE;
  1722. return STATUS_SUCCESS;
  1723. }
  1724. } else {
  1725. //
  1726. // Unsuccessful IO at the client end
  1727. //
  1728. TRC_DBG((TB, "Unsuccessful DeviceControl at the client end"));
  1729. if (cbPacket >= FIELD_OFFSET(RDPDR_IOCOMPLETION_PACKET,
  1730. IoCompletion.Parameters.DeviceIoControl.OutputBuffer)) {
  1731. if (RxContext != NULL) {
  1732. CompleteBusyExchange(Exchange,
  1733. CompletionPacket->IoCompletion.IoStatus,
  1734. 0);
  1735. }
  1736. else {
  1737. DiscardBusyExchange(Exchange);
  1738. }
  1739. *DoDefaultRead = TRUE;
  1740. return STATUS_SUCCESS;
  1741. } else {
  1742. TRC_ERR((TB, "DeviceControl returned invalid data "));
  1743. if (RxContext != NULL) {
  1744. CompleteBusyExchange(Exchange, STATUS_DEVICE_PROTOCOL_ERROR, 0);
  1745. } else {
  1746. DiscardBusyExchange(Exchange);
  1747. }
  1748. *DoDefaultRead = FALSE;
  1749. return STATUS_DEVICE_PROTOCOL_ERROR;
  1750. }
  1751. }
  1752. }
  1753. NTSTATUS NTAPI DrDevice::MinirdrCancelRoutine(PRX_CONTEXT RxContext)
  1754. {
  1755. SmartPtr<DrExchange> Exchange;
  1756. DrIoContext *Context;
  1757. BOOL bFound = FALSE;
  1758. BEGIN_FN_STATIC("DrDevice::MinirdrCancelRoutine");
  1759. DrAcquireMutex();
  1760. Exchange = (DrExchange *)RxContext->MRxContext[MRX_DR_CONTEXT];
  1761. if (Exchange == NULL) {
  1762. DrReleaseMutex();
  1763. return STATUS_SUCCESS;
  1764. }
  1765. ASSERT(Exchange->IsValid());
  1766. Context = (DrIoContext *)Exchange->_Context;
  1767. if (Context != NULL) {
  1768. TRC_DBG((TB, "Marking Exchange cancelled"));
  1769. //
  1770. // Mark it as cancelled, if it is busy, it will be cancelled
  1771. // when it goes back to idle
  1772. //
  1773. Context->_Cancelled = TRUE;
  1774. if (!Context->_Busy) {
  1775. ASSERT(Context->_RxContext == RxContext);
  1776. //
  1777. // Wasn't busy, cancelling work should be done here
  1778. //
  1779. Context->_RxContext = NULL;
  1780. TRC_DBG((TB, "Found context to cancel"));
  1781. bFound = TRUE;
  1782. } else {
  1783. TRC_DBG((TB, "DrExchange was busy or RxContext "
  1784. "not found"));
  1785. }
  1786. } else {
  1787. //
  1788. // This could happened if we destroyed the atlas
  1789. //
  1790. TRC_NRM((TB, "DrExchange was already cancelled"));
  1791. }
  1792. DrReleaseMutex();
  1793. if (bFound) {
  1794. //
  1795. // Do the cancelling outside the mutex
  1796. //
  1797. CompleteRxContext(RxContext, STATUS_CANCELLED, 0);
  1798. }
  1799. return STATUS_SUCCESS;
  1800. }
  1801. VOID DrDevice::OnIoDisconnected(SmartPtr<DrExchange> &Exchange)
  1802. {
  1803. DrIoContext *Context, *DeleteContext = NULL;
  1804. PRX_CONTEXT RxContext = NULL;
  1805. BOOL bFound = FALSE;
  1806. BEGIN_FN("DrDevice::OnIoDisconnected");
  1807. DrAcquireMutex();
  1808. ASSERT(Exchange->IsValid());
  1809. Context = (DrIoContext *)Exchange->_Context;
  1810. if (Context != NULL) {
  1811. TRC_DBG((TB, "Marking Exchange cancelled"));
  1812. //
  1813. // Mark it as cancelled, if it is busy, it will be cancelled
  1814. // when it goes back to idle
  1815. //
  1816. // Also mark it disconnected, so we know to completely clean
  1817. // up the Context
  1818. //
  1819. Context->_Cancelled = TRUE;
  1820. Context->_Disconnected = TRUE;
  1821. if (!Context->_Busy) {
  1822. RxContext = Context->_RxContext;
  1823. Exchange->_Context = NULL;
  1824. // Need to delete the context when the exchange is already cancelled or
  1825. // about to be cancelled. Also deletion needs to happen outside the mutex
  1826. DeleteContext = Context;
  1827. //
  1828. // Wasn't busy, cancelling work should be done here
  1829. //
  1830. if (RxContext) {
  1831. RxContext->MRxContext[MRX_DR_CONTEXT] = NULL;
  1832. TRC_DBG((TB, "Found context to cancel"));
  1833. bFound = TRUE;
  1834. } else {
  1835. TRC_DBG((TB, "RxContext was already cancelled "));
  1836. }
  1837. } else {
  1838. TRC_DBG((TB, "DrExchange was busy or RxContext "
  1839. "not found"));
  1840. }
  1841. } else {
  1842. //
  1843. // This could happened if we destroyed the atlas right after
  1844. // the IO was completed, but before we discarded it
  1845. //
  1846. TRC_NRM((TB, "DrExchange was already cancelled"));
  1847. }
  1848. DrReleaseMutex();
  1849. if (bFound) {
  1850. //
  1851. // Do the cancelling outside the mutex
  1852. //
  1853. CompleteRxContext(RxContext, STATUS_CANCELLED, 0);
  1854. }
  1855. if (DeleteContext != NULL) {
  1856. delete DeleteContext;
  1857. }
  1858. }
  1859. NTSTATUS DrDevice::OnStartExchangeCompletion(SmartPtr<DrExchange> &Exchange,
  1860. PIO_STATUS_BLOCK IoStatusBlock)
  1861. {
  1862. BEGIN_FN("DrDevice::OnStartExchangeCompletion");
  1863. //
  1864. // if an error is returned, the connection should be dropped, and that
  1865. // is correct when an error comes in
  1866. //
  1867. return IoStatusBlock->Status;
  1868. }
  1869. VOID DrDevice::Remove()
  1870. {
  1871. BEGIN_FN("DrDevice::Remove");
  1872. _DeviceStatus = dsDisabled;
  1873. }
  1874. DrIoContext::DrIoContext(PRX_CONTEXT RxContext, SmartPtr<DrDevice> &Device)
  1875. {
  1876. BEGIN_FN("DrIoContext::DrIoContext");
  1877. SetClassName("DrIoContext");
  1878. _Device = Device;
  1879. _MajorFunction = RxContext->MajorFunction;
  1880. _MinorFunction = RxContext->MinorFunction;
  1881. _Busy = FALSE;
  1882. _Cancelled = FALSE;
  1883. _Disconnected = FALSE;
  1884. _TimedOut = FALSE;
  1885. _DataCopied = 0;
  1886. _RxContext = RxContext;
  1887. }
  1888. VOID DrDevice::NotifyClose()
  1889. {
  1890. BEGIN_FN("DrDevice::NotifyClose");
  1891. // This was added for ports, which need to track exclusivity
  1892. }