Fixed reading of HIDDEN SYSTEM files.
[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                 /* HIDDEN SYSTEM files (FIXME: or just HIDDEN or just SYSTEM?)
218                  * refuse to be GNOME_VFS_OPEN_WRITE-opened.
219                  */
220                 if (errvfsresult==GNOME_VFS_ERROR_ACCESS_DENIED) {
221                         G_LOCK(libcaptive);
222                         errvfsresult=captive_file_new_open(&captive_file_object,captivefs_vfs->captive_vfs_object,name_normalized,
223                                         GNOME_VFS_OPEN_READ|GNOME_VFS_OPEN_RANDOM);
224                         G_UNLOCK(libcaptive);
225                         }
226                 }
227         else {
228                 G_LOCK(libcaptive);
229                 errvfsresult=captive_file_new_create(&captive_file_object,captivefs_vfs->captive_vfs_object,name_normalized,
230                                 GNOME_VFS_OPEN_READ|GNOME_VFS_OPEN_WRITE|GNOME_VFS_OPEN_RANDOM, /* mode */
231                                 FALSE,  /* exclusive */
232                                 0600);  /* perm */
233                 G_UNLOCK(libcaptive);
234                 }
235         if (errvfsresult!=GNOME_VFS_OK) {
236                 G_UNLOCK(FileHandle_hash);
237                 g_free(name_normalized);
238                 return NULL;
239                 }
240         FileHandle_enter(captivefs_vfs,captive_file_object,name_normalized);
241         G_UNLOCK(FileHandle_hash);
242         g_object_unref(captive_file_object);    /* for captive_file_new_open() / captive_file_new_create() */
243         g_free(name_normalized);
244
245         return captive_file_object;
246 }
247
248 /* Read a file/dir's attributes
249  * Fill all relevant data into the fattr structure.
250  * The uid/gid fields are just ownership hints hints:
251  *    != 0 => we own the file
252  *    == 0 => we don't own it
253  * The credentials structure (if applicable and saved from _init)
254  * can help determine ownership based on remote uids/gids.
255  *
256  * Notes:
257  *     If your filesysem doesn't natively support '.' or '..',
258  * don't forget to special-case them here.
259  *     It is best to assume that name is a relative path, not an
260  * absolute one.  Thus, you need to either be keeping track of the
261  * last accessed directory in readdir, or, as this code does, changing
262  * to the current directory there.
263  */
264 int captivefs_stat(struct captivefs_vfs *captivefs_vfs,const char *name,struct lufs_fattr *fattr)
265 {
266 GnomeVFSResult errvfsresult;
267 CaptiveFileObject *captive_file_object;
268 GnomeVFSFileInfo file_info;
269
270         g_return_val_if_fail(captivefs_vfs_validate(captivefs_vfs),-1);
271         g_return_val_if_fail(name!=NULL,-1);
272         g_return_val_if_fail(fattr!=NULL,-1);
273
274         if (captivefs_vfs->options.debug_messages)
275                 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"captivefs_stat: name=%s",name);
276
277         name=CAPTIVEFS_FILENAME_TO_UTF8_ALLOCA(name);
278
279         captive_file_object=FileHandle_lookup_open_enter(captivefs_vfs,name,FALSE);
280         if (!captive_file_object)
281                 return -1;
282
283         G_LOCK(libcaptive);
284         errvfsresult=captive_file_file_info_get(captive_file_object,&file_info);
285         G_UNLOCK(libcaptive);
286
287         FileHandle_leave(captivefs_vfs,captive_file_object,name);
288
289         if (errvfsresult!=GNOME_VFS_OK)
290                 return -1;
291
292         if (!captivefs_GnomeVFSFileInfo_to_lufs_fattr(captivefs_vfs,fattr,&file_info))
293                 return -1;
294
295         return 0;
296 }
297
298
299 /* Create a file
300  */
301 int captivefs_create(struct captivefs_vfs *captivefs_vfs,const char *file,int mode)
302 {
303 CaptiveFileObject *captive_file_object;
304
305         g_return_val_if_fail(captivefs_vfs_validate(captivefs_vfs),-1);
306         g_return_val_if_fail(file!=NULL,-1);
307
308         if (captivefs_vfs->options.debug_messages)
309                 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"captivefs_create: file=%s,mode=0x%X",file,mode);
310
311         file=CAPTIVEFS_FILENAME_TO_UTF8_ALLOCA(file);
312
313         captive_file_object=FileHandle_lookup_open_enter(captivefs_vfs,file,TRUE);
314         if (!captive_file_object)
315                 return -1;
316
317         FileHandle_leave(captivefs_vfs,captive_file_object,file);
318
319         return 0;
320 }
321
322
323 /* Delete a file
324  */
325 int captivefs_unlink(struct captivefs_vfs *captivefs_vfs,const char *file)
326 {
327 GnomeVFSResult errvfsresult;
328 CaptiveFileObject *captive_file_object;
329
330         g_return_val_if_fail(captivefs_vfs_validate(captivefs_vfs),-1);
331         g_return_val_if_fail(file!=NULL,-1);
332
333         if (captivefs_vfs->options.debug_messages)
334                 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"captivefs_unlink: file=%s",file);
335
336         file=CAPTIVEFS_FILENAME_TO_UTF8_ALLOCA(file);
337
338         captive_file_object=FileHandle_lookup_open_enter(captivefs_vfs,file,FALSE);
339         if (!captive_file_object)
340                 return -1;
341
342         G_LOCK(libcaptive);
343         errvfsresult=captive_file_remove(captive_file_object);
344         G_UNLOCK(libcaptive);
345
346         FileHandle_leave(captivefs_vfs,captive_file_object,file);
347
348         if (errvfsresult!=GNOME_VFS_OK)
349                 return -1;
350
351         return 0;
352 }
353
354
355 /* Rename a file/dir
356  */
357 int captivefs_rename(struct captivefs_vfs *captivefs_vfs,const char *old_name,const char *new_name)
358 {
359 GnomeVFSResult errvfsresult;
360 CaptiveFileObject *captive_file_object;
361 gint use_count;
362
363         g_return_val_if_fail(captivefs_vfs_validate(captivefs_vfs),-1);
364         g_return_val_if_fail(old_name!=NULL,-1);
365         g_return_val_if_fail(new_name!=NULL,-1);
366
367         if (captivefs_vfs->options.debug_messages)
368                 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"captivefs_rename: old_name=%s,new_name=%s",old_name,new_name);
369
370         old_name=CAPTIVEFS_FILENAME_TO_UTF8_ALLOCA(old_name);
371         new_name=CAPTIVEFS_FILENAME_TO_UTF8_ALLOCA(new_name);
372
373         captive_file_object=FileHandle_lookup_open_enter(captivefs_vfs,old_name,FALSE);
374         if (!captive_file_object)
375                 return -1;
376
377         G_LOCK(FileHandle_hash);
378         use_count=FileHandle_get_use_count(captive_file_object);
379         g_assert(use_count>=1);
380         if (use_count!=1) {
381                 if (captivefs_vfs->options.debug_messages)
382                         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"captivefs_rename: old_name=%s,new_name=%s: BUSY use_count=%d",
383                                         old_name,new_name,(int)use_count);
384                 G_UNLOCK(FileHandle_hash);
385                 FileHandle_leave(captivefs_vfs,captive_file_object,old_name);
386                 return -1;
387                 }
388
389         G_LOCK(libcaptive);
390         errvfsresult=captive_file_move(captive_file_object,new_name,
391                         FALSE); /* force_replace */
392         G_UNLOCK(libcaptive);
393
394         FileHandle_leave_locked(captivefs_vfs,captive_file_object,old_name);
395         G_UNLOCK(FileHandle_hash);
396
397         if (errvfsresult!=GNOME_VFS_OK)
398                 return -1;
399
400         return 0;
401 }
402
403
404 /* Open a file
405  *
406  * Notes:
407  *     By default, LUFS has no concept of file handles.  To implement file
408  * handles, take a look at the atbl class in sshfs - it is easy to cut&paste
409  * for use, and can be easily adapted for whatever purpose you need handles
410  * for.
411  *
412  *     Unlike the POSIX open command which has both a "mode" variable and
413  * a "flags" variable, this only has a "mode" variable.  To convert to the
414  * POSIX version, ->flags=mode^O_ACCMODE and ->mode=mode&O_ACCMODE.
415  */
416 int captivefs_open(struct captivefs_vfs *captivefs_vfs,const char *file,unsigned mode)
417 {
418 CaptiveFileObject *captive_file_object;
419 GnomeVFSOpenMode gnome_vfs_open_mode;
420
421         /* We may be called from the parent. */
422         g_return_val_if_fail(captivefs_vfs!=NULL,FALSE);
423         g_return_val_if_fail(file!=NULL,-1);
424
425         if (captivefs_vfs->options.debug_messages)
426                 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"captivefs_open: file=%s,mode=0x%X",file,mode);
427
428         file=CAPTIVEFS_FILENAME_TO_UTF8_ALLOCA(file);
429
430         if (!captivefs_vfs_validate(captivefs_vfs))
431                 return -1;
432
433         if (mode & ~(O_RDONLY|O_WRONLY|O_RDWR)) {
434                 g_warning("captivefs_open: Unrecognized 'mode' 0x%X",mode);
435                 return -1;
436                 }
437         switch (mode & (O_RDONLY|O_WRONLY|O_RDWR)) {
438                 case O_RDONLY: gnome_vfs_open_mode=GNOME_VFS_OPEN_READ;  break;
439                 case O_WRONLY: gnome_vfs_open_mode=GNOME_VFS_OPEN_WRITE; break;
440                 case O_RDWR:   gnome_vfs_open_mode=GNOME_VFS_OPEN_READ|GNOME_VFS_OPEN_WRITE; break;
441                 default:
442                         g_warning("captivefs_open: Unrecognized 'mode' 0x%X",mode);
443                         return -1;
444                 }
445
446         captive_file_object=FileHandle_lookup_open_enter(captivefs_vfs,file,FALSE);
447         if (!captive_file_object)
448                 return -1;
449
450         /* Leave 'captive_file_object' entered. */
451         return 0;
452 }
453
454
455 int captivefs_release(struct captivefs_vfs *captivefs_vfs,const char *file)
456 {
457 CaptiveFileObject *captive_file_object;
458
459         g_return_val_if_fail(captivefs_vfs_validate(captivefs_vfs),-1);
460         g_return_val_if_fail(file!=NULL,-1);
461
462         if (captivefs_vfs->options.debug_messages)
463                 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"captivefs_release: file=%s",file);
464
465         file=CAPTIVEFS_FILENAME_TO_UTF8_ALLOCA(file);
466
467         if (!(captive_file_object=FileHandle_lookup_enter(captivefs_vfs,file)))
468                 return -1;
469
470         FileHandle_leave(captivefs_vfs,captive_file_object,file);       /* for FileHandle_lookup_enter() */
471         FileHandle_leave(captivefs_vfs,captive_file_object,file);
472
473         return 0;
474 }
475
476
477 /* Read from a file. Changed to use the (2) routines not for efficiency,
478  * but to make it work with 64-bit offsets :-(.
479  */
480 int captivefs_read(struct captivefs_vfs *captivefs_vfs,const char *file,long long offset,unsigned long count,void *buf)
481 {
482 CaptiveFileObject *captive_file_object;
483 GnomeVFSFileSize bytes_read;
484 GnomeVFSResult errvfsresult;
485
486         g_return_val_if_fail(captivefs_vfs_validate(captivefs_vfs),-1);
487         g_return_val_if_fail(file!=NULL,-1);
488         g_return_val_if_fail(buf!=NULL || count==0,-1);
489
490         if (captivefs_vfs->options.debug_messages)
491                 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"captivefs_read: file=%s,offset=%" G_GINT64_FORMAT ",count=0x%lX",
492                                 file,(gint64)offset,count);
493
494         file=CAPTIVEFS_FILENAME_TO_UTF8_ALLOCA(file);
495
496         if (!(captive_file_object=FileHandle_lookup_enter(captivefs_vfs,file)))
497                 return -1;
498
499         /* Do not unlock 'libcaptive' between seek() and read()! */
500         G_LOCK(libcaptive);
501         errvfsresult=captive_file_seek(captive_file_object,GNOME_VFS_SEEK_START,offset);
502         if (errvfsresult!=GNOME_VFS_OK) {
503                 G_UNLOCK(libcaptive);
504                 FileHandle_leave(captivefs_vfs,captive_file_object,file);
505                 return -1;
506                 }
507
508         errvfsresult=captive_file_read(captive_file_object,buf,count,&bytes_read);
509         G_UNLOCK(libcaptive);
510         FileHandle_leave(captivefs_vfs,captive_file_object,file);
511         if (errvfsresult!=GNOME_VFS_OK)
512                 return -1;
513
514         if (bytes_read>INT_MAX) {
515                 g_warning("captivefs_read: Read %" G_GUINT64_FORMAT " bytes > INT_MAX=%d; dropped data",
516                                 (guint64)bytes_read,INT_MAX);
517                 bytes_read=INT_MAX;
518                 }
519
520         return bytes_read;
521 }
522
523
524 /* Write to a file
525  */
526 int captivefs_write(struct captivefs_vfs *captivefs_vfs,const char *file,long long offset,unsigned long count,const void *buf)
527 {
528 CaptiveFileObject *captive_file_object;
529 GnomeVFSFileSize bytes_written;
530 GnomeVFSResult errvfsresult;
531
532         g_return_val_if_fail(captivefs_vfs_validate(captivefs_vfs),-1);
533         g_return_val_if_fail(file!=NULL,-1);
534         g_return_val_if_fail(buf!=NULL || count==0,-1);
535
536         if (captivefs_vfs->options.debug_messages)
537                 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"captivefs_write: file=%s,offset=%" G_GINT64_FORMAT ",count=0x%lX",
538                                 file,(gint64)offset,count);
539
540         file=CAPTIVEFS_FILENAME_TO_UTF8_ALLOCA(file);
541
542         if (!(captive_file_object=FileHandle_lookup_enter(captivefs_vfs,file)))
543                 return -1;
544
545         /* Do not unlock 'libcaptive' between seek() and write()! */
546         G_LOCK(libcaptive);
547         errvfsresult=captive_file_seek(captive_file_object,GNOME_VFS_SEEK_START,offset);
548         if (errvfsresult!=GNOME_VFS_OK) {
549                 G_UNLOCK(libcaptive);
550                 FileHandle_leave(captivefs_vfs,captive_file_object,file);
551                 return -1;
552                 }
553
554         errvfsresult=captive_file_write(captive_file_object,buf,count,&bytes_written);
555         G_UNLOCK(libcaptive);
556         FileHandle_leave(captivefs_vfs,captive_file_object,file);
557         if (errvfsresult!=GNOME_VFS_OK)
558                 return -1;
559
560         if (bytes_written>INT_MAX) {
561                 g_warning("captivefs_written: Written %" G_GUINT64_FORMAT " bytes > INT_MAX=%d; dropped data",
562                                 (guint64)bytes_written,INT_MAX);
563                 bytes_written=INT_MAX;
564                 }
565
566         return bytes_written;
567 }
568
569
570 /* Change a file/dir's attributes
571  */
572 int captivefs_setattr(struct captivefs_vfs *captivefs_vfs,const char *file,const struct lufs_fattr *fattr)
573 {
574 GnomeVFSFileInfo file_info;
575 GnomeVFSResult errvfsresult;
576 CaptiveFileObject *captive_file_object;
577
578         g_return_val_if_fail(captivefs_vfs_validate(captivefs_vfs),-1);
579         g_return_val_if_fail(file!=NULL,-1);
580         g_return_val_if_fail(fattr!=NULL,-1);
581
582         if (captivefs_vfs->options.debug_messages)
583                 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"captivefs_setattr: file=%s",file);
584
585         file=CAPTIVEFS_FILENAME_TO_UTF8_ALLOCA(file);
586
587         if (!captivefs_lufs_fattr_to_GnomeVFSFileInfo(&file_info,fattr))
588                 return -1;
589
590         captive_file_object=FileHandle_lookup_open_enter(captivefs_vfs,file,FALSE);
591         if (!captive_file_object)
592                 return -1;
593
594         G_LOCK(libcaptive);
595         errvfsresult=captive_file_file_info_set(captive_file_object,&file_info,
596                         0
597                                         | 0 /* 'GNOME_VFS_SET_FILE_INFO_NAME' is never set */
598                                         | (!(file_info.valid_fields&GNOME_VFS_FILE_INFO_FIELDS_PERMISSIONS) ? 0 : GNOME_VFS_SET_FILE_INFO_PERMISSIONS)
599                                         | 0 /* 'GNOME_VFS_SET_FILE_INFO_OWNER' is never set */
600                                         | (!(file_info.valid_fields&(0
601                                                                         | GNOME_VFS_FILE_INFO_FIELDS_ATIME
602                                                                         | GNOME_VFS_FILE_INFO_FIELDS_MTIME
603                                                                         | GNOME_VFS_FILE_INFO_FIELDS_CTIME))
604                                                         ? 0 : GNOME_VFS_SET_FILE_INFO_TIME));
605         G_UNLOCK(libcaptive);
606         G_LOCK(libcaptive);
607         if (errvfsresult==GNOME_VFS_OK)
608         errvfsresult=captive_file_truncate(captive_file_object,fattr->f_size);
609         G_UNLOCK(libcaptive);
610         FileHandle_leave(captivefs_vfs,captive_file_object,file);
611         if (errvfsresult!=GNOME_VFS_OK)
612                 return -1;
613
614         return 0;
615 }