X-Git-Url: http://git.jankratochvil.net/?p=captive.git;a=blobdiff_plain;f=src%2Fclient%2Flufs%2Fcaptivefs-file.c;h=0dc7b4d9e5adc13eec2f82104f77d8f9eec04bd3;hp=d570ae81b9cbdc6bd31aacd01d928ee6525a221c;hb=e6af6c57e7a91f5e9aaea5722125032f28bf9012;hpb=555bcab591d9c9f17f06969325e329cd2cfd9889 diff --git a/src/client/lufs/captivefs-file.c b/src/client/lufs/captivefs-file.c index d570ae8..0dc7b4d 100644 --- a/src/client/lufs/captivefs-file.c +++ b/src/client/lufs/captivefs-file.c @@ -27,10 +27,202 @@ #include #include +#include #include +/* 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: @@ -60,33 +252,23 @@ GnomeVFSFileInfo file_info; 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; } @@ -94,7 +276,6 @@ fail: */ 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); @@ -103,17 +284,11 @@ CaptiveFileObject *captive_file_object; 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; } @@ -132,30 +307,20 @@ CaptiveFileObject *captive_file_object; 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; } @@ -165,6 +330,7 @@ int captivefs_rename(struct captivefs_vfs *captivefs_vfs,const char *old_name,co { 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); @@ -173,80 +339,34 @@ CaptiveFileObject *captive_file_object; 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; } @@ -266,7 +386,6 @@ int captivefs_open(struct captivefs_vfs *captivefs_vfs,const char *file,unsigned { 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); @@ -278,16 +397,6 @@ GnomeVFSResult errvfsresult; 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; @@ -301,22 +410,18 @@ GnomeVFSResult errvfsresult; 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); @@ -324,15 +429,11 @@ gboolean errbool; 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; } @@ -355,7 +456,7 @@ GnomeVFSResult errvfsresult; 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()! */ @@ -363,11 +464,13 @@ GnomeVFSResult errvfsresult; 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; @@ -397,7 +500,7 @@ GnomeVFSResult errvfsresult; 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()! */ @@ -405,11 +508,13 @@ GnomeVFSResult errvfsresult; 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; @@ -441,11 +546,9 @@ CaptiveFileObject *captive_file_object; 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, @@ -459,19 +562,9 @@ CaptiveFileObject *captive_file_object; | 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; }