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

source code of gtk/subprojects/glib/gio/gvfs.c