1 /*
2 * Copyright (C) 2008 Giuseppe Torelli - <colossus73@gmail.com>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
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., 51 Franklin Street, Fifth Floor, Boston,
17 * MA 02110-1301 USA.
18 */
19
20 #include <errno.h>
21 #include <string.h>
22 #include <glib/gprintf.h>
23 #include "rpm.h"
24 #include "date_utils.h"
25 #include "main.h"
26 #include "string_utils.h"
27 #include "support.h"
28 #include "window.h"
29
30 #define LEAD_LEN 96
31 #define HDRSIG_MAGIC_LEN 3
32 #define HDRSIG_VERSION_LEN 1
33 #define HDRSIG_RESERVED_LEN 4
34 #define HDRSIG_LEAD_IN_LEN (HDRSIG_MAGIC_LEN + HDRSIG_VERSION_LEN + HDRSIG_RESERVED_LEN)
35 #define SIGNATURE_START (LEAD_LEN + HDRSIG_LEAD_IN_LEN)
36 #define HDRSIG_ENTRY_INFO_LEN 8
37 #define HDRSIG_ENTRY_INDEX_LEN 16
38
xa_rpm_ask(XArchive * archive)39 void xa_rpm_ask (XArchive *archive)
40 {
41 archive->can_extract = TRUE;
42 archive->can_full_path[0] = TRUE;
43 archive->can_touch = TRUE;
44 archive->can_overwrite = TRUE;
45 }
46
xa_rpm2cpio(XArchive * archive)47 static gchar *xa_rpm2cpio (XArchive *archive)
48 {
49 unsigned char bytes[HDRSIG_ENTRY_INFO_LEN];
50 int datalen, entries;
51 long offset;
52 gchar *cpio_z, *ibs, *command, *executable;
53 FILE *stream;
54 gboolean success;
55 ArchiveType xa;
56
57 signal(SIGPIPE, SIG_IGN);
58 stream = fopen(archive->path[0], "r");
59
60 if (stream == NULL)
61 {
62 gchar *msg, *err;
63
64 msg = g_strdup_printf(_("Can't open RPM file %s:"), archive->path[0]);
65 err = g_strconcat(msg, " ", g_strerror(errno), NULL);
66
67 g_free(msg);
68
69 return err;
70 }
71
72 /* Signature section */
73 if (fseek(stream, SIGNATURE_START, SEEK_CUR) == -1)
74 {
75 fclose (stream);
76 return g_strconcat(_("Can't fseek to position 104:"), " ", g_strerror(errno), NULL);
77 }
78 if (fread(bytes, sizeof(*bytes), HDRSIG_ENTRY_INFO_LEN, stream) != HDRSIG_ENTRY_INFO_LEN)
79 {
80 fclose ( stream );
81 return g_strconcat(_("Can't read data from file:"), " ", g_strerror(errno), NULL);
82 }
83 entries = 256 * (256 * (256 * bytes[0] + bytes[1]) + bytes[2]) + bytes[3];
84 datalen = 256 * (256 * (256 * bytes[4] + bytes[5]) + bytes[6]) + bytes[7];
85 datalen += (16 - (datalen % 16)) % 16; // header section is aligned
86 offset = HDRSIG_ENTRY_INDEX_LEN * entries + datalen;
87
88 /* Header section */
89 if (fseek(stream, offset, SEEK_CUR))
90 {
91 fclose (stream);
92 return g_strconcat(_("Can't fseek in file:"), " ", g_strerror(errno), NULL);
93 }
94 if (fread(bytes, sizeof(*bytes), HDRSIG_ENTRY_INFO_LEN, stream) != HDRSIG_ENTRY_INFO_LEN)
95 {
96 fclose ( stream );
97 return g_strconcat(_("Can't read data from file:"), " ", g_strerror(errno), NULL);
98 }
99 entries = 256 * (256 * (256 * bytes[0] + bytes[1]) + bytes[2]) + bytes[3];
100 datalen = 256 * (256 * (256 * bytes[4] + bytes[5]) + bytes[6]) + bytes[7];
101 offset = HDRSIG_ENTRY_INDEX_LEN * entries + datalen;
102 offset += ftell(stream); // offset from top
103
104 fclose(stream);
105
106 /* create a unique temp dir in /tmp */
107 if (!xa_create_working_directory(archive))
108 return g_strdup("");
109
110 cpio_z = g_strconcat(archive->working_dir, "/xa-tmp.cpio_z", NULL);
111 ibs = g_strdup_printf("%lu", offset);
112
113 /* run dd to have the payload (compressed cpio archive) in /tmp */
114 command = g_strconcat("dd if=", archive->path[1], " ibs=", ibs, " skip=1 of=", cpio_z, NULL);
115 g_free(ibs);
116
117 success = xa_run_command(archive, command);
118 g_free(command);
119
120 if (!success)
121 {
122 g_free(cpio_z);
123 return g_strdup("");
124 }
125
126 xa = xa_detect_archive_type(cpio_z);
127
128 switch (xa.type)
129 {
130 case XARCHIVETYPE_GZIP:
131 executable = "gzip -dc ";
132 break;
133
134 case XARCHIVETYPE_BZIP2:
135 executable = "bzip2 -dc ";
136 break;
137
138 case XARCHIVETYPE_LZMA:
139 case XARCHIVETYPE_XZ:
140 executable = "xz -dc ";
141 break;
142
143 default:
144 g_free(cpio_z);
145 return g_strdup(_("Unknown compression type!"));
146 }
147
148 command = g_strconcat("sh -c \"", executable, cpio_z, " > ", archive->working_dir, "/xa-tmp.cpio\"", NULL);
149 g_free(cpio_z);
150
151 success = xa_run_command(archive, command);
152 g_free(command);
153
154 return (success ? NULL : g_strdup(""));
155 }
156
xa_cpio_parse_output(gchar * line,XArchive * archive)157 static void xa_cpio_parse_output (gchar *line, XArchive *archive)
158 {
159 XEntry *entry;
160 gchar *filename, time[6];
161 gpointer item[7];
162 gint n = 0, a = 0 ,linesize = 0;
163
164 linesize = strlen(line);
165
166 /* Permissions */
167 line[10] = '\0';
168 item[4] = line;
169 a = 11;
170
171 /* Hard Link */
172 for(n=a; n < linesize && line[n] == ' '; ++n);
173 line[++n] = '\0';
174 //item[5] = line + a;
175 n++;
176 a = n;
177
178 /* Owner */
179 for(; n < linesize && line[n] != ' '; ++n);
180 line[n] = '\0';
181 item[5] = line + a;
182 n++;
183
184 /* Group */
185 for(; n < linesize && line[n] == ' '; ++n);
186 a = n;
187
188 for(; n < linesize && line[n] != ' '; ++n);
189 line[n] = '\0';
190 item[6] = line + a;
191 n++;
192
193 /* Size */
194 for(; n < linesize && line[n] == ' '; ++n);
195 a = n;
196
197 for(; n < linesize && line[n] != ' '; ++n);
198 line[n] = '\0';
199 item[1] = line + a;
200 n++;
201
202 /* Date and Time */
203 line[54] = '\0';
204 item[2] = line + n;
205 n = 55;
206
207 /* Time */
208 if (((char *) item[2])[9] == ':')
209 {
210 memcpy(time, item[2] + 7, 5);
211 time[5] = 0;
212 }
213 else
214 strcpy(time, "-----");
215
216 item[2] = date_MMM_dD_HourYear(item[2]);
217 item[3] = time;
218
219 line[linesize-1] = '\0';
220 filename = line + n;
221
222 /* Symbolic link */
223 gchar *temp = g_strrstr (filename,"->");
224 if (temp)
225 {
226 a = 3;
227 gint len = strlen(filename) - strlen(temp);
228 item[0] = filename + a + len;
229 filename[strlen(filename) - strlen(temp)] = '\0';
230 }
231 else
232 item[0] = NULL;
233
234 if(line[0] == 'd')
235 {
236 /* Work around for cpio, which does
237 * not output / with directories */
238
239 if(line[linesize-2] != '/')
240 filename = g_strconcat(line + n, "/", NULL);
241 else
242 filename = g_strdup(line + n);
243 }
244 else
245 filename = g_strdup(line + n);
246
247 entry = xa_set_archive_entries_for_each_row(archive, filename, item);
248
249 if (entry)
250 {
251 if (!entry->is_dir)
252 archive->files++;
253
254 archive->files_size += g_ascii_strtoull(item[1], NULL, 0);
255 }
256
257 g_free (filename);
258 }
259
xa_rpm_list(XArchive * archive)260 void xa_rpm_list (XArchive *archive)
261 {
262 const GType types[] = {GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_UINT64, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER};
263 const gchar *titles[] = {_("Points to"), _("Original Size"), _("Date"), _("Time"), _("Permissions"), _("Owner"), _("Group")};
264 gchar *result, *command;
265 guint i;
266
267 result = xa_rpm2cpio(archive);
268
269 archive->files_size = 0;
270 archive->files = 0;
271
272 archive->columns = 10;
273 archive->size_column = 3;
274 archive->column_types = g_malloc0(sizeof(types));
275
276 for (i = 0; i < archive->columns; i++)
277 archive->column_types[i] = types[i];
278
279 xa_create_liststore(archive, titles);
280
281 if (result != NULL)
282 {
283 if (*result)
284 command = g_strconcat("sh -c \"echo ", result, " >&2; exit 1\"", NULL);
285 else
286 command = g_strdup("sh -c \"\"");
287
288 g_free(result);
289 }
290 else
291 command = g_strconcat(archiver[archive->type].program[0], " -tv -I ", archive->working_dir, "/xa-tmp.cpio", NULL);
292
293 archive->parse_output = xa_cpio_parse_output;
294 xa_spawn_async_process (archive,command);
295 g_free(command);
296 }
297
298 /*
299 * Note: cpio lists ' ' as '\ ', '"' as '\"' and '\' as '\\' while it
300 * extracts ' ', '"' and '\' respectively, i.e. file names containing
301 * one of these three characters can't be handled entirely.
302 */
303
xa_rpm_extract(XArchive * archive,GSList * file_list)304 gboolean xa_rpm_extract (XArchive *archive, GSList *file_list)
305 {
306 GString *files;
307 gchar *extract_to, *command;
308 gboolean result;
309
310 if (archive->do_full_path)
311 extract_to = g_strdup(archive->extraction_dir);
312 else
313 {
314 if (!xa_create_working_directory(archive))
315 return FALSE;
316
317 extract_to = g_strconcat(archive->working_dir, "/xa-tmp.XXXXXX", NULL);
318
319 if (!g_mkdtemp(extract_to))
320 {
321 g_free(extract_to);
322 return FALSE;
323 }
324 }
325
326 files = xa_quote_filenames(file_list, "*?[]\"", FALSE);
327 archive->child_dir = g_strdup(extract_to);
328 command = g_strconcat(archiver[archive->type].program[0], " -id",
329 archive->do_touch ? "" : " -m",
330 archive->do_overwrite ? " -u" : "",
331 " -I ", archive->working_dir, "/xa-tmp.cpio",
332 files->str, NULL);
333 result = xa_run_command(archive, command);
334 g_free(command);
335
336 g_free(archive->child_dir);
337 archive->child_dir = NULL;
338
339 /* collect all files that have been extracted to move them without full path */
340 if (result && !archive->do_full_path)
341 {
342 GString *all_files = xa_collect_files_in_dir(extract_to);
343
344 archive->child_dir = g_strdup(extract_to);
345 command = g_strconcat("mv",
346 archive->do_overwrite ? " -f" : " -n",
347 all_files->str, " ", archive->extraction_dir, NULL);
348 g_string_free(all_files, TRUE);
349
350 result = xa_run_command(archive, command);
351 g_free(command);
352
353 g_free(archive->child_dir);
354 archive->child_dir = NULL;
355 }
356
357 g_free(extract_to);
358 g_string_free(files,TRUE);
359
360 return result;
361 }
362