| /* 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 |
| * |
| * [email protected] |
| * |
| */ |
| |
| #include "sysincludes.h" |
| #include "stream.h" |
| #include "mtools.h" |
| #include "msdos.h" |
| #include "scsi.h" |
| #include "floppyd_io.h" |
| |
| /* ######################################################################## */ |
| |
| |
| static 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 { |
| struct Stream_t head; |
| |
| int fd; |
| mt_off_t offset; |
| mt_off_t lastwhere; |
| mt_off_t size; |
| unsigned int version; |
| unsigned int capabilities; |
| int drive; |
| } RemoteFile_t; |
| |
| |
| #include "byte_dword.h" |
| #include "read_dword.h" |
| |
| |
| /* ######################################################################## */ |
| |
| static unsigned int authenticate_to_floppyd(RemoteFile_t *floppyd, |
| int sock, char *display) |
| { |
| size_t cookielen; |
| uint16_t filelen; |
| ssize_t newlen; |
| Byte buf[16]; |
| const char *command[] = { "xauth", "xauth", "extract", "-", 0, 0 }; |
| char *xcookie; |
| Dword errcode; |
| int l; |
| |
| command[4] = display; |
| |
| cookielen=strlen(display); |
| cookielen += 100; |
| |
| xcookie = (char *) safe_malloc(cookielen+4); |
| newlen = safePopenOut(command, xcookie+4, cookielen); |
| if(newlen < 1 || newlen > UINT16_MAX) |
| return AUTH_AUTHFAILED; |
| filelen = (uint16_t) newlen; |
| |
| /* Version negotiation */ |
| dword2byte(4,buf); |
| dword2byte(floppyd->version,buf+4); |
| if(write(sock, buf, 8) < 8) |
| return AUTH_IO_ERROR; |
| |
| if ( (l = (int) 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) < ((ssize_t) (filelen + 4))) |
| return AUTH_IO_ERROR; |
| |
| if (read_dword(sock) != 4) { |
| return AUTH_PACKETOVERSIZE; |
| } |
| |
| errcode = read_dword(sock); |
| |
| return errcode; |
| } |
| |
| |
| static ssize_t floppyd_reader(int fd, char* buffer, uint32_t len) |
| { |
| Dword errcode; |
| Dword gotlen; |
| 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 != (Dword) -1) { |
| size_t l; |
| unsigned int start; |
| if (read_dword(fd) != gotlen) { |
| errno = EIO; |
| return -1; |
| } |
| for (start = 0, l = 0; start < gotlen; start += l) { |
| ssize_t ret = read(fd, buffer+start, gotlen-start); |
| if( ret < 0) |
| return -1; |
| if (ret == 0) { |
| errno = EIO; |
| return -1; |
| } |
| l = (size_t) ret; |
| } |
| } else { |
| errno = (int) errcode; |
| } |
| return (ssize_t) gotlen; |
| } |
| |
| static ssize_t floppyd_writer(int fd, char* buffer, uint32_t len) |
| { |
| int errcode; |
| int32_t gotlen; |
| Byte buf[16]; |
| ssize_t ret; |
| |
| dword2byte(1, buf); |
| buf[4] = OP_WRITE; |
| dword2byte(len, buf+5); |
| |
| cork(fd, 1); |
| if(write(fd, buf, 9) < 9) |
| return AUTH_IO_ERROR; |
| ret = write(fd, buffer, len); |
| if(ret == -1 || (size_t) ret < len) |
| return AUTH_IO_ERROR; |
| cork(fd, 0); |
| |
| if (read_dword(fd) != 8) { |
| errno = EIO; |
| return -1; |
| } |
| |
| gotlen = read_sdword(fd); |
| errcode = read_sdword(fd); |
| |
| errno = errcode; |
| if(errno != 0 && gotlen == 0) { |
| if (errno == EBADF) |
| errno = EROFS; |
| gotlen = -1; |
| } |
| |
| return gotlen; |
| } |
| |
| static int floppyd_lseek(int fd, int32_t offset, int whence) |
| { |
| int errcode; |
| int gotlen; |
| Byte buf[32]; |
| |
| dword2byte(1, buf); |
| buf[4] = OP_SEEK; |
| |
| dword2byte(8, buf+5); |
| sdword2byte(offset, buf+9); |
| sdword2byte(whence, buf+13); |
| |
| if(write(fd, buf, 17) < 17) |
| return AUTH_IO_ERROR; |
| |
| if (read_dword(fd) != 8) { |
| errno = EIO; |
| return -1; |
| } |
| |
| gotlen = read_sdword(fd); |
| errcode = read_sdword(fd); |
| |
| errno = errcode; |
| |
| return gotlen; |
| } |
| |
| #if SIZEOF_OFF_T >= 8 |
| static mt_off_t floppyd_lseek64(int fd, mt_off_t offset, int whence) |
| { |
| int errcode; |
| struct SQwordRet gotlen; |
| Byte buf[32]; |
| |
| dword2byte(1, buf); |
| buf[4] = OP_SEEK64; |
| |
| dword2byte(12, buf+5); |
| qword2byte((uint32_t)offset, buf+9); |
| sdword2byte(whence, buf+17); |
| |
| if(write(fd, buf, 21) < 21) |
| return AUTH_IO_ERROR; |
| |
| if (read_dword(fd) != 12) { |
| errno = EIO; |
| return -1; |
| } |
| |
| gotlen = read_sqword(fd); |
| errcode = read_sdword(fd); |
| |
| errno = errcode; |
| |
| return gotlen.v; |
| } |
| #endif |
| |
| static int floppyd_open(RemoteFile_t *This, int mode) |
| { |
| int errcode; |
| int 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); |
| sdword2byte(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_sdword(This->fd); |
| errcode = read_sdword(This->fd); |
| |
| errno = errcode; |
| |
| return gotlen; |
| } |
| |
| |
| /* ######################################################################## */ |
| |
| typedef ssize_t (*iofn) (int, char *, uint32_t); |
| |
| static ssize_t floppyd_io(Stream_t *Stream, char *buf, mt_off_t where, |
| size_t len, iofn io) |
| { |
| DeclareThis(RemoteFile_t); |
| ssize_t ret; |
| |
| where += This->offset; |
| |
| if (where != This->lastwhere ){ |
| #if SIZEOF_OFF_T >= 8 |
| if(This->capabilities & FLOPPYD_CAP_LARGE_SEEK) { |
| if(floppyd_lseek64( This->fd, where, SEEK_SET) < 0 ){ |
| perror("floppyd_lseek64"); |
| This->lastwhere = -1; |
| return -1; |
| } |
| } else |
| #endif |
| { |
| if(where > INT32_MAX || where < INT32_MIN) { |
| fprintf(stderr, "Seek position out of range\n"); |
| return -1; |
| } |
| if(floppyd_lseek(This->fd, (int32_t) where, SEEK_SET) < 0 ){ |
| perror("floppyd_lseek"); |
| This->lastwhere = -1; |
| return -1; |
| } |
| } |
| } |
| ret = io(This->fd, buf, |
| (len > INT32_MAX) ? (uint32_t)INT32_MAX+1 : (uint32_t) len); |
| if ( ret == -1 ){ |
| perror("floppyd_io"); |
| This->lastwhere = -1; |
| return -1; |
| } |
| This->lastwhere = where + ret; |
| return ret; |
| } |
| |
| static ssize_t floppyd_pread(Stream_t *Stream, char *buf, |
| mt_off_t where, size_t len) |
| { |
| return floppyd_io(Stream, buf, where, len, floppyd_reader); |
| } |
| |
| static ssize_t floppyd_pwrite(Stream_t *Stream, char *buf, |
| mt_off_t where, size_t len) |
| { |
| return floppyd_io(Stream, buf, where, len, 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_sdword(This->fd); |
| errcode = read_sdword(This->fd); |
| |
| errno = errcode; |
| |
| close(This->fd); |
| return gotlen; |
| } else { |
| return 0; |
| } |
| } |
| |
| |
| |
| static int floppyd_data(Stream_t *Stream, time_t *date, mt_off_t *size, |
| int *type, uint32_t *address) |
| { |
| DeclareThis(RemoteFile_t); |
| |
| if(date) |
| /* unknown, and irrelevant anyways */ |
| *date = 0; |
| if(size) |
| /* the size derived from the geometry */ |
| *size = This->size; |
| if(type) |
| *type = 0; /* not a directory */ |
| if(address) |
| *address = 0; |
| return 0; |
| } |
| |
| /* ######################################################################## */ |
| |
| static Class_t FloppydFileClass = { |
| 0, |
| 0, |
| floppyd_pread, |
| floppyd_pwrite, |
| floppyd_flush, |
| floppyd_free, |
| set_geom_noop, |
| floppyd_data, |
| 0, /* pre_allocate */ |
| 0, /* get_dosConvert */ |
| 0 /* discard */ |
| }; |
| |
| /* ######################################################################## */ |
| |
| static int get_host_and_port_and_drive(const char* name, char** hostname, |
| char **display, uint16_t* 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 = strtou16(p, &p, 0); |
| if(*p == '/') |
| p++; |
| *drive = 0; |
| if(*p >= '0' && *p <= '9') |
| *drive = strtoi(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 in_addr_t getipaddress(char *ipaddr) |
| { |
| struct hostent *host; |
| in_addr_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(in_addr_t ip, uint16_t 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, |
| const char *name, int mode, char *errmsg, |
| mt_off_t *maxSize) |
| { |
| RemoteFile_t *This; |
| |
| if (!dev || !(dev->misc_flags & FLOPPYD_FLAG)) |
| return NULL; |
| |
| This = New(RemoteFile_t); |
| if (!This){ |
| printOom(); |
| return NULL; |
| } |
| init_head(&This->head, &FloppydFileClass, NULL); |
| |
| This->offset = 0; |
| This->lastwhere = 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 &This->head; |
| } |
| |
| static int ConnectToFloppyd(RemoteFile_t *floppyd, const char* name, |
| char *errmsg) |
| { |
| char* hostname; |
| char* display; |
| uint16_t port; |
| int rval = get_host_and_port_and_drive(name, &hostname, &display, |
| &port, &floppyd->drive); |
| int sock; |
| unsigned 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; |
| } |