1 <refentry id="gnome-vfs-writing-modules" revision="6 Sep 2001">
3 <refentrytitle>Writing Modules</refentrytitle>
4 <manvolnum>3</manvolnum>
5 <refmiscinfo>GNOME-VFS Library</refmiscinfo>
9 <refname>Writing Modules</refname><refpurpose>basic gnome-vfs module concepts</refpurpose>
12 <refsect1 id="Introduction">
13 <title>Introduction</title>
15 <para>This section will introduce the basic concepts that are
16 needed for writing GNOME Virtual File System modules.</para>
19 <title>GNOME VFS URIs (Uniform Resource Identifiers)</title>
21 <para>The GNOME Virtual file system uses URIs similiar to the
22 standard WWW URIs. The basic difference between a VFS URI and
23 WWW URI is that, while with WWW URIs you can only use a single
24 protocol for accessing a certain file, with GNOME VFS URIs you
25 can combine different access methods in sequence.</para>
27 <para>For example, suppose you want to access file
28 <filename>hello.c</filename> in a <filename>tar.gz</filename>
29 file which is in turn accessible through FTP from a remote
30 machine. In order to access this file, you would need to:</para>
33 <listitem><para>Connect to the FTP site.</para></listitem>
35 <listitem><para>Fetch the <filename>tar.gz</filename>
36 file.</para></listitem>
38 <listitem><para>Decompress the <filename>tar.gz</filename> file using
39 GZIP.</para></listitem>
41 <listitem><para>Extract <filename>hello.c</filename> from the resulting
42 uncompressed <filename>tar</filename> file.</para></listitem>
45 <para>The GNOME Virtual File System lets you express this by
46 combining the three access methods (i.e. tar, GZIP and FTP)
47 into a single URI. Access methods are combined in the URI by
48 using the `#' character, followed by the name for the access
49 method and the subpath for that specific access method. The
50 subpath can be omitted for those storage methods that do not
51 need a path to retrieve the file. (For example, a GZIP file
52 always contains a single uncompressed file, so no path is
53 needed to locate the uncompressed file within the GZIP file.
54 But on the other hand, the TAR method requires a path to
55 locate a specific file or directory.)</para>
57 <para>For example, in the case we outlined above, the URI would
58 look something like:</para>
60 <!-- FixMe what should I use here instead of programlisting? -->
63 ftp://username:password@host.net/path/to/file.tar.gz#gzip#tar/path/to/hello.c</programlisting>
65 <para>Each method/subpath couple is called a <firstterm>URI
66 element</firstterm>. When URI elements are combined like this,
67 each URI element uses the previous one to access a base resource
68 into which it will look up a file, using the subpath
69 information. For this reason, we will say that each element is
70 the <firstterm>parent</firstterm> element for the following one.</para>
72 <para>The first URI element, the one which has no parent, is
73 called the <firstterm>toplevel element</firstterm>. It does not
74 use the `#' character; instead, it uses the standard syntax of
79 method://user:password@host/path/to/file</programlisting>
81 <para>This way, normal WWW URIs can be used with the GNOME Virtual
84 <para>Toplevel elements are also special because they let users
85 specify user names, passwords and host names, while
86 non-toplevel elements don't.</para>
90 <title>The <structname>GnomeVFSURI</structname> type</title>
92 <para>Within the GNOME Virtual File System library, URI elements
93 are represented by a special type,
94 <structname>GnomeVFSURI</structname>, which is meant to represent
95 user-provided URIs in a machine-optimized way. </para>
97 <para>Every <structname>GnomeVFSURI</structname> contains the
98 following information:</para>
101 <listitem><para>A reference counter</para></listitem>
103 <listitem><para>A pointer to the parent
104 <structname>GnomeVFSURI</structname> URI element.</para></listitem>
106 <listitem><para>The subpath.</para></listitem>
108 <listitem><para>The name of the access method.</para></listitem>
110 <listitem><para>A pointer to a
111 <structname>GnomeVFSMethod</structname> object, describing the
112 access method (see below).</para></listitem>
120 <title>GNOME Virtual File System access method implementation</title>
122 <para>In the GNOME Virtual File System, the implementations for
123 all the access methods are loaded at runtime, as shared library
124 modules. The modules are loaded during parsing of the string URI:
125 if the parser encounters an access method for which no
126 implementation is currently loaded, it retrieves the corresponding
127 library file, dynamically links it into the executable, and
128 initializes it.</para>
130 <para>After initialization, the module returns a special
131 <structname>GnomeVFSMethod</structname> object that contains
132 pointers to the various implementation functions for that specific
133 method. By storing a pointer to this object into the
134 <structname>GnomeVFSURI</structname> type, the VFS library is then
135 able to use these functions for file access.</para>
138 <title>How file access is performed</title>
140 <para>When the VFS library needs to perform some file operation,
141 it performs the following steps:</para>
145 <listitem><para>If the URI is given in textual form (i.e. as a
146 string), it parses it and activates the necessary access method
147 modules.</para></listitem>
149 <listitem><para>It retrieves a pointer to the lowmost
150 level URI element.</para></listitem>
152 <listitem><para>It retrieves a pointer to the
153 <structname>GnomeVFSMethod</structname> object that corresponds
154 to the access method for that URI element.</para></listitem>
156 <listitem><para>It retrieves a pointer to the implementation
157 function for that operation from the
158 <structname>GnomeVFSMethod</structname>object.</para></listitem>
160 <listitem><para>It invokes that implementation function
161 passing the pointer to the lowmost level URI
162 element.</para></listitem>
166 <para>Combining the access methods is always done within the
167 method implementation. If the method implementation needs to do
168 some file operation on the the parent URI element, it can do so
169 by simply invoking the corresponding VFS function in, by using
170 the parent pointer in the <structname>GnomeVFSURI</structname>
173 <para>For example, suppose you have to read a simple URI like
174 the following:</para>
176 <!-- FixMe what should I use here instead of programlisting? -->
179 file:/home/ettore/file.gz#gzip</programlisting>
181 <para>In this case, the GZIP method will be invoked with a
182 pointer to the <structname>GnomeVFSURI</structname> describing the
183 `gzip' part; then the GZIP method will be able to read
184 <filename>file.gz</filename> by just invoking the corresponding
185 GNOME VFS library function on its parent, and decompress it on
193 <title>Implementing an access method in practice</title>
195 <para>Implementing a new access method is really not difficult at
196 all. This section explains how this is done.</para>
199 <title>Using shared libraries</title>
201 <para>Every module must be compiled as a shared library (i.e. a
202 <filename>.so</filename> file).</para>
204 <para>The current way for accessing the right module for the
205 right method is very simple, and is based on file names. In
206 practice, a module implementing access method named
207 <filename>foo</filename> must be named
208 <filename>libfoo.so</filename>. For example, the module
209 implementing the <filename>ftp:</filename> access method is
210 called <filename>libftp.so</filename>, the module implementing
211 <filename>#gzip</filename> access is called
212 <filename>libgzip.so</filename> and so on.</para>
214 <para>This might change in the future.</para>
219 <title>The initialization/shutdown functions</title>
221 <para>Every shared library module must provide two functions:</para>
223 <programlisting role="c">
225 GnomeVFSMethod *vfs_module_init (void);
226 void vfs_module_shutdown (GnomeVFSMethod *method);</programlisting>
228 <para>These are the only functions that the VFS library will
229 access directly. All the other symbols (i.e. functions and
230 variables) in the module should be made static. </para>
232 <para><function>vfs_module_init()</function> is called
233 as soon as the module is loaded in memory. It will have to
234 return a pointer to a <structname>GnomeVFSMethod</structname>
235 object that will contain the pointers to the method's
236 implementation functions. We will describe this later. </para>
238 <para><function>vfs_module_shutdown</function>, instead,
239 is called before the module is unloaded or the program that uses
240 it dies. This functions should:</para>
244 <listitem><para>Deallocate all the memory allocated by the
245 module.</para></listitem>
247 <listitem><para>Close all the file descriptors associated with
248 the module.</para></listitem>
250 <listitem><para>Kill any external process spawned by the
251 module.</para></listitem>
253 <listitem><para>In general, make sure that any operation that
254 was going on before this function was called will be
255 interrupted correctly, as soon as possible and without any
256 leaks.</para></listitem>
263 <title>The <structname>GnomeVFSMethod</structname> object</title>
265 <para>This object is contains pointers to the module
266 implementation functions.</para>
268 <programlisting role="c">
270 GnomeVFSResult (* open) (GnomeVFSMethodHandle **method_handle_return,
272 GnomeVFSOpenMode mode
273 GnomeVFSCancellation *cancellation);
275 GnomeVFSResult (* create) (GnomeVFSMethodHandle **method_handle_return,
277 GnomeVFSOpenMode mode,
280 GnomeVFSCancellation *cancellation);
282 GnomeVFSResult (* close) (GnomeVFSMethodHandle *method_handle
283 GnomeVFSCancellation *cancellation);
285 GnomeVFSResult (* read) (GnomeVFSMethodHandle *method_handle,
287 GnomeVFSFileSize num_bytes,
288 GnomeVFSFileSize *bytes_read_return
289 GnomeVFSCancellation *cancellation);
291 GnomeVFSResult (* write) (GnomeVFSMethodHandle *method_handle,
292 gconstpointer buffer,
293 GnomeVFSFileSize num_bytes,
294 GnomeVFSFileSize *bytes_written_return
295 GnomeVFSCancellation *cancellation);
297 GnomeVFSResult (* seek) (GnomeVFSMethodHandle *method_handle,
298 GnomeVFSSeekPosition whence,
299 GnomeVFSFileOffset offset
300 GnomeVFSCancellation *cancellation);
302 GnomeVFSResult (* tell) (GnomeVFSMethodHandle *method_handle,
303 GnomeVFSFileOffset *offset_return);
305 GnomeVFSResult (* truncate) (GnomeVFSMethodHandle *method_handle,
306 GnomeVFSFileSize where
307 GnomeVFSCancellation *cancellation);
309 GnomeVFSResult (* open_directory) (GnomeVFSMethodHandle **method_handle,
311 GnomeVFSFileInfoOptions options,
312 const GList *meta_keys,
313 const GnomeVFSDirectoryFilter *filter
314 GnomeVFSCancellation *cancellation);
316 GnomeVFSResult (* close_directory) (GnomeVFSMethodHandle *method_handle
317 GnomeVFSCancellation *cancellation);
319 GnomeVFSResult (* read_directory) (GnomeVFSMethodHandle *method_handle,
320 GnomeVFSFileInfo *file_info
321 GnomeVFSCancellation *cancellation);
323 GnomeVFSResult (* get_file_info) (GnomeVFSURI *uri,
324 GnomeVFSFileInfo *file_info,
325 GnomeVFSFileInfoOptions options,
326 const GList *meta_keys
327 GnomeVFSCancellation *cancellation);
329 GnomeVFSResult (* get_file_info_from_handle)
330 (GnomeVFSMethodHandle *method_handle,
331 GnomeVFSFileInfo *file_info,
332 GnomeVFSFileInfoOptions options,
333 const GList *meta_keys
334 GnomeVFSCancellation *cancellation);
336 gboolean (* is_local) (const GnomeVFSURI *uri
337 GnomeVFSCancellation *cancellation);
339 GnomeVFSResult (* rename) (GnomeVFSURI *uri, const gchar *new_name
340 GnomeVFSCancellation *cancellation);
342 GnomeVFSResult (* make_directory) (GnomeVFSURI *uri, guint perm
343 GnomeVFSCancellation *cancellation);
345 GnomeVFSResult (* remove_directory) (GnomeVFSURI *uri
346 GnomeVFSCancellation *cancellation);
348 GnomeVFSResult (* unlink) (GnomeVFSURI *uri
349 GnomeVFSCancellation *cancellation);
357 <title>Handling cancellation</title>
359 <para>As VFS operations might be very long, especially in the case
360 of transient errors (such as a network server that has gone down),
361 the GNOME Virtual File System Library provides a standard way for
362 handling cancellation of VFS operations.</para>
365 <title>The <structname>GnomeVFSCancellation</structname> object</title>
367 <para>The object that encapsulates this functionality is
368 <structname>GnomeVFSCancellation</structname>. Most
369 implementation functions get a pointer to such an object, and are
370 expected to use this object to recognize when an operation should
371 be interrupted.</para>
373 <para>The most simple way to check for a cancellation request is
374 to poll the object with
375 <function>gnome_vfs_cancellation_check()</function>:</para>
377 <programlisting role="c">
379 gboolean gnome_vfs_cancellation_check (GnomeVFSCancellation *cancellation);</programlisting>
381 <para>This function will return a nonzero value if the current
382 operation should be cancelled.</para>
384 <para>Notice that cancellation is an asynchronous operation that
385 might happen outside your function, in parallel with the code that
386 you are writing. For example, in the case of threads, the request
387 will be set in the master thread; in the case of slave
388 CORBA-driven processes, the request will be activated by a Unix
389 signal. So you can expect a cancellation request to happen (and
390 consequently be signalled in
391 <structname>GnomeVFSCancellation</structname>) at any time.</para>
393 <para>For this reason, you should be calling this function
394 periodically, whenever you are going to perform several
395 iterations of the same task, or execute a single expensive task.
396 When the function returns a nonzero value, the correct way to
400 <listitem><para>Clean things up so that the result of the
401 operations that have been performed are all
402 cancelled.</para></listitem>
403 <listitem><para>Return the
404 <symbol>GNOME_VFS_ERROR_CANCELLED</symbol> error
405 code.</para></listitem>
408 <para>But there are some other situations in which you want to
409 be able to interrupt a I/O operation when a cancellation request
410 is performed. In such cases, polling is not a viable option.</para>
412 <para>For this reason,
413 <structname>GnomeVFSCancellation</structname> provides an
414 alternative way of sending notifications, using a file
415 descriptor. To use this feature, you should use the following
420 gint gnome_vfs_cancellation_get_fd (GnomeVFSCancellation *cancellation); </programlisting>
422 <para>When this function is called, it will return an open file
423 descriptor, which is the read-side of a pipe. The pipe will be
424 given a character from the write side as soon as a cancellation
425 request is sent. For this reason, you can check for a
426 cancellation by using the <function>select()</function> system
427 call: as soon as <function>select</function> reports that some
428 data is available on the file descriptor, you know that a
429 cancellation is being requested.</para>
431 <para>For example, if you are reading from a file descriptor and
432 you want to check for a pending cancellation at the same time,
433 you can set up <function>select</function>for checking if data
434 is available on both the cancellation file descriptor and the
435 file descriptor you are reading from.</para>
439 <title>Dealing with <symbol>EINTR</symbol></title>
441 <para>In order to maximize the chance of cancelling an operation
442 immediately, the GNOME Virtual File System can sends a signal to
443 the asynchronous thread or process. This does not happen on all
444 the systems and setups, though.</para>
446 <para>The result of this is that, if a process is in the middle
447 of a Unix system call while receiving this signal, the system
448 call might be interrupted and return a <symbol>EINTR</symbol>
451 <para>For this reason, when you receive <symbol>EINTR</symbol>
452 you should check if a cancellation request is pending, using
453 <function>gnome_vfs_cancellation_check()</function> on the
454 <structname>GnomeVFSCancellation</structname> object that the
455 implementation function received:</para>
458 <listitem><para>If a cancellation is indeed pending
459 (<function>gnome_vfs_cancellation_check()</function> returns a
460 nonzero value), you should cancel the operation, cleaning up
461 all the effects, and return
462 <symbol>GNOME_VFS_ERROR_INTERRUPTED</symbol> or
463 <symbol>GNOME_VFS_ERROR_CANCELLED</symbol></para></listitem>
465 <listitem><para>Otherwise, retry the system call as you would
466 normally do.</para></listitem>
473 <title>Basic guidelines for writing a module</title>
475 <para>Writing GNOME VFS modules is easy, but there are a few
476 things that you must keep in mind when hacking them:</para>
479 <listitem><para>All of the code must be completely thread safe.
480 The reason for this is that the asynchronous GNOME VFS engine
481 will use threads when available; if you don't make sure that the
482 code is thread-safe, every kind of weird and unexpected errors
483 will happen. As debugging these problems can be very hard, it's
484 important to write the code with threads in mind right from the
485 start.</para></listitem>
487 <listitem><para>Use the special
488 <function>gnome_vfs_*_cancellable()</function> VFS functions
489 instead of the standard non-cancellable ones, passing them the
490 same <structname>GnomeVFSCancellation</structname> object you
491 are given, so that the operation can always be interrrupted at
492 any time.</para></listitem>
494 <listitem><para>The code should respect the basic GNOME
495 guidelines for source code indentation and
496 style.</para></listitem>
500 <title>How to make the code thread safe</title>
502 <para>Although it might sound scary at first, making the code
503 for the modules thread safe is not complicated at all.</para>
505 <para>First of all, make sure the amount of global variables is
506 kept to the bare minimum. If possible, you should avoid them at
509 <para>For those cases where globals are inevitable (such as
510 caches, connection pools or things like that), you have to make
511 sure every variable is properly associated with a mutex, and
512 that the mutex is locked before every access to this variable
513 and released afterwards. You can also use
514 <function>G_LOCK_DEFINE_STATIC</function>,
515 <function>G_LOCK</function> and <function>G_UNLOCK</function>
519 <para>Generally speaking, if you are going to dynamically
520 allocate structures that are shared by more than one
521 operation/file, you should provide all of them with their nice
524 <para>Finally, make sure mutexes are used only if they are
525 available. One way to do so is to use macros like the
530 #ifdef G_THREADS_ENABLED
531 #define MUTEX_NEW() g_mutex_new ()
532 #define MUTEX_FREE(a) g_mutex_free (a)
533 #define MUTEX_LOCK(a) if ((a) != NULL) g_mutex_lock (a)
534 #define MUTEX_UNLOCK(a) if ((a) != NULL) g_mutex_unlock (a)
536 #define MUTEX_NEW() NULL
537 #define MUTEX_FREE(a)
538 #define MUTEX_LOCK(a)
539 #define MUTEX_UNLOCK(a)
540 #endif</programlisting>
542 <para><function>G_LOCK_DEFINE_STATIC</function>,
543 <function>G_LOCK</function> and <function>G_UNLOCK</function> in
544 GLib are always safe to use, as they are already defined to be
545 nothing when thread support is not available.</para>
547 <para>(Probably it would be a good idea to have something in the
548 private GNOME VFS API that does this stuff for all the