1 /* Copyright 1997,2001-2003 Alain Knaff.
2 * This file is part of mtools.
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.
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.
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/>.
17 * Buffer read/write module
20 #include "sysincludes.h"
24 typedef struct Buffer_t {
27 size_t size; /* size of read/write buffer */
28 int dirty; /* is the buffer dirty? */
30 size_t sectorSize; /* sector size: all operations happen
31 * in multiples of this */
32 size_t cylinderSize; /* cylinder size: preferred alignment,
33 * but for efficiency, less data may be read */
34 int ever_dirty; /* was the buffer ever dirty? */
37 mt_off_t current; /* first sector in buffer */
38 size_t cur_size; /* the current size */
39 char *buf; /* disk read/write buffer */
42 /* Convert position relative to buffer to absolute position */
43 static mt_off_t abs_pos(Buffer_t *Buffer, size_t rel) {
44 return Buffer->current + (mt_off_t) rel;
47 /* End of currently valid buffer */
48 static mt_off_t cur_end(Buffer_t *Buffer) {
49 return abs_pos(Buffer, Buffer->cur_size);
52 /* distance from absolute position until next full cylinder. If position already
53 * *is* on a full cylinder boundary, return size of full cylinder */
54 static size_t pos_to_next_full_cyl(Buffer_t *Buffer, mt_off_t pos) {
55 return Buffer->cylinderSize -
56 (size_t) (pos % (mt_off_t) Buffer->cylinderSize);
60 * Flush a dirty buffer to disk. Resets Buffer->dirty to zero.
61 * All errors are fatal.
64 static int _buf_flush(Buffer_t *Buffer)
69 assert(Buffer->head.Next != NULL);
75 fprintf(stderr, "write %08x -- %02x %08x %08x\n",
77 (unsigned char) Buffer->buf[0],
78 Buffer->current + Buffer->dirty_pos,
79 Buffer->dirty_end - Buffer->dirty_pos);
82 ret = force_pwrite(Buffer->head.Next,
83 Buffer->buf + Buffer->dirty_pos,
84 Buffer->current + (mt_off_t) Buffer->dirty_pos,
85 Buffer->dirty_end - Buffer->dirty_pos);
87 perror("buffer_flush: write");
91 if((size_t) ret != Buffer->dirty_end - Buffer->dirty_pos) {
92 fprintf(stderr,"buffer_flush: short write\n");
96 Buffer->dirty_end = 0;
97 Buffer->dirty_pos = 0;
101 static int invalidate_buffer(Buffer_t *Buffer, mt_off_t start)
103 if(_buf_flush(Buffer) < 0)
106 /* start reading at the beginning of start's sector
107 * don't start reading too early, or we might not even reach
109 Buffer->current = ROUND_DOWN(start, (mt_off_t) Buffer->sectorSize);
110 Buffer->cur_size = 0;
115 #define OFFSET ((size_t)(start - This->current))
117 typedef enum position_t {
124 static position_t isInBuffer(Buffer_t *This, mt_off_t start, size_t *len)
126 if(start >= This->current && start < cur_end(This)) {
127 maximize(*len, This->cur_size - OFFSET);
129 } else if(start == cur_end(This) &&
130 This->cur_size < This->size &&
131 *len >= This->sectorSize) {
132 /* append to the buffer for this, three conditions have to
134 * 1. The start falls exactly at the end of the currently
136 * 2. There is still space
137 * 3. We append at least one sector
139 maximize(*len, This->size - This->cur_size);
140 *len = ROUND_DOWN(*len, This->sectorSize);
143 if(invalidate_buffer(This, start) < 0)
145 maximize(*len, This->cylinderSize - OFFSET);
146 maximize(*len, pos_to_next_full_cyl(This, This->current));
151 static ssize_t buf_pread(Stream_t *Stream, char *buf,
152 mt_off_t start, size_t len)
158 DeclareThis(Buffer_t);
163 /*fprintf(stderr, "buf read %x %x %x\n", Stream, start, len);*/
164 switch(isInBuffer(This, start, &len)) {
167 /* always load until the end of the cylinder */
168 length = pos_to_next_full_cyl(This, cur_end(This));
169 maximize(length, This->size - This->cur_size);
172 ret=PREADS(This->head.Next,
173 This->buf + This->cur_size,
174 This->current + (mt_off_t) This->cur_size,
178 This->cur_size += (size_t) ret;
179 if (This->current+(mt_off_t)This->cur_size < start) {
180 fprintf(stderr, "Short buffer fill\n");
192 disk_ptr = This->buf + offset;
193 maximize(len, This->cur_size - offset);
194 memcpy(buf, disk_ptr, len);
195 return (ssize_t) len;
198 static ssize_t buf_pwrite(Stream_t *Stream, char *buf,
199 mt_off_t start, size_t len)
202 DeclareThis(Buffer_t);
208 This->ever_dirty = 1;
211 fprintf(stderr, "buf write %x %02x %08x %08x -- %08x %08x -- %08x\n",
212 Stream, (unsigned char) This->buf[0],
213 start, len, This->current, This->cur_size, This->size);
214 fprintf(stderr, "%d %d %d %x %x\n",
215 start == This->current + This->cur_size,
216 This->cur_size < This->size,
217 len >= This->sectorSize, len, This->sectorSize);
219 switch(isInBuffer(This, start, &len)) {
222 fprintf(stderr, "outside\n");
224 if(start % (mt_off_t) This->cylinderSize ||
225 len < This->sectorSize) {
230 readSize = This->cylinderSize -
231 (size_t)(This->current % (mt_off_t) This->cylinderSize);
233 ret=PREADS(This->head.Next, This->buf,
234 (mt_off_t)This->current, readSize);
238 bytes_read = (size_t) ret;
239 if(bytes_read % This->sectorSize) {
240 fprintf(stderr, "Weird: read size ("SSZF") not a multiple of sector size (%d)\n", bytes_read, (int) This->sectorSize);
241 bytes_read -= bytes_read % This->sectorSize;
242 if(bytes_read == 0) {
243 fprintf(stderr, "Nothing left\n");
247 This->cur_size = bytes_read;
248 /* for dosemu. Autoextend size */
249 if(!This->cur_size) {
250 memset(This->buf,0,readSize);
251 This->cur_size = readSize;
259 fprintf(stderr, "append\n");
261 len = ROUND_DOWN(len, This->sectorSize);
263 maximize(len, This->size - offset);
264 This->cur_size += len;
265 if(This->head.Next->Class->pre_allocate)
266 PRE_ALLOCATE(This->head.Next, cur_end(This));
271 fprintf(stderr, "inside\n");
274 maximize(len, This->cur_size - offset);
280 fprintf(stderr, "Should not happen\n");
285 disk_ptr = This->buf + offset;
287 /* extend if we write beyond end */
288 if(offset + len > This->cur_size) {
289 len -= (offset + len) % This->sectorSize;
290 This->cur_size = len + offset;
293 memcpy(disk_ptr, buf, len);
294 if(!This->dirty || offset < This->dirty_pos)
295 This->dirty_pos = ROUND_DOWN(offset, This->sectorSize);
296 if(!This->dirty || offset + len > This->dirty_end)
297 This->dirty_end = ROUND_UP(offset + len, This->sectorSize);
299 if(This->dirty_end > This->cur_size) {
301 "Internal error, dirty end too big dirty_end=%x cur_size=%x len=%x offset=%d sectorSize=%x\n",
302 (unsigned int) This->dirty_end,
303 (unsigned int) This->cur_size,
305 (int) offset, (int) This->sectorSize);
306 fprintf(stderr, "offset + len + grain - 1 = %x\n",
307 (int) (offset + len + This->sectorSize - 1));
308 fprintf(stderr, "ROUNDOWN(offset + len + grain - 1) = %x\n",
309 (int)ROUND_DOWN(offset + len + This->sectorSize - 1,
311 fprintf(stderr, "This->dirty = %d\n", This->dirty);
316 return (ssize_t) len;
319 static int buf_flush(Stream_t *Stream)
322 DeclareThis(Buffer_t);
324 if (!This->ever_dirty)
326 ret = _buf_flush(This);
328 This->ever_dirty = 0;
333 static int buf_free(Stream_t *Stream)
335 DeclareThis(Buffer_t);
343 static Class_t BufferClass = {
351 get_data_pass_through, /* get_data */
352 0, /* pre-allocate */
353 get_dosConvert_pass_through, /* dos convert */
357 Stream_t *buf_init(Stream_t *Next, size_t size,
365 assert(cylinderSize != 0);
366 assert(sectorSize != 0);
367 assert(Next != NULL);
370 if(size % cylinderSize != 0) {
371 fprintf(stderr, "size not multiple of cylinder size\n");
374 if(cylinderSize % sectorSize != 0) {
375 fprintf(stderr, "cylinder size not multiple of sector size\n");
379 Buffer = New(Buffer_t);
382 init_head(&Buffer->head, &BufferClass, Next);
383 Buffer->buf = malloc(size);
390 Buffer->cylinderSize = cylinderSize;
391 Buffer->sectorSize = sectorSize;
393 Buffer->ever_dirty = 0;
394 Buffer->dirty_pos = 0;
395 Buffer->dirty_end = 0;
396 Buffer->current = 0L;
397 Buffer->cur_size = 0; /* buffer currently empty */
399 return &Buffer->head;