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