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 #include <unistd.h>
39 
40 #include <glib.h>
41 #include <glib/gi18n-lib.h>
42 
43 #include "burn-volume-source.h"
44 #include "burn-volume.h"
45 #include "burn-iso9660.h"
46 #include "brasero-media.h"
47 #include "brasero-media-private.h"
48 #include "brasero-units.h"
49 
50 struct _BraseroTagDesc {
51 	guint16 id;
52 	guint16 version;
53 	guchar checksum;
54 	guchar reserved;
55 	guint16 serial;
56 	guint16 crc;
57 	guint16 crc_len;
58 	guint32 location;
59 };
60 typedef struct _BraseroTagDesc BraseroTagDesc;
61 
62 struct _BraseroAnchorDesc {
63 	BraseroTagDesc tag;
64 
65 	guchar main_extent		[8];
66 	guchar reserve_extent		[8];
67 };
68 typedef struct _BraseroAnchorDesc BraseroAnchorDesc;
69 
70 #define SYSTEM_AREA_SECTORS		16
71 #define ANCHOR_AREA_SECTORS		256
72 
73 
74 void
brasero_volume_file_free(BraseroVolFile * file)75 brasero_volume_file_free (BraseroVolFile *file)
76 {
77 	if (!file)
78 		return;
79 
80 	if (file->isdir) {
81 		GList *iter;
82 
83 		if (file->isdir_loaded) {
84 			for (iter = file->specific.dir.children; iter; iter = iter->next)
85 				brasero_volume_file_free (iter->data);
86 
87 			g_list_free (file->specific.dir.children);
88 		}
89 	}
90 	else {
91 		g_slist_foreach (file->specific.file.extents,
92 				 (GFunc) g_free,
93 				 NULL);
94 		g_slist_free (file->specific.file.extents);
95 	}
96 
97 	g_free (file->rr_name);
98 	g_free (file->name);
99 	g_free (file);
100 }
101 
102 static gboolean
brasero_volume_get_primary_from_file(BraseroVolSrc * vol,gchar * primary_vol,GError ** error)103 brasero_volume_get_primary_from_file (BraseroVolSrc *vol,
104 				      gchar *primary_vol,
105 				      GError **error)
106 {
107 	BraseroVolDesc *desc;
108 
109 	/* skip the first 16 blocks */
110 	if (BRASERO_VOL_SRC_SEEK (vol, SYSTEM_AREA_SECTORS, SEEK_CUR, error) == -1)
111 		return FALSE;
112 
113 	if (!BRASERO_VOL_SRC_READ (vol, primary_vol, 1, error))
114 		return FALSE;
115 
116 	/* make a few checks to ensure this is an ECMA volume */
117 	desc = (BraseroVolDesc *) primary_vol;
118 	if (memcmp (desc->id, "CD001", 5)
119 	&&  memcmp (desc->id, "BEA01", 5)
120 	&&  memcmp (desc->id, "BOOT2", 5)
121 	&&  memcmp (desc->id, "CDW02", 5)
122 	&&  memcmp (desc->id, "NSR02", 5)	/* usually UDF */
123 	&&  memcmp (desc->id, "NSR03", 5)	/* usually UDF */
124 	&&  memcmp (desc->id, "TEA01", 5)) {
125 		g_set_error (error,
126 			     BRASERO_MEDIA_ERROR,
127 			     BRASERO_MEDIA_ERROR_IMAGE_INVALID,
128 			     _("It does not appear to be a valid ISO image"));
129 		BRASERO_MEDIA_LOG ("Wrong volume descriptor, got %.5s", desc->id);
130 		return FALSE;
131 	}
132 
133 	return TRUE;
134 }
135 
136 gboolean
brasero_volume_get_size(BraseroVolSrc * vol,gint64 block,gint64 * nb_blocks,GError ** error)137 brasero_volume_get_size (BraseroVolSrc *vol,
138 			 gint64 block,
139 			 gint64 *nb_blocks,
140 			 GError **error)
141 {
142 	gboolean result;
143 	gchar buffer [ISO9660_BLOCK_SIZE];
144 
145 	if (block && BRASERO_VOL_SRC_SEEK (vol, block, SEEK_SET, error) == -1)
146 		return FALSE;
147 
148 	result = brasero_volume_get_primary_from_file (vol, buffer, error);
149 	if (!result)
150 		return FALSE;
151 
152 	if (!brasero_iso9660_is_primary_descriptor (buffer, error))
153 		return FALSE;
154 
155 	return brasero_iso9660_get_size (buffer, nb_blocks, error);
156 }
157 
158 BraseroVolFile *
brasero_volume_get_files(BraseroVolSrc * vol,gint64 block,gchar ** label,gint64 * nb_blocks,gint64 * data_blocks,GError ** error)159 brasero_volume_get_files (BraseroVolSrc *vol,
160 			  gint64 block,
161 			  gchar **label,
162 			  gint64 *nb_blocks,
163 			  gint64 *data_blocks,
164 			  GError **error)
165 {
166 	gchar buffer [ISO9660_BLOCK_SIZE];
167 
168 	if (BRASERO_VOL_SRC_SEEK (vol, block, SEEK_SET, error) == -1)
169 		return FALSE;
170 
171 	if (!brasero_volume_get_primary_from_file (vol, buffer, error))
172 		return NULL;
173 
174 	if (!brasero_iso9660_is_primary_descriptor (buffer, error))
175 		return NULL;
176 
177 	if (label
178 	&& !brasero_iso9660_get_label (buffer, label, error))
179 		return NULL;
180 
181 	if (nb_blocks
182 	&& !brasero_iso9660_get_size (buffer, nb_blocks, error))
183 		return NULL;
184 
185 	return brasero_iso9660_get_contents (vol,
186 					     buffer,
187 					     data_blocks,
188 					     error);
189 }
190 
191 GList *
brasero_volume_load_directory_contents(BraseroVolSrc * vol,gint64 session_block,gint64 block,GError ** error)192 brasero_volume_load_directory_contents (BraseroVolSrc *vol,
193 					gint64 session_block,
194 					gint64 block,
195 					GError **error)
196 {
197 	gchar buffer [ISO9660_BLOCK_SIZE];
198 
199 	if (BRASERO_VOL_SRC_SEEK (vol, session_block, SEEK_SET, error) == -1)
200 		return FALSE;
201 
202 	if (!brasero_volume_get_primary_from_file (vol, buffer, error))
203 		return NULL;
204 
205 	if (!brasero_iso9660_is_primary_descriptor (buffer, error))
206 		return NULL;
207 
208 	return brasero_iso9660_get_directory_contents (vol,
209 						       buffer,
210 						       block,
211 						       error);
212 }
213 
214 BraseroVolFile *
brasero_volume_get_file(BraseroVolSrc * vol,const gchar * path,gint64 volume_start_block,GError ** error)215 brasero_volume_get_file (BraseroVolSrc *vol,
216 			 const gchar *path,
217 			 gint64 volume_start_block,
218 			 GError **error)
219 {
220 	gchar buffer [ISO9660_BLOCK_SIZE];
221 
222 	if (BRASERO_VOL_SRC_SEEK (vol, volume_start_block, SEEK_SET, error) == -1)
223 		return NULL;
224 
225 	if (!brasero_volume_get_primary_from_file (vol, buffer, error))
226 		return NULL;
227 
228 	if (!brasero_iso9660_is_primary_descriptor (buffer, error))
229 		return NULL;
230 
231 	return brasero_iso9660_get_file (vol, path, buffer, error);
232 }
233 
234 gchar *
brasero_volume_file_to_path(BraseroVolFile * file)235 brasero_volume_file_to_path (BraseroVolFile *file)
236 {
237 	GString *path;
238 	BraseroVolFile *parent;
239 	GSList *components = NULL, *iter, *next;
240 
241 	if (!file)
242 		return NULL;
243 
244 	/* make a list of all the components of the path by going up to root */
245 	parent = file->parent;
246 	while (parent && parent->name) {
247 		components = g_slist_prepend (components, BRASERO_VOLUME_FILE_NAME (parent));
248 		parent = parent->parent;
249 	}
250 
251 	if (!components)
252 		return NULL;
253 
254 	path = g_string_new (NULL);
255 	for (iter = components; iter; iter = next) {
256 		gchar *name;
257 
258 		name = iter->data;
259 		next = iter->next;
260 		components = g_slist_remove (components, name);
261 
262 		g_string_append_c (path, G_DIR_SEPARATOR);
263 		g_string_append (path, name);
264 	}
265 
266 	g_slist_free (components);
267 	return g_string_free (path, FALSE);
268 }
269 
270 BraseroVolFile *
brasero_volume_file_from_path(const gchar * ptr,BraseroVolFile * parent)271 brasero_volume_file_from_path (const gchar *ptr,
272 			       BraseroVolFile *parent)
273 {
274 	GList *iter;
275 	gchar *next;
276 	gint len;
277 
278 	/* first determine the name of the directory / file to look for */
279 	if (!ptr || ptr [0] != '/' || !parent)
280 		return NULL;
281 
282 	ptr ++;
283 	next = g_utf8_strchr (ptr, -1, G_DIR_SEPARATOR);
284 	if (!next)
285 		len = strlen (ptr);
286 	else
287 		len = next - ptr;
288 
289 	for (iter = parent->specific.dir.children; iter; iter = iter->next) {
290 		BraseroVolFile *file;
291 
292 		file = iter->data;
293 		if (!strncmp (ptr, BRASERO_VOLUME_FILE_NAME (file), len)) {
294 			/* we've found it seek for the next if any */
295 			if (!next)
296 				return file;
297 
298 			ptr = next;
299 			return brasero_volume_file_from_path (ptr, file);
300 		}
301 	}
302 
303 	return NULL;
304 }
305 
306 gint64
brasero_volume_file_size(BraseroVolFile * file)307 brasero_volume_file_size (BraseroVolFile *file)
308 {
309 	GList *iter;
310 	gint64 size = 0;
311 
312 	if (!file->isdir) {
313 		GSList *extents;
314 
315 		for (extents = file->specific.file.extents; extents; extents = extents->next) {
316 			BraseroVolFileExtent *extent;
317 
318 			extent = extents->data;
319 			size += extent->size;
320 		}
321 		return BRASERO_BYTES_TO_SECTORS (size, 2048);
322 	}
323 
324 	for (iter = file->specific.dir.children; iter; iter = iter->next) {
325 		file = iter->data;
326 
327 		if (file->isdir)
328 			size += brasero_volume_file_size (file);
329 		else
330 			size += BRASERO_BYTES_TO_SECTORS (file->specific.file.size_bytes, 2048);
331 	}
332 
333 	return size;
334 }
335 
336 BraseroVolFile *
brasero_volume_file_merge(BraseroVolFile * file1,BraseroVolFile * file2)337 brasero_volume_file_merge (BraseroVolFile *file1,
338 			   BraseroVolFile *file2)
339 {
340 	file1->specific.file.size_bytes += file2->specific.file.size_bytes;
341 	file1->specific.file.extents = g_slist_concat (file1->specific.file.extents,
342 							     file2->specific.file.extents);
343 
344 	file2->specific.file.extents = NULL;
345 	brasero_volume_file_free (file2);
346 
347 	return file1;
348 }
349 
350