1/*
2 * Copyright 2007-2013, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Copyright 2003-2010, Axel D��rfler, axeld@pinc-software.de.
4 * Distributed under the terms of the MIT License.
5 */
6
7
8#include "fifo.h"
9
10#include <limits.h>
11#include <stdio.h>
12#include <stdlib.h>
13#include <string.h>
14#include <sys/ioctl.h>
15#include <sys/stat.h>
16
17#include <new>
18
19#include <KernelExport.h>
20#include <NodeMonitor.h>
21#include <Select.h>
22
23#include <condition_variable.h>
24#include <debug_hex_dump.h>
25#include <lock.h>
26#include <select_sync_pool.h>
27#include <syscall_restart.h>
28#include <team.h>
29#include <thread.h>
30#include <util/DoublyLinkedList.h>
31#include <util/AutoLock.h>
32#include <util/ring_buffer.h>
33#include <vfs.h>
34#include <vfs_defs.h>
35#include <vm/vm.h>
36
37
38//#define TRACE_FIFO
39#ifdef TRACE_FIFO
40#	define TRACE(x...) dprintf(x)
41#else
42#	define TRACE(x...)
43#endif
44
45
46namespace fifo {
47
48
49struct file_cookie;
50class Inode;
51
52
53class RingBuffer {
54public:
55								RingBuffer();
56								~RingBuffer();
57
58			status_t			CreateBuffer();
59			void				DeleteBuffer();
60
61			ssize_t				Write(const void* buffer, size_t length,
62									bool isUser);
63			ssize_t				Read(void* buffer, size_t length, bool isUser);
64			ssize_t				Peek(size_t offset, void* buffer,
65									size_t length) const;
66
67			size_t				Readable() const;
68			size_t				Writable() const;
69
70private:
71			struct ring_buffer*	fBuffer;
72};
73
74
75class ReadRequest : public DoublyLinkedListLinkImpl<ReadRequest> {
76public:
77	ReadRequest(file_cookie* cookie)
78		:
79		fThread(thread_get_current_thread()),
80		fCookie(cookie),
81		fNotified(true)
82	{
83		B_INITIALIZE_SPINLOCK(&fLock);
84	}
85
86	void SetNotified(bool notified)
87	{
88		InterruptsSpinLocker _(fLock);
89		fNotified = notified;
90	}
91
92	void Notify(status_t status = B_OK)
93	{
94		InterruptsSpinLocker _(fLock);
95		TRACE("ReadRequest %p::Notify(), fNotified %d\n", this, fNotified);
96
97		if (!fNotified) {
98			thread_unblock(fThread, status);
99			fNotified = true;
100		}
101	}
102
103	Thread* GetThread() const
104	{
105		return fThread;
106	}
107
108	file_cookie* Cookie() const
109	{
110		return fCookie;
111	}
112
113private:
114	spinlock		fLock;
115	Thread*			fThread;
116	file_cookie*	fCookie;
117	volatile bool	fNotified;
118};
119
120
121class WriteRequest : public DoublyLinkedListLinkImpl<WriteRequest> {
122public:
123	WriteRequest(Thread* thread, size_t minimalWriteCount)
124		:
125		fThread(thread),
126		fMinimalWriteCount(minimalWriteCount)
127	{
128	}
129
130	Thread* GetThread() const
131	{
132		return fThread;
133	}
134
135	size_t MinimalWriteCount() const
136	{
137		return fMinimalWriteCount;
138	}
139
140private:
141	Thread*	fThread;
142	size_t	fMinimalWriteCount;
143};
144
145
146typedef DoublyLinkedList<ReadRequest> ReadRequestList;
147typedef DoublyLinkedList<WriteRequest> WriteRequestList;
148
149
150class Inode {
151public:
152								Inode();
153								~Inode();
154
155			status_t			InitCheck();
156
157			bool				IsActive() const { return fActive; }
158			timespec			CreationTime() const { return fCreationTime; }
159			void				SetCreationTime(timespec creationTime)
160									{ fCreationTime = creationTime; }
161			timespec			ModificationTime() const
162									{ return fModificationTime; }
163			void				SetModificationTime(timespec modificationTime)
164									{ fModificationTime = modificationTime; }
165
166			mutex*				RequestLock() { return &fRequestLock; }
167
168			status_t			WriteDataToBuffer(const void* data,
169									size_t* _length, bool nonBlocking,
170									bool isUser);
171			status_t			ReadDataFromBuffer(void* data, size_t* _length,
172									bool nonBlocking, bool isUser,
173									ReadRequest& request);
174			size_t				BytesAvailable() const
175									{ return fBuffer.Readable(); }
176			size_t				BytesWritable() const
177									{ return fBuffer.Writable(); }
178
179			void				AddReadRequest(ReadRequest& request);
180			void				RemoveReadRequest(ReadRequest& request);
181			status_t			WaitForReadRequest(ReadRequest& request);
182
183			void				NotifyBytesRead(size_t bytes);
184			void				NotifyReadDone();
185			void				NotifyBytesWritten(size_t bytes);
186			void				NotifyEndClosed(bool writer);
187
188			void				Open(int openMode);
189			void				Close(file_cookie* cookie);
190			int32				ReaderCount() const { return fReaderCount; }
191			int32				WriterCount() const { return fWriterCount; }
192
193			status_t			Select(uint8 event, selectsync* sync,
194									int openMode);
195			status_t			Deselect(uint8 event, selectsync* sync,
196									int openMode);
197
198			void				Dump(bool dumpData) const;
199	static	int					Dump(int argc, char** argv);
200
201private:
202			timespec			fCreationTime;
203			timespec			fModificationTime;
204
205			RingBuffer			fBuffer;
206
207			ReadRequestList		fReadRequests;
208			WriteRequestList	fWriteRequests;
209
210			mutex				fRequestLock;
211
212			ConditionVariable	fWriteCondition;
213
214			int32				fReaderCount;
215			int32				fWriterCount;
216			bool				fActive;
217
218			select_sync_pool*	fReadSelectSyncPool;
219			select_sync_pool*	fWriteSelectSyncPool;
220};
221
222
223class FIFOInode : public Inode {
224public:
225	FIFOInode(fs_vnode* vnode)
226		:
227		Inode(),
228		fSuperVnode(*vnode)
229	{
230	}
231
232	fs_vnode*	SuperVnode() { return &fSuperVnode; }
233
234private:
235	fs_vnode	fSuperVnode;
236};
237
238
239struct file_cookie {
240	int	open_mode;
241			// guarded by Inode::fRequestLock
242
243	void SetNonBlocking(bool nonBlocking)
244	{
245		if (nonBlocking)
246			open_mode |= O_NONBLOCK;
247		else
248			open_mode &= ~(int)O_NONBLOCK;
249	}
250};
251
252
253// #pragma mark -
254
255
256RingBuffer::RingBuffer()
257	:
258	fBuffer(NULL)
259{
260}
261
262
263RingBuffer::~RingBuffer()
264{
265	DeleteBuffer();
266}
267
268
269status_t
270RingBuffer::CreateBuffer()
271{
272	if (fBuffer != NULL)
273		return B_OK;
274
275	fBuffer = create_ring_buffer(VFS_FIFO_BUFFER_CAPACITY);
276	return fBuffer != NULL ? B_OK : B_NO_MEMORY;
277}
278
279
280void
281RingBuffer::DeleteBuffer()
282{
283	if (fBuffer != NULL) {
284		delete_ring_buffer(fBuffer);
285		fBuffer = NULL;
286	}
287}
288
289
290inline ssize_t
291RingBuffer::Write(const void* buffer, size_t length, bool isUser)
292{
293	if (fBuffer == NULL)
294		return B_NO_MEMORY;
295	if (isUser && !IS_USER_ADDRESS(buffer))
296		return B_BAD_ADDRESS;
297
298	return isUser
299		? ring_buffer_user_write(fBuffer, (const uint8*)buffer, length)
300		: ring_buffer_write(fBuffer, (const uint8*)buffer, length);
301}
302
303
304inline ssize_t
305RingBuffer::Read(void* buffer, size_t length, bool isUser)
306{
307	if (fBuffer == NULL)
308		return B_NO_MEMORY;
309	if (isUser && !IS_USER_ADDRESS(buffer))
310		return B_BAD_ADDRESS;
311
312	return isUser
313		? ring_buffer_user_read(fBuffer, (uint8*)buffer, length)
314		: ring_buffer_read(fBuffer, (uint8*)buffer, length);
315}
316
317
318inline ssize_t
319RingBuffer::Peek(size_t offset, void* buffer, size_t length) const
320{
321	if (fBuffer == NULL)
322		return B_NO_MEMORY;
323
324	return ring_buffer_peek(fBuffer, offset, (uint8*)buffer, length);
325}
326
327
328inline size_t
329RingBuffer::Readable() const
330{
331	return fBuffer != NULL ? ring_buffer_readable(fBuffer) : 0;
332}
333
334
335inline size_t
336RingBuffer::Writable() const
337{
338	return fBuffer != NULL ? ring_buffer_writable(fBuffer) : 0;
339}
340
341
342//	#pragma mark -
343
344
345Inode::Inode()
346	:
347	fReadRequests(),
348	fWriteRequests(),
349	fReaderCount(0),
350	fWriterCount(0),
351	fActive(false),
352	fReadSelectSyncPool(NULL),
353	fWriteSelectSyncPool(NULL)
354{
355	fWriteCondition.Publish(this, "pipe");
356	mutex_init(&fRequestLock, "pipe request");
357
358	bigtime_t time = real_time_clock();
359	fModificationTime.tv_sec = time / 1000000;
360	fModificationTime.tv_nsec = (time % 1000000) * 1000;
361	fCreationTime = fModificationTime;
362}
363
364
365Inode::~Inode()
366{
367	fWriteCondition.Unpublish();
368	mutex_destroy(&fRequestLock);
369}
370
371
372status_t
373Inode::InitCheck()
374{
375	return B_OK;
376}
377
378
379/*!	Writes the specified data bytes to the inode's ring buffer. The
380	request lock must be held when calling this method.
381	Notifies readers if necessary, so that blocking readers will get started.
382	Returns B_OK for success, B_BAD_ADDRESS if copying from the buffer failed,
383	and various semaphore errors (like B_WOULD_BLOCK in non-blocking mode). If
384	the returned length is > 0, the returned error code can be ignored.
385*/
386status_t
387Inode::WriteDataToBuffer(const void* _data, size_t* _length, bool nonBlocking,
388	bool isUser)
389{
390	const uint8* data = (const uint8*)_data;
391	size_t dataSize = *_length;
392	size_t& written = *_length;
393	written = 0;
394
395	TRACE("Inode %p::WriteDataToBuffer(data = %p, bytes = %zu)\n", this, data,
396		dataSize);
397
398	// A request up to VFS_FIFO_ATOMIC_WRITE_SIZE bytes shall not be
399	// interleaved with other writer's data.
400	size_t minToWrite = 1;
401	if (dataSize <= VFS_FIFO_ATOMIC_WRITE_SIZE)
402		minToWrite = dataSize;
403
404	while (dataSize > 0) {
405		// Wait until enough space in the buffer is available.
406		while (!fActive
407				|| (fBuffer.Writable() < minToWrite && fReaderCount > 0)) {
408			if (nonBlocking)
409				return B_WOULD_BLOCK;
410
411			ConditionVariableEntry entry;
412			entry.Add(this);
413
414			WriteRequest request(thread_get_current_thread(), minToWrite);
415			fWriteRequests.Add(&request);
416
417			mutex_unlock(&fRequestLock);
418			status_t status = entry.Wait(B_CAN_INTERRUPT);
419			mutex_lock(&fRequestLock);
420
421			fWriteRequests.Remove(&request);
422
423			if (status != B_OK)
424				return status;
425		}
426
427		// write only as long as there are readers left
428		if (fActive && fReaderCount == 0) {
429			if (written == 0)
430				send_signal(find_thread(NULL), SIGPIPE);
431			return EPIPE;
432		}
433
434		// write as much as we can
435
436		size_t toWrite = (fActive ? fBuffer.Writable() : 0);
437		if (toWrite > dataSize)
438			toWrite = dataSize;
439
440		if (toWrite > 0) {
441			ssize_t bytesWritten = fBuffer.Write(data, toWrite, isUser);
442			if (bytesWritten < 0)
443				return bytesWritten;
444		}
445
446		data += toWrite;
447		dataSize -= toWrite;
448		written += toWrite;
449
450		NotifyBytesWritten(toWrite);
451	}
452
453	return B_OK;
454}
455
456
457status_t
458Inode::ReadDataFromBuffer(void* data, size_t* _length, bool nonBlocking,
459	bool isUser, ReadRequest& request)
460{
461	size_t dataSize = *_length;
462	*_length = 0;
463
464	// wait until our request is first in queue
465	status_t error;
466	if (fReadRequests.Head() != &request) {
467		if (nonBlocking)
468			return B_WOULD_BLOCK;
469
470		TRACE("Inode %p::%s(): wait for request %p to become the first "
471			"request.\n", this, __FUNCTION__, &request);
472
473		error = WaitForReadRequest(request);
474		if (error != B_OK)
475			return error;
476	}
477
478	// wait until data are available
479	while (fBuffer.Readable() == 0) {
480		if (nonBlocking)
481			return B_WOULD_BLOCK;
482
483		if (fActive && fWriterCount == 0)
484			return B_OK;
485
486		TRACE("Inode %p::%s(): wait for data, request %p\n", this, __FUNCTION__,
487			&request);
488
489		error = WaitForReadRequest(request);
490		if (error != B_OK)
491			return error;
492	}
493
494	// read as much as we can
495	size_t toRead = fBuffer.Readable();
496	if (toRead > dataSize)
497		toRead = dataSize;
498
499	ssize_t bytesRead = fBuffer.Read(data, toRead, isUser);
500	if (bytesRead < 0)
501		return bytesRead;
502
503	NotifyBytesRead(toRead);
504
505	*_length = toRead;
506
507	return B_OK;
508}
509
510
511void
512Inode::AddReadRequest(ReadRequest& request)
513{
514	fReadRequests.Add(&request);
515}
516
517
518void
519Inode::RemoveReadRequest(ReadRequest& request)
520{
521	fReadRequests.Remove(&request);
522}
523
524
525status_t
526Inode::WaitForReadRequest(ReadRequest& request)
527{
528	// add the entry to wait on
529	thread_prepare_to_block(thread_get_current_thread(), B_CAN_INTERRUPT,
530		THREAD_BLOCK_TYPE_OTHER, "fifo read request");
531
532	request.SetNotified(false);
533
534	// wait
535	mutex_unlock(&fRequestLock);
536	status_t status = thread_block();
537
538	// Before going to lock again, we need to make sure no one tries to
539	// unblock us. Otherwise that would screw with mutex_lock().
540	request.SetNotified(true);
541
542	mutex_lock(&fRequestLock);
543
544	return status;
545}
546
547
548void
549Inode::NotifyBytesRead(size_t bytes)
550{
551	// notify writer, if something can be written now
552	size_t writable = fBuffer.Writable();
553	if (bytes > 0) {
554		// notify select()ors only, if nothing was writable before
555		if (writable == bytes) {
556			if (fWriteSelectSyncPool)
557				notify_select_event_pool(fWriteSelectSyncPool, B_SELECT_WRITE);
558		}
559
560		// If any of the waiting writers has a minimal write count that has
561		// now become satisfied, we notify all of them (condition variables
562		// don't support doing that selectively).
563		WriteRequest* request;
564		WriteRequestList::Iterator iterator = fWriteRequests.GetIterator();
565		while ((request = iterator.Next()) != NULL) {
566			size_t minWriteCount = request->MinimalWriteCount();
567			if (minWriteCount > 0 && minWriteCount <= writable
568					&& minWriteCount > writable - bytes) {
569				fWriteCondition.NotifyAll();
570				break;
571			}
572		}
573	}
574}
575
576
577void
578Inode::NotifyReadDone()
579{
580	// notify next reader, if there's still something to be read
581	if (fBuffer.Readable() > 0) {
582		if (ReadRequest* request = fReadRequests.First())
583			request->Notify();
584	}
585}
586
587
588void
589Inode::NotifyBytesWritten(size_t bytes)
590{
591	// notify reader, if something can be read now
592	if (bytes > 0 && fBuffer.Readable() == bytes) {
593		if (fReadSelectSyncPool)
594			notify_select_event_pool(fReadSelectSyncPool, B_SELECT_READ);
595
596		if (ReadRequest* request = fReadRequests.First())
597			request->Notify();
598	}
599}
600
601
602void
603Inode::NotifyEndClosed(bool writer)
604{
605	TRACE("Inode %p::%s(%s)\n", this, __FUNCTION__,
606		writer ? "writer" : "reader");
607
608	if (writer) {
609		// Our last writer has been closed; if the pipe
610		// contains no data, unlock all waiting readers
611		TRACE("  buffer readable: %zu\n", fBuffer.Readable());
612		if (fBuffer.Readable() == 0) {
613			ReadRequestList::Iterator iterator = fReadRequests.GetIterator();
614			while (ReadRequest* request = iterator.Next())
615				request->Notify();
616
617			if (fReadSelectSyncPool)
618				notify_select_event_pool(fReadSelectSyncPool, B_SELECT_READ);
619		}
620	} else {
621		// Last reader is gone. Wake up all writers.
622		fWriteCondition.NotifyAll();
623
624		if (fWriteSelectSyncPool) {
625			notify_select_event_pool(fWriteSelectSyncPool, B_SELECT_WRITE);
626			notify_select_event_pool(fWriteSelectSyncPool, B_SELECT_ERROR);
627		}
628	}
629}
630
631
632void
633Inode::Open(int openMode)
634{
635	MutexLocker locker(RequestLock());
636
637	if ((openMode & O_ACCMODE) == O_WRONLY)
638		fWriterCount++;
639
640	if ((openMode & O_ACCMODE) == O_RDONLY || (openMode & O_ACCMODE) == O_RDWR)
641		fReaderCount++;
642
643	if (fReaderCount > 0 && fWriterCount > 0) {
644		TRACE("Inode %p::Open(): fifo becomes active\n", this);
645		fBuffer.CreateBuffer();
646		fActive = true;
647
648		// notify all waiting writers that they can start
649		if (fWriteSelectSyncPool)
650			notify_select_event_pool(fWriteSelectSyncPool, B_SELECT_WRITE);
651		fWriteCondition.NotifyAll();
652	}
653}
654
655
656void
657Inode::Close(file_cookie* cookie)
658{
659	TRACE("Inode %p::Close(openMode = %d)\n", this, openMode);
660
661	MutexLocker locker(RequestLock());
662
663	int openMode = cookie->open_mode;
664
665	// Notify all currently reading file descriptors
666	ReadRequestList::Iterator iterator = fReadRequests.GetIterator();
667	while (ReadRequest* request = iterator.Next()) {
668		if (request->Cookie() == cookie)
669			request->Notify(B_FILE_ERROR);
670	}
671
672	if ((openMode & O_ACCMODE) == O_WRONLY && --fWriterCount == 0)
673		NotifyEndClosed(true);
674
675	if ((openMode & O_ACCMODE) == O_RDONLY
676		|| (openMode & O_ACCMODE) == O_RDWR) {
677		if (--fReaderCount == 0)
678			NotifyEndClosed(false);
679	}
680
681	if (fWriterCount == 0) {
682		// Notify any still reading writers to stop
683		// TODO: This only works reliable if there is only one writer - we could
684		// do the same thing done for the read requests.
685		fWriteCondition.NotifyAll(B_FILE_ERROR);
686	}
687
688	if (fReaderCount == 0 && fWriterCount == 0) {
689		fActive = false;
690		fBuffer.DeleteBuffer();
691	}
692}
693
694
695status_t
696Inode::Select(uint8 event, selectsync* sync, int openMode)
697{
698	bool writer = true;
699	select_sync_pool** pool;
700	if ((openMode & O_RWMASK) == O_RDONLY) {
701		pool = &fReadSelectSyncPool;
702		writer = false;
703	} else if ((openMode & O_RWMASK) == O_WRONLY) {
704		pool = &fWriteSelectSyncPool;
705	} else
706		return B_NOT_ALLOWED;
707
708	if (add_select_sync_pool_entry(pool, sync, event) != B_OK)
709		return B_ERROR;
710
711	// signal right away, if the condition holds already
712	if (writer) {
713		if ((event == B_SELECT_WRITE
714				&& (fBuffer.Writable() > 0 || fReaderCount == 0))
715			|| (event == B_SELECT_ERROR && fReaderCount == 0)) {
716			return notify_select_event(sync, event);
717		}
718	} else {
719		if (event == B_SELECT_READ
720				&& (fBuffer.Readable() > 0 || fWriterCount == 0)) {
721			return notify_select_event(sync, event);
722		}
723	}
724
725	return B_OK;
726}
727
728
729status_t
730Inode::Deselect(uint8 event, selectsync* sync, int openMode)
731{
732	select_sync_pool** pool;
733	if ((openMode & O_RWMASK) == O_RDONLY) {
734		pool = &fReadSelectSyncPool;
735	} else if ((openMode & O_RWMASK) == O_WRONLY) {
736		pool = &fWriteSelectSyncPool;
737	} else
738		return B_NOT_ALLOWED;
739
740	remove_select_sync_pool_entry(pool, sync, event);
741	return B_OK;
742}
743
744
745void
746Inode::Dump(bool dumpData) const
747{
748	kprintf("FIFO %p\n", this);
749	kprintf("  active:        %s\n", fActive ? "true" : "false");
750	kprintf("  readers:       %" B_PRId32 "\n", fReaderCount);
751	kprintf("  writers:       %" B_PRId32 "\n", fWriterCount);
752
753	if (!fReadRequests.IsEmpty()) {
754		kprintf(" pending readers:\n");
755		for (ReadRequestList::ConstIterator it = fReadRequests.GetIterator();
756			ReadRequest* request = it.Next();) {
757			kprintf("    %p: thread %" B_PRId32 ", cookie: %p\n", request,
758				request->GetThread()->id, request->Cookie());
759		}
760	}
761
762	if (!fWriteRequests.IsEmpty()) {
763		kprintf(" pending writers:\n");
764		for (WriteRequestList::ConstIterator it = fWriteRequests.GetIterator();
765			WriteRequest* request = it.Next();) {
766			kprintf("    %p:  thread %" B_PRId32 ", min count: %zu\n", request,
767				request->GetThread()->id, request->MinimalWriteCount());
768		}
769	}
770
771	kprintf("  %zu bytes buffered\n", fBuffer.Readable());
772
773	if (dumpData && fBuffer.Readable() > 0) {
774		struct DataProvider : BKernel::HexDumpDataProvider {
775			DataProvider(const RingBuffer& buffer)
776				:
777				fBuffer(buffer),
778				fOffset(0)
779			{
780			}
781
782			virtual bool HasMoreData() const
783			{
784				return fOffset < fBuffer.Readable();
785			}
786
787			virtual uint8 NextByte()
788			{
789				uint8 byte = '\0';
790				if (fOffset < fBuffer.Readable()) {
791					fBuffer.Peek(fOffset, &byte, 1);
792					fOffset++;
793				}
794				return byte;
795			}
796
797			virtual bool GetAddressString(char* buffer, size_t bufferSize) const
798			{
799				snprintf(buffer, bufferSize, "    %4zx", fOffset);
800				return true;
801			}
802
803		private:
804			const RingBuffer&	fBuffer;
805			size_t				fOffset;
806		};
807
808		DataProvider dataProvider(fBuffer);
809		BKernel::print_hex_dump(dataProvider, fBuffer.Readable());
810	}
811}
812
813
814/*static*/ int
815Inode::Dump(int argc, char** argv)
816{
817	bool dumpData = false;
818	int argi = 1;
819	if (argi < argc && strcmp(argv[argi], "-d") == 0) {
820		dumpData = true;
821		argi++;
822	}
823
824	if (argi >= argc || argi + 2 < argc) {
825		print_debugger_command_usage(argv[0]);
826		return 0;
827	}
828
829	Inode* node = (Inode*)parse_expression(argv[argi]);
830	if (IS_USER_ADDRESS(node)) {
831		kprintf("invalid FIFO address\n");
832		return 0;
833	}
834
835	node->Dump(dumpData);
836	return 0;
837}
838
839
840//	#pragma mark - vnode API
841
842
843static status_t
844fifo_put_vnode(fs_volume* volume, fs_vnode* vnode, bool reenter)
845{
846	FIFOInode* fifo = (FIFOInode*)vnode->private_node;
847	fs_vnode* superVnode = fifo->SuperVnode();
848
849	status_t error = B_OK;
850	if (superVnode->ops->put_vnode != NULL)
851		error = superVnode->ops->put_vnode(volume, superVnode, reenter);
852
853	delete fifo;
854
855	return error;
856}
857
858
859static status_t
860fifo_remove_vnode(fs_volume* volume, fs_vnode* vnode, bool reenter)
861{
862	FIFOInode* fifo = (FIFOInode*)vnode->private_node;
863	fs_vnode* superVnode = fifo->SuperVnode();
864
865	status_t error = B_OK;
866	if (superVnode->ops->remove_vnode != NULL)
867		error = superVnode->ops->remove_vnode(volume, superVnode, reenter);
868
869	delete fifo;
870
871	return error;
872}
873
874
875static status_t
876fifo_open(fs_volume* _volume, fs_vnode* _node, int openMode,
877	void** _cookie)
878{
879	Inode* inode = (Inode*)_node->private_node;
880
881	TRACE("fifo_open(): node = %p, openMode = %d\n", inode, openMode);
882
883	file_cookie* cookie = (file_cookie*)malloc(sizeof(file_cookie));
884	if (cookie == NULL)
885		return B_NO_MEMORY;
886
887	TRACE("  open cookie = %p\n", cookie);
888	cookie->open_mode = openMode;
889	inode->Open(openMode);
890
891	*_cookie = (void*)cookie;
892
893	return B_OK;
894}
895
896
897static status_t
898fifo_close(fs_volume* volume, fs_vnode* vnode, void* _cookie)
899{
900	file_cookie* cookie = (file_cookie*)_cookie;
901	FIFOInode* fifo = (FIFOInode*)vnode->private_node;
902
903	fifo->Close(cookie);
904
905	return B_OK;
906}
907
908
909static status_t
910fifo_free_cookie(fs_volume* _volume, fs_vnode* _node, void* _cookie)
911{
912	file_cookie* cookie = (file_cookie*)_cookie;
913
914	TRACE("fifo_freecookie: entry vnode %p, cookie %p\n", _node, _cookie);
915
916	free(cookie);
917
918	return B_OK;
919}
920
921
922static status_t
923fifo_fsync(fs_volume* _volume, fs_vnode* _node)
924{
925	return B_OK;
926}
927
928
929static status_t
930fifo_read(fs_volume* _volume, fs_vnode* _node, void* _cookie,
931	off_t /*pos*/, void* buffer, size_t* _length)
932{
933	file_cookie* cookie = (file_cookie*)_cookie;
934	Inode* inode = (Inode*)_node->private_node;
935
936	TRACE("fifo_read(vnode = %p, cookie = %p, length = %lu, mode = %d)\n",
937		inode, cookie, *_length, cookie->open_mode);
938
939	MutexLocker locker(inode->RequestLock());
940
941	if ((cookie->open_mode & O_RWMASK) != O_RDONLY)
942		return B_NOT_ALLOWED;
943
944	if (inode->IsActive() && inode->WriterCount() == 0) {
945		// as long there is no writer, and the pipe is empty,
946		// we always just return 0 to indicate end of file
947		if (inode->BytesAvailable() == 0) {
948			*_length = 0;
949			return B_OK;
950		}
951	}
952
953	// issue read request
954
955	ReadRequest request(cookie);
956	inode->AddReadRequest(request);
957
958	TRACE("  issue read request %p\n", &request);
959
960	size_t length = *_length;
961	status_t status = inode->ReadDataFromBuffer(buffer, &length,
962		(cookie->open_mode & O_NONBLOCK) != 0, is_called_via_syscall(),
963		request);
964
965	inode->RemoveReadRequest(request);
966	inode->NotifyReadDone();
967
968	TRACE("  done reading request %p, length %zu\n", &request, length);
969
970	if (length > 0)
971		status = B_OK;
972
973	*_length = length;
974	return status;
975}
976
977
978static status_t
979fifo_write(fs_volume* _volume, fs_vnode* _node, void* _cookie,
980	off_t /*pos*/, const void* buffer, size_t* _length)
981{
982	file_cookie* cookie = (file_cookie*)_cookie;
983	Inode* inode = (Inode*)_node->private_node;
984
985	TRACE("fifo_write(vnode = %p, cookie = %p, length = %lu)\n",
986		_node, cookie, *_length);
987
988	MutexLocker locker(inode->RequestLock());
989
990	if ((cookie->open_mode & O_RWMASK) != O_WRONLY)
991		return B_NOT_ALLOWED;
992
993	size_t length = *_length;
994	if (length == 0)
995		return B_OK;
996
997	// copy data into ring buffer
998	status_t status = inode->WriteDataToBuffer(buffer, &length,
999		(cookie->open_mode & O_NONBLOCK) != 0, is_called_via_syscall());
1000
1001	if (length > 0)
1002		status = B_OK;
1003
1004	*_length = length;
1005	return status;
1006}
1007
1008
1009static status_t
1010fifo_read_stat(fs_volume* volume, fs_vnode* vnode, struct ::stat* st)
1011{
1012	FIFOInode* fifo = (FIFOInode*)vnode->private_node;
1013	fs_vnode* superVnode = fifo->SuperVnode();
1014
1015	if (superVnode->ops->read_stat == NULL)
1016		return B_BAD_VALUE;
1017
1018	status_t error = superVnode->ops->read_stat(volume, superVnode, st);
1019	if (error != B_OK)
1020		return error;
1021
1022
1023	MutexLocker locker(fifo->RequestLock());
1024
1025	st->st_size = fifo->BytesAvailable();
1026
1027	st->st_blksize = 4096;
1028
1029	// TODO: Just pass the changes to our modification time on to the super node.
1030	st->st_atim.tv_sec = time(NULL);
1031	st->st_atim.tv_nsec = 0;
1032	st->st_mtim = st->st_ctim = fifo->ModificationTime();
1033
1034	return B_OK;
1035}
1036
1037
1038static status_t
1039fifo_write_stat(fs_volume* volume, fs_vnode* vnode, const struct ::stat* st,
1040	uint32 statMask)
1041{
1042	// we cannot change the size of anything
1043	if ((statMask & B_STAT_SIZE) != 0)
1044		return B_BAD_VALUE;
1045
1046	FIFOInode* fifo = (FIFOInode*)vnode->private_node;
1047	fs_vnode* superVnode = fifo->SuperVnode();
1048
1049	if (superVnode->ops->write_stat == NULL)
1050		return B_BAD_VALUE;
1051
1052	status_t error = superVnode->ops->write_stat(volume, superVnode, st,
1053		statMask);
1054	if (error != B_OK)
1055		return error;
1056
1057	return B_OK;
1058}
1059
1060
1061static status_t
1062fifo_ioctl(fs_volume* _volume, fs_vnode* _node, void* _cookie, uint32 op,
1063	void* buffer, size_t length)
1064{
1065	file_cookie* cookie = (file_cookie*)_cookie;
1066	Inode* inode = (Inode*)_node->private_node;
1067
1068	TRACE("fifo_ioctl: vnode %p, cookie %p, op %ld, buf %p, len %ld\n",
1069		_vnode, _cookie, op, buffer, length);
1070
1071	switch (op) {
1072		case FIONBIO:
1073		{
1074			if (buffer == NULL)
1075				return B_BAD_VALUE;
1076
1077			int value;
1078			if (is_called_via_syscall()) {
1079				if (!IS_USER_ADDRESS(buffer)
1080					|| user_memcpy(&value, buffer, sizeof(int)) != B_OK) {
1081					return B_BAD_ADDRESS;
1082				}
1083			} else
1084				value = *(int*)buffer;
1085
1086			MutexLocker locker(inode->RequestLock());
1087			cookie->SetNonBlocking(value != 0);
1088			return B_OK;
1089		}
1090
1091		case FIONREAD:
1092		{
1093			if (buffer == NULL)
1094				return B_BAD_VALUE;
1095
1096			MutexLocker locker(inode->RequestLock());
1097			int available = (int)inode->BytesAvailable();
1098			locker.Unlock();
1099
1100			if (is_called_via_syscall()) {
1101				if (!IS_USER_ADDRESS(buffer)
1102					|| user_memcpy(buffer, &available, sizeof(available))
1103						!= B_OK) {
1104					return B_BAD_ADDRESS;
1105				}
1106			} else
1107				*(int*)buffer = available;
1108
1109			return B_OK;
1110		}
1111
1112		case B_SET_BLOCKING_IO:
1113		case B_SET_NONBLOCKING_IO:
1114		{
1115			MutexLocker locker(inode->RequestLock());
1116			cookie->SetNonBlocking(op == B_SET_NONBLOCKING_IO);
1117			return B_OK;
1118		}
1119	}
1120
1121	return EINVAL;
1122}
1123
1124
1125static status_t
1126fifo_set_flags(fs_volume* _volume, fs_vnode* _node, void* _cookie,
1127	int flags)
1128{
1129	Inode* inode = (Inode*)_node->private_node;
1130	file_cookie* cookie = (file_cookie*)_cookie;
1131
1132	TRACE("fifo_set_flags(vnode = %p, flags = %x)\n", _vnode, flags);
1133
1134	MutexLocker locker(inode->RequestLock());
1135	cookie->open_mode = (cookie->open_mode & ~(O_APPEND | O_NONBLOCK)) | flags;
1136	return B_OK;
1137}
1138
1139
1140static status_t
1141fifo_select(fs_volume* _volume, fs_vnode* _node, void* _cookie,
1142	uint8 event, selectsync* sync)
1143{
1144	file_cookie* cookie = (file_cookie*)_cookie;
1145
1146	TRACE("fifo_select(vnode = %p)\n", _node);
1147	Inode* inode = (Inode*)_node->private_node;
1148	if (!inode)
1149		return B_ERROR;
1150
1151	MutexLocker locker(inode->RequestLock());
1152	return inode->Select(event, sync, cookie->open_mode);
1153}
1154
1155
1156static status_t
1157fifo_deselect(fs_volume* _volume, fs_vnode* _node, void* _cookie,
1158	uint8 event, selectsync* sync)
1159{
1160	file_cookie* cookie = (file_cookie*)_cookie;
1161
1162	TRACE("fifo_deselect(vnode = %p)\n", _node);
1163	Inode* inode = (Inode*)_node->private_node;
1164	if (inode == NULL)
1165		return B_ERROR;
1166
1167	MutexLocker locker(inode->RequestLock());
1168	return inode->Deselect(event, sync, cookie->open_mode);
1169}
1170
1171
1172static bool
1173fifo_can_page(fs_volume* _volume, fs_vnode* _node, void* cookie)
1174{
1175	return false;
1176}
1177
1178
1179static status_t
1180fifo_read_pages(fs_volume* _volume, fs_vnode* _node, void* cookie, off_t pos,
1181	const iovec* vecs, size_t count, size_t* _numBytes)
1182{
1183	return B_NOT_ALLOWED;
1184}
1185
1186
1187static status_t
1188fifo_write_pages(fs_volume* _volume, fs_vnode* _node, void* cookie,
1189	off_t pos, const iovec* vecs, size_t count, size_t* _numBytes)
1190{
1191	return B_NOT_ALLOWED;
1192}
1193
1194
1195static status_t
1196fifo_get_super_vnode(fs_volume* volume, fs_vnode* vnode, fs_volume* superVolume,
1197	fs_vnode* _superVnode)
1198{
1199	FIFOInode* fifo = (FIFOInode*)vnode->private_node;
1200	fs_vnode* superVnode = fifo->SuperVnode();
1201
1202	if (superVnode->ops->get_super_vnode != NULL) {
1203		return superVnode->ops->get_super_vnode(volume, superVnode, superVolume,
1204			_superVnode);
1205	}
1206
1207	*_superVnode = *superVnode;
1208
1209	return B_OK;
1210}
1211
1212
1213static fs_vnode_ops sFIFOVnodeOps = {
1214	NULL,	// lookup
1215	NULL,	// get_vnode_name
1216					// TODO: This is suboptimal! We'd need to forward the
1217					// super node's hook, if it has got one.
1218
1219	&fifo_put_vnode,
1220	&fifo_remove_vnode,
1221
1222	&fifo_can_page,
1223	&fifo_read_pages,
1224	&fifo_write_pages,
1225
1226	NULL,	// io()
1227	NULL,	// cancel_io()
1228
1229	NULL,	// get_file_map
1230
1231	/* common */
1232	&fifo_ioctl,
1233	&fifo_set_flags,
1234	&fifo_select,
1235	&fifo_deselect,
1236	&fifo_fsync,
1237
1238	NULL,	// fs_read_link
1239	NULL,	// fs_symlink
1240	NULL,	// fs_link
1241	NULL,	// unlink
1242	NULL,	// rename
1243
1244	NULL,	// fs_access()
1245	&fifo_read_stat,
1246	&fifo_write_stat,
1247	NULL,
1248
1249	/* file */
1250	NULL,	// create()
1251	&fifo_open,
1252	&fifo_close,
1253	&fifo_free_cookie,
1254	&fifo_read,
1255	&fifo_write,
1256
1257	/* directory */
1258	NULL,	// create_dir
1259	NULL,	// remove_dir
1260	NULL,	// open_dir
1261	NULL,	// close_dir
1262	NULL,	// free_dir_cookie
1263	NULL,	// read_dir
1264	NULL,	// rewind_dir
1265
1266	/* attribute directory operations */
1267	NULL,	// open_attr_dir
1268	NULL,	// close_attr_dir
1269	NULL,	// free_attr_dir_cookie
1270	NULL,	// read_attr_dir
1271	NULL,	// rewind_attr_dir
1272
1273	/* attribute operations */
1274	NULL,	// create_attr
1275	NULL,	// open_attr
1276	NULL,	// close_attr
1277	NULL,	// free_attr_cookie
1278	NULL,	// read_attr
1279	NULL,	// write_attr
1280
1281	NULL,	// read_attr_stat
1282	NULL,	// write_attr_stat
1283	NULL,	// rename_attr
1284	NULL,	// remove_attr
1285
1286	/* support for node and FS layers */
1287	NULL,	// create_special_node
1288	&fifo_get_super_vnode,
1289};
1290
1291
1292}	// namespace fifo
1293
1294
1295using namespace fifo;
1296
1297
1298// #pragma mark -
1299
1300
1301status_t
1302create_fifo_vnode(fs_volume* superVolume, fs_vnode* vnode)
1303{
1304	FIFOInode* fifo = new(std::nothrow) FIFOInode(vnode);
1305	if (fifo == NULL)
1306		return B_NO_MEMORY;
1307
1308	status_t status = fifo->InitCheck();
1309	if (status != B_OK) {
1310		delete fifo;
1311		return status;
1312	}
1313
1314	vnode->private_node = fifo;
1315	vnode->ops = &sFIFOVnodeOps;
1316
1317	return B_OK;
1318}
1319
1320
1321void
1322fifo_init()
1323{
1324	add_debugger_command_etc("fifo", &Inode::Dump,
1325		"Print info about the specified FIFO node",
1326		"[ \"-d\" ] <address>\n"
1327		"Prints information about the FIFO node specified by address\n"
1328		"<address>. If \"-d\" is given, the data in the FIFO's ring buffer\n"
1329		"hexdumped as well.\n",
1330		0);
1331}
1332