/* * Medium sense for 34xx tapes. There is no 'real' medium sense call. * So we just do a normal sense. */ static void __tape_34xx_medium_sense(struct tape_request *request) { struct tape_device *device = request->device; unsigned char *sense; if (request->rc == 0) { sense = request->cpdata; /* * This isn't quite correct. But since INTERVENTION_REQUIRED * means that the drive is 'neither ready nor on-line' it is * only slightly inaccurate to say there is no tape loaded if * the drive isn't online... */ if (sense[0] & SENSE_INTERVENTION_REQUIRED) tape_med_state_set(device, MS_UNLOADED); else tape_med_state_set(device, MS_LOADED); if (sense[1] & SENSE_WRITE_PROTECT) device->tape_generic_status |= GMT_WR_PROT(~0); else device->tape_generic_status &= ~GMT_WR_PROT(~0); } else DBF_EVENT(4, "tape_34xx: medium sense failed with rc=%d\n", request->rc); tape_free_request(request); }
/****************************************************************** * TAPE_GetMediaParams */ static NTSTATUS TAPE_GetMediaParams( int fd, TAPE_GET_MEDIA_PARAMETERS *data ) { #ifdef HAVE_SYS_MTIO_H struct mtget get; NTSTATUS status; TRACE( "fd: %d\n", fd ); memset( data, 0, sizeof(TAPE_GET_MEDIA_PARAMETERS) ); status = TAPE_GetStatus( ioctl( fd, MTIOCGET, &get ) ); if (status != STATUS_SUCCESS) return status; data->Capacity.QuadPart = 1024 * 1024 * 1024; data->Remaining.QuadPart = 1024 * 1024 * 1024; #ifdef HAVE_STRUCT_MTGET_MT_BLKSIZ data->BlockSize = get.mt_blksiz; #else data->BlockSize = get.mt_dsreg & MT_ST_BLKSIZE_MASK; #endif data->PartitionCount = 1; #ifdef HAVE_STRUCT_MTGET_MT_GSTAT data->WriteProtected = (GMT_WR_PROT(get.mt_gstat) != 0); #else data->WriteProtected = 0; #endif return status; #else FIXME( "Not implemented.\n" ); return STATUS_NOT_SUPPORTED; #endif }
/* * Medium sense for 34xx tapes. There is no 'real' medium sense call. * So we just do a normal sense. */ static int tape_34xx_medium_sense(struct tape_device *device) { struct tape_request *request; unsigned char *sense; int rc; request = tape_alloc_request(1, 32); if (IS_ERR(request)) { DBF_EXCEPTION(6, "MSEN fail\n"); return PTR_ERR(request); } request->op = TO_MSEN; tape_ccw_end(request->cpaddr, SENSE, 32, request->cpdata); rc = tape_do_io_interruptible(device, request); if (request->rc == 0) { sense = request->cpdata; /* * This isn't quite correct. But since INTERVENTION_REQUIRED * means that the drive is 'neither ready nor on-line' it is * only slightly inaccurate to say there is no tape loaded if * the drive isn't online... */ if (sense[0] & SENSE_INTERVENTION_REQUIRED) tape_med_state_set(device, MS_UNLOADED); else tape_med_state_set(device, MS_LOADED); if (sense[1] & SENSE_WRITE_PROTECT) device->tape_generic_status |= GMT_WR_PROT(~0); else device->tape_generic_status &= ~GMT_WR_PROT(~0); } else { DBF_EVENT(4, "tape_34xx: medium sense failed with rc=%d\n", request->rc); } tape_free_request(request); return rc; }
static int w32_internal_rc ( U32* pStat ) { ASSERT( pStat ); // (sanity check) // PROGRAMMING NOTE: the 'door open' (no tape in drive) and the // 'write protected' statuses are "sticky" in that they never change // until a new/different tape is mounted. All the other statuses // however, change dynamically as one does i/o to the tape... if (0 || ERROR_BUS_RESET == errno // (See KB 111837: "ERROR_BUS_RESET May Be Benign") || ERROR_MEDIA_CHANGED == errno || ERROR_DEVICE_NOT_CONNECTED == errno // (shouldn't occur but we'll check anyway) || ERROR_DEV_NOT_EXIST == errno // (shouldn't occur but we'll check anyway) || ERROR_FILE_NOT_FOUND == errno // (shouldn't occur but we'll check anyway) ) { *pStat &= ~GMT_DR_OPEN (0xFFFFFFFF); *pStat &= ~GMT_WR_PROT (0xFFFFFFFF); } // (see PROGRAMMING NOTE above) *pStat &= ~GMT_BOT (0xFFFFFFFF); *pStat &= ~GMT_SM (0xFFFFFFFF); *pStat &= ~GMT_EOF (0xFFFFFFFF); *pStat &= ~GMT_EOT (0xFFFFFFFF); *pStat &= ~GMT_EOD (0xFFFFFFFF); if (0 || ERROR_BUS_RESET == errno // (spurious error; retry) || ERROR_MEDIA_CHANGED == errno // (spurious error; retry) // || ERROR_DEVICE_NOT_CONNECTED == errno // (PERM ERROR! NO RETRY!) // || ERROR_DEV_NOT_EXIST == errno // (PERM ERROR! NO RETRY!) // || ERROR_FILE_NOT_FOUND == errno // (PERM ERROR! NO RETRY!) ) { return EINTR; // (Interrupted system call; Retry) } // (see PROGRAMMING NOTE further above) switch (errno) { default: break; // (leave errno set to whatever it already is) case NO_ERROR: errno = 0; break; // (normal expected i/o result) case ERROR_BEGINNING_OF_MEDIA: *pStat |= GMT_BOT (0xFFFFFFFF); errno = EIO; break; case ERROR_END_OF_MEDIA: *pStat |= GMT_EOT (0xFFFFFFFF); errno = ENOSPC; break; // "ERROR_END_OF_MEDIA" // // Msg: "The physical end of the tape has been reached." // // The EOT warning reflector has been reached or passed (i.e. you're // now/still in the "EOT Warning Zone" area). Writing additional data // and/or tapemarks may still be possible depending on the size of the // EOT Warning Zone (as set by a SetTapeParameters call with a non-zero // EOTWarningZoneSize value (if supported; see further below)) and // how much data you've already written to the EOT Warning Zone area // (i.e. once you're in the warning area, this "error" occurs after // EACH and EVERY I/O [in the warning zone area] until the ABSOLUTE // physical end-of-tape (ERROR_EOM_OVERFLOW) is reached; see below). // // // *********************** // ** IMPORTANT NOTE! ** // *********************** // // This is NOT actually an "error"!!! // // // When this "error" occurs, your "ReadFile" and/or "WriteFile" call // returns 'FALSE' even though ALL of your requested data was actually // written successfully!! This can be verified by checking to ensure // the returned "number of bytes written" actually matches the amount // you asked to be written. If they're the same (and they ALWAYS will // be for this specific "error" code), then it means this "error" is // NOT actually an error at all, but rather just a WARNING instead!! // (Had it been an actual i/o error, the error code would have been // some other DIFFERENT error code value instead!!) // // // *********************** // ** ALSO IMPORTANT! ** // *********************** // See also: // // http://fixunix.com/storage/205622-bug-dlttape-sys-no-eot-warning.html // // for ADDITIONAL IMPORTANT INFORMATION regarding always having to // specifically request that this "error" code be returned to you: // // Even when a drive reports it does not support the setting of the // the 'EOTWarningZoneSize' value (i.e. the FeaturesLow field of the // GetTapeParameters call returns '0' for TAPE_DRIVE_SET_EOT_WZ_SIZE // field), it may still be possible for "ERROR_END_OF_MEDIA" warnings // to be generated anyway by simply calling SetTapeParameters with a // non-zero 'EOTWarningZoneSize' value anyway. // // The reason for this is because some drives may not allow CHANGING // the value (thus the reason for it reporting that setting the value // is not supported), but may nevertheless still support the ENABLING // of their own hard-coded internal value. That is to say, while the // size of the warning zone may not be modifiable (as it may be hard- // coded and thus unchangeable), the drive may still have the ability // to REPORT reaching the EOT Warning zone IF SPECIFICALLY REQUESTED // TO DO SO! (which is presumably what requesting a non-zero Warning // Zone size would end up doing: i.e. even though such calls APPEAR // to fail, they actually DO succeed in accomplishing SOMETHING, just // not what you originally/specifically requested). // // Thus calling SetTapeParameters with a non-zero 'EOTWarningZoneSize' // value might very well succeed anyway even though GetTapeParameters // reports that doing so is not supported, and by so doing, may cause // the drive to begin reporting of "ERROR_END_OF_MEDIA" (whereas not // attempting to do so would end up leaving the drive in its default // non-reporting mode. That is to say, you should ALWAYS try setting // a non-zero 'EOTWarningZoneSize' value, ignoring any "unsupported" // error code that may be returned from such a call.) case ERROR_EOM_OVERFLOW: *pStat |= GMT_EOT (0xFFFFFFFF); errno = EIO; break; // "ERROR_EOM_OVERFLOW" // // Msg: "Physical end of tape encountered." // // This error code means that the actual physical end-of-media has been // reached, and no more data can be written to the tape. This includes // tapemarks as well. // // *********************** // ** IMPORTANT NOTE! ** // *********************** // // This is a HARD (UNRECOVERABLE) error!! // // To be programmatically informed of when you are coming close to the // physical end-of-the-tape (such that you could be assured room still // remained to write logical end-of-volume labels for example), simply // call SetTapeParameters with a non-zero 'EOTWarningZoneSize' value // and treat any "ERROR_END_OF_MEDIA" "errors" received when writing // as warnings instead. (See prior discussion of "ERROR_END_OF_MEDIA" // return code further above) case ERROR_NO_DATA_DETECTED: *pStat |= GMT_EOD (0xFFFFFFFF); errno = EIO; break; case ERROR_FILEMARK_DETECTED: *pStat |= GMT_EOF (0xFFFFFFFF); errno = EIO; break; case ERROR_SETMARK_DETECTED: *pStat |= GMT_SM (0xFFFFFFFF); errno = EIO; break; case ERROR_NOT_READY: *pStat |= GMT_DR_OPEN (0xFFFFFFFF); errno = ENOMEDIUM; break; case ERROR_NO_MEDIA_IN_DRIVE: *pStat |= GMT_DR_OPEN (0xFFFFFFFF); errno = ENOMEDIUM; break; case ERROR_WRITE_PROTECT: *pStat |= GMT_WR_PROT (0xFFFFFFFF); errno = EROFS; break; } return errno; }
static int w32_internal_mtget ( HANDLE hFile, U32* pStat, struct mtget* mtget, ifd_t ifd ) { TAPE_GET_MEDIA_PARAMETERS media_parms; DWORD dwRetCode, dwSize, dwLogicalPosition; ASSERT( pStat && mtget ); mtget->mt_resid = 0; // (unknown/unsupported) mtget->mt_erreg = 0; // (unknown/unsupported) mtget->mt_fileno = -1; // (unknown/unsupported) mtget->mt_blkno = -1; // (unknown as of yet; set further below) mtget->mt_type = MT_ISSCSI2; // "Generic ANSI SCSI-2 tape unit" mtget->mt_gstat = -1; // (purposely invalid; set correctly below) // Reset the mounted status; it will get set further below... *pStat &= ~GMT_DR_OPEN (0xFFFFFFFF); // Attempt to retrieve the status of the tape-drive... dwRetCode = w32_get_tape_status( hFile ); // Windows returns 'ERROR_NOT_READY' if no tape is mounted // instead of the usual expected 'ERROR_NO_MEDIA_IN_DRIVE' if ( ERROR_NOT_READY == dwRetCode ) dwRetCode = ERROR_NO_MEDIA_IN_DRIVE; // If there is not tape mounted OR a new tape was mounted, // then the following status bits are now unknown/obsolete if (0 || ERROR_NO_MEDIA_IN_DRIVE == dwRetCode || ERROR_MEDIA_CHANGED == dwRetCode ) { // (these statuse are now obsolete) *pStat &= ~GMT_WR_PROT (0xFFFFFFFF); *pStat &= ~GMT_BOT (0xFFFFFFFF); *pStat &= ~GMT_EOT (0xFFFFFFFF); *pStat &= ~GMT_EOD (0xFFFFFFFF); *pStat &= ~GMT_EOF (0xFFFFFFFF); *pStat &= ~GMT_SM (0xFFFFFFFF); } // There's no sense trying to get media parameters // unless there's some media loaded on the drive! if ( ERROR_NO_MEDIA_IN_DRIVE == dwRetCode ) { *pStat |= GMT_DR_OPEN (0xFFFFFFFF); // (no tape mounted in drive) mtget->mt_gstat = *pStat; // (return current status) return 0; // (nothing more we can do) } // A tape appears to be mounted on the drive... // Retrieve the media parameters information... dwSize = sizeof(media_parms); memset( &media_parms, 0, dwSize ); dwRetCode = GetTapeParameters( hFile, GET_TAPE_MEDIA_INFORMATION, &dwSize, &media_parms ); ASSERT( sizeof(media_parms) == dwSize ); if ( NO_ERROR == dwRetCode ) { mtget->mt_dsreg = media_parms.BlockSize; if (media_parms.WriteProtected) *pStat |= GMT_WR_PROT (0xFFFFFFFF); else *pStat &= ~GMT_WR_PROT (0xFFFFFFFF); } else mtget->mt_dsreg = 0; // (unknown; variable blocks presumed) // Lastly, attempt to determine if we are at BOT (i.e. load-point)... if ( 0 != ( errno = w32_internal_mtpos( hFile, pStat, &dwLogicalPosition, NULL, ifd ) ) ) { mtget->mt_gstat = *pStat; return -1; } mtget->mt_blkno = dwLogicalPosition; if ( ( dwLogicalPosition & g_BOTmsk[ ifd ] ) == g_BOTbot[ ifd ] ) *pStat |= GMT_BOT (0xFFFFFFFF); else *pStat &= ~GMT_BOT (0xFFFFFFFF); mtget->mt_gstat = *pStat; return 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; }
/* * Return the status of the device. This was meant * to be a generic routine. Unfortunately, it doesn't * seem possible (at least I do not know how to do it * currently), which means that for the moment, this * routine has very little value. * * Returns: status */ uint32_t status_dev(DEVICE *dev) { struct mtget mt_stat; uint32_t stat = 0; if (dev->state & (ST_EOT | ST_WEOT)) { stat |= BMT_EOD; Pmsg0(-20, " EOD"); } if (dev->state & ST_EOF) { stat |= BMT_EOF; Pmsg0(-20, " EOF"); } if (dev->is_tape()) { stat |= BMT_TAPE; Pmsg0(-20,_(" Bacula status:")); Pmsg2(-20,_(" file=%d block=%d\n"), dev->file, dev->block_num); if (dev->d_ioctl(dev->fd(), MTIOCGET, (char *)&mt_stat) < 0) { berrno be; dev->dev_errno = errno; Mmsg2(dev->errmsg, _("ioctl MTIOCGET error on %s. ERR=%s.\n"), dev->print_name(), be.bstrerror()); return 0; } Pmsg0(-20, _(" Device status:")); #if defined(HAVE_LINUX_OS) if (GMT_EOF(mt_stat.mt_gstat)) { stat |= BMT_EOF; Pmsg0(-20, " EOF"); } if (GMT_BOT(mt_stat.mt_gstat)) { stat |= BMT_BOT; Pmsg0(-20, " BOT"); } if (GMT_EOT(mt_stat.mt_gstat)) { stat |= BMT_EOT; Pmsg0(-20, " EOT"); } if (GMT_SM(mt_stat.mt_gstat)) { stat |= BMT_SM; Pmsg0(-20, " SM"); } if (GMT_EOD(mt_stat.mt_gstat)) { stat |= BMT_EOD; Pmsg0(-20, " EOD"); } if (GMT_WR_PROT(mt_stat.mt_gstat)) { stat |= BMT_WR_PROT; Pmsg0(-20, " WR_PROT"); } if (GMT_ONLINE(mt_stat.mt_gstat)) { stat |= BMT_ONLINE; Pmsg0(-20, " ONLINE"); } if (GMT_DR_OPEN(mt_stat.mt_gstat)) { stat |= BMT_DR_OPEN; Pmsg0(-20, " DR_OPEN"); } if (GMT_IM_REP_EN(mt_stat.mt_gstat)) { stat |= BMT_IM_REP_EN; Pmsg0(-20, " IM_REP_EN"); } #elif defined(HAVE_WIN32) if (GMT_EOF(mt_stat.mt_gstat)) { stat |= BMT_EOF; Pmsg0(-20, " EOF"); } if (GMT_BOT(mt_stat.mt_gstat)) { stat |= BMT_BOT; Pmsg0(-20, " BOT"); } if (GMT_EOT(mt_stat.mt_gstat)) { stat |= BMT_EOT; Pmsg0(-20, " EOT"); } if (GMT_EOD(mt_stat.mt_gstat)) { stat |= BMT_EOD; Pmsg0(-20, " EOD"); } if (GMT_WR_PROT(mt_stat.mt_gstat)) { stat |= BMT_WR_PROT; Pmsg0(-20, " WR_PROT"); } if (GMT_ONLINE(mt_stat.mt_gstat)) { stat |= BMT_ONLINE; Pmsg0(-20, " ONLINE"); } if (GMT_DR_OPEN(mt_stat.mt_gstat)) { stat |= BMT_DR_OPEN; Pmsg0(-20, " DR_OPEN"); } if (GMT_IM_REP_EN(mt_stat.mt_gstat)) { stat |= BMT_IM_REP_EN; Pmsg0(-20, " IM_REP_EN"); } #endif /* !SunOS && !OSF */ if (dev->has_cap(CAP_MTIOCGET)) { Pmsg2(-20, _(" file=%d block=%d\n"), mt_stat.mt_fileno, mt_stat.mt_blkno); } else { Pmsg2(-20, _(" file=%d block=%d\n"), -1, -1); } } else { stat |= BMT_ONLINE | BMT_BOT; } return stat; }
int main(int argc, char ** argv) { static char * buffer[32]; static char buffer_size[16]; const char * device = DEFAULT_DEVICE; ssize_t size = DEFAULT_SIZE; bool no_rewind = false; bool rewind = false; ssize_t max_buffer_size = MAX_BUFFER_SIZE; ssize_t min_buffer_size = MIN_BUFFER_SIZE; ssize_t tmp_size = 0; enum { OPT_DEVICE = 'd', OPT_HELP = 'h', OPT_MAX_BUFFER = 'M', OPT_MIN_BUFFER = 'm', OPT_NO_REWIND = 'r', OPT_REWIND = 'R', OPT_SIZE = 's', OPT_VERSION = 'V', }; static struct option op[] = { { "device", 1, 0, OPT_DEVICE }, { "help", 0, 0, OPT_HELP }, { "max-buffer-size", 1, 0, OPT_MAX_BUFFER }, { "min-buffer-size", 1, 0, OPT_MIN_BUFFER }, { "no-rewind", 0, 0, OPT_NO_REWIND }, { "size", 1, 0, OPT_SIZE }, { "rewind-at-start", 0, 0, OPT_REWIND }, { "version", 0, 0, OPT_VERSION }, { 0, 0, 0, 0 }, }; static int lo; for (;;) { int c = getopt_long(argc, argv, "d:hm:M:s:rRV?", op, &lo); if (c == -1) break; switch (c) { case OPT_DEVICE: device = optarg; break; case OPT_HELP: case '?': printf("tape-benchmark (" TAPEBENCHMARK_VERSION ")\n"); printf(" -d, --device=DEV : use this device DEV instead of \"" DEFAULT_DEVICE "\"\n"); printf(" -h, --help : show this and quit\n"); convert_size(buffer_size, 16, MAX_BUFFER_SIZE); printf(" -M, --max-buffer-size=SIZE : maximum buffer size (instead of %s)\n", buffer_size); convert_size(buffer_size, 16, MIN_BUFFER_SIZE); printf(" -m, --min-buffer-size=SIZE : minimum buffer size (instead of %s)\n", buffer_size); convert_size(buffer_size, 16, DEFAULT_SIZE); printf(" -s, --size=SIZE : size of file (default: %s)\n", buffer_size); printf(" -r, --no-rewind : no rewind tape between step (default: rewind between step)\n"); printf(" -R, --rewind-at-start : rewind tape before writing on tape, (default: no rewind at start)\n\n"); printf("SIZE can be specified with (BKGT)\n"); printf(" 1B => 1 byte, 1K => 1024B, 1M => 1024K, 1G => 1024M, 1T => 1024G\n"); printf("Another way to set the size is by specifying an integer which will be interpreted as a power of two.\n"); printf(" 10 => 2^10 bytes (= 1K), 16 => 2^16 bytes (= 64K), 24 => 2^24 bytes (= 16M), and so on\n"); printf("Constraint: min-buffer-size and max-buffer-size should be a power of two\n\n"); printf("Note: this programme will allocate 32 buffers of max-buffer-size\n"); return 0; case OPT_MAX_BUFFER: tmp_size = parse_size(optarg); if (check_size(tmp_size)) { max_buffer_size = tmp_size; } else { printf("Error: max-buffer-size should be a power of two\n"); return 1; } break; case OPT_MIN_BUFFER: tmp_size = parse_size(optarg); if (check_size(tmp_size)) { min_buffer_size = tmp_size; } else { printf("Error: min-buffer-size should be a power of two\n"); return 1; } break; case OPT_NO_REWIND: no_rewind = true; break; case OPT_SIZE: size = parse_size(optarg); break; case OPT_REWIND: rewind = true; break; case OPT_VERSION: printf("tape-benchmark (" TAPEBENCHMARK_VERSION ", date and time : " __DATE__ " " __TIME__ ")\n"); printf("checksum of source code: " TAPEBENCHMARK_SRCSUM "\n"); printf("git commit: " TAPEBENCHMARK_GIT_COMMIT "\n"); return 0; } } print_time(); print_flush("Openning \"%s\"... ", device); int fd_tape = open(device, O_RDONLY); if (fd_tape < 0) { printf("failed!!!, because %m\n"); return 2; } struct mtget mt; int failed = ioctl(fd_tape, MTIOCGET, &mt); if (failed != 0) { close(fd_tape); printf("Oops: seem not to be a valid tape device\n"); return 2; } if (GMT_WR_PROT(mt.mt_gstat)) { close(fd_tape); printf("Oops: Write lock enabled\n"); return 2; } failed = close(fd_tape); fd_tape = open(device, O_WRONLY); if (fd_tape < 0) { printf("failed!!!, because %m\n"); return 2; } else { printf("fd: %d\n", fd_tape); } if (rewind && !rewind_tape(fd_tape)) return 2; ssize_t current_block_size = (mt.mt_dsreg & MT_ST_BLKSIZE_MASK) >> MT_ST_BLKSIZE_SHIFT; print_time(); print_flush("Generate random data from \"/dev/urandom\"... "); int fd_ran = open("/dev/urandom", O_RDONLY); if (fd_ran < 0) { printf("Failed to open because %m\n"); close(fd_tape); return 2; } int j; for (j = 0; j < 32; j++) { buffer[j] = malloc(max_buffer_size); if (buffer[j] == NULL) { printf("Error: failed to allocated memory (size: %zd) because %m\n", max_buffer_size); close(fd_tape); close(fd_ran); return 3; } ssize_t nb_read = read(fd_ran, buffer[j], max_buffer_size); if (nb_read < 0) printf("\nWarning: failed to read from \"/dev/urandom\" because %m\n"); else if (nb_read < max_buffer_size) printf("\nWarning: read less than expected, %zd instead of %zd\n", nb_read, max_buffer_size); } close(fd_ran); printf("done\n"); static char clean_line[64]; memset(clean_line, ' ', 64); ssize_t write_size; for (write_size = min_buffer_size; write_size <= max_buffer_size; write_size <<= 1) { if (current_block_size > 0) { write_size = current_block_size; printf("Warning: block size is defined to %zd instead of %zd\n", current_block_size, write_size); } struct pollfd plfd = { fd_tape, POLLOUT, 0 }; int pll_rslt = poll(&plfd, 1, 100); int poll_retry = 0; while (pll_rslt < 1) { if (poll_retry == 0) printf("Device is no ready, so we wait until"); else printf("."); fflush(stdout); poll_retry++; pll_rslt = poll(&plfd, 1, 6000); if (pll_rslt > 0) printf("\n"); } struct timeval time_start; gettimeofday(&time_start, NULL); ssize_t nb_loop = size / write_size; convert_size(buffer_size, 16, write_size); print_time(); printf("Starting, nb loop: %zd, block size: %s\n", nb_loop, buffer_size); struct timespec start, last, current; clock_gettime(CLOCK_MONOTONIC, &start); last = start; int write_error = 0; long long int i; static int last_width = 64; for (i = 0; i < nb_loop; i++) { ssize_t nb_write = write(fd_tape, buffer[i & 0x1F], write_size); if (nb_write < 0) { if (last_width > 0) printf("\r%*s\r", last_width, clean_line); switch (errno) { case EINVAL: convert_size(buffer_size, 16, write_size >> 1); printf("It seems that you cannot use a block size greater than %s\n", buffer_size); break; case EBUSY: printf("rDevice is busy, so we wait a few seconds before restarting\n"); sleep(8); print_time(); printf("Restarting, nb loop: %zd, block size: %s\n", nb_loop, buffer_size); i = -1; clock_gettime(CLOCK_MONOTONIC, &start); break; default: printf("Oops: an error occured => (%d) %m\n", errno); printf("fd: %d, buffer: %p, count: %zd\n", fd_tape, buffer[i & 0x1F], write_size); break; } write_error = 1; break; } clock_gettime(CLOCK_MONOTONIC, ¤t); if (last.tv_sec + 5 <= current.tv_sec) { float pct = 100 * i; double time_spent = difftime(current.tv_sec, start.tv_sec); double speed = i * write_size; speed /= time_spent; convert_size(buffer_size, 16, speed); printf("\r%*s\r", last_width, clean_line); printf("loop: %lld, current speed %s, done: %.2f%%%n", i, buffer_size, pct / nb_loop, &last_width); fflush(stdout); last = current; } } printf("\r%*s\r", last_width, clean_line); struct timeval end; gettimeofday(&end, 0); clock_gettime(CLOCK_MONOTONIC, ¤t); double time_spent = difftime(current.tv_sec, start.tv_sec); double speed = i * write_size; speed /= time_spent; convert_size(buffer_size, 16, speed); print_time(); printf("Finished, current speed %s\n", buffer_size); struct mtget mt2; failed = ioctl(fd_tape, MTIOCGET, &mt2); if (failed != 0) { printf("MTIOCGET failed => %m\n"); break; } struct mtop eof = { MTWEOF, 1 }; failed = ioctl(fd_tape, MTIOCTOP, &eof); if (failed != 0) { printf("Weof failed => %m\n"); break; } struct mtop nop = { MTNOP, 1 }; failed = ioctl(fd_tape, MTIOCTOP, &nop); if (failed != 0) { printf("Nop failed => %m\n"); break; } failed = ioctl(fd_tape, MTIOCGET, &mt2); if (failed != 0) { printf("MTIOCGET failed => %m\n"); break; } if (!no_rewind) { if (mt.mt_fileno < 2) { rewind_tape(fd_tape); } else { print_time(); print_flush("Moving backward space 1 file... "); static struct mtop rewind = { MTBSFM, 2 }; failed = ioctl(fd_tape, MTIOCTOP, &rewind); if (failed != 0) printf("Failed => %m\n"); else printf("done\n"); } } failed = ioctl(fd_tape, MTIOCGET, &mt2); if (failed) printf("MTIOCGET failed => %m\n"); if (current_block_size > 0 || write_error) break; } close(fd_tape); for (j = 0; j < 32; j++) free(buffer[j]); return 0; }
uint32_t generic_tape_device::status_dev() { struct mtget mt_stat; uint32_t status = 0; if (state & (ST_EOT | ST_WEOT)) { status |= BMT_EOD; Pmsg0(-20, " EOD"); } if (state & ST_EOF) { status |= BMT_EOF; Pmsg0(-20, " EOF"); } status |= BMT_TAPE; Pmsg0(-20,_(" Bareos status:")); Pmsg2(-20,_(" file=%d block=%d\n"), file, block_num); if (d_ioctl(m_fd, MTIOCGET, (char *)&mt_stat) < 0) { berrno be; dev_errno = errno; Mmsg2(errmsg, _("ioctl MTIOCGET error on %s. ERR=%s.\n"), print_name(), be.bstrerror()); return 0; } Pmsg0(-20, _(" Device status:")); #if defined(HAVE_LINUX_OS) if (GMT_EOF(mt_stat.mt_gstat)) { status |= BMT_EOF; Pmsg0(-20, " EOF"); } if (GMT_BOT(mt_stat.mt_gstat)) { status |= BMT_BOT; Pmsg0(-20, " BOT"); } if (GMT_EOT(mt_stat.mt_gstat)) { status |= BMT_EOT; Pmsg0(-20, " EOT"); } if (GMT_SM(mt_stat.mt_gstat)) { status |= BMT_SM; Pmsg0(-20, " SM"); } if (GMT_EOD(mt_stat.mt_gstat)) { status |= BMT_EOD; Pmsg0(-20, " EOD"); } if (GMT_WR_PROT(mt_stat.mt_gstat)) { status |= BMT_WR_PROT; Pmsg0(-20, " WR_PROT"); } if (GMT_ONLINE(mt_stat.mt_gstat)) { status |= BMT_ONLINE; Pmsg0(-20, " ONLINE"); } if (GMT_DR_OPEN(mt_stat.mt_gstat)) { status |= BMT_DR_OPEN; Pmsg0(-20, " DR_OPEN"); } if (GMT_IM_REP_EN(mt_stat.mt_gstat)) { status |= BMT_IM_REP_EN; Pmsg0(-20, " IM_REP_EN"); } #elif defined(HAVE_WIN32) if (GMT_EOF(mt_stat.mt_gstat)) { status |= BMT_EOF; Pmsg0(-20, " EOF"); } if (GMT_BOT(mt_stat.mt_gstat)) { status |= BMT_BOT; Pmsg0(-20, " BOT"); } if (GMT_EOT(mt_stat.mt_gstat)) { status |= BMT_EOT; Pmsg0(-20, " EOT"); } if (GMT_EOD(mt_stat.mt_gstat)) { status |= BMT_EOD; Pmsg0(-20, " EOD"); } if (GMT_WR_PROT(mt_stat.mt_gstat)) { status |= BMT_WR_PROT; Pmsg0(-20, " WR_PROT"); } if (GMT_ONLINE(mt_stat.mt_gstat)) { status |= BMT_ONLINE; Pmsg0(-20, " ONLINE"); } if (GMT_DR_OPEN(mt_stat.mt_gstat)) { status |= BMT_DR_OPEN; Pmsg0(-20, " DR_OPEN"); } if (GMT_IM_REP_EN(mt_stat.mt_gstat)) { status |= BMT_IM_REP_EN; Pmsg0(-20, " IM_REP_EN"); } #endif /* HAVE_LINUX_OS || HAVE_WIN32 */ if (has_cap(CAP_MTIOCGET)) { Pmsg2(-20, _(" file=%d block=%d\n"), mt_stat.mt_fileno, mt_stat.mt_blkno); } else { Pmsg2(-20, _(" file=%d block=%d\n"), -1, -1); } return status; }