Added a test for MUIA_Listview_SelectChange.
[AROS.git] / rom / filesys / pfs3 / fs / disk.c
blob19ef46ab7a06e2262a6cd09b30e2746e024d7f27
1 /* $Id$ */
2 /* $Log: disk.c $
3 * Revision 15.12 1999/03/25 22:05:00 Michiel
4 * fixed deldir related (beta) bug
6 * Revision 15.11 1999/02/22 16:25:30 Michiel
7 * Changes for increasing deldir capacity
9 * Revision 15.10 1998/09/27 11:26:37 Michiel
10 * ErrorMsg param
12 * Revision 15.9 1998/09/03 07:12:14 Michiel
13 * versie 17.4
14 * bugfixes 118, 121, 123 and superindexblocks and td64 support
16 * Revision 15.8 1998/04/23 22:27:07 Michiel
17 * FakeCachedRead toegevoegd om munglist check hits te voorkomen
18 * Bug in WriteToFile opgelost: toevoeging CorrectAnodeAc
20 * Revision 15.7 1997/03/03 22:04:04 Michiel
21 * Release 16.21
23 * Revision 15.6 1996/03/14 19:32:56 Michiel
24 * Fixed ChangeFileSize bug: had no effect when # blocks remained constant
26 * Revision 15.5 1996/01/03 09:58:36 Michiel
27 * replaced CopyMem() by memcpy()
29 * Revision 15.4 1995/12/21 11:59:31 Michiel
30 * bugfixes: ValidateCache() block flushing and
31 * WriteToFile expensive last block
33 * Revision 15.3 1995/12/21 11:32:44 Michiel
34 * code cleanup
36 * Revision 15.2 1995/12/14 13:24:08 Michiel
37 * Direct SCSI support
39 * Revision 15.1 1995/12/05 15:47:27 Michiel
40 * Rollover files implemented
41 * Restructured: ReadFromObject, WriteToObject etc
42 * bugfixes
44 * Revision 14.4 1995/11/15 15:44:40 Michiel
45 * WriteToFile, ChangeFileSize adapted to postponed op.
47 * Revision 14.3 1995/11/07 14:57:31 Michiel
48 * support for online directory update in WriteToFile and ChangeFileSize
49 * ReservedAreaLock check
51 * Revision 14.2 1995/10/11 23:25:24 Michiel
52 * UpdateSlot added: improved sequential write
54 * Revision 14.1 1995/10/11 22:18:30 Michiel
55 * new data-caching algorithm
57 * Revision 13.2 1995/10/05 11:01:32 Michiel
58 * minor changes
60 * Revision 13.1 1995/10/03 11:08:55 Michiel
61 * merged with developtree: anodecache
63 * Revision 12.15 1995/09/04 09:57:10 Michiel
64 * mask check now starts at first whole block and includes last block
66 * Revision 12.14 1995/09/01 11:20:15 Michiel
67 * RawRead and RawWrite error handling changed:
68 * on error a retry|cancel requester appears. Retry and
69 * same volumecheck changed. Numsofterrors update added.
71 * Revision 12.13 1995/08/24 13:48:26 Michiel
72 * TD_DiskChange checking enabled
74 * Revision 12.12 1995/08/21 04:25:48 Michiel
75 * better checks for out of memory
77 * Revision 12.11 1995/08/04 04:13:15 Michiel
78 * extra CUTDOWN protection
80 * Revision 12.10 1995/07/21 06:48:51 Michiel
81 * DELDIR adaptions
82 * bugfix: MaxTransfer now doesn't have to be multiple of blocksize
84 * Revision 12.9 1995/07/11 17:29:31 Michiel
85 * ErrorMsg () calls use messages.c variables now.
87 * Revision 12.8 1995/07/07 14:39:17 Michiel
88 * AFSLITE stuff
90 * Revision 12.7 1995/06/19 09:42:45 Michiel
91 * Rawwrite returns error if softprotect is on
93 * Revision 12.6 1995/06/16 10:00:15 Michiel
94 * using Allec & FreeBufMem
96 * Revision 12.5 1995/06/16 04:06:29 Michiel
97 * No Touch () in write, just MakeBlockDirty
99 * Revision 12.4 1995/05/20 12:12:12 Michiel
100 * Updated messages to reflect Ami-FileLock
101 * CUTDOWN version
102 * protection update
104 * Revision 12.3 1995/03/30 11:54:29 Michiel
105 * Write & Setfilesize now set the checknotify flag
107 * Revision 12.2 1995/02/15 16:43:39 Michiel
108 * Release version
109 * Using new headers (struct.h & blocks.h)
111 * Revision 12.1 1995/02/02 11:26:11 Michiel
112 * Version number fix. No changes.
114 * Revision 11.8 1995/01/29 07:34:57 Michiel
115 * Major update
116 * ReadFromFile, WriteToFile completely rewritten
117 * CachedRead added
118 * DataCaching updated
120 * Revision 11.7 1995/01/26 12:20:42 Michiel
121 * a minor change
123 * Revision 11.6 1995/01/24 17:44:34 Michiel
124 * bug fixes
126 * Revision 11.5 1995/01/24 15:54:20 Michiel
127 * DirectRead, CachedRead etc
129 * Revision 11.4 1995/01/23 16:43:35 Michiel
130 * Directwrite in WriteToFile added
132 * Revision 11.3 1995/01/18 04:29:34 Michiel
133 * Bugfixes. Now ready for beta release.
135 * Revision 11.2 1995/01/15 05:24:44 Michiel
136 * trackdisk specific parts inhibited
138 * Revision 11.1 1995/01/08 16:17:32 Michiel
139 * Compiled (new MODE_BIG version)
141 * Revision 10.4 1994/11/15 18:06:58 Michiel
142 * __USE_SYSBASE moved..
144 * Revision 10.3 1994/10/28 06:06:40 Michiel
145 * uses new listentry field anodenr
147 * Revision 10.2 1994/10/27 11:30:12 Michiel
148 * *** empty log message ***
150 * Revision 10.1 1994/10/24 11:16:28 Michiel
151 * first RCS revision
152 * */
154 #define TESTING 1
156 //#define DEBUG 1
157 #define __USE_SYSBASE
159 #include <exec/types.h>
160 #include <exec/memory.h>
161 #include <exec/devices.h>
162 #include <exec/io.h>
163 #include <exec/errors.h>
164 #include <dos/filehandler.h>
165 //#include <sprof.h>
166 #include <string.h>
167 #include <stdio.h>
168 #include <math.h>
169 #include "debug.h"
171 // own includes
172 #include "blocks.h"
173 #include "struct.h"
174 #include "disk_protos.h"
175 #include "allocation_protos.h"
176 #include "volume_protos.h"
177 #include "directory_protos.h"
178 #include "anodes_protos.h"
179 #include "update_protos.h"
180 #include "checkaccess_protos.h"
182 #define PROFILE_OFF()
183 #define PROFILE_ON()
185 /**********************************************************************/
186 /* DEBUG */
187 /**********************************************************************/
189 #ifdef DEBUG
190 static UBYTE debugbuf[120];
191 extern BOOL debug;
192 #define DebugOn debug++
193 #define DebugOff debug=0
194 #define DebugMsg(msg) if(debug) {NormalErrorMsg(msg, NULL);debug=0;}
195 #define DebugMsgNum(msg, num) sprintf(debugbuf, "%s 0x%08lx.", msg, num); \
196 if(debug) {NormalErrorMsg(debugbuf, NULL);debug=0;}
197 #define DebugMsgName(msg, name) sprintf(debugbuf, "%s >%s<.", msg, name); \
198 if(debug) {NormalErrorMsg(debugbuf, NULL);debug=0;}
199 #else
200 #define DebugOn
201 #define DebugOff
202 #define DebugMsg(m)
203 #define DebugMsgNum(msg,num)
204 #define DebugMsgName(msg, name)
205 #endif
207 enum vctype {read, write};
208 static int CheckDataCache(ULONG blocknr, globaldata *g);
209 static int CachedRead(ULONG blocknr, SIPTR *error, globaldata *g);
210 static int FakeCachedRead(ULONG blocknr, SIPTR *error, globaldata *g);
211 static UBYTE *CachedReadD(ULONG blknr, SIPTR *err, globaldata *g);
212 static int CachedWrite(UBYTE *data, ULONG blocknr, globaldata *g);
213 static void ValidateCache(ULONG blocknr, ULONG numblocks, enum vctype, globaldata *g);
214 static void UpdateSlot(int slotnr, globaldata *g);
215 static ULONG ReadFromRollover(fileentry_t *file, UBYTE *buffer, ULONG size, SIPTR *error, globaldata *g);
216 static ULONG WriteToRollover(fileentry_t *file, UBYTE *buffer, ULONG size, SIPTR *error, globaldata *g);
217 static SFSIZE SeekInRollover(fileentry_t *file, SFSIZE offset, LONG mode, SIPTR *error, globaldata *g);
218 static SFSIZE ChangeRolloverSize(fileentry_t *file, SFSIZE releof, LONG mode, SIPTR *error, globaldata *g);
219 static ULONG ReadFromFile(fileentry_t *file, UBYTE *buffer, ULONG size, SIPTR *error, globaldata *g);
220 static ULONG WriteToFile(fileentry_t *file, UBYTE *buffer, ULONG size, SIPTR *error, globaldata *g);
222 /**********************************************************************/
223 /* READ & WRITE */
224 /* READ & WRITE */
225 /* READ & WRITE */
226 /**********************************************************************/
228 ULONG ReadFromObject(fileentry_t *file, UBYTE *buffer, ULONG size,
229 SIPTR *error, globaldata *g)
231 if (!CheckReadAccess(file,error,g))
232 return -1;
234 /* check anodechain, make if not there */
235 if (!file->anodechain)
237 DB(Trace(2,"ReadFromObject","getting anodechain"));
238 if (!(file->anodechain = GetAnodeChain(file->le.anodenr, g)))
240 *error = ERROR_NO_FREE_STORE;
241 return -1;
245 #if ROLLOVER
246 if (IsRollover(file->le.info))
247 return ReadFromRollover(file,buffer,size,error,g);
248 else
249 #endif
250 return ReadFromFile(file,buffer,size,error,g);
253 ULONG WriteToObject(fileentry_t *file, UBYTE *buffer, ULONG size,
254 SIPTR *error, globaldata *g)
256 /* check write access */
257 if (!CheckWriteAccess(file, error, g))
258 return -1;
260 /* check anodechain, make if not there */
261 if (!file->anodechain)
263 if (!(file->anodechain = GetAnodeChain(file->le.anodenr, g)))
265 *error = ERROR_NO_FREE_STORE;
266 return -1;
270 /* changing file -> set notify flag */
271 file->checknotify = 1;
272 g->dirty = 1;
274 #if ROLLOVER
275 if (IsRollover(file->le.info))
276 return WriteToRollover(file,buffer,size,error,g);
277 else
278 #endif
279 return WriteToFile(file,buffer,size,error,g);
282 SFSIZE SeekInObject(fileentry_t *file, SFSIZE offset, LONG mode, SIPTR *error,
283 globaldata *g)
285 /* check access */
286 if (!CheckOperateFile(file,error,g))
287 return -1;
289 /* check anodechain, make if not there */
290 if (!file->anodechain)
292 if (!(file->anodechain = GetAnodeChain(file->le.anodenr, g)))
294 *error = ERROR_NO_FREE_STORE;
295 return -1;
299 #if ROLLOVER
300 if (IsRollover(file->le.info))
301 return SeekInRollover(file,offset,mode,error,g);
302 else
303 #endif
304 return SeekInFile(file,offset,mode,error,g);
307 SFSIZE ChangeObjectSize(fileentry_t *file, SFSIZE releof, LONG mode,
308 SIPTR *error, globaldata *g)
310 /* check access */
311 if (!CheckChangeAccess(file, error, g))
312 return -1;
314 /* Changing file -> set notify flag */
315 file->checknotify = 1;
316 *error = 0;
318 /* check anodechain, make if not there */
319 if (!file->anodechain)
321 if (!(file->anodechain = GetAnodeChain(file->le.anodenr, g)))
323 *error = ERROR_NO_FREE_STORE;
324 return -1;
328 #if ROLLOVER
329 if (IsRollover(file->le.info))
330 return ChangeRolloverSize(file,releof,mode,error,g);
331 else
332 #endif
333 return ChangeFileSize(file,releof,mode,error,g);
338 /**********************************************************************
340 **********************************************************************/
342 #if ROLLOVER
344 /* Read from rollover: at end of file,
345 * goto start
347 static ULONG ReadFromRollover(fileentry_t *file, UBYTE *buffer, ULONG size,
348 SIPTR *error, globaldata *g)
350 #define direntry_m file->le.info.file.direntry
351 #define filesize_m GetDEFileSize(file->le.info.file.direntry, g)
353 struct extrafields extrafields;
354 ULONG read = 0;
355 LONG q; // quantity
356 LONG end, virtualoffset, virtualend, t;
358 DB(Trace(1,"ReadFromRollover","size = %lx offset = %lx\n",size,file->offset));
359 if (!size) return 0;
360 GetExtraFields(direntry_m,&extrafields);
362 /* limit access to end of file */
363 virtualoffset = file->offset - extrafields.rollpointer;
364 if (virtualoffset < 0) virtualoffset += filesize_m;
365 virtualend = virtualoffset + size;
366 virtualend = min(virtualend, extrafields.virtualsize);
367 end = virtualend - virtualoffset + file->offset;
369 if (end > filesize_m)
371 q = filesize_m - file->offset;
372 if ((read = ReadFromFile(file, buffer, q, error, g)) != q)
373 return read;
375 end -= filesize_m;
376 buffer += q;
377 SeekInFile(file, 0, OFFSET_BEGINNING, error, g);
380 q = end - file->offset;
381 t = ReadFromFile(file, buffer, q, error, g);
382 if (t == -1)
383 return (ULONG)t;
384 else
385 read += t;
387 return read;
389 #undef filesize_m
390 #undef direntry_m
393 /* Write to rollover file. First write upto end of rollover. Then
394 * flip to start.
395 * Max virtualsize = filesize-1
397 static ULONG WriteToRollover(fileentry_t *file, UBYTE *buffer, ULONG size,
398 SIPTR *error, globaldata *g)
400 #define direntry_m file->le.info.file.direntry
401 #define filesize_m GetDEFileSize(file->le.info.file.direntry, g)
403 struct extrafields extrafields;
404 struct direntry *destentry;
405 union objectinfo directory;
406 struct fileinfo fi;
407 UBYTE entrybuffer[MAX_ENTRYSIZE];
408 LONG written = 0;
409 LONG q; // quantity
410 LONG end, virtualend, virtualoffset, t;
411 BOOL extend = FALSE;
413 DB(Trace(1,"WriteToRollover","size = %lx offset=%lx, file=%lx\n",size,file->offset,file));
414 GetExtraFields(direntry_m,&extrafields);
415 end = file->offset + size;
417 /* new virtual size */
418 virtualoffset = file->offset - extrafields.rollpointer;
419 if (virtualoffset < 0) virtualoffset += filesize_m;
420 virtualend = virtualoffset + size;
421 if (virtualend >= extrafields.virtualsize)
423 extrafields.virtualsize = min(filesize_m-1, virtualend);
424 extend = TRUE;
427 while (end > filesize_m)
429 q = filesize_m - file->offset;
430 t = WriteToFile(file, buffer, q, error, g);
431 if (t == -1) return (ULONG)t;
432 written += t;
433 if (t != q) return (ULONG)written;
434 end -= filesize_m;
435 buffer += q;
436 SeekInFile(file, 0, OFFSET_BEGINNING, error, g);
439 q = end - file->offset;
440 t = WriteToFile(file, buffer, q, error, g);
441 if (t == -1)
442 return (ULONG)t;
443 else
444 written += t;
446 /* change rollpointer etc */
447 if (extend && extrafields.virtualsize == filesize_m - 1)
448 extrafields.rollpointer = end + 1; /* byte PAST eof is offset 0 */
449 destentry = (struct direntry *)entrybuffer;
450 memcpy(destentry, direntry_m, direntry_m->next);
451 AddExtraFields(destentry, &extrafields);
453 /* commit changes */
454 if (!GetParent(&file->le.info, &directory, error, g))
455 return DOSFALSE;
456 else
457 ChangeDirEntry(file->le.info.file, destentry, &directory, &fi, g);
459 return (ULONG)written;
461 #undef direntry_m
462 #undef filesize_m
465 static SFSIZE SeekInRollover(fileentry_t *file, SFSIZE offset, LONG mode, SIPTR *error, globaldata *g)
467 #define filesize_m GetDEFileSize(file->le.info.file.direntry, g)
468 #define direntry_m file->le.info.file.direntry
470 struct extrafields extrafields;
471 LONG oldvirtualoffset, virtualoffset;
472 ULONG anodeoffset, blockoffset;
474 DB(Trace(1,"SeekInRollover","offset = %ld mode=%ld\n",offset,mode));
475 GetExtraFields(direntry_m,&extrafields);
477 /* do the seeking */
478 oldvirtualoffset = file->offset - extrafields.rollpointer;
479 if (oldvirtualoffset < 0) oldvirtualoffset += filesize_m;
481 switch (mode)
483 case OFFSET_BEGINNING:
484 virtualoffset = offset;
485 break;
487 case OFFSET_END:
488 virtualoffset = extrafields.virtualsize + offset;
489 break;
491 case OFFSET_CURRENT:
492 virtualoffset = oldvirtualoffset + offset;
493 break;
495 default:
496 *error = ERROR_SEEK_ERROR;
497 return -1;
500 if ((virtualoffset > extrafields.virtualsize) || virtualoffset < 0)
502 *error = ERROR_SEEK_ERROR;
503 return -1;
506 /* calculate real offset */
507 file->offset = virtualoffset + extrafields.rollpointer;
508 if (file->offset > filesize_m)
509 file->offset -= filesize_m;
511 /* calculate new values */
512 anodeoffset = file->offset >> BLOCKSHIFT;
513 blockoffset = file->offset & (BLOCKSIZE-1);
514 file->currnode = &file->anodechain->head;
515 CorrectAnodeAC(&file->currnode, &anodeoffset, g);
517 file->anodeoffset = anodeoffset;
518 file->blockoffset = blockoffset;
520 return oldvirtualoffset;
522 #undef filesize_m
523 #undef direntry_m
527 static SFSIZE ChangeRolloverSize(fileentry_t *file, SFSIZE releof, LONG mode,
528 SIPTR *error, globaldata *g)
530 #define filesize_m GetDEFileSize(file->le.info.file.direntry, g)
531 #define direntry_m file->le.info.file.direntry
533 struct extrafields extrafields;
534 SFSIZE virtualeof, virtualoffset;
535 union objectinfo directory;
536 struct fileinfo fi;
537 struct direntry *destentry;
538 UBYTE entrybuffer[MAX_ENTRYSIZE];
540 DB(Trace(1,"ChangeRolloverSize","offset = %ld mode=%ld\n",releof,mode));
541 GetExtraFields(direntry_m,&extrafields);
543 switch (mode)
545 case OFFSET_BEGINNING:
546 virtualeof = releof;
547 break;
549 case OFFSET_END:
550 virtualeof = extrafields.virtualsize + releof;
551 break;
553 case OFFSET_CURRENT:
554 virtualoffset = file->offset - extrafields.rollpointer;
555 if (virtualoffset < 0) virtualoffset += filesize_m;
556 virtualeof = virtualoffset + releof;
557 break;
558 default: /* bogus parameter -> ERROR_SEEK_ERROR */
559 virtualeof = -1;
560 break;
563 if (virtualeof < 0)
565 *error = ERROR_SEEK_ERROR;
566 return -1;
569 /* change virtual size */
570 if (virtualeof >= filesize_m)
571 extrafields.virtualsize = filesize_m - 1;
572 else
573 extrafields.virtualsize = virtualeof;
575 /* we don't update other filehandles or current offset here */
577 /* commit directoryentry changes */
578 destentry = (struct direntry *)entrybuffer;
579 memcpy(destentry, direntry_m, direntry_m->next);
580 AddExtraFields(destentry, &extrafields);
582 /* commit changes */
583 if (!GetParent(&file->le.info, &directory, error, g))
584 return DOSFALSE;
585 else
586 ChangeDirEntry(file->le.info.file, destentry, &directory, &fi, g);
588 return virtualeof;
590 #undef filesize_m
591 #undef direntry_m
594 #endif /* ROLLOVER */
596 /* <ReadFromFile>
598 ** Specification:
600 ** Reads 'size' bytes from file to buffer (if not readprotected)
601 ** result: #bytes read; -1 = error; 0 = eof
603 static ULONG ReadFromFile(fileentry_t *file, UBYTE *buffer, ULONG size,
604 SIPTR *error, globaldata *g)
606 ULONG anodeoffset, blockoffset, blockstoread;
607 ULONG fullblks, bytesleft;
608 ULONG t;
609 FSIZE tfs;
610 UBYTE *data = NULL, *dataptr;
611 BOOL directread = FALSE;
612 struct anodechainnode *chnode;
613 #if DELDIR
614 struct deldirentry *dde;
615 #endif
617 DB(Trace(1,"ReadFromFile","size = %lx offset = %lx\n",size,file->offset));
618 if (!CheckReadAccess(file, error, g))
619 return -1;
621 /* correct size and check if zero */
622 #if DELDIR
623 if (IsDelFile(file->le.info)) {
624 if (!(dde = GetDeldirEntryQuick(file->le.info.delfile.slotnr, g)))
625 return -1;
626 tfs = GetDDFileSize(dde, g) - file->offset;
628 else
629 #endif
630 tfs = GetDEFileSize(file->le.info.file.direntry, g) - file->offset;
632 if (!(size = min(tfs, size)))
633 return 0;
635 /* initialize */
636 anodeoffset = file->anodeoffset;
637 blockoffset = file->blockoffset;
638 chnode = file->currnode;
639 t = blockoffset + size;
640 fullblks = t>>BLOCKSHIFT; /* # full blocks */
641 bytesleft = t&(BLOCKSIZE-1); /* # bytes in last incomplete block */
643 /* check mask, both at start and end */
644 t = (((IPTR)(buffer-blockoffset+BLOCKSIZE))&~g->dosenvec->de_Mask) ||
645 (((IPTR)(buffer+size-bytesleft))&~g->dosenvec->de_Mask);
646 t = !t;
648 /* read indirect if
649 * - mask failure
650 * - too small
651 * - larger than one block (use 'direct' cached read for just one)
653 if (!t || (fullblks<2*DIRECTSIZE && (blockoffset+size>BLOCKSIZE) &&
654 (blockoffset || (bytesleft&&fullblks<DIRECTSIZE))))
656 /* full indirect read */
657 blockstoread = fullblks + (bytesleft>0);
658 if (!(data = AllocBufmem (blockstoread<<BLOCKSHIFT, g)))
660 *error = ERROR_NO_FREE_STORE;
661 return -1;
663 dataptr = data;
665 else
667 /* direct read */
668 directread = TRUE;
669 blockstoread = fullblks;
670 dataptr = buffer;
672 /* read first blockpart */
673 if (blockoffset)
675 data = CachedReadD(chnode->an.blocknr + anodeoffset, error, g);
676 if (data)
678 NextBlockAC(&chnode, &anodeoffset, g);
680 /* calc numbytes */
681 t = BLOCKSIZE-blockoffset;
682 t = min(t, size);
683 memcpy(dataptr, data+blockoffset, t);
684 dataptr+=t;
685 if (blockstoread)
686 blockstoread--;
687 else
688 bytesleft = 0; /* single block access */
693 /* read middle part */
694 while (blockstoread && !*error)
696 if ((blockstoread + anodeoffset) >= chnode->an.clustersize)
697 t = chnode->an.clustersize - anodeoffset; /* read length */
698 else
699 t = blockstoread;
701 *error = DiskRead(dataptr, t, chnode->an.blocknr + anodeoffset, g);
702 if (!*error)
704 blockstoread -= t;
705 dataptr += t<<BLOCKSHIFT;
706 anodeoffset += t;
707 CorrectAnodeAC(&chnode, &anodeoffset, g);
711 /* read last block part/ copy read data to buffer */
712 if (!*error)
714 if (!directread)
715 memcpy(buffer, data+blockoffset, size);
716 else if (bytesleft)
718 data = CachedReadD(chnode->an.blocknr+anodeoffset, error, g);
719 if (data)
720 memcpy(dataptr, data, bytesleft);
724 if (!directread)
725 FreeBufmem(data, g);
726 if (!*error)
728 file->anodeoffset += fullblks;
729 file->blockoffset = (file->blockoffset + size)&(BLOCKSIZE-1); // not bytesleft!!
730 CorrectAnodeAC(&file->currnode, &file->anodeoffset, g);
731 file->offset += size;
732 return size;
734 else
736 DB(Trace(1,"Read","failed\n"));
737 return -1;
743 /* <WriteToFile>
745 ** Specification:
747 ** - Copy data in file at current position;
748 ** - Automatic fileextension;
749 ** - Error = bytecount <> opdracht
750 ** - On error no position update
752 ** - Clear Archivebit -> done by Touch()
753 **V- directory protection (amigados does not do this)
755 ** result: num bytes written; DOPUS wants -1 = error;
757 ** Implementation parts
759 ** - Test on writeprotection; yes -> error;
760 ** - Initialisation
761 ** - Extend filesize
762 ** - Write firstblockpart
763 ** - Write all whole blocks
764 ** - Write last block
765 ** - | Update directory (if no errors)
766 ** | Deextent filesize (if error)
768 static ULONG WriteToFile(fileentry_t *file, UBYTE *buffer, ULONG size,
769 SIPTR *error, globaldata *g)
771 ULONG maskok, t;
772 ULONG totalblocks, oldblocksinfile;
773 FSIZE oldfilesize, newfileoffset;
774 ULONG newblocksinfile, bytestowrite, blockstofill;
775 ULONG anodeoffset, blockoffset;
776 UBYTE *data = NULL, *dataptr;
777 BOOL directwrite = FALSE;
778 struct anodechainnode *chnode;
779 int slotnr;
781 DB(Trace(1,"WriteToFile","size = %lx offset=%lx, file=%lx\n",size,file->offset,file));
782 /* initialization values */
783 chnode = file->currnode;
784 anodeoffset = file->anodeoffset;
785 blockoffset = file->blockoffset;
786 totalblocks = (blockoffset + size + BLOCKSIZE-1)>>BLOCKSHIFT; /* total # changed blocks */
787 if (!(bytestowrite = size)) /* # bytes to be done */
788 return 0;
790 /* filesize extend */
791 oldfilesize = GetDEFileSize(file->le.info.file.direntry, g);
792 newfileoffset = file->offset + size;
794 /* Check if too large (QUAD) or overflowed (ULONG)? */
795 if (newfileoffset > MAX_FILE_SIZE || newfileoffset < file->offset) {
796 *error = ERROR_DISK_FULL;
797 return -1;
800 oldblocksinfile = (oldfilesize + BLOCKSIZE-1)>>BLOCKSHIFT;
801 newblocksinfile = (newfileoffset + BLOCKSIZE-1)>>BLOCKSHIFT;
802 if (newblocksinfile > oldblocksinfile)
804 t = newblocksinfile - oldblocksinfile;
805 if (!AllocateBlocksAC(file->anodechain, t, &file->le.info.file, g))
807 SetDEFileSize(file->le.info.file.direntry, oldfilesize, g);
808 *error = ERROR_DISK_FULL;
809 return -1;
812 /* BUG 980422: this CorrectAnodeAC mode because of AllocateBlockAC!! AND
813 * because anodeoffset can be outside last block! (filepointer is
814 * byte 0 new block
816 CorrectAnodeAC(&chnode,&anodeoffset,g);
818 /* check mask */
819 maskok = (((IPTR)(buffer-blockoffset+BLOCKSIZE))&~g->dosenvec->de_Mask) ||
820 (((IPTR)(buffer-blockoffset+(totalblocks<<BLOCKSHIFT)))&~g->dosenvec->de_Mask);
821 maskok = !maskok;
823 /* write indirect if
824 * - mask failure
825 * - too small
827 if (!maskok || (totalblocks<2*DIRECTSIZE && (blockoffset+size>BLOCKSIZE*2) &&
828 (blockoffset || totalblocks<DIRECTSIZE)))
830 /* indirect */
831 /* allocate temporary data buffer */
832 if (!(dataptr = data = AllocBufmem(totalblocks<<BLOCKSHIFT, g)))
834 *error = ERROR_NO_FREE_STORE;
835 goto wtf_error;
838 /* first blockpart */
839 if (blockoffset)
841 *error = DiskRead(dataptr, 1, chnode->an.blocknr + anodeoffset, g);
842 bytestowrite += blockoffset;
843 if (bytestowrite<BLOCKSIZE)
844 bytestowrite = BLOCKSIZE; /* the first could also be the last block */
847 /* copy all 'to be written' to databuffer */
848 memcpy(dataptr+blockoffset, buffer, size);
850 else
852 /* direct */
853 dataptr = buffer;
854 directwrite = TRUE;
856 /* first blockpart */
857 if (blockoffset || (totalblocks==1 && newfileoffset > oldfilesize))
859 ULONG fbp; /* first block part */
860 UBYTE *firstblock;
862 if (blockoffset)
864 slotnr = CachedRead(chnode->an.blocknr + anodeoffset, error, g);
865 if (*error)
866 goto wtf_error;
868 else
870 /* for one block no offset growing file */
871 slotnr = FakeCachedRead(chnode->an.blocknr + anodeoffset, error, g);
874 /* copy data to cache and mark block as dirty */
875 firstblock = &g->dc.data[slotnr<<BLOCKSHIFT];
876 fbp = BLOCKSIZE-blockoffset;
877 fbp = min(bytestowrite, fbp); /* the first could also be the last block */
878 memcpy(firstblock+blockoffset, buffer, fbp);
879 MarkDataDirty(slotnr);
881 NextBlockAC(&chnode, &anodeoffset, g);
882 bytestowrite -= fbp;
883 dataptr += fbp;
884 totalblocks--;
888 /* write following blocks. If done, then blockoffset always 0 */
889 if (newfileoffset > oldfilesize)
891 blockstofill = totalblocks;
892 bytestowrite = totalblocks<<BLOCKSHIFT;
894 else
895 blockstofill = bytestowrite>>BLOCKSHIFT;
897 while (blockstofill && !*error)
899 if (blockstofill + anodeoffset >= chnode->an.clustersize)
900 t = chnode->an.clustersize - anodeoffset; /* t is # blocks to write now */
901 else
902 t = blockstofill;
904 *error = DiskWrite(dataptr, t, chnode->an.blocknr + anodeoffset, g);
905 if (!*error)
907 blockstofill -= t;
908 dataptr += t<<BLOCKSHIFT;
909 bytestowrite -= t<<BLOCKSHIFT;
910 anodeoffset += t;
911 CorrectAnodeAC(&chnode, &anodeoffset, g);
915 /* write last block (RAW because cache direct) */
916 if (bytestowrite && !*error)
918 UBYTE *lastblock;
920 slotnr = CachedRead(chnode->an.blocknr + anodeoffset, error, g);
921 if (!*error)
923 lastblock = &g->dc.data[slotnr<<BLOCKSHIFT];
924 memcpy(lastblock, dataptr, bytestowrite);
925 MarkDataDirty(slotnr);
929 /* free mem for indirect write */
930 if (!directwrite)
931 FreeBufmem(data, g);
932 if (!*error)
934 file->anodeoffset += (blockoffset + size)>>BLOCKSHIFT;
935 file->blockoffset = (blockoffset + size)&(BLOCKSIZE-1);
936 CorrectAnodeAC(&file->currnode, &file->anodeoffset, g);
937 file->offset += size;
938 SetDEFileSize(file->le.info.file.direntry, max(oldfilesize, file->offset), g);
939 MakeBlockDirty((struct cachedblock *)file->le.info.file.dirblock, g);
940 return size;
943 wtf_error:
944 if (newblocksinfile>oldblocksinfile)
946 /* restore old state of file */
947 #if VERSION23
948 SetDEFileSize(file->le.info.file.direntry, oldfilesize, g);
949 MakeBlockDirty((struct cachedblock *)file->le.info.file.dirblock, g);
950 FreeBlocksAC(file->anodechain, newblocksinfile-oldblocksinfile, freeanodes, g);
951 #else
952 FreeBlocksAC(file->anodechain, newblocksinfile-oldblocksinfile, freeanodes, g);
953 SetDEFileSize(file->le.info.file.direntry, oldfilesize, g);
954 MakeBlockDirty((struct cachedblock *)file->le.info.file.dirblock, g);
955 #endif
958 DB(Trace(1,"WriteToFile","failed\n"));
959 return -1;
963 /* SeekInFile
965 ** Specification:
967 ** - set fileposition
968 ** - if wrong position, resultposition unknown and error
969 ** - result = old position to start of file, -1 = error
971 ** - the end of the file is 0 from end
974 SFSIZE SeekInFile(fileentry_t *file, SFSIZE offset, LONG mode, SIPTR *error, globaldata *g)
976 SFSIZE oldoffset, newoffset;
977 ULONG anodeoffset, blockoffset;
978 #if DELDIR
979 struct deldirentry *delfile = NULL;
981 DB(Trace(1,"SeekInFile","offset = %ld mode=%ld\n",offset,mode));
982 if (IsDelFile(file->le.info))
983 if (!(delfile = GetDeldirEntryQuick(file->le.info.delfile.slotnr, g)))
984 return -1;
985 #endif
987 /* do the seeking */
988 oldoffset = file->offset;
989 newoffset = -1;
991 /* TODO: 32-bit wraparound checks */
993 switch (mode)
995 case OFFSET_BEGINNING:
996 newoffset = offset;
997 break;
999 case OFFSET_END:
1000 #if DELDIR
1001 if (delfile)
1002 newoffset = GetDDFileSize(delfile, g) + offset;
1003 else
1004 #endif
1005 newoffset = GetDEFileSize(file->le.info.file.direntry, g) + offset;
1006 break;
1008 case OFFSET_CURRENT:
1009 newoffset = oldoffset + offset;
1010 break;
1012 default:
1013 *error = ERROR_SEEK_ERROR;
1014 return -1;
1017 #if DELDIR
1018 if ((newoffset > (delfile ? GetDDFileSize(delfile, g) :
1019 GetDEFileSize(file->le.info.file.direntry, g))) || (newoffset < 0))
1020 #else
1021 if ((newoffset > GetDEFileSize(file->le.info.file.direntry)) || (newoffset < 0))
1022 #endif
1024 *error = ERROR_SEEK_ERROR;
1025 return -1;
1028 /* calculate new values */
1029 anodeoffset = newoffset >> BLOCKSHIFT;
1030 blockoffset = newoffset & (BLOCKSIZE-1);
1031 file->currnode = &file->anodechain->head;
1032 CorrectAnodeAC(&file->currnode, &anodeoffset, g);
1033 /* DiskSeek(anode.blocknr + anodeoffset, g); */
1035 file->anodeoffset = anodeoffset;
1036 file->blockoffset = blockoffset;
1037 file->offset = newoffset;
1038 return oldoffset;
1042 /* Changes the length of a file
1043 ** Returns new length; -1 if failure
1045 ** Check if allowed
1046 ** 'Seek with extend'
1047 ** change other locks
1048 ** change direntry
1050 SFSIZE ChangeFileSize(fileentry_t *file, SFSIZE releof, LONG mode, SIPTR *error,
1051 globaldata *g)
1053 listentry_t *fe;
1054 SFSIZE abseof;
1055 LONG t;
1056 ULONG myanode, oldblocksinfile, newblocksinfile;
1057 FSIZE oldfilesize;
1059 /* check access */
1060 DB(Trace(1,"ChangeFileSize","offset = %ld mode=%ld\n",releof,mode));
1061 if (!CheckChangeAccess(file, error, g))
1062 return -1;
1064 /* Changing file -> set notify flag */
1065 file->checknotify = 1;
1066 *error = 0;
1068 /* TODO: 32-bit wraparound checks */
1070 /* calculate new eof (ala 'Seek') */
1071 switch (mode)
1073 case OFFSET_BEGINNING:
1074 abseof = releof;
1075 break;
1077 case OFFSET_END:
1078 abseof = GetDEFileSize(file->le.info.file.direntry, g) + releof;
1079 break;
1081 case OFFSET_CURRENT:
1082 abseof = file->offset + releof;
1083 break;
1085 default:
1086 *error = ERROR_SEEK_ERROR;
1087 return DOSFALSE;
1090 /* < 0 check still needed because QUAD is signed */
1091 if (abseof < 0 || abseof > MAX_FILE_SIZE)
1093 *error = ERROR_SEEK_ERROR;
1094 return -1;
1097 /* change allocation (ala WriteToFile) */
1098 oldfilesize = GetDEFileSize(file->le.info.file.direntry, g);
1099 oldblocksinfile = (GetDEFileSize(file->le.info.file.direntry, g) + BLOCKSIZE-1)>>BLOCKSHIFT;
1100 newblocksinfile = (abseof+BLOCKSIZE-1)>>BLOCKSHIFT;
1102 if (newblocksinfile > oldblocksinfile)
1104 /* new blocks, 4*allocated anode, dirblock */
1105 t = newblocksinfile - oldblocksinfile;
1106 if (!AllocateBlocksAC(file->anodechain, t, &file->le.info.file, g))
1108 SetDEFileSize(file->le.info.file.direntry, oldfilesize, g);
1109 *error = ERROR_DISK_FULL;
1110 return -1;
1113 /* change directory: in case of allocate this has to be done
1114 * afterwards
1116 SetDEFileSize(file->le.info.file.direntry, abseof, g);
1117 MakeBlockDirty((struct cachedblock *)file->le.info.file.dirblock, g);
1119 else if (oldblocksinfile > newblocksinfile)
1121 /* change directoryentry beforehand (needed for postponed delete but not
1122 * allowed for online updating allocate).
1124 SetDEFileSize(file->le.info.file.direntry, abseof, g);
1125 MakeBlockDirty((struct cachedblock *)file->le.info.file.dirblock, g);
1127 t = oldblocksinfile - newblocksinfile;
1128 FreeBlocksAC(file->anodechain, t, freeanodes, g);
1130 /* PS: there will always be an anode left, since FreeBlocksAC
1131 * doesn't delete the last anode
1134 else
1136 /* no change in number of blocks, just change directory entry */
1137 SetDEFileSize(file->le.info.file.direntry, abseof, g);
1138 MakeBlockDirty((struct cachedblock *)file->le.info.file.dirblock, g);
1141 /* change filehandles (including own) */
1142 myanode = file->le.anodenr;
1143 for (fe = HeadOf(&g->currentvolume->fileentries); fe->next; fe=fe->next)
1145 if (fe->anodenr == myanode)
1147 if (IsFileEntry(fe) && ((fileentry_t *)fe)->offset >= abseof)
1148 SeekInFile((fileentry_t *)fe, abseof, OFFSET_BEGINNING, error, g);
1152 return abseof;
1156 /**********************************************************************/
1157 /* CACHE STUFF */
1158 /**********************************************************************/
1160 /* check datacache. return cache slotnr or -1
1161 * if not found
1163 static int CheckDataCache(ULONG blocknr, globaldata *g)
1165 int i;
1167 for (i = 0; i < g->dc.size; i++)
1169 if (g->dc.ref[i].blocknr == blocknr)
1170 return i;
1173 return -1;
1176 /* get block from cache or put it in cache if it wasn't
1177 * there already. return cache slotnr. errors are indicated by 'error'
1178 * (null = ok)
1180 static int CachedRead(ULONG blocknr, SIPTR *error, globaldata *g)
1182 int i;
1184 *error = 0;
1185 i = CheckDataCache(blocknr, g);
1186 if (i != -1) return i;
1187 i = g->dc.roving;
1188 if (g->dc.ref[i].dirty && g->dc.ref[i].blocknr)
1189 UpdateSlot(i, g);
1191 *error = RawRead(&g->dc.data[i<<BLOCKSHIFT], 1, blocknr, g);
1192 g->dc.roving = (g->dc.roving+1)&g->dc.mask;
1193 g->dc.ref[i].dirty = 0;
1194 g->dc.ref[i].blocknr = blocknr;
1195 return i;
1198 static int FakeCachedRead(ULONG blocknr, SIPTR *error, globaldata *g)
1200 int i;
1202 *error = 0;
1203 i = CheckDataCache(blocknr, g);
1204 if (i != -1) return i;
1205 i = g->dc.roving;
1206 if (g->dc.ref[i].dirty && g->dc.ref[i].blocknr)
1207 UpdateSlot(i, g);
1209 memset(&g->dc.data[i<<BLOCKSHIFT], 0xAA, BLOCKSIZE);
1210 g->dc.roving = (g->dc.roving+1)&g->dc.mask;
1211 g->dc.ref[i].dirty = 0;
1212 g->dc.ref[i].blocknr = blocknr;
1213 return i;
1216 static UBYTE *CachedReadD(ULONG blknr, SIPTR *err, globaldata *g)
1218 int i;
1220 i = CachedRead(blknr,err,g);
1221 if (*err)
1222 return NULL;
1223 else
1224 return &g->dc.data[i<<BLOCKSHIFT];
1227 /* write block in cache. if block was already cached,
1228 * overwrite it. return slotnr (never fails).
1230 static int CachedWrite(UBYTE *data, ULONG blocknr, globaldata *g)
1232 int i;
1234 i = CheckDataCache(blocknr, g);
1235 if (i == -1)
1237 i = g->dc.roving;
1238 g->dc.roving = (g->dc.roving+1)&g->dc.mask;
1239 if (g->dc.ref[i].dirty && g->dc.ref[i].blocknr)
1240 UpdateSlot(i, g);
1242 memcpy(&g->dc.data[i<<BLOCKSHIFT], data, BLOCKSIZE);
1243 g->dc.ref[i].dirty = 1;
1244 g->dc.ref[i].blocknr = blocknr;
1245 return i;
1249 /* flush all blocks in datacache (without updating them first).
1251 void FlushDataCache(globaldata *g)
1253 int i;
1255 for (i=0; i<g->dc.size; i++)
1256 g->dc.ref[i].blocknr = 0;
1259 /* write all dirty blocks to disk
1261 void UpdateDataCache(globaldata *g)
1263 int i;
1265 for (i=0; i<g->dc.size; i++)
1266 if (g->dc.ref[i].dirty && g->dc.ref[i].blocknr)
1267 UpdateSlot (i, g);
1271 /* update a data cache slot, and any adjacent blocks
1273 static void UpdateSlot(int slotnr, globaldata *g)
1275 ULONG blocknr;
1276 int i;
1278 blocknr = g->dc.ref[slotnr].blocknr;
1280 /* find out how many adjacent blocks can be written */
1281 for (i=slotnr; i<g->dc.size; i++)
1283 if (g->dc.ref[i].blocknr != blocknr++)
1284 break;
1285 g->dc.ref[i].dirty = 0;
1288 /* write them */
1289 RawWrite(&g->dc.data[slotnr<<BLOCKSHIFT], i-slotnr, g->dc.ref[slotnr].blocknr, g);
1292 /* update cache to reflect blocks read to or written
1293 * from disk. to be called before disk is accessed.
1295 static void ValidateCache(ULONG blocknr, ULONG numblocks, enum vctype vctype, globaldata *g)
1297 int i;
1299 ENTER("ValidateCache");
1300 for (i=0; i<g->dc.size; i++)
1302 if (g->dc.ref[i].blocknr >= blocknr &&
1303 g->dc.ref[i].blocknr < blocknr + numblocks)
1305 if (vctype == read)
1307 if (g->dc.ref[i].dirty)
1308 UpdateSlot(i, g);
1310 else // flush
1311 g->dc.ref[i].blocknr = 0;
1317 /**********************************************************************/
1318 /* DEVICECOMMANDS */
1319 /* DEVICECOMMANDS */
1320 /* DEVICECOMMANDS */
1321 /**********************************************************************/
1324 /* DiskRead
1326 ** Reads 'blocks' complete blocks in a caller supplied buffer.
1328 ** input : - buffer: buffer for data
1329 ** - blockstoread: number of blocks to read
1330 ** - blocknr: starting block
1332 ** global: - disk is used to get request struct
1334 ** result: errornr, 0=ok
1336 ULONG DiskRead(UBYTE *buffer, ULONG blockstoread, ULONG blocknr, globaldata *g)
1338 SIPTR error;
1339 int slotnr;
1341 DB(Trace(1, "DiskRead", "%ld blocks from %ld firstblock %ld\n",
1342 (ULONG)blockstoread, (ULONG)blocknr, g->firstblock));
1344 if (blocknr == (ULONG)-1) // blocknr of uninitialised anode
1345 return 1;
1346 if (!blockstoread)
1347 return 0;
1349 if (blockstoread == 1)
1351 slotnr = CachedRead(blocknr, &error, g);
1352 memcpy(buffer, &g->dc.data[slotnr<<BLOCKSHIFT], BLOCKSIZE);
1353 return error;
1355 ValidateCache(blocknr, blockstoread, read, g);
1356 return RawRead(buffer, blockstoread, blocknr, g);
1360 /* DiskWrite
1362 ** Writes 'blocks' complete blocks from a buffer.
1364 ** input : - buffer: the data
1365 ** - blockstowrite: number of blocks to write
1366 ** - blocknr: starting block
1368 ** global: - disk is used to get request struct
1370 ** result: errornr, 0=ok
1372 ULONG DiskWrite(UBYTE *buffer, ULONG blockstowrite, ULONG blocknr, globaldata *g)
1374 ULONG slotnr;
1375 ULONG error = 0;
1377 DB(Trace(1, "DiskWrite", "%ld blocks from %ld + %ld\n", blockstowrite, blocknr,
1378 g->firstblock));
1380 if (blocknr == (ULONG)-1) // blocknr of uninitialised anode
1381 return 1;
1382 if (!blockstowrite)
1383 return 0;
1385 if (blockstowrite == 1)
1387 CachedWrite(buffer, blocknr, g);
1388 return 0;
1390 ValidateCache(blocknr, blockstowrite, write, g);
1391 error = RawWrite(buffer, blockstowrite, blocknr, g);
1393 /* cache last block written */
1394 if (!error)
1396 buffer += ((blockstowrite-1)<<BLOCKSHIFT);
1397 slotnr = CachedWrite(buffer, blocknr+blockstowrite-1, g);
1398 g->dc.ref[slotnr].dirty = 0; // we just wrote it
1400 return error;
1404 * SCSI direct functions
1407 #if SCSIDIRECT
1409 static int DoSCSICommand(UBYTE *data, ULONG datalen, UBYTE *command,
1410 UWORD commandlen, UBYTE direction, globaldata *g)
1412 g->scsicmd.scsi_Data = (UWORD *)data;
1413 g->scsicmd.scsi_Length = datalen;
1414 g->scsicmd.scsi_Command = command;
1415 g->scsicmd.scsi_CmdLength = commandlen;
1416 g->scsicmd.scsi_Flags = SCSIF_AUTOSENSE | direction; /* SCSIF_READ or SCSIF_WRITE */
1417 g->scsicmd.scsi_SenseData = g->sense;
1418 g->scsicmd.scsi_SenseLength = 18;
1419 g->scsicmd.scsi_SenseActual = 0;
1420 g->scsicmd.scsi_Status = 1;
1422 g->request->iotd_Req.io_Length = sizeof(struct SCSICmd);
1423 g->request->iotd_Req.io_Data = (APTR)&g->scsicmd;
1424 g->request->iotd_Req.io_Command = HD_SCSICMD;
1425 if (DoIO((struct IORequest *)g->request) != 0)
1426 return 0;
1427 #if 0
1428 DebugPutHex("doio", err);
1429 DebugPutHex("status", g->scsicmd.scsi_Status);
1430 #endif
1431 if (g->scsicmd.scsi_Status)
1432 return 0;
1433 else
1434 return 1;
1437 static ULONG RawRead_DS(UBYTE *buffer, ULONG blocks, ULONG blocknr, globaldata *g)
1439 UBYTE cmdbuf[10];
1440 ULONG transfer, maxtransfer;
1442 if(blocknr == (ULONG)-1) // blocknr of uninitialised anode
1443 return 1;
1445 blocknr += g->firstblock;
1446 retry_read:
1447 if(!(InPartition(blocknr) && InPartition(blocknr+blocks-1)))
1449 ErrorMsg(AFS_ERROR_READ_OUTSIDE, NULL, g);
1450 return ERROR_SEEK_ERROR;
1453 /* chop in maxtransfer chunks */
1454 maxtransfer = g->maxtransfer >> BLOCKSHIFT;
1455 while (blocks > 0)
1457 transfer = min(blocks,maxtransfer);
1458 *((UWORD *)&cmdbuf[0]) = 0x2800;
1459 *((ULONG *)&cmdbuf[2]) = blocknr;
1460 *((ULONG *)&cmdbuf[6]) = transfer<<8;
1461 PROFILE_OFF();
1462 if (!DoSCSICommand(buffer,transfer<<BLOCKSHIFT,cmdbuf,10,SCSIF_READ,g))
1464 ULONG args[2];
1465 PROFILE_ON();
1466 args[0] = g->sense[2];
1467 args[1] = blocknr - g->firstblock;
1468 while ((g->ErrorMsg)(AFS_ERROR_READ_ERROR, args, 2, g))
1470 if (CheckCurrentVolumeBack(g))
1471 goto retry_read;
1473 if (!g->softprotect)
1475 g->softprotect = 1;
1476 g->protectkey = ~0;
1478 if (g->currentvolume)
1479 g->currentvolume->numsofterrors++;
1481 PROFILE_ON();
1482 buffer += transfer<<BLOCKSHIFT;
1483 blocks -= transfer;
1484 blocknr += transfer;
1487 return 0;
1491 * VVV Todo: SCSI ErrorNumber in requester
1494 static ULONG RawWrite_DS(UBYTE *buffer, ULONG blocks, ULONG blocknr, globaldata *g)
1496 UBYTE cmdbuf[10];
1497 ULONG transfer, maxtransfer;
1499 if(blocknr == (ULONG)-1)
1500 return 1;
1502 blocknr += g->firstblock;
1503 retry_write:
1504 if(g->softprotect)
1505 return ERROR_DISK_WRITE_PROTECTED;
1507 if (!(InPartition(blocknr) && InPartition(blocknr+blocks-1)))
1509 ErrorMsg (AFS_ERROR_WRITE_OUTSIDE, NULL, g);
1510 return ERROR_SEEK_ERROR;
1513 /* chop in maxtransfer chunks */
1514 maxtransfer = g->maxtransfer >> BLOCKSHIFT;
1515 while (blocks > 0)
1517 transfer = min(blocks,maxtransfer);
1518 *((UWORD *)&cmdbuf[0]) = 0x2a00;
1519 *((ULONG *)&cmdbuf[2]) = blocknr;
1520 *((ULONG *)&cmdbuf[6]) = transfer<<8;
1521 PROFILE_OFF();
1522 if (!DoSCSICommand(buffer,blocks<<BLOCKSHIFT,cmdbuf,10,SCSIF_WRITE,g))
1524 ULONG args[2];
1525 PROFILE_ON();
1526 args[0] = g->sense[2];
1527 args[1] = blocknr - g->firstblock;
1528 while ((g->ErrorMsg)(AFS_ERROR_WRITE_ERROR, args, 2, g))
1530 if (CheckCurrentVolumeBack(g))
1531 goto retry_write;
1533 if (!g->softprotect)
1535 g->softprotect = 1;
1536 g->protectkey = ~0;
1538 if (g->currentvolume)
1539 g->currentvolume->numsofterrors++;
1541 PROFILE_ON();
1542 buffer += transfer<<BLOCKSHIFT;
1543 blocks -= transfer;
1544 blocknr += transfer;
1547 return 0;
1550 #endif /* SCSI Direct */
1552 #if TD64
1555 * Normal commands
1558 /* Geometry MUST be loaded!!
1560 static ULONG RawRead_TD(UBYTE *buffer, ULONG blocks, ULONG blocknr, globaldata *g)
1562 struct IOExtTD *request;
1563 ULONG realblocknr;
1564 ULONG io_length, io_transfer, io_offset, io_actual = 0, io_startblock = 0;
1565 UBYTE *io_buffer;
1567 DB(Trace(1, "RawRead", "%ld blocks from %ld firstblock %ld\n",
1568 (ULONG)blocks, (ULONG)blocknr, g->firstblock));
1570 retry_read:
1571 if(blocknr == (ULONG)-1) // blocknr of uninitialised anode
1572 return 1;
1574 realblocknr = blocknr + g->firstblock;
1575 if(!(InPartition(realblocknr) && InPartition(realblocknr+blocks-1)))
1577 ErrorMsg (AFS_ERROR_READ_OUTSIDE, NULL, g);
1578 return ERROR_SEEK_ERROR;
1581 io_length = blocks << BLOCKSHIFT;
1582 io_offset = realblocknr << BLOCKSHIFT;
1583 io_buffer = buffer;
1584 if (g->tdmode >= ACCESS_TD64) {
1585 // upper 32 bit of offset
1586 io_actual = realblocknr >> (32-BLOCKSHIFT);
1587 io_startblock = realblocknr;
1590 while (io_length > 0)
1592 io_transfer = min(io_length, g->maxtransfer);
1593 io_transfer &= ~(BLOCKSIZE-1);
1594 request = g->request;
1595 request->iotd_Req.io_Command = CMD_READ;
1596 request->iotd_Req.io_Length = io_transfer;
1597 request->iotd_Req.io_Data = io_buffer; // bufmemtype ??
1598 request->iotd_Req.io_Offset = io_offset;
1599 if (g->tdmode >= ACCESS_TD64) {
1600 request->iotd_Req.io_Actual = io_actual;
1601 request->iotd_Req.io_Command = g->tdmode == ACCESS_NSD ? NSCMD_TD_READ64 : TD_READ64;
1604 PROFILE_OFF();
1605 if (DoIO((struct IORequest*)request) != 0)
1607 ULONG args[2];
1608 PROFILE_ON();
1609 args[0] = request->iotd_Req.io_Error;
1610 args[1] = blocknr; /* should be realblocknr ?? */
1611 while ((g->ErrorMsg)(AFS_ERROR_READ_ERROR, args, 2, g))
1613 if (CheckCurrentVolumeBack(g))
1614 goto retry_read;
1616 if (!g->softprotect)
1618 g->softprotect = 1;
1619 g->protectkey = ~0;
1621 if (g->currentvolume)
1622 g->currentvolume->numsofterrors++;
1623 DB(Trace(1,"RawRead","readerror nr %ld\n", args[0], args[1]));
1624 return ERROR_NOT_A_DOS_DISK;
1626 PROFILE_ON();
1627 io_buffer += io_transfer;
1628 io_length -= io_transfer;
1629 if (g->tdmode >= ACCESS_TD64) {
1630 io_startblock += (io_transfer >> BLOCKSHIFT);
1631 io_offset = io_startblock << BLOCKSHIFT;
1632 io_actual = io_startblock >> (32-BLOCKSHIFT);
1633 } else {
1634 io_offset += io_transfer;
1638 return 0;
1642 #if 0
1643 static ULONG TD_Format(UBYTE *buffer, ULONG blocks, ULONG blocknr, globaldata *g)
1645 struct IOExtTD *request;
1646 ULONG realblocknr;
1648 DB(Trace(1, "TD_Format", "%ld blocks from %ld + %ld\n", blocks, blocknr,
1649 g->firstblock));
1651 retry_format:
1652 if (blocknr == (ULONG)-1) // blocknr of uninitialised anode
1653 return(1);
1655 realblocknr = blocknr + g->firstblock;
1656 if(!InPartition(realblocknr))
1658 ErrorMsg (AFS_ERROR_WRITE_OUTSIDE, NULL, g);
1659 return ERROR_SEEK_ERROR;
1662 request = g->request;
1663 request->iotd_Req.io_Command = TD_FORMAT;
1664 request->iotd_Req.io_Length = blocks*BLOCKSIZE;
1665 request->iotd_Req.io_Data = buffer; // bufmemtype ??
1666 request->iotd_Req.io_Offset = realblocknr*BLOCKSIZE;
1667 PROFILE_OFF();
1668 if(DoIO((struct IORequest*)request) != NULL)
1670 ULONG args[2];
1671 PROFILE_ON();
1672 args[0] = request->iotd_Req.io_Error;
1673 args[1] = blocknr; /* should be realblocknr?? */
1674 while ((g->ErrorMsg)(AFS_ERROR_WRITE_ERROR, args, 2, g))
1676 if (CheckCurrentVolumeBack(g))
1677 goto retry_format;
1679 if (!g->softprotect)
1681 g->softprotect = 1;
1682 g->protectkey = ~0;
1684 if (g->currentvolume)
1685 g->currentvolume->numsofterrors++;
1686 DB(Trace(1,"TD_Format","writeerror nr %d\n", args[0], args[1]));
1687 return ERROR_NOT_A_DOS_DISK;
1689 PROFILE_ON();
1690 return NULL;
1693 #endif /* TRACKDISK */
1696 static ULONG RawWrite_TD(UBYTE *buffer, ULONG blocks, ULONG blocknr, globaldata *g)
1698 struct IOExtTD *request;
1699 ULONG realblocknr;
1700 ULONG io_length, io_transfer, io_offset, io_actual = 0, io_startblock = 0;
1701 UBYTE *io_buffer;
1703 DB(Trace(1, "RawWrite", "%ld blocks from %ld + %ld\n", blocks, blocknr,
1704 g->firstblock));
1706 retry_write:
1707 if(blocknr == (ULONG)-1) // blocknr of uninitialised anode
1708 return 1;
1710 if (g->softprotect)
1711 return ERROR_DISK_WRITE_PROTECTED;
1713 realblocknr = blocknr + g->firstblock;
1714 if (!(InPartition(realblocknr) && InPartition(realblocknr+blocks-1)))
1716 ErrorMsg (AFS_ERROR_WRITE_OUTSIDE, NULL, g);
1717 return ERROR_SEEK_ERROR;
1720 io_length = blocks << BLOCKSHIFT;
1721 io_offset = realblocknr << BLOCKSHIFT;
1722 io_buffer = buffer;
1723 if (g->tdmode >= ACCESS_TD64) {
1724 // upper 32 bit of offset
1725 io_actual = realblocknr >> (32 - BLOCKSHIFT);
1726 io_startblock = realblocknr;
1729 while(io_length > 0)
1731 io_transfer = min(io_length, g->maxtransfer);
1732 io_transfer &= ~(BLOCKSIZE-1);
1733 request = g->request;
1734 request->iotd_Req.io_Command = CMD_WRITE;
1735 request->iotd_Req.io_Length = io_transfer;
1736 request->iotd_Req.io_Data = io_buffer; // bufmemtype ??
1737 request->iotd_Req.io_Offset = io_offset;
1738 if (g->tdmode >= ACCESS_TD64) {
1739 request->iotd_Req.io_Actual = io_actual;
1740 request->iotd_Req.io_Command = g->tdmode == ACCESS_NSD ? NSCMD_TD_WRITE64 : TD_WRITE64;
1743 PROFILE_OFF();
1744 if (DoIO((struct IORequest*)request) != 0)
1746 ULONG args[2];
1747 PROFILE_ON();
1748 args[0] = request->iotd_Req.io_Error;
1749 args[1] = blocknr; /* should be realblocknr?? */
1750 while ((g->ErrorMsg)(AFS_ERROR_WRITE_ERROR, args, 2, g))
1752 if (CheckCurrentVolumeBack(g))
1753 goto retry_write;
1755 if (!g->softprotect)
1757 g->softprotect = 1;
1758 g->protectkey = ~0;
1760 if (g->currentvolume)
1761 g->currentvolume->numsofterrors++;
1762 DB(Trace(1,"RawWrite","writeerror nr %d\n", args[0], args[1]));
1763 return ERROR_NOT_A_DOS_DISK;
1765 PROFILE_ON();
1766 io_buffer += io_transfer;
1767 io_length -= io_transfer;
1768 if (g->tdmode >= ACCESS_TD64) {
1769 io_startblock += (io_transfer >> BLOCKSHIFT);
1770 io_offset = io_startblock << BLOCKSHIFT;
1771 io_actual = io_startblock >> (32-BLOCKSHIFT);
1772 } else {
1773 io_offset += io_transfer;
1777 return 0;
1780 #endif /* TD64 */
1782 ULONG RawRead2(UBYTE *buffer, ULONG blocks, ULONG blocknr, globaldata *g)
1784 #if (TRACKDISK || TD64 || NSD) && SCSIDIRECT
1785 if (g->tdmode == ACCESS_DS)
1786 return RawRead_DS(buffer, blocks, blocknr, g);
1787 else
1788 return RawRead_TD(buffer, blocks, blocknr, g);
1789 #elif SCSIDIRECT
1790 return RawRead_DS(buffer, blocks, blocknr, g);
1791 #else
1792 return RawRead_TD(buffer, blocks, blocknr, g);
1793 #endif
1796 ULONG RawRead(UBYTE *buffer, ULONG blocks, ULONG blocknr, globaldata *g)
1798 ULONG err = RawRead2(buffer, blocks, blocknr, g);
1799 if (err) {
1800 ULONG args[4] = { (ULONG)buffer, blocks, blocknr, err };
1801 ErrorMsg ("RawRead(%lX, %ld, %ld) failed with error %ld", args, g);
1803 return err;
1806 ULONG RawWrite2(UBYTE *buffer, ULONG blocks, ULONG blocknr, globaldata *g)
1808 #if (TRACKDISK || TD64 || NSD) && SCSIDIRECT
1809 if (g->tdmode == ACCESS_DS)
1810 return RawWrite_DS(buffer, blocks, blocknr, g);
1811 else
1812 return RawWrite_TD(buffer, blocks, blocknr, g);
1813 #elif SCSIDIRECT
1814 return RawWrite_DS(buffer, blocks, blocknr, g);
1815 #else
1816 return RawWrite_TD(buffer, blocks, blocknr, g);
1817 #endif
1820 ULONG RawWrite(UBYTE *buffer, ULONG blocks, ULONG blocknr, globaldata *g)
1822 ULONG err = RawWrite2(buffer, blocks, blocknr, g);
1823 if (err) {
1824 ULONG args[4] = { (ULONG)buffer, blocks, blocknr, err };
1825 ErrorMsg ("RawWrite(%lX, %ld, %ld) failed with error %ld", args, g);
1827 return err;
1830 #if ACCESS_DETECT
1832 #if DETECTDEBUG
1833 static UBYTE ACCESS_DEBUG1[] = "%s:%ld\nfirstblock=%ld\nlastblock=%ld\nblockshift=%ld\nblocksize=%ld\ninside4G=%ld";
1834 static UBYTE ACCESS_DEBUG2[] = "Test %ld = %ld";
1835 static UBYTE ACCESS_DEBUG3[] = "SCSI Read Capacity = %ld, Lastblock = %ld";
1836 #endif
1838 static void fillbuffer(UBYTE *buffer, UBYTE data, globaldata *g)
1840 memset (buffer, data + 1, BLOCKSIZE);
1842 /* Check if at least one byte has changed */
1843 static BOOL testbuffer(UBYTE *buffer, UBYTE data, globaldata *g)
1845 ULONG cnt;
1847 for (cnt = 0; cnt < BLOCKSIZE; cnt++) {
1848 if (buffer[cnt] != data + 1)
1849 return TRUE;
1851 return FALSE;
1854 #if SCSIDIRECT
1855 static BOOL testread_ds2(UBYTE *buffer, globaldata *g)
1857 UBYTE cmdbuf[10];
1858 UBYTE cnt;
1860 #if DETECTDEBUG
1861 DebugPutStr("testread_ds\n");
1862 #endif
1864 for (cnt = 0; cnt < 2; cnt++) {
1865 ULONG capacity;
1867 fillbuffer(buffer, 0xfe, g);
1868 /* Read Capacity */
1869 *((UWORD *)&cmdbuf[0]) = 0x2500;
1870 *((ULONG *)&cmdbuf[2]) = 0;
1871 *((ULONG *)&cmdbuf[6]) = 0;
1872 if (!DoSCSICommand(buffer, 8, cmdbuf, 10, SCSIF_READ, g)) {
1873 #if DETECTDEBUG
1874 DebugPutStr("DoSCSICommand Read Capacity failed\n");
1875 #endif
1876 return FALSE;
1878 capacity = *((ULONG*)buffer);
1880 #if DETECTDEBUG
1881 ULONG args[2];
1882 args[0] = capacity;
1883 args[1] = g->lastblock;
1884 g->ErrorMsg = _NormalErrorMsg;
1885 (g->ErrorMsg)(ACCESS_DEBUG3, args, 1, g);
1886 #endif
1888 if (g->lastblock > capacity) {
1889 #if DETECTDEBUG
1890 DebugPutStr("DoSCSICommand capacity smaller than last block\n");
1891 #endif
1892 return FALSE;
1894 fillbuffer(buffer, cnt, g);
1895 /* Read(10) */
1896 *((UWORD *)&cmdbuf[0]) = 0x2800;
1897 *((ULONG *)&cmdbuf[2]) = g->lastblock;
1898 *((ULONG *)&cmdbuf[6]) = 1 << 8;
1899 if (!DoSCSICommand(buffer, 1 << BLOCKSHIFT, cmdbuf, 10, SCSIF_READ, g)) {
1900 #if DETECTDEBUG
1901 DebugPutStr("DoSCSICommand Read(10) failed\n");
1902 #endif
1903 return FALSE;
1905 if (testbuffer(buffer, cnt, g)) {
1906 #ifdef DETECTDEBUG
1907 DebugPutStr("ok\n");
1908 #endif
1909 return TRUE;
1912 #if DETECTDEBUG
1913 DebugPutStr("testbuffer fail\n");
1914 #endif
1915 return FALSE;
1917 #endif
1919 static BOOL testread_ds(UBYTE *buffer, globaldata *g)
1921 BOOL ok;
1923 ok = testread_ds2(buffer, g);
1924 #if DETECTDEBUG
1925 ULONG args[2];
1926 args[0] = g->tdmode;
1927 args[1] = ok;
1928 g->ErrorMsg = _NormalErrorMsg;
1929 (g->ErrorMsg)(ACCESS_DEBUG2, args, 1, g);
1930 #endif
1931 return ok;
1934 static BOOL testread_td2(UBYTE *buffer, globaldata *g)
1936 struct IOExtTD *io = g->request;
1937 UBYTE cnt;
1939 #if NSD
1940 if (g->tdmode == ACCESS_NSD) {
1941 struct NSDeviceQueryResult nsdqr;
1942 UWORD *cmdcheck;
1943 nsdqr.SizeAvailable = 0;
1944 nsdqr.DevQueryFormat = 0;
1945 io->iotd_Req.io_Command = NSCMD_DEVICEQUERY;
1946 io->iotd_Req.io_Length = sizeof(nsdqr);
1947 io->iotd_Req.io_Data = (APTR)&nsdqr;
1948 if (DoIO((struct IORequest*)io) != 0)
1949 return FALSE;
1950 if (!((io->iotd_Req.io_Actual >= 16) && (io->iotd_Req.io_Actual <= sizeof(nsdqr)) && (nsdqr.SizeAvailable == io->iotd_Req.io_Actual)))
1951 return FALSE;
1952 if(nsdqr.DeviceType != NSDEVTYPE_TRACKDISK)
1953 return FALSE;
1954 for(cmdcheck = nsdqr.SupportedCommands; *cmdcheck; cmdcheck++) {
1955 if(*cmdcheck == NSCMD_TD_READ64)
1956 break;
1958 if (*cmdcheck == 0)
1959 return FALSE;
1961 #endif
1962 #if TD64
1963 if (g->tdmode == ACCESS_TD64) {
1964 UBYTE err;
1965 io->iotd_Req.io_Command = TD_READ64;
1966 io->iotd_Req.io_Length = 0;
1967 io->iotd_Req.io_Data = 0;
1968 io->iotd_Req.io_Offset = 0;
1969 io->iotd_Req.io_Actual = 0;
1970 err = DoIO((struct IORequest*)io);
1971 if (err != 0 && err != IOERR_BADLENGTH && err != IOERR_BADADDRESS)
1972 return FALSE;
1974 #endif
1975 for (cnt = 0; cnt < 2; cnt++) {
1976 fillbuffer(buffer, cnt, g);
1977 io->iotd_Req.io_Command = CMD_READ;
1978 io->iotd_Req.io_Length = BLOCKSIZE;
1979 io->iotd_Req.io_Data = buffer;
1980 io->iotd_Req.io_Offset = g->lastblock << BLOCKSHIFT;
1981 if (g->tdmode >= ACCESS_TD64) {
1982 io->iotd_Req.io_Actual = g->lastblock >> (32 - BLOCKSHIFT);
1983 io->iotd_Req.io_Command = g->tdmode == ACCESS_NSD ? NSCMD_TD_READ64 : TD_READ64;
1985 if (DoIO((struct IORequest*)io) != 0)
1986 return FALSE;
1987 if (testbuffer(buffer, cnt, g))
1988 return TRUE;
1990 return TRUE;
1993 static BOOL testread_td(UBYTE *buffer, globaldata *g)
1995 BOOL ok;
1997 #if DETECTDEBUG
1998 DebugPutHex("testread_td mode=", g->tdmode);
1999 #endif
2000 ok = testread_td2(buffer, g);
2002 #if DETECTDEBUG
2003 ULONG args[3];
2004 args[0] = g->tdmode;
2005 args[1] = ok;
2006 g->ErrorMsg = _NormalErrorMsg;
2007 (g->ErrorMsg)(ACCESS_DEBUG2, args, 1, g);
2008 #endif
2010 #if DETECTDEBUG
2011 DebugPutHex("testread_td ret=", ok);
2012 #endif
2013 return ok;
2016 BOOL detectaccessmode(UBYTE *buffer, globaldata *g)
2018 UBYTE name[FNSIZE];
2019 BOOL inside4G = g->lastblock < (0x80000000ul >> (BLOCKSHIFT - 1));
2021 BCPLtoCString(name, (UBYTE *)BADDR(g->startup->fssm_Device));
2023 #if DETECTDEBUG
2024 ULONG args[8];
2025 args[0] = (ULONG)name;
2026 args[1] = g->startup->fssm_Unit;
2027 args[2] = g->firstblock;
2028 args[3] = g->lastblock;
2029 args[4] = BLOCKSHIFT;
2030 args[5] = BLOCKSIZE;
2031 args[6] = inside4G;
2032 g->ErrorMsg = _NormalErrorMsg;
2033 (g->ErrorMsg)(ACCESS_DEBUG1, args, 1, g);
2035 DebugPutHex("firstblock", g->firstblock);
2036 DebugPutHex("lastblock", g->lastblock);
2037 DebugPutHex("inside4G", inside4G);
2038 DebugPutHex("maxtransfer", g->maxtransfer);
2039 #endif
2041 #if SCSIDIRECT
2042 /* if dostype = PDSx, test Direct SCSI first and always use it if test succeeded */
2043 if ((g->dosenvec->de_DosType & 0xffffff00) == 0x50445300) {
2044 g->tdmode = ACCESS_DS;
2045 if (testread_ds(buffer, g))
2046 return TRUE;
2048 #endif
2050 if (g->lastblock < 262144) {
2051 /* Don't bother with tests if small enough (<128M) */
2052 g->tdmode = ACCESS_STD;
2053 return TRUE;
2056 if (inside4G) {
2057 ULONG args[3];
2058 /* inside first 4G? Try standard CMD_READ first. */
2059 g->tdmode = ACCESS_STD;
2060 if (testread_td(buffer, g))
2061 return TRUE;
2062 #if SCSIDIRECT
2063 g->tdmode = ACCESS_DS;
2064 /* It failed, we may have 1G limit A590 pre-7.0 ROM or CDTV SCSI, try DS */
2065 if (testread_ds(buffer, g))
2066 return TRUE;
2067 #endif
2068 g->tdmode = ACCESS_STD;
2069 /* Both failed. Panic! */
2070 args[0] = g->lastblock;
2071 args[1] = (ULONG)name;
2072 args[2] = g->startup->fssm_Unit;
2073 g->ErrorMsg = _NormalErrorMsg;
2074 (g->ErrorMsg)(AFS_ERROR_32BIT_ACCESS_ERROR, args, 1, g);
2075 return FALSE;
2077 /* outside of first 4G, must use TD64, NSD or DS */
2078 #if NSD
2079 g->tdmode = ACCESS_NSD;
2080 if (testread_td(buffer, g))
2081 return TRUE;
2082 #endif
2083 #if TD64
2084 g->tdmode = ACCESS_TD64;
2085 if (testread_td(buffer, g))
2086 return TRUE;
2087 #endif
2088 #if SCSIDIRECT
2089 g->tdmode = ACCESS_DS;
2090 if (testread_ds(buffer, g))
2091 return TRUE;
2092 #endif
2094 #if DETECTDEBUG
2095 args[0] = -1;
2096 args[1] = 0;
2097 g->ErrorMsg = _NormalErrorMsg;
2098 (g->ErrorMsg)(ACCESS_DEBUG2, args, 1, g);
2099 #endif
2101 g->tdmode = ACCESS_STD;
2102 return FALSE;
2104 #endif
2106 /*************************************************************************/
2108 /* turn drivemotor off */
2109 void MotorOff(globaldata *g)
2111 struct IOExtTD *request = g->request;
2113 if(g->removable)
2115 request->iotd_Req.io_Command = TD_MOTOR;
2116 request->iotd_Req.io_Length = 0;
2117 request->iotd_Count = g->changecount;
2119 DoIO((struct IORequest*)request);