diff options
Diffstat (limited to 'mzip.c')
-rw-r--r-- | mzip.c | 552 |
1 files changed, 552 insertions, 0 deletions
@@ -0,0 +1,552 @@ +/* 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) +{ + 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) +{ + void *extra_data; + 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 = 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]; + + 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 mispelled 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); +} |