1 | /*
|
---|
2 | * Buffer read/write module
|
---|
3 | */
|
---|
4 |
|
---|
5 | #include "sysincludes.h"
|
---|
6 | #include "msdos.h"
|
---|
7 | #include "mtools.h"
|
---|
8 | #include "buffer.h"
|
---|
9 |
|
---|
10 | typedef struct Buffer_t {
|
---|
11 | Class_t *Class;
|
---|
12 | int refs;
|
---|
13 | Stream_t *Next;
|
---|
14 | Stream_t *Buffer;
|
---|
15 |
|
---|
16 | size_t size; /* size of read/write buffer */
|
---|
17 | int dirty; /* is the buffer dirty? */
|
---|
18 |
|
---|
19 | int sectorSize; /* sector size: all operations happen
|
---|
20 | * in multiples of this */
|
---|
21 | int cylinderSize; /* cylinder size: preferred alignemnt,
|
---|
22 | * but for efficiency, less data may be read */
|
---|
23 | int ever_dirty; /* was the buffer ever dirty? */
|
---|
24 | int dirty_pos;
|
---|
25 | int dirty_end;
|
---|
26 | mt_off_t current; /* first sector in buffer */
|
---|
27 | size_t cur_size; /* the current size */
|
---|
28 | char *buf; /* disk read/write buffer */
|
---|
29 | } Buffer_t;
|
---|
30 |
|
---|
31 | /*
|
---|
32 | * Flush a dirty buffer to disk. Resets Buffer->dirty to zero.
|
---|
33 | * All errors are fatal.
|
---|
34 | */
|
---|
35 |
|
---|
36 | static int _buf_flush(Buffer_t *Buffer)
|
---|
37 | {
|
---|
38 | int ret;
|
---|
39 |
|
---|
40 | if (!Buffer->Next || !Buffer->dirty)
|
---|
41 | return 0;
|
---|
42 | if(Buffer->current < 0L) {
|
---|
43 | fprintf(stderr,"Should not happen\n");
|
---|
44 | return -1;
|
---|
45 | }
|
---|
46 | #ifdef DEBUG
|
---|
47 | fprintf(stderr, "write %08x -- %02x %08x %08x\n",
|
---|
48 | Buffer,
|
---|
49 | (unsigned char) Buffer->buf[0],
|
---|
50 | Buffer->current + Buffer->dirty_pos,
|
---|
51 | Buffer->dirty_end - Buffer->dirty_pos);
|
---|
52 | #endif
|
---|
53 |
|
---|
54 | ret = force_write(Buffer->Next,
|
---|
55 | Buffer->buf + Buffer->dirty_pos,
|
---|
56 | Buffer->current + Buffer->dirty_pos,
|
---|
57 | Buffer->dirty_end - Buffer->dirty_pos);
|
---|
58 | if(ret != Buffer->dirty_end - Buffer->dirty_pos) {
|
---|
59 | if(ret < 0)
|
---|
60 | perror("buffer_flush: write");
|
---|
61 | else
|
---|
62 | fprintf(stderr,"buffer_flush: short write\n");
|
---|
63 | return -1;
|
---|
64 | }
|
---|
65 | Buffer->dirty = 0;
|
---|
66 | Buffer->dirty_end = 0;
|
---|
67 | Buffer->dirty_pos = 0;
|
---|
68 | return 0;
|
---|
69 | }
|
---|
70 |
|
---|
71 | static int invalidate_buffer(Buffer_t *Buffer, mt_off_t start)
|
---|
72 | {
|
---|
73 | /*fprintf(stderr, "invalidate %x\n", Buffer);*/
|
---|
74 | if(Buffer->sectorSize == 32) {
|
---|
75 | fprintf(stderr, "refreshing directory\n");
|
---|
76 | }
|
---|
77 |
|
---|
78 | if(_buf_flush(Buffer) < 0)
|
---|
79 | return -1;
|
---|
80 |
|
---|
81 | /* start reading at the beginning of start's sector
|
---|
82 | * don't start reading too early, or we might not even reach
|
---|
83 | * start */
|
---|
84 | Buffer->current = ROUND_DOWN(start, Buffer->sectorSize);
|
---|
85 | Buffer->cur_size = 0;
|
---|
86 | return 0;
|
---|
87 | }
|
---|
88 |
|
---|
89 | #undef OFFSET
|
---|
90 | #define OFFSET (start - This->current)
|
---|
91 |
|
---|
92 | typedef enum position_t {
|
---|
93 | OUTSIDE,
|
---|
94 | APPEND,
|
---|
95 | INSIDE,
|
---|
96 | ERROR
|
---|
97 | } position_t;
|
---|
98 |
|
---|
99 | static position_t isInBuffer(Buffer_t *This, mt_off_t start, size_t *len)
|
---|
100 | {
|
---|
101 | if(start >= This->current &&
|
---|
102 | start < This->current + This->cur_size) {
|
---|
103 | maximize(*len, This->cur_size - OFFSET);
|
---|
104 | return INSIDE;
|
---|
105 | } else if(start == This->current + This->cur_size &&
|
---|
106 | This->cur_size < This->size &&
|
---|
107 | *len >= This->sectorSize) {
|
---|
108 | /* append to the buffer for this, three conditions have to
|
---|
109 | * be met:
|
---|
110 | * 1. The start falls exactly at the end of the currently
|
---|
111 | * loaded data
|
---|
112 | * 2. There is still space
|
---|
113 | * 3. We append at least one sector
|
---|
114 | */
|
---|
115 | maximize(*len, This->size - This->cur_size);
|
---|
116 | *len = ROUND_DOWN(*len, This->sectorSize);
|
---|
117 | return APPEND;
|
---|
118 | } else {
|
---|
119 | if(invalidate_buffer(This, start) < 0)
|
---|
120 | return ERROR;
|
---|
121 | maximize(*len, This->cylinderSize - OFFSET);
|
---|
122 | maximize(*len, This->cylinderSize - This->current % This->cylinderSize);
|
---|
123 | return OUTSIDE;
|
---|
124 | }
|
---|
125 | }
|
---|
126 |
|
---|
127 | static int buf_read(Stream_t *Stream, char *buf, mt_off_t start, size_t len)
|
---|
128 | {
|
---|
129 | size_t length;
|
---|
130 | int offset;
|
---|
131 | char *disk_ptr;
|
---|
132 | int ret;
|
---|
133 | DeclareThis(Buffer_t);
|
---|
134 |
|
---|
135 | if(!len)
|
---|
136 | return 0;
|
---|
137 |
|
---|
138 | /*fprintf(stderr, "buf read %x %x %x\n", Stream, start, len);*/
|
---|
139 | switch(isInBuffer(This, start, &len)) {
|
---|
140 | case OUTSIDE:
|
---|
141 | case APPEND:
|
---|
142 | /* always load until the end of the cylinder */
|
---|
143 | length = This->cylinderSize -
|
---|
144 | (This->current + This->cur_size) % This->cylinderSize;
|
---|
145 | maximize(length, This->size - This->cur_size);
|
---|
146 |
|
---|
147 | /* read it! */
|
---|
148 | ret=READS(This->Next,
|
---|
149 | This->buf + This->cur_size,
|
---|
150 | This->current + This->cur_size,
|
---|
151 | length);
|
---|
152 | if ( ret < 0 )
|
---|
153 | return ret;
|
---|
154 | This->cur_size += ret;
|
---|
155 | if (This->current+This->cur_size < start) {
|
---|
156 | fprintf(stderr, "Short buffer fill\n");
|
---|
157 | exit(1);
|
---|
158 | }
|
---|
159 | break;
|
---|
160 | case INSIDE:
|
---|
161 | /* nothing to do */
|
---|
162 | break;
|
---|
163 | case ERROR:
|
---|
164 | return -1;
|
---|
165 | }
|
---|
166 |
|
---|
167 | offset = OFFSET;
|
---|
168 | disk_ptr = This->buf + offset;
|
---|
169 | maximize(len, This->cur_size - offset);
|
---|
170 | memcpy(buf, disk_ptr, len);
|
---|
171 | return len;
|
---|
172 | }
|
---|
173 |
|
---|
174 | static int buf_write(Stream_t *Stream, char *buf, mt_off_t start, size_t len)
|
---|
175 | {
|
---|
176 | char *disk_ptr;
|
---|
177 | DeclareThis(Buffer_t);
|
---|
178 | int offset, ret;
|
---|
179 |
|
---|
180 | if(!len)
|
---|
181 | return 0;
|
---|
182 |
|
---|
183 | This->ever_dirty = 1;
|
---|
184 |
|
---|
185 | #ifdef DEBUG
|
---|
186 | fprintf(stderr, "buf write %x %02x %08x %08x -- %08x %08x -- %08x\n",
|
---|
187 | Stream, (unsigned char) This->buf[0],
|
---|
188 | start, len, This->current, This->cur_size, This->size);
|
---|
189 | fprintf(stderr, "%d %d %d %x %x\n",
|
---|
190 | start == This->current + This->cur_size,
|
---|
191 | This->cur_size < This->size,
|
---|
192 | len >= This->sectorSize, len, This->sectorSize);
|
---|
193 | #endif
|
---|
194 | switch(isInBuffer(This, start, &len)) {
|
---|
195 | case OUTSIDE:
|
---|
196 | #ifdef DEBUG
|
---|
197 | fprintf(stderr, "outside\n");
|
---|
198 | #endif
|
---|
199 | if(start % This->cylinderSize ||
|
---|
200 | len < This->sectorSize) {
|
---|
201 | size_t readSize;
|
---|
202 |
|
---|
203 | readSize = This->cylinderSize -
|
---|
204 | This->current % This->cylinderSize;
|
---|
205 |
|
---|
206 | ret=READS(This->Next, This->buf, This->current, readSize);
|
---|
207 | /* read it! */
|
---|
208 | if ( ret < 0 )
|
---|
209 | return ret;
|
---|
210 | This->cur_size = ret;
|
---|
211 | /* for dosemu. Autoextend size */
|
---|
212 | if(!This->cur_size) {
|
---|
213 | memset(This->buf,0,readSize);
|
---|
214 | This->cur_size = readSize;
|
---|
215 | }
|
---|
216 | offset = OFFSET;
|
---|
217 | break;
|
---|
218 | }
|
---|
219 | /* FALL THROUGH */
|
---|
220 | case APPEND:
|
---|
221 | #ifdef DEBUG
|
---|
222 | fprintf(stderr, "append\n");
|
---|
223 | #endif
|
---|
224 | len = ROUND_DOWN(len, This->sectorSize);
|
---|
225 | offset = OFFSET;
|
---|
226 | maximize(len, This->size - offset);
|
---|
227 | This->cur_size += len;
|
---|
228 | if(This->Next->Class->pre_allocate)
|
---|
229 | PRE_ALLOCATE(This->Next,
|
---|
230 | This->current + This->cur_size);
|
---|
231 | break;
|
---|
232 | case INSIDE:
|
---|
233 | /* nothing to do */
|
---|
234 | #ifdef DEBUG
|
---|
235 | fprintf(stderr, "inside\n");
|
---|
236 | #endif
|
---|
237 | offset = OFFSET;
|
---|
238 | maximize(len, This->cur_size - offset);
|
---|
239 | break;
|
---|
240 | case ERROR:
|
---|
241 | return -1;
|
---|
242 | default:
|
---|
243 | #ifdef DEBUG
|
---|
244 | fprintf(stderr, "Should not happen\n");
|
---|
245 | #endif
|
---|
246 | exit(1);
|
---|
247 | }
|
---|
248 |
|
---|
249 | disk_ptr = This->buf + offset;
|
---|
250 |
|
---|
251 | /* extend if we write beyond end */
|
---|
252 | if(offset + len > This->cur_size) {
|
---|
253 | len -= (offset + len) % This->sectorSize;
|
---|
254 | This->cur_size = len + offset;
|
---|
255 | }
|
---|
256 |
|
---|
257 | memcpy(disk_ptr, buf, len);
|
---|
258 | if(!This->dirty || offset < This->dirty_pos)
|
---|
259 | This->dirty_pos = ROUND_DOWN(offset, This->sectorSize);
|
---|
260 | if(!This->dirty || offset + len > This->dirty_end)
|
---|
261 | This->dirty_end = ROUND_UP(offset + len, This->sectorSize);
|
---|
262 |
|
---|
263 | if(This->dirty_end > This->cur_size) {
|
---|
264 | fprintf(stderr,
|
---|
265 | "Internal error, dirty end too big %x %x %x %d %x\n",
|
---|
266 | This->dirty_end, (unsigned int) This->cur_size, (unsigned int) len,
|
---|
267 | (int) offset, (int) This->sectorSize);
|
---|
268 | fprintf(stderr, "offset + len + grain - 1 = %x\n",
|
---|
269 | (int) (offset + len + This->sectorSize - 1));
|
---|
270 | fprintf(stderr, "ROUNDOWN(offset + len + grain - 1) = %x\n",
|
---|
271 | (int)ROUND_DOWN(offset + len + This->sectorSize - 1,
|
---|
272 | This->sectorSize));
|
---|
273 | fprintf(stderr, "This->dirty = %d\n", This->dirty);
|
---|
274 | exit(1);
|
---|
275 | }
|
---|
276 |
|
---|
277 | This->dirty = 1;
|
---|
278 | return len;
|
---|
279 | }
|
---|
280 |
|
---|
281 | static int buf_flush(Stream_t *Stream)
|
---|
282 | {
|
---|
283 | int ret;
|
---|
284 | DeclareThis(Buffer_t);
|
---|
285 |
|
---|
286 | if (!This->ever_dirty)
|
---|
287 | return 0;
|
---|
288 | ret = _buf_flush(This);
|
---|
289 | if(ret == 0)
|
---|
290 | This->ever_dirty = 0;
|
---|
291 | return ret;
|
---|
292 | }
|
---|
293 |
|
---|
294 |
|
---|
295 | static int buf_free(Stream_t *Stream)
|
---|
296 | {
|
---|
297 | DeclareThis(Buffer_t);
|
---|
298 |
|
---|
299 | if(This->buf)
|
---|
300 | free(This->buf);
|
---|
301 | This->buf = 0;
|
---|
302 | return 0;
|
---|
303 | }
|
---|
304 |
|
---|
305 | static Class_t BufferClass = {
|
---|
306 | buf_read,
|
---|
307 | buf_write,
|
---|
308 | buf_flush,
|
---|
309 | buf_free,
|
---|
310 | 0, /* set_geom */
|
---|
311 | get_data_pass_through, /* get_data */
|
---|
312 | 0, /* pre-allocate */
|
---|
313 | };
|
---|
314 |
|
---|
315 | Stream_t *buf_init(Stream_t *Next, int size,
|
---|
316 | int cylinderSize,
|
---|
317 | int sectorSize)
|
---|
318 | {
|
---|
319 | Buffer_t *Buffer;
|
---|
320 | Stream_t *Stream;
|
---|
321 |
|
---|
322 |
|
---|
323 | if(size % cylinderSize != 0) {
|
---|
324 | fprintf(stderr, "size not multiple of cylinder size\n");
|
---|
325 | exit(1);
|
---|
326 | }
|
---|
327 | if(cylinderSize % sectorSize != 0) {
|
---|
328 | fprintf(stderr, "cylinder size not multiple of sector size\n");
|
---|
329 | exit(1);
|
---|
330 | }
|
---|
331 |
|
---|
332 | if(Next->Buffer){
|
---|
333 | Next->refs--;
|
---|
334 | Next->Buffer->refs++;
|
---|
335 | return Next->Buffer;
|
---|
336 | }
|
---|
337 |
|
---|
338 | Stream = (Stream_t *) malloc (sizeof(Buffer_t));
|
---|
339 | if(!Stream)
|
---|
340 | return 0;
|
---|
341 | Buffer = (Buffer_t *) Stream;
|
---|
342 | Buffer->buf = malloc(size);
|
---|
343 | if ( !Buffer->buf){
|
---|
344 | Free(Stream);
|
---|
345 | return 0;
|
---|
346 | }
|
---|
347 | Buffer->size = size;
|
---|
348 | Buffer->dirty = 0;
|
---|
349 | Buffer->cylinderSize = cylinderSize;
|
---|
350 | Buffer->sectorSize = sectorSize;
|
---|
351 |
|
---|
352 | Buffer->ever_dirty = 0;
|
---|
353 | Buffer->dirty_pos = 0;
|
---|
354 | Buffer->dirty_end = 0;
|
---|
355 | Buffer->current = 0;
|
---|
356 | Buffer->cur_size = 0; /* buffer currently empty */
|
---|
357 |
|
---|
358 | Buffer->Next = Next;
|
---|
359 | Buffer->Class = &BufferClass;
|
---|
360 | Buffer->refs = 1;
|
---|
361 | Buffer->Buffer = 0;
|
---|
362 | Buffer->Next->Buffer = (Stream_t *) Buffer;
|
---|
363 | return Stream;
|
---|
364 | }
|
---|
365 |
|
---|