Allow concurrent file access by multiple handles; use 'FileHandle_hash'.
authorshort <>
Wed, 12 Nov 2003 11:02:44 +0000 (11:02 +0000)
committershort <>
Wed, 12 Nov 2003 11:02:44 +0000 (11:02 +0000)
 - Fixes abort of scp(1) copying to destination Captive volume drive.

src/client/lufs/captivefs-file.c

index d570ae8..0dc7b4d 100644 (file)
 
 #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:
@@ -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;
 }