1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2 /*
3  * Libbrasero-burn
4  * Copyright (C) Philippe Rouquier 2005-2009 <bonfire-app@wanadoo.fr>
5  *
6  * Libbrasero-burn 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-burn authors hereby grant permission for non-GPL compatible
12  * GStreamer plugins to be used and distributed together with GStreamer
13  * and Libbrasero-burn. This permission is above and beyond the permissions granted
14  * by the GPL license by which Libbrasero-burn 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-burn 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 #include "scsi-device.h"
32 #include "scsi-mmc1.h"
33 #include "burn-volume.h"
34 #include "burn-iso9660.h"
35 #include "burn-volume-read.h"
36 
37 struct _BraseroVolFileHandle {
38 	/* 64 is an empirical value based on one of my drives. */
39 	guchar buffer [2048 * 64];
40 	guint buffer_max;
41 
42 	/* position in buffer */
43 	guint offset;
44 
45 	/* address (in blocks) for current extent */
46 	guint extent_last;
47 
48 	/* size in bytes for the current extent */
49 	guint extent_size;
50 
51 	BraseroVolSrc *src;
52 	GSList *extents_backward;
53 	GSList *extents_forward;
54 	guint position;
55 };
56 
57 void
brasero_volume_file_close(BraseroVolFileHandle * handle)58 brasero_volume_file_close (BraseroVolFileHandle *handle)
59 {
60 	g_slist_free (handle->extents_forward);
61 	g_slist_free (handle->extents_backward);
62 	brasero_volume_source_close (handle->src);
63 	g_free (handle);
64 }
65 
66 static gboolean
brasero_volume_file_fill_buffer(BraseroVolFileHandle * handle)67 brasero_volume_file_fill_buffer (BraseroVolFileHandle *handle)
68 {
69 	guint blocks;
70 	gboolean result;
71 
72 	blocks = MIN (sizeof (handle->buffer) / 2048,
73 		      handle->extent_last - handle->position);
74 
75 	result = BRASERO_VOL_SRC_READ (handle->src,
76 				       (char *) handle->buffer,
77 				       blocks,
78 				       NULL);
79 	if (!result)
80 		return FALSE;
81 
82 	handle->offset = 0;
83 	handle->position += blocks;
84 
85 	if (handle->position == handle->extent_last)
86 		handle->buffer_max = (blocks - 1) * 2048 +
87 				     ((handle->extent_size % 2048) ?
88 				      (handle->extent_size % 2048) :
89 				       2048);
90 	else
91 		handle->buffer_max = sizeof (handle->buffer);
92 
93 	return TRUE;
94 }
95 
96 static gboolean
brasero_volume_file_next_extent(BraseroVolFileHandle * handle)97 brasero_volume_file_next_extent (BraseroVolFileHandle *handle)
98 {
99 	BraseroVolFileExtent *extent;
100 	gint res_seek;
101 	GSList *node;
102 
103 	node = handle->extents_forward;
104 	extent = node->data;
105 
106 	handle->extents_forward = g_slist_remove_link (handle->extents_forward, node);
107 	node->next = handle->extents_backward;
108 	handle->extents_backward = node;
109 
110 	handle->position = extent->block;
111 	handle->extent_size = extent->size;
112 	handle->extent_last = BRASERO_BYTES_TO_SECTORS (extent->size, 2048) + extent->block;
113 
114 	res_seek = BRASERO_VOL_SRC_SEEK (handle->src, handle->position, SEEK_SET,  NULL);
115 	if (res_seek == -1)
116 		return FALSE;
117 
118 	return TRUE;
119 }
120 
121 static gboolean
brasero_volume_file_rewind_real(BraseroVolFileHandle * handle)122 brasero_volume_file_rewind_real (BraseroVolFileHandle *handle)
123 {
124 	if (!brasero_volume_file_next_extent (handle))
125 		return FALSE;
126 
127 	return brasero_volume_file_fill_buffer (handle);
128 }
129 
130 BraseroVolFileHandle *
brasero_volume_file_open(BraseroVolSrc * src,BraseroVolFile * file)131 brasero_volume_file_open (BraseroVolSrc *src,
132 			  BraseroVolFile *file)
133 {
134 	BraseroVolFileHandle *handle;
135 
136 	if (file->isdir)
137 		return NULL;
138 
139 	handle = g_new0 (BraseroVolFileHandle, 1);
140 	handle->src = src;
141 	brasero_volume_source_ref (src);
142 
143 	handle->extents_forward = g_slist_copy (file->specific.file.extents);
144 	if (!brasero_volume_file_rewind_real (handle)) {
145 		brasero_volume_file_close (handle);
146 		return NULL;
147 	}
148 
149 	return handle;
150 }
151 
152 gboolean
brasero_volume_file_rewind(BraseroVolFileHandle * handle)153 brasero_volume_file_rewind (BraseroVolFileHandle *handle)
154 {
155 	GSList *node, *next;
156 
157 	/* Put back all extents in the unread list */
158 	for (node = handle->extents_backward; node; node = next) {
159 		next = node->next;
160 		handle->extents_backward = g_slist_remove_link (handle->extents_backward, node);
161 
162 		node->next = handle->extents_forward;
163 		handle->extents_forward = node;
164 	}
165 	return brasero_volume_file_rewind_real (handle);
166 }
167 
168 static BraseroBurnResult
brasero_volume_file_check_state(BraseroVolFileHandle * handle)169 brasero_volume_file_check_state (BraseroVolFileHandle *handle)
170 {
171 	/* check if we need to load a new block */
172 	if (handle->offset < handle->buffer_max)
173 		return BRASERO_BURN_RETRY;
174 
175 	/* check if we need to change our extent */
176 	if (handle->position >= handle->extent_last) {
177 		/* we are at the end of current extent try to find another */
178 		if (!handle->extents_forward) {
179 			/* we reached the end of our file */
180 			return BRASERO_BURN_OK;
181 		}
182 
183 		if (!brasero_volume_file_next_extent (handle))
184 			return BRASERO_BURN_ERR;
185 	}
186 
187 	/* Refill buffer */
188 	if (!brasero_volume_file_fill_buffer (handle))
189 		return BRASERO_BURN_ERR;
190 
191 	return BRASERO_BURN_RETRY;
192 }
193 
194 gint
brasero_volume_file_read(BraseroVolFileHandle * handle,gchar * buffer,guint len)195 brasero_volume_file_read (BraseroVolFileHandle *handle,
196 			  gchar *buffer,
197 			  guint len)
198 {
199 	guint buffer_offset = 0;
200 	BraseroBurnResult result;
201 
202 	while ((len - buffer_offset) > (handle->buffer_max - handle->offset)) {
203 		/* copy what is already in the buffer and refill the latter */
204 		memcpy (buffer + buffer_offset,
205 			handle->buffer + handle->offset,
206 			handle->buffer_max - handle->offset);
207 
208 		buffer_offset += handle->buffer_max - handle->offset;
209 		handle->offset = handle->buffer_max;
210 
211 		result = brasero_volume_file_check_state (handle);
212 		if (result == BRASERO_BURN_OK)
213 			return buffer_offset;
214 
215 		if (result == BRASERO_BURN_ERR)
216 			return -1;
217 	}
218 
219 	/* we filled the buffer and put len bytes in it */
220 	memcpy (buffer + buffer_offset,
221 		handle->buffer + handle->offset,
222 		len - buffer_offset);
223 
224 	handle->offset += len - buffer_offset;
225 
226 	result = brasero_volume_file_check_state (handle);
227 	if (result == BRASERO_BURN_ERR)
228 		return -1;
229 
230 	return len;
231 }
232 
233 static gint
brasero_volume_file_find_line_break(BraseroVolFileHandle * handle,guint buffer_offset,gchar * buffer,guint len)234 brasero_volume_file_find_line_break (BraseroVolFileHandle *handle,
235 				     guint buffer_offset,
236 				     gchar *buffer,
237 				     guint len)
238 {
239 	guchar *break_line;
240 	guint line_len;
241 
242 	/* search the next end of line characher in the buffer */
243 	break_line = memchr (handle->buffer + handle->offset,
244 			     '\n',
245 			     handle->buffer_max - handle->offset);
246 
247 	if (!break_line)
248 		return FALSE;
249 
250 	line_len = break_line - (handle->buffer + handle->offset);
251 	if (len && line_len >= len) {
252 		/* - 1 is to be able to set last character to '\0' */
253 		if (buffer) {
254 			memcpy (buffer + buffer_offset,
255 				handle->buffer + handle->offset,
256 				len - buffer_offset - 1);
257 
258 			buffer [len - 1] = '\0';
259 		}
260 
261 		handle->offset += len - buffer_offset - 1;
262 		return TRUE;
263 	}
264 
265 	if (buffer) {
266 		memcpy (buffer, handle->buffer + handle->offset, line_len);
267 		buffer [line_len] = '\0';
268 	}
269 
270 	/* add 1 to skip the line break */
271 	handle->offset += line_len + 1;
272 	return TRUE;
273 }
274 
275 BraseroBurnResult
brasero_volume_file_read_line(BraseroVolFileHandle * handle,gchar * buffer,guint len)276 brasero_volume_file_read_line (BraseroVolFileHandle *handle,
277 			       gchar *buffer,
278 			       guint len)
279 {
280 	guint buffer_offset = 0;
281 	gboolean found;
282 
283 	found = brasero_volume_file_find_line_break (handle,
284 						     buffer_offset,
285 						     buffer,
286 						     len);
287 	if (found)
288 		return brasero_volume_file_check_state (handle);
289 
290 	/* continue while remaining data is too small to fit buffer */
291 	while (!len || (len - buffer_offset) > (handle->buffer_max - handle->offset)) {
292 		BraseroBurnResult result;
293 
294 		/* copy what we already have in the buffer. */
295 		if (buffer)
296 			memcpy (buffer + buffer_offset,
297 				handle->offset + handle->buffer,
298 				handle->buffer_max - handle->offset);
299 
300 		buffer_offset += handle->buffer_max - handle->offset;
301 		handle->offset = handle->buffer_max;
302 
303 		/* refill buffer */
304 		result = brasero_volume_file_check_state (handle);
305 		if (result == BRASERO_BURN_OK) {
306 			if (buffer)
307 				buffer [len - 1] = '\0';
308 
309 			return result;
310 		}
311 
312 		found = brasero_volume_file_find_line_break (handle,
313 							     buffer_offset,
314 							     buffer,
315 							     len);
316 		if (found)
317 			return brasero_volume_file_check_state (handle);
318 	}
319 
320 	/* we filled the buffer */
321 	if (buffer) {
322 		memcpy (buffer + buffer_offset,
323 			handle->buffer + handle->offset,
324 			len - buffer_offset - 1);
325 		buffer [len - 1] = '\0';
326 	}
327 
328 	/* NOTE: when len == 0 we never reach this part */
329 	handle->offset += len - buffer_offset - 1;
330 
331 	return brasero_volume_file_check_state (handle);
332 }
333 
334 BraseroVolFileHandle *
brasero_volume_file_open_direct(BraseroVolSrc * src,BraseroVolFile * file)335 brasero_volume_file_open_direct (BraseroVolSrc *src,
336 				 BraseroVolFile *file)
337 {
338 	BraseroVolFileHandle *handle;
339 
340 	if (file->isdir)
341 		return NULL;
342 
343 	handle = g_new0 (BraseroVolFileHandle, 1);
344 	handle->src = src;
345 	brasero_volume_source_ref (src);
346 
347 	handle->extents_forward = g_slist_copy (file->specific.file.extents);
348 
349 	/* Here the buffer stays unused, we copy straight to the buffer passed
350 	 * in the read direct function. */
351 	if (!brasero_volume_file_next_extent (handle)) {
352 		brasero_volume_file_close (handle);
353 		return NULL;
354 	}
355 
356 	return handle;
357 }
358 
359 gint64
brasero_volume_file_read_direct(BraseroVolFileHandle * handle,guchar * buffer,guint blocks)360 brasero_volume_file_read_direct (BraseroVolFileHandle *handle,
361 				 guchar *buffer,
362 				 guint blocks)
363 {
364 	gboolean result;
365 	guint block2read;
366 	guint readblocks = 0;
367 
368 start:
369 
370 	block2read = MIN (blocks - readblocks, handle->extent_last - handle->position);
371 	if (!block2read)
372 		return readblocks * 2048;
373 
374 	result = BRASERO_VOL_SRC_READ (handle->src,
375 				       (char *) buffer + readblocks * 2048,
376 				       block2read,
377 				       NULL);
378 	if (!result)
379 		return -1;
380 
381 	handle->position += block2read;
382 	readblocks += block2read;
383 
384 	if (handle->position == handle->extent_last) {
385 		/* we are at the end of current extent try to find another */
386 		if (!handle->extents_forward) {
387 			/* we reached the end of our file */
388 			return (readblocks - 1) * 2048 +
389 			       ((handle->extent_size % 2048) != 0?
390 			        (handle->extent_size % 2048) :
391 				 2048);
392 		}
393 
394 		if (!brasero_volume_file_next_extent (handle))
395 			return -1;
396 
397 		goto start;
398 	}
399 
400 	return readblocks * 2048;
401 }
402 
403