#include <captive/client-vfs.h>
#include <captive/client-file.h>
+#include <captive/client.h>
#include <lufs/fs.h>
+/* Config: */
+#define CLIENT_LUFS_USE_COUNT "client-lufs-use_count"
+
+
+/* map: (const gchar *) -> (CaptiveFileObject *) */
+static GHashTable *FileHandle_hash;
+G_LOCK_DEFINE_STATIC(FileHandle_hash);
+
+static void FileHandle_hash_init(void)
+{
+ G_LOCK(FileHandle_hash);
+ if (!FileHandle_hash) {
+ FileHandle_hash=g_hash_table_new_full(g_str_hash,g_str_equal,
+ (GDestroyNotify)g_free, /* key_destroy_func */
+ (GDestroyNotify)NULL); /* value_destroy_func; handled by FileHandle_hash_captive_file_object_weak_notify() */
+ }
+ G_UNLOCK(FileHandle_hash);
+}
+
+/* Do not: GObject->ref_count
+ * as 'ref_count' may be increased for deleted files by ParentConnector
+ * until the volume gets unmounted. Therefore any deleted file would not
+ * be removed from our 'FileHandle_hash' and no object would be reinstantiable
+ * under the original deleted name.
+ */
+static gint FileHandle_get_use_count(CaptiveFileObject *captive_file_object)
+{
+ g_return_val_if_fail(CAPTIVE_FILE_IS_OBJECT(captive_file_object),0);
+
+ return GPOINTER_TO_INT(g_object_get_data(G_OBJECT(captive_file_object),CLIENT_LUFS_USE_COUNT));
+}
+
+static void FileHandle_set_use_count(CaptiveFileObject *captive_file_object,gint use_count)
+{
+ g_return_if_fail(CAPTIVE_FILE_IS_OBJECT(captive_file_object));
+
+ g_object_set_data(G_OBJECT(captive_file_object),CLIENT_LUFS_USE_COUNT,GINT_TO_POINTER(use_count));
+}
+
+/* FileHandle_hash_init(); must be executed! */
+/* G_LOCK(FileHandle_hash); must be held! */
+static void FileHandle_enter(CaptiveFileObject *captive_file_object,const gchar *name_normalized)
+{
+gint use_count;
+
+ g_return_if_fail(CAPTIVE_FILE_IS_OBJECT(captive_file_object));
+ g_return_if_fail(name_normalized);
+
+ use_count=FileHandle_get_use_count(captive_file_object);
+ g_assert(use_count>=0);
+ use_count++;
+ FileHandle_set_use_count(captive_file_object,use_count);
+ if (use_count>1) {
+ g_assert(captive_file_object==g_hash_table_lookup(FileHandle_hash,name_normalized));
+ return;
+ }
+ g_assert(NULL==g_hash_table_lookup(FileHandle_hash,name_normalized));
+ g_object_ref(captive_file_object);
+ g_hash_table_insert(FileHandle_hash,g_strdup(name_normalized),captive_file_object);
+}
+
+/* FileHandle_hash_init(); must be executed! */
+/* G_LOCK(FileHandle_hash); must be held! */
+/* G_LOCK(libcaptive); must NOT be held! */
+static void FileHandle_leave_locked(CaptiveFileObject *captive_file_object,const gchar *name)
+{
+gboolean errbool;
+gint use_count;
+gchar *name_normalized;
+
+ g_return_if_fail(CAPTIVE_FILE_IS_OBJECT(captive_file_object));
+ g_return_if_fail(name);
+
+ name_normalized=captive_path_normalize(name);
+ use_count=FileHandle_get_use_count(captive_file_object);
+ g_assert(use_count>=1);
+ use_count--;
+ FileHandle_set_use_count(captive_file_object,use_count);
+ g_assert(captive_file_object==g_hash_table_lookup(FileHandle_hash,name_normalized));
+ if (use_count>=1) {
+ g_free(name_normalized);
+ return;
+ }
+ errbool=g_hash_table_remove(FileHandle_hash,name_normalized);
+ g_assert(errbool==TRUE);
+ G_LOCK(libcaptive);
+ g_object_unref(captive_file_object);
+ G_UNLOCK(libcaptive);
+ g_assert(NULL==g_hash_table_lookup(FileHandle_hash,name_normalized));
+ g_free(name_normalized);
+}
+
+/* FileHandle_hash_init(); must be executed! */
+/* G_LOCK(FileHandle_hash); must NOT be held! */
+/* G_LOCK(libcaptive); must NOT be held! */
+static void FileHandle_leave(CaptiveFileObject *captive_file_object,const gchar *name)
+{
+ g_return_if_fail(CAPTIVE_FILE_IS_OBJECT(captive_file_object));
+ g_return_if_fail(name);
+
+ G_LOCK(FileHandle_hash);
+ FileHandle_leave_locked(captive_file_object,name);
+ G_UNLOCK(FileHandle_hash);
+}
+
+/* 'name' is required to exist. */
+static CaptiveFileObject *FileHandle_lookup_enter(const char *name)
+{
+CaptiveFileObject *captive_file_object;
+gchar *name_normalized;
+
+ g_return_val_if_fail(name!=NULL,NULL);
+
+ name_normalized=captive_path_normalize(name);
+ FileHandle_hash_init();
+ G_LOCK(FileHandle_hash);
+ captive_file_object=g_hash_table_lookup(FileHandle_hash,name_normalized);
+ if (!captive_file_object) {
+ G_UNLOCK(FileHandle_hash);
+ g_warning("FileHandle_lookup_enter: FileHandle not found of: %s",name_normalized);
+ g_free(name_normalized);
+ return NULL;
+ }
+ g_assert(CAPTIVE_FILE_IS_OBJECT(captive_file_object));
+ FileHandle_enter(captive_file_object,name_normalized);
+ G_UNLOCK(FileHandle_hash);
+ g_free(name_normalized);
+
+ return captive_file_object;
+}
+
+/* 'name' will be opened if needed. */
+static CaptiveFileObject *FileHandle_lookup_open_enter(struct captivefs_vfs *captivefs_vfs,const char *name,gboolean create)
+{
+CaptiveFileObject *captive_file_object;
+GnomeVFSResult errvfsresult;
+gchar *name_normalized;
+
+ g_return_val_if_fail(name!=NULL,NULL);
+
+ name_normalized=captive_path_normalize(name);
+ FileHandle_hash_init();
+ G_LOCK(FileHandle_hash);
+ captive_file_object=g_hash_table_lookup(FileHandle_hash,name_normalized);
+ if (!create && captive_file_object) {
+ g_assert(CAPTIVE_FILE_IS_OBJECT(captive_file_object));
+ FileHandle_enter(captive_file_object,name_normalized);
+ G_UNLOCK(FileHandle_hash);
+ g_free(name_normalized);
+ return captive_file_object;
+ }
+ if ( create && captive_file_object) {
+ g_assert(CAPTIVE_FILE_IS_OBJECT(captive_file_object));
+ FileHandle_enter(captive_file_object,name_normalized);
+ G_UNLOCK(FileHandle_hash);
+ g_free(name_normalized);
+ return NULL;
+ }
+
+ /* FIXME: Respect 'gnome_vfs_open_mode'.
+ * We would have to do some reopens of files already contained in 'FileHandle_hash'.
+ * As W32 filesystem will allow us to open file 'GNOME_VFS_OPEN_WRITE'
+ * even on read-only media we use the full open permissions always.
+ */
+ if (!create) {
+ G_LOCK(libcaptive);
+ errvfsresult=captive_file_new_open(&captive_file_object,captivefs_vfs->captive_vfs_object,name_normalized,
+ GNOME_VFS_OPEN_READ|GNOME_VFS_OPEN_WRITE|GNOME_VFS_OPEN_RANDOM);
+ G_UNLOCK(libcaptive);
+ }
+ else {
+ G_LOCK(libcaptive);
+ errvfsresult=captive_file_new_create(&captive_file_object,captivefs_vfs->captive_vfs_object,name_normalized,
+ GNOME_VFS_OPEN_READ|GNOME_VFS_OPEN_WRITE|GNOME_VFS_OPEN_RANDOM, /* mode */
+ FALSE, /* exclusive */
+ 0600); /* perm */
+ G_UNLOCK(libcaptive);
+ }
+ if (errvfsresult!=GNOME_VFS_OK) {
+ G_UNLOCK(FileHandle_hash);
+ g_free(name_normalized);
+ return NULL;
+ }
+ FileHandle_enter(captive_file_object,name_normalized);
+ G_UNLOCK(FileHandle_hash);
+ g_object_unref(captive_file_object); /* for captive_file_new_open() / captive_file_new_create() */
+ g_free(name_normalized);
+
+ return captive_file_object;
+}
+
/* Read a file/dir's attributes
* Fill all relevant data into the fattr structure.
* The uid/gid fields are just ownership hints hints:
if (captivefs_vfs->options.debug_messages)
g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"captivefs_stat: name=%s",name);
- G_LOCK(libcaptive);
- errvfsresult=captive_file_new_open(&captive_file_object,captivefs_vfs->captive_vfs_object,name,GNOME_VFS_OPEN_READ);
- G_UNLOCK(libcaptive);
- if (errvfsresult!=GNOME_VFS_OK)
- goto fail;
+ captive_file_object=FileHandle_lookup_open_enter(captivefs_vfs,name,FALSE);
+ if (!captive_file_object)
+ return -1;
G_LOCK(libcaptive);
errvfsresult=captive_file_file_info_get(captive_file_object,&file_info);
G_UNLOCK(libcaptive);
- if (errvfsresult!=GNOME_VFS_OK)
- goto fail_unref;
- G_LOCK(libcaptive);
- g_object_unref(captive_file_object);
- G_UNLOCK(libcaptive);
+ FileHandle_leave(captive_file_object,name);
+
+ if (errvfsresult!=GNOME_VFS_OK)
+ return -1;
if (!captivefs_GnomeVFSFileInfo_to_lufs_fattr(fattr,&file_info))
return -1;
return 0;
-
-fail_unref:
- G_LOCK(libcaptive);
- g_object_unref(captive_file_object);
- G_UNLOCK(libcaptive);
-fail:
- return -1;
}
*/
int captivefs_create(struct captivefs_vfs *captivefs_vfs,const char *file,int mode)
{
-GnomeVFSResult errvfsresult;
CaptiveFileObject *captive_file_object;
g_return_val_if_fail(captivefs_vfs_validate(captivefs_vfs),-1);
if (captivefs_vfs->options.debug_messages)
g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"captivefs_create: file=%s,mode=0x%X",file,mode);
- G_LOCK(libcaptive);
- errvfsresult=captive_file_new_create(&captive_file_object,captivefs_vfs->captive_vfs_object,file,GNOME_VFS_OPEN_WRITE,
- FALSE, /* exclusive */
- mode); /* perm */
- G_UNLOCK(libcaptive);
- if (errvfsresult!=GNOME_VFS_OK)
+ captive_file_object=FileHandle_lookup_open_enter(captivefs_vfs,file,TRUE);
+ if (!captive_file_object)
return -1;
- G_LOCK(libcaptive);
- g_object_unref(captive_file_object);
- G_UNLOCK(libcaptive);
+ FileHandle_leave(captive_file_object,file);
return 0;
}
if (captivefs_vfs->options.debug_messages)
g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"captivefs_unlink: file=%s",file);
- G_LOCK(libcaptive);
- errvfsresult=captive_file_new_open(&captive_file_object,captivefs_vfs->captive_vfs_object,file,GNOME_VFS_OPEN_WRITE);
- G_UNLOCK(libcaptive);
- if (errvfsresult!=GNOME_VFS_OK)
- goto fail;
+ captive_file_object=FileHandle_lookup_open_enter(captivefs_vfs,file,FALSE);
+ if (!captive_file_object)
+ return -1;
G_LOCK(libcaptive);
errvfsresult=captive_file_remove(captive_file_object);
G_UNLOCK(libcaptive);
- if (errvfsresult!=GNOME_VFS_OK)
- goto fail_unref;
- G_LOCK(libcaptive);
- g_object_unref(captive_file_object);
- G_UNLOCK(libcaptive);
+ FileHandle_leave(captive_file_object,file);
- return 0;
+ if (errvfsresult!=GNOME_VFS_OK)
+ return -1;
-fail_unref:
- G_LOCK(libcaptive);
- g_object_unref(captive_file_object);
- G_UNLOCK(libcaptive);
-fail:
- return -1;
+ return 0;
}
{
GnomeVFSResult errvfsresult;
CaptiveFileObject *captive_file_object;
+gint use_count;
g_return_val_if_fail(captivefs_vfs_validate(captivefs_vfs),-1);
g_return_val_if_fail(old_name!=NULL,-1);
if (captivefs_vfs->options.debug_messages)
g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"captivefs_rename: old_name=%s,new_name=%s",old_name,new_name);
- G_LOCK(libcaptive);
- errvfsresult=captive_file_new_open(&captive_file_object,captivefs_vfs->captive_vfs_object,old_name,GNOME_VFS_OPEN_WRITE);
- G_UNLOCK(libcaptive);
- if (errvfsresult!=GNOME_VFS_OK)
- goto fail;
+ captive_file_object=FileHandle_lookup_open_enter(captivefs_vfs,old_name,FALSE);
+ if (!captive_file_object)
+ return -1;
+
+ G_LOCK(FileHandle_hash);
+ use_count=FileHandle_get_use_count(captive_file_object);
+ g_assert(use_count>=1);
+ if (use_count!=1) {
+ if (captivefs_vfs->options.debug_messages)
+ g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"captivefs_rename: old_name=%s,new_name=%s: BUSY use_count=%d",
+ old_name,new_name,(int)use_count);
+ G_UNLOCK(FileHandle_hash);
+ FileHandle_leave(captive_file_object,old_name);
+ return -1;
+ }
G_LOCK(libcaptive);
errvfsresult=captive_file_move(captive_file_object,new_name,
FALSE); /* force_replace */
G_UNLOCK(libcaptive);
- if (errvfsresult!=GNOME_VFS_OK)
- goto fail_unref;
-
- G_LOCK(libcaptive);
- g_object_unref(captive_file_object);
- G_UNLOCK(libcaptive);
-
- return 0;
-
-fail_unref:
- G_LOCK(libcaptive);
- g_object_unref(captive_file_object);
- G_UNLOCK(libcaptive);
-fail:
- return -1;
-}
-
-
-/* map: (const gchar *) -> (CaptiveFileObject *) */
-static GHashTable *FileHandle_hash;
-G_LOCK_DEFINE_STATIC(FileHandle_hash);
-
-static void FileHandle_hash_value_destroy_func(CaptiveFileObject *captive_file_object)
-{
- g_return_if_fail(CAPTIVE_FILE_IS_OBJECT(captive_file_object));
-
- G_LOCK(libcaptive);
- g_object_unref(captive_file_object);
- G_UNLOCK(libcaptive);
-}
-
-static void FileHandle_hash_init(void)
-{
- G_LOCK(FileHandle_hash);
- if (!FileHandle_hash) {
- FileHandle_hash=g_hash_table_new_full(g_str_hash,g_str_equal,
- (GDestroyNotify)g_free, /* key_destroy_func */
- (GDestroyNotify)FileHandle_hash_value_destroy_func); /* value_destroy_func */
- }
- G_UNLOCK(FileHandle_hash);
-}
-
-/* FIXME: g_object_ref() the resulting 'CaptiveFileObject'.
- * Currently we assume open/ops/release is done in single thread.
- */
-static CaptiveFileObject *FileHandle_lookup(const char *name)
-{
-CaptiveFileObject *captive_file_object;
-
- g_return_val_if_fail(name!=NULL,NULL);
- FileHandle_hash_init();
- G_LOCK(FileHandle_hash);
- captive_file_object=g_hash_table_lookup(FileHandle_hash,name);
+ FileHandle_leave_locked(captive_file_object,old_name);
G_UNLOCK(FileHandle_hash);
- if (!captive_file_object) {
- g_warning("FileHandle_lookup: FileHandle not found of: %s",name);
- return NULL;
- }
-
- g_assert(CAPTIVE_FILE_IS_OBJECT(captive_file_object));
+ if (errvfsresult!=GNOME_VFS_OK)
+ return -1;
- return captive_file_object;
+ return 0;
}
{
CaptiveFileObject *captive_file_object;
GnomeVFSOpenMode gnome_vfs_open_mode;
-GnomeVFSResult errvfsresult;
/* We may be called from the parent. */
g_return_val_if_fail(captivefs_vfs!=NULL,FALSE);
if (!captivefs_vfs_validate(captivefs_vfs))
return -1;
- FileHandle_hash_init();
- G_LOCK(FileHandle_hash);
- captive_file_object=g_hash_table_lookup(FileHandle_hash,file);
- G_UNLOCK(FileHandle_hash);
- if (captive_file_object) {
- g_assert(CAPTIVE_FILE_IS_OBJECT(captive_file_object));
- g_warning("captivefs_open: FileHandle already exists of: %s",file);
- return -1;
- }
-
if (mode & ~(O_RDONLY|O_WRONLY|O_RDWR)) {
g_warning("captivefs_open: Unrecognized 'mode' 0x%X",mode);
return -1;
return -1;
}
- G_LOCK(libcaptive);
- errvfsresult=captive_file_new_open(&captive_file_object,captivefs_vfs->captive_vfs_object,file,
- gnome_vfs_open_mode|GNOME_VFS_OPEN_RANDOM);
- G_UNLOCK(libcaptive);
- if (errvfsresult!=GNOME_VFS_OK)
+ captive_file_object=FileHandle_lookup_open_enter(captivefs_vfs,file,FALSE);
+ if (!captive_file_object)
return -1;
- g_hash_table_insert(FileHandle_hash,g_strdup(file),captive_file_object);
-
+ /* Leave 'captive_file_object' entered. */
return 0;
}
int captivefs_release(struct captivefs_vfs *captivefs_vfs,const char *file)
{
-gboolean errbool;
+CaptiveFileObject *captive_file_object;
g_return_val_if_fail(captivefs_vfs_validate(captivefs_vfs),-1);
g_return_val_if_fail(file!=NULL,-1);
if (captivefs_vfs->options.debug_messages)
g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"captivefs_release: file=%s",file);
- FileHandle_hash_init();
- G_LOCK(FileHandle_hash);
- errbool=g_hash_table_remove(FileHandle_hash,file);
- G_UNLOCK(FileHandle_hash);
-
- if (!errbool) {
- g_warning("captivefs_release: FileHandle not found of: %s",file);
+ if (!(captive_file_object=FileHandle_lookup_enter(file)))
return -1;
- }
+
+ FileHandle_leave(captive_file_object,file); /* for FileHandle_lookup_enter() */
+ FileHandle_leave(captive_file_object,file);
return 0;
}
g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"captivefs_read: file=%s,offset=%" G_GINT64_FORMAT ",count=0x%lX",
file,(gint64)offset,count);
- if (!(captive_file_object=FileHandle_lookup(file)))
+ if (!(captive_file_object=FileHandle_lookup_enter(file)))
return -1;
/* Do not unlock 'libcaptive' between seek() and read()! */
errvfsresult=captive_file_seek(captive_file_object,GNOME_VFS_SEEK_START,offset);
if (errvfsresult!=GNOME_VFS_OK) {
G_UNLOCK(libcaptive);
+ FileHandle_leave(captive_file_object,file);
return -1;
}
errvfsresult=captive_file_read(captive_file_object,buf,count,&bytes_read);
G_UNLOCK(libcaptive);
+ FileHandle_leave(captive_file_object,file);
if (errvfsresult!=GNOME_VFS_OK)
return -1;
g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"captivefs_write: file=%s,offset=%" G_GINT64_FORMAT ",count=0x%lX",
file,(gint64)offset,count);
- if (!(captive_file_object=FileHandle_lookup(file)))
+ if (!(captive_file_object=FileHandle_lookup_enter(file)))
return -1;
/* Do not unlock 'libcaptive' between seek() and write()! */
errvfsresult=captive_file_seek(captive_file_object,GNOME_VFS_SEEK_START,offset);
if (errvfsresult!=GNOME_VFS_OK) {
G_UNLOCK(libcaptive);
+ FileHandle_leave(captive_file_object,file);
return -1;
}
errvfsresult=captive_file_write(captive_file_object,buf,count,&bytes_written);
G_UNLOCK(libcaptive);
+ FileHandle_leave(captive_file_object,file);
if (errvfsresult!=GNOME_VFS_OK)
return -1;
if (!captivefs_lufs_fattr_to_GnomeVFSFileInfo(&file_info,fattr))
return -1;
- G_LOCK(libcaptive);
- errvfsresult=captive_file_new_open(&captive_file_object,captivefs_vfs->captive_vfs_object,file,GNOME_VFS_OPEN_WRITE);
- G_UNLOCK(libcaptive);
- if (errvfsresult!=GNOME_VFS_OK)
- goto fail;
+ captive_file_object=FileHandle_lookup_open_enter(captivefs_vfs,file,FALSE);
+ if (!captive_file_object)
+ return -1;
G_LOCK(libcaptive);
errvfsresult=captive_file_file_info_set(captive_file_object,&file_info,
| GNOME_VFS_FILE_INFO_FIELDS_CTIME))
? 0 : GNOME_VFS_SET_FILE_INFO_TIME));
G_UNLOCK(libcaptive);
+ FileHandle_leave(captive_file_object,file);
if (errvfsresult!=GNOME_VFS_OK)
- goto fail_unref;
-
- G_LOCK(libcaptive);
- g_object_unref(captive_file_object);
- G_UNLOCK(libcaptive);
+ return -1;
return 0;
-
-fail_unref:
- G_LOCK(libcaptive);
- g_object_unref(captive_file_object);
- G_UNLOCK(libcaptive);
-fail:
- return -1;
}