diff options
Diffstat (limited to 'readom')
-rw-r--r-- | readom/CMakeLists.txt | 19 | ||||
-rw-r--r-- | readom/io.c | 193 | ||||
-rw-r--r-- | readom/readom.1 | 497 | ||||
-rw-r--r-- | readom/readom.c | 2194 |
4 files changed, 2903 insertions, 0 deletions
diff --git a/readom/CMakeLists.txt b/readom/CMakeLists.txt new file mode 100644 index 0000000..81536d8 --- /dev/null +++ b/readom/CMakeLists.txt @@ -0,0 +1,19 @@ +PROJECT (READECD C) +INCLUDE_DIRECTORIES(../include ../wodim ${CMAKE_BINARY_DIR} ${CMAKE_BINARY_DIR}/include) +INCLUDE(../include/AddScgBits.cmake) +INCLUDE(../include/AddSchilyBits.cmake) + +AUX_SOURCE_DIRECTORY(. readom_SRCS) + +LINK_DIRECTORIES(../librols ../libusal ../wodim) + +ADD_DEFINITIONS(-DCLONE_WRITE) + +ADD_EXECUTABLE (readom ${readom_SRCS}) +TARGET_LINK_LIBRARIES(readom wodimstuff ${EXTRA_LIBS}) +SET_TARGET_PROPERTIES(readom PROPERTIES SKIP_BUILD_RPATH TRUE) + +INSTALL(TARGETS readom DESTINATION bin) +INSTALL(FILES +readom.1 +DESTINATION ${MANSUBDIR}/man1) diff --git a/readom/io.c b/readom/io.c new file mode 100644 index 0000000..364e0ed --- /dev/null +++ b/readom/io.c @@ -0,0 +1,193 @@ +/* + * This file has been modified for the cdrkit suite. + * + * The behaviour and appearence of the program code below can differ to a major + * extent from the version distributed by the original author(s). + * + * For details, see Changelog file distributed with the cdrkit package. If you + * received this file from another source then ask the distributing person for + * a log of modifications. + * + */ + +/* @(#)io.c 1.19 98/10/10 Copyright 1988 J. Schilling */ +/* + * Copyright (c) 1988 J. Schilling + */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; see the file COPYING. If not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <mconfig.h> +#include <stdio.h> +#include <standard.h> +#include <vadefs.h> +#include <stdxlib.h> +#include <strdefs.h> +#include <utypes.h> +#include <schily.h> +#include <ctype.h> + +struct disk { + int dummy; +}; + +static char *skipwhite(const char *); +static void prt_std(char *, long, long, long, struct disk *); +BOOL cvt_std(char *, long *, long, long, struct disk *); +extern BOOL getvalue(char *, long *, long, long, + void (*)(char *, long, long, long, struct disk *), + BOOL (*)(char *, long *, long, long, struct disk *), + struct disk *); +extern BOOL getlong(char *, long *, long, long); +extern BOOL getint(char *, int *, int, int); +extern BOOL yes(char *, ...); + +static +char *skipwhite(const char *s) +{ + register const Uchar *p = (const Uchar *)s; + + while (*p) { + if (!isspace(*p)) + break; + p++; + } + return ((char *)p); +} + +/* ARGSUSED */ + +BOOL +cvt_std(char *linep, long *lp, long mini, long maxi, struct disk *dp) +{ + long l = -1L; + +/* printf("cvt_std(\"%s\", %d, %d, %d);\n", linep, *lp, mini, maxi);*/ + + if (linep[0] == '?') { + printf("Enter a number in the range from %ld to %ld\n", + mini, maxi); + printf("The default radix is 10\n"); + printf("Precede number with '0x' for hexadecimal or with '0' for octal\n"); + printf("Shorthands are:\n"); + printf("\t'^' for minimum value (%ld)\n", mini); + printf("\t'$' for maximum value (%ld)\n", maxi); + printf("\t'+' for incrementing value to %ld\n", *lp + 1); + printf("\t'-' for decrementing value to %ld\n", *lp - 1); + return (FALSE); + } + if (linep[0] == '^' && *skipwhite(&linep[1]) == '\0') { + l = mini; + } else if (linep[0] == '$' && *skipwhite(&linep[1]) == '\0') { + l = maxi; + } else if (linep[0] == '+' && *skipwhite(&linep[1]) == '\0') { + if (*lp < maxi) + l = *lp + 1; + } else if (linep[0] == '-' && *skipwhite(&linep[1]) == '\0') { + if (*lp > mini) + l = *lp - 1; + } else if (*astol(linep, &l)) { + printf("Not a number: '%s'.\n", linep); + return (FALSE); + } + if (l < mini || l > maxi) { + printf("'%s' is out of range.\n", linep); + return (FALSE); + } + *lp = l; + return (TRUE); +} + +/* ARGSUSED */ +static void +prt_std(char *s, long l, long mini, long maxi, struct disk *dp) +{ + printf("%s %ld (%ld - %ld)/<cr>:", s, l, mini, maxi); +} + + +BOOL getvalue(char *s, long *lp, long mini, long maxi, + void (*prt)(char *, long, long, long, struct disk *), + BOOL (*cvt)(char *, long *, long, long, struct disk *), + struct disk *dp) +{ + char line[128]; + char *linep; + + for(;;) { + (*prt)(s, *lp, mini, maxi, dp); + flush(); + line[0] = '\0'; + if (rols_getline(line, 80) == EOF) + exit(EX_BAD); + + linep = skipwhite(line); + /* + * Nicht initialisierte Variablen + * duerfen nicht uebernommen werden + */ + if (linep[0] == '\0' && *lp != -1L) + return (FALSE); + + if (strlen(linep) == 0) { + /* Leere Eingabe */ + /* EMPTY */ + } else if ((*cvt)(linep, lp, mini, maxi, dp)) + return (TRUE); + } + /* NOTREACHED */ +} + + +BOOL getlong(char *s, long *lp, long mini, long maxi) +{ + return (getvalue(s, lp, mini, maxi, prt_std, cvt_std, (void *)0)); +} + + +BOOL getint(char *s, int *ip, int mini, int maxi) +{ + long l = *ip; + BOOL ret; + + ret = getlong(s, &l, (long)mini, (long)maxi); + *ip = l; + return (ret); +} + +/* VARARGS1 */ +BOOL yes(char *form, ...) +{ + va_list args; + char okbuf[10]; + +again: + va_start(args, form); + vprintf(form, args); + va_end(args); + flush(); + if (rols_getline(okbuf, sizeof(okbuf)) == EOF) + exit(EX_BAD); + if (okbuf[0] == '?') { + printf("Enter 'y', 'Y', 'yes' or 'YES' if you agree with the previous asked question.\n"); + printf("All other input will be handled as if the question has beed answered with 'no'.\n"); + goto again; + } + if(streql(okbuf, "y") || streql(okbuf, "yes") || + streql(okbuf, "Y") || streql(okbuf, "YES")) + return(TRUE); + else + return(FALSE); +} diff --git a/readom/readom.1 b/readom/readom.1 new file mode 100644 index 0000000..3bdc47b --- /dev/null +++ b/readom/readom.1 @@ -0,0 +1,497 @@ +.\" @(#)readom.1 1.23 06/01/12 Copyright 1996-2006 J. Schilling +.\" +.\" Modified version of readcd.1 by J. Schilling, 11/2006 +.\" +.\" This program is free software; you can redistribute it and/or modify +.\" it under the terms of the GNU General Public License version 2 +.\" as published by the Free Software Foundation. +.\" +.\" The GNU General Public License's references to "object code" +.\" and "executables" are to be interpreted as the output of any +.\" document formatting or typesetting system, including +.\" intermediate and printed output. +.\" +.\" This manual is distributed in the hope that it will be useful, +.\" but WITHOUT ANY WARRANTY; without even the implied warranty of +.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +.\" GNU General Public License for more details. +.\" +.\" You should have received a copy of the GNU General Public License along with +.\" this program; see the file COPYING. If not, write to the Free Software +.\" Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +.if t .ds a \v'-0.55m'\h'0.00n'\z.\h'0.40n'\z.\v'0.55m'\h'-0.40n'a +.if t .ds o \v'-0.55m'\h'0.00n'\z.\h'0.45n'\z.\v'0.55m'\h'-0.45n'o +.if t .ds u \v'-0.55m'\h'0.00n'\z.\h'0.40n'\z.\v'0.55m'\h'-0.40n'u +.if t .ds A \v'-0.77m'\h'0.25n'\z.\h'0.45n'\z.\v'0.77m'\h'-0.70n'A +.if t .ds O \v'-0.77m'\h'0.25n'\z.\h'0.45n'\z.\v'0.77m'\h'-0.70n'O +.if t .ds U \v'-0.77m'\h'0.30n'\z.\h'0.45n'\z.\v'0.77m'\h'-0.75n'U +.if t .ds s \\(*b +.if t .ds S SS +.if n .ds a ae +.if n .ds o oe +.if n .ds u ue +.if n .ds s sz +.TH READOM 1 "Version 2.0" "J\*org Schilling" "Schily\'s USER COMMANDS" +.SH NAME +readom \- read or write data Compact Discs +.SH SYNOPSIS +.B readom +.BI dev= device +[ +.I options +] + +.SH DESCRIPTION +.B Readom +is used to read or write Compact Discs. +.PP +The +.I device +refers to a device location similar to the one used in the wodim command. Refer to its manpage for details. +.PP +Also note that this version of readom uses a modified libusal library which has +a different behaviour compared to the one distributed by its original author. + +.SH OPTIONS +.PP +If no options except the +.I dev= +option have been specified, +.B readom +goes into interactive mode. +Select a primary function and then follow the instructions. +.PP +.TP +.B \-version +Print version information and exit. +.TP +.BI dev= target +Sets the SCSI target for the drive, see notes above. +A typical device specification is +.BI dev= 6,0 +\&. +If a filename must be provided together with the numerical target +specification, the filename is implementation specific. +The correct filename in this case can be found in the system specific +manuals of the target operating system. +On a +.I FreeBSD +system without +.I CAM +support, you need to use the control device (e.g. +.IR /dev/rcd0.ctl ). +A correct device specification in this case may be +.BI dev= /dev/rcd0.ctl:@ +\&. +.sp +On Linux, drives connected to a parallel port adapter are mapped +to a virtual SCSI bus. Different adapters are mapped to different +targets on this virtual SCSI bus. +.sp +If no +.I dev +option is present, +.B readom +will try to get the device from the +.B CDR_DEVICE +environment. +.sp +If the argument to the +.B dev= +option does not contain the characters ',', '/', '@' or ':', +it is interpreted as an label name that may be found in the file +/etc/wodim.conf (see FILES section). +.TP +.BI timeout= # +Set the default SCSI command timeout value to +.IR # " seconds. +The default SCSI command timeout is the minimum timeout used for sending +SCSI commands. +If a SCSI command fails due to a timeout, you may try to raise the +default SCSI command timeout above the timeout value of the failed command. +If the command runs correctly with a raised command timeout, +please report the better timeout value and the corresponding command to +the author of the program. +If no +.I timeout +option is present, a default timeout of 40 seconds is used. +.TP +.BI debug= "#, " -d +Set the misc debug value to # (with debug=#) or increment +the misc debug level by one (with -d). If you specify +.I -dd, +this equals to +.BI debug= 2. +This may help to find problems while opening a driver for libusal. +as well as with sector sizes and sector types. +Using +.B \-debug +slows down the process and may be the reason for a buffer underrun. +.TP +.BR kdebug= "#, " kd= # +Tell the +.BR usal -driver +to modify the kernel debug value while SCSI commands are running. +.TP +.BR \-silent ", " \-s +Do not print out a status report for failed SCSI commands. +.TP +.B \-v +Increment the level of general verbosity by one. +This is used e.g. to display the progress of the process. +.TP +.B \-V +Increment the verbose level with respect of SCSI command transport by one. +This helps to debug problems +during the process, that occur in the CD-Recorder. +If you get incomprehensible error messages you should use this flag +to get more detailed output. +.B \-VV +will show data buffer content in addition. +Using +.B \-V +or +.B \-VV +slows down the process. +.TP +.BI f= file +Specify the filename where the output should be written or the input should +be taken from. Using '-' as filename will cause +.B readom +to use +.BR stdout " resp. " stdin . +.TP +.B \-w +Switch to write mode. If this option is not present, +.B readom +reads from the specified device. +.TP +.B \-c2scan +Scans the whole CD or the range specified by the +.BI sectors= range +for C2 errors. C2 errors are errors that are uncorrectable after the second +stage of the 24/28 + 28/32 Reed Solomon correction system at audio level +(2352 bytes sector size). If an audio CD has C2 errors, interpolation is needed +to hide the errors. If a data CD has C2 errors, these errors are in most +cases corrected by the ECC/EDC code that makes 2352 bytes out of 2048 data +bytes. The ECC/EDC code should be able to correct about 100 C2 error bytes +per sector. +.sp +If you find C2 errors you may want to reduce the speed using the +.B speed= +option as C2 errors may be a result of dynamic unbalance on the medium. +.TP +.B \-scanbus +Scan all SCSI devices on all SCSI busses and print the inquiry +strings. This option may be used to find SCSI address of the +devices on a system. +The numbers printed out as labels are computed by: +.B "bus * 100 + target +.TP +.BI sectors= range +Specify a sector range that should be read. +The range is specified by the starting sector number, a minus sign and the +ending sector number. +The end sector is not included in the list, so +.BR sectors= 0-0 +will not read anything and may be used to check for a CD in the drive. +.TP +.BR speed= # +Set the speed factor of the read or write process to #. +# is an integer, representing a multiple of the audio speed. +This is about 150 KB/s for CD-ROM and about 172 KB/s for CD-Audio. +If no +.I speed +option is present, +.B readom +will use maximum speed. +Only MMC compliant drives will benefit from this option. +The speed of non MMC drives is not changed. +.sp +Using a lower speed may increase the readability of a CD or DVD. +.TP +.BR ts= # +Set the maximum transfer size for a single SCSI command to #. +The syntax for the +.B ts= +option is the same as for wodim fs=# or sdd bs=#. +.sp +If no +.B ts= +option has been specified, +.B readom +defaults to a transfer size of 256 kB. If libusal gets lower values from the +operating system, the value is reduced to the maximum value that is possible +with the current operating system. +Sometimes, it may help to further reduce the transfer size or to enhance it, +but note that it may take a long time to find a better value by experimenting +with the +.B ts= +option. +.TP +.B \-notrunc +Do not truncate the output file when opening it. +.TP +.B \-fulltoc +Retrieve a full TOC from the current disk and print it in hex. +.TP +.B \-clone +Do a clone read. Read the CD with all sub-channel data and a full TOC. +The full TOC data will be put into a file with similar name as with the +.B f= +option but the suffix +.B .toc +added. +.TP +.B \-noerror +Do not abort if the high level error checking in +.B readom +found an uncorrectable error in the data stream. +.TP +.B \-nocorr +Switch the drive into a mode where it ignores read errors in data sectors that +are a result of uncorrectable ECC/EDC errors before reading. +If +.B readom +completes, the error recovery mode of the drive is switched back to the remembered +old mode. +.TP +.BI retries= # +Set the retry count for high level retries in +.B readom +to +.IR # . +The default is to do 128 retries which may be too much if you like to read a CD +with many unreadable sectors. +.TP +.B \-overhead +Meter the SCSI command overhead time. +This is done by executing several commands 1000 times and printing the +total time used. If you divide the displayed times by 1000, you get +the average overhead time for a single command. +.TP +.BR meshpoints= # +Print read-speed at # locations. +The purpose of this option is to create a list of read speed values suitable +for e.g. +.BR gnuplot . +The speed values are calculated assuming that 1000 bytes are one kilobyte +as documented in the SCSI standard. +The output data created for this purpose is written to +.IR stdout . +.TP +.B \-factor +Output the speed values for +.BR meshpoints= # +as factor based on +.I "single speed +of the current medium. +This only works if +.B readom +is able to determine the current medium type. +.SH EXAMPLES +.PP +For all examples below, it will be assumed that the drive is +connected to the primary SCSI bus of the machine. The SCSI target id is +set to 2. +.PP +To read the complete media from a CD-ROM writing the data to the file +.IR cdimage.raw : +.PP + readom dev=2,0 f=cdimage.raw +.PP +To read sectors from range 150 ... 10000 from a CD-ROM writing the data to the file +.IR cdimage.raw : +.PP + readom dev=2,0 sectors=150-10000 f=cdimage.raw +.PP +To write the data from the file +.I cdimage.raw +(e.g. a filesystem image from +.BR genisoimage ) +to a DVD-RAM, call: +.PP + readom dev=2,0 -w f=cdimage.raw + +.SH ENVIRONMENT +.TP +.B RSH +If the +.B RSH +environment is present, the remote connection will not be created via +.BR rcmd (3) +but by calling the program pointed to by +.BR RSH . +Use e.g. +.BR RSH= /usr/bin/ssh +to create a secure shell connection. +.sp +Note that this forces +.B wodim +to create a pipe to the +.B rsh(1) +program and disallows +.B wodim +to directly access the network socket to the remote server. +This makes it impossible to set up performance parameters and slows down +the connection compared to a +.B root +initiated +.B rcmd(3) +connection. +.TP +.B RSCSI +If the +.B RSCSI +environment is present, the remote SCSI server will not be the program +.B /opt/schily/sbin/rscsi +but the program pointed to by +.BR RSCSI . +Note that the remote SCSI server program name will be ignored if you log in +using an account that has been created with a remote SCSI server program as +login shell. +.SH SEE ALSO +.BR wodim (1), +.BR genisoimage (1), +.BR rcmd (3), +.BR ssh (1). + +.SH NOTES +.PP +Unless you want to risk getting problems, +.B readom +should be run as root. If you don't want to allow users to become root on your system, +.B readom +may safely be installed suid root. +For more information see the additional notes of your system/program +distribution or README.suidroot which is part of the Cdrkit source. +.PP +Documentation of the +.B wodim +program contains more technical details which could also apply to +.B readom. + +.SH DIAGNOSTICS +.PP +.PP +A typical error message for a SCSI command looks like: +.sp +.RS +.nf +readom: I/O error. test unit ready: scsi sendcmd: no error +CDB: 00 20 00 00 00 00 +status: 0x2 (CHECK CONDITION) +Sense Bytes: 70 00 05 00 00 00 00 0A 00 00 00 00 25 00 00 00 00 00 +Sense Key: 0x5 Illegal Request, Segment 0 +Sense Code: 0x25 Qual 0x00 (logical unit not supported) Fru 0x0 +Sense flags: Blk 0 (not valid) +cmd finished after 0.002s timeout 40s +.fi +.sp +.RE +The first line gives information about the transport of the command. +The text after the first colon gives the error text for the system call +from the view of the kernel. It usually is: +.B "I/O error +unless other problems happen. The next words contain a short description for +the SCSI command that fails. The rest of the line tells you if there were +any problems for the transport of the command over the SCSI bus. +.B "fatal error +means that it was not possible to transport the command (i.e. no device present +at the requested SCSI address). +.PP +The second line prints the SCSI command descriptor block for the failed command. +.PP +The third line gives information on the SCSI status code returned by the +command, if the transport of the command succeeds. +This is error information from the SCSI device. +.PP +The fourth line is a hex dump of the auto request sense information for the +command. +.PP +The fifth line is the error text for the sense key if available, followed +by the segment number that is only valid if the command was a +.I copy +command. If the error message is not directly related to the current command, +the text +.I deferred error +is appended. +.PP +The sixth line is the error text for the sense code and the sense qualifier if available. +If the type of the device is known, the sense data is decoded from tables +in +.IR scsierrs.c " . +The text is followed by the error value for a field replaceable unit. +.PP +The seventh line prints the block number that is related to the failed command +and text for several error flags. The block number may not be valid. +.PP +The eight line reports the timeout set up for this command and the time +that the command really needed to complete. + +.SH BUGS +.PP +The +.B readom +program described here is the Cdrkit spinoff from the original +.B readcd +application (see AUTHOR section for details). It may contain bugs not present +in the original implementation. +.PP +It is definitely less portable than the original implementation. +.PP +For platform specific bugs, see the corresponding README.platform file in the +Cdrkit documentation (eg. README.linux). + +.SH "MAILING LISTS +If you want to actively take part on the development of readom, +you may join the developer mailing list via this URL: +.sp +.B +http://alioth.debian.org/mail/?group_id=31006 +.PP +The mail address of the list is: +.B +debburn-devel@lists.alioth.debian.org + + + + +.SH AUTHOR +.nf +J\*org Schilling +Seestr. 110 +D-13353 Berlin +Germany +.fi + +.PP +This is application is a spinoff from the original implementation of readcd delivered in +the cdrtools package [1] created by Joerg Schilling, who deserves the most credits +for its success. However, he is not involved into the development +of this spinoff and therefore he shall not be made responsible for any problem +caused by it. Do not try to get support from the original author! +.PP +Additional information can be found on: +.br +https://alioth.debian.org/projects/debburn/ +.PP +If you have support questions, send them to +.PP +.B +debburn-devel@lists.alioth.debian.org +.br +.PP +If you have definitely found a bug, send a mail to this list or to +.PP +.B +submit@bugs.debian.org +.br +.PP +writing at least a short description into the Subject and "Package: cdrkit" +into the first line of the mail body. +.SH SOURCES +.PP +.br +[1] Cdrtools 2.01.01a08 from May 2006, http://cdrecord.berlios.de + diff --git a/readom/readom.c b/readom/readom.c new file mode 100644 index 0000000..d52fe5d --- /dev/null +++ b/readom/readom.c @@ -0,0 +1,2194 @@ +/* + * This file has been modified for the cdrkit suite. + * + * The behaviour and appearence of the program code below can differ to a major + * extent from the version distributed by the original author(s). + * + * For details, see Changelog file distributed with the cdrkit package. If you + * received this file from another source then ask the distributing person for + * a log of modifications. + * + */ + +/* @(#)readcd.c 1.80 06/02/05 Copyright 1987, 1995-2006 J. Schilling */ +/* + * Skeleton for the use of the usal genearal SCSI - driver + * + * Copyright (c) 1987, 1995-2004 J. Schilling + */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; see the file COPYING. If not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <mconfig.h> +#include <stdio.h> +#include <standard.h> +#include <unixstd.h> +#include <stdxlib.h> +#include <strdefs.h> +#include <fctldefs.h> +#include <timedefs.h> +#include <signal.h> +#include <schily.h> +#ifdef HAVE_PRIV_H +#include <priv.h> +#endif + +#ifdef NEED_O_BINARY +#include <io.h> /* for setmode() prototype */ +#endif + +#include <usal/usalcmd.h> +#include <usal/scsireg.h> +#include <usal/scsitransp.h> + +#include "scsi_scan.h" +#include "scsimmc.h" +#define qpto96 __nothing__ +#include "wodim.h" +#include "defaults.h" +#undef qpto96 +#include "movesect.h" + +char cdr_version[] = "2.01.01a05"; + +#if defined(PROTOTYPES) +#define UINT_C(a) (a##u) +#define ULONG_C(a) (a##ul) +#define USHORT_C(a) (a##uh) +#define CONCAT(a, b) a##b +#else +#define UINT_C(a) ((unsigned)(a)) +#define ULONG_C(a) ((unsigned long)(a)) +#define USHORT_C(a) ((unsigned short)(a)) +/* CSTYLED */ +#define CONCAT(a, b) a/**/b +#endif + +extern BOOL getlong(char *, long *, long, long); +extern BOOL getint(char *, int *, int, int); + +typedef struct { + long start; + long end; + long sptr; /* sectors per transfer */ + BOOL askrange; + char *name; +} parm_t; + +typedef struct { + int errors; + int c2_errors; + int c2_maxerrs; + int c2_errsecs; + int c2_badsecs; + int secsize; + BOOL ismmc; +} rparm_t; + +struct exargs { + SCSI *usalp; + int old_secsize; + int flags; + int exflags; + char oerr[3]; +} exargs; + +BOOL cvt_cyls(void); +BOOL cvt_bcyls(void); +void print_defect_list(void); +static void usage(int ret); +static void intr(int sig); +static void exscsi(int excode, void *arg); +static void excdr(int excode, void *arg); +static int prstats(void); +static int prstats_silent(void); +static void dorw(SCSI *usalp, char *filename, char *sectors); +static void doit(SCSI *usalp); +static void read_disk(SCSI *usalp, parm_t *parmp); +#ifdef CLONE_WRITE +static void readcd_disk(SCSI *usalp, parm_t *parmp); +static void read_lin(SCSI *usalp, parm_t *parmp); +static int read_secheader(SCSI *usalp, long addr); +static int read_ftoc(SCSI *usalp, parm_t *parmp, BOOL do_sectype); +static void read_sectypes(SCSI *usalp, FILE *f); +static void get_sectype(SCSI *usalp, long addr, char *st); +#endif + +static void readc2_disk(SCSI *usalp, parm_t *parmp); +static int fread_data(SCSI *usalp, rparm_t *rp, caddr_t bp, long addr, + int cnt); +#ifdef CLONE_WRITE +static int fread_2448(SCSI *usalp, rparm_t *rp, caddr_t bp, long addr, + int cnt); +static int fread_2448_16(SCSI *usalp, rparm_t *rp, caddr_t bp, long addr, + int cnt); +static int fread_2352(SCSI *usalp, rparm_t *rp, caddr_t bp, long addr, + int cnt); +static int fread_lin(SCSI *usalp, rparm_t *rp, caddr_t bp, long addr, + int cnt); +#endif +static int bits(int c); +static int bitidx(int c); +static int fread_c2(SCSI *usalp, rparm_t *rp, caddr_t bp, long addr, + int cnt); + +static int fdata_null(rparm_t *rp, caddr_t bp, long addr, int cnt); +static int fdata_c2(rparm_t *rp, caddr_t bp, long addr, int cnt); + +#ifdef used +static int read_scsi_g1(SCSI *usalp, caddr_t bp, long addr, int cnt); +#endif + +int write_scsi(SCSI *usalp, caddr_t bp, long addr, int cnt); +int write_g0(SCSI *usalp, caddr_t bp, long addr, int cnt); +int write_g1(SCSI *usalp, caddr_t bp, long addr, int cnt); + +#ifdef used +static void Xrequest_sense(SCSI *usalp); +#endif +static int read_retry(SCSI *usalp, caddr_t bp, long addr, long cnt, + int (*rfunc)(SCSI *usalp, rparm_t *rp, caddr_t bp, long addr, int cnt), + rparm_t *rp); +static void read_generic(SCSI *usalp, parm_t *parmp, + int (*rfunc)(SCSI *usalp, rparm_t *rp, caddr_t bp, long addr, int cnt), + rparm_t *rp, + int (*dfunc)(rparm_t *rp, caddr_t bp, long addr, int cnt)); +static void write_disk(SCSI *usalp, parm_t *parmp); +static int choice(int n); +static void ra(SCSI *usalp); + +int read_da(SCSI *usalp, caddr_t bp, long addr, int cnt, int framesize, + int subcode); +int read_cd(SCSI *usalp, caddr_t bp, long addr, int cnt, int framesize, + int data, int subch); + +static void oldmode(SCSI *usalp, int *errp, int *retrp); +static void domode(SCSI *usalp, int err, int retr); + +static void qpto96(Uchar *sub, Uchar *subq, int dop); +static void ovtime(SCSI *usalp); +static void add_bad(long addr); +static void print_bad(void); + +struct timeval starttime; +struct timeval stoptime; +int didintr; +int exsig; + +char *Sbuf; +long Sbufsize; + +/*#define MAX_RETRY 32*/ +#define MAX_RETRY 128 + +int help; +int xdebug; +int lverbose; +int quiet; +BOOL is_suid; +BOOL is_cdrom; +BOOL is_dvd; +BOOL do_write; +BOOL c2scan; +BOOL fulltoc; +BOOL clonemode; +BOOL noerror; +BOOL nocorr; +BOOL notrunc; +int retries = MAX_RETRY; +int maxtry = 0; +int meshpoints; +BOOL do_factor; + +struct scsi_format_data fmt; + +/*XXX*/EXPORT BOOL cvt_cyls(void) { return (FALSE); } +/*XXX*/EXPORT BOOL cvt_bcyls(void) { return (FALSE); } +/*XXX*/EXPORT void print_defect_list(void) {} + +static void +usage(int ret) +{ + fprintf(stderr, "Usage:\treadom [options]\n"); + fprintf(stderr, "options:\n"); + fprintf(stderr, "\t-version print version information and exit\n"); + fprintf(stderr, "\tdev=target SCSI target to use\n"); + fprintf(stderr, "\tf=filename Name of file to read/write\n"); + fprintf(stderr, "\tsectors=range Range of sectors to read/write\n"); + fprintf(stderr, "\tspeed=# set speed of drive (MMC only)\n"); + fprintf(stderr, "\tts=# set maximum transfer size for a single SCSI command\n"); + fprintf(stderr, "\t-w Switch to write mode\n"); + fprintf(stderr, "\t-c2scan Do a C2 error scan\n"); +#ifdef CLONE_WRITE + fprintf(stderr, "\t-fulltoc Retrieve the full TOC\n"); + fprintf(stderr, "\t-clone Retrieve the full TOC and all data\n"); +#endif + fprintf(stderr, "\ttimeout=# set the default SCSI command timeout to #.\n"); + fprintf(stderr, "\tdebug=#,-d Set to # or increment misc debug level\n"); + fprintf(stderr, "\tkdebug=#,kd=# do Kernel debugging\n"); + fprintf(stderr, "\t-quiet,-q be more quiet in error retry mode\n"); + fprintf(stderr, "\t-verbose,-v increment general verbose level by one\n"); + fprintf(stderr, "\t-Verbose,-V increment SCSI command transport verbose level by one\n"); + fprintf(stderr, "\t-silent,-s do not print status of failed SCSI commands\n"); + fprintf(stderr, "\t-scanbus scan the SCSI bus and exit\n"); + fprintf(stderr, "\t-noerror do not abort on error\n"); +#ifdef CLONE_WRITE + fprintf(stderr, "\t-nocorr do not apply error correction in drive\n"); +#endif + fprintf(stderr, "\t-notrunc do not truncate outputfile in read mode\n"); + fprintf(stderr, "\tretries=# set retry count (default is %d)\n", retries); + fprintf(stderr, "\t-overhead meter SCSI command overhead times\n"); + fprintf(stderr, "\tmeshpoints=# print read-speed at # locations\n"); + fprintf(stderr, "\t-factor try to use speed factor with meshpoints=# if possible\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "sectors=0-0 will read nothing, sectors=0-1 will read one sector starting from 0\n"); + exit(ret); +} + +/* CSTYLED */ +char opts[] = "debug#,d+,kdebug#,kd#,timeout#,quiet,q,verbose+,v+,Verbose+,V+,x+,xd#,silent,s,help,h,version,scanbus,dev*,sectors*,w,c2scan,fulltoc,clone,noerror,nocorr,notrunc,retries#,factor,f*,speed#,ts&,overhead,meshpoints#"; + +int +main(int argc, char *argv[]) +{ + char *dev = NULL; + int fcount; + int cac; + char * const *cav; + int scsibus = -1; + int target = -1; + int lun = -1; + int silent = 0; + int verbose = 0; + int kdebug = 0; + int debug = 0; + int deftimeout = 40; + int pversion = 0; + int scanbus = 0; + int speed = -1; + int dooverhead = 0; + SCSI *usalp; + char *filename = NULL; + char *sectors = NULL; + + save_args(argc, argv); + + cac = --argc; + cav = ++argv; + + if (getallargs(&cac, &cav, opts, + &debug, &debug, + &kdebug, &kdebug, + &deftimeout, + &quiet, &quiet, + &lverbose, &lverbose, + &verbose, &verbose, + &xdebug, &xdebug, + &silent, &silent, + &help, &help, &pversion, + &scanbus, &dev, §ors, &do_write, + &c2scan, + &fulltoc, &clonemode, + &noerror, &nocorr, + ¬runc, &retries, &do_factor, &filename, + &speed, getnum, &Sbufsize, + &dooverhead, &meshpoints) < 0) { + errmsgno(EX_BAD, "Bad flag: %s.\n", cav[0]); + usage(EX_BAD); + } + if (help) + usage(0); + if (pversion) { + printf("readcd %s is not what you see here. This line is only a fake for too clever\n" + "GUIs and other frontend applications. In fact, this program is:\n", cdr_version); + + printf("readom " CDRKIT_VERSION " (" HOST_SYSTEM ")\n" + "Copyright (C) 1987, 1995-2006 Joerg Schilling\n" + "Copyright (C) 2006 Cdrkit maintainers\n" + "(modified version of <censored> -- " + "don't bother Joerg Schilling with problems)\n"); + exit(0); + } + + fcount = 0; + cac = argc; + cav = argv; + + while (getfiles(&cac, &cav, opts) > 0) { + fcount++; + if (fcount == 1) { + if (*astoi(cav[0], &target) != '\0') { + errmsgno(EX_BAD, + "Target '%s' is not a Number.\n", + cav[0]); + usage(EX_BAD); + /* NOTREACHED */ + } + } + if (fcount == 2) { + if (*astoi(cav[0], &lun) != '\0') { + errmsgno(EX_BAD, + "Lun is '%s' not a Number.\n", + cav[0]); + usage(EX_BAD); + /* NOTREACHED */ + } + } + if (fcount == 3) { + if (*astoi(cav[0], &scsibus) != '\0') { + errmsgno(EX_BAD, + "Scsibus is '%s' not a Number.\n", + cav[0]); + usage(EX_BAD); + /* NOTREACHED */ + } + } + cac--; + cav++; + } +/*fprintf(stderr, "dev: '%s'\n", dev);*/ + if (!scanbus) + cdr_defaults(&dev, NULL, NULL, NULL); + if (debug) { + printf("dev: '%s'\n", dev); + } + if (!scanbus && dev == NULL && + scsibus == -1 && (target == -1 || lun == -1)) { + errmsgno(EX_BAD, "No SCSI device specified.\n"); + usage(EX_BAD); + } + if (dev || scanbus) { + char errstr[80]; + + /* + * Call usal_remote() to force loading the remote SCSI transport + * library code that is located in librusal instead of the dummy + * remote routines that are located inside libusal. + */ + usal_remote(); + if (dev != NULL && + ((strncmp(dev, "HELP", 4) == 0) || + (strncmp(dev, "help", 4) == 0))) { + usal_help(stderr); + exit(0); + } + if ((usalp = usal_open(dev, errstr, sizeof (errstr), debug, lverbose)) == (SCSI *)0) { + int err = geterrno(); + + errmsgno(err, "%s%sCannot open SCSI driver.\n", errstr, errstr[0]?". ":""); + errmsgno(EX_BAD, "For possible targets try 'wodim -scanbus'.%s\n", + geteuid() ? " Make sure you are root.":""); + errmsgno(EX_BAD, "For possible transport specifiers try 'wodim dev=help'.\n"); + exit(err); + } + } else { + if (scsibus == -1 && target >= 0 && lun >= 0) + scsibus = 0; + + usalp = usal_smalloc(); + usalp->debug = debug; + usalp->kdebug = kdebug; + + usal_settarget(usalp, scsibus, target, lun); + if (usal__open(usalp, NULL) <= 0) + comerr("Cannot open SCSI driver.\n"); + } + usalp->silent = silent; + usalp->verbose = verbose; + usalp->debug = debug; + usalp->kdebug = kdebug; + usal_settimeout(usalp, deftimeout); + + if (Sbufsize == 0) + Sbufsize = 256*1024L; + Sbufsize = usal_bufsize(usalp, Sbufsize); + if ((Sbuf = usal_getbuf(usalp, Sbufsize)) == NULL) + comerr("Cannot get SCSI I/O buffer.\n"); + +#ifdef HAVE_PRIV_SET + is_suid = priv_ineffect(PRIV_FILE_DAC_READ) && + !priv_ineffect(PRIV_PROC_SETID); + /* + * Give up privs we do not need anymore. + * We no longer need: + * file_dac_read,net_privaddr + * We still need: + * sys_devices + */ + priv_set(PRIV_OFF, PRIV_EFFECTIVE, + PRIV_FILE_DAC_READ, PRIV_NET_PRIVADDR, NULL); + priv_set(PRIV_OFF, PRIV_PERMITTED, + PRIV_FILE_DAC_READ, PRIV_NET_PRIVADDR, NULL); + priv_set(PRIV_OFF, PRIV_INHERITABLE, + PRIV_FILE_DAC_READ, PRIV_NET_PRIVADDR, PRIV_SYS_DEVICES, NULL); +#endif + /* + * This is only for OS that do not support fine grained privs. + */ + if (!is_suid) + is_suid = geteuid() != getuid(); + /* + * We don't need root privilleges anymore. + */ +#ifdef HAVE_SETREUID + if (setreuid(-1, getuid()) < 0) +#else +#ifdef HAVE_SETEUID + if (seteuid(getuid()) < 0) +#else + if (setuid(getuid()) < 0) +#endif +#endif + comerr("Panic cannot set back effective uid.\n"); + + /* code to use SCG */ + + if (scanbus) { + select_target(usalp, stdout); + exit(0); + } + do_inquiry(usalp, FALSE); + allow_atapi(usalp, TRUE); /* Try to switch to 10 byte mode cmds */ + if (is_mmc(usalp, NULL, NULL)) { + int rspeed; + int wspeed; + /* + * At this point we know that we have a SCSI-3/mmc compliant drive. + * Unfortunately ATAPI drives violate the SCSI spec in returning + * a response data format of '1' which from the SCSI spec would + * tell us not to use the "PF" bit in mode select. As ATAPI drives + * require the "PF" bit to be set, we 'correct' the inquiry data. + */ + if (usalp->inq->data_format < 2) + usalp->inq->data_format = 2; + + if ((rspeed = get_curprofile(usalp)) >= 0) { + if (rspeed >= 0x08 && rspeed < 0x10) + is_cdrom = TRUE; + if (rspeed >= 0x10 && rspeed < 0x20) + is_dvd = TRUE; + } else { + BOOL dvd; + + mmc_check(usalp, NULL, NULL, NULL, NULL, &dvd, NULL); + if (dvd == FALSE) { + is_cdrom = TRUE; + } else { + char xb[32]; + + if (read_dvd_structure(usalp, (caddr_t)xb, 32, 0, 0, 0) >= 0) { + /* + * If read DVD structure is supported and works, then + * we must have a DVD media in the drive. Signal to + * use the DVD driver. + */ + is_dvd = TRUE; + } else { + is_cdrom = TRUE; + } + } + } + + if (speed > 0) + speed *= 177; + if (speed > 0xFFFF || speed < 0) + speed = 0xFFFF; + scsi_set_speed(usalp, speed, speed, ROTCTL_CLV); + if (scsi_get_speed(usalp, &rspeed, &wspeed) >= 0) { + fprintf(stderr, "Read speed: %5d kB/s (CD %3dx, DVD %2dx).\n", + rspeed, rspeed/176, rspeed/1385); + fprintf(stderr, "Write speed: %5d kB/s (CD %3dx, DVD %2dx).\n", + wspeed, wspeed/176, wspeed/1385); + } + } + exargs.usalp = usalp; + exargs.old_secsize = -1; +/* exargs.flags = flags;*/ + exargs.oerr[2] = 0; + + /* + * Install exit handler before we change the drive status. + */ + on_comerr(exscsi, &exargs); + signal(SIGINT, intr); + signal(SIGTERM, intr); + + if (dooverhead) { + ovtime(usalp); + comexit(0); + } + + if (is_suid) { + if (usalp->inq->type != INQ_ROMD) + comerrno(EX_BAD, "Not root. Will only work on CD-ROM in suid/priv mode\n"); + } + + if (filename || sectors || c2scan || meshpoints || fulltoc || clonemode) { + dorw(usalp, filename, sectors); + } else { + doit(usalp); + } + comexit(0); + return (0); +} + +/* + * XXX Leider kann man vim Signalhandler keine SCSI Kommandos verschicken + * XXX da meistens das letzte SCSI Kommando noch laeuft. + * XXX Eine Loesung waere ein Abort Callback in SCSI *. + */ +static void +intr(int sig) +{ + didintr++; + exsig = sig; +/* comexit(sig);*/ +} + +/* ARGSUSED */ +static void +exscsi(int excode, void *arg) +{ + struct exargs *exp = (struct exargs *)arg; + int i; + + /* + * Try to restore the old sector size. + */ + if (exp != NULL && exp->exflags == 0) { + for (i = 0; i < 10*100; i++) { + if (!exp->usalp->running) + break; + if (i == 10) { + errmsgno(EX_BAD, + "Waiting for current SCSI command to finish.\n"); + } + usleep(100000); + } + + if (!exp->usalp->running) { + if (exp->oerr[2] != 0) { + domode(exp->usalp, exp->oerr[0], exp->oerr[1]); + } + if (exp->old_secsize > 0 && exp->old_secsize != 2048) + select_secsize(exp->usalp, exp->old_secsize); + } + exp->exflags++; /* Make sure that it only get called once */ + } +} + +static void +excdr(int excode, void *arg) +{ + exscsi(excode, arg); + +#ifdef needed + /* Do several other restores/statistics here (see cdrecord.c) */ +#endif +} + +/* + * Return milliseconds since start time. + */ +static int +prstats(void) +{ + int sec; + int usec; + int tmsec; + + if (gettimeofday(&stoptime, (struct timezone *)0) < 0) + comerr("Cannot get time\n"); + + sec = stoptime.tv_sec - starttime.tv_sec; + usec = stoptime.tv_usec - starttime.tv_usec; + tmsec = sec*1000 + usec/1000; +#ifdef lint + tmsec = tmsec; /* Bisz spaeter */ +#endif + if (usec < 0) { + sec--; + usec += 1000000; + } + + fprintf(stderr, "Time total: %d.%03dsec\n", sec, usec/1000); + return (1000*sec + (usec / 1000)); +} + +/* + * Return milliseconds since start time, but be silent this time. + */ +static int +prstats_silent(void) +{ + int sec; + int usec; + int tmsec; + + if (gettimeofday(&stoptime, (struct timezone *)0) < 0) + comerr("Cannot get time\n"); + + sec = stoptime.tv_sec - starttime.tv_sec; + usec = stoptime.tv_usec - starttime.tv_usec; + tmsec = sec*1000 + usec/1000; +#ifdef lint + tmsec = tmsec; /* Bisz spaeter */ +#endif + if (usec < 0) { + sec--; + usec += 1000000; + } + + return (1000*sec + (usec / 1000)); +} + +static void +dorw(SCSI *usalp, char *filename, char *sectors) +{ + parm_t params; + char *p = NULL; + + params.start = 0; + params.end = -1; + params.sptr = -1; + params.askrange = FALSE; + params.name = NULL; + + if (filename) + params.name = filename; + if (meshpoints > 0) { + if (params.name == NULL) + params.name = "/dev/null"; + } + if (sectors) + p = astol(sectors, ¶ms.start); + if (p && *p == '-') + p = astol(++p, ¶ms.end); + if (p && *p != '\0') + comerrno(EX_BAD, "Not a valid sector range '%s'\n", sectors); + + if (!wait_unit_ready(usalp, 60)) + comerrno(EX_BAD, "Device not ready.\n"); + +#ifdef CLONE_WRITE + if (fulltoc) { + if (params.name == NULL) + params.name = "/dev/null"; + read_ftoc(usalp, ¶ms, FALSE); + } else if (clonemode) { + if (!is_mmc(usalp, NULL, NULL)) + comerrno(EX_BAD, "Unsupported device for clone mode.\n"); + noerror = TRUE; + if (retries == MAX_RETRY) + retries = 10; + if (params.name == NULL) + params.name = "/dev/null"; + + if (read_ftoc(usalp, ¶ms, TRUE) < 0) + comerrno(EX_BAD, "Read fulltoc problems.\n"); + readcd_disk(usalp, ¶ms); + } else +#endif + if (c2scan) { + noerror = TRUE; + if (retries == MAX_RETRY) + retries = 10; + if (params.name == NULL) + params.name = "/dev/null"; + readc2_disk(usalp, ¶ms); + } else if (do_write) + write_disk(usalp, ¶ms); + else + read_disk(usalp, ¶ms); +} + +static void +doit(SCSI *usalp) +{ + int i = 0; + parm_t params; + + params.start = 0; + params.end = -1; + params.sptr = -1; + params.askrange = TRUE; + params.name = "/dev/null"; + + for (;;) { + if (!wait_unit_ready(usalp, 60)) + comerrno(EX_BAD, "Device not ready.\n"); + + printf("0:read 1:veri 2:erase 3:read buffer 4:cache 5:ovtime 6:cap\n"); + printf("7:wne 8:floppy 9:verify 10:checkcmds 11:read disk 12:write disk\n"); + printf("13:scsireset 14:seektest 15: readda 16: reada 17: c2err\n"); +#ifdef CLONE_WRITE + printf("18:readom 19: lin 20: full toc\n"); +#endif + + getint("Enter selection:", &i, 0, 20); + if (didintr) + return; + + switch (i) { + + case 5: ovtime(usalp); break; + case 11: read_disk(usalp, 0); break; + case 12: write_disk(usalp, 0); break; + case 15: ra(usalp); break; +/* case 16: reada_disk(usalp, 0, 0); break;*/ + case 17: readc2_disk(usalp, ¶ms); break; +#ifdef CLONE_WRITE + case 18: readcd_disk(usalp, 0); break; + case 19: read_lin(usalp, 0); break; + case 20: read_ftoc(usalp, 0, FALSE); break; +#endif + } + } +} + +static void +read_disk(SCSI *usalp, parm_t *parmp) +{ + rparm_t rp; + + read_capacity(usalp); + print_capacity(usalp, stderr); + + rp.errors = 0; + rp.c2_errors = 0; + rp.c2_maxerrs = 0; + rp.c2_errsecs = 0; + rp.c2_badsecs = 0; + rp.secsize = usalp->cap->c_bsize; + + read_generic(usalp, parmp, fread_data, &rp, fdata_null); +} + +#ifdef CLONE_WRITE +static void +readcd_disk(SCSI *usalp, parm_t *parmp) +{ + rparm_t rp; + int osecsize = 2048; + int oerr = 0; + int oretr = 10; + int (*funcp)(SCSI *_usalp, rparm_t *_rp, caddr_t bp, long addr, int cnt); + + usalp->silent++; + if (read_capacity(usalp) >= 0) + osecsize = usalp->cap->c_bsize; + usalp->silent--; + if (osecsize != 2048) + select_secsize(usalp, 2048); + + read_capacity(usalp); + print_capacity(usalp, stderr); + + rp.errors = 0; + rp.c2_errors = 0; + rp.c2_maxerrs = 0; + rp.c2_errsecs = 0; + rp.c2_badsecs = 0; + rp.secsize = 2448; + rp.ismmc = is_mmc(usalp, NULL, NULL); + funcp = fread_2448; + + wait_unit_ready(usalp, 10); + if (fread_2448(usalp, &rp, Sbuf, 0, 0) < 0) { + errmsgno(EX_BAD, "read 2448 failed\n"); + if (rp.ismmc && + fread_2448_16(usalp, &rp, Sbuf, 0, 0) >= 0) { + errmsgno(EX_BAD, "read 2448_16 : OK\n"); + + funcp = fread_2448_16; + } + } + + oldmode(usalp, &oerr, &oretr); + exargs.oerr[0] = oerr; + exargs.oerr[1] = oretr; + exargs.oerr[2] = 0xFF; + if (parmp == NULL) /* XXX Nur am Anfang!!! */ + domode(usalp, -1, -1); + else + domode(usalp, nocorr?0x21:0x20, 10); + + read_generic(usalp, parmp, funcp, &rp, fdata_null); + if (osecsize != 2048) + select_secsize(usalp, osecsize); + domode(usalp, oerr, oretr); +} + +/* ARGSUSED */ +static void +read_lin(SCSI *usalp, parm_t *parmp) +{ + parm_t parm; + rparm_t rp; + + read_capacity(usalp); + print_capacity(usalp, stderr); + + parm.start = ULONG_C(0xF0000000); + parm.end = ULONG_C(0xFF000000); + parm.name = "DDD"; + + rp.errors = 0; + rp.c2_errors = 0; + rp.c2_maxerrs = 0; + rp.c2_errsecs = 0; + rp.c2_badsecs = 0; + rp.secsize = 2448; + rp.ismmc = is_mmc(usalp, NULL, NULL); + domode(usalp, -1, -1); + read_generic(usalp, &parm, fread_lin, &rp, fdata_null); +} + +static int +read_secheader(SCSI *usalp, long addr) +{ + rparm_t rp; + int osecsize = 2048; + int ret = 0; + + usalp->silent++; + if (read_capacity(usalp) >= 0) + osecsize = usalp->cap->c_bsize; + usalp->silent--; + if (osecsize != 2048) + select_secsize(usalp, 2048); + + read_capacity(usalp); + + rp.errors = 0; + rp.c2_errors = 0; + rp.c2_maxerrs = 0; + rp.c2_errsecs = 0; + rp.c2_badsecs = 0; + rp.secsize = 2352; + rp.ismmc = is_mmc(usalp, NULL, NULL); + + wait_unit_ready(usalp, 10); + + fillbytes(Sbuf, 2352, '\0'); + if (fread_2352(usalp, &rp, Sbuf, addr, 1) < 0) { + ret = -1; + } + if (osecsize != 2048) + select_secsize(usalp, osecsize); + return (ret); +} + +/* ARGSUSED */ +static int +read_ftoc(SCSI *usalp, parm_t *parmp, BOOL do_sectype) +{ + FILE *f; + int i; + char filename[1024]; + struct tocheader *tp; + char *p; + char xb[256]; + int len; + char xxb[10000]; + + + strcpy(filename, "toc.dat"); + if (strcmp(parmp->name, "/dev/null") != 0) { + + len = strlen(parmp->name); + if (len > (sizeof (filename)-5)) { + len = sizeof (filename)-5; + } + snprintf(filename, sizeof (filename), "%.*s.toc", len, parmp->name); + } + + tp = (struct tocheader *)xb; + + fillbytes((caddr_t)xb, sizeof (xb), '\0'); + if (read_toc(usalp, xb, 0, sizeof (struct tocheader), 0, FMT_FULLTOC) < 0) { + if (usalp->silent == 0 || usalp->verbose > 0) + errmsgno(EX_BAD, "Cannot read TOC header\n"); + return (-1); + } + len = a_to_u_2_byte(tp->len) + sizeof (struct tocheader)-2; + fprintf(stderr, "TOC len: %d. First Session: %d Last Session: %d.\n", len, tp->first, tp->last); + + if (read_toc(usalp, xxb, 0, len, 0, FMT_FULLTOC) < 0) { + if (len & 1) { + /* + * Work around a bug in some operating systems that do not + * handle odd byte DMA correctly for ATAPI drives. + */ + wait_unit_ready(usalp, 30); + read_toc(usalp, xb, 0, sizeof (struct tocheader), 0, FMT_FULLTOC); + wait_unit_ready(usalp, 30); + if (read_toc(usalp, xxb, 0, len+1, 0, FMT_FULLTOC) >= 0) { + goto itworked; + } + } + if (usalp->silent == 0) + errmsgno(EX_BAD, "Cannot read full TOC\n"); + return (-1); + } + +itworked: + f = fileopen(filename, "wctb"); + + if (f == NULL) + comerr("Cannot open '%s'.\n", filename); + filewrite(f, xxb, len); + if (do_sectype) + read_sectypes(usalp, f); + fflush(f); + fclose(f); + + p = &xxb[4]; + for (; p < &xxb[len]; p += 11) { + for (i = 0; i < 11; i++) + fprintf(stderr, "%02X ", p[i] & 0xFF); + fprintf(stderr, "\n"); + } + /* + * List all lead out start times to give information about multi + * session disks. + */ + p = &xxb[4]; + for (; p < &xxb[len]; p += 11) { + if ((p[3] & 0xFF) == 0xA2) { + fprintf(stderr, "Lead out %d: %ld\n", p[0], msf_to_lba(p[8], p[9], p[10], TRUE)); + } + } + return (0); +} + +static void +read_sectypes(SCSI *usalp, FILE *f) +{ + char sect; + + sect = SECT_AUDIO; + get_sectype(usalp, 4, §); + if (f != NULL) + filewrite(f, §, 1); + if (xdebug) + usal_prbytes("sec 0", (Uchar *)Sbuf, 16); + + sect = SECT_AUDIO; + get_sectype(usalp, usalp->cap->c_baddr-4, §); + if (f != NULL) + filewrite(f, §, 1); + if (xdebug) { + usal_prbytes("sec E", (Uchar *)Sbuf, 16); + fprintf(stderr, "baddr: %ld\n", (long)usalp->cap->c_baddr); + } +} + +static void +get_sectype(SCSI *usalp, long addr, char *st) +{ + char *synchdr = "\0\377\377\377\377\377\377\377\377\377\377\0"; + int sectype = SECT_AUDIO; + int i; + long raddr = addr; +#define _MAX_TRY_ 20 + + usalp->silent++; + for (i = 0; i < _MAX_TRY_ && read_secheader(usalp, raddr) < 0; i++) { + if (addr == 0) + raddr++; + else + raddr--; + } + usalp->silent--; + if (i >= _MAX_TRY_) { + fprintf(stderr, "Sectype (%ld) is CANNOT\n", addr); + return; + } else if (i > 0) { + fprintf(stderr, "Sectype (%ld) needed %d retries\n", addr, i); + } +#undef _MAX_TRY_ + + if (cmpbytes(Sbuf, synchdr, 12) < 12) { + if (xdebug) + fprintf(stderr, "Sectype (%ld) is AUDIO\n", addr); + if (st) + *st = SECT_AUDIO; + return; + } + if (xdebug) + fprintf(stderr, "Sectype (%ld) is DATA\n", addr); + if (Sbuf[15] == 0) { + if (xdebug) + fprintf(stderr, "Sectype (%ld) is MODE 0\n", addr); + sectype = SECT_MODE_0; + + } else if (Sbuf[15] == 1) { + if (xdebug) + fprintf(stderr, "Sectype (%ld) is MODE 1\n", addr); + sectype = SECT_ROM; + + } else if (Sbuf[15] == 2) { + if (xdebug) + fprintf(stderr, "Sectype (%ld) is MODE 2\n", addr); + + if ((Sbuf[16+2] & 0x20) == 0 && + (Sbuf[16+4+2] & 0x20) == 0) { + if (xdebug) + fprintf(stderr, "Sectype (%ld) is MODE 2 form 1\n", addr); + sectype = SECT_MODE_2_F1; + + } else if ((Sbuf[16+2] & 0x20) != 0 && + (Sbuf[16+4+2] & 0x20) != 0) { + if (xdebug) + fprintf(stderr, "Sectype (%ld) is MODE 2 form 2\n", addr); + sectype = SECT_MODE_2_F2; + } else { + if (xdebug) + fprintf(stderr, "Sectype (%ld) is MODE 2 formless\n", addr); + sectype = SECT_MODE_2; + } + } else { + fprintf(stderr, "Sectype (%ld) is UNKNOWN\n", addr); + } + if (st) + *st = sectype; + if (xdebug) + fprintf(stderr, "Sectype (%ld) is 0x%02X\n", addr, sectype); +} + +#endif /* CLONE_WRITE */ + +char zeroblk[512]; + +static void +readc2_disk(SCSI *usalp, parm_t *parmp) +{ + rparm_t rp; + int osecsize = 2048; + int oerr = 0; + int oretr = 10; + + usalp->silent++; + if (read_capacity(usalp) >= 0) + osecsize = usalp->cap->c_bsize; + usalp->silent--; + if (osecsize != 2048) + select_secsize(usalp, 2048); + + read_capacity(usalp); + print_capacity(usalp, stderr); + + rp.errors = 0; + rp.c2_errors = 0; + rp.c2_maxerrs = 0; + rp.c2_errsecs = 0; + rp.c2_badsecs = 0; + rp.secsize = 2352 + 294; + rp.ismmc = is_mmc(usalp, NULL, NULL); + + oldmode(usalp, &oerr, &oretr); + exargs.oerr[0] = oerr; + exargs.oerr[1] = oretr; + exargs.oerr[2] = 0xFF; + domode(usalp, 0x21, 10); + + + read_generic(usalp, parmp, fread_c2, &rp, fdata_c2); + if (osecsize != 2048) + select_secsize(usalp, osecsize); + domode(usalp, oerr, oretr); + + printf("Total of %d hard read errors.\n", rp.errors); + printf("C2 errors total: %d bytes in %d sectors on disk\n", rp.c2_errors, rp.c2_errsecs); + printf("C2 errors rate: %f%% \n", (100.0*rp.c2_errors)/usalp->cap->c_baddr/2352); + printf("C2 errors on worst sector: %d, sectors with 100+ C2 errors: %d\n", rp.c2_maxerrs, rp.c2_badsecs); +} + +/* ARGSUSED */ +static int +fread_data(SCSI *usalp, rparm_t *rp, caddr_t bp, long addr, int cnt) +{ + return (read_g1(usalp, bp, addr, cnt)); +} + +#ifdef CLONE_WRITE +static int +fread_2448(SCSI *usalp, rparm_t *rp, caddr_t bp, long addr, int cnt) +{ + if (rp->ismmc) { + return (read_cd(usalp, bp, addr, cnt, rp->secsize, + /* Sync + all headers + user data + EDC/ECC */ + (1 << 7 | 3 << 5 | 1 << 4 | 1 << 3), + /* plus all subchannels RAW */ + 1)); + } else { + return (read_da(usalp, bp, addr, cnt, rp->secsize, + /* Sync + all headers + user data + EDC/ECC + all subch */ + 0x02)); + } +} + +static int +fread_2448_16(SCSI *usalp, rparm_t *rp, caddr_t bp, long addr, int cnt) +{ + + if (rp->ismmc) { + track_t trackdesc; + int ret; + int i; + char *p; + + trackdesc.isecsize = 2368; + trackdesc.secsize = 2448; + ret = read_cd(usalp, bp, addr, cnt, 2368, + /* Sync + all headers + user data + EDC/ECC */ + (1 << 7 | 3 << 5 | 1 << 4 | 1 << 3), + /* subchannels P/Q */ + 2); + if (ret < 0) + return (ret); + + scatter_secs(&trackdesc, bp, cnt); + for (i = 0, p = bp+2352; i < cnt; i++) { +#ifdef more_than_q_sub + if ((p[15] & 0x80) != 0) + printf("P"); +#endif + /* + * As the drives don't return P-sub, we check + * whether the index equals 0. + */ + qpto96((Uchar *)p, (Uchar *)p, p[2] == 0); + p += 2448; + } + return (ret); + } else { + comerrno(EX_BAD, "Cannot fread_2448_16 on non MMC drives\n"); + + return (read_da(usalp, bp, addr, cnt, rp->secsize, + /* Sync + all headers + user data + EDC/ECC + all subch */ + 0x02)); + } +} + +static int +fread_2352(SCSI *usalp, rparm_t *rp, caddr_t bp, long addr, int cnt) +{ + if (rp->ismmc) { + return (read_cd(usalp, bp, addr, cnt, rp->secsize, + /* Sync + all headers + user data + EDC/ECC */ + (1 << 7 | 3 << 5 | 1 << 4 | 1 << 3), + /* NO subchannels */ + 0)); + } else { + comerrno(EX_BAD, "Cannot fread_2352 on non MMC drives\n"); + + return (read_da(usalp, bp, addr, cnt, rp->secsize, + /* Sync + all headers + user data + EDC/ECC + all subch */ + 0x02)); + } +} + +static int +fread_lin(SCSI *usalp, rparm_t *rp, caddr_t bp, long addr, int cnt) +{ + if (addr != ULONG_C(0xF0000000)) + addr = ULONG_C(0xFFFFFFFF); + + return (read_cd(usalp, bp, addr, cnt, rp->secsize, + /* Sync + all headers + user data + EDC/ECC */ + (1 << 7 | 3 << 5 | 1 << 4 | 1 << 3), + /* plus all subchannels RAW */ + 1)); +} +#endif /* CLONE_WRITE */ + +static int +bits(int c) +{ + int n = 0; + + if (c & 0x01) + n++; + if (c & 0x02) + n++; + if (c & 0x04) + n++; + if (c & 0x08) + n++; + if (c & 0x10) + n++; + if (c & 0x20) + n++; + if (c & 0x40) + n++; + if (c & 0x80) + n++; + return (n); +} + +static int +bitidx(int c) +{ + if (c & 0x80) + return (0); + if (c & 0x40) + return (1); + if (c & 0x20) + return (2); + if (c & 0x10) + return (3); + if (c & 0x08) + return (4); + if (c & 0x04) + return (5); + if (c & 0x02) + return (6); + if (c & 0x01) + return (7); + return (-1); +} + +static int +fread_c2(SCSI *usalp, rparm_t *rp, caddr_t bp, long addr, int cnt) +{ + if (rp->ismmc) { + return (read_cd(usalp, bp, addr, cnt, rp->secsize, + /* Sync + all headers + user data + EDC/ECC + C2 */ +/* (1 << 7 | 3 << 5 | 1 << 4 | 1 << 3 | 2 << 1),*/ + (1 << 7 | 3 << 5 | 1 << 4 | 1 << 3 | 1 << 1), + /* without subchannels */ + 0)); + } else { + return (read_da(usalp, bp, addr, cnt, rp->secsize, + /* Sync + all headers + user data + EDC/ECC + C2 */ + 0x04)); + } +} + +/* ARGSUSED */ +static int +fdata_null(rparm_t *rp, caddr_t bp, long addr, int cnt) +{ + return (0); +} + +static int +fdata_c2(rparm_t *rp, caddr_t bp, long addr, int cnt) +{ + int i; + int j; + int k; + char *p; + + p = &bp[2352]; + + for (i = 0; i < cnt; i++, p += (2352+294)) { +/* usal_prbytes("XXX ", p, 294);*/ + if ((j = cmpbytes(p, zeroblk, 294)) < 294) { + printf("C2 in sector: %3ld first at byte: %4d (0x%02X)", addr+i, + j*8 + bitidx(p[j]), p[j]&0xFF); + for (j = 0, k = 0; j < 294; j++) + k += bits(p[j]); + printf(" total: %4d errors\n", k); +/* usal_prbytes("XXX ", p, 294);*/ + rp->c2_errors += k; + if (k > rp->c2_maxerrs) + rp->c2_maxerrs = k; + rp->c2_errsecs++; + if (k >= 100) + rp->c2_badsecs += 1; + } + } + return (0); +} + +#ifdef used +static int +read_scsi_g1(SCSI *usalp, caddr_t bp, long addr, int cnt) +{ + register struct usal_cmd *scmd = usalp->scmd; + + fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); + scmd->addr = bp; +/* scmd->size = cnt*512;*/ + scmd->size = cnt*usalp->cap->c_bsize; + scmd->flags = SCG_RECV_DATA|SCG_DISRE_ENA; + scmd->cdb_len = SC_G1_CDBLEN; + scmd->sense_len = CCS_SENSE_LEN; + scmd->cdb.g1_cdb.cmd = 0x28; + scmd->cdb.g1_cdb.lun = usal_lun(usalp); + g1_cdbaddr(&scmd->cdb.g1_cdb, addr); + g1_cdblen(&scmd->cdb.g1_cdb, cnt); + + usalp->cmdname = "read extended"; + + return (usal_cmd(usalp)); +} +#endif + +#define G0_MAXADDR 0x1FFFFFL + +int +write_scsi(SCSI *usalp, caddr_t bp, long addr, int cnt) +{ + if (addr <= G0_MAXADDR) + return (write_g0(usalp, bp, addr, cnt)); + else + return (write_g1(usalp, bp, addr, cnt)); +} + +int +write_g0(SCSI *usalp, caddr_t bp, long addr, int cnt) +{ + register struct usal_cmd *scmd = usalp->scmd; + + if (usalp->cap->c_bsize <= 0) + raisecond("capacity_not_set", 0L); + + fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); + scmd->addr = bp; + scmd->size = cnt*usalp->cap->c_bsize; + scmd->flags = SCG_DISRE_ENA; + scmd->cdb_len = SC_G0_CDBLEN; + scmd->sense_len = CCS_SENSE_LEN; + scmd->cdb.g0_cdb.cmd = SC_WRITE; + scmd->cdb.g0_cdb.lun = usal_lun(usalp); + g0_cdbaddr(&scmd->cdb.g0_cdb, addr); + scmd->cdb.g0_cdb.count = (Uchar)cnt; + + usalp->cmdname = "write_g0"; + + return (usal_cmd(usalp)); +} + +int +write_g1(SCSI *usalp, caddr_t bp, long addr, int cnt) +{ + register struct usal_cmd *scmd = usalp->scmd; + + if (usalp->cap->c_bsize <= 0) + raisecond("capacity_not_set", 0L); + + fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); + scmd->addr = bp; + scmd->size = cnt*usalp->cap->c_bsize; + scmd->flags = SCG_DISRE_ENA; + scmd->cdb_len = SC_G1_CDBLEN; + scmd->sense_len = CCS_SENSE_LEN; + scmd->cdb.g1_cdb.cmd = SC_EWRITE; + scmd->cdb.g1_cdb.lun = usal_lun(usalp); + g1_cdbaddr(&scmd->cdb.g1_cdb, addr); + g1_cdblen(&scmd->cdb.g1_cdb, cnt); + + usalp->cmdname = "write_g1"; + + return (usal_cmd(usalp)); +} + +#ifdef used +static void +Xrequest_sense(SCSI *usalp) +{ + char sense_buf[32]; + struct usal_cmd ocmd; + int sense_count; + char *cmdsave; + register struct usal_cmd *scmd = usalp->scmd; + + cmdsave = usalp->cmdname; + + movebytes(scmd, &ocmd, sizeof (*scmd)); + + fillbytes((caddr_t)sense_buf, sizeof (sense_buf), '\0'); + + fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); + scmd->addr = (caddr_t)sense_buf; + scmd->size = sizeof (sense_buf); + scmd->flags = SCG_RECV_DATA|SCG_DISRE_ENA; + scmd->cdb_len = SC_G0_CDBLEN; + scmd->sense_len = CCS_SENSE_LEN; + scmd->cdb.g1_cdb.cmd = 0x3; + scmd->cdb.g1_cdb.lun = usal_lun(usalp); + scmd->cdb.g0_cdb.count = sizeof (sense_buf); + + usalp->cmdname = "request sense"; + + usal_cmd(usalp); + + sense_count = sizeof (sense_buf) - usal_getresid(usalp); + movebytes(&ocmd, scmd, sizeof (*scmd)); + scmd->sense_count = sense_count; + movebytes(sense_buf, (Uchar *)&scmd->sense, scmd->sense_count); + + usalp->cmdname = cmdsave; + usal_printerr(usalp); + usal_printresult(usalp); /* XXX restore key/code in future */ +} +#endif + +static int +read_retry(SCSI *usalp, caddr_t bp, long addr, long cnt, + int (*rfunc)(SCSI *usalp, rparm_t *rp, caddr_t bp, long addr, int cnt), + rparm_t *rp) +{ +/* int secsize = usalp->cap->c_bsize;*/ + int secsize = rp->secsize; + int try = 0; + int err; + char dummybuf[8192]; + + if (secsize > sizeof (dummybuf)) { + errmsgno(EX_BAD, "Cannot retry, sector size %d too big.\n", secsize); + return (-1); + } + + errmsgno(EX_BAD, "Retrying from sector %ld.\n", addr); + while (cnt > 0) { + fprintf(stderr, "."); + + do { + if (didintr) + comexit(exsig); /* XXX besseres Konzept?!*/ + wait_unit_ready(usalp, 120); + if (try >= 10) { /* First 10 retries without seek */ + if ((try % 8) == 0) { + fprintf(stderr, "+"); /* Read last sector */ + usalp->silent++; + (*rfunc)(usalp, rp, dummybuf, usalp->cap->c_baddr, 1); + usalp->silent--; + } else if ((try % 4) == 0) { + fprintf(stderr, "-"); /* Read first sector */ + usalp->silent++; + (*rfunc)(usalp, rp, dummybuf, 0, 1); + usalp->silent--; + } else { + fprintf(stderr, "~"); /* Read random sector */ + usalp->silent++; + (*rfunc)(usalp, rp, dummybuf, choice(usalp->cap->c_baddr), 1); + usalp->silent--; + } + if (didintr) + comexit(exsig); /* XXX besseres Konzept?!*/ + wait_unit_ready(usalp, 120); + } + if (didintr) + comexit(exsig); /* XXX besseres Konzept?!*/ + + fillbytes(bp, secsize, 0); + + usalp->silent++; + err = (*rfunc)(usalp, rp, bp, addr, 1); + usalp->silent--; + + if (err < 0) { + err = usalp->scmd->ux_errno; +/* fprintf(stderr, "\n");*/ +/* errmsgno(err, "Cannot read source disk\n");*/ + } else { + if (usal_getresid(usalp)) { + fprintf(stderr, "\nresid: %d\n", usal_getresid(usalp)); + return (-1); + } + break; + } + } while (++try < retries); + + if (try >= retries) { + fprintf(stderr, "\n"); + errmsgno(err, "Error on sector %ld not corrected. Total of %d errors.\n", + addr, ++rp->errors); + + if (usalp->silent <= 1 && lverbose > 0) + usal_printerr(usalp); + + add_bad(addr); + + if (!noerror) + return (-1); + errmsgno(EX_BAD, "-noerror set, continuing ...\n"); + } else { + if (try >= maxtry) + maxtry = try; + + if (try > 1) { + fprintf(stderr, "\n"); + errmsgno(EX_BAD, + "Error on sector %ld corrected after %d tries. Total of %d errors.\n", + addr, try, rp->errors); + } + } + try = 0; + cnt -= 1; + addr += 1; + bp += secsize; + } + return (0); +} + +static void +read_generic(SCSI *usalp, parm_t *parmp, + int (*rfunc)(SCSI *usalp, rparm_t *rp, caddr_t bp, long addr, int cnt), + rparm_t *rp, + int (*dfunc)(rparm_t *rp, caddr_t bp, long addr, int cnt)) +{ + char filename[512]; + char *defname = NULL; + FILE *f; + long addr = 0L; + long old_addr = 0L; + long num; + long end = 0L; + long start = 0L; + long cnt = 0L; + long next_point = 0L; + long secs_per_point = 0L; + double speed; + int msec; + int old_msec = 0; + int err = 0; + BOOL askrange = FALSE; + BOOL isrange = FALSE; + int secsize = rp->secsize; + int i = 0; + + if (is_suid) { + if (usalp->inq->type != INQ_ROMD) + comerrno(EX_BAD, "Not root. Will only read from CD in suid/priv mode\n"); + } + + if (parmp == NULL || parmp->askrange) + askrange = TRUE; + if (parmp != NULL && !askrange && (parmp->start <= parmp->end)) + isrange = TRUE; + + filename[0] = '\0'; + + usalp->silent++; + if (read_capacity(usalp) >= 0) + end = usalp->cap->c_baddr + 1; + usalp->silent--; + + if ((end <= 0 && isrange) || (askrange && usal_yes("Ignore disk size? "))) + end = 10000000; /* Hack to read empty (e.g. blank=fast) disks */ + + if (parmp) { + if (parmp->name) + defname = parmp->name; + if (defname != NULL) { + fprintf(stderr, "Copy from SCSI (%d,%d,%d) disk to file '%s'\n", + usal_scsibus(usalp), usal_target(usalp), usal_lun(usalp), + defname); + } + + addr = start = parmp->start; + if (parmp->end != -1 && parmp->end < end) + end = parmp->end; + cnt = Sbufsize / secsize; + } + + if (defname == NULL) { + defname = "disk.out"; + fprintf(stderr, "Copy from SCSI (%d,%d,%d) disk to file\n", + usal_scsibus(usalp), usal_target(usalp), usal_lun(usalp)); + fprintf(stderr, "Enter filename [%s]: ", defname); flush(); + (void) rols_getline(filename, sizeof (filename)); + } + + if (askrange) { + addr = start; + getlong("Enter starting sector for copy:", &addr, start, end-1); +/* getlong("Enter starting sector for copy:", &addr, -300, end-1);*/ + start = addr; + } + + if (askrange) { + num = end - addr; + getlong("Enter number of sectors to copy:", &num, 1L, num); + end = addr + num; + } + + if (askrange) { +/* XXX askcnt */ + cnt = Sbufsize / secsize; + getlong("Enter number of sectors per copy:", &cnt, 1L, cnt); + } + + if (filename[0] == '\0') + strncpy(filename, defname, sizeof (filename)); + filename[sizeof (filename)-1] = '\0'; + if (streql(filename, "-")) { + f = stdout; +#ifdef NEED_O_BINARY + setmode(STDOUT_FILENO, O_BINARY); +#endif + } else if ((f = fileopen(filename, notrunc?"wcub":"wctub")) == NULL) + comerr("Cannot open '%s'.\n", filename); + + fprintf(stderr, "end: %8ld\n", end); + if (gettimeofday(&starttime, (struct timezone *)0) < 0) + comerr("Cannot get start time\n"); + + if (meshpoints > 0) { + if ((end-start) < meshpoints) + secs_per_point = 1; + else + secs_per_point = (end-start) / meshpoints; + next_point = start + secs_per_point; + old_addr = start; + } + + for (; addr < end; addr += cnt) { + if (didintr) + comexit(exsig); /* XXX besseres Konzept?!*/ + + if ((addr + cnt) > end) + cnt = end - addr; + + if (meshpoints > 0) { + if (addr > next_point) { + + msec = prstats_silent(); + if ((msec - old_msec) == 0) /* Avoid division by zero */ + msec = old_msec + 1; + speed = ((addr - old_addr)/(1000.0/secsize)) / (0.001*(msec - old_msec)); + if (do_factor) { + if (is_cdrom) + speed /= 176.400; + else if (is_dvd) + speed /= 1385.0; + } + fprintf(stderr, "addr: %8ld cnt: %ld", addr, cnt); + printf("%8ld %8.2f\n", addr, speed); + fprintf(stderr, "\r"); + next_point += secs_per_point; + old_addr = addr; + old_msec = msec; + i++; + if (meshpoints < 100) + flush(); + else if (i % (meshpoints/100) == 0) + flush(); + } + } + fprintf(stderr, "addr: %8ld cnt: %ld\r", addr, cnt); + + usalp->silent++; + if ((*rfunc)(usalp, rp, Sbuf, addr, cnt) < 0) { + usalp->silent--; + err = usalp->scmd->ux_errno; + if (quiet) { + fprintf(stderr, "\n"); + } else if (usalp->silent == 0) { + usal_printerr(usalp); + } + errmsgno(err, "Cannot read source disk\n"); + + if (read_retry(usalp, Sbuf, addr, cnt, rfunc, rp) < 0) + goto out; + } else { + usalp->silent--; + if (usal_getresid(usalp)) { + fprintf(stderr, "\nresid: %d\n", usal_getresid(usalp)); + goto out; + } + } + (*dfunc)(rp, Sbuf, addr, cnt); + if (filewrite(f, Sbuf, cnt * secsize) < 0) { + err = geterrno(); + fprintf(stderr, "\n"); + errmsgno(err, "Cannot write '%s'\n", filename); + break; + } + } + fprintf(stderr, "addr: %8ld", addr); +out: + fprintf(stderr, "\n"); + msec = prstats(); + if (msec == 0) /* Avoid division by zero */ + msec = 1; +#ifdef OOO + fprintf(stderr, "Read %.2f kB at %.1f kB/sec.\n", + (double)(addr - start)/(1024.0/usalp->cap->c_bsize), + (double)((addr - start)/(1024.0/usalp->cap->c_bsize)) / (0.001*msec)); +#else + fprintf(stderr, "Read %.2f kB at %.1f kB/sec.\n", + (double)(addr - start)/(1024.0/secsize), + (double)((addr - start)/(1024.0/secsize)) / (0.001*msec)); +#endif + print_bad(); +} + +static void +write_disk(SCSI *usalp, parm_t *parmp) +{ + char filename[512]; + char *defname = "disk.out"; + FILE *f; + long addr = 0L; + long cnt; + long amt; + long end; + int msec; + int start; + + if (is_suid) + comerrno(EX_BAD, "Not root. Will not write in suid/priv mode\n"); + + filename[0] = '\0'; + if (read_capacity(usalp) >= 0) { + end = usalp->cap->c_baddr + 1; + print_capacity(usalp, stderr); + } + + if (end <= 1) + end = 10000000; /* Hack to write empty disks */ + + if (parmp) { + if (parmp->name) + defname = parmp->name; + fprintf(stderr, "Copy from file '%s' to SCSI (%d,%d,%d) disk\n", + defname, + usal_scsibus(usalp), usal_target(usalp), usal_lun(usalp)); + + addr = start = parmp->start; + if (parmp->end != -1 && parmp->end < end) + end = parmp->end; + cnt = Sbufsize / usalp->cap->c_bsize; + } else { + fprintf(stderr, "Copy from file to SCSI (%d,%d,%d) disk\n", + usal_scsibus(usalp), usal_target(usalp), usal_lun(usalp)); + fprintf(stderr, "Enter filename [%s]: ", defname); flush(); + (void) rols_getline(filename, sizeof (filename)); + fprintf(stderr, "Notice: reading from file always starts at file offset 0.\n"); + + getlong("Enter starting sector for copy:", &addr, 0L, end-1); + start = addr; + cnt = end - addr; + getlong("Enter number of sectors to copy:", &end, 1L, end); + end = addr + cnt; + + cnt = Sbufsize / usalp->cap->c_bsize; + getlong("Enter number of sectors per copy:", &cnt, 1L, cnt); +/* fprintf(stderr, "end: %8ld\n", end);*/ + } + + if (filename[0] == '\0') + strncpy(filename, defname, sizeof (filename)); + filename[sizeof (filename)-1] = '\0'; + if (streql(filename, "-")) { + f = stdin; +#ifdef NEED_O_BINARY + setmode(STDIN_FILENO, O_BINARY); +#endif + } else if ((f = fileopen(filename, "rub")) == NULL) + comerr("Cannot open '%s'.\n", filename); + + fprintf(stderr, "end: %8ld\n", end); + if (gettimeofday(&starttime, (struct timezone *)0) < 0) + comerr("Cannot get start time\n"); + + for (; addr < end; addr += cnt) { + if (didintr) + comexit(exsig); /* XXX besseres Konzept?!*/ + + if ((addr + cnt) > end) + cnt = end - addr; + + fprintf(stderr, "addr: %8ld cnt: %ld\r", addr, cnt); + + if ((amt = fileread(f, Sbuf, cnt * usalp->cap->c_bsize)) < 0) + comerr("Cannot read '%s'\n", filename); + if (amt == 0) + break; + if ((amt / usalp->cap->c_bsize) < cnt) + cnt = amt / usalp->cap->c_bsize; + if (write_scsi(usalp, Sbuf, addr, cnt) < 0) + comerrno(usalp->scmd->ux_errno, + "Cannot write destination disk\n"); + } + fprintf(stderr, "addr: %8ld\n", addr); + msec = prstats(); + if (msec == 0) /* Avoid division by zero */ + msec = 1; + fprintf(stderr, "Wrote %.2f kB at %.1f kB/sec.\n", + (double)(addr - start)/(1024.0/usalp->cap->c_bsize), + (double)((addr - start)/(1024.0/usalp->cap->c_bsize)) / (0.001*msec)); +} + +static int +choice(int n) +{ +#if defined(HAVE_DRAND48) + extern double drand48(void); + + return (drand48() * n); +#else +# if defined(HAVE_RAND) + extern int rand(void); + + return (rand() % n); +# else + return (0); +# endif +#endif +} + +static void +ra(SCSI *usalp) +{ +/* char filename[512];*/ + FILE *f; +/* long addr = 0L;*/ +/* long cnt;*/ +/* long end;*/ +/* int msec;*/ +/* int start;*/ +/* int err = 0;*/ + + select_secsize(usalp, 2352); + read_capacity(usalp); + print_capacity(usalp, stderr); + fillbytes(Sbuf, 50*2352, 0); + if (read_g1(usalp, Sbuf, 0, 50) < 0) + errmsg("read CD\n"); + f = fileopen("DDA", "wctb"); +/* filewrite(f, Sbuf, 50 * 2352 - usal_getresid(usalp));*/ + filewrite(f, Sbuf, 50 * 2352); + fclose(f); +} + +#define g5x_cdblen(cdb, len) ((cdb)->count[0] = ((len) >> 16L)& 0xFF,\ + (cdb)->count[1] = ((len) >> 8L) & 0xFF,\ + (cdb)->count[2] = (len) & 0xFF) + +int +read_da(SCSI *usalp, caddr_t bp, long addr, int cnt, int framesize, int subcode) +{ + register struct usal_cmd *scmd = usalp->scmd; + + if (usalp->cap->c_bsize <= 0) + raisecond("capacity_not_set", 0L); + + fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); + scmd->addr = bp; + scmd->size = cnt*framesize; + scmd->flags = SCG_RECV_DATA|SCG_DISRE_ENA; + scmd->cdb_len = SC_G5_CDBLEN; + scmd->sense_len = CCS_SENSE_LEN; + scmd->cdb.g5_cdb.cmd = 0xd8; + scmd->cdb.g5_cdb.lun = usal_lun(usalp); + g5_cdbaddr(&scmd->cdb.g5_cdb, addr); + g5_cdblen(&scmd->cdb.g5_cdb, cnt); + scmd->cdb.g5_cdb.res10 = subcode; + + usalp->cmdname = "read_da"; + + return (usal_cmd(usalp)); +} + +int +read_cd(SCSI *usalp, caddr_t bp, long addr, int cnt, int framesize, int data, + int subch) +{ + register struct usal_cmd *scmd = usalp->scmd; + + fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); + scmd->addr = bp; + scmd->size = cnt*framesize; + scmd->flags = SCG_RECV_DATA|SCG_DISRE_ENA; + scmd->cdb_len = SC_G5_CDBLEN; + scmd->sense_len = CCS_SENSE_LEN; + scmd->cdb.g5_cdb.cmd = 0xBE; + scmd->cdb.g5_cdb.lun = usal_lun(usalp); + scmd->cdb.g5_cdb.res = 0; /* expected sector type field ALL */ + g5_cdbaddr(&scmd->cdb.g5_cdb, addr); + g5x_cdblen(&scmd->cdb.g5_cdb, cnt); + + scmd->cdb.g5_cdb.count[3] = data & 0xFF; + scmd->cdb.g5_cdb.res10 = subch & 0x07; + + usalp->cmdname = "read_cd"; + + return (usal_cmd(usalp)); +} + +static void +oldmode(SCSI *usalp, int *errp, int *retrp) +{ + Uchar mode[0x100]; + Uchar cmode[0x100]; + Uchar *p; + int i; + int len; + + fillbytes(mode, sizeof (mode), '\0'); + fillbytes(cmode, sizeof (cmode), '\0'); + + if (!get_mode_params(usalp, 0x01, "CD error recovery parameter", + mode, (Uchar *)0, (Uchar *)cmode, (Uchar *)0, &len)) { + return; + } + if (xdebug) + usal_prbytes("Mode Sense Data", mode, len); + + mode[0] = 0; + mode[2] = 0; /* ??? ist manchmal 0x80 */ + p = mode; + p += mode[3] + 4; + *p &= 0x3F; + + if (xdebug) + usal_prbytes("Mode page 1:", p, 0x10); + + i = p[2]; + if (errp != NULL) + *errp = i; + + i = p[3]; + if (retrp != NULL) + *retrp = i; +} + +static void +domode(SCSI *usalp, int err, int retr) +{ + Uchar mode[0x100]; + Uchar cmode[0x100]; + Uchar *p; + int i; + int len; + + fillbytes(mode, sizeof (mode), '\0'); + fillbytes(cmode, sizeof (cmode), '\0'); + + if (!get_mode_params(usalp, 0x01, "CD error recovery parameter", + mode, (Uchar *)0, (Uchar *)cmode, (Uchar *)0, &len)) { + return; + } + if (xdebug || (err == -1 && retr == -1)) { + usal_prbytes("Mode Sense Data", mode, len); + } + + mode[0] = 0; + mode[2] = 0; /* ??? ist manchmal 0x80 */ + p = mode; + p += mode[3] + 4; + *p &= 0x3F; + + if (xdebug || (err == -1 && retr == -1)) + usal_prbytes("Mode page 1:", p, 0x10); + + i = p[2]; + if (err == -1) { + getint("Error handling? ", &i, 0, 255); + p[2] = i; + } else { + if (xdebug) + fprintf(stderr, "Error handling set from %02X to %02X\n", + p[2], err); + p[2] = err; + } + + i = p[3]; + if (retr == -1) { + getint("Retry count? ", &i, 0, 255); + p[3] = i; + } else { + if (xdebug) + fprintf(stderr, "Retry count set from %d to %d\n", + p[3] & 0xFF, retr); + p[3] = retr; + } + + if (xdebug || (err == -1 && retr == -1)) + usal_prbytes("Mode Select Data", mode, len); + mode_select(usalp, mode, len, 0, usalp->inq->data_format >= 2); +} + + +/*--------------------------------------------------------------------------*/ +static void qpto96(Uchar *sub, Uchar *subq, int dop); +/*EXPORT void qpto96 __PR((Uchar *sub, Uchar *subq, int dop));*/ +/* + * Q-Sub auf 96 Bytes blähen und P-Sub addieren + * + * OUT: sub, IN: subqptr + */ +static void +/*EXPORT void*/ +qpto96(Uchar *sub, Uchar *subqptr, int dop) +{ + Uchar tmp[16]; + Uchar *p; + int c; + int i; + + if (subqptr == sub) { + movebytes(subqptr, tmp, 12); + subqptr = tmp; + } + fillbytes(sub, 96, '\0'); + + /* CSTYLED */ + if (dop) for (i = 0, p = sub; i < 96; i++) { + *p++ |= 0x80; + } + for (i = 0, p = sub; i < 12; i++) { + c = subqptr[i] & 0xFF; +/*printf("%02X\n", c);*/ + if (c & 0x80) + *p++ |= 0x40; + else + p++; + if (c & 0x40) + *p++ |= 0x40; + else + p++; + if (c & 0x20) + *p++ |= 0x40; + else + p++; + if (c & 0x10) + *p++ |= 0x40; + else + p++; + if (c & 0x08) + *p++ |= 0x40; + else + p++; + if (c & 0x04) + *p++ |= 0x40; + else + p++; + if (c & 0x02) + *p++ |= 0x40; + else + p++; + if (c & 0x01) + *p++ |= 0x40; + else + p++; + } +} + +/*--------------------------------------------------------------------------*/ + +static void +ovtime(SCSI *usalp) +{ + register int i; + + usalp->silent++; + (void) test_unit_ready(usalp); + usalp->silent--; + if (test_unit_ready(usalp) < 0) + return; + + printf("Doing 1000 'TEST UNIT READY' operations.\n"); + + if (gettimeofday(&starttime, (struct timezone *)0) < 0) + comerr("Cannot get start time\n"); + + for (i = 1000; --i >= 0; ) { + (void) test_unit_ready(usalp); + + if (didintr) + return; + } + + prstats(); + + /* + * ATAPI drives do not like seek_g0() + */ + usalp->silent++; + i = seek_g0(usalp, 0L); + usalp->silent--; + + if (i >= 0) { + printf("Doing 1000 'SEEK_G0 (0)' operations.\n"); + + if (gettimeofday(&starttime, (struct timezone *)0) < 0) + comerr("Cannot get start time\n"); + + for (i = 1000; --i >= 0; ) { + (void) seek_g0(usalp, 0L); + + if (didintr) + return; + } + + prstats(); + } + + usalp->silent++; + i = seek_g1(usalp, 0L); + usalp->silent--; + if (i < 0) + return; + + printf("Doing 1000 'SEEK_G1 (0)' operations.\n"); + + if (gettimeofday(&starttime, (struct timezone *)0) < 0) + comerr("Cannot get start time\n"); + + for (i = 1000; --i >= 0; ) { + (void) seek_g1(usalp, 0L); + + if (didintr) + return; + } + + prstats(); +} + +#define BAD_INC 16 +long *badsecs; +int nbad; +int maxbad; + +static void +add_bad(long addr) +{ + if (maxbad == 0) { + maxbad = BAD_INC; + badsecs = malloc(maxbad * sizeof (long)); + if (badsecs == NULL) + comerr("No memory for bad sector list\n."); + } + if (nbad >= maxbad) { + maxbad += BAD_INC; + badsecs = realloc(badsecs, maxbad * sizeof (long)); + if (badsecs == NULL) + comerr("No memory to grow bad sector list\n."); + } + badsecs[nbad++] = addr; +} + +static void +print_bad(void) +{ + int i; + + if (nbad == 0) + return; + + fprintf(stderr, "Max corected retry count was %d (limited to %d).\n", maxtry, retries); + fprintf(stderr, "The following %d sector(s) could not be read correctly:\n", nbad); + for (i = 0; i < nbad; i++) + fprintf(stderr, "%ld\n", badsecs[i]); +} |