summaryrefslogtreecommitdiff
path: root/floppyd_io.c
diff options
context:
space:
mode:
Diffstat (limited to 'floppyd_io.c')
-rw-r--r--floppyd_io.c679
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