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

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