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