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