#include <sys/mman.h>
#include <unistd.h>
#include "main.h"
+#include <signal.h>
+#include <setjmp.h>
+#include <sys/time.h>
+#include "cabinet-memory.h"
#include <captive/macros.h>
+/* Config: */
+#define ACQUIRE_CABINET_READ_RAW_READ_TRY_MAX 5
+#define ACQUIRE_CABINET_READ_RAW_READ_TIMEOUT 20
+#define ACQUIRE_CABINET_READ_RAW_READ_ITER_SEC 0
+#define ACQUIRE_CABINET_READ_RAW_READ_ITER_USEC 100000
+
+
+#define ACQUIRE_CABINET_READ_RAW_READ_TIMEOUT_ITERS \
+ ((ACQUIRE_CABINET_READ_RAW_READ_TIMEOUT *1000000LL) \
+ /(ACQUIRE_CABINET_READ_RAW_READ_ITER_SEC*1000000LL+ACQUIRE_CABINET_READ_RAW_READ_ITER_USEC))
+
+
void acquire_cabinet_seek(struct acquire_cabinet *acquire_cabinet,GnomeVFSFileOffset offset)
{
g_return_if_fail(acquire_cabinet!=NULL);
return acquire_cabinet->offset;
}
+static guint handler_SIGALRM_hits;
+static sigjmp_buf handler_SIGALRM_sigjmp_buf;
+
+static void handler_SIGALRM(int signo)
+{
+ g_return_if_fail(signo==SIGALRM);
+
+ /* Try to abort the read(2) call first.
+ * If it already read something it will return the partially read data.
+ * Otherwise gnome_vfs_inet_connection_read() will loop back to retry read(2)
+ * and we will abort it after 1 second. OK, some data may be read that time
+ * but who cares.
+ */
+ if (handler_SIGALRM_hits++<ACQUIRE_CABINET_READ_RAW_READ_TIMEOUT_ITERS
+ && !(*ui_progress)(NULL))
+ return;
+
+ siglongjmp(handler_SIGALRM_sigjmp_buf,1); /* 1; meaning: !=0 */
+}
+
+/* FIXME: This is hack.
+ * Correct way would be to use 'GnomeVFSCancellation'
+ * to abort 'GnomeVFSInetConnection' acting as 'GnomeVFSSocket'.
+ * This abort should be handled from 'http' handler
+ * but gnome_vfs_cancellation_cancel() cannot be invoked from
+ * the asynchronous slave thread.
+ */
+static GnomeVFSResult acquire_cabinet_read_raw
+ (struct acquire_cabinet *acquire_cabinet,gpointer buffer,GnomeVFSFileSize bytes,GnomeVFSFileSize *bytes_read,
+ GnomeVFSFileOffset offset)
+{
+gint try=0;
+
+ g_return_val_if_fail(acquire_cabinet!=NULL,GNOME_VFS_ERROR_BAD_PARAMETERS);
+ g_return_val_if_fail(buffer!=NULL || bytes==0,GNOME_VFS_ERROR_BAD_PARAMETERS);
+ g_return_val_if_fail(bytes_read!=NULL,GNOME_VFS_ERROR_BAD_PARAMETERS);
+
+ *bytes_read=0;
+
+ if (!bytes)
+ return GNOME_VFS_ERROR_EOF;
+
+ while (try++<=ACQUIRE_CABINET_READ_RAW_READ_TRY_MAX) {
+GnomeVFSResult errvfsresult;
+GnomeVFSHandle *handle_new;
+struct sigaction oldact;
+int errint;
+struct itimerval itimerval;
+GnomeVFSFileSize offset_current;
+
+ if ((*ui_progress)(NULL))
+ return GNOME_VFS_ERROR_INTERRUPTED;
+
+ if (!sigsetjmp(
+ handler_SIGALRM_sigjmp_buf, /* env */
+ TRUE)) { /* savesigs */
+ handler_SIGALRM_hits=0;
+ errint=sigaction(
+ SIGALRM, /* signum */
+ NULL, /* act */
+ &oldact); /* oldact */
+ g_assert(errint==0);
+ signal(SIGALRM,handler_SIGALRM);
+ itimerval.it_interval.tv_sec=ACQUIRE_CABINET_READ_RAW_READ_ITER_SEC;
+ itimerval.it_interval.tv_usec=ACQUIRE_CABINET_READ_RAW_READ_ITER_USEC;
+ itimerval.it_value=itimerval.it_interval;
+ errint=setitimer(
+ ITIMER_REAL, /* which */
+ &itimerval, /* value */
+ NULL); /* ovalue */
+ g_assert(errint==0);
+ /* Optimization avoid resetting connection
+ * in neon "http" handler of: FC4 gnome-vfs2-2.10.0-5
+ * http://bugzilla.gnome.org/show_bug.cgi?id=324984
+ */
+ errvfsresult=gnome_vfs_tell(*acquire_cabinet->handlep,&offset_current);
+ if (GNOME_VFS_OK==errvfsresult && (GnomeVFSFileOffset)offset_current!=offset)
+ errvfsresult=gnome_vfs_seek(*acquire_cabinet->handlep,GNOME_VFS_SEEK_START,offset);
+ if (GNOME_VFS_OK==errvfsresult)
+ errvfsresult=gnome_vfs_read(*acquire_cabinet->handlep,buffer,bytes,bytes_read);
+ }
+ else
+ errvfsresult=GNOME_VFS_ERROR_INTERRUPTED;
+ itimerval.it_interval.tv_sec=0;
+ itimerval.it_interval.tv_usec=0;
+ itimerval.it_value=itimerval.it_interval;
+ errint=setitimer(
+ ITIMER_REAL, /* which */
+ &itimerval, /* value */
+ NULL); /* ovalue */
+ g_assert(errint==0);
+ errint=sigaction(
+ SIGALRM, /* signum */
+ &oldact, /* act */
+ NULL); /* oldact */
+ g_assert(errint==0);
+ if (errvfsresult==GNOME_VFS_OK) {
+ g_assert(*bytes_read>0);
+ return GNOME_VFS_OK;
+ }
+
+ /* Reopen '*acquire_cabinet->handlep' */
+ g_assert(acquire_cabinet->handle_uri!=NULL);
+ if (GNOME_VFS_OK==(errvfsresult=gnome_vfs_open_uri(&handle_new,acquire_cabinet->handle_uri,
+ GNOME_VFS_OPEN_READ|GNOME_VFS_OPEN_RANDOM))) {
+ gnome_vfs_close(*acquire_cabinet->handlep); /* errors ignored */
+ *acquire_cabinet->handlep=handle_new;
+ }
+ }
+
+ return GNOME_VFS_ERROR_IO;
+}
+
#define ACQUIRE_CABINET_BYTE_CACHED(acquire_cabinet,pos) (!(acquire_cabinet)->base_cached \
|| (acquire_cabinet)->base_cached[(pos)/8] & 1<<((pos)&7))
#define ACQUIRE_CABINET_SET_BYTE_CACHED(acquire_cabinet,pos) ((acquire_cabinet)->base_cached[(pos)/8] |= 1<<((pos)&7))
g_return_val_if_fail(acquire_cabinet!=NULL,GNOME_VFS_ERROR_BAD_PARAMETERS);
g_return_val_if_fail(buffer!=NULL || bytes==0,GNOME_VFS_ERROR_BAD_PARAMETERS);
+ *bytes_read=0;
+
if ((*ui_progress)(NULL))
return GNOME_VFS_ERROR_INTERRUPTED;
if (!bytes)
return GNOME_VFS_ERROR_EOF;
- *bytes_read=0;
while (bytes) {
read_behind =acquire_cabinet->offset+bytes;
while (offset_end<read_behind && !ACQUIRE_CABINET_BYTE_CACHED(acquire_cabinet,offset_end))
offset_end++;
if (offset_end>offset_start) {
- if (GNOME_VFS_OK!=(errvfsresult=gnome_vfs_seek(acquire_cabinet->handle,GNOME_VFS_SEEK_START,offset_start)))
- return errvfsresult;
- errvfsresult=gnome_vfs_read(acquire_cabinet->handle,
- acquire_cabinet->base+offset_start,offset_end-offset_start,&bytes_read_now);
+ errvfsresult=acquire_cabinet_read_raw(acquire_cabinet,
+ acquire_cabinet->base+offset_start,offset_end-offset_start,&bytes_read_now,offset_start);
if (errvfsresult!=GNOME_VFS_OK)
return errvfsresult;
g_assert(bytes_read_now>0);
uri_cabextract->parent=gnome_vfs_uri_dup(uri);
acquire_cabinet->uri=uri_cabextract;
+ acquire_cabinet->handle_uri=gnome_vfs_uri_ref(uri);
acquire_cabinet->filename=gnome_vfs_uri_to_string(acquire_cabinet->uri,GNOME_VFS_URI_HIDE_PASSWORD);
}
r->base=(/* de-const */ gpointer)file_base;
r->base_cached=NULL;
r->offset=0;
- r->handle=NULL;
+ r->handlep=NULL;
r->size=file_length;
acquire_cabinet_set_uri(r,uri);
r->cabinet_done=0;
r->cabinet_used=cabinet_used;
+ r->memory=acquire_cabinet_memory_object_new();
return r;
}
struct acquire_cabinet *acquire_cabinet_new_from_handle
- (GnomeVFSHandle *handle,GnomeVFSFileInfo *file_info,GnomeVFSURI *uri,gint cabinet_used)
+ (GnomeVFSHandle **handlep,GnomeVFSFileInfo *file_info,GnomeVFSURI *uri,gint cabinet_used)
{
struct acquire_cabinet *r;
- g_return_val_if_fail(handle!=NULL,NULL);
+ g_return_val_if_fail(handlep!=NULL,NULL);
+ g_return_val_if_fail(*handlep!=NULL,NULL);
g_return_val_if_fail(file_info!=NULL,NULL);
g_return_val_if_fail(uri!=NULL,NULL);
}
captive_new0n(r->base_cached,CAPTIVE_ROUND_UP64(file_info->size,8)/8);
r->offset=0;
- r->handle=handle;
+ r->handlep=handlep;
r->size=file_info->size;
- acquire_cabinet_set_uri(r,uri);
r->cabinet_done=0;
r->cabinet_used=cabinet_used;
+ acquire_cabinet_set_uri(r,uri);
+ r->memory=acquire_cabinet_memory_object_new();
+
return r;
}
}
g_free((/* de-const */ gchar *)acquire_cabinet->filename);
gnome_vfs_uri_unref(acquire_cabinet->uri);
+ gnome_vfs_uri_unref(acquire_cabinet->handle_uri);
+ g_object_unref(acquire_cabinet->memory);
g_free(acquire_cabinet);
}
if ((*ui_progress)(acquire_cabinet->uri))
return;
+ acquire_cabinet_memory_object_push(acquire_cabinet->memory);
+
basecab=find_cabs_in_file(acquire_cabinet);
if (!basecab)
- return;
+ goto fail_memory_pop;
if (basecab->next)
- return;
+ goto fail_memory_pop;
if (basecab->prevcab || basecab->nextcab)
- return;
+ goto fail_memory_pop;
filelist=process_files(basecab);
for (fi=filelist;fi;fi=fi->next) {
gpointer file_buffer;
GnomeVFSURI *uri_fi;
+int errint;
if (!captivemodid_module_length_is_valid(fi->length))
continue;
uri_fi=gnome_vfs_uri_append_file_name(acquire_cabinet->uri,fi->filename);
if ((*ui_progress)(uri_fi)) {
gnome_vfs_uri_unref(uri_fi);
- return;
+ goto fail_memory_pop;
}
file_write_fi_assertion=fi;
file_write_bytearray=g_byte_array_new();
- extract_file(fi,
+ /* extract_file() returns 1 for success. */
+ errint=extract_file(fi,
0, /* lower; ignored now */
FALSE, /* fix */
NULL); /* dir; ignored now */
- if (fi->length!=file_write_bytearray->len) {
+ if (!errint || fi->length!=file_write_bytearray->len) {
g_byte_array_free(file_write_bytearray,
TRUE); /* free_segment */
gnome_vfs_uri_unref(uri_fi);
+ if (!errint)
+ goto fail_memory_pop;
continue;
}
file_buffer=g_byte_array_free(file_write_bytearray,
gnome_vfs_uri_unref(uri_fi);
g_free(file_buffer);
}
+
+ /* FALLTHRU */
+
+fail_memory_pop:
+ acquire_cabinet_memory_object_pop(acquire_cabinet->memory);
}