/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ /* Copyright (c) 2006 - 2010 Thomas Schmitt Provided under GPL version 2 or later. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* XXX */ /* ts A70909 */ #include /* ts B00121 */ #include /* DIOCGMEDIASIZE */ /* ts B00326 : For use of CAM_PASS_ERR_RECOVER with ahci */ #define Libburn_for_freebsd_ahcI yes /* ts B00327 : for debugging of cam_send_cdb() failures # define Libburn_ahci_verbouS yes */ /* ts B00327 : Apply CAM_PASS_ERR_RECOVER to drives even if not ahci # define libburn_ahci_style_for_alL yes */ #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" #include "libdax_msgs.h" extern struct libdax_msgs *libdax_messenger; struct burn_drive_enumeration_state { int fd; union ccb ccb; unsigned int i; int skip_device; }; 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 A60821 debug: for tracing calls which might use open drive fds or for catching SCSI usage of emulated drives. */ int mmc_function_spy(struct burn_drive *d, char * text); /* ts B00113 Whether to log SCSI commands: bit0= log in /tmp/libburn_sg_command_log bit1= log to stderr bit2= flush every line */ extern int burn_sg_log_scsi; /* ts B00114 */ /* Storage object is in libburn/init.c whether to strive for exclusive access to the drive */ extern int burn_sg_open_o_excl; /* ts A91227 */ /** Returns the id string of the SCSI transport adapter and eventually needed operating system facilities. This call is usable even if sg_initialize() was not called yet. In that case a preliminary constant message might be issued if detailed info is not available yet. @param msg returns id string @param flag unused yet, submit 0 @return 1 = success, <=0 = failure */ int sg_id_string(char msg[1024], int flag) { strcpy(msg, "internal FreeBSD CAM adapter sg-freebsd"); return 1; } /* ts A91227 */ /** Performs global initialization of the SCSI transport adapter and eventually needed operating system facilities. Checks for compatibility supporting software components. @param msg returns ids and/or error messages of eventual helpers @param flag unused yet, submit 0 @return 1 = success, <=0 = failure */ int sg_initialize(char msg[1024], int flag) { return sg_id_string(msg, 0); } /* ts A91227 */ /** Performs global finalization of the SCSI transport adapter and eventually needed operating system facilities. Releases globally aquired resources. @param flag unused yet, submit 0 @return 1 = success, <=0 = failure */ int sg_shutdown(int flag) { return 1; } /** Finalizes BURN_OS_TRANSPORT_DRIVE_ELEMENTS, the components of struct burn_drive which are defined in os-*.h. The eventual initialization of those components was made underneath scsi_enumerate_drives(). This will be called when a burn_drive gets disposed. @param d the drive to be finalized @param flag unused yet, submit 0 @return 1 = success, <=0 = failure */ int sg_dispose_drive(struct burn_drive *d, int flag) { return 1; } /* ts A61021 : Moved most code from scsi_enumerate_drives under sg_give_next_adr() */ /* Some helper functions for scsi_give_next_adr() */ static int sg_init_enumerator(burn_drive_enumerator_t *idx_) { struct burn_drive_enumeration_state *idx; int bufsize; idx = calloc(1, sizeof(*idx)); if (idx == NULL) { warnx("cannot allocate memory for enumerator"); return -1; } idx->skip_device = 0; if ((idx->fd = open(XPT_DEVICE, O_RDWR)) == -1) { warn("could not open %s", XPT_DEVICE); free(idx); idx = NULL; return -1; } bzero(&(idx->ccb), sizeof(union ccb)); idx->ccb.ccb_h.path_id = CAM_XPT_PATH_ID; idx->ccb.ccb_h.target_id = CAM_TARGET_WILDCARD; idx->ccb.ccb_h.target_lun = CAM_LUN_WILDCARD; idx->ccb.ccb_h.func_code = XPT_DEV_MATCH; bufsize = sizeof(struct dev_match_result) * 100; idx->ccb.cdm.match_buf_len = bufsize; idx->ccb.cdm.matches = (struct dev_match_result *) calloc(1, bufsize); if (idx->ccb.cdm.matches == NULL) { warnx("cannot allocate memory for matches"); close(idx->fd); free(idx); return -1; } idx->ccb.cdm.num_matches = 0; idx->i = idx->ccb.cdm.num_matches; /* to trigger buffer load */ /* * We fetch all nodes, since we display most of them in the default * case, and all in the verbose case. */ idx->ccb.cdm.num_patterns = 0; idx->ccb.cdm.pattern_buf_len = 0; *idx_ = idx; return 1; } static void sg_destroy_enumerator(burn_drive_enumerator_t *idx_) { struct burn_drive_enumeration_state *idx = *idx_; if(idx->fd != -1) close(idx->fd); free(idx->ccb.cdm.matches); free(idx); *idx_ = NULL; } static int sg_next_enumeration_buffer(burn_drive_enumerator_t *idx_) { struct burn_drive_enumeration_state *idx = *idx_; /* * We do the ioctl multiple times if necessary, in case there are * more than 100 nodes in the EDT. */ if (ioctl(idx->fd, CAMIOCOMMAND, &(idx->ccb)) == -1) { warn("error sending CAMIOCOMMAND ioctl"); return -1; } if ((idx->ccb.ccb_h.status != CAM_REQ_CMP) || ((idx->ccb.cdm.status != CAM_DEV_MATCH_LAST) && (idx->ccb.cdm.status != CAM_DEV_MATCH_MORE))) { warnx("got CAM error %#x, CDM error %d\n", idx->ccb.ccb_h.status, idx->ccb.cdm.status); return -1; } return 1; } /** Returns the next index object state 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) { struct burn_drive_enumeration_state *idx; int ret; if (initialize == 1) { ret = sg_init_enumerator(idx_); if (ret<=0) return ret; } else if (initialize == -1) { sg_destroy_enumerator(idx_); return 0; } idx = *idx_; do { if (idx->i >= idx->ccb.cdm.num_matches) { ret = sg_next_enumeration_buffer(idx_); if (ret<=0) return -1; idx->i = 0; } else (idx->i)++; while (idx->i < idx->ccb.cdm.num_matches) { switch (idx->ccb.cdm.matches[idx->i].type) { case DEV_MATCH_BUS: break; case DEV_MATCH_DEVICE: { struct device_match_result* result; result = &(idx->ccb.cdm.matches[idx->i].result.device_result); if (result->flags & DEV_RESULT_UNCONFIGURED) idx->skip_device = 1; else idx->skip_device = 0; break; } case DEV_MATCH_PERIPH: { struct periph_match_result* result; result = &(idx->ccb.cdm.matches[idx->i].result.periph_result); /* ts B00112 : we really want only "cd" devices. if (idx->skip_device || strcmp(result->periph_name, "pass") == 0) break; */ if (idx->skip_device || strcmp(result->periph_name, "cd") != 0) break; ret = snprintf(adr, adr_size, "/dev/%s%d", result->periph_name, result->unit_number); if(ret >= adr_size) return -1; /* Found next enumerable address */ return 1; } default: /* fprintf(stderr, "unknown match type\n"); */ break; } (idx->i)++; } } while ((idx->ccb.ccb_h.status == CAM_REQ_CMP) && (idx->ccb.cdm.status == CAM_DEV_MATCH_MORE)); return 0; } int sg_is_enumerable_adr(char* adr) { burn_drive_enumerator_t idx; int ret; char buf[64]; ret = sg_init_enumerator(&idx); if (ret <= 0) return 0; while(1) { ret = sg_give_next_adr(&idx, buf, sizeof(buf), 0); if (ret <= 0) break; if (strcmp(adr, buf) == 0) { sg_destroy_enumerator(&idx); return 1; } } sg_destroy_enumerator(&idx); return (0); } /** 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) { burn_drive_enumerator_t idx; int ret; char buf[64]; struct periph_match_result* result; ret = sg_init_enumerator(&idx); if (ret <= 0) return 0; while(1) { ret = sg_give_next_adr(&idx, buf, sizeof(buf), 0); if (ret <= 0) break; if (strcmp(path, buf) == 0) { result = &(idx->ccb.cdm.matches[idx->i].result.periph_result); *bus_no = result->path_id; *host_no = result->path_id; *channel_no = 0; *target_no = result->target_id; *lun_no = result->target_lun; sg_destroy_enumerator(&idx); return 1; } } sg_destroy_enumerator(&idx); return (0); } int sg_close_drive(struct burn_drive * d) { if (d->cam != NULL) { cam_close_device(d->cam); d->cam = NULL; } if (d->lock_fd > 0) { close(d->lock_fd); d->lock_fd = -1; } return 0; } int sg_drive_is_open(struct burn_drive * d) { return (d->cam != NULL); } int scsi_enumerate_drives(void) { burn_drive_enumerator_t idx; int ret; char buf[64]; struct periph_match_result* result; ret = sg_init_enumerator(&idx); if (ret <= 0) return 0; while(1) { ret = sg_give_next_adr(&idx, buf, sizeof(buf), 0); if (ret <= 0) break; if (burn_drive_is_banned(buf)) continue; result = &idx->ccb.cdm.matches[idx->i].result.periph_result; enumerate_common(buf, result->path_id, result->path_id, 0, result->target_id, result->target_lun); } sg_destroy_enumerator(&idx); return 1; } #ifdef Scsi_freebsd_make_own_enumeratE /* ts A61021: The old version which mixes SCSI and operating system adapter */ static void enumerate_common(char *fname, int bus_no, int host_no, int channel_no, int target_no, int lun_no) { struct burn_drive *t; struct burn_drive out; /* ts A60923 */ out.bus_no = bus_no; out.host = host_no; out.id = target_no; out.channel = channel_no; out.lun = lun_no; out.devname = burn_strdup(fname); out.cam = NULL; out.lock_fd = -1; out.is_ahci = 0; out.start_lba= -2000000000; out.end_lba= -2000000000; out.read_atip = mmc_read_atip; out.grab = sg_grab; out.release = sg_release; out.drive_is_open= sg_drive_is_open; out.issue_command = sg_issue_command; out.getcaps = spc_getcaps; out.released = 1; out.status = BURN_DISC_UNREADY; out.eject = sbc_eject; out.load = sbc_load; out.lock = spc_prevent; out.unlock = spc_allow; out.read_disc_info = spc_sense_write_params; out.get_erase_progress = spc_get_erase_progress; out.test_unit_ready = spc_test_unit_ready; out.probe_write_modes = spc_probe_write_modes; out.read_toc = mmc_read_toc; out.write = mmc_write; out.erase = mmc_erase; out.read_sectors = mmc_read_sectors; out.perform_opc = mmc_perform_opc; out.set_speed = mmc_set_speed; out.send_parameters = spc_select_error_params; out.send_write_parameters = spc_select_write_params; out.send_cue_sheet = mmc_send_cue_sheet; out.sync_cache = mmc_sync_cache; out.get_nwa = mmc_get_nwa; out.close_disc = mmc_close_disc; out.close_session = mmc_close_session; out.close_track_session = mmc_close; out.read_buffer_capacity = mmc_read_buffer_capacity; out.idata = calloc(1, sizeof(struct burn_scsi_inquiry_data)); out.idata->valid = 0; out.mdata = calloc(1, sizeof(struct scsi_mode_data)); out.mdata->valid = 0; if (out.idata == NULL || out.mdata == NULL) { libdax_msgs_submit(libdax_messenger, -1, 0x00020108, LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, "Could not allocate new drive object", 0, 0); return; } memset(&out.params, 0, sizeof(struct params)); t = burn_drive_register(&out); /* ts A60821 <<< debug: for tracing calls which might use open drive fds */ mmc_function_spy(NULL, "enumerate_common : -------- doing grab"); /* try to get the drive info */ if (t->grab(t)) { burn_print(2, "getting drive info\n"); t->getcaps(t); t->unlock(t); t->released = 1; } else { burn_print(2, "unable to grab new located drive\n"); } /* ts A60821 <<< debug: for tracing calls which might use open drive fds */ mmc_function_spy(NULL, "enumerate_common : ----- would release "); } #else /* Scsi_freebsd_make_own_enumeratE */ /* The new, more concise version of enumerate_common */ static void enumerate_common(char *fname, int bus_no, int host_no, int channel_no, int target_no, int lun_no) { int ret; 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 CAM */ /* Adapter specific handles and data */ out.cam = NULL; out.lock_fd = -1; out.is_ahci = 0; /* Adapter specific functions */ out.grab = sg_grab; out.release = sg_release; out.drive_is_open = sg_drive_is_open; out.issue_command = sg_issue_command; /* Finally register drive and inquire drive information */ burn_drive_finish_enum(&out); } #endif /* ! Scsi_freebsd_make_own_enumeratE */ /* Lock the inode associated to dev_fd and the inode associated to devname. Return OS errno, number of pass device of dev_fd, locked fd to devname, error message. A return value of > 0 means success, <= 0 means failure. */ static int freebsd_dev_lock(int dev_fd, char *devname, int *os_errno, int *pass_dev_no, int *lock_fd, char msg[4096], int flag) { int lock_denied = 0, fd_stbuf_valid, name_stbuf_valid, i, pass_l = 100; int max_retry = 3, tries = 0; struct stat fd_stbuf, name_stbuf; char pass_name[16], *lock_name; *os_errno = 0; *pass_dev_no = -1; *lock_fd = -1; msg[0] = 0; fd_stbuf_valid = !fstat(dev_fd, &fd_stbuf); /* Try to find name of pass device by inode number */ lock_name = (char *) "effective device"; if(fd_stbuf_valid) { for (i = 0; i < pass_l; i++) { sprintf(pass_name, "/dev/pass%d", i); if (stat(pass_name, &name_stbuf) != -1) if(fd_stbuf.st_ino == name_stbuf.st_ino && fd_stbuf.st_dev == name_stbuf.st_dev) break; } if (i < pass_l) { lock_name = pass_name; *pass_dev_no = i; } } name_stbuf_valid = !stat(devname, &name_stbuf); for (tries= 0; tries <= max_retry; tries++) { lock_denied = flock(dev_fd, LOCK_EX | LOCK_NB); *os_errno = errno; if (lock_denied) { if (errno == EAGAIN && tries < max_retry) { /* <<< debugging fprintf(stderr, "\nlibcdio_DEBUG: EAGAIN pass, tries= %d\n", tries); */ usleep(2000000); continue; } sprintf(msg, "Device busy. flock(LOCK_EX) failed on %s of %s", strlen(lock_name) > 2000 || *pass_dev_no < 0 ? "pass device" : lock_name, strlen(devname) > 2000 ? "drive" : devname); return 0; } break; } /* fprintf(stderr, "libburn_DEBUG: flock obtained on %s of %s\n", lock_name, devname); */ /* Eventually lock the official device node too */ if (fd_stbuf_valid && name_stbuf_valid && (fd_stbuf.st_ino != name_stbuf.st_ino || fd_stbuf.st_dev != name_stbuf.st_dev)) { *lock_fd = open(devname, O_RDONLY); if (*lock_fd == 0) { close(*lock_fd); *lock_fd = -1; } if (*lock_fd > 0) { for (tries = 0; tries <= max_retry; tries++) { lock_denied = flock(*lock_fd, LOCK_EX | LOCK_NB); if (lock_denied) { if (errno == EAGAIN && tries < max_retry) { /* <<< debugging fprintf(stderr, "\nlibcdio_DEBUG: EAGAIN dev, tries= %d\n", tries); */ usleep(2000000); continue; } close(*lock_fd); *lock_fd = -1; sprintf(msg, "Device busy. flock(LOCK_EX) failed on %s", strlen(devname) > 4000 ? "drive" : devname); return 0; } break; } } /* fprintf(stderr, "libburn_DEBUG: flock obtained on %s\n", devname); */ } return 1; } static int sg_lock(struct burn_drive *d, int flag) { int ret, os_errno, pass_dev_no = -1, flock_fd = -1; char msg[4096]; ret = freebsd_dev_lock(d->cam->fd, d->devname, &os_errno, &pass_dev_no, &flock_fd, msg, 0); if (ret <= 0) { libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020008, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, msg, os_errno, 0); sg_close_drive(d); return 0; } if (d->lock_fd > 0) close(d->lock_fd); d->lock_fd = flock_fd; return 1; } int sg_grab(struct burn_drive *d) { struct cam_device *cam; char path_string[80]; if (mmc_function_spy(d, "sg_grab") <= 0) return 0; if (burn_drive_is_open(d)) { d->released = 0; return 1; } cam = cam_open_device(d->devname, O_RDWR); if (cam == NULL) { libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020003, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, "Could not grab drive", errno, 0); return 0; } d->cam = cam; if (burn_sg_open_o_excl & 63) if (sg_lock(d, 0) <= 0) return 0; fcntl(cam->fd, F_SETOWN, getpid()); cam_path_string(d->cam, path_string, sizeof(path_string)); #ifdef Libburn_ahci_verbouS fprintf(stderr, "libburn_EXPERIMENTAL: CAM path = %s\n", path_string); #endif if (strstr(path_string, ":ahcich") != NULL) d->is_ahci = 1; else d->is_ahci = -1; d->released = 0; return 1; } /* 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) { if (mmc_function_spy(d, "sg_release") <= 0) return 0; if (d->cam == NULL) { burn_print(1, "release an ungrabbed drive. die\n"); return 0; } mmc_function_spy(NULL, "sg_release ----------- closing."); sg_close_drive(d); d->released = 1; return 0; } int sg_issue_command(struct burn_drive *d, struct command *c) { int done = 0, err, sense_len = 0, ret, ignore_error, no_retry = 0; int cam_pass_err_recover = 0; union ccb *ccb; char buf[161]; static FILE *fp = NULL; snprintf(buf, sizeof (buf), "sg_issue_command d->cam=%p d->released=%d", (void*)d->cam, d->released); mmc_function_spy(NULL, buf); if (d->cam == NULL) { c->error = 0; return 0; } if (burn_sg_log_scsi & 1) { if (fp == NULL) { fp= fopen("/tmp/libburn_sg_command_log", "a"); fprintf(fp, "\n-----------------------------------------\n"); } } if (burn_sg_log_scsi & 3) scsi_log_cmd(c,fp,0); c->error = 0; ccb = cam_getccb(d->cam); cam_fill_csio(&ccb->csio, 1, /* retries */ NULL, /* cbfncp */ CAM_DEV_QFRZDIS, /* flags */ MSG_SIMPLE_Q_TAG, /* tag_action */ NULL, /* data_ptr */ 0, /* dxfer_len */ sizeof (ccb->csio.sense_data), /* sense_len */ 0, /* cdb_len */ 30*1000); /* timeout */ switch (c->dir) { case TO_DRIVE: ccb->csio.ccb_h.flags |= CAM_DIR_OUT; break; case FROM_DRIVE: ccb->csio.ccb_h.flags |= CAM_DIR_IN; break; case NO_TRANSFER: ccb->csio.ccb_h.flags |= CAM_DIR_NONE; break; } #ifdef Libburn_for_freebsd_ahcI /* ts B00325 : Advise by Alexander Motin */ /* Runs well on 8-STABLE (23 Mar 2003) But on 8-RELEASE cam_send_ccb() returns non-zero with errno 6 on eject. Long lasting TEST UNIT READY cycles break with errno 16. */ #ifdef Libburn_ahci_style_for_alL { #else if (d->is_ahci > 0) { #endif ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER; cam_pass_err_recover = 1; } #endif /* Libburn_for_freebsd_ahcI */ ccb->csio.cdb_len = c->oplen; memcpy(&ccb->csio.cdb_io.cdb_bytes, &c->opcode, c->oplen); memset(&ccb->csio.sense_data, 0, sizeof (ccb->csio.sense_data)); if (c->page) { ccb->csio.data_ptr = c->page->data; if (c->dir == FROM_DRIVE) { /* ts A90430 : Ticket 148 , by jwehle : "On ... FreeBSD 6.4 which has a usb memory reader in addition to a ATAPI DVD burner sg_issue_command will hang while the SCSI bus is being scanned" */ if (c->dxfer_len >= 0) ccb->csio.dxfer_len = c->dxfer_len; else ccb->csio.dxfer_len = BUFFER_SIZE; /* touch page so we can use valgrind */ memset(c->page->data, 0, BUFFER_SIZE); } else { ccb->csio.dxfer_len = c->page->bytes; } } else { ccb->csio.data_ptr = NULL; ccb->csio.dxfer_len = 0; } do { memset(c->sense, 0, sizeof(c->sense)); err = cam_send_ccb(d->cam, ccb); ignore_error = sense_len = 0; /* ts B00325 : CAM_AUTOSNS_VALID advised by Alexander Motin */ if (ccb->ccb_h.status & CAM_AUTOSNS_VALID) { /* ts B00110 */ /* Better curb sense_len */ sense_len = ccb->csio.sense_len; if (sense_len > sizeof(c->sense)) sense_len = sizeof(c->sense); memcpy(c->sense, &ccb->csio.sense_data, sense_len); if (sense_len >= 14 && cam_pass_err_recover && (c->sense[2] & 0x0f)) ignore_error = 1; } if (err == -1 && cam_pass_err_recover && ! ignore_error) { #ifdef Libburn_ahci_verbouS fprintf(stderr, "libburn_EXPERIMENTAL: errno = %d . cam_errbuf = '%s'\n", errno, cam_errbuf); #endif if (errno == ENXIO && c->opcode[0] != 0) { /* Operations on empty or ejected tray */ /* MEDIUM NOT PRESENT */ #ifdef Libburn_ahci_verbouS fprintf(stderr, "libburn_EXPERIMENTAL: Emulating [2,3A,00] MEDIUM NOT PRESENT\n"); #endif c->sense[2] = 0x02; c->sense[12] = 0x3A; c->sense[13] = 0x00; sense_len = 14; ignore_error = 1; } else if (c->opcode[0] == 0 && (errno == EBUSY || errno == ENXIO)) { /* Timeout of TEST UNIT READY loop */ /* Inquiries while tray is being loaded */ /*LOGICAL UNIT NOT READY,CAUSE NOT REPORTABLE*/ #ifdef Libburn_ahci_verbouS fprintf(stderr, "libburn_EXPERIMENTAL: Emulating [2,04,00] LOGICAL UNIT NOT READY,CAUSE NOT REPORTABLE\n"); #endif c->sense[2] = 0x02; c->sense[12] = 0x04; c->sense[13] = 0x00; sense_len = 14; ignore_error = 1; } else if (errno == EINVAL) { /* Inappropriate MODE SENSE */ /* INVALID FIELD IN CDB */ #ifdef Libburn_ahci_verbouS fprintf(stderr, "libburn_EXPERIMENTAL: Emulating [5,24,00] INVALID FIELD IN CDB\n"); #endif c->sense[2] = 0x05; c->sense[12] = 0x24; c->sense[13] = 0x00; sense_len = 14; ignore_error = 1; } } if (err == -1 && !ignore_error) { 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; {ret = -1; goto ex;} } /* XXX */ if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { if (sense_len < 14) { /*LOGICAL UNIT NOT READY,CAUSE NOT REPORTABLE*/ #ifdef Libburn_ahci_verbouS fprintf(stderr, "libburn_EXPERIMENTAL: CAM_STATUS= %d .Emulating [2,04,00] LOGICAL UNIT NOT READY,CAUSE NOT REPORTABLE\n", (ccb->ccb_h.status & CAM_STATUS_MASK)); #endif c->sense[2] = 0x02; c->sense[12] = 0x04; c->sense[13] = 0x00; no_retry = 1; } if (no_retry || ignore_error || !c->retry) { c->error = 1; {ret = 1; goto ex;} } switch (scsi_error(d, c->sense, 0)) { case RETRY: done = 0; if (burn_sg_log_scsi & 3) { /* >>> Need own duration time measurement. Then remove bit1 */ scsi_log_err(c, fp, c->sense, 0, (c->error != 0) | 2); scsi_log_cmd(c,fp,0); } break; case FAIL: done = 1; c->error = 1; break; } } else { done = 1; } } while (!done); ret = 1; ex:; if (c->error) scsi_notify_error(d, c, c->sense, 18, 0); if (burn_sg_log_scsi & 3) /* >>> Need own duration time measurement. Then remove bit1 */ scsi_log_err(c, fp, c->sense, 0, (c->error != 0) | 2); cam_freeccb(ccb); return ret; } /* ts B00115 */ /* Return 1 if the given path leads to a regular file or a device that can be seeked, read and eventually written with 2 kB granularity. */ int burn_os_is_2k_seekrw(char *path, int flag) { struct stat stbuf; char *spt; int i, e; if (stat(path, &stbuf) == -1) return 0; if (S_ISREG(stbuf.st_mode)) return 1; if (!S_ISCHR(stbuf.st_mode)) return 0; spt = strrchr(path, '/'); if (spt == NULL) spt = path; else spt++; e = strlen(spt); for (i = strlen(spt) - 1; i > 0; i--) if (spt[i] >= '0' && spt[i] <= '9') e = i; if (strncmp(spt, "da", e) == 0) /* SCSI disk. E.g. USB stick. */ return 1; if (strncmp(spt, "cd", e) == 0) /* SCSI CD drive might be writeable. */ return 1; if (strncmp(spt, "ad", e) == 0) /* IDE hard drive */ return 1; if (strncmp(spt, "acd", e) == 0) /* IDE CD drive might be writeable */ return 1; if (strncmp(spt, "fd", e) == 0) /* Floppy disk */ return 1; if (strncmp(spt, "fla", e) == 0) /* Flash drive */ return 1; return 0; } /* ts A70909 */ /** Estimate the potential payload capacity of a file address. @param path The address of the file to be examined. If it does not exist yet, then the directory will be inquired. @param bytes This value gets modified if an estimation is possible @return -2 = cannot perform necessary operations on file object -1 = neither path nor dirname of path exist 0 = could not estimate size capacity of file object 1 = estimation has been made, bytes was set */ int burn_os_stdio_capacity(char *path, off_t *bytes) { struct stat stbuf; struct statvfs vfsbuf; char testpath[4096], *cpt; long blocks; off_t add_size = 0; int fd, ret; testpath[0] = 0; blocks = *bytes / 512; if (stat(path, &stbuf) == -1) { strcpy(testpath, path); cpt = strrchr(testpath, '/'); if(cpt == NULL) strcpy(testpath, "."); else if(cpt == testpath) testpath[1] = 0; else *cpt = 0; if (stat(testpath, &stbuf) == -1) return -1; #ifdef Libburn_if_this_was_linuX } else if(S_ISBLK(stbuf.st_mode)) { int open_mode = O_RDWR, fd, ret; if(burn_sg_open_o_excl) open_mode |= O_EXCL; fd = open(path, open_mode); if (fd == -1) return -2; ret = ioctl(fd, BLKGETSIZE, &blocks); close(fd); if (ret == -1) return -2; *bytes = ((off_t) blocks) * (off_t) 512; #endif /* Libburn_if_this_was_linuX */ } else if(S_ISCHR(stbuf.st_mode)) { fd = open(path, O_RDONLY); if (fd == -1) return -2; ret = ioctl(fd, DIOCGMEDIASIZE, &add_size); close(fd); if (ret == -1) return -2; *bytes = add_size; } else if(S_ISREG(stbuf.st_mode)) { add_size = stbuf.st_blocks * (off_t) 512; strcpy(testpath, path); } else return 0; if (testpath[0]) { if (statvfs(testpath, &vfsbuf) == -1) return -2; *bytes = add_size + ((off_t) vfsbuf.f_frsize) * (off_t) vfsbuf.f_bavail; } return 1; } /* ts A91122 : an interface to open(O_DIRECT) or similar OS tricks. */ #ifdef Libburn_read_o_direcT /* No special O_DIRECT-like precautions are implemented here */ #endif /* Libburn_read_o_direcT */ int burn_os_open_track_src(char *path, int open_flags, int flag) { int fd; fd = open(path, open_flags); return fd; } void *burn_os_alloc_buffer(size_t amount, int flag) { void *buf = NULL; buf = calloc(1, amount); return buf; } int burn_os_free_buffer(void *buffer, size_t amount, int flag) { if (buffer == NULL) return 0; free(buffer); return 1; }