blob: 450432c8e0c002f1e8e686b13d0296a0c7fe38ea [file] [log] [blame]
bigbiff bigbiff9c754052013-01-09 09:09:08 -05001/* 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>
Matt Mower18794c82015-11-11 16:22:45 -06005 Copyright (C) 2008-2014 Daniel Baumann <mail@daniel-baumann.ch>
bigbiff bigbiff9c754052013-01-09 09:09:08 -05006
7 This program is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
19
Matt Mower18794c82015-11-11 16:22:45 -060020 The complete text of the GNU General Public License
bigbiff bigbiff9c754052013-01-09 09:09:08 -050021 can be found in /usr/share/common-licenses/GPL-3 file.
22*/
23
24/*
25 * Thu Feb 26 01:15:36 CET 1998: Martin Schulze <joey@infodrom.north.de>
26 * Fixed nasty bug that caused every file with a name like
27 * xxxxxxxx.xxx to be treated as bad name that needed to be fixed.
28 */
29
30/* FAT32, VFAT, Atari format support, and various fixes additions May 1998
31 * by Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> */
32
33#define _LARGEFILE64_SOURCE
bigbiff bigbiff9c754052013-01-09 09:09:08 -050034#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
Matt Mower18794c82015-11-11 16:22:45 -060044#include "fsck.fat.h"
bigbiff bigbiff9c754052013-01-09 09:09:08 -050045#include "common.h"
46#include "io.h"
47
48typedef struct _change {
49 void *data;
50 loff_t pos;
51 int size;
52 struct _change *next;
53} CHANGE;
54
55static CHANGE *changes, *last;
56static int fd, did_change = 0;
57
58unsigned device_no;
59
60#ifdef __DJGPP__
61#include "volume.h" /* DOS lowlevel disk access functions */
62loff_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
74loff_t llseek(int fd, loff_t offset, int whence)
75{
76 return (loff_t) lseek64(fd, (off64_t) offset, whence);
77}
78#endif
79
80void 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 */
120void 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,
Matt Mower18794c82015-11-11 16:22:45 -0600135 walk->
136 size -
137 pos +
138 walk->
139 pos));
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500140 else
141 memcpy((char *)data + walk->pos - pos, walk->data,
142 min(walk->size, size + pos - walk->pos));
143 }
144 }
145}
146
147int fs_test(loff_t pos, int size)
148{
149 void *scratch;
150 int okay;
151
152 if (llseek(fd, pos, 0) != pos)
153 pdie("Seek to %lld", pos);
154 scratch = alloc(size);
155 okay = read(fd, scratch, size) == size;
156 free(scratch);
157 return okay;
158}
159
160void fs_write(loff_t pos, int size, void *data)
161{
162 CHANGE *new;
163 int did;
164
165 if (write_immed) {
166 did_change = 1;
167 if (llseek(fd, pos, 0) != pos)
168 pdie("Seek to %lld", pos);
169 if ((did = write(fd, data, size)) == size)
170 return;
171 if (did < 0)
172 pdie("Write %d bytes at %lld", size, pos);
173 die("Wrote %d bytes instead of %d at %lld", did, size, pos);
174 }
175 new = alloc(sizeof(CHANGE));
176 new->pos = pos;
177 memcpy(new->data = alloc(new->size = size), data, size);
178 new->next = NULL;
179 if (last)
180 last->next = new;
181 else
182 changes = new;
183 last = new;
184}
185
186static void fs_flush(void)
187{
188 CHANGE *this;
189 int size;
190
191 while (changes) {
192 this = changes;
193 changes = changes->next;
194 if (llseek(fd, this->pos, 0) != this->pos)
195 fprintf(stderr,
196 "Seek to %lld failed: %s\n Did not write %d bytes.\n",
197 (long long)this->pos, strerror(errno), this->size);
198 else if ((size = write(fd, this->data, this->size)) < 0)
199 fprintf(stderr, "Writing %d bytes at %lld failed: %s\n", this->size,
200 (long long)this->pos, strerror(errno));
201 else if (size != this->size)
202 fprintf(stderr, "Wrote %d bytes instead of %d bytes at %lld."
203 "\n", size, this->size, (long long)this->pos);
204 free(this->data);
205 free(this);
206 }
207}
208
209int fs_close(int write)
210{
211 CHANGE *next;
212 int changed;
213
214 changed = ! !changes;
215 if (write)
216 fs_flush();
217 else
218 while (changes) {
219 next = changes->next;
220 free(changes->data);
221 free(changes);
222 changes = next;
223 }
224 if (close(fd) < 0)
Matt Mower18794c82015-11-11 16:22:45 -0600225 pdie("closing filesystem");
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500226 return changed || did_change;
227}
228
229int fs_changed(void)
230{
231 return ! !changes || did_change;
232}