e670ff78cb24a3dc93a80cfe5f430302f172e359
[platform/upstream/mtools.git] / floppyd_io.c
1 /*  Copyright 1999 Peter Schlaile.
2  *  Copyright 1999-2002,2005-2007,2009 Alain Knaff.
3  *  This file is part of mtools.
4  *
5  *  Mtools is free software: you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation, either version 3 of the License, or
8  *  (at your option) any later version.
9  *
10  *  Mtools is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with Mtools.  If not, see <http://www.gnu.org/licenses/>.
17  *
18  * IO to the floppyd daemon running on the local X-Server Host
19  *
20  * written by:
21  *
22  * Peter Schlaile
23  *
24  * udbz@rz.uni-karlsruhe.de
25  *
26  */
27
28 #include "sysincludes.h"
29 #include "stream.h"
30 #include "mtools.h"
31 #include "msdos.h"
32 #include "scsi.h"
33 #include "partition.h"
34 #include "floppyd_io.h"
35
36 #ifdef USE_FLOPPYD
37
38 /* ######################################################################## */
39
40
41 typedef unsigned char Byte;
42 typedef unsigned long Dword;
43 typedef mt_off_t Qword;
44
45 const char* AuthErrors[] = {
46         "Auth success",
47         "Auth failed: Packet oversized",
48         "Auth failed: X-Cookie doesn't match",
49         "Auth failed: Wrong transmission protocol version",
50         "Auth failed: Device locked"
51         "Auth failed: Bad packet",
52         "Auth failed: I/O Error"
53 };
54
55
56 typedef struct RemoteFile_t {
57         Class_t *Class;
58         int refs;
59         Stream_t *Next;
60         Stream_t *Buffer;
61         int fd;
62         mt_off_t offset;
63         mt_off_t lastwhere;
64         mt_off_t size;
65         int version;
66         int capabilities;
67         int drive;
68 } RemoteFile_t;
69
70
71 #include "byte_dword.h"
72 #include "read_dword.h"
73
74
75 /* ######################################################################## */
76
77 static int authenticate_to_floppyd(RemoteFile_t *floppyd, int sock, char *display)
78 {
79         off_t filelen;
80         Byte buf[16];
81         const char *command[] = { "xauth", "xauth", "extract", "-", 0, 0 };
82         char *xcookie;
83         Dword errcode;
84         int l;
85
86         command[4] = display;
87
88         filelen=strlen(display);
89         filelen += 100;
90
91         xcookie = (char *) safe_malloc(filelen+4);
92         filelen = safePopenOut(command, xcookie+4, filelen);
93         if(filelen < 1)
94                 return AUTH_AUTHFAILED;
95
96         /* Version negotiation */
97         dword2byte(4,buf);
98         dword2byte(floppyd->version,buf+4);
99         if(write(sock, buf, 8) < 8)
100                 return AUTH_IO_ERROR;
101
102         if ( (l = read_dword(sock)) < 4) {
103                 return AUTH_WRONGVERSION;
104         }
105
106         errcode = read_dword(sock);
107
108         if (errcode != AUTH_SUCCESS) {
109                 return errcode;
110         }
111
112         if(l >= 8)
113                 floppyd->version = read_dword(sock);
114         if(l >= 12)
115                 floppyd->capabilities = read_dword(sock);
116
117         dword2byte(filelen, (Byte *)xcookie);
118         if(write(sock, xcookie, filelen+4) < filelen + 4)
119                 return AUTH_IO_ERROR;
120
121         if (read_dword(sock) != 4) {
122                 return AUTH_PACKETOVERSIZE;
123         }
124
125         errcode = read_dword(sock);
126         
127         return errcode;
128 }
129
130
131 static int floppyd_reader(int fd, char* buffer, int len) 
132 {
133         Dword errcode;
134         Dword gotlen;
135         int l;
136         int start;
137         Byte buf[16];
138
139         dword2byte(1, buf);
140         buf[4] = OP_READ;
141         dword2byte(4, buf+5);
142         dword2byte(len, buf+9);
143         if(write(fd, buf, 13) < 13)
144                 return AUTH_IO_ERROR;
145
146         if (read_dword(fd) != 8) {
147                 errno = EIO;
148                 return -1;
149         }
150
151         gotlen = read_dword(fd);
152         errcode = read_dword(fd);
153
154         if (gotlen != -1) {
155                 if (read_dword(fd) != gotlen) {
156                         errno = EIO;
157                         return -1;
158                 }
159                 for (start = 0, l = 0; start < gotlen; start += l) {
160                         l = read(fd, buffer+start, gotlen-start);
161                         if (l == 0) {
162                                 errno = EIO;
163                                 return -1;
164                         }
165                 }
166         } else {
167                 errno = errcode;
168         }
169         return gotlen;
170 }
171
172 static int floppyd_writer(int fd, char* buffer, int len) 
173 {
174         Dword errcode;
175         Dword gotlen;
176         Byte buf[16];
177
178         dword2byte(1, buf);
179         buf[4] = OP_WRITE;
180         dword2byte(len, buf+5);
181
182         if(write(fd, buf, 9) < 9)
183                 return AUTH_IO_ERROR;
184         if(write(fd, buffer, len) < len)
185                 return AUTH_IO_ERROR;
186         
187         if (read_dword(fd) != 8) {
188                 errno = EIO;
189                 return -1;
190         }
191
192         gotlen = read_dword(fd);
193         errcode = read_dword(fd);
194
195         errno = errcode;
196         if(errno != 0 && gotlen == 0) {
197             if (errno == EBADF)
198                 errno = EROFS;
199             gotlen = -1;
200         }
201
202         return gotlen;
203 }
204
205 static int floppyd_lseek(int fd, mt_off_t offset, int whence) 
206 {
207         Dword errcode;
208         Dword gotlen;
209         Byte buf[32];
210         
211         dword2byte(1, buf);
212         buf[4] = OP_SEEK;
213         
214         dword2byte(8, buf+5);
215         dword2byte(truncBytes32(offset), buf+9);
216         dword2byte(whence, buf+13);
217         
218         if(write(fd, buf, 17) < 17)
219                 return AUTH_IO_ERROR;
220        
221         if (read_dword(fd) != 8) {
222                 errno = EIO;
223                 return -1;
224         }
225
226         gotlen = read_dword(fd);
227         errcode = read_dword(fd);
228
229         errno = errcode;
230         
231         return gotlen;
232 }
233
234 static mt_off_t floppyd_lseek64(int fd, mt_off_t offset, int whence) 
235 {
236         Dword errcode;
237         Qword gotlen;
238         Byte buf[32];
239         
240         dword2byte(1, buf);
241         buf[4] = OP_SEEK64;
242         
243         dword2byte(12, buf+5);
244         qword2byte(offset, buf+9);
245         dword2byte(whence, buf+17);
246         
247         if(write(fd, buf, 21) < 21)
248                 return AUTH_IO_ERROR;
249        
250         if (read_dword(fd) != 12) {
251                 errno = EIO;
252                 return -1;
253         }
254
255         gotlen = read_qword(fd);
256         errcode = read_dword(fd);
257
258         errno = errcode;
259         
260         return gotlen;
261 }
262
263 static int floppyd_open(RemoteFile_t *This, int mode) 
264 {
265         Dword errcode;
266         Dword gotlen;
267         Byte buf[16];
268         
269         if(! (This->capabilities & FLOPPYD_CAP_EXPLICIT_OPEN) ) {
270                 /* floppyd has no "explicit seek" capabilities */
271                 return 0;
272         }
273
274         dword2byte(1, buf);
275         if((mode & O_ACCMODE) == O_RDONLY)
276                 buf[4] = OP_OPRO;
277         else
278                 buf[4] = OP_OPRW;
279         dword2byte(4, buf+5);
280         dword2byte(This->drive, buf+9);
281
282         if(write(This->fd, buf, 13) < 13)
283                 return AUTH_IO_ERROR;
284        
285         if (read_dword(This->fd) != 8) {
286                 errno = EIO;
287                 return -1;
288         }
289
290         gotlen = read_dword(This->fd);
291         errcode = read_dword(This->fd);
292
293         errno = errcode;
294         
295         return gotlen;
296 }
297
298
299 /* ######################################################################## */
300
301 typedef int (*iofn) (int, char *, int);
302
303 static int floppyd_io(Stream_t *Stream, char *buf, mt_off_t where, int len,
304                    iofn io)
305 {
306         DeclareThis(RemoteFile_t);
307         int ret;
308
309         where += This->offset;
310
311         if (where != This->lastwhere ){
312                 if(This->capabilities & FLOPPYD_CAP_LARGE_SEEK) {
313                         if(floppyd_lseek64( This->fd, where, SEEK_SET) < 0 ){
314                                 perror("floppyd_lseek64");
315                                 This->lastwhere = (mt_off_t) -1;
316                                 return -1;
317                         }
318                 } else {
319                         if(floppyd_lseek( This->fd, where, SEEK_SET) < 0 ){
320                                 perror("floppyd_lseek");
321                                 This->lastwhere = (mt_off_t) -1;
322                                 return -1;
323                         }
324                 }
325         }
326         ret = io(This->fd, buf, len);
327         if ( ret == -1 ){
328                 perror("floppyd_io");
329                 This->lastwhere = (mt_off_t) -1;
330                 return -1;
331         }
332         This->lastwhere = where + ret;
333         return ret;
334 }
335
336 static int floppyd_read(Stream_t *Stream, char *buf, mt_off_t where, size_t len)
337 {       
338         return floppyd_io(Stream, buf, where, len, (iofn) floppyd_reader);
339 }
340
341 static int floppyd_write(Stream_t *Stream, char *buf, mt_off_t where, size_t len)
342 {
343         return floppyd_io(Stream, buf, where, len, (iofn) floppyd_writer);
344 }
345
346 static int floppyd_flush(Stream_t *Stream)
347 {
348         Byte buf[16];
349
350         DeclareThis(RemoteFile_t);
351
352         dword2byte(1, buf);
353         buf[4] = OP_FLUSH;
354         dword2byte(1, buf+5);
355         buf[9] = '\0';
356
357         if(write(This->fd, buf, 10) < 10)
358                 return AUTH_IO_ERROR;
359
360         if (read_dword(This->fd) != 8) {
361                 errno = EIO;
362                 return -1;
363         }
364
365         read_dword(This->fd);
366         read_dword(This->fd);
367         return 0;
368 }
369
370 static int floppyd_free(Stream_t *Stream)
371 {
372         Byte buf[16];
373         int gotlen;
374         int errcode;
375         DeclareThis(RemoteFile_t);
376
377         if (This->fd > 2) {
378                 dword2byte(1, buf);
379                 buf[4] = OP_CLOSE;
380                 if(write(This->fd, buf, 5) < 5)
381                         return AUTH_IO_ERROR;
382                 shutdown(This->fd, 1);
383                 if (read_dword(This->fd) != 8) {
384                     errno = EIO;
385                     return -1;
386                 }
387                 
388                 gotlen = read_dword(This->fd);
389                 errcode = read_dword(This->fd);
390                 
391                 errno = errcode;
392
393                 close(This->fd);
394                 return gotlen;
395         } else {
396                 return 0;
397         }
398 }
399
400 static int floppyd_geom(Stream_t *Stream, struct device *dev, 
401                      struct device *orig_dev,
402                      int media, union bootsector *boot)
403 {
404         size_t tot_sectors;
405         int sect_per_track;
406         DeclareThis(RemoteFile_t);
407
408         dev->ssize = 2; /* allow for init_geom to change it */
409         dev->use_2m = 0x80; /* disable 2m mode to begin */
410
411         if(media == 0xf0 || media >= 0x100){            
412                 dev->heads = WORD(nheads);
413                 dev->sectors = WORD(nsect);
414                 tot_sectors = DWORD(bigsect);
415                 SET_INT(tot_sectors, WORD(psect));
416                 sect_per_track = dev->heads * dev->sectors;
417                 tot_sectors += sect_per_track - 1; /* round size up */
418                 dev->tracks = tot_sectors / sect_per_track;
419
420         } else if (media >= 0xf8){
421                 media &= 3;
422                 dev->heads = old_dos[media].heads;
423                 dev->tracks = old_dos[media].tracks;
424                 dev->sectors = old_dos[media].sectors;
425                 dev->ssize = 0x80;
426                 dev->use_2m = ~1;
427         } else {
428                 fprintf(stderr,"Unknown media type\n");
429                 exit(1);
430         }
431
432         This->size = (mt_off_t) 512 * dev->sectors * dev->tracks * dev->heads;
433
434         return 0;
435 }
436
437
438 static int floppyd_data(Stream_t *Stream, time_t *date, mt_size_t *size,
439                      int *type, int *address)
440 {
441         DeclareThis(RemoteFile_t);
442
443         if(date)
444                 /* unknown, and irrelevant anyways */
445                 *date = 0;
446         if(size)
447                 /* the size derived from the geometry */
448                 *size = (mt_size_t) This->size;
449         if(type)
450                 *type = 0; /* not a directory */
451         if(address)
452                 *address = 0;
453         return 0;
454 }
455
456 /* ######################################################################## */
457
458 static Class_t FloppydFileClass = {
459         floppyd_read, 
460         floppyd_write,
461         floppyd_flush,
462         floppyd_free,
463         floppyd_geom,
464         floppyd_data
465 };
466
467 /* ######################################################################## */
468
469 static int get_host_and_port_and_drive(const char* name, char** hostname,
470                                        char **display, short* port, int *drive)
471 {
472         char* newname = strdup(name);
473         char* p;
474         char* p2;
475
476         p = newname;
477         while (*p != '/' && *p) p++;
478         p2 = p;
479         if (*p) p++;
480         *p2 = 0;
481         
482         *port = FLOPPYD_DEFAULT_PORT;   
483         if(*p >= '0' && *p <= '9')
484           *port = strtoul(p, &p, 0);
485         if(*p == '/')
486           p++;
487         *drive = 0;
488         if(*p >= '0' && *p <= '9')
489           *drive = strtoul(p, &p, 0);
490
491         *display = strdup(newname);
492
493         p = newname;
494         while (*p != ':' && *p) p++;
495         p2 = p;
496         if (*p) p++;
497         *p2 = 0;
498
499         *port += atoi(p);  /* add display number to the port */
500
501         if (!*newname || strcmp(newname, "unix") == 0) {
502                 free(newname);
503                 newname = strdup("localhost");
504         }
505
506         *hostname = newname;
507         return 1;
508 }
509
510 /*
511  *  * Return the IP address of the specified host.
512  *  */
513 static IPaddr_t getipaddress(char *ipaddr)
514 {
515         
516         struct hostent  *host;
517         IPaddr_t        ip;
518
519         if (((ip = inet_addr(ipaddr)) == INADDR_NONE) &&
520             (strcmp(ipaddr, "255.255.255.255") != 0)) {
521                 
522                 if ((host = gethostbyname(ipaddr)) != NULL) {
523                         memcpy(&ip, host->h_addr, sizeof(ip));
524                 }
525                 
526                 endhostent();
527         }
528         
529 #ifdef DEBUG
530         fprintf(stderr, "IP lookup %s -> 0x%08lx\n", ipaddr, ip);
531 #endif
532           
533         return (ip);
534 }
535
536 /*
537  *  * Connect to the floppyd server.
538  *  */
539 static int connect_to_server(IPaddr_t ip, short port)
540 {
541         
542         struct sockaddr_in      addr;
543         int                     sock;
544         
545         /*
546          * Allocate a socket.
547          */
548         if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
549                 return (-1);
550         }
551         
552         /*
553          * Set the address to connect to.
554          */
555         
556         addr.sin_family = AF_INET;
557         addr.sin_port = htons(port);
558         addr.sin_addr.s_addr = ip;
559         
560         /*
561          * Connect our socket to the above address.
562          */
563         if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
564                 return (-1);
565         }
566
567         /*
568          * Set the keepalive socket option to on.
569          */
570         {
571                 int             on = 1;
572                 setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, 
573                            (char *)&on, sizeof(on));
574         }
575         
576         return (sock);
577 }
578
579 static int ConnectToFloppyd(RemoteFile_t *floppyd, const char* name, 
580                             char *errmsg);
581
582 Stream_t *FloppydOpen(struct device *dev, struct device *dev2,
583                       char *name, int mode, char *errmsg,
584                       int mode2, int locked, mt_size_t *maxSize)
585 {
586         RemoteFile_t *This;
587
588         if (!dev ||  !(dev->misc_flags & FLOPPYD_FLAG))
589                 return NULL;
590         
591         This = New(RemoteFile_t);
592         if (!This){
593                 printOom();
594                 return NULL;
595         }
596         This->Class = &FloppydFileClass;
597         This->Next = 0;
598         This->offset = 0;
599         This->lastwhere = 0;
600         This->refs = 1;
601         This->Buffer = 0;
602
603         This->fd = ConnectToFloppyd(This, name, errmsg);
604         if (This->fd == -1) {
605                 Free(This);
606                 return NULL;
607         }
608
609         if(floppyd_open(This, mode) < 0) {
610                 sprintf(errmsg,
611                         "Can't open remote drive: %s", strerror(errno));
612                 close(This->fd);
613                 Free(This);
614                 return NULL;
615         }
616
617         if(maxSize) {
618                 *maxSize = 
619                         (This->capabilities & FLOPPYD_CAP_LARGE_SEEK) ?
620                         max_off_t_seek : max_off_t_31;
621         }
622         return (Stream_t *) This;
623 }
624
625 static int ConnectToFloppyd(RemoteFile_t *floppyd, const char* name, 
626                             char *errmsg) 
627 {
628         char* hostname;
629         char* display;
630         short port;
631         int rval = get_host_and_port_and_drive(name, &hostname, &display, 
632                                                &port, &floppyd->drive);
633         int sock;
634         int reply;
635         
636         if (!rval) return -1;
637
638         floppyd->version = FLOPPYD_PROTOCOL_VERSION;
639         floppyd->capabilities = 0;
640         while(1) {
641                 sock = connect_to_server(getipaddress(hostname), port);
642                 
643                 if (sock == -1) {
644 #ifdef HAVE_SNPRINTF
645                         snprintf(errmsg, 200,
646                                  "Can't connect to floppyd server on %s, port %i (%s)!",
647                                  hostname, port, strerror(errno));
648 #else
649                         sprintf(errmsg,
650                                  "Can't connect to floppyd server on %s, port %i!",
651                                  hostname, port);
652 #endif
653                         return -1;
654                 }
655                 
656                 reply = authenticate_to_floppyd(floppyd, sock, display);
657                 if(floppyd->version == FLOPPYD_PROTOCOL_VERSION_OLD)
658                         break;
659                 if(reply == AUTH_WRONGVERSION) {
660                         /* fall back on old version */
661                         floppyd->version = FLOPPYD_PROTOCOL_VERSION_OLD;
662                         continue;
663                 }
664                 break;
665         }
666
667         if (reply != 0) {
668                 fprintf(stderr, 
669                         "Permission denied, authentication failed!\n"
670                         "%s\n", AuthErrors[reply]);
671                 return -1;
672         }
673
674         free(hostname);
675         free(display);
676
677         return sock;
678 }
679 #endif