Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (c) 1995-1997 Claus-Justus Heine |
| 3 | |
| 4 | This program is free software; you can redistribute it and/or |
| 5 | modify it under the terms of the GNU General Public License as |
| 6 | published by the Free Software Foundation; either version 2, or (at |
| 7 | your option) any later version. |
| 8 | |
| 9 | This program is distributed in the hope that it will be useful, but |
| 10 | WITHOUT ANY WARRANTY; without even the implied warranty of |
| 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 12 | General Public License for more details. |
| 13 | |
| 14 | You should have received a copy of the GNU General Public License |
| 15 | along with this program; see the file COPYING. If not, write to |
| 16 | the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, |
| 17 | USA. |
| 18 | |
| 19 | * |
| 20 | * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-vtbl.c,v $ |
| 21 | * $Revision: 1.7.6.1 $ |
| 22 | * $Date: 1997/11/24 13:48:31 $ |
| 23 | * |
| 24 | * This file defines a volume table as defined in various QIC |
| 25 | * standards. |
| 26 | * |
| 27 | * This is a minimal implementation, just allowing ordinary DOS |
| 28 | * :( prgrams to identify the cartridge as used. |
| 29 | */ |
| 30 | |
| 31 | #include <linux/errno.h> |
| 32 | #include <linux/mm.h> |
| 33 | #include <linux/slab.h> |
| 34 | |
| 35 | #include <linux/zftape.h> |
| 36 | #include "../zftape/zftape-init.h" |
| 37 | #include "../zftape/zftape-eof.h" |
| 38 | #include "../zftape/zftape-ctl.h" |
| 39 | #include "../zftape/zftape-write.h" |
| 40 | #include "../zftape/zftape-read.h" |
| 41 | #include "../zftape/zftape-rw.h" |
| 42 | #include "../zftape/zftape-vtbl.h" |
| 43 | |
| 44 | #define ZFT_CMAP_HACK /* leave this defined to hide the compression map */ |
| 45 | |
| 46 | /* |
| 47 | * global variables |
| 48 | */ |
| 49 | int zft_qic_mode = 1; /* use the vtbl */ |
| 50 | int zft_old_ftape; /* prevents old ftaped tapes to be overwritten */ |
| 51 | int zft_volume_table_changed; /* for write_header_segments() */ |
| 52 | |
| 53 | /* |
| 54 | * private variables (only exported for inline functions) |
| 55 | */ |
| 56 | LIST_HEAD(zft_vtbl); |
| 57 | |
| 58 | /* We could also allocate these dynamically when extracting the volume table |
| 59 | * sizeof(zft_volinfo) is about 32 or something close to that |
| 60 | */ |
| 61 | static zft_volinfo tape_vtbl; |
| 62 | static zft_volinfo eot_vtbl; |
| 63 | static zft_volinfo *cur_vtbl; |
| 64 | |
| 65 | static inline void zft_new_vtbl_entry(void) |
| 66 | { |
| 67 | struct list_head *tmp = &zft_last_vtbl->node; |
| 68 | zft_volinfo *new = zft_kmalloc(sizeof(zft_volinfo)); |
| 69 | |
| 70 | list_add(&new->node, tmp); |
| 71 | new->count = zft_eom_vtbl->count ++; |
| 72 | } |
| 73 | |
| 74 | void zft_free_vtbl(void) |
| 75 | { |
| 76 | for (;;) { |
| 77 | struct list_head *tmp = zft_vtbl.prev; |
| 78 | zft_volinfo *vtbl; |
| 79 | |
| 80 | if (tmp == &zft_vtbl) |
| 81 | break; |
| 82 | list_del(tmp); |
| 83 | vtbl = list_entry(tmp, zft_volinfo, node); |
| 84 | zft_kfree(vtbl, sizeof(zft_volinfo)); |
| 85 | } |
| 86 | INIT_LIST_HEAD(&zft_vtbl); |
| 87 | cur_vtbl = NULL; |
| 88 | } |
| 89 | |
| 90 | /* initialize vtbl, called by ftape_new_cartridge() |
| 91 | */ |
| 92 | void zft_init_vtbl(void) |
| 93 | { |
| 94 | zft_volinfo *new; |
| 95 | |
| 96 | zft_free_vtbl(); |
| 97 | |
| 98 | /* Create the two dummy vtbl entries |
| 99 | */ |
| 100 | new = zft_kmalloc(sizeof(zft_volinfo)); |
| 101 | list_add(&new->node, &zft_vtbl); |
| 102 | new = zft_kmalloc(sizeof(zft_volinfo)); |
| 103 | list_add(&new->node, &zft_vtbl); |
| 104 | zft_head_vtbl->end_seg = ft_first_data_segment; |
| 105 | zft_head_vtbl->blk_sz = zft_blk_sz; |
| 106 | zft_head_vtbl->count = -1; |
| 107 | zft_eom_vtbl->start_seg = ft_first_data_segment + 1; |
| 108 | zft_eom_vtbl->end_seg = ft_last_data_segment + 1; |
| 109 | zft_eom_vtbl->blk_sz = zft_blk_sz; |
| 110 | zft_eom_vtbl->count = 0; |
| 111 | |
| 112 | /* Reset the pointer for zft_find_volume() |
| 113 | */ |
| 114 | cur_vtbl = zft_eom_vtbl; |
| 115 | |
| 116 | /* initialize the dummy vtbl entries for zft_qic_mode == 0 |
| 117 | */ |
| 118 | eot_vtbl.start_seg = ft_last_data_segment + 1; |
| 119 | eot_vtbl.end_seg = ft_last_data_segment + 1; |
| 120 | eot_vtbl.blk_sz = zft_blk_sz; |
| 121 | eot_vtbl.count = -1; |
| 122 | tape_vtbl.start_seg = ft_first_data_segment; |
| 123 | tape_vtbl.end_seg = ft_last_data_segment; |
| 124 | tape_vtbl.blk_sz = zft_blk_sz; |
| 125 | tape_vtbl.size = zft_capacity; |
| 126 | tape_vtbl.count = 0; |
| 127 | } |
| 128 | |
| 129 | /* check for a valid VTBL signature. |
| 130 | */ |
| 131 | static int vtbl_signature_valid(__u8 signature[4]) |
| 132 | { |
| 133 | const char *vtbl_ids[] = VTBL_IDS; /* valid signatures */ |
| 134 | int j; |
| 135 | |
| 136 | for (j = 0; |
| 137 | (j < NR_ITEMS(vtbl_ids)) && (memcmp(signature, vtbl_ids[j], 4) != 0); |
| 138 | j++); |
| 139 | return j < NR_ITEMS(vtbl_ids); |
| 140 | } |
| 141 | |
| 142 | /* We used to store the block-size of the volume in the volume-label, |
| 143 | * using the keyword "blocksize". The blocksize written to the |
| 144 | * volume-label is in bytes. |
| 145 | * |
| 146 | * We use this now only for compatibility with old zftape version. We |
| 147 | * store the blocksize directly as binary number in the vendor |
| 148 | * extension part of the volume entry. |
| 149 | */ |
| 150 | static int check_volume_label(const char *label, int *blk_sz) |
| 151 | { |
| 152 | int valid_format; |
| 153 | char *blocksize; |
| 154 | TRACE_FUN(ft_t_flow); |
| 155 | |
| 156 | TRACE(ft_t_noise, "called with \"%s\" / \"%s\"", label, ZFT_VOL_NAME); |
| 157 | if (strncmp(label, ZFT_VOL_NAME, strlen(ZFT_VOL_NAME)) != 0) { |
| 158 | *blk_sz = 1; /* smallest block size that we allow */ |
| 159 | valid_format = 0; |
| 160 | } else { |
| 161 | TRACE(ft_t_noise, "got old style zftape vtbl entry"); |
| 162 | /* get the default blocksize */ |
| 163 | /* use the kernel strstr() */ |
| 164 | blocksize= strstr(label, " blocksize "); |
| 165 | if (blocksize) { |
| 166 | blocksize += strlen(" blocksize "); |
| 167 | for(*blk_sz= 0; |
| 168 | *blocksize >= '0' && *blocksize <= '9'; |
| 169 | blocksize++) { |
| 170 | *blk_sz *= 10; |
| 171 | *blk_sz += *blocksize - '0'; |
| 172 | } |
| 173 | if (*blk_sz > ZFT_MAX_BLK_SZ) { |
| 174 | *blk_sz= 1; |
| 175 | valid_format= 0; |
| 176 | } else { |
| 177 | valid_format = 1; |
| 178 | } |
| 179 | } else { |
| 180 | *blk_sz= 1; |
| 181 | valid_format= 0; |
| 182 | } |
| 183 | } |
| 184 | TRACE_EXIT valid_format; |
| 185 | } |
| 186 | |
| 187 | /* check for a zftape volume |
| 188 | */ |
| 189 | static int check_volume(__u8 *entry, zft_volinfo *volume) |
| 190 | { |
| 191 | TRACE_FUN(ft_t_flow); |
| 192 | |
| 193 | if(strncmp(&entry[VTBL_EXT+EXT_ZFTAPE_SIG], ZFTAPE_SIG, |
| 194 | strlen(ZFTAPE_SIG)) == 0) { |
| 195 | TRACE(ft_t_noise, "got new style zftape vtbl entry"); |
| 196 | volume->blk_sz = GET2(entry, VTBL_EXT+EXT_ZFTAPE_BLKSZ); |
| 197 | volume->qic113 = entry[VTBL_EXT+EXT_ZFTAPE_QIC113]; |
| 198 | TRACE_EXIT 1; |
| 199 | } else { |
| 200 | TRACE_EXIT check_volume_label(&entry[VTBL_DESC], &volume->blk_sz); |
| 201 | } |
| 202 | } |
| 203 | |
| 204 | |
| 205 | /* create zftape specific vtbl entry, the volume bounds are inserted |
| 206 | * in the calling function, zft_create_volume_headers() |
| 207 | */ |
| 208 | static void create_zft_volume(__u8 *entry, zft_volinfo *vtbl) |
| 209 | { |
| 210 | TRACE_FUN(ft_t_flow); |
| 211 | |
| 212 | memset(entry, 0, VTBL_SIZE); |
| 213 | memcpy(&entry[VTBL_SIG], VTBL_ID, 4); |
| 214 | sprintf(&entry[VTBL_DESC], ZFT_VOL_NAME" %03d", vtbl->count); |
| 215 | entry[VTBL_FLAGS] = (VTBL_FL_NOT_VERIFIED | VTBL_FL_SEG_SPANNING); |
| 216 | entry[VTBL_M_NO] = 1; /* multi_cartridge_count */ |
| 217 | strcpy(&entry[VTBL_EXT+EXT_ZFTAPE_SIG], ZFTAPE_SIG); |
| 218 | PUT2(entry, VTBL_EXT+EXT_ZFTAPE_BLKSZ, vtbl->blk_sz); |
| 219 | if (zft_qic113) { |
| 220 | PUT8(entry, VTBL_DATA_SIZE, vtbl->size); |
| 221 | entry[VTBL_CMPR] = VTBL_CMPR_UNREG; |
| 222 | if (vtbl->use_compression) { /* use compression: */ |
| 223 | entry[VTBL_CMPR] |= VTBL_CMPR_USED; |
| 224 | } |
| 225 | entry[VTBL_EXT+EXT_ZFTAPE_QIC113] = 1; |
| 226 | } else { |
| 227 | PUT4(entry, VTBL_DATA_SIZE, vtbl->size); |
| 228 | entry[VTBL_K_CMPR] = VTBL_CMPR_UNREG; |
| 229 | if (vtbl->use_compression) { /* use compression: */ |
| 230 | entry[VTBL_K_CMPR] |= VTBL_CMPR_USED; |
| 231 | } |
| 232 | } |
| 233 | if (ft_format_code == fmt_big) { |
| 234 | /* SCSI like vtbl, store the number of used |
| 235 | * segments as 4 byte value |
| 236 | */ |
| 237 | PUT4(entry, VTBL_SCSI_SEGS, vtbl->end_seg-vtbl->start_seg + 1); |
| 238 | } else { |
| 239 | /* normal, QIC-80MC like vtbl |
| 240 | */ |
| 241 | PUT2(entry, VTBL_START, vtbl->start_seg); |
| 242 | PUT2(entry, VTBL_END, vtbl->end_seg); |
| 243 | } |
| 244 | TRACE_EXIT; |
| 245 | } |
| 246 | |
| 247 | /* this one creates the volume headers for each volume. It is assumed |
| 248 | * that buffer already contains the old volume-table, so that vtbl |
| 249 | * entries without the zft_volume flag set can savely be ignored. |
| 250 | */ |
| 251 | static void zft_create_volume_headers(__u8 *buffer) |
| 252 | { |
| 253 | __u8 *entry; |
| 254 | struct list_head *tmp; |
| 255 | zft_volinfo *vtbl; |
| 256 | TRACE_FUN(ft_t_flow); |
| 257 | |
| 258 | #ifdef ZFT_CMAP_HACK |
| 259 | if((strncmp(&buffer[VTBL_EXT+EXT_ZFTAPE_SIG], ZFTAPE_SIG, |
| 260 | strlen(ZFTAPE_SIG)) == 0) && |
| 261 | buffer[VTBL_EXT+EXT_ZFTAPE_CMAP] != 0) { |
| 262 | TRACE(ft_t_noise, "deleting cmap volume"); |
| 263 | memmove(buffer, buffer + VTBL_SIZE, |
| 264 | FT_SEGMENT_SIZE - VTBL_SIZE); |
| 265 | } |
| 266 | #endif |
| 267 | entry = buffer; |
| 268 | for (tmp = zft_head_vtbl->node.next; |
| 269 | tmp != &zft_eom_vtbl->node; |
| 270 | tmp = tmp->next) { |
| 271 | vtbl = list_entry(tmp, zft_volinfo, node); |
| 272 | /* we now fill in the values only for newly created volumes. |
| 273 | */ |
| 274 | if (vtbl->new_volume) { |
| 275 | create_zft_volume(entry, vtbl); |
| 276 | vtbl->new_volume = 0; /* clear the flag */ |
| 277 | } |
| 278 | |
| 279 | DUMP_VOLINFO(ft_t_noise, &entry[VTBL_DESC], vtbl); |
| 280 | entry += VTBL_SIZE; |
| 281 | } |
| 282 | memset(entry, 0, FT_SEGMENT_SIZE - zft_eom_vtbl->count * VTBL_SIZE); |
| 283 | TRACE_EXIT; |
| 284 | } |
| 285 | |
| 286 | /* write volume table to tape. Calls zft_create_volume_headers() |
| 287 | */ |
| 288 | int zft_update_volume_table(unsigned int segment) |
| 289 | { |
| 290 | int result = 0; |
| 291 | __u8 *verify_buf = NULL; |
| 292 | TRACE_FUN(ft_t_flow); |
| 293 | |
| 294 | TRACE_CATCH(result = ftape_read_segment(ft_first_data_segment, |
| 295 | zft_deblock_buf, |
| 296 | FT_RD_SINGLE),); |
| 297 | zft_create_volume_headers(zft_deblock_buf); |
| 298 | TRACE(ft_t_noise, "writing volume table segment %d", segment); |
| 299 | if (zft_vmalloc_once(&verify_buf, FT_SEGMENT_SIZE) == 0) { |
| 300 | TRACE_CATCH(zft_verify_write_segments(segment, |
| 301 | zft_deblock_buf, result, |
| 302 | verify_buf), |
| 303 | zft_vfree(&verify_buf, FT_SEGMENT_SIZE)); |
| 304 | zft_vfree(&verify_buf, FT_SEGMENT_SIZE); |
| 305 | } else { |
| 306 | TRACE_CATCH(ftape_write_segment(segment, zft_deblock_buf, |
| 307 | FT_WR_SINGLE),); |
| 308 | } |
| 309 | TRACE_EXIT 0; |
| 310 | } |
| 311 | |
| 312 | /* non zftape volumes are handled in raw mode. Thus we need to |
| 313 | * calculate the raw amount of data contained in those segments. |
| 314 | */ |
| 315 | static void extract_alien_volume(__u8 *entry, zft_volinfo *vtbl) |
| 316 | { |
| 317 | TRACE_FUN(ft_t_flow); |
| 318 | |
| 319 | vtbl->size = (zft_calc_tape_pos(zft_last_vtbl->end_seg+1) - |
| 320 | zft_calc_tape_pos(zft_last_vtbl->start_seg)); |
| 321 | vtbl->use_compression = 0; |
| 322 | vtbl->qic113 = zft_qic113; |
| 323 | if (vtbl->qic113) { |
| 324 | TRACE(ft_t_noise, |
| 325 | "Fake alien volume's size from " LL_X " to " LL_X, |
| 326 | LL(GET8(entry, VTBL_DATA_SIZE)), LL(vtbl->size)); |
| 327 | } else { |
| 328 | TRACE(ft_t_noise, |
| 329 | "Fake alien volume's size from %d to " LL_X, |
| 330 | (int)GET4(entry, VTBL_DATA_SIZE), LL(vtbl->size)); |
| 331 | } |
| 332 | TRACE_EXIT; |
| 333 | } |
| 334 | |
| 335 | |
| 336 | /* extract an zftape specific volume |
| 337 | */ |
| 338 | static void extract_zft_volume(__u8 *entry, zft_volinfo *vtbl) |
| 339 | { |
| 340 | TRACE_FUN(ft_t_flow); |
| 341 | |
| 342 | if (vtbl->qic113) { |
| 343 | vtbl->size = GET8(entry, VTBL_DATA_SIZE); |
| 344 | vtbl->use_compression = |
| 345 | (entry[VTBL_CMPR] & VTBL_CMPR_USED) != 0; |
| 346 | } else { |
| 347 | vtbl->size = GET4(entry, VTBL_DATA_SIZE); |
| 348 | if (entry[VTBL_K_CMPR] & VTBL_CMPR_UNREG) { |
| 349 | vtbl->use_compression = |
| 350 | (entry[VTBL_K_CMPR] & VTBL_CMPR_USED) != 0; |
| 351 | } else if (entry[VTBL_CMPR] & VTBL_CMPR_UNREG) { |
| 352 | vtbl->use_compression = |
| 353 | (entry[VTBL_CMPR] & VTBL_CMPR_USED) != 0; |
| 354 | } else { |
| 355 | TRACE(ft_t_warn, "Geeh! There is something wrong:\n" |
| 356 | KERN_INFO "QIC compression (Rev = K): %x\n" |
| 357 | KERN_INFO "QIC compression (Rev > K): %x", |
| 358 | entry[VTBL_K_CMPR], entry[VTBL_CMPR]); |
| 359 | } |
| 360 | } |
| 361 | TRACE_EXIT; |
| 362 | } |
| 363 | |
| 364 | /* extract the volume table from buffer. "buffer" must already contain |
| 365 | * the vtbl-segment |
| 366 | */ |
| 367 | int zft_extract_volume_headers(__u8 *buffer) |
| 368 | { |
| 369 | __u8 *entry; |
| 370 | TRACE_FUN(ft_t_flow); |
| 371 | |
| 372 | zft_init_vtbl(); |
| 373 | entry = buffer; |
| 374 | #ifdef ZFT_CMAP_HACK |
| 375 | if ((strncmp(&entry[VTBL_EXT+EXT_ZFTAPE_SIG], ZFTAPE_SIG, |
| 376 | strlen(ZFTAPE_SIG)) == 0) && |
| 377 | entry[VTBL_EXT+EXT_ZFTAPE_CMAP] != 0) { |
| 378 | TRACE(ft_t_noise, "ignoring cmap volume"); |
| 379 | entry += VTBL_SIZE; |
| 380 | } |
| 381 | #endif |
| 382 | /* the end of the vtbl is indicated by an invalid signature |
| 383 | */ |
| 384 | while (vtbl_signature_valid(&entry[VTBL_SIG]) && |
| 385 | (entry - buffer) < FT_SEGMENT_SIZE) { |
| 386 | zft_new_vtbl_entry(); |
| 387 | if (ft_format_code == fmt_big) { |
| 388 | /* SCSI like vtbl, stores only the number of |
| 389 | * segments used |
| 390 | */ |
| 391 | unsigned int num_segments= GET4(entry, VTBL_SCSI_SEGS); |
| 392 | zft_last_vtbl->start_seg = zft_eom_vtbl->start_seg; |
| 393 | zft_last_vtbl->end_seg = |
| 394 | zft_last_vtbl->start_seg + num_segments - 1; |
| 395 | } else { |
| 396 | /* `normal', QIC-80 like vtbl |
| 397 | */ |
| 398 | zft_last_vtbl->start_seg = GET2(entry, VTBL_START); |
| 399 | zft_last_vtbl->end_seg = GET2(entry, VTBL_END); |
| 400 | } |
| 401 | zft_eom_vtbl->start_seg = zft_last_vtbl->end_seg + 1; |
| 402 | /* check if we created this volume and get the |
| 403 | * blk_sz |
| 404 | */ |
| 405 | zft_last_vtbl->zft_volume = check_volume(entry, zft_last_vtbl); |
| 406 | if (zft_last_vtbl->zft_volume == 0) { |
| 407 | extract_alien_volume(entry, zft_last_vtbl); |
| 408 | } else { |
| 409 | extract_zft_volume(entry, zft_last_vtbl); |
| 410 | } |
| 411 | DUMP_VOLINFO(ft_t_noise, &entry[VTBL_DESC], zft_last_vtbl); |
| 412 | entry +=VTBL_SIZE; |
| 413 | } |
| 414 | #if 0 |
| 415 | /* |
| 416 | * undefine to test end of tape handling |
| 417 | */ |
| 418 | zft_new_vtbl_entry(); |
| 419 | zft_last_vtbl->start_seg = zft_eom_vtbl->start_seg; |
| 420 | zft_last_vtbl->end_seg = ft_last_data_segment - 10; |
| 421 | zft_last_vtbl->blk_sz = zft_blk_sz; |
| 422 | zft_last_vtbl->zft_volume = 1; |
| 423 | zft_last_vtbl->qic113 = zft_qic113; |
| 424 | zft_last_vtbl->size = (zft_calc_tape_pos(zft_last_vtbl->end_seg+1) |
| 425 | - zft_calc_tape_pos(zft_last_vtbl->start_seg)); |
| 426 | #endif |
| 427 | TRACE_EXIT 0; |
| 428 | } |
| 429 | |
| 430 | /* this functions translates the failed_sector_log, misused as |
| 431 | * EOF-marker list, into a virtual volume table. The table mustn't be |
| 432 | * written to tape, because this would occupy the first data segment, |
| 433 | * which should be the volume table, but is actually the first segment |
| 434 | * that is filled with data (when using standard ftape). We assume, |
| 435 | * that we get a non-empty failed_sector_log. |
| 436 | */ |
| 437 | int zft_fake_volume_headers (eof_mark_union *eof_map, int num_failed_sectors) |
| 438 | { |
| 439 | unsigned int segment, sector; |
| 440 | int have_eom = 0; |
| 441 | int vol_no; |
| 442 | TRACE_FUN(ft_t_flow); |
| 443 | |
| 444 | if ((num_failed_sectors >= 2) && |
| 445 | (GET2(&eof_map[num_failed_sectors - 1].mark.segment, 0) |
| 446 | == |
| 447 | GET2(&eof_map[num_failed_sectors - 2].mark.segment, 0) + 1) && |
| 448 | (GET2(&eof_map[num_failed_sectors - 1].mark.date, 0) == 1)) { |
| 449 | /* this should be eom. We keep the remainder of the |
| 450 | * tape as another volume. |
| 451 | */ |
| 452 | have_eom = 1; |
| 453 | } |
| 454 | zft_init_vtbl(); |
| 455 | zft_eom_vtbl->start_seg = ft_first_data_segment; |
| 456 | for(vol_no = 0; vol_no < num_failed_sectors - have_eom; vol_no ++) { |
| 457 | zft_new_vtbl_entry(); |
| 458 | |
| 459 | segment = GET2(&eof_map[vol_no].mark.segment, 0); |
| 460 | sector = GET2(&eof_map[vol_no].mark.date, 0); |
| 461 | |
| 462 | zft_last_vtbl->start_seg = zft_eom_vtbl->start_seg; |
| 463 | zft_last_vtbl->end_seg = segment; |
| 464 | zft_eom_vtbl->start_seg = segment + 1; |
| 465 | zft_last_vtbl->blk_sz = 1; |
| 466 | zft_last_vtbl->size = |
| 467 | (zft_calc_tape_pos(zft_last_vtbl->end_seg) |
| 468 | - zft_calc_tape_pos(zft_last_vtbl->start_seg) |
| 469 | + (sector-1) * FT_SECTOR_SIZE); |
| 470 | TRACE(ft_t_noise, |
| 471 | "failed sector log: segment: %d, sector: %d", |
| 472 | segment, sector); |
| 473 | DUMP_VOLINFO(ft_t_noise, "Faked volume", zft_last_vtbl); |
| 474 | } |
| 475 | if (!have_eom) { |
| 476 | zft_new_vtbl_entry(); |
| 477 | zft_last_vtbl->start_seg = zft_eom_vtbl->start_seg; |
| 478 | zft_last_vtbl->end_seg = ft_last_data_segment; |
| 479 | zft_eom_vtbl->start_seg = ft_last_data_segment + 1; |
| 480 | zft_last_vtbl->size = zft_capacity; |
| 481 | zft_last_vtbl->size -= zft_calc_tape_pos(zft_last_vtbl->start_seg); |
| 482 | zft_last_vtbl->blk_sz = 1; |
| 483 | DUMP_VOLINFO(ft_t_noise, "Faked volume",zft_last_vtbl); |
| 484 | } |
| 485 | TRACE_EXIT 0; |
| 486 | } |
| 487 | |
| 488 | /* update the internal volume table |
| 489 | * |
| 490 | * if before start of last volume: erase all following volumes if |
| 491 | * inside a volume: set end of volume to infinity |
| 492 | * |
| 493 | * this function is intended to be called every time _ftape_write() is |
| 494 | * called |
| 495 | * |
| 496 | * return: 0 if no new volume was created, 1 if a new volume was |
| 497 | * created |
| 498 | * |
| 499 | * NOTE: we don't need to check for zft_mode as ftape_write() does |
| 500 | * that already. This function gets never called without accessing |
| 501 | * zftape via the *qft* devices |
| 502 | */ |
| 503 | |
| 504 | int zft_open_volume(zft_position *pos, int blk_sz, int use_compression) |
| 505 | { |
| 506 | TRACE_FUN(ft_t_flow); |
| 507 | |
| 508 | if (!zft_qic_mode) { |
| 509 | TRACE_EXIT 0; |
| 510 | } |
| 511 | if (zft_tape_at_lbot(pos)) { |
| 512 | zft_init_vtbl(); |
| 513 | if(zft_old_ftape) { |
| 514 | /* clear old ftape's eof marks */ |
| 515 | zft_clear_ftape_file_marks(); |
| 516 | zft_old_ftape = 0; /* no longer old ftape */ |
| 517 | } |
| 518 | zft_reset_position(pos); |
| 519 | } |
| 520 | if (pos->seg_pos != zft_last_vtbl->end_seg + 1) { |
| 521 | TRACE_ABORT(-EIO, ft_t_bug, |
| 522 | "BUG: seg_pos: %d, zft_last_vtbl->end_seg: %d", |
| 523 | pos->seg_pos, zft_last_vtbl->end_seg); |
| 524 | } |
| 525 | TRACE(ft_t_noise, "create new volume"); |
| 526 | if (zft_eom_vtbl->count >= ZFT_MAX_VOLUMES) { |
| 527 | TRACE_ABORT(-ENOSPC, ft_t_err, |
| 528 | "Error: maxmimal number of volumes exhausted " |
| 529 | "(maxmimum is %d)", ZFT_MAX_VOLUMES); |
| 530 | } |
| 531 | zft_new_vtbl_entry(); |
| 532 | pos->volume_pos = pos->seg_byte_pos = 0; |
| 533 | zft_last_vtbl->start_seg = pos->seg_pos; |
| 534 | zft_last_vtbl->end_seg = ft_last_data_segment; /* infinity */ |
| 535 | zft_last_vtbl->blk_sz = blk_sz; |
| 536 | zft_last_vtbl->size = zft_capacity; |
| 537 | zft_last_vtbl->zft_volume = 1; |
| 538 | zft_last_vtbl->use_compression = use_compression; |
| 539 | zft_last_vtbl->qic113 = zft_qic113; |
| 540 | zft_last_vtbl->new_volume = 1; |
| 541 | zft_last_vtbl->open = 1; |
| 542 | zft_volume_table_changed = 1; |
| 543 | zft_eom_vtbl->start_seg = ft_last_data_segment + 1; |
| 544 | TRACE_EXIT 0; |
| 545 | } |
| 546 | |
| 547 | /* perform mtfsf, mtbsf, not allowed without zft_qic_mode |
| 548 | */ |
| 549 | int zft_skip_volumes(int count, zft_position *pos) |
| 550 | { |
| 551 | const zft_volinfo *vtbl; |
| 552 | TRACE_FUN(ft_t_flow); |
| 553 | |
| 554 | TRACE(ft_t_noise, "count: %d", count); |
| 555 | |
| 556 | vtbl= zft_find_volume(pos->seg_pos); |
| 557 | while (count > 0 && vtbl != zft_eom_vtbl) { |
| 558 | vtbl = list_entry(vtbl->node.next, zft_volinfo, node); |
| 559 | count --; |
| 560 | } |
| 561 | while (count < 0 && vtbl != zft_first_vtbl) { |
| 562 | vtbl = list_entry(vtbl->node.prev, zft_volinfo, node); |
| 563 | count ++; |
| 564 | } |
| 565 | pos->seg_pos = vtbl->start_seg; |
| 566 | pos->seg_byte_pos = 0; |
| 567 | pos->volume_pos = 0; |
| 568 | pos->tape_pos = zft_calc_tape_pos(pos->seg_pos); |
| 569 | zft_just_before_eof = vtbl->size == 0; |
| 570 | if (zft_cmpr_ops) { |
| 571 | (*zft_cmpr_ops->reset)(); |
| 572 | } |
| 573 | zft_deblock_segment = -1; /* no need to keep cache */ |
| 574 | TRACE(ft_t_noise, "repositioning to:\n" |
| 575 | KERN_INFO "zft_seg_pos : %d\n" |
| 576 | KERN_INFO "zft_seg_byte_pos : %d\n" |
| 577 | KERN_INFO "zft_tape_pos : " LL_X "\n" |
| 578 | KERN_INFO "zft_volume_pos : " LL_X "\n" |
| 579 | KERN_INFO "file number : %d", |
| 580 | pos->seg_pos, pos->seg_byte_pos, |
| 581 | LL(pos->tape_pos), LL(pos->volume_pos), vtbl->count); |
| 582 | zft_resid = count < 0 ? -count : count; |
| 583 | TRACE_EXIT zft_resid ? -EINVAL : 0; |
| 584 | } |
| 585 | |
| 586 | /* the following simply returns the raw data position of the EOM |
| 587 | * marker, MTIOCSIZE ioctl |
| 588 | */ |
| 589 | __s64 zft_get_eom_pos(void) |
| 590 | { |
| 591 | if (zft_qic_mode) { |
| 592 | return zft_calc_tape_pos(zft_eom_vtbl->start_seg); |
| 593 | } else { |
| 594 | /* there is only one volume in raw mode */ |
| 595 | return zft_capacity; |
| 596 | } |
| 597 | } |
| 598 | |
| 599 | /* skip to eom, used for MTEOM |
| 600 | */ |
| 601 | void zft_skip_to_eom(zft_position *pos) |
| 602 | { |
| 603 | TRACE_FUN(ft_t_flow); |
| 604 | pos->seg_pos = zft_eom_vtbl->start_seg; |
| 605 | pos->seg_byte_pos = |
| 606 | pos->volume_pos = |
| 607 | zft_just_before_eof = 0; |
| 608 | pos->tape_pos = zft_calc_tape_pos(pos->seg_pos); |
| 609 | TRACE(ft_t_noise, "ftape positioned to segment %d, data pos " LL_X, |
| 610 | pos->seg_pos, LL(pos->tape_pos)); |
| 611 | TRACE_EXIT; |
| 612 | } |
| 613 | |
| 614 | /* write an EOF-marker by setting zft_last_vtbl->end_seg to seg_pos. |
| 615 | * NOTE: this function assumes that zft_last_vtbl points to a valid |
| 616 | * vtbl entry |
| 617 | * |
| 618 | * NOTE: this routine always positions before the EOF marker |
| 619 | */ |
| 620 | int zft_close_volume(zft_position *pos) |
| 621 | { |
| 622 | TRACE_FUN(ft_t_any); |
| 623 | |
| 624 | if (zft_vtbl_empty || !zft_last_vtbl->open) { /* should not happen */ |
| 625 | TRACE(ft_t_noise, "There are no volumes to finish"); |
| 626 | TRACE_EXIT -EIO; |
| 627 | } |
| 628 | if (pos->seg_byte_pos == 0 && |
| 629 | pos->seg_pos != zft_last_vtbl->start_seg) { |
| 630 | pos->seg_pos --; |
| 631 | pos->seg_byte_pos = zft_get_seg_sz(pos->seg_pos); |
| 632 | } |
| 633 | zft_last_vtbl->end_seg = pos->seg_pos; |
| 634 | zft_last_vtbl->size = pos->volume_pos; |
| 635 | zft_volume_table_changed = 1; |
| 636 | zft_just_before_eof = 1; |
| 637 | zft_eom_vtbl->start_seg = zft_last_vtbl->end_seg + 1; |
| 638 | zft_last_vtbl->open = 0; /* closed */ |
| 639 | TRACE_EXIT 0; |
| 640 | } |
| 641 | |
| 642 | /* write count file-marks at current position. |
| 643 | * |
| 644 | * The tape is positioned after the eof-marker, that is at byte 0 of |
| 645 | * the segment following the eof-marker |
| 646 | * |
| 647 | * this function is only allowed in zft_qic_mode |
| 648 | * |
| 649 | * Only allowed when tape is at BOT or EOD. |
| 650 | */ |
| 651 | int zft_weof(unsigned int count, zft_position *pos) |
| 652 | { |
| 653 | |
| 654 | TRACE_FUN(ft_t_flow); |
| 655 | |
| 656 | if (!count) { /* write zero EOF marks should be a real no-op */ |
| 657 | TRACE_EXIT 0; |
| 658 | } |
| 659 | zft_volume_table_changed = 1; |
| 660 | if (zft_tape_at_lbot(pos)) { |
| 661 | zft_init_vtbl(); |
| 662 | if(zft_old_ftape) { |
| 663 | /* clear old ftape's eof marks */ |
| 664 | zft_clear_ftape_file_marks(); |
| 665 | zft_old_ftape = 0; /* no longer old ftape */ |
| 666 | } |
| 667 | } |
| 668 | if (zft_last_vtbl->open) { |
| 669 | zft_close_volume(pos); |
| 670 | zft_move_past_eof(pos); |
| 671 | count --; |
| 672 | } |
| 673 | /* now it's easy, just append eof-marks, that is empty |
| 674 | * volumes, to the end of the already recorded media. |
| 675 | */ |
| 676 | while (count > 0 && |
| 677 | pos->seg_pos <= ft_last_data_segment && |
| 678 | zft_eom_vtbl->count < ZFT_MAX_VOLUMES) { |
| 679 | TRACE(ft_t_noise, |
| 680 | "Writing zero sized file at segment %d", pos->seg_pos); |
| 681 | zft_new_vtbl_entry(); |
| 682 | zft_last_vtbl->start_seg = pos->seg_pos; |
| 683 | zft_last_vtbl->end_seg = pos->seg_pos; |
| 684 | zft_last_vtbl->size = 0; |
| 685 | zft_last_vtbl->blk_sz = zft_blk_sz; |
| 686 | zft_last_vtbl->zft_volume = 1; |
| 687 | zft_last_vtbl->use_compression = 0; |
| 688 | pos->tape_pos += zft_get_seg_sz(pos->seg_pos); |
| 689 | zft_eom_vtbl->start_seg = ++ pos->seg_pos; |
| 690 | count --; |
| 691 | } |
| 692 | if (count > 0) { |
| 693 | /* there are two possibilities: end of tape, or the |
| 694 | * maximum number of files is exhausted. |
| 695 | */ |
| 696 | zft_resid = count; |
| 697 | TRACE(ft_t_noise,"Number of marks NOT written: %d", zft_resid); |
| 698 | if (zft_eom_vtbl->count == ZFT_MAX_VOLUMES) { |
| 699 | TRACE_ABORT(-EINVAL, ft_t_warn, |
| 700 | "maximum allowed number of files " |
| 701 | "exhausted: %d", ZFT_MAX_VOLUMES); |
| 702 | } else { |
| 703 | TRACE_ABORT(-ENOSPC, |
| 704 | ft_t_noise, "reached end of tape"); |
| 705 | } |
| 706 | } |
| 707 | TRACE_EXIT 0; |
| 708 | } |
| 709 | |
| 710 | const zft_volinfo *zft_find_volume(unsigned int seg_pos) |
| 711 | { |
| 712 | TRACE_FUN(ft_t_flow); |
| 713 | |
| 714 | TRACE(ft_t_any, "called with seg_pos %d",seg_pos); |
| 715 | if (!zft_qic_mode) { |
| 716 | if (seg_pos > ft_last_data_segment) { |
| 717 | TRACE_EXIT &eot_vtbl; |
| 718 | } |
| 719 | tape_vtbl.blk_sz = zft_blk_sz; |
| 720 | TRACE_EXIT &tape_vtbl; |
| 721 | } |
| 722 | if (seg_pos < zft_first_vtbl->start_seg) { |
| 723 | TRACE_EXIT (cur_vtbl = zft_first_vtbl); |
| 724 | } |
| 725 | while (seg_pos > cur_vtbl->end_seg) { |
| 726 | cur_vtbl = list_entry(cur_vtbl->node.next, zft_volinfo, node); |
| 727 | TRACE(ft_t_noise, "%d - %d", cur_vtbl->start_seg, cur_vtbl->end_seg); |
| 728 | } |
| 729 | while (seg_pos < cur_vtbl->start_seg) { |
| 730 | cur_vtbl = list_entry(cur_vtbl->node.prev, zft_volinfo, node); |
| 731 | TRACE(ft_t_noise, "%d - %d", cur_vtbl->start_seg, cur_vtbl->end_seg); |
| 732 | } |
| 733 | if (seg_pos > cur_vtbl->end_seg || seg_pos < cur_vtbl->start_seg) { |
| 734 | TRACE(ft_t_bug, "This cannot happen"); |
| 735 | } |
| 736 | DUMP_VOLINFO(ft_t_noise, "", cur_vtbl); |
| 737 | TRACE_EXIT cur_vtbl; |
| 738 | } |
| 739 | |
| 740 | /* this function really assumes that we are just before eof |
| 741 | */ |
| 742 | void zft_move_past_eof(zft_position *pos) |
| 743 | { |
| 744 | TRACE_FUN(ft_t_flow); |
| 745 | |
| 746 | TRACE(ft_t_noise, "old seg. pos: %d", pos->seg_pos); |
| 747 | pos->tape_pos += zft_get_seg_sz(pos->seg_pos++) - pos->seg_byte_pos; |
| 748 | pos->seg_byte_pos = 0; |
| 749 | pos->volume_pos = 0; |
| 750 | if (zft_cmpr_ops) { |
| 751 | (*zft_cmpr_ops->reset)(); |
| 752 | } |
| 753 | zft_just_before_eof = 0; |
| 754 | zft_deblock_segment = -1; /* no need to cache it anymore */ |
| 755 | TRACE(ft_t_noise, "new seg. pos: %d", pos->seg_pos); |
| 756 | TRACE_EXIT; |
| 757 | } |