/* Copyright 1997,2001-2003 Alain Knaff. * This file is part of mtools. * * Mtools is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Mtools is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Mtools. If not, see . * * Buffer read/write module */ #include "sysincludes.h" #include "msdos.h" #include "mtools.h" #include "buffer.h" typedef struct Buffer_t { Class_t *Class; int refs; Stream_t *Next; Stream_t *Buffer; size_t size; /* size of read/write buffer */ int dirty; /* is the buffer dirty? */ size_t sectorSize; /* sector size: all operations happen * in multiples of this */ size_t cylinderSize; /* cylinder size: preferred alignment, * but for efficiency, less data may be read */ int ever_dirty; /* was the buffer ever dirty? */ size_t dirty_pos; size_t dirty_end; mt_off_t current; /* first sector in buffer */ size_t cur_size; /* the current size */ char *buf; /* disk read/write buffer */ } Buffer_t; /* * Flush a dirty buffer to disk. Resets Buffer->dirty to zero. * All errors are fatal. */ static int _buf_flush(Buffer_t *Buffer) { int ret; if (!Buffer->Next || !Buffer->dirty) return 0; if(Buffer->current < 0L) { fprintf(stderr,"Should not happen\n"); return -1; } #ifdef DEBUG fprintf(stderr, "write %08x -- %02x %08x %08x\n", Buffer, (unsigned char) Buffer->buf[0], Buffer->current + Buffer->dirty_pos, Buffer->dirty_end - Buffer->dirty_pos); #endif ret = force_write(Buffer->Next, Buffer->buf + Buffer->dirty_pos, Buffer->current + Buffer->dirty_pos, Buffer->dirty_end - Buffer->dirty_pos); if(ret != (signed int) (Buffer->dirty_end - Buffer->dirty_pos)) { if(ret < 0) perror("buffer_flush: write"); else fprintf(stderr,"buffer_flush: short write\n"); return -1; } Buffer->dirty = 0; Buffer->dirty_end = 0; Buffer->dirty_pos = 0; return 0; } static int invalidate_buffer(Buffer_t *Buffer, mt_off_t start) { if(_buf_flush(Buffer) < 0) return -1; /* start reading at the beginning of start's sector * don't start reading too early, or we might not even reach * start */ Buffer->current = ROUND_DOWN(start, Buffer->sectorSize); Buffer->cur_size = 0; return 0; } #undef OFFSET #define OFFSET (start - This->current) typedef enum position_t { OUTSIDE, APPEND, INSIDE, ERROR } position_t; static position_t isInBuffer(Buffer_t *This, mt_off_t start, size_t *len) { if(start >= This->current && start < This->current + (mt_off_t) This->cur_size) { maximize(*len, This->cur_size - OFFSET); return INSIDE; } else if(start == This->current + (mt_off_t) This->cur_size && This->cur_size < This->size && *len >= This->sectorSize) { /* append to the buffer for this, three conditions have to * be met: * 1. The start falls exactly at the end of the currently * loaded data * 2. There is still space * 3. We append at least one sector */ maximize(*len, This->size - This->cur_size); *len = ROUND_DOWN(*len, This->sectorSize); return APPEND; } else { if(invalidate_buffer(This, start) < 0) return ERROR; maximize(*len, This->cylinderSize - OFFSET); maximize(*len, This->cylinderSize - This->current % This->cylinderSize); return OUTSIDE; } } static int buf_read(Stream_t *Stream, char *buf, mt_off_t start, size_t len) { size_t length; mt_off_t offset; char *disk_ptr; int ret; DeclareThis(Buffer_t); if(!len) return 0; /*fprintf(stderr, "buf read %x %x %x\n", Stream, start, len);*/ switch(isInBuffer(This, start, &len)) { case OUTSIDE: case APPEND: /* always load until the end of the cylinder */ length = This->cylinderSize - (This->current + This->cur_size) % This->cylinderSize; maximize(length, This->size - This->cur_size); /* read it! */ ret=READS(This->Next, This->buf + This->cur_size, This->current + This->cur_size, length); if ( ret < 0 ) return ret; This->cur_size += ret; if (This->current+(mt_off_t)This->cur_size < start) { fprintf(stderr, "Short buffer fill\n"); exit(1); } break; case INSIDE: /* nothing to do */ break; case ERROR: return -1; } offset = OFFSET; disk_ptr = This->buf + offset; maximize(len, This->cur_size - offset); memcpy(buf, disk_ptr, len); return len; } static int buf_write(Stream_t *Stream, char *buf, mt_off_t start, size_t len) { char *disk_ptr; DeclareThis(Buffer_t); size_t offset=0; if(!len) return 0; This->ever_dirty = 1; #ifdef DEBUG fprintf(stderr, "buf write %x %02x %08x %08x -- %08x %08x -- %08x\n", Stream, (unsigned char) This->buf[0], start, len, This->current, This->cur_size, This->size); fprintf(stderr, "%d %d %d %x %x\n", start == This->current + This->cur_size, This->cur_size < This->size, len >= This->sectorSize, len, This->sectorSize); #endif switch(isInBuffer(This, start, &len)) { case OUTSIDE: #ifdef DEBUG fprintf(stderr, "outside\n"); #endif if(start % This->cylinderSize || len < This->sectorSize) { size_t readSize; int ret; readSize = This->cylinderSize - This->current % This->cylinderSize; ret=READS(This->Next, This->buf, This->current, readSize); /* read it! */ if ( ret < 0 ) return ret; if(ret % This->sectorSize) { fprintf(stderr, "Weird: read size (%d) not a multiple of sector size (%d)\n", ret, (int) This->sectorSize); ret -= ret % This->sectorSize; if(ret == 0) { fprintf(stderr, "Nothing left\n"); exit(1); } } This->cur_size = ret; /* for dosemu. Autoextend size */ if(!This->cur_size) { memset(This->buf,0,readSize); This->cur_size = readSize; } offset = OFFSET; break; } /* FALL THROUGH */ case APPEND: #ifdef DEBUG fprintf(stderr, "append\n"); #endif len = ROUND_DOWN(len, This->sectorSize); offset = OFFSET; maximize(len, This->size - offset); This->cur_size += len; if(This->Next->Class->pre_allocate) PRE_ALLOCATE(This->Next, This->current + This->cur_size); break; case INSIDE: /* nothing to do */ #ifdef DEBUG fprintf(stderr, "inside\n"); #endif offset = OFFSET; maximize(len, This->cur_size - offset); break; case ERROR: return -1; #ifdef DEBUG default: fprintf(stderr, "Should not happen\n"); exit(1); #endif } disk_ptr = This->buf + offset; /* extend if we write beyond end */ if(offset + len > This->cur_size) { len -= (offset + len) % This->sectorSize; This->cur_size = len + offset; } memcpy(disk_ptr, buf, len); if(!This->dirty || offset < This->dirty_pos) This->dirty_pos = ROUND_DOWN(offset, This->sectorSize); if(!This->dirty || offset + len > This->dirty_end) This->dirty_end = ROUND_UP(offset + len, This->sectorSize); if(This->dirty_end > This->cur_size) { fprintf(stderr, "Internal error, dirty end too big dirty_end=%x cur_size=%x len=%x offset=%d sectorSize=%x\n", (unsigned int) This->dirty_end, (unsigned int) This->cur_size, (unsigned int) len, (int) offset, (int) This->sectorSize); fprintf(stderr, "offset + len + grain - 1 = %x\n", (int) (offset + len + This->sectorSize - 1)); fprintf(stderr, "ROUNDOWN(offset + len + grain - 1) = %x\n", (int)ROUND_DOWN(offset + len + This->sectorSize - 1, This->sectorSize)); fprintf(stderr, "This->dirty = %d\n", This->dirty); exit(1); } This->dirty = 1; return len; } static int buf_flush(Stream_t *Stream) { int ret; DeclareThis(Buffer_t); if (!This->ever_dirty) return 0; ret = _buf_flush(This); if(ret == 0) This->ever_dirty = 0; return ret; } static int buf_free(Stream_t *Stream) { DeclareThis(Buffer_t); if(This->buf) free(This->buf); This->buf = 0; return 0; } static Class_t BufferClass = { buf_read, buf_write, buf_flush, buf_free, 0, /* set_geom */ get_data_pass_through, /* get_data */ 0, /* pre-allocate */ get_dosConvert_pass_through, /* dos convert */ 0, /* discard */ }; Stream_t *buf_init(Stream_t *Next, int size, int cylinderSize, int sectorSize) { Buffer_t *Buffer; Stream_t *Stream; if(size % cylinderSize != 0) { fprintf(stderr, "size not multiple of cylinder size\n"); exit(1); } if(cylinderSize % sectorSize != 0) { fprintf(stderr, "cylinder size not multiple of sector size\n"); exit(1); } if(Next->Buffer){ Next->refs--; Next->Buffer->refs++; return Next->Buffer; } Stream = (Stream_t *) malloc (sizeof(Buffer_t)); if(!Stream) return 0; Buffer = (Buffer_t *) Stream; Buffer->buf = malloc(size); if ( !Buffer->buf){ Free(Stream); return 0; } Buffer->size = size; Buffer->dirty = 0; Buffer->cylinderSize = cylinderSize; Buffer->sectorSize = sectorSize; Buffer->ever_dirty = 0; Buffer->dirty_pos = 0; Buffer->dirty_end = 0; Buffer->current = 0L; Buffer->cur_size = 0; /* buffer currently empty */ Buffer->Next = Next; Buffer->Class = &BufferClass; Buffer->refs = 1; Buffer->Buffer = 0; Buffer->Next->Buffer = (Stream_t *) Buffer; return Stream; }