1 | /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ |
---|
2 | /* gnome-vfs-method.c - Handling of access methods in the GNOME |
---|
3 | Virtual File System. |
---|
4 | |
---|
5 | Copyright (C) 1999 Free Software Foundation |
---|
6 | |
---|
7 | The Gnome Library is free software; you can redistribute it and/or |
---|
8 | modify it under the terms of the GNU Library General Public License as |
---|
9 | published by the Free Software Foundation; either version 2 of the |
---|
10 | License, or (at your option) any later version. |
---|
11 | |
---|
12 | The Gnome Library is distributed in the hope that it will be useful, |
---|
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
---|
15 | Library General Public License for more details. |
---|
16 | |
---|
17 | You should have received a copy of the GNU Library General Public |
---|
18 | License along with the Gnome Library; see the file COPYING.LIB. If not, |
---|
19 | write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, |
---|
20 | Boston, MA 02111-1307, USA. |
---|
21 | |
---|
22 | Author: Ettore Perazzoli <ettore@gnu.org> */ |
---|
23 | |
---|
24 | #include <config.h> |
---|
25 | #include "gnome-vfs-method.h" |
---|
26 | |
---|
27 | #include <stdlib.h> |
---|
28 | #include <string.h> |
---|
29 | #include <sys/types.h> |
---|
30 | #include <unistd.h> |
---|
31 | |
---|
32 | #include <glib.h> |
---|
33 | #include <gmodule.h> |
---|
34 | |
---|
35 | #include <libgnomevfs/gnome-vfs-transform.h> |
---|
36 | #include <libgnomevfs/gnome-vfs-module.h> |
---|
37 | #include <libgnomevfs/gnome-vfs-private.h> |
---|
38 | |
---|
39 | |
---|
40 | struct _ModuleElement { |
---|
41 | char *name; |
---|
42 | const char *args; |
---|
43 | GnomeVFSMethod *method; |
---|
44 | GnomeVFSTransform *transform; |
---|
45 | int nusers; |
---|
46 | }; |
---|
47 | typedef struct _ModuleElement ModuleElement; |
---|
48 | |
---|
49 | static gboolean method_already_initialized = FALSE; |
---|
50 | G_LOCK_DEFINE_STATIC (method_already_initialized); |
---|
51 | |
---|
52 | static GHashTable *module_hash = NULL; |
---|
53 | G_LOCK_DEFINE_STATIC (module_hash); |
---|
54 | |
---|
55 | static GList *module_path_list = NULL; |
---|
56 | G_LOCK_DEFINE_STATIC (module_path_list); |
---|
57 | |
---|
58 | |
---|
59 | static gboolean |
---|
60 | init_hash_table (void) |
---|
61 | { |
---|
62 | G_LOCK (module_hash); |
---|
63 | module_hash = g_hash_table_new (g_str_hash, g_str_equal); |
---|
64 | G_UNLOCK (module_hash); |
---|
65 | |
---|
66 | return TRUE; |
---|
67 | } |
---|
68 | |
---|
69 | static gboolean |
---|
70 | install_path_list (const gchar *user_path_list) |
---|
71 | { |
---|
72 | const gchar *p, *oldp; |
---|
73 | |
---|
74 | /* Notice that this assumes the list has already been locked. */ |
---|
75 | |
---|
76 | oldp = user_path_list; |
---|
77 | while (1) { |
---|
78 | gchar *elem; |
---|
79 | |
---|
80 | p = strchr (oldp, ':'); |
---|
81 | |
---|
82 | if (p == NULL) { |
---|
83 | if (*oldp != '\0') { |
---|
84 | elem = g_strdup (oldp); |
---|
85 | module_path_list = g_list_append |
---|
86 | (module_path_list, elem); |
---|
87 | } |
---|
88 | break; |
---|
89 | } else if (p != oldp) { |
---|
90 | elem = g_strndup (oldp, p - oldp); |
---|
91 | module_path_list = g_list_append (module_path_list, |
---|
92 | elem); |
---|
93 | } else { |
---|
94 | elem = NULL; |
---|
95 | } |
---|
96 | |
---|
97 | oldp = p + 1; |
---|
98 | } |
---|
99 | |
---|
100 | return TRUE; |
---|
101 | } |
---|
102 | |
---|
103 | static gboolean |
---|
104 | init_path_list (void) |
---|
105 | { |
---|
106 | const gchar *user_path_list; |
---|
107 | gboolean retval; |
---|
108 | |
---|
109 | retval = TRUE; |
---|
110 | |
---|
111 | G_LOCK (module_path_list); |
---|
112 | |
---|
113 | if (module_path_list != NULL) { |
---|
114 | retval = TRUE; |
---|
115 | goto end; |
---|
116 | } |
---|
117 | |
---|
118 | /* User-supplied path. */ |
---|
119 | |
---|
120 | user_path_list = getenv ("GNOME_VFS_MODULE_PATH"); |
---|
121 | if (user_path_list != NULL) { |
---|
122 | if (! install_path_list (user_path_list)) { |
---|
123 | retval = FALSE; |
---|
124 | goto end; |
---|
125 | } |
---|
126 | } |
---|
127 | |
---|
128 | /* Default path. It comes last so that users can override it. */ |
---|
129 | |
---|
130 | module_path_list = g_list_append (module_path_list, |
---|
131 | g_strdup (GNOME_VFS_MODULE_DIR)); |
---|
132 | |
---|
133 | end: |
---|
134 | G_UNLOCK (module_path_list); |
---|
135 | return retval; |
---|
136 | } |
---|
137 | |
---|
138 | gboolean |
---|
139 | gnome_vfs_method_init (void) |
---|
140 | { |
---|
141 | G_LOCK (method_already_initialized); |
---|
142 | |
---|
143 | if (method_already_initialized) { |
---|
144 | G_UNLOCK (method_already_initialized); |
---|
145 | return TRUE; |
---|
146 | } |
---|
147 | |
---|
148 | if (! init_hash_table ()) |
---|
149 | return FALSE; |
---|
150 | if (! init_path_list ()) |
---|
151 | return FALSE; |
---|
152 | |
---|
153 | method_already_initialized = TRUE; |
---|
154 | G_UNLOCK (method_already_initialized); |
---|
155 | |
---|
156 | return TRUE; |
---|
157 | } |
---|
158 | |
---|
159 | /* Athena hack to work around Solaris #define of "tell" */ |
---|
160 | #undef tell |
---|
161 | |
---|
162 | static void |
---|
163 | load_module (const gchar *module_name, const char *method_name, const char *args, |
---|
164 | GnomeVFSMethod **method, GnomeVFSTransform **transform) |
---|
165 | { |
---|
166 | GModule *module; |
---|
167 | GnomeVFSMethod *temp_method = NULL; |
---|
168 | GnomeVFSTransform *temp_transform = NULL; |
---|
169 | |
---|
170 | GnomeVFSMethodInitFunc init_function = NULL; |
---|
171 | GnomeVFSTransformInitFunc transform_function = NULL; |
---|
172 | GnomeVFSMethodShutdownFunc shutdown_function = NULL; |
---|
173 | |
---|
174 | *method = NULL; |
---|
175 | *transform = NULL; |
---|
176 | |
---|
177 | module = g_module_open (module_name, G_MODULE_BIND_LAZY); |
---|
178 | if (module == NULL) { |
---|
179 | g_warning ("Cannot load module `%s' (%s)", module_name, g_module_error ()); |
---|
180 | return; |
---|
181 | } |
---|
182 | |
---|
183 | g_module_symbol (module, GNOME_VFS_MODULE_INIT, |
---|
184 | (gpointer *) &init_function); |
---|
185 | g_module_symbol (module, GNOME_VFS_MODULE_TRANSFORM, |
---|
186 | (gpointer *) &transform_function); |
---|
187 | g_module_symbol (module, GNOME_VFS_MODULE_SHUTDOWN, |
---|
188 | (gpointer *) &shutdown_function); |
---|
189 | |
---|
190 | if ((init_function == NULL || shutdown_function == NULL) && |
---|
191 | (transform_function == NULL)) { |
---|
192 | g_warning ("module '%s' has no init function; may be an out-of-date module", module_name); |
---|
193 | return; |
---|
194 | } |
---|
195 | |
---|
196 | if (init_function) |
---|
197 | temp_method = (* init_function) (method_name, args); |
---|
198 | |
---|
199 | if (temp_method == NULL && init_function) { |
---|
200 | g_warning ("module '%s' returned a NULL handle", module_name); |
---|
201 | return; |
---|
202 | } |
---|
203 | |
---|
204 | if (temp_method != NULL) { |
---|
205 | /* Some basic checks */ |
---|
206 | if (temp_method->method_table_size == 0) { |
---|
207 | g_warning ("module '%s' has 0 table size", module_name); |
---|
208 | return; |
---|
209 | } else if (temp_method->method_table_size > (0x100 * sizeof (GnomeVFSMethod))) { |
---|
210 | g_warning ("module '%s' has unreasonable table size, perhaps it is using the old GnomeVFSMethod struct?", module_name); |
---|
211 | return; |
---|
212 | } else if (!VFS_METHOD_HAS_FUNC(temp_method, open)) { |
---|
213 | g_warning ("module '%s' has no open fn", module_name); |
---|
214 | return; |
---|
215 | #if 0 |
---|
216 | } else if (!VFS_METHOD_HAS_FUNC(temp_method, create)) { |
---|
217 | g_warning ("module '%s' has no create fn", module_name); |
---|
218 | return; |
---|
219 | #endif |
---|
220 | } else if (!VFS_METHOD_HAS_FUNC(temp_method, is_local)) { |
---|
221 | g_warning ("module '%s' has no is-local fn", module_name); |
---|
222 | return; |
---|
223 | #if 0 |
---|
224 | } else if (!VFS_METHOD_HAS_FUNC(temp_method, get_file_info)) { |
---|
225 | g_warning ("module '%s' has no get-file-info fn", module_name); |
---|
226 | return; |
---|
227 | #endif |
---|
228 | } |
---|
229 | |
---|
230 | /* More advanced assumptions. */ |
---|
231 | if (VFS_METHOD_HAS_FUNC(temp_method, tell) && !VFS_METHOD_HAS_FUNC(temp_method, seek)) { |
---|
232 | g_warning ("module '%s' has tell and no seek", module_name); |
---|
233 | return; |
---|
234 | } |
---|
235 | |
---|
236 | if (VFS_METHOD_HAS_FUNC(temp_method, seek) && !VFS_METHOD_HAS_FUNC(temp_method, tell)) { |
---|
237 | g_warning ("module '%s' has seek and no tell", module_name); |
---|
238 | return; |
---|
239 | } |
---|
240 | } |
---|
241 | |
---|
242 | if (transform_function) |
---|
243 | temp_transform = (* transform_function) (method_name, args); |
---|
244 | if (temp_transform) { |
---|
245 | if (temp_transform->transform == NULL) { |
---|
246 | g_warning ("module '%s' has no transform method", module_name); |
---|
247 | return; |
---|
248 | } |
---|
249 | } |
---|
250 | |
---|
251 | *method = temp_method; |
---|
252 | *transform = temp_transform; |
---|
253 | } |
---|
254 | |
---|
255 | static void |
---|
256 | load_module_in_path_list (const gchar *base_name, const char *method_name, const char *args, |
---|
257 | GnomeVFSMethod **method, GnomeVFSTransform **transform) |
---|
258 | { |
---|
259 | GList *p; |
---|
260 | |
---|
261 | *method = NULL; |
---|
262 | *transform = NULL; |
---|
263 | |
---|
264 | for (p = module_path_list; p != NULL; p = p->next) { |
---|
265 | const gchar *path; |
---|
266 | gchar *name; |
---|
267 | |
---|
268 | path = p->data; |
---|
269 | name = g_module_build_path (path, base_name); |
---|
270 | |
---|
271 | load_module (name, method_name, args, method, transform); |
---|
272 | g_free (name); |
---|
273 | |
---|
274 | if (*method != NULL || *transform != NULL) |
---|
275 | return; |
---|
276 | } |
---|
277 | } |
---|
278 | |
---|
279 | static gboolean |
---|
280 | gnome_vfs_add_module_to_hash_table (const gchar *name) |
---|
281 | { |
---|
282 | GnomeVFSMethod *method = NULL; |
---|
283 | GnomeVFSTransform *transform = NULL; |
---|
284 | ModuleElement *module_element; |
---|
285 | const char *module_name; |
---|
286 | pid_t saved_uid; |
---|
287 | gid_t saved_gid; |
---|
288 | const char *args; |
---|
289 | |
---|
290 | G_LOCK (module_hash); |
---|
291 | module_element = g_hash_table_lookup (module_hash, name); |
---|
292 | G_UNLOCK (module_hash); |
---|
293 | |
---|
294 | if (module_element != NULL) |
---|
295 | return TRUE; |
---|
296 | |
---|
297 | module_name = gnome_vfs_configuration_get_module_path (name, &args); |
---|
298 | if (module_name == NULL) |
---|
299 | return FALSE; |
---|
300 | |
---|
301 | /* Set the effective UID/GID to the user UID/GID to prevent attacks to |
---|
302 | setuid/setgid executables. */ |
---|
303 | |
---|
304 | saved_uid = geteuid (); |
---|
305 | saved_gid = getegid (); |
---|
306 | seteuid (getuid ()); |
---|
307 | setegid (getgid ()); |
---|
308 | |
---|
309 | if (g_path_is_absolute (module_name)) |
---|
310 | load_module (module_name, name, args, &method, &transform); |
---|
311 | else |
---|
312 | load_module_in_path_list (module_name, name, args, &method, &transform); |
---|
313 | |
---|
314 | seteuid (saved_uid); |
---|
315 | setegid (saved_gid); |
---|
316 | |
---|
317 | if (method == NULL && transform == NULL) |
---|
318 | return FALSE; |
---|
319 | |
---|
320 | module_element = g_new (ModuleElement, 1); |
---|
321 | module_element->name = g_strdup (name); |
---|
322 | module_element->method = method; |
---|
323 | module_element->transform = transform; |
---|
324 | |
---|
325 | G_LOCK (module_hash); |
---|
326 | g_hash_table_insert (module_hash, module_element->name, module_element); |
---|
327 | G_UNLOCK (module_hash); |
---|
328 | |
---|
329 | return TRUE; |
---|
330 | } |
---|
331 | |
---|
332 | GnomeVFSMethod * |
---|
333 | gnome_vfs_method_get (const gchar *name) |
---|
334 | { |
---|
335 | ModuleElement *module_element; |
---|
336 | |
---|
337 | g_return_val_if_fail (name != NULL, NULL); |
---|
338 | |
---|
339 | G_LOCK (module_hash); |
---|
340 | module_element = g_hash_table_lookup (module_hash, name); |
---|
341 | G_UNLOCK (module_hash); |
---|
342 | |
---|
343 | if (module_element != NULL) |
---|
344 | return module_element->method; |
---|
345 | |
---|
346 | if (gnome_vfs_add_module_to_hash_table (name)) { |
---|
347 | G_LOCK (module_hash); |
---|
348 | module_element = g_hash_table_lookup (module_hash, name); |
---|
349 | G_UNLOCK (module_hash); |
---|
350 | |
---|
351 | if (module_element != NULL) |
---|
352 | return module_element->method; |
---|
353 | } |
---|
354 | |
---|
355 | return NULL; |
---|
356 | } |
---|
357 | |
---|
358 | GnomeVFSTransform * |
---|
359 | gnome_vfs_transform_get (const gchar *name) |
---|
360 | { |
---|
361 | ModuleElement *module_element; |
---|
362 | |
---|
363 | g_return_val_if_fail (name != NULL, NULL); |
---|
364 | |
---|
365 | G_LOCK (module_hash); |
---|
366 | module_element = g_hash_table_lookup (module_hash, name); |
---|
367 | G_UNLOCK (module_hash); |
---|
368 | |
---|
369 | if (module_element != NULL) |
---|
370 | return module_element->transform; |
---|
371 | |
---|
372 | if (gnome_vfs_add_module_to_hash_table (name)) { |
---|
373 | G_LOCK (module_hash); |
---|
374 | module_element = g_hash_table_lookup (module_hash, name); |
---|
375 | G_UNLOCK (module_hash); |
---|
376 | |
---|
377 | if (module_element != NULL) |
---|
378 | return module_element->transform; |
---|
379 | } |
---|
380 | |
---|
381 | return NULL; |
---|
382 | } |
---|