1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
5 * Copyright (C) 2002-2004 Jody Goldberg (jody@gnome.org)
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of version 2.1 of the GNU Lesser General Public
9 * License as published by the Free Software Foundation.
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 Lesser 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
22 /* Lots of useful information in
23 * http://www.aafassociation.org/html/specs/aafcontainerspec-v1.0.1.pdf
26 #include <gsf-config.h>
27 #include <gsf/gsf-infile-impl.h>
28 #include <gsf/gsf-infile-msole.h>
29 #include <gsf/gsf-impl-utils.h>
30 #include <gsf/gsf-utils.h>
31 #include <gsf/gsf-msole-impl.h>
32 #include <gsf/gsf-input-proxy.h>
38 #define G_LOG_DOMAIN "libgsf:msole"
40 static GObjectClass *parent_class;
54 gboolean is_directory;
56 unsigned char clsid[16]; /* 16 byte GUID used by some apps */
67 guint32 threshold; /* transition between small and big blocks */
68 guint32 sbat_start, num_sbat;
70 MSOleDirent *root_dir;
76 struct _GsfInfileMSOle {
92 GsfInfileClass parent_class;
93 } GsfInfileMSOleClass;
95 #define GSF_INFILE_MSOLE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GSF_INFILE_MSOLE_TYPE, GsfInfileMSOleClass))
96 #define GSF_IS_INFILE_MSOLE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GSF_INFILE_MSOLE_TYPE))
99 #define OLE_BIG_BLOCK(index, ole) ((index) >> ole->info->bb.shift)
101 static GsfInput *gsf_infile_msole_new_child (GsfInfileMSOle *parent,
102 MSOleDirent *dirent, GError **err);
103 static void ole_info_unref (MSOleInfo *info);
108 * @block: block number
109 * @buffer: optionally NULL
111 * Read a block of data from the underlying input.
114 * Returns: pointer to the buffer or NULL if there is an error or 0 bytes are
117 static guint8 const *
118 ole_get_block (GsfInfileMSOle const *ole, guint32 block, guint8 *buffer)
120 g_return_val_if_fail (block < ole->info->max_block, NULL);
122 /* OLE_HEADER_SIZE is fixed at 512, but the sector containing the
123 * header is padded out to bb.size (sector size) when bb.size > 512. */
124 if (gsf_input_seek (ole->input,
125 (gsf_off_t)(MAX (OLE_HEADER_SIZE, ole->info->bb.size) + (block << ole->info->bb.shift)),
129 return gsf_input_read (ole->input, ole->info->bb.size, buffer);
134 * @metabat: a meta bat to connect to the raw blocks (small or large)
135 * @size_guess: An optional guess as to how many blocks are in the file
136 * @block: The first block in the list.
137 * @res: where to store the result.
139 * Walk the linked list of the supplied block allocation table and build up a
140 * table for the list starting in @block.
142 * Returns: TRUE on error.
145 ole_make_bat (MSOleBAT const *metabat, size_t size_guess, guint32 block,
148 /* NOTE : Only use size as a suggestion, sometimes it is wrong */
149 GArray *bat = g_array_sized_new (FALSE, FALSE,
150 sizeof (guint32), size_guess);
152 guint8 *used = (guint8*)g_alloca (1 + metabat->num_blocks / 8);
153 memset (used, 0, 1 + metabat->num_blocks / 8);
155 if (block < metabat->num_blocks)
157 /* Catch cycles in the bat list */
158 if (used[block/8] & (1 << (block & 0x7)))
160 used[block/8] |= 1 << (block & 0x7);
162 g_array_append_val (bat, block);
163 block = metabat->block [block];
164 } while (block < metabat->num_blocks);
168 res->num_blocks = bat->len;
169 res->block = (guint32 *) (gpointer) g_array_free (bat, FALSE);
171 if (block != BAT_MAGIC_END_OF_CHAIN) {
172 g_warning ("This OLE2 file is invalid.\n"
173 "The Block Allocation Table for one of the streams had %x instead of a terminator (%x).\n"
174 "We might still be able to extract some data, but you'll want to check the file.",
175 block, BAT_MAGIC_END_OF_CHAIN);
182 ols_bat_release (MSOleBAT *bat)
184 if (bat->block != NULL) {
191 /* A small utility routine to read a set of references to bat blocks
192 * either from the OLE header, or a meta-bat block.
193 * Returns: a pointer to the element after the last position filled. */
195 ole_info_read_metabat (GsfInfileMSOle *ole, guint32 *bats, guint32 max_bat,
196 guint32 const *metabat, guint32 const *metabat_end)
198 guint8 const *bat, *end;
200 for (; metabat < metabat_end; metabat++) {
201 if (*metabat != BAT_MAGIC_UNUSED) {
202 bat = ole_get_block (ole, *metabat, NULL);
205 end = bat + ole->info->bb.size;
206 for ( ; bat < end ; bat += BAT_INDEX_SIZE, bats++) {
207 *bats = GSF_LE_GET_GUINT32 (bat);
208 if (*bats >= max_bat && *bats < BAT_MAGIC_METABAT) {
209 g_warning ("Invalid metabat item %08x",
215 /* Looks like something in the wild sometimes creates
216 * 'unused' entries in the metabat. Let's assume that
217 * corresponds to lots of unused blocks
218 * http://bugzilla.gnome.org/show_bug.cgi?id=336858 */
219 unsigned i = ole->info->bb.size / BAT_INDEX_SIZE;
221 *bats++ = BAT_MAGIC_UNUSED;
227 /* Copy some some raw data into an array of guint32. */
229 gsf_ole_get_guint32s (guint32 *dst, guint8 const *src, int num_bytes)
231 for (; (num_bytes -= BAT_INDEX_SIZE) >= 0 ; src += BAT_INDEX_SIZE)
232 *dst++ = GSF_LE_GET_GUINT32 (src);
236 ole_info_get_sb_file (GsfInfileMSOle *parent)
240 if (parent->info->sb_file != NULL)
241 return parent->info->sb_file;
243 parent->info->sb_file = gsf_infile_msole_new_child (parent,
244 parent->info->root_dir, NULL);
245 if (!parent->info->sb_file)
248 /* avoid creating a circular reference */
249 ole_info_unref (((GsfInfileMSOle *)parent->info->sb_file)->info);
251 g_return_val_if_fail (parent->info->sb.bat.block == NULL, NULL);
253 if (ole_make_bat (&parent->info->bb.bat,
254 parent->info->num_sbat, parent->info->sbat_start, &meta_sbat))
257 parent->info->sb.bat.num_blocks = meta_sbat.num_blocks * (parent->info->bb.size / BAT_INDEX_SIZE);
258 parent->info->sb.bat.block = g_new0 (guint32, parent->info->sb.bat.num_blocks);
259 ole_info_read_metabat (parent, parent->info->sb.bat.block,
260 parent->info->sb.bat.num_blocks,
261 meta_sbat.block, meta_sbat.block + meta_sbat.num_blocks);
262 ols_bat_release (&meta_sbat);
264 return parent->info->sb_file;
268 ole_dirent_cmp (MSOleDirent const *a, MSOleDirent const *b)
270 g_return_val_if_fail (a, 0);
271 g_return_val_if_fail (b, 0);
273 g_return_val_if_fail (a->collation_name, 0);
274 g_return_val_if_fail (b->collation_name, 0);
276 return strcmp (b->collation_name, a->collation_name);
279 /* Parse dirent number @entry and recursively handle its siblings and children.
280 * parent is optional. */
282 ole_dirent_new (GsfInfileMSOle *ole, guint32 entry, MSOleDirent *parent,
286 guint32 block, next, prev, child, size;
291 if (entry >= DIRENT_MAGIC_END)
294 g_return_val_if_fail (entry <= G_MAXUINT / DIRENT_SIZE, NULL);
296 block = OLE_BIG_BLOCK (entry * DIRENT_SIZE, ole);
297 g_return_val_if_fail (block < ole->bat.num_blocks, NULL);
299 g_return_val_if_fail (!seen_before[entry], NULL);
300 seen_before[entry] = TRUE;
302 data = ole_get_block (ole, ole->bat.block [block], NULL);
305 data += (DIRENT_SIZE * entry) % ole->info->bb.size;
307 type = GSF_LE_GET_GUINT8 (data + DIRENT_TYPE);
308 if (type != DIRENT_TYPE_DIR &&
309 type != DIRENT_TYPE_FILE &&
310 type != DIRENT_TYPE_ROOTDIR) {
311 g_warning ("Unknown stream type 0x%x", type);
314 if (!parent && type != DIRENT_TYPE_ROOTDIR) {
315 /* See bug 346118. */
316 g_warning ("Root directory is not marked as such.");
317 type = DIRENT_TYPE_ROOTDIR;
320 /* It looks like directory (and root directory) sizes are sometimes bogus */
321 size = GSF_LE_GET_GUINT32 (data + DIRENT_FILE_SIZE);
322 g_return_val_if_fail (type == DIRENT_TYPE_DIR || type == DIRENT_TYPE_ROOTDIR ||
323 size <= (guint32)ole->input->size, NULL);
325 dirent = g_new0 (MSOleDirent, 1);
326 dirent->index = entry;
328 /* Store the class id which is 16 byte identifier used by some apps */
329 memcpy(dirent->clsid, data + DIRENT_CLSID, sizeof(dirent->clsid));
331 /* root dir is always big block */
332 dirent->use_sb = parent && (size < ole->info->threshold);
333 dirent->first_block = (GSF_LE_GET_GUINT32 (data + DIRENT_FIRSTBLOCK));
334 dirent->is_directory = (type != DIRENT_TYPE_FILE);
335 dirent->children = NULL;
336 prev = GSF_LE_GET_GUINT32 (data + DIRENT_PREV);
337 next = GSF_LE_GET_GUINT32 (data + DIRENT_NEXT);
338 child = GSF_LE_GET_GUINT32 (data + DIRENT_CHILD);
339 name_len = GSF_LE_GET_GUINT16 (data + DIRENT_NAME_LEN);
341 if (0 < name_len && name_len <= DIRENT_MAX_NAME_SIZE) {
342 gunichar2 uni_name [DIRENT_MAX_NAME_SIZE+1];
347 * Sometimes, rarely, people store the stream name as ascii
348 * rather than utf16. Do a validation first just in case.
350 if (!g_utf8_validate (data, -1, &end) ||
351 ((guint8 const *)end - data + 1) != name_len) {
352 /* be wary about endianness */
353 for (i = 0 ; i < name_len ; i += 2)
354 uni_name [i/2] = GSF_LE_GET_GUINT16 (data + i);
357 dirent->name = g_utf16_to_utf8 (uni_name, -1, NULL, NULL, NULL);
359 dirent->name = g_strndup ((gchar *)data, (gsize)((guint8 const *)end - data + 1));
361 /* be really anal in the face of screwups */
362 if (dirent->name == NULL)
363 dirent->name = g_strdup ("");
364 dirent->collation_name = g_utf8_collate_key (dirent->name, -1);
367 printf ("%c '%s' :\tsize = %d\tfirst_block = 0x%x\n",
368 dirent->is_directory ? 'd' : ' ',
369 dirent->name, dirent->size, dirent->first_block);
373 parent->children = g_list_insert_sorted (parent->children,
374 dirent, (GCompareFunc)ole_dirent_cmp);
376 /* NOTE : These links are a tree, not a linked list */
377 ole_dirent_new (ole, prev, parent, seen_before);
378 ole_dirent_new (ole, next, parent, seen_before);
380 if (dirent->is_directory)
381 ole_dirent_new (ole, child, dirent, seen_before);
382 else if (child != DIRENT_MAGIC_END)
383 g_warning ("A non directory stream with children ?");
389 ole_dirent_free (MSOleDirent *dirent)
392 g_return_if_fail (dirent != NULL);
394 g_free (dirent->name);
395 g_free (dirent->collation_name);
397 for (tmp = dirent->children; tmp; tmp = tmp->next)
398 ole_dirent_free ((MSOleDirent *)tmp->data);
399 g_list_free (dirent->children);
403 /*****************************************************************************/
406 ole_info_unref (MSOleInfo *info)
408 if (info->ref_count-- != 1)
411 ols_bat_release (&info->bb.bat);
412 ols_bat_release (&info->sb.bat);
413 if (info->root_dir != NULL) {
414 ole_dirent_free (info->root_dir);
415 info->root_dir = NULL;
417 if (info->sb_file != NULL) {
418 g_object_unref (G_OBJECT (info->sb_file));
419 info->sb_file = NULL;
425 ole_info_ref (MSOleInfo *info)
431 /* Utility routine to _partially_ replicate a file. It does NOT copy the bat
432 * blocks, or init the dirent. */
433 static GsfInfileMSOle *
434 ole_dup (GsfInfileMSOle const *src, GError **err)
439 g_return_val_if_fail (src != NULL, NULL);
441 input = gsf_input_dup (src->input, err);
444 *err = g_error_new (gsf_input_error_id (), 0,
445 "Failed to duplicate input stream");
449 dst = (GsfInfileMSOle *)g_object_new (GSF_INFILE_MSOLE_TYPE, NULL);
450 if (G_UNLIKELY (NULL == dst)) return NULL;
453 dst->info = ole_info_ref (src->info);
454 /* buf and buf_size are initialized to NULL */
459 /* Read an OLE header and do some sanity checking
461 * Returns: %TRUE on error setting @err if it is supplied. */
463 ole_init_info (GsfInfileMSOle *ole, GError **err)
465 static guint8 const signature[] =
466 { 0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1 };
468 guint8 const *header, *tmp;
469 guint32 *metabat = NULL;
471 guint32 bb_shift, sb_shift, num_bat, num_metabat, last, dirent_start;
472 guint32 metabat_block, *ptr;
475 /* check the header */
476 if (gsf_input_seek (ole->input, 0, G_SEEK_SET) ||
477 NULL == (header = gsf_input_read (ole->input, OLE_HEADER_SIZE, NULL)) ||
478 0 != memcmp (header, signature, sizeof (signature))) {
480 *err = g_error_new (gsf_input_error_id (), 0,
481 "No OLE2 signature");
485 bb_shift = GSF_LE_GET_GUINT16 (header + OLE_HEADER_BB_SHIFT);
486 sb_shift = GSF_LE_GET_GUINT16 (header + OLE_HEADER_SB_SHIFT);
487 num_bat = GSF_LE_GET_GUINT32 (header + OLE_HEADER_NUM_BAT);
488 dirent_start = GSF_LE_GET_GUINT32 (header + OLE_HEADER_DIRENT_START);
489 metabat_block = GSF_LE_GET_GUINT32 (header + OLE_HEADER_METABAT_BLOCK);
490 num_metabat = GSF_LE_GET_GUINT32 (header + OLE_HEADER_NUM_METABAT);
492 /* Some sanity checks
493 * 1) There should always be at least 1 BAT block
494 * 2) It makes no sense to have a block larger than 2^31 for now.
495 * Maybe relax this later, but not much.
497 if (6 > bb_shift || bb_shift >= 31 || sb_shift > bb_shift ||
498 (gsf_input_size (ole->input) >> bb_shift) < 1) {
500 *err = g_error_new (gsf_input_error_id (), 0,
501 "Unreasonable block sizes");
505 info = g_new0 (MSOleInfo, 1);
509 info->bb.shift = bb_shift;
510 info->bb.size = 1 << info->bb.shift;
511 info->bb.filter = info->bb.size - 1;
512 info->sb.shift = sb_shift;
513 info->sb.size = 1 << info->sb.shift;
514 info->sb.filter = info->sb.size - 1;
515 info->threshold = GSF_LE_GET_GUINT32 (header + OLE_HEADER_THRESHOLD);
516 info->sbat_start = GSF_LE_GET_GUINT32 (header + OLE_HEADER_SBAT_START);
517 info->num_sbat = GSF_LE_GET_GUINT32 (header + OLE_HEADER_NUM_SBAT);
518 info->max_block = (gsf_input_size (ole->input) - OLE_HEADER_SIZE) / info->bb.size;
519 info->sb_file = NULL;
521 if (info->num_sbat == 0 &&
522 info->sbat_start != BAT_MAGIC_END_OF_CHAIN &&
523 info->sbat_start != BAT_MAGIC_UNUSED) {
524 g_warning ("There are not supposed to be any blocks in the small block allocation table, yet there is a link to some. Ignoring it.");
527 /* very rough heuristic, just in case */
528 if (num_bat < info->max_block && info->num_sbat < info->max_block) {
529 info->bb.bat.num_blocks = num_bat * (info->bb.size / BAT_INDEX_SIZE);
530 info->bb.bat.block = g_new0 (guint32, info->bb.bat.num_blocks);
532 metabat = g_try_new (guint32, MAX (info->bb.size, OLE_HEADER_SIZE));
536 *err = g_error_new (gsf_input_error_id (), 0,
537 "Insufficient memory");
541 /* Reading the elements invalidates this memory, make copy */
542 gsf_ole_get_guint32s (metabat, header + OLE_HEADER_START_BAT,
543 OLE_HEADER_SIZE - OLE_HEADER_START_BAT);
545 if (last > OLE_HEADER_METABAT_SIZE)
546 last = OLE_HEADER_METABAT_SIZE;
548 ptr = ole_info_read_metabat (ole, info->bb.bat.block,
549 info->bb.bat.num_blocks, metabat, metabat + last);
554 last = (info->bb.size - BAT_INDEX_SIZE) / BAT_INDEX_SIZE;
555 while (ptr != NULL && num_metabat-- > 0) {
556 tmp = ole_get_block (ole, metabat_block, NULL);
562 /* Reading the elements invalidates this memory, make copy */
563 gsf_ole_get_guint32s (metabat, tmp, (int)info->bb.size);
565 if (num_metabat == 0) {
566 if (last < num_bat) {
567 /* there should be less that a full metabat block
573 } else if (num_metabat > 0) {
574 metabat_block = metabat[last];
575 if (num_bat < last) {
576 /* ::num_bat and ::num_metabat are
577 * inconsistent. There are too many metabats
578 * for the bat count in the header. */
585 ptr = ole_info_read_metabat (ole, ptr,
586 info->bb.bat.num_blocks, metabat, metabat + last);
588 fail = (ptr == NULL);
591 metabat = ptr = NULL;
595 *err = g_error_new (gsf_input_error_id (), 0,
596 "Inconsistent block allocation table");
600 /* Read the directory's bat, we do not know the size */
601 if (ole_make_bat (&info->bb.bat, 0, dirent_start, &ole->bat)) {
603 *err = g_error_new (gsf_input_error_id (), 0,
604 "Problems making block allocation table");
608 /* Read the directory */
609 seen_before = g_malloc0 ((ole->bat.num_blocks << info->bb.shift) * DIRENT_SIZE + 1);
610 ole->dirent = info->root_dir =
611 ole_dirent_new (ole, 0, NULL, seen_before);
612 g_free (seen_before);
613 if (ole->dirent == NULL) {
615 *err = g_error_new (gsf_input_error_id (), 0,
616 "Problems reading directory");
624 gsf_infile_msole_finalize (GObject *obj)
626 GsfInfileMSOle *ole = GSF_INFILE_MSOLE (obj);
628 if (ole->input != NULL) {
629 g_object_unref (G_OBJECT (ole->input));
632 if (ole->info != NULL &&
633 ole->info->sb_file != (GsfInput *)ole) {
634 ole_info_unref (ole->info);
637 ols_bat_release (&ole->bat);
639 g_free (ole->stream.buf);
641 parent_class->finalize (obj);
645 gsf_infile_msole_dup (GsfInput *src_input, GError **err)
647 GsfInfileMSOle const *src = GSF_INFILE_MSOLE (src_input);
648 GsfInfileMSOle *dst = ole_dup (src, err);
653 if (src->bat.block != NULL) {
654 dst->bat.block = g_new (guint32, src->bat.num_blocks),
655 memcpy (dst->bat.block, src->bat.block,
656 sizeof (guint32) * src->bat.num_blocks);
658 dst->bat.num_blocks = src->bat.num_blocks;
659 dst->dirent = src->dirent;
661 return GSF_INPUT (dst);
664 static guint8 const *
665 gsf_infile_msole_read (GsfInput *input, size_t num_bytes, guint8 *buffer)
667 GsfInfileMSOle *ole = GSF_INFILE_MSOLE (input);
668 gsf_off_t first_block, last_block, raw_block, offset, i;
673 /* small block files are preload */
674 if (ole->dirent != NULL && ole->dirent->use_sb) {
675 if (buffer != NULL) {
676 memcpy (buffer, ole->stream.buf + input->cur_offset, num_bytes);
679 return ole->stream.buf + input->cur_offset;
682 /* GsfInput guarantees that num_bytes > 0 */
683 first_block = OLE_BIG_BLOCK (input->cur_offset, ole);
684 last_block = OLE_BIG_BLOCK (input->cur_offset + num_bytes - 1, ole);
685 offset = input->cur_offset & ole->info->bb.filter;
687 /* optimization : are all the raw blocks contiguous */
689 raw_block = ole->bat.block [i];
690 while (++i <= last_block && ++raw_block == ole->bat.block [i])
692 if (i > last_block) {
693 /* optimization don't seek if we don't need to */
694 if (ole->cur_block != first_block) {
695 if (gsf_input_seek (ole->input,
696 (gsf_off_t)(MAX (OLE_HEADER_SIZE, ole->info->bb.size) + (ole->bat.block [first_block] << ole->info->bb.shift) + offset),
700 ole->cur_block = last_block;
701 return gsf_input_read (ole->input, num_bytes, buffer);
704 /* damn, we need to copy it block by block */
705 if (buffer == NULL) {
706 if (ole->stream.buf_size < num_bytes) {
707 g_free (ole->stream.buf);
708 ole->stream.buf_size = num_bytes;
709 ole->stream.buf = g_new (guint8, num_bytes);
711 buffer = ole->stream.buf;
715 for (i = first_block ; i <= last_block ; i++ , ptr += count, num_bytes -= count) {
716 count = ole->info->bb.size - offset;
717 if (count > num_bytes)
719 data = ole_get_block (ole, ole->bat.block [i], NULL);
723 /* TODO : this could be optimized to avoid the copy */
724 memcpy (ptr, data + offset, count);
727 ole->cur_block = BAT_MAGIC_UNUSED;
733 gsf_infile_msole_seek (GsfInput *input, gsf_off_t offset, GSeekType whence)
735 GsfInfileMSOle *ole = GSF_INFILE_MSOLE (input);
740 ole->cur_block = BAT_MAGIC_UNUSED;
745 gsf_infile_msole_new_child (GsfInfileMSOle *parent,
746 MSOleDirent *dirent, GError **err)
748 GsfInfileMSOle *child;
750 MSOleBAT const *metabat;
751 GsfInput *sb_file = NULL;
754 child = ole_dup (parent, err);
758 child->dirent = dirent;
759 gsf_input_set_size (GSF_INPUT (child), (gsf_off_t) dirent->size);
761 /* The root dirent defines the small block file */
762 if (dirent->index != 0) {
763 gsf_input_set_name (GSF_INPUT (child), dirent->name);
764 gsf_input_set_container (GSF_INPUT (child), GSF_INFILE (parent));
766 if (dirent->is_directory) {
767 /* be wary. It seems as if some implementations pretend that the
768 * directories contain data */
769 gsf_input_set_size (GSF_INPUT (child), 0);
770 return GSF_INPUT (child);
777 if (dirent->use_sb) {
778 metabat = &info->sb.bat;
779 size_guess = dirent->size >> info->sb.shift;
780 sb_file = ole_info_get_sb_file (parent);
783 *err = g_error_new (gsf_input_error_id (), 0,
784 "Failed to access child");
785 g_object_unref (G_OBJECT (child));
789 metabat = &info->bb.bat;
790 size_guess = dirent->size >> info->bb.shift;
792 if (ole_make_bat (metabat, size_guess + 1, dirent->first_block, &child->bat)) {
793 g_object_unref (G_OBJECT (child));
797 if (dirent->use_sb) {
802 g_return_val_if_fail (sb_file != NULL, NULL);
804 child->stream.buf_size = remaining = dirent->size;
805 child->stream.buf = g_new (guint8, child->stream.buf_size);
807 for (i = 0 ; remaining > 0 && i < child->bat.num_blocks; i++, remaining -= info->sb.size)
808 if (gsf_input_seek (GSF_INPUT (sb_file),
809 (gsf_off_t)(child->bat.block [i] << info->sb.shift), G_SEEK_SET) < 0 ||
810 (data = gsf_input_read (GSF_INPUT (sb_file),
811 MIN (remaining, (int)info->sb.size),
812 child->stream.buf + (i << info->sb.shift))) == NULL) {
814 g_warning ("failure reading block %d for '%s'", i, dirent->name);
815 if (err) *err = g_error_new (gsf_input_error_id (), 0, "failure reading block");
816 g_object_unref (G_OBJECT (child));
821 if (err) *err = g_error_new (gsf_input_error_id (), 0, "insufficient blocks");
822 g_warning ("Small-block file '%s' has insufficient blocks (%u) for the stated size (%lu)", dirent->name, child->bat.num_blocks, (long)dirent->size);
823 g_object_unref (G_OBJECT (child));
828 return GSF_INPUT (child);
832 gsf_infile_msole_child_by_index (GsfInfile *infile, int target, GError **err)
834 GsfInfileMSOle *ole = GSF_INFILE_MSOLE (infile);
837 for (p = ole->dirent->children; p != NULL ; p = p->next)
839 return gsf_infile_msole_new_child (ole,
840 (MSOleDirent *)p->data, err);
845 gsf_infile_msole_name_by_index (GsfInfile *infile, int target)
847 GsfInfileMSOle *ole = GSF_INFILE_MSOLE (infile);
850 for (p = ole->dirent->children; p != NULL ; p = p->next)
852 return ((MSOleDirent *)p->data)->name;
857 gsf_infile_msole_child_by_name (GsfInfile *infile, char const *name, GError **err)
859 GsfInfileMSOle *ole = GSF_INFILE_MSOLE (infile);
862 for (p = ole->dirent->children; p != NULL ; p = p->next) {
863 MSOleDirent *dirent = p->data;
864 if (dirent->name != NULL && !strcmp (name, dirent->name))
865 return gsf_infile_msole_new_child (ole, dirent, err);
871 gsf_infile_msole_num_children (GsfInfile *infile)
873 GsfInfileMSOle *ole = GSF_INFILE_MSOLE (infile);
875 g_return_val_if_fail (ole->dirent != NULL, -1);
877 if (!ole->dirent->is_directory)
879 return g_list_length (ole->dirent->children);
883 gsf_infile_msole_init (GObject *obj)
885 GsfInfileMSOle *ole = GSF_INFILE_MSOLE (obj);
889 ole->bat.block = NULL;
890 ole->bat.num_blocks = 0;
891 ole->cur_block = BAT_MAGIC_UNUSED;
892 ole->stream.buf = NULL;
893 ole->stream.buf_size = 0;
897 gsf_infile_msole_class_init (GObjectClass *gobject_class)
899 GsfInputClass *input_class = GSF_INPUT_CLASS (gobject_class);
900 GsfInfileClass *infile_class = GSF_INFILE_CLASS (gobject_class);
902 gobject_class->finalize = gsf_infile_msole_finalize;
903 input_class->Dup = gsf_infile_msole_dup;
904 input_class->Read = gsf_infile_msole_read;
905 input_class->Seek = gsf_infile_msole_seek;
906 infile_class->num_children = gsf_infile_msole_num_children;
907 infile_class->name_by_index = gsf_infile_msole_name_by_index;
908 infile_class->child_by_index = gsf_infile_msole_child_by_index;
909 infile_class->child_by_name = gsf_infile_msole_child_by_name;
911 parent_class = g_type_class_peek_parent (gobject_class);
914 GSF_CLASS (GsfInfileMSOle, gsf_infile_msole,
915 gsf_infile_msole_class_init, gsf_infile_msole_init,
919 * gsf_infile_msole_new :
921 * @err: optional place to store an error
923 * Opens the root directory of an MS OLE file.
924 * <note>This adds a reference to @source.</note>
926 * Returns: the new ole file handler
929 gsf_infile_msole_new (GsfInput *source, GError **err)
932 gsf_off_t calling_pos;
934 g_return_val_if_fail (GSF_IS_INPUT (source), NULL);
936 ole = (GsfInfileMSOle *)g_object_new (GSF_INFILE_MSOLE_TYPE, NULL);
937 if (G_UNLIKELY (NULL == ole)) return NULL;
939 ole->input = gsf_input_proxy_new (source);
940 gsf_input_set_size (GSF_INPUT (ole), 0);
942 calling_pos = gsf_input_tell (source);
943 if (ole_init_info (ole, err)) {
944 /* We do this so other kinds of archives can be tried. */
945 (void)gsf_input_seek (source, calling_pos, G_SEEK_SET);
947 g_object_unref (G_OBJECT (ole));
951 return GSF_INFILE (ole);
955 * gsf_infile_msole_get_class_id :
956 * @ole: a #GsfInfileMSOle
957 * @res: 16 byte identifier (often a GUID in MS Windows apps)
959 * Retrieves the 16 byte indentifier (often a GUID in MS Windows apps)
960 * stored within the directory associated with @ole and stores it in @res.
962 * Returns: TRUE on success
965 gsf_infile_msole_get_class_id (GsfInfileMSOle const *ole, guint8 *res)
967 g_return_val_if_fail (ole != NULL && ole->dirent != NULL, FALSE);
969 memcpy (res, ole->dirent->clsid,
970 sizeof(ole->dirent->clsid));