You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
555 lines
13 KiB
555 lines
13 KiB
/* Copyright 1996 Grant R. Guenther, based on work of Itai Nahshon
|
|
* http://www.torque.net/ziptool.html
|
|
* Copyright 1997-2002,2007-2009 Alain Knaff.
|
|
* This file is part of mtools.
|
|
*
|
|
* Mtools 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, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* Mtools is distributed in the hope that it will 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 Mtools. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
* mzip.c
|
|
* Iomega Zip/Jaz drive tool
|
|
* change protection mode and eject disk
|
|
*/
|
|
|
|
/* mzip.c by Markus Gyger <mgyger@itr.ch> */
|
|
/* This code is based on ftp://gear.torque.net/pub/ziptool.c */
|
|
/* by Grant R. Guenther with the following copyright notice: */
|
|
|
|
/* (c) 1996 Grant R. Guenther, based on work of Itai Nahshon */
|
|
/* http://www.torque.net/ziptool.html */
|
|
|
|
|
|
/* Unprotect-till-eject modes and mount tests added
|
|
* by Ilya Ovchinnikov <ilya@socio.msu.su>
|
|
*/
|
|
|
|
#include "sysincludes.h"
|
|
#include "mtools.h"
|
|
#include "scsi.h"
|
|
|
|
#ifndef _PASSWORD_LEN
|
|
#define _PASSWORD_LEN 33
|
|
#endif
|
|
|
|
#ifdef OS_linux
|
|
|
|
#if __GLIBC__ >=2
|
|
#include <sys/mount.h>
|
|
#else
|
|
#define _LINUX_KDEV_T_H 1 /* don't redefine MAJOR/MINOR */
|
|
#include <linux/fs.h>
|
|
#endif
|
|
|
|
#include "devices.h"
|
|
|
|
#endif
|
|
|
|
|
|
static int zip_cmd(int priv, int fd, unsigned char cdb[6], int clen,
|
|
scsi_io_mode_t mode, void *data, size_t len,
|
|
void *extra_data)
|
|
{
|
|
int r;
|
|
|
|
if(priv)
|
|
reclaim_privs();
|
|
r = scsi_cmd(fd, cdb, clen, mode, data, len, extra_data);
|
|
if(priv)
|
|
drop_privs();
|
|
return r;
|
|
}
|
|
|
|
static int test_mounted ( char *dev )
|
|
{
|
|
#ifdef HAVE_MNTENT_H
|
|
struct mntent *mnt;
|
|
struct MT_STAT st_dev, st_mnt;
|
|
FILE *mtab;
|
|
/*
|
|
* Now check if any partition of this device is already mounted (this
|
|
* includes checking if the device is mounted under a different name).
|
|
*/
|
|
|
|
if (MT_STAT (dev, &st_dev)) {
|
|
fprintf (stderr, "%s: stat(%s) failed: %s.\n",
|
|
progname, dev, strerror (errno));
|
|
exit(1);
|
|
}
|
|
|
|
if (!S_ISBLK (st_dev.st_mode)) /* not a block device, cannot
|
|
* be mounted */
|
|
return 0;
|
|
|
|
#ifndef _PATH_MOUNTED
|
|
# define _PATH_MOUNTED "/etc/mtab"
|
|
#endif
|
|
|
|
if ((mtab = setmntent (_PATH_MOUNTED, "r")) == NULL) {
|
|
fprintf (stderr, "%s: can't open %s.\n",
|
|
progname, _PATH_MOUNTED);
|
|
exit(1);
|
|
}
|
|
|
|
while ( ( mnt = getmntent (mtab) ) ) {
|
|
if (!mnt->mnt_fsname
|
|
|
|
#ifdef MNTTYPE_SWAP
|
|
|| !strcmp (mnt->mnt_type, MNTTYPE_SWAP)
|
|
#endif
|
|
#ifdef MNTTYPE_NFS
|
|
|| !strcmp (mnt->mnt_type, MNTTYPE_NFS)
|
|
#endif
|
|
|| !strcmp (mnt->mnt_type, "proc")
|
|
|| !strcmp (mnt->mnt_type, "smbfs")
|
|
#ifdef MNTTYPE_IGNORE
|
|
|| !strcmp (mnt->mnt_type, MNTTYPE_IGNORE)
|
|
#endif
|
|
)
|
|
continue;
|
|
|
|
if (MT_STAT (mnt->mnt_fsname, &st_mnt)) {
|
|
continue;
|
|
}
|
|
|
|
if (S_ISBLK (st_mnt.st_mode)) {
|
|
#ifdef OS_linux
|
|
/* on Linux, warn also if the device is on the same
|
|
* partition */
|
|
if (MAJOR(st_mnt.st_rdev) == MAJOR(st_dev.st_rdev) &&
|
|
MINOR(st_mnt.st_rdev) >= MINOR(st_dev.st_rdev) &&
|
|
MINOR(st_mnt.st_rdev) <= MINOR(st_dev.st_rdev)+15){
|
|
fprintf (stderr,
|
|
"Device %s%d is mounted on %s.\n",
|
|
dev,
|
|
MINOR(st_mnt.st_rdev) -
|
|
MINOR(st_dev.st_rdev),
|
|
mnt->mnt_dir);
|
|
#else
|
|
if(st_mnt.st_rdev != st_dev.st_rdev) {
|
|
#endif
|
|
endmntent (mtab);
|
|
return 1;
|
|
}
|
|
#if 0
|
|
} /* keep Emacs indentation happy */
|
|
#endif
|
|
}
|
|
}
|
|
endmntent (mtab);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
|
|
static void usage(int ret) NORETURN;
|
|
static void usage(int ret)
|
|
{
|
|
fprintf(stderr,
|
|
"Mtools version %s, dated %s\n",
|
|
mversion, mdate);
|
|
fprintf(stderr,
|
|
"Usage: %s [-V] [-q] [-e] [-u] [-r|-w|-p|-x] [drive:]\n"
|
|
"\t-q print status\n"
|
|
"\t-e eject disk\n"
|
|
"\t-f eject disk even when mounted\n"
|
|
"\t-r write protected (read-only)\n"
|
|
"\t-w not write-protected (read-write)\n"
|
|
"\t-p password write protected\n"
|
|
"\t-x password protected\n"
|
|
"\t-u unprotect till disk ejecting\n",
|
|
progname);
|
|
exit(ret);
|
|
}
|
|
|
|
|
|
enum mode_t {
|
|
ZIP_RW = 0,
|
|
ZIP_RO = 2,
|
|
ZIP_RO_PW = 3,
|
|
ZIP_PW = 5,
|
|
ZIP_UNLOCK_TIL_EJECT = 8
|
|
};
|
|
|
|
static enum mode_t get_zip_status(int priv, int fd, void *extra_data)
|
|
{
|
|
unsigned char status[128];
|
|
unsigned char cdb[6] = { 0x06, 0, 0x02, 0, sizeof status, 0 };
|
|
|
|
if (zip_cmd(priv, fd, cdb, 6, SCSI_IO_READ,
|
|
status, sizeof status, extra_data) == -1) {
|
|
perror("status: ");
|
|
exit(1);
|
|
}
|
|
return status[21] & 0xf;
|
|
}
|
|
|
|
|
|
static int short_command(int priv, int fd, int cmd1, int cmd2,
|
|
int cmd3, const char *data, void *extra_data)
|
|
{
|
|
unsigned char cdb[6] = { 0, 0, 0, 0, 0, 0 };
|
|
|
|
cdb[0] = cmd1;
|
|
cdb[1] = cmd2;
|
|
cdb[4] = cmd3;
|
|
|
|
return zip_cmd(priv, fd, cdb, 6, SCSI_IO_WRITE,
|
|
(char *) data, data ? strlen(data) : 0, extra_data);
|
|
}
|
|
|
|
|
|
static int iomega_command(int priv, int fd, int mode, const char *data,
|
|
void *extra_data)
|
|
{
|
|
return short_command(priv, fd,
|
|
SCSI_IOMEGA, mode, data ? strlen(data) : 0,
|
|
data, extra_data);
|
|
}
|
|
|
|
static int door_command(int priv, int fd, int cmd1, int cmd2,
|
|
void *extra_data)
|
|
{
|
|
return short_command(priv, fd, cmd1, 0, cmd2, 0, extra_data);
|
|
}
|
|
|
|
void mzip(int argc, char **argv, int type UNUSEDP) NORETURN;
|
|
void mzip(int argc, char **argv, int type UNUSEDP)
|
|
{
|
|
void *extra_data = NULL;
|
|
int c;
|
|
char drive;
|
|
device_t *dev;
|
|
int fd = -1;
|
|
char name[EXPAND_BUF];
|
|
enum { ZIP_NIX = 0,
|
|
ZIP_STATUS = 1 << 0,
|
|
ZIP_EJECT = 1 << 1,
|
|
ZIP_MODE_CHANGE = 1 << 2,
|
|
ZIP_FORCE = 1 << 3
|
|
} request = ZIP_NIX;
|
|
|
|
enum mode_t newMode = ZIP_RW;
|
|
enum mode_t oldMode = ZIP_RW;
|
|
|
|
#define setMode(x) \
|
|
if(request & ZIP_MODE_CHANGE) usage(1); \
|
|
request |= ZIP_MODE_CHANGE; \
|
|
newMode = x; \
|
|
break;
|
|
|
|
/* get command line options */
|
|
if(helpFlag(argc, argv))
|
|
usage(0);
|
|
while ((c = getopt(argc, argv, "i:efpqrwxuh")) != EOF) {
|
|
switch (c) {
|
|
case 'i':
|
|
set_cmd_line_image(optarg);
|
|
break;
|
|
case 'f':
|
|
if (get_real_uid()) {
|
|
fprintf(stderr,
|
|
"Only root can use force. Sorry.\n");
|
|
exit(1);
|
|
}
|
|
request |= ZIP_FORCE;
|
|
break;
|
|
case 'e': /* eject */
|
|
request |= ZIP_EJECT;
|
|
break;
|
|
case 'q': /* status query */
|
|
request |= ZIP_STATUS;
|
|
break;
|
|
|
|
case 'p': /* password read-only */
|
|
setMode(ZIP_RO_PW);
|
|
case 'r': /* read-only */
|
|
setMode(ZIP_RO);
|
|
case 'w': /* read-write */
|
|
setMode(ZIP_RW);
|
|
case 'x': /* password protected */
|
|
setMode(ZIP_PW);
|
|
case 'u': /* password protected */
|
|
setMode(ZIP_UNLOCK_TIL_EJECT)
|
|
case 'h':
|
|
usage(0);
|
|
default: /* unrecognized */
|
|
usage(1);
|
|
|
|
}
|
|
}
|
|
|
|
if (request == ZIP_NIX) request = ZIP_STATUS; /* default action */
|
|
|
|
if (argc - optind > 1 ||
|
|
(argc - optind == 1 &&
|
|
(!argv[optind][0] || argv[optind][1] != ':')))
|
|
usage(1);
|
|
|
|
drive = ch_toupper(argc - optind == 1 ? argv[argc - 1][0] : ':');
|
|
|
|
for (dev = devices; dev->name; dev++) {
|
|
unsigned char cdb[6] = { 0, 0, 0, 0, 0, 0 };
|
|
struct {
|
|
char type,
|
|
type_modifier,
|
|
scsi_version,
|
|
data_format,
|
|
length,
|
|
reserved1[2],
|
|
capabilities,
|
|
vendor[8],
|
|
product[16],
|
|
revision[4],
|
|
vendor_specific[20],
|
|
reserved2[40];
|
|
} inq_data;
|
|
|
|
if (dev->drive != drive)
|
|
continue;
|
|
expand(dev->name, name);
|
|
if ((request & (ZIP_MODE_CHANGE | ZIP_EJECT)) &&
|
|
!(request & ZIP_FORCE) &&
|
|
test_mounted(name)) {
|
|
fprintf(stderr,
|
|
"Can\'t change status of/eject mounted device\n");
|
|
exit(1);
|
|
}
|
|
precmd(dev);
|
|
|
|
if(IS_PRIVILEGED(dev))
|
|
reclaim_privs();
|
|
fd = scsi_open(name, O_RDONLY
|
|
#ifdef O_NDELAY
|
|
| O_NDELAY
|
|
#endif
|
|
, 0644,
|
|
&extra_data);
|
|
if(IS_PRIVILEGED(dev))
|
|
drop_privs();
|
|
|
|
/* need readonly, else we can't
|
|
* open the drive on Solaris if
|
|
* write-protected */
|
|
if (fd == -1)
|
|
continue;
|
|
closeExec(fd);
|
|
|
|
if (!(request & (ZIP_MODE_CHANGE | ZIP_STATUS)))
|
|
/* if no mode change or ZIP specific status is
|
|
* involved, the command (eject) is applicable
|
|
* on all drives */
|
|
break;
|
|
|
|
cdb[0] = SCSI_INQUIRY;
|
|
cdb[4] = sizeof inq_data;
|
|
if (zip_cmd(IS_PRIVILEGED(dev), fd, cdb, 6, SCSI_IO_READ,
|
|
&inq_data, sizeof inq_data, extra_data) != 0) {
|
|
close(fd);
|
|
continue;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "device: %s\n\tvendor: %.8s\n\tproduct: %.16s\n"
|
|
"\trevision: %.4s\n", name, inq_data.vendor,
|
|
inq_data.product, inq_data.revision);
|
|
#endif /* DEBUG */
|
|
|
|
if (strncasecmp("IOMEGA ", inq_data.vendor,
|
|
sizeof inq_data.vendor) ||
|
|
(strncasecmp("ZIP 100 ",
|
|
inq_data.product, sizeof inq_data.product) &&
|
|
strncasecmp("ZIP 100 PLUS ",
|
|
inq_data.product, sizeof inq_data.product) &&
|
|
strncasecmp("ZIP 250 ",
|
|
inq_data.product, sizeof inq_data.product) &&
|
|
strncasecmp("ZIP 750 ",
|
|
inq_data.product, sizeof inq_data.product) &&
|
|
strncasecmp("JAZ 1GB ",
|
|
inq_data.product, sizeof inq_data.product) &&
|
|
strncasecmp("JAZ 2GB ",
|
|
inq_data.product, sizeof inq_data.product))) {
|
|
|
|
/* debugging */
|
|
fprintf(stderr,"Skipping drive with vendor='");
|
|
fwrite(inq_data.vendor,1, sizeof(inq_data.vendor),
|
|
stderr);
|
|
fprintf(stderr,"' product='");
|
|
fwrite(inq_data.product,1, sizeof(inq_data.product),
|
|
stderr);
|
|
fprintf(stderr,"'\n");
|
|
/* end debugging */
|
|
close(fd);
|
|
continue;
|
|
}
|
|
break; /* found Zip/Jaz drive */
|
|
}
|
|
|
|
if (dev->drive == 0) {
|
|
fprintf(stderr, "%s: drive '%c:' is not a Zip or Jaz drive\n",
|
|
argv[0], drive);
|
|
exit(1);
|
|
}
|
|
|
|
if (request & (ZIP_MODE_CHANGE | ZIP_STATUS))
|
|
oldMode = get_zip_status(IS_PRIVILEGED(dev), fd, extra_data);
|
|
|
|
if (request & ZIP_MODE_CHANGE) {
|
|
/* request temp unlock, and disk is already unlocked */
|
|
if(newMode == ZIP_UNLOCK_TIL_EJECT &&
|
|
(oldMode & ZIP_UNLOCK_TIL_EJECT))
|
|
request &= ~ZIP_MODE_CHANGE;
|
|
|
|
/* no password change requested, and disk is already
|
|
* in the requested state */
|
|
if(!(newMode & 0x01) && newMode == oldMode)
|
|
request &= ~ZIP_MODE_CHANGE;
|
|
}
|
|
|
|
if (request & ZIP_MODE_CHANGE) {
|
|
int ret;
|
|
enum mode_t unlockMode, unlockMask;
|
|
const char *passwd;
|
|
char dummy[1];
|
|
|
|
if(newMode == ZIP_UNLOCK_TIL_EJECT) {
|
|
unlockMode = newMode | oldMode;
|
|
unlockMask = 9;
|
|
} else {
|
|
unlockMode = newMode & ~0x5;
|
|
unlockMask = 1;
|
|
}
|
|
|
|
if ((oldMode & unlockMask) == 1) { /* unlock first */
|
|
char *s;
|
|
passwd = "APlaceForYourStuff";
|
|
if ((s = strchr(passwd, '\n'))) *s = '\0'; /* chomp */
|
|
iomega_command(IS_PRIVILEGED(dev), fd, unlockMode,
|
|
passwd, extra_data);
|
|
}
|
|
|
|
if ((get_zip_status(IS_PRIVILEGED(dev), fd, extra_data) &
|
|
unlockMask) == 1) {
|
|
/* unlock first */
|
|
char *s;
|
|
passwd = getpass("Password: ");
|
|
if ((s = strchr(passwd, '\n'))) *s = '\0'; /* chomp */
|
|
if((ret=iomega_command(IS_PRIVILEGED(dev), fd,
|
|
unlockMode, passwd,
|
|
extra_data))){
|
|
if (ret == -1) perror("passwd: ");
|
|
else fprintf(stderr, "wrong password\n");
|
|
exit(1);
|
|
}
|
|
if((get_zip_status(IS_PRIVILEGED(dev),
|
|
fd, extra_data) &
|
|
unlockMask) == 1) {
|
|
fprintf(stderr, "wrong password\n");
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
if (newMode & 0x1) {
|
|
char first_try[_PASSWORD_LEN+1];
|
|
|
|
passwd = getpass("Enter new password:");
|
|
strncpy(first_try, passwd,_PASSWORD_LEN);
|
|
passwd = getpass("Re-type new password:");
|
|
if(strncmp(first_try, passwd, _PASSWORD_LEN)) {
|
|
fprintf(stderr,
|
|
"You misspelled it. Password not set.\n");
|
|
exit(1);
|
|
}
|
|
} else {
|
|
passwd = dummy;
|
|
dummy[0] = '\0';
|
|
}
|
|
|
|
if(newMode == ZIP_UNLOCK_TIL_EJECT)
|
|
newMode |= oldMode;
|
|
|
|
if((ret=iomega_command(IS_PRIVILEGED(dev), fd,
|
|
newMode, passwd, extra_data))){
|
|
if (ret == -1) perror("set passwd: ");
|
|
else fprintf(stderr, "password not changed\n");
|
|
exit(1);
|
|
}
|
|
#ifdef OS_linux
|
|
ioctl(fd, BLKRRPART); /* revalidate the disk, so that the
|
|
kernel notices that its writable
|
|
status has changed */
|
|
#endif
|
|
}
|
|
|
|
if (request & ZIP_STATUS) {
|
|
const char *unlocked;
|
|
|
|
if(oldMode & 8)
|
|
unlocked = " and unlocked until eject";
|
|
else
|
|
unlocked = "";
|
|
switch (oldMode & ~8) {
|
|
case ZIP_RW:
|
|
printf("Drive '%c:' is not write-protected\n",
|
|
drive);
|
|
break;
|
|
case ZIP_RO:
|
|
printf("Drive '%c:' is write-protected%s\n",
|
|
drive, unlocked);
|
|
break;
|
|
case ZIP_RO_PW:
|
|
printf("Drive '%c:' is password write-protected%s\n",
|
|
drive, unlocked);
|
|
break;
|
|
case ZIP_PW:
|
|
printf("Drive '%c:' is password protected%s\n",
|
|
drive, unlocked);
|
|
break;
|
|
default:
|
|
printf("Unknown protection mode %d of drive '%c:'\n",
|
|
oldMode, drive);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (request & ZIP_EJECT) {
|
|
if(request & ZIP_FORCE)
|
|
if(door_command(IS_PRIVILEGED(dev), fd,
|
|
SCSI_ALLOW_MEDIUM_REMOVAL, 0,
|
|
extra_data) < 0) {
|
|
perror("door unlock: ");
|
|
exit(1);
|
|
}
|
|
|
|
if(door_command(IS_PRIVILEGED(dev), fd,
|
|
SCSI_START_STOP, 1,
|
|
extra_data) < 0) {
|
|
perror("stop motor: ");
|
|
exit(1);
|
|
}
|
|
|
|
if(door_command(IS_PRIVILEGED(dev), fd,
|
|
SCSI_START_STOP, 2, extra_data) < 0) {
|
|
perror("eject: ");
|
|
exit(1);
|
|
}
|
|
if(door_command(IS_PRIVILEGED(dev), fd,
|
|
SCSI_START_STOP, 2, extra_data) < 0) {
|
|
perror("second eject: ");
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
close(fd);
|
|
exit(0);
|
|
}
|