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>
5 *
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.
10 *
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.
18 *
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.
23 *
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.
29 */
30
31 #ifdef HAVE_CONFIG_H
32 # include <config.h>
33 #endif
34
35 #include <errno.h>
36 #include <string.h>
37 #include <stdio.h>
38
39 #include <glib.h>
40 #include <glib/gi18n-lib.h>
41
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"
50
51 struct _BraseroIsoCtx {
52 gint num_blocks;
53
54 gchar buffer [ISO9660_BLOCK_SIZE];
55 gint offset;
56 BraseroVolSrc *vol;
57
58 gchar *spare_record;
59
60 guint64 data_blocks;
61 GError *error;
62
63 guchar susp_skip;
64
65 guint is_root:1;
66 guint has_susp:1;
67 guint has_RR:1;
68 };
69 typedef struct _BraseroIsoCtx BraseroIsoCtx;
70
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;
80
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];
93 };
94 typedef struct _BraseroIsoDirRec BraseroIsoDirRec;
95
96 struct _BraseroIsoPrimary {
97 guchar type;
98 gchar id [5];
99 guchar version;
100
101 guchar unused_0;
102
103 gchar system_id [32];
104 gchar vol_id [32];
105
106 guchar unused_1 [8];
107
108 guchar vol_size [8];
109
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];
119
120 /* the following has a fixed size of 34 bytes */
121 BraseroIsoDirRec root_rec [0];
122
123 /* to be continued if needed */
124 };
125 typedef struct _BraseroIsoPrimary BraseroIsoPrimary;
126
127 typedef enum {
128 BRASERO_ISO_OK,
129 BRASERO_ISO_END,
130 BRASERO_ISO_ERROR
131 } BraseroIsoResult;
132
133 #define ISO9660_BYTES_TO_BLOCKS(size) BRASERO_BYTES_TO_SECTORS ((size), ISO9660_BLOCK_SIZE)
134
135 static GList *
136 brasero_iso9660_load_directory_records (BraseroIsoCtx *ctx,
137 BraseroVolFile *parent,
138 BraseroIsoDirRec *record,
139 gboolean recursive);
140
141 static BraseroVolFile *
142 brasero_iso9660_lookup_directory_records (BraseroIsoCtx *ctx,
143 const gchar *path,
144 gint address);
145
146 gboolean
brasero_iso9660_is_primary_descriptor(const char * buffer,GError ** error)147 brasero_iso9660_is_primary_descriptor (const char *buffer,
148 GError **error)
149 {
150 BraseroVolDesc *vol;
151
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;
160 }
161
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;
169 }
170
171 return TRUE;
172 }
173
174 gboolean
brasero_iso9660_get_size(const gchar * block,gint64 * nb_blocks,GError ** error)175 brasero_iso9660_get_size (const gchar *block,
176 gint64 *nb_blocks,
177 GError **error)
178 {
179 BraseroIsoPrimary *vol;
180
181 /* read the size of the volume */
182 vol = (BraseroIsoPrimary *) block;
183 *nb_blocks = (gint64) brasero_iso9660_get_733_val (vol->vol_size);
184
185 return TRUE;
186 }
187
188 gboolean
brasero_iso9660_get_label(const gchar * block,gchar ** label,GError ** error)189 brasero_iso9660_get_label (const gchar *block,
190 gchar **label,
191 GError **error)
192 {
193 BraseroIsoPrimary *vol;
194
195 /* read the identifier */
196 vol = (BraseroIsoPrimary *) block;
197 *label = g_strndup (vol->vol_id, sizeof (vol->vol_id));
198
199 return TRUE;
200 }
201
202 static BraseroIsoResult
brasero_iso9660_seek(BraseroIsoCtx * ctx,gint address)203 brasero_iso9660_seek (BraseroIsoCtx *ctx, gint address)
204 {
205 ctx->offset = 0;
206 ctx->num_blocks = 1;
207
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;
214
215 if (!BRASERO_VOL_SRC_READ (ctx->vol, ctx->buffer, 1, &(ctx->error)))
216 return BRASERO_ISO_ERROR;
217
218 return BRASERO_ISO_OK;
219 }
220
221 static BraseroIsoResult
brasero_iso9660_next_block(BraseroIsoCtx * ctx)222 brasero_iso9660_next_block (BraseroIsoCtx *ctx)
223 {
224 ctx->offset = 0;
225 ctx->num_blocks ++;
226
227 if (!BRASERO_VOL_SRC_READ (ctx->vol, ctx->buffer, 1, &(ctx->error)))
228 return BRASERO_ISO_ERROR;
229
230 return BRASERO_ISO_OK;
231 }
232
233 static gboolean
brasero_iso9660_read_susp(BraseroIsoCtx * ctx,BraseroSuspCtx * susp_ctx,gchar * susp,gint susp_len)234 brasero_iso9660_read_susp (BraseroIsoCtx *ctx,
235 BraseroSuspCtx *susp_ctx,
236 gchar *susp,
237 gint susp_len)
238 {
239 gboolean result = TRUE;
240 guint64 current_position = -1;
241
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;
246 }
247
248 while (susp_ctx->CE_address) {
249 gchar CE_block [ISO9660_BLOCK_SIZE];
250 gint64 seek_res;
251 guint32 offset;
252 guint32 len;
253
254 BRASERO_MEDIA_LOG ("Continuation Area");
255
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;
262 }
263
264 if (current_position == -1)
265 current_position = seek_res;
266
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;
271 }
272
273 offset = susp_ctx->CE_offset;
274 len = MIN (susp_ctx->CE_len, sizeof (CE_block) - offset);
275
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));
280
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;
286 }
287 }
288
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;
294 }
295
296 return result;
297 }
298
299 static gchar *
brasero_iso9660_get_susp(BraseroIsoCtx * ctx,BraseroIsoDirRec * record,guint * susp_len)300 brasero_iso9660_get_susp (BraseroIsoCtx *ctx,
301 BraseroIsoDirRec *record,
302 guint *susp_len)
303 {
304 gchar *susp_block;
305 gint start;
306 gint len;
307
308 start = sizeof (BraseroIsoDirRec) + record->id_size;
309 /* padding byte when id_size is an even number */
310 if (start & 1)
311 start ++;
312
313 if (ctx->susp_skip)
314 start += ctx->susp_skip;
315
316 /* we don't want to go beyond end of buffer */
317 if (start >= record->record_size)
318 return NULL;
319
320 len = record->record_size - start;
321
322 if (len <= 0)
323 return NULL;
324
325 if (susp_len)
326 *susp_len = len;
327
328 susp_block = ((gchar *) record) + start;
329
330 BRASERO_MEDIA_LOG ("Got susp block");
331 return susp_block;
332 }
333
334 static BraseroIsoResult
brasero_iso9660_next_record(BraseroIsoCtx * ctx,BraseroIsoDirRec ** retval)335 brasero_iso9660_next_record (BraseroIsoCtx *ctx, BraseroIsoDirRec **retval)
336 {
337 BraseroIsoDirRec *record;
338
339 if (ctx->offset > sizeof (ctx->buffer)) {
340 BRASERO_MEDIA_LOG ("Invalid record size");
341 goto error;
342 }
343
344 if (ctx->offset == sizeof (ctx->buffer)) {
345 BRASERO_MEDIA_LOG ("No next record");
346 return BRASERO_ISO_END;
347 }
348
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;
354 }
355
356 if (record->record_size > (sizeof (ctx->buffer) - ctx->offset)) {
357 gint part_one, part_two;
358
359 /* This is for cross sector boundary records */
360 BRASERO_MEDIA_LOG ("Cross sector boundary record");
361
362 /* some implementations write across block boundary which is
363 * "forbidden" by ECMA-119. But linux kernel accepts it, so ...
364 */
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;
369 */
370 if (ctx->spare_record)
371 g_free (ctx->spare_record);
372
373 ctx->spare_record = g_new0 (gchar, record->record_size);
374
375 part_one = sizeof (ctx->buffer) - ctx->offset;
376 part_two = record->record_size - part_one;
377
378 memcpy (ctx->spare_record,
379 ctx->buffer + ctx->offset,
380 part_one);
381
382 if (brasero_iso9660_next_block (ctx) == BRASERO_ISO_ERROR)
383 goto error;
384
385 memcpy (ctx->spare_record + part_one,
386 ctx->buffer,
387 part_two);
388 ctx->offset = part_two;
389
390 record = (BraseroIsoDirRec *) ctx->spare_record;
391 }
392 else
393 ctx->offset += record->record_size;
394
395 *retval = record;
396 return BRASERO_ISO_OK;
397
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;
404 }
405
406 static BraseroIsoResult
brasero_iso9660_get_first_directory_record(BraseroIsoCtx * ctx,BraseroIsoDirRec ** record,gint address)407 brasero_iso9660_get_first_directory_record (BraseroIsoCtx *ctx,
408 BraseroIsoDirRec **record,
409 gint address)
410 {
411 BraseroIsoResult result;
412
413 BRASERO_MEDIA_LOG ("Reading directory record");
414
415 result = brasero_iso9660_seek (ctx, address);
416 if (result != BRASERO_ISO_OK)
417 return BRASERO_ISO_ERROR;
418
419 /* load "." */
420 result = brasero_iso9660_next_record (ctx, record);
421 if (result != BRASERO_ISO_OK)
422 return BRASERO_ISO_ERROR;
423
424 return BRASERO_ISO_OK;
425 }
426
427 static BraseroVolFile *
brasero_iso9660_read_file_record(BraseroIsoCtx * ctx,BraseroIsoDirRec * record,BraseroSuspCtx * susp_ctx)428 brasero_iso9660_read_file_record (BraseroIsoCtx *ctx,
429 BraseroIsoDirRec *record,
430 BraseroSuspCtx *susp_ctx)
431 {
432 BraseroVolFile *file;
433 BraseroVolFileExtent *extent;
434
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;
441 }
442
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);
447
448 file->specific.file.size_bytes = brasero_iso9660_get_733_val (record->file_size);
449
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);
455
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;
460 }
461
462 BRASERO_MEDIA_LOG ("New file %s with a suspend area", file->name);
463
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;
468 }
469
470 return file;
471 }
472
473 static BraseroVolFile *
brasero_iso9660_read_directory_record(BraseroIsoCtx * ctx,BraseroIsoDirRec * record,gboolean recursive)474 brasero_iso9660_read_directory_record (BraseroIsoCtx *ctx,
475 BraseroIsoDirRec *record,
476 gboolean recursive)
477 {
478 gchar *susp;
479 gint address;
480 guint susp_len = 0;
481 BraseroSuspCtx susp_ctx;
482 BraseroVolFile *directory;
483
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;
490 }
491
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);
498
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.
502 */
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;
508 }
509
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;
514 }
515 else
516 address = brasero_iso9660_get_733_val (record->address);
517
518 BRASERO_MEDIA_LOG ("New directory %s with susp area", directory->name);
519
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;
526 }
527
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;
532 }
533
534 brasero_susp_ctx_clean (&susp_ctx);
535 }
536 else
537 address = brasero_iso9660_get_733_val (record->address);
538
539 /* load contents if recursive */
540 if (recursive) {
541 GList *children;
542
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);
554
555 return NULL;
556 }
557
558 directory->isdir_loaded = TRUE;
559 directory->specific.dir.children = children;
560 }
561 else /* store the address of contents for later use */
562 directory->specific.dir.address = address;
563
564 BRASERO_MEDIA_LOG ("New directory %s", directory->name);
565 return directory;
566 }
567
568 static GList *
brasero_iso9660_load_directory_records(BraseroIsoCtx * ctx,BraseroVolFile * parent,BraseroIsoDirRec * record,gboolean recursive)569 brasero_iso9660_load_directory_records (BraseroIsoCtx *ctx,
570 BraseroVolFile *parent,
571 BraseroIsoDirRec *record,
572 gboolean recursive)
573 {
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;
581
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);
585
586 /* skip ".." */
587 result = brasero_iso9660_next_record (ctx, &record);
588 if (result != BRASERO_ISO_OK)
589 return NULL;
590
591 BRASERO_MEDIA_LOG ("Skipped '.' and '..'");
592
593 while (1) {
594 BraseroIsoResult result;
595
596 result = brasero_iso9660_next_record (ctx, &record);
597 if (result == BRASERO_ISO_END) {
598 if (ctx->num_blocks >= max_block)
599 break;
600
601 result = brasero_iso9660_next_block (ctx);
602 if (result != BRASERO_ISO_OK)
603 goto error;
604
605 continue;
606 }
607 else if (result == BRASERO_ISO_ERROR)
608 goto error;
609
610 if (!record)
611 break;
612
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;
617
618 copy = g_new0 (gchar, record->record_size);
619 memcpy (copy, record, record->record_size);
620 directories = g_slist_prepend (directories, copy);
621 continue;
622 }
623
624 if (ctx->has_RR) {
625 BraseroSuspCtx susp_ctx = { NULL, };
626 guint susp_len = 0;
627 gchar *susp;
628
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;
637 }
638
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;
644
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);
649
650 brasero_susp_ctx_clean (&susp_ctx);
651 memset (&susp_ctx, 0, sizeof (BraseroSuspCtx));
652 continue;
653 }
654
655 entry = brasero_iso9660_read_file_record (ctx, record, &susp_ctx);
656 brasero_susp_ctx_clean (&susp_ctx);
657 }
658 else
659 entry = brasero_iso9660_read_file_record (ctx, record, NULL);
660
661 if (!entry)
662 goto error;
663
664 entry->parent = parent;
665
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;
671
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;
679 }
680 }
681
682 children = g_list_prepend (children, entry);
683 ctx->data_blocks += ISO9660_BYTES_TO_BLOCKS (entry->specific.file.size_bytes);
684 }
685
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;
690
691 entry = brasero_iso9660_read_directory_record (ctx, record, recursive);
692 if (!entry)
693 goto error;
694
695 if (entry->relocated) {
696 brasero_volume_file_free (entry);
697 continue;
698 }
699
700 entry->parent = parent;
701 children = g_list_prepend (children, entry);
702 }
703 g_slist_foreach (directories, (GFunc) g_free, NULL);
704 g_slist_free (directories);
705
706 return children;
707
708 error:
709
710 g_list_foreach (children, (GFunc) brasero_volume_file_free, NULL);
711 g_list_free (children);
712
713 g_slist_foreach (directories, (GFunc) g_free, NULL);
714 g_slist_free (directories);
715
716 return NULL;
717 }
718
719 static gboolean
brasero_iso9660_check_SUSP_RR_use(BraseroIsoCtx * ctx,BraseroIsoDirRec * record)720 brasero_iso9660_check_SUSP_RR_use (BraseroIsoCtx *ctx,
721 BraseroIsoDirRec *record)
722 {
723 BraseroSuspCtx susp_ctx;
724 guint susp_len = 0;
725 gchar *susp;
726
727 susp = brasero_iso9660_get_susp (ctx, record, &susp_len);
728 brasero_iso9660_read_susp (ctx, &susp_ctx, susp, susp_len);
729
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;
734
735 if (ctx->has_susp)
736 BRASERO_MEDIA_LOG ("File system supports system use sharing protocol");
737
738 if (ctx->has_RR)
739 BRASERO_MEDIA_LOG ("File system has Rock Ridge extension");
740
741 brasero_susp_ctx_clean (&susp_ctx);
742 return TRUE;
743 }
744
745 static void
brasero_iso9660_ctx_init(BraseroIsoCtx * ctx,BraseroVolSrc * vol)746 brasero_iso9660_ctx_init (BraseroIsoCtx *ctx, BraseroVolSrc *vol)
747 {
748 memset (ctx, 0, sizeof (BraseroIsoCtx));
749
750 ctx->is_root = TRUE;
751 ctx->vol = vol;
752 ctx->offset = 0;
753
754 /* to fully initialize the context we need the root directory record */
755 }
756
757 BraseroVolFile *
brasero_iso9660_get_contents(BraseroVolSrc * vol,const gchar * block,gint64 * data_blocks,GError ** error)758 brasero_iso9660_get_contents (BraseroVolSrc *vol,
759 const gchar *block,
760 gint64 *data_blocks,
761 GError **error)
762 {
763 BraseroIsoPrimary *primary;
764 BraseroIsoDirRec *record;
765 BraseroVolFile *volfile;
766 BraseroIsoDirRec *root;
767 BraseroIsoCtx ctx;
768 GList *children;
769 gint address;
770
771 primary = (BraseroIsoPrimary *) block;
772 root = primary->root_rec;
773
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);
779
780 /* create volume file */
781 volfile = g_new0 (BraseroVolFile, 1);
782 volfile->isdir = TRUE;
783 volfile->isdir_loaded = FALSE;
784
785 children = brasero_iso9660_load_directory_records (&ctx,
786 volfile,
787 record,
788 TRUE);
789 volfile->specific.dir.children = children;
790
791 if (ctx.spare_record)
792 g_free (ctx.spare_record);
793
794 if (data_blocks)
795 *data_blocks = ctx.data_blocks;
796
797 if (!children && ctx.error) {
798 if (error)
799 g_propagate_error (error, ctx.error);
800
801 brasero_volume_file_free (volfile);
802 volfile = NULL;
803 }
804
805 return volfile;
806 }
807
808 static BraseroVolFile *
brasero_iso9660_lookup_directory_record_RR(BraseroIsoCtx * ctx,const gchar * path,guint len,BraseroIsoDirRec * record)809 brasero_iso9660_lookup_directory_record_RR (BraseroIsoCtx *ctx,
810 const gchar *path,
811 guint len,
812 BraseroIsoDirRec *record)
813 {
814 BraseroVolFile *entry = NULL;
815 BraseroSuspCtx susp_ctx;
816 gchar record_name [256];
817 guint susp_len = 0;
818 gchar *susp;
819
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;
826 }
827
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';
832 }
833 else
834 strcpy (record_name, susp_ctx.rr_name);
835
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 ++;
844
845 entry = brasero_iso9660_lookup_directory_records (ctx,
846 path,
847 susp_ctx.CL_address);
848 }
849 }
850 else if (!strcmp (record_name, path))
851 entry = brasero_iso9660_read_file_record (ctx,
852 record,
853 &susp_ctx);
854 }
855 else if (len && !strncmp (record_name, path, len)) {
856 gint address;
857
858 /* move path forward */
859 path += len;
860 path ++;
861
862 address = brasero_iso9660_get_733_val (record->address);
863 entry = brasero_iso9660_lookup_directory_records (ctx,
864 path,
865 address);
866 }
867
868 brasero_susp_ctx_clean (&susp_ctx);
869 return entry;
870 }
871
872 static BraseroVolFile *
brasero_iso9660_lookup_directory_record_ISO(BraseroIsoCtx * ctx,const gchar * path,guint len,BraseroIsoDirRec * record)873 brasero_iso9660_lookup_directory_record_ISO (BraseroIsoCtx *ctx,
874 const gchar *path,
875 guint len,
876 BraseroIsoDirRec *record)
877 {
878 BraseroVolFile *entry = NULL;
879
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);
885 }
886 else if (len && !strncmp (record->id, path, record->id_size)) {
887 gint address;
888
889 /* move path forward */
890 path += len;
891 path ++;
892
893 address = brasero_iso9660_get_733_val (record->address);
894 entry = brasero_iso9660_lookup_directory_records (ctx,
895 path,
896 address);
897 }
898
899 return entry;
900 }
901
902 static BraseroVolFile *
brasero_iso9660_lookup_directory_records(BraseroIsoCtx * ctx,const gchar * path,gint address)903 brasero_iso9660_lookup_directory_records (BraseroIsoCtx *ctx,
904 const gchar *path,
905 gint address)
906 {
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;
914
915 BRASERO_MEDIA_LOG ("Reading directory record");
916
917 result = brasero_iso9660_seek (ctx, address);
918 if (result != BRASERO_ISO_OK)
919 return NULL;
920
921 /* "." */
922 result = brasero_iso9660_next_record (ctx, &record);
923 if (result != BRASERO_ISO_OK)
924 return NULL;
925
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;
932
933 susp = brasero_iso9660_get_susp (ctx, record, &susp_len);
934 brasero_iso9660_read_susp (ctx, &susp_ctx, susp, susp_len);
935
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;
940
941 brasero_susp_ctx_clean (&susp_ctx);
942
943 if (ctx->has_susp)
944 BRASERO_MEDIA_LOG ("File system supports system use sharing protocol");
945
946 if (ctx->has_RR)
947 BRASERO_MEDIA_LOG ("File system has Rock Ridge extension");
948 }
949
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);
953
954 /* skip ".." */
955 result = brasero_iso9660_next_record (ctx, &record);
956 if (result != BRASERO_ISO_OK)
957 return NULL;
958
959 BRASERO_MEDIA_LOG ("Skipped '.' and '..'");
960
961 end = strchr (path, '/');
962 if (!end)
963 /* reached the final file */
964 len = 0;
965 else
966 len = end - path;
967
968 while (1) {
969 BraseroIsoResult result;
970 BraseroVolFile *entry;
971
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;
977 }
978
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;
983 }
984
985 continue;
986 }
987 else if (result == BRASERO_ISO_ERROR) {
988 BRASERO_MEDIA_LOG ("Error retrieving next record");
989 return NULL;
990 }
991
992 if (!record) {
993 BRASERO_MEDIA_LOG ("No record !!!");
994 break;
995 }
996
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);
1007
1008 if (!entry)
1009 continue;
1010
1011 if (file) {
1012 /* add size and addresses */
1013 file = brasero_volume_file_merge (file, entry);
1014 BRASERO_MEDIA_LOG ("Multi extent file");
1015 }
1016 else
1017 file = entry;
1018
1019 /* carry on in case that's a multi extent file */
1020 }
1021
1022 return file;
1023 }
1024
1025 BraseroVolFile *
brasero_iso9660_get_file(BraseroVolSrc * vol,const gchar * path,const gchar * block,GError ** error)1026 brasero_iso9660_get_file (BraseroVolSrc *vol,
1027 const gchar *path,
1028 const gchar *block,
1029 GError **error)
1030 {
1031 BraseroIsoPrimary *primary;
1032 BraseroIsoDirRec *root;
1033 BraseroVolFile *entry;
1034 BraseroIsoCtx ctx;
1035 gint address;
1036
1037 primary = (BraseroIsoPrimary *) block;
1038 root = primary->root_rec;
1039
1040 address = brasero_iso9660_get_733_val (root->address);
1041 brasero_iso9660_ctx_init (&ctx, vol);
1042
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);
1048
1049 /* clean context */
1050 if (ctx.spare_record)
1051 g_free (ctx.spare_record);
1052
1053 if (error && ctx.error)
1054 g_propagate_error (error, ctx.error);
1055
1056 return entry;
1057 }
1058
1059 GList *
brasero_iso9660_get_directory_contents(BraseroVolSrc * vol,const gchar * vol_desc,gint address,GError ** error)1060 brasero_iso9660_get_directory_contents (BraseroVolSrc *vol,
1061 const gchar *vol_desc,
1062 gint address,
1063 GError **error)
1064 {
1065 BraseroIsoDirRec *record = NULL;
1066 BraseroIsoPrimary *primary;
1067 BraseroIsoDirRec *root;
1068 BraseroIsoCtx ctx;
1069 GList *children;
1070
1071 /* Check root "." for use of RR and things like that */
1072 primary = (BraseroIsoPrimary *) vol_desc;
1073 root = primary->root_rec;
1074
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);
1080
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);
1086
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);
1094
1095 return children;
1096 }
1097