Initial import package mtools: Programs for accessing MS-DOS disks without mounting...
[profile/ivi/mtools.git] / mzip.c
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
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
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)
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
174 enum 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
182 static 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
196 static 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
210 static 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
218 static 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
224 void 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 }