device.c 23.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
 *****************************************************************************/

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

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

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

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

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

66 67 68 69 70 71 72 73
#include "dvdcss/dvdcss.h"

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

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

82 83 84 85
static int stream_seek  ( dvdcss_t, int );
static int stream_read  ( dvdcss_t, void *, int );
static int stream_readv ( dvdcss_t, const struct iovec *, int );

86
#ifdef _WIN32
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 );
90
static int win2k_readv ( dvdcss_t, const struct iovec *, int );
91

92
#elif defined( __OS2__ )
93
static int os2_open ( dvdcss_t, const char * );
94 95
#endif

96
int dvdcss_use_ioctls( dvdcss_t dvdcss )
97
{
98 99 100
    if( dvdcss->p_stream )
        return 0;

101
#if defined( _WIN32 )
102
    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 )
    {
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 ) ||
143
        S_ISCHR( fileinfo.st_mode ) )
144
    {
145
        return 1;
146 147 148
    }
    else
    {
149
        return 0;
150 151 152 153
    }
#endif
}

154
void dvdcss_check_device ( dvdcss_t dvdcss )
155
{
156
#if defined( _WIN32 )
157 158
    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
    int i, i_fd;
#endif

192
    /* If the device name is non-NULL or stream is set, return. */
193
    if( (dvdcss->psz_device && dvdcss->psz_device[0]) || dvdcss->p_stream )
194 195 196 197
    {
        return;
    }

198
#if defined( _WIN32 )
199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219
    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 347 348 349 350
    const char *psz_device = getenv( "DVDCSS_RAW_DEVICE" );
    if( !psz_device )
    {
         psz_device = dvdcss->psz_device;
    }
351
    print_debug( dvdcss, "opening target `%s'", psz_device );
352

353 354 355 356 357 358 359 360 361 362
    /* if callback functions are initialized */
    if( dvdcss->p_stream )
    {
        print_debug( dvdcss, "using stream API for access" );
        dvdcss->pf_seek  = stream_seek;
        dvdcss->pf_read  = stream_read;
        dvdcss->pf_readv = stream_readv;
        return 0;
    }

363
#if defined( _WIN32 )
364 365 366 367 368 369 370
    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;

    /* Initialize readv temporary buffer */
371 372 373
    dvdcss->p_readv_buffer   = NULL;
    dvdcss->i_readv_buf_size = 0;

374
    if( !dvdcss->b_file )
375
    {
376
        print_debug( dvdcss, "using Win2K API for access" );
377 378
        dvdcss->pf_seek  = win2k_seek;
        dvdcss->pf_read  = win2k_read;
379
        dvdcss->pf_readv = win2k_readv;
380
        return win2k_open( dvdcss, psz_device );
381 382
    }
    else
383
#elif defined( __OS2__ )
384 385 386 387
    /* 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] ) ) )
    {
388
        print_debug( dvdcss, "using OS/2 API for access" );
389 390 391
        dvdcss->pf_seek  = libc_seek;
        dvdcss->pf_read  = libc_read;
        dvdcss->pf_readv = libc_readv;
KO Myung-Hun's avatar
KO Myung-Hun committed
392 393 394
        return os2_open( dvdcss, psz_device );
    }
    else
395
#endif
396
    {
397
        print_debug( dvdcss, "using libc API for access" );
398 399 400 401 402
        dvdcss->pf_seek  = libc_seek;
        dvdcss->pf_read  = libc_read;
        dvdcss->pf_readv = libc_readv;
        return libc_open( dvdcss, psz_device );
    }
403 404
}

405
int dvdcss_close_device ( dvdcss_t dvdcss )
406
{
407
#if defined( _WIN32 )
408
    /* Free readv temporary buffer */
409 410 411
    free( dvdcss->p_readv_buffer );
    dvdcss->p_readv_buffer   = NULL;
    dvdcss->i_readv_buf_size = 0;
412

413
    if( !dvdcss->b_file )
414
    {
415
        CloseHandle( (HANDLE) dvdcss->i_fd );
416
    }
417
    else
418
#endif
419 420 421 422 423 424 425 426
    {
        int i_ret = close( dvdcss->i_fd );
        if( i_ret < 0 )
        {
            print_error( dvdcss, "Failed to close fd, data loss possible." );
            return i_ret;
        }
    }
427 428 429 430

    return 0;
}

431
/* Following functions are local */
432

433 434 435
/*****************************************************************************
 * Open commands.
 *****************************************************************************/
436
static int libc_open ( dvdcss_t dvdcss, const char *psz_device )
437
{
438
    dvdcss->i_fd = open( psz_device, O_BINARY );
439

440
    if( dvdcss->i_fd == -1 )
441
    {
442 443
        print_error( dvdcss, "failed to open device %s (%s)",
                     psz_device, strerror(errno) );
444
        return -1;
445 446
    }

447
    return 0;
448 449
}

450
#if defined( _WIN32 )
451
static int win2k_open ( dvdcss_t dvdcss, const char *psz_device )
452
{
453 454
    char psz_dvd[7] = "\\\\.\\\0:";
    psz_dvd[4] = psz_device[0];
455 456 457 458

    /* 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
459
     * administrator privileges so we allow for a fallback method with
460 461 462 463
     * 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) */
464 465 466 467 468
    dvdcss->i_fd = (int)
                CreateFile( psz_dvd, GENERIC_READ | GENERIC_WRITE,
                            FILE_SHARE_READ | FILE_SHARE_WRITE,
                            NULL, OPEN_EXISTING,
                            FILE_FLAG_RANDOM_ACCESS, NULL );
469

470 471 472 473 474
    if( (HANDLE) dvdcss->i_fd == INVALID_HANDLE_VALUE )
        dvdcss->i_fd = (int)
                    CreateFile( psz_dvd, GENERIC_READ, FILE_SHARE_READ,
                                NULL, OPEN_EXISTING,
                                FILE_FLAG_RANDOM_ACCESS, NULL );
475

476
    if( (HANDLE) dvdcss->i_fd == INVALID_HANDLE_VALUE )
477
    {
478
        print_error( dvdcss, "failed to open device %s", psz_device );
479
        return -1;
480 481
    }

482 483
    dvdcss->i_pos = 0;

484
    return 0;
485
}
486
#endif /* defined( _WIN32 ) */
487

488
#ifdef __OS2__
489
static int os2_open ( dvdcss_t dvdcss, const char *psz_device )
KO Myung-Hun's avatar
KO Myung-Hun committed
490 491 492 493 494 495 496 497
{
    char  psz_dvd[] = "X:";
    HFILE hfile;
    ULONG ulAction;
    ULONG rc;

    psz_dvd[0] = psz_device[0];

498 499 500 501
    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
502 503 504

    if( rc )
    {
505
        print_error( dvdcss, "failed to open device %s", psz_device );
KO Myung-Hun's avatar
KO Myung-Hun committed
506 507 508
        return -1;
    }

509 510
    setmode( hfile, O_BINARY );

511
    dvdcss->i_fd = hfile;
KO Myung-Hun's avatar
KO Myung-Hun committed
512 513 514 515 516

    dvdcss->i_pos = 0;

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

519
/*****************************************************************************
520
 * Seek commands.
521
 *****************************************************************************/
522
static int libc_seek( dvdcss_t dvdcss, int i_blocks )
523
{
524
    off_t   i_seek;
525

526 527 528 529 530 531
    if( dvdcss->i_pos == i_blocks )
    {
        /* We are already in position */
        return i_blocks;
    }

532
    i_seek = (off_t)i_blocks * (off_t)DVDCSS_BLOCK_SIZE;
533
    i_seek = lseek( dvdcss->i_fd, i_seek, SEEK_SET );
534

535
    if( i_seek < 0 )
536
    {
537
        print_error( dvdcss, "seek error" );
538
        dvdcss->i_pos = -1;
539
        return i_seek;
540 541
    }

542
    dvdcss->i_pos = i_seek / DVDCSS_BLOCK_SIZE;
543 544

    return dvdcss->i_pos;
545 546
}

547 548
static int stream_seek( dvdcss_t dvdcss, int i_blocks )
{
549
    off_t i_seek = (off_t) i_blocks * (off_t) DVDCSS_BLOCK_SIZE;
550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571

    if( !dvdcss->p_stream_cb->pf_seek )
        return -1;

    if( dvdcss->i_pos == i_blocks )
    {
        /* We are already in position */
        return i_blocks;
    }

    if( dvdcss->p_stream_cb->pf_seek( dvdcss->p_stream, i_seek ) != 0 )
    {
        print_error( dvdcss, "seek error" );
        dvdcss->i_pos = -1;
        return -1;
    }

    dvdcss->i_pos = i_blocks;

    return dvdcss->i_pos;
}

572
#if defined( _WIN32 )
573 574
static int win2k_seek( dvdcss_t dvdcss, int i_blocks )
{
575
    LARGE_INTEGER li_seek;
576

577 578 579 580 581 582
    if( dvdcss->i_pos == i_blocks )
    {
        /* We are already in position */
        return i_blocks;
    }

583
    li_seek.QuadPart = (LONGLONG)i_blocks * DVDCSS_BLOCK_SIZE;
584

585 586
    li_seek.LowPart = SetFilePointer( (HANDLE) dvdcss->i_fd,
                                      li_seek.LowPart,
587 588
                                      &li_seek.HighPart, FILE_BEGIN );
    if( (li_seek.LowPart == INVALID_SET_FILE_POINTER)
589 590
        && GetLastError() != NO_ERROR)
    {
591 592
        dvdcss->i_pos = -1;
        return -1;
593 594
    }

595
    dvdcss->i_pos = li_seek.QuadPart / DVDCSS_BLOCK_SIZE;
596 597

    return dvdcss->i_pos;
598
}
599
#endif /* defined( _WIN32 ) */
600 601

/*****************************************************************************
602
 * Read commands.
603
 *****************************************************************************/
604 605
static int libc_read ( dvdcss_t dvdcss, void *p_buffer, int i_blocks )
{
606
    off_t i_size, i_ret, i_ret_blocks;
607

608
    i_size = (off_t)i_blocks * (off_t)DVDCSS_BLOCK_SIZE;
609
    i_ret = read( dvdcss->i_fd, p_buffer, i_size );
610

611 612
    if( i_ret < 0 )
    {
613
        print_error( dvdcss, "read error" );
614
        dvdcss->i_pos = -1;
615 616 617
        return i_ret;
    }

618 619
    i_ret_blocks = i_ret / DVDCSS_BLOCK_SIZE;

620
    /* Handle partial reads */
Sam Hocevar's avatar
Sam Hocevar committed
621
    if( i_ret != i_size )
622
    {
623
        int i_seek, i_set_pos;
624

625
        i_set_pos = dvdcss->i_pos + i_ret_blocks;
626
        dvdcss->i_pos = -1;
627
        i_seek = libc_seek( dvdcss, i_set_pos );
628 629 630 631
        if( i_seek < 0 )
        {
            return i_seek;
        }
632

633
        /* We have to return now so that i_pos isn't clobbered */
634
        return i_ret_blocks;
635 636
    }

637 638
    dvdcss->i_pos += i_ret_blocks;
    return i_ret_blocks;
639 640
}

641 642 643 644
static int stream_read ( dvdcss_t dvdcss, void *p_buffer, int i_blocks )
{
    off_t i_size, i_ret, i_ret_blocks;

645
    i_size = (off_t)i_blocks * (off_t)DVDCSS_BLOCK_SIZE;
646 647 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 676 677 678 679 680

    if( !dvdcss->p_stream_cb->pf_read )
        return -1;

    i_ret = dvdcss->p_stream_cb->pf_read( dvdcss->p_stream, p_buffer, i_size );

    if( i_ret < 0 )
    {
        print_error( dvdcss, "read error" );
        dvdcss->i_pos = -1;
        return i_ret;
    }

    i_ret_blocks = i_ret / DVDCSS_BLOCK_SIZE;

    /* Handle partial reads */
    if( i_ret != i_size )
    {
        int i_seek;

        dvdcss->i_pos = -1;
        i_seek = stream_seek( dvdcss, i_ret_blocks );
        if( i_seek < 0 )
        {
            return i_seek;
        }

        /* We have to return now so that i_pos isn't clobbered */
        return i_ret_blocks;
    }

    dvdcss->i_pos += i_ret_blocks;
    return i_ret_blocks;
}

681
#if defined( _WIN32 )
682 683
static int win2k_read ( dvdcss_t dvdcss, void *p_buffer, int i_blocks )
{
684
    DWORD i_bytes;
685

686 687 688
    if( !ReadFile( (HANDLE) dvdcss->i_fd, p_buffer,
              i_blocks * DVDCSS_BLOCK_SIZE,
              &i_bytes, NULL ) )
689
    {
690
        dvdcss->i_pos = -1;
691 692 693
        return -1;
    }

694 695 696
    i_bytes /= DVDCSS_BLOCK_SIZE;
    dvdcss->i_pos += i_bytes;
    return i_bytes;
697
}
698
#endif /* defined( _WIN32 ) */
699 700 701 702

/*****************************************************************************
 * Readv commands.
 *****************************************************************************/
703 704
static int libc_readv ( dvdcss_t dvdcss, const struct iovec *p_iovec,
                        int i_blocks )
705
{
706
#if defined( _WIN32 )
707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727
    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.
728 729
             * We won't even bother returning the reads that went OK,
             * and as in the POSIX spec the file position is left
730
             * unspecified after a failure */
731
            dvdcss->i_pos = -1;
732 733 734 735
            return -1;
        }

        i_total += i_bytes;
736
        i_total /= DVDCSS_BLOCK_SIZE;
737 738 739 740 741

        if( i_bytes != i_len )
        {
            /* We reached the end of the file or a signal interrupted
             * the read. Return a partial read. */
742 743 744
            int i_seek;

            dvdcss->i_pos = -1;
745
            i_seek = libc_seek( dvdcss, i_total );
746 747 748 749 750 751
            if( i_seek < 0 )
            {
                return i_seek;
            }

            /* We have to return now so that i_pos isn't clobbered */
752
            return i_total;
753 754 755
        }
    }

756 757
    dvdcss->i_pos += i_total;
    return i_total;
758
#else
759
    int i_read = readv( dvdcss->i_fd, p_iovec, i_blocks );
760

761 762
    if( i_read < 0 )
    {
763
        dvdcss->i_pos = -1;
764 765 766
        return i_read;
    }

767 768 769
    i_read /= DVDCSS_BLOCK_SIZE;
    dvdcss->i_pos += i_read;
    return i_read;
770 771 772
#endif
}

773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796
/*****************************************************************************
 * stream_readv: vectored read
 *****************************************************************************/
static int stream_readv ( dvdcss_t dvdcss, const struct iovec *p_iovec,
                          int i_blocks )
{
    int i_read;

    if( !dvdcss->p_stream_cb->pf_readv )
        return -1;

    i_read = dvdcss->p_stream_cb->pf_readv( dvdcss->p_stream, p_iovec,
                                            i_blocks );

    if( i_read < 0 )
    {
        dvdcss->i_pos = -1;
        return i_read;
    }

    dvdcss->i_pos += i_read / DVDCSS_BLOCK_SIZE;
    return i_read / DVDCSS_BLOCK_SIZE;
}

797
#if defined( _WIN32 )
798
/*****************************************************************************
799
 * win2k_readv: vectored read using ReadFile for Win2K
800
 *****************************************************************************/
801 802
static int win2k_readv ( dvdcss_t dvdcss, const struct iovec *p_iovec,
                         int i_blocks )
803 804 805
{
    int i_index;
    int i_blocks_read, i_blocks_total = 0;
806
    DWORD i_bytes;
807 808 809 810 811 812 813

    /* 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;

814
        free( dvdcss->p_readv_buffer );
815 816 817 818 819 820

        /* 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 )
        {
821
            print_error( dvdcss, "scatter input (readv) failed" );
822
            dvdcss->i_pos = -1;
823 824 825 826 827 828
            return -1;
        }
    }

    for( i_index = i_blocks; i_index; i_index-- )
    {
829
        i_blocks_total += p_iovec[i_index-1].iov_len;
830 831 832 833
    }

    if( i_blocks_total <= 0 ) return 0;

834
    if( !ReadFile( (HANDLE)dvdcss->i_fd, dvdcss->p_readv_buffer,
835
                   i_blocks_total, &i_bytes, NULL ) )
836
    {
837 838 839 840 841
        /* 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;
842
    }
843
    i_blocks_read = i_bytes / DVDCSS_BLOCK_SIZE;
844 845 846 847 848 849 850 851 852 853 854 855 856 857

    /* 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 );
858
    }
859

860
    dvdcss->i_pos += i_blocks_read;
861 862
    return i_blocks_read;
}
863
#endif /* defined( _WIN32 ) */