4 * Copyright (C) 2008 Morten Welinder (terra@gnome.org)
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
26 #include <gsf-config.h>
27 #include <gsf/gsf-infile-impl.h>
28 #include <gsf/gsf-infile-tar.h>
29 #include <gsf/gsf-impl-utils.h>
30 #include <gsf/gsf-utils.h>
31 #include <gsf/gsf-input-proxy.h>
36 #define G_LOG_DOMAIN "libgsf:tar"
38 static void gsf_infile_tar_set_source (GsfInfileTar *tar, GsfInput *src);
45 static GObjectClass *parent_class;
50 /* The location of data. */
54 /* The directory object, or NULL for a data file. */
58 /* tar header from POSIX 1003.1-1990. */
60 char name[100]; /* 0 */
61 char mode[8]; /* 100 (octal) */
62 char uid[8]; /* 108 (octal) */
63 char gid[8]; /* 116 (octal) */
64 char size[12]; /* 124 (octal) */
65 char mtime[12]; /* 136 (octal) */
66 char chksum[8]; /* 148 (octal) */
67 char typeflag; /* 156 */
68 char linkname[100]; /* 157 */
69 char magic[6]; /* 257 */
70 char version[2]; /* 263 */
71 char uname[32]; /* 265 */
72 char gname[32]; /* 297 */
73 char devmajor[8]; /* 329 (octal) */
74 char devminor[8]; /* 337 (octal) */
75 char prefix[155]; /* 345 */
76 char filler[12]; /* 500 */
79 #define HEADER_SIZE (sizeof (TarHeader))
80 #define BLOCK_SIZE 512
81 #define MAGIC_LONGNAME "././@LongLink"
83 struct _GsfInfileTar {
87 GArray *children; /* of TarChild */
92 GsfInfileClass parent_class;
95 #define GSF_INFILE_TAR_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GSF_INFILE_TAR_TYPE, GsfInfileTarClass))
96 #define GSF_IS_INFILE_TAR_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GSF_INFILE_TAR_TYPE))
99 unpack_octal (GsfInfileTar *tar, const char *s, size_t len)
104 unsigned char c = *s++;
107 if (c < '0' || c > '7') {
108 tar->err = g_error_new (gsf_input_error_id (), 0,
109 "Invalid tar header");
112 res = (res << 3) + (c - '0');
119 static GsfInfileTar *
120 tar_create_dir (GsfInfileTar *dir, const char *name)
126 c.name = g_strdup (name);
127 c.dir = g_object_new (GSF_INFILE_TAR_TYPE, NULL);
130 * We set the source here, so gsf_infile_tar_constructor doesn't
131 * start reading the tarfile recursively.
133 gsf_infile_tar_set_source (c.dir, dir->source);
135 gsf_input_set_name (GSF_INPUT (c.dir), name);
137 g_array_append_val (dir->children, c);
142 static GsfInfileTar *
143 tar_directory_for_file (GsfInfileTar *dir, const char *name, gboolean last)
145 const char *s = name;
151 /* Find a directory component, if any. */
159 /* This is deliberately slash-only. */
165 dirname = g_strndup (s0, s - s0);
169 if (strcmp (dirname, ".") != 0) {
171 gsf_infile_child_by_name (GSF_INFILE (dir),
175 g_object_unref (subdir);
176 dir = GSF_INFILE_TAR (subdir);
178 dir = tar_create_dir (dir, dirname);
186 /*****************************************************************************/
190 * @tar : #GsfInfileTar
192 * Read tar headers and do some sanity checking
196 tar_init_info (GsfInfileTar *tar)
199 const TarHeader *header;
200 gsf_off_t pos0 = gsf_input_tell (tar->source);
201 char *pending_longname = NULL;
203 memset (&end, 0, sizeof (end));
205 while (tar->err == NULL &&
206 (header = (const TarHeader *)gsf_input_read (tar->source,
213 if (memcmp (header->filler, end.filler, sizeof (end.filler))) {
214 tar->err = g_error_new (gsf_input_error_id (), 0,
215 "Invalid tar header");
219 if (memcmp (header, &end, HEADER_SIZE) == 0)
222 if (pending_longname) {
223 name = pending_longname;
224 pending_longname = NULL;
226 name = g_strndup (header->name, sizeof (header->name));
227 length = unpack_octal (tar, header->size, sizeof (header->size));
228 offset = gsf_input_tell (tar->source);
231 g_printerr ("[%s]: %d\n", name, (int)length);
234 switch (header->typeflag) {
238 const char *n = name, *s;
241 /* This is deliberately slash-only. */
242 while ((s = strchr (n, '/')))
244 c.name = g_strdup (n);
248 dir = tar_directory_for_file (tar, name, FALSE);
249 g_array_append_val (dir->children, c);
254 (void)tar_directory_for_file (tar, name, TRUE);
260 if (pending_longname ||
261 strcmp (name, MAGIC_LONGNAME) != 0) {
262 tar->err = g_error_new (gsf_input_error_id (), 0,
263 "Invalid longname header");
267 n = gsf_input_read (tar->source, length, NULL);
269 tar->err = g_error_new (gsf_input_error_id (), 0,
270 "Failed to read longname");
274 pending_longname = g_strndup (n, length);
278 /* Other -- ignore */
284 /* Round up to block size */
285 length = (length + (BLOCK_SIZE - 1)) / BLOCK_SIZE * BLOCK_SIZE;
288 gsf_input_seek (tar->source, offset + length, G_SEEK_SET)) {
289 tar->err = g_error_new (gsf_input_error_id (), 0,
295 if (pending_longname) {
297 tar->err = g_error_new (gsf_input_error_id (), 0,
298 "Truncated archive");
299 g_free (pending_longname);
303 gsf_input_seek (tar->source, pos0, G_SEEK_SET);
307 /* GsfInput class functions */
310 gsf_infile_tar_dup (GsfInput *src_input, GError **err)
312 GsfInfileTar *res, *src;
315 src = GSF_INFILE_TAR (src_input);
318 *err = g_error_copy (src->err);
322 res = (GsfInfileTar *)g_object_new (GSF_INFILE_TAR_TYPE, NULL);
323 gsf_infile_tar_set_source (res, src->source);
325 for (ui = 0; ui < src->children->len; ui++) {
326 /* This copies the structure. */
327 TarChild c = g_array_index (src->children, TarChild, ui);
328 c.name = g_strdup (c.name);
329 if (c.dir) g_object_ref (c.dir);
330 g_array_append_val (res->children, c);
336 static guint8 const *
337 gsf_infile_tar_read (GsfInput *input, size_t num_bytes, guint8 *buffer)
346 gsf_infile_tar_seek (GsfInput *input, gsf_off_t offset, GSeekType whence)
354 /* GsfInfile class functions */
356 /*****************************************************************************/
359 gsf_infile_tar_child_by_index (GsfInfile *infile, int target, GError **err)
361 GsfInfileTar *tar = GSF_INFILE_TAR (infile);
367 if (target < 0 || (unsigned)target >= tar->children->len)
370 c = &g_array_index (tar->children, TarChild, target);
372 return g_object_ref (c->dir);
374 GsfInput *input = gsf_input_proxy_new_section (tar->source,
377 gsf_input_set_name (input, c->name);
383 gsf_infile_tar_name_by_index (GsfInfile *infile, int target)
385 GsfInfileTar *tar = GSF_INFILE_TAR (infile);
387 if (target < 0 || (unsigned)target >= tar->children->len)
390 return g_array_index (tar->children, TarChild, target).name;
394 gsf_infile_tar_child_by_name (GsfInfile *infile, char const *name, GError **err)
396 GsfInfileTar *tar = GSF_INFILE_TAR (infile);
399 for (ui = 0; ui < tar->children->len; ui++) {
400 const TarChild *c = &g_array_index (tar->children,
403 if (strcmp (name, c->name) == 0)
404 return gsf_infile_tar_child_by_index (infile, ui, err);
411 gsf_infile_tar_num_children (GsfInfile *infile)
413 GsfInfileTar *tar = GSF_INFILE_TAR (infile);
415 return tar->children->len;
419 gsf_infile_tar_finalize (GObject *obj)
421 GsfInfileTar *tar = GSF_INFILE_TAR (obj);
423 if (tar->source != NULL) {
424 g_object_unref (G_OBJECT (tar->source));
430 for (ui = 0; ui < tar->children->len; ui++) {
431 TarChild *c = &g_array_index (tar->children,
436 g_object_unref (c->dir);
438 g_array_free (tar->children, TRUE);
441 g_clear_error (&tar->err);
443 parent_class->finalize (obj);
447 gsf_infile_tar_constructor (GType type,
448 guint n_construct_properties,
449 GObjectConstructParam *construct_params)
451 GsfInfileTar *tar = (GsfInfileTar *)
452 (parent_class->constructor (type,
453 n_construct_properties,
458 return (GObject *)tar;
463 gsf_infile_tar_init (GObject *obj)
465 GsfInfileTar *tar = (GsfInfileTar *)obj;
467 tar->children = g_array_new (FALSE, FALSE, sizeof (TarChild));
472 gsf_infile_tar_get_property (GObject *object,
477 GsfInfileTar *tar = (GsfInfileTar *)object;
479 switch (property_id) {
481 g_value_set_object (value, tar->source);
484 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
490 gsf_infile_tar_set_source (GsfInfileTar *tar, GsfInput *src)
493 src = gsf_input_proxy_new (src);
495 g_object_unref (tar->source);
500 gsf_infile_tar_set_property (GObject *object,
505 GsfInfileTar *tar = (GsfInfileTar *)object;
507 switch (property_id) {
509 gsf_infile_tar_set_source (tar, g_value_get_object (value));
512 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
519 gsf_infile_tar_class_init (GObjectClass *gobject_class)
521 GsfInputClass *input_class = GSF_INPUT_CLASS (gobject_class);
522 GsfInfileClass *infile_class = GSF_INFILE_CLASS (gobject_class);
524 gobject_class->constructor = gsf_infile_tar_constructor;
525 gobject_class->finalize = gsf_infile_tar_finalize;
526 gobject_class->get_property = gsf_infile_tar_get_property;
527 gobject_class->set_property = gsf_infile_tar_set_property;
529 input_class->Dup = gsf_infile_tar_dup;
530 input_class->Read = gsf_infile_tar_read;
531 input_class->Seek = gsf_infile_tar_seek;
532 infile_class->num_children = gsf_infile_tar_num_children;
533 infile_class->name_by_index = gsf_infile_tar_name_by_index;
534 infile_class->child_by_index = gsf_infile_tar_child_by_index;
535 infile_class->child_by_name = gsf_infile_tar_child_by_name;
537 parent_class = g_type_class_peek_parent (gobject_class);
539 g_object_class_install_property
542 g_param_spec_object ("source",
544 "The archive being interpreted.",
548 G_PARAM_CONSTRUCT_ONLY));
551 GSF_CLASS (GsfInfileTar, gsf_infile_tar,
552 gsf_infile_tar_class_init, gsf_infile_tar_init,
556 * gsf_infile_tar_new :
557 * @source: A base #GsfInput
558 * @err: A #GError, optionally %null
560 * Opens the root directory of a Tar file.
561 * <note>This adds a reference to @source.</note>
563 * Returns: the new tar file handler
566 gsf_infile_tar_new (GsfInput *source, GError **err)
570 g_return_val_if_fail (GSF_IS_INPUT (source), NULL);
572 tar = g_object_new (GSF_INFILE_TAR_TYPE,
578 *err = g_error_copy (tar->err);
579 g_object_unref (tar);
583 return GSF_INFILE (tar);