vserver 1.9.5.x5
[linux-2.6.git] / drivers / cdrom / cdrom.c
index 2e1ee63..9deca49 100644 (file)
   -- Mt Rainier support
   -- DVD-RAM write open fixes
 
+  Nov 5 2001, Aug 8 2002. Modified by Andy Polyakov
+  <appro@fy.chalmers.se> to support MMC-3 compliant DVD+RW units.
+
+  Modified by Nigel Kukard <nkukard@lbsd.net> - support DVD+RW
+  2.4.x patch by Andy Polyakov <appro@fy.chalmers.se>
+
 -------------------------------------------------------------------------*/
 
 #define REVISION "Revision: 3.20"
@@ -290,14 +296,14 @@ static int lockdoor = 1;
 static int check_media_type;
 /* automatically restart mrw format */
 static int mrw_format_restart = 1;
-MODULE_PARM(debug, "i");
-MODULE_PARM(autoclose, "i");
-MODULE_PARM(autoeject, "i");
-MODULE_PARM(lockdoor, "i");
-MODULE_PARM(check_media_type, "i");
-MODULE_PARM(mrw_format_restart, "i");
+module_param(debug, bool, 0);
+module_param(autoclose, bool, 0);
+module_param(autoeject, bool, 0);
+module_param(lockdoor, bool, 0);
+module_param(check_media_type, bool, 0);
+module_param(mrw_format_restart, bool, 0);
 
-static spinlock_t cdrom_lock = SPIN_LOCK_UNLOCKED;
+static DEFINE_SPINLOCK(cdrom_lock);
 
 static const char *mrw_format_status[] = {
        "not mrw",
@@ -354,6 +360,19 @@ static void cdrom_sysctl_register(void);
 #endif /* CONFIG_SYSCTL */ 
 static struct cdrom_device_info *topCdromPtr;
 
+static int cdrom_dummy_generic_packet(struct cdrom_device_info *cdi,
+                                     struct packet_command *cgc)
+{
+       if (cgc->sense) {
+               cgc->sense->sense_key = 0x05;
+               cgc->sense->asc = 0x20;
+               cgc->sense->ascq = 0x00;
+       }
+
+       cgc->stat = -EIO;
+       return -EIO;
+}
+
 /* This macro makes sure we don't have to check on cdrom_device_ops
  * existence in the run-time routines below. Change_capability is a
  * hack to have the capability flags defined const, while we can still
@@ -411,6 +430,9 @@ int register_cdrom(struct cdrom_device_info *cdi)
        else
                cdi->cdda_method = CDDA_OLD;
 
+       if (!cdo->generic_packet)
+               cdo->generic_packet = cdrom_dummy_generic_packet;
+
        cdinfo(CD_REG_UNREG, "drive \"/dev/%s\" registered\n", cdi->name);
        spin_lock(&cdrom_lock);
        cdi->next = topCdromPtr;        
@@ -472,6 +494,9 @@ int cdrom_get_media_event(struct cdrom_device_info *cdi,
        if (be16_to_cpu(eh->data_len) < sizeof(*med))
                return 1;
 
+       if (eh->nea || eh->notification_class != 0x4)
+               return 1;
+
        memcpy(med, &buffer[sizeof(*eh)], sizeof(*med));
        return 0;
 }
@@ -480,7 +505,7 @@ int cdrom_get_media_event(struct cdrom_device_info *cdi,
  * the first prototypes used 0x2c as the page code for the mrw mode page,
  * subsequently this was changed to 0x03. probe the one used by this drive
  */
-int cdrom_mrw_probe_pc(struct cdrom_device_info *cdi)
+static int cdrom_mrw_probe_pc(struct cdrom_device_info *cdi)
 {
        struct packet_command cgc;
        char buffer[16];
@@ -501,7 +526,7 @@ int cdrom_mrw_probe_pc(struct cdrom_device_info *cdi)
        return 1;
 }
 
-int cdrom_is_mrw(struct cdrom_device_info *cdi, int *write)
+static int cdrom_is_mrw(struct cdrom_device_info *cdi, int *write)
 {
        struct packet_command cgc;
        struct mrw_feature_desc *mfd;
@@ -521,6 +546,8 @@ int cdrom_is_mrw(struct cdrom_device_info *cdi, int *write)
                return ret;
 
        mfd = (struct mrw_feature_desc *)&buffer[sizeof(struct feature_header)];
+       if (be16_to_cpu(mfd->feature_code) != CDF_MRW)
+               return 1;
        *write = mfd->write;
 
        if ((ret = cdrom_mrw_probe_pc(cdi))) {
@@ -605,13 +632,16 @@ static int cdrom_flush_cache(struct cdrom_device_info *cdi)
 static int cdrom_mrw_exit(struct cdrom_device_info *cdi)
 {
        disc_information di;
-       int ret = 0;
+       int ret;
 
-       if (cdrom_get_disc_info(cdi, &di))
+       ret = cdrom_get_disc_info(cdi, &di);
+       if (ret < 0 || ret < (int)offsetof(typeof(di),disc_type))
                return 1;
 
+       ret = 0;
        if (di.mrw_status == CDM_MRW_BGFORMAT_ACTIVE) {
-               printk(KERN_INFO "cdrom: issuing MRW back ground format suspend\n");
+               printk(KERN_INFO "cdrom: issuing MRW back ground "
+                               "format suspend\n");
                ret = cdrom_mrw_bgformat_susp(cdi, 0);
        }
 
@@ -650,7 +680,7 @@ static int cdrom_mrw_set_lba_space(struct cdrom_device_info *cdi, int space)
        return 0;
 }
 
-int cdrom_get_random_writable(struct cdrom_device_info *cdi,
+static int cdrom_get_random_writable(struct cdrom_device_info *cdi,
                              struct rwrt_feature_desc *rfd)
 {
        struct packet_command cgc;
@@ -671,7 +701,7 @@ int cdrom_get_random_writable(struct cdrom_device_info *cdi,
        return 0;
 }
 
-int cdrom_has_defect_mgt(struct cdrom_device_info *cdi)
+static int cdrom_has_defect_mgt(struct cdrom_device_info *cdi)
 {
        struct packet_command cgc;
        char buffer[16];
@@ -696,7 +726,7 @@ int cdrom_has_defect_mgt(struct cdrom_device_info *cdi)
 }
 
 
-int cdrom_is_random_writable(struct cdrom_device_info *cdi, int *write)
+static int cdrom_is_random_writable(struct cdrom_device_info *cdi, int *write)
 {
        struct rwrt_feature_desc rfd;
        int ret;
@@ -715,8 +745,10 @@ int cdrom_is_random_writable(struct cdrom_device_info *cdi, int *write)
 static int cdrom_media_erasable(struct cdrom_device_info *cdi)
 {
        disc_information di;
+       int ret;
 
-       if (cdrom_get_disc_info(cdi, &di))
+       ret = cdrom_get_disc_info(cdi, &di);
+       if (ret < 0 || ret < offsetof(typeof(di), n_first_track))
                return -1;
 
        return di.erasable;
@@ -752,7 +784,8 @@ static int cdrom_mrw_open_write(struct cdrom_device_info *cdi)
                return 1;
        }
 
-       if (cdrom_get_disc_info(cdi, &di))
+       ret = cdrom_get_disc_info(cdi, &di);
+       if (ret < 0 || ret < offsetof(typeof(di),disc_type))
                return 1;
 
        if (!di.erasable)
@@ -766,10 +799,12 @@ static int cdrom_mrw_open_write(struct cdrom_device_info *cdi)
         * 3    -       MRW formatting complete
         */
        ret = 0;
-       printk(KERN_INFO "cdrom open: mrw_status '%s'\n", mrw_format_status[di.mrw_status]);
+       printk(KERN_INFO "cdrom open: mrw_status '%s'\n",
+                       mrw_format_status[di.mrw_status]);
        if (!di.mrw_status)
                ret = 1;
-       else if (di.mrw_status == CDM_MRW_BGFORMAT_INACTIVE && mrw_format_restart)
+       else if (di.mrw_status == CDM_MRW_BGFORMAT_INACTIVE &&
+                       mrw_format_restart)
                ret = cdrom_mrw_bgformat(cdi, 1);
 
        return ret;
@@ -821,6 +856,39 @@ static int cdrom_ram_open_write(struct cdrom_device_info *cdi)
        return ret;
 }
 
+static void cdrom_mmc3_profile(struct cdrom_device_info *cdi)
+{
+       struct packet_command cgc;
+       char buffer[32];
+       int ret, mmc3_profile;
+
+       init_cdrom_command(&cgc, buffer, sizeof(buffer), CGC_DATA_READ);
+
+       cgc.cmd[0] = GPCMD_GET_CONFIGURATION;
+       cgc.cmd[1] = 0;
+       cgc.cmd[2] = cgc.cmd[3] = 0;            /* Starting Feature Number */
+       cgc.cmd[8] = sizeof(buffer);            /* Allocation Length */
+       cgc.quiet = 1;
+
+       if ((ret = cdi->ops->generic_packet(cdi, &cgc)))
+               mmc3_profile = 0xffff;
+       else
+               mmc3_profile = (buffer[6] << 8) | buffer[7];
+
+       cdi->mmc3_profile = mmc3_profile;
+}
+
+static int cdrom_is_dvd_rw(struct cdrom_device_info *cdi)
+{
+       switch (cdi->mmc3_profile) {
+       case 0x12:      /* DVD-RAM      */
+       case 0x1A:      /* DVD+RW       */
+               return 0;
+       default:
+               return 1;
+       }
+}
+
 /*
  * returns 0 for ok to open write, non-0 to disallow
  */
@@ -862,10 +930,50 @@ static int cdrom_open_write(struct cdrom_device_info *cdi)
                ret = cdrom_ram_open_write(cdi);
        else if (CDROM_CAN(CDC_MO_DRIVE))
                ret = mo_open_write(cdi);
+       else if (!cdrom_is_dvd_rw(cdi))
+               ret = 0;
 
        return ret;
 }
 
+static void cdrom_dvd_rw_close_write(struct cdrom_device_info *cdi)
+{
+       struct packet_command cgc;
+
+       if (cdi->mmc3_profile != 0x1a) {
+               cdinfo(CD_CLOSE, "%s: No DVD+RW\n", cdi->name);
+               return;
+       }
+
+       if (!cdi->media_written) {
+               cdinfo(CD_CLOSE, "%s: DVD+RW media clean\n", cdi->name);
+               return;
+       }
+
+       printk(KERN_INFO "cdrom: %s: dirty DVD+RW media, \"finalizing\"\n",
+              cdi->name);
+
+       init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE);
+       cgc.cmd[0] = GPCMD_FLUSH_CACHE;
+       cgc.timeout = 30*HZ;
+       cdi->ops->generic_packet(cdi, &cgc);
+
+       init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE);
+       cgc.cmd[0] = GPCMD_CLOSE_TRACK;
+       cgc.timeout = 3000*HZ;
+       cgc.quiet = 1;
+       cdi->ops->generic_packet(cdi, &cgc);
+
+       init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE);
+       cgc.cmd[0] = GPCMD_CLOSE_TRACK;
+       cgc.cmd[2] = 2;  /* Close session */
+       cgc.quiet = 1;
+       cgc.timeout = 3000*HZ;
+       cdi->ops->generic_packet(cdi, &cgc);
+
+       cdi->media_written = 0;
+}
+
 static int cdrom_close_write(struct cdrom_device_info *cdi)
 {
 #if 0
@@ -898,6 +1006,7 @@ int cdrom_open(struct cdrom_device_info *cdi, struct inode *ip, struct file *fp)
                ret = open_for_data(cdi);
                if (ret)
                        goto err;
+               cdrom_mmc3_profile(cdi);
                if (fp->f_mode & FMODE_WRITE) {
                        ret = -EROFS;
                        if (cdrom_open_write(cdi))
@@ -905,6 +1014,7 @@ int cdrom_open(struct cdrom_device_info *cdi, struct inode *ip, struct file *fp)
                        if (!CDROM_CAN(CDC_RAM))
                                goto err;
                        ret = 0;
+                       cdi->media_written = 0;
                }
        }
 
@@ -966,6 +1076,8 @@ int open_for_data(struct cdrom_device_info * cdi)
                        }
                        cdinfo(CD_OPEN, "the tray is now closed.\n"); 
                }
+               /* the door should be closed now, check for the disc */
+               ret = cdo->drive_status(cdi, CDSL_CURRENT);
                if (ret!=CDS_DISC_OK) {
                        ret = -ENOMEDIUM;
                        goto clean_up_and_return;
@@ -1096,6 +1208,8 @@ int cdrom_release(struct cdrom_device_info *cdi, struct file *fp)
                cdi->use_count--;
        if (cdi->use_count == 0)
                cdinfo(CD_CLOSE, "Use count for \"/dev/%s\" now zero\n", cdi->name);
+       if (cdi->use_count == 0)
+               cdrom_dvd_rw_close_write(cdi);
        if (cdi->use_count == 0 &&
            (cdo->capability & CDC_LOCK) && !keeplocked) {
                cdinfo(CD_CLOSE, "Unlocking door!\n");
@@ -1302,6 +1416,7 @@ int media_changed(struct cdrom_device_info *cdi, int queue)
        if (cdi->ops->media_changed(cdi, CDSL_CURRENT)) {
                cdi->mc_flags = 0x3;    /* set bit on both queues */
                ret |= 1;
+               cdi->media_written = 0;
        }
        cdi->mc_flags &= ~mask;         /* clear bit */
        return ret;
@@ -1763,7 +1878,7 @@ static int dvd_read_manufact(struct cdrom_device_info *cdi, dvd_struct *s)
        s->manufact.len = buf[0] << 8 | buf[1];
        if (s->manufact.len < 0 || s->manufact.len > 2048) {
                cdinfo(CD_WARNING, "Received invalid manufacture info length"
-                                  " (%d)\n", s->bca.len);
+                                  " (%d)\n", s->manufact.len);
                ret = -EIO;
        } else {
                memcpy(s->manufact.value, &buf[4], s->manufact.len);
@@ -1922,7 +2037,8 @@ static int cdrom_read_cdda_old(struct cdrom_device_info *cdi, __u8 __user *ubuf,
                               int lba, int nframes)
 {
        struct packet_command cgc;
-       int nr, ret;
+       int ret = 0;
+       int nr;
 
        cdi->last_sense = 0;
 
@@ -1944,8 +2060,8 @@ static int cdrom_read_cdda_old(struct cdrom_device_info *cdi, __u8 __user *ubuf,
                return -ENOMEM;
 
        if (!access_ok(VERIFY_WRITE, ubuf, nframes * CD_FRAMESIZE_RAW)) {
-               kfree(cgc.buffer);
-               return -EFAULT;
+               ret = -EFAULT;
+               goto out;
        }
 
        cgc.data_direction = CGC_DATA_READ;
@@ -1956,13 +2072,17 @@ static int cdrom_read_cdda_old(struct cdrom_device_info *cdi, __u8 __user *ubuf,
                ret = cdrom_read_block(cdi, &cgc, lba, nr, 1, CD_FRAMESIZE_RAW);
                if (ret)
                        break;
-               __copy_to_user(ubuf, cgc.buffer, CD_FRAMESIZE_RAW * nr);
+               if (__copy_to_user(ubuf, cgc.buffer, CD_FRAMESIZE_RAW * nr)) {
+                       ret = -EFAULT;
+                       break;
+               }
                ubuf += CD_FRAMESIZE_RAW * nr;
                nframes -= nr;
                lba += nr;
        }
+out:
        kfree(cgc.buffer);
-       return 0;
+       return ret;
 }
 
 static int cdrom_read_cdda_bpc(struct cdrom_device_info *cdi, __u8 __user *ubuf,
@@ -2504,7 +2624,7 @@ static int mmc_ioctl(struct cdrom_device_info *cdi, unsigned int cmd,
        struct cdrom_device_ops *cdo = cdi->ops;
        struct packet_command cgc;
        struct request_sense sense;
-       char buffer[32];
+       unsigned char buffer[32];
        int ret = 0;
 
        memset(&cgc, 0, sizeof(cgc));
@@ -2631,8 +2751,9 @@ static int mmc_ioctl(struct cdrom_device_info *cdi, unsigned int cmd,
        case CDROMVOLCTRL:
        case CDROMVOLREAD: {
                struct cdrom_volctrl volctrl;
-               char mask[32];
+               char mask[sizeof(buffer)];
                unsigned short offset;
+
                cdinfo(CD_DO_IOCTL, "entering CDROMVOLUME\n");
 
                IOCTL_IN(arg, struct cdrom_volctrl, volctrl);
@@ -2642,17 +2763,27 @@ static int mmc_ioctl(struct cdrom_device_info *cdi, unsigned int cmd,
                if ((ret = cdrom_mode_sense(cdi, &cgc, GPMODE_AUDIO_CTL_PAGE, 0)))
                    return ret;
                
-               /* some drives have longer pages, adjust and reread. */
-               if (buffer[1] > cgc.buflen) {
-                       cgc.buflen = buffer[1] + 2;
-                       if ((ret = cdrom_mode_sense(cdi, &cgc, 
-                                       GPMODE_AUDIO_CTL_PAGE, 0))) 
-                           return ret;
+               /* originally the code depended on buffer[1] to determine
+                  how much data is available for transfer. buffer[1] is
+                  unfortunately ambigious and the only reliable way seem
+                  to be to simply skip over the block descriptor... */
+               offset = 8 + be16_to_cpu(*(unsigned short *)(buffer+6));
+
+               if (offset + 16 > sizeof(buffer))
+                       return -E2BIG;
+
+               if (offset + 16 > cgc.buflen) {
+                       cgc.buflen = offset+16;
+                       ret = cdrom_mode_sense(cdi, &cgc,
+                                               GPMODE_AUDIO_CTL_PAGE, 0);
+                       if (ret)
+                               return ret;
                }
-               
-               /* get the offset from the length of the page. length
-                  is measure from byte 2 an on, thus the 14. */
-               offset = buffer[1] - 14;
+
+               /* sanity check */
+               if ((buffer[offset] & 0x3f) != GPMODE_AUDIO_CTL_PAGE ||
+                               buffer[offset+1] < 14)
+                       return -EINVAL;
 
                /* now we have the current volume settings. if it was only
                   a CDROMVOLREAD, return these values */
@@ -2677,7 +2808,8 @@ static int mmc_ioctl(struct cdrom_device_info *cdi, unsigned int cmd,
                buffer[offset+15] = volctrl.channel3 & mask[offset+15];
 
                /* set volume */
-               cgc.buffer = buffer;
+               cgc.buffer = buffer + offset - 8;
+               memset(cgc.buffer, 0, 8);
                return cdrom_mode_select(cdi, &cgc);
                }
 
@@ -2760,7 +2892,7 @@ static int cdrom_get_track_info(struct cdrom_device_info *cdi, __u16 track, __u8
 {
        struct cdrom_device_ops *cdo = cdi->ops;
        struct packet_command cgc;
-       int ret;
+       int ret, buflen;
 
        init_cdrom_command(&cgc, ti, 8, CGC_DATA_READ);
        cgc.cmd[0] = GPCMD_READ_TRACK_RZONE_INFO;
@@ -2773,14 +2905,18 @@ static int cdrom_get_track_info(struct cdrom_device_info *cdi, __u16 track, __u8
        if ((ret = cdo->generic_packet(cdi, &cgc)))
                return ret;
        
-       cgc.buflen = be16_to_cpu(ti->track_information_length) +
+       buflen = be16_to_cpu(ti->track_information_length) +
                     sizeof(ti->track_information_length);
 
-       if (cgc.buflen > sizeof(track_information))
-               cgc.buflen = sizeof(track_information);
+       if (buflen > sizeof(track_information))
+               buflen = sizeof(track_information);
 
-       cgc.cmd[8] = cgc.buflen;
-       return cdo->generic_packet(cdi, &cgc);
+       cgc.cmd[8] = cgc.buflen = buflen;
+       if ((ret = cdo->generic_packet(cdi, &cgc)))
+               return ret;
+
+       /* return actual fill size */
+       return buflen;
 }
 
 /* requires CD R/RW */
@@ -2788,7 +2924,7 @@ static int cdrom_get_disc_info(struct cdrom_device_info *cdi, disc_information *
 {
        struct cdrom_device_ops *cdo = cdi->ops;
        struct packet_command cgc;
-       int ret;
+       int ret, buflen;
 
        /* set up command and get the disc info */
        init_cdrom_command(&cgc, di, sizeof(*di), CGC_DATA_READ);
@@ -2802,14 +2938,18 @@ static int cdrom_get_disc_info(struct cdrom_device_info *cdi, disc_information *
        /* not all drives have the same disc_info length, so requeue
         * packet with the length the drive tells us it can supply
         */
-       cgc.buflen = be16_to_cpu(di->disc_information_length) +
+       buflen = be16_to_cpu(di->disc_information_length) +
                     sizeof(di->disc_information_length);
 
-       if (cgc.buflen > sizeof(disc_information))
-               cgc.buflen = sizeof(disc_information);
+       if (buflen > sizeof(disc_information))
+               buflen = sizeof(disc_information);
 
-       cgc.cmd[8] = cgc.buflen;
-       return cdo->generic_packet(cdi, &cgc);
+       cgc.cmd[8] = cgc.buflen = buflen;
+       if ((ret = cdo->generic_packet(cdi, &cgc)))
+               return ret;
+
+       /* return actual fill size */
+       return buflen;
 }
 
 /* return the last written block on the CD-R media. this is for the udf
@@ -2820,27 +2960,37 @@ int cdrom_get_last_written(struct cdrom_device_info *cdi, long *last_written)
        disc_information di;
        track_information ti;
        __u32 last_track;
-       int ret = -1;
+       int ret = -1, ti_size;
 
        if (!CDROM_CAN(CDC_GENERIC_PACKET))
                goto use_toc;
 
-       if ((ret = cdrom_get_disc_info(cdi, &di)))
+       ret = cdrom_get_disc_info(cdi, &di);
+       if (ret < (int)(offsetof(typeof(di), last_track_lsb)
+                       + sizeof(di.last_track_lsb)))
                goto use_toc;
 
+       /* if unit didn't return msb, it's zeroed by cdrom_get_disc_info */
        last_track = (di.last_track_msb << 8) | di.last_track_lsb;
-       if ((ret = cdrom_get_track_info(cdi, last_track, 1, &ti)))
+       ti_size = cdrom_get_track_info(cdi, last_track, 1, &ti);
+       if (ti_size < (int)offsetof(typeof(ti), track_start))
                goto use_toc;
 
        /* if this track is blank, try the previous. */
        if (ti.blank) {
-               last_track--;
-               if ((ret = cdrom_get_track_info(cdi, last_track, 1, &ti)))
+               if (last_track==1)
                        goto use_toc;
+               last_track--;
+               ti_size = cdrom_get_track_info(cdi, last_track, 1, &ti);
        }
 
+       if (ti_size < (int)(offsetof(typeof(ti), track_size)
+                               + sizeof(ti.track_size)))
+               goto use_toc;
+
        /* if last recorded field is valid, return it. */
-       if (ti.lra_v) {
+       if (ti.lra_v && ti_size >= (int)(offsetof(typeof(ti), last_rec_address)
+                               + sizeof(ti.last_rec_address))) {
                *last_written = be32_to_cpu(ti.last_rec_address);
        } else {
                /* make it up instead */
@@ -2853,11 +3003,12 @@ int cdrom_get_last_written(struct cdrom_device_info *cdi, long *last_written)
 
        /* this is where we end up if the drive either can't do a
           GPCMD_READ_DISC_INFO or GPCMD_READ_TRACK_RZONE_INFO or if
-          it fails. then we return the toc contents. */
+          it doesn't give enough information or fails. then we return
+          the toc contents. */
 use_toc:
        toc.cdte_format = CDROM_MSF;
        toc.cdte_track = CDROM_LEADOUT;
-       if (cdi->ops->audio_ioctl(cdi, CDROMREADTOCENTRY, &toc))
+       if ((ret = cdi->ops->audio_ioctl(cdi, CDROMREADTOCENTRY, &toc)))
                return ret;
        sanitize_format(&toc.cdte_addr, &toc.cdte_format, CDROM_LBA);
        *last_written = toc.cdte_addr.lba;
@@ -2870,32 +3021,38 @@ static int cdrom_get_next_writable(struct cdrom_device_info *cdi, long *next_wri
        disc_information di;
        track_information ti;
        __u16 last_track;
-       int ret = -1;
+       int ret, ti_size;
 
        if (!CDROM_CAN(CDC_GENERIC_PACKET))
                goto use_last_written;
 
-       if ((ret = cdrom_get_disc_info(cdi, &di)))
+       ret = cdrom_get_disc_info(cdi, &di);
+       if (ret < 0 || ret < offsetof(typeof(di), last_track_lsb)
+                               + sizeof(di.last_track_lsb))
                goto use_last_written;
 
+       /* if unit didn't return msb, it's zeroed by cdrom_get_disc_info */
        last_track = (di.last_track_msb << 8) | di.last_track_lsb;
-       if ((ret = cdrom_get_track_info(cdi, last_track, 1, &ti)))
+       ti_size = cdrom_get_track_info(cdi, last_track, 1, &ti);
+       if (ti_size < 0 || ti_size < offsetof(typeof(ti), track_start))
                goto use_last_written;
 
         /* if this track is blank, try the previous. */
        if (ti.blank) {
+               if (last_track == 1)
+                       goto use_last_written;
                last_track--;
-               if ((ret = cdrom_get_track_info(cdi, last_track, 1, &ti)))
+               ti_size = cdrom_get_track_info(cdi, last_track, 1, &ti);
+               if (ti_size < 0)
                        goto use_last_written;
        }
 
        /* if next recordable address field is valid, use it. */
-       if (ti.nwa_v)
+       if (ti.nwa_v && ti_size >= offsetof(typeof(ti), next_writable)
+                               + sizeof(ti.next_writable)) {
                *next_writable = be32_to_cpu(ti.next_writable);
-       else
-               goto use_last_written;
-
-       return 0;
+               return 0;
+       }
 
 use_last_written:
        if ((ret = cdrom_get_last_written(cdi, next_writable))) {
@@ -2919,14 +3076,12 @@ EXPORT_SYMBOL(cdrom_mode_select);
 EXPORT_SYMBOL(cdrom_mode_sense);
 EXPORT_SYMBOL(init_cdrom_command);
 EXPORT_SYMBOL(cdrom_get_media_event);
-EXPORT_SYMBOL(cdrom_is_mrw);
-EXPORT_SYMBOL(cdrom_is_random_writable);
 
 #ifdef CONFIG_SYSCTL
 
 #define CDROM_STR_SIZE 1000
 
-struct cdrom_sysctl_settings {
+static struct cdrom_sysctl_settings {
        char    info[CDROM_STR_SIZE];   /* general info */
        int     autoclose;              /* close tray upon mount, etc */
        int     autoeject;              /* eject on umount */
@@ -2935,7 +3090,7 @@ struct cdrom_sysctl_settings {
        int     check;                  /* check media type */
 } cdrom_sysctl_settings;
 
-int cdrom_sysctl_info(ctl_table *ctl, int write, struct file * filp,
+static int cdrom_sysctl_info(ctl_table *ctl, int write, struct file * filp,
                            void __user *buffer, size_t *lenp, loff_t *ppos)
 {
         int pos;
@@ -3038,7 +3193,7 @@ int cdrom_sysctl_info(ctl_table *ctl, int write, struct file * filp,
    procfs/sysctl yet. When they are, this will naturally disappear. For now
    just update all drives. Later this will become the template on which
    new registered drives will be based. */
-void cdrom_update_settings(void)
+static void cdrom_update_settings(void)
 {
        struct cdrom_device_info *cdi;
 
@@ -3116,7 +3271,7 @@ static int cdrom_sysctl_handler(ctl_table *ctl, int write, struct file * filp,
 }
 
 /* Place files in /proc/sys/dev/cdrom */
-ctl_table cdrom_table[] = {
+static ctl_table cdrom_table[] = {
        {
                .ctl_name       = DEV_CDROM_INFO,
                .procname       = "info",
@@ -3168,7 +3323,7 @@ ctl_table cdrom_table[] = {
        { .ctl_name = 0 }
 };
 
-ctl_table cdrom_cdrom_table[] = {
+static ctl_table cdrom_cdrom_table[] = {
        {
                .ctl_name       = DEV_CDROM,
                .procname       = "cdrom",
@@ -3180,8 +3335,7 @@ ctl_table cdrom_cdrom_table[] = {
 };
 
 /* Make sure that /proc/sys/dev is there */
-ctl_table cdrom_root_table[] = {
-#ifdef CONFIG_PROC_FS
+static ctl_table cdrom_root_table[] = {
        {
                .ctl_name       = CTL_DEV,
                .procname       = "dev",
@@ -3189,7 +3343,6 @@ ctl_table cdrom_root_table[] = {
                .mode           = 0555,
                .child          = cdrom_cdrom_table,
        },
-#endif /* CONFIG_PROC_FS */
        { .ctl_name = 0 }
 };
 static struct ctl_table_header *cdrom_sysctl_header;