Implemented HTTP read retries (5 retries, timeout 20sec).
[captive.git] / src / install / acquire / cabinet.c
1 /* $Id$
2  * cabextract interface for acquiration installation utility
3  * Copyright (C) 2003 Jan Kratochvil <project-captive@jankratochvil.net>
4  * 
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; exactly version 2 of June 1991 is required
8  * 
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  * 
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17  */
18
19
20 #include "config.h"
21
22 #include "cabinet.h"    /* self */
23 #include <glib/gmessages.h>
24 #include <libgnomevfs/gnome-vfs-file-size.h>
25 #include <libgnomevfs/gnome-vfs-ops.h>
26 #include "cabextract/cabextract.h"
27 #include "captivemodid.h"
28 #include "moduriload.h"
29 #include <sys/mman.h>
30 #include <unistd.h>
31 #include "main.h"
32 #include <signal.h>
33 #include <setjmp.h>
34
35 #include <captive/macros.h>
36
37
38 /* Config: */
39 #define ACQUIRE_CABINET_READ_RAW_READ_TRY_MAX 5
40 #define ACQUIRE_CABINET_READ_RAW_READ_TIMEOUT 20
41
42
43 void acquire_cabinet_seek(struct acquire_cabinet *acquire_cabinet,GnomeVFSFileOffset offset)
44 {
45         g_return_if_fail(acquire_cabinet!=NULL);
46
47         /* Do not: (*ui_progress)(acquire_cabinet->uri);
48          * as we currently extract some specific file out of it.
49          */
50         (*ui_progress)(NULL);
51
52         acquire_cabinet->offset=offset;
53 }
54
55 void acquire_cabinet_seek_skip(struct acquire_cabinet *acquire_cabinet,GnomeVFSFileOffset offset)
56 {
57         g_return_if_fail(acquire_cabinet!=NULL);
58
59         (*ui_progress)(NULL);
60
61         acquire_cabinet->offset+=offset;
62 }
63
64 GnomeVFSFileOffset acquire_cabinet_tell(struct acquire_cabinet *acquire_cabinet)
65 {
66         g_return_val_if_fail(acquire_cabinet!=NULL,GNOME_VFS_ERROR_BAD_PARAMETERS);
67
68         (*ui_progress)(NULL);
69
70         return acquire_cabinet->offset;
71 }
72
73 static gboolean handler_SIGALRM_hit;
74 static sigjmp_buf handler_SIGALRM_sigjmp_buf;
75
76 static void handler_SIGALRM(int signo)
77 {
78         g_return_if_fail(signo==SIGALRM);
79
80         /* Try to abort the read(2) call first.
81          * If it already read something it will return the partially read data.
82          * Otherwise gnome_vfs_inet_connection_read() will loop back to retry read(2)
83          * and we will abort it after 1 second. OK, some data may be read that time
84          * but who cares.
85          */
86         if (!handler_SIGALRM_hit) {
87                 handler_SIGALRM_hit=TRUE;
88                 alarm(1);
89                 return;
90                 }
91
92         siglongjmp(handler_SIGALRM_sigjmp_buf,1);       /* 1; meaning: !=0 */
93 }
94
95 /* FIXME: This is hack.
96  * Correct way would be to use 'GnomeVFSCancellation'
97  * to abort 'GnomeVFSInetConnection' acting as 'GnomeVFSSocket'.
98  * This abort should be handled from 'http'/'httpcaptive' handler
99  * but gnome_vfs_cancellation_cancel() cannot be invoked from
100  * the asynchronous slave thread.
101  */
102 static GnomeVFSResult acquire_cabinet_read_raw
103                 (struct acquire_cabinet *acquire_cabinet,gpointer buffer,GnomeVFSFileSize bytes,GnomeVFSFileSize *bytes_read,
104                 GnomeVFSFileOffset offset)
105 {
106 gint try=0;
107
108         g_return_val_if_fail(acquire_cabinet!=NULL,GNOME_VFS_ERROR_BAD_PARAMETERS);
109         g_return_val_if_fail(buffer!=NULL || bytes==0,GNOME_VFS_ERROR_BAD_PARAMETERS);
110         g_return_val_if_fail(bytes_read!=NULL,GNOME_VFS_ERROR_BAD_PARAMETERS);
111
112         *bytes_read=0;
113
114         if (!bytes)
115                 return GNOME_VFS_ERROR_EOF;
116
117         while (try++<=ACQUIRE_CABINET_READ_RAW_READ_TRY_MAX) {
118 GnomeVFSResult errvfsresult;
119 unsigned erruint;
120 GnomeVFSHandle *handle_new;
121 struct sigaction oldact;
122 int errint;
123
124                 if ((*ui_progress)(NULL))
125                         return GNOME_VFS_ERROR_INTERRUPTED;
126
127                 if (!sigsetjmp(
128                                 handler_SIGALRM_sigjmp_buf,     /* env */
129                                 TRUE)) {        /* savesigs */
130                         handler_SIGALRM_hit=FALSE;
131                         errint=sigaction(
132                                         SIGALRM,        /* signum */
133                                         NULL,   /* act */
134                                         &oldact);       /* oldact */
135                         g_assert(errint==0);
136                         signal(SIGALRM,handler_SIGALRM);
137                         erruint=alarm(ACQUIRE_CABINET_READ_RAW_READ_TIMEOUT);
138                         g_assert(erruint==0);
139                         errvfsresult=gnome_vfs_seek(acquire_cabinet->handle,GNOME_VFS_SEEK_START,offset);
140                         if (GNOME_VFS_OK==errvfsresult)
141                                 errvfsresult=gnome_vfs_read(acquire_cabinet->handle,buffer,bytes,bytes_read);
142                         }
143                 else
144                         errvfsresult=GNOME_VFS_ERROR_INTERRUPTED;
145                 alarm(0);
146                 errint=sigaction(
147                                 SIGALRM,        /* signum */
148                                 &oldact,        /* act */
149                                 NULL);  /* oldact */
150                 g_assert(errint==0);
151                 if (errvfsresult==GNOME_VFS_OK) {
152                         g_assert(*bytes_read>0);
153                         return GNOME_VFS_OK;
154                         }
155
156                 /* Reopen 'acquire_cabinet->handle' */
157
158                 g_assert(acquire_cabinet->handle_uri!=NULL);
159                 if (GNOME_VFS_OK==(errvfsresult=gnome_vfs_open_uri(&handle_new,acquire_cabinet->handle_uri,GNOME_VFS_OPEN_READ))) {
160                         gnome_vfs_close(acquire_cabinet->handle);       /* errors ignored */
161                         acquire_cabinet->handle=handle_new;
162                         }
163                 }
164
165         return GNOME_VFS_ERROR_IO;
166 }
167
168 #define ACQUIRE_CABINET_BYTE_CACHED(acquire_cabinet,pos)     (!(acquire_cabinet)->base_cached \
169                                                            || (acquire_cabinet)->base_cached[(pos)/8] &  1<<((pos)&7))
170 #define ACQUIRE_CABINET_SET_BYTE_CACHED(acquire_cabinet,pos) ((acquire_cabinet)->base_cached[(pos)/8] |= 1<<((pos)&7))
171
172 GnomeVFSResult acquire_cabinet_read
173                 (struct acquire_cabinet *acquire_cabinet,gpointer buffer,GnomeVFSFileSize bytes,GnomeVFSFileSize *bytes_read)
174 {
175 GnomeVFSFileOffset offset_start,offset_end,read_behind;
176 GnomeVFSResult errvfsresult;
177 GnomeVFSFileSize bytes_read_now;
178
179         g_return_val_if_fail(acquire_cabinet!=NULL,GNOME_VFS_ERROR_BAD_PARAMETERS);
180         g_return_val_if_fail(buffer!=NULL || bytes==0,GNOME_VFS_ERROR_BAD_PARAMETERS);
181
182         *bytes_read=0;
183
184         if ((*ui_progress)(NULL))
185                 return GNOME_VFS_ERROR_INTERRUPTED;
186
187         bytes=MAX(0,MIN(bytes,acquire_cabinet->size-acquire_cabinet->offset));
188         if (!bytes)
189                 return GNOME_VFS_ERROR_EOF;
190
191         while (bytes) {
192                 read_behind =acquire_cabinet->offset+bytes;
193
194                 /* GnomeVFS block transfer: */
195                 offset_start=acquire_cabinet->offset;
196                 offset_end  =acquire_cabinet->offset;
197                 while (offset_end<read_behind && !ACQUIRE_CABINET_BYTE_CACHED(acquire_cabinet,offset_end))
198                         offset_end++;
199                 if (offset_end>offset_start) {
200                         errvfsresult=acquire_cabinet_read_raw(acquire_cabinet,
201                                         acquire_cabinet->base+offset_start,offset_end-offset_start,&bytes_read_now,offset_start);
202                         if (errvfsresult!=GNOME_VFS_OK)
203                                 return errvfsresult;
204                         g_assert(bytes_read_now>0);
205                         acquire_cabinet->cabinet_done+=bytes_read_now;
206                         if (ui_progress_bar)
207                                 (*ui_progress_bar)(acquire_cabinet->cabinet_done,acquire_cabinet->cabinet_used);
208                         while (bytes_read_now) {
209                                 ACQUIRE_CABINET_SET_BYTE_CACHED(acquire_cabinet,offset_start);
210                                 offset_start++;
211                                 bytes_read_now--;
212                                 }
213                         }
214
215                 /* Memory block transfer: */
216                 offset_start=acquire_cabinet->offset;
217                 offset_end  =acquire_cabinet->offset;
218                 while (offset_end<read_behind && ACQUIRE_CABINET_BYTE_CACHED(acquire_cabinet,offset_end))
219                         offset_end++;
220                 memcpy(buffer,acquire_cabinet->base+offset_start,offset_end-offset_start);
221                 if (bytes_read)
222                         *bytes_read+=offset_end-offset_start;
223                 buffer+=offset_end-offset_start;
224                 bytes-=offset_end-offset_start;
225                 acquire_cabinet->offset=offset_end;
226                 }
227
228         return GNOME_VFS_OK;
229 }
230
231 static void acquire_cabinet_set_uri(struct acquire_cabinet *acquire_cabinet,GnomeVFSURI *uri)
232 {
233 GnomeVFSURI *uri_cabextract;
234
235         g_return_if_fail(acquire_cabinet!=NULL);
236         g_return_if_fail(uri!=NULL);
237
238         /* FIXME: HACK: Use proper 'cabextract' scheme after it gets implemented.
239          * GnomeVFS will return NULL on gnome_vfs_uri_new() with scheme not available.
240          */
241         uri_cabextract=gnome_vfs_uri_new("file://");
242         g_assert(uri_cabextract->parent==NULL);
243         /* Do not: g_assert(!strcmp(uri_cabextract->method_string,"file"));
244          *         uri_cabextract->method_string=g_strdup("cabextract");
245          * as it will just strip such anchor. FIXME: Why?
246          */
247
248         uri_cabextract->parent=gnome_vfs_uri_dup(uri);
249
250         acquire_cabinet->uri=uri_cabextract;
251         acquire_cabinet->handle_uri=gnome_vfs_uri_ref(uri);
252         acquire_cabinet->filename=gnome_vfs_uri_to_string(acquire_cabinet->uri,GNOME_VFS_URI_HIDE_PASSWORD);
253 }
254
255 struct acquire_cabinet *acquire_cabinet_new_from_memory
256                 (gconstpointer file_base,size_t file_length,GnomeVFSURI *uri,gint cabinet_used)
257 {
258 struct acquire_cabinet *r;
259
260         g_return_val_if_fail(file_base!=NULL,NULL);
261         g_return_val_if_fail(uri!=NULL,NULL);
262         
263         captive_new(r);
264         r->base=(/* de-const */ gpointer)file_base;
265         r->base_cached=NULL;
266         r->offset=0;
267         r->handle=NULL;
268         r->size=file_length;
269         acquire_cabinet_set_uri(r,uri);
270         r->cabinet_done=0;
271         r->cabinet_used=cabinet_used;
272
273         return r;
274 }
275
276 struct acquire_cabinet *acquire_cabinet_new_from_handle
277                 (GnomeVFSHandle *handle,GnomeVFSFileInfo *file_info,GnomeVFSURI *uri,gint cabinet_used)
278 {
279 struct acquire_cabinet *r;
280
281         g_return_val_if_fail(handle!=NULL,NULL);
282         g_return_val_if_fail(file_info!=NULL,NULL);
283         g_return_val_if_fail(uri!=NULL,NULL);
284         
285         captive_new(r);
286         if (MAP_FAILED==(r->base=mmap(
287                         NULL,   /* start */
288                         CAPTIVE_ROUND_UP64(file_info->size,getpagesize()),      /* length */
289                         PROT_READ|PROT_WRITE,
290                         MAP_ANONYMOUS|MAP_PRIVATE       /* flags */
291                                         |MAP_NORESERVE, /* We will not probably not read the whole cabinet. */
292                         -1,     /* fd; ignored due to MAP_ANONYMOUS */
293                         0))) {  /* offset; ignored due to MAP_ANONYMOUS */
294                 g_free(r);
295                 g_return_val_if_reached(NULL);
296                 }
297         captive_new0n(r->base_cached,CAPTIVE_ROUND_UP64(file_info->size,8)/8);
298         r->offset=0;
299         r->handle=handle;
300         r->size=file_info->size;
301         acquire_cabinet_set_uri(r,uri);
302         r->cabinet_done=0;
303         r->cabinet_used=cabinet_used;
304
305         return r;
306 }
307
308 void acquire_cabinet_free(struct acquire_cabinet *acquire_cabinet)
309 {
310         g_return_if_fail(acquire_cabinet!=NULL);
311
312         if (acquire_cabinet->base_cached) {
313                 munmap(acquire_cabinet->base,CAPTIVE_ROUND_UP64(acquire_cabinet->size,getpagesize()));  /* errors ignored */
314                 g_free(acquire_cabinet->base_cached);
315                 }
316         g_free((/* de-const */ gchar *)acquire_cabinet->filename);
317         gnome_vfs_uri_unref(acquire_cabinet->uri);
318         gnome_vfs_uri_unref(acquire_cabinet->handle_uri);
319         g_free(acquire_cabinet);
320 }
321
322 static struct file *file_write_fi_assertion;
323 static GByteArray *file_write_bytearray;
324
325 int file_write(struct file *fi, UBYTE *buf, size_t length)
326 {
327         g_return_val_if_fail(fi!=NULL,0);
328         g_return_val_if_fail(buf!=NULL || length==0,0);
329
330         g_return_val_if_fail(fi==file_write_fi_assertion,0);
331         g_return_val_if_fail(file_write_bytearray!=NULL,0);
332
333         if ((*ui_progress)(NULL))
334                 return 0;
335
336         g_byte_array_append(file_write_bytearray,buf,length);
337
338         return 1;       /* success */
339 }
340
341 void acquire_cabinet_load(struct acquire_cabinet *acquire_cabinet)
342 {
343 struct cabinet *basecab;
344 struct file *filelist,*fi;
345
346         g_return_if_fail(acquire_cabinet!=NULL);
347
348         if (ui_progress_bar)
349                 (*ui_progress_bar)(acquire_cabinet->cabinet_done,acquire_cabinet->cabinet_used);
350
351         if ((*ui_progress)(acquire_cabinet->uri))
352                 return;
353
354         basecab=find_cabs_in_file(acquire_cabinet);
355         if (!basecab)
356                 return;
357         if (basecab->next)
358                 return;
359         if (basecab->prevcab || basecab->nextcab)
360                 return;
361
362         filelist=process_files(basecab);
363
364         for (fi=filelist;fi;fi=fi->next) {
365 gpointer file_buffer;
366 GnomeVFSURI *uri_fi;
367 int errint;
368
369                 if (!captivemodid_module_length_is_valid(fi->length))
370                         continue;
371
372                 uri_fi=gnome_vfs_uri_append_file_name(acquire_cabinet->uri,fi->filename);
373                 if ((*ui_progress)(uri_fi)) {
374                         gnome_vfs_uri_unref(uri_fi);
375                         return;
376                         }
377
378                 file_write_fi_assertion=fi;
379                 file_write_bytearray=g_byte_array_new();
380                 /* extract_file() returns 1 for success. */
381                 errint=extract_file(fi,
382                                 0,      /* lower; ignored now */
383                                 FALSE,  /* fix */
384                                 NULL);  /* dir; ignored now */
385                 if (!errint || fi->length!=file_write_bytearray->len) {
386                         g_byte_array_free(file_write_bytearray,
387                                         TRUE);  /* free_segment */
388                         gnome_vfs_uri_unref(uri_fi);
389                         if (!errint)
390                                 return;
391                         continue;
392                         }
393                 file_buffer=g_byte_array_free(file_write_bytearray,
394                                 FALSE); /* free_segment */
395                 mod_uri_load_file_from_memory(file_buffer,fi->length,uri_fi);
396                 gnome_vfs_uri_unref(uri_fi);
397                 g_free(file_buffer);
398     }
399 }