+/* 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;
+}
+