Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

816 lines
20 KiB

  1. /*++
  2. Copyright (c) 1991 Microsoft Corporation
  3. Module Name:
  4. sh_ioctl.c
  5. Abstract:
  6. This source deals with those streamio(2) functions that are synchronous,
  7. in that a message is sent downstream, and a reply is waited for.
  8. It is based on the SpiderSTREAMS source, stremul\msgsrvr.c.
  9. Author:
  10. Eric Chin (ericc) January 6, 1992
  11. Revision History:
  12. Notes:
  13. 1. The O_NONBLOCK state of a stream does not affect the behaviour of an
  14. ioctl(I_STR).
  15. 2. The write error state of a stream is represented by ms->e_werror. Once
  16. set, this is never reset. This corresponds to the STREAMS semantics as
  17. defined by AT&T. Once a user is notified of a write error on a stream,
  18. about the only recourse is to close the stream.
  19. --*/
  20. #include "shead.h"
  21. #include "sh_proto.h"
  22. //
  23. // Private Functions
  24. //
  25. STATIC VOID
  26. cancel_ioctl(
  27. IN PDEVICE_OBJECT device,
  28. IN PIRP irp
  29. );
  30. STATIC NTSTATUS
  31. do_sioctl(
  32. IN PIRP irp,
  33. IN BOOLEAN from_queue,
  34. IN int *spl_levelp,
  35. OUT BOOLEAN *ignored OPTIONAL
  36. );
  37. STATIC VOID
  38. handle_ioctlers (
  39. IN STREAM_ENDPOINT *ms,
  40. IN int *spl_levelp
  41. );
  42. STATIC int
  43. ioc_timeout(
  44. IN char *arg
  45. );
  46. NTSTATUS
  47. SHDispIStr(
  48. IN PIRP irp
  49. )
  50. /*++
  51. Routine Description:
  52. This routine is called to process an ioctl(I_STR). It is based on the
  53. SpiderSTREAMS emulator's routine, msgserver().
  54. This routine merely peels open the IRP, checks the arguments for
  55. consistency, locks the appropriate stream, and then calls do_sioctl(),
  56. which does the bulk of the work.
  57. Arguments:
  58. irp - pointer to the IRP representing this request
  59. Return Value:
  60. an NT status code.
  61. --*/
  62. {
  63. int timout;
  64. int spl_level;
  65. int spl_level2;
  66. NTSTATUS status;
  67. PSTREAM_ENDPOINT ms;
  68. PISTR_ARGS_INOUT inbuf;
  69. struct strioctl *striop;
  70. PIO_STACK_LOCATION irpsp;
  71. irpsp = IoGetCurrentIrpStackLocation(irp);
  72. ASSERT((irpsp->Parameters.DeviceIoControl.IoControlCode & 0x3) ==
  73. METHOD_BUFFERED);
  74. ms = (STREAM_ENDPOINT *) irpsp->FileObject->FsContext;
  75. if (irpsp->Parameters.DeviceIoControl.InputBufferLength <
  76. sizeof(ISTR_ARGS_INOUT) - 1) {
  77. IF_STRMDBG(TERSE) {
  78. STRMTRACE(("SHEAD: SHDispIStr(%lx) insufficient nbytes = %lx\n",
  79. irp, irpsp->Parameters.DeviceIoControl.InputBufferLength));
  80. }
  81. return(STATUS_INVALID_PARAMETER);
  82. }
  83. //
  84. // the caller marshalled the input arguments contiguously thus:
  85. //
  86. // typedef struct _ISTR_ARGS_INOUT { // ioctl(,I_STR,)
  87. // int a_iocode; // I_STR
  88. // struct strioctl a_strio; // (required)
  89. // int a_unused[2]; // (required)
  90. // char a_stuff[1]; // ic_dp buffer (optional)
  91. //
  92. // } ISTR_ARGS_INOUT, PISTR_ARGS_INOUT;
  93. //
  94. //
  95. //
  96. inbuf = (PISTR_ARGS_INOUT) irp->AssociatedIrp.SystemBuffer;
  97. striop = &(inbuf->a_strio);
  98. IF_STRMDBG(VERBOSE) {
  99. STRMTRACE(("SHEAD: SHDispIStr(ic_cmd, timout, len = %lx, %lx, %lx)\n",
  100. striop->ic_cmd, striop->ic_timout, striop->ic_len));
  101. }
  102. //
  103. // don't let user-defined ioctl codes coincide with the standard STREAMS
  104. // ioctl codes. Otherwise, confusion will reign.
  105. //
  106. switch (striop->ic_cmd) {
  107. case I_LINK:
  108. case I_UNLINK:
  109. case I_PLINK:
  110. case I_PUNLINK:
  111. SHpGenReply(irp, -1, EINVAL);
  112. return(STATUS_SUCCESS);
  113. break;
  114. }
  115. if ((striop->ic_timout < -1) || (striop->ic_len < 0)) {
  116. SHpGenReply(irp, -1, EINVAL);
  117. return(STATUS_SUCCESS);
  118. }
  119. IoAcquireCancelSpinLock(&irp->CancelIrql);
  120. if (irp->Cancel) {
  121. IoReleaseCancelSpinLock(irp->CancelIrql);
  122. shortreply(irp, STATUS_CANCELLED, 0);
  123. return(STATUS_CANCELLED);
  124. }
  125. spl_level = lock_strm(ms->e_strm);
  126. if (shrange(ms->e_strm, sizeof(struct iocblk), striop->ic_len) < 0) {
  127. unlock_strm(ms->e_strm, spl_level);
  128. IoReleaseCancelSpinLock(irp->CancelIrql);
  129. SHpGenReply(irp, -1, ERANGE);
  130. return(STATUS_SUCCESS);
  131. }
  132. IoMarkIrpPending(irp);
  133. //
  134. // if the ioctl is to time out after a specific time, start a timer
  135. // running. iocrdy() will clear the timer.
  136. //
  137. // Based on a tip from larryo, irp->IoStatus.Information is used only
  138. // when an IRP is completed successfully. Hence, we keep the timer
  139. // id there.
  140. //
  141. irp->IoStatus.Information = 0;
  142. irp->IoStatus.Status = STATUS_PENDING;
  143. if (striop->ic_timout != -1) {
  144. timout = striop->ic_timout ? striop->ic_timout : STRTIMOUT;
  145. irp->IoStatus.Information = timeout(ioc_timeout,
  146. (char *) irp, timout * HZ);
  147. }
  148. //
  149. // At any given time, there must only be one outstanding ioctl(I_STR) on
  150. // a stream. If there is another ioctl outstanding, chain this IRP to
  151. // the tail of the pending ioctl'ers list.
  152. //
  153. if (ms->e_active_ioctl) {
  154. status = SHAddPendingIrp(&(ms->e_ioctlers), FALSE, irp, do_sioctl);
  155. unlock_strm(ms->e_strm, spl_level);
  156. if (status != STATUS_SUCCESS) {
  157. IoReleaseCancelSpinLock(irp->CancelIrql);
  158. shortreply(irp, status, 0);
  159. return(status);
  160. }
  161. IoSetCancelRoutine(irp, cancel_ioctl);
  162. IoReleaseCancelSpinLock(irp->CancelIrql);
  163. return(STATUS_PENDING);
  164. }
  165. ms->e_active_ioctl = irp;
  166. //
  167. // do_sioctl() calls unlock_strm(ms->e_strm).
  168. //
  169. IoReleaseCancelSpinLock((KIRQL) spl_level);
  170. spl_level2 = (int) irp->CancelIrql;
  171. if (do_sioctl(irp, FALSE, &spl_level2, NULL) != STATUS_PENDING) {
  172. spl_level = lock_strm(ms->e_strm);
  173. handle_ioctlers(ms, &spl_level);
  174. }
  175. IF_STRMDBG(CALL) {
  176. STRMTRACE(("SHEAD: SHDispIStr(ms = %lx) returns, irp pending\n", ms));
  177. }
  178. return(STATUS_PENDING);
  179. } // SHDispIStr
  180. STATIC VOID
  181. cancel_ioctl(
  182. IN PDEVICE_OBJECT device,
  183. IN PIRP irp
  184. )
  185. /*++
  186. Routine Description:
  187. This routine is called when an ioctl(...,I_STR,...) is cancelled.
  188. It must release the cancel spinlock before returning !! The caller
  189. has already acquired the cancel spinlock. ref: IoCancelIrp().
  190. Arguments:
  191. device - pointer to the device object
  192. irp - pointer to the irp of this request
  193. Return Value:
  194. none.
  195. --*/
  196. {
  197. int spl_level;
  198. PLIST_ENTRY tmp;
  199. PWAITING_IRP item;
  200. PSTREAM_ENDPOINT ms;
  201. PIO_STACK_LOCATION irpsp = IoGetCurrentIrpStackLocation(irp);
  202. int spl_level2;
  203. ASSERT(device == (PDEVICE_OBJECT) StreamDevice);
  204. ASSERT(irpsp->MajorFunction == IRP_MJ_DEVICE_CONTROL);
  205. ASSERT(irpsp->Parameters.DeviceIoControl.IoControlCode ==
  206. IOCTL_STREAMS_IOCTL);
  207. IF_STRMDBG(CALL) {
  208. STRMTRACE(("SHEAD: cancel_ioctl(irp = %lx) entered\n", irp));
  209. }
  210. IoSetCancelRoutine(irp, NULL); /* unnecessary, but cheap */
  211. ms = (PSTREAM_ENDPOINT) irpsp->FileObject->FsContext;
  212. spl_level = lock_strm(ms->e_strm);
  213. //
  214. // I'm releasing the cancel spinlock and stream lock in the reverse
  215. // order of acquisition. Thus, I need to swap the irql's.
  216. //
  217. spl_level2 = (int) irp->CancelIrql;
  218. IoReleaseCancelSpinLock((KIRQL) spl_level);
  219. spl_level = spl_level2;
  220. if (irp->IoStatus.Information) {
  221. if (untimeout((int)irp->IoStatus.Information) == 0) {
  222. //
  223. // the timeout routine is already running. Just return and let it
  224. // handle the irp.
  225. //
  226. unlock_strm(ms->e_strm, spl_level);
  227. return;
  228. }
  229. irp->IoStatus.Information = 0;
  230. }
  231. if (irp == ms->e_active_ioctl) {
  232. IF_STRMDBG(CALL) {
  233. STRMTRACE(("SHEAD: cancel_ioctl(irp = %lx) cancelled\n", irp));
  234. }
  235. ms->e_active_ioctl = NULL;
  236. handle_ioctlers(ms, &spl_level);
  237. shortreply(irp, STATUS_CANCELLED, 0);
  238. return;
  239. }
  240. for (tmp = ms->e_ioctlers.Flink;
  241. tmp != &ms->e_ioctlers;
  242. tmp = tmp->Flink) {
  243. item = CONTAINING_RECORD(tmp,
  244. WAITING_IRP,
  245. w_list);
  246. if (irp != item->w_irp) {
  247. continue;
  248. }
  249. RemoveEntryList(&(item->w_list));
  250. ExFreePool(item);
  251. unlock_strm(ms->e_strm, spl_level);
  252. shortreply(irp, STATUS_CANCELLED, 0);
  253. IF_STRMDBG(CALL) {
  254. STRMTRACE(("SHEAD: cancel_ioctl(irp = %lx) cancelled\n", irp));
  255. }
  256. return;
  257. }
  258. unlock_strm(ms->e_strm, spl_level);
  259. IF_STRMDBG(CALL) {
  260. STRMTRACE(("SHEAD: cancel_ioctl(irp = %lx) not found\n", irp));
  261. }
  262. } // cancel_ioctl
  263. STATIC NTSTATUS
  264. do_sioctl(
  265. IN PIRP irp,
  266. IN BOOLEAN ignored,
  267. IN int *spl_levelp,
  268. OUT BOOLEAN *must_be_null
  269. )
  270. /*++
  271. Routine Description:
  272. This function is called to put an M_IOCTL message down a stream. It
  273. either sends the message or chains it to ms->e_writers. This function
  274. is similar to do_putmsg(), both of which are based on the SpiderStreams
  275. emulator's function, do_req().
  276. Call this function with the stream locked !!!
  277. Arguments:
  278. irp - pointer to the IRP representing this request
  279. ignored - this parameter is ignored
  280. spl_levelp - pointer to the interrupt priority level at which the
  281. stream was locked
  282. must_be_null - since this function doesn't set this optional return
  283. parameter, this must be NULL
  284. Return Value:
  285. an NT status code. Unless this is STATUS_PENDING, this function has
  286. completed the IRP.
  287. --*/
  288. {
  289. int MyErrno;
  290. mblk_t *mp;
  291. struct iocblk *iocp;
  292. PSTREAM_ENDPOINT ms;
  293. PISTR_ARGS_INOUT inbuf;
  294. struct strioctl *striop;
  295. PIO_STACK_LOCATION irpsp;
  296. IF_STRMDBG(CALL) {
  297. STRMTRACE(("SHEAD: do_sioctl(irp = %lx) entered\n", irp));
  298. }
  299. ASSERT(must_be_null == NULL);
  300. irpsp = IoGetCurrentIrpStackLocation(irp);
  301. //
  302. // these were already verified by SHDispIStr().
  303. //
  304. ASSERT(irpsp->Parameters.DeviceIoControl.InputBufferLength >=
  305. sizeof(ISTR_ARGS_INOUT) - 1);
  306. ASSERT((irpsp->Parameters.DeviceIoControl.IoControlCode & 0x3) ==
  307. METHOD_BUFFERED);
  308. ms = (STREAM_ENDPOINT *) irpsp->FileObject->FsContext;
  309. ASSERT(irp == ms->e_active_ioctl);
  310. if (ms->e_werror) {
  311. MyErrno = ms->e_werror;
  312. }
  313. else if (ms->e_linked) {
  314. MyErrno = EINVAL;
  315. }
  316. else {
  317. MyErrno = 0;
  318. }
  319. if (MyErrno) {
  320. IF_STRMDBG(TERSE) {
  321. STRMTRACE(("SHEAD: do_sioctl(%lx) error = %d\n", ms, MyErrno));
  322. }
  323. if (irp->IoStatus.Information) {
  324. if (untimeout((int)irp->IoStatus.Information) == 0) {
  325. //
  326. // The timeout routine will handle this
  327. //
  328. unlock_strm(ms->e_strm, *spl_levelp);
  329. return(STATUS_PENDING);
  330. }
  331. irp->IoStatus.Information = 0;
  332. }
  333. ms->e_active_ioctl = NULL;
  334. unlock_strm(ms->e_strm, *spl_levelp);
  335. SHpGenReply(irp, -1, MyErrno);
  336. return(STATUS_SUCCESS);
  337. }
  338. //
  339. // the caller marshalled the input arguments contiguously thus:
  340. //
  341. // typedef struct _ISTR_ARGS_INOUT { // ioctl(,I_STR,)
  342. // int a_iocode; // I_STR
  343. // struct strioctl a_strio; // (required)
  344. // int a_unused[2]; // (required)
  345. // char a_stuff[1]; // ic_dp buffer (optional)
  346. //
  347. // } ISTR_ARGS_INOUT, PISTR_ARGS_INOUT;
  348. //
  349. //
  350. //
  351. inbuf = (PISTR_ARGS_INOUT) irp->AssociatedIrp.SystemBuffer;
  352. striop = &(inbuf->a_strio);
  353. IF_STRMDBG(VERBOSE) {
  354. STRMTRACE(("SHEAD: do_sioctl(ic_cmd, timout, len = %lx, %lx, %lx)\n",
  355. striop->ic_cmd, striop->ic_timout, striop->ic_len));
  356. }
  357. mp = irptomp(irp, BPRI_LO, sizeof(struct iocblk), striop->ic_len,
  358. (char *) &(inbuf->a_strio));
  359. if (!mp) {
  360. if (irp->IoStatus.Information) {
  361. if (untimeout((int)irp->IoStatus.Information) == 0) {
  362. //
  363. // The timeout routine will handle this
  364. //
  365. unlock_strm(ms->e_strm, *spl_levelp);
  366. return(STATUS_PENDING);
  367. }
  368. irp->IoStatus.Information = 0;
  369. }
  370. ms->e_active_ioctl = NULL;
  371. unlock_strm(ms->e_strm, *spl_levelp);
  372. shortreply(irp, STATUS_NO_MEMORY, 0);
  373. return(STATUS_NO_MEMORY);
  374. }
  375. ASSERT(mp->b_datap->db_type == M_PROTO);
  376. mp->b_datap->db_type = M_IOCTL;
  377. iocp = (struct iocblk *) mp->b_rptr;
  378. ASSERT(iocp);
  379. iocp->ioc_cmd = striop->ic_cmd;
  380. iocp->ioc_uid = 0;
  381. iocp->ioc_gid = 0;
  382. iocp->ioc_id = ++(ms->e_strm->str_iocid);
  383. if (iocp->ioc_id == 0) {
  384. iocp->ioc_id = ms->e_strm->str_iocid = 1;
  385. }
  386. iocp->ioc_count = striop->ic_len;
  387. iocp->ioc_error = 0;
  388. iocp->ioc_rval = 0;
  389. //
  390. // shput() calls unlock_strm(ms->e_strm).
  391. //
  392. shput(ms->e_strm, mp, 0, spl_levelp);
  393. IF_STRMDBG(CALL) {
  394. STRMTRACE(("SHEAD: do_sioctl(ms = %lx) returns, irp pending\n", ms));
  395. }
  396. return(STATUS_PENDING);
  397. } // do_sioctl
  398. STATIC VOID
  399. handle_ioctlers (
  400. IN STREAM_ENDPOINT *ms,
  401. IN int *spl_levelp
  402. )
  403. /*++
  404. Routine Description:
  405. This routine starts the next ioctl() that is pending on a stream. It
  406. is based on the SpiderStreams emulator function of the same name.
  407. This routine is called with the stream locked, and unlocks the stream
  408. before returning.
  409. Arguments:
  410. ms - pointer to the stream endpoint
  411. spl_level - priority level to resume after releasing lock.
  412. Return Value:
  413. none.
  414. --*/
  415. {
  416. PIRP irp;
  417. PLIST_ENTRY tmp;
  418. PWAITING_IRP item;
  419. ASSERT(ms);
  420. if (ms->e_active_ioctl) {
  421. //
  422. // Already processing an ioctl
  423. //
  424. unlock_strm(ms->e_strm, *spl_levelp);
  425. return;
  426. }
  427. ASSERT(!ms->e_active_ioctl);
  428. while (!IsListEmpty(&(ms->e_ioctlers))) {
  429. tmp = RemoveHeadList( &(ms->e_ioctlers) );
  430. item = CONTAINING_RECORD(tmp,
  431. WAITING_IRP,
  432. w_list);
  433. ASSERT(item->w_function == do_sioctl);
  434. irp = item->w_irp;
  435. ms->e_active_ioctl = irp;
  436. ExFreePool(item);
  437. if (do_sioctl(irp, FALSE, spl_levelp, NULL) == STATUS_PENDING) {
  438. return;
  439. }
  440. *spl_levelp = lock_strm(ms->e_strm);
  441. }
  442. unlock_strm(ms->e_strm, *spl_levelp);
  443. return;
  444. } // handle_ioctlers
  445. STATIC int
  446. ioc_timeout(
  447. IN char *arg
  448. )
  449. /*++
  450. Routine Description:
  451. This function is called when an ioctl() times out. It is based on the
  452. SpiderStreams emulator function of the same name.
  453. Arguments:
  454. arg - pointer to irp representing the ioctl() which timed out.
  455. Return Value:
  456. 0
  457. Discussion:
  458. Suppose iocrdy() is called because our M_IOCACK arrives. iocrdy() calls
  459. lock_strm(), and prepares to complete the IRP. Just then, our timeout
  460. fires, and this function is called. We call lock_strm() and spin.
  461. Then, iocrdy() completes the irp and calls unlock_strm(), unblocking us.
  462. We unchain an irp from ms->e_ioctlers and completes it with an ETIME
  463. error. However, this is the wrong IRP to complete !!
  464. One possible solution is for arg to be a pointer to a structure containing
  465. both ms and the IRP. Then, we can verify that the IRP at the head of the
  466. list is ours. Still, what if the IRP is reused ?
  467. The bug above may arise after any of the following situations:
  468. 1. a stream is dup()'ed,
  469. 2. we are called from a multi-threaded application,
  470. 3. when a stream is NtOpen()'ed without the SYNCHRONOUS flag,
  471. 4. an IRP is cancelled.
  472. --*/
  473. {
  474. PIRP irp;
  475. int spl_level;
  476. PLIST_ENTRY tmp;
  477. PWAITING_IRP item;
  478. STREAM_ENDPOINT *ms;
  479. PIO_STACK_LOCATION irpsp;
  480. IF_STRMDBG(CALL) {
  481. STRMTRACE(("SHEAD: ioc_timeout(irp = %lx) entered\n", arg));
  482. }
  483. irp = (PIRP) arg;
  484. irpsp = IoGetCurrentIrpStackLocation(irp);
  485. ms = (STREAM_ENDPOINT *) irpsp->FileObject->FsContext;
  486. ASSERT(irpsp->MajorFunction == IRP_MJ_DEVICE_CONTROL);
  487. ASSERT(irpsp->Parameters.DeviceIoControl.IoControlCode ==
  488. IOCTL_STREAMS_IOCTL);
  489. spl_level = lock_strm(ms->e_strm);
  490. if (irp == ms->e_active_ioctl) {
  491. ms->e_active_ioctl = NULL;
  492. handle_ioctlers(ms, &spl_level);
  493. }
  494. else {
  495. for (tmp = ms->e_ioctlers.Flink;
  496. tmp != &(ms->e_ioctlers);
  497. tmp = tmp->Flink) {
  498. item = CONTAINING_RECORD(tmp,
  499. WAITING_IRP,
  500. w_list);
  501. if (irp != item->w_irp) {
  502. continue;
  503. }
  504. RemoveEntryList(&(item->w_list));
  505. ExFreePool(item);
  506. break;
  507. }
  508. unlock_strm(ms->e_strm, spl_level);
  509. }
  510. irp->IoStatus.Information = 0;
  511. SHpGenReply(irp, -1, ETIME);
  512. IF_STRMDBG(CALL) {
  513. STRMTRACE(("SHEAD: ioc_timeout(irp = %lx) completed\n", irp));
  514. }
  515. return(0);
  516. } // ioc_timeout
  517. void
  518. iocrdy(
  519. IN STREAM_ENDPOINT *ms,
  520. IN mblk_t *mp,
  521. IN int *spl_levelp
  522. )
  523. /*++
  524. Routine Description:
  525. This function is called by the Stream Head Driver when either an M_IOCACK
  526. or an M_IOCNAK message arrives at its read queue of the specified stream.
  527. It is based on the SpiderStreams emulator function of the same name.
  528. This function is called with the stream locked, and unlocks the stream
  529. before returning.
  530. Arguments:
  531. ms - pointer to the stream endpoint
  532. mp - pointer to the STREAMS message that arrived
  533. spl_levelp - ptr to priority level to resume after releasing lock.
  534. Return Value:
  535. none.
  536. --*/
  537. {
  538. PIRP irp;
  539. struct iocblk *iocp = (struct iocblk *) mp->b_rptr;
  540. IF_STRMDBG(CALL) {
  541. STRMTRACE(("SHEAD: iocrdy(ms = %lx)\n", ms));
  542. }
  543. if (!ms) {
  544. IF_STRMDBG(TERSE) {
  545. STRMTRACE(("SHEAD: iocrdy(ms = NULL) !!\n"));
  546. }
  547. freemsg(mp);
  548. unlock_strm(ms->e_strm, *spl_levelp);
  549. return;
  550. }
  551. irp = ms->e_active_ioctl;
  552. ms->e_active_ioctl = NULL;
  553. //
  554. // ensure that someone is still waiting for the reply.
  555. //
  556. if (!irp) {
  557. freemsg(mp);
  558. unlock_strm(ms->e_strm, *spl_levelp);
  559. qenable((ms->e_strm)->str_sq);
  560. // handle_ioctlers(ms, spl_levelp);
  561. return;
  562. }
  563. //
  564. // if there's an ioctl timer running, clear it now.
  565. //
  566. if (irp->IoStatus.Information) {
  567. if (untimeout((int)irp->IoStatus.Information) == 0) {
  568. //
  569. // The timeout routine will handle this
  570. //
  571. ms->e_active_ioctl = irp;
  572. unlock_strm(ms->e_strm, *spl_levelp);
  573. freemsg(mp);
  574. return;
  575. }
  576. irp->IoStatus.Information = 0;
  577. }
  578. switch (iocp->ioc_cmd) {
  579. case I_LINK:
  580. case I_UNLINK:
  581. unlock_strm(ms->e_strm, *spl_levelp);
  582. if (mp->b_datap->db_type == M_IOCACK) {
  583. SHpGenReply(irp,
  584. ((struct linkblk *) (mp->b_cont->b_rptr))->l_index,
  585. 0);
  586. }
  587. else {
  588. SHpGenReply(irp, -1, iocp->ioc_error);
  589. }
  590. // *spl_levelp = lock_strm(ms->e_strm);
  591. freemsg(mp);
  592. break;
  593. case I_PLINK:
  594. case I_PUNLINK:
  595. ASSERT(0);
  596. break;
  597. //
  598. // if we get here, it's an ioctl(I_STR). iocp->ioc_cmd is a some
  599. // user-defined command code.
  600. //
  601. default:
  602. unlock_strm(ms->e_strm, *spl_levelp);
  603. (void) iocreply(mp, irp);
  604. // *spl_levelp = lock_strm(ms->e_strm);
  605. break;
  606. }
  607. qenable((ms->e_strm)->str_sq);
  608. // handle_ioctlers(ms, spl_levelp);
  609. return;
  610. } // iocrdy