Commit | Line | Data |
---|---|---|
9815cb4a AM |
1 | /* |
2 | * Copyright (c) 1993 Steve Gerakines | |
3 | * | |
4 | * This is freely redistributable software. You may do anything you | |
5 | * wish with it, so long as the above notice stays intact. | |
6 | * | |
7 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS | |
8 | * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |
9 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
10 | * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, | |
11 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
12 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | |
13 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
14 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, | |
15 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING | |
16 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | |
17 | * POSSIBILITY OF SUCH DAMAGE. | |
18 | * | |
19 | * ft.c - simple floppy tape filter | |
20 | * | |
21 | * 01/28/94 v0.3b (Jim Babb) | |
22 | * Fixed bug when all sectors in a segment are marked bad. | |
23 | * | |
24 | * 10/30/93 v0.3 | |
25 | * Minor revisions. Seems pretty stable. | |
26 | * | |
27 | * 09/02/93 v0.2 pl01 | |
28 | * Initial revision. | |
29 | * | |
30 | * usage: ftfilt [ -f tape ] [ description ] | |
31 | */ | |
32 | ||
33 | #include <stdio.h> | |
34 | #include <stdlib.h> | |
35 | #include <string.h> | |
36 | #include <signal.h> | |
37 | #include <time.h> | |
38 | #include <sys/ftape.h> | |
39 | ||
40 | #define DEFQIC "/dev/rft0" | |
41 | ||
42 | char buff[QCV_SEGSIZE]; /* scratch buffer */ | |
43 | char hbuff[QCV_SEGSIZE]; /* header buffer */ | |
44 | QIC_Header *hptr = (QIC_Header *)hbuff; /* header structure */ | |
45 | int hsn = -1; /* segment number of header */ | |
46 | int dhsn = -1; /* segment number of duplicate header */ | |
47 | int tfd; /* tape file descriptor */ | |
48 | QIC_Geom geo; /* tape geometry */ | |
49 | int tvno = 1; /* tape volume number */ | |
50 | int tvlast; /* TRUE if last volume in set */ | |
51 | long tvsize = 0; /* tape volume size in bytes */ | |
52 | long tvtime = NULL; /* tape change time */ | |
53 | char *tvnote = ""; /* tape note */ | |
54 | ||
55 | /* Lookup the badmap for a given track and segment. */ | |
56 | #define BADMAP(t,s) hptr->qh_badmap[(t)*geo.g_segtrk+(s)] | |
57 | ||
58 | /* Retrieve values from a character array. */ | |
59 | #define UL_VAL(s,p) (*((ULONG *)&(s)[p])) | |
60 | #define US_VAL(s,p) (*((USHORT *)&(s)[p])) | |
61 | ||
62 | #define equal(s1,s2) (strcmp(s1, s2) == 0) | |
63 | ||
64 | ||
65 | ||
66 | /* Entry */ | |
67 | main(int argc, char *argv[]) | |
68 | { | |
69 | int r, s; | |
70 | char *tape, *getenv(); | |
71 | ||
72 | if (argc > 2 && (equal(argv[1], "-t") || equal(argv[1], "-f"))) { | |
73 | argc -= 2; | |
74 | tape = argv[2]; | |
75 | argv += 2; | |
76 | } else | |
77 | if ((tape = getenv("TAPE")) == NULL) | |
78 | tape = DEFQIC; | |
79 | if (argc > 1) { | |
80 | tvnote = argv[1]; | |
81 | if (strlen(tvnote) > 18) argv[1][18] = '\0'; | |
82 | } | |
83 | ||
84 | /* Open the tape device */ | |
85 | if ((tfd = open(tape, 2)) < 0) { | |
86 | perror(tape); | |
87 | exit(1); | |
88 | } | |
89 | ||
90 | if (!isatty(0)) | |
91 | do_write(); | |
92 | else if (!isatty(1)) | |
93 | do_read(); | |
94 | else | |
95 | do_getname(); | |
96 | ||
97 | close(tfd); | |
98 | exit(0); | |
99 | } | |
100 | ||
101 | ||
102 | /* Check status of tape drive */ | |
103 | int check_stat(int fd, int wr) | |
104 | { | |
105 | int r, s; | |
106 | int sawit = 0; | |
107 | ||
108 | /* get tape status */ | |
109 | if (ioctl(fd, QIOSTATUS, &s) < 0) { | |
110 | fprintf(stderr, "could not get drive status\n"); | |
111 | return(1); | |
112 | } | |
113 | ||
114 | /* wait for the tape drive to become ready */ | |
115 | while ((s & QS_READY) == 0) { | |
116 | if (!sawit) { | |
117 | fprintf(stderr, "waiting for drive to become ready...\n"); | |
118 | sawit = 1; | |
119 | } | |
120 | sleep(2); | |
121 | if (ioctl(fd, QIOSTATUS, &s) < 0) { | |
122 | fprintf(stderr, "could not get drive status\n"); | |
123 | return(1); | |
124 | } | |
125 | } | |
126 | ||
127 | if ((s & QS_FMTOK) == 0) { | |
128 | fprintf(stderr, "tape is not formatted\n"); | |
129 | return(2); | |
130 | } | |
131 | ||
132 | if (wr && (s & QS_RDONLY) != 0) { | |
133 | fprintf(stderr, "tape is write protected\n"); | |
134 | return(3); | |
135 | } | |
136 | ||
137 | return(0); | |
138 | } | |
139 | ||
140 | ||
141 | ||
142 | ULONG qtimeval(time_t t) | |
143 | { | |
144 | struct tm *tp; | |
145 | ULONG r; | |
146 | ||
147 | tp = localtime(&t); | |
148 | r = 2678400 * tp->tm_mon + | |
149 | 86400 *(tp->tm_mday-1) + | |
150 | 3600 * tp->tm_hour + | |
151 | 60 * tp->tm_min + | |
152 | tp->tm_sec; | |
153 | r |= (tp->tm_year - 70) << 25; | |
154 | return(r); | |
155 | } | |
156 | ||
157 | /* Return tm struct from QIC date format. */ | |
158 | struct tm *qtime(UCHAR *qt) | |
159 | { | |
160 | ULONG *vp = (ULONG *)qt; | |
161 | struct tm t; | |
162 | ULONG v; | |
163 | time_t tv; | |
164 | ||
165 | v = *vp; | |
166 | t.tm_year = ((v >> 25) & 0x7f)+70; v &= 0x1ffffff; | |
167 | t.tm_mon = v / 2678400; v %= 2678400; | |
168 | t.tm_mday = v / 86400 + 1; v %= 86400; | |
169 | t.tm_hour = v / 3600; v %= 3600; | |
170 | t.tm_min = v / 60; v %= 60; | |
171 | t.tm_sec = v; | |
172 | t.tm_wday = 0; /* XXX - let mktime do the real work */ | |
173 | t.tm_yday = 0; | |
174 | t.tm_isdst = 0; | |
175 | t.tm_gmtoff = 0; | |
176 | t.tm_zone = NULL; | |
177 | tv = mktime(&t); | |
178 | return(localtime(&tv)); | |
179 | } | |
180 | ||
181 | /* Return a string, zero terminated */ | |
182 | char *qstr(char *str, int nchar) | |
183 | { | |
184 | static char tstr[256]; | |
185 | strncpy(tstr, str, nchar); | |
186 | tstr[nchar] = '\0'; | |
187 | return(tstr); | |
188 | } | |
189 | ||
190 | /* Read header from tape */ | |
191 | get_header(int fd) | |
192 | { | |
193 | int r, sn, bytes; | |
194 | QIC_Segment s; | |
195 | int gothdr = 0; | |
196 | ||
197 | if (ioctl(fd, QIOGEOM, &geo) < 0) { | |
198 | fprintf(stderr, "couldn't determine tape geometry\n"); | |
199 | return(1); | |
200 | } | |
201 | ||
202 | /* Get the header and duplicate */ | |
203 | for (sn = 0; sn < 16; sn++) { | |
204 | s.sg_trk = 0; | |
205 | s.sg_seg = sn; | |
206 | s.sg_badmap = 0; | |
207 | s.sg_data = (UCHAR *)&buff[0]; | |
208 | ioctl(fd, QIOREAD, &s); | |
209 | r = check_parity(s.sg_data, 0, s.sg_crcmap); | |
210 | if (s.sg_data[0] == 0x55 && s.sg_data[1] == 0xaa && | |
211 | s.sg_data[2] == 0x55 && s.sg_data[3] == 0xaa) { | |
212 | if (hsn >= 0) { | |
213 | dhsn = sn; | |
214 | if (!r && !gothdr) { | |
215 | fprintf(stderr, "using secondary header\n"); | |
216 | bcopy(s.sg_data, hbuff, QCV_SEGSIZE); | |
217 | gothdr = 1; | |
218 | } | |
219 | break; | |
220 | } | |
221 | hsn = sn; | |
222 | if (!r) { | |
223 | bcopy(s.sg_data, hbuff, QCV_SEGSIZE); | |
224 | gothdr = 1; | |
225 | } else { | |
226 | fprintf(stderr, "too many errors in primary header\n"); | |
227 | } | |
228 | } | |
229 | } | |
230 | ||
231 | if (!gothdr) { | |
232 | fprintf(stderr, "couldn't read header segment\n"); | |
233 | ioctl(fd, QIOREWIND); | |
234 | return(1); | |
235 | } | |
236 | ||
237 | return(0); | |
238 | } | |
239 | ||
240 | ||
241 | ask_vol(int vn) | |
242 | { | |
243 | FILE *inp; | |
244 | int fd; | |
245 | char c; | |
246 | ||
247 | if ((fd = open("/dev/tty", 2)) < 0) { | |
248 | fprintf(stderr, "argh!! can't open /dev/tty\n"); | |
249 | exit(1); | |
250 | } | |
251 | ||
252 | fprintf(stderr, "Insert ftfilt volume %02d and press enter:", vn); | |
253 | read(fd, &c, 1); | |
254 | close(fd); | |
255 | } | |
256 | ||
257 | ||
258 | /* Return the name of the tape only. */ | |
259 | do_getname() | |
260 | { | |
261 | if (check_stat(tfd, 0)) exit(1); | |
262 | if (get_header(tfd)) exit(1); | |
263 | fprintf(stderr, "\"%s\" - %s", | |
264 | qstr(hptr->qh_tname,44), asctime(qtime(hptr->qh_chgdate))); | |
265 | ioctl(tfd, QIOREWIND); | |
266 | } | |
267 | ||
268 | ||
269 | /* Extract data from tape to stdout */ | |
270 | do_read() | |
271 | { | |
272 | int sno, vno, sbytes, r; | |
273 | long curpos; | |
274 | char *hname; | |
275 | QIC_Segment s; | |
276 | ||
277 | /* Process multiple volumes if necessary */ | |
278 | vno = 1; | |
279 | for (;;) { | |
280 | if (check_stat(tfd, 0)) { | |
281 | ask_vol(vno); | |
282 | continue; | |
283 | } | |
284 | if (get_header(tfd)) { | |
285 | ask_vol(vno); | |
286 | continue; | |
287 | } | |
288 | ||
289 | /* extract volume and header info from label */ | |
290 | hname = hptr->qh_tname; | |
291 | hname[43] = '\0'; | |
292 | tvno = atoi(&hname[11]); | |
293 | tvlast = (hname[10] == '*') ? 1 : 0; | |
294 | tvsize = atoi(&hname[14]); | |
295 | tvnote = &hname[25]; | |
296 | if (vno != tvno || strncmp(hname, "ftfilt", 6) != 0) { | |
297 | fprintf(stderr, "Incorrect volume inserted. This tape is:\n"); | |
298 | fprintf(stderr,"\"%s\" - %s\n", hname, | |
299 | asctime(qtime(hptr->qh_chgdate))); | |
300 | ask_vol(vno); | |
301 | continue; | |
302 | } | |
303 | ||
304 | /* Process this volume */ | |
305 | curpos = 0; | |
306 | for (sno = hptr->qh_first; tvsize > 0; sno++) { | |
307 | s.sg_trk = sno / geo.g_segtrk; | |
308 | s.sg_seg = sno % geo.g_segtrk; | |
309 | s.sg_badmap = BADMAP(s.sg_trk,s.sg_seg); | |
310 | sbytes = sect_bytes(s.sg_badmap) - QCV_ECCSIZE; | |
311 | s.sg_data = (UCHAR *)&buff[0]; | |
312 | ||
313 | /* skip segments with *all* sectors flagged as bad */ | |
314 | if (sbytes > 0) { | |
315 | if (ioctl(tfd, QIOREAD, &s) < 0) perror("QIOREAD"); | |
316 | r = check_parity(s.sg_data, s.sg_badmap, s.sg_crcmap); | |
317 | if (r) fprintf(stderr, "** warning: ecc failed at byte %ld\n", | |
318 | curpos); | |
319 | if (tvsize < sbytes) sbytes = tvsize; | |
320 | write(1, s.sg_data, sbytes); | |
321 | tvsize -= sbytes; | |
322 | curpos += sbytes; | |
323 | } | |
324 | } | |
325 | if (tvlast) break; | |
326 | ioctl(tfd, QIOREWIND); | |
327 | ask_vol(++vno); | |
328 | } | |
329 | ioctl(tfd, QIOREWIND); | |
330 | return(0); | |
331 | } | |
332 | ||
333 | ||
334 | /* Dump data from stdin to tape */ | |
335 | do_write() | |
336 | { | |
337 | int sno, vno, amt, sbytes; | |
338 | int c, maxseg, r; | |
339 | ULONG qnow; | |
340 | QIC_Segment s; | |
341 | char tmpstr[80]; | |
342 | ||
343 | qnow = qtimeval(time(NULL)); | |
344 | vno = 1; | |
345 | ||
346 | for (;;) { | |
347 | if (check_stat(tfd, 1)) { | |
348 | ask_vol(vno); | |
349 | continue; | |
350 | } | |
351 | if (get_header(tfd)) { | |
352 | ask_vol(vno); | |
353 | continue; | |
354 | } | |
355 | ||
356 | maxseg = geo.g_segtrk * geo.g_trktape - 1; | |
357 | sno = hptr->qh_first; | |
358 | tvno = vno; | |
359 | tvsize = 0; | |
360 | tvlast = 0; | |
361 | ||
362 | /* Process until end of volume or end of data */ | |
363 | for (sno = hptr->qh_first; sno < maxseg && tvlast == 0; ++sno) { | |
364 | /* Prepare to load the next segment */ | |
365 | s.sg_trk = sno / geo.g_segtrk; | |
366 | s.sg_seg = sno % geo.g_segtrk; | |
367 | s.sg_badmap = BADMAP(s.sg_trk,s.sg_seg); | |
368 | sbytes = sect_bytes(s.sg_badmap) - QCV_ECCSIZE; | |
369 | s.sg_data = (UCHAR *)&buff[0]; | |
370 | ||
371 | /* Ugh. Loop to get the full amt. */ | |
372 | for (amt = 0; amt < sbytes; amt += r) { | |
373 | r = read(0, &s.sg_data[amt], sbytes - amt); | |
374 | if (r <= 0) { | |
375 | tvlast = 1; | |
376 | break; | |
377 | } | |
378 | } | |
379 | /* skip the segment if *all* sectors are flagged as bad */ | |
380 | if (amt) { | |
381 | if (amt < sbytes) | |
382 | bzero(&s.sg_data[amt], sbytes - amt); | |
383 | r = set_parity(s.sg_data, s.sg_badmap); | |
384 | if (r) fprintf(stderr, "** warning: ecc problem !!\n"); | |
385 | if (ioctl(tfd, QIOWRITE, &s) < 0) { | |
386 | perror("QIOWRITE"); | |
387 | exit(1); | |
388 | } | |
389 | tvsize += amt; | |
390 | } | |
391 | } | |
392 | ||
393 | /* Build new header info */ | |
394 | /* ftfilt vol*xx yyyyyyyyyy note56789012345678 */ | |
395 | /* 01234567890123456789012345678901234567890123 */ | |
396 | ||
397 | sprintf(tmpstr, "ftfilt vol%s%02d %010d %s", | |
398 | (tvlast) ? "*" : " ", tvno, tvsize, tvnote); | |
399 | strncpy(hptr->qh_tname, tmpstr, 44); | |
400 | UL_VAL(hptr->qh_chgdate,0) = qnow; | |
401 | ||
402 | /* Update the header for this volume */ | |
403 | if (hsn >= 0) { | |
404 | s.sg_trk = hsn / geo.g_segtrk; | |
405 | s.sg_seg = hsn % geo.g_segtrk; | |
406 | s.sg_badmap = 0; | |
407 | s.sg_data = (UCHAR *)hbuff; | |
408 | r = set_parity(s.sg_data, s.sg_badmap); | |
409 | if (r) fprintf(stderr, "** warning: header ecc problem !!\n"); | |
410 | if (ioctl(tfd, QIOWRITE, &s) < 0) { | |
411 | perror("QIOWRITE"); | |
412 | exit(1); | |
413 | } | |
414 | } | |
415 | if (dhsn >= 0) { | |
416 | s.sg_trk = dhsn / geo.g_segtrk; | |
417 | s.sg_seg = dhsn % geo.g_segtrk; | |
418 | s.sg_badmap = 0; | |
419 | s.sg_data = (UCHAR *)hbuff; | |
420 | r = set_parity(s.sg_data, s.sg_badmap); | |
421 | if (r) fprintf(stderr, "** warning: duphdr ecc problem !!\n"); | |
422 | if (ioctl(tfd, QIOWRITE, &s) < 0) { | |
423 | perror("QIOWRITE"); | |
424 | exit(1); | |
425 | } | |
426 | } | |
427 | ioctl(tfd, QIOREWIND); | |
428 | if (tvlast) break; | |
429 | ask_vol(++vno); | |
430 | } | |
431 | return(0); | |
432 | } |