0dc7b4d9e5adc13eec2f82104f77d8f9eec04bd3
[captive.git] / src / client / lufs / captivefs-file.c
1 /* $Id$
2  * lufs interface module file objects implementation for libcaptive
3  * Copyright (C) 2003 Jan Kratochvil <project-captive@jankratochvil.net>
4  * 
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; exactly version 2 of June 1991 is required
8  * 
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  * 
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17  */
18
19
20 #include "config.h"
21
22 #include <glib/gmessages.h>
23 #include "captivefs-misc.h"
24 #include "captivefs-attr.h"
25 #include "captivefs-vfs.h"
26 #include <fcntl.h>
27
28 #include <captive/client-vfs.h>
29 #include <captive/client-file.h>
30 #include <captive/client.h>
31
32 #include <lufs/fs.h>
33
34
35 /* Config: */
36 #define CLIENT_LUFS_USE_COUNT "client-lufs-use_count"
37
38
39 /* map: (const gchar *) -> (CaptiveFileObject *) */
40 static GHashTable *FileHandle_hash;
41 G_LOCK_DEFINE_STATIC(FileHandle_hash);
42
43 static void FileHandle_hash_init(void)
44 {
45         G_LOCK(FileHandle_hash);
46         if (!FileHandle_hash) {
47                 FileHandle_hash=g_hash_table_new_full(g_str_hash,g_str_equal,
48                                 (GDestroyNotify)g_free, /* key_destroy_func */
49                                 (GDestroyNotify)NULL);  /* value_destroy_func; handled by FileHandle_hash_captive_file_object_weak_notify() */
50                 }
51         G_UNLOCK(FileHandle_hash);
52 }
53
54 /* Do not: GObject->ref_count
55  * as 'ref_count' may be increased for deleted files by ParentConnector
56  * until the volume gets unmounted. Therefore any deleted file would not
57  * be removed from our 'FileHandle_hash' and no object would be reinstantiable
58  * under the original deleted name.
59  */
60 static gint FileHandle_get_use_count(CaptiveFileObject *captive_file_object)
61 {
62         g_return_val_if_fail(CAPTIVE_FILE_IS_OBJECT(captive_file_object),0);
63
64         return GPOINTER_TO_INT(g_object_get_data(G_OBJECT(captive_file_object),CLIENT_LUFS_USE_COUNT));
65 }
66
67 static void FileHandle_set_use_count(CaptiveFileObject *captive_file_object,gint use_count)
68 {
69         g_return_if_fail(CAPTIVE_FILE_IS_OBJECT(captive_file_object));
70
71         g_object_set_data(G_OBJECT(captive_file_object),CLIENT_LUFS_USE_COUNT,GINT_TO_POINTER(use_count));
72 }
73
74 /* FileHandle_hash_init(); must be executed! */
75 /* G_LOCK(FileHandle_hash); must be held! */
76 static void FileHandle_enter(CaptiveFileObject *captive_file_object,const gchar *name_normalized)
77 {
78 gint use_count;
79
80         g_return_if_fail(CAPTIVE_FILE_IS_OBJECT(captive_file_object));
81         g_return_if_fail(name_normalized);
82
83         use_count=FileHandle_get_use_count(captive_file_object);
84         g_assert(use_count>=0);
85         use_count++;
86         FileHandle_set_use_count(captive_file_object,use_count);
87         if (use_count>1) {
88                 g_assert(captive_file_object==g_hash_table_lookup(FileHandle_hash,name_normalized));
89                 return;
90                 }
91         g_assert(NULL==g_hash_table_lookup(FileHandle_hash,name_normalized));
92         g_object_ref(captive_file_object);
93         g_hash_table_insert(FileHandle_hash,g_strdup(name_normalized),captive_file_object);
94 }
95
96 /* FileHandle_hash_init(); must be executed! */
97 /* G_LOCK(FileHandle_hash); must be held! */
98 /* G_LOCK(libcaptive); must NOT be held! */
99 static void FileHandle_leave_locked(CaptiveFileObject *captive_file_object,const gchar *name)
100 {
101 gboolean errbool;
102 gint use_count;
103 gchar *name_normalized;
104
105         g_return_if_fail(CAPTIVE_FILE_IS_OBJECT(captive_file_object));
106         g_return_if_fail(name);
107
108         name_normalized=captive_path_normalize(name);
109         use_count=FileHandle_get_use_count(captive_file_object);
110         g_assert(use_count>=1);
111         use_count--;
112         FileHandle_set_use_count(captive_file_object,use_count);
113         g_assert(captive_file_object==g_hash_table_lookup(FileHandle_hash,name_normalized));
114         if (use_count>=1) {
115                 g_free(name_normalized);
116                 return;
117                 }
118         errbool=g_hash_table_remove(FileHandle_hash,name_normalized);
119         g_assert(errbool==TRUE);
120         G_LOCK(libcaptive);
121         g_object_unref(captive_file_object);
122         G_UNLOCK(libcaptive);
123         g_assert(NULL==g_hash_table_lookup(FileHandle_hash,name_normalized));
124         g_free(name_normalized);
125 }
126
127 /* FileHandle_hash_init(); must be executed! */
128 /* G_LOCK(FileHandle_hash); must NOT be held! */
129 /* G_LOCK(libcaptive); must NOT be held! */
130 static void FileHandle_leave(CaptiveFileObject *captive_file_object,const gchar *name)
131 {
132         g_return_if_fail(CAPTIVE_FILE_IS_OBJECT(captive_file_object));
133         g_return_if_fail(name);
134
135         G_LOCK(FileHandle_hash);
136         FileHandle_leave_locked(captive_file_object,name);
137         G_UNLOCK(FileHandle_hash);
138 }
139
140 /* 'name' is required to exist. */
141 static CaptiveFileObject *FileHandle_lookup_enter(const char *name)
142 {
143 CaptiveFileObject *captive_file_object;
144 gchar *name_normalized;
145
146         g_return_val_if_fail(name!=NULL,NULL);
147
148         name_normalized=captive_path_normalize(name);
149         FileHandle_hash_init();
150         G_LOCK(FileHandle_hash);
151         captive_file_object=g_hash_table_lookup(FileHandle_hash,name_normalized);
152         if (!captive_file_object) {
153                 G_UNLOCK(FileHandle_hash);
154                 g_warning("FileHandle_lookup_enter: FileHandle not found of: %s",name_normalized);
155                 g_free(name_normalized);
156                 return NULL;
157                 }
158         g_assert(CAPTIVE_FILE_IS_OBJECT(captive_file_object));
159         FileHandle_enter(captive_file_object,name_normalized);
160         G_UNLOCK(FileHandle_hash);
161         g_free(name_normalized);
162
163         return captive_file_object;
164 }
165
166 /* 'name' will be opened if needed. */
167 static CaptiveFileObject *FileHandle_lookup_open_enter(struct captivefs_vfs *captivefs_vfs,const char *name,gboolean create)
168 {
169 CaptiveFileObject *captive_file_object;
170 GnomeVFSResult errvfsresult;
171 gchar *name_normalized;
172
173         g_return_val_if_fail(name!=NULL,NULL);
174
175         name_normalized=captive_path_normalize(name);
176         FileHandle_hash_init();
177         G_LOCK(FileHandle_hash);
178         captive_file_object=g_hash_table_lookup(FileHandle_hash,name_normalized);
179         if (!create && captive_file_object) {
180                 g_assert(CAPTIVE_FILE_IS_OBJECT(captive_file_object));
181                 FileHandle_enter(captive_file_object,name_normalized);
182                 G_UNLOCK(FileHandle_hash);
183                 g_free(name_normalized);
184                 return captive_file_object;
185                 }
186         if ( create && captive_file_object) {
187                 g_assert(CAPTIVE_FILE_IS_OBJECT(captive_file_object));
188                 FileHandle_enter(captive_file_object,name_normalized);
189                 G_UNLOCK(FileHandle_hash);
190                 g_free(name_normalized);
191                 return NULL;
192                 }
193
194         /* FIXME: Respect 'gnome_vfs_open_mode'.
195          * We would have to do some reopens of files already contained in 'FileHandle_hash'.
196          * As W32 filesystem will allow us to open file 'GNOME_VFS_OPEN_WRITE'
197          * even on read-only media we use the full open permissions always.
198          */
199         if (!create) {
200                 G_LOCK(libcaptive);
201                 errvfsresult=captive_file_new_open(&captive_file_object,captivefs_vfs->captive_vfs_object,name_normalized,
202                                 GNOME_VFS_OPEN_READ|GNOME_VFS_OPEN_WRITE|GNOME_VFS_OPEN_RANDOM);
203                 G_UNLOCK(libcaptive);
204                 }
205         else {
206                 G_LOCK(libcaptive);
207                 errvfsresult=captive_file_new_create(&captive_file_object,captivefs_vfs->captive_vfs_object,name_normalized,
208                                 GNOME_VFS_OPEN_READ|GNOME_VFS_OPEN_WRITE|GNOME_VFS_OPEN_RANDOM, /* mode */
209                                 FALSE,  /* exclusive */
210                                 0600);  /* perm */
211                 G_UNLOCK(libcaptive);
212                 }
213         if (errvfsresult!=GNOME_VFS_OK) {
214                 G_UNLOCK(FileHandle_hash);
215                 g_free(name_normalized);
216                 return NULL;
217                 }
218         FileHandle_enter(captive_file_object,name_normalized);
219         G_UNLOCK(FileHandle_hash);
220         g_object_unref(captive_file_object);    /* for captive_file_new_open() / captive_file_new_create() */
221         g_free(name_normalized);
222
223         return captive_file_object;
224 }
225
226 /* Read a file/dir's attributes
227  * Fill all relevant data into the fattr structure.
228  * The uid/gid fields are just ownership hints hints:
229  *    != 0 => we own the file
230  *    == 0 => we don't own it
231  * The credentials structure (if applicable and saved from _init)
232  * can help determine ownership based on remote uids/gids.
233  *
234  * Notes:
235  *     If your filesysem doesn't natively support '.' or '..',
236  * don't forget to special-case them here.
237  *     It is best to assume that name is a relative path, not an
238  * absolute one.  Thus, you need to either be keeping track of the
239  * last accessed directory in readdir, or, as this code does, changing
240  * to the current directory there.
241  */
242 int captivefs_stat(struct captivefs_vfs *captivefs_vfs,const char *name,struct lufs_fattr *fattr)
243 {
244 GnomeVFSResult errvfsresult;
245 CaptiveFileObject *captive_file_object;
246 GnomeVFSFileInfo file_info;
247
248         g_return_val_if_fail(captivefs_vfs_validate(captivefs_vfs),-1);
249         g_return_val_if_fail(name!=NULL,-1);
250         g_return_val_if_fail(fattr!=NULL,-1);
251
252         if (captivefs_vfs->options.debug_messages)
253                 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"captivefs_stat: name=%s",name);
254
255         captive_file_object=FileHandle_lookup_open_enter(captivefs_vfs,name,FALSE);
256         if (!captive_file_object)
257                 return -1;
258
259         G_LOCK(libcaptive);
260         errvfsresult=captive_file_file_info_get(captive_file_object,&file_info);
261         G_UNLOCK(libcaptive);
262
263         FileHandle_leave(captive_file_object,name);
264
265         if (errvfsresult!=GNOME_VFS_OK)
266                 return -1;
267
268         if (!captivefs_GnomeVFSFileInfo_to_lufs_fattr(fattr,&file_info))
269                 return -1;
270
271         return 0;
272 }
273
274
275 /* Create a file
276  */
277 int captivefs_create(struct captivefs_vfs *captivefs_vfs,const char *file,int mode)
278 {
279 CaptiveFileObject *captive_file_object;
280
281         g_return_val_if_fail(captivefs_vfs_validate(captivefs_vfs),-1);
282         g_return_val_if_fail(file!=NULL,-1);
283
284         if (captivefs_vfs->options.debug_messages)
285                 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"captivefs_create: file=%s,mode=0x%X",file,mode);
286
287         captive_file_object=FileHandle_lookup_open_enter(captivefs_vfs,file,TRUE);
288         if (!captive_file_object)
289                 return -1;
290
291         FileHandle_leave(captive_file_object,file);
292
293         return 0;
294 }
295
296
297 /* Delete a file
298  */
299 int captivefs_unlink(struct captivefs_vfs *captivefs_vfs,const char *file)
300 {
301 GnomeVFSResult errvfsresult;
302 CaptiveFileObject *captive_file_object;
303
304         g_return_val_if_fail(captivefs_vfs_validate(captivefs_vfs),-1);
305         g_return_val_if_fail(file!=NULL,-1);
306
307         if (captivefs_vfs->options.debug_messages)
308                 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"captivefs_unlink: file=%s",file);
309
310         captive_file_object=FileHandle_lookup_open_enter(captivefs_vfs,file,FALSE);
311         if (!captive_file_object)
312                 return -1;
313
314         G_LOCK(libcaptive);
315         errvfsresult=captive_file_remove(captive_file_object);
316         G_UNLOCK(libcaptive);
317
318         FileHandle_leave(captive_file_object,file);
319
320         if (errvfsresult!=GNOME_VFS_OK)
321                 return -1;
322
323         return 0;
324 }
325
326
327 /* Rename a file/dir
328  */
329 int captivefs_rename(struct captivefs_vfs *captivefs_vfs,const char *old_name,const char *new_name)
330 {
331 GnomeVFSResult errvfsresult;
332 CaptiveFileObject *captive_file_object;
333 gint use_count;
334
335         g_return_val_if_fail(captivefs_vfs_validate(captivefs_vfs),-1);
336         g_return_val_if_fail(old_name!=NULL,-1);
337         g_return_val_if_fail(new_name!=NULL,-1);
338
339         if (captivefs_vfs->options.debug_messages)
340                 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"captivefs_rename: old_name=%s,new_name=%s",old_name,new_name);
341
342         captive_file_object=FileHandle_lookup_open_enter(captivefs_vfs,old_name,FALSE);
343         if (!captive_file_object)
344                 return -1;
345
346         G_LOCK(FileHandle_hash);
347         use_count=FileHandle_get_use_count(captive_file_object);
348         g_assert(use_count>=1);
349         if (use_count!=1) {
350                 if (captivefs_vfs->options.debug_messages)
351                         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"captivefs_rename: old_name=%s,new_name=%s: BUSY use_count=%d",
352                                         old_name,new_name,(int)use_count);
353                 G_UNLOCK(FileHandle_hash);
354                 FileHandle_leave(captive_file_object,old_name);
355                 return -1;
356                 }
357
358         G_LOCK(libcaptive);
359         errvfsresult=captive_file_move(captive_file_object,new_name,
360                         FALSE); /* force_replace */
361         G_UNLOCK(libcaptive);
362
363         FileHandle_leave_locked(captive_file_object,old_name);
364         G_UNLOCK(FileHandle_hash);
365
366         if (errvfsresult!=GNOME_VFS_OK)
367                 return -1;
368
369         return 0;
370 }
371
372
373 /* Open a file
374  *
375  * Notes:
376  *     By default, LUFS has no concept of file handles.  To implement file
377  * handles, take a look at the atbl class in sshfs - it is easy to cut&paste
378  * for use, and can be easily adapted for whatever purpose you need handles
379  * for.
380  *
381  *     Unlike the POSIX open command which has both a "mode" variable and
382  * a "flags" variable, this only has a "mode" variable.  To convert to the
383  * POSIX version, ->flags=mode^O_ACCMODE and ->mode=mode&O_ACCMODE.
384  */
385 int captivefs_open(struct captivefs_vfs *captivefs_vfs,const char *file,unsigned mode)
386 {
387 CaptiveFileObject *captive_file_object;
388 GnomeVFSOpenMode gnome_vfs_open_mode;
389
390         /* We may be called from the parent. */
391         g_return_val_if_fail(captivefs_vfs!=NULL,FALSE);
392         g_return_val_if_fail(file!=NULL,-1);
393
394         if (captivefs_vfs->options.debug_messages)
395                 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"captivefs_open: file=%s,mode=0x%X",file,mode);
396
397         if (!captivefs_vfs_validate(captivefs_vfs))
398                 return -1;
399
400         if (mode & ~(O_RDONLY|O_WRONLY|O_RDWR)) {
401                 g_warning("captivefs_open: Unrecognized 'mode' 0x%X",mode);
402                 return -1;
403                 }
404         switch (mode & (O_RDONLY|O_WRONLY|O_RDWR)) {
405                 case O_RDONLY: gnome_vfs_open_mode=GNOME_VFS_OPEN_READ;  break;
406                 case O_WRONLY: gnome_vfs_open_mode=GNOME_VFS_OPEN_WRITE; break;
407                 case O_RDWR:   gnome_vfs_open_mode=GNOME_VFS_OPEN_READ|GNOME_VFS_OPEN_WRITE; break;
408                 default:
409                         g_warning("captivefs_open: Unrecognized 'mode' 0x%X",mode);
410                         return -1;
411                 }
412
413         captive_file_object=FileHandle_lookup_open_enter(captivefs_vfs,file,FALSE);
414         if (!captive_file_object)
415                 return -1;
416
417         /* Leave 'captive_file_object' entered. */
418         return 0;
419 }
420
421
422 int captivefs_release(struct captivefs_vfs *captivefs_vfs,const char *file)
423 {
424 CaptiveFileObject *captive_file_object;
425
426         g_return_val_if_fail(captivefs_vfs_validate(captivefs_vfs),-1);
427         g_return_val_if_fail(file!=NULL,-1);
428
429         if (captivefs_vfs->options.debug_messages)
430                 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"captivefs_release: file=%s",file);
431
432         if (!(captive_file_object=FileHandle_lookup_enter(file)))
433                 return -1;
434
435         FileHandle_leave(captive_file_object,file);     /* for FileHandle_lookup_enter() */
436         FileHandle_leave(captive_file_object,file);
437
438         return 0;
439 }
440
441
442 /* Read from a file. Changed to use the (2) routines not for efficiency,
443  * but to make it work with 64-bit offsets :-(.
444  */
445 int captivefs_read(struct captivefs_vfs *captivefs_vfs,const char *file,long long offset,unsigned long count,void *buf)
446 {
447 CaptiveFileObject *captive_file_object;
448 GnomeVFSFileSize bytes_read;
449 GnomeVFSResult errvfsresult;
450
451         g_return_val_if_fail(captivefs_vfs_validate(captivefs_vfs),-1);
452         g_return_val_if_fail(file!=NULL,-1);
453         g_return_val_if_fail(buf!=NULL || count==0,-1);
454
455         if (captivefs_vfs->options.debug_messages)
456                 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"captivefs_read: file=%s,offset=%" G_GINT64_FORMAT ",count=0x%lX",
457                                 file,(gint64)offset,count);
458
459         if (!(captive_file_object=FileHandle_lookup_enter(file)))
460                 return -1;
461
462         /* Do not unlock 'libcaptive' between seek() and read()! */
463         G_LOCK(libcaptive);
464         errvfsresult=captive_file_seek(captive_file_object,GNOME_VFS_SEEK_START,offset);
465         if (errvfsresult!=GNOME_VFS_OK) {
466                 G_UNLOCK(libcaptive);
467                 FileHandle_leave(captive_file_object,file);
468                 return -1;
469                 }
470
471         errvfsresult=captive_file_read(captive_file_object,buf,count,&bytes_read);
472         G_UNLOCK(libcaptive);
473         FileHandle_leave(captive_file_object,file);
474         if (errvfsresult!=GNOME_VFS_OK)
475                 return -1;
476
477         if (bytes_read>INT_MAX) {
478                 g_warning("captivefs_read: Read %" G_GUINT64_FORMAT " bytes > INT_MAX=%d; dropped data",
479                                 (guint64)bytes_read,INT_MAX);
480                 bytes_read=INT_MAX;
481                 }
482
483         return bytes_read;
484 }
485
486
487 /* Write to a file
488  */
489 int captivefs_write(struct captivefs_vfs *captivefs_vfs,const char *file,long long offset,unsigned long count,const void *buf)
490 {
491 CaptiveFileObject *captive_file_object;
492 GnomeVFSFileSize bytes_written;
493 GnomeVFSResult errvfsresult;
494
495         g_return_val_if_fail(captivefs_vfs_validate(captivefs_vfs),-1);
496         g_return_val_if_fail(file!=NULL,-1);
497         g_return_val_if_fail(buf!=NULL || count==0,-1);
498
499         if (captivefs_vfs->options.debug_messages)
500                 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"captivefs_write: file=%s,offset=%" G_GINT64_FORMAT ",count=0x%lX",
501                                 file,(gint64)offset,count);
502
503         if (!(captive_file_object=FileHandle_lookup_enter(file)))
504                 return -1;
505
506         /* Do not unlock 'libcaptive' between seek() and write()! */
507         G_LOCK(libcaptive);
508         errvfsresult=captive_file_seek(captive_file_object,GNOME_VFS_SEEK_START,offset);
509         if (errvfsresult!=GNOME_VFS_OK) {
510                 G_UNLOCK(libcaptive);
511                 FileHandle_leave(captive_file_object,file);
512                 return -1;
513                 }
514
515         errvfsresult=captive_file_write(captive_file_object,buf,count,&bytes_written);
516         G_UNLOCK(libcaptive);
517         FileHandle_leave(captive_file_object,file);
518         if (errvfsresult!=GNOME_VFS_OK)
519                 return -1;
520
521         if (bytes_written>INT_MAX) {
522                 g_warning("captivefs_written: Written %" G_GUINT64_FORMAT " bytes > INT_MAX=%d; dropped data",
523                                 (guint64)bytes_written,INT_MAX);
524                 bytes_written=INT_MAX;
525                 }
526
527         return bytes_written;
528 }
529
530
531 /* Change a file/dir's attributes
532  */
533 int captivefs_setattr(struct captivefs_vfs *captivefs_vfs,const char *file,const struct lufs_fattr *fattr)
534 {
535 GnomeVFSFileInfo file_info;
536 GnomeVFSResult errvfsresult;
537 CaptiveFileObject *captive_file_object;
538
539         g_return_val_if_fail(captivefs_vfs_validate(captivefs_vfs),-1);
540         g_return_val_if_fail(file!=NULL,-1);
541         g_return_val_if_fail(fattr!=NULL,-1);
542
543         if (captivefs_vfs->options.debug_messages)
544                 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"captivefs_setattr: file=%s",file);
545
546         if (!captivefs_lufs_fattr_to_GnomeVFSFileInfo(&file_info,fattr))
547                 return -1;
548
549         captive_file_object=FileHandle_lookup_open_enter(captivefs_vfs,file,FALSE);
550         if (!captive_file_object)
551                 return -1;
552
553         G_LOCK(libcaptive);
554         errvfsresult=captive_file_file_info_set(captive_file_object,&file_info,
555                         0
556                                         | 0 /* 'GNOME_VFS_SET_FILE_INFO_NAME' is never set */
557                                         | (!(file_info.valid_fields&GNOME_VFS_FILE_INFO_FIELDS_PERMISSIONS) ? 0 : GNOME_VFS_SET_FILE_INFO_PERMISSIONS)
558                                         | 0 /* 'GNOME_VFS_SET_FILE_INFO_OWNER' is never set */
559                                         | (!(file_info.valid_fields&(0
560                                                                         | GNOME_VFS_FILE_INFO_FIELDS_ATIME
561                                                                         | GNOME_VFS_FILE_INFO_FIELDS_MTIME
562                                                                         | GNOME_VFS_FILE_INFO_FIELDS_CTIME))
563                                                         ? 0 : GNOME_VFS_SET_FILE_INFO_TIME));
564         G_UNLOCK(libcaptive);
565         FileHandle_leave(captive_file_object,file);
566         if (errvfsresult!=GNOME_VFS_OK)
567                 return -1;
568
569         return 0;
570 }