xref: /haiku/src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/FileSystem.cpp (revision b671e9bbdbd10268a042b4f4cc4317ccd03d105e)
1 /*
2  * Copyright 2001-2009, Ingo Weinhold, ingo_weinhold@gmx.de.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 #include "Volume.h"
7 
8 #include <util/AutoLock.h>
9 
10 #include "AutoLocker.h"
11 #include "Compatibility.h"
12 #include "Debug.h"
13 #include "FileSystem.h"
14 #include "HashMap.h"
15 #include "kernel_interface.h"
16 #include "KernelRequestHandler.h"
17 #include "PortReleaser.h"
18 #include "RequestAllocator.h"
19 #include "Requests.h"
20 #include "Settings.h"
21 #include "SingleReplyRequestHandler.h"
22 
23 // The time after which the notification thread times out at the port and
24 // restarts the loop. Of interest only when the FS is deleted. It is the
25 // maximal time the destructor has to wait for the thread.
26 static const bigtime_t kNotificationRequestTimeout = 50000;	// 50 ms
27 
28 // SelectSyncMap
29 struct FileSystem::SelectSyncMap
30 	: public SynchronizedHashMap<HashKey32<selectsync*>, int32*> {
31 };
32 
33 // constructor
34 FileSystem::FileSystem()
35 	:
36 	fVolumes(),
37 	fName(),
38 	fTeam(-1),
39 	fNotificationPort(NULL),
40 	fNotificationThread(-1),
41 	fPortPool(),
42 	fSelectSyncs(NULL),
43 	fSettings(NULL),
44 	fUserlandServerTeam(-1),
45 	fInitialized(false),
46 	fTerminating(false)
47 {
48 	mutex_init(&fVolumeLock, "userlandfs volumes");
49 	mutex_init(&fVNodeOpsLock, "userlandfs vnode ops");
50 }
51 
52 // destructor
53 FileSystem::~FileSystem()
54 {
55 	fTerminating = true;
56 
57 	// wait for the notification thread to terminate
58 	if (fNotificationThread >= 0) {
59 		int32 result;
60 		wait_for_thread(fNotificationThread, &result);
61 	}
62 
63 	// delete our data structures
64 	if (fSelectSyncs) {
65 		for (SelectSyncMap::Iterator it = fSelectSyncs->GetIterator();
66 			 it.HasNext();) {
67 			SelectSyncMap::Entry entry = it.Next();
68 			delete entry.value;
69 		}
70 		delete fSelectSyncs;
71 	}
72 	delete fSettings;
73 
74 	// delete vnode ops vectors -- there shouldn't be any left, though
75 	VNodeOps* ops = fVNodeOps.Clear();
76 	int32 count = 0;
77 	while (ops != NULL) {
78 		count++;
79 		VNodeOps* next = ops->hash_link;
80 		free(ops);
81 		ops = next;
82 	}
83 	if (count > 0)
84 		WARN(("Deleted %ld vnode ops vectors!\n", count));
85 }
86 
87 // Init
88 status_t
89 FileSystem::Init(const char* name, team_id team, Port::Info* infos, int32 count,
90 	const FSCapabilities& capabilities)
91 {
92 	PRINT(("FileSystem::Init(\"%s\", %p, %ld)\n", name, infos, count));
93 	capabilities.Dump();
94 
95 	// check parameters
96 	if (!name || !infos || count < 2)
97 		RETURN_ERROR(B_BAD_VALUE);
98 
99 	// set the name
100 	if (!fName.SetTo(name))
101 		return B_NO_MEMORY;
102 
103 	// init VNodeOps map
104 	status_t error = fVNodeOps.Init();
105 	if (error != B_OK)
106 		return error;
107 
108 	fTeam = team;
109 	fCapabilities = capabilities;
110 
111 	// create the select sync entry map
112 	fSelectSyncs = new(nothrow) SelectSyncMap;
113 	if (!fSelectSyncs)
114 		return B_NO_MEMORY;
115 
116 	// create the request ports
117 	// the notification port
118 	fNotificationPort = new(nothrow) RequestPort(infos);
119 	if (!fNotificationPort)
120 		RETURN_ERROR(B_NO_MEMORY);
121 	error = fNotificationPort->InitCheck();
122 	if (error != B_OK)
123 		return error;
124 
125 	// the other request ports
126 	for (int32 i = 1; i < count; i++) {
127 		RequestPort* port = new(nothrow) RequestPort(infos + i);
128 		if (!port)
129 			RETURN_ERROR(B_NO_MEMORY);
130 		error = port->InitCheck();
131 		if (error == B_OK)
132 			error = fPortPool.AddPort(port);
133 		if (error != B_OK) {
134 			delete port;
135 			RETURN_ERROR(error);
136 		}
137 	}
138 
139 	// get the userland team
140 	port_info portInfo;
141 	error = get_port_info(infos[0].owner_port, &portInfo);
142 	if (error != B_OK)
143 		RETURN_ERROR(error);
144 	fUserlandServerTeam = portInfo.team;
145 
146 	// print some info about the userland team
147 	D(
148 		PRINT(("  userland team is: %ld\n", fUserlandServerTeam));
149 		int32 cookie = 0;
150 		thread_info threadInfo;
151 		while (get_next_thread_info(fUserlandServerTeam, &cookie, &threadInfo)
152 			   == B_OK) {
153 			PRINT(("    userland thread: %ld: `%s'\n", threadInfo.thread,
154 				threadInfo.name));
155 		}
156 	);
157 
158 	// load the settings
159 	fSettings = new(nothrow) Settings;
160 	if (fSettings) {
161 		status_t settingsError = fSettings->SetTo(fName.GetString());
162 		if (settingsError != B_OK) {
163 			PRINT(("Failed to load settings: %s\n", strerror(settingsError)));
164 			delete fSettings;
165 			fSettings = NULL;
166 		} else
167 			fSettings->Dump();
168 	} else
169 		ERROR(("Failed to allocate settings.\n"));
170 
171 	// spawn the notification thread
172 	#if USER
173 		fNotificationThread = spawn_thread(_NotificationThreadEntry,
174 			"UFS notification thread", B_NORMAL_PRIORITY, this);
175 	#else
176 		fNotificationThread = spawn_kernel_thread(_NotificationThreadEntry,
177 			"UFS notification thread", B_NORMAL_PRIORITY, this);
178 	#endif
179 	if (fNotificationThread < 0)
180 		RETURN_ERROR(fNotificationThread);
181 	resume_thread(fNotificationThread);
182 
183 	fInitialized = (error == B_OK);
184 	RETURN_ERROR(error);
185 }
186 
187 // GetName
188 const char*
189 FileSystem::GetName() const
190 {
191 	return fName.GetString();
192 }
193 
194 // GetCapabilities
195 const FSCapabilities&
196 FileSystem::GetCapabilities() const
197 {
198 	return fCapabilities;
199 }
200 
201 // GetPortPool
202 RequestPortPool*
203 FileSystem::GetPortPool()
204 {
205 	return &fPortPool;
206 }
207 
208 // Mount
209 status_t
210 FileSystem::Mount(fs_volume* fsVolume, const char* device, uint32 flags,
211 	const char* parameters, Volume** _volume)
212 {
213 	// check initialization and parameters
214 	if (!fInitialized || !_volume)
215 		return B_BAD_VALUE;
216 
217 	// create volume
218 	Volume* volume = new(nothrow) Volume(this, fsVolume);
219 	if (!volume)
220 		return B_NO_MEMORY;
221 
222 	// add volume to the volume list
223 	MutexLocker locker(fVolumeLock);
224 	status_t error = fVolumes.PushBack(volume);
225 	locker.Unlock();
226 	if (error != B_OK)
227 		return error;
228 
229 	// mount volume
230 	error = volume->Mount(device, flags, parameters);
231 	if (error != B_OK) {
232 		MutexLocker locker(fVolumeLock);
233 		fVolumes.Remove(volume);
234 		locker.Unlock();
235 		volume->RemoveReference();
236 		return error;
237 	}
238 
239 	*_volume = volume;
240 	return error;
241 }
242 
243 // Initialize
244 /*status_t
245 FileSystem::Initialize(const char* deviceName, const char* parameters,
246 	size_t len)
247 {
248 	// get a free port
249 	RequestPort* port = fPortPool.AcquirePort();
250 	if (!port)
251 		return B_ERROR;
252 	PortReleaser _(&fPortPool, port);
253 	// prepare the request
254 	RequestAllocator allocator(port->GetPort());
255 	MountVolumeRequest* request;
256 	status_t error = AllocateRequest(allocator, &request);
257 	if (error != B_OK)
258 		return error;
259 	error = allocator.AllocateString(request->device, deviceName);
260 	if (error == B_OK)
261 		error = allocator.AllocateData(request->parameters, parameters, len, 1);
262 	if (error != B_OK)
263 		return error;
264 	// send the request
265 	SingleReplyRequestHandler handler(MOUNT_VOLUME_REPLY);
266 	InitializeVolumeReply* reply;
267 	error = port->SendRequest(&allocator, &handler, (Request**)&reply);
268 	if (error != B_OK)
269 		return error;
270 	RequestReleaser requestReleaser(port, reply);
271 	// process the reply
272 	if (reply->error != B_OK)
273 		return reply->error;
274 	return error;
275 }*/
276 
277 // VolumeUnmounted
278 void
279 FileSystem::VolumeUnmounted(Volume* volume)
280 {
281 	MutexLocker locker(fVolumeLock);
282 	fVolumes.Remove(volume);
283 }
284 
285 // GetVolume
286 Volume*
287 FileSystem::GetVolume(dev_t id)
288 {
289 	MutexLocker _(fVolumeLock);
290 	for (Vector<Volume*>::Iterator it = fVolumes.Begin();
291 		 it != fVolumes.End();
292 		 it++) {
293 		 Volume* volume = *it;
294 		 if (volume->GetID() == id) {
295 		 	volume->AddReference();
296 		 	return volume;
297 		 }
298 	}
299 	return NULL;
300 }
301 
302 // GetIOCtlInfo
303 const IOCtlInfo*
304 FileSystem::GetIOCtlInfo(int command) const
305 {
306 	return (fSettings ? fSettings->GetIOCtlInfo(command) : NULL);
307 }
308 
309 // AddSelectSyncEntry
310 status_t
311 FileSystem::AddSelectSyncEntry(selectsync* sync)
312 {
313 	AutoLocker<SelectSyncMap> _(fSelectSyncs);
314 	int32* count = fSelectSyncs->Get(sync);
315 	if (!count) {
316 		count = new(nothrow) int32(0);
317 		if (!count)
318 			return B_NO_MEMORY;
319 		status_t error = fSelectSyncs->Put(sync, count);
320 		if (error != B_OK) {
321 			delete count;
322 			return error;
323 		}
324 	}
325 	(*count)++;
326 	return B_OK;
327 }
328 
329 // RemoveSelectSyncEntry
330 void
331 FileSystem::RemoveSelectSyncEntry(selectsync* sync)
332 {
333 	AutoLocker<SelectSyncMap> _(fSelectSyncs);
334 	if (int32* count = fSelectSyncs->Get(sync)) {
335 		if (--(*count) <= 0) {
336 			fSelectSyncs->Remove(sync);
337 			delete count;
338 		}
339 	}
340 }
341 
342 
343 // KnowsSelectSyncEntry
344 bool
345 FileSystem::KnowsSelectSyncEntry(selectsync* sync)
346 {
347 	return fSelectSyncs->ContainsKey(sync);
348 }
349 
350 
351 // GetVNodeOps
352 VNodeOps*
353 FileSystem::GetVNodeOps(const FSVNodeCapabilities& capabilities)
354 {
355 	MutexLocker locker(fVNodeOpsLock);
356 
357 	// do we already have ops for those capabilities
358 	VNodeOps* ops = fVNodeOps.Lookup(capabilities);
359 	if (ops != NULL) {
360 		ops->refCount++;
361 		return ops;
362 	}
363 
364 	// no, create a new object
365 	fs_vnode_ops* opsVector = new(std::nothrow) fs_vnode_ops;
366 	if (opsVector == NULL)
367 		return NULL;
368 
369 	// set the operations
370 	_InitVNodeOpsVector(opsVector, capabilities);
371 
372 	// create the VNodeOps object
373 	ops = new(std::nothrow) VNodeOps(capabilities, opsVector);
374 	if (ops == NULL) {
375 		delete opsVector;
376 		return NULL;
377 	}
378 
379 	fVNodeOps.Insert(ops);
380 
381 	return ops;
382 }
383 
384 
385 // PutVNodeOps
386 void
387 FileSystem::PutVNodeOps(VNodeOps* ops)
388 {
389 	MutexLocker locker(fVNodeOpsLock);
390 
391 	if (--ops->refCount == 0) {
392 		fVNodeOps.Remove(ops);
393 		delete ops;
394 	}
395 }
396 
397 
398 // IsUserlandServerThread
399 bool
400 FileSystem::IsUserlandServerThread() const
401 {
402 	thread_info info;
403 	get_thread_info(find_thread(NULL), &info);
404 	return (info.team == fUserlandServerTeam);
405 }
406 
407 
408 // _InitVNodeOpsVector
409 void
410 FileSystem::_InitVNodeOpsVector(fs_vnode_ops* ops,
411 	const FSVNodeCapabilities& capabilities)
412 {
413 	memcpy(ops, &gUserlandFSVnodeOps, sizeof(fs_vnode_ops));
414 
415 	#undef CLEAR_UNSUPPORTED
416 	#define CLEAR_UNSUPPORTED(capability, op) 	\
417 		if (!capabilities.Get(capability))				\
418 			ops->op = NULL
419 
420 	// vnode operations
421 	// FS_VNODE_CAPABILITY_LOOKUP: lookup
422 	// FS_VNODE_CAPABILITY_GET_VNODE_NAME: get_vnode_name
423 		// emulated in userland
424 	// FS_VNODE_CAPABILITY_PUT_VNODE: put_vnode
425 	// FS_VNODE_CAPABILITY_REMOVE_VNODE: remove_vnode
426 		// needed by Volume to clean up
427 
428 	// asynchronous I/O
429 	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_IO, io);
430 	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_CANCEL_IO, cancel_io);
431 
432 	// cache file access
433 	ops->get_file_map = NULL;	// never used
434 
435 	// common operations
436 	// FS_VNODE_CAPABILITY_IOCTL: ioctl
437 		// needed by Volume
438 	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_SET_FLAGS, set_flags);
439 	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_SELECT, select);
440 	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_DESELECT, deselect);
441 	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_FSYNC, fsync);
442 
443 	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_READ_SYMLINK, read_symlink);
444 	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_CREATE_SYMLINK, create_symlink);
445 
446 	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_LINK, link);
447 	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_UNLINK, unlink);
448 	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_RENAME, rename);
449 
450 	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_ACCESS, access);
451 	// FS_VNODE_CAPABILITY_READ_STAT: read_stat
452 		// needed by Volume
453 	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_WRITE_STAT, write_stat);
454 
455 	// file operations
456 	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_CREATE, create);
457 	// FS_VNODE_CAPABILITY_OPEN: open
458 		// mandatory
459 	// FS_VNODE_CAPABILITY_CLOSE: close
460 		// needed by Volume
461 	// FS_VNODE_CAPABILITY_FREE_COOKIE: free_cookie
462 		// needed by Volume
463 	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_READ, read);
464 	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_WRITE, write);
465 
466 	// directory operations
467 	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_CREATE_DIR, create_dir);
468 	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_REMOVE_DIR, remove_dir);
469 	// FS_VNODE_CAPABILITY_OPEN_DIR: open_dir
470 		// mandatory
471 	// FS_VNODE_CAPABILITY_CLOSE_DIR: close_dir
472 		// needed by Volume
473 	// FS_VNODE_CAPABILITY_FREE_DIR_COOKIE: free_dir_cookie
474 		// needed by Volume
475 	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_READ_DIR, read_dir);
476 	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_REWIND_DIR, rewind_dir);
477 
478 	// attribute directory operations
479 	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_OPEN_ATTR_DIR, open_attr_dir);
480 	// FS_VNODE_CAPABILITY_CLOSE_ATTR_DIR: close_attr_dir
481 		// needed by Volume
482 	// FS_VNODE_CAPABILITY_FREE_ATTR_DIR_COOKIE: free_attr_dir_cookie
483 		// needed by Volume
484 	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_READ_ATTR_DIR, read_attr_dir);
485 	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_REWIND_ATTR_DIR, rewind_attr_dir);
486 
487 	// attribute operations
488 	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_CREATE_ATTR, create_attr);
489 	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_OPEN_ATTR, open_attr);
490 	// FS_VNODE_CAPABILITY_CLOSE_ATTR: close_attr
491 		// needed by Volume
492 	// FS_VNODE_CAPABILITY_FREE_ATTR_COOKIE: free_attr_cookie
493 		// needed by Volume
494 	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_READ_ATTR, read_attr);
495 	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_WRITE_ATTR, write_attr);
496 
497 	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_READ_ATTR_STAT, read_attr_stat);
498 	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_WRITE_ATTR_STAT, write_attr_stat);
499 	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_RENAME_ATTR, rename_attr);
500 	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_REMOVE_ATTR, remove_attr);
501 
502 	// support for node and FS layers
503 	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_CREATE_SPECIAL_NODE,
504 		create_special_node);
505 	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_GET_SUPER_VNODE, get_super_vnode);
506 
507 	#undef CLEAR_UNSUPPORTED
508 }
509 
510 
511 // _NotificationThreadEntry
512 int32
513 FileSystem::_NotificationThreadEntry(void* data)
514 {
515 	return ((FileSystem*)data)->_NotificationThread();
516 }
517 
518 
519 // _NotificationThread
520 int32
521 FileSystem::_NotificationThread()
522 {
523 	// process the notification requests until the FS is deleted
524 	while (!fTerminating) {
525 		if (fNotificationPort->InitCheck() != B_OK)
526 			return fNotificationPort->InitCheck();
527 		KernelRequestHandler handler(this, NO_REQUEST);
528 		fNotificationPort->HandleRequests(&handler, NULL,
529 			kNotificationRequestTimeout);
530 	}
531 	// We eat all remaining notification requests, so that they aren't
532 	// presented to the file system, when it is mounted next time.
533 	// TODO: We should probably use a special handler that sends an ack reply,
534 	// but ignores the requests otherwise.
535 	KernelRequestHandler handler(this, NO_REQUEST);
536 	fNotificationPort->HandleRequests(&handler, NULL, 0);
537 	return 0;
538 }
539 
540