Пример #1
0
/** Gives up the drive for SCSI commands and releases eventual access locks.
    (Note: this is not physical tray locking.) 
*/
int sg_release(struct burn_drive *d)
{
	if (d->cam == NULL)
		return 0;
	sg_close_drive(d);
	return 0;
}
Пример #2
0
int sg_release(struct burn_drive *d)
{
	if (mmc_function_spy(d, "sg_release") <= 0)
		return 0;

	if (d->cam == NULL)
		return 0;

	mmc_function_spy(NULL, "sg_release ----------- closing.");

	sg_close_drive(d);
	d->released = 1;
	return 0;
}
Пример #3
0
static int sg_lock(struct burn_drive *d, int flag)
{
	int ret, os_errno, pass_dev_no = -1, flock_fd = -1;
	char *msg = NULL;

	BURN_ALLOC_MEM(msg, char, 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);
		{ret = 0; goto ex;}
	}
	if (d->lock_fd > 0)
		close(d->lock_fd);
	d->lock_fd = flock_fd;
	ret = 1;
ex:;
	BURN_FREE_MEM(msg);
	return ret;
}
Пример #4
0
/** Sends a SCSI command to the drive, receives reply and evaluates wether
    the command succeeded or shall be retried or finally failed.
    Returned SCSI errors shall not lead to a return value indicating failure.
    The callers get notified by c->error. An SCSI failure which leads not to
    a retry shall be notified via scsi_notify_error().
    The Libburn_log_sg_commandS facility might be of help when problems with
    a drive have to be examined. It shall stay disabled for normal use.
    @return: 1 success , <=0 failure
*/
int sg_issue_command(struct burn_drive *d, struct command *c)
{
	int done = 0;
	int err;
	union ccb *ccb;

	if (d->cam == NULL) {
		c->error = 0;
		return 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;
	}

	ccb->csio.cdb_len = c->oplen;
	memcpy(&ccb->csio.cdb_io.cdb_bytes, &c->opcode, c->oplen);
	
	if (c->page) {
		ccb->csio.data_ptr  = c->page->data;
		if (c->dir == FROM_DRIVE) {
			ccb->csio.dxfer_len = BUFFER_SIZE;
/* touch page so we can use valgrind */
			memset(c->page->data, 0, BUFFER_SIZE);
		} else {

			/* ts A61115: removed a ssert() */
			if(c->page->bytes <= 0)
				return 0;

			ccb->csio.dxfer_len = c->page->bytes;
		}
	} else {
		ccb->csio.data_ptr  = NULL;
		ccb->csio.dxfer_len = 0;
	}

	do {
		memset(&ccb->csio.sense_data, 0, sizeof(ccb->csio.sense_data));
		err = cam_send_ccb(d->cam, ccb);
		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);
			cam_freeccb(ccb);
			sg_close_drive(d);
			d->released = 1;
			d->busy = BURN_DRIVE_IDLE;
			c->error = 1;
			return -1;
		}
		/* XXX */
		memcpy(c->sense, &ccb->csio.sense_data, ccb->csio.sense_len);
		if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
			if (!c->retry) {
				c->error = 1;
				cam_freeccb(ccb);
				return 1;
			}
			switch (scsi_error(d, c->sense, 0)) {
			case RETRY:
				done = 0;
				break;
			case FAIL:
				done = 1;
				c->error = 1;
				break;
			}
		} else {
			done = 1;
		}
	} while (!done);
	cam_freeccb(ccb);
	return 1;
}
Пример #5
0
int sg_issue_command(struct burn_drive *d, struct command *c)
{
	int done = 0, err, sense_len = 0, ret, ignore_error, i;
	int cam_pass_err_recover = 0, key, asc, ascq, timeout_ms;
	union ccb *ccb;
	static FILE *fp = NULL;
	time_t start_time;

	mmc_function_spy(NULL, "sg_issue_command");

	c->error = 0;
	memset(c->sense, 0, sizeof(c->sense));

	if (d->cam == NULL)
		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;
	if (c->timeout > 0)
		timeout_ms = c->timeout;
	else
		timeout_ms = 200000;

	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 */
				  timeout_ms);                    /* 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);
	
	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;
	}

	start_time = time(NULL);
	for (i = 0; !done; i++) {

		memset(&ccb->csio.sense_data, 0, sizeof(ccb->csio.sense_data));
		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 > (int) sizeof(c->sense))
				sense_len = sizeof(c->sense);
			memcpy(c->sense, &ccb->csio.sense_data, sense_len);
			spc_decode_sense(c->sense, sense_len,
							&key, &asc, &ascq);
			if (sense_len >= 14 && cam_pass_err_recover && key)
				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[0] = 0x70; /*Fixed format sense data*/
				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[0] = 0x70; /*Fixed format sense data*/
				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[0] = 0x70; /*Fixed format sense data*/
				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[0] = 0x70; /*Fixed format sense data*/
				c->sense[2] = 0x02;
				c->sense[12] = 0x04;
				c->sense[13] = 0x00;
				done = 1;
			}

			/* >>> Need own duration time measurement.
			       Then remove bit1 from flag.
			*/
			done = scsi_eval_cmd_outcome(d, c, fp, c->sense,
						sense_len, 0, start_time,
						timeout_ms, i,
						2 | !!ignore_error);
			if (d->cancel)
				done = 1;
		} else {
			done = 1;
		}
	} while (!done);
	ret = 1;
ex:;
	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;
#ifdef Libburn_DIOCGMEDIASIZE_ISBLK
	int fd, ret;
	off_t add_size;
#else
	char *spt;
	int i, e;
#endif /* ! Libburn_DIOCGMEDIASIZE_ISBLK */

        if (stat(path, &stbuf) == -1)
                return 0;
        if (S_ISREG(stbuf.st_mode))
                return 1;
	if (!S_ISCHR(stbuf.st_mode))
		return 0;

#ifdef Libburn_DIOCGMEDIASIZE_ISBLK

	/* If it throws no error with DIOCGMEDIASIZE then it is a
	   'block device'
	*/
	fd = open(path, O_RDONLY);
	if (fd == -1)
		return 0;
	ret = ioctl(fd, DIOCGMEDIASIZE, &add_size);
	close(fd);

	return (ret != -1);

#else /* Libburn_DIOCGMEDIASIZE_ISBLK */

	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;

#endif /* ! Libburn_DIOCGMEDIASIZE_ISBLK */

}


/* 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 = NULL, *cpt;
	off_t add_size = 0;
	int fd, ret;

	BURN_ALLOC_MEM(testpath, char, 4096);
	testpath[0] = 0;
	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)
			{ret = -1; goto ex;}

#ifdef Libburn_if_this_was_linuX

	} else if(S_ISBLK(stbuf.st_mode)) {
		int open_mode = O_RDWR, fd, ret;
		long blocks;

		blocks = *bytes / 512;
		if(burn_sg_open_o_excl)
			open_mode |= O_EXCL;
		fd = open(path, open_mode);
		if (fd == -1)
			{ret = -2; goto ex;}
		ret = ioctl(fd, BLKGETSIZE, &blocks);
		close(fd);
		if (ret == -1)
			{ret = -2; goto ex;}
		*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)
			{ret = -2; goto ex;}
		ret = ioctl(fd, DIOCGMEDIASIZE, &add_size);
		close(fd);
		if (ret == -1)
			{ret = -2; goto ex;}
		*bytes = add_size;
	} else if(S_ISREG(stbuf.st_mode)) {
		add_size = stbuf.st_blocks * (off_t) 512;
		strcpy(testpath, path);
	} else
		{ret = 0; goto ex;}

	if (testpath[0]) {	
		if (statvfs(testpath, &vfsbuf) == -1)
			{ret = -2; goto ex;}
		*bytes = add_size + ((off_t) vfsbuf.f_frsize) *
						(off_t) vfsbuf.f_bavail;
	}
	ret = 1;
ex:
	BURN_FREE_MEM(testpath);
	return ret;
}


/* 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;
}