1 /* OMATAPE.C (c) Copyright Roger Bowler, 1999-2009 */
2 /* Hercules Tape Device Handler for OMATAPE */
3
4 /* Original Author: Roger Bowler */
5 /* Prime Maintainer: Ivan Warren */
6 /* Secondary Maintainer: "Fish" (David B. Trout) */
7
8 /*-------------------------------------------------------------------*/
9 /* This module contains the OMATAPE emulated tape format support. */
10 /* */
11 /* The subroutines in this module are called by the general tape */
12 /* device handler (tapedev.c) when the tape format is OMATAPE. */
13 /* */
14 /* Messages issued by this module are prefixed HHCTA2nn */
15 /*-------------------------------------------------------------------*/
16
17 /*-------------------------------------------------------------------*/
18 /* Reference information: */
19 /* SC53-1200 S/370 and S/390 Optical Media Attach/2 User's Guide */
20 /* SC53-1201 S/370 and S/390 Optical Media Attach/2 Technical Ref */
21 /*-------------------------------------------------------------------*/
22
23 #include "hstdinc.h"
24 #include "hercules.h" /* need Hercules control blocks */
25 #include "tapedev.h" /* Main tape handler header file */
26
27 //#define ENABLE_TRACING_STMTS // (Fish: DEBUGGING)
28
29 #ifdef ENABLE_TRACING_STMTS
30 #if !defined(DEBUG)
31 #warning DEBUG required for ENABLE_TRACING_STMTS
32 #endif
33 // (TRACE, ASSERT, and VERIFY macros are #defined in hmacros.h)
34 #else
35 #undef TRACE
36 #undef ASSERT
37 #undef VERIFY
38 #define TRACE 1 ? ((void)0) : logmsg
39 #define ASSERT(a)
40 #define VERIFY(a) ((void)(a))
41 #endif
42
43 /*-------------------------------------------------------------------*/
44 /* Read the OMA tape descriptor file */
45 /*-------------------------------------------------------------------*/
read_omadesc(DEVBLK * dev)46 int read_omadesc (DEVBLK *dev)
47 {
48 int rc; /* Return code */
49 int i; /* Array subscript */
50 int pathlen; /* Length of TDF path name */
51 int tdfsize; /* Size of TDF file in bytes */
52 int filecount; /* Number of files */
53 int stmt; /* TDF file statement number */
54 int fd; /* TDF file descriptor */
55 struct stat statbuf; /* TDF file information */
56 U32 blklen; /* Fixed block length */
57 int tdfpos; /* Position in TDF buffer */
58 char *tdfbuf; /* -> TDF file buffer */
59 char *tdfrec; /* -> TDF record */
60 char *tdffilenm; /* -> Filename in TDF record */
61 char *tdfformat; /* -> Format in TDF record */
62 char *tdfreckwd; /* -> Keyword in TDF record */
63 char *tdfblklen; /* -> Length in TDF record */
64 OMATAPE_DESC *tdftab; /* -> Tape descriptor array */
65 BYTE c; /* Work area for sscanf */
66 char pathname[MAX_PATH]; /* file path in host format */
67
68 /* Isolate the base path name of the TDF file */
69 for (pathlen = strlen(dev->filename); pathlen > 0; )
70 {
71 pathlen--;
72 if (dev->filename[pathlen-1] == '/') break;
73 }
74 #if 0
75 // JCS thinks this is bad
76 if (pathlen < 7
77 || strncasecmp(dev->filename+pathlen-7, "/tapes/", 7) != 0)
78 {
79 logmsg (_("HHCTA232I %4.4X: Invalid filename %s: "
80 "TDF files must be in the TAPES subdirectory\n"),
81 dev->devnum, dev->filename+pathlen);
82 return -1;
83 }
84 pathlen -= 7;
85 #endif
86
87 /* Open the tape descriptor file */
88 hostpath(pathname, dev->filename, sizeof(pathname));
89 fd = hopen(pathname, O_RDONLY | O_BINARY);
90 if (fd < 0)
91 {
92 logmsg (_("HHCTA239E %4.4X: Error opening TDF file %s: %s\n"),
93 dev->devnum, dev->filename, strerror(errno));
94 return -1;
95 }
96
97 /* Determine the size of the tape descriptor file */
98 rc = fstat (fd, &statbuf);
99 if (rc < 0)
100 {
101 logmsg (_("HHCTA240E %4.4X: File %s fstat error: %s\n"),
102 dev->devnum, dev->filename, strerror(errno));
103 close (fd);
104 return -1;
105 }
106 tdfsize = statbuf.st_size;
107
108 /* Obtain a buffer for the tape descriptor file */
109 tdfbuf = malloc (tdfsize);
110 if (tdfbuf == NULL)
111 {
112 logmsg (_("HHCTA241E %4.4X: Cannot obtain buffer for TDF file %s: %s\n"),
113 dev->devnum, dev->filename, strerror(errno));
114 close (fd);
115 return -1;
116 }
117
118 /* Read the tape descriptor file into the buffer */
119 rc = read (fd, tdfbuf, tdfsize);
120 if (rc < tdfsize)
121 {
122 logmsg (_("HHCTA242E %4.4X: Error reading TDF file %s: %s\n"),
123 dev->devnum, dev->filename, strerror(errno));
124 free (tdfbuf);
125 close (fd);
126 return -1;
127 }
128
129 /* Close the tape descriptor file */
130 close (fd); fd = -1;
131
132 /* Check that the first record is a TDF header */
133 if (memcmp(tdfbuf, "@TDF", 4) != 0)
134 {
135 logmsg (_("HHCTA243E %4.4X: %s is not a valid TDF file\n"),
136 dev->devnum, dev->filename);
137 free (tdfbuf);
138 return -1;
139 }
140
141 /* Count the number of linefeeds in the tape descriptor file
142 to determine the size of the descriptor array required */
143 for (i = 0, filecount = 0; i < tdfsize; i++)
144 {
145 if (tdfbuf[i] == '\n') filecount++;
146 } /* end for(i) */
147 /* ISW Add 1 to filecount to add an extra EOT marker */
148 filecount++;
149
150 /* Obtain storage for the tape descriptor array */
151 tdftab = (OMATAPE_DESC*)malloc (filecount * sizeof(OMATAPE_DESC));
152 if (tdftab == NULL)
153 {
154 logmsg (_("HHCTA244E %4.4X: Cannot obtain buffer for TDF array: %s\n"),
155 dev->devnum, strerror(errno));
156 free (tdfbuf);
157 return -1;
158 }
159
160 /* Build the tape descriptor array */
161 for (filecount = 0, tdfpos = 0, stmt = 1; ; filecount++)
162 {
163 /* Clear the tape descriptor array entry */
164 memset (&(tdftab[filecount]), 0, sizeof(OMATAPE_DESC));
165
166 /* Point past the next linefeed in the TDF file */
167 while (tdfpos < tdfsize && tdfbuf[tdfpos++] != '\n');
168 stmt++;
169
170 /* Exit at end of TDF file */
171 if (tdfpos >= tdfsize) break;
172
173 /* Mark the end of the TDF record with a null terminator */
174 tdfrec = tdfbuf + tdfpos;
175 while (tdfpos < tdfsize && tdfbuf[tdfpos]!='\r'
176 && tdfbuf[tdfpos]!='\n') tdfpos++;
177 c = tdfbuf[tdfpos];
178 if (tdfpos >= tdfsize) break;
179 tdfbuf[tdfpos] = '\0';
180
181 /* Exit if TM or EOT record */
182 if (strcasecmp(tdfrec, "TM") == 0)
183 {
184 tdftab[filecount].format='X';
185 tdfbuf[tdfpos] = c;
186 continue;
187 }
188 if(strcasecmp(tdfrec, "EOT") == 0)
189 {
190 tdftab[filecount].format='E';
191 break;
192 }
193
194 /* Parse the TDF record */
195 tdffilenm = strtok (tdfrec, " \t");
196 tdfformat = strtok (NULL, " \t");
197 tdfreckwd = strtok (NULL, " \t");
198 tdfblklen = strtok (NULL, " \t");
199
200 /* Check for missing fields */
201 if (tdffilenm == NULL || tdfformat == NULL)
202 {
203 logmsg (_("HHCTA245E %4.4X: Filename or format missing in "
204 "line %d of file %s\n"),
205 dev->devnum, stmt, dev->filename);
206 free (tdftab);
207 free (tdfbuf);
208 return -1;
209 }
210
211 /* Check that the file name is not too long */
212 if (pathlen + 1 + strlen(tdffilenm)
213 > sizeof(tdftab[filecount].filename) - 1)
214 {
215 logmsg (_("HHCTA246E %4.4X: Filename %s too long in "
216 "line %d of file %s\n"),
217 dev->devnum, tdffilenm, stmt, dev->filename);
218 free (tdftab);
219 free (tdfbuf);
220 return -1;
221 }
222
223 /* Convert the file name to Unix format */
224 for (i = 0; i < (int)strlen(tdffilenm); i++)
225 {
226 if (tdffilenm[i] == '\\')
227 tdffilenm[i] = '/';
228 /* JCS */
229 // else
230 // tdffilenm[i] = tolower(tdffilenm[i]);
231 } /* end for(i) */
232
233 /* Prefix the file name with the base path name and
234 save it in the tape descriptor array */
235 /* but only if the filename lacks a leading slash - JCS */
236 /*
237 strncpy (tdftab[filecount].filename, dev->filename, pathlen);
238 if (tdffilenm[0] != '/')
239 stlrcat ( tdftab[filecount].filename, "/", sizeof(tdftab[filecount].filename) );
240 strlcat ( tdftab[filecount].filename, tdffilenm, sizeof(tdftab[filecount].filename) );
241 */
242 tdftab[filecount].filename[0] = 0;
243
244 if ((tdffilenm[0] != '/') && (tdffilenm[1] != ':'))
245 {
246 strncpy (tdftab[filecount].filename, dev->filename, pathlen);
247 strlcat (tdftab[filecount].filename, "/", sizeof(tdftab[filecount].filename) );
248 }
249
250 strlcat (tdftab[filecount].filename, tdffilenm, sizeof(tdftab[filecount].filename) );
251
252 /* Check for valid file format code */
253 if (strcasecmp(tdfformat, "HEADERS") == 0)
254 {
255 tdftab[filecount].format = 'H';
256 }
257 else if (strcasecmp(tdfformat, "TEXT") == 0)
258 {
259 tdftab[filecount].format = 'T';
260 }
261 else if (strcasecmp(tdfformat, "FIXED") == 0)
262 {
263 /* Check for RECSIZE keyword */
264 if (tdfreckwd == NULL
265 || strcasecmp(tdfreckwd, "RECSIZE") != 0)
266 {
267 logmsg (_("HHCTA247E %4.4X: RECSIZE keyword missing in "
268 "line %d of file %s\n"),
269 dev->devnum, stmt, dev->filename);
270 free (tdftab);
271 free (tdfbuf);
272 return -1;
273 }
274
275 /* Check for valid fixed block length */
276 if (tdfblklen == NULL
277 || sscanf(tdfblklen, "%u%c", &blklen, &c) != 1
278 || blklen < 1 || blklen > MAX_BLKLEN)
279 {
280 logmsg (_("HHCTA248E %4.4X: Invalid record size %s in "
281 "line %d of file %s\n"),
282 dev->devnum, tdfblklen, stmt, dev->filename);
283 free (tdftab);
284 free (tdfbuf);
285 return -1;
286 }
287
288 /* Set format and block length in descriptor array */
289 tdftab[filecount].format = 'F';
290 tdftab[filecount].blklen = blklen;
291 }
292 else
293 {
294 logmsg (_("HHCTA249E %4.4X: Invalid record format %s in "
295 "line %d of file %s\n"),
296 dev->devnum, tdfformat, stmt, dev->filename);
297 free (tdftab);
298 free (tdfbuf);
299 return -1;
300 }
301 tdfbuf[tdfpos] = c;
302 } /* end for(filecount) */
303 /* Force an EOT as last entry (filecount is correctly adjusted here) */
304 tdftab[filecount].format='E';
305
306 /* Save the file count and TDF array pointer in the device block */
307 dev->omafiles = filecount+1;
308 dev->omadesc = tdftab;
309
310 /* Release the TDF file buffer and exit */
311 free (tdfbuf);
312 return 0;
313
314 } /* end function read_omadesc */
315
316 /*-------------------------------------------------------------------*/
317 /* Open the OMATAPE file defined by the current file number */
318 /* */
319 /* The OMA tape descriptor file is read if necessary. */
320 /* If successful, the file descriptor is stored in the device block */
321 /* and the return value is zero. Otherwise the return value is -1. */
322 /*-------------------------------------------------------------------*/
open_omatape(DEVBLK * dev,BYTE * unitstat,BYTE code)323 int open_omatape (DEVBLK *dev, BYTE *unitstat,BYTE code)
324 {
325 int fd; /* File descriptor integer */
326 int rc; /* Return code */
327 OMATAPE_DESC *omadesc; /* -> OMA descriptor entry */
328 char pathname[MAX_PATH]; /* file path in host format */
329
330 /* Check for no tape in drive */
331 if (!strcmp (dev->filename, TAPE_UNLOADED))
332 {
333 build_senseX(TAPE_BSENSE_TAPEUNLOADED,dev,unitstat,code);
334 return -1;
335 }
336
337 /* Read the OMA descriptor file if necessary */
338 if (dev->omadesc == NULL)
339 {
340 rc = read_omadesc (dev);
341 if (rc < 0)
342 {
343 build_senseX(TAPE_BSENSE_TAPELOADFAIL,dev,unitstat,code);
344 return -1;
345 }
346 dev->blockid = 0;
347 }
348
349 dev->fenced = 0;
350
351 /* Unit exception if beyond end of tape */
352 /* ISW: CHANGED PROCESSING - RETURN UNDEFINITE Tape Marks */
353 /* NOTE: The last entry in the TDF table is ALWAYS */
354 /* an EOT Condition */
355 /* This is ensured by the TDF reading routine */
356 #if 0
357 if (dev->curfilen >= dev->omafiles)
358 {
359 logmsg (_("HHCTA250E %4.4X: Attempt to access beyond end of tape %s\n"),
360 dev->devnum, dev->filename);
361
362 build_senseX(TAPE_BSENSE_ENDOFTAPE,dev,unitstat,code);
363 return -1;
364 }
365 #else
366 if(dev->curfilen>dev->omafiles)
367 {
368 dev->curfilen=dev->omafiles;
369 return(0);
370 }
371 #endif
372
373 /* Point to the current file entry in the OMA descriptor table */
374 omadesc = (OMATAPE_DESC*)(dev->omadesc);
375 omadesc += (dev->curfilen-1);
376 if(omadesc->format=='X')
377 {
378 return 0;
379 }
380 if(omadesc->format=='E')
381 {
382 return 0;
383 }
384
385 /* Open the OMATAPE file */
386 hostpath(pathname, omadesc->filename, sizeof(pathname));
387 fd = hopen(pathname, O_RDONLY | O_BINARY);
388
389 /* Check for successful open */
390 if (fd < 0 || lseek (fd, 0, SEEK_END) > LONG_MAX)
391 {
392 if (fd >= 0) /* (if open was successful, then it) */
393 errno = EOVERFLOW; /* (must have been a lseek overflow) */
394
395 logmsg (_("HHCTA251E %4.4X: Error opening %s: %s\n"),
396 dev->devnum, omadesc->filename, strerror(errno));
397
398 if (fd >= 0)
399 close(fd); /* (close the file if it was opened) */
400
401 build_senseX(TAPE_BSENSE_TAPELOADFAIL,dev,unitstat,code);
402 return -1;
403 }
404
405 /* OMA tapes are always read-only */
406 dev->readonly = 1;
407
408 /* Store the file descriptor in the device block */
409 dev->fd = fd;
410 return 0;
411
412 } /* end function open_omatape */
413
414 /*-------------------------------------------------------------------*/
415 /* Read a block header from an OMA tape file in OMA headers format */
416 /* */
417 /* If successful, return value is zero, and the current block */
418 /* length and previous and next header offsets are returned. */
419 /* If error, return value is -1 and unitstat is set to CE+DE+UC */
420 /*-------------------------------------------------------------------*/
readhdr_omaheaders(DEVBLK * dev,OMATAPE_DESC * omadesc,long blkpos,S32 * pcurblkl,S32 * pprvhdro,S32 * pnxthdro,BYTE * unitstat,BYTE code)421 int readhdr_omaheaders (DEVBLK *dev, OMATAPE_DESC *omadesc,
422 long blkpos, S32 *pcurblkl, S32 *pprvhdro,
423 S32 *pnxthdro, BYTE *unitstat,BYTE code)
424 {
425 int rc; /* Return code */
426 off_t rcoff; /* Return code from lseek() */
427 int padding; /* Number of padding bytes */
428 OMATAPE_BLKHDR omahdr; /* OMATAPE block header */
429 S32 curblkl; /* Length of current block */
430 S32 prvhdro; /* Offset of previous header */
431 S32 nxthdro; /* Offset of next header */
432
433 /* Seek to start of block header */
434 rcoff = lseek (dev->fd, blkpos, SEEK_SET);
435 if (rcoff < 0)
436 {
437 /* Handle seek error condition */
438 logmsg (_("HHCTA252E %4.4X: Error seeking to offset "I32_FMTX" "
439 "in file %s: %s\n"),
440 dev->devnum, blkpos, omadesc->filename, strerror(errno));
441
442 /* Set unit check with equipment check */
443 build_senseX(TAPE_BSENSE_LOCATEERR,dev,unitstat,code);
444 return -1;
445 }
446
447 /* Read the 16-byte block header */
448 rc = read (dev->fd, &omahdr, sizeof(omahdr));
449
450 /* Handle read error condition */
451 if (rc < 0)
452 {
453 logmsg (_("HHCTA253E %4.4X: Error reading block header "
454 "at offset "I32_FMTX" in file %s: %s\n"),
455 dev->devnum, blkpos, omadesc->filename,
456 strerror(errno));
457
458 /* Set unit check with equipment check */
459 build_senseX(TAPE_BSENSE_READFAIL,dev,unitstat,code);
460 return -1;
461 }
462
463 /* Handle end of file within block header */
464 if (rc < (int)sizeof(omahdr))
465 {
466 logmsg (_("HHCTA254E %4.4X: Unexpected end of file in block header "
467 "at offset "I32_FMTX" in file %s\n"),
468 dev->devnum, blkpos, omadesc->filename);
469
470 /* Set unit check with data check and partial record */
471 build_senseX(TAPE_BSENSE_BLOCKSHORT,dev,unitstat,code);
472 return -1;
473 }
474
475 /* Extract the current block length and previous header offset */
476 curblkl = (S32)(((U32)(omahdr.curblkl[3]) << 24)
477 | ((U32)(omahdr.curblkl[2]) << 16)
478 | ((U32)(omahdr.curblkl[1]) << 8)
479 | omahdr.curblkl[0]);
480 prvhdro = (S32)((U32)(omahdr.prvhdro[3]) << 24)
481 | ((U32)(omahdr.prvhdro[2]) << 16)
482 | ((U32)(omahdr.prvhdro[1]) << 8)
483 | omahdr.prvhdro[0];
484
485 /* Check for valid block header */
486 if (curblkl < -1 || curblkl == 0 || curblkl > MAX_BLKLEN
487 || memcmp(omahdr.omaid, "@HDF", 4) != 0)
488 {
489 logmsg (_("HHCTA255E %4.4X: Invalid block header "
490 "at offset "I32_FMTX" in file %s\n"),
491 dev->devnum, blkpos, omadesc->filename);
492
493 build_senseX(TAPE_BSENSE_READFAIL,dev,unitstat,code);
494 return -1;
495 }
496
497 /* Calculate the number of padding bytes which follow the data */
498 padding = (16 - (curblkl & 15)) & 15;
499
500 /* Calculate the offset of the next block header */
501 nxthdro = blkpos + sizeof(OMATAPE_BLKHDR) + curblkl + padding;
502
503 /* Return current block length and previous/next header offsets */
504 *pcurblkl = curblkl;
505 *pprvhdro = prvhdro;
506 *pnxthdro = nxthdro;
507 return 0;
508
509 } /* end function readhdr_omaheaders */
510
511 /*-------------------------------------------------------------------*/
512 /* Read a block from an OMA tape file in OMA headers format */
513 /* */
514 /* If successful, return value is block length read. */
515 /* If a tapemark was read, the file is closed, the current file */
516 /* number in the device block is incremented so that the next file */
517 /* will be opened by the next CCW, and the return value is zero. */
518 /* If error, return value is -1 and unitstat is set to CE+DE+UC */
519 /*-------------------------------------------------------------------*/
read_omaheaders(DEVBLK * dev,OMATAPE_DESC * omadesc,BYTE * buf,BYTE * unitstat,BYTE code)520 int read_omaheaders (DEVBLK *dev, OMATAPE_DESC *omadesc,
521 BYTE *buf, BYTE *unitstat,BYTE code)
522 {
523 int rc; /* Return code */
524 long blkpos; /* Offset to block header */
525 S32 curblkl; /* Length of current block */
526 S32 prvhdro; /* Offset of previous header */
527 S32 nxthdro; /* Offset of next header */
528
529 /* Read the 16-byte block header */
530 blkpos = dev->nxtblkpos;
531 rc = readhdr_omaheaders (dev, omadesc, blkpos, &curblkl,
532 &prvhdro, &nxthdro, unitstat,code);
533 if (rc < 0) return -1;
534
535 /* Update the offsets of the next and previous blocks */
536 dev->nxtblkpos = nxthdro;
537 dev->prvblkpos = blkpos;
538
539 /* Increment file number and return zero if tapemark */
540 if (curblkl == -1)
541 {
542 close (dev->fd);
543 dev->fd = -1;
544 dev->curfilen++;
545 dev->nxtblkpos = 0;
546 dev->prvblkpos = -1;
547 return 0;
548 }
549
550 /* Read data block from tape file */
551 rc = read (dev->fd, buf, curblkl);
552
553 /* Handle read error condition */
554 if (rc < 0)
555 {
556 logmsg (_("HHCTA256E %4.4X: Error reading data block "
557 "at offset "I32_FMTX" in file %s: %s\n"),
558 dev->devnum, blkpos, omadesc->filename,
559 strerror(errno));
560
561 /* Set unit check with equipment check */
562 build_senseX(TAPE_BSENSE_READFAIL,dev,unitstat,code);
563 return -1;
564 }
565
566 /* Handle end of file within data block */
567 if (rc < curblkl)
568 {
569 logmsg (_("HHCTA257E %4.4X: Unexpected end of file in data block "
570 "at offset "I32_FMTX" in file %s\n"),
571 dev->devnum, blkpos, omadesc->filename);
572
573 /* Set unit check with data check and partial record */
574 build_senseX(TAPE_BSENSE_BLOCKSHORT,dev,unitstat,code);
575 return -1;
576 }
577
578 /* Return block length */
579 return curblkl;
580
581 } /* end function read_omaheaders */
582
583 /*-------------------------------------------------------------------*/
584 /* Read a block from an OMA tape file in fixed block format */
585 /* */
586 /* If successful, return value is block length read. */
587 /* If a tapemark was read, the file is closed, the current file */
588 /* number in the device block is incremented so that the next file */
589 /* will be opened by the next CCW, and the return value is zero. */
590 /* If error, return value is -1 and unitstat is set to CE+DE+UC */
591 /*-------------------------------------------------------------------*/
read_omafixed(DEVBLK * dev,OMATAPE_DESC * omadesc,BYTE * buf,BYTE * unitstat,BYTE code)592 int read_omafixed (DEVBLK *dev, OMATAPE_DESC *omadesc,
593 BYTE *buf, BYTE *unitstat,BYTE code)
594 {
595 off_t rcoff; /* Return code from lseek() */
596 int blklen; /* Block length */
597 long blkpos; /* Offset of block in file */
598
599 /* Initialize current block position */
600 blkpos = dev->nxtblkpos;
601
602 /* Seek to new current block position */
603 rcoff = lseek (dev->fd, blkpos, SEEK_SET);
604 if (rcoff < 0)
605 {
606 /* Handle seek error condition */
607 logmsg (_("HHCTA258E %4.4X: Error seeking to offset "I32_FMTX" "
608 "in file %s: %s\n"),
609 dev->devnum, blkpos, omadesc->filename, strerror(errno));
610
611 /* Set unit check with equipment check */
612 build_senseX(TAPE_BSENSE_LOCATEERR,dev,unitstat,code);
613 return -1;
614 }
615
616 /* Read fixed length block or short final block */
617 blklen = read (dev->fd, buf, omadesc->blklen);
618
619 /* Handle read error condition */
620 if (blklen < 0)
621 {
622 logmsg (_("HHCTA259E %4.4X: Error reading data block "
623 "at offset "I32_FMTX" in file %s: %s\n"),
624 dev->devnum, blkpos, omadesc->filename,
625 strerror(errno));
626
627 build_senseX(TAPE_BSENSE_READFAIL,dev,unitstat,code);
628 return -1;
629 }
630
631 /* At end of file return zero to indicate tapemark */
632 if (blklen == 0)
633 {
634 close (dev->fd);
635 dev->fd = -1;
636 dev->curfilen++;
637 dev->nxtblkpos = 0;
638 dev->prvblkpos = -1;
639 return 0;
640 }
641
642 /* Calculate the offsets of the next and previous blocks */
643 dev->nxtblkpos = blkpos + blklen;
644 dev->prvblkpos = blkpos;
645
646 /* Return block length, or zero to indicate tapemark */
647 return blklen;
648
649 } /* end function read_omafixed */
650
651 /*-------------------------------------------------------------------*/
652 /* Read a block from an OMA tape file in ASCII text format */
653 /* */
654 /* If successful, return value is block length read. */
655 /* If a tapemark was read, the file is closed, the current file */
656 /* number in the device block is incremented so that the next file */
657 /* will be opened by the next CCW, and the return value is zero. */
658 /* If error, return value is -1 and unitstat is set to CE+DE+UC */
659 /* */
660 /* The buf parameter points to the I/O buffer during a read */
661 /* operation, or is NULL for a forward space block operation. */
662 /*-------------------------------------------------------------------*/
read_omatext(DEVBLK * dev,OMATAPE_DESC * omadesc,BYTE * buf,BYTE * unitstat,BYTE code)663 int read_omatext (DEVBLK *dev, OMATAPE_DESC *omadesc,
664 BYTE *buf, BYTE *unitstat,BYTE code)
665 {
666 int rc; /* Return code */
667 off_t rcoff; /* Return code from lseek() */
668 int num; /* Number of characters read */
669 int pos; /* Position in I/O buffer */
670 long blkpos; /* Offset of block in file */
671 BYTE c; /* Character work area */
672
673 /* Initialize current block position */
674 blkpos = dev->nxtblkpos;
675
676 /* Seek to new current block position */
677 rcoff = lseek (dev->fd, blkpos, SEEK_SET);
678 if (rcoff < 0)
679 {
680 /* Handle seek error condition */
681 logmsg (_("HHCTA260E %4.4X: Error seeking to offset "I32_FMTX" "
682 "in file %s: %s\n"),
683 dev->devnum, blkpos, omadesc->filename, strerror(errno));
684
685 /* Set unit check with equipment check */
686 build_senseX(TAPE_BSENSE_LOCATEERR,dev,unitstat,code);
687 return -1;
688 }
689
690 /* Read data from tape file until end of line */
691 for (num = 0, pos = 0; ; )
692 {
693 rc = read (dev->fd, &c, 1);
694 if (rc < 1) break;
695
696 /* Treat X'1A' as end of file */
697 if (c == '\x1A')
698 {
699 rc = 0;
700 break;
701 }
702
703 /* Count characters read */
704 num++;
705
706 /* Ignore carriage return character */
707 if (c == '\r') continue;
708
709 /* Exit if newline character */
710 if (c == '\n') break;
711
712 /* Ignore characters in excess of I/O buffer length */
713 if (pos >= MAX_BLKLEN) continue;
714
715 /* Translate character to EBCDIC and copy to I/O buffer */
716 if (buf != NULL)
717 buf[pos] = host_to_guest(c);
718
719 /* Count characters copied or skipped */
720 pos++;
721
722 } /* end for(num) */
723
724 /* At end of file return zero to indicate tapemark */
725 if (rc == 0 && num == 0)
726 {
727 close (dev->fd);
728 dev->fd = -1;
729 dev->curfilen++;
730 dev->nxtblkpos = 0;
731 dev->prvblkpos = -1;
732 return 0;
733 }
734
735 /* Handle read error condition */
736 if (rc < 0)
737 {
738 logmsg (_("HHCTA261E %4.4X: Error reading data block "
739 "at offset "I32_FMTX" in file %s: %s\n"),
740 dev->devnum, blkpos, omadesc->filename,
741 strerror(errno));
742
743 build_senseX(TAPE_BSENSE_READFAIL,dev,unitstat,code);
744 return -1;
745 }
746
747 /* Check for block not terminated by newline */
748 if (rc < 1)
749 {
750 logmsg (_("HHCTA262E %4.4X: Unexpected end of file in data block "
751 "at offset "I32_FMTX" in file %s\n"),
752 dev->devnum, blkpos, omadesc->filename);
753
754 /* Set unit check with data check and partial record */
755 build_senseX(TAPE_BSENSE_BLOCKSHORT,dev,unitstat,code);
756 return -1;
757 }
758
759 /* Check for invalid zero length block */
760 if (pos == 0)
761 {
762 logmsg (_("HHCTA263E %4.4X: Invalid zero length block "
763 "at offset "I32_FMTX" in file %s\n"),
764 dev->devnum, blkpos, omadesc->filename);
765
766 /* Set unit check with equipment check */
767 build_senseX(TAPE_BSENSE_BLOCKSHORT,dev,unitstat,code);
768 return -1;
769 }
770
771 /* Calculate the offsets of the next and previous blocks */
772 dev->nxtblkpos = blkpos + num;
773 dev->prvblkpos = blkpos;
774
775 /* Return block length */
776 return pos;
777
778 } /* end function read_omatext */
779
780 /*-------------------------------------------------------------------*/
781 /* Read a block from an OMA - Selection of format done here */
782 /* */
783 /* If successful, return value is block length read. */
784 /* If a tapemark was read, the file is closed, the current file */
785 /* number in the device block is incremented so that the next file */
786 /* will be opened by the next CCW, and the return value is zero. */
787 /* If error, return value is -1 and unitstat is set to CE+DE+UC */
788 /* */
789 /* The buf parameter points to the I/O buffer during a read */
790 /* operation, or is NULL for a forward space block operation. */
791 /*-------------------------------------------------------------------*/
read_omatape(DEVBLK * dev,BYTE * buf,BYTE * unitstat,BYTE code)792 int read_omatape (DEVBLK *dev,
793 BYTE *buf, BYTE *unitstat,BYTE code)
794 {
795 int len;
796 OMATAPE_DESC *omadesc;
797 omadesc = (OMATAPE_DESC*)(dev->omadesc);
798 omadesc += (dev->curfilen-1);
799
800 switch (omadesc->format)
801 {
802 default:
803 case 'H':
804 len = read_omaheaders (dev, omadesc, buf, unitstat,code);
805 break;
806 case 'F':
807 len = read_omafixed (dev, omadesc, buf, unitstat,code);
808 break;
809 case 'T':
810 len = read_omatext (dev, omadesc, buf, unitstat,code);
811 break;
812 case 'X':
813 len=0;
814 dev->curfilen++;
815 break;
816 case 'E':
817 len=0;
818 break;
819 } /* end switch(omadesc->format) */
820
821 if (len >= 0)
822 dev->blockid++;
823
824 return len;
825 }
826
827 /*-------------------------------------------------------------------*/
828 /* Forward space to next file of OMA tape device */
829 /* */
830 /* For OMA tape devices, the forward space file operation is */
831 /* achieved by closing the current file, and incrementing the */
832 /* current file number in the device block, which causes the */
833 /* next file will be opened when the next CCW is processed. */
834 /* If error, return value is -1 and unitstat is set to CE+DE+UC */
835 /*-------------------------------------------------------------------*/
fsf_omatape(DEVBLK * dev,BYTE * unitstat,BYTE code)836 int fsf_omatape (DEVBLK *dev, BYTE *unitstat,BYTE code)
837 {
838 UNREFERENCED(unitstat);
839 UNREFERENCED(code);
840
841 /* Close the current OMA file */
842 if (dev->fd >= 0)
843 close (dev->fd);
844 dev->fd = -1;
845 dev->nxtblkpos = 0;
846 dev->prvblkpos = -1;
847
848 /* Increment the current file number */
849 dev->curfilen++;
850
851 /* Return normal status */
852 return 0;
853
854 } /* end function fsf_omatape */
855
856 /*-------------------------------------------------------------------*/
857 /* Forward space over next block of OMA file in OMA headers format */
858 /* */
859 /* If successful, return value is the length of the block skipped. */
860 /* If the block skipped was a tapemark, the return value is zero, */
861 /* the file is closed, and the current file number in the device */
862 /* block is incremented so that the next file belonging to the OMA */
863 /* tape will be opened when the next CCW is executed. */
864 /* If error, return value is -1 and unitstat is set to CE+DE+UC */
865 /*-------------------------------------------------------------------*/
fsb_omaheaders(DEVBLK * dev,OMATAPE_DESC * omadesc,BYTE * unitstat,BYTE code)866 int fsb_omaheaders (DEVBLK *dev, OMATAPE_DESC *omadesc,
867 BYTE *unitstat,BYTE code)
868 {
869 int rc; /* Return code */
870 long blkpos; /* Offset of block header */
871 S32 curblkl; /* Length of current block */
872 S32 prvhdro; /* Offset of previous header */
873 S32 nxthdro; /* Offset of next header */
874
875 /* Initialize current block position */
876 blkpos = dev->nxtblkpos;
877
878 /* Read the 16-byte block header */
879 rc = readhdr_omaheaders (dev, omadesc, blkpos, &curblkl,
880 &prvhdro, &nxthdro, unitstat,code);
881 if (rc < 0) return -1;
882
883 /* Check if tapemark was skipped */
884 if (curblkl == -1)
885 {
886 /* Close the current OMA file */
887 if (dev->fd >= 0)
888 close (dev->fd);
889 dev->fd = -1;
890 dev->nxtblkpos = 0;
891 dev->prvblkpos = -1;
892
893 /* Increment the file number */
894 dev->curfilen++;
895
896 /* Return zero to indicate tapemark */
897 return 0;
898 }
899
900 /* Update the offsets of the next and previous blocks */
901 dev->nxtblkpos = nxthdro;
902 dev->prvblkpos = blkpos;
903
904 /* Return block length */
905 return curblkl;
906
907 } /* end function fsb_omaheaders */
908
909 /*-------------------------------------------------------------------*/
910 /* Forward space over next block of OMA file in fixed block format */
911 /* */
912 /* If successful, return value is the length of the block skipped. */
913 /* If already at end of file, the return value is zero, */
914 /* the file is closed, and the current file number in the device */
915 /* block is incremented so that the next file belonging to the OMA */
916 /* tape will be opened when the next CCW is executed. */
917 /* If error, return value is -1 and unitstat is set to CE+DE+UC */
918 /*-------------------------------------------------------------------*/
fsb_omafixed(DEVBLK * dev,OMATAPE_DESC * omadesc,BYTE * unitstat,BYTE code)919 int fsb_omafixed (DEVBLK *dev, OMATAPE_DESC *omadesc,
920 BYTE *unitstat,BYTE code)
921 {
922 off_t eofpos; /* Offset of end of file */
923 off_t blkpos; /* Offset of current block */
924 int curblkl; /* Length of current block */
925
926 /* Initialize current block position */
927 blkpos = dev->nxtblkpos;
928
929 /* Seek to end of file to determine file size */
930 eofpos = lseek (dev->fd, 0, SEEK_END);
931 if (eofpos < 0 || eofpos >= LONG_MAX)
932 {
933 /* Handle seek error condition */
934 if ( eofpos >= LONG_MAX) errno = EOVERFLOW;
935 logmsg (_("HHCTA264E %4.4X: Error seeking to end of file %s: %s\n"),
936 dev->devnum, omadesc->filename, strerror(errno));
937
938 /* Set unit check with equipment check */
939 build_senseX(TAPE_BSENSE_LOCATEERR,dev,unitstat,code);
940 return -1;
941 }
942
943 /* Check if already at end of file */
944 if (blkpos >= eofpos)
945 {
946 /* Close the current OMA file */
947 if (dev->fd >= 0)
948 close (dev->fd);
949 dev->fd = -1;
950 dev->nxtblkpos = 0;
951 dev->prvblkpos = -1;
952
953 /* Increment the file number */
954 dev->curfilen++;
955
956 /* Return zero to indicate tapemark */
957 return 0;
958 }
959
960 /* Calculate current block length */
961 curblkl = (int)(eofpos - blkpos);
962 if (curblkl > omadesc->blklen)
963 curblkl = omadesc->blklen;
964
965 /* Update the offsets of the next and previous blocks */
966 dev->nxtblkpos = (long)(blkpos + curblkl);
967 dev->prvblkpos = (long)(blkpos);
968
969 /* Return block length */
970 return curblkl;
971
972 } /* end function fsb_omafixed */
973
974 /*-------------------------------------------------------------------*/
975 /* Forward space to next block of OMA file */
976 /* */
977 /* If successful, return value is the length of the block skipped. */
978 /* If forward spaced over end of file, return value is 0. */
979 /* If error, return value is -1 and unitstat is set to CE+DE+UC */
980 /*-------------------------------------------------------------------*/
fsb_omatape(DEVBLK * dev,BYTE * unitstat,BYTE code)981 int fsb_omatape (DEVBLK *dev, BYTE *unitstat,BYTE code)
982 {
983 int rc; /* Return code */
984 OMATAPE_DESC *omadesc; /* -> OMA descriptor entry */
985
986 /* Point to the current file entry in the OMA descriptor table */
987 omadesc = (OMATAPE_DESC*)(dev->omadesc);
988 omadesc += (dev->curfilen-1);
989
990 /* Forward space block depending on OMA file type */
991 switch (omadesc->format)
992 {
993 default:
994 case 'H':
995 rc = fsb_omaheaders (dev, omadesc, unitstat,code);
996 break;
997 case 'F':
998 rc = fsb_omafixed (dev, omadesc, unitstat,code);
999 break;
1000 case 'T':
1001 rc = read_omatext (dev, omadesc, NULL, unitstat,code);
1002 break;
1003 } /* end switch(omadesc->format) */
1004
1005 if (rc >= 0) dev->blockid++;
1006
1007 return rc;
1008
1009 } /* end function fsb_omatape */
1010
1011 /*-------------------------------------------------------------------*/
1012 /* Backspace to previous file of OMA tape device */
1013 /* */
1014 /* If the current file number is 1, then backspace file simply */
1015 /* closes the file, setting the current position to start of tape. */
1016 /* Otherwise, the current file is closed, the current file number */
1017 /* is decremented, the new file is opened, and the new file is */
1018 /* repositioned to just before the tape mark at the end of the file. */
1019 /* If error, return value is -1 and unitstat is set to CE+DE+UC */
1020 /*-------------------------------------------------------------------*/
bsf_omatape(DEVBLK * dev,BYTE * unitstat,BYTE code)1021 int bsf_omatape (DEVBLK *dev, BYTE *unitstat,BYTE code)
1022 {
1023 int rc; /* Return code */
1024 off_t pos; /* File position */
1025 OMATAPE_DESC *omadesc; /* -> OMA descriptor entry */
1026 S32 curblkl; /* Length of current block */
1027 S32 prvhdro; /* Offset of previous header */
1028 S32 nxthdro; /* Offset of next header */
1029
1030 /* Close the current OMA file */
1031 if (dev->fd >= 0)
1032 close (dev->fd);
1033 dev->fd = -1;
1034 dev->nxtblkpos = 0;
1035 dev->prvblkpos = -1;
1036
1037 /* Exit with tape at load point if currently on first file */
1038 if (dev->curfilen <= 1)
1039 {
1040 build_senseX(TAPE_BSENSE_LOADPTERR,dev,unitstat,code);
1041 return -1;
1042 }
1043
1044 /* Decrement current file number */
1045 dev->curfilen--;
1046
1047 /* Point to the current file entry in the OMA descriptor table */
1048 omadesc = (OMATAPE_DESC*)(dev->omadesc);
1049 omadesc += (dev->curfilen-1);
1050
1051 /* Open the new current file */
1052 rc = open_omatape (dev, unitstat,code);
1053 if (rc < 0) return rc;
1054
1055 /* Reposition before tapemark header at end of file, or
1056 to end of file for fixed block or ASCII text files */
1057 pos = 0;
1058 if ( 'H' == omadesc->format )
1059 pos -= sizeof(OMATAPE_BLKHDR);
1060
1061 pos = lseek (dev->fd, pos, SEEK_END);
1062 if (pos < 0)
1063 {
1064 /* Handle seek error condition */
1065 logmsg (_("HHCTA265E %4.4X: Error seeking to end of file %s: %s\n"),
1066 dev->devnum, omadesc->filename, strerror(errno));
1067
1068 /* Set unit check with equipment check */
1069 build_senseX(TAPE_BSENSE_LOCATEERR,dev,unitstat,code);
1070 dev->sense[0] = SENSE_EC;
1071 *unitstat = CSW_CE | CSW_DE | CSW_UC;
1072 return -1;
1073 }
1074 dev->nxtblkpos = pos;
1075 dev->prvblkpos = -1;
1076
1077 /* Determine the offset of the previous block */
1078 switch (omadesc->format)
1079 {
1080 case 'H':
1081 /* For OMA headers files, read the tapemark header
1082 and extract the previous block offset */
1083 rc = readhdr_omaheaders (dev, omadesc, pos, &curblkl,
1084 &prvhdro, &nxthdro, unitstat,code);
1085 if (rc < 0) return -1;
1086 dev->prvblkpos = prvhdro;
1087 break;
1088 case 'F':
1089 /* For OMA fixed block files, calculate the previous block
1090 offset allowing for a possible short final block */
1091 pos = (pos + omadesc->blklen - 1) / omadesc->blklen;
1092 dev->prvblkpos = (pos > 0 ? (pos - 1) * omadesc->blklen : -1);
1093 break;
1094 case 'T':
1095 /* For OMA ASCII text files, the previous block is unknown */
1096 dev->prvblkpos = -1;
1097 break;
1098 } /* end switch(omadesc->format) */
1099
1100 /* Return normal status */
1101 return 0;
1102
1103 } /* end function bsf_omatape */
1104
1105 /*-------------------------------------------------------------------*/
1106 /* Backspace to previous block of OMA file */
1107 /* */
1108 /* If successful, return value is +1. */
1109 /* If current position is at start of a file, then a backspace file */
1110 /* operation is performed to reset the position to the end of the */
1111 /* previous file, and the return value is zero. */
1112 /* If error, return value is -1 and unitstat is set to CE+DE+UC */
1113 /* */
1114 /* Note that for ASCII text files, the previous block position is */
1115 /* known only if the previous CCW was a read or a write, so any */
1116 /* attempt to issue more than one consecutive backspace block on */
1117 /* an ASCII text file will fail with unit check status. */
1118 /*-------------------------------------------------------------------*/
bsb_omatape(DEVBLK * dev,BYTE * unitstat,BYTE code)1119 int bsb_omatape (DEVBLK *dev, BYTE *unitstat,BYTE code)
1120 {
1121 int rc; /* Return code */
1122 OMATAPE_DESC *omadesc; /* -> OMA descriptor entry */
1123 long blkpos; /* Offset of block header */
1124 S32 curblkl; /* Length of current block */
1125 S32 prvhdro; /* Offset of previous header */
1126 S32 nxthdro; /* Offset of next header */
1127
1128 /* Point to the current file entry in the OMA descriptor table */
1129 omadesc = (OMATAPE_DESC*)(dev->omadesc);
1130 omadesc += (dev->curfilen-1);
1131
1132 /* Backspace file if current position is at start of file */
1133 if (dev->nxtblkpos == 0)
1134 {
1135 /* Unit check if already at start of tape */
1136 if (dev->curfilen <= 1)
1137 {
1138 build_senseX(TAPE_BSENSE_LOADPTERR,dev,unitstat,code);
1139 return -1;
1140 }
1141
1142 /* Perform backspace file operation */
1143 rc = bsf_omatape (dev, unitstat,code);
1144 if (rc < 0) return -1;
1145
1146 dev->blockid--;
1147
1148 /* Return zero to indicate tapemark detected */
1149 return 0;
1150 }
1151
1152 /* Unit check if previous block position is unknown */
1153 if (dev->prvblkpos < 0)
1154 {
1155 build_senseX(TAPE_BSENSE_LOADPTERR,dev,unitstat,code);
1156 return -1;
1157 }
1158
1159 /* Backspace to previous block position */
1160 blkpos = dev->prvblkpos;
1161
1162 /* Determine new previous block position */
1163 switch (omadesc->format)
1164 {
1165 case 'H':
1166 /* For OMA headers files, read the previous block header to
1167 extract the block length and new previous block offset */
1168 rc = readhdr_omaheaders (dev, omadesc, blkpos, &curblkl,
1169 &prvhdro, &nxthdro, unitstat,code);
1170 if (rc < 0) return -1;
1171 break;
1172 case 'F':
1173 /* For OMA fixed block files, calculate the new previous
1174 block offset by subtracting the fixed block length */
1175 if (blkpos >= omadesc->blklen)
1176 prvhdro = blkpos - omadesc->blklen;
1177 else
1178 prvhdro = -1;
1179 break;
1180 default:
1181 case 'T':
1182 /* For OMA ASCII text files, new previous block is unknown */
1183 prvhdro = -1;
1184 break;
1185 } /* end switch(omadesc->format) */
1186
1187 /* Update the offsets of the next and previous blocks */
1188 dev->nxtblkpos = blkpos;
1189 dev->prvblkpos = prvhdro;
1190
1191 dev->blockid--;
1192
1193 /* Return +1 to indicate backspace successful */
1194 return +1;
1195
1196 } /* end function bsb_omatape */
1197
1198 /*-------------------------------------------------------------------*/
1199 /* Close an OMA tape file set */
1200 /* */
1201 /* All errors are ignored */
1202 /*-------------------------------------------------------------------*/
close_omatape2(DEVBLK * dev)1203 void close_omatape2(DEVBLK *dev)
1204 {
1205 if (dev->fd >= 0)
1206 close (dev->fd);
1207 dev->fd=-1;
1208 if (dev->omadesc != NULL)
1209 {
1210 free (dev->omadesc);
1211 dev->omadesc = NULL;
1212 }
1213
1214 /* Reset the device dependent fields */
1215 dev->nxtblkpos=0;
1216 dev->prvblkpos=-1;
1217 dev->curfilen=1;
1218 dev->blockid=0;
1219 dev->fenced = 0;
1220 dev->omafiles = 0;
1221 return;
1222 }
1223
1224 /*-------------------------------------------------------------------*/
1225 /* Close an OMA tape file set */
1226 /* */
1227 /* All errors are ignored */
1228 /* Change the filename to '*' - unloaded */
1229 /* TAPE REALLY UNLOADED */
1230 /*-------------------------------------------------------------------*/
close_omatape(DEVBLK * dev)1231 void close_omatape(DEVBLK *dev)
1232 {
1233 close_omatape2(dev);
1234 strcpy(dev->filename,TAPE_UNLOADED);
1235 dev->blockid = 0;
1236 dev->fenced = 0;
1237 return;
1238 }
1239
1240 /*-------------------------------------------------------------------*/
1241 /* Rewind an OMA tape file set */
1242 /* */
1243 /* All errors are ignored */
1244 /*-------------------------------------------------------------------*/
rewind_omatape(DEVBLK * dev,BYTE * unitstat,BYTE code)1245 int rewind_omatape(DEVBLK *dev,BYTE *unitstat,BYTE code)
1246 {
1247 UNREFERENCED(unitstat);
1248 UNREFERENCED(code);
1249 close_omatape2(dev);
1250 dev->fenced = 0;
1251 return 0;
1252 }
1253