blob: 9b40727fe79a48be4fca09d0b0b930ae0e05a6f7 [file] [log] [blame]
Alistair Delvabeaee832021-02-24 11:27:23 -08001/* Copyright 1997,2001-2003 Alain Knaff.
2 * This file is part of mtools.
3 *
4 * Mtools is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * Mtools is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with Mtools. If not, see <http://www.gnu.org/licenses/>.
16 *
17 * Buffer read/write module
18 */
19
20#include "sysincludes.h"
21#include "msdos.h"
22#include "mtools.h"
23#include "buffer.h"
24
25typedef struct Buffer_t {
Yi Kong39bbd962022-01-09 19:41:38 +080026 struct Stream_t head;
27
Alistair Delvabeaee832021-02-24 11:27:23 -080028 size_t size; /* size of read/write buffer */
29 int dirty; /* is the buffer dirty? */
30
31 size_t sectorSize; /* sector size: all operations happen
32 * in multiples of this */
33 size_t cylinderSize; /* cylinder size: preferred alignment,
34 * but for efficiency, less data may be read */
35 int ever_dirty; /* was the buffer ever dirty? */
36 size_t dirty_pos;
37 size_t dirty_end;
Yi Kong39bbd962022-01-09 19:41:38 +080038 mt_off_t current; /* first sector in buffer */
39 size_t cur_size; /* the current size */
Alistair Delvabeaee832021-02-24 11:27:23 -080040 char *buf; /* disk read/write buffer */
41} Buffer_t;
42
Yi Kong39bbd962022-01-09 19:41:38 +080043/* Convert position relative to buffer to absolute position */
44static mt_off_t abs_pos(Buffer_t *Buffer, size_t rel) {
45 return Buffer->current + (mt_off_t) rel;
46}
47
48/* End of currently valid buffer */
49static mt_off_t cur_end(Buffer_t *Buffer) {
50 return abs_pos(Buffer, Buffer->cur_size);
51}
52
53/* distance from absolute position until next full cylinder. If position already
54 * *is* on a full cylinder boundary, return size of full cylinder */
55static size_t pos_to_next_full_cyl(Buffer_t *Buffer, mt_off_t pos) {
56 return Buffer->cylinderSize -
57 (size_t) (pos % (mt_off_t) Buffer->cylinderSize);
58}
59
Alistair Delvabeaee832021-02-24 11:27:23 -080060/*
61 * Flush a dirty buffer to disk. Resets Buffer->dirty to zero.
62 * All errors are fatal.
63 */
64
65static int _buf_flush(Buffer_t *Buffer)
66{
Yi Kong39bbd962022-01-09 19:41:38 +080067 ssize_t ret;
Alistair Delvabeaee832021-02-24 11:27:23 -080068
Yi Kong39bbd962022-01-09 19:41:38 +080069#ifdef HAVE_ASSERT_H
70 assert(Buffer->head.Next != NULL);
71#endif
72
73 if (!Buffer->dirty)
Alistair Delvabeaee832021-02-24 11:27:23 -080074 return 0;
Alistair Delvabeaee832021-02-24 11:27:23 -080075#ifdef DEBUG
76 fprintf(stderr, "write %08x -- %02x %08x %08x\n",
77 Buffer,
78 (unsigned char) Buffer->buf[0],
79 Buffer->current + Buffer->dirty_pos,
80 Buffer->dirty_end - Buffer->dirty_pos);
81#endif
82
Yi Kong39bbd962022-01-09 19:41:38 +080083 ret = force_pwrite(Buffer->head.Next,
84 Buffer->buf + Buffer->dirty_pos,
85 Buffer->current + (mt_off_t) Buffer->dirty_pos,
86 Buffer->dirty_end - Buffer->dirty_pos);
87 if(ret < 0) {
88 perror("buffer_flush: write");
89 return -1;
90 }
91
92 if((size_t) ret != Buffer->dirty_end - Buffer->dirty_pos) {
93 fprintf(stderr,"buffer_flush: short write\n");
Alistair Delvabeaee832021-02-24 11:27:23 -080094 return -1;
95 }
96 Buffer->dirty = 0;
97 Buffer->dirty_end = 0;
98 Buffer->dirty_pos = 0;
99 return 0;
100}
101
102static int invalidate_buffer(Buffer_t *Buffer, mt_off_t start)
103{
104 if(_buf_flush(Buffer) < 0)
105 return -1;
106
107 /* start reading at the beginning of start's sector
108 * don't start reading too early, or we might not even reach
109 * start */
Yi Kong39bbd962022-01-09 19:41:38 +0800110 Buffer->current = ROUND_DOWN(start, (mt_off_t) Buffer->sectorSize);
Alistair Delvabeaee832021-02-24 11:27:23 -0800111 Buffer->cur_size = 0;
112 return 0;
113}
114
115#undef OFFSET
Yi Kong39bbd962022-01-09 19:41:38 +0800116#define OFFSET ((size_t)(start - This->current))
Alistair Delvabeaee832021-02-24 11:27:23 -0800117
118typedef enum position_t {
119 OUTSIDE,
120 APPEND,
121 INSIDE,
122 ERROR
123} position_t;
124
125static position_t isInBuffer(Buffer_t *This, mt_off_t start, size_t *len)
126{
Yi Kong39bbd962022-01-09 19:41:38 +0800127 if(start >= This->current && start < cur_end(This)) {
Alistair Delvabeaee832021-02-24 11:27:23 -0800128 maximize(*len, This->cur_size - OFFSET);
129 return INSIDE;
Yi Kong39bbd962022-01-09 19:41:38 +0800130 } else if(start == cur_end(This) &&
Alistair Delvabeaee832021-02-24 11:27:23 -0800131 This->cur_size < This->size &&
132 *len >= This->sectorSize) {
133 /* append to the buffer for this, three conditions have to
134 * be met:
135 * 1. The start falls exactly at the end of the currently
136 * loaded data
137 * 2. There is still space
138 * 3. We append at least one sector
139 */
140 maximize(*len, This->size - This->cur_size);
141 *len = ROUND_DOWN(*len, This->sectorSize);
142 return APPEND;
143 } else {
144 if(invalidate_buffer(This, start) < 0)
145 return ERROR;
146 maximize(*len, This->cylinderSize - OFFSET);
Yi Kong39bbd962022-01-09 19:41:38 +0800147 maximize(*len, pos_to_next_full_cyl(This, This->current));
Alistair Delvabeaee832021-02-24 11:27:23 -0800148 return OUTSIDE;
149 }
150}
151
Yi Kong39bbd962022-01-09 19:41:38 +0800152static ssize_t buf_pread(Stream_t *Stream, char *buf,
153 mt_off_t start, size_t len)
Alistair Delvabeaee832021-02-24 11:27:23 -0800154{
155 size_t length;
Yi Kong39bbd962022-01-09 19:41:38 +0800156 size_t offset;
Alistair Delvabeaee832021-02-24 11:27:23 -0800157 char *disk_ptr;
Yi Kong39bbd962022-01-09 19:41:38 +0800158 ssize_t ret;
159 DeclareThis(Buffer_t);
Alistair Delvabeaee832021-02-24 11:27:23 -0800160
161 if(!len)
Yi Kong39bbd962022-01-09 19:41:38 +0800162 return 0;
Alistair Delvabeaee832021-02-24 11:27:23 -0800163
164 /*fprintf(stderr, "buf read %x %x %x\n", Stream, start, len);*/
165 switch(isInBuffer(This, start, &len)) {
166 case OUTSIDE:
167 case APPEND:
168 /* always load until the end of the cylinder */
Yi Kong39bbd962022-01-09 19:41:38 +0800169 length = pos_to_next_full_cyl(This, cur_end(This));
Alistair Delvabeaee832021-02-24 11:27:23 -0800170 maximize(length, This->size - This->cur_size);
171
172 /* read it! */
Yi Kong39bbd962022-01-09 19:41:38 +0800173 ret=PREADS(This->head.Next,
174 This->buf + This->cur_size,
175 This->current + (mt_off_t) This->cur_size,
176 length);
Alistair Delvabeaee832021-02-24 11:27:23 -0800177 if ( ret < 0 )
178 return ret;
Yi Kong39bbd962022-01-09 19:41:38 +0800179 This->cur_size += (size_t) ret;
Alistair Delvabeaee832021-02-24 11:27:23 -0800180 if (This->current+(mt_off_t)This->cur_size < start) {
181 fprintf(stderr, "Short buffer fill\n");
182 exit(1);
183 }
184 break;
185 case INSIDE:
186 /* nothing to do */
187 break;
188 case ERROR:
189 return -1;
190 }
191
192 offset = OFFSET;
193 disk_ptr = This->buf + offset;
194 maximize(len, This->cur_size - offset);
195 memcpy(buf, disk_ptr, len);
Yi Kong39bbd962022-01-09 19:41:38 +0800196 return (ssize_t) len;
Alistair Delvabeaee832021-02-24 11:27:23 -0800197}
198
Yi Kong39bbd962022-01-09 19:41:38 +0800199static ssize_t buf_pwrite(Stream_t *Stream, char *buf,
200 mt_off_t start, size_t len)
Alistair Delvabeaee832021-02-24 11:27:23 -0800201{
202 char *disk_ptr;
Yi Kong39bbd962022-01-09 19:41:38 +0800203 DeclareThis(Buffer_t);
Alistair Delvabeaee832021-02-24 11:27:23 -0800204 size_t offset=0;
205
206 if(!len)
207 return 0;
208
209 This->ever_dirty = 1;
210
211#ifdef DEBUG
212 fprintf(stderr, "buf write %x %02x %08x %08x -- %08x %08x -- %08x\n",
213 Stream, (unsigned char) This->buf[0],
214 start, len, This->current, This->cur_size, This->size);
215 fprintf(stderr, "%d %d %d %x %x\n",
216 start == This->current + This->cur_size,
217 This->cur_size < This->size,
218 len >= This->sectorSize, len, This->sectorSize);
219#endif
220 switch(isInBuffer(This, start, &len)) {
221 case OUTSIDE:
222#ifdef DEBUG
223 fprintf(stderr, "outside\n");
224#endif
Yi Kong39bbd962022-01-09 19:41:38 +0800225 if(start % (mt_off_t) This->cylinderSize ||
Alistair Delvabeaee832021-02-24 11:27:23 -0800226 len < This->sectorSize) {
227 size_t readSize;
Yi Kong39bbd962022-01-09 19:41:38 +0800228 ssize_t ret;
229 size_t bytes_read;
Alistair Delvabeaee832021-02-24 11:27:23 -0800230
231 readSize = This->cylinderSize -
Yi Kong39bbd962022-01-09 19:41:38 +0800232 (size_t)(This->current % (mt_off_t) This->cylinderSize);
Alistair Delvabeaee832021-02-24 11:27:23 -0800233
Yi Kong39bbd962022-01-09 19:41:38 +0800234 ret=PREADS(This->head.Next, This->buf,
235 (mt_off_t)This->current, readSize);
Alistair Delvabeaee832021-02-24 11:27:23 -0800236 /* read it! */
237 if ( ret < 0 )
238 return ret;
Yi Kong39bbd962022-01-09 19:41:38 +0800239 bytes_read = (size_t) ret;
240 if(bytes_read % This->sectorSize) {
241 fprintf(stderr, "Weird: read size (%zd) not a multiple of sector size (%d)\n", bytes_read, (int) This->sectorSize);
242 bytes_read -= bytes_read % This->sectorSize;
243 if(bytes_read == 0) {
Alistair Delvabeaee832021-02-24 11:27:23 -0800244 fprintf(stderr, "Nothing left\n");
245 exit(1);
246 }
247 }
Yi Kong39bbd962022-01-09 19:41:38 +0800248 This->cur_size = bytes_read;
Alistair Delvabeaee832021-02-24 11:27:23 -0800249 /* for dosemu. Autoextend size */
250 if(!This->cur_size) {
251 memset(This->buf,0,readSize);
252 This->cur_size = readSize;
253 }
254 offset = OFFSET;
255 break;
256 }
257 /* FALL THROUGH */
258 case APPEND:
259#ifdef DEBUG
260 fprintf(stderr, "append\n");
261#endif
262 len = ROUND_DOWN(len, This->sectorSize);
263 offset = OFFSET;
264 maximize(len, This->size - offset);
265 This->cur_size += len;
Yi Kong39bbd962022-01-09 19:41:38 +0800266 if(This->head.Next->Class->pre_allocate)
267 PRE_ALLOCATE(This->head.Next, cur_end(This));
Alistair Delvabeaee832021-02-24 11:27:23 -0800268 break;
269 case INSIDE:
270 /* nothing to do */
271#ifdef DEBUG
272 fprintf(stderr, "inside\n");
273#endif
274 offset = OFFSET;
275 maximize(len, This->cur_size - offset);
276 break;
277 case ERROR:
278 return -1;
279#ifdef DEBUG
280 default:
281 fprintf(stderr, "Should not happen\n");
282 exit(1);
283#endif
284 }
285
286 disk_ptr = This->buf + offset;
287
288 /* extend if we write beyond end */
289 if(offset + len > This->cur_size) {
290 len -= (offset + len) % This->sectorSize;
291 This->cur_size = len + offset;
292 }
293
294 memcpy(disk_ptr, buf, len);
295 if(!This->dirty || offset < This->dirty_pos)
296 This->dirty_pos = ROUND_DOWN(offset, This->sectorSize);
297 if(!This->dirty || offset + len > This->dirty_end)
298 This->dirty_end = ROUND_UP(offset + len, This->sectorSize);
Yi Kong39bbd962022-01-09 19:41:38 +0800299
Alistair Delvabeaee832021-02-24 11:27:23 -0800300 if(This->dirty_end > This->cur_size) {
301 fprintf(stderr,
302 "Internal error, dirty end too big dirty_end=%x cur_size=%x len=%x offset=%d sectorSize=%x\n",
303 (unsigned int) This->dirty_end,
304 (unsigned int) This->cur_size,
305 (unsigned int) len,
306 (int) offset, (int) This->sectorSize);
307 fprintf(stderr, "offset + len + grain - 1 = %x\n",
308 (int) (offset + len + This->sectorSize - 1));
309 fprintf(stderr, "ROUNDOWN(offset + len + grain - 1) = %x\n",
310 (int)ROUND_DOWN(offset + len + This->sectorSize - 1,
311 This->sectorSize));
312 fprintf(stderr, "This->dirty = %d\n", This->dirty);
313 exit(1);
314 }
315
316 This->dirty = 1;
Yi Kong39bbd962022-01-09 19:41:38 +0800317 return (ssize_t) len;
Alistair Delvabeaee832021-02-24 11:27:23 -0800318}
319
320static int buf_flush(Stream_t *Stream)
321{
322 int ret;
323 DeclareThis(Buffer_t);
324
325 if (!This->ever_dirty)
326 return 0;
327 ret = _buf_flush(This);
328 if(ret == 0)
329 This->ever_dirty = 0;
330 return ret;
331}
332
333
334static int buf_free(Stream_t *Stream)
335{
336 DeclareThis(Buffer_t);
337
338 if(This->buf)
339 free(This->buf);
340 This->buf = 0;
341 return 0;
342}
343
344static Class_t BufferClass = {
Yi Kong39bbd962022-01-09 19:41:38 +0800345 0,
346 0,
347 buf_pread,
348 buf_pwrite,
Alistair Delvabeaee832021-02-24 11:27:23 -0800349 buf_flush,
350 buf_free,
351 0, /* set_geom */
352 get_data_pass_through, /* get_data */
353 0, /* pre-allocate */
354 get_dosConvert_pass_through, /* dos convert */
355 0, /* discard */
356};
357
Yi Kong39bbd962022-01-09 19:41:38 +0800358Stream_t *buf_init(Stream_t *Next, size_t size,
359 size_t cylinderSize,
360 size_t sectorSize)
Alistair Delvabeaee832021-02-24 11:27:23 -0800361{
362 Buffer_t *Buffer;
Alistair Delvabeaee832021-02-24 11:27:23 -0800363
Yi Kong39bbd962022-01-09 19:41:38 +0800364#ifdef HAVE_ASSERT_H
365 assert(size != 0);
366 assert(cylinderSize != 0);
367 assert(sectorSize != 0);
368 assert(Next != NULL);
369#endif
Alistair Delvabeaee832021-02-24 11:27:23 -0800370
371 if(size % cylinderSize != 0) {
372 fprintf(stderr, "size not multiple of cylinder size\n");
373 exit(1);
374 }
375 if(cylinderSize % sectorSize != 0) {
376 fprintf(stderr, "cylinder size not multiple of sector size\n");
377 exit(1);
378 }
379
Yi Kong39bbd962022-01-09 19:41:38 +0800380 Buffer = New(Buffer_t);
381 if(!Buffer)
Alistair Delvabeaee832021-02-24 11:27:23 -0800382 return 0;
Yi Kong39bbd962022-01-09 19:41:38 +0800383 init_head(&Buffer->head, &BufferClass, Next);
Alistair Delvabeaee832021-02-24 11:27:23 -0800384 Buffer->buf = malloc(size);
385 if ( !Buffer->buf){
Yi Kong39bbd962022-01-09 19:41:38 +0800386 Free(Buffer);
Alistair Delvabeaee832021-02-24 11:27:23 -0800387 return 0;
388 }
389 Buffer->size = size;
390 Buffer->dirty = 0;
391 Buffer->cylinderSize = cylinderSize;
392 Buffer->sectorSize = sectorSize;
393
394 Buffer->ever_dirty = 0;
395 Buffer->dirty_pos = 0;
396 Buffer->dirty_end = 0;
397 Buffer->current = 0L;
398 Buffer->cur_size = 0; /* buffer currently empty */
399
Yi Kong39bbd962022-01-09 19:41:38 +0800400 return &Buffer->head;
Alistair Delvabeaee832021-02-24 11:27:23 -0800401}
402