X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=extras%2Fcdrom_id%2Fcdrom_id.c;h=7908f6d19ae91a1c87c2e223caa48e43fd86ec95;hb=85e172c9bd223e08f31b466f86df3cca2b517216;hp=6500206228a3de4f30842a128e3343357fba9cfb;hpb=d83dfabe0d75d0c5dd322f05abc1f04d2ca5be6c;p=elogind.git diff --git a/extras/cdrom_id/cdrom_id.c b/extras/cdrom_id/cdrom_id.c index 650020622..7908f6d19 100644 --- a/extras/cdrom_id/cdrom_id.c +++ b/extras/cdrom_id/cdrom_id.c @@ -85,6 +85,8 @@ static unsigned int cd_media_cd_rw = 0; static unsigned int cd_media_dvd_rom = 0; static unsigned int cd_media_dvd_r = 0; static unsigned int cd_media_dvd_rw = 0; +static unsigned int cd_media_dvd_rw_ro = 0; /* restricted overwrite mode */ +static unsigned int cd_media_dvd_rw_seq = 0; /* sequential mode */ static unsigned int cd_media_dvd_ram = 0; static unsigned int cd_media_dvd_plus_r = 0; static unsigned int cd_media_dvd_plus_rw = 0; @@ -259,6 +261,115 @@ static int cd_inquiry(struct udev *udev, int fd) { return 0; } +static void feature_profile_media(struct udev *udev, int cur_profile) +{ + switch (cur_profile) { + case 0x03: + case 0x04: + case 0x05: + info(udev, "profile 0x%02x \n", cur_profile); + cd_media = 1; + cd_media_mo = 1; + break; + case 0x08: + info(udev, "profile 0x%02x media_cd_rom\n", cur_profile); + cd_media = 1; + cd_media_cd_rom = 1; + break; + case 0x09: + info(udev, "profile 0x%02x media_cd_r\n", cur_profile); + cd_media = 1; + cd_media_cd_r = 1; + break; + case 0x0a: + info(udev, "profile 0x%02x media_cd_rw\n", cur_profile); + cd_media = 1; + cd_media_cd_rw = 1; + break; + case 0x10: + info(udev, "profile 0x%02x media_dvd_ro\n", cur_profile); + cd_media = 1; + cd_media_dvd_rom = 1; + break; + case 0x11: + info(udev, "profile 0x%02x media_dvd_r\n", cur_profile); + cd_media = 1; + cd_media_dvd_r = 1; + break; + case 0x12: + info(udev, "profile 0x%02x media_dvd_ram\n", cur_profile); + cd_media = 1; + cd_media_dvd_ram = 1; + break; + case 0x13: + info(udev, "profile 0x%02x media_dvd_rw_ro\n", cur_profile); + cd_media = 1; + cd_media_dvd_rw = 1; + cd_media_dvd_rw_ro = 1; + break; + case 0x14: + info(udev, "profile 0x%02x media_dvd_rw_seq\n", cur_profile); + cd_media = 1; + cd_media_dvd_rw = 1; + cd_media_dvd_rw_seq = 1; + break; + case 0x1B: + info(udev, "profile 0x%02x media_dvd_plus_r\n", cur_profile); + cd_media = 1; + cd_media_dvd_plus_r = 1; + break; + case 0x1A: + info(udev, "profile 0x%02x media_dvd_plus_rw\n", cur_profile); + cd_media = 1; + cd_media_dvd_plus_rw = 1; + break; + case 0x2A: + info(udev, "profile 0x%02x media_dvd_plus_rw_dl\n", cur_profile); + cd_media = 1; + cd_media_dvd_plus_rw_dl = 1; + break; + case 0x2B: + info(udev, "profile 0x%02x media_dvd_plus_r_dl\n", cur_profile); + cd_media = 1; + cd_media_dvd_plus_r_dl = 1; + break; + case 0x40: + info(udev, "profile 0x%02x media_bd\n", cur_profile); + cd_media = 1; + cd_media_bd = 1; + break; + case 0x41: + case 0x42: + info(udev, "profile 0x%02x media_bd_r\n", cur_profile); + cd_media = 1; + cd_media_bd_r = 1; + break; + case 0x43: + info(udev, "profile 0x%02x media_bd_re\n", cur_profile); + cd_media = 1; + cd_media_bd_re = 1; + break; + case 0x50: + info(udev, "profile 0x%02x media_hddvd\n", cur_profile); + cd_media = 1; + cd_media_hddvd = 1; + break; + case 0x51: + info(udev, "profile 0x%02x media_hddvd_r\n", cur_profile); + cd_media = 1; + cd_media_hddvd_r = 1; + break; + case 0x52: + info(udev, "profile 0x%02x media_hddvd_rw\n", cur_profile); + cd_media = 1; + cd_media_hddvd_rw = 1; + break; + default: + info(udev, "profile 0x%02x \n", cur_profile); + break; + } +} + static int feature_profiles(struct udev *udev, const unsigned char *profiles, size_t size) { unsigned int i; @@ -348,6 +459,7 @@ static int feature_profiles(struct udev *udev, const unsigned char *profiles, si return 0; } +/* returns 0 if media was detected */ static int cd_profiles_old_mmc(struct udev *udev, int fd) { struct scsi_cmd sc; @@ -362,8 +474,14 @@ static int cd_profiles_old_mmc(struct udev *udev, int fd) err = scsi_cmd_run(udev, &sc, fd, header, sizeof(header)); if ((err != 0)) { info_scsi_cmd_err(udev, "READ DISC INFORMATION", err); - info(udev, "no current profile, assuming no media\n"); - return -1; + if (cd_media == 1) { + info(udev, "no current profile, but disc is present; assuming CD-ROM\n"); + cd_media_cd_rom = 1; + return 0; + } else { + info(udev, "no current profile, assuming no media\n"); + return -1; + } }; cd_media = 1; @@ -371,18 +489,17 @@ static int cd_profiles_old_mmc(struct udev *udev, int fd) if (header[2] & 16) { cd_media_cd_rw = 1; info(udev, "profile 0x0a media_cd_rw\n"); - } - else if ((header[2] & 3) < 2 && cd_cd_r) { + } else if ((header[2] & 3) < 2 && cd_cd_r) { cd_media_cd_r = 1; info(udev, "profile 0x09 media_cd_r\n"); - } - else { - cd_media_cd_rom = 1; - info(udev, "profile 0x08 media_cd_rom\n"); + } else { + cd_media_cd_rom = 1; + info(udev, "profile 0x08 media_cd_rom\n"); } return 0; } +/* returns 0 if media was detected */ static int cd_profiles(struct udev *udev, int fd) { struct scsi_cmd sc; @@ -391,24 +508,59 @@ static int cd_profiles(struct udev *udev, int fd) unsigned int len; unsigned int i; int err; + int ret; + + ret = -1; + /* First query the current profile */ scsi_cmd_init(udev, &sc, features, sizeof(features)); scsi_cmd_set(udev, &sc, 0, 0x46); - scsi_cmd_set(udev, &sc, 7, (sizeof(features) >> 8) & 0xff); - scsi_cmd_set(udev, &sc, 8, sizeof(features) & 0xff); + scsi_cmd_set(udev, &sc, 8, 8); scsi_cmd_set(udev, &sc, 9, 0); - err = scsi_cmd_run(udev, &sc, fd, features, sizeof(features)); + err = scsi_cmd_run(udev, &sc, fd, features, 8); if ((err != 0)) { info_scsi_cmd_err(udev, "GET CONFIGURATION", err); /* handle pre-MMC2 drives which do not support GET CONFIGURATION */ if (SK(err) == 0x5 && ASC(err) == 0x20) { info(udev, "drive is pre-MMC2 and does not support 46h get configuration command\n"); info(udev, "trying to work around the problem\n"); - return cd_profiles_old_mmc(udev, fd); + ret = cd_profiles_old_mmc(udev, fd); } + goto out; + } + + cur_profile = features[6] << 8 | features[7]; + if (cur_profile > 0) { + info(udev, "current profile 0x%02x\n", cur_profile); + feature_profile_media (udev, cur_profile); + ret = 0; /* we have media */ + } else { + info(udev, "no current profile, assuming no media\n"); + } + + len = features[0] << 24 | features[1] << 16 | features[2] << 8 | features[3]; + info(udev, "GET CONFIGURATION: size of features buffer 0x%04x\n", len); + + if (len > sizeof(features)) { + info(udev, "can not get features in a single query, truncating\n"); + len = sizeof(features); + } else if (len <= 8) { + len = sizeof(features); + } + + /* Now get the full feature buffer */ + scsi_cmd_init(udev, &sc, features, len); + scsi_cmd_set(udev, &sc, 0, 0x46); + scsi_cmd_set(udev, &sc, 7, ( len >> 8 ) & 0xff); + scsi_cmd_set(udev, &sc, 8, len & 0xff); + scsi_cmd_set(udev, &sc, 9, 0); + err = scsi_cmd_run(udev, &sc, fd, features, len); + if ((err != 0)) { + info_scsi_cmd_err(udev, "GET CONFIGURATION", err); return -1; } + /* parse the length once more, in case the drive decided to have other features suddenly :) */ len = features[0] << 24 | features[1] << 16 | features[2] << 8 | features[3]; info(udev, "GET CONFIGURATION: size of features buffer 0x%04x\n", len); @@ -433,115 +585,8 @@ static int cd_profiles(struct udev *udev, int fd) break; } } - - cur_profile = features[6] << 8 | features[7]; - if (cur_profile > 0) { - info(udev, "current profile 0x%02x\n", cur_profile); - } else { - info(udev, "no current profile, assuming no media\n"); - return -1; - } - - switch (cur_profile) { - case 0x03: - case 0x04: - case 0x05: - info(udev, "profile 0x%02x \n", cur_profile); - cd_media = 1; - cd_media_mo = 1; - break; - case 0x08: - info(udev, "profile 0x%02x media_cd_rom\n", cur_profile); - cd_media = 1; - cd_media_cd_rom = 1; - break; - case 0x09: - info(udev, "profile 0x%02x media_cd_r\n", cur_profile); - cd_media = 1; - cd_media_cd_r = 1; - break; - case 0x0a: - info(udev, "profile 0x%02x media_cd_rw\n", cur_profile); - cd_media = 1; - cd_media_cd_rw = 1; - break; - case 0x10: - info(udev, "profile 0x%02x media_dvd_ro\n", cur_profile); - cd_media = 1; - cd_media_dvd_rom = 1; - break; - case 0x11: - info(udev, "profile 0x%02x media_dvd_r\n", cur_profile); - cd_media = 1; - cd_media_dvd_r = 1; - break; - case 0x12: - info(udev, "profile 0x%02x media_dvd_ram\n", cur_profile); - cd_media = 1; - cd_media_dvd_ram = 1; - break; - case 0x13: - case 0x14: - info(udev, "profile 0x%02x media_dvd_rw\n", cur_profile); - cd_media = 1; - cd_media_dvd_rw = 1; - break; - case 0x1B: - info(udev, "profile 0x%02x media_dvd_plus_r\n", cur_profile); - cd_media = 1; - cd_media_dvd_plus_r = 1; - break; - case 0x1A: - info(udev, "profile 0x%02x media_dvd_plus_rw\n", cur_profile); - cd_media = 1; - cd_media_dvd_plus_rw = 1; - break; - case 0x2A: - info(udev, "profile 0x%02x media_dvd_plus_rw_dl\n", cur_profile); - cd_media = 1; - cd_media_dvd_plus_rw_dl = 1; - break; - case 0x2B: - info(udev, "profile 0x%02x media_dvd_plus_r_dl\n", cur_profile); - cd_media = 1; - cd_media_dvd_plus_r_dl = 1; - break; - case 0x40: - info(udev, "profile 0x%02x media_bd\n", cur_profile); - cd_media = 1; - cd_media_bd = 1; - break; - case 0x41: - case 0x42: - info(udev, "profile 0x%02x media_bd_r\n", cur_profile); - cd_media = 1; - cd_media_bd_r = 1; - break; - case 0x43: - info(udev, "profile 0x%02x media_bd_re\n", cur_profile); - cd_media = 1; - cd_media_bd_re = 1; - break; - case 0x50: - info(udev, "profile 0x%02x media_hddvd\n", cur_profile); - cd_media = 1; - cd_media_hddvd = 1; - break; - case 0x51: - info(udev, "profile 0x%02x media_hddvd_r\n", cur_profile); - cd_media = 1; - cd_media_hddvd_r = 1; - break; - case 0x52: - info(udev, "profile 0x%02x media_hddvd_rw\n", cur_profile); - cd_media = 1; - cd_media_hddvd_rw = 1; - break; - default: - info(udev, "profile 0x%02x \n", cur_profile); - break; - } - return 0; +out: + return ret; } static int cd_media_info(struct udev *udev, int fd) @@ -568,16 +613,23 @@ static int cd_media_info(struct udev *udev, int fd) cd_media = 1; info(udev, "disk type %02x\n", header[8]); + info(udev, "hardware reported media status: %s\n", media_status[header[2] & 3]); /* exclude plain CDROM, some fake cdroms return 0 for "blank" media here */ if (!cd_media_cd_rom) cd_media_state = media_status[header[2] & 3]; - /* DVD+RW discs once formatted are always "complete", DVD-RAM are - * "other" or "complete" if the disc is write protected; we need to - * check the contents if it is blank */ - if ((cd_media_dvd_plus_rw || cd_media_dvd_plus_rw_dl || cd_media_dvd_ram) && (header[2] & 3) > 1) { - unsigned char buffer[17 * 2048]; + /* fresh DVD-RW in restricted overwite mode reports itself as + * "appendable"; change it to "blank" to make it consistent with what + * gets reported after blanking, and what userspace expects */ + if (cd_media_dvd_rw_ro && (header[2] & 3) == 1) + cd_media_state = media_status[0]; + + /* DVD+RW discs (and DVD-RW in restricted mode) once formatted are + * always "complete", DVD-RAM are "other" or "complete" if the disc is + * write protected; we need to check the contents if it is blank */ + if ((cd_media_dvd_rw_ro || cd_media_dvd_plus_rw || cd_media_dvd_plus_rw_dl || cd_media_dvd_ram) && (header[2] & 3) > 1) { + unsigned char buffer[32 * 2048]; unsigned char result, len; int block, offset; @@ -648,10 +700,11 @@ static int cd_media_info(struct udev *udev, int fd) scsi_cmd_init(udev, &sc, buffer, sizeof(buffer)); scsi_cmd_set(udev, &sc, 0, 0x28); scsi_cmd_set(udev, &sc, 5, 0); - scsi_cmd_set(udev, &sc, 8, 17); + scsi_cmd_set(udev, &sc, 8, 32); scsi_cmd_set(udev, &sc, 9, 0); err = scsi_cmd_run(udev, &sc, fd, buffer, sizeof(buffer)); if ((err != 0)) { + cd_media = 0; info_scsi_cmd_err(udev, "READ FIRST 32 BLOCKS", err); return -1; } @@ -678,7 +731,9 @@ static int cd_media_info(struct udev *udev, int fd) } determined: - if ((header[2] & 3) != 2) + /* "other" is e. g. DVD-RAM, can't append sessions there; DVDs in + * restricted overwrite mode can never append, only in sequential mode */ + if ((header[2] & 3) < 2 && !cd_media_dvd_rw_ro) cd_media_session_next = header[10] << 8 | header[5]; cd_media_session_count = header[9] << 8 | header[4]; cd_media_track_count = header[11] << 8 | header[6]; @@ -690,8 +745,8 @@ static int cd_media_toc(struct udev *udev, int fd) { struct scsi_cmd sc; unsigned char header[12]; - unsigned char toc[2048]; - unsigned int len, i; + unsigned char toc[65536]; + unsigned int len, i, num_tracks; unsigned char *p; int err; @@ -707,11 +762,13 @@ static int cd_media_toc(struct udev *udev, int fd) } len = (header[0] << 8 | header[1]) + 2; - info(udev, "READ TOC: len: %d\n", len); + info(udev, "READ TOC: len: %d, start track: %d, end track: %d\n", len, header[2], header[3]); if (len > sizeof(toc)) return -1; if (len < 2) return -1; + /* 2: first track, 3: last track */ + num_tracks = header[3] - header[2] + 1; /* empty media has no tracks */ if (len < 8) @@ -729,7 +786,10 @@ static int cd_media_toc(struct udev *udev, int fd) return -1; } - for (p = toc+4, i = 4; i < len-8; i += 8, p += 8) { + /* Take care to not iterate beyond the last valid track as specified in + * the TOC, but also avoid going beyond the TOC length, just in case + * the last track number is invalidly large */ + for (p = toc+4, i = 4; i < len-8 && num_tracks > 0; i += 8, p += 8, --num_tracks) { unsigned int block; unsigned int is_data_track; @@ -765,13 +825,11 @@ int main(int argc, char *argv[]) { struct udev *udev; static const struct option options[] = { - { "export", no_argument, NULL, 'x' }, { "debug", no_argument, NULL, 'd' }, { "help", no_argument, NULL, 'h' }, {} }; const char *node = NULL; - int export = 0; int fd = -1; int cnt; int rc = 0; @@ -786,7 +844,7 @@ int main(int argc, char *argv[]) while (1) { int option; - option = getopt_long(argc, argv, "dxh", options, NULL); + option = getopt_long(argc, argv, "dh", options, NULL); if (option == -1) break; @@ -796,12 +854,8 @@ int main(int argc, char *argv[]) if (udev_get_log_priority(udev) < LOG_INFO) udev_set_log_priority(udev, LOG_INFO); break; - case 'x': - export = 1; - break; case 'h': printf("Usage: cdrom_id [options] \n" - " --export export key/value pairs\n" " --debug debug to stderr\n" " --help print this help text\n\n"); goto exit; @@ -853,16 +907,17 @@ int main(int argc, char *argv[]) goto print; /* read drive and possibly current profile */ - if (cd_profiles(udev, fd) < 0) + if (cd_profiles(udev, fd) != 0) goto print; - /* get writable media state */ - if (cd_media_info(udev, fd) < 0) - goto print; + /* at this point we are guaranteed to have media in the + * drive - find out more about it */ /* get session/track info */ - if (cd_media_toc(udev, fd) < 0) - goto print; + cd_media_toc(udev, fd); + + /* get writable media state */ + cd_media_info(udev, fd); print: printf("ID_CDROM=1\n");