2 * lufs interface module file objects implementation for libcaptive
3 * Copyright (C) 2003 Jan Kratochvil <project-captive@jankratochvil.net>
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
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.
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
22 #include <glib/gmessages.h>
23 #include "captivefs-misc.h"
24 #include "captivefs-attr.h"
25 #include "captivefs-vfs.h"
28 #include <captive/client-vfs.h>
29 #include <captive/client-file.h>
30 #include <captive/client.h>
36 #define CLIENT_LUFS_USE_COUNT "client-lufs-use_count"
39 /* map: (const gchar *) -> (CaptiveFileObject *) */
40 static GHashTable *FileHandle_hash;
41 G_LOCK_DEFINE_STATIC(FileHandle_hash);
43 static void FileHandle_hash_init(void)
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() */
51 G_UNLOCK(FileHandle_hash);
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.
60 static gint FileHandle_get_use_count(CaptiveFileObject *captive_file_object)
62 g_return_val_if_fail(CAPTIVE_FILE_IS_OBJECT(captive_file_object),0);
64 return GPOINTER_TO_INT(g_object_get_data(G_OBJECT(captive_file_object),CLIENT_LUFS_USE_COUNT));
67 static void FileHandle_set_use_count(CaptiveFileObject *captive_file_object,gint use_count)
69 g_return_if_fail(CAPTIVE_FILE_IS_OBJECT(captive_file_object));
71 g_object_set_data(G_OBJECT(captive_file_object),CLIENT_LUFS_USE_COUNT,GINT_TO_POINTER(use_count));
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)
80 g_return_if_fail(CAPTIVE_FILE_IS_OBJECT(captive_file_object));
81 g_return_if_fail(name_normalized);
83 use_count=FileHandle_get_use_count(captive_file_object);
84 g_assert(use_count>=0);
86 FileHandle_set_use_count(captive_file_object,use_count);
88 g_assert(captive_file_object==g_hash_table_lookup(FileHandle_hash,name_normalized));
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);
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)
103 gchar *name_normalized;
105 g_return_if_fail(CAPTIVE_FILE_IS_OBJECT(captive_file_object));
106 g_return_if_fail(name);
108 name_normalized=captive_path_normalize(name);
109 use_count=FileHandle_get_use_count(captive_file_object);
110 g_assert(use_count>=1);
112 FileHandle_set_use_count(captive_file_object,use_count);
113 g_assert(captive_file_object==g_hash_table_lookup(FileHandle_hash,name_normalized));
115 g_free(name_normalized);
118 errbool=g_hash_table_remove(FileHandle_hash,name_normalized);
119 g_assert(errbool==TRUE);
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);
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)
132 g_return_if_fail(CAPTIVE_FILE_IS_OBJECT(captive_file_object));
133 g_return_if_fail(name);
135 G_LOCK(FileHandle_hash);
136 FileHandle_leave_locked(captive_file_object,name);
137 G_UNLOCK(FileHandle_hash);
140 /* 'name' is required to exist. */
141 static CaptiveFileObject *FileHandle_lookup_enter(const char *name)
143 CaptiveFileObject *captive_file_object;
144 gchar *name_normalized;
146 g_return_val_if_fail(name!=NULL,NULL);
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);
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);
163 return captive_file_object;
166 /* 'name' will be opened if needed. */
167 static CaptiveFileObject *FileHandle_lookup_open_enter(struct captivefs_vfs *captivefs_vfs,const char *name,gboolean create)
169 CaptiveFileObject *captive_file_object;
170 GnomeVFSResult errvfsresult;
171 gchar *name_normalized;
173 g_return_val_if_fail(name!=NULL,NULL);
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;
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);
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.
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);
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 */
211 G_UNLOCK(libcaptive);
213 if (errvfsresult!=GNOME_VFS_OK) {
214 G_UNLOCK(FileHandle_hash);
215 g_free(name_normalized);
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);
223 return captive_file_object;
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.
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.
242 int captivefs_stat(struct captivefs_vfs *captivefs_vfs,const char *name,struct lufs_fattr *fattr)
244 GnomeVFSResult errvfsresult;
245 CaptiveFileObject *captive_file_object;
246 GnomeVFSFileInfo file_info;
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);
252 if (captivefs_vfs->options.debug_messages)
253 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"captivefs_stat: name=%s",name);
255 captive_file_object=FileHandle_lookup_open_enter(captivefs_vfs,name,FALSE);
256 if (!captive_file_object)
260 errvfsresult=captive_file_file_info_get(captive_file_object,&file_info);
261 G_UNLOCK(libcaptive);
263 FileHandle_leave(captive_file_object,name);
265 if (errvfsresult!=GNOME_VFS_OK)
268 if (!captivefs_GnomeVFSFileInfo_to_lufs_fattr(fattr,&file_info))
277 int captivefs_create(struct captivefs_vfs *captivefs_vfs,const char *file,int mode)
279 CaptiveFileObject *captive_file_object;
281 g_return_val_if_fail(captivefs_vfs_validate(captivefs_vfs),-1);
282 g_return_val_if_fail(file!=NULL,-1);
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);
287 captive_file_object=FileHandle_lookup_open_enter(captivefs_vfs,file,TRUE);
288 if (!captive_file_object)
291 FileHandle_leave(captive_file_object,file);
299 int captivefs_unlink(struct captivefs_vfs *captivefs_vfs,const char *file)
301 GnomeVFSResult errvfsresult;
302 CaptiveFileObject *captive_file_object;
304 g_return_val_if_fail(captivefs_vfs_validate(captivefs_vfs),-1);
305 g_return_val_if_fail(file!=NULL,-1);
307 if (captivefs_vfs->options.debug_messages)
308 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"captivefs_unlink: file=%s",file);
310 captive_file_object=FileHandle_lookup_open_enter(captivefs_vfs,file,FALSE);
311 if (!captive_file_object)
315 errvfsresult=captive_file_remove(captive_file_object);
316 G_UNLOCK(libcaptive);
318 FileHandle_leave(captive_file_object,file);
320 if (errvfsresult!=GNOME_VFS_OK)
329 int captivefs_rename(struct captivefs_vfs *captivefs_vfs,const char *old_name,const char *new_name)
331 GnomeVFSResult errvfsresult;
332 CaptiveFileObject *captive_file_object;
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);
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);
342 captive_file_object=FileHandle_lookup_open_enter(captivefs_vfs,old_name,FALSE);
343 if (!captive_file_object)
346 G_LOCK(FileHandle_hash);
347 use_count=FileHandle_get_use_count(captive_file_object);
348 g_assert(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);
359 errvfsresult=captive_file_move(captive_file_object,new_name,
360 FALSE); /* force_replace */
361 G_UNLOCK(libcaptive);
363 FileHandle_leave_locked(captive_file_object,old_name);
364 G_UNLOCK(FileHandle_hash);
366 if (errvfsresult!=GNOME_VFS_OK)
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
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.
385 int captivefs_open(struct captivefs_vfs *captivefs_vfs,const char *file,unsigned mode)
387 CaptiveFileObject *captive_file_object;
388 GnomeVFSOpenMode gnome_vfs_open_mode;
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);
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);
397 if (!captivefs_vfs_validate(captivefs_vfs))
400 if (mode & ~(O_RDONLY|O_WRONLY|O_RDWR)) {
401 g_warning("captivefs_open: Unrecognized 'mode' 0x%X",mode);
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;
409 g_warning("captivefs_open: Unrecognized 'mode' 0x%X",mode);
413 captive_file_object=FileHandle_lookup_open_enter(captivefs_vfs,file,FALSE);
414 if (!captive_file_object)
417 /* Leave 'captive_file_object' entered. */
422 int captivefs_release(struct captivefs_vfs *captivefs_vfs,const char *file)
424 CaptiveFileObject *captive_file_object;
426 g_return_val_if_fail(captivefs_vfs_validate(captivefs_vfs),-1);
427 g_return_val_if_fail(file!=NULL,-1);
429 if (captivefs_vfs->options.debug_messages)
430 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"captivefs_release: file=%s",file);
432 if (!(captive_file_object=FileHandle_lookup_enter(file)))
435 FileHandle_leave(captive_file_object,file); /* for FileHandle_lookup_enter() */
436 FileHandle_leave(captive_file_object,file);
442 /* Read from a file. Changed to use the (2) routines not for efficiency,
443 * but to make it work with 64-bit offsets :-(.
445 int captivefs_read(struct captivefs_vfs *captivefs_vfs,const char *file,long long offset,unsigned long count,void *buf)
447 CaptiveFileObject *captive_file_object;
448 GnomeVFSFileSize bytes_read;
449 GnomeVFSResult errvfsresult;
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);
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);
459 if (!(captive_file_object=FileHandle_lookup_enter(file)))
462 /* Do not unlock 'libcaptive' between seek() and read()! */
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);
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)
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);
489 int captivefs_write(struct captivefs_vfs *captivefs_vfs,const char *file,long long offset,unsigned long count,const void *buf)
491 CaptiveFileObject *captive_file_object;
492 GnomeVFSFileSize bytes_written;
493 GnomeVFSResult errvfsresult;
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);
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);
503 if (!(captive_file_object=FileHandle_lookup_enter(file)))
506 /* Do not unlock 'libcaptive' between seek() and write()! */
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);
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)
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;
527 return bytes_written;
531 /* Change a file/dir's attributes
533 int captivefs_setattr(struct captivefs_vfs *captivefs_vfs,const char *file,const struct lufs_fattr *fattr)
535 GnomeVFSFileInfo file_info;
536 GnomeVFSResult errvfsresult;
537 CaptiveFileObject *captive_file_object;
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);
543 if (captivefs_vfs->options.debug_messages)
544 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"captivefs_setattr: file=%s",file);
546 if (!captivefs_lufs_fattr_to_GnomeVFSFileInfo(&file_info,fattr))
549 captive_file_object=FileHandle_lookup_open_enter(captivefs_vfs,file,FALSE);
550 if (!captive_file_object)
554 errvfsresult=captive_file_file_info_set(captive_file_object,&file_info,
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)