Merge branch 'master' into 'master'
[brasero.git] / libbrasero-media / burn-iso9660.c
blob053d1bdb9db086f1278985138dc7332a2a2369ef
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2 /*
3 * Libbrasero-media
4 * Copyright (C) Philippe Rouquier 2005-2009 <bonfire-app@wanadoo.fr>
6 * Libbrasero-media 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 * The Libbrasero-media authors hereby grant permission for non-GPL compatible
12 * GStreamer plugins to be used and distributed together with GStreamer
13 * and Libbrasero-media. This permission is above and beyond the permissions granted
14 * by the GPL license by which Libbrasero-media is covered. If you modify this code
15 * you may extend this exception to your version of the code, but you are not
16 * obligated to do so. If you do not wish to do so, delete this exception
17 * statement from your version.
19 * Libbrasero-media is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU Library General Public License for more details.
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, write to:
26 * The Free Software Foundation, Inc.,
27 * 51 Franklin Street, Fifth Floor
28 * Boston, MA 02110-1301, USA.
31 #ifdef HAVE_CONFIG_H
32 # include <config.h>
33 #endif
35 #include <errno.h>
36 #include <string.h>
37 #include <stdio.h>
39 #include <glib.h>
40 #include <glib/gi18n-lib.h>
42 #include "brasero-units.h"
43 #include "brasero-media.h"
44 #include "brasero-media-private.h"
45 #include "burn-iso9660.h"
46 #include "burn-iso-field.h"
47 #include "burn-susp.h"
48 #include "brasero-media.h"
49 #include "burn-volume.h"
51 struct _BraseroIsoCtx {
52 gint num_blocks;
54 gchar buffer [ISO9660_BLOCK_SIZE];
55 gint offset;
56 BraseroVolSrc *vol;
58 gchar *spare_record;
60 guint64 data_blocks;
61 GError *error;
63 guchar susp_skip;
65 guint is_root:1;
66 guint has_susp:1;
67 guint has_RR:1;
69 typedef struct _BraseroIsoCtx BraseroIsoCtx;
71 typedef enum {
72 BRASERO_ISO_FILE_EXISTENCE = 1,
73 BRASERO_ISO_FILE_DIRECTORY = 1 << 1,
74 BRASERO_ISO_FILE_ASSOCIATED = 1 << 2,
75 BRASERO_ISO_FILE_RECORD = 1 << 3,
76 BRASERO_ISO_FILE_PROTECTION = 1 << 4,
77 /* Reserved */
78 BRASERO_ISO_FILE_MULTI_EXTENT_FINAL = 1 << 7
79 } BraseroIsoFileFlag;
81 struct _BraseroIsoDirRec {
82 guchar record_size;
83 guchar x_attr_size;
84 guchar address [8];
85 guchar file_size [8];
86 guchar date_time [7];
87 guchar flags;
88 guchar file_unit;
89 guchar gap_size;
90 guchar volseq_num [4];
91 guchar id_size;
92 gchar id [0];
94 typedef struct _BraseroIsoDirRec BraseroIsoDirRec;
96 struct _BraseroIsoPrimary {
97 guchar type;
98 gchar id [5];
99 guchar version;
101 guchar unused_0;
103 gchar system_id [32];
104 gchar vol_id [32];
106 guchar unused_1 [8];
108 guchar vol_size [8];
110 guchar escapes [32];
111 guchar volset_size [4];
112 guchar sequence_num [4];
113 guchar block_size [4];
114 guchar path_table_size [8];
115 guchar L_table_loc [4];
116 guchar opt_L_table_loc [4];
117 guchar M_table_loc [4];
118 guchar opt_M_table_loc [4];
120 /* the following has a fixed size of 34 bytes */
121 BraseroIsoDirRec root_rec [0];
123 /* to be continued if needed */
125 typedef struct _BraseroIsoPrimary BraseroIsoPrimary;
127 typedef enum {
128 BRASERO_ISO_OK,
129 BRASERO_ISO_END,
130 BRASERO_ISO_ERROR
131 } BraseroIsoResult;
133 #define ISO9660_BYTES_TO_BLOCKS(size) BRASERO_BYTES_TO_SECTORS ((size), ISO9660_BLOCK_SIZE)
135 static GList *
136 brasero_iso9660_load_directory_records (BraseroIsoCtx *ctx,
137 BraseroVolFile *parent,
138 BraseroIsoDirRec *record,
139 gboolean recursive);
141 static BraseroVolFile *
142 brasero_iso9660_lookup_directory_records (BraseroIsoCtx *ctx,
143 const gchar *path,
144 gint address);
146 gboolean
147 brasero_iso9660_is_primary_descriptor (const char *buffer,
148 GError **error)
150 BraseroVolDesc *vol;
152 /* must be CD001 */
153 vol = (BraseroVolDesc *) buffer;
154 if (memcmp (vol->id, "CD001", 5)) {
155 g_set_error (error,
156 BRASERO_MEDIA_ERROR,
157 BRASERO_MEDIA_ERROR_IMAGE_INVALID,
158 _("It does not appear to be a valid ISO image"));
159 return FALSE;
162 /* must be "1" for primary volume */
163 if (vol->type != 1) {
164 g_set_error (error,
165 BRASERO_MEDIA_ERROR,
166 BRASERO_MEDIA_ERROR_IMAGE_INVALID,
167 _("It does not appear to be a valid ISO image"));
168 return FALSE;
171 return TRUE;
174 gboolean
175 brasero_iso9660_get_size (const gchar *block,
176 gint64 *nb_blocks,
177 GError **error)
179 BraseroIsoPrimary *vol;
181 /* read the size of the volume */
182 vol = (BraseroIsoPrimary *) block;
183 *nb_blocks = (gint64) brasero_iso9660_get_733_val (vol->vol_size);
185 return TRUE;
188 gboolean
189 brasero_iso9660_get_label (const gchar *block,
190 gchar **label,
191 GError **error)
193 BraseroIsoPrimary *vol;
195 /* read the identifier */
196 vol = (BraseroIsoPrimary *) block;
197 *label = g_strndup (vol->vol_id, sizeof (vol->vol_id));
199 return TRUE;
202 static BraseroIsoResult
203 brasero_iso9660_seek (BraseroIsoCtx *ctx, gint address)
205 ctx->offset = 0;
206 ctx->num_blocks = 1;
208 /* The size of all the records is given by size member and its location
209 * by its address member. In a set of directory records the first two
210 * records are: '.' (id == 0) and '..' (id == 1). So since we've got
211 * the address of the set load the block. */
212 if (BRASERO_VOL_SRC_SEEK (ctx->vol, address, SEEK_SET, &(ctx->error)) == -1)
213 return BRASERO_ISO_ERROR;
215 if (!BRASERO_VOL_SRC_READ (ctx->vol, ctx->buffer, 1, &(ctx->error)))
216 return BRASERO_ISO_ERROR;
218 return BRASERO_ISO_OK;
221 static BraseroIsoResult
222 brasero_iso9660_next_block (BraseroIsoCtx *ctx)
224 ctx->offset = 0;
225 ctx->num_blocks ++;
227 if (!BRASERO_VOL_SRC_READ (ctx->vol, ctx->buffer, 1, &(ctx->error)))
228 return BRASERO_ISO_ERROR;
230 return BRASERO_ISO_OK;
233 static gboolean
234 brasero_iso9660_read_susp (BraseroIsoCtx *ctx,
235 BraseroSuspCtx *susp_ctx,
236 gchar *susp,
237 gint susp_len)
239 gboolean result = TRUE;
240 guint64 current_position = -1;
242 memset (susp_ctx, 0, sizeof (BraseroSuspCtx));
243 if (!brasero_susp_read (susp_ctx, susp, susp_len)) {
244 BRASERO_MEDIA_LOG ("Could not read susp area");
245 return FALSE;
248 while (susp_ctx->CE_address) {
249 gchar CE_block [ISO9660_BLOCK_SIZE];
250 gint64 seek_res;
251 guint32 offset;
252 guint32 len;
254 BRASERO_MEDIA_LOG ("Continuation Area");
256 /* we need to move to another block */
257 seek_res = BRASERO_VOL_SRC_SEEK (ctx->vol, susp_ctx->CE_address, SEEK_SET, NULL);
258 if (seek_res == -1) {
259 BRASERO_MEDIA_LOG ("Could not seek to continuation area");
260 result = FALSE;
261 break;
264 if (current_position == -1)
265 current_position = seek_res;
267 if (!BRASERO_VOL_SRC_READ (ctx->vol, CE_block, 1, NULL)) {
268 BRASERO_MEDIA_LOG ("Could not get continuation area");
269 result = FALSE;
270 break;
273 offset = susp_ctx->CE_offset;
274 len = MIN (susp_ctx->CE_len, sizeof (CE_block) - offset);
276 /* reset information about the CE area */
277 memset (&susp_ctx->CE_address, 0, sizeof (susp_ctx->CE_address));
278 memset (&susp_ctx->CE_offset, 0, sizeof (susp_ctx->CE_offset));
279 memset (&susp_ctx->CE_len, 0, sizeof (susp_ctx->CE_len));
281 /* read all information contained in the CE area */
282 if (!brasero_susp_read (susp_ctx, CE_block + offset, len)) {
283 BRASERO_MEDIA_LOG ("Could not read continuation area");
284 result = FALSE;
285 break;
289 /* reset the reading address properly */
290 if (current_position != -1
291 && BRASERO_VOL_SRC_SEEK (ctx->vol, current_position, SEEK_SET, NULL) == -1) {
292 BRASERO_MEDIA_LOG ("Could not rewind to previous position");
293 result = FALSE;
296 return result;
299 static gchar *
300 brasero_iso9660_get_susp (BraseroIsoCtx *ctx,
301 BraseroIsoDirRec *record,
302 guint *susp_len)
304 gchar *susp_block;
305 gint start;
306 gint len;
308 start = sizeof (BraseroIsoDirRec) + record->id_size;
309 /* padding byte when id_size is an even number */
310 if (start & 1)
311 start ++;
313 if (ctx->susp_skip)
314 start += ctx->susp_skip;
316 /* we don't want to go beyond end of buffer */
317 if (start >= record->record_size)
318 return NULL;
320 len = record->record_size - start;
322 if (len <= 0)
323 return NULL;
325 if (susp_len)
326 *susp_len = len;
328 susp_block = ((gchar *) record) + start;
330 BRASERO_MEDIA_LOG ("Got susp block");
331 return susp_block;
334 static BraseroIsoResult
335 brasero_iso9660_next_record (BraseroIsoCtx *ctx, BraseroIsoDirRec **retval)
337 BraseroIsoDirRec *record;
339 if (ctx->offset > sizeof (ctx->buffer)) {
340 BRASERO_MEDIA_LOG ("Invalid record size");
341 goto error;
344 if (ctx->offset == sizeof (ctx->buffer)) {
345 BRASERO_MEDIA_LOG ("No next record");
346 return BRASERO_ISO_END;
349 /* record_size already checked last time function was called */
350 record = (BraseroIsoDirRec *) (ctx->buffer + ctx->offset);
351 if (!record->record_size) {
352 BRASERO_MEDIA_LOG ("Last record");
353 return BRASERO_ISO_END;
356 if (record->record_size > (sizeof (ctx->buffer) - ctx->offset)) {
357 gint part_one, part_two;
359 /* This is for cross sector boundary records */
360 BRASERO_MEDIA_LOG ("Cross sector boundary record");
362 /* some implementations write across block boundary which is
363 * "forbidden" by ECMA-119. But linux kernel accepts it, so ...
365 /* ctx->error = g_error_new (BRASERO_MEDIA_ERROR,
366 BRASERO_MEDIA_ERROR_IMAGE_INVALID,
367 _("It does not appear to be a valid ISO image"));
368 goto error;
370 if (ctx->spare_record)
371 g_free (ctx->spare_record);
373 ctx->spare_record = g_new0 (gchar, record->record_size);
375 part_one = sizeof (ctx->buffer) - ctx->offset;
376 part_two = record->record_size - part_one;
378 memcpy (ctx->spare_record,
379 ctx->buffer + ctx->offset,
380 part_one);
382 if (brasero_iso9660_next_block (ctx) == BRASERO_ISO_ERROR)
383 goto error;
385 memcpy (ctx->spare_record + part_one,
386 ctx->buffer,
387 part_two);
388 ctx->offset = part_two;
390 record = (BraseroIsoDirRec *) ctx->spare_record;
392 else
393 ctx->offset += record->record_size;
395 *retval = record;
396 return BRASERO_ISO_OK;
398 error:
399 if (!ctx->error)
400 ctx->error = g_error_new (BRASERO_MEDIA_ERROR,
401 BRASERO_MEDIA_ERROR_IMAGE_INVALID,
402 _("It does not appear to be a valid ISO image"));
403 return BRASERO_ISO_ERROR;
406 static BraseroIsoResult
407 brasero_iso9660_get_first_directory_record (BraseroIsoCtx *ctx,
408 BraseroIsoDirRec **record,
409 gint address)
411 BraseroIsoResult result;
413 BRASERO_MEDIA_LOG ("Reading directory record");
415 result = brasero_iso9660_seek (ctx, address);
416 if (result != BRASERO_ISO_OK)
417 return BRASERO_ISO_ERROR;
419 /* load "." */
420 result = brasero_iso9660_next_record (ctx, record);
421 if (result != BRASERO_ISO_OK)
422 return BRASERO_ISO_ERROR;
424 return BRASERO_ISO_OK;
427 static BraseroVolFile *
428 brasero_iso9660_read_file_record (BraseroIsoCtx *ctx,
429 BraseroIsoDirRec *record,
430 BraseroSuspCtx *susp_ctx)
432 BraseroVolFile *file;
433 BraseroVolFileExtent *extent;
435 if (record->id_size > record->record_size - sizeof (BraseroIsoDirRec)) {
436 BRASERO_MEDIA_LOG ("Filename is too long");
437 ctx->error = g_error_new (BRASERO_MEDIA_ERROR,
438 BRASERO_MEDIA_ERROR_IMAGE_INVALID,
439 _("It does not appear to be a valid ISO image"));
440 return NULL;
443 file = g_new0 (BraseroVolFile, 1);
444 file->isdir = 0;
445 file->name = g_new0 (gchar, record->id_size + 1);
446 memcpy (file->name, record->id, record->id_size);
448 file->specific.file.size_bytes = brasero_iso9660_get_733_val (record->file_size);
450 /* NOTE: a file can be in multiple places */
451 extent = g_new (BraseroVolFileExtent, 1);
452 extent->block = brasero_iso9660_get_733_val (record->address);
453 extent->size = brasero_iso9660_get_733_val (record->file_size);
454 file->specific.file.extents = g_slist_prepend (file->specific.file.extents, extent);
456 /* see if we've got a susp area */
457 if (!susp_ctx) {
458 BRASERO_MEDIA_LOG ("New file %s", file->name);
459 return file;
462 BRASERO_MEDIA_LOG ("New file %s with a suspend area", file->name);
464 if (susp_ctx->rr_name) {
465 BRASERO_MEDIA_LOG ("Got a susp (RR) %s", susp_ctx->rr_name);
466 file->rr_name = susp_ctx->rr_name;
467 susp_ctx->rr_name = NULL;
470 return file;
473 static BraseroVolFile *
474 brasero_iso9660_read_directory_record (BraseroIsoCtx *ctx,
475 BraseroIsoDirRec *record,
476 gboolean recursive)
478 gchar *susp;
479 gint address;
480 guint susp_len = 0;
481 BraseroSuspCtx susp_ctx;
482 BraseroVolFile *directory;
484 if (record->id_size > record->record_size - sizeof (BraseroIsoDirRec)) {
485 BRASERO_MEDIA_LOG ("Filename is too long");
486 ctx->error = g_error_new (BRASERO_MEDIA_ERROR,
487 BRASERO_MEDIA_ERROR_IMAGE_INVALID,
488 _("It does not appear to be a valid ISO image"));
489 return NULL;
492 /* create the directory and set information */
493 directory = g_new0 (BraseroVolFile, 1);
494 directory->isdir = TRUE;
495 directory->isdir_loaded = FALSE;
496 directory->name = g_new0 (gchar, record->id_size + 1);
497 memcpy (directory->name, record->id, record->id_size);
499 if (ctx->has_susp && ctx->has_RR) {
500 /* See if we've got a susp area. Do it now to see if it has a CL
501 * entry. The rest will be checked later after reading contents.
503 susp = brasero_iso9660_get_susp (ctx, record, &susp_len);
504 if (!brasero_iso9660_read_susp (ctx, &susp_ctx, susp, susp_len)) {
505 BRASERO_MEDIA_LOG ("Could not read susp area");
506 brasero_volume_file_free (directory);
507 return NULL;
510 /* look for a "CL" SUSP entry in case the directory was relocated */
511 if (susp_ctx.CL_address) {
512 BRASERO_MEDIA_LOG ("Entry has a CL entry");
513 address = susp_ctx.CL_address;
515 else
516 address = brasero_iso9660_get_733_val (record->address);
518 BRASERO_MEDIA_LOG ("New directory %s with susp area", directory->name);
520 /* if this directory has a "RE" susp entry then drop it; it's
521 * not at the right place in the Rock Ridge file hierarchy. It
522 * will probably be skipped */
523 if (susp_ctx.has_RE) {
524 BRASERO_MEDIA_LOG ("Rock Ridge relocated directory. Skipping entry.");
525 directory->relocated = TRUE;
528 if (susp_ctx.rr_name) {
529 BRASERO_MEDIA_LOG ("Got a susp (RR) %s", susp_ctx.rr_name);
530 directory->rr_name = susp_ctx.rr_name;
531 susp_ctx.rr_name = NULL;
534 brasero_susp_ctx_clean (&susp_ctx);
536 else
537 address = brasero_iso9660_get_733_val (record->address);
539 /* load contents if recursive */
540 if (recursive) {
541 GList *children;
543 brasero_iso9660_get_first_directory_record (ctx,
544 &record,
545 address);
546 children = brasero_iso9660_load_directory_records (ctx,
547 directory,
548 record,
549 TRUE);
550 if (!children && ctx->error) {
551 brasero_volume_file_free (directory);
552 if (ctx->has_susp && ctx->has_RR)
553 brasero_susp_ctx_clean (&susp_ctx);
555 return NULL;
558 directory->isdir_loaded = TRUE;
559 directory->specific.dir.children = children;
561 else /* store the address of contents for later use */
562 directory->specific.dir.address = address;
564 BRASERO_MEDIA_LOG ("New directory %s", directory->name);
565 return directory;
568 static GList *
569 brasero_iso9660_load_directory_records (BraseroIsoCtx *ctx,
570 BraseroVolFile *parent,
571 BraseroIsoDirRec *record,
572 gboolean recursive)
574 GSList *iter;
575 gint max_block;
576 gint max_record_size;
577 BraseroVolFile *entry;
578 GList *children = NULL;
579 BraseroIsoResult result;
580 GSList *directories = NULL;
582 max_record_size = brasero_iso9660_get_733_val (record->file_size);
583 max_block = ISO9660_BYTES_TO_BLOCKS (max_record_size);
584 BRASERO_MEDIA_LOG ("Maximum directory record length %i block (= %i bytes)", max_block, max_record_size);
586 /* skip ".." */
587 result = brasero_iso9660_next_record (ctx, &record);
588 if (result != BRASERO_ISO_OK)
589 return NULL;
591 BRASERO_MEDIA_LOG ("Skipped '.' and '..'");
593 while (1) {
594 BraseroIsoResult result;
596 result = brasero_iso9660_next_record (ctx, &record);
597 if (result == BRASERO_ISO_END) {
598 if (ctx->num_blocks >= max_block)
599 break;
601 result = brasero_iso9660_next_block (ctx);
602 if (result != BRASERO_ISO_OK)
603 goto error;
605 continue;
607 else if (result == BRASERO_ISO_ERROR)
608 goto error;
610 if (!record)
611 break;
613 /* if it's a directory, keep the record for later (we don't
614 * want to change the reading offset for the moment) */
615 if (record->flags & BRASERO_ISO_FILE_DIRECTORY) {
616 gpointer copy;
618 copy = g_new0 (gchar, record->record_size);
619 memcpy (copy, record, record->record_size);
620 directories = g_slist_prepend (directories, copy);
621 continue;
624 if (ctx->has_RR) {
625 BraseroSuspCtx susp_ctx = { NULL, };
626 guint susp_len = 0;
627 gchar *susp;
629 /* See if we've got a susp area. Do it now to see if it
630 * has a CL entry. The rest will be checked later after
631 * reading contents. Otherwise we wouldn't be able to
632 * get deep directories that are flagged as files. */
633 susp = brasero_iso9660_get_susp (ctx, record, &susp_len);
634 if (!brasero_iso9660_read_susp (ctx, &susp_ctx, susp, susp_len)) {
635 BRASERO_MEDIA_LOG ("Could not read susp area");
636 goto error;
639 /* look for a "CL" SUSP entry in case the directory was
640 * relocated. If it has, it's a directory and keep it
641 * for later. */
642 if (susp_ctx.CL_address) {
643 gpointer copy;
645 BRASERO_MEDIA_LOG ("Entry has a CL entry, keeping for later");
646 copy = g_new0 (gchar, record->record_size);
647 memcpy (copy, record, record->record_size);
648 directories = g_slist_prepend (directories, copy);
650 brasero_susp_ctx_clean (&susp_ctx);
651 memset (&susp_ctx, 0, sizeof (BraseroSuspCtx));
652 continue;
655 entry = brasero_iso9660_read_file_record (ctx, record, &susp_ctx);
656 brasero_susp_ctx_clean (&susp_ctx);
658 else
659 entry = brasero_iso9660_read_file_record (ctx, record, NULL);
661 if (!entry)
662 goto error;
664 entry->parent = parent;
666 /* check that we don't have another file record for the
667 * same file (usually files > 4G). It always follows
668 * its sibling */
669 if (children) {
670 BraseroVolFile *last;
672 last = children->data;
673 if (!last->isdir && !strcmp (BRASERO_VOLUME_FILE_NAME (last), BRASERO_VOLUME_FILE_NAME (entry))) {
674 /* add size and addresses */
675 ctx->data_blocks += ISO9660_BYTES_TO_BLOCKS (entry->specific.file.size_bytes);
676 last = brasero_volume_file_merge (last, entry);
677 BRASERO_MEDIA_LOG ("Multi extent file");
678 continue;
682 children = g_list_prepend (children, entry);
683 ctx->data_blocks += ISO9660_BYTES_TO_BLOCKS (entry->specific.file.size_bytes);
686 /* Takes care of the directories: we accumulate them not to change the
687 * offset of file descriptor FILE */
688 for (iter = directories; iter; iter = iter->next) {
689 record = iter->data;
691 entry = brasero_iso9660_read_directory_record (ctx, record, recursive);
692 if (!entry)
693 goto error;
695 if (entry->relocated) {
696 brasero_volume_file_free (entry);
697 continue;
700 entry->parent = parent;
701 children = g_list_prepend (children, entry);
703 g_slist_foreach (directories, (GFunc) g_free, NULL);
704 g_slist_free (directories);
706 return children;
708 error:
710 g_list_foreach (children, (GFunc) brasero_volume_file_free, NULL);
711 g_list_free (children);
713 g_slist_foreach (directories, (GFunc) g_free, NULL);
714 g_slist_free (directories);
716 return NULL;
719 static gboolean
720 brasero_iso9660_check_SUSP_RR_use (BraseroIsoCtx *ctx,
721 BraseroIsoDirRec *record)
723 BraseroSuspCtx susp_ctx;
724 guint susp_len = 0;
725 gchar *susp;
727 susp = brasero_iso9660_get_susp (ctx, record, &susp_len);
728 brasero_iso9660_read_susp (ctx, &susp_ctx, susp, susp_len);
730 ctx->has_susp = susp_ctx.has_SP;
731 ctx->has_RR = susp_ctx.has_RockRidge;
732 ctx->susp_skip = susp_ctx.skip;
733 ctx->is_root = FALSE;
735 if (ctx->has_susp)
736 BRASERO_MEDIA_LOG ("File system supports system use sharing protocol");
738 if (ctx->has_RR)
739 BRASERO_MEDIA_LOG ("File system has Rock Ridge extension");
741 brasero_susp_ctx_clean (&susp_ctx);
742 return TRUE;
745 static void
746 brasero_iso9660_ctx_init (BraseroIsoCtx *ctx, BraseroVolSrc *vol)
748 memset (ctx, 0, sizeof (BraseroIsoCtx));
750 ctx->is_root = TRUE;
751 ctx->vol = vol;
752 ctx->offset = 0;
754 /* to fully initialize the context we need the root directory record */
757 BraseroVolFile *
758 brasero_iso9660_get_contents (BraseroVolSrc *vol,
759 const gchar *block,
760 gint64 *data_blocks,
761 GError **error)
763 BraseroIsoPrimary *primary;
764 BraseroIsoDirRec *record;
765 BraseroVolFile *volfile;
766 BraseroIsoDirRec *root;
767 BraseroIsoCtx ctx;
768 GList *children;
769 gint address;
771 primary = (BraseroIsoPrimary *) block;
772 root = primary->root_rec;
774 /* check settings */
775 address = brasero_iso9660_get_733_val (root->address);
776 brasero_iso9660_ctx_init (&ctx, vol);
777 brasero_iso9660_get_first_directory_record (&ctx, &record, address);
778 brasero_iso9660_check_SUSP_RR_use (&ctx, record);
780 /* create volume file */
781 volfile = g_new0 (BraseroVolFile, 1);
782 volfile->isdir = TRUE;
783 volfile->isdir_loaded = FALSE;
785 children = brasero_iso9660_load_directory_records (&ctx,
786 volfile,
787 record,
788 TRUE);
789 volfile->specific.dir.children = children;
791 if (ctx.spare_record)
792 g_free (ctx.spare_record);
794 if (data_blocks)
795 *data_blocks = ctx.data_blocks;
797 if (!children && ctx.error) {
798 if (error)
799 g_propagate_error (error, ctx.error);
801 brasero_volume_file_free (volfile);
802 volfile = NULL;
805 return volfile;
808 static BraseroVolFile *
809 brasero_iso9660_lookup_directory_record_RR (BraseroIsoCtx *ctx,
810 const gchar *path,
811 guint len,
812 BraseroIsoDirRec *record)
814 BraseroVolFile *entry = NULL;
815 BraseroSuspCtx susp_ctx;
816 gchar record_name [256];
817 guint susp_len = 0;
818 gchar *susp;
820 /* See if we've got a susp area. Do it now to see if it
821 * has a CL entry and rr_name. */
822 susp = brasero_iso9660_get_susp (ctx, record, &susp_len);
823 if (!brasero_iso9660_read_susp (ctx, &susp_ctx, susp, susp_len)) {
824 BRASERO_MEDIA_LOG ("Could not read susp area");
825 return NULL;
828 /* set name */
829 if (!susp_ctx.rr_name) {
830 memcpy (record_name, record->id, record->id_size);
831 record_name [record->id_size] = '\0';
833 else
834 strcpy (record_name, susp_ctx.rr_name);
836 if (!(record->flags & BRASERO_ISO_FILE_DIRECTORY)) {
837 if (len) {
838 /* Look for a "CL" SUSP entry in case it was
839 * relocated. If it has, it's a directory. */
840 if (susp_ctx.CL_address && !strncmp (record_name, path, len)) {
841 /* move path forward */
842 path += len;
843 path ++;
845 entry = brasero_iso9660_lookup_directory_records (ctx,
846 path,
847 susp_ctx.CL_address);
850 else if (!strcmp (record_name, path))
851 entry = brasero_iso9660_read_file_record (ctx,
852 record,
853 &susp_ctx);
855 else if (len && !strncmp (record_name, path, len)) {
856 gint address;
858 /* move path forward */
859 path += len;
860 path ++;
862 address = brasero_iso9660_get_733_val (record->address);
863 entry = brasero_iso9660_lookup_directory_records (ctx,
864 path,
865 address);
868 brasero_susp_ctx_clean (&susp_ctx);
869 return entry;
872 static BraseroVolFile *
873 brasero_iso9660_lookup_directory_record_ISO (BraseroIsoCtx *ctx,
874 const gchar *path,
875 guint len,
876 BraseroIsoDirRec *record)
878 BraseroVolFile *entry = NULL;
880 if (!(record->flags & BRASERO_ISO_FILE_DIRECTORY)) {
881 if (!len && !strncmp (record->id, path, record->id_size))
882 entry = brasero_iso9660_read_file_record (ctx,
883 record,
884 NULL);
886 else if (len && !strncmp (record->id, path, record->id_size)) {
887 gint address;
889 /* move path forward */
890 path += len;
891 path ++;
893 address = brasero_iso9660_get_733_val (record->address);
894 entry = brasero_iso9660_lookup_directory_records (ctx,
895 path,
896 address);
899 return entry;
902 static BraseroVolFile *
903 brasero_iso9660_lookup_directory_records (BraseroIsoCtx *ctx,
904 const gchar *path,
905 gint address)
907 guint len;
908 gchar *end;
909 gint max_block;
910 gint max_record_size;
911 BraseroIsoResult result;
912 BraseroIsoDirRec *record;
913 BraseroVolFile *file = NULL;
915 BRASERO_MEDIA_LOG ("Reading directory record");
917 result = brasero_iso9660_seek (ctx, address);
918 if (result != BRASERO_ISO_OK)
919 return NULL;
921 /* "." */
922 result = brasero_iso9660_next_record (ctx, &record);
923 if (result != BRASERO_ISO_OK)
924 return NULL;
926 /* Look for "SP" SUSP if it's root directory. Also look for "ER" which
927 * should tell us whether Rock Ridge could be used. */
928 if (ctx->is_root) {
929 BraseroSuspCtx susp_ctx;
930 guint susp_len = 0;
931 gchar *susp;
933 susp = brasero_iso9660_get_susp (ctx, record, &susp_len);
934 brasero_iso9660_read_susp (ctx, &susp_ctx, susp, susp_len);
936 ctx->has_susp = susp_ctx.has_SP;
937 ctx->has_RR = susp_ctx.has_RockRidge;
938 ctx->susp_skip = susp_ctx.skip;
939 ctx->is_root = FALSE;
941 brasero_susp_ctx_clean (&susp_ctx);
943 if (ctx->has_susp)
944 BRASERO_MEDIA_LOG ("File system supports system use sharing protocol");
946 if (ctx->has_RR)
947 BRASERO_MEDIA_LOG ("File system has Rock Ridge extension");
950 max_record_size = brasero_iso9660_get_733_val (record->file_size);
951 max_block = ISO9660_BYTES_TO_BLOCKS (max_record_size);
952 BRASERO_MEDIA_LOG ("Maximum directory record length %i block (= %i bytes)", max_block, max_record_size);
954 /* skip ".." */
955 result = brasero_iso9660_next_record (ctx, &record);
956 if (result != BRASERO_ISO_OK)
957 return NULL;
959 BRASERO_MEDIA_LOG ("Skipped '.' and '..'");
961 end = strchr (path, '/');
962 if (!end)
963 /* reached the final file */
964 len = 0;
965 else
966 len = end - path;
968 while (1) {
969 BraseroIsoResult result;
970 BraseroVolFile *entry;
972 result = brasero_iso9660_next_record (ctx, &record);
973 if (result == BRASERO_ISO_END) {
974 if (ctx->num_blocks >= max_block) {
975 BRASERO_MEDIA_LOG ("Reached the end of directory record");
976 break;
979 result = brasero_iso9660_next_block (ctx);
980 if (result != BRASERO_ISO_OK) {
981 BRASERO_MEDIA_LOG ("Failed to load next block");
982 return NULL;
985 continue;
987 else if (result == BRASERO_ISO_ERROR) {
988 BRASERO_MEDIA_LOG ("Error retrieving next record");
989 return NULL;
992 if (!record) {
993 BRASERO_MEDIA_LOG ("No record !!!");
994 break;
997 if (ctx->has_RR)
998 entry = brasero_iso9660_lookup_directory_record_RR (ctx,
999 path,
1000 len,
1001 record);
1002 else
1003 entry = brasero_iso9660_lookup_directory_record_ISO (ctx,
1004 path,
1005 len,
1006 record);
1008 if (!entry)
1009 continue;
1011 if (file) {
1012 /* add size and addresses */
1013 file = brasero_volume_file_merge (file, entry);
1014 BRASERO_MEDIA_LOG ("Multi extent file");
1016 else
1017 file = entry;
1019 /* carry on in case that's a multi extent file */
1022 return file;
1025 BraseroVolFile *
1026 brasero_iso9660_get_file (BraseroVolSrc *vol,
1027 const gchar *path,
1028 const gchar *block,
1029 GError **error)
1031 BraseroIsoPrimary *primary;
1032 BraseroIsoDirRec *root;
1033 BraseroVolFile *entry;
1034 BraseroIsoCtx ctx;
1035 gint address;
1037 primary = (BraseroIsoPrimary *) block;
1038 root = primary->root_rec;
1040 address = brasero_iso9660_get_733_val (root->address);
1041 brasero_iso9660_ctx_init (&ctx, vol);
1043 /* now that we have root block address, skip first "/" and go. */
1044 path ++;
1045 entry = brasero_iso9660_lookup_directory_records (&ctx,
1046 path,
1047 address);
1049 /* clean context */
1050 if (ctx.spare_record)
1051 g_free (ctx.spare_record);
1053 if (error && ctx.error)
1054 g_propagate_error (error, ctx.error);
1056 return entry;
1059 GList *
1060 brasero_iso9660_get_directory_contents (BraseroVolSrc *vol,
1061 const gchar *vol_desc,
1062 gint address,
1063 GError **error)
1065 BraseroIsoDirRec *record = NULL;
1066 BraseroIsoPrimary *primary;
1067 BraseroIsoDirRec *root;
1068 BraseroIsoCtx ctx;
1069 GList *children;
1071 /* Check root "." for use of RR and things like that */
1072 primary = (BraseroIsoPrimary *) vol_desc;
1073 root = primary->root_rec;
1075 brasero_iso9660_ctx_init (&ctx, vol);
1076 brasero_iso9660_get_first_directory_record (&ctx,
1077 &record,
1078 brasero_iso9660_get_733_val (root->address));
1079 brasero_iso9660_check_SUSP_RR_use (&ctx, record);
1081 /* Seek up to the contents of the directory */
1082 if (address > 0)
1083 brasero_iso9660_get_first_directory_record (&ctx,
1084 &record,
1085 address);
1087 /* load */
1088 children = brasero_iso9660_load_directory_records (&ctx,
1089 NULL,
1090 record,
1091 FALSE);
1092 if (ctx.error && error)
1093 g_propagate_error (error, ctx.error);
1095 return children;