vfs: check userland buffers before reading them.
[haiku.git] / src / tools / fs_shell / module.cpp
blob7c9ca572e966db990c23d23b4d4692129ed73683
1 /*
2 * Copyright 2002-2008, Haiku Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
5 * Copyright 2001, Thomas Kurschel. All rights reserved.
6 * Distributed under the terms of the NewOS License.
7 */
9 /** Manages kernel add-ons and their exported modules. */
11 #include "module.h"
13 #include <stdlib.h>
15 #include "fssh_errors.h"
16 #include "fssh_kernel_export.h"
17 #include "fssh_lock.h"
18 #include "fssh_module.h"
19 #include "fssh_string.h"
20 #include "hash.h"
23 //#define TRACE_MODULE
24 #ifdef TRACE_MODULE
25 # define TRACE(x) fssh_dprintf x
26 #else
27 # define TRACE(x) ;
28 #endif
29 #define FATAL(x) fssh_dprintf x
32 namespace FSShell {
35 #define MODULE_HASH_SIZE 16
37 enum module_state {
38 MODULE_QUERIED = 0,
39 MODULE_LOADED,
40 MODULE_INIT,
41 MODULE_READY,
42 MODULE_UNINIT,
43 MODULE_ERROR
47 /* Each known module will have this structure which is put in the
48 * gModulesHash, and looked up by name.
51 struct module {
52 struct module *next;
53 char *name;
54 char *file;
55 int32_t ref_count;
56 fssh_module_info *info; /* will only be valid if ref_count > 0 */
57 int32_t offset; /* this is the offset in the headers */
58 module_state state; /* state of module */
59 uint32_t flags;
62 #define FSSH_B_BUILT_IN_MODULE 2
65 /* locking scheme: there is a global lock only; having several locks
66 * makes trouble if dependent modules get loaded concurrently ->
67 * they have to wait for each other, i.e. we need one lock per module;
68 * also we must detect circular references during init and not dead-lock
70 static fssh_recursive_lock sModulesLock;
72 /* we store the loaded modules by directory path, and all known modules by module name
73 * in a hash table for quick access
75 static hash_table *sModulesHash;
78 /** calculates hash for a module using its name */
80 static uint32_t
81 module_hash(void *_module, const void *_key, uint32_t range)
83 module *module = (struct module *)_module;
84 const char *name = (const char *)_key;
86 if (module != NULL)
87 return hash_hash_string(module->name) % range;
89 if (name != NULL)
90 return hash_hash_string(name) % range;
92 return 0;
96 /** compares a module to a given name */
98 static int
99 module_compare(void *_module, const void *_key)
101 module *module = (struct module *)_module;
102 const char *name = (const char *)_key;
103 if (name == NULL)
104 return -1;
106 return fssh_strcmp(module->name, name);
110 static inline void
111 inc_module_ref_count(struct module *module)
113 module->ref_count++;
117 static inline void
118 dec_module_ref_count(struct module *module)
120 module->ref_count--;
124 /** Extract the information from the module_info structure pointed at
125 * by "info" and create the entries required for access to it's details.
128 static fssh_status_t
129 create_module(fssh_module_info *info, const char *file, int offset, module **_module)
131 module *module;
133 TRACE(("create_module(info = %p, file = \"%s\", offset = %d, _module = %p)\n",
134 info, file, offset, _module));
136 if (!info->name)
137 return FSSH_B_BAD_VALUE;
139 module = (struct module *)hash_lookup(sModulesHash, info->name);
140 if (module) {
141 FATAL(("Duplicate module name (%s) detected... ignoring new one\n", info->name));
142 return FSSH_B_FILE_EXISTS;
145 if ((module = (struct module *)malloc(sizeof(struct module))) == NULL)
146 return FSSH_B_NO_MEMORY;
148 TRACE(("create_module: name = \"%s\", file = \"%s\"\n", info->name, file));
150 module->name = fssh_strdup(info->name);
151 if (module->name == NULL) {
152 free(module);
153 return FSSH_B_NO_MEMORY;
156 module->file = fssh_strdup(file);
157 if (module->file == NULL) {
158 free(module->name);
159 free(module);
160 return FSSH_B_NO_MEMORY;
163 module->state = MODULE_QUERIED;
164 module->info = info;
165 module->offset = offset;
166 // record where the module_info can be found in the module_info array
167 module->ref_count = 0;
168 module->flags = info->flags;
170 fssh_recursive_lock_lock(&sModulesLock);
171 hash_insert(sModulesHash, module);
172 fssh_recursive_lock_unlock(&sModulesLock);
174 if (_module)
175 *_module = module;
177 return FSSH_B_OK;
181 /** Initializes a loaded module depending on its state */
183 static inline fssh_status_t
184 init_module(module *module)
186 switch (module->state) {
187 case MODULE_QUERIED:
188 case MODULE_LOADED:
190 fssh_status_t status;
191 module->state = MODULE_INIT;
193 // init module
195 TRACE(("initializing module %s (at %p)... \n", module->name, module->info->std_ops));
196 status = module->info->std_ops(FSSH_B_MODULE_INIT);
197 TRACE(("...done (%s)\n", strerror(status)));
199 if (status >= FSSH_B_OK)
200 module->state = MODULE_READY;
201 else {
202 module->state = MODULE_LOADED;
205 return status;
208 case MODULE_READY:
209 return FSSH_B_OK;
211 case MODULE_INIT:
212 FATAL(("circular reference to %s\n", module->name));
213 return FSSH_B_ERROR;
215 case MODULE_UNINIT:
216 FATAL(("tried to load module %s which is currently unloading\n", module->name));
217 return FSSH_B_ERROR;
219 case MODULE_ERROR:
220 FATAL(("cannot load module %s because its earlier unloading failed\n", module->name));
221 return FSSH_B_ERROR;
223 default:
224 return FSSH_B_ERROR;
226 // never trespasses here
230 /** Uninitializes a module depeding on its state */
232 static inline int
233 uninit_module(module *module)
235 TRACE(("uninit_module(%s)\n", module->name));
237 switch (module->state) {
238 case MODULE_QUERIED:
239 case MODULE_LOADED:
240 return FSSH_B_NO_ERROR;
242 case MODULE_INIT:
243 fssh_panic("Trying to unload module %s which is initializing\n", module->name);
244 return FSSH_B_ERROR;
246 case MODULE_UNINIT:
247 fssh_panic("Trying to unload module %s which is un-initializing\n", module->name);
248 return FSSH_B_ERROR;
250 case MODULE_READY:
252 fssh_status_t status;
254 module->state = MODULE_UNINIT;
256 TRACE(("uninitializing module %s...\n", module->name));
257 status = module->info->std_ops(FSSH_B_MODULE_UNINIT);
258 TRACE(("...done (%s)\n", strerror(status)));
260 if (status == FSSH_B_NO_ERROR) {
261 module->state = MODULE_LOADED;
262 return 0;
265 FATAL(("Error unloading module %s (%s)\n", module->name, fssh_strerror(status)));
267 module->state = MODULE_ERROR;
268 module->flags |= FSSH_B_KEEP_LOADED;
270 return status;
272 default:
273 return FSSH_B_ERROR;
275 // never trespasses here
279 void
280 register_builtin_module(struct fssh_module_info *info)
282 info->flags |= FSSH_B_BUILT_IN_MODULE;
283 // this is an internal flag, it doesn't have to be set by modules itself
285 if (create_module(info, "", -1, NULL) != FSSH_B_OK)
286 fssh_dprintf("creation of built-in module \"%s\" failed!\n", info->name);
290 static int
291 dump_modules(int argc, char **argv)
293 hash_iterator iterator;
294 struct module *module;
296 hash_rewind(sModulesHash, &iterator);
297 fssh_dprintf("-- known modules:\n");
299 while ((module = (struct module *)hash_next(sModulesHash, &iterator)) != NULL) {
300 fssh_dprintf("%p: \"%s\", \"%s\" (%d), refcount = %d, state = %d\n",
301 module, module->name, module->file, (int)module->offset, (int)module->ref_count,
302 module->state);
305 return 0;
309 // #pragma mark -
310 // Exported Kernel API (private part)
313 /** Setup the module structures and data for use - must be called
314 * before any other module call.
317 fssh_status_t
318 module_init(kernel_args *args)
320 fssh_recursive_lock_init(&sModulesLock, "modules rlock");
322 sModulesHash = hash_init(MODULE_HASH_SIZE, 0, module_compare, module_hash);
323 if (sModulesHash == NULL)
324 return FSSH_B_NO_MEMORY;
326 fssh_add_debugger_command("modules", &dump_modules, "list all known & loaded modules");
328 return FSSH_B_OK;
332 } // namespace FSShell
335 // #pragma mark -
336 // Exported Kernel API (public part)
339 using namespace FSShell;
342 fssh_status_t
343 fssh_get_module(const char *path, fssh_module_info **_info)
345 module *module;
346 fssh_status_t status;
348 TRACE(("get_module(%s)\n", path));
350 if (path == NULL)
351 return FSSH_B_BAD_VALUE;
353 fssh_recursive_lock_lock(&sModulesLock);
355 module = (struct module *)hash_lookup(sModulesHash, path);
356 if (module == NULL)
357 goto err;
359 // The state will be adjusted by the call to init_module
360 // if we have just loaded the file
361 if (module->ref_count == 0)
362 status = init_module(module);
363 else
364 status = FSSH_B_OK;
366 if (status == FSSH_B_OK) {
367 inc_module_ref_count(module);
368 *_info = module->info;
371 fssh_recursive_lock_unlock(&sModulesLock);
372 return status;
374 err:
375 fssh_recursive_lock_unlock(&sModulesLock);
376 return FSSH_B_ENTRY_NOT_FOUND;
380 fssh_status_t
381 fssh_put_module(const char *path)
383 module *module;
385 TRACE(("put_module(path = %s)\n", path));
387 fssh_recursive_lock_lock(&sModulesLock);
389 module = (struct module *)hash_lookup(sModulesHash, path);
390 if (module == NULL) {
391 FATAL(("module: We don't seem to have a reference to module %s\n", path));
392 fssh_recursive_lock_unlock(&sModulesLock);
393 return FSSH_B_BAD_VALUE;
396 if ((module->flags & FSSH_B_KEEP_LOADED) == 0) {
397 dec_module_ref_count(module);
399 if (module->ref_count == 0)
400 uninit_module(module);
403 fssh_recursive_lock_unlock(&sModulesLock);
404 return FSSH_B_OK;