Beispiel #1
0
/*
 * 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);
}
Beispiel #2
0
/******************************************************************
 *      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
}
Beispiel #3
0
/*
 * 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;
}
Beispiel #4
0
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;
}
Beispiel #5
0
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;
}
Beispiel #6
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;
}
Beispiel #7
0
/*
 * 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, &current);

			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, &current);

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