device.c 21.3 KB
Newer Older
1
2
3
/*****************************************************************************
 * device.h: DVD device access
 *****************************************************************************
4
 * Copyright (C) 1998-2006 VideoLAN
5
 *
6
 * Authors: Stéphane Borel <stef@via.ecp.fr>
7
 *          Sam Hocevar <sam@zoy.org>
8
 *          Håkan Hjort <d95hjort@dtek.chalmers.se>
9
 *
10
 * libdvdcss is free software; you can redistribute it and/or modify
11
12
13
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
14
 *
15
 * libdvdcss is distributed in the hope that it will be useful,
16
17
18
19
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
Diego Biurrun's avatar
Diego Biurrun committed
20
 * You should have received a copy of the GNU General Public License along
21
 * with libdvdcss; if not, write to the Free Software Foundation, Inc.,
Diego Biurrun's avatar
Diego Biurrun committed
22
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23
24
25
26
27
28
29
30
31
32
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
#include "config.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
33
34
35
#ifdef HAVE_ERRNO_H
#   include <errno.h>
#endif
36
37
#include <sys/types.h>
#include <sys/stat.h>
38
39
40
#ifdef HAVE_SYS_PARAM_H
#   include <sys/param.h>
#endif
41
42
43
44
#include <fcntl.h>

#ifdef HAVE_UNISTD_H
#   include <unistd.h>
45
46
#endif

47
#ifdef HAVE_LIMITS_H
48
#   include <limits.h>
49
50
#endif

51
52
#ifdef DARWIN_DVD_IOCTL
#   include <paths.h>
53
#   include <CoreFoundation/CoreFoundation.h>
54
55
56
57
58
59
60
#   include <IOKit/IOKitLib.h>
#   include <IOKit/IOBSD.h>
#   include <IOKit/storage/IOMedia.h>
#   include <IOKit/storage/IOCDMedia.h>
#   include <IOKit/storage/IODVDMedia.h>
#endif

61
#ifdef __OS2__
KO Myung-Hun's avatar
KO Myung-Hun committed
62
63
64
#   define INCL_DOS
#   define INCL_DOSDEVIOCTL
#   include <os2.h>
65
66
#   include <io.h>                                              /* setmode() */
#   include <fcntl.h>                                           /* O_BINARY  */
KO Myung-Hun's avatar
KO Myung-Hun committed
67
68
#endif

69
70
71
72
73
74
75
76
#include "dvdcss/dvdcss.h"

#include "common.h"
#include "css.h"
#include "libdvdcss.h"
#include "ioctl.h"
#include "device.h"

77
/*****************************************************************************
78
 * Device reading prototypes
79
 *****************************************************************************/
80
static int libc_open  ( dvdcss_t, const char * );
81
82
83
84
static int libc_seek  ( dvdcss_t, int );
static int libc_read  ( dvdcss_t, void *, int );
static int libc_readv ( dvdcss_t, struct iovec *, int );

85
#ifdef WIN32
86
87
88
89
static int win2k_open  ( dvdcss_t, const char * );
static int win2k_seek  ( dvdcss_t, int );
static int win2k_read  ( dvdcss_t, void *, int );
static int win2k_readv ( dvdcss_t, struct iovec *, int );
90

91
#elif defined( __OS2__ )
92
static int os2_open ( dvdcss_t, const char * );
KO Myung-Hun's avatar
KO Myung-Hun committed
93
94
95
96
/* just use macros for libc */
#   define os2_seek     libc_seek
#   define os2_read     libc_read
#   define os2_readv    libc_readv
97
98
#endif

99
int dvdcss_use_ioctls( dvdcss_t dvdcss )
100
{
101
102
#if defined( WIN32 )
    if( dvdcss->b_file )
103
    {
104
        return 0;
105
106
    }

107
    /* FIXME: implement this for Windows */
108
    return 1;
109
#elif defined( __OS2__ )
KO Myung-Hun's avatar
KO Myung-Hun committed
110
111
112
113
114
115
116
117
118
    ULONG ulMode;

    if( DosQueryFHState( dvdcss->i_fd, &ulMode ) != 0 )
        return 1;  /* What to do?  Be conservative and try to use the ioctls */

    if( ulMode & OPEN_FLAGS_DASD )
        return 1;

    return 0;
119
120
121
122
123
124
125
#else
    struct stat fileinfo;
    int ret;

    ret = fstat( dvdcss->i_fd, &fileinfo );
    if( ret < 0 )
    {
Sam Hocevar's avatar
Sam Hocevar committed
126
        return 1;  /* What to do?  Be conservative and try to use the ioctls */
127
    }
128
129

    /* Complete this list and check that we test for the right things
130
131
132
133
134
     * (I've assumed for all OSs that 'r', (raw) device, are char devices
     *  and those that don't contain/use an 'r' in the name are block devices)
     *
     * Linux    needs a block device
     * Solaris  needs a char device
135
     * Darwin   needs a char device
136
137
138
139
     * OpenBSD  needs a char device
     * NetBSD   needs a char device
     * FreeBSD  can use either the block or the char device
     */
140

141
    /* Check if this is a block/char device */
142
    if( S_ISBLK( fileinfo.st_mode ) ||
Sam Hocevar's avatar
Sam Hocevar committed
143
        S_ISCHR( fileinfo.st_mode ) )
144
    {
Sam Hocevar's avatar
Sam Hocevar committed
145
        return 1;
146
147
148
    }
    else
    {
Sam Hocevar's avatar
Sam Hocevar committed
149
        return 0;
150
151
152
153
    }
#endif
}

154
void dvdcss_check_device ( dvdcss_t dvdcss )
155
156
157
158
{
#if defined( WIN32 )
    DWORD drives;
    int i;
159
160
161
162
163
164
#elif defined( DARWIN_DVD_IOCTL )
    io_object_t next_media;
    mach_port_t master_port;
    kern_return_t kern_result;
    io_iterator_t media_iterator;
    CFMutableDictionaryRef classes_to_match;
165
#elif defined( __OS2__ )
KO Myung-Hun's avatar
KO Myung-Hun committed
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
#pragma pack( 1 )
    struct
    {
        BYTE bCmdInfo;
        BYTE bDrive;
    } param;

    struct
    {
        BYTE    abEBPB[31];
        USHORT  usCylinders;
        BYTE    bDevType;
        USHORT  usDevAttr;
    } data;
#pragma pack()

    ULONG ulParamLen;
    ULONG ulDataLen;
    ULONG rc;

    int i;
187
#else
188
    const char *ppsz_devices[] = { "/dev/dvd", "/dev/cdrom", "/dev/hdc", NULL };
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
    int i, i_fd;
#endif

    /* If the device name is non-null, return */
    if( dvdcss->psz_device[0] )
    {
        return;
    }

#if defined( WIN32 )
    drives = GetLogicalDrives();

    for( i = 0; drives; i++ )
    {
        char psz_device[5];
        DWORD cur = 1 << i;
        UINT i_ret;

        if( (drives & cur) == 0 )
        {
            continue;
        }
        drives &= ~cur;

        sprintf( psz_device, "%c:\\", 'A' + i );
        i_ret = GetDriveType( psz_device );
        if( i_ret != DRIVE_CDROM )
        {
            continue;
        }

220
221
222
        /* Remove trailing backslash */
        psz_device[2] = '\0';

223
        /* FIXME: we want to differentiate between CD and DVD drives
224
225
226
227
228
229
         * using DeviceIoControl() */
        print_debug( dvdcss, "defaulting to drive `%s'", psz_device );
        free( dvdcss->psz_device );
        dvdcss->psz_device = strdup( psz_device );
        return;
    }
230
231
232
233
234
235
236
237
#elif defined( DARWIN_DVD_IOCTL )

    kern_result = IOMasterPort( MACH_PORT_NULL, &master_port );
    if( kern_result != KERN_SUCCESS )
    {
        return;
    }

238
    classes_to_match = IOServiceMatching( kIODVDMediaClass );
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
    if( classes_to_match == NULL )
    {
        return;
    }

    CFDictionarySetValue( classes_to_match, CFSTR( kIOMediaEjectableKey ),
                          kCFBooleanTrue );

    kern_result = IOServiceGetMatchingServices( master_port, classes_to_match,
                                                &media_iterator );
    if( kern_result != KERN_SUCCESS )
    {
        return;
    }

    next_media = IOIteratorNext( media_iterator );
255
    for( ; ; )
256
257
258
259
260
    {
        char psz_buf[0x32];
        size_t i_pathlen;
        CFTypeRef psz_path;

261
262
        next_media = IOIteratorNext( media_iterator );
        if( next_media == 0 )
263
        {
264
265
            break;
        }
266

267
268
269
270
271
272
273
274
275
        psz_path = IORegistryEntryCreateCFProperty( next_media,
                                                    CFSTR( kIOBSDNameKey ),
                                                    kCFAllocatorDefault,
                                                    0 );
        if( psz_path == NULL )
        {
            IOObjectRelease( next_media );
            continue;
        }
276

277
278
279
280
281
282
283
284
285
286
        snprintf( psz_buf, sizeof(psz_buf), "%s%c", _PATH_DEV, 'r' );
        i_pathlen = strlen( psz_buf );

        if( CFStringGetCString( psz_path,
                                (char*)&psz_buf + i_pathlen,
                                sizeof(psz_buf) - i_pathlen,
                                kCFStringEncodingASCII ) )
        {
            print_debug( dvdcss, "defaulting to drive `%s'", psz_buf );
            CFRelease( psz_path );
287
            IOObjectRelease( next_media );
288
289
290
291
292
293
294
            IOObjectRelease( media_iterator );
            free( dvdcss->psz_device );
            dvdcss->psz_device = strdup( psz_buf );
            return;
        }

        CFRelease( psz_path );
295

296
        IOObjectRelease( next_media );
297
298
299
    }

    IOObjectRelease( media_iterator );
300
#elif defined( __OS2__ )
KO Myung-Hun's avatar
KO Myung-Hun committed
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
    for( i = 0; i < 26; i++ )
    {
        param.bCmdInfo = 0;
        param.bDrive = i;

        rc = DosDevIOCtl( ( HFILE )-1, IOCTL_DISK, DSK_GETDEVICEPARAMS,
                          &param, sizeof( param ), &ulParamLen,
                          &data, sizeof( data ), &ulDataLen );

        if( rc == 0 )
        {
            /* Check for removable and for cylinders */
            if( ( data.usDevAttr & 1 ) == 0 && data.usCylinders == 0xFFFF )
            {
                char psz_dvd[] = "A:";

                psz_dvd[0] += i;

                print_debug( dvdcss, "defaulting to drive `%s'", psz_dvd );
                free( dvdcss->psz_device );
                dvdcss->psz_device = strdup( psz_dvd );
                return;
            }
        }
    }
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
#else
    for( i = 0; ppsz_devices[i]; i++ )
    {
        i_fd = open( ppsz_devices[i], 0 );
        if( i_fd != -1 )
        {
            print_debug( dvdcss, "defaulting to drive `%s'", ppsz_devices[i] );
            close( i_fd );
            free( dvdcss->psz_device );
            dvdcss->psz_device = strdup( ppsz_devices[i] );
            return;
        }
    }
#endif

    print_error( dvdcss, "could not find a suitable default drive" );
}

344
int dvdcss_open_device ( dvdcss_t dvdcss )
345
{
346
    const char *psz_device = dvdcss->psz_device;
347

348
    print_debug( dvdcss, "opening target `%s'", psz_device );
349

350
#if defined( WIN32 )
351
352
353
354
355
    dvdcss->b_file = 1;
    /* If device is "X:" or "X:\", we are not actually opening a file. */
    if (psz_device[0] && psz_device[1] == ':' &&
       (!psz_device[2] || (psz_device[2] == '\\' && !psz_device[3])))
        dvdcss->b_file = 0;
356

357
    /* Initialize readv temporary buffer */
358
359
360
    dvdcss->p_readv_buffer   = NULL;
    dvdcss->i_readv_buf_size = 0;

361
    if( !dvdcss->b_file )
362
    {
363
        print_debug( dvdcss, "using Win2K API for access" );
364
365
        dvdcss->pf_seek  = win2k_seek;
        dvdcss->pf_read  = win2k_read;
366
        dvdcss->pf_readv = win2k_readv;
367
        return win2k_open( dvdcss, psz_device );
368
    }
369
    else
370
#elif defined( __OS2__ )
KO Myung-Hun's avatar
KO Myung-Hun committed
371
372
373
374
    /* If device is "X:" or "X:\", we are not actually opening a file. */
    if( psz_device[0] && psz_device[1] == ':' &&
        ( !psz_device[2] || ( psz_device[2] == '\\' && !psz_device[3] ) ) )
    {
375
        print_debug( dvdcss, "using OS/2 API for access" );
KO Myung-Hun's avatar
KO Myung-Hun committed
376
377
378
379
380
381
        dvdcss->pf_seek  = os2_seek;
        dvdcss->pf_read  = os2_read;
        dvdcss->pf_readv = os2_readv;
        return os2_open( dvdcss, psz_device );
    }
    else
382
#endif
383
    {
384
        print_debug( dvdcss, "using libc for access" );
385
386
387
388
389
        dvdcss->pf_seek  = libc_seek;
        dvdcss->pf_read  = libc_read;
        dvdcss->pf_readv = libc_readv;
        return libc_open( dvdcss, psz_device );
    }
390
391
}

392
#ifdef DVDCSS_RAW_OPEN
393
int dvdcss_raw_open ( dvdcss_t dvdcss, const char *psz_device )
394
395
396
397
398
{
    dvdcss->i_raw_fd = open( psz_device, 0 );

    if( dvdcss->i_raw_fd == -1 )
    {
399
400
401
        print_debug( dvdcss, "cannot open %s (%s)",
                             psz_device, strerror(errno) );
        print_error( dvdcss, "failed to open raw device, but continuing" );
402
403
404
405
406
407
408
409
410
        return -1;
    }
    else
    {
        dvdcss->i_read_fd = dvdcss->i_raw_fd;
    }

    return 0;
}
411
#endif /* DVDCSS_RAW_OPEN */
412

413
int dvdcss_close_device ( dvdcss_t dvdcss )
414
415
{
#if defined( WIN32 )
416
417
418
419
    if( dvdcss->b_file )
    {
        close( dvdcss->i_fd );
    }
420
    else
421
422
423
424
425
    {
        CloseHandle( (HANDLE) dvdcss->i_fd );
    }

    /* Free readv temporary buffer */
426
427
428
    free( dvdcss->p_readv_buffer );
    dvdcss->p_readv_buffer   = NULL;
    dvdcss->i_readv_buf_size = 0;
429

430
    return 0;
431
432
433
#else
    close( dvdcss->i_fd );

434
#ifdef DVDCSS_RAW_OPEN
435
436
437
438
439
    if( dvdcss->i_raw_fd >= 0 )
    {
        close( dvdcss->i_raw_fd );
        dvdcss->i_raw_fd = -1;
    }
KO Myung-Hun's avatar
KO Myung-Hun committed
440
#endif
441
442
443
444
445

    return 0;
#endif
}

446
/* Following functions are local */
447

448
449
450
/*****************************************************************************
 * Open commands.
 *****************************************************************************/
451
static int libc_open ( dvdcss_t dvdcss, const char *psz_device )
452
{
453
    dvdcss->i_fd = dvdcss->i_read_fd = open( psz_device, O_BINARY );
454

455
    if( dvdcss->i_fd == -1 )
456
    {
457
458
459
        print_debug( dvdcss, "cannot open %s (%s)",
                             psz_device, strerror(errno) );
        print_error( dvdcss, "failed to open device" );
460
        return -1;
461
462
    }

463
464
    dvdcss->i_pos = 0;

465
    return 0;
466
467
468
}

#if defined( WIN32 )
469
static int win2k_open ( dvdcss_t dvdcss, const char *psz_device )
470
{
471
472
    char psz_dvd[7] = "\\\\.\\\0:";
    psz_dvd[4] = psz_device[0];
473
474
475
476

    /* To work around an M$ bug in IOCTL_DVD_READ_STRUCTURE, we need read
     * _and_ write access to the device (so we can make SCSI Pass Through
     * Requests). Unfortunately this is only allowed if you have
477
     * administrator privileges so we allow for a fallback method with
478
479
480
481
     * only read access to the device (in this case ioctl_ReadCopyright()
     * won't send back the right result).
     * (See Microsoft Q241374: Read and Write Access Required for SCSI
     * Pass Through Requests) */
482
    dvdcss->i_fd = (int)
483
484
485
486
                CreateFile( psz_dvd, GENERIC_READ | GENERIC_WRITE,
                            FILE_SHARE_READ | FILE_SHARE_WRITE,
                            NULL, OPEN_EXISTING,
                            FILE_FLAG_RANDOM_ACCESS, NULL );
487

488
    if( (HANDLE) dvdcss->i_fd == INVALID_HANDLE_VALUE )
489
        dvdcss->i_fd = (int)
490
491
492
                    CreateFile( psz_dvd, GENERIC_READ, FILE_SHARE_READ,
                                NULL, OPEN_EXISTING,
                                FILE_FLAG_RANDOM_ACCESS, NULL );
493

494
    if( (HANDLE) dvdcss->i_fd == INVALID_HANDLE_VALUE )
495
    {
496
497
        print_error( dvdcss, "failed opening device" );
        return -1;
498
499
    }

500
501
    dvdcss->i_pos = 0;

502
    return 0;
503
}
504
#endif /* defined( WIN32 ) */
505

506
#ifdef __OS2__
507
static int os2_open ( dvdcss_t dvdcss, const char *psz_device )
KO Myung-Hun's avatar
KO Myung-Hun committed
508
509
510
511
512
513
514
515
{
    char  psz_dvd[] = "X:";
    HFILE hfile;
    ULONG ulAction;
    ULONG rc;

    psz_dvd[0] = psz_device[0];

516
517
518
519
    rc = DosOpenL( ( PSZ )psz_dvd, &hfile, &ulAction, 0, FILE_NORMAL,
                   OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_FAIL_IF_NEW,
                   OPEN_ACCESS_READONLY | OPEN_SHARE_DENYNONE | OPEN_FLAGS_DASD,
                   NULL );
KO Myung-Hun's avatar
KO Myung-Hun committed
520
521
522
523
524
525
526

    if( rc )
    {
        print_error( dvdcss, "failed to open device" );
        return -1;
    }

527
528
    setmode( hfile, O_BINARY );

KO Myung-Hun's avatar
KO Myung-Hun committed
529
530
531
532
533
534
    dvdcss->i_fd = dvdcss->i_read_fd = hfile;

    dvdcss->i_pos = 0;

    return 0;
}
535
#endif /* __OS2__ */
KO Myung-Hun's avatar
KO Myung-Hun committed
536

537
/*****************************************************************************
538
 * Seek commands.
539
 *****************************************************************************/
540
static int libc_seek( dvdcss_t dvdcss, int i_blocks )
541
{
542
    off_t   i_seek;
543

544
545
546
547
548
549
    if( dvdcss->i_pos == i_blocks )
    {
        /* We are already in position */
        return i_blocks;
    }

Sam Hocevar's avatar
Sam Hocevar committed
550
    i_seek = (off_t)i_blocks * (off_t)DVDCSS_BLOCK_SIZE;
551
    i_seek = lseek( dvdcss->i_read_fd, i_seek, SEEK_SET );
552

553
    if( i_seek < 0 )
554
    {
555
        print_error( dvdcss, "seek error" );
556
557
        dvdcss->i_pos = -1;
        return i_seek;
558
559
    }

560
561
562
    dvdcss->i_pos = i_seek / DVDCSS_BLOCK_SIZE;

    return dvdcss->i_pos;
563
564
}

565
566
567
#if defined( WIN32 )
static int win2k_seek( dvdcss_t dvdcss, int i_blocks )
{
568
    LARGE_INTEGER li_seek;
569

570
571
572
573
574
575
    if( dvdcss->i_pos == i_blocks )
    {
        /* We are already in position */
        return i_blocks;
    }

576
    li_seek.QuadPart = (LONGLONG)i_blocks * DVDCSS_BLOCK_SIZE;
577

578
579
580
581
    li_seek.LowPart = SetFilePointer( (HANDLE) dvdcss->i_fd,
                                      li_seek.LowPart,
                                      &li_seek.HighPart, FILE_BEGIN );
    if( (li_seek.LowPart == INVALID_SET_FILE_POINTER)
582
583
        && GetLastError() != NO_ERROR)
    {
584
585
        dvdcss->i_pos = -1;
        return -1;
586
587
    }

588
589
590
    dvdcss->i_pos = li_seek.QuadPart / DVDCSS_BLOCK_SIZE;

    return dvdcss->i_pos;
591
}
592
#endif /* defined( WIN32 ) */
593
594

/*****************************************************************************
595
 * Read commands.
596
 *****************************************************************************/
597
598
static int libc_read ( dvdcss_t dvdcss, void *p_buffer, int i_blocks )
{
Sam Hocevar's avatar
Sam Hocevar committed
599
    off_t i_size, i_ret;
600

Sam Hocevar's avatar
Sam Hocevar committed
601
602
    i_size = (off_t)i_blocks * (off_t)DVDCSS_BLOCK_SIZE;
    i_ret = read( dvdcss->i_read_fd, p_buffer, i_size );
603

604
605
    if( i_ret < 0 )
    {
606
        print_error( dvdcss, "read error" );
607
        dvdcss->i_pos = -1;
608
609
610
        return i_ret;
    }

611
    /* Handle partial reads */
Sam Hocevar's avatar
Sam Hocevar committed
612
    if( i_ret != i_size )
613
    {
614
        int i_seek;
615

616
617
618
619
620
621
        dvdcss->i_pos = -1;
        i_seek = libc_seek( dvdcss, i_ret / DVDCSS_BLOCK_SIZE );
        if( i_seek < 0 )
        {
            return i_seek;
        }
622

623
624
625
626
627
628
        /* We have to return now so that i_pos isn't clobbered */
        return i_ret / DVDCSS_BLOCK_SIZE;
    }

    dvdcss->i_pos += i_ret / DVDCSS_BLOCK_SIZE;
    return i_ret / DVDCSS_BLOCK_SIZE;
629
630
631
632
633
}

#if defined( WIN32 )
static int win2k_read ( dvdcss_t dvdcss, void *p_buffer, int i_blocks )
{
634
    DWORD i_bytes;
635
636
637

    if( !ReadFile( (HANDLE) dvdcss->i_fd, p_buffer,
              i_blocks * DVDCSS_BLOCK_SIZE,
638
              &i_bytes, NULL ) )
639
    {
640
        dvdcss->i_pos = -1;
641
642
643
        return -1;
    }

644
    dvdcss->i_pos += i_bytes / DVDCSS_BLOCK_SIZE;
645
646
    return i_bytes / DVDCSS_BLOCK_SIZE;
}
647
#endif /* defined( WIN32 ) */
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675

/*****************************************************************************
 * Readv commands.
 *****************************************************************************/
static int libc_readv ( dvdcss_t dvdcss, struct iovec *p_iovec, int i_blocks )
{
#if defined( WIN32 )
    int i_index, i_len, i_total = 0;
    unsigned char *p_base;
    int i_bytes;

    for( i_index = i_blocks;
         i_index;
         i_index--, p_iovec++ )
    {
        i_len  = p_iovec->iov_len;
        p_base = p_iovec->iov_base;

        if( i_len <= 0 )
        {
            continue;
        }

        i_bytes = read( dvdcss->i_fd, p_base, i_len );

        if( i_bytes < 0 )
        {
            /* One of the reads failed, too bad.
676
677
             * We won't even bother returning the reads that went OK,
             * and as in the POSIX spec the file position is left
678
             * unspecified after a failure */
679
            dvdcss->i_pos = -1;
680
681
682
683
684
685
686
687
688
            return -1;
        }

        i_total += i_bytes;

        if( i_bytes != i_len )
        {
            /* We reached the end of the file or a signal interrupted
             * the read. Return a partial read. */
689
690
691
692
693
694
695
696
697
698
            int i_seek;

            dvdcss->i_pos = -1;
            i_seek = libc_seek( dvdcss, i_total / DVDCSS_BLOCK_SIZE );
            if( i_seek < 0 )
            {
                return i_seek;
            }

            /* We have to return now so that i_pos isn't clobbered */
699
700
701
702
            return i_total / DVDCSS_BLOCK_SIZE;
        }
    }

703
    dvdcss->i_pos += i_total / DVDCSS_BLOCK_SIZE;
704
705
706
707
    return i_total / DVDCSS_BLOCK_SIZE;
#else
    int i_read = readv( dvdcss->i_read_fd, p_iovec, i_blocks );

708
709
    if( i_read < 0 )
    {
710
        dvdcss->i_pos = -1;
711
712
713
714
        return i_read;
    }

    dvdcss->i_pos += i_read / DVDCSS_BLOCK_SIZE;
715
716
717
718
719
720
    return i_read / DVDCSS_BLOCK_SIZE;
#endif
}

#if defined( WIN32 )
/*****************************************************************************
721
 * win2k_readv: vectored read using ReadFile for Win2K
722
 *****************************************************************************/
723
static int win2k_readv ( dvdcss_t dvdcss, struct iovec *p_iovec, int i_blocks )
724
725
726
{
    int i_index;
    int i_blocks_read, i_blocks_total = 0;
727
    DWORD i_bytes;
728
729
730
731
732
733
734

    /* Check the size of the readv temp buffer, just in case we need to
     * realloc something bigger */
    if( dvdcss->i_readv_buf_size < i_blocks * DVDCSS_BLOCK_SIZE )
    {
        dvdcss->i_readv_buf_size = i_blocks * DVDCSS_BLOCK_SIZE;

735
        free( dvdcss->p_readv_buffer );
736
737
738
739
740
741

        /* Allocate a buffer which will be used as a temporary storage
         * for readv */
        dvdcss->p_readv_buffer = malloc( dvdcss->i_readv_buf_size );
        if( !dvdcss->p_readv_buffer )
        {
742
            print_error( dvdcss, " failed (readv)" );
743
            dvdcss->i_pos = -1;
744
745
746
747
748
749
            return -1;
        }
    }

    for( i_index = i_blocks; i_index; i_index-- )
    {
750
        i_blocks_total += p_iovec[i_index-1].iov_len;
751
752
753
754
755
756
    }

    if( i_blocks_total <= 0 ) return 0;

    i_blocks_total /= DVDCSS_BLOCK_SIZE;

757
758
    if( !ReadFile( (HANDLE)dvdcss->i_fd, dvdcss->p_readv_buffer,
                   i_blocks_total * DVDCSS_BLOCK_SIZE, &i_bytes, NULL ) )
759
    {
760
761
762
763
764
        /* The read failed... too bad.
         * As in the POSIX spec the file position is left
         * unspecified after a failure */
        dvdcss->i_pos = -1;
        return -1;
765
    }
766
    i_blocks_read = i_bytes / DVDCSS_BLOCK_SIZE;
767
768
769
770
771
772
773
774
775
776
777
778
779
780

    /* We just have to copy the content of the temp buffer into the iovecs */
    for( i_index = 0, i_blocks_total = i_blocks_read;
         i_blocks_total > 0;
         i_index++ )
    {
        memcpy( p_iovec[i_index].iov_base,
                dvdcss->p_readv_buffer + (i_blocks_read - i_blocks_total)
                                           * DVDCSS_BLOCK_SIZE,
                p_iovec[i_index].iov_len );
        /* if we read less blocks than asked, we'll just end up copying
         * garbage, this isn't an issue as we return the number of
         * blocks actually read */
        i_blocks_total -= ( p_iovec[i_index].iov_len / DVDCSS_BLOCK_SIZE );
781
    }
782

783
    dvdcss->i_pos += i_blocks_read;
784
785
    return i_blocks_read;
}
786
#endif /* defined( WIN32 ) */