blob: bd6f8870273183142a4ced1948371cf883f8d400 [file] [log] [blame]
/*
* Copyright (c) 2000-2001 Silicon Graphics, Inc.; provided copyright in
* certain portions may be held by third parties as indicated herein.
* All Rights Reserved.
*
* The code in this source file represents an aggregation of work from
* Georgia Tech, Fred Fish, Jeff Lee, Arnold Robbins and other Silicon
* Graphics engineers over the period 1985-2000.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it would be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/mtio.h>
#include <sys/param.h>
#include <assert.h>
#include "config.h"
#include "rmtlib.h"
#include "swap.h"
/*
* uses old_mtget IRIX structure since we don't bother
* sending the "V" version command.
*/
struct irix_mtget {
short mt_type; /* type of magtape device */
unsigned short mt_dsreg; /* ``drive status'' register */
short mt_erreg; /* ``error'' register */
short mt_resid; /* residual count */
int mt_fileno; /* file number of current position */
int mt_blkno; /* block number of current position */
};
struct linux32_mtget
{
int32_t mt_type; /* Type of magtape device. */
int32_t mt_resid; /* Residual count: */
/* The following registers are device dependent. */
int32_t mt_dsreg; /* Status register. */
int32_t mt_gstat; /* Generic (device independent) status. */
int32_t mt_erreg; /* Error register. */
/* The next two fields are not always used. */
int32_t mt_fileno; /* Number of current file on tape. */
int32_t mt_blkno; /* Current block number. */
};
struct linux64_mtget
{
int64_t mt_type; /* Type of magtape device. */
int64_t mt_resid; /* Residual count. */
/* The following registers are device dependent. */
int64_t mt_dsreg; /* Status register. */
int64_t mt_gstat; /* Generic (device independent) status. */
int64_t mt_erreg; /* Error register. */
/* The next two fields are not always used. */
int32_t mt_fileno; /* Number of current file on tape. */
int32_t mt_blkno; /* Current block number. */
};
/* IRIX tape device status values */
#define IRIX_MT_EOT 0x01 /* tape is at end of media */
#define IRIX_MT_BOT 0x02 /* tape is at beginning of media */
#define IRIX_MT_WPROT 0x04 /* tape is write-protected */
#define IRIX_MT_EW 0x08 /* hit early warning marker */
#define IRIX_MT_ONL 0x40 /* drive is online */
#define IRIX_MT_EOD 0x4000 /* tape is at end of data */
#define IRIX_MT_FMK 0x8000 /* tape is at file mark */
/* IRIX mt operations (mt_op values for MTIOCTOP) */
#define IRIX_MTWEOF 0 /* write an end-of-file record */
#define IRIX_MTFSF 1 /* forward space file */
#define IRIX_MTBSF 2 /* backward space file */
#define IRIX_MTFSR 3 /* forward space record */
#define IRIX_MTBSR 4 /* backward space record */
#define IRIX_MTREW 5 /* rewind */
#define IRIX_MTOFFL 6 /* rewind and put the drive offline */
#define IRIX_MTERASE 12 /* erase tape from current position to EOT */
#define IRIX_MTUNLOAD 13 /* unload tape from drive */
/* std (common) mt op codes */
#define STD_MTWEOF 0 /* write an end-of-file record */
#define STD_MTFSF 1 /* forward space file */
#define STD_MTBSF 2 /* backward space file */
#define STD_MTFSR 3 /* forward space record */
#define STD_MTBSR 4 /* backward space record */
#define STD_MTREW 5 /* rewind */
#define STD_MTOFFL 6 /* rewind and put the drive offline */
#define MT_MAX 40 /* encompass potential range of mt_op values */
static int mtop_irixmap[MT_MAX] = {-1};
static int mtop_stdmap[MT_MAX] = {-1};
static void
init_mtop_map(void)
{
/* set all other values to sentinel (-1) */
/* only map the ones which xfsdump/restore are interested in */
mtop_irixmap[MTWEOF] = IRIX_MTWEOF;
mtop_irixmap[MTFSF] = IRIX_MTFSF;
mtop_irixmap[MTBSF] = IRIX_MTBSF;
mtop_irixmap[MTFSR] = IRIX_MTFSR;
mtop_irixmap[MTBSR] = IRIX_MTBSR;
mtop_irixmap[MTREW] = IRIX_MTREW;
mtop_irixmap[MTOFFL] = IRIX_MTOFFL;
mtop_irixmap[MTERASE] = IRIX_MTERASE;
mtop_irixmap[MTUNLOAD] = IRIX_MTUNLOAD;
mtop_stdmap[MTWEOF] = STD_MTWEOF;
mtop_stdmap[MTFSF] = STD_MTFSF;
mtop_stdmap[MTBSF] = STD_MTBSF;
mtop_stdmap[MTFSR] = STD_MTFSR;
mtop_stdmap[MTBSR] = STD_MTBSR;
mtop_stdmap[MTREW] = STD_MTREW;
mtop_stdmap[MTOFFL] = STD_MTOFFL;
mtop_stdmap[MTUNLOAD] = STD_MTOFFL;
}
static int _rmt_ioctl(int, unsigned int, void *);
/*
* Do ioctl on file. Looks just like ioctl(2) to caller.
*/
int
rmtioctl(int fildes, unsigned int request, void *arg)
{
if (isrmt (fildes)) {
return (_rmt_ioctl (fildes - REM_BIAS, request, arg));
}
else {
return (ioctl (fildes, request, arg));
}
}
/*
* _rmt_ioctl --- perform raw tape operations remotely
*/
/*
* WARNING: MTIOCGET code is highly dependent on the format
* of mtget on different platforms
* We only support Linux 32/ia64 and IRIX 32/64 for this case.
* We use the result of uname(1) (in rmtopen()) and
* the size of the mtget structure to determine which
* architecture it is.
*/
static int
_rmt_ioctl(int fildes, unsigned int op, void *arg)
{
char buffer[BUFMAGIC];
int rc, cnt, ssize;
char *p = NULL, *irixget = NULL, *linux32get = NULL, *linux64get = NULL;
struct irix_mtget irix_mtget;
struct linux32_mtget linux32_mtget;
struct linux64_mtget linux64_mtget;
int islinux32 = 1; /* is remote machine Linux 32 bit */
static int onetrip = 0;
if (!onetrip) {
onetrip = 1;
init_mtop_map();
}
/*
* MTIOCTOP is the easy one. nothing is transfered in binary
*/
if (op == MTIOCTOP) {
int mt_op = ((struct mtop *) arg)->mt_op;
int mt_count = ((struct mtop *) arg)->mt_count;
if (RMTHOST(fildes) == UNAME_UNDEFINED) {
_rmt_msg(RMTWARN,
_("rmtioctl: remote host type not supported for MTIOCTOP\n"));
setoserror( EPROTONOSUPPORT );
return(-1);
}
/* map the linux op code to the irix op code */
if (RMTHOST(fildes) == UNAME_IRIX) {
mt_op = mtop_irixmap[mt_op];
if (mt_op == -1) {
setoserror( EINVAL );
return(-1);
}
}
else if (RMTHOST(fildes) != UNAME_LINUX) {
/* map the linux op code to the standard/fallback op code */
mt_op = mtop_stdmap[mt_op];
if (mt_op == -1) {
setoserror( EINVAL );
return(-1);
}
}
sprintf(buffer, "I%d\n%d\n", mt_op, mt_count);
if (_rmt_command(fildes, buffer) == -1) {
return(-1);
}
return(_rmt_status(fildes));
}
else if (op == MTIOCGET) {
/*
* Grab the status and read it directly into the structure.
* Since the data is binary data, and the other machine might
* be IRIX or Linux of a different byte-order,
* we have to be careful in converting the data.
*
* NOTE: the original /etc/rmt did NOT support a newline after
* the S command, and Sun still does not. Neither does the
* current bsd source, all the way through the tahoe release.
* So do NOT add the \n to this! The sgi rmt command will
* work either way. Olson, 4/91
*/
if (_rmt_command(fildes, "S") == -1 ||
(rc = _rmt_status(fildes)) == -1)
return(-1);
/* If undefined then try and define it by looking
* and the size of the get structure.
* If we know our rmt host, then verify that the
* structure is the correct size for the supported ones
*/
switch (RMTHOST(fildes)) {
case UNAME_UNDEFINED:
_rmt_msg(RMTWARN,
_("rmtioctl: remote host type not supported for MTIOCGET\n"));
setoserror( EPROTONOSUPPORT );
return(-1);
case UNAME_IRIX:
if (sizeof(struct irix_mtget) != rc) {
_rmt_msg(RMTWARN,
_("rmtioctl: IRIX mtget structure of wrong size"
" - got %d, wanted %d\n"),
rc, sizeof(struct irix_mtget));
setoserror( EPROTONOSUPPORT );
return(-1);
}
break;
case UNAME_LINUX:
if (sizeof(struct linux32_mtget) == rc) {
islinux32 = 1;
}
else if (sizeof(struct linux64_mtget) == rc) {
islinux32 = 0;
}
else {
_rmt_msg(RMTWARN,
_("rmtioctl: Linux mtget structure of wrong size "
"- got %d, wanted %d or %d\n"),
rc, sizeof(struct linux32_mtget),
sizeof(struct linux64_mtget));
setoserror( EPROTONOSUPPORT );
return(-1);
}
break;
default:
setoserror( EPROTONOSUPPORT );
return(-1);
}
assert(RMTHOST(fildes)==UNAME_LINUX || RMTHOST(fildes)==UNAME_IRIX);
if (RMTHOST(fildes) == UNAME_IRIX) {
p = irixget = (char *)&irix_mtget;
}
else if (islinux32) {
p = linux32get = (char *)&linux32_mtget;
}
else {
p = linux64get = (char *)&linux64_mtget;
}
/* read in all the data */
ssize = rc;
for (; ssize > 0; ssize -= cnt, p += cnt) {
cnt = read(READ(fildes), p, ssize);
if (cnt <= 0) {
_rmt_abort(fildes);
setoserror( EIO );
return(-1);
}
}
/*
* May need to byteswap
*/
if (RMTHOST(fildes) == UNAME_IRIX) {
struct irix_mtget *irixp = (struct irix_mtget *)irixget;
if (irixp->mt_type > 0xff) {
/* assume that mt_type should fit in 1 byte */
irixp->mt_type = INT_SWAP(irixp->mt_type, irixp->mt_type);
irixp->mt_dsreg = INT_SWAP(irixp->mt_dsreg, irixp->mt_dsreg);
irixp->mt_erreg = INT_SWAP(irixp->mt_erreg, irixp->mt_erreg);
irixp->mt_resid = INT_SWAP(irixp->mt_resid, irixp->mt_resid);
irixp->mt_fileno = INT_SWAP(irixp->mt_fileno, irixp->mt_fileno);
irixp->mt_blkno = INT_SWAP(irixp->mt_blkno, irixp->mt_blkno);
}
}
else if (islinux32) {
struct linux32_mtget *linuxp = (struct linux32_mtget *)linux32get;
if (linuxp->mt_type > 0xffff) {
/* assume that mt_type should fit in 2 bytes */
linuxp->mt_type = INT_SWAP(linuxp->mt_type, linuxp->mt_type);
linuxp->mt_dsreg = INT_SWAP(linuxp->mt_dsreg, linuxp->mt_dsreg);
linuxp->mt_erreg = INT_SWAP(linuxp->mt_erreg, linuxp->mt_erreg);
linuxp->mt_resid = INT_SWAP(linuxp->mt_resid, linuxp->mt_resid);
linuxp->mt_fileno = INT_SWAP(linuxp->mt_fileno, linuxp->mt_fileno);
linuxp->mt_blkno = INT_SWAP(linuxp->mt_blkno, linuxp->mt_blkno);
linuxp->mt_gstat = INT_SWAP(linuxp->mt_gstat, linuxp->mt_gstat);
}
}
else {
struct linux64_mtget *linuxp = (struct linux64_mtget *)linux64get;
if (linuxp->mt_type > 0xffff) {
/* assume that mt_type should fit in 2 bytes */
linuxp->mt_type = INT_SWAP(linuxp->mt_type, linuxp->mt_type);
linuxp->mt_dsreg = INT_SWAP(linuxp->mt_dsreg, linuxp->mt_dsreg);
linuxp->mt_erreg = INT_SWAP(linuxp->mt_erreg, linuxp->mt_erreg);
linuxp->mt_resid = INT_SWAP(linuxp->mt_resid, linuxp->mt_resid);
linuxp->mt_fileno = INT_SWAP(linuxp->mt_fileno, linuxp->mt_fileno);
linuxp->mt_blkno = INT_SWAP(linuxp->mt_blkno, linuxp->mt_blkno);
linuxp->mt_gstat = INT_SWAP(linuxp->mt_gstat, linuxp->mt_gstat);
}
}
/*
* now mtget has the correct (byte-swapped if needed) data,
* so we just need to copy over the fields which are possibly
* of different length and different semantics.
*/
if (RMTHOST(fildes) == UNAME_IRIX) {
struct mtget *dstp = (struct mtget *)arg;
struct irix_mtget *srcp = (struct irix_mtget *)irixget;
short status = srcp->mt_dsreg;
dstp->mt_type = srcp->mt_type;
dstp->mt_erreg = srcp->mt_erreg;
dstp->mt_resid = srcp->mt_resid;
dstp->mt_fileno = srcp->mt_fileno;
dstp->mt_blkno = srcp->mt_blkno;
dstp->mt_dsreg = srcp->mt_dsreg; /* different semantics */
/* need to do tape status conversions */
dstp->mt_gstat = 0;
if (status & IRIX_MT_EOT)
dstp->mt_gstat |= GMT_EOT(0xffffffff);
if (status & IRIX_MT_BOT)
dstp->mt_gstat |= GMT_BOT(0xffffffff);
if (status & IRIX_MT_WPROT)
dstp->mt_gstat |= GMT_WR_PROT(0xffffffff);
if (status & IRIX_MT_ONL)
dstp->mt_gstat |= GMT_ONLINE(0xffffffff);
if (status & IRIX_MT_EOD)
dstp->mt_gstat |= GMT_EOD(0xffffffff);
if (status & IRIX_MT_FMK)
dstp->mt_gstat |= GMT_EOF(0xffffffff);
if (status & IRIX_MT_EW)
;/* No GMT_ to map it to */
}
else if (islinux32) {
struct mtget *dstp = (struct mtget *)arg;
struct linux32_mtget *srcp = (struct linux32_mtget *)linux32get;
dstp->mt_type = srcp->mt_type;
dstp->mt_erreg = srcp->mt_erreg;
dstp->mt_resid = srcp->mt_resid;
dstp->mt_fileno = srcp->mt_fileno;
dstp->mt_blkno = srcp->mt_blkno;
dstp->mt_dsreg = srcp->mt_dsreg;
dstp->mt_gstat = srcp->mt_gstat;
}
else {
struct mtget *dstp = (struct mtget *)arg;
struct linux64_mtget *srcp = (struct linux64_mtget *)linux64get;
dstp->mt_type = srcp->mt_type;
dstp->mt_erreg = srcp->mt_erreg;
dstp->mt_resid = srcp->mt_resid;
dstp->mt_fileno = srcp->mt_fileno;
dstp->mt_blkno = srcp->mt_blkno;
dstp->mt_dsreg = srcp->mt_dsreg;
dstp->mt_gstat = srcp->mt_gstat;
}
return(0);
}
else {
setoserror( EINVAL );
return(-1);
}
}