1// FileSystem.cpp
2
3#include "AutoLocker.h"
4#include "Compatibility.h"
5#include "Debug.h"
6#include "FileSystem.h"
7#include "HashMap.h"
8#include "KernelRequestHandler.h"
9#include "PortReleaser.h"
10#include "RequestAllocator.h"
11#include "RequestPort.h"
12#include "Requests.h"
13#include "Settings.h"
14#include "SingleReplyRequestHandler.h"
15#include "Volume.h"
16
17// The time after which the notification thread times out at the port and
18// restarts the loop. Of interest only when the FS is deleted. It is the
19// maximal time the destructor has to wait for the thread.
20static const bigtime_t kNotificationRequestTimeout = 50000;	// 50 ms
21
22// SelectSyncMap
23struct FileSystem::SelectSyncMap
24	: public SynchronizedHashMap<HashKey32<selectsync*>, int32*> {
25};
26
27// constructor
28FileSystem::FileSystem(const char* name, RequestPort* initPort, status_t* error)
29	: LazyInitializable(),
30	  Referencable(),
31	  fVolumes(),
32	  fVolumeLock(),
33	  fName(name),
34	  fInitPort(initPort),
35	  fNotificationPort(NULL),
36	  fNotificationThread(-1),
37	  fPortPool(),
38	  fSelectSyncs(NULL),
39	  fSettings(NULL),
40	  fUserlandServerTeam(-1),
41	  fTerminating(false)
42{
43	if (error)
44		*error = (fName.GetLength() == 0 ? B_NO_MEMORY : B_OK);
45}
46
47// destructor
48FileSystem::~FileSystem()
49{
50	fTerminating = true;
51	// wait for the notification thread to terminate
52	if (fNotificationThread >= 0) {
53		int32 result;
54		wait_for_thread(fNotificationThread, &result);
55	}
56	// delete our data structures
57	if (fSelectSyncs) {
58		for (SelectSyncMap::Iterator it = fSelectSyncs->GetIterator();
59			 it.HasNext();) {
60			SelectSyncMap::Entry entry = it.Next();
61			delete entry.value;
62		}
63		delete fSelectSyncs;
64	}
65	delete fSettings;
66}
67
68// GetName
69const char*
70FileSystem::GetName() const
71{
72	return fName.GetString();
73}
74
75// GetPortPool
76RequestPortPool*
77FileSystem::GetPortPool()
78{
79	return &fPortPool;
80}
81
82// Mount
83status_t
84FileSystem::Mount(nspace_id id, const char* device, ulong flags,
85	const char* parameters, int32 len, Volume** _volume)
86{
87	// check initialization and parameters
88	if (InitCheck() != B_OK)
89		return InitCheck();
90	if (!_volume)
91		return B_BAD_VALUE;
92	// create volume
93	Volume* volume = new(nothrow) Volume(this, id);
94	if (!volume)
95		return B_NO_MEMORY;
96	// add volume to the volume list
97	fVolumeLock.Lock();
98	status_t error = fVolumes.PushBack(volume);
99	fVolumeLock.Unlock();
100	if (error != B_OK)
101		return error;
102	// mount volume
103	error = volume->Mount(device, flags, parameters, len);
104	if (error != B_OK) {
105		fVolumeLock.Lock();
106		fVolumes.Remove(volume);
107		fVolumeLock.Unlock();
108		volume->RemoveReference();
109		return error;
110	}
111	*_volume = volume;
112	return error;
113}
114
115// Initialize
116status_t
117FileSystem::Initialize(const char* deviceName, const char* parameters,
118	size_t len)
119{
120	// get a free port
121	RequestPort* port = fPortPool.AcquirePort();
122	if (!port)
123		return B_ERROR;
124	PortReleaser _(&fPortPool, port);
125	// prepare the request
126	RequestAllocator allocator(port->GetPort());
127	MountVolumeRequest* request;
128	status_t error = AllocateRequest(allocator, &request);
129	if (error != B_OK)
130		return error;
131	error = allocator.AllocateString(request->device, deviceName);
132	if (error == B_OK)
133		error = allocator.AllocateData(request->parameters, parameters, len, 1);
134	if (error != B_OK)
135		return error;
136	// send the request
137	SingleReplyRequestHandler handler(MOUNT_VOLUME_REPLY);
138	InitializeVolumeReply* reply;
139	error = port->SendRequest(&allocator, &handler, (Request**)&reply);
140	if (error != B_OK)
141		return error;
142	RequestReleaser requestReleaser(port, reply);
143	// process the reply
144	if (reply->error != B_OK)
145		return reply->error;
146	return error;
147}
148
149// VolumeUnmounted
150void
151FileSystem::VolumeUnmounted(Volume* volume)
152{
153	fVolumeLock.Lock();
154	fVolumes.Remove(volume);
155	fVolumeLock.Unlock();
156}
157
158// GetVolume
159Volume*
160FileSystem::GetVolume(nspace_id id)
161{
162	AutoLocker<Locker> _(fVolumeLock);
163	for (Vector<Volume*>::Iterator it = fVolumes.Begin();
164		 it != fVolumes.End();
165		 it++) {
166		 Volume* volume = *it;
167		 if (volume->GetID() == id) {
168		 	volume->AddReference();
169		 	return volume;
170		 }
171	}
172	return NULL;
173}
174
175// GetIOCtlInfo
176const IOCtlInfo*
177FileSystem::GetIOCtlInfo(int command) const
178{
179	return (fSettings ? fSettings->GetIOCtlInfo(command) : NULL);
180}
181
182// AddSelectSyncEntry
183status_t
184FileSystem::AddSelectSyncEntry(selectsync* sync)
185{
186	AutoLocker<SelectSyncMap> _(fSelectSyncs);
187	int32* count = fSelectSyncs->Get(sync);
188	if (!count) {
189		count = new(nothrow) int32(0);
190		if (!count)
191			return B_NO_MEMORY;
192		status_t error = fSelectSyncs->Put(sync, count);
193		if (error != B_OK) {
194			delete count;
195			return error;
196		}
197	}
198	(*count)++;
199	return B_OK;
200}
201
202// RemoveSelectSyncEntry
203void
204FileSystem::RemoveSelectSyncEntry(selectsync* sync)
205{
206	AutoLocker<SelectSyncMap> _(fSelectSyncs);
207	if (int32* count = fSelectSyncs->Get(sync)) {
208		if (--(*count) <= 0) {
209			fSelectSyncs->Remove(sync);
210			delete count;
211		}
212	}
213}
214
215// KnowsSelectSyncEntry
216bool
217FileSystem::KnowsSelectSyncEntry(selectsync* sync)
218{
219	return fSelectSyncs->ContainsKey(sync);
220}
221
222// IsUserlandServerThread
223bool
224FileSystem::IsUserlandServerThread() const
225{
226	thread_info info;
227	get_thread_info(find_thread(NULL), &info);
228	return (info.team == fUserlandServerTeam);
229}
230
231// FirstTimeInit
232status_t
233FileSystem::FirstTimeInit()
234{
235	if (fName.GetLength() == 0)
236		RETURN_ERROR(B_NO_MEMORY);
237	PRINT(("FileSystem::FirstTimeInit(): %s\n", fName.GetString()));
238	// create the select sync entry map
239	fSelectSyncs = new(nothrow) SelectSyncMap;
240	if (!fSelectSyncs)
241		return B_NO_MEMORY;
242	// prepare the request
243	RequestAllocator allocator(fInitPort->GetPort());
244	FSConnectRequest* request;
245	status_t error = AllocateRequest(allocator, &request);
246	if (error != B_OK)
247		RETURN_ERROR(error);
248	error = allocator.AllocateString(request->fsName, fName.GetString());
249	if (error != B_OK)
250		RETURN_ERROR(error);
251	// send the request
252	SingleReplyRequestHandler handler(FS_CONNECT_REPLY);
253	FSConnectReply* reply;
254	error = fInitPort->SendRequest(&allocator, &handler, (Request**)&reply);
255	if (error != B_OK)
256		RETURN_ERROR(error);
257	RequestReleaser requestReleaser(fInitPort, reply);
258	// process the reply
259	if (reply->error != B_OK)
260		RETURN_ERROR(reply->error);
261	// get the port infos
262	int32 count = reply->portInfoCount;
263	if (count < 2)
264		RETURN_ERROR(B_BAD_DATA);
265	if (reply->portInfos.GetSize() != count * (int32)sizeof(Port::Info))
266		RETURN_ERROR(B_BAD_DATA);
267	Port::Info* infos = (Port::Info*)reply->portInfos.GetData();
268	// create the request ports
269	// the notification port
270	fNotificationPort = new(nothrow) RequestPort(infos);
271	if (!fNotificationPort)
272		RETURN_ERROR(B_NO_MEMORY);
273	error = fNotificationPort->InitCheck();
274	if (error != B_OK)
275		return error;
276	// the other request ports
277	for (int32 i = 1; i < count; i++) {
278		RequestPort* port = new(nothrow) RequestPort(infos + i);
279		if (!port)
280			RETURN_ERROR(B_NO_MEMORY);
281		error = port->InitCheck();
282		if (error == B_OK)
283			error = fPortPool.AddPort(port);
284		if (error != B_OK) {
285			delete port;
286			RETURN_ERROR(error);
287		}
288	}
289	// get the userland team
290	port_info portInfo;
291	error = get_port_info(infos[0].owner_port, &portInfo);
292	if (error != B_OK)
293		RETURN_ERROR(error);
294	fUserlandServerTeam = portInfo.team;
295	// print some info about the userland team
296	D(
297		PRINT(("  userland team is: %ld\n", fUserlandServerTeam));
298		int32 cookie = 0;
299		thread_info threadInfo;
300		while (get_next_thread_info(fUserlandServerTeam, &cookie, &threadInfo)
301			   == B_OK) {
302			PRINT(("    userland thread: %ld: `%s'\n", threadInfo.thread,
303				threadInfo.name));
304		}
305	);
306	// load the settings
307	fSettings = new(nothrow) Settings;
308	if (fSettings) {
309		status_t settingsError = fSettings->SetTo(fName.GetString());
310		if (settingsError != B_OK) {
311			PRINT(("Failed to load settings: %s\n", strerror(settingsError)));
312			delete fSettings;
313			fSettings = NULL;
314		} else
315			fSettings->Dump();
316	} else
317		ERROR(("Failed to allocate settings.\n"));
318	// spawn the notification thread
319	#if USER
320		fNotificationThread = spawn_thread(_NotificationThreadEntry,
321			"UFS notification thread", B_NORMAL_PRIORITY, this);
322	#else
323		fNotificationThread = spawn_kernel_thread(_NotificationThreadEntry,
324			"UFS notification thread", B_NORMAL_PRIORITY, this);
325	#endif
326	if (fNotificationThread < 0)
327		RETURN_ERROR(fNotificationThread);
328	resume_thread(fNotificationThread);
329	RETURN_ERROR(error);
330}
331
332// _NotificationThreadEntry
333int32
334FileSystem::_NotificationThreadEntry(void* data)
335{
336	return ((FileSystem*)data)->_NotificationThread();
337}
338
339// _NotificationThread
340int32
341FileSystem::_NotificationThread()
342{
343	// process the notification requests until the FS is deleted
344	while (!fTerminating) {
345		if (fNotificationPort->InitCheck() != B_OK)
346			return fNotificationPort->InitCheck();
347		KernelRequestHandler handler(this, NO_REQUEST);
348		fNotificationPort->HandleRequests(&handler, NULL,
349			kNotificationRequestTimeout);
350	}
351	// We eat all remaining notification requests, so that they aren't
352	// presented to the file system, when it is mounted next time.
353	// TODO: We should probably use a special handler that sends an ack reply,
354	// but ignores the requests otherwise.
355	KernelRequestHandler handler(this, NO_REQUEST);
356	fNotificationPort->HandleRequests(&handler, NULL, 0);
357	return 0;
358}
359
360