Added pfsdoctor. Includes PFS3 v19 large partition and file size support.
[AROS.git] / rom / filesys / pfs3 / pfsdoctor / device.c
blob6a08458d374dede951685a29d41f9de6c075a808
1 /* $Id$
2 * $Log: device.c $
3 * Revision 2.5 1999/09/10 22:14:49 Michiel
4 * Bugfixes etc (1.4)
6 * Revision 2.4 1999/05/07 16:49:00 Michiel
7 * bugfixes etc
9 * Revision 2.3 1999/05/04 17:59:09 Michiel
10 * check mode, logfile, search rootblock implemented
11 * bugfixes
13 * Revision 2.2 1999/05/04 04:27:13 Michiel
14 * debugged upto buildrext
16 * Revision 2.1 1999/04/30 12:17:58 Michiel
17 * Accepts OK disks, bitmapfix and hardlink fix works
19 * Revision 1.2 1999/04/22 15:26:05 Michiel
20 * compiled
22 * Revision 1.1 1999/04/19 22:16:53 Michiel
23 * Initial revision
25 * Device Access functies, inclusief cache etc.
28 #define __USE_SYSBASE
29 #include <string.h>
30 #include <math.h>
32 #include <exec/memory.h>
33 #include <exec/errors.h>
34 #include <devices/trackdisk.h>
35 #include <devices/scsidisk.h>
36 #include <dos/filehandler.h>
37 #include <proto/exec.h>
38 #include <clib/alib_protos.h>
39 #include <stdlib.h>
40 #include "pfs3.h"
41 #include "doctor.h"
43 #define BLOCKSHIFT (volume.blockshift)
45 /**************************************
46 * Proto
47 **************************************/
49 static struct cacheline *c_GetCacheLine(uint32 bloknr);
51 /**************************************
52 * Globals
53 **************************************/
55 struct cache cache;
57 /**************************************
58 * Cache
60 * Cache module. Implemented as proxy on device. This module has a problem
61 * at the end of the disk: it does not take partial cachelines into
62 * account. This is no real problem since it is only used for the reserved
63 * area which is located at the start of the partition.
64 **************************************/
66 /* create cache
67 * cacheline size: 4-8-16
68 * cachelines: 8-16-32-64-128
69 * size = linesize * nolines * blocksize
71 error_t InitCache(uint32 linesize, uint32 nolines)
73 int i;
75 /* allocate memory for the cache */
76 if (!(cache.cachelines = calloc(nolines, sizeof(struct cacheline))))
77 return e_out_of_memory;
79 cache.linesize = linesize;
80 cache.nolines = nolines;
81 NewList((struct List *)&cache.LRUqueue);
82 NewList((struct List *)&cache.LRUpool);
83 for (i=0; i<nolines; i++)
85 cache.cachelines[i].blocknr = CL_UNUSED; /* a bloknr that is never used */
86 cache.cachelines[i].data = AllocBufMem(linesize*volume.blocksize);
87 if (cache.cachelines[i].data)
89 MinAddHead(&cache.LRUpool, &cache.cachelines[i]);
92 return e_none;
95 /* get block (of size 'bytes') from cache. Cache is per >device<.
97 error_t c_GetBlock(uint8 *data, uint32 bloknr, uint32 bytes)
99 uint32 lineblnr, offset, total;
100 struct cacheline *cl = NULL;
101 error_t error = e_none;
103 __chkabort();
104 for (total=0; total<bytes; total+=volume.blocksize, bloknr++, data+=volume.blocksize)
106 lineblnr = (bloknr / cache.linesize) * cache.linesize;
107 offset = (bloknr % cache.linesize);
108 cl = c_GetCacheLine(lineblnr);
109 if (!cl)
110 return e_read_error;
112 memcpy(data, cl->data + offset*volume.blocksize, volume.blocksize);
114 return error;
117 /* write block (of size 'bytes') to cache
119 error_t c_WriteBlock(uint8 *data, uint32 bloknr, uint32 bytes)
121 uint32 lineblnr, offset, total;
122 struct cacheline *cl = NULL;
123 error_t error = e_none;
125 __chkabort();
126 for (total=0; total<bytes; total+=volume.blocksize, bloknr++, data+=volume.blocksize)
128 lineblnr = (bloknr / cache.linesize) * cache.linesize;
129 offset = (bloknr % cache.linesize);
130 cl = c_GetCacheLine(lineblnr);
131 if (!cl)
132 return e_read_error;
134 memcpy(cl->data + offset*volume.blocksize, data, volume.blocksize);
135 cl->dirty = true;
137 return error;
140 /* (private) locale function to search a cacheline
142 static struct cacheline *c_GetCacheLine(uint32 bloknr)
144 uint32 i;
145 struct cacheline *cl = NULL;
146 error_t error;
148 for (i=0; i<cache.nolines; i++)
150 if (cache.cachelines[i].blocknr == bloknr)
152 cl = &cache.cachelines[i];
153 break;
157 if (!cl)
159 if (IsMinListEmpty(&cache.LRUpool))
161 cl = (struct cacheline *)cache.LRUqueue.mlh_TailPred;
162 if (cl->dirty)
163 error = volume.writerawblocks(cl->data, cache.linesize, cl->blocknr);
165 else
167 cl = HeadOf(&cache.LRUpool);
170 error = volume.getrawblocks(cl->data, cache.linesize, bloknr);
171 if (error)
172 return NULL;
173 cl->blocknr = bloknr;
174 cl->dirty = false;
177 MinRemove(cl);
178 MinAddHead(&cache.LRUqueue, cl);
179 return cl;
182 /* update alle dirty blocks in the cache
184 void UpdateCache(void)
186 struct cacheline *cl;
187 error_t error;
189 for (cl = HeadOf(&cache.LRUqueue); cl->next; cl=cl->next)
191 if (cl->dirty)
193 error = volume.writerawblocks(cl->data, cache.linesize, cl->blocknr);
194 if (error)
195 adderror("write error");
200 /* destroy cache
202 void FreeCache(void)
204 int i;
206 if (cache.cachelines)
208 UpdateCache();
209 for (i = 0; i < cache.nolines; i++)
210 FreeBufMem(cache.cachelines[i].data);
212 free(cache.cachelines);
213 cache.cachelines = NULL;
218 /**************************************
219 * Device functions
220 **************************************/
222 /* OpenDiskDevice
224 * Open the diskdevice passed in 'startup' and generate the
225 * messageport and request structure, returned in 'port'
226 * and 'request' resp.
228 * in startup
229 * out port, request, trackdisk
231 BOOL OpenDiskDevice(struct FileSysStartupMsg *startup, struct MsgPort **port,
232 struct IOExtTD **request, BOOL *trackdisk)
234 UBYTE name[FNSIZE];
236 *port = CreateMsgPort();
237 if(*port)
239 *request = (struct IOExtTD *)CreateIORequest(*port, sizeof(struct IOExtTD));
240 if(*request)
242 BCPLtoCString(name, (UBYTE *)BADDR(startup->fssm_Device));
243 *trackdisk = (strcmp(name, "trackdisk.device") == 0) || (strcmp(name, "diskspare.device") == 0);
244 if(OpenDevice(name, startup->fssm_Unit, (struct IORequest *)*request,
245 startup->fssm_Flags) == 0)
246 return TRUE;
249 return FALSE;
253 * Get blocks from device. Blocknrs are disk (not partition!) based
255 error_t dev_GetBlocks(uint8 *buffer, int32 blocks, uint32 blocknr)
257 struct IOExtTD *request;
258 uint32 io_length, io_transfer, io_offset, io_actual = 0, io_startblock = 0;
259 uint8 *io_buffer;
260 int retry = 4;
262 retry_read:
263 if(blocknr == -1) // blocknr of uninitialised anode
264 return 1;
266 io_length = blocks << volume.blockshift;
267 io_offset = blocknr << volume.blockshift;
268 io_buffer = buffer;
269 if (volume.td64mode || volume.nsdmode) {
270 // upper 32 bit of offset
271 io_actual = blocknr >> (32-volume.blockshift);
272 io_startblock = blocknr;
275 while (io_length > 0)
277 io_transfer = min(io_length, volume.dosenvec->de_MaxTransfer);
278 io_transfer &= ~(volume.blocksize-1);
279 request = volume.request;
280 request->iotd_Req.io_Command = CMD_READ;
281 request->iotd_Req.io_Length = io_transfer;
282 request->iotd_Req.io_Data = io_buffer; // bufmemtype ??
283 request->iotd_Req.io_Offset = io_offset;
284 if (volume.td64mode) {
285 request->iotd_Req.io_Actual = io_actual;
286 request->iotd_Req.io_Command = TD_READ64;
287 } else if (volume.nsdmode) {
288 request->iotd_Req.io_Actual = io_actual;
289 request->iotd_Req.io_Command = NSCMD_TD_READ64;
292 if (DoIO((struct IORequest*)request))
294 volume.showmsg("READ ERROR\n");
295 if (!retry--)
296 return e_read_error;
297 goto retry_read;
299 io_buffer += io_transfer;
300 io_length -= io_transfer;
301 if (volume.td64mode || volume.nsdmode) {
302 io_startblock += (io_transfer >> volume.blockshift);
303 io_offset = io_startblock << volume.blockshift;
304 io_actual = io_startblock >> (32-volume.blockshift);
305 } else {
306 io_offset += io_transfer;
310 return e_none;
314 error_t dev_GetBlocksDS(uint8 *buffer, int32 blocks, uint32 blocknr)
316 uint8 cmdbuf[10];
317 uint32 transfer, maxtransfer;
318 int retry = 4;
320 retry_read:
321 if(blocknr == -1) // blocknr of uninitialised anode
322 return 1;
324 /* chop in maxtransfer chunks */
325 maxtransfer = volume.dosenvec->de_MaxTransfer >> volume.blockshift;
326 while (blocks > 0)
328 transfer = min(blocks,maxtransfer);
329 *((UWORD *)&cmdbuf[0]) = 0x2800;
330 *((ULONG *)&cmdbuf[2]) = blocknr;
331 *((ULONG *)&cmdbuf[6]) = transfer<<8;
332 if (!DoSCSICommand(buffer,transfer<<BLOCKSHIFT,cmdbuf,10,SCSIF_READ))
334 volume.showmsg("READ ERROR\n");
335 if (!retry--)
336 return e_read_error;
338 goto retry_read;
340 buffer += transfer<<BLOCKSHIFT;
341 blocks -= transfer;
342 blocknr += transfer;
345 return e_none;
348 error_t dev_WriteBlocksDummy(uint8 *buffer, int32 blocks, uint32 blocknr)
350 return e_none;
353 error_t dev_WriteBlocksDS(uint8 *buffer, int32 blocks, uint32 blocknr)
355 UBYTE cmdbuf[10];
356 ULONG transfer, maxtransfer;
357 int retry = 4;
359 retry_write:
360 if(blocknr == -1)
361 return 1;
363 /* chop in maxtransfer chunks */
364 maxtransfer = volume.dosenvec->de_MaxTransfer >> volume.blockshift;
365 while (blocks > 0)
367 transfer = min(blocks,maxtransfer);
368 *((UWORD *)&cmdbuf[0]) = 0x2a00;
369 *((ULONG *)&cmdbuf[2]) = blocknr;
370 *((ULONG *)&cmdbuf[6]) = transfer<<8;
371 if (!DoSCSICommand(buffer,blocks<<BLOCKSHIFT,cmdbuf,10,SCSIF_WRITE))
373 volume.showmsg("WRITE ERROR\n");
374 if (!retry--)
375 return e_write_error;
377 goto retry_write;
379 buffer += transfer<<BLOCKSHIFT;
380 blocks -= transfer;
381 blocknr += transfer;
384 return e_none;
387 error_t dev_WriteBlocks(uint8 *buffer, int32 blocks, uint32 blocknr)
389 struct IOExtTD *request;
390 uint32 io_length, io_transfer, io_offset, io_actual = 0, io_startblock = 0;
391 uint8 *io_buffer;
392 int retry = 4;
394 retry_write:
395 if(blocknr == -1) // blocknr of uninitialised anode
396 return e_write_error;
398 io_length = blocks << volume.blockshift;
399 io_offset = blocknr << volume.blockshift;
400 io_buffer = buffer;
401 if (volume.td64mode || volume.nsdmode) {
402 // upper 32 bit of offset
403 io_actual = blocknr >> (32 - BLOCKSHIFT);
404 io_startblock = blocknr;
407 while(io_length > 0)
409 io_transfer = min(io_length, volume.dosenvec->de_MaxTransfer);
410 io_transfer &= ~(volume.blocksize-1);
411 request = volume.request;
412 request->iotd_Req.io_Command = CMD_WRITE;
413 request->iotd_Req.io_Length = io_transfer;
414 request->iotd_Req.io_Data = io_buffer; // bufmemtype ??
415 request->iotd_Req.io_Offset = io_offset;
416 if (volume.td64mode) {
417 request->iotd_Req.io_Actual = io_actual;
418 request->iotd_Req.io_Command = TD_WRITE64;
419 } else if (volume.nsdmode) {
420 request->iotd_Req.io_Actual = io_actual;
421 request->iotd_Req.io_Command = NSCMD_TD_WRITE64;
424 if (DoIO((struct IORequest*)request))
426 volume.showmsg("WRITE ERROR\n");
427 if (!retry--)
428 return e_write_error;
429 goto retry_write;
431 io_buffer += io_transfer;
432 io_length -= io_transfer;
433 if (volume.td64mode || volume.nsdmode) {
434 io_startblock += (io_transfer >> volume.blockshift);
435 io_offset = io_startblock << volume.blockshift;
436 io_actual = io_startblock >> (32-volume.blockshift);
437 } else {
438 io_offset += io_transfer;
442 return e_none;
446 * Allocation of memory for buffers
449 void *AllocBufMem(uint32 size)
451 void *buffer;
453 buffer = AllocVec(size, volume.dosenvec->de_BufMemType|MEMF_CLEAR);
455 if (((uint32)buffer) & ~volume.dosenvec->de_Mask)
457 volume.showmsg ("Memory mask fails - aborting\n");
458 FreeVec(buffer);
459 return NULL;
462 return buffer;
465 void FreeBufMem (void *mem)
467 FreeVec (mem);
471 * Do direct scsi command
474 static uint8 sense[20];
475 static struct SCSICmd scsicmd;
477 int DoSCSICommand(UBYTE *data, ULONG datalen, UBYTE *command,
478 UWORD commandlen, UBYTE direction)
480 scsicmd.scsi_Data = (UWORD *)data;
481 scsicmd.scsi_Length = datalen;
482 scsicmd.scsi_Command = command;
483 scsicmd.scsi_CmdLength = commandlen;
484 scsicmd.scsi_Flags = SCSIF_AUTOSENSE | direction; /* SCSIF_READ or SCSIF_WRITE */
485 scsicmd.scsi_SenseData = sense;
486 scsicmd.scsi_SenseLength = 18;
487 scsicmd.scsi_SenseActual = 0;
489 volume.request->iotd_Req.io_Length = sizeof(struct SCSICmd);
490 volume.request->iotd_Req.io_Data = (APTR)&scsicmd;
491 volume.request->iotd_Req.io_Command = HD_SCSICMD;
492 DoIO((struct IORequest *)volume.request);
493 if (scsicmd.scsi_Status)
494 return 0;
495 else
496 return 1;
499 /* Convert BCPL/Pascal string to a CString.
500 * dest: resulting c string
501 * src: source pascal string (no BPTR!)
503 UBYTE *BCPLtoCString(STRPTR dest, UBYTE *src)
505 UBYTE len, *to;
507 len = *(src++);
508 len = min (len, PATHSIZE);
509 to = dest;
511 while (len--)
512 *(dest++) = *(src++);
513 *dest = 0x0;
515 return to;
518 /* ACCESS MODE AUTODETECT */
520 #define BLOCKSHIFT (volume.blockshift)
521 #define BLOCKSIZE (volume.blocksize)
523 static void fillbuffer(UBYTE *buffer, UBYTE data)
525 memset (buffer, data + 1, BLOCKSIZE);
527 /* Check if at least one byte has changed */
528 static BOOL testbuffer(UBYTE *buffer, UBYTE data)
530 ULONG cnt;
532 for (cnt = 0; cnt < BLOCKSIZE; cnt++) {
533 if (buffer[cnt] != data + 1)
534 return TRUE;
536 return FALSE;
539 static BOOL testread_ds(UBYTE *buffer)
541 UBYTE cmdbuf[10];
542 UBYTE cnt;
544 for (cnt = 0; cnt < 2; cnt++) {
545 ULONG capacity;
547 fillbuffer(buffer, 0xfe);
548 /* Read Capacity */
549 *((UWORD *)&cmdbuf[0]) = 0x2500;
550 *((ULONG *)&cmdbuf[2]) = 0;
551 *((ULONG *)&cmdbuf[6]) = 0;
552 if (!DoSCSICommand(buffer, 8, cmdbuf, 10, SCSIF_READ)) {
553 return FALSE;
555 capacity = *((ULONG*)buffer);
557 if (volume.lastblock > capacity) {
558 return FALSE;
560 fillbuffer(buffer, cnt);
561 /* Read(10) */
562 *((UWORD *)&cmdbuf[0]) = 0x2800;
563 *((ULONG *)&cmdbuf[2]) = volume.lastblock;
564 *((ULONG *)&cmdbuf[6]) = 1 << 8;
565 if (!DoSCSICommand(buffer, 1 << BLOCKSHIFT, cmdbuf, 10, SCSIF_READ)) {
566 return FALSE;
568 if (testbuffer(buffer, cnt)) {
569 return TRUE;
572 return FALSE;
575 static BOOL testread_td(UBYTE *buffer)
577 struct IOExtTD *io = volume.request;
578 UBYTE cnt;
580 if (volume.accessmode == ACCESS_NSD) {
581 struct NSDeviceQueryResult nsdqr;
582 UWORD *cmdcheck;
583 nsdqr.SizeAvailable = 0;
584 nsdqr.DevQueryFormat = 0;
585 io->iotd_Req.io_Command = NSCMD_DEVICEQUERY;
586 io->iotd_Req.io_Length = sizeof(nsdqr);
587 io->iotd_Req.io_Data = (APTR)&nsdqr;
588 if (DoIO((struct IORequest*)io) != 0)
589 return FALSE;
590 if (!((io->iotd_Req.io_Actual >= 16) && (io->iotd_Req.io_Actual <= sizeof(nsdqr)) && (nsdqr.SizeAvailable == io->iotd_Req.io_Actual)))
591 return FALSE;
592 if(nsdqr.DeviceType != NSDEVTYPE_TRACKDISK)
593 return FALSE;
594 for(cmdcheck = nsdqr.SupportedCommands; *cmdcheck; cmdcheck++) {
595 if(*cmdcheck == NSCMD_TD_READ64)
596 break;
598 if (*cmdcheck == 0)
599 return FALSE;
601 if (volume.accessmode == ACCESS_TD64) {
602 UBYTE err;
603 io->iotd_Req.io_Command = TD_READ64;
604 io->iotd_Req.io_Length = 0;
605 io->iotd_Req.io_Data = 0;
606 io->iotd_Req.io_Offset = 0;
607 io->iotd_Req.io_Actual = 0;
608 err = DoIO((struct IORequest*)io);
609 if (err != 0 && err != IOERR_BADLENGTH && err != IOERR_BADADDRESS)
610 return FALSE;
612 for (cnt = 0; cnt < 2; cnt++) {
613 fillbuffer(buffer, cnt);
614 io->iotd_Req.io_Command = CMD_READ;
615 io->iotd_Req.io_Length = BLOCKSIZE;
616 io->iotd_Req.io_Data = buffer;
617 io->iotd_Req.io_Offset = volume.lastblock << BLOCKSHIFT;
618 if (volume.accessmode >= ACCESS_TD64) {
619 io->iotd_Req.io_Actual = volume.lastblock >> (32 - BLOCKSHIFT);
620 io->iotd_Req.io_Command = volume.accessmode == ACCESS_NSD ? NSCMD_TD_READ64 : TD_READ64;
622 if (DoIO((struct IORequest*)io) != 0)
623 return FALSE;
624 if (testbuffer(buffer, cnt))
625 return TRUE;
627 return TRUE;
630 BOOL DetectAccessmode(UBYTE *buffer, BOOL scsidirectfirst)
632 BOOL inside4G = volume.lastblock < (0x80000000ul >> (BLOCKSHIFT - 1));
634 if (scsidirectfirst) {
635 volume.accessmode = ACCESS_DS;
636 if (testread_ds(buffer))
637 return TRUE;
640 if (volume.lastblock < 262144) {
641 /* Don't bother with tests if small enough (<128M) */
642 volume.accessmode = ACCESS_STD;
643 return TRUE;
646 if (inside4G) {
647 /* inside first 4G? Try standard CMD_READ first. */
648 volume.accessmode = ACCESS_STD;
649 if (testread_td(buffer))
650 return TRUE;
651 volume.accessmode = ACCESS_DS;
652 /* It failed, we may have 1G limit A590 pre-7.0 ROM or CDTV SCSI, try DS */
653 if (testread_ds(buffer))
654 return TRUE;
655 volume.accessmode = ACCESS_STD;
656 return FALSE;
658 /* outside of first 4G, must use TD64, NSD or DS */
659 volume.accessmode = ACCESS_NSD;
660 if (testread_td(buffer))
661 return TRUE;
662 volume.accessmode = ACCESS_TD64;
663 if (testread_td(buffer))
664 return TRUE;
665 volume.accessmode = ACCESS_DS;
666 if (testread_ds(buffer))
667 return TRUE;
669 volume.accessmode = ACCESS_STD;
670 return FALSE;