zftape-read.c 11.0 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 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;
}