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