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