• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 	struct Stream_t head;
89 
90 	int fd;
91 	char *buffer;
92 
93 	bool track_valid;
94 	uint8_t current_track;
95 
96 	sector_map_t *map;
97 
98 	uint32_t track_size;
99 	int track0_size;
100 	uint16_t sector_size;
101 	uint8_t FatSize;
102 	uint16_t RootDirSize;
103 	TrackMap_t *track_map;
104 
105 	unsigned char last_sector;
106 	unsigned char rate;
107 
108 	unsigned int stretch:1;
109 	unsigned int rootskip:1;
110 	signed  int drive:4;
111 } Xdf_t;
112 
113 typedef struct {
114 	unsigned char head;
115 	unsigned char sector;
116 	unsigned char ptr;
117 } Compactify_t;
118 
119 
analyze_reply(RawRequest_t * raw_cmd,int do_print)120 static int analyze_reply(RawRequest_t *raw_cmd, int do_print)
121 {
122 	int ret, bytes, newbytes;
123 
124 	bytes = 0;
125 	while(1) {
126 		ret = analyze_one_reply(raw_cmd, &newbytes, do_print);
127 		bytes += newbytes;
128 		switch(ret) {
129 			case 0:
130 				return bytes;
131 			case 1:
132 				raw_cmd++;
133 				break;
134 			case -1:
135 				if(bytes)
136 					return bytes;
137 				else
138 					return 0;
139 		}
140 	}
141 }
142 
143 
144 
send_cmd(int fd,RawRequest_t * raw_cmd,int nr,const char * message,int retries)145 static int send_cmd(int fd, RawRequest_t *raw_cmd, int nr,
146 		    const char *message, int retries)
147 {
148 	int j;
149 	int ret=-1;
150 
151 	if(!nr)
152 		return 0;
153 	for (j=0; j< retries; j++){
154 		switch(send_one_cmd(fd, raw_cmd, message)) {
155 			case -1:
156 				return -1;
157 			case 1:
158 				j++;
159 				continue;
160 			case 0:
161 				break;
162 		}
163 		if((ret=analyze_reply(raw_cmd, j)) > 0)
164 			return ret; /* ok */
165 	}
166 	if(j > 1 && j == retries) {
167 		fprintf(stderr,"Too many errors, giving up\n");
168 		return 0;
169 	}
170 	return -1;
171 }
172 
173 
174 
175 #define REC (This->track_map[ptr])
176 #define END(x) (This->track_map[(x)].end)
177 #define BEGIN(x) (This->track_map[(x)].begin)
178 
add_to_request(Xdf_t * This,unsigned char ptr,RawRequest_t * request,int * nr,int direction,Compactify_t * compactify)179 static int add_to_request(Xdf_t *This, unsigned char ptr,
180 			  RawRequest_t *request, int *nr,
181 			  int direction, Compactify_t *compactify)
182 {
183 #if 0
184 	if(direction == MT_WRITE) {
185 		printf("writing %d: %u %d %d %d [%02x]\n",
186 		       ptr, This->current_track,
187 		       REC.head, REC.sector, REC.sizecode,
188 		       *(This->buffer + ptr * This->sector_size));
189 	} else
190 			printf(" load %d.%u\n", This->current_track, ptr);
191 #endif
192 	if(REC.phantom) {
193 		if(direction== MT_READ)
194 			memset(This->buffer + ptr * This->sector_size, 0,
195 			       128u << REC.sizecode);
196 		return 0;
197 	}
198 
199 	if(*nr &&
200 	   RR_SIZECODE(request+(*nr)-1) == REC.sizecode &&
201 	   compactify->head == REC.head &&
202 	   compactify->ptr + 1 == ptr &&
203 	   compactify->sector +1 == REC.sector) {
204 		RR_SETSIZECODE(request+(*nr)-1, REC.sizecode);
205 	} else {
206 		if(*nr)
207 			RR_SETCONT(request+(*nr)-1);
208 		RR_INIT(request+(*nr));
209 		RR_SETDRIVE(request+(*nr), This->drive);
210 		RR_SETRATE(request+(*nr), This->rate);
211 		RR_SETTRACK(request+(*nr), This->current_track);
212 		RR_SETPTRACK(request+(*nr),
213 			     This->current_track << This->stretch);
214 		RR_SETHEAD(request+(*nr), REC.head);
215 		RR_SETSECTOR(request+(*nr), REC.sector);
216 		RR_SETSIZECODE(request+(*nr), REC.sizecode);
217 		RR_SETDIRECTION(request+(*nr), direction);
218 		RR_SETDATA(request+(*nr),
219 			   (caddr_t) This->buffer + ptr * This->sector_size);
220 		(*nr)++;
221 	}
222 	compactify->ptr = ptr;
223 	compactify->head = REC.head;
224 	compactify->sector = REC.sector;
225 	return 0;
226 }
227 
228 
add_to_request_if_invalid(Xdf_t * This,unsigned char ptr,RawRequest_t * request,int * nr,Compactify_t * compactify)229 static void add_to_request_if_invalid(Xdf_t *This, unsigned char ptr,
230 				     RawRequest_t *request, int *nr,
231 				     Compactify_t *compactify)
232 {
233 	if(!REC.valid)
234 		add_to_request(This, ptr, request, nr, MT_READ, compactify);
235 
236 }
237 
238 
adjust_bounds(Xdf_t * This,uint32_t ibegin,uint32_t iend,uint8_t * begin,uint8_t * end)239 static void adjust_bounds(Xdf_t *This, uint32_t ibegin, uint32_t iend,
240 			  uint8_t *begin, uint8_t *end)
241 {
242 	/* translates begin and end from byte to sectors */
243 	*begin = (uint8_t) (ibegin / This->sector_size);
244 	*end = (uint8_t) ((iend + This->sector_size - 1) / This->sector_size);
245 }
246 
247 
try_flush_dirty(Xdf_t * This)248 static __inline__ int try_flush_dirty(Xdf_t *This)
249 {
250 	unsigned char ptr;
251 	int nr, bytes;
252 	RawRequest_t requests[100];
253 	Compactify_t compactify;
254 
255 	if(!This->track_valid)
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,uint32_t ibegin,uint32_t iend,int retries)296 static ssize_t load_data(Xdf_t *This, uint32_t ibegin, uint32_t iend,
297 			 int retries)
298 {
299 	unsigned char ptr;
300 	int nr, bytes;
301 	RawRequest_t requests[100];
302 	Compactify_t compactify;
303 	unsigned char begin, end;
304 	adjust_bounds(This, ibegin, iend, &begin, &end);
305 
306 	ptr = begin;
307 	nr = 0;
308 	for(ptr=REC.begin; ptr < end ; ptr = REC.end)
309 		add_to_request_if_invalid(This, ptr, requests, &nr,
310 					  &compactify);
311 	bytes = send_cmd(This->fd,requests, nr, "reading", retries);
312 	if(bytes < 0)
313 		return bytes;
314 	ptr = begin;
315 	for(ptr=REC.begin; ptr < end ; ptr = REC.end) {
316 		if(!REC.valid) {
317 			if(bytes >= REC.end - REC.begin) {
318 				bytes -= REC.end - REC.begin;
319 				REC.valid = 1;
320 			} else if(ptr > begin)
321 				return ptr * This->sector_size;
322 			else
323 				return -1;
324 		}
325 	}
326 	return end * This->sector_size;
327 }
328 
mark_dirty(Xdf_t * This,uint32_t ibegin,uint32_t iend)329 static void mark_dirty(Xdf_t *This, uint32_t ibegin, uint32_t iend)
330 {
331 	int ptr;
332 	unsigned char begin, end;
333 
334 	adjust_bounds(This, ibegin, iend, &begin, &end);
335 
336 	ptr = begin;
337 	for(ptr=REC.begin; ptr < end ; ptr = REC.end) {
338 		REC.valid = 1;
339 		if(!REC.phantom)
340 			REC.dirty = 1;
341 	}
342 }
343 
344 
load_bounds(Xdf_t * This,uint32_t begin,uint32_t end)345 static ssize_t load_bounds(Xdf_t *This, uint32_t begin, uint32_t end)
346 {
347 	unsigned char lbegin, lend;
348 
349 	adjust_bounds(This, begin, end, &lbegin, &lend);
350 
351 	if(begin != BEGIN(lbegin) * This->sector_size &&
352 	   end != BEGIN(lend) * This->sector_size &&
353 	   lend < END(END(lbegin)))
354 		/* contiguous end & begin, load them in one go */
355 		return load_data(This, begin, end, 4);
356 
357 	if(begin != BEGIN(lbegin) * This->sector_size) {
358 		ssize_t ret = load_data(This, begin, begin, 4);
359 		if(ret < 0)
360 			return ret;
361 	}
362 
363 	if(end != BEGIN(lend) * This->sector_size) {
364 		ssize_t ret = load_data(This, end, end, 4);
365 		if(ret < 0)
366 			return BEGIN(lend) * This->sector_size;
367 	}
368 	return lend * This->sector_size;
369 }
370 
371 /* Fill out a map that is just sufficient to read boot sector */
fill_boot(Xdf_t * This)372 static void fill_boot(Xdf_t *This)
373 {
374 	uint8_t ptr=0;
375 
376 	REC.head = 0;
377 	REC.sector = 129;
378 	REC.phantom = 0;
379 
380 	REC.begin = ptr;
381 	REC.end = ptr+1;
382 
383 	REC.sizecode = 2;
384 
385 	REC.valid = 0;
386 	REC.dirty = 0;
387 	This->last_sector=1;
388 	This->current_track=0;
389 }
390 
391 
fill_t0(Xdf_t * This,uint8_t ptr,unsigned int size,uint8_t * sector,uint8_t * head)392 static uint8_t fill_t0(Xdf_t *This, uint8_t ptr, unsigned int size,
393 		       uint8_t *sector, uint8_t *head)
394 {
395 	unsigned int n;
396 
397 	for(n = 0; n < size; ptr++,n++) {
398 		REC.head = *head;
399 		REC.sector = *sector + 129;
400 		REC.phantom = 0;
401 		(*sector)++;
402 		if(!*head && *sector >= This->track0_size - 8) {
403 			*sector = 0;
404 			*head = 1;
405 		}
406 	}
407 	return ptr;
408 }
409 
410 
fill_phantoms(Xdf_t * This,uint8_t ptr,uint8_t size)411 static uint8_t fill_phantoms(Xdf_t *This, uint8_t ptr, uint8_t size)
412 {
413 	unsigned int n;
414 
415 	for(n = 0; n < size; ptr++,n++)
416 		REC.phantom = 1;
417 	return ptr;
418 }
419 
decompose(Xdf_t * This,mt_off_t iwhere,size_t len,uint32_t * begin,uint32_t * end,uint8_t boot)420 static int decompose(Xdf_t *This, mt_off_t iwhere, size_t len,
421 		     uint32_t *begin, uint32_t *end, uint8_t boot)
422 {
423 	uint8_t ptr;
424 	sector_map_t *map;
425 	uint8_t lbegin, lend;
426 	uint32_t track_size = This->track_size * 1024;
427 
428 	smt_off_t track = (smt_off_t) iwhere / track_size;
429 	uint32_t where = (smt_off_t) iwhere % track_size;
430 
431 	*begin = where;
432 	if(where + len > track_size)
433 		*end = track_size;
434 	else
435 		*end = (uint32_t) (where + len);
436 
437 	if(This->current_track == track && !boot)
438 		/* already OK, return immediately */
439 		return 0;
440 	if(!boot)
441 		flush_dirty(This);
442 	if(track >= 80)
443 		return -1;
444 	This->current_track = (uint8_t) track;
445 	This->track_valid = true;
446 
447 	if(track) {
448 		for(ptr=0, map=This->map; map->size; map++) {
449 			/* iterate through all sectors */
450 			lbegin = ptr;
451 			lend = ptr +
452 				(uint8_t) ((128u<<map->size)/This->sector_size);
453 			for( ; ptr < lend ; ptr++) {
454 				REC.begin = lbegin;
455 				REC.end = lend;
456 
457 				REC.head = map->head;
458 				REC.sector = map->size + 128;
459 				REC.sizecode = map->size;
460 
461 				REC.valid = 0;
462 				REC.dirty = 0;
463 				REC.phantom = 0;
464 			}
465 		}
466 		REC.begin = REC.end = ptr;
467 	} else {
468 		uint8_t sector, head;
469 
470 		head = 0;
471 		sector = 0;
472 
473 		for(ptr=boot; ptr < 2 * This->track_size; ptr++) {
474 			REC.begin = ptr;
475 			REC.end = ptr+1;
476 
477 			REC.sizecode = 2;
478 
479 			REC.valid = 0;
480 			REC.dirty = 0;
481 		}
482 
483 		/* boot & 1st fat */
484 		ptr=fill_t0(This, 0, 1 + This->FatSize, &sector, &head);
485 
486 		/* second fat */
487 		ptr=fill_phantoms(This, ptr, This->FatSize);
488 
489 		/* root dir */
490 		ptr=fill_t0(This, ptr, This->RootDirSize, &sector, &head);
491 
492 		/* "bad sectors" at the beginning of the fs */
493 		ptr=fill_phantoms(This, ptr, 5);
494 
495 		if(This->rootskip)
496 			sector++;
497 
498 		/* beginning of the file system */
499 		ptr = fill_t0(This, ptr,
500 			      (This->track_size - This->FatSize) * 2 -
501 			      This->RootDirSize - 6,
502 			      &sector, &head);
503 	}
504 	This->last_sector = ptr;
505 	return 0;
506 }
507 
508 
xdf_pread(Stream_t * Stream,char * buf,mt_off_t where,size_t len)509 static ssize_t xdf_pread(Stream_t *Stream, char *buf,
510 			 mt_off_t where, size_t len)
511 {
512 	uint32_t begin, end;
513 	ssize_t ret;
514 	DeclareThis(Xdf_t);
515 
516 	if(decompose(This, truncBytes32(where), len, &begin, &end, 0) < 0)
517 		/* Read beyond end of device */
518 		return 0;
519 	ret = load_data(This, begin, end, 4);
520 	if(ret < 0 || (size_t) ret < begin)
521 		return -1;
522 	maximize(len, (size_t) ret - begin);
523 	memcpy(buf, This->buffer + begin, len);
524 	return (ssize_t) (end - begin);
525 }
526 
xdf_pwrite(Stream_t * Stream,char * buf,mt_off_t where,size_t len)527 static ssize_t xdf_pwrite(Stream_t *Stream, char *buf,
528 			  mt_off_t where, size_t len)
529 {
530 	uint32_t begin, end;
531 	ssize_t len2;
532 	DeclareThis(Xdf_t);
533 
534 	if(decompose(This, truncBytes32(where), len, &begin, &end, 0) < 0) {
535 		/* Write beyond end of device */
536 		errno = EFBIG;
537 		return -1;
538 	}
539 
540 	len2 = load_bounds(This, begin, end);
541 	if(len2 < 0)
542 		return -1;
543 	maximize(end, (uint32_t)len2);
544 	len2 -= begin;
545 	maximize(len, (size_t) len2);
546 	memcpy(This->buffer + begin, buf, len);
547 	mark_dirty(This, begin, end);
548 	return (ssize_t) (end - begin);
549 }
550 
xdf_flush(Stream_t * Stream)551 static int xdf_flush(Stream_t *Stream)
552 {
553 	DeclareThis(Xdf_t);
554 
555 	return flush_dirty(This);
556 }
557 
xdf_free(Stream_t * Stream)558 static int xdf_free(Stream_t *Stream)
559 {
560 	DeclareThis(Xdf_t);
561 	Free(This->track_map);
562 	Free(This->buffer);
563 	return close(This->fd);
564 }
565 
566 
check_geom(Xdf_t * This,struct device * dev)567 static int check_geom(Xdf_t *This, struct device *dev)
568 {
569 	unsigned int sect;
570 
571 	if (!IS_MFORMAT_ONLY(dev)) {
572 	    if(compare(dev->sectors, 19) &&
573 	       compare(dev->sectors, 23) &&
574 	       compare(dev->sectors, 24) &&
575 	       compare(dev->sectors, 46) &&
576 	       compare(dev->sectors, 48))
577 		return 1;
578 
579 	    /* check against contradictory info from configuration file */
580 	    if(compare(dev->heads, 2))
581 		return 1;
582 	}
583 
584 	/* check against info from boot */
585 	if(This) {
586 		sect = This->track_size;
587 		if((sect != 19 && sect != 23 && sect != 24 &&
588 		    sect != 46 && sect != 48) ||
589 		   (!IS_MFORMAT_ONLY(dev) && compare(dev->sectors, sect)))
590 			return 1;
591 	}
592 	return 0;
593 }
594 
set_geom(Xdf_t * This,struct device * dev)595 static void set_geom(Xdf_t *This, struct device *dev)
596 {
597 	/* fill in config info to be returned to user */
598 	dev->heads = 2;
599 	dev->use_2m = 0xff;
600 	dev->sectors = (uint16_t) This->track_size;
601 	dev->tracks = 80;
602 }
603 
config_geom(Stream_t * Stream UNUSEDP,struct device * dev,struct device * orig_dev UNUSEDP)604 static int config_geom(Stream_t *Stream UNUSEDP, struct device *dev,
605 		       struct device *orig_dev UNUSEDP)
606 {
607 	DeclareThis(Xdf_t);
608 	if(check_geom(This, dev))
609 		return 1;
610 	set_geom(This, dev);
611 	return 0;
612 }
613 
614 static Class_t XdfClass = {
615 	0,
616 	0,
617 	xdf_pread,
618 	xdf_pwrite,
619 	xdf_flush,
620 	xdf_free,
621 	config_geom,
622 	0, /* get_data */
623 	0, /* pre-allocate */
624 	0, /* get_dosConvert */
625 	0 /* discard */
626 };
627 
XdfOpen(struct device * dev,const char * name,int mode,char * errmsg,struct xdf_info * info)628 Stream_t *XdfOpen(struct device *dev, const char *name,
629 		  int mode, char *errmsg, struct xdf_info *info)
630 {
631 	Xdf_t *This;
632 	uint32_t begin, end;
633 	union bootsector *boot;
634 	unsigned int type;
635 	uint16_t fatSize;
636 
637 	if(dev && ((!SHOULD_USE_XDF(dev) && !getenv("MTOOLS_USE_XDF")) ||
638 		   check_geom(NULL, dev)))
639 		return NULL;
640 
641 	This = New(Xdf_t);
642 	if (!This)
643 		return NULL;
644 	init_head(&This->head, &XdfClass, NULL);
645 
646 	This->sector_size = 512;
647 	This->stretch = 0;
648 
649 	precmd(dev);
650 	This->fd = open(name,
651 			((mode | dev->mode) & ~O_ACCMODE) |
652 			O_EXCL | O_NDELAY | O_RDWR);
653 	if(This->fd < 0) {
654 #ifdef HAVE_SNPRINTF
655 		snprintf(errmsg,199,"xdf floppy: open: \"%s\"", strerror(errno));
656 #else
657 		sprintf(errmsg,"xdf floppy: open: \"%s\"", strerror(errno));
658 #endif
659 		goto exit_0;
660 	}
661 	closeExec(This->fd);
662 
663 	This->drive = GET_DRIVE(This->fd);
664 	if(This->drive < 0)
665 		goto exit_1;
666 
667 	/* allocate buffer */
668 	This->buffer = (char *) malloc(96 * 512);
669 	if (!This->buffer)
670 		goto exit_1;
671 
672 	This->track_valid = false;
673 	This->track_map = (TrackMap_t *)
674 		calloc(96, sizeof(TrackMap_t));
675 	if(!This->track_map)
676 		goto exit_2;
677 
678 	/* lock the device on writes */
679 	if (lock_dev(This->fd, mode == O_RDWR, dev)) {
680 #ifdef HAVE_SNPRINTF
681 		snprintf(errmsg,199,"xdf floppy: device \"%s\" busy:",
682 			dev->name);
683 #else
684 		sprintf(errmsg,"xdf floppy: device \"%s\" busy:",
685 			dev->name);
686 #endif
687 		goto exit_3;
688 	}
689 
690 	/* Before reading the boot sector, assume dummy values suitable
691 	 * for reading just the boot sector */
692 	fill_boot(This);
693 	This->rate = 0;
694 	if (load_data(This, 0, 1, 4) < 0 ) {
695 		This->rate = 0x43;
696 		if(load_data(This, 0, 1, 4) < 0)
697 			goto exit_3;
698 	}
699 
700 	boot = (union bootsector *) This->buffer;
701 
702 	fatSize = WORD(fatlen);
703 	if(fatSize > UINT8_MAX) {
704 		fprintf(stderr, "Fat size %d too large\n", fatSize);
705 		exit(1);
706 	}
707 	This->FatSize = (uint8_t) fatSize;
708 	This->RootDirSize = WORD(dirents)/16;
709 	This->track_size = WORD(nsect);
710 	for(type=0; type < NUMBER(xdf_table); type++) {
711 		if(xdf_table[type].track_size == This->track_size) {
712 			This->map = xdf_table[type].map;
713 			This->track0_size = xdf_table[type].track0_size;
714 			This->rootskip = xdf_table[type].rootskip;
715 			This->rate = xdf_table[type].rate;
716 			break;
717 		}
718 	}
719 	if(type == NUMBER(xdf_table))
720 		goto exit_3;
721 
722 	if(info) {
723 		info->RootDirSize = This->RootDirSize;
724 		info->FatSize = This->FatSize;
725 		info->BadSectors = 5;
726 	}
727 	decompose(This, 0, 512, &begin, &end, 1);
728 
729 	if(dev)
730 		set_geom(This, dev);
731 	return &This->head;
732 
733 exit_3:
734 	Free(This->track_map);
735 exit_2:
736 	Free(This->buffer);
737 exit_1:
738 	close(This->fd);
739 exit_0:
740 	Free(This);
741 	return NULL;
742 }
743 
744 #endif
745 
746 /* Algorithms can't be patented */
747