/* $Id$ * cabextract interface for acquiration installation utility * Copyright (C) 2003 Jan Kratochvil * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; exactly version 2 of June 1991 is required * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include "cabinet.h" /* self */ #include #include #include #include "cabextract/cabextract.h" #include "captivemodid.h" #include "moduriload.h" #include #include #include "main.h" #include #include #include #include /* 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); /* Do not: (*ui_progress)(acquire_cabinet->uri); * as we currently extract some specific file out of it. */ (*ui_progress)(NULL); acquire_cabinet->offset=offset; } void acquire_cabinet_seek_skip(struct acquire_cabinet *acquire_cabinet,GnomeVFSFileOffset offset) { g_return_if_fail(acquire_cabinet!=NULL); (*ui_progress)(NULL); acquire_cabinet->offset+=offset; } GnomeVFSFileOffset acquire_cabinet_tell(struct acquire_cabinet *acquire_cabinet) { g_return_val_if_fail(acquire_cabinet!=NULL,GNOME_VFS_ERROR_BAD_PARAMETERS); (*ui_progress)(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++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_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)) GnomeVFSResult acquire_cabinet_read (struct acquire_cabinet *acquire_cabinet,gpointer buffer,GnomeVFSFileSize bytes,GnomeVFSFileSize *bytes_read) { GnomeVFSFileOffset offset_start,offset_end,read_behind; GnomeVFSResult errvfsresult; GnomeVFSFileSize bytes_read_now; 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; bytes=MAX(0,MIN(bytes,acquire_cabinet->size-acquire_cabinet->offset)); if (!bytes) return GNOME_VFS_ERROR_EOF; while (bytes) { read_behind =acquire_cabinet->offset+bytes; /* GnomeVFS block transfer: */ offset_start=acquire_cabinet->offset; offset_end =acquire_cabinet->offset; while (offset_endoffset_start) { 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); acquire_cabinet->cabinet_done+=bytes_read_now; if (ui_progress_bar) (*ui_progress_bar)(acquire_cabinet->cabinet_done,acquire_cabinet->cabinet_used); while (bytes_read_now) { ACQUIRE_CABINET_SET_BYTE_CACHED(acquire_cabinet,offset_start); offset_start++; bytes_read_now--; } } /* Memory block transfer: */ offset_start=acquire_cabinet->offset; offset_end =acquire_cabinet->offset; while (offset_endbase+offset_start,offset_end-offset_start); if (bytes_read) *bytes_read+=offset_end-offset_start; buffer+=offset_end-offset_start; bytes-=offset_end-offset_start; acquire_cabinet->offset=offset_end; } return GNOME_VFS_OK; } static void acquire_cabinet_set_uri(struct acquire_cabinet *acquire_cabinet,GnomeVFSURI *uri) { GnomeVFSURI *uri_cabextract; g_return_if_fail(acquire_cabinet!=NULL); g_return_if_fail(uri!=NULL); /* FIXME: HACK: Use proper 'cabextract' scheme after it gets implemented. * GnomeVFS will return NULL on gnome_vfs_uri_new() with scheme not available. */ uri_cabextract=gnome_vfs_uri_new("file://"); g_assert(uri_cabextract->parent==NULL); /* Do not: g_assert(!strcmp(uri_cabextract->method_string,"file")); * uri_cabextract->method_string=g_strdup("cabextract"); * as it will just strip such anchor. FIXME: Why? */ 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); } struct acquire_cabinet *acquire_cabinet_new_from_memory (gconstpointer file_base,size_t file_length,GnomeVFSURI *uri,gint cabinet_used) { struct acquire_cabinet *r; g_return_val_if_fail(file_base!=NULL,NULL); g_return_val_if_fail(uri!=NULL,NULL); captive_new(r); r->base=(/* de-const */ gpointer)file_base; r->base_cached=NULL; r->offset=0; r->handlep=NULL; r->size=file_length; acquire_cabinet_set_uri(r,uri); r->cabinet_done=0; r->cabinet_used=cabinet_used; return r; } struct acquire_cabinet *acquire_cabinet_new_from_handle (GnomeVFSHandle **handlep,GnomeVFSFileInfo *file_info,GnomeVFSURI *uri,gint cabinet_used) { struct acquire_cabinet *r; 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_new(r); if (MAP_FAILED==(r->base=mmap( NULL, /* start */ CAPTIVE_ROUND_UP64(file_info->size,getpagesize()), /* length */ PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE /* flags */ |MAP_NORESERVE, /* We will not probably not read the whole cabinet. */ -1, /* fd; ignored due to MAP_ANONYMOUS */ 0))) { /* offset; ignored due to MAP_ANONYMOUS */ g_free(r); g_return_val_if_reached(NULL); } captive_new0n(r->base_cached,CAPTIVE_ROUND_UP64(file_info->size,8)/8); r->offset=0; r->handlep=handlep; r->size=file_info->size; acquire_cabinet_set_uri(r,uri); r->cabinet_done=0; r->cabinet_used=cabinet_used; return r; } void acquire_cabinet_free(struct acquire_cabinet *acquire_cabinet) { g_return_if_fail(acquire_cabinet!=NULL); if (acquire_cabinet->base_cached) { munmap(acquire_cabinet->base,CAPTIVE_ROUND_UP64(acquire_cabinet->size,getpagesize())); /* errors ignored */ g_free(acquire_cabinet->base_cached); } g_free((/* de-const */ gchar *)acquire_cabinet->filename); gnome_vfs_uri_unref(acquire_cabinet->uri); gnome_vfs_uri_unref(acquire_cabinet->handle_uri); g_free(acquire_cabinet); } static struct file *file_write_fi_assertion; static GByteArray *file_write_bytearray; int file_write(struct file *fi, UBYTE *buf, size_t length) { g_return_val_if_fail(fi!=NULL,0); g_return_val_if_fail(buf!=NULL || length==0,0); g_return_val_if_fail(fi==file_write_fi_assertion,0); g_return_val_if_fail(file_write_bytearray!=NULL,0); if ((*ui_progress)(NULL)) return 0; g_byte_array_append(file_write_bytearray,buf,length); return 1; /* success */ } void acquire_cabinet_load(struct acquire_cabinet *acquire_cabinet) { struct cabinet *basecab; struct file *filelist,*fi; g_return_if_fail(acquire_cabinet!=NULL); if (ui_progress_bar) (*ui_progress_bar)(acquire_cabinet->cabinet_done,acquire_cabinet->cabinet_used); if ((*ui_progress)(acquire_cabinet->uri)) return; basecab=find_cabs_in_file(acquire_cabinet); if (!basecab) return; if (basecab->next) return; if (basecab->prevcab || basecab->nextcab) return; 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; } file_write_fi_assertion=fi; file_write_bytearray=g_byte_array_new(); /* extract_file() returns 1 for success. */ errint=extract_file(fi, 0, /* lower; ignored now */ FALSE, /* fix */ NULL); /* dir; ignored now */ 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) return; continue; } file_buffer=g_byte_array_free(file_write_bytearray, FALSE); /* free_segment */ mod_uri_load_file_from_memory(file_buffer,fi->length,uri_fi); gnome_vfs_uri_unref(uri_fi); g_free(file_buffer); } }