1 /*
2  * Copyright (C) 2001-2004 Billy Biggs <vektor@dumbterm.net>,
3  *                         Håkan Hjort <d95hjort@dtek.chalmers.se>,
4  *                         Björn Englund <d4bjorn@dtek.chalmers.se>
5  *
6  * This file is part of libdvdread.
7  *
8  * libdvdread is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * libdvdread 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 General Public License along
19  * with libdvdread; if not, write to the Free Software Foundation, Inc.,
20  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21  */
22 
23 #include "config.h"
24 #include <sys/types.h>      /* off_t */
25 #include <sys/stat.h>       /* stat */
26 #include <sys/time.h>       /* For the timing of dvdcss_title crack. */
27 #include <fcntl.h>          /* open */
28 #include <stdlib.h>         /* free */
29 #include <stdio.h>          /* fprintf */
30 #include <errno.h>          /* errno, EIN* */
31 #include <string.h>         /* memcpy, strlen */
32 #include <unistd.h>         /* chdir, getcwd */
33 #include <limits.h>         /* PATH_MAX */
34 #include <dirent.h>         /* opendir, readdir */
35 #include <ctype.h>          /* isalpha */
36 
37 #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__bsdi__) || defined(__APPLE__)
38 # define SYS_BSD 1
39 #endif
40 
41 #if defined(__sun)
42 # include <sys/mnttab.h>
43 #elif defined(__APPLE__)
44 # include <sys/param.h>
45 # include <sys/ucred.h>
46 # include <sys/mount.h>
47 #elif defined(SYS_BSD)
48 # include <sys/mount.h>
49 #elif defined(__linux__)
50 # include <mntent.h>
51 # include <paths.h>
52 #endif
53 
54 #include "dvdread/dvd_udf.h"
55 #include "dvdread/dvd_reader.h"
56 #include "dvd_input.h"
57 #include "dvdread_internal.h"
58 #include "md5.h"
59 #include "dvdread/ifo_read.h"
60 
61 #if defined(_WIN32)
62 # include <windows.h>
63 # include "msvc/contrib/win32_cs.h"
64 #endif
65 
66 /* misc win32 helpers */
67 
68 #ifdef _WIN32
69 # ifndef HAVE_GETTIMEOFDAY
70    /* replacement gettimeofday implementation */
71 #  include <sys/timeb.h>
_private_gettimeofday(struct timeval * tv,void * tz)72 static inline int _private_gettimeofday( struct timeval *tv, void *tz )
73 {
74   struct timeb t;
75   ftime( &t );
76   tv->tv_sec = t.time;
77   tv->tv_usec = t.millitm * 1000;
78   return 0;
79 }
80 #  define gettimeofday(TV, TZ) _private_gettimeofday((TV), (TZ))
81 # endif
82 #endif /* _WIN32 */
83 
84 /* Compat wrapper for stat() */
85 
86 #if defined(_WIN32)
87 /* can't re-define stat (used in both function name and struct name) */
88 typedef struct _stat64 dvdstat_t;
dvdstat(const char * path,dvdstat_t * st)89 static inline int dvdstat(const char *path, dvdstat_t *st)
90 {
91   wchar_t *wpath, *it;
92   int ret;
93 
94   wpath = _utf8_to_wchar(path);
95   if (!wpath) {
96     return -1;
97   }
98 
99   /* need to strip possible trailing \\ */
100   for (it = wpath; *it; it++)
101     if ((*it == '\\' || *it == '/') && *(it+1) == 0)
102       *it = 0;
103 
104   ret = _wstat64(wpath, st);
105   free(wpath);
106   return ret;
107 }
108 #else
109 typedef struct stat dvdstat_t;
dvdstat(const char * file,dvdstat_t * st)110 static inline int dvdstat(const char *file, dvdstat_t *st) {
111   return stat(file, st);
112 }
113 #endif
114 
115 #if defined(_WIN32)
116 /* UTF-8 aware version of opendir()/readdir() */
117 
118 #include <io.h>
119 
120 typedef struct {
121   intptr_t handle;
122   struct _wfinddata_t went;
123   struct dirent ent;
124 } win32_dir_t;
125 
win32_opendir(const char * path)126 win32_dir_t *win32_opendir(const char *path)
127 {
128   char    *filespec;
129   wchar_t *wfilespec;
130   win32_dir_t *d;
131 
132   d = calloc(1, sizeof(*d));
133   if (!d)
134     return NULL;
135 
136   filespec = malloc(strlen(path) + 3);
137   if (!filespec) {
138     goto fail;
139   }
140   sprintf(filespec, "%s\\*", path);
141 
142   wfilespec = _utf8_to_wchar(filespec);
143   free(filespec);
144   if (!wfilespec) {
145     goto fail;
146   }
147 
148   d->handle = _wfindfirst(wfilespec, &d->went);
149   free(wfilespec);
150   if (d->handle != -1) {
151     return d;
152   }
153 
154  fail:
155   free(d);
156   return NULL;
157 }
158 
win32_readdir(win32_dir_t * dir)159 static struct dirent *win32_readdir(win32_dir_t *dir)
160 {
161   if (dir->went.name[0]) {
162     if (!WideCharToMultiByte(CP_UTF8, 0, dir->went.name, -1, dir->ent.d_name, sizeof(dir->ent.d_name), NULL, NULL))
163       dir->ent.d_name[0] = 0; /* allow reading next */
164     dir->went.name[0] = 0;
165     _wfindnext(dir->handle, &dir->went);
166     return &dir->ent;
167   }
168 
169   return NULL;
170 }
171 
win32_closedir(win32_dir_t * dir)172 static void win32_closedir(win32_dir_t *dir)
173 {
174   _findclose(dir->handle);
175   free(dir);
176 }
177 
178 #define DIR       win32_dir_t
179 #define opendir   win32_opendir
180 #define readdir   win32_readdir
181 #define closedir  win32_closedir
182 
183 #endif /* _WIN32 */
184 
185 #define DEFAULT_UDF_CACHE_LEVEL 1
186 
187 struct dvd_reader_device_s {
188   /* Basic information. */
189   int isImageFile;
190 
191   /* Hack for keeping track of the css status.
192    * 0: no css, 1: perhaps (need init of keys), 2: have done init */
193   int css_state;
194   int css_title; /* Last title that we have called dvdinpute_title for. */
195 
196   /* Information required for an image file. */
197   dvd_input_t dev;
198 
199   /* Information required for a directory path drive. */
200   char *path_root;
201 
202   /* Filesystem cache */
203   int udfcache_level; /* 0 - turned off, 1 - on */
204   void *udfcache;
205 };
206 
207 #define TITLES_MAX 9
208 
209 struct dvd_file_s {
210   /* Basic information. */
211   dvd_reader_t *ctx;
212 
213   /* Hack for selecting the right css title. */
214   int css_title;
215 
216   /* Information required for an image file. */
217   uint32_t lb_start;
218   uint32_t seek_pos;
219 
220   /* Information required for a directory path drive. */
221   size_t title_sizes[ TITLES_MAX ];
222   dvd_input_t title_devs[ TITLES_MAX ];
223 
224   /* Calculated at open-time, size in blocks. */
225   ssize_t filesize;
226 
227   /* Cache of the dvd_file. If not NULL, the cache corresponds to the whole
228    * dvd_file. Used only for IFO and BUP. */
229   unsigned char *cache;
230 };
231 
232 /**
233  * Set the level of caching on udf
234  * level = 0 (no caching)
235  * level = 1 (caching filesystem info)
236  */
DVDUDFCacheLevel(dvd_reader_t * reader,int level)237 int DVDUDFCacheLevel(dvd_reader_t *reader, int level)
238 {
239   dvd_reader_device_t *dev = reader->rd;
240 
241   if(level > 0) {
242     level = 1;
243   } else if(level < 0) {
244     return dev->udfcache_level;
245   }
246 
247   dev->udfcache_level = level;
248 
249   return level;
250 }
251 
GetUDFCacheHandle(dvd_reader_t * reader)252 void *GetUDFCacheHandle(dvd_reader_t *reader)
253 {
254   dvd_reader_device_t *dev = reader->rd;
255 
256   return dev->udfcache;
257 }
258 
SetUDFCacheHandle(dvd_reader_t * reader,void * cache)259 void SetUDFCacheHandle(dvd_reader_t *reader, void *cache)
260 {
261   dvd_reader_device_t *dev = reader->rd;
262 
263   dev->udfcache = cache;
264 }
265 
266 
267 
268 /* Loop over all titles and call dvdcss_title to crack the keys. */
initAllCSSKeys(dvd_reader_t * ctx)269 static int initAllCSSKeys( dvd_reader_t *ctx )
270 {
271   dvd_reader_device_t *dvd = ctx->rd;
272   struct timeval all_s, all_e;
273   struct timeval t_s, t_e;
274   char filename[ MAX_UDF_FILE_NAME_LEN ];
275   uint32_t start, len;
276   int title;
277 
278   const char *nokeys_str = getenv("DVDREAD_NOKEYS");
279   if(nokeys_str != NULL)
280     return 0;
281 
282   Log2(ctx,"Attempting to retrieve all CSS keys" );
283   Log2(ctx,"This can take a _long_ time, please be patient" );
284   gettimeofday(&all_s, NULL);
285 
286   for( title = 0; title < 100; title++ ) {
287     gettimeofday( &t_s, NULL );
288     if( title == 0 ) {
289       sprintf( filename, "/VIDEO_TS/VIDEO_TS.VOB" );
290     } else {
291       sprintf( filename, "/VIDEO_TS/VTS_%02d_%d.VOB", title, 0 );
292     }
293     start = UDFFindFile( ctx, filename, &len );
294     if( start != 0 && len != 0 ) {
295       /* Perform CSS key cracking for this title. */
296       Log3(ctx,"Get key for %s at 0x%08x",filename, start );
297       if( dvdinput_title( dvd->dev, (int)start ) < 0 ) {
298         Log1(ctx,"Error cracking CSS key for %s (0x%08x)", filename, start);
299       }
300       gettimeofday( &t_e, NULL );
301       Log3(ctx,"Elapsed time %ld", (long int) t_e.tv_sec - t_s.tv_sec );
302     }
303 
304     if( title == 0 ) continue;
305 
306     gettimeofday( &t_s, NULL );
307     sprintf( filename, "/VIDEO_TS/VTS_%02d_%d.VOB", title, 1 );
308     start = UDFFindFile( ctx, filename, &len );
309     if( start == 0 || len == 0 ) break;
310 
311     /* Perform CSS key cracking for this title. */
312     Log3(ctx,"Get key for %s at 0x%08x",filename, start );
313     if( dvdinput_title( dvd->dev, (int)start ) < 0 ) {
314       Log1(ctx,"Error cracking CSS key for %s (0x%08x)", filename, start);
315     }
316     gettimeofday( &t_e, NULL );
317     Log3(ctx,"Elapsed time %ld", (long int) t_e.tv_sec - t_s.tv_sec );
318   }
319   title--;
320 
321   Log3(ctx,"Found %d VTS's", title );
322   gettimeofday(&all_e, NULL);
323   Log3(ctx,"Elapsed time %ld", (long int) all_e.tv_sec - all_s.tv_sec );
324 
325   return 0;
326 }
327 
328 
329 
330 /**
331  * Open a DVD image or block device file or use stream_cb functions.
332  */
DVDOpenImageFile(dvd_reader_t * ctx,const char * location,dvd_reader_stream_cb * stream_cb,int have_css)333 static dvd_reader_device_t *DVDOpenImageFile( dvd_reader_t *ctx,
334                                               const char *location,
335                                         dvd_reader_stream_cb *stream_cb,
336                                        int have_css )
337 {
338   dvd_reader_device_t *dvd;
339   dvd_input_t dev;
340 
341   dev = dvdinput_open( ctx->priv, &ctx->logcb, location, stream_cb );
342   if( !dev ) {
343     Log0(ctx,"Can't open %s for reading", location );
344     return NULL;
345   }
346 
347   dvd = calloc( 1, sizeof( dvd_reader_device_t ) );
348   if( !dvd ) {
349     dvdinput_close(dev);
350     return NULL;
351   }
352   dvd->isImageFile = 1;
353   dvd->dev = dev;
354 
355   dvd->udfcache_level = DEFAULT_UDF_CACHE_LEVEL;
356 
357   if( have_css ) {
358     /* Only if DVDCSS_METHOD = title, a bit if it's disc or if
359      * DVDCSS_METHOD = key but region mismatch. Unfortunately we
360      * don't have that information. */
361 
362     dvd->css_state = 1; /* Need key init. */
363   }
364 
365   return dvd;
366 }
367 
DVDOpenPath(const char * path_root)368 static dvd_reader_device_t *DVDOpenPath( const char *path_root )
369 {
370   dvd_reader_device_t *dvd;
371 
372   dvd = calloc( 1, sizeof( dvd_reader_device_t ) );
373   if( !dvd ) return NULL;
374   dvd->path_root = strdup( path_root );
375   if(!dvd->path_root) {
376     free(dvd);
377     return NULL;
378   }
379   dvd->udfcache_level = DEFAULT_UDF_CACHE_LEVEL;
380 
381   return dvd;
382 }
383 
384 #if defined(__sun)
385 /* /dev/rdsk/c0t6d0s0 (link to /devices/...)
386    /vol/dev/rdsk/c0t6d0/??
387    /vol/rdsk/<name> */
sun_block2char(const char * path)388 static char *sun_block2char( const char *path )
389 {
390   char *new_path;
391 
392   /* Must contain "/dsk/" */
393   if( !strstr( path, "/dsk/" ) ) return (char *) strdup( path );
394 
395   /* Replace "/dsk/" with "/rdsk/" */
396   new_path = malloc( strlen(path) + 2 );
397   if(!new_path) return NULL;
398   strcpy( new_path, path );
399   strcpy( strstr( new_path, "/dsk/" ), "" );
400   strcat( new_path, "/rdsk/" );
401   strcat( new_path, strstr( path, "/dsk/" ) + strlen( "/dsk/" ) );
402 
403   return new_path;
404 }
405 #endif
406 
407 #if defined(SYS_BSD)
408 /* FreeBSD /dev/(r)(a)cd0c (a is for atapi), recommended to _not_ use r
409    update: FreeBSD and DragonFly no longer uses the prefix so don't add it.
410    OpenBSD /dev/rcd0c, it needs to be the raw device
411    NetBSD  /dev/rcd0[d|c|..] d for x86, c (for non x86), perhaps others
412    Darwin  /dev/rdisk0,  it needs to be the raw device
413    BSD/OS  /dev/sr0c (if not mounted) or /dev/rsr0c ('c' any letter will do)
414    returns a string allocated with strdup. It should be freed when no longer
415    used. */
bsd_block2char(const char * path)416 static char *bsd_block2char( const char *path )
417 {
418 #if defined(__FreeBSD__) || defined(__DragonFly__)
419   return (char *) strdup( path );
420 #else
421   char *new_path;
422 
423   /* If it doesn't start with "/dev/" or does start with "/dev/r" exit */
424   if( strncmp( path, "/dev/",  5 ) || !strncmp( path, "/dev/r", 6 ) )
425     return (char *) strdup( path );
426 
427   /* Replace "/dev/" with "/dev/r" */
428   new_path = malloc( strlen(path) + 2 );
429   if(!new_path) return NULL;
430   strcpy( new_path, "/dev/r" );
431   strcat( new_path, path + strlen( "/dev/" ) );
432 
433   return new_path;
434 #endif /* __FreeBSD__ || __DragonFly__ */
435 }
436 #endif
437 
DVDOpenCommon(void * priv,const dvd_logger_cb * logcb,const char * ppath,dvd_reader_stream_cb * stream_cb)438 static dvd_reader_t *DVDOpenCommon( void *priv,
439                                     const dvd_logger_cb *logcb,
440                                     const char *ppath,
441                                     dvd_reader_stream_cb *stream_cb )
442 {
443   dvdstat_t fileinfo;
444   int ret, have_css, cdir = -1;
445   char *dev_name = NULL;
446   char *path = NULL, *new_path = NULL, *path_copy = NULL;
447   dvd_reader_t *ctx = calloc(1, sizeof(*ctx));
448   if(!ctx)
449       return NULL;
450 
451   ctx->priv = priv;
452   if(logcb)
453     ctx->logcb = *logcb;
454 
455 #if defined(_WIN32) || defined(__OS2__)
456       int len;
457 #endif
458 
459   /* Try to open DVD using stream_cb functions */
460   if( priv != NULL && stream_cb != NULL )
461   {
462     have_css = dvdinput_setup( ctx->priv, &ctx->logcb );
463     ctx->rd = DVDOpenImageFile( ctx, NULL, stream_cb, have_css );
464     if(!ctx->rd)
465     {
466         free(ctx);
467         return NULL;
468     }
469     return ctx;
470   }
471 
472   if( ppath == NULL )
473     goto DVDOpen_error;
474 
475   path = strdup(ppath);
476   if( path == NULL )
477     goto DVDOpen_error;
478 
479   /* Try to open libdvdcss or fall back to standard functions */
480   have_css = dvdinput_setup( ctx->priv, &ctx->logcb );
481 
482 #if defined(_WIN32) || defined(__OS2__)
483   /* Strip off the trailing \ if it is not a drive */
484   len = strlen(path);
485   if ((len > 1) &&
486       (path[len - 1] == '\\')  &&
487       (path[len - 2] != ':'))
488   {
489     path[len-1] = '\0';
490   }
491 #endif
492 
493   ret = dvdstat( path, &fileinfo );
494 
495   if( ret < 0 ) {
496 
497     /* maybe "host:port" url? try opening it with acCeSS library */
498     if( strchr(path,':') ) {
499       ctx->rd = DVDOpenImageFile( ctx, path, NULL, have_css );
500       free(path);
501       if(!ctx->rd)
502       {
503           free(ctx);
504           return NULL;
505       }
506       return ctx;
507     }
508 
509     /* If we can't stat the file, give up */
510     Log0(ctx, "Can't stat %s", path );
511     perror("");
512     goto DVDOpen_error;
513   }
514 
515   /* First check if this is a block/char device or a file*/
516   if( S_ISBLK( fileinfo.st_mode ) ||
517       S_ISCHR( fileinfo.st_mode ) ||
518       S_ISREG( fileinfo.st_mode ) ) {
519 
520     /**
521      * Block devices and regular files are assumed to be DVD-Video images.
522      */
523 #if defined(__sun)
524     dev_name = sun_block2char( path );
525 #elif defined(SYS_BSD)
526     dev_name = bsd_block2char( path );
527 #else
528     dev_name = strdup( path );
529 #endif
530     if(!dev_name)
531         goto DVDOpen_error;
532     ctx->rd = DVDOpenImageFile( ctx, dev_name, NULL, have_css );
533     free( dev_name );
534     free(path);
535     if(!ctx->rd)
536     {
537         free(ctx);
538         return NULL;
539     }
540     return ctx;
541   } else if( S_ISDIR( fileinfo.st_mode ) ) {
542 #if defined(SYS_BSD)
543     struct statfs fs;
544 #elif defined(__sun) || defined(__linux__)
545     FILE *mntfile;
546 #endif
547 
548     /* XXX: We should scream real loud here. */
549     if( !(path_copy = strdup( path ) ) )
550       goto DVDOpen_error;
551 
552 #ifndef _WIN32 /* don't have fchdir, and getcwd( NULL, ... ) is strange */
553               /* Also WIN32 does not have symlinks, so we don't need this bit of code. */
554 
555     /* Resolve any symlinks and get the absolute dir name. */
556     {
557       if( ( cdir  = open( ".", O_RDONLY ) ) >= 0 ) {
558         int retval;
559         if( chdir( path_copy ) == -1 ) {
560           goto DVDOpen_error;
561         }
562         new_path = malloc(PATH_MAX+1);
563         if(!new_path) {
564           goto DVDOpen_error;
565         }
566         if( getcwd( new_path, PATH_MAX ) == NULL ) {
567           goto DVDOpen_error;
568         }
569         retval = fchdir( cdir );
570         close( cdir );
571         cdir = -1;
572         if( retval == -1 ) {
573           goto DVDOpen_error;
574         }
575         free(path_copy);
576         path_copy = new_path;
577         new_path = NULL;
578       }
579     }
580 #endif
581 
582     /**
583      * If we're being asked to open a directory, check if that directory
584      * is the mount point for a DVD-ROM which we can use instead.
585      */
586 
587     if( strlen( path_copy ) > 1 ) {
588       if( path_copy[ strlen( path_copy ) - 1 ] == '/' ) {
589         path_copy[ strlen( path_copy ) - 1 ] = '\0';
590       }
591     }
592 
593 #if defined(_WIN32) || defined(__OS2__)
594     if( strlen( path_copy ) > 9 ) {
595       if( !strcasecmp( &(path_copy[ strlen( path_copy ) - 9 ]),
596                        "\\video_ts"))
597         path_copy[ strlen( path_copy ) - (9-1) ] = '\0';
598     }
599 #endif
600     if( strlen( path_copy ) > 9 ) {
601       if( !strcasecmp( &(path_copy[ strlen( path_copy ) - 9 ]),
602                        "/video_ts" ) ) {
603         path_copy[ strlen( path_copy ) - 9 ] = '\0';
604       }
605     }
606 
607     if(path_copy[0] == '\0') {
608       free( path_copy );
609       if( !(path_copy = strdup( "/" ) ) )
610         goto DVDOpen_error;
611     }
612 
613 #if defined(__APPLE__)
614     struct statfs s[128];
615     int r = getfsstat(NULL, 0, MNT_NOWAIT);
616     if (r > 0) {
617         if (r > 128)
618             r = 128;
619         r = getfsstat(s, r * sizeof(s[0]), MNT_NOWAIT);
620         int i;
621         for (i=0; i<r; i++) {
622             if (!strcmp(path_copy, s[i].f_mntonname)) {
623                 dev_name = bsd_block2char(s[i].f_mntfromname);
624                 Log3(ctx, "Attempting to use device %s"
625                           " mounted on %s for CSS authentication",
626                         dev_name,
627                         s[i].f_mntonname);
628                 ctx->rd = DVDOpenImageFile( ctx, dev_name, NULL, have_css );
629                 break;
630             }
631         }
632     }
633 #elif defined(SYS_BSD)
634     if( statfs( path_copy, &fs ) == 0 ) {
635         if( !strcmp( path_copy, fs.f_mntonname ) ) {
636             dev_name = bsd_block2char( fs.f_mntfromname );
637             Log3(ctx, "Attempting to use device %s"
638                      " mounted on %s for CSS authentication",
639                      dev_name,
640                      fs.f_mntonname );
641             ctx->rd = DVDOpenImageFile( ctx, dev_name, NULL, have_css );
642         }
643     }
644 #elif defined(__sun)
645     mntfile = fopen( MNTTAB, "r" );
646     if( mntfile ) {
647       struct mnttab mp;
648       int res;
649 
650       while( ( res = getmntent( mntfile, &mp ) ) != -1 ) {
651         if( res == 0 && !strcmp( mp.mnt_mountp, path_copy ) ) {
652           dev_name = sun_block2char( mp.mnt_special );
653           Log3(ctx, "Attempting to use device %s"
654                    " mounted on %s for CSS authentication",
655                    dev_name,
656                    mp.mnt_mountp );
657           ctx->rd = DVDOpenImageFile( ctx, dev_name, NULL, have_css );
658           break;
659         }
660       }
661       fclose( mntfile );
662     }
663 #elif defined(__linux__)
664     mntfile = fopen( _PATH_MOUNTED, "r" );
665     if( mntfile ) {
666 
667 #ifdef HAVE_GETMNTENT_R
668       struct mntent *me, mbuf;
669       char buf [8192];
670       while( ( me = getmntent_r( mntfile, &mbuf, buf, sizeof(buf) ) ) ) {
671 #else
672       struct mntent *me;
673       while( ( me = getmntent( mntfile ) ) ) {
674 #endif
675         if( !strcmp( me->mnt_dir, path_copy ) ) {
676           Log3(ctx, "Attempting to use device %s"
677                    " mounted on %s for CSS authentication",
678                    me->mnt_fsname,
679                    me->mnt_dir );
680           ctx->rd = DVDOpenImageFile( ctx, me->mnt_fsname, NULL, have_css );
681           dev_name = strdup(me->mnt_fsname);
682           break;
683         }
684       }
685       fclose( mntfile );
686     }
687 #elif defined(_WIN32) || defined(__OS2__)
688 #ifdef __OS2__
689     /* Use DVDOpenImageFile() only if it is a drive */
690     if(isalpha(path[0]) && path[1] == ':' &&
691         ( !path[2] ||
692           ((path[2] == '\\' || path[2] == '/') && !path[3])))
693 #endif
694     ctx->rd = DVDOpenImageFile( ctx, path, NULL, have_css );
695 #endif
696 
697 #if !defined(_WIN32) && !defined(__OS2__)
698     if( !dev_name ) {
699       Log0(ctx, "Couldn't find device name." );
700     } else if( !ctx->rd ) {
701       Log0(ctx, "Device %s inaccessible, "
702                 "CSS authentication not available.", dev_name );
703     }
704 #else
705     if( !ctx->rd ) {
706         Log0(ctx, "Device %s inaccessible, "
707                  "CSS authentication not available.", path );
708     }
709 #endif
710 
711     free( dev_name );
712     dev_name = NULL;
713     free( path_copy );
714     path_copy = NULL;
715 
716     /**
717      * If we've opened a drive, just use that.
718      */
719     if(ctx->rd)
720     {
721         free(path);
722         return ctx;
723     }
724     /**
725      * Otherwise, we now try to open the directory tree instead.
726      */
727     ctx->rd = DVDOpenPath( path );
728     free( path );
729     if(!ctx->rd)
730     {
731         free(ctx);
732         return NULL;
733     }
734     return ctx;
735   }
736 
737 DVDOpen_error:
738   /* If it's none of the above, screw it. */
739   Log0(ctx, "Could not open %s", path );
740   free( path );
741   free( path_copy );
742   if ( cdir >= 0 )
743     close( cdir );
744   free( new_path );
745   return NULL;
746 }
747 
748 dvd_reader_t *DVDOpen( const char *ppath )
749 {
750     return DVDOpenCommon( NULL, NULL, ppath, NULL );
751 }
752 
753 dvd_reader_t *DVDOpenStream( void *stream,
754                              dvd_reader_stream_cb *stream_cb )
755 {
756     return DVDOpenCommon( stream, NULL, NULL, stream_cb );
757 }
758 
759 dvd_reader_t *DVDOpen2( void *priv, const dvd_logger_cb *logcb,
760                         const char *ppath )
761 {
762     return DVDOpenCommon( priv, logcb, ppath, NULL );
763 }
764 
765 dvd_reader_t *DVDOpenStream2( void *priv, const dvd_logger_cb *logcb,
766                               dvd_reader_stream_cb *stream_cb )
767 {
768     return DVDOpenCommon( priv, logcb, NULL, stream_cb );
769 }
770 
771 void DVDClose( dvd_reader_t *dvd )
772 {
773   if( dvd ) {
774     if( dvd->rd->dev ) dvdinput_close( dvd->rd->dev );
775     if( dvd->rd->path_root ) free( dvd->rd->path_root );
776     if( dvd->rd->udfcache ) FreeUDFCache( dvd->rd->udfcache );
777     free( dvd->rd );
778     free( dvd );
779   }
780 }
781 
782 /**
783  * Open an unencrypted file on a DVD image file.
784  */
785 static dvd_file_t *DVDOpenFileUDF( dvd_reader_t *ctx, const char *filename,
786                                    int do_cache )
787 {
788   uint32_t start, len;
789   dvd_file_t *dvd_file;
790 
791   start = UDFFindFile( ctx, filename, &len );
792   if( !start ) {
793     Log0(ctx, "DVDOpenFileUDF:UDFFindFile %s failed", filename );
794     return NULL;
795   }
796 
797   dvd_file = calloc( 1, sizeof( dvd_file_t ) );
798   if( !dvd_file ) {
799     Log0(ctx, "DVDOpenFileUDF:malloc failed" );
800     return NULL;
801   }
802   dvd_file->ctx = ctx;
803   dvd_file->lb_start = start;
804   dvd_file->filesize = len / DVD_VIDEO_LB_LEN;
805 
806   /* Read the whole file in cache (unencrypted) if asked and if it doesn't
807    * exceed 128KB */
808   if( do_cache && len < 64 * DVD_VIDEO_LB_LEN ) {
809     int ret;
810 
811     dvd_file->cache = malloc( len );
812     if( !dvd_file->cache )
813         return dvd_file;
814 
815     ret = InternalUDFReadBlocksRaw( ctx, dvd_file->lb_start,
816                                     dvd_file->filesize, dvd_file->cache,
817                                     DVDINPUT_NOFLAGS );
818     if( ret != dvd_file->filesize ) {
819         free( dvd_file->cache );
820         dvd_file->cache = NULL;
821     }
822   }
823 
824   return dvd_file;
825 }
826 
827 /**
828  * Searches for <file> in directory <path>, ignoring case.
829  * Returns 0 and full filename in <filename>.
830  *     or -1 on file not found.
831  *     or -2 on path not found.
832  */
833 static int findDirFile( const char *path, const char *file, char *filename )
834 {
835   DIR *dir;
836   struct dirent *ent;
837 
838   dir = opendir( path );
839   if( !dir ) return -2;
840 
841   while( ( ent = readdir( dir ) ) != NULL ) {
842     if( !strcasecmp( ent->d_name, file ) ) {
843       sprintf( filename, "%s%s%s", path,
844                ( ( path[ strlen( path ) - 1 ] == '/' ) ? "" : "/" ),
845                ent->d_name );
846       closedir(dir);
847       return 0;
848     }
849   }
850   closedir(dir);
851   return -1;
852 }
853 
854 static int findDVDFile( dvd_reader_t *dvd, const char *file, char *filename )
855 {
856   const char *nodirfile;
857   int ret;
858 
859   /* Strip off the directory for our search */
860   if( !strncasecmp( "/VIDEO_TS/", file, 10 ) ) {
861     nodirfile = &(file[ 10 ]);
862   } else {
863     nodirfile = file;
864   }
865 
866   ret = findDirFile( dvd->rd->path_root, nodirfile, filename );
867   if( ret < 0 ) {
868     char video_path[ PATH_MAX + 1 ];
869 
870     /* Try also with adding the path, just in case. */
871     sprintf( video_path, "%s/VIDEO_TS/", dvd->rd->path_root );
872     ret = findDirFile( video_path, nodirfile, filename );
873     if( ret < 0 ) {
874       /* Try with the path, but in lower case. */
875       sprintf( video_path, "%s/video_ts/", dvd->rd->path_root );
876       ret = findDirFile( video_path, nodirfile, filename );
877       if( ret < 0 ) {
878         return 0;
879       }
880     }
881   }
882 
883   return 1;
884 }
885 
886 /**
887  * Open an unencrypted file from a DVD directory tree.
888  */
889 static dvd_file_t *DVDOpenFilePath( dvd_reader_t *ctx, const char *filename )
890 {
891   char full_path[ PATH_MAX + 1 ];
892   dvd_file_t *dvd_file;
893   dvdstat_t fileinfo;
894   dvd_input_t dev;
895 
896   /* Get the full path of the file. */
897   if( !findDVDFile( ctx, filename, full_path ) ) {
898     Log0(ctx, "DVDOpenFilePath:findDVDFile %s failed", filename );
899     return NULL;
900   }
901 
902   dev = dvdinput_open( ctx->priv, &ctx->logcb, full_path, NULL );
903   if( !dev ) {
904     Log0(ctx, "DVDOpenFilePath:dvdinput_open %s failed", full_path );
905     return NULL;
906   }
907 
908   dvd_file = calloc( 1, sizeof( dvd_file_t ) );
909   if( !dvd_file ) {
910     Log0(ctx, "DVDOpenFilePath:dvd_file malloc failed" );
911     dvdinput_close(dev);
912     return NULL;
913   }
914   dvd_file->ctx = ctx;
915 
916   if( dvdstat( full_path, &fileinfo ) < 0 ) {
917     Log0(ctx, "Can't stat() %s.", filename );
918     free( dvd_file );
919     dvdinput_close( dev );
920     return NULL;
921   }
922   dvd_file->title_sizes[ 0 ] = fileinfo.st_size / DVD_VIDEO_LB_LEN;
923   dvd_file->title_devs[ 0 ] = dev;
924   dvd_file->filesize = dvd_file->title_sizes[ 0 ];
925 
926   return dvd_file;
927 }
928 
929 static dvd_file_t *DVDOpenVOBUDF( dvd_reader_t *ctx, int title, int menu )
930 {
931   char filename[ MAX_UDF_FILE_NAME_LEN ];
932   uint32_t start, len;
933   dvd_file_t *dvd_file;
934 
935   if( title == 0 ) {
936     strcpy( filename, "/VIDEO_TS/VIDEO_TS.VOB" );
937   } else {
938     sprintf( filename, "/VIDEO_TS/VTS_%02d_%d.VOB", title, menu ? 0 : 1 );
939   }
940   start = UDFFindFile( ctx, filename, &len );
941   if( start == 0 ) return NULL;
942 
943   dvd_file = calloc( 1, sizeof( dvd_file_t ) );
944   if( !dvd_file ) return NULL;
945   dvd_file->ctx = ctx;
946   /*Hack*/ dvd_file->css_title = title << 1 | menu;
947   dvd_file->lb_start = start;
948   dvd_file->filesize = len / DVD_VIDEO_LB_LEN;
949 
950   /* Calculate the complete file size for every file in the VOBS */
951   if( !menu ) {
952     int cur;
953 
954     for( cur = 2; cur < 10; cur++ ) {
955       sprintf( filename, "/VIDEO_TS/VTS_%02d_%d.VOB", title, cur );
956       if( !UDFFindFile( ctx, filename, &len ) ) break;
957       dvd_file->filesize += len / DVD_VIDEO_LB_LEN;
958     }
959   }
960 
961   if( ctx->rd->css_state == 1 /* Need key init */ ) {
962     initAllCSSKeys( ctx );
963     ctx->rd->css_state = 2;
964   }
965   /*
966   if( dvdinput_title( dvd_file->dvd->dev, (int)start ) < 0 ) {
967       Log0(ctx, "Error cracking CSS key for %s", filename );
968   }
969   */
970 
971   return dvd_file;
972 }
973 
974 static dvd_file_t *DVDOpenVOBPath( dvd_reader_t *ctx, int title, int menu )
975 {
976   char filename[ MAX_UDF_FILE_NAME_LEN ];
977   char full_path[ PATH_MAX + 1 ];
978   dvdstat_t fileinfo;
979   dvd_file_t *dvd_file;
980 
981   dvd_file = calloc( 1, sizeof( dvd_file_t ) );
982   if( !dvd_file ) return NULL;
983   dvd_file->ctx = ctx;
984   /*Hack*/ dvd_file->css_title = title << 1 | menu;
985 
986   if( menu ) {
987     dvd_input_t dev;
988 
989     if( title == 0 ) {
990       strcpy( filename, "VIDEO_TS.VOB" );
991     } else {
992       sprintf( filename, "VTS_%02i_0.VOB", title );
993     }
994     if( !findDVDFile( ctx, filename, full_path ) ) {
995       free( dvd_file );
996       return NULL;
997     }
998 
999     dev = dvdinput_open( ctx->priv, &ctx->logcb, full_path, NULL );
1000     if( dev == NULL ) {
1001       free( dvd_file );
1002       return NULL;
1003     }
1004 
1005     if( dvdstat( full_path, &fileinfo ) < 0 ) {
1006       Log0(ctx, "Can't stat() %s.", filename );
1007       dvdinput_close(dev);
1008       free( dvd_file );
1009       return NULL;
1010     }
1011     dvd_file->title_sizes[ 0 ] = fileinfo.st_size / DVD_VIDEO_LB_LEN;
1012     dvd_file->title_devs[ 0 ] = dev;
1013     dvdinput_title( dvd_file->title_devs[0], 0);
1014     dvd_file->filesize = dvd_file->title_sizes[ 0 ];
1015 
1016   } else {
1017     int i;
1018 
1019     for( i = 0; i < TITLES_MAX; ++i ) {
1020 
1021       sprintf( filename, "VTS_%02i_%i.VOB", title, i + 1 );
1022       if( !findDVDFile( ctx, filename, full_path ) ) {
1023         break;
1024       }
1025 
1026       if( dvdstat( full_path, &fileinfo ) < 0 ) {
1027         Log0(ctx, "Can't stat() %s.", filename );
1028         break;
1029       }
1030 
1031       dvd_file->title_sizes[ i ] = fileinfo.st_size / DVD_VIDEO_LB_LEN;
1032       dvd_file->title_devs[ i ] = dvdinput_open( ctx->priv, &ctx->logcb, full_path, NULL );
1033       dvdinput_title( dvd_file->title_devs[ i ], 0 );
1034       dvd_file->filesize += dvd_file->title_sizes[ i ];
1035     }
1036     if( !dvd_file->title_devs[ 0 ] ) {
1037       free( dvd_file );
1038       return NULL;
1039     }
1040   }
1041 
1042   return dvd_file;
1043 }
1044 
1045 dvd_file_t *DVDOpenFile( dvd_reader_t *ctx, int titlenum,
1046                          dvd_read_domain_t domain )
1047 {
1048   dvd_reader_device_t *dvd = ctx->rd;
1049   char filename[ MAX_UDF_FILE_NAME_LEN ];
1050   int do_cache = 0;
1051 
1052   /* Check arguments. */
1053   if( dvd == NULL || titlenum < 0 )
1054     return NULL;
1055 
1056   switch( domain ) {
1057   case DVD_READ_INFO_FILE:
1058     if( titlenum == 0 ) {
1059       strcpy( filename, "/VIDEO_TS/VIDEO_TS.IFO" );
1060     } else {
1061       sprintf( filename, "/VIDEO_TS/VTS_%02i_0.IFO", titlenum );
1062     }
1063     do_cache = 1;
1064     break;
1065   case DVD_READ_INFO_BACKUP_FILE:
1066     if( titlenum == 0 ) {
1067       strcpy( filename, "/VIDEO_TS/VIDEO_TS.BUP" );
1068     } else {
1069       sprintf( filename, "/VIDEO_TS/VTS_%02i_0.BUP", titlenum );
1070     }
1071     do_cache = 1;
1072     break;
1073   case DVD_READ_MENU_VOBS:
1074     if( dvd->isImageFile ) {
1075       return DVDOpenVOBUDF( ctx, titlenum, 1 );
1076     } else {
1077       return DVDOpenVOBPath( ctx, titlenum, 1 );
1078     }
1079     break;
1080   case DVD_READ_TITLE_VOBS:
1081     if( titlenum == 0 ) return NULL;
1082     if( dvd->isImageFile ) {
1083       return DVDOpenVOBUDF( ctx, titlenum, 0 );
1084     } else {
1085       return DVDOpenVOBPath( ctx, titlenum, 0 );
1086     }
1087     break;
1088   default:
1089     Log1(ctx, "Invalid domain for file open." );
1090     return NULL;
1091   }
1092 
1093   if( dvd->isImageFile ) {
1094     return DVDOpenFileUDF( ctx, filename, do_cache );
1095   } else {
1096     return DVDOpenFilePath( ctx, filename );
1097   }
1098 }
1099 
1100 void DVDCloseFile( dvd_file_t *dvd_file )
1101 {
1102   dvd_reader_device_t *dvd = dvd_file->ctx->rd;
1103   if( dvd_file && dvd ) {
1104     if( !dvd->isImageFile ) {
1105       int i;
1106 
1107       for( i = 0; i < TITLES_MAX; ++i ) {
1108         if( dvd_file->title_devs[ i ] ) {
1109           dvdinput_close( dvd_file->title_devs[i] );
1110         }
1111       }
1112     }
1113 
1114     free( dvd_file->cache );
1115     free( dvd_file );
1116     dvd_file = NULL;
1117   }
1118 }
1119 
1120 static int DVDFileStatVOBUDF( dvd_reader_t *dvd, int title,
1121                               int menu, dvd_stat_t *statbuf )
1122 {
1123   char filename[ MAX_UDF_FILE_NAME_LEN ];
1124   uint32_t size;
1125   off_t tot_size;
1126   off_t parts_size[ 9 ];
1127   int nr_parts = 0;
1128   int n;
1129 
1130   if( title == 0 )
1131     strcpy( filename, "/VIDEO_TS/VIDEO_TS.VOB" );
1132   else
1133     sprintf( filename, "/VIDEO_TS/VTS_%02d_%d.VOB", title, menu ? 0 : 1 );
1134 
1135   if( !UDFFindFile( dvd, filename, &size ) )
1136     return -1;
1137 
1138   tot_size = size;
1139   nr_parts = 1;
1140   parts_size[ 0 ] = size;
1141 
1142   if( !menu ) {
1143     int cur;
1144 
1145     for( cur = 2; cur < 10; cur++ ) {
1146       sprintf( filename, "/VIDEO_TS/VTS_%02d_%d.VOB", title, cur );
1147       if( !UDFFindFile( dvd, filename, &size ) )
1148         break;
1149 
1150       parts_size[ nr_parts ] = size;
1151       tot_size += size;
1152       nr_parts++;
1153     }
1154   }
1155 
1156   statbuf->size = tot_size;
1157   statbuf->nr_parts = nr_parts;
1158   for( n = 0; n < nr_parts; n++ )
1159     statbuf->parts_size[ n ] = parts_size[ n ];
1160 
1161   return 0;
1162 }
1163 
1164 
1165 static int DVDFileStatVOBPath( dvd_reader_t *dvd, int title,
1166                                int menu, dvd_stat_t *statbuf )
1167 {
1168   char filename[ MAX_UDF_FILE_NAME_LEN ];
1169   char full_path[ PATH_MAX + 1 ];
1170   dvdstat_t fileinfo;
1171   off_t tot_size;
1172   off_t parts_size[ 9 ];
1173   int nr_parts = 0;
1174   int n;
1175 
1176   if( title == 0 )
1177     strcpy( filename, "VIDEO_TS.VOB" );
1178   else
1179     sprintf( filename, "VTS_%02d_%d.VOB", title, menu ? 0 : 1 );
1180 
1181   if( !findDVDFile( dvd, filename, full_path ) )
1182     return -1;
1183 
1184   if( dvdstat( full_path, &fileinfo ) < 0 ) {
1185     Log1(dvd, "Can't stat() %s.", filename );
1186     return -1;
1187   }
1188 
1189   tot_size = fileinfo.st_size;
1190   nr_parts = 1;
1191   parts_size[ 0 ] = fileinfo.st_size;
1192 
1193   if( !menu ) {
1194     int cur;
1195     for( cur = 2; cur < 10; cur++ ) {
1196       sprintf( filename, "VTS_%02d_%d.VOB", title, cur );
1197       if( !findDVDFile( dvd, filename, full_path ) )
1198         break;
1199 
1200       if( dvdstat( full_path, &fileinfo ) < 0 ) {
1201         Log1(dvd, "Can't stat() %s.", filename );
1202         break;
1203       }
1204 
1205       parts_size[ nr_parts ] = fileinfo.st_size;
1206       tot_size += parts_size[ nr_parts ];
1207       nr_parts++;
1208     }
1209   }
1210 
1211   statbuf->size = tot_size;
1212   statbuf->nr_parts = nr_parts;
1213   for( n = 0; n < nr_parts; n++ )
1214     statbuf->parts_size[ n ] = parts_size[ n ];
1215 
1216   return 0;
1217 }
1218 
1219 
1220 int DVDFileStat( dvd_reader_t *reader, int titlenum,
1221                  dvd_read_domain_t domain, dvd_stat_t *statbuf )
1222 {
1223   dvd_reader_device_t *dvd = reader->rd;
1224   char filename[ MAX_UDF_FILE_NAME_LEN ];
1225   dvdstat_t fileinfo;
1226   uint32_t size;
1227 
1228   /* Check arguments. */
1229   if( dvd == NULL || titlenum < 0 ) {
1230     errno = EINVAL;
1231     return -1;
1232   }
1233 
1234   switch( domain ) {
1235   case DVD_READ_INFO_FILE:
1236     if( titlenum == 0 )
1237       strcpy( filename, "/VIDEO_TS/VIDEO_TS.IFO" );
1238     else
1239       sprintf( filename, "/VIDEO_TS/VTS_%02i_0.IFO", titlenum );
1240 
1241     break;
1242   case DVD_READ_INFO_BACKUP_FILE:
1243     if( titlenum == 0 )
1244       strcpy( filename, "/VIDEO_TS/VIDEO_TS.BUP" );
1245     else
1246       sprintf( filename, "/VIDEO_TS/VTS_%02i_0.BUP", titlenum );
1247 
1248     break;
1249   case DVD_READ_MENU_VOBS:
1250     if( dvd->isImageFile )
1251       return DVDFileStatVOBUDF( reader, titlenum, 1, statbuf );
1252     else
1253       return DVDFileStatVOBPath( reader, titlenum, 1, statbuf );
1254 
1255     break;
1256   case DVD_READ_TITLE_VOBS:
1257     if( titlenum == 0 )
1258       return -1;
1259 
1260     if( dvd->isImageFile )
1261       return DVDFileStatVOBUDF( reader, titlenum, 0, statbuf );
1262     else
1263       return DVDFileStatVOBPath( reader, titlenum, 0, statbuf );
1264 
1265     break;
1266   default:
1267     Log1(reader, "Invalid domain for file stat." );
1268     errno = EINVAL;
1269     return -1;
1270   }
1271 
1272   if( dvd->isImageFile ) {
1273     if( UDFFindFile( reader, filename, &size ) ) {
1274       statbuf->size = size;
1275       statbuf->nr_parts = 1;
1276       statbuf->parts_size[ 0 ] = size;
1277       return 0;
1278     }
1279   } else {
1280     char full_path[ PATH_MAX + 1 ];
1281 
1282     if( findDVDFile( reader, filename, full_path ) ) {
1283       if( dvdstat( full_path, &fileinfo ) < 0 )
1284         Log1(reader, "Can't stat() %s.", filename );
1285       else {
1286         statbuf->size = fileinfo.st_size;
1287         statbuf->nr_parts = 1;
1288         statbuf->parts_size[ 0 ] = statbuf->size;
1289         return 0;
1290       }
1291     }
1292   }
1293   return -1;
1294 }
1295 
1296 /* Internal, but used from dvd_udf.c */
1297 int InternalUDFReadBlocksRaw( const dvd_reader_t *ctx, uint32_t lb_number,
1298                       size_t block_count, unsigned char *data,
1299                       int encrypted )
1300 {
1301   int ret;
1302 
1303   if( !ctx->rd->dev ) {
1304     Log0(ctx, "Fatal error in block read." );
1305     return -1;
1306   }
1307 
1308   ret = dvdinput_seek( ctx->rd->dev, (int) lb_number );
1309   if( ret != (int) lb_number ) {
1310     Log1(ctx, "Can't seek to block %u", lb_number );
1311     return ret;
1312   }
1313 
1314   ret = dvdinput_read( ctx->rd->dev, (char *) data,
1315                        (int) block_count, encrypted );
1316   return ret;
1317 }
1318 
1319 /* This is using a single input and starting from 'dvd_file->lb_start' offset.
1320  *
1321  * Reads 'block_count' blocks from 'dvd_file' at block offset 'offset'
1322  * into the buffer located at 'data' and if 'encrypted' is set
1323  * descramble the data if it's encrypted.  Returning either an
1324  * negative error or the number of blocks read. */
1325 static int DVDReadBlocksUDF( const dvd_file_t *dvd_file, uint32_t offset,
1326                              size_t block_count, unsigned char *data,
1327                              int encrypted )
1328 {
1329   /* If the cache is present and we don't need to decrypt, use the cache to
1330    * feed the data */
1331   if( dvd_file->cache && (encrypted & DVDINPUT_READ_DECRYPT) == 0 ) {
1332     /* Check if we don't exceed the cache (or file) size */
1333     if( block_count + offset > (size_t) dvd_file->filesize )
1334       return 0;
1335 
1336     /* Copy the cache at a specified offset into data. offset and block_count
1337      * must be converted into bytes */
1338     memcpy( data, dvd_file->cache + (off_t)offset * (off_t)DVD_VIDEO_LB_LEN,
1339             (off_t)block_count * (off_t)DVD_VIDEO_LB_LEN );
1340 
1341     /* return the amount of blocks copied */
1342     return block_count;
1343   } else {
1344     /* use dvdinput access */
1345     return InternalUDFReadBlocksRaw( dvd_file->ctx, dvd_file->lb_start + offset,
1346                              block_count, data, encrypted );
1347   }
1348 }
1349 
1350 /* This is using possibly several inputs and starting from an offset of '0'.
1351  *
1352  * Reads 'block_count' blocks from 'dvd_file' at block offset 'offset'
1353  * into the buffer located at 'data' and if 'encrypted' is set
1354  * descramble the data if it's encrypted.  Returning either an
1355  * negative error or the number of blocks read. */
1356 static int DVDReadBlocksPath( const dvd_file_t *dvd_file, unsigned int offset,
1357                               size_t block_count, unsigned char *data,
1358                               int encrypted )
1359 {
1360   const dvd_reader_t *ctx = dvd_file->ctx;
1361   int i;
1362   int ret, ret2, off;
1363 
1364   ret = 0;
1365   ret2 = 0;
1366   for( i = 0; i < TITLES_MAX; ++i ) {
1367     if( !dvd_file->title_sizes[ i ] ) return 0; /* Past end of file */
1368 
1369     if( offset < dvd_file->title_sizes[ i ] ) {
1370       if( ( offset + block_count ) <= dvd_file->title_sizes[ i ] ) {
1371         off = dvdinput_seek( dvd_file->title_devs[ i ], (int)offset );
1372         if( off < 0 || off != (int)offset ) {
1373           Log1(ctx, "Can't seek to block %u", offset );
1374           return off < 0 ? off : 0;
1375         }
1376         ret = dvdinput_read( dvd_file->title_devs[ i ], data,
1377                              (int)block_count, encrypted );
1378         break;
1379       } else {
1380         size_t part1_size = dvd_file->title_sizes[ i ] - offset;
1381         /* FIXME: Really needs to be a while loop.
1382          * (This is only true if you try and read >1GB at a time) */
1383 
1384         /* Read part 1 */
1385         off = dvdinput_seek( dvd_file->title_devs[ i ], (int)offset );
1386         if( off < 0 || off != (int)offset ) {
1387           Log1(ctx, "Can't seek to block %u", offset );
1388           return off < 0 ? off : 0;
1389         }
1390         ret = dvdinput_read( dvd_file->title_devs[ i ], data,
1391                              (int)part1_size, encrypted );
1392         if( ret < 0 ) return ret;
1393         /* FIXME: This is wrong if i is the last file in the set.
1394          * also error from this read will not show in ret. */
1395 
1396         /* Does the next part exist? If not then return now. */
1397         if( i + 1 >= TITLES_MAX || !dvd_file->title_devs[ i + 1 ] )
1398           return ret;
1399 
1400         /* Read part 2 */
1401         off = dvdinput_seek( dvd_file->title_devs[ i + 1 ], 0 );
1402         if( off < 0 || off != 0 ) {
1403           Log1(ctx, "Can't seek to block %d", 0 );
1404           return off < 0 ? off : 0;
1405         }
1406         ret2 = dvdinput_read( dvd_file->title_devs[ i + 1 ],
1407                               data + ( part1_size
1408                                        * (int64_t)DVD_VIDEO_LB_LEN ),
1409                               (int)(block_count - part1_size),
1410                               encrypted );
1411         if( ret2 < 0 ) return ret2;
1412         break;
1413       }
1414     } else {
1415       offset -= dvd_file->title_sizes[ i ];
1416     }
1417   }
1418 
1419   return ret + ret2;
1420 }
1421 
1422 /* This is broken reading more than 2Gb at a time is ssize_t is 32-bit. */
1423 ssize_t DVDReadBlocks( dvd_file_t *dvd_file, int offset,
1424                        size_t block_count, unsigned char *data )
1425 {
1426   dvd_reader_t *ctx = dvd_file->ctx;
1427   dvd_reader_device_t *dvd = ctx->rd;
1428   int ret;
1429 
1430   /* Check arguments. */
1431   if( dvd_file == NULL || offset < 0 || data == NULL )
1432     return -1;
1433 
1434   /* Hack, and it will still fail for multiple opens in a threaded app ! */
1435   if( dvd->css_title != dvd_file->css_title ) {
1436       dvd->css_title = dvd_file->css_title;
1437     if( dvd->isImageFile ) {
1438       dvdinput_title( dvd->dev, (int)dvd_file->lb_start );
1439     }
1440     /* Here each vobu has it's own dvdcss handle, so no need to update
1441     else {
1442       dvdinput_title( dvd_file->title_devs[ 0 ], (int)dvd_file->lb_start );
1443     }*/
1444   }
1445 
1446   if( dvd->isImageFile ) {
1447     ret = DVDReadBlocksUDF( dvd_file, (uint32_t)offset,
1448                             block_count, data, DVDINPUT_READ_DECRYPT );
1449   } else {
1450     ret = DVDReadBlocksPath( dvd_file, (unsigned int)offset,
1451                              block_count, data, DVDINPUT_READ_DECRYPT );
1452   }
1453 
1454   return (ssize_t)ret;
1455 }
1456 
1457 int32_t DVDFileSeek( dvd_file_t *dvd_file, int32_t offset )
1458 {
1459   /* Check arguments. */
1460   if( dvd_file == NULL || offset < 0 )
1461     return -1;
1462 
1463   if( offset > dvd_file->filesize * DVD_VIDEO_LB_LEN ) {
1464     return -1;
1465   }
1466   dvd_file->seek_pos = (uint32_t) offset;
1467   return offset;
1468 }
1469 
1470 int DVDFileSeekForce(dvd_file_t *dvd_file, int offset, int force_size)
1471 {
1472   dvd_reader_t *ctx = dvd_file->ctx;
1473   dvd_reader_device_t *dvd = ctx->rd;
1474   /* Check arguments. */
1475   if( dvd_file == NULL || offset <= 0 )
1476       return -1;
1477 
1478   if( dvd->isImageFile ) {
1479     if( force_size < 0 )
1480       force_size = (offset - 1) / DVD_VIDEO_LB_LEN + 1;
1481     if( dvd_file->filesize < force_size ) {
1482       dvd_file->filesize = force_size;
1483       free(dvd_file->cache);
1484       dvd_file->cache = NULL;
1485       Log2(ctx, "Ignored size of file indicated in UDF.");
1486     }
1487   }
1488 
1489   if( offset > dvd_file->filesize * DVD_VIDEO_LB_LEN )
1490     return -1;
1491 
1492   dvd_file->seek_pos = (uint32_t) offset;
1493   return offset;
1494 }
1495 
1496 ssize_t DVDReadBytes( dvd_file_t *dvd_file, void *data, size_t byte_size )
1497 {
1498   dvd_reader_t *ctx = dvd_file->ctx;
1499   dvd_reader_device_t *dvd = ctx->rd;
1500   unsigned char *secbuf_base, *secbuf;
1501   unsigned int numsec, seek_sector, seek_byte;
1502   int ret;
1503 
1504   /* Check arguments. */
1505   if( dvd_file == NULL || data == NULL || (ssize_t)byte_size < 0 )
1506     return -1;
1507 
1508   seek_sector = dvd_file->seek_pos / DVD_VIDEO_LB_LEN;
1509   seek_byte   = dvd_file->seek_pos % DVD_VIDEO_LB_LEN;
1510 
1511   numsec = ( ( seek_byte + byte_size ) / DVD_VIDEO_LB_LEN ) +
1512     ( ( ( seek_byte + byte_size ) % DVD_VIDEO_LB_LEN ) ? 1 : 0 );
1513 
1514   secbuf_base = malloc( numsec * DVD_VIDEO_LB_LEN + 2048 );
1515   if( !secbuf_base ) {
1516     Log0(ctx, "Can't allocate memory for file read" );
1517     return 0;
1518   }
1519   secbuf = (unsigned char *)(((uintptr_t)secbuf_base & ~((uintptr_t)2047)) + 2048);
1520 
1521   if( dvd->isImageFile ) {
1522     ret = DVDReadBlocksUDF( dvd_file, (uint32_t) seek_sector,
1523                             (size_t) numsec, secbuf, DVDINPUT_NOFLAGS );
1524   } else {
1525     ret = DVDReadBlocksPath( dvd_file, seek_sector,
1526                              (size_t) numsec, secbuf, DVDINPUT_NOFLAGS );
1527   }
1528 
1529   if( ret != (int) numsec ) {
1530     free( secbuf_base );
1531     return ret < 0 ? ret : 0;
1532   }
1533 
1534   memcpy( data, &(secbuf[ seek_byte ]), byte_size );
1535   free( secbuf_base );
1536 
1537   DVDFileSeekForce(dvd_file, dvd_file->seek_pos + byte_size, -1);
1538   return byte_size;
1539 }
1540 
1541 ssize_t DVDFileSize( dvd_file_t *dvd_file )
1542 {
1543   /* Check arguments. */
1544   if( dvd_file == NULL )
1545     return -1;
1546 
1547   return dvd_file->filesize;
1548 }
1549 
1550 int DVDDiscID( dvd_reader_t *dvd, unsigned char *discid )
1551 {
1552   struct md5_s ctx;
1553   int title;
1554   int title_sets;
1555   int nr_of_files = 0;
1556   ifo_handle_t *vmg_ifo;
1557 
1558   /* Check arguments. */
1559   if( dvd == NULL || discid == NULL )
1560     return 0;
1561 
1562   vmg_ifo = ifoOpen( dvd, 0 );
1563   if( !vmg_ifo ) {
1564     Log0(dvd, "DVDDiscId, failed to open VMG IFO" );
1565     return -1;
1566   }
1567 
1568   title_sets = vmg_ifo->vmgi_mat->vmg_nr_of_title_sets + 1;
1569   ifoClose( vmg_ifo );
1570 
1571   if( title_sets > 10 )
1572   	title_sets = 10;
1573 
1574   /* Go through the first IFO:s, in order, up until the tenth,
1575    * and md5sum them, i.e  VIDEO_TS.IFO and VTS_0?_0.IFO */
1576   InitMD5( &ctx );
1577   for( title = 0; title < title_sets; title++ ) {
1578     dvd_file_t *dvd_file = DVDOpenFile( dvd, title, DVD_READ_INFO_FILE );
1579     if( dvd_file != NULL ) {
1580       ssize_t bytes_read;
1581       ssize_t file_size = dvd_file->filesize * DVD_VIDEO_LB_LEN;
1582       char *buffer_base = malloc( file_size + 2048 );
1583 
1584       if( buffer_base == NULL ) {
1585           DVDCloseFile( dvd_file );
1586           Log0(dvd, "DVDDiscId, failed to allocate memory for file read" );
1587           return -1;
1588       }
1589 
1590       char *buffer = (char *)(((uintptr_t)buffer_base & ~((uintptr_t)2047)) + 2048);
1591 
1592       bytes_read = DVDReadBytes( dvd_file, buffer, file_size );
1593       if( bytes_read != file_size ) {
1594           Log1(dvd, "DVDDiscId read returned %zd bytes"
1595                    ", wanted %zd", bytes_read, file_size );
1596           DVDCloseFile( dvd_file );
1597           free( buffer_base );
1598           return -1;
1599       }
1600 
1601       AddMD5( &ctx, buffer, file_size );
1602 
1603       DVDCloseFile( dvd_file );
1604       free( buffer_base );
1605       nr_of_files++;
1606     }
1607   }
1608   EndMD5( &ctx );
1609   memcpy( discid, ctx.buf, 16 );
1610   if(!nr_of_files)
1611     return -1;
1612 
1613   return 0;
1614 }
1615 
1616 
1617 int DVDISOVolumeInfo( dvd_reader_t *ctx,
1618                       char *volid, unsigned int volid_size,
1619                       unsigned char *volsetid, unsigned int volsetid_size )
1620 {
1621   dvd_reader_device_t *dvd = ctx->rd;
1622   unsigned char *buffer, *buffer_base;
1623   int ret;
1624 
1625   /* Check arguments. */
1626   if( dvd == NULL )
1627     return 0;
1628 
1629   if( dvd->dev == NULL ) {
1630     /* No block access, so no ISO... */
1631     return -1;
1632   }
1633 
1634   buffer_base = malloc( DVD_VIDEO_LB_LEN + 2048 );
1635 
1636   if( buffer_base == NULL ) {
1637     Log0(ctx, "DVDISOVolumeInfo, failed to "
1638              "allocate memory for file read" );
1639     return -1;
1640   }
1641 
1642   buffer = (unsigned char *)(((uintptr_t)buffer_base & ~((uintptr_t)2047)) + 2048);
1643 
1644   ret = InternalUDFReadBlocksRaw( ctx, 16, 1, buffer, 0 );
1645   if( ret != 1 ) {
1646     Log0(ctx, "DVDISOVolumeInfo, failed to "
1647              "read ISO9660 Primary Volume Descriptor" );
1648     free( buffer_base );
1649     return -1;
1650   }
1651 
1652   if( (volid != NULL) && (volid_size > 0) ) {
1653     unsigned int n;
1654     for(n = 0; n < 32; n++) {
1655       if(buffer[40+n] == 0x20) {
1656         break;
1657       }
1658     }
1659 
1660     if(volid_size > n+1) {
1661       volid_size = n+1;
1662     }
1663 
1664     memcpy(volid, &buffer[40], volid_size-1);
1665     volid[volid_size-1] = '\0';
1666   }
1667 
1668   if( (volsetid != NULL) && (volsetid_size > 0) ) {
1669     if(volsetid_size > 128) {
1670       volsetid_size = 128;
1671     }
1672     memcpy(volsetid, &buffer[190], volsetid_size);
1673   }
1674   free( buffer_base );
1675   return 0;
1676 }
1677 
1678 
1679 int DVDUDFVolumeInfo( dvd_reader_t *ctx,
1680                       char *volid, unsigned int volid_size,
1681                       unsigned char *volsetid, unsigned int volsetid_size )
1682 {
1683   int ret;
1684   /* Check arguments. */
1685   if( ctx == NULL || ctx->rd == NULL )
1686     return -1;
1687 
1688   if( ctx->rd->dev == NULL ) {
1689     /* No block access, so no UDF VolumeSet Identifier */
1690     return -1;
1691   }
1692 
1693   if( (volid != NULL) && (volid_size > 0) ) {
1694     ret = UDFGetVolumeIdentifier(ctx, volid, volid_size);
1695     if(!ret) {
1696       return -1;
1697     }
1698   }
1699   if( (volsetid != NULL) && (volsetid_size > 0) ) {
1700     ret =  UDFGetVolumeSetIdentifier(ctx, volsetid, volsetid_size);
1701     if(!ret) {
1702       return -1;
1703     }
1704   }
1705 
1706   return 0;
1707 }
1708