diff options
Diffstat (limited to 'src/io.c')
-rw-r--r-- | src/io.c | 231 |
1 files changed, 231 insertions, 0 deletions
diff --git a/src/io.c b/src/io.c new file mode 100644 index 0000000..d862fcf --- /dev/null +++ b/src/io.c @@ -0,0 +1,231 @@ +/* io.c - Virtual disk input/output + + Copyright (C) 1993 Werner Almesberger <werner.almesberger@lrc.di.epfl.ch> + Copyright (C) 1998 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> + Copyright (C) 2008-2013 Daniel Baumann <mail@daniel-baumann.ch> + + This program 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. + + This program 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 this program. If not, see <http://www.gnu.org/licenses/>. + + On Debian systems, the complete text of the GNU General Public License + can be found in /usr/share/common-licenses/GPL-3 file. +*/ + +/* + * Thu Feb 26 01:15:36 CET 1998: Martin Schulze <joey@infodrom.north.de> + * Fixed nasty bug that caused every file with a name like + * xxxxxxxx.xxx to be treated as bad name that needed to be fixed. + */ + +/* FAT32, VFAT, Atari format support, and various fixes additions May 1998 + * by Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> */ + +#define _LARGEFILE64_SOURCE +#include <sys/types.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <errno.h> +#include <fcntl.h> +#include <linux/fd.h> + +#include "dosfsck.h" +#include "common.h" +#include "io.h" + +typedef struct _change { + void *data; + loff_t pos; + int size; + struct _change *next; +} CHANGE; + +static CHANGE *changes, *last; +static int fd, did_change = 0; + +unsigned device_no; + +#ifdef __DJGPP__ +#include "volume.h" /* DOS lowlevel disk access functions */ +loff_t llseek(int fd, loff_t offset, int whence) +{ + if ((whence != SEEK_SET) || (fd == 4711)) + return -1; /* only those supported */ + return VolumeSeek(offset); +} + +#define open OpenVolume +#define close CloseVolume +#define read(a,b,c) ReadVolume(b,c) +#define write(a,b,c) WriteVolume(b,c) +#else +loff_t llseek(int fd, loff_t offset, int whence) +{ + return (loff_t) lseek64(fd, (off64_t) offset, whence); +} +#endif + +void fs_open(char *path, int rw) +{ + struct stat stbuf; + + if ((fd = open(path, rw ? O_RDWR : O_RDONLY)) < 0) { + perror("open"); + exit(6); + } + changes = last = NULL; + did_change = 0; + +#ifndef _DJGPP_ + if (fstat(fd, &stbuf) < 0) + pdie("fstat %s", path); + device_no = S_ISBLK(stbuf.st_mode) ? (stbuf.st_rdev >> 8) & 0xff : 0; +#else + if (IsWorkingOnImageFile()) { + if (fstat(GetVolumeHandle(), &stbuf) < 0) + pdie("fstat image %s", path); + device_no = 0; + } else { + /* return 2 for floppy, 1 for ramdisk, 7 for loopback */ + /* used by boot.c in Atari mode: floppy always FAT12, */ + /* loopback / ramdisk only FAT12 if usual floppy size, */ + /* harddisk always FAT16 on Atari... */ + device_no = (GetVolumeHandle() < 2) ? 2 : 1; + /* telling "floppy" for A:/B:, "ramdisk" for the rest */ + } +#endif +} + +/** + * Read data from the partition, accounting for any pending updates that are + * queued for writing. + * + * @param[in] pos Byte offset, relative to the beginning of the partition, + * at which to read + * @param[in] size Number of bytes to read + * @param[out] data Where to put the data read + */ +void fs_read(loff_t pos, int size, void *data) +{ + CHANGE *walk; + int got; + + if (llseek(fd, pos, 0) != pos) + pdie("Seek to %lld", pos); + if ((got = read(fd, data, size)) < 0) + pdie("Read %d bytes at %lld", size, pos); + if (got != size) + die("Got %d bytes instead of %d at %lld", got, size, pos); + for (walk = changes; walk; walk = walk->next) { + if (walk->pos < pos + size && walk->pos + walk->size > pos) { + if (walk->pos < pos) + memcpy(data, (char *)walk->data + pos - walk->pos, min(size, + walk->size + - pos + + walk->pos)); + else + memcpy((char *)data + walk->pos - pos, walk->data, + min(walk->size, size + pos - walk->pos)); + } + } +} + +int fs_test(loff_t pos, int size) +{ + void *scratch; + int okay; + + if (llseek(fd, pos, 0) != pos) + pdie("Seek to %lld", pos); + scratch = alloc(size); + okay = read(fd, scratch, size) == size; + free(scratch); + return okay; +} + +void fs_write(loff_t pos, int size, void *data) +{ + CHANGE *new; + int did; + + if (write_immed) { + did_change = 1; + if (llseek(fd, pos, 0) != pos) + pdie("Seek to %lld", pos); + if ((did = write(fd, data, size)) == size) + return; + if (did < 0) + pdie("Write %d bytes at %lld", size, pos); + die("Wrote %d bytes instead of %d at %lld", did, size, pos); + } + new = alloc(sizeof(CHANGE)); + new->pos = pos; + memcpy(new->data = alloc(new->size = size), data, size); + new->next = NULL; + if (last) + last->next = new; + else + changes = new; + last = new; +} + +static void fs_flush(void) +{ + CHANGE *this; + int size; + + while (changes) { + this = changes; + changes = changes->next; + if (llseek(fd, this->pos, 0) != this->pos) + fprintf(stderr, + "Seek to %lld failed: %s\n Did not write %d bytes.\n", + (long long)this->pos, strerror(errno), this->size); + else if ((size = write(fd, this->data, this->size)) < 0) + fprintf(stderr, "Writing %d bytes at %lld failed: %s\n", this->size, + (long long)this->pos, strerror(errno)); + else if (size != this->size) + fprintf(stderr, "Wrote %d bytes instead of %d bytes at %lld." + "\n", size, this->size, (long long)this->pos); + free(this->data); + free(this); + } +} + +int fs_close(int write) +{ + CHANGE *next; + int changed; + + changed = ! !changes; + if (write) + fs_flush(); + else + while (changes) { + next = changes->next; + free(changes->data); + free(changes); + changes = next; + } + if (close(fd) < 0) + pdie("closing file system"); + return changed || did_change; +} + +int fs_changed(void) +{ + return ! !changes || did_change; +} |