Beispiel #1
0
/*      IOCTL routine called by kernel-interface code
 */
int _ftape_ioctl(unsigned int command, void *arg)
{
	TRACE_FUN(8, "ftape_ioctl");
	int result = EINVAL;
	union {
		struct mtop mtop;
		struct mtget mtget;
	} krnl_arg;
	int arg_size = (command & IOCSIZE_MASK) >> IOCSIZE_SHIFT;

	/* This check will only catch arguments that are too large !
	 */
	if ((command & IOC_INOUT) && arg_size > sizeof(krnl_arg)) {
		TRACEi(1, "bad argument size:", arg_size);
		TRACE_EXIT;
		return -EINVAL;
	}
	if (command & IOC_IN) {
		int error = verify_area(VERIFY_READ, arg, arg_size);
		if (error) {
			TRACE_EXIT;
			return error;
		}
		memcpy_fromfs(&krnl_arg.mtop, arg, arg_size);
	}
	TRACEx1(5, "called with ioctl command: 0x%08x", command);
	switch (command) {
		/* cpio compatibility
		 * mtrasx and mtreset are mt extension by Hennus Bergman
		 * mtseek and mttell are mt extension by eddy olk
		 */
	case MTIOCTOP:
		TRACEx1(5, "calling MTIOCTOP command: 0x%08x", krnl_arg.mtop.mt_op);
		switch (krnl_arg.mtop.mt_op) {
		case MTNOP:
			/* gnu mt calls MTNOP before MTIOCGET to set status */
			result = 0;
			break;
		case MTRESET:
			result = ftape_reset_drive();
			init_drive_needed = 1;
			if (result < 0 || ftape_offline) {
				break;
			}
			result = ftape_seek_to_bot();
			ftape_reset_position();
			break;
		case MTREW:
		case MTOFFL:
			if (ftape_offline) {
				result = -EIO;
				break;
			}
			ftape_flush_buffers();
			ftape_update_header_segments(NULL, 1);
			result = ftape_seek_to_bot();
			ftape_reset_position();
			if (krnl_arg.mtop.mt_op == MTOFFL) {
				going_offline = 1;
				TRACE(4, "Putting tape drive offline");
			}
			result = 0;
			break;
		case MTRETEN:
			if (ftape_offline) {
				result = -EIO;
				break;
			}
			result = ftape_seek_to_eot();
			if (result >= 0) {
				result = ftape_seek_to_bot();
			}
			ftape_reset_position();
			break;
		case MTERASE:
			if (ftape_offline) {
				result = -EIO;
				break;
			}
			result = ftape_erase();
			break;
		case MTEOM:
			if (ftape_offline) {
				result = -EIO;
				break;
			}
			result = ftape_seek_eom();
			break;
		case MTFSFM:
			if (ftape_offline) {
				result = -EIO;
				break;
			}
			eof_mark = 1;	/* position ready to extend */
		case MTFSF:
			if (ftape_offline) {
				result = -EIO;
				break;
			}
			result = ftape_seek_eof(krnl_arg.mtop.mt_count);
			break;
		case MTBSFM:
			if (ftape_offline) {
				result = -EIO;
				break;
			}
			eof_mark = 1;	/* position ready to extend */
		case MTBSF:
			if (ftape_offline) {
				result = -EIO;
				break;
			}
			result = ftape_seek_eof(-krnl_arg.mtop.mt_count);
			break;
		case MTFSR:
			if (ftape_offline) {
				result = -EIO;
				break;
			}
			tracing = krnl_arg.mtop.mt_count;
			TRACEx1(2, "tracing set to %d", tracing);
			result = 0;
			break;
		case MTBSR:
			if (ftape_offline) {
				result = -EIO;
				break;
			}
#if 0
			result = ftape_fix();
#else
			result = 0;
#endif
			break;
		case MTWEOF:
			if (ftape_offline) {
				result = -EIO;
				break;
			}
			result = ftape_weof(krnl_arg.mtop.mt_count, ftape_seg_pos, 1);
			if (result >= 0) {
				ftape_seg_pos += krnl_arg.mtop.mt_count - 1;
			}
			break;
			/* MTRASx and MTRESET are mt extension by Hennus Bergman
			 */
		case MTRAS1:
		case MTRAS2:
		case MTRAS3:
		case MTSEEK:
		case MTTELL:
		default:
			TRACEi(1, "MTIOCTOP sub-command not implemented:", krnl_arg.mtop.mt_op);
			result = -EIO;
			break;
		}
		break;
	case MTIOCGET:
		krnl_arg.mtget.mt_type = drive_type.vendor_id + 0x800000;
		krnl_arg.mtget.mt_resid = 0;	/* not implemented */
		krnl_arg.mtget.mt_dsreg = 0;	/* status register */
		krnl_arg.mtget.mt_gstat =	/* device independent status */
		    ((ftape_offline) ? 0 : GMT_ONLINE(-1L)) |
		    ((write_protected) ? GMT_WR_PROT(-1L) : 0) |
		    ((no_tape) ? GMT_DR_OPEN(-1L) : 0);
		krnl_arg.mtget.mt_erreg = ftape_last_error;	/* error register */
		result = ftape_file_no(&krnl_arg.mtget.mt_fileno,
				       &krnl_arg.mtget.mt_blkno);
		break;
	case MTIOCPOS:
		TRACE(5, "Mag tape ioctl command: MTIOCPOS");
		TRACE(1, "MTIOCPOS command not implemented");
		break;
	default:
		result = -EINVAL;
		break;
	}
	if (command & IOC_OUT) {
		int error = verify_area(VERIFY_WRITE, arg, arg_size);
		if (error) {
			TRACE_EXIT;
			return error;
		}
		memcpy_tofs(arg, &krnl_arg, arg_size);
	}
	TRACE_EXIT;
	return result;
}
int ftape_flush_buffers(void)
{
    TRACE_FUN(5, "ftape_flush_buffers");
    int result;
    int pad_count;
    int data_remaining;
    static int active = 0;

    if (active) {
        TRACE(5, "nested call, abort");
        TRACE_EXIT;
        return 0;
    }
    active = 1;
    TRACEi(5, "entered, ftape_state =", ftape_state);
    if (ftape_state != writing && !need_flush) {
        active = 0;
        TRACE(5, "no need for flush");
        TRACE_EXIT;
        return 0;
    }
    data_remaining = buf_pos_wr;
    buf_pos_wr = 0;		/* prevent further writes if this fails */
    TRACE(5, "flushing write buffers");
    if (last_write_failed) {
        ftape_zap_write_buffers();
        active = 0;
        TRACE_EXIT;
        return write_protected ? -EROFS : -EIO;
    }
    /*
     *    If there is any data not written to tape yet, append zero's
     *    up to the end of the sector. Then write the segment(s) to tape.
     */
    if (data_remaining > 0) {
        int written;

        do {
            TRACEi(4, "remaining in buffer:", data_remaining);
            pad_count = sizeof(deblock_buffer) - data_remaining;
            TRACEi(7, "flush, padding count:", pad_count);
            memset(deblock_buffer + data_remaining, 0, pad_count);	/* pad buffer */
            result = _write_segment(ftape_seg_pos, deblock_buffer, 1);
            if (result < 0) {
                if (result != -ENOSPC) {
                    last_write_failed = 1;
                }
                active = 0;
                TRACE_EXIT;
                return result;
            }
            written = result;
            clear_eof_mark_if_set(ftape_seg_pos, written);
            TRACEi(7, "flush, moved out buffer:", written);
            if (written > 0) {
                data_remaining -= written;
                if (data_remaining > 0) {
                    /*  Need another segment for remaining data, move the remainder
                     *  to the beginning of the buffer
                     */
                    memmove(deblock_buffer, deblock_buffer + written, data_remaining);
                }
            }
            ++ftape_seg_pos;
        } while (data_remaining > 0);
        /*  Data written to last segment == data_remaining + written
         *  value is in range [1..29K].
         */
        TRACEx2(4, "last write: %d, netto pad-count: %d",
                data_remaining + written, -data_remaining);
        if (-1024 < data_remaining && data_remaining <= 0) {
            /*  Last sector of segment was used for data, so put eof mark
             *  in next segment and position at second file mark.
             */
            if (ftape_weof(2, ftape_seg_pos, 1) >= 0) {
                ++ftape_seg_pos;	/* position between file marks */
            }
        } else {
            /*  Put eof mark in previous segment after data and position
             *  at second file mark.
             */
            ftape_weof(2, ftape_seg_pos - 1, 1 +
                       ((SECTOR_SIZE - 1 + result + data_remaining) / SECTOR_SIZE));
        }
    } else {
        TRACE(7, "deblock_buffer empty");
        if (ftape_weof(2, ftape_seg_pos, 1) >= 0) {
            ++ftape_seg_pos;	/* position between file marks */
        }
        start_writing(WRITE_MULTI);
    }
    TRACE(7, "waiting");
    result = loop_until_writes_done();
    if (result < 0) {
        TRACE(1, "flush buffers failed");
    }
    ftape_state = idle;
    last_write_failed = 0;
    need_flush = 0;
    active = 0;
    TRACE_EXIT;
    return result;
}