root/drivers/char/ftape/ftape-eof.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. ftape_validate_label
  2. find_end_of_eof_list
  3. reset_eof_list
  4. check_for_eof
  5. clear_eof_mark_if_set
  6. put_file_mark_in_map
  7. ftape_weof
  8. ftape_erase
  9. extract_file_marks
  10. update_failed_sector_log
  11. ftape_seek_eom
  12. ftape_seek_eof
  13. ftape_file_no

   1 
   2 /*
   3  *      Copyright (C) 1994-1995 Bas Laarhoven.
   4 
   5  This program is free software; you can redistribute it and/or modify
   6  it under the terms of the GNU General Public License as published by
   7  the Free Software Foundation; either version 2, or (at your option)
   8  any later version.
   9 
  10  This program is distributed in the hope that it will be useful,
  11  but WITHOUT ANY WARRANTY; without even the implied warranty of
  12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13  GNU General Public License for more details.
  14 
  15  You should have received a copy of the GNU General Public License
  16  along with this program; see the file COPYING.  If not, write to
  17  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  18 
  19  $Source: /home/bas/distr/ftape-2.03b/RCS/ftape-eof.c,v $
  20  $Author: bas $
  21  *
  22  $Revision: 1.21 $
  23  $Date: 1995/05/27 08:54:21 $
  24  $State: Beta $
  25  *
  26  *      This file contains the eof mark handling code
  27  *      for the QIC-40/80 floppy-tape driver for Linux.
  28  */
  29 
  30 #include <linux/module.h>
  31 #include <linux/ftape.h>
  32 #include <linux/string.h>
  33 #include <linux/errno.h>
  34 
  35 #include "tracing.h"
  36 #include "ftape-eof.h"
  37 #include "ftape-write.h"
  38 #include "ftape-read.h"
  39 #include "ftape-rw.h"
  40 #include "ftape-ctl.h"
  41 #include "ftape-bsm.h"
  42 
  43 /*      Global vars.
  44  */
  45 int failed_sector_log_changed = 0;
  46 int eof_mark = 0;
  47 
  48 /*      Local vars.
  49  */
  50 static struct failed_sector_entry {
  51         unsigned short segment;
  52         unsigned short sector;
  53 } *eof_mark_ptr;
  54 
  55 typedef union {
  56         struct failed_sector_entry mark;
  57         unsigned long entry;
  58 } eof_mark_union;
  59 
  60 /*  a copy of the failed sector log from the header segment.
  61  */
  62 static eof_mark_union eof_map[(2048 - 256) / 4];
  63 
  64 /*  index into eof_map table pointing to last found eof mark.
  65  */
  66 static int eof_index;
  67 
  68 /*  number of eof marks (entries in bad sector log) on tape.
  69  */
  70 static int nr_of_eof_marks = -1;
  71 
  72 static char linux_tape_label[] = "Linux raw format V";
  73 enum {
  74         min_fmt_version = 1, max_fmt_version = 2
  75 };
  76 static unsigned ftape_fmt_version = 0;
  77 
  78 
  79 /*  Ftape (mis)uses the bad sector log to record end-of-file marks.
  80  *  Initially (when the tape is erased) all entries in the bad sector
  81  *  log are added to the tape's bad sector map. The bad sector log
  82  *  then is cleared.
  83  *
  84  *  The bad sector log normally contains entries of the form:
  85  *  even 16-bit word: segment number of bad sector
  86  *   odd 16-bit word: encoded date
  87  *  There can be a total of 448 entries (1792 bytes).
  88  *
  89  *  My guess is that no program is using this bad sector log (the
  90  *  format seems useless as there is no indication of the bad sector
  91  *  itself, only the segment)
  92  *  However, if any program does use the bad sector log, the format
  93  *  used by ftape will let the program think there are some bad sectors
  94  *  and no harm is done.
  95  *  
  96  *  The eof mark entries that ftape stores in the bad sector log:
  97  *  even 16-bit word: segment number of eof mark
  98  *   odd 16-bit word: sector number of eof mark [1..32]
  99  *  
 100  *  The eof_map as maintained is a sorted list of eof mark entries.
 101  *
 102  *
 103  *  The tape name field in the header segments is used to store a
 104  *  linux tape identification string and a version number.
 105  *  This way the tape can be recognized as a Linux raw format
 106  *  tape when using tools under other OS's.
 107  *
 108  *  'Wide' QIC tapes (format code 4) don't have a failed sector list
 109  *  anymore. That space is used for the (longer) bad sector map that
 110  *  now is a variable length list too.
 111  *  We now store our end-of-file marker list after the bad-sector-map
 112  *  on tape. The list is delimited by a (long) 0 entry.
 113  */
 114 
 115 int ftape_validate_label(char *label)
     /* [previous][next][first][last][top][bottom][index][help] */
 116 {
 117         TRACE_FUN(8, "ftape_validate_label");
 118         int result = 0;
 119 
 120         TRACEx1(4, "tape  label = `%s'", label);
 121         ftape_fmt_version = 0;
 122         if (memcmp(label, linux_tape_label, strlen(linux_tape_label)) == 0) {
 123                 int pos = strlen(linux_tape_label);
 124                 while (label[pos] >= '0' && label[pos] <= '9') {
 125                         ftape_fmt_version *= 10;
 126                         ftape_fmt_version = label[pos++] - '0';
 127                 }
 128                 result = (ftape_fmt_version >= min_fmt_version &&
 129                           ftape_fmt_version <= max_fmt_version);
 130         }
 131         TRACEx1(4, "format version = %d", ftape_fmt_version);
 132         TRACE_EXIT;
 133         return result;
 134 }
 135 
 136 static byte *
 137  find_end_of_eof_list(byte * ptr, byte * limit)
     /* [previous][next][first][last][top][bottom][index][help] */
 138 {
 139         while (ptr + 3 < limit) {
 140                 if (*(unsigned long *) ptr) {
 141                         ++(unsigned long *) ptr;
 142                 } else {
 143                         return ptr;
 144                 }
 145         }
 146         return NULL;
 147 }
 148 
 149 void reset_eof_list(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 150 {
 151         TRACE_FUN(8, "reset_eof_list");
 152 
 153         eof_mark_ptr = &eof_map[0].mark;
 154         eof_index = 0;
 155         eof_mark = 0;
 156         TRACE_EXIT;
 157 }
 158 
 159 /*  Test if `segment' has an eof mark set (optimized for sequential access).
 160  *  return 0 if not eof mark or sector number (> 0) if eof mark set.
 161  */
 162 int check_for_eof(unsigned segment)
     /* [previous][next][first][last][top][bottom][index][help] */
 163 {
 164         TRACE_FUN(8, "check_for_eof");
 165         static unsigned last_reference = INT_MAX;
 166         int result;
 167 
 168         if (segment < last_reference) {
 169                 reset_eof_list();
 170         }
 171         last_reference = segment;
 172         while (eof_index < nr_of_eof_marks && segment > eof_mark_ptr->segment) {
 173                 ++eof_mark_ptr;
 174                 ++eof_index;
 175         }
 176         if (eof_index < nr_of_eof_marks && segment == eof_mark_ptr->segment) {
 177                 TRACEx3(5, "hit mark %d/%d at index %d",
 178                         eof_map[eof_index].mark.segment, eof_map[eof_index].mark.sector,
 179                         eof_index);
 180                 if (eof_mark_ptr->sector >= SECTORS_PER_SEGMENT) {
 181                         TRACEx2(-1, "Bad file mark detected: %d/%d",
 182                             eof_mark_ptr->segment, eof_mark_ptr->sector);
 183                         result = 0;     /* return bogus (but valid) value */
 184                 } else {
 185                         result = eof_mark_ptr->sector;
 186                 }
 187         } else {
 188                 result = 0;
 189         }
 190         TRACE_EXIT;
 191         return result;
 192 }
 193 
 194 void clear_eof_mark_if_set(unsigned segment, unsigned byte_count)
     /* [previous][next][first][last][top][bottom][index][help] */
 195 {
 196         TRACE_FUN(5, "clear_eof_mark_if_set");
 197         if (ftape_fmt_version != 0 &&
 198             check_for_eof(segment) > 0 &&
 199             byte_count >= eof_mark_ptr->sector * SECTOR_SIZE) {
 200                 TRACEx3(5, "clearing mark %d/%d at index %d",
 201                  eof_mark_ptr->segment, eof_mark_ptr->sector, eof_index);
 202                 memmove(&eof_map[eof_index], &eof_map[eof_index + 1],
 203                         (nr_of_eof_marks - eof_index) * sizeof(*eof_map));
 204                 --nr_of_eof_marks;
 205                 failed_sector_log_changed = 1;
 206         }
 207         TRACE_EXIT;
 208 }
 209 
 210 void put_file_mark_in_map(unsigned segment, unsigned sector)
     /* [previous][next][first][last][top][bottom][index][help] */
 211 {
 212         TRACE_FUN(8, "put_file_mark_in_map");
 213         eof_mark_union new;
 214         int index;
 215         eof_mark_union *ptr;
 216 
 217         if (ftape_fmt_version != 0) {
 218                 new.mark.segment = segment;
 219                 new.mark.sector = sector;
 220                 for (index = 0, ptr = &eof_map[0];
 221                   index < nr_of_eof_marks && ptr->mark.segment < segment;
 222                      ++index, ++ptr) {
 223                 }
 224                 if (index < nr_of_eof_marks) {
 225                         if (ptr->mark.segment == segment) {
 226                                 /* overwrite */
 227                                 if (ptr->mark.sector == sector) {
 228                                         TRACEx2(5, "mark %d/%d already exists",
 229                                                 new.mark.segment, new.mark.sector);
 230                                 } else {
 231                                         TRACEx5(5, "overwriting %d/%d at index %d with %d/%d",
 232                                                 ptr->mark.segment, ptr->mark.sector, index,
 233                                                 new.mark.segment, new.mark.sector);
 234                                         ptr->entry = new.entry;
 235                                         failed_sector_log_changed = 1;
 236                                 }
 237                         } else {
 238                                 /* insert */
 239                                 TRACEx5(5, "inserting %d/%d at index %d before %d/%d",
 240                                 new.mark.segment, new.mark.sector, index,
 241                                     ptr->mark.segment, ptr->mark.sector);
 242                                 memmove(ptr + 1, ptr, (nr_of_eof_marks - index) * sizeof(*eof_map));
 243                                 ptr->entry = new.entry;
 244                                 ++nr_of_eof_marks;
 245                                 failed_sector_log_changed = 1;
 246                         }
 247                 } else {
 248                         /* append */
 249                         TRACEx3(5, "appending %d/%d at index %d",
 250                                 new.mark.segment, new.mark.sector, index);
 251                         ptr->entry = new.entry;
 252                         ++nr_of_eof_marks;
 253                         failed_sector_log_changed = 1;
 254                 }
 255         }
 256         TRACE_EXIT;
 257 }
 258 
 259 /*  Write count file marks to tape starting at first non-bad
 260  *  sector following the given segment and sector.
 261  *  sector = base 1 !
 262  */
 263 int ftape_weof(unsigned count, unsigned segment, unsigned sector)
     /* [previous][next][first][last][top][bottom][index][help] */
 264 {
 265         TRACE_FUN(5, "ftape_weof");
 266         int result = 0;
 267         unsigned long mask = get_bad_sector_entry(segment);
 268         unsigned sector_nr = 0;
 269 
 270         if (ftape_fmt_version != 0) {
 271                 if (sector < 1 || sector > 29 ||
 272                     segment + count >= ftape_last_segment.id) {
 273                         TRACEx3(5, "parameter out of range: %d, %d, %d", count, segment, sector);
 274                         result = -EIO;
 275                 } else {
 276                         while (count-- > 0) {
 277                                 do {    /* count logical sectors */
 278                                         do {    /* skip until good sector */
 279                                                 while (mask & 1) {      /* skip bad sectors */
 280                                                         ++sector_nr;
 281                                                         mask >>= 1;
 282                                                 }
 283                                                 if (sector_nr >= 29) {
 284                                                         if (++segment >= ftape_last_segment.id) {
 285                                                                 TRACEx1(5, "segment out of range: %d", segment);
 286                                                                 result = -EIO;
 287                                                                 break;
 288                                                         }
 289                                                         mask = get_bad_sector_entry(segment);
 290                                                         sector_nr = 0;
 291                                                 }
 292                                         } while (mask & 1);
 293                                         ++sector_nr;    /* point to good sector */
 294                                         mask >>= 1;
 295                                 } while (--sector);
 296                                 if (result >= 0) {
 297                                         TRACEx2(5, "writing filemark %d/%d", segment, sector_nr);
 298                                         put_file_mark_in_map(segment, sector_nr);
 299                                         ++segment;      /* next segment */
 300                                         sector_nr = 0;
 301                                         sector = 1;     /* first sector */
 302                                 }
 303                         }
 304                 }
 305         } else {
 306                 result = -EPERM;
 307         }
 308         TRACE_EXIT;
 309         return result;
 310 }
 311 
 312 int ftape_erase(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 313 {
 314         TRACE_FUN(5, "ftape_erase");
 315         int result = 0;
 316         int i;
 317         unsigned long now = 0;
 318         byte *buffer = deblock_buffer;
 319 
 320         if (write_protected) {
 321                 result = -EROFS;
 322         } else {
 323                 result = read_header_segment(buffer);
 324                 if (result >= 0) {
 325                         /*  Copy entries from bad-sector-log into bad-sector-map
 326                          */
 327                         TRACEx1(5, "old label: `%s'", (char *) (buffer + 30));
 328                         if (!ftape_validate_label((char *) &buffer[30])) {
 329                                 TRACE(5, "invalid label, overwriting with new");
 330                                 memset(buffer + 30, 0, 44);
 331                                 memcpy(buffer + 30, linux_tape_label, strlen(linux_tape_label));
 332                                 buffer[30 + strlen(linux_tape_label)] = '2';
 333                                 TRACEx1(5, "new label: `%s'", (char *) (buffer + 30));
 334                                 PUT4(buffer, 74, now);
 335                                 if (format_code != 4) {
 336                                         for (i = 0; i < nr_of_eof_marks; ++i) {
 337                                                 unsigned failing_segment = eof_map[i].mark.segment;
 338 
 339                                                 if (!valid_segment_no(failing_segment)) {
 340                                                         TRACEi(4, "bad entry in failed sector log:", failing_segment);
 341                                                 } else {
 342                                                         put_bad_sector_entry(failing_segment, EMPTY_SEGMENT);
 343                                                         TRACEx2(4, "moved entry %d from failed sector log (%d)",
 344                                                                 i, failing_segment);
 345                                                 }
 346                                         }
 347                                 }
 348                         }
 349                         /*  Clear failed sector log: remove all tape marks
 350                          */
 351                         failed_sector_log_changed = 1;
 352                         memset(eof_map, 0, sizeof(eof_map));
 353                         nr_of_eof_marks = 0;
 354                         ftape_fmt_version = max_fmt_version;
 355 #if 0
 356                         fix_tape(buffer);       /* see ftape-bsm.c ! */
 357 #endif
 358                         result = ftape_update_header_segments(buffer, 1);
 359                         prevent_flush();        /* prevent flush_buffers writing file marks */
 360                         reset_eof_list();
 361                 }
 362         }
 363         TRACE_EXIT;
 364         return result;
 365 }
 366 
 367 void extract_file_marks(byte * address)
     /* [previous][next][first][last][top][bottom][index][help] */
 368 {
 369         TRACE_FUN(8, "extract_file_marks");
 370         int i;
 371 
 372         if (format_code == 4) {
 373                 byte *end;
 374                 byte *start = find_end_of_bsm_list(address + 256,
 375                                              address + 29 * SECTOR_SIZE);
 376 
 377                 memset(eof_map, 0, sizeof(eof_map));
 378                 nr_of_eof_marks = 0;
 379                 if (start) {
 380                         start += 3;     /* skip end of list mark */
 381                         end = find_end_of_eof_list(start, address + 29 * SECTOR_SIZE);
 382                         if (end && end - start <= sizeof(eof_map)) {
 383                                 nr_of_eof_marks = (end - start) / sizeof(unsigned long);
 384                                 memcpy(eof_map, start, end - start);
 385                         } else {
 386                                 TRACE(1, "File Mark List is too long or damaged !");
 387                         }
 388                 } else {
 389                         TRACE(1, "Bad Sector List is too long or damaged !");
 390                 }
 391         } else {
 392                 memcpy(eof_map, address + 256, sizeof(eof_map));
 393                 nr_of_eof_marks = GET2(address, 144);
 394         }
 395         TRACEi(4, "number of file marks:", nr_of_eof_marks);
 396         if (ftape_fmt_version == 1) {
 397                 TRACE(-1, "swapping version 1 fields");
 398                 /*  version 1 format uses swapped sector and segment fields, correct that !
 399                  */
 400                 for (i = 0; i < nr_of_eof_marks; ++i) {
 401                         unsigned short tmp = eof_map[i].mark.segment;
 402                         eof_map[i].mark.segment = eof_map[i].mark.sector;
 403                         eof_map[i].mark.sector = tmp;
 404                 }
 405         }
 406         for (i = 0; i < nr_of_eof_marks; ++i) {
 407                 TRACEx2(4, "eof mark: %5d/%2d",
 408                         eof_map[i].mark.segment, eof_map[i].mark.sector);
 409         }
 410         reset_eof_list();
 411         TRACE_EXIT;
 412 }
 413 
 414 int update_failed_sector_log(byte * buffer)
     /* [previous][next][first][last][top][bottom][index][help] */
 415 {
 416         TRACE_FUN(8, "update_failed_sector_log");
 417 
 418         if (ftape_fmt_version != 0 && failed_sector_log_changed) {
 419                 if (ftape_fmt_version == 1) {
 420                         TRACE(-1, "upgrading version 1 format to version 2");
 421                         /*  version 1 will be upgraded to version 2 when written.
 422                          */
 423                         buffer[30 + strlen(linux_tape_label)] = '2';
 424                         ftape_fmt_version = 2;
 425                         TRACEx1(-1, "new tape label = \"%s\"", &buffer[30]);
 426                 }
 427                 if (format_code == 4) {
 428                         byte *dest = find_end_of_bsm_list(buffer + 256,
 429                                           buffer + 29 * SECTOR_SIZE) + 3;
 430 
 431                         if (dest) {
 432                                 TRACEx2(4, "eof_map at byte offset %6d, size %d",
 433                                         dest - buffer - 256, nr_of_eof_marks * sizeof(unsigned long));
 434                                 memcpy(dest, eof_map, nr_of_eof_marks * sizeof(unsigned long));
 435                                 PUT4(dest, nr_of_eof_marks * sizeof(unsigned long), 0);
 436                         }
 437                 } else {
 438                         memcpy(buffer + 256, eof_map, sizeof(eof_map));
 439                         PUT2(buffer, 144, nr_of_eof_marks);
 440                 }
 441                 failed_sector_log_changed = 0;
 442                 return 1;
 443         }
 444         TRACE_EXIT;
 445         return 0;
 446 }
 447 
 448 int ftape_seek_eom(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 449 {
 450         TRACE_FUN(5, "ftape_seek_eom");
 451         int result = 0;
 452         unsigned eom;
 453 
 454         if (first_data_segment == -1) {
 455                 result = read_header_segment(deblock_buffer);
 456         }
 457         if (result >= 0 && ftape_fmt_version != 0) {
 458                 eom = first_data_segment;
 459                 eof_index = 0;
 460                 eof_mark_ptr = &eof_map[0].mark;
 461                 /*  If fresh tape, count should be zero but we don't
 462                  *  want to worry about the case it's one.
 463                  */
 464                 for (eof_index = 1, eof_mark_ptr = &eof_map[1].mark;
 465                      eof_index < nr_of_eof_marks; ++eof_index, ++eof_mark_ptr) {
 466                         /*  The eom is recorded as two eof marks in succeeding segments
 467                          *  where the second one is always at segment number 1.
 468                          */
 469                         if (eof_mark_ptr->sector == 1) {
 470                                 if (eof_mark_ptr->segment == (eof_mark_ptr - 1)->segment + 1) {
 471                                         eom = eof_mark_ptr->segment;
 472                                         break;
 473                                 }
 474                         }
 475                 }
 476                 ftape_seg_pos = eom;
 477                 TRACEx1(5, "eom found at segment %d", eom);
 478         } else {
 479                 TRACE(5, "Couldn't get eof mark table");
 480                 result = -EIO;
 481         }
 482         TRACE_EXIT;
 483         return result;
 484 }
 485 
 486 int ftape_seek_eof(unsigned count)
     /* [previous][next][first][last][top][bottom][index][help] */
 487 {
 488         TRACE_FUN(5, "ftape_seek_eof");
 489         int result = 0;
 490         enum {
 491                 not = 0, begin, end
 492         } bad_seek = not;
 493 
 494         if (first_data_segment == -1) {
 495                 result = read_header_segment(deblock_buffer);
 496         }
 497         TRACEx1(5, "tape positioned at segment %d", ftape_seg_pos);
 498         if (ftape_fmt_version == 0) {
 499                 result = -1;
 500         }
 501         if (result >= 0 && count != 0) {
 502                 for (eof_index = 0; eof_index <= nr_of_eof_marks; ++eof_index) {
 503                         if (eof_index == nr_of_eof_marks ||     /* start seeking after last mark */
 504                             ftape_seg_pos <= eof_map[eof_index].mark.segment) {
 505                                 eof_index += count;
 506                                 if (eof_index < 1) {    /* begin of tape */
 507                                         ftape_seg_pos = first_data_segment;
 508                                         if (eof_index < 0) {    /* `before' begin of tape */
 509                                                 eof_index = 0;
 510                                                 bad_seek = begin;
 511                                         }
 512                                 } else if (eof_index >= nr_of_eof_marks) {      /* `after' end of tape */
 513                                         ftape_seg_pos = segments_per_track * tracks_per_tape;
 514                                         if (eof_index > nr_of_eof_marks) {
 515                                                 eof_index = nr_of_eof_marks;
 516                                                 bad_seek = end;
 517                                         }
 518                                 } else {        /* after requested file mark */
 519                                         ftape_seg_pos = eof_map[eof_index - 1].mark.segment + 1;
 520                                 }
 521                                 eof_mark_ptr = &eof_map[eof_index].mark;
 522                                 break;
 523                         }
 524                 }
 525         }
 526         if (result < 0) {
 527                 TRACE(5, "Couldn't get eof mark table");
 528                 result = -EIO;
 529         } else if (bad_seek != not) {
 530                 TRACEx1(1, "seek reached %s of tape",
 531                         (bad_seek == begin) ? "begin" : "end");
 532                 result = -EIO;
 533         } else {
 534                 TRACEx1(5, "tape repositioned to segment %d", ftape_seg_pos);
 535         }
 536         TRACE_EXIT;
 537         return result;
 538 }
 539 
 540 int ftape_file_no(daddr_t * f_no, daddr_t * b_no)
     /* [previous][next][first][last][top][bottom][index][help] */
 541 {
 542         TRACE_FUN(5, "ftape_file_no");
 543         int result = 0;
 544         int i;
 545 
 546         *f_no = eof_index;
 547         *b_no = ftape_seg_pos;
 548         TRACEi(4, "number of file marks:", nr_of_eof_marks);
 549         for (i = 0; i < nr_of_eof_marks; ++i) {
 550                 TRACEx2(4, "eof mark: %5d/%2d",
 551                         eof_map[i].mark.segment, eof_map[i].mark.sector);
 552         }
 553         TRACE_EXIT;
 554         return result;
 555 }

/* [previous][next][first][last][top][bottom][index][help] */