1 <html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>Writing Modules</title><meta name="generator" content="DocBook XSL Stylesheets V1.61.0"><meta name="generator" content="GTK-Doc V1.1 (XML mode)"><style type="text/css">
2 .synopsis, .classsynopsis {
4 border: solid 1px #aaaaaa;
9 border: solid 1px #aaaaff;
18 border: solid 1px #ffaaaa;
25 .navigation a:visited {
31 </style><link rel="home" href="index.html" title="GnomeVFS - Filesystem Abstraction library"><link rel="up" href="modules.html" title="Filesystem Modules"><link rel="previous" href="modules.html" title="Filesystem Modules"><link rel="next" href="gnome-vfs-20-gnome-vfs-mime.html" title="MIME typing"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><table class="navigation" width="100%" summary="Navigation header" cellpadding="2" cellspacing="2"><tr valign="middle"><td><a accesskey="p" href="modules.html"><img src="left.png" width="24" height="24" border="0" alt="Prev"></a></td><td><a accesskey="u" href="modules.html"><img src="up.png" width="24" height="24" border="0" alt="Up"></a></td><td><a accesskey="h" href="index.html"><img src="home.png" width="24" height="24" border="0" alt="Home"></a></td><th width="100%" align="center">GnomeVFS - Filesystem Abstraction library</th><td><a accesskey="n" href="gnome-vfs-20-gnome-vfs-mime.html"><img src="right.png" width="24" height="24" border="0" alt="Next"></a></td></tr></table><div class="refentry" lang="en"><a name="gnome-vfs-writing-modules"></a><div class="titlepage"><div></div><div></div></div><div class="refnamediv"><h2><span class="refentrytitle">Writing Modules</span></h2><p>Writing Modules — basic gnome-vfs module concepts</p></div><div class="refsect1" lang="en"><a name="Introduction"></a><h2>Introduction</h2><p>This section will introduce the basic concepts that are
32 needed for writing GNOME Virtual File System modules.</p><div class="refsect2" lang="en"><a name="uris"></a><h3>GNOME VFS URIs (Uniform Resource Identifiers)</h3><p>The GNOME Virtual file system uses URIs similiar to the
33 standard WWW URIs. The basic difference between a VFS URI and
34 WWW URI is that, while with WWW URIs you can only use a single
35 protocol for accessing a certain file, with GNOME VFS URIs you
36 can combine different access methods in sequence.</p><p>For example, suppose you want to access file
37 <tt class="filename">hello.c</tt> in a <tt class="filename">tar.gz</tt>
38 file which is in turn accessible through FTP from a remote
39 machine. In order to access this file, you would need to:</p><div class="orderedlist"><ol type="1"><li><p>Connect to the FTP site.</p></li><li><p>Fetch the <tt class="filename">tar.gz</tt>
40 file.</p></li><li><p>Decompress the <tt class="filename">tar.gz</tt> file using
41 GZIP.</p></li><li><p>Extract <tt class="filename">hello.c</tt> from the resulting
42 uncompressed <tt class="filename">tar</tt> file.</p></li></ol></div><p>The GNOME Virtual File System lets you express this by
43 combining the three access methods (i.e. tar, GZIP and FTP)
44 into a single URI. Access methods are combined in the URI by
45 using the `#' character, followed by the name for the access
46 method and the subpath for that specific access method. The
47 subpath can be omitted for those storage methods that do not
48 need a path to retrieve the file. (For example, a GZIP file
49 always contains a single uncompressed file, so no path is
50 needed to locate the uncompressed file within the GZIP file.
51 But on the other hand, the TAR method requires a path to
52 locate a specific file or directory.)</p><p>For example, in the case we outlined above, the URI would
53 look something like:</p><pre class="programlisting">
55 ftp://username:password@host.net/path/to/file.tar.gz#gzip#tar/path/to/hello.c</pre><p>Each method/subpath couple is called a <i class="firstterm">URI
56 element</i>. When URI elements are combined like this,
57 each URI element uses the previous one to access a base resource
58 into which it will look up a file, using the subpath
59 information. For this reason, we will say that each element is
60 the <i class="firstterm">parent</i> element for the following one.</p><p>The first URI element, the one which has no parent, is
61 called the <i class="firstterm">toplevel element</i>. It does not
62 use the `#' character; instead, it uses the standard syntax of
63 WWW URIs: </p><pre class="programlisting">
65 method://user:password@host/path/to/file</pre><p>This way, normal WWW URIs can be used with the GNOME Virtual
66 File System.</p><p>Toplevel elements are also special because they let users
67 specify user names, passwords and host names, while
68 non-toplevel elements don't.</p></div><hr><div class="refsect2" lang="en"><h3>The <span class="structname">GnomeVFSURI</span> type</h3><p>Within the GNOME Virtual File System library, URI elements
69 are represented by a special type,
70 <span class="structname">GnomeVFSURI</span>, which is meant to represent
71 user-provided URIs in a machine-optimized way. </p><p>Every <span class="structname">GnomeVFSURI</span> contains the
72 following information:</p><div class="itemizedlist"><ul type="disc"><li><p>A reference counter</p></li><li><p>A pointer to the parent
73 <span class="structname">GnomeVFSURI</span> URI element.</p></li><li><p>The subpath.</p></li><li><p>The name of the access method.</p></li><li><p>A pointer to a
74 <span class="structname">GnomeVFSMethod</span> object, describing the
75 access method (see below).</p></li></ul></div></div></div><div class="refsect1" lang="en"><h2>GNOME Virtual File System access method implementation</h2><p>In the GNOME Virtual File System, the implementations for
76 all the access methods are loaded at runtime, as shared library
77 modules. The modules are loaded during parsing of the string URI:
78 if the parser encounters an access method for which no
79 implementation is currently loaded, it retrieves the corresponding
80 library file, dynamically links it into the executable, and
81 initializes it.</p><p>After initialization, the module returns a special
82 <span class="structname">GnomeVFSMethod</span> object that contains
83 pointers to the various implementation functions for that specific
84 method. By storing a pointer to this object into the
85 <span class="structname">GnomeVFSURI</span> type, the VFS library is then
86 able to use these functions for file access.</p><div class="refsect2" lang="en"><h3>How file access is performed</h3><p>When the VFS library needs to perform some file operation,
87 it performs the following steps:</p><div class="itemizedlist"><ul type="disc"><li><p>If the URI is given in textual form (i.e. as a
88 string), it parses it and activates the necessary access method
89 modules.</p></li><li><p>It retrieves a pointer to the lowmost
90 level URI element.</p></li><li><p>It retrieves a pointer to the
91 <span class="structname">GnomeVFSMethod</span> object that corresponds
92 to the access method for that URI element.</p></li><li><p>It retrieves a pointer to the implementation
93 function for that operation from the
94 <span class="structname">GnomeVFSMethod</span>object.</p></li><li><p>It invokes that implementation function
95 passing the pointer to the lowmost level URI
96 element.</p></li></ul></div><p>Combining the access methods is always done within the
97 method implementation. If the method implementation needs to do
98 some file operation on the the parent URI element, it can do so
99 by simply invoking the corresponding VFS function in, by using
100 the parent pointer in the <span class="structname">GnomeVFSURI</span>
101 object. </p><p>For example, suppose you have to read a simple URI like
102 the following:</p><pre class="programlisting">
104 file:/home/ettore/file.gz#gzip</pre><p>In this case, the GZIP method will be invoked with a
105 pointer to the <span class="structname">GnomeVFSURI</span> describing the
106 `gzip' part; then the GZIP method will be able to read
107 <tt class="filename">file.gz</tt> by just invoking the corresponding
108 GNOME VFS library function on its parent, and decompress it on
109 the fly. </p></div></div><div class="refsect1" lang="en"><h2>Implementing an access method in practice</h2><p>Implementing a new access method is really not difficult at
110 all. This section explains how this is done.</p><div class="refsect2" lang="en"><h3>Using shared libraries</h3><p>Every module must be compiled as a shared library (i.e. a
111 <tt class="filename">.so</tt> file).</p><p>The current way for accessing the right module for the
112 right method is very simple, and is based on file names. In
113 practice, a module implementing access method named
114 <tt class="filename">foo</tt> must be named
115 <tt class="filename">libfoo.so</tt>. For example, the module
116 implementing the <tt class="filename">ftp:</tt> access method is
117 called <tt class="filename">libftp.so</tt>, the module implementing
118 <tt class="filename">#gzip</tt> access is called
119 <tt class="filename">libgzip.so</tt> and so on.</p><p>This might change in the future.</p></div><hr><div class="refsect2" lang="en"><h3>The initialization/shutdown functions</h3><p>Every shared library module must provide two functions:</p><pre class="programlisting">
121 GnomeVFSMethod *vfs_module_init (void);
122 void vfs_module_shutdown (GnomeVFSMethod *method);</pre><p>These are the only functions that the VFS library will
123 access directly. All the other symbols (i.e. functions and
124 variables) in the module should be made static. </p><p><tt class="function">vfs_module_init()</tt> is called
125 as soon as the module is loaded in memory. It will have to
126 return a pointer to a <span class="structname">GnomeVFSMethod</span>
127 object that will contain the pointers to the method's
128 implementation functions. We will describe this later. </p><p><tt class="function">vfs_module_shutdown</tt>, instead,
129 is called before the module is unloaded or the program that uses
130 it dies. This functions should:</p><div class="itemizedlist"><ul type="disc"><li><p>Deallocate all the memory allocated by the
131 module.</p></li><li><p>Close all the file descriptors associated with
132 the module.</p></li><li><p>Kill any external process spawned by the
133 module.</p></li><li><p>In general, make sure that any operation that
134 was going on before this function was called will be
135 interrupted correctly, as soon as possible and without any
136 leaks.</p></li></ul></div></div><hr><div class="refsect2" lang="en"><h3>The <span class="structname">GnomeVFSMethod</span> object</h3><p>This object is contains pointers to the module
137 implementation functions.</p><pre class="programlisting">
139 GnomeVFSResult (* open) (GnomeVFSMethodHandle **method_handle_return,
141 GnomeVFSOpenMode mode
142 GnomeVFSCancellation *cancellation);
144 GnomeVFSResult (* create) (GnomeVFSMethodHandle **method_handle_return,
146 GnomeVFSOpenMode mode,
149 GnomeVFSCancellation *cancellation);
151 GnomeVFSResult (* close) (GnomeVFSMethodHandle *method_handle
152 GnomeVFSCancellation *cancellation);
154 GnomeVFSResult (* read) (GnomeVFSMethodHandle *method_handle,
156 GnomeVFSFileSize num_bytes,
157 GnomeVFSFileSize *bytes_read_return
158 GnomeVFSCancellation *cancellation);
160 GnomeVFSResult (* write) (GnomeVFSMethodHandle *method_handle,
161 gconstpointer buffer,
162 GnomeVFSFileSize num_bytes,
163 GnomeVFSFileSize *bytes_written_return
164 GnomeVFSCancellation *cancellation);
166 GnomeVFSResult (* seek) (GnomeVFSMethodHandle *method_handle,
167 GnomeVFSSeekPosition whence,
168 GnomeVFSFileOffset offset
169 GnomeVFSCancellation *cancellation);
171 GnomeVFSResult (* tell) (GnomeVFSMethodHandle *method_handle,
172 GnomeVFSFileOffset *offset_return);
174 GnomeVFSResult (* truncate) (GnomeVFSMethodHandle *method_handle,
175 GnomeVFSFileSize where
176 GnomeVFSCancellation *cancellation);
178 GnomeVFSResult (* open_directory) (GnomeVFSMethodHandle **method_handle,
180 GnomeVFSFileInfoOptions options,
181 const GList *meta_keys,
182 const GnomeVFSDirectoryFilter *filter
183 GnomeVFSCancellation *cancellation);
185 GnomeVFSResult (* close_directory) (GnomeVFSMethodHandle *method_handle
186 GnomeVFSCancellation *cancellation);
188 GnomeVFSResult (* read_directory) (GnomeVFSMethodHandle *method_handle,
189 GnomeVFSFileInfo *file_info
190 GnomeVFSCancellation *cancellation);
192 GnomeVFSResult (* get_file_info) (GnomeVFSURI *uri,
193 GnomeVFSFileInfo *file_info,
194 GnomeVFSFileInfoOptions options,
195 const GList *meta_keys
196 GnomeVFSCancellation *cancellation);
198 GnomeVFSResult (* get_file_info_from_handle)
199 (GnomeVFSMethodHandle *method_handle,
200 GnomeVFSFileInfo *file_info,
201 GnomeVFSFileInfoOptions options,
202 const GList *meta_keys
203 GnomeVFSCancellation *cancellation);
205 gboolean (* is_local) (const GnomeVFSURI *uri
206 GnomeVFSCancellation *cancellation);
208 GnomeVFSResult (* rename) (GnomeVFSURI *uri, const gchar *new_name
209 GnomeVFSCancellation *cancellation);
211 GnomeVFSResult (* make_directory) (GnomeVFSURI *uri, guint perm
212 GnomeVFSCancellation *cancellation);
214 GnomeVFSResult (* remove_directory) (GnomeVFSURI *uri
215 GnomeVFSCancellation *cancellation);
217 GnomeVFSResult (* unlink) (GnomeVFSURI *uri
218 GnomeVFSCancellation *cancellation);
219 </pre></div></div><div class="refsect1" lang="en"><h2>Handling cancellation</h2><p>As VFS operations might be very long, especially in the case
220 of transient errors (such as a network server that has gone down),
221 the GNOME Virtual File System Library provides a standard way for
222 handling cancellation of VFS operations.</p><div class="refsect2" lang="en"><h3>The <span class="structname">GnomeVFSCancellation</span> object</h3><p>The object that encapsulates this functionality is
223 <span class="structname">GnomeVFSCancellation</span>. Most
224 implementation functions get a pointer to such an object, and are
225 expected to use this object to recognize when an operation should
226 be interrupted.</p><p>The most simple way to check for a cancellation request is
227 to poll the object with
228 <tt class="function">gnome_vfs_cancellation_check()</tt>:</p><pre class="programlisting">
230 gboolean gnome_vfs_cancellation_check (GnomeVFSCancellation *cancellation);</pre><p>This function will return a nonzero value if the current
231 operation should be cancelled.</p><p>Notice that cancellation is an asynchronous operation that
232 might happen outside your function, in parallel with the code that
233 you are writing. For example, in the case of threads, the request
234 will be set in the master thread; in the case of slave
235 CORBA-driven processes, the request will be activated by a Unix
236 signal. So you can expect a cancellation request to happen (and
237 consequently be signalled in
238 <span class="structname">GnomeVFSCancellation</span>) at any time.</p><p>For this reason, you should be calling this function
239 periodically, whenever you are going to perform several
240 iterations of the same task, or execute a single expensive task.
241 When the function returns a nonzero value, the correct way to
242 react is:</p><div class="orderedlist"><ol type="1"><li><p>Clean things up so that the result of the
243 operations that have been performed are all
244 cancelled.</p></li><li><p>Return the
245 <span class="symbol">GNOME_VFS_ERROR_CANCELLED</span> error
246 code.</p></li></ol></div><p>But there are some other situations in which you want to
247 be able to interrupt a I/O operation when a cancellation request
248 is performed. In such cases, polling is not a viable option.</p><p>For this reason,
249 <span class="structname">GnomeVFSCancellation</span> provides an
250 alternative way of sending notifications, using a file
251 descriptor. To use this feature, you should use the following
252 function:</p><pre class="programlisting">
254 gint gnome_vfs_cancellation_get_fd (GnomeVFSCancellation *cancellation); </pre><p>When this function is called, it will return an open file
255 descriptor, which is the read-side of a pipe. The pipe will be
256 given a character from the write side as soon as a cancellation
257 request is sent. For this reason, you can check for a
258 cancellation by using the <tt class="function">select()</tt> system
259 call: as soon as <tt class="function">select</tt> reports that some
260 data is available on the file descriptor, you know that a
261 cancellation is being requested.</p><p>For example, if you are reading from a file descriptor and
262 you want to check for a pending cancellation at the same time,
263 you can set up <tt class="function">select</tt>for checking if data
264 is available on both the cancellation file descriptor and the
265 file descriptor you are reading from.</p></div><hr><div class="refsect2" lang="en"><h3>Dealing with <span class="symbol">EINTR</span></h3><p>In order to maximize the chance of cancelling an operation
266 immediately, the GNOME Virtual File System can sends a signal to
267 the asynchronous thread or process. This does not happen on all
268 the systems and setups, though.</p><p>The result of this is that, if a process is in the middle
269 of a Unix system call while receiving this signal, the system
270 call might be interrupted and return a <span class="symbol">EINTR</span>
271 error.</p><p>For this reason, when you receive <span class="symbol">EINTR</span>
272 you should check if a cancellation request is pending, using
273 <tt class="function">gnome_vfs_cancellation_check()</tt> on the
274 <span class="structname">GnomeVFSCancellation</span> object that the
275 implementation function received:</p><div class="itemizedlist"><ul type="disc"><li><p>If a cancellation is indeed pending
276 (<tt class="function">gnome_vfs_cancellation_check()</tt> returns a
277 nonzero value), you should cancel the operation, cleaning up
278 all the effects, and return
279 <span class="symbol">GNOME_VFS_ERROR_INTERRUPTED</span> or
280 <span class="symbol">GNOME_VFS_ERROR_CANCELLED</span></p></li><li><p>Otherwise, retry the system call as you would
281 normally do.</p></li></ul></div></div></div><div class="refsect1" lang="en"><h2>Basic guidelines for writing a module</h2><p>Writing GNOME VFS modules is easy, but there are a few
282 things that you must keep in mind when hacking them:</p><div class="itemizedlist"><ul type="disc"><li><p>All of the code must be completely thread safe.
283 The reason for this is that the asynchronous GNOME VFS engine
284 will use threads when available; if you don't make sure that the
285 code is thread-safe, every kind of weird and unexpected errors
286 will happen. As debugging these problems can be very hard, it's
287 important to write the code with threads in mind right from the
288 start.</p></li><li><p>Use the special
289 <tt class="function">gnome_vfs_*_cancellable()</tt> VFS functions
290 instead of the standard non-cancellable ones, passing them the
291 same <span class="structname">GnomeVFSCancellation</span> object you
292 are given, so that the operation can always be interrrupted at
293 any time.</p></li><li><p>The code should respect the basic GNOME
294 guidelines for source code indentation and
295 style.</p></li></ul></div><div class="refsect2" lang="en"><h3>How to make the code thread safe</h3><p>Although it might sound scary at first, making the code
296 for the modules thread safe is not complicated at all.</p><p>First of all, make sure the amount of global variables is
297 kept to the bare minimum. If possible, you should avoid them at
298 all cost.</p><p>For those cases where globals are inevitable (such as
299 caches, connection pools or things like that), you have to make
300 sure every variable is properly associated with a mutex, and
301 that the mutex is locked before every access to this variable
302 and released afterwards. You can also use
303 <tt class="function">G_LOCK_DEFINE_STATIC</tt>,
304 <tt class="function">G_LOCK</tt> and <tt class="function">G_UNLOCK</tt>
306 </p><p>Generally speaking, if you are going to dynamically
307 allocate structures that are shared by more than one
308 operation/file, you should provide all of them with their nice
309 mutex locks.</p><p>Finally, make sure mutexes are used only if they are
310 available. One way to do so is to use macros like the
311 following:</p><pre class="programlisting">
313 #ifdef G_THREADS_ENABLED
314 #define MUTEX_NEW() g_mutex_new ()
315 #define MUTEX_FREE(a) g_mutex_free (a)
316 #define MUTEX_LOCK(a) if ((a) != NULL) g_mutex_lock (a)
317 #define MUTEX_UNLOCK(a) if ((a) != NULL) g_mutex_unlock (a)
319 #define MUTEX_NEW() NULL
320 #define MUTEX_FREE(a)
321 #define MUTEX_LOCK(a)
322 #define MUTEX_UNLOCK(a)
323 #endif</pre><p><tt class="function">G_LOCK_DEFINE_STATIC</tt>,
324 <tt class="function">G_LOCK</tt> and <tt class="function">G_UNLOCK</tt> in
325 GLib are always safe to use, as they are already defined to be
326 nothing when thread support is not available.</p><p>(Probably it would be a good idea to have something in the
327 private GNOME VFS API that does this stuff for all the
328 modules.)</p></div></div></div><table class="navigation" width="100%" summary="Navigation footer" cellpadding="2" cellspacing="0"><tr valign="middle"><td align="left"><a accesskey="p" href="modules.html"><b><< Filesystem Modules</b></a></td><td align="right"><a accesskey="n" href="gnome-vfs-20-gnome-vfs-mime.html"><b>MIME typing >></b></a></td></tr></table></body></html>