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