source: trunk/third/gnome-vfs2/doc/writing-modules.sgml @ 20794

Revision 20794, 26.8 KB checked in by ghudson, 20 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r20793, which included commits to RCS files with non-trunk default branches.
Line 
1<refentry id="gnome-vfs-writing-modules" revision="16 Apr 2004">
2<refmeta>
3<refentrytitle>Writing Modules</refentrytitle>
4<manvolnum>3</manvolnum>
5<refmiscinfo>GNOME-VFS Library</refmiscinfo>
6</refmeta>
7
8<refnamediv>
9<refname>Writing Modules</refname><refpurpose>basic gnome-vfs module concepts</refpurpose>
10</refnamediv>
11
12  <refsect1 id="Introduction">
13    <title>Introduction</title>
14
15    <para>This section will introduce the basic concepts that are
16      needed for writing GNOME Virtual File System modules.</para>
17
18    <refsect2 id="uris">
19      <title>GNOME VFS URIs (Uniform Resource Identifiers)</title>
20
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>
26
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>
31
32      <orderedlist>
33        <listitem><para>Connect to the FTP site.</para></listitem>
34       
35        <listitem><para>Fetch the <filename>tar.gz</filename>
36        file.</para></listitem>
37       
38        <listitem><para>Decompress the <filename>tar.gz</filename> file using
39          GZIP.</para></listitem>
40
41        <listitem><para>Extract <filename>hello.c</filename> from the resulting
42          uncompressed <filename>tar</filename> file.</para></listitem>
43      </orderedlist>
44
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>
56
57      <para>For example, in the case we outlined above, the URI would
58        look something like:</para>
59
60      <!-- FixMe what should I use here instead of programlisting? -->
61      <programlisting>
62
63        ftp://username:password@host.net/path/to/file.tar.gz#gzip:#tar:/path/to/hello.c</programlisting>
64
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>
71
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
75      WWW URIs: </para>
76
77      <programlisting>
78
79        method://user:password@host/path/to/file</programlisting>
80
81      <para>This way, normal WWW URIs can be used with the GNOME Virtual
82      File System.</para>
83
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>
87    </refsect2>
88
89    <refsect2>
90      <title>The <structname>GnomeVFSURI</structname> type</title>
91
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>
96
97      <para>Every <structname>GnomeVFSURI</structname> contains the
98      following information:</para>
99
100      <itemizedlist>
101        <listitem><para>A reference counter</para></listitem>
102     
103        <listitem><para>A pointer to the parent
104        <structname>GnomeVFSURI</structname> URI element.</para></listitem>
105     
106        <listitem><para>The subpath.</para></listitem>
107     
108        <listitem><para>The name of the access method.</para></listitem>
109     
110        <listitem><para>A pointer to a
111        <structname>GnomeVFSMethod</structname> object, describing the
112        access method (see below).</para></listitem>
113      </itemizedlist>
114
115    </refsect2>
116
117  </refsect1>
118
119  <refsect1>
120    <title>GNOME Virtual File System access method implementation</title>
121
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>
129
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>
136
137    <refsect2>
138      <title>How file access is performed</title>
139
140      <para>When the VFS library needs to perform some file operation,
141      it performs the following steps:</para>
142
143      <itemizedlist>
144
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>
148
149        <listitem><para>It retrieves a pointer to the lowmost
150        level URI element.</para></listitem>
151
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>
155       
156        <listitem><para>It retrieves a pointer to the implementation
157        function for that operation from the
158        <structname>GnomeVFSMethod</structname>object.</para></listitem>
159
160        <listitem><para>It invokes that implementation function
161        passing the pointer to the lowmost level URI
162        element.</para></listitem>
163       
164      </itemizedlist>
165
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, by using
170      the parent pointer in the <structname>GnomeVFSURI</structname>
171      object. </para>
172
173      <para>For example, suppose you have to read a simple URI like
174      the following:</para>
175
176      <!-- FixMe what should I use here instead of programlisting? -->
177      <programlisting>
178
179        file:/home/ettore/file.gz#gzip:</programlisting>
180
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. 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 decompressing it on
186      the fly. </para>
187
188    </refsect2>
189
190  </refsect1>
191
192  <refsect1>
193    <title>Implementing an access method in practice</title>
194
195    <para>Implementing a new access method is really not difficult at
196    all.  This section explains how this is done.</para>
197
198    <refsect2>
199      <title>Using shared libraries</title>
200
201      <para>Every module must be compiled as a shared library (i.e. a
202      <filename>.so</filename> file).</para>
203
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 an 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>
213
214      <para>This might change in the future.</para>
215
216    </refsect2>
217
218    <refsect2>
219      <title>The initialization/shutdown functions</title>
220
221      <para>Every shared library module must provide two functions:</para>
222
223      <programlisting role="c">
224
225GnomeVFSMethod *vfs_module_init (const char *method_name, const char *args);
226void vfs_module_shutdown (GnomeVFSMethod *method);</programlisting>
227
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>
231
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>
237
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>
241
242      <itemizedlist>
243
244        <listitem><para>Deallocate all the memory allocated by the
245        module.</para></listitem>
246
247        <listitem><para>Close all the file descriptors associated with
248        the module.</para></listitem>
249
250        <listitem><para>Kill any external process spawned by the
251        module.</para></listitem>
252
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>
257
258      </itemizedlist>
259
260    </refsect2>
261
262    <refsect2>
263      <title>The <structname>GnomeVFSMethod</structname> object</title>
264
265      <para>This object contains pointers to the module
266      implementation functions.</para>
267
268      <programlisting role="c">
269GnomeVFSResult (* open)              (GnomeVFSMethod         *method,
270                                      GnomeVFSMethodHandle  **method_handle_return,
271                                      GnomeVFSURI            *uri,
272                                      GnomeVFSOpenMode        mode,
273                                      GnomeVFSContext        *context);
274
275GnomeVFSResult (* create)            (GnomeVFSMethod         *method,
276                                      GnomeVFSMethodHandle  **method_handle_return,
277                                      GnomeVFSURI            *uri,
278                                      GnomeVFSOpenMode        mode,
279                                      gboolean                exclusive,
280                                      guint                   perm,
281                                      GnomeVFSContext        *context);
282
283GnomeVFSResult (* close)             (GnomeVFSMethod         *method,
284                                      GnomeVFSMethodHandle   *method_handle,
285                                      GnomeVFSContext        *context);
286
287GnomeVFSResult (* read)              (GnomeVFSMethod         *method,
288                                      GnomeVFSMethodHandle   *method_handle,
289                                      gpointer                buffer,
290                                      GnomeVFSFileSize        num_bytes,
291                                      GnomeVFSFileSize       *bytes_read_return,
292                                      GnomeVFSContext        *context);
293
294GnomeVFSResult (* write)             (GnomeVFSMethod         *method,
295                                      GnomeVFSMethodHandle   *method_handle,
296                                      gconstpointer           buffer,
297                                      GnomeVFSFileSize        num_bytes,
298                                      GnomeVFSFileSize       *bytes_written_return,
299                                      GnomeVFSContext        *context);
300
301GnomeVFSResult (* seek)              (GnomeVFSMethod         *method,
302                                      GnomeVFSMethodHandle   *method_handle,
303                                      GnomeVFSSeekPosition    whence,
304                                      GnomeVFSFileOffset      offset,
305                                      GnomeVFSContext        *context);
306
307GnomeVFSResult (* tell)              (GnomeVFSMethod         *method,
308                                      GnomeVFSMethodHandle   *method_handle,
309                                      GnomeVFSFileOffset     *offset_return);
310
311GnomeVFSResult (* truncate)          (GnomeVFSMethod         *method,
312                                      GnomeVFSMethodHandle  **method_handle,
313                                      GnomeVFSURI            *uri,
314                                      GnomeVFSFileInfoOptions options,
315                                      GnomeVFSContext        *context);
316
317GnomeVFSResult (* close_directory)   (GnomeVFSMethod         *method,
318                                      GnomeVFSMethodHandle   *method_handle,
319                                      GnomeVFSContext        *context);
320
321GnomeVFSResult (* read_directory)    (GnomeVFSMethod         *method,
322                                      GnomeVFSMethodHandle   *method_handle,
323                                      GnomeVFSFileInfo       *file_info,
324                                      GnomeVFSContext        *context);
325
326GnomeVFSResult (* get_file_info)     (GnomeVFSMethod         *method,
327                                      GnomeVFSURI            *uri,
328                                      GnomeVFSFileInfo       *file_info,
329                                      GnomeVFSFileInfoOptions options,
330                                      GnomeVFSContext        *context);
331
332GnomeVFSResult (* get_file_info_from_handle)
333                                     (GnomeVFSMethod         *method,
334                                      GnomeVFSMethodHandl e  *method_handle,
335                                      GnomeVFSFileInfo       *file_info,
336                                      GnomeVFSFileInfoOptions options,
337                                      GnomeVFSContext        *context);
338
339GnomeVFSResult (* truncate)          (GnomeVFSMethod         *method,
340                                      GnomeVFSURI            *uri,
341                                      GnomeVFSFileSize        length,
342                                      GnomeVFSContext        *context);
343
344GnomeVFSResult (* truncate_handle)   (GnomeVFSMethod         *method,
345                                      GnomeVFSMethodHandle   *handle,
346                                      GnomeVFSFileSize        length,
347                                      GnomeVFSContext        *context);
348
349gboolean (* is_local)                (GnomeVFSMethod         *method,
350                                      const GnomeVFSURI      *uri);
351
352GnomeVFSResult (* make_directory)    (GnomeVFSMethod         *method,
353                                      GnomeVFSURI            *uri,
354                                      guint                   perm,
355                                      GnomeVFSContext        *context);
356
357GnomeVFSResult (* find_directory)    (GnomeVFSMethod         *method,
358                                      GnomeVFSURI            *find_near_uri,
359                                      GnomeVFSFindDirectoryKind kind,
360                                      GnomeVFSURI           **result_uri,
361                                      gboolean                create_if_needed,
362                                      gboolean                find_if_needed,
363                                      guint                   perm,
364                                      GnomeVFSContext        *context);
365
366GnomeVFSResult (* remove_directory)  (GnomeVFSMethod         *method,
367                                      GnomeVFSURI            *uri,
368                                      GnomeVFSContext        *context);
369
370GnomeVFSResult (* move)              (GnomeVFSMethod         *method,
371                                      GnomeVFSURI            *old_uri,
372                                      GnomeVFSURI            *new_uri,
373                                      gboolean                force_replace,
374                                      GnomeVFSContext        *context);
375
376GnomeVFSResult (* unlink)            (GnomeVFSMethod         *method,
377                                      GnomeVFSURI            *uri,
378                                      GnomeVFSContext        *context);
379
380GnomeVFSResult (* check_same_fs)     (GnomeVFSMethod         *method,
381                                      GnomeVFSURI            *a,
382                                      GnomeVFSURI            *b,
383                                      gboolean               *same_fs_return,
384                                      GnomeVFSContext        *context);
385
386GnomeVFSResult (* set_file_info)     (GnomeVFSMethod         *method,
387                                      GnomeVFSURI            *a,
388                                      const GnomeVFSFileInfo *info,
389                                      GnomeVFSSetFileInfoMask mask,
390                                      GnomeVFSContext        *context);
391
392GnomeVFSResult (* create_symbolic_link) (GnomeVFSMethod      *method,
393                                      GnomeVFSURI            *uri,
394                                      const gchar            *target_reference,
395                                      GnomeVFSContext        *context);
396
397GnomeVFSResult (* monitor_add)       (GnomeVFSMethod         *method,
398                                      GnomeVFSMethodHandle  **method_handle_return,
399                                      GnomeVFSURI            *uri,
400                                      GnomeVFSMonitorType     monitor_type);
401
402GnomeVFSResult (* monitor_cancel)    (GnomeVFSMethod         *method,
403                                      GnomeVFSMethodHandle   *handle);
404
405GnomeVFSResult (* file_control)      (GnomeVFSMethod         *method,
406                                      GnomeVFSMethodHandle   *method_handle,
407                                      const char             *operation,
408                                      gpointer                operation_data,
409                                      GnomeVFSContext        *context);
410</programlisting>
411
412    </refsect2>
413
414  </refsect1>
415
416  <refsect1>
417    <title>Handling cancellation</title>
418
419    <para>As VFS operations might take very long to complete, especially in the
420    case of transient errors (such as a network server that has gone down),
421    the GNOME Virtual File System Library provides a standard way to
422    handle the cancellation of VFS operations.</para>
423
424    <refsect2>
425      <title>The <structname>GnomeVFSCancellation</structname> object</title>
426   
427      <para>The object that encapsulates this functionality is
428      <structname>GnomeVFSCancellation</structname>.  Most
429      implementation functions get a pointer to such an object, and are
430      expected to use this object to recognize when an operation should
431      be interrupted.</para>
432 
433      <para>The most simple way to check for a cancellation request is
434      to poll the object with
435      <function>gnome_vfs_cancellation_check()</function>:</para>
436 
437      <programlisting role="c">
438 
439gboolean gnome_vfs_cancellation_check (GnomeVFSCancellation *cancellation);</programlisting>
440 
441      <para>This function will return a nonzero value if the current
442      operation should be cancelled.</para>
443 
444      <para>Notice that cancellation is an asynchronous operation that
445      might happen outside your function, in parallel with the code that
446      you are writing.  For example, in the case of threads, the request
447      will be set in the master thread; in the case of slave
448      CORBA-driven processes, the request will be activated by a Unix
449      signal.  So you can expect a cancellation request to happen (and
450      consequently be signalled in
451      <structname>GnomeVFSCancellation</structname>) at any time.</para>
452
453      <para>For this reason, you should be calling this function
454      periodically, whenever you are going to perform several
455      iterations of the same task, or execute a single expensive task.
456      When the function returns a nonzero value, the correct way to
457      react is:</para>
458
459      <orderedlist>
460        <listitem><para>Clean things up so that the result of the
461        operations that have been performed are all
462        cancelled.</para></listitem>
463        <listitem><para>Return the
464        <symbol>GNOME_VFS_ERROR_CANCELLED</symbol> error
465        code.</para></listitem>
466      </orderedlist>
467
468      <para>Note, there are some other situations in which you want to
469      be able to interrupt an I/O operation when a cancellation request
470      is performed.  In such cases, polling is not a viable option.</para>
471
472      <para>For this reason,
473      <structname>GnomeVFSCancellation</structname> provides an
474      alternative way of sending notifications, using a file
475      descriptor.  To use this feature, you should use the following
476      function:</para>
477
478      <programlisting>
479
480gint gnome_vfs_cancellation_get_fd (GnomeVFSCancellation *cancellation); </programlisting>
481
482      <para>When this function is called, it will return an open file
483      descriptor, which is the read-side of a pipe.  The pipe will be
484      given a character from the write side as soon as a cancellation
485      request is sent.  You can check for a cancellation by using the
486      <function>select()</function> system call with this file descriptor.
487      As soon as <function>select</function> reports that some
488      data is available on this file descriptor, you know that a
489      cancellation is being requested.</para>
490
491      <para>For example, if you are reading from a file descriptor and
492      you want to check for a pending cancellation at the same time,
493      you can set up <function>select</function>for checking if data
494      is available on both the cancellation file descriptor and the
495      file descriptor you are reading from.</para>
496    </refsect2>
497
498    <refsect2>
499      <title>Dealing with <symbol>EINTR</symbol></title>
500
501      <para>In order to maximize the chance of cancelling an operation
502      immediately, the GNOME Virtual File System can sends a signal to
503      the asynchronous thread or process.  This does not happen on all
504      the systems and setups, though.</para>
505
506      <para>The result of this is that, if a process is in the middle
507      of a Unix system call while receiving this signal, the system
508      call might be interrupted and return a <symbol>EINTR</symbol>
509      error.</para>
510
511      <para>For this reason, when you receive <symbol>EINTR</symbol>
512      you should check if a cancellation request is pending, using
513      <function>gnome_vfs_cancellation_check()</function> on the
514      <structname>GnomeVFSCancellation</structname> object that the
515      implementation function received:</para>
516
517      <itemizedlist>
518        <listitem><para>If a cancellation is indeed pending
519        (<function>gnome_vfs_cancellation_check()</function> returns a
520        nonzero value), you should cancel the operation, cleaning up
521        all the effects, and return
522        <symbol>GNOME_VFS_ERROR_INTERRUPTED</symbol> or
523        <symbol>GNOME_VFS_ERROR_CANCELLED</symbol></para></listitem>
524
525        <listitem><para>Otherwise, retry the system call as you would
526        normally do.</para></listitem>
527      </itemizedlist>
528    </refsect2>
529
530  </refsect1>
531
532  <refsect1>
533    <title>Basic guidelines for writing a module</title>
534
535    <para>Writing GNOME VFS modules is easy, but there are a few
536    things that you must keep in mind when hacking them:</para>
537
538    <itemizedlist>
539      <listitem><para>All of the code must be completely thread safe.
540      The reason for this is that the asynchronous GNOME VFS engine
541      will use threads when available; if you don't make sure that the
542      code is thread-safe, every kind of weird and unexpected errors
543      will happen.  As debugging these problems can be very hard, it's
544      important to write the code with threads in mind right from the
545      start.</para></listitem>
546
547      <listitem><para>Use the special
548      <function>gnome_vfs_*_cancellable()</function> VFS functions
549      instead of the standard non-cancellable ones, passing them the
550      same <structname>GnomeVFSCancellation</structname> object you
551      are given, so that the operation can always be interrrupted at
552      any time.</para></listitem>
553
554      <listitem><para>The code should respect the basic GNOME
555      guidelines for source code indentation and
556      style.</para></listitem>
557    </itemizedlist>
558
559    <refsect2>
560      <title>How to make the code thread safe</title>
561
562      <para>Although it might sound scary at first, making the code
563      for the modules thread safe is not complicated at all.</para>
564
565      <para>First of all, make sure the amount of global variables is
566      kept to the bare minimum.  If possible, you should avoid them at
567      all cost.</para>
568
569      <para>For those cases where globals are inevitable (such as
570      caches, connection pools or things like that), you have to make
571      sure every variable is properly associated with a mutex, and
572      that the mutex is locked before every access to this variable
573      and released afterwards.  You can also use
574      <function>G_LOCK_DEFINE_STATIC</function>,
575      <function>G_LOCK</function> and <function>G_UNLOCK</function>
576      for this.
577      </para>
578
579      <para>Generally speaking, if you are going to dynamically
580      allocate structures that are shared by more than one
581      operation/file, you should provide all of them with their nice
582      mutex locks.</para>
583
584      <para>Finally, make sure mutexes are used only if they are
585      available.  One way to do so is to use macros like the
586      following:</para>
587
588      <programlisting>
589
590#ifdef G_THREADS_ENABLED
591#define MUTEX_NEW()     g_mutex_new ()
592#define MUTEX_FREE(a)   g_mutex_free (a)
593#define MUTEX_LOCK(a)   if ((a) != NULL) g_mutex_lock (a)
594#define MUTEX_UNLOCK(a) if ((a) != NULL) g_mutex_unlock (a)
595#else
596#define MUTEX_NEW()     NULL
597#define MUTEX_FREE(a)
598#define MUTEX_LOCK(a)
599#define MUTEX_UNLOCK(a)
600#endif</programlisting>
601
602      <para><function>G_LOCK_DEFINE_STATIC</function>,
603      <function>G_LOCK</function> and <function>G_UNLOCK</function> in
604      GLib are always safe to use, as they are already defined to be
605      nothing when thread support is not available.</para>
606
607      <para>(Probably it would be a good idea to have something in the
608      private GNOME VFS API that does this stuff for all the
609      modules.)</para>
610
611    </refsect2>
612  </refsect1>
613
614</refentry>
Note: See TracBrowser for help on using the repository browser.