1 /*  Copyright 1994,1996-2003,2005,2007,2009 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  * Io to an xdf disk
18  *
19  * written by:
20  *
21  * Alain L. Knaff
22  * alain@knaff.lu
23  *
24  */
25 
26 
27 #include "sysincludes.h"
28 #ifdef OS_linux
29 #include "msdos.h"
30 #include "mtools.h"
31 #include "devices.h"
32 #include "xdf_io.h"
33 
34 /* Algorithms can't be patented */
35 
36 typedef struct sector_map {
37 	unsigned int head:1;
38 	unsigned int size:7;
39 } sector_map_t;
40 
41 
42 static struct {
43   unsigned char track_size;
44   unsigned int track0_size:7;
45   unsigned int rootskip:1;
46   unsigned char rate;
47   sector_map_t map[9];
48 } xdf_table[]= {
49   {
50     19, 16, 0, 0,
51     {	{0,3},	{0,6},	{1,2},	{0,2},	{1,6},	{1,3},	{0,0} }
52   },
53   {
54     23, 19, 0, 0,
55     {	{0,3},	{0,4},	{1,6},	{0,2},	{1,2},	{0,6},	{1,4},	{1,3},	{0,0} }
56   },
57   {
58     46, 37, 1, 0x43,
59     {	{0,3},	{0,4},	{0,5},	{0,7},	{1,3},	{1,4},	{1,5},	{1,7},	{0,0} }
60   },
61   {
62     24, 20, 1, 0,
63     {	{0,5},	{1,6},	{0,6},	{1, 5} }
64   },
65   {
66     48, 41, 1, 0,
67     {	{0,6},	{1,7},	{0,7},	{1, 6} }
68   }
69 };
70 
71 #define NUMBER(x) (sizeof(x)/sizeof(x[0]))
72 
73 typedef struct {
74 	unsigned char begin; /* where it begins */
75 	unsigned char end;
76 	unsigned char sector;
77 	unsigned char sizecode;
78 
79 	unsigned int dirty:1;
80 	unsigned int phantom:2;
81 	unsigned int valid:1;
82 	unsigned int head:1;
83 } TrackMap_t;
84 
85 
86 
87 typedef struct Xdf_t {
88 	Class_t *Class;
89 	int refs;
90 	Stream_t *Next;
91 	Stream_t *Buffer;
92 
93 	int fd;
94 	char *buffer;
95 
96 	int current_track;
97 
98 	sector_map_t *map;
99 
100 	int track_size;
101 	int track0_size;
102 	int sector_size;
103 	unsigned int FatSize;
104 	unsigned int RootDirSize;
105 	TrackMap_t *track_map;
106 
107 	unsigned char last_sector;
108 	unsigned char rate;
109 
110 	unsigned int stretch:1;
111 	unsigned int rootskip:1;
112 	signed  int drive:4;
113 } Xdf_t;
114 
115 typedef struct {
116 	unsigned char head;
117 	unsigned char sector;
118 	unsigned char ptr;
119 } Compactify_t;
120 
121 
analyze_reply(RawRequest_t * raw_cmd,int do_print)122 static int analyze_reply(RawRequest_t *raw_cmd, int do_print)
123 {
124 	int ret, bytes, newbytes;
125 
126 	bytes = 0;
127 	while(1) {
128 		ret = analyze_one_reply(raw_cmd, &newbytes, do_print);
129 		bytes += newbytes;
130 		switch(ret) {
131 			case 0:
132 				return bytes;
133 			case 1:
134 				raw_cmd++;
135 				break;
136 			case -1:
137 				if(bytes)
138 					return bytes;
139 				else
140 					return 0;
141 		}
142 	}
143 }
144 
145 
146 
send_cmd(int fd,RawRequest_t * raw_cmd,int nr,const char * message,int retries)147 static int send_cmd(int fd, RawRequest_t *raw_cmd, int nr,
148 		    const char *message, int retries)
149 {
150 	int j;
151 	int ret=-1;
152 
153 	if(!nr)
154 		return 0;
155 	for (j=0; j< retries; j++){
156 		switch(send_one_cmd(fd, raw_cmd, message)) {
157 			case -1:
158 				return -1;
159 			case 1:
160 				j++;
161 				continue;
162 			case 0:
163 				break;
164 		}
165 		if((ret=analyze_reply(raw_cmd, j)) > 0)
166 			return ret; /* ok */
167 	}
168 	if(j > 1 && j == retries) {
169 		fprintf(stderr,"Too many errors, giving up\n");
170 		return 0;
171 	}
172 	return -1;
173 }
174 
175 
176 
177 #define REC (This->track_map[ptr])
178 #define END(x) (This->track_map[(x)].end)
179 #define BEGIN(x) (This->track_map[(x)].begin)
180 
add_to_request(Xdf_t * This,int ptr,RawRequest_t * request,int * nr,int direction,Compactify_t * compactify)181 static int add_to_request(Xdf_t *This, int ptr,
182 			  RawRequest_t *request, int *nr,
183 			  int direction, Compactify_t *compactify)
184 {
185 #if 0
186 	if(direction == MT_WRITE) {
187 		printf("writing %d: %d %d %d %d [%02x]\n",
188 		       ptr, This->current_track,
189 		       REC.head, REC.sector, REC.sizecode,
190 		       *(This->buffer + ptr * This->sector_size));
191 	} else
192 			printf(" load %d.%d\n", This->current_track, ptr);
193 #endif
194 	if(REC.phantom) {
195 		if(direction== MT_READ)
196 			memset(This->buffer + ptr * This->sector_size, 0,
197 			       128 << REC.sizecode);
198 		return 0;
199 	}
200 
201 	if(*nr &&
202 	   RR_SIZECODE(request+(*nr)-1) == REC.sizecode &&
203 	   compactify->head == REC.head &&
204 	   compactify->ptr + 1 == ptr &&
205 	   compactify->sector +1 == REC.sector) {
206 		RR_SETSIZECODE(request+(*nr)-1, REC.sizecode);
207 	} else {
208 		if(*nr)
209 			RR_SETCONT(request+(*nr)-1);
210 		RR_INIT(request+(*nr));
211 		RR_SETDRIVE(request+(*nr), This->drive);
212 		RR_SETRATE(request+(*nr), This->rate);
213 		RR_SETTRACK(request+(*nr), This->current_track);
214 		RR_SETPTRACK(request+(*nr),
215 			     This->current_track << This->stretch);
216 		RR_SETHEAD(request+(*nr), REC.head);
217 		RR_SETSECTOR(request+(*nr), REC.sector);
218 		RR_SETSIZECODE(request+(*nr), REC.sizecode);
219 		RR_SETDIRECTION(request+(*nr), direction);
220 		RR_SETDATA(request+(*nr),
221 			   (caddr_t) This->buffer + ptr * This->sector_size);
222 		(*nr)++;
223 	}
224 	compactify->ptr = ptr;
225 	compactify->head = REC.head;
226 	compactify->sector = REC.sector;
227 	return 0;
228 }
229 
230 
add_to_request_if_invalid(Xdf_t * This,int ptr,RawRequest_t * request,int * nr,Compactify_t * compactify)231 static void add_to_request_if_invalid(Xdf_t *This, int ptr,
232 				     RawRequest_t *request, int *nr,
233 				     Compactify_t *compactify)
234 {
235 	if(!REC.valid)
236 		add_to_request(This, ptr, request, nr, MT_READ, compactify);
237 
238 }
239 
240 
adjust_bounds(Xdf_t * This,off_t * begin,off_t * end)241 static void adjust_bounds(Xdf_t *This, off_t *begin, off_t *end)
242 {
243 	/* translates begin and end from byte to sectors */
244 	*begin = *begin / This->sector_size;
245 	*end = (*end + This->sector_size - 1) / This->sector_size;
246 }
247 
248 
try_flush_dirty(Xdf_t * This)249 static __inline__ int try_flush_dirty(Xdf_t *This)
250 {
251 	int ptr, nr, bytes;
252 	RawRequest_t requests[100];
253 	Compactify_t compactify;
254 
255 	if(This->current_track < 0)
256 		return 0;
257 
258 	nr = 0;
259 	for(ptr=0; ptr < This->last_sector; ptr=REC.end)
260 		if(REC.dirty)
261 			add_to_request(This, ptr,
262 				       requests, &nr,
263 				       MT_WRITE, &compactify);
264 #if 1
265 	bytes = send_cmd(This->fd,requests, nr, "writing", 4);
266 	if(bytes < 0)
267 		return bytes;
268 #else
269 	bytes = 0xffffff;
270 #endif
271 	for(ptr=0; ptr < This->last_sector; ptr=REC.end)
272 		if(REC.dirty) {
273 			if(bytes >= REC.end - REC.begin) {
274 				bytes -= REC.end - REC.begin;
275 				REC.dirty = 0;
276 			} else
277 				return 1;
278 		}
279 	return 0;
280 }
281 
282 
283 
flush_dirty(Xdf_t * This)284 static int flush_dirty(Xdf_t *This)
285 {
286 	int ret;
287 
288 	while((ret = try_flush_dirty(This))) {
289 		if(ret < 0)
290 			return ret;
291 	}
292 	return 0;
293 }
294 
295 
load_data(Xdf_t * This,off_t begin,off_t end,int retries)296 static int load_data(Xdf_t *This, off_t begin, off_t end, int retries)
297 {
298 	int ptr, nr, bytes;
299 	RawRequest_t requests[100];
300 	Compactify_t compactify;
301 
302 	adjust_bounds(This, &begin, &end);
303 
304 	ptr = begin;
305 	nr = 0;
306 	for(ptr=REC.begin; ptr < end ; ptr = REC.end)
307 		add_to_request_if_invalid(This, ptr, requests, &nr,
308 					  &compactify);
309 	bytes = send_cmd(This->fd,requests, nr, "reading", retries);
310 	if(bytes < 0)
311 		return bytes;
312 	ptr = begin;
313 	for(ptr=REC.begin; ptr < end ; ptr = REC.end) {
314 		if(!REC.valid) {
315 			if(bytes >= REC.end - REC.begin) {
316 				bytes -= REC.end - REC.begin;
317 				REC.valid = 1;
318 			} else if(ptr > begin)
319 				return ptr * This->sector_size;
320 			else
321 				return -1;
322 		}
323 	}
324 	return end * This->sector_size;
325 }
326 
mark_dirty(Xdf_t * This,off_t begin,off_t end)327 static void mark_dirty(Xdf_t *This, off_t begin, off_t end)
328 {
329 	int ptr;
330 
331 	adjust_bounds(This, &begin, &end);
332 
333 	ptr = begin;
334 	for(ptr=REC.begin; ptr < end ; ptr = REC.end) {
335 		REC.valid = 1;
336 		if(!REC.phantom)
337 			REC.dirty = 1;
338 	}
339 }
340 
341 
load_bounds(Xdf_t * This,off_t begin,off_t end)342 static int load_bounds(Xdf_t *This, off_t begin, off_t end)
343 {
344 	off_t lbegin, lend;
345 	int endp1, endp2;
346 
347 	lbegin = begin;
348 	lend = end;
349 
350 	adjust_bounds(This, &lbegin, &lend);
351 
352 	if(begin != BEGIN(lbegin) * This->sector_size &&
353 	   end != BEGIN(lend) * This->sector_size &&
354 	   lend < END(END(lbegin)))
355 		/* contiguous end & begin, load them in one go */
356 		return load_data(This, begin, end, 4);
357 
358 	if(begin != BEGIN(lbegin) * This->sector_size) {
359 		endp1 = load_data(This, begin, begin, 4);
360 		if(endp1 < 0)
361 			return endp1;
362 	}
363 
364 	if(end != BEGIN(lend) * This->sector_size) {
365 		endp2 = load_data(This, end, end, 4);
366 		if(endp2 < 0)
367 			return BEGIN(lend) * This->sector_size;
368 	}
369 	return lend * This->sector_size;
370 }
371 
372 
fill_t0(Xdf_t * This,int ptr,unsigned int size,int * sector,int * head)373 static int fill_t0(Xdf_t *This, int ptr, unsigned int size,
374 		   int *sector, int *head)
375 {
376 	int n;
377 
378 	for(n = 0; n < size; ptr++,n++) {
379 		REC.head = *head;
380 		REC.sector = *sector + 129;
381 		REC.phantom = 0;
382 		(*sector)++;
383 		if(!*head && *sector >= This->track0_size - 8) {
384 			*sector = 0;
385 			*head = 1;
386 		}
387 	}
388 	return ptr;
389 }
390 
391 
fill_phantoms(Xdf_t * This,int ptr,unsigned int size)392 static int fill_phantoms(Xdf_t *This, int ptr, unsigned int size)
393 {
394 	int n;
395 
396 	for(n = 0; n < size; ptr++,n++)
397 		REC.phantom = 1;
398 	return ptr;
399 }
400 
decompose(Xdf_t * This,int where,int len,off_t * begin,off_t * end,int boot)401 static void decompose(Xdf_t *This, int where, int len, off_t *begin,
402 					  off_t *end, int boot)
403 {
404 	int ptr, track;
405 	sector_map_t *map;
406 	int lbegin, lend;
407 
408 	track = where / This->track_size / 1024;
409 
410 	*begin = where - track * This->track_size * 1024;
411 	*end = where + len - track * This->track_size * 1024;
412 	maximize(*end, This->track_size * 1024);
413 
414 	if(This->current_track == track && !boot)
415 		/* already OK, return immediately */
416 		return;
417 	if(!boot)
418 		flush_dirty(This);
419 	This->current_track = track;
420 
421 	if(track) {
422 		for(ptr=0, map=This->map; map->size; map++) {
423 			/* iterate through all sectors */
424 			lbegin = ptr;
425 			lend = ptr + (128 << map->size) / This->sector_size;
426 			for( ; ptr < lend ; ptr++) {
427 				REC.begin = lbegin;
428 				REC.end = lend;
429 
430 				REC.head = map->head;
431 				REC.sector = map->size + 128;
432 				REC.sizecode = map->size;
433 
434 				REC.valid = 0;
435 				REC.dirty = 0;
436 				REC.phantom = 0;
437 			}
438 		}
439 		REC.begin = REC.end = ptr;
440 	} else {
441 		int sector, head;
442 
443 		head = 0;
444 		sector = 0;
445 
446 		for(ptr=boot; ptr < 2 * This->track_size; ptr++) {
447 			REC.begin = ptr;
448 			REC.end = ptr+1;
449 
450 			REC.sizecode = 2;
451 
452 			REC.valid = 0;
453 			REC.dirty = 0;
454 		}
455 
456 		/* boot & 1st fat */
457 		ptr=fill_t0(This, 0, 1 + This->FatSize, &sector, &head);
458 
459 		/* second fat */
460 		ptr=fill_phantoms(This, ptr, This->FatSize);
461 
462 		/* root dir */
463 		ptr=fill_t0(This, ptr, This->RootDirSize, &sector, &head);
464 
465 		/* "bad sectors" at the beginning of the fs */
466 		ptr=fill_phantoms(This, ptr, 5);
467 
468 		if(This->rootskip)
469 			sector++;
470 
471 		/* beginning of the file system */
472 		ptr = fill_t0(This, ptr,
473 			      (This->track_size - This->FatSize) * 2 -
474 			      This->RootDirSize - 6,
475 			      &sector, &head);
476 	}
477 	This->last_sector = ptr;
478 }
479 
480 
xdf_read(Stream_t * Stream,char * buf,mt_off_t where,size_t len)481 static int xdf_read(Stream_t *Stream, char *buf, mt_off_t where, size_t len)
482 {
483 	off_t begin, end;
484 	size_t len2;
485 	DeclareThis(Xdf_t);
486 
487 	decompose(This, truncBytes32(where), len, &begin, &end, 0);
488 	len2 = load_data(This, begin, end, 4);
489 	len2 -= begin;
490 	maximize(len, len2);
491 	memcpy(buf, This->buffer + begin, len);
492 	return end - begin;
493 }
494 
xdf_write(Stream_t * Stream,char * buf,mt_off_t where,size_t len)495 static int xdf_write(Stream_t *Stream, char *buf, mt_off_t where, size_t len)
496 {
497 	off_t begin, end;
498 	size_t len2;
499 	DeclareThis(Xdf_t);
500 
501 	decompose(This, truncBytes32(where), len, &begin, &end, 0);
502 	len2 = load_bounds(This, begin, end);
503 	smaximize(end, (off_t)len2);
504 	len2 -= begin;
505 	sizemaximize(len, (off_t)len2);
506 	memcpy(This->buffer + begin, buf, len);
507 	mark_dirty(This, begin, end);
508 	return end - begin;
509 }
510 
xdf_flush(Stream_t * Stream)511 static int xdf_flush(Stream_t *Stream)
512 {
513 	DeclareThis(Xdf_t);
514 
515 	return flush_dirty(This);
516 }
517 
xdf_free(Stream_t * Stream)518 static int xdf_free(Stream_t *Stream)
519 {
520 	DeclareThis(Xdf_t);
521 	Free(This->track_map);
522 	Free(This->buffer);
523 	return close(This->fd);
524 }
525 
526 
check_geom(struct device * dev,int media,union bootsector * boot)527 static int check_geom(struct device *dev, int media, union bootsector *boot)
528 {
529 	int sect;
530 
531 	if(media >= 0xfc && media <= 0xff)
532 		return 1; /* old DOS */
533 
534 	if (!IS_MFORMAT_ONLY(dev)) {
535 	    if(compare(dev->sectors, 19) &&
536 	       compare(dev->sectors, 23) &&
537 	       compare(dev->sectors, 24) &&
538 	       compare(dev->sectors, 46) &&
539 	       compare(dev->sectors, 48))
540 		return 1;
541 
542 	    /* check against contradictory info from configuration file */
543 	    if(compare(dev->heads, 2))
544 		return 1;
545 	}
546 
547 	/* check against info from boot */
548 	if(boot) {
549 		sect = WORD(nsect);
550 		if((sect != 19 && sect != 23 && sect != 24 &&
551 		    sect != 46 && sect != 48) ||
552 		   (!IS_MFORMAT_ONLY(dev) && compare(dev->sectors, sect)) ||
553 		   WORD(nheads) !=2)
554 		    return 1;
555 	}
556 	return 0;
557 }
558 
set_geom(union bootsector * boot,struct device * dev)559 static void set_geom(union bootsector *boot, struct device *dev)
560 {
561 	/* fill in config info to be returned to user */
562 	dev->heads = 2;
563 	dev->use_2m = 0xff;
564 	if(boot) {
565 		dev->sectors = WORD(nsect);
566 		if(WORD(psect))
567 			dev->tracks = WORD(psect) / dev->sectors / 2;
568 	}
569 }
570 
config_geom(Stream_t * Stream UNUSEDP,struct device * dev,struct device * orig_dev UNUSEDP,int media,union bootsector * boot)571 static int config_geom(Stream_t *Stream UNUSEDP, struct device *dev,
572 		       struct device *orig_dev UNUSEDP, int media,
573 		       union bootsector *boot)
574 {
575 	if(check_geom(dev, media, boot))
576 		return 1;
577 	set_geom(boot,dev);
578 	return 0;
579 }
580 
581 static Class_t XdfClass = {
582 	xdf_read,
583 	xdf_write,
584 	xdf_flush,
585 	xdf_free,
586 	config_geom,
587 	0, /* get_data */
588 	0, /* pre-allocate */
589 	0, /* get_dosConvert */
590 	0 /* discard */
591 };
592 
XdfOpen(struct device * dev,char * name,int mode,char * errmsg,struct xdf_info * info)593 Stream_t *XdfOpen(struct device *dev, char *name,
594 		  int mode, char *errmsg, struct xdf_info *info)
595 {
596 	Xdf_t *This;
597 	off_t begin, end;
598 	union bootsector *boot;
599 	unsigned int type;
600 
601 	if(dev && (!SHOULD_USE_XDF(dev) || check_geom(dev, 0, 0)))
602 		return NULL;
603 
604 	This = New(Xdf_t);
605 	if (!This)
606 		return NULL;
607 
608 	This->Class = &XdfClass;
609 	This->sector_size = 512;
610 	This->stretch = 0;
611 
612 	precmd(dev);
613 	This->fd = open(name, mode | dev->mode | O_EXCL | O_NDELAY);
614 	if(This->fd < 0) {
615 #ifdef HAVE_SNPRINTF
616 		snprintf(errmsg,199,"xdf floppy: open: \"%s\"", strerror(errno));
617 #else
618 		sprintf(errmsg,"xdf floppy: open: \"%s\"", strerror(errno));
619 #endif
620 		goto exit_0;
621 	}
622 	closeExec(This->fd);
623 
624 	This->drive = GET_DRIVE(This->fd);
625 	if(This->drive < 0)
626 		goto exit_1;
627 
628 	/* allocate buffer */
629 	This->buffer = (char *) malloc(96 * 512);
630 	if (!This->buffer)
631 		goto exit_1;
632 
633 	This->current_track = -1;
634 	This->track_map = (TrackMap_t *)
635 		calloc(96, sizeof(TrackMap_t));
636 	if(!This->track_map)
637 		goto exit_2;
638 
639 	/* lock the device on writes */
640 	if (lock_dev(This->fd, mode == O_RDWR, dev)) {
641 #ifdef HAVE_SNPRINTF
642 		snprintf(errmsg,199,"xdf floppy: device \"%s\" busy:",
643 			dev->name);
644 #else
645 		sprintf(errmsg,"xdf floppy: device \"%s\" busy:",
646 			dev->name);
647 #endif
648 		goto exit_3;
649 	}
650 
651 	/* Before reading the boot sector, assume dummy values suitable
652 	 * for reading at least the boot sector */
653 	This->track_size = 11;
654 	This->track0_size = 6;
655 	This->rate = 0;
656 	This->FatSize = 9;
657 	This->RootDirSize = 1;
658 	decompose(This, 0, 512, &begin, &end, 0);
659 	if (load_data(This, 0, 1, 1) < 0 ) {
660 		This->rate = 0x43;
661 		if(load_data(This, 0, 1, 1) < 0)
662 			goto exit_3;
663 	}
664 
665 	boot = (union bootsector *) This->buffer;
666 	This->FatSize = WORD(fatlen);
667 	This->RootDirSize = WORD(dirents)/16;
668 	This->track_size = WORD(nsect);
669 	for(type=0; type < NUMBER(xdf_table); type++) {
670 		if(xdf_table[type].track_size == This->track_size) {
671 			This->map = xdf_table[type].map;
672 			This->track0_size = xdf_table[type].track0_size;
673 			This->rootskip = xdf_table[type].rootskip;
674 			This->rate = xdf_table[type].rate;
675 			break;
676 		}
677 	}
678 	if(type == NUMBER(xdf_table))
679 		goto exit_3;
680 
681 	if(info) {
682 		info->RootDirSize = This->RootDirSize;
683 		info->FatSize = This->FatSize;
684 		info->BadSectors = 5;
685 	}
686 	decompose(This, 0, 512, &begin, &end, 1);
687 
688 	This->refs = 1;
689 	This->Next = 0;
690 	This->Buffer = 0;
691 	if(dev)
692 		set_geom(boot, dev);
693 	return (Stream_t *) This;
694 
695 exit_3:
696 	Free(This->track_map);
697 exit_2:
698 	Free(This->buffer);
699 exit_1:
700 	close(This->fd);
701 exit_0:
702 	Free(This);
703 	return NULL;
704 }
705 
706 #endif
707 
708 /* Algorithms can't be patented */
709 
710