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