/* * Copyright (c) 1995-1997 Claus-Justus Heine This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. * * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-vtbl.c,v $ * $Revision: 1.7.6.1 $ * $Date: 1997/11/24 13:48:31 $ * * This file defines a volume table as defined in various QIC * standards. * * This is a minimal implementation, just allowing ordinary DOS * :( prgrams to identify the cartridge as used. */ #include #include #include #include #include "../zftape/zftape-init.h" #include "../zftape/zftape-eof.h" #include "../zftape/zftape-ctl.h" #include "../zftape/zftape-write.h" #include "../zftape/zftape-read.h" #include "../zftape/zftape-rw.h" #include "../zftape/zftape-vtbl.h" #define ZFT_CMAP_HACK /* leave this defined to hide the compression map */ /* * global variables */ int zft_qic_mode = 1; /* use the vtbl */ int zft_old_ftape; /* prevents old ftaped tapes to be overwritten */ int zft_volume_table_changed; /* for write_header_segments() */ /* * private variables (only exported for inline functions) */ LIST_HEAD(zft_vtbl); /* We could also allocate these dynamically when extracting the volume table * sizeof(zft_volinfo) is about 32 or something close to that */ static zft_volinfo tape_vtbl; static zft_volinfo eot_vtbl; static zft_volinfo *cur_vtbl; static inline void zft_new_vtbl_entry(void) { struct list_head *tmp = &zft_last_vtbl->node; zft_volinfo *new = zft_kmalloc(sizeof(zft_volinfo)); list_add(&new->node, tmp); new->count = zft_eom_vtbl->count ++; } void zft_free_vtbl(void) { for (;;) { struct list_head *tmp = zft_vtbl.prev; zft_volinfo *vtbl; if (tmp == &zft_vtbl) break; list_del(tmp); vtbl = list_entry(tmp, zft_volinfo, node); zft_kfree(vtbl, sizeof(zft_volinfo)); } INIT_LIST_HEAD(&zft_vtbl); cur_vtbl = NULL; } /* initialize vtbl, called by ftape_new_cartridge() */ void zft_init_vtbl(void) { zft_volinfo *new; zft_free_vtbl(); /* Create the two dummy vtbl entries */ new = zft_kmalloc(sizeof(zft_volinfo)); list_add(&new->node, &zft_vtbl); new = zft_kmalloc(sizeof(zft_volinfo)); list_add(&new->node, &zft_vtbl); zft_head_vtbl->end_seg = ft_first_data_segment; zft_head_vtbl->blk_sz = zft_blk_sz; zft_head_vtbl->count = -1; zft_eom_vtbl->start_seg = ft_first_data_segment + 1; zft_eom_vtbl->end_seg = ft_last_data_segment + 1; zft_eom_vtbl->blk_sz = zft_blk_sz; zft_eom_vtbl->count = 0; /* Reset the pointer for zft_find_volume() */ cur_vtbl = zft_eom_vtbl; /* initialize the dummy vtbl entries for zft_qic_mode == 0 */ eot_vtbl.start_seg = ft_last_data_segment + 1; eot_vtbl.end_seg = ft_last_data_segment + 1; eot_vtbl.blk_sz = zft_blk_sz; eot_vtbl.count = -1; tape_vtbl.start_seg = ft_first_data_segment; tape_vtbl.end_seg = ft_last_data_segment; tape_vtbl.blk_sz = zft_blk_sz; tape_vtbl.size = zft_capacity; tape_vtbl.count = 0; } /* check for a valid VTBL signature. */ static int vtbl_signature_valid(__u8 signature[4]) { const char *vtbl_ids[] = VTBL_IDS; /* valid signatures */ int j; for (j = 0; (j < NR_ITEMS(vtbl_ids)) && (memcmp(signature, vtbl_ids[j], 4) != 0); j++); return j < NR_ITEMS(vtbl_ids); } /* We used to store the block-size of the volume in the volume-label, * using the keyword "blocksize". The blocksize written to the * volume-label is in bytes. * * We use this now only for compatibility with old zftape version. We * store the blocksize directly as binary number in the vendor * extension part of the volume entry. */ static int check_volume_label(const char *label, int *blk_sz) { int valid_format; char *blocksize; TRACE_FUN(ft_t_flow); TRACE(ft_t_noise, "called with \"%s\" / \"%s\"", label, ZFT_VOL_NAME); if (strncmp(label, ZFT_VOL_NAME, strlen(ZFT_VOL_NAME)) != 0) { *blk_sz = 1; /* smallest block size that we allow */ valid_format = 0; } else { TRACE(ft_t_noise, "got old style zftape vtbl entry"); /* get the default blocksize */ /* use the kernel strstr() */ blocksize= strstr(label, " blocksize "); if (blocksize) { blocksize += strlen(" blocksize "); for(*blk_sz= 0; *blocksize >= '0' && *blocksize <= '9'; blocksize++) { *blk_sz *= 10; *blk_sz += *blocksize - '0'; } if (*blk_sz > ZFT_MAX_BLK_SZ) { *blk_sz= 1; valid_format= 0; } else { valid_format = 1; } } else { *blk_sz= 1; valid_format= 0; } } TRACE_EXIT valid_format; } /* check for a zftape volume */ static int check_volume(__u8 *entry, zft_volinfo *volume) { TRACE_FUN(ft_t_flow); if(strncmp(&entry[VTBL_EXT+EXT_ZFTAPE_SIG], ZFTAPE_SIG, strlen(ZFTAPE_SIG)) == 0) { TRACE(ft_t_noise, "got new style zftape vtbl entry"); volume->blk_sz = GET2(entry, VTBL_EXT+EXT_ZFTAPE_BLKSZ); volume->qic113 = entry[VTBL_EXT+EXT_ZFTAPE_QIC113]; TRACE_EXIT 1; } else { TRACE_EXIT check_volume_label(&entry[VTBL_DESC], &volume->blk_sz); } } /* create zftape specific vtbl entry, the volume bounds are inserted * in the calling function, zft_create_volume_headers() */ static void create_zft_volume(__u8 *entry, zft_volinfo *vtbl) { TRACE_FUN(ft_t_flow); memset(entry, 0, VTBL_SIZE); memcpy(&entry[VTBL_SIG], VTBL_ID, 4); sprintf(&entry[VTBL_DESC], ZFT_VOL_NAME" %03d", vtbl->count); entry[VTBL_FLAGS] = (VTBL_FL_NOT_VERIFIED | VTBL_FL_SEG_SPANNING); entry[VTBL_M_NO] = 1; /* multi_cartridge_count */ strcpy(&entry[VTBL_EXT+EXT_ZFTAPE_SIG], ZFTAPE_SIG); PUT2(entry, VTBL_EXT+EXT_ZFTAPE_BLKSZ, vtbl->blk_sz); if (zft_qic113) { PUT8(entry, VTBL_DATA_SIZE, vtbl->size); entry[VTBL_CMPR] = VTBL_CMPR_UNREG; if (vtbl->use_compression) { /* use compression: */ entry[VTBL_CMPR] |= VTBL_CMPR_USED; } entry[VTBL_EXT+EXT_ZFTAPE_QIC113] = 1; } else { PUT4(entry, VTBL_DATA_SIZE, vtbl->size); entry[VTBL_K_CMPR] = VTBL_CMPR_UNREG; if (vtbl->use_compression) { /* use compression: */ entry[VTBL_K_CMPR] |= VTBL_CMPR_USED; } } if (ft_format_code == fmt_big) { /* SCSI like vtbl, store the number of used * segments as 4 byte value */ PUT4(entry, VTBL_SCSI_SEGS, vtbl->end_seg-vtbl->start_seg + 1); } else { /* normal, QIC-80MC like vtbl */ PUT2(entry, VTBL_START, vtbl->start_seg); PUT2(entry, VTBL_END, vtbl->end_seg); } TRACE_EXIT; } /* this one creates the volume headers for each volume. It is assumed * that buffer already contains the old volume-table, so that vtbl * entries without the zft_volume flag set can savely be ignored. */ static void zft_create_volume_headers(__u8 *buffer) { __u8 *entry; struct list_head *tmp; zft_volinfo *vtbl; TRACE_FUN(ft_t_flow); #ifdef ZFT_CMAP_HACK if((strncmp(&buffer[VTBL_EXT+EXT_ZFTAPE_SIG], ZFTAPE_SIG, strlen(ZFTAPE_SIG)) == 0) && buffer[VTBL_EXT+EXT_ZFTAPE_CMAP] != 0) { TRACE(ft_t_noise, "deleting cmap volume"); memmove(buffer, buffer + VTBL_SIZE, FT_SEGMENT_SIZE - VTBL_SIZE); } #endif entry = buffer; for (tmp = zft_head_vtbl->node.next; tmp != &zft_eom_vtbl->node; tmp = tmp->next) { vtbl = list_entry(tmp, zft_volinfo, node); /* we now fill in the values only for newly created volumes. */ if (vtbl->new_volume) { create_zft_volume(entry, vtbl); vtbl->new_volume = 0; /* clear the flag */ } DUMP_VOLINFO(ft_t_noise, &entry[VTBL_DESC], vtbl); entry += VTBL_SIZE; } memset(entry, 0, FT_SEGMENT_SIZE - zft_eom_vtbl->count * VTBL_SIZE); TRACE_EXIT; } /* write volume table to tape. Calls zft_create_volume_headers() */ int zft_update_volume_table(unsigned int segment) { int result = 0; __u8 *verify_buf = NULL; TRACE_FUN(ft_t_flow); TRACE_CATCH(result = ftape_read_segment(ft_first_data_segment, zft_deblock_buf, FT_RD_SINGLE),); zft_create_volume_headers(zft_deblock_buf); TRACE(ft_t_noise, "writing volume table segment %d", segment); if (zft_vmalloc_once(&verify_buf, FT_SEGMENT_SIZE) == 0) { TRACE_CATCH(zft_verify_write_segments(segment, zft_deblock_buf, result, verify_buf), zft_vfree(&verify_buf, FT_SEGMENT_SIZE)); zft_vfree(&verify_buf, FT_SEGMENT_SIZE); } else { TRACE_CATCH(ftape_write_segment(segment, zft_deblock_buf, FT_WR_SINGLE),); } TRACE_EXIT 0; } /* non zftape volumes are handled in raw mode. Thus we need to * calculate the raw amount of data contained in those segments. */ static void extract_alien_volume(__u8 *entry, zft_volinfo *vtbl) { TRACE_FUN(ft_t_flow); vtbl->size = (zft_calc_tape_pos(zft_last_vtbl->end_seg+1) - zft_calc_tape_pos(zft_last_vtbl->start_seg)); vtbl->use_compression = 0; vtbl->qic113 = zft_qic113; if (vtbl->qic113) { TRACE(ft_t_noise, "Fake alien volume's size from " LL_X " to " LL_X, LL(GET8(entry, VTBL_DATA_SIZE)), LL(vtbl->size)); } else { TRACE(ft_t_noise, "Fake alien volume's size from %d to " LL_X, (int)GET4(entry, VTBL_DATA_SIZE), LL(vtbl->size)); } TRACE_EXIT; } /* extract an zftape specific volume */ static void extract_zft_volume(__u8 *entry, zft_volinfo *vtbl) { TRACE_FUN(ft_t_flow); if (vtbl->qic113) { vtbl->size = GET8(entry, VTBL_DATA_SIZE); vtbl->use_compression = (entry[VTBL_CMPR] & VTBL_CMPR_USED) != 0; } else { vtbl->size = GET4(entry, VTBL_DATA_SIZE); if (entry[VTBL_K_CMPR] & VTBL_CMPR_UNREG) { vtbl->use_compression = (entry[VTBL_K_CMPR] & VTBL_CMPR_USED) != 0; } else if (entry[VTBL_CMPR] & VTBL_CMPR_UNREG) { vtbl->use_compression = (entry[VTBL_CMPR] & VTBL_CMPR_USED) != 0; } else { TRACE(ft_t_warn, "Geeh! There is something wrong:\n" KERN_INFO "QIC compression (Rev = K): %x\n" KERN_INFO "QIC compression (Rev > K): %x", entry[VTBL_K_CMPR], entry[VTBL_CMPR]); } } TRACE_EXIT; } /* extract the volume table from buffer. "buffer" must already contain * the vtbl-segment */ int zft_extract_volume_headers(__u8 *buffer) { __u8 *entry; TRACE_FUN(ft_t_flow); zft_init_vtbl(); entry = buffer; #ifdef ZFT_CMAP_HACK if ((strncmp(&entry[VTBL_EXT+EXT_ZFTAPE_SIG], ZFTAPE_SIG, strlen(ZFTAPE_SIG)) == 0) && entry[VTBL_EXT+EXT_ZFTAPE_CMAP] != 0) { TRACE(ft_t_noise, "ignoring cmap volume"); entry += VTBL_SIZE; } #endif /* the end of the vtbl is indicated by an invalid signature */ while (vtbl_signature_valid(&entry[VTBL_SIG]) && (entry - buffer) < FT_SEGMENT_SIZE) { zft_new_vtbl_entry(); if (ft_format_code == fmt_big) { /* SCSI like vtbl, stores only the number of * segments used */ unsigned int num_segments= GET4(entry, VTBL_SCSI_SEGS); zft_last_vtbl->start_seg = zft_eom_vtbl->start_seg; zft_last_vtbl->end_seg = zft_last_vtbl->start_seg + num_segments - 1; } else { /* `normal', QIC-80 like vtbl */ zft_last_vtbl->start_seg = GET2(entry, VTBL_START); zft_last_vtbl->end_seg = GET2(entry, VTBL_END); } zft_eom_vtbl->start_seg = zft_last_vtbl->end_seg + 1; /* check if we created this volume and get the * blk_sz */ zft_last_vtbl->zft_volume = check_volume(entry, zft_last_vtbl); if (zft_last_vtbl->zft_volume == 0) { extract_alien_volume(entry, zft_last_vtbl); } else { extract_zft_volume(entry, zft_last_vtbl); } DUMP_VOLINFO(ft_t_noise, &entry[VTBL_DESC], zft_last_vtbl); entry +=VTBL_SIZE; } #if 0 /* * undefine to test end of tape handling */ zft_new_vtbl_entry(); zft_last_vtbl->start_seg = zft_eom_vtbl->start_seg; zft_last_vtbl->end_seg = ft_last_data_segment - 10; zft_last_vtbl->blk_sz = zft_blk_sz; zft_last_vtbl->zft_volume = 1; zft_last_vtbl->qic113 = zft_qic113; zft_last_vtbl->size = (zft_calc_tape_pos(zft_last_vtbl->end_seg+1) - zft_calc_tape_pos(zft_last_vtbl->start_seg)); #endif TRACE_EXIT 0; } /* this functions translates the failed_sector_log, misused as * EOF-marker list, into a virtual volume table. The table mustn't be * written to tape, because this would occupy the first data segment, * which should be the volume table, but is actually the first segment * that is filled with data (when using standard ftape). We assume, * that we get a non-empty failed_sector_log. */ int zft_fake_volume_headers (eof_mark_union *eof_map, int num_failed_sectors) { unsigned int segment, sector; int have_eom = 0; int vol_no; TRACE_FUN(ft_t_flow); if ((num_failed_sectors >= 2) && (GET2(&eof_map[num_failed_sectors - 1].mark.segment, 0) == GET2(&eof_map[num_failed_sectors - 2].mark.segment, 0) + 1) && (GET2(&eof_map[num_failed_sectors - 1].mark.date, 0) == 1)) { /* this should be eom. We keep the remainder of the * tape as another volume. */ have_eom = 1; } zft_init_vtbl(); zft_eom_vtbl->start_seg = ft_first_data_segment; for(vol_no = 0; vol_no < num_failed_sectors - have_eom; vol_no ++) { zft_new_vtbl_entry(); segment = GET2(&eof_map[vol_no].mark.segment, 0); sector = GET2(&eof_map[vol_no].mark.date, 0); zft_last_vtbl->start_seg = zft_eom_vtbl->start_seg; zft_last_vtbl->end_seg = segment; zft_eom_vtbl->start_seg = segment + 1; zft_last_vtbl->blk_sz = 1; zft_last_vtbl->size = (zft_calc_tape_pos(zft_last_vtbl->end_seg) - zft_calc_tape_pos(zft_last_vtbl->start_seg) + (sector-1) * FT_SECTOR_SIZE); TRACE(ft_t_noise, "failed sector log: segment: %d, sector: %d", segment, sector); DUMP_VOLINFO(ft_t_noise, "Faked volume", zft_last_vtbl); } if (!have_eom) { zft_new_vtbl_entry(); zft_last_vtbl->start_seg = zft_eom_vtbl->start_seg; zft_last_vtbl->end_seg = ft_last_data_segment; zft_eom_vtbl->start_seg = ft_last_data_segment + 1; zft_last_vtbl->size = zft_capacity; zft_last_vtbl->size -= zft_calc_tape_pos(zft_last_vtbl->start_seg); zft_last_vtbl->blk_sz = 1; DUMP_VOLINFO(ft_t_noise, "Faked volume",zft_last_vtbl); } TRACE_EXIT 0; } /* update the internal volume table * * if before start of last volume: erase all following volumes if * inside a volume: set end of volume to infinity * * this function is intended to be called every time _ftape_write() is * called * * return: 0 if no new volume was created, 1 if a new volume was * created * * NOTE: we don't need to check for zft_mode as ftape_write() does * that already. This function gets never called without accessing * zftape via the *qft* devices */ int zft_open_volume(zft_position *pos, int blk_sz, int use_compression) { TRACE_FUN(ft_t_flow); if (!zft_qic_mode) { TRACE_EXIT 0; } if (zft_tape_at_lbot(pos)) { zft_init_vtbl(); if(zft_old_ftape) { /* clear old ftape's eof marks */ zft_clear_ftape_file_marks(); zft_old_ftape = 0; /* no longer old ftape */ } zft_reset_position(pos); } if (pos->seg_pos != zft_last_vtbl->end_seg + 1) { TRACE_ABORT(-EIO, ft_t_bug, "BUG: seg_pos: %d, zft_last_vtbl->end_seg: %d", pos->seg_pos, zft_last_vtbl->end_seg); } TRACE(ft_t_noise, "create new volume"); if (zft_eom_vtbl->count >= ZFT_MAX_VOLUMES) { TRACE_ABORT(-ENOSPC, ft_t_err, "Error: maxmimal number of volumes exhausted " "(maxmimum is %d)", ZFT_MAX_VOLUMES); } zft_new_vtbl_entry(); pos->volume_pos = pos->seg_byte_pos = 0; zft_last_vtbl->start_seg = pos->seg_pos; zft_last_vtbl->end_seg = ft_last_data_segment; /* infinity */ zft_last_vtbl->blk_sz = blk_sz; zft_last_vtbl->size = zft_capacity; zft_last_vtbl->zft_volume = 1; zft_last_vtbl->use_compression = use_compression; zft_last_vtbl->qic113 = zft_qic113; zft_last_vtbl->new_volume = 1; zft_last_vtbl->open = 1; zft_volume_table_changed = 1; zft_eom_vtbl->start_seg = ft_last_data_segment + 1; TRACE_EXIT 0; } /* perform mtfsf, mtbsf, not allowed without zft_qic_mode */ int zft_skip_volumes(int count, zft_position *pos) { const zft_volinfo *vtbl; TRACE_FUN(ft_t_flow); TRACE(ft_t_noise, "count: %d", count); vtbl= zft_find_volume(pos->seg_pos); while (count > 0 && vtbl != zft_eom_vtbl) { vtbl = list_entry(vtbl->node.next, zft_volinfo, node); count --; } while (count < 0 && vtbl != zft_first_vtbl) { vtbl = list_entry(vtbl->node.prev, zft_volinfo, node); count ++; } pos->seg_pos = vtbl->start_seg; pos->seg_byte_pos = 0; pos->volume_pos = 0; pos->tape_pos = zft_calc_tape_pos(pos->seg_pos); zft_just_before_eof = vtbl->size == 0; if (zft_cmpr_ops) { (*zft_cmpr_ops->reset)(); } zft_deblock_segment = -1; /* no need to keep cache */ TRACE(ft_t_noise, "repositioning to:\n" KERN_INFO "zft_seg_pos : %d\n" KERN_INFO "zft_seg_byte_pos : %d\n" KERN_INFO "zft_tape_pos : " LL_X "\n" KERN_INFO "zft_volume_pos : " LL_X "\n" KERN_INFO "file number : %d", pos->seg_pos, pos->seg_byte_pos, LL(pos->tape_pos), LL(pos->volume_pos), vtbl->count); zft_resid = count < 0 ? -count : count; TRACE_EXIT zft_resid ? -EINVAL : 0; } /* the following simply returns the raw data position of the EOM * marker, MTIOCSIZE ioctl */ __s64 zft_get_eom_pos(void) { if (zft_qic_mode) { return zft_calc_tape_pos(zft_eom_vtbl->start_seg); } else { /* there is only one volume in raw mode */ return zft_capacity; } } /* skip to eom, used for MTEOM */ void zft_skip_to_eom(zft_position *pos) { TRACE_FUN(ft_t_flow); pos->seg_pos = zft_eom_vtbl->start_seg; pos->seg_byte_pos = pos->volume_pos = zft_just_before_eof = 0; pos->tape_pos = zft_calc_tape_pos(pos->seg_pos); TRACE(ft_t_noise, "ftape positioned to segment %d, data pos " LL_X, pos->seg_pos, LL(pos->tape_pos)); TRACE_EXIT; } /* write an EOF-marker by setting zft_last_vtbl->end_seg to seg_pos. * NOTE: this function assumes that zft_last_vtbl points to a valid * vtbl entry * * NOTE: this routine always positions before the EOF marker */ int zft_close_volume(zft_position *pos) { TRACE_FUN(ft_t_any); if (zft_vtbl_empty || !zft_last_vtbl->open) { /* should not happen */ TRACE(ft_t_noise, "There are no volumes to finish"); TRACE_EXIT -EIO; } if (pos->seg_byte_pos == 0 && pos->seg_pos != zft_last_vtbl->start_seg) { pos->seg_pos --; pos->seg_byte_pos = zft_get_seg_sz(pos->seg_pos); } zft_last_vtbl->end_seg = pos->seg_pos; zft_last_vtbl->size = pos->volume_pos; zft_volume_table_changed = 1; zft_just_before_eof = 1; zft_eom_vtbl->start_seg = zft_last_vtbl->end_seg + 1; zft_last_vtbl->open = 0; /* closed */ TRACE_EXIT 0; } /* write count file-marks at current position. * * The tape is positioned after the eof-marker, that is at byte 0 of * the segment following the eof-marker * * this function is only allowed in zft_qic_mode * * Only allowed when tape is at BOT or EOD. */ int zft_weof(unsigned int count, zft_position *pos) { TRACE_FUN(ft_t_flow); if (!count) { /* write zero EOF marks should be a real no-op */ TRACE_EXIT 0; } zft_volume_table_changed = 1; if (zft_tape_at_lbot(pos)) { zft_init_vtbl(); if(zft_old_ftape) { /* clear old ftape's eof marks */ zft_clear_ftape_file_marks(); zft_old_ftape = 0; /* no longer old ftape */ } } if (zft_last_vtbl->open) { zft_close_volume(pos); zft_move_past_eof(pos); count --; } /* now it's easy, just append eof-marks, that is empty * volumes, to the end of the already recorded media. */ while (count > 0 && pos->seg_pos <= ft_last_data_segment && zft_eom_vtbl->count < ZFT_MAX_VOLUMES) { TRACE(ft_t_noise, "Writing zero sized file at segment %d", pos->seg_pos); zft_new_vtbl_entry(); zft_last_vtbl->start_seg = pos->seg_pos; zft_last_vtbl->end_seg = pos->seg_pos; zft_last_vtbl->size = 0; zft_last_vtbl->blk_sz = zft_blk_sz; zft_last_vtbl->zft_volume = 1; zft_last_vtbl->use_compression = 0; pos->tape_pos += zft_get_seg_sz(pos->seg_pos); zft_eom_vtbl->start_seg = ++ pos->seg_pos; count --; } if (count > 0) { /* there are two possibilities: end of tape, or the * maximum number of files is exhausted. */ zft_resid = count; TRACE(ft_t_noise,"Number of marks NOT written: %d", zft_resid); if (zft_eom_vtbl->count == ZFT_MAX_VOLUMES) { TRACE_ABORT(-EINVAL, ft_t_warn, "maximum allowed number of files " "exhausted: %d", ZFT_MAX_VOLUMES); } else { TRACE_ABORT(-ENOSPC, ft_t_noise, "reached end of tape"); } } TRACE_EXIT 0; } const zft_volinfo *zft_find_volume(unsigned int seg_pos) { TRACE_FUN(ft_t_flow); TRACE(ft_t_any, "called with seg_pos %d",seg_pos); if (!zft_qic_mode) { if (seg_pos > ft_last_data_segment) { TRACE_EXIT &eot_vtbl; } tape_vtbl.blk_sz = zft_blk_sz; TRACE_EXIT &tape_vtbl; } if (seg_pos < zft_first_vtbl->start_seg) { TRACE_EXIT (cur_vtbl = zft_first_vtbl); } while (seg_pos > cur_vtbl->end_seg) { cur_vtbl = list_entry(cur_vtbl->node.next, zft_volinfo, node); TRACE(ft_t_noise, "%d - %d", cur_vtbl->start_seg, cur_vtbl->end_seg); } while (seg_pos < cur_vtbl->start_seg) { cur_vtbl = list_entry(cur_vtbl->node.prev, zft_volinfo, node); TRACE(ft_t_noise, "%d - %d", cur_vtbl->start_seg, cur_vtbl->end_seg); } if (seg_pos > cur_vtbl->end_seg || seg_pos < cur_vtbl->start_seg) { TRACE(ft_t_bug, "This cannot happen"); } DUMP_VOLINFO(ft_t_noise, "", cur_vtbl); TRACE_EXIT cur_vtbl; } /* this function really assumes that we are just before eof */ void zft_move_past_eof(zft_position *pos) { TRACE_FUN(ft_t_flow); TRACE(ft_t_noise, "old seg. pos: %d", pos->seg_pos); pos->tape_pos += zft_get_seg_sz(pos->seg_pos++) - pos->seg_byte_pos; pos->seg_byte_pos = 0; pos->volume_pos = 0; if (zft_cmpr_ops) { (*zft_cmpr_ops->reset)(); } zft_just_before_eof = 0; zft_deblock_segment = -1; /* no need to cache it anymore */ TRACE(ft_t_noise, "new seg. pos: %d", pos->seg_pos); TRACE_EXIT; }