diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/char/ftape/zftape |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/char/ftape/zftape')
-rw-r--r-- | drivers/char/ftape/zftape/Makefile | 36 | ||||
-rw-r--r-- | drivers/char/ftape/zftape/zftape-buffers.c | 149 | ||||
-rw-r--r-- | drivers/char/ftape/zftape/zftape-buffers.h | 55 | ||||
-rw-r--r-- | drivers/char/ftape/zftape/zftape-ctl.c | 1418 | ||||
-rw-r--r-- | drivers/char/ftape/zftape/zftape-ctl.h | 59 | ||||
-rw-r--r-- | drivers/char/ftape/zftape/zftape-eof.c | 199 | ||||
-rw-r--r-- | drivers/char/ftape/zftape/zftape-eof.h | 52 | ||||
-rw-r--r-- | drivers/char/ftape/zftape/zftape-init.c | 403 | ||||
-rw-r--r-- | drivers/char/ftape/zftape/zftape-init.h | 77 | ||||
-rw-r--r-- | drivers/char/ftape/zftape/zftape-read.c | 377 | ||||
-rw-r--r-- | drivers/char/ftape/zftape/zftape-read.h | 53 | ||||
-rw-r--r-- | drivers/char/ftape/zftape/zftape-rw.c | 376 | ||||
-rw-r--r-- | drivers/char/ftape/zftape/zftape-rw.h | 102 | ||||
-rw-r--r-- | drivers/char/ftape/zftape/zftape-vtbl.c | 757 | ||||
-rw-r--r-- | drivers/char/ftape/zftape/zftape-vtbl.h | 227 | ||||
-rw-r--r-- | drivers/char/ftape/zftape/zftape-write.c | 483 | ||||
-rw-r--r-- | drivers/char/ftape/zftape/zftape-write.h | 38 | ||||
-rw-r--r-- | drivers/char/ftape/zftape/zftape_syms.c | 43 |
18 files changed, 4904 insertions, 0 deletions
diff --git a/drivers/char/ftape/zftape/Makefile b/drivers/char/ftape/zftape/Makefile new file mode 100644 index 00000000000..6d91c1f77c0 --- /dev/null +++ b/drivers/char/ftape/zftape/Makefile @@ -0,0 +1,36 @@ +# +# Copyright (C) 1996, 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/Makefile,v $ +# $Revision: 1.4 $ +# $Date: 1997/10/05 19:18:58 $ +# +# Makefile for the QIC-40/80/3010/3020 zftape interface VFS to +# ftape +# + + +# ZFT_OBSOLETE - enable the MTIOC_ZFTAPE_GETBLKSZ ioctl. You should +# leave this enabled for compatibility with taper. + +obj-$(CONFIG_ZFTAPE) += zftape.o + +zftape-objs := zftape-rw.o zftape-ctl.o zftape-read.o \ + zftape-write.o zftape-vtbl.o zftape-eof.o \ + zftape-init.o zftape-buffers.o zftape_syms.o + +EXTRA_CFLAGS := -DZFT_OBSOLETE diff --git a/drivers/char/ftape/zftape/zftape-buffers.c b/drivers/char/ftape/zftape/zftape-buffers.c new file mode 100644 index 00000000000..da06f138334 --- /dev/null +++ b/drivers/char/ftape/zftape/zftape-buffers.c @@ -0,0 +1,149 @@ +/* + * 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-buffers.c,v $ + * $Revision: 1.2 $ + * $Date: 1997/10/05 19:18:59 $ + * + * This file contains the dynamic buffer allocation routines + * of zftape + */ + +#include <linux/errno.h> +#include <linux/mm.h> +#include <linux/slab.h> +#include <linux/delay.h> + +#include <linux/zftape.h> + +#include <linux/vmalloc.h> + +#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" + +/* global variables + */ + +/* local varibales + */ +static unsigned int used_memory; +static unsigned int peak_memory; + +void zft_memory_stats(void) +{ + TRACE_FUN(ft_t_flow); + + TRACE(ft_t_noise, "Memory usage (vmalloc allocations):\n" + KERN_INFO "total allocated: %d\n" + KERN_INFO "peak allocation: %d", + used_memory, peak_memory); + peak_memory = used_memory; + TRACE_EXIT; +} + +int zft_vcalloc_once(void *new, size_t size) +{ + TRACE_FUN(ft_t_flow); + if (zft_vmalloc_once(new, size) < 0) { + TRACE_EXIT -ENOMEM; + } + memset(*(void **)new, '\0', size); + TRACE_EXIT 0; +} +int zft_vmalloc_once(void *new, size_t size) +{ + TRACE_FUN(ft_t_flow); + + if (*(void **)new != NULL || size == 0) { + TRACE_EXIT 0; + } + if ((*(void **)new = vmalloc(size)) == NULL) { + TRACE_EXIT -ENOMEM; + } + used_memory += size; + if (peak_memory < used_memory) { + peak_memory = used_memory; + } + TRACE_ABORT(0, ft_t_noise, + "allocated buffer @ %p, %d bytes", *(void **)new, size); +} +int zft_vmalloc_always(void *new, size_t size) +{ + TRACE_FUN(ft_t_flow); + + zft_vfree(new, size); + TRACE_EXIT zft_vmalloc_once(new, size); +} +void zft_vfree(void *old, size_t size) +{ + TRACE_FUN(ft_t_flow); + + if (*(void **)old) { + vfree(*(void **)old); + used_memory -= size; + TRACE(ft_t_noise, "released buffer @ %p, %d bytes", + *(void **)old, size); + *(void **)old = NULL; + } + TRACE_EXIT; +} + +void *zft_kmalloc(size_t size) +{ + void *new; + + while ((new = kmalloc(size, GFP_KERNEL)) == NULL) { + msleep_interruptible(100); + } + memset(new, 0, size); + used_memory += size; + if (peak_memory < used_memory) { + peak_memory = used_memory; + } + return new; +} + +void zft_kfree(void *old, size_t size) +{ + kfree(old); + used_memory -= size; +} + +/* there are some more buffers that are allocated on demand. + * cleanup_module() calles this function to be sure to have released + * them + */ +void zft_uninit_mem(void) +{ + TRACE_FUN(ft_t_flow); + + zft_vfree(&zft_hseg_buf, FT_SEGMENT_SIZE); + zft_vfree(&zft_deblock_buf, FT_SEGMENT_SIZE); zft_deblock_segment = -1; + zft_free_vtbl(); + if (zft_cmpr_lock(0 /* don't load */) == 0) { + (*zft_cmpr_ops->cleanup)(); + (*zft_cmpr_ops->reset)(); /* unlock it again */ + } + zft_memory_stats(); + TRACE_EXIT; +} diff --git a/drivers/char/ftape/zftape/zftape-buffers.h b/drivers/char/ftape/zftape/zftape-buffers.h new file mode 100644 index 00000000000..798e3128c68 --- /dev/null +++ b/drivers/char/ftape/zftape/zftape-buffers.h @@ -0,0 +1,55 @@ +#ifndef _FTAPE_DYNMEM_H +#define _FTAPE_DYNMEM_H + +/* + * 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-buffers.h,v $ + * $Revision: 1.2 $ + * $Date: 1997/10/05 19:18:59 $ + * + * memory allocation routines. + * + */ + +/* we do not allocate all of the really large buffer memory before + * someone tries to open the drive. ftape_open() may fail with + * -ENOMEM, but that's better having 200k of vmalloced memory which + * cannot be swapped out. + */ + +extern void zft_memory_stats(void); +extern int zft_vmalloc_once(void *new, size_t size); +extern int zft_vcalloc_once(void *new, size_t size); +extern int zft_vmalloc_always(void *new, size_t size); +extern void zft_vfree(void *old, size_t size); +extern void *zft_kmalloc(size_t size); +extern void zft_kfree(void *old, size_t size); + +/* called by cleanup_module() + */ +extern void zft_uninit_mem(void); + +#endif + + + + + + + diff --git a/drivers/char/ftape/zftape/zftape-ctl.c b/drivers/char/ftape/zftape/zftape-ctl.c new file mode 100644 index 00000000000..6c7874e5c19 --- /dev/null +++ b/drivers/char/ftape/zftape/zftape-ctl.c @@ -0,0 +1,1418 @@ +/* + * Copyright (C) 1996, 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-ctl.c,v $ + * $Revision: 1.2.6.2 $ + * $Date: 1997/11/14 18:07:33 $ + * + * This file contains the non-read/write zftape functions + * for the QIC-40/80/3010/3020 floppy-tape driver for Linux. + */ + +#include <linux/config.h> +#include <linux/errno.h> +#include <linux/mm.h> +#include <linux/module.h> +#include <linux/fcntl.h> + +#include <linux/zftape.h> + +#include <asm/uaccess.h> + +#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" + +/* Global vars. + */ +int zft_write_protected; /* this is when cartridge rdonly or O_RDONLY */ +int zft_header_read; +int zft_offline; +unsigned int zft_unit; +int zft_resid; +int zft_mt_compression; + +/* Local vars. + */ +static int going_offline; + +typedef int (mt_fun)(int *argptr); +typedef int (*mt_funp)(int *argptr); +typedef struct +{ + mt_funp function; + unsigned offline : 1; /* op permitted if offline or no_tape */ + unsigned write_protected : 1; /* op permitted if write-protected */ + unsigned not_formatted : 1; /* op permitted if tape not formatted */ + unsigned raw_mode : 1; /* op permitted if zft_mode == 0 */ + unsigned need_idle_state : 1; /* need to call def_idle_state */ + char *name; +} fun_entry; + +static mt_fun mt_dummy, mt_reset, mt_fsr, mt_bsr, mt_rew, mt_offl, mt_nop, + mt_weof, mt_erase, mt_ras2, mt_setblk, mt_setdensity, + mt_seek, mt_tell, mt_reten, mt_eom, mt_fsf, mt_bsf, + mt_fsfm, mt_bsfm, mt_setdrvbuffer, mt_compression; + +static fun_entry mt_funs[]= +{ + {mt_reset , 1, 1, 1, 1, 0, "MT_RESET" }, /* 0 */ + {mt_fsf , 0, 1, 0, 0, 1, "MT_FSF" }, + {mt_bsf , 0, 1, 0, 0, 1, "MT_BSF" }, + {mt_fsr , 0, 1, 0, 1, 1, "MT_FSR" }, + {mt_bsr , 0, 1, 0, 1, 1, "MT_BSR" }, + {mt_weof , 0, 0, 0, 0, 0, "MT_WEOF" }, /* 5 */ + {mt_rew , 0, 1, 1, 1, 0, "MT_REW" }, + {mt_offl , 0, 1, 1, 1, 0, "MT_OFFL" }, + {mt_nop , 1, 1, 1, 1, 0, "MT_NOP" }, + {mt_reten , 0, 1, 1, 1, 0, "MT_RETEN" }, + {mt_bsfm , 0, 1, 0, 0, 1, "MT_BSFM" }, /* 10 */ + {mt_fsfm , 0, 1, 0, 0, 1, "MT_FSFM" }, + {mt_eom , 0, 1, 0, 0, 1, "MT_EOM" }, + {mt_erase , 0, 0, 0, 1, 0, "MT_ERASE" }, + {mt_dummy , 1, 1, 1, 1, 0, "MT_RAS1" }, + {mt_ras2 , 0, 0, 0, 1, 0, "MT_RAS2" }, + {mt_dummy , 1, 1, 1, 1, 0, "MT_RAS3" }, + {mt_dummy , 1, 1, 1, 1, 0, "UNKNOWN" }, + {mt_dummy , 1, 1, 1, 1, 0, "UNKNOWN" }, + {mt_dummy , 1, 1, 1, 1, 0, "UNKNOWN" }, + {mt_setblk , 1, 1, 1, 1, 1, "MT_SETBLK"}, /* 20 */ + {mt_setdensity , 1, 1, 1, 1, 0, "MT_SETDENSITY"}, + {mt_seek , 0, 1, 0, 1, 1, "MT_SEEK" }, + {mt_dummy , 0, 1, 0, 1, 1, "MT_TELL" }, /* wr-only ?! */ + {mt_setdrvbuffer, 1, 1, 1, 1, 0, "MT_SETDRVBUFFER" }, + {mt_dummy , 1, 1, 1, 1, 0, "MT_FSS" }, /* 25 */ + {mt_dummy , 1, 1, 1, 1, 0, "MT_BSS" }, + {mt_dummy , 1, 1, 1, 1, 0, "MT_WSM" }, + {mt_dummy , 1, 1, 1, 1, 0, "MT_LOCK" }, + {mt_dummy , 1, 1, 1, 1, 0, "MT_UNLOCK"}, + {mt_dummy , 1, 1, 1, 1, 0, "MT_LOAD" }, /* 30 */ + {mt_dummy , 1, 1, 1, 1, 0, "MT_UNLOAD"}, + {mt_compression , 1, 1, 1, 0, 1, "MT_COMPRESSION"}, + {mt_dummy , 1, 1, 1, 1, 0, "MT_SETPART"}, + {mt_dummy , 1, 1, 1, 1, 0, "MT_MKPART"} +}; + +#define NR_MT_CMDS NR_ITEMS(mt_funs) + +void zft_reset_position(zft_position *pos) +{ + TRACE_FUN(ft_t_flow); + + pos->seg_byte_pos = + pos->volume_pos = 0; + if (zft_header_read) { + /* need to keep track of the volume table and + * compression map. We therefor simply + * position at the beginning of the first + * volume. This covers old ftape archives as + * well has various flavours of the + * compression map segments. The worst case is + * that the compression map shows up as a + * additional volume in front of all others. + */ + pos->seg_pos = zft_find_volume(0)->start_seg; + pos->tape_pos = zft_calc_tape_pos(pos->seg_pos); + } else { + pos->tape_pos = 0; + pos->seg_pos = -1; + } + zft_just_before_eof = 0; + zft_deblock_segment = -1; + zft_io_state = zft_idle; + zft_zap_read_buffers(); + zft_prevent_flush(); + /* unlock the compresison module if it is loaded. + * The zero arg means not to try to load the module. + */ + if (zft_cmpr_lock(0) == 0) { + (*zft_cmpr_ops->reset)(); /* unlock */ + } + TRACE_EXIT; +} + +static void zft_init_driver(void) +{ + TRACE_FUN(ft_t_flow); + + zft_resid = + zft_header_read = + zft_old_ftape = + zft_offline = + zft_write_protected = + going_offline = + zft_mt_compression = + zft_header_changed = + zft_volume_table_changed = + zft_written_segments = 0; + zft_blk_sz = CONFIG_ZFT_DFLT_BLK_SZ; + zft_reset_position(&zft_pos); /* does most of the stuff */ + ftape_zap_read_buffers(); + ftape_set_state(idle); + TRACE_EXIT; +} + +int zft_def_idle_state(void) +{ + int result = 0; + TRACE_FUN(ft_t_flow); + + if (!zft_header_read) { + result = zft_read_header_segments(); + } else if ((result = zft_flush_buffers()) >= 0 && zft_qic_mode) { + /* don't move past eof + */ + (void)zft_close_volume(&zft_pos); + } + if (ftape_abort_operation() < 0) { + TRACE(ft_t_warn, "ftape_abort_operation() failed"); + result = -EIO; + } + /* clear remaining read buffers */ + zft_zap_read_buffers(); + zft_io_state = zft_idle; + TRACE_EXIT result; +} + +/***************************************************************************** + * * + * functions for the MTIOCTOP commands * + * * + *****************************************************************************/ + +static int mt_dummy(int *dummy) +{ + TRACE_FUN(ft_t_flow); + + TRACE_EXIT -ENOSYS; +} + +static int mt_reset(int *dummy) +{ + TRACE_FUN(ft_t_flow); + + (void)ftape_seek_to_bot(); + TRACE_CATCH(ftape_reset_drive(), + zft_init_driver(); zft_uninit_mem(); zft_offline = 1); + /* fake a re-open of the device. This will set all flage and + * allocate buffers as appropriate. The new tape condition will + * force the open routine to do anything we need. + */ + TRACE_CATCH(_zft_open(-1 /* fake reopen */, 0 /* dummy */),); + TRACE_EXIT 0; +} + +static int mt_fsf(int *arg) +{ + int result; + TRACE_FUN(ft_t_flow); + + result = zft_skip_volumes(*arg, &zft_pos); + zft_just_before_eof = 0; + TRACE_EXIT result; +} + +static int mt_bsf(int *arg) +{ + int result = 0; + TRACE_FUN(ft_t_flow); + + if (*arg != 0) { + result = zft_skip_volumes(-*arg + 1, &zft_pos); + } + TRACE_EXIT result; +} + +static int seek_block(__s64 data_offset, + __s64 block_increment, + zft_position *pos) +{ + int result = 0; + __s64 new_block_pos; + __s64 vol_block_count; + const zft_volinfo *volume; + int exceed; + TRACE_FUN(ft_t_flow); + + volume = zft_find_volume(pos->seg_pos); + if (volume->start_seg == 0 || volume->end_seg == 0) { + TRACE_EXIT -EIO; + } + new_block_pos = (zft_div_blksz(data_offset, volume->blk_sz) + + block_increment); + vol_block_count = zft_div_blksz(volume->size, volume->blk_sz); + if (new_block_pos < 0) { + TRACE(ft_t_noise, + "new_block_pos " LL_X " < 0", LL(new_block_pos)); + zft_resid = (int)new_block_pos; + new_block_pos = 0; + exceed = 1; + } else if (new_block_pos > vol_block_count) { + TRACE(ft_t_noise, + "new_block_pos " LL_X " exceeds size of volume " LL_X, + LL(new_block_pos), LL(vol_block_count)); + zft_resid = (int)(vol_block_count - new_block_pos); + new_block_pos = vol_block_count; + exceed = 1; + } else { + exceed = 0; + } + if (zft_use_compression && volume->use_compression) { + TRACE_CATCH(zft_cmpr_lock(1 /* try to load */),); + result = (*zft_cmpr_ops->seek)(new_block_pos, pos, volume, + zft_deblock_buf); + pos->tape_pos = zft_calc_tape_pos(pos->seg_pos); + pos->tape_pos += pos->seg_byte_pos; + } else { + pos->volume_pos = zft_mul_blksz(new_block_pos, volume->blk_sz); + pos->tape_pos = zft_calc_tape_pos(volume->start_seg); + pos->tape_pos += pos->volume_pos; + pos->seg_pos = zft_calc_seg_byte_coord(&pos->seg_byte_pos, + pos->tape_pos); + } + zft_just_before_eof = volume->size == pos->volume_pos; + if (zft_just_before_eof) { + /* why this? because zft_file_no checks agains start + * and end segment of a volume. We do not want to + * advance to the next volume with this function. + */ + TRACE(ft_t_noise, "set zft_just_before_eof"); + zft_position_before_eof(pos, volume); + } + TRACE(ft_t_noise, "\n" + KERN_INFO "new_seg_pos : %d\n" + KERN_INFO "new_tape_pos: " LL_X "\n" + KERN_INFO "vol_size : " LL_X "\n" + KERN_INFO "seg_byte_pos: %d\n" + KERN_INFO "blk_sz : %d", + pos->seg_pos, LL(pos->tape_pos), + LL(volume->size), pos->seg_byte_pos, + volume->blk_sz); + if (!exceed) { + zft_resid = new_block_pos - zft_div_blksz(pos->volume_pos, + volume->blk_sz); + } + if (zft_resid < 0) { + zft_resid = -zft_resid; + } + TRACE_EXIT ((exceed || zft_resid != 0) && result >= 0) ? -EINVAL : result; +} + +static int mt_fsr(int *arg) +{ + int result; + TRACE_FUN(ft_t_flow); + + result = seek_block(zft_pos.volume_pos, *arg, &zft_pos); + TRACE_EXIT result; +} + +static int mt_bsr(int *arg) +{ + int result; + TRACE_FUN(ft_t_flow); + + result = seek_block(zft_pos.volume_pos, -*arg, &zft_pos); + TRACE_EXIT result; +} + +static int mt_weof(int *arg) +{ + int result; + TRACE_FUN(ft_t_flow); + + TRACE_CATCH(zft_flush_buffers(),); + result = zft_weof(*arg, &zft_pos); + TRACE_EXIT result; +} + +static int mt_rew(int *dummy) +{ + int result; + TRACE_FUN(ft_t_flow); + + if(zft_header_read) { + (void)zft_def_idle_state(); + } + result = ftape_seek_to_bot(); + zft_reset_position(&zft_pos); + TRACE_EXIT result; +} + +static int mt_offl(int *dummy) +{ + int result; + TRACE_FUN(ft_t_flow); + + going_offline= 1; + result = mt_rew(NULL); + TRACE_EXIT result; +} + +static int mt_nop(int *dummy) +{ + TRACE_FUN(ft_t_flow); + /* should we set tape status? + */ + if (!zft_offline) { /* offline includes no_tape */ + (void)zft_def_idle_state(); + } + TRACE_EXIT 0; +} + +static int mt_reten(int *dummy) +{ + int result; + TRACE_FUN(ft_t_flow); + + if(zft_header_read) { + (void)zft_def_idle_state(); + } + result = ftape_seek_to_eot(); + if (result >= 0) { + result = ftape_seek_to_bot(); + } + TRACE_EXIT(result); +} + +static int fsfbsfm(int arg, zft_position *pos) +{ + const zft_volinfo *vtbl; + __s64 block_pos; + TRACE_FUN(ft_t_flow); + + /* What to do? This should seek to the next file-mark and + * position BEFORE. That is, a next write would just extend + * the current file. Well. Let's just seek to the end of the + * current file, if count == 1. If count > 1, then do a + * "mt_fsf(count - 1)", and then seek to the end of that file. + * If count == 0, do nothing + */ + if (arg == 0) { + TRACE_EXIT 0; + } + zft_just_before_eof = 0; + TRACE_CATCH(zft_skip_volumes(arg < 0 ? arg : arg-1, pos), + if (arg > 0) { + zft_resid ++; + }); + vtbl = zft_find_volume(pos->seg_pos); + block_pos = zft_div_blksz(vtbl->size, vtbl->blk_sz); + (void)seek_block(0, block_pos, pos); + if (pos->volume_pos != vtbl->size) { + zft_just_before_eof = 0; + zft_resid = 1; + /* we didn't managed to go there */ + TRACE_ABORT(-EIO, ft_t_err, + "wanted file position " LL_X ", arrived at " LL_X, + LL(vtbl->size), LL(pos->volume_pos)); + } + zft_just_before_eof = 1; + TRACE_EXIT 0; +} + +static int mt_bsfm(int *arg) +{ + int result; + TRACE_FUN(ft_t_flow); + + result = fsfbsfm(-*arg, &zft_pos); + TRACE_EXIT result; +} + +static int mt_fsfm(int *arg) +{ + int result; + TRACE_FUN(ft_t_flow); + + result = fsfbsfm(*arg, &zft_pos); + TRACE_EXIT result; +} + +static int mt_eom(int *dummy) +{ + TRACE_FUN(ft_t_flow); + + zft_skip_to_eom(&zft_pos); + TRACE_EXIT 0; +} + +static int mt_erase(int *dummy) +{ + int result; + TRACE_FUN(ft_t_flow); + + result = zft_erase(); + TRACE_EXIT result; +} + +static int mt_ras2(int *dummy) +{ + int result; + TRACE_FUN(ft_t_flow); + + result = -ENOSYS; + TRACE_EXIT result; +} + +/* Sets the new blocksize in BYTES + * + */ +static int mt_setblk(int *new_size) +{ + TRACE_FUN(ft_t_flow); + + if((unsigned int)(*new_size) > ZFT_MAX_BLK_SZ) { + TRACE_ABORT(-EINVAL, ft_t_info, + "desired blk_sz (%d) should be <= %d bytes", + *new_size, ZFT_MAX_BLK_SZ); + } + if ((*new_size & (FT_SECTOR_SIZE-1)) != 0) { + TRACE_ABORT(-EINVAL, ft_t_info, + "desired blk_sz (%d) must be a multiple of %d bytes", + *new_size, FT_SECTOR_SIZE); + } + if (*new_size == 0) { + if (zft_use_compression) { + TRACE_ABORT(-EINVAL, ft_t_info, + "Variable block size not yet " + "supported with compression"); + } + *new_size = 1; + } + zft_blk_sz = *new_size; + TRACE_EXIT 0; +} + +static int mt_setdensity(int *arg) +{ + TRACE_FUN(ft_t_flow); + + SET_TRACE_LEVEL(*arg); + TRACE(TRACE_LEVEL, "tracing set to %d", TRACE_LEVEL); + if ((int)TRACE_LEVEL != *arg) { + TRACE_EXIT -EINVAL; + } + TRACE_EXIT 0; +} + +static int mt_seek(int *new_block_pos) +{ + int result= 0; + TRACE_FUN(ft_t_any); + + result = seek_block(0, (__s64)*new_block_pos, &zft_pos); + TRACE_EXIT result; +} + +/* OK, this is totally different from SCSI, but the worst thing that can + * happen is that there is not enough defragmentated memory that can be + * allocated. Also, there is a hardwired limit of 16 dma buffers in the + * stock ftape module. This shouldn't bring the system down. + * + * NOTE: the argument specifies the total number of dma buffers to use. + * The driver needs at least 3 buffers to function at all. + * + */ +static int mt_setdrvbuffer(int *cnt) +{ + TRACE_FUN(ft_t_flow); + + if (*cnt < 3) { + TRACE_EXIT -EINVAL; + } + TRACE_CATCH(ftape_set_nr_buffers(*cnt),); + TRACE_EXIT 0; +} +/* return the block position from start of volume + */ +static int mt_tell(int *arg) +{ + TRACE_FUN(ft_t_flow); + + *arg = zft_div_blksz(zft_pos.volume_pos, + zft_find_volume(zft_pos.seg_pos)->blk_sz); + TRACE_EXIT 0; +} + +static int mt_compression(int *arg) +{ + TRACE_FUN(ft_t_flow); + + /* Ok. We could also check whether compression is available at + * all by trying to load the compression module. We could + * also check for a block size of 1 byte which is illegal + * with compression. Instead of doing it here we rely on + * zftape_write() to do the proper checks. + */ + if ((unsigned int)*arg > 1) { + TRACE_EXIT -EINVAL; + } + if (*arg != 0 && zft_blk_sz == 1) { /* variable block size */ + TRACE_ABORT(-EINVAL, ft_t_info, + "Compression not yet supported " + "with variable block size"); + } + zft_mt_compression = *arg; + if ((zft_unit & ZFT_ZIP_MODE) == 0) { + zft_use_compression = zft_mt_compression; + } + TRACE_EXIT 0; +} + +/* check whether write access is allowed. Write access is denied when + * + zft_write_protected == 1 -- this accounts for either hard write + * protection of the cartridge or for + * O_RDONLY access mode of the tape device + * + zft_offline == 1 -- this meany that there is either no tape + * or that the MTOFFLINE ioctl has been + * previously issued (`soft eject') + * + ft_formatted == 0 -- this means that the cartridge is not + * formatted + * Then we distinuguish two cases. When zft_qic_mode is TRUE, then we try + * to emulate a `traditional' (aka SCSI like) UN*X tape device. Therefore we + * deny writes when + * + zft_qic_mode ==1 && + * (!zft_tape_at_lbot() && -- tape no at logical BOT + * !(zft_tape_at_eom() || -- tape not at logical EOM (or EOD) + * (zft_tape_at_eom() && + * zft_old_ftape()))) -- we can't add new volume to tapes + * written by old ftape because ftape + * don't use the volume table + * + * when the drive is in true raw mode (aka /dev/rawft0) then we don't + * care about LBOT and EOM conditions. This device is intended for a + * user level program that wants to truly implement the QIC-80 compliance + * at the logical data layout level of the cartridge, i.e. implement all + * that volume table and volume directory stuff etc.< + */ +int zft_check_write_access(zft_position *pos) +{ + TRACE_FUN(ft_t_flow); + + if (zft_offline) { /* offline includes no_tape */ + TRACE_ABORT(-ENXIO, + ft_t_info, "tape is offline or no cartridge"); + } + if (!ft_formatted) { + TRACE_ABORT(-EACCES, ft_t_info, "tape is not formatted"); + } + if (zft_write_protected) { + TRACE_ABORT(-EACCES, ft_t_info, "cartridge write protected"); + } + if (zft_qic_mode) { + /* check BOT condition */ + if (!zft_tape_at_lbot(pos)) { + /* protect cartridges written by old ftape if + * not at BOT because they use the vtbl + * segment for storing data + */ + if (zft_old_ftape) { + TRACE_ABORT(-EACCES, ft_t_warn, + "Cannot write to cartridges written by old ftape when not at BOT"); + } + /* not at BOT, but allow writes at EOD, of course + */ + if (!zft_tape_at_eod(pos)) { + TRACE_ABORT(-EACCES, ft_t_info, + "tape not at BOT and not at EOD"); + } + } + /* fine. Now the tape is either at BOT or at EOD. */ + } + /* or in raw mode in which case we don't care about BOT and EOD */ + TRACE_EXIT 0; +} + +/* OPEN routine called by kernel-interface code + * + * NOTE: this is also called by mt_reset() with dev_minor == -1 + * to fake a reopen after a reset. + */ +int _zft_open(unsigned int dev_minor, unsigned int access_mode) +{ + static unsigned int tape_unit; + static unsigned int file_access_mode; + int result; + TRACE_FUN(ft_t_flow); + + if ((int)dev_minor == -1) { + /* fake reopen */ + zft_unit = tape_unit; + access_mode = file_access_mode; + zft_init_driver(); /* reset all static data to defaults */ + } else { + tape_unit = dev_minor; + file_access_mode = access_mode; + if ((result = ftape_enable(FTAPE_SEL(dev_minor))) < 0) { + TRACE_ABORT(-ENXIO, ft_t_err, + "ftape_enable failed: %d", result); + } + if (ft_new_tape || ft_no_tape || !ft_formatted || + (FTAPE_SEL(zft_unit) != FTAPE_SEL(dev_minor)) || + (zft_unit & ZFT_RAW_MODE) != (dev_minor & ZFT_RAW_MODE)) { + /* reset all static data to defaults, + */ + zft_init_driver(); + } + zft_unit = dev_minor; + } + zft_set_flags(zft_unit); /* decode the minor bits */ + if (zft_blk_sz == 1 && zft_use_compression) { + ftape_disable(); /* resets ft_no_tape */ + TRACE_ABORT(-ENODEV, ft_t_warn, "Variable block size not yet " + "supported with compression"); + } + /* no need for most of the buffers when no tape or not + * formatted. for the read/write operations, it is the + * regardless whether there is no tape, a not-formatted tape + * or the whether the driver is soft offline. + * Nevertheless we allow some ioctls with non-formatted tapes, + * like rewind and reset. + */ + if (ft_no_tape || !ft_formatted) { + zft_uninit_mem(); + } + if (ft_no_tape) { + zft_offline = 1; /* so we need not test two variables */ + } + if ((access_mode == O_WRONLY || access_mode == O_RDWR) && + (ft_write_protected || ft_no_tape)) { + ftape_disable(); /* resets ft_no_tape */ + TRACE_ABORT(ft_no_tape ? -ENXIO : -EROFS, + ft_t_warn, "wrong access mode %s cartridge", + ft_no_tape ? "without a" : "with write protected"); + } + zft_write_protected = (access_mode == O_RDONLY || + ft_write_protected != 0); + if (zft_write_protected) { + TRACE(ft_t_noise, + "read only access mode: %d, " + "drive write protected: %d", + access_mode == O_RDONLY, + ft_write_protected != 0); + } + if (!zft_offline) { + TRACE_CATCH(zft_vmalloc_once(&zft_deblock_buf,FT_SEGMENT_SIZE), + ftape_disable()); + } + /* zft_seg_pos should be greater than the vtbl segpos but not + * if in compatibility mode and only after we read in the + * header segments + * + * might also be a problem if the user makes a backup with a + * *qft* device and rewinds it with a raw device. + */ + if (zft_qic_mode && + !zft_old_ftape && + zft_pos.seg_pos >= 0 && + zft_header_read && + zft_pos.seg_pos <= ft_first_data_segment) { + TRACE(ft_t_noise, "you probably mixed up the zftape devices!"); + zft_reset_position(&zft_pos); + } + TRACE_EXIT 0; +} + +/* RELEASE routine called by kernel-interface code + */ +int _zft_close(void) +{ + int result = 0; + TRACE_FUN(ft_t_flow); + + if (zft_offline) { + /* call the hardware release routine. Puts the drive offline */ + ftape_disable(); + TRACE_EXIT 0; + } + if (!(ft_write_protected || zft_old_ftape)) { + result = zft_flush_buffers(); + TRACE(ft_t_noise, "writing file mark at current position"); + if (zft_qic_mode && zft_close_volume(&zft_pos) == 0) { + zft_move_past_eof(&zft_pos); + } + if ((zft_tape_at_lbot(&zft_pos) || + !(zft_unit & FTAPE_NO_REWIND))) { + if (result >= 0) { + result = zft_update_header_segments(); + } else { + TRACE(ft_t_err, + "Error: unable to update header segments"); + } + } + } + ftape_abort_operation(); + if (!(zft_unit & FTAPE_NO_REWIND)) { + TRACE(ft_t_noise, "rewinding tape"); + if (ftape_seek_to_bot() < 0 && result >= 0) { + result = -EIO; /* keep old value */ + } + zft_reset_position(&zft_pos); + } + zft_zap_read_buffers(); + /* now free up memory as much as possible. We don't destroy + * the deblock buffer if it containes a valid segment. + */ + if (zft_deblock_segment == -1) { + zft_vfree(&zft_deblock_buf, FT_SEGMENT_SIZE); + } + /* high level driver status, forces creation of a new volume + * when calling ftape_write again and not zft_just_before_eof + */ + zft_io_state = zft_idle; + if (going_offline) { + zft_init_driver(); + zft_uninit_mem(); + going_offline = 0; + zft_offline = 1; + } else if (zft_cmpr_lock(0 /* don't load */) == 0) { + (*zft_cmpr_ops->reset)(); /* unlock it again */ + } + zft_memory_stats(); + /* call the hardware release routine. Puts the drive offline */ + ftape_disable(); + TRACE_EXIT result; +} + +/* + * the wrapper function around the wrapper MTIOCTOP ioctl + */ +static int mtioctop(struct mtop *mtop, int arg_size) +{ + int result = 0; + fun_entry *mt_fun_entry; + TRACE_FUN(ft_t_flow); + + if (arg_size != sizeof(struct mtop) || mtop->mt_op >= NR_MT_CMDS) { + TRACE_EXIT -EINVAL; + } + TRACE(ft_t_noise, "calling MTIOCTOP command: %s", + mt_funs[mtop->mt_op].name); + mt_fun_entry= &mt_funs[mtop->mt_op]; + zft_resid = mtop->mt_count; + if (!mt_fun_entry->offline && zft_offline) { + if (ft_no_tape) { + TRACE_ABORT(-ENXIO, ft_t_info, "no tape present"); + } else { + TRACE_ABORT(-ENXIO, ft_t_info, "drive is offline"); + } + } + if (!mt_fun_entry->not_formatted && !ft_formatted) { + TRACE_ABORT(-EACCES, ft_t_info, "tape is not formatted"); + } + if (!mt_fun_entry->write_protected) { + TRACE_CATCH(zft_check_write_access(&zft_pos),); + } + if (mt_fun_entry->need_idle_state && !(zft_offline || !ft_formatted)) { + TRACE_CATCH(zft_def_idle_state(),); + } + if (!zft_qic_mode && !mt_fun_entry->raw_mode) { + TRACE_ABORT(-EACCES, ft_t_info, +"Drive needs to be in QIC-80 compatibility mode for this command"); + } + result = (mt_fun_entry->function)(&mtop->mt_count); + if (zft_tape_at_lbot(&zft_pos)) { + TRACE_CATCH(zft_update_header_segments(),); + } + if (result >= 0) { + zft_resid = 0; + } + TRACE_EXIT result; +} + +/* + * standard MTIOCGET ioctl + */ +static int mtiocget(struct mtget *mtget, int arg_size) +{ + const zft_volinfo *volume; + __s64 max_tape_pos; + TRACE_FUN(ft_t_flow); + + if (arg_size != sizeof(struct mtget)) { + TRACE_ABORT(-EINVAL, ft_t_info, "bad argument size: %d", + arg_size); + } + mtget->mt_type = ft_drive_type.vendor_id + 0x800000; + mtget->mt_dsreg = ft_last_status.space; + mtget->mt_erreg = ft_last_error.space; /* error register */ + mtget->mt_resid = zft_resid; /* residuum of writes, reads and + * MTIOCTOP commands + */ + if (!zft_offline) { /* neither no_tape nor soft offline */ + mtget->mt_gstat = GMT_ONLINE(~0UL); + /* should rather return the status of the cartridge + * than the access mode of the file, therefor use + * ft_write_protected, not zft_write_protected + */ + if (ft_write_protected) { + mtget->mt_gstat |= GMT_WR_PROT(~0UL); + } + if(zft_header_read) { /* this catches non-formatted */ + volume = zft_find_volume(zft_pos.seg_pos); + mtget->mt_fileno = volume->count; + max_tape_pos = zft_capacity - zft_blk_sz; + if (zft_use_compression) { + max_tape_pos -= ZFT_CMPR_OVERHEAD; + } + if (zft_tape_at_eod(&zft_pos)) { + mtget->mt_gstat |= GMT_EOD(~0UL); + } + if (zft_pos.tape_pos > max_tape_pos) { + mtget->mt_gstat |= GMT_EOT(~0UL); + } + mtget->mt_blkno = zft_div_blksz(zft_pos.volume_pos, + volume->blk_sz); + if (zft_just_before_eof) { + mtget->mt_gstat |= GMT_EOF(~0UL); + } + if (zft_tape_at_lbot(&zft_pos)) { + mtget->mt_gstat |= GMT_BOT(~0UL); + } + } else { + mtget->mt_fileno = mtget->mt_blkno = -1; + if (mtget->mt_dsreg & QIC_STATUS_AT_BOT) { + mtget->mt_gstat |= GMT_BOT(~0UL); + } + } + } else { + if (ft_no_tape) { + mtget->mt_gstat = GMT_DR_OPEN(~0UL); + } else { + mtget->mt_gstat = 0UL; + } + mtget->mt_fileno = mtget->mt_blkno = -1; + } + TRACE_EXIT 0; +} + +#ifdef MTIOCRDFTSEG +/* + * Read a floppy tape segment. This is useful for manipulating the + * volume table, and read the old header segment before re-formatting + * the cartridge. + */ +static int mtiocrdftseg(struct mtftseg * mtftseg, int arg_size) +{ + TRACE_FUN(ft_t_flow); + + TRACE(ft_t_noise, "Mag tape ioctl command: MTIOCRDFTSEG"); + if (zft_qic_mode) { + TRACE_ABORT(-EACCES, ft_t_info, + "driver needs to be in raw mode for this ioctl"); + } + if (arg_size != sizeof(struct mtftseg)) { + TRACE_ABORT(-EINVAL, ft_t_info, "bad argument size: %d", + arg_size); + } + if (zft_offline) { + TRACE_EXIT -ENXIO; + } + if (mtftseg->mt_mode != FT_RD_SINGLE && + mtftseg->mt_mode != FT_RD_AHEAD) { + TRACE_ABORT(-EINVAL, ft_t_info, "invalid read mode"); + } + if (!ft_formatted) { + TRACE_EXIT -EACCES; /* -ENXIO ? */ + + } + if (!zft_header_read) { + TRACE_CATCH(zft_def_idle_state(),); + } + if (mtftseg->mt_segno > ft_last_data_segment) { + TRACE_ABORT(-EINVAL, ft_t_info, "segment number is too large"); + } + mtftseg->mt_result = ftape_read_segment(mtftseg->mt_segno, + zft_deblock_buf, + mtftseg->mt_mode); + if (mtftseg->mt_result < 0) { + /* a negativ result is not an ioctl error. if + * the user wants to read damaged tapes, + * it's up to her/him + */ + TRACE_EXIT 0; + } + if (copy_to_user(mtftseg->mt_data, + zft_deblock_buf, + mtftseg->mt_result) != 0) { + TRACE_EXIT -EFAULT; + } + TRACE_EXIT 0; +} +#endif + +#ifdef MTIOCWRFTSEG +/* + * write a floppy tape segment. This version features writing of + * deleted address marks, and gracefully ignores the (software) + * ft_formatted flag to support writing of header segments after + * formatting. + */ +static int mtiocwrftseg(struct mtftseg * mtftseg, int arg_size) +{ + int result; + TRACE_FUN(ft_t_flow); + + TRACE(ft_t_noise, "Mag tape ioctl command: MTIOCWRFTSEG"); + if (zft_write_protected || zft_qic_mode) { + TRACE_EXIT -EACCES; + } + if (arg_size != sizeof(struct mtftseg)) { + TRACE_ABORT(-EINVAL, ft_t_info, "bad argument size: %d", + arg_size); + } + if (zft_offline) { + TRACE_EXIT -ENXIO; + } + if (mtftseg->mt_mode != FT_WR_ASYNC && + mtftseg->mt_mode != FT_WR_MULTI && + mtftseg->mt_mode != FT_WR_SINGLE && + mtftseg->mt_mode != FT_WR_DELETE) { + TRACE_ABORT(-EINVAL, ft_t_info, "invalid write mode"); + } + /* + * We don't check for ft_formatted, because this gives + * only the software status of the driver. + * + * We assume that the user knows what it is + * doing. And rely on the low level stuff to fail + * when the tape isn't formatted. We only make sure + * that The header segment buffer is allocated, + * because it holds the bad sector map. + */ + if (zft_hseg_buf == NULL) { + TRACE_EXIT -ENXIO; + } + if (mtftseg->mt_mode != FT_WR_DELETE) { + if (copy_from_user(zft_deblock_buf, + mtftseg->mt_data, + FT_SEGMENT_SIZE) != 0) { + TRACE_EXIT -EFAULT; + } + } + mtftseg->mt_result = ftape_write_segment(mtftseg->mt_segno, + zft_deblock_buf, + mtftseg->mt_mode); + if (mtftseg->mt_result >= 0 && mtftseg->mt_mode == FT_WR_SINGLE) { + /* + * a negativ result is not an ioctl error. if + * the user wants to write damaged tapes, + * it's up to her/him + */ + if ((result = ftape_loop_until_writes_done()) < 0) { + mtftseg->mt_result = result; + } + } + TRACE_EXIT 0; +} +#endif + +#ifdef MTIOCVOLINFO +/* + * get information about volume positioned at. + */ +static int mtiocvolinfo(struct mtvolinfo *volinfo, int arg_size) +{ + const zft_volinfo *volume; + TRACE_FUN(ft_t_flow); + + TRACE(ft_t_noise, "Mag tape ioctl command: MTIOCVOLINFO"); + if (arg_size != sizeof(struct mtvolinfo)) { + TRACE_ABORT(-EINVAL, + ft_t_info, "bad argument size: %d", arg_size); + } + if (zft_offline) { + TRACE_EXIT -ENXIO; + } + if (!ft_formatted) { + TRACE_EXIT -EACCES; + } + TRACE_CATCH(zft_def_idle_state(),); + volume = zft_find_volume(zft_pos.seg_pos); + volinfo->mt_volno = volume->count; + volinfo->mt_blksz = volume->blk_sz == 1 ? 0 : volume->blk_sz; + volinfo->mt_size = volume->size >> 10; + volinfo->mt_rawsize = ((zft_calc_tape_pos(volume->end_seg + 1) >> 10) - + (zft_calc_tape_pos(volume->start_seg) >> 10)); + volinfo->mt_cmpr = volume->use_compression; + TRACE_EXIT 0; +} +#endif + +#ifdef ZFT_OBSOLETE +static int mtioc_zftape_getblksz(struct mtblksz *blksz, int arg_size) +{ + TRACE_FUN(ft_t_flow); + + TRACE(ft_t_noise, "\n" + KERN_INFO "Mag tape ioctl command: MTIOC_ZTAPE_GETBLKSZ\n" + KERN_INFO "This ioctl is here merely for compatibility.\n" + KERN_INFO "Please use MTIOCVOLINFO instead"); + if (arg_size != sizeof(struct mtblksz)) { + TRACE_ABORT(-EINVAL, + ft_t_info, "bad argument size: %d", arg_size); + } + if (zft_offline) { + TRACE_EXIT -ENXIO; + } + if (!ft_formatted) { + TRACE_EXIT -EACCES; + } + TRACE_CATCH(zft_def_idle_state(),); + blksz->mt_blksz = zft_find_volume(zft_pos.seg_pos)->blk_sz; + TRACE_EXIT 0; +} +#endif + +#ifdef MTIOCGETSIZE +/* + * get the capacity of the tape cartridge. + */ +static int mtiocgetsize(struct mttapesize *size, int arg_size) +{ + TRACE_FUN(ft_t_flow); + + TRACE(ft_t_noise, "Mag tape ioctl command: MTIOC_ZFTAPE_GETSIZE"); + if (arg_size != sizeof(struct mttapesize)) { + TRACE_ABORT(-EINVAL, + ft_t_info, "bad argument size: %d", arg_size); + } + if (zft_offline) { + TRACE_EXIT -ENXIO; + } + if (!ft_formatted) { + TRACE_EXIT -EACCES; + } + TRACE_CATCH(zft_def_idle_state(),); + size->mt_capacity = (unsigned int)(zft_capacity>>10); + size->mt_used = (unsigned int)(zft_get_eom_pos()>>10); + TRACE_EXIT 0; +} +#endif + +static int mtiocpos(struct mtpos *mtpos, int arg_size) +{ + int result; + TRACE_FUN(ft_t_flow); + + TRACE(ft_t_noise, "Mag tape ioctl command: MTIOCPOS"); + if (arg_size != sizeof(struct mtpos)) { + TRACE_ABORT(-EINVAL, + ft_t_info, "bad argument size: %d", arg_size); + } + result = mt_tell((int *)&mtpos->mt_blkno); + TRACE_EXIT result; +} + +#ifdef MTIOCFTFORMAT +/* + * formatting of floppy tape cartridges. This is intended to be used + * together with the MTIOCFTCMD ioctl and the new mmap feature + */ + +/* + * This function uses ftape_decode_header_segment() to inform the low + * level ftape module about the new parameters. + * + * It erases the hseg_buf. The calling process must specify all + * parameters to assure proper operation. + * + * return values: -EINVAL - wrong argument size + * -EINVAL - if ftape_decode_header_segment() failed. + */ +static int set_format_parms(struct ftfmtparms *p, __u8 *hseg_buf) +{ + ft_trace_t old_level = TRACE_LEVEL; + TRACE_FUN(ft_t_flow); + + TRACE(ft_t_noise, "MTIOCFTFORMAT operation FTFMT_SETPARMS"); + memset(hseg_buf, 0, FT_SEGMENT_SIZE); + PUT4(hseg_buf, FT_SIGNATURE, FT_HSEG_MAGIC); + + /* fill in user specified parameters + */ + hseg_buf[FT_FMT_CODE] = (__u8)p->ft_fmtcode; + PUT2(hseg_buf, FT_SPT, p->ft_spt); + hseg_buf[FT_TPC] = (__u8)p->ft_tpc; + hseg_buf[FT_FHM] = (__u8)p->ft_fhm; + hseg_buf[FT_FTM] = (__u8)p->ft_ftm; + + /* fill in sane defaults to make ftape happy. + */ + hseg_buf[FT_FSM] = (__u8)128; /* 128 is hard wired all over ftape */ + if (p->ft_fmtcode == fmt_big) { + PUT4(hseg_buf, FT_6_HSEG_1, 0); + PUT4(hseg_buf, FT_6_HSEG_2, 1); + PUT4(hseg_buf, FT_6_FRST_SEG, 2); + PUT4(hseg_buf, FT_6_LAST_SEG, p->ft_spt * p->ft_tpc - 1); + } else { + PUT2(hseg_buf, FT_HSEG_1, 0); + PUT2(hseg_buf, FT_HSEG_2, 1); + PUT2(hseg_buf, FT_FRST_SEG, 2); + PUT2(hseg_buf, FT_LAST_SEG, p->ft_spt * p->ft_tpc - 1); + } + + /* Synchronize with the low level module. This is particularly + * needed for unformatted cartridges as the QIC std was previously + * unknown BUT is needed to set data rate and to calculate timeouts. + */ + TRACE_CATCH(ftape_calibrate_data_rate(p->ft_qicstd&QIC_TAPE_STD_MASK), + _res = -EINVAL); + + /* The following will also recalcualte the timeouts for the tape + * length and QIC std we want to format to. + * abort with -EINVAL rather than -EIO + */ + SET_TRACE_LEVEL(ft_t_warn); + TRACE_CATCH(ftape_decode_header_segment(hseg_buf), + SET_TRACE_LEVEL(old_level); _res = -EINVAL); + SET_TRACE_LEVEL(old_level); + TRACE_EXIT 0; +} + +/* + * Return the internal SOFTWARE status of the kernel driver. This does + * NOT query the tape drive about its status. + */ +static int get_format_parms(struct ftfmtparms *p, __u8 *hseg_buffer) +{ + TRACE_FUN(ft_t_flow); + + TRACE(ft_t_noise, "MTIOCFTFORMAT operation FTFMT_GETPARMS"); + p->ft_qicstd = ft_qic_std; + p->ft_fmtcode = ft_format_code; + p->ft_fhm = hseg_buffer[FT_FHM]; + p->ft_ftm = hseg_buffer[FT_FTM]; + p->ft_spt = ft_segments_per_track; + p->ft_tpc = ft_tracks_per_tape; + TRACE_EXIT 0; +} + +static int mtiocftformat(struct mtftformat *mtftformat, int arg_size) +{ + int result; + union fmt_arg *arg = &mtftformat->fmt_arg; + TRACE_FUN(ft_t_flow); + + TRACE(ft_t_noise, "Mag tape ioctl command: MTIOCFTFORMAT"); + if (zft_offline) { + if (ft_no_tape) { + TRACE_ABORT(-ENXIO, ft_t_info, "no tape present"); + } else { + TRACE_ABORT(-ENXIO, ft_t_info, "drive is offline"); + } + } + if (zft_qic_mode) { + TRACE_ABORT(-EACCES, ft_t_info, + "driver needs to be in raw mode for this ioctl"); + } + if (zft_hseg_buf == NULL) { + TRACE_CATCH(zft_vcalloc_once(&zft_hseg_buf, FT_SEGMENT_SIZE),); + } + zft_header_read = 0; + switch(mtftformat->fmt_op) { + case FTFMT_SET_PARMS: + TRACE_CATCH(set_format_parms(&arg->fmt_parms, zft_hseg_buf),); + TRACE_EXIT 0; + case FTFMT_GET_PARMS: + TRACE_CATCH(get_format_parms(&arg->fmt_parms, zft_hseg_buf),); + TRACE_EXIT 0; + case FTFMT_FORMAT_TRACK: + if ((ft_formatted && zft_check_write_access(&zft_pos) < 0) || + (!ft_formatted && zft_write_protected)) { + TRACE_ABORT(-EACCES, ft_t_info, "Write access denied"); + } + TRACE_CATCH(ftape_format_track(arg->fmt_track.ft_track, + arg->fmt_track.ft_gap3),); + TRACE_EXIT 0; + case FTFMT_STATUS: + TRACE_CATCH(ftape_format_status(&arg->fmt_status.ft_segment),); + TRACE_EXIT 0; + case FTFMT_VERIFY: + TRACE_CATCH(ftape_verify_segment(arg->fmt_verify.ft_segment, + (SectorMap *)&arg->fmt_verify.ft_bsm),); + TRACE_EXIT 0; + default: + TRACE_ABORT(-EINVAL, ft_t_err, "Invalid format operation"); + } + TRACE_EXIT result; +} +#endif + +#ifdef MTIOCFTCMD +/* + * send a QIC-117 command to the drive, with optional timeouts, + * parameter and result bits. This is intended to be used together + * with the formatting ioctl. + */ +static int mtiocftcmd(struct mtftcmd *ftcmd, int arg_size) +{ + int i; + TRACE_FUN(ft_t_flow); + + TRACE(ft_t_noise, "Mag tape ioctl command: MTIOCFTCMD"); + if (!capable(CAP_SYS_ADMIN)) { + TRACE_ABORT(-EPERM, ft_t_info, + "need CAP_SYS_ADMIN capability to send raw qic-117 commands"); + } + if (zft_qic_mode) { + TRACE_ABORT(-EACCES, ft_t_info, + "driver needs to be in raw mode for this ioctl"); + } + if (arg_size != sizeof(struct mtftcmd)) { + TRACE_ABORT(-EINVAL, + ft_t_info, "bad argument size: %d", arg_size); + } + if (ftcmd->ft_wait_before) { + TRACE_CATCH(ftape_ready_wait(ftcmd->ft_wait_before, + &ftcmd->ft_status),); + } + if (ftcmd->ft_status & QIC_STATUS_ERROR) + goto ftmtcmd_error; + if (ftcmd->ft_result_bits != 0) { + TRACE_CATCH(ftape_report_operation(&ftcmd->ft_result, + ftcmd->ft_cmd, + ftcmd->ft_result_bits),); + } else { + TRACE_CATCH(ftape_command(ftcmd->ft_cmd),); + if (ftcmd->ft_status & QIC_STATUS_ERROR) + goto ftmtcmd_error; + for (i = 0; i < ftcmd->ft_parm_cnt; i++) { + TRACE_CATCH(ftape_parameter(ftcmd->ft_parms[i]&0x0f),); + if (ftcmd->ft_status & QIC_STATUS_ERROR) + goto ftmtcmd_error; + } + } + if (ftcmd->ft_wait_after != 0) { + TRACE_CATCH(ftape_ready_wait(ftcmd->ft_wait_after, + &ftcmd->ft_status),); + } +ftmtcmd_error: + if (ftcmd->ft_status & QIC_STATUS_ERROR) { + TRACE(ft_t_noise, "error status set"); + TRACE_CATCH(ftape_report_error(&ftcmd->ft_error, + &ftcmd->ft_cmd, 1),); + } + TRACE_EXIT 0; /* this is not an i/o error */ +} +#endif + +/* IOCTL routine called by kernel-interface code + */ +int _zft_ioctl(unsigned int command, void __user * arg) +{ + int result; + union { struct mtop mtop; + struct mtget mtget; + struct mtpos mtpos; +#ifdef MTIOCRDFTSEG + struct mtftseg mtftseg; +#endif +#ifdef MTIOCVOLINFO + struct mtvolinfo mtvolinfo; +#endif +#ifdef MTIOCGETSIZE + struct mttapesize mttapesize; +#endif +#ifdef MTIOCFTFORMAT + struct mtftformat mtftformat; +#endif +#ifdef ZFT_OBSOLETE + struct mtblksz mtblksz; +#endif +#ifdef MTIOCFTCMD + struct mtftcmd mtftcmd; +#endif + } krnl_arg; + int arg_size = _IOC_SIZE(command); + int dir = _IOC_DIR(command); + TRACE_FUN(ft_t_flow); + + /* This check will only catch arguments that are too large ! + */ + if (dir & (_IOC_READ | _IOC_WRITE) && arg_size > sizeof(krnl_arg)) { + TRACE_ABORT(-EINVAL, + ft_t_info, "bad argument size: %d", arg_size); + } + if (dir & _IOC_WRITE) { + if (copy_from_user(&krnl_arg, arg, arg_size) != 0) { + TRACE_EXIT -EFAULT; + } + } + TRACE(ft_t_flow, "called with ioctl command: 0x%08x", command); + switch (command) { + case MTIOCTOP: + result = mtioctop(&krnl_arg.mtop, arg_size); + break; + case MTIOCGET: + result = mtiocget(&krnl_arg.mtget, arg_size); + break; + case MTIOCPOS: + result = mtiocpos(&krnl_arg.mtpos, arg_size); + break; +#ifdef MTIOCVOLINFO + case MTIOCVOLINFO: + result = mtiocvolinfo(&krnl_arg.mtvolinfo, arg_size); + break; +#endif +#ifdef ZFT_OBSOLETE + case MTIOC_ZFTAPE_GETBLKSZ: + result = mtioc_zftape_getblksz(&krnl_arg.mtblksz, arg_size); + break; +#endif +#ifdef MTIOCRDFTSEG + case MTIOCRDFTSEG: /* read a segment via ioctl */ + result = mtiocrdftseg(&krnl_arg.mtftseg, arg_size); + break; +#endif +#ifdef MTIOCWRFTSEG + case MTIOCWRFTSEG: /* write a segment via ioctl */ + result = mtiocwrftseg(&krnl_arg.mtftseg, arg_size); + break; +#endif +#ifdef MTIOCGETSIZE + case MTIOCGETSIZE: + result = mtiocgetsize(&krnl_arg.mttapesize, arg_size); + break; +#endif +#ifdef MTIOCFTFORMAT + case MTIOCFTFORMAT: + result = mtiocftformat(&krnl_arg.mtftformat, arg_size); + break; +#endif +#ifdef MTIOCFTCMD + case MTIOCFTCMD: + result = mtiocftcmd(&krnl_arg.mtftcmd, arg_size); + break; +#endif + default: + result = -EINVAL; + break; + } + if ((result >= 0) && (dir & _IOC_READ)) { + if (copy_to_user(arg, &krnl_arg, arg_size) != 0) { + TRACE_EXIT -EFAULT; + } + } + TRACE_EXIT result; +} diff --git a/drivers/char/ftape/zftape/zftape-ctl.h b/drivers/char/ftape/zftape/zftape-ctl.h new file mode 100644 index 00000000000..41415989199 --- /dev/null +++ b/drivers/char/ftape/zftape/zftape-ctl.h @@ -0,0 +1,59 @@ +#ifndef _ZFTAPE_CTL_H +#define _ZFTAPE_CTL_H + +/* + * Copyright (C) 1996, 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-ctl.h,v $ + * $Revision: 1.2 $ + * $Date: 1997/10/05 19:19:02 $ + * + * This file contains the non-standard IOCTL related definitions + * for the QIC-40/80 floppy-tape driver for Linux. + */ + +#include <linux/config.h> +#include <linux/ioctl.h> +#include <linux/mtio.h> + +#include "../zftape/zftape-rw.h" + +#ifdef CONFIG_ZFTAPE_MODULE +#define ftape_status (*zft_status) +#endif + +extern int zft_offline; +extern int zft_mt_compression; +extern int zft_write_protected; +extern int zft_header_read; +extern unsigned int zft_unit; +extern int zft_resid; + +extern void zft_reset_position(zft_position *pos); +extern int zft_check_write_access(zft_position *pos); +extern int zft_def_idle_state(void); + +/* hooks for the VFS interface + */ +extern int _zft_open(unsigned int dev_minor, unsigned int access_mode); +extern int _zft_close(void); +extern int _zft_ioctl(unsigned int command, void __user *arg); +#endif + + + diff --git a/drivers/char/ftape/zftape/zftape-eof.c b/drivers/char/ftape/zftape/zftape-eof.c new file mode 100644 index 00000000000..dcadcaee9ac --- /dev/null +++ b/drivers/char/ftape/zftape/zftape-eof.c @@ -0,0 +1,199 @@ +/* + * I use these routines just to decide when I have to fake a + * volume-table to preserve compatibility to original ftape. + */ +/* + * Copyright (C) 1994-1995 Bas Laarhoven. + * + * Modified for zftape 1996, 1997 Claus 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-eof.c,v $ + * $Revision: 1.2 $ + * $Date: 1997/10/05 19:19:02 $ + * + * This file contains the eof mark handling code + * for the QIC-40/80 floppy-tape driver for Linux. + */ + +#include <linux/string.h> +#include <linux/errno.h> + +#include <linux/zftape.h> + +#include "../zftape/zftape-init.h" +#include "../zftape/zftape-rw.h" +#include "../zftape/zftape-eof.h" + +/* Global vars. + */ + +/* a copy of the failed sector log from the header segment. + */ +eof_mark_union *zft_eof_map; + +/* number of eof marks (entries in bad sector log) on tape. + */ +int zft_nr_eof_marks = -1; + + +/* Local vars. + */ + +static char linux_tape_label[] = "Linux raw format V"; +enum { + min_fmt_version = 1, max_fmt_version = 2 +}; +static unsigned ftape_fmt_version = 0; + + +/* Ftape (mis)uses the bad sector log to record end-of-file marks. + * Initially (when the tape is erased) all entries in the bad sector + * log are added to the tape's bad sector map. The bad sector log then + * is cleared. + * + * The bad sector log normally contains entries of the form: + * even 16-bit word: segment number of bad sector + * odd 16-bit word: encoded date + * There can be a total of 448 entries (1792 bytes). + * + * My guess is that no program is using this bad sector log (the * + * format seems useless as there is no indication of the bad sector + * itself, only the segment) However, if any program does use the bad + * sector log, the format used by ftape will let the program think + * there are some bad sectors and no harm is done. + * + * The eof mark entries that ftape stores in the bad sector log: even + * 16-bit word: segment number of eof mark odd 16-bit word: sector + * number of eof mark [1..32] + * + * The zft_eof_map as maintained is a sorted list of eof mark entries. + * + * + * The tape name field in the header segments is used to store a linux + * tape identification string and a version number. This way the tape + * can be recognized as a Linux raw format tape when using tools under + * other OS's. + * + * 'Wide' QIC tapes (format code 4) don't have a failed sector list + * anymore. That space is used for the (longer) bad sector map that + * now is a variable length list too. We now store our end-of-file + * marker list after the bad-sector-map on tape. The list is delimited + * by a (__u32) 0 entry. + */ + +int zft_ftape_validate_label(char *label) +{ + static char tmp_label[45]; + int result = 0; + TRACE_FUN(ft_t_any); + + memcpy(tmp_label, label, FT_LABEL_SZ); + tmp_label[FT_LABEL_SZ] = '\0'; + TRACE(ft_t_noise, "tape label = `%s'", tmp_label); + ftape_fmt_version = 0; + if (memcmp(label, linux_tape_label, strlen(linux_tape_label)) == 0) { + int pos = strlen(linux_tape_label); + while (label[pos] >= '0' && label[pos] <= '9') { + ftape_fmt_version *= 10; + ftape_fmt_version = label[ pos++] - '0'; + } + result = (ftape_fmt_version >= min_fmt_version && + ftape_fmt_version <= max_fmt_version); + } + TRACE(ft_t_noise, "format version = %d", ftape_fmt_version); + TRACE_EXIT result; +} + +static __u8 * find_end_of_eof_list(__u8 * ptr, __u8 * limit) +{ + while (ptr + 3 < limit) { + + if (get_unaligned((__u32*)ptr)) { + ptr += sizeof(__u32); + } else { + return ptr; + } + } + return NULL; +} + +void zft_ftape_extract_file_marks(__u8* address) +{ + int i; + TRACE_FUN(ft_t_any); + + zft_eof_map = NULL; + if (ft_format_code == fmt_var || ft_format_code == fmt_big) { + __u8* end; + __u8* start = ftape_find_end_of_bsm_list(address); + + zft_nr_eof_marks = 0; + if (start) { + start += 3; /* skip end of list mark */ + end = find_end_of_eof_list(start, + address + FT_SEGMENT_SIZE); + if (end && end - start <= FT_FSL_SIZE) { + zft_nr_eof_marks = ((end - start) / + sizeof(eof_mark_union)); + zft_eof_map = (eof_mark_union *)start; + } else { + TRACE(ft_t_err, + "EOF Mark List is too long or damaged!"); + } + } else { + TRACE(ft_t_err, + "Bad Sector List is too long or damaged !"); + } + } else { + zft_eof_map = (eof_mark_union *)&address[FT_FSL]; + zft_nr_eof_marks = GET2(address, FT_FSL_CNT); + } + TRACE(ft_t_noise, "number of file marks: %d", zft_nr_eof_marks); + if (ftape_fmt_version == 1) { + TRACE(ft_t_info, "swapping version 1 fields"); + /* version 1 format uses swapped sector and segment + * fields, correct that ! + */ + for (i = 0; i < zft_nr_eof_marks; ++i) { + __u16 tmp = GET2(&zft_eof_map[i].mark.segment,0); + PUT2(&zft_eof_map[i].mark.segment, 0, + GET2(&zft_eof_map[i].mark.date,0)); + PUT2(&zft_eof_map[i].mark.date, 0, tmp); + } + } + for (i = 0; i < zft_nr_eof_marks; ++i) { + TRACE(ft_t_noise, "eof mark: %5d/%2d", + GET2(&zft_eof_map[i].mark.segment, 0), + GET2(&zft_eof_map[i].mark.date,0)); + } + TRACE_EXIT; +} + +void zft_clear_ftape_file_marks(void) +{ + TRACE_FUN(ft_t_flow); + /* Clear failed sector log: remove all tape marks. We + * don't use old ftape-style EOF-marks. + */ + TRACE(ft_t_info, "Clearing old ftape's eof map"); + memset(zft_eof_map, 0, zft_nr_eof_marks * sizeof(__u32)); + zft_nr_eof_marks = 0; + PUT2(zft_hseg_buf, FT_FSL_CNT, 0); /* nr of eof-marks */ + zft_header_changed = 1; + zft_update_label(zft_hseg_buf); + TRACE_EXIT; +} diff --git a/drivers/char/ftape/zftape/zftape-eof.h b/drivers/char/ftape/zftape/zftape-eof.h new file mode 100644 index 00000000000..26568c26c51 --- /dev/null +++ b/drivers/char/ftape/zftape/zftape-eof.h @@ -0,0 +1,52 @@ +#ifndef _ZFTAPE_EOF_H +#define _ZFTAPE_EOF_H + +/* + * Copyright (C) 1994-1995 Bas Laarhoven. + * adaptaed for zftape 1996, 1997 by Claus 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-eof.h,v $ + * $Revision: 1.2 $ + * $Date: 1997/10/05 19:19:03 $ + * + * Definitions and declarations for the end of file markers + * for the QIC-40/80 floppy-tape driver for Linux. + */ + +#include <linux/ftape-header-segment.h> +#include "../zftape/zftape-buffers.h" +/* failed sector log size (only used if format code != 4). + */ + +typedef union { + ft_fsl_entry mark; + __u32 entry; +} eof_mark_union; + +/* ftape-eof.c defined global vars. + */ +extern int zft_nr_eof_marks; +extern eof_mark_union *zft_eof_map; + +/* ftape-eof.c defined global functions. + */ +extern void zft_ftape_extract_file_marks(__u8* address); +extern int zft_ftape_validate_label(char* label); +extern void zft_clear_ftape_file_marks(void); + +#endif diff --git a/drivers/char/ftape/zftape/zftape-init.c b/drivers/char/ftape/zftape/zftape-init.c new file mode 100644 index 00000000000..dbac7e54e8e --- /dev/null +++ b/drivers/char/ftape/zftape/zftape-init.c @@ -0,0 +1,403 @@ +/* + * Copyright (C) 1996, 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. + + * + * This file contains the code that registers the zftape frontend + * to the ftape floppy tape driver for Linux + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/fs.h> +#include <linux/kernel.h> +#include <linux/signal.h> +#include <linux/major.h> +#include <linux/slab.h> +#ifdef CONFIG_KMOD +#include <linux/kmod.h> +#endif +#include <linux/fcntl.h> +#include <linux/smp_lock.h> +#include <linux/devfs_fs_kernel.h> + +#include <linux/zftape.h> +#include <linux/init.h> +#include <linux/device.h> + +#include "../zftape/zftape-init.h" +#include "../zftape/zftape-read.h" +#include "../zftape/zftape-write.h" +#include "../zftape/zftape-ctl.h" +#include "../zftape/zftape-buffers.h" + +MODULE_AUTHOR("(c) 1996, 1997 Claus-Justus Heine " + "(claus@momo.math.rwth-aachen.de)"); +MODULE_DESCRIPTION(ZFTAPE_VERSION " - " + "VFS interface for the Linux floppy tape driver. " + "Support for QIC-113 compatible volume table " + "and builtin compression (lzrw3 algorithm)"); +MODULE_SUPPORTED_DEVICE("char-major-27"); +MODULE_LICENSE("GPL"); + +/* Global vars. + */ +struct zft_cmpr_ops *zft_cmpr_ops = NULL; +const ftape_info *zft_status; + +/* Local vars. + */ +static unsigned long busy_flag; + +static sigset_t orig_sigmask; + +/* the interface to the kernel vfs layer + */ + +/* Note about llseek(): + * + * st.c and tpqic.c update fp->f_pos but don't implment llseek() and + * initialize the llseek component of the file_ops struct with NULL. + * This means that the user will get the default seek, but the tape + * device will not respect the new position, but happily read from the + * old position. Think a zftape specific llseek() function would be + * better, returning -ESPIPE. TODO. + */ + +static int zft_open (struct inode *ino, struct file *filep); +static int zft_close(struct inode *ino, struct file *filep); +static int zft_ioctl(struct inode *ino, struct file *filep, + unsigned int command, unsigned long arg); +static int zft_mmap(struct file *filep, struct vm_area_struct *vma); +static ssize_t zft_read (struct file *fp, char __user *buff, + size_t req_len, loff_t *ppos); +static ssize_t zft_write(struct file *fp, const char __user *buff, + size_t req_len, loff_t *ppos); + +static struct file_operations zft_cdev = +{ + .owner = THIS_MODULE, + .read = zft_read, + .write = zft_write, + .ioctl = zft_ioctl, + .mmap = zft_mmap, + .open = zft_open, + .release = zft_close, +}; + +static struct class_simple *zft_class; + +/* Open floppy tape device + */ +static int zft_open(struct inode *ino, struct file *filep) +{ + int result; + TRACE_FUN(ft_t_flow); + + nonseekable_open(ino, filep); + TRACE(ft_t_flow, "called for minor %d", iminor(ino)); + if ( test_and_set_bit(0,&busy_flag) ) { + TRACE_ABORT(-EBUSY, ft_t_warn, "failed: already busy"); + } + if ((iminor(ino) & ~(ZFT_MINOR_OP_MASK | FTAPE_NO_REWIND)) + > + FTAPE_SEL_D) { + clear_bit(0,&busy_flag); + TRACE_ABORT(-ENXIO, ft_t_err, "failed: invalid unit nr"); + } + orig_sigmask = current->blocked; + sigfillset(¤t->blocked); + result = _zft_open(iminor(ino), filep->f_flags & O_ACCMODE); + if (result < 0) { + current->blocked = orig_sigmask; /* restore mask */ + clear_bit(0,&busy_flag); + TRACE_ABORT(result, ft_t_err, "_ftape_open failed"); + } else { + /* Mask signals that will disturb proper operation of the + * program that is calling. + */ + current->blocked = orig_sigmask; + sigaddsetmask (¤t->blocked, _DO_BLOCK); + TRACE_EXIT 0; + } +} + +/* Close floppy tape device + */ +static int zft_close(struct inode *ino, struct file *filep) +{ + int result; + TRACE_FUN(ft_t_flow); + + if ( !test_bit(0,&busy_flag) || iminor(ino) != zft_unit) { + TRACE(ft_t_err, "failed: not busy or wrong unit"); + TRACE_EXIT 0; + } + sigfillset(¤t->blocked); + result = _zft_close(); + if (result < 0) { + TRACE(ft_t_err, "_zft_close failed"); + } + current->blocked = orig_sigmask; /* restore before open state */ + clear_bit(0,&busy_flag); + TRACE_EXIT 0; +} + +/* Ioctl for floppy tape device + */ +static int zft_ioctl(struct inode *ino, struct file *filep, + unsigned int command, unsigned long arg) +{ + int result = -EIO; + sigset_t old_sigmask; + TRACE_FUN(ft_t_flow); + + if ( !test_bit(0,&busy_flag) || iminor(ino) != zft_unit || ft_failure) { + TRACE_ABORT(-EIO, ft_t_err, + "failed: not busy, failure or wrong unit"); + } + old_sigmask = current->blocked; /* save mask */ + sigfillset(¤t->blocked); + /* This will work as long as sizeof(void *) == sizeof(long) */ + result = _zft_ioctl(command, (void __user *) arg); + current->blocked = old_sigmask; /* restore mask */ + TRACE_EXIT result; +} + +/* Ioctl for floppy tape device + */ +static int zft_mmap(struct file *filep, struct vm_area_struct *vma) +{ + int result = -EIO; + sigset_t old_sigmask; + TRACE_FUN(ft_t_flow); + + if ( !test_bit(0,&busy_flag) || + iminor(filep->f_dentry->d_inode) != zft_unit || + ft_failure) + { + TRACE_ABORT(-EIO, ft_t_err, + "failed: not busy, failure or wrong unit"); + } + old_sigmask = current->blocked; /* save mask */ + sigfillset(¤t->blocked); + if ((result = ftape_mmap(vma)) >= 0) { +#ifndef MSYNC_BUG_WAS_FIXED + static struct vm_operations_struct dummy = { NULL, }; + vma->vm_ops = &dummy; +#endif + } + current->blocked = old_sigmask; /* restore mask */ + TRACE_EXIT result; +} + +/* Read from floppy tape device + */ +static ssize_t zft_read(struct file *fp, char __user *buff, + size_t req_len, loff_t *ppos) +{ + int result = -EIO; + sigset_t old_sigmask; + struct inode *ino = fp->f_dentry->d_inode; + TRACE_FUN(ft_t_flow); + + TRACE(ft_t_data_flow, "called with count: %ld", (unsigned long)req_len); + if (!test_bit(0,&busy_flag) || iminor(ino) != zft_unit || ft_failure) { + TRACE_ABORT(-EIO, ft_t_err, + "failed: not busy, failure or wrong unit"); + } + old_sigmask = current->blocked; /* save mask */ + sigfillset(¤t->blocked); + result = _zft_read(buff, req_len); + current->blocked = old_sigmask; /* restore mask */ + TRACE(ft_t_data_flow, "return with count: %d", result); + TRACE_EXIT result; +} + +/* Write to tape device + */ +static ssize_t zft_write(struct file *fp, const char __user *buff, + size_t req_len, loff_t *ppos) +{ + int result = -EIO; + sigset_t old_sigmask; + struct inode *ino = fp->f_dentry->d_inode; + TRACE_FUN(ft_t_flow); + + TRACE(ft_t_flow, "called with count: %ld", (unsigned long)req_len); + if (!test_bit(0,&busy_flag) || iminor(ino) != zft_unit || ft_failure) { + TRACE_ABORT(-EIO, ft_t_err, + "failed: not busy, failure or wrong unit"); + } + old_sigmask = current->blocked; /* save mask */ + sigfillset(¤t->blocked); + result = _zft_write(buff, req_len); + current->blocked = old_sigmask; /* restore mask */ + TRACE(ft_t_data_flow, "return with count: %d", result); + TRACE_EXIT result; +} + +/* END OF VFS INTERFACE + * + *****************************************************************************/ + +/* driver/module initialization + */ + +/* the compression module has to call this function to hook into the zftape + * code + */ +int zft_cmpr_register(struct zft_cmpr_ops *new_ops) +{ + TRACE_FUN(ft_t_flow); + + if (zft_cmpr_ops != NULL) { + TRACE_EXIT -EBUSY; + } else { + zft_cmpr_ops = new_ops; + TRACE_EXIT 0; + } +} + +/* lock the zft-compressor() module. + */ +int zft_cmpr_lock(int try_to_load) +{ + if (zft_cmpr_ops == NULL) { +#ifdef CONFIG_KMOD + if (try_to_load) { + request_module("zft-compressor"); + if (zft_cmpr_ops == NULL) { + return -ENOSYS; + } + } else { + return -ENOSYS; + } +#else + return -ENOSYS; +#endif + } + (*zft_cmpr_ops->lock)(); + return 0; +} + +#ifdef CONFIG_ZFT_COMPRESSOR +extern int zft_compressor_init(void); +#endif + +/* Called by modules package when installing the driver or by kernel + * during the initialization phase + */ +int __init zft_init(void) +{ + int i; + TRACE_FUN(ft_t_flow); + +#ifdef MODULE + printk(KERN_INFO ZFTAPE_VERSION "\n"); + if (TRACE_LEVEL >= ft_t_info) { + printk( +KERN_INFO +"(c) 1996, 1997 Claus-Justus Heine (claus@momo.math.rwth-aachen.de)\n" +KERN_INFO +"vfs interface for ftape floppy tape driver.\n" +KERN_INFO +"Support for QIC-113 compatible volume table, dynamic memory allocation\n" +KERN_INFO +"and builtin compression (lzrw3 algorithm).\n"); + } +#else /* !MODULE */ + /* print a short no-nonsense boot message */ + printk(KERN_INFO ZFTAPE_VERSION "\n"); +#endif /* MODULE */ + TRACE(ft_t_info, "zft_init @ 0x%p", zft_init); + TRACE(ft_t_info, + "installing zftape VFS interface for ftape driver ..."); + TRACE_CATCH(register_chrdev(QIC117_TAPE_MAJOR, "zft", &zft_cdev),); + + zft_class = class_simple_create(THIS_MODULE, "zft"); + for (i = 0; i < 4; i++) { + class_simple_device_add(zft_class, MKDEV(QIC117_TAPE_MAJOR, i), NULL, "qft%i", i); + devfs_mk_cdev(MKDEV(QIC117_TAPE_MAJOR, i), + S_IFCHR | S_IRUSR | S_IWUSR, + "qft%i", i); + class_simple_device_add(zft_class, MKDEV(QIC117_TAPE_MAJOR, i + 4), NULL, "nqft%i", i); + devfs_mk_cdev(MKDEV(QIC117_TAPE_MAJOR, i + 4), + S_IFCHR | S_IRUSR | S_IWUSR, + "nqft%i", i); + class_simple_device_add(zft_class, MKDEV(QIC117_TAPE_MAJOR, i + 16), NULL, "zqft%i", i); + devfs_mk_cdev(MKDEV(QIC117_TAPE_MAJOR, i + 16), + S_IFCHR | S_IRUSR | S_IWUSR, + "zqft%i", i); + class_simple_device_add(zft_class, MKDEV(QIC117_TAPE_MAJOR, i + 20), NULL, "nzqft%i", i); + devfs_mk_cdev(MKDEV(QIC117_TAPE_MAJOR, i + 20), + S_IFCHR | S_IRUSR | S_IWUSR, + "nzqft%i", i); + class_simple_device_add(zft_class, MKDEV(QIC117_TAPE_MAJOR, i + 32), NULL, "rawqft%i", i); + devfs_mk_cdev(MKDEV(QIC117_TAPE_MAJOR, i + 32), + S_IFCHR | S_IRUSR | S_IWUSR, + "rawqft%i", i); + class_simple_device_add(zft_class, MKDEV(QIC117_TAPE_MAJOR, i + 36), NULL, "nrawrawqft%i", i); + devfs_mk_cdev(MKDEV(QIC117_TAPE_MAJOR, i + 36), + S_IFCHR | S_IRUSR | S_IWUSR, + "nrawqft%i", i); + } + +#ifdef CONFIG_ZFT_COMPRESSOR + (void)zft_compressor_init(); +#endif + zft_status = ftape_get_status(); /* fetch global data of ftape + * hardware driver + */ + TRACE_EXIT 0; +} + + +/* Called by modules package when removing the driver + */ +static void zft_exit(void) +{ + int i; + TRACE_FUN(ft_t_flow); + + if (unregister_chrdev(QIC117_TAPE_MAJOR, "zft") != 0) { + TRACE(ft_t_warn, "failed"); + } else { + TRACE(ft_t_info, "successful"); + } + for (i = 0; i < 4; i++) { + devfs_remove("qft%i", i); + class_simple_device_remove(MKDEV(QIC117_TAPE_MAJOR, i)); + devfs_remove("nqft%i", i); + class_simple_device_remove(MKDEV(QIC117_TAPE_MAJOR, i + 4)); + devfs_remove("zqft%i", i); + class_simple_device_remove(MKDEV(QIC117_TAPE_MAJOR, i + 16)); + devfs_remove("nzqft%i", i); + class_simple_device_remove(MKDEV(QIC117_TAPE_MAJOR, i + 20)); + devfs_remove("rawqft%i", i); + class_simple_device_remove(MKDEV(QIC117_TAPE_MAJOR, i + 32)); + devfs_remove("nrawqft%i", i); + class_simple_device_remove(MKDEV(QIC117_TAPE_MAJOR, i + 36)); + } + class_simple_destroy(zft_class); + zft_uninit_mem(); /* release remaining memory, if any */ + printk(KERN_INFO "zftape successfully unloaded.\n"); + TRACE_EXIT; +} + +module_init(zft_init); +module_exit(zft_exit); diff --git a/drivers/char/ftape/zftape/zftape-init.h b/drivers/char/ftape/zftape/zftape-init.h new file mode 100644 index 00000000000..937e5d48c20 --- /dev/null +++ b/drivers/char/ftape/zftape/zftape-init.h @@ -0,0 +1,77 @@ +#ifndef _ZFTAPE_INIT_H +#define _ZFTAPE_INIT_H + +/* + * Copyright (C) 1996, 1997 Claus 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-init.h,v $ + * $Revision: 1.2 $ + * $Date: 1997/10/05 19:19:05 $ + * + * This file contains definitions and macro for the vfs + * interface defined by zftape + * + */ + +#include <linux/ftape-header-segment.h> + +#include "../lowlevel/ftape-tracing.h" +#include "../lowlevel/ftape-ctl.h" +#include "../lowlevel/ftape-read.h" +#include "../lowlevel/ftape-write.h" +#include "../lowlevel/ftape-bsm.h" +#include "../lowlevel/ftape-io.h" +#include "../lowlevel/ftape-buffer.h" +#include "../lowlevel/ftape-format.h" + +#include "../zftape/zftape-rw.h" + +#ifdef MODULE +#define ftape_status (*zft_status) +#endif + +extern const ftape_info *zft_status; /* needed for zftape-vtbl.h */ + +#include "../zftape/zftape-vtbl.h" + +struct zft_cmpr_ops { + int (*write)(int *write_cnt, + __u8 *dst_buf, const int seg_sz, + const __u8 __user *src_buf, const int req_len, + const zft_position *pos, const zft_volinfo *volume); + int (*read)(int *read_cnt, + __u8 __user *dst_buf, const int req_len, + const __u8 *src_buf, const int seg_sz, + const zft_position *pos, const zft_volinfo *volume); + int (*seek)(unsigned int new_block_pos, + zft_position *pos, const zft_volinfo *volume, + __u8 *buffer); + void (*lock) (void); + void (*reset) (void); + void (*cleanup)(void); +}; + +extern struct zft_cmpr_ops *zft_cmpr_ops; +/* zftape-init.c defined global functions. + */ +extern int zft_cmpr_register(struct zft_cmpr_ops *new_ops); +extern int zft_cmpr_lock(int try_to_load); + +#endif + + diff --git a/drivers/char/ftape/zftape/zftape-read.c b/drivers/char/ftape/zftape/zftape-read.c new file mode 100644 index 00000000000..214bf03dce6 --- /dev/null +++ b/drivers/char/ftape/zftape/zftape-read.c @@ -0,0 +1,377 @@ +/* + * Copyright (C) 1996, 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-read.c,v $ + * $Revision: 1.2 $ + * $Date: 1997/10/05 19:19:06 $ + * + * This file contains the high level reading code + * for the QIC-117 floppy-tape driver for Linux. + */ + +#include <linux/errno.h> +#include <linux/mm.h> + +#include <linux/zftape.h> + +#include <asm/uaccess.h> + +#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" + +/* Global vars. + */ +int zft_just_before_eof; + +/* Local vars. + */ +static int buf_len_rd; + +void zft_zap_read_buffers(void) +{ + buf_len_rd = 0; +} + +int zft_read_header_segments(void) +{ + TRACE_FUN(ft_t_flow); + + zft_header_read = 0; + TRACE_CATCH(zft_vmalloc_once(&zft_hseg_buf, FT_SEGMENT_SIZE),); + TRACE_CATCH(ftape_read_header_segment(zft_hseg_buf),); + TRACE(ft_t_info, "Segments written since first format: %d", + (int)GET4(zft_hseg_buf, FT_SEG_CNT)); + zft_qic113 = (ft_format_code != fmt_normal && + ft_format_code != fmt_1100ft && + ft_format_code != fmt_425ft); + TRACE(ft_t_info, "ft_first_data_segment: %d, ft_last_data_segment: %d", + ft_first_data_segment, ft_last_data_segment); + zft_capacity = zft_get_capacity(); + zft_old_ftape = zft_ftape_validate_label(&zft_hseg_buf[FT_LABEL]); + if (zft_old_ftape) { + TRACE(ft_t_info, +"Found old ftaped tape, emulating eof marks, entering read-only mode"); + zft_ftape_extract_file_marks(zft_hseg_buf); + TRACE_CATCH(zft_fake_volume_headers(zft_eof_map, + zft_nr_eof_marks),); + } else { + /* the specs say that the volume table must be + * initialized with zeroes during formatting, so it + * MUST be readable, i.e. contain vaid ECC + * information. + */ + TRACE_CATCH(ftape_read_segment(ft_first_data_segment, + zft_deblock_buf, + FT_RD_SINGLE),); + TRACE_CATCH(zft_extract_volume_headers(zft_deblock_buf),); + } + zft_header_read = 1; + zft_set_flags(zft_unit); + zft_reset_position(&zft_pos); + TRACE_EXIT 0; +} + +int zft_fetch_segment_fraction(const unsigned int segment, void *buffer, + const ft_read_mode_t read_mode, + const unsigned int start, + const unsigned int size) +{ + int seg_sz; + TRACE_FUN(ft_t_flow); + + if (segment == zft_deblock_segment) { + TRACE(ft_t_data_flow, + "re-using segment %d already in deblock buffer", + segment); + seg_sz = zft_get_seg_sz(segment); + if (start > seg_sz) { + TRACE_ABORT(-EINVAL, ft_t_bug, + "trying to read beyond end of segment:\n" + KERN_INFO "seg_sz : %d\n" + KERN_INFO "start : %d\n" + KERN_INFO "segment: %d", + seg_sz, start, segment); + } + if ((start + size) > seg_sz) { + TRACE_EXIT seg_sz - start; + } + TRACE_EXIT size; + } + seg_sz = ftape_read_segment_fraction(segment, buffer, read_mode, + start, size); + TRACE(ft_t_data_flow, "segment %d, result %d", segment, seg_sz); + if ((int)seg_sz >= 0 && start == 0 && size == FT_SEGMENT_SIZE) { + /* this implicitly assumes that we are always called with + * buffer == zft_deblock_buf + */ + zft_deblock_segment = segment; + } else { + zft_deblock_segment = -1; + } + TRACE_EXIT seg_sz; +} + +/* + * out: + * + * int *read_cnt: the number of bytes we removed from the + * zft_deblock_buf (result) + * + * int *to_do : the remaining size of the read-request. Is changed. + * + * in: + * + * char *buff : buff is the address of the upper part of the user + * buffer, that hasn't been filled with data yet. + * int buf_pos_read: copy of buf_pos_rd + * int buf_len_read: copy of buf_len_rd + * char *zft_deblock_buf: ftape_zft_deblock_buf + * + * returns the amount of data actually copied to the user-buffer + * + * to_do MUST NOT SHRINK except to indicate an EOT. In this case to_do + * has to be set to 0. We cannot return -ENOSPC, because we return the + * amount of data actually * copied to the user-buffer + */ +static int zft_simple_read (int *read_cnt, + __u8 __user *dst_buf, + const int to_do, + const __u8 *src_buf, + const int seg_sz, + const zft_position *pos, + const zft_volinfo *volume) +{ + TRACE_FUN(ft_t_flow); + + if (seg_sz - pos->seg_byte_pos < to_do) { + *read_cnt = seg_sz - pos->seg_byte_pos; + } else { + *read_cnt = to_do; + } + if (copy_to_user(dst_buf, + src_buf + pos->seg_byte_pos, *read_cnt) != 0) { + TRACE_EXIT -EFAULT; + } + TRACE(ft_t_noise, "nr bytes just read: %d", *read_cnt); + TRACE_EXIT *read_cnt; +} + +/* req_len: gets clipped due to EOT of EOF. + * req_clipped: is a flag indicating whether req_len was clipped or not + * volume: contains information on current volume (blk_sz etc.) + */ +static int check_read_access(int *req_len, + const zft_volinfo **volume, + int *req_clipped, + const zft_position *pos) +{ + static __s64 remaining; + static int eod; + TRACE_FUN(ft_t_flow); + + if (zft_io_state != zft_reading) { + if (zft_offline) { /* offline includes no_tape */ + TRACE_ABORT(-ENXIO, ft_t_warn, + "tape is offline or no cartridge"); + } + if (!ft_formatted) { + TRACE_ABORT(-EACCES, + ft_t_warn, "tape is not formatted"); + } + /* now enter defined state, read header segment if not + * already done and flush write buffers + */ + TRACE_CATCH(zft_def_idle_state(),); + zft_io_state = zft_reading; + if (zft_tape_at_eod(pos)) { + eod = 1; + TRACE_EXIT 1; + } + eod = 0; + *volume = zft_find_volume(pos->seg_pos); + /* get the space left until EOF */ + remaining = zft_check_for_eof(*volume, pos); + buf_len_rd = 0; + TRACE(ft_t_noise, "remaining: " LL_X ", vol_no: %d", + LL(remaining), (*volume)->count); + } else if (zft_tape_at_eod(pos)) { + if (++eod > 2) { + TRACE_EXIT -EIO; /* st.c also returns -EIO */ + } else { + TRACE_EXIT 1; + } + } + if ((*req_len % (*volume)->blk_sz) != 0) { + /* this message is informational only. The user gets the + * proper return value + */ + TRACE_ABORT(-EINVAL, ft_t_info, + "req_len %d not a multiple of block size %d", + *req_len, (*volume)->blk_sz); + } + /* As GNU tar doesn't accept partial read counts when the + * multiple volume flag is set, we make sure to return the + * requested amount of data. Except, of course, at the end of + * the tape or file mark. + */ + remaining -= *req_len; + if (remaining <= 0) { + TRACE(ft_t_noise, + "clipped request from %d to %d.", + *req_len, (int)(*req_len + remaining)); + *req_len += remaining; + *req_clipped = 1; + } else { + *req_clipped = 0; + } + TRACE_EXIT 0; +} + +/* this_segs_size: the current segment's size. + * buff: the USER-SPACE buffer provided by the calling function. + * req_len: how much data should be read at most. + * volume: contains information on current volume (blk_sz etc.) + */ +static int empty_deblock_buf(__u8 __user *usr_buf, const int req_len, + const __u8 *src_buf, const int seg_sz, + zft_position *pos, + const zft_volinfo *volume) +{ + int cnt; + int result = 0; + TRACE_FUN(ft_t_flow); + + TRACE(ft_t_data_flow, "this_segs_size: %d", seg_sz); + if (zft_use_compression && volume->use_compression) { + TRACE_CATCH(zft_cmpr_lock(1 /* try to load */),); + TRACE_CATCH(result= (*zft_cmpr_ops->read)(&cnt, + usr_buf, req_len, + src_buf, seg_sz, + pos, volume),); + } else { + TRACE_CATCH(result= zft_simple_read (&cnt, + usr_buf, req_len, + src_buf, seg_sz, + pos, volume),); + } + pos->volume_pos += result; + pos->tape_pos += cnt; + pos->seg_byte_pos += cnt; + buf_len_rd -= cnt; /* remaining bytes in buffer */ + TRACE(ft_t_data_flow, "buf_len_rd: %d, cnt: %d", buf_len_rd, cnt); + if(pos->seg_byte_pos >= seg_sz) { + pos->seg_pos++; + pos->seg_byte_pos = 0; + } + TRACE(ft_t_data_flow, "bytes moved out of deblock-buffer: %d", cnt); + TRACE_EXIT result; +} + + +/* note: we store the segment id of the segment that is inside the + * deblock buffer. This spares a lot of ftape_read_segment()s when we + * use small block-sizes. The block-size may be 1kb (SECTOR_SIZE). In + * this case a MTFSR 28 maybe still inside the same segment. + */ +int _zft_read(char __user *buff, int req_len) +{ + int req_clipped; + int result = 0; + int bytes_read = 0; + static unsigned int seg_sz = 0; + static const zft_volinfo *volume = NULL; + TRACE_FUN(ft_t_flow); + + zft_resid = req_len; + result = check_read_access(&req_len, &volume, + &req_clipped, &zft_pos); + switch(result) { + case 0: + break; /* nothing special */ + case 1: + TRACE(ft_t_noise, "EOD reached"); + TRACE_EXIT 0; /* EOD */ + default: + TRACE_ABORT(result, ft_t_noise, + "check_read_access() failed with result %d", + result); + TRACE_EXIT result; + } + while (req_len > 0) { + /* Allow escape from this loop on signal ! + */ + FT_SIGNAL_EXIT(_DONT_BLOCK); + /* buf_len_rd == 0 means that we need to read a new + * segment. + */ + if (buf_len_rd == 0) { + while((result = zft_fetch_segment(zft_pos.seg_pos, + zft_deblock_buf, + FT_RD_AHEAD)) == 0) { + zft_pos.seg_pos ++; + zft_pos.seg_byte_pos = 0; + } + if (result < 0) { + zft_resid -= bytes_read; + TRACE_ABORT(result, ft_t_noise, + "zft_fetch_segment(): %d", + result); + } + seg_sz = result; + buf_len_rd = seg_sz - zft_pos.seg_byte_pos; + } + TRACE_CATCH(result = empty_deblock_buf(buff, + req_len, + zft_deblock_buf, + seg_sz, + &zft_pos, + volume), + zft_resid -= bytes_read); + TRACE(ft_t_data_flow, "bytes just read: %d", result); + bytes_read += result; /* what we got so far */ + buff += result; /* index in user-buffer */ + req_len -= result; /* what's left from req_len */ + } /* while (req_len > 0) */ + if (req_clipped) { + TRACE(ft_t_data_flow, + "maybe partial count because of eof mark"); + if (zft_just_before_eof && bytes_read == 0) { + /* req_len was > 0, but user didn't get + * anything the user has read in the eof-mark + */ + zft_move_past_eof(&zft_pos); + ftape_abort_operation(); + } else { + /* don't skip to the next file before the user + * tried to read a second time past EOF Just + * mark that we are at EOF and maybe decrement + * zft_seg_pos to stay in the same volume; + */ + zft_just_before_eof = 1; + zft_position_before_eof(&zft_pos, volume); + TRACE(ft_t_noise, "just before eof"); + } + } + zft_resid -= result; /* for MTSTATUS */ + TRACE_EXIT bytes_read; +} diff --git a/drivers/char/ftape/zftape/zftape-read.h b/drivers/char/ftape/zftape/zftape-read.h new file mode 100644 index 00000000000..42941de0c23 --- /dev/null +++ b/drivers/char/ftape/zftape/zftape-read.h @@ -0,0 +1,53 @@ +#ifndef _ZFTAPE_READ_H +#define _ZFTAPE_READ_H + +/* + * Copyright (C) 1996, 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-read.h,v $ + * $Revision: 1.2 $ + * $Date: 1997/10/05 19:19:07 $ + * + * This file contains the definitions for the read functions + * for the zftape driver for Linux. + * + */ + +#include "../lowlevel/ftape-read.h" + +/* ftape-read.c defined global vars. + */ +extern int zft_just_before_eof; + +/* ftape-read.c defined global functions. + */ +extern void zft_zap_read_buffers(void); +extern int zft_read_header_segments(void); +extern int zft_fetch_segment_fraction(const unsigned int segment, + void *buffer, + const ft_read_mode_t read_mode, + const unsigned int start, + const unsigned int size); +#define zft_fetch_segment(segment, address, read_mode) \ + zft_fetch_segment_fraction(segment, address, read_mode, \ + 0, FT_SEGMENT_SIZE) +/* hook for the VFS interface + */ +extern int _zft_read(char __user *buff, int req_len); + +#endif /* _ZFTAPE_READ_H */ diff --git a/drivers/char/ftape/zftape/zftape-rw.c b/drivers/char/ftape/zftape/zftape-rw.c new file mode 100644 index 00000000000..a61ef50f3df --- /dev/null +++ b/drivers/char/ftape/zftape/zftape-rw.c @@ -0,0 +1,376 @@ +/* + * Copyright (C) 1996, 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-rw.c,v $ + * $Revision: 1.2 $ + * $Date: 1997/10/05 19:19:08 $ + * + * This file contains some common code for the r/w code for + * zftape. + */ + +#include <linux/config.h> /* for CONFIG_ZFT_DFLT_BLK_SZ */ +#include <linux/errno.h> +#include <linux/mm.h> + +#include <linux/zftape.h> +#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" + +/* Global vars. + */ + +__u8 *zft_deblock_buf; +__u8 *zft_hseg_buf; +int zft_deblock_segment = -1; +zft_status_enum zft_io_state = zft_idle; +int zft_header_changed; +int zft_qic113; /* conform to old specs. and old zftape */ +int zft_use_compression; +zft_position zft_pos = { + -1, /* seg_pos */ + 0, /* seg_byte_pos */ + 0, /* tape_pos */ + 0 /* volume_pos */ +}; +unsigned int zft_blk_sz = CONFIG_ZFT_DFLT_BLK_SZ; +__s64 zft_capacity; + +unsigned int zft_written_segments; +int zft_label_changed; + +/* Local vars. + */ + +unsigned int zft_get_seg_sz(unsigned int segment) +{ + int size; + TRACE_FUN(ft_t_any); + + size = FT_SEGMENT_SIZE - + count_ones(ftape_get_bad_sector_entry(segment))*FT_SECTOR_SIZE; + if (size > 0) { + TRACE_EXIT (unsigned)size; + } else { + TRACE_EXIT 0; + } +} + +/* ftape_set_flags(). Claus-Justus Heine, 1994/1995 + */ +void zft_set_flags(unsigned minor_unit) +{ + TRACE_FUN(ft_t_flow); + + zft_use_compression = zft_qic_mode = 0; + switch (minor_unit & ZFT_MINOR_OP_MASK) { + case (ZFT_Q80_MODE | ZFT_ZIP_MODE): + case ZFT_ZIP_MODE: + zft_use_compression = 1; + case 0: + case ZFT_Q80_MODE: + zft_qic_mode = 1; + if (zft_mt_compression) { /* override the default */ + zft_use_compression = 1; + } + break; + case ZFT_RAW_MODE: + TRACE(ft_t_noise, "switching to raw mode"); + break; + default: + TRACE(ft_t_warn, "Warning:\n" + KERN_INFO "Wrong combination of minor device bits.\n" + KERN_INFO "Switching to raw read-only mode."); + zft_write_protected = 1; + break; + } + TRACE_EXIT; +} + +/* computes the segment and byte offset inside the segment + * corresponding to tape_pos. + * + * tape_pos gives the offset in bytes from the beginning of the + * ft_first_data_segment *seg_byte_pos is the offset in the current + * segment in bytes + * + * Of, if this routine was called often one should cache the last data + * pos it was called with, but actually this is only needed in + * ftape_seek_block(), that is, almost never. + */ +int zft_calc_seg_byte_coord(int *seg_byte_pos, __s64 tape_pos) +{ + int segment; + int seg_sz; + TRACE_FUN(ft_t_flow); + + if (tape_pos == 0) { + *seg_byte_pos = 0; + segment = ft_first_data_segment; + } else { + seg_sz = 0; + + for (segment = ft_first_data_segment; + ((tape_pos > 0) && (segment <= ft_last_data_segment)); + segment++) { + seg_sz = zft_get_seg_sz(segment); + tape_pos -= seg_sz; + } + if(tape_pos >= 0) { + /* the case tape_pos > != 0 means that the + * argument tape_pos lies beyond the EOT. + */ + *seg_byte_pos= 0; + } else { /* tape_pos < 0 */ + segment--; + *seg_byte_pos= tape_pos + seg_sz; + } + } + TRACE_EXIT(segment); +} + +/* ftape_calc_tape_pos(). + * + * computes the offset in bytes from the beginning of the + * ft_first_data_segment inverse to ftape_calc_seg_byte_coord + * + * We should do some caching. But how: + * + * Each time the header segments are read in, this routine is called + * with ft_tracks_per_tape*segments_per_track argumnet. So this should be + * the time to reset the cache. + * + * Also, it might be in the future that the bad sector map gets + * changed. -> reset the cache + */ +static int seg_pos; +static __s64 tape_pos; + +__s64 zft_get_capacity(void) +{ + seg_pos = ft_first_data_segment; + tape_pos = 0; + + while (seg_pos <= ft_last_data_segment) { + tape_pos += zft_get_seg_sz(seg_pos ++); + } + return tape_pos; +} + +__s64 zft_calc_tape_pos(int segment) +{ + int d1, d2, d3; + TRACE_FUN(ft_t_any); + + if (segment > ft_last_data_segment) { + TRACE_EXIT zft_capacity; + } + if (segment < ft_first_data_segment) { + TRACE_EXIT 0; + } + d2 = segment - seg_pos; + if (-d2 > 10) { + d1 = segment - ft_first_data_segment; + if (-d2 > d1) { + tape_pos = 0; + seg_pos = ft_first_data_segment; + d2 = d1; + } + } + if (d2 > 10) { + d3 = ft_last_data_segment - segment; + if (d2 > d3) { + tape_pos = zft_capacity; + seg_pos = ft_last_data_segment + 1; + d2 = -d3; + } + } + if (d2 > 0) { + while (seg_pos < segment) { + tape_pos += zft_get_seg_sz(seg_pos++); + } + } else { + while (seg_pos > segment) { + tape_pos -= zft_get_seg_sz(--seg_pos); + } + } + TRACE(ft_t_noise, "new cached pos: %d", seg_pos); + + TRACE_EXIT tape_pos; +} + +/* copy Z-label string to buffer, keeps track of the correct offset in + * `buffer' + */ +void zft_update_label(__u8 *buffer) +{ + TRACE_FUN(ft_t_flow); + + if (strncmp(&buffer[FT_LABEL], ZFTAPE_LABEL, + sizeof(ZFTAPE_LABEL)-1) != 0) { + TRACE(ft_t_info, "updating label from \"%s\" to \"%s\"", + &buffer[FT_LABEL], ZFTAPE_LABEL); + strcpy(&buffer[FT_LABEL], ZFTAPE_LABEL); + memset(&buffer[FT_LABEL] + sizeof(ZFTAPE_LABEL) - 1, ' ', + FT_LABEL_SZ - sizeof(ZFTAPE_LABEL + 1)); + PUT4(buffer, FT_LABEL_DATE, 0); + zft_label_changed = zft_header_changed = 1; /* changed */ + } + TRACE_EXIT; +} + +int zft_verify_write_segments(unsigned int segment, + __u8 *data, size_t size, + __u8 *buffer) +{ + int result; + __u8 *write_buf; + __u8 *src_buf; + int single; + int seg_pos; + int seg_sz; + int remaining; + ft_write_mode_t write_mode; + TRACE_FUN(ft_t_flow); + + seg_pos = segment; + seg_sz = zft_get_seg_sz(seg_pos); + src_buf = data; + single = size <= seg_sz; + remaining = size; + do { + TRACE(ft_t_noise, "\n" + KERN_INFO "remaining: %d\n" + KERN_INFO "seg_sz : %d\n" + KERN_INFO "segment : %d", + remaining, seg_sz, seg_pos); + if (remaining == seg_sz) { + write_buf = src_buf; + write_mode = single ? FT_WR_SINGLE : FT_WR_MULTI; + remaining = 0; + } else if (remaining > seg_sz) { + write_buf = src_buf; + write_mode = FT_WR_ASYNC; /* don't start tape */ + remaining -= seg_sz; + } else { /* remaining < seg_sz */ + write_buf = buffer; + memcpy(write_buf, src_buf, remaining); + memset(&write_buf[remaining],'\0',seg_sz-remaining); + write_mode = single ? FT_WR_SINGLE : FT_WR_MULTI; + remaining = 0; + } + if ((result = ftape_write_segment(seg_pos, + write_buf, + write_mode)) != seg_sz) { + TRACE(ft_t_err, "Error: " + "Couldn't write segment %d", seg_pos); + TRACE_EXIT result < 0 ? result : -EIO; /* bail out */ + } + zft_written_segments ++; + seg_sz = zft_get_seg_sz(++seg_pos); + src_buf += result; + } while (remaining > 0); + if (ftape_get_status()->fti_state == writing) { + TRACE_CATCH(ftape_loop_until_writes_done(),); + TRACE_CATCH(ftape_abort_operation(),); + zft_prevent_flush(); + } + seg_pos = segment; + src_buf = data; + remaining = size; + do { + TRACE_CATCH(result = ftape_read_segment(seg_pos, buffer, + single ? FT_RD_SINGLE + : FT_RD_AHEAD),); + if (memcmp(src_buf, buffer, + remaining > result ? result : remaining) != 0) { + TRACE_ABORT(-EIO, ft_t_err, + "Failed to verify written segment %d", + seg_pos); + } + remaining -= result; + TRACE(ft_t_noise, "verify successful:\n" + KERN_INFO "segment : %d\n" + KERN_INFO "segsize : %d\n" + KERN_INFO "remaining: %d", + seg_pos, result, remaining); + src_buf += seg_sz; + seg_pos++; + } while (remaining > 0); + TRACE_EXIT size; +} + + +/* zft_erase(). implemented compression-handling + * + * calculate the first data-segment when using/not using compression. + * + * update header-segment and compression-map-segment. + */ +int zft_erase(void) +{ + int result = 0; + TRACE_FUN(ft_t_flow); + + if (!zft_header_read) { + TRACE_CATCH(zft_vmalloc_once((void **)&zft_hseg_buf, + FT_SEGMENT_SIZE),); + /* no need to read the vtbl and compression map */ + TRACE_CATCH(ftape_read_header_segment(zft_hseg_buf),); + if ((zft_old_ftape = + zft_ftape_validate_label(&zft_hseg_buf[FT_LABEL]))) { + zft_ftape_extract_file_marks(zft_hseg_buf); + } + TRACE(ft_t_noise, + "ft_first_data_segment: %d, ft_last_data_segment: %d", + ft_first_data_segment, ft_last_data_segment); + zft_qic113 = (ft_format_code != fmt_normal && + ft_format_code != fmt_1100ft && + ft_format_code != fmt_425ft); + } + if (zft_old_ftape) { + zft_clear_ftape_file_marks(); + zft_old_ftape = 0; /* no longer old ftape */ + } + PUT2(zft_hseg_buf, FT_CMAP_START, 0); + zft_volume_table_changed = 1; + zft_capacity = zft_get_capacity(); + zft_init_vtbl(); + /* the rest must be done in ftape_update_header_segments + */ + zft_header_read = 1; + zft_header_changed = 1; /* force update of timestamp */ + result = zft_update_header_segments(); + + ftape_abort_operation(); + + zft_reset_position(&zft_pos); + zft_set_flags (zft_unit); + TRACE_EXIT result; +} + +unsigned int zft_get_time(void) +{ + unsigned int date = FT_TIME_STAMP(2097, 11, 30, 23, 59, 59); /* fun */ + return date; +} diff --git a/drivers/char/ftape/zftape/zftape-rw.h b/drivers/char/ftape/zftape/zftape-rw.h new file mode 100644 index 00000000000..14c07f08657 --- /dev/null +++ b/drivers/char/ftape/zftape/zftape-rw.h @@ -0,0 +1,102 @@ +#ifndef _ZFTAPE_RW_H +#define _ZFTAPE_RW_H + +/* + * Copyright (C) 1996, 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-rw.h,v $ + * $Revision: 1.2 $ + * $Date: 1997/10/05 19:19:09 $ + * + * This file contains the definitions for the read and write + * functions for the QIC-117 floppy-tape driver for Linux. + * + */ + +#include <linux/config.h> /* for CONFIG_ZFT_DFLT_BLK_SZ */ +#include "../zftape/zftape-buffers.h" + +#define SEGMENTS_PER_TAPE (ft_segments_per_track * ft_tracks_per_tape) + +/* QIC-113 Rev. G says that `a maximum of 63488 raw bytes may be + * compressed into a single frame'. + * Maybe we should stick to 32kb to make it more `beautiful' + */ +#define ZFT_MAX_BLK_SZ (62*1024) /* bytes */ +#if !defined(CONFIG_ZFT_DFLT_BLK_SZ) +# define CONFIG_ZFT_DFLT_BLK_SZ (10*1024) /* bytes, default of gnu tar */ +#elif CONFIG_ZFT_DFLT_BLK_SZ == 0 +# undef CONFIG_ZFT_DFLT_BLK_SZ +# define CONFIG_ZFT_DFLT_BLK_SZ 1 +#elif (CONFIG_ZFT_DFLT_BLK_SZ % 1024) != 0 +# error CONFIG_ZFT_DFLT_BLK_SZ must be 1 or a multiple of 1024 +#endif +/* The *optional* compression routines need some overhead per tape + * block for their purposes. Instead of asking the actual compression + * implementation how much it needs, we restrict this overhead to be + * maximal of ZFT_CMPT_OVERHEAD size. We need this for EOT + * conditions. The tape is assumed to be logical at EOT when the + * distance from the physical EOT is less than + * one tape block + ZFT_CMPR_OVERHEAD + */ +#define ZFT_CMPR_OVERHEAD 16 /* bytes */ + +typedef enum +{ + zft_idle = 0, + zft_reading, + zft_writing, +} zft_status_enum; + +typedef struct /* all values measured in bytes */ +{ + int seg_pos; /* segment currently positioned at */ + int seg_byte_pos; /* offset in current segment */ + __s64 tape_pos; /* real offset from BOT */ + __s64 volume_pos; /* pos. in uncompressed data stream in + * current volume + */ +} zft_position; + +extern zft_position zft_pos; +extern __u8 *zft_deblock_buf; +extern __u8 *zft_hseg_buf; +extern int zft_deblock_segment; +extern zft_status_enum zft_io_state; +extern int zft_header_changed; +extern int zft_qic113; /* conform to old specs. and old zftape */ +extern int zft_use_compression; +extern unsigned int zft_blk_sz; +extern __s64 zft_capacity; +extern unsigned int zft_written_segments; +extern int zft_label_changed; + +/* zftape-rw.c exported functions + */ +extern unsigned int zft_get_seg_sz(unsigned int segment); +extern void zft_set_flags(unsigned int minor_unit); +extern int zft_calc_seg_byte_coord(int *seg_byte_pos, __s64 tape_pos); +extern __s64 zft_calc_tape_pos(int segment); +extern __s64 zft_get_capacity(void); +extern void zft_update_label(__u8 *buffer); +extern int zft_erase(void); +extern int zft_verify_write_segments(unsigned int segment, + __u8 *data, size_t size, __u8 *buffer); +extern unsigned int zft_get_time(void); +#endif /* _ZFTAPE_RW_H */ + diff --git a/drivers/char/ftape/zftape/zftape-vtbl.c b/drivers/char/ftape/zftape/zftape-vtbl.c new file mode 100644 index 00000000000..ad7f8be6340 --- /dev/null +++ b/drivers/char/ftape/zftape/zftape-vtbl.c @@ -0,0 +1,757 @@ +/* + * 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 <linux/errno.h> +#include <linux/mm.h> +#include <linux/slab.h> + +#include <linux/zftape.h> +#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; +} diff --git a/drivers/char/ftape/zftape/zftape-vtbl.h b/drivers/char/ftape/zftape/zftape-vtbl.h new file mode 100644 index 00000000000..f31d196d175 --- /dev/null +++ b/drivers/char/ftape/zftape/zftape-vtbl.h @@ -0,0 +1,227 @@ +#ifndef _ZFTAPE_VTBL_H +#define _ZFTAPE_VTBL_H + +/* + * 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.h,v $ + * $Revision: 1.3 $ + * $Date: 1997/10/28 14:30:09 $ + * + * This file defines a volume table as defined in the QIC-80 + * development standards. + */ + +#include <linux/list.h> + +#include "../lowlevel/ftape-tracing.h" + +#include "../zftape/zftape-eof.h" +#include "../zftape/zftape-ctl.h" +#include "../zftape/zftape-rw.h" + +#define VTBL_SIZE 128 /* bytes */ + +/* The following are offsets in the vtbl. */ +#define VTBL_SIG 0 +#define VTBL_START 4 +#define VTBL_END 6 +#define VTBL_DESC 8 +#define VTBL_DATE 52 +#define VTBL_FLAGS 56 +#define VTBL_FL_VENDOR_SPECIFIC (1<<0) +#define VTBL_FL_MUTLI_CARTRIDGE (1<<1) +#define VTBL_FL_NOT_VERIFIED (1<<2) +#define VTBL_FL_REDIR_INHIBIT (1<<3) +#define VTBL_FL_SEG_SPANNING (1<<4) +#define VTBL_FL_DIRECTORY_LAST (1<<5) +#define VTBL_FL_RESERVED_6 (1<<6) +#define VTBL_FL_RESERVED_7 (1<<7) +#define VTBL_M_NO 57 +#define VTBL_EXT 58 +#define EXT_ZFTAPE_SIG 0 +#define EXT_ZFTAPE_BLKSZ 10 +#define EXT_ZFTAPE_CMAP 12 +#define EXT_ZFTAPE_QIC113 13 +#define VTBL_PWD 84 +#define VTBL_DIR_SIZE 92 +#define VTBL_DATA_SIZE 96 +#define VTBL_OS_VERSION 104 +#define VTBL_SRC_DRIVE 106 +#define VTBL_DEV 122 +#define VTBL_RESERVED_1 123 +#define VTBL_CMPR 124 +#define VTBL_CMPR_UNREG 0x3f +#define VTBL_CMPR_USED 0x80 +#define VTBL_FMT 125 +#define VTBL_RESERVED_2 126 +#define VTBL_RESERVED_3 127 +/* compatibility with pre revision K */ +#define VTBL_K_CMPR 120 + +/* the next is used by QIC-3020 tapes with format code 6 (>2^16 + * segments) It is specified in QIC-113, Rev. G, Section 5 (SCSI + * volume table). The difference is simply, that we only store the + * number of segments used, not the starting segment. + */ +#define VTBL_SCSI_SEGS 4 /* is a 4 byte value */ + +/* one vtbl is 128 bytes, that results in a maximum number of + * 29*1024/128 = 232 volumes. + */ +#define ZFT_MAX_VOLUMES (FT_SEGMENT_SIZE/VTBL_SIZE) +#define VTBL_ID "VTBL" +#define VTBL_IDS { VTBL_ID, "XTBL", "UTID", "EXVT" } /* other valid ids */ +#define ZFT_VOL_NAME "zftape volume" /* volume label used by me */ +#define ZFTAPE_SIG "LINUX ZFT" + +/* global variables + */ +typedef struct zft_internal_vtbl +{ + struct list_head node; + int count; + unsigned int start_seg; /* 32 bits are enough for now */ + unsigned int end_seg; /* 32 bits are enough for now */ + __s64 size; /* uncompressed size */ + unsigned int blk_sz; /* block size for this volume */ + unsigned int zft_volume :1; /* zftape created this volume */ + unsigned int use_compression:1; /* compressed volume */ + unsigned int qic113 :1; /* layout of compressed block + * info and vtbl conforms to + * QIC-113, Rev. G + */ + unsigned int new_volume :1; /* it was created by us, this + * run. this allows the + * fields that aren't really + * used by zftape to be filled + * in by some user level + * program. + */ + unsigned int open :1; /* just in progress of being + * written + */ +} zft_volinfo; + +extern struct list_head zft_vtbl; +#define zft_head_vtbl list_entry(zft_vtbl.next, zft_volinfo, node) +#define zft_eom_vtbl list_entry(zft_vtbl.prev, zft_volinfo, node) +#define zft_last_vtbl list_entry(zft_eom_vtbl->node.prev, zft_volinfo, node) +#define zft_first_vtbl list_entry(zft_head_vtbl->node.next, zft_volinfo, node) +#define zft_vtbl_empty (zft_eom_vtbl->node.prev == &zft_head_vtbl->node) + +#define DUMP_VOLINFO(level, desc, info) \ +{ \ + char tmp[21]; \ + strlcpy(tmp, desc, sizeof(tmp)); \ + TRACE(level, "Volume %d:\n" \ + KERN_INFO "description : %s\n" \ + KERN_INFO "first segment: %d\n" \ + KERN_INFO "last segment: %d\n" \ + KERN_INFO "size : " LL_X "\n" \ + KERN_INFO "block size : %d\n" \ + KERN_INFO "compression : %d\n" \ + KERN_INFO "zftape volume: %d\n" \ + KERN_INFO "QIC-113 conf.: %d", \ + (info)->count, tmp, (info)->start_seg, (info)->end_seg, \ + LL((info)->size), (info)->blk_sz, \ + (info)->use_compression != 0, (info)->zft_volume != 0, \ + (info)->qic113 != 0); \ +} + +extern int zft_qic_mode; +extern int zft_old_ftape; +extern int zft_volume_table_changed; + +/* exported functions */ +extern void zft_init_vtbl (void); +extern void zft_free_vtbl (void); +extern int zft_extract_volume_headers(__u8 *buffer); +extern int zft_update_volume_table (unsigned int segment); +extern int zft_open_volume (zft_position *pos, + int blk_sz, int use_compression); +extern int zft_close_volume (zft_position *pos); +extern const zft_volinfo *zft_find_volume(unsigned int seg_pos); +extern int zft_skip_volumes (int count, zft_position *pos); +extern __s64 zft_get_eom_pos (void); +extern void zft_skip_to_eom (zft_position *pos); +extern int zft_fake_volume_headers (eof_mark_union *eof_map, + int num_failed_sectors); +extern int zft_weof (unsigned int count, zft_position *pos); +extern void zft_move_past_eof (zft_position *pos); + +static inline int zft_tape_at_eod (const zft_position *pos); +static inline int zft_tape_at_lbot (const zft_position *pos); +static inline void zft_position_before_eof (zft_position *pos, + const zft_volinfo *volume); +static inline __s64 zft_check_for_eof(const zft_volinfo *vtbl, + const zft_position *pos); + +/* this function decrements the zft_seg_pos counter if we are right + * at the beginning of a segment. This is to handle fsfm/bsfm -- we + * need to position before the eof mark. NOTE: zft_tape_pos is not + * changed + */ +static inline void zft_position_before_eof(zft_position *pos, + const zft_volinfo *volume) +{ + TRACE_FUN(ft_t_flow); + + if (pos->seg_pos == volume->end_seg + 1 && pos->seg_byte_pos == 0) { + pos->seg_pos --; + pos->seg_byte_pos = zft_get_seg_sz(pos->seg_pos); + } + TRACE_EXIT; +} + +/* Mmmh. Is the position at the end of the last volume, that is right + * before the last EOF mark also logical an EOD condition? + */ +static inline int zft_tape_at_eod(const zft_position *pos) +{ + TRACE_FUN(ft_t_any); + + if (zft_qic_mode) { + TRACE_EXIT (pos->seg_pos >= zft_eom_vtbl->start_seg || + zft_last_vtbl->open); + } else { + TRACE_EXIT pos->seg_pos > ft_last_data_segment; + } +} + +static inline int zft_tape_at_lbot(const zft_position *pos) +{ + if (zft_qic_mode) { + return (pos->seg_pos <= zft_first_vtbl->start_seg && + pos->volume_pos == 0); + } else { + return (pos->seg_pos <= ft_first_data_segment && + pos->volume_pos == 0); + } +} + +/* This one checks for EOF. return remaing space (may be negative) + */ +static inline __s64 zft_check_for_eof(const zft_volinfo *vtbl, + const zft_position *pos) +{ + return (__s64)(vtbl->size - pos->volume_pos); +} + +#endif /* _ZFTAPE_VTBL_H */ diff --git a/drivers/char/ftape/zftape/zftape-write.c b/drivers/char/ftape/zftape/zftape-write.c new file mode 100644 index 00000000000..94327b8c97b --- /dev/null +++ b/drivers/char/ftape/zftape/zftape-write.c @@ -0,0 +1,483 @@ +/* + * Copyright (C) 1996, 1997 Claus 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-write.c,v $ + * $Revision: 1.3 $ + * $Date: 1997/11/06 00:50:29 $ + * + * This file contains the writing code + * for the QIC-117 floppy-tape driver for Linux. + */ + +#include <linux/errno.h> +#include <linux/mm.h> + +#include <linux/zftape.h> + +#include <asm/uaccess.h> + +#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" + +/* Global vars. + */ + +/* Local vars. + */ +static int last_write_failed; +static int need_flush; + +void zft_prevent_flush(void) +{ + need_flush = 0; +} + +static int zft_write_header_segments(__u8* buffer) +{ + int header_1_ok = 0; + int header_2_ok = 0; + unsigned int time_stamp; + TRACE_FUN(ft_t_noise); + + TRACE_CATCH(ftape_abort_operation(),); + ftape_seek_to_bot(); /* prevents extra rewind */ + if (GET4(buffer, 0) != FT_HSEG_MAGIC) { + TRACE_ABORT(-EIO, ft_t_err, + "wrong header signature found, aborting"); + } + /* Be optimistic: */ + PUT4(buffer, FT_SEG_CNT, + zft_written_segments + GET4(buffer, FT_SEG_CNT) + 2); + if ((time_stamp = zft_get_time()) != 0) { + PUT4(buffer, FT_WR_DATE, time_stamp); + if (zft_label_changed) { + PUT4(buffer, FT_LABEL_DATE, time_stamp); + } + } + TRACE(ft_t_noise, + "writing first header segment %d", ft_header_segment_1); + header_1_ok = zft_verify_write_segments(ft_header_segment_1, + buffer, FT_SEGMENT_SIZE, + zft_deblock_buf) >= 0; + TRACE(ft_t_noise, + "writing second header segment %d", ft_header_segment_2); + header_2_ok = zft_verify_write_segments(ft_header_segment_2, + buffer, FT_SEGMENT_SIZE, + zft_deblock_buf) >= 0; + if (!header_1_ok) { + TRACE(ft_t_warn, "Warning: " + "update of first header segment failed"); + } + if (!header_2_ok) { + TRACE(ft_t_warn, "Warning: " + "update of second header segment failed"); + } + if (!header_1_ok && !header_2_ok) { + TRACE_ABORT(-EIO, ft_t_err, "Error: " + "update of both header segments failed."); + } + TRACE_EXIT 0; +} + +int zft_update_header_segments(void) +{ + TRACE_FUN(ft_t_noise); + + /* must NOT use zft_write_protected, as it also includes the + * file access mode. But we also want to update when soft + * write protection is enabled (O_RDONLY) + */ + if (ft_write_protected || zft_old_ftape) { + TRACE_ABORT(0, ft_t_noise, "Tape set read-only: no update"); + } + if (!zft_header_read) { + TRACE_ABORT(0, ft_t_noise, "Nothing to update"); + } + if (!zft_header_changed) { + zft_header_changed = zft_written_segments > 0; + } + if (!zft_header_changed && !zft_volume_table_changed) { + TRACE_ABORT(0, ft_t_noise, "Nothing to update"); + } + TRACE(ft_t_noise, "Updating header segments"); + if (ftape_get_status()->fti_state == writing) { + TRACE_CATCH(ftape_loop_until_writes_done(),); + } + TRACE_CATCH(ftape_abort_operation(),); + + zft_deblock_segment = -1; /* invalidate the cache */ + if (zft_header_changed) { + TRACE_CATCH(zft_write_header_segments(zft_hseg_buf),); + } + if (zft_volume_table_changed) { + TRACE_CATCH(zft_update_volume_table(ft_first_data_segment),); + } + zft_header_changed = + zft_volume_table_changed = + zft_label_changed = + zft_written_segments = 0; + TRACE_CATCH(ftape_abort_operation(),); + ftape_seek_to_bot(); + TRACE_EXIT 0; +} + +static int read_merge_buffer(int seg_pos, __u8 *buffer, int offset, int seg_sz) +{ + int result = 0; + const ft_trace_t old_tracing = TRACE_LEVEL; + TRACE_FUN(ft_t_flow); + + if (zft_qic_mode) { + /* writing in the middle of a volume is NOT allowed + * + */ + TRACE(ft_t_noise, "No need to read a segment"); + memset(buffer + offset, 0, seg_sz - offset); + TRACE_EXIT 0; + } + TRACE(ft_t_any, "waiting"); + ftape_start_writing(FT_WR_MULTI); + TRACE_CATCH(ftape_loop_until_writes_done(),); + + TRACE(ft_t_noise, "trying to read segment %d from offset %d", + seg_pos, offset); + SET_TRACE_LEVEL(ft_t_bug); + result = zft_fetch_segment_fraction(seg_pos, buffer, + FT_RD_SINGLE, + offset, seg_sz - offset); + SET_TRACE_LEVEL(old_tracing); + if (result != (seg_sz - offset)) { + TRACE(ft_t_noise, "Ignore error: read_segment() result: %d", + result); + memset(buffer + offset, 0, seg_sz - offset); + } + TRACE_EXIT 0; +} + +/* flush the write buffer to tape and write an eof-marker at the + * current position if not in raw mode. This function always + * positions the tape before the eof-marker. _ftape_close() should + * then advance to the next segment. + * + * the parameter "finish_volume" describes whether to position before + * or after the possibly created file-mark. We always position after + * the file-mark when called from ftape_close() and a flush was needed + * (that is ftape_write() was the last tape operation before calling + * ftape_flush) But we always position before the file-mark when this + * function get's called from outside ftape_close() + */ +int zft_flush_buffers(void) +{ + int result; + int data_remaining; + int this_segs_size; + TRACE_FUN(ft_t_flow); + + TRACE(ft_t_data_flow, + "entered, ftape_state = %d", ftape_get_status()->fti_state); + if (ftape_get_status()->fti_state != writing && !need_flush) { + TRACE_ABORT(0, ft_t_noise, "no need for flush"); + } + zft_io_state = zft_idle; /* triggers some initializations for the + * read and write routines + */ + if (last_write_failed) { + ftape_abort_operation(); + TRACE_EXIT -EIO; + } + TRACE(ft_t_noise, "flushing write buffers"); + this_segs_size = zft_get_seg_sz(zft_pos.seg_pos); + if (this_segs_size == zft_pos.seg_byte_pos) { + zft_pos.seg_pos ++; + data_remaining = zft_pos.seg_byte_pos = 0; + } else { + data_remaining = zft_pos.seg_byte_pos; + } + /* If there is any data not written to tape yet, append zero's + * up to the end of the sector (if using compression) or merge + * it with the data existing on the tape Then write the + * segment(s) to tape. + */ + TRACE(ft_t_noise, "Position:\n" + KERN_INFO "seg_pos : %d\n" + KERN_INFO "byte pos : %d\n" + KERN_INFO "remaining: %d", + zft_pos.seg_pos, zft_pos.seg_byte_pos, data_remaining); + if (data_remaining > 0) { + do { + this_segs_size = zft_get_seg_sz(zft_pos.seg_pos); + if (this_segs_size > data_remaining) { + TRACE_CATCH(read_merge_buffer(zft_pos.seg_pos, + zft_deblock_buf, + data_remaining, + this_segs_size), + last_write_failed = 1); + } + result = ftape_write_segment(zft_pos.seg_pos, + zft_deblock_buf, + FT_WR_MULTI); + if (result != this_segs_size) { + TRACE(ft_t_err, "flush buffers failed"); + zft_pos.tape_pos -= zft_pos.seg_byte_pos; + zft_pos.seg_byte_pos = 0; + + last_write_failed = 1; + TRACE_EXIT result; + } + zft_written_segments ++; + TRACE(ft_t_data_flow, + "flush, moved out buffer: %d", result); + /* need next segment for more data (empty segments?) + */ + if (result < data_remaining) { + if (result > 0) { + /* move remainder to buffer beginning + */ + memmove(zft_deblock_buf, + zft_deblock_buf + result, + FT_SEGMENT_SIZE - result); + } + } + data_remaining -= result; + zft_pos.seg_pos ++; + } while (data_remaining > 0); + TRACE(ft_t_any, "result: %d", result); + zft_deblock_segment = --zft_pos.seg_pos; + if (data_remaining == 0) { /* first byte next segment */ + zft_pos.seg_byte_pos = this_segs_size; + } else { /* after data previous segment, data_remaining < 0 */ + zft_pos.seg_byte_pos = data_remaining + result; + } + } else { + TRACE(ft_t_noise, "zft_deblock_buf empty"); + zft_pos.seg_pos --; + zft_pos.seg_byte_pos = zft_get_seg_sz (zft_pos.seg_pos); + ftape_start_writing(FT_WR_MULTI); + } + TRACE(ft_t_any, "waiting"); + if ((result = ftape_loop_until_writes_done()) < 0) { + /* that's really bad. What to to with zft_tape_pos? + */ + TRACE(ft_t_err, "flush buffers failed"); + } + TRACE(ft_t_any, "zft_seg_pos: %d, zft_seg_byte_pos: %d", + zft_pos.seg_pos, zft_pos.seg_byte_pos); + last_write_failed = + need_flush = 0; + TRACE_EXIT result; +} + +/* return-value: the number of bytes removed from the user-buffer + * + * out: + * int *write_cnt: how much actually has been moved to the + * zft_deblock_buf + * int req_len : MUST NOT BE CHANGED, except at EOT, in + * which case it may be adjusted + * in : + * char *buff : the user buffer + * int buf_pos_write : copy of buf_len_wr int + * this_segs_size : the size in bytes of the actual segment + * char + * *zft_deblock_buf : zft_deblock_buf + */ +static int zft_simple_write(int *cnt, + __u8 *dst_buf, const int seg_sz, + const __u8 __user *src_buf, const int req_len, + const zft_position *pos,const zft_volinfo *volume) +{ + int space_left; + TRACE_FUN(ft_t_flow); + + /* volume->size holds the tape capacity while volume is open */ + if (pos->tape_pos + volume->blk_sz > volume->size) { + TRACE_EXIT -ENOSPC; + } + /* remaining space in this segment, NOT zft_deblock_buf + */ + space_left = seg_sz - pos->seg_byte_pos; + *cnt = req_len < space_left ? req_len : space_left; + if (copy_from_user(dst_buf + pos->seg_byte_pos, src_buf, *cnt) != 0) { + TRACE_EXIT -EFAULT; + } + TRACE_EXIT *cnt; +} + +static int check_write_access(int req_len, + const zft_volinfo **volume, + zft_position *pos, + const unsigned int blk_sz) +{ + int result; + TRACE_FUN(ft_t_flow); + + if ((req_len % zft_blk_sz) != 0) { + TRACE_ABORT(-EINVAL, ft_t_info, + "write-count %d must be multiple of block-size %d", + req_len, blk_sz); + } + if (zft_io_state == zft_writing) { + /* all other error conditions have been checked earlier + */ + TRACE_EXIT 0; + } + zft_io_state = zft_idle; + TRACE_CATCH(zft_check_write_access(pos),); + /* If we haven't read the header segment yet, do it now. + * This will verify the configuration, get the bad sector + * table and read the volume table segment + */ + if (!zft_header_read) { + TRACE_CATCH(zft_read_header_segments(),); + } + /* fine. Now the tape is either at BOT or at EOD, + * Write start of volume now + */ + TRACE_CATCH(zft_open_volume(pos, blk_sz, zft_use_compression),); + *volume = zft_find_volume(pos->seg_pos); + DUMP_VOLINFO(ft_t_noise, "", *volume); + zft_just_before_eof = 0; + /* now merge with old data if necessary */ + if (!zft_qic_mode && pos->seg_byte_pos != 0){ + result = zft_fetch_segment(pos->seg_pos, + zft_deblock_buf, + FT_RD_SINGLE); + if (result < 0) { + if (result == -EINTR || result == -ENOSPC) { + TRACE_EXIT result; + } + TRACE(ft_t_noise, + "ftape_read_segment() result: %d. " + "This might be normal when using " + "a newly\nformatted tape", result); + memset(zft_deblock_buf, '\0', pos->seg_byte_pos); + } + } + zft_io_state = zft_writing; + TRACE_EXIT 0; +} + +static int fill_deblock_buf(__u8 *dst_buf, const int seg_sz, + zft_position *pos, const zft_volinfo *volume, + const char __user *usr_buf, const int req_len) +{ + int cnt = 0; + int result = 0; + TRACE_FUN(ft_t_flow); + + if (seg_sz == 0) { + TRACE_ABORT(0, ft_t_data_flow, "empty segment"); + } + TRACE(ft_t_data_flow, "\n" + KERN_INFO "remaining req_len: %d\n" + KERN_INFO " buf_pos: %d", + req_len, pos->seg_byte_pos); + /* zft_deblock_buf will not contain a valid segment any longer */ + zft_deblock_segment = -1; + if (zft_use_compression) { + TRACE_CATCH(zft_cmpr_lock(1 /* try to load */),); + TRACE_CATCH(result= (*zft_cmpr_ops->write)(&cnt, + dst_buf, seg_sz, + usr_buf, req_len, + pos, volume),); + } else { + TRACE_CATCH(result= zft_simple_write(&cnt, + dst_buf, seg_sz, + usr_buf, req_len, + pos, volume),); + } + pos->volume_pos += result; + pos->seg_byte_pos += cnt; + pos->tape_pos += cnt; + TRACE(ft_t_data_flow, "\n" + KERN_INFO "removed from user-buffer : %d bytes.\n" + KERN_INFO "copied to zft_deblock_buf: %d bytes.\n" + KERN_INFO "zft_tape_pos : " LL_X " bytes.", + result, cnt, LL(pos->tape_pos)); + TRACE_EXIT result; +} + + +/* called by the kernel-interface routine "zft_write()" + */ +int _zft_write(const char __user *buff, int req_len) +{ + int result = 0; + int written = 0; + int write_cnt; + int seg_sz; + static const zft_volinfo *volume = NULL; + TRACE_FUN(ft_t_flow); + + zft_resid = req_len; + last_write_failed = 1; /* reset to 0 when successful */ + /* check if write is allowed + */ + TRACE_CATCH(check_write_access(req_len, &volume,&zft_pos,zft_blk_sz),); + while (req_len > 0) { + /* Allow us to escape from this loop with a signal ! + */ + FT_SIGNAL_EXIT(_DONT_BLOCK); + seg_sz = zft_get_seg_sz(zft_pos.seg_pos); + if ((write_cnt = fill_deblock_buf(zft_deblock_buf, + seg_sz, + &zft_pos, + volume, + buff, + req_len)) < 0) { + zft_resid -= written; + if (write_cnt == -ENOSPC) { + /* leave the remainder to flush_buffers() + */ + TRACE(ft_t_info, "No space left on device"); + last_write_failed = 0; + if (!need_flush) { + need_flush = written > 0; + } + TRACE_EXIT written > 0 ? written : -ENOSPC; + } else { + TRACE_EXIT result; + } + } + if (zft_pos.seg_byte_pos == seg_sz) { + TRACE_CATCH(ftape_write_segment(zft_pos.seg_pos, + zft_deblock_buf, + FT_WR_ASYNC), + zft_resid -= written); + zft_written_segments ++; + zft_pos.seg_byte_pos = 0; + zft_deblock_segment = zft_pos.seg_pos; + ++zft_pos.seg_pos; + } + written += write_cnt; + buff += write_cnt; + req_len -= write_cnt; + } /* while (req_len > 0) */ + TRACE(ft_t_data_flow, "remaining in blocking buffer: %d", + zft_pos.seg_byte_pos); + TRACE(ft_t_data_flow, "just written bytes: %d", written); + last_write_failed = 0; + zft_resid -= written; + need_flush = need_flush || written > 0; + TRACE_EXIT written; /* bytes written */ +} diff --git a/drivers/char/ftape/zftape/zftape-write.h b/drivers/char/ftape/zftape/zftape-write.h new file mode 100644 index 00000000000..ea887015b49 --- /dev/null +++ b/drivers/char/ftape/zftape/zftape-write.h @@ -0,0 +1,38 @@ +#ifndef _ZFTAPE_WRITE_H +#define _ZFTAPE_WRITE_H + +/* + * Copyright (C) 1996, 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-write.h,v $ + * $Revision: 1.2 $ + * $Date: 1997/10/05 19:19:13 $ + * + * This file contains the definitions for the write functions + * for the zftape driver for Linux. + * + */ + +extern int zft_flush_buffers(void); +extern int zft_update_header_segments(void); +extern void zft_prevent_flush(void); + +/* hook for the VFS interface + */ +extern int _zft_write(const char __user *buff, int req_len); +#endif /* _ZFTAPE_WRITE_H */ diff --git a/drivers/char/ftape/zftape/zftape_syms.c b/drivers/char/ftape/zftape/zftape_syms.c new file mode 100644 index 00000000000..2db1401682d --- /dev/null +++ b/drivers/char/ftape/zftape/zftape_syms.c @@ -0,0 +1,43 @@ +/* + * Copyright (C) 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_syms.c,v $ + * $Revision: 1.3 $ + * $Date: 1997/10/05 19:19:14 $ + * + * This file contains the symbols that the zftape frontend to + * the ftape floppy tape driver exports + */ + +#include <linux/module.h> + +#include <linux/zftape.h> + +#include "../zftape/zftape-init.h" +#include "../zftape/zftape-read.h" +#include "../zftape/zftape-buffers.h" +#include "../zftape/zftape-ctl.h" + +/* zftape-init.c */ +EXPORT_SYMBOL(zft_cmpr_register); +/* zftape-read.c */ +EXPORT_SYMBOL(zft_fetch_segment_fraction); +/* zftape-buffers.c */ +EXPORT_SYMBOL(zft_vmalloc_once); +EXPORT_SYMBOL(zft_vmalloc_always); +EXPORT_SYMBOL(zft_vfree); |