1/* Copyright 1996 Grant R. Guenther, based on work of Itai Nahshon 2 * http://www.torque.net/ziptool.html 3 * Copyright 1997-2002,2007-2009 Alain Knaff. 4 * This file is part of mtools. 5 * 6 * Mtools is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation, either version 3 of the License, or 9 * (at your option) any later version. 10 * 11 * Mtools is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with Mtools. If not, see <http://www.gnu.org/licenses/>. 18 * 19 * mzip.c 20 * Iomega Zip/Jaz drive tool 21 * change protection mode and eject disk 22 */ 23 24/* mzip.c by Markus Gyger <mgyger@itr.ch> */ 25/* This code is based on ftp://gear.torque.net/pub/ziptool.c */ 26/* by Grant R. Guenther with the following copyright notice: */ 27 28/* (c) 1996 Grant R. Guenther, based on work of Itai Nahshon */ 29/* http://www.torque.net/ziptool.html */ 30 31 32/* Unprotect-till-eject modes and mount tests added 33 * by Ilya Ovchinnikov <ilya@socio.msu.su> 34 */ 35 36#include "sysincludes.h" 37#include "mtools.h" 38#include "scsi.h" 39 40#ifndef _PASSWORD_LEN 41#define _PASSWORD_LEN 33 42#endif 43 44#ifdef OS_linux 45 46#if __GLIBC__ >=2 47#include <sys/mount.h> 48#else 49#define _LINUX_KDEV_T_H 1 /* don't redefine MAJOR/MINOR */ 50#include <linux/fs.h> 51#endif 52 53#include "devices.h" 54 55#endif 56 57 58static int zip_cmd(int priv, int fd, unsigned char cdb[6], int clen, 59 scsi_io_mode_t mode, void *data, size_t len, 60 void *extra_data) 61{ 62 int r; 63 64 if(priv) 65 reclaim_privs(); 66 r = scsi_cmd(fd, cdb, clen, mode, data, len, extra_data); 67 if(priv) 68 drop_privs(); 69 return r; 70} 71 72static int test_mounted ( char *dev ) 73{ 74#ifdef HAVE_MNTENT_H 75 struct mntent *mnt; 76 struct MT_STAT st_dev, st_mnt; 77 FILE *mtab; 78/* 79 * Now check if any partition of this device is already mounted (this 80 * includes checking if the device is mounted under a different name). 81 */ 82 83 if (MT_STAT (dev, &st_dev)) { 84 fprintf (stderr, "%s: stat(%s) failed: %s.\n", 85 progname, dev, strerror (errno)); 86 exit(1); 87 } 88 89 if (!S_ISBLK (st_dev.st_mode)) /* not a block device, cannot 90 * be mounted */ 91 return 0; 92 93#ifndef _PATH_MOUNTED 94# define _PATH_MOUNTED "/etc/mtab" 95#endif 96 97 if ((mtab = setmntent (_PATH_MOUNTED, "r")) == NULL) { 98 fprintf (stderr, "%s: can't open %s.\n", 99 progname, _PATH_MOUNTED); 100 exit(1); 101 } 102 103 while ( ( mnt = getmntent (mtab) ) ) { 104 if (!mnt->mnt_fsname 105 106#ifdef MNTTYPE_SWAP 107 || !strcmp (mnt->mnt_type, MNTTYPE_SWAP) 108#endif 109#ifdef MNTTYPE_NFS 110 || !strcmp (mnt->mnt_type, MNTTYPE_NFS) 111#endif 112 || !strcmp (mnt->mnt_type, "proc") 113 || !strcmp (mnt->mnt_type, "smbfs") 114#ifdef MNTTYPE_IGNORE 115 || !strcmp (mnt->mnt_type, MNTTYPE_IGNORE) 116#endif 117 ) 118 continue; 119 120 if (MT_STAT (mnt->mnt_fsname, &st_mnt)) { 121 continue; 122 } 123 124 if (S_ISBLK (st_mnt.st_mode)) { 125#ifdef OS_linux 126 /* on Linux, warn also if the device is on the same 127 * partition */ 128 if (MAJOR(st_mnt.st_rdev) == MAJOR(st_dev.st_rdev) && 129 MINOR(st_mnt.st_rdev) >= MINOR(st_dev.st_rdev) && 130 MINOR(st_mnt.st_rdev) <= MINOR(st_dev.st_rdev)+15){ 131 fprintf (stderr, 132 "Device %s%d is mounted on %s.\n", 133 dev, 134 MINOR(st_mnt.st_rdev) - 135 MINOR(st_dev.st_rdev), 136 mnt->mnt_dir); 137#else 138 if(st_mnt.st_rdev != st_dev.st_rdev) { 139#endif 140 endmntent (mtab); 141 return 1; 142 } 143#if 0 144 } /* keep Emacs indentation happy */ 145#endif 146 } 147 } 148 endmntent (mtab); 149#endif 150 return 0; 151} 152 153 154static void usage(int ret) 155{ 156 fprintf(stderr, 157 "Mtools version %s, dated %s\n", 158 mversion, mdate); 159 fprintf(stderr, 160 "Usage: %s [-V] [-q] [-e] [-u] [-r|-w|-p|-x] [drive:]\n" 161 "\t-q print status\n" 162 "\t-e eject disk\n" 163 "\t-f eject disk even when mounted\n" 164 "\t-r write protected (read-only)\n" 165 "\t-w not write-protected (read-write)\n" 166 "\t-p password write protected\n" 167 "\t-x password protected\n" 168 "\t-u unprotect till disk ejecting\n", 169 progname); 170 exit(ret); 171} 172 173 174enum mode_t { 175 ZIP_RW = 0, 176 ZIP_RO = 2, 177 ZIP_RO_PW = 3, 178 ZIP_PW = 5, 179 ZIP_UNLOCK_TIL_EJECT = 8 180}; 181 182static enum mode_t get_zip_status(int priv, int fd, void *extra_data) 183{ 184 unsigned char status[128]; 185 unsigned char cdb[6] = { 0x06, 0, 0x02, 0, sizeof status, 0 }; 186 187 if (zip_cmd(priv, fd, cdb, 6, SCSI_IO_READ, 188 status, sizeof status, extra_data) == -1) { 189 perror("status: "); 190 exit(1); 191 } 192 return status[21] & 0xf; 193} 194 195 196static int short_command(int priv, int fd, int cmd1, int cmd2, 197 int cmd3, const char *data, void *extra_data) 198{ 199 unsigned char cdb[6] = { 0, 0, 0, 0, 0, 0 }; 200 201 cdb[0] = cmd1; 202 cdb[1] = cmd2; 203 cdb[4] = cmd3; 204 205 return zip_cmd(priv, fd, cdb, 6, SCSI_IO_WRITE, 206 (char *) data, data ? strlen(data) : 0, extra_data); 207} 208 209 210static int iomega_command(int priv, int fd, int mode, const char *data, 211 void *extra_data) 212{ 213 return short_command(priv, fd, 214 SCSI_IOMEGA, mode, data ? strlen(data) : 0, 215 data, extra_data); 216} 217 218static int door_command(int priv, int fd, int cmd1, int cmd2, 219 void *extra_data) 220{ 221 return short_command(priv, fd, cmd1, 0, cmd2, 0, extra_data); 222} 223 224void mzip(int argc, char **argv, int type) 225{ 226 void *extra_data; 227 int c; 228 char drive; 229 device_t *dev; 230 int fd = -1; 231 char name[EXPAND_BUF]; 232 enum { ZIP_NIX = 0, 233 ZIP_STATUS = 1 << 0, 234 ZIP_EJECT = 1 << 1, 235 ZIP_MODE_CHANGE = 1 << 2, 236 ZIP_FORCE = 1 << 3 237 } request = ZIP_NIX; 238 239 enum mode_t newMode = ZIP_RW; 240 enum mode_t oldMode = ZIP_RW; 241 242#define setMode(x) \ 243 if(request & ZIP_MODE_CHANGE) usage(1); \ 244 request |= ZIP_MODE_CHANGE; \ 245 newMode = x; \ 246 break; 247 248 /* get command line options */ 249 if(helpFlag(argc, argv)) 250 usage(0); 251 while ((c = getopt(argc, argv, "i:efpqrwxuh")) != EOF) { 252 switch (c) { 253 case 'i': 254 set_cmd_line_image(optarg, SCSI_FLAG); 255 break; 256 case 'f': 257 if (get_real_uid()) { 258 fprintf(stderr, 259 "Only root can use force. Sorry.\n"); 260 exit(1); 261 } 262 request |= ZIP_FORCE; 263 break; 264 case 'e': /* eject */ 265 request |= ZIP_EJECT; 266 break; 267 case 'q': /* status query */ 268 request |= ZIP_STATUS; 269 break; 270 271 case 'p': /* password read-only */ 272 setMode(ZIP_RO_PW); 273 case 'r': /* read-only */ 274 setMode(ZIP_RO); 275 case 'w': /* read-write */ 276 setMode(ZIP_RW); 277 case 'x': /* password protected */ 278 setMode(ZIP_PW); 279 case 'u': /* password protected */ 280 setMode(ZIP_UNLOCK_TIL_EJECT) 281 case 'h': 282 usage(0); 283 default: /* unrecognized */ 284 usage(1); 285 286 } 287 } 288 289 if (request == ZIP_NIX) request = ZIP_STATUS; /* default action */ 290 291 if (argc - optind > 1 || 292 (argc - optind == 1 && 293 (!argv[optind][0] || argv[optind][1] != ':'))) 294 usage(1); 295 296 drive = toupper(argc - optind == 1 ? argv[argc - 1][0] : ':'); 297 298 for (dev = devices; dev->name; dev++) { 299 unsigned char cdb[6] = { 0, 0, 0, 0, 0, 0 }; 300 struct { 301 char type, 302 type_modifier, 303 scsi_version, 304 data_format, 305 length, 306 reserved1[2], 307 capabilities, 308 vendor[8], 309 product[16], 310 revision[4], 311 vendor_specific[20], 312 reserved2[40]; 313 } inq_data; 314 315 if (dev->drive != drive) 316 continue; 317 expand(dev->name, name); 318 if ((request & (ZIP_MODE_CHANGE | ZIP_EJECT)) && 319 !(request & ZIP_FORCE) && 320 test_mounted(name)) { 321 fprintf(stderr, 322 "Can\'t change status of/eject mounted device\n"); 323 exit(1); 324 } 325 precmd(dev); 326 327 if(IS_PRIVILEGED(dev)) 328 reclaim_privs(); 329 fd = scsi_open(name, O_RDONLY 330#ifdef O_NDELAY 331 | O_NDELAY 332#endif 333 , 0644, 334 &extra_data); 335 if(IS_PRIVILEGED(dev)) 336 drop_privs(); 337 338 /* need readonly, else we can't 339 * open the drive on Solaris if 340 * write-protected */ 341 if (fd == -1) 342 continue; 343 closeExec(fd); 344 345 if (!(request & (ZIP_MODE_CHANGE | ZIP_STATUS))) 346 /* if no mode change or ZIP specific status is 347 * involved, the command (eject) is applicable 348 * on all drives */ 349 break; 350 351 cdb[0] = SCSI_INQUIRY; 352 cdb[4] = sizeof inq_data; 353 if (zip_cmd(IS_PRIVILEGED(dev), fd, cdb, 6, SCSI_IO_READ, 354 &inq_data, sizeof inq_data, extra_data) != 0) { 355 close(fd); 356 continue; 357 } 358 359#ifdef DEBUG 360 fprintf(stderr, "device: %s\n\tvendor: %.8s\n\tproduct: %.16s\n" 361 "\trevision: %.4s\n", name, inq_data.vendor, 362 inq_data.product, inq_data.revision); 363#endif /* DEBUG */ 364 365 if (strncasecmp("IOMEGA ", inq_data.vendor, 366 sizeof inq_data.vendor) || 367 (strncasecmp("ZIP 100 ", 368 inq_data.product, sizeof inq_data.product) && 369 strncasecmp("ZIP 100 PLUS ", 370 inq_data.product, sizeof inq_data.product) && 371 strncasecmp("ZIP 250 ", 372 inq_data.product, sizeof inq_data.product) && 373 strncasecmp("ZIP 750 ", 374 inq_data.product, sizeof inq_data.product) && 375 strncasecmp("JAZ 1GB ", 376 inq_data.product, sizeof inq_data.product) && 377 strncasecmp("JAZ 2GB ", 378 inq_data.product, sizeof inq_data.product))) { 379 380 /* debugging */ 381 fprintf(stderr,"Skipping drive with vendor='"); 382 fwrite(inq_data.vendor,1, sizeof(inq_data.vendor), 383 stderr); 384 fprintf(stderr,"' product='"); 385 fwrite(inq_data.product,1, sizeof(inq_data.product), 386 stderr); 387 fprintf(stderr,"'\n"); 388 /* end debugging */ 389 close(fd); 390 continue; 391 } 392 break; /* found Zip/Jaz drive */ 393 } 394 395 if (dev->drive == 0) { 396 fprintf(stderr, "%s: drive '%c:' is not a Zip or Jaz drive\n", 397 argv[0], drive); 398 exit(1); 399 } 400 401 if (request & (ZIP_MODE_CHANGE | ZIP_STATUS)) 402 oldMode = get_zip_status(IS_PRIVILEGED(dev), fd, extra_data); 403 404 if (request & ZIP_MODE_CHANGE) { 405 /* request temp unlock, and disk is already unlocked */ 406 if(newMode == ZIP_UNLOCK_TIL_EJECT && 407 (oldMode & ZIP_UNLOCK_TIL_EJECT)) 408 request &= ~ZIP_MODE_CHANGE; 409 410 /* no password change requested, and disk is already 411 * in the requested state */ 412 if(!(newMode & 0x01) && newMode == oldMode) 413 request &= ~ZIP_MODE_CHANGE; 414 } 415 416 if (request & ZIP_MODE_CHANGE) { 417 int ret; 418 enum mode_t unlockMode, unlockMask; 419 const char *passwd; 420 char dummy[1]; 421 422 if(newMode == ZIP_UNLOCK_TIL_EJECT) { 423 unlockMode = newMode | oldMode; 424 unlockMask = 9; 425 } else { 426 unlockMode = newMode & ~0x5; 427 unlockMask = 1; 428 } 429 430 if ((oldMode & unlockMask) == 1) { /* unlock first */ 431 char *s; 432 passwd = "APlaceForYourStuff"; 433 if ((s = strchr(passwd, '\n'))) *s = '\0'; /* chomp */ 434 iomega_command(IS_PRIVILEGED(dev), fd, unlockMode, 435 passwd, extra_data); 436 } 437 438 if ((get_zip_status(IS_PRIVILEGED(dev), fd, extra_data) & 439 unlockMask) == 1) { 440 /* unlock first */ 441 char *s; 442 passwd = getpass("Password: "); 443 if ((s = strchr(passwd, '\n'))) *s = '\0'; /* chomp */ 444 if((ret=iomega_command(IS_PRIVILEGED(dev), fd, 445 unlockMode, passwd, 446 extra_data))){ 447 if (ret == -1) perror("passwd: "); 448 else fprintf(stderr, "wrong password\n"); 449 exit(1); 450 } 451 if((get_zip_status(IS_PRIVILEGED(dev), 452 fd, extra_data) & 453 unlockMask) == 1) { 454 fprintf(stderr, "wrong password\n"); 455 exit(1); 456 } 457 } 458 459 if (newMode & 0x1) { 460 char first_try[_PASSWORD_LEN]; 461 462 passwd = getpass("Enter new password:"); 463 strncpy(first_try, passwd,_PASSWORD_LEN); 464 passwd = getpass("Re-type new password:"); 465 if(strncmp(first_try, passwd, _PASSWORD_LEN)) { 466 fprintf(stderr, 467 "You mispelled it. Password not set.\n"); 468 exit(1); 469 } 470 } else { 471 passwd = dummy; 472 dummy[0] = '\0'; 473 } 474 475 if(newMode == ZIP_UNLOCK_TIL_EJECT) 476 newMode |= oldMode; 477 478 if((ret=iomega_command(IS_PRIVILEGED(dev), fd, 479 newMode, passwd, extra_data))){ 480 if (ret == -1) perror("set passwd: "); 481 else fprintf(stderr, "password not changed\n"); 482 exit(1); 483 } 484#ifdef OS_linux 485 ioctl(fd, BLKRRPART); /* revalidate the disk, so that the 486 kernel notices that its writable 487 status has changed */ 488#endif 489 } 490 491 if (request & ZIP_STATUS) { 492 const char *unlocked; 493 494 if(oldMode & 8) 495 unlocked = " and unlocked until eject"; 496 else 497 unlocked = ""; 498 switch (oldMode & ~8) { 499 case ZIP_RW: 500 printf("Drive '%c:' is not write-protected\n", 501 drive); 502 break; 503 case ZIP_RO: 504 printf("Drive '%c:' is write-protected%s\n", 505 drive, unlocked); 506 break; 507 case ZIP_RO_PW: 508 printf("Drive '%c:' is password write-protected%s\n", 509 drive, unlocked); 510 break; 511 case ZIP_PW: 512 printf("Drive '%c:' is password protected%s\n", 513 drive, unlocked); 514 break; 515 default: 516 printf("Unknown protection mode %d of drive '%c:'\n", 517 oldMode, drive); 518 break; 519 } 520 } 521 522 if (request & ZIP_EJECT) { 523 if(request & ZIP_FORCE) 524 if(door_command(IS_PRIVILEGED(dev), fd, 525 SCSI_ALLOW_MEDIUM_REMOVAL, 0, 526 extra_data) < 0) { 527 perror("door unlock: "); 528 exit(1); 529 } 530 531 if(door_command(IS_PRIVILEGED(dev), fd, 532 SCSI_START_STOP, 1, 533 extra_data) < 0) { 534 perror("stop motor: "); 535 exit(1); 536 } 537 538 if(door_command(IS_PRIVILEGED(dev), fd, 539 SCSI_START_STOP, 2, extra_data) < 0) { 540 perror("eject: "); 541 exit(1); 542 } 543 if(door_command(IS_PRIVILEGED(dev), fd, 544 SCSI_START_STOP, 2, extra_data) < 0) { 545 perror("second eject: "); 546 exit(1); 547 } 548 } 549 550 close(fd); 551 exit(0); 552} 553