diff options
Diffstat (limited to 'floppyd_io.c')
-rw-r--r-- | floppyd_io.c | 679 |
1 files changed, 679 insertions, 0 deletions
diff --git a/floppyd_io.c b/floppyd_io.c new file mode 100644 index 0000000..d8e0fdb --- /dev/null +++ b/floppyd_io.c @@ -0,0 +1,679 @@ +/* Copyright 1999 Peter Schlaile. + * Copyright 1999-2002,2005-2007,2009 Alain Knaff. + * This file is part of mtools. + * + * Mtools is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Mtools is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Mtools. If not, see <http://www.gnu.org/licenses/>. + * + * IO to the floppyd daemon running on the local X-Server Host + * + * written by: + * + * Peter Schlaile + * + * udbz@rz.uni-karlsruhe.de + * + */ + +#include "sysincludes.h" +#include "stream.h" +#include "mtools.h" +#include "msdos.h" +#include "scsi.h" +#include "partition.h" +#include "floppyd_io.h" + +#ifdef USE_FLOPPYD + +/* ######################################################################## */ + + +typedef unsigned char Byte; +typedef unsigned long Dword; +typedef mt_off_t Qword; + +const char* AuthErrors[] = { + "Auth success", + "Auth failed: Packet oversized", + "Auth failed: X-Cookie doesn't match", + "Auth failed: Wrong transmission protocol version", + "Auth failed: Device locked" + "Auth failed: Bad packet", + "Auth failed: I/O Error" +}; + + +typedef struct RemoteFile_t { + Class_t *Class; + int refs; + Stream_t *Next; + Stream_t *Buffer; + int fd; + mt_off_t offset; + mt_off_t lastwhere; + mt_off_t size; + int version; + int capabilities; + int drive; +} RemoteFile_t; + + +#include "byte_dword.h" +#include "read_dword.h" + + +/* ######################################################################## */ + +static int authenticate_to_floppyd(RemoteFile_t *floppyd, int sock, char *display) +{ + off_t filelen; + Byte buf[16]; + const char *command[] = { "xauth", "xauth", "extract", "-", 0, 0 }; + char *xcookie; + Dword errcode; + int l; + + command[4] = display; + + filelen=strlen(display); + filelen += 100; + + xcookie = (char *) safe_malloc(filelen+4); + filelen = safePopenOut(command, xcookie+4, filelen); + if(filelen < 1) + return AUTH_AUTHFAILED; + + /* Version negotiation */ + dword2byte(4,buf); + dword2byte(floppyd->version,buf+4); + if(write(sock, buf, 8) < 8) + return AUTH_IO_ERROR; + + if ( (l = read_dword(sock)) < 4) { + return AUTH_WRONGVERSION; + } + + errcode = read_dword(sock); + + if (errcode != AUTH_SUCCESS) { + return errcode; + } + + if(l >= 8) + floppyd->version = read_dword(sock); + if(l >= 12) + floppyd->capabilities = read_dword(sock); + + dword2byte(filelen, (Byte *)xcookie); + if(write(sock, xcookie, filelen+4) < filelen + 4) + return AUTH_IO_ERROR; + + if (read_dword(sock) != 4) { + return AUTH_PACKETOVERSIZE; + } + + errcode = read_dword(sock); + + return errcode; +} + + +static int floppyd_reader(int fd, char* buffer, int len) +{ + Dword errcode; + Dword gotlen; + int l; + int start; + Byte buf[16]; + + dword2byte(1, buf); + buf[4] = OP_READ; + dword2byte(4, buf+5); + dword2byte(len, buf+9); + if(write(fd, buf, 13) < 13) + return AUTH_IO_ERROR; + + if (read_dword(fd) != 8) { + errno = EIO; + return -1; + } + + gotlen = read_dword(fd); + errcode = read_dword(fd); + + if (gotlen != -1) { + if (read_dword(fd) != gotlen) { + errno = EIO; + return -1; + } + for (start = 0, l = 0; start < gotlen; start += l) { + l = read(fd, buffer+start, gotlen-start); + if (l == 0) { + errno = EIO; + return -1; + } + } + } else { + errno = errcode; + } + return gotlen; +} + +static int floppyd_writer(int fd, char* buffer, int len) +{ + Dword errcode; + Dword gotlen; + Byte buf[16]; + + dword2byte(1, buf); + buf[4] = OP_WRITE; + dword2byte(len, buf+5); + + if(write(fd, buf, 9) < 9) + return AUTH_IO_ERROR; + if(write(fd, buffer, len) < len) + return AUTH_IO_ERROR; + + if (read_dword(fd) != 8) { + errno = EIO; + return -1; + } + + gotlen = read_dword(fd); + errcode = read_dword(fd); + + errno = errcode; + if(errno != 0 && gotlen == 0) { + if (errno == EBADF) + errno = EROFS; + gotlen = -1; + } + + return gotlen; +} + +static int floppyd_lseek(int fd, mt_off_t offset, int whence) +{ + Dword errcode; + Dword gotlen; + Byte buf[32]; + + dword2byte(1, buf); + buf[4] = OP_SEEK; + + dword2byte(8, buf+5); + dword2byte(truncBytes32(offset), buf+9); + dword2byte(whence, buf+13); + + if(write(fd, buf, 17) < 17) + return AUTH_IO_ERROR; + + if (read_dword(fd) != 8) { + errno = EIO; + return -1; + } + + gotlen = read_dword(fd); + errcode = read_dword(fd); + + errno = errcode; + + return gotlen; +} + +static mt_off_t floppyd_lseek64(int fd, mt_off_t offset, int whence) +{ + Dword errcode; + Qword gotlen; + Byte buf[32]; + + dword2byte(1, buf); + buf[4] = OP_SEEK64; + + dword2byte(12, buf+5); + qword2byte(offset, buf+9); + dword2byte(whence, buf+17); + + if(write(fd, buf, 21) < 21) + return AUTH_IO_ERROR; + + if (read_dword(fd) != 12) { + errno = EIO; + return -1; + } + + gotlen = read_qword(fd); + errcode = read_dword(fd); + + errno = errcode; + + return gotlen; +} + +static int floppyd_open(RemoteFile_t *This, int mode) +{ + Dword errcode; + Dword gotlen; + Byte buf[16]; + + if(! (This->capabilities & FLOPPYD_CAP_EXPLICIT_OPEN) ) { + /* floppyd has no "explicit seek" capabilities */ + return 0; + } + + dword2byte(1, buf); + if((mode & O_ACCMODE) == O_RDONLY) + buf[4] = OP_OPRO; + else + buf[4] = OP_OPRW; + dword2byte(4, buf+5); + dword2byte(This->drive, buf+9); + + if(write(This->fd, buf, 13) < 13) + return AUTH_IO_ERROR; + + if (read_dword(This->fd) != 8) { + errno = EIO; + return -1; + } + + gotlen = read_dword(This->fd); + errcode = read_dword(This->fd); + + errno = errcode; + + return gotlen; +} + + +/* ######################################################################## */ + +typedef int (*iofn) (int, char *, int); + +static int floppyd_io(Stream_t *Stream, char *buf, mt_off_t where, int len, + iofn io) +{ + DeclareThis(RemoteFile_t); + int ret; + + where += This->offset; + + if (where != This->lastwhere ){ + if(This->capabilities & FLOPPYD_CAP_LARGE_SEEK) { + if(floppyd_lseek64( This->fd, where, SEEK_SET) < 0 ){ + perror("floppyd_lseek64"); + This->lastwhere = (mt_off_t) -1; + return -1; + } + } else { + if(floppyd_lseek( This->fd, where, SEEK_SET) < 0 ){ + perror("floppyd_lseek"); + This->lastwhere = (mt_off_t) -1; + return -1; + } + } + } + ret = io(This->fd, buf, len); + if ( ret == -1 ){ + perror("floppyd_io"); + This->lastwhere = (mt_off_t) -1; + return -1; + } + This->lastwhere = where + ret; + return ret; +} + +static int floppyd_read(Stream_t *Stream, char *buf, mt_off_t where, size_t len) +{ + return floppyd_io(Stream, buf, where, len, (iofn) floppyd_reader); +} + +static int floppyd_write(Stream_t *Stream, char *buf, mt_off_t where, size_t len) +{ + return floppyd_io(Stream, buf, where, len, (iofn) floppyd_writer); +} + +static int floppyd_flush(Stream_t *Stream) +{ + Byte buf[16]; + + DeclareThis(RemoteFile_t); + + dword2byte(1, buf); + buf[4] = OP_FLUSH; + dword2byte(1, buf+5); + buf[9] = '\0'; + + if(write(This->fd, buf, 10) < 10) + return AUTH_IO_ERROR; + + if (read_dword(This->fd) != 8) { + errno = EIO; + return -1; + } + + read_dword(This->fd); + read_dword(This->fd); + return 0; +} + +static int floppyd_free(Stream_t *Stream) +{ + Byte buf[16]; + int gotlen; + int errcode; + DeclareThis(RemoteFile_t); + + if (This->fd > 2) { + dword2byte(1, buf); + buf[4] = OP_CLOSE; + if(write(This->fd, buf, 5) < 5) + return AUTH_IO_ERROR; + shutdown(This->fd, 1); + if (read_dword(This->fd) != 8) { + errno = EIO; + return -1; + } + + gotlen = read_dword(This->fd); + errcode = read_dword(This->fd); + + errno = errcode; + + close(This->fd); + return gotlen; + } else { + return 0; + } +} + +static int floppyd_geom(Stream_t *Stream, struct device *dev, + struct device *orig_dev UNUSEDP, + int media, union bootsector *boot) +{ + size_t tot_sectors; + int sect_per_track; + DeclareThis(RemoteFile_t); + + dev->ssize = 2; /* allow for init_geom to change it */ + dev->use_2m = 0x80; /* disable 2m mode to begin */ + + if(media == 0xf0 || media >= 0x100){ + dev->heads = WORD(nheads); + dev->sectors = WORD(nsect); + tot_sectors = DWORD(bigsect); + SET_INT(tot_sectors, WORD(psect)); + sect_per_track = dev->heads * dev->sectors; + tot_sectors += sect_per_track - 1; /* round size up */ + dev->tracks = tot_sectors / sect_per_track; + + } else if (media >= 0xf8){ + media &= 3; + dev->heads = old_dos[media].heads; + dev->tracks = old_dos[media].tracks; + dev->sectors = old_dos[media].sectors; + dev->ssize = 0x80; + dev->use_2m = ~1; + } else { + fprintf(stderr,"Unknown media type\n"); + exit(1); + } + + This->size = (mt_off_t) 512 * dev->sectors * dev->tracks * dev->heads; + + return 0; +} + + +static int floppyd_data(Stream_t *Stream, time_t *date, mt_size_t *size, + int *type, int *address) +{ + DeclareThis(RemoteFile_t); + + if(date) + /* unknown, and irrelevant anyways */ + *date = 0; + if(size) + /* the size derived from the geometry */ + *size = (mt_size_t) This->size; + if(type) + *type = 0; /* not a directory */ + if(address) + *address = 0; + return 0; +} + +/* ######################################################################## */ + +static Class_t FloppydFileClass = { + floppyd_read, + floppyd_write, + floppyd_flush, + floppyd_free, + floppyd_geom, + floppyd_data +}; + +/* ######################################################################## */ + +static int get_host_and_port_and_drive(const char* name, char** hostname, + char **display, short* port, int *drive) +{ + char* newname = strdup(name); + char* p; + char* p2; + + p = newname; + while (*p != '/' && *p) p++; + p2 = p; + if (*p) p++; + *p2 = 0; + + *port = FLOPPYD_DEFAULT_PORT; + if(*p >= '0' && *p <= '9') + *port = strtoul(p, &p, 0); + if(*p == '/') + p++; + *drive = 0; + if(*p >= '0' && *p <= '9') + *drive = strtoul(p, &p, 0); + + *display = strdup(newname); + + p = newname; + while (*p != ':' && *p) p++; + p2 = p; + if (*p) p++; + *p2 = 0; + + *port += atoi(p); /* add display number to the port */ + + if (!*newname || strcmp(newname, "unix") == 0) { + free(newname); + newname = strdup("localhost"); + } + + *hostname = newname; + return 1; +} + +/* + * * Return the IP address of the specified host. + * */ +static IPaddr_t getipaddress(char *ipaddr) +{ + + struct hostent *host; + IPaddr_t ip; + + if (((ip = inet_addr(ipaddr)) == INADDR_NONE) && + (strcmp(ipaddr, "255.255.255.255") != 0)) { + + if ((host = gethostbyname(ipaddr)) != NULL) { + memcpy(&ip, host->h_addr, sizeof(ip)); + } + + endhostent(); + } + +#ifdef DEBUG + fprintf(stderr, "IP lookup %s -> 0x%08lx\n", ipaddr, ip); +#endif + + return (ip); +} + +/* + * * Connect to the floppyd server. + * */ +static int connect_to_server(IPaddr_t ip, short port) +{ + + struct sockaddr_in addr; + int sock; + + /* + * Allocate a socket. + */ + if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + return (-1); + } + + /* + * Set the address to connect to. + */ + + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + addr.sin_addr.s_addr = ip; + + /* + * Connect our socket to the above address. + */ + if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + return (-1); + } + + /* + * Set the keepalive socket option to on. + */ + { + int on = 1; + setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, + (char *)&on, sizeof(on)); + } + + return (sock); +} + +static int ConnectToFloppyd(RemoteFile_t *floppyd, const char* name, + char *errmsg); + +Stream_t *FloppydOpen(struct device *dev, + char *name, int mode, char *errmsg, + mt_size_t *maxSize) +{ + RemoteFile_t *This; + + if (!dev || !(dev->misc_flags & FLOPPYD_FLAG)) + return NULL; + + This = New(RemoteFile_t); + if (!This){ + printOom(); + return NULL; + } + This->Class = &FloppydFileClass; + This->Next = 0; + This->offset = 0; + This->lastwhere = 0; + This->refs = 1; + This->Buffer = 0; + + This->fd = ConnectToFloppyd(This, name, errmsg); + if (This->fd == -1) { + Free(This); + return NULL; + } + + if(floppyd_open(This, mode) < 0) { + sprintf(errmsg, + "Can't open remote drive: %s", strerror(errno)); + close(This->fd); + Free(This); + return NULL; + } + + if(maxSize) { + *maxSize = + (This->capabilities & FLOPPYD_CAP_LARGE_SEEK) ? + max_off_t_seek : max_off_t_31; + } + return (Stream_t *) This; +} + +static int ConnectToFloppyd(RemoteFile_t *floppyd, const char* name, + char *errmsg) +{ + char* hostname; + char* display; + short port; + int rval = get_host_and_port_and_drive(name, &hostname, &display, + &port, &floppyd->drive); + int sock; + int reply; + + if (!rval) return -1; + + floppyd->version = FLOPPYD_PROTOCOL_VERSION; + floppyd->capabilities = 0; + while(1) { + sock = connect_to_server(getipaddress(hostname), port); + + if (sock == -1) { +#ifdef HAVE_SNPRINTF + snprintf(errmsg, 200, + "Can't connect to floppyd server on %s, port %i (%s)!", + hostname, port, strerror(errno)); +#else + sprintf(errmsg, + "Can't connect to floppyd server on %s, port %i!", + hostname, port); +#endif + return -1; + } + + reply = authenticate_to_floppyd(floppyd, sock, display); + if(floppyd->version == FLOPPYD_PROTOCOL_VERSION_OLD) + break; + if(reply == AUTH_WRONGVERSION) { + /* fall back on old version */ + floppyd->version = FLOPPYD_PROTOCOL_VERSION_OLD; + continue; + } + break; + } + + if (reply != 0) { + fprintf(stderr, + "Permission denied, authentication failed!\n" + "%s\n", AuthErrors[reply]); + return -1; + } + + free(hostname); + free(display); + + return sock; +} +#endif |