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