| /* 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); |
| } |