1 /*
2  * Optical disk functions
3  *
4  * Copyright (C) 2010-2021, Joachim Metz <joachim.metz@gmail.com>
5  *
6  * Refer to AUTHORS for acknowledgements.
7  *
8  * This program is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU Lesser General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
20  */
21 
22 #include <common.h>
23 #include <byte_stream.h>
24 #include <memory.h>
25 #include <types.h>
26 
27 #if defined( HAVE_LINUX_CDROM_H )
28 #include <linux/cdrom.h>
29 #endif
30 
31 #include "libsmdev_definitions.h"
32 #include "libsmdev_handle.h"
33 #include "libsmdev_libcdata.h"
34 #include "libsmdev_libcerror.h"
35 #include "libsmdev_libcfile.h"
36 #include "libsmdev_libcnotify.h"
37 #include "libsmdev_optical_disc.h"
38 #include "libsmdev_sector_range.h"
39 #include "libsmdev_scsi.h"
40 #include "libsmdev_track_value.h"
41 
42 #if defined( HAVE_LINUX_CDROM_H )
43 
44 #define libsmdev_optical_disc_copy_absolute_msf_to_lba( minutes, seconds, frames, lba ) \
45 	lba  = minutes; \
46 	lba *= CD_SECS; \
47 	lba += seconds; \
48 	lba *= CD_FRAMES; \
49 	lba += frames;
50 
51 #define libsmdev_optical_disc_copy_msf_to_lba( minutes, seconds, frames, lba ) \
52 	lba  = minutes; \
53 	lba *= CD_SECS; \
54 	lba += seconds; \
55 	lba *= CD_FRAMES; \
56 	lba += frames; \
57 	lba -= CD_MSF_OFFSET;
58 
59 /* Retrieves the table of contents (toc) from the optical disk
60  * Returns 1 if successful, 0 if not or -1 on error
61  */
libsmdev_optical_disc_get_table_of_contents(libcfile_file_t * device_file,libsmdev_internal_handle_t * internal_handle,libcerror_error_t ** error)62 int libsmdev_optical_disc_get_table_of_contents(
63      libcfile_file_t *device_file,
64      libsmdev_internal_handle_t *internal_handle,
65      libcerror_error_t **error )
66 {
67 	static char *function = "libsmdev_optical_disc_get_table_of_contents";
68 	int result            = 0;
69 
70 	result = libsmdev_optical_disc_get_table_of_contents_scsi(
71 	          device_file,
72 	          internal_handle,
73 	          error );
74 
75 	if( result == -1 )
76 	{
77 		libcerror_error_set(
78 		 error,
79 		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
80 		 LIBCERROR_RUNTIME_ERROR_GET_FAILED,
81 		 "%s: unable to retrieve table of contents using SCSI commands.",
82 		 function );
83 
84 		return( -1 );
85 	}
86 	else if( result == 0 )
87 	{
88 		result = libsmdev_optical_disc_get_table_of_contents_ioctl(
89 		          device_file,
90 		          internal_handle,
91 		          error );
92 
93 		if( result == -1 )
94 		{
95 			libcerror_error_set(
96 			 error,
97 			 LIBCERROR_ERROR_DOMAIN_RUNTIME,
98 			 LIBCERROR_RUNTIME_ERROR_GET_FAILED,
99 			 "%s: unable to retrieve table of contents using IO control.",
100 			 function );
101 
102 			return( -1 );
103 		}
104 	}
105 	return( result );
106 }
107 
108 /* Retrieves the table of contents from the optical disk using IOCTL
109  * Returns 1 if successful, 0 if not or -1 on error
110  */
libsmdev_optical_disc_get_table_of_contents_ioctl(libcfile_file_t * device_file,libsmdev_internal_handle_t * internal_handle,libcerror_error_t ** error)111 int libsmdev_optical_disc_get_table_of_contents_ioctl(
112      libcfile_file_t *device_file,
113      libsmdev_internal_handle_t *internal_handle,
114      libcerror_error_t **error )
115 {
116 	struct cdrom_tochdr toc_header;
117 	struct cdrom_tocentry toc_entry;
118 
119 	static char *function        = "libsmdev_optical_disc_get_table_of_contents_ioctl";
120 	ssize_t read_count           = 0;
121 	uint32_t last_session_size   = 0;
122 	uint32_t last_session_offset = 0;
123 	uint32_t last_track_size     = 0;
124 	uint32_t last_track_offset   = 0;
125 	uint32_t offset              = 0;
126 	uint16_t entry_index         = 0;
127 	uint8_t first_entry          = 0;
128 	uint8_t last_entry           = 0;
129 	uint8_t last_track_type      = 0;
130 	uint8_t session_index        = 0;
131 	uint8_t track_index          = 0;
132 	uint8_t track_type           = 0;
133 	int result                   = 0;
134 
135 	if( device_file == NULL )
136 	{
137 		libcerror_error_set(
138 		 error,
139 		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
140 		 LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
141 		 "%s: invalid device file.",
142 		 function );
143 
144 		return( -1 );
145 	}
146 	if( internal_handle == NULL )
147 	{
148 		libcerror_error_set(
149 		 error,
150 		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
151 		 LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
152 		 "%s: invalid handle.",
153 		 function );
154 
155 		return( -1 );
156 	}
157 	read_count = libcfile_file_io_control_read(
158 	              device_file,
159 	              CDROMREADTOCHDR,
160 	              NULL,
161 	              0,
162 	              (uint8_t *) &toc_header,
163 	              sizeof( struct cdrom_tochdr ),
164 	              error );
165 
166 	if( read_count == -1 )
167 	{
168 		libcerror_error_set(
169 		 error,
170 		 LIBCERROR_ERROR_DOMAIN_IO,
171 		 LIBCERROR_IO_ERROR_IOCTL_FAILED,
172 		 "%s: unable to query device for: CDROMREADTOCHDR.",
173 		 function );
174 
175 #if defined( HAVE_DEBUG_OUTPUT )
176 		if( libcnotify_verbose != 0 )
177 		{
178 			if( ( error != NULL )
179 			 && ( *error != NULL ) )
180 			{
181 				libcnotify_print_error_backtrace(
182 				 *error );
183 			}
184 		}
185 #endif
186 		libcerror_error_free(
187 		 error );
188 
189 		return( 0 );
190 	}
191 	first_entry = toc_header.cdth_trk0;
192 	last_entry  = toc_header.cdth_trk1;
193 
194 #if defined( HAVE_DEBUG_OUTPUT )
195 	if( libcnotify_verbose != 0 )
196 	{
197 		libcnotify_printf(
198 		 "%s: number of entries\t: %" PRIu8 "\n",
199 		 function,
200 		 last_entry );
201 	}
202 #endif
203 	for( entry_index = (uint16_t) first_entry;
204 	     entry_index <= (uint16_t) last_entry;
205 	     entry_index++ )
206 	{
207 		if( memory_set(
208 		     &toc_entry,
209 		     0,
210 		     sizeof( struct cdrom_tocentry ) ) == NULL )
211 		{
212 			libcerror_error_set(
213 			 error,
214 			 LIBCERROR_ERROR_DOMAIN_MEMORY,
215 			 LIBCERROR_MEMORY_ERROR_SET_FAILED,
216 			 "%s: unable to clear TOC entry.",
217 			 function );
218 
219 			goto on_error;
220 		}
221 		toc_entry.cdte_track  = (uint8_t) entry_index;
222 		toc_entry.cdte_format = CDROM_LBA;
223 
224 		read_count = libcfile_file_io_control_read(
225 		              device_file,
226 		              CDROMREADTOCENTRY,
227 		              NULL,
228 		              0,
229 		              (uint8_t *) &toc_entry,
230 		              sizeof( struct cdrom_tocentry ),
231 		              error );
232 
233 		if( read_count == -1 )
234 		{
235 			libcerror_error_set(
236 			 error,
237 			 LIBCERROR_ERROR_DOMAIN_IO,
238 			 LIBCERROR_IO_ERROR_IOCTL_FAILED,
239 			 "%s: unable to query device for: CDROMREADTOCENTRY.",
240 			 function );
241 
242 #if defined( HAVE_DEBUG_OUTPUT )
243 			if( libcnotify_verbose != 0 )
244 			{
245 				if( ( error != NULL )
246 				 && ( *error != NULL ) )
247 				{
248 					libcnotify_print_error_backtrace(
249 					 *error );
250 				}
251 			}
252 #endif
253 			libcerror_error_free(
254 			 error );
255 
256 			break;
257 		}
258 		if( toc_entry.cdte_format == CDROM_LBA )
259 		{
260 			offset = (uint32_t) toc_entry.cdte_addr.lba;
261 		}
262 		else if( toc_entry.cdte_format == CDROM_MSF )
263 		{
264 			libsmdev_optical_disc_copy_msf_to_lba(
265 			 toc_entry.cdte_addr.msf.minute,
266 			 toc_entry.cdte_addr.msf.second,
267 			 toc_entry.cdte_addr.msf.frame,
268 			 offset );
269 		}
270 		else
271 		{
272 			libcerror_error_set(
273 			 error,
274 			 LIBCERROR_ERROR_DOMAIN_RUNTIME,
275 			 LIBCERROR_RUNTIME_ERROR_UNSUPPORTED_VALUE,
276 			 "%s: unsupported CDTE format.",
277 			 function );
278 
279 			goto on_error;
280 		}
281 		if( ( toc_entry.cdte_ctrl & CDROM_DATA_TRACK ) == 0 )
282 		{
283 			track_type = LIBSMDEV_TRACK_TYPE_AUDIO;
284 		}
285 		else
286 		{
287 			track_type = LIBSMDEV_TRACK_TYPE_MODE1_2048;
288 		}
289 #if defined( HAVE_DEBUG_OUTPUT )
290 		if( libcnotify_verbose != 0 )
291 		{
292 			libcnotify_printf(
293 			 "%s: entry: %" PRIu16 "",
294 			 function,
295 			 entry_index );
296 
297 			if( ( toc_entry.cdte_ctrl & CDROM_DATA_TRACK ) == 0 )
298 			{
299 				libcnotify_printf(
300 				 " (audio)" );
301 			}
302 			else
303 			{
304 				libcnotify_printf(
305 				 " (data)" );
306 			}
307 			if( toc_entry.cdte_format == CDROM_LBA )
308 			{
309 				libcnotify_printf(
310 				 " start\t: %" PRIu32 "",
311 				 toc_entry.cdte_addr.lba );
312 			}
313 			else if( toc_entry.cdte_format == CDROM_MSF )
314 			{
315 				libcnotify_printf(
316 				 " start\t: %02" PRIu8 ":%02" PRIu8 ".%02" PRIu8 "",
317 				 toc_entry.cdte_addr.msf.minute,
318 				 toc_entry.cdte_addr.msf.second,
319 				 toc_entry.cdte_addr.msf.frame );
320 			}
321 			libcnotify_printf(
322 			 " (offset: %" PRIu32 ")\n",
323 			 offset );
324 		}
325 #endif
326 		if( entry_index > first_entry )
327 		{
328 			if( ( offset < last_track_offset )
329 			 || ( offset < last_session_offset ) )
330 			{
331 				libcerror_error_set(
332 				 error,
333 				 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
334 				 LIBCERROR_ARGUMENT_ERROR_VALUE_OUT_OF_BOUNDS,
335 				 "%s: invalid offset value out of bounds.",
336 				 function );
337 
338 				goto on_error;
339 			}
340 			last_track_size = offset - last_track_offset;
341 
342 			if( ( last_track_type == LIBSMDEV_TRACK_TYPE_MODE1_2048 )
343 			 || ( last_track_type != track_type ) )
344 			{
345 				if( session_index == 0 )
346 				{
347 					if( last_track_size < 11400 )
348 					{
349 						libcerror_error_set(
350 						 error,
351 						 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
352 						 LIBCERROR_ARGUMENT_ERROR_VALUE_OUT_OF_BOUNDS,
353 						 "%s: invalid last track size value out of bounds.",
354 						 function );
355 
356 						goto on_error;
357 					}
358 					last_track_size -= 11400;
359 				}
360 				else
361 				{
362 					if( last_track_size < 6900 )
363 					{
364 						libcerror_error_set(
365 						 error,
366 						 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
367 						 LIBCERROR_ARGUMENT_ERROR_VALUE_OUT_OF_BOUNDS,
368 						 "%s: invalid last track size value out of bounds.",
369 						 function );
370 
371 						goto on_error;
372 					}
373 					last_track_size -= 6900;
374 				}
375 			}
376 			if( libsmdev_handle_append_track(
377 			     internal_handle,
378 			     last_track_offset,
379 			     last_track_size,
380 			     last_track_type,
381 			     error ) != 1 )
382 			{
383 				libcerror_error_set(
384 				 error,
385 				 LIBCERROR_ERROR_DOMAIN_RUNTIME,
386 				 LIBCERROR_RUNTIME_ERROR_APPEND_FAILED,
387 				 "%s: unable to append track: %" PRIu8 ".",
388 				 function,
389 				 track_index );
390 
391 				goto on_error;
392 			}
393 			track_index++;
394 
395 			if( ( last_track_type == LIBSMDEV_TRACK_TYPE_MODE1_2048 )
396 			 || ( last_track_type != track_type ) )
397 			{
398 				last_session_size = offset - last_session_offset;
399 
400 				if( libsmdev_handle_append_session(
401 				     internal_handle,
402 				     last_session_offset,
403 				     last_session_size,
404 				     error ) != 1 )
405 				{
406 					libcerror_error_set(
407 					 error,
408 					 LIBCERROR_ERROR_DOMAIN_RUNTIME,
409 					 LIBCERROR_RUNTIME_ERROR_APPEND_FAILED,
410 					 "%s: unable to append session: %" PRIu8 ".",
411 					 function,
412 					 session_index );
413 
414 					goto on_error;
415 				}
416 				session_index++;
417 
418 				last_session_offset = offset;
419 			}
420 		}
421 		last_track_offset = offset;
422 		last_track_type   = track_type;
423 	}
424 	if( read_count != -1 )
425 	{
426 		if( memory_set(
427 		     &toc_entry,
428 		     0,
429 		     sizeof( struct cdrom_tocentry ) ) == NULL )
430 		{
431 			libcerror_error_set(
432 			 error,
433 			 LIBCERROR_ERROR_DOMAIN_MEMORY,
434 			 LIBCERROR_MEMORY_ERROR_SET_FAILED,
435 			 "%s: unable to clear TOC entry.",
436 			 function );
437 
438 			goto on_error;
439 		}
440 		toc_entry.cdte_track  = CDROM_LEADOUT;
441 		toc_entry.cdte_format = CDROM_LBA;
442 
443 		read_count = libcfile_file_io_control_read(
444 			      device_file,
445 			      CDROMREADTOCENTRY,
446 			      NULL,
447 			      0,
448 			      (uint8_t *) &toc_entry,
449 			      sizeof( struct cdrom_tocentry ),
450 			      error );
451 
452 		if( read_count == -1 )
453 		{
454 			libcerror_error_set(
455 			 error,
456 			 LIBCERROR_ERROR_DOMAIN_IO,
457 			 LIBCERROR_IO_ERROR_IOCTL_FAILED,
458 			 "%s: unable to query device for: CDROMREADTOCENTRY.",
459 			 function );
460 
461 #if defined( HAVE_DEBUG_OUTPUT )
462 			if( libcnotify_verbose != 0 )
463 			{
464 				if( ( error != NULL )
465 				 && ( *error != NULL ) )
466 				{
467 					libcnotify_print_error_backtrace(
468 					 *error );
469 				}
470 			}
471 #endif
472 			libcerror_error_free(
473 			 error );
474 		}
475 		else
476 		{
477 			if( toc_entry.cdte_format == CDROM_LBA )
478 			{
479 				offset = (uint32_t) toc_entry.cdte_addr.lba;
480 			}
481 			else if( toc_entry.cdte_format == CDROM_MSF )
482 			{
483 				libsmdev_optical_disc_copy_msf_to_lba(
484 				 toc_entry.cdte_addr.msf.minute,
485 				 toc_entry.cdte_addr.msf.second,
486 				 toc_entry.cdte_addr.msf.frame,
487 				 offset );
488 			}
489 			else
490 			{
491 				libcerror_error_set(
492 				 error,
493 				 LIBCERROR_ERROR_DOMAIN_RUNTIME,
494 				 LIBCERROR_RUNTIME_ERROR_UNSUPPORTED_VALUE,
495 				 "%s: unsupported CDTE format.",
496 				 function );
497 
498 				goto on_error;
499 			}
500 #if defined( HAVE_DEBUG_OUTPUT )
501 			if( libcnotify_verbose != 0 )
502 			{
503 				libcnotify_printf(
504 				 "\tLead out" );
505 
506 				if( ( toc_entry.cdte_ctrl & CDROM_DATA_TRACK ) == 0 )
507 				{
508 					libcnotify_printf(
509 					 " (audio)" );
510 				}
511 				else
512 				{
513 					libcnotify_printf(
514 					 " (data)" );
515 				}
516 				if( toc_entry.cdte_format == CDROM_LBA )
517 				{
518 					libcnotify_printf(
519 					 " start:\t%" PRIu32 "",
520 					 toc_entry.cdte_addr.lba );
521 				}
522 				else if( toc_entry.cdte_format == CDROM_MSF )
523 				{
524 					libcnotify_printf(
525 					 " start:\t%02" PRIu8 ":%02" PRIu8 ".02%" PRIu8 "",
526 					 toc_entry.cdte_addr.msf.minute,
527 					 toc_entry.cdte_addr.msf.second,
528 					 toc_entry.cdte_addr.msf.frame );
529 				}
530 				libcnotify_printf(
531 				 " (offset: %" PRIu32 ")\n\n",
532 				 offset );
533 			}
534 #endif
535 			if( ( offset < last_track_offset )
536 			 || ( offset < last_session_offset ) )
537 			{
538 				libcerror_error_set(
539 				 error,
540 				 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
541 				 LIBCERROR_ARGUMENT_ERROR_VALUE_OUT_OF_BOUNDS,
542 				 "%s: invalid offset value out of bounds.",
543 				 function );
544 
545 				goto on_error;
546 			}
547 			last_track_size = offset - last_track_offset;
548 
549 			if( libsmdev_handle_append_track(
550 			     internal_handle,
551 			     last_track_offset,
552 			     last_track_size,
553 			     last_track_type,
554 			     error ) != 1 )
555 			{
556 				libcerror_error_set(
557 				 error,
558 				 LIBCERROR_ERROR_DOMAIN_RUNTIME,
559 				 LIBCERROR_RUNTIME_ERROR_APPEND_FAILED,
560 				 "%s: unable to append last track: %" PRIu8 ".",
561 				 function,
562 				 track_index );
563 
564 				goto on_error;
565 			}
566 			last_session_size = offset - last_session_offset;
567 
568 			if( libsmdev_handle_append_session(
569 			     internal_handle,
570 			     last_session_offset,
571 			     last_session_size,
572 			     error ) != 1 )
573 			{
574 				libcerror_error_set(
575 				 error,
576 				 LIBCERROR_ERROR_DOMAIN_RUNTIME,
577 				 LIBCERROR_RUNTIME_ERROR_APPEND_FAILED,
578 				 "%s: unable to append session: %" PRIu8 ".",
579 				 function,
580 				 session_index );
581 
582 				goto on_error;
583 			}
584 			result = 1;
585 		}
586 	}
587 	if( result == 0 )
588 	{
589 		if( libcdata_array_empty(
590 		     internal_handle->tracks_array,
591 		     (int (*)(intptr_t **, libcerror_error_t **)) &libsmdev_track_value_free,
592 		     error ) != 1 )
593 		{
594 			libcerror_error_set(
595 			 error,
596 			 LIBCERROR_ERROR_DOMAIN_RUNTIME,
597 			 LIBCERROR_RUNTIME_ERROR_FINALIZE_FAILED,
598 			 "%s: unable to empty tracks array.",
599 			 function );
600 
601 			goto on_error;
602 		}
603 		if( libcdata_array_empty(
604 		     internal_handle->sessions_array,
605 		     (int (*)(intptr_t **, libcerror_error_t **)) &libsmdev_sector_range_free,
606 		     error ) != 1 )
607 		{
608 			libcerror_error_set(
609 			 error,
610 			 LIBCERROR_ERROR_DOMAIN_RUNTIME,
611 			 LIBCERROR_RUNTIME_ERROR_FINALIZE_FAILED,
612 			 "%s: unable to empty sessions array.",
613 			 function );
614 
615 			goto on_error;
616 		}
617 	}
618 	return( result );
619 
620 on_error:
621 	libcdata_array_empty(
622 	 internal_handle->tracks_array,
623 	 (int (*)(intptr_t **, libcerror_error_t **)) &libsmdev_track_value_free,
624 	 NULL );
625 
626 	libcdata_array_empty(
627 	 internal_handle->sessions_array,
628 	 (int (*)(intptr_t **, libcerror_error_t **)) &libsmdev_sector_range_free,
629 	 NULL );
630 
631 	return( -1 );
632 }
633 
634 /* Retrieves the table of contents from the optical disk using the SCSI READ TOC command
635  * Returns 1 if successful, 0 if not or -1 on error
636  */
libsmdev_optical_disc_get_table_of_contents_scsi(libcfile_file_t * device_file,libsmdev_internal_handle_t * internal_handle,libcerror_error_t ** error)637 int libsmdev_optical_disc_get_table_of_contents_scsi(
638      libcfile_file_t *device_file,
639      libsmdev_internal_handle_t *internal_handle,
640      libcerror_error_t **error )
641 {
642 	uint8_t track_info_data[ 64 ];
643 
644 	uint8_t *toc_data            = NULL;
645 	uint8_t *toc_entries         = NULL;
646 	static char *function        = "libsmdev_optical_disc_get_table_of_contents_scsi";
647 	void *reallocation           = NULL;
648 	size_t toc_data_offset       = 0;
649 	size_t toc_data_size         = 0;
650 	ssize_t read_count           = 0;
651 	uint32_t lead_out_size       = 0;
652 	uint32_t lead_out_offset     = 0;
653 	uint32_t last_track_offset   = 0;
654 	uint32_t track_offset        = 0;
655 	uint32_t session_size        = 0;
656 	uint32_t session_offset      = 0;
657 	uint32_t next_session_offset = 0;
658 	uint16_t entry_index         = 0;
659 	uint8_t first_track_number   = 0;
660 	uint8_t last_track_number    = 0;
661 	uint8_t lead_out_index       = 0;
662 	uint8_t number_of_sessions   = 0;
663 	uint8_t session_index        = 0;
664 	uint8_t track_index          = 0;
665 	uint8_t track_number         = 0;
666 	uint8_t track_type           = 0;
667 	int result                   = 0;
668 
669 	if( device_file == NULL )
670 	{
671 		libcerror_error_set(
672 		 error,
673 		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
674 		 LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
675 		 "%s: invalid device file.",
676 		 function );
677 
678 		return( -1 );
679 	}
680 	if( internal_handle == NULL )
681 	{
682 		libcerror_error_set(
683 		 error,
684 		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
685 		 LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
686 		 "%s: invalid handle.",
687 		 function );
688 
689 		return( -1 );
690 	}
691 	toc_data_size = 1024;
692 
693 	toc_data = (uint8_t *) memory_allocate(
694 	                        sizeof( uint8_t ) * toc_data_size );
695 
696 	if( toc_data == NULL )
697 	{
698 		libcerror_error_set(
699 		 error,
700 		 LIBCERROR_ERROR_DOMAIN_MEMORY,
701 		 LIBCERROR_MEMORY_ERROR_INSUFFICIENT,
702 		 "%s: unable to create TOC data.",
703 		 function );
704 
705 		goto on_error;
706 	}
707 	if( memory_set(
708 	     toc_data,
709 	     0,
710 	     sizeof( uint8_t ) * toc_data_size ) == NULL )
711 	{
712 		libcerror_error_set(
713 		 error,
714 		 LIBCERROR_ERROR_DOMAIN_MEMORY,
715 		 LIBCERROR_MEMORY_ERROR_SET_FAILED,
716 		 "%s: unable to clear TOC data.",
717 		 function );
718 
719 		goto on_error;
720 	}
721 	read_count = libsmdev_scsi_read_toc(
722 	              device_file,
723 	              LIBSMDEV_SCSI_TOC_CDB_FORMAT_RAW_TOC,
724 	              toc_data,
725 	              toc_data_size,
726 	              error );
727 
728 	if( read_count == -1 )
729 	{
730 		libcerror_error_set(
731 		 error,
732 		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
733 		 LIBCERROR_RUNTIME_ERROR_GET_FAILED,
734 		 "%s: unable to retrieve TOC.",
735 		 function );
736 
737 #if defined( HAVE_DEBUG_OUTPUT )
738 		if( libcnotify_verbose != 0 )
739 		{
740 			if( ( error != NULL )
741 			 && ( *error != NULL ) )
742 			{
743 				libcnotify_print_error_backtrace(
744 				 *error );
745 			}
746 		}
747 #endif
748 		libcerror_error_free(
749 		 error );
750 
751 		byte_stream_copy_to_uint16_big_endian(
752 		 toc_data,
753 		 toc_data_size );
754 
755 		if( toc_data_size > 1024 )
756 		{
757 			reallocation = memory_reallocate(
758 					toc_data,
759 					sizeof( uint8_t ) * toc_data_size );
760 
761 			if( reallocation == NULL )
762 			{
763 				libcerror_error_set(
764 				 error,
765 				 LIBCERROR_ERROR_DOMAIN_MEMORY,
766 				 LIBCERROR_MEMORY_ERROR_INSUFFICIENT,
767 				 "%s: unable to resize TOC data.",
768 				 function );
769 
770 				goto on_error;
771 			}
772 			toc_data = (uint8_t *) reallocation;
773 
774 			read_count = libsmdev_scsi_read_toc(
775 			              device_file,
776 			              LIBSMDEV_SCSI_TOC_CDB_FORMAT_RAW_TOC,
777 			              toc_data,
778 			              toc_data_size,
779 			              error );
780 
781 			if( read_count == -1 )
782 			{
783 				libcerror_error_set(
784 				 error,
785 				 LIBCERROR_ERROR_DOMAIN_RUNTIME,
786 				 LIBCERROR_RUNTIME_ERROR_GET_FAILED,
787 				 "%s: unable to retrieve TOC.",
788 				 function );
789 
790 #if defined( HAVE_DEBUG_OUTPUT )
791 				if( libcnotify_verbose != 0 )
792 				{
793 					if( ( error != NULL )
794 					 && ( *error != NULL ) )
795 					{
796 						libcnotify_print_error_backtrace(
797 						 *error );
798 					}
799 				}
800 #endif
801 				libcerror_error_free(
802 				 error );
803 			}
804 		}
805 	}
806 	toc_data_size = (size_t) read_count;
807 
808 	if( toc_data_size > 4 )
809 	{
810 #if defined( HAVE_DEBUG_OUTPUT )
811 		if( libcnotify_verbose != 0 )
812 		{
813 			libcnotify_printf(
814 			 "%s: header:\n",
815 			 function );
816 			libcnotify_print_data(
817 			 toc_data,
818 			 4,
819 			 0 );
820 		}
821 #endif
822 		number_of_sessions = (uint16_t) toc_data[ 3 ];
823 
824 #if defined( HAVE_DEBUG_OUTPUT )
825 		if( libcnotify_verbose != 0 )
826 		{
827 			libcnotify_printf(
828 			 "%s: number of sessions\t\t\t: %" PRIu8 "\n",
829 			 function,
830 			 number_of_sessions );
831 
832 			libcnotify_printf(
833 			 "\n" );
834 		}
835 #endif
836 		toc_entries     = &( toc_data[ 4 ] );
837 		toc_data_offset = 4;
838 
839 		while( toc_data_offset < (size_t) toc_data_size )
840 		{
841 #if defined( HAVE_DEBUG_OUTPUT )
842 			if( libcnotify_verbose != 0 )
843 			{
844 				libcnotify_printf(
845 				 "%s: entry: %02" PRIu16 ":\n",
846 				 function,
847 				 entry_index );
848 				libcnotify_print_data(
849 				 toc_entries,
850 				 11,
851 				 0 );
852 			}
853 #endif
854 			if( toc_entries[ 3 ] <= 0x63 )
855 			{
856 				libsmdev_optical_disc_copy_msf_to_lba(
857 				 toc_entries[ 8 ],
858 				 toc_entries[ 9 ],
859 				 toc_entries[ 10 ],
860 				 track_offset );
861 			}
862 			else if( toc_entries[ 3 ] == 0xa0 )
863 			{
864 				first_track_number = toc_entries[ 8 ];
865 			}
866 			else if( toc_entries[ 3 ] == 0xa1 )
867 			{
868 				last_track_number = toc_entries[ 8 ];
869 			}
870 			else if( toc_entries[ 3 ] == 0xa2 )
871 			{
872 				libsmdev_optical_disc_copy_msf_to_lba(
873 				 toc_entries[ 8 ],
874 				 toc_entries[ 9 ],
875 				 toc_entries[ 10 ],
876 				 lead_out_offset );
877 			}
878 			else if( toc_entries[ 3 ] == 0xb0 )
879 			{
880 				libsmdev_optical_disc_copy_absolute_msf_to_lba(
881 				 toc_entries[ 4 ],
882 				 toc_entries[ 5 ],
883 				 toc_entries[ 6 ],
884 				 next_session_offset );
885 			}
886 #if defined( HAVE_DEBUG_OUTPUT )
887 			if( libcnotify_verbose != 0 )
888 			{
889 				if( toc_entries[ 3 ] <= 0x63 )
890 				{
891 					libcnotify_printf(
892 					 "%s: session: %02" PRIu16 " track: %02" PRIu8 "\t\t\t: %02" PRIu8 ":%02" PRIu8 ".%02" PRIu8 " (offset: %" PRIu32 ")\n",
893 					 function,
894 					 toc_entries[ 0 ],
895 					 toc_entries[ 3 ],
896 					 toc_entries[ 4 ],
897 					 toc_entries[ 5 ],
898 					 toc_entries[ 6 ],
899 					 track_offset );
900 				}
901 				else if( toc_entries[ 3 ] == 0xa0 )
902 				{
903 					libcnotify_printf(
904 					 "%s: session: %02" PRIu8 " first track number\t: %" PRIu8 "\n",
905 					 function,
906 					 toc_entries[ 0 ],
907 					 first_track_number );
908 				}
909 				else if( toc_entries[ 3 ] == 0xa1 )
910 				{
911 					libcnotify_printf(
912 					 "%s: session: %02" PRIu8 " last track number\t\t: %" PRIu8 "\n",
913 					 function,
914 					 toc_entries[ 0 ],
915 					 last_track_number );
916 				}
917 				else if( toc_entries[ 3 ] == 0xa2 )
918 				{
919 					libcnotify_printf(
920 					 "%s: session: %02" PRIu8 " lead out\t\t\t: %02" PRIu8 ":%02" PRIu8 ".%02" PRIu8 " (offset: %" PRIu32 ")\n",
921 					 function,
922 					 toc_entries[ 0 ],
923 					 toc_entries[ 8 ],
924 					 toc_entries[ 9 ],
925 					 toc_entries[ 10 ],
926 					 lead_out_offset );
927 				}
928 				else if( toc_entries[ 3 ] == 0xb0 )
929 				{
930 					libcnotify_printf(
931 					 "%s: session: %02" PRIu16 " end\t\t\t: %02" PRIu8 ":%02" PRIu8 ".%02" PRIu8 " (offset: %" PRIu32 ")\n",
932 					 function,
933 					 toc_entries[ 0 ],
934 					 toc_entries[ 4 ],
935 					 toc_entries[ 5 ],
936 					 toc_entries[ 6 ],
937 					 next_session_offset );
938 				}
939 				libcnotify_printf(
940 				 "\n" );
941 			}
942 #endif
943 			if( ( toc_entries[ 3 ] <= 0x63 )
944 			 || ( toc_entries[ 3 ] == 0xb0 ) )
945 			{
946 				if( track_number >= first_track_number )
947 				{
948 			 		if( toc_entries[ 3 ] == 0xb0 )
949 					{
950 						track_offset = lead_out_offset;
951 					}
952 					if( track_offset < last_track_offset )
953 					{
954 						libcerror_error_set(
955 						 error,
956 						 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
957 						 LIBCERROR_ARGUMENT_ERROR_VALUE_OUT_OF_BOUNDS,
958 						 "%s: invalid track offset value out of bounds.",
959 						 function );
960 
961 						goto on_error;
962 					}
963 					if( ( track_index + 1 ) != track_number )
964 					{
965 						libcerror_error_set(
966 						 error,
967 						 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
968 						 LIBCERROR_ARGUMENT_ERROR_VALUE_OUT_OF_BOUNDS,
969 						 "%s: invalid track number value out of bounds.",
970 						 function );
971 
972 						goto on_error;
973 					}
974 			 		if( toc_entries[ 3 ] == 0xa2 )
975 					{
976 						if( track_number != last_track_number )
977 						{
978 							libcerror_error_set(
979 							 error,
980 							 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
981 							 LIBCERROR_ARGUMENT_ERROR_VALUE_OUT_OF_BOUNDS,
982 							 "%s: invalid track number value out of bounds.",
983 							 function );
984 
985 							goto on_error;
986 						}
987 					}
988 					if( memory_set(
989 					     track_info_data,
990 					     0,
991 					     64 ) == NULL )
992 					{
993 						libcerror_error_set(
994 						 error,
995 						 LIBCERROR_ERROR_DOMAIN_MEMORY,
996 						 LIBCERROR_MEMORY_ERROR_SET_FAILED,
997 						 "%s: unable to clear track info data.",
998 						 function );
999 
1000 						goto on_error;
1001 					}
1002 					read_count = libsmdev_scsi_read_track_information(
1003 					              device_file,
1004 					              last_track_offset,
1005 					              track_info_data,
1006 					              64,
1007 					              error );
1008 
1009 					if( read_count == -1 )
1010 					{
1011 						libcerror_error_set(
1012 						 error,
1013 						 LIBCERROR_ERROR_DOMAIN_RUNTIME,
1014 						 LIBCERROR_RUNTIME_ERROR_GET_FAILED,
1015 						 "%s: unable to retrieve track info data: %d.",
1016 						 function,
1017 						 track_index );
1018 
1019 #if defined( HAVE_DEBUG_OUTPUT )
1020 						if( libcnotify_verbose != 0 )
1021 						{
1022 							if( ( error != NULL )
1023 							 && ( *error != NULL ) )
1024 							{
1025 								libcnotify_print_error_backtrace(
1026 								 *error );
1027 							}
1028 						}
1029 #endif
1030 						libcerror_error_free(
1031 						 error );
1032 
1033 						break;
1034 					}
1035 #if defined( HAVE_DEBUG_OUTPUT )
1036 					if( libcnotify_verbose != 0 )
1037 					{
1038 						libcnotify_printf(
1039 						 "%s: track information data: %d:\n",
1040 						 function,
1041 						 track_index );
1042 						libcnotify_print_data(
1043 						 track_info_data,
1044 						 (size_t) read_count,
1045 						 0 );
1046 					}
1047 #endif
1048 					if( track_info_data[ 2 ] != toc_entries[ 0 ] )
1049 					{
1050 						libcerror_error_set(
1051 						 error,
1052 						 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
1053 						 LIBCERROR_ARGUMENT_ERROR_VALUE_OUT_OF_BOUNDS,
1054 						 "%s: invalid track information data - session number value out of bounds.",
1055 						 function );
1056 
1057 						goto on_error;
1058 					}
1059 					if( track_info_data[ 3 ] != track_number )
1060 					{
1061 						libcerror_error_set(
1062 						 error,
1063 						 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
1064 						 LIBCERROR_ARGUMENT_ERROR_VALUE_OUT_OF_BOUNDS,
1065 						 "%s: invalid track information data - track number value out of bounds.",
1066 						 function );
1067 
1068 						goto on_error;
1069 					}
1070 					track_type = LIBSMDEV_TRACK_TYPE_UNKNOWN;
1071 
1072 					if( ( track_info_data[ 5 ] & 0x04 ) != 0 )
1073 					{
1074 						if( ( track_info_data[ 5 ] & 0x08 ) == 0 )
1075 						{
1076 							if( ( track_info_data[ 6 ] & 0x0f ) == 1 )
1077 							{
1078 								track_type = LIBSMDEV_TRACK_TYPE_MODE1_2048;
1079 							}
1080 							else if( ( track_info_data[ 6 ] & 0x0f ) == 2 )
1081 							{
1082 								track_type = LIBSMDEV_TRACK_TYPE_MODE2_2048;
1083 							}
1084 						}
1085 					}
1086 					else
1087 					{
1088 						track_type = LIBSMDEV_TRACK_TYPE_AUDIO;
1089 					}
1090 					if( libsmdev_handle_append_track(
1091 					     internal_handle,
1092 					     last_track_offset,
1093 					     track_offset - last_track_offset,
1094 					     track_type,
1095 					     error ) != 1 )
1096 					{
1097 						libcerror_error_set(
1098 						 error,
1099 						 LIBCERROR_ERROR_DOMAIN_RUNTIME,
1100 						 LIBCERROR_RUNTIME_ERROR_APPEND_FAILED,
1101 						 "%s: unable to append track: %d.",
1102 						 function,
1103 						 track_index );
1104 
1105 						goto on_error;
1106 					}
1107 					track_index++;
1108 				}
1109 				last_track_offset = track_offset;
1110 
1111 			 	if( toc_entries[ 3 ] != 0xb0 )
1112 				{
1113 					track_number = toc_entries[ 3 ];
1114 				}
1115 			}
1116 			if( toc_entries[ 3 ] == 0xb0 )
1117 			{
1118 				if( session_offset >= next_session_offset )
1119 				{
1120 					libcerror_error_set(
1121 					 error,
1122 					 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
1123 					 LIBCERROR_ARGUMENT_ERROR_VALUE_OUT_OF_BOUNDS,
1124 					 "%s: invalid session offset value out of bounds.",
1125 					 function );
1126 
1127 					goto on_error;
1128 				}
1129 				if( ( session_index + 1 ) != toc_entries[ 0 ] )
1130 				{
1131 					libcerror_error_set(
1132 					 error,
1133 					 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
1134 					 LIBCERROR_ARGUMENT_ERROR_VALUE_OUT_OF_BOUNDS,
1135 					 "%s: invalid session number value out of bounds.",
1136 					 function );
1137 
1138 					goto on_error;
1139 				}
1140 				lead_out_size = 0;
1141 
1142 				if( ( lead_out_offset >= session_offset )
1143 				 && ( lead_out_offset < next_session_offset ) )
1144 				{
1145 					lead_out_size = next_session_offset - lead_out_offset;
1146 
1147 					if( libsmdev_handle_append_lead_out(
1148 					     internal_handle,
1149 					     lead_out_offset,
1150 					     lead_out_size,
1151 					     error ) != 1 )
1152 					{
1153 						libcerror_error_set(
1154 						 error,
1155 						 LIBCERROR_ERROR_DOMAIN_RUNTIME,
1156 						 LIBCERROR_RUNTIME_ERROR_APPEND_FAILED,
1157 						 "%s: unable to append lead_out: %d.",
1158 						 function,
1159 						 lead_out_index );
1160 
1161 						goto on_error;
1162 					}
1163 					lead_out_index++;
1164 				}
1165 				session_size = next_session_offset - session_offset;
1166 
1167 				if( ( session_index + 1 ) == number_of_sessions )
1168 				{
1169 					session_size -= lead_out_size;
1170 				}
1171 				if( libsmdev_handle_append_session(
1172 				     internal_handle,
1173 				     session_offset,
1174 				     session_size,
1175 				     error ) != 1 )
1176 				{
1177 					libcerror_error_set(
1178 					 error,
1179 					 LIBCERROR_ERROR_DOMAIN_RUNTIME,
1180 					 LIBCERROR_RUNTIME_ERROR_APPEND_FAILED,
1181 					 "%s: unable to append session: %d.",
1182 					 function,
1183 					 session_index );
1184 
1185 					goto on_error;
1186 				}
1187 				session_offset = next_session_offset;
1188 
1189 				session_index++;
1190 			}
1191 			toc_entries     += 11;
1192 			toc_data_offset += 11;
1193 
1194 			entry_index++;
1195 		}
1196 		if( ( track_index + 1 ) == track_number )
1197 		{
1198 			if( track_offset < last_track_offset )
1199 			{
1200 				libcerror_error_set(
1201 				 error,
1202 				 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
1203 				 LIBCERROR_ARGUMENT_ERROR_VALUE_OUT_OF_BOUNDS,
1204 				 "%s: invalid track offset value out of bounds.",
1205 				 function );
1206 
1207 				goto on_error;
1208 			}
1209 			if( memory_set(
1210 			     track_info_data,
1211 			     0,
1212 			     64 ) == NULL )
1213 			{
1214 				libcerror_error_set(
1215 				 error,
1216 				 LIBCERROR_ERROR_DOMAIN_MEMORY,
1217 				 LIBCERROR_MEMORY_ERROR_SET_FAILED,
1218 				 "%s: unable to clear track info data.",
1219 				 function );
1220 
1221 				goto on_error;
1222 			}
1223 			read_count = libsmdev_scsi_read_track_information(
1224 			              device_file,
1225 			              last_track_offset,
1226 			              track_info_data,
1227 			              64,
1228 			              error );
1229 
1230 			if( read_count == -1 )
1231 			{
1232 				libcerror_error_set(
1233 				 error,
1234 				 LIBCERROR_ERROR_DOMAIN_RUNTIME,
1235 				 LIBCERROR_RUNTIME_ERROR_GET_FAILED,
1236 				 "%s: unable to retrieve track info data: %d.",
1237 				 function,
1238 				 track_index );
1239 
1240 #if defined( HAVE_DEBUG_OUTPUT )
1241 				if( libcnotify_verbose != 0 )
1242 				{
1243 					if( ( error != NULL )
1244 					 && ( *error != NULL ) )
1245 					{
1246 						libcnotify_print_error_backtrace(
1247 						 *error );
1248 					}
1249 				}
1250 #endif
1251 				libcerror_error_free(
1252 				 error );
1253 			}
1254 			else
1255 			{
1256 #if defined( HAVE_DEBUG_OUTPUT )
1257 				if( libcnotify_verbose != 0 )
1258 				{
1259 					libcnotify_printf(
1260 					 "%s: track information data: %d:\n",
1261 					 function,
1262 					 track_index );
1263 					libcnotify_print_data(
1264 					 track_info_data,
1265 					 (size_t) read_count,
1266 					 0 );
1267 				}
1268 #endif
1269 				if( track_info_data[ 2 ] != toc_entries[ 0 ] )
1270 				{
1271 					libcerror_error_set(
1272 					 error,
1273 					 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
1274 					 LIBCERROR_ARGUMENT_ERROR_VALUE_OUT_OF_BOUNDS,
1275 					 "%s: invalid track information data - session number value out of bounds.",
1276 					 function );
1277 
1278 					goto on_error;
1279 				}
1280 				if( track_info_data[ 3 ] != toc_entries[ 3 ] )
1281 				{
1282 					libcerror_error_set(
1283 					 error,
1284 					 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
1285 					 LIBCERROR_ARGUMENT_ERROR_VALUE_OUT_OF_BOUNDS,
1286 					 "%s: invalid track information data - track number value out of bounds.",
1287 					 function );
1288 
1289 					goto on_error;
1290 				}
1291 				track_type = LIBSMDEV_TRACK_TYPE_UNKNOWN;
1292 
1293 				if( ( track_info_data[ 5 ] & 0x04 ) != 0 )
1294 				{
1295 					if( ( track_info_data[ 5 ] & 0x08 ) == 0 )
1296 					{
1297 						if( ( track_info_data[ 6 ] & 0x0f ) == 1 )
1298 						{
1299 							track_type = LIBSMDEV_TRACK_TYPE_MODE1_2048;
1300 						}
1301 						else if( ( track_info_data[ 6 ] & 0x0f ) == 2 )
1302 						{
1303 							track_type = LIBSMDEV_TRACK_TYPE_MODE2_2048;
1304 						}
1305 					}
1306 				}
1307 				else
1308 				{
1309 					track_type = LIBSMDEV_TRACK_TYPE_AUDIO;
1310 				}
1311 				if( libsmdev_handle_append_track(
1312 				     internal_handle,
1313 				     last_track_offset,
1314 				     track_offset - last_track_offset,
1315 				     track_type,
1316 				     error ) != 1 )
1317 				{
1318 					libcerror_error_set(
1319 					 error,
1320 					 LIBCERROR_ERROR_DOMAIN_RUNTIME,
1321 					 LIBCERROR_RUNTIME_ERROR_APPEND_FAILED,
1322 					 "%s: unable to append last track: %d.",
1323 					 function,
1324 					 track_index );
1325 
1326 					goto on_error;
1327 				}
1328 			}
1329 			if( session_index != number_of_sessions )
1330 			{
1331 				libcerror_error_set(
1332 				 error,
1333 				 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
1334 				 LIBCERROR_ARGUMENT_ERROR_VALUE_OUT_OF_BOUNDS,
1335 				 "%s: invalid session index value out of bounds.",
1336 				 function );
1337 
1338 				goto on_error;
1339 			}
1340 			result = 1;
1341 		}
1342 	}
1343 	if( result == 0 )
1344 	{
1345 		if( libcdata_array_empty(
1346 		     internal_handle->tracks_array,
1347 		     (int (*)(intptr_t **, libcerror_error_t **)) &libsmdev_track_value_free,
1348 		     error ) != 1 )
1349 		{
1350 			libcerror_error_set(
1351 			 error,
1352 			 LIBCERROR_ERROR_DOMAIN_RUNTIME,
1353 			 LIBCERROR_RUNTIME_ERROR_FINALIZE_FAILED,
1354 			 "%s: unable to empty tracks array.",
1355 			 function );
1356 
1357 			goto on_error;
1358 		}
1359 		if( libcdata_array_empty(
1360 		     internal_handle->lead_outs_array,
1361 		     (int (*)(intptr_t **, libcerror_error_t **)) &libsmdev_sector_range_free,
1362 		     error ) != 1 )
1363 		{
1364 			libcerror_error_set(
1365 			 error,
1366 			 LIBCERROR_ERROR_DOMAIN_RUNTIME,
1367 			 LIBCERROR_RUNTIME_ERROR_FINALIZE_FAILED,
1368 			 "%s: unable to empty lead outs array.",
1369 			 function );
1370 
1371 			goto on_error;
1372 		}
1373 		if( libcdata_array_empty(
1374 		     internal_handle->sessions_array,
1375 		     (int (*)(intptr_t **, libcerror_error_t **)) &libsmdev_sector_range_free,
1376 		     error ) != 1 )
1377 		{
1378 			libcerror_error_set(
1379 			 error,
1380 			 LIBCERROR_ERROR_DOMAIN_RUNTIME,
1381 			 LIBCERROR_RUNTIME_ERROR_FINALIZE_FAILED,
1382 			 "%s: unable to empty sessions array.",
1383 			 function );
1384 
1385 			goto on_error;
1386 		}
1387 	}
1388 	memory_free(
1389 	 toc_data );
1390 
1391 	toc_data = NULL;
1392 
1393 	return( result );
1394 
1395 on_error:
1396 	if( toc_data != NULL )
1397 	{
1398 		memory_free(
1399 		 toc_data );
1400 	}
1401 	libcdata_array_empty(
1402 	 internal_handle->tracks_array,
1403 	 (int (*)(intptr_t **, libcerror_error_t **)) &libsmdev_track_value_free,
1404 	 NULL );
1405 
1406 	libcdata_array_empty(
1407 	 internal_handle->lead_outs_array,
1408 	 (int (*)(intptr_t **, libcerror_error_t **)) &libsmdev_sector_range_free,
1409 	 NULL );
1410 
1411 	libcdata_array_empty(
1412 	 internal_handle->sessions_array,
1413 	 (int (*)(intptr_t **, libcerror_error_t **)) &libsmdev_sector_range_free,
1414 	 NULL );
1415 
1416 	return( -1 );
1417 }
1418 
1419 #endif /* defined( HAVE_LINUX_CDROM_H ) */
1420 
1421