void print_pos() { struct mtget mt_get; ioctl(fd, MTIOCGET, &mt_get); printf("*** file:block %i:%i BOT=%u EOD=%u EOF=%u\n", mt_get.mt_fileno, mt_get.mt_blkno, GMT_BOT(mt_get.mt_gstat) != 0, GMT_EOD(mt_get.mt_gstat) != 0, GMT_EOF(mt_get.mt_gstat) != 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; }
static int w32_internal_mtpos ( HANDLE hFile, U32* pStat, DWORD* pdwLogPos, DWORD* pdwAbsPos, ifd_t ifd ) { DWORD dwDummyPartition, dwDummyPositionHigh; ASSERT( pStat && pdwLogPos ); // (sanity check) // PROGRAMMING NOTE: the SDK docs state that for the 'lpdwOffsetHigh' // parameter (i.e. dwDummyPositionHigh, the 5th paramater): // // "This parameter can be NULL if the // high-order bits are not required." // // But it LIES! Simple expirical observation reveals that ALL parameters // are in fact required. If any are NULL then 'GetTapePosition' crashes // and burns (which is unusual since usually when you pass invalid args // to an API it usually just returns an error code, but in this case it // doesn't. It actually crashes) do { U32 dummy_stat = 0; errno = GetTapePosition ( hFile, TAPE_LOGICAL_POSITION, &dwDummyPartition, pdwLogPos, &dwDummyPositionHigh ); errno = w32_internal_rc ( &dummy_stat ); } while ( EINTR == errno ); if (errno) return -1; if (pdwAbsPos) // (may be NULL if they're not interested in it) { do { U32 dummy_stat = 0; errno = GetTapePosition ( hFile, TAPE_ABSOLUTE_POSITION, &dwDummyPartition, pdwAbsPos, &dwDummyPositionHigh ); errno = w32_internal_rc ( &dummy_stat ); } while ( EINTR == errno ); if (errno) return -1; } // PROGRAMMING NOTE: the Windows 'GetTapePosition' API returns either // a LOGICAL position value or an ABSOLUTE position value. Based on // trial and error it was determined the LOGICAL position corresponds // to the SCSI "READ POSITION" command's "first block location" value, // and the ABSOLUTE tape position appears to correspond to the SCSI // "last block location". // Since what we want is what IBM calls the "Channel block ID" (which // itself appears to correspond to what the SCSI documentation refers // to as the "First block location"), then what we want here is what // Windows refers to as the LOGICAL position, not the ABSOLUTE (i.e. // device-relative) position I originally thought we needed/wanted. if ( ( *pdwLogPos & g_BOTmsk[ ifd ] ) == g_BOTbot[ ifd ] ) *pStat |= GMT_BOT (0xFFFFFFFF); else *pStat &= ~GMT_BOT (0xFFFFFFFF); return 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; }
/* * 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; }
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; }