Bump to 4.0.43
[platform/upstream/mtools.git] / buffer.c
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 "mtools.h"
22 #include "buffer.h"
23
24 typedef struct Buffer_t {
25         struct Stream_t head;
26
27         size_t size;            /* size of read/write buffer */
28         int dirty;              /* is the buffer dirty? */
29
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? */
35         size_t dirty_pos;
36         size_t dirty_end;
37         mt_off_t current;       /* first sector in buffer */
38         size_t cur_size;        /* the current size */
39         char *buf;              /* disk read/write buffer */
40 } Buffer_t;
41
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;
45 }
46
47 /* End of currently valid buffer */
48 static mt_off_t cur_end(Buffer_t *Buffer) {
49         return abs_pos(Buffer, Buffer->cur_size);
50 }
51
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);
57 }
58
59 /*
60  * Flush a dirty buffer to disk.  Resets Buffer->dirty to zero.
61  * All errors are fatal.
62  */
63
64 static int _buf_flush(Buffer_t *Buffer)
65 {
66         ssize_t ret;
67
68 #ifdef HAVE_ASSERT_H
69         assert(Buffer->head.Next != NULL);
70 #endif
71         
72         if (!Buffer->dirty)
73                 return 0;
74 #ifdef DEBUG
75         fprintf(stderr, "write %08x -- %02x %08x %08x\n",
76                 Buffer,
77                 (unsigned char) Buffer->buf[0],
78                 Buffer->current + Buffer->dirty_pos,
79                 Buffer->dirty_end - Buffer->dirty_pos);
80 #endif
81
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);
86         if(ret < 0) {
87                 perror("buffer_flush: write");
88                 return -1;
89         }
90         
91         if((size_t) ret != Buffer->dirty_end - Buffer->dirty_pos) {
92                 fprintf(stderr,"buffer_flush: short write\n");
93                 return -1;
94         }
95         Buffer->dirty = 0;
96         Buffer->dirty_end = 0;
97         Buffer->dirty_pos = 0;
98         return 0;
99 }
100
101 static int invalidate_buffer(Buffer_t *Buffer, mt_off_t start)
102 {
103         if(_buf_flush(Buffer) < 0)
104                 return -1;
105
106         /* start reading at the beginning of start's sector
107          * don't start reading too early, or we might not even reach
108          * start */
109         Buffer->current = ROUND_DOWN(start, (mt_off_t) Buffer->sectorSize);
110         Buffer->cur_size = 0;
111         return 0;
112 }
113
114 #undef OFFSET
115 #define OFFSET ((size_t)(start - This->current))
116
117 typedef enum position_t {
118         OUTSIDE,
119         APPEND,
120         INSIDE,
121         ERROR
122 } position_t;
123
124 static position_t isInBuffer(Buffer_t *This, mt_off_t start, size_t *len)
125 {
126         if(start >= This->current && start < cur_end(This)) {
127                 maximize(*len, This->cur_size - OFFSET);
128                 return INSIDE;
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
133                  * be met:
134                  *  1. The start falls exactly at the end of the currently
135                  *     loaded data
136                  *  2. There is still space
137                  *  3. We append at least one sector
138                  */
139                 maximize(*len, This->size - This->cur_size);
140                 *len = ROUND_DOWN(*len, This->sectorSize);
141                 return APPEND;
142         } else {
143                 if(invalidate_buffer(This, start) < 0)
144                         return ERROR;
145                 maximize(*len, This->cylinderSize - OFFSET);
146                 maximize(*len, pos_to_next_full_cyl(This, This->current));
147                 return OUTSIDE;
148         }
149 }
150
151 static ssize_t buf_pread(Stream_t *Stream, char *buf,
152                          mt_off_t start, size_t len)
153 {
154         size_t length;
155         size_t offset;
156         char *disk_ptr;
157         ssize_t ret;
158         DeclareThis(Buffer_t);
159
160         if(!len)
161                 return 0;
162
163         /*fprintf(stderr, "buf read %x   %x %x\n", Stream, start, len);*/
164         switch(isInBuffer(This, start, &len)) {
165                 case OUTSIDE:
166                 case APPEND:
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);
170
171                         /* read it! */
172                         ret=PREADS(This->head.Next,
173                                    This->buf + This->cur_size,
174                                    This->current + (mt_off_t) This->cur_size,
175                                    length);
176                         if ( ret < 0 )
177                                 return ret;
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");
181                                 exit(1);
182                         }
183                         break;
184                 case INSIDE:
185                         /* nothing to do */
186                         break;
187                 case ERROR:
188                         return -1;
189         }
190
191         offset = OFFSET;
192         disk_ptr = This->buf + offset;
193         maximize(len, This->cur_size - offset);
194         memcpy(buf, disk_ptr, len);
195         return (ssize_t) len;
196 }
197
198 static ssize_t buf_pwrite(Stream_t *Stream, char *buf,
199                           mt_off_t start, size_t len)
200 {
201         char *disk_ptr;
202         DeclareThis(Buffer_t);
203         size_t offset=0;
204
205         if(!len)
206                 return 0;
207
208         This->ever_dirty = 1;
209
210 #ifdef DEBUG
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);
218 #endif
219         switch(isInBuffer(This, start, &len)) {
220                 case OUTSIDE:
221 #ifdef DEBUG
222                         fprintf(stderr, "outside\n");
223 #endif
224                         if(start % (mt_off_t) This->cylinderSize ||
225                            len < This->sectorSize) {
226                                 size_t readSize;
227                                 ssize_t ret;
228                                 size_t bytes_read;
229
230                                 readSize = This->cylinderSize -
231                                         (size_t)(This->current % (mt_off_t) This->cylinderSize);
232
233                                 ret=PREADS(This->head.Next, This->buf,
234                                            (mt_off_t)This->current, readSize);
235                                 /* read it! */
236                                 if ( ret < 0 )
237                                         return ret;
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");
244                                         exit(1);
245                                     }
246                                 }
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;
252                                 }
253                                 offset = OFFSET;
254                                 break;
255                         }
256                         /* FALL THROUGH */
257                 case APPEND:
258 #ifdef DEBUG
259                         fprintf(stderr, "append\n");
260 #endif
261                         len = ROUND_DOWN(len, This->sectorSize);
262                         offset = OFFSET;
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));
267                         break;
268                 case INSIDE:
269                         /* nothing to do */
270 #ifdef DEBUG
271                         fprintf(stderr, "inside\n");
272 #endif
273                         offset = OFFSET;
274                         maximize(len, This->cur_size - offset);
275                         break;
276                 case ERROR:
277                         return -1;
278 #ifdef DEBUG
279                 default:
280                         fprintf(stderr, "Should not happen\n");
281                         exit(1);
282 #endif
283         }
284
285         disk_ptr = This->buf + offset;
286
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;
291         }
292
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);
298
299         if(This->dirty_end > This->cur_size) {
300                 fprintf(stderr,
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,
304                         (unsigned int) len,
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,
310                                                                 This->sectorSize));
311                 fprintf(stderr, "This->dirty = %d\n", This->dirty);
312                 exit(1);
313         }
314
315         This->dirty = 1;
316         return (ssize_t) len;
317 }
318
319 static int buf_flush(Stream_t *Stream)
320 {
321         int ret;
322         DeclareThis(Buffer_t);
323
324         if (!This->ever_dirty)
325                 return 0;
326         ret = _buf_flush(This);
327         if(ret == 0)
328                 This->ever_dirty = 0;
329         return ret;
330 }
331
332
333 static int buf_free(Stream_t *Stream)
334 {
335         DeclareThis(Buffer_t);
336
337         if(This->buf)
338                 free(This->buf);
339         This->buf = 0;
340         return 0;
341 }
342
343 static Class_t BufferClass = {
344         0,
345         0,
346         buf_pread,
347         buf_pwrite,
348         buf_flush,
349         buf_free,
350         0, /* set_geom */
351         get_data_pass_through, /* get_data */
352         0, /* pre-allocate */
353         get_dosConvert_pass_through, /* dos convert */
354         0, /* discard */
355 };
356
357 Stream_t *buf_init(Stream_t *Next, size_t size,
358                    size_t cylinderSize,
359                    size_t sectorSize)
360 {
361         Buffer_t *Buffer;
362
363 #ifdef HAVE_ASSERT_H
364         assert(size != 0);
365         assert(cylinderSize != 0);
366         assert(sectorSize != 0);
367         assert(Next != NULL);
368 #endif
369
370         if(size % cylinderSize != 0) {
371                 fprintf(stderr, "size not multiple of cylinder size\n");
372                 exit(1);
373         }
374         if(cylinderSize % sectorSize != 0) {
375                 fprintf(stderr, "cylinder size not multiple of sector size\n");
376                 exit(1);
377         }
378
379         Buffer = New(Buffer_t);
380         if(!Buffer)
381                 return 0;
382         init_head(&Buffer->head, &BufferClass, Next);
383         Buffer->buf = malloc(size);
384         if ( !Buffer->buf){
385                 Free(Buffer);
386                 return 0;
387         }
388         Buffer->size = size;
389         Buffer->dirty = 0;
390         Buffer->cylinderSize = cylinderSize;
391         Buffer->sectorSize = sectorSize;
392
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 */
398
399         return &Buffer->head;
400 }
401