0e9a790ed9e7fa74e3599912a0d56e0f6f77bb32
[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
33 #include <captive/macros.h>
34
35
36 void acquire_cabinet_seek(struct acquire_cabinet *acquire_cabinet,GnomeVFSFileOffset offset)
37 {
38         g_return_if_fail(acquire_cabinet!=NULL);
39
40         /* Do not: (*ui_progress)(acquire_cabinet->uri);
41          * as we currently extract some specific file out of it.
42          */
43         (*ui_progress)(NULL);
44
45         acquire_cabinet->offset=offset;
46 }
47
48 void acquire_cabinet_seek_skip(struct acquire_cabinet *acquire_cabinet,GnomeVFSFileOffset offset)
49 {
50         g_return_if_fail(acquire_cabinet!=NULL);
51
52         (*ui_progress)(NULL);
53
54         acquire_cabinet->offset+=offset;
55 }
56
57 GnomeVFSFileOffset acquire_cabinet_tell(struct acquire_cabinet *acquire_cabinet)
58 {
59         g_return_val_if_fail(acquire_cabinet!=NULL,GNOME_VFS_ERROR_BAD_PARAMETERS);
60
61         (*ui_progress)(NULL);
62
63         return acquire_cabinet->offset;
64 }
65
66 #define ACQUIRE_CABINET_BYTE_CACHED(acquire_cabinet,pos)     (!(acquire_cabinet)->base_cached \
67                                                            || (acquire_cabinet)->base_cached[(pos)/8] &  1<<((pos)&7))
68 #define ACQUIRE_CABINET_SET_BYTE_CACHED(acquire_cabinet,pos) ((acquire_cabinet)->base_cached[(pos)/8] |= 1<<((pos)&7))
69
70 GnomeVFSResult acquire_cabinet_read
71                 (struct acquire_cabinet *acquire_cabinet,gpointer buffer,GnomeVFSFileSize bytes,GnomeVFSFileSize *bytes_read)
72 {
73 GnomeVFSFileOffset offset_start,offset_end,read_behind;
74 GnomeVFSResult errvfsresult;
75 GnomeVFSFileSize bytes_read_now;
76
77         g_return_val_if_fail(acquire_cabinet!=NULL,GNOME_VFS_ERROR_BAD_PARAMETERS);
78         g_return_val_if_fail(buffer!=NULL || bytes==0,GNOME_VFS_ERROR_BAD_PARAMETERS);
79
80         if ((*ui_progress)(NULL))
81                 return GNOME_VFS_ERROR_INTERRUPTED;
82
83         bytes=MAX(0,MIN(bytes,acquire_cabinet->size-acquire_cabinet->offset));
84         if (!bytes)
85                 return GNOME_VFS_ERROR_EOF;
86
87         *bytes_read=0;
88         while (bytes) {
89                 read_behind =acquire_cabinet->offset+bytes;
90
91                 /* GnomeVFS block transfer: */
92                 offset_start=acquire_cabinet->offset;
93                 offset_end  =acquire_cabinet->offset;
94                 while (offset_end<read_behind && !ACQUIRE_CABINET_BYTE_CACHED(acquire_cabinet,offset_end))
95                         offset_end++;
96                 if (offset_end>offset_start) {
97                         if (GNOME_VFS_OK!=(errvfsresult=gnome_vfs_seek(acquire_cabinet->handle,GNOME_VFS_SEEK_START,offset_start)))
98                                 return errvfsresult;
99                         errvfsresult=gnome_vfs_read(acquire_cabinet->handle,
100                                         acquire_cabinet->base+offset_start,offset_end-offset_start,&bytes_read_now);
101                         if (errvfsresult!=GNOME_VFS_OK)
102                                 return errvfsresult;
103                         g_assert(bytes_read_now>0);
104                         while (bytes_read_now) {
105                                 ACQUIRE_CABINET_SET_BYTE_CACHED(acquire_cabinet,offset_start);
106                                 offset_start++;
107                                 bytes_read_now--;
108                                 }
109                         }
110
111                 /* Memory block transfer: */
112                 offset_start=acquire_cabinet->offset;
113                 offset_end  =acquire_cabinet->offset;
114                 while (offset_end<read_behind && ACQUIRE_CABINET_BYTE_CACHED(acquire_cabinet,offset_end))
115                         offset_end++;
116                 memcpy(buffer,acquire_cabinet->base+offset_start,offset_end-offset_start);
117                 if (bytes_read)
118                         *bytes_read+=offset_end-offset_start;
119                 buffer+=offset_end-offset_start;
120                 bytes-=offset_end-offset_start;
121                 acquire_cabinet->offset=offset_end;
122                 }
123
124         return GNOME_VFS_OK;
125 }
126
127 static void acquire_cabinet_set_uri(struct acquire_cabinet *acquire_cabinet,GnomeVFSURI *uri)
128 {
129 GnomeVFSURI *uri_cabextract;
130
131         g_return_if_fail(acquire_cabinet!=NULL);
132         g_return_if_fail(uri!=NULL);
133
134         /* FIXME: HACK: Use proper 'cabextract' scheme after it gets implemented.
135          * GnomeVFS will return NULL on gnome_vfs_uri_new() with scheme not available.
136          */
137         uri_cabextract=gnome_vfs_uri_new("file://");
138         g_assert(uri_cabextract->parent==NULL);
139         /* Do not: g_assert(!strcmp(uri_cabextract->method_string,"file"));
140          *         uri_cabextract->method_string=g_strdup("cabextract");
141          * as it will just strip such anchor. FIXME: Why?
142          */
143
144         uri_cabextract->parent=gnome_vfs_uri_dup(uri);
145
146         acquire_cabinet->uri=uri_cabextract;
147         acquire_cabinet->filename=gnome_vfs_uri_to_string(acquire_cabinet->uri,GNOME_VFS_URI_HIDE_PASSWORD);
148 }
149
150 struct acquire_cabinet *acquire_cabinet_new_from_memory(gconstpointer file_base,size_t file_length,GnomeVFSURI *uri)
151 {
152 struct acquire_cabinet *r;
153
154         g_return_val_if_fail(file_base!=NULL,NULL);
155         g_return_val_if_fail(uri!=NULL,NULL);
156         
157         captive_new(r);
158         r->base=(/* de-const */ gpointer)file_base;
159         r->base_cached=NULL;
160         r->offset=0;
161         r->handle=NULL;
162         r->size=file_length;
163         acquire_cabinet_set_uri(r,uri);
164
165         return r;
166 }
167
168 struct acquire_cabinet *acquire_cabinet_new_from_handle(GnomeVFSHandle *handle,GnomeVFSFileInfo *file_info,GnomeVFSURI *uri)
169 {
170 struct acquire_cabinet *r;
171
172         g_return_val_if_fail(handle!=NULL,NULL);
173         g_return_val_if_fail(file_info!=NULL,NULL);
174         g_return_val_if_fail(uri!=NULL,NULL);
175         
176         captive_new(r);
177         if (MAP_FAILED==(r->base=mmap(
178                         NULL,   /* start */
179                         CAPTIVE_ROUND_UP64(file_info->size,getpagesize()),      /* length */
180                         PROT_READ|PROT_WRITE,
181                         MAP_ANONYMOUS|MAP_PRIVATE       /* flags */
182                                         |MAP_NORESERVE, /* We will not probably not read the whole cabinet. */
183                         -1,     /* fd; ignored due to MAP_ANONYMOUS */
184                         0))) {  /* offset; ignored due to MAP_ANONYMOUS */
185                 g_free(r);
186                 g_return_val_if_reached(NULL);
187                 }
188         captive_new0n(r->base_cached,CAPTIVE_ROUND_UP64(file_info->size,8)/8);
189         r->offset=0;
190         r->handle=handle;
191         r->size=file_info->size;
192         acquire_cabinet_set_uri(r,uri);
193
194         return r;
195 }
196
197 void acquire_cabinet_free(struct acquire_cabinet *acquire_cabinet)
198 {
199         g_return_if_fail(acquire_cabinet!=NULL);
200
201         if (acquire_cabinet->base_cached) {
202                 munmap(acquire_cabinet->base,CAPTIVE_ROUND_UP64(acquire_cabinet->size,getpagesize()));  /* errors ignored */
203                 g_free(acquire_cabinet->base_cached);
204                 }
205         g_free((/* de-const */ gchar *)acquire_cabinet->filename);
206         gnome_vfs_uri_unref(acquire_cabinet->uri);
207         g_free(acquire_cabinet);
208 }
209
210 static struct file *file_write_fi_assertion;
211 static GByteArray *file_write_bytearray;
212
213 int file_write(struct file *fi, UBYTE *buf, size_t length)
214 {
215         g_return_val_if_fail(fi!=NULL,0);
216         g_return_val_if_fail(buf!=NULL || length==0,0);
217
218         g_return_val_if_fail(fi==file_write_fi_assertion,0);
219         g_return_val_if_fail(file_write_bytearray!=NULL,0);
220
221         if ((*ui_progress)(NULL))
222                 return 0;
223
224         g_byte_array_append(file_write_bytearray,buf,length);
225
226         return 1;       /* success */
227 }
228
229 void acquire_cabinet_load(struct acquire_cabinet *acquire_cabinet)
230 {
231 struct cabinet *basecab;
232 struct file *filelist,*fi;
233
234         g_return_if_fail(acquire_cabinet!=NULL);
235
236         if ((*ui_progress)(acquire_cabinet->uri))
237                 return;
238
239         basecab=find_cabs_in_file(acquire_cabinet);
240         if (!basecab)
241                 return;
242         if (basecab->next)
243                 return;
244         if (basecab->prevcab || basecab->nextcab)
245                 return;
246
247         filelist=process_files(basecab);
248
249         for (fi=filelist;fi;fi=fi->next) {
250 gpointer file_buffer;
251 GnomeVFSURI *uri_fi;
252
253                 if (!captivemodid_module_length_is_valid(fi->length))
254                         continue;
255
256                 uri_fi=gnome_vfs_uri_append_file_name(acquire_cabinet->uri,fi->filename);
257                 if ((*ui_progress)(uri_fi)) {
258                         gnome_vfs_uri_unref(uri_fi);
259                         return;
260                         }
261
262                 file_write_fi_assertion=fi;
263                 file_write_bytearray=g_byte_array_new();
264                 extract_file(fi,
265                                 0,      /* lower; ignored now */
266                                 FALSE,  /* fix */
267                                 NULL);  /* dir; ignored now */
268                 if (fi->length!=file_write_bytearray->len) {
269                         g_byte_array_free(file_write_bytearray,
270                                         TRUE);  /* free_segment */
271                         gnome_vfs_uri_unref(uri_fi);
272                         continue;
273                         }
274                 file_buffer=g_byte_array_free(file_write_bytearray,
275                                 FALSE); /* free_segment */
276                 mod_uri_load_file_from_memory(file_buffer,fi->length,uri_fi);
277                 gnome_vfs_uri_unref(uri_fi);
278                 g_free(file_buffer);
279     }
280 }