]> git.sesse.net Git - vlc/blob - plugins/dvd/dvd_udf.c
3211960cb10712a40f65717abacbde1f13d9c2ca
[vlc] / plugins / dvd / dvd_udf.c
1 /*****************************************************************************
2  * dvd_udf.c: udf filesystem tools.
3  * ---
4  * Mainly used to find asolute logical block adress of *.ifo files. It only
5  * contains the basic udf handling functions
6  *****************************************************************************
7  * Copyright (C) 1998-2001 VideoLAN
8  * $Id: dvd_udf.c,v 1.5 2001/04/15 04:19:57 sam Exp $
9  *
10  * Author: Stéphane Borel <stef@via.ecp.fr>
11  *
12  * based on:
13  *  - dvdudf by Christian Wolff <scarabaeus@convergence.de>
14  *  - fixes by Billy Biggs <vektor@dumbterm.net>
15  *
16  * This program is free software; you can redistribute it and/or modify
17  * it under the terms of the GNU General Public License as published by
18  * the Free Software Foundation; either version 2 of the License, or
19  * (at your option) any later version.
20  * 
21  * This program is distributed in the hope that it will be useful,
22  * but WITHOUT ANY WARRANTY; without even the implied warranty of
23  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24  * GNU General Public License for more details.
25  *
26  * You should have received a copy of the GNU General Public License
27  * along with this program; if not, write to the Free Software
28  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
29  *****************************************************************************/
30
31 /*
32  * Preamble
33  */
34 #include "defs.h"
35
36 #ifdef HAVE_CSS
37 #define MODULE_NAME dvd
38 #else /* HAVE_CSS */
39 #define MODULE_NAME dvdnocss
40 #endif /* HAVE_CSS */
41 #include "modules_inner.h"
42
43 #include <stdio.h>
44 #include <unistd.h>
45 #include <string.h>
46 #include <fcntl.h>
47
48 #include "common.h"
49 #include "intf_msg.h"
50 #include "dvd_css.h"
51 #include "dvd_ifo.h"
52 #include "input_dvd.h"
53
54 #define UDFADshort      1
55 #define UDFADlong       2
56 #define UDFADext        4
57
58 typedef struct partition_s
59 {
60     boolean_t   b_valid;
61     u8          pi_volume_desc[128];
62     u16         i_flags;
63     u16         i_number;
64     u8          pi_contents[32];
65     u32         i_access_type;
66     u32         i_start;
67     u32         i_length;
68     int         i_fd;
69 } partition_t;
70
71 typedef struct ad_s
72 {
73     u32         i_location;
74     u32         i_length;
75     u8          i_flags;
76     u16         i_partition;
77 } ad_t;
78
79 /* for direct data access, LSB first */
80 #define GETN1(p) ((u8)pi_data[p])
81 #define GETN2(p) ((u16)pi_data[p]|((u16)pi_data[(p)+1]<<8))
82 #define GETN4(p) ((u32)pi_data[p]|((u32)pi_data[(p)+1]<<8)|((u32)pi_data[(p)+2]<<16)|((u32)pi_data[(p)+3]<<24))
83 #define GETN(p,n,target) memcpy(target,&pi_data[p],n)
84
85
86 /*****************************************************************************
87  * UDFReadLB: reads absolute Logical Block of the disc
88  * ---
89  * returns number of read bytes on success, 0 on error
90  *****************************************************************************/
91 static int UDFReadLB( int i_fd, off_t i_lba, size_t i_block_count, u8 *pi_data )
92 {
93     if( i_fd < 0 )
94     {
95         return 0;
96     }
97
98     if( lseek( i_fd, i_lba * (off_t) DVD_LB_SIZE, SEEK_SET ) < 0 )
99     {
100         intf_ErrMsg( "UDF: Postion not found" );
101         return 0;
102     }
103
104     return read( i_fd, pi_data, i_block_count *DVD_LB_SIZE);
105 }
106
107
108 /*****************************************************************************
109  * UDFDecode: decode unicode encoded udf data
110  *****************************************************************************/
111 static int UDFDecode( u8 * pi_data, int i_len, char * psz_target )
112 {
113     int     p = 1;
114     int     i = 0;
115
116     if( !( pi_data[0] & 0x18 ) )
117     {
118         psz_target[0] = '\0';
119         return 0;
120     }
121
122     if( pi_data[0] & 0x10 )
123     {
124         /* ignore MSB of unicode16 */
125         p++;
126
127         while( p < i_len )
128         {
129             psz_target[i++] = pi_data[p+=2];
130         }
131     }
132     else
133     {
134         while( p < i_len )
135         {
136             psz_target[i++] = pi_data[p++];
137         }
138     }
139     
140     psz_target[i]='\0';
141
142     return 0;
143 }
144
145 #if 0
146 /**
147  *
148  **/
149
150 int UDFEntity (u8 *data, u8 *Flags, char *Identifier)
151 {
152     Flags[0] = data[0];
153     strncpy (Identifier, &data[1], 5);
154
155     return 0;
156 }
157 #endif
158
159
160 /*****************************************************************************
161  * UDFDescriptor: gives a tag ID from your data to find out what it refers to
162  *****************************************************************************/
163 static int UDFDescriptor( u8 * pi_data, u16 * pi_tag_id )
164 {
165     pi_tag_id[0] = GETN2( 0 );
166     /* TODO: check CRC 'n stuff */
167
168     return 0;
169 }
170
171
172 /*****************************************************************************
173  * UDFExtendAD: main volume information
174  *****************************************************************************/
175 static int UDFExtentAD (u8 * pi_data, u32 * pi_length, u32 * pi_location)
176 {
177     pi_length[0] = GETN4( 0 );
178     pi_location[0] = GETN4( 4 );
179
180     return 0;
181 }
182
183
184 /*****************************************************************************
185  * UDFAD: file set information
186  *****************************************************************************/
187 static int UDFAD( u8 * pi_data, struct ad_s * p_ad, u8 i_type,
188                   struct partition_s partition )
189 {
190     p_ad->i_length = GETN4( 0 );
191     p_ad->i_flags = p_ad->i_length >> 30;
192     p_ad->i_length &= 0x3FFFFFFF;
193
194     switch( i_type )
195     {
196         case UDFADshort:
197             p_ad->i_location = GETN4( 4 );
198             /* use number of current partition */
199             p_ad->i_partition = partition.i_number;
200              break;
201
202         case UDFADlong:
203             p_ad->i_location = GETN4( 4 );
204             p_ad->i_partition = GETN2( 8 );
205             break;
206
207         case UDFADext:
208             p_ad->i_location = GETN4( 12 );
209             p_ad->i_partition = GETN2( 16 );
210             break;
211     }
212
213     return 0;
214 }
215
216
217 /*****************************************************************************
218  * UDFICB: takes Information Control Block from pi_data
219  *****************************************************************************/
220 static int UDFICB( u8 * pi_data, u8 * pi_file_type, u16 * pi_flags)
221 {
222     pi_file_type[0] = GETN1( 11 );
223     pi_flags[0] = GETN2( 18 );
224
225     return 0;
226 }
227
228
229 /*****************************************************************************
230  * UDFPartition: gets partition descriptor
231  *****************************************************************************/
232 static int UDFPartition( u8 * pi_data, u16 * pi_flags, u16 * pi_nb,
233                          char * ps_contents, u32 * pi_start, u32 * pi_length )
234 {
235     pi_flags[0] = GETN2( 20 );
236     pi_nb[0] = GETN2( 22 );
237     GETN( 24, 32, ps_contents );
238     pi_start[0] = GETN4( 188 );
239     pi_length[0] = GETN4( 192 );
240
241     return 0;
242 }
243
244
245 /*****************************************************************************
246  * UDFLogVolume: reads the volume descriptor and checks the parameters
247  * ---
248  * returns 0 on OK, 1 on error
249  *****************************************************************************/
250 static int UDFLogVolume(u8 * pi_data, char * p_volume_descriptor )
251 {
252     u32 i_lb_size;
253     u32 i_MT_L;
254     u32 i_N_PM;
255
256     UDFDecode( &pi_data[84], 128, p_volume_descriptor );
257
258     i_lb_size = GETN4( 212 );        // should be 2048
259     i_MT_L = GETN4( 264 );        // should be 6
260     i_N_PM = GETN4( 268 );        // should be 1
261
262     if( i_lb_size != DVD_LB_SIZE )
263     {
264         intf_ErrMsg( "UDF: Non valid sector size (%d)", i_lb_size );
265         return 1;
266     }
267
268     return 0;
269 }
270
271
272 /*****************************************************************************
273  * UDFFileEntry: fills a ad_t struct with information at pi_data
274  *****************************************************************************/
275 static int UDFFileEntry( u8 * pi_data, u8 * pi_file_type, struct ad_s * p_ad,
276                          struct partition_s partition )
277 {
278     u8      i_file_type;
279     u16     i_flags;
280     u32     i_L_EA;
281     u32     i_L_AD;
282     int     p;
283
284     UDFICB( &pi_data[16], &i_file_type, &i_flags );
285
286     pi_file_type[0] = i_file_type;
287     i_L_EA = GETN4( 168 );
288     i_L_AD = GETN4( 172 );
289     p = 176 + i_L_EA;
290
291     while( p < 176 + i_L_EA + i_L_AD )
292     {
293         switch( i_flags & 0x07 )
294         {
295         case 0:
296             UDFAD( &pi_data[p], p_ad, UDFADshort, partition );
297             p += 0x08;
298             break;
299         case 1:
300             UDFAD( &pi_data[p], p_ad, UDFADlong, partition );
301             p += 0x10;
302             break;
303         case 2:
304             UDFAD( &pi_data[p], p_ad, UDFADext, partition );
305             p += 0x14;
306             break;
307         case 3:
308             switch( i_L_AD )
309             {
310             case 0x08:
311                 UDFAD( &pi_data[p], p_ad, UDFADshort, partition );
312                 break;
313             case 0x10:
314                 UDFAD( &pi_data[p], p_ad, UDFADlong, partition );
315                 break;
316             case 0x14:
317                 UDFAD( &pi_data[p], p_ad, UDFADext, partition );
318                 break;
319             }
320         default:
321             p += i_L_AD;
322             break;
323         }
324     }
325
326     return 0;
327 }
328
329
330 /*****************************************************************************
331  * UDFFileIdentifier: gives filename and characteristics of pi_data
332  *****************************************************************************/
333 static int UDFFileIdentifier( u8 * pi_data, u8 * pi_file_characteristics,
334                               char * psz_filename, struct ad_s * p_file_icb,
335                               struct partition_s partition )
336 {
337     u8      i_L_FI;
338     u16     i_L_IU;
339   
340     pi_file_characteristics[0] = GETN1( 18 );
341     i_L_FI = GETN1( 19 );
342     UDFAD( &pi_data[20], p_file_icb, UDFADlong, partition );
343     i_L_IU = GETN2( 36 );
344
345     if( i_L_FI )
346     {
347         UDFDecode( &pi_data[38+i_L_IU], i_L_FI, psz_filename );
348     }
349     else
350     {
351         psz_filename[0]='\0';
352     }
353
354     return  4 * ( ( 38 + i_L_FI + i_L_IU + 3 ) / 4 );
355 }
356
357
358 /*****************************************************************************
359  * UDFMapICB: Maps ICB to FileAD
360  * ---
361  * ICB: Location of ICB of directory to scan
362  * FileType: Type of the file
363  * File: Location of file the ICB is pointing to
364  * return 1 on success, 0 on error;
365  *****************************************************************************/
366 static int UDFMapICB( struct ad_s icb, u8 * pi_file_type, struct ad_s * p_file,
367                       struct partition_s partition )
368 {
369     u8      pi_lb[DVD_LB_SIZE];
370     u32     i_lba;
371     u16     i_tag_id;
372
373     i_lba = partition.i_start + icb.i_location;
374
375     do
376     {
377         if( !UDFReadLB( partition.i_fd, i_lba++, 1, pi_lb ) )
378         {
379             i_tag_id = 0;
380         }
381         else
382         {
383             UDFDescriptor( pi_lb , &i_tag_id );
384         }
385
386         if( i_tag_id == 261 )
387         {
388             UDFFileEntry( pi_lb, pi_file_type, p_file, partition );
389             return 1;
390         }
391
392     } while( ( i_lba <= partition.i_start + icb.i_location +
393                ( icb.i_length - 1 ) / DVD_LB_SIZE ) && ( i_tag_id != 261 ) );
394
395     return 0;
396 }
397
398 /*****************************************************************************
399  * UDFScanDir: serach filename in dir
400  * ---
401  * Dir: Location of directory to scan
402  * FileName: Name of file to look for
403  * FileICB: Location of ICB of the found file
404  * return 1 on success, 0 on error;
405  *****************************************************************************/
406 static int UDFScanDir( struct ad_s dir, char * psz_filename,
407                        struct ad_s * p_file_icb, struct partition_s partition )
408 {
409     u8      pi_lb[2*DVD_LB_SIZE];
410     u32     i_lba;
411     u16     i_tag_id;
412     u8      i_file_char;
413     char    psz_temp[DVD_LB_SIZE];
414     int     p;
415   
416     /* Scan dir for ICB of file */
417     i_lba = partition.i_start + dir.i_location;
418 #if 0
419     do
420     {
421         if( !UDFReadLB( partition.i_fd, i_lba++, 1, pi_lb ) )
422         {
423             i_tag_id = 0;
424         }
425         else
426         {
427             p=0;
428             while( p < DVD_LB_SIZE )
429             {
430                 UDFDescriptor( &pi_lb[p], &i_tag_id );
431
432                 if( i_tag_id == 257 )
433                 {
434                     p += UDFFileIdentifier( &pi_lb[p], &i_file_char,
435                                             psz_temp, p_file_icb, partition );
436                     if( !strcasecmp( psz_filename, psz_temp ) )
437                     {
438                         return 1;
439                     }
440                 }
441                 else
442                 {
443                     p = DVD_LB_SIZE;
444                 }
445             }
446         }
447
448     } while( i_lba <=
449       partition.i_start + dir.i_location + ( dir.i_length - 1 ) / DVD_LB_SIZE );
450
451 #else
452
453     if( UDFReadLB( partition.i_fd, i_lba, 2, pi_lb ) <= 0 ) {
454         return 0;
455     }
456
457     p = 0;
458     while( p < dir.i_length )
459     {
460         if( p > DVD_LB_SIZE )
461         {
462             ++i_lba;
463             p -= DVD_LB_SIZE;
464             dir.i_length -= DVD_LB_SIZE;
465             if( UDFReadLB( partition.i_fd, i_lba, 2, pi_lb ) <= 0 )
466             {
467                 return 0;
468             }
469         }
470
471         UDFDescriptor( &pi_lb[p], &i_tag_id );
472
473         if( i_tag_id == 257 )
474         {
475             p += UDFFileIdentifier( &pi_lb[p], &i_file_char,
476                                     psz_temp, p_file_icb, partition );
477             if( !strcasecmp( psz_filename, psz_temp ) )
478             {
479                 return 1;
480             }
481         }
482         else
483         {
484             return 0;
485         }
486     }
487
488 #endif
489     return 0;
490 }
491
492
493 /******************************************************************************
494  * UDFFindPartition: looks for a partition on the disc
495  * ---
496  *   partnum: number of the partition, starting at 0
497  *   part: structure to fill with the partition information
498  *   return 1 if partition found, 0 on error;
499  ******************************************************************************/
500 static int UDFFindPartition( int i_part_nb, struct partition_s *p_partition )
501 {
502     u8          pi_lb[DVD_LB_SIZE];
503     u8          pi_anchor[DVD_LB_SIZE];
504     u16         i_tag_id;
505     u32         i_lba;
506     u32         i_MVDS_location;
507     u32         i_MVDS_length;
508     u32         i_last_sector;
509     boolean_t   b_term;
510     boolean_t   b_vol_valid;
511     int         i;
512
513     /* Find Anchor */
514     i_last_sector = 0;
515  
516     /* try #1, prime anchor */
517     i_lba = 256;    
518     b_term = 0;
519
520     /* Search anchor loop */
521     while( 1 )
522     {
523         if( UDFReadLB( p_partition->i_fd, i_lba, 1, pi_anchor ) )
524         {
525             UDFDescriptor( pi_anchor, &i_tag_id );
526         }
527         else
528         {
529             i_tag_id = 0;
530         }
531
532         if( i_tag_id != 2 )
533         {                
534             /* not an anchor? */
535             if( b_term )
536             {
537                 /* final try failed */
538                 return 0;
539             }
540
541             if( i_last_sector )
542             {
543                 /* we already found the last sector
544                  * try #3, alternative backup anchor */
545                 i_lba = i_last_sector;    
546             
547                 /* but that's just about enough, then! */
548                 b_term = 1;            
549             }
550             else
551             {
552                 /* TODO: find last sector of the disc (this is optional) */
553                 if( i_last_sector )
554                 {
555                     /* try #2, backup anchor */
556                     i_lba = i_last_sector - 256;                
557                 }
558                 else
559                 {
560                     /* unable to find last sector */
561                     return 0;
562                 }
563             }
564         }
565         else
566         {
567             /* it is an anchor! continue... */
568             break;
569         }
570     }
571
572     /* main volume descriptor */
573     UDFExtentAD( &pi_anchor[16], &i_MVDS_length, &i_MVDS_location );
574   
575     p_partition->b_valid = 0;
576     b_vol_valid = 0;
577     p_partition->pi_volume_desc[0] = '\0';
578
579     i = 1;
580     /* Find Volume Descriptor */
581     do
582     {
583         i_lba = i_MVDS_location;
584
585         do
586         {
587             if( !UDFReadLB( p_partition->i_fd, i_lba++, 1, pi_lb ) )
588             {
589                 i_tag_id = 0;
590             }
591             else
592             {
593                 UDFDescriptor( pi_lb, &i_tag_id );
594             }
595
596             if( ( i_tag_id == 5 ) && ( !p_partition->b_valid ) )
597             {
598                 /* Partition Descriptor */
599                 UDFPartition( pi_lb,
600                               &p_partition->i_flags,
601                               &p_partition->i_number,
602                               p_partition->pi_contents,
603                               &p_partition->i_start,
604                               &p_partition->i_length );
605                 p_partition->b_valid = ( i_part_nb == p_partition->i_number );
606             }
607             else if( ( i_tag_id == 6 ) && ( !b_vol_valid) )
608             {
609                 /* Logical Volume Descriptor */
610                 if( UDFLogVolume( pi_lb , p_partition->pi_volume_desc ) )
611                 {  
612                 /* TODO: sector size wrong! */
613                 }
614                 else
615                 {
616                     b_vol_valid = 1;
617                 }
618             }
619
620         } while( ( i_lba <= i_MVDS_location +
621                    ( i_MVDS_length - 1 ) / DVD_LB_SIZE )
622                  && ( i_tag_id != 8 )
623                  && ( ( !p_partition->b_valid ) || ( !b_vol_valid ) ) );
624     
625         if( ( !p_partition->b_valid ) || ( !b_vol_valid ) )
626         {
627             /* backup volume descriptor */
628             UDFExtentAD( &pi_anchor[24], &i_MVDS_length, &i_MVDS_location );
629         }
630     } while( i-- && ( ( !p_partition->b_valid ) || ( !b_vol_valid ) ) );
631
632     /* we only care for the partition, not the volume */
633     return( p_partition->b_valid);
634 }
635
636
637 /******************************************************************************
638  * UDFFindFile: looks for a file on the UDF disc/imagefile
639  * ---
640  * Path has to be the absolute pathname on the UDF filesystem,
641  * starting with '/'.
642  * returns absolute LB number, or 0 on error
643  ******************************************************************************/
644 u32 UDFFindFile( int i_fd, char * psz_path )
645 {
646     struct partition_s  partition;
647     struct ad_s         root_icb;
648     struct ad_s         file;
649     struct ad_s         icb;
650     u32                 i_lba;
651     u16                 i_tag_id;
652     u8                  pi_lb[DVD_LB_SIZE];
653     u8                  i_file_type;
654     char                psz_tokenline[DVD_LB_SIZE] = "";
655     char *              psz_token;
656     int                 i_partition;
657  
658     strcat( psz_tokenline, psz_path );
659
660     /* Init file descriptor of UDF filesystem (== DVD) */
661     partition.i_fd = i_fd;
662
663     /* Find partition 0, standard partition for DVD-Video */
664     i_partition = 0;
665     if( !UDFFindPartition( i_partition, &partition ) )
666     {
667         intf_ErrMsg( "UDF: Partition 0 not found" );
668         return 0;
669     }
670   
671     /* Find root dir ICB */
672     i_lba = partition.i_start;
673
674     do
675     {
676         if( !UDFReadLB( i_fd, i_lba++, 1, pi_lb ) )
677         {
678             i_tag_id = 0;
679         }
680         else
681         {
682             UDFDescriptor( pi_lb, &i_tag_id );
683         }
684
685         if( i_tag_id == 256 )
686         {
687             /* File Set Descriptor */
688             UDFAD( &pi_lb[400], &root_icb, UDFADlong, partition );
689         }
690
691     } while( ( i_lba < partition.i_start + partition.i_length )
692           && ( i_tag_id != 8) && ( i_tag_id != 256 ) );
693
694     if( i_tag_id != 256 )
695     {
696         intf_ErrMsg( "UDF: Bad descriptor" );
697         return 0;
698     }
699     if( root_icb.i_partition != i_partition )
700     {
701         intf_ErrMsg( "UDF: Bad partition" );
702         return 0;
703     }
704   
705     /* Find root dir */
706     if( !UDFMapICB( root_icb, &i_file_type, &file, partition ) )
707     {
708         intf_ErrMsg( "UDF: Can't find root dir" );
709         return 0;
710     }
711
712     /* root dir should be dir */
713     if( i_file_type != 4 )
714     {
715         intf_ErrMsg( "UDF: Root dir error" );
716         return 0;
717     }
718
719     /* Tokenize filepath */
720     psz_token = strtok( psz_tokenline, "/" );
721     while( psz_token )
722     {
723         if( !UDFScanDir( file, psz_token, &icb, partition ) )
724         {
725             intf_ErrMsg( "UDF: Scan dir error" );
726             return 0;
727         }
728
729         if( !UDFMapICB ( icb, &i_file_type, &file, partition ) )
730         {
731             intf_ErrMsg( "UDF: ICB error" );
732             return 0;
733         }
734
735         psz_token = strtok( NULL, "/" );
736     }
737
738     return partition.i_start + file.i_location;
739 }