1// UserlandFS.cpp
2
3#include <KernelExport.h>
4
5#include "AutoLocker.h"
6#include "Compatibility.h"
7#include "Debug.h"
8#include "DispatcherDefs.h"
9#include "FileSystem.h"
10#include "KernelDebug.h"
11#include "RequestPort.h"
12#include "Requests.h"
13#include "UserlandFS.h"
14
15typedef AutoLocker<UserlandFS::FileSystemMap> FileSystemLocker;
16
17UserlandFS* volatile UserlandFS::sUserlandFS = NULL;
18spinlock UserlandFS::sUserlandFSLock = 0;
19vint32 UserlandFS::sMountedFileSystems = 0;
20
21// constructor
22UserlandFS::UserlandFS()
23	: LazyInitializable(),
24	  fPort(NULL),
25	  fFileSystems(NULL),
26	  fDebuggerCommandsAdded(false)
27{
28	// beware what you do here: the caller holds a spin lock
29}
30
31// destructor
32UserlandFS::~UserlandFS()
33{
34PRINT(("UserlandFS::~UserlandFS()\n"))
35	if (fPort) {
36		// send a disconnect request
37		RequestAllocator allocator(fPort->GetPort());
38		UFSDisconnectRequest* request;
39		if (AllocateRequest(allocator, &request) == B_OK) {
40			if (fPort->SendRequest(&allocator) != B_OK)
41				PRINT(("  failed to send disconnect request\n"));
42		} else
43			PRINT(("  failed to allocate disconnect request\n"));
44		delete fPort;
45	} else
46		PRINT(("  no port\n"));
47
48	delete fFileSystems;
49	if (fDebuggerCommandsAdded)
50		KernelDebug::RemoveDebuggerCommands();
51}
52
53// RegisterUserlandFS
54status_t
55UserlandFS::RegisterUserlandFS(UserlandFS** _userlandFS)
56{
57	// first check, if there's already an instance
58	bool create = false;
59
60	cpu_status cpuStatus = disable_interrupts();
61	acquire_spinlock(&sUserlandFSLock);
62
63	if (sUserlandFS)
64		sMountedFileSystems++;
65	else
66		create = true;
67
68	release_spinlock(&sUserlandFSLock);
69	restore_interrupts(cpuStatus);
70
71	// if there's not, create a new
72	status_t error = B_OK;
73	if (create) {
74		// first create an instance
75		// Note, that we can't even construct a LazyInitializable with a
76		// spinlock being held, since it allocates a semaphore, which may
77		// allocate memory, which will acquire a semaphore.
78		UserlandFS* userlandFS = new(nothrow) UserlandFS;
79		if (userlandFS) {
80			// now set the instance unless someone else beat us to it
81			bool deleteInstance = false;
82
83			cpu_status cpuStatus = disable_interrupts();
84			acquire_spinlock(&sUserlandFSLock);
85
86			sMountedFileSystems++;
87			if (sUserlandFS)
88				deleteInstance = true;
89			else
90				sUserlandFS = userlandFS;
91
92			release_spinlock(&sUserlandFSLock);
93			restore_interrupts(cpuStatus);
94
95			// delete the new instance, if there was one already
96			if (deleteInstance)
97				delete userlandFS;
98		} else
99			error = B_NO_MEMORY;
100	}
101	if (error != B_OK)
102		return error;
103
104	// init the thing, if necessary
105	error = sUserlandFS->Access();
106	if (error == B_OK)
107		*_userlandFS = sUserlandFS;
108	else
109		UnregisterUserlandFS();
110	return error;
111}
112
113// UnregisterUserlandFS
114void
115UserlandFS::UnregisterUserlandFS()
116{
117	cpu_status cpuStatus = disable_interrupts();
118	acquire_spinlock(&sUserlandFSLock);
119
120	--sMountedFileSystems;
121	UserlandFS* userlandFS = NULL;
122	if (sMountedFileSystems == 0 && sUserlandFS) {
123		userlandFS = sUserlandFS;
124		sUserlandFS = NULL;
125	}
126
127	release_spinlock(&sUserlandFSLock);
128	restore_interrupts(cpuStatus);
129
130	// delete, if the last FS has been unmounted
131	if (userlandFS) {
132		userlandFS->~UserlandFS();
133		delete[] (uint8*)userlandFS;
134	}
135}
136
137// GetUserlandFS
138UserlandFS*
139UserlandFS::GetUserlandFS()
140{
141	return sUserlandFS;
142}
143
144// RegisterFileSystem
145status_t
146UserlandFS::RegisterFileSystem(const char* name, FileSystem** _fileSystem)
147{
148	// check initialization and parameters
149	if (InitCheck() != B_OK)
150		return InitCheck();
151	if (!name || !_fileSystem)
152		return B_BAD_VALUE;
153	// check, if we do already know this file system, and create it, if not
154	FileSystem* fileSystem;
155	{
156		FileSystemLocker _(fFileSystems);
157		fileSystem = fFileSystems->Get(name);
158		if (fileSystem) {
159			fileSystem->AddReference();
160		} else {
161			status_t error;
162			fileSystem = new(nothrow) FileSystem(name, fPort, &error);
163			if (!fileSystem)
164				return B_NO_MEMORY;
165			if (error == B_OK)
166				error = fFileSystems->Put(name, fileSystem);
167			if (error != B_OK) {
168				delete fileSystem;
169				return error;
170			}
171		}
172	}
173	// prepare the file system
174	status_t error = fileSystem->Access();
175	if (error != B_OK) {
176		UnregisterFileSystem(fileSystem);
177		return error;
178	}
179	*_fileSystem = fileSystem;
180	return error;
181}
182
183// UnregisterFileSystem
184status_t
185UserlandFS::UnregisterFileSystem(FileSystem* fileSystem)
186{
187	if (!fileSystem)
188		return B_BAD_VALUE;
189	// find the FS and decrement its reference counter
190	bool deleteFS = false;
191	{
192		FileSystemLocker _(fFileSystems);
193		fileSystem = fFileSystems->Get(fileSystem->GetName());
194		if (!fileSystem)
195			return B_BAD_VALUE;
196		deleteFS = fileSystem->RemoveReference();
197		if (deleteFS)
198			fFileSystems->Remove(fileSystem->GetName());
199	}
200	// delete the FS, if the last reference has been removed
201	if (deleteFS)
202		delete fileSystem;
203	return B_OK;
204}
205
206// CountFileSystems
207int32
208UserlandFS::CountFileSystems() const
209{
210	return fFileSystems->Size();
211}
212
213// FirstTimeInit
214status_t
215UserlandFS::FirstTimeInit()
216{
217	// add debugger commands
218	KernelDebug::AddDebuggerCommands();
219	fDebuggerCommandsAdded = true;
220	// create file system map
221	fFileSystems = new(nothrow) FileSystemMap;
222	if (!fFileSystems)
223		RETURN_ERROR(B_NO_MEMORY);
224	status_t error = fFileSystems->InitCheck();
225	if (error != B_OK)
226		RETURN_ERROR(error);
227	// find the dispatcher ports
228	port_id port = find_port(kUserlandFSDispatcherPortName);
229	if (port < 0)
230		RETURN_ERROR(B_ERROR);
231	port_id replyPort = find_port(kUserlandFSDispatcherReplyPortName);
232	if (replyPort < 0)
233		RETURN_ERROR(B_ERROR);
234	// create a reply port
235	// send a connection request
236	error = write_port(port, UFS_DISPATCHER_CONNECT, NULL, 0);
237	if (error != B_OK)
238		RETURN_ERROR(error);
239	// receive the reply
240	int32 replyCode;
241	Port::Info portInfo;
242	ssize_t bytesRead = read_port(replyPort, &replyCode, &portInfo,
243		sizeof(Port::Info));
244	if (bytesRead < 0)
245		RETURN_ERROR(bytesRead);
246	if (replyCode != UFS_DISPATCHER_CONNECT_ACK)
247		RETURN_ERROR(B_BAD_DATA);
248	if (bytesRead != sizeof(Port::Info))
249		RETURN_ERROR(B_BAD_DATA);
250	// create a request port
251	fPort = new(nothrow) RequestPort(&portInfo);
252	if (!fPort)
253		RETURN_ERROR(B_NO_MEMORY);
254	if ((error = fPort->InitCheck()) != B_OK)
255		RETURN_ERROR(error);
256	RETURN_ERROR(error);
257}
258
259