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.

1324 lines
32 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. lpipeio.c
  5. Abstract:
  6. This module implements all file descriptor oriented APIs.
  7. Author:
  8. Mark Lucovsky (markl) 30-Mar-1989
  9. Revision History:
  10. --*/
  11. #include <sys/stat.h>
  12. #include "psxsrv.h"
  13. BOOLEAN
  14. LocalPipeRead (
  15. IN PPSX_PROCESS p,
  16. IN OUT PPSX_API_MSG m,
  17. IN PFILEDESCRIPTOR Fd
  18. );
  19. BOOLEAN
  20. LocalPipeWrite (
  21. IN PPSX_PROCESS p,
  22. IN OUT PPSX_API_MSG m,
  23. IN PFILEDESCRIPTOR Fd
  24. );
  25. BOOLEAN
  26. LocalPipeDup(
  27. IN PPSX_PROCESS p,
  28. IN OUT PPSX_API_MSG m,
  29. IN PFILEDESCRIPTOR Fd,
  30. IN PFILEDESCRIPTOR FdDup
  31. );
  32. BOOLEAN
  33. LocalPipeLseek (
  34. IN PPSX_PROCESS p,
  35. IN OUT PPSX_API_MSG m,
  36. IN PFILEDESCRIPTOR Fd
  37. )
  38. /*++
  39. Routine Description:
  40. This procedure implements lseek when the device being seeked on
  41. is a local or named pipe.
  42. Arguments:
  43. p - Supplies the address of the process making the call.
  44. m - Supplies the address of the message associated with the request.
  45. Fd - supplies the address of the file descriptor being seekd
  46. Return Value:
  47. ???
  48. --*/
  49. {
  50. m->Error = ESPIPE;
  51. return TRUE;
  52. }
  53. BOOLEAN
  54. LocalPipeStat (
  55. IN PIONODE IoNode,
  56. IN HANDLE FileHandle,
  57. OUT struct stat *StatBuf,
  58. OUT NTSTATUS *pStatus
  59. )
  60. /*++
  61. Routine Description:
  62. This procedure implements stat when the device being read
  63. is a local pipe.
  64. Arguments:
  65. IoNode - supplies a pointer to the ionode of the pipe for which stat is
  66. requested.
  67. FileHandle - supplies the Nt file handle of the pipe. NULL for local pipes.
  68. StatBuf - Supplies the address of the statbuf portion of the message
  69. associated with the request.
  70. Return Value:
  71. ???
  72. --*/
  73. {
  74. // Pipe() sets the IoNode fields.
  75. StatBuf->st_mode = IoNode->Mode;
  76. StatBuf->st_ino = (ino_t)IoNode->FileSerialNumber;
  77. StatBuf->st_dev = IoNode->DeviceSerialNumber;
  78. StatBuf->st_uid = IoNode->OwnerId;
  79. StatBuf->st_gid = IoNode->GroupId;
  80. StatBuf->st_atime = IoNode->AccessDataTime;
  81. StatBuf->st_mtime = IoNode->ModifyDataTime;
  82. StatBuf->st_ctime = IoNode->ModifyIoNodeTime;
  83. StatBuf->st_size = PIPE_BUF;
  84. // This implementation dependent.
  85. StatBuf->st_nlink = 0;
  86. return TRUE;
  87. }
  88. VOID
  89. LocalPipeNewHandle (
  90. IN PPSX_PROCESS p,
  91. IN PFILEDESCRIPTOR Fd
  92. )
  93. /*++
  94. Routine Description:
  95. This function is called any time a handle is created for a pipe.
  96. Arguments:
  97. p - Supplies a pointer to the process creating the handle to the pipe.
  98. Fd - Supplies the file descriptor that refers to the pipe.
  99. Return Value:
  100. None.
  101. --*/
  102. {
  103. PLOCAL_PIPE Pipe;
  104. Pipe = (PLOCAL_PIPE)Fd->SystemOpenFileDesc->IoNode->Context;
  105. RtlEnterCriticalSection(&Pipe->CriticalSection);
  106. if (Fd->SystemOpenFileDesc->Flags & PSX_FD_READ) {
  107. Pipe->ReadHandleCount++;
  108. }
  109. if (Fd->SystemOpenFileDesc->Flags & PSX_FD_WRITE) {
  110. Pipe->WriteHandleCount++;
  111. }
  112. RtlLeaveCriticalSection(&Pipe->CriticalSection);
  113. }
  114. VOID
  115. LocalPipeClose (
  116. IN PPSX_PROCESS p,
  117. IN PFILEDESCRIPTOR Fd
  118. )
  119. /*++
  120. Routine Description:
  121. This function is called any time a handle is deleted for a pipe.
  122. Arguments:
  123. p - Supplies a pointer to the closing the handle to the pipe.
  124. Fd - Supplies the file descriptor that refers to the pipe.
  125. Return Value:
  126. None.
  127. --*/
  128. {
  129. PLOCAL_PIPE Pipe;
  130. PINTCB IntCb;
  131. PPSX_PROCESS WaitingProc;
  132. PLIST_ENTRY Next;
  133. Pipe = (PLOCAL_PIPE)Fd->SystemOpenFileDesc->IoNode->Context;
  134. RtlEnterCriticalSection(&Pipe->CriticalSection);
  135. if ((Fd->SystemOpenFileDesc->Flags & PSX_FD_READ) &&
  136. (0 == --Pipe->ReadHandleCount)) {
  137. //
  138. // Last reader close; any writers hanging around
  139. // get EPIPE and a SIGPIPE
  140. //
  141. RtlEnterCriticalSection(&BlockLock);
  142. Next = Pipe->WaitingWriters.Flink;
  143. while (Next != &Pipe->WaitingWriters) {
  144. IntCb = CONTAINING_RECORD(Next, INTCB, Links);
  145. WaitingProc = (PPSX_PROCESS)IntCb->IntContext;
  146. UnblockProcess(WaitingProc, IoCompletionInterrupt,
  147. TRUE, 0);
  148. RtlEnterCriticalSection(&BlockLock);
  149. Next = Pipe->WaitingWriters.Flink;
  150. }
  151. RtlLeaveCriticalSection(&BlockLock);
  152. }
  153. if ((Fd->SystemOpenFileDesc->Flags & PSX_FD_WRITE) &&
  154. (0 == --Pipe->WriteHandleCount)) {
  155. //
  156. // Last writer close; any readers hanging around
  157. // get 0.
  158. //
  159. RtlEnterCriticalSection(&BlockLock);
  160. Next = Pipe->WaitingReaders.Flink;
  161. while (Next != &Pipe->WaitingReaders) {
  162. IntCb = CONTAINING_RECORD(Next, INTCB, Links);
  163. WaitingProc = (PPSX_PROCESS)IntCb->IntContext;
  164. UnblockProcess(WaitingProc, IoCompletionInterrupt,
  165. TRUE, 0);
  166. RtlEnterCriticalSection(&BlockLock);
  167. Next = Pipe->WaitingReaders.Flink;
  168. }
  169. RtlLeaveCriticalSection(&BlockLock);
  170. }
  171. RtlLeaveCriticalSection(&Pipe->CriticalSection);
  172. }
  173. VOID
  174. LocalPipeIoNodeClose (
  175. IN PIONODE IoNode
  176. )
  177. /*++
  178. Routine Description:
  179. This function is called when the IONODE representing a pipe is
  180. closed. Its function is to tear down the pipe.
  181. Arguments:
  182. IoNode - Supplies the IoNode being deleted
  183. Return Value:
  184. None.
  185. --*/
  186. {
  187. PLOCAL_PIPE Pipe;
  188. Pipe = (PLOCAL_PIPE) IoNode->Context;
  189. RtlDeleteCriticalSection(&Pipe->CriticalSection);
  190. RtlFreeHeap(PsxHeap, 0,Pipe);
  191. }
  192. PSXIO_VECTORS LocalPipeVectors = {
  193. NULL,
  194. LocalPipeNewHandle,
  195. LocalPipeClose,
  196. NULL,
  197. LocalPipeIoNodeClose,
  198. LocalPipeRead,
  199. LocalPipeWrite,
  200. LocalPipeDup,
  201. LocalPipeLseek,
  202. LocalPipeStat
  203. };
  204. VOID
  205. InitializeLocalPipe(
  206. IN PLOCAL_PIPE Pipe
  207. )
  208. /*++
  209. Routine Description:
  210. This function initializes a local pipe
  211. Arguments:
  212. Pipe - Supplies the address of a local pipe
  213. Return Value:
  214. None.
  215. --*/
  216. {
  217. NTSTATUS st;
  218. st = RtlInitializeCriticalSection(&Pipe->CriticalSection);
  219. ASSERT(NT_SUCCESS(st));
  220. InitializeListHead(&Pipe->WaitingWriters);
  221. InitializeListHead(&Pipe->WaitingReaders);
  222. Pipe->ReadHandleCount = 0;
  223. Pipe->WriteHandleCount = 0;
  224. Pipe->BufferSize = PIPE_BUF;
  225. Pipe->DataInPipe = 0;
  226. Pipe->WritePointer = &Pipe->Buffer[0];
  227. Pipe->ReadPointer = &Pipe->Buffer[0];
  228. }
  229. VOID
  230. LocalPipeWriteHandler(
  231. IN PPSX_PROCESS p,
  232. IN PINTCB IntControlBlock,
  233. IN PSX_INTERRUPTREASON InterruptReason,
  234. IN int Signal // signal causing wakeup, if any
  235. )
  236. /*++
  237. Routine Description:
  238. This procedure is called when a there is room in a pipe, and a blocked
  239. writer exists whose current write request length is less than the
  240. amount of room in the pipe.
  241. Arguments:
  242. p - Supplies the address of the process being interrupted.
  243. IntControlBlock - Supplies the address of the interrupt control block.
  244. InterruptReason - Supplies the reason that this process is being
  245. interrupted.
  246. Return Value:
  247. None.
  248. --*/
  249. {
  250. PFILEDESCRIPTOR Fd;
  251. BOOLEAN reply;
  252. PPSX_API_MSG m;
  253. PPSX_WRITE_MSG args;
  254. RtlLeaveCriticalSection(&BlockLock);
  255. m = IntControlBlock->IntMessage;
  256. args = &m->u.Write;
  257. if (InterruptReason == SignalInterrupt) {
  258. //
  259. // The write was interrupted by a signal. Bail out of
  260. // service and let the interrupt be handled
  261. //
  262. RtlFreeHeap(PsxHeap, 0,IntControlBlock);
  263. m->Error = EINTR;
  264. m->Signal = Signal;
  265. ApiReply(p,m,NULL);
  266. RtlFreeHeap(PsxHeap, 0,m);
  267. return;
  268. }
  269. Fd = FdIndexToFd(p,args->FileDes);
  270. if (!Fd) {
  271. Panic("LocalPipeWriteHandler: FdIndex");
  272. }
  273. RtlFreeHeap(PsxHeap, 0, IntControlBlock);
  274. reply = LocalPipeWrite(p, m, Fd);
  275. if (reply) {
  276. ApiReply(p, m, NULL);
  277. }
  278. RtlFreeHeap(PsxHeap, 0,m);
  279. }
  280. BOOLEAN
  281. LocalPipeWrite (
  282. IN PPSX_PROCESS p,
  283. IN OUT PPSX_API_MSG m,
  284. IN PFILEDESCRIPTOR Fd
  285. )
  286. /*++
  287. Routine Description:
  288. This procedure implements write when the device being written
  289. is a local pipe.
  290. Arguments:
  291. p - Supplies the address of the process making the call.
  292. m - Supplies the address of the message associated with the request.
  293. Fd - supplies the address of the file descriptor being written.
  294. Return Value:
  295. TRUE - the routine completed, and a reply should be sent
  296. FALSE - the routine was blocked, no reply should be sent
  297. --*/
  298. {
  299. PPSX_WRITE_MSG args;
  300. PLOCAL_PIPE Pipe;
  301. LONG Chunk, RoomInPipe;
  302. SIZE_T cb;
  303. PUCHAR WriteDataPoint, ProcessBuffer;
  304. NTSTATUS st;
  305. PINTCB IntCb;
  306. PPSX_PROCESS WaitingReader;
  307. LARGE_INTEGER Time;
  308. ULONG PosixTime;
  309. NTSTATUS Status;
  310. args = &m->u.Write;
  311. Pipe = (PLOCAL_PIPE) Fd->SystemOpenFileDesc->IoNode->Context;
  312. RtlEnterCriticalSection(&Pipe->CriticalSection);
  313. //
  314. // If we're writing to a pipe with no readers connected, we return
  315. // EPIPE and send a SIGPIPE to the process. Broken pipe, call a
  316. // plumber.
  317. //
  318. if (0 == Pipe->ReadHandleCount) {
  319. RtlLeaveCriticalSection(&Pipe->CriticalSection);
  320. m->Error = EPIPE;
  321. AcquireProcessStructureLock();
  322. PsxSignalProcess(p, SIGPIPE);
  323. ReleaseProcessStructureLock();
  324. return TRUE;
  325. }
  326. //
  327. // if requested write size is greater than buffer size,
  328. // write must be broken up into Pipe->BufferSize atomic
  329. // chunks. If this is the case, Scratch1 is used to record
  330. // amount of data transfered so far, and Scratch2 is used to
  331. // record the number of bytes left in the total transfer
  332. //
  333. if (args->Nbytes > Pipe->BufferSize) {
  334. args->Scratch2 = args->Nbytes - Pipe->BufferSize;
  335. args->Nbytes = Pipe->BufferSize;
  336. }
  337. RoomInPipe = Pipe->BufferSize - Pipe->DataInPipe;
  338. if (args->Nbytes > RoomInPipe) {
  339. //
  340. // There is not enough space in the pipe for the write to
  341. // succeed. If the O_NONBLOCK flag is set, write whatever
  342. // will fit. Otherwise, block the write and wait for a read
  343. // to empty some of the data.
  344. //
  345. if (Fd->SystemOpenFileDesc->Flags & PSX_FD_NOBLOCK) {
  346. args->Nbytes = 1;
  347. if (args->Nbytes > RoomInPipe) {
  348. m->Error = EAGAIN;
  349. return TRUE;
  350. }
  351. // continue below to write
  352. } else {
  353. Status = BlockProcess(p, (PVOID)p, LocalPipeWriteHandler, m,
  354. &Pipe->WaitingWriters, &Pipe->CriticalSection);
  355. if (!NT_SUCCESS(Status)) {
  356. m->Error = PsxStatusToErrno(Status);
  357. return TRUE;
  358. }
  359. //
  360. // Successfully blocked -- don't reply to message.
  361. //
  362. return FALSE;
  363. }
  364. }
  365. //
  366. // there is room in the pipe for the write to occur
  367. //
  368. WriteDataPoint = Pipe->WritePointer;
  369. ProcessBuffer = (PUCHAR) args->Buf;
  370. if ((ULONG_PTR)WriteDataPoint + args->Nbytes >
  371. (ULONG_PTR)&Pipe->Buffer[Pipe->BufferSize-1]) {
  372. Chunk = (LONG)((ULONG_PTR)&Pipe->Buffer[Pipe->BufferSize-1] -
  373. (ULONG_PTR)WriteDataPoint + 1);
  374. } else {
  375. Chunk = args->Nbytes;
  376. }
  377. st = NtReadVirtualMemory(p->Process, ProcessBuffer, WriteDataPoint,
  378. (SIZE_T)Chunk, &cb);
  379. if (!NT_SUCCESS(st) || (LONG)cb != Chunk) {
  380. //
  381. // If the read did not work, then report as IO error
  382. //
  383. RtlLeaveCriticalSection(&Pipe->CriticalSection);
  384. m->Error = EIO;
  385. return TRUE;
  386. }
  387. ProcessBuffer += Chunk;
  388. if (Chunk < args->Nbytes) {
  389. Chunk = args->Nbytes - Chunk;
  390. WriteDataPoint = &Pipe->Buffer[0];
  391. st = NtReadVirtualMemory(p->Process, ProcessBuffer, WriteDataPoint,
  392. (ULONG)Chunk, &cb);
  393. if (!NT_SUCCESS(st) || (LONG)cb != Chunk) {
  394. //
  395. // If the read did not work, then report as IO error
  396. //
  397. RtlLeaveCriticalSection(&Pipe->CriticalSection);
  398. m->Error = EIO;
  399. return TRUE;
  400. }
  401. Pipe->WritePointer = (PUCHAR)((ULONG_PTR)WriteDataPoint + Chunk);
  402. } else {
  403. Pipe->WritePointer = (PUCHAR)((ULONG_PTR)WriteDataPoint + Chunk);
  404. }
  405. if (Pipe->WritePointer > &Pipe->Buffer[Pipe->BufferSize - 1]) {
  406. Pipe->WritePointer = &Pipe->Buffer[0];
  407. }
  408. Pipe->DataInPipe += args->Nbytes;
  409. if (Pipe->DataInPipe > Pipe->BufferSize) {
  410. Panic("LocalPipeWrite: Oops\n");
  411. }
  412. // Update ctime and mtime in IoNode - done in subsystem for local pipes
  413. NtQuerySystemTime(&Time);
  414. if (!RtlTimeToSecondsSince1970(&Time, &PosixTime)) {
  415. PosixTime = 0L; // Time not within range of 1970 - 2105
  416. }
  417. RtlEnterCriticalSection(&Fd->SystemOpenFileDesc->IoNode->IoNodeLock);
  418. Fd->SystemOpenFileDesc->IoNode->ModifyDataTime =
  419. Fd->SystemOpenFileDesc->IoNode->ModifyIoNodeTime = PosixTime;
  420. RtlLeaveCriticalSection(&Fd->SystemOpenFileDesc->IoNode->IoNodeLock);
  421. m->ReturnValue += args->Nbytes;
  422. args->Buf += args->Nbytes;
  423. //
  424. // Check for WaitingReaders. If any are found, then kick em
  425. //
  426. RtlEnterCriticalSection(&BlockLock);
  427. if (!IsListEmpty(&Pipe->WaitingReaders)) {
  428. IntCb = (PINTCB)Pipe->WaitingReaders.Flink;
  429. IntCb = CONTAINING_RECORD(IntCb,INTCB,Links);
  430. WaitingReader = (PPSX_PROCESS) IntCb->IntContext;
  431. RtlLeaveCriticalSection(&Pipe->CriticalSection);
  432. UnblockProcess(WaitingReader, IoCompletionInterrupt, TRUE, 0);
  433. //
  434. // Determine if this is a broken up long write. If Scratch2 is
  435. // non-zero then more transfers need to occur. If Scratch1 is
  436. // non-zero, then update to account for data transfered in this
  437. // iteration.
  438. //
  439. } else {
  440. RtlLeaveCriticalSection(&BlockLock);
  441. RtlLeaveCriticalSection(&Pipe->CriticalSection);
  442. }
  443. //
  444. // If we're doing non-blocking io, we've written what will fit into
  445. // the pipe and we should return to the user now.
  446. //
  447. if (Fd->SystemOpenFileDesc->Flags & PSX_FD_NOBLOCK) {
  448. return TRUE;
  449. }
  450. //
  451. // Determine if this is a broken up long write. If Scratch2 is
  452. // non-zero then more transfers need to occur. If Scratch1 is
  453. // non-zero, then update to account for data transfered in this
  454. // iteration.
  455. //
  456. if (args->Scratch2) {
  457. args->Nbytes = args->Scratch2;
  458. args->Scratch2 = 0;
  459. return LocalPipeWrite(p, m, Fd);
  460. }
  461. return TRUE;
  462. }
  463. VOID
  464. LocalPipeReadHandler(
  465. IN PPSX_PROCESS p,
  466. IN PINTCB IntControlBlock,
  467. IN PSX_INTERRUPTREASON InterruptReason,
  468. IN int Signal // signal causing wakeup, if any
  469. )
  470. /*++
  471. Routine Description:
  472. This procedure is called when data appears in a pipe and the process
  473. specified by p has placed itself on the WaitingReaders queue for the pipe.
  474. Arguments:
  475. p - Supplies the address of the process being interrupted.
  476. IntControlBlock - Supplies the address of the interrupt control block.
  477. InterruptReason - Supplies the reason that this process is being
  478. interrupted. Not used in this handler.
  479. Return Value:
  480. None.
  481. --*/
  482. {
  483. PFILEDESCRIPTOR Fd;
  484. BOOLEAN reply;
  485. PPSX_API_MSG m;
  486. PPSX_READ_MSG args;
  487. RtlLeaveCriticalSection(&BlockLock);
  488. m = IntControlBlock->IntMessage;
  489. args = &m->u.Read;
  490. if (InterruptReason == SignalInterrupt) {
  491. //
  492. // The read was interrupted by a signal. Bail out of
  493. // service and let the interrupt be handled
  494. //
  495. RtlFreeHeap(PsxHeap, 0, IntControlBlock);
  496. m->Error = EINTR;
  497. m->Signal = Signal;
  498. ApiReply(p, m, NULL);
  499. RtlFreeHeap(PsxHeap, 0, m);
  500. return;
  501. }
  502. //
  503. // IoCompletionInterrupt
  504. //
  505. Fd = FdIndexToFd(p, args->FileDes);
  506. if (!Fd) {
  507. Panic("LocalPipeReadHandler: FdIndex");
  508. }
  509. reply = LocalPipeRead(p, m, Fd);
  510. RtlFreeHeap(PsxHeap, 0, IntControlBlock);
  511. if (reply) {
  512. ApiReply(p, m, NULL);
  513. }
  514. RtlFreeHeap(PsxHeap, 0, m);
  515. }
  516. BOOLEAN
  517. LocalPipeRead (
  518. IN PPSX_PROCESS p,
  519. IN OUT PPSX_API_MSG m,
  520. IN PFILEDESCRIPTOR Fd
  521. )
  522. /*++
  523. Routine Description:
  524. This procedure implements read when the device being read
  525. is a local pipe.
  526. Arguments:
  527. p - Supplies the address of the process making the call.
  528. m - Supplies the address of the message associated with the request.
  529. Fd - supplies the address of the file descriptor being read.
  530. Return Value:
  531. TRUE if the read completed, FALSE if the process should block.
  532. --*/
  533. {
  534. PPSX_READ_MSG args, WaitingArgs;
  535. PPSX_PROCESS WaitingWriter;
  536. PLOCAL_PIPE Pipe;
  537. SIZE_T Chunk;
  538. LONG RoomInPipe;
  539. ULONG LargestRead;
  540. SIZE_T cb;
  541. PUCHAR ReadDataPoint, ProcessBuffer;
  542. NTSTATUS st;
  543. PPSX_API_MSG WaitingM;
  544. PLIST_ENTRY Next;
  545. PINTCB IntCb;
  546. LARGE_INTEGER Time;
  547. ULONG PosixTime;
  548. args = &m->u.Read;
  549. //
  550. // check to see if any process has the pipe open for write
  551. //
  552. Pipe = (PLOCAL_PIPE)Fd->SystemOpenFileDesc->IoNode->Context;
  553. ASSERT(NULL != Pipe);
  554. RtlEnterCriticalSection(&Pipe->CriticalSection);
  555. if (0 == Pipe->WriteHandleCount && !Pipe->DataInPipe) {
  556. //
  557. // Reading from an empty pipe with no writers attached gets you
  558. // 0 (EOF).
  559. //
  560. RtlLeaveCriticalSection(&Pipe->CriticalSection);
  561. m->ReturnValue = 0;
  562. return TRUE;
  563. }
  564. if (!Pipe->DataInPipe) {
  565. //
  566. // if we have the pipe open O_NOBLOCK, then simply
  567. // return EAGAIN
  568. //
  569. if (Fd->SystemOpenFileDesc->Flags & PSX_FD_NOBLOCK) {
  570. RtlLeaveCriticalSection(&Pipe->CriticalSection);
  571. m->Error = EAGAIN;
  572. return TRUE;
  573. }
  574. //
  575. // There is no data in the pipe. Set up an interrupt control
  576. // block to wait for data and then block.
  577. //
  578. st = BlockProcess(p, (PVOID)p, LocalPipeReadHandler, m,
  579. &Pipe->WaitingReaders, &Pipe->CriticalSection);
  580. if (!NT_SUCCESS(st)) {
  581. m->Error = PsxStatusToErrno(st);
  582. return TRUE;
  583. }
  584. //
  585. // Successfully blocked -- don't reply to api request.
  586. //
  587. return FALSE;
  588. }
  589. //
  590. // If there is any data in the pipe, then compute the largest
  591. // read size. Then figure out if it has to be broken into two
  592. // transfers in order to turn the circular buffer boundary.
  593. //
  594. if (args->Nbytes > Pipe->DataInPipe) {
  595. LargestRead = Pipe->DataInPipe;
  596. } else {
  597. LargestRead = args->Nbytes;
  598. }
  599. ReadDataPoint = Pipe->ReadPointer;
  600. ProcessBuffer = (PUCHAR)args->Buf;
  601. //
  602. // determine if read can be done in one piece, or if
  603. // the read has to be done in two pieces.
  604. //
  605. if ((ULONG_PTR)ReadDataPoint + LargestRead >
  606. (ULONG_PTR)&Pipe->Buffer[Pipe->BufferSize - 1]) {
  607. Chunk = (SIZE_T)((ULONG_PTR)&Pipe->Buffer[Pipe->BufferSize - 1] -
  608. (ULONG_PTR)ReadDataPoint + 1);
  609. } else {
  610. Chunk = LargestRead;
  611. }
  612. //
  613. // transfer from the pipe to the reading process
  614. //
  615. st = NtWriteVirtualMemory(p->Process, ProcessBuffer, ReadDataPoint,
  616. Chunk, &cb);
  617. if (!NT_SUCCESS(st) || cb != Chunk ) {
  618. //
  619. // If the write did not work, then report as IO error
  620. //
  621. RtlLeaveCriticalSection(&Pipe->CriticalSection);
  622. m->Error = EIO;
  623. return TRUE;
  624. }
  625. ProcessBuffer += Chunk;
  626. if (Chunk < LargestRead) {
  627. //
  628. // the read wraps the pipe boundry. Transfer the second part of
  629. // the read.
  630. //
  631. Chunk = LargestRead - Chunk;
  632. ReadDataPoint = &Pipe->Buffer[0];
  633. st = NtWriteVirtualMemory(p->Process, ProcessBuffer,
  634. ReadDataPoint, Chunk, &cb);
  635. if (!NT_SUCCESS(st) || cb != Chunk) {
  636. //
  637. // If the read did not work, then report as IO error
  638. //
  639. RtlLeaveCriticalSection(&Pipe->CriticalSection);
  640. m->Error = EIO;
  641. return TRUE;
  642. }
  643. Pipe->ReadPointer = (PUCHAR)((ULONG_PTR)ReadDataPoint + Chunk);
  644. } else {
  645. Pipe->ReadPointer = (PUCHAR)((ULONG_PTR)ReadDataPoint + Chunk);
  646. }
  647. if (Pipe->ReadPointer > &Pipe->Buffer[Pipe->BufferSize - 1]) {
  648. Pipe->ReadPointer = &Pipe->Buffer[0];
  649. }
  650. //
  651. // Adjust DataInPipe to account for read. Then check if there
  652. // are any writers present. Pick a writer and kick him
  653. //
  654. Pipe->DataInPipe -= LargestRead;
  655. m->ReturnValue = LargestRead;
  656. RoomInPipe = Pipe->BufferSize - Pipe->DataInPipe;
  657. // Update atime in IoNode - done in subsystem for local pipes
  658. NtQuerySystemTime(&Time);
  659. if ( !RtlTimeToSecondsSince1970(&Time, &PosixTime) ) {
  660. PosixTime = 0L; // Time not within range of 1970 - 2105
  661. }
  662. RtlEnterCriticalSection(&Fd->SystemOpenFileDesc->IoNode->IoNodeLock);
  663. Fd->SystemOpenFileDesc->IoNode->AccessDataTime = PosixTime;
  664. RtlLeaveCriticalSection(&Fd->SystemOpenFileDesc->IoNode->IoNodeLock);
  665. //
  666. // Check for WaitingWriters. If any are found, then kick em
  667. //
  668. RtlEnterCriticalSection(&BlockLock);
  669. if (!IsListEmpty(&Pipe->WaitingWriters)) {
  670. //
  671. // If there are waiting writers, then pick a writer
  672. // and unblock him. The first writer whose current
  673. // write count that is less than or equal to the room
  674. // in pipe is chosen.
  675. //
  676. Next = Pipe->WaitingWriters.Flink;
  677. while (Next != &Pipe->WaitingWriters) {
  678. IntCb = CONTAINING_RECORD(Next, INTCB, Links);
  679. WaitingM = IntCb->IntMessage;
  680. WaitingArgs = &WaitingM->u.Read;
  681. WaitingWriter = (PPSX_PROCESS) IntCb->IntContext;
  682. if (WaitingArgs->Nbytes <= RoomInPipe) {
  683. RtlLeaveCriticalSection(&Pipe->CriticalSection);
  684. UnblockProcess(WaitingWriter,
  685. IoCompletionInterrupt, TRUE, 0);
  686. return TRUE;
  687. }
  688. Next = Next->Flink;
  689. }
  690. }
  691. RtlLeaveCriticalSection(&BlockLock);
  692. RtlLeaveCriticalSection(&Pipe->CriticalSection);
  693. return TRUE;
  694. }
  695. BOOLEAN
  696. LocalPipeDup(
  697. IN PPSX_PROCESS p,
  698. IN OUT PPSX_API_MSG m,
  699. IN PFILEDESCRIPTOR Fd,
  700. IN PFILEDESCRIPTOR FdDup
  701. )
  702. {
  703. PPSX_DUP_MSG args;
  704. PLOCAL_PIPE Pipe;
  705. args = &m->u.Dup;
  706. //
  707. // Copy contents of source file descriptor
  708. // Note that FD_CLOEXEC must be CLEAR in FdDup.
  709. //
  710. *FdDup = *Fd;
  711. FdDup->Flags &= ~PSX_FD_CLOSE_ON_EXEC;
  712. //
  713. // Increment reference count assocated with the SystemOpenFile
  714. // descriptor for this file.
  715. //
  716. RtlEnterCriticalSection(&SystemOpenFileLock);
  717. Fd->SystemOpenFileDesc->HandleCount++;
  718. RtlLeaveCriticalSection(&SystemOpenFileLock);
  719. Pipe = (PLOCAL_PIPE)Fd->SystemOpenFileDesc->IoNode->Context;
  720. RtlEnterCriticalSection(&Pipe->CriticalSection);
  721. if (Fd->SystemOpenFileDesc->Flags & PSX_FD_READ) {
  722. ++Pipe->ReadHandleCount;
  723. }
  724. if (Fd->SystemOpenFileDesc->Flags & PSX_FD_WRITE) {
  725. ++Pipe->WriteHandleCount;
  726. }
  727. RtlLeaveCriticalSection(&Pipe->CriticalSection);
  728. return TRUE;
  729. }
  730. //
  731. // Named Pipes are very similar to local pipes
  732. // once the opens have completed. For that reason,
  733. // they share read/write io routines.
  734. //
  735. VOID
  736. NamedPipeOpenHandler(
  737. IN PPSX_PROCESS p,
  738. IN PINTCB IntControlBlock,
  739. IN PSX_INTERRUPTREASON InterruptReason,
  740. IN int Signal // Signal causing interruption, if any
  741. )
  742. /*++
  743. Routine Description:
  744. This procedure is called when a process waiting for a named pipe
  745. open to complete is either interrupted, or the pipe is opened.
  746. Arguments:
  747. p - Supplies the address of the process being interrupted.
  748. IntControlBlock - Supplies the address of the interrupt control block.
  749. InterruptReason - Supplies the reason that this process is being
  750. interrupted. Not used in this handler.
  751. Return Value:
  752. None.
  753. --*/
  754. {
  755. PFILEDESCRIPTOR Fd;
  756. PPSX_API_MSG m;
  757. PPSX_OPEN_MSG args;
  758. PLOCAL_PIPE Pipe;
  759. PLIST_ENTRY ListToScan;
  760. PPSX_PROCESS Waiter;
  761. PPSX_API_MSG WaitingM;
  762. PLIST_ENTRY Next;
  763. PINTCB IntCb;
  764. RtlLeaveCriticalSection(&BlockLock);
  765. m = IntControlBlock->IntMessage;
  766. args = &m->u.Open;
  767. RtlFreeHeap(PsxHeap, 0, IntControlBlock);
  768. if (InterruptReason == SignalInterrupt) {
  769. //
  770. // The open was interrupted by a signal. Bail out of
  771. // service by closing the half opened pipe and let
  772. // the interrupt be handled
  773. //
  774. m->Error = EINTR;
  775. m->Signal = Signal;
  776. DeallocateFd(p, m->ReturnValue);
  777. ApiReply(p, m, NULL);
  778. RtlFreeHeap(PsxHeap, 0,m);
  779. return;
  780. }
  781. //
  782. // This Open Should be completed.
  783. // Determine the list to scan to
  784. // see if more opens should be completed
  785. // at this time.
  786. //
  787. Fd = FdIndexToFd(p, m->ReturnValue);
  788. ASSERT(Fd);
  789. Pipe = (PLOCAL_PIPE)Fd->SystemOpenFileDesc->IoNode->Context;
  790. //
  791. // The list to scan is the list this process just came off
  792. //
  793. if (Fd->SystemOpenFileDesc->Flags & PSX_FD_READ) {
  794. ListToScan = &Pipe->WaitingReaders;
  795. } else {
  796. ListToScan = &Pipe->WaitingWriters;
  797. }
  798. RtlEnterCriticalSection(&Pipe->CriticalSection);
  799. RtlEnterCriticalSection(&BlockLock);
  800. if (!IsListEmpty(ListToScan)) {
  801. //
  802. // Scan list to see if there are processes waiting in an
  803. // open whose wait can be satisfied.
  804. //
  805. Next = ListToScan->Flink;
  806. while ( Next != ListToScan ) {
  807. IntCb = CONTAINING_RECORD(Next,INTCB,Links);
  808. WaitingM = IntCb->IntMessage;
  809. if ( WaitingM->ApiNumber == PsxOpenApi ) {
  810. Waiter = (PPSX_PROCESS) IntCb->IntContext;
  811. RtlLeaveCriticalSection(&Pipe->CriticalSection);
  812. UnblockProcess(Waiter, IoCompletionInterrupt, TRUE, 0);
  813. ApiReply(p, m, NULL);
  814. RtlFreeHeap(PsxHeap, 0, m);
  815. return;
  816. }
  817. Next = Next->Flink;
  818. }
  819. }
  820. RtlLeaveCriticalSection(&BlockLock);
  821. RtlLeaveCriticalSection(&Pipe->CriticalSection);
  822. ApiReply(p, m, NULL);
  823. RtlFreeHeap(PsxHeap, 0, m);
  824. }
  825. BOOLEAN
  826. NamedPipeOpenNewHandle (
  827. IN PPSX_PROCESS p,
  828. IN PFILEDESCRIPTOR Fd,
  829. IN OUT PPSX_API_MSG m
  830. )
  831. {
  832. NTSTATUS Status;
  833. PLOCAL_PIPE Pipe;
  834. PULONG CountToTest;
  835. PLIST_ENTRY ListToBlockOn;
  836. PLIST_ENTRY ListToScan;
  837. PPSX_PROCESS Waiter;
  838. PPSX_API_MSG WaitingM;
  839. PLIST_ENTRY Next;
  840. PINTCB IntCb;
  841. Pipe = (PLOCAL_PIPE)Fd->SystemOpenFileDesc->IoNode->Context;
  842. LocalPipeNewHandle(p, Fd);
  843. if ((Fd->SystemOpenFileDesc->Flags & (PSX_FD_READ | PSX_FD_WRITE)) ==
  844. (PSX_FD_READ | PSX_FD_WRITE)) {
  845. return TRUE;
  846. }
  847. if (Fd->SystemOpenFileDesc->Flags & PSX_FD_NOBLOCK) {
  848. if (Fd->SystemOpenFileDesc->Flags & PSX_FD_READ) {
  849. return TRUE;
  850. } else {
  851. RtlEnterCriticalSection(&Pipe->CriticalSection);
  852. if (!Pipe->ReadHandleCount) {
  853. m->Error = ENXIO;
  854. RtlLeaveCriticalSection(&Pipe->CriticalSection);
  855. DeallocateFd(p,m->ReturnValue);
  856. } else {
  857. RtlLeaveCriticalSection(&Pipe->CriticalSection);
  858. }
  859. return TRUE;
  860. }
  861. } else {
  862. //
  863. // Pipe is not being opened O_NONBLOCK. If pipe is being opened
  864. // for read, then wait for a writer; otherwise, wait for
  865. // a reader
  866. //
  867. if (Fd->SystemOpenFileDesc->Flags & PSX_FD_READ) {
  868. CountToTest = &Pipe->WriteHandleCount;
  869. ListToBlockOn = &Pipe->WaitingReaders;
  870. ListToScan = &Pipe->WaitingWriters;
  871. } else {
  872. CountToTest = &Pipe->ReadHandleCount;
  873. ListToBlockOn = &Pipe->WaitingWriters;
  874. ListToScan = &Pipe->WaitingReaders;
  875. }
  876. RtlEnterCriticalSection(&Pipe->CriticalSection);
  877. if (!*CountToTest) {
  878. Status = BlockProcess(p, (PVOID)p, NamedPipeOpenHandler, m,
  879. ListToBlockOn, &Pipe->CriticalSection);
  880. if (!NT_SUCCESS(Status)) {
  881. m->Error = PsxStatusToErrno(Status);
  882. return TRUE;
  883. }
  884. //
  885. // The process is successfully blocked -- do not reply to the api
  886. // request.
  887. //
  888. return FALSE;
  889. } else {
  890. RtlEnterCriticalSection(&BlockLock);
  891. if (!IsListEmpty(ListToScan)) {
  892. //
  893. // Scan list to see if there are processes waiting in an
  894. // open whose wait can be satisfied.
  895. //
  896. Next = ListToScan->Flink;
  897. while (Next != ListToScan) {
  898. IntCb = CONTAINING_RECORD(Next,INTCB,Links);
  899. WaitingM = IntCb->IntMessage;
  900. if (WaitingM->ApiNumber == PsxOpenApi) {
  901. Waiter = (PPSX_PROCESS) IntCb->IntContext;
  902. RtlLeaveCriticalSection(&Pipe->CriticalSection);
  903. UnblockProcess(Waiter, IoCompletionInterrupt, TRUE, 0);
  904. return TRUE;
  905. }
  906. Next = Next->Flink;
  907. }
  908. }
  909. RtlLeaveCriticalSection(&BlockLock);
  910. }
  911. RtlLeaveCriticalSection(&Pipe->CriticalSection);
  912. return TRUE;
  913. }
  914. }
  915. VOID
  916. NamedPipeLastClose (
  917. IN PPSX_PROCESS p,
  918. IN PSYSTEMOPENFILE SystemOpenFile
  919. )
  920. /*++
  921. Routine Description:
  922. This function is called when the last handle to a local
  923. pipe is closed. Its function is to tear down the pipe.
  924. Arguments:
  925. SystemOpenFile - Supplies the system open file describing the
  926. pipe being closed.
  927. Return Value:
  928. None.
  929. --*/
  930. {
  931. NTSTATUS st;
  932. st = NtClose(SystemOpenFile->NtIoHandle);
  933. ASSERT(NT_SUCCESS(st));
  934. }
  935. PSXIO_VECTORS NamedPipeVectors = {
  936. NamedPipeOpenNewHandle,
  937. LocalPipeNewHandle,
  938. LocalPipeClose,
  939. NamedPipeLastClose,
  940. LocalPipeIoNodeClose,
  941. LocalPipeRead,
  942. LocalPipeWrite,
  943. LocalPipeDup,
  944. LocalPipeLseek,
  945. LocalPipeStat
  946. };