/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ /* ts A61010 */ /* #include */ #include #include #include #include #include #include #include /* #include ts A61013 : not in Linux man 3 malloc */ #include #include #include #include #include #include #include "transport.h" #include "drive.h" #include "sg.h" #include "spc.h" #include "mmc.h" #include "sbc.h" #include "debug.h" #include "toc.h" #include "util.h" /* kludge! glibc headers don't define all the SCSI stuff that we use! */ #ifndef SG_GET_ACCESS_COUNT # define SG_GET_ACCESS_COUNT 0x2289 #endif #include "libdax_msgs.h" extern struct libdax_msgs *libdax_messenger; static void enumerate_common(char *fname, int bus_no, int host_no, int channel_no, int target_no, int lun_no); /* ts A51221 */ int burn_drive_is_banned(char *device_address); /* ts A60813 : storage objects are in libburn/init.c wether to use O_EXCL wether to use O_NOBLOCK with open(2) on devices wether to take O_EXCL rejection as fatal error */ extern int burn_sg_open_o_excl; extern int burn_sg_open_o_nonblock; extern int burn_sg_open_abort_busy; /* ts A60821 <<< debug: for tracing calls which might use open drive fds */ int mmc_function_spy(char * text); static int sgio_test(int fd) { unsigned char test_ops[] = { 0, 0, 0, 0, 0, 0 }; sg_io_hdr_t s; memset(&s, 0, sizeof(sg_io_hdr_t)); s.interface_id = 'S'; s.dxfer_direction = SG_DXFER_NONE; s.cmd_len = 6; s.cmdp = test_ops; s.timeout = 12345; return ioctl(fd, SG_IO, &s); } /* ts A60925 : ticket 74 */ int sg_close_drive_fd(char *fname, int driveno, int *fd, int sorry) { int ret, os_errno, sevno= LIBDAX_MSGS_SEV_DEBUG; char msg[4096+100]; if(*fd < 0) return(0); ret = close(*fd); *fd = -1337; if(ret != -1) return 1; os_errno= errno; if (fname != NULL) sprintf(msg, "Encountered error when closing drive '%s'", fname); else sprintf(msg, "Encountered error when closing drive"); if (sorry) sevno = LIBDAX_MSGS_SEV_SORRY; libdax_msgs_submit(libdax_messenger, driveno, 0x00020002, sevno, LIBDAX_MSGS_PRIO_HIGH, msg, os_errno, 0); return 0; } int sg_drive_is_open(struct burn_drive * d) { /* a bit more detailed case distinction than needed */ if (d->fd == -1337) return 0; if (d->fd < 0) return 0; return 1; } /* ts A60924 */ int sg_handle_busy_device(char *fname, int os_errno) { char msg[4096]; /* ts A60814 : i saw no way to do this more nicely */ if (burn_sg_open_abort_busy) { fprintf(stderr, "\nlibburn: FATAL : Application triggered abort on busy device '%s'\n", fname); /* ts A61007 */ abort(); /* a ssert("drive busy" == "non fatal"); */ } /* ts A60924 : now reporting to libdax_msgs */ sprintf(msg, "Cannot open busy device '%s'", fname); libdax_msgs_submit(libdax_messenger, -1, 0x00020001, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_LOW, msg, os_errno, 0); return 1; } /* ts A60922 ticket 33 */ /** Returns the next index number and the next enumerated drive address. @param idx An opaque handle. Make no own theories about it. @param adr Takes the reply @param adr_size Gives maximum size of reply including final 0 @param initialize 1 = start new, 0 = continue, use no other values for now -1 = finish @return 1 = reply is a valid address , 0 = no further address available -1 = severe error (e.g. adr_size too small) */ int sg_give_next_adr(burn_drive_enumerator_t *idx, char adr[], int adr_size, int initialize) { /* sg.h : typedef int burn_drive_enumerator_t; */ static int sg_limit = 32, ata_limit = 26; int baseno = 0; if (initialize == -1) return 0; if (initialize == 1) *idx = -1; (*idx)++; if (*idx >= sg_limit) goto next_ata; if (adr_size < 10) return -1; sprintf(adr, "/dev/sg%d", *idx); return 1; next_ata:; baseno += sg_limit; if (*idx - baseno >= ata_limit) goto next_nothing; if (adr_size < 9) return -1; sprintf(adr, "/dev/hd%c", 'a' + (*idx - baseno)); return 1; next_nothing:; baseno += ata_limit; return 0; } int sg_is_enumerable_adr(char *adr) { char fname[4096]; int i, ret = 0, first = 1; while (1) { ret= sg_give_next_adr(&i, fname, sizeof(fname), first); if(ret <= 0) break; first = 0; if (strcmp(adr, fname) == 0) return 1; } return(0); } /* ts A60926 */ int sg_release_siblings(int sibling_fds[], int *sibling_count) { int i; char msg[81]; for(i= 0; i < *sibling_count; i++) sg_close_drive_fd(NULL, -1, &(sibling_fds[i]), 0); if(*sibling_count > 0) { sprintf(msg, "Closed %d O_EXCL scsi siblings", *sibling_count); libdax_msgs_submit(libdax_messenger, -1, 0x00020007, LIBDAX_MSGS_SEV_NOTE, LIBDAX_MSGS_PRIO_HIGH, msg, 0,0); } *sibling_count = 0; return 1; } /* ts A60926 */ int sg_open_drive_fd(char *fname, int scan_mode) { int open_mode = O_RDWR, fd; char msg[81]; /* ts A60813 - A60927 O_EXCL with devices is a non-POSIX feature of Linux kernels. Possibly introduced 2002. Mentioned in "The Linux SCSI Generic (sg) HOWTO" */ if(burn_sg_open_o_excl) open_mode |= O_EXCL; /* ts A60813 O_NONBLOCK was already hardcoded in ata_ but not in sg_. There must be some reason for this. So O_NONBLOCK is default mode for both now. Disable on own risk. */ if(burn_sg_open_o_nonblock) open_mode |= O_NONBLOCK; /* <<< debugging fprintf(stderr, "\nlibburn: experimental: o_excl= %d , o_nonblock= %d, abort_on_busy= %d\n", burn_sg_open_o_excl,burn_sg_open_o_nonblock,burn_sg_open_abort_busy); fprintf(stderr, "libburn: experimental: O_EXCL= %d , O_NONBLOCK= %d\n", !!(open_mode&O_EXCL),!!(open_mode&O_NONBLOCK)); */ fd = open(fname, open_mode); if (fd == -1) { /* <<< debugging fprintf(stderr, "\nlibburn: experimental: fname= %s , errno= %d\n", fname,errno); */ if (errno == EBUSY) { sg_handle_busy_device(fname, errno); return -1; } if (scan_mode) return -1; sprintf(msg, "Failed to open device '%s'",fname); libdax_msgs_submit(libdax_messenger, -1, 0x00020005, LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, msg, errno, 0); return -1; } return fd; } /* ts A60926 */ int sg_open_scsi_siblings(char *path, int driveno, int sibling_fds[], int *sibling_count, int host_no, int channel_no, int id_no, int lun_no) { int tld, i, ret, fd, i_bus_no = -1; int i_host_no = -1, i_channel_no = -1, i_target_no = -1, i_lun_no = -1; char msg[161], fname[81]; static char tldev[][81]= {"/dev/sr%d", "/dev/scd%d", "/dev/st%d", ""}; if(host_no < 0 || id_no < 0 || channel_no < 0 || lun_no < 0) return(2); if(*sibling_count > 0) sg_release_siblings(sibling_fds, sibling_count); for (tld = 0; tldev[tld][0] != 0; tld++) { for (i = 0; i < 32; i++) { sprintf(fname, tldev[tld], i); ret = sg_obtain_scsi_adr(fname, &i_bus_no, &i_host_no, &i_channel_no, &i_target_no, &i_lun_no); if (ret <= 0) continue; if (i_host_no != host_no || i_channel_no != channel_no) continue; if (i_target_no != id_no || i_lun_no != lun_no) continue; fd = sg_open_drive_fd(fname, 0); if (fd < 0) goto failed; if (*sibling_count>=LIBBURN_SG_MAX_SIBLINGS) { sprintf(msg, "Too many scsi siblings of '%s'", path); libdax_msgs_submit(libdax_messenger, driveno, 0x00020006, LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); goto failed; } sprintf(msg, "Opened O_EXCL scsi sibling '%s' of '%s'", fname, path); libdax_msgs_submit(libdax_messenger, driveno, 0x00020004, LIBDAX_MSGS_SEV_NOTE, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); sibling_fds[*sibling_count] = fd; (*sibling_count)++; } } return(1); failed:; sg_release_siblings(sibling_fds, sibling_count); return 0; } /* ts A60926 */ int sg_close_drive(struct burn_drive *d) { int ret; if (!burn_drive_is_open(d)) return 0; sg_release_siblings(d->sibling_fds, &(d->sibling_count)); ret = sg_close_drive_fd(d->devname, d->global_index, &(d->fd), 0); return ret; } void ata_enumerate(void) { struct hd_driveid tm; int i, fd; char fname[10]; for (i = 0; i < 26; i++) { sprintf(fname, "/dev/hd%c", 'a' + i); /* ts A51221 */ if (burn_drive_is_banned(fname)) continue; fd = sg_open_drive_fd(fname, 1); if (fd == -1) continue; /* found a drive */ ioctl(fd, HDIO_GET_IDENTITY, &tm); /* not atapi */ if (!(tm.config & 0x8000) || (tm.config & 0x4000)) { sg_close_drive_fd(fname, -1, &fd, 0); continue; } /* if SG_IO fails on an atapi device, we should stop trying to use hd* devices */ if (sgio_test(fd) == -1) { sg_close_drive_fd(fname, -1, &fd, 0); return; } if (sg_close_drive_fd(fname, -1, &fd, 1) <= 0) continue; enumerate_common(fname, -1, -1, -1, -1, -1); } } void sg_enumerate(void) { struct sg_scsi_id sid; int i, fd, sibling_fds[LIBBURN_SG_MAX_SIBLINGS], sibling_count= 0, ret; int bus_no = -1; char fname[10]; for (i = 0; i < 32; i++) { sprintf(fname, "/dev/sg%d", i); /* ts A51221 */ if (burn_drive_is_banned(fname)) continue; /* ts A60927 */ fd = sg_open_drive_fd(fname, 1); if (fd == -1) continue; /* found a drive */ ioctl(fd, SG_GET_SCSI_ID, &sid); #ifdef SCSI_IOCTL_GET_BUS_NUMBER /* Hearsay A61005 */ if (ioctl(fd, SCSI_IOCTL_GET_BUS_NUMBER, &bus_no) == -1) bus_no = -1; #endif if (sg_close_drive_fd(fname, -1, &fd, sid.scsi_type == TYPE_ROM ) <= 0) continue; if (sid.scsi_type != TYPE_ROM) continue; /* ts A60927 : trying to do locking with growisofs */ if(burn_sg_open_o_excl>1) { ret = sg_open_scsi_siblings( fname, -1, sibling_fds, &sibling_count, sid.host_no, sid.channel, sid.scsi_id, sid.lun); if (ret<=0) { sg_handle_busy_device(fname, 0); continue; } /* the final occupation will be done in sg_grab() */ sg_release_siblings(sibling_fds, &sibling_count); } #ifdef SCSI_IOCTL_GET_BUS_NUMBER if(bus_no == -1) bus_no = 1000 * (sid.host_no + 1) + sid.channel; #else bus_no = sid.host_no; #endif enumerate_common(fname, bus_no, sid.host_no, sid.channel, sid.scsi_id, sid.lun); } } /* ts A60923 - A61005 : introduced new SCSI parameters */ /* ts A61021 : moved non os-specific code to spc,sbc,mmc,drive */ static void enumerate_common(char *fname, int bus_no, int host_no, int channel_no, int target_no, int lun_no) { int ret, i; struct burn_drive out; /* General libburn drive setup */ burn_setup_drive(&out, fname); /* This transport adapter uses SCSI-family commands and models (seems the adapter would know better than its boss, if ever) */ ret = burn_scsi_setup_drive(&out, bus_no, host_no, channel_no, target_no, lun_no, 0); if (ret<=0) return; /* Operating system adapter is Linux Generic SCSI (sg) */ /* Adapter specific handles and data */ out.fd = -1337; out.sibling_count = 0; for(i= 0; ifd == -1337) { */ if(! burn_drive_is_open(d)) { /* ts A60821 <<< debug: for tracing calls which might use open drive fds */ mmc_function_spy("sg_grab ----------- opening"); /* ts A60926 */ if(burn_sg_open_o_excl>1) { fd = -1; ret = sg_open_scsi_siblings(d->devname, d->global_index,d->sibling_fds, &(d->sibling_count), d->host, d->channel, d->id, d->lun); if(ret <= 0) goto drive_is_in_use; } fd = open(d->devname, open_mode); os_errno = errno; } else fd= d->fd; /* ts A61007 : this is redundant */ /* a ssert(fd != -1337); */ if (fd >= 0) { /* ts A60814: according to my experiments this test would work now ! */ /* ts A60926 : this was disabled */ /* Tests with growisofs on kernel 2.4.21 yielded that this does not help against blocking on busy drives. */ /* <<< the old dummy */ /* er = ioctl(fd, SG_GET_ACCESS_COUNT, &count);*/ count = 1; if (1 == count) { d->fd = fd; fcntl(fd, F_SETOWN, getpid()); d->released = 0; return 1; } drive_is_in_use:; libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020003, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, "Could not grab drive - already in use", 0, 0); sg_close_drive(d); d->fd = -1337; return 0; } libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020003, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, "Could not grab drive", os_errno, 0); return 0; } /* non zero return means you still have the drive and it's not in a state to be released? (is that even possible?) */ int sg_release(struct burn_drive *d) { /* ts A60821 <<< debug: for tracing calls which might use open drive fds */ mmc_function_spy("sg_release"); if (d->fd < 1) { burn_print(1, "release an ungrabbed drive. die\n"); return 0; } /* ts A60821 <<< debug: for tracing calls which might use open drive fds */ mmc_function_spy("sg_release ----------- closing"); sg_close_drive(d); return 0; } int sg_issue_command(struct burn_drive *d, struct command *c) { int done = 0, no_c_page = 0; int err; sg_io_hdr_t s; #ifdef Libburn_log_sg_commandS /* <<< ts A61030 */ static FILE *fp= NULL; static int fpcount= 0; int i; #endif /* Libburn_log_sg_commandS */ /* <<< ts A60821 debug: for tracing calls which might use open drive fds */ char buf[161]; sprintf(buf,"sg_issue_command d->fd= %d d->released= %d\n", d->fd,d->released); mmc_function_spy(buf); #ifdef Libburn_log_sg_commandS /* <<< ts A61030 */ if(fp==NULL) { fp= fopen("/tmp/libburn_sg_command_log","a"); fprintf(fp,"\n-----------------------------------------\n"); } for(i=0;i<10;i++) fprintf(fp,"%2.2x ", c->opcode[i]); fprintf(fp,"\n"); fpcount++; #endif /* Libburn_log_sg_commandS */ /* ts A61010 : with no fd there is no chance to send an ioctl */ if (d->fd < 0) { c->error = 1; return 0; } c->error = 0; memset(&s, 0, sizeof(sg_io_hdr_t)); s.interface_id = 'S'; if (c->dir == TO_DRIVE) s.dxfer_direction = SG_DXFER_TO_DEV; else if (c->dir == FROM_DRIVE) s.dxfer_direction = SG_DXFER_FROM_DEV; else if (c->dir == NO_TRANSFER) { s.dxfer_direction = SG_DXFER_NONE; /* ts A61007 */ /* a ssert(!c->page); */ no_c_page = 1; } s.cmd_len = c->oplen; s.cmdp = c->opcode; s.mx_sb_len = 32; s.sbp = c->sense; memset(c->sense, 0, sizeof(c->sense)); s.timeout = 200000; if (c->page && !no_c_page) { s.dxferp = c->page->data; if (c->dir == FROM_DRIVE) { s.dxfer_len = BUFFER_SIZE; /* touch page so we can use valgrind */ memset(c->page->data, 0, BUFFER_SIZE); } else { /* ts A61010 */ /* a ssert(c->page->bytes > 0); */ if (c->page->bytes <= 0) { c->error = 1; return 0; } s.dxfer_len = c->page->bytes; } } else { s.dxferp = NULL; s.dxfer_len = 0; } s.usr_ptr = c; do { err = ioctl(d->fd, SG_IO, &s); /* ts A61010 */ /* a ssert(err != -1); */ if (err == -1) { libdax_msgs_submit(libdax_messenger, d->global_index, 0x0002010c, LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, "Failed to transfer command to drive", errno, 0); sg_close_drive(d); d->released = 1; d->busy = BURN_DRIVE_IDLE; c->error = 1; return -1; } if (s.sb_len_wr) { if (!c->retry) { c->error = 1; /* ts A61030 */ /* >>> to become d->notify_error() */ scsi_notify_error(d, c, s.sbp, s.sb_len_wr, 0); return 1; } switch (scsi_error(d, s.sbp, s.sb_len_wr)) { case RETRY: done = 0; break; case FAIL: done = 1; c->error = 1; break; } } else { done = 1; } } while (!done); return 1; } /* ts A61030 */ /* @param flag bit0=do also report TEST UNIT READY failures */ int scsi_notify_error(struct burn_drive *d, struct command *c, unsigned char *sense, int senselen, int flag) { int key, asc, ascq, ret; char msg[160]; if (c->opcode[0] == 0) /* SPC : TEST UNIT READY command */ if(!(flag & 1)) return 1; key = sense[2]; asc = sense[12]; ascq = sense[13]; sprintf(msg,"SCSI error condition on command %2.2Xh : ", c->opcode[0]); sprintf(msg+strlen(msg), "key= %x asc= %x ascq= %x\n", key,asc,ascq); ret = libdax_msgs_submit(libdax_messenger, d->global_index, 0x0002010f, LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, msg,0,0); return ret; } enum response scsi_error(struct burn_drive *d, unsigned char *sense, int senselen) { int key, asc, ascq; senselen = senselen; key = sense[2]; asc = sense[12]; ascq = sense[13]; burn_print(12, "CONDITION: 0x%x 0x%x 0x%x on %s %s\n", key, asc, ascq, d->idata->vendor, d->idata->product); switch (asc) { case 0: burn_print(12, "NO ERROR!\n"); return RETRY; case 2: burn_print(1, "not ready\n"); return RETRY; case 4: burn_print(1, "logical unit is in the process of becoming ready\n"); return RETRY; case 0x20: if (key == 5) burn_print(1, "bad opcode\n"); return FAIL; case 0x21: burn_print(1, "invalid address or something\n"); return FAIL; case 0x24: if (key == 5) burn_print(1, "invalid field in cdb\n"); else break; return FAIL; case 0x26: if ( key == 5 ) burn_print( 1, "invalid field in parameter list\n" ); return FAIL; case 0x28: if (key == 6) burn_print(1, "Not ready to ready change, medium may have changed\n"); else break; return RETRY; case 0x3A: burn_print(12, "Medium not present in %s %s\n", d->idata->vendor, d->idata->product); d->status = BURN_DISC_EMPTY; return FAIL; } burn_print(1, "unknown failure\n"); burn_print(1, "key:0x%x, asc:0x%x, ascq:0x%x\n", key, asc, ascq); return FAIL; } /* ts A60922 */ /** Try to obtain SCSI address parameters. @return 1 is success , 0 is failure */ int sg_obtain_scsi_adr(char *path, int *bus_no, int *host_no, int *channel_no, int *target_no, int *lun_no) { int fd, ret; struct my_scsi_idlun { int x; int host_unique_id; }; struct my_scsi_idlun idlun; if (strncmp(path, "/dev/hd", 7) == 0 && path[7] >= 'a' && path[7] <= 'z' && path[8] == 0) return 0; /* on RIP 14 all hdx return SCSI adr 0,0,0,0 */ fd = open(path, O_RDONLY | O_NONBLOCK); if(fd < 0) return 0; #ifdef SCSI_IOCTL_GET_BUS_NUMBER /* Hearsay A61005 */ if (ioctl(fd, SCSI_IOCTL_GET_BUS_NUMBER, bus_no) == -1) *bus_no = -1; #endif /* http://www.tldp.org/HOWTO/SCSI-Generic-HOWTO/scsi_g_idlun.html */ ret = ioctl(fd, SCSI_IOCTL_GET_IDLUN, &idlun); sg_close_drive_fd(path, -1, &fd, 0); if (ret == -1) return(0); *host_no= (idlun.x>>24)&255; *channel_no= (idlun.x>>16)&255; *target_no= (idlun.x)&255; *lun_no= (idlun.x>>8)&255; #ifdef SCSI_IOCTL_GET_BUS_NUMBER if(*bus_no == -1) *bus_no = 1000 * (*host_no + 1) + *channel_no; #else *bus_no= *host_no; #endif return 1; }