• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
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)58 static 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 
test_mounted(char * dev)72 static 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 
154 static void usage(int ret) NORETURN;
usage(int ret)155 static void usage(int ret)
156 {
157 	fprintf(stderr,
158 		"Mtools version %s, dated %s\n",
159 		mversion, mdate);
160 	fprintf(stderr,
161 		"Usage: %s [-V] [-q] [-e] [-u] [-r|-w|-p|-x] [drive:]\n"
162 		"\t-q print status\n"
163 		"\t-e eject disk\n"
164 		"\t-f eject disk even when mounted\n"
165 		"\t-r write protected (read-only)\n"
166 		"\t-w not write-protected (read-write)\n"
167 		"\t-p password write protected\n"
168 		"\t-x password protected\n"
169 		"\t-u unprotect till disk ejecting\n",
170 		progname);
171 	exit(ret);
172 }
173 
174 
175 enum mode_t {
176 	ZIP_RW = 0,
177 	ZIP_RO = 2,
178 	ZIP_RO_PW = 3,
179 	ZIP_PW = 5,
180 	ZIP_UNLOCK_TIL_EJECT = 8
181 };
182 
get_zip_status(int priv,int fd,void * extra_data)183 static enum mode_t get_zip_status(int priv, int fd, void *extra_data)
184 {
185 	unsigned char status[128];
186 	unsigned char cdb[6] = { 0x06, 0, 0x02, 0, sizeof status, 0 };
187 
188 	if (zip_cmd(priv, fd, cdb, 6, SCSI_IO_READ,
189 		    status, sizeof status, extra_data) == -1) {
190 		perror("status: ");
191 		exit(1);
192 	}
193 	return status[21] & 0xf;
194 }
195 
196 
short_command(int priv,int fd,int cmd1,int cmd2,int cmd3,const char * data,void * extra_data)197 static int short_command(int priv, int fd, int cmd1, int cmd2,
198 			 int cmd3, const char *data, void *extra_data)
199 {
200 	unsigned char cdb[6] = { 0, 0, 0, 0, 0, 0 };
201 
202 	cdb[0] = cmd1;
203 	cdb[1] = cmd2;
204 	cdb[4] = cmd3;
205 
206 	return zip_cmd(priv, fd, cdb, 6, SCSI_IO_WRITE,
207 		       (char *) data, data ? strlen(data) : 0, extra_data);
208 }
209 
210 
iomega_command(int priv,int fd,int mode,const char * data,void * extra_data)211 static int iomega_command(int priv, int fd, int mode, const char *data,
212 			  void *extra_data)
213 {
214 	return short_command(priv, fd,
215 			     SCSI_IOMEGA, mode, data ? strlen(data) : 0,
216 			     data, extra_data);
217 }
218 
door_command(int priv,int fd,int cmd1,int cmd2,void * extra_data)219 static int door_command(int priv, int fd, int cmd1, int cmd2,
220 			void *extra_data)
221 {
222 	return short_command(priv, fd, cmd1, 0, cmd2, 0, extra_data);
223 }
224 
225 void mzip(int argc, char **argv, int type UNUSEDP) NORETURN;
mzip(int argc,char ** argv,int type UNUSEDP)226 void mzip(int argc, char **argv, int type UNUSEDP)
227 {
228 	void *extra_data = NULL;
229 	int c;
230 	char drive;
231 	device_t *dev;
232 	int fd = -1;
233 	char name[EXPAND_BUF];
234 	enum { ZIP_NIX    =      0,
235 	       ZIP_STATUS = 1 << 0,
236 	       ZIP_EJECT  = 1 << 1,
237 	       ZIP_MODE_CHANGE = 1 << 2,
238 	       ZIP_FORCE  = 1 << 3
239 	} request = ZIP_NIX;
240 
241 	enum mode_t newMode = ZIP_RW;
242 	enum mode_t oldMode = ZIP_RW;
243 
244 #define setMode(x) \
245 	if(request & ZIP_MODE_CHANGE) usage(1); \
246 	request |= ZIP_MODE_CHANGE; \
247 	newMode = x; \
248 	break;
249 
250 	/* get command line options */
251 	if(helpFlag(argc, argv))
252 		usage(0);
253 	while ((c = getopt(argc, argv, "i:efpqrwxuh")) != EOF) {
254 		switch (c) {
255 			case 'i':
256 				set_cmd_line_image(optarg);
257 				break;
258 			case 'f':
259 				if (get_real_uid()) {
260 					fprintf(stderr,
261 						"Only root can use force. Sorry.\n");
262 					exit(1);
263 				}
264 				request |= ZIP_FORCE;
265 				break;
266 			case 'e': /* eject */
267 				request |= ZIP_EJECT;
268 				break;
269 			case 'q': /* status query */
270 				request |= ZIP_STATUS;
271 				break;
272 
273 			case 'p': /* password read-only */
274 				setMode(ZIP_RO_PW);
275 			case 'r': /* read-only */
276 				setMode(ZIP_RO);
277 			case 'w': /* read-write */
278 				setMode(ZIP_RW);
279 			case 'x': /* password protected */
280 				setMode(ZIP_PW);
281 			case 'u': /* password protected */
282 				setMode(ZIP_UNLOCK_TIL_EJECT)
283 			case 'h':
284 				usage(0);
285 			default:  /* unrecognized */
286 				usage(1);
287 
288 		}
289 	}
290 
291 	if (request == ZIP_NIX) request = ZIP_STATUS;  /* default action */
292 
293 	if (argc - optind > 1 ||
294 	    (argc - optind == 1 &&
295 	     (!argv[optind][0] || argv[optind][1] != ':')))
296 		usage(1);
297 
298 	drive = ch_toupper(argc - optind == 1 ? argv[argc - 1][0] : ':');
299 
300 	for (dev = devices; dev->name; dev++) {
301 		unsigned char cdb[6] = { 0, 0, 0, 0, 0, 0 };
302 		struct {
303 			char    type,
304 				type_modifier,
305 				scsi_version,
306 				data_format,
307 				length,
308 				reserved1[2],
309 				capabilities,
310 				vendor[8],
311 				product[16],
312 				revision[4],
313 				vendor_specific[20],
314 				reserved2[40];
315 		} inq_data;
316 
317 		if (dev->drive != drive)
318 			continue;
319 		expand(dev->name, name);
320 		if ((request & (ZIP_MODE_CHANGE | ZIP_EJECT)) &&
321 		    !(request & ZIP_FORCE) &&
322 		    test_mounted(name)) {
323 			fprintf(stderr,
324 				"Can\'t change status of/eject mounted device\n");
325 			exit(1);
326 		}
327 		precmd(dev);
328 
329 		if(IS_PRIVILEGED(dev))
330 			reclaim_privs();
331 		fd = scsi_open(name, O_RDONLY
332 #ifdef O_NDELAY
333 			       | O_NDELAY
334 #endif
335 			       , 0644,
336 			       &extra_data);
337 		if(IS_PRIVILEGED(dev))
338 			drop_privs();
339 
340 				/* need readonly, else we can't
341 				 * open the drive on Solaris if
342 				 * write-protected */
343 		if (fd == -1)
344 			continue;
345 		closeExec(fd);
346 
347 		if (!(request & (ZIP_MODE_CHANGE | ZIP_STATUS)))
348 			/* if no mode change or ZIP specific status is
349 			 * involved, the command (eject) is applicable
350 			 * on all drives */
351 			break;
352 
353 		cdb[0] = SCSI_INQUIRY;
354 		cdb[4] = sizeof inq_data;
355 		if (zip_cmd(IS_PRIVILEGED(dev), fd, cdb, 6, SCSI_IO_READ,
356 			    &inq_data, sizeof inq_data, extra_data) != 0) {
357 			close(fd);
358 			continue;
359 		}
360 
361 #ifdef DEBUG
362 		fprintf(stderr, "device: %s\n\tvendor: %.8s\n\tproduct: %.16s\n"
363 			"\trevision: %.4s\n", name, inq_data.vendor,
364 			inq_data.product, inq_data.revision);
365 #endif /* DEBUG */
366 
367 		if (strncasecmp("IOMEGA  ", inq_data.vendor,
368 				sizeof inq_data.vendor) ||
369 		    (strncasecmp("ZIP 100         ",
370 				 inq_data.product, sizeof inq_data.product) &&
371 		     strncasecmp("ZIP 100 PLUS    ",
372 				 inq_data.product, sizeof inq_data.product) &&
373 		     strncasecmp("ZIP 250         ",
374 				 inq_data.product, sizeof inq_data.product) &&
375 		     strncasecmp("ZIP 750         ",
376 				 inq_data.product, sizeof inq_data.product) &&
377 		     strncasecmp("JAZ 1GB         ",
378 				 inq_data.product, sizeof inq_data.product) &&
379 		     strncasecmp("JAZ 2GB         ",
380 				 inq_data.product, sizeof inq_data.product))) {
381 
382 			/* debugging */
383 			fprintf(stderr,"Skipping drive with vendor='");
384 			fwrite(inq_data.vendor,1, sizeof(inq_data.vendor),
385 			       stderr);
386 			fprintf(stderr,"' product='");
387 			fwrite(inq_data.product,1, sizeof(inq_data.product),
388 			       stderr);
389 			fprintf(stderr,"'\n");
390 			/* end debugging */
391 			close(fd);
392 			continue;
393 		}
394 		break;  /* found Zip/Jaz drive */
395 	}
396 
397 	if (dev->drive == 0) {
398 		fprintf(stderr, "%s: drive '%c:' is not a Zip or Jaz drive\n",
399 			argv[0], drive);
400 		exit(1);
401 	}
402 
403 	if (request & (ZIP_MODE_CHANGE | ZIP_STATUS))
404 		oldMode = get_zip_status(IS_PRIVILEGED(dev), fd, extra_data);
405 
406 	if (request & ZIP_MODE_CHANGE) {
407 				/* request temp unlock, and disk is already unlocked */
408 		if(newMode == ZIP_UNLOCK_TIL_EJECT &&
409 		   (oldMode & ZIP_UNLOCK_TIL_EJECT))
410 			request &= ~ZIP_MODE_CHANGE;
411 
412 				/* no password change requested, and disk is already
413 				 * in the requested state */
414 		if(!(newMode & 0x01) && newMode == oldMode)
415 			request &= ~ZIP_MODE_CHANGE;
416 	}
417 
418 	if (request & ZIP_MODE_CHANGE) {
419 		int ret;
420 		enum mode_t unlockMode, unlockMask;
421 		const char *passwd;
422 		char dummy[1];
423 
424 		if(newMode == ZIP_UNLOCK_TIL_EJECT) {
425 			unlockMode = newMode | oldMode;
426 			unlockMask = 9;
427 		} else {
428 			unlockMode = newMode & ~0x5;
429 			unlockMask = 1;
430 		}
431 
432 		if ((oldMode & unlockMask) == 1) {  /* unlock first */
433 			char *s;
434 			passwd = "APlaceForYourStuff";
435 			if ((s = strchr(passwd, '\n'))) *s = '\0';  /* chomp */
436 			iomega_command(IS_PRIVILEGED(dev), fd, unlockMode,
437 				       passwd, extra_data);
438 		}
439 
440 		if ((get_zip_status(IS_PRIVILEGED(dev), fd, extra_data) &
441 		     unlockMask) == 1) {
442 			/* unlock first */
443 			char *s;
444 			passwd = getpass("Password: ");
445 			if ((s = strchr(passwd, '\n'))) *s = '\0';  /* chomp */
446 			if((ret=iomega_command(IS_PRIVILEGED(dev), fd,
447 					       unlockMode, passwd,
448 					       extra_data))){
449 				if (ret == -1) perror("passwd: ");
450 				else fprintf(stderr, "wrong password\n");
451 				exit(1);
452 			}
453 			if((get_zip_status(IS_PRIVILEGED(dev),
454 					   fd, extra_data) &
455 			    unlockMask) == 1) {
456 				fprintf(stderr, "wrong password\n");
457 				exit(1);
458 			}
459 		}
460 
461 		if (newMode & 0x1) {
462 			char first_try[_PASSWORD_LEN+1];
463 
464 			passwd = getpass("Enter new password:");
465 			strncpy(first_try, passwd,_PASSWORD_LEN);
466 			passwd = getpass("Re-type new password:");
467 			if(strncmp(first_try, passwd, _PASSWORD_LEN)) {
468 				fprintf(stderr,
469 					"You misspelled it. Password not set.\n");
470 				exit(1);
471 			}
472 		} else {
473 			passwd = dummy;
474 			dummy[0] = '\0';
475 		}
476 
477 		if(newMode == ZIP_UNLOCK_TIL_EJECT)
478 			newMode |= oldMode;
479 
480 		if((ret=iomega_command(IS_PRIVILEGED(dev), fd,
481 				       newMode, passwd, extra_data))){
482 			if (ret == -1) perror("set passwd: ");
483 			else fprintf(stderr, "password not changed\n");
484 			exit(1);
485 		}
486 #ifdef OS_linux
487 		ioctl(fd, BLKRRPART); /* revalidate the disk, so that the
488 					 kernel notices that its writable
489 					 status has changed */
490 #endif
491 	}
492 
493 	if (request & ZIP_STATUS) {
494 		const char *unlocked;
495 
496 		if(oldMode & 8)
497 			unlocked = " and unlocked until eject";
498 		else
499 			unlocked = "";
500 		switch (oldMode & ~8) {
501 			case ZIP_RW:
502 				printf("Drive '%c:' is not write-protected\n",
503 				       drive);
504 				break;
505 			case ZIP_RO:
506 				printf("Drive '%c:' is write-protected%s\n",
507 				       drive, unlocked);
508 				break;
509 			case ZIP_RO_PW:
510 				printf("Drive '%c:' is password write-protected%s\n",
511 				       drive, unlocked);
512 				break;
513 			case ZIP_PW:
514 				printf("Drive '%c:' is password protected%s\n",
515 				       drive, unlocked);
516 				break;
517 			default:
518 				printf("Unknown protection mode %d of drive '%c:'\n",
519 				       oldMode, drive);
520 				break;
521 		}
522 	}
523 
524 	if (request & ZIP_EJECT) {
525 		if(request & ZIP_FORCE)
526 			if(door_command(IS_PRIVILEGED(dev), fd,
527 					SCSI_ALLOW_MEDIUM_REMOVAL, 0,
528 					extra_data) < 0) {
529 				perror("door unlock: ");
530 				exit(1);
531 			}
532 
533 		if(door_command(IS_PRIVILEGED(dev), fd,
534 				SCSI_START_STOP, 1,
535 				extra_data) < 0) {
536 			perror("stop motor: ");
537 			exit(1);
538 		}
539 
540 		if(door_command(IS_PRIVILEGED(dev), fd,
541 				SCSI_START_STOP, 2, extra_data) < 0) {
542 			perror("eject: ");
543 			exit(1);
544 		}
545 		if(door_command(IS_PRIVILEGED(dev), fd,
546 				SCSI_START_STOP, 2, extra_data) < 0) {
547 			perror("second eject: ");
548 			exit(1);
549 		}
550 	}
551 
552 	close(fd);
553 	exit(0);
554 }
555