63f9ffe62845544cd9a92440e262f8b6048c35a3
[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
31 #include <lufs/fs.h>
32
33
34 /* Read a file/dir's attributes
35  * Fill all relevant data into the fattr structure.
36  * The uid/gid fields are just ownership hints hints:
37  *    != 0 => we own the file
38  *    == 0 => we don't own it
39  * The credentials structure (if applicable and saved from _init)
40  * can help determine ownership based on remote uids/gids.
41  *
42  * Notes:
43  *     If your filesysem doesn't natively support '.' or '..',
44  * don't forget to special-case them here.
45  *     It is best to assume that name is a relative path, not an
46  * absolute one.  Thus, you need to either be keeping track of the
47  * last accessed directory in readdir, or, as this code does, changing
48  * to the current directory there.
49  */
50 int captivefs_stat(struct captivefs_vfs *captivefs_vfs,const char *name,struct lufs_fattr *fattr)
51 {
52 GnomeVFSResult errvfsresult;
53 CaptiveFileObject *captive_file_object;
54 GnomeVFSFileInfo file_info;
55
56         g_return_val_if_fail(captivefs_vfs_validate(captivefs_vfs),-1);
57         g_return_val_if_fail(name!=NULL,-1);
58         g_return_val_if_fail(fattr!=NULL,-1);
59
60         if (captivefs_vfs->options.debug_messages)
61                 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"captivefs_stat: name=%s",name);
62
63         G_LOCK(libcaptive);
64         errvfsresult=captive_file_new_open(&captive_file_object,captivefs_vfs->captive_vfs_object,name,GNOME_VFS_OPEN_READ);
65         G_UNLOCK(libcaptive);
66         if (errvfsresult!=GNOME_VFS_OK)
67                 goto fail;
68
69         G_LOCK(libcaptive);
70         errvfsresult=captive_file_file_info_get(captive_file_object,&file_info);
71         G_UNLOCK(libcaptive);
72         if (errvfsresult!=GNOME_VFS_OK)
73                 goto fail_unref;
74
75         G_LOCK(libcaptive);
76         g_object_unref(captive_file_object);
77         G_UNLOCK(libcaptive);
78
79         if (!captivefs_GnomeVFSFileInfo_to_lufs_fattr(fattr,&file_info))
80                 return -1;
81
82         return 0;
83
84 fail_unref:
85         G_LOCK(libcaptive);
86         g_object_unref(captive_file_object);
87         G_UNLOCK(libcaptive);
88 fail:
89         return -1;
90 }
91
92
93 /* Create a file
94  */
95 int captivefs_create(struct captivefs_vfs *captivefs_vfs,const char *file,int mode)
96 {
97 GnomeVFSResult errvfsresult;
98 CaptiveFileObject *captive_file_object;
99
100         g_return_val_if_fail(captivefs_vfs_validate(captivefs_vfs),-1);
101         g_return_val_if_fail(file!=NULL,-1);
102
103         if (captivefs_vfs->options.debug_messages)
104                 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"captivefs_create: file=%s,mode=0x%X",file,mode);
105
106         G_LOCK(libcaptive);
107         errvfsresult=captive_file_new_create(&captive_file_object,captivefs_vfs->captive_vfs_object,file,GNOME_VFS_OPEN_WRITE,
108                         FALSE,  /* exclusive */
109                         mode);  /* perm */
110         G_UNLOCK(libcaptive);
111         if (errvfsresult!=GNOME_VFS_OK)
112                 return -1;
113
114         G_LOCK(libcaptive);
115         g_object_unref(captive_file_object);
116         G_UNLOCK(libcaptive);
117
118         return 0;
119 }
120
121
122 /* Delete a file
123  */
124 int captivefs_unlink(struct captivefs_vfs *captivefs_vfs,const char *file)
125 {
126 GnomeVFSResult errvfsresult;
127 CaptiveFileObject *captive_file_object;
128
129         g_return_val_if_fail(captivefs_vfs_validate(captivefs_vfs),-1);
130         g_return_val_if_fail(file!=NULL,-1);
131
132         if (captivefs_vfs->options.debug_messages)
133                 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"captivefs_unlink: file=%s",file);
134
135         G_LOCK(libcaptive);
136         errvfsresult=captive_file_new_open(&captive_file_object,captivefs_vfs->captive_vfs_object,file,GNOME_VFS_OPEN_WRITE);
137         G_UNLOCK(libcaptive);
138         if (errvfsresult!=GNOME_VFS_OK)
139                 goto fail;
140
141         G_LOCK(libcaptive);
142         errvfsresult=captive_file_remove(captive_file_object);
143         G_UNLOCK(libcaptive);
144         if (errvfsresult!=GNOME_VFS_OK)
145                 goto fail_unref;
146
147         G_LOCK(libcaptive);
148         g_object_unref(captive_file_object);
149         G_UNLOCK(libcaptive);
150
151         return 0;
152
153 fail_unref:
154         G_LOCK(libcaptive);
155         g_object_unref(captive_file_object);
156         G_UNLOCK(libcaptive);
157 fail:
158         return -1;
159 }
160
161
162 /* Rename a file/dir
163  */
164 int captivefs_rename(struct captivefs_vfs *captivefs_vfs,const char *old_name,const char *new_name)
165 {
166 GnomeVFSResult errvfsresult;
167 CaptiveFileObject *captive_file_object;
168
169         g_return_val_if_fail(captivefs_vfs_validate(captivefs_vfs),-1);
170         g_return_val_if_fail(old_name!=NULL,-1);
171         g_return_val_if_fail(new_name!=NULL,-1);
172
173         if (captivefs_vfs->options.debug_messages)
174                 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"captivefs_rename: old_name=%s,new_name=%s",old_name,new_name);
175
176         G_LOCK(libcaptive);
177         errvfsresult=captive_file_new_open(&captive_file_object,captivefs_vfs->captive_vfs_object,old_name,GNOME_VFS_OPEN_WRITE);
178         G_UNLOCK(libcaptive);
179         if (errvfsresult!=GNOME_VFS_OK)
180                 goto fail;
181
182         G_LOCK(libcaptive);
183         errvfsresult=captive_file_move(captive_file_object,new_name,
184                         FALSE); /* force_replace */
185         G_UNLOCK(libcaptive);
186         if (errvfsresult!=GNOME_VFS_OK)
187                 goto fail_unref;
188
189         G_LOCK(libcaptive);
190         g_object_unref(captive_file_object);
191         G_UNLOCK(libcaptive);
192
193         return 0;
194
195 fail_unref:
196         G_LOCK(libcaptive);
197         g_object_unref(captive_file_object);
198         G_UNLOCK(libcaptive);
199 fail:
200         return -1;
201 }
202
203
204 /* map: (const gchar *) -> (CaptiveFileObject *) */
205 static GHashTable *FileHandle_hash;
206 G_LOCK_DEFINE_STATIC(FileHandle_hash);
207
208 static void FileHandle_hash_value_destroy_func(CaptiveFileObject *captive_file_object)
209 {
210         g_return_if_fail(CAPTIVE_FILE_IS_OBJECT(captive_file_object));
211
212         G_LOCK(libcaptive);
213         g_object_unref(captive_file_object);
214         G_UNLOCK(libcaptive);
215 }
216
217 static void FileHandle_hash_init(void)
218 {
219         G_LOCK(FileHandle_hash);
220         if (!FileHandle_hash) {
221                 FileHandle_hash=g_hash_table_new_full(g_str_hash,g_str_equal,
222                                 (GDestroyNotify)g_free, /* key_destroy_func */
223                                 (GDestroyNotify)FileHandle_hash_value_destroy_func);    /* value_destroy_func */
224                 }
225         G_UNLOCK(FileHandle_hash);
226 }
227
228 /* FIXME: g_object_ref() the resulting 'CaptiveFileObject'.
229  * Currently we assume open/ops/release is done in single thread.
230  */
231 static CaptiveFileObject *FileHandle_lookup(const char *name)
232 {
233 CaptiveFileObject *captive_file_object;
234
235         g_return_val_if_fail(name!=NULL,NULL);
236
237         FileHandle_hash_init();
238         G_LOCK(FileHandle_hash);
239         captive_file_object=g_hash_table_lookup(FileHandle_hash,name);
240         G_UNLOCK(FileHandle_hash);
241
242         if (!captive_file_object) {
243                 g_warning("FileHandle_lookup: FileHandle not found of: %s",name);
244                 return NULL;
245                 }
246
247         g_assert(CAPTIVE_FILE_IS_OBJECT(captive_file_object));
248
249         return captive_file_object;
250 }
251
252
253 /* Open a file
254  *
255  * Notes:
256  *     By default, LUFS has no concept of file handles.  To implement file
257  * handles, take a look at the atbl class in sshfs - it is easy to cut&paste
258  * for use, and can be easily adapted for whatever purpose you need handles
259  * for.
260  *
261  *     Unlike the POSIX open command which has both a "mode" variable and
262  * a "flags" variable, this only has a "mode" variable.  To convert to the
263  * POSIX version, ->flags=mode^O_ACCMODE and ->mode=mode&O_ACCMODE.
264  */
265 int captivefs_open(struct captivefs_vfs *captivefs_vfs,const char *file,unsigned mode)
266 {
267 CaptiveFileObject *captive_file_object;
268 GnomeVFSOpenMode gnome_vfs_open_mode;
269 GnomeVFSResult errvfsresult;
270
271         /* We may be called from the parent. */
272         g_return_val_if_fail(captivefs_vfs!=NULL,FALSE);
273         g_return_val_if_fail(file!=NULL,-1);
274
275         if (captivefs_vfs->options.debug_messages)
276                 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"captivefs_open: file=%s,mode=0x%X",file,mode);
277
278         if (!captivefs_vfs_validate(captivefs_vfs))
279                 return -1;
280
281         FileHandle_hash_init();
282         G_LOCK(FileHandle_hash);
283         captive_file_object=g_hash_table_lookup(FileHandle_hash,file);
284         G_UNLOCK(FileHandle_hash);
285         if (captive_file_object) {
286                 g_assert(CAPTIVE_FILE_IS_OBJECT(captive_file_object));
287                 g_warning("captivefs_open: FileHandle already exists of: %s",file);
288                 return -1;
289                 }
290
291         if (mode & ~(O_RDONLY|O_WRONLY|O_RDWR)) {
292                 g_warning("captivefs_open: Unrecognized 'mode' 0x%X",mode);
293                 return -1;
294                 }
295         switch (mode & (O_RDONLY|O_WRONLY|O_RDWR)) {
296                 case O_RDONLY: gnome_vfs_open_mode=GNOME_VFS_OPEN_READ;  break;
297                 case O_WRONLY: gnome_vfs_open_mode=GNOME_VFS_OPEN_WRITE; break;
298                 case O_RDWR:   gnome_vfs_open_mode=GNOME_VFS_OPEN_READ|GNOME_VFS_OPEN_WRITE; break;
299                 default:
300                         g_warning("captivefs_open: Unrecognized 'mode' 0x%X",mode);
301                         return -1;
302                 }
303
304         G_LOCK(libcaptive);
305         errvfsresult=captive_file_new_open(&captive_file_object,captivefs_vfs->captive_vfs_object,file,
306                         gnome_vfs_open_mode|GNOME_VFS_OPEN_RANDOM);
307         G_UNLOCK(libcaptive);
308         if (errvfsresult!=GNOME_VFS_OK)
309                 return -1;
310
311         g_hash_table_insert(FileHandle_hash,g_strdup(file),captive_file_object);
312
313         return 0;
314 }
315
316
317 int captivefs_release(struct captivefs_vfs *captivefs_vfs,const char *file)
318 {
319 gboolean errbool;
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_release: file=%s",file);
326
327         FileHandle_hash_init();
328         G_LOCK(FileHandle_hash);
329         errbool=g_hash_table_remove(FileHandle_hash,file);
330         G_UNLOCK(FileHandle_hash);
331
332         if (!errbool) {
333                 g_warning("captivefs_release: FileHandle not found of: %s",file);
334                 return -1;
335                 }
336
337         return 0;
338 }
339
340
341 /* Read from a file. Changed to use the (2) routines not for efficiency,
342  * but to make it work with 64-bit offsets :-(.
343  */
344 int captivefs_read(struct captivefs_vfs *captivefs_vfs,const char *file,long long offset,unsigned long count,void *buf)
345 {
346 CaptiveFileObject *captive_file_object;
347 GnomeVFSFileSize bytes_read;
348 GnomeVFSResult errvfsresult;
349
350         g_return_val_if_fail(captivefs_vfs_validate(captivefs_vfs),-1);
351         g_return_val_if_fail(file!=NULL,-1);
352         g_return_val_if_fail(buf!=NULL || count==0,-1);
353
354         if (captivefs_vfs->options.debug_messages)
355                 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"captivefs_read: file=%s,offset=%" G_GINT64_FORMAT ",count=0x%lX",
356                                 file,(gint64)offset,count);
357
358         if (!(captive_file_object=FileHandle_lookup(file)))
359                 return -1;
360
361         G_LOCK(libcaptive);
362         errvfsresult=captive_file_seek(captive_file_object,GNOME_VFS_SEEK_START,offset);
363         G_UNLOCK(libcaptive);
364         if (errvfsresult!=GNOME_VFS_OK)
365                 return -1;
366
367         G_LOCK(libcaptive);
368         errvfsresult=captive_file_read(captive_file_object,buf,count,&bytes_read);
369         G_UNLOCK(libcaptive);
370         if (errvfsresult!=GNOME_VFS_OK)
371                 return -1;
372
373         if (bytes_read>INT_MAX) {
374                 g_warning("captivefs_read: Read %" G_GUINT64_FORMAT " bytes > INT_MAX=%d; dropped data",
375                                 (guint64)bytes_read,INT_MAX);
376                 bytes_read=INT_MAX;
377                 }
378
379         return bytes_read;
380 }
381
382
383 /* Write to a file
384  */
385 int captivefs_write(struct captivefs_vfs *captivefs_vfs,const char *file,long long offset,unsigned long count,const void *buf)
386 {
387 CaptiveFileObject *captive_file_object;
388 GnomeVFSFileSize bytes_written;
389 GnomeVFSResult errvfsresult;
390
391         g_return_val_if_fail(captivefs_vfs_validate(captivefs_vfs),-1);
392         g_return_val_if_fail(file!=NULL,-1);
393         g_return_val_if_fail(buf!=NULL || count==0,-1);
394
395         if (captivefs_vfs->options.debug_messages)
396                 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"captivefs_write: file=%s,offset=%" G_GINT64_FORMAT ",count=0x%lX",
397                                 file,(gint64)offset,count);
398
399         if (!(captive_file_object=FileHandle_lookup(file)))
400                 return -1;
401
402         G_LOCK(libcaptive);
403         errvfsresult=captive_file_seek(captive_file_object,GNOME_VFS_SEEK_START,offset);
404         G_UNLOCK(libcaptive);
405         if (errvfsresult!=GNOME_VFS_OK)
406                 return -1;
407
408         G_LOCK(libcaptive);
409         errvfsresult=captive_file_write(captive_file_object,buf,count,&bytes_written);
410         G_UNLOCK(libcaptive);
411         if (errvfsresult!=GNOME_VFS_OK)
412                 return -1;
413
414         if (bytes_written>INT_MAX) {
415                 g_warning("captivefs_written: Written %" G_GUINT64_FORMAT " bytes > INT_MAX=%d; dropped data",
416                                 (guint64)bytes_written,INT_MAX);
417                 bytes_written=INT_MAX;
418                 }
419
420         return bytes_written;
421 }
422
423
424 /* Change a file/dir's attributes
425  */
426 int captivefs_setattr(struct captivefs_vfs *captivefs_vfs,const char *file,const struct lufs_fattr *fattr)
427 {
428 GnomeVFSFileInfo file_info;
429 GnomeVFSResult errvfsresult;
430 CaptiveFileObject *captive_file_object;
431
432         g_return_val_if_fail(captivefs_vfs_validate(captivefs_vfs),-1);
433         g_return_val_if_fail(file!=NULL,-1);
434         g_return_val_if_fail(fattr!=NULL,-1);
435
436         if (captivefs_vfs->options.debug_messages)
437                 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"captivefs_setattr: file=%s",file);
438
439         if (!captivefs_lufs_fattr_to_GnomeVFSFileInfo(&file_info,fattr))
440                 return -1;
441
442         G_LOCK(libcaptive);
443         errvfsresult=captive_file_new_open(&captive_file_object,captivefs_vfs->captive_vfs_object,file,GNOME_VFS_OPEN_WRITE);
444         G_UNLOCK(libcaptive);
445         if (errvfsresult!=GNOME_VFS_OK)
446                 goto fail;
447
448         G_LOCK(libcaptive);
449         errvfsresult=captive_file_file_info_set(captive_file_object,&file_info,
450                         0
451                                         | 0 /* 'GNOME_VFS_SET_FILE_INFO_NAME' is never set */
452                                         | (!(file_info.valid_fields&GNOME_VFS_FILE_INFO_FIELDS_PERMISSIONS) ? 0 : GNOME_VFS_SET_FILE_INFO_PERMISSIONS)
453                                         | 0 /* 'GNOME_VFS_SET_FILE_INFO_OWNER' is never set */
454                                         | (!(file_info.valid_fields&(0
455                                                                         | GNOME_VFS_FILE_INFO_FIELDS_ATIME
456                                                                         | GNOME_VFS_FILE_INFO_FIELDS_MTIME
457                                                                         | GNOME_VFS_FILE_INFO_FIELDS_CTIME))
458                                                         ? 0 : GNOME_VFS_SET_FILE_INFO_TIME));
459         G_UNLOCK(libcaptive);
460         if (errvfsresult!=GNOME_VFS_OK)
461                 goto fail_unref;
462
463         G_LOCK(libcaptive);
464         g_object_unref(captive_file_object);
465         G_UNLOCK(libcaptive);
466
467         return 0;
468
469 fail_unref:
470         G_LOCK(libcaptive);
471         g_object_unref(captive_file_object);
472         G_UNLOCK(libcaptive);
473 fail:
474         return -1;
475 }