62dd06932b30d0a0ed7006893de03210ed107cd3
[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 *utf8) -> (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
77                 (struct captivefs_vfs *captivefs_vfs,CaptiveFileObject *captive_file_object,const gchar *name_normalized)
78 {
79 gint use_count;
80
81         g_return_if_fail(captivefs_vfs_validate(captivefs_vfs));
82         g_return_if_fail(CAPTIVE_FILE_IS_OBJECT(captive_file_object));
83         g_return_if_fail(name_normalized);
84
85         use_count=FileHandle_get_use_count(captive_file_object);
86         if (captivefs_vfs->options.debug_messages)
87                 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"FileHandle_enter: name_normalized=%s, use_count++=%d",
88                                 name_normalized,(int)use_count);
89         g_assert(use_count>=0);
90         use_count++;
91         FileHandle_set_use_count(captive_file_object,use_count);
92         if (use_count>1) {
93                 g_assert(captive_file_object==g_hash_table_lookup(FileHandle_hash,name_normalized));
94                 return;
95                 }
96         g_assert(NULL==g_hash_table_lookup(FileHandle_hash,name_normalized));
97         g_object_ref(captive_file_object);
98         g_hash_table_insert(FileHandle_hash,g_strdup(name_normalized),captive_file_object);
99 }
100
101 /* FileHandle_hash_init(); must be executed! */
102 /* G_LOCK(FileHandle_hash); must be held! */
103 /* G_LOCK(libcaptive); must NOT be held! */
104 static void FileHandle_leave_locked
105                 (struct captivefs_vfs *captivefs_vfs,CaptiveFileObject *captive_file_object,const gchar *name)
106 {
107 gboolean errbool;
108 gint use_count;
109 gchar *name_normalized;
110
111         g_return_if_fail(captivefs_vfs_validate(captivefs_vfs));
112         g_return_if_fail(CAPTIVE_FILE_IS_OBJECT(captive_file_object));
113         g_return_if_fail(name);
114
115         name_normalized=captive_path_normalize(name);
116         use_count=FileHandle_get_use_count(captive_file_object);
117         if (captivefs_vfs->options.debug_messages)
118                 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"FileHandle_leave_locked: name_normalized=%s, use_count--=%d",
119                                 name_normalized,(int)use_count);
120         g_assert(use_count>=1);
121         use_count--;
122         FileHandle_set_use_count(captive_file_object,use_count);
123         g_assert(captive_file_object==g_hash_table_lookup(FileHandle_hash,name_normalized));
124         if (use_count>=1) {
125                 g_free(name_normalized);
126                 return;
127                 }
128         errbool=g_hash_table_remove(FileHandle_hash,name_normalized);
129         g_assert(errbool==TRUE);
130         G_LOCK(libcaptive);
131         g_object_unref(captive_file_object);
132         G_UNLOCK(libcaptive);
133         g_assert(NULL==g_hash_table_lookup(FileHandle_hash,name_normalized));
134         g_free(name_normalized);
135 }
136
137 /* FileHandle_hash_init(); must be executed! */
138 /* G_LOCK(FileHandle_hash); must NOT be held! */
139 /* G_LOCK(libcaptive); must NOT be held! */
140 static void FileHandle_leave(struct captivefs_vfs *captivefs_vfs,CaptiveFileObject *captive_file_object,const gchar *name)
141 {
142         g_return_if_fail(captivefs_vfs_validate(captivefs_vfs));
143         g_return_if_fail(CAPTIVE_FILE_IS_OBJECT(captive_file_object));
144         g_return_if_fail(name);
145
146         G_LOCK(FileHandle_hash);
147         FileHandle_leave_locked(captivefs_vfs,captive_file_object,name);
148         G_UNLOCK(FileHandle_hash);
149 }
150
151 /* 'name' is required to exist. */
152 static CaptiveFileObject *FileHandle_lookup_enter(struct captivefs_vfs *captivefs_vfs,const char *name)
153 {
154 CaptiveFileObject *captive_file_object;
155 gchar *name_normalized;
156
157         g_return_val_if_fail(captivefs_vfs_validate(captivefs_vfs),NULL);
158         g_return_val_if_fail(name!=NULL,NULL);
159
160         name_normalized=captive_path_normalize(name);
161         FileHandle_hash_init();
162         G_LOCK(FileHandle_hash);
163         captive_file_object=g_hash_table_lookup(FileHandle_hash,name_normalized);
164         if (!captive_file_object) {
165                 G_UNLOCK(FileHandle_hash);
166                 g_warning("FileHandle_lookup_enter: FileHandle not found of: %s",name_normalized);
167                 g_free(name_normalized);
168                 return NULL;
169                 }
170         g_assert(CAPTIVE_FILE_IS_OBJECT(captive_file_object));
171         FileHandle_enter(captivefs_vfs,captive_file_object,name_normalized);
172         G_UNLOCK(FileHandle_hash);
173         g_free(name_normalized);
174
175         return captive_file_object;
176 }
177
178 /* 'name' will be opened if needed. */
179 static CaptiveFileObject *FileHandle_lookup_open_enter(struct captivefs_vfs *captivefs_vfs,const char *name,gboolean create)
180 {
181 CaptiveFileObject *captive_file_object;
182 GnomeVFSResult errvfsresult;
183 gchar *name_normalized;
184
185         g_return_val_if_fail(captivefs_vfs_validate(captivefs_vfs),NULL);
186         g_return_val_if_fail(name!=NULL,NULL);
187
188         name_normalized=captive_path_normalize(name);
189         FileHandle_hash_init();
190         G_LOCK(FileHandle_hash);
191         captive_file_object=g_hash_table_lookup(FileHandle_hash,name_normalized);
192         if (!create && captive_file_object) {
193                 g_assert(CAPTIVE_FILE_IS_OBJECT(captive_file_object));
194                 FileHandle_enter(captivefs_vfs,captive_file_object,name_normalized);
195                 G_UNLOCK(FileHandle_hash);
196                 g_free(name_normalized);
197                 return captive_file_object;
198                 }
199         if ( create && captive_file_object) {
200                 g_assert(CAPTIVE_FILE_IS_OBJECT(captive_file_object));
201                 FileHandle_enter(captivefs_vfs,captive_file_object,name_normalized);
202                 G_UNLOCK(FileHandle_hash);
203                 g_free(name_normalized);
204                 return NULL;
205                 }
206
207         /* FIXME: Respect 'gnome_vfs_open_mode'.
208          * We would have to do some reopens of files already contained in 'FileHandle_hash'.
209          * As W32 filesystem will allow us to open file 'GNOME_VFS_OPEN_WRITE'
210          * even on read-only media we use the full open permissions always.
211          */
212         if (!create) {
213                 G_LOCK(libcaptive);
214                 errvfsresult=captive_file_new_open(&captive_file_object,captivefs_vfs->captive_vfs_object,name_normalized,
215                                 GNOME_VFS_OPEN_READ|GNOME_VFS_OPEN_WRITE|GNOME_VFS_OPEN_RANDOM);
216                 G_UNLOCK(libcaptive);
217                 }
218         else {
219                 G_LOCK(libcaptive);
220                 errvfsresult=captive_file_new_create(&captive_file_object,captivefs_vfs->captive_vfs_object,name_normalized,
221                                 GNOME_VFS_OPEN_READ|GNOME_VFS_OPEN_WRITE|GNOME_VFS_OPEN_RANDOM, /* mode */
222                                 FALSE,  /* exclusive */
223                                 0600);  /* perm */
224                 G_UNLOCK(libcaptive);
225                 }
226         if (errvfsresult!=GNOME_VFS_OK) {
227                 G_UNLOCK(FileHandle_hash);
228                 g_free(name_normalized);
229                 return NULL;
230                 }
231         FileHandle_enter(captivefs_vfs,captive_file_object,name_normalized);
232         G_UNLOCK(FileHandle_hash);
233         g_object_unref(captive_file_object);    /* for captive_file_new_open() / captive_file_new_create() */
234         g_free(name_normalized);
235
236         return captive_file_object;
237 }
238
239 /* Read a file/dir's attributes
240  * Fill all relevant data into the fattr structure.
241  * The uid/gid fields are just ownership hints hints:
242  *    != 0 => we own the file
243  *    == 0 => we don't own it
244  * The credentials structure (if applicable and saved from _init)
245  * can help determine ownership based on remote uids/gids.
246  *
247  * Notes:
248  *     If your filesysem doesn't natively support '.' or '..',
249  * don't forget to special-case them here.
250  *     It is best to assume that name is a relative path, not an
251  * absolute one.  Thus, you need to either be keeping track of the
252  * last accessed directory in readdir, or, as this code does, changing
253  * to the current directory there.
254  */
255 int captivefs_stat(struct captivefs_vfs *captivefs_vfs,const char *name,struct lufs_fattr *fattr)
256 {
257 GnomeVFSResult errvfsresult;
258 CaptiveFileObject *captive_file_object;
259 GnomeVFSFileInfo file_info;
260
261         g_return_val_if_fail(captivefs_vfs_validate(captivefs_vfs),-1);
262         g_return_val_if_fail(name!=NULL,-1);
263         g_return_val_if_fail(fattr!=NULL,-1);
264
265         if (captivefs_vfs->options.debug_messages)
266                 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"captivefs_stat: name=%s",name);
267
268         name=CAPTIVEFS_FILENAME_TO_UTF8_ALLOCA(name);
269
270         captive_file_object=FileHandle_lookup_open_enter(captivefs_vfs,name,FALSE);
271         if (!captive_file_object)
272                 return -1;
273
274         G_LOCK(libcaptive);
275         errvfsresult=captive_file_file_info_get(captive_file_object,&file_info);
276         G_UNLOCK(libcaptive);
277
278         FileHandle_leave(captivefs_vfs,captive_file_object,name);
279
280         if (errvfsresult!=GNOME_VFS_OK)
281                 return -1;
282
283         if (!captivefs_GnomeVFSFileInfo_to_lufs_fattr(captivefs_vfs,fattr,&file_info))
284                 return -1;
285
286         return 0;
287 }
288
289
290 /* Create a file
291  */
292 int captivefs_create(struct captivefs_vfs *captivefs_vfs,const char *file,int mode)
293 {
294 CaptiveFileObject *captive_file_object;
295
296         g_return_val_if_fail(captivefs_vfs_validate(captivefs_vfs),-1);
297         g_return_val_if_fail(file!=NULL,-1);
298
299         if (captivefs_vfs->options.debug_messages)
300                 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"captivefs_create: file=%s,mode=0x%X",file,mode);
301
302         file=CAPTIVEFS_FILENAME_TO_UTF8_ALLOCA(file);
303
304         captive_file_object=FileHandle_lookup_open_enter(captivefs_vfs,file,TRUE);
305         if (!captive_file_object)
306                 return -1;
307
308         FileHandle_leave(captivefs_vfs,captive_file_object,file);
309
310         return 0;
311 }
312
313
314 /* Delete a file
315  */
316 int captivefs_unlink(struct captivefs_vfs *captivefs_vfs,const char *file)
317 {
318 GnomeVFSResult errvfsresult;
319 CaptiveFileObject *captive_file_object;
320
321         g_return_val_if_fail(captivefs_vfs_validate(captivefs_vfs),-1);
322         g_return_val_if_fail(file!=NULL,-1);
323
324         if (captivefs_vfs->options.debug_messages)
325                 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"captivefs_unlink: file=%s",file);
326
327         file=CAPTIVEFS_FILENAME_TO_UTF8_ALLOCA(file);
328
329         captive_file_object=FileHandle_lookup_open_enter(captivefs_vfs,file,FALSE);
330         if (!captive_file_object)
331                 return -1;
332
333         G_LOCK(libcaptive);
334         errvfsresult=captive_file_remove(captive_file_object);
335         G_UNLOCK(libcaptive);
336
337         FileHandle_leave(captivefs_vfs,captive_file_object,file);
338
339         if (errvfsresult!=GNOME_VFS_OK)
340                 return -1;
341
342         return 0;
343 }
344
345
346 /* Rename a file/dir
347  */
348 int captivefs_rename(struct captivefs_vfs *captivefs_vfs,const char *old_name,const char *new_name)
349 {
350 GnomeVFSResult errvfsresult;
351 CaptiveFileObject *captive_file_object;
352 gint use_count;
353
354         g_return_val_if_fail(captivefs_vfs_validate(captivefs_vfs),-1);
355         g_return_val_if_fail(old_name!=NULL,-1);
356         g_return_val_if_fail(new_name!=NULL,-1);
357
358         if (captivefs_vfs->options.debug_messages)
359                 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"captivefs_rename: old_name=%s,new_name=%s",old_name,new_name);
360
361         old_name=CAPTIVEFS_FILENAME_TO_UTF8_ALLOCA(old_name);
362         new_name=CAPTIVEFS_FILENAME_TO_UTF8_ALLOCA(new_name);
363
364         captive_file_object=FileHandle_lookup_open_enter(captivefs_vfs,old_name,FALSE);
365         if (!captive_file_object)
366                 return -1;
367
368         G_LOCK(FileHandle_hash);
369         use_count=FileHandle_get_use_count(captive_file_object);
370         g_assert(use_count>=1);
371         if (use_count!=1) {
372                 if (captivefs_vfs->options.debug_messages)
373                         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"captivefs_rename: old_name=%s,new_name=%s: BUSY use_count=%d",
374                                         old_name,new_name,(int)use_count);
375                 G_UNLOCK(FileHandle_hash);
376                 FileHandle_leave(captivefs_vfs,captive_file_object,old_name);
377                 return -1;
378                 }
379
380         G_LOCK(libcaptive);
381         errvfsresult=captive_file_move(captive_file_object,new_name,
382                         FALSE); /* force_replace */
383         G_UNLOCK(libcaptive);
384
385         FileHandle_leave_locked(captivefs_vfs,captive_file_object,old_name);
386         G_UNLOCK(FileHandle_hash);
387
388         if (errvfsresult!=GNOME_VFS_OK)
389                 return -1;
390
391         return 0;
392 }
393
394
395 /* Open a file
396  *
397  * Notes:
398  *     By default, LUFS has no concept of file handles.  To implement file
399  * handles, take a look at the atbl class in sshfs - it is easy to cut&paste
400  * for use, and can be easily adapted for whatever purpose you need handles
401  * for.
402  *
403  *     Unlike the POSIX open command which has both a "mode" variable and
404  * a "flags" variable, this only has a "mode" variable.  To convert to the
405  * POSIX version, ->flags=mode^O_ACCMODE and ->mode=mode&O_ACCMODE.
406  */
407 int captivefs_open(struct captivefs_vfs *captivefs_vfs,const char *file,unsigned mode)
408 {
409 CaptiveFileObject *captive_file_object;
410 GnomeVFSOpenMode gnome_vfs_open_mode;
411
412         /* We may be called from the parent. */
413         g_return_val_if_fail(captivefs_vfs!=NULL,FALSE);
414         g_return_val_if_fail(file!=NULL,-1);
415
416         if (captivefs_vfs->options.debug_messages)
417                 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"captivefs_open: file=%s,mode=0x%X",file,mode);
418
419         file=CAPTIVEFS_FILENAME_TO_UTF8_ALLOCA(file);
420
421         if (!captivefs_vfs_validate(captivefs_vfs))
422                 return -1;
423
424         if (mode & ~(O_RDONLY|O_WRONLY|O_RDWR)) {
425                 g_warning("captivefs_open: Unrecognized 'mode' 0x%X",mode);
426                 return -1;
427                 }
428         switch (mode & (O_RDONLY|O_WRONLY|O_RDWR)) {
429                 case O_RDONLY: gnome_vfs_open_mode=GNOME_VFS_OPEN_READ;  break;
430                 case O_WRONLY: gnome_vfs_open_mode=GNOME_VFS_OPEN_WRITE; break;
431                 case O_RDWR:   gnome_vfs_open_mode=GNOME_VFS_OPEN_READ|GNOME_VFS_OPEN_WRITE; break;
432                 default:
433                         g_warning("captivefs_open: Unrecognized 'mode' 0x%X",mode);
434                         return -1;
435                 }
436
437         captive_file_object=FileHandle_lookup_open_enter(captivefs_vfs,file,FALSE);
438         if (!captive_file_object)
439                 return -1;
440
441         /* Leave 'captive_file_object' entered. */
442         return 0;
443 }
444
445
446 int captivefs_release(struct captivefs_vfs *captivefs_vfs,const char *file)
447 {
448 CaptiveFileObject *captive_file_object;
449
450         g_return_val_if_fail(captivefs_vfs_validate(captivefs_vfs),-1);
451         g_return_val_if_fail(file!=NULL,-1);
452
453         if (captivefs_vfs->options.debug_messages)
454                 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"captivefs_release: file=%s",file);
455
456         file=CAPTIVEFS_FILENAME_TO_UTF8_ALLOCA(file);
457
458         if (!(captive_file_object=FileHandle_lookup_enter(captivefs_vfs,file)))
459                 return -1;
460
461         FileHandle_leave(captivefs_vfs,captive_file_object,file);       /* for FileHandle_lookup_enter() */
462         FileHandle_leave(captivefs_vfs,captive_file_object,file);
463
464         return 0;
465 }
466
467
468 /* Read from a file. Changed to use the (2) routines not for efficiency,
469  * but to make it work with 64-bit offsets :-(.
470  */
471 int captivefs_read(struct captivefs_vfs *captivefs_vfs,const char *file,long long offset,unsigned long count,void *buf)
472 {
473 CaptiveFileObject *captive_file_object;
474 GnomeVFSFileSize bytes_read;
475 GnomeVFSResult errvfsresult;
476
477         g_return_val_if_fail(captivefs_vfs_validate(captivefs_vfs),-1);
478         g_return_val_if_fail(file!=NULL,-1);
479         g_return_val_if_fail(buf!=NULL || count==0,-1);
480
481         if (captivefs_vfs->options.debug_messages)
482                 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"captivefs_read: file=%s,offset=%" G_GINT64_FORMAT ",count=0x%lX",
483                                 file,(gint64)offset,count);
484
485         file=CAPTIVEFS_FILENAME_TO_UTF8_ALLOCA(file);
486
487         if (!(captive_file_object=FileHandle_lookup_enter(captivefs_vfs,file)))
488                 return -1;
489
490         /* Do not unlock 'libcaptive' between seek() and read()! */
491         G_LOCK(libcaptive);
492         errvfsresult=captive_file_seek(captive_file_object,GNOME_VFS_SEEK_START,offset);
493         if (errvfsresult!=GNOME_VFS_OK) {
494                 G_UNLOCK(libcaptive);
495                 FileHandle_leave(captivefs_vfs,captive_file_object,file);
496                 return -1;
497                 }
498
499         errvfsresult=captive_file_read(captive_file_object,buf,count,&bytes_read);
500         G_UNLOCK(libcaptive);
501         FileHandle_leave(captivefs_vfs,captive_file_object,file);
502         if (errvfsresult!=GNOME_VFS_OK)
503                 return -1;
504
505         if (bytes_read>INT_MAX) {
506                 g_warning("captivefs_read: Read %" G_GUINT64_FORMAT " bytes > INT_MAX=%d; dropped data",
507                                 (guint64)bytes_read,INT_MAX);
508                 bytes_read=INT_MAX;
509                 }
510
511         return bytes_read;
512 }
513
514
515 /* Write to a file
516  */
517 int captivefs_write(struct captivefs_vfs *captivefs_vfs,const char *file,long long offset,unsigned long count,const void *buf)
518 {
519 CaptiveFileObject *captive_file_object;
520 GnomeVFSFileSize bytes_written;
521 GnomeVFSResult errvfsresult;
522
523         g_return_val_if_fail(captivefs_vfs_validate(captivefs_vfs),-1);
524         g_return_val_if_fail(file!=NULL,-1);
525         g_return_val_if_fail(buf!=NULL || count==0,-1);
526
527         if (captivefs_vfs->options.debug_messages)
528                 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"captivefs_write: file=%s,offset=%" G_GINT64_FORMAT ",count=0x%lX",
529                                 file,(gint64)offset,count);
530
531         file=CAPTIVEFS_FILENAME_TO_UTF8_ALLOCA(file);
532
533         if (!(captive_file_object=FileHandle_lookup_enter(captivefs_vfs,file)))
534                 return -1;
535
536         /* Do not unlock 'libcaptive' between seek() and write()! */
537         G_LOCK(libcaptive);
538         errvfsresult=captive_file_seek(captive_file_object,GNOME_VFS_SEEK_START,offset);
539         if (errvfsresult!=GNOME_VFS_OK) {
540                 G_UNLOCK(libcaptive);
541                 FileHandle_leave(captivefs_vfs,captive_file_object,file);
542                 return -1;
543                 }
544
545         errvfsresult=captive_file_write(captive_file_object,buf,count,&bytes_written);
546         G_UNLOCK(libcaptive);
547         FileHandle_leave(captivefs_vfs,captive_file_object,file);
548         if (errvfsresult!=GNOME_VFS_OK)
549                 return -1;
550
551         if (bytes_written>INT_MAX) {
552                 g_warning("captivefs_written: Written %" G_GUINT64_FORMAT " bytes > INT_MAX=%d; dropped data",
553                                 (guint64)bytes_written,INT_MAX);
554                 bytes_written=INT_MAX;
555                 }
556
557         return bytes_written;
558 }
559
560
561 /* Change a file/dir's attributes
562  */
563 int captivefs_setattr(struct captivefs_vfs *captivefs_vfs,const char *file,const struct lufs_fattr *fattr)
564 {
565 GnomeVFSFileInfo file_info;
566 GnomeVFSResult errvfsresult;
567 CaptiveFileObject *captive_file_object;
568
569         g_return_val_if_fail(captivefs_vfs_validate(captivefs_vfs),-1);
570         g_return_val_if_fail(file!=NULL,-1);
571         g_return_val_if_fail(fattr!=NULL,-1);
572
573         if (captivefs_vfs->options.debug_messages)
574                 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"captivefs_setattr: file=%s",file);
575
576         file=CAPTIVEFS_FILENAME_TO_UTF8_ALLOCA(file);
577
578         if (!captivefs_lufs_fattr_to_GnomeVFSFileInfo(&file_info,fattr))
579                 return -1;
580
581         captive_file_object=FileHandle_lookup_open_enter(captivefs_vfs,file,FALSE);
582         if (!captive_file_object)
583                 return -1;
584
585         G_LOCK(libcaptive);
586         errvfsresult=captive_file_file_info_set(captive_file_object,&file_info,
587                         0
588                                         | 0 /* 'GNOME_VFS_SET_FILE_INFO_NAME' is never set */
589                                         | (!(file_info.valid_fields&GNOME_VFS_FILE_INFO_FIELDS_PERMISSIONS) ? 0 : GNOME_VFS_SET_FILE_INFO_PERMISSIONS)
590                                         | 0 /* 'GNOME_VFS_SET_FILE_INFO_OWNER' is never set */
591                                         | (!(file_info.valid_fields&(0
592                                                                         | GNOME_VFS_FILE_INFO_FIELDS_ATIME
593                                                                         | GNOME_VFS_FILE_INFO_FIELDS_MTIME
594                                                                         | GNOME_VFS_FILE_INFO_FIELDS_CTIME))
595                                                         ? 0 : GNOME_VFS_SET_FILE_INFO_TIME));
596         G_UNLOCK(libcaptive);
597         G_LOCK(libcaptive);
598         if (errvfsresult==GNOME_VFS_OK)
599         errvfsresult=captive_file_truncate(captive_file_object,fattr->f_size);
600         G_UNLOCK(libcaptive);
601         FileHandle_leave(captivefs_vfs,captive_file_object,file);
602         if (errvfsresult!=GNOME_VFS_OK)
603                 return -1;
604
605         return 0;
606 }