bigbiff bigbiff | 9c75405 | 2013-01-09 09:09:08 -0500 | [diff] [blame] | 1 | /* io.c - Virtual disk input/output |
| 2 | |
| 3 | Copyright (C) 1993 Werner Almesberger <werner.almesberger@lrc.di.epfl.ch> |
| 4 | Copyright (C) 1998 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> |
| 5 | |
| 6 | This program is free software: you can redistribute it and/or modify |
| 7 | it under the terms of the GNU General Public License as published by |
| 8 | the Free Software Foundation, either version 3 of the License, or |
| 9 | (at your option) any later version. |
| 10 | |
| 11 | This program is distributed in the hope that it will be useful, |
| 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 14 | GNU General Public License for more details. |
| 15 | |
| 16 | You should have received a copy of the GNU General Public License |
| 17 | along with this program. If not, see <http://www.gnu.org/licenses/>. |
| 18 | |
| 19 | On Debian systems, the complete text of the GNU General Public License |
| 20 | can be found in /usr/share/common-licenses/GPL-3 file. |
| 21 | */ |
| 22 | |
| 23 | /* |
| 24 | * Thu Feb 26 01:15:36 CET 1998: Martin Schulze <joey@infodrom.north.de> |
| 25 | * Fixed nasty bug that caused every file with a name like |
| 26 | * xxxxxxxx.xxx to be treated as bad name that needed to be fixed. |
| 27 | */ |
| 28 | |
| 29 | /* FAT32, VFAT, Atari format support, and various fixes additions May 1998 |
| 30 | * by Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> */ |
| 31 | |
| 32 | #define _LARGEFILE64_SOURCE |
| 33 | #include <sys/types.h> |
| 34 | #include <stdlib.h> |
| 35 | #include <stdio.h> |
| 36 | #include <string.h> |
| 37 | #include <unistd.h> |
| 38 | #include <sys/stat.h> |
| 39 | #include <sys/ioctl.h> |
| 40 | #include <errno.h> |
| 41 | #include <fcntl.h> |
| 42 | #include <linux/fd.h> |
| 43 | |
| 44 | #include "dosfsck.h" |
| 45 | #include "common.h" |
| 46 | #include "io.h" |
| 47 | |
| 48 | typedef struct _change { |
| 49 | void *data; |
| 50 | loff_t pos; |
| 51 | int size; |
| 52 | struct _change *next; |
| 53 | } CHANGE; |
| 54 | |
| 55 | static CHANGE *changes, *last; |
| 56 | static int fd, did_change = 0; |
| 57 | |
| 58 | unsigned device_no; |
| 59 | |
| 60 | #ifdef __DJGPP__ |
| 61 | #include "volume.h" /* DOS lowlevel disk access functions */ |
| 62 | loff_t llseek(int fd, loff_t offset, int whence) |
| 63 | { |
| 64 | if ((whence != SEEK_SET) || (fd == 4711)) |
| 65 | return -1; /* only those supported */ |
| 66 | return VolumeSeek(offset); |
| 67 | } |
| 68 | |
| 69 | #define open OpenVolume |
| 70 | #define close CloseVolume |
| 71 | #define read(a,b,c) ReadVolume(b,c) |
| 72 | #define write(a,b,c) WriteVolume(b,c) |
| 73 | #else |
| 74 | loff_t llseek(int fd, loff_t offset, int whence) |
| 75 | { |
| 76 | return (loff_t) lseek64(fd, (off64_t) offset, whence); |
| 77 | } |
| 78 | #endif |
| 79 | |
| 80 | void fs_open(char *path, int rw) |
| 81 | { |
| 82 | struct stat stbuf; |
| 83 | |
| 84 | if ((fd = open(path, rw ? O_RDWR : O_RDONLY)) < 0) { |
| 85 | perror("open"); |
| 86 | exit(6); |
| 87 | } |
| 88 | changes = last = NULL; |
| 89 | did_change = 0; |
| 90 | |
| 91 | #ifndef _DJGPP_ |
| 92 | if (fstat(fd, &stbuf) < 0) |
| 93 | pdie("fstat %s", path); |
| 94 | device_no = S_ISBLK(stbuf.st_mode) ? (stbuf.st_rdev >> 8) & 0xff : 0; |
| 95 | #else |
| 96 | if (IsWorkingOnImageFile()) { |
| 97 | if (fstat(GetVolumeHandle(), &stbuf) < 0) |
| 98 | pdie("fstat image %s", path); |
| 99 | device_no = 0; |
| 100 | } else { |
| 101 | /* return 2 for floppy, 1 for ramdisk, 7 for loopback */ |
| 102 | /* used by boot.c in Atari mode: floppy always FAT12, */ |
| 103 | /* loopback / ramdisk only FAT12 if usual floppy size, */ |
| 104 | /* harddisk always FAT16 on Atari... */ |
| 105 | device_no = (GetVolumeHandle() < 2) ? 2 : 1; |
| 106 | /* telling "floppy" for A:/B:, "ramdisk" for the rest */ |
| 107 | } |
| 108 | #endif |
| 109 | } |
| 110 | |
| 111 | /** |
| 112 | * Read data from the partition, accounting for any pending updates that are |
| 113 | * queued for writing. |
| 114 | * |
| 115 | * @param[in] pos Byte offset, relative to the beginning of the partition, |
| 116 | * at which to read |
| 117 | * @param[in] size Number of bytes to read |
| 118 | * @param[out] data Where to put the data read |
| 119 | */ |
| 120 | void fs_read(loff_t pos, int size, void *data) |
| 121 | { |
| 122 | CHANGE *walk; |
| 123 | int got; |
| 124 | |
| 125 | if (llseek(fd, pos, 0) != pos) |
| 126 | pdie("Seek to %lld", pos); |
| 127 | if ((got = read(fd, data, size)) < 0) |
| 128 | pdie("Read %d bytes at %lld", size, pos); |
| 129 | if (got != size) |
| 130 | die("Got %d bytes instead of %d at %lld", got, size, pos); |
| 131 | for (walk = changes; walk; walk = walk->next) { |
| 132 | if (walk->pos < pos + size && walk->pos + walk->size > pos) { |
| 133 | if (walk->pos < pos) |
| 134 | memcpy(data, (char *)walk->data + pos - walk->pos, min(size, |
| 135 | walk->size |
| 136 | - pos + |
| 137 | walk->pos)); |
| 138 | else |
| 139 | memcpy((char *)data + walk->pos - pos, walk->data, |
| 140 | min(walk->size, size + pos - walk->pos)); |
| 141 | } |
| 142 | } |
| 143 | } |
| 144 | |
| 145 | int fs_test(loff_t pos, int size) |
| 146 | { |
| 147 | void *scratch; |
| 148 | int okay; |
| 149 | |
| 150 | if (llseek(fd, pos, 0) != pos) |
| 151 | pdie("Seek to %lld", pos); |
| 152 | scratch = alloc(size); |
| 153 | okay = read(fd, scratch, size) == size; |
| 154 | free(scratch); |
| 155 | return okay; |
| 156 | } |
| 157 | |
| 158 | void fs_write(loff_t pos, int size, void *data) |
| 159 | { |
| 160 | CHANGE *new; |
| 161 | int did; |
| 162 | |
| 163 | if (write_immed) { |
| 164 | did_change = 1; |
| 165 | if (llseek(fd, pos, 0) != pos) |
| 166 | pdie("Seek to %lld", pos); |
| 167 | if ((did = write(fd, data, size)) == size) |
| 168 | return; |
| 169 | if (did < 0) |
| 170 | pdie("Write %d bytes at %lld", size, pos); |
| 171 | die("Wrote %d bytes instead of %d at %lld", did, size, pos); |
| 172 | } |
| 173 | new = alloc(sizeof(CHANGE)); |
| 174 | new->pos = pos; |
| 175 | memcpy(new->data = alloc(new->size = size), data, size); |
| 176 | new->next = NULL; |
| 177 | if (last) |
| 178 | last->next = new; |
| 179 | else |
| 180 | changes = new; |
| 181 | last = new; |
| 182 | } |
| 183 | |
| 184 | static void fs_flush(void) |
| 185 | { |
| 186 | CHANGE *this; |
| 187 | int size; |
| 188 | |
| 189 | while (changes) { |
| 190 | this = changes; |
| 191 | changes = changes->next; |
| 192 | if (llseek(fd, this->pos, 0) != this->pos) |
| 193 | fprintf(stderr, |
| 194 | "Seek to %lld failed: %s\n Did not write %d bytes.\n", |
| 195 | (long long)this->pos, strerror(errno), this->size); |
| 196 | else if ((size = write(fd, this->data, this->size)) < 0) |
| 197 | fprintf(stderr, "Writing %d bytes at %lld failed: %s\n", this->size, |
| 198 | (long long)this->pos, strerror(errno)); |
| 199 | else if (size != this->size) |
| 200 | fprintf(stderr, "Wrote %d bytes instead of %d bytes at %lld." |
| 201 | "\n", size, this->size, (long long)this->pos); |
| 202 | free(this->data); |
| 203 | free(this); |
| 204 | } |
| 205 | } |
| 206 | |
| 207 | int fs_close(int write) |
| 208 | { |
| 209 | CHANGE *next; |
| 210 | int changed; |
| 211 | |
| 212 | changed = ! !changes; |
| 213 | if (write) |
| 214 | fs_flush(); |
| 215 | else |
| 216 | while (changes) { |
| 217 | next = changes->next; |
| 218 | free(changes->data); |
| 219 | free(changes); |
| 220 | changes = next; |
| 221 | } |
| 222 | if (close(fd) < 0) |
| 223 | pdie("closing file system"); |
| 224 | return changed || did_change; |
| 225 | } |
| 226 | |
| 227 | int fs_changed(void) |
| 228 | { |
| 229 | return ! !changes || did_change; |
| 230 | } |