Branch data Line data Source code
1 : : /* GIO - GLib Input, Output and Streaming Library
2 : : *
3 : : * Copyright (C) 2006-2007 Red Hat, Inc.
4 : : *
5 : : * SPDX-License-Identifier: LGPL-2.1-or-later
6 : : *
7 : : * This library is free software; you can redistribute it and/or
8 : : * modify it under the terms of the GNU Lesser General Public
9 : : * License as published by the Free Software Foundation; either
10 : : * version 2.1 of the License, or (at your option) any later version.
11 : : *
12 : : * This 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 : : * Lesser General Public License for more details.
16 : : *
17 : : * You should have received a copy of the GNU Lesser General
18 : : * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
19 : : *
20 : : * Author: Alexander Larsson <alexl@redhat.com>
21 : : */
22 : :
23 : : #include "config.h"
24 : : #include <string.h>
25 : : #include "gvfs.h"
26 : : #include "glib-private.h"
27 : : #include "glocalvfs.h"
28 : : #include "gresourcefile.h"
29 : : #include "giomodule-priv.h"
30 : : #include "glibintl.h"
31 : :
32 : :
33 : : /**
34 : : * GVfs:
35 : : *
36 : : * Entry point for using GIO functionality.
37 : : */
38 : :
39 : : static GRWLock additional_schemes_lock;
40 : :
41 : : typedef struct _GVfsPrivate {
42 : : GHashTable *additional_schemes;
43 : : char const **supported_schemes;
44 : : } GVfsPrivate;
45 : :
46 : : typedef struct {
47 : : GVfsFileLookupFunc uri_func;
48 : : gpointer uri_data;
49 : : GDestroyNotify uri_destroy;
50 : :
51 : : GVfsFileLookupFunc parse_name_func;
52 : : gpointer parse_name_data;
53 : : GDestroyNotify parse_name_destroy;
54 : : } GVfsURISchemeData;
55 : :
56 : 1616 : G_DEFINE_TYPE_WITH_PRIVATE (GVfs, g_vfs, G_TYPE_OBJECT)
57 : :
58 : : static void
59 : 1 : g_vfs_dispose (GObject *object)
60 : : {
61 : 1 : GVfs *vfs = G_VFS (object);
62 : 1 : GVfsPrivate *priv = g_vfs_get_instance_private (vfs);
63 : :
64 : 1 : g_clear_pointer (&priv->additional_schemes, g_hash_table_destroy);
65 : 1 : g_clear_pointer (&priv->supported_schemes, g_free);
66 : :
67 : 1 : G_OBJECT_CLASS (g_vfs_parent_class)->dispose (object);
68 : 1 : }
69 : :
70 : : static void
71 : 34 : g_vfs_class_init (GVfsClass *klass)
72 : : {
73 : 34 : GObjectClass *object_class = G_OBJECT_CLASS (klass);
74 : 34 : object_class->dispose = g_vfs_dispose;
75 : 34 : }
76 : :
77 : : static GFile *
78 : 6 : resource_parse_name (GVfs *vfs,
79 : : const char *parse_name,
80 : : gpointer user_data)
81 : : {
82 : 6 : if (g_str_has_prefix (parse_name, "resource:"))
83 : 0 : return _g_resource_file_new (parse_name);
84 : :
85 : 6 : return NULL;
86 : : }
87 : :
88 : : static GFile *
89 : 19 : resource_get_file_for_uri (GVfs *vfs,
90 : : const char *uri,
91 : : gpointer user_data)
92 : : {
93 : 19 : return _g_resource_file_new (uri);
94 : : }
95 : :
96 : : static void
97 : 2 : g_vfs_uri_lookup_func_closure_free (gpointer data)
98 : : {
99 : 2 : GVfsURISchemeData *closure = data;
100 : :
101 : 2 : if (closure->uri_destroy)
102 : 0 : closure->uri_destroy (closure->uri_data);
103 : 2 : if (closure->parse_name_destroy)
104 : 0 : closure->parse_name_destroy (closure->parse_name_data);
105 : :
106 : 2 : g_free (closure);
107 : 2 : }
108 : :
109 : : static void
110 : 34 : g_vfs_init (GVfs *vfs)
111 : : {
112 : 34 : GVfsPrivate *priv = g_vfs_get_instance_private (vfs);
113 : 34 : priv->additional_schemes =
114 : 34 : g_hash_table_new_full (g_str_hash, g_str_equal,
115 : : g_free, g_vfs_uri_lookup_func_closure_free);
116 : :
117 : 34 : g_vfs_register_uri_scheme (vfs, "resource",
118 : : resource_get_file_for_uri, NULL, NULL,
119 : : resource_parse_name, NULL, NULL);
120 : 34 : }
121 : :
122 : : /**
123 : : * g_vfs_is_active:
124 : : * @vfs: a #GVfs.
125 : : *
126 : : * Checks if the VFS is active.
127 : : *
128 : : * Returns: %TRUE if construction of the @vfs was successful
129 : : * and it is now active.
130 : : */
131 : : gboolean
132 : 35 : g_vfs_is_active (GVfs *vfs)
133 : : {
134 : : GVfsClass *class;
135 : :
136 : 35 : g_return_val_if_fail (G_IS_VFS (vfs), FALSE);
137 : :
138 : 35 : class = G_VFS_GET_CLASS (vfs);
139 : :
140 : 35 : return (* class->is_active) (vfs);
141 : : }
142 : :
143 : :
144 : : /**
145 : : * g_vfs_get_file_for_path:
146 : : * @vfs: a #GVfs.
147 : : * @path: a string containing a VFS path.
148 : : *
149 : : * Gets a #GFile for @path.
150 : : *
151 : : * Returns: (transfer full): a #GFile.
152 : : * Free the returned object with g_object_unref().
153 : : */
154 : : GFile *
155 : 405 : g_vfs_get_file_for_path (GVfs *vfs,
156 : : const char *path)
157 : : {
158 : : GVfsClass *class;
159 : :
160 : 405 : g_return_val_if_fail (G_IS_VFS (vfs), NULL);
161 : 405 : g_return_val_if_fail (path != NULL, NULL);
162 : :
163 : 405 : class = G_VFS_GET_CLASS (vfs);
164 : :
165 : 405 : return (* class->get_file_for_path) (vfs, path);
166 : : }
167 : :
168 : : static GFile *
169 : 7 : parse_name_internal (GVfs *vfs,
170 : : const char *parse_name)
171 : : {
172 : 7 : GVfsPrivate *priv = g_vfs_get_instance_private (vfs);
173 : : GHashTableIter iter;
174 : : GVfsURISchemeData *closure;
175 : 7 : GFile *ret = NULL;
176 : :
177 : 7 : g_rw_lock_reader_lock (&additional_schemes_lock);
178 : 7 : g_hash_table_iter_init (&iter, priv->additional_schemes);
179 : :
180 : 13 : while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &closure))
181 : : {
182 : 7 : ret = closure->parse_name_func (vfs, parse_name,
183 : 7 : closure->parse_name_data);
184 : :
185 : 7 : if (ret)
186 : 1 : break;
187 : : }
188 : :
189 : 7 : g_rw_lock_reader_unlock (&additional_schemes_lock);
190 : :
191 : 7 : return ret;
192 : : }
193 : :
194 : : static GFile *
195 : 301 : get_file_for_uri_internal (GVfs *vfs,
196 : : const char *uri)
197 : : {
198 : 301 : GVfsPrivate *priv = g_vfs_get_instance_private (vfs);
199 : 301 : GFile *ret = NULL;
200 : : char *scheme;
201 : : GVfsURISchemeData *closure;
202 : :
203 : 301 : scheme = g_uri_parse_scheme (uri);
204 : 301 : if (scheme == NULL)
205 : 1 : return NULL;
206 : :
207 : 300 : g_rw_lock_reader_lock (&additional_schemes_lock);
208 : 300 : closure = g_hash_table_lookup (priv->additional_schemes, scheme);
209 : :
210 : 300 : if (closure)
211 : 20 : ret = closure->uri_func (vfs, uri, closure->uri_data);
212 : :
213 : 300 : g_rw_lock_reader_unlock (&additional_schemes_lock);
214 : :
215 : 300 : g_free (scheme);
216 : 300 : return ret;
217 : : }
218 : :
219 : : /**
220 : : * g_vfs_get_file_for_uri:
221 : : * @vfs: a#GVfs.
222 : : * @uri: a string containing a URI
223 : : *
224 : : * Gets a #GFile for @uri.
225 : : *
226 : : * This operation never fails, but the returned object
227 : : * might not support any I/O operation if the URI
228 : : * is malformed or if the URI scheme is not supported.
229 : : *
230 : : * Returns: (transfer full): a #GFile.
231 : : * Free the returned object with g_object_unref().
232 : : */
233 : : GFile *
234 : 301 : g_vfs_get_file_for_uri (GVfs *vfs,
235 : : const char *uri)
236 : : {
237 : : GVfsClass *class;
238 : 301 : GFile *ret = NULL;
239 : :
240 : 301 : g_return_val_if_fail (G_IS_VFS (vfs), NULL);
241 : 301 : g_return_val_if_fail (uri != NULL, NULL);
242 : :
243 : 301 : class = G_VFS_GET_CLASS (vfs);
244 : :
245 : 301 : ret = get_file_for_uri_internal (vfs, uri);
246 : 301 : if (!ret)
247 : 282 : ret = (* class->get_file_for_uri) (vfs, uri);
248 : :
249 : 301 : g_assert (ret != NULL);
250 : :
251 : 301 : return g_steal_pointer (&ret);
252 : : }
253 : :
254 : : /**
255 : : * g_vfs_get_supported_uri_schemes:
256 : : * @vfs: a #GVfs.
257 : : *
258 : : * Gets a list of URI schemes supported by @vfs.
259 : : *
260 : : * Returns: (transfer none): a %NULL-terminated array of strings.
261 : : * The returned array belongs to GIO and must
262 : : * not be freed or modified.
263 : : */
264 : : const gchar * const *
265 : 3 : g_vfs_get_supported_uri_schemes (GVfs *vfs)
266 : : {
267 : : GVfsPrivate *priv;
268 : :
269 : 3 : g_return_val_if_fail (G_IS_VFS (vfs), NULL);
270 : :
271 : 3 : priv = g_vfs_get_instance_private (vfs);
272 : :
273 : 3 : if (!priv->supported_schemes)
274 : : {
275 : : GVfsClass *class;
276 : : const char * const *default_schemes;
277 : : const char *additional_scheme;
278 : : GPtrArray *supported_schemes;
279 : : GHashTableIter iter;
280 : :
281 : 3 : class = G_VFS_GET_CLASS (vfs);
282 : :
283 : 3 : default_schemes = (* class->get_supported_uri_schemes) (vfs);
284 : 3 : supported_schemes = g_ptr_array_new ();
285 : :
286 : 6 : for (; default_schemes && *default_schemes; default_schemes++)
287 : 3 : g_ptr_array_add (supported_schemes, (gpointer) *default_schemes);
288 : :
289 : 3 : g_rw_lock_reader_lock (&additional_schemes_lock);
290 : 3 : g_hash_table_iter_init (&iter, priv->additional_schemes);
291 : :
292 : 7 : while (g_hash_table_iter_next
293 : : (&iter, (gpointer *) &additional_scheme, NULL))
294 : 4 : g_ptr_array_add (supported_schemes, (gpointer) additional_scheme);
295 : :
296 : 3 : g_rw_lock_reader_unlock (&additional_schemes_lock);
297 : :
298 : 3 : g_ptr_array_add (supported_schemes, NULL);
299 : :
300 : 3 : g_free (priv->supported_schemes);
301 : 3 : priv->supported_schemes =
302 : 3 : (char const **) g_ptr_array_free (supported_schemes, FALSE);
303 : : }
304 : :
305 : 3 : return priv->supported_schemes;
306 : : }
307 : :
308 : : /**
309 : : * g_vfs_parse_name:
310 : : * @vfs: a #GVfs.
311 : : * @parse_name: a string to be parsed by the VFS module.
312 : : *
313 : : * This operation never fails, but the returned object might
314 : : * not support any I/O operations if the @parse_name cannot
315 : : * be parsed by the #GVfs module.
316 : : *
317 : : * Returns: (transfer full): a #GFile for the given @parse_name.
318 : : * Free the returned object with g_object_unref().
319 : : */
320 : : GFile *
321 : 7 : g_vfs_parse_name (GVfs *vfs,
322 : : const char *parse_name)
323 : : {
324 : : GVfsClass *class;
325 : : GFile *ret;
326 : :
327 : 7 : g_return_val_if_fail (G_IS_VFS (vfs), NULL);
328 : 7 : g_return_val_if_fail (parse_name != NULL, NULL);
329 : :
330 : 7 : class = G_VFS_GET_CLASS (vfs);
331 : :
332 : 7 : ret = parse_name_internal (vfs, parse_name);
333 : 7 : if (ret)
334 : 1 : return ret;
335 : :
336 : 6 : return (* class->parse_name) (vfs, parse_name);
337 : : }
338 : :
339 : : static GVfs *vfs_default_singleton = NULL; /* (owned) (atomic) */
340 : :
341 : : /**
342 : : * g_vfs_get_default:
343 : : *
344 : : * Gets the default #GVfs for the system.
345 : : *
346 : : * Returns: (not nullable) (transfer none): a #GVfs, which will be the local
347 : : * file system #GVfs if no other implementation is available.
348 : : */
349 : : GVfs *
350 : 1924 : g_vfs_get_default (void)
351 : : {
352 : 1924 : if (GLIB_PRIVATE_CALL (g_check_setuid) ())
353 : 0 : return g_vfs_get_local ();
354 : :
355 : 1924 : if (g_once_init_enter_pointer (&vfs_default_singleton))
356 : : {
357 : : GVfs *singleton;
358 : :
359 : 32 : singleton = _g_io_module_get_default (G_VFS_EXTENSION_POINT_NAME,
360 : : "GIO_USE_VFS",
361 : : (GIOModuleVerifyFunc) g_vfs_is_active);
362 : :
363 : 32 : g_once_init_leave_pointer (&vfs_default_singleton, singleton);
364 : : }
365 : :
366 : 1924 : return vfs_default_singleton;
367 : : }
368 : :
369 : : /**
370 : : * g_vfs_get_local:
371 : : *
372 : : * Gets the local #GVfs for the system.
373 : : *
374 : : * Returns: (transfer none): a #GVfs.
375 : : */
376 : : GVfs *
377 : 2 : g_vfs_get_local (void)
378 : : {
379 : : static GVfs *vfs = 0;
380 : :
381 : 2 : if (g_once_init_enter_pointer (&vfs))
382 : 1 : g_once_init_leave_pointer (&vfs, _g_local_vfs_new ());
383 : :
384 : 2 : return vfs;
385 : : }
386 : :
387 : : /**
388 : : * g_vfs_register_uri_scheme:
389 : : * @vfs: a #GVfs
390 : : * @scheme: an URI scheme, e.g. "http"
391 : : * @uri_func: (scope notified) (nullable): a #GVfsFileLookupFunc
392 : : * @uri_data: (nullable): custom data passed to be passed to @uri_func, or %NULL
393 : : * @uri_destroy: (nullable): function to be called when unregistering the
394 : : * URI scheme, or when @vfs is disposed, to free the resources used
395 : : * by the URI lookup function
396 : : * @parse_name_func: (scope notified) (nullable): a #GVfsFileLookupFunc
397 : : * @parse_name_data: (nullable): custom data passed to be passed to
398 : : * @parse_name_func, or %NULL
399 : : * @parse_name_destroy: (nullable): function to be called when unregistering the
400 : : * URI scheme, or when @vfs is disposed, to free the resources used
401 : : * by the parse name lookup function
402 : : *
403 : : * Registers @uri_func and @parse_name_func as the #GFile URI and parse name
404 : : * lookup functions for URIs with a scheme matching @scheme.
405 : : * Note that @scheme is registered only within the running application, as
406 : : * opposed to desktop-wide as it happens with GVfs backends.
407 : : *
408 : : * When a #GFile is requested with an URI containing @scheme (e.g. through
409 : : * g_file_new_for_uri()), @uri_func will be called to allow a custom
410 : : * constructor. The implementation of @uri_func should not be blocking, and
411 : : * must not call g_vfs_register_uri_scheme() or g_vfs_unregister_uri_scheme().
412 : : *
413 : : * When g_file_parse_name() is called with a parse name obtained from such file,
414 : : * @parse_name_func will be called to allow the #GFile to be created again. In
415 : : * that case, it's responsibility of @parse_name_func to make sure the parse
416 : : * name matches what the custom #GFile implementation returned when
417 : : * g_file_get_parse_name() was previously called. The implementation of
418 : : * @parse_name_func should not be blocking, and must not call
419 : : * g_vfs_register_uri_scheme() or g_vfs_unregister_uri_scheme().
420 : : *
421 : : * It's an error to call this function twice with the same scheme. To unregister
422 : : * a custom URI scheme, use g_vfs_unregister_uri_scheme().
423 : : *
424 : : * Returns: %TRUE if @scheme was successfully registered, or %FALSE if a handler
425 : : * for @scheme already exists.
426 : : *
427 : : * Since: 2.50
428 : : */
429 : : gboolean
430 : 36 : g_vfs_register_uri_scheme (GVfs *vfs,
431 : : const char *scheme,
432 : : GVfsFileLookupFunc uri_func,
433 : : gpointer uri_data,
434 : : GDestroyNotify uri_destroy,
435 : : GVfsFileLookupFunc parse_name_func,
436 : : gpointer parse_name_data,
437 : : GDestroyNotify parse_name_destroy)
438 : : {
439 : : GVfsPrivate *priv;
440 : : GVfsURISchemeData *closure;
441 : :
442 : 36 : g_return_val_if_fail (G_IS_VFS (vfs), FALSE);
443 : 36 : g_return_val_if_fail (scheme != NULL, FALSE);
444 : :
445 : 36 : priv = g_vfs_get_instance_private (vfs);
446 : :
447 : 36 : g_rw_lock_reader_lock (&additional_schemes_lock);
448 : 36 : closure = g_hash_table_lookup (priv->additional_schemes, scheme);
449 : 36 : g_rw_lock_reader_unlock (&additional_schemes_lock);
450 : :
451 : 36 : if (closure != NULL)
452 : 1 : return FALSE;
453 : :
454 : 35 : closure = g_new0 (GVfsURISchemeData, 1);
455 : 35 : closure->uri_func = uri_func;
456 : 35 : closure->uri_data = uri_data;
457 : 35 : closure->uri_destroy = uri_destroy;
458 : 35 : closure->parse_name_func = parse_name_func;
459 : 35 : closure->parse_name_data = parse_name_data;
460 : 35 : closure->parse_name_destroy = parse_name_destroy;
461 : :
462 : 35 : g_rw_lock_writer_lock (&additional_schemes_lock);
463 : 35 : g_hash_table_insert (priv->additional_schemes, g_strdup (scheme), closure);
464 : 35 : g_rw_lock_writer_unlock (&additional_schemes_lock);
465 : :
466 : : /* Invalidate supported schemes */
467 : 35 : g_clear_pointer (&priv->supported_schemes, g_free);
468 : :
469 : 35 : return TRUE;
470 : : }
471 : :
472 : : /**
473 : : * g_vfs_unregister_uri_scheme:
474 : : * @vfs: a #GVfs
475 : : * @scheme: an URI scheme, e.g. "http"
476 : : *
477 : : * Unregisters the URI handler for @scheme previously registered with
478 : : * g_vfs_register_uri_scheme().
479 : : *
480 : : * Returns: %TRUE if @scheme was successfully unregistered, or %FALSE if a
481 : : * handler for @scheme does not exist.
482 : : *
483 : : * Since: 2.50
484 : : */
485 : : gboolean
486 : 2 : g_vfs_unregister_uri_scheme (GVfs *vfs,
487 : : const char *scheme)
488 : : {
489 : : GVfsPrivate *priv;
490 : : gboolean res;
491 : :
492 : 2 : g_return_val_if_fail (G_IS_VFS (vfs), FALSE);
493 : 2 : g_return_val_if_fail (scheme != NULL, FALSE);
494 : :
495 : 2 : priv = g_vfs_get_instance_private (vfs);
496 : :
497 : 2 : g_rw_lock_writer_lock (&additional_schemes_lock);
498 : 2 : res = g_hash_table_remove (priv->additional_schemes, scheme);
499 : 2 : g_rw_lock_writer_unlock (&additional_schemes_lock);
500 : :
501 : 2 : if (res)
502 : : {
503 : : /* Invalidate supported schemes */
504 : 1 : g_clear_pointer (&priv->supported_schemes, g_free);
505 : :
506 : 1 : return TRUE;
507 : : }
508 : :
509 : 1 : return FALSE;
510 : : }
|