xref: /aosp_15_r20/external/mtools/buffer.c (revision d5c9a868b113e0ec0db2f27bc2ce8a253e77c4b0)
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 
25 typedef struct Buffer_t {
26 	struct Stream_t head;
27 
28 	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;
38 	mt_off_t current;	/* first sector in buffer */
39 	size_t cur_size;	/* the current size */
40 	char *buf;		/* disk read/write buffer */
41 } Buffer_t;
42 
43 /* Convert position relative to buffer to absolute position */
abs_pos(Buffer_t * Buffer,size_t rel)44 static 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 */
cur_end(Buffer_t * Buffer)49 static 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 */
pos_to_next_full_cyl(Buffer_t * Buffer,mt_off_t pos)55 static 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 
60 /*
61  * Flush a dirty buffer to disk.  Resets Buffer->dirty to zero.
62  * All errors are fatal.
63  */
64 
_buf_flush(Buffer_t * Buffer)65 static int _buf_flush(Buffer_t *Buffer)
66 {
67 	ssize_t ret;
68 
69 #ifdef HAVE_ASSERT_H
70 	assert(Buffer->head.Next != NULL);
71 #endif
72 
73 	if (!Buffer->dirty)
74 		return 0;
75 #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 
83 	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");
94 		return -1;
95 	}
96 	Buffer->dirty = 0;
97 	Buffer->dirty_end = 0;
98 	Buffer->dirty_pos = 0;
99 	return 0;
100 }
101 
invalidate_buffer(Buffer_t * Buffer,mt_off_t start)102 static 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 */
110 	Buffer->current = ROUND_DOWN(start, (mt_off_t) Buffer->sectorSize);
111 	Buffer->cur_size = 0;
112 	return 0;
113 }
114 
115 #undef OFFSET
116 #define OFFSET ((size_t)(start - This->current))
117 
118 typedef enum position_t {
119 	OUTSIDE,
120 	APPEND,
121 	INSIDE,
122 	ERROR
123 } position_t;
124 
isInBuffer(Buffer_t * This,mt_off_t start,size_t * len)125 static position_t isInBuffer(Buffer_t *This, mt_off_t start, size_t *len)
126 {
127 	if(start >= This->current && start < cur_end(This)) {
128 		maximize(*len, This->cur_size - OFFSET);
129 		return INSIDE;
130 	} else if(start == cur_end(This) &&
131 		  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);
147 		maximize(*len, pos_to_next_full_cyl(This, This->current));
148 		return OUTSIDE;
149 	}
150 }
151 
buf_pread(Stream_t * Stream,char * buf,mt_off_t start,size_t len)152 static ssize_t buf_pread(Stream_t *Stream, char *buf,
153 			 mt_off_t start, size_t len)
154 {
155 	size_t length;
156 	size_t offset;
157 	char *disk_ptr;
158 	ssize_t ret;
159 	DeclareThis(Buffer_t);
160 
161 	if(!len)
162 		return 0;
163 
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 */
169 			length = pos_to_next_full_cyl(This, cur_end(This));
170 			maximize(length, This->size - This->cur_size);
171 
172 			/* read it! */
173 			ret=PREADS(This->head.Next,
174 				   This->buf + This->cur_size,
175 				   This->current + (mt_off_t) This->cur_size,
176 				   length);
177 			if ( ret < 0 )
178 				return ret;
179 			This->cur_size += (size_t) ret;
180 			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);
196 	return (ssize_t) len;
197 }
198 
buf_pwrite(Stream_t * Stream,char * buf,mt_off_t start,size_t len)199 static ssize_t buf_pwrite(Stream_t *Stream, char *buf,
200 			  mt_off_t start, size_t len)
201 {
202 	char *disk_ptr;
203 	DeclareThis(Buffer_t);
204 	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
225 			if(start % (mt_off_t) This->cylinderSize ||
226 			   len < This->sectorSize) {
227 				size_t readSize;
228 				ssize_t ret;
229 				size_t bytes_read;
230 
231 				readSize = This->cylinderSize -
232 					(size_t)(This->current % (mt_off_t) This->cylinderSize);
233 
234 				ret=PREADS(This->head.Next, This->buf,
235 					   (mt_off_t)This->current, readSize);
236 				/* read it! */
237 				if ( ret < 0 )
238 					return ret;
239 				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) {
244 					fprintf(stderr, "Nothing left\n");
245 					exit(1);
246 				    }
247 				}
248 				This->cur_size = bytes_read;
249 				/* 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;
266 			if(This->head.Next->Class->pre_allocate)
267 				PRE_ALLOCATE(This->head.Next, cur_end(This));
268 			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);
299 
300 	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;
317 	return (ssize_t) len;
318 }
319 
buf_flush(Stream_t * Stream)320 static 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 
buf_free(Stream_t * Stream)334 static 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 
344 static Class_t BufferClass = {
345 	0,
346 	0,
347 	buf_pread,
348 	buf_pwrite,
349 	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 
buf_init(Stream_t * Next,size_t size,size_t cylinderSize,size_t sectorSize)358 Stream_t *buf_init(Stream_t *Next, size_t size,
359 		   size_t cylinderSize,
360 		   size_t sectorSize)
361 {
362 	Buffer_t *Buffer;
363 
364 #ifdef HAVE_ASSERT_H
365 	assert(size != 0);
366 	assert(cylinderSize != 0);
367 	assert(sectorSize != 0);
368 	assert(Next != NULL);
369 #endif
370 
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 
380 	Buffer = New(Buffer_t);
381 	if(!Buffer)
382 		return 0;
383 	init_head(&Buffer->head, &BufferClass, Next);
384 	Buffer->buf = malloc(size);
385 	if ( !Buffer->buf){
386 		Free(Buffer);
387 		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 
400 	return &Buffer->head;
401 }
402 
403