1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 /*
3 * cdrom_id - optical drive and media information prober
4 */
5
6 #include <fcntl.h>
7 #include <getopt.h>
8 #include <linux/cdrom.h>
9 #include <scsi/sg.h>
10 #include <sys/ioctl.h>
11 #include <unistd.h>
12
13 #include "fd-util.h"
14 #include "main-func.h"
15 #include "memory-util.h"
16 #include "random-util.h"
17 #include "sort-util.h"
18 #include "string-table.h"
19 #include "string-util.h"
20 #include "udev-util.h"
21 #include "unaligned.h"
22
23 static bool arg_eject = false;
24 static bool arg_lock = false;
25 static bool arg_unlock = false;
26 static const char *arg_node = NULL;
27
28 typedef enum Feature {
29 FEATURE_RW_NONREMOVABLE = 0x01,
30 FEATURE_RW_REMOVABLE = 0x02,
31
32 FEATURE_MO_SE = 0x03, /* sector erase */
33 FEATURE_MO_WO = 0x04, /* write once */
34 FEATURE_MO_AS = 0x05, /* advance storage */
35
36 FEATURE_CD_ROM = 0x08,
37 FEATURE_CD_R = 0x09,
38 FEATURE_CD_RW = 0x0a,
39
40 FEATURE_DVD_ROM = 0x10,
41 FEATURE_DVD_R = 0x11,
42 FEATURE_DVD_RAM = 0x12,
43 FEATURE_DVD_RW_RO = 0x13, /* restricted overwrite mode */
44 FEATURE_DVD_RW_SEQ = 0x14, /* sequential mode */
45 FEATURE_DVD_R_DL_SEQ = 0x15, /* sequential recording */
46 FEATURE_DVD_R_DL_JR = 0x16, /* jump recording */
47 FEATURE_DVD_RW_DL = 0x17,
48 FEATURE_DVD_R_DDR = 0x18, /* download disc recording - dvd for css managed recording */
49 FEATURE_DVD_PLUS_RW = 0x1a,
50 FEATURE_DVD_PLUS_R = 0x1b,
51
52 FEATURE_DDCD_ROM = 0x20,
53 FEATURE_DDCD_R = 0x21,
54 FEATURE_DDCD_RW = 0x22,
55
56 FEATURE_DVD_PLUS_RW_DL = 0x2a,
57 FEATURE_DVD_PLUS_R_DL = 0x2b,
58
59 FEATURE_BD = 0x40,
60 FEATURE_BD_R_SRM = 0x41, /* sequential recording mode */
61 FEATURE_BD_R_RRM = 0x42, /* random recording mode */
62 FEATURE_BD_RE = 0x43,
63
64 FEATURE_HDDVD = 0x50,
65 FEATURE_HDDVD_R = 0x51,
66 FEATURE_HDDVD_RAM = 0x52,
67 FEATURE_HDDVD_RW = 0x53,
68 FEATURE_HDDVD_R_DL = 0x58,
69 FEATURE_HDDVD_RW_DL = 0x5a,
70
71 FEATURE_MRW,
72 FEATURE_MRW_W,
73
74 _FEATURE_MAX,
75 _FEATURE_INVALID = -EINVAL,
76 } Feature;
77
78 typedef enum MediaState {
79 MEDIA_STATE_BLANK = 0,
80 MEDIA_STATE_APPENDABLE = 1,
81 MEDIA_STATE_COMPLETE = 2,
82 MEDIA_STATE_OTHER = 3,
83 _MEDIA_STATE_MAX,
84 _MEDIA_STATE_INVALID = -EINVAL,
85 } MediaState;
86
87 typedef struct Context {
88 int fd;
89
90 Feature *drive_features;
91 size_t n_drive_feature;
92
93 Feature media_feature;
94 bool has_media;
95
96 MediaState media_state;
97 unsigned media_session_next;
98 unsigned media_session_count;
99 unsigned media_track_count;
100 unsigned media_track_count_data;
101 unsigned media_track_count_audio;
102 uint64_t media_session_last_offset;
103 } Context;
104
105 #define CONTEXT_EMPTY { \
106 .fd = -1, \
107 .media_feature = _FEATURE_INVALID, \
108 .media_state = _MEDIA_STATE_INVALID, \
109 }
110
context_clear(Context * c)111 static void context_clear(Context *c) {
112 if (!c)
113 return;
114
115 safe_close(c->fd);
116 free(c->drive_features);
117 }
118
drive_has_feature(const Context * c,Feature f)119 static bool drive_has_feature(const Context *c, Feature f) {
120 assert(c);
121
122 for (size_t i = 0; i < c->n_drive_feature; i++)
123 if (c->drive_features[i] == f)
124 return true;
125
126 return false;
127 }
128
set_drive_feature(Context * c,Feature f)129 static int set_drive_feature(Context *c, Feature f) {
130 assert(c);
131
132 if (drive_has_feature(c, f))
133 return 0;
134
135 if (!GREEDY_REALLOC(c->drive_features, c->n_drive_feature + 1))
136 return -ENOMEM;
137
138 c->drive_features[c->n_drive_feature++] = f;
139 return 1;
140 }
141
142 #define ERRCODE(s) ((((s)[2] & 0x0F) << 16) | ((s)[12] << 8) | ((s)[13]))
143 #define SK(errcode) (((errcode) >> 16) & 0xF)
144 #define ASC(errcode) (((errcode) >> 8) & 0xFF)
145 #define ASCQ(errcode) ((errcode) & 0xFF)
146 #define CHECK_CONDITION 0x01
147
log_scsi_debug_errno(int error,const char * msg)148 static int log_scsi_debug_errno(int error, const char *msg) {
149 assert(error != 0);
150
151 /* error < 0 means errno-style error, error > 0 means SCSI error */
152
153 if (error < 0)
154 return log_debug_errno(error, "Failed to %s: %m", msg);
155
156 return log_debug_errno(SYNTHETIC_ERRNO(EIO),
157 "Failed to %s with SK=%X/ASC=%02X/ACQ=%02X",
158 msg, SK(error), ASC(error), ASCQ(error));
159 }
160
161 struct scsi_cmd {
162 struct cdrom_generic_command cgc;
163 union {
164 struct request_sense s;
165 unsigned char u[18];
166 } _sense;
167 struct sg_io_hdr sg_io;
168 };
169
scsi_cmd_init(struct scsi_cmd * cmd)170 static void scsi_cmd_init(struct scsi_cmd *cmd) {
171 memzero(cmd, sizeof(struct scsi_cmd));
172 cmd->cgc.quiet = 1;
173 cmd->cgc.sense = &cmd->_sense.s;
174 cmd->sg_io.interface_id = 'S';
175 cmd->sg_io.mx_sb_len = sizeof(cmd->_sense);
176 cmd->sg_io.cmdp = cmd->cgc.cmd;
177 cmd->sg_io.sbp = cmd->_sense.u;
178 cmd->sg_io.flags = SG_FLAG_LUN_INHIBIT | SG_FLAG_DIRECT_IO;
179 }
180
scsi_cmd_set(struct scsi_cmd * cmd,size_t i,unsigned char arg)181 static void scsi_cmd_set(struct scsi_cmd *cmd, size_t i, unsigned char arg) {
182 cmd->sg_io.cmd_len = i + 1;
183 cmd->cgc.cmd[i] = arg;
184 }
185
scsi_cmd_run(struct scsi_cmd * cmd,int fd,unsigned char * buf,size_t bufsize)186 static int scsi_cmd_run(struct scsi_cmd *cmd, int fd, unsigned char *buf, size_t bufsize) {
187 int r;
188
189 assert(cmd);
190 assert(fd >= 0);
191 assert(buf || bufsize == 0);
192
193 /* Return 0 on success. On failure, return negative errno or positive error code. */
194
195 if (bufsize > 0) {
196 cmd->sg_io.dxferp = buf;
197 cmd->sg_io.dxfer_len = bufsize;
198 cmd->sg_io.dxfer_direction = SG_DXFER_FROM_DEV;
199 } else
200 cmd->sg_io.dxfer_direction = SG_DXFER_NONE;
201
202 if (ioctl(fd, SG_IO, &cmd->sg_io) < 0)
203 return -errno;
204
205 if ((cmd->sg_io.info & SG_INFO_OK_MASK) != SG_INFO_OK) {
206 if (cmd->sg_io.masked_status & CHECK_CONDITION) {
207 r = ERRCODE(cmd->_sense.u);
208 if (r != 0)
209 return r;
210 }
211 return -EIO;
212 }
213
214 return 0;
215 }
216
scsi_cmd_run_and_log(struct scsi_cmd * cmd,int fd,unsigned char * buf,size_t bufsize,const char * msg)217 static int scsi_cmd_run_and_log(struct scsi_cmd *cmd, int fd, unsigned char *buf, size_t bufsize, const char *msg) {
218 int r;
219
220 assert(msg);
221
222 r = scsi_cmd_run(cmd, fd, buf, bufsize);
223 if (r != 0)
224 return log_scsi_debug_errno(r, msg);
225
226 return 0;
227 }
228
media_lock(int fd,bool lock)229 static int media_lock(int fd, bool lock) {
230 /* disable the kernel's lock logic */
231 if (ioctl(fd, CDROM_CLEAR_OPTIONS, CDO_LOCK) < 0)
232 log_debug_errno(errno, "Failed to issue ioctl(CDROM_CLEAR_OPTIONS, CDO_LOCK), ignoring: %m");
233
234 if (ioctl(fd, CDROM_LOCKDOOR, lock ? 1 : 0) < 0)
235 return log_debug_errno(errno, "Failed to issue ioctl(CDROM_LOCKDOOR): %m");
236
237 return 0;
238 }
239
media_eject(int fd)240 static int media_eject(int fd) {
241 struct scsi_cmd sc;
242
243 scsi_cmd_init(&sc);
244 scsi_cmd_set(&sc, 0, GPCMD_START_STOP_UNIT);
245 scsi_cmd_set(&sc, 4, 0x02);
246 scsi_cmd_set(&sc, 5, 0);
247
248 return scsi_cmd_run_and_log(&sc, fd, NULL, 0, "start/stop unit");
249 }
250
cd_capability_compat(Context * c)251 static int cd_capability_compat(Context *c) {
252 int capability, r;
253
254 assert(c);
255
256 capability = ioctl(c->fd, CDROM_GET_CAPABILITY, NULL);
257 if (capability < 0)
258 return log_debug_errno(errno, "CDROM_GET_CAPABILITY failed");
259
260 if (capability & CDC_CD_R) {
261 r = set_drive_feature(c, FEATURE_CD_R);
262 if (r < 0)
263 return log_oom_debug();
264 }
265 if (capability & CDC_CD_RW) {
266 r = set_drive_feature(c, FEATURE_CD_RW);
267 if (r < 0)
268 return log_oom_debug();
269 }
270 if (capability & CDC_DVD) {
271 r = set_drive_feature(c, FEATURE_DVD_ROM);
272 if (r < 0)
273 return log_oom_debug();
274 }
275 if (capability & CDC_DVD_R) {
276 r = set_drive_feature(c, FEATURE_DVD_R);
277 if (r < 0)
278 return log_oom_debug();
279 }
280 if (capability & CDC_DVD_RAM) {
281 r = set_drive_feature(c, FEATURE_DVD_RAM);
282 if (r < 0)
283 return log_oom_debug();
284 }
285 if (capability & CDC_MRW) {
286 r = set_drive_feature(c, FEATURE_MRW);
287 if (r < 0)
288 return log_oom_debug();
289 }
290 if (capability & CDC_MRW_W) {
291 r = set_drive_feature(c, FEATURE_MRW_W);
292 if (r < 0)
293 return log_oom_debug();
294 }
295
296 return 0;
297 }
298
cd_media_compat(Context * c)299 static int cd_media_compat(Context *c) {
300 int r;
301
302 assert(c);
303
304 r = ioctl(c->fd, CDROM_DRIVE_STATUS, CDSL_CURRENT);
305 if (r < 0)
306 return log_debug_errno(errno, "ioctl(CDROM_DRIVE_STATUS) failed: %m");
307 if (r != CDS_DISC_OK)
308 return log_debug_errno(SYNTHETIC_ERRNO(ENOMEDIUM),
309 "ioctl(CDROM_DRIVE_STATUS) → %d (%s), ignoring.",
310 r,
311 r == CDS_NO_INFO ? "no info" :
312 r == CDS_NO_DISC ? "no disc" :
313 r == CDS_TRAY_OPEN ? "tray open" :
314 r == CDS_DRIVE_NOT_READY ? "drive not ready" :
315 "unknown status");
316
317 c->has_media = true;
318 return 0;
319 }
320
cd_inquiry(Context * c)321 static int cd_inquiry(Context *c) {
322 struct scsi_cmd sc;
323 unsigned char inq[36];
324 int r;
325
326 assert(c);
327
328 scsi_cmd_init(&sc);
329 scsi_cmd_set(&sc, 0, GPCMD_INQUIRY);
330 scsi_cmd_set(&sc, 4, sizeof(inq));
331 scsi_cmd_set(&sc, 5, 0);
332 r = scsi_cmd_run_and_log(&sc, c->fd, inq, sizeof(inq), "inquire");
333 if (r < 0)
334 return r;
335
336 if ((inq[0] & 0x1F) != 5)
337 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Not an MMC unit.");
338
339 log_debug("INQUIRY: [%.8s][%.16s][%.4s]", inq + 8, inq + 16, inq + 32);
340 return 0;
341 }
342
feature_profiles(Context * c,const unsigned char * profiles,size_t size)343 static int feature_profiles(Context *c, const unsigned char *profiles, size_t size) {
344 int r;
345
346 assert(c);
347
348 for (size_t i = 0; i + 4 <= size; i += 4) {
349 r = set_drive_feature(c, (Feature) unaligned_read_be16(&profiles[i]));
350 if (r < 0)
351 return log_oom_debug();
352 }
353
354 return 1;
355 }
356
cd_profiles_old_mmc(Context * c)357 static int cd_profiles_old_mmc(Context *c) {
358 disc_information discinfo;
359 struct scsi_cmd sc;
360 size_t len;
361 int r;
362
363 assert(c);
364
365 scsi_cmd_init(&sc);
366 scsi_cmd_set(&sc, 0, GPCMD_READ_DISC_INFO);
367 scsi_cmd_set(&sc, 8, sizeof(discinfo.disc_information_length));
368 scsi_cmd_set(&sc, 9, 0);
369 r = scsi_cmd_run_and_log(&sc, c->fd, (unsigned char *)&discinfo.disc_information_length, sizeof(discinfo.disc_information_length), "read disc information");
370 if (r >= 0) {
371 /* Not all drives have the same disc_info length, so requeue
372 * packet with the length the drive tells us it can supply */
373 len = be16toh(discinfo.disc_information_length) + sizeof(discinfo.disc_information_length);
374 if (len > sizeof(discinfo))
375 len = sizeof(discinfo);
376
377 scsi_cmd_init(&sc);
378 scsi_cmd_set(&sc, 0, GPCMD_READ_DISC_INFO);
379 scsi_cmd_set(&sc, 8, len);
380 scsi_cmd_set(&sc, 9, 0);
381 r = scsi_cmd_run_and_log(&sc, c->fd, (unsigned char *)&discinfo, len, "read disc information");
382 }
383 if (r < 0) {
384 if (c->has_media) {
385 log_debug("No current profile, but disc is present; assuming CD-ROM.");
386 c->media_feature = FEATURE_CD_ROM;
387 c->media_track_count = 1;
388 c->media_track_count_data = 1;
389 return 1;
390 } else
391 return log_debug_errno(SYNTHETIC_ERRNO(ENOMEDIUM),
392 "no current profile, assuming no media.");
393 };
394
395 c->has_media = true;
396
397 if (discinfo.erasable)
398 c->media_feature = FEATURE_CD_RW;
399 else if (discinfo.disc_status < 2 && drive_has_feature(c, FEATURE_CD_R))
400 c->media_feature = FEATURE_CD_R;
401 else
402 c->media_feature = FEATURE_CD_ROM;
403
404 return 0;
405 }
406
cd_profiles(Context * c)407 static int cd_profiles(Context *c) {
408 struct scsi_cmd sc;
409 unsigned char features[65530];
410 unsigned cur_profile;
411 size_t len;
412 int r;
413
414 assert(c);
415
416 /* First query the current profile */
417 scsi_cmd_init(&sc);
418 scsi_cmd_set(&sc, 0, GPCMD_GET_CONFIGURATION);
419 scsi_cmd_set(&sc, 8, 8);
420 scsi_cmd_set(&sc, 9, 0);
421 r = scsi_cmd_run(&sc, c->fd, features, 8);
422 if (r != 0) {
423 /* handle pre-MMC2 drives which do not support GET CONFIGURATION */
424 if (r > 0 && SK(r) == 0x5 && IN_SET(ASC(r), 0x20, 0x24)) {
425 log_debug("Drive is pre-MMC2 and does not support 46h get configuration command; "
426 "trying to work around the problem.");
427 return cd_profiles_old_mmc(c);
428 }
429
430 return log_scsi_debug_errno(r, "get configuration");
431 }
432
433 cur_profile = unaligned_read_be16(&features[6]);
434 if (cur_profile > 0) {
435 log_debug("current profile 0x%02x", cur_profile);
436 c->media_feature = (Feature) cur_profile;
437 c->has_media = true;
438 } else {
439 log_debug("no current profile, assuming no media");
440 c->has_media = false;
441 }
442
443 len = unaligned_read_be32(features);
444 log_debug("GET CONFIGURATION: size of features buffer %zu", len);
445
446 if (len > sizeof(features)) {
447 log_debug("Cannot get features in a single query, truncating.");
448 len = sizeof(features);
449 } else if (len <= 8)
450 len = sizeof(features);
451
452 /* Now get the full feature buffer */
453 scsi_cmd_init(&sc);
454 scsi_cmd_set(&sc, 0, GPCMD_GET_CONFIGURATION);
455 scsi_cmd_set(&sc, 7, (len >> 8) & 0xff);
456 scsi_cmd_set(&sc, 8, len & 0xff);
457 scsi_cmd_set(&sc, 9, 0);
458 r = scsi_cmd_run_and_log(&sc, c->fd, features, len, "get configuration");
459 if (r < 0)
460 return r;
461
462 /* parse the length once more, in case the drive decided to have other features suddenly :) */
463 len = unaligned_read_be32(features);
464 log_debug("GET CONFIGURATION: size of features buffer %zu", len);
465
466 if (len > sizeof(features)) {
467 log_debug("Cannot get features in a single query, truncating.");
468 len = sizeof(features);
469 }
470
471 /* device features */
472 for (size_t i = 8; i + 4 < len; i += 4 + features[i + 3]) {
473 unsigned feature;
474
475 feature = unaligned_read_be16(&features[i]);
476
477 switch (feature) {
478 case 0x00:
479 log_debug("GET CONFIGURATION: feature 'profiles', with %u entries", features[i + 3] / 4);
480 feature_profiles(c, features + i + 4, MIN(features[i + 3], len - i - 4));
481 break;
482 default:
483 log_debug("GET CONFIGURATION: feature 0x%04x <ignored>, with 0x%02x bytes", feature, features[i + 3]);
484 break;
485 }
486 }
487
488 return c->has_media;
489 }
490
491 static const char * const media_state_table[_MEDIA_STATE_MAX] = {
492 [MEDIA_STATE_BLANK] = "blank",
493 [MEDIA_STATE_APPENDABLE] = "appendable",
494 [MEDIA_STATE_COMPLETE] = "complete",
495 [MEDIA_STATE_OTHER] = "other",
496 };
497
498 DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(media_state, MediaState);
499
dvd_ram_media_update_state(Context * c)500 static int dvd_ram_media_update_state(Context *c) {
501 struct scsi_cmd sc;
502 unsigned char dvdstruct[8];
503 unsigned char format[12];
504 unsigned char len;
505 int r;
506
507 assert(c);
508
509 /* Return 1 if media state is determined. */
510
511 if (c->media_feature != FEATURE_DVD_RAM)
512 return 0;
513
514 /* a write protected dvd-ram may report "complete" status */
515 scsi_cmd_init(&sc);
516 scsi_cmd_set(&sc, 0, GPCMD_READ_DVD_STRUCTURE);
517 scsi_cmd_set(&sc, 7, 0xC0);
518 scsi_cmd_set(&sc, 9, sizeof(dvdstruct));
519 scsi_cmd_set(&sc, 11, 0);
520 r = scsi_cmd_run_and_log(&sc, c->fd, dvdstruct, sizeof(dvdstruct), "read DVD structure");
521 if (r < 0)
522 return r;
523
524 if (dvdstruct[4] & 0x02) {
525 c->media_state = MEDIA_STATE_COMPLETE;
526 log_debug("Write-protected DVD-RAM media inserted");
527 return 1;
528 }
529
530 /* let's make sure we don't try to read unformatted media */
531 scsi_cmd_init(&sc);
532 scsi_cmd_set(&sc, 0, GPCMD_READ_FORMAT_CAPACITIES);
533 scsi_cmd_set(&sc, 8, sizeof(format));
534 scsi_cmd_set(&sc, 9, 0);
535 r = scsi_cmd_run_and_log(&sc, c->fd, format, sizeof(format), "read DVD format capacities");
536 if (r < 0)
537 return r;
538
539 len = format[3];
540 if (len & 7 || len < 16)
541 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
542 "Invalid format capacities length.");
543
544 switch (format[8] & 3) {
545 case 1:
546 /* This means that last format was interrupted or failed, blank dvd-ram discs are
547 * factory formatted. Take no action here as it takes quite a while to reformat a
548 * dvd-ram and it's not automatically started. */
549 log_debug("Unformatted DVD-RAM media inserted.");
550 return 1;
551
552 case 2:
553 log_debug("Formatted DVD-RAM media inserted.");
554 return 0;
555
556 case 3:
557 c->has_media = false;
558 return log_debug_errno(SYNTHETIC_ERRNO(ENOMEDIUM),
559 "Format capacities returned no media.");
560 }
561
562 return 0;
563 }
564
dvd_media_update_state(Context * c)565 static int dvd_media_update_state(Context *c) {
566 struct scsi_cmd sc;
567 unsigned char buffer[32 * 2048];
568 int r;
569
570 r = dvd_ram_media_update_state(c);
571 if (r != 0)
572 return r;
573
574 /* Take a closer look at formatted media (unformatted DVD+RW
575 * has "blank" status", DVD-RAM was examined earlier) and check
576 * for ISO and UDF PVDs or a fs superblock presence and do it
577 * in one ioctl (we need just sectors 0 and 16) */
578
579 scsi_cmd_init(&sc);
580 scsi_cmd_set(&sc, 0, GPCMD_READ_10);
581 scsi_cmd_set(&sc, 5, 0);
582 scsi_cmd_set(&sc, 8, sizeof(buffer)/2048);
583 scsi_cmd_set(&sc, 9, 0);
584 r = scsi_cmd_run_and_log(&sc, c->fd, buffer, sizeof(buffer), "read first 32 blocks");
585 if (r < 0) {
586 c->has_media = false;
587 return r;
588 }
589
590 /* if any non-zero data is found in sector 16 (iso and udf) or
591 * eventually 0 (fat32 boot sector, ext2 superblock, etc), disc
592 * is assumed non-blank */
593
594 for (size_t offset = 32768; offset < 32768 + 2048; offset++)
595 if (buffer[offset] != 0) {
596 log_debug("Data in block 16, assuming complete.");
597 return 0;
598 }
599
600 for (size_t offset = 0; offset < 2048; offset++)
601 if (buffer[offset] != 0) {
602 log_debug("Data in block 0, assuming complete.");
603 return 0;
604 }
605
606 log_debug("No data in blocks 0 or 16, assuming blank.");
607 c->media_state = MEDIA_STATE_BLANK;
608 return 0;
609 }
610
cd_media_info(Context * c)611 static int cd_media_info(Context *c) {
612 struct scsi_cmd sc;
613 unsigned char header[32];
614 MediaState state;
615 int r;
616
617 assert(c);
618
619 scsi_cmd_init(&sc);
620 scsi_cmd_set(&sc, 0, GPCMD_READ_DISC_INFO);
621 scsi_cmd_set(&sc, 8, sizeof(header));
622 scsi_cmd_set(&sc, 9, 0);
623 r = scsi_cmd_run_and_log(&sc, c->fd, header, sizeof(header), "read disc information");
624 if (r < 0)
625 return r;
626
627 c->has_media = true;
628 log_debug("disk type %02x", header[8]);
629
630 state = (MediaState) (header[2] & 0x03);
631 log_debug("hardware reported media status: %s", strna(media_state_to_string(state)));
632
633 /* exclude plain CDROM, some fake cdroms return 0 for "blank" media here */
634 if (c->media_feature != FEATURE_CD_ROM)
635 c->media_state = state;
636
637 /* fresh DVD-RW in restricted overwrite mode reports itself as
638 * "appendable"; change it to "blank" to make it consistent with what
639 * gets reported after blanking, and what userspace expects. */
640 if (c->media_feature == FEATURE_DVD_RW_RO && state == MEDIA_STATE_APPENDABLE)
641 c->media_state = MEDIA_STATE_BLANK;
642
643 /* DVD+RW discs (and DVD-RW in restricted mode) once formatted are
644 * always "complete", DVD-RAM are "other" or "complete" if the disc is
645 * write protected; we need to check the contents if it is blank */
646 if (IN_SET(c->media_feature, FEATURE_DVD_RW_RO, FEATURE_DVD_PLUS_RW, FEATURE_DVD_PLUS_RW_DL, FEATURE_DVD_RAM) &&
647 IN_SET(state, MEDIA_STATE_COMPLETE, MEDIA_STATE_OTHER)) {
648 r = dvd_media_update_state(c);
649 if (r < 0)
650 return r;
651 }
652
653 /* "other" is e. g. DVD-RAM, can't append sessions there; DVDs in
654 * restricted overwrite mode can never append, only in sequential mode */
655 if (c->media_feature != FEATURE_DVD_RW_RO && IN_SET(state, MEDIA_STATE_BLANK, MEDIA_STATE_APPENDABLE))
656 c->media_session_next = header[10] << 8 | header[5];
657 c->media_session_count = header[9] << 8 | header[4];
658 c->media_track_count = header[11] << 8 | header[6];
659
660 return 0;
661 }
662
cd_media_toc(Context * c)663 static int cd_media_toc(Context *c) {
664 struct scsi_cmd sc;
665 unsigned char header[12];
666 unsigned char toc[65536];
667 unsigned num_tracks;
668 size_t len;
669 int r;
670
671 assert(c);
672
673 scsi_cmd_init(&sc);
674 scsi_cmd_set(&sc, 0, GPCMD_READ_TOC_PMA_ATIP);
675 scsi_cmd_set(&sc, 6, 1);
676 scsi_cmd_set(&sc, 8, sizeof(header));
677 scsi_cmd_set(&sc, 9, 0);
678 r = scsi_cmd_run_and_log(&sc, c->fd, header, sizeof(header), "read TOC");
679 if (r < 0)
680 return r;
681
682 len = unaligned_read_be16(header) + 2;
683 log_debug("READ TOC: len: %zu, start track: %u, end track: %u", len, header[2], header[3]);
684
685 if (len > sizeof(toc))
686 return -1;
687 /* empty media has no tracks */
688 if (len < 8)
689 return 0;
690
691 /* 2: first track, 3: last track */
692 num_tracks = header[3] - header[2] + 1;
693
694 scsi_cmd_init(&sc);
695 scsi_cmd_set(&sc, 0, GPCMD_READ_TOC_PMA_ATIP);
696 scsi_cmd_set(&sc, 6, header[2]); /* First Track/Session Number */
697 scsi_cmd_set(&sc, 7, (len >> 8) & 0xff);
698 scsi_cmd_set(&sc, 8, len & 0xff);
699 scsi_cmd_set(&sc, 9, 0);
700 r = scsi_cmd_run_and_log(&sc, c->fd, toc, len, "read TOC (tracks)");
701 if (r < 0)
702 return r;
703
704 /* Take care to not iterate beyond the last valid track as specified in
705 * the TOC, but also avoid going beyond the TOC length, just in case
706 * the last track number is invalidly large */
707 for (size_t i = 4; i + 8 < len && num_tracks > 0; i += 8, --num_tracks) {
708 bool is_data_track;
709 uint32_t block;
710
711 is_data_track = (toc[i + 1] & 0x04) != 0;
712 block = unaligned_read_be32(&toc[i + 4]);
713
714 log_debug("track=%u info=0x%x(%s) start_block=%"PRIu32,
715 toc[i + 2], toc[i + 1] & 0x0f, is_data_track ? "data":"audio", block);
716
717 if (is_data_track)
718 c->media_track_count_data++;
719 else
720 c->media_track_count_audio++;
721 }
722
723 scsi_cmd_init(&sc);
724 scsi_cmd_set(&sc, 0, GPCMD_READ_TOC_PMA_ATIP);
725 scsi_cmd_set(&sc, 2, 1); /* Session Info */
726 scsi_cmd_set(&sc, 8, sizeof(header));
727 scsi_cmd_set(&sc, 9, 0);
728 r = scsi_cmd_run_and_log(&sc, c->fd, header, sizeof(header), "read TOC (multi session)");
729 if (r < 0)
730 return r;
731
732 len = unaligned_read_be32(&header[8]);
733 log_debug("last track %u starts at block %zu", header[4+2], len);
734 c->media_session_last_offset = (uint64_t) len * 2048;
735
736 return 0;
737 }
738
open_drive(Context * c)739 static int open_drive(Context *c) {
740 int fd;
741
742 assert(c);
743 assert(c->fd < 0);
744
745 for (int cnt = 0;; cnt++) {
746 fd = open(arg_node, O_RDONLY|O_NONBLOCK|O_CLOEXEC);
747 if (fd >= 0)
748 break;
749 if (++cnt >= 20 || errno != EBUSY)
750 return log_debug_errno(errno, "Unable to open '%s': %m", arg_node);
751
752 (void) usleep(100 * USEC_PER_MSEC + random_u64_range(100 * USEC_PER_MSEC));
753 }
754
755 log_debug("probing: '%s'", arg_node);
756 c->fd = fd;
757 return 0;
758 }
759
760 typedef struct FeatureToString {
761 Feature feature;
762 const char *str;
763 } FeatureToString;
764
765 static const FeatureToString feature_to_string[] = {
766 { .feature = FEATURE_RW_NONREMOVABLE, .str = "RW_NONREMOVABLE", },
767 { .feature = FEATURE_RW_REMOVABLE, .str = "RW_REMOVABLE", },
768
769 { .feature = FEATURE_MO_SE, .str = "MO_SE", },
770 { .feature = FEATURE_MO_WO, .str = "MO_WO", },
771 { .feature = FEATURE_MO_AS, .str = "MO_AS", },
772
773 { .feature = FEATURE_CD_ROM, .str = "CD", },
774 { .feature = FEATURE_CD_R, .str = "CD_R", },
775 { .feature = FEATURE_CD_RW, .str = "CD_RW", },
776
777 { .feature = FEATURE_DVD_ROM, .str = "DVD", },
778 { .feature = FEATURE_DVD_R, .str = "DVD_R", },
779 { .feature = FEATURE_DVD_RAM, .str = "DVD_RAM", },
780 { .feature = FEATURE_DVD_RW_RO, .str = "DVD_RW_RO", },
781 { .feature = FEATURE_DVD_RW_SEQ, .str = "DVD_RW_SEQ", },
782 { .feature = FEATURE_DVD_R_DL_SEQ, .str = "DVD_R_DL_SEQ", },
783 { .feature = FEATURE_DVD_R_DL_JR, .str = "DVD_R_DL_JR", },
784 { .feature = FEATURE_DVD_RW_DL, .str = "DVD_RW_DL", },
785 { .feature = FEATURE_DVD_R_DDR, .str = "DVD_R_DDR", },
786 { .feature = FEATURE_DVD_PLUS_RW, .str = "DVD_PLUS_RW", },
787 { .feature = FEATURE_DVD_PLUS_R, .str = "DVD_PLUS_R", },
788
789 { .feature = FEATURE_DDCD_ROM, .str = "DDCD", },
790 { .feature = FEATURE_DDCD_R, .str = "DDCD_R", },
791 { .feature = FEATURE_DDCD_RW, .str = "DDCD_RW", },
792
793 { .feature = FEATURE_DVD_PLUS_RW_DL, .str = "DVD_PLUS_RW_DL", },
794 { .feature = FEATURE_DVD_PLUS_R_DL, .str = "DVD_PLUS_R_DL", },
795
796 { .feature = FEATURE_BD, .str = "BD", },
797 { .feature = FEATURE_BD_R_SRM, .str = "BD_R_SRM", },
798 { .feature = FEATURE_BD_R_RRM, .str = "BD_R_RRM", },
799 { .feature = FEATURE_BD_RE, .str = "BD_RE", },
800
801 { .feature = FEATURE_HDDVD, .str = "HDDVD", },
802 { .feature = FEATURE_HDDVD_R, .str = "HDDVD_R", },
803 { .feature = FEATURE_HDDVD_RAM, .str = "HDDVD_RAM", },
804 { .feature = FEATURE_HDDVD_RW, .str = "HDDVD_RW", },
805 { .feature = FEATURE_HDDVD_R_DL, .str = "HDDVD_R_DL", },
806 { .feature = FEATURE_HDDVD_RW_DL, .str = "HDDVD_RW_DL", },
807
808 { .feature = FEATURE_MRW, .str = "MRW", },
809 { .feature = FEATURE_MRW_W, .str = "MRW_W", },
810 };
811
feature_to_string_compare_func(const FeatureToString * a,const FeatureToString * b)812 static int feature_to_string_compare_func(const FeatureToString *a, const FeatureToString *b) {
813 assert(a);
814 assert(b);
815
816 return CMP(a->feature, b->feature);
817 }
818
print_feature(Feature feature,const char * prefix)819 static void print_feature(Feature feature, const char *prefix) {
820 FeatureToString *found, in = {
821 .feature = feature,
822 };
823
824 assert(prefix);
825
826 found = typesafe_bsearch(&in, feature_to_string, ELEMENTSOF(feature_to_string), feature_to_string_compare_func);
827 if (!found)
828 return (void) log_debug("Unknown feature 0x%02x, ignoring.", (unsigned) feature);
829
830 printf("%s_%s=1\n", prefix, found->str);
831 }
832
print_properties(const Context * c)833 static void print_properties(const Context *c) {
834 const char *state;
835
836 assert(c);
837
838 printf("ID_CDROM=1\n");
839 for (size_t i = 0; i < c->n_drive_feature; i++)
840 print_feature(c->drive_features[i], "ID_CDROM");
841
842 if (drive_has_feature(c, FEATURE_MO_SE) ||
843 drive_has_feature(c, FEATURE_MO_WO) ||
844 drive_has_feature(c, FEATURE_MO_AS))
845 printf("ID_CDROM_MO=1\n");
846
847 if (drive_has_feature(c, FEATURE_DVD_RW_RO) ||
848 drive_has_feature(c, FEATURE_DVD_RW_SEQ))
849 printf("ID_CDROM_DVD_RW=1\n");
850
851 if (drive_has_feature(c, FEATURE_DVD_R_DL_SEQ) ||
852 drive_has_feature(c, FEATURE_DVD_R_DL_JR))
853 printf("ID_CDROM_DVD_R_DL=1\n");
854
855 if (drive_has_feature(c, FEATURE_DVD_R_DDR))
856 printf("ID_CDROM_DVD_R=1\n");
857
858 if (drive_has_feature(c, FEATURE_BD_R_SRM) ||
859 drive_has_feature(c, FEATURE_BD_R_RRM))
860 printf("ID_CDROM_BD_R=1\n");
861
862 if (c->has_media) {
863 printf("ID_CDROM_MEDIA=1\n");
864 print_feature(c->media_feature, "ID_CDROM_MEDIA");
865
866 if (IN_SET(c->media_feature, FEATURE_MO_SE, FEATURE_MO_WO, FEATURE_MO_AS))
867 printf("ID_CDROM_MEDIA_MO=1\n");
868
869 if (IN_SET(c->media_feature, FEATURE_DVD_RW_RO, FEATURE_DVD_RW_SEQ))
870 printf("ID_CDROM_MEDIA_DVD_RW=1\n");
871
872 if (IN_SET(c->media_feature, FEATURE_DVD_R_DL_SEQ, FEATURE_DVD_R_DL_JR))
873 printf("ID_CDROM_MEDIA_DVD_R_DL=1\n");
874
875 if (c->media_feature == FEATURE_DVD_R_DDR)
876 printf("ID_CDROM_MEDIA_DVD_R=1\n");
877
878 if (IN_SET(c->media_feature, FEATURE_BD_R_SRM, FEATURE_BD_R_RRM))
879 printf("ID_CDROM_MEDIA_BD_R=1\n");
880 }
881
882 state = media_state_to_string(c->media_state);
883 if (state)
884 printf("ID_CDROM_MEDIA_STATE=%s\n", state);
885 if (c->media_session_next > 0)
886 printf("ID_CDROM_MEDIA_SESSION_NEXT=%u\n", c->media_session_next);
887 if (c->media_session_count > 0)
888 printf("ID_CDROM_MEDIA_SESSION_COUNT=%u\n", c->media_session_count);
889 if (c->media_session_count > 1 && c->media_session_last_offset > 0)
890 printf("ID_CDROM_MEDIA_SESSION_LAST_OFFSET=%" PRIu64 "\n", c->media_session_last_offset);
891 if (c->media_track_count > 0)
892 printf("ID_CDROM_MEDIA_TRACK_COUNT=%u\n", c->media_track_count);
893 if (c->media_track_count_audio > 0)
894 printf("ID_CDROM_MEDIA_TRACK_COUNT_AUDIO=%u\n", c->media_track_count_audio);
895 if (c->media_track_count_data > 0)
896 printf("ID_CDROM_MEDIA_TRACK_COUNT_DATA=%u\n", c->media_track_count_data);
897 }
898
help(void)899 static int help(void) {
900 printf("Usage: %s [options] <device>\n"
901 " -l --lock-media lock the media (to enable eject request events)\n"
902 " -u --unlock-media unlock the media\n"
903 " -e --eject-media eject the media\n"
904 " -d --debug print debug messages to stderr\n"
905 " -h --help print this help text\n"
906 "\n",
907 program_invocation_short_name);
908
909 return 0;
910 }
911
parse_argv(int argc,char * argv[])912 static int parse_argv(int argc, char *argv[]) {
913 static const struct option options[] = {
914 { "lock-media", no_argument, NULL, 'l' },
915 { "unlock-media", no_argument, NULL, 'u' },
916 { "eject-media", no_argument, NULL, 'e' },
917 { "debug", no_argument, NULL, 'd' },
918 { "help", no_argument, NULL, 'h' },
919 {}
920 };
921 int c;
922
923 while ((c = getopt_long(argc, argv, "deluh", options, NULL)) >= 0)
924 switch (c) {
925 case 'l':
926 arg_lock = true;
927 break;
928 case 'u':
929 arg_unlock = true;
930 break;
931 case 'e':
932 arg_eject = true;
933 break;
934 case 'd':
935 log_set_target(LOG_TARGET_CONSOLE);
936 log_set_max_level(LOG_DEBUG);
937 log_open();
938 break;
939 case 'h':
940 return help();
941 default:
942 assert_not_reached();
943 }
944
945 arg_node = argv[optind];
946 if (!arg_node)
947 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No device specified.");
948
949 return 1;
950 }
951
run(int argc,char * argv[])952 static int run(int argc, char *argv[]) {
953 _cleanup_(context_clear) Context c = CONTEXT_EMPTY;
954 int r;
955
956 log_set_target(LOG_TARGET_AUTO);
957 udev_parse_config();
958 log_parse_environment();
959 log_open();
960
961 r = parse_argv(argc, argv);
962 if (r <= 0)
963 return r;
964
965 r = open_drive(&c);
966 if (r < 0)
967 return r;
968
969 /* same data as original cdrom_id */
970 r = cd_capability_compat(&c);
971 if (r < 0)
972 return r;
973
974 /* check for media - don't bail if there's no media as we still need to
975 * to read profiles */
976 (void) cd_media_compat(&c);
977
978 /* check if drive talks MMC */
979 if (cd_inquiry(&c) < 0)
980 goto work;
981
982 r = cd_profiles(&c); /* read drive and possibly current profile */
983 if (r > 0) {
984 /* at this point we are guaranteed to have media in the drive - find out more about it */
985
986 /* get session/track info */
987 (void) cd_media_toc(&c);
988
989 /* get writable media state */
990 (void) cd_media_info(&c);
991 }
992
993 work:
994 /* lock the media, so we enable eject button events */
995 if (arg_lock && c.has_media) {
996 log_debug("PREVENT_ALLOW_MEDIUM_REMOVAL (lock)");
997 (void) media_lock(c.fd, true);
998 }
999
1000 if (arg_unlock && c.has_media) {
1001 log_debug("PREVENT_ALLOW_MEDIUM_REMOVAL (unlock)");
1002 (void) media_lock(c.fd, false);
1003 }
1004
1005 if (arg_eject) {
1006 log_debug("PREVENT_ALLOW_MEDIUM_REMOVAL (unlock)");
1007 (void) media_lock(c.fd, false);
1008 log_debug("START_STOP_UNIT (eject)");
1009 (void) media_eject(c.fd);
1010 }
1011
1012 print_properties(&c);
1013
1014 return 0;
1015 }
1016
1017 DEFINE_MAIN_FUNCTION(run);
1018