|
@@ -1,1417 +0,0 @@
|
|
|
-/*
|
|
|
- * 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/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;
|
|
|
-}
|