summaryrefslogtreecommitdiffstats
path: root/dvdread/dvd_udf.c
diff options
context:
space:
mode:
Diffstat (limited to 'dvdread/dvd_udf.c')
-rw-r--r--dvdread/dvd_udf.c282
1 files changed, 250 insertions, 32 deletions
diff --git a/dvdread/dvd_udf.c b/dvdread/dvd_udf.c
index 56682e7405..19b69560cf 100644
--- a/dvdread/dvd_udf.c
+++ b/dvdread/dvd_udf.c
@@ -37,23 +37,173 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#ifndef __MINGW32__
-#include <sys/ioctl.h>
-#endif
+
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
+#include <errno.h>
+
+#if defined(HAVE_INTTYPES_H)
#include <inttypes.h>
+#elif defined(HAVE_STDINT_H)
+#include <stdint.h>
+#endif
#include "dvd_reader.h"
#include "dvd_udf.h"
+#include "dvdread_internal.h"
+
+#ifndef EMEDIUMTYPE
+#define EMEDIUMTYPE ENOENT
+#endif
+
+#ifndef HAVE_UINTPTR_T
+#warning "Assuming that (unsigned long) can hold (void *)"
+typedef unsigned long uintptr_t;
+#endif
+
+#define DVD_ALIGN(ptr) (void *)((((uintptr_t)(ptr)) + (DVD_VIDEO_LB_LEN-1)) \
+ / DVD_VIDEO_LB_LEN * DVD_VIDEO_LB_LEN)
+
+typedef struct {
+ void *start;
+ void *aligned;
+} dvdalign_ptrs_t;
+
+typedef struct {
+ dvdalign_ptrs_t *ptrs;
+ uint32_t ptrs_in_use;
+ uint32_t ptrs_max;
+} dvdalign_t;
+
+extern void *GetAlignHandle(dvd_reader_t *device);
+extern void SetAlignHandle(dvd_reader_t *device, void *align);
+
+/**
+ * Allocates aligned memory (for use with reads from raw/O_DIRECT devices).
+ * This memory must be freed with dvdalign_free()
+ * The size of the memory that is allocate is num_lbs*2048 bytes.
+ * The memory will be suitably aligned for use with
+ * block reads from raw/O_DIRECT device.
+ * @param num_lbs Number of logical blocks (2048 bytes) to allocate.
+ * @return Returns pointer to allocated memory, or NULL on failure
+ * This isn't supposed to be fast/efficient, if that is needed
+ * this function should be rewritten to use posix_memalign or similar.
+ * It's just needed for aligning memory for small block reads from
+ * raw/O_DIRECT devices.
+ * We assume that 2048 is enough alignment for all systems at the moment.
+ * Not thread safe. Only use this from one thread.
+ * Depends on sizeof(unsigned long) being at least as large as sizeof(void *)
+ */
+static void *dvdalign_lbmalloc(dvd_reader_t *device, uint32_t num_lbs)
+{
+ void *m;
+ int n;
+ dvdalign_t *a;
+
+ m = malloc((num_lbs+1)*DVD_VIDEO_LB_LEN);
+ if(m == NULL) {
+ return m;
+ }
+ a = (dvdalign_t *)GetAlignHandle(device);
+ if(a == NULL) {
+ a = malloc(sizeof(dvdalign_t));
+ if(a == NULL) {
+ return a;
+ }
+ a->ptrs = NULL;
+ a->ptrs_in_use = 0;
+ a->ptrs_max = 0;
+ SetAlignHandle(device, (void *)a);
+ }
+
+ if(a->ptrs_in_use >= a->ptrs_max) {
+ a->ptrs = realloc(a->ptrs, (a->ptrs_max+10)*sizeof(dvdalign_ptrs_t));
+ if(a->ptrs == NULL) {
+ free(m);
+ return NULL;
+ }
+ a->ptrs_max+=10;
+ for(n = a->ptrs_in_use; n < a->ptrs_max; n++) {
+ a->ptrs[n].start = NULL;
+ a->ptrs[n].aligned = NULL;
+ }
+ n = a->ptrs_in_use;
+ } else {
+ for(n = 0; n < a->ptrs_max; n++) {
+ if(a->ptrs[n].start == NULL) {
+ break;
+ }
+ }
+ }
+
+ a->ptrs[n].start = m;
+ a->ptrs[n].aligned = DVD_ALIGN(m);
+
+ a->ptrs_in_use++;
+
+ /* If this function starts to be used too much print a warning.
+ Either there is a memory leak somewhere or we need to rewrite this to
+ a more efficient version.
+ */
+ if(a->ptrs_in_use > 50) {
+ if(dvdread_verbose(device) >= 0) {
+ fprintf(stderr, "libdvdread: dvdalign_lbmalloc(), more allocs than supposed: %u\n", a->ptrs_in_use);
+ }
+ }
+
+ return a->ptrs[n].aligned;
+}
+
+/**
+ * Frees memory allocated with dvdalign_lbmemory()
+ * @param ptr Pointer to memory space to free
+ * Not thread safe.
+ */
+static void dvdalign_lbfree(dvd_reader_t *device, void *ptr)
+{
+ int n;
+ dvdalign_t *a;
+
+ a = (dvdalign_t *)GetAlignHandle(device);
+ if(a && a->ptrs) {
+ for(n = 0; n < a->ptrs_max; n++) {
+ if(a->ptrs[n].aligned == ptr) {
+ free(a->ptrs[n].start);
+ a->ptrs[n].start = NULL;
+ a->ptrs[n].aligned = NULL;
+ a->ptrs_in_use--;
+ if(a->ptrs_in_use == 0) {
+ free(a->ptrs);
+ a->ptrs = NULL;
+ a->ptrs_max = 0;
+ free(a);
+ a = NULL;
+ SetAlignHandle(device, (void *)a);
+ }
+ return;
+ }
+ }
+ }
+ if(dvdread_verbose(device) >= 0) {
+ fprintf(stderr, "libdvdread: dvdalign_lbfree(), error trying to free mem: %08lx (%u)\n", (unsigned long)ptr, a ? a->ptrs_in_use : 0);
+ }
+}
+
/* Private but located in/shared with dvd_reader.c */
extern int UDFReadBlocksRaw( dvd_reader_t *device, uint32_t lb_number,
size_t block_count, unsigned char *data,
int encrypted );
-/* It's required to either fail or deliver all the blocks asked for. */
+/** @internal
+ * Its required to either fail or deliver all the blocks asked for.
+ *
+ * @param data Pointer to a buffer where data is returned. This must be large
+ * enough to hold lb_number*2048 bytes.
+ * It must be aligned to system specific (2048) logical blocks size when
+ * reading from raw/O_DIRECT device.
+ */
static int DVDReadLBUDF( dvd_reader_t *device, uint32_t lb_number,
size_t block_count, unsigned char *data,
int encrypted )
@@ -149,12 +299,24 @@ typedef enum {
extern void *GetUDFCacheHandle(dvd_reader_t *device);
extern void SetUDFCacheHandle(dvd_reader_t *device, void *cache);
-void FreeUDFCache(void *cache)
+
+void FreeUDFCache(dvd_reader_t *device, void *cache)
{
+ int n;
+
struct udf_cache *c = (struct udf_cache *)cache;
if(c == NULL) {
return;
}
+
+ for(n = 0; n < c->lb_num; n++) {
+ if(c->lbs[n].data) {
+ /* free data */
+ dvdalign_lbfree(device, c->lbs[n].data);
+ }
+ }
+ c->lb_num = 0;
+
if(c->lbs) {
free(c->lbs);
}
@@ -493,7 +655,7 @@ static int UDFFileIdentifier( uint8_t *data, uint8_t *FileCharacteristics,
static int UDFMapICB( dvd_reader_t *device, struct AD ICB, uint8_t *FileType,
struct Partition *partition, struct AD *File )
{
- uint8_t LogBlock[DVD_VIDEO_LB_LEN];
+ uint8_t *LogBlock;
uint32_t lbnum;
uint16_t TagID;
struct icbmap tmpmap;
@@ -506,6 +668,11 @@ static int UDFMapICB( dvd_reader_t *device, struct AD ICB, uint8_t *FileType,
return 1;
}
+ LogBlock = dvdalign_lbmalloc(device, 1);
+ if(!LogBlock) {
+ return 0;
+ }
+
do {
if( DVDReadLBUDF( device, lbnum++, 1, LogBlock, 0 ) <= 0 ) {
TagID = 0;
@@ -518,11 +685,13 @@ static int UDFMapICB( dvd_reader_t *device, struct AD ICB, uint8_t *FileType,
tmpmap.file = *File;
tmpmap.filetype = *FileType;
SetUDFCache(device, MapCache, tmpmap.lbn, &tmpmap);
+ dvdalign_lbfree(device, LogBlock);
return 1;
};
} while( ( lbnum <= partition->Start + ICB.Location + ( ICB.Length - 1 )
/ DVD_VIDEO_LB_LEN ) && ( TagID != 261 ) );
+ dvdalign_lbfree(device, LogBlock);
return 0;
}
@@ -537,7 +706,7 @@ static int UDFScanDir( dvd_reader_t *device, struct AD Dir, char *FileName,
int cache_file_info)
{
char filename[ MAX_UDF_FILE_NAME_LEN ];
- uint8_t directory[ 2 * DVD_VIDEO_LB_LEN ];
+ uint8_t *directory;
uint32_t lbnum;
uint16_t TagID;
uint8_t filechar;
@@ -556,19 +725,13 @@ static int UDFScanDir( dvd_reader_t *device, struct AD Dir, char *FileName,
if(!GetUDFCache(device, LBUDFCache, lbnum, &cached_dir)) {
dir_lba = (Dir.Length + DVD_VIDEO_LB_LEN) / DVD_VIDEO_LB_LEN;
- if((cached_dir = malloc(dir_lba * DVD_VIDEO_LB_LEN)) == NULL) {
+ if((cached_dir = dvdalign_lbmalloc(device, dir_lba)) == NULL) {
return 0;
}
if( DVDReadLBUDF( device, lbnum, dir_lba, cached_dir, 0) <= 0 ) {
- free(cached_dir);
+ dvdalign_lbfree(device, cached_dir);
cached_dir = NULL;
}
- /*
- if(cached_dir) {
- fprintf(stderr, "malloc dir: %d\n",
- dir_lba * DVD_VIDEO_LB_LEN);
- }
- */
SetUDFCache(device, LBUDFCache, lbnum, &cached_dir);
} else {
in_cache = 1;
@@ -615,7 +778,12 @@ static int UDFScanDir( dvd_reader_t *device, struct AD Dir, char *FileName,
return 0;
}
+ directory = dvdalign_lbmalloc(device, 2);
+ if(!directory) {
+ return 0;
+ }
if( DVDReadLBUDF( device, lbnum, 2, directory, 0 ) <= 0 ) {
+ dvdalign_lbfree(device, directory);
return 0;
}
@@ -626,6 +794,7 @@ static int UDFScanDir( dvd_reader_t *device, struct AD Dir, char *FileName,
p -= DVD_VIDEO_LB_LEN;
Dir.Length -= DVD_VIDEO_LB_LEN;
if( DVDReadLBUDF( device, lbnum, 2, directory, 0 ) <= 0 ) {
+ dvdalign_lbfree(device, directory);
return 0;
}
}
@@ -634,13 +803,16 @@ static int UDFScanDir( dvd_reader_t *device, struct AD Dir, char *FileName,
p += UDFFileIdentifier( &directory[ p ], &filechar,
filename, FileICB );
if( !strcasecmp( FileName, filename ) ) {
+ dvdalign_lbfree(device, directory);
return 1;
}
} else {
+ dvdalign_lbfree(device, directory);
return 0;
}
}
+ dvdalign_lbfree(device, directory);
return 0;
}
@@ -648,7 +820,7 @@ static int UDFScanDir( dvd_reader_t *device, struct AD Dir, char *FileName,
static int UDFGetAVDP( dvd_reader_t *device,
struct avdp_t *avdp)
{
- uint8_t Anchor[ DVD_VIDEO_LB_LEN ];
+ uint8_t *Anchor;
uint32_t lbnum, MVDS_location, MVDS_length;
uint16_t TagID;
uint32_t lastsector;
@@ -664,6 +836,10 @@ static int UDFGetAVDP( dvd_reader_t *device,
lbnum = 256; /* Try #1, prime anchor */
terminate = 0;
+ Anchor = dvdalign_lbmalloc(device, 1);
+ if(!Anchor) {
+ return 0;
+ }
for(;;) {
if( DVDReadLBUDF( device, lbnum, 1, Anchor, 0 ) > 0 ) {
UDFDescriptor( Anchor, &TagID );
@@ -672,10 +848,13 @@ static int UDFGetAVDP( dvd_reader_t *device,
}
if (TagID != 2) {
/* Not an anchor */
- if( terminate ) return 0; /* Final try failed */
+ if( terminate ) {
+ dvdalign_lbfree(device, Anchor);
+ errno = EMEDIUMTYPE;
+ return 0; /* Final try failed */
+ }
if( lastsector ) {
-
/* We already found the last sector. Try #3, alternative
* backup anchor. If that fails, don't try again.
*/
@@ -688,6 +867,8 @@ static int UDFGetAVDP( dvd_reader_t *device,
lbnum = lastsector - 256;
} else {
/* Unable to find last sector */
+ dvdalign_lbfree(device, Anchor);
+ errno = EMEDIUMTYPE;
return 0;
}
}
@@ -708,6 +889,7 @@ static int UDFGetAVDP( dvd_reader_t *device,
SetUDFCache(device, AVDPCache, 0, avdp);
+ dvdalign_lbfree(device, Anchor);
return 1;
}
@@ -719,7 +901,7 @@ static int UDFGetAVDP( dvd_reader_t *device,
static int UDFFindPartition( dvd_reader_t *device, int partnum,
struct Partition *part )
{
- uint8_t LogBlock[ DVD_VIDEO_LB_LEN ];
+ uint8_t *LogBlock;
uint32_t lbnum, MVDS_location, MVDS_length;
uint16_t TagID;
int i, volvalid;
@@ -730,6 +912,10 @@ static int UDFFindPartition( dvd_reader_t *device, int partnum,
return 0;
}
+ LogBlock = dvdalign_lbmalloc(device, 1);
+ if(!LogBlock) {
+ return 0;
+ }
/* Main volume descriptor */
MVDS_location = avdp.mvds.location;
MVDS_length = avdp.mvds.length;
@@ -774,6 +960,7 @@ static int UDFFindPartition( dvd_reader_t *device, int partnum,
}
} while( i-- && ( ( !part->valid ) || ( !volvalid ) ) );
+ dvdalign_lbfree(device, LogBlock);
/* We only care for the partition, not the volume */
return part->valid;
}
@@ -781,7 +968,7 @@ static int UDFFindPartition( dvd_reader_t *device, int partnum,
uint32_t UDFFindFile( dvd_reader_t *device, char *filename,
uint32_t *filesize )
{
- uint8_t LogBlock[ DVD_VIDEO_LB_LEN ];
+ uint8_t *LogBlock;
uint32_t lbnum;
uint16_t TagID;
struct Partition partition;
@@ -790,7 +977,9 @@ uint32_t UDFFindFile( dvd_reader_t *device, char *filename,
char *token;
uint8_t filetype;
+ if(filesize) {
*filesize = 0;
+ }
tokenline[0] = '\0';
strcat( tokenline, filename );
@@ -798,9 +987,15 @@ uint32_t UDFFindFile( dvd_reader_t *device, char *filename,
if(!(GetUDFCache(device, PartitionCache, 0, &partition) &&
GetUDFCache(device, RootICBCache, 0, &RootICB))) {
/* Find partition, 0 is the standard location for DVD Video.*/
- if( !UDFFindPartition( device, 0, &partition ) ) return 0;
+ if( !UDFFindPartition( device, 0, &partition ) ) {
+ return 0;
+ }
SetUDFCache(device, PartitionCache, 0, &partition);
+ LogBlock = dvdalign_lbmalloc(device, 1);
+ if(!LogBlock) {
+ return 0;
+ }
/* Find root dir ICB */
lbnum = partition.Start;
do {
@@ -817,16 +1012,25 @@ uint32_t UDFFindFile( dvd_reader_t *device, char *filename,
} while( ( lbnum < partition.Start + partition.Length )
&& ( TagID != 8 ) && ( TagID != 256 ) );
+ dvdalign_lbfree(device, LogBlock);
+
/* Sanity checks. */
- if( TagID != 256 ) return 0;
- if( RootICB.Partition != 0 ) return 0;
+ if( TagID != 256 ) {
+ return 0;
+ }
+ if( RootICB.Partition != 0 ) {
+ return 0;
+ }
SetUDFCache(device, RootICBCache, 0, &RootICB);
}
/* Find root dir */
- if( !UDFMapICB( device, RootICB, &filetype, &partition, &File ) ) return 0;
- if( filetype != 4 ) return 0; /* Root dir should be dir */
-
+ if( !UDFMapICB( device, RootICB, &filetype, &partition, &File ) ) {
+ return 0;
+ }
+ if( filetype != 4 ) {
+ return 0; /* Root dir should be dir */
+ }
{
int cache_file_info = 0;
/* Tokenize filepath */
@@ -849,15 +1053,20 @@ uint32_t UDFFindFile( dvd_reader_t *device, char *filename,
}
/* Sanity check. */
- if( File.Partition != 0 ) return 0;
+ if( File.Partition != 0 ) {
+ return 0;
+ }
+ if(filesize) {
*filesize = File.Length;
+ }
/* Hack to not return partition.Start for empty files. */
- if( !File.Location )
+ if( !File.Location ) {
return 0;
- else
+ } else {
return partition.Start + File.Location;
}
+}
@@ -865,7 +1074,8 @@ uint32_t UDFFindFile( dvd_reader_t *device, char *filename,
* Gets a Descriptor .
* Returns 1 if descriptor found, 0 on error.
* id, tagid of descriptor
- * bufsize, size of BlockBuf (must be >= DVD_VIDEO_LB_LEN).
+ * bufsize, size of BlockBuf (must be >= DVD_VIDEO_LB_LEN)
+ * and aligned for raw/O_DIRECT read.
*/
static int UDFGetDescriptor( dvd_reader_t *device, int id,
uint8_t *descriptor, int bufsize)
@@ -919,19 +1129,25 @@ static int UDFGetDescriptor( dvd_reader_t *device, int id,
}
} while( i-- && ( !desc_found ) );
+
return desc_found;
}
static int UDFGetPVD(dvd_reader_t *device, struct pvd_t *pvd)
{
- uint8_t pvd_buf[DVD_VIDEO_LB_LEN];
+ uint8_t *pvd_buf;
if(GetUDFCache(device, PVDCache, 0, pvd)) {
return 1;
}
- if(!UDFGetDescriptor( device, 1, pvd_buf, sizeof(pvd_buf))) {
+ pvd_buf = dvdalign_lbmalloc(device, 1);
+ if(!pvd_buf) {
+ return 0;
+ }
+ if(!UDFGetDescriptor( device, 1, pvd_buf, 1*DVD_VIDEO_LB_LEN)) {
+ dvdalign_lbfree(device, pvd_buf);
return 0;
}
@@ -939,6 +1155,8 @@ static int UDFGetPVD(dvd_reader_t *device, struct pvd_t *pvd)
memcpy(pvd->VolumeSetIdentifier, &pvd_buf[72], 128);
SetUDFCache(device, PVDCache, 0, pvd);
+ dvdalign_lbfree(device, pvd_buf);
+
return 1;
}