/* 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; }