[vlc-commits] [Git][videolan/vlc][master] cdrom: distinguish unrecoverable read failures

Steve Lhomme (@robUx4) gitlab at videolan.org
Tue Sep 15 06:08:15 UTC 2026



Steve Lhomme pushed to branch master at VideoLAN / VLC


Commits:
c0a6eb3d by Pierre Bogdanovscky at 2026-09-15T05:59:07+00:00
cdrom: distinguish unrecoverable read failures

ioctl_ReadSectors() answered -1 for every failure, so callers skipped the
sector and asked for the next one. That is right for a scratch and wrong
for an ejected disc, where a track takes its full running time to produce
silence. Return -2 when the drive says there is nothing left to read, from
ENOMEDIUM, ENODEV and ENXIO on Linux; the other platforms keep returning
-1 until someone with the hardware can look at them. cdda.c then returns
VLC_DEMUXER_EGENERIC and vcd.c sets *eof, Block() having no way to report
an error. Nothing else changes: -2 is a value no arm returned before, and
every caller tested for a negative result.

Fixes: #7545
Assisted-by: claude-code:claude-opus-5-0

- - - - -


4 changed files:

- modules/access/cdda.c
- modules/access/vcd/cdrom.c
- modules/access/vcd/cdrom.h
- modules/access/vcd/vcd.c


Changes:

=====================================
modules/access/cdda.c
=====================================
@@ -165,13 +165,18 @@ static int Demux(demux_t *demux)
     if (unlikely(block == NULL))
         return VLC_DEMUXER_EOF;
 
-    if (ioctl_ReadSectors(VLC_OBJECT(demux), sys->vcddev,
-                          sys->start + sys->position,
-                          block->p_buffer, count, CDDA_TYPE) < 0)
+    int ret = ioctl_ReadSectors(VLC_OBJECT(demux), sys->vcddev,
+                                sys->start + sys->position,
+                                block->p_buffer, count, CDDA_TYPE);
+    if (ret < 0)
     {
         msg_Err(demux, "cannot read sector %u", sys->position);
         block_Release(block);
 
+        /* Nothing left to read: the disc is gone, or the drive is. */
+        if (ret == -2)
+            return VLC_DEMUXER_EGENERIC;
+
         /* Skip potentially bad sector */
         sys->position++;
         return VLC_DEMUXER_SUCCESS;


=====================================
modules/access/vcd/cdrom.c
=====================================
@@ -41,6 +41,7 @@
 #   define INCL_DOSDEVIOCTL
 #endif
 
+#include <errno.h>
 #include <sys/types.h>
 #include <unistd.h>
 #include <sys/stat.h>
@@ -658,6 +659,7 @@ int ioctl_ReadSectors( vlc_object_t *p_this, const vcddev_t *p_vcddev,
 
         if( ioctl( p_vcddev->i_device_handle, DKIOCCDREAD, &cd_read ) == -1 )
         {
+            /* TODO return -2 in case of unrecoverable read failure. */
             msg_Err( p_this, "could not read block %d", i_sector );
             goto error;
         }
@@ -676,18 +678,40 @@ int ioctl_ReadSectors( vlc_object_t *p_this, const vcddev_t *p_vcddev,
                              VCD_SECTOR_SIZE * i_nb, &dwBytesReturned,
                              NULL ) == 0 )
         {
+            DWORD dwError = GetLastError();
+            bool b_read = false;
+
             if( i_type == VCD_TYPE )
             {
                 /* Retry in YellowMode2 */
                 cdrom_raw.TrackMode = YellowMode2;
-                if( DeviceIoControl( p_vcddev->h_device_handle,
-                                     IOCTL_CDROM_RAW_READ, &cdrom_raw,
-                                     sizeof(RAW_READ_INFO), p_block,
-                                     VCD_SECTOR_SIZE * i_nb, &dwBytesReturned,
-                                     NULL ) == 0 )
-                    goto error;
+                b_read = DeviceIoControl( p_vcddev->h_device_handle,
+                                          IOCTL_CDROM_RAW_READ, &cdrom_raw,
+                                          sizeof(RAW_READ_INFO), p_block,
+                                          VCD_SECTOR_SIZE * i_nb,
+                                          &dwBytesReturned, NULL ) != 0;
+                if( !b_read )
+                    dwError = GetLastError();
+            }
+
+            if( !b_read )
+            {
+                if( dwError == ERROR_NOT_READY ||
+                    dwError == ERROR_MEDIA_CHANGED ||
+                    dwError == ERROR_NO_MEDIA_IN_DRIVE ||
+                    dwError == ERROR_DEV_NOT_EXIST ||
+                    dwError == ERROR_DEVICE_NOT_CONNECTED ||
+                    dwError == ERROR_DEVICE_REMOVED )
+                {
+                    msg_Err( p_this, "cannot read block %d: no disc",
+                             i_sector );
+                    goto unrecoverable;
+                }
+
+                msg_Err( p_this, "could not read block %d from disc "
+                         "(error %lu)", i_sector, dwError );
+                goto error;
             }
-            else return -1;
         }
 
 #elif defined( __OS2__ )
@@ -706,6 +730,7 @@ int ioctl_ReadSectors( vlc_object_t *p_this, const vcddev_t *p_vcddev,
                           p_block, VCD_SECTOR_SIZE * i_nb, &data_len );
         if( rc )
         {
+            /* TODO return -2 in case of unrecoverable read failure. */
             msg_Err( p_this, "could not read block %d", i_sector );
             goto error;
         }
@@ -742,6 +767,7 @@ int ioctl_ReadSectors( vlc_object_t *p_this, const vcddev_t *p_vcddev,
         }
         if( sc.retsts || sc.error )
         {
+            /* TODO return -2 in case of unrecoverable read failure. */
             msg_Err( p_this, "SCSI command failed: status %d error %d",
                              sc.retsts, sc.error );
             goto error;
@@ -767,6 +793,7 @@ int ioctl_ReadSectors( vlc_object_t *p_this, const vcddev_t *p_vcddev,
         ssize_t toread = VCD_SECTOR_SIZE * i_nb;
         if( read( p_vcddev->i_device_handle, p_block, toread ) != toread )
         {
+            /* TODO return -2 in case of unrecoverable read failure. */
             msg_Err( p_this, "Could not read sector %d", i_sector );
             goto error;
         }
@@ -785,6 +812,21 @@ int ioctl_ReadSectors( vlc_object_t *p_this, const vcddev_t *p_vcddev,
             if( ioctl( p_vcddev->i_device_handle, CDROMREADRAW,
                        p_block + i * VCD_SECTOR_SIZE ) == -1 )
             {
+                /* ENOMEDIUM is Linux's, and this arm is the last #else of the
+                 * platform chain, so it may be compiled where that spelling
+                 * does not exist. ENODEV and ENXIO are POSIX and need no
+                 * guard. */
+                if( errno == ENODEV || errno == ENXIO
+#ifdef ENOMEDIUM
+                    || errno == ENOMEDIUM
+#endif
+                )
+                    {
+                        msg_Err( p_this, "cannot read block %i: no disc",
+                                 i_sector );
+                        goto unrecoverable;
+                    }
+
                 msg_Err( p_this, "could not read block %i from disc",
                          i_sector );
 
@@ -816,6 +858,11 @@ error:
     if( i_type == VCD_TYPE )
         free( p_block );
     return( -1 );
+
+unrecoverable:
+    if( i_type == VCD_TYPE )
+        free( p_block );
+    return( -2 );
 }
 
 /****************************************************************************


=====================================
modules/access/vcd/cdrom.h
=====================================
@@ -155,6 +155,11 @@ typedef struct entries_sect_s
 vcddev_t *ioctl_Open         ( vlc_object_t *, const char * );
 void      ioctl_Close        ( vlc_object_t *, vcddev_t * );
 vcddev_toc_t * ioctl_GetTOC  ( vlc_object_t *, const vcddev_t * );
+
+/* Returns:
+ *    0  success
+ *   -1  recoverable failure, or at least not known not to be (ex: a scratch)
+ *   -2  irrecoverable failure (ex: disc ejected, device unplugged) */
 int       ioctl_ReadSectors  ( vlc_object_t *, const vcddev_t *,
                                int, uint8_t *, int, int );
 


=====================================
modules/access/vcd/vcd.c
=====================================
@@ -369,12 +369,20 @@ static block_t *Block( stream_t *p_access, bool *restrict eof )
         return NULL;
     }
 
-    if( ioctl_ReadSectors( VLC_OBJECT(p_access), p_sys->vcddev,
-            p_sys->i_sector, p_block->p_buffer, i_blocks, VCD_TYPE ) < 0 )
+    int i_ret = ioctl_ReadSectors( VLC_OBJECT(p_access), p_sys->vcddev,
+                                   p_sys->i_sector, p_block->p_buffer, i_blocks, VCD_TYPE );
+    if( i_ret < 0 )
     {
         msg_Err( p_access, "cannot read sector %i", p_sys->i_sector );
         block_Release( p_block );
 
+        /* Nothing left to read: the disc is gone, or the drive is. */
+        if( i_ret == -2 )
+        {
+            *eof = true;
+            return NULL;
+        }
+
         /* Try to skip one sector (in case of bad sectors) */
         p_sys->offset += VCD_DATA_SIZE;
         p_sys->i_sector++;



View it on GitLab: https://code.videolan.org/videolan/vlc/-/commit/c0a6eb3d965bacb3f1f34a9f6d55742257dcd66b

-- 
View it on GitLab: https://code.videolan.org/videolan/vlc/-/commit/c0a6eb3d965bacb3f1f34a9f6d55742257dcd66b
You're receiving this email because of your account on code.videolan.org. Manage all notifications: https://code.videolan.org/-/profile/notifications | Help: https://code.videolan.org/help




More information about the vlc-commits mailing list